@gemus/mcp-proxy 0.1.5 → 0.1.6

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gemus/mcp-proxy",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
4
4
  "description": "Local stdio<->HTTP MCP proxy for Codex desktop: transparent passthrough + execute tap + imagegen backfill (Issue #1751, P1).",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,11 +1,32 @@
1
1
  import { describe, expect, it } from 'vitest'
2
+ import { CLIENT_CAPABILITIES } from '@gemus/codex-backfill-core'
2
3
  import { buildProxyUpstreamHeaders } from '../mcpHeaders.mjs'
3
4
 
4
5
  describe('buildProxyUpstreamHeaders', () => {
5
- it('advertises the canonical Codex MCP capabilities alongside bearer authentication', () => {
6
+ it('advertises only the capabilities the proxy can actually fulfil', () => {
6
7
  expect(buildProxyUpstreamHeaders('mak_test123')).toEqual({
7
8
  Authorization: 'Bearer mak_test123',
8
9
  'X-Gemus-Client-Capabilities': 'agent-authored-prompts,codex-imagen',
9
10
  })
10
11
  })
12
+
13
+ it('never advertises codex-instant-delivery — the proxy has no ImageScheduler to renew the lease (#2049)', () => {
14
+ // 该能力门控服务端的 AWAITING_CLIENT execute 分支,要求客户端有 scheduler 每 60s renewLease。
15
+ // 只有 bridge 有(且 probe 门控,见 bridge/src/cli.ts)。proxy 没有 —— 发了它就等于承诺一个
16
+ // 永不到来的续租 → 5min 后 CLIENT_LEASE_EXPIRED → 图 orphan 成孤立 image-upload 节点。
17
+ // absent → 服务端回退经典两阶段 client-delivered 协议,proxy 完全支持(backfill.mjs)。
18
+ const header = buildProxyUpstreamHeaders('mak_test123')['X-Gemus-Client-Capabilities']
19
+ expect(header).not.toContain('codex-instant-delivery')
20
+ })
21
+
22
+ it('stays a strict subset of the shared allowlist — a new capability must not auto-inherit (#2049)', () => {
23
+ // 回归门:本 bug 的成因正是 proxy 发「整个 CLIENT_CAPABILITIES 数组」—— #1969 往数组里加了
24
+ // codex-instant-delivery,proxy 就自动继承了一个它做不到的承诺,而 #1990 把红掉的断言改成
25
+ // 迎合它。此断言让「加能力」与「proxy 声称支持它」变成两个独立决策,不再自动传染。
26
+ const advertised = buildProxyUpstreamHeaders('mak_test123')['X-Gemus-Client-Capabilities'].split(',')
27
+ for (const capability of advertised) {
28
+ expect(CLIENT_CAPABILITIES).toContain(capability)
29
+ }
30
+ expect(advertised.length).toBeLessThan(CLIENT_CAPABILITIES.length)
31
+ })
11
32
  })
@@ -4,9 +4,36 @@ import {
4
4
  serializeClientCapabilities,
5
5
  } from '@gemus/codex-backfill-core'
6
6
 
7
+ /**
8
+ * #2049:proxy 只 advertise **它自己能履行**的能力 —— 不是 `CLIENT_CAPABILITIES` 全集。
9
+ *
10
+ * `CLIENT_CAPABILITIES` 是**所有已知能力的 allowlist**(共享 SSOT),不是「每个客户端都支持」的清单。
11
+ * 各客户端各自声明自己能兑现的子集:bridge 见 `bridge/src/cli.ts`(`imageScheduler` 与
12
+ * `activeCapabilities` **成对注入** —— 有 scheduler 才发 capability,且 probe 门控)。
13
+ *
14
+ * **`codex-instant-delivery` 必须排除**:它门控服务端的 AWAITING_CLIENT execute 分支,
15
+ * 要求客户端有 ImageScheduler 每 60s `renewLease`。**proxy 没有 scheduler**(本包与
16
+ * `@gemus/codex-backfill-core` 里 `renew`/`lease` 零命中)。发了它 = 承诺一个永不到来的续租:
17
+ * execute 建 AWAITING_CLIENT job(lease=+5min)→ 无人续 → `CLIENT_LEASE_EXPIRED` → 节点 FAILED
18
+ * → Codex 的图回来时目标已死 → `backfill.mjs` orphan 兜底 → 孤立 `image-upload` 节点。
19
+ * 真机实测见 #2049。
20
+ * **不发它 → 服务端回退经典两阶段 client-delivered 协议,proxy 完全支持**(`backfill.mjs`
21
+ * tap execute + 注入交付)—— 这正是 `codex-backfill-core/src/mcp.ts` 那条注释写的
22
+ * "absent → the classic dryRun/Phase-2 two-phase client-delivered protocol is used unchanged"。
23
+ *
24
+ * ⚠️ **往 `CLIENT_CAPABILITIES` 加新能力时**:默认**不会**、也**不该**自动出现在这里。先问
25
+ * 「proxy 拿什么兑现它?」—— 本 bug 的成因正是「发全集」让新能力自动传染(#1969 加,proxy 白捡,
26
+ * #1990 把红掉的断言改成迎合)。`__tests__/mcpHeaders.test.ts` 有严格子集断言守着这条。
27
+ */
28
+ const PROXY_UNSUPPORTED_CAPABILITIES = new Set(['codex-instant-delivery'])
29
+
30
+ const PROXY_CAPABILITIES = Object.freeze(
31
+ CLIENT_CAPABILITIES.filter((capability) => !PROXY_UNSUPPORTED_CAPABILITIES.has(capability)),
32
+ )
33
+
7
34
  export function buildProxyUpstreamHeaders(apiKey) {
8
35
  return {
9
36
  Authorization: `Bearer ${apiKey}`,
10
- [CLIENT_CAPABILITIES_HEADER]: serializeClientCapabilities(CLIENT_CAPABILITIES),
37
+ [CLIENT_CAPABILITIES_HEADER]: serializeClientCapabilities(PROXY_CAPABILITIES),
11
38
  }
12
39
  }