@operatingtag/dsh-unclesam 1.3.3 → 1.3.4

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.
Files changed (55) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/README.ar.md +1 -1
  3. package/README.az.md +1 -1
  4. package/README.bg.md +1 -1
  5. package/README.bn.md +1 -1
  6. package/README.cs.md +1 -1
  7. package/README.da.md +1 -1
  8. package/README.de.md +1 -1
  9. package/README.el.md +1 -1
  10. package/README.es.md +1 -1
  11. package/README.fa.md +1 -1
  12. package/README.fi.md +1 -1
  13. package/README.fr.md +1 -1
  14. package/README.he.md +1 -1
  15. package/README.hi.md +1 -1
  16. package/README.hr.md +1 -1
  17. package/README.hu.md +1 -1
  18. package/README.id.md +1 -1
  19. package/README.it.md +1 -1
  20. package/README.ja.md +1 -1
  21. package/README.ka.md +1 -1
  22. package/README.kk.md +1 -1
  23. package/README.km.md +1 -1
  24. package/README.ko.md +1 -1
  25. package/README.lt.md +1 -1
  26. package/README.md +1 -1
  27. package/README.mr.md +1 -1
  28. package/README.ms.md +1 -1
  29. package/README.my.md +1 -1
  30. package/README.ne.md +1 -1
  31. package/README.nl.md +1 -1
  32. package/README.no.md +1 -1
  33. package/README.pl.md +1 -1
  34. package/README.pt.md +1 -1
  35. package/README.ro.md +1 -1
  36. package/README.ru.md +1 -1
  37. package/README.si.md +1 -1
  38. package/README.sk.md +1 -1
  39. package/README.sl.md +1 -1
  40. package/README.sr.md +1 -1
  41. package/README.sv.md +1 -1
  42. package/README.sw.md +1 -1
  43. package/README.ta.md +1 -1
  44. package/README.te.md +1 -1
  45. package/README.th.md +1 -1
  46. package/README.tl.md +1 -1
  47. package/README.tr.md +1 -1
  48. package/README.uk.md +1 -1
  49. package/README.ur.md +1 -1
  50. package/README.uz.md +1 -1
  51. package/README.vi.md +1 -1
  52. package/README.zh.md +1 -1
  53. package/index.js +31 -8
  54. package/lib/prompt.mjs +168 -4
  55. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -8,6 +8,38 @@ fixes the patch.
8
8
 
9
9
  ---
10
10
 
11
+ ## 1.3.4
12
+
13
+ **A tool call is now written `name({...})` instead of a JSON envelope, so the answer can stream.**
14
+
15
+ MEASURED: a reply that is nothing but `{"tool":…}` cannot be shown while it is written — `streamSafeCut`
16
+ returns 0 for the whole generation — so the harness sits empty for the entire turn and looks stuck. And a
17
+ brace is the commonest punctuation in technical prose, so `{` is a poor delimiter: it both blocks streaming
18
+ and invites false positives.
19
+
20
+ `replace_note({"text":"…"})` needs no instruction, because it is the shape models already produce, and it is
21
+ unambiguous once anchored on a tool name the model was actually offered. Prose streams until a known name
22
+ opens a parenthesis, instead of stopping at the first brace anywhere.
23
+
24
+ - **`parseToolCall` walks characters, not a regex**, so an argument may contain a parenthesis, a brace, an
25
+ escaped quote or a newline. `read()` parses as empty arguments; `see figure(2)` does not parse at all,
26
+ because `figure` was never offered.
27
+ - **The JSON envelope is still accepted**, fenced or bare, as a fallback — a model may emit it unprompted
28
+ and refusing it would throw away a working call.
29
+ - **`stripToolCall` removes whichever shape was used**, so neither reaches the transcript.
30
+ - **`streamSafeCut` holds from a known name plus `(`, or from a trailing fragment of a known name of three
31
+ characters or more.** The first version held any fragment, so a sentence ending in `r` stalled one
32
+ character back — `r` prefixes `read`. Found by a test, fixed before shipping.
33
+ - **`dev/test-call-form.mjs`** pins ten shapes that are calls, five that are not, and the stripping and
34
+ streaming behaviour. Two of its own assertions were written from guesswork and failed; they now measure.
35
+ - **`dev/test-web-tool-write.mjs` asserts the shape**, not just the outcome: the raw reply is captured through
36
+ `UNCLE_SAM_RAW_REPLY` and must start with `name(`. Measured live: the page replied
37
+ `replace_note({"text":"…"})` and the file changed, **4,062 ms**.
38
+
39
+ The raw capture is off unless that variable names a file, so nothing is written during ordinary use.
40
+
41
+ ---
42
+
11
43
  ## 1.3.3
12
44
 
13
45
  **The tool names were not enough: a call with the wrong argument name cannot be executed.**
package/README.ar.md CHANGED
@@ -250,7 +250,7 @@ Copyright (c) 2026 Darren T. Weldon، منشور تحت اسم Operating Tag.
250
250
  Uncle Sam إضافة مستقلة. غير مرتبطة بـ DeepSeek، ولا تحظى بتأييدهم أو دعمهم.
251
251
 
252
252
  ```
253
- @operatingtag/dsh-unclesam v1.3.3
253
+ @operatingtag/dsh-unclesam v1.3.4
254
254
  المؤلف Operating Tag
255
255
  الترخيص CC BY-NC-ND 4.0 — مجاني للاستخدام، لا للتغيير أو المشاركة أو البيع
256
256
  ```
package/README.az.md CHANGED
@@ -256,7 +256,7 @@ soruşun — aydın cavab və aydın qiymət alacaqsınız.
256
256
  Uncle Sam müstəqil plagindir. DeepSeek ilə bağlı deyil, onlar tərəfindən təsdiqlənməyib və dəstəklənmir.
257
257
 
258
258
  ```
259
- @operatingtag/dsh-unclesam v1.3.3
259
+ @operatingtag/dsh-unclesam v1.3.4
260
260
  Müəllif Operating Tag
261
261
  Lisenziya CC BY-NC-ND 4.0 — istifadə üçün pulsuz, dəyişdirmək, paylaşmaq və ya satmaq üçün deyil
262
262
  ```
package/README.bg.md CHANGED
@@ -259,7 +259,7 @@ Copyright (c) 2026 Darren T. Weldon, публикувано под името Op
259
259
  Uncle Sam е независима приставка. Не е свързана с DeepSeek, не е одобрена или поддържана от тях.
260
260
 
261
261
  ```
262
- @operatingtag/dsh-unclesam v1.3.3
262
+ @operatingtag/dsh-unclesam v1.3.4
263
263
  Автор Operating Tag
264
264
  Лиценз CC BY-NC-ND 4.0 — свободно за използване, не за промяна, споделяне или продажба
265
265
  ```
