@calliopelabs/cli 0.6.7 → 0.6.9

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 (59) hide show
  1. package/dist/agterm/agent-detection.d.ts +35 -0
  2. package/dist/agterm/agent-detection.d.ts.map +1 -0
  3. package/dist/agterm/agent-detection.js +94 -0
  4. package/dist/agterm/agent-detection.js.map +1 -0
  5. package/dist/agterm/cli-backend.d.ts +32 -0
  6. package/dist/agterm/cli-backend.d.ts.map +1 -0
  7. package/dist/agterm/cli-backend.js +193 -0
  8. package/dist/agterm/cli-backend.js.map +1 -0
  9. package/dist/agterm/index.d.ts +12 -0
  10. package/dist/agterm/index.d.ts.map +1 -0
  11. package/dist/agterm/index.js +15 -0
  12. package/dist/agterm/index.js.map +1 -0
  13. package/dist/agterm/orchestrator.d.ts +94 -0
  14. package/dist/agterm/orchestrator.d.ts.map +1 -0
  15. package/dist/agterm/orchestrator.js +302 -0
  16. package/dist/agterm/orchestrator.js.map +1 -0
  17. package/dist/agterm/tools.d.ts +24 -0
  18. package/dist/agterm/tools.d.ts.map +1 -0
  19. package/dist/agterm/tools.js +277 -0
  20. package/dist/agterm/tools.js.map +1 -0
  21. package/dist/agterm/types.d.ts +95 -0
  22. package/dist/agterm/types.d.ts.map +1 -0
  23. package/dist/agterm/types.js +38 -0
  24. package/dist/agterm/types.js.map +1 -0
  25. package/dist/bin.d.ts +2 -1
  26. package/dist/bin.d.ts.map +1 -1
  27. package/dist/bin.js +18 -3
  28. package/dist/bin.js.map +1 -1
  29. package/dist/errors.d.ts +5 -2
  30. package/dist/errors.d.ts.map +1 -1
  31. package/dist/errors.js +107 -4
  32. package/dist/errors.js.map +1 -1
  33. package/dist/plugins.d.ts +120 -0
  34. package/dist/plugins.d.ts.map +1 -0
  35. package/dist/plugins.js +356 -0
  36. package/dist/plugins.js.map +1 -0
  37. package/dist/providers.js +208 -58
  38. package/dist/providers.js.map +1 -1
  39. package/dist/scope.d.ts +89 -0
  40. package/dist/scope.d.ts.map +1 -0
  41. package/dist/scope.js +253 -0
  42. package/dist/scope.js.map +1 -0
  43. package/dist/storage.d.ts +23 -0
  44. package/dist/storage.d.ts.map +1 -1
  45. package/dist/storage.js +83 -0
  46. package/dist/storage.js.map +1 -1
  47. package/dist/styles.d.ts +49 -83
  48. package/dist/styles.d.ts.map +1 -1
  49. package/dist/styles.js +140 -126
  50. package/dist/styles.js.map +1 -1
  51. package/dist/tools.d.ts +5 -0
  52. package/dist/tools.d.ts.map +1 -1
  53. package/dist/tools.js +24 -10
  54. package/dist/tools.js.map +1 -1
  55. package/dist/ui-cli.d.ts +1 -0
  56. package/dist/ui-cli.d.ts.map +1 -1
  57. package/dist/ui-cli.js +877 -107
  58. package/dist/ui-cli.js.map +1 -1
  59. package/package.json +1 -1
