( config: MagicContextConfig, )
| 372 | } |
| 373 | |
| 374 | export function resolveHistorianFromConfig( |
| 375 | config: MagicContextConfig, |
| 376 | ): PiHistorianOptions | undefined { |
| 377 | // Defensive: schema declares `historian` required with default {}, but the |
| 378 | // runtime config can come from a malformed JSONC merge that drops the |
| 379 | // field. Fall back to undefined-safe access so plugin load never crashes. |
| 380 | const historian = config.historian as HistorianConfig | undefined; |
| 381 | if (historian?.disable === true) return undefined; |
| 382 | const model = historian?.model?.trim(); |
| 383 | if (!model || model.length === 0) return undefined; |
| 384 | |
| 385 | // The historian chunk budget is anchored to the HISTORIAN model because |
| 386 | // it bounds one summarizer call. The trigger budget is intentionally NOT |
| 387 | // derived at startup: Pi resolves it per context pass from the live main |
| 388 | // session model + effective execute threshold to match OpenCode. |
| 389 | const historianContextLimit = resolveHistorianContextLimit(model); |
| 390 | const historianChunkTokens = deriveHistorianChunkTokens( |
| 391 | historianContextLimit, |
| 392 | ); |
| 393 | |
| 394 | const fallbackModels = resolveFallbackChain(historian?.fallback_models); |
| 395 | |
| 396 | return { |
| 397 | runner: new PiSubagentRunner(), |
| 398 | model, |
| 399 | fallbackModels, |
| 400 | historianChunkTokens, |
| 401 | timeoutMs: config.historian_timeout_ms, |
| 402 | // `historian.two_pass` runs an editor pass after a successful |
| 403 | // first pass to clean low-signal U: lines and cross-compartment |
| 404 | // duplicates. Mirrors OpenCode's config flag — defaults to false |
| 405 | // on the schema side because the editor pass adds a second |
| 406 | // historian round-trip's latency and token cost. Enable for |
| 407 | // long sessions where chunk dedupe matters more than speed. |
| 408 | twoPass: historian?.two_pass === true, |
| 409 | // Pi only: explicit thinking level for historian subagent invocations. |
| 410 | // When set, passed as --thinking <level> to Pi subprocess. |
| 411 | // Required for providers like GitHub Copilot that apply bad defaults. |
| 412 | thinkingLevel: historian?.thinking_level, |
| 413 | executeThresholdPercentage: config.execute_threshold_percentage, |
| 414 | executeThresholdTokens: config.execute_threshold_tokens, |
| 415 | commitClusterTrigger: config.commit_cluster_trigger, |
| 416 | protectedTags: config.protected_tags, |
| 417 | clearReasoningAge: config.clear_reasoning_age, |
| 418 | historyBudgetPercentage: config.history_budget_percentage, |
| 419 | memoryEnabled: config.memory.enabled, |
| 420 | autoPromote: config.memory.auto_promote, |
| 421 | userMemoriesEnabled: userMemoryCollectionEnabled(config.dreamer), |
| 422 | language: config.language, |
| 423 | }; |
| 424 | } |
| 425 | |
| 426 | function resolveAutoSearchFromConfig( |
| 427 | config: MagicContextConfig, |
no test coverage detected