docs: clarify retained proxy and execution-world requirements

This commit is contained in:
Tianyi Cui committed 2026-09-11 22:35:08 +08:00
1 parent c49db8bc8c
commit 63a16e254d
9 files changed
+15 -23

No files matched your search

@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write .agents/notes/implemented/architecture/2026-07-28-portable-execution-world-consumers.md
2026-07-28-portable-execution-world-consumers.md: 74430f18a344aede8c64d3ae0696271092ede9f8
2026-07-28-portable-execution-world-consumers.zh.md: 67ca507ed67e744a8ade676442446806377dab64
2026-07-28-portable-execution-world-consumers.md: 3375a38d41724aee9b3f7591b014d6eea6979857
2026-07-28-portable-execution-world-consumers.zh.md: 62d7338d237358b05c0c52f97720312474b74717
@@ -56,7 +56,7 @@ The local filesystem, subprocess, terminal and LSP suites cover path identity, e
**Implement remote filesystem operations only through shell commands.** Rejected because that discards structured filesystem identity, errors, streaming, version guards, and atomic mutation semantics already consumed by the file tools.
**Add a generic distributed-runtime abstraction or reconnect live handles.** Rejected because the existing capability seams carry the demonstrated contracts, while remote identity alone cannot reconstruct callbacks, pending promises, authority, protocol state, or output cursors. A new layer would speculate about persistence and synchronization beyond the POC.
**Add a generic distributed-runtime abstraction or reconnect live handles.** Rejected because the existing capability seams carry the demonstrated contracts, while remote identity alone cannot reconstruct callbacks, pending promises, authority, protocol state, or output cursors. A new layer would speculate about persistence and synchronization beyond the demonstrated consumer contracts.
## Consequences
@@ -56,7 +56,7 @@ Status: implemented
**只通过 shell 命令实现远程文件系统操作。** 不予采纳,因为这会丢弃现有文件工具已消费的结构化文件系统身份、错误、流式输出、版本保护和原子变更语义。
**新增通用分布式运行时抽象,或重新连接活跃句柄。** 不予采纳,因为现有能力 seam 已承载经证实的约定,而仅凭远程身份无法重建回调、待处理 promise、权限、协议状态或输出游标。新增一层只会推测 POC 边界之外的持久化与同步问题。
**新增通用分布式运行时抽象,或重新连接活跃句柄。** 不予采纳,因为现有能力 seam 已承载经证实的约定,而仅凭远程身份无法重建回调、待处理 promise、权限、协议状态或输出游标。新增一层只会推测 已验证消费方约定之外的持久化与同步问题。
## 后果
@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write .agents/notes/implemented/architecture/2026-08-27-outbound-proxy-policy.md
2026-08-27-outbound-proxy-policy.md: 355cd9638b0fb78fc230848afd545d24e17911ef
2026-08-27-outbound-proxy-policy.zh.md: 462b3de85e68c3404cac03da822b1ec3c50c4e6c
2026-08-27-outbound-proxy-policy.md: 015b6edd3f4153f29db4084c92e8e8da8cad70e2
2026-08-27-outbound-proxy-policy.zh.md: c7bdcaee138ac41a55733d76da1063b25f9ba633
@@ -24,9 +24,7 @@ An earlier revision put it in a new `net/` package group, reasoning that dependi
The plugin that revision shipped is gone with it. It let a composition declare the policy in `cordis.yml`, but no shipped bundle mounted it, so the launcher's path was the only reachable one — and its `Config` was the sole supplier of a configuration branch nothing else could reach.
**One operation per caller need.** `installProxyFromEnvironment`, `proxyRouteFor`, `proxyEnvironmentForChild`, and `clearedProxyEnv` cover installation, per-request routing, child inheritance and fixture isolation. SDK-specific factories would expose individual transport constraints through the shared API. `web-fetch-http` consumes the resolved route when constructing its pinning transport; the OTLP exporter remains direct.
What remains is `installProxyFromEnvironment`, `proxyRouteFor`, `proxyEnvironmentForChild`, and `clearedProxyEnv` — one per way a caller can need the policy, none per SDK. Installation absorbed resolution and diagnostic reporting, which no caller needed apart: a resolved policy that is not installed routes nothing.
**One operation per caller need.** `installProxyFromEnvironment`, `proxyRouteFor`, `proxyEnvironmentForChild`, and `clearedProxyEnv` cover installation, per-request routing, child inheritance and fixture isolation. SDK-specific factories would expose individual transport constraints through the shared API. `web-fetch-http` consumes the resolved route when constructing its pinning transport; the OTLP exporter remains direct. Installation includes resolution and diagnostic reporting because callers need one operation that resolves and installs routing.
`proxyRouteFor` also closes a defect the old accessor made expressible. `web-fetch-http` read the policy to decide whether to pin, then read it again to build a transport; an unmount between the two returned a direct, unpinned agent for a URL the first read had cleared as proxied. A route carries both, so the branch and the request cannot disagree. Its dispatcher is the process-wide one, closed rather than destroyed on disposal, so a request already in flight when a policy is unmounted still finishes.
@@ -6,7 +6,7 @@ Status: implemented
## Problem
Node 内置的 `fetch` 会忽略 `HTTP_PROXY` 与 `HTTPS_PROXY`。开发者运行的其他工具——curl、git、npm、pip——都遵循它们,所以代理后面的用户导出一次变量就期待一切随之生效。Harness 并没有:`setGlobalDispatcher`、`ProxyAgent` 与 `EnvHttpProxyAgent` 在 `packages/` 与 `apps/` 中出现次数为零,因此模型请求、每次 web 搜索、`web_fetch`、走 HTTP 的 MCP与 OTLP 导出器 全部直连,且是静默的,任何地方都没有诊断。
Node 内置的 `fetch` 会忽略 `HTTP_PROXY` 与 `HTTPS_PROXY`。开发者运行的其他工具——curl、git、npm、pip——都遵循它们,所以代理后面的用户导出一次变量就期待一切随之生效。Harness 并没有:`setGlobalDispatcher`、`ProxyAgent` 与 `EnvHttpProxyAgent` 在 `packages/` 与 `apps/` 中出现次数为零,因此模型请求、每次 web 搜索、`web_fetch`、走 HTTP 的 MCP 与 OTLP 导出器全部直连,且是静默的,任何地方都没有诊断。
仓库曾短暂拥有过答案,又在无人察觉时弄丢了。PR #971 在 `bin/dsh` 里设置了 `NODE_USE_ENV_PROXY=1`;十一天后 `bbb1b1cc38 cleanup: remove managed source installer` 整体删除了那个启动器,把该标志一并带走。留下的只有 `apps/cli/reference/README.md` 里的一句话,让读者去设置一个已经无人消费的变量。
@@ -24,9 +24,7 @@ Node 内置的 `fetch` 会忽略 `HTTP_PROXY` 与 `HTTPS_PROXY`。开发者运
那次修订一并引入的插件也随之删除。它让某个组合可以把策略写进 `cordis.yml`,但没有任何随附 bundle 挂载它,因此启动器那条路径是唯一可达的——而它的 `Config` 是那条配置分支唯一的供给方,别处无从到达。
**每种调用需求对应一项操作。** `installProxyFromEnvironment`、`proxyRouteFor`、`proxyEnvironmentForChild` 与 `clearedProxyEnv` 分别负责安装、逐请求路由、子进程继承和 fixture 隔离。特定于 SDK 的工厂会通过共享 API 暴露各自的传输约束。`web-fetch-http` 在构造地址固定传输时使用已解析路由;OTLP 导出器保持直连。
剩下的是 `installProxyFromEnvironment`、`proxyRouteFor`、`proxyEnvironmentForChild` 与 `clearedProxyEnv`——按「调用方需要策略的方式」各一个,而不是按 SDK 各一个。安装吸收了解析与诊断上报,因为没有调用方需要把它们分开:解析出来却不安装的策略什么也路由不了。
**每种调用需求对应一项操作。** `installProxyFromEnvironment`、`proxyRouteFor`、`proxyEnvironmentForChild` 与 `clearedProxyEnv` 分别负责安装、逐请求路由、子进程继承和 fixture 隔离。特定于 SDK 的工厂会通过共享 API 暴露各自的传输约束。`web-fetch-http` 在构造地址固定传输时使用已解析路由;OTLP 导出器保持直连。安装包含解析与诊断上报,因为调用方需要一项同时解析并安装路由的操作。
`proxyRouteFor` 还堵掉了旧访问器让人写得出来的一个缺陷。`web-fetch-http` 先读策略决定是否 pin,再读一次去构造传输;两次读取之间发生卸载,就会为第一次读取已判定走代理的 URL 返回一个直连且未 pin 的 agent。路由把两者一起交出,分支与请求便无从分歧。它携带的是进程级 dispatcher,dispose 时是 close 而非 destroy,因此策略被卸载时已经发出的请求仍会跑完。
+2 -2
View File
@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write packages/util/http-proxy/README.md
README.md: fc0118a7a48b4ddea80bf7049ac1b44e9ba9268a
README.zh.md: 648d964bb53c51ae640ebc315bdf47095f77ed43
README.md: 66c0b026ee8b26f23fd39a25fd7e4adc44077dbe
README.zh.md: c3ee9fc5815b754027c54648d527c2f34fdc96c0
+2 -4
View File
@@ -29,7 +29,7 @@ Nothing to mount, and nothing to configure. The `dsh` launcher resolves and inst
### Writing a new outbound call
Plain `fetch()` is proxied, and so is any SDK that reaches `globalThis.fetch` — the MCP HTTP transport and the pi-ai provider stack both do. An SDK that builds its own transport is outside that dispatcher: the OTLP exporter posts through `node:http`. Verify each SDK's actual transport.
Plain `fetch()` is proxied, and so is any SDK that reaches `globalThis.fetch` — the MCP HTTP transport and the pi-ai provider stack both do. Verify each SDK's actual transport; exceptions belong under [Known Limitations and Deferred Work](#known-limitations-and-deferred-work).
| You are writing | Use |
|---|---|
@@ -41,11 +41,9 @@ Plain `fetch()` is proxied, and so is any SDK that reaches `globalThis.fetch`
`proxyRouteFor` answers with the transport that answer assumed, not just the answer: its proxied arm carries the dispatcher already routing by this policy. A caller that read the policy and then built its own transport could have an unmount land between the two and send the request somewhere its branch never cleared.
An SDK that builds its own transport reaches none of this. The OTLP telemetry exporter posts through `node:http` and stays direct; see the limitation below.
Constructing `new Agent(...)` and passing it as `dispatcher` overrides the global one and silently bypasses the proxy. `verify-no-bare-dispatcher` rejects that outside this package. One call site legitimately owns its transport — `web-fetch-http` pins a request to addresses it validated, which is per-request state a process-wide dispatcher cannot hold — and says so with a `proxy-exempt:` comment on the line.
That gate cannot see inside an SDK, so each outbound call site carries an `egress.spec.ts` that drives its actual transport through a fake proxy and checks the observed route. Telemetry asserts its direct-route exception. These tests detect dependency changes that alter routing without changing the call site.
That gate cannot see inside an SDK, so each outbound call site carries an `egress.spec.ts` that drives its actual transport through a fake proxy and checks the observed route. Every new outbound call site must include that transport test. Telemetry asserts its direct-route exception. These tests detect dependency changes that alter routing without changing the call site.
### What the policy reads
+2 -4
View File
@@ -29,7 +29,7 @@ kind: "package-reference"
### 编写新的出站调用
普通 `fetch()` 会走代理,任何使用 `globalThis.fetch` 的 SDK 也一样——MCP HTTP 传输与 pi-ai 提供方栈都使用它。自建传输的 SDK 不经过该 dispatcher:OTLP 导出器通过 `node:http` 投递。应验证每个 SDK 的实际传输。
普通 `fetch()` 会走代理,任何使用 `globalThis.fetch` 的 SDK 也一样——MCP HTTP 传输与 pi-ai 提供方栈都使用它。应验证每个 SDK 的实际传输;例外见[已知限制与延后工作](#known-limitations-and-deferred-work)。
| 你要写的东西 | 使用 |
|---|---|
@@ -41,11 +41,9 @@ kind: "package-reference"
`proxyRouteFor` 给出的不只是答案,还有该答案所假定的传输:走代理的那一支携带着此刻正按该策略路由的 dispatcher。若调用方先读策略、再自建传输,卸载就可能落在两次读取之间,把请求发往其分支从未放行的去处。
自建传输的 SDK 接触不到上述任何一条。OTLP 遥测导出器通过 `node:http` 投递并保持直连;见下方限制。
构造 `new Agent(...)` 再作为 `dispatcher` 传入会覆盖全局 dispatcher,从而静默绕开代理。`verify-no-bare-dispatcher` 会在本包之外拒绝该写法。有一处调用点确实自有传输——`web-fetch-http` 会把请求钉在它已校验过的地址上,而这是进程级 dispatcher 无法承载的单次请求状态——它在该行用 `proxy-exempt:` 注释说明。
该门禁看不进 SDK 内部,因此每个出网点都配有 `egress.spec.ts`,通过假代理驱动实际传输并检查观察到的路由。遥测断言其直连例外。这些测试可发现调用点不变但依赖变更改变路由的情况。
该门禁看不进 SDK 内部,因此每个出网点都配有 `egress.spec.ts`,通过假代理驱动实际传输并检查观察到的路由。每个新的出网点必须包含该传输测试。遥测断言其直连例外。这些测试可发现调用点不变但依赖变更改变路由的情况。
### 策略读取哪些值