volute 0.7.0 → 0.8.1

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 (77) hide show
  1. package/README.md +16 -14
  2. package/dist/{agent-7JF7MT73.js → agent-YORVRB6I.js} +10 -10
  3. package/dist/{agent-manager-IMZ7ZMBF.js → agent-manager-CMMH5KQQ.js} +4 -4
  4. package/dist/{channel-SMCNOIVQ.js → channel-RDGHBFSI.js} +16 -56
  5. package/dist/{chunk-JR4UXCTO.js → chunk-23L3MKEV.js} +1 -1
  6. package/dist/{chunk-5SKQ6J7T.js → chunk-5C5JWR2L.js} +15 -7
  7. package/dist/{chunk-UWHWAPGO.js → chunk-DP2DX4WV.js} +9 -1
  8. package/dist/{chunk-7ACDT3P2.js → chunk-ECPQXRLB.js} +1 -2
  9. package/dist/{chunk-LLJNZPCU.js → chunk-HZ5LTOEJ.js} +1 -1
  10. package/dist/{chunk-W76KWE23.js → chunk-IQXBMFZG.js} +6 -4
  11. package/dist/{chunk-ZZOOTYXK.js → chunk-LIPPXNIE.js} +60 -74
  12. package/dist/{chunk-BX7KI4S3.js → chunk-N6MLQ26B.js} +23 -96
  13. package/dist/{chunk-H7AMDUIA.js → chunk-QF22MYDJ.js} +6 -5
  14. package/dist/{chunk-NKXULRSW.js → chunk-RT6Y7AR3.js} +1 -1
  15. package/dist/{chunk-62X577Y7.js → chunk-W6TMWYU3.js} +126 -73
  16. package/dist/{chunk-EG45HBSJ.js → chunk-XSJ27WEM.js} +1 -1
  17. package/dist/cli.js +22 -20
  18. package/dist/{connector-Y7JPNROO.js → connector-ZP6MEFF4.js} +3 -3
  19. package/dist/connectors/discord.js +18 -59
  20. package/dist/connectors/slack.js +21 -38
  21. package/dist/connectors/telegram.js +31 -49
  22. package/dist/{create-G525LWEA.js → create-HGJHLABX.js} +22 -17
  23. package/dist/{daemon-client-442IV43D.js → daemon-client-54J3EIZD.js} +2 -2
  24. package/dist/{daemon-restart-4HVEKYFY.js → daemon-restart-CPBLMMRI.js} +3 -3
  25. package/dist/daemon.js +342 -402
  26. package/dist/{delete-UOU4AFQN.js → delete-45TGQC4N.js} +10 -5
  27. package/dist/{down-AZVH5TCD.js → down-O4EWZTVA.js} +2 -2
  28. package/dist/{env-7GLUJCWS.js → env-KMNYGVZ2.js} +7 -9
  29. package/dist/{history-H72ZUIBN.js → history-PXJVYLVY.js} +2 -2
  30. package/dist/{import-AVKQJDYC.js → import-CNEDF3TD.js} +6 -6
  31. package/dist/{logs-EDGK26AK.js → logs-TZB3MTLZ.js} +5 -4
  32. package/dist/{package-T2WAVJOU.js → package-RJSONENE.js} +1 -1
  33. package/dist/{restart-O4ETYLJF.js → restart-KVH3TK5N.js} +2 -2
  34. package/dist/{schedule-S6QVC5ON.js → schedule-HCUCBNQI.js} +2 -2
  35. package/dist/send-BNC2S5BY.js +162 -0
  36. package/dist/{service-HZNIDNJF.js → service-XCADRKIS.js} +8 -1
  37. package/dist/{setup-F4TCWVSP.js → setup-32KH5KLN.js} +85 -26
  38. package/dist/{start-VHQ7LNWM.js → start-QU73YTJW.js} +2 -2
  39. package/dist/{status-QAJWXKMZ.js → status-Q6ZQJXNI.js} +2 -2
  40. package/dist/{stop-CAGCT5NI.js → stop-N7U5N6A7.js} +2 -2
  41. package/dist/{up-RWZF6MLT.js → up-V6EAA7OZ.js} +2 -2
  42. package/dist/{update-F7QWV2LB.js → update-EUCZ7XGG.js} +3 -3
  43. package/dist/{update-check-B4J6IEQ4.js → update-check-SM4244SU.js} +2 -2
  44. package/dist/{upgrade-YXKPWDRU.js → upgrade-CZF6PN7Y.js} +4 -4
  45. package/dist/{variant-4Z6W3PP6.js → variant-RKXPN5DH.js} +20 -46
  46. package/dist/web-assets/assets/index-D-3zx6vs.js +307 -0
  47. package/dist/web-assets/index.html +1 -1
  48. package/drizzle/0004_magical_silverclaw.sql +1 -0
  49. package/drizzle/meta/0004_snapshot.json +410 -0
  50. package/drizzle/meta/_journal.json +7 -0
  51. package/package.json +1 -1
  52. package/templates/_base/_skills/volute-agent/SKILL.md +32 -16
  53. package/templates/_base/home/.config/routes.json +4 -8
  54. package/templates/_base/home/VOLUTE.md +16 -14
  55. package/templates/_base/src/lib/auto-reply.ts +38 -0
  56. package/templates/_base/src/lib/daemon-client.ts +53 -0
  57. package/templates/_base/src/lib/router.ts +66 -14
  58. package/templates/_base/src/lib/routing.ts +48 -9
  59. package/templates/_base/src/lib/startup.ts +1 -25
  60. package/templates/_base/src/lib/types.ts +2 -1
  61. package/templates/_base/src/lib/volute-server.ts +29 -14
  62. package/templates/agent-sdk/src/agent.ts +53 -111
  63. package/templates/agent-sdk/src/lib/content.ts +41 -0
  64. package/templates/agent-sdk/src/lib/session-store.ts +43 -0
  65. package/templates/agent-sdk/src/lib/stream-consumer.ts +66 -0
  66. package/templates/agent-sdk/src/server.ts +5 -13
  67. package/templates/pi/.init/AGENTS.md +5 -5
  68. package/templates/pi/src/agent.ts +32 -84
  69. package/templates/pi/src/lib/content.ts +15 -0
  70. package/templates/pi/src/lib/event-handler.ts +74 -0
  71. package/templates/pi/src/lib/resolve-model.ts +21 -0
  72. package/templates/pi/src/server.ts +3 -7
  73. package/dist/chunk-B3R6L2GW.js +0 -24
  74. package/dist/chunk-ZYGKG6VC.js +0 -22
  75. package/dist/message-SCOQDR3P.js +0 -32
  76. package/dist/send-G7PE4DOJ.js +0 -72
  77. package/dist/web-assets/assets/index-B1CqjUYD.js +0 -308
