saico 2.3.0 → 2.4.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 (7) hide show
  1. package/README.md +287 -300
  2. package/index.js +1 -4
  3. package/itask.js +16 -3
  4. package/msgs.js +35 -81
  5. package/package.json +1 -2
  6. package/saico.js +307 -35
  7. package/sid.js +0 -248
package/README.md CHANGED
@@ -1,398 +1,385 @@
1
- # Saico - Simple AI-agent Conversation Orchestrator
1
+ # Saico - Hierarchical AI Conversation Orchestrator
2
2
 
3
- `Saico` is a minimal yet powerful JavaScript/Node.js library for managing AI conversations with hierarchical context, token-aware summarization, and **enterprise-grade tool calling capabilities**. It's designed to support complex nested conversations while maintaining clean summaries and parent context, making it ideal for AI agents, assistants, and customer support bots.
3
+ Saico is a Node.js library for building AI agents with hierarchical conversations, automatic context aggregation, and enterprise-grade tool calling. It manages nested task trees where each node can have its own conversation context, system prompt, tools, and state and the library automatically assembles the full payload sent to the LLM by walking the tree.
4
4
 
5
- ---
5
+ ## Features
6
6
 
7
- ## Features
7
+ - **Hierarchical conversations** — Parent-child task trees with automatic prompt, tool, and state summary aggregation
8
+ - **Token-aware summarization** — Automatic summarization when message history approaches token limits
9
+ - **Tool calling** — Depth control, deferred execution, duplicate detection, repetition prevention, and timeout handling
10
+ - **Pluggable storage** — Optional Redis persistence (auto-save via proxy) and pluggable DB backends (DynamoDB adapter included)
11
+ - **Isolation boundaries** — `opt.isolate` stops ancestor aggregation at any node in the tree
12
+ - **Serialization** — Full state save/restore for long-running agents
8
13
 
9
- - 📚 **Hierarchical Conversations** — Track parent-child chat contexts with summary propagation.
10
- - 🧵 **Scoped Memory** — Manage sub-conversations independently while maintaining parent relevance.
11
- - 🔁 **Token-Aware Summarization** — Automatically summarize message history based on token thresholds.
12
- - 💬 **Message-Level Metadata** — Track reply state, summaries, and custom flags.
13
- - 🛠️ **OpenAI-Compatible Format** — Built for seamless interaction with OpenAI-compatible APIs.
14
- - 🧰 **Proxy-Based Interface** — Interact with message history like an array, with extra powers.
15
- - **🚀 NEW: Tool Calls** — Complete tool calling system with depth control, deferred execution, and safety features.
14
+ ## Installation
16
15
 
17
- ---
16
+ ```bash
17
+ npm install saico
18
+ ```
18
19
 
19
- ## 🔧 Tool Calls System
20
+ ## Quick Start
20
21
 
