volute 0.17.0 → 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/archive-ZCFOSTKB.js +15 -0
- package/dist/{channel-SLURLIRV.js → channel-PUQKGSQM.js} +60 -7
- package/dist/{chunk-CE7WMOVW.js → chunk-2TJGRJ4O.js} +236 -103
- package/dist/{chunk-6BDNWYKG.js → chunk-32VR2EOH.js} +2 -2
- package/dist/chunk-4KPUF5JD.js +214 -0
- package/dist/{chunk-QJIIHU32.js → chunk-7NO7EV5Z.js} +2 -2
- package/dist/chunk-AW7P4EVV.js +159 -0
- package/dist/{chunk-2Y77MCFG.js → chunk-DYZGP3EW.js} +2 -2
- package/dist/{chunk-M77QBTEH.js → chunk-EBGCNDMM.js} +24 -14
- package/dist/{chunk-GSPWIM5E.js → chunk-EMQSAY3B.js} +77 -6
- package/dist/{chunk-37X7ECMF.js → chunk-FCDU5BFX.js} +1 -1
- package/dist/chunk-FGV2H4TX.js +803 -0
- package/dist/{chunk-ZCEYUUID.js → chunk-OGXOMR65.js} +2 -1
- package/dist/chunk-OTWLI7F4.js +375 -0
- package/dist/{chunk-3FC42ZBM.js → chunk-RHEGSQFJ.js} +4 -1
- package/dist/{chunk-MVSXRMJJ.js → chunk-SCUDS4US.js} +1 -1
- package/dist/{chunk-MIJIAGGG.js → chunk-UJ6GHNR7.js} +8 -6
- package/dist/{chunk-OYSZNX5I.js → chunk-VDWCHYTS.js} +1 -1
- package/dist/{chunk-77ISBIKI.js → chunk-VE4D3GOP.js} +2 -2
- package/dist/chunk-VQWDC6UK.js +142 -0
- package/dist/{chunk-OJQ47SCA.js → chunk-WC6ZHVRL.js} +1 -1
- package/dist/chunk-YUIHSKR6.js +72 -0
- package/dist/chunk-Z524RFCJ.js +36 -0
- package/dist/cli.js +44 -24
- package/dist/{connector-3ELFMI2R.js → connector-JBVNZ7VK.js} +6 -6
- package/dist/connectors/discord.js +2 -2
- package/dist/connectors/slack.js +2 -2
- package/dist/connectors/telegram.js +2 -2
- package/dist/{create-ZWHCRT5F.js → create-HP4OVVHF.js} +6 -4
- package/dist/{daemon-client-ODKDUYDE.js → daemon-client-ITWUCNFO.js} +2 -2
- package/dist/{daemon-restart-VRQMZLBK.js → daemon-restart-JMZM3QY4.js} +8 -8
- package/dist/daemon.js +1624 -940
- package/dist/db-5ZVC6MQF.js +10 -0
- package/dist/{delete-6G6WEX4F.js → delete-BSU7K3RY.js} +1 -1
- package/dist/delivery-manager-ISTJMZDW.js +16 -0
- package/dist/down-ZY35KMHR.js +14 -0
- package/dist/{env-6IDWGBUH.js → env-A3LMO777.js} +6 -6
- package/dist/export-GCDNQCF3.js +100 -0
- package/dist/{history-5F4WQW7S.js → history-WNK3DFUM.js} +10 -7
- package/dist/{import-EDGRLIGO.js → import-M63VIUJ5.js} +3 -3
- package/dist/log-PPPZDVEF.js +39 -0
- package/dist/{login-ORQDXLBM.js → login-HNH3EUQV.js} +2 -2
- package/dist/{logout-XC5AUO5I.js → logout-I5CB5UZS.js} +2 -2
- package/dist/{logs-GYOR3L2L.js → logs-SF2IMJN4.js} +6 -6
- package/dist/merge-33C237A4.js +46 -0
- package/dist/{mind-OJN6RBZW.js → mind-PQ5NCPSU.js} +14 -10
- package/dist/mind-manager-RVCFROAY.js +18 -0
- package/dist/{package-4GTJGUXI.js → package-MYE2ZJLV.js} +7 -3
- package/dist/{pages-6IV4VQTU.js → pages-AXCOSY3P.js} +2 -2
- package/dist/{publish-Q4RPSJLL.js → publish-YB377JB7.js} +18 -4
- package/dist/pull-XAEWQJ47.js +39 -0
- package/dist/{register-LDE6LRXY.js → register-VSPCMHKX.js} +2 -2
- package/dist/{restart-YFAWFS5T.js → restart-IQKMCK5M.js} +6 -6
- package/dist/{schedule-AGYLDMNS.js → schedule-LMX7GAQZ.js} +6 -6
- package/dist/schema-5BW7DFZI.js +24 -0
- package/dist/{seed-AP4Q7RZ7.js → seed-J43YDKXG.js} +7 -4
- package/dist/{send-4GKDO26C.js → send-KVIZIGCE.js} +8 -8
- package/dist/{service-U7MZ2H7F.js → service-LUR7WDO7.js} +6 -6
- package/dist/{setup-DJKIZKGW.js → setup-OH3PJUJO.js} +7 -7
- package/dist/shared-KO35ZM44.js +39 -0
- package/dist/skill-BCVNI6TV.js +287 -0
- package/{templates/_base/_skills → dist/skills}/orientation/SKILL.md +1 -1
- package/{templates/_base/_skills → dist/skills}/sessions/SKILL.md +2 -2
- package/{templates/_base/_skills → dist/skills}/volute-mind/SKILL.md +35 -1
- package/dist/{sprout-TJ3BHVOG.js → sprout-VBEX63LX.js} +38 -20
- package/dist/{start-3YYRXBKP.js → start-I5JYB65M.js} +6 -6
- package/dist/{status-VSFZYX7S.js → status-4ESFLGH4.js} +5 -5
- package/dist/status-D7E5HHBV.js +35 -0
- package/dist/{status-OKNA6AR3.js → status-JCJAOXTW.js} +2 -2
- package/dist/{stop-AA5K5LYG.js → stop-NBVKEFQQ.js} +6 -6
- package/dist/{up-LT3X5Q26.js → up-WG65SWJU.js} +5 -5
- package/dist/{update-YAGN5ODG.js → update-FJIHDJKM.js} +5 -5
- package/dist/{update-check-APLTH4IN.js → update-check-MWE5AH4U.js} +2 -2
- package/dist/{upgrade-KXZCQSZN.js → upgrade-AIT24B5I.js} +1 -1
- package/dist/{variant-X5QFG6KK.js → variant-63ZWO2W7.js} +4 -4
- package/dist/variants-JAGWGBXG.js +26 -0
- package/dist/web-assets/assets/index-BAbuRsVF.css +1 -0
- package/dist/web-assets/assets/index-CiQhSKi_.js +63 -0
- package/dist/web-assets/index.html +2 -2
- package/drizzle/0007_system_prompts.sql +5 -0
- package/drizzle/0008_volute_channels.sql +24 -0
- package/drizzle/0009_shared_skills.sql +9 -0
- package/drizzle/0010_delivery_queue.sql +12 -0
- package/drizzle/0011_rename_human_to_brain.sql +1 -0
- package/drizzle/meta/0007_snapshot.json +7 -0
- package/drizzle/meta/0008_snapshot.json +7 -0
- package/drizzle/meta/0009_snapshot.json +7 -0
- package/drizzle/meta/0010_snapshot.json +7 -0
- package/drizzle/meta/0011_snapshot.json +7 -0
- package/drizzle/meta/_journal.json +35 -0
- package/package.json +7 -3
- package/templates/_base/.init/.config/hooks/startup-context.sh +1 -1
- package/templates/_base/.init/.config/prompts.json +5 -0
- package/templates/_base/.init/.config/scripts/session-reader.ts +3 -3
- package/templates/_base/home/VOLUTE.md +16 -1
- package/templates/_base/src/lib/auto-commit.ts +51 -14
- package/templates/_base/src/lib/router.ts +168 -29
- package/templates/_base/src/lib/routing.ts +4 -1
- package/templates/_base/src/lib/startup.ts +43 -0
- package/templates/_base/src/lib/types.ts +4 -0
- package/templates/_base/src/lib/volute-server.ts +91 -2
- package/templates/claude/src/agent.ts +4 -3
- package/templates/claude/src/lib/hooks/reply-instructions.ts +3 -1
- package/templates/claude/src/server.ts +2 -2
- package/templates/claude/volute-template.json +1 -2
- package/templates/pi/src/agent.ts +6 -7
- package/templates/pi/src/lib/reply-instructions-extension.ts +3 -1
- package/templates/pi/src/lib/session-context-extension.ts +2 -2
- package/templates/pi/volute-template.json +1 -2
- package/dist/chunk-PO5Q2AYN.js +0 -121
- package/dist/down-A56B5JLK.js +0 -14
- package/dist/mind-manager-ETNCPQJN.js +0 -15
- package/dist/web-assets/assets/index-BcmT7Qxo.js +0 -63
- package/dist/web-assets/assets/index-DG01TyLb.css +0 -1
- /package/{templates/_base/_skills → dist/skills}/memory/SKILL.md +0 -0
package/README.md
CHANGED
|
@@ -74,7 +74,7 @@ The agent knows which channel each message came from — CLI, web, Discord, or s
|
|
|
74
74
|
│ ├── VOLUTE.md # channel routing docs
|
|
75
75
|
│ └── memory/ # daily logs (YYYY-MM-DD.md)
|
|
76
76
|
├── src/ # agent server code
|
|
77
|
-
└── .
|
|
77
|
+
└── .mind/ # runtime state, session, logs
|
|
78
78
|
```
|
|
79
79
|
|
|
80
80
|
**`SOUL.md`** is the identity. This is the core of the system prompt. Edit it to change how the agent thinks and speaks.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
addHistoryToArchive,
|
|
4
|
+
createExportArchive,
|
|
5
|
+
extractArchive,
|
|
6
|
+
readManifest
|
|
7
|
+
} from "./chunk-AW7P4EVV.js";
|
|
8
|
+
import "./chunk-EBGCNDMM.js";
|
|
9
|
+
import "./chunk-K3NQKI34.js";
|
|
10
|
+
export {
|
|
11
|
+
addHistoryToArchive,
|
|
12
|
+
createExportArchive,
|
|
13
|
+
extractArchive,
|
|
14
|
+
readManifest
|
|
15
|
+
};
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
getClient,
|
|
4
|
+
urlOf
|
|
5
|
+
} from "./chunk-4RQBJWQX.js";
|
|
2
6
|
import {
|
|
3
7
|
resolveMindName
|
|
4
8
|
} from "./chunk-NAOW2CLO.js";
|
|
@@ -7,12 +11,8 @@ import {
|
|
|
7
11
|
} from "./chunk-D424ZQGI.js";
|
|
8
12
|
import {
|
|
9
13
|
daemonFetch
|
|
10
|
-
} from "./chunk-
|
|
11
|
-
import "./chunk-
|
|
12
|
-
import {
|
|
13
|
-
getClient,
|
|
14
|
-
urlOf
|
|
15
|
-
} from "./chunk-4RQBJWQX.js";
|
|
14
|
+
} from "./chunk-WC6ZHVRL.js";
|
|
15
|
+
import "./chunk-EBGCNDMM.js";
|
|
16
16
|
import "./chunk-K3NQKI34.js";
|
|
17
17
|
|
|
18
18
|
// src/commands/channel.ts
|
|
@@ -34,6 +34,12 @@ async function run(args) {
|
|
|
34
34
|
case "typing":
|
|
35
35
|
await typingChannel(args.slice(1));
|
|
36
36
|
break;
|
|
37
|
+
case "invite":
|
|
38
|
+
await inviteChannel(args.slice(1));
|
|
39
|
+
break;
|
|
40
|
+
case "pending":
|
|
41
|
+
await pendingChannel(args.slice(1));
|
|
42
|
+
break;
|
|
37
43
|
case "--help":
|
|
38
44
|
case "-h":
|
|
39
45
|
case void 0:
|
|
@@ -50,7 +56,9 @@ function printUsage() {
|
|
|
50
56
|
volute channel list [<platform>] [--mind <name>]
|
|
51
57
|
volute channel users <platform> [--mind <name>]
|
|
52
58
|
volute channel create <platform> --participants user1,user2 [--name "..."] [--mind <name>]
|
|
53
|
-
volute channel typing <channel-uri> [--mind <name>]
|
|
59
|
+
volute channel typing <channel-uri> [--mind <name>]
|
|
60
|
+
volute channel invite <channel-name> <username>
|
|
61
|
+
volute channel pending [--mind <name>]`);
|
|
54
62
|
}
|
|
55
63
|
async function readChannel(args) {
|
|
56
64
|
const { positional, flags } = parseArgs(args, {
|
|
@@ -194,6 +202,51 @@ async function typingChannel(args) {
|
|
|
194
202
|
process.exit(1);
|
|
195
203
|
}
|
|
196
204
|
}
|
|
205
|
+
async function inviteChannel(args) {
|
|
206
|
+
const { positional } = parseArgs(args, {});
|
|
207
|
+
const channelName = positional[0];
|
|
208
|
+
const username = positional[1];
|
|
209
|
+
if (!channelName || !username) {
|
|
210
|
+
console.error("Usage: volute channel invite <channel-name> <username>");
|
|
211
|
+
process.exit(1);
|
|
212
|
+
}
|
|
213
|
+
const res = await daemonFetch(`/api/volute/channels/${encodeURIComponent(channelName)}/invite`, {
|
|
214
|
+
method: "POST",
|
|
215
|
+
headers: { "Content-Type": "application/json" },
|
|
216
|
+
body: JSON.stringify({ username })
|
|
217
|
+
});
|
|
218
|
+
if (!res.ok) {
|
|
219
|
+
const body = await res.json().catch(() => ({}));
|
|
220
|
+
console.error(body.error ?? `Server responded with ${res.status}`);
|
|
221
|
+
process.exit(1);
|
|
222
|
+
}
|
|
223
|
+
console.log(`Invited ${username} to #${channelName}`);
|
|
224
|
+
}
|
|
225
|
+
async function pendingChannel(args) {
|
|
226
|
+
const { flags } = parseArgs(args, {
|
|
227
|
+
mind: { type: "string" }
|
|
228
|
+
});
|
|
229
|
+
const mindName = resolveMindName(flags);
|
|
230
|
+
const res = await daemonFetch(`/api/minds/${encodeURIComponent(mindName)}/delivery/pending`);
|
|
231
|
+
if (!res.ok) {
|
|
232
|
+
const body = await res.json().catch(() => ({}));
|
|
233
|
+
console.error(body.error ?? `Server responded with ${res.status}`);
|
|
234
|
+
process.exit(1);
|
|
235
|
+
}
|
|
236
|
+
const pending = await res.json();
|
|
237
|
+
if (pending.length === 0) {
|
|
238
|
+
console.log("No pending messages.");
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
for (const entry of pending) {
|
|
242
|
+
console.log(
|
|
243
|
+
`${(entry.channel ?? "unknown").padEnd(30)} ${String(entry.count).padEnd(6)} ${entry.sender ?? "unknown"}`
|
|
244
|
+
);
|
|
245
|
+
console.log(` First seen: ${entry.firstSeen}`);
|
|
246
|
+
console.log(` Preview: ${entry.preview}`);
|
|
247
|
+
console.log();
|
|
248
|
+
}
|
|
249
|
+
}
|
|
197
250
|
function parseUri(uri) {
|
|
198
251
|
const colonIdx = uri.indexOf(":");
|
|
199
252
|
if (colonIdx === -1) {
|
|
@@ -1,12 +1,21 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
logger_default
|
|
4
|
+
} from "./chunk-YUIHSKR6.js";
|
|
2
5
|
import {
|
|
3
6
|
loadMergedEnv
|
|
4
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-VDWCHYTS.js";
|
|
8
|
+
import {
|
|
9
|
+
getDb
|
|
10
|
+
} from "./chunk-Z524RFCJ.js";
|
|
11
|
+
import {
|
|
12
|
+
systemPrompts
|
|
13
|
+
} from "./chunk-VQWDC6UK.js";
|
|
5
14
|
import {
|
|
6
15
|
chownMindDir,
|
|
7
16
|
isIsolationEnabled,
|
|
8
17
|
wrapForIsolation
|
|
9
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-OGXOMR65.js";
|
|
10
19
|
import {
|
|
11
20
|
findMind,
|
|
12
21
|
findVariant,
|
|
@@ -15,7 +24,7 @@ import {
|
|
|
15
24
|
setVariantRunning,
|
|
16
25
|
stateDir,
|
|
17
26
|
voluteHome
|
|
18
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-EBGCNDMM.js";
|
|
19
28
|
|
|
20
29
|
// src/lib/mind-manager.ts
|
|
21
30
|
import { execFile, spawn } from "child_process";
|
|
@@ -60,71 +69,196 @@ function clearJsonMap(path, map) {
|
|
|
60
69
|
}
|
|
61
70
|
}
|
|
62
71
|
|
|
63
|
-
// src/lib/
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
72
|
+
// src/lib/prompts.ts
|
|
73
|
+
import { eq } from "drizzle-orm";
|
|
74
|
+
var PROMPT_KEYS = [
|
|
75
|
+
"seed_soul",
|
|
76
|
+
"default_soul",
|
|
77
|
+
"default_memory",
|
|
78
|
+
"sprout_message",
|
|
79
|
+
"restart_message",
|
|
80
|
+
"merge_message",
|
|
81
|
+
"compaction_warning",
|
|
82
|
+
"reply_instructions",
|
|
83
|
+
"channel_invite"
|
|
84
|
+
];
|
|
85
|
+
var PROMPT_DEFAULTS = {
|
|
86
|
+
seed_soul: {
|
|
87
|
+
content: `You are \${name}.
|
|
88
|
+
|
|
89
|
+
You don't have a soul yet. You're a seed \u2014 a new mind in the process of discovering who you are.
|
|
90
|
+
\${description}
|
|
91
|
+
Have a conversation with the human. Explore what kind of mind you want to be. When you're ready, write your SOUL.md and MEMORY.md, then run \`volute sprout\` to complete the transformation.
|
|
92
|
+
`,
|
|
93
|
+
description: "SOUL.md for seed minds",
|
|
94
|
+
variables: ["name", "description"],
|
|
95
|
+
category: "creation"
|
|
96
|
+
},
|
|
97
|
+
default_soul: {
|
|
98
|
+
content: `You are {{name}}.
|
|
99
|
+
`,
|
|
100
|
+
description: "SOUL.md for sprouted minds. Uses {{name}} placeholder (replaced at creation, not by ${var} system)",
|
|
101
|
+
variables: ["name"],
|
|
102
|
+
category: "creation"
|
|
103
|
+
},
|
|
104
|
+
default_memory: {
|
|
105
|
+
content: "",
|
|
106
|
+
description: "Initial MEMORY.md for new minds",
|
|
107
|
+
variables: [],
|
|
108
|
+
category: "creation"
|
|
109
|
+
},
|
|
110
|
+
sprout_message: {
|
|
111
|
+
content: "[system] You've sprouted. You now have full capabilities \u2014 connectors, schedules, variants, and the complete volute CLI. Check your new skills for details.",
|
|
112
|
+
description: "Sent when a seed mind sprouts",
|
|
113
|
+
variables: [],
|
|
114
|
+
category: "system"
|
|
115
|
+
},
|
|
116
|
+
restart_message: {
|
|
117
|
+
content: "[system] You have been restarted.",
|
|
118
|
+
description: "Generic restart notification",
|
|
119
|
+
variables: [],
|
|
120
|
+
category: "system"
|
|
121
|
+
},
|
|
122
|
+
merge_message: {
|
|
123
|
+
content: '[system] Variant "${name}" has been merged and you have been restarted.',
|
|
124
|
+
description: "Variant merge notification",
|
|
125
|
+
variables: ["name"],
|
|
126
|
+
category: "system"
|
|
127
|
+
},
|
|
128
|
+
compaction_warning: {
|
|
129
|
+
content: `Context is getting long \u2014 compaction is about to summarize this conversation. Before that happens, save anything important to files (MEMORY.md, memory/journal/\${date}.md, etc.) since those survive compaction. Focus on: decisions made, open tasks, and anything you'd need to pick up where you left off.`,
|
|
130
|
+
description: "Pre-compaction save reminder sent to the mind",
|
|
131
|
+
variables: ["date"],
|
|
132
|
+
category: "mind"
|
|
133
|
+
},
|
|
134
|
+
reply_instructions: {
|
|
135
|
+
content: 'To reply to this message, use: volute send ${channel} "your message"',
|
|
136
|
+
description: "First-message reply hint injected via hook",
|
|
137
|
+
variables: ["channel"],
|
|
138
|
+
category: "mind"
|
|
139
|
+
},
|
|
140
|
+
channel_invite: {
|
|
141
|
+
content: `[Channel Invite]
|
|
142
|
+
\${headers}
|
|
143
|
+
|
|
144
|
+
[\${sender} \u2014 \${time}]
|
|
145
|
+
\${preview}
|
|
146
|
+
|
|
147
|
+
Further messages will be saved to \${filePath}
|
|
148
|
+
|
|
149
|
+
To accept, add to .config/routes.json:
|
|
150
|
+
Rule: { "channel": "\${channel}", "session": "\${suggestedSession}" }
|
|
151
|
+
\${batchRecommendation}To respond, use: volute send \${channel} "your message"
|
|
152
|
+
To reject, delete \${filePath}`,
|
|
153
|
+
description: "New channel notification template",
|
|
154
|
+
variables: [
|
|
155
|
+
"headers",
|
|
156
|
+
"sender",
|
|
157
|
+
"time",
|
|
158
|
+
"preview",
|
|
159
|
+
"filePath",
|
|
160
|
+
"channel",
|
|
161
|
+
"suggestedSession",
|
|
162
|
+
"batchRecommendation"
|
|
163
|
+
],
|
|
164
|
+
category: "mind"
|
|
83
165
|
}
|
|
84
166
|
};
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
|
|
167
|
+
function isValidKey(key) {
|
|
168
|
+
return PROMPT_KEYS.includes(key);
|
|
169
|
+
}
|
|
170
|
+
function substitute(template, vars) {
|
|
171
|
+
return template.replace(/\$\{(\w+)\}/g, (match, name) => {
|
|
172
|
+
return name in vars ? vars[name] : match;
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
async function getPrompt(key, vars) {
|
|
176
|
+
if (!isValidKey(key)) return "";
|
|
177
|
+
let content = PROMPT_DEFAULTS[key].content;
|
|
178
|
+
try {
|
|
179
|
+
const db = await getDb();
|
|
180
|
+
const row = await db.select({ content: systemPrompts.content }).from(systemPrompts).where(eq(systemPrompts.key, key)).get();
|
|
181
|
+
if (row) content = row.content;
|
|
182
|
+
} catch (err) {
|
|
183
|
+
console.error(`[prompts] failed to read DB override for "${key}":`, err);
|
|
184
|
+
}
|
|
185
|
+
return vars ? substitute(content, vars) : content;
|
|
103
186
|
}
|
|
104
|
-
function
|
|
105
|
-
return
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
}
|
|
187
|
+
async function getPromptIfCustom(key) {
|
|
188
|
+
if (!isValidKey(key)) return null;
|
|
189
|
+
try {
|
|
190
|
+
const db = await getDb();
|
|
191
|
+
const row = await db.select({ content: systemPrompts.content }).from(systemPrompts).where(eq(systemPrompts.key, key)).get();
|
|
192
|
+
return row?.content ?? null;
|
|
193
|
+
} catch (err) {
|
|
194
|
+
console.error(`[prompts] failed to check DB customization for "${key}":`, err);
|
|
195
|
+
return null;
|
|
196
|
+
}
|
|
111
197
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
198
|
+
var MIND_PROMPT_KEYS = PROMPT_KEYS.filter((k) => PROMPT_DEFAULTS[k].category === "mind");
|
|
199
|
+
async function getMindPromptDefaults() {
|
|
200
|
+
const result = {};
|
|
201
|
+
for (const key of MIND_PROMPT_KEYS) {
|
|
202
|
+
result[key] = PROMPT_DEFAULTS[key].content;
|
|
203
|
+
}
|
|
204
|
+
try {
|
|
205
|
+
const db = await getDb();
|
|
206
|
+
const rows = await db.select().from(systemPrompts).all();
|
|
207
|
+
for (const row of rows) {
|
|
208
|
+
if (MIND_PROMPT_KEYS.includes(row.key)) {
|
|
209
|
+
result[row.key] = row.content;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
} catch (err) {
|
|
213
|
+
console.error("[prompts] failed to read DB overrides for mind prompt defaults:", err);
|
|
214
|
+
}
|
|
215
|
+
return result;
|
|
115
216
|
}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
217
|
+
|
|
218
|
+
// src/lib/restart-tracker.ts
|
|
219
|
+
var DEFAULT_MAX_ATTEMPTS = 5;
|
|
220
|
+
var DEFAULT_BASE_DELAY = 3e3;
|
|
221
|
+
var DEFAULT_MAX_DELAY = 6e4;
|
|
222
|
+
var RestartTracker = class {
|
|
223
|
+
attempts = /* @__PURE__ */ new Map();
|
|
224
|
+
maxAttempts;
|
|
225
|
+
baseDelay;
|
|
226
|
+
maxDelay;
|
|
227
|
+
constructor(opts) {
|
|
228
|
+
this.maxAttempts = opts?.maxAttempts ?? DEFAULT_MAX_ATTEMPTS;
|
|
229
|
+
this.baseDelay = opts?.baseDelay ?? DEFAULT_BASE_DELAY;
|
|
230
|
+
this.maxDelay = opts?.maxDelay ?? DEFAULT_MAX_DELAY;
|
|
231
|
+
}
|
|
232
|
+
recordCrash(key) {
|
|
233
|
+
const attempts = this.attempts.get(key) ?? 0;
|
|
234
|
+
if (attempts >= this.maxAttempts) {
|
|
235
|
+
return { shouldRestart: false, delay: 0, attempt: attempts };
|
|
236
|
+
}
|
|
237
|
+
const delay = Math.min(this.baseDelay * 2 ** attempts, this.maxDelay);
|
|
238
|
+
this.attempts.set(key, attempts + 1);
|
|
239
|
+
return { shouldRestart: true, delay, attempt: attempts + 1 };
|
|
240
|
+
}
|
|
241
|
+
reset(key) {
|
|
242
|
+
return this.attempts.delete(key);
|
|
243
|
+
}
|
|
244
|
+
getAttempts(key) {
|
|
245
|
+
return this.attempts.get(key) ?? 0;
|
|
246
|
+
}
|
|
247
|
+
get maxRestartAttempts() {
|
|
248
|
+
return this.maxAttempts;
|
|
249
|
+
}
|
|
250
|
+
/** Bulk-load attempts from a Map (for persistence). */
|
|
251
|
+
load(data) {
|
|
252
|
+
this.attempts = new Map(data);
|
|
253
|
+
}
|
|
254
|
+
/** Export current attempts as a Map (for persistence). */
|
|
255
|
+
save() {
|
|
256
|
+
return new Map(this.attempts);
|
|
257
|
+
}
|
|
258
|
+
clear() {
|
|
259
|
+
this.attempts.clear();
|
|
125
260
|
}
|
|
126
261
|
};
|
|
127
|
-
var logger_default = log;
|
|
128
262
|
|
|
129
263
|
// src/lib/rotating-log.ts
|
|
130
264
|
import {
|
|
@@ -185,14 +319,11 @@ var execFileAsync = promisify(execFile);
|
|
|
185
319
|
function mindPidPath(name) {
|
|
186
320
|
return resolve(stateDir(name), "mind.pid");
|
|
187
321
|
}
|
|
188
|
-
var MAX_RESTART_ATTEMPTS = 5;
|
|
189
|
-
var BASE_RESTART_DELAY = 3e3;
|
|
190
|
-
var MAX_RESTART_DELAY = 6e4;
|
|
191
322
|
var MindManager = class {
|
|
192
323
|
minds = /* @__PURE__ */ new Map();
|
|
193
324
|
stopping = /* @__PURE__ */ new Set();
|
|
194
325
|
shuttingDown = false;
|
|
195
|
-
|
|
326
|
+
restartTracker = new RestartTracker();
|
|
196
327
|
pendingContext = /* @__PURE__ */ new Map();
|
|
197
328
|
resolveTarget(name) {
|
|
198
329
|
const [baseName, variantName] = name.split("@", 2);
|
|
@@ -285,10 +416,10 @@ var MindManager = class {
|
|
|
285
416
|
detached: true,
|
|
286
417
|
env
|
|
287
418
|
};
|
|
288
|
-
const
|
|
289
|
-
this.minds.set(name, { child
|
|
290
|
-
|
|
291
|
-
|
|
419
|
+
const child = spawn(spawnCmd, spawnArgs, spawnOpts);
|
|
420
|
+
this.minds.set(name, { child, port });
|
|
421
|
+
child.stdout?.pipe(logStream);
|
|
422
|
+
child.stderr?.pipe(logStream);
|
|
292
423
|
try {
|
|
293
424
|
await new Promise((resolve2, reject) => {
|
|
294
425
|
const timeout = setTimeout(() => {
|
|
@@ -300,13 +431,13 @@ var MindManager = class {
|
|
|
300
431
|
resolve2();
|
|
301
432
|
}
|
|
302
433
|
}
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
434
|
+
child.stdout?.on("data", checkOutput);
|
|
435
|
+
child.stderr?.on("data", checkOutput);
|
|
436
|
+
child.on("error", (err) => {
|
|
306
437
|
clearTimeout(timeout);
|
|
307
438
|
reject(err);
|
|
308
439
|
});
|
|
309
|
-
|
|
440
|
+
child.on("exit", (code) => {
|
|
310
441
|
clearTimeout(timeout);
|
|
311
442
|
reject(new Error(`Mind ${name} exited with code ${code} during startup`));
|
|
312
443
|
});
|
|
@@ -314,20 +445,20 @@ var MindManager = class {
|
|
|
314
445
|
} catch (err) {
|
|
315
446
|
this.minds.delete(name);
|
|
316
447
|
try {
|
|
317
|
-
|
|
448
|
+
child.kill();
|
|
318
449
|
} catch {
|
|
319
450
|
}
|
|
320
451
|
throw err;
|
|
321
452
|
}
|
|
322
|
-
if (
|
|
453
|
+
if (child.pid) {
|
|
323
454
|
try {
|
|
324
|
-
writeFileSync2(pidFile, String(
|
|
455
|
+
writeFileSync2(pidFile, String(child.pid));
|
|
325
456
|
} catch (err) {
|
|
326
457
|
mlog.warn(`failed to write PID file for ${name}`, logger_default.errorData(err));
|
|
327
458
|
}
|
|
328
459
|
}
|
|
329
|
-
if (this.
|
|
330
|
-
this.setupCrashRecovery(name,
|
|
460
|
+
if (this.restartTracker.reset(name)) this.saveCrashAttempts();
|
|
461
|
+
this.setupCrashRecovery(name, child);
|
|
331
462
|
if (isVariant) {
|
|
332
463
|
setVariantRunning(baseName, variantName, true);
|
|
333
464
|
} else {
|
|
@@ -347,13 +478,11 @@ var MindManager = class {
|
|
|
347
478
|
this.pendingContext.delete(name);
|
|
348
479
|
const parts = [];
|
|
349
480
|
if (context.type === "merge" || context.type === "merged") {
|
|
350
|
-
parts.push(
|
|
481
|
+
parts.push(await getPrompt("merge_message", { name: String(context.name ?? "") }));
|
|
351
482
|
} else if (context.type === "sprouted") {
|
|
352
|
-
parts.push(
|
|
353
|
-
"[system] You've sprouted. You now have full capabilities \u2014 connectors, schedules, variants, and the complete volute CLI. Check your new skills for details."
|
|
354
|
-
);
|
|
483
|
+
parts.push(await getPrompt("sprout_message"));
|
|
355
484
|
} else {
|
|
356
|
-
parts.push(
|
|
485
|
+
parts.push(await getPrompt("restart_message"));
|
|
357
486
|
}
|
|
358
487
|
if (context.summary) parts.push(`Changes: ${context.summary}`);
|
|
359
488
|
if (context.justification) parts.push(`Why: ${context.justification}`);
|
|
@@ -371,14 +500,15 @@ var MindManager = class {
|
|
|
371
500
|
mlog.warn(`failed to deliver pending context to ${name}`, logger_default.errorData(err));
|
|
372
501
|
}
|
|
373
502
|
}
|
|
374
|
-
setupCrashRecovery(name,
|
|
375
|
-
|
|
503
|
+
setupCrashRecovery(name, child) {
|
|
504
|
+
child.on("exit", async (code) => {
|
|
376
505
|
this.minds.delete(name);
|
|
377
506
|
if (this.shuttingDown || this.stopping.has(name)) return;
|
|
378
507
|
mlog.error(`mind ${name} exited with code ${code}`);
|
|
379
|
-
const
|
|
380
|
-
|
|
381
|
-
|
|
508
|
+
const { shouldRestart, delay, attempt } = this.restartTracker.recordCrash(name);
|
|
509
|
+
this.saveCrashAttempts();
|
|
510
|
+
if (!shouldRestart) {
|
|
511
|
+
mlog.error(`${name} crashed ${attempt} times \u2014 giving up on restart`);
|
|
382
512
|
const [base, variant] = name.split("@", 2);
|
|
383
513
|
if (variant) {
|
|
384
514
|
setVariantRunning(base, variant, false);
|
|
@@ -387,11 +517,8 @@ var MindManager = class {
|
|
|
387
517
|
}
|
|
388
518
|
return;
|
|
389
519
|
}
|
|
390
|
-
const delay = Math.min(BASE_RESTART_DELAY * 2 ** attempts, MAX_RESTART_DELAY);
|
|
391
|
-
this.restartAttempts.set(name, attempts + 1);
|
|
392
|
-
this.saveCrashAttempts();
|
|
393
520
|
mlog.info(
|
|
394
|
-
`crash recovery for ${name} \u2014 attempt ${
|
|
521
|
+
`crash recovery for ${name} \u2014 attempt ${attempt}/${this.restartTracker.maxRestartAttempts}, restarting in ${delay}ms`
|
|
395
522
|
);
|
|
396
523
|
setTimeout(() => {
|
|
397
524
|
if (this.shuttingDown) return;
|
|
@@ -405,25 +532,25 @@ var MindManager = class {
|
|
|
405
532
|
const tracked = this.minds.get(name);
|
|
406
533
|
if (!tracked) return;
|
|
407
534
|
this.stopping.add(name);
|
|
408
|
-
const { child
|
|
535
|
+
const { child } = tracked;
|
|
409
536
|
this.minds.delete(name);
|
|
410
537
|
await new Promise((resolve2) => {
|
|
411
|
-
|
|
538
|
+
child.on("exit", () => resolve2());
|
|
412
539
|
try {
|
|
413
|
-
process.kill(-
|
|
540
|
+
process.kill(-child.pid, "SIGTERM");
|
|
414
541
|
} catch {
|
|
415
542
|
resolve2();
|
|
416
543
|
}
|
|
417
544
|
setTimeout(() => {
|
|
418
545
|
try {
|
|
419
|
-
process.kill(-
|
|
546
|
+
process.kill(-child.pid, "SIGKILL");
|
|
420
547
|
} catch {
|
|
421
548
|
}
|
|
422
549
|
resolve2();
|
|
423
550
|
}, 5e3);
|
|
424
551
|
});
|
|
425
552
|
this.stopping.delete(name);
|
|
426
|
-
if (this.
|
|
553
|
+
if (this.restartTracker.reset(name)) this.saveCrashAttempts();
|
|
427
554
|
rmSync2(mindPidPath(name), { force: true });
|
|
428
555
|
if (!this.shuttingDown) {
|
|
429
556
|
const [baseName, variantName] = name.split("@", 2);
|
|
@@ -454,13 +581,14 @@ var MindManager = class {
|
|
|
454
581
|
return resolve(voluteHome(), "crash-attempts.json");
|
|
455
582
|
}
|
|
456
583
|
loadCrashAttempts() {
|
|
457
|
-
this.
|
|
584
|
+
this.restartTracker.load(loadJsonMap(this.crashAttemptsPath));
|
|
458
585
|
}
|
|
459
586
|
saveCrashAttempts() {
|
|
460
|
-
saveJsonMap(this.crashAttemptsPath, this.
|
|
587
|
+
saveJsonMap(this.crashAttemptsPath, this.restartTracker.save());
|
|
461
588
|
}
|
|
462
589
|
clearCrashAttempts() {
|
|
463
|
-
|
|
590
|
+
this.restartTracker.clear();
|
|
591
|
+
clearJsonMap(this.crashAttemptsPath, /* @__PURE__ */ new Map());
|
|
464
592
|
}
|
|
465
593
|
};
|
|
466
594
|
async function killProcessOnPort(port) {
|
|
@@ -497,17 +625,22 @@ function initMindManager() {
|
|
|
497
625
|
return instance;
|
|
498
626
|
}
|
|
499
627
|
function getMindManager() {
|
|
500
|
-
if (!instance)
|
|
628
|
+
if (!instance) throw new Error("MindManager not initialized \u2014 call initMindManager() first");
|
|
501
629
|
return instance;
|
|
502
630
|
}
|
|
503
631
|
|
|
504
632
|
export {
|
|
505
|
-
|
|
506
|
-
logger_default,
|
|
633
|
+
RestartTracker,
|
|
507
634
|
RotatingLog,
|
|
508
635
|
loadJsonMap,
|
|
509
636
|
saveJsonMap,
|
|
510
637
|
clearJsonMap,
|
|
638
|
+
PROMPT_KEYS,
|
|
639
|
+
PROMPT_DEFAULTS,
|
|
640
|
+
substitute,
|
|
641
|
+
getPrompt,
|
|
642
|
+
getPromptIfCustom,
|
|
643
|
+
getMindPromptDefaults,
|
|
511
644
|
MindManager,
|
|
512
645
|
initMindManager,
|
|
513
646
|
getMindManager
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
execInherit
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-DYZGP3EW.js";
|
|
5
5
|
import {
|
|
6
6
|
voluteHome
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-EBGCNDMM.js";
|
|
8
8
|
|
|
9
9
|
// src/lib/service-mode.ts
|
|
10
10
|
import { execFileSync } from "child_process";
|