dsh-agy-link 0.4.28 → 0.4.32

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/index.js CHANGED
@@ -398,6 +398,8 @@ var RunRecording = class {
398
398
  [waiters] = /* @__PURE__ */ new Set();
399
399
  /** DB-resolved full tool args keyed by event index (set during span driving). */
400
400
  fullArgs = null;
401
+ /** DB-resolved thoughts keyed by event index (set during span driving). */
402
+ thoughts = null;
401
403
  /**
402
404
  * Set by the adapter right after spawn. A mid-turn user steer makes DSH
403
405
  * open a NEW stream() call for the same session while this run's process
@@ -536,6 +538,15 @@ var RunRecording = class {
536
538
  this.fullArgs ??= /* @__PURE__ */ new Map();
537
539
  this.fullArgs.set(eventIndex, args);
538
540
  }
541
+ /** Store DB-resolved thoughts for a step (set during span driving). */
542
+ setThoughts(eventIndex, text) {
543
+ this.thoughts ??= /* @__PURE__ */ new Map();
544
+ this.thoughts.set(eventIndex, text);
545
+ }
546
+ /** Retrieve DB-resolved thoughts for a step if previously resolved. */
547
+ getThoughts(eventIndex) {
548
+ return this.thoughts?.get(eventIndex);
549
+ }
539
550
  };
540
551
  /** Prefix every mirrored agy tool callId carries; continuation detection key. */
541
552
  const AGY_CALL_PREFIX = "agytc-";
@@ -1008,11 +1019,26 @@ var EventMapper = class {
1008
1019
  text: delta
1009
1020
  };
1010
1021
  }