package/README.bn.md CHANGED
@@ -254,7 +254,7 @@ Copyright (c) 2026 Darren T. Weldon, Operating Tag নামে প্রকা
254
254
  Uncle Sam একটি স্বাধীন প্লাগইন। DeepSeek-এর সঙ্গে যুক্ত নয়, তাদের অনুমোদন বা সমর্থন নেই।
255
255
 
256
256
  ```
257
- @operatingtag/dsh-unclesam v1.3.3
257
+ @operatingtag/dsh-unclesam v1.3.4
258
258
  লেখক Operating Tag
259
259
  লাইসেন্স CC BY-NC-ND 4.0 — ব্যবহারে বিনামূল্যে, পরিবর্তন, শেয়ার বা বিক্রিতে নয়
260
260
  ```
package/README.cs.md CHANGED
@@ -256,7 +256,7 @@ Závazný je soubor [LICENSE](LICENSE) a začíná stejným shrnutím v jednoduc
256
256
  Uncle Sam je nezávislý plugin. Není spojený s DeepSeek, není jimi schválený ani podporovaný.
257
257
 
258
258
  ```
259
- @operatingtag/dsh-unclesam v1.3.3
259
+ @operatingtag/dsh-unclesam v1.3.4
260
260
  Autor Operating Tag
261
261
  Licence CC BY-NC-ND 4.0 — zdarma k použití, ne ke změně, sdílení nebo prodeji
262
262
  ```
package/README.da.md CHANGED
@@ -257,7 +257,7 @@ Filen [LICENSE](LICENSE) er den gældende, og den begynder med samme resumé på
257
257
  Uncle Sam er et uafhængigt plugin. Ikke knyttet til DeepSeek, ikke godkendt eller støttet af dem.
258
258
 
259
259
  ```
260
- @operatingtag/dsh-unclesam v1.3.3
260
+ @operatingtag/dsh-unclesam v1.3.4
261
261
  Forfatter Operating Tag
262
262
  Licens CC BY-NC-ND 4.0 — frit at bruge, ikke at ændre, dele eller sælge
263
263
  ```
package/README.de.md CHANGED
@@ -270,7 +270,7 @@ Englisch.
270
270
  Uncle Sam ist ein unabhängiges Plugin. Nicht mit DeepSeek verbunden, von ihnen weder unterstützt noch gefördert.
271
271
 
272
272
  ```
273
- @operatingtag/dsh-unclesam v1.3.3
273
+ @operatingtag/dsh-unclesam v1.3.4
274
274
  Autor Operating Tag
275
275
  Lizenz CC BY-NC-ND 4.0 — frei zur Nutzung, nicht zum Ändern, Weitergabe oder Verkauf
276
276
  ```
package/README.el.md CHANGED
@@ -266,7 +266,7 @@ Copyright (c) 2026 Darren T. Weldon, δημοσιευμένο με το όνομ
266
266
  Το Uncle Sam είναι ανεξάρτητο πρόσθετο. Δεν συνδέεται με τη DeepSeek, δεν εγκρίνεται ούτε υποστηρίζεται από αυτούς.
267
267
 
268
268
  ```
269
- @operatingtag/dsh-unclesam v1.3.3
269
+ @operatingtag/dsh-unclesam v1.3.4
270
270
  Δημιουργός Operating Tag
271
271
  Άδεια CC BY-NC-ND 4.0 — ελεύθερο για χρήση, όχι για αλλαγή, κοινή χρήση ή πώληση
272
272
  ```
package/README.es.md CHANGED
@@ -264,7 +264,7 @@ El archivo [LICENSE](LICENSE) es el que rige, y abre con el mismo resumen en ing
264
264
  Uncle Sam es un plugin independiente. No está afiliado, respaldado ni soportado por DeepSeek.
265
265
 
266
266
  ```
267
- @operatingtag/dsh-unclesam v1.3.3
267
+ @operatingtag/dsh-unclesam v1.3.4
268
268
  Autor Operating Tag
269
269
  Licencia CC BY-NC-ND 4.0 — gratis para usar, no para cambiar, compartir o vender
270
270
  ```
package/README.fa.md CHANGED
@@ -256,7 +256,7 @@ Copyright (c) 2026 Darren T. Weldon، منتشرشده با نام Operating Tag
256
256
  Uncle Sam یک افزونه مستقل است. وابسته به DeepSeek نیست، تأیید یا پشتیبانی نمی‌شود.
257
257
 
258
258
  ```
259
- @operatingtag/dsh-unclesam v1.3.3
259
+ @operatingtag/dsh-unclesam v1.3.4
260
260
  نویسنده Operating Tag
261
261
  پروانه CC BY-NC-ND 4.0 — آزاد برای استفاده، نه برای تغییر، اشتراک یا فروش
262
262
  ```
package/README.fi.md CHANGED
@@ -258,7 +258,7 @@ Tiedosto [LICENSE](LICENSE) on ratkaiseva, ja se alkaa samalla yhteenvedolla yks
258
258
  Uncle Sam on itsenäinen lisäosa. Ei sidoksissa DeepSeekiin, ei heidän hyväksymänsä tai tukemansa.
259
259
 
260
260
  ```
261
- @operatingtag/dsh-unclesam v1.3.3
261
+ @operatingtag/dsh-unclesam v1.3.4
262
262
  Tekijä Operating Tag
263
263
  Lisenssi CC BY-NC-ND 4.0 — vapaa käytettäväksi, ei muutettavaksi, jaettavaksi tai myytäväksi
264
264
  ```
package/README.fr.md CHANGED
@@ -268,7 +268,7 @@ Le fichier [LICENSE](LICENSE) fait foi, et il s'ouvre sur le même résumé en a
268
268
  Uncle Sam est un plugin indépendant. Non affilié à DeepSeek, ni approuvé ni soutenu par eux.
269
269
 
270
270
  ```
271
- @operatingtag/dsh-unclesam v1.3.3
271
+ @operatingtag/dsh-unclesam v1.3.4
272
272
  Auteur Operating Tag
273
273
  Licence CC BY-NC-ND 4.0 — gratuit à l'usage, pas au changement, au partage ou à la vente
274
274
  ```
package/README.he.md CHANGED
@@ -248,7 +248,7 @@ Copyright (c) 2026 Darren T. Weldon, פורסם תחת השם Operating Tag.
248
248
  Uncle Sam הוא תוסף עצמאי. לא קשור ל‑DeepSeek, לא מאושר ולא נתמך על ידם.
249
249
 
250
250
  ```
251
- @operatingtag/dsh-unclesam v1.3.3
251
+ @operatingtag/dsh-unclesam v1.3.4
252
252
  יוצר Operating Tag
253
253
  רישיון CC BY-NC-ND 4.0 — חופשי לשימוש, לא לשינוי, שיתוף או מכירה
254
254
  ```
package/README.hi.md CHANGED
@@ -259,7 +259,7 @@ Copyright (c) 2026 Darren T. Weldon, Operating Tag नाम से प्रक
259
259
  Uncle Sam एक स्वतंत्र प्लगइन है। DeepSeek से जुड़ा नहीं, उनका समर्थन या अनुमोदन नहीं।
