saico 2.3.0 → 2.5.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 -297
  2. package/index.js +2 -9
  3. package/itask.js +16 -4
  4. package/msgs.js +106 -99
  5. package/package.json +1 -2
  6. package/saico.js +305 -41
  7. package/sid.js +0 -248
package/README.md CHANGED
@@ -1,398 +1,388 @@
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
+ functions: [{
31
+ type: 'function',
32
+ function: {
33
+ name: 'get_weather',
34
+ description: 'Get weather for a location',
35
+ parameters: {
36
+ type: 'object',
37
+ properties: { location: { type: 'string' } },
38
+ required: ['location']
39
+ }
40
+ }
41
+ }]
42
+ });
43
+ }
22
44
 
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
45
+ // Tool implementations — define TOOL_ prefix methods
46
+ async TOOL_get_weather(args) {
47
+ return `Weather in ${args.location}: 72F, sunny`;
48
+ }
49
+ }
31
50
 
32
- ---
51
+ const agent = new MyAgent();
52
+ agent.activate({ createQ: true });
33
53
 
34
- ## 📦 Installation
54
+ // Backend message (prefixed with [BACKEND] automatically)
55
+ const reply = await agent.sendMessage('What is the weather in Tokyo?');
35
56
 
36
- ```bash
37
- npm install saico-ai-thread --save
57
+ // User-facing chat message (routed to deepest active context)
58
+ const chatReply = await agent.recvChatMessage('Hello!');
38
59
  ```
39
60
 
40
- Or clone manually:
61
+ ## Core Concepts
41
62
 
42
- ```bash
43
- git clone https://github.com/wanderli-ai/saico
44
- cd saico
45
- ```
63
+ ### Saico Lifecycle
46
64
 
47
- ---
65
+ Saico separates construction from activation:
48
66
 
49
- ## 🧑‍💻 Usage
67
+ ```js
68
+ // 1. Construct — sets up config, Redis proxy, DB access. No task yet.
69
+ const agent = new Saico({
70
+ name: 'agent',
71
+ prompt: 'System prompt here',
72
+ dynamodb_table: 'my_data', // optional DB
73
+ });
50
74
 
51
- ### Basic Setup with Tool Handler
75
+ // DB methods work before activation
76
+ const item = await agent.dbGetItem('id', '123');
52
77
 
53
- ```js
54
- const { createQ } = require('saico');
55
-
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
- }
78
+ // 2. Activate — creates internal task + optional message queue context
79
+ agent.activate({ createQ: true });
69
80
 
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?');
81
+ // 3. Use send messages, spawn children
82
+ await agent.sendMessage('Do something');
83
+ await agent.recvChatMessage('User says hello');
84
+
85
+ // 4. Deactivate — bubbles cleaned messages to parent, closes context
86
+ await agent.deactivate();
83
87
  ```
84
88
 
85
- ### Create a Sub-Conversation with Tool Inheritance
89
+ ### Message Orchestration
86
90
 
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
91
+ When `sendMessage()` or `recvChatMessage()` is called, Saico walks the parent chain to build the full LLM payload:
92
+
93
+ ```
94
+ Root Saico (prompt: "You are a manager")
95
+ +-- Child Saico (prompt: "Handle bookings")
96
+ +-- Grandchild Saico (prompt: "Process payment")
97
+ sendMessage("Charge $50")
98
+ |
99
+ v
100
+ Preamble built automatically:
101
+ [Root prompt] [Root state summary] [Root tool digest]
102
+ [Child prompt] [Child state summary + recent msgs] [Child tool digest]
103
+ [Grandchild prompt] [Grandchild state summary]
104
+ ... then the actual message queue messages ...
105
+
106
+ Functions aggregated from all levels.
99
107
  ```
100
108
 
101
- ### Advanced Tool Configuration
109
+ - **`sendMessage(content, functions, opts)`** — Sends a backend message (auto-prefixed `[BACKEND]`). Uses the current or nearest ancestor context.
110
+ - **`recvChatMessage(content, opts)`** — Routes a user chat message DOWN to the deepest descendant with a message queue.
111
+
112
+ ### Isolation
113
+
114
+ Set `isolate: true` to prevent ancestor aggregation:
102
115
 
103
116
  ```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
117
+ const isolated = new Saico({
118
+ name: 'isolated-agent',
119
+ prompt: 'Independent context',
120
+ isolate: true // won't include parent prompts/tools/summaries
122
121
  });
