fix: address ds-review-bot v7 findings on the merged image-input head
- gate model selection on steering-placement image carriers from enqueue until their steering/message event publishes; release the gate when an admission ends idle without publication (both behaviorally asserted) - reject session.updateQueue edits carrying non-text blocks at the RPC boundary (queue edits cannot bypass image admission) - extend the durable-directory walk past a first-created DSH_HOME to the deepest pre-existing ancestor - strip Windows-style separators from attachment display names on POSIX - verify attachment reads with a header-only probe (digest already proves the bytes decoded fully at admission); document the read path - make SessionInputShell.addImages refusal observable and keep workspace transfers/composer intake from leaking refused drafts - own ONE recursive image walk (dsh-llm contentHasImage) across apiproxy, pi-ai, compact-basic, and the DeepSeek text-only assertion - drop the redundant canonical-base64 regex and the no-op role read - move AttachmentId/AttachmentError out of types.ts (brand.ts/error.ts); document why AttachmentError does not extend HarnessError - document the hard attachments inject in both consumer READMEs
This commit is contained in:
@@ -114,7 +114,7 @@ function makeHarness(init?: Partial<ConversationSnapshot>) {
|
||||
useInput: (() => { throw new Error('unused') }),
|
||||
inputActions: {
|
||||
setDraft: () => {},
|
||||
addImages: () => {},
|
||||
addImages: () => true,
|
||||
removeImage: () => {},
|
||||
pruneImages: () => {},
|
||||
submit: () => {},
|
||||
|
||||
@@ -80,7 +80,7 @@ describe('render branch tails', () => {
|
||||
useInput={(() => { throw new Error('unused') })}
|
||||
inputActions={{
|
||||
setDraft: () => {},
|
||||
addImages: () => {},
|
||||
addImages: () => true,
|
||||
removeImage: () => {},
|
||||
pruneImages: () => {},
|
||||
submit: () => {},
|
||||
@@ -122,7 +122,7 @@ describe('render branch tails', () => {
|
||||
useInput={(() => { throw new Error('unused') })}
|
||||
inputActions={{
|
||||
setDraft: () => {},
|
||||
addImages: () => {},
|
||||
addImages: () => true,
|
||||
removeImage: () => {},
|
||||
pruneImages: () => {},
|
||||
submit: () => {},
|
||||
|
||||
@@ -339,6 +339,26 @@ describe('machine pending lock', () => {
|
||||
expect(textarea.readOnly).toBe(true)
|
||||
expect(view.container.querySelector<HTMLButtonElement>('button[aria-label="Send message"]')!.disabled).toBe(true)
|
||||
})
|
||||
|
||||
it('addImages reports refusal in busy phases so callers keep draft ownership', () => {
|
||||
const { shell } = bench()
|
||||
act(() => {
|
||||
shell.setDraft('/goal ')
|
||||
shell.beginCommand(
|
||||
{
|
||||
token: '/goal ',
|
||||
submit: () => new Promise<never>(() => {}), // never settles: stays submitting
|
||||
},
|
||||
{ start: 0, end: 6, draftRev: shell.snapshot.draftRev },
|
||||
)
|
||||
shell.submit('queue')
|
||||
})
|
||||
expect(shell.snapshot.phase).toBe('submitting')
|
||||
// A refused batch must be observable (the workspace-switch transfer keeps
|
||||
// the source shell's drafts alive instead of leaking their object URLs).
|
||||
expect(shell.addImages(['busy-1' as never])).toBe(false)
|
||||
expect(shell.snapshot.imageIds).toEqual([])
|
||||
})
|
||||
})
|
||||
|
||||
describe('decorations', () => {
|
||||
|
||||
@@ -395,7 +395,7 @@ describe('DetailsPanel Output section', () => {
|
||||
useSessions={bindSnapshotSelector(sessions)}
|
||||
useWorkspaces={bindSnapshotSelector(workspaces)}
|
||||
useInput={(() => { throw new Error('unused') })}
|
||||
inputActions={{ setDraft: () => {}, addImages: () => {}, removeImage: () => {}, pruneImages: () => {}, submit: () => {} }}
|
||||
inputActions={{ setDraft: () => {}, addImages: () => true, removeImage: () => {}, pruneImages: () => {}, submit: () => {} }}
|
||||
useProjection={(() => undefined)}
|
||||
useStore={bindSnapshotSelector(chat)}
|
||||
actions={chat.actions}
|
||||
@@ -566,7 +566,7 @@ describe('DetailsPanel Output section', () => {
|
||||
baselinesReady: true, recentWorkspaceId: undefined,
|
||||
}))}
|
||||
useInput={(() => { throw new Error('unused') })}
|
||||
inputActions={{ setDraft: () => {}, addImages: () => {}, removeImage: () => {}, pruneImages: () => {}, submit: () => {} }}
|
||||
inputActions={{ setDraft: () => {}, addImages: () => true, removeImage: () => {}, pruneImages: () => {}, submit: () => {} }}
|
||||
useProjection={(() => undefined)}
|
||||
useStore={bindSnapshotSelector(chat)}
|
||||
actions={chat.actions}
|
||||
|
||||
Reference in New Issue
Block a user