260
260
 
261
261
  ```
262
- @operatingtag/dsh-unclesam v1.3.3
262
+ @operatingtag/dsh-unclesam v1.3.4
263
263
  लेखक Operating Tag
264
264
  लाइसेंस CC BY-NC-ND 4.0 — इस्तेमाल मुफ़्त, बदलना, बाँटना या बेचना नहीं
265
265
  ```
package/README.hr.md CHANGED
@@ -256,7 +256,7 @@ Datoteka [LICENSE](LICENSE) je obvezujuća i počinje istim sažetkom na jednost
256
256
  Uncle Sam je neovisni dodatak. Nije povezan s DeepSeekom, nije odobren ni podržan od njih.
257
257
 
258
258
  ```
259
- @operatingtag/dsh-unclesam v1.3.3
259
+ @operatingtag/dsh-unclesam v1.3.4
260
260
  Autor Operating Tag
261
261
  Licencija CC BY-NC-ND 4.0 — besplatno za korištenje, ne za izmjenu, dijeljenje ili prodaju
262
262
  ```
package/README.hu.md CHANGED
@@ -260,7 +260,7 @@ A [LICENSE](LICENSE) fájl a mérvadó, és ugyanazzal az egyszerű angol össze
260
260
  Az Uncle Sam független bővítmény. Nem kapcsolódik a DeepSeekhez, ő nem hagyja jóvá és nem támogatja.
261
261
 
262
262
  ```
263
- @operatingtag/dsh-unclesam v1.3.3
263
+ @operatingtag/dsh-unclesam v1.3.4
264
264
  Szerző Operating Tag
265
265
  Licenc CC BY-NC-ND 4.0 — szabadon használható, nem módosítható, osztható meg vagy adható el
266
266
  ```
package/README.id.md CHANGED
@@ -260,7 +260,7 @@ Berkas [LICENSE](LICENSE) yang mengikat, dan ia dibuka dengan ringkasan yang sam
260
260
  Uncle Sam adalah plugin independen. Tidak berafiliasi dengan DeepSeek, tidak didukung maupun disponsori oleh mereka.
261
261
 
262
262
  ```
263
- @operatingtag/dsh-unclesam v1.3.3
263
+ @operatingtag/dsh-unclesam v1.3.4
264
264
  Penulis Operating Tag
265
265
  Lisensi CC BY-NC-ND 4.0 — bebas dipakai, tidak untuk diubah, dibagikan, atau dijual
266
266
  ```
package/README.it.md CHANGED
@@ -264,7 +264,7 @@ Il file [LICENSE](LICENSE) è quello che fa fede, e si apre con lo stesso riassu
264
264
  Uncle Sam è un plugin indipendente. Non affiliato a DeepSeek, né approvato né supportato da loro.
265
265
 
266
266
  ```
267
- @operatingtag/dsh-unclesam v1.3.3
267
+ @operatingtag/dsh-unclesam v1.3.4
268
268
  Autore Operating Tag
269
269
  Licenza CC BY-NC-ND 4.0 — libero da usare, non da modificare, condividere o vendere
270
270
  ```
package/README.ja.md CHANGED
@@ -259,7 +259,7 @@ Copyright (c) 2026 Darren T. Weldon、Operating Tag 名義で公開。
259
259
  Uncle Sam は独立したプラグインです。DeepSeek とは関係がなく、承認も支援も受けていません。
260
260
 
261
261
  ```
262
- @operatingtag/dsh-unclesam v1.3.3
262
+ @operatingtag/dsh-unclesam v1.3.4
263
263
  作者 Operating Tag
264
264
  ライセンス CC BY-NC-ND 4.0 — 使用は自由、変更・共有・販売は不可
265
265
  ```
package/README.ka.md CHANGED
@@ -257,7 +257,7 @@ Copyright (c) 2026 Darren T. Weldon, გამოქვეყნებული
257
257
  Uncle Sam დამოუკიდებელი დანამატია. არ არის დაკავშირებული DeepSeek-თან, მათ მიერ არ არის დამტკიცებული ან მხარდაჭერილი.
258
258
 
259
259
  ```
260
- @operatingtag/dsh-unclesam v1.3.3
260
+ @operatingtag/dsh-unclesam v1.3.4
261
261
  ავტორი Operating Tag
262
262
  ლიცენზია CC BY-NC-ND 4.0 — თავისუფალი გამოსაყენებლად, არა შესაცვლელად, გასაზიარებლად ან გასაყიდად
263
263
  ```
package/README.kk.md CHANGED
@@ -258,7 +258,7 @@ Operating Tag-тан сұраңыз — нақты жауап және нақт
258
258
  Uncle Sam — тәуелсіз плагин. DeepSeek-пен байланысты емес, олар мақұлдамаған немесе қолдамаған.
259
259
 
260
260
  ```
261
- @operatingtag/dsh-unclesam v1.3.3
261
+ @operatingtag/dsh-unclesam v1.3.4
262
262
  Авторы Operating Tag
263
263
  Лицензия CC BY-NC-ND 4.0 — пайдалануға тегін, өзгертуге, бөлісуге немесе сатуға емес
264
264
  ```
package/README.km.md CHANGED
@@ -253,7 +253,7 @@ Tag — អ្នកនឹងទទួលបានចម្លើយច្បា
253
253
  Uncle Sam គឺជាកម្មវិធីបន្ថែមឯករាជ្យ។ មិនពាក់ព័ន្ធនឹង DeepSeek មិនត្រូវបានអនុម័ត ឬគាំទ្រដោយពួកគេ។
254
254
 
255
255
  ```
256
- @operatingtag/dsh-unclesam v1.3.3
256
+ @operatingtag/dsh-unclesam v1.3.4
257
257
  អ្នកនិពន្ធ Operating Tag
258
258
  អាជ្ញាបណ្ណ CC BY-NC-ND 4.0 — ប្រើឥតគិតថ្លៃ មិនមែនសម្រាប់កែប្រែ ចែករំលែក ឬលក់
259
259
  ```
package/README.ko.md CHANGED
@@ -254,7 +254,7 @@ Copyright (c) 2026 Darren T. Weldon, Operating Tag 이름으로 공개.
254
254
  Uncle Sam은 독립적인 플러그인입니다. DeepSeek과 무관하며, 그들의 보증이나 지원을 받지 않습니다.
255
255
 
256
256
  ```
257
- @operatingtag/dsh-unclesam v1.3.3
257
+ @operatingtag/dsh-unclesam v1.3.4
258
258
  저자 Operating Tag
259
259
  라이선스 CC BY-NC-ND 4.0 — 사용은 자유, 변경·공유·판매는 불가
260
260
  ```