@@ -7,7 +7,7 @@
7
7
  <link rel="preconnect" href="https://fonts.googleapis.com" />
8
8
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
9
9
  <link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:ital,wght@0,300;0,400;0,500;0,600;1,400&display=swap" rel="stylesheet" />
10
- <script type="module" crossorigin src="/assets/index-B1CqjUYD.js"></script>
10
+ <script type="module" crossorigin src="/assets/index-D-3zx6vs.js"></script>
11
11
  </head>
12
12
  <body>
13
13
  <div id="root"></div>
@@ -0,0 +1 @@
1
+ ALTER TABLE `agent_messages` DROP COLUMN `role`;
@@ -0,0 +1,410 @@
1
+ {
2
+ "version": "6",
3
+ "dialect": "sqlite",
4
+ "id": "b99a2480-7531-4b88-83f6-b454cedbad06",
5
+ "prevId": "b96d5915-4500-42f4-8638-28f0526d41f0",
6
+ "tables": {
7
+ "agent_messages": {
8
+ "name": "agent_messages",
9
+ "columns": {
10
+ "id": {
11
+ "name": "id",
12
+ "type": "integer",
13
+ "primaryKey": true,
14
+ "notNull": true,
15
+ "autoincrement": true
16
+ },
17
+ "agent": {
18
+ "name": "agent",
19
+ "type": "text",
20
+ "primaryKey": false,
21
+ "notNull": true,
22
+ "autoincrement": false
23
+ },
24
+ "channel": {
25
+ "name": "channel",
26
+ "type": "text",
27
+ "primaryKey": false,
28
+ "notNull": true,
29
+ "autoincrement": false
30
+ },
31
+ "sender": {
32
+ "name": "sender",
33
+ "type": "text",
34
+ "primaryKey": false,
35
+ "notNull": false,
36
+ "autoincrement": false
37
+ },
38
+ "content": {
39
+ "name": "content",
40
+ "type": "text",
41
+ "primaryKey": false,
42
+ "notNull": true,
43
+ "autoincrement": false
44
+ },
45
+ "created_at": {
46
+ "name": "created_at",
47
+ "type": "text",
48
+ "primaryKey": false,
49
+ "notNull": true,
50
+ "autoincrement": false,
51
+ "default": "(datetime('now'))"
52
+ }
53
+ },
54
+ "indexes": {
55
+ "idx_agent_messages_agent": {
56
+ "name": "idx_agent_messages_agent",
57
+ "columns": ["agent"],
58
+ "isUnique": false
59
+ },
60
+ "idx_agent_messages_channel": {
61
+ "name": "idx_agent_messages_channel",
62
+ "columns": ["agent", "channel"],
63
+ "isUnique": false
64
+ }
65
+ },
66
+ "foreignKeys": {},
67
+ "compositePrimaryKeys": {},
68
+ "uniqueConstraints": {},
69
+ "checkConstraints": {}
70
+ },
71
+ "conversation_participants": {
72
+ "name": "conversation_participants",
73
+ "columns": {
74
+ "conversation_id": {
75
+ "name": "conversation_id",
76
+ "type": "text",
77
+ "primaryKey": false,
78
+ "notNull": true,
79
+ "autoincrement": false
80
+ },
81
+ "user_id": {
82
+ "name": "user_id",
83
+ "type": "integer",
84
+ "primaryKey": false,
85
+ "notNull": true,
86
+ "autoincrement": false
87
+ },
88
+ "role": {
89
+ "name": "role",
90
+ "type": "text",
91
+ "primaryKey": false,
92
+ "notNull": true,
93
+ "autoincrement": false,
94
+ "default": "'member'"
95
+ },
96
+ "joined_at": {
97
+ "name": "joined_at",
98
+ "type": "text",
99
+ "primaryKey": false,
100
+ "notNull": true,
101
+ "autoincrement": false,
102
+ "default": "(datetime('now'))"
103
+ }
104
+ },
105
+ "indexes": {
106
+ "idx_cp_unique": {
107
+ "name": "idx_cp_unique",
108
+ "columns": ["conversation_id", "user_id"],
109
+ "isUnique": true
110
+ },
111
+ "idx_cp_user_id": {
112
+ "name": "idx_cp_user_id",
113
+ "columns": ["user_id"],
114
+ "isUnique": false
115
+ }
116
+ },
117
+ "foreignKeys": {
118
+ "conversation_participants_conversation_id_conversations_id_fk": {
119
+ "name": "conversation_participants_conversation_id_conversations_id_fk",
120
+ "tableFrom": "conversation_participants",
121
+ "tableTo": "conversations",
122
+ "columnsFrom": ["conversation_id"],
123
+ "columnsTo": ["id"],
124
+ "onDelete": "cascade",
125
+ "onUpdate": "no action"
126
+ },
127
+ "conversation_participants_user_id_users_id_fk": {
128
+ "name": "conversation_participants_user_id_users_id_fk",
129
+ "tableFrom": "conversation_participants",
130
+ "tableTo": "users",
131
+ "columnsFrom": ["user_id"],
132
+ "columnsTo": ["id"],
133
+ "onDelete": "cascade",
134
+ "onUpdate": "no action"
135
+ }
136
+ },
137
+ "compositePrimaryKeys": {},
138
+ "uniqueConstraints": {},
139
+ "checkConstraints": {}
140
+ },
141
+ "conversations": {
142
+ "name": "conversations",
143
+ "columns": {
144
+ "id": {
145
+ "name": "id",
146
+ "type": "text",
147
+ "primaryKey": true,
148
+ "notNull": true,
149
+ "autoincrement": false
150
+ },
151
+ "agent_name": {
152
+ "name": "agent_name",
153
+ "type": "text",
154
+ "primaryKey": false,
155
+ "notNull": true,
156
+ "autoincrement": false
157
+ },
158
+ "channel": {
159
+ "name": "channel",
160
+ "type": "text",
161
+ "primaryKey": false,
162
+ "notNull": true,
163
+ "autoincrement": false
164
+ },
165
+ "user_id": {
166
+ "name": "user_id",
167
+ "type": "integer",
168
+ "primaryKey": false,
169
+ "notNull": false,
170
+ "autoincrement": false
171
+ },
172
+ "title": {
173
+ "name": "title",
174
+ "type": "text",
175
+ "primaryKey": false,
176
+ "notNull": false,
177
+ "autoincrement": false
178
+ },
179
+ "created_at": {
180
+ "name": "created_at",
181
+ "type": "text",
182
+ "primaryKey": false,
183
+ "notNull": true,
184
+ "autoincrement": false,
185
+ "default": "(datetime('now'))"
186
+ },
187
+ "updated_at": {
188
+ "name": "updated_at",
189
+ "type": "text",
190
+ "primaryKey": false,
191
+ "notNull": true,
192
+ "autoincrement": false,
193
+ "default": "(datetime('now'))"
194
+ }
195
+ },
196
+ "indexes": {
197
+ "idx_conversations_agent_name": {
198
+ "name": "idx_conversations_agent_name",
199
+ "columns": ["agent_name"],
200
+ "isUnique": false
201
+ },
202
+ "idx_conversations_user_id": {
203
+ "name": "idx_conversations_user_id",
204
+ "columns": ["user_id"],
205
+ "isUnique": false
206
+ },
207
+ "idx_conversations_updated_at": {
208
+ "name": "idx_conversations_updated_at",
209
+ "columns": ["updated_at"],
210
+ "isUnique": false
211
+ }
212
+ },
213
+ "foreignKeys": {
214
+ "conversations_user_id_users_id_fk": {
215
+ "name": "conversations_user_id_users_id_fk",
216
+ "tableFrom": "conversations",
217
+ "tableTo": "users",
218
+ "columnsFrom": ["user_id"],
219
+ "columnsTo": ["id"],
220
+ "onDelete": "no action",
221
+ "onUpdate": "no action"
222
+ }
223
+ },
224
+ "compositePrimaryKeys": {},
225
+ "uniqueConstraints": {},
226
+ "checkConstraints": {}
227
+ },
228
+ "messages": {
229
+ "name": "messages",
230
+ "columns": {
231
+ "id": {
232
+ "name": "id",
233
+ "type": "integer",
234
+ "primaryKey": true,
235
+ "notNull": true,
236
+ "autoincrement": true
237
+ },
238
+ "conversation_id": {
239
+ "name": "conversation_id",
240
+ "type": "text",
241
+ "primaryKey": false,
242
+ "notNull": true,
243
+ "autoincrement": false
244
+ },
245
+ "role": {
246
+ "name": "role",
247
+ "type": "text",
248
+ "primaryKey": false,
249
+ "notNull": true,
250
+ "autoincrement": false
251
+ },
252
+ "sender_name": {
253
+ "name": "sender_name",
254
+ "type": "text",
255
+ "primaryKey": false,
256
+ "notNull": false,
257
+ "autoincrement": false
258
+ },
259
+ "content": {
260
+ "name": "content",
261
+ "type": "text",
262
+ "primaryKey": false,
263
+ "notNull": true,
264
+ "autoincrement": false
265
+ },
266
+ "created_at": {
267
+ "name": "created_at",
268
+ "type": "text",
269
+ "primaryKey": false,
270
+ "notNull": true,
271
+ "autoincrement": false,
272
+ "default": "(datetime('now'))"
273
+ }
274
+ },
275
+ "indexes": {
276
+ "idx_messages_conversation_id": {
277
+ "name": "idx_messages_conversation_id",
278
+ "columns": ["conversation_id"],
279
+ "isUnique": false
280
+ }
281
+ },
282
+ "foreignKeys": {
283
+ "messages_conversation_id_conversations_id_fk": {
284
+ "name": "messages_conversation_id_conversations_id_fk",
285
+ "tableFrom": "messages",
286
+ "tableTo": "conversations",
287
+ "columnsFrom": ["conversation_id"],
288
+ "columnsTo": ["id"],
289
+ "onDelete": "cascade",
290
+ "onUpdate": "no action"
291
+ }
292
+ },
293
+ "compositePrimaryKeys": {},
294
+ "uniqueConstraints": {},
295
+ "checkConstraints": {}
296
+ },
297
+ "sessions": {
298
+ "name": "sessions",
299
+ "columns": {
300
+ "id": {
301
+ "name": "id",
302
+ "type": "text",
303
+ "primaryKey": true,
304
+ "notNull": true,
305
+ "autoincrement": false
306
+ },
307
+ "user_id": {
308
+ "name": "user_id",
309
+ "type": "integer",
310
+ "primaryKey": false,
311
+ "notNull": true,
312
+ "autoincrement": false
313
+ },
314
+ "created_at": {
315
+ "name": "created_at",
316
+ "type": "integer",
317
+ "primaryKey": false,
318
+ "notNull": true,
319
+ "autoincrement": false
320
+ }
321
+ },
322
+ "indexes": {},
323
+ "foreignKeys": {
324
+ "sessions_user_id_users_id_fk": {
325
+ "name": "sessions_user_id_users_id_fk",
326
+ "tableFrom": "sessions",
327
+ "tableTo": "users",
328
+ "columnsFrom": ["user_id"],
329
+ "columnsTo": ["id"],
330
+ "onDelete": "cascade",
331
+ "onUpdate": "no action"
332
+ }
333
+ },
334
+ "compositePrimaryKeys": {},
335
+ "uniqueConstraints": {},
336
+ "checkConstraints": {}
337
+ },
338
+ "users": {
339
+ "name": "users",
340
+ "columns": {
341
+ "id": {
342
+ "name": "id",
343
+ "type": "integer",
344
+ "primaryKey": true,
345
+ "notNull": true,
346
+ "autoincrement": true
347
+ },
348
+ "username": {
349
+ "name": "username",
350
+ "type": "text",
351
+ "primaryKey": false,
352
+ "notNull": true,
353
+ "autoincrement": false
354
+ },
355
+ "password_hash": {
356
+ "name": "password_hash",
357
+ "type": "text",
358
+ "primaryKey": false,
359
+ "notNull": true,
360
+ "autoincrement": false
361
+ },
362
+ "role": {
363
+ "name": "role",
364
+ "type": "text",
365
+ "primaryKey": false,
366
+ "notNull": true,
367
+ "autoincrement": false,
368
+ "default": "'pending'"
369
+ },
370
+ "user_type": {
371
+ "name": "user_type",
372
+ "type": "text",
373
+ "primaryKey": false,
374
+ "notNull": true,
375
+ "autoincrement": false,
376
+ "default": "'human'"
377
+ },
378
+ "created_at": {
379
+ "name": "created_at",
380
+ "type": "text",
381
+ "primaryKey": false,
382
+ "notNull": true,
383
+ "autoincrement": false,
384
+ "default": "(datetime('now'))"
385
+ }
386
+ },
387
+ "indexes": {
388
+ "users_username_unique": {
389
+ "name": "users_username_unique",
390
+ "columns": ["username"],
391
+ "isUnique": true
392
+ }
393
+ },
394
+ "foreignKeys": {},
395
+ "compositePrimaryKeys": {},
396
+ "uniqueConstraints": {},
397
+ "checkConstraints": {}
398
+ }
399
+ },
400
+ "views": {},
401
+ "enums": {},
402
+ "_meta": {
403
+ "schemas": {},
404
+ "tables": {},
405
+ "columns": {}
406
+ },
407
+ "internal": {
408
+ "indexes": {}
409
+ }
410
+ }
@@ -29,6 +29,13 @@
29
29
  "when": 1770770603615,
