dsh-agy-link 0.2.6 → 0.2.7

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/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.2.7 (2026-08-19)
4
+
5
+ - Visible agy status in DSH: conversation header `AGY` pill, Settings → Antigravity status page, and sidebar footer label.
6
+ - Workspace auto-binding: agy now runs in the DSH session's `cwd` when `workspaceRoot` is not configured.
7
+ - New `/agy workspace [path]` command and `DSH_AGY_WORKSPACE_ROOT` env.
8
+ - README language switcher at the top (中文 / English).
9
+
3
10
  ## 0.2.2 (2026-08-19)
4
11
 
5
12
  - README: dedicated Prerequisites section (DSH, Node >= 24, the agy CLI with Google's official install guide link, first-run login, subscription note), a "how it works" intro paragraph, and an honest "what it cannot do" list in both languages - onboarding now covers first-time users.
package/README.md CHANGED
@@ -11,6 +11,12 @@
11
11
  <img src="https://img.shields.io/badge/node-%3E%3D24-green" alt="node"/>
12
12
  </p>
13
13
 
14
+ <p align="center">
15
+ <b>🌐 Language / 语言:</b>
16
+ <a href="#中文">中文</a> ·
17
+ <a href="#english">English</a>
18
+ </p>
19
+
14
20
  ---
15
21
 
16
22
  # 中文
@@ -31,9 +37,10 @@
31
37
  | 🌊 **完整流式** | 文本、思考、agy 工具活动(注记为 reasoning 块)全部映射到 DSH 原生流协议 |
32
38
  | 🔗 **会话连续** | 每个 DSH 会话绑定原生 agy 会话(`--conversation`),多轮上下文由 agy 历史承载,不重发全量 |
33
39
  | 📊 **token 用量** | 输入/输出/思考/缓存 token 全部进入 DSH 用量统计 |
40
+ | ⚙️ **设置页状态** | DSH 设置 → Antigravity 页面显示 agy 连接/登录/工作区/绑定数/最近运行状态 |
34
41
  | 🔐 **GUI 内 Google 登录** | `/agy auth` 打印授权 URL;侧栏面板提供二维码 + 授权码粘贴框 |
35
42
  | 🤝 **`agy_ask` 工具** | 任何 DSH 模型都可以把一次性任务委托给 Antigravity 模型(AskAntigravity 模式) |
36
- | ⌨️ **`/agy` 命令族** | `status` / `auth` / `auth-code` / `models` / `mode` / `effort` / `clear` / `doctor` / `help` |
43
+ | ⌨️ **`/agy` 命令族** | `status` / `auth` / `auth-code` / `models` / `mode` / `effort` / `workspace` / `clear` / `doctor` / `help` |
37
44
  | 🖼 **图片多模态(v0.2)** | 图片落盘到本地媒体目录(TTL 清理),prompt 以绝对路径引用 + `--add-dir` 授权,agy 用自己的工具看图 |
38
45
  | 📎 **文件内联 + 结构化输出(v0.2)** | `agy_ask` 新增 `readPaths`(文本文件内联)与 `schema`(`--json-schema` 强约束答案) |
39
46
  | 🌉 **MCP 反向桥(v0.2 实验)** | `mcpBridge: true` 开启后 agy 可直接调 DSH 侧工具(回环 + token 守卫端点 + 零依赖 stdio MCP 服务器,`.mcp.json` 合并写入/禁用还原) |
@@ -90,6 +97,7 @@ dsh plugin --profile web add dsh-agy-link
90
97
  | defaultEffort | `DSH_AGY_DEFAULT_EFFORT` | `(模型默认)` | `low` / `medium` / `high` |
91
98
  | timeoutMs | `DSH_AGY_TIMEOUT_MS` | `600000` | 单轮看门狗 |
92
99
  | extraArgs | `DSH_AGY_EXTRA_ARGS` | — | 附加 agy 参数(空格分隔) |
100
+ | workspaceRoot | `DSH_AGY_WORKSPACE_ROOT` | 会话 cwd | agy 工作区;显式配置优先,未设置时自动使用 DSH 会话的 cwd |
93
101
 
94
102
  ### ⚠️ 权限模式——必读
95
103
 
@@ -140,9 +148,10 @@ Bring **Google Antigravity models into DeepSeek Harness (DSH)** — chat, thinki
140
148
  | 🌊 **Full streaming** | Text, thinking (reasoning), and agy tool activity (annotated reasoning blocks) mapped onto DSH's native chunk protocol |
141
149
  | 🔗 **Session continuity** | Each DSH session binds to a native agy conversation (`--conversation`); multi-turn context rides agy history instead of re-sending everything |
142
150
  | 📊 **Token usage** | Input/output/thinking/cache tokens surface in DSH usage accounting |
151
+ | ⚙️ **Settings status** | DSH Settings → Antigravity page shows agy connection/login/workspace/bindings/last-run state |
143
152
  | 🔐 **In-GUI Google login** | `/agy auth` prints the consent URL; the sidebar panel adds a QR code and a paste box for the authorization code |
144
153
  | 🤝 **`agy_ask` tool** | Let any DSH model delegate a one-shot task to an Antigravity model (the AskAntigravity pattern) |
145
- | ⌨️ **`/agy` commands** | `status`, `auth`, `auth-code`, `models`, `mode`, `effort`, `clear`, `doctor`, `help` |
154
+ | ⌨️ **`/agy` commands** | `status`, `auth`, `auth-code`, `models`, `mode`, `effort`, `workspace`, `clear`, `doctor`, `help` |
146
155
  | 😴 **Dormant-safe** | No agy binary? Not signed in? The plugin loads anyway and tells you what to fix |
147
156
  | 🖼 **Image multimodal (v0.2)** | image attachments staged to a TTL-swept local dir, referenced by absolute path with `--add-dir`; agy views them with its own tools |
148
157
  | 📎 **File inlining + structured output (v0.2)** | `agy_ask` gains `readPaths` (inline text files) and `schema` (enforced via `--json-schema`) |
@@ -199,6 +208,7 @@ Config lives in the `agy-link` plugin entry (edit via `/plugin` or the profile p
199
208
  | defaultEffort | `DSH_AGY_DEFAULT_EFFORT` | `(model default)` | `low` / `medium` / `high` |
200
209
  | timeoutMs | `DSH_AGY_TIMEOUT_MS` | `600000` | per-turn watchdog |
201
210
  | extraArgs | `DSH_AGY_EXTRA_ARGS` | — | extra agy flags, space-separated |
211
+ | workspaceRoot | `DSH_AGY_WORKSPACE_ROOT` | session cwd | agy workspace; explicit config wins, otherwise the DSH session's cwd is used |
202
212
 
203
213
  ### ⚠️ Permission modes — read this
204
214
 
package/cordis.patch.yml CHANGED
@@ -10,5 +10,6 @@
10
10
  # permissionMode: skip | plan | accept-edits (skip passes
11
11
  # --dangerously-skip-permissions; see README security notes)
12
12
  permissionMode: !!js "process.env.DSH_AGY_MODE ?? 'skip'"
13
+ workspaceRoot: !!js "process.env.DSH_AGY_WORKSPACE_ROOT ?? ''"
13
14
  defaultModel: !!js "process.env.DSH_AGY_DEFAULT_MODEL ?? ''"
14
15
  defaultEffort: !!js "process.env.DSH_AGY_DEFAULT_EFFORT ?? ''"
package/dist/client.js CHANGED
@@ -73,7 +73,8 @@ window.__ModuleLoader__.load({
73
73
  }
74
74
  };
75
75
  function apply(ctx) {
76
- const AgyPanel = () => {
76
+ const AgyPanel = (props) => {
77
+ const { wide = false } = props ?? {};
77
78
  const [open, setOpen] = useState(false);
78
79
  const [status, setStatus] = useState(null);
79
80
  const [code, setCode] = useState("");
@@ -140,14 +141,18 @@ window.__ModuleLoader__.load({
140
141
  height: "9px",
141
142
  borderRadius: "50%",
142
143
  background: dot
143
- } }));
144
+ } }), wide ? h("span", { style: {
145
+ marginLeft: "6px",
146
+ fontSize: "12px",
147
+ lineHeight: 1
148
+ } }, "AGY") : null);
144
149
  if (!open) return badge;
