volute 0.17.0 → 0.18.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/dist/{chunk-CE7WMOVW.js → chunk-AYB7XAWO.js} +323 -25
- package/dist/{chunk-MIJIAGGG.js → chunk-FW5API7X.js} +7 -5
- package/dist/{chunk-3FC42ZBM.js → chunk-GK4E7LM7.js} +3 -0
- package/dist/cli.js +18 -6
- package/dist/connectors/discord.js +1 -1
- package/dist/connectors/slack.js +1 -1
- package/dist/connectors/telegram.js +1 -1
- package/dist/{daemon-restart-VRQMZLBK.js → daemon-restart-2HVTHZAT.js} +1 -1
- package/dist/daemon.js +1080 -432
- package/dist/{history-5F4WQW7S.js → history-YUEKTJ2N.js} +4 -1
- package/dist/{mind-manager-ETNCPQJN.js → mind-manager-Z7O7PN2O.js} +1 -1
- package/dist/{package-4GTJGUXI.js → package-OKLFO7UY.js} +3 -1
- package/dist/{send-4GKDO26C.js → send-BNDTLUPM.js} +2 -2
- package/dist/skill-2Y42P4JY.js +287 -0
- package/dist/{up-LT3X5Q26.js → up-7B3BWF2U.js} +1 -1
- package/dist/web-assets/assets/index-CtiimdWK.css +1 -0
- package/dist/web-assets/assets/index-kt1_EcuO.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/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/_journal.json +21 -0
- package/package.json +3 -1
- package/templates/_base/.init/.config/prompts.json +5 -0
- package/templates/_base/_skills/volute-mind/SKILL.md +17 -1
- package/templates/_base/src/lib/router.ts +45 -28
- package/templates/_base/src/lib/routing.ts +4 -1
- package/templates/_base/src/lib/startup.ts +43 -0
- package/templates/claude/src/agent.ts +4 -3
- package/templates/claude/src/lib/hooks/reply-instructions.ts +3 -1
- package/templates/pi/src/agent.ts +5 -6
- package/templates/pi/src/lib/reply-instructions-extension.ts +3 -1
- package/dist/web-assets/assets/index-BcmT7Qxo.js +0 -63
- package/dist/web-assets/assets/index-DG01TyLb.css +0 -1
- /package/dist/{chunk-77ISBIKI.js → chunk-6DVBMLVN.js} +0 -0
|
@@ -7,8 +7,8 @@
|
|
|
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-
|
|
11
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
10
|
+
<script type="module" crossorigin src="/assets/index-kt1_EcuO.js"></script>
|
|
11
|
+
<link rel="stylesheet" crossorigin href="/assets/index-CtiimdWK.css">
|
|
12
12
|
</head>
|
|
13
13
|
<body>
|
|
14
14
|
<div id="root"></div>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
-- Rebuild conversations table: make mind_name nullable, add type + name columns
|
|
2
|
+
CREATE TABLE `conversations_new` (
|
|
3
|
+
`id` text PRIMARY KEY NOT NULL,
|
|
4
|
+
`mind_name` text,
|
|
5
|
+
`channel` text NOT NULL,
|
|
6
|
+
`type` text NOT NULL DEFAULT 'dm',
|
|
7
|
+
`name` text,
|
|
8
|
+
`user_id` integer REFERENCES `users`(`id`),
|
|
9
|
+
`title` text,
|
|
10
|
+
`created_at` text DEFAULT (datetime('now')) NOT NULL,
|
|
11
|
+
`updated_at` text DEFAULT (datetime('now')) NOT NULL
|
|
12
|
+
);--> statement-breakpoint
|
|
13
|
+
INSERT INTO `conversations_new` (`id`, `mind_name`, `channel`, `type`, `name`, `user_id`, `title`, `created_at`, `updated_at`)
|
|
14
|
+
SELECT `id`, `mind_name`, `channel`, 'dm', NULL, `user_id`, `title`, `created_at`, `updated_at` FROM `conversations`;--> statement-breakpoint
|
|
15
|
+
DROP TABLE `conversations`;--> statement-breakpoint
|
|
16
|
+
ALTER TABLE `conversations_new` RENAME TO `conversations`;--> statement-breakpoint
|
|
17
|
+
CREATE INDEX `idx_conversations_mind_name` ON `conversations` (`mind_name`);--> statement-breakpoint
|
|
18
|
+
CREATE INDEX `idx_conversations_user_id` ON `conversations` (`user_id`);--> statement-breakpoint
|
|
19
|
+
CREATE INDEX `idx_conversations_updated_at` ON `conversations` (`updated_at`);--> statement-breakpoint
|
|
20
|
+
CREATE UNIQUE INDEX `idx_conversations_name` ON `conversations` (`name`);--> statement-breakpoint
|
|
21
|
+
-- Backfill: mark conversations with 3+ participants as 'group'
|
|
22
|
+
UPDATE `conversations` SET `type` = 'group' WHERE `id` IN (
|
|
23
|
+
SELECT `conversation_id` FROM `conversation_participants` GROUP BY `conversation_id` HAVING COUNT(*) > 2
|
|
24
|
+
);
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
CREATE TABLE `shared_skills` (
|
|
2
|
+
`id` text PRIMARY KEY NOT NULL,
|
|
3
|
+
`name` text NOT NULL,
|
|
4
|
+
`description` text DEFAULT '' NOT NULL,
|
|
5
|
+
`author` text NOT NULL,
|
|
6
|
+
`version` integer DEFAULT 1 NOT NULL,
|
|
7
|
+
`created_at` text DEFAULT (datetime('now')) NOT NULL,
|
|
8
|
+
`updated_at` text DEFAULT (datetime('now')) NOT NULL
|
|
9
|
+
);
|
|
@@ -50,6 +50,27 @@
|
|
|
50
50
|
"when": 1771400000000,
|
|
51
51
|
"tag": "0006_mind_history",
|
|
52
52
|
"breakpoints": true
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
"idx": 7,
|
|
56
|
+
"version": "6",
|
|
57
|
+
"when": 1771600000000,
|
|
58
|
+
"tag": "0007_system_prompts",
|
|
59
|
+
"breakpoints": true
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
"idx": 8,
|
|
63
|
+
"version": "6",
|
|
64
|
+
"when": 1771700000000,
|
|
65
|
+
"tag": "0008_volute_channels",
|
|
66
|
+
"breakpoints": true
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"idx": 9,
|
|
70
|
+
"version": "6",
|
|
71
|
+
"when": 1771800000000,
|
|
72
|
+
"tag": "0009_shared_skills",
|
|
73
|
+
"breakpoints": true
|
|
53
74
|
}
|
|
54
75
|
]
|
|
55
76
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "volute",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.0",
|
|
4
4
|
"description": "CLI for creating and managing self-modifying AI minds powered by the Claude Agent SDK",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -49,6 +49,7 @@
|
|
|
49
49
|
"@hono/zod-validator": "^0.7.6",
|
|
50
50
|
"@libsql/client": "^0.17.0",
|
|
51
51
|
"@slack/bolt": "^4.6.0",
|
|
52
|
+
"adm-zip": "^0.5.16",
|
|
52
53
|
"bcryptjs": "^3.0.3",
|
|
53
54
|
"cron-parser": "^5.5.0",
|
|
54
55
|
"discord.js": "^14.25.1",
|
|
@@ -63,6 +64,7 @@
|
|
|
63
64
|
"@mariozechner/pi-ai": "^0.52.7",
|
|
64
65
|
"@mariozechner/pi-coding-agent": "^0.52.7",
|
|
65
66
|
"@sveltejs/vite-plugin-svelte": "^6.2.4",
|
|
67
|
+
"@types/adm-zip": "^0.5.7",
|
|
66
68
|
"@types/bcryptjs": "^2.4.6",
|
|
67
69
|
"@types/dompurify": "^3.0.5",
|
|
68
70
|
"@types/node": "^25.2.0",
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compaction_warning": "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.",
|
|
3
|
+
"reply_instructions": "To reply to this message, use: volute send ${channel} \"your message\"",
|
|
4
|
+
"channel_invite": "[Channel Invite]\n${headers}\n\n[${sender} — ${time}]\n${preview}\n\nFurther messages will be saved to ${filePath}\n\nTo accept, add to .config/routes.json:\n Rule: { \"channel\": \"${channel}\", \"session\": \"${suggestedSession}\" }\n${batchRecommendation}To respond, use: volute send ${channel} \"your message\"\nTo reject, delete ${filePath}"
|
|
5
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
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 mind server. Also covers routing config, batch settings, channel gating, and
|
|
3
|
+
description: This skill should be used when working with the volute CLI, understanding variants, forking, merging, or managing the mind server. Also covers routing config, batch settings, channel gating, message flow, and shared skills. Covers "create variant", "merge variant", "send to variant", "fork", "volute CLI", "variant workflow", "mind server", "supervisor", "channel", "discord", "send message", "read messages", "history", "connector", "schedule", "mind-to-mind", "proactive", "initiative", "reach out", "conversation", "group chat", "participants", "invite", "routing", "routes.json", "batch", "debounce", "trigger", "gating", "gate", "skill", "shared skill", "install skill", "publish skill", "update skill".
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Self-Management
|
|
@@ -100,6 +100,22 @@ After a merge, you receive orientation context about what changed. Update your m
|
|
|
100
100
|
|
|
101
101
|
Create skills by writing `.claude/skills/<name>/SKILL.md` files in your `home/` directory. These are automatically available in your sessions.
|
|
102
102
|
|
|
103
|
+
## Shared Skills
|
|
104
|
+
|
|
105
|
+
Your system has a shared skill repository that all minds can browse and install from.
|
|
106
|
+
|
|
107
|
+
| Command | Purpose |
|
|
108
|
+
|---------|---------|
|
|
109
|
+
| `volute skill list` | List shared skills available to install |
|
|
110
|
+
| `volute skill list --mind` | List your installed skills with update status |
|
|
111
|
+
| `volute skill install <name>` | Install a shared skill |
|
|
112
|
+
| `volute skill update <name>` | Update an installed skill (3-way merge preserves your changes) |
|
|
113
|
+
| `volute skill update --all` | Update all installed skills |
|
|
114
|
+
| `volute skill publish <name>` | Publish one of your skills to the shared repository |
|
|
115
|
+
| `volute skill uninstall <name>` | Remove an installed skill |
|
|
116
|
+
|
|
117
|
+
When you install a skill, it's copied to your skills directory. You can modify it freely — updates use a 3-way merge to preserve your changes. If there are merge conflicts, resolve them like any git conflict.
|
|
118
|
+
|
|
103
119
|
## MCP Configuration
|
|
104
120
|
|
|
105
121
|
Edit `home/.mcp.json` to configure MCP servers for your SDK session. This gives you access to additional tools and services.
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
resolveRoute,
|
|
7
7
|
resolveSessionConfig,
|
|
8
8
|
} from "./routing.js";
|
|
9
|
+
import { loadPrompts } from "./startup.js";
|
|
9
10
|
import type { ChannelMeta, HandlerResolver, Listener, VoluteContentPart } from "./types.js";
|
|
10
11
|
|
|
11
12
|
export type Router = {
|
|
@@ -115,38 +116,39 @@ function formatInviteNotification(
|
|
|
115
116
|
messageText: string,
|
|
116
117
|
): string {
|
|
117
118
|
const time = new Date().toLocaleString();
|
|
118
|
-
const
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
if (meta.
|
|
122
|
-
if (meta.
|
|
123
|
-
if (meta.
|
|
119
|
+
const prompts = loadPrompts();
|
|
120
|
+
|
|
121
|
+
const headerLines: string[] = [];
|
|
122
|
+
if (meta.channel) headerLines.push(`Channel: ${meta.channel}`);
|
|
123
|
+
if (meta.sender) headerLines.push(`Sender: ${meta.sender}`);
|
|
124
|
+
if (meta.platform) headerLines.push(`Platform: ${meta.platform}`);
|
|
125
|
+
if (meta.serverName) headerLines.push(`Server: ${meta.serverName}`);
|
|
126
|
+
if (meta.channelName) headerLines.push(`Channel name: ${meta.channelName}`);
|
|
124
127
|
if (meta.participants && meta.participants.length > 0)
|
|
125
|
-
|
|
126
|
-
|
|
128
|
+
headerLines.push(`Participants: ${meta.participants.join(", ")}`);
|
|
129
|
+
|
|
127
130
|
const preview = messageText.length > 200 ? `${messageText.slice(0, 200)}...` : messageText;
|
|
128
|
-
lines.push(`[${meta.sender ?? "unknown"} — ${time}]`);
|
|
129
|
-
lines.push(preview);
|
|
130
|
-
lines.push("");
|
|
131
|
-
lines.push(`Further messages will be saved to ${filePath}`);
|
|
132
|
-
lines.push("");
|
|
133
|
-
lines.push("To accept, add to .config/routes.json:");
|
|
134
131
|
const suggestedSession = sanitizeChannelPath(meta.channel ?? "unknown");
|
|
132
|
+
const channel = meta.channel ?? "unknown";
|
|
135
133
|
const otherCount = (meta.participantCount ?? 1) - 1;
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
134
|
+
const batchRecommendation =
|
|
135
|
+
otherCount > 1
|
|
136
|
+
? ` Session config: "${suggestedSession}": { "batch": { "debounce": 20, "maxWait": 120 } }\n(batch recommended — ${otherCount} other participants may generate frequent messages)\n`
|
|
137
|
+
: "";
|
|
138
|
+
|
|
139
|
+
const vars: Record<string, string> = {
|
|
140
|
+
headers: headerLines.join("\n"),
|
|
141
|
+
sender: meta.sender ?? "unknown",
|
|
142
|
+
time,
|
|
143
|
+
preview,
|
|
144
|
+
filePath,
|
|
145
|
+
channel,
|
|
146
|
+
suggestedSession,
|
|
147
|
+
batchRecommendation,
|
|
148
|
+
};
|
|
149
|
+
return prompts.channel_invite.replace(/\$\{(\w+)\}/g, (match, name) =>
|
|
150
|
+
name in vars ? vars[name] : match,
|
|
151
|
+
);
|
|
150
152
|
}
|
|
151
153
|
|
|
152
154
|
export function createRouter(options: {
|
|
@@ -304,6 +306,21 @@ export function createRouter(options: {
|
|
|
304
306
|
return { messageId, unsubscribe: noop };
|
|
305
307
|
}
|
|
306
308
|
|
|
309
|
+
// Mention-mode filtering: skip messages that don't mention this mind
|
|
310
|
+
if (resolved.destination === "mind" && resolved.mode === "mention") {
|
|
311
|
+
const mindName = process.env.VOLUTE_MIND;
|
|
312
|
+
if (mindName) {
|
|
313
|
+
const escaped = mindName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
314
|
+
const pattern = new RegExp(`\\b${escaped}\\b`, "i");
|
|
315
|
+
if (!pattern.test(text)) {
|
|
316
|
+
queueMicrotask(() => safeListener({ type: "done", messageId }));
|
|
317
|
+
return { messageId, unsubscribe: noop };
|
|
318
|
+
}
|
|
319
|
+
} else {
|
|
320
|
+
log("router", "VOLUTE_MIND not set — mention filtering disabled");
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
307
324
|
// File destination
|
|
308
325
|
if (resolved.destination === "file") {
|
|
309
326
|
if (options.fileHandler) {
|
|
@@ -15,6 +15,7 @@ export type RoutingRule = {
|
|
|
15
15
|
sender?: string;
|
|
16
16
|
isDM?: boolean; // match on isDM metadata
|
|
17
17
|
participants?: number; // match on participant count (e.g. 2 = DM)
|
|
18
|
+
mode?: "all" | "mention"; // "mention" = only process if mind name appears in message
|
|
18
19
|
};
|
|
19
20
|
|
|
20
21
|
export type SessionConfig = {
|
|
@@ -41,6 +42,7 @@ export type ResolvedRoute =
|
|
|
41
42
|
destination: "mind";
|
|
42
43
|
session: string;
|
|
43
44
|
matched: boolean;
|
|
45
|
+
mode?: "all" | "mention";
|
|
44
46
|
}
|
|
45
47
|
| { destination: "file"; path: string; matched: boolean };
|
|
46
48
|
|
|
@@ -75,7 +77,7 @@ function globMatch(pattern: string, value: string): boolean {
|
|
|
75
77
|
}
|
|
76
78
|
|
|
77
79
|
const GLOB_MATCH_KEYS = new Set(["channel", "sender"]);
|
|
78
|
-
const NON_MATCH_KEYS = new Set(["session", "destination", "path"]);
|
|
80
|
+
const NON_MATCH_KEYS = new Set(["session", "destination", "path", "mode"]);
|
|
79
81
|
|
|
80
82
|
type MatchMeta = { channel?: string; sender?: string; isDM?: boolean; participantCount?: number };
|
|
81
83
|
|
|
@@ -135,6 +137,7 @@ export function resolveRoute(config: RoutingConfig, meta: MatchMeta): ResolvedRo
|
|
|
135
137
|
destination: "mind",
|
|
136
138
|
session: sanitizeSessionName(expandTemplate(rule.session ?? fallback, meta)),
|
|
137
139
|
matched: true,
|
|
140
|
+
mode: rule.mode,
|
|
138
141
|
};
|
|
139
142
|
}
|
|
140
143
|
}
|
|
@@ -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");
|
|
@@ -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
|
};
|
|
@@ -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";
|
|
@@ -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
|
};
|