1011
- /** The honest thinking signal agy exposes: a token-count line. */
1012
- *emitThinkingLine(thoughtTokens) {
1022
+ /**
1023
+ * Emit thinking: always retain the [agy thinking turn · ... thinking tokens]
1024
+ * banner. When real thought prose is present, place the banner and thought
1025
+ * text on the same initial line so DSH's collapsed summary renders:
1026
+ * [agy thinking turn · *** thinking tokens] [Chain-of-Thought body]
1027
+ */
1028
+ *emitThinking(absIndex, thoughtTokens) {
1029
+ const text = this.opts.resolvedThoughts?.get(absIndex);
1030
+ const hasText = text !== void 0 && text.trim() !== "";
1031
+ if (!hasText && thoughtTokens <= 0) return;
1013
1032
  yield* this.ensureBlock("reasoning");
1014
- const d = this.appendDelta("[agy thinking turn · " + thoughtTokens + " thinking tokens]\n");
1015
- if (d) yield d;
1033
+ const banner = thoughtTokens > 0 ? "[agy thinking turn · " + thoughtTokens + " thinking tokens]" : "[agy thinking turn]";
1034
+ if (hasText) {
1035
+ const combined = `${banner} ${text.trim()}\n`;
1036
+ const d = this.appendDelta(combined);
1037
+ if (d) yield d;
1038
+ } else {
1039
+ const d = this.appendDelta(`${banner}\n`);
1040
+ if (d) yield d;
1041
+ }
1016
1042
  }
1017
1043
  /**
1018
1044
  * Map one event. `absIndex` is the event's position in the run recording;
@@ -1028,15 +1054,17 @@ var EventMapper = class {
1028
1054
  if (ev.stepKind === "text") {
1029
1055
  const thoughtTokens = ev.usage?.thinking_tokens ?? 0;
1030
1056
  const stepTextEmitted = (this.emittedByKey.get(ev.stepKey) ?? "") !== "";
1031
- if (thoughtTokens > 0 && !stepTextEmitted && !this.thinkingAnnounced.has(ev.stepKey)) {
1057
+ const dbThought = this.opts.resolvedThoughts?.get(absIndex);
1058
+ const shouldAnnounce = dbThought !== void 0 && dbThought.trim() !== "" || thoughtTokens > 0;
1059
+ if (shouldAnnounce && !stepTextEmitted && !this.thinkingAnnounced.has(ev.stepKey)) {
1032
1060
  this.thinkingAnnounced.add(ev.stepKey);
1033
- yield* this.emitThinkingLine(thoughtTokens);
1061
+ yield* this.emitThinking(absIndex, thoughtTokens);
1034
1062
  }
1035
- const deferred = thoughtTokens > 0 && stepTextEmitted && !this.thinkingAnnounced.has(ev.stepKey);
1063
+ const deferred = shouldAnnounce && stepTextEmitted && !this.thinkingAnnounced.has(ev.stepKey);
1036
1064
  if (ev.text === "" && !ev.fragment) {
1037
1065
  if (deferred) {
1038
1066
  this.thinkingAnnounced.add(ev.stepKey);
1039
- yield* this.emitThinkingLine(thoughtTokens);
1067
+ yield* this.emitThinking(absIndex, thoughtTokens);
1040
1068
  }
1041
1069
  return;
1042
1070
  }
@@ -1055,17 +1083,25 @@ var EventMapper = class {
1055
1083
  if (d) yield d;
1056
1084
  if (deferred) {
1057
1085
  this.thinkingAnnounced.add(ev.stepKey);
1058
- yield* this.emitThinkingLine(thoughtTokens);
1086
+ yield* this.emitThinking(absIndex, thoughtTokens);
1059
1087
  }
1060
1088
  return;
1061
1089
  }
1062
1090
  if (ev.stepKind === "thinking" || ev.stepKind === "subagent") {
1063
1091
  yield* this.ensureBlock("reasoning");
1064
1092
  if (ev.stepKind === "thinking") {
1065
- const delta = suffixDelta(this.emittedByKey.get(ev.stepKey) ?? "", ev.text);
1066
- this.emittedByKey.set(ev.stepKey, ev.text);
1067
- const d = this.appendDelta(delta);
1068
- if (d) yield d;
1093
+ const dbThought = this.opts.resolvedThoughts?.get(absIndex);
1094
+ if (dbThought && dbThought.trim() !== "") {
1095
+ const delta = suffixDelta(this.emittedByKey.get(ev.stepKey) ?? "", dbThought);
1096
+ this.emittedByKey.set(ev.stepKey, dbThought);
1097
+ const d = this.appendDelta(delta);
1098
+ if (d) yield d;
1099
+ } else {
1100
+ const delta = suffixDelta(this.emittedByKey.get(ev.stepKey) ?? "", ev.text);
1101
+ this.emittedByKey.set(ev.stepKey, ev.text);
1102
+ const d = this.appendDelta(delta);
1103
+ if (d) yield d;
1104
+ }
1069
1105
  } else {
1070
1106
  const d = this.appendDelta("[agy subagent] " + ev.text + "\n");
1071
1107
  if (d) yield d;
@@ -1454,7 +1490,7 @@ const THINKING_STEP_TYPES = /* @__PURE__ */ new Set([
1454
1490
  "thought",
1455
1491
  "reasoning"
1456
1492
  ]);
1457
- const TOOL_STEP_TYPES = /* @__PURE__ */ new Set([
1493
+ const TOOL_STEP_TYPES$1 = /* @__PURE__ */ new Set([
1458
1494
  "tool_call",
1459
1495
  "toolcall",
1460
1496
  "tool",
@@ -1503,7 +1539,7 @@ function normalizeStepKind(v) {
1503
1539
  const s = v.toLowerCase();
1504
1540
  if (TEXT_STEP_TYPES.has(s)) return "text";
1505
1541
  if (THINKING_STEP_TYPES.has(s)) return "thinking";
1506
- if (TOOL_STEP_TYPES.has(s)) return "tool";
1542
+ if (TOOL_STEP_TYPES$1.has(s)) return "tool";
1507
1543
  if (TITLE_STEP_TYPES.has(s)) return "title";
1508
1544
  if (SUBAGENT_STEP_TYPES.has(s)) return "subagent";
1509
1545
  if (USER_INPUT_STEP_TYPES.has(s)) return "user-input";
@@ -2192,6 +2228,19 @@ async function probeProcess(bin, args, timeoutMs = 3e4, signal, env) {
2192
2228
  //#region src/host/agy-db.ts
2193
2229
  const execFileAsync = promisify(execFile);
2194
2230
  let AGY_DB_DIR = join(homedir(), ".gemini", "antigravity-cli", "conversations");
2231
+ const TOOL_STEP_TYPES = /* @__PURE__ */ new Set([
2232
+ 5,
2233
+ 7,
2234
+ 8,
2235
+ 9,
2236
+ 17,
2237
+ 21,
2238
+ 33,
2239
+ 101,
2240
+ 132,
2241
+ 138,
2242
+ 139
2243
+ ]);
2195
2244
  const MAX_CACHE_AGE_MS = 3e5;
2196
2245
  let cache = null;
2197
2246
  let cacheTime = 0;
@@ -2200,19 +2249,112 @@ const SAFE_CONVERSATION_ID = /^[A-Za-z0-9_-]{1,64}$/;
2200
2249
  function isSafeConversationId(id) {
2201
2250
  return SAFE_CONVERSATION_ID.test(id);
2202
2251
  }
2252
+ function readVarint(b, offset) {
2253
+ let val = 0;
2254
+ let shift = 0;
2255
+ let pos = offset;
2256
+ while (pos < b.length) {
2257
+ const byte = b[pos++];
2258
+ val |= (byte & 127) << shift;
2259
+ shift += 7;
2260
+ if ((byte & 128) === 0) return {
2261
+ val,
2262
+ next: pos
2263
+ };
2264
+ if (shift > 35) return null;
2265
+ }
2266
+ return null;
2267
+ }
2268
+ /**
2269
+ * Extract model thought text from a protobuf step_payload.
2270
+ *
2271
+ * In agy SQLite databases (steps table, step_type = 15 agent_response),
2272
+ * the thought text is stored inside Protobuf field 20 (response body) ->
2273
+ * sub-field 3 (length-delimited UTF-8 string).
2274
+ */
2275
+ function extractStepThoughts(payload) {
2276
+ let pos = 0;
2277
+ while (pos < payload.length) {
2278
+ const res = readVarint(payload, pos);
2279
+ if (res === null) break;
2280
+ pos = res.next;
2281
+ const tag = res.val;
2282
+ const wireType = tag & 7;
2283
+ const fieldNum = tag >>> 3;
2284
+ if (wireType === 2) {
2285
+ const lenRes = readVarint(payload, pos);
2286
+ if (lenRes === null) break;
2287
+ pos = lenRes.next;
2288
+ const len = lenRes.val;
2289
+ if (pos + len > payload.length) break;
2290
+ const slice = payload.subarray(pos, pos + len);
2291
+ pos += len;
2292
+ if (fieldNum === 20) {
2293
+ let p20 = 0;
2294
+ while (p20 < slice.length) {
2295
+ const t20Res = readVarint(slice, p20);
2296
+ if (t20Res === null) break;
2297
+ p20 = t20Res.next;
2298
+ const t20 = t20Res.val;
2299
+ const w20 = t20 & 7;
2300
+ const f20 = t20 >>> 3;
2301
+ if (w20 === 2) {
2302
+ const l20Res = readVarint(slice, p20);
2303
+ if (l20Res === null) break;
2304
+ p20 = l20Res.next;
2305
+ const l20 = l20Res.val;
2306
+ if (p20 + l20 > slice.length) break;
2307
+ const s20 = slice.subarray(p20, p20 + l20);
2308
+ p20 += l20;
2309
+ if (f20 === 3) {
2310
+ const str = s20.toString("utf-8");
2311
+ if (str.length > 0) return str;
2312
+ }
2313
+ } else if (w20 === 0) {
2314
+ const v20Res = readVarint(slice, p20);
2315
+ if (v20Res === null) break;
2316
+ p20 = v20Res.next;
2317
+ } else if (w20 === 1) p20 += 8;
2318
+ else if (w20 === 5) p20 += 4;
2319
+ else break;
2320
+ }
2321
+ }
2322
+ } else if (wireType === 0) {
2323
+ const vRes = readVarint(payload, pos);
2324
+ if (vRes === null) break;
2325
+ pos = vRes.next;
2326
+ } else if (wireType === 1) pos += 8;
2327
+ else if (wireType === 5) pos += 4;
2328
+ else break;
2329
+ }
2330
+ return null;
2331
+ }
2203
2332
  /**
2204
2333
  * Copy the agy SQLite DB to a temp path (avoiding WAL lock issues), then
2205
- * query tool step payloads via sqlite3 CLI. Returns a Map of stepIndex →
2206
- * {name, args} for every tool step found.
2334
+ * query tool and thought step payloads via sqlite3 CLI.
2207
2335
  */
2208
- async function loadToolSteps(conversationId) {
2209
- const result = /* @__PURE__ */ new Map();
2210
- if (!isSafeConversationId(conversationId)) return result;
2336
+ async function loadAgyDbData(conversationId) {
2337
+ const steps = /* @__PURE__ */ new Map();
2338
+ const thoughts = /* @__PURE__ */ new Map();
2339
+ const seenSteps = /* @__PURE__ */ new Set();
2340
+ if (!isSafeConversationId(conversationId)) return {
2341
+ steps,
2342
+ thoughts,
2343
+ seenSteps
2344
+ };
2211
2345
  const dbPath = join(AGY_DB_DIR, `${conversationId}.db`);
2212
2346
  try {
2213
- if ((await stat(dbPath)).size === 0) return result;
2347
+ if ((await stat(dbPath)).size === 0) return {
2348
+ steps,
2349
+ thoughts,
2350
+ seenSteps
2351
+ };
2214
2352
  } catch {
2215
- return result;
2353
+ return {
2354
+ steps,
2355
+ thoughts,
2356
+ seenSteps
2357
+ };
2216
2358
  }
2217
2359
  const tmpDb = join(tmpdir(), `agy-db-${conversationId.slice(0, 8)}-${Date.now()}.db`);
2218
2360
  try {
@@ -2223,20 +2365,31 @@ async function loadToolSteps(conversationId) {
2223
2365
  try {
2224
2366
  await copyFile(dbPath + "-shm", tmpDb + "-shm");
2225
2367
  } catch {}
2226
- const { stdout } = await execFileAsync("sqlite3", [tmpDb, `SELECT idx, hex(step_payload) FROM steps WHERE step_type IN (5,7,8,9,17,21,33,101,132) AND length(step_payload) > 20`], {
2368
+ const { stdout } = await execFileAsync("sqlite3", [tmpDb, `SELECT idx, step_type, hex(step_payload) FROM steps WHERE (step_type IN (5,7,8,9,14,15,17,21,33,101,132,138,139)) AND length(step_payload) > 20`], {
2227
2369
  timeout: 5e3,
2228
2370
  encoding: "utf-8",
2229
- maxBuffer: 10485760
2371
+ maxBuffer: 26214400
2230
2372
  });
2231
2373
  for (const line of stdout.split("\n")) {
2232
- const pipeIdx = line.indexOf("|");
2233
- if (pipeIdx < 0) continue;
2234
- const idx = parseInt(line.slice(0, pipeIdx), 10);
2374
+ const firstPipe = line.indexOf("|");
2375
+ if (firstPipe < 0) continue;
2376
+ const secondPipe = line.indexOf("|", firstPipe + 1);
2377
+ if (secondPipe < 0) continue;
2378
+ const idx = parseInt(line.slice(0, firstPipe), 10);
2379
+ const stepType = parseInt(line.slice(firstPipe + 1, secondPipe), 10);
2235
2380
  if (!Number.isFinite(idx)) continue;
2236
- const hexPayload = line.slice(pipeIdx + 1).trim();
2381
+ seenSteps.add(idx);
2382
+ const hexPayload = line.slice(secondPipe + 1).trim();
2237
2383
  if (hexPayload === "" || hexPayload === "NULL") continue;
2238
- const info = extractToolInfo(Buffer.from(hexPayload, "hex"));
2239
- if (info !== null) result.set(idx, info);
2384
+ const payload = Buffer.from(hexPayload, "hex");
2385
+ if (TOOL_STEP_TYPES.has(stepType)) {
2386
+ const info = extractToolInfo(payload);
2387
+ if (info !== null) steps.set(idx, info);
2388
+ }
2389
+ if (stepType === 14 || stepType === 15) {
2390
+ const th = extractStepThoughts(payload);
2391
+ if (th !== null && th.trim() !== "") thoughts.set(idx, th);
2392
+ }
2240
2393
  }
2241
2394
  } catch {} finally {
2242
2395
  try {
@@ -2249,7 +2402,11 @@ async function loadToolSteps(conversationId) {
2249
2402
  await unlink(tmpDb + "-shm");
2250
2403
  } catch {}
2251
2404
  }
2252
- return result;
2405
+ return {
2406
+ steps,
2407
+ thoughts,
2408
+ seenSteps
2409
+ };
2253
2410
  }
2254
2411
  /**
2255
2412
  * Extract tool name and full JSON args from a protobuf step_payload.
@@ -2380,15 +2537,42 @@ async function readFullToolArgs(conversationId, stepIndex) {
2380
2537
  if (cache?.conversationId === conversationId && cache.loaded && now - cacheTime < MAX_CACHE_AGE_MS) {
2381
2538
  const cached = cache.steps.get(stepIndex);
2382
2539
  if (cached !== void 0) return cached;
2540
+ if (cache.checkedSteps.has(stepIndex)) return null;
2383
2541
  }
2384
- const steps = await loadToolSteps(conversationId);
2542
+ const data = await loadAgyDbData(conversationId);
2385
2543
  cache = {
2386
2544
  conversationId,
2387
- steps,
2545
+ steps: data.steps,
2546
+ thoughts: data.thoughts,
2547
+ checkedSteps: data.seenSteps,
2388
2548
  loaded: true
2389
2549
  };
2390
2550
  cacheTime = now;
2391
- return steps.get(stepIndex) ?? null;
2551
+ return data.steps.get(stepIndex) ?? null;
2552
+ }
2553
+ /**
2554
+ * Read the full thought/reasoning text for a given step index from the agy
2555
+ * conversation database. Returns null when the DB is unavailable or the
2556
+ * step does not contain thought text.
2557
+ */
2558
+ async function readStepThoughts(conversationId, stepIndex) {
2559
+ if (!isSafeConversationId(conversationId)) return null;
2560
+ const now = Date.now();
2561
+ if (cache?.conversationId === conversationId && cache.loaded && now - cacheTime < MAX_CACHE_AGE_MS) {
2562
+ const cached = cache.thoughts.get(stepIndex);
2563
+ if (cached !== void 0) return cached;
2564
+ if (cache.checkedSteps.has(stepIndex)) return null;
2565
+ }
2566
+ const data = await loadAgyDbData(conversationId);
2567
+ cache = {
2568
+ conversationId,
2569
+ steps: data.steps,
2570
+ thoughts: data.thoughts,
2571
+ checkedSteps: data.seenSteps,
2572
+ loaded: true
2573
+ };
2574
+ cacheTime = now;
2575
+ return data.thoughts.get(stepIndex) ?? null;
2392
2576
  }
2393
2577
  //#endregion
2394
2578
  //#region src/host/adapter.ts
@@ -2784,6 +2968,7 @@ var AgyAdapter = class extends LlmAdapter {
2784
2968
  this.deps.onParser?.(parser);
2785
2969
  let streamCid = null;
2786
2970
  const activeModelForArgs = activeModel === "" ? cfg.defaultModel : activeModel;
2971
+ const effectiveAddDirs = workspaceRoot !== "" ? stagedDirs.includes(workspaceRoot) ? stagedDirs : [workspaceRoot, ...stagedDirs] : stagedDirs;
2787
2972
  const argsBase = this.buildArgs({
2788
2973
  prompt,
2789
2974
  model: activeModelForArgs,
@@ -2793,7 +2978,7 @@ var AgyAdapter = class extends LlmAdapter {
2793
2978
  timeoutMs: cfg.timeoutMs,
2794
2979
  printTimeoutMinutes: Math.max(240, Math.ceil(cfg.timeoutMs / 6e4)),
2795
2980
  extraArgs: cfg.extraArgs,
2796
- addDirs: stagedDirs
2981
+ addDirs: effectiveAddDirs
2797
2982
  });
2798
2983
  const promptViaStdin = shouldUsePromptStdin(argsBase);
2799
2984
  const args = promptViaStdin ? this.buildArgs({
@@ -2805,7 +2990,7 @@ var AgyAdapter = class extends LlmAdapter {
2805
2990
  timeoutMs: cfg.timeoutMs,
2806
2991
  printTimeoutMinutes: Math.max(240, Math.ceil(cfg.timeoutMs / 6e4)),
2807
2992
  extraArgs: cfg.extraArgs,
2808
- addDirs: stagedDirs,
2993
+ addDirs: effectiveAddDirs,
2809
2994
  promptViaStdin: true
2810
2995
  }) : argsBase;
2811
2996
  const stdinPayload = promptViaStdin ? buildStreamInputLine(prompt) : void 0;
@@ -2972,6 +3157,7 @@ var AgyAdapter = class extends LlmAdapter {
2972
3157
  const queue = new ChunkQueue();
2973
3158
  (async () => {
2974
3159
  const resolved = /* @__PURE__ */ new Map();
3160
+ const resolvedThoughts = /* @__PURE__ */ new Map();
2975
3161
  rec.conversationId;
2976
3162
  const mapper = new EventMapper({
2977
3163
  runId: rec.runId,
@@ -2979,11 +3165,31 @@ var AgyAdapter = class extends LlmAdapter {
2979
3165
  initialSawText: rec.sawTextBefore(from),
2980
3166
  useCodeWrapper,
2981
3167
  usage: rec,
2982
- resolvedFullArgs: resolved
3168
+ resolvedFullArgs: resolved,
3169
+ resolvedThoughts
2983
3170
  });
2984
3171
  let i = from;
2985
3172
  try {
2986
3173
  for await (const ev of rec.eventsFrom(from)) {
3174
+ const recordedThought = rec.getThoughts(i);
3175
+ if (recordedThought !== void 0) resolvedThoughts.set(i, recordedThought);
3176
+ else if (ev.kind === "step") {
3177
+ const rawObj = ev.raw && typeof ev.raw === "object" ? ev.raw : null;
3178
+ const stepUpdate = rawObj?.step_update && typeof rawObj.step_update === "object" ? rawObj.step_update : null;
3179
+ const activeConvId = rec.conversationId ?? (typeof rawObj?.conversation_id === "string" ? rawObj.conversation_id : null) ?? (typeof stepUpdate?.conversation_id === "string" ? stepUpdate.conversation_id : null);
3180
+ const stepIdx = parseInt(ev.stepKey, 10);
3181
+ if (activeConvId !== null && Number.isFinite(stepIdx)) try {
3182
+ let th = await readStepThoughts(activeConvId, stepIdx);
3183
+ if (th === null && (ev.state === "DONE" || (ev.usage?.thinking_tokens ?? 0) > 0 || ev.stepKind === "thinking")) {
3184
+ await new Promise((r) => setTimeout(r, 50));
3185
+ th = await readStepThoughts(activeConvId, stepIdx);
3186
+ }
3187
+ if (th !== null && th.trim() !== "") {
3188
+ resolvedThoughts.set(i, th);
3189
+ rec.setThoughts(i, th);
3190
+ }
3191
+ } catch {}
3192
+ }
2987
3193
  if (ev.kind === "step" && ev.stepKind === "tool" && ev.tool) {
2988
3194
  const rawObj = ev.raw && typeof ev.raw === "object" ? ev.raw : null;
2989
3195
  const stepUpdate = rawObj?.step_update && typeof rawObj.step_update === "object" ? rawObj.step_update : null;
@@ -27339,6 +27545,7 @@ function agyCommandDefinition(deps) {
27339
27545
  return {
27340
27546
  name: "agy",
27341
27547
  description: "Antigravity (agy CLI) bridge: status, login, models, mode, diagnostics",
27548
+ input: { hint: "<status|pool|models|mode|effort|workspace|clear|doctor|help>" },
27342
27549
  handler: (invocation) => handle(deps, invocation.rawInput)
27343
27550
  };
27344
27551
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-agy-link",
3
- "version": "0.4.28",
3
+ "version": "0.4.32",
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",
@@ -42,7 +42,7 @@
42
42
  "optional": true
43
43
  },
44
44
  "@deepseek-ai/dsh-client-locale": {
45
- "optional": true
45
+ "optional": false
46
46
  },
47
47
  "@deepseek-ai/dsh-commands": {
48
48
  "optional": true