package/README.lt.md CHANGED
@@ -257,7 +257,7 @@ Failas [LICENSE](LICENSE) yra privalomas, ir jis prasideda ta pačia santrauka p
257
257
  Uncle Sam yra nepriklausomas papildinys. Nesusijęs su DeepSeek, jų nepatvirtintas ir neremiamas.
258
258
 
259
259
  ```
260
- @operatingtag/dsh-unclesam v1.3.3
260
+ @operatingtag/dsh-unclesam v1.3.4
261
261
  Autorius Operating Tag
262
262
  Licencija CC BY-NC-ND 4.0 — nemokama naudoti, ne keisti, bendrinti ar parduoti
263
263
  ```
package/README.md CHANGED
@@ -257,7 +257,7 @@ The [LICENSE](LICENSE) file governs, and it opens with the same summary in plain
257
257
  Uncle Sam is an independent plugin. Not affiliated with, endorsed by, or supported by DeepSeek.
258
258
 
259
259
  ```
260
- @operatingtag/dsh-unclesam v1.3.3
260
+ @operatingtag/dsh-unclesam v1.3.4
261
261
  Author Operating Tag
262
262
  Licence CC BY-NC-ND 4.0 — free to use, not to change, share or sell
263
263
  ```
package/README.mr.md CHANGED
@@ -254,7 +254,7 @@ Copyright (c) 2026 Darren T. Weldon, Operating Tag नावाने प्र
254
254
  Uncle Sam एक स्वतंत्र प्लगइन आहे. DeepSeek शी संबंधित नाही, त्यांची मान्यता किंवा समर्थन नाही.
255
255
 
256
256
  ```
257
- @operatingtag/dsh-unclesam v1.3.3
257
+ @operatingtag/dsh-unclesam v1.3.4
258
258
  लेखक Operating Tag
259
259
  परवाना CC BY-NC-ND 4.0 — वापरण्यास मोफत, बदलण्यास, सामायिक करण्यास किंवा विकण्यास नाही
260
260
  ```
package/README.ms.md CHANGED
@@ -261,7 +261,7 @@ mudah.
261
261
  Uncle Sam ialah pemalam bebas. Tidak berkaitan dengan DeepSeek, tidak disokong atau ditaja oleh mereka.
262
262
 
263
263
  ```
264
- @operatingtag/dsh-unclesam v1.3.3
264
+ @operatingtag/dsh-unclesam v1.3.4
265
265
  Penulis Operating Tag
266
266
  Lesen CC BY-NC-ND 4.0 — bebas digunakan, bukan untuk diubah, dikongsi atau dijual
267
267
  ```
package/README.my.md CHANGED
@@ -262,7 +262,7 @@ Copyright (c) 2026 Darren T. Weldon, Operating Tag အမည်ဖြင့်
262
262
  Uncle Sam သည် သီးခြားလွတ်လပ်သော plugin ဖြစ်သည်။ DeepSeek နှင့် မသက်ဆိုင်ပါ၊ ၎င်းတို့က အတည်မပြု၊ မပံ့ပိုးပါ။
263
263
 
264
264
  ```
265
- @operatingtag/dsh-unclesam v1.3.3
265
+ @operatingtag/dsh-unclesam v1.3.4
266
266
  စာရေးသူ Operating Tag
267
267
  လိုင်စင် CC BY-NC-ND 4.0 — အသုံးပြုရန် အခမဲ့၊ ပြောင်းလဲရန်၊ မျှဝေရန် သို့မဟုတ် ရောင်းရန် မဟုတ်
268
268
  ```
package/README.ne.md CHANGED
@@ -254,7 +254,7 @@ Copyright (c) 2026 Darren T. Weldon, Operating Tag नाममा प्रक
254
254
  Uncle Sam स्वतन्त्र प्लगइन हो। DeepSeek सँग सम्बन्धित छैन, उनीहरूको स्वीकृति वा समर्थन छैन।
255
255
 
256
256
  ```
257
- @operatingtag/dsh-unclesam v1.3.3
257
+ @operatingtag/dsh-unclesam v1.3.4
258
258
  लेखक Operating Tag
259
259
  इजाजतपत्र CC BY-NC-ND 4.0 — प्रयोग गर्न निःशुल्क, परिवर्तन, बाँड्न वा बेच्न होइन
260
260
  ```
package/README.nl.md CHANGED
@@ -262,7 +262,7 @@ Het bestand [LICENSE](LICENSE) is bindend, en het begint met dezelfde samenvatti
262
262
  Uncle Sam is een onafhankelijke plugin. Niet verbonden met DeepSeek, door hen niet goedgekeurd of ondersteund.
263
263
 
264
264
  ```
265
- @operatingtag/dsh-unclesam v1.3.3
265
+ @operatingtag/dsh-unclesam v1.3.4
266
266
  Auteur Operating Tag
267
267
  Licentie CC BY-NC-ND 4.0 — vrij om te gebruiken, niet om te wijzigen, delen of verkopen
268
268
  ```
package/README.no.md CHANGED
@@ -257,7 +257,7 @@ Filen [LICENSE](LICENSE) er den gjeldende, og den begynner med det samme sammend
257
257
  Uncle Sam er en uavhengig utvidelse. Ikke knyttet til DeepSeek, ikke godkjent eller støttet av dem.
258
258
 
259
259
  ```
260
- @operatingtag/dsh-unclesam v1.3.3
260
+ @operatingtag/dsh-unclesam v1.3.4
261
261
  Forfatter Operating Tag
262
262
  Lisens CC BY-NC-ND 4.0 — fritt å bruke, ikke å endre, dele eller selge
263
263
  ```
package/README.pl.md CHANGED
@@ -262,7 +262,7 @@ Wiążący jest plik [LICENSE](LICENSE), a otwiera się tym samym podsumowaniem
262
262
  Uncle Sam to niezależna wtyczka. Niepowiązana z DeepSeek, nie firmowana ani nie wspierana przez nich.
263
263
 
264
264
  ```
265
- @operatingtag/dsh-unclesam v1.3.3
265
+ @operatingtag/dsh-unclesam v1.3.4
266
266
  Autor Operating Tag
267
267
  Licencja CC BY-NC-ND 4.0 — wolno używać, nie wolno zmieniać, udostępniać ani sprzedawać
268
268
  ```
package/README.pt.md CHANGED
@@ -261,7 +261,7 @@ O arquivo [LICENSE](LICENSE) é o que vale, e ele abre com o mesmo resumo em ing
261
261
  O Uncle Sam é um plugin independente. Não afiliado ao DeepSeek, nem endossado nem apoiado por ele.
262
262
 
263
263
  ```
264
- @operatingtag/dsh-unclesam v1.3.3
264
+ @operatingtag/dsh-unclesam v1.3.4
265
265
  Autor Operating Tag
266
266
  Licença CC BY-NC-ND 4.0 — livre para usar, não para alterar, compartilhar ou vender
267
267
  ```
