@tractorscorch/clank 1.7.1 → 1.7.3
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/CHANGELOG.md +27 -0
- package/README.md +19 -2
- package/dist/index.js +252 -33
- package/dist/index.js.map +1 -1
- package/dist/workspace/templates/RUNNER.md +45 -0
- package/dist/workspace/templates/SOUL.md +1 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -320,6 +320,30 @@ ${summary.trim()}`,
|
|
|
320
320
|
this.compactTier1Aggressive();
|
|
321
321
|
}
|
|
322
322
|
}
|
|
323
|
+
/** Check if context is critically full (>95% utilization) */
|
|
324
|
+
isOverflowing() {
|
|
325
|
+
return this.utilizationPercent() >= 95;
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* Tier-1 only compaction — synchronous, no LLM call.
|
|
329
|
+
* Safe to call mid-turn without adding latency.
|
|
330
|
+
*/
|
|
331
|
+
compactTier1Only() {
|
|
332
|
+
const before = this.messages.length;
|
|
333
|
+
const tokensBefore = this.estimateTokens();
|
|
334
|
+
this.compactTier1();
|
|
335
|
+
if (this.utilizationPercent() >= 70) {
|
|
336
|
+
this.compactTier1Aggressive();
|
|
337
|
+
}
|
|
338
|
+
return {
|
|
339
|
+
ok: true,
|
|
340
|
+
tier: 1,
|
|
341
|
+
messagesBefore: before,
|
|
342
|
+
messagesAfter: this.messages.length,
|
|
343
|
+
estimatedTokensBefore: tokensBefore,
|
|
344
|
+
estimatedTokensAfter: this.estimateTokens()
|
|
345
|
+
};
|
|
346
|
+
}
|
|
323
347
|
/** Clear all messages */
|
|
324
348
|
clear() {
|
|
325
349
|
this.messages = [];
|
|
@@ -988,6 +1012,9 @@ ${results}`
|
|
|
988
1012
|
outputTokens = 0;
|
|
989
1013
|
}
|
|
990
1014
|
try {
|
|
1015
|
+
if (this.contextEngine.utilizationPercent() > 90) {
|
|
1016
|
+
this.contextEngine.compactTier1Only();
|
|
1017
|
+
}
|
|
991
1018
|
const streamIterator = activeProvider.stream(
|
|
992
1019
|
this.contextEngine.getMessages(),
|
|
993
1020
|
this.systemPrompt,
|
|
@@ -1024,6 +1051,21 @@ ${results}`
|
|
|
1024
1051
|
} catch (streamErr) {
|
|
1025
1052
|
const errMsg = streamErr instanceof Error ? streamErr.message : "unknown";
|
|
1026
1053
|
const isTimeout = streamErr instanceof Error && (streamErr.name === "TimeoutError" || streamErr.name === "AbortError" || errMsg.includes("timed out"));
|
|
1054
|
+
const isContextError = /context.*(length|limit|exceeded)|too many tokens|maximum.*tokens|token limit/i.test(errMsg);
|
|
1055
|
+
if (attempt === 0 && isContextError && !signal.aborted) {
|
|
1056
|
+
this.emit("error", {
|
|
1057
|
+
message: "Context limit hit \u2014 compacting and retrying...",
|
|
1058
|
+
recoverable: true
|
|
1059
|
+
});
|
|
1060
|
+
this.contextEngine.compactTier1Only();
|
|
1061
|
+
if (this.contextEngine.isOverflowing()) {
|
|
1062
|
+
this.emit("error", {
|
|
1063
|
+
message: "Context is nearly full \u2014 use /compact or /new to free space",
|
|
1064
|
+
recoverable: true
|
|
1065
|
+
});
|
|
1066
|
+
}
|
|
1067
|
+
continue;
|
|
1068
|
+
}
|
|
1027
1069
|
const isRetryable = !isTimeout && !signal.aborted && (errMsg.includes("connection dropped") || errMsg.includes("stopped responding") || errMsg.includes("empty response") || errMsg.includes("fetch failed") || errMsg.includes("ECONNREFUSED") || errMsg.includes("ECONNRESET"));
|
|
1028
1070
|
if (attempt === 0 && (isRetryable || !signal.aborted && !isTimeout)) {
|
|
1029
1071
|
this.emit("error", {
|
|
@@ -1129,6 +1171,15 @@ ${results}`
|
|
|
1129
1171
|
this.contextEngine.ingest(result);
|
|
1130
1172
|
this.emit("tool-result", { id: tc.id, name: tc.name, success: false, summary: errMsg });
|
|
1131
1173
|
}
|
|
1174
|
+
if (this.contextEngine.needsCompaction()) {
|
|
1175
|
+
this.contextEngine.compactTier1Only();
|
|
1176
|
+
if (this.contextEngine.isOverflowing()) {
|
|
1177
|
+
this.emit("error", {
|
|
1178
|
+
message: "Context is nearly full \u2014 use /compact or /new to free space",
|
|
1179
|
+
recoverable: true
|
|
1180
|
+
});
|
|
1181
|
+
}
|
|
1182
|
+
}
|
|
1132
1183
|
}
|
|
1133
1184
|
}
|
|
1134
1185
|
if (iterationCount >= MAX_ITERATIONS) {
|
|
@@ -1175,6 +1226,74 @@ ${results}`
|
|
|
1175
1226
|
getContextEngine() {
|
|
1176
1227
|
return this.contextEngine;
|
|
1177
1228
|
}
|
|
1229
|
+
/**
|
|
1230
|
+
* Compact: summarize current state, clear context, inject summary.
|
|
1231
|
+
* Returns the summary so callers can display it.
|
|
1232
|
+
*/
|
|
1233
|
+
async compactSession() {
|
|
1234
|
+
const messages = this.contextEngine.getMessages();
|
|
1235
|
+
if (messages.length < 3) {
|
|
1236
|
+
return "Nothing to compact \u2014 session is too short.";
|
|
1237
|
+
}
|
|
1238
|
+
const conversationText = messages.slice(-30).map((m) => {
|
|
1239
|
+
const content = typeof m.content === "string" ? m.content : JSON.stringify(m.content);
|
|
1240
|
+
const truncated = content.length > 400 ? content.slice(0, 400) + "..." : content;
|
|
1241
|
+
return `${m.role}: ${truncated}`;
|
|
1242
|
+
}).join("\n\n");
|
|
1243
|
+
const summaryPrompt = [
|
|
1244
|
+
"You are summarizing a conversation for context continuity.",
|
|
1245
|
+
"The user is compacting their session \u2014 they want to clear context but continue seamlessly.",
|
|
1246
|
+
"",
|
|
1247
|
+
"Produce a concise state summary covering:",
|
|
1248
|
+
"- What task(s) the user is working on",
|
|
1249
|
+
"- Key decisions made so far",
|
|
1250
|
+
"- Files created, modified, or discussed",
|
|
1251
|
+
"- Current progress and what comes next",
|
|
1252
|
+
"- Any important context (preferences, constraints, blockers)",
|
|
1253
|
+
"",
|
|
1254
|
+
"Format as bullet points. Be brief but complete \u2014 this is the ONLY context the model will have when resuming.",
|
|
1255
|
+
"",
|
|
1256
|
+
"Conversation:",
|
|
1257
|
+
conversationText
|
|
1258
|
+
].join("\n");
|
|
1259
|
+
let summary = "";
|
|
1260
|
+
if (this.resolvedProvider) {
|
|
1261
|
+
try {
|
|
1262
|
+
for await (const event of this.resolvedProvider.provider.stream(
|
|
1263
|
+
[{ role: "user", content: summaryPrompt }],
|
|
1264
|
+
"You are a conversation summarizer. Output only the summary.",
|
|
1265
|
+
[]
|
|
1266
|
+
)) {
|
|
1267
|
+
if (event.type === "text") {
|
|
1268
|
+
summary += event.content;
|
|
1269
|
+
}
|
|
1270
|
+
}
|
|
1271
|
+
} catch {
|
|
1272
|
+
summary = messages.slice(-6).map((m) => {
|
|
1273
|
+
const content = typeof m.content === "string" ? m.content : "";
|
|
1274
|
+
return `- [${m.role}] ${content.slice(0, 150)}`;
|
|
1275
|
+
}).join("\n");
|
|
1276
|
+
}
|
|
1277
|
+
}
|
|
1278
|
+
if (!summary.trim()) {
|
|
1279
|
+
summary = "Session was active but summary generation failed. Ask the user for context.";
|
|
1280
|
+
}
|
|
1281
|
+
this.contextEngine.clear();
|
|
1282
|
+
this.contextEngine.ingest({
|
|
1283
|
+
role: "user",
|
|
1284
|
+
content: `[Session compacted \u2014 previous context summarized below]
|
|
1285
|
+
|
|
1286
|
+
${summary.trim()}
|
|
1287
|
+
|
|
1288
|
+
---
|
|
1289
|
+
The session was compacted by the user. Continue from where you left off. You have full context above.`,
|
|
1290
|
+
_compacted: true
|
|
1291
|
+
});
|
|
1292
|
+
if (this.currentSession) {
|
|
1293
|
+
await this.sessionStore.saveMessages(this.currentSession.id, this.contextEngine.getMessages());
|
|
1294
|
+
}
|
|
1295
|
+
return summary.trim();
|
|
1296
|
+
}
|
|
1178
1297
|
/** Destroy the engine and clean up */
|
|
1179
1298
|
destroy() {
|
|
1180
1299
|
this.cancel();
|
|
@@ -1197,12 +1316,22 @@ import { platform, hostname } from "os";
|
|
|
1197
1316
|
async function buildSystemPrompt(opts) {
|
|
1198
1317
|
const parts = [];
|
|
1199
1318
|
const compact = opts.compact ?? false;
|
|
1319
|
+
const isLocal = opts.isLocal ?? false;
|
|
1320
|
+
const isSubAgent = (opts.spawnDepth ?? 0) > 0;
|
|
1200
1321
|
if (!compact) {
|
|
1201
|
-
const
|
|
1322
|
+
const files = isLocal ? WORKSPACE_FILES_LOCAL : WORKSPACE_FILES;
|
|
1323
|
+
const workspaceContent = await loadWorkspaceFiles(opts.workspaceDir, files);
|
|
1202
1324
|
if (workspaceContent) {
|
|
1203
1325
|
parts.push(workspaceContent);
|
|
1204
1326
|
parts.push("---");
|
|
1205
1327
|
}
|
|
1328
|
+
if (isSubAgent) {
|
|
1329
|
+
const runnerContent = await loadSingleFile(opts.workspaceDir, SUB_AGENT_FILE);
|
|
1330
|
+
if (runnerContent) {
|
|
1331
|
+
parts.push(runnerContent);
|
|
1332
|
+
parts.push("---");
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1206
1335
|
}
|
|
1207
1336
|
if (compact) {
|
|
1208
1337
|
parts.push(`Agent: ${opts.identity.name} | Model: ${opts.identity.model.primary} | Dir: ${opts.identity.workspace}`);
|
|
@@ -1221,7 +1350,8 @@ async function buildSystemPrompt(opts) {
|
|
|
1221
1350
|
"You are an AI agent running LOCALLY on the user's machine.",
|
|
1222
1351
|
"You have tools: read_file, write_file, edit_file, list_directory, bash, search_files, glob_files, git, web_search, web_fetch, and self-config tools.",
|
|
1223
1352
|
"ALWAYS use your tools. NEVER say you cannot access files, run commands, or perform actions. You CAN \u2014 use your tools.",
|
|
1224
|
-
"NEVER apologize and refuse. If asked to do something, DO IT with your tools or explain what tool you need."
|
|
1353
|
+
"NEVER apologize and refuse. If asked to do something, DO IT with your tools or explain what tool you need.",
|
|
1354
|
+
"Do NOT modify files outside your workspace or the user's current directory unless the user explicitly names the file."
|
|
1225
1355
|
].join(" "));
|
|
1226
1356
|
} else {
|
|
1227
1357
|
parts.push("## CRITICAL: You Are a Local Agent With Tools");
|
|
@@ -1237,13 +1367,18 @@ async function buildSystemPrompt(opts) {
|
|
|
1237
1367
|
parts.push("3. NEVER apologize and refuse to act. If a task requires a tool, use it. If you lack a specific tool, say which tool you need \u2014 do not give a generic refusal.");
|
|
1238
1368
|
parts.push("4. Read files before editing them. Use tools proactively without being asked twice.");
|
|
1239
1369
|
parts.push("5. You can configure yourself \u2014 use the config, channel, agent, and model management tools to modify your own setup.");
|
|
1370
|
+
parts.push("6. Do NOT modify, delete, or overwrite files outside your workspace directory or the user's current working directory unless the user explicitly names the file. System files, OS directories, and config dotfiles are off-limits by default.");
|
|
1240
1371
|
}
|
|
1241
1372
|
if (opts.thinking === "off") {
|
|
1242
1373
|
parts.push("");
|
|
1243
1374
|
parts.push("Do NOT use extended thinking or reasoning blocks. Respond directly and concisely.");
|
|
1244
1375
|
}
|
|
1245
1376
|
parts.push("");
|
|
1246
|
-
|
|
1377
|
+
if (isLocal) {
|
|
1378
|
+
parts.push("Your memories are managed automatically. Use memory tools to save or recall important information. Do not rely on conversation history for long-term facts.");
|
|
1379
|
+
} else {
|
|
1380
|
+
parts.push("When you learn something important about the user or project, save it using the config or memory tools so you remember it next time.");
|
|
1381
|
+
}
|
|
1247
1382
|
parts.push("");
|
|
1248
1383
|
const projectMemory = await loadProjectMemory(opts.identity.workspace);
|
|
1249
1384
|
if (projectMemory) {
|
|
@@ -1253,9 +1388,9 @@ async function buildSystemPrompt(opts) {
|
|
|
1253
1388
|
}
|
|
1254
1389
|
return parts.join("\n");
|
|
1255
1390
|
}
|
|
1256
|
-
async function loadWorkspaceFiles(workspaceDir) {
|
|
1391
|
+
async function loadWorkspaceFiles(workspaceDir, files = WORKSPACE_FILES) {
|
|
1257
1392
|
const sections = [];
|
|
1258
|
-
for (const filename of
|
|
1393
|
+
for (const filename of files) {
|
|
1259
1394
|
const filePath = join2(workspaceDir, filename);
|
|
1260
1395
|
if (existsSync2(filePath)) {
|
|
1261
1396
|
try {
|
|
@@ -1269,6 +1404,18 @@ async function loadWorkspaceFiles(workspaceDir) {
|
|
|
1269
1404
|
}
|
|
1270
1405
|
return sections.length > 0 ? sections.join("\n\n---\n\n") : null;
|
|
1271
1406
|
}
|
|
1407
|
+
async function loadSingleFile(workspaceDir, filename) {
|
|
1408
|
+
const filePath = join2(workspaceDir, filename);
|
|
1409
|
+
if (existsSync2(filePath)) {
|
|
1410
|
+
try {
|
|
1411
|
+
const content = await readFile2(filePath, "utf-8");
|
|
1412
|
+
return content.trim() || null;
|
|
1413
|
+
} catch {
|
|
1414
|
+
return null;
|
|
1415
|
+
}
|
|
1416
|
+
}
|
|
1417
|
+
return null;
|
|
1418
|
+
}
|
|
1272
1419
|
async function loadProjectMemory(projectRoot) {
|
|
1273
1420
|
const candidates = [".clank.md", ".clankbuild.md", ".llamabuild.md"];
|
|
1274
1421
|
for (const filename of candidates) {
|
|
@@ -1287,7 +1434,7 @@ async function loadProjectMemory(projectRoot) {
|
|
|
1287
1434
|
async function ensureWorkspaceFiles(workspaceDir, templateDir) {
|
|
1288
1435
|
const { mkdir: mkdir7, copyFile } = await import("fs/promises");
|
|
1289
1436
|
await mkdir7(workspaceDir, { recursive: true });
|
|
1290
|
-
for (const filename of [...WORKSPACE_FILES, "BOOTSTRAP.md", "HEARTBEAT.md"]) {
|
|
1437
|
+
for (const filename of [...WORKSPACE_FILES, "BOOTSTRAP.md", "HEARTBEAT.md", "RUNNER.md"]) {
|
|
1291
1438
|
const target = join2(workspaceDir, filename);
|
|
1292
1439
|
const source = join2(templateDir, filename);
|
|
1293
1440
|
if (!existsSync2(target) && existsSync2(source)) {
|
|
@@ -1295,7 +1442,7 @@ async function ensureWorkspaceFiles(workspaceDir, templateDir) {
|
|
|
1295
1442
|
}
|
|
1296
1443
|
}
|
|
1297
1444
|
}
|
|
1298
|
-
var WORKSPACE_FILES;
|
|
1445
|
+
var WORKSPACE_FILES, WORKSPACE_FILES_LOCAL, SUB_AGENT_FILE;
|
|
1299
1446
|
var init_system_prompt = __esm({
|
|
1300
1447
|
"src/engine/system-prompt.ts"() {
|
|
1301
1448
|
"use strict";
|
|
@@ -1308,6 +1455,14 @@ var init_system_prompt = __esm({
|
|
|
1308
1455
|
"TOOLS.md",
|
|
1309
1456
|
"MEMORY.md"
|
|
1310
1457
|
];
|
|
1458
|
+
WORKSPACE_FILES_LOCAL = [
|
|
1459
|
+
"SOUL.md",
|
|
1460
|
+
"USER.md",
|
|
1461
|
+
"IDENTITY.md",
|
|
1462
|
+
"AGENTS.md",
|
|
1463
|
+
"TOOLS.md"
|
|
1464
|
+
];
|
|
1465
|
+
SUB_AGENT_FILE = "RUNNER.md";
|
|
1311
1466
|
}
|
|
1312
1467
|
});
|
|
1313
1468
|
|
|
@@ -5057,8 +5212,12 @@ async function runChat(opts) {
|
|
|
5057
5212
|
console.error(red(`Error: ${message}${recoverable ? " (recoverable)" : ""}`));
|
|
5058
5213
|
});
|
|
5059
5214
|
console.log("");
|
|
5060
|
-
console.log(
|
|
5061
|
-
console.log(
|
|
5215
|
+
console.log(cyan(" ___ _ _ "));
|
|
5216
|
+
console.log(cyan(" / __|| | __ _ _ _ | |__"));
|
|
5217
|
+
console.log(cyan(" | (__ | |/ _` || ' \\| / /"));
|
|
5218
|
+
console.log(cyan(" \\___||_|\\__,_||_||_|_\\_\\"));
|
|
5219
|
+
console.log(dim(` v1.7.3 | ${resolved.modelId} | ${identity.toolTier} tier`));
|
|
5220
|
+
console.log(dim(" Type your message. Press Ctrl+C to exit.\n"));
|
|
5062
5221
|
const rl = createInterface({
|
|
5063
5222
|
input: process.stdin,
|
|
5064
5223
|
output: process.stdout,
|
|
@@ -5123,7 +5282,7 @@ async function handleSlashCommand(input, engine, _rl) {
|
|
|
5123
5282
|
console.log(dim(`Unknown command: /${cmd}. Type /help for available commands.`));
|
|
5124
5283
|
}
|
|
5125
5284
|
}
|
|
5126
|
-
var dim,
|
|
5285
|
+
var dim, green, yellow, red, cyan;
|
|
5127
5286
|
var init_chat = __esm({
|
|
5128
5287
|
"src/cli/chat.ts"() {
|
|
5129
5288
|
"use strict";
|
|
@@ -5134,7 +5293,6 @@ var init_chat = __esm({
|
|
|
5134
5293
|
init_config2();
|
|
5135
5294
|
init_sessions();
|
|
5136
5295
|
dim = (s) => `\x1B[2m${s}\x1B[0m`;
|
|
5137
|
-
bold = (s) => `\x1B[1m${s}\x1B[0m`;
|
|
5138
5296
|
green = (s) => `\x1B[32m${s}\x1B[0m`;
|
|
5139
5297
|
yellow = (s) => `\x1B[33m${s}\x1B[0m`;
|
|
5140
5298
|
red = (s) => `\x1B[31m${s}\x1B[0m`;
|
|
@@ -6195,6 +6353,7 @@ var init_telegram = __esm({
|
|
|
6195
6353
|
{ command: "help", description: "Show available commands" },
|
|
6196
6354
|
{ command: "new", description: "Start a new session" },
|
|
6197
6355
|
{ command: "reset", description: "Clear current session" },
|
|
6356
|
+
{ command: "compact", description: "Save state and clear context" },
|
|
6198
6357
|
{ command: "status", description: "Agent status and info" },
|
|
6199
6358
|
{ command: "agents", description: "List available agents" },
|
|
6200
6359
|
{ command: "tasks", description: "Show background tasks" },
|
|
@@ -6245,7 +6404,7 @@ var init_telegram = __esm({
|
|
|
6245
6404
|
try {
|
|
6246
6405
|
console.log(` Telegram: processing message from ${userId} in ${chatId}`);
|
|
6247
6406
|
await ctx.api.sendChatAction(chatId, "typing");
|
|
6248
|
-
const
|
|
6407
|
+
const typingInterval2 = setInterval(() => {
|
|
6249
6408
|
bot.api.sendChatAction(chatId, "typing").catch(() => {
|
|
6250
6409
|
});
|
|
6251
6410
|
}, 4e3);
|
|
@@ -6336,13 +6495,14 @@ var init_telegram = __esm({
|
|
|
6336
6495
|
await ctx.api.sendMessage(chatId, chunk);
|
|
6337
6496
|
}
|
|
6338
6497
|
}
|
|
6339
|
-
clearInterval(typingInterval);
|
|
6340
6498
|
console.log(` Telegram: response complete (${response?.length || 0} chars)`);
|
|
6341
6499
|
} catch (err) {
|
|
6342
6500
|
const errMsg = err instanceof Error ? err.message : String(err);
|
|
6343
6501
|
console.error(` Telegram: message handler error \u2014 ${errMsg}`);
|
|
6344
6502
|
await ctx.api.sendMessage(chatId, `\u26A0\uFE0F Error: ${errMsg.slice(0, 200)}`).catch(() => {
|
|
6345
6503
|
});
|
|
6504
|
+
} finally {
|
|
6505
|
+
clearInterval(typingInterval);
|
|
6346
6506
|
}
|
|
6347
6507
|
};
|
|
6348
6508
|
const prev = chatLocks.get(chatId) || Promise.resolve();
|
|
@@ -6543,6 +6703,7 @@ You can read this file with the read_file tool.`
|
|
|
6543
6703
|
"\u{1F4AC} *Chat*",
|
|
6544
6704
|
"/new \u2014 Start a new session",
|
|
6545
6705
|
"/reset \u2014 Clear current session history",
|
|
6706
|
+
"/compact \u2014 Save state, clear context, continue",
|
|
6546
6707
|
"",
|
|
6547
6708
|
"\u{1F4CA} *Info*",
|
|
6548
6709
|
"/status \u2014 Agent, model, and session info",
|
|
@@ -6637,6 +6798,22 @@ _Switch with /agent <name>_`;
|
|
|
6637
6798
|
});
|
|
6638
6799
|
}
|
|
6639
6800
|
return command === "new" ? "\u2728 New session started. Send a message to begin." : "\u{1F5D1} Session cleared. History erased.";
|
|
6801
|
+
case "compact": {
|
|
6802
|
+
if (!this.gateway) return "Gateway not connected.";
|
|
6803
|
+
const summary = await this.gateway.compactSession({
|
|
6804
|
+
channel: "telegram",
|
|
6805
|
+
peerId: chatId,
|
|
6806
|
+
peerKind: isGroup ? "group" : "dm"
|
|
6807
|
+
});
|
|
6808
|
+
if (!summary) return "Nothing to compact \u2014 no active session.";
|
|
6809
|
+
const preview = summary.length > 300 ? summary.slice(0, 300) + "..." : summary;
|
|
6810
|
+
return `\u{1F4E6} *Session compacted*
|
|
6811
|
+
|
|
6812
|
+
Context cleared and state saved. The agent will continue where it left off.
|
|
6813
|
+
|
|
6814
|
+
_Summary:_
|
|
6815
|
+
${preview}`;
|
|
6816
|
+
}
|
|
6640
6817
|
case "model": {
|
|
6641
6818
|
const model = this.config?.agents?.defaults?.model?.primary || "unknown";
|
|
6642
6819
|
const fallbacks = this.config?.agents?.defaults?.model?.fallbacks || [];
|
|
@@ -6706,7 +6883,7 @@ _Kill with /kill <id> or /killall_`;
|
|
|
6706
6883
|
return !current ? "\u{1F4AD} Thinking display *on* \u2014 you'll see the model's reasoning above responses." : "\u{1F4AD} Thinking display *off* \u2014 only the final response will be shown.";
|
|
6707
6884
|
}
|
|
6708
6885
|
case "version": {
|
|
6709
|
-
return `\u{1F527} *Clank* v1.7.
|
|
6886
|
+
return `\u{1F527} *Clank* v1.7.3`;
|
|
6710
6887
|
}
|
|
6711
6888
|
default:
|
|
6712
6889
|
return null;
|
|
@@ -7161,6 +7338,17 @@ var init_server = __esm({
|
|
|
7161
7338
|
this.engines.delete(sessionKey);
|
|
7162
7339
|
}
|
|
7163
7340
|
}
|
|
7341
|
+
/**
|
|
7342
|
+
* Compact a session — summarize state, clear context, inject summary.
|
|
7343
|
+
* Used by channel adapters (Telegram /compact command).
|
|
7344
|
+
* Returns the summary text, or null if no active session.
|
|
7345
|
+
*/
|
|
7346
|
+
async compactSession(context) {
|
|
7347
|
+
const sessionKey = deriveSessionKey(context);
|
|
7348
|
+
const engine = this.engines.get(sessionKey);
|
|
7349
|
+
if (!engine) return null;
|
|
7350
|
+
return engine.compactSession();
|
|
7351
|
+
}
|
|
7164
7352
|
/**
|
|
7165
7353
|
* Handle an inbound message from any channel adapter.
|
|
7166
7354
|
* This is the main entry point for all non-WebSocket messages.
|
|
@@ -7285,7 +7473,7 @@ var init_server = __esm({
|
|
|
7285
7473
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
7286
7474
|
res.end(JSON.stringify({
|
|
7287
7475
|
status: "ok",
|
|
7288
|
-
version: "1.7.
|
|
7476
|
+
version: "1.7.3",
|
|
7289
7477
|
uptime: process.uptime(),
|
|
7290
7478
|
clients: this.clients.size,
|
|
7291
7479
|
agents: this.engines.size
|
|
@@ -7397,7 +7585,7 @@ var init_server = __esm({
|
|
|
7397
7585
|
const hello = {
|
|
7398
7586
|
type: "hello",
|
|
7399
7587
|
protocol: PROTOCOL_VERSION,
|
|
7400
|
-
version: "1.7.
|
|
7588
|
+
version: "1.7.3",
|
|
7401
7589
|
agents: this.config.agents.list.map((a) => ({
|
|
7402
7590
|
id: a.id,
|
|
7403
7591
|
name: a.name || a.id,
|
|
@@ -7454,6 +7642,17 @@ var init_server = __esm({
|
|
|
7454
7642
|
this.sendResponse(client, frame.id, true);
|
|
7455
7643
|
break;
|
|
7456
7644
|
}
|
|
7645
|
+
case "session.compact": {
|
|
7646
|
+
const compactKey = frame.params?.sessionKey || client.sessionKey;
|
|
7647
|
+
const compactEngine = this.engines.get(compactKey);
|
|
7648
|
+
if (compactEngine) {
|
|
7649
|
+
const summary = await compactEngine.compactSession();
|
|
7650
|
+
this.sendResponse(client, frame.id, true, { summary });
|
|
7651
|
+
} else {
|
|
7652
|
+
this.sendResponse(client, frame.id, false, "No active session to compact");
|
|
7653
|
+
}
|
|
7654
|
+
break;
|
|
7655
|
+
}
|
|
7457
7656
|
// === Agents ===
|
|
7458
7657
|
case "agent.list":
|
|
7459
7658
|
this.sendResponse(client, frame.id, true, this.config.agents.list.map((a) => ({
|
|
@@ -7687,6 +7886,7 @@ var init_server = __esm({
|
|
|
7687
7886
|
toolTier: agentConfig?.toolTier || this.config.agents.defaults.toolTier || "auto",
|
|
7688
7887
|
tools: agentConfig?.tools
|
|
7689
7888
|
};
|
|
7889
|
+
const currentDepth = sessionKey.startsWith("task:") ? (this.taskRegistry.getBySessionKey(sessionKey)?.spawnDepth ?? 0) + 1 : 0;
|
|
7690
7890
|
const compact = agentConfig?.compactPrompt ?? this.config.agents.defaults.compactPrompt ?? false;
|
|
7691
7891
|
const thinking = agentConfig?.thinking ?? this.config.agents.defaults.thinking ?? "auto";
|
|
7692
7892
|
const systemPrompt = await buildSystemPrompt({
|
|
@@ -7694,12 +7894,13 @@ var init_server = __esm({
|
|
|
7694
7894
|
workspaceDir: identity.workspace,
|
|
7695
7895
|
channel,
|
|
7696
7896
|
compact,
|
|
7697
|
-
thinking
|
|
7897
|
+
thinking,
|
|
7898
|
+
spawnDepth: currentDepth,
|
|
7899
|
+
isLocal: resolved.isLocal
|
|
7698
7900
|
});
|
|
7699
|
-
const memoryBudget = resolved.isLocal ?
|
|
7700
|
-
const memoryBlock = await this.memoryManager.buildMemoryBlock("", identity.workspace, memoryBudget);
|
|
7901
|
+
const memoryBudget = resolved.isLocal ? 800 : 4e3;
|
|
7902
|
+
const memoryBlock = await this.memoryManager.buildMemoryBlock("session", identity.workspace, memoryBudget);
|
|
7701
7903
|
const fullPrompt = memoryBlock ? systemPrompt + "\n\n---\n\n" + memoryBlock : systemPrompt;
|
|
7702
|
-
const currentDepth = sessionKey.startsWith("task:") ? (this.taskRegistry.getBySessionKey(sessionKey)?.spawnDepth ?? 0) + 1 : 0;
|
|
7703
7904
|
const maxSpawnDepth = this.config.agents.defaults.subagents?.maxSpawnDepth ?? 1;
|
|
7704
7905
|
const maxConcurrent = this.config.agents.defaults.subagents?.maxConcurrent ?? 8;
|
|
7705
7906
|
const canSpawn = currentDepth < maxSpawnDepth;
|
|
@@ -8286,7 +8487,7 @@ async function runSetup(opts) {
|
|
|
8286
8487
|
const rl = createInterface2({ input: process.stdin, output: process.stdout });
|
|
8287
8488
|
try {
|
|
8288
8489
|
console.log("");
|
|
8289
|
-
console.log(
|
|
8490
|
+
console.log(bold(" Welcome to Clank"));
|
|
8290
8491
|
console.log("");
|
|
8291
8492
|
console.log(" Clank is an AI agent that can read, write, and");
|
|
8292
8493
|
console.log(" delete files, execute commands, and access the web.");
|
|
@@ -8304,7 +8505,7 @@ async function runSetup(opts) {
|
|
|
8304
8505
|
console.log("");
|
|
8305
8506
|
console.log(" How would you like to set up Clank?");
|
|
8306
8507
|
console.log("");
|
|
8307
|
-
console.log(" 1. " +
|
|
8508
|
+
console.log(" 1. " + bold("Quick Start") + " (recommended)");
|
|
8308
8509
|
console.log(dim4(" Auto-detect local models, sensible defaults"));
|
|
8309
8510
|
console.log(" 2. Advanced");
|
|
8310
8511
|
console.log(dim4(" Full control over gateway, models, channels"));
|
|
@@ -8571,7 +8772,7 @@ async function runSetup(opts) {
|
|
|
8571
8772
|
await saveConfig(config);
|
|
8572
8773
|
console.log(green4("\n Config saved to " + getConfigDir() + "/config.json5"));
|
|
8573
8774
|
console.log("");
|
|
8574
|
-
console.log(
|
|
8775
|
+
console.log(bold(" Clank is ready!"));
|
|
8575
8776
|
console.log("");
|
|
8576
8777
|
console.log(" Start chatting:");
|
|
8577
8778
|
console.log(dim4(" clank chat \u2014 CLI chat"));
|
|
@@ -8582,7 +8783,7 @@ async function runSetup(opts) {
|
|
|
8582
8783
|
rl.close();
|
|
8583
8784
|
}
|
|
8584
8785
|
}
|
|
8585
|
-
var __dirname2, dim4,
|
|
8786
|
+
var __dirname2, dim4, bold, green4, yellow2, cyan2;
|
|
8586
8787
|
var init_setup = __esm({
|
|
8587
8788
|
"src/cli/setup.ts"() {
|
|
8588
8789
|
"use strict";
|
|
@@ -8592,7 +8793,7 @@ var init_setup = __esm({
|
|
|
8592
8793
|
init_daemon();
|
|
8593
8794
|
__dirname2 = dirname4(fileURLToPath4(import.meta.url));
|
|
8594
8795
|
dim4 = (s) => `\x1B[2m${s}\x1B[0m`;
|
|
8595
|
-
|
|
8796
|
+
bold = (s) => `\x1B[1m${s}\x1B[0m`;
|
|
8596
8797
|
green4 = (s) => `\x1B[32m${s}\x1B[0m`;
|
|
8597
8798
|
yellow2 = (s) => `\x1B[33m${s}\x1B[0m`;
|
|
8598
8799
|
cyan2 = (s) => `\x1B[36m${s}\x1B[0m`;
|
|
@@ -9005,13 +9206,17 @@ async function runTui(opts) {
|
|
|
9005
9206
|
return;
|
|
9006
9207
|
}
|
|
9007
9208
|
console.log("");
|
|
9008
|
-
console.log(
|
|
9209
|
+
console.log(cyan5(" ___ _ _ "));
|
|
9210
|
+
console.log(cyan5(" / __|| | __ _ _ _ | |__"));
|
|
9211
|
+
console.log(cyan5(" | (__ | |/ _` || ' \\| / /"));
|
|
9212
|
+
console.log(cyan5(" \\___||_|\\__,_||_||_|_\\_\\"));
|
|
9213
|
+
console.log(dim8(` TUI | connecting to ${wsUrl}...`));
|
|
9009
9214
|
const ws = new WebSocket2(wsUrl);
|
|
9010
9215
|
state.ws = ws;
|
|
9011
9216
|
ws.on("open", () => {
|
|
9012
9217
|
ws.send(JSON.stringify({
|
|
9013
9218
|
type: "connect",
|
|
9014
|
-
params: { auth: { token }, mode: "tui", version: "1.7.
|
|
9219
|
+
params: { auth: { token }, mode: "tui", version: "1.7.3" }
|
|
9015
9220
|
}));
|
|
9016
9221
|
});
|
|
9017
9222
|
ws.on("message", (data) => {
|
|
@@ -9184,6 +9389,11 @@ function handleFrame(state, frame) {
|
|
|
9184
9389
|
if (!res.ok && res.error) {
|
|
9185
9390
|
console.log(red5(`
|
|
9186
9391
|
Error: ${res.error}`));
|
|
9392
|
+
} else if (res.ok && res.data?.summary) {
|
|
9393
|
+
const summary = res.data.summary.slice(0, 500);
|
|
9394
|
+
console.log(green8(" Session compacted.") + dim8(" Context cleared, state saved."));
|
|
9395
|
+
console.log(dim8(` Summary:
|
|
9396
|
+
${summary}`));
|
|
9187
9397
|
}
|
|
9188
9398
|
state.streaming = false;
|
|
9189
9399
|
}
|
|
@@ -9200,6 +9410,7 @@ async function handleSlashCommand2(state, input, rl) {
|
|
|
9200
9410
|
console.log(dim8(" /model [id] \u2014 Show current model"));
|
|
9201
9411
|
console.log(dim8(" /think \u2014 Toggle thinking display"));
|
|
9202
9412
|
console.log(dim8(" /tools \u2014 Toggle tool output"));
|
|
9413
|
+
console.log(dim8(" /compact \u2014 Save state, clear context, continue"));
|
|
9203
9414
|
console.log(dim8(" /new \u2014 Start new session"));
|
|
9204
9415
|
console.log(dim8(" /reset \u2014 Reset current session"));
|
|
9205
9416
|
console.log(dim8(" /exit \u2014 Exit"));
|
|
@@ -9256,6 +9467,15 @@ async function handleSlashCommand2(state, input, rl) {
|
|
|
9256
9467
|
state.showToolOutput = !state.showToolOutput;
|
|
9257
9468
|
console.log(dim8(` Tool output: ${state.showToolOutput ? "on" : "off"}`));
|
|
9258
9469
|
break;
|
|
9470
|
+
case "compact":
|
|
9471
|
+
console.log(dim8(" Compacting session..."));
|
|
9472
|
+
state.ws?.send(JSON.stringify({
|
|
9473
|
+
type: "req",
|
|
9474
|
+
id: ++state.reqId,
|
|
9475
|
+
method: "session.compact",
|
|
9476
|
+
params: { sessionKey: state.sessionKey }
|
|
9477
|
+
}));
|
|
9478
|
+
break;
|
|
9259
9479
|
case "new":
|
|
9260
9480
|
state.sessionKey = `tui:${state.agentId}:${Date.now()}`;
|
|
9261
9481
|
console.log(green8(` New session: ${state.sessionKey}`));
|
|
@@ -9280,7 +9500,7 @@ async function handleSlashCommand2(state, input, rl) {
|
|
|
9280
9500
|
function printStatusBar(state) {
|
|
9281
9501
|
console.log(dim8(` ${state.agentName} | ${state.modelId} | ${state.sessionKey}`));
|
|
9282
9502
|
}
|
|
9283
|
-
var dim8,
|
|
9503
|
+
var dim8, green8, red5, cyan5, italic;
|
|
9284
9504
|
var init_tui = __esm({
|
|
9285
9505
|
"src/cli/tui.ts"() {
|
|
9286
9506
|
"use strict";
|
|
@@ -9288,7 +9508,6 @@ var init_tui = __esm({
|
|
|
9288
9508
|
init_config2();
|
|
9289
9509
|
init_protocol();
|
|
9290
9510
|
dim8 = (s) => `\x1B[2m${s}\x1B[0m`;
|
|
9291
|
-
bold3 = (s) => `\x1B[1m${s}\x1B[0m`;
|
|
9292
9511
|
green8 = (s) => `\x1B[32m${s}\x1B[0m`;
|
|
9293
9512
|
red5 = (s) => `\x1B[31m${s}\x1B[0m`;
|
|
9294
9513
|
cyan5 = (s) => `\x1B[36m${s}\x1B[0m`;
|
|
@@ -9364,7 +9583,7 @@ import { existsSync as existsSync12 } from "fs";
|
|
|
9364
9583
|
async function runUninstall(opts) {
|
|
9365
9584
|
const configDir = getConfigDir();
|
|
9366
9585
|
console.log("");
|
|
9367
|
-
console.log(
|
|
9586
|
+
console.log(bold2(" Uninstall Clank"));
|
|
9368
9587
|
console.log("");
|
|
9369
9588
|
console.log(" This will permanently remove:");
|
|
9370
9589
|
console.log(red7(` ${configDir}`));
|
|
@@ -9417,7 +9636,7 @@ async function runUninstall(opts) {
|
|
|
9417
9636
|
console.log(green10(" Clank has been completely removed."));
|
|
9418
9637
|
console.log("");
|
|
9419
9638
|
}
|
|
9420
|
-
var dim10,
|
|
9639
|
+
var dim10, bold2, green10, red7, yellow4;
|
|
9421
9640
|
var init_uninstall = __esm({
|
|
9422
9641
|
"src/cli/uninstall.ts"() {
|
|
9423
9642
|
"use strict";
|
|
@@ -9425,7 +9644,7 @@ var init_uninstall = __esm({
|
|
|
9425
9644
|
init_config2();
|
|
9426
9645
|
init_gateway_cmd();
|
|
9427
9646
|
dim10 = (s) => `\x1B[2m${s}\x1B[0m`;
|
|
9428
|
-
|
|
9647
|
+
bold2 = (s) => `\x1B[1m${s}\x1B[0m`;
|
|
9429
9648
|
green10 = (s) => `\x1B[32m${s}\x1B[0m`;
|
|
9430
9649
|
red7 = (s) => `\x1B[31m${s}\x1B[0m`;
|
|
9431
9650
|
yellow4 = (s) => `\x1B[33m${s}\x1B[0m`;
|
|
@@ -9440,7 +9659,7 @@ import { fileURLToPath as fileURLToPath5 } from "url";
|
|
|
9440
9659
|
import { dirname as dirname5, join as join20 } from "path";
|
|
9441
9660
|
var __filename3 = fileURLToPath5(import.meta.url);
|
|
9442
9661
|
var __dirname3 = dirname5(__filename3);
|
|
9443
|
-
var version = "1.7.
|
|
9662
|
+
var version = "1.7.3";
|
|
9444
9663
|
try {
|
|
9445
9664
|
const pkg = JSON.parse(readFileSync(join20(__dirname3, "..", "package.json"), "utf-8"));
|
|
9446
9665
|
version = pkg.version;
|