zyn-ai 1.3.7 → 1.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.
package/README.md CHANGED
@@ -8,45 +8,41 @@
8
8
  <img src="https://img.shields.io/npm/v/zyn-ai?label=npm&color=%23CB3837" alt="NPM Version"/>
9
9
  <img src="https://img.shields.io/github/v/release/SoyMaycol/Zyn?include_prereleases&sort=semver" alt="Latest Release"/>
10
10
  <img src="https://img.shields.io/npm/dt/zyn-ai" alt="Downloads"/>
11
+ <img src="https://img.shields.io/github/license/SoyMaycol/Zyn" alt="License"/>
11
12
  </p>
12
13
 
13
14
  <p align="center">
14
- <b>Local AI agent for terminal, TUI, and web.</b>
15
+ <b>A complete CLI Agent for production and enterprise level, with incredible results.</b>
15
16
  </p>
16
17
 
17
18
  <p align="center">
18
- <a href="https://github.com/SoyMaycol/Zyn">Official repository</a>
19
+ <a href="https://github.com/SoyMaycol/Zyn">GitHub</a>
19
20
  </p>
20
21
 
21
22
  ---
22
23
 
23
- > [!CAUTION]
24
- > Using the web version of the agent is not recommended as it contains bugs. I am doing my best to fix most of the web bugs.
24
+ ## Features
25
25
 
26
- ## What is Zyn
27
-
28
- Zyn is a local AI agent designed for terminal and web usage. It supports persistent sessions, system tools, multiple AI providers, session exports, and configurable models.
29
-
30
- ---
31
-
32
- ## Requirements
33
-
34
- - Node.js 18+
35
- - npm
36
- - Internet connection for remote providers
26
+ - **CLI + TUI** — full terminal UI with keyboard navigation, token display, **improved history & compaction**, overlay system
27
+ - **Multi-provider** — Zen (free, no config), Gemini, Qwen (DashScope), HuggingFace, **skill search**, custom providers (any OpenAI-compatible API)
28
+ - **Skills system** folder-based skills with **improved prompt integration** and YAML frontmatter
29
+ - **Tool execution** — read/write files, run commands, search code, browse web, glob patterns
30
+ - **Session management** — **real conversation history**, persistent sessions with **better compaction**, full transcript replay, resume, export
31
+ - **Context tracking** — token estimator with per-model context limits, **auto-compaction at 85%**, updated to reflect new history behavior
32
+ - **Multi-platform** — embeddable in WhatsApp (Baileys), Discord, and Telegram bots, Although this function is not complete (It is under development)
33
+ - **Background workers** — detach long-running turns to background processes
34
+ - **i18n** — English and Spanish interfaces
37
35
 
38
36
  ---
39
37
 
40
38
  ## Installation
41
39
 
42
- ### Global install
43
-
44
40
  ```bash
45
41
  npm install -g zyn-ai
46
42
  zyn
47
43
  ```
48
44
 
49
- ### Local development
45
+ ### From source
50
46
 
51
47
  ```bash
52
48
  git clone https://github.com/SoyMaycol/Zyn.git
@@ -55,125 +51,190 @@ npm install
55
51
  npm start
56
52
  ```
57
53
 
58
- ---
59
-
60
54
  ## Usage
61
55
 
62
56
  ```bash
63
- zyn
64
- zyn "Explain this project"
65
- zyn --new
66
- zyn --resume ID
57
+ zyn # Interactive TUI (default)
58
+ zyn "question" # Single prompt (CLI mode)
59
+ zyn --new # Force new session
60
+ zyn --resume ID # Resume existing session (keeps history)
67
61
  ```
68
62
 
69
- ---
70
-
71
- ## Web mode
63
+ ## Models
72
64
 
73
- Inside Zyn:
65
+ 14 built-in models across 4 providers. Each model has its real context length in the StatusBar (e.g. `10.8K/128K`).
74
66
 
75
- ```text
76
- /web
77
- /web 0.0.0.0:3000
78
- ```
67
+ ### Zen (free, no configuration)
79
68
 
80
- Or directly:
69
+ | Key | Model | Context |
70
+ |---|---|---|
71
+ | `nemotron` | Nemotron 3 Ultra | 128K |
72
+ | `mimo` | Mimo 2.5 | 128K |
73
+ | `north-mini` | North Mini Code | 128K |
74
+ | `deepseek` | DeepSeek V4 Flash | 128K |
81
75
 
82
- ```bash
83
- npm run web
84
- ```
76
+ ### Gemini (requires API key)
85
77
 
