feat: ship v0.7.0 with AI knowledge context integration

Add semi-automatic knowledge suggestions and user-confirmed injection across chat, interactive, auto, and wizard modes; fix writing flows to persist full systemPrompt in contextJson.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-07 15:33:53 +08:00
parent 2afbb83c43
commit aa2c7dfed3
41 changed files with 1315 additions and 168 deletions
+21 -7
View File
@@ -1,9 +1,10 @@
import { BrowserWindow, ipcMain } from 'electron'
import { IPC } from '../../../shared/ipc-channels'
import type { AutoWritingConfig } from '../../../shared/types'
import type { AiContextBinding, AutoWritingConfig } from '../../../shared/types'
import { BookRegistryService } from '../../services/book-registry'
import { GlobalSettingsService } from '../../services/global-settings'
import { AiClientService } from '../../services/ai-client.service'
import { buildFlowContextPayload, readFlowSystemPrompt } from '../../services/flow-context.util'
import { checkInteractiveGate } from '../../services/interactive-gate.service'
import { AutoWritingService } from '../../services/auto-writing.service'
import { InteractiveRepository } from '../../db/repositories/interactive.repo'
@@ -26,12 +27,7 @@ function readContextText(registry: BookRegistryService, bookId: string, flowId:
.getDb(bookId)
.prepare('SELECT context_json FROM interactive_flows WHERE id = ?')
.get(flowId) as { context_json: string } | undefined
try {
const parsed = JSON.parse(row?.context_json ?? '{}') as { contextSummary?: string }
return parsed.contextSummary ?? '(无上下文摘要)'
} catch {
return '(无上下文摘要)'
}
return readFlowSystemPrompt(row?.context_json ?? '{}')
}
export function registerAutoHandlers(
@@ -76,6 +72,24 @@ export function registerAutoHandlers(
})
)
ipcMain.handle(
IPC.AUTO_CONFIRM_CONTEXT,
(
_e,
{ bookId, flowId, binding }: { bookId: string; flowId: string; binding: AiContextBinding }
) =>
wrap(() => {
const svc = serviceFor(registry, bookId, aiClient)
const payload = buildFlowContextPayload(
binding,
registry,
bookId,
settings.get().penName
)
return svc.enrichFlow(svc.confirmContext(flowId, payload))
})
)
ipcMain.handle(
IPC.AUTO_PLAN_SCENES,
(_e, { bookId, flowId, contextSummary }: { bookId: string; flowId: string; contextSummary?: string }) =>
+6 -6
View File
@@ -4,7 +4,7 @@ import type { AiContextBinding, PlotSelection } from '../../../shared/types'
import { BookRegistryService } from '../../services/book-registry'
import { GlobalSettingsService } from '../../services/global-settings'
import { AiClientService } from '../../services/ai-client.service'
import { aiContextBuilder } from '../../services/ai-context-builder.service'
import { buildFlowContextPayload, readFlowSystemPrompt } from '../../services/flow-context.util'
import { checkInteractiveGate } from '../../services/interactive-gate.service'
import { InteractiveWritingService } from '../../services/interactive-writing.service'
import { wrap } from '../result'
@@ -56,13 +56,13 @@ export function registerInteractiveHandlers(
}: { bookId: string; flowId: string; binding: AiContextBinding }
) =>
wrap(() => {
const built = aiContextBuilder.build(binding, registry, bookId, settings.get().penName)
const summary = built.preview.map((p) => p.title).join('、') || '(空)'
const flow = serviceFor(registry, bookId, aiClient).confirmContext(
flowId,
const payload = buildFlowContextPayload(
binding,
summary
registry,
bookId,
settings.get().penName
)
const flow = serviceFor(registry, bookId, aiClient).confirmContext(flowId, payload)
return serviceFor(registry, bookId, aiClient).enrichFlow(flow)
})
)
+19 -2
View File
@@ -1,11 +1,16 @@
import { ipcMain } from 'electron'
import { IPC } from '../../../shared/ipc-channels'
import type { CreateKnowledgeInput, KnowledgeStatus, KnowledgeType } from '../../../shared/types'
import type { CreateKnowledgeInput, KnowledgeStatus, KnowledgeType, KnowledgeSuggestOptions } from '../../../shared/types'
import { BookRegistryService } from '../../services/book-registry'
import { GlobalSettingsService } from '../../services/global-settings'
import { KnowledgeRetrievalService } from '../../services/knowledge-retrieval.service'
import { KnowledgeService } from '../../services/knowledge.service'
import { wrap } from '../result'
export function registerKnowledgeHandlers(registry: BookRegistryService): void {
export function registerKnowledgeHandlers(
registry: BookRegistryService,
settings: GlobalSettingsService
): void {
ipcMain.handle(
IPC.KNOWLEDGE_LIST,
(
@@ -65,4 +70,16 @@ export function registerKnowledgeHandlers(registry: BookRegistryService): void {
ipcMain.handle(IPC.KNOWLEDGE_STATS, (_e, { bookId }: { bookId: string }) =>
wrap(() => new KnowledgeService(registry.getDb(bookId)).getForeshadowStats())
)
ipcMain.handle(
IPC.KNOWLEDGE_SUGGEST_CONTEXT,
(_e, { bookId, opts }: { bookId: string; opts?: KnowledgeSuggestOptions }) =>
wrap(() => {
const topN = opts?.topN ?? settings.get().knowledgeSuggestTopN ?? 8
return new KnowledgeRetrievalService(registry.getDb(bookId)).suggest({
...opts,
topN
})
})
)
}
+9 -10
View File
@@ -4,7 +4,7 @@ import type { AiContextBinding, AutoRhythm, AutoWritingConfig, WizardWritingConf
import { BookRegistryService } from '../../services/book-registry'
import { GlobalSettingsService } from '../../services/global-settings'
import { AiClientService } from '../../services/ai-client.service'
import { aiContextBuilder } from '../../services/ai-context-builder.service'
import { buildFlowContextPayload, readFlowSystemPrompt } from '../../services/flow-context.util'
import { WizardWritingService } from '../../services/wizard-writing.service'
import { InteractiveRepository } from '../../db/repositories/interactive.repo'
import { wrap } from '../result'
@@ -20,12 +20,7 @@ function readContextText(registry: BookRegistryService, bookId: string, flowId:
.getDb(bookId)
.prepare('SELECT context_json FROM interactive_flows WHERE id = ?')
.get(flowId) as { context_json: string } | undefined
try {
const parsed = JSON.parse(row?.context_json ?? '{}') as { contextSummary?: string }
return parsed.contextSummary ?? '(无上下文摘要)'
} catch {
return '(无上下文摘要)'
}
return readFlowSystemPrompt(row?.context_json ?? '{}')
}
export function registerWizardHandlers(
@@ -98,10 +93,14 @@ export function registerWizardHandlers(
{ bookId, flowId, binding }: { bookId: string; flowId: string; binding: AiContextBinding }
) =>
wrap(() => {
const built = aiContextBuilder.build(binding, registry, bookId, settings.get().penName)
const summary = built.preview.map((p) => p.title).join('、') || '(空)'
const payload = buildFlowContextPayload(
binding,
registry,
bookId,
settings.get().penName
)
const svc = serviceFor(registry, bookId, aiClient)
return svc.enrichFlow(svc.confirmContext(flowId, binding, summary))
return svc.enrichFlow(svc.confirmContext(flowId, payload))
})
)
+1 -1
View File
@@ -52,7 +52,7 @@ export function registerIpc(): { settings: GlobalSettingsService; books: BookReg
registerInteractiveHandlers(books, settings, aiClient)
registerAutoHandlers(books, settings, aiClient)
registerWizardHandlers(books, settings, aiClient)
registerKnowledgeHandlers(books)
registerKnowledgeHandlers(books, settings)
registerCockpitHandlers(books, settings)
registerBridgeHandlers(books, aiClient)