21
- Saico now includes a sophisticated tool calling system with enterprise-grade safety and control features:
22
+ ```js
23
+ const { Saico } = require('saico');
24
+
25
+ class MyAgent extends Saico {
26
+ constructor() {
27
+ super({
28
+ name: 'my-agent',
29
+ prompt: 'You are a helpful assistant.',
30
+ tool_handler: (name, args) => this.handleTool(name, args),
31
+ functions: [{
32
+ type: 'function',
33
+ function: {
34
+ name: 'get_weather',
35
+ description: 'Get weather for a location',
36
+ parameters: {
37
+ type: 'object',
38
+ properties: { location: { type: 'string' } },
39
+ required: ['location']
40
+ }
41
+ }
42
+ }]
43
+ });
44
+ }
22
45
 
23
- ### Key Features:
24
- - **🎛️ Depth Control** — Prevent infinite recursion with configurable depth limits
25
- - **🔄 Deferred Execution** — Tool calls automatically defer and resume when depth limits reached
26
- - **🚫 Duplicate Protection** Identical tool calls blocked while active to prevent resource waste
27
- - **⏱️ Timeout Handling** — Configurable timeouts (default: 5s) with graceful failure
28
- - **🔁 Repetition Prevention** — Block excessive repeated tool calls (default: 20 max)
29
- - **📥 Message Queuing** — Messages automatically queue when tool calls are pending
30
- - **👨‍👩‍👧‍👦 Parent-Child Inheritance** — Unresponded tool calls move from parent to child contexts
46
+ async handleTool(name, argsString) {
47
+ const args = JSON.parse(argsString);
48
+ if (name === 'get_weather')
49
+ return `Weather in ${args.location}: 72F, sunny`;
50
+ return 'Unknown tool';
51
+ }
52
+ }
31
53
 
32
- ---
54
+ const agent = new MyAgent();
55
+ agent.activate({ createQ: true });
33
56
 
34
- ## 📦 Installation
57
+ // Backend message (prefixed with [BACKEND] automatically)
58
+ const reply = await agent.sendMessage('What is the weather in Tokyo?');
35
59
 
36
- ```bash
37
- npm install saico-ai-thread --save
60
+ // User-facing chat message (routed to deepest active context)
61
+ const chatReply = await agent.recvChatMessage('Hello!');
38
62
  ```
39
63
 
40
- Or clone manually:
64
+ ## Core Concepts
41
65
 
42
- ```bash
43
- git clone https://github.com/wanderli-ai/saico
44
- cd saico
45
- ```
66
+ ### Saico Lifecycle
46
67
 
47
- ---
68
+ Saico separates construction from activation:
48
69
 
49
- ## 🧑‍💻 Usage
70
+ ```js
71
+ // 1. Construct — sets up config, Redis proxy, DB access. No task yet.
72
+ const agent = new Saico({
73
+ name: 'agent',
74
+ prompt: 'System prompt here',
75
+ dynamodb_table: 'my_data', // optional DB
76
+ });
50
77
 
51
- ### Basic Setup with Tool Handler
78
+ // DB methods work before activation
79
+ const item = await agent.dbGetItem('id', '123');
52
80
 
53
- ```js
54
- const { createQ } = require('saico');
81
+ // 2. Activate — creates internal task + optional message queue context
82
+ agent.activate({ createQ: true });
55
83
 
56
- // Define your tool handler
57
- async function toolHandler(toolName, argumentsString) {
58
- const args = JSON.parse(argumentsString);
59
-
60
- switch (toolName) {
61
- case 'get_weather':
62
- return `Weather in ${args.location}: 72°F, sunny`;
63
- case 'book_hotel':
64
- return `Booked ${args.hotel} for ${args.nights} nights`;
65
- default:
66
- return 'Tool not found';
67
- }
68
- }
84
+ // 3. Use send messages, spawn children
85
+ await agent.sendMessage('Do something');
86
+ await agent.recvChatMessage('User says hello');
69
87
 
70
- // Create conversation with tool support
71
- const q = createQ(
72
- "You are a helpful assistant.", // prompt
73
- null, // parent (null for root)
74
- "main", // tag
75
- 4000, // token limit
76
- null, // initial messages
77
- toolHandler, // tool handler function
78
- { max_depth: 5, max_tool_repetition: 20 } // config
79
- );
80
-
81
- // Send a message that might trigger tool calls
82
- await q.sendMessage('user', 'What\'s the weather in New York?');
88
+ // 4. Deactivate bubbles cleaned messages to parent, closes context
89
+ await agent.deactivate();
83
90
  ```
84
91
 
85
- ### Create a Sub-Conversation with Tool Inheritance
92
+ ### Message Orchestration
86
93
 
