wormclaude 1.0.230 → 1.0.232

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.js CHANGED
@@ -80,18 +80,39 @@ function backoff(attempt) {
80
80
  const base = Math.min(BASE_DELAY_MS * 2 ** attempt, MAX_DELAY_MS);
81
81
  return base + Math.random() * 0.25 * base; // jitter %25
82
82
  }
83
+ // Retry-After header'ı ms'ye çevir (saniye VEYA HTTP-date). Sunucu "şu kadar bekle" derse
84
+ // kör backoff yerine onu onurla → 429/503'te ne erken hammer'la ne fazla bekle. Yoksa null.
85
+ function retryAfterMs(res) {
86
+ try {
87
+ const h = res.headers?.get?.('retry-after');
88
+ if (!h)
89
+ return null;
90
+ const secs = Number(h);
91
+ if (Number.isFinite(secs))
92
+ return Math.min(Math.max(0, secs * 1000), 60000);
93
+ const when = Date.parse(h);
94
+ if (!Number.isNaN(when))
95
+ return Math.min(Math.max(0, when - Date.now()), 60000);
96
+ }
97
+ catch { }
98
+ return null;
99
+ }
83
100
  const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
84
101
  // Sadece BAĞLANTI kurulurken retry edilir (stream başladıktan sonra retry edilmez
85
102
  // — aksi halde kısmi metin tekrar gelir). HTTP başlamadan önceki hatalar güvenli.
86
103
  async function fetchWithRetry(url, init) {
87
104
  let lastErr;
88
105
  for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
106
+ let waitMs = backoff(attempt);
89
107
  try {
90
108
  const res = await fetch(url, init);
91
109
  if (res.ok)
92
110
  return res;
93
111
  if (!RETRYABLE_STATUS.has(res.status) || attempt === MAX_RETRIES)
94
112
  return res;
113
+ const ra = retryAfterMs(res); // sunucu bekleme süresi verdiyse onu onurla
114
+ if (ra != null)
115
+ waitMs = ra;
95
116
  try {
96
117
  await res.body?.cancel();
97
118
  }
@@ -107,7 +128,7 @@ async function fetchWithRetry(url, init) {
107
128
  if (attempt === MAX_RETRIES)
108
129
  throw e;
109
130
  }
110
- await sleep(backoff(attempt));
131
+ await sleep(waitMs);
111
132
  }
112
133
  throw lastErr ?? new Error('retry exhausted');
113
134
  }
package/dist/compact.js CHANGED
@@ -53,5 +53,19 @@ export async function runCompact(history, config, focus) {
53
53
  sys ?? { role: 'system', content: 'You are WormClaude.' },
54
54
  { role: 'user', content: 'Önceki konuşmanın özeti (bağlam):\n\n' + summary },
55
55
  ];
56
+ // DRIFT KORUMASI: özet, modelin GERÇEK mevcut isteğini bulanıklaştırıp yanlış davranışa
57
+ // (ör. "sunucuyu başlat" → saçma "paylaşamam") itebiliyor. Son kullanıcı mesajını AYNEN
58
+ // ekle ki model demirlensin. (Blackbox son tool-call/result çiftini korur; bizim minimal
59
+ // eşdeğerimiz: son kullanıcı isteğini verbatim tut.)
60
+ let lastUser = '';
61
+ for (let i = convo.length - 1; i >= 0; i--) {
62
+ if (convo[i].role === 'user' && typeof convo[i].content === 'string') {
63
+ lastUser = convo[i].content;
64
+ break;
65
+ }
66
+ }
67
+ if (lastUser && !/^\s*(önceki konuşmanın özeti|özellikle şuna odaklan)/i.test(lastUser)) {
68
+ newHist.push({ role: 'user', content: 'CURRENT REQUEST (continue exactly this — the summary above is only background, do not refuse or restart it):\n' + lastUser });
69
+ }
56
70
  return { history: newHist, summary };
57
71
  }
@@ -2,6 +2,36 @@
2
2
  // Qwen-14B + vLLM(hermes) parçalı JSON arg, index çakışması, ID'siz devam chunk'ı üretebilir.
3
3
  // Gemini streamingToolCallParser'dan uyarlandı; bizim {index, id?, name?, argChunk} delta'mıza göre sade.
4
4
  import { safeJsonParse } from './safejson.js';
5
+ // Yarım kalmış JSON'u onar: açık string'i kapat + string-dışı açık {/[ 'ları doğru sırada kapat.
6
+ // Stream son string arg'ı (ör. uzun file_path) yarıda keserse argümanlar {} olarak kaybolmasın.
7
+ function repairJson(buf) {
8
+ let s = buf, inStr = false, esc = false;
9
+ const stack = [];
10
+ for (const ch of s) {
11
+ if (inStr) {
12
+ if (esc)
13
+ esc = false;
14
+ else if (ch === '\\')
15
+ esc = true;
16
+ else if (ch === '"')
17
+ inStr = false;
18
+ continue;
19
+ }
20
+ if (ch === '"')
21
+ inStr = true;
22
+ else if (ch === '{')
23
+ stack.push('}');
24
+ else if (ch === '[')
25
+ stack.push(']');
26
+ else if (ch === '}' || ch === ']')
27
+ stack.pop();
28
+ }
29
+ if (inStr)
30
+ s += '"';
31
+ while (stack.length)
32
+ s += stack.pop();
33
+ return s;
34
+ }
5
35
  export class StreamingToolCallParser {
6
36
  buffers = new Map();
7
37
  depths = new Map();
@@ -88,6 +118,13 @@ export class StreamingToolCallParser {
88
118
  return { complete: true, value: JSON.parse(newBuffer) };
89
119
  }
90
120
  catch {
121
+ // Kapanmamış string onarımı: kapanış tırnağı stream'de düştüyse (inString) ekle + tekrar dene.
122
+ if (inString) {
123
+ try {
124
+ return { complete: true, value: JSON.parse(newBuffer + '"') };
125
+ }
126
+ catch { }
127
+ }
91
128
  return { complete: false };
92
129
  }
93
130
  }
