@everworker/oneringai 0.2.1 → 0.2.3

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/README.md CHANGED
@@ -27,8 +27,9 @@
27
27
  - [13. Streaming](#13-streaming)
28
28
  - [14. OAuth for External APIs](#14-oauth-for-external-apis)
29
29
  - [15. Developer Tools](#15-developer-tools)
30
- - [16. Document Reader](#16-document-reader-new) — PDF, DOCX, XLSX, PPTX, CSV, HTML, images
31
- - [17. External API Integration](#17-external-api-integration) — Scoped Registry, Vendor Templates, Tool Discovery
30
+ - [16. Custom Tool Generation](#16-custom-tool-generation-new) — Agents create, test, and persist their own tools
31
+ - [17. Document Reader](#17-document-reader) — PDF, DOCX, XLSX, PPTX, CSV, HTML, images
32
+ - [18. External API Integration](#18-external-api-integration) — Scoped Registry, Vendor Templates, Tool Discovery
32
33
  - [MCP Integration](#mcp-model-context-protocol-integration)
33
34
  - [Documentation](#documentation)
34
35
  - [Examples](#examples)
@@ -55,6 +56,15 @@
55
56
 
56
57
  ---
57
58
 
59
+ ## HOSEA APP
60
+ We realize that library alone in these times is not enough to get you excited, so we built a FREE FOREVER desktop app on top of this library to showcase its power! It's as easy to start using as cloning this library's repo, and then `cd apps/hosea` and then `npm install` and then `npm run dev`. Or watch the video first:
61
+
62
+ [![Watch the demo](https://img.youtube.com/vi/_LzDiuOQD8Y/maxresdefault.jpg)](https://www.youtube.com/watch?v=_LzDiuOQD8Y)
63
+
64
+ Or read the more detailed installation / setup instructions [here](https://github.com/Integrail/oneringai/blob/main/apps/hosea/README.md)
65
+
66
+ Better to see once and then dig in the code! :)
67
+
58
68
  ## Features
59
69
 
60
70
  - ✨ **Unified API** - One interface for 10+ AI providers (OpenAI, Anthropic, Google, Groq, DeepSeek, and more)
@@ -78,6 +88,7 @@
78
88
  - 📝 **Persistent Instructions** - NEW: Agent-level custom instructions that persist across sessions on disk
79
89
  - 🛠️ **Agentic Workflows** - Built-in tool calling and multi-turn conversations
80
90
  - 🔧 **Developer Tools** - NEW: Filesystem and shell tools for coding assistants (read, write, edit, grep, glob, bash)
91
+ - 🧰 **Custom Tool Generation** - NEW: Let agents create, test, and persist their own reusable tools at runtime — complete meta-tool system with VM sandbox
81
92
  - 🖥️ **Desktop Automation** - NEW: OS-level computer use — screenshot, mouse, keyboard, and window control for vision-driven agent loops
82
93
  - 📄 **Document Reader** - NEW: Universal file-to-text converter — PDF, DOCX, XLSX, PPTX, CSV, HTML, images auto-converted to markdown
83
94
  - 🔌 **MCP Integration** - NEW: Model Context Protocol client for seamless tool discovery from local and remote servers
@@ -311,7 +322,7 @@ const response = await agent.run([
311
322
  Connector-based web search with multiple providers:
312
323
 
313
324
  ```typescript
314
- import { Connector, SearchProvider, Services, webSearch, Agent } from '@everworker/oneringai';
325
+ import { Connector, SearchProvider, ConnectorTools, Services, Agent, tools } from '@everworker/oneringai';
315
326
 
316
327
  // Create search connector
317
328
  Connector.create({
@@ -329,11 +340,13 @@ const results = await search.search('latest AI developments 2026', {
329
340
  language: 'en',
330
341
  });
331
342
 
332
- // Option 2: Use with Agent
343
+ // Option 2: Use with Agent via ConnectorTools
344
+ const searchTools = ConnectorTools.for('serper-main');
345
+
333
346
  const agent = Agent.create({
334
347
  connector: 'openai',
335
348
  model: 'gpt-4',
336
- tools: [webSearch],
349
+ tools: [...searchTools, tools.webFetch],
337
350
  });
338
351
 
339
352
  await agent.run('Search for quantum computing news and summarize');
@@ -350,7 +363,7 @@ await agent.run('Search for quantum computing news and summarize');
350
363
  Enterprise web scraping with automatic fallback and bot protection bypass:
351
364
 
352
365
  ```typescript
353
- import { Connector, ScrapeProvider, Services, webScrape, Agent } from '@everworker/oneringai';
366
+ import { Connector, ScrapeProvider, ConnectorTools, Services, Agent, tools } from '@everworker/oneringai';
354
367
 
355
368
  // Create ZenRows connector for bot-protected sites
356
369
  Connector.create({
@@ -370,19 +383,24 @@ const result = await scraper.scrape('https://protected-site.com', {
370
383
  },
371
384
  });
372
385
 
373
- // Option 2: Use webScrape tool with Agent
386
+ // Option 2: Use web_scrape tool with Agent via ConnectorTools
387
+ const scrapeTools = ConnectorTools.for('zenrows');
388
+
374
389
  const agent = Agent.create({
375
390
  connector: 'openai',
376
391
  model: 'gpt-4',
377
- tools: [webScrape],
392
+ tools: [...scrapeTools, tools.webFetch],
378
393
  });
379
394
 
380
- // webScrape auto-falls back: native → JS → API
395
+ // web_scrape auto-falls back: native → API
381
396
  await agent.run('Scrape https://example.com and summarize');
382
397
  ```
383
398
 
384
399
  **Supported Scrape Providers:**
385
400
  - **ZenRows** - Enterprise scraping with JS rendering, residential proxies, anti-bot bypass
401
+ - **Jina Reader** - Clean content extraction with AI-powered readability
402
+ - **Firecrawl** - Web scraping with JavaScript rendering
403
+ - **ScrapingBee** - Headless browser scraping with proxy rotation
386
404
 
387
405
  ## Supported Providers
388
406
 
@@ -392,7 +410,7 @@ await agent.run('Scrape https://example.com and summarize');
392
410
  | **Anthropic (Claude)** | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ✅ | 200K |
393
411
  | **Google (Gemini)** | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ | 1M |
394
412
  | **Google Vertex AI** | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ✅ | 1M |
395
- | **Grok (xAI)** | ✅ | ✅ | ❌ | ❌ | | | ✅ | 128K |
413
+ | **Grok (xAI)** | ✅ | ✅ | ❌ | ❌ | | | ✅ | 128K |
396
414
  | **Groq** | ✅ | ❌ | ❌ | ✅ | ❌ | ❌ | ✅ | 128K |
397
415
  | **Together AI** | ✅ | Some | ❌ | ❌ | ❌ | ❌ | ✅ | 128K |
398
416
  | **DeepSeek** | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | 64K |
@@ -634,7 +652,7 @@ await agent.run('Check weather for SF and remember the result');
634
652
  Use `Agent` with search tools and `WorkingMemoryPluginNextGen` for research workflows:
635
653
 
636
654
  ```typescript
637
- import { Agent, webSearch, SearchProvider, Connector, Services } from '@everworker/oneringai';
655
+ import { Agent, ConnectorTools, Connector, Services, tools } from '@everworker/oneringai';
638
656
 
639
657
  // Setup search connector
640
658
  Connector.create({
@@ -645,10 +663,12 @@ Connector.create({
645
663
  });
646
664
 
647
665
  // Create agent with search and memory
666
+ const searchTools = ConnectorTools.for('serper-main');
667
+
648
668
  const agent = Agent.create({
649
669
  connector: 'openai',
650
670
  model: 'gpt-4',
651
- tools: [webSearch],
671
+ tools: [...searchTools, tools.webFetch],
652
672
  context: {
653
673
  features: { workingMemory: true },
654
674
  },
@@ -1015,7 +1035,42 @@ await agent.run('Run npm test and report any failures');
1015
1035
  - Timeout protection (default 2 min)
1016
1036
  - Output truncation for large outputs
1017
1037
 
1018
- ### 16. Desktop Automation Tools (NEW)
1038
+ ### 16. Custom Tool Generation (NEW)
1039
+
1040
+ Let agents **create their own tools** at runtime — draft, test, iterate, save, and reuse. The agent writes JavaScript code, validates it, tests it in the VM sandbox, and persists it for future use. All 6 meta-tools are auto-registered and visible in Hosea.
1041
+
1042
+ ```typescript
1043
+ import { createCustomToolMetaTools, hydrateCustomTool } from '@everworker/oneringai';
1044
+
1045
+ // Give an agent the ability to create tools
1046
+ const agent = Agent.create({
1047
+ connector: 'openai',
1048
+ model: 'gpt-4',
1049
+ tools: [...createCustomToolMetaTools()],
1050
+ });
1051
+
1052
+ // The agent can now: draft → test → save tools autonomously
1053
+ await agent.run('Create a tool that fetches weather data from the OpenWeather API');
1054
+
1055
+ // Later: load and use a saved tool
1056
+ import { createFileCustomToolStorage } from '@everworker/oneringai';
1057
+ const storage = createFileCustomToolStorage();
1058
+ const definition = await storage.load('fetch_weather');
1059
+ const weatherTool = hydrateCustomTool(definition!);
1060
+
1061
+ // Register on any agent
1062
+ agent.tools.register(weatherTool, { source: 'custom', tags: ['weather', 'api'] });
1063
+ ```
1064
+
1065
+ **Meta-Tools:** `custom_tool_draft` (validate), `custom_tool_test` (execute in sandbox), `custom_tool_save` (persist), `custom_tool_list` (search), `custom_tool_load` (retrieve), `custom_tool_delete` (remove)
1066
+
1067
+ **Dynamic Descriptions:** Draft and test tools use `descriptionFactory` to show all available connectors and the full sandbox API — automatically updated when connectors are added or removed.
1068
+
1069
+ **Pluggable Storage:** Default `FileCustomToolStorage` saves to `~/.oneringai/custom-tools/`. Implement `ICustomToolStorage` for MongoDB, S3, or any backend.
1070
+
1071
+ > See the [User Guide](./USER_GUIDE.md#custom-tool-generation) for the complete workflow, sandbox API reference, and examples.
1072
+
1073
+ ### 17. Desktop Automation Tools (NEW)
1019
1074
 
1020
1075
  OS-level desktop automation for building "computer use" agents — screenshot the screen, send to a vision model, receive tool calls (click, type, etc.), execute them, repeat:
1021
1076
 
@@ -1051,7 +1106,7 @@ await agent.run('Open Safari and search for "weather forecast"');
1051
1106
  - Screenshots use the `__images` convention for automatic multimodal handling across all providers (Anthropic, OpenAI, Google)
1052
1107
  - Requires `@nut-tree-fork/nut-js` as an optional peer dependency: `npm install @nut-tree-fork/nut-js`
1053
1108
 
1054
- ### 17. Document Reader (NEW)
1109
+ ### 18. Document Reader
1055
1110
 
1056
1111
  Universal file-to-LLM-content converter. Reads arbitrary document formats and produces clean markdown text with optional image extraction:
1057
1112
 
@@ -1116,7 +1171,7 @@ await agent.run([
1116
1171
 
1117
1172
  See the [User Guide](./USER_GUIDE.md#document-reader) for complete API reference and configuration options.
1118
1173
 
1119
- ### 18. External API Integration
1174
+ ### 19. External API Integration
1120
1175
 
1121
1176
  Connect your AI agents to 35+ external services with enterprise-grade resilience:
1122
1177
 
@@ -1468,4 +1523,4 @@ MIT License - See [LICENSE](./LICENSE) file.
1468
1523
 
1469
1524
  ---
1470
1525
 
1471
- **Version:** 0.2.1 | **Last Updated:** 2026-02-11 | **[User Guide](./USER_GUIDE.md)** | **[API Reference](./API_REFERENCE.md)** | **[Changelog](./CHANGELOG.md)**
1526
+ **Version:** 0.2.1 | **Last Updated:** 2026-02-17 | **[User Guide](./USER_GUIDE.md)** | **[API Reference](./API_REFERENCE.md)** | **[Changelog](./CHANGELOG.md)**
@@ -342,14 +342,32 @@ var AuthCodePKCEFlow = class {
342
342
  if (this.config.usePKCE !== false && verifierData) {
343
343
  params.append("code_verifier", verifierData.verifier);
344
344
  }
345
- const response = await fetch(this.config.tokenUrl, {
345
+ let response = await fetch(this.config.tokenUrl, {
346
346
  method: "POST",
347
347
  headers: {
348
348
  "Content-Type": "application/x-www-form-urlencoded"
349
349
  },
350
350
  body: params
351
351
  });
352
- if (!response.ok) {
352
+ if (!response.ok && this.config.clientSecret) {
353
+ const errorText = await response.text();
354
+ if (isPublicClientError(errorText)) {
355
+ params.delete("client_secret");
356
+ response = await fetch(this.config.tokenUrl, {
357
+ method: "POST",
358
+ headers: {
359
+ "Content-Type": "application/x-www-form-urlencoded"
360
+ },
361
+ body: params
362
+ });
363
+ if (!response.ok) {
364
+ const retryError = await response.text();
365
+ throw new Error(`Token exchange failed: ${response.status} ${response.statusText} - ${retryError}`);
366
+ }
367
+ } else {
368
+ throw new Error(`Token exchange failed: ${response.status} ${response.statusText} - ${errorText}`);
369
+ }
370
+ } else if (!response.ok) {
353
371
  const error = await response.text();
354
372
  throw new Error(`Token exchange failed: ${response.status} ${response.statusText} - ${error}`);
355
373
  }
@@ -395,14 +413,32 @@ var AuthCodePKCEFlow = class {
395
413
  if (this.config.clientSecret) {
396
414
  params.append("client_secret", this.config.clientSecret);
397
415
  }
398
- const response = await fetch(this.config.tokenUrl, {
416
+ let response = await fetch(this.config.tokenUrl, {
399
417
  method: "POST",
400
418
  headers: {
401
419
  "Content-Type": "application/x-www-form-urlencoded"
402
420
  },
403
421
  body: params
404
422
  });
405
- if (!response.ok) {
423
+ if (!response.ok && this.config.clientSecret) {
424
+ const errorText = await response.text();
425
+ if (isPublicClientError(errorText)) {
426
+ params.delete("client_secret");
427
+ response = await fetch(this.config.tokenUrl, {
428
+ method: "POST",
429
+ headers: {
430
+ "Content-Type": "application/x-www-form-urlencoded"
431
+ },
432
+ body: params
433
+ });
434
+ if (!response.ok) {
435
+ const retryError = await response.text();
436
+ throw new Error(`Token refresh failed: ${response.status} ${response.statusText} - ${retryError}`);
437
+ }
438
+ } else {
439
+ throw new Error(`Token refresh failed: ${response.status} ${response.statusText} - ${errorText}`);
440
+ }
441
+ } else if (!response.ok) {
406
442
  const error = await response.text();
407
443
  throw new Error(`Token refresh failed: ${response.status} ${response.statusText} - ${error}`);
408
444
  }
@@ -457,6 +493,10 @@ var AuthCodePKCEFlow = class {
457
493
  }
458
494
  }
459
495
  };
496
+ function isPublicClientError(responseBody) {
497
+ const lower = responseBody.toLowerCase();
498
+ return lower.includes("aadsts700025") || lower.includes("invalid_client") && lower.includes("public");
499
+ }
460
500
 
461
501
  // src/connectors/oauth/flows/ClientCredentials.ts
462
502
  var ClientCredentialsFlow = class {