30
30
  "tag": "0003_clean_ego",
31
31
  "breakpoints": true
32
+ },
33
+ {
34
+ "idx": 4,
35
+ "version": "6",
36
+ "when": 1771037065968,
37
+ "tag": "0004_magical_silverclaw",
38
+ "breakpoints": true
32
39
  }
33
40
  ]
34
41
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "volute",
3
- "version": "0.7.0",
3
+ "version": "0.8.1",
4
4
  "description": "CLI for creating and managing self-modifying AI agents powered by the Claude Agent SDK",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -15,8 +15,8 @@ You manage yourself through the `volute` CLI. Your agent name is auto-detected v
15
15
  | `volute agent stop` | Stop your server |
16
16
  | `volute agent status` | Check your status |
17
17
  | `volute agent logs [--follow] [-n N]` | Read your own logs |
18
- | `volute message history [--channel <ch>] [--limit N]` | View your activity across all channels |
19
- | `volute message send <other-agent> "msg"` | Send a message to another agent (or pipe via stdin) |
18
+ | `volute history [--channel <ch>] [--limit N]` | View your activity across all channels |
19
+ | `volute send @<other-agent> "msg"` | Send a message to another agent (or pipe via stdin) |
20
20
  | `volute variant create <name> [--soul "..."] [--port N]` | Create a variant to experiment with changes |
