Merge latest master into codex/llm-error-recovery-rfc

# Conflicts:
#	docs/architecture.md
#	docs/config-catalog.md
#	docs/cordis-catalog/events.md
#	docs/event-producer-consumer.md
#	docs/module-graph.md
#	docs/persistence-catalog.md
#	packages/cordis/tool-cordis/src/api-catalog.ts
#	packages/core/agent-loop/src/loop.ts
#	packages/core/agent/src/types.ts
#	packages/examples/agent-spine-demo/README.md
#	packages/examples/stdio-demo/README.md
#	packages/examples/stdio-demo/src/index.ts
#	packages/llm/llm-deepseek/README.md
#	packages/llm/llm-deepseek/src/adapter.ts
#	packages/llm/llm-deepseek/tests/adapter.spec.ts
#	packages/llm/llm/README.md
#	packages/llm/llm/src/error.ts
#	packages/llm/llm/tests/service.spec.ts
#	packages/sandbox/sandbox-policy/tsconfig.json
#	packages/ui/stdio/README.md
#	packages/ui/stdio/src/index.ts
#	packages/ui/stdio/tests/stdio.spec.ts
#	packages/ui/tui/src/index.ts
#	pnpm-lock.yaml
#	website/zh-CN/api/harness/events.md
#	website/zh-CN/api/harness/llm.md
This commit is contained in:
Tianyi Cui
2026-07-20 20:59:11 +08:00
672 changed files with 18585 additions and 13005 deletions
@@ -4,6 +4,7 @@ import { afterEach, describe, expect, it, vi } from 'vitest'
import { Context } from 'cordis'
import LlmService, {
CONTEXT_WINDOW_EXCEEDED_CODE,
errorChain,
LlmError,
ProviderRequestId,
QUOTA_EXCEEDED_CODE,
@@ -323,6 +324,40 @@ describe('DeepSeekAdapter against a mock server', () => {
expect(httpErrorCode(418)).toBe('HTTP_418')
})
it('wraps a transport failure in TRANSPORT with the fetch cause chain in the message', async () => {
// Port 1 is reserved/unbound: fetch rejects with `TypeError: fetch failed`
// whose actionable detail (ECONNREFUSED) lives on `cause`.
const ctx = await harness('http://127.0.0.1:1')
let caught: unknown
try {
await assemble(ctx, { model: 'deepseek-v4-flash', messages: [] })
} catch (error: unknown) {
caught = error
}
expect(caught).toBeInstanceOf(LlmError)
const llmError = caught as LlmError
expect(llmError.code).toBe('TRANSPORT')
expect(llmError.message).toContain('http://127.0.0.1:1')
expect(llmError.cause).toBeInstanceOf(TypeError)
// The chain renderer reaches the transport diagnosis through the cause.
expect(errorChain(llmError)).toMatch(/ECONNREFUSED|EADDRNOTAVAIL|bad port/)
})
it('classifies an aborted request without losing the transport rejection', async () => {
const controller = new AbortController()
controller.abort()
const ctx = await harness('http://127.0.0.1:1')
let caught: unknown
try {
await assemble(ctx, { model: 'deepseek-v4-flash', messages: [], signal: controller.signal })
} catch (error: unknown) {
caught = error
}
expect(caught).toBeInstanceOf(LlmError)
expect(caught).toMatchObject({ code: 'ABORTED' })
expect((caught as LlmError).cause).toMatchObject({ name: 'AbortError' })
})
it('throws EMPTY_RESPONSE when the response has no body', async () => {
const adapter = new DeepSeekAdapter({ apiKey: 'k', baseURL: 'http://127.0.0.1:1' })
const fetchSpy = vi.spyOn(globalThis, 'fetch').mockResolvedValue(
@@ -396,7 +431,7 @@ describe('DeepSeekAdapter against a mock server', () => {
for await (const _chunk of adapter.stream({ provider: 'deepseek', model: 'm', messages: [] })) { /* drain */ }
}
await expect(drain()).rejects.toMatchObject({
message: 'DeepSeek transport failed: offline',
message: 'DeepSeek API request to https://example.invalid failed',
code: 'TRANSPORT',
cause: 'offline',
})