@eddyskywalker/dsh-chatgpt-subscription 0.2.15 → 0.2.17
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 +15 -1
- package/README.md +28 -2
- package/lib/index.js +481 -22
- package/lib/types/host/codex-fetch.d.ts +7 -0
- package/lib/types/host/codex-fetch.d.ts.map +1 -1
- package/lib/types/host/fetch-address-policy.d.ts +78 -0
- package/lib/types/host/fetch-address-policy.d.ts.map +1 -0
- package/lib/types/host/search-provider-switcher.d.ts +15 -1
- package/lib/types/host/search-provider-switcher.d.ts.map +1 -1
- package/lib/types/host/subagent-model-authorization.d.ts +177 -0
- package/lib/types/host/subagent-model-authorization.d.ts.map +1 -0
- package/lib/types/index.d.ts +21 -1
- package/lib/types/index.d.ts.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## Unreleased
|
|
4
|
+
|
|
5
|
+
- 修复开启系统代理的机器上 `web_fetch` 必然失败的问题:DSH 内置抓取 provider 在连接前解析、校验并固定目标地址,而代理工具(Clash/Mihomo 等)的 fake-ip DNS 会把域名解析成 `198.18.0.0/15` 里的保留地址(实测 `api.github.com` → `198.18.0.17`),于是每次调用都以 `WEB_BLOCKED_URL`(`resolves to a non-public IP address`)结束——代理根本没被用上。DSH 只在进程环境变量里读到代理时才走代理,看不到系统代理。现在只要插件配置了可用代理(系统代理自动检测或自定义代理),`web_fetch` 就改用本插件的抓取 provider:由代理解析源站,与 DSH 对“走代理的请求”采用的语义一致;未配置代理时仍由内置 provider 抓取,其解析与固定策略不变。
|
|
6
|
+
- 抓取 provider 新增地址策略 `src/host/fetch-address-policy.ts`,保留内置 provider 安全边界中不需要 DNS 的那一半:URL 里写明的 IP 字面量只有全球可路由单播才放行(loopback、私网、链路本地、CGNAT、多播、保留地址、IPv6 转换与隧道前缀一律拒绝);域名用本机解析器检查一次,落在私网(含 `localhost`、`127.0.0.1.nip.io` 这类)一律拒绝;只有代理的 fake-ip 答案被接受,本机解析不出的域名交给代理处理。与内置 provider 的差别是不再固定(pin)连接地址——fake-ip 环境下这一步无法成立,已在 README 的安全边界中写明。
|
|
7
|
+
- 代理偏好在运行时变化(例如系统代理 ↔ 直连)会重新选择抓取后端;选择 ChatGPT 搜索来源时同时切换搜索与抓取的行为保持不变。
|
|
8
|
+
- 新增 `test/fetch-address-policy.test.ts`(地址分类、fake-ip 识别、私网解析拒绝、解析失败放行),并扩充 `test/codex-fetch.test.ts`、`test/search-provider-switcher.test.ts`、`test/web-provider-lifecycle.test.ts` 覆盖抓取 provider 的拒绝路径与后端切换。
|
|
9
|
+
|
|
10
|
+
## 0.2.15 - 2026-09-11
|
|
11
|
+
|
|
12
|
+
- 新增子代理模型授权守卫:DSH 的「Subagent」允许列表(`subagent-model-selection`)原本只在模型**显式**填写 `provider`/`model` 时生效,而委派调用不填路由时子代理会继承父级模型——于是 DeepSeek 会话里的子代理仍会在未授权模型上运行。插件现在在 Host 工具注册表上注册一个单调守卫(`ctx.tools.guard`),当调用会话(或最近的、记录了策略的祖先会话)带有允许列表时,任何生效路由不在此列表内的委派都会在子代理启动前被拒绝,拒绝理由里带上全部已授权路由,模型据此重试即可选中合规模型。
|
|
13
|
+
- 该守卫只在会话确实记录了允许列表时介入(与 DSH 委派工具的会话快照语义一致),不会改变未启用该设置的会话;`subagentModelScope: 'preference'` 可让当前设置卡允许列表也约束未记录策略的会话(含恢复的旧会话)。
|
|
14
|
+
- 只读取公开接口:会话日志事件 `subagent/model-selection-policy`、父会话谱系、Host 设置文档与 `ctx.tools.guard`,不修改 DSH 本体。
|
|
15
|
+
- 新增配置项:`subagentModelAuthorization`(默认 `true`)、`subagentModelTools`(默认 `['subagent']`)、`subagentModelScope`(默认 `session`,可选 `preference`)。
|
|
16
|
+
- 新增 14 条单测覆盖策略解析、谱系继承、设置读取、显式/继承路由的拒绝与放行、作用域切换以及守卫安装路径(含注册与释放)。
|
|
3
17
|
## 0.2.14 - 2026-09-10
|
|
4
18
|
|
|
5
19
|
- 移除失效的「子代理最大嵌套深度 / 子代理上下文预算」设置:这两个偏好键在 DSH 侧没有任何消费方(全仓库无引用),插件自身也只有写、没有读,保存了也不生效。它们源于 2026-08-28 的 `5b08e2e`——那次提交迁移到 DSH 原生子代理模型路由,删除了 `subagent-context-adapter` / `subagent-policy` / `SubagentSettingsSection`,但把这两个控件和偏好键留了下来。相关 DTO 字段、schema、路由校验、界面控件、6 条未引用文案与失效单测一并移除;子代理模型与思考深度改用 DSH 自身的“Subagent”设置卡片,最大嵌套深度由 preset 中 `tool-subagent` 的 `maxDepth` 决定,README 同步更正。
|
|
@@ -61,4 +75,4 @@
|
|
|
61
75
|
- 适配 DSH `0.1.0-rc.8`:peerDependencies / devDependencies 中全部 DSH 包范围从 `^0.1.0-rc.6` 提升至 `^0.1.0-rc.8`,并重新生成 package-lock.json。已在本机 rc.8 依赖集上验证:typecheck、55 项测试与 tsdown 构建全部通过,插件代码无需改动。
|
|
62
76
|
- 移除已无引用的 `@deepseek-ai/dsh-agent` peer 依赖(子代理报告去重兼容模块已于 0.1.11 移除)。
|
|
63
77
|
- 新增 `@deepseek-ai/dsh-client-connection` peer 依赖(客户端 `ContentBlock` 类型来源,符合 rc.8 客户端包 peer 声明惯例)。
|
|
64
|
-
- 说明:rc.8 中 `@deepseek-ai/dsh-client-ui-slots` 不再是 DSH 运行时依赖图成员(仅保留为各客户端包的 devDependency),插件继续将其声明为 peer 依赖以保证安装时解析(`^0.1.0-rc.6` 联网安装时解析到 rc.8;本地锁文件因离线环境暂固定 rc.7,API 与 rc.8 用法一致)。
|
|
78
|
+
- 说明:rc.8 中 `@deepseek-ai/dsh-client-ui-slots` 不再是 DSH 运行时依赖图成员(仅保留为各客户端包的 devDependency),插件继续将其声明为 peer 依赖以保证安装时解析(`^0.1.0-rc.6` 联网安装时解析到 rc.8;本地锁文件因离线环境暂固定 rc.7,API 与 rc.8 用法一致)。
|
package/README.md
CHANGED
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
- [环境要求](#环境要求)
|
|
12
12
|
- [安装](#安装)
|
|
13
13
|
- [使用](#使用)
|
|
14
|
+
- [子代理模型授权](#子代理模型授权0215-起)
|
|
14
15
|
- [升级、降级与卸载](#升级降级与卸载)
|
|
15
16
|
- [安全边界](#安全边界)
|
|
16
17
|
- [插件路由](#插件路由)
|
|
@@ -120,7 +121,9 @@ DSH 模型选择器应显示 **“Codex(ChatGPT 订阅)”**。6 Astra 与 G
|
|
|
120
121
|
|
|
121
122
|
**设置 → Codex 订阅 → 网络代理** 同时控制 GPT 与 Antigravity(Gemini)的 Host 请求,可选择系统代理(自动检测)、自定义代理或直连。Gemini 模型生成、网页登录后的令牌交换、令牌刷新、账号信息、项目发现、配额与模型目录查询均使用此设置;修改后对后续请求生效,无需重启 DSH。浏览器中的 Google 授权页面使用浏览器自己的网络设置。
|
|
122
123
|
|
|
123
|
-
**搜索与抓取来源**
|
|
124
|
+
**搜索与抓取来源** 切换 DSH 的搜索后端;网页抓取后端在有可用代理时自动改用本插件。选择 ChatGPT 来源后,网页由本插件在 Host 抓取,并使用上述网络代理设置;纯 TUN 模式可使用直连,流量由虚拟网卡接管。来源切换即时生效,DSH web 服务重载后会重新注册插件后端,并保留切回 DSH 默认来源所需的配置。
|
|
125
|
+
|
|
126
|
+
**网页抓取后端(web_fetch)** DSH 内置抓取 provider 会先解析域名、校验并固定解析结果,而且只在进程环境变量里读到代理时才走代理——系统代理对它不可见。代理工具(Clash/Mihomo 等)常把域名解析成自己的 fake-ip 地址(默认 `198.18.0.0/15`),于是内置 provider 直接以 `WEB_BLOCKED_URL`(resolves to a non-public IP address)拒绝,代理根本没被用上。因此只要插件配置了可用代理(**网络代理** 选系统代理且检测到,或填写自定义代理),`web_fetch` 就改用本插件的 provider:由代理解析源站,与 DSH 对“走代理的请求”采用的语义一致;未配置代理时仍由 DSH 内置 provider 抓取,保留其解析与固定策略。若在纯 TUN 模式下把代理设为**直连**,内置 provider 会重新接管,此时可改回系统代理让插件接管抓取。
|
|
124
127
|
|
|
125
128
|
卸载前建议先在设置页点击 **“注销”**,它会删除当前平台的凭据和 Host 内存中的额度缓存。
|
|
126
129
|
|
|
@@ -132,6 +135,27 @@ DSH 模型选择器应显示 **“Codex(ChatGPT 订阅)”**。6 Astra 与 G
|
|
|
132
135
|
|
|
133
136
|
> Windows 文件只能由创建它的用户通过 DPAPI 解密。macOS 凭据由登录钥匙串在本机加密保存。Linux 文件是未额外加密的 JSON,依赖目录 `0700` 和文件 `0600` 隔离;不要复制、打印或提交该文件。跨平台迁移需要重新登录。
|
|
134
137
|
|
|
138
|
+
## 子代理模型授权(0.2.15 起)
|
|
139
|
+
|
|
140
|
+
DSH 设置页的「Subagent」卡片会把勾选的模型写成会话级的允许列表(会话日志事件 `subagent/model-selection-policy`)。DSH 内置委派工具只拒绝**模型显式填写**且不在列表内的路由;调用里不写 `provider`/`model` 时,子代理会继承父级模型,于是白名单之外的主模型(例如 `deepseek-official/deepseek-flash`)仍会被子代理使用。
|
|
141
|
+
|
|
142
|
+
插件在 Host 工具注册表上补一个单调守卫(`ctx.tools.guard`),在委派执行前判定生效路由:
|
|
143
|
+
|
|
144
|
+
- 调用会话(或最近的、记录了策略的祖先会话)带有允许列表时,生效路由必须落在列表内;
|
|
145
|
+
- 不写路由的委派按“继承父级模型”判定,因此父级模型不在列表内时会被拒绝;
|
|
146
|
+
- 拒绝结果里会列出全部已授权路由(例如 `antigravity/gemini-3.8-flash`),模型照此重试即可;`list_subagent_models` 仍只展示已授权路由;
|
|
147
|
+
- 未记录允许列表的会话(例如恢复的旧会话、未启用该设置的会话)保持 DSH 原有行为。
|
|
148
|
+
|
|
149
|
+
配置项(插件行 `config`,全部可省略):
|
|
150
|
+
|
|
151
|
+
| 字段 | 默认 | 含义 |
|
|
152
|
+
|---|---|---|
|
|
153
|
+
| `subagentModelAuthorization` | `true` | 是否启用上述授权守卫;设为 `false` 回到 DSH 原有行为 |
|
|
154
|
+
| `subagentModelTools` | `['subagent']` | 需要授权的委派工具名;preset 里自定义了 `toolName` 时在此列出 |
|
|
155
|
+
| `subagentModelScope` | `session` | `session` 只约束记录了允许列表的会话;`preference` 额外用当前设置卡列表约束未记录的会话 |
|
|
156
|
+
|
|
157
|
+
改动只在设置卡片里保存过的勾选生效:设置改动只影响之后新建的会话(DSH 的会话快照语义),已运行的会话继续使用它自己记录的那份列表。
|
|
158
|
+
|
|
135
159
|
## 安全边界
|
|
136
160
|
|
|
137
161
|
Antigravity 的 access token / refresh token 使用独立的系统凭据存储:Windows 使用 CurrentUser DPAPI(`$DSH_HOME/storages/antigravity-oauth.json.dpapi`),macOS 使用登录钥匙串,Linux 使用 Secret Service。macOS / Linux 的服务名为 `dsh-antigravity`,账号键按旧凭据文件的绝对路径生成,隔离不同的 `DSH_HOME`。
|
|
@@ -148,6 +172,7 @@ Antigravity 的 Gemini 用量以流结束时的上游累计计数为准,缓存
|
|
|
148
172
|
- Windows token bundle 使用 CurrentUser DPAPI;明文只经过 Host 内存和 PowerShell stdin/stdout;
|
|
149
173
|
- macOS token bundle 存入登录钥匙串,通过系统 `security` 命令读写;明文只经过 Host 内存和 `security` 命令行参数,Keychain 在本机加密保存;
|
|
150
174
|
- Linux token bundle 原子写入当前用户私有文件并在读取时校验普通文件、所有者与 `0600` 权限,同时拒绝符号链接;该文件没有应用层加密,同 UID 进程、root、备份和磁盘快照仍可读取;
|
|
175
|
+
- 本插件抓取 provider 的地址策略:URL 里直接写明的 IP 字面量只有在全球可路由单播时才放行(loopback、私网、链路本地、CGNAT、多播、保留地址、IPv6 转换与隧道前缀一律拒绝);域名会先用本机解析器检查一次,解析结果落在私网时同样拒绝,只有代理的 fake-ip 地址(`198.18.0.0/15`)被接受——那正是本机代理声称拥有该域名的表现;本机解析不出的域名交给代理处理。与 DSH 内置 provider 的差别是不固定(pin)连接地址,因为 fake-ip 环境下这一步无法成立;
|
|
151
176
|
- 所有平台的 token 都不会进入浏览器、`settings.yaml` 或日志;
|
|
152
177
|
- 工具调用只有在参数是完整 JSON 对象时才产生可执行的 `block-end`;畸形参数终止本次生成;
|
|
153
178
|
- 所有修改状态的路由只接受同源 JSON POST,并校验 `Origin` 与 `Host`。
|
|
@@ -190,6 +215,7 @@ npm pack --dry-run
|
|
|
190
215
|
| 额度显示旧数据 | 设置页会保留最后成功值;等待 15 秒节流窗口后手动刷新 |
|
|
191
216
|
| 429 | 插件遵守 `Retry-After`,不会高频轮询;模型请求由 DSH retry policy 有界重试 |
|
|
192
217
|
| 模型不可用 | 检查 ChatGPT 套餐、workspace 权限与当前模型可用性 |
|
|
218
|
+
| `web_fetch` 报 `resolves to a non-public IP address` | 机器启用了系统代理(fake-ip DNS),但插件没有可用代理可接管抓取:在 **设置 → Codex 订阅 → 网络代理** 选择系统代理(自动检测)或填写自定义代理 |
|
|
193
219
|
| DPAPI 读取失败 | 确认 DSH 以创建凭据时的同一 Windows 用户运行;必要时清理凭据后重新登录 |
|
|
194
220
|
| Linux 凭据存储不可用 | 确认凭据属于当前用户且权限为 `0600`,父目录权限为 `0700`;修复权限或注销后重新登录 |
|
|
195
|
-
| Linux 上工具调用语法错误 | 确认 DSH 暴露的是 `bash`、`sh` 或 `shell`,并使用相应的 Bash/POSIX 语法与 `/` 路径 |
|
|
221
|
+
| Linux 上工具调用语法错误 | 确认 DSH 暴露的是 `bash`、`sh` 或 `shell`,并使用相应的 Bash/POSIX 语法与 `/` 路径 |
|
package/lib/index.js
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { createRequire } from "node:module";
|
|
2
|
+
import z from "@deepseek-ai/schemastery";
|
|
2
3
|
import * as LlmModule from "@deepseek-ai/dsh-llm";
|
|
3
4
|
import { HarnessError, LlmAdapter, LlmError, ProviderRequestId, ReasoningEffortId, attributionHeaders, resolveRetryPolicy } from "@deepseek-ai/dsh-llm";
|
|
4
5
|
import { WebError } from "@deepseek-ai/dsh-web";
|
|
6
|
+
import { lookup } from "node:dns/promises";
|
|
7
|
+
import { isIP } from "node:net";
|
|
5
8
|
import { createUserMessage } from "@deepseek-ai/dsh-llm/message";
|
|
6
9
|
import { defineTool } from "@deepseek-ai/dsh-tools";
|
|
7
10
|
import { createHash, randomBytes, randomUUID } from "node:crypto";
|
|
@@ -12,7 +15,6 @@ import path, { dirname, join } from "node:path";
|
|
|
12
15
|
import os, { homedir } from "node:os";
|
|
13
16
|
import { ProxyAgent, fetch as fetch$1 } from "undici";
|
|
14
17
|
import * as SettingsModule from "@deepseek-ai/dsh-settings";
|
|
15
|
-
import z from "@deepseek-ai/schemastery";
|
|
16
18
|
import fsPromises, { chmod, lstat, mkdir, open, rename, stat, unlink } from "node:fs/promises";
|
|
17
19
|
import { isDeepStrictEqual } from "node:util";
|
|
18
20
|
import { URL as URL$1, URLSearchParams as URLSearchParams$1 } from "node:url";
|
|
@@ -274,6 +276,225 @@ var CodexChatGptAdapter = class extends LlmAdapter {
|
|
|
274
276
|
}
|
|
275
277
|
};
|
|
276
278
|
//#endregion
|
|
279
|
+
//#region src/host/fetch-address-policy.ts
|
|
280
|
+
/**
|
|
281
|
+
* Destination policy for this plugin's fetch provider.
|
|
282
|
+
*
|
|
283
|
+
* DSH's built-in provider resolves every destination and validates and pins the
|
|
284
|
+
* address before it connects. That cannot work on a machine whose proxy answers
|
|
285
|
+
* DNS with its own fake-ip range (`198.18.0.0/15` for Clash/Mihomo, the usual
|
|
286
|
+
* companion of a system proxy): the hostname is ordinary, the answer is reserved
|
|
287
|
+
* space, and every request is refused before the proxy that could have resolved
|
|
288
|
+
* it is ever consulted. This provider exists for that machine, so it cannot
|
|
289
|
+
* validate names the way the built-in one does.
|
|
290
|
+
*
|
|
291
|
+
* It keeps the half of the policy that needs no resolution — an address the URL
|
|
292
|
+
* states outright is refused unless it is globally reachable unicast, because a
|
|
293
|
+
* proxy on this machine must not become a path into loopback or a LAN — and it
|
|
294
|
+
* also refuses a name this machine resolves into private space. Only the proxy's
|
|
295
|
+
* own fake-ip answers are accepted, since that is what the proxy's name
|
|
296
|
+
* resolution looks like from here; the URL hostname still reaches the proxy
|
|
297
|
+
* intact, so the proxy decides where the name really goes.
|
|
298
|
+
*
|
|
299
|
+
* @module dsh-chatgpt-subscription/fetch-address-policy
|
|
300
|
+
*/
|
|
301
|
+
/**
|
|
302
|
+
* Every IPv4 range a fetch may not target: "this network", private space,
|
|
303
|
+
* carrier-grade NAT, loopback, link-local, IETF assignments, documentation,
|
|
304
|
+
* the 6to4 relay anycast, the benchmarking range the mainstream proxy tools use
|
|
305
|
+
* for fake-ip answers, multicast, and reserved space including the broadcast
|
|
306
|
+
* address.
|
|
307
|
+
*/
|
|
308
|
+
const NON_PUBLIC_IPV4 = [
|
|
309
|
+
"0.0.0.0/8",
|
|
310
|
+
"10.0.0.0/8",
|
|
311
|
+
"100.64.0.0/10",
|
|
312
|
+
"127.0.0.0/8",
|
|
313
|
+
"169.254.0.0/16",
|
|
314
|
+
"172.16.0.0/12",
|
|
315
|
+
"192.0.0.0/24",
|
|
316
|
+
"192.0.2.0/24",
|
|
317
|
+
"192.88.99.0/24",
|
|
318
|
+
"192.168.0.0/16",
|
|
319
|
+
"198.18.0.0/15",
|
|
320
|
+
"198.51.100.0/24",
|
|
321
|
+
"203.0.113.0/24",
|
|
322
|
+
"224.0.0.0/4",
|
|
323
|
+
"240.0.0.0/4"
|
|
324
|
+
];
|
|
325
|
+
/** The fake-ip range the mainstream proxy tools hand out for the names they own. */
|
|
326
|
+
const FAKE_IP_RANGE = "198.18.0.0/15";
|
|
327
|
+
/**
|
|
328
|
+
* Resolve one hostname to every address the local resolver returns, or to no
|
|
329
|
+
* addresses when it cannot.
|
|
330
|
+
*
|
|
331
|
+
* A failure is not evidence of a local destination: the configured proxy
|
|
332
|
+
* resolves the origin itself, so a name this machine cannot resolve must not
|
|
333
|
+
* fail a request the proxy could have served.
|
|
334
|
+
*
|
|
335
|
+
* @param hostname - the URL hostname, without brackets.
|
|
336
|
+
* @returns the resolved addresses, or an empty list when resolution failed.
|
|
337
|
+
*/
|
|
338
|
+
async function lookupHostAddresses(hostname) {
|
|
339
|
+
try {
|
|
340
|
+
return (await lookup(hostname, {
|
|
341
|
+
all: true,
|
|
342
|
+
verbatim: true
|
|
343
|
+
})).map((answer) => answer.address);
|
|
344
|
+
} catch {
|
|
345
|
+
return [];
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* Whether a host is stated as an IP address rather than as a name.
|
|
350
|
+
*
|
|
351
|
+
* @param hostname - a URL hostname, bracketed or not.
|
|
352
|
+
* @returns true when the host is an IPv4 or IPv6 literal.
|
|
353
|
+
*/
|
|
354
|
+
function isIpLiteral(hostname) {
|
|
355
|
+
return isIP(unbracket(hostname)) !== 0;
|
|
356
|
+
}
|
|
357
|
+
/**
|
|
358
|
+
* Whether an address is one only a proxy's fake-ip resolver hands out. The IANA
|
|
359
|
+
* benchmarking range carries no real host, so an answer inside it is this
|
|
360
|
+
* machine's proxy claiming the name, not a private destination.
|
|
361
|
+
*
|
|
362
|
+
* @param address - a textual IPv4 (or IPv4-mapped IPv6) address.
|
|
363
|
+
* @returns true when the address is inside the proxy fake-ip range.
|
|
364
|
+
*/
|
|
365
|
+
function isProxyFakeIpAddress(address) {
|
|
366
|
+
const value = ipv4Value(unbracket(address).replace(/^::ffff:/i, ""));
|
|
367
|
+
return value !== void 0 && inRange(value, FAKE_IP_RANGE);
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* Whether an address is globally reachable unicast. IPv4-mapped IPv6 is
|
|
371
|
+
* classified by the IPv4 address it embeds; every other IPv6 address is public
|
|
372
|
+
* only inside `2000::/3`, which excludes loopback, unique-local, link-local,
|
|
373
|
+
* multicast, and the transition and translation prefixes whose real destination
|
|
374
|
+
* cannot be seen from the address alone.
|
|
375
|
+
*
|
|
376
|
+
* @param address - a textual IPv4 or IPv6 address, bracketed or not.
|
|
377
|
+
* @returns true only for a public unicast destination.
|
|
378
|
+
*/
|
|
379
|
+
function isPublicIpAddress(address) {
|
|
380
|
+
const unbracketed = unbracket(address);
|
|
381
|
+
const family = isIP(unbracketed);
|
|
382
|
+
if (family === 4) return isPublicIpv4(unbracketed);
|
|
383
|
+
if (family !== 6) return false;
|
|
384
|
+
const mapped = unbracketed.replace(/^::ffff:/i, "");
|
|
385
|
+
if (isIP(mapped) === 4) return isPublicIpv4(mapped);
|
|
386
|
+
return isPublicIpv6(unbracketed);
|
|
387
|
+
}
|
|
388
|
+
/**
|
|
389
|
+
* Whether a hostname is an IP literal no request may target.
|
|
390
|
+
*
|
|
391
|
+
* @param hostname - a URL hostname, bracketed or not.
|
|
392
|
+
* @returns true when the host is a literal address that is not public.
|
|
393
|
+
*/
|
|
394
|
+
function isNonPublicIpLiteral(hostname) {
|
|
395
|
+
const unbracketed = unbracket(hostname);
|
|
396
|
+
return isIP(unbracketed) !== 0 && !isPublicIpAddress(unbracketed);
|
|
397
|
+
}
|
|
398
|
+
/**
|
|
399
|
+
* Refuse a fetch destination the local network policy exists to keep out of
|
|
400
|
+
* reach, before any request is made.
|
|
401
|
+
*
|
|
402
|
+
* @param hostname - the URL hostname, bracketed or not.
|
|
403
|
+
* @param addresses - every address the local resolver returned for the host.
|
|
404
|
+
* @throws WebError `WEB_BLOCKED_URL` for a non-public literal, or for a name
|
|
405
|
+
* this machine resolves into private space that is not the proxy's fake-ip.
|
|
406
|
+
*/
|
|
407
|
+
function assertPublicFetchTarget(hostname, addresses) {
|
|
408
|
+
if (isNonPublicIpLiteral(hostname)) throw new WebError(`URL hostname "${hostname}" is not a public IP address`, "WEB_BLOCKED_URL");
|
|
409
|
+
for (const address of addresses) {
|
|
410
|
+
if (isPublicIpAddress(address) || isProxyFakeIpAddress(address)) continue;
|
|
411
|
+
throw new WebError(`URL hostname "${hostname}" resolves to a non-public IP address`, "WEB_BLOCKED_URL");
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
/** Whether a dotted quad is public, using {@link NON_PUBLIC_IPV4}. */
|
|
415
|
+
function isPublicIpv4(address) {
|
|
416
|
+
const value = ipv4Value(address);
|
|
417
|
+
if (value === void 0) return false;
|
|
418
|
+
return !NON_PUBLIC_IPV4.some((cidr) => inRange(value, cidr));
|
|
419
|
+
}
|
|
420
|
+
/** Whether a 32-bit address sits inside one `a.b.c.d/prefix` range. */
|
|
421
|
+
function inRange(value, cidr) {
|
|
422
|
+
const slash = cidr.indexOf("/");
|
|
423
|
+
const network = ipv4Value(cidr.slice(0, slash));
|
|
424
|
+
const prefix = Number(cidr.slice(slash + 1));
|
|
425
|
+
if (network === void 0 || !Number.isInteger(prefix) || prefix < 0 || prefix > 32) return false;
|
|
426
|
+
const mask = prefix === 0 ? 0 : 4294967295 << 32 - prefix >>> 0;
|
|
427
|
+
return (value & mask) >>> 0 === (network & mask) >>> 0;
|
|
428
|
+
}
|
|
429
|
+
/** Parse a dotted quad into its 32-bit value, or `undefined` when it is not one. */
|
|
430
|
+
function ipv4Value(address) {
|
|
431
|
+
const parts = address.split(".");
|
|
432
|
+
if (parts.length !== 4) return void 0;
|
|
433
|
+
let value = 0;
|
|
434
|
+
for (const part of parts) {
|
|
435
|
+
if (!/^\d{1,3}$/.test(part)) return void 0;
|
|
436
|
+
const octet = Number(part);
|
|
437
|
+
if (octet > 255) return void 0;
|
|
438
|
+
value = (value << 8 | octet) >>> 0;
|
|
439
|
+
}
|
|
440
|
+
return value;
|
|
441
|
+
}
|
|
442
|
+
/** Whether an IPv6 literal is inside `2000::/3` and outside its blocked sub-ranges. */
|
|
443
|
+
function isPublicIpv6(address) {
|
|
444
|
+
const words = ipv6Words(address);
|
|
445
|
+
if (words === void 0) return false;
|
|
446
|
+
const [first = 0, second = 0] = words;
|
|
447
|
+
if ((first >> 8 & 224) !== 32) return false;
|
|
448
|
+
if (first === 8193) {
|
|
449
|
+
if (second === 3512) return false;
|
|
450
|
+
if (second === 0) return false;
|
|
451
|
+
if (second === 2) return false;
|
|
452
|
+
if ((second & 65520) === 16) return false;
|
|
453
|
+
if ((second & 65520) === 32) return false;
|
|
454
|
+
}
|
|
455
|
+
if (first === 8194) return false;
|
|
456
|
+
if (first === 16383 && second <= 4095) return false;
|
|
457
|
+
return true;
|
|
458
|
+
}
|
|
459
|
+
/** Parse an IPv6 literal into its eight 16-bit words, or `undefined` when it is not one. */
|
|
460
|
+
function ipv6Words(address) {
|
|
461
|
+
const zone = address.indexOf("%");
|
|
462
|
+
const halves = (zone === -1 ? address : address.slice(0, zone)).split("::");
|
|
463
|
+
if (halves.length > 2) return void 0;
|
|
464
|
+
const head = parseIpv6Half(halves[0] ?? "");
|
|
465
|
+
if (head === void 0) return void 0;
|
|
466
|
+
if (halves.length === 1) return head.length === 8 ? head : void 0;
|
|
467
|
+
const tail = parseIpv6Half(halves[1] ?? "");
|
|
468
|
+
if (tail === void 0) return void 0;
|
|
469
|
+
const gap = 8 - head.length - tail.length;
|
|
470
|
+
if (gap < 1) return void 0;
|
|
471
|
+
return [
|
|
472
|
+
...head,
|
|
473
|
+
...new Array(gap).fill(0),
|
|
474
|
+
...tail
|
|
475
|
+
];
|
|
476
|
+
}
|
|
477
|
+
/** Parse one '::'-free half of an IPv6 literal, expanding a trailing dotted quad. */
|
|
478
|
+
function parseIpv6Half(segment) {
|
|
479
|
+
if (segment === "") return [];
|
|
480
|
+
const words = [];
|
|
481
|
+
for (const piece of segment.split(":")) {
|
|
482
|
+
if (piece.includes(".")) {
|
|
483
|
+
const value = ipv4Value(piece);
|
|
484
|
+
if (value === void 0) return void 0;
|
|
485
|
+
words.push(value >>> 16, value & 65535);
|
|
486
|
+
continue;
|
|
487
|
+
}
|
|
488
|
+
if (!/^[0-9a-fA-F]{1,4}$/.test(piece)) return void 0;
|
|
489
|
+
words.push(parseInt(piece, 16));
|
|
490
|
+
}
|
|
491
|
+
return words;
|
|
492
|
+
}
|
|
493
|
+
/** WHATWG URL keeps brackets around an IPv6 hostname; IP parsers do not. */
|
|
494
|
+
function unbracket(hostname) {
|
|
495
|
+
return hostname.startsWith("[") && hostname.endsWith("]") ? hostname.slice(1, -1) : hostname;
|
|
496
|
+
}
|
|
497
|
+
//#endregion
|
|
277
498
|
//#region src/host/codex-fetch.ts
|
|
278
499
|
const DEFAULT_MAX_RESPONSE_BYTES = 2 * 1024 * 1024;
|
|
279
500
|
const DEFAULT_MAX_BODY_CHARS = 1e5;
|
|
@@ -281,6 +502,7 @@ function createCodexFetchProvider(options = {}) {
|
|
|
281
502
|
const fetchFn = options.fetchFn ?? fetch;
|
|
282
503
|
const maxResponseBytes = options.maxResponseBytes ?? DEFAULT_MAX_RESPONSE_BYTES;
|
|
283
504
|
const maxBodyChars = options.maxBodyChars ?? DEFAULT_MAX_BODY_CHARS;
|
|
505
|
+
const resolveHostAddresses = options.resolveHostAddresses ?? lookupHostAddresses;
|
|
284
506
|
return {
|
|
285
507
|
id: CODEX_FETCH_PROVIDER_ID,
|
|
286
508
|
available: () => true,
|
|
@@ -293,6 +515,7 @@ function createCodexFetchProvider(options = {}) {
|
|
|
293
515
|
throw new WebError(`invalid URL: ${targetUrl}`, "WEB_INVALID_URL", { cause: error });
|
|
294
516
|
}
|
|
295
517
|
if (parsedUrl.protocol !== "http:" && parsedUrl.protocol !== "https:") throw new WebError(`unsupported URL scheme "${parsedUrl.protocol}" (only http and https are allowed)`, "WEB_INVALID_URL");
|
|
518
|
+
assertPublicFetchTarget(parsedUrl.hostname, isIpLiteral(parsedUrl.hostname) ? [] : await resolveHostAddresses(parsedUrl.hostname));
|
|
296
519
|
let response;
|
|
297
520
|
try {
|
|
298
521
|
response = await fetchFn(targetUrl, {
|
|
@@ -3538,7 +3761,7 @@ var SearchProviderSwitcher = class {
|
|
|
3538
3761
|
constructor(loader) {
|
|
3539
3762
|
this.loader = loader;
|
|
3540
3763
|
}
|
|
3541
|
-
async select(preference) {
|
|
3764
|
+
async select(preference, options = {}) {
|
|
3542
3765
|
const entry = this.findWebEntry();
|
|
3543
3766
|
if (entry === null) return;
|
|
3544
3767
|
const config = currentConfig(entry);
|
|
@@ -3547,9 +3770,9 @@ var SearchProviderSwitcher = class {
|
|
|
3547
3770
|
this.originalFetchProvider = typeof config.fetchProvider === "string" && config.fetchProvider !== "codex-subscription" ? config.fetchProvider : void 0;
|
|
3548
3771
|
this.initialized = true;
|
|
3549
3772
|
}
|
|
3550
|
-
const
|
|
3551
|
-
const nextSearch =
|
|
3552
|
-
const nextFetch =
|
|
3773
|
+
const codexSelected = preference === SEARCH_PROVIDER_CODEX;
|
|
3774
|
+
const nextSearch = codexSelected ? CODEX_SEARCH_PROVIDER_ID : this.originalSearchProvider;
|
|
3775
|
+
const nextFetch = codexSelected || options.pluginFetch === true ? CODEX_FETCH_PROVIDER_ID : this.originalFetchProvider;
|
|
3553
3776
|
if (config.searchProvider === nextSearch && config.fetchProvider === nextFetch) return;
|
|
3554
3777
|
const nextConfig = { ...config };
|
|
3555
3778
|
if (nextSearch === void 0) delete nextConfig.searchProvider;
|
|
@@ -4912,7 +5135,7 @@ function sanitizeText(text) {
|
|
|
4912
5135
|
function isRecord(value) {
|
|
4913
5136
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4914
5137
|
}
|
|
4915
|
-
function asString(value) {
|
|
5138
|
+
function asString$1(value) {
|
|
4916
5139
|
return typeof value === "string" ? value : void 0;
|
|
4917
5140
|
}
|
|
4918
5141
|
function safeJsonParse(text) {
|
|
@@ -4935,10 +5158,10 @@ function parseArguments(raw) {
|
|
|
4935
5158
|
return isRecord(parsed) ? parsed : {};
|
|
4936
5159
|
}
|
|
4937
5160
|
function imageBlockToPart(block) {
|
|
4938
|
-
let data = asString(block.data) || asString(block.base64);
|
|
5161
|
+
let data = asString$1(block.data) || asString$1(block.base64);
|
|
4939
5162
|
const source = isRecord(block.source) ? block.source : void 0;
|
|
4940
|
-
if (!data && source) data = asString(source.data) || asString(source.base64);
|
|
4941
|
-
let mimeType = asString(block.mimeType) || asString(block.mediaType) || (source ? asString(source.mimeType) || asString(source.mediaType) : void 0) || "image/png";
|
|
5163
|
+
if (!data && source) data = asString$1(source.data) || asString$1(source.base64);
|
|
5164
|
+
let mimeType = asString$1(block.mimeType) || asString$1(block.mediaType) || (source ? asString$1(source.mimeType) || asString$1(source.mediaType) : void 0) || "image/png";
|
|
4942
5165
|
if (data?.startsWith("data:")) {
|
|
4943
5166
|
const match = data.match(/^data:([^;,]+);base64,(.*)$/s);
|
|
4944
5167
|
if (match) {
|
|
@@ -4984,7 +5207,7 @@ function replayBlockFor(message, index) {
|
|
|
4984
5207
|
}
|
|
4985
5208
|
}
|
|
4986
5209
|
function thoughtSignature(part) {
|
|
4987
|
-
return asString(part?.thoughtSignature) || asString(part?.thought_signature) || asString(part?.thinkingSignature) || asString(part?.textSignature);
|
|
5210
|
+
return asString$1(part?.thoughtSignature) || asString$1(part?.thought_signature) || asString$1(part?.thinkingSignature) || asString$1(part?.textSignature);
|
|
4988
5211
|
}
|
|
4989
5212
|
function replayPart(part) {
|
|
4990
5213
|
const copy = { ...part };
|
|
@@ -5003,7 +5226,7 @@ function assistantParts(message, model, runtimeModel, toolCalls) {
|
|
|
5003
5226
|
if (!isRecord(block)) continue;
|
|
5004
5227
|
const replay = replayBlockFor(message, index);
|
|
5005
5228
|
const originalParts = Array.isArray(replay?.parts) ? replay.parts.filter(isRecord) : [];
|
|
5006
|
-
if ((block.type === "text" || block.type === "reasoning") && originalParts.length > 0 && originalParts.every((part) => !part.functionCall) && originalParts.map((part) => asString(part.text) || "").join("") === sanitizeText(String(block.text || ""))) {
|
|
5229
|
+
if ((block.type === "text" || block.type === "reasoning") && originalParts.length > 0 && originalParts.every((part) => !part.functionCall) && originalParts.map((part) => asString$1(part.text) || "").join("") === sanitizeText(String(block.text || ""))) {
|
|
5007
5230
|
parts.push(...originalParts.map(replayPart));
|
|
5008
5231
|
continue;
|
|
5009
5232
|
}
|
|
@@ -5024,7 +5247,7 @@ function assistantParts(message, model, runtimeModel, toolCalls) {
|
|
|
5024
5247
|
const toolId = String(block.id || "");
|
|
5025
5248
|
const toolName = String(block.name || "");
|
|
5026
5249
|
const originalCall = originalParts.find((part) => isRecord(part.functionCall));
|
|
5027
|
-
const wireId = asString((isRecord(originalCall?.functionCall) ? originalCall.functionCall : void 0)?.id) || (toolCallIdNeeded(model.id, runtimeModel) ? sanitizeToolCallId(toolId, toolName) : originalCall ? void 0 : toolId || void 0);
|
|
5250
|
+
const wireId = asString$1((isRecord(originalCall?.functionCall) ? originalCall.functionCall : void 0)?.id) || (toolCallIdNeeded(model.id, runtimeModel) ? sanitizeToolCallId(toolId, toolName) : originalCall ? void 0 : toolId || void 0);
|
|
5028
5251
|
toolCalls.set(toolId, {
|
|
5029
5252
|
name: toolName,
|
|
5030
5253
|
id: wireId
|
|
@@ -5301,8 +5524,8 @@ function processStreamLine(line, state) {
|
|
|
5301
5524
|
if (isRecord(part.functionCall)) {
|
|
5302
5525
|
out.push(...closeCurrentBlock(state));
|
|
5303
5526
|
const fc = part.functionCall;
|
|
5304
|
-
const toolName = asString(fc.name) || "";
|
|
5305
|
-
const toolId = asString(fc.id) || sanitizeToolCallId("", toolName);
|
|
5527
|
+
const toolName = asString$1(fc.name) || "";
|
|
5528
|
+
const toolId = asString$1(fc.id) || sanitizeToolCallId("", toolName);
|
|
5306
5529
|
const argsText = JSON.stringify(isRecord(fc.args) ? fc.args : {});
|
|
5307
5530
|
const index = state.blocks.length;
|
|
5308
5531
|
const block = {
|
|
@@ -5340,7 +5563,7 @@ function processStreamLine(line, state) {
|
|
|
5340
5563
|
}
|
|
5341
5564
|
collectUsage(chunk.usageMetadata, state);
|
|
5342
5565
|
if (responseData !== chunk) collectUsage(responseData.usageMetadata, state);
|
|
5343
|
-
const finishReason = asString(candidate?.finishReason) || asString(responseData.finishReason);
|
|
5566
|
+
const finishReason = asString$1(candidate?.finishReason) || asString$1(responseData.finishReason);
|
|
5344
5567
|
if (finishReason) {
|
|
5345
5568
|
state.finishReason = finishReason;
|
|
5346
5569
|
out.push(...closeCurrentBlock(state));
|
|
@@ -5685,7 +5908,230 @@ function registerAntigravityRoutes(ctx, store, modelSettings, preferences, fetch
|
|
|
5685
5908
|
});
|
|
5686
5909
|
}
|
|
5687
5910
|
//#endregion
|
|
5911
|
+
//#region src/host/subagent-model-authorization.ts
|
|
5912
|
+
/** DSH settings namespace owned by the Subagent settings card. */
|
|
5913
|
+
const SUBAGENT_MODEL_SELECTION_NAMESPACE = "subagent-model-selection";
|
|
5914
|
+
/** Durable Session event carrying one Session's authorized child routes. */
|
|
5915
|
+
const SUBAGENT_POLICY_EVENT = "subagent/model-selection-policy";
|
|
5916
|
+
/** Delegation tools whose child routes this guard authorizes. */
|
|
5917
|
+
const DEFAULT_DELEGATION_TOOLS = ["subagent"];
|
|
5918
|
+
/**
|
|
5919
|
+
* Read one deployment event field as a string.
|
|
5920
|
+
* @param value - Candidate field value from a durable event.
|
|
5921
|
+
* @returns the string value, or undefined for any other type.
|
|
5922
|
+
*/
|
|
5923
|
+
function asString(value) {
|
|
5924
|
+
return typeof value === "string" ? value : void 0;
|
|
5925
|
+
}
|
|
5926
|
+
/**
|
|
5927
|
+
* Read one deployment event field as a plain object.
|
|
5928
|
+
* @param value - Candidate field value from a durable event.
|
|
5929
|
+
* @returns the record value, or undefined for arrays, null, and primitives.
|
|
5930
|
+
*/
|
|
5931
|
+
function asRecord(value) {
|
|
5932
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
5933
|
+
}
|
|
5934
|
+
/**
|
|
5935
|
+
* Read one deployment event field as a boolean.
|
|
5936
|
+
* @param value - Candidate field value from a durable event.
|
|
5937
|
+
* @returns the boolean value, or undefined for any other type.
|
|
5938
|
+
*/
|
|
5939
|
+
function asBoolean(value) {
|
|
5940
|
+
return typeof value === "boolean" ? value : void 0;
|
|
5941
|
+
}
|
|
5942
|
+
/**
|
|
5943
|
+
* Parse the allowlist out of a durable route-policy event.
|
|
5944
|
+
* @param data - `subagent/model-selection-policy` event payload.
|
|
5945
|
+
* @returns every well-formed route, or undefined when the payload carries none.
|
|
5946
|
+
*/
|
|
5947
|
+
function parseAllowedRoutes(data) {
|
|
5948
|
+
const allowed = asRecord(data)?.["allowedModels"];
|
|
5949
|
+
if (!Array.isArray(allowed)) return void 0;
|
|
5950
|
+
const routes = [];
|
|
5951
|
+
for (const entry of allowed) {
|
|
5952
|
+
const record = asRecord(entry);
|
|
5953
|
+
const provider = asString(record?.["provider"]);
|
|
5954
|
+
const model = asString(record?.["model"]);
|
|
5955
|
+
if (provider === void 0 || model === void 0) continue;
|
|
5956
|
+
if (provider.length === 0 || model.length === 0) continue;
|
|
5957
|
+
routes.push({
|
|
5958
|
+
provider,
|
|
5959
|
+
model
|
|
5960
|
+
});
|
|
5961
|
+
}
|
|
5962
|
+
return routes.length === 0 ? void 0 : routes;
|
|
5963
|
+
}
|
|
5964
|
+
/**
|
|
5965
|
+
* Find one Session's recorded allowlist among its durable events.
|
|
5966
|
+
* @param session - Session whose log is scanned.
|
|
5967
|
+
* @returns the authorized routes, or undefined for a Session that recorded none.
|
|
5968
|
+
*/
|
|
5969
|
+
function policyRoutesOf(session) {
|
|
5970
|
+
if (session === void 0 || typeof session.eventAt !== "function") return void 0;
|
|
5971
|
+
for (let seq = 0; seq < 256; seq += 1) {
|
|
5972
|
+
let event;
|
|
5973
|
+
try {
|
|
5974
|
+
event = session.eventAt(seq);
|
|
5975
|
+
} catch {
|
|
5976
|
+
return;
|
|
5977
|
+
}
|
|
5978
|
+
if (event === void 0) return void 0;
|
|
5979
|
+
if (event.type !== "subagent/model-selection-policy") continue;
|
|
5980
|
+
const routes = parseAllowedRoutes(event.data);
|
|
5981
|
+
if (routes !== void 0) return routes;
|
|
5982
|
+
}
|
|
5983
|
+
}
|
|
5984
|
+
/**
|
|
5985
|
+
* Resolve the allowlist a delegation from this agent must respect: the agent's
|
|
5986
|
+
* own recorded policy, else the nearest ancestor Session that recorded one
|
|
5987
|
+
* (the inheritance the delegation tool applies to child Sessions).
|
|
5988
|
+
* @param agent - Calling agent.
|
|
5989
|
+
* @param sessions - Session registry used for ancestor lookup.
|
|
5990
|
+
* @returns the authorized routes, or undefined when no Session recorded any.
|
|
5991
|
+
*/
|
|
5992
|
+
function authorizedRoutesFor(agent, sessions) {
|
|
5993
|
+
let session = agent?.session;
|
|
5994
|
+
for (let depth = 0; depth < 8 && session !== void 0; depth += 1) {
|
|
5995
|
+
const recorded = policyRoutesOf(session);
|
|
5996
|
+
if (recorded !== void 0) return recorded;
|
|
5997
|
+
const header = session.header;
|
|
5998
|
+
if (header?.origin !== "subagent") return void 0;
|
|
5999
|
+
const parentId = asString(header.parentSession);
|
|
6000
|
+
if (parentId === void 0) return void 0;
|
|
6001
|
+
session = sessions.get(parentId);
|
|
6002
|
+
}
|
|
6003
|
+
}
|
|
6004
|
+
/**
|
|
6005
|
+
* Read the Host preference that owns the allowlist. Values in the stored
|
|
6006
|
+
* document are untrusted JSON, so every field is narrowed before use.
|
|
6007
|
+
* @param settings - Live settings service, when composed.
|
|
6008
|
+
* @returns the resolved preference, or undefined without a settings service.
|
|
6009
|
+
*/
|
|
6010
|
+
function subagentModelSelectionPreference(settings) {
|
|
6011
|
+
if (settings === void 0) return void 0;
|
|
6012
|
+
const register = settings["register"];
|
|
6013
|
+
if (typeof register !== "function") return void 0;
|
|
6014
|
+
try {
|
|
6015
|
+
const value = asRecord(register.call(settings, SUBAGENT_MODEL_SELECTION_NAMESPACE, z.object({
|
|
6016
|
+
enabled: z.boolean().default(false),
|
|
6017
|
+
allowedModels: z.array(z.object({
|
|
6018
|
+
provider: z.string().min(1).required(),
|
|
6019
|
+
model: z.string().min(1).required()
|
|
6020
|
+
})).default([])
|
|
6021
|
+
})).get());
|
|
6022
|
+
if (value === void 0) return void 0;
|
|
6023
|
+
return {
|
|
6024
|
+
enabled: asBoolean(value["enabled"]) === true,
|
|
6025
|
+
allowedModels: parseAllowedRoutes(value) ?? []
|
|
6026
|
+
};
|
|
6027
|
+
} catch {
|
|
6028
|
+
return;
|
|
6029
|
+
}
|
|
6030
|
+
}
|
|
6031
|
+
/** Whether an exact route is authorized by a allowlist. */
|
|
6032
|
+
function routesInclude(allowed, provider, model) {
|
|
6033
|
+
return allowed.some((route) => route.provider === provider && route.model === model);
|
|
6034
|
+
}
|
|
6035
|
+
/**
|
|
6036
|
+
* Build the denial reason for a delegation that would run on an unauthorized
|
|
6037
|
+
* route. The reason names the authorized routes so the next call can select one.
|
|
6038
|
+
* @param provider - Effective child provider id, when one is known.
|
|
6039
|
+
* @param model - Effective child model id, when one is known.
|
|
6040
|
+
* @param allowed - Routes the calling Session authorizes.
|
|
6041
|
+
* @param explicit - Whether the model named the route in the tool arguments.
|
|
6042
|
+
* @returns the corrective reason handed back to the model.
|
|
6043
|
+
*/
|
|
6044
|
+
function unauthorizedRouteReason(provider, model, allowed, explicit) {
|
|
6045
|
+
const route = provider === void 0 || model === void 0 ? "the inherited route" : `route "${provider}/${model}"`;
|
|
6046
|
+
return `subagent model selection: ${explicit ? "the model this call selected is not on the Session allowlist" : "the route this call would inherit from the parent is not on the Session allowlist"} (${route}). Provide an authorized provider and model — ${allowed.map((entry) => `${entry.provider}/${entry.model}`).join(", ")} — using list_subagent_models to inspect their reasoning efforts.`;
|
|
6047
|
+
}
|
|
6048
|
+
/**
|
|
6049
|
+
* Decide whether one delegation call may start its child.
|
|
6050
|
+
* @param agent - Calling agent.
|
|
6051
|
+
* @param toolName - Tool being dispatched.
|
|
6052
|
+
* @param args - Parsed tool arguments.
|
|
6053
|
+
* @param preference - Current Host preference, when the settings service exists.
|
|
6054
|
+
* @param sessions - Session registry used for ancestor lookup.
|
|
6055
|
+
* @param toolNames - Delegation tool names this guard authorizes.
|
|
6056
|
+
* @param scope - Whether an unrecorded Session falls back to the preference.
|
|
6057
|
+
* @returns a denial reason, or undefined to leave the call untouched.
|
|
6058
|
+
*/
|
|
6059
|
+
function delegationDenialReason(agent, toolName, args, preference, sessions, toolNames = DEFAULT_DELEGATION_TOOLS, scope = "session") {
|
|
6060
|
+
if (!toolNames.includes(toolName)) return void 0;
|
|
6061
|
+
if (preference !== void 0 && !preference.enabled) return void 0;
|
|
6062
|
+
const allowed = authorizedRoutesFor(agent, sessions) ?? (scope === "preference" ? preference?.allowedModels ?? [] : []);
|
|
6063
|
+
if (allowed.length === 0) return void 0;
|
|
6064
|
+
const request = asRecord(args) ?? {};
|
|
6065
|
+
const requestedProvider = asString(request["provider"]);
|
|
6066
|
+
const requestedModel = asString(request["model"]);
|
|
6067
|
+
const explicit = requestedProvider !== void 0 || requestedModel !== void 0;
|
|
6068
|
+
const implicit = requestedProvider === void 0 && requestedModel === void 0;
|
|
6069
|
+
const provider = implicit ? asString(agent?.options?.provider) : requestedProvider;
|
|
6070
|
+
const model = implicit ? asString(agent?.options?.model) : requestedModel;
|
|
6071
|
+
if (provider === void 0 || model === void 0) return;
|
|
6072
|
+
if (routesInclude(allowed, provider, model)) return void 0;
|
|
6073
|
+
return unauthorizedRouteReason(provider, model, allowed, explicit);
|
|
6074
|
+
}
|
|
6075
|
+
/**
|
|
6076
|
+
* Build the monotonic tool guard for one deployment. The Host preference is
|
|
6077
|
+
* sampled from the live settings document on every call, because a settings
|
|
6078
|
+
* edit must not rebuild the guard the way it cannot rebuild a Session.
|
|
6079
|
+
* @param options - settings, session registry, and delegation tool names.
|
|
6080
|
+
* @returns a guard that denies delegations outside the Session allowlist.
|
|
6081
|
+
*/
|
|
6082
|
+
function createSubagentAuthorization(options) {
|
|
6083
|
+
return (agent, toolName, args) => delegationDenialReason(agent, toolName, args, subagentModelSelectionPreference(options.settings), options.sessions, options.toolNames, options.scope ?? "session");
|
|
6084
|
+
}
|
|
6085
|
+
/** Delegation names this guard recognizes when configuration omits them. */
|
|
6086
|
+
function normalizeDelegationToolNames(value) {
|
|
6087
|
+
const names = Array.isArray(value) ? value.filter((entry) => typeof entry === "string" && entry.trim().length > 0) : [];
|
|
6088
|
+
return [...new Set(names.length === 0 ? [...DEFAULT_DELEGATION_TOOLS] : names.map((name) => name.trim()))];
|
|
6089
|
+
}
|
|
6090
|
+
/**
|
|
6091
|
+
* Validate the plugin configuration that owns this guard.
|
|
6092
|
+
* @param toolNames - Candidate delegation tool names.
|
|
6093
|
+
* @returns the exact names this guard authorizes.
|
|
6094
|
+
*/
|
|
6095
|
+
function validateDelegationToolNames(toolNames) {
|
|
6096
|
+
if (toolNames.length === 0) throw new Error("dsh-chatgpt-subscription: subagentModelAuthorization.toolNames must name at least one delegation tool");
|
|
6097
|
+
for (const name of toolNames) if (name.length === 0 || name !== name.trim()) throw new Error(`dsh-chatgpt-subscription: subagentModelAuthorization.toolNames entry "${name}" must be a non-empty trimmed string`);
|
|
6098
|
+
return [...toolNames];
|
|
6099
|
+
}
|
|
6100
|
+
/**
|
|
6101
|
+
* Register the monotonic guard on the Host tool registry.
|
|
6102
|
+
* @param ctx - Host context carrying `tools` (and optionally `settings`).
|
|
6103
|
+
* @param sessions - Session registry used for ancestor lookup.
|
|
6104
|
+
* @param config - Enforcement toggle and delegation tool names.
|
|
6105
|
+
* @returns the exact disposer that unregisters the guard.
|
|
6106
|
+
*/
|
|
6107
|
+
function installSubagentModelAuthorization(ctx, sessions, config = {}) {
|
|
6108
|
+
const toolNames = validateDelegationToolNames(config.toolNames ?? [...DEFAULT_DELEGATION_TOOLS]);
|
|
6109
|
+
const scope = validateAuthorizationScope(config.scope ?? "session");
|
|
6110
|
+
const settings = ctx.get?.("settings");
|
|
6111
|
+
const authorize = createSubagentAuthorization({
|
|
6112
|
+
settings,
|
|
6113
|
+
sessions,
|
|
6114
|
+
toolNames,
|
|
6115
|
+
scope
|
|
6116
|
+
});
|
|
6117
|
+
return ctx.tools.guard((exec) => authorize(exec.agent, exec.name, exec.arguments));
|
|
6118
|
+
}
|
|
6119
|
+
/**
|
|
6120
|
+
* Validate the configured authorization scope.
|
|
6121
|
+
* @param scope - Candidate scope from deployment configuration.
|
|
6122
|
+
* @returns the exact scope this guard enforces.
|
|
6123
|
+
*/
|
|
6124
|
+
function validateAuthorizationScope(scope) {
|
|
6125
|
+
if (scope === "session" || scope === "preference") return scope;
|
|
6126
|
+
throw new Error(`dsh-chatgpt-subscription: subagentModelScope must be "session" or "preference", received ${JSON.stringify(scope)}`);
|
|
6127
|
+
}
|
|
6128
|
+
//#endregion
|
|
5688
6129
|
//#region src/index.ts
|
|
6130
|
+
const Config = z.object({
|
|
6131
|
+
subagentModelAuthorization: z.boolean().default(true),
|
|
6132
|
+
subagentModelTools: z.array(z.string()).default([]),
|
|
6133
|
+
subagentModelScope: z.union([z.const("session"), z.const("preference")]).default("session")
|
|
6134
|
+
});
|
|
5689
6135
|
const inject = [
|
|
5690
6136
|
"webServer",
|
|
5691
6137
|
"llm",
|
|
@@ -5694,12 +6140,24 @@ const inject = [
|
|
|
5694
6140
|
"settings",
|
|
5695
6141
|
"loader"
|
|
5696
6142
|
];
|
|
5697
|
-
function apply(ctx) {
|
|
6143
|
+
function apply(ctx, pluginConfig = {}) {
|
|
5698
6144
|
const store = createPlatformTokenStore();
|
|
5699
6145
|
const preferences = registerPreferenceStore(ctx.settings);
|
|
5700
6146
|
const antigravityStore = new FileCredentialStore();
|
|
5701
6147
|
const antigravityModelSettings = new FileModelSettingsStore();
|
|
5702
6148
|
const antigravityPreferences = registerAntigravityPreferenceStore(ctx.settings, antigravityModelSettings);
|
|
6149
|
+
const delegationToolNames = normalizeDelegationToolNames(pluginConfig.subagentModelTools);
|
|
6150
|
+
if (pluginConfig.subagentModelAuthorization !== false) ctx.inject(["sessions"], (scoped) => {
|
|
6151
|
+
const sessions = scoped.get("sessions");
|
|
6152
|
+
if (sessions === void 0) return;
|
|
6153
|
+
scoped.effect(() => {
|
|
6154
|
+
if (typeof scoped.tools?.guard !== "function") return () => void 0;
|
|
6155
|
+
return installSubagentModelAuthorization(scoped, sessions, {
|
|
6156
|
+
toolNames: delegationToolNames,
|
|
6157
|
+
scope: pluginConfig.subagentModelScope ?? "session"
|
|
6158
|
+
});
|
|
6159
|
+
}, "dsh-chatgpt-subscription: subagent model authorization");
|
|
6160
|
+
});
|
|
5703
6161
|
ctx.effect(() => {
|
|
5704
6162
|
const proxyManager = new ProxyManager({
|
|
5705
6163
|
getPreferences: () => preferences.status(),
|
|
@@ -5723,9 +6181,10 @@ function apply(ctx) {
|
|
|
5723
6181
|
reasoningSummary: () => preferences.status().reasoningSummary
|
|
5724
6182
|
}), preferences);
|
|
5725
6183
|
const searchSwitcher = new SearchProviderSwitcher(ctx.loader);
|
|
5726
|
-
const
|
|
5727
|
-
|
|
5728
|
-
|
|
6184
|
+
const applyWebProviders = (current = preferences.status()) => {
|
|
6185
|
+
const pluginFetch = proxyManager.resolveActiveProxyUrl() !== null;
|
|
6186
|
+
searchSwitcher.select(current.searchProvider, { pluginFetch }).catch((error) => {
|
|
6187
|
+
ctx.logger.warn(`[dsh-chatgpt-subscription] Web provider selection could not be applied: ${error instanceof Error ? error.message : String(error)}`);
|
|
5729
6188
|
});
|
|
5730
6189
|
};
|
|
5731
6190
|
const disposeRoutes = registerRoutes(ctx, oauth, usage, preferences, proxyManager);
|
|
@@ -5734,9 +6193,9 @@ function apply(ctx) {
|
|
|
5734
6193
|
ctx.inject(["web"], (ctx) => {
|
|
5735
6194
|
ctx.web.registerSearchProvider(createCodexSearchProvider(oauth, { fetchFn: proxyFetch }));
|
|
5736
6195
|
ctx.web.registerFetchProvider(createCodexFetchProvider({ fetchFn: proxyFetch }));
|
|
5737
|
-
|
|
6196
|
+
applyWebProviders();
|
|
5738
6197
|
});
|
|
5739
|
-
const disposePreferenceWatch = preferences.watch((next) =>
|
|
6198
|
+
const disposePreferenceWatch = preferences.watch((next) => applyWebProviders(next));
|
|
5740
6199
|
return () => {
|
|
5741
6200
|
disposePreferenceWatch();
|
|
5742
6201
|
disposeImageTool();
|
|
@@ -5753,4 +6212,4 @@ function localWebServerBaseUrl(host, port) {
|
|
|
5753
6212
|
return `http://${host === "0.0.0.0" ? "127.0.0.1" : host}:${port}`;
|
|
5754
6213
|
}
|
|
5755
6214
|
//#endregion
|
|
5756
|
-
export { AntigravityAdapter, CodexChatGptAdapter, FileCredentialStore, FileModelSettingsStore, LinuxFileTokenStore, MacKeychainTokenStore, OAuthService, ProxyManager, ResponsesClient, UsageService, WindowsDpapiTokenStore, apply, beginWebLogin, clearCachedQuota, createCodexFetchProvider, createCodexImageTool, createCodexSearchProvider, createPlatformTokenStore, credentialPath, detectSystemProxy, fetchAccountQuota, getCachedQuota, inject, loginAndSave, mapCodexUsage, modelSettingsPath, parseCodexUsage, parseResponsesStream, refreshAntigravityToken };
|
|
6215
|
+
export { AntigravityAdapter, CodexChatGptAdapter, Config, FileCredentialStore, FileModelSettingsStore, LinuxFileTokenStore, MacKeychainTokenStore, OAuthService, ProxyManager, ResponsesClient, SUBAGENT_MODEL_SELECTION_NAMESPACE, SUBAGENT_POLICY_EVENT, UsageService, WindowsDpapiTokenStore, apply, authorizedRoutesFor, beginWebLogin, clearCachedQuota, createCodexFetchProvider, createCodexImageTool, createCodexSearchProvider, createPlatformTokenStore, createSubagentAuthorization, credentialPath, delegationDenialReason, detectSystemProxy, fetchAccountQuota, getCachedQuota, inject, installSubagentModelAuthorization, loginAndSave, mapCodexUsage, modelSettingsPath, normalizeDelegationToolNames, parseAllowedRoutes, parseCodexUsage, parseResponsesStream, policyRoutesOf, refreshAntigravityToken, subagentModelSelectionPreference, unauthorizedRouteReason, validateAuthorizationScope, validateDelegationToolNames };
|
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
import { type WebFetchProvider } from '@deepseek-ai/dsh-web';
|
|
2
2
|
type FetchLike = typeof fetch;
|
|
3
|
+
/** Resolves a hostname to every address this machine's resolver returns. */
|
|
4
|
+
export type HostAddressResolver = (hostname: string) => Promise<readonly string[]>;
|
|
3
5
|
export interface CodexFetchProviderOptions {
|
|
4
6
|
fetchFn?: FetchLike;
|
|
5
7
|
maxResponseBytes?: number;
|
|
6
8
|
maxBodyChars?: number;
|
|
9
|
+
/**
|
|
10
|
+
* Address lookup behind the destination policy. Overridden only by focused
|
|
11
|
+
* tests; the policy itself is what every request goes through.
|
|
12
|
+
*/
|
|
13
|
+
resolveHostAddresses?: HostAddressResolver;
|
|
7
14
|
}
|
|
8
15
|
export declare function createCodexFetchProvider(options?: CodexFetchProviderOptions): WebFetchProvider;
|
|
9
16
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"codex-fetch.d.ts","sourceRoot":"","sources":["../../../src/host/codex-fetch.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,KAAK,gBAAgB,EAA6C,MAAM,sBAAsB,CAAA;
|
|
1
|
+
{"version":3,"file":"codex-fetch.d.ts","sourceRoot":"","sources":["../../../src/host/codex-fetch.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,KAAK,gBAAgB,EAA6C,MAAM,sBAAsB,CAAA;AAIjH,KAAK,SAAS,GAAG,OAAO,KAAK,CAAA;AAE7B,4EAA4E;AAC5E,MAAM,MAAM,mBAAmB,GAAG,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,SAAS,MAAM,EAAE,CAAC,CAAA;AAElF,MAAM,WAAW,yBAAyB;IACxC,OAAO,CAAC,EAAE,SAAS,CAAA;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB;;;OAGG;IACH,oBAAoB,CAAC,EAAE,mBAAmB,CAAA;CAC3C;AAKD,wBAAgB,wBAAwB,CACtC,OAAO,GAAE,yBAA8B,GACtC,gBAAgB,CAgGlB"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Destination policy for this plugin's fetch provider.
|
|
3
|
+
*
|
|
4
|
+
* DSH's built-in provider resolves every destination and validates and pins the
|
|
5
|
+
* address before it connects. That cannot work on a machine whose proxy answers
|
|
6
|
+
* DNS with its own fake-ip range (`198.18.0.0/15` for Clash/Mihomo, the usual
|
|
7
|
+
* companion of a system proxy): the hostname is ordinary, the answer is reserved
|
|
8
|
+
* space, and every request is refused before the proxy that could have resolved
|
|
9
|
+
* it is ever consulted. This provider exists for that machine, so it cannot
|
|
10
|
+
* validate names the way the built-in one does.
|
|
11
|
+
*
|
|
12
|
+
* It keeps the half of the policy that needs no resolution — an address the URL
|
|
13
|
+
* states outright is refused unless it is globally reachable unicast, because a
|
|
14
|
+
* proxy on this machine must not become a path into loopback or a LAN — and it
|
|
15
|
+
* also refuses a name this machine resolves into private space. Only the proxy's
|
|
16
|
+
* own fake-ip answers are accepted, since that is what the proxy's name
|
|
17
|
+
* resolution looks like from here; the URL hostname still reaches the proxy
|
|
18
|
+
* intact, so the proxy decides where the name really goes.
|
|
19
|
+
*
|
|
20
|
+
* @module dsh-chatgpt-subscription/fetch-address-policy
|
|
21
|
+
*/
|
|
22
|
+
/**
|
|
23
|
+
* Resolve one hostname to every address the local resolver returns, or to no
|
|
24
|
+
* addresses when it cannot.
|
|
25
|
+
*
|
|
26
|
+
* A failure is not evidence of a local destination: the configured proxy
|
|
27
|
+
* resolves the origin itself, so a name this machine cannot resolve must not
|
|
28
|
+
* fail a request the proxy could have served.
|
|
29
|
+
*
|
|
30
|
+
* @param hostname - the URL hostname, without brackets.
|
|
31
|
+
* @returns the resolved addresses, or an empty list when resolution failed.
|
|
32
|
+
*/
|
|
33
|
+
export declare function lookupHostAddresses(hostname: string): Promise<readonly string[]>;
|
|
34
|
+
/**
|
|
35
|
+
* Whether a host is stated as an IP address rather than as a name.
|
|
36
|
+
*
|
|
37
|
+
* @param hostname - a URL hostname, bracketed or not.
|
|
38
|
+
* @returns true when the host is an IPv4 or IPv6 literal.
|
|
39
|
+
*/
|
|
40
|
+
export declare function isIpLiteral(hostname: string): boolean;
|
|
41
|
+
/**
|
|
42
|
+
* Whether an address is one only a proxy's fake-ip resolver hands out. The IANA
|
|
43
|
+
* benchmarking range carries no real host, so an answer inside it is this
|
|
44
|
+
* machine's proxy claiming the name, not a private destination.
|
|
45
|
+
*
|
|
46
|
+
* @param address - a textual IPv4 (or IPv4-mapped IPv6) address.
|
|
47
|
+
* @returns true when the address is inside the proxy fake-ip range.
|
|
48
|
+
*/
|
|
49
|
+
export declare function isProxyFakeIpAddress(address: string): boolean;
|
|
50
|
+
/**
|
|
51
|
+
* Whether an address is globally reachable unicast. IPv4-mapped IPv6 is
|
|
52
|
+
* classified by the IPv4 address it embeds; every other IPv6 address is public
|
|
53
|
+
* only inside `2000::/3`, which excludes loopback, unique-local, link-local,
|
|
54
|
+
* multicast, and the transition and translation prefixes whose real destination
|
|
55
|
+
* cannot be seen from the address alone.
|
|
56
|
+
*
|
|
57
|
+
* @param address - a textual IPv4 or IPv6 address, bracketed or not.
|
|
58
|
+
* @returns true only for a public unicast destination.
|
|
59
|
+
*/
|
|
60
|
+
export declare function isPublicIpAddress(address: string): boolean;
|
|
61
|
+
/**
|
|
62
|
+
* Whether a hostname is an IP literal no request may target.
|
|
63
|
+
*
|
|
64
|
+
* @param hostname - a URL hostname, bracketed or not.
|
|
65
|
+
* @returns true when the host is a literal address that is not public.
|
|
66
|
+
*/
|
|
67
|
+
export declare function isNonPublicIpLiteral(hostname: string): boolean;
|
|
68
|
+
/**
|
|
69
|
+
* Refuse a fetch destination the local network policy exists to keep out of
|
|
70
|
+
* reach, before any request is made.
|
|
71
|
+
*
|
|
72
|
+
* @param hostname - the URL hostname, bracketed or not.
|
|
73
|
+
* @param addresses - every address the local resolver returned for the host.
|
|
74
|
+
* @throws WebError `WEB_BLOCKED_URL` for a non-public literal, or for a name
|
|
75
|
+
* this machine resolves into private space that is not the proxy's fake-ip.
|
|
76
|
+
*/
|
|
77
|
+
export declare function assertPublicFetchTarget(hostname: string, addresses: readonly string[]): void;
|
|
78
|
+
//# sourceMappingURL=fetch-address-policy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fetch-address-policy.d.ts","sourceRoot":"","sources":["../../../src/host/fetch-address-policy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAkCH;;;;;;;;;;GAUG;AACH,wBAAsB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,MAAM,EAAE,CAAC,CAOtF;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAErD;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAG7D;AAED;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAQ1D;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAG9D;AAED;;;;;;;;GAQG;AACH,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI,CAQ5F"}
|
|
@@ -3,13 +3,27 @@ import type { SearchProviderPreference } from '../shared/contracts.ts';
|
|
|
3
3
|
interface LoaderLike {
|
|
4
4
|
entries(): Iterable<Entry>;
|
|
5
5
|
}
|
|
6
|
+
/** What one selection asks for on top of the settings preference. */
|
|
7
|
+
export interface WebProviderSelectionOptions {
|
|
8
|
+
/**
|
|
9
|
+
* Whether this plugin's fetch provider must serve the `web_fetch` tool.
|
|
10
|
+
*
|
|
11
|
+
* DSH's built-in provider resolves and pins every destination before it
|
|
12
|
+
* connects and routes through a proxy only when the process environment names
|
|
13
|
+
* one, so an OS-level proxy plus the fake-ip DNS that usually comes with it
|
|
14
|
+
* fails every fetch with `WEB_BLOCKED_URL`. Selecting this plugin's provider
|
|
15
|
+
* hands the origin's resolution to the configured proxy — the same
|
|
16
|
+
* proxied-hop semantics DSH applies to a URL it routes through a proxy.
|
|
17
|
+
*/
|
|
18
|
+
readonly pluginFetch?: boolean;
|
|
19
|
+
}
|
|
6
20
|
export declare class SearchProviderSwitcher {
|
|
7
21
|
private readonly loader;
|
|
8
22
|
private originalSearchProvider;
|
|
9
23
|
private originalFetchProvider;
|
|
10
24
|
private initialized;
|
|
11
25
|
constructor(loader: LoaderLike);
|
|
12
|
-
select(preference: SearchProviderPreference): Promise<void>;
|
|
26
|
+
select(preference: SearchProviderPreference, options?: WebProviderSelectionOptions): Promise<void>;
|
|
13
27
|
private findWebEntry;
|
|
14
28
|
}
|
|
15
29
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"search-provider-switcher.d.ts","sourceRoot":"","sources":["../../../src/host/search-provider-switcher.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,mCAAmC,CAAA;AAG9D,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,wBAAwB,CAAA;AAEtE,UAAU,UAAU;IAClB,OAAO,IAAI,QAAQ,CAAC,KAAK,CAAC,CAAA;CAC3B;AAED,qBAAa,sBAAsB;IAKrB,OAAO,CAAC,QAAQ,CAAC,MAAM;IAJnC,OAAO,CAAC,sBAAsB,CAAoB;IAClD,OAAO,CAAC,qBAAqB,CAAoB;IACjD,OAAO,CAAC,WAAW,CAAQ;gBAEE,MAAM,EAAE,UAAU;IAEzC,MAAM,CAAC,UAAU,EAAE,wBAAwB,GAAG,OAAO,CAAC,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"search-provider-switcher.d.ts","sourceRoot":"","sources":["../../../src/host/search-provider-switcher.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,mCAAmC,CAAA;AAG9D,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,wBAAwB,CAAA;AAEtE,UAAU,UAAU;IAClB,OAAO,IAAI,QAAQ,CAAC,KAAK,CAAC,CAAA;CAC3B;AAED,qEAAqE;AACrE,MAAM,WAAW,2BAA2B;IAC1C;;;;;;;;;OASG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAA;CAC/B;AAED,qBAAa,sBAAsB;IAKrB,OAAO,CAAC,QAAQ,CAAC,MAAM;IAJnC,OAAO,CAAC,sBAAsB,CAAoB;IAClD,OAAO,CAAC,qBAAqB,CAAoB;IACjD,OAAO,CAAC,WAAW,CAAQ;gBAEE,MAAM,EAAE,UAAU;IAEzC,MAAM,CAAC,UAAU,EAAE,wBAAwB,EAAE,OAAO,GAAE,2BAAgC,GAAG,OAAO,CAAC,IAAI,CAAC;IAkC5G,OAAO,CAAC,YAAY;CAOrB"}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Host-side authorization for the models a subagent may run.
|
|
3
|
+
*
|
|
4
|
+
* The `subagent-model-selection` preference is sampled into each Session as a
|
|
5
|
+
* durable route allowlist, but the built-in delegation tool only rejects an
|
|
6
|
+
* explicit route outside that list: a call that selects no route lets the child
|
|
7
|
+
* inherit the parent's model, so a DeepSeek parent could spawn DeepSeek
|
|
8
|
+
* children even though the allowlist authorizes only other models.
|
|
9
|
+
*
|
|
10
|
+
* This guard closes that gap from the plugin plane. Whenever the calling
|
|
11
|
+
* Session (or the nearest ancestor that recorded one) carries an allowlist,
|
|
12
|
+
* a delegation whose effective child route the list does not authorize is
|
|
13
|
+
* denied before the child starts, with the authorized routes in the reason so
|
|
14
|
+
* the model can retry and pick one of them.
|
|
15
|
+
* @module dsh-chatgpt-subscription/subagent-model-authorization
|
|
16
|
+
*/
|
|
17
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
18
|
+
import type { SettingsProvider } from '@deepseek-ai/dsh-settings';
|
|
19
|
+
/** DSH settings namespace owned by the Subagent settings card. */
|
|
20
|
+
export declare const SUBAGENT_MODEL_SELECTION_NAMESPACE = "subagent-model-selection";
|
|
21
|
+
/** Durable Session event carrying one Session's authorized child routes. */
|
|
22
|
+
export declare const SUBAGENT_POLICY_EVENT = "subagent/model-selection-policy";
|
|
23
|
+
/** Delegation tools whose child routes this guard authorizes. */
|
|
24
|
+
export declare const DEFAULT_DELEGATION_TOOLS: readonly string[];
|
|
25
|
+
/** One exact provider/model route the user authorized for children. */
|
|
26
|
+
export interface AllowedModelRoute {
|
|
27
|
+
readonly provider: string;
|
|
28
|
+
readonly model: string;
|
|
29
|
+
}
|
|
30
|
+
/** Resolved model-selection preference read from the Host settings document. */
|
|
31
|
+
export interface SubagentModelSelectionPreference {
|
|
32
|
+
readonly enabled: boolean;
|
|
33
|
+
readonly allowedModels: readonly AllowedModelRoute[];
|
|
34
|
+
}
|
|
35
|
+
/** One durable Session record this module reads: its events and its lineage. */
|
|
36
|
+
export interface PolicySession {
|
|
37
|
+
eventAt?(seq: number): {
|
|
38
|
+
type?: unknown;
|
|
39
|
+
data?: unknown;
|
|
40
|
+
} | undefined;
|
|
41
|
+
readonly header?: {
|
|
42
|
+
readonly origin?: unknown;
|
|
43
|
+
readonly parentSession?: unknown;
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
/** Session lookup the guard walks from the calling agent to its ancestors. */
|
|
47
|
+
export interface SessionsResolver {
|
|
48
|
+
get(id: string): PolicySession | undefined;
|
|
49
|
+
}
|
|
50
|
+
/** Calling agent fields the guard reads. */
|
|
51
|
+
export interface AuthorizationAgent {
|
|
52
|
+
readonly session?: PolicySession;
|
|
53
|
+
readonly options?: {
|
|
54
|
+
readonly provider?: unknown;
|
|
55
|
+
readonly model?: unknown;
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
/** What the delegation tool received from the model. */
|
|
59
|
+
export interface DelegationArguments {
|
|
60
|
+
readonly provider?: unknown;
|
|
61
|
+
readonly model?: unknown;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Parse the allowlist out of a durable route-policy event.
|
|
65
|
+
* @param data - `subagent/model-selection-policy` event payload.
|
|
66
|
+
* @returns every well-formed route, or undefined when the payload carries none.
|
|
67
|
+
*/
|
|
68
|
+
export declare function parseAllowedRoutes(data: unknown): AllowedModelRoute[] | undefined;
|
|
69
|
+
/**
|
|
70
|
+
* Find one Session's recorded allowlist among its durable events.
|
|
71
|
+
* @param session - Session whose log is scanned.
|
|
72
|
+
* @returns the authorized routes, or undefined for a Session that recorded none.
|
|
73
|
+
*/
|
|
74
|
+
export declare function policyRoutesOf(session: PolicySession | undefined): AllowedModelRoute[] | undefined;
|
|
75
|
+
/**
|
|
76
|
+
* Resolve the allowlist a delegation from this agent must respect: the agent's
|
|
77
|
+
* own recorded policy, else the nearest ancestor Session that recorded one
|
|
78
|
+
* (the inheritance the delegation tool applies to child Sessions).
|
|
79
|
+
* @param agent - Calling agent.
|
|
80
|
+
* @param sessions - Session registry used for ancestor lookup.
|
|
81
|
+
* @returns the authorized routes, or undefined when no Session recorded any.
|
|
82
|
+
*/
|
|
83
|
+
export declare function authorizedRoutesFor(agent: AuthorizationAgent | undefined, sessions: SessionsResolver): AllowedModelRoute[] | undefined;
|
|
84
|
+
/**
|
|
85
|
+
* Read the Host preference that owns the allowlist. Values in the stored
|
|
86
|
+
* document are untrusted JSON, so every field is narrowed before use.
|
|
87
|
+
* @param settings - Live settings service, when composed.
|
|
88
|
+
* @returns the resolved preference, or undefined without a settings service.
|
|
89
|
+
*/
|
|
90
|
+
export declare function subagentModelSelectionPreference(settings: SettingsProvider | undefined): SubagentModelSelectionPreference | undefined;
|
|
91
|
+
/**
|
|
92
|
+
* Build the denial reason for a delegation that would run on an unauthorized
|
|
93
|
+
* route. The reason names the authorized routes so the next call can select one.
|
|
94
|
+
* @param provider - Effective child provider id, when one is known.
|
|
95
|
+
* @param model - Effective child model id, when one is known.
|
|
96
|
+
* @param allowed - Routes the calling Session authorizes.
|
|
97
|
+
* @param explicit - Whether the model named the route in the tool arguments.
|
|
98
|
+
* @returns the corrective reason handed back to the model.
|
|
99
|
+
*/
|
|
100
|
+
export declare function unauthorizedRouteReason(provider: string | undefined, model: string | undefined, allowed: readonly AllowedModelRoute[], explicit: boolean): string;
|
|
101
|
+
/** Which Session's recorded allowlist a delegation must respect. */
|
|
102
|
+
export type AuthorizationScope =
|
|
103
|
+
/**
|
|
104
|
+
* `session` (default): a Session that recorded an allowlist governs itself
|
|
105
|
+
* and its descendants, matching the delegation tool's durable snapshot; a
|
|
106
|
+
* Session that recorded none keeps the built-in behavior.
|
|
107
|
+
*/
|
|
108
|
+
'session'
|
|
109
|
+
/**
|
|
110
|
+
* `preference`: the current Host preference also governs Sessions that never
|
|
111
|
+
* recorded an allowlist, so the Settings card applies without restarting the
|
|
112
|
+
* Session. A Session that recorded its own routes still outranks it.
|
|
113
|
+
*/
|
|
114
|
+
| 'preference';
|
|
115
|
+
/**
|
|
116
|
+
* Decide whether one delegation call may start its child.
|
|
117
|
+
* @param agent - Calling agent.
|
|
118
|
+
* @param toolName - Tool being dispatched.
|
|
119
|
+
* @param args - Parsed tool arguments.
|
|
120
|
+
* @param preference - Current Host preference, when the settings service exists.
|
|
121
|
+
* @param sessions - Session registry used for ancestor lookup.
|
|
122
|
+
* @param toolNames - Delegation tool names this guard authorizes.
|
|
123
|
+
* @param scope - Whether an unrecorded Session falls back to the preference.
|
|
124
|
+
* @returns a denial reason, or undefined to leave the call untouched.
|
|
125
|
+
*/
|
|
126
|
+
export declare function delegationDenialReason(agent: AuthorizationAgent | undefined, toolName: string, args: unknown, preference: SubagentModelSelectionPreference | undefined, sessions: SessionsResolver, toolNames?: readonly string[], scope?: AuthorizationScope): string | undefined;
|
|
127
|
+
/** Runtime inputs the guard closes over. */
|
|
128
|
+
export interface SubagentAuthorizationOptions {
|
|
129
|
+
/** Live settings service, when composed; absent leaves recorded policies in charge. */
|
|
130
|
+
readonly settings?: SettingsProvider;
|
|
131
|
+
/** Session registry used for ancestor lookup. */
|
|
132
|
+
readonly sessions: SessionsResolver;
|
|
133
|
+
/** Exact delegation tool names this guard authorizes. */
|
|
134
|
+
readonly toolNames: readonly string[];
|
|
135
|
+
/** Whether an unrecorded Session falls back to the current preference. */
|
|
136
|
+
readonly scope?: AuthorizationScope;
|
|
137
|
+
}
|
|
138
|
+
/** The exact guard signature the Host tool registry evaluates. */
|
|
139
|
+
export type DelegationGuard = (agent: AuthorizationAgent | undefined, toolName: string, args: unknown) => string | undefined;
|
|
140
|
+
/**
|
|
141
|
+
* Build the monotonic tool guard for one deployment. The Host preference is
|
|
142
|
+
* sampled from the live settings document on every call, because a settings
|
|
143
|
+
* edit must not rebuild the guard the way it cannot rebuild a Session.
|
|
144
|
+
* @param options - settings, session registry, and delegation tool names.
|
|
145
|
+
* @returns a guard that denies delegations outside the Session allowlist.
|
|
146
|
+
*/
|
|
147
|
+
export declare function createSubagentAuthorization(options: SubagentAuthorizationOptions): DelegationGuard;
|
|
148
|
+
/** Delegation names this guard recognizes when configuration omits them. */
|
|
149
|
+
export declare function normalizeDelegationToolNames(value: unknown): string[];
|
|
150
|
+
/**
|
|
151
|
+
* Validate the plugin configuration that owns this guard.
|
|
152
|
+
* @param toolNames - Candidate delegation tool names.
|
|
153
|
+
* @returns the exact names this guard authorizes.
|
|
154
|
+
*/
|
|
155
|
+
export declare function validateDelegationToolNames(toolNames: readonly string[]): string[];
|
|
156
|
+
/** Configuration accepted by {@link installSubagentModelAuthorization}. */
|
|
157
|
+
export interface SubagentModelAuthorizationConfig {
|
|
158
|
+
/** Delegation tool names the guard authorizes. */
|
|
159
|
+
readonly toolNames?: readonly string[];
|
|
160
|
+
/** Whether an unrecorded Session falls back to the current preference. */
|
|
161
|
+
readonly scope?: AuthorizationScope;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Register the monotonic guard on the Host tool registry.
|
|
165
|
+
* @param ctx - Host context carrying `tools` (and optionally `settings`).
|
|
166
|
+
* @param sessions - Session registry used for ancestor lookup.
|
|
167
|
+
* @param config - Enforcement toggle and delegation tool names.
|
|
168
|
+
* @returns the exact disposer that unregisters the guard.
|
|
169
|
+
*/
|
|
170
|
+
export declare function installSubagentModelAuthorization(ctx: Context, sessions: SessionsResolver, config?: SubagentModelAuthorizationConfig): () => void;
|
|
171
|
+
/**
|
|
172
|
+
* Validate the configured authorization scope.
|
|
173
|
+
* @param scope - Candidate scope from deployment configuration.
|
|
174
|
+
* @returns the exact scope this guard enforces.
|
|
175
|
+
*/
|
|
176
|
+
export declare function validateAuthorizationScope(scope: unknown): AuthorizationScope;
|
|
177
|
+
//# sourceMappingURL=subagent-model-authorization.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"subagent-model-authorization.d.ts","sourceRoot":"","sources":["../../../src/host/subagent-model-authorization.ts"],"names":[],"mappings":"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"}
|
package/lib/types/index.d.ts
CHANGED
|
@@ -1,7 +1,27 @@
|
|
|
1
1
|
import type { Context } from '@deepseek-ai/cordis';
|
|
2
|
+
import z from '@deepseek-ai/schemastery';
|
|
3
|
+
/** Optional deployment configuration for this plugin. */
|
|
4
|
+
export interface Config {
|
|
5
|
+
/**
|
|
6
|
+
* Whether the Subagent model allowlist also governs the route a delegation
|
|
7
|
+
* that selects no model would inherit from its parent. Default `true`; set
|
|
8
|
+
* `false` to leave inherited routes to the built-in delegation tool.
|
|
9
|
+
*/
|
|
10
|
+
subagentModelAuthorization?: boolean;
|
|
11
|
+
/** Delegation tool names the authorization guard recognizes (default `subagent`). */
|
|
12
|
+
subagentModelTools?: string[];
|
|
13
|
+
/**
|
|
14
|
+
* `session` (default) enforces the allowlist a Session recorded, matching the
|
|
15
|
+
* delegation tool's snapshot; `preference` also enforces the current Settings
|
|
16
|
+
* card allowlist for Sessions that recorded none.
|
|
17
|
+
*/
|
|
18
|
+
subagentModelScope?: 'session' | 'preference';
|
|
19
|
+
}
|
|
20
|
+
export declare const Config: z<Config>;
|
|
2
21
|
export declare const inject: string[];
|
|
3
|
-
export declare function apply(ctx: Context): void;
|
|
22
|
+
export declare function apply(ctx: Context, pluginConfig?: Config): void;
|
|
4
23
|
export { ProxyManager, detectSystemProxy } from './host/proxy-manager.ts';
|
|
24
|
+
export { SUBAGENT_MODEL_SELECTION_NAMESPACE, SUBAGENT_POLICY_EVENT, authorizedRoutesFor, createSubagentAuthorization, delegationDenialReason, installSubagentModelAuthorization, normalizeDelegationToolNames, parseAllowedRoutes, policyRoutesOf, subagentModelSelectionPreference, unauthorizedRouteReason, validateAuthorizationScope, validateDelegationToolNames, } from './host/subagent-model-authorization.ts';
|
|
5
25
|
export { OAuthService } from './host/oauth-service.ts';
|
|
6
26
|
export { CodexChatGptAdapter } from './host/adapter.ts';
|
|
7
27
|
export { createCodexImageTool } from './host/codex-images.ts';
|
package/lib/types/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,CAAC,MAAM,0BAA0B,CAAA;AAsCxC,yDAAyD;AACzD,MAAM,WAAW,MAAM;IACrB;;;;OAIG;IACH,0BAA0B,CAAC,EAAE,OAAO,CAAA;IACpC,qFAAqF;IACrF,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAA;IAC7B;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,SAAS,GAAG,YAAY,CAAA;CAC9C;AAED,eAAO,MAAM,MAAM,EAAE,CAAC,CAAC,MAAM,CAI3B,CAAA;AAEF,eAAO,MAAM,MAAM,UAAqE,CAAA;AAExF,wBAAgB,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,YAAY,GAAE,MAAW,GAAG,IAAI,CAsGnE;AAED,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAA;AACzE,OAAO,EACL,kCAAkC,EAClC,qBAAqB,EACrB,mBAAmB,EACnB,2BAA2B,EAC3B,sBAAsB,EACtB,iCAAiC,EACjC,4BAA4B,EAC5B,kBAAkB,EAClB,cAAc,EACd,gCAAgC,EAChC,uBAAuB,EACvB,0BAA0B,EAC1B,2BAA2B,GAC5B,MAAM,wCAAwC,CAAA;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAA;AACtD,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAA;AACvD,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAA;AAC7D,OAAO,EAAE,yBAAyB,EAAE,MAAM,wBAAwB,CAAA;AAClE,OAAO,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAA;AAChE,OAAO,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAA;AAClF,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAA;AACtF,OAAO,EAAE,wBAAwB,EAAE,MAAM,gCAAgC,CAAA;AACzE,OAAO,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAA;AACnE,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAA;AACjE,OAAO,EAAE,sBAAsB,EAAE,MAAM,+BAA+B,CAAA;AACtE,YAAY,EAAE,UAAU,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAA;AAE/E,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAA;AAClE,OAAO,EACL,mBAAmB,EACnB,sBAAsB,EACtB,cAAc,EACd,iBAAiB,GAClB,MAAM,mCAAmC,CAAA;AAC1C,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,uBAAuB,EAAE,MAAM,6BAA6B,CAAA;AAClG,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAA"}
|