@game_ryo/lsji 1.0.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE CHANGED
@@ -1,185 +1,21 @@
1
- Apache License
2
- Version 2.0, January 2004
3
- http://www.apache.org/licenses/
4
-
5
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
-
7
- 1. Definitions.
8
-
9
- "License" shall mean the terms and conditions for use, reproduction,
10
- and distribution as defined by Sections 1 through 9 of this document.
11
-
12
- "Licensor" shall mean the copyright owner or entity authorized by
13
- the copyright owner that is granting the License.
14
-
15
- "Legal Entity" shall mean the union of the acting entity and all
16
- other entities that control, are controlled by, or are under common
17
- control with that entity. For the purposes of this definition,
18
- "control" means (i) the power, direct or indirect, to cause the
19
- direction or management of such entity, whether by contract or
20
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
- outstanding shares, or (iii) the beneficial ownership of such entity.
22
-
23
- "You" (or "Your") shall mean an individual or Legal Entity
24
- exercising permissions granted by this License.
25
-
26
- "Source" form shall mean the preferred form for making modifications,
27
- including but not limited to software source code, documentation
28
- source, and configuration files.
29
-
30
- "Object" form shall mean any form resulting from mechanical
31
- transformation or translation of a Source form, including but
32
- not limited to compiled object code, generated documentation,
33
- and conversions to other media types.
34
-
35
- "Work" shall mean the work of authorship, whether in Source or
36
- Object form, made available under the License, as indicated by a
37
- copyright notice that is included in or attached to the work
38
- (an example is provided in the Appendix below).
39
-
40
- "Derivative Works" shall mean any work, whether in Source or Object
41
- form, that is based on (or derived from) the Work and for which the
42
- editorial revisions, annotations, elaborations, or other modifications
43
- represent, as a whole, an original work of authorship. For the purposes
44
- of this License, Derivative Works shall not include works that remain
45
- separable from, or merely link (or bind by name) to the interfaces of,
46
- the Work and Derivative Works thereof.
47
-
48
- "Contribution" shall mean any work of authorship, including
49
- the original version of the Work and any modifications or additions
50
- to that Work or Derivative Works thereof, that is intentionally
51
- submitted to Licensor for inclusion in the Work by the copyright owner
52
- or by an individual or Legal Entity authorized to submit on behalf of
53
- the copyright owner. For the purposes of this definition, "submitted"
54
- means any form of electronic, verbal, or written communication sent
55
- to the Licensor or its representatives, including but not limited to
56
- communication on electronic mailing lists, source code control systems,
57
- and issue tracking systems that are managed by, or on behalf of, the
58
- Licensor for the purpose of discussing and improving the Work, but
59
- excluding communication that is conspicuously marked or otherwise
60
- designated in writing by the copyright owner as "Not a Contribution."
61
-
62
- "Contributor" shall mean Licensor and any individual or Legal Entity
63
- on behalf of whom a Contribution has been received by Licensor and
64
- subsequently incorporated within the Work.
65
-
66
- 2. Grant of Copyright License. Subject to the terms and conditions of
67
- this License, each Contributor hereby grants to You a perpetual,
68
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
- copyright license to reproduce, prepare Derivative Works of,
70
- publicly display, publicly perform, sublicense, and distribute the
71
- Work and such Derivative Works in Source or Object form.
72
-
73
- 3. Grant of Patent License. Subject to the terms and conditions of
74
- this License, each Contributor hereby grants to You a perpetual,
75
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
- (except as stated in this section) patent license to make, have made,
77
- use, offer to sell, sell, import, and otherwise transfer the Work,
78
- where such license applies only to those patent claims licensable
79
- by such Contributor that are necessarily infringed by their
80
- Contribution(s) alone or by combination of their Contribution(s)
81
- with other Contributions to which such Contribution(s) was submitted.
82
- If You institute patent litigation against any entity (including a
83
- cross-claim or counterclaim in a lawsuit) alleging that the Work
84
- or a Contribution incorporated within the Work constitutes direct
85
- or contributory patent infringement, then any patent licenses
86
- granted to You under this License for that Work shall terminate
87
- as of the date such litigation is instituted.
88
-
89
- 4. Redistribution. You may reproduce and distribute copies of the
90
- Work or Derivative Works thereof in any medium, with or without
91
- modifications, and in Source or Object form, provided that You
92
- meet the following conditions:
93
-
94
- (a) You must give any other recipients of the Work or
95
- Derivative Works a copy of this License; and
96
-
97
- (b) You must cause any modified files to carry prominent notices
98
- stating that You changed the files; and
99
-
100
- (c) You must retain, in the form of any Derivative Works that
101
- You distribute, all copyright, patent, trademark, and
102
- attribution notices from the Source form of the Work,
103
- excluding those notices that do not pertain to any part of
104
- the Derivative Works; and
105
-
106
- (d) If the Work includes a "NOTICE" text file as part of its
107
- distribution, then any Derivative Works that You distribute must
108
- include a readable copy of the NOTICE file contained within the
109
- Work, along with any attribution notices contained within such
110
- NOTICE file, excluding those notices that do not pertain to any
111
- part of the Derivative Works.
112
-
113
- You may add Your own attribution notices within Derivative Works
114
- that You distribute, alongside the attribution notices from the
115
- Source form of the Work, provided that such additional attribution
116
- notices cannot be construed as modifying or changing the License
117
- granted by the Licensor.
118
-
119
- 5. Submission of Contributions. Unless You explicitly state otherwise,
120
- any Contribution intentionally submitted for inclusion in the Work
121
- by You to the Licensor shall be under the terms and conditions of
122
- this License, without any additional terms or conditions.
123
- Notwithstanding the above, nothing herein shall supersede or modify
124
- the terms of any separate license agreement you may have executed
125
- with Licensor regarding such Contributions.
126
-
127
- 6. Trademarks. This License does not grant permission to use the trade
128
- names, trademarks, service marks, or product names of the Licensor,
129
- except as required for reasonable and customary use in describing the
130
- origin of the Work and reproducing the content of the NOTICE file.
131
-
132
- 7. Disclaimer of Warranty. Unless required by applicable law or
133
- agreed to in writing, Licensor provides the Work (and each
134
- Contributor provides its Contributions) on an "AS IS" BASIS,
135
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
136
- implied, including, without limitation, warranties or conditions
137
- of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
138
- NONINFRINGEMENT. The Licensor does not warrant that the Work will
139
- operate uninterrupted or error-free, or that the Work will meet
140
- Your specific requirements.
141
-
142
- 8. Limitation of Liability. In no event and under no legal theory,
143
- whether in tort (including negligence), contract, or otherwise,
144
- unless required by applicable law or agreed to in writing,
145
- shall any Contributor be liable to You for damages, including any
146
- direct, indirect, special, incidental, or consequential damages
147
- of any character arising as a result of this License or use of the
148
- Work (including without limitation damages for loss of goodwill,
149
- work stoppage, computer failure or malfunction, or any other
150
- damages), even if such Contributor has been advised of the
151
- possibility of such damages.
152
-
153
- 9. Accepting Warranty or Additional Liability. While redistributing
154
- the Work or Derivative Works thereof, You may choose to offer,
155
- and charge a fee for, acceptance of support, warranty, or
156
- liability obligations consistent with this License. However, in
157
- accepting such obligations, You act solely on Your own behalf and
158
- on Your own risk, and You accept full responsibility for any
159
- consequences of such acceptance.
160
-
161
- END OF TERMS AND CONDITIONS
162
-
163
- APPENDIX: How to apply the Apache License to your work.
164
-
165
- To apply the Apache License to your work, attach the following
166
- boilerplate notice, with the fields enclosed by brackets "[]"
167
- replaced with your own identifying information. (Don't include
168
- the brackets!) The text should be wrapped in the appropriate
169
- comment syntax for the file format. We also recommend that a
170
- statement such as the above be included in the file header
171
- comments.
172
-
173
- Copyright 2026 ryopc org
174
-
175
- Licensed under the Apache License, Version 2.0 (the "License");
176
- you may not use this file except in compliance with the License.
177
- You may obtain a copy of the License at
178
-
179
- http://www.apache.org/licenses/LICENSE-2.0
180
-
181
- Unless required by applicable law or agreed to in writing, software
182
- distributed under the License is distributed on an "AS IS" BASIS,
183
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
184
- See the License for the specific language governing permissions and
185
- limitations under the License.
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ryopc org
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  > **Antigravity-level agentic autonomy meets the ultimate simplicity of self-hosting.**
4
4
 
