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
|
@@ -102,6 +102,49 @@ export async function handleStartupContext(sendMessage: (content: string) => voi
|
|
|
102
102
|
}
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
+
export type MindPrompts = {
|
|
106
|
+
compaction_warning: string;
|
|
107
|
+
reply_instructions: string;
|
|
108
|
+
channel_invite: string;
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
const DEFAULT_PROMPTS: MindPrompts = {
|
|
112
|
+
compaction_warning:
|
|
113
|
+
"Context is getting long — 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.",
|
|
114
|
+
reply_instructions: 'To reply to this message, use: volute send ${channel} "your message"',
|
|
115
|
+
channel_invite: `[Channel Invite]
|
|
116
|
+
\${headers}
|
|
117
|
+
|
|
118
|
+
[\${sender} — \${time}]
|
|
119
|
+
\${preview}
|
|
120
|
+
|
|
121
|
+
Further messages will be saved to \${filePath}
|
|
122
|
+
|
|
123
|
+
To accept, add to .config/routes.json:
|
|
124
|
+
Rule: { "channel": "\${channel}", "session": "\${suggestedSession}" }
|
|
125
|
+
\${batchRecommendation}To respond, use: volute send \${channel} "your message"
|
|
126
|
+
To reject, delete \${filePath}`,
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
export function loadPrompts(): MindPrompts {
|
|
130
|
+
try {
|
|
131
|
+
const raw = readFileSync(resolve("home/.config/prompts.json"), "utf-8");
|
|
132
|
+
const parsed = JSON.parse(raw);
|
|
133
|
+
const result = { ...DEFAULT_PROMPTS };
|
|
134
|
+
for (const key of Object.keys(DEFAULT_PROMPTS) as (keyof MindPrompts)[]) {
|
|
135
|
+
if (typeof parsed[key] === "string") {
|
|
136
|
+
result[key] = parsed[key];
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return result;
|
|
140
|
+
} catch (err: any) {
|
|
141
|
+
if (err?.code !== "ENOENT") {
|
|
142
|
+
log("startup", "failed to load prompts.json, using defaults:", err);
|
|
143
|
+
}
|
|
144
|
+
return DEFAULT_PROMPTS;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
105
148
|
export function setupShutdown(): void {
|
|
106
149
|
function shutdown() {
|
|
107
150
|
log("server", "shutdown signal received");
|
|
@@ -13,6 +13,10 @@ export type ChannelMeta = {
|
|
|
13
13
|
participants?: string[];
|
|
14
14
|
participantCount?: number;
|
|
15
15
|
typing?: string[];
|
|
16
|
+
signature?: string;
|
|
17
|
+
signatureTimestamp?: string;
|
|
18
|
+
signerFingerprint?: string;
|
|
19
|
+
verified?: boolean;
|
|
16
20
|
};
|
|
17
21
|
|
|
18
22
|
/** ChannelMeta enriched by the router with dispatch info. */
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import { createHash, verify } from "node:crypto";
|
|
1
2
|
import { createServer, type IncomingMessage, type Server } from "node:http";
|
|
2
3
|
import { log } from "./logger.js";
|
|
3
4
|
import type { Router } from "./router.js";
|
|
4
|
-
import type { VoluteRequest } from "./types.js";
|
|
5
|
+
import type { VoluteContentPart, VoluteRequest } from "./types.js";
|
|
5
6
|
|
|
6
7
|
function readBody(req: IncomingMessage): Promise<string> {
|
|
7
8
|
return new Promise((resolve, reject) => {
|
|
@@ -12,6 +13,71 @@ function readBody(req: IncomingMessage): Promise<string> {
|
|
|
12
13
|
});
|
|
13
14
|
}
|
|
14
15
|
|
|
16
|
+
function extractText(content: VoluteContentPart[] | string): string {
|
|
17
|
+
if (typeof content === "string") return content;
|
|
18
|
+
return content
|
|
19
|
+
.filter((p): p is { type: "text"; text: string } => p.type === "text")
|
|
20
|
+
.map((p) => p.text)
|
|
21
|
+
.join("\n");
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** Normalize content to VoluteContentPart[] — connectors may send plain strings. */
|
|
25
|
+
function normalizeContent(content: unknown): VoluteContentPart[] {
|
|
26
|
+
if (Array.isArray(content)) return content as VoluteContentPart[];
|
|
27
|
+
if (typeof content === "string") return [{ type: "text", text: content }];
|
|
28
|
+
return [{ type: "text", text: JSON.stringify(content) }];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Verify an Ed25519 signature against a public key */
|
|
32
|
+
function verifySignature(
|
|
33
|
+
publicKeyPem: string,
|
|
34
|
+
content: string,
|
|
35
|
+
timestamp: string,
|
|
36
|
+
signature: string,
|
|
37
|
+
): boolean {
|
|
38
|
+
try {
|
|
39
|
+
const data = `${content}\n${timestamp}`;
|
|
40
|
+
return verify(null, Buffer.from(data), publicKeyPem, Buffer.from(signature, "base64"));
|
|
41
|
+
} catch {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Look up a mind's public key via the daemon API */
|
|
47
|
+
async function fetchPublicKey(fingerprint: string): Promise<string | null> {
|
|
48
|
+
const daemonPort = process.env.VOLUTE_DAEMON_PORT;
|
|
49
|
+
const daemonToken = process.env.VOLUTE_DAEMON_TOKEN;
|
|
50
|
+
if (!daemonPort || !daemonToken) return null;
|
|
51
|
+
|
|
52
|
+
try {
|
|
53
|
+
const res = await fetch(
|
|
54
|
+
`http://127.0.0.1:${daemonPort}/api/keys/${encodeURIComponent(fingerprint)}`,
|
|
55
|
+
{ headers: { Authorization: `Bearer ${daemonToken}` }, signal: AbortSignal.timeout(2000) },
|
|
56
|
+
);
|
|
57
|
+
if (!res.ok) return null;
|
|
58
|
+
const data = (await res.json()) as { publicKey?: string };
|
|
59
|
+
return data.publicKey ?? null;
|
|
60
|
+
} catch (err) {
|
|
61
|
+
log("identity", "failed to fetch public key:", err);
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Best-effort signature verification */
|
|
67
|
+
async function verifyRequest(body: VoluteRequest): Promise<boolean | undefined> {
|
|
68
|
+
if (!body.signature || !body.signatureTimestamp || !body.signerFingerprint) return undefined;
|
|
69
|
+
|
|
70
|
+
const publicKey = await fetchPublicKey(body.signerFingerprint);
|
|
71
|
+
if (!publicKey) return false;
|
|
72
|
+
|
|
73
|
+
// Verify the fingerprint matches
|
|
74
|
+
const expectedFingerprint = createHash("sha256").update(publicKey).digest("hex");
|
|
75
|
+
if (expectedFingerprint !== body.signerFingerprint) return false;
|
|
76
|
+
|
|
77
|
+
const text = extractText(body.content);
|
|
78
|
+
return verifySignature(publicKey, text, body.signatureTimestamp, body.signature);
|
|
79
|
+
}
|
|
80
|
+
|
|
15
81
|
export function createVoluteServer(options: {
|
|
16
82
|
router: Router;
|
|
17
83
|
port: number;
|
|
@@ -32,7 +98,30 @@ export function createVoluteServer(options: {
|
|
|
32
98
|
if (req.method === "POST" && url.pathname === "/message") {
|
|
33
99
|
try {
|
|
34
100
|
const body = JSON.parse(await readBody(req)) as VoluteRequest;
|
|
35
|
-
|
|
101
|
+
|
|
102
|
+
// Strip any sender-provided verified field to prevent spoofing
|
|
103
|
+
delete body.verified;
|
|
104
|
+
|
|
105
|
+
// Best-effort signature verification (non-blocking)
|
|
106
|
+
const verified = await verifyRequest(body);
|
|
107
|
+
if (verified !== undefined) body.verified = verified;
|
|
108
|
+
|
|
109
|
+
// Normalize content — connectors may send plain strings
|
|
110
|
+
body.content = normalizeContent(body.content);
|
|
111
|
+
|
|
112
|
+
// Handle batch payloads from delivery manager
|
|
113
|
+
if ((body as any).batch) {
|
|
114
|
+
const batch = (body as any).batch as {
|
|
115
|
+
channels: Record<string, any[]>;
|
|
116
|
+
};
|
|
117
|
+
router.dispatchBatch(batch, body.session ?? "main", body);
|
|
118
|
+
} else if (body.session) {
|
|
119
|
+
// Pre-routed by daemon delivery manager — dispatch directly
|
|
120
|
+
router.dispatch(body.content, body.session, body);
|
|
121
|
+
} else {
|
|
122
|
+
// Legacy: local routing (for minds running with old daemon)
|
|
123
|
+
router.route(body.content, body);
|
|
124
|
+
}
|
|
36
125
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
37
126
|
res.end(JSON.stringify({ ok: true }));
|
|
38
127
|
} catch (err) {
|
|
@@ -9,6 +9,7 @@ import { createSessionContextHook } from "./lib/hooks/session-context.js";
|
|
|
9
9
|
import { log } from "./lib/logger.js";
|
|
10
10
|
import { createMessageChannel } from "./lib/message-channel.js";
|
|
11
11
|
import { createSessionStore } from "./lib/session-store.js";
|
|
12
|
+
import { loadPrompts } from "./lib/startup.js";
|
|
12
13
|
import { consumeStream } from "./lib/stream-consumer.js";
|
|
13
14
|
import type {
|
|
14
15
|
HandlerMeta,
|
|
@@ -47,10 +48,10 @@ export function createMind(options: {
|
|
|
47
48
|
];
|
|
48
49
|
|
|
49
50
|
const sessions = new Map<string, Session>();
|
|
50
|
-
const
|
|
51
|
+
const prompts = loadPrompts();
|
|
52
|
+
const today = new Date().toLocaleDateString("en-CA");
|
|
51
53
|
const compactionMessage =
|
|
52
|
-
options.compactionMessage ??
|
|
53
|
-
`Context is getting long — compaction is about to summarize this conversation. Before that happens, save anything important to files (MEMORY.md, memory/journal/${today}.md, etc.) since those survive compaction. Focus on: decisions made, open tasks, and anything you'd need to pick up where you left off.`;
|
|
54
|
+
options.compactionMessage ?? prompts.compaction_warning.replace("${date}", today);
|
|
54
55
|
|
|
55
56
|
// --- Event broadcasting ---
|
|
56
57
|
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import type { HookCallback } from "@anthropic-ai/claude-agent-sdk";
|
|
2
|
+
import { loadPrompts } from "../startup.js";
|
|
2
3
|
|
|
3
4
|
export function createReplyInstructionsHook(messageChannels: Map<string, string>) {
|
|
4
5
|
let fired = false;
|
|
6
|
+
const prompts = loadPrompts();
|
|
5
7
|
|
|
6
8
|
const hook: HookCallback = async () => {
|
|
7
9
|
if (fired) return {};
|
|
@@ -14,7 +16,7 @@ export function createReplyInstructionsHook(messageChannels: Map<string, string>
|
|
|
14
16
|
return {
|
|
15
17
|
hookSpecificOutput: {
|
|
16
18
|
hookEventName: "UserPromptSubmit" as const,
|
|
17
|
-
additionalContext:
|
|
19
|
+
additionalContext: prompts.reply_instructions.replace(/\$\{channel\}/g, channel),
|
|
18
20
|
},
|
|
19
21
|
};
|
|
20
22
|
};
|
|
@@ -20,10 +20,10 @@ if (config.model) log("server", `using model: ${config.model}`);
|
|
|
20
20
|
if (config.maxThinkingTokens) log("server", `max thinking tokens: ${config.maxThinkingTokens}`);
|
|
21
21
|
|
|
22
22
|
const systemPrompt = loadSystemPrompt();
|
|
23
|
-
const sessionsDir = resolve(".
|
|
23
|
+
const sessionsDir = resolve(".mind/sessions");
|
|
24
24
|
|
|
25
25
|
// Migrate old single session.json → sessions/main.json
|
|
26
|
-
const oldSessionPath = resolve(".
|
|
26
|
+
const oldSessionPath = resolve(".mind/session.json");
|
|
27
27
|
if (existsSync(oldSessionPath) && !existsSync(resolve(sessionsDir, "main.json"))) {
|
|
28
28
|
mkdirSync(sessionsDir, { recursive: true });
|
|
29
29
|
renameSync(oldSessionPath, resolve(sessionsDir, "main.json"));
|
|
@@ -4,6 +4,5 @@
|
|
|
4
4
|
"biome.json.tmpl": "biome.json",
|
|
5
5
|
"home/.config/config.json.tmpl": "home/.config/config.json"
|
|
6
6
|
},
|
|
7
|
-
"substitute": ["package.json", ".init/SOUL.md", "home/.config/routes.json"]
|
|
8
|
-
"skillsDir": "home/.claude/skills"
|
|
7
|
+
"substitute": ["package.json", ".init/SOUL.md", "home/.config/routes.json"]
|
|
9
8
|
}
|
|
@@ -13,6 +13,7 @@ import { log } from "./lib/logger.js";
|
|
|
13
13
|
import { createReplyInstructionsExtension } from "./lib/reply-instructions-extension.js";
|
|
14
14
|
import { resolveModel } from "./lib/resolve-model.js";
|
|
15
15
|
import { createSessionContextExtension } from "./lib/session-context-extension.js";
|
|
16
|
+
import { loadPrompts } from "./lib/startup.js";
|
|
16
17
|
import type {
|
|
17
18
|
HandlerMeta,
|
|
18
19
|
HandlerResolver,
|
|
@@ -35,11 +36,6 @@ type PiSession = {
|
|
|
35
36
|
messageChannels: Map<string, string>;
|
|
36
37
|
};
|
|
37
38
|
|
|
38
|
-
function defaultCompactionMessage(): string {
|
|
39
|
-
const today = new Date().toISOString().slice(0, 10);
|
|
40
|
-
return `Context is getting long — compaction is about to summarize this conversation. Before that happens, save anything important to files (MEMORY.md, memory/journal/${today}.md, etc.) since those survive compaction. Focus on: decisions made, open tasks, and anything you'd need to pick up where you left off.`;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
39
|
export function createMind(options: {
|
|
44
40
|
systemPrompt: string;
|
|
45
41
|
cwd: string;
|
|
@@ -48,7 +44,10 @@ export function createMind(options: {
|
|
|
48
44
|
compactionMessage?: string;
|
|
49
45
|
}): { resolve: HandlerResolver } {
|
|
50
46
|
const sessions = new Map<string, PiSession>();
|
|
51
|
-
const
|
|
47
|
+
const prompts = loadPrompts();
|
|
48
|
+
const today = new Date().toLocaleDateString("en-CA");
|
|
49
|
+
const compactionMessage =
|
|
50
|
+
options.compactionMessage ?? prompts.compaction_warning.replace("${date}", today);
|
|
52
51
|
|
|
53
52
|
// Shared setup (created once)
|
|
54
53
|
const modelStr = options.model || process.env.PI_MODEL || "anthropic:claude-sonnet-4-20250514";
|
|
@@ -84,7 +83,7 @@ export function createMind(options: {
|
|
|
84
83
|
|
|
85
84
|
const sessionManager = isEphemeral
|
|
86
85
|
? SessionManager.inMemory()
|
|
87
|
-
: SessionManager.continueRecent(options.cwd, `.
|
|
86
|
+
: SessionManager.continueRecent(options.cwd, `.mind/pi-sessions/${session.name}`);
|
|
88
87
|
|
|
89
88
|
log("mind", `session "${session.name}": ${isEphemeral ? "ephemeral" : "persistent"}`);
|
|
90
89
|
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import type { ExtensionFactory } from "@mariozechner/pi-coding-agent";
|
|
2
|
+
import { loadPrompts } from "./startup.js";
|
|
2
3
|
|
|
3
4
|
export function createReplyInstructionsExtension(
|
|
4
5
|
messageChannels: Map<string, string>,
|
|
5
6
|
): ExtensionFactory {
|
|
7
|
+
const prompts = loadPrompts();
|
|
6
8
|
return (pi) => {
|
|
7
9
|
let fired = false;
|
|
8
10
|
pi.on("before_agent_start", () => {
|
|
@@ -16,7 +18,7 @@ export function createReplyInstructionsExtension(
|
|
|
16
18
|
return {
|
|
17
19
|
message: {
|
|
18
20
|
customType: "reply-instructions",
|
|
19
|
-
content:
|
|
21
|
+
content: prompts.reply_instructions.replace(/\$\{channel\}/g, channel),
|
|
20
22
|
display: true,
|
|
21
23
|
},
|
|
22
24
|
};
|
|
@@ -9,11 +9,11 @@ export function createSessionContextExtension(options: {
|
|
|
9
9
|
return (pi) => {
|
|
10
10
|
pi.on("before_agent_start", () => {
|
|
11
11
|
try {
|
|
12
|
-
const sessionsDir = resolve(options.cwd, ".
|
|
12
|
+
const sessionsDir = resolve(options.cwd, ".mind/pi-sessions");
|
|
13
13
|
const summary = getSessionUpdates({
|
|
14
14
|
currentSession: options.currentSession,
|
|
15
15
|
sessionsDir,
|
|
16
|
-
cursorFile: resolve(options.cwd, ".
|
|
16
|
+
cursorFile: resolve(options.cwd, ".mind/session-cursors.json"),
|
|
17
17
|
jsonlResolver: (name) => resolvePiJsonl(sessionsDir, name),
|
|
18
18
|
format: "pi",
|
|
19
19
|
});
|
|
@@ -4,6 +4,5 @@
|
|
|
4
4
|
"biome.json.tmpl": "biome.json",
|
|
5
5
|
"home/.config/config.json.tmpl": "home/.config/config.json"
|
|
6
6
|
},
|
|
7
|
-
"substitute": ["package.json", ".init/SOUL.md", "home/.config/routes.json"]
|
|
8
|
-
"skillsDir": "home/.claude/skills"
|
|
7
|
+
"substitute": ["package.json", ".init/SOUL.md", "home/.config/routes.json"]
|
|
9
8
|
}
|
package/dist/chunk-PO5Q2AYN.js
DELETED
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
// src/lib/template.ts
|
|
4
|
-
import {
|
|
5
|
-
cpSync,
|
|
6
|
-
existsSync,
|
|
7
|
-
mkdirSync,
|
|
8
|
-
readdirSync,
|
|
9
|
-
readFileSync,
|
|
10
|
-
renameSync,
|
|
11
|
-
rmSync,
|
|
12
|
-
statSync,
|
|
13
|
-
writeFileSync
|
|
14
|
-
} from "fs";
|
|
15
|
-
import { tmpdir } from "os";
|
|
16
|
-
import { dirname, join, relative, resolve } from "path";
|
|
17
|
-
function findTemplatesRoot() {
|
|
18
|
-
let dir = dirname(new URL(import.meta.url).pathname);
|
|
19
|
-
for (let i = 0; i < 5; i++) {
|
|
20
|
-
const candidate = resolve(dir, "templates");
|
|
21
|
-
if (existsSync(resolve(candidate, "_base"))) return candidate;
|
|
22
|
-
dir = dirname(dir);
|
|
23
|
-
}
|
|
24
|
-
console.error(
|
|
25
|
-
"Templates directory not found. Searched up from:",
|
|
26
|
-
dirname(new URL(import.meta.url).pathname)
|
|
27
|
-
);
|
|
28
|
-
process.exit(1);
|
|
29
|
-
}
|
|
30
|
-
function composeTemplate(templatesRoot, templateName) {
|
|
31
|
-
const baseDir = resolve(templatesRoot, "_base");
|
|
32
|
-
const templateDir = resolve(templatesRoot, templateName);
|
|
33
|
-
if (!existsSync(baseDir)) {
|
|
34
|
-
console.error("Base template not found:", baseDir);
|
|
35
|
-
process.exit(1);
|
|
36
|
-
}
|
|
37
|
-
if (!existsSync(templateDir)) {
|
|
38
|
-
console.error(`Template not found: ${templateName}`);
|
|
39
|
-
process.exit(1);
|
|
40
|
-
}
|
|
41
|
-
const composedDir = resolve(tmpdir(), `volute-template-${Date.now()}`);
|
|
42
|
-
mkdirSync(composedDir, { recursive: true });
|
|
43
|
-
cpSync(baseDir, composedDir, { recursive: true });
|
|
44
|
-
for (const file of listFiles(templateDir)) {
|
|
45
|
-
const src = resolve(templateDir, file);
|
|
46
|
-
const dest = resolve(composedDir, file);
|
|
47
|
-
mkdirSync(dirname(dest), { recursive: true });
|
|
48
|
-
cpSync(src, dest);
|
|
49
|
-
}
|
|
50
|
-
const manifestPath = resolve(composedDir, "volute-template.json");
|
|
51
|
-
if (!existsSync(manifestPath)) {
|
|
52
|
-
rmSync(composedDir, { recursive: true, force: true });
|
|
53
|
-
console.error(`Template manifest not found: ${templateName}/volute-template.json`);
|
|
54
|
-
process.exit(1);
|
|
55
|
-
}
|
|
56
|
-
const manifest = JSON.parse(readFileSync(manifestPath, "utf-8"));
|
|
57
|
-
const skillsSrc = resolve(composedDir, "_skills");
|
|
58
|
-
if (existsSync(skillsSrc)) {
|
|
59
|
-
const skillsDest = resolve(composedDir, manifest.skillsDir);
|
|
60
|
-
mkdirSync(skillsDest, { recursive: true });
|
|
61
|
-
cpSync(skillsSrc, skillsDest, { recursive: true });
|
|
62
|
-
rmSync(skillsSrc, { recursive: true, force: true });
|
|
63
|
-
}
|
|
64
|
-
rmSync(manifestPath);
|
|
65
|
-
return { composedDir, manifest };
|
|
66
|
-
}
|
|
67
|
-
function copyTemplateToDir(composedDir, destDir, mindName, manifest) {
|
|
68
|
-
cpSync(composedDir, destDir, { recursive: true });
|
|
69
|
-
for (const [from, to] of Object.entries(manifest.rename)) {
|
|
70
|
-
const fromPath = resolve(destDir, from);
|
|
71
|
-
if (existsSync(fromPath)) {
|
|
72
|
-
renameSync(fromPath, resolve(destDir, to));
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
for (const file of manifest.substitute) {
|
|
76
|
-
const path = resolve(destDir, file);
|
|
77
|
-
if (existsSync(path)) {
|
|
78
|
-
const content = readFileSync(path, "utf-8");
|
|
79
|
-
writeFileSync(path, content.replaceAll("{{name}}", mindName));
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
function applyInitFiles(destDir) {
|
|
84
|
-
const initDir = resolve(destDir, ".init");
|
|
85
|
-
if (!existsSync(initDir)) return;
|
|
86
|
-
const homeDir = resolve(destDir, "home");
|
|
87
|
-
for (const file of listFiles(initDir)) {
|
|
88
|
-
const src = resolve(initDir, file);
|
|
89
|
-
const dest = resolve(homeDir, file);
|
|
90
|
-
const parent = dirname(dest);
|
|
91
|
-
if (!existsSync(parent)) {
|
|
92
|
-
mkdirSync(parent, { recursive: true });
|
|
93
|
-
}
|
|
94
|
-
cpSync(src, dest);
|
|
95
|
-
}
|
|
96
|
-
rmSync(initDir, { recursive: true, force: true });
|
|
97
|
-
}
|
|
98
|
-
function listFiles(dir) {
|
|
99
|
-
const results = [];
|
|
100
|
-
function walk(current) {
|
|
101
|
-
for (const entry of readdirSync(current)) {
|
|
102
|
-
const full = join(current, entry);
|
|
103
|
-
if (statSync(full).isDirectory()) {
|
|
104
|
-
if (entry === ".git") continue;
|
|
105
|
-
walk(full);
|
|
106
|
-
} else {
|
|
107
|
-
results.push(relative(dir, full));
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
walk(dir);
|
|
112
|
-
return results;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
export {
|
|
116
|
-
findTemplatesRoot,
|
|
117
|
-
composeTemplate,
|
|
118
|
-
copyTemplateToDir,
|
|
119
|
-
applyInitFiles,
|
|
120
|
-
listFiles
|
|
121
|
-
};
|
package/dist/down-A56B5JLK.js
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
run,
|
|
4
|
-
stopDaemon
|
|
5
|
-
} from "./chunk-QJIIHU32.js";
|
|
6
|
-
import "./chunk-6BDNWYKG.js";
|
|
7
|
-
import "./chunk-2Y77MCFG.js";
|
|
8
|
-
import "./chunk-ZCEYUUID.js";
|
|
9
|
-
import "./chunk-M77QBTEH.js";
|
|
10
|
-
import "./chunk-K3NQKI34.js";
|
|
11
|
-
export {
|
|
12
|
-
run,
|
|
13
|
-
stopDaemon
|
|
14
|
-
};
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
MindManager,
|
|
4
|
-
getMindManager,
|
|
5
|
-
initMindManager
|
|
6
|
-
} from "./chunk-CE7WMOVW.js";
|
|
7
|
-
import "./chunk-OYSZNX5I.js";
|
|
8
|
-
import "./chunk-ZCEYUUID.js";
|
|
9
|
-
import "./chunk-M77QBTEH.js";
|
|
10
|
-
import "./chunk-K3NQKI34.js";
|
|
11
|
-
export {
|
|
12
|
-
MindManager,
|
|
13
|
-
getMindManager,
|
|
14
|
-
initMindManager
|
|
15
|
-
};
|