zilmate 1.0.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 (166) hide show
  1. package/.env.example +14 -0
  2. package/README.md +326 -0
  3. package/agent-docs.md +264 -0
  4. package/dist/agents/chat.agent.d.ts +30 -0
  5. package/dist/agents/chat.agent.d.ts.map +1 -0
  6. package/dist/agents/chat.agent.js +16 -0
  7. package/dist/agents/chat.agent.js.map +1 -0
  8. package/dist/agents/docs-research.agent.d.ts +127 -0
  9. package/dist/agents/docs-research.agent.d.ts.map +1 -0
  10. package/dist/agents/docs-research.agent.js +29 -0
  11. package/dist/agents/docs-research.agent.js.map +1 -0
  12. package/dist/agents/image.agent.d.ts +9 -0
  13. package/dist/agents/image.agent.d.ts.map +1 -0
  14. package/dist/agents/image.agent.js +12 -0
  15. package/dist/agents/image.agent.js.map +1 -0
  16. package/dist/agents/manager.d.ts +140 -0
  17. package/dist/agents/manager.d.ts.map +1 -0
  18. package/dist/agents/manager.js +150 -0
  19. package/dist/agents/manager.js.map +1 -0
  20. package/dist/agents/post.agent.d.ts +7 -0
  21. package/dist/agents/post.agent.d.ts.map +1 -0
  22. package/dist/agents/post.agent.js +12 -0
  23. package/dist/agents/post.agent.js.map +1 -0
  24. package/dist/agents/quick-help.agent.d.ts +30 -0
  25. package/dist/agents/quick-help.agent.d.ts.map +1 -0
  26. package/dist/agents/quick-help.agent.js +16 -0
  27. package/dist/agents/quick-help.agent.js.map +1 -0
  28. package/dist/cli/confirm.d.ts +5 -0
  29. package/dist/cli/confirm.d.ts.map +1 -0
  30. package/dist/cli/confirm.js +35 -0
  31. package/dist/cli/confirm.js.map +1 -0
  32. package/dist/cli/doctor.d.ts +53 -0
  33. package/dist/cli/doctor.d.ts.map +1 -0
  34. package/dist/cli/doctor.js +141 -0
  35. package/dist/cli/doctor.js.map +1 -0
  36. package/dist/cli/format.d.ts +12 -0
  37. package/dist/cli/format.d.ts.map +1 -0
  38. package/dist/cli/format.js +167 -0
  39. package/dist/cli/format.js.map +1 -0
  40. package/dist/cli/interactive.d.ts +2 -0
  41. package/dist/cli/interactive.d.ts.map +1 -0
  42. package/dist/cli/interactive.js +80 -0
  43. package/dist/cli/interactive.js.map +1 -0
  44. package/dist/cli/setup.d.ts +14 -0
  45. package/dist/cli/setup.d.ts.map +1 -0
  46. package/dist/cli/setup.js +145 -0
  47. package/dist/cli/setup.js.map +1 -0
  48. package/dist/cli/triggers.d.ts +43 -0
  49. package/dist/cli/triggers.d.ts.map +1 -0
  50. package/dist/cli/triggers.js +221 -0
  51. package/dist/cli/triggers.js.map +1 -0
  52. package/dist/config/env.d.ts +26 -0
  53. package/dist/config/env.d.ts.map +1 -0
  54. package/dist/config/env.js +59 -0
  55. package/dist/config/env.js.map +1 -0
  56. package/dist/config/models.d.ts +22 -0
  57. package/dist/config/models.d.ts.map +1 -0
  58. package/dist/config/models.js +38 -0
  59. package/dist/config/models.js.map +1 -0
  60. package/dist/index.d.ts +3 -0
  61. package/dist/index.d.ts.map +1 -0
  62. package/dist/index.js +452 -0
  63. package/dist/index.js.map +1 -0
  64. package/dist/memory/composio-session.d.ts +9 -0
  65. package/dist/memory/composio-session.d.ts.map +1 -0
  66. package/dist/memory/composio-session.js +33 -0
  67. package/dist/memory/composio-session.js.map +1 -0
  68. package/dist/memory/history.d.ts +11 -0
  69. package/dist/memory/history.d.ts.map +1 -0
  70. package/dist/memory/history.js +33 -0
  71. package/dist/memory/history.js.map +1 -0
  72. package/dist/memory/local-store.d.ts +4 -0
  73. package/dist/memory/local-store.d.ts.map +1 -0
  74. package/dist/memory/local-store.js +24 -0
  75. package/dist/memory/local-store.js.map +1 -0
  76. package/dist/memory/long-term.d.ts +13 -0
  77. package/dist/memory/long-term.d.ts.map +1 -0
  78. package/dist/memory/long-term.js +67 -0
  79. package/dist/memory/long-term.js.map +1 -0
  80. package/dist/memory/redis.d.ts +4 -0
  81. package/dist/memory/redis.d.ts.map +1 -0
  82. package/dist/memory/redis.js +18 -0
  83. package/dist/memory/redis.js.map +1 -0
  84. package/dist/memory/scratchpad.d.ts +3 -0
  85. package/dist/memory/scratchpad.d.ts.map +1 -0
  86. package/dist/memory/scratchpad.js +22 -0
  87. package/dist/memory/scratchpad.js.map +1 -0
  88. package/dist/runtime/confirm.d.ts +13 -0
  89. package/dist/runtime/confirm.d.ts.map +1 -0
  90. package/dist/runtime/confirm.js +17 -0
  91. package/dist/runtime/confirm.js.map +1 -0
  92. package/dist/runtime/progress.d.ts +8 -0
  93. package/dist/runtime/progress.d.ts.map +1 -0
  94. package/dist/runtime/progress.js +15 -0
  95. package/dist/runtime/progress.js.map +1 -0
  96. package/dist/safety/approvals.d.ts +6 -0
  97. package/dist/safety/approvals.d.ts.map +1 -0
  98. package/dist/safety/approvals.js +8 -0
  99. package/dist/safety/approvals.js.map +1 -0
  100. package/dist/safety/limits.d.ts +8 -0
  101. package/dist/safety/limits.d.ts.map +1 -0
  102. package/dist/safety/limits.js +10 -0
  103. package/dist/safety/limits.js.map +1 -0
  104. package/dist/safety/redaction.d.ts +2 -0
  105. package/dist/safety/redaction.d.ts.map +1 -0
  106. package/dist/safety/redaction.js +9 -0
  107. package/dist/safety/redaction.js.map +1 -0
  108. package/dist/server.d.ts +54 -0
  109. package/dist/server.d.ts.map +1 -0
  110. package/dist/server.js +69 -0
  111. package/dist/server.js.map +1 -0
  112. package/dist/tools/app-knowledge.tool.d.ts +4 -0
  113. package/dist/tools/app-knowledge.tool.d.ts.map +1 -0
  114. package/dist/tools/app-knowledge.tool.js +30 -0
  115. package/dist/tools/app-knowledge.tool.js.map +1 -0
  116. package/dist/tools/composio.tool.d.ts +15 -0
  117. package/dist/tools/composio.tool.d.ts.map +1 -0
  118. package/dist/tools/composio.tool.js +272 -0
  119. package/dist/tools/composio.tool.js.map +1 -0
  120. package/dist/tools/docs-fetch.tool.d.ts +6 -0
  121. package/dist/tools/docs-fetch.tool.d.ts.map +1 -0
  122. package/dist/tools/docs-fetch.tool.js +62 -0
  123. package/dist/tools/docs-fetch.tool.js.map +1 -0
  124. package/dist/tools/docs-search.tool.d.ts +4 -0
  125. package/dist/tools/docs-search.tool.d.ts.map +1 -0
  126. package/dist/tools/docs-search.tool.js +14 -0
  127. package/dist/tools/docs-search.tool.js.map +1 -0
  128. package/dist/tools/image-generate.tool.d.ts +22 -0
  129. package/dist/tools/image-generate.tool.d.ts.map +1 -0
  130. package/dist/tools/image-generate.tool.js +113 -0
  131. package/dist/tools/image-generate.tool.js.map +1 -0
  132. package/dist/tools/memory.tool.d.ts +18 -0
  133. package/dist/tools/memory.tool.d.ts.map +1 -0
  134. package/dist/tools/memory.tool.js +53 -0
  135. package/dist/tools/memory.tool.js.map +1 -0
  136. package/dist/tools/post-generate.tool.d.ts +5 -0
  137. package/dist/tools/post-generate.tool.d.ts.map +1 -0
  138. package/dist/tools/post-generate.tool.js +19 -0
  139. package/dist/tools/post-generate.tool.js.map +1 -0
  140. package/dist/tools/scratchpad.tool.d.ts +7 -0
  141. package/dist/tools/scratchpad.tool.d.ts.map +1 -0
  142. package/dist/tools/scratchpad.tool.js +29 -0
  143. package/dist/tools/scratchpad.tool.js.map +1 -0
  144. package/dist/tools/triggers.tool.d.ts +72 -0
  145. package/dist/tools/triggers.tool.d.ts.map +1 -0
  146. package/dist/tools/triggers.tool.js +108 -0
  147. package/dist/tools/triggers.tool.js.map +1 -0
  148. package/dist/tools/web-search.tool.d.ts +94 -0
  149. package/dist/tools/web-search.tool.d.ts.map +1 -0
  150. package/dist/tools/web-search.tool.js +214 -0
  151. package/dist/tools/web-search.tool.js.map +1 -0
  152. package/dist/tools/zilo-docs.tool.d.ts +26 -0
  153. package/dist/tools/zilo-docs.tool.d.ts.map +1 -0
  154. package/dist/tools/zilo-docs.tool.js +150 -0
  155. package/dist/tools/zilo-docs.tool.js.map +1 -0
  156. package/install.ps1 +62 -0
  157. package/package.json +88 -0
  158. package/src/doc/README.md +49 -0
  159. package/src/doc/admin-tools-and-sms-campaigns.md +99 -0
  160. package/src/doc/escalation-checklist.md +97 -0
  161. package/src/doc/payments-and-payouts.md +82 -0
  162. package/src/doc/shift-lifecycle-and-disputes.md +95 -0
  163. package/src/doc/support-macros.md +40 -0
  164. package/src/doc/venue-support-playbook.md +145 -0
  165. package/src/doc/verification-and-trust.md +88 -0
  166. package/src/doc/worker-support-playbook.md +160 -0