123
122
  ```
124
123
 
125
- ### Hierarchy Example with Tool Calls
124
+ ### State Summaries
126
125
 
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
- ```
126
+ Override `getStateSummary()` in your subclass to provide dynamic state context:
132
127
 
133
- ---
128
+ ```js
129
+ class OrderAgent extends Saico {
130
+ getStateSummary() {
131
+ return `Active order: #${this.orderId}, items: ${this.items.length}`;
132
+ }
133
+ }
134
+ ```
134
135
 
135
- ## 🧠 Enhanced Message API
136
+ 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
137
 
137
- Each message is stored with enhanced tool call support:
138
+ ### Spawning Child Tasks
138
139
 
139
140
  ```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
- }
141
+ // Child with its own conversation context
142
+ const child = agent.spawnTaskWithContext({
143
+ name: 'subtask',
144
+ prompt: 'Handle this specific sub-task',
145
+ functions: [/* child-specific tools */]
146
+ }, [
147
+ async function main() {
148
+ return await this.sendMessage('Working on subtask...');
149
+ }
150
+ ]);
151
+
152
+ // Child without context (uses parent's)
153
+ const simple = agent.spawnTask({ name: 'simple' }, [
154
+ async function main() {
155
+ await this.sendMessage('Quick operation');
156
+ }
157
+ ]);
158
158
  ```
159
159
 
160
- ### Enhanced API Methods
160
+ Child tasks inherit `sessionConfig` defaults (token_limit, max_depth, etc.) from the parent Saico.
161
161
 
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
162
+ ### Deactivation and Message Bubbling
169
163
 
170
- ---
164
+ When a Saico deactivates, cleaned messages (no tool calls, no `[BACKEND]` messages) are pushed into the parent's message queue, preserving conversation continuity.
171
165
 
172
- ## 🛡️ Tool Call Safety Features
166
+ ## Constructor Options
173
167
 
174
- ### Depth Control & Deferred Execution
175
168
  ```js
