Replace truncated review input with paged, resumable batches and retain extracted facts and source references through navigation summaries. Expose concurrency, timeouts, batch budgets and response capacity in supervisor settings. Separate automatic supervision from review results, activity and settings; use a standard plan modal and show execution stages with saved batch details. Fix historical entity identity mapping, navigation output validation and arbitrary summary length failures. Include focused regressions, isolated real-data validation scripts and updated design and verification records. Overall layout acceptance remains outstanding; this commit does not claim that all visual issues are resolved. Release note: 监督回顾支持完整分页、分批保存和暂停后继续,可配置并发、超时及响应容量;修复实体引用、汇总校验和长摘要报错,并重新组织自动监督与活动记录入口。
620 lines
21 KiB
TypeScript
620 lines
21 KiB
TypeScript
import { readFile, realpath, stat } from 'node:fs/promises'
|
|
import { delimiter, extname, isAbsolute, join } from 'node:path'
|
|
import { z } from 'zod'
|
|
import {
|
|
applicationSettingsSchema,
|
|
applicationSettingsUpdateSchema,
|
|
applicationNavigationSchema,
|
|
builtInApplicationIds,
|
|
defaultApplicationNavigation,
|
|
defaultSupervisionTimeoutSeconds,
|
|
defaultSupervisorModelConcurrency,
|
|
type ApplicationSettings
|
|
} from '../shared/application-settings-contracts'
|
|
import {
|
|
defaultLocalToolEnvironmentSettings,
|
|
localToolExecutablePathSchema,
|
|
type LocalToolEnvironmentSettings
|
|
} from '../shared/local-tool-environment-contracts'
|
|
import { inspectLocalToolExecutable } from './local-tool-environment/local-tool-environment'
|
|
import { releaseVersionSchema } from '../shared/release-notes-contracts'
|
|
import type { SettingsWarning } from '../shared/settings-warning-contracts'
|
|
import {
|
|
assertSupportedSettingsVersion,
|
|
isolateCorruptSettingsFile,
|
|
isMissingFileError,
|
|
UnsupportedSettingsVersionError,
|
|
writeJsonFileAtomically
|
|
} from './settings-file-utils'
|
|
export {
|
|
applicationSettingsSchema,
|
|
applicationSettingsUpdateSchema
|
|
} from '../shared/application-settings-contracts'
|
|
export type { ApplicationSettings } from '../shared/application-settings-contracts'
|
|
|
|
const CURRENT_SETTINGS_VERSION = 12
|
|
|
|
const legacyStoredApplicationSettingsSchema = z
|
|
.object({
|
|
version: z.union([z.literal(1), z.literal(2)]),
|
|
checkUpdatesOnStartup: z.boolean()
|
|
})
|
|
.strict()
|
|
|
|
const versionTwoStoredApplicationSettingsSchema = z
|
|
.object({
|
|
version: z.literal(2),
|
|
checkUpdatesOnStartup: z.boolean(),
|
|
magicNotesEnabled: z.boolean()
|
|
})
|
|
.strict()
|
|
|
|
const versionThreeStoredApplicationSettingsSchema = z
|
|
.object({
|
|
version: z.literal(3),
|
|
checkUpdatesOnStartup: z.boolean(),
|
|
magicNotesEnabled: z.boolean(),
|
|
magicNoteCommentMode: applicationSettingsSchema.shape.magicNoteCommentMode
|
|
})
|
|
.strict()
|
|
|
|
const versionFourStoredApplicationSettingsSchema = applicationSettingsSchema
|
|
.omit({
|
|
updateSource: true,
|
|
modelDownloadSource: true,
|
|
localToolEnvironment: true,
|
|
conversationHtmlRenderingEnabled: true,
|
|
remoteProjectsEnabled: true,
|
|
magicNotesShowIncompleteTodoCount: true
|
|
})
|
|
.extend({
|
|
version: z.literal(4)
|
|
})
|
|
.strict()
|
|
|
|
const versionFiveStoredApplicationSettingsSchema = applicationSettingsSchema
|
|
.omit({
|
|
updateSource: true,
|
|
modelDownloadSource: true,
|
|
localToolEnvironment: true,
|
|
conversationHtmlRenderingEnabled: true,
|
|
remoteProjectsEnabled: true,
|
|
magicNotesShowIncompleteTodoCount: true
|
|
})
|
|
.extend({
|
|
version: z.literal(5),
|
|
lastSeenReleaseNotesVersion: releaseVersionSchema.nullable()
|
|
})
|
|
.strict()
|
|
|
|
const versionSixStoredApplicationSettingsSchema = applicationSettingsSchema
|
|
.omit({
|
|
modelDownloadSource: true,
|
|
localToolEnvironment: true,
|
|
conversationHtmlRenderingEnabled: true,
|
|
remoteProjectsEnabled: true,
|
|
magicNotesShowIncompleteTodoCount: true
|
|
})
|
|
.extend({
|
|
version: z.literal(6),
|
|
lastSeenReleaseNotesVersion: releaseVersionSchema.nullable()
|
|
})
|
|
.strict()
|
|
|
|
const versionSevenStoredApplicationSettingsSchema = applicationSettingsSchema
|
|
.omit({
|
|
localToolEnvironment: true,
|
|
conversationHtmlRenderingEnabled: true,
|
|
remoteProjectsEnabled: true,
|
|
magicNotesShowIncompleteTodoCount: true
|
|
})
|
|
.extend({
|
|
version: z.literal(7),
|
|
lastSeenReleaseNotesVersion: releaseVersionSchema.nullable()
|
|
})
|
|
.strict()
|
|
|
|
const versionEightStoredApplicationSettingsSchema = applicationSettingsSchema
|
|
.omit({
|
|
localToolEnvironment: true,
|
|
conversationHtmlRenderingEnabled: true,
|
|
remoteProjectsEnabled: true
|
|
})
|
|
.extend({
|
|
version: z.literal(8),
|
|
lastSeenReleaseNotesVersion: releaseVersionSchema.nullable()
|
|
})
|
|
.strict()
|
|
|
|
const versionNineStoredApplicationSettingsSchema = applicationSettingsSchema
|
|
.omit({
|
|
localToolEnvironment: true,
|
|
conversationHtmlRenderingEnabled: true
|
|
})
|
|
.extend({
|
|
version: z.literal(9),
|
|
lastSeenReleaseNotesVersion: releaseVersionSchema.nullable()
|
|
})
|
|
.strict()
|
|
|
|
const versionTenStoredApplicationSettingsSchema =
|
|
applicationSettingsSchema
|
|
.omit({ conversationHtmlRenderingEnabled: true })
|
|
.extend({
|
|
version: z.literal(10),
|
|
lastSeenReleaseNotesVersion: releaseVersionSchema.nullable()
|
|
})
|
|
.strict()
|
|
|
|
const storedApplicationSettingsSchema = applicationSettingsSchema
|
|
.extend({
|
|
// Stored partial orders are upgraded here; public settings writes remain strict.
|
|
applicationNavigation: applicationNavigationSchema.extend({
|
|
order: z.array(z.enum(builtInApplicationIds))
|
|
.refine(ids => new Set(ids).size === ids.length, 'Duplicate application')
|
|
.default([])
|
|
.transform(ids => [
|
|
...(['knowledge', 'heartbeat'] as const).filter(id => !ids.includes(id)),
|
|
...ids,
|
|
...defaultApplicationNavigation.order.filter(id => id !== 'knowledge' && id !== 'heartbeat' && !ids.includes(id))
|
|
]),
|
|
pinned: applicationNavigationSchema.shape.pinned.partial()
|
|
.default({})
|
|
.transform(pinned => ({ ...defaultApplicationNavigation.pinned, ...pinned }))
|
|
}).default(defaultApplicationNavigation),
|
|
version: z.literal(CURRENT_SETTINGS_VERSION),
|
|
lastSeenReleaseNotesVersion: releaseVersionSchema.nullable()
|
|
})
|
|
.strict()
|
|
|
|
const versionElevenStoredApplicationSettingsSchema = storedApplicationSettingsSchema.extend({ version: z.literal(11) })
|
|
|
|
type StoredApplicationSettings = z.infer<typeof storedApplicationSettingsSchema>
|
|
|
|
export type LegacyLocalToolEnvironmentPaths = {
|
|
nodeExecutablePath?: string
|
|
pythonExecutablePath?: string
|
|
}
|
|
|
|
export type LegacyLocalToolEnvironmentResolver =
|
|
() => Promise<LegacyLocalToolEnvironmentPaths>
|
|
|
|
async function resolveExecutable(
|
|
kind: 'node' | 'python',
|
|
binaryNames: readonly string[]
|
|
): Promise<string | undefined> {
|
|
const extensions =
|
|
process.platform === 'win32'
|
|
? (process.env.PATHEXT ?? '.COM;.EXE;.BAT;.CMD')
|
|
.split(';')
|
|
.filter((extension) => /^\.[A-Za-z0-9]+$/u.test(extension))
|
|
: ['']
|
|
const directories = (
|
|
process.env.PATH ??
|
|
process.env.Path ??
|
|
process.env.path ??
|
|
''
|
|
)
|
|
.split(delimiter)
|
|
.map((directory) => directory.trim())
|
|
.filter(isAbsolute)
|
|
|
|
for (const directory of directories) {
|
|
for (const binaryName of binaryNames) {
|
|
const names =
|
|
process.platform === 'win32' && !extname(binaryName)
|
|
? extensions.map((extension) => `${binaryName}${extension}`)
|
|
: [binaryName]
|
|
for (const name of names) {
|
|
try {
|
|
const executablePath = await realpath(join(directory, name))
|
|
const metadata = await stat(executablePath)
|
|
if (
|
|
metadata.isFile() &&
|
|
localToolExecutablePathSchema.safeParse(executablePath).success
|
|
) {
|
|
const candidate = await inspectLocalToolExecutable(
|
|
kind,
|
|
executablePath,
|
|
{ baseEnvironment: process.env }
|
|
)
|
|
if (candidate) {
|
|
return candidate.executablePath
|
|
}
|
|
}
|
|
} catch {
|
|
// Continue through PATH candidates.
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return undefined
|
|
}
|
|
|
|
export const resolveLegacyLocalToolEnvironmentPaths: LegacyLocalToolEnvironmentResolver =
|
|
async () => {
|
|
const [nodeExecutablePath, pythonExecutablePath] = await Promise.all([
|
|
resolveExecutable('node', ['node']),
|
|
resolveExecutable(
|
|
'python',
|
|
process.platform === 'win32'
|
|
? ['python', 'python3']
|
|
: ['python3', 'python']
|
|
)
|
|
])
|
|
return {
|
|
...(nodeExecutablePath ? { nodeExecutablePath } : {}),
|
|
...(pythonExecutablePath ? { pythonExecutablePath } : {})
|
|
}
|
|
}
|
|
|
|
export const defaultApplicationSettings: ApplicationSettings = {
|
|
checkUpdatesOnStartup: true,
|
|
updateSource: 'github',
|
|
modelDownloadSource: 'modelscope',
|
|
localToolEnvironment: defaultLocalToolEnvironmentSettings,
|
|
conversationHtmlRenderingEnabled: true,
|
|
remoteProjectsEnabled: false,
|
|
applicationNavigation: defaultApplicationNavigation,
|
|
localInferenceEnabled: true,
|
|
magicNotesEnabled: true,
|
|
heartbeatEnabled: false,
|
|
heartbeatReportTimeoutSeconds: defaultSupervisionTimeoutSeconds,
|
|
supervisorOrganizeTimeoutSeconds: defaultSupervisionTimeoutSeconds,
|
|
supervisorModelConcurrency: defaultSupervisorModelConcurrency,
|
|
magicNotesShowIncompleteTodoCount: true,
|
|
magicNoteCommentMode: 'immediate',
|
|
magicNoteCommentFormat: 'combined',
|
|
magicNoteCanvasPageCount: 1
|
|
}
|
|
|
|
export class ApplicationSettingsStore {
|
|
private settings?: StoredApplicationSettings
|
|
private settingsLoad?: Promise<StoredApplicationSettings>
|
|
private warnings: SettingsWarning[] = []
|
|
private updateQueue: Promise<void> = Promise.resolve()
|
|
private readonly listeners = new Set<(settings: ApplicationSettings) => void>()
|
|
|
|
constructor(
|
|
private readonly filePath: string,
|
|
private readonly legacyLocalToolEnvironmentResolver: LegacyLocalToolEnvironmentResolver = resolveLegacyLocalToolEnvironmentPaths
|
|
) {}
|
|
|
|
onChanged(listener: (settings: ApplicationSettings) => void): () => void {
|
|
this.listeners.add(listener)
|
|
return () => { this.listeners.delete(listener) }
|
|
}
|
|
|
|
private async migrateLegacy(
|
|
settings: Omit<
|
|
StoredApplicationSettings,
|
|
| 'conversationHtmlRenderingEnabled'
|
|
| 'localToolEnvironment'
|
|
| 'version'
|
|
| 'applicationNavigation'
|
|
| 'localInferenceEnabled'
|
|
| 'heartbeatEnabled'
|
|
| 'magicNoteCanvasPageCount'
|
|
> & {
|
|
conversationHtmlRenderingEnabled?: boolean
|
|
magicNoteCanvasPageCount?: number
|
|
version: number
|
|
}
|
|
): Promise<StoredApplicationSettings> {
|
|
const resolved = await this.legacyLocalToolEnvironmentResolver()
|
|
const selection = (
|
|
executablePath: string | undefined
|
|
): LocalToolEnvironmentSettings['node'] =>
|
|
executablePath &&
|
|
localToolExecutablePathSchema.safeParse(executablePath).success
|
|
? { source: 'custom', executablePath }
|
|
: { source: 'managed' }
|
|
const next: StoredApplicationSettings = {
|
|
...defaultApplicationSettings,
|
|
...settings,
|
|
conversationHtmlRenderingEnabled:
|
|
settings.conversationHtmlRenderingEnabled ?? true,
|
|
version: CURRENT_SETTINGS_VERSION,
|
|
localToolEnvironment: {
|
|
node: selection(resolved.nodeExecutablePath),
|
|
python: selection(resolved.pythonExecutablePath),
|
|
artifactDownloadSource: 'native'
|
|
}
|
|
}
|
|
await this.persist(next)
|
|
return next
|
|
}
|
|
|
|
private async isolateCorruptFile(): Promise<void> {
|
|
await isolateCorruptSettingsFile(
|
|
this.filePath,
|
|
'Application settings are corrupt and could not be isolated'
|
|
)
|
|
}
|
|
|
|
private async loadStored(): Promise<StoredApplicationSettings> {
|
|
if (this.settings) {
|
|
return this.settings
|
|
}
|
|
if (!this.settingsLoad) {
|
|
this.settingsLoad = this.readStored().finally(() => {
|
|
this.settingsLoad = undefined
|
|
})
|
|
}
|
|
return this.settingsLoad
|
|
}
|
|
|
|
private async readStored(): Promise<StoredApplicationSettings> {
|
|
try {
|
|
const contents = await readFile(this.filePath, 'utf8')
|
|
let parsed: unknown
|
|
try {
|
|
parsed = JSON.parse(contents) as unknown
|
|
} catch {
|
|
await this.isolateCorruptFile()
|
|
this.warnings = [{ code: 'application-settings-recovered' }]
|
|
this.settings = {
|
|
version: CURRENT_SETTINGS_VERSION,
|
|
lastSeenReleaseNotesVersion: null,
|
|
...defaultApplicationSettings
|
|
}
|
|
return this.settings
|
|
}
|
|
assertSupportedSettingsVersion(
|
|
parsed,
|
|
CURRENT_SETTINGS_VERSION,
|
|
(version) =>
|
|
`当前 GoodBuddy 不支持应用设置版本 ${version},请升级应用后重试`
|
|
)
|
|
const result = storedApplicationSettingsSchema.safeParse(parsed)
|
|
if (!result.success) {
|
|
const versionElevenResult = versionElevenStoredApplicationSettingsSchema.safeParse(parsed)
|
|
if (versionElevenResult.success) {
|
|
const next: StoredApplicationSettings = { ...versionElevenResult.data, version: CURRENT_SETTINGS_VERSION }
|
|
await this.persist(next)
|
|
return next
|
|
}
|
|
const versionTenResult =
|
|
versionTenStoredApplicationSettingsSchema.safeParse(parsed)
|
|
if (versionTenResult.success) {
|
|
const next: StoredApplicationSettings = {
|
|
...versionTenResult.data,
|
|
version: CURRENT_SETTINGS_VERSION,
|
|
conversationHtmlRenderingEnabled: true
|
|
}
|
|
await this.persist(next)
|
|
return next
|
|
}
|
|
const versionNineResult =
|
|
versionNineStoredApplicationSettingsSchema.safeParse(parsed)
|
|
if (versionNineResult.success) {
|
|
return this.migrateLegacy(versionNineResult.data)
|
|
}
|
|
const versionEightResult =
|
|
versionEightStoredApplicationSettingsSchema.safeParse(parsed)
|
|
if (versionEightResult.success) {
|
|
return this.migrateLegacy({
|
|
...versionEightResult.data,
|
|
version: CURRENT_SETTINGS_VERSION,
|
|
remoteProjectsEnabled: false
|
|
})
|
|
}
|
|
const versionSevenResult =
|
|
versionSevenStoredApplicationSettingsSchema.safeParse(parsed)
|
|
if (versionSevenResult.success) {
|
|
return this.migrateLegacy({
|
|
...versionSevenResult.data,
|
|
version: CURRENT_SETTINGS_VERSION,
|
|
remoteProjectsEnabled: false,
|
|
magicNotesShowIncompleteTodoCount: true
|
|
})
|
|
}
|
|
const versionSixResult =
|
|
versionSixStoredApplicationSettingsSchema.safeParse(parsed)
|
|
if (versionSixResult.success) {
|
|
return this.migrateLegacy({
|
|
...versionSixResult.data,
|
|
version: CURRENT_SETTINGS_VERSION,
|
|
modelDownloadSource: 'modelscope',
|
|
remoteProjectsEnabled: false,
|
|
magicNotesShowIncompleteTodoCount: true
|
|
})
|
|
}
|
|
const versionFiveResult =
|
|
versionFiveStoredApplicationSettingsSchema.safeParse(parsed)
|
|
if (versionFiveResult.success) {
|
|
return this.migrateLegacy({
|
|
...versionFiveResult.data,
|
|
version: CURRENT_SETTINGS_VERSION,
|
|
updateSource: 'github',
|
|
modelDownloadSource: 'modelscope',
|
|
remoteProjectsEnabled: false,
|
|
magicNotesShowIncompleteTodoCount: true
|
|
})
|
|
}
|
|
const versionFourResult =
|
|
versionFourStoredApplicationSettingsSchema.safeParse(parsed)
|
|
if (versionFourResult.success) {
|
|
return this.migrateLegacy({
|
|
...versionFourResult.data,
|
|
version: CURRENT_SETTINGS_VERSION,
|
|
updateSource: 'github',
|
|
modelDownloadSource: 'modelscope',
|
|
remoteProjectsEnabled: false,
|
|
magicNotesShowIncompleteTodoCount: true,
|
|
lastSeenReleaseNotesVersion: null
|
|
})
|
|
}
|
|
const versionThreeResult =
|
|
versionThreeStoredApplicationSettingsSchema.safeParse(parsed)
|
|
if (versionThreeResult.success) {
|
|
return this.migrateLegacy({
|
|
...versionThreeResult.data,
|
|
version: CURRENT_SETTINGS_VERSION,
|
|
updateSource: 'github',
|
|
modelDownloadSource: 'modelscope',
|
|
remoteProjectsEnabled: false,
|
|
magicNotesShowIncompleteTodoCount: true,
|
|
magicNoteCommentFormat: 'combined',
|
|
lastSeenReleaseNotesVersion: null
|
|
})
|
|
}
|
|
const versionTwoResult =
|
|
versionTwoStoredApplicationSettingsSchema.safeParse(parsed)
|
|
if (versionTwoResult.success) {
|
|
return this.migrateLegacy({
|
|
...versionTwoResult.data,
|
|
version: CURRENT_SETTINGS_VERSION,
|
|
updateSource: 'github',
|
|
modelDownloadSource: 'modelscope',
|
|
remoteProjectsEnabled: false,
|
|
magicNotesShowIncompleteTodoCount: true,
|
|
magicNoteCommentMode: 'immediate',
|
|
magicNoteCommentFormat: 'combined',
|
|
lastSeenReleaseNotesVersion: null
|
|
})
|
|
}
|
|
const legacyResult =
|
|
legacyStoredApplicationSettingsSchema.safeParse(parsed)
|
|
if (legacyResult.success) {
|
|
return this.migrateLegacy({
|
|
version: CURRENT_SETTINGS_VERSION,
|
|
checkUpdatesOnStartup: legacyResult.data.checkUpdatesOnStartup,
|
|
updateSource: 'github',
|
|
modelDownloadSource: 'modelscope',
|
|
remoteProjectsEnabled: false,
|
|
magicNotesEnabled: true,
|
|
magicNotesShowIncompleteTodoCount: true,
|
|
magicNoteCommentMode: 'immediate',
|
|
magicNoteCommentFormat: 'combined',
|
|
lastSeenReleaseNotesVersion: null
|
|
})
|
|
}
|
|
await this.isolateCorruptFile()
|
|
this.warnings = [{ code: 'application-settings-recovered' }]
|
|
this.settings = {
|
|
version: CURRENT_SETTINGS_VERSION,
|
|
lastSeenReleaseNotesVersion: null,
|
|
...defaultApplicationSettings
|
|
}
|
|
return this.settings
|
|
}
|
|
this.settings = result.data
|
|
} catch (error) {
|
|
if (error instanceof UnsupportedSettingsVersionError) {
|
|
throw error
|
|
}
|
|
if (!isMissingFileError(error)) {
|
|
throw new Error('Application settings could not be read', {
|
|
cause: error
|
|
})
|
|
}
|
|
this.settings = {
|
|
version: CURRENT_SETTINGS_VERSION,
|
|
lastSeenReleaseNotesVersion: null,
|
|
...defaultApplicationSettings
|
|
}
|
|
}
|
|
return this.settings
|
|
}
|
|
|
|
async get(): Promise<ApplicationSettings> {
|
|
const stored = await this.loadStored()
|
|
return {
|
|
checkUpdatesOnStartup: stored.checkUpdatesOnStartup,
|
|
updateSource: stored.updateSource,
|
|
modelDownloadSource: stored.modelDownloadSource,
|
|
localToolEnvironment: stored.localToolEnvironment,
|
|
conversationHtmlRenderingEnabled:
|
|
stored.conversationHtmlRenderingEnabled,
|
|
remoteProjectsEnabled: stored.remoteProjectsEnabled,
|
|
applicationNavigation: stored.applicationNavigation,
|
|
localInferenceEnabled: stored.localInferenceEnabled,
|
|
magicNotesEnabled: stored.magicNotesEnabled,
|
|
heartbeatEnabled: stored.heartbeatEnabled ?? false,
|
|
heartbeatReportTimeoutSeconds: stored.heartbeatReportTimeoutSeconds ?? defaultSupervisionTimeoutSeconds,
|
|
supervisorOrganizeTimeoutSeconds: stored.supervisorOrganizeTimeoutSeconds ?? defaultSupervisionTimeoutSeconds,
|
|
supervisorModelConcurrency: stored.supervisorModelConcurrency ?? defaultSupervisorModelConcurrency,
|
|
supervisionReview: stored.supervisionReview,
|
|
magicNotesShowIncompleteTodoCount:
|
|
stored.magicNotesShowIncompleteTodoCount,
|
|
magicNoteCommentMode: stored.magicNoteCommentMode,
|
|
magicNoteCommentFormat: stored.magicNoteCommentFormat,
|
|
magicNoteCanvasPageCount: stored.magicNoteCanvasPageCount,
|
|
...(this.warnings.length > 0 ? { warnings: [...this.warnings] } : {})
|
|
}
|
|
}
|
|
|
|
async getLastSeenReleaseNotesVersion(): Promise<string | null> {
|
|
return (await this.loadStored()).lastSeenReleaseNotesVersion
|
|
}
|
|
|
|
private async persist(next: StoredApplicationSettings): Promise<void> {
|
|
await writeJsonFileAtomically(this.filePath, next)
|
|
this.settings = next
|
|
}
|
|
|
|
update(input: unknown): Promise<ApplicationSettings> {
|
|
const operation = this.updateQueue.then(async () => {
|
|
const updates = applicationSettingsUpdateSchema.parse(input)
|
|
const current = await this.loadStored()
|
|
const next: StoredApplicationSettings = {
|
|
...current,
|
|
...updates,
|
|
version: CURRENT_SETTINGS_VERSION
|
|
}
|
|
await this.persist(next)
|
|
this.warnings = []
|
|
const settings: ApplicationSettings = {
|
|
checkUpdatesOnStartup: next.checkUpdatesOnStartup,
|
|
updateSource: next.updateSource,
|
|
modelDownloadSource: next.modelDownloadSource,
|
|
localToolEnvironment: next.localToolEnvironment,
|
|
conversationHtmlRenderingEnabled:
|
|
next.conversationHtmlRenderingEnabled,
|
|
remoteProjectsEnabled: next.remoteProjectsEnabled,
|
|
applicationNavigation: next.applicationNavigation,
|
|
localInferenceEnabled: next.localInferenceEnabled,
|
|
magicNotesEnabled: next.magicNotesEnabled,
|
|
heartbeatEnabled: next.heartbeatEnabled ?? false,
|
|
heartbeatReportTimeoutSeconds: next.heartbeatReportTimeoutSeconds ?? defaultSupervisionTimeoutSeconds,
|
|
supervisorOrganizeTimeoutSeconds: next.supervisorOrganizeTimeoutSeconds ?? defaultSupervisionTimeoutSeconds,
|
|
supervisorModelConcurrency: next.supervisorModelConcurrency ?? defaultSupervisorModelConcurrency,
|
|
supervisionReview: next.supervisionReview,
|
|
magicNotesShowIncompleteTodoCount:
|
|
next.magicNotesShowIncompleteTodoCount,
|
|
magicNoteCommentMode: next.magicNoteCommentMode,
|
|
magicNoteCommentFormat: next.magicNoteCommentFormat,
|
|
magicNoteCanvasPageCount: next.magicNoteCanvasPageCount
|
|
}
|
|
for (const listener of this.listeners) listener(settings)
|
|
return settings
|
|
})
|
|
this.updateQueue = operation.then(
|
|
() => undefined,
|
|
() => undefined
|
|
)
|
|
return operation
|
|
}
|
|
|
|
setLastSeenReleaseNotesVersion(version: unknown): Promise<void> {
|
|
const operation = this.updateQueue.then(async () => {
|
|
const parsedVersion = releaseVersionSchema.parse(version)
|
|
const current = await this.loadStored()
|
|
if (current.lastSeenReleaseNotesVersion === parsedVersion) {
|
|
return
|
|
}
|
|
await this.persist({
|
|
...current,
|
|
version: CURRENT_SETTINGS_VERSION,
|
|
lastSeenReleaseNotesVersion: parsedVersion
|
|
})
|
|
})
|
|
this.updateQueue = operation.then(
|
|
() => undefined,
|
|
() => undefined
|
|
)
|
|
return operation
|
|
}
|
|
}
|