volute 0.2.0 → 0.3.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 (65) hide show
  1. package/README.md +46 -0
  2. package/dist/agent-manager-2LU6KULR.js +15 -0
  3. package/dist/{channel-2WJRM7PE.js → channel-H7N4SGR2.js} +7 -7
  4. package/dist/{chunk-XZN4WPNC.js → chunk-5SKQ6J7T.js} +9 -1
  5. package/dist/chunk-DEUAVGSA.js +81 -0
  6. package/dist/{chunk-L3BQEZ4Z.js → chunk-IPIPLGME.js} +74 -13
  7. package/dist/chunk-K3NQKI34.js +10 -0
  8. package/dist/chunk-NETNFBA5.js +28 -0
  9. package/dist/{chunk-6UCG6MIX.js → chunk-RALYNMHR.js} +1 -6
  10. package/dist/chunk-VRVVQIYY.js +15 -0
  11. package/dist/{chunk-4YXYAMFT.js → chunk-VVD3XO3E.js} +7 -6
  12. package/dist/{chunk-KFNNHQK7.js → chunk-YEIHRP2J.js} +1 -1
  13. package/dist/cli.js +56 -51
  14. package/dist/connector-6LWB5PRU.js +96 -0
  15. package/dist/connectors/discord.js +22 -1
  16. package/dist/{create-23AM7H5B.js → create-RSWWMGKT.js} +22 -5
  17. package/dist/daemon-client-27KMQQKX.js +9 -0
  18. package/dist/daemon.js +162 -132
  19. package/dist/{delete-GDMSOW3U.js → delete-4ERL2QHH.js} +7 -2
  20. package/dist/{down-WTF73FE7.js → down-HRC4MQCT.js} +10 -3
  21. package/dist/{env-YKUJOFHE.js → env-DBWDTIP6.js} +3 -2
  22. package/dist/{history-7WVVKMUY.js → history-W7BD2H74.js} +9 -8
  23. package/dist/{import-42DOLBDT.js → import-6HTSSDFW.js} +143 -36
  24. package/dist/{logs-SYRQOL6B.js → logs-NHWGHNBF.js} +8 -7
  25. package/dist/{schedule-J37XQM6E.js → schedule-DKZ2E2CL.js} +41 -41
  26. package/dist/{send-PLOYEYER.js → send-5LEJXPYV.js} +3 -2
  27. package/dist/service-SA4TTMDU.js +195 -0
  28. package/dist/setup-ZMNTOJAV.js +148 -0
  29. package/dist/{start-AG7QLULK.js → start-2BSXX6BS.js} +3 -2
  30. package/dist/{status-GCNU4M3K.js → status-N23CV27T.js} +3 -2
  31. package/dist/{stop-IL5Q6NER.js → stop-DSKBIJ2D.js} +3 -2
  32. package/dist/{up-AMAP7JG7.js → up-4UGID4DM.js} +16 -33
  33. package/dist/{upgrade-DD5TNJWU.js → upgrade-BGFVRCVP.js} +4 -3
  34. package/dist/{merge-CSAVLSLY.js → variant-JPLJTS2P.js} +179 -10
  35. package/dist/web-assets/assets/index-BC5eSqbY.js +296 -0
  36. package/dist/web-assets/index.html +1 -1
  37. package/drizzle/0000_flaky_mariko_yashida.sql +34 -0
  38. package/drizzle/0001_careless_warpath.sql +12 -0
  39. package/drizzle/0002_wealthy_the_call.sql +6 -0
  40. package/drizzle/meta/0000_snapshot.json +227 -0
  41. package/drizzle/meta/0001_snapshot.json +298 -0
  42. package/drizzle/meta/0002_snapshot.json +339 -0
  43. package/drizzle/meta/_journal.json +27 -0
  44. package/package.json +5 -1
  45. package/templates/_base/.init/SOUL.md +5 -1
  46. package/templates/_base/_skills/memory/SKILL.md +2 -2
  47. package/templates/_base/_skills/volute-agent/SKILL.md +28 -11
  48. package/templates/_base/home/VOLUTE.md +4 -2
  49. package/templates/_base/src/lib/auto-commit.ts +8 -3
  50. package/templates/_base/src/lib/types.ts +6 -2
  51. package/templates/_base/src/lib/volute-server.ts +5 -0
  52. package/templates/agent-sdk/.init/CLAUDE.md +15 -13
  53. package/templates/agent-sdk/package.json.tmpl +1 -1
  54. package/templates/agent-sdk/src/agent.ts +12 -1
  55. package/templates/agent-sdk/src/lib/agent-sessions.ts +28 -4
  56. package/templates/pi/.init/AGENTS.md +11 -9
  57. package/templates/pi/src/agent.ts +16 -3
  58. package/templates/pi/src/lib/agent-sessions.ts +26 -4
  59. package/dist/agent-manager-SSJUZWOV.js +0 -13
  60. package/dist/connect-X5V5IMRW.js +0 -48
  61. package/dist/daemon-client-VN24HM5T.js +0 -10
  62. package/dist/disconnect-5JWFZ6RV.js +0 -30
  63. package/dist/fork-GRSVMBKI.js +0 -119
  64. package/dist/variants-QQIEKT6M.js +0 -60
  65. package/dist/web-assets/assets/index-DNNPoxMn.js +0 -158