176
- const q = createQ("Assistant", null, "main", 4000, null, toolHandler, {
177
- max_depth: 3 // Tool calls defer at depth 4+
169
+ new Saico({
170
+ // Identity
171
+ id: 'custom-id', // Auto-generated if omitted
172
+ name: 'my-agent', // Defaults to class name
173
+
174
+ // AI config
175
+ prompt: 'System prompt',
176
+ functions: [], // OpenAI function definitions
177
+
178
+ // Behavior
179
+ isolate: false, // Stop ancestor aggregation
180
+
181
+ // Session config (defaults for this agent and its children)
182
+ token_limit: 4000,
183
+ max_depth: 5, // Max tool call recursion depth
184
+ max_tool_repetition: 20, // Max consecutive repeated tool calls
185
+ queue_limit: 100, // Message queue limit
186
+ min_chat_messages: 5, // Min messages to keep in queue
187
+ sessionConfig: {}, // Override any of the above
188
+
189
+ // Storage
190
+ redis: true, // Set false to skip Redis proxy
191
+ key: 'custom-redis-key',
192
+ dynamodb_table: 'table', // Auto-creates DynamoDB adapter
193
+ dynamodb_region: 'us-east-1',
194
+ db: customAdapter, // Any adapter with put/get/delete/query interface
195
+
196
+ // User data
197
+ userData: {}, // Arbitrary user metadata
178
198
  });
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
199
  ```
186
200
 
187
- ### Repetition Prevention
201
+ ## Activate Options
202
+
188
203
  ```js
189
- const q = createQ("Assistant", null, "main", 4000, null, toolHandler, {
190
- max_tool_repetition: 5 // Block tools called >5 times consecutively
204
+ agent.activate({
205
+ createQ: true, // Create message queue context
206
+ prompt: 'Extra prompt', // Appended to class-level prompt
207
+ states: [], // Task state functions
208
+ parent: parentTask, // Parent task to spawn under
209
+ taskId: 'custom-id',
210
+ sequential_mode: true, // Process messages sequentially
211
+
212
+ // Override session config for this activation
213
+ token_limit: 8000,
214
+ max_depth: 10,
215
+ queue_limit: 200,
191
216
  });
192
-
193
- // Automatically filters excessive repeated tool calls
194
- // Logs: "Dropping excessive tool call: get_weather (hit max_tool_repetition=5)"
195
217
  ```
196
218
 
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
- ```
219
+ ## User Data
202
220
 
203
- ### Timeout Handling
204
221
  ```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 });
222
+ agent.setUserData('preference', 'dark-mode'); // returns this (chainable)
223
+ agent.getUserData('preference'); // 'dark-mode'
224
+ agent.getUserData(); // { preference: 'dark-mode' }
225
+ agent.clearUserData(); // returns this
210
226
  ```
211
227
 
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
228
+ ## Session Info
223
229
 
224
- ---
225
-
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)
230
+ ```js
231
+ agent.getSessionInfo();
232
+ // {
233
+ // id, name, running, completed,
234
+ // messageCount, childCount,
235
+ // userData, uptime
236
+ // }
237
+
238
+ await agent.closeSession(); // Close context and cancel task
239
+ ```
237
240
 
238
- ### 🔧 Setup
241
+ ## Database Access
239
242
 
240
- 1. Install `redis`:
243
+ Saico provides backend-agnostic DB methods. Configure via `dynamodb_table` (auto-creates DynamoDB adapter) or `db` (any adapter implementing the interface).
241
244
 
242
- ```bash
243
- npm install redis
245
+ ```js
246
+ // CRUD
247
+ await agent.dbPutItem({ id: '123', name: 'test' });
248
+ const item = await agent.dbGetItem('id', '123');
249
+ await agent.dbDeleteItem('id', '123');
250
+ const items = await agent.dbQuery('email-index', 'email', 'user@test.com');
251
+ const all = await agent.dbGetAll();
252
+
253
+ // Updates
254
+ await agent.dbUpdate('id', '123', 'status', 'active');
255
+ await agent.dbUpdatePath('id', '123', [{ key: 'nested' }], 'field', 'value');
256
+ await agent.dbListAppend('id', '123', 'tags', 'new-tag');
257
+
258
+ // Counters
259
+ const nextId = await agent.dbNextCounterId('OrderId');
260
+ const count = await agent.dbGetCounterValue('OrderId');
261
+ await agent.dbSetCounterValue('OrderId', 100);
262
+ const total = await agent.dbCountItems();
244
263
  ```
245
264
 
246
- 2. Initialize Redis:
265
+ Override `_deserializeRecord(raw)` to transform raw DB records on retrieval (e.g., restore class instances):
247
266
 
248
267
  ```js
249
- const { init, createObservableForRedis } = require('./redis-store');
250
- await init(); // connects to redis://localhost:6379
268
+ class MyAgent extends Saico {
269
+ _deserializeRecord(raw) {
270
+ if (raw.type === 'order') return new Order(raw);
271
+ return raw;
272
+ }
273
+ }
251
274
  ```
252
275
 
253
- 3. Wrap a tool-enabled conversation:
276
+ ## Serialization
254
277
 
255
278
  ```js
256
- const { createQ } = require('./saico');
257
- const q = createQ("Travel assistant", null, "flights", 3000, null, toolHandler);
279
+ // Save
280
+ const json = agent.serialize();
258
281
 
259
- // Wrap with Redis observable - tool states auto-persist
260
- const obsQ = createObservableForRedis("q:session:12345", q);
282
+ // Restore
283
+ const restored = Saico.deserialize(json, {
284
+ functions: myFunctions,
285
+ });
261
286
  ```
262
287
 
263
- Now, any changes to `obsQ` including tool call states, deferred calls, and message queues are **automatically saved** to Redis.
288
+ Serialization includes: id, name, prompt, userData, sessionConfig, tm_create, isolate, and full context state (messages, tool_digest, chat_history).
264
289
 
265
- ---
290
+ ## Redis Persistence
266
291
 
267
- ## 🧼 Auto-Sanitization Rules
292
+ When Redis is initialized, Saico instances are automatically wrapped in an observable proxy. Any property change triggers a debounced save to Redis.
268
293
 
269
- When saving to Redis:
294
+ ```js
295
+ const { init } = require('saico');
270
296
 
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
297
+ // Initialize with Redis
298
+ await init({ redis: true });
275
299
 
276
- ---
300
+ const agent = new Saico({ name: 'persistent-agent' });
301
+ agent.someProperty = 'value'; // Auto-saved to Redis
302
+ ```
277
303
 
278
- ## 🔌 OpenAI Integration
304
+ Properties prefixed with `_` are internal and not persisted.
279
305
 
280
- This library supports the modern OpenAI Tools API:
306
+ ## Tool Implementation (TOOL_ methods)
281
307
 
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
308
+ Define tool implementations as `TOOL_`-prefixed methods on your Saico subclass. When the LLM returns a tool call, Context automatically searches the Saico hierarchy (current → up parents → down children) to find and invoke the matching method with parsed arguments.
286
309
 
287
310
  ```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"}'
311
+ class MyAgent extends Saico {
312
+ async TOOL_get_weather(args) {
313
+ // args is already JSON.parse'd
314
+ return `Weather in ${args.location}: 72F, sunny`;
298
315
  }
299
- }]
300
- }
301
316
 
302
- // Saico handles the complete tool execution cycle automatically
317
+ async TOOL_search(args) {
318
+ const results = await search(args.query);
319
+ return { content: JSON.stringify(results), functions: updatedTools };
320
+ }
321
+ }
303
322
  ```
304
323
 
305
- ---
324
+ Return a string or `{ content: string, functions?: [] }`.
306
325
 
307
- ## 🧪 Testing
326
+ ### Tool Safety Features
308
327
 
309
- Comprehensive test suite with **37 tests** covering:
328
+ - **Depth control** `max_depth` (default: 5) prevents infinite tool call recursion
329
+ - **Deferred execution** — Tool calls defer when max depth is reached, resume when depth reduces
330
+ - **Duplicate detection** — Identical active tool calls are blocked
331
+ - **Repetition prevention** — `max_tool_repetition` (default: 20) blocks excessive repeated calls
332
+ - **Timeout handling** — Configurable timeout (default: 5s) with graceful failure
333
+ - **Message queuing** — Messages queue automatically when tool calls are pending
310
334
 
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
335
+ ## Low-Level API
320
336
 
321
- ```bash
322
- npm test # Run full test suite
323
- ```
337
+ For cases where you don't need the Saico master class:
324
338
 
325
- ---
339
+ ```js
340
+ const { createTask, createContext, createQ } = require('saico');
326
341
 
