Merge branch 'codex/invariant-package-registration-gate' into codex/package-invariant-checks

This commit is contained in:
Tianyi Cui
2026-07-20 19:55:46 +08:00
398 changed files with 15485 additions and 8603 deletions
+1 -1
View File
@@ -37,7 +37,7 @@ DeepSeek returns no provider-generated answer surface this provider trusts as `c
Results are deduplicated by URL because one request may surface the same page across searches. DeepSeek exposes `maxUses`, not a result-count knob, so the seam enforces `maxResults` by truncating `sources[]` and setting `truncated`.
Provider failures become `WEB_PROVIDER_ERROR`; caller cancellation becomes `WEB_ABORTED`.
Provider failures become `WEB_PROVIDER_ERROR`; caller cancellation becomes `WEB_ABORTED`. HTTP redirects are rejected before the `Location` target is contacted and surface as `WEB_PROVIDER_ERROR`.
## Model Experience
@@ -127,7 +127,7 @@ export function mapAnthropicResponse(response: AnthropicResponse): WebSearchResu
return { sources, truncated: false }
}
/** The DeepSeek-backed search provider. */
/** The DeepSeek-backed search provider; HTTP redirects fail as `WEB_PROVIDER_ERROR`. */
export class DeepSeekSearchProvider implements WebSearchProvider {
readonly id = DEEPSEEK_PROVIDER_ID
@@ -145,6 +145,7 @@ export class DeepSeekSearchProvider implements WebSearchProvider {
try {
response = await fetch(`${this.options.baseURL}/messages`, {
method: 'POST',
redirect: 'error',
headers: {
// Official DeepSeek expects `x-api-key`; an Anthropic-compatible proxy
// may expect `Authorization: Bearer` — send both so either resolves.
@@ -162,6 +162,7 @@ describe('DeepSeekSearchProvider request mapping', () => {
await new DeepSeekSearchProvider(options).search({ query: 'hello' })
const [url, init] = fetchMock.mock.calls[0] as unknown as [string, RequestInit]
expect(url).toBe('https://api.deepseek.test/anthropic/v1/messages')
expect(init).toMatchObject({ method: 'POST', redirect: 'error' })
const headers = init.headers as Record<string, string>
expect(headers['x-api-key']).toBe('ds-key')
expect(headers['authorization']).toBe('Bearer ds-key')
@@ -0,0 +1,123 @@
/**
* Real HTTP coverage proves whether native `fetch` contacts a cross-origin `Location`; mocked
* request-init assertions alone cannot observe that boundary.
*/
import { afterAll, beforeAll, describe, expect, it } from 'vitest'
import { createServer, type IncomingMessage, type Server } from 'node:http'
import type { AddressInfo } from 'node:net'
import { DeepSeekSearchProvider } from '@deepseek-ai/dsh-web-search-deepseek'
const TEST_API_KEY = 'redirect-test-key'
const TEST_QUERY = 'private redirect query'
const targetRequests: ReceivedRequest[] = []
interface ReceivedRequest {
readonly body: string
readonly headers: IncomingMessage['headers']
readonly method?: string
}
let redirectOrigin: string
let targetOrigin: string
const targetServer = createServer((request, response) => {
void captureRequest(request).then((received) => {
targetRequests.push(received)
response.writeHead(204).end()
}, (error: unknown) => response.destroy(asError(error)))
})
const redirectServer = createServer((request, response) => {
request.resume()
const status = Number(new URL(request.url ?? '/', 'http://fixture.test').pathname.split('/')[1])
response.writeHead(status, { location: `${targetOrigin}/collect` }).end()
})
beforeAll(async () => {
targetOrigin = await listen(targetServer)
redirectOrigin = await listen(redirectServer)
})
afterAll(async () => {
await Promise.all([close(redirectServer), close(targetServer)])
})
describe('DeepSeekSearchProvider redirect policy', () => {
it.each([301, 302, 303, 307, 308])('rejects HTTP %i before contacting Location', async (status) => {
targetRequests.length = 0
const provider = new DeepSeekSearchProvider({
apiKey: TEST_API_KEY,
baseURL: `${redirectOrigin}/${status}`,
model: 'deepseek-chat',
apiVersion: '2023-06-01',
maxTokens: 32,
maxUses: 1,
})
await expect(provider.search({ query: TEST_QUERY }))
.rejects.toMatchObject({ code: 'WEB_PROVIDER_ERROR' })
expect(targetRequests).toHaveLength(0)
})
it('shows default 307 following forwards the custom credential and POST body', async () => {
targetRequests.length = 0
const body = JSON.stringify({ query: TEST_QUERY })
await fetch(`${redirectOrigin}/307`, {
method: 'POST',
headers: {
'x-api-key': TEST_API_KEY,
'authorization': `Bearer ${TEST_API_KEY}`,
'content-type': 'application/json',
},
body,
})
expect(targetRequests).toHaveLength(1)
expect(targetRequests[0]).toMatchObject({ method: 'POST', body })
expect(targetRequests[0]?.headers['x-api-key']).toBe(TEST_API_KEY)
})
})
/** Read a complete request received by the redirect target. */
function captureRequest(request: IncomingMessage): Promise<ReceivedRequest> {
return new Promise((resolve, reject) => {
const chunks: Uint8Array[] = []
request.on('data', (chunk: unknown) => {
if (typeof chunk === 'string' || chunk instanceof Uint8Array) chunks.push(Buffer.from(chunk))
else reject(new TypeError('unexpected HTTP request chunk'))
})
request.once('error', reject)
request.once('end', () => {
resolve({
...request.method !== undefined ? { method: request.method } : {},
headers: request.headers,
body: Buffer.concat(chunks).toString('utf8'),
})
})
})
}
/** Listen on an ephemeral loopback port and return the server origin. */
async function listen(server: Server): Promise<string> {
await new Promise<void>((resolve, reject) => {
server.once('error', reject)
server.listen(0, '127.0.0.1', resolve)
})
const address = server.address() as AddressInfo
return `http://127.0.0.1:${address.port}`
}
/** Close a listening fixture server after every request has settled. */
async function close(server: Server): Promise<void> {
if (!server.listening) return
await new Promise<void>((resolve, reject) => server.close((error) => {
if (error === undefined) resolve()
else reject(error)
}))
}
/** Normalize an unknown fixture failure for `ServerResponse.destroy`. */
function asError(error: unknown): Error {
return error instanceof Error ? error : new Error(String(error))
}