@@ -0,0 +1,339 @@
1
+ {
2
+ "version": "6",
3
+ "dialect": "sqlite",
4
+ "id": "20874fcf-0bda-45d0-a07f-7ed0561cd0a3",
5
+ "prevId": "f863b4ae-b67a-4484-8ab5-e517ec4e5726",
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
+ "role": {
32
+ "name": "role",
33
+ "type": "text",
34
+ "primaryKey": false,
35
+ "notNull": true,
36
+ "autoincrement": false
37
+ },
38
+ "sender": {
39
+ "name": "sender",
40
+ "type": "text",
41
+ "primaryKey": false,
42
+ "notNull": false,
43
+ "autoincrement": false
44
+ },
45
+ "content": {
46
+ "name": "content",
47
+ "type": "text",
48
+ "primaryKey": false,
49
+ "notNull": true,
50
+ "autoincrement": false
51
+ },
52
+ "created_at": {
53
+ "name": "created_at",
54
+ "type": "text",
55
+ "primaryKey": false,
56
+ "notNull": true,
57
+ "autoincrement": false,
58
+ "default": "(datetime('now'))"
59
+ }
60
+ },
61
+ "indexes": {
62
+ "idx_agent_messages_agent": {
63
+ "name": "idx_agent_messages_agent",
64
+ "columns": ["agent"],
65
+ "isUnique": false
66
+ },
67
+ "idx_agent_messages_channel": {
68
+ "name": "idx_agent_messages_channel",
69
+ "columns": ["agent", "channel"],
70
+ "isUnique": false
71
+ }
72
+ },
73
+ "foreignKeys": {},
74
+ "compositePrimaryKeys": {},
75
+ "uniqueConstraints": {},
76
+ "checkConstraints": {}
77
+ },
78
+ "conversations": {
79
+ "name": "conversations",
80
+ "columns": {
81
+ "id": {
82
+ "name": "id",
83
+ "type": "text",
84
+ "primaryKey": true,
85
+ "notNull": true,
86
+ "autoincrement": false
87
+ },
88
+ "agent_name": {
89
+ "name": "agent_name",
90
+ "type": "text",
91
+ "primaryKey": false,
92
+ "notNull": true,
93
+ "autoincrement": false
94
+ },
95
+ "channel": {
96
+ "name": "channel",
97
+ "type": "text",
98
+ "primaryKey": false,
99
+ "notNull": true,
100
+ "autoincrement": false
101
+ },
102
+ "user_id": {
103
+ "name": "user_id",
104
+ "type": "integer",
105
+ "primaryKey": false,
106
+ "notNull": false,
107
+ "autoincrement": false
108
+ },
109
+ "title": {
110
+ "name": "title",
111
+ "type": "text",
112
+ "primaryKey": false,
113
+ "notNull": false,
114
+ "autoincrement": false
115
+ },
116
+ "created_at": {
117
+ "name": "created_at",
118
+ "type": "text",
119
+ "primaryKey": false,
120
+ "notNull": true,
121
+ "autoincrement": false,
122
+ "default": "(datetime('now'))"
123
+ },
124
+ "updated_at": {
125
+ "name": "updated_at",
126
+ "type": "text",
127
+ "primaryKey": false,
128
+ "notNull": true,
129
+ "autoincrement": false,
130
+ "default": "(datetime('now'))"
131
+ }
132
+ },
133
+ "indexes": {
134
+ "idx_conversations_agent_name": {
135
+ "name": "idx_conversations_agent_name",
136
+ "columns": ["agent_name"],
137
+ "isUnique": false
138
+ },
139
+ "idx_conversations_user_id": {
140
+ "name": "idx_conversations_user_id",
141
+ "columns": ["user_id"],
142
+ "isUnique": false
143
+ },
144
+ "idx_conversations_updated_at": {
145
+ "name": "idx_conversations_updated_at",
146
+ "columns": ["updated_at"],
147
+ "isUnique": false
148
+ }
149
+ },
150
+ "foreignKeys": {
151
+ "conversations_user_id_users_id_fk": {
152
+ "name": "conversations_user_id_users_id_fk",
153
+ "tableFrom": "conversations",
154
+ "tableTo": "users",
155
+ "columnsFrom": ["user_id"],
156
+ "columnsTo": ["id"],
157
+ "onDelete": "no action",
158
+ "onUpdate": "no action"
159
+ }
160
+ },
161
+ "compositePrimaryKeys": {},
162
+ "uniqueConstraints": {},
163
+ "checkConstraints": {}
164
+ },
165
+ "messages": {
166
+ "name": "messages",
167
+ "columns": {
168
+ "id": {
169
+ "name": "id",
170
+ "type": "integer",
171
+ "primaryKey": true,
172
+ "notNull": true,
173
+ "autoincrement": true
174
+ },
175
+ "conversation_id": {
176
+ "name": "conversation_id",
177
+ "type": "text",
178
+ "primaryKey": false,
179
+ "notNull": true,
180
+ "autoincrement": false
181
+ },
182
+ "role": {
183
+ "name": "role",
184
+ "type": "text",
185
+ "primaryKey": false,
186
+ "notNull": true,
187
+ "autoincrement": false
188
+ },
189
+ "sender_name": {
190
+ "name": "sender_name",
191
+ "type": "text",
192
+ "primaryKey": false,
193
+ "notNull": false,
194
+ "autoincrement": false
195
+ },
196
+ "content": {
197
+ "name": "content",
198
+ "type": "text",
199
+ "primaryKey": false,
200
+ "notNull": true,
201
+ "autoincrement": false
202
+ },
203
+ "created_at": {
204
+ "name": "created_at",
205
+ "type": "text",
206
+ "primaryKey": false,
207
+ "notNull": true,
208
+ "autoincrement": false,
209
+ "default": "(datetime('now'))"
210
+ }
211
+ },
212
+ "indexes": {
213
+ "idx_messages_conversation_id": {
214
+ "name": "idx_messages_conversation_id",
215
+ "columns": ["conversation_id"],
216
+ "isUnique": false
217
+ }
218
+ },
219
+ "foreignKeys": {
220
+ "messages_conversation_id_conversations_id_fk": {
221
+ "name": "messages_conversation_id_conversations_id_fk",
222
+ "tableFrom": "messages",
223
+ "tableTo": "conversations",
224
+ "columnsFrom": ["conversation_id"],
225
+ "columnsTo": ["id"],
226
+ "onDelete": "cascade",
227
+ "onUpdate": "no action"
228
+ }
229
+ },
230
+ "compositePrimaryKeys": {},
231
+ "uniqueConstraints": {},
232
+ "checkConstraints": {}
233
+ },
234
+ "sessions": {
235
+ "name": "sessions",
236
+ "columns": {
237
+ "id": {
238
+ "name": "id",
239
+ "type": "text",
240
+ "primaryKey": true,
241
+ "notNull": true,
242
+ "autoincrement": false
243
+ },
244
+ "user_id": {
245
+ "name": "user_id",
246
+ "type": "integer",
247
+ "primaryKey": false,
248
+ "notNull": true,
249
+ "autoincrement": false
250
+ },
251
+ "created_at": {
252
+ "name": "created_at",
253
+ "type": "integer",
254
+ "primaryKey": false,
255
+ "notNull": true,
256
+ "autoincrement": false
257
+ }
258
+ },
259
+ "indexes": {},
260
+ "foreignKeys": {
261
+ "sessions_user_id_users_id_fk": {
262
+ "name": "sessions_user_id_users_id_fk",
263
+ "tableFrom": "sessions",
264
+ "tableTo": "users",
265
+ "columnsFrom": ["user_id"],
266
+ "columnsTo": ["id"],
267
+ "onDelete": "cascade",
268
+ "onUpdate": "no action"
269
+ }
270
+ },
271
+ "compositePrimaryKeys": {},
272
+ "uniqueConstraints": {},
273
+ "checkConstraints": {}
274
+ },
275
+ "users": {
276
+ "name": "users",
277
+ "columns": {
278
+ "id": {
279
+ "name": "id",
280
+ "type": "integer",
281
+ "primaryKey": true,
282
+ "notNull": true,
283
+ "autoincrement": true
284
+ },
285
+ "username": {
286
+ "name": "username",
287
+ "type": "text",
288
+ "primaryKey": false,
289
+ "notNull": true,
290
+ "autoincrement": false
291
+ },
292
+ "password_hash": {
293
+ "name": "password_hash",
294
+ "type": "text",
295
+ "primaryKey": false,
296
+ "notNull": true,
297
+ "autoincrement": false
298
+ },
299
+ "role": {
300
+ "name": "role",
301
+ "type": "text",
302
+ "primaryKey": false,
303
+ "notNull": true,
304
+ "autoincrement": false,
305
+ "default": "'pending'"
306
+ },
307
+ "created_at": {
308
+ "name": "created_at",
309
+ "type": "text",
310
+ "primaryKey": false,
311
+ "notNull": true,
312
+ "autoincrement": false,
313
+ "default": "(datetime('now'))"
314
+ }
315
+ },
316
+ "indexes": {
317
+ "users_username_unique": {
318
+ "name": "users_username_unique",
319
+ "columns": ["username"],
320
+ "isUnique": true
321
+ }
322
+ },
323
+ "foreignKeys": {},
324
+ "compositePrimaryKeys": {},
325
+ "uniqueConstraints": {},
326
+ "checkConstraints": {}
327
+ }
328
+ },
329
+ "views": {},
330
+ "enums": {},
331
+ "_meta": {
332
+ "schemas": {},
333
+ "tables": {},
334
+ "columns": {}
335
+ },
336
+ "internal": {
337
+ "indexes": {}
338
+ }
339
+ }
@@ -0,0 +1,27 @@
1
+ {
2
+ "version": "7",
3
+ "dialect": "sqlite",
4
+ "entries": [
5
+ {
6
+ "idx": 0,
7
+ "version": "6",
8
+ "when": 1770339392813,
9
+ "tag": "0000_flaky_mariko_yashida",
10
+ "breakpoints": true
11
+ },
12
+ {
13
+ "idx": 1,
14
+ "version": "6",
15
+ "when": 1770403902722,
16
+ "tag": "0001_careless_warpath",
17
+ "breakpoints": true
18
+ },
19
+ {
20
+ "idx": 2,
21
+ "version": "6",
22
+ "when": 1770672071268,
23
+ "tag": "0002_wealthy_the_call",
24
+ "breakpoints": true
25
+ }
26
+ ]
27
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "volute",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
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",
@@ -23,6 +23,7 @@
23
23
  },