package/README.ro.md CHANGED
@@ -258,7 +258,7 @@ Fișierul [LICENSE](LICENSE) este cel care guvernează și începe cu același r
258
258
  Uncle Sam este un plugin independent. Neafiliat cu DeepSeek, neaprobat și nesprijinit de ei.
259
259
 
260
260
  ```
261
- @operatingtag/dsh-unclesam v1.3.3
261
+ @operatingtag/dsh-unclesam v1.3.4
262
262
  Autor Operating Tag
263
263
  Licență CC BY-NC-ND 4.0 — liber de folosit, nu de modificat, partajat sau vândut
264
264
  ```
package/README.ru.md CHANGED
@@ -265,7 +265,7 @@ International. Copyright (c) 2026 Darren T. Weldon, опубликовано п
265
265
  Uncle Sam — независимый плагин. Не связан с DeepSeek, не одобрен и не поддерживается им.
266
266
 
267
267
  ```
268
- @operatingtag/dsh-unclesam v1.3.3
268
+ @operatingtag/dsh-unclesam v1.3.4
269
269
  Автор Operating Tag
270
270
  Лицензия CC BY-NC-ND 4.0 — бесплатно для использования, не для изменения, передачи или продажи
271
271
  ```
package/README.si.md CHANGED
@@ -255,7 +255,7 @@ Copyright (c) 2026 Darren T. Weldon, Operating Tag නාමයෙන් ප්
255
255
  Uncle Sam ස්වාධීන ප්ලගීනයකි. DeepSeek සමඟ සම්බන්ධ නැත, ඔවුන් අනුමත හෝ අනුග්‍රහය නොදක්වයි.
256
256
 
257
257
  ```
258
- @operatingtag/dsh-unclesam v1.3.3
258
+ @operatingtag/dsh-unclesam v1.3.4
259
259
  කතෘ Operating Tag
260
260
  බලපත්‍රය CC BY-NC-ND 4.0 — භාවිතයට නොමිලේ, වෙනස් කිරීමට, බෙදා ගැනීමට හෝ විකිණීමට නොවේ
261
261
  ```
package/README.sk.md CHANGED
@@ -257,7 +257,7 @@ Záväzný je súbor [LICENSE](LICENSE) a začína rovnakým zhrnutím v jednodu
257
257
  Uncle Sam je nezávislý doplnok. Nie je spojený s DeepSeek, nie je nimi schválený ani podporovaný.
258
258
 
259
259
  ```
260
- @operatingtag/dsh-unclesam v1.3.3
260
+ @operatingtag/dsh-unclesam v1.3.4
261
261
  Autor Operating Tag
262
262
  Licencia CC BY-NC-ND 4.0 — zadarmo na používanie, nie na zmenu, zdieľanie alebo predaj
263
263
  ```
package/README.sl.md CHANGED
@@ -258,7 +258,7 @@ Zavezujoča je datoteka [LICENSE](LICENSE) in se začne z istim povzetkom v prep
258
258
  Uncle Sam je neodvisen vtičnik. Ni povezan z DeepSeek, ni odobren ali podprt z njihove strani.
259
259
 
260
260
  ```
261
- @operatingtag/dsh-unclesam v1.3.3
261
+ @operatingtag/dsh-unclesam v1.3.4
262
262
  Avtor Operating Tag
263
263
  Licenca CC BY-NC-ND 4.0 — brezplačno za uporabo, ne za spreminjanje, deljenje ali prodajo
264
264
  ```
package/README.sr.md CHANGED
@@ -258,7 +258,7 @@ Copyright (c) 2026 Darren T. Weldon, објављено под именом Oper
258
258
  Uncle Sam је независни додатак. Није повезан са DeepSeek, није одобрен ни подржан од њих.
259
259
 
260
260
  ```
261
- @operatingtag/dsh-unclesam v1.3.3
261
+ @operatingtag/dsh-unclesam v1.3.4
262
262
  Аутор Operating Tag
263
263
  Лиценца CC BY-NC-ND 4.0 — бесплатно за употребу, не за измену, дељење или продају
264
264
  ```
package/README.sv.md CHANGED
@@ -259,7 +259,7 @@ Filen [LICENSE](LICENSE) är den som gäller, och den börjar med samma sammanfa
259
259
  Uncle Sam är ett fristående insticksprogram. Inte knutet till DeepSeek, inte godkänt eller stött av dem.
260
260
 
261
261
  ```
262
- @operatingtag/dsh-unclesam v1.3.3
262
+ @operatingtag/dsh-unclesam v1.3.4
263
263
  Författare Operating Tag
264
264
  Licens CC BY-NC-ND 4.0 — fritt att använda, inte att ändra, dela eller sälja
265
265
  ```
package/README.sw.md CHANGED
@@ -259,7 +259,7 @@ Faili [LICENSE](LICENSE) ndiyo inayotumika, na inaanza na muhtasari huo huo kwa
259
259
  Uncle Sam ni programu-jalizi huru. Haijahusishwa na DeepSeek, haijaidhinishwa wala kuungwa mkono nao.
260
260
 
261
261
  ```
262
- @operatingtag/dsh-unclesam v1.3.3
262
+ @operatingtag/dsh-unclesam v1.3.4
263
263
  Mwandishi Operating Tag
264
264
  Leseni CC BY-NC-ND 4.0 — huru kutumika, si kubadilisha, kushiriki au kuuza
265
265
  ```
package/README.ta.md CHANGED
@@ -262,7 +262,7 @@ Tag ஐக் கேளுங்கள் — தெளிவான பதில
262
262
  Uncle Sam ஒரு சுயாதீனச் செருகல். DeepSeek உடன் தொடர்பில்லை, அவர்களின் ஒப்புதல் அல்லது ஆதரவு இல்லை.
263
263
 
264
264
  ```
265
- @operatingtag/dsh-unclesam v1.3.3
265
+ @operatingtag/dsh-unclesam v1.3.4
266
266
  ஆசிரியர் Operating Tag
267
267
  உரிமம் CC BY-NC-ND 4.0 — பயன்படுத்த இலவசம், மாற்ற, பகிர அல்லது விற்க அல்ல
268
268
  ```
package/README.te.md CHANGED
@@ -256,7 +256,7 @@ Tag ను అడగండి — స్పష్టమైన సమాధాన
256
256
  Uncle Sam స్వతంత్ర ప్లగిన్. DeepSeek తో సంబంధం లేదు, వారి ఆమోదం లేదా మద్దతు లేదు.
257
257
 
258
258
  ```
259
- @operatingtag/dsh-unclesam v1.3.3
259
+ @operatingtag/dsh-unclesam v1.3.4
260
260
  రచయిత Operating Tag
261
261
  లైసెన్స్ CC BY-NC-ND 4.0 — వాడటానికి ఉచితం, మార్చడానికి, పంచుకోవడానికి లేదా అమ్మడానికి కాదు
262
262
  ```