5
- [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
5
+ [![License](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
6
  [![Node.js](https://img.shields.io/badge/Node.js-%3E%3D22-green.svg)](https://nodejs.org/)
7
7
  [![npm](https://img.shields.io/npm/v/@game_ryo/lsji.svg)](https://www.npmjs.com/package/@game_ryo/lsji)
8
8
 
@@ -178,11 +178,11 @@ Environment variables:
178
178
 
179
179
  - [ ] **v0.4** — Multi-agent orchestration, A2A protocol
180
180
  - [ ] **v0.5** — WASM sandbox for code_exec, vector memory
181
- - [ ] **v1.0** — ASF incubation, stability guarantees
181
+ - [ ] **v1.0** — Linux Foundation Sandbox, stability guarantees
182
182
 
183
183
  ## License
184
184
 
185
- Apache License 2.0 — see [LICENSE](LICENSE) for details.
185
+ MIT License — see [LICENSE](LICENSE) for details.
186
186
 
187
187
  Copyright 2026 ryopc org
188
188
 
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@game_ryo/lsji",
3
- "version": "1.0.0",
3
+ "version": "1.2.0",
4
4
  "description": "A general-purpose reinforcement learning agent framework (Node.js) with LLM agent capabilities",
5
- "license": "Apache-2.0",
5
+ "license": "MIT",
6
6
  "type": "module",
7
7
  "main": "./src/index.js",
8
8
  "bin": {
package/src/cli.js CHANGED
@@ -591,9 +591,9 @@ Usage: lsji <command> [options]
591
591
  --hand <num> Your hand: 0=Rock, 1=Scissors, 2=Paper
592
592
 
593
593
  === LLM Agent Options ===
594
- --provider <name> LLM provider: openai, anthropic, local (default: openai)
595
- --model <name> Model name (default: gpt-4o-mini)
596
- --api-key <key> API key (or use OPENAI_API_KEY env)
594
+ --provider <name> LLM provider: openai, anthropic, gemini, local (default: openai)
595
+ --model <name> Model name (default: gpt-4o-mini, gemini: gemini-1.5-flash)
596
+ --api-key <key> API key (or use OPENAI_API_KEY, GEMINI_API_KEY, ANTHROPIC_API_KEY env)
597
597
  --max-cost <num> Max cost per run USD (default: 10)
598
598
  --max-tokens <num> Max tokens per run (default: 100000)
599
599
  --hitl <true|false> Enable HITL (default: true)
@@ -122,12 +122,16 @@ export class QLearning {
122
122
  const currentQ = this.getQValue(state, action);
123
123
 
124
124
  // Find max Q-value for next state
125
- let maxNextQ = 0;
125
+ // BUG FIX: Initialize to -Infinity to correctly handle negative Q-values
126
+ let maxNextQ = -Infinity;
126
127
  for (let a = 0; a < nextActionSize; a++) {
127
128
  const q = this.getQValue(nextState, a);
128
129
  if (q > maxNextQ) maxNextQ = q;
129
130
  }
130
131
 
132
+ // Handle case where all Q-values are unseen (0)
133
+ if (maxNextQ === -Infinity) maxNextQ = 0;
134
+
131
135
  // TD update: Q(s,a) = Q(s,a) + alpha * (reward + gamma * max Q(s',a') - Q(s,a))
132
136
  const target = reward + this.gamma * maxNextQ;
133
137
  const newQ = currentQ + this.alpha * (target - currentQ);
@@ -24,6 +24,7 @@ export const CircuitState = {
24
24
  * @property {number} [timeWindow] - Time window for threshold (ms), default 1 hour
25
25
  * @property {number} [resetTimeout] - Time before trying half-open (ms), default 5 min
26
26
  * @property {number} [failureThreshold] - Number of failures before opening
27
+ * @property {CostTracker} [costTracker] - Cost tracker instance (optional, uses global if not provided)
27
28
  */
28
29
 
29
30
  /**
@@ -39,11 +40,13 @@ export class CircuitBreaker {
39
40
  failureThreshold: config.failureThreshold || 5,
40
41
  };
41
42
 
43
+ // BUG FIX: Allow injecting costTracker instead of hardcoding global
44
+ this.costTracker = config.costTracker || globalCostTracker;
45
+
42
46
  this.state = CircuitState.CLOSED;
43
47
  this.failureCount = 0;
44
48
  this.lastFailureTime = null;
45
49
  this.lastStateChange = Date.now();
46
- this.costTracker = globalCostTracker;
47
50
  this.listeners = new Map(); // event -> callbacks
48
51
  }
49
52
 
@@ -46,6 +46,7 @@ export class CostTracker {
46
46
  this.runCosts = new Map(); // budgetId -> accumulated cost
47
47
  this.runTokens = new Map(); // budgetId -> accumulated tokens
48
48
  this.alerts = [];
49
+ this.listeners = new Map(); // event -> callbacks
49
50
  }
50
51
 
51
52
  /**
@@ -151,20 +152,54 @@ export class CostTracker {
151
152
  }
152
153
 
153
154
  /**
154
- * Trigger alert
155
+ * Trigger alert - Node.js compatible event emitter pattern
155
156
  */
156
157
  triggerAlert(type, data) {
157
158
  const alert = { type, data, timestamp: new Date() };
158
159
  this.alerts.push(alert);
159
160
 
160
- // Emit event for external handlers
161
- if (typeof window !== 'undefined' && window.dispatchEvent) {
162
- window.dispatchEvent(new CustomEvent('lsji:budget-alert', { detail: alert }));
163
- }
161
+ // Emit event for external handlers (Node.js compatible)
162
+ this.emit('alert', alert);
164
163
 
165
164
  return alert;
166
165
  }
167
166
 
167
+ /**
168
+ * Add event listener
169
+ */
170
+ on(event, callback) {
171
+ if (!this.listeners.has(event)) {
172
+ this.listeners.set(event, []);
173
+ }
174
+ this.listeners.get(event).push(callback);
175
+ }
176
+
177
+ /**
178
+ * Remove event listener
179
+ */
180
+ off(event, callback) {
181
+ if (this.listeners.has(event)) {
182
+ const callbacks = this.listeners.get(event);
183
+ const index = callbacks.indexOf(callback);
184
+ if (index >= 0) callbacks.splice(index, 1);
185
+ }
186
+ }
187
+
188
+ /**
189
+ * Emit event
190
+ */
191
+ emit(event, data) {
192
+ if (this.listeners.has(event)) {
193
+ for (const callback of this.listeners.get(event)) {
194
+ try {
195
+ callback(data);
196
+ } catch (e) {
197
+ console.error(`Cost tracker listener error:`, e);
198
+ }
199
+ }
200
+ }
201
+ }
202
+
168
203
  /**
169
204
  * Get alerts
170
205
  */
@@ -6,7 +6,7 @@
6
6
  */
7
7
 
8
8
  import { createStorage } from '../index.js';
9
- import { IdempotencyStore, createIdempotencyStore } from './idempotency.js';
9
+ import { createIdempotencyStore } from './idempotency.js';
10
10
  import { v4 as uuidv4 } from 'uuid';
11
11
 
12
12
  /**
@@ -185,7 +185,9 @@ export class ExecutionEngine {
185
185
  try {
186
186
  const stepResult = await stepFn(context);
187
187
  results.push({ step: i, success: true, result: stepResult });
188
- context = { ...context, [steps[i].name || `step_${i}`]: stepResult };
188
+ // BUG FIX: Safely access step name
189
+ const stepName = steps[i].name || `step_${i}`;
190
+ context = { ...context, [stepName]: stepResult };
189
191
  } catch (error) {
190
192
  results.push({ step: i, success: false, error: error.message });
191
193
 
@@ -7,6 +7,7 @@
7
7
 
8
8
  import { createStorage } from '../index.js';
9
9
  import { v4 as uuidv4 } from 'uuid';
10
+ import { createHash } from 'crypto';
10
11
 
11
12
  /**
12
13
  * Idempotency record
@@ -80,16 +81,39 @@ export class IdempotencyStore {
80
81
 
81
82
  /**
82
83
  * Create hash of request for deduplication
84
+ * BUG FIX: Use native Web Crypto API instead of require('crypto')
83
85
  */
84
86
  hashRequest(request) {
85
- const crypto = require('crypto');
86
- return crypto.createHash('sha256').update(JSON.stringify(request)).digest('hex');
87
+ const data = JSON.stringify(request);
88
+ const hashBuffer = createHash('sha256').update(data).digest();
89
+ return Buffer.from(hashBuffer).toString('hex');
90
+ }
91
+
92
+ /**
93
+ * Transform database row or memory record to standard format
94
+ */
95
+ _transformRecord(record) {
96
+ return {
97
+ key: record.key,
98
+ operation: record.operation,
99
+ requestHash: record.request_hash || record.requestHash,
100
+ request: (record.request_data || record.requestData) ? JSON.parse(record.request_data || record.requestData) : null,
101
+ response: (record.response_data || record.responseData) ? JSON.parse(record.response_data || record.responseData) : null,
102
+ status: record.status,
103
+ createdAt: record.created_at || record.createdAt,
104
+ completedAt: record.completed_at || record.completedAt,
105
+ expiresAt: record.expires_at || record.expiresAt,
106
+ };
87
107
  }
88
108
 
89
109
  /**
90
110
  * Check if key exists and return cached response
111
+ * BUG FIX: Added request parameter to verify hash matches
112
+ * @param {string} key - Idempotency key
113
+ * @param {Object} [request] - Optional request to verify hash against stored hash
114
+ * @returns {Promise<Object|null>} Cached record or null
91
115
  */
92
- async check(key) {
116
+ async check(key, request = null) {
93
117
  await this.initialize();
94
118
 
95
119
  if (this.isSqlStorage()) {
@@ -99,24 +123,30 @@ export class IdempotencyStore {
99
123
  );
100
124
 
101
125
  if (row) {
102
- return {
103
- key: row.key,
104
- operation: row.operation,
105
- requestHash: row.request_hash,
106
- request: row.request_data ? JSON.parse(row.request_data) : null,
107
- response: row.response_data ? JSON.parse(row.response_data) : null,
108
- status: row.status,
109
- createdAt: row.created_at,
110
- completedAt: row.completed_at,
111
- expiresAt: row.expires_at,
112
- };
126
+ // BUG FIX: Verify request hash matches if request provided
127
+ if (request !== null) {
128
+ const currentHash = this.hashRequest(request);
129
+ if (row.request_hash !== currentHash) {
130
+ // Hash mismatch - different request with same key, treat as new
131
+ return null;
132
+ }
133
+ }
134
+
135
+ return this._transformRecord(row);
113
136
  }
114
137
  } else {
115
138
  // Memory fallback
116
139
  if (!this.memoryKeys) this.memoryKeys = new Map();
117
140
  const record = this.memoryKeys.get(key);
118
141
  if (record && new Date(record.expiresAt) > new Date()) {
119
- return record;
142
+ // BUG FIX: Verify request hash matches if request provided
143
+ if (request !== null) {
144
+ const currentHash = this.hashRequest(request);
145
+ if (record.requestHash !== currentHash) {
146
+ return null;
147
+ }
148
+ }
149
+ return this._transformRecord(record);
120
150
  }
121
151
  }
122
152
 
@@ -211,10 +241,11 @@ export class IdempotencyStore {
211
241
 
212
242
  /**
213
243
  * Execute operation with idempotency
244
+ * BUG FIX: Pass request to check() for hash verification
214
245
  */
215
246
  async execute(key, operation, request, executor) {
216
- // Check for existing
217
- const existing = await this.check(key);
247
+ // Check for existing - BUG FIX: Pass request for hash verification
248
+ const existing = await this.check(key, request);
218
249
  if (existing) {
219
250
  if (existing.status === 'completed') {
220
251
  return { ...existing.response, idempotent: true };
@@ -281,17 +312,7 @@ export class IdempotencyStore {
281
312
  'SELECT * FROM idempotency_keys WHERE operation = ? ORDER BY created_at DESC LIMIT ?',
282
313
  [operation, limit]
283
314
  );
284
- return rows.map(r => ({
285
- key: r.key,
286
- operation: r.operation,
287
- requestHash: r.request_hash,
288
- request: r.request_data ? JSON.parse(r.request_data) : null,
289
- response: r.response_data ? JSON.parse(r.response_data) : null,
290
- status: r.status,
291
- createdAt: r.created_at,
292
- completedAt: r.completed_at,
293
- expiresAt: r.expires_at,
294
- }));
315
+ return rows.map(r => this._transformRecord(r));
295
316
  }
296
317
 
297
318
  return [];
package/src/index.js CHANGED
@@ -64,6 +64,7 @@ export {
64
64
  createProvider,
65
65
  OpenAIProvider,
66
66
  AnthropicProvider,
67
+ GeminiProvider,
67
68
  LocalProvider,
68
69
  ToolRegistry,
69
70
  createToolRegistry,
package/src/llm/index.js CHANGED
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Exports all LLM agent components:
5
5
  * - LLMAgent: Main agent class
6
- * - Providers: OpenAI, Anthropic, Local
6
+ * - Providers: OpenAI, Anthropic, Gemini, Local
7
7
  * - Tools: Registry and built-in tools
8
8
  * - Memory: Conversation, Semantic, Episodic
9
9
  * - PromptManager: Template management
@@ -13,6 +13,7 @@ export { LLMAgent, createLLMAgent } from './llm-agent.js';
13
13
  export { LLMProvider, createProvider } from './providers/base.js';
14
14
  export { OpenAIProvider } from './providers/openai.js';
15
15
  export { AnthropicProvider } from './providers/anthropic.js';
16
+ export { GeminiProvider } from './providers/gemini.js';
16
17
  export { LocalProvider } from './providers/local.js';
17
18
  export { ToolRegistry, createToolRegistry } from './tools/registry.js';
18
19
  export { ConversationMemory, createConversationMemory } from './memory/conversation.js';
@@ -105,6 +105,10 @@ export async function createProvider(config) {
105
105
  const { AnthropicProvider } = await import('./anthropic.js');
106
106
  return new AnthropicProvider(options);
107
107
  }
108
+ case 'gemini': {
109
+ const { GeminiProvider } = await import('./gemini.js');
110
+ return new GeminiProvider(options);
111
+ }
108
112
  case 'local':
109
113
  case 'ollama': {
110
114
  const { LocalProvider } = await import('./local.js');