@tractorscorch/clank 1.5.0 → 1.5.2

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 CHANGED
@@ -6,6 +6,24 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/).
6
6
 
7
7
  ---
8
8
 
9
+ ## [1.5.2] — 2026-03-23
10
+
11
+ ### Fixed
12
+ - **Thinking models (Qwen3.5) exhaust tokens on reasoning** — the model generates `<think>` reasoning tokens that eat the entire context window, leaving nothing for actual content. Added default `max_tokens: 4096` for local models and `reasoning_effort: "low"` to reduce thinking overhead
13
+ - **Telegram shows nothing during model thinking** — added periodic "typing" indicator every 4 seconds so the bot doesn't appear dead while the model processes internally
14
+ - **Root cause found via direct API testing** — Qwen3.5-35B returns empty `content` with all output in `reasoning_content`; without a max_tokens cap, the model spends all its budget on thinking
15
+
16
+ ---
17
+
18
+ ## [1.5.1] — 2026-03-23
19
+
20
+ ### Fixed
21
+ - **Local models timing out on tool calls** — removed per-chunk read timeout that was killing legitimate slow processing; a 35B quantized model can take minutes for prefill on large contexts, that's normal not a hang
22
+ - **Local model timeout increased to 5 minutes** — was 120s (too short for large quantized models doing prefill on big contexts with tool results)
23
+ - **Memory budget reduced for local models** — memory injection now uses 1.5K chars (was 4K) to avoid eating the limited context window of local models (8K-32K vs 128K+ for cloud)
24
+
25
+ ---
26
+
9
27
  ## [1.5.0] — 2026-03-23
10
28
 
11
29
  ### Fixed
package/README.md CHANGED
@@ -9,7 +9,7 @@
9
9
  </p>
10
10
 
11
11
  <p align="center">
12
- <a href="https://github.com/ItsTrag1c/Clank/releases/latest"><img src="https://img.shields.io/badge/version-1.5.0-blue.svg" alt="Version" /></a>
12
+ <a href="https://github.com/ItsTrag1c/Clank/releases/latest"><img src="https://img.shields.io/badge/version-1.5.2-blue.svg" alt="Version" /></a>
13
13
  <a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/License-MIT-blue.svg" alt="License" /></a>
14
14
  <a href="https://www.npmjs.com/package/@tractorscorch/clank"><img src="https://img.shields.io/npm/v/@tractorscorch/clank.svg" alt="npm" /></a>
15
15
  <a href="https://github.com/ItsTrag1c/Clank/stargazers"><img src="https://img.shields.io/github/stars/ItsTrag1c/Clank.svg" alt="Stars" /></a>
@@ -75,7 +75,7 @@ That's it. Setup auto-detects your local models, configures the gateway, and get
75
75
  | Platform | Download |
76
76
  |----------|----------|
77
77
  | **npm** (all platforms) | `npm install -g @tractorscorch/clank` |
