Merge remote-tracking branch 'origin/master' into docs/post-v3-release-proofreading
# Conflicts: # .agents/notes/implemented/architecture/2026-06-20-generic-long-running-tool-runtime.i18n.yaml # .agents/notes/implemented/architecture/2026-06-20-generic-long-running-tool-runtime.zh.md # README.i18n.yaml # README.zh.md # scripts/snapshots/translation-prompt-v4/request-response.expected.json
This commit is contained in:
@@ -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/client/connection/README.md
|
||||
README.md: a82bb55ab65df2732ad16248d2cc9aa15b60e94d
|
||||
README.zh.md: cc9e4e01bb0dec278cf162ae442154d11c76aa85
|
||||
README.md: d3727df981ecbc022345def48b38fbc879e29cb2
|
||||
README.zh.md: 5e632bf6d7135bda60178f0699691aff2d5623db
|
||||
|
||||
@@ -23,3 +23,4 @@ None; this package neither assembles nor sends a provider request.
|
||||
## Known Limitations and Deferred Work
|
||||
|
||||
- **History resumes an unattached session** — opening history may create the host-side agent and add latency to the first open; there is no persistence-only read path.
|
||||
- **The `/api` bridge buffers each request body in memory** — `maxRequestBodyBytes` (default 160 MiB, sized for the default 100 MiB aggregate image limit after base64 expansion plus envelope headroom) is therefore also the per-request resident bound; a streaming body path would be needed to lower it without shrinking the image limits.
|
||||
|
||||
@@ -23,3 +23,4 @@ node 半侧在桥接或 upgrade 前守卫 `/api` 下的每个入口(`src/api-r
|
||||
## 已知限制与暂缓事项
|
||||
|
||||
- **History 会恢复未附加的会话**:打开 history 可能创建宿主侧 agent,并增加首次打开的延迟;没有仅从持久化读取的路径。
|
||||
- **`/api` 桥把每个请求体整体缓冲在内存里**:`maxRequestBodyBytes`(默认 160 MiB,按默认 100 MiB 图片总量上限经 base64 膨胀加信封余量得出)因此同时是单请求的驻留内存上界;要降低它而不缩小图片限额,需要流式请求体路径。
|
||||
|
||||
@@ -979,6 +979,18 @@ function projectionValuesOf(log: readonly SessionEvent[]): Record<string, unknow
|
||||
values['contextPressure'] = contextPressureOf(log)
|
||||
// Always present (token-meter composed): heuristic request composition.
|
||||
values['contextBreakdown'] = contextBreakdownOf(log)
|
||||
// Always present (attachment service composed): the deployment image
|
||||
// limits, constant per boot (mirrors the attachment-local defaults).
|
||||
// Deliberate host divergence: the real gateway never pushes an imageLimits
|
||||
// change frame (constant unit), but the fixture's uniform baseline replay
|
||||
// frames every key here, incidentally exercising higher-seq-wins.
|
||||
values['imageLimits'] = {
|
||||
maxImageBytes: 5 * 1024 * 1024,
|
||||
maxImagesPerMessage: 20,
|
||||
maxMessageImageBytes: 100 * 1024 * 1024,
|
||||
maxImagePixels: 40_000_000,
|
||||
mediaTypes: ['image/png', 'image/jpeg', 'image/webp', 'image/gif'],
|
||||
}
|
||||
return values
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,12 @@
|
||||
|
||||
import type { IncomingMessage, ServerResponse } from 'node:http'
|
||||
|
||||
/** Default carrier cap for all HTTP RPC bodies: sized for the default
|
||||
* aggregate image limit (100 MiB) after base64 expansion plus envelope
|
||||
* headroom (~134.3 MiB required), rounded up for slack. The bridge buffers
|
||||
* each body in memory, so this cap is also the per-request resident bound. */
|
||||
export const DEFAULT_MAX_REQUEST_BODY_BYTES = 160 * 1024 * 1024
|
||||
|
||||
/** Transport-independent request handler consumed by the Host HTTP bridge. */
|
||||
export interface FetchHandler {
|
||||
/**
|
||||
@@ -27,7 +33,7 @@ export async function bridge(
|
||||
req: IncomingMessage,
|
||||
res: ServerResponse,
|
||||
apiHandler: FetchHandler,
|
||||
maxRequestBodyBytes = 32 * 1024 * 1024,
|
||||
maxRequestBodyBytes = DEFAULT_MAX_REQUEST_BODY_BYTES,
|
||||
): Promise<void> {
|
||||
const abort = new AbortController()
|
||||
// Client-disconnect detection MUST hang off the response, not the request:
|
||||
|
||||
@@ -6,7 +6,7 @@ import type {} from '@deepseek-ai/dsh-attachment'
|
||||
import type { WebRoute, WebUpgradeRoute } from '@deepseek-ai/dsh-host-webserver'
|
||||
import { toFetchHandler } from '@deepseek-ai/dsh-host-apiproxy'
|
||||
import { API_PATH, HOST_EVENTS_PATH, MUX_EVENTS_PATH } from './api-path.ts'
|
||||
import { bridge } from './http-bridge.ts'
|
||||
import { bridge, DEFAULT_MAX_REQUEST_BODY_BYTES } from './http-bridge.ts'
|
||||
import { assertTrustedAuthority, isTrustedApiRequest } from './api-request-trust.ts'
|
||||
import { HostConnectionService } from './rpc-host.ts'
|
||||
import { rejectWebSocketUpgrade, WebSocketDownlinks } from './websocket-downlink.ts'
|
||||
@@ -42,8 +42,6 @@ function assertImageBodyCapacity(ctx: Context, maxRequestBodyBytes: number): voi
|
||||
)
|
||||
}
|
||||
}
|
||||
/** Default carrier cap for all HTTP RPC bodies. */
|
||||
const DEFAULT_MAX_REQUEST_BODY_BYTES = 32 * 1024 * 1024
|
||||
|
||||
/** Services required before providing Connection; API Proxy is an optional `/api` fallback. */
|
||||
export const inject = ['httpServer']
|
||||
|
||||
@@ -164,6 +164,13 @@ describe('createFixtureApi', () => {
|
||||
toolsTokens: 0,
|
||||
messageTokens: 0,
|
||||
},
|
||||
imageLimits: {
|
||||
maxImageBytes: 5 * 1024 * 1024,
|
||||
maxImagesPerMessage: 20,
|
||||
maxMessageImageBytes: 100 * 1024 * 1024,
|
||||
maxImagePixels: 40_000_000,
|
||||
mediaTypes: ['image/png', 'image/jpeg', 'image/webp', 'image/gif'],
|
||||
},
|
||||
} },
|
||||
})
|
||||
})
|
||||
@@ -374,10 +381,14 @@ describe('createFixtureApi', () => {
|
||||
value: { systemTokens: 0, toolsTokens: 0 },
|
||||
})
|
||||
expect((first[8]?.payload as { value: { messageTokens: number } }).value.messageTokens).toBeGreaterThan(0)
|
||||
expect(first[9]?.payload).toMatchObject({ type: 'approval/requested', toolName: 'dangerous_tool' })
|
||||
expect(second[9]?.rpcId).toBe(first[9]?.rpcId) // stable rpcId across replays (host replay semantics)
|
||||
expect(first[10]?.payload).toMatchObject({ type: 'question/requested', sessionId: 'fx-alpha' })
|
||||
expect(second[10]?.rpcId).toBe(first[10]?.rpcId)
|
||||
expect(first[9]?.payload).toMatchObject({
|
||||
type: 'session/projection', sessionId: 'fx-alpha', key: 'imageLimits',
|
||||
value: { maxImagesPerMessage: 20, maxImageBytes: 5 * 1024 * 1024 },
|
||||
})
|
||||
expect(first[10]?.payload).toMatchObject({ type: 'approval/requested', toolName: 'dangerous_tool' })
|
||||
expect(second[10]?.rpcId).toBe(first[10]?.rpcId) // stable rpcId across replays (host replay semantics)
|
||||
expect(first[11]?.payload).toMatchObject({ type: 'question/requested', sessionId: 'fx-alpha' })
|
||||
expect(second[11]?.rpcId).toBe(first[11]?.rpcId)
|
||||
})
|
||||
|
||||
it('steer with no replay in flight falls through to a fresh queued turn; non-text blocks stringify empty', async () => {
|
||||
|
||||
Reference in New Issue
Block a user