mirror of
https://github.com/deepseek-ai/deepseek-harness.git
synced 2026-09-14 04:01:35 +00:00
fix: deliver images reliably with steer and follow-up messages
A steer or follow-up accepted while a turn is closing is now claimed by a fresh turn at the driver's clean exit instead of stranding in the inbox; cancellation and pre-step rejection still park accepted work. Continuable subagent follow-ups accept image parts: the wire is upload-shaped, the Host admits and persists each batch before inbox acceptance, and delivery is refused when the child model declines image input. The queue dock renders durable image thumbnails instead of an [image] text marker. Fixes #3186
This commit is contained in:
@@ -7,12 +7,12 @@
|
||||
* must stub); implementation-internal entry points (history staging, wire-frame
|
||||
* dispatch) stay on the class, invisible out here.
|
||||
*/
|
||||
import type { AttachmentIdType, ImageAttachmentRef } from '@deepseek-ai/dsh-attachment'
|
||||
import type { AttachmentIdType, ImageAttachmentRef, PromptContentPart } from '@deepseek-ai/dsh-attachment'
|
||||
import type { MessageId } from '@deepseek-ai/dsh-llm/brand'
|
||||
import type { SessionId } from '@deepseek-ai/dsh-session/types'
|
||||
import type { RemoteResult } from '@deepseek-ai/dsh-typert-protocol'
|
||||
import type { ObservableSnapshot } from '@deepseek-ai/dsh-client-store'
|
||||
import type { PromptContentPart, QueueAction } from '../../types.ts'
|
||||
import type { QueueAction } from '../../types.ts'
|
||||
import type { ClientResult } from './result.ts'
|
||||
import type { SessionSnapshot } from './snapshot.ts'
|
||||
|
||||
|
||||
@@ -5,8 +5,11 @@ import type { QueuedMessage } from '../contract/snapshot.ts'
|
||||
|
||||
const QUEUE_PREVIEW_CHARS = 200
|
||||
|
||||
// Image blocks are excluded: queue presentation renders them as thumbnails
|
||||
// from `content`, so the text preview covers only what has no visual form.
|
||||
function previewOf(content: readonly ContentBlock[]): string {
|
||||
const flat = content
|
||||
.filter(block => block.type !== 'image')
|
||||
.map(block => (block.type === 'text' ? block.text : `[${block.type}]`))
|
||||
.join(' ').replace(/\s+/g, ' ').trim()
|
||||
const chars = Array.from(flat)
|
||||
|
||||
@@ -208,28 +208,15 @@ export class Session implements SessionFace {
|
||||
},
|
||||
}
|
||||
} else {
|
||||
if (content.some(part => part.type === 'image')) {
|
||||
result = {
|
||||
ok: false,
|
||||
error: {
|
||||
code: 'attachment-error',
|
||||
message: 'Image input is unavailable for subagent continuations.',
|
||||
details: { reason: 'SUBAGENT_IMAGE_UNSUPPORTED' },
|
||||
},
|
||||
}
|
||||
} else {
|
||||
const routed = toSessionResult(await this.remote.subagents.prompt({
|
||||
requestId: randomUUID() as SessionRequestId,
|
||||
parentSessionId: this.address.parentSessionId,
|
||||
childSessionId: this.address.childSessionId,
|
||||
mode: this.address.mode,
|
||||
content: content.flatMap(part => part.type === 'text'
|
||||
? [{ type: 'text' as const, text: part.text }]
|
||||
: []),
|
||||
clientTimeZone: resolvedClientTimeZone(),
|
||||
}, signal))
|
||||
result = routed.ok ? { ok: true, value: { accepted: true } } : routed
|
||||
}
|
||||
const routed = toSessionResult(await this.remote.subagents.prompt({
|
||||
requestId: randomUUID() as SessionRequestId,
|
||||
parentSessionId: this.address.parentSessionId,
|
||||
childSessionId: this.address.childSessionId,
|
||||
mode: this.address.mode,
|
||||
content,
|
||||
clientTimeZone: resolvedClientTimeZone(),
|
||||
}, signal))
|
||||
result = routed.ok ? { ok: true, value: { accepted: true } } : routed
|
||||
}
|
||||
} catch (error) {
|
||||
result = transportResult(error)
|
||||
|
||||
Reference in New Issue
Block a user