@gemus/mcp-proxy 0.1.4 → 0.1.5
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 +2 -2
- package/src/__tests__/backfill.test.ts +39 -1
- package/src/backfill.mjs +20 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gemus/mcp-proxy",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
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": {
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
28
|
-
"@gemus/codex-backfill-core": "0.1.
|
|
28
|
+
"@gemus/codex-backfill-core": "0.1.3"
|
|
29
29
|
},
|
|
30
30
|
"engines": {
|
|
31
31
|
"node": ">=18"
|
|
@@ -74,7 +74,7 @@ beforeEach(() => {
|
|
|
74
74
|
mocks.readRollout.mockReset().mockReturnValue('')
|
|
75
75
|
mocks.findRolloutFile.mockReset().mockReturnValue('/fake/rollout.jsonl')
|
|
76
76
|
// Default: generated_images unavailable → backfill falls back to the rollout source (existing tests).
|
|
77
|
-
mocks.readGeneratedImageFiles.mockReset().mockReturnValue({ available: false, images: [] })
|
|
77
|
+
mocks.readGeneratedImageFiles.mockReset().mockReturnValue({ available: false, images: [], failedCallIds: [] })
|
|
78
78
|
})
|
|
79
79
|
|
|
80
80
|
describe('normalizeToolName (tap → tracker)', () => {
|
|
@@ -353,4 +353,42 @@ describe('backfillTurn — Layer 2 file byte source (#1756)', () => {
|
|
|
353
353
|
expect(mocks.readRollout).toHaveBeenCalledWith('/x')
|
|
354
354
|
expect(r.delivered).toBe(1)
|
|
355
355
|
})
|
|
356
|
+
|
|
357
|
+
it('exec-* file-source image (opaque call-id) is delivered exactly once (#1968)', async () => {
|
|
358
|
+
const bf = makeBackfiller()
|
|
359
|
+
observeExecuteAnchor(bf, 1, 't1', 's1', 'wf_real', 'nodeA')
|
|
360
|
+
mocks.readGeneratedImageFiles.mockReturnValue({
|
|
361
|
+
available: true,
|
|
362
|
+
images: [{ callId: 'exec-3eb2a29c', base64: 'iVBORw0KGgoAAAA1', mimeType: 'image/png' }],
|
|
363
|
+
failedCallIds: [],
|
|
364
|
+
})
|
|
365
|
+
|
|
366
|
+
const r = await bf.backfillTurn({ turnKey: 't1', sessionId: 's1', rolloutPath: '/x', salvageOnly: false })
|
|
367
|
+
|
|
368
|
+
expect(mocks.readRollout).not.toHaveBeenCalled()
|
|
369
|
+
expect(r.delivered).toBe(1)
|
|
370
|
+
expect(mocks.deliverImageToNode).toHaveBeenCalledTimes(1)
|
|
371
|
+
expect(mocks.deliverImageToNode.mock.calls[0][0]).toMatchObject({
|
|
372
|
+
nodeId: 'nodeA',
|
|
373
|
+
image: { callId: 'exec-3eb2a29c' },
|
|
374
|
+
})
|
|
375
|
+
})
|
|
376
|
+
|
|
377
|
+
it('unreadable fresh candidate is recovered from rollout by EXACT call-id only — no history bypass (#1968)', async () => {
|
|
378
|
+
const bf = makeBackfiller()
|
|
379
|
+
observeExecuteAnchor(bf, 1, 't1', 's1', 'wf_real', 'nodeA')
|
|
380
|
+
// File source: nothing readable, but one fresh candidate failed to read (torn write).
|
|
381
|
+
mocks.readGeneratedImageFiles.mockReturnValue({ available: true, images: [], failedCallIds: ['exec-torn'] })
|
|
382
|
+
// Rollout holds the failed id AND an unrelated historical image that must NOT be surfaced.
|
|
383
|
+
mocks.readRollout.mockReturnValue(rollout([['exec-torn', 'iVBORw0KGgoAAAA9'], ['ig_history', 'iVBORw0KGgoAAAA8']]))
|
|
384
|
+
|
|
385
|
+
const r = await bf.backfillTurn({ turnKey: 't1', sessionId: 's1', rolloutPath: '/x', salvageOnly: false })
|
|
386
|
+
|
|
387
|
+
expect(mocks.readRollout).toHaveBeenCalledWith('/x')
|
|
388
|
+
expect(r.delivered).toBe(1)
|
|
389
|
+
expect(mocks.deliverImageToNode).toHaveBeenCalledTimes(1)
|
|
390
|
+
expect(mocks.deliverImageToNode.mock.calls[0][0]).toMatchObject({ image: { callId: 'exec-torn' } })
|
|
391
|
+
// The historical image was in the rollout but is NOT in failedCallIds → never delivered or orphaned.
|
|
392
|
+
expect(mocks.addImageToCanvas).not.toHaveBeenCalled()
|
|
393
|
+
})
|
|
356
394
|
})
|
package/src/backfill.mjs
CHANGED
|
@@ -137,17 +137,33 @@ export function createBackfiller({ server, apiKey, codexHome, call, log = () =>
|
|
|
137
137
|
async function backfillTurnInner({ turnKey, sessionId, rolloutPath, salvageOnly }) {
|
|
138
138
|
const t = turns.get(turnKey)
|
|
139
139
|
const sid = sessionId || t?.sessionId
|
|
140
|
-
// #1756 Layer 2: 优先读 generated_images 落盘文件(去 rollout-scrape
|
|
141
|
-
//
|
|
142
|
-
//
|
|
140
|
+
// #1756 Layer 2: 优先读 generated_images 落盘文件(去 rollout-scrape 脆化)。仅目录整体不可用
|
|
141
|
+
// (旧 codex 不落盘)才整源回退 rollout;目录可用即锁定文件源,否则会绕过 mtime 守卫把历史图从
|
|
142
|
+
// rollout 重新捞出重复交付。#1968:文件源里「已确认 fresh 但读失败」的候选(torn/locked write)
|
|
143
|
+
// 按精确 call-id 定向从 rollout 恢复——只捞这几个 id,不换整源、不越 mtime 守卫。
|
|
143
144
|
const fileResult = sid
|
|
144
145
|
? core.readGeneratedImageFiles(codexHome, sid, procStartMs)
|
|
145
|
-
: { available: false, images: [] }
|
|
146
|
+
: { available: false, images: [], failedCallIds: [] }
|
|
146
147
|
let all
|
|
147
148
|
let source
|
|
148
149
|
if (fileResult.available) {
|
|
149
150
|
all = fileResult.images
|
|
150
151
|
source = 'generated_images'
|
|
152
|
+
const failedCallIds = fileResult.failedCallIds || []
|
|
153
|
+
if (failedCallIds.length) {
|
|
154
|
+
const path = rolloutPath || core.findRolloutFile(codexHome, sid)
|
|
155
|
+
const want = new Set(failedCallIds)
|
|
156
|
+
const recovered = core.extractImageOutputs(core.readRollout(path)).filter((img) => want.has(img.callId))
|
|
157
|
+
if (recovered.length) {
|
|
158
|
+
all = all.concat(recovered)
|
|
159
|
+
source = `${source}+rollout`
|
|
160
|
+
}
|
|
161
|
+
log('backfill: unreadable generated_images candidates', {
|
|
162
|
+
turnKey,
|
|
163
|
+
failed: failedCallIds.length,
|
|
164
|
+
recovered: recovered.length,
|
|
165
|
+
})
|
|
166
|
+
}
|
|
151
167
|
} else {
|
|
152
168
|
const path = rolloutPath || core.findRolloutFile(codexHome, sid)
|
|
153
169
|
all = core.extractImageOutputs(core.readRollout(path))
|