86
- ---
78
+ | Key | Model | Context |
79
+ |---|---|---|
80
+ | `gemini-flash` | Gemini 2.5 Flash | 1M |
81
+ | `gemini-flash-001` | Gemini 2.5 Flash 001 | 1M |
82
+ | `gemini-pro` | Gemini 2.5 Pro | 1M |
83
+ | `gemini-flash-lite` | Gemini 2.5 Flash Lite | 1M |
84
+ | `gemini-flash-lite-001` | Gemini 2.5 Flash Lite 001 | 1M |
85
+ | `gemma-3` | Gemma 3 27B | 128K |
87
86
 
88
- ## Language
87
+ ### Qwen (requires DashScope API key)
89
88
 
90
- Supported languages:
89
+ | Key | Model | Context |
90
+ |---|---|---|
91
+ | `qwen-plus` | Qwen Plus | 128K |
92
+ | `qwen-max` | Qwen Max | 32K |
93
+ | `qwen-turbo` | Qwen Turbo | 1M |
91
94
 
92
- - `en`
93
- - `es`
95
+ ### HuggingFace (requires HF token)
94
96
 
95
- Commands:
97
+ | Key | Model | Context |
98
+ |---|---|---|
99
+ | `hf-ling-2.6-1t` | InclusionAI Ling 2.6 1T | 128K |
96
100
 
97
- ```text
98
- /lang
99
- /lang en
100
- /lang es
101
- ```
101
+ Default model: `nemotron` (Zen, no configuration required).
102
102
 
103
- ---
104
-
105
- ## Main Commands
103
+ ## Commands
106
104
 
107
105
  ### Sessions
108
106
 
109
107
  | Command | Description |
110
108
  |---|---|
111
- | `/help` | Show available commands |
112
- | `/status` | Show current status |
113
- | `/history` | Show recent actions |
114
- | `/memory` | Show memory summary |
115
- | `/sessions` | List saved sessions |
116
- | `/new` | Create a new session |
117
- | `/resume <ID>` | Resume a session |
109
+ | `/help` | Show help |
110
+ | `/status` | Current status |
111
+ | `/history` | Recent actions (last 20) |
112
+ | `/memory` | Memory summary |
113
+ | `/session` | Current session info |
114
+ | `/sessions` | List all sessions |
115
+ | `/new` | New session |
116
+ | `/resume <ID>` | Resume session (keeps history) |
118
117
  | `/title <text>` | Rename session |
119
118
 
120
119
  ### Configuration
121
120
 
122
121
  | Command | Description |
123
122
  |---|---|
124
- | `/model` | Show or change model |
125
- | `/models` | List models |
126
- | `/providers` | List providers |
123
+ | `/models` | Open model picker (current provider) |
124
+ | `/providers` | Open interactive provider picker → configure → pick model |
125
+ | `/provider list` | List configured providers and their fields |
126
+ | `/provider sync <name>` | Fetch models from a provider's API |
127
+ | `/provider set <name> <field> <value>` | Set provider config (apiKey, baseUrl, modelId, contextLength) |
128
+ | `/provider remove <name>` | Remove a provider configuration |
127
129
  | `/lang <en\|es>` | Change language |
130
+ | `/auto on\|off` | Toggle auto-approval |
131
+ | `/concuerdo` | Toggle group model mode (queries all configured models) |
132
+ | `/persona set <text>` | Set response persona |
128
133
  | `/config show` | Show config |
129
- | `/auto on\|off` | Toggle auto approval |
134
+ | `/git set\|list\|remove` | Manage git credentials |
130
135
  | `/cwd <path>` | Change working directory |
131
136
 
132
- ### Tools
137
+ ### Tools & Skills
133
138
 
134
139
  | Command | Description |
135
140
  |---|---|
136
- | `/tools` | List tools |
137
- | `/skills` | List skills |
138
- | `/gmail connect` | Connect Gmail with Google OAuth + PKCE |
139
- | `/gmail status` | Show Gmail connection status |
140
- | `/gmail disconnect` | Remove saved Gmail tokens |
141
- | `/cwd` | Show working directory |
141
+ | `/tools` | List agent tools |
142
+ | `/skills` | List loaded skills |
143
+ | `/gmail connect` | Connect Gmail via OAuth |
142
144
 
143
- ### Web & Export
145
+ ### Export & Control
144
146
 
145
147
  | Command | Description |
146
148
  |---|---|
