wave 2 (platform stream): settings storage & plumbing foundation (P2)

- src/core/settings.ts: versioned DELTA record schema + sanitizer (unknown keys preserved)
  — general/chat/agent{models,tools,prompts}/appearance/graph sections
- src/core/settings-resolution.ts: pure resolveSettings (factory defaults → user delta),
  extraction/graph-context channel predicates
- src/store/settings.ts + prompt-overrides.ts: SettingsStore/PromptOverrideStore adapters
  wired through stores.ts (effectiveSettings() fresh per call); lazy migration of
  default-settings.json into record.chat
- app/api/settings/** routes (record, section bodies, prompt overrides per segment)
- chat-handler: graph gates at trigger call-sites + toolToggles injection seam
- config.ts: SETTINGS_FILE + PROMPTS_OVERRIDE_DIR paths

587/587 tests; typecheck/lint/build clean
This commit is contained in:
amosblomqvist
2026-09-19 18:05:30 +03:00
parent 36c5d19437
commit cb4fe25c89
24 changed files with 2096 additions and 101 deletions
+16 -4
View File
@@ -18,6 +18,7 @@ import { getSupportedThinkingLevels, type Api, type Model } from "@earendil-work
import type { ThinkingLevel } from "@earendil-works/pi-agent-core";
import { MODELS } from "../../config.ts";
import { piModels } from "../agent/models.ts";
import { settingsStore } from "../store/settings.ts";
import type { ChatSettings } from "../core/contracts/chat.ts";
import type {
CatalogDefaults,
@@ -28,14 +29,25 @@ import type {
/** The picker's client-safe model entry (contract: core/contracts/models.ts). */
export type ModelDto = ModelCatalogEntry;
/** The conversation agents' config pin — what a settings-less chat runs. */
export function catalogDefaults(): CatalogDefaults {
/** The conversation agents' resolved default pin (pure form — the record's
* chat section in, CatalogDefaults out; `catalogDefaults()` below is the
* fs wrapper). Absent fields (or no record — BYOK first boot) fall back
* to the config pin, exactly as before. */
export function catalogDefaultsFor(chat?: ChatSettings): CatalogDefaults {
return {
model: { provider: "openrouter", id: MODELS.prober.model },
thinkingLevel: MODELS.prober.effort as ThinkingLevel,
model: chat?.model ?? { provider: "openrouter", id: MODELS.prober.model },
thinkingLevel: (chat?.thinkingLevel ?? MODELS.prober.effort) as ThinkingLevel,
};
}
/** The conversation agents' config pin — what a settings-less chat runs.
* (settings-storage wave, 2026-09-19): the user's picked default rides in
* the settings record's `chat` section; the picker's defaults row shows
* it once chosen. */
export function catalogDefaults(): CatalogDefaults {
return catalogDefaultsFor(settingsStore.readSettings()?.chat);
}
/** Project one catalog model to the client DTO. */
export function toModelDto(model: Model<Api>): ModelDto {
return {