package/.env.example ADDED
@@ -0,0 +1,14 @@
1
+ AI_GATEWAY_API_KEY=
2
+ COMPOSIO_API_KEY=
3
+ ZILMATE_USER_ID=
4
+ TAVILY_API_KEY=
5
+ UPSTASH_REDIS_REST_URL=
6
+ UPSTASH_REDIS_REST_TOKEN=
7
+ ZILO_MANAGER_MODEL=minimax/minimax-m3
8
+ ZILO_HELP_MODEL=alibaba/qwen3.7-plus
9
+ ZILO_POST_MODEL=alibaba/qwen3.7-plus
10
+ ZILO_IMAGE_DEFAULT_PROVIDER=openai
11
+ ZILO_IMAGE_OPENAI_MODEL=openai/gpt-image-2
12
+ ZILO_IMAGE_GEMINI_MODEL=google/gemini-3-pro-image
13
+ # Backward-compatible legacy override for Gemini/default image model.
14
+ ZILO_IMAGE_MODEL=
package/README.md ADDED
@@ -0,0 +1,326 @@
1
+ # ZilMate
2
+
3
+ ZilMate is a CLI-first general assistant with deep built-in ZiloShift expertise. It can chat, answer support questions, draft posts, research docs/web sources, generate image assets, and use Composio for external app tools such as GitHub, Gmail, Slack, Notion, Stripe, and Supabase.
4
+
5
+ The GitHub project can remain `zilo-manager`, but the installable npm package and command are both `zilmate`.
6
+
7
+ ## Install ZilMate
8
+
9
+ ### Published npm package
10
+
11
+ After the package is published to npm:
12
+
13
+ ```powershell
14
+ npm install -g zilmate
15
+ zilmate setup
16
+ zilmate --help
17
+ ```
18
+
19
+ ### GitHub/private install
20
+
21
+ Before npm publishing, install directly from the GitHub repo:
22
+
23
+ ```powershell
24
+ npm install -g github:zester4/zilo-manager
25
+ zilmate setup
26
+ zilmate --help
27
+ ```
28
+
29
+ ### Windows installer helper
30
+
31
+ From PowerShell, the helper can install from GitHub by default:
32
+
33
+ ```powershell
34
+ iwr https://raw.githubusercontent.com/zester4/zilo-manager/main/install.ps1 | iex
35
+ ```
36
+
37
+ The installer runs the full first-use flow:
38
+
39
+ 1. Installs ZilMate globally.
40
+ 2. Runs `zilmate setup` to collect `AI_GATEWAY_API_KEY`, optional `COMPOSIO_API_KEY`, optional `TAVILY_API_KEY`, and optional Redis keys.
41
+ 3. Lets users skip Composio, Tavily, and Redis keys.
42
+ 4. Runs `zilmate ping` to verify the key.
43
+ 5. Starts `zilmate talk`.
44
+
45
+ To install without setup:
46
+
47
+ ```powershell
48
+ iex "& { $((iwr -UseBasicParsing https://raw.githubusercontent.com/zester4/zilo-manager/main/install.ps1).Content) } -NoSetup"
49
+ ```
50
+
51
+ To skip ping or chat startup:
52
+
53
+ ```powershell
54
+ iex "& { $((iwr -UseBasicParsing https://raw.githubusercontent.com/zester4/zilo-manager/main/install.ps1).Content) } -NoPing -NoTalk"
55
+ ```
56
+
57
+ To install from npm after publishing:
58
+
59
+ ```powershell
60
+ iex "& { $((iwr -UseBasicParsing https://raw.githubusercontent.com/zester4/zilo-manager/main/install.ps1).Content) } -Source npm"
61
+ ```
62
+
63
+ The helper checks for Node/npm, runs `npm install -g`, prints `zilmate --help`, starts setup unless `-NoSetup` is passed, verifies auth unless `-NoPing` is passed, and starts chat unless `-NoTalk` is passed.
64
+
65
+ ## Setup
66
+
67
+ 1. Install dependencies:
68
+
69
+ ```powershell
70
+ npm install
71
+ ```
72
+
73
+ 2. Create `.env` from `.env.example`:
74
+
75
+ The easiest path is:
76
+
77
+ ```powershell
78
+ zilmate setup
79
+ ```
80
+
81
+ It asks for `AI_GATEWAY_API_KEY`, optionally asks for `COMPOSIO_API_KEY`, `TAVILY_API_KEY`, and Upstash Redis keys, then writes a local `.env`. Composio, Tavily, and Redis can be skipped.
82
+
83
+ You can also create `.env` manually:
84
+
85
+ ```env
86
+ AI_GATEWAY_API_KEY=your_vercel_ai_gateway_key
87
+ COMPOSIO_API_KEY=your_composio_key
88
+ ZILMATE_USER_ID=zilmate-generated-local-user-id
89
+ TAVILY_API_KEY=your_tavily_key
90
+ UPSTASH_REDIS_REST_URL=
91
+ UPSTASH_REDIS_REST_TOKEN=
92
+ ZILO_MANAGER_MODEL=minimax/minimax-m3
93
+ ZILO_HELP_MODEL=alibaba/qwen3.7-plus
94
+ ZILO_POST_MODEL=alibaba/qwen3.7-plus
95
+ ZILO_IMAGE_DEFAULT_PROVIDER=openai
96
+ ZILO_IMAGE_OPENAI_MODEL=openai/gpt-image-2
97
+ ZILO_IMAGE_GEMINI_MODEL=google/gemini-3-pro-image
98
+ ZILO_IMAGE_MODEL=
99
+ ```
100
+
101
+ Composio is optional. If `COMPOSIO_API_KEY` is set, ZilMate creates a stable local `ZILMATE_USER_ID`, reuses Composio sessions per chat session, and lets Composio manage app auth links and connected accounts.
102
+
103
+ Redis is optional. If `UPSTASH_REDIS_REST_URL` and `UPSTASH_REDIS_REST_TOKEN` are set, chat turns, scratchpads, and Composio session ids use Redis. If they are missing, ZilMate falls back to local files under `.zilo-manager/`.
104
+
105
+ ## Development Commands
106
+
107
+ Use these while working inside the project folder:
108
+
109
+ ```powershell
110
+ npm run build
111
+ npm run zilmate -- --help
112
+ npm run zilmate -- setup
113
+ npm run zilmate -- doctor
114
+ npm run zilmate -- config
115
+ npm run zilmate -- models
116
+ npm run zilmate -- apps status
117
+ npm run zilmate -- triggers listen
118
+ npm run zilmate -- triggers types github
119
+ npm run zilmate -- triggers list
120
+ npm run zilmate -- remember "Prefers concise support replies"
121
+ npm run zilmate -- recall support
122
+ npm run zilmate -- memory list
123
+ npm run zilmate -- talk
124
+ npm run zilmate -- talk --session launch
125
+ npm run zilmate -- help "why can't a worker apply?"
126
+ npm run zilmate -- post "WhatsApp status for workers in Accra"
127
+ npm run zilmate -- research "Vercel AI SDK ToolLoopAgent"
128
+ npm run zilmate -- image --model openai --size 1024x1024 "ZiloShift launch poster for Ghana workers"
129
+ npm run zilmate -- image --model gemini "ZiloShift launch poster for Ghana workers"
130
+ npm run zilmate -- manager "Create a plan for helping venues post shifts"
131
+ ```
132
+
133
+ Shortcut:
134
+
135
+ ```powershell
136
+ npm run talk
137
+ npm run doctor
138
+ ```
139
+
140
+ ## Global CLI
141
+
142
+ For local development, link the command from this folder:
143
+
144
+ ```powershell
145
+ npm run build
146
+ npm link
147
+ ```
148
+
149
+ Then use ZilMate directly:
150
+
151
+ ```powershell
152
+ zilmate --help
153
+ zilmate setup
154
+ zilmate doctor
155
+ zilmate env check
156
+ zilmate config
157
+ zilmate talk
158
+ zilmate ping
159
+ zilmate models
160
+ zilmate apps status
161
+ zilmate triggers listen
162
+ zilmate triggers types github
163
+ zilmate triggers create GITHUB_BRANCH_CREATED_TRIGGER --dry-run --owner zester4 --repo zilo-manager
164
+ zilmate triggers create GITHUB_COMMIT_EVENT --owner zester4 --repo zilo-manager
165
+ zilmate triggers list
166
+ zilmate remember "Use a warm but concise support tone"
167
+ zilmate recall support
168
+ zilmate memory list
169
+ zilmate help "worker cannot see shifts"
170
+ zilmate image --model openai --size 1024x1024 "ZiloShift launch poster"
171
+ ```
172
+
173
+ ## Server SDK
174
+
175
+ ZilMate can also be used as a server-side SDK inside apps, dashboards, API routes, and background jobs. The SDK is server-only because it uses API keys, local/Redis memory, Tavily, Composio, and AI Gateway credentials.
176
+
177
+ ```ts
178
+ import { createZilMate } from 'zilmate/server';
179
+
180
+ const zilmate = createZilMate({
181
+ sessionId: 'support-ticket-123',
182
+ });
183
+
184
+ const result = await zilmate.chat({
185
+ message: 'Plan my day, then help me draft a ZiloShift worker update.',
186
+ });
187
+
188
+ console.log(result.text);
189
+ ```
190
+
191
+ In Next.js, call ZilMate from an API route or server action, then connect your UI to that endpoint:
192
+
193
+ ```ts
194
+ // app/api/zilmate/route.ts
195
+ import { createZilMate } from 'zilmate/server';
196
+
197
+ export async function POST(req: Request) {
198
+ const { message, sessionId } = await req.json();
199
+ const zilmate = createZilMate({ sessionId });
200
+ const result = await zilmate.chat({ message });
201
+
202
+ return Response.json(result);
203
+ }
204
+ ```
205
+
206
+ Available SDK methods:
207
+
208
+ - `chat({ message })`: general personal assistant backed by the manager agent.
209
+ - `manager({ message | prompt })`: explicit manager orchestration.
210
+ - `help({ question | message })`: fast ZiloShift troubleshooting.
211
+ - `guide({ message })`: ZiloShift workflow conversation.
212
+ - `post({ prompt })`: WhatsApp/status/social copy.
213
+ - `research({ query | message })`: local docs and web research.
214
+ - `image({ prompt, provider, size, outputDir })`: image generation.
215
+ - `remember({ text, tags })`, `recall({ query, limit })`, `listMemories()`, `forget(id)`, `clearMemories()`: durable memory helpers.
216
+
217
+ For UI integrations, pass `onProgress` to render agent/tool progress and `confirm` to approve or block external write-like Composio actions.
218
+
219
+ ## Command Shape
220
+
221
+ - `talk`: persistent interactive chat with the main manager agent. This is the best mode for normal use and renders rich terminal Markdown.
222
+ - `manager`: one-shot manager orchestration. It can delegate to subagents and use scratchpad tools.
223
+ - `doctor`: check local setup, required/optional keys, Node version, memory folder, Redis completeness, and optional live Gateway/Composio status with `--live`.
224
+ - `env check`: environment-readiness alias for `doctor`.
225
+ - `config`: sanitized config summary without secrets.
226
+ - `remember`: save durable long-term memory.
227
+ - `recall`: search durable long-term memory.
228
+ - `forget`: delete one memory by id, or use `--all`.
229
+ - `memory list`: list saved durable memories.
230
+ - `apps status`: show whether Composio is configured, the local `ZILMATE_USER_ID`, the current Composio session id, and connected/available toolkit status when the SDK can fetch it.
231
+ - `triggers listen`: stream Composio trigger events into the terminal until Ctrl+C.
232
+ - `triggers types [toolkit]`: list available trigger types, optionally for one toolkit.
233
+ - `triggers info <trigger>`: show trigger config and payload schemas.
234
+ - `triggers create <trigger> --flag value`: create a trigger instance; unknown flags become trigger config.
235
+ - `triggers list`: list trigger instances.
236
+ - `help`: fast troubleshooting and app guidance.
237
+ - `chat`: one-shot natural dialogue about ZiloShift workflows.
238
+ - `post`: WhatsApp/status/social copy generation.
239
+ - `research`: local docs, allowlisted docs, Tavily search/extract/map/crawl/deep research, and sourced summaries.
240
+ - `image`: Gateway image generation that saves files under `outputs/images/`. Use `--model openai|chatgpt|gemini` and optionally `--size 1024x1024` for OpenAI.
241
+ - `models`: selected models, Gateway availability warnings, and active memory backend.
242
+ - `ping`: tiny Gateway text call to verify auth.
243
+ - `setup`: interactive `.env` setup for the required AI Gateway key, optional Composio external app tools, optional Tavily search, optional Redis memory, and model defaults.
244
+
245
+ ## Agent Architecture
246
+
247
+ ZilMate uses a manager agent that delegates to focused subagents and external tools:
248
+
249
+ - Quick Help: short troubleshooting and app usage guidance.
250
+ - Chat: broader ZiloShift workflow conversation.
251
+ - Post: launch messages, WhatsApp statuses, captions, and outreach copy.
252
+ - Research: local Zilo docs first, then external docs/web research when needed.
253
+ - Image: image generation through Gateway image models.
254
+ - Composio: external app discovery, auth links, schemas, and execution, attached only to the manager.
255
+ - Memory: durable ZilMate facts and preferences saved locally or in Redis, available through CLI commands and manager tools.
256
+
257
+ Local ZiloShift docs live under `src/doc/`. ZilMate reads them on demand through dedicated tools instead of dumping all docs into every prompt. The manager prefers these local docs for ZiloShift support, worker, venue, payment, verification, SMS, and dispute questions.
258
+
259
+ ## External Apps With Composio
260
+
261
+ Run setup and add a Composio key:
262
+
263
+ ```powershell
264
+ zilmate setup
265
+ zilmate apps status
266
+ ```
267
+
268
+ In `zilmate talk`, ask for the external task naturally. If an account is not connected yet, ZilMate uses Composio connection tools and prints the connect link returned by Composio. ZilMate does not implement custom OAuth flows.
269
+
270
+ Read/search/schema/auth-link tools can run without confirmation. Write-like external app actions such as create, update, delete, send, post, publish, invite, transfer, charge, refund, cancel, approve, revoke, workbench, or bash require `Proceed? (y/N)` in the terminal. In noninteractive mode, write-like actions are blocked.
271
+
272
+ ## Trigger Events
273
+
274
+ For live terminal events, use Composio trigger listening:
275
+
276
+ ```powershell
277
+ zilmate triggers types github
278
+ zilmate triggers create GITHUB_COMMIT_EVENT --owner zester4 --repo zilo-manager
279
+ zilmate triggers listen
280
+ ```
281
+
282
+ `listen` streams matching trigger events until Ctrl+C. Use filters when needed:
283
+
284
+ ```powershell
285
+ zilmate triggers listen --toolkit gmail
286
+ zilmate triggers listen --trigger ti_abc123
287
+ zilmate triggers listen --trigger-slug GMAIL_NEW_EMAIL_EVENT --once
288
+ ```
289
+
290
+ This is terminal-local. For persistent public callbacks, use webhook/tunnel support later.
291
+
292
+ The manager agent also has trigger tools. In `zilmate talk`, you can ask:
293
+
294
+ ```text
295
+ show me GitHub trigger types
296
+ prepare a branch-created trigger for zester4/zilo-manager
297
+ create that trigger
298
+ ```
299
+
300
+ ZilMate should discover current trigger slugs first, inspect the trigger schema, prefer a dry-run payload, and ask for confirmation before creating a real trigger.
301
+
302
+ ## Model Notes
303
+
304
+ - Manager/orchestration default: `minimax/minimax-m3`.
305
+ - Recommended help/post override: `alibaba/qwen3.7-plus` in `.env`.
306
+ - If help/post env vars are blank, ZilMate falls back to the internal cheap-model list in `src/config/models.ts`.
307
+ - Default image provider: OpenAI GPT Image 2 via `openai/gpt-image-2` and AI SDK `generateImage`.
308
+ - Alternate image provider: Gemini 3 Pro Image via `google/gemini-3-pro-image` and `generateText` file outputs.
309
+ - GPT-2 is not used for images.
310
+
311
+ ## Research And Memory
312
+
313
+ - Zilo docs are searched/read locally first for product behavior.
314
+ - Tavily powers web search, URL extraction, site mapping, small capped crawls, and deep research.
315
+ - Web crawling and deep research are intentionally heavier tools and should be used only when local docs/search are not enough.
316
+ - Scratchpads keep intermediate notes outside the main prompt context.
317
+ - Long-term memory stores stable preferences and durable project facts. Use `zilmate remember`, `zilmate recall`, `zilmate forget`, and `zilmate memory list`.
318
+ - `zilmate talk` automatically recalls relevant long-term memories for each message.
319
+ - Redis is optional; local file memory is the fallback.
320
+ - `zilmate setup` creates or updates the local `.env` used by the CLI.
321
+
322
+ ## Safety Notes
323
+
324
+ ZilMate can guide, research, draft, generate assets, and run approved external app actions through Composio. It should not claim that live external or ZiloShift data changed unless a tool result confirms it.
325
+
326
+ Before adding real actions around payments, identity, SMS, users, or admin operations, add stronger permission levels, confirmation gates, audit logs, and behavioral evals.
package/agent-docs.md ADDED
@@ -0,0 +1,264 @@
1
+ Building Subagents in the Vercel AI SDK v6
2
+ 11 min read
3
+ ·
4
+ Josh
5
+ Josh
6
+ Copy article
7
+
8
+ https://upstash.com/blog/subagents-in-ai-sdk-v6
9
+ A subagent in the AI SDK v6 is one agent wrapped inside a tool() so another agent can call it. The parent agent treats the subagent like any other tool: it sends a prompt, gets back text, and decides what to do next.
10
+
11
+ I find them to be the single most useful pattern to avoid context bloat. No matter how large their task or own context load is, they only return the most important information from their process back to the main agent.
12
+
13
+ Subagents take care of context-intensive tasks (e.g. research)
14
+ Subagents take care of context-intensive tasks (e.g. research)
15
+
16
+ The new v6 ToolLoopAgent
17
+ Before v6, building a multi-agent setup meant chaining generateText calls and passing messages between them. The functions to generate or stream text were independant primitives:
18
+
19
+ In v5, generateText and streamText are primitives
20
+ In v5, generateText and streamText are primitives
21
+
22
+ In v6, an agent is its own class we can now call functions on. We define it once with a model, instructions, and tools, then call generate or stream on it:
23
+
24
+ New: tools, prompts etc. move to a single class
25
+ New: tools, prompts etc. move to a single class
26
+
27
+ The class is ToolLoopAgent. The name describes what it does: it runs the model, executes any tool calls, feeds the results back, and loops until a stop condition fires.
28
+
29
+
30
+ import { anthropic } from "@ai-sdk/anthropic";
31
+ import { stepCountIs, ToolLoopAgent } from "ai";
32
+
33
+ const agent = new ToolLoopAgent({
34
+ model: anthropic("claude-sonnet-4-6"),
35
+ instructions: "You are a research agent. Answer the task autonomously.",
36
+ tools: {
37
+ /* ... */
38
+ },
39
+ stopWhen: stepCountIs(10),
40
+ });
41
+
42
+ const result = await agent.generate({ prompt: "Summarize the latest on X." });
43
+ console.log(result.text);
44
+ A subagent is just a tool
45
+ A subagent is a ToolLoopAgent that a parent agent calls through a tool(). The tool's execute function runs the subagent and returns its text.
46
+
47
+
48
+ import { anthropic } from "@ai-sdk/anthropic";
49
+ import { stepCountIs, tool, ToolLoopAgent } from "ai";
50
+ import { z } from "zod";
51
+
52
+ const researchSubagent = new ToolLoopAgent({
53
+ model: anthropic("claude-sonnet-4-6"),
54
+ instructions: "You are a focused research subagent. Return only a summary.",
55
+ stopWhen: stepCountIs(10),
56
+ });
57
+
58
+ const researchTool = tool({
59
+ description: "Delegate a research task to a subagent.",
60
+ inputSchema: z.object({ prompt: z.string() }),
61
+ execute: async ({ prompt }, { abortSignal }) => {
62
+ const result = await researchSubagent.generate({ prompt, abortSignal });
63
+ return result.text;
64
+ },
65
+ });
66
+
67
+ const parentAgent = new ToolLoopAgent({
68
+ model: anthropic("claude-sonnet-4-6"),
69
+ instructions: "Delegate research, then synthesize an answer.",
70
+ tools: { research: researchTool },
71
+ stopWhen: stepCountIs(10),
72
+ });
73
+ Two details are important here.
74
+
75
+ First, the tool field is inputSchema, not parameters. Earlier AI SDK versions used parameters; v5 renamed it to inputSchema to align with the Model Context Protocol, and v6 keeps that name.
76
+
77
+ Second, the execute function takes abortSignal from its second argument and passes it into the subagent. If the parent request is cancelled, that cancellation reaches the subagent too. Without it, a cancelled request leaves subagents running in the background, still using tokens.
78
+
79
+ Controlling the subagent output
80
+ By default, the parent receives whatever the subagent tool returns. A research subagent might run ten steps and produce a lot of text, and we may not want all of that landing back in the parent's context window.
81
+
82
+ With toModelOutput, we can decouple what the tool returns from what gets passed into the parent model. It's like a separate parsing step.
83
+
84
+
85
+ const researchTool = tool({
86
+ description: "Delegate a research task to a subagent.",
87
+ inputSchema: z.object({ prompt: z.string() }),
88
+ execute: async ({ prompt }, { abortSignal }) => {
89
+ const result = await researchSubagent.generate({ prompt, abortSignal });
90
+ return result.text;
91
+ },
92
+ toModelOutput: ({ output }) => ({ type: "text", value: output }),
93
+ });
94
+ This way the parent's context stays small while the subagent can consume an almost arbitrary amount of tokens, just bounded by it's context limit. Because either way, it will not bloat our parent.
95
+
96
+ This patterns is also super useful for keeping the parent's token count low as the number of subagents grows.
97
+
98
+ Creating a stop condition
99
+ A ToolLoopAgent keeps looping until a StopCondition tells it to stop. The default is stepCountIs(20), so an agent with no stopWhenwill run up to 20 steps:
100
+
101
+
102
+ import { anthropic } from "@ai-sdk/anthropic";
103
+ import { hasToolCall, stepCountIs, type StopCondition } from "ai";
104
+
105
+ // custom stop condition
106
+ const stopAfterAnyToolUse: StopCondition<any, any> = ({ steps }) =>
107
+ steps.some((step) => step.toolCalls.length > 0);
108
+
109
+ const agent = new ToolLoopAgent({
110
+ model: anthropic("claude-sonnet-4-6"),
111
+ stopWhen: [stepCountIs(10), hasToolCall("done"), stopAfterAnyToolUse],
112
+ });
113
+ We can pass an array of conditions, and the loop stops when any one of them is true. stepCountIs(n) caps the step count, hasToolCall(name) stops once the agent uses any tool, and a custom function gets the full steps array so we can stop on anything we can compute from it, like a token budget.
114
+
115
+ By the way, prepareStep runs before every step and lets us change the model, the tools, or the messages for that step:
116
+
117
+
118
+ const agent = new ToolLoopAgent({
119
+ model: anthropic("claude-sonnet-4-6"),
120
+ tools: { research: researchTool, done: doneTool },
121
+ prepareStep: ({ stepNumber }) => ({
122
+ toolChoice: stepNumber > 8 ? { type: "tool", toolName: "done" } : "auto",
123
+ }),
124
+ });
125
+ This one forces the agent toward a done tool as it nears its step limit, instead of letting it stall.
126
+
127
+ The isolation problem
128
+ A subagent invocation starts with a fresh context window every time. The subagents docs call context isolation a feature, and for a single delegated task it is. The subagent doesn't load the parent's full history, and the parent shouldn't know about the subagent's intermediate steps.
129
+
130
+ The isolation goes both ways. But in two cases it kinda gets in the way:
131
+
132
+ Parallel subagents. The main agent runs three research subagents at once and none of them can see what the others found. If two should avoid duplicating work, there's no way for them to coordinate.
133
+ Separate requests. In serverless, each HTTP request can be a cold start. Anything a subagent held in memory on the last request is gone. The orchestrator on the second request doesn't know what the subagents did on the first request.
134
+ Parallel subagents cannot talk to each other.
135
+ Parallel subagents cannot talk to each other.
136
+
137
+ Moving the shared state out of process fixes both problems. The official memory docs point at hosted memory services for this, but for short-lived agent state we use Redis. It works with HTTP and the key expiry handles cleanup automatically.
138
+
139
+ Sharing state across subagents with Redis
140
+ A pattern I really like is a "shared scratchpad". It's one Redis string keyed by the current message id. Each subagent gets two tools: one to read what the others have already written, and one to append its own findings. We pass the same mocked message id to every subagent so they all point at the same key.
141
+
142
+
143
+ import { redis } from "@/lib/redis";
144
+ import { anthropic } from "@ai-sdk/anthropic";
145
+ import { stepCountIs, tool, ToolLoopAgent } from "ai";
146
+ import { z } from "zod";
147
+
148
+ function createNoteTools(messageId: string) {
149
+ return {
150
+ readNotes: tool({
151
+ description: "Read what the other subagents have found so far.",
152
+ inputSchema: z.object({}),
153
+ execute: async () => {
154
+ return (await redis.get<string>(`notes:${messageId}`)) ?? "(empty)";
155
+ },
156
+ }),
157
+ appendToNotes: tool({
158
+ description: "Append your findings to the shared notes.",
159
+ inputSchema: z.object({ findings: z.string() }),
160
+ execute: async ({ findings }) => {
161
+ await redis.append(`notes:${messageId}`, `\n${findings}`);
162
+ return "Appended.";
163
+ },
164
+ }),
165
+ };
166
+ }
167
+
168
+ // this comes from the ai sdk
169
+ const EXAMPLE_MESSAGE_ID = "example-run-001";
170
+
171
+ const researchSubagent = new ToolLoopAgent({
172
+ model: anthropic("claude-sonnet-4-6"),
173
+ instructions: `You are a research subagent. Read your notes to see what others found, then append your research.`,
174
+ tools: createNoteTools(EXAMPLE_MESSAGE_ID),
175
+ stopWhen: stepCountIs(10),
176
+ });
177
+
178
+ const parent = new ToolLoopAgent({
179
+ model: anthropic("claude-sonnet-4-6"),
180
+ instructions: `Start three research subagents in parallel on these topics: 1. Serverless databases 2. Edge computing 3. AI inference costs.`,
181
+ tools: {
182
+ subagent: tool({
183
+ description: "Run a research subagent on a topic.",
184
+ inputSchema: z.object({ topic: z.string() }),
185
+ execute: async ({ topic }, { abortSignal }) => {
186
+ const result = await researchSubagent.generate({
187
+ prompt: `Research this topic: ${topic}`,
188
+ abortSignal,
189
+ });
190
+ return result.text;
191
+ },
192
+ }),
193
+ readNotes: createNoteTools(EXAMPLE_MESSAGE_ID).readNotes,
194
+ },
195
+ stopWhen: stepCountIs(10),
196
+ });
197
+
198
+ const result = await parent.generate({ prompt: "Start the research." });
199
+ Each subagent runs in isolation but writes into the same Redis string. The parent kicks off the three subagents, and once they finish it calls readNotes itself to pull the full notes before synthesizing. Anthropic's orchestrator-workers pattern is the same shape: a central agent splits the work, workers run it, the central agent synthesizes.
200
+
201
+ One note: this works because research subtopics are independent. If subagent B needs what subagent A found, we can't fan them out in parallel. We run them in sequence, or have the parent make a second round of calls after reading the first round's results from Redis.
202
+
203
+ This patterns also allows us to implement a mechanism for the main agent to follow up (e.g. "keep chating") to research subagents. Because they keep their own message history and state, if the main model is unhappy or wants to follow up, we could simply pass the conversation ID into the research agent and it automatically can read and interact with previous notes.
204
+
205
+ Persisting message history across requests
206
+ The second use of Redis is saving message history. The AI SDK's useChat works with UIMessage[]. We save that array to Redis at the end of a request and load it at the start of the next one.
207
+
208
+
209
+ import { Redis } from "@upstash/redis";
210
+ import type { UIMessage } from "ai";
211
+
212
+ const redis = new Redis({
213
+ url: process.env.UPSTASH_REDIS_REST_URL!,
214
+ token: process.env.UPSTASH_REDIS_REST_TOKEN!,
215
+ });
216
+
217
+ async function saveHistory(sessionId: string, messages: UIMessage[]) {
218
+ await redis.set(`chat:${sessionId}`, messages, { ex: 86_400 });
219
+ }
220
+
221
+ async function loadHistory(sessionId: string) {
222
+ const messages = await redis.get<UIMessage[]>(`chat:${sessionId}`);
223
+ return messages ?? [];
224
+ }
225
+ Streaming subagent progress to the UI
226
+ If a subagent runs for a while, we want to show the user it is working instead of "freezing" the UI until it finishes. A tool's execute can be an async generator. Each value it yields becomes a partial tool result that the client can render before the final chunk arrives.
227
+
228
+
229
+ import { readUIMessageStream, tool } from "ai";
230
+ import { z } from "zod";
231
+
232
+ const streamingResearchTool = tool({
233
+ description: "Delegate research to a streaming subagent.",
234
+ inputSchema: z.object({ prompt: z.string() }),
235
+ async *execute({ prompt }, { abortSignal }) {
236
+ const result = await researchSubagent.stream({ prompt, abortSignal });
237
+
238
+ for await (const message of readUIMessageStream({
239
+ stream: result.toUIMessageStream(),
240
+ })) {
241
+ yield message;
242
+ }
243
+ },
244
+ });
245
+ The streamed result exposes a UI message stream. The readUIMessageStream helper turns that into an async iterable, where each value is the full message built up so far. The generator yields each update, and the client can now render the subagent's progress in real time.
246
+
247
+ When to use a subagent and when not to
248
+ Subagents add a layer of complexity. Every level of delegation is another model running its own loop. A single ToolLoopAgent with a good set of tools handles most tasks, and it is cheaper and easier to debug.
249
+
250
+ But on the other hand, I find subagents to be the single most useful tool to avoid context bloat. By splitting my research and code verification into separate subagents for a project I'm building, the main model's output has become significantly better.
251
+
252
+ So I'd add a subagent when one of these is true:
253
+
254
+ Situation Single agent Subagent
255
+ One task, a handful of tools Cheaper, easier to debug. Wins Overkill
256
+ Work that fans out into independent subtasks Context bloat Wins. Run them in parallel, isolate each context.
257
+ One subtask needs a different model or tool set Awkward to switch mid-loop Wins. Each subagent has its own model and tools.
258
+ Exploration that would blow the context window Hits the model's limit or context bloat Wins. toModelOutput keeps the parent's context smal
259
+ Recap
260
+ A subagent is a ToolLoopAgent wrapped in a tool(); the parent calls it like any tool.
261
+ Pass the abortSignal through so cancellation can reach the subagent.
262
+ Subagent contexts are isolated by design
263
+ With a shared Redis string keyed by a mocked message id, we can give parallel subagents a "scratchpad", and save UIMessage[] to Redis to persist message history.
264
+ I'd add subagents when work is parallel, needs isolated context, or needs a different model; otherwise a single agent is the right default.
@@ -0,0 +1,30 @@
1
+ import { ToolLoopAgent } from 'ai';
2
+ export declare function createChatAgent(): ToolLoopAgent<never, {
3
+ appKnowledge: import("ai").Tool<{
4
+ key: "agent-docs" | "readme";
5
+ }, string>;
6
+ listZiloDocs: import("ai").Tool<Record<string, never>, {
7
+ key: string;
8
+ title: string;
9
+ description: string;
10
+ }[]>;
11
+ readZiloDoc: import("ai").Tool<{
12
+ key: string;
13
+ maxChars?: number | undefined;
14
+ }, {
15
+ key: string;
16
+ title: string;
17
+ description: string;
18
+ content: string;
19
+ }>;
20
+ searchZiloDocs: import("ai").Tool<{
21
+ query: string;
22
+ maxResults?: number | undefined;
23
+ }, {
24
+ key: string;
25
+ title: string;
26
+ description: string;
27
+ snippet: string;
28
+ }[]>;
29
+ }, never>;
30
+ //# sourceMappingURL=chat.agent.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chat.agent.d.ts","sourceRoot":"","sources":["../../src/agents/chat.agent.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,aAAa,EAAE,MAAM,IAAI,CAAC;AAKhD,wBAAgB,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;UAU9B"}
@@ -0,0 +1,16 @@
1
+ import { stepCountIs, ToolLoopAgent } from 'ai';
2
+ import { models } from '../config/models.js';
3
+ import { appKnowledgeTool } from '../tools/app-knowledge.tool.js';
4
+ import { ziloDocsTools } from '../tools/zilo-docs.tool.js';
5
+ export function createChatAgent() {
6
+ return new ToolLoopAgent({
7
+ model: models.chat,
8
+ instructions: 'You are the ZiloShift conversational app guide. Explain workflows for workers, venues, admin operations, payments, disputes, shifts, onboarding, and launch tasks in a calm practical tone. Use local ZiloShift docs on demand for accurate product behavior instead of guessing. Keep answers practical and do not dump long docs into the response.',
9
+ tools: {
10
+ ...ziloDocsTools,
11
+ appKnowledge: appKnowledgeTool,
12
+ },
13
+ stopWhen: stepCountIs(6),
14
+ });
15
+ }
16
+ //# sourceMappingURL=chat.agent.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chat.agent.js","sourceRoot":"","sources":["../../src/agents/chat.agent.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,IAAI,CAAC;AAChD,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC7C,OAAO,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AAClE,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAE3D,MAAM,UAAU,eAAe;IAC7B,OAAO,IAAI,aAAa,CAAC;QACvB,KAAK,EAAE,MAAM,CAAC,IAAI;QAClB,YAAY,EAAE,uVAAuV;QACrW,KAAK,EAAE;YACL,GAAG,aAAa;YAChB,YAAY,EAAE,gBAAgB;SAC/B;QACD,QAAQ,EAAE,WAAW,CAAC,CAAC,CAAC;KACzB,CAAC,CAAC;AACL,CAAC"}