87
- ```js
88
- const subQ = q.spawnChild(
89
- "Now focus only on hotel bookings.", // prompt
90
- "hotels", // tag
91
- null, // token limit (inherits from parent)
92
- null, // initial messages
93
- null, // tool handler (inherits from parent)
94
- { max_depth: 3 } // custom config
95
- );
96
-
97
- await subQ.sendMessage('user', 'Book me something in Rome.');
98
- await subQ.close(); // Automatically summarizes and passes back to parent
94
+ When `sendMessage()` or `recvChatMessage()` is called, Saico walks the parent chain to build the full LLM payload:
95
+
96
+ ```
97
+ Root Saico (prompt: "You are a manager")
98
+ +-- Child Saico (prompt: "Handle bookings")
99
+ +-- Grandchild Saico (prompt: "Process payment")
100
+ sendMessage("Charge $50")
101
+ |
102
+ v
103
+ Preamble built automatically:
104
+ [Root prompt] [Root state summary] [Root tool digest]
105
+ [Child prompt] [Child state summary + recent msgs] [Child tool digest]
106
+ [Grandchild prompt] [Grandchild state summary]
107
+ ... then the actual message queue messages ...
108
+
109
+ Functions aggregated from all levels.
99
110
  ```
100
111
 
101
- ### Advanced Tool Configuration
112
+ - **`sendMessage(content, functions, opts)`** — Sends a backend message (auto-prefixed `[BACKEND]`). Uses the current or nearest ancestor context.
113
+ - **`recvChatMessage(content, opts)`** — Routes a user chat message DOWN to the deepest descendant with a message queue.
114
+
115
+ ### Isolation
116
+
117
+ Set `isolate: true` to prevent ancestor aggregation:
102
118
 
103
119
  ```js