145
150
  const needsAuth = status !== null && !status.dormantReason && status.auth !== void 0 && status.auth.phase !== "ok";
146
151
  const dormantLine = h("div", null, "Bridge dormant: ", status?.dormantReason ?? "");
147
152
  const infoBlock = h("div", { style: {
148
153
  ...S.muted,
149
154
  lineHeight: 1.5
150
- } }, h("div", null, "agy: ", status?.bin ?? "not found", " — v", status?.version ?? "?"), h("div", null, "auth: ", status?.auth?.phase ?? "unknown"), h("div", null, "models: ", String(status?.catalog?.count ?? 0), " — ", status?.catalog?.source ?? ""), h("div", null, "bindings: ", String(status?.bindings ?? 0)), h("div", null, "last run: ", status?.lastRun ? (status.lastRun.ok ? "ok" : status.lastRun.code) + " — " + status.lastRun.model : "none"));
155
+ } }, h("div", null, "agy: ", status?.bin ?? "not found", " — v", status?.version ?? "?"), h("div", null, "auth: ", status?.auth?.phase ?? "unknown"), h("div", null, "models: ", String(status?.catalog?.count ?? 0), " — ", status?.catalog?.source ?? ""), h("div", null, "workspace: ", status?.workspaceRoot ? status.workspaceRoot : "(session cwd / process cwd)"), h("div", null, "bindings: ", String(status?.bindings ?? 0)), h("div", null, "last run: ", status?.lastRun ? (status.lastRun.ok ? "ok" : status.lastRun.code) + " — " + status.lastRun.model : "none"));
151
156
  const modeRow = h("div", { style: {
152
157
  ...S.row,
153
158
  marginTop: "8px"
@@ -244,6 +249,84 @@ window.__ModuleLoader__.load({
244
249
  } }, header, body);
245
250
  return h("div", { style: { display: "contents" } }, badge, portalToBody(popover));
246
251
  };
252
+ const AgySettingsSection = () => {
253
+ const [status, setStatus] = useState(null);
254
+ useEffect(() => {
255
+ let alive = true;
256
+ const tick = async () => {
257
+ const st = await getStatus();
258
+ if (alive) setStatus(st);
259
+ };
260
+ tick();
261
+ const timer = setInterval(tick, 3e3);
262
+ return () => {
263
+ alive = false;
264
+ clearInterval(timer);
265
+ };
266
+ }, []);
267
+ const summary = status === null ? "Loading…" : status.dormantReason ? "Not ready — " + status.dormantReason : status.auth?.phase === "ok" ? "Ready — agy is connected" : "Needs Google login — open the sidebar dot or run /agy auth";
268
+ const color = status === null ? "#9aa0a6" : status.dormantReason ? "#f5a623" : status.auth?.phase === "ok" ? "#30a46c" : "#f5a623";
269
+ return h("div", { style: { lineHeight: 1.6 } }, h("div", { style: {
270
+ display: "flex",
271
+ alignItems: "center",
272
+ gap: "8px",
273
+ fontWeight: 600,
274
+ fontSize: "14px"
275
+ } }, h("span", { style: {
276
+ display: "inline-block",
277
+ width: "10px",
278
+ height: "10px",
279
+ borderRadius: "50%",
280
+ background: color
281
+ } }), "Antigravity (agy CLI)"), h("div", { style: {
282
+ ...S.muted,
283
+ margin: "6px 0 12px"
284
+ } }, summary), h("div", { style: {
285
+ ...S.muted,
286
+ lineHeight: 1.6
287
+ } }, h("div", null, "agy binary: ", status?.bin ?? "not found", status?.version ? " — v" + status.version : ""), h("div", null, "auth: ", status?.auth?.phase ?? "unknown"), h("div", null, "workspace: ", status?.workspaceRoot ? status.workspaceRoot : "(session cwd / process cwd)"), h("div", null, "models: ", String(status?.catalog?.count ?? 0), " — ", status?.catalog?.source ?? ""), h("div", null, "bindings: ", String(status?.bindings ?? 0)), h("div", null, "last run: ", status?.lastRun ? (status.lastRun.ok ? "ok" : status.lastRun.code) + " — " + status.lastRun.model : "none")), h("div", { style: S.muted }, "Run `/agy status` for full diagnostics, or use the sidebar dot for quick login/controls."));
288
+ };
289
+ const AgySessionStatus = () => {
290
+ const [status, setStatus] = useState(null);
291
+ useEffect(() => {
292
+ let alive = true;
293
+ const tick = async () => {
294
+ const st = await getStatus();
295
+ if (alive) setStatus(st);
296
+ };
297
+ tick();
298
+ const timer = setInterval(tick, 3e3);
299
+ return () => {
300
+ alive = false;
301
+ clearInterval(timer);
302
+ };
303
+ }, []);
304
+ const color = status === null ? "#9aa0a6" : status.dormantReason ? "#f5a623" : status.auth?.phase === "ok" ? "#30a46c" : "#f5a623";
305
+ const title = status === null ? "Antigravity (agy) status…" : status.dormantReason ? "Antigravity not ready: " + status.dormantReason : status.auth?.phase === "ok" ? "Antigravity ready — agy is connected" : "Antigravity needs Google login";
306
+ return h("button", {
307
+ type: "button",
308
+ title,
309
+ style: {
310
+ background: "transparent",
311
+ border: "1px solid rgba(128,128,128,0.3)",
312
+ borderRadius: "999px",
313
+ cursor: "default",
314
+ padding: "2px 8px",
315
+ fontSize: "11px",
316
+ lineHeight: 1.5,
317
+ color: "inherit",
318
+ display: "inline-flex",
319
+ alignItems: "center",
320
+ gap: "5px"
321
+ }
322
+ }, h("span", { style: {
323
+ display: "inline-block",
324
+ width: "8px",
325
+ height: "8px",
326
+ borderRadius: "50%",
327
+ background: color
328
+ } }), "AGY");
329
+ };
247
330
  ctx.slots.inject("sidebar.footer.action", () => {
248
331
  return ctx.slots.register({
249
332
  name: "sidebar.footer.action",
@@ -252,6 +335,22 @@ window.__ModuleLoader__.load({
252
335
  label: "Antigravity"
253
336
  }, AgyPanel);
254
337
  });
338
+ ctx.slots.inject("conversation.session.header.actions", () => {
339
+ return ctx.slots.register({
340
+ name: "conversation.session.header.actions",
341
+ id: "agy-link-status",
342
+ order: 10,
343
+ label: "Antigravity"
344
+ }, AgySessionStatus);
345
+ });
346
+ ctx.slots.inject("settings.section", () => {
347
+ return ctx.slots.register({
348
+ name: "settings.section",
349
+ id: "agy-link",
350
+ order: 20,
351
+ label: "Antigravity"
352
+ }, AgySettingsSection);
353
+ });
255
354
  }
256
355
  //#endregion
257
356
  exports.apply = apply;
package/dist/index.js CHANGED
@@ -248,6 +248,7 @@ function resolveConfig(entry, env = process.env, overrides = readOverrides()) {
248
248
  if (t && t > 0) cfg.timeoutMs = t;
249
249
  }
250
250
  if (env.DSH_AGY_EXTRA_ARGS) cfg.extraArgs = env.DSH_AGY_EXTRA_ARGS.split(/\s+/).filter(Boolean);
251
+ if (env.DSH_AGY_WORKSPACE_ROOT) cfg.workspaceRoot = env.DSH_AGY_WORKSPACE_ROOT;
251
252
  if (env.DSH_AGY_MEDIA_DIR) cfg.mediaDir = env.DSH_AGY_MEDIA_DIR;
252
253
  if (env.DSH_AGY_MEDIA_TTL_MS) {
253
254
  const t = asNum(env.DSH_AGY_MEDIA_TTL_MS);
@@ -1452,6 +1453,7 @@ var AgyAdapter = class extends LlmAdapter {
1452
1453
  const isAux = options.purpose === "compaction" || options.purpose === "session-title";
1453
1454
  if (isAux && !cfg.allowAuxiliary) throw new LlmError("auxiliary calls are disabled for the antigravity route (allowAuxiliary: false)", Err.AUX_DISABLED);
1454
1455
  const sessionKey = options.sessionId !== void 0 ? String(options.sessionId) : "";
1456
+ const workspaceRoot = cfg.workspaceRoot !== "" ? cfg.workspaceRoot : this.deps.sessionCwd?.(sessionKey) || process.cwd();
1455
1457
  const catalog = this.deps.catalog.get();
1456
1458
  const model = options.model;
1457
1459
  const entry = findEntry(catalog, model);
@@ -1521,7 +1523,8 @@ var AgyAdapter = class extends LlmAdapter {
1521
1523
  if (prompt.trim() === "") throw new LlmError("request carries no user text or images to forward to agy", Err.AGY_ERROR);
1522
1524
  } else if (prompt.trim() === "") throw new LlmError("request carries no user text to forward to agy", Err.AGY_ERROR);
1523
1525
  const before = snapshotConversations();
1524
- const mapper = new EventMapper({ toolOutput: this.deps.toolOutput });
1526
+ const toolOutput = this.deps.toolOutput;
1527
+ const mapper = new EventMapper({ toolOutput: toolOutput ? (name, args, output) => toolOutput(name, args, output, workspaceRoot) : void 0 });
1525
1528
  const parser = new StreamJsonParser();
1526
1529
  this.deps.onParser?.(parser);
1527
1530
  const queue = new ChunkQueue();
@@ -1548,7 +1551,7 @@ var AgyAdapter = class extends LlmAdapter {
1548
1551
  proc = startAgyProcess({
1549
1552
  bin,
1550
1553
  args,
1551
- cwd: cfg.workspaceRoot !== "" ? cfg.workspaceRoot : void 0,
1554
+ cwd: workspaceRoot,
1552
1555
  timeoutMs: cfg.timeoutMs,
1553
1556
  signal: options.signal,
1554
1557
  onLine: (line) => {
@@ -2070,6 +2073,7 @@ const HELP = [
2070
2073
  "- `/agy models` — refresh and list discovered models",
2071
2074
  "- `/agy mode <skip|plan|accept-edits>` — permission mode (next turn)",
2072
2075
  "- `/agy effort <low|medium|high|default>` — default reasoning effort",
2076
+ "- `/agy workspace [path]` — set the agy working directory (absolute path; omit to show)",
2073
2077
  "- `/agy clear` — drop the most recent conversation binding",
2074
2078
  "- `/agy doctor` — write a diagnostic report and return its path",
2075
2079
  "- `/agy help` — this text"
@@ -2133,6 +2137,16 @@ async function handle(deps, raw) {
2133
2137
  deps.setOverride("defaultEffort", arg === "default" ? "" : arg);
2134
2138
  return ok("Default effort set to **" + (arg === "default" ? "model default" : arg) + "**.");
2135
2139
  }
2140
+ if (sub === "workspace") {
2141
+ if (arg === "") return ok("Current workspace: " + (deps.cfg().workspaceRoot !== "" ? deps.cfg().workspaceRoot : "(session cwd / process cwd)"));
2142
+ if (arg === "default" || arg === "clear") {
2143
+ deps.setOverride("workspaceRoot", "");
2144
+ return ok("Workspace reset to session cwd / process cwd.");
2145
+ }
2146
+ if (!isAbsolute(arg)) return err("workspace must be an absolute path (or `default` to clear)");
2147
+ deps.setOverride("workspaceRoot", arg);
2148
+ return ok("Workspace set to **" + arg + "** — effective next turn.");
2149
+ }
2136
2150
  if (sub === "clear") {
2137
2151
  const all = deps.store().all();
2138
2152
  const keys = Object.keys(all);
@@ -2162,6 +2176,7 @@ function renderStatus(deps) {
2162
2176
  "- version: " + (deps.version() ?? "unknown"),
2163
2177
  "- auth: " + (auth ? auth.phase + (auth.message ? " — " + auth.message : "") : "unknown"),
2164
2178
  "- permission mode: " + cfg.permissionMode + (cfg.permissionMode === "skip" ? " — WARNING: agy runs tools without approval" : ""),
2179
+ "- workspace: " + (cfg.workspaceRoot !== "" ? cfg.workspaceRoot : "(session cwd / process cwd)"),
2165
2180
  "- default model: " + (cfg.defaultModel === "" ? "(agy default)" : cfg.defaultModel),
2166
2181
  "- default effort: " + (cfg.defaultEffort === "" ? "(model default)" : cfg.defaultEffort),
2167
2182
  "- catalog: " + cat.models.length + " models — " + cat.source + (cat.lastError === void 0 ? "" : " — last error: " + cat.lastError),
@@ -2591,9 +2606,12 @@ function apply(ctx, entryConfig = {}) {
2591
2606
  bin,
2592
2607
  acquire: () => semaphore.acquire(),
2593
2608
  log,
2594
- toolOutput: (name, args, output) => {
2609
+ toolOutput: (name, args, output, cwd) => {
2595
2610
  const ws = getConfig().workspaceRoot;
2596
- return renderToolActivity(name, args, output, ws !== "" ? ws : process.cwd());
2611
+ return renderToolActivity(name, args, output, cwd ?? (ws !== "" ? ws : process.cwd()));
2612
+ },
2613
+ sessionCwd: (sessionId) => {
2614
+ return ctx.get("sessions")?.get(sessionId)?.header?.cwd;
2597
2615
  },
2598
2616
  onRun: (info) => {
2599
2617
  lastRun = info;
@@ -2727,6 +2745,7 @@ function apply(ctx, entryConfig = {}) {
2727
2745
  dormantReason,
2728
2746
  enabled: cfg.enabled,
2729
2747
  permissionMode: cfg.permissionMode,
2748
+ workspaceRoot: cfg.workspaceRoot,
2730
2749
  defaultModel: cfg.defaultModel,
2731
2750
  defaultEffort: cfg.defaultEffort,
2732
2751
  askTool: cfg.askTool,
@@ -2792,7 +2811,8 @@ function apply(ctx, entryConfig = {}) {
2792
2811
  "permissionMode",
2793
2812
  "defaultModel",
2794
2813
  "defaultEffort",
2795
- "askTool"
2814
+ "askTool",
2815
+ "workspaceRoot"
2796
2816
  ].includes(key)) {
2797
2817
  sendJson(res, 400, { error: "key not settable" });
2798
2818
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-agy-link",
3
- "version": "0.2.6",
3
+ "version": "0.2.7",
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",