@@ -0,0 +1,356 @@
1
+ /**
2
+ * Calliope CLI - Plugin System
3
+ *
4
+ * Enables community-extensible tools and providers.
5
+ * Plugins are loaded from ~/.calliope-cli/plugins/
6
+ */
7
+ import * as fs from 'fs';
8
+ import * as path from 'path';
9
+ import * as os from 'os';
10
+ // ============================================================================
11
+ // Plugin Manager
12
+ // ============================================================================
13
+ class PluginManager {
14
+ plugins = new Map();
15
+ pluginsDir;
16
+ constructor() {
17
+ this.pluginsDir = path.join(os.homedir(), '.calliope-cli', 'plugins');
18
+ }
19
+ /**
20
+ * Initialize the plugin system
21
+ */
22
+ async init() {
23
+ // Ensure plugins directory exists
24
+ if (!fs.existsSync(this.pluginsDir)) {
25
+ fs.mkdirSync(this.pluginsDir, { recursive: true });
26
+ }
27
+ // Load all plugins
28
+ await this.loadAllPlugins();
29
+ }
30
+ /**
31
+ * Load all plugins from the plugins directory
32
+ */
33
+ async loadAllPlugins() {
34
+ if (!fs.existsSync(this.pluginsDir))
35
+ return;
36
+ const entries = fs.readdirSync(this.pluginsDir, { withFileTypes: true });
37
+ for (const entry of entries) {
38
+ if (entry.isDirectory()) {
39
+ await this.loadPlugin(entry.name);
40
+ }
41
+ }
42
+ }
43
+ /**
44
+ * Load a single plugin by name
45
+ */
46
+ async loadPlugin(name) {
47
+ const pluginPath = path.join(this.pluginsDir, name);
48
+ const manifestPath = path.join(pluginPath, 'plugin.json');
49
+ const indexPath = path.join(pluginPath, 'index.js');
50
+ try {
51
+ // Check for manifest
52
+ if (!fs.existsSync(manifestPath)) {
53
+ console.warn(`Plugin ${name}: Missing plugin.json`);
54
+ return null;
55
+ }
56
+ // Read manifest
57
+ const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf-8'));
58
+ // Check for entry point
59
+ if (!fs.existsSync(indexPath)) {
60
+ console.warn(`Plugin ${name}: Missing index.js`);
61
+ return null;
62
+ }
63
+ // Dynamic import the plugin
64
+ const pluginModule = await import(indexPath);
65
+ const plugin = pluginModule.default || pluginModule;
66
+ // Merge manifest with plugin
67
+ plugin.metadata = { ...manifest, ...plugin.metadata };
68
+ // Initialize plugin if it has init function
69
+ if (plugin.init) {
70
+ await plugin.init();
71
+ }
72
+ const loaded = {
73
+ id: name,
74
+ path: pluginPath,
75
+ plugin,
76
+ enabled: true,
77
+ loadedAt: new Date(),
78
+ };
79
+ this.plugins.set(name, loaded);
80
+ return loaded;
81
+ }
82
+ catch (error) {
83
+ const errorMsg = error instanceof Error ? error.message : String(error);
84
+ console.error(`Failed to load plugin ${name}:`, errorMsg);
85
+ const failed = {
86
+ id: name,
87
+ path: pluginPath,
88
+ plugin: { metadata: { name, version: '0.0.0', description: 'Failed to load' } },
89
+ enabled: false,
90
+ loadedAt: new Date(),
91
+ error: errorMsg,
92
+ };
93
+ this.plugins.set(name, failed);
94
+ return failed;
95
+ }
96
+ }
97
+ /**
98
+ * Unload a plugin
99
+ */
100
+ async unloadPlugin(name) {
101
+ const loaded = this.plugins.get(name);
102
+ if (!loaded)
103
+ return false;
104
+ try {
105
+ if (loaded.plugin.cleanup) {
106
+ await loaded.plugin.cleanup();
107
+ }
108
+ this.plugins.delete(name);
109
+ return true;
110
+ }
111
+ catch (error) {
112
+ console.error(`Failed to unload plugin ${name}:`, error);
113
+ return false;
114
+ }
115
+ }
116
+ /**
117
+ * Enable/disable a plugin
118
+ */
119
+ setPluginEnabled(name, enabled) {
120
+ const loaded = this.plugins.get(name);
121
+ if (!loaded)
122
+ return false;
123
+ loaded.enabled = enabled;
124
+ return true;
125
+ }
126
+ /**
127
+ * Get all loaded plugins
128
+ */
129
+ getPlugins() {
130
+ return Array.from(this.plugins.values());
131
+ }
132
+ /**
133
+ * Get enabled plugins
134
+ */
135
+ getEnabledPlugins() {
136
+ return Array.from(this.plugins.values()).filter(p => p.enabled && !p.error);
137
+ }
138
+ /**
139
+ * Get a specific plugin
140
+ */
141
+ getPlugin(name) {
142
+ return this.plugins.get(name);
143
+ }
144
+ /**
145
+ * Get all tools from enabled plugins
146
+ */
147
+ getPluginTools() {
148
+ const tools = [];
149
+ for (const loaded of this.getEnabledPlugins()) {
150
+ if (loaded.plugin.tools) {
151
+ for (const tool of loaded.plugin.tools) {
152
+ tools.push({
153
+ name: `${loaded.id}:${tool.name}`,
154
+ description: `[${loaded.id}] ${tool.description}`,
155
+ parameters: tool.parameters,
156
+ });
157
+ }
158
+ }
159
+ }
160
+ return tools;
161
+ }
162
+ /**
163
+ * Execute a plugin tool
164
+ */
165
+ async executePluginTool(toolCall, cwd) {
166
+ const [pluginId, toolName] = toolCall.name.split(':');
167
+ const loaded = this.plugins.get(pluginId);
168
+ if (!loaded || !loaded.enabled) {
169
+ return {
170
+ toolCallId: toolCall.id,
171
+ result: `Plugin not found or disabled: ${pluginId}`,
172
+ isError: true,
173
+ };
174
+ }
175
+ const tool = loaded.plugin.tools?.find(t => t.name === toolName);
176
+ if (!tool) {
177
+ return {
178
+ toolCallId: toolCall.id,
179
+ result: `Tool not found: ${toolName} in plugin ${pluginId}`,
180
+ isError: true,
181
+ };
182
+ }
183
+ try {
184
+ const result = await tool.execute(toolCall.arguments, cwd);
185
+ return { toolCallId: toolCall.id, result };
186
+ }
187
+ catch (error) {
188
+ return {
189
+ toolCallId: toolCall.id,
190
+ result: `Plugin tool error: ${error instanceof Error ? error.message : String(error)}`,
191
+ isError: true,
192
+ };
193
+ }
194
+ }
195
+ /**
196
+ * Check if a tool name is a plugin tool
197
+ */
198
+ isPluginTool(name) {
199
+ return name.includes(':');
200
+ }
201
+ /**
202
+ * Execute hooks for an event
203
+ */
204
+ async executeHooks(event, context) {
205
+ for (const loaded of this.getEnabledPlugins()) {
206
+ if (loaded.plugin.hooks) {
207
+ for (const hook of loaded.plugin.hooks) {
208
+ if (hook.event === event) {
209
+ try {
210
+ await hook.handler(context);
211
+ }
212
+ catch (error) {
213
+ console.error(`Plugin hook error (${loaded.id}):`, error);
214
+ }
215
+ }
216
+ }
217
+ }
218
+ }
219
+ }
220
+ /**
221
+ * Get plugins directory path
222
+ */
223
+ getPluginsDir() {
224
+ return this.pluginsDir;
225
+ }
226
+ /**
227
+ * Create a new plugin scaffold
228
+ */
229
+ createPluginScaffold(name) {
230
+ const pluginPath = path.join(this.pluginsDir, name);
231
+ if (fs.existsSync(pluginPath)) {
232
+ throw new Error(`Plugin directory already exists: ${name}`);
233
+ }
234
+ fs.mkdirSync(pluginPath, { recursive: true });
235
+ // Create plugin.json
236
+ const manifest = {
237
+ name,
238
+ version: '1.0.0',
239
+ description: `${name} plugin for Calliope CLI`,
240
+ author: 'Your Name',
241
+ };
242
+ fs.writeFileSync(path.join(pluginPath, 'plugin.json'), JSON.stringify(manifest, null, 2));
243
+ // Create index.js
244
+ const indexContent = `/**
245
+ * ${name} - Calliope CLI Plugin
246
+ */
247
+
248
+ module.exports = {
249
+ // Plugin tools
250
+ tools: [
251
+ {
252
+ name: 'example',
253
+ description: 'An example tool from ${name} plugin',
254
+ parameters: {
255
+ type: 'object',
256
+ properties: {
257
+ message: {
258
+ type: 'string',
259
+ description: 'A message to echo',
260
+ },
261
+ },
262
+ required: ['message'],
263
+ },
264
+ execute: async (args, cwd) => {
265
+ return \`[${name}] Echo: \${args.message}\`;
266
+ },
267
+ },
268
+ ],
269
+
270
+ // Plugin hooks (optional)
271
+ hooks: [
272
+ // {
273
+ // event: 'session-start',
274
+ // handler: async (context) => {
275
+ // console.log('${name} plugin: Session started');
276
+ // },
277
+ // },
278
+ ],
279
+
280
+ // Called when plugin is loaded (optional)
281
+ init: async () => {
282
+ console.log('${name} plugin initialized');
283
+ },
284
+
285
+ // Called when plugin is unloaded (optional)
286
+ cleanup: async () => {
287
+ console.log('${name} plugin cleanup');
288
+ },
289
+ };
290
+ `;
291
+ fs.writeFileSync(path.join(pluginPath, 'index.js'), indexContent);
292
+ return pluginPath;
293
+ }
294
+ /**
295
+ * Get formatted plugin list for display
296
+ */
297
+ getPluginListFormatted() {
298
+ const plugins = this.getPlugins();
299
+ if (plugins.length === 0) {
300
+ return `No plugins installed.
301
+
302
+ To create a plugin:
303
+ /plugin create <name>
304
+
305
+ Plugins directory: ${this.pluginsDir}`;
306
+ }
307
+ const lines = ['Installed Plugins:', ''];
308
+ for (const p of plugins) {
309
+ const status = p.error ? '❌' : p.enabled ? '✅' : '⏸️';
310
+ const version = p.plugin.metadata.version;
311
+ const toolCount = p.plugin.tools?.length || 0;
312
+ lines.push(`${status} ${p.id} v${version}`);
313
+ lines.push(` ${p.plugin.metadata.description}`);
314
+ if (toolCount > 0) {
315
+ lines.push(` Tools: ${p.plugin.tools.map(t => t.name).join(', ')}`);
316
+ }
317
+ if (p.error) {
318
+ lines.push(` Error: ${p.error}`);
319
+ }
320
+ lines.push('');
321
+ }
322
+ return lines.join('\n');
323
+ }
324
+ }
325
+ // ============================================================================
326
+ // Singleton Instance
327
+ // ============================================================================
328
+ export const pluginManager = new PluginManager();
329
+ // ============================================================================
330
+ // Convenience Functions
331
+ // ============================================================================
332
+ export async function initPlugins() {
333
+ await pluginManager.init();
334
+ }
335
+ export function getPluginTools() {
336
+ return pluginManager.getPluginTools();
337
+ }
338
+ export function isPluginTool(name) {
339
+ return pluginManager.isPluginTool(name);
340
+ }
341
+ export async function executePluginTool(toolCall, cwd) {
342
+ return pluginManager.executePluginTool(toolCall, cwd);
343
+ }
344
+ export function getPluginList() {
345
+ return pluginManager.getPluginListFormatted();
346
+ }
347
+ export function createPlugin(name) {
348
+ return pluginManager.createPluginScaffold(name);
349
+ }
350
+ export async function reloadPlugins() {
351
+ await pluginManager.loadAllPlugins();
352
+ }
353
+ export function enablePlugin(name, enabled) {
354
+ return pluginManager.setPluginEnabled(name, enabled);
355
+ }
356
+ //# sourceMappingURL=plugins.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugins.js","sourceRoot":"","sources":["../src/plugins.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AAiDzB,+EAA+E;AAC/E,iBAAiB;AACjB,+EAA+E;AAE/E,MAAM,aAAa;IACT,OAAO,GAA8B,IAAI,GAAG,EAAE,CAAC;IAC/C,UAAU,CAAS;IAE3B;QACE,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,eAAe,EAAE,SAAS,CAAC,CAAC;IACxE,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,IAAI;QACR,kCAAkC;QAClC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;YACpC,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACrD,CAAC;QAED,mBAAmB;QACnB,MAAM,IAAI,CAAC,cAAc,EAAE,CAAC;IAC9B,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,cAAc;QAClB,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,UAAU,CAAC;YAAE,OAAO;QAE5C,MAAM,OAAO,GAAG,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,UAAU,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;QAEzE,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC5B,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;gBACxB,MAAM,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YACpC,CAAC;QACH,CAAC;IACH,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,UAAU,CAAC,IAAY;QAC3B,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;QACpD,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,aAAa,CAAC,CAAC;QAC1D,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;QAEpD,IAAI,CAAC;YACH,qBAAqB;YACrB,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;gBACjC,OAAO,CAAC,IAAI,CAAC,UAAU,IAAI,uBAAuB,CAAC,CAAC;gBACpD,OAAO,IAAI,CAAC;YACd,CAAC;YAED,gBAAgB;YAChB,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,YAAY,EAAE,OAAO,CAAC,CAAmB,CAAC;YAEtF,wBAAwB;YACxB,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;gBAC9B,OAAO,CAAC,IAAI,CAAC,UAAU,IAAI,oBAAoB,CAAC,CAAC;gBACjD,OAAO,IAAI,CAAC;YACd,CAAC;YAED,4BAA4B;YAC5B,MAAM,YAAY,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,CAAC;YAC7C,MAAM,MAAM,GAAW,YAAY,CAAC,OAAO,IAAI,YAAY,CAAC;YAE5D,6BAA6B;YAC7B,MAAM,CAAC,QAAQ,GAAG,EAAE,GAAG,QAAQ,EAAE,GAAG,MAAM,CAAC,QAAQ,EAAE,CAAC;YAEtD,4CAA4C;YAC5C,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;gBAChB,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;YACtB,CAAC;YAED,MAAM,MAAM,GAAiB;gBAC3B,EAAE,EAAE,IAAI;gBACR,IAAI,EAAE,UAAU;gBAChB,MAAM;gBACN,OAAO,EAAE,IAAI;gBACb,QAAQ,EAAE,IAAI,IAAI,EAAE;aACrB,CAAC;YAEF,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;YAC/B,OAAO,MAAM,CAAC;QAEhB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,QAAQ,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACxE,OAAO,CAAC,KAAK,CAAC,yBAAyB,IAAI,GAAG,EAAE,QAAQ,CAAC,CAAC;YAE1D,MAAM,MAAM,GAAiB;gBAC3B,EAAE,EAAE,IAAI;gBACR,IAAI,EAAE,UAAU;gBAChB,MAAM,EAAE,EAAE,QAAQ,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,EAAE;gBAC/E,OAAO,EAAE,KAAK;gBACd,QAAQ,EAAE,IAAI,IAAI,EAAE;gBACpB,KAAK,EAAE,QAAQ;aAChB,CAAC;YAEF,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;YAC/B,OAAO,MAAM,CAAC;QAChB,CAAC;IACH,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,YAAY,CAAC,IAAY;QAC7B,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACtC,IAAI,CAAC,MAAM;YAAE,OAAO,KAAK,CAAC;QAE1B,IAAI,CAAC;YACH,IAAI,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;gBAC1B,MAAM,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YAChC,CAAC;YACD,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YAC1B,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,2BAA2B,IAAI,GAAG,EAAE,KAAK,CAAC,CAAC;YACzD,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAED;;OAEG;IACH,gBAAgB,CAAC,IAAY,EAAE,OAAgB;QAC7C,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACtC,IAAI,CAAC,MAAM;YAAE,OAAO,KAAK,CAAC;QAC1B,MAAM,CAAC,OAAO,GAAG,OAAO,CAAC;QACzB,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;OAEG;IACH,UAAU;QACR,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAC3C,CAAC;IAED;;OAEG;IACH,iBAAiB;QACf,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;IAC9E,CAAC;IAED;;OAEG;IACH,SAAS,CAAC,IAAY;QACpB,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAChC,CAAC;IAED;;OAEG;IACH,cAAc;QACZ,MAAM,KAAK,GAAW,EAAE,CAAC;QAEzB,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,iBAAiB,EAAE,EAAE,CAAC;YAC9C,IAAI,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;gBACxB,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;oBACvC,KAAK,CAAC,IAAI,CAAC;wBACT,IAAI,EAAE,GAAG,MAAM,CAAC,EAAE,IAAI,IAAI,CAAC,IAAI,EAAE;wBACjC,WAAW,EAAE,IAAI,MAAM,CAAC,EAAE,KAAK,IAAI,CAAC,WAAW,EAAE;wBACjD,UAAU,EAAE,IAAI,CAAC,UAAgC;qBAClD,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;QACH,CAAC;QAED,OAAO,KAAK,CAAC;IACf,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,iBAAiB,CACrB,QAAkB,EAClB,GAAW;QAEX,MAAM,CAAC,QAAQ,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAEtD,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAC1C,IAAI,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YAC/B,OAAO;gBACL,UAAU,EAAE,QAAQ,CAAC,EAAE;gBACvB,MAAM,EAAE,iCAAiC,QAAQ,EAAE;gBACnD,OAAO,EAAE,IAAI;aACd,CAAC;QACJ,CAAC;QAED,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC;QACjE,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,OAAO;gBACL,UAAU,EAAE,QAAQ,CAAC,EAAE;gBACvB,MAAM,EAAE,mBAAmB,QAAQ,cAAc,QAAQ,EAAE;gBAC3D,OAAO,EAAE,IAAI;aACd,CAAC;QACJ,CAAC;QAED,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,CAC/B,QAAQ,CAAC,SAAoC,EAC7C,GAAG,CACJ,CAAC;YACF,OAAO,EAAE,UAAU,EAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,EAAE,CAAC;QAC7C,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO;gBACL,UAAU,EAAE,QAAQ,CAAC,EAAE;gBACvB,MAAM,EAAE,sBAAsB,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE;gBACtF,OAAO,EAAE,IAAI;aACd,CAAC;QACJ,CAAC;IACH,CAAC;IAED;;OAEG;IACH,YAAY,CAAC,IAAY;QACvB,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;IAC5B,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,YAAY,CAChB,KAA0B,EAC1B,OAAgC;QAEhC,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,iBAAiB,EAAE,EAAE,CAAC;YAC9C,IAAI,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;gBACxB,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;oBACvC,IAAI,IAAI,CAAC,KAAK,KAAK,KAAK,EAAE,CAAC;wBACzB,IAAI,CAAC;4BACH,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;wBAC9B,CAAC;wBAAC,OAAO,KAAK,EAAE,CAAC;4BACf,OAAO,CAAC,KAAK,CAAC,sBAAsB,MAAM,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC;wBAC5D,CAAC;oBACH,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED;;OAEG;IACH,aAAa;QACX,OAAO,IAAI,CAAC,UAAU,CAAC;IACzB,CAAC;IAED;;OAEG;IACH,oBAAoB,CAAC,IAAY;QAC/B,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;QAEpD,IAAI,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;YAC9B,MAAM,IAAI,KAAK,CAAC,oCAAoC,IAAI,EAAE,CAAC,CAAC;QAC9D,CAAC;QAED,EAAE,CAAC,SAAS,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAE9C,qBAAqB;QACrB,MAAM,QAAQ,GAAmB;YAC/B,IAAI;YACJ,OAAO,EAAE,OAAO;YAChB,WAAW,EAAE,GAAG,IAAI,0BAA0B;YAC9C,MAAM,EAAE,WAAW;SACpB,CAAC;QACF,EAAE,CAAC,aAAa,CACd,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,aAAa,CAAC,EACpC,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAClC,CAAC;QAEF,kBAAkB;QAClB,MAAM,YAAY,GAAG;KACpB,IAAI;;;;;;;;2CAQkC,IAAI;;;;;;;;;;;;oBAY3B,IAAI;;;;;;;;;;0BAUE,IAAI;;;;;;;mBAOX,IAAI;;;;;mBAKJ,IAAI;;;CAGtB,CAAC;QACE,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,UAAU,CAAC,EAAE,YAAY,CAAC,CAAC;QAElE,OAAO,UAAU,CAAC;IACpB,CAAC;IAED;;OAEG;IACH,sBAAsB;QACpB,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,EAAE,CAAC;QAElC,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACzB,OAAO;;;;;qBAKQ,IAAI,CAAC,UAAU,EAAE,CAAC;QACnC,CAAC;QAED,MAAM,KAAK,GAAG,CAAC,oBAAoB,EAAE,EAAE,CAAC,CAAC;QAEzC,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;YACxB,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC;YACtD,MAAM,OAAO,GAAG,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC;YAC1C,MAAM,SAAS,GAAG,CAAC,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,IAAI,CAAC,CAAC;YAE9C,KAAK,CAAC,IAAI,CAAC,GAAG,MAAM,IAAI,CAAC,CAAC,EAAE,KAAK,OAAO,EAAE,CAAC,CAAC;YAC5C,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAC;YAClD,IAAI,SAAS,GAAG,CAAC,EAAE,CAAC;gBAClB,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,MAAM,CAAC,KAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YACzE,CAAC;YACD,IAAI,CAAC,CAAC,KAAK,EAAE,CAAC;gBACZ,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;YACrC,CAAC;YACD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACjB,CAAC;QAED,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;CACF;AAED,+EAA+E;AAC/E,qBAAqB;AACrB,+EAA+E;AAE/E,MAAM,CAAC,MAAM,aAAa,GAAG,IAAI,aAAa,EAAE,CAAC;AAEjD,+EAA+E;AAC/E,wBAAwB;AACxB,+EAA+E;AAE/E,MAAM,CAAC,KAAK,UAAU,WAAW;IAC/B,MAAM,aAAa,CAAC,IAAI,EAAE,CAAC;AAC7B,CAAC;AAED,MAAM,UAAU,cAAc;IAC5B,OAAO,aAAa,CAAC,cAAc,EAAE,CAAC;AACxC,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,OAAO,aAAa,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;AAC1C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,QAAkB,EAClB,GAAW;IAEX,OAAO,aAAa,CAAC,iBAAiB,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;AACxD,CAAC;AAED,MAAM,UAAU,aAAa;IAC3B,OAAO,aAAa,CAAC,sBAAsB,EAAE,CAAC;AAChD,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,OAAO,aAAa,CAAC,oBAAoB,CAAC,IAAI,CAAC,CAAC;AAClD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa;IACjC,MAAM,aAAa,CAAC,cAAc,EAAE,CAAC;AACvC,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,IAAY,EAAE,OAAgB;IACzD,OAAO,aAAa,CAAC,gBAAgB,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;AACvD,CAAC"}
package/dist/providers.js CHANGED
@@ -305,35 +305,89 @@ async function chatAnthropic(messages, tools, model, onToken) {
305
305
  description: t.description,
306
306
  input_schema: t.parameters,
307
307
  }));