147
- | `/web` | Start web interface |
148
- | `/transcript` | Show transcript |
149
- | `/export` | Export session |
149
+ | `/bg` | Detach current turn to background |
150
+ | `/transcript` | View session transcript |
151
+ | `/export` | Export session to txt |
152
+ | `/stop` | Stop current agent turn |
153
+ | `/undo` | Undo last turn |
154
+ | `/redo` | Redo undone turn |
155
+ | `/reset` | Reset context |
156
+ | `/exit` | Exit |
150
157
 
151
- ### Control
158
+ Press `ESC` twice in the TUI to stop the current task.
152
159
 
153
- | Command | Description |
154
- |---|---|
155
- | `/stop` | Stop current task |
156
- | `/reset` | Reset session |
157
- | `/exit` | Exit Zyn |
160
+ ## Custom Providers
161
+
162
+ Add any OpenAI-compatible API:
158
163
 
159
- In the TUI, press `ESC` twice to stop the current task.
164
+ ```bash
165
+ # Interactive: run /providers → select "+ Add custom provider"
166
+ # Or manual:
167
+ /provider set groq baseUrl https://api.groq.com/openai/v1
168
+ /provider set groq apiKey gsk_xxxx
169
+ /provider set groq contextLength 128000
170
+ /provider sync groq
171
+ ```
172
+
173
+ Configurable fields: `apiKey`, `baseUrl`, `modelId`, `contextLength`, `email`, `password`, `modelEndpoint`, `chatEndpoint`.
160
174
 
175
+ ## Skills
176
+
177
+ Skills are folders under `data/skills/<name>/SKILL.md` with YAML frontmatter:
178
+
179
+ ```markdown
180
+ ---
181
+ name: reasoning
182
+ description: Reasoning and planning for complex tasks
161
183
  ---
162
184
 
163
- ## Models
185
+ # Skill body here
186
+ ```
187
+
188
+ The system prompt automatically advertises every loaded skill to the model, and mentions of GitHub repos/skills trigger `web_search` to find them.
189
+
190
+ ## API
191
+
192
+ ### Embed in your bot
164
193
 
