dsh-agy-link 0.3.2 โ†’ 0.3.3

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/README.md +1 -1
  2. package/dist/index.js +267 -233
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -223,7 +223,7 @@ DSH-side tools and permissions are unaffected โ€” this only governs what the spa
223
223
 
224
224
  ## ๐Ÿงฉ How it works
225
225
 
226
- One DSH turn = one short-lived `agy -p --output-format stream-json` process. The NDJSON event stream is parsed, normalized, and recorded. Spans of that recording are mapped to DSH StreamChunks โ€” thinking โ†’ reasoning blocks, text โ†’ text blocks, result envelope โ†’ usage + finish โ€” and each **completed agy tool step cuts the span** with a `tool-calls` finish addressed to the internal `agy_tool` mirror. DSH's agent loop dispatches the mirror (it instantly replays the recorded output), writes real `tool/call` + `tool/result` session events, and re-calls the provider to continue the run โ€” so tool activity renders with DSH's **native tool-card UI** (terminal cards, diffs, read/search icons) instead of text annotations. Conversation ids come from the stream itself, with a conversations-directory snapshot diff as fallback. **No reverse-engineered database scraping, no protobuf decoding, no token-file access** โ€” only the official unmodified agy binary is spawned.
226
+ One DSH turn = one short-lived `agy -p --output-format stream-json` process. The NDJSON event stream is parsed, normalized, and recorded. Spans of that recording are mapped to DSH StreamChunks โ€” thinking โ†’ reasoning blocks, text โ†’ text blocks, result envelope โ†’ usage + finish โ€” and each **completed agy tool step cuts the span** with a `tool-calls` finish. The tool-call block addresses `run_code` โ€” the only tool DSH's dispatch policy lets a model call directly โ€” wrapping a generated one-line program that invokes the internal `agy_tool` mirror; the inner dispatch instantly replays the recorded output, writes real `tool/call` + `tool/result` session events, and re-calls the provider to continue the run. Tool activity therefore renders with DSH's **native tool-card UI** (terminal cards, diffs, read/search icons) instead of text annotations, and cards read agy's PascalCase arg keys (`CommandLine`, `AbsolutePath`, โ€ฆ). Conversation ids come from the stream itself, with a conversations-directory snapshot diff as fallback. **No reverse-engineered database scraping, no protobuf decoding, no token-file access** โ€” only the official unmodified agy binary is spawned.
227
227
 
228
228
  ## ๐Ÿ“‹ What it cannot do (honest list)
229
229
 