package/README.th.md CHANGED
@@ -252,7 +252,7 @@ Operating Tag แล้วคุณจะได้คำตอบที่ชั
252
252
  Uncle Sam เป็นปลั๊กอินอิสระ ไม่เกี่ยวข้องกับ DeepSeek ไม่ได้รับการรับรองหรือสนับสนุนจากพวกเขา
253
253
 
254
254
  ```
255
- @operatingtag/dsh-unclesam v1.3.3
255
+ @operatingtag/dsh-unclesam v1.3.4
256
256
  ผู้เขียน Operating Tag
257
257
  สัญญาอนุญาต CC BY-NC-ND 4.0 — ใช้ฟรี ไม่ใช่เพื่อดัดแปลง แบ่งปัน หรือขาย
258
258
  ```
package/README.tl.md CHANGED
@@ -265,7 +265,7 @@ Ang file na [LICENSE](LICENSE) ang namamahala, at nagsisimula ito sa parehong bu
265
265
  Ang Uncle Sam ay isang malayang plugin. Hindi kaanib sa DeepSeek, hindi inendorso o sinusuportahan nila.
266
266
 
267
267
  ```
268
- @operatingtag/dsh-unclesam v1.3.3
268
+ @operatingtag/dsh-unclesam v1.3.4
269
269
  May-akda Operating Tag
270
270
  Lisensya CC BY-NC-ND 4.0 — libre gamitin, hindi para baguhin, ibahagi o ibenta
271
271
  ```
package/README.tr.md CHANGED
@@ -261,7 +261,7 @@ Uncle Sam bağımsız bir eklentidir. DeepSeek ile bağlantılı değildir, onla
261
261
  desteklenmemiştir.
262
262
 
263
263
  ```
264
- @operatingtag/dsh-unclesam v1.3.3
264
+ @operatingtag/dsh-unclesam v1.3.4
265
265
  Yazar Operating Tag
266
266
  Lisans CC BY-NC-ND 4.0 — kullanması serbest, değiştirmesi, paylaşması veya satması değil
267
267
  ```
package/README.uk.md CHANGED
@@ -261,7 +261,7 @@ International. Copyright (c) 2026 Darren T. Weldon, опубліковано п
261
261
  Uncle Sam — незалежний плагін. Не пов'язаний із DeepSeek, не схвалений і не підтриманий ними.
262
262
 
263
263
  ```
264
- @operatingtag/dsh-unclesam v1.3.3
264
+ @operatingtag/dsh-unclesam v1.3.4
265
265
  Автор Operating Tag
266
266
  Ліцензія CC BY-NC-ND 4.0 — вільно використовувати, не змінювати, передавати чи продавати
267
267
  ```
package/README.ur.md CHANGED
@@ -257,7 +257,7 @@ Copyright (c) 2026 Darren T. Weldon، Operating Tag نام سے شائع شدہ
257
257
  Uncle Sam ایک خودمختار پلگ ان ہے۔ DeepSeek سے وابستہ نہیں، ان کی توثیق یا معاونت حاصل نہیں۔
258
258
 
259
259
  ```
260
- @operatingtag/dsh-unclesam v1.3.3
260
+ @operatingtag/dsh-unclesam v1.3.4
261
261
  مصنف Operating Tag
262
262
  لائسنس CC BY-NC-ND 4.0 — استعمال کے لیے مفت، تبدیلی، اشتراک یا فروخت کے لیے نہیں
263
263
  ```
package/README.uz.md CHANGED
@@ -258,7 +258,7 @@ Tag dan so'rang — aniq javob va aniq narx olasiz.
258
258
  Uncle Sam — mustaqil plagin. DeepSeek bilan bog'liq emas, ular tomonidan ma'qullanmagan yoki qo'llab-quvvatlanmagan.
259
259
 
260
260
  ```
261
- @operatingtag/dsh-unclesam v1.3.3
261
+ @operatingtag/dsh-unclesam v1.3.4
262
262
  Muallif Operating Tag
263
263
  Litsenziya CC BY-NC-ND 4.0 — foydalanish uchun bepul, o'zgartirish, ulashish yoki sotish uchun emas
264
264
  ```
package/README.vi.md CHANGED
@@ -259,7 +259,7 @@ Tệp [LICENSE](LICENSE) là văn bản có hiệu lực, và nó mở đầu b
259
259
  Uncle Sam là một plugin độc lập. Không liên kết với DeepSeek, không được họ chứng thực hay hỗ trợ.
260
260
 
261
261
  ```
262
- @operatingtag/dsh-unclesam v1.3.3
262
+ @operatingtag/dsh-unclesam v1.3.4
263
263
  Tác giả Operating Tag
264
264
  Giấy phép CC BY-NC-ND 4.0 — miễn phí để dùng, không được sửa, chia sẻ hay bán
265
265
  ```
package/README.zh.md CHANGED
@@ -244,7 +244,7 @@ Copyright (c) 2026 Darren T. Weldon,以 Operating Tag 名义发布。
244
244
  Uncle Sam 是一个独立插件。与 DeepSeek 无关联,未获其背书,也未获其支持。
245
245
 
246
246
  ```
247
- @operatingtag/dsh-unclesam v1.3.3
247
+ @operatingtag/dsh-unclesam v1.3.4
248
248
  作者 Operating Tag
249
249
  许可 CC BY-NC-ND 4.0 —— 可免费使用,不可修改、共享或出售
250
250
  ```
package/index.js CHANGED
@@ -27,8 +27,8 @@
27
27
  * prose instead of a call, and the harness sees no tool use that turn.
28
28
  */
29
29
 
30
- import { ChatSession, WebChatError } from './lib/engine.mjs';
31
- import { buildPrompt, messageText, parseToolCall, stripToolCall, streamSafeCut } from './lib/prompt.mjs';
30
+ import { writeFileSync } from 'node:fs';
31
+ import { ChatSession, WebChatError } from './lib/engine.mjs';import { buildPrompt, messageText, parseToolCall, stripToolCall, streamSafeCut } from './lib/prompt.mjs';
32
32
  import { SavingsMeter, estimateTokens as estimateTokensForSavings } from './lib/savings.mjs';
33
33
  import { Locales } from './lib/i18n.mjs';
34
34
  import { Prefs, prefsPath, profileDir } from './lib/prefs.mjs';
@@ -457,6 +457,13 @@ export function apply(ctx, config = {}) {
457
457
  }
458
458
 
459
459
  const wantTools = Array.isArray(options.tools) && options.tools.length > 0;
460
+ /*
461
+ * The names this turn offered, exactly as the page was told them.
462
+ *
463
+ * `name({...})` is only a call when `name` is one of these - that is what makes the form unambiguous.
464
+ * Without the list, prose like `see figure(2)` would look like a call.
465
+ */
466
+ const toolNames = wantTools ? options.tools.map((t) => t?.name).filter(Boolean) : [];
460
467
  const switches = switchesFor(model, settings);
461
468
  // A search answer is delivered as two blocks: what was searched and where it looked, then the