165
- Custom models can be added using `data/models.json`.
194
+ ```js
195
+ const { createAgent } = require('zyn-ai');
166
196
 
167
- Example:
197
+ const agent = createAgent({
198
+ model: 'nemotron',
199
+ language: 'en',
200
+ autoApprove: false,
201
+ });
168
202
 
169
- ```json
170
- {
171
- "models": {
172
- "my-gemini-flash": {
173
- "label": "Gemini Flash",
174
- "provider": "gemini",
175
- "geminiModel": "gemini-flash"
176
- }
177
- }
178
- }
203
+ const response = await agent.send('userId', 'Hello!');
179
204
  ```
205
+
206
+ ### Platforms
207
+
208
+ ```js
209
+ const { createAgent, platforms } = require('zyn-ai');
210
+
211
+ const agent = createAgent({ model: 'nemotron' });
212
+
213
+ // WhatsApp (Baileys)
214
+ await platforms.whatsapp({ agent, session: './whatsapp-auth' });
215
+
216
+ // Discord
217
+ await platforms.discord({ agent, token: 'DISCORD_TOKEN' });
218
+
219
+ // Telegram
220
+ await platforms.telegram({ agent, token: 'TELEGRAM_TOKEN' });
221
+ ```
222
+
223
+ Install the corresponding `optionalDependencies` only if you need them.
224
+
225
+ ## Environment Variables
226
+
227
+ | Variable | Description |
228
+ |---|---|
229
+ | `ZYN_DEFAULT_MODEL` | Override default model key |
230
+ | `ZYN_DEFAULT_LANG` | Default language (`en` or `es`) |
231
+ | `ZYN_GEMINI_API_KEY` | Gemini API key |
232
+ | `ZYN_QWEN_API_KEY` | DashScope API key |
233
+ | `ZYN_HUGGINGFACE_TOKEN` | HuggingFace token |
234
+ | `ZYN_REQUEST_TIMEOUT_MS` | Request timeout (default: 180000) |
235
+ | `ZYN_PROVIDER_TIMEOUT_MAX_ATTEMPTS` | Retry attempts on provider failure (default: 3) |
236
+ | `ZYN_GMAIL_CLIENT_SECRET` | Gmail OAuth client secret |
237
+
238
+ ## License
239
+
240
+ MIT - Maycol B.T
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "zyn-ai",
3
- "version": "1.3.7",
4
- "description": "Production-ready AI agent for CLI and web with real tool execution and automation",
3
+ "version": "1.4.0",
4
+ "description": "Production-ready AI agent for CLI, TUI, and external platforms (WhatsApp/Baileys, Discord, Telegram) with real tool execution and automation",
5
5
  "author": "Maycol",
6
6
  "keywords": [
7
7
  "ai-agent",
@@ -14,7 +14,12 @@
14
14
  "productivity-tool",
15
15
  "local-ai-agent",
16
16
  "command-line-ai",
17
- "web-ai-agent",
17
+ "tui-agent",
18
+ "whatsapp-bot",
19
+ "discord-bot",
20
+ "telegram-bot",
21
+ "multi-platform",
22
+ "embeddable-agent",
18
23
  "zyn"
19
24
  ],
20
25
  "bin": {
@@ -22,22 +27,23 @@
22
27
  "Zyn": "./zyn.js"
23
28
  },
24
29
  "type": "commonjs",
30
+ "main": "./src/agent.js",
25
31
  "scripts": {
26
32
  "start": "node zyn.js",
27
33
  "dev": "node zyn.js",
28
- "web": "node src/web/server.js",
29
- "check": "node --check zyn.js && node --check src/cli/runtime.js"
34
+ "check": "node --check zyn.js && node --check src/cli/runtime.js && node --check src/agent.js"
30
35
  },
31
36
  "dependencies": {
32
37
  "axios": "^1.7.9",
33
- "bcryptjs": "^3.0.3",
34
38
  "cheerio": "^1.2.0",
35
- "express": "^5.2.1",
36
- "express-session": "^1.19.0",
37
39
  "ink": "^6.8.0",
38
40
  "react": "^19.0.0",
39
- "session-file-store": "^1.5.0",
40
- "jimp": "^1.6.1"
41
+ "jimp": "^0.16.1"
42
+ },
43
+ "optionalDependencies": {
44
+ "@whiskeysockets/baileys": "^6.7.0",
45
+ "discord.js": "^14.16.0",
46
+ "node-telegram-bot-api": "^0.66.0"
41
47
  },
42
48
  "repository": {
43
49
  "type": "git",
@@ -58,7 +64,12 @@
58
64
  "LICENSE"
59
65
  ],
60
66
  "exports": {
61
- ".": "./zyn.js"
67
+ ".": "./src/agent.js",
68
+ "./agent": "./src/agent.js",
69
+ "./platforms": "./src/platforms/index.js",
70
+ "./platforms/baileys": "./src/platforms/baileys.js",
71
+ "./platforms/discord": "./src/platforms/discord.js",
72
+ "./platforms/telegram": "./src/platforms/telegram.js"
62
73
  },
63
74
  "preferGlobal": true
64
75
  }
package/src/agent.js ADDED
@@ -0,0 +1,168 @@
1
+ const { EventEmitter } = require('events');
2
+ const { runAgentTurn } = require('./core/agent');
3
+ const { createNewSessionState, loadSessionState, loadOrCreateSessionState, listSessions, saveState } = require('./utils/sessionStorage');
4
+ const { MODELS, DEFAULT_MODEL_KEY, DEFAULT_LANGUAGE } = require('./config');
5
+ const { listSkills, listModels } = require('./public/helpers');
6
+ const { runBackgroundWorker, detachBackgroundTurn } = require('./utils/backgroundWorker');
7
+ const { enqueueBackgroundTask, listBackgroundResults, consumeBackgroundResult } = require('./utils/sessionStorage');
8
+
9
+ function noopUi() {
10
+ return {
11
+ beginThinkingStream() {},
12
+ writeThinkingDelta() {},
13
+ endThinkingStream() {},
14
+ beginAssistantStream() {},
15
+ writeAssistantDelta() {},
16
+ endAssistantStream() {},
17
+ startThinkingIndicator() { return () => {}; },
18
+ pushAction() {},
19
+ logEvent() {},
20
+ paint: (text) => text,
21
+ };
22
+ }
23
+
24
+ function createAgent(options = {}) {
25
+ const emitter = new EventEmitter();
26
+ emitter.setMaxListeners(50);
27
+
28
+ const config = {
29
+ sessionId: options.sessionId || null,
30
+ cwd: options.cwd || process.cwd(),
31
+ model: options.model || DEFAULT_MODEL_KEY,
32
+ language: options.language || DEFAULT_LANGUAGE,
33
+ autoApprove: options.autoApprove !== undefined ? Boolean(options.autoApprove) : true,
34
+ personaPrompt: options.personaPrompt || '',
35
+ userId: options.userId || 'default',
36
+ resume: options.resume || false,
37
+ };
38
+
39
+ let state = null;
40
+ let initPromise = null;
41
+
42
+ async function ensureState() {
43
+ if (state) return state;
44
+ if (!initPromise) {
45
+ initPromise = (async () => {
46
+ if (config.sessionId) {
47
+ const loaded = await loadSessionState(config.sessionId, null);
48
+ if (loaded) {
49
+ if (config.cwd) loaded.cwd = config.cwd;
50
+ loaded.activeModel = config.model;
51
+ loaded.language = config.language;
52
+ loaded.autoApprove = config.autoApprove;
53
+ if (config.personaPrompt) loaded.personaPrompt = config.personaPrompt;
54
+ state = loaded;
55
+ return state;
56
+ }
57
+ }
58
+ if (config.resume) {
59
+ const loaded = await loadOrCreateSessionState(null, { forceNew: false, resume: true });
60
+ state = loaded.state;
61
+ return state;
62
+ }
63
+ state = await createNewSessionState(null);
64
+ state.cwd = config.cwd;
65
+ state.activeModel = config.model;
66
+ state.language = config.language;
67
+ state.autoApprove = config.autoApprove;
68
+ state.personaPrompt = config.personaPrompt;
69
+ await saveState(state);
70
+ return state;
71
+ })();
72
+ }
73
+ return initPromise;
74
+ }
75
+
76
+ emitter.send = async function send(userId, text) {
77
+ const s = await ensureState();
78
+ const userKey = userId || config.userId;
79
+ s.history.push({ role: 'user', content: String(text), userId: userKey });
80
+ const ui = noopUi();
81
+ const controller = new AbortController();
82
+ emitter.emit('turnStart', { userId: userKey, text });
83
+ try {
84
+ const result = await runAgentTurn(String(text), s, ui, { signal: controller.signal });
85
+ emitter.emit('turnEnd', { userId: userKey, text, content: result.content });
86
+ emitter.emit('message', { userId: userKey, role: 'assistant', content: result.content });
87
+ return result.content;
88
+ } catch (err) {
89
+ emitter.emit('error', err);
90
+ throw err;
91
+ } finally {
92
+ await saveState(s);
93
+ }
94
+ };
95
+
96
+ emitter.detach = function detach(userId, text) {
97
+ return (async () => {
98
+ const s = await ensureState();
99
+ const userKey = userId || config.userId;
100
+ const taskId = enqueueBackgroundTask({ sessionId: s.sessionId, input: String(text), detachedAt: new Date().toISOString() });
101
+ detachBackgroundTurn({
102
+ taskId,
103
+ sessionId: s.sessionId,
104
+ input: String(text),
105
+ cwd: s.cwd,
106
+ modelKey: s.activeModel,
107
+ language: s.language,
108
+ personaPrompt: s.personaPrompt,
109
+ autoApprove: s.autoApprove,
110
+ });
111
+ return taskId;
112
+ })();
113
+ };
114
+
115
+ emitter.consumeBackground = async function consumeBackground() {
116
+ const s = await ensureState();
117
+ const results = await listBackgroundResults(s.sessionId);
118
+ for (const r of results) {
119
+ emitter.emit('background', r);
120
+ await consumeBackgroundResult(r.taskId);
121
+ }
122
+ return results;
123
+ };
124
+
125
+ emitter.getState = async function getState() {
126
+ const s = await ensureState();
127
+ return {
128
+ sessionId: s.sessionId,
129
+ title: s.title,
130
+ cwd: s.cwd,
131
+ activeModel: s.activeModel,
132
+ language: s.language,
133
+ turnCount: s.turnCount,
134
+ historyLength: s.history.length,
135
+ };
136
+ };
137
+
138
+ emitter.reset = async function reset() {
139
+ const s = await ensureState();
140
+ s.history = [];
141
+ s.actionLog = [];
142
+ s.turnCount = 0;
143
+ s.memorySummary = '';
144
+ await saveState(s);
145
+ return true;
146
+ };
147
+
148
+ emitter.close = async function close() {
149
+ if (state) {
150
+ await saveState(state);
151
+ }
152
+ };
153
+
154
+ return emitter;
155
+ }
156
+
157
+ module.exports = {
158
+ createAgent,
159
+ listSkills,
160
+ listModels,
161
+ listSessions,
162
+ loadSessionState,
163
+ runBackgroundWorker,
164
+ runAgentTurn,
165
+ DEFAULT_LANGUAGE,
166
+ DEFAULT_MODEL_KEY,
167
+ MODELS,
168
+ };