dsh-agy-link 0.2.7 → 0.2.9
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 +52 -0
- package/README.md +5 -5
- package/dist/client.js +20 -101
- package/dist/index.js +272 -102
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,57 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.2.9 (2026-08-19)
|
|
4
|
+
|
|
5
|
+
- **Tool activity moved out of the thinking panel into the reply body.**
|
|
6
|
+
User feedback on 0.2.8: tool annotations hidden inside the DSH thinking
|
|
7
|
+
fold were invisible and felt wrong. agy tools now render as visible
|
|
8
|
+
`🔧 [agy tool: name] args -> output` lines in the message body (they
|
|
9
|
+
cannot become native DSH tool cards: a finish:tool-calls would make the
|
|
10
|
+
DSH agent try to execute tools it does not own — agy runs its own closed
|
|
11
|
+
tool loop). The thinking panel keeps only what is genuinely thinking
|
|
12
|
+
signal: `[agy thinking turn · N thinking tokens]` turn annotations
|
|
13
|
+
(agy print mode never streams thinking text) and terminal error notes.
|
|
14
|
+
|
|
15
|
+
## 0.2.8 (2026-08-19)
|
|
16
|
+
|
|
17
|
+
- **Fix: real agy 1.1.15 stream-json parsing.** The parser only understood
|
|
18
|
+
flat, hypothesized event shapes; the live CLI nests every step payload
|
|
19
|
+
under a `step_update` envelope and uses a different vocabulary
|
|
20
|
+
(`agent_response` + `text_delta` fragments, `tool` with `tool_name` /
|
|
21
|
+
`tool_info` with `parameters` / `output` / `error`). As a result thinking
|
|
22
|
+
and tool activity were silently dropped and only the final result text
|
|
23
|
+
survived. Both shapes are now parsed (legacy aliases kept).
|
|
24
|
+
- **New: visible thinking turns + tool activity.** agy does not stream
|
|
25
|
+
thinking text in print mode (only `thinking_tokens` usage), so each
|
|
26
|
+
thinking-only `agent_response` turn surfaces as an annotated reasoning
|
|
27
|
+
block (`[agy thinking turn · N thinking tokens]`), tool calls render as
|
|
28
|
+
`[agy tool: name] args -> output` reasoning annotations, and failed tools
|
|
29
|
+
render `[agy tool error: name] message`.
|
|
30
|
+
- **Fix: `result.status=ERROR` with a usable response** (e.g. a tool timed
|
|
31
|
+
out mid-run) no longer discards the answer — the response streams, the
|
|
32
|
+
error is annotated, the turn finishes normally. A bare envelope error
|
|
33
|
+
now maps to a precise `AGY_ERROR` finish instead of INVALID_OUTPUT.
|
|
34
|
+
- **Fix: `agy models` deadlock — stdin pipe never closed.** agy reads stdin
|
|
35
|
+
when it is a pipe and waits for EOF; every non-auth spawn kept the pipe
|
|
36
|
+
open, so model discovery silently timed out and the catalog always fell
|
|
37
|
+
back to the bundled list. Stdin now closes right after spawn (auth probes
|
|
38
|
+
keep it open).
|
|
39
|
+
- **Fix: auth phase was always `idle`** until someone ran `/agy auth`, so
|
|
40
|
+
the settings page claimed "needs Google login" for signed-in users.
|
|
41
|
+
Status surfaces now lazily probe the real login state via `agy models`
|
|
42
|
+
(60s cache) and report `ok` / `signed-out` truthfully.
|
|
43
|
+
- **Fix: `/plugins/agy-link/*` routes never registered on dsh web** (settings
|
|
44
|
+
page stuck on `binary: not found / auth: unknown / models: 0`). Routes are
|
|
45
|
+
now registered through a reactive `ctx.inject(['webServer'])` sub-fiber,
|
|
46
|
+
so they attach whenever the service appears instead of racing it. The
|
|
47
|
+
settings page also renders an honest "endpoint unreachable" state instead
|
|
48
|
+
of misleading placeholders when the route is missing.
|
|
49
|
+
- **Removed: sidebar footer AGY button** (user request). Login (QR +
|
|
50
|
+
authorization code) and mode/effort quick controls moved to Settings →
|
|
51
|
+
Antigravity; the conversation header `AGY` pill stays.
|
|
52
|
+
- Tests: real-1.1.15 fixture modes (`real`, `real-error`, `real-fail`) and
|
|
53
|
+
parser/mapper/adapter coverage for the nested format (73 tests).
|
|
54
|
+
|
|
3
55
|
## 0.2.7 (2026-08-19)
|
|
4
56
|
|
|
5
57
|
- Visible agy status in DSH: conversation header `AGY` pill, Settings → Antigravity status page, and sidebar footer label.
|
package/README.md
CHANGED
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
| 🔗 **会话连续** | 每个 DSH 会话绑定原生 agy 会话(`--conversation`),多轮上下文由 agy 历史承载,不重发全量 |
|
|
39
39
|
| 📊 **token 用量** | 输入/输出/思考/缓存 token 全部进入 DSH 用量统计 |
|
|
40
40
|
| ⚙️ **设置页状态** | DSH 设置 → Antigravity 页面显示 agy 连接/登录/工作区/绑定数/最近运行状态 |
|
|
41
|
-
| 🔐 **GUI 内 Google 登录** | `/agy auth` 打印授权 URL
|
|
41
|
+
| 🔐 **GUI 内 Google 登录** | `/agy auth` 打印授权 URL;设置 → Antigravity 页面提供二维码 + 授权码粘贴框 |
|
|
42
42
|
| 🤝 **`agy_ask` 工具** | 任何 DSH 模型都可以把一次性任务委托给 Antigravity 模型(AskAntigravity 模式) |
|
|
43
43
|
| ⌨️ **`/agy` 命令族** | `status` / `auth` / `auth-code` / `models` / `mode` / `effort` / `workspace` / `clear` / `doctor` / `help` |
|
|
44
44
|
| 🖼 **图片多模态(v0.2)** | 图片落盘到本地媒体目录(TTL 清理),prompt 以绝对路径引用 + `--add-dir` 授权,agy 用自己的工具看图 |
|
|
@@ -75,7 +75,7 @@ dsh plugin --profile web add dsh-agy-link
|
|
|
75
75
|
|
|
76
76
|
# 3. 登录(一次性;终端里已经跑过 agy 登录过的可跳过):
|
|
77
77
|
# /agy auth ← 打开授权 URL,批准并复制授权码
|
|
78
|
-
# /agy auth-code <授权码> ←
|
|
78
|
+
# /agy auth-code <授权码> ← 或直接用设置 → Antigravity 页面的二维码 + 粘贴框
|
|
79
79
|
|
|
80
80
|
# 4. /model 选择器里选 antigravity 模型,开聊
|
|
81
81
|
```
|
|
@@ -111,7 +111,7 @@ DSH 侧的工具与权限不受影响——这里只约束 agy 子进程在自
|
|
|
111
111
|
|
|
112
112
|
## 🧩 工作原理
|
|
113
113
|
|
|
114
|
-
一次 DSH 模型调用 = 一个短生命周期 `agy -p --output-format stream-json` 进程。NDJSON 事件流被解析、归一化并映射为 DSH StreamChunk
|
|
114
|
+
一次 DSH 模型调用 = 一个短生命周期 `agy -p --output-format stream-json` 进程。NDJSON 事件流被解析、归一化并映射为 DSH StreamChunk:文本 → text 块,工具活动 → 注记 reasoning 块(含失败注记),结果信封 → usage + finish。agy 的 print 模式**不输出思考文本**(只有 `thinking_tokens` 计数),因此每个思考轮次以 `[agy thinking turn · N thinking tokens]` 注记呈现,思考 token 也进入用量统计。会话 id 优先取自流本身,conversations 目录快照对比兜底。**不逆向数据库、不解码 protobuf、不碰 token 文件**——只启动官方未修改的 agy 二进制。
|
|
115
115
|
|
|
116
116
|
## 📋 它做不到什么(诚实清单)
|
|
117
117
|
|
|
@@ -149,7 +149,7 @@ Bring **Google Antigravity models into DeepSeek Harness (DSH)** — chat, thinki
|
|
|
149
149
|
| 🔗 **Session continuity** | Each DSH session binds to a native agy conversation (`--conversation`); multi-turn context rides agy history instead of re-sending everything |
|
|
150
150
|
| 📊 **Token usage** | Input/output/thinking/cache tokens surface in DSH usage accounting |
|
|
151
151
|
| ⚙️ **Settings status** | DSH Settings → Antigravity page shows agy connection/login/workspace/bindings/last-run state |
|
|
152
|
-
| 🔐 **In-GUI Google login** | `/agy auth` prints the consent URL;
|
|
152
|
+
| 🔐 **In-GUI Google login** | `/agy auth` prints the consent URL; Settings → Antigravity adds a QR code and a paste box for the authorization code |
|
|
153
153
|
| 🤝 **`agy_ask` tool** | Let any DSH model delegate a one-shot task to an Antigravity model (the AskAntigravity pattern) |
|
|
154
154
|
| ⌨️ **`/agy` commands** | `status`, `auth`, `auth-code`, `models`, `mode`, `effort`, `workspace`, `clear`, `doctor`, `help` |
|
|
155
155
|
| 😴 **Dormant-safe** | No agy binary? Not signed in? The plugin loads anyway and tells you what to fix |
|
|
@@ -186,7 +186,7 @@ dsh plugin --profile web add dsh-agy-link
|
|
|
186
186
|
|
|
187
187
|
# 3. Login (one time; skip if you already ran agy in a terminal):
|
|
188
188
|
# /agy auth <- open the consent URL, approve, copy the code
|
|
189
|
-
# /agy auth-code <code> <- or use
|
|
189
|
+
# /agy auth-code <code> <- or use Settings -> Antigravity (QR + paste box)
|
|
190
190
|
|
|
191
191
|
# 4. Pick an antigravity model in /model and chat
|
|
192
192
|
```
|
package/dist/client.js
CHANGED
|
@@ -6,10 +6,7 @@ window.__ModuleLoader__.load({
|
|
|
6
6
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
7
7
|
//#region src/client/index.ts
|
|
8
8
|
const { createElement: h, useState, useEffect } = require("react");
|
|
9
|
-
const reactDom = require("react-dom");
|
|
10
9
|
const win = globalThis;
|
|
11
|
-
const bodyEl = win.document?.body;
|
|
12
|
-
const portalToBody = bodyEl != null && reactDom.createPortal ? (node) => reactDom.createPortal(node, bodyEl) : (node) => node;
|
|
13
10
|
const name = "dsh-agy-link-client";
|
|
14
11
|
const inject = ["slots"];
|
|
15
12
|
const base = win.location?.origin ?? "";
|
|
@@ -73,15 +70,12 @@ window.__ModuleLoader__.load({
|
|
|
73
70
|
}
|
|
74
71
|
};
|
|
75
72
|
function apply(ctx) {
|
|
76
|
-
const
|
|
77
|
-
const { wide = false } = props ?? {};
|
|
78
|
-
const [open, setOpen] = useState(false);
|
|
73
|
+
const AgySettingsSection = () => {
|
|
79
74
|
const [status, setStatus] = useState(null);
|
|
80
75
|
const [code, setCode] = useState("");
|
|
81
76
|
const [busy, setBusy] = useState(false);
|
|
82
77
|
const [notice, setNotice] = useState("");
|
|
83
78
|
useEffect(() => {
|
|
84
|
-
if (!open) return;
|
|
85
79
|
let alive = true;
|
|
86
80
|
const tick = async () => {
|
|
87
81
|
const st = await getStatus();
|
|
@@ -93,14 +87,15 @@ window.__ModuleLoader__.load({
|
|
|
93
87
|
alive = false;
|
|
94
88
|
clearInterval(timer);
|
|
95
89
|
};
|
|
96
|
-
}, [
|
|
97
|
-
const
|
|
90
|
+
}, []);
|
|
91
|
+
const refresh = async () => {
|
|
92
|
+
setStatus(await getStatus());
|
|
93
|
+
};
|
|
98
94
|
const startAuth = async () => {
|
|
99
95
|
setBusy(true);
|
|
100
96
|
setNotice("");
|
|
101
97
|
await postJson("/plugins/agy-link/auth", {});
|
|
102
|
-
|
|
103
|
-
setStatus(st);
|
|
98
|
+
await refresh();
|
|
104
99
|
setBusy(false);
|
|
105
100
|
};
|
|
106
101
|
const submitCode = async () => {
|
|
@@ -119,40 +114,17 @@ window.__ModuleLoader__.load({
|
|
|
119
114
|
key,
|
|
120
115
|
value
|
|
121
116
|
});
|
|
122
|
-
|
|
123
|
-
setStatus(st);
|
|
117
|
+
await refresh();
|
|
124
118
|
setBusy(false);
|
|
125
119
|
};
|
|
126
|
-
const
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
style: {
|
|
130
|
-
background: "transparent",
|
|
131
|
-
border: "none",
|
|
132
|
-
cursor: "pointer",
|
|
133
|
-
padding: "2px 6px",
|
|
134
|
-
fontSize: "14px",
|
|
135
|
-
lineHeight: 1
|
|
136
|
-
},
|
|
137
|
-
title: "Antigravity (agy) bridge"
|
|
138
|
-
}, h("span", { style: {
|
|
139
|
-
display: "inline-block",
|
|
140
|
-
width: "9px",
|
|
141
|
-
height: "9px",
|
|
142
|
-
borderRadius: "50%",
|
|
143
|
-
background: dot
|
|
144
|
-
} }), wide ? h("span", { style: {
|
|
145
|
-
marginLeft: "6px",
|
|
146
|
-
fontSize: "12px",
|
|
147
|
-
lineHeight: 1
|
|
148
|
-
} }, "AGY") : null);
|
|
149
|
-
if (!open) return badge;
|
|
150
|
-
const needsAuth = status !== null && !status.dormantReason && status.auth !== void 0 && status.auth.phase !== "ok";
|
|
151
|
-
const dormantLine = h("div", null, "Bridge dormant: ", status?.dormantReason ?? "");
|
|
120
|
+
const reachable = status !== null;
|
|
121
|
+
const summary = !reachable ? "Status endpoint unreachable — the plugin host did not answer /plugins/agy-link/status. Restart the DSH web server and check its logs." : status.dormantReason ? "Not ready — " + status.dormantReason : status.auth?.phase === "ok" ? "Ready — agy is connected" : "Needs Google login — start the login flow below, or run /agy auth";
|
|
122
|
+
const color = !reachable ? "#e5484d" : status.dormantReason ? "#f5a623" : status.auth?.phase === "ok" ? "#30a46c" : "#f5a623";
|
|
152
123
|
const infoBlock = h("div", { style: {
|
|
153
124
|
...S.muted,
|
|
154
|
-
lineHeight: 1.
|
|
155
|
-
|
|
125
|
+
lineHeight: 1.6,
|
|
126
|
+
margin: "6px 0 12px"
|
|
127
|
+
} }, 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"));
|
|
156
128
|
const modeRow = h("div", { style: {
|
|
157
129
|
...S.row,
|
|
158
130
|
marginTop: "8px"
|
|
@@ -190,7 +162,10 @@ window.__ModuleLoader__.load({
|
|
|
190
162
|
style: status?.defaultEffort === "" ? S.btnPrimary : S.btn,
|
|
191
163
|
onClick: () => void setCfg("defaultEffort", "")
|
|
192
164
|
}, "auto"));
|
|
193
|
-
const skipWarn = h("div", { style:
|
|
165
|
+
const skipWarn = h("div", { style: {
|
|
166
|
+
...S.muted,
|
|
167
|
+
marginBottom: "8px"
|
|
168
|
+
} }, "skip runs agy tools with --dangerously-skip-permissions (no approval prompts). plan is the safe read-only default.");
|
|
194
169
|
const authBlock = h("div", null, h("div", { style: {
|
|
195
170
|
fontWeight: 600,
|
|
196
171
|
marginBottom: "4px"
|
|
@@ -219,53 +194,8 @@ window.__ModuleLoader__.load({
|
|
|
219
194
|
disabled: busy,
|
|
220
195
|
onClick: () => void startAuth()
|
|
221
196
|
}, "Restart login")), notice !== "" ? h("div", null, notice) : null);
|
|
222
|
-
const
|
|
223
|
-
const
|
|
224
|
-
display: "flex",
|
|
225
|
-
justifyContent: "space-between",
|
|
226
|
-
alignItems: "center",
|
|
227
|
-
marginBottom: "8px"
|
|
228
|
-
} }, h("span", { style: {
|
|
229
|
-
fontWeight: 600,
|
|
230
|
-
fontSize: "13px"
|
|
231
|
-
} }, "Antigravity bridge"), h("button", {
|
|
232
|
-
type: "button",
|
|
233
|
-
style: S.btn,
|
|
234
|
-
onClick: () => setOpen(false)
|
|
235
|
-
}, "close"));
|
|
236
|
-
const popover = h("div", { style: {
|
|
237
|
-
position: "fixed",
|
|
238
|
-
bottom: "56px",
|
|
239
|
-
left: "16px",
|
|
240
|
-
width: "300px",
|
|
241
|
-
maxHeight: "70vh",
|
|
242
|
-
overflowY: "auto",
|
|
243
|
-
padding: "12px 14px",
|
|
244
|
-
borderRadius: "10px",
|
|
245
|
-
border: "1px solid rgba(128,128,128,0.35)",
|
|
246
|
-
background: "rgba(28,28,32,0.98)",
|
|
247
|
-
boxShadow: "0 8px 32px rgba(0,0,0,0.5)",
|
|
248
|
-
zIndex: 1e3
|
|
249
|
-
} }, header, body);
|
|
250
|
-
return h("div", { style: { display: "contents" } }, badge, portalToBody(popover));
|
|
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";
|
|
197
|
+
const needsAuth = reachable && !status.dormantReason && status.auth !== void 0 && status.auth.phase !== "ok";
|
|
198
|
+
const controls = !reachable ? null : status.dormantReason ? h("div", { style: S.muted }, "Bridge dormant: ", status.dormantReason) : needsAuth ? authBlock : h("div", null, infoBlock, modeRow, effortRow, skipWarn);
|
|
269
199
|
return h("div", { style: { lineHeight: 1.6 } }, h("div", { style: {
|
|
270
200
|
display: "flex",
|
|
271
201
|
alignItems: "center",
|
|
@@ -281,10 +211,7 @@ window.__ModuleLoader__.load({
|
|
|
281
211
|
} }), "Antigravity (agy CLI)"), h("div", { style: {
|
|
282
212
|
...S.muted,
|
|
283
213
|
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."));
|
|
214
|
+
} }, summary), controls, h("div", { style: S.muted }, "Run `/agy status` for full diagnostics, `/agy doctor` to export a report."));
|
|
288
215
|
};
|
|
289
216
|
const AgySessionStatus = () => {
|
|
290
217
|
const [status, setStatus] = useState(null);
|
|
@@ -327,14 +254,6 @@ window.__ModuleLoader__.load({
|
|
|
327
254
|
background: color
|
|
328
255
|
} }), "AGY");
|
|
329
256
|
};
|
|
330
|
-
ctx.slots.inject("sidebar.footer.action", () => {
|
|
331
|
-
return ctx.slots.register({
|
|
332
|
-
name: "sidebar.footer.action",
|
|
333
|
-
id: "agy-link-entry",
|
|
334
|
-
order: 30,
|
|
335
|
-
label: "Antigravity"
|
|
336
|
-
}, AgyPanel);
|
|
337
|
-
});
|
|
338
257
|
ctx.slots.inject("conversation.session.header.actions", () => {
|
|
339
258
|
return ctx.slots.register({
|
|
340
259
|
name: "conversation.session.header.actions",
|
package/dist/index.js
CHANGED
|
@@ -349,6 +349,7 @@ var EventMapper = class {
|
|
|
349
349
|
openAcc = "";
|
|
350
350
|
emittedByKey = /* @__PURE__ */ new Map();
|
|
351
351
|
toolPhaseSeen = /* @__PURE__ */ new Set();
|
|
352
|
+
thinkingAnnounced = /* @__PURE__ */ new Set();
|
|
352
353
|
sawTextStep = false;
|
|
353
354
|
finished = false;
|
|
354
355
|
constructor(opts = {}) {
|
|
@@ -408,47 +409,97 @@ var EventMapper = class {
|
|
|
408
409
|
if (ev.kind === "garbage") return;
|
|
409
410
|
if (ev.kind === "step") {
|
|
410
411
|
if (ev.stepKind === "text") {
|
|
412
|
+
const thoughtTokens = ev.usage?.thinking_tokens ?? 0;
|
|
413
|
+
if (ev.text === "" && thoughtTokens > 0 && !this.thinkingAnnounced.has(ev.stepKey)) {
|
|
414
|
+
this.thinkingAnnounced.add(ev.stepKey);
|
|
415
|
+
yield* this.ensureBlock("reasoning");
|
|
416
|
+
const d = this.appendDelta("[agy thinking turn · " + thoughtTokens + " thinking tokens]\n");
|
|
417
|
+
if (d) yield d;
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
if (ev.text === "" && !ev.fragment) return;
|
|
411
421
|
this.sawTextStep = true;
|
|
412
422
|
yield* this.ensureBlock("text");
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
423
|
+
let d;
|
|
424
|
+
if (ev.fragment === true) {
|
|
425
|
+
const acc = (this.emittedByKey.get(ev.stepKey) ?? "") + ev.text;
|
|
426
|
+
this.emittedByKey.set(ev.stepKey, acc);
|
|
427
|
+
d = this.appendDelta(ev.text);
|
|
428
|
+
} else {
|
|
429
|
+
const delta = suffixDelta(this.emittedByKey.get(ev.stepKey) ?? "", ev.text);
|
|
430
|
+
this.emittedByKey.set(ev.stepKey, ev.text);
|
|
431
|
+
d = this.appendDelta(delta);
|
|
432
|
+
}
|
|
416
433
|
if (d) yield d;
|
|
417
434
|
return;
|
|
418
435
|
}
|
|
419
|
-
if (ev.stepKind === "thinking" || ev.stepKind === "
|
|
436
|
+
if (ev.stepKind === "thinking" || ev.stepKind === "subagent") {
|
|
420
437
|
yield* this.ensureBlock("reasoning");
|
|
421
438
|
if (ev.stepKind === "thinking") {
|
|
422
439
|
const delta = suffixDelta(this.emittedByKey.get(ev.stepKey) ?? "", ev.text);
|
|
423
440
|
this.emittedByKey.set(ev.stepKey, ev.text);
|
|
424
441
|
const d = this.appendDelta(delta);
|
|
425
442
|
if (d) yield d;
|
|
426
|
-
} else
|
|
427
|
-
const announceKey = ev.stepKey + ":a";
|
|
428
|
-
if (!this.toolPhaseSeen.has(announceKey)) {
|
|
429
|
-
this.toolPhaseSeen.add(announceKey);
|
|
430
|
-
const line = (this.opts.announce ?? defaultAnnounce)(ev.tool.name, ev.tool.args);
|
|
431
|
-
const d = this.appendDelta(line);
|
|
432
|
-
if (d) yield d;
|
|
433
|
-
}
|
|
434
|
-
const outKey = ev.stepKey + ":o";
|
|
435
|
-
if (ev.tool.output !== void 0 && !this.toolPhaseSeen.has(outKey)) {
|
|
436
|
-
this.toolPhaseSeen.add(outKey);
|
|
437
|
-
const rendered = this.opts.toolOutput ? this.opts.toolOutput(ev.tool.name, ev.tool.args, ev.tool.output) : defaultOutput(ev.tool.output);
|
|
438
|
-
if (rendered !== null) {
|
|
439
|
-
const d = this.appendDelta(rendered);
|
|
440
|
-
if (d) yield d;
|
|
441
|
-
}
|
|
442
|
-
}
|
|
443
|
-
} else if (ev.stepKind === "subagent") {
|
|
443
|
+
} else {
|
|
444
444
|
const d = this.appendDelta("[agy subagent] " + ev.text + "\n");
|
|
445
445
|
if (d) yield d;
|
|
446
446
|
}
|
|
447
447
|
return;
|
|
448
448
|
}
|
|
449
|
+
if (ev.stepKind === "tool" && ev.tool) {
|
|
450
|
+
yield* this.ensureBlock("text");
|
|
451
|
+
const announceKey = ev.stepKey + ":a";
|
|
452
|
+
if (!this.toolPhaseSeen.has(announceKey)) {
|
|
453
|
+
this.toolPhaseSeen.add(announceKey);
|
|
454
|
+
const line = (this.opts.announce ?? defaultAnnounce)(ev.tool.name, ev.tool.args);
|
|
455
|
+
const d = this.appendDelta("🔧 " + line);
|
|
456
|
+
if (d) yield d;
|
|
457
|
+
}
|
|
458
|
+
const outKey = ev.stepKey + ":o";
|
|
459
|
+
const errKey = ev.stepKey + ":e";
|
|
460
|
+
if (ev.tool.error !== void 0 && !this.toolPhaseSeen.has(errKey)) {
|
|
461
|
+
this.toolPhaseSeen.add(errKey);
|
|
462
|
+
const d = this.appendDelta("⚠ [agy tool error: " + ev.tool.name + "] " + ev.tool.error + "\n");
|
|
463
|
+
if (d) yield d;
|
|
464
|
+
}
|
|
465
|
+
if (ev.tool.output !== void 0 && !this.toolPhaseSeen.has(outKey)) {
|
|
466
|
+
this.toolPhaseSeen.add(outKey);
|
|
467
|
+
const rendered = this.opts.toolOutput ? this.opts.toolOutput(ev.tool.name, ev.tool.args, ev.tool.output) : defaultOutput(ev.tool.output);
|
|
468
|
+
if (rendered !== null) {
|
|
469
|
+
const d = this.appendDelta(rendered);
|
|
470
|
+
if (d) yield d;
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
return;
|
|
474
|
+
}
|
|
475
|
+
return;
|
|
476
|
+
}
|
|
477
|
+
if (!ev.ok) {
|
|
478
|
+
if (ev.response === "") return;
|
|
479
|
+
if (!this.sawTextStep) {
|
|
480
|
+
yield* this.ensureBlock("text");
|
|
481
|
+
const d = this.appendDelta(ev.response);
|
|
482
|
+
if (d) yield d;
|
|
483
|
+
}
|
|
484
|
+
if (ev.error !== void 0 && ev.error !== "") {
|
|
485
|
+
yield* this.ensureBlock("reasoning");
|
|
486
|
+
const d = this.appendDelta("[agy finished with error] " + ev.error + "\n");
|
|
487
|
+
if (d) yield d;
|
|
488
|
+
}
|
|
489
|
+
const closeErr = this.closeOpen();
|
|
490
|
+
if (closeErr) yield closeErr;
|
|
491
|
+
yield {
|
|
492
|
+
type: "usage",
|
|
493
|
+
usage: usageFromRaw(ev.usage)
|
|
494
|
+
};
|
|
495
|
+
yield {
|
|
496
|
+
type: "finish",
|
|
497
|
+
reason: { kind: "stop" },
|
|
498
|
+
...ev.conversationId !== "" ? { replayState: { response: { conversationId: ev.conversationId } } } : {}
|
|
499
|
+
};
|
|
500
|
+
this.finished = true;
|
|
449
501
|
return;
|
|
450
502
|
}
|
|
451
|
-
if (!ev.ok) return;
|
|
452
503
|
if (!this.sawTextStep && ev.response !== "") {
|
|
453
504
|
yield* this.ensureBlock("text");
|
|
454
505
|
const d = this.appendDelta(ev.response);
|
|
@@ -512,7 +563,7 @@ function parseModelsOutput(stdout) {
|
|
|
512
563
|
for (const line of text.split(/\n/)) {
|
|
513
564
|
const t = line.trim();
|
|
514
565
|
if (t === "" || t.startsWith("Fetching") || t.startsWith("Error")) continue;
|
|
515
|
-
const m = t.match(/^(\S+)
|
|
566
|
+
const m = t.match(/^(\S+)(?:\t+|\s{2,})(.+)$/);
|
|
516
567
|
if (m && m[1] !== void 0 && m[2] !== void 0) out.push({
|
|
517
568
|
slug: m[1],
|
|
518
569
|
label: m[2].trim()
|
|
@@ -717,7 +768,9 @@ const TEXT_STEP_TYPES = /* @__PURE__ */ new Set([
|
|
|
717
768
|
"model_response",
|
|
718
769
|
"modelresponse",
|
|
719
770
|
"message",
|
|
720
|
-
"response_text"
|
|
771
|
+
"response_text",
|
|
772
|
+
"agent_response",
|
|
773
|
+
"agentresponse"
|
|
721
774
|
]);
|
|
722
775
|
const THINKING_STEP_TYPES = /* @__PURE__ */ new Set([
|
|
723
776
|
"thinking",
|
|
@@ -802,6 +855,11 @@ function extractText(obj) {
|
|
|
802
855
|
}
|
|
803
856
|
return "";
|
|
804
857
|
}
|
|
858
|
+
/** agy ≥1.1.15 streams assistant text as `text_delta` fragments. */
|
|
859
|
+
function extractTextDelta(obj) {
|
|
860
|
+
const v = obj.text_delta ?? obj.textDelta;
|
|
861
|
+
return typeof v === "string" ? v : void 0;
|
|
862
|
+
}
|
|
805
863
|
function extractTool(obj) {
|
|
806
864
|
const info = pick(obj, [
|
|
807
865
|
"tool_info",
|
|
@@ -818,21 +876,31 @@ function extractTool(obj) {
|
|
|
818
876
|
"canonical_name"
|
|
819
877
|
]);
|
|
820
878
|
if (typeof name !== "string" || name === "") return void 0;
|
|
879
|
+
const args = pick(src, [
|
|
880
|
+
"parameters",
|
|
881
|
+
"params",
|
|
882
|
+
"input",
|
|
883
|
+
"args",
|
|
884
|
+
"input_json",
|
|
885
|
+
"inputJson"
|
|
886
|
+
]);
|
|
887
|
+
const output = pick(src, [
|
|
888
|
+
"output",
|
|
889
|
+
"result",
|
|
890
|
+
"output_text"
|
|
891
|
+
]);
|
|
892
|
+
const errObj = src.error;
|
|
893
|
+
let error;
|
|
894
|
+
if (typeof errObj === "string") error = errObj;
|
|
895
|
+
else if (errObj && typeof errObj === "object") {
|
|
896
|
+
const msg = errObj.message;
|
|
897
|
+
if (typeof msg === "string" && msg !== "") error = msg;
|
|
898
|
+
}
|
|
821
899
|
return {
|
|
822
900
|
name,
|
|
823
|
-
args
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
"input",
|
|
827
|
-
"args",
|
|
828
|
-
"input_json",
|
|
829
|
-
"inputJson"
|
|
830
|
-
]),
|
|
831
|
-
output: pick(src, [
|
|
832
|
-
"output",
|
|
833
|
-
"result",
|
|
834
|
-
"output_text"
|
|
835
|
-
])
|
|
901
|
+
args,
|
|
902
|
+
output,
|
|
903
|
+
...error !== void 0 ? { error } : {}
|
|
836
904
|
};
|
|
837
905
|
}
|
|
838
906
|
function parseUsage(v) {
|
|
@@ -866,20 +934,36 @@ function classifyEvent(obj, seq) {
|
|
|
866
934
|
"session_id",
|
|
867
935
|
"sessionId"
|
|
868
936
|
]);
|
|
937
|
+
const initObj = o.init;
|
|
938
|
+
const initSrc = initObj && typeof initObj === "object" && !Array.isArray(initObj) ? initObj : {};
|
|
869
939
|
const model = pick(o, [
|
|
870
940
|
"model",
|
|
871
941
|
"model_name",
|
|
872
942
|
"modelName"
|
|
943
|
+
]) ?? pick(initSrc, [
|
|
944
|
+
"model",
|
|
945
|
+
"model_name",
|
|
946
|
+
"modelName"
|
|
873
947
|
]);
|
|
948
|
+
let convId = typeof cid === "string" && cid !== "" ? cid : void 0;
|
|
949
|
+
if (convId === void 0) {
|
|
950
|
+
const nested = pick(initSrc, ["conversation_id", "conversationId"]);
|
|
951
|
+
if (typeof nested === "string" && nested !== "") convId = nested;
|
|
952
|
+
}
|
|
874
953
|
return {
|
|
875
954
|
kind: "init",
|
|
876
|
-
...
|
|
955
|
+
...convId !== void 0 ? { conversationId: convId } : {},
|
|
877
956
|
...typeof model === "string" ? { model } : {},
|
|
878
957
|
raw: o
|
|
879
958
|
};
|
|
880
959
|
}
|
|
881
960
|
if (evt === "step_update" || evt === "step" || evt === "stepUpdate") {
|
|
882
|
-
const
|
|
961
|
+
const nested = o.step_update ?? o.stepUpdate ?? o.step;
|
|
962
|
+
const su = nested && typeof nested === "object" && !Array.isArray(nested) ? nested : {};
|
|
963
|
+
const src = Object.keys(su).length > 0 ? su : o;
|
|
964
|
+
const idxV = pick(src, [
|
|
965
|
+
"step_index",
|
|
966
|
+
"stepIndex",
|
|
883
967
|
"idx",
|
|
884
968
|
"index",
|
|
885
969
|
"step_idx",
|
|
@@ -889,18 +973,25 @@ function classifyEvent(obj, seq) {
|
|
|
889
973
|
"stepId"
|
|
890
974
|
]);
|
|
891
975
|
const stepKey = typeof idxV === "number" || typeof idxV === "string" ? String(idxV) : String(seq);
|
|
892
|
-
const stepKind = normalizeStepKind(pick(
|
|
976
|
+
const stepKind = normalizeStepKind(pick(src, [
|
|
893
977
|
"step_type",
|
|
894
978
|
"stepType",
|
|
895
979
|
"type"
|
|
896
980
|
]));
|
|
897
|
-
const toolInfo = stepKind === "tool" ? extractTool(
|
|
981
|
+
const toolInfo = stepKind === "tool" ? extractTool(src) : void 0;
|
|
982
|
+
const delta = extractTextDelta(src);
|
|
983
|
+
const text = delta !== void 0 ? delta : extractText(src);
|
|
984
|
+
const stateV = pick(src, ["state"]);
|
|
985
|
+
const usageRaw = pick(src, ["usage"]);
|
|
898
986
|
return {
|
|
899
987
|
kind: "step",
|
|
900
988
|
stepKey,
|
|
901
989
|
stepKind,
|
|
902
|
-
text
|
|
990
|
+
text,
|
|
991
|
+
...delta !== void 0 ? { fragment: true } : {},
|
|
903
992
|
...toolInfo !== void 0 ? { tool: toolInfo } : {},
|
|
993
|
+
...typeof stateV === "string" ? { state: stateV } : {},
|
|
994
|
+
...usageRaw && typeof usageRaw === "object" ? { usage: parseUsage(usageRaw) } : {},
|
|
904
995
|
raw: o
|
|
905
996
|
};
|
|
906
997
|
}
|
|
@@ -938,6 +1029,10 @@ function classifyEvent(obj, seq) {
|
|
|
938
1029
|
event: "step_update",
|
|
939
1030
|
...o
|
|
940
1031
|
}, seq);
|
|
1032
|
+
if ((o.step_update !== void 0 || o.stepUpdate !== void 0) && (o.step_update ?? o.stepUpdate) !== null && typeof (o.step_update ?? o.stepUpdate) === "object") return classifyEvent({
|
|
1033
|
+
event: "step_update",
|
|
1034
|
+
step_update: o.step_update ?? o.stepUpdate
|
|
1035
|
+
}, seq);
|
|
941
1036
|
}
|
|
942
1037
|
var StreamJsonParser = class {
|
|
943
1038
|
buffer = "";
|
|
@@ -947,7 +1042,8 @@ var StreamJsonParser = class {
|
|
|
947
1042
|
garbage: 0,
|
|
948
1043
|
consecutiveGarbage: 0,
|
|
949
1044
|
authUrl: void 0,
|
|
950
|
-
sawAuthFailure: false
|
|
1045
|
+
sawAuthFailure: false,
|
|
1046
|
+
lastResultError: void 0
|
|
951
1047
|
};
|
|
952
1048
|
/** Ring buffer of raw stdout lines for /agy doctor export. */
|
|
953
1049
|
recentLines = [];
|
|
@@ -1003,6 +1099,7 @@ var StreamJsonParser = class {
|
|
|
1003
1099
|
line
|
|
1004
1100
|
};
|
|
1005
1101
|
}
|
|
1102
|
+
if (ev.kind === "result") this.stats.lastResultError = ev.error ?? void 0;
|
|
1006
1103
|
this.stats.consecutiveGarbage = 0;
|
|
1007
1104
|
return ev;
|
|
1008
1105
|
}
|
|
@@ -1225,6 +1322,9 @@ function startAgyProcess(opts) {
|
|
|
1225
1322
|
let timedOut = false;
|
|
1226
1323
|
let aborted = false;
|
|
1227
1324
|
let settled = false;
|
|
1325
|
+
if (!opts.keepStdin) try {
|
|
1326
|
+
child.stdin?.end();
|
|
1327
|
+
} catch {}
|
|
1228
1328
|
const watchdog = opts.timeoutMs && opts.timeoutMs > 0 ? setTimeout(() => {
|
|
1229
1329
|
timedOut = true;
|
|
1230
1330
|
killTree(child);
|
|
@@ -1263,9 +1363,6 @@ function startAgyProcess(opts) {
|
|
|
1263
1363
|
opts.onLine?.(pending);
|
|
1264
1364
|
pending = "";
|
|
1265
1365
|
}
|
|
1266
|
-
if (!opts.keepStdin) try {
|
|
1267
|
-
child.stdin?.end();
|
|
1268
|
-
} catch {}
|
|
1269
1366
|
resolve({
|
|
1270
1367
|
code,
|
|
1271
1368
|
signal,
|
|
@@ -1597,6 +1694,11 @@ var AgyAdapter = class extends LlmAdapter {
|
|
|
1597
1694
|
code: Err.PROCESS_EXIT,
|
|
1598
1695
|
message: "agy exited with code " + outcome.code + (outcome.stderrTail !== "" ? ": " + brief(outcome.stderrTail) : "")
|
|
1599
1696
|
};
|
|
1697
|
+
else if (parser.stats.lastResultError) failure = {
|
|
1698
|
+
kind: "error",
|
|
1699
|
+
code: Err.AGY_ERROR,
|
|
1700
|
+
message: "agy reported an error: " + parser.stats.lastResultError
|
|
1701
|
+
};
|
|
1600
1702
|
else failure = {
|
|
1601
1703
|
kind: "error",
|
|
1602
1704
|
code: Err.INVALID_OUTPUT,
|
|
@@ -1907,12 +2009,63 @@ var AuthHelper = class {
|
|
|
1907
2009
|
capturedUrl = null;
|
|
1908
2010
|
urlWaiter = null;
|
|
1909
2011
|
urlTimer = null;
|
|
2012
|
+
signedInCache = null;
|
|
2013
|
+
signedInFlight = null;
|
|
1910
2014
|
constructor(bin) {
|
|
1911
2015
|
this.bin = bin;
|
|
1912
2016
|
}
|
|
1913
2017
|
status() {
|
|
1914
2018
|
return { ...this.state };
|
|
1915
2019
|
}
|
|
2020
|
+
/**
|
|
2021
|
+
* Ground-truth login probe: `agy models` succeeds only when signed in and
|
|
2022
|
+
* prints a sign-in error otherwise. Cached for 60s; in-flight calls share
|
|
2023
|
+
* one spawn. Returns true/false, or null when undeterminable (no binary,
|
|
2024
|
+
* spawn failure, ambiguous output). While a login flow is active the
|
|
2025
|
+
* in-memory phase wins and the probe is skipped.
|
|
2026
|
+
*/
|
|
2027
|
+
async probeSignedIn(force = false) {
|
|
2028
|
+
if (this.state.phase === "pending" || this.state.phase === "submitting") return null;
|
|
2029
|
+
if (this.state.phase === "ok") return true;
|
|
2030
|
+
const now = Date.now();
|
|
2031
|
+
if (!force && this.signedInCache !== null && now - this.signedInCache.at < 6e4) return this.signedInCache.value;
|
|
2032
|
+
if (this.signedInFlight !== null) return this.signedInFlight;
|
|
2033
|
+
const bin = this.bin();
|
|
2034
|
+
if (!bin) return null;
|
|
2035
|
+
this.signedInFlight = (async () => {
|
|
2036
|
+
let value = null;
|
|
2037
|
+
try {
|
|
2038
|
+
const out = await probeProcess(bin, ["models"], 15e3);
|
|
2039
|
+
const tail = out.stderrTail + "\n" + out.stdout.slice(0, 2e3);
|
|
2040
|
+
if (out.code === 0 && !looksLikeAuthFailure(out.stdout)) value = true;
|
|
2041
|
+
else if (out.code !== 0 && looksLikeAuthFailure(tail)) value = false;
|
|
2042
|
+
} catch {
|
|
2043
|
+
value = null;
|
|
2044
|
+
}
|
|
2045
|
+
this.signedInCache = {
|
|
2046
|
+
value,
|
|
2047
|
+
at: Date.now()
|
|
2048
|
+
};
|
|
2049
|
+
this.signedInFlight = null;
|
|
2050
|
+
return value;
|
|
2051
|
+
})();
|
|
2052
|
+
return this.signedInFlight;
|
|
2053
|
+
}
|
|
2054
|
+
/** status() enriched with a lazily probed login state for idle phases. */
|
|
2055
|
+
async resolvedStatus() {
|
|
2056
|
+
const st = this.status();
|
|
2057
|
+
if (st.phase !== "idle") return st;
|
|
2058
|
+
const signedIn = await this.probeSignedIn();
|
|
2059
|
+
if (signedIn === true) return {
|
|
2060
|
+
phase: "ok",
|
|
2061
|
+
message: "signed in (probed via agy models)"
|
|
2062
|
+
};
|
|
2063
|
+
if (signedIn === false) return {
|
|
2064
|
+
phase: "signed-out",
|
|
2065
|
+
message: "agy is not signed in — run /agy auth"
|
|
2066
|
+
};
|
|
2067
|
+
return st;
|
|
2068
|
+
}
|
|
1916
2069
|
startProbe() {
|
|
1917
2070
|
const bin = this.bin();
|
|
1918
2071
|
if (!bin) return null;
|
|
@@ -2090,7 +2243,7 @@ async function handle(deps, raw) {
|
|
|
2090
2243
|
const sub = parts[0] ?? "help";
|
|
2091
2244
|
const arg = parts[1] ?? "";
|
|
2092
2245
|
try {
|
|
2093
|
-
if (sub === "status") return ok(renderStatus(deps));
|
|
2246
|
+
if (sub === "status") return ok(await renderStatus(deps));
|
|
2094
2247
|
if (sub === "auth") {
|
|
2095
2248
|
const auth = deps.auth();
|
|
2096
2249
|
if (!auth) return err("agy binary not found — install the CLI first");
|
|
@@ -2163,10 +2316,11 @@ async function handle(deps, raw) {
|
|
|
2163
2316
|
return err(e instanceof Error ? e.message : String(e));
|
|
2164
2317
|
}
|
|
2165
2318
|
}
|
|
2166
|
-
function renderStatus(deps) {
|
|
2319
|
+
async function renderStatus(deps) {
|
|
2167
2320
|
const cfg = deps.cfg();
|
|
2168
2321
|
const bin = deps.bin();
|
|
2169
|
-
const
|
|
2322
|
+
const authHelper = deps.auth();
|
|
2323
|
+
const auth = authHelper ? await authHelper.resolvedStatus() : void 0;
|
|
2170
2324
|
const cat = deps.catalog().get();
|
|
2171
2325
|
const bindings = Object.keys(deps.store().all()).length;
|
|
2172
2326
|
const last = deps.lastRun();
|
|
@@ -2707,60 +2861,66 @@ function apply(ctx, entryConfig = {}) {
|
|
|
2707
2861
|
}
|
|
2708
2862
|
};
|
|
2709
2863
|
syncAskTool();
|
|
2710
|
-
const
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
const
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2864
|
+
const registerRoutes = (webServer) => {
|
|
2865
|
+
const disposers = [];
|
|
2866
|
+
const reg = (route) => {
|
|
2867
|
+
const d = webServer.register(route);
|
|
2868
|
+
if (typeof d === "function") disposers.push(d);
|
|
2869
|
+
};
|
|
2870
|
+
const sendJson = (res, status, body) => {
|
|
2871
|
+
res.writeHead(status, { "Content-Type": "application/json; charset=utf-8" });
|
|
2872
|
+
res.end(JSON.stringify(body));
|
|
2873
|
+
};
|
|
2874
|
+
const readBody = (req) => {
|
|
2875
|
+
const r = req;
|
|
2876
|
+
return new Promise((resolve) => {
|
|
2877
|
+
const chunks = [];
|
|
2878
|
+
r.on?.("data", (c) => chunks.push(c));
|
|
2879
|
+
r.on?.("end", () => {
|
|
2880
|
+
try {
|
|
2881
|
+
const v = JSON.parse(Buffer.concat(chunks).toString("utf8"));
|
|
2882
|
+
resolve(v && typeof v === "object" ? v : {});
|
|
2883
|
+
} catch {
|
|
2884
|
+
resolve({});
|
|
2885
|
+
}
|
|
2886
|
+
});
|
|
2727
2887
|
});
|
|
2728
|
-
}
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
if (webServer) {
|
|
2735
|
-
webServer.register({
|
|
2888
|
+
};
|
|
2889
|
+
const methodOf = (req) => {
|
|
2890
|
+
const m = req.method;
|
|
2891
|
+
return typeof m === "string" ? m.toUpperCase() : "GET";
|
|
2892
|
+
};
|
|
2893
|
+
reg({
|
|
2736
2894
|
kind: "exact",
|
|
2737
2895
|
path: "/plugins/agy-link/status",
|
|
2738
2896
|
handler: (_req, res) => {
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2897
|
+
(async () => {
|
|
2898
|
+
const cfg = getConfig();
|
|
2899
|
+
const cat = catalog.get();
|
|
2900
|
+
sendJson(res, 200, {
|
|
2901
|
+
plugin: "dsh-agy-link",
|
|
2902
|
+
bin: bin(),
|
|
2903
|
+
version: versionCache,
|
|
2904
|
+
dormantReason,
|
|
2905
|
+
enabled: cfg.enabled,
|
|
2906
|
+
permissionMode: cfg.permissionMode,
|
|
2907
|
+
workspaceRoot: cfg.workspaceRoot,
|
|
2908
|
+
defaultModel: cfg.defaultModel,
|
|
2909
|
+
defaultEffort: cfg.defaultEffort,
|
|
2910
|
+
askTool: cfg.askTool,
|
|
2911
|
+
auth: await auth.resolvedStatus(),
|
|
2912
|
+
catalog: {
|
|
2913
|
+
source: cat.source,
|
|
2914
|
+
count: cat.models.length,
|
|
2915
|
+
lastError: cat.lastError ?? null
|
|
2916
|
+
},
|
|
2917
|
+
bindings: Object.keys(store.all()).length,
|
|
2918
|
+
lastRun
|
|
2919
|
+
});
|
|
2920
|
+
})();
|
|
2761
2921
|
}
|
|
2762
2922
|
});
|
|
2763
|
-
|
|
2923
|
+
reg({
|
|
2764
2924
|
kind: "exact",
|
|
2765
2925
|
path: "/plugins/agy-link/auth",
|
|
2766
2926
|
handler: (req, res) => {
|
|
@@ -2775,7 +2935,7 @@ function apply(ctx, entryConfig = {}) {
|
|
|
2775
2935
|
})();
|
|
2776
2936
|
}
|
|
2777
2937
|
});
|
|
2778
|
-
|
|
2938
|
+
reg({
|
|
2779
2939
|
kind: "exact",
|
|
2780
2940
|
path: "/plugins/agy-link/auth-code",
|
|
2781
2941
|
handler: (req, res) => {
|
|
@@ -2796,7 +2956,7 @@ function apply(ctx, entryConfig = {}) {
|
|
|
2796
2956
|
})();
|
|
2797
2957
|
}
|
|
2798
2958
|
});
|
|
2799
|
-
|
|
2959
|
+
reg({
|
|
2800
2960
|
kind: "exact",
|
|
2801
2961
|
path: "/plugins/agy-link/config",
|
|
2802
2962
|
handler: (req, res) => {
|
|
@@ -2827,7 +2987,7 @@ function apply(ctx, entryConfig = {}) {
|
|
|
2827
2987
|
})();
|
|
2828
2988
|
}
|
|
2829
2989
|
});
|
|
2830
|
-
|
|
2990
|
+
reg({
|
|
2831
2991
|
kind: "exact",
|
|
2832
2992
|
path: "/plugins/agy-link/qr",
|
|
2833
2993
|
handler: (_req, res) => {
|
|
@@ -2858,7 +3018,17 @@ function apply(ctx, entryConfig = {}) {
|
|
|
2858
3018
|
})();
|
|
2859
3019
|
}
|
|
2860
3020
|
});
|
|
2861
|
-
|
|
3021
|
+
return () => {
|
|
3022
|
+
for (const d of disposers) try {
|
|
3023
|
+
d();
|
|
3024
|
+
} catch {}
|
|
3025
|
+
};
|
|
3026
|
+
};
|
|
3027
|
+
ctx.inject(["webServer"], (sub) => {
|
|
3028
|
+
const webServer = sub.get("webServer");
|
|
3029
|
+
if (!webServer) return;
|
|
3030
|
+
return registerRoutes(webServer);
|
|
3031
|
+
});
|
|
2862
3032
|
const mediaDir = () => {
|
|
2863
3033
|
const cfg = getConfig();
|
|
2864
3034
|
return cfg.mediaDir !== "" ? cfg.mediaDir : defaultMediaDir(stateDir());
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-agy-link",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.9",
|
|
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",
|