Merge remote-tracking branch 'origin/master' into worktree/web-multimodal-image-input

# Conflicts:
#	apps/cli/README.i18n.yaml
#	docs/core-data-structures/core.i18n.yaml
#	docs/core-data-structures/llm-streaming.i18n.yaml
#	examples/acp-agent/tests/snapshots/cordis-inspect-jsdoc/session.jsonl
#	packages/client/connection/src/client/fixture.ts
#	packages/client/connection/src/client/index.ts
#	packages/client/runtime/README.i18n.yaml
#	packages/client/runtime/README.md
#	packages/client/runtime/README.zh.md
#	packages/client/ui-conversation/README.i18n.yaml
#	packages/client/ui-conversation/src/client/input/hub.ts
#	packages/client/ui-conversation/src/client/service.ts
#	packages/client/ui-conversation/src/client/skeleton/InputBar.tsx
#	packages/compact/compact-basic/src/summarizer.ts
#	packages/compact/compact-basic/tests/compact-basic.spec.ts
#	packages/host/apiproxy/src/api-proxy.ts
#	packages/host/apiproxy/src/api/rpc.schema.ts
#	packages/host/apiproxy/src/api/rpc.ts
#	packages/host/apiproxy/src/api/sessions.ts
#	packages/host/apiproxy/tests/rpc-schemas.spec.ts
#	packages/llm/llm-deepseek/tests/serialize.spec.ts
#	packages/llm/llm-pi-ai/tests/adapter.spec.ts
#	packages/llm/llm-pi-ai/tests/convert.spec.ts
#	packages/llm/llm-pi-ai/tests/provider-apis.e2e.ts
#	packages/llm/token-meter/tests/token-meter.spec.ts
#	packages/ui/tui/README.i18n.yaml
This commit is contained in:
Yichen Jiang
2026-07-29 10:18:53 +08:00
763 changed files with 19809 additions and 7096 deletions
+13 -11
View File
@@ -9,7 +9,7 @@
*/
import { LlmError } from '@deepseek-ai/dsh-llm'
import type { Message } from '@deepseek-ai/dsh-llm'
import type { Message, ModelMessageSource } from '@deepseek-ai/dsh-llm'
import type { Api, AssistantMessage, Usage as PiUsage } from '@earendil-works/pi-ai'
type PiAiReplayBlock =
@@ -123,6 +123,7 @@ function readReplayState(value: unknown): PiAiReplayState {
/** Convert provider-neutral blocks without trusting them as same-model replay. */
function foreignAssistant(message: Message): AssistantMessage {
const source = message.source.kind === 'model' ? message.source : undefined
const content: AssistantMessage['content'] = []
for (const block of message.content) {
switch (block.type) {
@@ -145,10 +146,10 @@ function foreignAssistant(message: Message): AssistantMessage {
role: 'assistant',
content,
// Deliberately never equals a catalog API: absent replay state is foreign
// even if provenance names the same provider/model as this request.
// even if source names the same provider/model as this request.
api: 'dsh-foreign',
provider: message.provenance?.provider ?? 'dsh-foreign',
model: message.provenance?.model ?? 'dsh-foreign',
provider: source?.provider ?? 'dsh-foreign',
model: source?.model ?? 'dsh-foreign',
usage: emptyPiUsage(),
stopReason: content.some(piece => piece.type === 'toolCall') ? 'toolUse' : 'stop',
timestamp: 0,
@@ -156,11 +157,10 @@ function foreignAssistant(message: Message): AssistantMessage {
}
/** Recombine durable Harness content with validated pi-ai replay metadata. */
function replayedAssistant(message: Message, rawState: unknown): AssistantMessage {
function replayedAssistant(message: Message, source: ModelMessageSource, rawState: unknown): AssistantMessage {
const state = readReplayState(rawState)
const provenance = message.provenance
if (state.provider !== provenance?.provider) return invalidReplay('provider does not match assistant provenance')
if (state.model !== provenance.model) return invalidReplay('model does not match assistant provenance')
if (state.provider !== source.provider) return invalidReplay('provider does not match assistant source')
if (state.model !== source.model) return invalidReplay('model does not match assistant source')
if (state.blocks.length !== message.content.length) return invalidReplay('block count does not match assistant content')
const content: AssistantMessage['content'] = message.content.map((block, index) => {
const replay = state.blocks[index]
@@ -204,10 +204,12 @@ function replayedAssistant(message: Message, rawState: unknown): AssistantMessag
/**
* Convert one durable Harness assistant message into pi-ai history.
* @param message - assistant content with optional adapter-owned replay metadata.
* @param message - assistant content with required source and optional adapter-owned replay metadata.
* @returns a native pi-ai assistant message reconstructed from durable content.
*/
export function toPiAssistant(message: Message): AssistantMessage {
const replayState = message.provenance?.replayState
return replayState === undefined ? foreignAssistant(message) : replayedAssistant(message, replayState)
const source = message.source
return source.kind !== 'model' || source.replayState === undefined
? foreignAssistant(message)
: replayedAssistant(message, source, source.replayState)
}