@@ -100,8 +137,23 @@ export class StreamingToolCallParser {
100
137
  const m = this.meta.get(index);
101
138
  if (!m?.name)
102
139
  continue; // isimsiz parça → çağrı değil
103
- // Boş buffer = argümansız tool (ör. {}). safeJsonParse onarım dener, başarısızsa {} fallback.
104
- const args = buffer.trim() ? safeJsonParse(buffer, {}) : {};
140
+ // Boş buffer = argümansız tool (ör. {}). Dolu ise: JSON.parse (kapanmamış string ise
141
+ // kapatma-tırnağı ekle+dene) safeJsonParse fallback. Böylece stream son string arg'ı
142
+ // yarıda kesse bile argümanlar {} olarak kaybolmaz.
143
+ let args = {};
144
+ if (buffer.trim()) {
145
+ try {
146
+ args = JSON.parse(buffer);
147
+ }
148
+ catch {
149
+ try {
150
+ args = JSON.parse(repairJson(buffer));
151
+ } // string + açık brace'leri kapat
152
+ catch {
153
+ args = safeJsonParse(buffer, {});
154
+ }
155
+ }
156
+ }
105
157
  out.push({ id: m.id || `call_${index}`, name: m.name, args, index });
106
158
  }
107
159
  return out;
package/dist/theme.js CHANGED
@@ -16,4 +16,4 @@ export const theme = {
16
16
  synType: '#a78bfa', // tip/sınıf adları, sabitler
17
17
  synProp: '#e0e0e0', // özellik/anahtar adları
18
18
  };
19
- export const VERSION = '1.0.230';
19
+ export const VERSION = '1.0.232';
package/dist/tools.js CHANGED
@@ -874,7 +874,7 @@ async function execOne(call, hooks) {
874
874
  // 1) Girdi doğrulama
875
875
  const verr = validateToolInput(call.name, args);
876
876
  if (verr)
877
- return { ok: false, output: `Geçersiz girdi: ${verr}`, args };
877
+ return { ok: false, output: `INVALID_PARAMS for ${call.name}: ${verr}. Fix the arguments and call ${call.name} again with the correct parameters.`, args };
878
878
  // İNCELEME KİLİDİ: incele görevinde "nasıl kullanayım?" diye SORMA — açıklamayı üret.
879
879
  if (inspectLock && call.name === 'AskUserQuestion') {
880
880
  return { ok: false, output: 'This is an INSPECT/UNDERSTAND task — do NOT ask the user what to do with the files or how they want to use them. Just explain, in text, what the folder contains: the key files and their purpose, and what the project/collection is overall. Deliver that written explanation now.', args };
package/dist/tui.js CHANGED
@@ -435,6 +435,7 @@ export async function runTui() {
435
435
  const timer = setInterval(() => { spin++; if (busy && !perm && !ask)
436
436
  refresh(); }, 120);
437
437
  let lastSig = '', sameCount = 0, usedWeb = false, lastAnswer = '';
438
+ const sigWindow = []; // dönüşümlü/döngüsel loop tespiti (A,B,A,B) için son imzalar
438
439
  let consecFail = 0, totalFails = 0; // devre-kesici: olmayan araç/sözdizimi döngüsünü kır
439
440
  let reactiveRetried = false; // bağlam taşmasında bir kez compact+retry
440
441
  let secrecyRetried = false; // inceleme görevinde yanlış gizlilik-reddinde bir kez zorla-okut
@@ -568,6 +569,20 @@ export async function runTui() {
568
569
  printItem({ kind: 'note', text: t('tui.loopStop') });
569
570
  break;
570
571
  }
572
+ // Dönüşümlü/döngüsel loop: son 8 turun içinde aynı imza ≥4 kez → A,B,A,B... yakala
573
+ // (ardışık-aynı kontrolünün kaçırdığı 2+ döngü alternasyonu).
574
+ sigWindow.push(sig);
575
+ if (sigWindow.length > 8)
576
+ sigWindow.shift();
577
+ if (sigWindow.length >= 6) {
578
+ const counts = {};
579
+ for (const s of sigWindow)
580
+ counts[s] = (counts[s] || 0) + 1;
581
+ if (Math.max(...Object.values(counts)) >= 4) {
582
+ printItem({ kind: 'note', text: t('tui.loopStop') });
583
+ break;
584
+ }
585
+ }
571
586
  const results = await executeToolCalls(toolCalls, {
572
587
  confirm: (c, args) => new Promise((resolve) => {
573
588
  if (allowAll.has(c.name))
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wormclaude",
3
- "version": "1.0.230",
3
+ "version": "1.0.232",
4
4
  "description": "WormClaude CLI - uncensored security+code assistant (ink TUI, Claude-style)",
5
5
  "type": "module",
6
6
  "bin": {