104
- const q = createQ(
105
- "You are a travel assistant.",
106
- null,
107
- "travel",
108
- 8000,
109
- null,
110
- toolHandler,
111
- {
112
- max_depth: 8, // Allow deeper tool call chains
113
- max_tool_repetition: 10 // Be more strict about repetitions
114
- }
115
- );
116
-
117
- // Send message with custom tool options
118
- await q.sendMessage('user', 'Plan my trip', null, {
119
- handler: customToolHandler, // Override default tool handler
120
- timeout: 10000, // 10 second timeout for this message's tools
121
- nofunc: false // Ensure tool calls are enabled
120
+ const isolated = new Saico({
121
+ name: 'isolated-agent',
122
+ prompt: 'Independent context',
123
+ isolate: true // won't include parent prompts/tools/summaries
122
124
  });
123
125
  ```
124
126
 
125
- ### Hierarchy Example with Tool Calls
127
+ ### State Summaries
126
128
 
127
- ```text
128
- [Main] (toolHandler: generalTools)
129
- ├── [hotels] (inherits generalTools) ➜ tool calls + summary returned to [Main]
130
- └── [flights] (inherits generalTools) ➜ tool calls + summary returned to [Main]
131
- ```
129
+ Override `getStateSummary()` in your subclass to provide dynamic state context:
132
130
 
133
- ---
131
+ ```js
132
+ class OrderAgent extends Saico {
133
+ getStateSummary() {
134
+ return `Active order: #${this.orderId}, items: ${this.items.length}`;
135
+ }
136
+ }
137
+ ```
134
138
 
135
- ## 🧠 Enhanced Message API
139
+ When a Saico's context is not the deepest active one, its last 5 user/assistant messages are also included in the state summary automatically.
136
140
 
137
- Each message is stored with enhanced tool call support:
141
+ ### Spawning Child Tasks
138
142
 
139
143
  ```js
140
- {
141
- msg: {
142
- role, // 'user', 'assistant', 'tool', 'system'
143
- content, // Message content
144
- name?, // Optional name for user/tool messages
145
- tool_calls?, // Array of tool calls from assistant
146
- tool_call_id? // ID linking tool responses to calls
147
- },
148
- opts: {
149
- summary?, // Is this a summary message?
150
- noreply?, // Skip AI reply for this message
151
- nofunc?, // Disable tool calls for this message
152
- handler?, // Custom tool handler override
153
- timeout? // Custom timeout for tool calls
154
- },
155
- msgid: String, // Unique message identifier
156
- replied: 0 | 1 | 3 // 0=pending, 1=user sent, 3=AI replied
157
- }
144
+ // Child with its own conversation context
145
+ const child = agent.spawnTaskWithContext({
146
+ name: 'subtask',
147
+ prompt: 'Handle this specific sub-task',
148
+ tool_handler: (name, args) => handleSubTools(name, args),
149
+ functions: [/* child-specific tools */]
150
+ }, [
151
+ async function main() {
152
+ return await this.sendMessage('Working on subtask...');
153
+ }
154
+ ]);
155
+
156
+ // Child without context (uses parent's)
157
+ const simple = agent.spawnTask({ name: 'simple' }, [
158
+ async function main() {
159
+ await this.sendMessage('Quick operation');
160
+ }
161
+ ]);
158
162
  ```
159
163
 
160
- ### Enhanced API Methods
164
+ Child tasks inherit `sessionConfig` defaults (token_limit, max_depth, etc.) from the parent Saico.
161
165
 
162
- * `q[0]` Access nth message
163
- * `q.length` — Total messages
164
- * `q.pushSummary(summary)` — Manually inject a summary
165
- * `q.getMsgContext()` — Get summarized parent chain
166
- * `q.serialize()` — Export current state
167
- * **NEW**: `q._hasPendingToolCalls()` — Check for pending tool executions
168
- * **NEW**: `q._processWaitingQueue()` — Manually process queued messages
166
+ ### Deactivation and Message Bubbling
169
167
 
170
- ---
168
+ When a Saico deactivates, cleaned messages (no tool calls, no `[BACKEND]` messages) are pushed into the parent's message queue, preserving conversation continuity.
171
169
 
172
- ## 🛡️ Tool Call Safety Features
170
+ ## Constructor Options
173
171
 
174
- ### Depth Control & Deferred Execution
175
172
  ```js
176
- const q = createQ("Assistant", null, "main", 4000, null, toolHandler, {
177
- max_depth: 3 // Tool calls defer at depth 4+
173
+ new Saico({
174
+ // Identity
175
+ id: 'custom-id', // Auto-generated if omitted
176
+ name: 'my-agent', // Defaults to class name
177
+
178
+ // AI config
179
+ prompt: 'System prompt',
180
+ tool_handler: fn, // async (name, argsString) => result
181
+ functions: [], // OpenAI function definitions
182
+
183
+ // Behavior
184
+ isolate: false, // Stop ancestor aggregation
185
+
186
+ // Session config (defaults for this agent and its children)
187
+ token_limit: 4000,
188
+ max_depth: 5, // Max tool call recursion depth
189
+ max_tool_repetition: 20, // Max consecutive repeated tool calls
190
+ queue_limit: 100, // Message queue limit
191
+ min_chat_messages: 5, // Min messages to keep in queue
192
+ sessionConfig: {}, // Override any of the above
193
+
194
+ // Storage
195
+ redis: true, // Set false to skip Redis proxy
196
+ key: 'custom-redis-key',
197
+ dynamodb_table: 'table', // Auto-creates DynamoDB adapter
198
+ dynamodb_region: 'us-east-1',
199
+ db: customAdapter, // Any adapter with put/get/delete/query interface
200
+
201
+ // User data
202
+ userData: {}, // Arbitrary user metadata
178
203
  });
179
-
180
- // When max depth reached:
181
- // 1. Tool calls are deferred (not executed immediately)
182
- // 2. Conversation continues normally
183
- // 3. Deferred tools execute when depth reduces
184
- // 4. Results are seamlessly integrated back
185
204
  ```
186
205
 
187
- ### Repetition Prevention
206
+ ## Activate Options
207
+
188
208
  ```js
189
- const q = createQ("Assistant", null, "main", 4000, null, toolHandler, {
190
- max_tool_repetition: 5 // Block tools called >5 times consecutively
209
+ agent.activate({
210
+ createQ: true, // Create message queue context
211
+ prompt: 'Extra prompt', // Appended to class-level prompt
212
+ states: [], // Task state functions
213
+ parent: parentTask, // Parent task to spawn under
214
+ taskId: 'custom-id',
215
+ sequential_mode: true, // Process messages sequentially
216
+
217
+ // Override session config for this activation
218
+ token_limit: 8000,
219
+ max_depth: 10,
220
+ queue_limit: 200,
191
221
  });
192
-
193
- // Automatically filters excessive repeated tool calls
194
- // Logs: "Dropping excessive tool call: get_weather (hit max_tool_repetition=5)"
195
222
  ```
196
223
 
197
- ### Duplicate Detection
198
- ```js
199
- // If two identical tool calls (same name + arguments) are active:
200
- // Second call returns: "Duplicate call detected. Please wait for previous call to complete."
201
- ```
224
+ ## User Data
202
225
 
203
- ### Timeout Handling
204
226
  ```js
205
- // Tool calls automatically timeout (default: 5s)
206
- // Returns: "Tool call 'slow_function' timed out after 5 seconds"
207
-
208
- // Custom timeout per message:
209
- await q.sendMessage('user', 'Run slow analysis', null, { timeout: 30000 });
227
+ agent.setUserData('preference', 'dark-mode'); // returns this (chainable)
228
+ agent.getUserData('preference'); // 'dark-mode'
229
+ agent.getUserData(); // { preference: 'dark-mode' }
230
+ agent.clearUserData(); // returns this
210
231
  ```
211
232
 
212
- ---
213
-
214
- ## 🧪 Summary Behavior
215
-
216
- Summaries trigger when total token count exceeds 85% of the limit and are always triggered when `close()` is called.
217
- Summaries are:
218
-
219
- * Injected as special `[SUMMARY]: ...` messages
220
- * Bubbled up into the parent context
221
- * Excluded from re-summarization unless explicitly kept
222
- * **NEW**: Include tool call results in summarization context
223
-
224
- ---
233
+ ## Session Info
225
234
 
226
- ## 🔄 Redis Integration (Persistent Observable State)
227
-
228
- This library includes an optional Redis-based persistence layer to automatically store and update conversation objects (or any JS object) using a **proxy-based observable**.
229
-
230
- It supports:
231
-
232
- * 🔄 **Auto-saving on change** (with debounce)
233
- * 🧠 **Selective serialization** (skips internal/private `_` properties)
234
- * 🗃️ **Support for serializing `Messages` class**
235
- * 🔍 **Efficient diff-checking** (saves only when changed)
236
- * **NEW**: **Tool call state persistence** (active calls, deferred calls, waiting queues)
235
+ ```js
236
+ agent.getSessionInfo();
237
+ // {
238
+ // id, name, running, completed,
239
+ // messageCount, childCount,
240
+ // userData, uptime
241
+ // }
242
+
243
+ await agent.closeSession(); // Close context and cancel task
244
+ ```
237
245
 
238
- ### 🔧 Setup
246
+ ## Database Access
239
247
 
240
- 1. Install `redis`:
248
+ Saico provides backend-agnostic DB methods. Configure via `dynamodb_table` (auto-creates DynamoDB adapter) or `db` (any adapter implementing the interface).
241
249
 
242
- ```bash
243
- npm install redis
250
+ ```js
251
+ // CRUD
252
+ await agent.dbPutItem({ id: '123', name: 'test' });
253
+ const item = await agent.dbGetItem('id', '123');
254
+ await agent.dbDeleteItem('id', '123');
255
+ const items = await agent.dbQuery('email-index', 'email', 'user@test.com');
256
+ const all = await agent.dbGetAll();
257
+
258
+ // Updates
259
+ await agent.dbUpdate('id', '123', 'status', 'active');
260
+ await agent.dbUpdatePath('id', '123', [{ key: 'nested' }], 'field', 'value');
261
+ await agent.dbListAppend('id', '123', 'tags', 'new-tag');
262
+
263
+ // Counters
264
+ const nextId = await agent.dbNextCounterId('OrderId');
265
+ const count = await agent.dbGetCounterValue('OrderId');
266
+ await agent.dbSetCounterValue('OrderId', 100);
267
+ const total = await agent.dbCountItems();
244
268
  ```
245
269
 
246
- 2. Initialize Redis:
270
+ Override `_deserializeRecord(raw)` to transform raw DB records on retrieval (e.g., restore class instances):
247
271
 
248
272
  ```js
249
- const { init, createObservableForRedis } = require('./redis-store');
250
- await init(); // connects to redis://localhost:6379
273
+ class MyAgent extends Saico {
274
+ _deserializeRecord(raw) {
275
+ if (raw.type === 'order') return new Order(raw);
276
+ return raw;
277
+ }
278
+ }
251
279
  ```
252
280
 
253
- 3. Wrap a tool-enabled conversation:
281
+ ## Serialization
254
282
 
255
283
  ```js
256
- const { createQ } = require('./saico');
257
- const q = createQ("Travel assistant", null, "flights", 3000, null, toolHandler);
284
+ // Save
285
+ const json = agent.serialize();
258
286
 
259
- // Wrap with Redis observable - tool states auto-persist
260
- const obsQ = createObservableForRedis("q:session:12345", q);
287
+ // Restore
288
+ const restored = Saico.deserialize(json, {
289
+ tool_handler: myHandler,
290
+ functions: myFunctions,
291
+ });
261
292
  ```
262
293
 
263
- Now, any changes to `obsQ` including tool call states, deferred calls, and message queues are **automatically saved** to Redis.
294
+ Serialization includes: id, name, prompt, userData, sessionConfig, tm_create, isolate, and full context state (messages, tool_digest, chat_history).
264
295
 
265
- ---
296
+ ## Redis Persistence
266
297
 
267
- ## 🧼 Auto-Sanitization Rules
298
+ When Redis is initialized, Saico instances are automatically wrapped in an observable proxy. Any property change triggers a debounced save to Redis.
268
299
 
269
- When saving to Redis:
270
-
271
- * All keys starting with `_` are ignored.
272
- * Custom `.serialize()` methods (like on `Messages`) are respected.
273
- * Object updates are **debounced (1s)** and only saved if actual changes are detected.
274
- * **NEW**: Tool call tracking data is sanitized automatically
300
+ ```js
301
+ const { init } = require('saico');
275
302
 
276
- ---
303
+ // Initialize with Redis
304
+ await init({ redis: true });
277
305
 
278
- ## 🔌 OpenAI Integration
306
+ const agent = new Saico({ name: 'persistent-agent' });
307
+ agent.someProperty = 'value'; // Auto-saved to Redis
308
+ ```
279
309
 
280
- This library supports the modern OpenAI Tools API:
310
+ Properties prefixed with `_` are internal and not persisted.
281
311
 
282
- * **NEW**: Native `tool_calls` support (OpenAI's current standard)
283
- * Backward compatibility with legacy `functions` format
284
- * Automatic format conversion in openai.js
285
- * Built-in retry logic with exponential backoff for rate limits
312
+ ## Tool Handler Interface
286
313
 
287
314
  ```js
288
- // OpenAI will return tool_calls in responses:
289
- {
290
- role: 'assistant',
291
- content: 'I need to check the weather',
292
- tool_calls: [{
293
- id: 'call_abc123',
294
- type: 'function',
295
- function: {
296
- name: 'get_weather',
297
- arguments: '{"location": "New York"}'
298
- }
299
- }]
315
+ async function toolHandler(toolName, argumentsString) {
316
+ const args = JSON.parse(argumentsString);
317
+ // Execute tool logic
318
+ return result; // string or { content: string, functions?: [] }
300
319
  }
301
-
302
- // Saico handles the complete tool execution cycle automatically
303
320
  ```
304
321
 
305
- ---
306
-
307
- ## 🧪 Testing
322
+ ### Tool Safety Features
308
323
 
309
- Comprehensive test suite with **37 tests** covering:
324
+ - **Depth control** `max_depth` (default: 5) prevents infinite tool call recursion
325
+ - **Deferred execution** — Tool calls defer when max depth is reached, resume when depth reduces
326
+ - **Duplicate detection** — Identical active tool calls are blocked
327
+ - **Repetition prevention** — `max_tool_repetition` (default: 20) blocks excessive repeated calls
328
+ - **Timeout handling** — Configurable timeout (default: 5s) with graceful failure
329
+ - **Message queuing** — Messages queue automatically when tool calls are pending
310
330
 
311
- * Core conversation management (25 tests)
312
- * **NEW**: Tool calls functionality (12 tests):
313
- - Basic tool execution
314
- - Depth limits and deferred execution
315
- - Repetition prevention and filtering
316
- - Duplicate detection
317
- - Message queuing systems
318
- - Timeout handling
319
- - Parent-child tool inheritance
320
-
321
- ```bash
322
- npm test # Run full test suite
323
- ```
331
+ ## Low-Level API
324
332
 
325
- ---
333
+ For cases where you don't need the Saico master class:
326
334
 
327
- ## 📁 Project Structure
335
+ ```js
336
+ const { createTask, createContext, createQ } = require('saico');
337
+
338
+ // Create a task with context
339
+ const task = createTask({
340
+ name: 'my-task',
341
+ prompt: 'You are helpful',
342
+ tool_handler: handler,
343
+ functions: tools
344
+ });
345
+ const reply = await task.sendMessage('Hello');
328
346
 
347
+ // Standalone context (legacy)
348
+ const ctx = createQ('System prompt', null, 'tag', 4000, null, handler);
349
+ const reply = await ctx.sendMessage('user', 'Hello', functions);
329
350
  ```
330
- .
331
- ├── saico.js # Core implementation with tool calls
332
- ├── openai.js # OpenAI API wrapper with tools support
333
- ├── redis.js # Saico compatible redis wrapper
334
- ├── util.js # Utilities: token counting, etc.
335
- ├── test.js # Comprehensive test suite
336
- ├── msgs.js # Original enhanced version (reference)
337
- └── README.md # This file
338
- ```
339
-
340
- ---
341
351
 
342
- ## 🚀 Migration Guide
352
+ ## Project Structure
343
353
 
344
- If upgrading from older versions:
345
-
346
- ### Old API:
347
- ```js
348
- const q = createQ(prompt, opts, msgs, parent);
349
354
  ```
350
-
351
- ### New API:
352
- ```js
353
- const q = createQ(prompt, parent, tag, token_limit, msgs, tool_handler, config);
355
+ saico/
356
+ +-- index.js # Entry point, exports all components
357
+ +-- saico.js # Saico master class
358
+ +-- itask.js # Base task class (hierarchy, states, cancellation)
359
+ +-- msgs.js # Conversation context (message queue, tool calls, summarization)
360
+ +-- context.js # Backward-compat shim for msgs.js
361
+ +-- dynamo.js # DynamoDB storage adapter
362
+ +-- store.js # Storage abstraction (Redis + pluggable backends)
363
+ +-- openai.js # OpenAI API wrapper with retry logic
364
+ +-- redis.js # Redis persistence with observable proxy
365
+ +-- util.js # Utilities (token counting, logging)
354
366
  ```
355
367
 
356
- ### Breaking Changes:
357
- - Constructor parameter order changed
358
- - `opts.tag` → `tag` parameter
359
- - `opts.token_limit` → `token_limit` parameter
360
- - Added `tool_handler` and `config` parameters
361
- - `function_call` → `tool_calls` in OpenAI responses
362
-
363
- ---
368
+ ## Testing
364
369
 
365
- ## 🔐 License
366
-
367
- MIT License © [Wanderli.ai]
368
-
369
- ---
370
-
371
- ## 🙌 Contributing
372
-
373
- Pull requests, issues, and suggestions welcome! Please fork the repo and open a PR, or submit issues directly.
374
-
375
- Areas where contributions are especially welcome:
376
- - Additional tool call safety features
377
- - Performance optimizations for large conversations
378
- - Extended test coverage
379
- - Documentation improvements
380
-
381
- ---
382
-
383
- ## 📣 Acknowledgements
370
+ ```bash
371
+ npm test
372
+ ```
384
373
 
385
- This project was inspired by the need for a lightweight, non-opinionated alternative to LangChain's memory modules, with full support for real-world LLM conversation flows and enterprise-grade tool calling capabilities.
374
+ 294 tests covering Saico lifecycle, task hierarchy, message handling, tool calls, DB adapters, serialization, and integration flows.
386
375
 
387
- ---
376
+ ## Requirements
388
377
 
389
- ## 🔮 Roadmap
378
+ - Node.js >= 16.0.0
379
+ - `OPENAI_API_KEY` environment variable for LLM calls
380
+ - Redis (optional, for auto-persistence)
381
+ - AWS SDK v3 (optional peer dependency, for DynamoDB)
390
382
 
391
- - [ ] **Multi-model support** (Anthropic, Google, etc.)
392
- - [ ] **Advanced tool call analytics** and monitoring
393
- - [ ] **Custom summarization strategies**
394
- - [ ] **Tool call result caching**
395
- - [ ] **Streaming tool call responses**
396
- - [ ] **Tool call permission systems**
383
+ ## License
397
384
 
398
- Let me know if you'd like to see any of these features prioritized!
385
+ ISC