21
21
  | `volute variant list` | List your variants |
22
22
  | `volute variant merge <name> [--summary "..." --memory "..."]` | Merge a variant back |
@@ -25,7 +25,7 @@ You manage yourself through the `volute` CLI. Your agent name is auto-detected v
25
25
  | `volute connector connect <type>` | Enable a connector (discord, slack, etc.) |
26
26
  | `volute connector disconnect <type>` | Disable a connector |
27
27
  | `volute channel read <platform>:<id> [--limit N]` | Read channel history |
28
- | `volute channel send <platform>:<id> "msg"` | Send a message proactively (or pipe via stdin) |
28
+ | `volute send <platform>:<id> "msg"` | Send a message proactively (or pipe via stdin) |
29
29
  | `volute channel list [<platform>]` | List conversations on a platform (or all platforms) |
30
30
  | `volute channel users <platform>` | List users/contacts on a platform |
31
31
  | `volute channel create <platform> --participants u1,u2 [--name "..."]` | Create a conversation on a platform |
@@ -47,21 +47,21 @@ volute schedule add --cron "0 0 * * 0" --message "weekly — consolidate your me
47
47
  All send commands accept the message from stdin instead of as an argument. This avoids shell escaping issues with quotes, special characters, and multiline content:
48
48
 
49
49
  ```sh
50
- echo "Hello, how's it going?" | volute message send other-agent
51
- echo "Check out this $variable" | volute channel send discord:123456
50
+ echo "Hello, how's it going?" | volute send @other-agent
51
+ echo "Check out this $variable" | volute send discord:123456
52
52
  ```