462
469
  // answer. That means the text cannot stream while search is on - the sources are only readable
@@ -556,6 +563,22 @@ export function apply(ctx, config = {}) {
556
563
  if (chunk.done) {
557
564
  assembled = chunk.text;
558
565
  if (chunk.sources) sources = chunk.sources;
566
+ /*
567
+ * Optional raw capture, for tests that need to see the reply AS THE PAGE WROTE IT.
568
+ *
569
+ * A recovered call proves the parser worked, not which shape the model chose - and the shape is
570
+ * the thing being changed when the call format changes. Writing the raw reply to a path the test
571
+ * names turns "the call parsed" into "the model emitted this, character for character".
572
+ *
573
+ * Off unless UNCLE_SAM_RAW_REPLY names a file, so nothing is written during ordinary use.
574
+ */
575
+ if (process.env.UNCLE_SAM_RAW_REPLY) {
576
+ try {
577
+ writeFileSync(process.env.UNCLE_SAM_RAW_REPLY, chunk.text ?? '', 'utf8');
578
+ } catch {
579
+ /* a diagnostic must never break the turn */
580
+ }
581
+ }
559
582
  continue;
560
583
  }
561
584
  if (!chunk.text) continue;
@@ -573,7 +596,7 @@ export function apply(ctx, config = {}) {
573
596
  * grow into {"tool":...}. Ordinary prose passes straight through, braces included, so the stream
574
597
  * stays live; it simply stops one brace early when a call might be starting.
575
598
  */
576
- const safe = streamSafeCut(assembled);
599
+ const safe = streamSafeCut(assembled, toolNames);
577
600
  if (safe > shown) {
578
601
  const out = assembled.slice(shown, safe);
579
602
  shown = safe;
@@ -634,7 +657,7 @@ export function apply(ctx, config = {}) {
634
657
  yield { type: 'text-delta', index: 0, text: summaryWithLabel };
635
658
  yield { type: 'block-end', index: 0, block: { type: 'text', text: summaryWithLabel } };
636
659
 
637
- const answerText = stripToolCall(assembled);
660
+ const answerText = stripToolCall(assembled, toolNames);
638
661
  yield { type: 'block-start', index: 1, blockType: 'text' };
639
662
  if (answerText) yield { type: 'text-delta', index: 1, text: answerText };
640
663
  yield { type: 'block-end', index: 1, block: { type: 'text', text: answerText } };
@@ -645,7 +668,7 @@ export function apply(ctx, config = {}) {
645
668
  * block is emitted after the answer, exactly as the plain path does.
646
669
  */
647
670
  if (wantTools) {
648
- const call = parseToolCall(assembled);
671
+ const call = parseToolCall(assembled, toolNames);
649
672
  if (call) {
650
673
  const callId = `webcall_${Date.now().toString(36)}`;
651
674
  yield { type: 'block-start', index: 2, blockType: 'tool-call' };
@@ -689,7 +712,7 @@ export function apply(ctx, config = {}) {
689
712
  }
690
713
 
691
714
  if (wantTools) {
692
- const call = parseToolCall(assembled);
715
+ const call = parseToolCall(assembled, toolNames);
693
716
  if (call) {
694
717
  // A tool call was made. Any prose the model wrapped around it belongs in the text block;
695
718
  // the JSON itself must not reach the transcript.
@@ -732,7 +755,7 @@ export function apply(ctx, config = {}) {
732
755
  emitted += WEB_LABEL;
733
756
  yield { type: 'text-delta', index: 0, text: WEB_LABEL };
734
757
  }
735
- const tail = stripToolCall(assembled.slice(shown));
758
+ const tail = stripToolCall(assembled.slice(shown), toolNames);
736
759
  if (tail) {
737
760
  emitted += tail;
738
761
  yield { type: 'text-delta', index: 0, text: tail };
@@ -774,7 +797,7 @@ export function apply(ctx, config = {}) {
774
797
  * scanner stopped one brace short of any. Scrubbing the tail and appending it keeps the committed
775
798
  * block equal to the deltas that were yielded, which the harness checks.
776
799
  */
777
- const held = stripToolCall(assembled.slice(shown));
800
+ const held = stripToolCall(assembled.slice(shown), toolNames);
778
801
  if (held) {
779
802
  if (!emitted) {
780
803
  emitted += WEB_LABEL;
package/lib/prompt.mjs CHANGED
@@ -161,7 +161,7 @@ export function toolPreamble(tools) {
161
161
  * emulation exists to work around.
162
162
  */
163
163
  const HEAD = 'Tools: ';
164
- const TAIL = ' · to call one reply only with {"tool":"name","arguments":{}}';
164
+ const TAIL = ' · to call one reply only with name({"arg":"value"})';
165
165
 
166
166
  /**
167
167
  * Room for the list.
@@ -393,10 +393,115 @@ function finish(text, expectsToolCall, continuing) {
393
393
  * that merely CONTAINS the word tool but no valid object is treated as prose, because treating it as a
394
394
  * malformed call would throw away a perfectly good answer.
395
395
  */
396
- export function parseToolCall(reply) {
396
+ /**
397
+ * Find `name({...})` in a reply, where `name` is one of the tools that was offered.
398
+ *
399
+ * WHY THIS FORM IS PREFERRED OVER JSON
400
+ * MEASURED: a reply that is nothing but `{"tool":…}` cannot be shown while it is written, so the harness
401
+ * sits empty for the whole generation and looks stuck. And a brace is the commonest punctuation in
402
+ * technical prose, so `{` is a poor delimiter - it both blocks streaming and invites false positives.
403
+ *
404
+ * `read({"path":"x"})` needs no instruction, because it is the shape models already produce, and it is
405
+ * unambiguous once it is anchored on a tool name the model was actually given. `read(` when `read` is in
406
+ * the offered list is a far stronger signal than `{`.
407
+ *
408
+ * WHAT IT DOES NOT DO
409
+ * It does not JSON.parse inside strings or respect nesting by regex - it walks the characters, because an
410
+ * argument may contain a parenthesis or a brace inside a string and a regex would stop there.
411
+ */
412
+ function findCallForm(text, names) {
413
+ const wanted = Array.isArray(names) && names.length ? names : null;
414
+
415
+ for (let i = 0; i < text.length; i += 1) {
416
+ if (!/[A-Za-z_]/.test(text[i])) continue;
417
+
418
+ // Read the identifier at this position.
419
+ let j = i;
420
+ let word = '';
421
+ while (j < text.length && /[A-Za-z0-9_.-]/.test(text[j])) {
422
+ word += text[j];
423
+ j += 1;
424
+ }
425
+ if (!word) continue;
426
+ if (wanted && !wanted.includes(word)) {
427
+ i = j - 1;
428
+ continue;
429
+ }
430
+
431
+ // Optional whitespace, then a parenthesis.
432
+ let k = j;
433
+ while (k < text.length && (text[k] === ' ' || text[k] === '\t' || text[k] === '\n')) k += 1;
434
+ if (text[k] !== '(') {
435
+ i = j - 1;
436
+ continue;
437
+ }
438
+
439
+ // Walk to the matching parenthesis, respecting strings and escapes.
440
+ let depth = 0;
441
+ let inString = false;
442
+ let escaped = false;
443
+ let end = -1;
444
+ for (let p = k; p < text.length; p += 1) {
445
+ const ch = text[p];
446
+ if (inString) {
447
+ if (escaped) escaped = false;
448
+ else if (ch === '\\') escaped = true;
449
+ else if (ch === '"') inString = false;
450
+ continue;
451
+ }
452
+ if (ch === '"') inString = true;
453
+ else if (ch === '(') depth += 1;
454
+ else if (ch === ')') {
455
+ depth -= 1;
456
+ if (depth === 0) {
457
+ end = p;
458
+ break;
459
+ }
460
+ }
461
+ }
462
+ if (end === -1) return null; // still being written - no complete call yet
463
+
464
+ const inside = text.slice(k + 1, end).trim();
465
+ let args = {};
466
+ if (inside) {
467
+ try {
468
+ args = JSON.parse(inside);
469
+ } catch {
470
+ return null; // parentheses that are not a call, or arguments not yet valid
471
+ }
472
+ }
473
+
474
+ return {
475
+ name: word,
476
+ arguments: JSON.stringify(args),
477
+ start: i,
478
+ end: end + 1,
479
+ };
480
+ }
481
+
482
+ return null;
483
+ }
484
+
485
+ /**
486
+ * Recover a tool call from a plain-text reply, if the model made one.
487
+ *
488
+ * TWO SHAPES, IN ORDER
489
+ * 1 `name({...})` — preferred: no instruction needed, and it streams
490
+ * 2 `{"tool":"name","arguments":{…}}` — kept because models emit it unprompted, and because a fallback
491
+ * costs nothing when the primary misses
492
+ *
493
+ * Fenced blocks are searched last-first because a model that explains itself tends to put the final answer at
494
+ * the end. A reply that merely CONTAINS the word tool but no valid object is treated as prose, because
495
+ * treating it as a malformed call would throw away a perfectly good answer.
496
+ */
497
+ export function parseToolCall(reply, names) {
397
498
  const text = String(reply ?? '').trim();
398
499
  if (!text) return null;
399
500
 
501
+ // Shape 1: name({...}).
502
+ const direct = findCallForm(text, names);
503
+ if (direct) return { name: direct.name, arguments: direct.arguments };
504
+
400
505
  const candidates = [];
401
506
  // fenced blocks first
402
507
  const fence = /```(?:json)?\s*([\s\S]*?)```/gi;
@@ -464,9 +569,56 @@ export function parseToolCall(reply) {
464
569
  * It does not decide whether a call exists - `parseToolCall` does that at the end. It only answers
465
570
  * "how far can I safely show right now", which is the question a live stream needs.
466
571
  */
467
- export function streamSafeCut(text) {
572
+ export function streamSafeCut(text, names) {
468
573
  const s = String(text ?? '');
469
574
 
575
+ /**
576
+ * Hold from a known tool name that has opened, or is about to open, a parenthesis.
577
+ *
578
+ * This is the call-syntax half of the same question. `read(` is a call; `read` alone might be the first
579
+ * letters of `readable`, so it is not held - but a trailing fragment that could still become a known name
580
+ * (`repl` at the very end of the text) IS held, because the next characters may complete it.
581
+ */
582
+ const wanted = Array.isArray(names) && names.length ? names : null;
583
+ if (wanted) {
584
+ for (let i = 0; i < s.length; i += 1) {
585
+ if (!/[A-Za-z_]/.test(s[i])) continue;
586
+ let j = i;
587
+ let word = '';
588
+ while (j < s.length && /[A-Za-z0-9_.-]/.test(s[j])) {
589
+ word += s[j];
590
+ j += 1;
591
+ }
592
+ if (wanted.includes(word)) {
593
+ let k = j;
594
+ while (k < s.length && /\s/.test(s[k])) k += 1;
595
+ // A complete `name (` is a call being written; nothing after it may stream.
596
+ if (k >= s.length || s[k] === '(') return i;
597
+ }
598
+ i = j - 1;
599
+ }
600
+
601
+ /**
602
+ * A name that is still arriving.
603
+ *
604
+ * The reply may end mid-identifier — `… now call replace_not` — and one more character would make it a
605
+ * call. Holding the fragment costs a few visible characters and stops a call's opening words from
606
+ * appearing in the transcript before the call does.
607
+ *
608
+ * THREE CHARACTERS MINIMUM, measured. The first version held any trailing fragment that prefixed a known
609
+ * name, so an ordinary sentence ending in `r` stalled one character back - `r` prefixes `read` and
610
+ * `replace_note`. On screen that is a reply that appears to stop mid-word and then finishes a keystroke
611
+ * later. A three-letter fragment is worth holding; one letter is not.
612
+ */
613
+ const tailStart = Math.max(0, s.length - 40);
614
+ for (let i = tailStart; i < s.length; i += 1) {
615
+ const fragment = s.slice(i);
616
+ if (fragment.length < 3) break;
617
+ if (!/^[A-Za-z_][A-Za-z0-9_.-]*$/.test(fragment)) continue;
618
+ if (wanted.some((n) => n.startsWith(fragment))) return i;
619
+ }
620
+ }
621
+
470
622
  /**
471
623
  * Could the brace at `i` still turn out to be the start of a tool object?
472
624
  *
@@ -497,9 +649,21 @@ export function streamSafeCut(text) {
497
649
  return s.length;
498
650
  }
499
651
 
500
- export function stripToolCall(reply) {
652
+ export function stripToolCall(reply, names) {
501
653
  let text = String(reply ?? '');
502
654
 
655
+ /**
656
+ * Shape 1: `name({...})`, wherever it appears, prose either side.
657
+ *
658
+ * Removed by the same character walk that found it, so a call whose arguments contain a parenthesis or a
659
+ * brace inside a string is removed whole rather than up to the first `)`.
660
+ */
661
+ for (let guard = 0; guard < 20; guard += 1) {
662
+ const found = findCallForm(text, names);
663
+ if (!found) break;
664
+ text = text.slice(0, found.start) + ' ' + text.slice(found.end);
665
+ }
666
+
503
667
  // Fenced calls first: the fence marks the whole block, so no brace counting is needed.
504
668
  text = text.replace(/```(?:json)?\s*\{\s*"tool"[\s\S]*?\}\s*```/gi, '');
505
669
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@operatingtag/dsh-unclesam",
3
- "version": "1.3.3",
3
+ "version": "1.3.4",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Uncle Sam — answers through chat.deepseek.com in a real browser tab instead of the metered API. Switch between the API and the web account from the composer; web replies are signed so you always know which route answered.",