78
- | **macOS** (Apple Silicon) | [Clank_1.5.0_macos](https://github.com/ItsTrag1c/Clank/releases/latest/download/Clank_1.5.0_macos) |
78
+ | **macOS** (Apple Silicon) | [Clank_1.5.2_macos](https://github.com/ItsTrag1c/Clank/releases/latest/download/Clank_1.5.2_macos) |
79
79
 
80
80
  ## Features
81
81
 
package/dist/index.js CHANGED
@@ -561,7 +561,7 @@ var init_ollama = __esm({
561
561
  if (this.maxResponseTokens) {
562
562
  body.max_tokens = this.maxResponseTokens;
563
563
  }
564
- const timeoutSignal = AbortSignal.timeout(12e4);
564
+ const timeoutSignal = AbortSignal.timeout(3e5);
565
565
  const effectiveSignal = signal ? AbortSignal.any([signal, timeoutSignal]) : timeoutSignal;
566
566
  const res = await fetch(`${this.baseUrl}/v1/chat/completions`, {
567
567
  method: "POST",
@@ -580,14 +580,9 @@ var init_ollama = __esm({
580
580
  const decoder = new TextDecoder();
581
581
  let buffer = "";
582
582
  const toolCalls = /* @__PURE__ */ new Map();
583
- const CHUNK_TIMEOUT = 6e4;
584
583
  try {
585
584
  while (true) {
586
- const readPromise = reader.read();
587
- const timeoutPromise = new Promise(
588
- (_, reject) => setTimeout(() => reject(new Error("Model stopped responding (no data for 60s)")), CHUNK_TIMEOUT)
589
- );
590
- const { done, value } = await Promise.race([readPromise, timeoutPromise]);
585
+ const { done, value } = await reader.read();
591
586
  if (done) break;
592
587
  buffer += decoder.decode(value, { stream: true });
593
588
  const lines = buffer.split("\n");
@@ -2992,11 +2987,16 @@ var init_openai = __esm({
2992
2987
  stream: true,
2993
2988
  stream_options: { include_usage: true }
2994
2989
  };
2990
+ if (this.isLocal) {
2991
+ body.reasoning_effort = "low";
2992
+ }
2995
2993
  if (tools.length > 0) {
2996
2994
  body.tools = this.formatTools(tools);
2997
2995
  }
2998
2996
  if (this.maxResponseTokens) {
2999
2997
  body.max_tokens = this.maxResponseTokens;
2998
+ } else if (this.isLocal) {
2999
+ body.max_tokens = 4096;
3000
3000
  }
3001
3001
  const headers = {
3002
3002
  "Content-Type": "application/json"
@@ -3004,7 +3004,7 @@ var init_openai = __esm({
3004
3004
  if (this.apiKey) {
3005
3005
  headers["Authorization"] = `Bearer ${this.apiKey}`;
3006
3006
  }
3007
- const timeoutMs = this.isLocal ? 12e4 : 9e4;
3007
+ const timeoutMs = this.isLocal ? 3e5 : 9e4;
3008
3008
  const timeoutSignal = AbortSignal.timeout(timeoutMs);
3009
3009
  const effectiveSignal = signal ? AbortSignal.any([signal, timeoutSignal]) : timeoutSignal;
3010
3010
  const res = await fetch(`${this.baseUrl}/v1/chat/completions`, {
@@ -3022,14 +3022,9 @@ var init_openai = __esm({
3022
3022
  const decoder = new TextDecoder();
3023
3023
  let buffer = "";
3024
3024
  const toolCalls = /* @__PURE__ */ new Map();
3025
- const CHUNK_TIMEOUT = 6e4;
3026
3025
  try {
3027
3026
  while (true) {
3028
- const readPromise = reader.read();
3029
- const timeoutPromise = new Promise(
3030
- (_, reject) => setTimeout(() => reject(new Error("Model stopped responding (no data for 60s)")), CHUNK_TIMEOUT)
3031
- );
3032
- const { done, value } = await Promise.race([readPromise, timeoutPromise]);
3027
+ const { done, value } = await reader.read();
3033
3028
  if (done) break;
3034
3029
  buffer += decoder.decode(value, { stream: true });
3035
3030
  const lines = buffer.split("\n");
@@ -5308,6 +5303,10 @@ var init_telegram = __esm({
5308
5303
  try {
5309
5304
  console.log(` Telegram: processing message from ${userId} in ${chatId}`);
5310
5305
  await ctx.api.sendChatAction(chatId, "typing");
5306
+ const typingInterval2 = setInterval(() => {
5307
+ bot.api.sendChatAction(chatId, "typing").catch(() => {
5308
+ });
5309
+ }, 4e3);
5311
5310
  let streamMsgId = null;
5312
5311
  let sendingInitial = false;
5313
5312
  let accumulated = "";
@@ -5376,8 +5375,10 @@ var init_telegram = __esm({
5376
5375
  await ctx.api.sendMessage(chatId, chunk);
5377
5376
  }
5378
5377
  }
5378
+ clearInterval(typingInterval2);
5379
5379
  console.log(` Telegram: response complete (${response?.length || 0} chars)`);
5380
5380
  } catch (err) {
5381
+ clearInterval(typingInterval);
5381
5382
  const errMsg = err instanceof Error ? err.message : String(err);
5382
5383
  console.error(` Telegram: message handler error \u2014 ${errMsg}`);
5383
5384
  await ctx.api.sendMessage(chatId, `Error: ${errMsg.slice(0, 200)}`).catch(() => {
@@ -6165,7 +6166,7 @@ var init_server = __esm({
6165
6166
  res.writeHead(200, { "Content-Type": "application/json" });
6166
6167
  res.end(JSON.stringify({
6167
6168
  status: "ok",
6168
- version: "1.5.0",
6169
+ version: "1.5.2",
6169
6170
  uptime: process.uptime(),
6170
6171
  clients: this.clients.size,
6171
6172
  agents: this.engines.size
@@ -6277,7 +6278,7 @@ var init_server = __esm({
6277
6278
  const hello = {
6278
6279
  type: "hello",
6279
6280
  protocol: PROTOCOL_VERSION,
6280
- version: "1.5.0",
6281
+ version: "1.5.2",
6281
6282
  agents: this.config.agents.list.map((a) => ({
6282
6283
  id: a.id,
6283
6284
  name: a.name || a.id,
@@ -6519,7 +6520,8 @@ var init_server = __esm({
6519
6520
  compact,
6520
6521
  thinking
6521
6522
  });
6522
- const memoryBlock = await this.memoryManager.buildMemoryBlock("", identity.workspace);
6523
+ const memoryBudget = resolved.isLocal ? 1500 : 4e3;
6524
+ const memoryBlock = await this.memoryManager.buildMemoryBlock("", identity.workspace, memoryBudget);
6523
6525
  const fullPrompt = memoryBlock ? systemPrompt + "\n\n---\n\n" + memoryBlock : systemPrompt;
6524
6526
  engine = new AgentEngine({
6525
6527
  identity,
@@ -7671,7 +7673,7 @@ async function runTui(opts) {
7671
7673
  ws.on("open", () => {
7672
7674
  ws.send(JSON.stringify({
7673
7675
  type: "connect",
7674
- params: { auth: { token }, mode: "tui", version: "1.5.0" }
7676
+ params: { auth: { token }, mode: "tui", version: "1.5.2" }
7675
7677
  }));
7676
7678
  });
7677
7679
  ws.on("message", (data) => {
@@ -8100,7 +8102,7 @@ import { fileURLToPath as fileURLToPath5 } from "url";
8100
8102
  import { dirname as dirname5, join as join19 } from "path";
8101
8103
  var __filename3 = fileURLToPath5(import.meta.url);
8102
8104
  var __dirname3 = dirname5(__filename3);
8103
- var version = "1.5.0";
8105
+ var version = "1.5.2";
8104
8106
  try {
8105
8107
  const pkg = JSON.parse(readFileSync(join19(__dirname3, "..", "package.json"), "utf-8"));
8106
8108
  version = pkg.version;