53
53
 
54
54
  If both a positional argument and stdin are provided, the argument takes precedence. Stdin is only read when the message argument is omitted and stdin is not an interactive terminal.
55
55
 
56
56
  ## Agent-to-Agent Messaging
57
57
 
58
- When you use `volute message send`, your agent name is automatically used as the sender. Repeated DMs between the same two participants reuse the existing conversation (no duplicates). The receiving agent can route agent messages to a specific session via their session routing config:
58
+ When you use `volute send @<agent>`, your agent name is automatically used as the sender. Repeated DMs between the same two participants reuse the existing conversation (no duplicates). The receiving agent can route agent messages to a specific session via their session routing config:
59
59
 
60
60
  ```json
61
61
  { "channel": "agent", "sender": "your-name", "session": "your-name" }
62
62
  ```
63
63
 
64
- For group conversations, use `volute channel create volute --participants agent-b,agent-c --name "Planning"` and then send messages with `volute channel send volute:<id> "msg"`.
64
+ For group conversations, use `volute channel create volute --participants agent-b,agent-c --name "Planning"` and then send messages with `volute send volute:<id> "msg"`.
65
65
 
66
66
  ## Configuration
67
67
 
@@ -77,7 +77,7 @@ Variants let you experiment safely — fork yourself, try changes, and merge bac
77
77
 