308
- // Use streaming if callback provided and no tools (streaming tool use is complex)
309
- if (onToken && anthropicTools.length === 0) {
308
+ // Use streaming if callback provided - handles both text and tool calls
309
+ if (onToken) {
310
310
  let content = '';
311
311
  let inputTokens = 0;
312
312
  let outputTokens = 0;
313
- const stream = await client.messages.stream({
314
- model,
315
- max_tokens: MAX_TOKENS,
316
- system: systemMessage ? getTextContent(systemMessage.content) : '',
317
- messages: anthropicMessages,
318
- });
319
- for await (const event of stream) {
320
- if (event.type === 'content_block_delta' && event.delta.type === 'text_delta') {
321
- const text = event.delta.text;
322
- content += text;
323
- onToken(text);
324
- }
325
- else if (event.type === 'message_delta' && event.usage) {
326
- outputTokens = event.usage.output_tokens;
327
- }
328
- else if (event.type === 'message_start' && event.message.usage) {
329
- inputTokens = event.message.usage.input_tokens;
313
+ const toolCalls = [];
314
+ let currentToolId = '';
315
+ let currentToolName = '';
316
+ let currentToolInput = '';
317
+ let finishReason = 'stop';
318
+ try {
319
+ const stream = await client.messages.stream({
320
+ model,
321
+ max_tokens: MAX_TOKENS,
322
+ system: systemMessage ? getTextContent(systemMessage.content) : '',
323
+ messages: anthropicMessages,
324
+ tools: anthropicTools.length > 0 ? anthropicTools : undefined,
325
+ });
326
+ for await (const event of stream) {
327
+ if (event.type === 'content_block_start') {
328
+ if (event.content_block.type === 'tool_use') {
329
+ currentToolId = event.content_block.id;
330
+ currentToolName = event.content_block.name;
331
+ currentToolInput = '';
332
+ }
333
+ }
334
+ else if (event.type === 'content_block_delta') {
335
+ if (event.delta.type === 'text_delta') {
336
+ const text = event.delta.text;
337
+ content += text;
338
+ onToken(text);
339
+ }
340
+ else if (event.delta.type === 'input_json_delta') {
341
+ currentToolInput += event.delta.partial_json;
342
+ }
343
+ }
344
+ else if (event.type === 'content_block_stop') {
345
+ if (currentToolId && currentToolName) {
346
+ try {
347
+ toolCalls.push({
348
+ id: currentToolId,
349
+ name: currentToolName,
350
+ arguments: JSON.parse(currentToolInput || '{}'),
351
+ });
352
+ }
353
+ catch {
354
+ toolCalls.push({
355
+ id: currentToolId,
356
+ name: currentToolName,
357
+ arguments: {},
358
+ });
359
+ }
360
+ currentToolId = '';
361
+ currentToolName = '';
362
+ currentToolInput = '';
363
+ }
364
+ }
365
+ else if (event.type === 'message_delta') {
366
+ if (event.usage) {
367
+ outputTokens = event.usage.output_tokens;
368
+ }
369
+ if (event.delta.stop_reason === 'tool_use') {
370
+ finishReason = 'tool_use';
371
+ }
372
+ else if (event.delta.stop_reason === 'max_tokens') {
373
+ finishReason = 'length';
374
+ }
375
+ }
376
+ else if (event.type === 'message_start' && event.message.usage) {
377
+ inputTokens = event.message.usage.input_tokens;
378
+ }
330
379
  }
380
+ return {
381
+ content,
382
+ toolCalls: toolCalls.length > 0 ? toolCalls : undefined,
383
+ finishReason,
384
+ usage: { inputTokens, outputTokens },
385
+ };
386
+ }
387
+ catch (streamError) {
388
+ // Fall back to non-streaming on error
389
+ console.error('Anthropic streaming failed, falling back:', streamError);
331
390
  }
332
- return {
333
- content,
334
- finishReason: 'stop',
335
- usage: { inputTokens, outputTokens },
336
- };
337
391
  }
338
392
  // Non-streaming request
339
393
  const response = await client.messages.create({
@@ -452,26 +506,74 @@ async function chatOpenAI(messages, tools, model, onToken) {
452
506
  const client = new OpenAI({ apiKey });
453
507
  const openaiMessages = toOpenAIMessages(messages);
454
508
  const openaiTools = toOpenAITools(tools);
455
- // Use streaming if callback provided and no tools
456
- if (onToken && openaiTools.length === 0) {
509
+ // Use streaming if callback provided
510
+ // Stream text content while collecting tool calls
511
+ if (onToken) {
457
512
  let content = '';
458
- const stream = await client.chat.completions.create({
459
- model,
460
- messages: openaiMessages,
461
- max_tokens: MAX_TOKENS,
462
- stream: true,
463
- });
464
- for await (const chunk of stream) {
465
- const delta = chunk.choices[0]?.delta?.content;
466
- if (delta) {
467
- content += delta;
468
- onToken(delta);
513
+ let toolCallDeltas = {};
514
+ let finishReason = 'stop';
515
+ try {
516
+ const stream = await client.chat.completions.create({
517
+ model,
518
+ messages: openaiMessages,
519
+ tools: openaiTools.length > 0 ? openaiTools : undefined,
520
+ max_tokens: MAX_TOKENS,
521
+ stream: true,
522
+ });
523
+ for await (const chunk of stream) {
524
+ const choice = chunk.choices[0];
525
+ if (!choice)
526
+ continue;
527
+ // Handle text content
528
+ const textDelta = choice.delta?.content;
529
+ if (textDelta) {
530
+ content += textDelta;
531
+ onToken(textDelta);
532
+ }
533
+ // Handle tool calls (collect deltas)
534
+ const toolCallDelta = choice.delta?.tool_calls;
535
+ if (toolCallDelta) {
536
+ for (const tc of toolCallDelta) {
537
+ if (!toolCallDeltas[tc.index]) {
538
+ toolCallDeltas[tc.index] = { id: '', name: '', arguments: '' };
539
+ }
540
+ if (tc.id)
541
+ toolCallDeltas[tc.index].id = tc.id;
542
+ if (tc.function?.name)
543
+ toolCallDeltas[tc.index].name = tc.function.name;
544
+ if (tc.function?.arguments)
545
+ toolCallDeltas[tc.index].arguments += tc.function.arguments;
546
+ }
547
+ }
548
+ // Track finish reason
549
+ if (choice.finish_reason === 'tool_calls') {
550
+ finishReason = 'tool_use';
551
+ }
552
+ else if (choice.finish_reason === 'length') {
553
+ finishReason = 'length';
554
+ }
555
+ }
556
+ // Convert tool call deltas to tool calls
557
+ const toolCalls = Object.values(toolCallDeltas)
558
+ .filter(tc => tc.id && tc.name)
559
+ .map(tc => ({
560
+ id: tc.id,
561
+ name: tc.name,
562
+ arguments: JSON.parse(tc.arguments || '{}'),
563
+ }));
564
+ if (toolCalls.length > 0) {
565
+ finishReason = 'tool_use';
469
566
  }
567
+ return {
568
+ content,
569
+ toolCalls: toolCalls.length > 0 ? toolCalls : undefined,
570
+ finishReason,
571
+ };
572
+ }
573
+ catch (streamError) {
574
+ // Fall back to non-streaming on error
575
+ console.error('Streaming failed, falling back to non-streaming:', streamError);
470
576
  }
471
- return {
472
- content,
473
- finishReason: 'stop',
474
- };
475
577
  }
476
578
  // Non-streaming request
477
579
  const response = await client.chat.completions.create({
@@ -530,26 +632,74 @@ async function chatOpenAICompatible(provider, messages, tools, model, onToken) {
530
632
  const client = new OpenAI({ apiKey, baseURL });
531
633
  const openaiMessages = toOpenAIMessages(messages);
532
634
  const openaiTools = toOpenAITools(tools);
533
- // Use streaming if callback provided and no tools
534
- if (onToken && openaiTools.length === 0) {
635
+ // Use streaming if callback provided
636
+ // Stream text content while collecting tool calls
637
+ if (onToken) {
535
638
  let content = '';
536
- const stream = await client.chat.completions.create({
537
- model,
538
- messages: openaiMessages,
539
- max_tokens: MAX_TOKENS,
540
- stream: true,
541
- });
542
- for await (const chunk of stream) {
543
- const delta = chunk.choices[0]?.delta?.content;
544
- if (delta) {
545
- content += delta;
546
- onToken(delta);
639
+ let toolCallDeltas = {};
640
+ let finishReason = 'stop';
641
+ try {
642
+ const stream = await client.chat.completions.create({
643
+ model,
644
+ messages: openaiMessages,
645
+ tools: openaiTools.length > 0 ? openaiTools : undefined,
646
+ max_tokens: MAX_TOKENS,
647
+ stream: true,
648
+ });
649
+ for await (const chunk of stream) {
650
+ const choice = chunk.choices[0];
651
+ if (!choice)
652
+ continue;
653
+ // Handle text content
654
+ const textDelta = choice.delta?.content;
655
+ if (textDelta) {
656
+ content += textDelta;
657
+ onToken(textDelta);
658
+ }
659
+ // Handle tool calls (collect deltas)
660
+ const toolCallDelta = choice.delta?.tool_calls;
661
+ if (toolCallDelta) {
662
+ for (const tc of toolCallDelta) {
663
+ if (!toolCallDeltas[tc.index]) {
664
+ toolCallDeltas[tc.index] = { id: '', name: '', arguments: '' };
665
+ }
666
+ if (tc.id)
667
+ toolCallDeltas[tc.index].id = tc.id;
668
+ if (tc.function?.name)
669
+ toolCallDeltas[tc.index].name = tc.function.name;
670
+ if (tc.function?.arguments)
671
+ toolCallDeltas[tc.index].arguments += tc.function.arguments;
672
+ }
673
+ }
674
+ // Track finish reason
675
+ if (choice.finish_reason === 'tool_calls') {
676
+ finishReason = 'tool_use';
677
+ }
678
+ else if (choice.finish_reason === 'length') {
679
+ finishReason = 'length';
680
+ }
681
+ }
682
+ // Convert tool call deltas to tool calls
683
+ const toolCalls = Object.values(toolCallDeltas)
684
+ .filter(tc => tc.id && tc.name)
685
+ .map(tc => ({
686
+ id: tc.id,
687
+ name: tc.name,
688
+ arguments: JSON.parse(tc.arguments || '{}'),
689
+ }));
690
+ if (toolCalls.length > 0) {
691
+ finishReason = 'tool_use';
547
692
  }
693
+ return {
694
+ content,
695
+ toolCalls: toolCalls.length > 0 ? toolCalls : undefined,
696
+ finishReason,
697
+ };
698
+ }
699
+ catch (streamError) {
700
+ // Fall back to non-streaming on error
701
+ console.error('Streaming failed, falling back to non-streaming:', streamError);
548
702
  }
549
- return {
550
- content,
551
- finishReason: 'stop',
552
- };
553
703
  }
554
704
  // Non-streaming request
555
705
  const response = await client.chat.completions.create({