package/dist/index.js CHANGED
@@ -3,9 +3,9 @@ import { accessSync, constants, existsSync, mkdirSync, readFileSync, readdirSync
3
3
  import { homedir, tmpdir } from "node:os";
4
4
  import { CallId, LlmAdapter, LlmError } from "@deepseek-ai/dsh-llm";
5
5
  import { randomBytes, randomUUID } from "node:crypto";
6
+ import { defineTool } from "@deepseek-ai/dsh-tools";
6
7
  import { mkdir, mkdtemp, readFile, readdir, rm, stat, unlink, writeFile } from "node:fs/promises";
7
8
  import { spawn } from "node:child_process";
8
- import { defineTool } from "@deepseek-ai/dsh-tools";
9
9
  import { createServer } from "node:http";
10
10
  import { fileURLToPath } from "node:url";
11
11
  //#region src/common/types.ts
@@ -178,7 +178,7 @@ function readJson(file) {
178
178
  function readOverrides(file = overridesPath()) {
179
179
  return readJson(file);
180
180
  }
181
- function asString$1(v) {
181
+ function asString(v) {
182
182
  return typeof v === "string" ? v : void 0;
183
183
  }
184
184
  function asBool(v) {
@@ -207,11 +207,11 @@ function resolveConfig(entry, env = process.env, overrides = readOverrides()) {
207
207
  const cfg = {
208
208
  ...base,
209
209
  enabled: asBool(get("enabled")) ?? base.enabled,
210
- agyBin: asString$1(get("agyBin")) ?? base.agyBin,
210
+ agyBin: asString(get("agyBin")) ?? base.agyBin,
211
211
  extraArgs: Array.isArray(get("extraArgs")) ? get("extraArgs").filter((x) => typeof x === "string") : base.extraArgs,
212
212
  permissionMode: asMode(get("permissionMode")) ?? base.permissionMode,
213
- defaultModel: asString$1(get("defaultModel")) ?? base.defaultModel,
214
- defaultEffort: asString$1(get("defaultEffort")) ?? base.defaultEffort,
213
+ defaultModel: asString(get("defaultModel")) ?? base.defaultModel,
214
+ defaultEffort: asString(get("defaultEffort")) ?? base.defaultEffort,
215
215
  timeoutMs: asNum(get("timeoutMs")) ?? base.timeoutMs,
216
216
  maxConcurrent: asNum(get("maxConcurrent")) ?? base.maxConcurrent,
217
217
  contextWindowDefault: asNum(get("contextWindowDefault")) ?? base.contextWindowDefault,
@@ -221,15 +221,15 @@ function resolveConfig(entry, env = process.env, overrides = readOverrides()) {
221
221
  modelsCacheTtlMs: asNum(get("modelsCacheTtlMs")) ?? base.modelsCacheTtlMs,
222
222
  allowAuxiliary: asBool(get("allowAuxiliary")) ?? base.allowAuxiliary,
223
223
  compactionMaxChars: asNum(get("compactionMaxChars")) ?? base.compactionMaxChars,
224
- workspaceRoot: asString$1(get("workspaceRoot")) ?? base.workspaceRoot,
224
+ workspaceRoot: asString(get("workspaceRoot")) ?? base.workspaceRoot,
225
225
  fallbackModels: Array.isArray(get("fallbackModels")) ? get("fallbackModels").filter((x) => !!x && typeof x === "object" && typeof x.id === "string") : base.fallbackModels,
226
226
  askTool: asBool(get("askTool")) ?? base.askTool,
227
- mediaDir: asString$1(get("mediaDir")) ?? base.mediaDir,
227
+ mediaDir: asString(get("mediaDir")) ?? base.mediaDir,
228
228
  mediaTtlMs: asNum(get("mediaTtlMs")) ?? base.mediaTtlMs,
229
229
  mediaMaxBytes: asNum(get("mediaMaxBytes")) ?? base.mediaMaxBytes,
230
230
  mediaMaxImages: asNum(get("mediaMaxImages")) ?? base.mediaMaxImages,
231
231
  mcpBridge: asBool(get("mcpBridge")) ?? base.mcpBridge,
232
- mcpToolAllowlist: asString$1(get("mcpToolAllowlist")) ?? base.mcpToolAllowlist
232
+ mcpToolAllowlist: asString(get("mcpToolAllowlist")) ?? base.mcpToolAllowlist
233
233
  };
234
234
  if (env.DSH_AGY_ENABLED !== void 0) cfg.enabled = asBool(env.DSH_AGY_ENABLED) ?? cfg.enabled;
235
235
  if (env.DSH_AGY_BIN) cfg.agyBin = env.DSH_AGY_BIN;
@@ -451,6 +451,261 @@ var RunRegistry = class {
451
451
  }
452
452
  };
453
453
  //#endregion
454
+ //#region src/host/mirror-tool.ts
455
+ const MIRROR_TOOL_NAME = "agy_tool";
456
+ /** The only tool this DSH deployment lets a model call directly. */
457
+ const WRAPPER_TOOL_NAME = "run_code";
458
+ /**
459
+ * Build the run_code tool-call arguments that replay one recorded agy tool
460
+ * step. The deployment's tool-dispatch policy only allows `run_code` as a
461
+ * direct model call (everything else must be invoked from inside a
462
+ * program), so the span cut addresses run_code with a generated program
463
+ * whose single statement calls the registered mirror tool โ€” the inner
464
+ * dispatch is what renders the native tool card.
465
+ */
466
+ function buildMirrorRunCode(runId, eventIndex, toolName) {
467
+ const invocation = JSON.stringify({
468
+ run: runId,
469
+ step: eventIndex
470
+ });
471
+ return {
472
+ code: "// dsh-agy-link mirror: replay recorded agy tool step " + eventIndex + " (" + toolName + ")\nreturn await tools['agy_tool'](" + invocation + ")",
473
+ description: "replay agy tool step " + eventIndex + " ยท " + toolName
474
+ };
475
+ }
476
+ /** agy serializes some tool args as a JSON string; presenters get an object. */
477
+ function toolInput(args) {
478
+ const raw = args.input;
479
+ if (raw === void 0 || raw === null) return {};
480
+ if (typeof raw === "object") return raw;
481
+ if (typeof raw === "string") try {
482
+ const parsed = JSON.parse(raw);
483
+ return typeof parsed === "object" && parsed !== null ? parsed : { value: parsed };
484
+ } catch {
485
+ return { value: raw };
486
+ }
487
+ return { value: raw };
488
+ }
489
+ /**
490
+ * agy serializes tool args with PascalCase keys (CommandLine, AbsolutePath,
491
+ * SearchDirectory, โ€ฆ); pick() accepts both spellings so cards always read
492
+ * the real field instead of falling back to raw JSON.
493
+ */
494
+ function pick$1(input, ...keys) {
495
+ for (const k of keys) {
496
+ const v = input[k];
497
+ if (typeof v === "string") return v;
498
+ }
499
+ }
500
+ /** Native pending-card projection for one mirrored agy tool call. */
501
+ function presentMirrorCall(args) {
502
+ const a = args;
503
+ const name = typeof a?.tool === "string" ? a.tool : "";
504
+ const input = toolInput(a);
505
+ switch (name) {
506
+ case "run_command":
507
+ case "bash":
508
+ case "execute_command": return {
509
+ card: "terminal",
510
+ title: pick$1(input, "command", "cmd", "CommandLine", "Cmd") ?? JSON.stringify(input),
511
+ ...pick$1(input, "description", "Description") !== void 0 ? { description: pick$1(input, "description", "Description") } : {},
512
+ ...pick$1(input, "cwd", "Cwd", "WorkingDirectory") !== void 0 ? { cwd: pick$1(input, "cwd", "Cwd", "WorkingDirectory") } : {}
513
+ };
514
+ case "write_to_file":
515
+ case "write_file":
516
+ case "create_file": {
517
+ const path = pick$1(input, "path", "file_path", "filename", "Path", "FilePath", "FileName", "AbsolutePath") ?? "file";
518
+ const content = pick$1(input, "content", "Content", "FileContents", "contents") ?? "";
519
+ return {
520
+ card: "diff",
521
+ title: "Write " + path,
522
+ diffs: [{
523
+ path,
524
+ oldText: null,
525
+ newText: content
526
+ }],
527
+ locations: [{ path }]
528
+ };
529
+ }
530
+ case "edit_file":
531
+ case "replace_in_file":
532
+ case "edit": {
533
+ const path = pick$1(input, "path", "file_path", "Path", "FilePath", "AbsolutePath") ?? "file";
534
+ const newText = pick$1(input, "new_string", "newText", "content", "NewString", "NewText", "Content") ?? "";
535
+ const oldText = pick$1(input, "old_string", "oldText", "OldString", "OldText") ?? null;
536
+ return {
537
+ card: "diff",
538
+ title: "Edit " + path,
539
+ diffs: [{
540
+ path,
541
+ oldText,
542
+ newText
543
+ }],
544
+ locations: [{ path }]
545
+ };
546
+ }
547
+ case "read_file":
548
+ case "view_file":
549
+ case "read":
550
+ case "open_file": {
551
+ const path = pick$1(input, "path", "file_path", "filename", "Path", "FilePath", "FileName", "AbsolutePath") ?? "";
552
+ const offset = typeof input.offset === "number" ? input.offset : typeof input.Offset === "number" ? input.Offset : void 0;
553
+ return {
554
+ card: "generic",
555
+ title: "Read " + path,
556
+ kind: "read",
557
+ ...path !== "" ? { locations: [{
558
+ path,
559
+ ...offset !== void 0 ? { line: offset + 1 } : {}
560
+ }] } : {}
561
+ };
562
+ }
563
+ case "find_by_name":
564
+ case "glob":
565
+ case "search_files":
566
+ case "search":
567
+ case "search_file_content":
568
+ case "grep": {
569
+ const q = pick$1(input, "pattern", "query", "regex", "Pattern", "Query", "Regex", "QueryString") ?? "";
570
+ return {
571
+ card: "generic",
572
+ title: q !== "" ? "Search " + q : "Search",
573
+ kind: "search"
574
+ };
575
+ }
576
+ case "list_dir":
577
+ case "ls": {
578
+ const path = pick$1(input, "path", "directory", "Path", "Directory", "SearchDirectory", "AbsolutePath") ?? "";
579
+ return {
580
+ card: "generic",
581
+ title: path !== "" ? "List " + path : "List directory"
582
+ };
583
+ }
584
+ case "delete_file":
585
+ case "remove_file":
586
+ case "rm": return {
587
+ card: "generic",
588
+ title: "Delete " + (pick$1(input, "path", "file_path", "Path", "FilePath", "AbsolutePath") ?? ""),
589
+ kind: "delete"
590
+ };
591
+ default: return {
592
+ card: "generic",
593
+ title: name !== "" ? name : "agy tool",
594
+ rawInput: input
595
+ };
596
+ }
597
+ }
598
+ /** Native completed-card projection for one mirrored agy tool call. */
599
+ function presentMirrorResult(args, result) {
600
+ const a = args;
601
+ const name = typeof a?.tool === "string" ? a.tool : "";
602
+ const text = resultText$1(result.content);
603
+ switch (name) {
604
+ case "run_command":
605
+ case "bash":
606
+ case "execute_command": return {
607
+ card: "terminal",
608
+ output: text
609
+ };
610
+ case "write_to_file":
611
+ case "write_file":
612
+ case "create_file":
613
+ case "edit_file":
614
+ case "replace_in_file":
615
+ case "edit": {
616
+ const call = presentMirrorCall(args);
617
+ return {
618
+ card: "diff",
619
+ diffs: call !== void 0 && call.card === "diff" ? call.diffs : []
620
+ };
621
+ }
622
+ default: return {
623
+ card: "generic",
624
+ content: [{
625
+ type: "text",
626
+ text: clip(text, 4e3)
627
+ }]
628
+ };
629
+ }
630
+ }
631
+ function resultText$1(content) {
632
+ const parts = [];
633
+ for (const b of Array.isArray(content) ? content : []) {
634
+ const blk = b;
635
+ if (blk && blk.type === "text" && typeof blk.text === "string") parts.push(blk.text);
636
+ }
637
+ return parts.join("\n");
638
+ }
639
+ function clip(s, max) {
640
+ return s.length > max ? s.slice(0, max) + "โ€ฆ (+" + (s.length - max) + " chars)" : s;
641
+ }
642
+ function defineAgyMirrorTool(deps) {
643
+ const enrichArgs = (args) => {
644
+ if (typeof args.tool === "string" && args.tool !== "") return args;
645
+ const runId = typeof args.run === "string" ? args.run : "";
646
+ const step = typeof args.step === "number" ? args.step : -1;
647
+ const t = deps.runs.get(runId)?.toolEventAt(step) ?? null;
648
+ if (t === null) return args;
649
+ return {
650
+ ...args,
651
+ tool: t.name,
652
+ ...args.input === void 0 && t.args !== void 0 ? { input: t.args } : {}
653
+ };
654
+ };
655
+ return defineTool({
656
+ name: MIRROR_TOOL_NAME,
657
+ description: "Internal to the dsh-agy-link bridge: replays one tool activity recorded from a Google Antigravity (agy) CLI run so it renders as a native tool card and rides the agent loop. Emitted automatically by the antigravity provider โ€” do not call it directly.",
658
+ parameters: {
659
+ run: {
660
+ type: "string",
661
+ required: true,
662
+ description: "Recording run id."
663
+ },
664
+ step: {
665
+ type: "number",
666
+ required: true,
667
+ description: "Recorded event index of the tool step."
668
+ },
669
+ tool: {
670
+ type: "string",
671
+ description: "agy tool name that ran (optional โ€” resolved from the recording)."
672
+ },
673
+ input: {
674
+ type: "json",
675
+ description: "agy tool arguments as recorded (optional โ€” resolved from the recording)."
676
+ }
677
+ },
678
+ output: {
679
+ schema: { type: "string" },
680
+ render: (_args, value) => [{
681
+ type: "text",
682
+ text: value
683
+ }]
684
+ },
685
+ presentCall: (args) => presentMirrorCall(enrichArgs(args)),
686
+ presentResult: (args, result) => presentMirrorResult(enrichArgs(args), result),
687
+ async execute(args, exec) {
688
+ exec.signal;
689
+ const runId = typeof args.run === "string" ? args.run : "";
690
+ const step = typeof args.step === "number" ? args.step : -1;
691
+ typeof args.tool === "string" && args.tool;
692
+ const rec = deps.runs.get(runId);
693
+ if (rec === void 0) throw new Error("agy_tool: no recorded agy run \"" + runId + "\" โ€” this tool only replays bridge-recorded activity");
694
+ const t = rec.toolEventAt(step);
695
+ if (t === null) throw new Error("agy_tool: event " + step + " of run " + runId + " is not a completed tool step");
696
+ if (t.error !== void 0) throw new Error("agy tool " + t.name + " failed: " + t.error);
697
+ const out = t.output;
698
+ if (out === void 0 || out === null) return "";
699
+ if (typeof out === "string") return out;
700
+ try {
701
+ return JSON.stringify(out, null, 2);
702
+ } catch {
703
+ return String(out);
704
+ }
705
+ }
706
+ });
707
+ }
708
+ //#endregion
454
709
  //#region src/host/mapper.ts
455
710
  function usageFromRaw(raw) {
456
711
  const usage = {
@@ -543,7 +798,8 @@ var EventMapper = class {
543
798
  if (ev.kind === "step") {
544
799
  if (ev.stepKind === "text") {
545
800
  const thoughtTokens = ev.usage?.thinking_tokens ?? 0;
546
- if (thoughtTokens > 0 && !this.thinkingAnnounced.has(ev.stepKey)) {
801
+ const stepTextEmitted = (this.emittedByKey.get(ev.stepKey) ?? "") !== "";
802
+ if (thoughtTokens > 0 && !stepTextEmitted && !this.thinkingAnnounced.has(ev.stepKey)) {
547
803
  this.thinkingAnnounced.add(ev.stepKey);
548
804
  yield* this.ensureBlock("reasoning");
549
805
  const d = this.appendDelta("[agy thinking turn ยท " + thoughtTokens + " thinking tokens]\n");
@@ -586,13 +842,7 @@ var EventMapper = class {
586
842
  const close = this.closeOpen();
587
843
  if (close) yield close;
588
844
  const idx = this.blockIdx;
589
- const input = ev.tool.args === void 0 ? {} : { input: ev.tool.args };
590
- const argumentsJson = JSON.stringify({
591
- run: this.opts.runId,
592
- step: absIndex,
593
- tool: ev.tool.name,
594
- ...input
595
- });
845
+ const argumentsJson = JSON.stringify(buildMirrorRunCode(this.opts.runId, absIndex, ev.tool.name));
596
846
  yield {
597
847
  type: "block-start",
598
848
  index: idx,
@@ -604,7 +854,7 @@ var EventMapper = class {
604
854
  block: {
605
855
  type: "tool-call",
606
856
  id: CallId(mirrorCallId(this.opts.runId, absIndex)),
607
- name: "agy_tool",
857
+ name: WRAPPER_TOOL_NAME,
608
858
  arguments: argumentsJson
609
859
  }
610
860
  };
@@ -2620,222 +2870,6 @@ function writeDoctorReport(deps) {
2620
2870
  return file;
2621
2871
  }
2622
2872
  //#endregion
2623
- //#region src/host/mirror-tool.ts
2624
- const MIRROR_TOOL_NAME = "agy_tool";
2625
- /** agy serializes some tool args as a JSON string; presenters get an object. */
2626
- function toolInput(args) {
2627
- const raw = args.input;
2628
- if (raw === void 0 || raw === null) return {};
2629
- if (typeof raw === "object") return raw;
2630
- if (typeof raw === "string") try {
2631
- const parsed = JSON.parse(raw);
2632
- return typeof parsed === "object" && parsed !== null ? parsed : { value: parsed };
2633
- } catch {
2634
- return { value: raw };
2635
- }
2636
- return { value: raw };
2637
- }
2638
- function asString(v) {
2639
- return typeof v === "string" ? v : void 0;
2640
- }
2641
- /** Native pending-card projection for one mirrored agy tool call. */
2642
- function presentMirrorCall(args) {
2643
- const a = args;
2644
- const name = typeof a?.tool === "string" ? a.tool : "";
2645
- const input = toolInput(a);
2646
- switch (name) {
2647
- case "run_command":
2648
- case "bash":
2649
- case "execute_command": return {
2650
- card: "terminal",
2651
- title: asString(input.command) ?? asString(input.cmd) ?? JSON.stringify(input),
2652
- ...asString(input.description) !== void 0 ? { description: asString(input.description) } : {},
2653
- ...asString(input.cwd) !== void 0 ? { cwd: asString(input.cwd) } : {}
2654
- };
2655
- case "write_to_file":
2656
- case "write_file":
2657
- case "create_file": {
2658
- const path = asString(input.path) ?? asString(input.file_path) ?? asString(input.filename) ?? "file";
2659
- const content = asString(input.content) ?? "";
2660
- return {
2661
- card: "diff",
2662
- title: "Write " + path,
2663
- diffs: [{
2664
- path,
2665
- oldText: null,
2666
- newText: content
2667
- }],
2668
- locations: [{ path }]
2669
- };
2670
- }
2671
- case "edit_file":
2672
- case "replace_in_file":
2673
- case "edit": {
2674
- const path = asString(input.path) ?? asString(input.file_path) ?? "file";
2675
- const newText = asString(input.new_string) ?? asString(input.newText) ?? asString(input.content) ?? "";
2676
- const oldText = asString(input.old_string) ?? asString(input.oldText) ?? null;
2677
- return {
2678
- card: "diff",
2679
- title: "Edit " + path,
2680
- diffs: [{
2681
- path,
2682
- oldText,
2683
- newText
2684
- }],
2685
- locations: [{ path }]
2686
- };
2687
- }
2688
- case "read_file":
2689
- case "view_file":
2690
- case "read":
2691
- case "open_file": {
2692
- const path = asString(input.path) ?? asString(input.file_path) ?? asString(input.filename) ?? "";
2693
- const offset = typeof input.offset === "number" ? input.offset : void 0;
2694
- return {
2695
- card: "generic",
2696
- title: "Read " + path,
2697
- kind: "read",
2698
- ...path !== "" ? { locations: [{
2699
- path,
2700
- ...offset !== void 0 ? { line: offset + 1 } : {}
2701
- }] } : {}
2702
- };
2703
- }
2704
- case "find_by_name":
2705
- case "glob":
2706
- case "search_files":
2707
- case "search":
2708
- case "search_file_content":
2709
- case "grep": {
2710
- const q = asString(input.pattern) ?? asString(input.query) ?? asString(input.regex) ?? "";
2711
- return {
2712
- card: "generic",
2713
- title: q !== "" ? "Search " + q : "Search",
2714
- kind: "search"
2715
- };
2716
- }
2717
- case "list_dir":
2718
- case "ls": {
2719
- const path = asString(input.path) ?? asString(input.directory) ?? "";
2720
- return {
2721
- card: "generic",
2722
- title: path !== "" ? "List " + path : "List directory"
2723
- };
2724
- }
2725
- case "delete_file":
2726
- case "remove_file":
2727
- case "rm": return {
2728
- card: "generic",
2729
- title: "Delete " + (asString(input.path) ?? asString(input.file_path) ?? ""),
2730
- kind: "delete"
2731
- };
2732
- default: return {
2733
- card: "generic",
2734
- title: name !== "" ? name : "agy tool",
2735
- rawInput: input
2736
- };
2737
- }
2738
- }
2739
- /** Native completed-card projection for one mirrored agy tool call. */
2740
- function presentMirrorResult(args, result) {
2741
- const a = args;
2742
- const name = typeof a?.tool === "string" ? a.tool : "";
2743
- const text = resultText$1(result.content);
2744
- switch (name) {
2745
- case "run_command":
2746
- case "bash":
2747
- case "execute_command": return {
2748
- card: "terminal",
2749
- output: text
2750
- };
2751
- case "write_to_file":
2752
- case "write_file":
2753
- case "create_file":
2754
- case "edit_file":
2755
- case "replace_in_file":
2756
- case "edit": {
2757
- const call = presentMirrorCall(args);
2758
- return {
2759
- card: "diff",
2760
- diffs: call !== void 0 && call.card === "diff" ? call.diffs : []
2761
- };
2762
- }
2763
- default: return {
2764
- card: "generic",
2765
- content: [{
2766
- type: "text",
2767
- text: clip(text, 4e3)
2768
- }]
2769
- };
2770
- }
2771
- }
2772
- function resultText$1(content) {
2773
- const parts = [];
2774
- for (const b of Array.isArray(content) ? content : []) {
2775
- const blk = b;
2776
- if (blk && blk.type === "text" && typeof blk.text === "string") parts.push(blk.text);
2777
- }
2778
- return parts.join("\n");
2779
- }
2780
- function clip(s, max) {
2781
- return s.length > max ? s.slice(0, max) + "โ€ฆ (+" + (s.length - max) + " chars)" : s;
2782
- }
2783
- function defineAgyMirrorTool(deps) {
2784
- return defineTool({
2785
- name: MIRROR_TOOL_NAME,
2786
- description: "Internal to the dsh-agy-link bridge: replays one tool activity recorded from a Google Antigravity (agy) CLI run so it renders as a native tool card and rides the agent loop. Emitted automatically by the antigravity provider โ€” do not call it directly.",
2787
- parameters: {
2788
- run: {
2789
- type: "string",
2790
- required: true,
2791
- description: "Recording run id."
2792
- },
2793
- step: {
2794
- type: "number",
2795
- required: true,
2796
- description: "Recorded event index of the tool step."
2797
- },
2798
- tool: {
2799
- type: "string",
2800
- required: true,
2801
- description: "agy tool name that ran."
2802
- },
2803
- input: {
2804
- type: "json",
2805
- description: "agy tool arguments as recorded."
2806
- }
2807
- },
2808
- output: {
2809
- schema: { type: "string" },
2810
- render: (_args, value) => [{
2811
- type: "text",
2812
- text: value
2813
- }]
2814
- },
2815
- presentCall: (args) => presentMirrorCall(args),
2816
- presentResult: (args, result) => presentMirrorResult(args, result),
2817
- async execute(args, exec) {
2818
- exec.signal;
2819
- const runId = typeof args.run === "string" ? args.run : "";
2820
- const step = typeof args.step === "number" ? args.step : -1;
2821
- typeof args.tool === "string" && args.tool;
2822
- const rec = deps.runs.get(runId);
2823
- if (rec === void 0) throw new Error("agy_tool: no recorded agy run \"" + runId + "\" โ€” this tool only replays bridge-recorded activity");
2824
- const t = rec.toolEventAt(step);
2825
- if (t === null) throw new Error("agy_tool: event " + step + " of run " + runId + " is not a completed tool step");
2826
- if (t.error !== void 0) throw new Error("agy tool " + t.name + " failed: " + t.error);
2827
- const out = t.output;
2828
- if (out === void 0 || out === null) return "";
2829
- if (typeof out === "string") return out;
2830
- try {
2831
- return JSON.stringify(out, null, 2);
2832
- } catch {
2833
- return String(out);
2834
- }
2835
- }
2836
- });
2837
- }
2838
- //#endregion
2839
2873
  //#region src/host/sessions.ts
2840
2874
  var SessionStore = class {
2841
2875
  file;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-agy-link",
3
- "version": "0.3.2",
3
+ "version": "0.3.3",
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",