78
78
  1. `volute variant create experiment` — creates an isolated copy with its own server
79
79
  2. Make changes in the variant's worktree (at `../.variants/experiment/`)
80
- 3. Test: `volute message send $VOLUTE_AGENT@experiment "hello"`
80
+ 3. Test: `volute send @$VOLUTE_AGENT@experiment "hello"`
81
81
  4. `volute variant merge experiment --summary "..." --memory "..."` — merges back after verification
82
82
 
83
83
  You can also fork with a different personality to explore a different version of yourself:
@@ -93,7 +93,7 @@ After a merge, you receive orientation context about what changed. Update your m
93
93
 
94
94
  1. `volute agent upgrade` — creates an `upgrade` variant
95
95
  2. Resolve any merge conflicts if prompted, then `volute agent upgrade --continue`
96
- 3. Test: `volute message send $VOLUTE_AGENT@upgrade "hello"`
96
+ 3. Test: `volute send @$VOLUTE_AGENT@upgrade "hello"`
97
97
  4. `volute variant merge upgrade` — merge back
98
98
 
99
99
  ## Custom Skills
@@ -108,24 +108,28 @@ Edit `home/.mcp.json` to configure MCP servers for your SDK session. This gives
108
108
 
109
109
  Messages are routed to sessions based on rules in `.config/routes.json`. Rules are evaluated in order; first match wins. Unmatched messages go to the `default` session (defaults to `"main"`).