327
- ## 📁 Project Structure
342
+ // Create a task with context
343
+ const task = createTask({
344
+ name: 'my-task',
345
+ prompt: 'You are helpful',
346
+ functions: tools
347
+ });
348
+ const reply = await task.sendMessage('Hello');
328
349
 
350
+ // Standalone context (legacy)
351
+ const ctx = createQ('System prompt', null, 'tag', 4000);
352
+ const reply = await ctx.sendMessage('user', 'Hello', functions);
329
353
  ```
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
-
342
- ## 🚀 Migration Guide
343
354
 
344
- If upgrading from older versions:
355
+ ## Project Structure
345
356
 
346
- ### Old API:
347
- ```js
348
- const q = createQ(prompt, opts, msgs, parent);
349
357
  ```
350
-
351
- ### New API:
352
- ```js
353
- const q = createQ(prompt, parent, tag, token_limit, msgs, tool_handler, config);
358
+ saico/
359
+ +-- index.js # Entry point, exports all components
360
+ +-- saico.js # Saico master class
361
+ +-- itask.js # Base task class (hierarchy, states, cancellation)
362
+ +-- msgs.js # Conversation context (message queue, tool calls, summarization)
363
+ +-- context.js # Backward-compat shim for msgs.js
364
+ +-- dynamo.js # DynamoDB storage adapter
365
+ +-- store.js # Storage abstraction (Redis + pluggable backends)
366
+ +-- openai.js # OpenAI API wrapper with retry logic
367
+ +-- redis.js # Redis persistence with observable proxy
368
+ +-- util.js # Utilities (token counting, logging)
354
369
  ```
355
370
 
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
- ---
371
+ ## Testing
364
372
 
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
373
+ ```bash
374
+ npm test
375
+ ```
384
376
 
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.
377
+ 293 tests covering Saico lifecycle, task hierarchy, message handling, tool calls, DB adapters, serialization, and integration flows.
386
378
 
387
- ---
379
+ ## Requirements
388
380
 
389
- ## 🔮 Roadmap
381
+ - Node.js >= 16.0.0
382
+ - `OPENAI_API_KEY` environment variable for LLM calls
383
+ - Redis (optional, for auto-persistence)
384
+ - AWS SDK v3 (optional peer dependency, for DynamoDB)
390
385
 
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**
386
+ ## License
397
387
 
398
- Let me know if you'd like to see any of these features prioritized!
388
+ ISC