volute 0.21.0 → 0.23.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/api.d.ts +4294 -0
- package/dist/chunk-G5KRTU2F.js +76 -0
- package/dist/chunk-ISWZ6QUK.js +2691 -0
- package/dist/{chunk-J5A3DF2U.js → chunk-JG4CCJOA.js} +1 -1
- package/dist/{chunk-IPJXU366.js → chunk-JTDFJWI2.js} +1 -0
- package/dist/{chunk-7LPTHFIL.js → chunk-M5CNKH4J.js} +55 -5
- package/dist/{chunk-L3LHXZD7.js → chunk-PHHKNGA3.js} +1 -1
- package/dist/{chunk-Q7AITQ44.js → chunk-QIXPN3OO.js} +1 -1
- package/dist/{chunk-PC6R6UUW.js → chunk-RK627D57.js} +36 -59
- package/dist/{chunk-5462YKWP.js → chunk-TFS25FIM.js} +1 -1
- package/dist/{chunk-QUJUKM4U.js → chunk-VT5QODNE.js} +1 -1
- package/dist/chunk-XLC342FO.js +29 -0
- package/dist/cli.js +10 -10
- package/dist/cloud-sync-PI47U2LT.js +96 -0
- package/dist/{daemon-restart-BH67ZOTE.js → daemon-restart-RMGOOGPE.js} +4 -4
- package/dist/daemon.js +1216 -1822
- package/dist/{down-LIOQ5JDH.js → down-WSUASL5E.js} +3 -3
- package/dist/{import-E433B4KG.js → import-EAXTHHXL.js} +2 -1
- package/dist/message-delivery-FHV4NO2F.js +23 -0
- package/dist/{mind-BIDOF65R.js → mind-BTXR5B3C.js} +13 -5
- package/dist/{mind-manager-3V2NXX4I.js → mind-manager-KMY4GA2J.js} +1 -1
- package/dist/mind-sleep-FWRBIFBS.js +41 -0
- package/dist/mind-wake-LJK2YU5X.js +36 -0
- package/dist/{package-HQR52XSG.js → package-CUBJ4PKS.js} +10 -1
- package/dist/{pages-KQBR5TAZ.js → pages-YSTRWJR4.js} +1 -1
- package/dist/{publish-OJ4QMXVZ.js → publish-BZNHKUUK.js} +2 -2
- package/dist/{service-TVNEORO7.js → service-7BFXDI6J.js} +4 -4
- package/dist/{setup-OZDYCKDI.js → setup-SSIIXQMI.js} +2 -2
- package/dist/sleep-manager-2TMQ65E4.js +27 -0
- package/dist/{sprout-6Z6C42YM.js → sprout-UKCYBGHK.js} +2 -2
- package/dist/{status-Z7NAFMBI.js → status-H2MKDN6L.js} +2 -2
- package/dist/{up-7BGDMFRT.js → up-Z5JRG2M2.js} +3 -3
- package/dist/{update-4WT7VWHW.js → update-ELC6MEUT.js} +2 -2
- package/dist/{upgrade-ZEC2GGFO.js → upgrade-GXW2EQY3.js} +11 -2
- package/dist/{version-notify-TFS2U5CF.js → version-notify-LKABEJSA.js} +11 -3
- package/dist/web-assets/assets/index-CZ26vsyY.js +69 -0
- package/dist/web-assets/assets/index-DyyAvJwW.css +1 -0
- package/dist/web-assets/index.html +2 -2
- package/package.json +10 -1
- package/templates/_base/.init/.config/prompts.json +1 -0
- package/templates/_base/home/.config/config.json.tmpl +4 -1
- package/templates/_base/src/lib/file-handler.ts +6 -1
- package/templates/_base/src/lib/logger.ts +68 -23
- package/templates/_base/src/lib/startup.ts +12 -3
- package/templates/claude/src/agent.ts +150 -29
- package/templates/claude/src/lib/hooks/pre-compact.ts +18 -4
- package/templates/claude/src/lib/message-channel.ts +6 -0
- package/templates/claude/src/lib/stream-consumer.ts +17 -1
- package/templates/claude/src/server.ts +3 -1
- package/templates/pi/home/.config/config.json.tmpl +4 -1
- package/templates/pi/src/agent.ts +87 -0
- package/templates/pi/src/lib/content.ts +18 -3
- package/templates/pi/src/lib/event-handler.ts +22 -2
- package/templates/pi/src/server.ts +3 -1
- package/dist/chunk-OGZYB5GL.js +0 -847
- package/dist/web-assets/assets/index-BR3gtK3E.css +0 -1
- package/dist/web-assets/assets/index-CWmrZRQd.js +0 -64
- /package/dist/{shared-DCQ2UXOM.js → shared-2OGT3NSL.js} +0 -0
|
@@ -1,14 +1,28 @@
|
|
|
1
|
-
import type { HookCallback } from "@anthropic-ai/claude-agent-sdk";
|
|
1
|
+
import type { HookCallback, PreCompactHookInput } from "@anthropic-ai/claude-agent-sdk";
|
|
2
2
|
import { log } from "../logger.js";
|
|
3
3
|
|
|
4
4
|
export function createPreCompactHook(onCompact: () => void) {
|
|
5
5
|
let compactBlocked = false;
|
|
6
6
|
|
|
7
|
-
const hook: HookCallback = async () => {
|
|
7
|
+
const hook: HookCallback = async (input) => {
|
|
8
|
+
const { trigger, custom_instructions } = input as PreCompactHookInput;
|
|
9
|
+
|
|
10
|
+
// Our custom compaction (via /compact with instructions) — allow through without the two-pass block
|
|
11
|
+
if (trigger === "manual" && custom_instructions) {
|
|
12
|
+
log("mind", "allowing manual compaction with custom instructions");
|
|
13
|
+
return {};
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Auto-compaction: two-pass block (first pass warns mind, second pass allows)
|
|
8
17
|
if (!compactBlocked) {
|
|
9
|
-
compactBlocked = true;
|
|
10
18
|
log("mind", "blocking compaction — asking mind to update daily log first");
|
|
11
|
-
|
|
19
|
+
try {
|
|
20
|
+
onCompact();
|
|
21
|
+
compactBlocked = true;
|
|
22
|
+
} catch (err) {
|
|
23
|
+
log("mind", "onCompact callback failed, allowing compaction:", err);
|
|
24
|
+
return {};
|
|
25
|
+
}
|
|
12
26
|
return { decision: "block" };
|
|
13
27
|
}
|
|
14
28
|
compactBlocked = false;
|
|
@@ -2,6 +2,7 @@ import type { SDKUserMessage } from "@anthropic-ai/claude-agent-sdk";
|
|
|
2
2
|
|
|
3
3
|
export type MessageChannel = {
|
|
4
4
|
push: (msg: SDKUserMessage) => void;
|
|
5
|
+
drain: () => SDKUserMessage[];
|
|
5
6
|
iterable: AsyncIterable<SDKUserMessage>;
|
|
6
7
|
};
|
|
7
8
|
|
|
@@ -19,6 +20,11 @@ export function createMessageChannel(): MessageChannel {
|
|
|
19
20
|
queue.push(msg);
|
|
20
21
|
}
|
|
21
22
|
},
|
|
23
|
+
drain() {
|
|
24
|
+
// Clear any pending iterator wait so it doesn't consume a message after drain
|
|
25
|
+
resolve = null;
|
|
26
|
+
return queue.splice(0);
|
|
27
|
+
},
|
|
22
28
|
iterable: {
|
|
23
29
|
[Symbol.asyncIterator]() {
|
|
24
30
|
return {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { query } from "@anthropic-ai/claude-agent-sdk";
|
|
2
2
|
import { daemonEmit, type EventType } from "./daemon-client.js";
|
|
3
|
-
import { log, logText, logThinking, logToolUse } from "./logger.js";
|
|
3
|
+
import { log, logText, logThinking, logToolUse, warn } from "./logger.js";
|
|
4
4
|
import { filterEvent, loadTransparencyPreset } from "./transparency.js";
|
|
5
5
|
import type { VoluteEvent } from "./types.js";
|
|
6
6
|
|
|
@@ -15,6 +15,7 @@ export type StreamCallbacks = {
|
|
|
15
15
|
onSessionId?: (sessionId: string) => void;
|
|
16
16
|
broadcast: (event: VoluteEvent) => void;
|
|
17
17
|
onTurnEnd?: () => void;
|
|
18
|
+
onContextTokens?: (tokens: number) => void;
|
|
18
19
|
};
|
|
19
20
|
|
|
20
21
|
// Loaded once at startup — mind restarts on config changes
|
|
@@ -50,6 +51,12 @@ export async function consumeStream(
|
|
|
50
51
|
callbacks.onSessionId?.(msg.session_id as string);
|
|
51
52
|
}
|
|
52
53
|
if (msg.type === "assistant") {
|
|
54
|
+
const usage = msg.message.usage as unknown as Record<string, unknown> | undefined;
|
|
55
|
+
const inputTokens = (usage?.input_tokens as number) ?? 0;
|
|
56
|
+
const cacheCreation = (usage?.cache_creation_input_tokens as number) ?? 0;
|
|
57
|
+
const cacheRead = (usage?.cache_read_input_tokens as number) ?? 0;
|
|
58
|
+
const contextTokens = inputTokens + cacheCreation + cacheRead;
|
|
59
|
+
if (contextTokens) callbacks.onContextTokens?.(contextTokens);
|
|
53
60
|
for (const b of msg.message.content) {
|
|
54
61
|
if (b.type === "thinking" && "thinking" in b && b.thinking) {
|
|
55
62
|
const text = b.thinking as string;
|
|
@@ -75,6 +82,15 @@ export async function consumeStream(
|
|
|
75
82
|
session.messageChannels.delete(session.currentMessageId);
|
|
76
83
|
}
|
|
77
84
|
log("mind", `session "${session.name}": turn done`);
|
|
85
|
+
// Log any error messages from the result
|
|
86
|
+
const resultMsg = msg as Record<string, unknown>;
|
|
87
|
+
if (Array.isArray(resultMsg.messages)) {
|
|
88
|
+
for (const m of resultMsg.messages) {
|
|
89
|
+
if (m && typeof m === "object" && "errorMessage" in m && m.errorMessage) {
|
|
90
|
+
warn("mind", `session "${session.name}": agent error: ${m.errorMessage}`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
78
94
|
const result = msg as { usage?: { input_tokens?: number; output_tokens?: number } };
|
|
79
95
|
if (result.usage) {
|
|
80
96
|
const usage = {
|
|
@@ -3,7 +3,7 @@ import { resolve } from "node:path";
|
|
|
3
3
|
import { createMind } from "./agent.js";
|
|
4
4
|
import { daemonRestart } from "./lib/daemon-client.js";
|
|
5
5
|
import { createFileHandlerResolver } from "./lib/file-handler.js";
|
|
6
|
-
import { log } from "./lib/logger.js";
|
|
6
|
+
import { log, setLevel } from "./lib/logger.js";
|
|
7
7
|
import { createRouter } from "./lib/router.js";
|
|
8
8
|
import {
|
|
9
9
|
loadConfig,
|
|
@@ -16,6 +16,7 @@ import { createVoluteServer } from "./lib/volute-server.js";
|
|
|
16
16
|
|
|
17
17
|
const { port } = parseArgs();
|
|
18
18
|
const config = loadConfig();
|
|
19
|
+
if (config.logLevel) setLevel(config.logLevel);
|
|
19
20
|
if (config.model) log("server", `using model: ${config.model}`);
|
|
20
21
|
if (config.maxThinkingTokens) log("server", `max thinking tokens: ${config.maxThinkingTokens}`);
|
|
21
22
|
|
|
@@ -40,6 +41,7 @@ const mind = createMind({
|
|
|
40
41
|
maxThinkingTokens: config.maxThinkingTokens,
|
|
41
42
|
sessionsDir,
|
|
42
43
|
compactionMessage: config.compactionMessage,
|
|
44
|
+
maxContextTokens: config.compaction?.maxContextTokens,
|
|
43
45
|
onIdentityReload: async () => {
|
|
44
46
|
log("server", "identity file changed — restarting to reload");
|
|
45
47
|
await mind.waitForCommits();
|
|
@@ -43,12 +43,19 @@ export function createMind(options: {
|
|
|
43
43
|
model?: string;
|
|
44
44
|
thinkingLevel?: "off" | "minimal" | "low" | "medium" | "high" | "xhigh";
|
|
45
45
|
compactionMessage?: string;
|
|
46
|
+
maxContextTokens?: number;
|
|
46
47
|
}): { resolve: HandlerResolver } {
|
|
47
48
|
const sessions = new Map<string, PiSession>();
|
|
48
49
|
const prompts = loadPrompts();
|
|
49
50
|
const today = new Date().toLocaleDateString("en-CA");
|
|
50
51
|
const compactionMessage =
|
|
51
52
|
options.compactionMessage ?? prompts.compaction_warning.replace("${date}", today);
|
|
53
|
+
const compactionInstructions = prompts.compaction_instructions;
|
|
54
|
+
const maxContextTokens = options.maxContextTokens;
|
|
55
|
+
|
|
56
|
+
if (maxContextTokens) {
|
|
57
|
+
log("mind", `compaction threshold: ${maxContextTokens} tokens`);
|
|
58
|
+
}
|
|
52
59
|
|
|
53
60
|
// Shared setup (created once)
|
|
54
61
|
const modelStr = options.model || process.env.PI_MODEL || "anthropic:claude-sonnet-4-20250514";
|
|
@@ -88,9 +95,35 @@ export function createMind(options: {
|
|
|
88
95
|
|
|
89
96
|
log("mind", `session "${session.name}": ${isEphemeral ? "ephemeral" : "persistent"}`);
|
|
90
97
|
|
|
98
|
+
// Compaction state machine:
|
|
99
|
+
// 1. onContextTokens sets compactionTriggered=true and sends warning
|
|
100
|
+
// 2. onTurnEnd (after warning turn): compactionTriggered -> compactOnNextTurnEnd
|
|
101
|
+
// 3. onTurnEnd (after mind's save turn): compactOnNextTurnEnd -> call compact()
|
|
91
102
|
let compactBlocked = false;
|
|
103
|
+
let manualCompactPending = false;
|
|
104
|
+
let compactionTriggered = false;
|
|
105
|
+
let compactOnNextTurnEnd = false;
|
|
106
|
+
let compactionInProgress = false;
|
|
107
|
+
|
|
108
|
+
function resetCompactionState() {
|
|
109
|
+
compactionTriggered = false;
|
|
110
|
+
compactOnNextTurnEnd = false;
|
|
111
|
+
compactionInProgress = false;
|
|
112
|
+
}
|
|
113
|
+
|
|
92
114
|
const preCompactExtension: ExtensionFactory = (pi) => {
|
|
93
115
|
pi.on("session_before_compact", () => {
|
|
116
|
+
// Our programmatic compact() call (triggered by token threshold) — allow through
|
|
117
|
+
if (manualCompactPending) {
|
|
118
|
+
manualCompactPending = false;
|
|
119
|
+
log(
|
|
120
|
+
"mind",
|
|
121
|
+
`session "${session.name}": allowing manual compaction with custom instructions`,
|
|
122
|
+
);
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Auto-compaction: two-pass block (first pass warns mind, second pass allows)
|
|
94
127
|
if (!compactBlocked) {
|
|
95
128
|
compactBlocked = true;
|
|
96
129
|
log(
|
|
@@ -146,6 +179,60 @@ export function createMind(options: {
|
|
|
146
179
|
createEventHandler(session, {
|
|
147
180
|
cwd: options.cwd,
|
|
148
181
|
broadcast: (event) => broadcast(session, event),
|
|
182
|
+
onContextTokens: maxContextTokens
|
|
183
|
+
? (tokens: number) => {
|
|
184
|
+
if (tokens >= maxContextTokens && !compactionTriggered && !compactionInProgress) {
|
|
185
|
+
if (!session.agentSession) {
|
|
186
|
+
log(
|
|
187
|
+
"mind",
|
|
188
|
+
`session "${session.name}": compaction threshold hit but session not ready`,
|
|
189
|
+
);
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
compactionTriggered = true;
|
|
193
|
+
log(
|
|
194
|
+
"mind",
|
|
195
|
+
`session "${session.name}": ${tokens} tokens >= ${maxContextTokens} — triggering compaction`,
|
|
196
|
+
);
|
|
197
|
+
// Send compaction warning; compaction will follow after the mind finishes its response turn
|
|
198
|
+
session.messageIds.push(undefined);
|
|
199
|
+
session.agentSession.prompt(compactionMessage, {
|
|
200
|
+
streamingBehavior: "followUp",
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
: undefined,
|
|
205
|
+
onTurnEnd: maxContextTokens
|
|
206
|
+
? () => {
|
|
207
|
+
try {
|
|
208
|
+
// Compact on the turn AFTER the warning was sent (so the mind gets a turn to save state)
|
|
209
|
+
if (compactOnNextTurnEnd) {
|
|
210
|
+
compactOnNextTurnEnd = false;
|
|
211
|
+
manualCompactPending = true;
|
|
212
|
+
compactionInProgress = true;
|
|
213
|
+
log("mind", `session "${session.name}": compacting with custom instructions`);
|
|
214
|
+
Promise.resolve(session.agentSession?.compact(compactionInstructions))
|
|
215
|
+
.catch((err) =>
|
|
216
|
+
log("mind", `session "${session.name}": compact() failed:`, err),
|
|
217
|
+
)
|
|
218
|
+
.finally(() => {
|
|
219
|
+
compactionInProgress = false;
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
if (compactionTriggered) {
|
|
223
|
+
compactionTriggered = false;
|
|
224
|
+
compactOnNextTurnEnd = true;
|
|
225
|
+
}
|
|
226
|
+
} catch (err) {
|
|
227
|
+
log(
|
|
228
|
+
"mind",
|
|
229
|
+
`session "${session.name}": onTurnEnd error, resetting compaction state:`,
|
|
230
|
+
err,
|
|
231
|
+
);
|
|
232
|
+
resetCompactionState();
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
: undefined,
|
|
149
236
|
}),
|
|
150
237
|
);
|
|
151
238
|
|
|
@@ -1,14 +1,29 @@
|
|
|
1
1
|
import type { ImageContent } from "@mariozechner/pi-ai";
|
|
2
|
-
import
|
|
2
|
+
import { warn } from "./logger.js";
|
|
3
3
|
|
|
4
|
-
export function extractText(content:
|
|
4
|
+
export function extractText(content: unknown): string {
|
|
5
|
+
if (typeof content === "string") return content;
|
|
6
|
+
if (!Array.isArray(content)) {
|
|
7
|
+
warn(
|
|
8
|
+
"mind",
|
|
9
|
+
`extractText received unexpected ${typeof content} instead of VoluteContentPart[]`,
|
|
10
|
+
);
|
|
11
|
+
return JSON.stringify(content);
|
|
12
|
+
}
|
|
5
13
|
return content
|
|
6
14
|
.filter((p): p is { type: "text"; text: string } => p.type === "text")
|
|
7
15
|
.map((p) => p.text)
|
|
8
16
|
.join("\n");
|
|
9
17
|
}
|
|
10
18
|
|
|
11
|
-
export function extractImages(content:
|
|
19
|
+
export function extractImages(content: unknown): ImageContent[] {
|
|
20
|
+
if (!Array.isArray(content)) {
|
|
21
|
+
warn(
|
|
22
|
+
"mind",
|
|
23
|
+
`extractImages received non-array content (${typeof content}) — images cannot be extracted`,
|
|
24
|
+
);
|
|
25
|
+
return [];
|
|
26
|
+
}
|
|
12
27
|
return content
|
|
13
28
|
.filter((p): p is { type: "image"; media_type: string; data: string } => p.type === "image")
|
|
14
29
|
.map((p) => ({ type: "image" as const, mimeType: p.media_type, data: p.data }));
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { commitFileChange } from "./auto-commit.js";
|
|
2
2
|
import { daemonEmit, type EventType } from "./daemon-client.js";
|
|
3
|
-
import { log, logText, logThinking, logToolResult, logToolUse } from "./logger.js";
|
|
3
|
+
import { log, logText, logThinking, logToolResult, logToolUse, warn } from "./logger.js";
|
|
4
4
|
import { filterEvent, loadTransparencyPreset } from "./transparency.js";
|
|
5
5
|
import type { VoluteEvent } from "./types.js";
|
|
6
6
|
|
|
@@ -14,6 +14,8 @@ export type EventSession = {
|
|
|
14
14
|
export type EventHandlerOptions = {
|
|
15
15
|
cwd: string;
|
|
16
16
|
broadcast: (event: VoluteEvent) => void;
|
|
17
|
+
onContextTokens?: (tokens: number) => void;
|
|
18
|
+
onTurnEnd?: () => void;
|
|
17
19
|
};
|
|
18
20
|
|
|
19
21
|
// Loaded once at startup — mind restarts on config changes
|
|
@@ -133,14 +135,28 @@ export function createEventHandler(session: EventSession, options: EventHandlerO
|
|
|
133
135
|
session.messageChannels.delete(session.currentMessageId);
|
|
134
136
|
}
|
|
135
137
|
log("mind", `session "${session.name}": turn done`);
|
|
136
|
-
//
|
|
138
|
+
// Log any error messages from the agent
|
|
139
|
+
if (event.messages) {
|
|
140
|
+
for (const msg of event.messages as any[]) {
|
|
141
|
+
if (msg.errorMessage) {
|
|
142
|
+
warn("mind", `session "${session.name}": agent error: ${msg.errorMessage}`);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
// Sum usage from assistant messages. The last assistant message's input tokens
|
|
147
|
+
// approximate current context size (it includes the full conversation up to that point).
|
|
137
148
|
if (event.messages) {
|
|
138
149
|
let inputTokens = 0;
|
|
139
150
|
let outputTokens = 0;
|
|
151
|
+
let lastInputTokens = 0;
|
|
140
152
|
for (const msg of event.messages as any[]) {
|
|
141
153
|
if (msg.role === "assistant" && msg.usage) {
|
|
142
154
|
inputTokens += msg.usage.input ?? 0;
|
|
143
155
|
outputTokens += msg.usage.output ?? 0;
|
|
156
|
+
const cacheWrite = msg.usage.cacheWrite ?? msg.usage.cache_creation ?? 0;
|
|
157
|
+
const cacheRead = msg.usage.cacheRead ?? msg.usage.cache_read ?? 0;
|
|
158
|
+
const contextTokens = (msg.usage.input ?? 0) + cacheWrite + cacheRead;
|
|
159
|
+
if (contextTokens) lastInputTokens = contextTokens;
|
|
144
160
|
}
|
|
145
161
|
}
|
|
146
162
|
if (inputTokens > 0 || outputTokens > 0) {
|
|
@@ -148,10 +164,14 @@ export function createEventHandler(session: EventSession, options: EventHandlerO
|
|
|
148
164
|
options.broadcast({ type: "usage", ...usage });
|
|
149
165
|
emit(session, { type: "usage", metadata: usage });
|
|
150
166
|
}
|
|
167
|
+
if (lastInputTokens > 0) {
|
|
168
|
+
options.onContextTokens?.(lastInputTokens);
|
|
169
|
+
}
|
|
151
170
|
}
|
|
152
171
|
options.broadcast({ type: "done" });
|
|
153
172
|
emit(session, { type: "done" });
|
|
154
173
|
session.currentMessageId = undefined;
|
|
174
|
+
options.onTurnEnd?.();
|
|
155
175
|
}
|
|
156
176
|
} catch (err) {
|
|
157
177
|
log("mind", `session "${session.name}": event handler error (${event?.type}):`, err);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { resolve } from "node:path";
|
|
2
2
|
import { createMind } from "./agent.js";
|
|
3
3
|
import { createFileHandlerResolver } from "./lib/file-handler.js";
|
|
4
|
-
import { log } from "./lib/logger.js";
|
|
4
|
+
import { log, setLevel } from "./lib/logger.js";
|
|
5
5
|
import { createRouter } from "./lib/router.js";
|
|
6
6
|
import {
|
|
7
7
|
handleStartupContext,
|
|
@@ -15,6 +15,7 @@ import { createVoluteServer } from "./lib/volute-server.js";
|
|
|
15
15
|
|
|
16
16
|
const { port } = parseArgs();
|
|
17
17
|
const config = loadConfig();
|
|
18
|
+
if (config.logLevel) setLevel(config.logLevel);
|
|
18
19
|
if (config.model) log("server", `using model: ${config.model}`);
|
|
19
20
|
if (config.thinkingLevel) log("server", `thinking level: ${config.thinkingLevel}`);
|
|
20
21
|
|
|
@@ -29,6 +30,7 @@ const mind = createMind({
|
|
|
29
30
|
model: config.model,
|
|
30
31
|
thinkingLevel: config.thinkingLevel,
|
|
31
32
|
compactionMessage: config.compactionMessage,
|
|
33
|
+
maxContextTokens: config.compaction?.maxContextTokens,
|
|
32
34
|
});
|
|
33
35
|
|
|
34
36
|
const router = createRouter({
|