fix: read ai chat db drafts

This commit is contained in:
centdix
2026-06-02 14:46:37 +02:00
parent eadeac248b
commit d40b3fd643
2 changed files with 105 additions and 6 deletions
@@ -361,6 +361,87 @@ describe('global AI tools', () => {
)
})
it('reads saved DB script drafts when no local draft exists', async () => {
vi.mocked(ScriptService.getScriptByPathWithDraft).mockResolvedValueOnce({
path: 'f/scripts/existing',
hash: 'deployed-hash',
summary: 'deployed summary',
description: 'deployed description',
content: 'deployed content',
language: 'bun',
kind: 'script',
draft: {
path: 'f/scripts/existing',
summary: 'db draft summary',
description: 'db draft description',
content: 'db draft content',
language: 'bun',
kind: 'script'
}
} as any)
const raw = await callGlobalTool('read_workspace_item', {
type: 'script',
path: 'f/scripts/existing'
})
const item = JSON.parse(raw)
expect(item).toEqual({
type: 'script',
path: 'f/scripts/existing',
summary: 'db draft summary',
language: 'bun',
value: 'db draft content',
isDraft: false
})
expect(raw).not.toContain('deployed content')
})
it('reads saved DB flow drafts when no local draft exists', async () => {
vi.mocked(FlowService.getFlowByPathWithDraft).mockResolvedValueOnce({
path: 'f/flows/existing',
summary: 'deployed summary',
value: {
modules: [{ id: 'deployed_step', value: { type: 'identity' } }]
},
schema: { type: 'object', properties: { deployed: { type: 'boolean' } } },
edited_by: 'admin',
edited_at: '2026-05-22T09:00:00Z',
archived: false,
extra_perms: {},
draft: {
path: 'f/flows/existing',
summary: 'db draft summary',
value: {
modules: [{ id: 'draft_step', value: { type: 'identity' } }]
},
schema: { type: 'object', properties: { draft: { type: 'string' } } },
edited_by: 'admin',
edited_at: '2026-05-22T10:00:00Z',
archived: false,
extra_perms: {}
}
} as any)
const raw = await callGlobalTool('read_workspace_item', {
type: 'flow',
path: 'f/flows/existing'
})
const item = JSON.parse(raw)
expect(item).toMatchObject({
type: 'flow',
path: 'f/flows/existing',
summary: 'db draft summary',
isDraft: false,
value: {
modules: [{ id: 'draft_step', value: { type: 'identity' } }],
schema: { type: 'object', properties: { draft: { type: 'string' } } }
}
})
expect(raw).not.toContain('deployed_step')
})
it('applies path_prefix to local drafts before enforcing the result limit', async () => {
await callGlobalTool('write_script', {
path: 'f/other/outside',
@@ -591,7 +591,9 @@ function itemMatches(
)
}
function scriptToItem(script: Script, includeValue: boolean): WorkspaceItem {
type ReadableScript = Pick<Script, 'path' | 'summary' | 'language' | 'content'>
function scriptToItem(script: ReadableScript, includeValue: boolean): WorkspaceItem {
return {
type: 'script',
path: script.path,
@@ -602,7 +604,10 @@ function scriptToItem(script: Script, includeValue: boolean): WorkspaceItem {
}
}
function flowToItem(flow: Flow, includeValue: boolean): WorkspaceItem {
function flowToItem(
flow: Pick<Flow, 'path' | 'summary' | 'value' | 'schema'>,
includeValue: boolean
): WorkspaceItem {
return {
type: 'flow',
path: flow.path,
@@ -614,6 +619,19 @@ function flowToItem(flow: Flow, includeValue: boolean): WorkspaceItem {
}
}
async function loadScriptWithDbDraft(path: string, workspace: string): Promise<ReadableScript> {
const script = await ScriptService.getScriptByPathWithDraft({ workspace, path })
return script.draft ? { ...script, ...script.draft, path: script.path } : script
}
async function loadFlowWithDbDraft(
path: string,
workspace: string
): Promise<Pick<Flow, 'path' | 'summary' | 'value' | 'schema'>> {
const flow = await FlowService.getFlowByPathWithDraft({ workspace, path })
return flow.draft ? { ...flow, ...flow.draft, path: flow.path } : flow
}
/**
* Turn a flow workspace item into the compact response we send to the model:
* rawscript content is replaced with `inline_script.<moduleId>` placeholders.
@@ -1098,9 +1116,9 @@ async function readWorkspaceItem(
): Promise<WorkspaceItem> {
switch (type) {
case 'script':
return scriptToItem(await ScriptService.getScriptByPath({ workspace, path }), true)
return scriptToItem(await loadScriptWithDbDraft(path, workspace), true)
case 'flow':
return flowToItem(await FlowService.getFlowByPath({ workspace, path }), true)
return flowToItem(await loadFlowWithDbDraft(path, workspace), true)
case 'schedule':
return scheduleToItem(await ScheduleService.getSchedule({ workspace, path }), true)
case 'trigger':
@@ -2327,7 +2345,7 @@ async function loadScriptForEdit(
}
return { content: draft.value, language: draft.language, summary: draft.summary }
}
const script = await ScriptService.getScriptByPath({ workspace, path })
const script = await loadScriptWithDbDraft(path, workspace)
return { content: script.content, language: script.language, summary: script.summary }
}
@@ -2362,7 +2380,7 @@ async function loadFlowDraftValue(
}
return { flow: draft.value as FlowDraftValue, summary: draft.summary }
}
const flow = await FlowService.getFlowByPath({ workspace, path })
const flow = await loadFlowWithDbDraft(path, workspace)
return {
flow: { value: flow.value, schema: flow.schema, groups: flow.value.groups ?? null },
summary: flow.summary