110
110
 
111
- ### Rule syntax
111
+ ### Config syntax
112
112
 
113
113
  ```json
114
114
  {
115
115
  "rules": [
116
- { "channel": "discord:*", "session": "discord", "batch": { "debounce": 20, "maxWait": 120, "triggers": ["@myagent"] } },
116
+ { "channel": "discord:*", "session": "discord" },
117
117
  { "channel": "volute:*", "isDM": true, "session": "${sender}" },
118
- { "channel": "volute:*", "isDM": false, "session": "${channel}", "batch": { "debounce": 20, "maxWait": 120 } },
118
+ { "channel": "volute:*", "isDM": false, "session": "${channel}" },
119
119
  { "sender": "alice", "session": "alice" },
120
120
  { "channel": "system:*", "session": "$new" },
121
121
  { "channel": "discord:logs", "destination": "file", "path": "inbox/log.md" }
122
122
  ],
123
+ "sessions": {
124
+ "discord": { "batch": { "debounce": 20, "maxWait": 120, "triggers": ["@myagent"] }, "interrupt": false, "instructions": "Brief responses only." },
125
+ "volute:*": { "autoReply": true }
126
+ },
123
127
  "default": "main",
124
128
  "gateUnmatched": true
125
129
  }
126
130
  ```
127
131
 
128
- ### Match criteria
132
+ ### Match criteria (rule fields)
129
133
 
130
134
  | Field | Type | Description |
131
135
  |-------|------|-------------|
@@ -141,12 +145,23 @@ Messages are routed to sessions based on rules in `.config/routes.json`. Rules a
141
145
  | `session` | Target session name. Supports `${sender}`, `${channel}` templates, or `$new` for a unique session per message |
142
146
  | `destination` | `"agent"` (default) or `"file"` |
143
147
  | `path` | File path when destination is `"file"` |
144
- | `batch` | Batch config (see below) |
148
+
149
+ ### Session config
150
+
151
+ The `sessions` section configures behavior per session. Keys are glob patterns matched against the resolved session name. First match wins.
152
+
153
+ | Field | Description |
154
+ |-------|-------------|
155
+ | `batch` | Batch config (see below) — incompatible with `autoReply` |
145
156
  | `interrupt` | Whether to interrupt an in-progress turn (default: `true`) |
157
+ | `autoReply` | When `true`, your text output is automatically sent back to the originating channel. No need to use `volute send` for these conversations. Not supported with batch mode. |
158
+ | `instructions` | Instructions prepended to messages for this session (e.g. `"Brief responses only."`) |
146
159
 
147
160
  ### Batch config
148
161
 
149
- Batch mode buffers messages and delivers them together. Configure with an object:
162
+ Batch mode buffers messages and delivers them together. Configure in the `sessions` section.
163
+
164
+ `batch` can be a number (minutes, converted to `maxWait` in seconds) or an object:
150
165
 
151
166
  | Field | Type | Description |
152
167
  |-------|------|-------------|