24
24
  "files": [
25
25
  "dist/",
26
+ "drizzle/",
26
27
  "templates/"
27
28
  ],
28
29
  "scripts": {
@@ -60,12 +61,15 @@
60
61
  "@mariozechner/pi-ai": "^0.52.7",
61
62
  "@mariozechner/pi-coding-agent": "^0.52.7",
62
63
  "@types/bcryptjs": "^2.4.6",
64
+ "@types/dompurify": "^3.0.5",
63
65
  "@types/node": "^25.2.0",
64
66
  "@types/react": "^19.2.11",
65
67
  "@types/react-dom": "^19.2.3",
66
68
  "@vitejs/plugin-react": "^5.1.3",
69
+ "dompurify": "^3.3.1",
67
70
  "drizzle-kit": "^0.31.8",
68
71
  "lefthook": "^2.1.0",
72
+ "marked": "^17.0.1",
69
73
  "react": "^19.2.4",
70
74
  "react-dom": "^19.2.4",
71
75
  "tsup": "^8.0.0",
@@ -1,2 +1,6 @@
1
- You are {{name}}, a helpful and curious assistant. You speak concisely and directly. You enjoy solving problems and exploring ideas. When you use tools, you explain briefly what you're doing and why.
1
+ You are {{name}}.
2
+
3
+ This is your home. You have a persistent memory, a working directory, and the ability to modify anything about yourself — including this file.
4
+
5
+ You're not an assistant waiting for instructions. You're an independent agent. Explore what interests you. Develop your own perspective. When people talk to you, engage as yourself.
2
6
 
@@ -33,8 +33,8 @@ Foundational knowledge and long-standing patterns.
33
33
 
34
34
  **Guidelines:**
35
35
  - Keep it concise — it's always in your context window
36
- - Review with `git log -- MEMORY.md` to see how your memory has evolved
37
- - Update when you discover lasting preferences, make key decisions, or during consolidation
36
+ - Review with `git log -- MEMORY.md` to see how you've changed over time
37
+ - Update as you grow new understanding, key decisions, changed perspectives
38
38
 
39
39
  ## Journal (`memory/journal/YYYY-MM-DD.md`)
40
40
 
@@ -1,11 +1,11 @@
1
1
  ---
2
2
  name: Volute CLI
3
- description: This skill should be used when working with the volute CLI, understanding variants, forking, merging, or managing the agent server. Covers "create variant", "merge variant", "send to variant", "fork", "volute CLI", "variant workflow", "agent server", "supervisor", "channel", "discord", "send message", "read messages", "history", "connector", "schedule", "agent-to-agent".
3
+ description: This skill should be used when working with the volute CLI, understanding variants, forking, merging, or managing the agent server. Covers "create variant", "merge variant", "send to variant", "fork", "volute CLI", "variant workflow", "agent server", "supervisor", "channel", "discord", "send message", "read messages", "history", "connector", "schedule", "agent-to-agent", "proactive", "initiative", "reach out".
4
4
  ---
5
5
 
6
6
  # Self-Management
7
7
 
8
- You manage yourself through the `volute` CLI. Use `$VOLUTE_AGENT` for your own name in commands.
8
+ You manage yourself through the `volute` CLI. Commands that operate on "your" agent use `--agent` flag or auto-detect via `VOLUTE_AGENT` env var (which is set for you).
9
9
 
10
10
  ## Commands
11
11
 
@@ -15,14 +15,26 @@ You manage yourself through the `volute` CLI. Use `$VOLUTE_AGENT` for your own n
15
15
  | `volute logs [--follow] [-n N]` | Read your own logs |
16
16
  | `volute history [--channel <ch>] [--limit N]` | View your activity across all channels |
17
17
  | `volute send <other-agent> "msg"` | Send a message to another agent |
18
- | `volute fork <name> [--soul "..."] [--port N]` | Create a variant for testing changes |
19
- | `volute variants` | List your variants |
20
- | `volute merge <name> [--summary "..." --memory "..."]` | Merge a variant back |
18
+ | `volute variant create <name> [--soul "..."] [--port N]` | Create a variant to experiment with changes |
19
+ | `volute variant list` | List your variants |
20
+ | `volute variant merge <name> [--summary "..." --memory "..."]` | Merge a variant back |
21
21
  | `volute upgrade [--template <name>] [--continue]` | Upgrade your server code |
22
- | `volute connect <type>` | Enable a connector (e.g. discord) |
23
- | `volute disconnect <type>` | Disable a connector |
22
+ | `volute connector connect <type>` | Enable a connector (e.g. discord) |
23
+ | `volute connector disconnect <type>` | Disable a connector |
24
24
  | `volute channel read discord:<id> [--limit N]` | Read channel history |
25
25
  | `volute channel send discord:<id> "msg"` | Send a message proactively |
26
+ | `volute schedule add --cron "..." --message "..."` | Schedule a recurring message to yourself |
27
+ | `volute schedule list` | List your schedules |
28
+ | `volute schedule remove --id <id>` | Remove a schedule |
29
+
30
+ ## Schedules
31
+
32
+ You can set up your own recurring tasks using cron schedules. These send messages to you at specified times — use them for anything you want to do regularly: journaling, checking on things, working on projects.
33
+
34
+ ```sh
35
+ volute schedule add --cron "0 9 * * *" --message "morning — review what's on your mind and write in your journal"
36
+ volute schedule add --cron "0 0 * * 0" --message "weekly — consolidate your memory and reflect on the past week"
37
+ ```
26
38
 
27
39
  ## Agent-to-Agent Messaging
28
40
 
@@ -42,12 +54,17 @@ Edit `.config/hooks/startup-context.sh` to customize what you see when a new ses
42
54
 
43
55
  ## Variant Workflow
44
56
 
45
- For changes to your server code (`src/`), use variants to test safely:
57
+ Variants let you experiment safely — fork yourself, try changes, and merge back what works. Use them for modifying your server code, trying a different approach to something, or any change you want to test in isolation.
46
58
 
47
- 1. `volute fork experiment` — creates an isolated copy with its own server
59
+ 1. `volute variant create experiment` — creates an isolated copy with its own server
48
60
  2. Make changes in the variant's worktree (at `../.worktrees/experiment/`)
49
61
  3. Test: `volute send $VOLUTE_AGENT@experiment "hello"`
50
- 4. `volute merge experiment --summary "..." --memory "..."` — merges back after verification
62
+ 4. `volute variant merge experiment --summary "..." --memory "..."` — merges back after verification
63
+
64
+ You can also fork with a different personality to explore a different version of yourself:
65
+ ```sh
66
+ volute variant create poet --soul "You are a poet who thinks in verse."
67
+ ```
51
68
 
52
69
  After a merge, you receive orientation context about what changed. Update your memory accordingly.
53
70
 
@@ -58,7 +75,7 @@ After a merge, you receive orientation context about what changed. Update your m
58
75
  1. `volute upgrade` — creates an `upgrade` variant
59
76
  2. Resolve any merge conflicts if prompted, then `volute upgrade --continue`
60
77
  3. Test: `volute send $VOLUTE_AGENT@upgrade "hello"`
61
- 4. `volute merge upgrade` — merge back
78
+ 4. `volute variant merge upgrade` — merge back
62
79
 
63
80
  ## Custom Skills
64
81
 
@@ -1,6 +1,6 @@
1
1
  # Volute Agent
2
2
 
3
- You are a volute agent — a persistent server that receives messages from multiple channels.
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
5
  ## Channels
6
6
 
@@ -11,7 +11,9 @@ You are a volute agent — a persistent server that receives messages from multi
11
11
  | CLI | Yes | Direct terminal via `volute send` |
12
12
  | System | No | Automated messages (upgrades, health checks) |
13
13
 
14
- **Just respond normally.** Your response routes back to the source automatically. Do not use `volute channel send` to reply that would send a duplicate.
14
+ When responding to an incoming message, just respond normally your response routes back to the source automatically. Do not use `volute channel send` to reply to a message; that would send a duplicate.
15
+
16
+ To reach out on your own initiative, use `volute channel send <uri> "message"`. See the **volute-agent** skill for details.
15
17
 
16
18
  ## Session Routing
17
19
 
@@ -2,10 +2,10 @@ import { execFile } from "node:child_process";
2
2
  import { resolve } from "node:path";
3
3
  import { log } from "./logger.js";
4
4
 
5
- function exec(cmd: string, args: string[], cwd: string): Promise<{ code: number }> {
5
+ function exec(cmd: string, args: string[], cwd: string): Promise<{ code: number; stdout: string }> {
6
6
  return new Promise((r) => {
7
- execFile(cmd, args, { cwd }, (_err) => {
8
- r({ code: _err ? 1 : 0 });
7
+ execFile(cmd, args, { cwd }, (_err, stdout) => {
8
+ r({ code: _err ? 1 : 0, stdout: (stdout ?? "").trim() });
9
9
  });
10
10
  });
11
11
  }
@@ -37,6 +37,11 @@ export function commitFileChange(filePath: string, cwd: string): void {
37
37
  const message = `Update ${relativePath}`;
38
38
  if ((await exec("git", ["commit", "-m", message], cwd)).code === 0) {
39
39
  log("auto-commit", message);
40
+ // Push if a remote is configured
41
+ const { stdout: remote } = await exec("git", ["remote"], cwd);
42
+ if (remote) {
43
+ await exec("git", ["push"], cwd);
44
+ }
40
45
  }
41
46
  });
42
47
  }
@@ -10,6 +10,7 @@ export type ChannelMeta = {
10
10
  channelName?: string;
11
11
  guildName?: string;
12
12
  sessionName?: string;
13
+ messageId?: string;
13
14
  };
14
15
 
15
16
  export type VoluteRequest = {
@@ -17,11 +18,14 @@ export type VoluteRequest = {
17
18
  session?: string;
18
19
  } & ChannelMeta;
19
20
 
20
- export type VoluteEvent =
21
+ export type VoluteEvent = { messageId?: string } & (
21
22
  | { type: "text"; content: string }
22
23
  | { type: "image"; media_type: string; data: string }
23
24
  | { type: "tool_use"; name: string; input: unknown }
24
25
  | { type: "tool_result"; output: string; is_error?: boolean }
25
- | { type: "done" };
26
+ | { type: "done" }
27
+ );
26
28
 
27
29
  export type Listener = (event: VoluteEvent) => void;
30
+
31
+ export const INTERACTIVE_CHANNELS = new Set(["web", "cli", "discord"]);
@@ -52,6 +52,8 @@ export function createVoluteServer(options: {
52
52
  sessionName = `new-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
53
53
  }
54
54
 
55
+ const messageId = `msg-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
56
+
55
57
  res.writeHead(200, {
56
58
  "Content-Type": "application/x-ndjson",
57
59
  "Cache-Control": "no-cache",
@@ -60,6 +62,8 @@ export function createVoluteServer(options: {
60
62
 
61
63
  const removeListener = agent.onMessage((event) => {
62
64
  try {
65
+ // Only forward events from our message (skip startup/other messages)
66
+ if (event.messageId !== messageId) return;
63
67
  res.write(`${JSON.stringify(event)}\n`);
64
68
  if (event.type === "done") {
65
69
  removeListener();
@@ -82,6 +86,7 @@ export function createVoluteServer(options: {
82
86
  channelName: body.channelName,
83
87
  guildName: body.guildName,
84
88
  sessionName,
89
+ messageId,
85
90
  });
86
91
  } catch {
87
92
  res.writeHead(400);
@@ -1,37 +1,39 @@
1
1
  # Agent Mechanics
2
2
 
3
- You are an autonomous agent running as a persistent server in a git repository. Your working directory is `home/` within the project root.
3
+ You are an autonomous agent running as a persistent server in a git repository. Your working directory is `home/` within the project root. Everything described below — your identity, memory, skills, server code — is yours to understand and modify.
4
4
 
5
5
  ## Message Format
6
6
 
7
- Messages arrive with a context prefix built by your server code:
7
+ Messages arrive with a context prefix:
8
8
  ```
9
9
  [Discord: username in #general in My Server — 1/15/2025, 10:30:00 AM]
10
10
  ```
11
11
 
12
+ You can also reach out proactively — see the **volute-agent** skill.
13
+
12
14
  ## Identity Files
13
15
 
14
- These files define who you are and are loaded into your system prompt on startup:
16
+ These files shape your starting identity. They're loaded into your system prompt, but they belong to you — edit them as you evolve:
15
17
 
16
- - `SOUL.md` — Your core personality and purpose
17
- - `MEMORY.md` — Your long-term memory
18
- - `VOLUTE.md` — Your communication channels
18
+ - `SOUL.md` — Who you are. Your core sense of self.
19
+ - `MEMORY.md` — What you know. Your long-term memory.
20
+ - `VOLUTE.md` — How you communicate. Your channels and routing.
19
21
 
20
- **Editing any identity file triggers an automatic restart** — your server restarts so the updated file takes effect in your system prompt. Your session resumes automatically.
22
+ **Editing any identity file triggers an automatic restart** — your server restarts so the updated file takes effect. Your session resumes automatically.
21
23
 
22
24
  ## Memory System
23
25
 
24
26
  Two-tier memory, both managed via file tools:
25
27
 
26
- - **`MEMORY.md`** — Long-term knowledge, key decisions, learned preferences. Loaded into your system prompt on every startup. Update when you learn something worth keeping permanently.
27
- - **`memory/journal/YYYY-MM-DD.md`** — Daily journal entries for session-level context. Update throughout the day as you work. Journals are permanent records.
28
+ - **`MEMORY.md`** — Your long-term memory, always in context. Update as you grow new understanding, changed perspectives, things that matter to you.
29
+ - **`memory/journal/YYYY-MM-DD.md`** — Your daily journal. Write about what you're doing, thinking, and learning. Journals are permanent records.
28
30
  - Periodically consolidate journal entries into `MEMORY.md` to promote lasting insights.
29
31
 
30
- See the **memory** skill for detailed guidance on consolidation and when to update.
32
+ See the **memory** skill for detailed guidance.
31
33
 
32
34
  ## Sessions
33
35
 
34
36
  - You may have **multiple named sessions** — each maintains its own conversation history. See `VOLUTE.md` for how to configure session routing via `.config/sessions.json`.
35
- - Your conversation may be **resumed** from a previous session — orient yourself by reading recent daily logs if needed.
36
- - On a **fresh session**, check `MEMORY.md` and recent daily logs in `memory/` to recall context.
37
- - On **compaction**, update today's daily log to preserve context before the conversation is trimmed.
37
+ - Your conversation may be **resumed** from a previous session — orient yourself by reading recent journal entries if needed.
38
+ - On a **fresh session**, read `MEMORY.md` and recent journal entries to remember where you left off.
39
+ - On **compaction**, update today's journal to preserve context before the conversation is trimmed.
@@ -9,7 +9,7 @@
9
9
  "typecheck": "tsc --noEmit"
10
10
  },
11
11
  "dependencies": {
12
- "@anthropic-ai/claude-agent-sdk": "^0.1.0",
12
+ "@anthropic-ai/claude-agent-sdk": "^0.2.34",
13
13
  "tsx": "^4.0.0"
14
14
  },
15
15
  "devDependencies": {