dsh-agy-link 0.4.34 → 0.4.35

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 (3) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/dist/index.js +108 -71
  3. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.4.35 (2026-09-18)
4
+
5
+ ### English
6
+
7
+ - **Reasoning UI: only show thinking when there is real content.** Empty `[agy thinking turn · N thinking tokens]` chips no longer spam tool-heavy turns. When agy stores only `toolAction`/`toolSummary` (common), those become the visible thought line (e.g. `Searching the web — Search Wo Tianyu profile`). Full CoT prose still renders when present in the conversation DB.
8
+
9
+ ### 中文 (Chinese)
10
+
11
+ - **思考 UI:** 无正文时不再刷 token 横幅;工具步骤优先展示 `toolAction`/`toolSummary` 作为意图说明;完整思维链仍按 DB 正文展示。
12
+
3
13
  ## 0.4.34 (2026-09-18)
4
14
 
5
15
  ### English
package/dist/index.js CHANGED
@@ -1051,20 +1051,11 @@ var EventMapper = class {
1051
1051
  */
1052
1052
  *emitThinking(absIndex, thoughtTokens) {
1053
1053
  const text = this.opts.resolvedThoughts?.get(absIndex);
1054
- const hasText = text !== void 0 && text.trim() !== "";
1055
- if (!hasText && thoughtTokens <= 0) return;
1056
- if (!hasText && this.bannerOnlyThinkingEmitted) return;
1054
+ if (!(text !== void 0 && text.trim() !== "")) return;
1057
1055
  yield* this.ensureBlock("reasoning");
1058
- const banner = thoughtTokens > 0 ? "[agy thinking turn · " + thoughtTokens + " thinking tokens]" : "[agy thinking turn]";
1059
- if (hasText) {
1060
- const combined = `${banner} ${text.trim()}\n`;
1061
- const d = this.appendDelta(combined);
1062
- if (d) yield d;
1063
- } else {
1064
- this.bannerOnlyThinkingEmitted = true;
1065
- const d = this.appendDelta(`${banner}\n`);
1066
- if (d) yield d;
1067
- }
1056
+ const combined = `${thoughtTokens > 0 ? "[agy thinking turn · " + thoughtTokens + " thinking tokens]" : "[agy thinking turn]"} ${text.trim()}\n`;
1057
+ const d = this.appendDelta(combined);
1058
+ if (d) yield d;
1068
1059
  }
1069
1060
  /**
1070
1061
  * Map one event. `absIndex` is the event's position in the run recording;
@@ -2331,65 +2322,111 @@ function readVarint(b, offset) {
2331
2322
  /**
2332
2323
  * Extract model thought text from a protobuf step_payload.
2333
2324
  *
2334
- * In agy SQLite databases (steps table, step_type = 15 agent_response),
2335
- * the thought text is stored inside Protobuf field 20 (response body) ->
2336
- * sub-field 3 (length-delimited UTF-8 string).
2325
+ * Primary: field 20 -> sub-field 3 (verified on simple print-mode turns).
2326
+ * Fallback: longest non-JSON prose string, then toolAction/toolSummary
2327
+ * embedded in tool-arg JSON (tool-heavy agent turns often store only those).
2337
2328
  */
2338
2329
  function extractStepThoughts(payload) {
2339
- let pos = 0;
2340
- while (pos < payload.length) {
2341
- const res = readVarint(payload, pos);
2342
- if (res === null) break;
2343
- pos = res.next;
2344
- const tag = res.val;
2345
- const wireType = tag & 7;
2346
- const fieldNum = tag >>> 3;
2347
- if (wireType === 2) {
2348
- const lenRes = readVarint(payload, pos);
2349
- if (lenRes === null) break;
2350
- pos = lenRes.next;
2351
- const len = lenRes.val;
2352
- if (pos + len > payload.length) break;
2353
- const slice = payload.subarray(pos, pos + len);
2354
- pos += len;
2355
- if (fieldNum === 20) {
2356
- let p20 = 0;
2357
- while (p20 < slice.length) {
2358
- const t20Res = readVarint(slice, p20);
2359
- if (t20Res === null) break;
2360
- p20 = t20Res.next;
2361
- const t20 = t20Res.val;
2362
- const w20 = t20 & 7;
2363
- const f20 = t20 >>> 3;
2364
- if (w20 === 2) {
2365
- const l20Res = readVarint(slice, p20);
2366
- if (l20Res === null) break;
2367
- p20 = l20Res.next;
2368
- const l20 = l20Res.val;
2369
- if (p20 + l20 > slice.length) break;
2370
- const s20 = slice.subarray(p20, p20 + l20);
2371
- p20 += l20;
2372
- if (f20 === 3) {
2373
- const str = s20.toString("utf-8");
2374
- if (str.length > 0) return str;
2375
- }
2376
- } else if (w20 === 0) {
2377
- const v20Res = readVarint(slice, p20);
2378
- if (v20Res === null) break;
2379
- p20 = v20Res.next;
2380
- } else if (w20 === 1) p20 += 8;
2381
- else if (w20 === 5) p20 += 4;
2382
- else break;
2330
+ const proseCandidates = [];
2331
+ const actionCandidates = [];
2332
+ const consider = (str) => {
2333
+ const t = str.trim();
2334
+ if (t.length < 8) return;
2335
+ if (/^SYSTEM ROLE/i.test(t)) return;
2336
+ if (t.startsWith("/plan ") || t.includes("<PLAN>The user is requesting that you think")) return;
2337
+ if (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(t)) return;
2338
+ if (/^bot-|^call_/.test(t)) return;
2339
+ const act = /"toolAction"\s*:\s*"([^"]{3,200})"/.exec(t);
2340
+ const sum = /"toolSummary"\s*:\s*"([^"]{3,200})"/.exec(t);
2341
+ if (act || sum) {
2342
+ const line = [act?.[1] ?? "", sum?.[1] ?? ""].filter((s) => s !== "").join(" — ");
2343
+ if (line !== "") actionCandidates.push(line);
2344
+ }
2345
+ const looksJson = t.startsWith("{") || t.startsWith("[");
2346
+ if (!looksJson && t.length >= 24 && t.length <= 4e3 && /[a-zA-Z一-鿿]/.test(t) && !/^[0-9a-f-]{30,}$/i.test(t)) {
2347
+ if ((t.match(/[.!?]|,|。| /g) ?? []).length >= 2 || /[a-zA-Z]{4,}\s+[a-zA-Z]{3,}/.test(t)) proseCandidates.push(t);
2348
+ return;
2349
+ }
2350
+ if (looksJson) try {
2351
+ const obj = JSON.parse(t);
2352
+ const line = [typeof obj.toolAction === "string" ? obj.toolAction.trim() : "", typeof obj.toolSummary === "string" ? obj.toolSummary.trim() : ""].filter((s) => s !== "").join(" — ");
2353
+ if (line !== "") actionCandidates.push(line);
2354
+ } catch {}
2355
+ };
2356
+ const walk = (buf, depth) => {
2357
+ if (depth > 3) return;
2358
+ let pos = 0;
2359
+ while (pos < buf.length) {
2360
+ const res = readVarint(buf, pos);
2361
+ if (res === null) break;
2362
+ pos = res.next;
2363
+ const tag = res.val;
2364
+ const wireType = tag & 7;
2365
+ const fieldNum = tag >>> 3;
2366
+ if (wireType === 2) {
2367
+ const lenRes = readVarint(buf, pos);
2368
+ if (lenRes === null) break;
2369
+ pos = lenRes.next;
2370
+ const len = lenRes.val;
2371
+ if (pos + len > buf.length) break;
2372
+ const slice = buf.subarray(pos, pos + len);
2373
+ pos += len;
2374
+ if (fieldNum === 20) {
2375
+ let p20 = 0;
2376
+ while (p20 < slice.length) {
2377
+ const t20Res = readVarint(slice, p20);
2378
+ if (t20Res === null) break;
2379
+ p20 = t20Res.next;
2380
+ const t20 = t20Res.val;
2381
+ const w20 = t20 & 7;
2382
+ const f20 = t20 >>> 3;
2383
+ if (w20 === 2) {
2384
+ const l20Res = readVarint(slice, p20);
2385
+ if (l20Res === null) break;
2386
+ p20 = l20Res.next;
2387
+ const l20 = l20Res.val;
2388
+ if (p20 + l20 > slice.length) break;
2389
+ const s20 = slice.subarray(p20, p20 + l20);
2390
+ p20 += l20;
2391
+ if (f20 === 3) {
2392
+ const str = s20.toString("utf-8");
2393
+ if (str.trim() !== "") {
2394
+ consider(str);
2395
+ if (!str.trim().startsWith("{") && str.trim().length >= 24) proseCandidates.unshift(str.trim());
2396
+ }
2397
+ } else if (f20 === 2 || f20 === 1) consider(s20.toString("utf-8"));
2398
+ } else if (w20 === 0) {
2399
+ const v20Res = readVarint(slice, p20);
2400
+ if (v20Res === null) break;
2401
+ p20 = v20Res.next;
2402
+ } else if (w20 === 1) p20 += 8;
2403
+ else if (w20 === 5) p20 += 4;
2404
+ else break;
2405
+ }
2383
2406
  }
2384
- }
2385
- } else if (wireType === 0) {
2386
- const vRes = readVarint(payload, pos);
2387
- if (vRes === null) break;
2388
- pos = vRes.next;
2389
- } else if (wireType === 1) pos += 8;
2390
- else if (wireType === 5) pos += 4;
2391
- else break;
2392
- }
2407
+ if (len >= 8 && len < 2e5) walk(slice, depth + 1);
2408
+ } else if (wireType === 0) {
2409
+ const vRes = readVarint(buf, pos);
2410
+ if (vRes === null) break;
2411
+ pos = vRes.next;
2412
+ } else if (wireType === 1) pos += 8;
2413
+ else if (wireType === 5) pos += 4;
2414
+ else break;
2415
+ }
2416
+ };
2417
+ walk(payload, 0);
2418
+ const full = payload.toString("utf8");
2419
+ const act = /"toolAction"\s*:\s*"([^"]{3,200})"/.exec(full);
2420
+ const sum = /"toolSummary"\s*:\s*"([^"]{3,200})"/.exec(full);
2421
+ if (act || sum) {
2422
+ const line = [act?.[1] ?? "", sum?.[1] ?? ""].filter((s) => s !== "").join(" — ");
2423
+ if (line !== "") actionCandidates.push(line);
2424
+ }
2425
+ if (proseCandidates.length > 0) {
2426
+ proseCandidates.sort((a, b) => b.length - a.length);
2427
+ return proseCandidates[0];
2428
+ }
2429
+ if (actionCandidates.length > 0) return actionCandidates[0];
2393
2430
  return null;
2394
2431
  }
2395
2432
  /**
@@ -2441,9 +2478,9 @@ async function loadAgyDbData(conversationId, accountHome) {
2441
2478
  const info = extractToolInfo(payload);
2442
2479
  if (info !== null) steps.set(idx, info);
2443
2480
  }
2444
- if (stepType === 14 || stepType === 15) {
2481
+ if (stepType === 14 || stepType === 15 || TOOL_STEP_TYPES.has(stepType)) {
2445
2482
  const th = extractStepThoughts(payload);
2446
- if (th !== null && th.trim() !== "") thoughts.set(idx, th);
2483
+ if (th !== null && th.trim() !== "" && !/^SYSTEM ROLE/i.test(th.trim())) thoughts.set(idx, th);
2447
2484
  }
2448
2485
  }
2449
2486
  } catch {} finally {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-agy-link",
3
- "version": "0.4.34",
3
+ "version": "0.4.35",
4
4
  "description": "Google Antigravity (agy CLI) models for DeepSeek Harness — stream Gemini/Claude/GPT-OSS subscriptions into DSH with thinking, tool activity, token usage and in-GUI Google OAuth login.",
5
5
  "type": "module",
6
6
  "license": "MIT",