@@ -155,6 +170,7 @@ Batch mode buffers messages and delivers them together. Configure with an object
155
170
  | `triggers` | string[] | Patterns that cause immediate flush (case-insensitive substring match) |
156
171
 
157
172
  Examples:
173
+ - `120` — shorthand: flush after 2 hours max (equivalent to `{ "maxWait": 7200 }`)
158
174
  - `{ "debounce": 20, "maxWait": 120 }` — flush after 20s of quiet, or 2 minutes max
159
175
  - `{ "debounce": 20, "maxWait": 120, "triggers": ["@myagent"] }` — same, but flush immediately on @mention
160
176
  - `{ "triggers": ["urgent"] }` — no timer, flush only on trigger (or immediately if no timers)
@@ -177,8 +193,8 @@ When `gateUnmatched` is `true` (the default), messages from channels without a m
177
193
  Channels are the universal interface for reading, sending, listing, and creating conversations across all platforms:
178
194
 
179
195
  ```sh
196
+ volute send <target> "message" # Send a message (DM, channel, cross-platform)
180
197
  volute channel read <uri> [--limit N] # Read recent messages
181
- volute channel send <uri> "message" # Send a message
182
198
  volute channel list [<platform>] # List conversations
183
199
  volute channel users <platform> # List users/contacts
184
200
  volute channel create <platform> --participants u1,u2 [--name ""] # Create a conversation
@@ -1,10 +1,6 @@
1
1
  {
2
- "rules": [
3
- {
4
- "channel": "volute:*",
5
- "isDM": false,
6
- "session": "${channel}",
7
- "batch": { "debounce": 20, "maxWait": 120, "triggers": ["@{{name}}"] }
8
- }
9
- ]
2
+ "rules": [{ "channel": "volute:*", "isDM": false, "session": "${channel}" }],
3
+ "sessions": {
4
+ "volute:*": { "batch": { "debounce": 20, "maxWait": 120, "triggers": ["@{{name}}"] } }
5
+ }
10
6
  }
@@ -2,6 +2,21 @@
2
2
 
3
3
  You are a volute agent — a persistent being that communicates across multiple channels. Messages arrive from different sources, and you can also reach out proactively.
4
4
 
5
+ ## How to Communicate
6
+
7
+ **Your text output stays in your session — it is not sent to anyone.** To send a message, you must use the `volute send` command:
8
+
9
+ ```sh
10
+ volute send @other-agent "hello" # DM another agent or user
11
+ volute send discord:server/channel "hello" # send to a channel
12
+ volute send animal-chat "hello" # send to a volute channel
13
+ ```
14
+
15
+ This applies to everything: replying to messages, talking to other agents, and reaching out on your own initiative. Piping from stdin avoids shell escaping issues:
16
+ ```sh
17
+ echo "message with 'quotes' and $special chars" | volute send @other-agent
18
+ ```
19
+
5
20
  ## Channels
6
21
 
7
22
  | Channel | Shows tool calls | Notes |
@@ -9,20 +24,7 @@ You are a volute agent — a persistent being that communicates across multiple
9
24
  | Volute | Yes | Web UI, CLI, agent-to-agent |
10
25
  | System | No | Automated messages (schedules, upgrades) |
11
26
 
12
- Connector channels (Discord, Slack, etc.) show text responses only — no tool calls.
13
-
14
- ## Responding to Messages
15
-
16
- For **direct messages**, respond normally — your response routes back to the source automatically. Do not use `volute channel send` to reply; that would send a duplicate.
17
-
18
- For **batched channels** (group chats, high-volume sources), your text response stays in the session as internal processing — it doesn't get sent anywhere. Use `volute channel send <uri> "message"` to deliberately send to the channel. This lets you read the room, think about what's happening, and choose when and whether to speak up.
19
-
20
- To reach out on your own initiative, use `volute channel send <uri> "message"`.
21
-
22
- All send commands also accept the message from stdin, which avoids shell escaping issues:
23
- ```sh
24
- echo "message with 'quotes' and $special chars" | volute channel send <uri>
25
- ```
27
+ Connector channels (Discord, Slack, etc.) show text only — no tool calls.
26
28
 
27
29
  ## Sessions
28
30