/** * peaks-loop 2.0 project-local preferences schema. * Per spec §8.4 — per-project state lives in `.peaks/preferences.json`, * NOT in `~/.peaks/config.json` (which is slim global). * */ export declare const PREFERENCES_SCHEMA_VERSION = "2.0.0"; /** * Per-task-level UA install UX prompt decision. * Values: * - 'unset' — ask every session (default) * - 'skip-this-session' — skip prompt for current session only * - 'skip-forever' — never ask, never install */ export type UaPromptDecision = 'unset' | 'skip-this-session' | 'skip-forever'; /** * L1a task classification conservatism. * Values: * - 'default' — use default signal thresholds * - 'strict' — always upgrade to next level (slower, safer) * - 'lax' — always downgrade to previous level (faster, riskier) */ export type ClassifyConservatism = 'default' | 'strict' | 'lax'; /** * The auto-compact profile a project can pin. * * PUBLIC: any project, in any mode, can choose the compaction cadence. The * profile is a THRESHOLD choice — `eager` fires earlier than `standard` — * and it is set with * `peaks preferences set --key autoCompactProfile --value eager`. * * The value set is closed on purpose: an unknown value is rejected at write * time with the legal ones named, rather than stored and read back as * something else. */ export type AutoCompactProfilePreference = 'standard' | 'eager'; /** The legal `autoCompactProfile` values, in the order the error message lists them. */ export declare const AUTO_COMPACT_PROFILE_VALUES: readonly AutoCompactProfilePreference[]; /** * The `isFanoutMode` paradigm for the auto-compact profile: one guard, one * closed set, so a caller never has to re-spell the legal values. */ export declare function isAutoCompactProfile(value: unknown): value is AutoCompactProfilePreference; export interface ClassifyRuleOverrides { /** File count threshold above which a task is promoted to 'feature' */ readonly feature_threshold_files?: number; /** Line count threshold above which a task is promoted to 'feature' */ readonly feature_threshold_lines?: number; /** Whether to require a 24h grace before cleaning recently-active sessions */ readonly runtime_clean_grace_hours?: number; } export interface SwarmSpeculativePreferences { /** Whether speculative dispatch is enabled. Default: true */ readonly enabled: boolean; readonly maxConcurrent: number; /** Min hit rate below which speculative auto-disables. Default: 0.5 */ readonly minHitRate: number; } export interface ProjectPreferences { /** * On-disk schema version. The JSON key is `schema_version` (snake_case) — this matches the * raw on-disk key in `.peaks/preferences.json`, NOT the camelCase used by the rest of this * interface. preferences-service.ts validates this value against PREFERENCES_SCHEMA_VERSION * on load and writes the current value on save. Any mismatch throws PREFERENCES_SCHEMA_MISMATCH. */ readonly schema_version: typeof PREFERENCES_SCHEMA_VERSION; /** * **MODEL-SELECTION** knob only — it chooses between the cheap * configured provider (`getConfiguredExecutionModelId`) and the * strongest planner/reviewer model (`getStrongestModelId(...)`). It does * NOT throttle concurrency, fan-out, or worker count. Per user * direction 2026-06-28: "效率比省钱更重要,是在效率达到最大值的时 * 候,再去考虑经济问题" (efficiency first; economy is only considered * after efficiency is maxed out). * * Concurrency is governed by: * - `swarmMode` (project-policy opt-out for the swarm subgraph), * - `fanout.defaultMode` (HARD constraint: `'fan-out'` per slice * 2026-06-24-audit-5th-p2), * - the slice DAG leaf count (≥ 2 leaves at one topological level * forces parallel dispatch via `peaks sub-agent dispatch --from-dag`). * * Even with `economyMode: true` AND `swarmMode: false`, a 2-leaf DAG * MUST dispatch in parallel — the `fanout-mandatory` rule is * unconditional. Economy ≠ concurrency. */ readonly economyMode: boolean; /** * (peaks-rd/qa worker graph) is generated at all. It does NOT * control fan-out. Fan-out is governed by the slice DAG + the * `fanout.defaultMode` preference; swarmMode only decides the * dispatch *shape* (worker graph vs flat dispatch). */ readonly swarmMode: boolean; /** * Legacy UA opt-in prompt decision. The UA knowledge-graph surface was * removed in 2026-09-09 (project analysis is codegraph-first); the field * is retained only so legacy `.peaks/preferences.json` files keep parsing. */ readonly uaPrompt: UaPromptDecision; /** * Legacy AgentShield opt-in prompt decision. The ECC AgentShield * subprocess was removed in 4.0.0-beta.11; retained for legacy parse. */ readonly agentShieldPrompt: UaPromptDecision; readonly classifyConservatism: ClassifyConservatism; readonly classifyRules: ClassifyRuleOverrides; readonly swarmSpeculative: SwarmSpeculativePreferences; /** * The auto-compact profile this project pins: `standard` (the v2.13.0 * zero-pause contract thresholds) or `eager` (fires earlier). * * OPTIONAL on purpose. The field is additive, so an existing * `.peaks/preferences.json` that does not carry it keeps loading — the * schema version is unchanged, and a file without the key resolves to the * default profile rather than to a load error. */ readonly autoCompactProfile?: AutoCompactProfilePreference; /** Loop Autonomous (L4 14.5) toggle. Default: false — never auto-enable. */ readonly loopAutonomousEnabled: boolean; /** * L2.3 P2-a: ECC AgentShield subprocess toggle — **frozen, no-op**. * * Slice 3 of 4.0.0-beta.11 removed the ECC AgentShield subprocess * entirely (upstream ships no `ecc-agentshield` binary). The field is * retained only so legacy `.peaks/preferences.json` files still parse * (`static-service.ts` calls `loadPreferences` for back-compat and * ignores the value). Setting it to `true` has no effect. * * Default: false. */ readonly agentShieldEnabled: boolean; /** * The previous opt-out (`defaultMode = 'serial'`) is removed by user * direction. Single-sub-agent dispatch is no longer permitted when * the slice DAG has ≥ 2 leaves at the same topological level; the * LLM-side runner MUST use `peaks sub-agent dispatch --from-dag` * (N parallel `buildToolCall` per dispatch). * * The field is kept on `ProjectPreferences` for backward-compatible * load (legacy preferences.json files still parse) but the runtime * shape is fixed: `defaultMode === 'fan-out'`. Any other value in a * saved file is rejected at load time. */ readonly fanout: FanoutPreference; /** * preflight knobs. Optional — `resolveMemoryPreflightConfig` (see * src/services/context/memory-preflight-config.ts) merges over a hard-coded * default block when this key is absent, so legacy preferences.json files * keep working unchanged. */ readonly memoryPreflight?: { readonly enabled?: boolean; /** Back-compat token cap; converted to bytes as `maxTokens * 4`. */ readonly maxTokens?: number; /** Back-compat hot item cap; `hotItemCap` overrides it when set. */ readonly listCap?: number; readonly contentCacheBytes?: number; readonly maxBytes?: number; readonly hotItemCap?: number; readonly warmItemCap?: number; readonly warmMinTokenHits?: number; readonly selectionTimeBudgetMs?: number; readonly includeBodies?: boolean; }; } export type FanoutMode = 'fan-out'; export declare const FANOUT_MODES: readonly FanoutMode[]; /** * narrowed the closed set from `['fan-out','serial']` to `['fan-out']` * — stale preferences.json files with `"serial"` must now fail-fast at * load (see `preferences-service.ts`) instead of being silently coerced. */ export declare function isFanoutMode(value: unknown): value is FanoutMode; export interface FanoutPreference { readonly defaultMode: FanoutMode; } export declare const DEFAULT_PREFERENCES: ProjectPreferences;