@x-otto/setting 0.0.1-alpha.2 → 0.0.1-alpha.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  > Configuration loading, merging, validation, migration, and monitoring. Single source of truth for the config shape via zod schema.
4
4
 
5
- `@x-otto/setting` manages the entire configuration lifecycle: loading from file/env/memory sources, deep merging with precedence layering, version migration, validation with lenient error handling, file watching, and remote sync. The config shape is defined by a canonical zod schema (`schema.ts`) from which the `Setting` type and `SETTING_KEYS` are derived. Supports 2 built-in Reviewer Agent presets (spec-reviewer, quality-reviewer).
5
+ `@x-otto/setting` manages the entire configuration lifecycle: loading from file/env/memory sources, deep merging with precedence layering, version migration, validation with lenient error handling, and remote sync. The config shape is defined by a canonical zod schema (`schema.ts`) from which the `Setting` type and `SETTING_KEYS` are derived. Supports 2 built-in Reviewer Agent presets (spec-reviewer, quality-reviewer).
6
6
 
7
7
  ## Installation
8
8
 
@@ -25,13 +25,6 @@ const manager = new SettingsManager({
25
25
  await manager.load()
26
26
  console.log(manager.get('model')) // current model setting
27
27
  console.log(manager.config) // merged full config
28
-
29
- // Watch for file changes
30
- const { createSettingWatcher } = await import('@x-otto/setting')
31
- const watcher = createSettingWatcher({
32
- paths: ['/path/to/project/.otto/config.json'],
33
- reload: (path) => manager.reload(),
34
- })
35
28
  ```
36
29
 
37
30
  ## API
@@ -41,23 +34,18 @@ const watcher = createSettingWatcher({
41
34
  - `load()` / `reload()` — load/reload from store + env
42
35
  - `update(overrides)` — apply runtime overrides
43
36
  - `get(key)` — typed access to specific settings
44
- - `config` / `snapshot` — full merged config access
45
- - `model` / `compaction` / `session` — convenience accessors
46
- - Events: `'change'` — emitted on config change
37
+ - `config` / `model` — full merged config and model accessor
38
+ - Events: `'change'` emitted on config change; `'remote-sync-failed'` — remote layer read failure
47
39
 
48
40
  ### SettingLoader
49
41
  - `loadSetting(options)` — multi-source load + merge + validate
50
- - `SettingLoader.save(config)` — write back to store
42
+ - `SettingLoader` — loader class (no write-back API; persistence is handled by stores)
51
43
 
52
44
  ### Stores
53
45
  - `RawFileSettingStore` — literal file path read/write (with `~/` expansion)
54
46
  - `RemoteSettingStore` — HTTP remote config fetching
55
47
  - `SettingStore` interface — pluggable storage
56
48
 
57
- ### Watcher
58
- - `createSettingWatcher(options)` — `fs.watch` with per-path debounce (300ms default)
59
- - Implements `Disposable` (`Symbol.dispose`)
60
-
61
49
  ### Migration
62
50
  - `migrate(config, migrations)` — pure function, semver-ordered migration chain
63
51
  - Appends `_migrations` log on each step
@@ -80,7 +68,7 @@ const watcher = createSettingWatcher({
80
68
  ### Built-in Presets
81
69
  - `BUILTIN_REVIEWER_AGENTS` — 2 reviewer agent presets
82
70
  - `DEFAULT_CONFIG` — complete default configuration
83
- - `BUILTIN_AGENT_PROFILES` — agent profile presets
71
+ - `BUILTIN_AGENT_PROFILES` — agent profile presets (removed; empty array — custom agents go through declarative `.md` files)
84
72
  - `HIGH_RISK_PROJECT_KEYS` / `sanitizeUntrustedProjectLayer` — project trust model
85
73
 
86
74
  ### Constants
@@ -90,7 +78,7 @@ const watcher = createSettingWatcher({
90
78
 
91
79
  ## Dependencies
92
80
 
93
- - Internal: `@x-otto/env`, `@x-otto/shared`, `@x-otto/hook-contracts`, `@x-otto/orchestration-contracts`, `@x-otto/plugin`, `@x-otto/prompt`
81
+ - Internal: `@x-otto/env`, `@x-otto/shared`, `@x-otto/hook-contracts`, `@x-otto/orchestration-contracts`, `@x-otto/persistence`, `@x-otto/plugin`, `@x-otto/prompt`
94
82
  - External: `semver`, `zod`
95
83
 
96
84
  ## Related
package/dist/index.d.ts CHANGED
@@ -1396,12 +1396,19 @@ declare const SettingSchema: z.ZodObject<{
1396
1396
  every_n_turns: z.ZodCatch<z.ZodOptional<z.ZodNumber>>;
1397
1397
  max_lessons: z.ZodCatch<z.ZodOptional<z.ZodNumber>>;
1398
1398
  similarity_threshold: z.ZodCatch<z.ZodOptional<z.ZodNumber>>;
1399
+ min_relevant_hits: z.ZodCatch<z.ZodOptional<z.ZodNumber>>;
1399
1400
  }, z.core.$strip>>>;
1400
1401
  skill_loop: z.ZodCatch<z.ZodOptional<z.ZodObject<{
1401
1402
  enabled: z.ZodCatch<z.ZodOptional<z.ZodBoolean>>;
1402
1403
  min_messages: z.ZodCatch<z.ZodOptional<z.ZodNumber>>;
1403
1404
  dismiss_cooldown_days: z.ZodCatch<z.ZodOptional<z.ZodNumber>>;
1404
1405
  }, z.core.$strip>>>;
1406
+ skill_internalization: z.ZodCatch<z.ZodOptional<z.ZodObject<{
1407
+ enabled: z.ZodCatch<z.ZodOptional<z.ZodBoolean>>;
1408
+ min_executions: z.ZodCatch<z.ZodOptional<z.ZodNumber>>;
1409
+ min_success_rate: z.ZodCatch<z.ZodOptional<z.ZodNumber>>;
1410
+ max_internalized: z.ZodCatch<z.ZodOptional<z.ZodNumber>>;
1411
+ }, z.core.$strip>>>;
1405
1412
  notification: z.ZodCatch<z.ZodOptional<z.ZodObject<{
1406
1413
  enabled: z.ZodCatch<z.ZodOptional<z.ZodBoolean>>;
1407
1414
  sound: z.ZodCatch<z.ZodOptional<z.ZodBoolean>>;
@@ -1636,7 +1643,7 @@ type SandboxConfig = z.infer<typeof sandboxConfigSchema>;
1636
1643
  type ProxyConfig = z.infer<typeof proxyConfigSchema>;
1637
1644
  type McpServerSetting = z.infer<typeof mcpServerSettingSchema>;
1638
1645
  type McpServersConfig = z.infer<typeof SettingSchema>['mcp_servers'];
1639
- declare const SETTING_KEYS: ("model" | "categories" | "prompt_output_token_budget" | "prompt_wall_clock_budget_ms" | "session_cost_budget_usd" | "stall_detection" | "config_version" | "version" | "log_level" | "model_fallback" | "recent_models" | "recent_mentions" | "shortcuts" | "feedback_repo" | "keybinding_overrides" | "language" | "theme" | "theme_overrides" | "theme_preset" | "nickname" | "has_completed_onboarding" | "has_completed_wizard" | "wizard_dont_show_again" | "first_prompt_submitted_at" | "thinking_level" | "agents" | "tool_preset" | "memory_auto_extract" | "skill_loop" | "notification" | "pulse_survey" | "history" | "workflow" | "model_slots" | "mcp_servers" | "resilience" | "sandbox" | "residency" | "disabled_agents" | "disabled_hooks" | "disabled_tools" | "disabled_skills" | "disabled_plugins" | "installed_plugins" | "auto_update_check" | "last_update_check" | "todo_continue_max" | "permission_mode" | "permissions" | "permission_always_allow" | "plugin_lifecycle" | "proxy" | "web_fetch_allowed_hosts" | "_migrations")[];
1646
+ declare const SETTING_KEYS: ("model" | "categories" | "prompt_output_token_budget" | "prompt_wall_clock_budget_ms" | "session_cost_budget_usd" | "stall_detection" | "config_version" | "version" | "log_level" | "model_fallback" | "recent_models" | "recent_mentions" | "shortcuts" | "feedback_repo" | "keybinding_overrides" | "language" | "theme" | "theme_overrides" | "theme_preset" | "nickname" | "has_completed_onboarding" | "has_completed_wizard" | "wizard_dont_show_again" | "first_prompt_submitted_at" | "thinking_level" | "agents" | "tool_preset" | "memory_auto_extract" | "skill_loop" | "skill_internalization" | "notification" | "pulse_survey" | "history" | "workflow" | "model_slots" | "mcp_servers" | "resilience" | "sandbox" | "residency" | "disabled_agents" | "disabled_hooks" | "disabled_tools" | "disabled_skills" | "disabled_plugins" | "installed_plugins" | "auto_update_check" | "last_update_check" | "todo_continue_max" | "permission_mode" | "permissions" | "permission_always_allow" | "plugin_lifecycle" | "proxy" | "web_fetch_allowed_hosts" | "_migrations")[];
1640
1647
  type SettingKey = keyof Setting;
1641
1648
  //#endregion
1642
1649
  //#region src/types.d.ts
@@ -1671,10 +1678,34 @@ declare function migrate(config: Record<string, unknown>, migrations?: Migration
1671
1678
  applied: string[];
1672
1679
  };
1673
1680
  //#endregion
1681
+ //#region src/config-origins.d.ts
1682
+ /**
1683
+ * config-origins.ts —— RFC review D11:配置来源溯源(codex config/fingerprint.rs
1684
+ * record_origins 移植)。
1685
+ *
1686
+ * 与 `setting.ts` 的 merge 链并行构建一棵「来源树」:每个配置字段记录其最终值的来源层。
1687
+ * 用途:`otto doctor` / 诊断面回答「这个值来自哪层」(多层配置合并时最常见的排障问题)。
1688
+ *
1689
+ * 层序(低→高,与 setting.ts merge 调用序一致):
1690
+ * 'default' < 'extra:N' < 'global' < 'project' < 'env'
1691
+ *
1692
+ * 纯函数、无 IO(与 merge 同构);数组/对象深合并语义与 merge 逐字对齐。
1693
+ */
1694
+ /** 配置层名:'default' | 'global' | 'project' | 'env' | 'extra:N'(N = extraLayers 下标)。 */
1695
+ type ConfigLayerName = 'default' | 'global' | 'project' | 'env' | `extra:${number}`;
1696
+ /** 来源树:与配置树同构;叶子 = 该字段最终值的来源层名。 */
1697
+ interface ConfigOrigins {
1698
+ [key: string]: ConfigLayerName | ConfigOrigins;
1699
+ }
1700
+ //#endregion
1674
1701
  //#region src/setting.d.ts
1675
1702
  declare class SettingLoader {
1676
1703
  private store?;
1704
+ /** RFC review D11:上次 load 的来源树(惰性快照,供诊断面查询「值来自哪层」)。 */
1705
+ private lastOrigins?;
1677
1706
  constructor(store?: SettingStore);
1707
+ /** RFC review D11:上次 load 的配置来源树(未 load 过返回 undefined)。 */
1708
+ getOrigins(): ConfigOrigins | undefined;
1678
1709
  load(options?: LoadSettingOptions): Promise<Setting>;
1679
1710
  }
1680
1711
  declare function loadSetting(options?: LoadSettingOptions): Promise<Setting>;
@@ -1766,7 +1797,7 @@ declare class RemoteSettingStore implements SettingStore {
1766
1797
  *
1767
1798
  * 与 SettingSchema 同包维护,字段增删时编译期提醒同步检查(R2)。
1768
1799
  */
1769
- declare const SYNCABLE_KEYS: Set<"model" | "categories" | "prompt_output_token_budget" | "prompt_wall_clock_budget_ms" | "session_cost_budget_usd" | "stall_detection" | "config_version" | "version" | "log_level" | "model_fallback" | "recent_models" | "recent_mentions" | "shortcuts" | "feedback_repo" | "keybinding_overrides" | "language" | "theme" | "theme_overrides" | "theme_preset" | "nickname" | "has_completed_onboarding" | "has_completed_wizard" | "wizard_dont_show_again" | "first_prompt_submitted_at" | "thinking_level" | "agents" | "tool_preset" | "memory_auto_extract" | "skill_loop" | "notification" | "pulse_survey" | "history" | "workflow" | "model_slots" | "mcp_servers" | "resilience" | "sandbox" | "residency" | "disabled_agents" | "disabled_hooks" | "disabled_tools" | "disabled_skills" | "disabled_plugins" | "installed_plugins" | "auto_update_check" | "last_update_check" | "todo_continue_max" | "permission_mode" | "permissions" | "permission_always_allow" | "plugin_lifecycle" | "proxy" | "web_fetch_allowed_hosts" | "_migrations">;
1800
+ declare const SYNCABLE_KEYS: Set<"model" | "categories" | "prompt_output_token_budget" | "prompt_wall_clock_budget_ms" | "session_cost_budget_usd" | "stall_detection" | "config_version" | "version" | "log_level" | "model_fallback" | "recent_models" | "recent_mentions" | "shortcuts" | "feedback_repo" | "keybinding_overrides" | "language" | "theme" | "theme_overrides" | "theme_preset" | "nickname" | "has_completed_onboarding" | "has_completed_wizard" | "wizard_dont_show_again" | "first_prompt_submitted_at" | "thinking_level" | "agents" | "tool_preset" | "memory_auto_extract" | "skill_loop" | "skill_internalization" | "notification" | "pulse_survey" | "history" | "workflow" | "model_slots" | "mcp_servers" | "resilience" | "sandbox" | "residency" | "disabled_agents" | "disabled_hooks" | "disabled_tools" | "disabled_skills" | "disabled_plugins" | "installed_plugins" | "auto_update_check" | "last_update_check" | "todo_continue_max" | "permission_mode" | "permissions" | "permission_always_allow" | "plugin_lifecycle" | "proxy" | "web_fetch_allowed_hosts" | "_migrations">;
1770
1801
  /**
1771
1802
  * RFC-052 D2:绝不可远程同步的字段(安全面/本地路径/凭据)。
1772
1803
  *
@@ -2055,6 +2086,14 @@ interface ConfigExportFile {
2055
2086
  /** 导出侧的配置 scope('global' = 用户级 `~/.otto/config.json`,'project' = 项目级)。 */
2056
2087
  scope: 'global' | 'project';
2057
2088
  fields: Partial<Setting>;
2089
+ /**
2090
+ * RFC review D13:脱敏 sidecar(可选,向后兼容)——`mcp_servers.*.env/headers`
2091
+ * 在导出时被剥离,此处记录位置与「是否曾持有值」。缺省(旧导出产物)无此字段。
2092
+ */
2093
+ redacted_secrets?: Array<{
2094
+ path: string[];
2095
+ set: boolean;
2096
+ }>;
2058
2097
  }
2059
2098
  /**
2060
2099
  * 从当前配置构建导出产物(RFC-219 重要事项规则 9)。
@@ -2081,5 +2120,5 @@ declare class ExportFileParseError extends Error {
2081
2120
  */
2082
2121
  declare function parseExportFile(raw: string): ConfigExportFile;
2083
2122
  //#endregion
2084
- export { type AgentOptions, BUILTIN_AGENT_PROFILES, BUILTIN_REVIEWER_AGENTS, CURRENT_EXPORT_VERSION, type CategoryConfig, type ConfigExportFile, DEFAULT_CONFIG, EXCLUDED_KEYS, ExportFileParseError, ExportFileVersionError, type ExternalHistoryConfig, FIELD_CLASSIFICATION, type FieldClassification, type FieldDiff, HIGH_RISK_PROJECT_KEYS, type HistoryConfig, type InstalledPluginEntry, LOG_LEVELS, type LoadSettingOptions, type LogLevel, type MarkdownThemeOverridesSetting, type McpServerSetting, type McpServersConfig, type MergeCategory, type Migration, NEVER_SYNCED_KEYS, type NotificationConfig, PERMISSION_MODES, type PermissionMode, type PermissionPolicySetting, type PermissionRuleConfig, type PluginDiff, type ProxyConfig, RawFileSettingStore, type RecentMentionEntry, RemoteSettingStore, SETTING_KEYS, SYNCABLE_KEYS, type SandboxConfig, type SanitizeResult, type Setting, type SettingFromSchema, type SettingKey, SettingLoader, type SettingStore, type SettingWarning, SettingsManager, type SettingsOptions, type StorageType, THEME_COLOR_ROLES, TOOL_PRESETS, type ThemeColorRole, type ThemeOverridesSetting, type ThemePresetSelectionSetting, type ToolPreset, applyMergeStrategy, assertFieldClassificationCoversAllKeys, buildExportPayload, classifyPluginChanges, diffConfig, filterSyncable, globToRegexSource, hasSecuritySensitiveChanges, loadClaudeSettingsLayer, loadSetting, migrate, parseExportFile, parseToolSpecifier, sanitizeUntrustedProjectLayer, translateClaudeSettings };
2123
+ export { type AgentOptions, BUILTIN_AGENT_PROFILES, BUILTIN_REVIEWER_AGENTS, CURRENT_EXPORT_VERSION, type CategoryConfig, type ConfigExportFile, type ConfigLayerName, type ConfigOrigins, DEFAULT_CONFIG, EXCLUDED_KEYS, ExportFileParseError, ExportFileVersionError, type ExternalHistoryConfig, FIELD_CLASSIFICATION, type FieldClassification, type FieldDiff, HIGH_RISK_PROJECT_KEYS, type HistoryConfig, type InstalledPluginEntry, LOG_LEVELS, type LoadSettingOptions, type LogLevel, type MarkdownThemeOverridesSetting, type McpServerSetting, type McpServersConfig, type MergeCategory, type Migration, NEVER_SYNCED_KEYS, type NotificationConfig, PERMISSION_MODES, type PermissionMode, type PermissionPolicySetting, type PermissionRuleConfig, type PluginDiff, type ProxyConfig, RawFileSettingStore, type RecentMentionEntry, RemoteSettingStore, SETTING_KEYS, SYNCABLE_KEYS, type SandboxConfig, type SanitizeResult, type Setting, type SettingFromSchema, type SettingKey, SettingLoader, type SettingStore, type SettingWarning, SettingsManager, type SettingsOptions, type StorageType, THEME_COLOR_ROLES, TOOL_PRESETS, type ThemeColorRole, type ThemeOverridesSetting, type ThemePresetSelectionSetting, type ToolPreset, applyMergeStrategy, assertFieldClassificationCoversAllKeys, buildExportPayload, classifyPluginChanges, diffConfig, filterSyncable, globToRegexSource, hasSecuritySensitiveChanges, loadClaudeSettingsLayer, loadSetting, migrate, parseExportFile, parseToolSpecifier, sanitizeUntrustedProjectLayer, translateClaudeSettings };
2085
2124
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../src/store.ts","../src/constants.ts","../src/schema.ts","../src/types.ts","../src/migrator.ts","../src/setting.ts","../src/raw-file-store.ts","../src/remote-setting-store.ts","../src/remote-sync-keys.ts","../src/settings-manager.ts","../src/claude-settings-adapter.ts","../src/project-trust.ts","../src/presets.ts","../src/config-merge-strategy.ts","../src/config-export-format.ts"],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/store.ts","../src/constants.ts","../src/schema.ts","../src/types.ts","../src/migrator.ts","../src/config-origins.ts","../src/setting.ts","../src/raw-file-store.ts","../src/remote-setting-store.ts","../src/remote-sync-keys.ts","../src/settings-manager.ts","../src/claude-settings-adapter.ts","../src/project-trust.ts","../src/presets.ts","../src/config-merge-strategy.ts","../src/config-export-format.ts"],"mappings":"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"}
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- import{PERMISSION_MODES as e}from"@x-otto/hook-contracts";import{z as t}from"zod";import{MODEL_SLOTS as n}from"@x-otto/orchestration-contracts";import{compare as r}from"semver";import{TypedEventEmitter as i,createLogger as a,mapClaudeToolName as o,parseJsonc as s,safeStringify as c}from"@x-otto/shared";import{atomicWriteFile as l}from"@x-otto/persistence";import{CLAUDE_HOME as u,OTTO_CONFIG_FILENAME as d,OTTO_HOME as ee,OTTO_PROJECT_CONFIG_RELPATH as te,isClaudeSettingsCompatEnabled as ne,isShadowModeEnabled as re}from"@x-otto/env";import{isAbsolute as ie,join as f,resolve as p}from"node:path";import{homedir as ae}from"node:os";import{existsSync as oe,readFileSync as se}from"node:fs";import{HIGH_RISK_CAPABILITIES as ce}from"@x-otto/plugin";const m=[`minimal`,`standard`,`full`],h=[`trace`,`debug`,`info`,`warn`,`error`,`fatal`],g=e=>e.optional().catch(void 0),_=t.enum(n),le=t.object({model:g(t.string()),description:g(t.string()),system_prompt:g(t.string()),tools:g(t.array(t.string())),capabilities:g(t.array(t.string())),categories:g(t.array(t.string())),default_workflow_slot:g(_),max_tool_turns:g(t.number()),max_tool_turn_extensions:g(t.number()),prompt_output_token_budget:g(t.number()),prompt_wall_clock_budget_ms:g(t.number()),session_cost_budget_usd:g(t.number()),stall_detection:g(t.union([t.literal(!1),t.object({window_turns:g(t.number()),repeat_threshold:g(t.number())})])),temperature:g(t.number()),max_tokens:g(t.number()),disabled:g(t.boolean())}),ue=t.object({preferred_models:g(t.array(t.string())),default_model:g(t.string())}),de=t.object({enabled:g(t.boolean()),every_n_turns:g(t.number()),max_lessons:g(t.number()),similarity_threshold:g(t.number())}),fe=t.object({enabled:g(t.boolean()),min_messages:g(t.number()),dismiss_cooldown_days:g(t.number())}),pe=t.object({slots:g(t.partialRecord(_,t.union([t.string(),t.array(t.string())]))),default:g(t.string()),subagent_default_slot:g(_)}),me=t.object({enabled:g(t.boolean()),sound:g(t.boolean()),on_completion:g(t.boolean()),on_error:g(t.boolean()),on_idle:g(t.boolean()),channel:g(t.enum([`auto`,`terminal_bell`,`iterm2`,`iterm2_with_bell`,`kitty`,`ghostty`,`disabled`])),condition:g(t.enum([`unfocused`,`always`])),idle_threshold_ms:g(t.number()),command:g(t.array(t.string()))}),he=t.object({enabled:g(t.boolean()),probability:g(t.number()),min_turn_gap:g(t.number()),sink:g(t.string())}),ge=t.object({id:g(t.string()),path:g(t.string()),enabled:g(t.boolean())}),_e=t.object({enabled:g(t.boolean()),maxEntries:g(t.number()),sources:g(t.array(ge))}),ve=t.object({external:g(_e)}),ye=t.object({enabled:t.boolean(),network:g(t.enum([`allow`,`deny`])),writablePaths:g(t.array(t.string())),protectCredentials:g(t.boolean()),autoAllowBashIfSandboxed:g(t.boolean())}),be=t.object({total_bytes:g(t.number()),min_per_session_bytes:g(t.number()),max_per_session_bytes:g(t.number()),max_history_messages:g(t.number()),resume_window_messages:g(t.number()),max_content_bytes:g(t.number()),warn_threshold_pct:g(t.number()),hard_threshold_pct:g(t.number()),compaction_timeout_ms:g(t.number()),coverage_warn_threshold:g(t.number()),coverage_error_threshold:g(t.number()),coverage_floor_threshold:g(t.number())});t.object({tool_preset:g(t.enum(m)),disabled_tools:g(t.array(t.string()))});const xe=t.object({enabled:g(t.boolean())}),Se=t.object({enabled:g(t.boolean()),maxAttempts:g(t.number())}),Ce=t.object({enabled:g(t.boolean()),failureThreshold:g(t.number()),timeoutMs:g(t.number())}),we=t.object({enabled:g(t.boolean())}),Te=t.object({enabled:g(t.boolean()),review:g(xe),retry:g(Se),circuitBreaker:g(Ce),routing:g(we)}),Ee=t.object({name:t.string(),effect:t.enum([`allow`,`deny`,`ask`]),tools:g(t.array(t.string())),paths:g(t.array(t.string())),deny_reason:g(t.string()),ask_prompt:g(t.string())}),De=t.object({name:t.string(),priority:g(t.number()),enabled:g(t.boolean()),scope:g(t.object({agents:g(t.array(t.string())),sessions:g(t.array(t.string()))})),rules:t.array(Ee)}),Oe=t.object({url:g(t.string()),enabled:g(t.boolean())}),ke=t.object({command:g(t.string()),args:g(t.array(t.string())),env:g(t.record(t.string(),t.string())),url:g(t.string()),type:g(t.enum([`stdio`,`sse`,`http`])),headers:g(t.record(t.string(),t.string())),requestTimeoutMs:g(t.number()),autoReconnect:g(t.boolean()),disabled:g(t.boolean())}),Ae=t.object({maxRetries:g(t.number().int().positive()),initialDelayMs:g(t.number().positive()),backoffFactor:g(t.number().positive()),maxDelayMs:g(t.number().positive())}),je=t.object({maxRetries:g(t.number().int().positive()),intervalMs:g(t.number().positive())}),Me=t.object({maxReconnectAttempts:g(t.number().int().nonnegative()),reconnectDelayMs:g(t.number().positive()),requestTimeoutMs:g(t.number().positive())}),Ne=t.object({retryBackoffMs:g(t.number().nonnegative())}),Pe=t.object({maxAttempts:g(t.number().int().positive()),intervalMs:g(t.number().positive())}),Fe=t.object({refreshLockTimeoutMs:g(t.number().min(1e3))}),Ie=t.object({stream:g(Ae),networkDisconnect:g(je),mcp:g(Me),schedule:g(Ne),errorRetry:g(Pe),oauth:g(Fe)}),Le=t.object({id:t.string(),source:t.enum([`npm`,`dir`,`url`,`git`]),sourceSpec:g(t.string()),installedAt:t.number(),version:g(t.string()),scope:t.enum([`global`,`project`,`repository`]),capabilities:g(t.array(t.string()))}),Re=t.object({label:t.string(),value:t.string(),kind:t.enum([`hashtag`,`file`,`person`,`agent`,`skill`,`mcp`,`plugin`,`model`,`resource`,`shortcut`,`effort`]),description:g(t.string())}),ze=t.object({name:t.string().min(1).max(64),text:t.string().min(1).max(4e3)}),v=`brand.brandShimmer.accent.accentDim.accentBright.text.inactive.secondary.subtle.replyPrefix.success.error.warning.special.suggestion.permission.codeText.diffAdd.diffRemove.diffAddBg.diffRemoveBg.panelBorder.overlayBg.tagBg.toolText.focusBorder.selection`.split(`.`),y=t.enum(v),Be=t.strictObject({heading1:g(y),heading2:g(y),headingWeak:g(y),listMarker:g(y),listMarkerMuted:g(y),quoteBar:g(y),quoteText:g(y),link:g(y),inlineCode:g(y),codeFence:g(y),tableHeader:g(y),tableDivider:g(y),listIndent:g(t.union([t.literal(2),t.literal(3)])),listMarkers:g(t.tuple([t.string().min(1),t.string().min(1),t.string().min(1),t.string().min(1)])),codeBlockDivider:g(t.enum([`hr`,`none`]))}),Ve=t.strictObject({markdown:g(Be)}),He=t.strictObject({dark:g(t.string().min(1).max(128)),light:g(t.string().min(1).max(128))}),b=t.object({config_version:g(t.string()),version:g(t.string()),log_level:g(t.enum(h)),model:g(t.string()),model_fallback:g(t.array(t.string())),recent_models:g(t.array(t.string())),recent_mentions:g(t.array(Re)),shortcuts:g(t.array(ze)),feedback_repo:g(t.string()),keybinding_overrides:g(t.record(t.string(),t.object({ctrl:t.boolean().optional(),shift:t.boolean().optional(),meta:t.boolean().optional(),input:t.string().optional(),key:t.enum([`tab`,`escape`,`return`]).optional()}))),language:g(t.string()),theme:g(t.enum([`dark`,`light`,`system`])),theme_overrides:g(Ve),theme_preset:g(He),nickname:g(t.string().max(64)),has_completed_onboarding:g(t.boolean()),has_completed_wizard:g(t.boolean()),wizard_dont_show_again:g(t.boolean()),first_prompt_submitted_at:g(t.number()),thinking_level:g(t.string()),agents:g(t.record(t.string(),le)),categories:g(t.record(t.string(),ue)),tool_preset:g(t.enum(m)),memory_auto_extract:g(de),skill_loop:g(fe),notification:g(me),pulse_survey:g(he),history:g(ve),workflow:g(Te),model_slots:g(pe),mcp_servers:g(t.record(t.string(),ke)),resilience:g(Ie),sandbox:g(ye),residency:g(be),disabled_agents:g(t.array(t.string())),disabled_hooks:g(t.array(t.string())),disabled_tools:g(t.array(t.string())),disabled_skills:g(t.array(t.string())),disabled_plugins:g(t.array(t.string())),installed_plugins:g(t.array(Le)),auto_update_check:g(t.boolean()),last_update_check:g(t.number()),prompt_output_token_budget:g(t.number()),prompt_wall_clock_budget_ms:g(t.number()),session_cost_budget_usd:g(t.number()),stall_detection:g(t.union([t.literal(!1),t.object({window_turns:g(t.number()),repeat_threshold:g(t.number())})])),todo_continue_max:g(t.number()),permission_mode:g(t.enum(e)),permissions:g(t.array(De)),permission_always_allow:g(t.array(t.string())),plugin_lifecycle:g(t.object({setupDone:g(t.array(t.object({id:t.string(),version:g(t.string())})))})),proxy:g(Oe),web_fetch_allowed_hosts:g(t.array(t.string())),_migrations:g(t.array(t.string()))}),Ue=v,x=b.keyof().options,S={"spec-reviewer":{description:`Reviews implementation against specification requirements`,categories:[`review`],default_workflow_slot:`critique`},"quality-reviewer":{description:`Reviews code quality, patterns, and best practices`,categories:[`review`],default_workflow_slot:`critique`}},C={config_version:`1.0.0`,log_level:`info`,model:void 0,recent_models:[],recent_mentions:[],shortcuts:[],language:`auto`,theme:void 0,nickname:void 0,has_completed_onboarding:void 0,has_completed_wizard:void 0,wizard_dont_show_again:void 0,first_prompt_submitted_at:void 0,thinking_level:void 0,tool_preset:`full`,agents:{...S},categories:{},workflow:{},disabled_agents:[],disabled_hooks:[],disabled_tools:[],disabled_skills:[],disabled_plugins:[],installed_plugins:[],auto_update_check:!0,last_update_check:void 0,permission_mode:`auto`,permissions:[],permission_always_allow:[],web_fetch_allowed_hosts:[],notification:{enabled:!0,sound:!1,on_completion:!0,on_error:!0,channel:`auto`,condition:`unfocused`,idle_threshold_ms:6e4}},We=a(`@x-otto/setting:migrator`);function w(e,t=[]){let n=typeof e.config_version==`string`?e.config_version:`0.0.0`,i=[],a={...e};for(let e of t)r(e.version,n)>0&&(a=e.migrate(a),a.config_version=e.version,i.push(`${n} → ${e.version}`),We.info({from:n,to:e.version},`Applying migration: ${e.description}`));if(i.length>0){let e=Array.isArray(a._migrations)?a._migrations:[];a._migrations=[...e,...i]}return{config:a,applied:i}}const T=[`permission_mode`,`permissions`,`permission_always_allow`,`mcp_servers`,`disabled_hooks`,`sandbox`];function E(e){let t={...e},n=[];for(let e of T)e in t&&(delete t[e],n.push(e));return{value:t,stripped:n}}const D=a(`@x-otto/setting`),Ge=new Set(h),Ke=[`mcp`,`skills`,`disabled_mcps`,`disabled_skills`],qe=[`theme_file`,`theme_spacing`,`theme_border`,`theme_figures`],Je=new Set([`accent`,`accentDim`,`accentBright`,`colors`,`spacing`,`border`,`figures`,`text`,`inactive`,`secondary`,`success`,`error`,`warning`,`special`]);function O(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function k(e,t,n,r){for(let[i,a]of Object.entries(e)){if(a===void 0)continue;let e=t[i],o=[...n,i].join(`.`);if(e===void 0){r.push({key:o,message:`Invalid value for "${o}" ignored`});continue}O(a)&&O(e)&&k(a,e,[...n,i],r)}}function A(e,t){let n={...e};for(let[e,r]of Object.entries(t))r!==void 0&&(O(r)&&O(n[e])?n[e]=A(n[e],r):n[e]=r);return n}function j(...e){let[t,n,...r]=e,i={...t};if(n){for(let[e,t]of Object.entries(n))if(t!==void 0)if(Array.isArray(i[e])&&Array.isArray(t)){let n=i[e],r=t;i[e]=[...new Set([...n,...r])]}else O(t)&&O(i[e])?i[e]=A(i[e],t):i[e]=t}return r.length>0?j(i,...r):i}function Ye(){let e={},t=process.env.OTTO_MODEL;t&&(e.model=t);let n=process.env.OTTO_LOG_LEVEL;return n&&Ge.has(n)&&(e.log_level=n),e}function Xe(e){let t=[],n={...e};for(let e of Ke)n[e]!==void 0&&(t.push({key:e,message:`Removed legacy config key is ignored`}),delete n[e]);for(let e of qe)n[e]!==void 0&&(t.push({key:e,message:`RFC-205 legacy theme override key is ignored`}),delete n[e]);if(O(n.theme_overrides)){let e=Object.keys(n.theme_overrides).filter(e=>Je.has(e));e.length>0&&t.push({key:`theme_overrides`,message:`RFC-205 legacy theme_overrides shape (keys: ${e.join(`, `)}) is ignored — use theme_overrides.markdown instead`})}let r=b.loose().safeParse(n),i=r.success?r.data:n,a={};for(let[e,t]of Object.entries(i))t!==void 0&&(a[e]=t);for(let e of x){let r=n[e],i=a[e];if(r!==void 0&&i===void 0){t.push({key:e,message:`Invalid value for "${e}" ignored`});continue}O(r)&&O(i)&&k(r,i,[e],t)}return{value:a,warnings:t}}async function Ze(e,t,n){let r=[],i=[];for(let a of t)try{let t=await e.read(a);if(t!==void 0){let e=s(t);if(n!==void 0&&a===n){let t=E(e);e=t.value,i.push(...t.stripped)}r.push(e),D.debug({path:a},`Config loaded from store`)}}catch(e){D.warn({path:a,err:e},`Failed to parse config file, skipping`)}return{layers:r,stripped:i}}var M=class{store;constructor(e){this.store=e}async load(e={}){let{store:t=this.store,paths:n=[],extraLayers:r=[],untrustedProjectPath:i}=e,{layers:a,stripped:o}=t&&n.length>0?await Ze(t,n,i):{layers:[],stripped:[]};for(let e of o)D.warn({key:e,path:i},`未受信任项目配置剔除高危键(permission_mode/mcp_servers 等需先信任该目录)`);let s=Ye(),{config:c,applied:l}=w(j(C,...r,...a,s));l.length>0&&D.info({applied:l},`Config migrations applied`);let{value:u,warnings:d}=Xe(c);for(let e of d)D.warn({key:e.key,message:e.message},`Config validation issue`);return{...C,...u}}};async function N(e={}){return new M(e.store).load(e)}var P=class{type=`file`;async read(e){let{readFile:t}=await import(`node:fs/promises`);try{return await t(await F(e),`utf-8`)}catch{return}}async write(e,t){let{mkdir:n}=await import(`node:fs/promises`),{dirname:r}=await import(`node:path`),i=await F(e);await n(r(i),{recursive:!0}),await l(i,c(t,2))}async exists(e){let{access:t}=await import(`node:fs/promises`);try{return await t(await F(e)),!0}catch{return!1}}};async function F(e){if(!e.startsWith(`~/`))return e;let{homedir:t}=await import(`node:os`);return`${t()}${e.slice(1)}`}const I=new Set([`model`,`model_fallback`,`recent_models`,`language`,`nickname`,`has_completed_onboarding`,`has_completed_wizard`,`wizard_dont_show_again`,`first_prompt_submitted_at`,`log_level`,`notification`,`auto_update_check`,`model_slots`,`disabled_agents`,`disabled_hooks`,`disabled_tools`,`disabled_skills`]),L=new Set([`recent_mentions`,`shortcuts`,`mcp_servers`,`sandbox`,`permission_mode`,`permissions`,`permission_always_allow`,`web_fetch_allowed_hosts`,`session`,`tool_preset`,`config_version`,`version`,`_migrations`,`last_update_check`,`memory_auto_extract`,`skill_loop`,`workflow`,`categories`,`agents`,`disabled_plugins`,`installed_plugins`]);function R(e){let t={};for(let n of Object.keys(e))I.has(n)&&!L.has(n)&&(t[n]=e[n]);return t}var Qe=class{type=`file`;baseUrl;userId;getAuth;fetchImpl;timeoutMs;constructor(e){this.baseUrl=e.baseUrl.replace(/\/+$/,``),this.userId=e.userId,this.getAuth=e.getAuth,this.fetchImpl=e.fetch,this.timeoutMs=e.timeoutMs}async read(e){let t={Authorization:`Bearer ${(await this.getAuth()).token}`,Accept:`application/json`,"X-Otto-User-Id":this.userId},n=new AbortController,r=setTimeout(()=>n.abort(),this.timeoutMs),i;try{i=await this.fetchImpl(`${this.baseUrl}/config/user`,{method:`GET`,headers:t,signal:n.signal})}finally{clearTimeout(r)}if(i.status===404)return;if(!i.ok)throw Error(`remote settings read failed: HTTP ${i.status}`);let a=await i.json();return JSON.stringify(a)}async write(e,t){let n=R(t);if(Object.keys(n).length===0)return;let r={Authorization:`Bearer ${(await this.getAuth()).token}`,"Content-Type":`application/json`,"X-Otto-User-Id":this.userId},i=new AbortController,a=setTimeout(()=>i.abort(),this.timeoutMs),o;try{o=await this.fetchImpl(`${this.baseUrl}/config/user`,{method:`PUT`,headers:r,body:JSON.stringify(n),signal:i.signal})}finally{clearTimeout(a)}if(!o.ok)throw Error(`remote settings write failed: HTTP ${o.status}`)}async exists(e){return!1}};const z=a(`@x-otto/setting:claude-adapter`),$e=new Set([`Read`,`Write`,`Edit`,`MultiEdit`,`Glob`,`Grep`]),B={default:`confirm`,acceptEdits:`auto`,bypassPermissions:`bypass`,plan:`readonly`};function V(e){let t=/^([A-Za-z][A-Za-z0-9_]*)(?:\((.*)\))?$/.exec(e.trim());return t?{tool:t[1],specifier:t[2]}:{tool:e.trim()}}function H(e){let t=e.startsWith(`~/`)?f(ae(),e.slice(2)):e,n=``;for(let e=0;e<t.length;e++){let r=t[e];r===`*`?t[e+1]===`*`?(n+=`.*`,e++):n+=`[^/]*`:r===`?`?n+=`.`:n+=r.replace(/[.+^${}()|[\]\\]/g,`\\$&`)}return`^${n}$`}function et(e,t,n,r){let{tool:i,specifier:a}=V(e),s=o(i);if(!s){n.warnings.push(`未知工具 "${i}"(${e})→ fail-closed 丢弃`);return}if(i===`WebFetch`&&a?.startsWith(`domain:`)){let e=a.slice(7).trim();if(t===`allow`&&e){n.webFetchHosts.push(e);return}let i=t===`deny`?`deny`:`ask`;n.rules.push({name:`claude-${r}`,effect:i,tools:[s]}),n.warnings.push(`WebFetch(${a}) 的 ${t} 不可按域表达 → ${i===`deny`?`降工具级 deny(fail-closed)`:`降 ask`}`);return}if(a===void 0||a===``){n.rules.push({name:`claude-${r}`,effect:t,tools:[s]});return}if($e.has(i)){n.rules.push({name:`claude-${r}`,effect:t,tools:[s],paths:[H(a)]});return}n.rules.push({name:`claude-${r}`,effect:`ask`,tools:[s],ask_prompt:`Claude 规则 "${e}" 含命令参数限定,otto 无法精确表达,已降为询问。`}),n.warnings.push(`"${e}" 含参数 specifier 不可表达 → fail-closed 降 ask`)}function U(e){let t={rules:[],webFetchHosts:[],warnings:[]},n=e?.permissions,r=0;for(let e of[`allow`,`deny`,`ask`]){let i=n?.[e];if(Array.isArray(i))for(let n of i)typeof n==`string`&&et(n,e,t,r++)}let i={};t.rules.length>0&&(i.permissions=[{name:`claude-compat`,rules:t.rules}]),t.webFetchHosts.length>0&&(i.web_fetch_allowed_hosts=[...new Set(t.webFetchHosts)]);let a=typeof n?.defaultMode==`string`?n.defaultMode:void 0;return a&&B[a]&&(i.permission_mode=B[a],a===`plan`&&t.warnings.push(`defaultMode=plan otto 无对应 → 降 readonly`)),{setting:i,warnings:t.warnings}}function W(e){if(!ne())return{};let t=[f(u,`settings.json`),...e?[f(p(e),`.claude`,`settings.json`),f(p(e),`.claude`,`settings.local.json`)]:[]],n=[],r=[],i,a=[];for(let e of t){if(!oe(e))continue;let t;try{t=s(se(e,`utf-8`))}catch(t){z.warn({file:e,err:t},`Failed to parse .claude settings, skipped`);continue}let{setting:o,warnings:c}=U(t);a.push(...c);let l=o.permissions?.[0]?.rules;l&&n.push(...l),o.web_fetch_allowed_hosts&&r.push(...o.web_fetch_allowed_hosts),o.permission_mode&&(i=o.permission_mode)}let o={};n.length>0&&(o.permissions=[{name:`claude-compat`,rules:n}]),r.length>0&&(o.web_fetch_allowed_hosts=[...new Set(r)]),i&&(o.permission_mode=i);let c=n.length;if(c>0||a.length>0){z.info({ruleCount:c,hosts:o.web_fetch_allowed_hosts?.length??0,mode:o.permission_mode},`Imported .claude/settings.json (RFC-032 D4)`);for(let e of a)z.warn({rule:e},`.claude settings 翻译降级`)}return o}var tt=class extends i{paths;workspaceDir;projectConfigTrusted;overrides;store;remoteStore;setting={};writeChain=Promise.resolve();constructor(e={}){super(),this.paths=nt(e),this.workspaceDir=e.workspaceDir,this.projectConfigTrusted=e.projectConfigTrusted??!0,this.overrides={...e.overrides},this.store=e.store??new P,this.remoteStore=e.remoteStore}get(e){return this.setting[e]}get config(){return this.setting}get model(){return this.setting.model}async load(){return this.reload(this.overrides)}async reload(e){let t;if(this.remoteStore)try{let e=await this.remoteStore.read(`/config/user`);e!==void 0&&(t=s(e))}catch(e){this.emit(`remote-sync-failed`,{op:`read`,error:e})}return this.setting={...await N({store:this.store,paths:this.paths,untrustedProjectPath:this.resolveUntrustedProjectPath(),extraLayers:[W(this.workspaceDir),...t?[t]:[]]}),...e},this.emit(`change`,this.setting),this.setting}async update(e){return this.overrides={...this.overrides,...e},this.reload(this.overrides)}async persist(e,t){if(re())return this.update(e);let n=t===`global`?this.paths[0]:this.paths[this.paths.length-1],r=this.store;return r&&n?this.enqueueWrite(async()=>{let t=await r.read(n),i={};if(t)try{i=s(t)}catch{i={}}return await r.write(n,{...i,...e}),this.remoteStore&&this.remoteStore.write(`/config/user`,{...i,...e}).catch(e=>{this.emit(`remote-sync-failed`,{op:`write`,error:e})}),this.reload(this.overrides)}):this.update(e)}enqueueWrite(e){let t=this.writeChain.then(e,e);return this.writeChain=t.then(()=>void 0,()=>void 0),t}resolveUntrustedProjectPath(){if(this.paths.length!==0)return(typeof this.projectConfigTrusted==`function`?this.projectConfigTrusted():this.projectConfigTrusted)?void 0:this.paths[this.paths.length-1]}dispose(){this.removeAllListeners()}};function nt(e){let t;if(e.projectConfigPath?t=e.projectConfigPath:e.workspaceDir&&(t=p(e.workspaceDir,te)),!t)return[];let n=p(ee,d);return p(t)===n?[t]:[n,t]}const rt=[],G=new Set([`config_version`,`version`,`last_update_check`,`_migrations`]),K=new Map([[`model`,{category:`scalar-override`,deviceBound:!1}],[`language`,{category:`scalar-override`,deviceBound:!1}],[`nickname`,{category:`scalar-override`,deviceBound:!1}],[`has_completed_onboarding`,{category:`scalar-override`,deviceBound:!1}],[`has_completed_wizard`,{category:`scalar-override`,deviceBound:!1}],[`wizard_dont_show_again`,{category:`scalar-override`,deviceBound:!1}],[`first_prompt_submitted_at`,{category:`scalar-override`,deviceBound:!1}],[`thinking_level`,{category:`scalar-override`,deviceBound:!1}],[`tool_preset`,{category:`scalar-override`,deviceBound:!1}],[`log_level`,{category:`scalar-override`,deviceBound:!1}],[`feedback_repo`,{category:`scalar-override`,deviceBound:!1}],[`permission_mode`,{category:`scalar-override`,deviceBound:!1}],[`prompt_output_token_budget`,{category:`scalar-override`,deviceBound:!1}],[`prompt_wall_clock_budget_ms`,{category:`scalar-override`,deviceBound:!1}],[`session_cost_budget_usd`,{category:`scalar-override`,deviceBound:!1}],[`memory_auto_extract`,{category:`scalar-override`,deviceBound:!1}],[`residency`,{category:`scalar-override`,deviceBound:!1}],[`skill_loop`,{category:`scalar-override`,deviceBound:!1}],[`history`,{category:`scalar-override`,deviceBound:!1}],[`workflow`,{category:`scalar-override`,deviceBound:!1}],[`todo_continue_max`,{category:`scalar-override`,deviceBound:!1}],[`stall_detection`,{category:`scalar-override`,deviceBound:!1}],[`sandbox`,{category:`scalar-override`,deviceBound:!1}],[`resilience`,{category:`scalar-override`,deviceBound:!1}],[`disabled_plugins`,{category:`array-merge`,deviceBound:!1}],[`model_fallback`,{category:`array-merge`,deviceBound:!1}],[`web_fetch_allowed_hosts`,{category:`array-merge`,deviceBound:!1}],[`recent_mentions`,{category:`array-merge`,deviceBound:!1}],[`shortcuts`,{category:`array-merge`,deviceBound:!1}],[`disabled_agents`,{category:`array-merge`,deviceBound:!1}],[`disabled_tools`,{category:`array-merge`,deviceBound:!1}],[`disabled_skills`,{category:`array-merge`,deviceBound:!1}],[`disabled_hooks`,{category:`array-merge`,deviceBound:!1}],[`permissions`,{category:`array-merge`,deviceBound:!1}],[`permission_always_allow`,{category:`array-merge`,deviceBound:!1}],[`agents`,{category:`record-merge`,deviceBound:!1}],[`categories`,{category:`record-merge`,deviceBound:!1}],[`model_slots`,{category:`record-merge`,deviceBound:!1}],[`mcp_servers`,{category:`record-merge`,deviceBound:!1}],[`recent_models`,{category:`array-merge`,deviceBound:!0}],[`keybinding_overrides`,{category:`record-merge`,deviceBound:!0}],[`installed_plugins`,{category:`array-merge`,deviceBound:!0}],[`plugin_lifecycle`,{category:`scalar-override`,deviceBound:!0}],[`auto_update_check`,{category:`scalar-override`,deviceBound:!0}],[`theme`,{category:`scalar-override`,deviceBound:!0}],[`theme_overrides`,{category:`scalar-override`,deviceBound:!0}],[`theme_preset`,{category:`scalar-override`,deviceBound:!0}],[`proxy`,{category:`scalar-override`,deviceBound:!0}],[`notification`,{category:`scalar-override`,deviceBound:!0}],[`pulse_survey`,{category:`scalar-override`,deviceBound:!0}]]);function it(){let e=x.filter(e=>!G.has(e)&&!K.has(e));if(e.length>0)throw Error(`ConfigMergeStrategy 字段分类表遗漏以下 SettingKey(需在 FIELD_CLASSIFICATION 或 EXCLUDED_KEYS 中归类): ${e.join(`, `)}`)}const at=new Set(T);function q(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function J(e,t){return e===t?!0:Array.isArray(e)&&Array.isArray(t)?e.length===t.length&&e.every((e,n)=>JSON.stringify(e)===JSON.stringify(t[n])):JSON.stringify(e)===JSON.stringify(t)}const Y={bypass:0,auto:1,confirm:2,strict:3,readonly:4};function ot(e,t){if(typeof t!=`string`||e===t)return!1;let n=typeof e==`string`?Y[e]:void 0,r=Y[t];return n===void 0||r===void 0?!0:r<n}function st(e,t){let n=q(e)?e.enabled:void 0,r=q(t)?t.enabled:void 0;return n===!0&&r===!1}function X(e,t){let n=Array.isArray(e)?e:[],r=Array.isArray(t)?t:[],i=new Set(n.map(e=>JSON.stringify(e)));return r.filter(e=>!i.has(JSON.stringify(e)))}function ct(e,t,n){if(!at.has(e))return`safe`;if(e===`permission_mode`)return ot(t,n)?`sensitive`:`safe`;if(e===`sandbox`)return st(t,n)?`sensitive`:`safe`;if(e===`mcp_servers`){let e=q(t)?t:{},r=q(n)?n:{};for(let[t,n]of Object.entries(r))if(!(t in e)||!J(e[t],n))return`sensitive`;return`safe`}if(e===`disabled_hooks`)return J(t,n)?`safe`:`sensitive`;if(e===`permissions`||e===`permission_always_allow`)return X(t,n).length>0?`sensitive`:`safe`;throw Error(`classifyRisk: 字段 "${e}" 在 HIGH_RISK_PROJECT_KEYS 中但未被显式处理——新增高危字段必须同步补充判定分支,不允许 fail-open`)}function lt(e,t){let n=[];for(let r of x){if(G.has(r)||r===`installed_plugins`||!(r in t))continue;let i=e[r],a=t[r];if(J(i,a))continue;let o=K.get(r);o&&n.push({key:r,from:i,to:a,category:o.category,deviceBound:o.deviceBound,risk:ct(r,i,a)})}return n}function ut(e,t){let n=new Map(e.map(e=>[e.id,e])),r=new Map(t.map(e=>[e.id,e])),i=[],a=[];for(let e of t){let t=n.get(e.id);if(!t){i.push(e);continue}e.version!==void 0&&t.version!==void 0&&e.version!==t.version&&a.push({id:e.id,local:t.version,incoming:e.version})}return{toInstall:i,localOnly:e.filter(e=>!r.has(e.id)),versionMismatch:a}}function dt(e,t){return e.some(e=>e.risk===`sensitive`)?!0:t.toInstall.some(e=>(e.capabilities??[]).some(e=>ce.includes(e)))}function ft(e,t){let n={};for(let r of e)if(!(t===`safe-only`&&r.risk===`sensitive`))if(r.category===`array-merge`){let e=Array.isArray(r.from)?r.from:[],t=Array.isArray(r.to)?r.to:[],i=new Set(e.map(e=>JSON.stringify(e))),a=[...e];for(let e of t){let t=JSON.stringify(e);i.has(t)||(i.add(t),a.push(e))}n[r.key]=a}else if(r.category===`record-merge`){let e=q(r.from)?r.from:{},t=q(r.to)?r.to:{};n[r.key]={...e,...t}}else n[r.key]=r.to;return n}const pt=1;function mt(e,t,n,r=()=>new Date().toISOString()){let i={};for(let[t,n]of Object.entries(e))if(!G.has(t)&&n!==void 0){if(t===`installed_plugins`){i[t]=n.map(e=>e.sourceSpec&&ie(e.sourceSpec)?{...e,sourceSpec:``}:e);continue}i[t]=n}return{otto_config_version:1,exported_at:r(),exported_from:n,scope:t,fields:i}}var Z=class extends Error{constructor(e,t){super(`导入文件 otto_config_version=${e},当前 otto 支持=${t}。请升级 otto 到最新版本后再试。`),this.fileVersion=e,this.supportedVersion=t,this.name=`ExportFileVersionError`}},Q=class extends Error{constructor(e){super(`导入文件格式非法:${e}`),this.name=`ExportFileParseError`}};function $(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function ht(e){let t;try{t=JSON.parse(e)}catch(e){throw new Q(e instanceof Error?e.message:String(e))}if(!$(t))throw new Q(`顶层结构必须是 JSON 对象`);let{otto_config_version:n,exported_at:r,exported_from:i,scope:a,fields:o}=t;if(typeof n!=`number`)throw new Q(`缺少或非法的 otto_config_version 字段`);if(n!==1)throw new Z(n,1);if(typeof r!=`string`||typeof i!=`string`)throw new Q(`缺少或非法的 exported_at/exported_from 字段`);if(a!==`global`&&a!==`project`)throw new Q(`scope 字段必须是 "global" 或 "project"`);if(!$(o))throw new Q(`fields 字段必须是 JSON 对象`);return{otto_config_version:n,exported_at:r,exported_from:i,scope:a,fields:o}}export{rt as BUILTIN_AGENT_PROFILES,S as BUILTIN_REVIEWER_AGENTS,pt as CURRENT_EXPORT_VERSION,C as DEFAULT_CONFIG,G as EXCLUDED_KEYS,Q as ExportFileParseError,Z as ExportFileVersionError,K as FIELD_CLASSIFICATION,T as HIGH_RISK_PROJECT_KEYS,h as LOG_LEVELS,L as NEVER_SYNCED_KEYS,e as PERMISSION_MODES,P as RawFileSettingStore,Qe as RemoteSettingStore,x as SETTING_KEYS,I as SYNCABLE_KEYS,M as SettingLoader,tt as SettingsManager,Ue as THEME_COLOR_ROLES,m as TOOL_PRESETS,ft as applyMergeStrategy,it as assertFieldClassificationCoversAllKeys,mt as buildExportPayload,ut as classifyPluginChanges,lt as diffConfig,R as filterSyncable,H as globToRegexSource,dt as hasSecuritySensitiveChanges,W as loadClaudeSettingsLayer,N as loadSetting,w as migrate,ht as parseExportFile,V as parseToolSpecifier,E as sanitizeUntrustedProjectLayer,U as translateClaudeSettings};
1
+ import{PERMISSION_MODES as e}from"@x-otto/hook-contracts";import{z as t}from"zod";import{MODEL_SLOTS as n}from"@x-otto/orchestration-contracts";import{compare as r}from"semver";import{TypedEventEmitter as i,createLogger as a,mapClaudeToolName as o,parseJsonc as s,safeStringify as c}from"@x-otto/shared";import{atomicWriteFile as l}from"@x-otto/persistence";import{CLAUDE_HOME as u,OTTO_CONFIG_FILENAME as d,OTTO_HOME as f,OTTO_PROJECT_CONFIG_RELPATH as ee,isClaudeSettingsCompatEnabled as te,isShadowModeEnabled as ne}from"@x-otto/env";import{isAbsolute as re,join as p,resolve as m}from"node:path";import{homedir as ie}from"node:os";import{existsSync as ae,readFileSync as oe}from"node:fs";import{HIGH_RISK_CAPABILITIES as se}from"@x-otto/plugin";const h=[`minimal`,`standard`,`full`],g=[`trace`,`debug`,`info`,`warn`,`error`,`fatal`],_=e=>e.optional().catch(void 0),v=t.enum(n),ce=t.object({model:_(t.string()),description:_(t.string()),system_prompt:_(t.string()),tools:_(t.array(t.string())),capabilities:_(t.array(t.string())),categories:_(t.array(t.string())),default_workflow_slot:_(v),max_tool_turns:_(t.number()),max_tool_turn_extensions:_(t.number()),prompt_output_token_budget:_(t.number()),prompt_wall_clock_budget_ms:_(t.number()),session_cost_budget_usd:_(t.number()),stall_detection:_(t.union([t.literal(!1),t.object({window_turns:_(t.number()),repeat_threshold:_(t.number())})])),temperature:_(t.number()),max_tokens:_(t.number()),disabled:_(t.boolean())}),le=t.object({preferred_models:_(t.array(t.string())),default_model:_(t.string())}),ue=t.object({enabled:_(t.boolean()),every_n_turns:_(t.number()),max_lessons:_(t.number()),similarity_threshold:_(t.number()),min_relevant_hits:_(t.number())}),de=t.object({enabled:_(t.boolean()),min_messages:_(t.number()),dismiss_cooldown_days:_(t.number())}),fe=t.object({enabled:_(t.boolean()),min_executions:_(t.number()),min_success_rate:_(t.number()),max_internalized:_(t.number())}),pe=t.object({slots:_(t.partialRecord(v,t.union([t.string(),t.array(t.string())]))),default:_(t.string()),subagent_default_slot:_(v)}),me=t.object({enabled:_(t.boolean()),sound:_(t.boolean()),on_completion:_(t.boolean()),on_error:_(t.boolean()),on_idle:_(t.boolean()),channel:_(t.enum([`auto`,`terminal_bell`,`iterm2`,`iterm2_with_bell`,`kitty`,`ghostty`,`disabled`])),condition:_(t.enum([`unfocused`,`always`])),idle_threshold_ms:_(t.number()),command:_(t.array(t.string()))}),he=t.object({enabled:_(t.boolean()),probability:_(t.number()),min_turn_gap:_(t.number()),sink:_(t.string())}),ge=t.object({id:_(t.string()),path:_(t.string()),enabled:_(t.boolean())}),_e=t.object({enabled:_(t.boolean()),maxEntries:_(t.number()),sources:_(t.array(ge))}),ve=t.object({external:_(_e)}),ye=t.object({enabled:t.boolean(),network:_(t.enum([`allow`,`deny`])),writablePaths:_(t.array(t.string())),protectCredentials:_(t.boolean()),autoAllowBashIfSandboxed:_(t.boolean())}),be=t.object({total_bytes:_(t.number()),min_per_session_bytes:_(t.number()),max_per_session_bytes:_(t.number()),max_history_messages:_(t.number()),resume_window_messages:_(t.number()),max_content_bytes:_(t.number()),warn_threshold_pct:_(t.number()),hard_threshold_pct:_(t.number()),compaction_timeout_ms:_(t.number()),coverage_warn_threshold:_(t.number()),coverage_error_threshold:_(t.number()),coverage_floor_threshold:_(t.number())});t.object({tool_preset:_(t.enum(h)),disabled_tools:_(t.array(t.string()))});const xe=t.object({enabled:_(t.boolean())}),Se=t.object({enabled:_(t.boolean()),maxAttempts:_(t.number())}),Ce=t.object({enabled:_(t.boolean()),failureThreshold:_(t.number()),timeoutMs:_(t.number())}),we=t.object({enabled:_(t.boolean())}),Te=t.object({enabled:_(t.boolean()),review:_(xe),retry:_(Se),circuitBreaker:_(Ce),routing:_(we)}),Ee=t.object({name:t.string(),effect:t.enum([`allow`,`deny`,`ask`]),tools:_(t.array(t.string())),paths:_(t.array(t.string())),deny_reason:_(t.string()),ask_prompt:_(t.string())}),De=t.object({name:t.string(),priority:_(t.number()),enabled:_(t.boolean()),scope:_(t.object({agents:_(t.array(t.string())),sessions:_(t.array(t.string()))})),rules:t.array(Ee)}),Oe=t.object({url:_(t.string()),enabled:_(t.boolean())}),ke=t.object({command:_(t.string()),args:_(t.array(t.string())),env:_(t.record(t.string(),t.string())),url:_(t.string()),type:_(t.enum([`stdio`,`sse`,`http`])),headers:_(t.record(t.string(),t.string())),requestTimeoutMs:_(t.number()),autoReconnect:_(t.boolean()),disabled:_(t.boolean())}),Ae=t.object({maxRetries:_(t.number().int().positive()),initialDelayMs:_(t.number().positive()),backoffFactor:_(t.number().positive()),maxDelayMs:_(t.number().positive())}),je=t.object({maxRetries:_(t.number().int().positive()),intervalMs:_(t.number().positive())}),Me=t.object({maxReconnectAttempts:_(t.number().int().nonnegative()),reconnectDelayMs:_(t.number().positive()),requestTimeoutMs:_(t.number().positive())}),Ne=t.object({retryBackoffMs:_(t.number().nonnegative())}),Pe=t.object({maxAttempts:_(t.number().int().positive()),intervalMs:_(t.number().positive())}),Fe=t.object({refreshLockTimeoutMs:_(t.number().min(1e3))}),Ie=t.object({stream:_(Ae),networkDisconnect:_(je),mcp:_(Me),schedule:_(Ne),errorRetry:_(Pe),oauth:_(Fe)}),Le=t.object({id:t.string(),source:t.enum([`npm`,`dir`,`url`,`git`]),sourceSpec:_(t.string()),installedAt:t.number(),version:_(t.string()),scope:t.enum([`global`,`project`,`repository`]),capabilities:_(t.array(t.string()))}),Re=t.object({label:t.string(),value:t.string(),kind:t.enum([`hashtag`,`file`,`person`,`agent`,`skill`,`mcp`,`plugin`,`model`,`resource`,`shortcut`,`effort`]),description:_(t.string())}),ze=t.object({name:t.string().min(1).max(64),text:t.string().min(1).max(4e3)}),y=`brand.brandShimmer.accent.accentDim.accentBright.text.inactive.secondary.subtle.replyPrefix.success.error.warning.special.suggestion.permission.codeText.diffAdd.diffRemove.diffAddBg.diffRemoveBg.panelBorder.overlayBg.tagBg.toolText.focusBorder.selection`.split(`.`),b=t.enum(y),Be=t.strictObject({heading1:_(b),heading2:_(b),headingWeak:_(b),listMarker:_(b),listMarkerMuted:_(b),quoteBar:_(b),quoteText:_(b),link:_(b),inlineCode:_(b),codeFence:_(b),tableHeader:_(b),tableDivider:_(b),listIndent:_(t.union([t.literal(2),t.literal(3)])),listMarkers:_(t.tuple([t.string().min(1),t.string().min(1),t.string().min(1),t.string().min(1)])),codeBlockDivider:_(t.enum([`hr`,`none`]))}),Ve=t.strictObject({markdown:_(Be)}),He=t.strictObject({dark:_(t.string().min(1).max(128)),light:_(t.string().min(1).max(128))}),x=t.object({config_version:_(t.string()),version:_(t.string()),log_level:_(t.enum(g)),model:_(t.string()),model_fallback:_(t.array(t.string())),recent_models:_(t.array(t.string())),recent_mentions:_(t.array(Re)),shortcuts:_(t.array(ze)),feedback_repo:_(t.string()),keybinding_overrides:_(t.record(t.string(),t.object({ctrl:t.boolean().optional(),shift:t.boolean().optional(),meta:t.boolean().optional(),input:t.string().optional(),key:t.enum([`tab`,`escape`,`return`]).optional()}))),language:_(t.string()),theme:_(t.enum([`dark`,`light`,`system`])),theme_overrides:_(Ve),theme_preset:_(He),nickname:_(t.string().max(64)),has_completed_onboarding:_(t.boolean()),has_completed_wizard:_(t.boolean()),wizard_dont_show_again:_(t.boolean()),first_prompt_submitted_at:_(t.number()),thinking_level:_(t.string()),agents:_(t.record(t.string(),ce)),categories:_(t.record(t.string(),le)),tool_preset:_(t.enum(h)),memory_auto_extract:_(ue),skill_loop:_(de),skill_internalization:_(fe),notification:_(me),pulse_survey:_(he),history:_(ve),workflow:_(Te),model_slots:_(pe),mcp_servers:_(t.record(t.string(),ke)),resilience:_(Ie),sandbox:_(ye),residency:_(be),disabled_agents:_(t.array(t.string())),disabled_hooks:_(t.array(t.string())),disabled_tools:_(t.array(t.string())),disabled_skills:_(t.array(t.string())),disabled_plugins:_(t.array(t.string())),installed_plugins:_(t.array(Le)),auto_update_check:_(t.boolean()),last_update_check:_(t.number()),prompt_output_token_budget:_(t.number()),prompt_wall_clock_budget_ms:_(t.number()),session_cost_budget_usd:_(t.number()),stall_detection:_(t.union([t.literal(!1),t.object({window_turns:_(t.number()),repeat_threshold:_(t.number())})])),todo_continue_max:_(t.number()),permission_mode:_(t.enum(e)),permissions:_(t.array(De)),permission_always_allow:_(t.array(t.string())),plugin_lifecycle:_(t.object({setupDone:_(t.array(t.object({id:t.string(),version:_(t.string())})))})),proxy:_(Oe),web_fetch_allowed_hosts:_(t.array(t.string())),_migrations:_(t.array(t.string()))}),Ue=y,S=x.keyof().options,C={"spec-reviewer":{description:`Reviews implementation against specification requirements`,categories:[`review`],default_workflow_slot:`critique`},"quality-reviewer":{description:`Reviews code quality, patterns, and best practices`,categories:[`review`],default_workflow_slot:`critique`}},w={config_version:`1.0.0`,log_level:`info`,model:void 0,recent_models:[],recent_mentions:[],shortcuts:[],language:`auto`,theme:void 0,nickname:void 0,has_completed_onboarding:void 0,has_completed_wizard:void 0,wizard_dont_show_again:void 0,first_prompt_submitted_at:void 0,thinking_level:void 0,tool_preset:`full`,agents:{...C},categories:{},workflow:{},disabled_agents:[],disabled_hooks:[],disabled_tools:[],disabled_skills:[],disabled_plugins:[],installed_plugins:[],auto_update_check:!0,last_update_check:void 0,permission_mode:`auto`,permissions:[],permission_always_allow:[],web_fetch_allowed_hosts:[],notification:{enabled:!0,sound:!1,on_completion:!0,on_error:!0,channel:`auto`,condition:`unfocused`,idle_threshold_ms:6e4}},We=a(`@x-otto/setting:migrator`);function T(e,t=[]){let n=typeof e.config_version==`string`?e.config_version:`0.0.0`,i=[],a={...e};for(let e of t)r(e.version,n)>0&&(a=e.migrate(a),a.config_version=e.version,i.push(`${n} → ${e.version}`),We.info({from:n,to:e.version},`Applying migration: ${e.description}`));if(i.length>0){let e=Array.isArray(a._migrations)?a._migrations:[];a._migrations=[...e,...i]}return{config:a,applied:i}}const E=[`permission_mode`,`permissions`,`permission_always_allow`,`mcp_servers`,`disabled_hooks`,`sandbox`];function D(e){let t={...e},n=[];for(let e of E)e in t&&(delete t[e],n.push(e));return{value:t,stripped:n}}function O(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function k(e,t,n,r){let i={...e},a={...n};for(let[e,o]of Object.entries(t))if(o!==void 0)if(O(o)&&O(i[e])){let t=k(i[e],o,n[e]??{},r);i[e]=t.value,a[e]=t.origins}else i[e]=o,a[e]=r;return{value:i,origins:a}}function Ge(e){let t={},n={};for(let{layer:r,name:i}of e)for(let[e,a]of Object.entries(r))if(a!==void 0){if(Array.isArray(t[e])&&Array.isArray(a)){let r=t[e],o=a;t[e]=[...new Set([...r,...o])],n[e]=i;continue}if(O(a)&&O(t[e])){let r=k(t[e],a,n[e]??{},i);t[e]=r.value,n[e]=r.origins}else if(O(a)){let r=k({},a,{},i);t[e]=r.value,n[e]=r.origins}else t[e]=a,n[e]=i}return{value:t,origins:n}}const A=a(`@x-otto/setting`),Ke=new Set(g),qe=[`mcp`,`skills`,`disabled_mcps`,`disabled_skills`],Je=[`theme_file`,`theme_spacing`,`theme_border`,`theme_figures`],Ye=new Set([`accent`,`accentDim`,`accentBright`,`colors`,`spacing`,`border`,`figures`,`text`,`inactive`,`secondary`,`success`,`error`,`warning`,`special`]);function j(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function M(e,t,n,r){for(let[i,a]of Object.entries(e)){if(a===void 0)continue;let e=t[i],o=[...n,i].join(`.`);if(e===void 0){r.push({key:o,message:`Invalid value for "${o}" ignored`});continue}j(a)&&j(e)&&M(a,e,[...n,i],r)}}function N(e,t){let n={...e};for(let[e,r]of Object.entries(t))r!==void 0&&(j(r)&&j(n[e])?n[e]=N(n[e],r):n[e]=r);return n}function P(...e){let[t,n,...r]=e,i={...t};if(n){for(let[e,t]of Object.entries(n))if(t!==void 0)if(Array.isArray(i[e])&&Array.isArray(t)){let n=i[e],r=t;i[e]=[...new Set([...n,...r])]}else j(t)&&j(i[e])?i[e]=N(i[e],t):i[e]=t}return r.length>0?P(i,...r):i}function Xe(){let e={},t=process.env.OTTO_MODEL;t&&(e.model=t);let n=process.env.OTTO_LOG_LEVEL;return n&&Ke.has(n)&&(e.log_level=n),e}function Ze(e){let t=[],n={...e};for(let e of qe)n[e]!==void 0&&(t.push({key:e,message:`Removed legacy config key is ignored`}),delete n[e]);for(let e of Je)n[e]!==void 0&&(t.push({key:e,message:`RFC-205 legacy theme override key is ignored`}),delete n[e]);if(j(n.theme_overrides)){let e=Object.keys(n.theme_overrides).filter(e=>Ye.has(e));e.length>0&&t.push({key:`theme_overrides`,message:`RFC-205 legacy theme_overrides shape (keys: ${e.join(`, `)}) is ignored — use theme_overrides.markdown instead`})}let r=x.loose().safeParse(n),i=r.success?r.data:n,a={};for(let[e,t]of Object.entries(i))t!==void 0&&(a[e]=t);for(let e of S){let r=n[e],i=a[e];if(r!==void 0&&i===void 0){t.push({key:e,message:`Invalid value for "${e}" ignored`});continue}j(r)&&j(i)&&M(r,i,[e],t)}return{value:a,warnings:t}}async function Qe(e,t,n){let r=[],i=[];for(let a of t)try{let t=await e.read(a);if(t!==void 0){let e=s(t);if(n!==void 0&&a===n){let t=D(e);e=t.value,i.push(...t.stripped)}r.push(e),A.debug({path:a},`Config loaded from store`)}}catch(e){A.warn({path:a,err:e},`Failed to parse config file, skipping`)}return{layers:r,stripped:i}}var F=class{store;lastOrigins;constructor(e){this.store=e}getOrigins(){return this.lastOrigins}async load(e={}){let{store:t=this.store,paths:n=[],extraLayers:r=[],untrustedProjectPath:i}=e,{layers:a,stripped:o}=t&&n.length>0?await Qe(t,n,i):{layers:[],stripped:[]};for(let e of o)A.warn({key:e,path:i},`未受信任项目配置剔除高危键(permission_mode/mcp_servers 等需先信任该目录)`);let s=Xe(),c=P(w,...r,...a,s);this.lastOrigins=Ge([{layer:w,name:`default`},...r.map((e,t)=>({layer:e,name:`extra:${t}`})),...a.map((e,t)=>({layer:e,name:t===a.length-1?`project`:`global`})),{layer:s,name:`env`}]).origins;let{config:l,applied:u}=T(c);u.length>0&&A.info({applied:u},`Config migrations applied`);let{value:d,warnings:f}=Ze(l);for(let e of f)A.warn({key:e.key,message:e.message},`Config validation issue`);return{...w,...d}}};async function I(e={}){return new F(e.store).load(e)}var L=class{type=`file`;async read(e){let{readFile:t}=await import(`node:fs/promises`);try{return await t(await R(e),`utf-8`)}catch{return}}async write(e,t){let{mkdir:n}=await import(`node:fs/promises`),{dirname:r}=await import(`node:path`),i=await R(e);await n(r(i),{recursive:!0}),await l(i,c(t,2))}async exists(e){let{access:t}=await import(`node:fs/promises`);try{return await t(await R(e)),!0}catch{return!1}}};async function R(e){if(!e.startsWith(`~/`))return e;let{homedir:t}=await import(`node:os`);return`${t()}${e.slice(1)}`}const z=new Set([`model`,`model_fallback`,`recent_models`,`language`,`nickname`,`has_completed_onboarding`,`has_completed_wizard`,`wizard_dont_show_again`,`first_prompt_submitted_at`,`log_level`,`notification`,`auto_update_check`,`model_slots`,`disabled_agents`,`disabled_hooks`,`disabled_tools`,`disabled_skills`]),B=new Set([`recent_mentions`,`shortcuts`,`mcp_servers`,`sandbox`,`permission_mode`,`permissions`,`permission_always_allow`,`web_fetch_allowed_hosts`,`session`,`tool_preset`,`config_version`,`version`,`_migrations`,`last_update_check`,`memory_auto_extract`,`skill_loop`,`skill_internalization`,`workflow`,`categories`,`agents`,`disabled_plugins`,`installed_plugins`]);function V(e){let t={};for(let n of Object.keys(e))z.has(n)&&!B.has(n)&&(t[n]=e[n]);return t}var $e=class{type=`file`;baseUrl;userId;getAuth;fetchImpl;timeoutMs;constructor(e){this.baseUrl=e.baseUrl.replace(/\/+$/,``),this.userId=e.userId,this.getAuth=e.getAuth,this.fetchImpl=e.fetch,this.timeoutMs=e.timeoutMs}async read(e){let t={Authorization:`Bearer ${(await this.getAuth()).token}`,Accept:`application/json`,"X-Otto-User-Id":this.userId},n=new AbortController,r=setTimeout(()=>n.abort(),this.timeoutMs),i;try{i=await this.fetchImpl(`${this.baseUrl}/config/user`,{method:`GET`,headers:t,signal:n.signal})}finally{clearTimeout(r)}if(i.status===404)return;if(!i.ok)throw Error(`remote settings read failed: HTTP ${i.status}`);let a=await i.json();return JSON.stringify(a)}async write(e,t){let n=V(t);if(Object.keys(n).length===0)return;let r={Authorization:`Bearer ${(await this.getAuth()).token}`,"Content-Type":`application/json`,"X-Otto-User-Id":this.userId},i=new AbortController,a=setTimeout(()=>i.abort(),this.timeoutMs),o;try{o=await this.fetchImpl(`${this.baseUrl}/config/user`,{method:`PUT`,headers:r,body:JSON.stringify(n),signal:i.signal})}finally{clearTimeout(a)}if(!o.ok)throw Error(`remote settings write failed: HTTP ${o.status}`)}async exists(e){return!1}};const H=a(`@x-otto/setting:claude-adapter`),et=new Set([`Read`,`Write`,`Edit`,`MultiEdit`,`Glob`,`Grep`]),U={default:`confirm`,acceptEdits:`auto`,bypassPermissions:`bypass`,plan:`readonly`};function W(e){let t=/^([A-Za-z][A-Za-z0-9_]*)(?:\((.*)\))?$/.exec(e.trim());return t?{tool:t[1],specifier:t[2]}:{tool:e.trim()}}function G(e){let t=e.startsWith(`~/`)?p(ie(),e.slice(2)):e,n=``;for(let e=0;e<t.length;e++){let r=t[e];r===`*`?t[e+1]===`*`?(n+=`.*`,e++):n+=`[^/]*`:r===`?`?n+=`.`:n+=r.replace(/[.+^${}()|[\]\\]/g,`\\$&`)}return`^${n}$`}function tt(e,t,n,r){let{tool:i,specifier:a}=W(e),s=o(i);if(!s){n.warnings.push(`未知工具 "${i}"(${e})→ fail-closed 丢弃`);return}if(i===`WebFetch`&&a?.startsWith(`domain:`)){let e=a.slice(7).trim();if(t===`allow`&&e){n.webFetchHosts.push(e);return}let i=t===`deny`?`deny`:`ask`;n.rules.push({name:`claude-${r}`,effect:i,tools:[s]}),n.warnings.push(`WebFetch(${a}) 的 ${t} 不可按域表达 → ${i===`deny`?`降工具级 deny(fail-closed)`:`降 ask`}`);return}if(a===void 0||a===``){n.rules.push({name:`claude-${r}`,effect:t,tools:[s]});return}if(et.has(i)){n.rules.push({name:`claude-${r}`,effect:t,tools:[s],paths:[G(a)]});return}n.rules.push({name:`claude-${r}`,effect:`ask`,tools:[s],ask_prompt:`Claude 规则 "${e}" 含命令参数限定,otto 无法精确表达,已降为询问。`}),n.warnings.push(`"${e}" 含参数 specifier 不可表达 → fail-closed 降 ask`)}function K(e){let t={rules:[],webFetchHosts:[],warnings:[]},n=e?.permissions,r=0;for(let e of[`allow`,`deny`,`ask`]){let i=n?.[e];if(Array.isArray(i))for(let n of i)typeof n==`string`&&tt(n,e,t,r++)}let i={};t.rules.length>0&&(i.permissions=[{name:`claude-compat`,rules:t.rules}]),t.webFetchHosts.length>0&&(i.web_fetch_allowed_hosts=[...new Set(t.webFetchHosts)]);let a=typeof n?.defaultMode==`string`?n.defaultMode:void 0;return a&&U[a]&&(i.permission_mode=U[a],a===`plan`&&t.warnings.push(`defaultMode=plan otto 无对应 → 降 readonly`)),{setting:i,warnings:t.warnings}}function q(e){if(!te())return{};let t=[p(u,`settings.json`),...e?[p(m(e),`.claude`,`settings.json`),p(m(e),`.claude`,`settings.local.json`)]:[]],n=[],r=[],i,a=[];for(let e of t){if(!ae(e))continue;let t;try{t=s(oe(e,`utf-8`))}catch(t){H.warn({file:e,err:t},`Failed to parse .claude settings, skipped`);continue}let{setting:o,warnings:c}=K(t);a.push(...c);let l=o.permissions?.[0]?.rules;l&&n.push(...l),o.web_fetch_allowed_hosts&&r.push(...o.web_fetch_allowed_hosts),o.permission_mode&&(i=o.permission_mode)}let o={};n.length>0&&(o.permissions=[{name:`claude-compat`,rules:n}]),r.length>0&&(o.web_fetch_allowed_hosts=[...new Set(r)]),i&&(o.permission_mode=i);let c=n.length;if(c>0||a.length>0){H.info({ruleCount:c,hosts:o.web_fetch_allowed_hosts?.length??0,mode:o.permission_mode},`Imported .claude/settings.json (RFC-032 D4)`);for(let e of a)H.warn({rule:e},`.claude settings 翻译降级`)}return o}var nt=class extends i{paths;workspaceDir;projectConfigTrusted;overrides;store;remoteStore;setting={};writeChain=Promise.resolve();constructor(e={}){super(),this.paths=rt(e),this.workspaceDir=e.workspaceDir,this.projectConfigTrusted=e.projectConfigTrusted??!0,this.overrides={...e.overrides},this.store=e.store??new L,this.remoteStore=e.remoteStore}get(e){return this.setting[e]}get config(){return this.setting}get model(){return this.setting.model}async load(){return this.reload(this.overrides)}async reload(e){let t;if(this.remoteStore)try{let e=await this.remoteStore.read(`/config/user`);e!==void 0&&(t=s(e))}catch(e){this.emit(`remote-sync-failed`,{op:`read`,error:e})}return this.setting={...await I({store:this.store,paths:this.paths,untrustedProjectPath:this.resolveUntrustedProjectPath(),extraLayers:[q(this.workspaceDir),...t?[t]:[]]}),...e},this.emit(`change`,this.setting),this.setting}async update(e){return this.overrides={...this.overrides,...e},this.reload(this.overrides)}async persist(e,t){if(ne())return this.update(e);let n=t===`global`?this.paths[0]:this.paths[this.paths.length-1],r=this.store;return r&&n?this.enqueueWrite(async()=>{let t=await r.read(n),i={};if(t)try{i=s(t)}catch{i={}}return await r.write(n,{...i,...e}),this.remoteStore&&this.remoteStore.write(`/config/user`,{...i,...e}).catch(e=>{this.emit(`remote-sync-failed`,{op:`write`,error:e})}),this.reload(this.overrides)}):this.update(e)}enqueueWrite(e){let t=this.writeChain.then(e,e);return this.writeChain=t.then(()=>void 0,()=>void 0),t}resolveUntrustedProjectPath(){if(this.paths.length!==0)return(typeof this.projectConfigTrusted==`function`?this.projectConfigTrusted():this.projectConfigTrusted)?void 0:this.paths[this.paths.length-1]}dispose(){this.removeAllListeners()}};function rt(e){let t;if(e.projectConfigPath?t=e.projectConfigPath:e.workspaceDir&&(t=m(e.workspaceDir,ee)),!t)return[];let n=m(f,d);return m(t)===n?[t]:[n,t]}const it=[],J=new Set([`config_version`,`version`,`last_update_check`,`_migrations`]),Y=new Map([[`model`,{category:`scalar-override`,deviceBound:!1}],[`language`,{category:`scalar-override`,deviceBound:!1}],[`nickname`,{category:`scalar-override`,deviceBound:!1}],[`has_completed_onboarding`,{category:`scalar-override`,deviceBound:!1}],[`has_completed_wizard`,{category:`scalar-override`,deviceBound:!1}],[`wizard_dont_show_again`,{category:`scalar-override`,deviceBound:!1}],[`first_prompt_submitted_at`,{category:`scalar-override`,deviceBound:!1}],[`thinking_level`,{category:`scalar-override`,deviceBound:!1}],[`tool_preset`,{category:`scalar-override`,deviceBound:!1}],[`log_level`,{category:`scalar-override`,deviceBound:!1}],[`feedback_repo`,{category:`scalar-override`,deviceBound:!1}],[`permission_mode`,{category:`scalar-override`,deviceBound:!1}],[`prompt_output_token_budget`,{category:`scalar-override`,deviceBound:!1}],[`prompt_wall_clock_budget_ms`,{category:`scalar-override`,deviceBound:!1}],[`session_cost_budget_usd`,{category:`scalar-override`,deviceBound:!1}],[`memory_auto_extract`,{category:`scalar-override`,deviceBound:!1}],[`skill_internalization`,{category:`scalar-override`,deviceBound:!1}],[`residency`,{category:`scalar-override`,deviceBound:!1}],[`skill_loop`,{category:`scalar-override`,deviceBound:!1}],[`history`,{category:`scalar-override`,deviceBound:!1}],[`workflow`,{category:`scalar-override`,deviceBound:!1}],[`todo_continue_max`,{category:`scalar-override`,deviceBound:!1}],[`stall_detection`,{category:`scalar-override`,deviceBound:!1}],[`sandbox`,{category:`scalar-override`,deviceBound:!1}],[`resilience`,{category:`scalar-override`,deviceBound:!1}],[`disabled_plugins`,{category:`array-merge`,deviceBound:!1}],[`model_fallback`,{category:`array-merge`,deviceBound:!1}],[`web_fetch_allowed_hosts`,{category:`array-merge`,deviceBound:!1}],[`recent_mentions`,{category:`array-merge`,deviceBound:!1}],[`shortcuts`,{category:`array-merge`,deviceBound:!1}],[`disabled_agents`,{category:`array-merge`,deviceBound:!1}],[`disabled_tools`,{category:`array-merge`,deviceBound:!1}],[`disabled_skills`,{category:`array-merge`,deviceBound:!1}],[`disabled_hooks`,{category:`array-merge`,deviceBound:!1}],[`permissions`,{category:`array-merge`,deviceBound:!1}],[`permission_always_allow`,{category:`array-merge`,deviceBound:!1}],[`agents`,{category:`record-merge`,deviceBound:!1}],[`categories`,{category:`record-merge`,deviceBound:!1}],[`model_slots`,{category:`record-merge`,deviceBound:!1}],[`mcp_servers`,{category:`record-merge`,deviceBound:!1}],[`recent_models`,{category:`array-merge`,deviceBound:!0}],[`keybinding_overrides`,{category:`record-merge`,deviceBound:!0}],[`installed_plugins`,{category:`array-merge`,deviceBound:!0}],[`plugin_lifecycle`,{category:`scalar-override`,deviceBound:!0}],[`auto_update_check`,{category:`scalar-override`,deviceBound:!0}],[`theme`,{category:`scalar-override`,deviceBound:!0}],[`theme_overrides`,{category:`scalar-override`,deviceBound:!0}],[`theme_preset`,{category:`scalar-override`,deviceBound:!0}],[`proxy`,{category:`scalar-override`,deviceBound:!0}],[`notification`,{category:`scalar-override`,deviceBound:!0}],[`pulse_survey`,{category:`scalar-override`,deviceBound:!0}]]);function at(){let e=S.filter(e=>!J.has(e)&&!Y.has(e));if(e.length>0)throw Error(`ConfigMergeStrategy 字段分类表遗漏以下 SettingKey(需在 FIELD_CLASSIFICATION 或 EXCLUDED_KEYS 中归类): ${e.join(`, `)}`)}const ot=new Set(E);function X(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function Z(e,t){return e===t?!0:Array.isArray(e)&&Array.isArray(t)?e.length===t.length&&e.every((e,n)=>JSON.stringify(e)===JSON.stringify(t[n])):JSON.stringify(e)===JSON.stringify(t)}const st={bypass:0,auto:1,confirm:2,strict:3,readonly:4};function ct(e,t){if(typeof t!=`string`||e===t)return!1;let n=typeof e==`string`?st[e]:void 0,r=st[t];return n===void 0||r===void 0?!0:r<n}function lt(e,t){let n=X(e)?e.enabled:void 0,r=X(t)?t.enabled:void 0;return n===!0&&r===!1}function ut(e,t){let n=Array.isArray(e)?e:[],r=Array.isArray(t)?t:[],i=new Set(n.map(e=>JSON.stringify(e)));return r.filter(e=>!i.has(JSON.stringify(e)))}function dt(e,t,n){if(!ot.has(e))return`safe`;if(e===`permission_mode`)return ct(t,n)?`sensitive`:`safe`;if(e===`sandbox`)return lt(t,n)?`sensitive`:`safe`;if(e===`mcp_servers`){let e=X(t)?t:{},r=X(n)?n:{};for(let[t,n]of Object.entries(r))if(!(t in e)||!Z(e[t],n))return`sensitive`;return`safe`}if(e===`disabled_hooks`)return Z(t,n)?`safe`:`sensitive`;if(e===`permissions`||e===`permission_always_allow`)return ut(t,n).length>0?`sensitive`:`safe`;throw Error(`classifyRisk: 字段 "${e}" 在 HIGH_RISK_PROJECT_KEYS 中但未被显式处理——新增高危字段必须同步补充判定分支,不允许 fail-open`)}function ft(e,t){let n=[];for(let r of S){if(J.has(r)||r===`installed_plugins`||!(r in t))continue;let i=e[r],a=t[r];if(Z(i,a))continue;let o=Y.get(r);o&&n.push({key:r,from:i,to:a,category:o.category,deviceBound:o.deviceBound,risk:dt(r,i,a)})}return n}function pt(e,t){let n=new Map(e.map(e=>[e.id,e])),r=new Map(t.map(e=>[e.id,e])),i=[],a=[];for(let e of t){let t=n.get(e.id);if(!t){i.push(e);continue}e.version!==void 0&&t.version!==void 0&&e.version!==t.version&&a.push({id:e.id,local:t.version,incoming:e.version})}return{toInstall:i,localOnly:e.filter(e=>!r.has(e.id)),versionMismatch:a}}function mt(e,t){return e.some(e=>e.risk===`sensitive`)?!0:t.toInstall.some(e=>(e.capabilities??[]).some(e=>se.includes(e)))}function ht(e,t){let n={};for(let r of e)if(!(t===`safe-only`&&r.risk===`sensitive`))if(r.category===`array-merge`){let e=Array.isArray(r.from)?r.from:[],t=Array.isArray(r.to)?r.to:[],i=new Set(e.map(e=>JSON.stringify(e))),a=[...e];for(let e of t){let t=JSON.stringify(e);i.has(t)||(i.add(t),a.push(e))}n[r.key]=a}else if(r.category===`record-merge`){let e=X(r.from)?r.from:{},t=X(r.to)?r.to:{};n[r.key]={...e,...t}}else n[r.key]=r.to;return n}const gt=new Set([`env`,`headers`]);function _t(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function vt(e){let t=[],n=(e,r)=>{if(!_t(e))return e;let i={};for(let[a,o]of Object.entries(e)){let e=[...r,a];if(r.length===2&&r[0]===`mcp_servers`&&gt.has(a)){t.push({path:e,set:o!==void 0});continue}i[a]=n(o,e)}return i};return{value:n(e,[]),secrets:t}}const yt=1;function bt(e,t,n,r=()=>new Date().toISOString()){let i={};for(let[t,n]of Object.entries(e))if(!J.has(t)&&n!==void 0){if(t===`installed_plugins`){i[t]=n.map(e=>e.sourceSpec&&re(e.sourceSpec)?{...e,sourceSpec:``}:e);continue}i[t]=n}let a=vt(i),o=a.value;return{otto_config_version:1,exported_at:r(),exported_from:n,scope:t,fields:o,...a.secrets.length>0?{redacted_secrets:a.secrets}:{redacted_secrets:[]}}}var xt=class extends Error{constructor(e,t){super(`导入文件 otto_config_version=${e},当前 otto 支持=${t}。请升级 otto 到最新版本后再试。`),this.fileVersion=e,this.supportedVersion=t,this.name=`ExportFileVersionError`}},Q=class extends Error{constructor(e){super(`导入文件格式非法:${e}`),this.name=`ExportFileParseError`}};function $(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function St(e){let t;try{t=JSON.parse(e)}catch(e){throw new Q(e instanceof Error?e.message:String(e))}if(!$(t))throw new Q(`顶层结构必须是 JSON 对象`);let{otto_config_version:n,exported_at:r,exported_from:i,scope:a,fields:o}=t;if(typeof n!=`number`)throw new Q(`缺少或非法的 otto_config_version 字段`);if(n!==1)throw new xt(n,1);if(typeof r!=`string`||typeof i!=`string`)throw new Q(`缺少或非法的 exported_at/exported_from 字段`);if(a!==`global`&&a!==`project`)throw new Q(`scope 字段必须是 "global" 或 "project"`);if(!$(o))throw new Q(`fields 字段必须是 JSON 对象`);return{otto_config_version:n,exported_at:r,exported_from:i,scope:a,fields:o,...Array.isArray(t.redacted_secrets)?{redacted_secrets:t.redacted_secrets}:{}}}export{it as BUILTIN_AGENT_PROFILES,C as BUILTIN_REVIEWER_AGENTS,yt as CURRENT_EXPORT_VERSION,w as DEFAULT_CONFIG,J as EXCLUDED_KEYS,Q as ExportFileParseError,xt as ExportFileVersionError,Y as FIELD_CLASSIFICATION,E as HIGH_RISK_PROJECT_KEYS,g as LOG_LEVELS,B as NEVER_SYNCED_KEYS,e as PERMISSION_MODES,L as RawFileSettingStore,$e as RemoteSettingStore,S as SETTING_KEYS,z as SYNCABLE_KEYS,F as SettingLoader,nt as SettingsManager,Ue as THEME_COLOR_ROLES,h as TOOL_PRESETS,ht as applyMergeStrategy,at as assertFieldClassificationCoversAllKeys,bt as buildExportPayload,pt as classifyPluginChanges,ft as diffConfig,V as filterSyncable,G as globToRegexSource,mt as hasSecuritySensitiveChanges,q as loadClaudeSettingsLayer,I as loadSetting,T as migrate,St as parseExportFile,W as parseToolSpecifier,D as sanitizeUntrustedProjectLayer,K as translateClaudeSettings};
2
2
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["logger","logger","isPlainObject","isPlainObject"],"sources":["../src/constants.ts","../src/schema.ts","../src/types.ts","../src/migrator.ts","../src/project-trust.ts","../src/setting.ts","../src/raw-file-store.ts","../src/remote-sync-keys.ts","../src/remote-setting-store.ts","../src/claude-settings-adapter.ts","../src/settings-manager.ts","../src/presets.ts","../src/config-merge-strategy.ts","../src/config-export-format.ts"],"sourcesContent":["export const TOOL_PRESETS = ['minimal', 'standard', 'full'] as const\nexport type ToolPreset = (typeof TOOL_PRESETS)[number]\n\nexport const LOG_LEVELS = ['trace', 'debug', 'info', 'warn', 'error', 'fatal'] as const\nexport type LogLevel = (typeof LOG_LEVELS)[number]\n\n// 权限模式词表单一真源在 @x-otto/hook-contracts(HK-1,setting/hooks 都下依赖到它);此处 re-export 保 API 不变。\nexport { PERMISSION_MODES } from '@x-otto/hook-contracts'\nexport type { PermissionMode } from '@x-otto/hook-contracts'\n","import { z } from 'zod'\nimport { MODEL_SLOTS } from '@x-otto/orchestration-contracts'\nimport type { PermissionRuleConfig, PermissionPolicySetting } from '@x-otto/hook-contracts'\nimport type { ResilienceConfigOverride } from '@x-otto/env'\nimport { LOG_LEVELS, PERMISSION_MODES, TOOL_PRESETS } from './constants'\n\nconst lenient = <T extends z.ZodType>(schema: T) => schema.optional().catch(undefined)\n\nconst workflowSlotSchema = z.enum(MODEL_SLOTS)\n\nconst agentOptionsSchema = z.object({\n model: lenient(z.string()),\n description: lenient(z.string()),\n system_prompt: lenient(z.string()),\n tools: lenient(z.array(z.string())),\n capabilities: lenient(z.array(z.string())),\n categories: lenient(z.array(z.string())),\n default_workflow_slot: lenient(workflowSlotSchema),\n max_tool_turns: lenient(z.number()),\n max_tool_turn_extensions: lenient(z.number()),\n /** RFC-104 D3:per-prompt output-token 预算(agent 级覆盖)。 */\n prompt_output_token_budget: lenient(z.number()),\n /** RFC-104 D3:per-prompt 墙钟预算(ms,agent 级覆盖)。 */\n prompt_wall_clock_budget_ms: lenient(z.number()),\n /** RFC-204 D1/D2:会话累计成本软预算(美元,agent 级覆盖)。 */\n session_cost_budget_usd: lenient(z.number()),\n /** RFC-204 D4:早期停滞检测配置(agent 级覆盖);false=关闭,对象=自定义窗口/阈值。 */\n stall_detection: lenient(\n z.union([z.literal(false), z.object({ window_turns: lenient(z.number()), repeat_threshold: lenient(z.number()) })]),\n ),\n temperature: lenient(z.number()),\n max_tokens: lenient(z.number()),\n disabled: lenient(z.boolean()),\n})\n\nconst categoryConfigSchema = z.object({\n preferred_models: lenient(z.array(z.string())),\n default_model: lenient(z.string()),\n})\n\n/** 会话结束后自动抽取跨会话经验(operational-learning)。默认关,灰度后再放开。 */\nconst memoryAutoExtractConfigSchema = z.object({\n enabled: lenient(z.boolean()),\n every_n_turns: lenient(z.number()),\n max_lessons: lenient(z.number()),\n similarity_threshold: lenient(z.number()),\n})\n\n/**\n * RFC-318 技能回路:观测重复任务流程并提议固化为 skill。默认关(D8:信噪比未验证前不默认开)。\n * `min_messages` 是触发一次抽取所需的最小新消息数(对齐 memory_auto_extract 的省调用口径)。\n */\nconst skillLoopConfigSchema = z.object({\n enabled: lenient(z.boolean()),\n min_messages: lenient(z.number()),\n /** 拒绝一个提案后,多少天内不再就同一个候选打扰(默认 14)。 */\n dismiss_cooldown_days: lenient(z.number()),\n})\n\nconst modelSlotConfigSchema = z.object({\n slots: lenient(z.partialRecord(workflowSlotSchema, z.union([z.string(), z.array(z.string())]))),\n default: lenient(z.string()),\n /**\n * 委派子会话(subagent)在**未指定 slot/category** 时默认套用的 slot。设为 'compact'\n * 即「未分类委派也默认走便宜档」。仅作用 subagent,主会话不受影响;不设=保持落 default。\n */\n subagent_default_slot: lenient(workflowSlotSchema),\n})\n\nconst notificationConfigSchema = z.object({\n enabled: lenient(z.boolean()),\n sound: lenient(z.boolean()),\n on_completion: lenient(z.boolean()),\n on_error: lenient(z.boolean()),\n /** @deprecated OQ1:on_completion 的别名,运行时 OR 合并。 */\n on_idle: lenient(z.boolean()),\n channel: lenient(\n z.enum(['auto', 'terminal_bell', 'iterm2', 'iterm2_with_bell', 'kitty', 'ghostty', 'disabled']),\n ),\n condition: lenient(z.enum(['unfocused', 'always'])),\n idle_threshold_ms: lenient(z.number()),\n command: lenient(z.array(z.string())),\n})\n\n/**\n * RFC-208 §2.5:满意度脉冲调查触发/开关配置。`sink` 当前仅 `'local'` 合法(预留后续\n * 扩展点,非法值 fallback 到 'local' 并 warn;RFC-209 M2 迁移后见\n * extensions/plugin-feedback/src/pulse-survey-sink.ts 的 resolvePulseSurveySink)。\n */\nconst pulseSurveyConfigSchema = z.object({\n /** 总开关,缺省 true。 */\n enabled: lenient(z.boolean()),\n /** 每次 prompt.end 后的抽样概率(0-1),缺省 0.05。 */\n probability: lenient(z.number()),\n /** 两次弹出之间的最小回合数间隔,缺省 20。 */\n min_turn_gap: lenient(z.number()),\n /** 数据落地 sink 类型,当前仅 'local' 合法。 */\n sink: lenient(z.string()),\n})\n\n/** RFC-129:外部 shell 历史源(默认关闭 opt-in)。 */\nconst externalHistorySourceSchema = z.object({\n id: lenient(z.string()),\n path: lenient(z.string()),\n enabled: lenient(z.boolean()),\n})\n\nconst externalHistoryConfigSchema = z.object({\n enabled: lenient(z.boolean()),\n maxEntries: lenient(z.number()),\n sources: lenient(z.array(externalHistorySourceSchema)),\n})\n\nconst historyConfigSchema = z.object({\n external: lenient(externalHistoryConfigSchema),\n})\n\nconst sandboxConfigSchema = z.object({\n enabled: z.boolean(),\n network: lenient(z.enum(['allow', 'deny'])),\n writablePaths: lenient(z.array(z.string())),\n /**\n * 凭证读隔离(opt-in,默认 false):开启后沙箱拒绝读取 ~/.ssh、~/.aws、~/.gnupg 及\n * otto auth store 目录,堵住凭证经命令读出外泄(含变量间接绕过 command 正则的情形)。\n * 代价:开启后沙箱内 git-over-ssh / aws-cli / gpg 签名会因读不到密钥而失败——故默认关。\n */\n protectCredentials: lenient(z.boolean()),\n /**\n * bash 沙箱越界自动放行(opt-in,默认 false,参照 Claude Code 的\n * `sandbox.autoAllowBashIfSandboxed`,默认关闭以维持 otto 现有\"先问后跑\"安全姿态不变)。\n *\n * 开启后:仅当权限层(含 destructive-guard 破坏性命令探测)已判定该 bash 命令为\n * `allow`(未命中任何安全护栏 ask/deny)时,sandbox-guard 才把纯路径越界的 `ask`\n * 降级为自动放行——交给沙箱本身在执行期强制边界,不再为已判定安全的命令弹审批\n * (对齐 codex `render_decision_for_unmatched_command` 的 \"let the sandbox enforce\n * restrictions without a user prompt\")。危险命令(destructive-guard 已 ask/deny)\n * 与 strict/readonly 模式不受影响,仍走原有审批/拒绝路径。\n */\n autoAllowBashIfSandboxed: lenient(z.boolean()),\n})\n\n/**\n * RFC-323:会话驻留预算配置——用户可通过 `.otto/config.json`(otto 原生配置文件,\n * 非 Claude 的 settings.json)调整,也可经环境变量覆盖(env 优先级 > config.json > 默认值,\n * 与既有常量模式一致)。\n *\n * 所有字段可选;缺省时回退到 `@x-otto/session-contract` 的默认值。\n */\nconst residencyConfigSchema = z.object({\n /** 进程级总预算(字节)。默认 1024MB(≈1GB)。CJK 下真实驻留 ≈ ×2.05。 */\n total_bytes: lenient(z.number()),\n /** 单会话预算下界(字节)。默认 16MB。 */\n min_per_session_bytes: lenient(z.number()),\n /** 单会话预算上界(字节)。默认 256MB。受压缩输入窗口约束。 */\n max_per_session_bytes: lenient(z.number()),\n /** 会话历史条数上限(结构兜底)。默认 50000。 */\n max_history_messages: lenient(z.number()),\n /** resume 尾窗口装载条数。默认 5000(与 max_history_messages 解耦)。 */\n resume_window_messages: lenient(z.number()),\n /** 单会话字节预算(G4 回退默认值,分配器缺席时使用)。默认 64MB。 */\n max_content_bytes: lenient(z.number()),\n /** RFC-324 D2:MemoryGovernor warning 阈值(usedPct 百分比)。\n * 真实堆使用率达此值时,ResidencyGovernor 升至 warning,估算层预算÷2 收紧。\n * 默认 70。env OTTO_MEMORY_WARN_PCT 优先。 */\n warn_threshold_pct: lenient(z.number()),\n /** RFC-324 D2:MemoryGovernor critical 阈值(usedPct 百分比)。\n * 真实堆使用率达此值时,ResidencyGovernor 升至 critical,估算层预算÷4 收紧。\n * 默认 85。env OTTO_MEMORY_HARD_PCT 优先。 */\n hard_threshold_pct: lenient(z.number()),\n /** RFC-324 D1:turn-gated 压缩超时(毫秒)。超时时降级为 fire-and-forget(不永久阻塞 turn)。默认 30000。 */\n compaction_timeout_ms: lenient(z.number()),\n /** RFC-324 D3:摘要质量 warning 阈值。低于此值时调大下次压缩批次上限。默认 0.7。 */\n coverage_warn_threshold: lenient(z.number()),\n /** RFC-324 D3:摘要质量 error 阈值。低于此值时双重调大批次上限(更激进保护)。默认 0.5。 */\n coverage_error_threshold: lenient(z.number()),\n /** RFC-340 D1:摘要质量**硬下限**。低于此值时在摘要正文前置\"低保真\"标注,让精度\n * 损失对模型可见(模型据此会向用户确认缺失细节,而非把不完整摘要当作全部事实)。\n * 与 warn/error 的区别:那两档只调节下次批次策略(对模型不可见),本档改变发给模型\n * 的摘要文本本身。**不阻断压缩**——低覆盖退化为不压缩会级联到 lossy 裸裁剪,严格更差\n * (RFC-340 §D1-v1)。不变式 floor ≤ error ≤ warn(接线层钳制)。默认 0.3。 */\n coverage_floor_threshold: lenient(z.number()),\n // 注:`archive_after_days` 曾在此声明,但**自动归档调度从未实现**(全仓无任何\n // 调度入口),用户配置它无任何效果且无警告——属承诺不存在的功能,故移除\n // (RFC-330 §4.5 记录的静默失效面)。手动归档(archiveSession)不依赖该配置。\n // 自动冷归档需独立 RFC 定义保留期/恢复/失败策略,届时再随实现一并加回。\n})\n\nconst toolsConfigSchema = z.object({\n tool_preset: lenient(z.enum(TOOL_PRESETS)),\n disabled_tools: lenient(z.array(z.string())),\n})\n\nconst workflowReviewSchema = z.object({ enabled: lenient(z.boolean()) })\nconst workflowRetrySchema = z.object({\n enabled: lenient(z.boolean()),\n maxAttempts: lenient(z.number()),\n})\nconst workflowCircuitBreakerSchema = z.object({\n enabled: lenient(z.boolean()),\n failureThreshold: lenient(z.number()),\n timeoutMs: lenient(z.number()),\n})\nconst workflowRoutingSchema = z.object({ enabled: lenient(z.boolean()) })\n\nconst workflowOptionsSchema = z.object({\n enabled: lenient(z.boolean()),\n review: lenient(workflowReviewSchema),\n retry: lenient(workflowRetrySchema),\n circuitBreaker: lenient(workflowCircuitBreakerSchema),\n routing: lenient(workflowRoutingSchema),\n})\n\nconst permissionRuleConfigSchema = z.object({\n name: z.string(),\n effect: z.enum(['allow', 'deny', 'ask']),\n tools: lenient(z.array(z.string())),\n paths: lenient(z.array(z.string())),\n deny_reason: lenient(z.string()),\n ask_prompt: lenient(z.string()),\n}) satisfies z.ZodType<PermissionRuleConfig>\n\nconst permissionPolicySettingSchema = z.object({\n name: z.string(),\n priority: lenient(z.number()),\n enabled: lenient(z.boolean()),\n scope: lenient(\n z.object({\n agents: lenient(z.array(z.string())),\n sessions: lenient(z.array(z.string())),\n }),\n ),\n rules: z.array(permissionRuleConfigSchema),\n}) satisfies z.ZodType<PermissionPolicySetting>\n\nconst proxyConfigSchema = z.object({\n /** 代理地址(http:// 或 https:// 协议)。 */\n url: lenient(z.string()),\n /** 是否启用代理(false=保留配置但停用)。 */\n enabled: lenient(z.boolean()),\n})\n\nconst mcpServerSettingSchema = z.object({\n command: lenient(z.string()),\n args: lenient(z.array(z.string())),\n env: lenient(z.record(z.string(), z.string())),\n url: lenient(z.string()),\n type: lenient(z.enum(['stdio', 'sse', 'http'])),\n headers: lenient(z.record(z.string(), z.string())),\n requestTimeoutMs: lenient(z.number()),\n autoReconnect: lenient(z.boolean()),\n disabled: lenient(z.boolean()),\n})\n\n/**\n * RFC-230:统一重试/超时/退避配置——契约单源在 `@x-otto/env`(`ResilienceConfig`),本 schema\n * 只是配置载体(`.otto/config.json` 的 `resilience` 字段),经 `satisfies z.ZodType<...>` 钉死\n * 与 `@x-otto/env` 契约同构(drift → 编译报错)。\n */\nconst resilienceStreamSchema = z.object({\n maxRetries: lenient(z.number().int().positive()),\n initialDelayMs: lenient(z.number().positive()),\n backoffFactor: lenient(z.number().positive()),\n maxDelayMs: lenient(z.number().positive()),\n})\nconst resilienceNetworkDisconnectSchema = z.object({\n maxRetries: lenient(z.number().int().positive()),\n intervalMs: lenient(z.number().positive()),\n})\nconst resilienceMcpSchema = z.object({\n maxReconnectAttempts: lenient(z.number().int().nonnegative()),\n reconnectDelayMs: lenient(z.number().positive()),\n requestTimeoutMs: lenient(z.number().positive()),\n})\nconst resilienceScheduleSchema = z.object({\n retryBackoffMs: lenient(z.number().nonnegative()),\n})\nconst resilienceErrorRetrySchema = z.object({\n maxAttempts: lenient(z.number().int().positive()),\n intervalMs: lenient(z.number().positive()),\n})\nconst resilienceOauthSchema = z.object({\n // 二轮评审 P1:正数无下限会导致 Math.floor(timeoutMs/3) 心跳间隔可为 0/1,\n // setInterval(fn, 0) 造成忙循环风险(见 D6 OAuth 小节心跳派生逻辑)。\n // 下限 1000ms 保证心跳间隔 >= 333ms,足够远离忙循环风险,同时仍比默认 15000ms 宽松得多,\n // 覆盖任何合理的高级调优场景。与 @x-otto/env 的 OAUTH_REFRESH_LOCK_TIMEOUT_MIN_MS 同值。\n refreshLockTimeoutMs: lenient(z.number().min(1000)),\n})\n\nconst resilienceConfigSchema = z.object({\n stream: lenient(resilienceStreamSchema),\n networkDisconnect: lenient(resilienceNetworkDisconnectSchema),\n mcp: lenient(resilienceMcpSchema),\n schedule: lenient(resilienceScheduleSchema),\n errorRetry: lenient(resilienceErrorRetrySchema),\n oauth: lenient(resilienceOauthSchema),\n}) satisfies z.ZodType<ResilienceConfigOverride>\n\n/**\n * RFC-036 @ 面板「最近使用」条目快照(MRU,最多 5 条)——`/model` `recent_models` 同款\n * 持久化模式的推广:不止模型,@ 面板选中的任意 sigil 条目(文件/agent/skill/mcp/…)\n * 都计入。只持久化渲染所需的最小字段(label/value/kind/description),不持久化\n * `preview`(@file 首屏预览,体积大且易过期,SigilPane 消费 recent 条目时按需重新拉取)。\n */\n/**\n * 已安装插件的权威清单条目(RFC-219 D1)。**逻辑记录**,与文件系统 symlink(物化形态)\n * 分离——安装/卸载时写入/移除,不与磁盘状态做自动双向纠正(不一致时告警引导用户修复,\n * 见 discoverPlugins 消费方)。\n */\nconst installedPluginEntrySchema = z.object({\n id: z.string(),\n source: z.enum(['npm', 'dir', 'url', 'git']),\n /**\n * 来源标识:npm 包名 / 目录路径(导出时若为绝对路径需标准化为空,RFC-219 重要事项\n * 规则 9,防止把导出机器的本地路径写入可分享产物)/ URL。\n */\n sourceSpec: lenient(z.string()),\n installedAt: z.number(),\n version: lenient(z.string()),\n /**\n * 安装目标级别,与 `@x-otto/install` 既有 `InstallScope`('global'|'project'|'repository')\n * 词表对齐,不另创新词表。`installed_plugins` 本身作为清单**恒定存储在 user 级配置\n * 文件**(RFC-219 重要事项规则 5)——此字段记录的是\"安装时的目标目录级别\"语义,\n * 供审计/展示用途,与清单的存储位置是两个独立维度。\n */\n scope: z.enum(['global', 'project', 'repository']),\n /**\n * 安装时声明的高危能力快照(`@x-otto/plugin` `readHighRiskCapabilities()` 输出,RFC-219\n * M2 补充)。安装路径(CLI/TUI)在安装前已读取过这份数据用于信任确认弹窗,此处只是\n * 复用同一份已计算结果持久化,不引入新的文件系统访问。存在的意义:M2\n * `ConfigMergeStrategy.classifyPluginChanges` 需要判断\"导入清单中的新装插件是否声明\n * 高危能力\"以触发 D4 安全门,若不快照该信息,M2 要么读盘(破坏纯函数边界),要么无法\n * 判断——快照字段是让 M2 保持零 I/O 纯函数的必要设计。\n */\n capabilities: lenient(z.array(z.string())),\n})\n\nconst recentMentionEntrySchema = z.object({\n label: z.string(),\n value: z.string(),\n // 与 @x-otto/interchange SigilKind 词表同步(RFC-210 D2 增 'resource'、RFC-233 增\n // 'shortcut'、RFC-360 增 'effort'——不同步则含新 kind 的最近使用条目被 lenient\n // fail-soft 静默丢弃,MRU 丢条目)。\n kind: z.enum(['hashtag', 'file', 'person', 'agent', 'skill', 'mcp', 'plugin', 'model', 'resource', 'shortcut', 'effort']),\n description: lenient(z.string()),\n})\n\n/** RFC-233:用户自定义文本快捷方式条目(`[@shortcut:name]` chip 展开为 `text`)。 */\nconst shortcutEntrySchema = z.object({\n name: z.string().min(1).max(64),\n text: z.string().min(1).max(4000),\n})\n\n/**\n * RFC-277 D3:Markdown typography 语义角色枚举——只允许引用 `ThemeColors` 的 key,\n * 禁止任意 hex(终局约束,不重走 RFC-205 已移除的任意覆盖老路)。这份枚举与\n * `packages/tui/src/theme/colors.ts` 的 `ThemeColors` 接口字段手动同步;两者漂移由\n * `packages/setting/tests/schema.test.ts` 的镜像清单断言钉死(改一处忘改另一处会测试红)。\n */\nconst THEME_COLOR_ROLE_VALUES = [\n 'brand', 'brandShimmer', 'accent', 'accentDim', 'accentBright', 'text', 'inactive',\n 'secondary', 'subtle', 'replyPrefix', 'success', 'error', 'warning', 'special',\n 'suggestion', 'permission', 'codeText', 'diffAdd', 'diffRemove', 'diffAddBg',\n 'diffRemoveBg', 'panelBorder', 'overlayBg', 'tagBg', 'toolText', 'focusBorder', 'selection',\n] as const\nconst themeColorRoleSchema = z.enum(THEME_COLOR_ROLE_VALUES)\n\n/**\n * RFC-277 D3:`MarkdownThemeOverrides` 的 settings 镜像——严格 schema,字段集合与\n * `packages/tui/src/theme/theme-registry.ts` 的 `MarkdownThemeOverrides` 接口手动同步\n * (镜像漂移由跨包类型测试钉死,见 schema.test.ts)。`listMarkers` 需四个非空字符串\n * (单宽校验在渲染层 `resolveMarkdownTypographyTheme` 做,这里只保证类型形状)。\n */\nconst markdownThemeOverridesSchema = z.strictObject({\n heading1: lenient(themeColorRoleSchema),\n heading2: lenient(themeColorRoleSchema),\n headingWeak: lenient(themeColorRoleSchema),\n listMarker: lenient(themeColorRoleSchema),\n listMarkerMuted: lenient(themeColorRoleSchema),\n quoteBar: lenient(themeColorRoleSchema),\n quoteText: lenient(themeColorRoleSchema),\n link: lenient(themeColorRoleSchema),\n inlineCode: lenient(themeColorRoleSchema),\n codeFence: lenient(themeColorRoleSchema),\n tableHeader: lenient(themeColorRoleSchema),\n tableDivider: lenient(themeColorRoleSchema),\n listIndent: lenient(z.union([z.literal(2), z.literal(3)])),\n listMarkers: lenient(z.tuple([\n z.string().min(1), z.string().min(1), z.string().min(1), z.string().min(1),\n ])),\n codeBlockDivider: lenient(z.enum(['hr', 'none'])),\n})\n\n/**\n * RFC-277 D3/D4:顶层主题 override 容器——`.strict()` 只放行 `markdown` 一个字段。\n * 未来若要支持其他排版角色(table/quote 之外的组件),必须显式加字段,不允许开放\n * 任意 key(保持\"受限覆盖面\"约束,防止悄悄扩大成任意配置面)。\n */\nconst themeOverridesSchema = z.strictObject({\n markdown: lenient(markdownThemeOverridesSchema),\n})\n\n/** RFC-277 D2/M6:preset selection 双槽 schema——见 SettingSchema.theme_preset 字段注释。 */\nconst themePresetSelectionSchema = z.strictObject({\n dark: lenient(z.string().min(1).max(128)),\n light: lenient(z.string().min(1).max(128)),\n})\n\nexport const SettingSchema = z.object({\n config_version: lenient(z.string()),\n version: lenient(z.string()),\n log_level: lenient(z.enum(LOG_LEVELS)),\n model: lenient(z.string()),\n model_fallback: lenient(z.array(z.string())),\n /** MRU 最近在前,最多 5 条;持久化真源。 */\n recent_models: lenient(z.array(z.string())),\n /**\n * 最近 @ 到的资源列表(MRU,最多 5 条,最近在前);SigilPane「最近使用」分组的\n * 持久化真源。项目级字段(落 `.otto/config.json`,不随全局 config 走),随目录走——\n * 契合「按项目记住常用 @ 对象」的心智(不同项目常用的文件/agent/skill 天然不同)。\n */\n recent_mentions: lenient(z.array(recentMentionEntrySchema)),\n /**\n * RFC-233:用户自定义 `[@shortcut:name]` 文本快捷方式(name → text 映射)。\n * `/shortcuts` 面板增删改;project 级字段(落 `.otto/config.json` 项目层,同\n * `recent_mentions` 语义——不同仓库常用的指令模板天然不同,不跨机器/账号同步)。\n */\n shortcuts: lenient(z.array(shortcutEntrySchema)),\n /**\n * RFC-207 §2.2/§3.4:`/feedback` 指令提交 issue 的目标仓库(`owner/repo` 格式)。\n * 未配置时 handler 尝试从 `git remote get-url origin` 解析(见 feedback.ts\n * `resolveFeedbackRepo`),仍解析失败则提示用户配置本字段。\n */\n feedback_repo: lenient(z.string()),\n /**\n * RFC-174 D2:用户快捷键覆盖表。key 为 commandId(跨全局命令表与控件级表统一命名空间),\n * value 为覆盖后的完整按键谓词——单条记录内部整体替换非深合并(改了 ctrl 字段后全量\n * 覆盖该 commandId 的谓词对象,不是只替换 ctrl 而保留其余默认字段)。\n * 空对象 `{}` 视为\"未分配\"态(全部字段缺省,`matchKeyBinding`/`matchControlBinding` 恒不匹配)。\n */\n keybinding_overrides: lenient(z.record(z.string(), z.object({\n ctrl: z.boolean().optional(),\n shift: z.boolean().optional(),\n meta: z.boolean().optional(),\n input: z.string().optional(),\n key: z.enum(['tab', 'escape', 'return']).optional(),\n }))),\n language: lenient(z.string()),\n /** TUI 主题模式(RFC-263 M5)。'system' 启动时经 OSC 11 探测终端背景明暗。device-bound,不跨设备同步。 */\n theme: lenient(z.enum(['dark', 'light', 'system'])),\n /**\n * RFC-277 M4:受限主题 override——目前只开放 Markdown typography 语义角色\n * (见 `markdownThemeOverridesSchema`)。**严格 schema**(`.strict()`):任何未声明字段\n * (尤其 RFC-205 移除的 `accent`/`colors`/`theme_spacing`/`theme_border`/`theme_figures`\n * 等旧任意覆盖字段)一律被 zod 拒绝整层,交由上层 `validate()` 的 `.catch(undefined)`\n * 静默丢弃并产出 warning——不允许任意 hex/spacing 复活。device-bound,不跨设备同步。\n */\n theme_overrides: lenient(themeOverridesSchema),\n /**\n * RFC-277 D2/M6:按 appearance 分槽的 preset 选择——`dark`/`light` 各自记录用户为该\n * appearance 选中的 preset effective id(builtin 为 `'dark'`/`'light'`;extension 贡献为\n * `${extensionId}/${localId}`,见 `ThemeRegistry`)。**不是**单个 `theme_preset: string`\n * (D2 已否决该形态——system 模式下 dark/light 需要各自记住用户选择)。id 格式在此不做\n * 强校验(registry 层 `resolve()` 对未知/已卸载 id 有 fallback 到 builtin appearance 的\n * 契约,M1 既有行为),本 schema 只保证类型形状。device-bound,不跨设备同步(同 theme)。\n */\n theme_preset: lenient(themePresetSelectionSchema),\n /** 用户昵称(首次 onboarding 设置,非强制;会话/通知中个性化显示)。 */\n nickname: lenient(z.string().max(64)),\n /** 首次 onboarding 是否已完成(防止 language='auto' 时重复弹出)。 */\n has_completed_onboarding: lenient(z.boolean()),\n /** Setup wizard 是否已走完所有五步(Provider → Trust → Login → Model → Start)。 */\n has_completed_wizard: lenient(z.boolean()),\n /** 用户在欢迎页勾选\"不再提示\"后设为 true,跳过向导自动弹出。 */\n wizard_dont_show_again: lenient(z.boolean()),\n /**\n * RFC-326 §6.5 / M4-02:用户首次实际提交 prompt 的时间戳(epoch ms)。\n * 只在真实提交(非 slash command、非草稿填充)后写入一次,此后恒不变——\n * 用于 Home 首 prompt 建议卡\"发过一次后自然消失\"的判据(`FtueStage`\n * `ready` → `first-prompt` 的转移条件,见 `ftue-snapshot.ts` deriveStage)。\n * 缺省 undefined 表示\"从未提交过\",与 §5.1\"持久化只保留用户意图\"的表述\n * (`ftue.firstPromptSubmittedAt?`)语义一致,字段名遵循本仓扁平 snake_case\n * 既有约定(未采用 RFC 原文的点分层级命名)。\n */\n first_prompt_submitted_at: lenient(z.number()),\n /** 推理投入档(reasoning effort):low|medium|high|xhigh|max;/effort 切换并持久化,下一回合生效。 */\n thinking_level: lenient(z.string()),\n agents: lenient(z.record(z.string(), agentOptionsSchema)),\n categories: lenient(z.record(z.string(), categoryConfigSchema)),\n tool_preset: lenient(z.enum(TOOL_PRESETS)),\n memory_auto_extract: lenient(memoryAutoExtractConfigSchema),\n /** RFC-318:技能回路(会话轨迹 → episode → 候选 → 提案)。默认关。 */\n skill_loop: lenient(skillLoopConfigSchema),\n notification: lenient(notificationConfigSchema),\n /** RFC-208:满意度脉冲调查触发/开关配置。 */\n pulse_survey: lenient(pulseSurveyConfigSchema),\n /** RFC-129:TUI 外部 shell 历史(ghost/Ctrl+R);`external.enabled` 默认 false。 */\n history: lenient(historyConfigSchema),\n workflow: lenient(workflowOptionsSchema),\n model_slots: lenient(modelSlotConfigSchema),\n mcp_servers: lenient(z.record(z.string(), mcpServerSettingSchema)),\n /** RFC-230:统一重试/超时/退避配置——settings 层(优先级:settings > env > 默认值,程序化注入最高)。 */\n resilience: lenient(resilienceConfigSchema),\n sandbox: lenient(sandboxConfigSchema),\n /** RFC-323:会话驻留预算配置。env 优先级 > settings > 默认值。 */\n residency: lenient(residencyConfigSchema),\n disabled_agents: lenient(z.array(z.string())),\n disabled_hooks: lenient(z.array(z.string())),\n disabled_tools: lenient(z.array(z.string())),\n /** 被禁用的 skill 名(/extension 面板切换;SkillRegistry 启动按此过滤、运行时 enable/disable 下一轮生效)。 */\n disabled_skills: lenient(z.array(z.string())),\n /** 被禁用的插件 id(bundle 启停;禁用即其 skill/agent/command/mcp 全部贡献消失,下次发现生效)。 */\n disabled_plugins: lenient(z.array(z.string())),\n /**\n * 已安装插件权威清单(RFC-219 D1)。安装(`installPluginFromPackage`/`installPluginFromDir`)\n * 写入条目,卸载(`@x-otto/install` `uninstall()` 的 plugin 分支)移除条目。与磁盘 symlink\n * 状态不做自动双向纠正,仅供一致性告警(discoverPlugins 消费)。恒定路由 user 级配置\n * 存储(RFC-219 重要事项规则 5),不随 project 级配置导出/导入。\n */\n installed_plugins: lenient(z.array(installedPluginEntrySchema)),\n /** 自动检查更新(启动时异步查 npm registry;仅通知,不自动安装)。 */\n auto_update_check: lenient(z.boolean()),\n /** 上次检查更新的时间戳(ms)。启动时若距上次检查 ≤6h 则跳过。 */\n last_update_check: lenient(z.number()),\n /** RFC-104 D3:per-prompt output-token 预算(顶层缺省;agent 级可覆盖)。 */\n prompt_output_token_budget: lenient(z.number()),\n /** RFC-104 D3:per-prompt 墙钟预算(ms,顶层缺省)。 */\n prompt_wall_clock_budget_ms: lenient(z.number()),\n /** RFC-204 D1/D2:会话累计成本软预算(美元,顶层缺省;agent 级可覆盖)。 */\n session_cost_budget_usd: lenient(z.number()),\n /** RFC-204 D4:早期停滞检测配置(顶层缺省;agent 级可覆盖);false=关闭。 */\n stall_detection: lenient(\n z.union([z.literal(false), z.object({ window_turns: lenient(z.number()), repeat_threshold: lenient(z.number()) })]),\n ),\n /** RFC-104 D4:todo 闸门非生产续跑轮上限(settings 优先于 env OTTO_TODO_CONTINUE_MAX;缺省 3)。 */\n todo_continue_max: lenient(z.number()),\n permission_mode: lenient(z.enum(PERMISSION_MODES)),\n permissions: lenient(z.array(permissionPolicySettingSchema)),\n /** 用户「始终允许」的工具名(R8-B):把该工具的 ask 升级为 allow,绝不覆盖 deny。 */\n permission_always_allow: lenient(z.array(z.string())),\n /**\n * 插件生命周期状态(RFC-080)。\n * `setupDone` 记录已执行过 setup 钩子的插件 id + 版本号(版本变化时重新执行)。\n */\n plugin_lifecycle: lenient(\n z.object({\n setupDone: lenient(\n z.array(\n z.object({\n id: z.string(),\n version: lenient(z.string()),\n }),\n ),\n ),\n }),\n ),\n /**\n * 出网 allowlist(opt-in):web_fetch 等带 url 参数的工具,host 不在此表 → 升 ask。\n * 空表 = 不限制(默认,零影响)。条目支持精确域或子域后缀(example.com 匹配 a.example.com)。\n */\n /** 出站 HTTP 代理配置(RFC-xxx /proxy 指令)。全局层,不读项目 config。 */\n proxy: lenient(proxyConfigSchema),\n web_fetch_allowed_hosts: lenient(z.array(z.string())),\n _migrations: lenient(z.array(z.string())),\n})\n\nexport type Setting = z.infer<typeof SettingSchema>\nexport type SettingFromSchema = Setting\n\nexport type ThemeColorRole = z.infer<typeof themeColorRoleSchema>\nexport type MarkdownThemeOverridesSetting = z.infer<typeof markdownThemeOverridesSchema>\nexport type ThemeOverridesSetting = z.infer<typeof themeOverridesSchema>\nexport type ThemePresetSelectionSetting = z.infer<typeof themePresetSelectionSchema>\n/** 镜像清单单一真源,供 schema.test.ts 与消费方核对不漂移。 */\nexport const THEME_COLOR_ROLES = THEME_COLOR_ROLE_VALUES\n\nexport type RecentMentionEntry = z.infer<typeof recentMentionEntrySchema>\nexport type InstalledPluginEntry = z.infer<typeof installedPluginEntrySchema>\n\nexport type AgentOptions = z.infer<typeof agentOptionsSchema>\nexport type AgentsConfig = z.infer<typeof SettingSchema>['agents']\nexport type CategoryConfig = z.infer<typeof categoryConfigSchema>\nexport type CategoriesConfig = z.infer<typeof SettingSchema>['categories']\nexport type MemoryAutoExtractConfig = z.infer<typeof memoryAutoExtractConfigSchema>\nexport type SkillLoopConfig = z.infer<typeof skillLoopConfigSchema>\nexport type ModelSlotConfig = z.infer<typeof modelSlotConfigSchema>\nexport type NotificationConfig = z.infer<typeof notificationConfigSchema>\nexport type PulseSurveyConfig = z.infer<typeof pulseSurveyConfigSchema>\nexport type ExternalHistoryConfig = z.infer<typeof externalHistoryConfigSchema>\nexport type HistoryConfig = z.infer<typeof historyConfigSchema>\nexport type SandboxConfig = z.infer<typeof sandboxConfigSchema>\nexport type ToolsConfig = z.infer<typeof toolsConfigSchema>\n// M-B gate ③:WorkflowOptions 单源为 @x-otto/orchestration-contracts(retry+circuitBreaker 2 字段)。\n// setting 侧 workflow 字段保留在 zod schema 中(用户可配 review/routing 等预留键),\n// 但其类型导出已摘除——零外部消费方,避免与 orchestration 侧同名异形类型形成双源漂移。\n// 配置类型单一真源在 @x-otto/hook-contracts(HK-1);zod schema 经上方 `satisfies` 钉死到它(drift→编译错)。\nexport type { PermissionRuleConfig, PermissionPolicySetting } from '@x-otto/hook-contracts'\nexport type ProxyConfig = z.infer<typeof proxyConfigSchema>\nexport type McpServerSetting = z.infer<typeof mcpServerSettingSchema>\nexport type McpServersConfig = z.infer<typeof SettingSchema>['mcp_servers']\n\nexport const SETTING_KEYS = SettingSchema.keyof().options\nexport type SettingKey = keyof Setting\n","import type { SettingStore } from './store'\n\nexport { LOG_LEVELS, PERMISSION_MODES, TOOL_PRESETS } from './constants'\nexport type { LogLevel, PermissionMode, ToolPreset } from './constants'\n\nexport { SettingSchema, SETTING_KEYS, THEME_COLOR_ROLES } from './schema'\nexport type {\n Setting,\n SettingFromSchema,\n SettingKey,\n RecentMentionEntry,\n InstalledPluginEntry,\n AgentOptions,\n AgentsConfig,\n CategoryConfig,\n CategoriesConfig,\n ModelSlotConfig,\n NotificationConfig,\n PulseSurveyConfig,\n ExternalHistoryConfig,\n HistoryConfig,\n SandboxConfig,\n ToolsConfig,\n // M-B gate ③: WorkflowOptions 单源为 @x-otto/orchestration-contracts。\n // setting 的 workflow zod schema 仍在(配置载体),类型导出已摘除。\n PermissionRuleConfig,\n PermissionPolicySetting,\n McpServerSetting,\n McpServersConfig,\n ProxyConfig,\n // RFC-277 M4:受限主题 override 类型。\n ThemeColorRole,\n MarkdownThemeOverridesSetting,\n ThemeOverridesSetting,\n // RFC-277 D2/M6:preset selection 双槽类型。\n ThemePresetSelectionSetting,\n} from './schema'\n\nimport type { AgentOptions, Setting } from './schema'\n\nexport const BUILTIN_REVIEWER_AGENTS: Record<string, AgentOptions> = {\n 'spec-reviewer': {\n description: 'Reviews implementation against specification requirements',\n categories: ['review'],\n default_workflow_slot: 'critique',\n },\n 'quality-reviewer': {\n description: 'Reviews code quality, patterns, and best practices',\n categories: ['review'],\n default_workflow_slot: 'critique',\n },\n}\n\nexport interface SettingWarning {\n key: string\n message: string\n line?: number\n column?: number\n}\n\nexport interface LoadSettingOptions {\n store?: SettingStore\n paths?: string[]\n /** RFC-032 D4:最低优先级附加层(如翻译后的 `.claude/settings.json`),merge 在文件层之前。 */\n extraLayers?: Partial<Setting>[]\n /**\n * 未受信任的项目配置文件绝对路径(须与 `paths` 中某项**逐字相等**)。命中即对该层\n * 剔除高危键(permission_mode/mcp_servers/… 见 project-trust.ts)。缺省 = 全信任,不门控。\n */\n untrustedProjectPath?: string\n}\n\nexport const DEFAULT_CONFIG: Setting = {\n config_version: '1.0.0',\n log_level: 'info',\n model: undefined,\n recent_models: [],\n recent_mentions: [],\n shortcuts: [],\n language: 'auto',\n theme: undefined,\n nickname: undefined,\n has_completed_onboarding: undefined,\n has_completed_wizard: undefined,\n wizard_dont_show_again: undefined,\n first_prompt_submitted_at: undefined,\n thinking_level: undefined,\n tool_preset: 'full',\n agents: { ...BUILTIN_REVIEWER_AGENTS },\n categories: {},\n workflow: {},\n disabled_agents: [],\n disabled_hooks: [],\n disabled_tools: [],\n disabled_skills: [],\n disabled_plugins: [],\n installed_plugins: [],\n auto_update_check: true,\n last_update_check: undefined,\n permission_mode: 'auto',\n permissions: [],\n permission_always_allow: [],\n web_fetch_allowed_hosts: [],\n notification: {\n enabled: true,\n sound: false,\n on_completion: true,\n on_error: true,\n channel: 'auto',\n condition: 'unfocused',\n idle_threshold_ms: 60000,\n },\n}\n","import { compare } from 'semver'\nimport { createLogger } from '@x-otto/shared'\n\nconst logger = createLogger('@x-otto/setting:migrator')\n\nexport interface Migration {\n version: string\n description: string\n migrate(config: Record<string, unknown>): Record<string, unknown>\n}\n\nexport function migrate(\n config: Record<string, unknown>,\n migrations: Migration[] = [],\n): {\n config: Record<string, unknown>\n applied: string[]\n} {\n const currentVersion = typeof config.config_version === 'string' ? config.config_version : '0.0.0'\n\n const applied: string[] = []\n let result = { ...config }\n\n for (const migration of migrations) {\n if (compare(migration.version, currentVersion) > 0) {\n result = migration.migrate(result)\n result.config_version = migration.version\n applied.push(`${currentVersion} → ${migration.version}`)\n\n logger.info(\n { from: currentVersion, to: migration.version },\n `Applying migration: ${migration.description}`,\n )\n }\n }\n\n if (applied.length > 0) {\n const existingMigrations = Array.isArray(result._migrations)\n ? (result._migrations as string[])\n : []\n\n result._migrations = [...existingMigrations, ...applied]\n }\n\n return { config: result, applied }\n}\n","import type { Setting } from './schema'\n\n/**\n * project-trust.ts —— 项目级 `.otto/config.json` 高危键信任门(纯函数,无 IO).\n *\n * 威胁:克隆任意仓库时,项目 `.otto/config.json` 是攻击者可控输入。其中若干键能在\n * **加载即生效**地提权或执行任意代码——例如 `permission_mode: \"bypass\"` 自动批准所有工具、\n * `mcp_servers` 启动即 spawn 任意二进制、`permissions`/`permission_always_allow` 预授权\n * Bash 等。otto 既有信任门只覆盖 `.claude/` 与 `.mcp.json`(见 coding `claude-trust.ts`),\n * 而 otto 原生 `.otto/config.json` 的这些键此前**无条件全信任**加载——本模块补齐该缺口。\n *\n * 对照业界 CLI 实现 `sanitize_project_config`(project 层 strip 掉 credential/provider/command 类键)\n * 与业界终端 Agent 实现(`.mcp.json` 默认 pending、trust-敏感开关排除 projectSettings)。otto 取\"未信任则\n * strip 高危键 + 告警,配置其余部分照常加载\"——安全降级而非拒绝启动。\n *\n * 纯层:只定义\"哪些键高危\"与\"如何剔除\",不做信任判定/持久化/IO(那是 coding 层职责)。\n */\n\n/**\n * 项目层未受信任时须剔除的高危键。每个都能\"加载即提权/执行\":\n * - `permission_mode` —— 'bypass'/'auto' 自动批准工具 → 克隆即 RCE。\n * - `permissions` —— 预声明 allow 规则(如 Bash(*))→ 预授权执行。\n * - `permission_always_allow` —— 预填会话级永久放行 → 同上。\n * - `mcp_servers` —— stdio server 启动即 spawn `command`+`args` 任意二进制。\n * - `disabled_hooks` —— 可关掉 permission-guard / sandbox-guard 等安全 hook。\n * - `sandbox` —— 可弱化/关闭沙箱(逃逸)。\n *\n * 刻意不含的键:`model`/`model_slots`(otto provider 在代码注册、非 config 定义 base_url,\n * 重定向风险低于业界 CLI 实现的 `model_providers`)、`disabled_tools`(减能力非提权)、`agents`\n * (prompt+工具限制、加载不执行,与业界终端 Agent 实现视 agent 为非可执行面一致)。这些如需收紧另议。\n */\nexport const HIGH_RISK_PROJECT_KEYS = [\n 'permission_mode',\n 'permissions',\n 'permission_always_allow',\n 'mcp_servers',\n 'disabled_hooks',\n 'sandbox',\n] as const satisfies readonly (keyof Setting)[]\n\nexport interface SanitizeResult {\n /** 剔除高危键后的层(浅拷贝,不改入参)。 */\n value: Partial<Setting>\n /** 实际被剔除的键名(用于告警;空数组 = 无高危键,可视为未门控)。 */\n stripped: string[]\n}\n\n/**\n * 从未受信任的项目层剔除高危键。纯函数:返回新对象,不改入参。\n * 仅作用于显式存在的键(`in` 命中),不会注入默认值。\n */\nexport function sanitizeUntrustedProjectLayer(layer: Partial<Setting>): SanitizeResult {\n const value: Partial<Setting> = { ...layer }\n const stripped: string[] = []\n for (const key of HIGH_RISK_PROJECT_KEYS) {\n if (key in value) {\n delete value[key]\n stripped.push(key)\n }\n }\n return { value, stripped }\n}\n","import { createLogger, parseJsonc } from '@x-otto/shared'\nimport { LOG_LEVELS, SETTING_KEYS, SettingSchema } from './types'\nimport { migrate } from './migrator'\nimport { sanitizeUntrustedProjectLayer } from './project-trust'\n\nimport { type Setting, type LoadSettingOptions, type SettingWarning, DEFAULT_CONFIG } from './types'\nimport type { SettingStore } from './store'\n\nconst logger = createLogger('@x-otto/setting')\nconst LOG_LEVEL_SET = new Set<string>(LOG_LEVELS)\n\nconst REMOVED_LEGACY_KEYS = ['mcp', 'skills', 'disabled_mcps', 'disabled_skills'] as const\n\n/**\n * RFC-205 移除的旧任意主题覆盖字段——曾经允许用户往 `config.json` 顶层写这些键实现\n * 任意 hex/spacing/figures 覆盖。RFC-277 M4 恢复了 `theme_overrides` 字段名,但语义\n * 收窄为「只放行 markdown typography 语义角色」;若磁盘上仍残留旧顶层字段,必须继续\n * 忽略并 warn-once(不能因为字段名复活就静默复活旧任意覆盖行为)。\n */\nconst REMOVED_LEGACY_THEME_KEYS = ['theme_file', 'theme_spacing', 'theme_border', 'theme_figures'] as const\n\n/**\n * `theme_overrides` 本身作为字段名被 RFC-277 M4 复用,但**旧 shape**(顶层直接放\n * `accent`/`colors`/`spacing`/`border`/`figures` 等任意覆盖键,而非新 shape 的\n * `{ markdown: {...} }`)必须被识别并单独 warn——不能让 `SettingSchema.strictObject`\n * 的静默丢弃看起来像\"什么都没发生\",用户需要知道旧配置已失效。\n */\nconst LEGACY_THEME_OVERRIDES_KEYS = new Set([\n 'accent', 'accentDim', 'accentBright', 'colors', 'spacing', 'border', 'figures',\n 'text', 'inactive', 'secondary', 'success', 'error', 'warning', 'special',\n])\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\n/**\n * 递归发现被 `lenient()`(`.optional().catch(undefined)`)静默吞掉的嵌套字段(F9)。\n *\n * 背景:SettingSchema 的每个字段都是 lenient——根级字段类型错误已被上层 `validate()` 的\n * 单层比对捕获,但**嵌套对象**(如 `agents.<id>.max_tool_turns`、`mcp_servers.<id>.command`)\n * 的子字段类型错误只让该子字段静默变 undefined,父对象仍然存在,单层比对因此漏检。\n *\n * 只递归 plain object(含 record 形态的动态键对象,如 `agents`/`mcp_servers`)——数组字段\n * (如 `permissions`)在 schema 里是 `lenient(z.array(...))`,单个元素类型错误会让**整个数组**\n * 被 catch 吞成 undefined(非逐元素丢弃),故数组场景已由上层单层比对(`value[key] === undefined`)\n * 覆盖,无需在此递归数组内部。\n */\nfunction collectDroppedNestedFields(\n input: Record<string, unknown>,\n parsed: Record<string, unknown>,\n path: string[],\n warnings: SettingWarning[],\n): void {\n for (const [key, inputVal] of Object.entries(input)) {\n if (inputVal === undefined) continue\n const parsedVal = parsed[key]\n const fullKey = [...path, key].join('.')\n if (parsedVal === undefined) {\n warnings.push({ key: fullKey, message: `Invalid value for \"${fullKey}\" ignored` })\n continue\n }\n if (isPlainObject(inputVal) && isPlainObject(parsedVal)) {\n collectDroppedNestedFields(inputVal, parsedVal, [...path, key], warnings)\n }\n }\n}\n\nfunction deepMerge(\n target: Record<string, unknown>,\n source: Record<string, unknown>,\n): Record<string, unknown> {\n const result = { ...target }\n\n for (const [key, value] of Object.entries(source)) {\n if (value === undefined) {\n continue\n }\n\n if (isPlainObject(value) && isPlainObject(result[key])) {\n result[key] = deepMerge(result[key], value)\n } else {\n result[key] = value\n }\n }\n\n return result\n}\n\nfunction merge(...layers: Partial<Setting>[]): Partial<Setting> {\n const [lower, higher, ...others] = layers\n const result: Record<string, unknown> = { ...lower }\n\n if (higher) {\n for (const [key, value] of Object.entries(higher)) {\n if (value === undefined) {\n continue\n }\n\n if (Array.isArray(result[key]) && Array.isArray(value)) {\n const existing = result[key] as string[]\n const incoming = value as string[]\n\n result[key] = [...new Set([...existing, ...incoming])]\n } else if (isPlainObject(value) && isPlainObject(result[key])) {\n result[key] = deepMerge(result[key], value)\n } else {\n result[key] = value\n }\n }\n }\n\n if (others.length > 0) {\n return merge(result as Partial<Setting>, ...others)\n }\n\n return result as Partial<Setting>\n}\n\nfunction loadSettingFromEnv(): Partial<Setting> {\n const setting: Partial<Setting> = {}\n\n const model = process.env.OTTO_MODEL\n if (model) {\n setting.model = model\n }\n\n const logLevel = process.env.OTTO_LOG_LEVEL\n if (logLevel) {\n if (LOG_LEVEL_SET.has(logLevel)) {\n setting.log_level = logLevel as Setting['log_level']\n }\n }\n\n return setting\n}\n\n/** 导出供单测直接验证 lenient() 静默丢弃字段的告警覆盖(F9),非公开配置加载 API 的一部分。 */\nexport function validate(setting: Record<string, unknown>): {\n value: Partial<Setting>\n warnings: SettingWarning[]\n} {\n const warnings: SettingWarning[] = []\n const input: Record<string, unknown> = { ...setting }\n\n for (const key of REMOVED_LEGACY_KEYS) {\n if (input[key] !== undefined) {\n warnings.push({ key, message: 'Removed legacy config key is ignored' })\n delete input[key]\n }\n }\n\n // RFC-205/RFC-277:`theme_file`/`theme_spacing`/`theme_border`/`theme_figures` 顶层键\n // 已随 RFC-205 移除;即使 `theme_overrides` 字段名在 RFC-277 M4 被复用,这几个独立\n // 顶层键仍必须继续被拒绝——防止旧任意主题覆盖机制的其他残留片段悄悄复活。\n for (const key of REMOVED_LEGACY_THEME_KEYS) {\n if (input[key] !== undefined) {\n warnings.push({ key, message: 'RFC-205 legacy theme override key is ignored' })\n delete input[key]\n }\n }\n\n // RFC-277 M4:`theme_overrides` 旧 shape(顶层直接放 accent/colors/spacing 等任意覆盖键,\n // 而非新 shape `{ markdown: {...} }`)单独识别并给出明确 warning——比通用的\n // \"Invalid value for theme_overrides ignored\" 更能定位问题根因(RFC-205 legacy shape\n // vs 单纯拼写错误),不能让用户以为是新 schema 校验失败。\n if (isPlainObject(input.theme_overrides)) {\n const legacyKeys = Object.keys(input.theme_overrides).filter((k) => LEGACY_THEME_OVERRIDES_KEYS.has(k))\n if (legacyKeys.length > 0) {\n warnings.push({\n key: 'theme_overrides',\n message: `RFC-205 legacy theme_overrides shape (keys: ${legacyKeys.join(', ')}) is ignored — use theme_overrides.markdown instead`,\n })\n }\n }\n\n const result = SettingSchema.loose().safeParse(input)\n const parsed: Record<string, unknown> = result.success ? result.data : input\n\n const value: Record<string, unknown> = {}\n for (const [key, val] of Object.entries(parsed)) {\n if (val !== undefined) {\n value[key] = val\n }\n }\n\n for (const key of SETTING_KEYS) {\n const inputVal = input[key]\n const valueVal = value[key]\n if (inputVal !== undefined && valueVal === undefined) {\n warnings.push({ key, message: `Invalid value for \"${key}\" ignored` })\n continue\n }\n // F9:根级字段本身有效(对象未被整体吞掉)时,递归比对其内部,捕获嵌套子字段被\n // lenient() 静默丢弃的情况(如 agents.<id>.max_tool_turns 类型错误)。\n if (isPlainObject(inputVal) && isPlainObject(valueVal)) {\n collectDroppedNestedFields(inputVal, valueVal, [key], warnings)\n }\n }\n\n return { value: value as Partial<Setting>, warnings }\n}\n\nasync function loadSettingFromStore(\n store: SettingStore,\n paths: string[],\n untrustedProjectPath?: string,\n): Promise<{ layers: Partial<Setting>[]; stripped: string[] }> {\n const layers: Partial<Setting>[] = []\n const stripped: string[] = []\n\n for (const path of paths) {\n try {\n const content = await store.read(path)\n if (content !== undefined) {\n let parsed = parseJsonc<Partial<Setting>>(content)\n // 未受信任的项目层(被克隆仓的 .otto/config.json)剔除高危键——加载即降级。\n if (untrustedProjectPath !== undefined && path === untrustedProjectPath) {\n const sanitized = sanitizeUntrustedProjectLayer(parsed)\n parsed = sanitized.value\n stripped.push(...sanitized.stripped)\n }\n layers.push(parsed)\n logger.debug({ path }, 'Config loaded from store')\n }\n } catch (error) {\n logger.warn({ path, err: error }, 'Failed to parse config file, skipping')\n }\n }\n\n return { layers, stripped }\n}\n\nexport class SettingLoader {\n private store?: SettingStore\n\n constructor(store?: SettingStore) {\n this.store = store\n }\n\n async load(options: LoadSettingOptions = {}): Promise<Setting> {\n const { store = this.store, paths = [], extraLayers = [], untrustedProjectPath } = options\n\n const loaded =\n store && paths.length > 0\n ? await loadSettingFromStore(store, paths, untrustedProjectPath)\n : { layers: [] as Partial<Setting>[], stripped: [] as string[] }\n const { layers, stripped } = loaded\n\n for (const key of stripped) {\n logger.warn(\n { key, path: untrustedProjectPath },\n '未受信任项目配置剔除高危键(permission_mode/mcp_servers 等需先信任该目录)',\n )\n }\n\n const env = loadSettingFromEnv()\n const merged = merge(DEFAULT_CONFIG, ...extraLayers, ...layers, env)\n\n const { config: migrated, applied } = migrate(merged as Record<string, unknown>)\n // 架构 review 2026-08-12 S8:migrations 参数当前无生产传入(恒空数组,migrate 为\n // 空转脚手架)——config 演进实际靠 lenient 静默丢弃 + validate() warning 的\n // fail-soft 单一策略。config_version/_migrations 字段被 remote-sync-keys 白名单与\n // config-export-format 排除表引用,删除会牵动远端同步契约面,故保留机制为未来\n // 「需要真数据迁移的破坏性变更」挂点,不在本次随手清单内拆除。\n if (applied.length > 0) {\n logger.info({ applied }, 'Config migrations applied')\n }\n\n const { value: validated, warnings } = validate(migrated as Record<string, unknown>)\n for (const warning of warnings) {\n logger.warn({ key: warning.key, message: warning.message }, 'Config validation issue')\n }\n\n return { ...DEFAULT_CONFIG, ...validated }\n }\n}\n\nexport async function loadSetting(options: LoadSettingOptions = {}): Promise<Setting> {\n const loader = new SettingLoader(options.store)\n return loader.load(options)\n}\n","import { safeStringify } from '@x-otto/shared'\nimport { atomicWriteFile } from '@x-otto/persistence'\n\nimport type { SettingStore } from './store'\nimport type { Setting } from './types'\n\n/**\n * 原文件 SettingStore(M14-06):按**字面路径**读写用户手写配置(.otto/config.json)。\n *\n * 修复点:此前 SettingsManager 无缺省 store(`this.store` 从未赋值)→ 项目配置文件在\n * 生产上从未被读取;而 persistence-backed 的 file store 是 snapshot 信封式\n * (id → baseDir/<encoded>.json),无法读取用户在固定路径手写的 jsonc 原文——\n * 两者叠加 = 配置文件整条链路死亡。本 store 面向\"用户手写配置文件\"语义:\n * 原文读取(jsonc 解析在 loader 层)、缺失返回 undefined、写入为格式化 JSON。\n *\n * RFC-303 D7(M2 T303-04):`write` 此前用裸 `writeFile`(truncate+写),非原子——\n * 跨进程两个 otto 并发 `persist()` 会交错 truncate/write,读者可能读到半写文件(下次\n * `loadSetting` 时 parseJsonc 失败、配置整体丢失或回退)。改用 `@x-otto/persistence` 的\n * `atomicWriteFile`(temp+fsync+rename+dir-fsync)——跨进程读者永不看到半写文件。\n * 不加跨进程 read-modify-write 锁(当前无实证丢写事故,先原子化消除损坏面;锁待有\n * 实证再加——反过度设计,同 RFC-303 D7 决策记录)。\n */\nexport class RawFileSettingStore implements SettingStore {\n readonly type = 'file' as const\n\n async read(path: string): Promise<string | undefined> {\n const { readFile } = await import('node:fs/promises')\n try {\n return await readFile(await expandPath(path), 'utf-8')\n } catch {\n return undefined\n }\n }\n\n async write(path: string, config: Partial<Setting>): Promise<void> {\n const { mkdir } = await import('node:fs/promises')\n const { dirname } = await import('node:path')\n\n const target = await expandPath(path)\n await mkdir(dirname(target), { recursive: true })\n await atomicWriteFile(target, safeStringify(config, 2))\n }\n\n async exists(path: string): Promise<boolean> {\n const { access } = await import('node:fs/promises')\n try {\n await access(await expandPath(path))\n return true\n } catch {\n return false\n }\n }\n}\n\nasync function expandPath(path: string): Promise<string> {\n if (!path.startsWith('~/')) {\n return path\n }\n const { homedir } = await import('node:os')\n return `${homedir()}${path.slice(1)}`\n}\n","import type { Setting, SettingKey } from './schema'\n\n/**\n * RFC-052 D2:可跨机器同步的用户偏好字段(syncable keys 白名单)。\n *\n * 与 SettingSchema 同包维护,字段增删时编译期提醒同步检查(R2)。\n */\nexport const SYNCABLE_KEYS = new Set<SettingKey>([\n 'model',\n 'model_fallback',\n 'recent_models',\n 'language',\n 'nickname',\n 'has_completed_onboarding',\n 'has_completed_wizard',\n 'wizard_dont_show_again',\n 'first_prompt_submitted_at',\n 'log_level',\n 'notification',\n 'auto_update_check',\n 'model_slots',\n 'disabled_agents',\n 'disabled_hooks',\n 'disabled_tools',\n 'disabled_skills',\n])\n\n/**\n * RFC-052 D2:绝不可远程同步的字段(安全面/本地路径/凭据)。\n *\n * 此列表保持与 SYNCABLE_KEYS 互斥;不在任一集合的字段为\"当前不支持同步,未来可能加入\"。\n */\nexport const NEVER_SYNCED_KEYS: ReadonlySet<string> = new Set([\n // recent_mentions 常含本地文件相对路径(@file 条目)——跨机器/跨项目同步会产生\n // 指向不存在文件的幽灵项,且该字段本就是项目级(落 .otto/config.json 项目层,\n // 语义上与 recent_models 的\"用户全局偏好\"不同),不应走远端 user-scope 同步。\n 'recent_mentions',\n // RFC-233:@ 快捷方式是仓库特定的指令模板(同 recent_mentions 语义),不应跨机器/\n // 账号同步——不同仓库的常用指令天然不同。\n 'shortcuts',\n 'mcp_servers',\n 'sandbox',\n 'permission_mode',\n 'permissions',\n 'permission_always_allow',\n 'web_fetch_allowed_hosts',\n 'session',\n 'tool_preset',\n 'config_version',\n 'version',\n '_migrations',\n 'last_update_check',\n 'memory_auto_extract',\n 'skill_loop',\n 'workflow',\n 'categories',\n 'agents',\n 'disabled_plugins',\n // RFC-219 D1:插件安装清单是设备绑定项(symlink 目标路径/安装来源与本机状态强相关),\n // 不应经 RemoteSettingStore 同步到远端账号——同一账号的另一台设备可能压根没装同一批插件。\n 'installed_plugins',\n])\n\n/**\n * 从 Partial<Setting> 中过滤出仅 syncable 字段(R2)。\n * NEVER_SYNCED 字段即使传入也被移除。\n */\nexport function filterSyncable(partial: Partial<Setting>): Partial<Setting> {\n const result: Partial<Setting> = {}\n for (const key of Object.keys(partial) as SettingKey[]) {\n if (SYNCABLE_KEYS.has(key) && !NEVER_SYNCED_KEYS.has(key)) {\n ;(result as Record<string, unknown>)[key] = partial[key]\n }\n }\n return result\n}\n","import { filterSyncable } from './remote-sync-keys'\nimport type { SettingStore } from './store'\nimport type { Setting } from './types'\n\n/**\n * 远端 user 级配置同步的 HTTP SettingStore(RFC-052 M71d)。\n *\n * 读:HTTP GET `/config/user` → 还原 syncable 字段子集\n * 写:filterSyncable() 过滤 → HTTP PUT `/config/user` push\n *\n * R5 降级:远端不可达时 read 返回 undefined、write 静默忽略,不阻塞启动。\n * R2:syncable 白名单由 remote-sync-keys.ts 维护。\n */\nexport class RemoteSettingStore implements SettingStore {\n readonly type = 'file' as const\n\n private readonly baseUrl: string\n private readonly userId: string\n private readonly getAuth: () => Promise<{ token: string }>\n private readonly fetchImpl: typeof globalThis.fetch\n private readonly timeoutMs: number\n\n constructor(options: {\n /** 远端服务器 base URL(不尾斜线)。 */\n baseUrl: string\n /** 获取 Bearer token 的工厂。 */\n getAuth: () => Promise<{ token: string }>\n /** fetch 实现(测试注入点)。 */\n fetch: typeof globalThis.fetch\n /** 请求超时 (ms)。 */\n timeoutMs: number\n /** 当前用户 identity id。 */\n userId: string\n }) {\n this.baseUrl = options.baseUrl.replace(/\\/+$/, '')\n this.userId = options.userId\n this.getAuth = options.getAuth\n this.fetchImpl = options.fetch\n this.timeoutMs = options.timeoutMs\n }\n\n /**\n * HTTP GET `/config/user` → JSON 同步字段子集。\n *\n * **失败传播(RFC-354 P5 修正,2026-08-12)**:同 `write()`——此前 `catch {}` 吞掉\n * 一切并返回 undefined,使 `SettingsManager.reload()` 里 S10 新增的\n * `catch → emit('remote-sync-failed', {op:'read'})` 永不触发。现在向调用方抛出;\n * manager 侧已有 try/catch 接住并发事件,**本地设置仍正常加载**(remoteLayer 保持\n * undefined 回退本地层),reload() 契约不变。\n *\n * \"远端读不到 = 无远端层\"与\"远端读失败 = 应当可诊断\"是两件事,此前被混为一谈。\n */\n async read(_path: string): Promise<string | undefined> {\n const auth = await this.getAuth()\n const headers: Record<string, string> = {\n Authorization: `Bearer ${auth.token}`,\n Accept: 'application/json',\n 'X-Otto-User-Id': this.userId,\n }\n\n const controller = new AbortController()\n const timeout = setTimeout(() => controller.abort(), this.timeoutMs)\n\n let response: Response\n try {\n response = await this.fetchImpl(`${this.baseUrl}/config/user`, {\n method: 'GET',\n headers,\n signal: controller.signal,\n })\n } finally {\n clearTimeout(timeout)\n }\n\n // 404 = 该用户尚无远端配置,是正常状态而非失败——返回 undefined 回退本地层,\n // 不发失败事件(否则新用户每次启动都会收到一条噪音诊断)。\n if (response.status === 404) return undefined\n\n if (!response.ok) {\n throw new Error(`remote settings read failed: HTTP ${response.status}`)\n }\n\n const body = await response.json()\n return JSON.stringify(body)\n }\n\n /**\n * HTTP PUT `/config/user` 推送过滤后的 syncable 字段。\n * R2: write 前按 SYNCABLE_KEYS 裁剪。\n *\n * **失败传播(RFC-354 P5 修正,2026-08-12)**:本方法此前用 `catch {}` 吞掉一切\n * (且不检查 `response.ok`),promise 永不 reject——导致 `SettingsManager` 里\n * RFC-353 S10 新增的 `.catch(() => emit('remote-sync-failed'))` **在生产路径永远\n * 触发不到**,S10 的修复实际是死代码(单测只因为注入了会 throw 的 mock store 才绿)。\n * 实证:HTTP 500 与网络异常两种情况下 write() 均不 reject。\n *\n * 现在改为向调用方传播失败。**这不改变\"远端不可达不阻塞本地保存\"的 R5 语义**——\n * 该语义由调用方承担(`SettingsManager` 不 await 本调用、只挂 `.catch()` 发事件,\n * 本地写入仍是权威成功信号)。把\"要不要忽略失败\"的决定权交给调用方,而不是在\n * store 内部单方面吞掉,是 S10 可见性要求的前提。\n */\n async write(_path: string, config: Partial<Setting>): Promise<void> {\n const syncable = filterSyncable(config)\n if (Object.keys(syncable).length === 0) return\n\n const auth = await this.getAuth()\n const headers: Record<string, string> = {\n Authorization: `Bearer ${auth.token}`,\n 'Content-Type': 'application/json',\n 'X-Otto-User-Id': this.userId,\n }\n\n const controller = new AbortController()\n const timeout = setTimeout(() => controller.abort(), this.timeoutMs)\n\n let response: Response\n try {\n response = await this.fetchImpl(`${this.baseUrl}/config/user`, {\n method: 'PUT',\n headers,\n body: JSON.stringify(syncable),\n signal: controller.signal,\n })\n } finally {\n clearTimeout(timeout)\n }\n\n // 非 2xx 同样是失败——此前连 response.ok 都不检查,服务端 500 被当成成功。\n if (!response.ok) {\n throw new Error(`remote settings write failed: HTTP ${response.status}`)\n }\n }\n\n async exists(_path: string): Promise<boolean> {\n return false\n }\n}\n","/** Claude Code settings adapter: read .claude/settings.json → otto config. */\nimport { homedir } from 'node:os'\nimport { existsSync, readFileSync } from 'node:fs'\nimport { join, resolve } from 'node:path'\nimport { createLogger, parseJsonc, mapClaudeToolName } from '@x-otto/shared'\nimport { CLAUDE_HOME, isClaudeSettingsCompatEnabled } from '@x-otto/env'\nimport type { Setting, PermissionRuleConfig, PermissionPolicySetting } from './types'\n\nconst logger = createLogger('@x-otto/setting:claude-adapter')\n\ntype Effect = 'allow' | 'deny' | 'ask'\n\n/** 具备文件路径 specifier 语义的 Claude 工具(specifier=glob 路径)。 */\nconst PATH_TOOLS = new Set(['Read', 'Write', 'Edit', 'MultiEdit', 'Glob', 'Grep'])\n\n/** Claude defaultMode → otto permission_mode(非 1:1,保守取收紧侧)。 */\nconst MODE_MAP: Record<string, Setting['permission_mode']> = {\n default: 'confirm',\n acceptEdits: 'auto',\n bypassPermissions: 'bypass',\n plan: 'readonly',\n}\n\n/** 解析 `Tool(specifier)` / 裸 `Tool`。 */\nexport function parseToolSpecifier(spec: string): { tool: string; specifier?: string } {\n const m = /^([A-Za-z][A-Za-z0-9_]*)(?:\\((.*)\\))?$/.exec(spec.trim())\n if (!m) return { tool: spec.trim() }\n return { tool: m[1]!, specifier: m[2] }\n}\n\n/** glob → 锚定正则字符串(`**`→`.*`,`*`→`[^/]*`,`?`→`.`,`~` 展开 home)。 */\nexport function globToRegexSource(glob: string): string {\n const expanded = glob.startsWith('~/') ? join(homedir(), glob.slice(2)) : glob\n let re = ''\n for (let i = 0; i < expanded.length; i++) {\n const c = expanded[i]!\n if (c === '*') {\n if (expanded[i + 1] === '*') {\n re += '.*'\n i++\n } else re += '[^/]*'\n } else if (c === '?') re += '.'\n else re += c.replace(/[.+^${}()|[\\]\\\\]/g, '\\\\$&')\n }\n return `^${re}$`\n}\n\ninterface TranslateResult {\n rules: PermissionRuleConfig[]\n webFetchHosts: string[]\n warnings: string[]\n}\n\nfunction translateSpecifier(spec: string, effect: Effect, out: TranslateResult, idx: number): void {\n const { tool, specifier } = parseToolSpecifier(spec)\n const ottoTool = mapClaudeToolName(tool)\n if (!ottoTool) {\n out.warnings.push(`未知工具 \"${tool}\"(${spec})→ fail-closed 丢弃`)\n return\n }\n\n if (tool === 'WebFetch' && specifier?.startsWith('domain:')) {\n const host = specifier.slice('domain:'.length).trim()\n if (effect === 'allow' && host) {\n out.webFetchHosts.push(host)\n return\n }\n // deny 降 ask 会放宽语义(ask 比 deny 宽);按 effect fail-closed 分流:deny→工具级 deny,其余→ask。\n const downgraded: Effect = effect === 'deny' ? 'deny' : 'ask'\n out.rules.push({ name: `claude-${idx}`, effect: downgraded, tools: [ottoTool] })\n out.warnings.push(\n `WebFetch(${specifier}) 的 ${effect} 不可按域表达 → ${downgraded === 'deny' ? '降工具级 deny(fail-closed)' : '降 ask'}`,\n )\n return\n }\n\n if (specifier === undefined || specifier === '') {\n out.rules.push({ name: `claude-${idx}`, effect, tools: [ottoTool] })\n return\n }\n\n if (PATH_TOOLS.has(tool)) {\n out.rules.push({\n name: `claude-${idx}`,\n effect,\n tools: [ottoTool],\n paths: [globToRegexSource(specifier)],\n })\n return\n }\n\n out.rules.push({\n name: `claude-${idx}`,\n effect: 'ask',\n tools: [ottoTool],\n ask_prompt: `Claude 规则 \"${spec}\" 含命令参数限定,otto 无法精确表达,已降为询问。`,\n })\n out.warnings.push(`\"${spec}\" 含参数 specifier 不可表达 → fail-closed 降 ask`)\n}\n\n/** 纯翻译:Claude settings 对象 → otto Partial<Setting>。 */\nexport function translateClaudeSettings(raw: unknown): {\n setting: Partial<Setting>\n warnings: string[]\n} {\n const result: TranslateResult = { rules: [], webFetchHosts: [], warnings: [] }\n const perms = (raw as { permissions?: Record<string, unknown> })?.permissions\n let idx = 0\n\n for (const effect of ['allow', 'deny', 'ask'] as Effect[]) {\n const list = perms?.[effect]\n if (!Array.isArray(list)) continue\n for (const spec of list) {\n if (typeof spec === 'string') translateSpecifier(spec, effect, result, idx++)\n }\n }\n\n const setting: Partial<Setting> = {}\n if (result.rules.length > 0) {\n const policy: PermissionPolicySetting = { name: 'claude-compat', rules: result.rules }\n setting.permissions = [policy]\n }\n if (result.webFetchHosts.length > 0) {\n setting.web_fetch_allowed_hosts = [...new Set(result.webFetchHosts)]\n }\n const mode = typeof perms?.['defaultMode'] === 'string' ? perms['defaultMode'] : undefined\n if (mode && MODE_MAP[mode]) {\n setting.permission_mode = MODE_MAP[mode]\n if (mode === 'plan') result.warnings.push('defaultMode=plan otto 无对应 → 降 readonly')\n }\n\n return { setting, warnings: result.warnings }\n}\n\n/**\n * 加载并翻译 `.claude` settings 层(最低优先级,供 SettingsManager 合并)。\n * 顺序(低→高合并):`~/.claude/settings.json` < 项目 `.claude/settings.json` < `.claude/settings.local.json`。\n * 默认关;启用时打印导入清单(条目数 + 降级 warning)供核对。返回 {} 当关闭/无文件。\n */\nexport function loadClaudeSettingsLayer(workspaceDir?: string): Partial<Setting> {\n if (!isClaudeSettingsCompatEnabled()) return {}\n\n const files = [\n join(CLAUDE_HOME, 'settings.json'),\n ...(workspaceDir\n ? [\n join(resolve(workspaceDir), '.claude', 'settings.json'),\n join(resolve(workspaceDir), '.claude', 'settings.local.json'),\n ]\n : []),\n ]\n\n // 深合并:permission 规则跨层累加(浅 spread 会让后层整体覆盖前层、丢前层 deny 规则 → 违 fail-closed)。\n // 评估器 deny 优先且名仅作标签,故累加顺序无关、重名无害;mode 取末层(高优先级)。\n const allRules: PermissionRuleConfig[] = []\n const allHosts: string[] = []\n let permissionMode: Setting['permission_mode'] | undefined\n const allWarnings: string[] = []\n for (const file of files) {\n if (!existsSync(file)) continue\n let raw: unknown\n try {\n raw = parseJsonc(readFileSync(file, 'utf-8'))\n } catch (err) {\n logger.warn({ file, err }, 'Failed to parse .claude settings, skipped')\n continue\n }\n const { setting, warnings } = translateClaudeSettings(raw)\n allWarnings.push(...warnings)\n const rules = setting.permissions?.[0]?.rules\n if (rules) allRules.push(...rules)\n if (setting.web_fetch_allowed_hosts) allHosts.push(...setting.web_fetch_allowed_hosts)\n if (setting.permission_mode) permissionMode = setting.permission_mode\n }\n\n const merged: Partial<Setting> = {}\n if (allRules.length > 0) {\n merged.permissions = [{ name: 'claude-compat', rules: allRules }]\n }\n if (allHosts.length > 0) {\n merged.web_fetch_allowed_hosts = [...new Set(allHosts)]\n }\n if (permissionMode) merged.permission_mode = permissionMode\n const ruleCount = allRules.length\n\n if (ruleCount > 0 || allWarnings.length > 0) {\n logger.info(\n {\n ruleCount,\n hosts: merged.web_fetch_allowed_hosts?.length ?? 0,\n mode: merged.permission_mode,\n },\n 'Imported .claude/settings.json (RFC-032 D4)',\n )\n for (const w of allWarnings) logger.warn({ rule: w }, '.claude settings 翻译降级')\n }\n return merged\n}\n","import { OTTO_CONFIG_FILENAME, OTTO_HOME, OTTO_PROJECT_CONFIG_RELPATH, isShadowModeEnabled } from '@x-otto/env'\nimport { TypedEventEmitter, parseJsonc } from '@x-otto/shared'\nimport { resolve } from 'node:path'\n\nimport { loadSetting } from './setting'\nimport { loadClaudeSettingsLayer } from './claude-settings-adapter'\nimport { RawFileSettingStore } from './raw-file-store'\n\nimport type { Setting } from './types'\nimport type { SettingStore } from './store'\n\nexport interface SettingsOptions {\n workspaceDir?: string\n projectConfigPath?: string\n overrides?: Partial<Setting>\n /** 配置读取后端(M14-06)。缺省原文件 store(.otto/config.json 字面路径读取)。 */\n store?: SettingStore\n /**\n * RFC-052 M71d:远端 user config store。\n * 有 identity 时拉取用户偏好基线(如 model/theme/language)。\n * 优先级:project config > remote config > claude compat layer。\n */\n remoteStore?: SettingStore\n /**\n * 项目配置是否受信任。缺省 `true`(保持既有行为,零破坏)。\n * 传 `false`(或返回 false 的 thunk)时对项目层(最高优先级文件层)剔除高危键\n * (permission_mode/mcp_servers/…,见 project-trust.ts)——用于克隆来的未信任仓库。\n * **thunk 形态每次 reload 重新求值**:`/compat trust` 写入白名单后 `settings.load()` 即时生效。\n * 信任判定/持久化在 coding 层(claude-trust)。\n */\n projectConfigTrusted?: boolean | (() => boolean)\n}\n\ninterface SettingsEvents {\n change: (config: Setting) => void\n /**\n * RFC-353 S10:远端配置同步状态——persist()/reload() 的远端读写失败此前被\n * `.catch(() => {})`/`catch {}` 静默吞掉,本地已保存但远端陈旧无从诊断。\n * 消费方(如 CLI 状态栏/doctor)可订阅本事件在远端失败时提示用户。\n * 不改变 persist() 返回类型(30+ 现有调用点零改动),失败信息走事件旁路。\n *\n * 契约:订阅方 handler 不得抛异常——`TypedEventEmitter.emit` 不捕获 listener 异常,\n * 若 handler 抛出会向上传播穿透 reload()/persist() 的 catch 块(与既有 'change' 事件\n * 同规则,非本事件特有)。\n */\n 'remote-sync-failed': (payload: { op: 'read' | 'write'; error: unknown }) => void\n}\n\nexport class SettingsManager extends TypedEventEmitter<SettingsEvents> {\n private readonly paths: string[]\n private readonly workspaceDir?: string\n /**\n * 项目信任判定:boolean 或 thunk。thunk 每次 reload 重新求值——故 `/compat trust`\n * 写入信任白名单后调 `settings.load()` 即可即时解除剔除,无需重启。缺省 true=全信任。\n */\n private readonly projectConfigTrusted: boolean | (() => boolean)\n private overrides: Partial<Setting>\n\n private store: SettingStore | undefined\n private remoteStore: SettingStore | undefined\n private setting: Setting = {} as Setting\n /**\n * persist 串行化链。`persist` 的读-改-写非原子(read→merge→write 之间有 await),\n * 并发调用会交错丢写无关键(如插件 setup 回调 fire-and-forget 撞用户 /model 改动)。\n * 所有 persist 临界区排队经此链顺序执行;单次失败不毒化后续(catch 吞错续链)。\n */\n private writeChain: Promise<unknown> = Promise.resolve()\n\n constructor(options: SettingsOptions = {}) {\n super()\n this.paths = buildSettingPaths(options)\n this.workspaceDir = options.workspaceDir\n this.projectConfigTrusted = options.projectConfigTrusted ?? true\n this.overrides = { ...options.overrides }\n this.store = options.store ?? new RawFileSettingStore()\n this.remoteStore = options.remoteStore\n }\n\n get<K extends keyof Setting>(key: K): Setting[K] {\n return this.setting[key]\n }\n\n /**\n * 整对象只读快照——供需要**整个 Setting 序列化/差异比对**的场景(配置导出、迁移 diff\n * 计算,见 config-migration.ts/migrate.ts)。单字段读取一律走 `get(key)`(类型安全 +\n * 全仓统一入口),不要新增 `.config.xxx` 调用点。\n */\n get config(): Readonly<Setting> {\n return this.setting\n }\n\n get model(): Setting['model'] {\n return this.setting.model\n }\n\n async load(): Promise<Setting> {\n return this.reload(this.overrides)\n }\n\n async reload(overrides?: Partial<Setting>): Promise<Setting> {\n let remoteLayer: Partial<Setting> | undefined\n if (this.remoteStore) {\n try {\n const raw = await this.remoteStore.read('/config/user')\n if (raw !== undefined) {\n remoteLayer = parseJsonc<Partial<Setting>>(raw)\n }\n } catch (error) {\n // RFC-353 S10:此前静默吞错(catch {})——本地设置仍能加载(remoteLayer 保持\n // undefined,回退本地/claude-compat 层),但远端读失败对调用方完全不可见。\n // 发事件而非吞错:不改变 reload() 契约(仍返回本地可用的 Setting),\n // 但订阅方(如 doctor/状态栏)现在能诊断\"远端配置读取失败\"。\n this.emit('remote-sync-failed', { op: 'read', error })\n }\n }\n\n const setting = await loadSetting({\n store: this.store,\n paths: this.paths,\n untrustedProjectPath: this.resolveUntrustedProjectPath(),\n extraLayers: [\n loadClaudeSettingsLayer(this.workspaceDir),\n ...(remoteLayer ? [remoteLayer] : []),\n ],\n })\n\n this.setting = {\n ...setting,\n ...overrides,\n }\n\n this.emit('change', this.setting)\n\n return this.setting\n }\n\n async update(overrides: Partial<Setting>): Promise<Setting> {\n this.overrides = {\n ...this.overrides,\n ...overrides,\n }\n\n return this.reload(this.overrides)\n }\n\n /**\n * 持久化到磁盘配置文件(读-改-写默认落项目层 path),再 reload 生效。\n * 全局做底为只读 underlay(见 buildSettingPaths),默认写回落在最后一个 path(项目级),\n * 不会污染全局 `${OTTO_HOME}/config.json`。\n * `target`(可选):'global' 显式落 `paths[0]`(全局层),'project' 显式落项目层\n * (与缺省行为一致)。缺省 undefined 保持原有\"恒落项目层\"语义,向后兼容旧调用点。\n * 无对应层 path 时(如 target='global' 但会话无项目上下文导致 paths 为空)静默退化为\n * 内存 update,不抛错。\n * 读取磁盘原文 → 顶层合并 partial(保留 mcp_servers 等其余键)→ 写回 →\n * reload 触发 'change'(下游 permissionSync 等据此重同步)。无 store/path 时退化为内存 update。\n * 注:写回为格式化 JSON,会丢失原文件注释。\n */\n async persist(partial: Partial<Setting>, target?: 'global' | 'project'): Promise<Setting> {\n // RFC-345 §D3:影子态 persist 退化为内存 update——读继承磁盘(reload 的 underlay),\n // 写只进 this.overrides(当前会话生效、退出即失),config.json 逐字节不变。既有 else\n // 分支本就是纯内存 update,此处复用同一语义(§D1 白名单点:SettingsManager 唯一写盘出口)。\n if (isShadowModeEnabled()) {\n return this.update(partial)\n }\n const path = target === 'global' ? this.paths[0] : this.paths[this.paths.length - 1]\n const store = this.store\n if (store && path) {\n // 串行化整个读-改-写-reload 临界区,杜绝并发交错丢写(见 writeChain 注释)。\n return this.enqueueWrite(async () => {\n const raw = await store.read(path)\n let current: Partial<Setting> = {}\n if (raw) {\n try {\n current = parseJsonc<Partial<Setting>>(raw)\n } catch {\n current = {}\n }\n }\n await store.write(path, { ...current, ...partial })\n\n if (this.remoteStore) {\n // RFC-353 S10:此前 `.catch(() => {})` 静默吞掉远端写失败——本地已保存\n // acknowledged,远端悄悄陈旧,调用方与用户都无从诊断。改为发事件旁路\n // (不改变 persist() 返回类型/awaitability,本地写入仍是权威成功信号)。\n this.remoteStore.write('/config/user', { ...current, ...partial }).catch((error: unknown) => {\n this.emit('remote-sync-failed', { op: 'write', error })\n })\n }\n\n return this.reload(this.overrides)\n })\n }\n return this.update(partial)\n }\n\n /**\n * 把一个写临界区追加到 `writeChain` 末尾顺序执行。前一个无论成败都接着跑\n * (`then(fn, fn)`),且把链尾的错误吞掉,避免一次失败毒化整条队列。\n */\n private enqueueWrite<T>(fn: () => Promise<T>): Promise<T> {\n const run = this.writeChain.then(fn, fn)\n this.writeChain = run.then(\n () => undefined,\n () => undefined,\n )\n return run\n }\n\n /**\n * 项目层(paths 末项)未受信任时的路径——loader 据此剔除高危键;undefined = 全信任。\n * 每次 reload 调用:thunk 形态的信任判定即时反映 `/compat trust` 写入。\n * 全局做底层(paths[0]) 用户自有,始终信任、永不进此剔除。\n */\n private resolveUntrustedProjectPath(): string | undefined {\n if (this.paths.length === 0) return undefined\n const trusted =\n typeof this.projectConfigTrusted === 'function'\n ? this.projectConfigTrusted()\n : this.projectConfigTrusted\n return trusted ? undefined : this.paths[this.paths.length - 1]\n }\n\n dispose(): void {\n this.removeAllListeners()\n }\n}\n\n/**\n * 配置层路径(数组序即合并优先级,低→高;见 setting.ts `merge`)。\n *\n * 全局 `${OTTO_HOME}/config.json` 做底(低优先),项目 `.otto/config.json` 覆盖(高优先)。\n * 这样标量(model/theme…)项目级胜出,而对象表(如 `mcp_servers`)跨层深合并——\n * 用户在全局配的 server 与项目 server 自动并表,无需每个项目手抄(对齐业界参考实现的\n * User 层/userSettings 做底设计)。同路径去重(在 OTTO_HOME 内运行或 `--config` 指向全局文件时)。\n *\n * 无项目上下文(既无 workspaceDir 又无 projectConfigPath)时保持仅默认/内存语义,返回空。\n */\nfunction buildSettingPaths(options: SettingsOptions): string[] {\n let projectPath: string | undefined\n if (options.projectConfigPath) {\n projectPath = options.projectConfigPath\n } else if (options.workspaceDir) {\n projectPath = resolve(options.workspaceDir, OTTO_PROJECT_CONFIG_RELPATH)\n }\n\n if (!projectPath) {\n return []\n }\n\n const globalPath = resolve(OTTO_HOME, OTTO_CONFIG_FILENAME)\n if (resolve(projectPath) === globalPath) {\n return [projectPath]\n }\n return [globalPath, projectPath]\n}\n","import type { AgentProfile } from '@x-otto/prompt'\n\n/**\n * 预制 agent persona 已移除(2026-06-13,agent-profiles-removal 方案)。\n *\n * 子代理改为 fork/inherit + 自由任务规约(对齐 Codex spawn / Claude Code fork):\n * 命名未命中声明式 .md 时继承父级工具/模型 + 共享 lead-guidance,不再挑固定 persona。\n * 用户定制走声明式 .md(.otto/agents/*.md)——唯一显式定制路径。\n */\nexport const BUILTIN_AGENT_PROFILES: AgentProfile[] = []\n","/** Config merge strategy: layered config merge (default < project < user < override). */\nimport { HIGH_RISK_CAPABILITIES } from '@x-otto/plugin'\nimport { SETTING_KEYS } from './schema'\nimport { HIGH_RISK_PROJECT_KEYS } from './project-trust'\nimport type { Setting, SettingKey, InstalledPluginEntry } from './schema'\n\n/** 合并策略类别(RFC-219 milestone M2 §设计闸门要点订正后的接口)。 */\nexport type MergeCategory = 'scalar-override' | 'array-merge' | 'record-merge'\n\n/** 单个字段的静态分类(与具体 diff 值无关)。 */\nexport interface FieldClassification {\n category: MergeCategory\n /**\n * 恒定路由 user 级配置存储,不受目标 scope 参数影响(RFC-219 重要事项规则 5)。\n * 仅供 M3/M4 的导入落盘目标判定使用,本模块自身不做任何写入。\n */\n deviceBound: boolean\n}\n\n/**\n * 排除字段——不导出、不导入,`diffConfig` 返回结果中永不出现(RFC-219 字段策略表)。\n * `config_version`/`version` 是元数据字段(导入侧沿用当前 otto 版本自身值,不采用导入\n * 文件里的值);`last_update_check`/`_migrations` 是内部记账字段。\n */\nexport const EXCLUDED_KEYS: ReadonlySet<SettingKey> = new Set([\n 'config_version',\n 'version',\n 'last_update_check',\n '_migrations',\n])\n\n/**\n * 字段静态分类表(RFC-219 §设计·字段策略表,M2 实现期订正版:合并策略与设备绑定分离为\n * 两个独立字段)。`EXCLUDED_KEYS` 里的字段不在此表出现(完整性校验单独处理,见\n * `assertFieldClassificationCoversAllKeys`)。\n */\nexport const FIELD_CLASSIFICATION: ReadonlyMap<SettingKey, FieldClassification> = new Map<\n SettingKey,\n FieldClassification\n>([\n // 标量偏好字段——导入意图即采用,非设备绑定。\n ['model', { category: 'scalar-override', deviceBound: false }],\n ['language', { category: 'scalar-override', deviceBound: false }],\n ['nickname', { category: 'scalar-override', deviceBound: false }],\n ['has_completed_onboarding', { category: 'scalar-override', deviceBound: false }],\n ['has_completed_wizard', { category: 'scalar-override', deviceBound: false }],\n ['wizard_dont_show_again', { category: 'scalar-override', deviceBound: false }],\n ['first_prompt_submitted_at', { category: 'scalar-override', deviceBound: false }],\n ['thinking_level', { category: 'scalar-override', deviceBound: false }],\n ['tool_preset', { category: 'scalar-override', deviceBound: false }],\n ['log_level', { category: 'scalar-override', deviceBound: false }],\n ['feedback_repo', { category: 'scalar-override', deviceBound: false }],\n ['permission_mode', { category: 'scalar-override', deviceBound: false }],\n ['prompt_output_token_budget', { category: 'scalar-override', deviceBound: false }],\n ['prompt_wall_clock_budget_ms', { category: 'scalar-override', deviceBound: false }],\n ['session_cost_budget_usd', { category: 'scalar-override', deviceBound: false }],\n\n // 会话行为调优参数——整体对象替换(非深合并),无安全含义。\n ['memory_auto_extract', { category: 'scalar-override', deviceBound: false }],\n // RFC-323 residency 预算(并发会话新增 schema 但漏归类,本分类策略全量门禁故\n // 需补齐——终局 review 暴露的 pre-existing 回归,与 behavior 同类)。\n ['residency', { category: 'scalar-override', deviceBound: false }],\n ['skill_loop', { category: 'scalar-override', deviceBound: false }],\n ['history', { category: 'scalar-override', deviceBound: false }],\n ['workflow', { category: 'scalar-override', deviceBound: false }],\n ['todo_continue_max', { category: 'scalar-override', deviceBound: false }],\n ['stall_detection', { category: 'scalar-override', deviceBound: false }],\n ['sandbox', { category: 'scalar-override', deviceBound: false }],\n // RFC-230:统一重试/超时/退避配置——整体对象替换(同 workflow),非设备绑定、无安全含义。\n ['resilience', { category: 'scalar-override', deviceBound: false }],\n\n // 数组并集合并字段——天然无损合并,非设备绑定。\n ['disabled_plugins', { category: 'array-merge', deviceBound: false }],\n ['model_fallback', { category: 'array-merge', deviceBound: false }],\n ['web_fetch_allowed_hosts', { category: 'array-merge', deviceBound: false }],\n ['recent_mentions', { category: 'array-merge', deviceBound: false }],\n // RFC-233:@ 快捷方式(用户自定义文本映射),同 recent_mentions 语义并集合并天然无损。\n ['shortcuts', { category: 'array-merge', deviceBound: false }],\n ['disabled_agents', { category: 'array-merge', deviceBound: false }],\n ['disabled_tools', { category: 'array-merge', deviceBound: false }],\n ['disabled_skills', { category: 'array-merge', deviceBound: false }],\n ['disabled_hooks', { category: 'array-merge', deviceBound: false }],\n ['permissions', { category: 'array-merge', deviceBound: false }],\n ['permission_always_allow', { category: 'array-merge', deviceBound: false }],\n\n // Record 按 key 合并字段——非设备绑定。\n ['agents', { category: 'record-merge', deviceBound: false }],\n ['categories', { category: 'record-merge', deviceBound: false }],\n ['model_slots', { category: 'record-merge', deviceBound: false }],\n ['mcp_servers', { category: 'record-merge', deviceBound: false }],\n\n // 设备绑定字段(RFC-219 重要事项规则 5)——恒定路由 user 级配置,不导出到 project。\n ['recent_models', { category: 'array-merge', deviceBound: true }],\n ['keybinding_overrides', { category: 'record-merge', deviceBound: true }],\n ['installed_plugins', { category: 'array-merge', deviceBound: true }],\n ['plugin_lifecycle', { category: 'scalar-override', deviceBound: true }],\n ['auto_update_check', { category: 'scalar-override', deviceBound: true }],\n ['theme', { category: 'scalar-override', deviceBound: true }],\n // RFC-277 M4:受限主题 override(目前仅 markdown typography)——视觉偏好,同 theme 语义\n // device-bound,不导出到 project、不跨设备同步(RFC-277 D9 settings merge/sync contract)。\n ['theme_overrides', { category: 'scalar-override', deviceBound: true }],\n // RFC-277 D2/M6:preset selection 双槽——同 theme/theme_overrides 语义,device-bound。\n ['theme_preset', { category: 'scalar-override', deviceBound: true }],\n ['proxy', { category: 'scalar-override', deviceBound: true }],\n ['notification', { category: 'scalar-override', deviceBound: true }],\n ['pulse_survey', { category: 'scalar-override', deviceBound: true }],\n])\n\n/**\n * 断言字段分类表覆盖全部 `SETTING_KEYS`(含 excluded 字段)。新增 schema 字段忘记归类\n * 时抛错——供单测调用,防止静默漏判(RFC-219 §风险与验证策略)。\n */\nexport function assertFieldClassificationCoversAllKeys(): void {\n const uncovered = SETTING_KEYS.filter(\n (key) => !EXCLUDED_KEYS.has(key) && !FIELD_CLASSIFICATION.has(key),\n )\n if (uncovered.length > 0) {\n throw new Error(\n `ConfigMergeStrategy 字段分类表遗漏以下 SettingKey(需在 FIELD_CLASSIFICATION 或 ` +\n `EXCLUDED_KEYS 中归类): ${uncovered.join(', ')}`,\n )\n }\n}\n\n/** 高风险配置字段——放宽方向变更触发 D4 安全门(复用 `HIGH_RISK_PROJECT_KEYS`,RFC-219 规则 8b)。 */\nconst SECURITY_SENSITIVE_KEYS: ReadonlySet<string> = new Set(HIGH_RISK_PROJECT_KEYS)\n\n/** 单字段差异。`risk` 是按本次具体 from/to 值计算的实例级结果,非字段静态属性。 */\nexport interface FieldDiff {\n key: SettingKey\n from: unknown\n to: unknown\n category: MergeCategory\n deviceBound: boolean\n risk: 'safe' | 'sensitive'\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\nfunction shallowEqual(a: unknown, b: unknown): boolean {\n if (a === b) return true\n if (Array.isArray(a) && Array.isArray(b)) {\n return a.length === b.length && a.every((v, i) => JSON.stringify(v) === JSON.stringify(b[i]))\n }\n return JSON.stringify(a) === JSON.stringify(b)\n}\n\n/**\n * `PermissionMode` 严格度排名(`@x-otto/hook-contracts` `PERMISSION_MODES` 真实词表:\n * `['bypass', 'auto', 'confirm', 'strict', 'readonly']`,非本模块设计——数值越大越严格)。\n * `bypass`(零确认)最宽松,`readonly`(只读)最严格。\n */\nconst PERMISSION_MODE_STRICTNESS: Record<string, number> = {\n bypass: 0,\n auto: 1,\n confirm: 2,\n strict: 3,\n readonly: 4,\n}\n\n/**\n * 判定字段是否为\"放宽方向\"变更(RFC-219 D4:只有放宽才触发安全门,收紧不触发)。\n * `permission_mode`:按 `PERMISSION_MODE_STRICTNESS` 排名比较,`to` 严格度低于 `from`\n * 视为放宽(未知值保守按放宽处理,宁可多问不可漏问)。\n * `sandbox`:`enabled: true → false` 视为放宽。\n * 其余 `HIGH_RISK_PROJECT_KEYS` 字段(mcp_servers/disabled_hooks/permissions/\n * permission_always_allow):新增/变更内容本身即视为敏感(不存在方向性判断,见\n * `classifyRisk` 对应分支)。\n */\nfunction isPermissionModeRelaxation(from: unknown, to: unknown): boolean {\n if (typeof to !== 'string' || from === to) return false\n const fromRank = typeof from === 'string' ? PERMISSION_MODE_STRICTNESS[from] : undefined\n const toRank = PERMISSION_MODE_STRICTNESS[to]\n // 未知值(非 PERMISSION_MODES 词表内)保守按放宽处理。\n if (fromRank === undefined || toRank === undefined) return true\n return toRank < fromRank\n}\n\nfunction isSandboxRelaxation(from: unknown, to: unknown): boolean {\n const fromEnabled = isPlainObject(from) ? from['enabled'] : undefined\n const toEnabled = isPlainObject(to) ? to['enabled'] : undefined\n return fromEnabled === true && toEnabled === false\n}\n\n/** 数组新增元素集合(`to` 中存在但 `from` 中不存在的项,按 JSON 序列化比较)。 */\nfunction addedArrayItems(from: unknown, to: unknown): unknown[] {\n const fromArr = Array.isArray(from) ? from : []\n const toArr = Array.isArray(to) ? to : []\n const fromSet = new Set(fromArr.map((v) => JSON.stringify(v)))\n return toArr.filter((v) => !fromSet.has(JSON.stringify(v)))\n}\n\n/** 按字段 key + from/to 具体值计算本次差异的安全风险等级(RFC-219 D4/规则 8a/8b)。 */\nfunction classifyRisk(key: SettingKey, from: unknown, to: unknown): 'safe' | 'sensitive' {\n if (!SECURITY_SENSITIVE_KEYS.has(key)) return 'safe'\n\n if (key === 'permission_mode') {\n return isPermissionModeRelaxation(from, to) ? 'sensitive' : 'safe'\n }\n if (key === 'sandbox') {\n return isSandboxRelaxation(from, to) ? 'sensitive' : 'safe'\n }\n if (key === 'mcp_servers') {\n // 新增/变更条目(record-merge:新 key 或同 key 值变化)均视为敏感(RFC-219 规则 8a)。\n const fromRec = isPlainObject(from) ? from : {}\n const toRec = isPlainObject(to) ? to : {}\n for (const [k, v] of Object.entries(toRec)) {\n if (!(k in fromRec) || !shallowEqual(fromRec[k], v)) return 'sensitive'\n }\n return 'safe'\n }\n if (key === 'disabled_hooks') {\n // 可禁用安全钩子——变化本身即敏感(不分方向,缺失钩子和新增钩子都可能改变安全态势)。\n return shallowEqual(from, to) ? 'safe' : 'sensitive'\n }\n if (key === 'permissions' || key === 'permission_always_allow') {\n // 仅新增预授权条目时敏感;纯移除/缩减不触发(RFC-219 规则 8b)。\n return addedArrayItems(from, to).length > 0 ? 'sensitive' : 'safe'\n }\n // 对抗审核 F2 修复(fail-closed):SECURITY_SENSITIVE_KEYS(HIGH_RISK_PROJECT_KEYS)\n // 六个成员已在上方逐一显式处理——若未来 `HIGH_RISK_PROJECT_KEYS` 新增第七个成员而本\n // 函数忘记同步处理,不能静默 fail-open 落到 'safe'(等于安全门对新增高危字段失效)。\n // 显式抛错逼停调用方,而非放行一个未经判断的字段。\n throw new Error(\n `classifyRisk: 字段 \"${key}\" 在 HIGH_RISK_PROJECT_KEYS 中但未被显式处理——` +\n `新增高危字段必须同步补充判定分支,不允许 fail-open`,\n )\n}\n\n/**\n * 计算当前配置与导入配置之间的差异(RFC-219 M2 核心函数)。\n *\n * 排除字段(`EXCLUDED_KEYS`)与值未变化的字段均不出现在返回数组中——只输出真实差异。\n * `installed_plugins` 字段不在此函数处理(插件差异走独立的 `classifyPluginChanges`,\n * 因为它需要\"新增/本地独有/版本不一致\"三分类而非简单的 from/to 覆盖,见下)。\n */\nexport function diffConfig(current: Setting, incoming: Partial<Setting>): FieldDiff[] {\n const diffs: FieldDiff[] = []\n for (const key of SETTING_KEYS) {\n if (EXCLUDED_KEYS.has(key)) continue\n if (key === 'installed_plugins') continue // 走 classifyPluginChanges\n if (!(key in incoming)) continue\n\n const from = current[key]\n const to = incoming[key]\n if (shallowEqual(from, to)) continue\n\n const classification = FIELD_CLASSIFICATION.get(key)\n if (!classification) continue // 理论上不可达(assertFieldClassificationCoversAllKeys 已钉死)\n\n diffs.push({\n key,\n from,\n to,\n category: classification.category,\n deviceBound: classification.deviceBound,\n risk: classifyRisk(key, from, to),\n })\n }\n return diffs\n}\n\n/** 插件清单差异分类(RFC-219 §设计·字段策略表 `installed_plugins` 行)。 */\nexport interface PluginDiff {\n /** 导入清单中有、本地未安装——需自动安装。 */\n toInstall: InstalledPluginEntry[]\n /** 本地已安装、导入清单中无——不受影响,不卸载。 */\n localOnly: InstalledPluginEntry[]\n /** 两边都有但版本不同。 */\n versionMismatch: Array<{ id: string; local: string; incoming: string }>\n}\n\n/**\n * 比对本地与导入的 `installed_plugins` 清单,产出三分类差异。\n *\n * 不做任何写入/安装动作——纯计算,供 M3/M4 的执行层驱动实际安装。\n */\nexport function classifyPluginChanges(\n current: readonly InstalledPluginEntry[],\n incoming: readonly InstalledPluginEntry[],\n): PluginDiff {\n const currentById = new Map(current.map((e) => [e.id, e]))\n const incomingById = new Map(incoming.map((e) => [e.id, e]))\n\n const toInstall: InstalledPluginEntry[] = []\n const versionMismatch: PluginDiff['versionMismatch'] = []\n for (const entry of incoming) {\n const local = currentById.get(entry.id)\n if (!local) {\n toInstall.push(entry)\n continue\n }\n if (entry.version !== undefined && local.version !== undefined && entry.version !== local.version) {\n versionMismatch.push({ id: entry.id, local: local.version, incoming: entry.version })\n }\n }\n\n const localOnly = current.filter((e) => !incomingById.has(e.id))\n\n return { toInstall, localOnly, versionMismatch }\n}\n\n/**\n * 判定本次导入是否包含安全敏感变更(RFC-219 D4 安全门触发条件)。\n *\n * 触发条件(任一即触发):\n * - `diffs` 中存在 `risk === 'sensitive'` 的字段差异。\n * - `pluginDiff.toInstall` 中存在声明 `HIGH_RISK_CAPABILITIES` 的新装插件。\n *\n * `capabilities` 快照字段(RFC-219 D1c)由安装路径调用 `readHighRiskCapabilities()`\n * 计算写入——该函数内部已按 `HIGH_RISK_CAPABILITIES` 过滤,理论上快照字段是其真子集。\n * 但本函数**不依赖这条隐式前提**(对抗审核 F1:不应仅凭\"上游已过滤\"就当作不变量)——\n * 显式 `import { HIGH_RISK_CAPABILITIES } from '@x-otto/plugin'` 权威常量重新交叉核对,\n * 即使未来 `capabilities` 快照字段的写入路径变化(如导出文件被手工篡改塞入非高危\n * 能力字符串),本函数依然只对真正命中 `HIGH_RISK_CAPABILITIES` 的能力判定为敏感。\n */\nexport function hasSecuritySensitiveChanges(diffs: readonly FieldDiff[], pluginDiff: PluginDiff): boolean {\n if (diffs.some((d) => d.risk === 'sensitive')) return true\n return pluginDiff.toInstall.some((p) =>\n (p.capabilities ?? []).some((c) => (HIGH_RISK_CAPABILITIES as readonly string[]).includes(c)),\n )\n}\n\n/**\n * 按接受策略把差异应用为可写入配置的 partial 对象。\n *\n * `'all'`:全部差异按各自 category 合并策略应用。\n * `'safe-only'`:跳过 `risk === 'sensitive'` 的字段(用户在安全门选择\"跳过安全项\"时使用)。\n *\n * 合并语义(`diff.from`/`diff.to` 已由 `diffConfig` 计算好,无需重新传入 current 配置):\n * - `scalar-override`:直接取 `to` 值整体替换。\n * - `array-merge`:`from` 与 `to` 数组按 JSON 序列化去重并集。\n * - `record-merge`:`from` 与 `to` 对象按 key 浅合并(同 key 以 `to` 值覆盖)。\n *\n * 实现期简化(milestone 设计稿原签名含 `current: Setting` 参数):`diff.from` 已是\n * `current[diff.key]`(由 `diffConfig` 计算),重复传入 `current` 是冗余参数,故本函数\n * 签名去掉该参数,只需 `diffs` + `accept` 两个入参即可完整还原合并结果。\n */\nexport function applyMergeStrategy(\n diffs: readonly FieldDiff[],\n accept: 'all' | 'safe-only',\n): Partial<Setting> {\n const result: Partial<Record<SettingKey, unknown>> = {}\n for (const diff of diffs) {\n if (accept === 'safe-only' && diff.risk === 'sensitive') continue\n\n if (diff.category === 'array-merge') {\n const fromArr = Array.isArray(diff.from) ? diff.from : []\n const toArr = Array.isArray(diff.to) ? diff.to : []\n const seen = new Set(fromArr.map((v) => JSON.stringify(v)))\n const merged = [...fromArr]\n for (const item of toArr) {\n const serialized = JSON.stringify(item)\n if (!seen.has(serialized)) {\n seen.add(serialized)\n merged.push(item)\n }\n }\n result[diff.key] = merged\n } else if (diff.category === 'record-merge') {\n const fromRec = isPlainObject(diff.from) ? diff.from : {}\n const toRec = isPlainObject(diff.to) ? diff.to : {}\n result[diff.key] = { ...fromRec, ...toRec }\n } else {\n result[diff.key] = diff.to\n }\n }\n return result as Partial<Setting>\n}\n","/**\n * config-export-format.ts —— 配置导出/导入的可移植文件格式(RFC-219 M3)。\n *\n * 纯函数、零 I/O——不碰文件系统。真正的文件读写在 CLI 层\n * (`packages/cli/src/commands/config-migration.ts`)。本模块只负责:\n * ① 从当前配置构建导出产物(应用字段白名单,剔除设备绑定的绝对路径);\n * ② 解析/校验导入文件的结构与版本。\n */\nimport { isAbsolute } from 'node:path'\nimport { EXCLUDED_KEYS } from './config-merge-strategy'\nimport type { Setting, SettingKey, InstalledPluginEntry } from './schema'\n\n/** 导出文件格式版本。结构变化时递增,`parseExportFile` 按此版本做兼容性判断。 */\nexport const CURRENT_EXPORT_VERSION = 1\n\nexport interface ConfigExportFile {\n otto_config_version: number\n exported_at: string\n /** 导出机器标识(`os.hostname()`),仅供人工审阅来源,不参与任何逻辑判断。 */\n exported_from: string\n /** 导出侧的配置 scope('global' = 用户级 `~/.otto/config.json`,'project' = 项目级)。 */\n scope: 'global' | 'project'\n fields: Partial<Setting>\n}\n\n/**\n * 从当前配置构建导出产物(RFC-219 重要事项规则 9)。\n *\n * 应用规则:\n * - `EXCLUDED_KEYS`(`config_version`/`version`/`last_update_check`/`_migrations`)不导出。\n * - `installed_plugins[].sourceSpec` 若为绝对路径,标准化为空字符串(导入侧走 npm/\n * workspace glob 解析重新定位,见 `resolvePackageDir` 既有机制)——防止把导出机器的\n * 本地路径写入可分享产物。\n * - `undefined` 值的字段不写入(保持导出产物精简,不必要地标注\"这个字段不存在\")。\n */\nexport function buildExportPayload(\n config: Setting,\n scope: 'global' | 'project',\n hostname: string,\n now: () => string = () => new Date().toISOString(),\n): ConfigExportFile {\n const fields: Partial<Record<SettingKey, unknown>> = {}\n for (const [key, value] of Object.entries(config) as Array<[SettingKey, unknown]>) {\n if (EXCLUDED_KEYS.has(key)) continue\n if (value === undefined) continue\n\n if (key === 'installed_plugins') {\n const entries = value as InstalledPluginEntry[]\n fields[key] = entries.map((entry) =>\n entry.sourceSpec && isAbsolute(entry.sourceSpec) ? { ...entry, sourceSpec: '' } : entry,\n )\n continue\n }\n fields[key] = value\n }\n\n return {\n otto_config_version: CURRENT_EXPORT_VERSION,\n exported_at: now(),\n exported_from: hostname,\n scope,\n fields: fields as Partial<Setting>,\n }\n}\n\nexport class ExportFileVersionError extends Error {\n constructor(\n public readonly fileVersion: number,\n public readonly supportedVersion: number,\n ) {\n super(\n `导入文件 otto_config_version=${fileVersion},当前 otto 支持=${supportedVersion}。` +\n `请升级 otto 到最新版本后再试。`,\n )\n this.name = 'ExportFileVersionError'\n }\n}\n\nexport class ExportFileParseError extends Error {\n constructor(reason: string) {\n super(`导入文件格式非法:${reason}`)\n this.name = 'ExportFileParseError'\n }\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\n/**\n * 解析并校验导入文件结构。版本不兼容或结构非法时抛出对应错误类型(调用方据此渲染\n * 不同的提示文案,见 RFC-219 §重要事项规则 3 附近的错误处理设计)。\n */\nexport function parseExportFile(raw: string): ConfigExportFile {\n let parsed: unknown\n try {\n parsed = JSON.parse(raw)\n } catch (err) {\n throw new ExportFileParseError(err instanceof Error ? err.message : String(err))\n }\n if (!isPlainObject(parsed)) {\n throw new ExportFileParseError('顶层结构必须是 JSON 对象')\n }\n const { otto_config_version, exported_at, exported_from, scope, fields } = parsed\n if (typeof otto_config_version !== 'number') {\n throw new ExportFileParseError('缺少或非法的 otto_config_version 字段')\n }\n if (otto_config_version !== CURRENT_EXPORT_VERSION) {\n throw new ExportFileVersionError(otto_config_version, CURRENT_EXPORT_VERSION)\n }\n if (typeof exported_at !== 'string' || typeof exported_from !== 'string') {\n throw new ExportFileParseError('缺少或非法的 exported_at/exported_from 字段')\n }\n if (scope !== 'global' && scope !== 'project') {\n throw new ExportFileParseError('scope 字段必须是 \"global\" 或 \"project\"')\n }\n if (!isPlainObject(fields)) {\n throw new ExportFileParseError('fields 字段必须是 JSON 对象')\n }\n return {\n otto_config_version,\n exported_at,\n exported_from,\n scope,\n fields: fields as Partial<Setting>,\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["logger","isPlainObject","logger","isPlainObject","isPlainObject"],"sources":["../src/constants.ts","../src/schema.ts","../src/types.ts","../src/migrator.ts","../src/project-trust.ts","../src/config-origins.ts","../src/setting.ts","../src/raw-file-store.ts","../src/remote-sync-keys.ts","../src/remote-setting-store.ts","../src/claude-settings-adapter.ts","../src/settings-manager.ts","../src/presets.ts","../src/config-merge-strategy.ts","../src/config-redact.ts","../src/config-export-format.ts"],"sourcesContent":["export const TOOL_PRESETS = ['minimal', 'standard', 'full'] as const\nexport type ToolPreset = (typeof TOOL_PRESETS)[number]\n\nexport const LOG_LEVELS = ['trace', 'debug', 'info', 'warn', 'error', 'fatal'] as const\nexport type LogLevel = (typeof LOG_LEVELS)[number]\n\n// 权限模式词表单一真源在 @x-otto/hook-contracts(HK-1,setting/hooks 都下依赖到它);此处 re-export 保 API 不变。\nexport { PERMISSION_MODES } from '@x-otto/hook-contracts'\nexport type { PermissionMode } from '@x-otto/hook-contracts'\n","import { z } from 'zod'\nimport { MODEL_SLOTS } from '@x-otto/orchestration-contracts'\nimport type { PermissionRuleConfig, PermissionPolicySetting } from '@x-otto/hook-contracts'\nimport type { ResilienceConfigOverride } from '@x-otto/env'\nimport { LOG_LEVELS, PERMISSION_MODES, TOOL_PRESETS } from './constants'\n\nconst lenient = <T extends z.ZodType>(schema: T) => schema.optional().catch(undefined)\n\nconst workflowSlotSchema = z.enum(MODEL_SLOTS)\n\nconst agentOptionsSchema = z.object({\n model: lenient(z.string()),\n description: lenient(z.string()),\n system_prompt: lenient(z.string()),\n tools: lenient(z.array(z.string())),\n capabilities: lenient(z.array(z.string())),\n categories: lenient(z.array(z.string())),\n default_workflow_slot: lenient(workflowSlotSchema),\n max_tool_turns: lenient(z.number()),\n max_tool_turn_extensions: lenient(z.number()),\n /** RFC-104 D3:per-prompt output-token 预算(agent 级覆盖)。 */\n prompt_output_token_budget: lenient(z.number()),\n /** RFC-104 D3:per-prompt 墙钟预算(ms,agent 级覆盖)。 */\n prompt_wall_clock_budget_ms: lenient(z.number()),\n /** RFC-204 D1/D2:会话累计成本软预算(美元,agent 级覆盖)。 */\n session_cost_budget_usd: lenient(z.number()),\n /** RFC-204 D4:早期停滞检测配置(agent 级覆盖);false=关闭,对象=自定义窗口/阈值。 */\n stall_detection: lenient(\n z.union([z.literal(false), z.object({ window_turns: lenient(z.number()), repeat_threshold: lenient(z.number()) })]),\n ),\n temperature: lenient(z.number()),\n max_tokens: lenient(z.number()),\n disabled: lenient(z.boolean()),\n})\n\nconst categoryConfigSchema = z.object({\n preferred_models: lenient(z.array(z.string())),\n default_model: lenient(z.string()),\n})\n\n/** 会话结束后自动抽取跨会话经验(operational-learning)。默认关,灰度后再放开。 */\nconst memoryAutoExtractConfigSchema = z.object({\n enabled: lenient(z.boolean()),\n every_n_turns: lenient(z.number()),\n max_lessons: lenient(z.number()),\n similarity_threshold: lenient(z.number()),\n /**\n * RFC-327 M3-05(T-327-FOLLOW-02):相关召回低水位阈值——既有 lessons 的最大\n * relevantHitCount 低于此水位时跳过 auto-extract(防 write-only 噪音积累)。\n * 设置后未注入读取端口时 fail-closed(不抽取);缺省不设 = 无门向后兼容。\n */\n min_relevant_hits: lenient(z.number()),\n})\n\n/**\n * RFC-318 技能回路:观测重复任务流程并提议固化为 skill。默认关(D8:信噪比未验证前不默认开)。\n * `min_messages` 是触发一次抽取所需的最小新消息数(对齐 memory_auto_extract 的省调用口径)。\n */\nconst skillLoopConfigSchema = z.object({\n enabled: lenient(z.boolean()),\n min_messages: lenient(z.number()),\n /** 拒绝一个提案后,多少天内不再就同一个候选打扰(默认 14)。 */\n dismiss_cooldown_days: lenient(z.number()),\n})\n\n/**\n * RFC-383 D4:高频 skill 内化决策器配置。默认关(R4 灰度哲学,对齐 RFC-318 D8 / auto-extract)。\n * `window` 无独立字段——SkillUsageStore 是进程内聚合、跨重启清零,判定窗口缺省 = 进程\n * 生命周期内累计(§9 修订 5 文档明示),无跨会话持久窗口可配。\n */\nconst skillInternalizationConfigSchema = z.object({\n enabled: lenient(z.boolean()),\n /** 高频阈值:执行次数下限(进程内累计)。 */\n min_executions: lenient(z.number()),\n /** 成功阈值:successes/executions 下限。 */\n min_success_rate: lenient(z.number()),\n /** 每轮最多内化数(按 executions 降序取 top-N)。 */\n max_internalized: lenient(z.number()),\n})\n\nconst modelSlotConfigSchema = z.object({\n slots: lenient(z.partialRecord(workflowSlotSchema, z.union([z.string(), z.array(z.string())]))),\n default: lenient(z.string()),\n /**\n * 委派子会话(subagent)在**未指定 slot/category** 时默认套用的 slot。设为 'compact'\n * 即「未分类委派也默认走便宜档」。仅作用 subagent,主会话不受影响;不设=保持落 default。\n */\n subagent_default_slot: lenient(workflowSlotSchema),\n})\n\nconst notificationConfigSchema = z.object({\n enabled: lenient(z.boolean()),\n sound: lenient(z.boolean()),\n on_completion: lenient(z.boolean()),\n on_error: lenient(z.boolean()),\n /** @deprecated OQ1:on_completion 的别名,运行时 OR 合并。 */\n on_idle: lenient(z.boolean()),\n channel: lenient(\n z.enum(['auto', 'terminal_bell', 'iterm2', 'iterm2_with_bell', 'kitty', 'ghostty', 'disabled']),\n ),\n condition: lenient(z.enum(['unfocused', 'always'])),\n idle_threshold_ms: lenient(z.number()),\n command: lenient(z.array(z.string())),\n})\n\n/**\n * RFC-208 §2.5:满意度脉冲调查触发/开关配置。`sink` 当前仅 `'local'` 合法(预留后续\n * 扩展点,非法值 fallback 到 'local' 并 warn;RFC-209 M2 迁移后见\n * extensions/plugin-feedback/src/pulse-survey-sink.ts 的 resolvePulseSurveySink)。\n */\nconst pulseSurveyConfigSchema = z.object({\n /** 总开关,缺省 true。 */\n enabled: lenient(z.boolean()),\n /** 每次 prompt.end 后的抽样概率(0-1),缺省 0.05。 */\n probability: lenient(z.number()),\n /** 两次弹出之间的最小回合数间隔,缺省 20。 */\n min_turn_gap: lenient(z.number()),\n /** 数据落地 sink 类型,当前仅 'local' 合法。 */\n sink: lenient(z.string()),\n})\n\n/** RFC-129:外部 shell 历史源(默认关闭 opt-in)。 */\nconst externalHistorySourceSchema = z.object({\n id: lenient(z.string()),\n path: lenient(z.string()),\n enabled: lenient(z.boolean()),\n})\n\nconst externalHistoryConfigSchema = z.object({\n enabled: lenient(z.boolean()),\n maxEntries: lenient(z.number()),\n sources: lenient(z.array(externalHistorySourceSchema)),\n})\n\nconst historyConfigSchema = z.object({\n external: lenient(externalHistoryConfigSchema),\n})\n\nconst sandboxConfigSchema = z.object({\n enabled: z.boolean(),\n network: lenient(z.enum(['allow', 'deny'])),\n writablePaths: lenient(z.array(z.string())),\n /**\n * 凭证读隔离(opt-in,默认 false):开启后沙箱拒绝读取 ~/.ssh、~/.aws、~/.gnupg 及\n * otto auth store 目录,堵住凭证经命令读出外泄(含变量间接绕过 command 正则的情形)。\n * 代价:开启后沙箱内 git-over-ssh / aws-cli / gpg 签名会因读不到密钥而失败——故默认关。\n */\n protectCredentials: lenient(z.boolean()),\n /**\n * bash 沙箱越界自动放行(opt-in,默认 false,参照 Claude Code 的\n * `sandbox.autoAllowBashIfSandboxed`,默认关闭以维持 otto 现有\"先问后跑\"安全姿态不变)。\n *\n * 开启后:仅当权限层(含 destructive-guard 破坏性命令探测)已判定该 bash 命令为\n * `allow`(未命中任何安全护栏 ask/deny)时,sandbox-guard 才把纯路径越界的 `ask`\n * 降级为自动放行——交给沙箱本身在执行期强制边界,不再为已判定安全的命令弹审批\n * (对齐 codex `render_decision_for_unmatched_command` 的 \"let the sandbox enforce\n * restrictions without a user prompt\")。危险命令(destructive-guard 已 ask/deny)\n * 与 strict/readonly 模式不受影响,仍走原有审批/拒绝路径。\n */\n autoAllowBashIfSandboxed: lenient(z.boolean()),\n})\n\n/**\n * RFC-323:会话驻留预算配置——用户可通过 `.otto/config.json`(otto 原生配置文件,\n * 非 Claude 的 settings.json)调整,也可经环境变量覆盖(env 优先级 > config.json > 默认值,\n * 与既有常量模式一致)。\n *\n * 所有字段可选;缺省时回退到 `@x-otto/session-contract` 的默认值。\n */\nconst residencyConfigSchema = z.object({\n /** 进程级总预算(字节)。默认 1024MB(≈1GB)。CJK 下真实驻留 ≈ ×2.05。 */\n total_bytes: lenient(z.number()),\n /** 单会话预算下界(字节)。默认 16MB。 */\n min_per_session_bytes: lenient(z.number()),\n /** 单会话预算上界(字节)。默认 256MB。受压缩输入窗口约束。 */\n max_per_session_bytes: lenient(z.number()),\n /** 会话历史条数上限(结构兜底)。默认 50000。 */\n max_history_messages: lenient(z.number()),\n /** resume 尾窗口装载条数。默认 5000(与 max_history_messages 解耦)。 */\n resume_window_messages: lenient(z.number()),\n /** 单会话字节预算(G4 回退默认值,分配器缺席时使用)。默认 64MB。 */\n max_content_bytes: lenient(z.number()),\n /** RFC-324 D2:MemoryGovernor warning 阈值(usedPct 百分比)。\n * 真实堆使用率达此值时,ResidencyGovernor 升至 warning,估算层预算÷2 收紧。\n * 默认 70。env OTTO_MEMORY_WARN_PCT 优先。 */\n warn_threshold_pct: lenient(z.number()),\n /** RFC-324 D2:MemoryGovernor critical 阈值(usedPct 百分比)。\n * 真实堆使用率达此值时,ResidencyGovernor 升至 critical,估算层预算÷4 收紧。\n * 默认 85。env OTTO_MEMORY_HARD_PCT 优先。 */\n hard_threshold_pct: lenient(z.number()),\n /** RFC-324 D1:turn-gated 压缩超时(毫秒)。超时时降级为 fire-and-forget(不永久阻塞 turn)。默认 30000。 */\n compaction_timeout_ms: lenient(z.number()),\n /** RFC-324 D3:摘要质量 warning 阈值。低于此值时调大下次压缩批次上限。默认 0.7。 */\n coverage_warn_threshold: lenient(z.number()),\n /** RFC-324 D3:摘要质量 error 阈值。低于此值时双重调大批次上限(更激进保护)。默认 0.5。 */\n coverage_error_threshold: lenient(z.number()),\n /** RFC-340 D1:摘要质量**硬下限**。低于此值时在摘要正文前置\"低保真\"标注,让精度\n * 损失对模型可见(模型据此会向用户确认缺失细节,而非把不完整摘要当作全部事实)。\n * 与 warn/error 的区别:那两档只调节下次批次策略(对模型不可见),本档改变发给模型\n * 的摘要文本本身。**不阻断压缩**——低覆盖退化为不压缩会级联到 lossy 裸裁剪,严格更差\n * (RFC-340 §D1-v1)。不变式 floor ≤ error ≤ warn(接线层钳制)。默认 0.3。 */\n coverage_floor_threshold: lenient(z.number()),\n // 注:`archive_after_days` 曾在此声明,但**自动归档调度从未实现**(全仓无任何\n // 调度入口),用户配置它无任何效果且无警告——属承诺不存在的功能,故移除\n // (RFC-330 §4.5 记录的静默失效面)。手动归档(archiveSession)不依赖该配置。\n // 自动冷归档需独立 RFC 定义保留期/恢复/失败策略,届时再随实现一并加回。\n})\n\nconst toolsConfigSchema = z.object({\n tool_preset: lenient(z.enum(TOOL_PRESETS)),\n disabled_tools: lenient(z.array(z.string())),\n})\n\nconst workflowReviewSchema = z.object({ enabled: lenient(z.boolean()) })\nconst workflowRetrySchema = z.object({\n enabled: lenient(z.boolean()),\n maxAttempts: lenient(z.number()),\n})\nconst workflowCircuitBreakerSchema = z.object({\n enabled: lenient(z.boolean()),\n failureThreshold: lenient(z.number()),\n timeoutMs: lenient(z.number()),\n})\nconst workflowRoutingSchema = z.object({ enabled: lenient(z.boolean()) })\n\nconst workflowOptionsSchema = z.object({\n enabled: lenient(z.boolean()),\n review: lenient(workflowReviewSchema),\n retry: lenient(workflowRetrySchema),\n circuitBreaker: lenient(workflowCircuitBreakerSchema),\n routing: lenient(workflowRoutingSchema),\n})\n\nconst permissionRuleConfigSchema = z.object({\n name: z.string(),\n effect: z.enum(['allow', 'deny', 'ask']),\n tools: lenient(z.array(z.string())),\n paths: lenient(z.array(z.string())),\n deny_reason: lenient(z.string()),\n ask_prompt: lenient(z.string()),\n}) satisfies z.ZodType<PermissionRuleConfig>\n\nconst permissionPolicySettingSchema = z.object({\n name: z.string(),\n priority: lenient(z.number()),\n enabled: lenient(z.boolean()),\n scope: lenient(\n z.object({\n agents: lenient(z.array(z.string())),\n sessions: lenient(z.array(z.string())),\n }),\n ),\n rules: z.array(permissionRuleConfigSchema),\n}) satisfies z.ZodType<PermissionPolicySetting>\n\nconst proxyConfigSchema = z.object({\n /** 代理地址(http:// 或 https:// 协议)。 */\n url: lenient(z.string()),\n /** 是否启用代理(false=保留配置但停用)。 */\n enabled: lenient(z.boolean()),\n})\n\nconst mcpServerSettingSchema = z.object({\n command: lenient(z.string()),\n args: lenient(z.array(z.string())),\n env: lenient(z.record(z.string(), z.string())),\n url: lenient(z.string()),\n type: lenient(z.enum(['stdio', 'sse', 'http'])),\n headers: lenient(z.record(z.string(), z.string())),\n requestTimeoutMs: lenient(z.number()),\n autoReconnect: lenient(z.boolean()),\n disabled: lenient(z.boolean()),\n})\n\n/**\n * RFC-230:统一重试/超时/退避配置——契约单源在 `@x-otto/env`(`ResilienceConfig`),本 schema\n * 只是配置载体(`.otto/config.json` 的 `resilience` 字段),经 `satisfies z.ZodType<...>` 钉死\n * 与 `@x-otto/env` 契约同构(drift → 编译报错)。\n */\nconst resilienceStreamSchema = z.object({\n maxRetries: lenient(z.number().int().positive()),\n initialDelayMs: lenient(z.number().positive()),\n backoffFactor: lenient(z.number().positive()),\n maxDelayMs: lenient(z.number().positive()),\n})\nconst resilienceNetworkDisconnectSchema = z.object({\n maxRetries: lenient(z.number().int().positive()),\n intervalMs: lenient(z.number().positive()),\n})\nconst resilienceMcpSchema = z.object({\n maxReconnectAttempts: lenient(z.number().int().nonnegative()),\n reconnectDelayMs: lenient(z.number().positive()),\n requestTimeoutMs: lenient(z.number().positive()),\n})\nconst resilienceScheduleSchema = z.object({\n retryBackoffMs: lenient(z.number().nonnegative()),\n})\nconst resilienceErrorRetrySchema = z.object({\n maxAttempts: lenient(z.number().int().positive()),\n intervalMs: lenient(z.number().positive()),\n})\nconst resilienceOauthSchema = z.object({\n // 二轮评审 P1:正数无下限会导致 Math.floor(timeoutMs/3) 心跳间隔可为 0/1,\n // setInterval(fn, 0) 造成忙循环风险(见 D6 OAuth 小节心跳派生逻辑)。\n // 下限 1000ms 保证心跳间隔 >= 333ms,足够远离忙循环风险,同时仍比默认 15000ms 宽松得多,\n // 覆盖任何合理的高级调优场景。与 @x-otto/env 的 OAUTH_REFRESH_LOCK_TIMEOUT_MIN_MS 同值。\n refreshLockTimeoutMs: lenient(z.number().min(1000)),\n})\n\nconst resilienceConfigSchema = z.object({\n stream: lenient(resilienceStreamSchema),\n networkDisconnect: lenient(resilienceNetworkDisconnectSchema),\n mcp: lenient(resilienceMcpSchema),\n schedule: lenient(resilienceScheduleSchema),\n errorRetry: lenient(resilienceErrorRetrySchema),\n oauth: lenient(resilienceOauthSchema),\n}) satisfies z.ZodType<ResilienceConfigOverride>\n\n/**\n * RFC-036 @ 面板「最近使用」条目快照(MRU,最多 5 条)——`/model` `recent_models` 同款\n * 持久化模式的推广:不止模型,@ 面板选中的任意 sigil 条目(文件/agent/skill/mcp/…)\n * 都计入。只持久化渲染所需的最小字段(label/value/kind/description),不持久化\n * `preview`(@file 首屏预览,体积大且易过期,SigilPane 消费 recent 条目时按需重新拉取)。\n */\n/**\n * 已安装插件的权威清单条目(RFC-219 D1)。**逻辑记录**,与文件系统 symlink(物化形态)\n * 分离——安装/卸载时写入/移除,不与磁盘状态做自动双向纠正(不一致时告警引导用户修复,\n * 见 discoverPlugins 消费方)。\n */\nconst installedPluginEntrySchema = z.object({\n id: z.string(),\n source: z.enum(['npm', 'dir', 'url', 'git']),\n /**\n * 来源标识:npm 包名 / 目录路径(导出时若为绝对路径需标准化为空,RFC-219 重要事项\n * 规则 9,防止把导出机器的本地路径写入可分享产物)/ URL。\n */\n sourceSpec: lenient(z.string()),\n installedAt: z.number(),\n version: lenient(z.string()),\n /**\n * 安装目标级别,与 `@x-otto/install` 既有 `InstallScope`('global'|'project'|'repository')\n * 词表对齐,不另创新词表。`installed_plugins` 本身作为清单**恒定存储在 user 级配置\n * 文件**(RFC-219 重要事项规则 5)——此字段记录的是\"安装时的目标目录级别\"语义,\n * 供审计/展示用途,与清单的存储位置是两个独立维度。\n */\n scope: z.enum(['global', 'project', 'repository']),\n /**\n * 安装时声明的高危能力快照(`@x-otto/plugin` `readHighRiskCapabilities()` 输出,RFC-219\n * M2 补充)。安装路径(CLI/TUI)在安装前已读取过这份数据用于信任确认弹窗,此处只是\n * 复用同一份已计算结果持久化,不引入新的文件系统访问。存在的意义:M2\n * `ConfigMergeStrategy.classifyPluginChanges` 需要判断\"导入清单中的新装插件是否声明\n * 高危能力\"以触发 D4 安全门,若不快照该信息,M2 要么读盘(破坏纯函数边界),要么无法\n * 判断——快照字段是让 M2 保持零 I/O 纯函数的必要设计。\n */\n capabilities: lenient(z.array(z.string())),\n})\n\nconst recentMentionEntrySchema = z.object({\n label: z.string(),\n value: z.string(),\n // 与 @x-otto/interchange SigilKind 词表同步(RFC-210 D2 增 'resource'、RFC-233 增\n // 'shortcut'、RFC-360 增 'effort'——不同步则含新 kind 的最近使用条目被 lenient\n // fail-soft 静默丢弃,MRU 丢条目)。\n kind: z.enum(['hashtag', 'file', 'person', 'agent', 'skill', 'mcp', 'plugin', 'model', 'resource', 'shortcut', 'effort']),\n description: lenient(z.string()),\n})\n\n/** RFC-233:用户自定义文本快捷方式条目(`[@shortcut:name]` chip 展开为 `text`)。 */\nconst shortcutEntrySchema = z.object({\n name: z.string().min(1).max(64),\n text: z.string().min(1).max(4000),\n})\n\n/**\n * RFC-277 D3:Markdown typography 语义角色枚举——只允许引用 `ThemeColors` 的 key,\n * 禁止任意 hex(终局约束,不重走 RFC-205 已移除的任意覆盖老路)。这份枚举与\n * `packages/tui/src/theme/colors.ts` 的 `ThemeColors` 接口字段手动同步;两者漂移由\n * `packages/setting/tests/schema.test.ts` 的镜像清单断言钉死(改一处忘改另一处会测试红)。\n */\nconst THEME_COLOR_ROLE_VALUES = [\n 'brand', 'brandShimmer', 'accent', 'accentDim', 'accentBright', 'text', 'inactive',\n 'secondary', 'subtle', 'replyPrefix', 'success', 'error', 'warning', 'special',\n 'suggestion', 'permission', 'codeText', 'diffAdd', 'diffRemove', 'diffAddBg',\n 'diffRemoveBg', 'panelBorder', 'overlayBg', 'tagBg', 'toolText', 'focusBorder', 'selection',\n] as const\nconst themeColorRoleSchema = z.enum(THEME_COLOR_ROLE_VALUES)\n\n/**\n * RFC-277 D3:`MarkdownThemeOverrides` 的 settings 镜像——严格 schema,字段集合与\n * `packages/tui/src/theme/theme-registry.ts` 的 `MarkdownThemeOverrides` 接口手动同步\n * (镜像漂移由跨包类型测试钉死,见 schema.test.ts)。`listMarkers` 需四个非空字符串\n * (单宽校验在渲染层 `resolveMarkdownTypographyTheme` 做,这里只保证类型形状)。\n */\nconst markdownThemeOverridesSchema = z.strictObject({\n heading1: lenient(themeColorRoleSchema),\n heading2: lenient(themeColorRoleSchema),\n headingWeak: lenient(themeColorRoleSchema),\n listMarker: lenient(themeColorRoleSchema),\n listMarkerMuted: lenient(themeColorRoleSchema),\n quoteBar: lenient(themeColorRoleSchema),\n quoteText: lenient(themeColorRoleSchema),\n link: lenient(themeColorRoleSchema),\n inlineCode: lenient(themeColorRoleSchema),\n codeFence: lenient(themeColorRoleSchema),\n tableHeader: lenient(themeColorRoleSchema),\n tableDivider: lenient(themeColorRoleSchema),\n listIndent: lenient(z.union([z.literal(2), z.literal(3)])),\n listMarkers: lenient(z.tuple([\n z.string().min(1), z.string().min(1), z.string().min(1), z.string().min(1),\n ])),\n codeBlockDivider: lenient(z.enum(['hr', 'none'])),\n})\n\n/**\n * RFC-277 D3/D4:顶层主题 override 容器——`.strict()` 只放行 `markdown` 一个字段。\n * 未来若要支持其他排版角色(table/quote 之外的组件),必须显式加字段,不允许开放\n * 任意 key(保持\"受限覆盖面\"约束,防止悄悄扩大成任意配置面)。\n */\nconst themeOverridesSchema = z.strictObject({\n markdown: lenient(markdownThemeOverridesSchema),\n})\n\n/** RFC-277 D2/M6:preset selection 双槽 schema——见 SettingSchema.theme_preset 字段注释。 */\nconst themePresetSelectionSchema = z.strictObject({\n dark: lenient(z.string().min(1).max(128)),\n light: lenient(z.string().min(1).max(128)),\n})\n\nexport const SettingSchema = z.object({\n config_version: lenient(z.string()),\n version: lenient(z.string()),\n log_level: lenient(z.enum(LOG_LEVELS)),\n model: lenient(z.string()),\n model_fallback: lenient(z.array(z.string())),\n /** MRU 最近在前,最多 5 条;持久化真源。 */\n recent_models: lenient(z.array(z.string())),\n /**\n * 最近 @ 到的资源列表(MRU,最多 5 条,最近在前);SigilPane「最近使用」分组的\n * 持久化真源。项目级字段(落 `.otto/config.json`,不随全局 config 走),随目录走——\n * 契合「按项目记住常用 @ 对象」的心智(不同项目常用的文件/agent/skill 天然不同)。\n */\n recent_mentions: lenient(z.array(recentMentionEntrySchema)),\n /**\n * RFC-233:用户自定义 `[@shortcut:name]` 文本快捷方式(name → text 映射)。\n * `/shortcuts` 面板增删改;project 级字段(落 `.otto/config.json` 项目层,同\n * `recent_mentions` 语义——不同仓库常用的指令模板天然不同,不跨机器/账号同步)。\n */\n shortcuts: lenient(z.array(shortcutEntrySchema)),\n /**\n * RFC-207 §2.2/§3.4:`/feedback` 指令提交 issue 的目标仓库(`owner/repo` 格式)。\n * 未配置时 handler 尝试从 `git remote get-url origin` 解析(见 feedback.ts\n * `resolveFeedbackRepo`),仍解析失败则提示用户配置本字段。\n */\n feedback_repo: lenient(z.string()),\n /**\n * RFC-174 D2:用户快捷键覆盖表。key 为 commandId(跨全局命令表与控件级表统一命名空间),\n * value 为覆盖后的完整按键谓词——单条记录内部整体替换非深合并(改了 ctrl 字段后全量\n * 覆盖该 commandId 的谓词对象,不是只替换 ctrl 而保留其余默认字段)。\n * 空对象 `{}` 视为\"未分配\"态(全部字段缺省,`matchKeyBinding`/`matchControlBinding` 恒不匹配)。\n */\n keybinding_overrides: lenient(z.record(z.string(), z.object({\n ctrl: z.boolean().optional(),\n shift: z.boolean().optional(),\n meta: z.boolean().optional(),\n input: z.string().optional(),\n key: z.enum(['tab', 'escape', 'return']).optional(),\n }))),\n language: lenient(z.string()),\n /** TUI 主题模式(RFC-263 M5)。'system' 启动时经 OSC 11 探测终端背景明暗。device-bound,不跨设备同步。 */\n theme: lenient(z.enum(['dark', 'light', 'system'])),\n /**\n * RFC-277 M4:受限主题 override——目前只开放 Markdown typography 语义角色\n * (见 `markdownThemeOverridesSchema`)。**严格 schema**(`.strict()`):任何未声明字段\n * (尤其 RFC-205 移除的 `accent`/`colors`/`theme_spacing`/`theme_border`/`theme_figures`\n * 等旧任意覆盖字段)一律被 zod 拒绝整层,交由上层 `validate()` 的 `.catch(undefined)`\n * 静默丢弃并产出 warning——不允许任意 hex/spacing 复活。device-bound,不跨设备同步。\n */\n theme_overrides: lenient(themeOverridesSchema),\n /**\n * RFC-277 D2/M6:按 appearance 分槽的 preset 选择——`dark`/`light` 各自记录用户为该\n * appearance 选中的 preset effective id(builtin 为 `'dark'`/`'light'`;extension 贡献为\n * `${extensionId}/${localId}`,见 `ThemeRegistry`)。**不是**单个 `theme_preset: string`\n * (D2 已否决该形态——system 模式下 dark/light 需要各自记住用户选择)。id 格式在此不做\n * 强校验(registry 层 `resolve()` 对未知/已卸载 id 有 fallback 到 builtin appearance 的\n * 契约,M1 既有行为),本 schema 只保证类型形状。device-bound,不跨设备同步(同 theme)。\n */\n theme_preset: lenient(themePresetSelectionSchema),\n /** 用户昵称(首次 onboarding 设置,非强制;会话/通知中个性化显示)。 */\n nickname: lenient(z.string().max(64)),\n /** 首次 onboarding 是否已完成(防止 language='auto' 时重复弹出)。 */\n has_completed_onboarding: lenient(z.boolean()),\n /** Setup wizard 是否已走完所有五步(Provider → Trust → Login → Model → Start)。 */\n has_completed_wizard: lenient(z.boolean()),\n /** 用户在欢迎页勾选\"不再提示\"后设为 true,跳过向导自动弹出。 */\n wizard_dont_show_again: lenient(z.boolean()),\n /**\n * RFC-326 §6.5 / M4-02:用户首次实际提交 prompt 的时间戳(epoch ms)。\n * 只在真实提交(非 slash command、非草稿填充)后写入一次,此后恒不变——\n * 用于 Home 首 prompt 建议卡\"发过一次后自然消失\"的判据(`FtueStage`\n * `ready` → `first-prompt` 的转移条件,见 `ftue-snapshot.ts` deriveStage)。\n * 缺省 undefined 表示\"从未提交过\",与 §5.1\"持久化只保留用户意图\"的表述\n * (`ftue.firstPromptSubmittedAt?`)语义一致,字段名遵循本仓扁平 snake_case\n * 既有约定(未采用 RFC 原文的点分层级命名)。\n */\n first_prompt_submitted_at: lenient(z.number()),\n /** 推理投入档(reasoning effort):low|medium|high|xhigh|max;/effort 切换并持久化,下一回合生效。 */\n thinking_level: lenient(z.string()),\n agents: lenient(z.record(z.string(), agentOptionsSchema)),\n categories: lenient(z.record(z.string(), categoryConfigSchema)),\n tool_preset: lenient(z.enum(TOOL_PRESETS)),\n memory_auto_extract: lenient(memoryAutoExtractConfigSchema),\n /** RFC-318:技能回路(会话轨迹 → episode → 候选 → 提案)。默认关。 */\n skill_loop: lenient(skillLoopConfigSchema),\n /** RFC-383:高频 skill 内化决策器(skill usage → 选择性 volatile 内化)。默认关。 */\n skill_internalization: lenient(skillInternalizationConfigSchema),\n notification: lenient(notificationConfigSchema),\n /** RFC-208:满意度脉冲调查触发/开关配置。 */\n pulse_survey: lenient(pulseSurveyConfigSchema),\n /** RFC-129:TUI 外部 shell 历史(ghost/Ctrl+R);`external.enabled` 默认 false。 */\n history: lenient(historyConfigSchema),\n workflow: lenient(workflowOptionsSchema),\n model_slots: lenient(modelSlotConfigSchema),\n mcp_servers: lenient(z.record(z.string(), mcpServerSettingSchema)),\n /** RFC-230:统一重试/超时/退避配置——settings 层(优先级:settings > env > 默认值,程序化注入最高)。 */\n resilience: lenient(resilienceConfigSchema),\n sandbox: lenient(sandboxConfigSchema),\n /** RFC-323:会话驻留预算配置。env 优先级 > settings > 默认值。 */\n residency: lenient(residencyConfigSchema),\n disabled_agents: lenient(z.array(z.string())),\n disabled_hooks: lenient(z.array(z.string())),\n disabled_tools: lenient(z.array(z.string())),\n /** 被禁用的 skill 名(/extension 面板切换;SkillRegistry 启动按此过滤、运行时 enable/disable 下一轮生效)。 */\n disabled_skills: lenient(z.array(z.string())),\n /** 被禁用的插件 id(bundle 启停;禁用即其 skill/agent/command/mcp 全部贡献消失,下次发现生效)。 */\n disabled_plugins: lenient(z.array(z.string())),\n /**\n * 已安装插件权威清单(RFC-219 D1)。安装(`installPluginFromPackage`/`installPluginFromDir`)\n * 写入条目,卸载(`@x-otto/install` `uninstall()` 的 plugin 分支)移除条目。与磁盘 symlink\n * 状态不做自动双向纠正,仅供一致性告警(discoverPlugins 消费)。恒定路由 user 级配置\n * 存储(RFC-219 重要事项规则 5),不随 project 级配置导出/导入。\n */\n installed_plugins: lenient(z.array(installedPluginEntrySchema)),\n /** 自动检查更新(启动时异步查 npm registry;仅通知,不自动安装)。 */\n auto_update_check: lenient(z.boolean()),\n /** 上次检查更新的时间戳(ms)。启动时若距上次检查 ≤6h 则跳过。 */\n last_update_check: lenient(z.number()),\n /** RFC-104 D3:per-prompt output-token 预算(顶层缺省;agent 级可覆盖)。 */\n prompt_output_token_budget: lenient(z.number()),\n /** RFC-104 D3:per-prompt 墙钟预算(ms,顶层缺省)。 */\n prompt_wall_clock_budget_ms: lenient(z.number()),\n /** RFC-204 D1/D2:会话累计成本软预算(美元,顶层缺省;agent 级可覆盖)。 */\n session_cost_budget_usd: lenient(z.number()),\n /** RFC-204 D4:早期停滞检测配置(顶层缺省;agent 级可覆盖);false=关闭。 */\n stall_detection: lenient(\n z.union([z.literal(false), z.object({ window_turns: lenient(z.number()), repeat_threshold: lenient(z.number()) })]),\n ),\n /** RFC-104 D4:todo 闸门非生产续跑轮上限(settings 优先于 env OTTO_TODO_CONTINUE_MAX;缺省 3)。 */\n todo_continue_max: lenient(z.number()),\n permission_mode: lenient(z.enum(PERMISSION_MODES)),\n permissions: lenient(z.array(permissionPolicySettingSchema)),\n /** 用户「始终允许」的工具名(R8-B):把该工具的 ask 升级为 allow,绝不覆盖 deny。 */\n permission_always_allow: lenient(z.array(z.string())),\n /**\n * 插件生命周期状态(RFC-080)。\n * `setupDone` 记录已执行过 setup 钩子的插件 id + 版本号(版本变化时重新执行)。\n */\n plugin_lifecycle: lenient(\n z.object({\n setupDone: lenient(\n z.array(\n z.object({\n id: z.string(),\n version: lenient(z.string()),\n }),\n ),\n ),\n }),\n ),\n /**\n * 出网 allowlist(opt-in):web_fetch 等带 url 参数的工具,host 不在此表 → 升 ask。\n * 空表 = 不限制(默认,零影响)。条目支持精确域或子域后缀(example.com 匹配 a.example.com)。\n */\n /** 出站 HTTP 代理配置(RFC-xxx /proxy 指令)。全局层,不读项目 config。 */\n proxy: lenient(proxyConfigSchema),\n web_fetch_allowed_hosts: lenient(z.array(z.string())),\n _migrations: lenient(z.array(z.string())),\n})\n\nexport type Setting = z.infer<typeof SettingSchema>\nexport type SettingFromSchema = Setting\n\nexport type ThemeColorRole = z.infer<typeof themeColorRoleSchema>\nexport type MarkdownThemeOverridesSetting = z.infer<typeof markdownThemeOverridesSchema>\nexport type ThemeOverridesSetting = z.infer<typeof themeOverridesSchema>\nexport type ThemePresetSelectionSetting = z.infer<typeof themePresetSelectionSchema>\n/** 镜像清单单一真源,供 schema.test.ts 与消费方核对不漂移。 */\nexport const THEME_COLOR_ROLES = THEME_COLOR_ROLE_VALUES\n\nexport type RecentMentionEntry = z.infer<typeof recentMentionEntrySchema>\nexport type InstalledPluginEntry = z.infer<typeof installedPluginEntrySchema>\n\nexport type AgentOptions = z.infer<typeof agentOptionsSchema>\nexport type AgentsConfig = z.infer<typeof SettingSchema>['agents']\nexport type CategoryConfig = z.infer<typeof categoryConfigSchema>\nexport type CategoriesConfig = z.infer<typeof SettingSchema>['categories']\nexport type MemoryAutoExtractConfig = z.infer<typeof memoryAutoExtractConfigSchema>\nexport type SkillLoopConfig = z.infer<typeof skillLoopConfigSchema>\nexport type SkillInternalizationConfig = z.infer<typeof skillInternalizationConfigSchema>\nexport type ModelSlotConfig = z.infer<typeof modelSlotConfigSchema>\nexport type NotificationConfig = z.infer<typeof notificationConfigSchema>\nexport type PulseSurveyConfig = z.infer<typeof pulseSurveyConfigSchema>\nexport type ExternalHistoryConfig = z.infer<typeof externalHistoryConfigSchema>\nexport type HistoryConfig = z.infer<typeof historyConfigSchema>\nexport type SandboxConfig = z.infer<typeof sandboxConfigSchema>\nexport type ToolsConfig = z.infer<typeof toolsConfigSchema>\n// M-B gate ③:WorkflowOptions 单源为 @x-otto/orchestration-contracts(retry+circuitBreaker 2 字段)。\n// setting 侧 workflow 字段保留在 zod schema 中(用户可配 review/routing 等预留键),\n// 但其类型导出已摘除——零外部消费方,避免与 orchestration 侧同名异形类型形成双源漂移。\n// 配置类型单一真源在 @x-otto/hook-contracts(HK-1);zod schema 经上方 `satisfies` 钉死到它(drift→编译错)。\nexport type { PermissionRuleConfig, PermissionPolicySetting } from '@x-otto/hook-contracts'\nexport type ProxyConfig = z.infer<typeof proxyConfigSchema>\nexport type McpServerSetting = z.infer<typeof mcpServerSettingSchema>\nexport type McpServersConfig = z.infer<typeof SettingSchema>['mcp_servers']\n\nexport const SETTING_KEYS = SettingSchema.keyof().options\nexport type SettingKey = keyof Setting\n","import type { SettingStore } from './store'\n\nexport { LOG_LEVELS, PERMISSION_MODES, TOOL_PRESETS } from './constants'\nexport type { LogLevel, PermissionMode, ToolPreset } from './constants'\n\nexport { SettingSchema, SETTING_KEYS, THEME_COLOR_ROLES } from './schema'\nexport type {\n Setting,\n SettingFromSchema,\n SettingKey,\n RecentMentionEntry,\n InstalledPluginEntry,\n AgentOptions,\n AgentsConfig,\n CategoryConfig,\n CategoriesConfig,\n ModelSlotConfig,\n NotificationConfig,\n PulseSurveyConfig,\n ExternalHistoryConfig,\n HistoryConfig,\n SandboxConfig,\n ToolsConfig,\n // M-B gate ③: WorkflowOptions 单源为 @x-otto/orchestration-contracts。\n // setting 的 workflow zod schema 仍在(配置载体),类型导出已摘除。\n PermissionRuleConfig,\n PermissionPolicySetting,\n McpServerSetting,\n McpServersConfig,\n ProxyConfig,\n // RFC-277 M4:受限主题 override 类型。\n ThemeColorRole,\n MarkdownThemeOverridesSetting,\n ThemeOverridesSetting,\n // RFC-277 D2/M6:preset selection 双槽类型。\n ThemePresetSelectionSetting,\n} from './schema'\n\nimport type { AgentOptions, Setting } from './schema'\n\nexport const BUILTIN_REVIEWER_AGENTS: Record<string, AgentOptions> = {\n 'spec-reviewer': {\n description: 'Reviews implementation against specification requirements',\n categories: ['review'],\n default_workflow_slot: 'critique',\n },\n 'quality-reviewer': {\n description: 'Reviews code quality, patterns, and best practices',\n categories: ['review'],\n default_workflow_slot: 'critique',\n },\n}\n\nexport interface SettingWarning {\n key: string\n message: string\n line?: number\n column?: number\n}\n\nexport interface LoadSettingOptions {\n store?: SettingStore\n paths?: string[]\n /** RFC-032 D4:最低优先级附加层(如翻译后的 `.claude/settings.json`),merge 在文件层之前。 */\n extraLayers?: Partial<Setting>[]\n /**\n * 未受信任的项目配置文件绝对路径(须与 `paths` 中某项**逐字相等**)。命中即对该层\n * 剔除高危键(permission_mode/mcp_servers/… 见 project-trust.ts)。缺省 = 全信任,不门控。\n */\n untrustedProjectPath?: string\n}\n\nexport const DEFAULT_CONFIG: Setting = {\n config_version: '1.0.0',\n log_level: 'info',\n model: undefined,\n recent_models: [],\n recent_mentions: [],\n shortcuts: [],\n language: 'auto',\n theme: undefined,\n nickname: undefined,\n has_completed_onboarding: undefined,\n has_completed_wizard: undefined,\n wizard_dont_show_again: undefined,\n first_prompt_submitted_at: undefined,\n thinking_level: undefined,\n tool_preset: 'full',\n agents: { ...BUILTIN_REVIEWER_AGENTS },\n categories: {},\n workflow: {},\n disabled_agents: [],\n disabled_hooks: [],\n disabled_tools: [],\n disabled_skills: [],\n disabled_plugins: [],\n installed_plugins: [],\n auto_update_check: true,\n last_update_check: undefined,\n permission_mode: 'auto',\n permissions: [],\n permission_always_allow: [],\n web_fetch_allowed_hosts: [],\n notification: {\n enabled: true,\n sound: false,\n on_completion: true,\n on_error: true,\n channel: 'auto',\n condition: 'unfocused',\n idle_threshold_ms: 60000,\n },\n}\n","import { compare } from 'semver'\nimport { createLogger } from '@x-otto/shared'\n\nconst logger = createLogger('@x-otto/setting:migrator')\n\nexport interface Migration {\n version: string\n description: string\n migrate(config: Record<string, unknown>): Record<string, unknown>\n}\n\nexport function migrate(\n config: Record<string, unknown>,\n migrations: Migration[] = [],\n): {\n config: Record<string, unknown>\n applied: string[]\n} {\n const currentVersion = typeof config.config_version === 'string' ? config.config_version : '0.0.0'\n\n const applied: string[] = []\n let result = { ...config }\n\n for (const migration of migrations) {\n if (compare(migration.version, currentVersion) > 0) {\n result = migration.migrate(result)\n result.config_version = migration.version\n applied.push(`${currentVersion} → ${migration.version}`)\n\n logger.info(\n { from: currentVersion, to: migration.version },\n `Applying migration: ${migration.description}`,\n )\n }\n }\n\n if (applied.length > 0) {\n const existingMigrations = Array.isArray(result._migrations)\n ? (result._migrations as string[])\n : []\n\n result._migrations = [...existingMigrations, ...applied]\n }\n\n return { config: result, applied }\n}\n","import type { Setting } from './schema'\n\n/**\n * project-trust.ts —— 项目级 `.otto/config.json` 高危键信任门(纯函数,无 IO).\n *\n * 威胁:克隆任意仓库时,项目 `.otto/config.json` 是攻击者可控输入。其中若干键能在\n * **加载即生效**地提权或执行任意代码——例如 `permission_mode: \"bypass\"` 自动批准所有工具、\n * `mcp_servers` 启动即 spawn 任意二进制、`permissions`/`permission_always_allow` 预授权\n * Bash 等。otto 既有信任门只覆盖 `.claude/` 与 `.mcp.json`(见 coding `claude-trust.ts`),\n * 而 otto 原生 `.otto/config.json` 的这些键此前**无条件全信任**加载——本模块补齐该缺口。\n *\n * 对照业界 CLI 实现 `sanitize_project_config`(project 层 strip 掉 credential/provider/command 类键)\n * 与业界终端 Agent 实现(`.mcp.json` 默认 pending、trust-敏感开关排除 projectSettings)。otto 取\"未信任则\n * strip 高危键 + 告警,配置其余部分照常加载\"——安全降级而非拒绝启动。\n *\n * 纯层:只定义\"哪些键高危\"与\"如何剔除\",不做信任判定/持久化/IO(那是 coding 层职责)。\n */\n\n/**\n * 项目层未受信任时须剔除的高危键。每个都能\"加载即提权/执行\":\n * - `permission_mode` —— 'bypass'/'auto' 自动批准工具 → 克隆即 RCE。\n * - `permissions` —— 预声明 allow 规则(如 Bash(*))→ 预授权执行。\n * - `permission_always_allow` —— 预填会话级永久放行 → 同上。\n * - `mcp_servers` —— stdio server 启动即 spawn `command`+`args` 任意二进制。\n * - `disabled_hooks` —— 可关掉 permission-guard / sandbox-guard 等安全 hook。\n * - `sandbox` —— 可弱化/关闭沙箱(逃逸)。\n *\n * 刻意不含的键:`model`/`model_slots`(otto provider 在代码注册、非 config 定义 base_url,\n * 重定向风险低于业界 CLI 实现的 `model_providers`)、`disabled_tools`(减能力非提权)、`agents`\n * (prompt+工具限制、加载不执行,与业界终端 Agent 实现视 agent 为非可执行面一致)。这些如需收紧另议。\n */\nexport const HIGH_RISK_PROJECT_KEYS = [\n 'permission_mode',\n 'permissions',\n 'permission_always_allow',\n 'mcp_servers',\n 'disabled_hooks',\n 'sandbox',\n] as const satisfies readonly (keyof Setting)[]\n\nexport interface SanitizeResult {\n /** 剔除高危键后的层(浅拷贝,不改入参)。 */\n value: Partial<Setting>\n /** 实际被剔除的键名(用于告警;空数组 = 无高危键,可视为未门控)。 */\n stripped: string[]\n}\n\n/**\n * 从未受信任的项目层剔除高危键。纯函数:返回新对象,不改入参。\n * 仅作用于显式存在的键(`in` 命中),不会注入默认值。\n */\nexport function sanitizeUntrustedProjectLayer(layer: Partial<Setting>): SanitizeResult {\n const value: Partial<Setting> = { ...layer }\n const stripped: string[] = []\n for (const key of HIGH_RISK_PROJECT_KEYS) {\n if (key in value) {\n delete value[key]\n stripped.push(key)\n }\n }\n return { value, stripped }\n}\n","/**\n * config-origins.ts —— RFC review D11:配置来源溯源(codex config/fingerprint.rs\n * record_origins 移植)。\n *\n * 与 `setting.ts` 的 merge 链并行构建一棵「来源树」:每个配置字段记录其最终值的来源层。\n * 用途:`otto doctor` / 诊断面回答「这个值来自哪层」(多层配置合并时最常见的排障问题)。\n *\n * 层序(低→高,与 setting.ts merge 调用序一致):\n * 'default' < 'extra:N' < 'global' < 'project' < 'env'\n *\n * 纯函数、无 IO(与 merge 同构);数组/对象深合并语义与 merge 逐字对齐。\n */\n\n/** 配置层名:'default' | 'global' | 'project' | 'env' | 'extra:N'(N = extraLayers 下标)。 */\nexport type ConfigLayerName = 'default' | 'global' | 'project' | 'env' | `extra:${number}`\n\n/** 来源树:与配置树同构;叶子 = 该字段最终值的来源层名。 */\nexport interface ConfigOrigins {\n [key: string]: ConfigLayerName | ConfigOrigins\n}\n\nfunction isPlainObject(v: unknown): v is Record<string, unknown> {\n return typeof v === 'object' && v !== null && !Array.isArray(v)\n}\n\n/**\n * 深合并两棵树,同时记录每个最终字段的来源层。\n * 语义与 setting.ts deepMerge 逐字对齐:\n * - 值 undefined → 跳过;\n * - 两侧皆 plain object → 递归深合并(来源树同步递归);\n * - 否则 higher 覆盖(来源树记录 higherLayer)。\n */\nfunction mergeValueWithOrigin(\n target: Record<string, unknown>,\n source: Record<string, unknown>,\n targetOrigins: ConfigOrigins,\n higherLayer: ConfigLayerName,\n): { value: Record<string, unknown>; origins: ConfigOrigins } {\n const result = { ...target }\n const origins: ConfigOrigins = { ...targetOrigins }\n\n for (const [key, value] of Object.entries(source)) {\n if (value === undefined) continue\n\n if (isPlainObject(value) && isPlainObject(result[key])) {\n const sub = mergeValueWithOrigin(\n result[key],\n value,\n (targetOrigins[key] as ConfigOrigins) ?? {},\n higherLayer,\n )\n result[key] = sub.value\n origins[key] = sub.origins\n } else {\n result[key] = value\n origins[key] = higherLayer\n }\n }\n\n return { value: result, origins }\n}\n\n/**\n * 多层合并 + 来源追踪。与 setting.ts `merge(...layers)` 语义逐字对齐:\n * - 数组字段(string[])跨层去重并集(既有元素保留原来源,新增元素记当前层);\n * - 对象深合并;\n * - 标量高层覆盖。\n *\n * @param layersAndNames 层内容 + 层名(数组序低→高)。\n */\nexport function mergeLayersWithOrigins(\n layersAndNames: Array<{ layer: Partial<Record<string, unknown>>; name: ConfigLayerName }>,\n): { value: Record<string, unknown>; origins: ConfigOrigins } {\n const value: Record<string, unknown> = {}\n const origins: ConfigOrigins = {}\n\n for (const { layer, name } of layersAndNames) {\n for (const [key, val] of Object.entries(layer)) {\n if (val === undefined) continue\n\n if (Array.isArray(value[key]) && Array.isArray(val)) {\n // 数组去重并集(与 merge 同语义)\n const existing = value[key] as string[]\n const incoming = val as string[]\n const mergedArr = [...new Set([...existing, ...incoming])]\n value[key] = mergedArr\n // 来源:数组元素级不可追溯(值语义),记录为「合并自当前层」——\n // 标注 lastTouchedLayer,诊断面显示「最后被 env/project 触碰」。\n origins[key] = name\n continue\n }\n\n if (isPlainObject(val) && isPlainObject(value[key])) {\n const sub = mergeValueWithOrigin(\n value[key],\n val,\n (origins[key] as ConfigOrigins) ?? {},\n name,\n )\n value[key] = sub.value\n origins[key] = sub.origins\n } else if (isPlainObject(val)) {\n // val 是对象但下层无对应对象(undefined)→ 递归初始化整棵子树\n const sub = mergeValueWithOrigin({}, val, {}, name)\n value[key] = sub.value\n origins[key] = sub.origins\n } else {\n value[key] = val\n origins[key] = name\n }\n }\n }\n\n return { value, origins }\n}\n\n/**\n * 读取某字段路径的来源层(点分路径,如 'agents.default.max_tool_turns')。\n * 数组字段返回层名;不存在返回 undefined。\n */\nexport function originAt(origins: ConfigOrigins, dottedPath: string): ConfigLayerName | undefined {\n let node: ConfigLayerName | ConfigOrigins | undefined = origins\n const parts = dottedPath.split('.')\n for (const part of parts) {\n if (node === undefined || typeof node === 'string') return undefined\n const next: ConfigLayerName | ConfigOrigins | undefined = node[part]\n if (next === undefined || typeof next === 'string') return next\n node = next\n }\n return undefined\n}\n","import { createLogger, parseJsonc } from '@x-otto/shared'\nimport { LOG_LEVELS, SETTING_KEYS, SettingSchema } from './types'\nimport { migrate } from './migrator'\nimport { sanitizeUntrustedProjectLayer } from './project-trust'\nimport { mergeLayersWithOrigins } from './config-origins'\n\nimport { type Setting, type LoadSettingOptions, type SettingWarning, DEFAULT_CONFIG } from './types'\nimport type { SettingStore } from './store'\n\nconst logger = createLogger('@x-otto/setting')\nconst LOG_LEVEL_SET = new Set<string>(LOG_LEVELS)\n\nconst REMOVED_LEGACY_KEYS = ['mcp', 'skills', 'disabled_mcps', 'disabled_skills'] as const\n\n/**\n * RFC-205 移除的旧任意主题覆盖字段——曾经允许用户往 `config.json` 顶层写这些键实现\n * 任意 hex/spacing/figures 覆盖。RFC-277 M4 恢复了 `theme_overrides` 字段名,但语义\n * 收窄为「只放行 markdown typography 语义角色」;若磁盘上仍残留旧顶层字段,必须继续\n * 忽略并 warn-once(不能因为字段名复活就静默复活旧任意覆盖行为)。\n */\nconst REMOVED_LEGACY_THEME_KEYS = ['theme_file', 'theme_spacing', 'theme_border', 'theme_figures'] as const\n\n/**\n * `theme_overrides` 本身作为字段名被 RFC-277 M4 复用,但**旧 shape**(顶层直接放\n * `accent`/`colors`/`spacing`/`border`/`figures` 等任意覆盖键,而非新 shape 的\n * `{ markdown: {...} }`)必须被识别并单独 warn——不能让 `SettingSchema.strictObject`\n * 的静默丢弃看起来像\"什么都没发生\",用户需要知道旧配置已失效。\n */\nconst LEGACY_THEME_OVERRIDES_KEYS = new Set([\n 'accent', 'accentDim', 'accentBright', 'colors', 'spacing', 'border', 'figures',\n 'text', 'inactive', 'secondary', 'success', 'error', 'warning', 'special',\n])\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\n/**\n * 递归发现被 `lenient()`(`.optional().catch(undefined)`)静默吞掉的嵌套字段(F9)。\n *\n * 背景:SettingSchema 的每个字段都是 lenient——根级字段类型错误已被上层 `validate()` 的\n * 单层比对捕获,但**嵌套对象**(如 `agents.<id>.max_tool_turns`、`mcp_servers.<id>.command`)\n * 的子字段类型错误只让该子字段静默变 undefined,父对象仍然存在,单层比对因此漏检。\n *\n * 只递归 plain object(含 record 形态的动态键对象,如 `agents`/`mcp_servers`)——数组字段\n * (如 `permissions`)在 schema 里是 `lenient(z.array(...))`,单个元素类型错误会让**整个数组**\n * 被 catch 吞成 undefined(非逐元素丢弃),故数组场景已由上层单层比对(`value[key] === undefined`)\n * 覆盖,无需在此递归数组内部。\n */\nfunction collectDroppedNestedFields(\n input: Record<string, unknown>,\n parsed: Record<string, unknown>,\n path: string[],\n warnings: SettingWarning[],\n): void {\n for (const [key, inputVal] of Object.entries(input)) {\n if (inputVal === undefined) continue\n const parsedVal = parsed[key]\n const fullKey = [...path, key].join('.')\n if (parsedVal === undefined) {\n warnings.push({ key: fullKey, message: `Invalid value for \"${fullKey}\" ignored` })\n continue\n }\n if (isPlainObject(inputVal) && isPlainObject(parsedVal)) {\n collectDroppedNestedFields(inputVal, parsedVal, [...path, key], warnings)\n }\n }\n}\n\nfunction deepMerge(\n target: Record<string, unknown>,\n source: Record<string, unknown>,\n): Record<string, unknown> {\n const result = { ...target }\n\n for (const [key, value] of Object.entries(source)) {\n if (value === undefined) {\n continue\n }\n\n if (isPlainObject(value) && isPlainObject(result[key])) {\n result[key] = deepMerge(result[key], value)\n } else {\n result[key] = value\n }\n }\n\n return result\n}\n\nfunction merge(...layers: Partial<Setting>[]): Partial<Setting> {\n const [lower, higher, ...others] = layers\n const result: Record<string, unknown> = { ...lower }\n\n if (higher) {\n for (const [key, value] of Object.entries(higher)) {\n if (value === undefined) {\n continue\n }\n\n if (Array.isArray(result[key]) && Array.isArray(value)) {\n const existing = result[key] as string[]\n const incoming = value as string[]\n\n result[key] = [...new Set([...existing, ...incoming])]\n } else if (isPlainObject(value) && isPlainObject(result[key])) {\n result[key] = deepMerge(result[key], value)\n } else {\n result[key] = value\n }\n }\n }\n\n if (others.length > 0) {\n return merge(result as Partial<Setting>, ...others)\n }\n\n return result as Partial<Setting>\n}\n\nfunction loadSettingFromEnv(): Partial<Setting> {\n const setting: Partial<Setting> = {}\n\n const model = process.env.OTTO_MODEL\n if (model) {\n setting.model = model\n }\n\n const logLevel = process.env.OTTO_LOG_LEVEL\n if (logLevel) {\n if (LOG_LEVEL_SET.has(logLevel)) {\n setting.log_level = logLevel as Setting['log_level']\n }\n }\n\n return setting\n}\n\n/** 导出供单测直接验证 lenient() 静默丢弃字段的告警覆盖(F9),非公开配置加载 API 的一部分。 */\nexport function validate(setting: Record<string, unknown>): {\n value: Partial<Setting>\n warnings: SettingWarning[]\n} {\n const warnings: SettingWarning[] = []\n const input: Record<string, unknown> = { ...setting }\n\n for (const key of REMOVED_LEGACY_KEYS) {\n if (input[key] !== undefined) {\n warnings.push({ key, message: 'Removed legacy config key is ignored' })\n delete input[key]\n }\n }\n\n // RFC-205/RFC-277:`theme_file`/`theme_spacing`/`theme_border`/`theme_figures` 顶层键\n // 已随 RFC-205 移除;即使 `theme_overrides` 字段名在 RFC-277 M4 被复用,这几个独立\n // 顶层键仍必须继续被拒绝——防止旧任意主题覆盖机制的其他残留片段悄悄复活。\n for (const key of REMOVED_LEGACY_THEME_KEYS) {\n if (input[key] !== undefined) {\n warnings.push({ key, message: 'RFC-205 legacy theme override key is ignored' })\n delete input[key]\n }\n }\n\n // RFC-277 M4:`theme_overrides` 旧 shape(顶层直接放 accent/colors/spacing 等任意覆盖键,\n // 而非新 shape `{ markdown: {...} }`)单独识别并给出明确 warning——比通用的\n // \"Invalid value for theme_overrides ignored\" 更能定位问题根因(RFC-205 legacy shape\n // vs 单纯拼写错误),不能让用户以为是新 schema 校验失败。\n if (isPlainObject(input.theme_overrides)) {\n const legacyKeys = Object.keys(input.theme_overrides).filter((k) => LEGACY_THEME_OVERRIDES_KEYS.has(k))\n if (legacyKeys.length > 0) {\n warnings.push({\n key: 'theme_overrides',\n message: `RFC-205 legacy theme_overrides shape (keys: ${legacyKeys.join(', ')}) is ignored — use theme_overrides.markdown instead`,\n })\n }\n }\n\n const result = SettingSchema.loose().safeParse(input)\n const parsed: Record<string, unknown> = result.success ? result.data : input\n\n const value: Record<string, unknown> = {}\n for (const [key, val] of Object.entries(parsed)) {\n if (val !== undefined) {\n value[key] = val\n }\n }\n\n for (const key of SETTING_KEYS) {\n const inputVal = input[key]\n const valueVal = value[key]\n if (inputVal !== undefined && valueVal === undefined) {\n warnings.push({ key, message: `Invalid value for \"${key}\" ignored` })\n continue\n }\n // F9:根级字段本身有效(对象未被整体吞掉)时,递归比对其内部,捕获嵌套子字段被\n // lenient() 静默丢弃的情况(如 agents.<id>.max_tool_turns 类型错误)。\n if (isPlainObject(inputVal) && isPlainObject(valueVal)) {\n collectDroppedNestedFields(inputVal, valueVal, [key], warnings)\n }\n }\n\n return { value: value as Partial<Setting>, warnings }\n}\n\nasync function loadSettingFromStore(\n store: SettingStore,\n paths: string[],\n untrustedProjectPath?: string,\n): Promise<{ layers: Partial<Setting>[]; stripped: string[] }> {\n const layers: Partial<Setting>[] = []\n const stripped: string[] = []\n\n for (const path of paths) {\n try {\n const content = await store.read(path)\n if (content !== undefined) {\n let parsed = parseJsonc<Partial<Setting>>(content)\n // 未受信任的项目层(被克隆仓的 .otto/config.json)剔除高危键——加载即降级。\n if (untrustedProjectPath !== undefined && path === untrustedProjectPath) {\n const sanitized = sanitizeUntrustedProjectLayer(parsed)\n parsed = sanitized.value\n stripped.push(...sanitized.stripped)\n }\n layers.push(parsed)\n logger.debug({ path }, 'Config loaded from store')\n }\n } catch (error) {\n logger.warn({ path, err: error }, 'Failed to parse config file, skipping')\n }\n }\n\n return { layers, stripped }\n}\n\nexport class SettingLoader {\n private store?: SettingStore\n /** RFC review D11:上次 load 的来源树(惰性快照,供诊断面查询「值来自哪层」)。 */\n private lastOrigins?: import('./config-origins').ConfigOrigins\n\n constructor(store?: SettingStore) {\n this.store = store\n }\n\n /** RFC review D11:上次 load 的配置来源树(未 load 过返回 undefined)。 */\n getOrigins(): import('./config-origins').ConfigOrigins | undefined {\n return this.lastOrigins\n }\n\n async load(options: LoadSettingOptions = {}): Promise<Setting> {\n const { store = this.store, paths = [], extraLayers = [], untrustedProjectPath } = options\n\n const loaded =\n store && paths.length > 0\n ? await loadSettingFromStore(store, paths, untrustedProjectPath)\n : { layers: [] as Partial<Setting>[], stripped: [] as string[] }\n const { layers, stripped } = loaded\n\n for (const key of stripped) {\n logger.warn(\n { key, path: untrustedProjectPath },\n '未受信任项目配置剔除高危键(permission_mode/mcp_servers 等需先信任该目录)',\n )\n }\n\n const env = loadSettingFromEnv()\n const merged = merge(DEFAULT_CONFIG, ...extraLayers, ...layers, env)\n\n // RFC review D11:来源树并行构建(default 底 → extra:N → global/project → env 顶,\n // 与 merge 调用序一致)。store layers 的层名按 paths 推断:最后一层=project、其余=global\n // (与 buildSettingPaths 的「全局做底、项目覆盖」语义一致)。\n const originsInput: Array<{ layer: Partial<Record<string, unknown>>; name: import('./config-origins').ConfigLayerName }> = [\n { layer: DEFAULT_CONFIG as Partial<Record<string, unknown>>, name: 'default' },\n ...extraLayers.map((l, i) => ({ layer: l as Partial<Record<string, unknown>>, name: `extra:${i}` as const })),\n ...layers.map((l, i) => ({\n layer: l as Partial<Record<string, unknown>>,\n // paths 去重后只有 global/project 两种;多路径时最后一个为 project(高优先)。\n name: (i === layers.length - 1 ? 'project' : 'global') as import('./config-origins').ConfigLayerName,\n })),\n { layer: env as Partial<Record<string, unknown>>, name: 'env' },\n ]\n this.lastOrigins = mergeLayersWithOrigins(originsInput).origins\n\n const { config: migrated, applied } = migrate(merged as Record<string, unknown>)\n // 架构 review 2026-08-12 S8:migrations 参数当前无生产传入(恒空数组,migrate 为\n // 空转脚手架)——config 演进实际靠 lenient 静默丢弃 + validate() warning 的\n // fail-soft 单一策略。config_version/_migrations 字段被 remote-sync-keys 白名单与\n // config-export-format 排除表引用,删除会牵动远端同步契约面,故保留机制为未来\n // 「需要真数据迁移的破坏性变更」挂点,不在本次随手清单内拆除。\n if (applied.length > 0) {\n logger.info({ applied }, 'Config migrations applied')\n }\n\n const { value: validated, warnings } = validate(migrated as Record<string, unknown>)\n for (const warning of warnings) {\n logger.warn({ key: warning.key, message: warning.message }, 'Config validation issue')\n }\n\n return { ...DEFAULT_CONFIG, ...validated }\n }\n}\n\nexport async function loadSetting(options: LoadSettingOptions = {}): Promise<Setting> {\n const loader = new SettingLoader(options.store)\n return loader.load(options)\n}\n","import { safeStringify } from '@x-otto/shared'\nimport { atomicWriteFile } from '@x-otto/persistence'\n\nimport type { SettingStore } from './store'\nimport type { Setting } from './types'\n\n/**\n * 原文件 SettingStore(M14-06):按**字面路径**读写用户手写配置(.otto/config.json)。\n *\n * 修复点:此前 SettingsManager 无缺省 store(`this.store` 从未赋值)→ 项目配置文件在\n * 生产上从未被读取;而 persistence-backed 的 file store 是 snapshot 信封式\n * (id → baseDir/<encoded>.json),无法读取用户在固定路径手写的 jsonc 原文——\n * 两者叠加 = 配置文件整条链路死亡。本 store 面向\"用户手写配置文件\"语义:\n * 原文读取(jsonc 解析在 loader 层)、缺失返回 undefined、写入为格式化 JSON。\n *\n * RFC-303 D7(M2 T303-04):`write` 此前用裸 `writeFile`(truncate+写),非原子——\n * 跨进程两个 otto 并发 `persist()` 会交错 truncate/write,读者可能读到半写文件(下次\n * `loadSetting` 时 parseJsonc 失败、配置整体丢失或回退)。改用 `@x-otto/persistence` 的\n * `atomicWriteFile`(temp+fsync+rename+dir-fsync)——跨进程读者永不看到半写文件。\n * 不加跨进程 read-modify-write 锁(当前无实证丢写事故,先原子化消除损坏面;锁待有\n * 实证再加——反过度设计,同 RFC-303 D7 决策记录)。\n */\nexport class RawFileSettingStore implements SettingStore {\n readonly type = 'file' as const\n\n async read(path: string): Promise<string | undefined> {\n const { readFile } = await import('node:fs/promises')\n try {\n return await readFile(await expandPath(path), 'utf-8')\n } catch {\n return undefined\n }\n }\n\n async write(path: string, config: Partial<Setting>): Promise<void> {\n const { mkdir } = await import('node:fs/promises')\n const { dirname } = await import('node:path')\n\n const target = await expandPath(path)\n await mkdir(dirname(target), { recursive: true })\n await atomicWriteFile(target, safeStringify(config, 2))\n }\n\n async exists(path: string): Promise<boolean> {\n const { access } = await import('node:fs/promises')\n try {\n await access(await expandPath(path))\n return true\n } catch {\n return false\n }\n }\n}\n\nasync function expandPath(path: string): Promise<string> {\n if (!path.startsWith('~/')) {\n return path\n }\n const { homedir } = await import('node:os')\n return `${homedir()}${path.slice(1)}`\n}\n","import type { Setting, SettingKey } from './schema'\n\n/**\n * RFC-052 D2:可跨机器同步的用户偏好字段(syncable keys 白名单)。\n *\n * 与 SettingSchema 同包维护,字段增删时编译期提醒同步检查(R2)。\n */\nexport const SYNCABLE_KEYS = new Set<SettingKey>([\n 'model',\n 'model_fallback',\n 'recent_models',\n 'language',\n 'nickname',\n 'has_completed_onboarding',\n 'has_completed_wizard',\n 'wizard_dont_show_again',\n 'first_prompt_submitted_at',\n 'log_level',\n 'notification',\n 'auto_update_check',\n 'model_slots',\n 'disabled_agents',\n 'disabled_hooks',\n 'disabled_tools',\n 'disabled_skills',\n])\n\n/**\n * RFC-052 D2:绝不可远程同步的字段(安全面/本地路径/凭据)。\n *\n * 此列表保持与 SYNCABLE_KEYS 互斥;不在任一集合的字段为\"当前不支持同步,未来可能加入\"。\n */\nexport const NEVER_SYNCED_KEYS: ReadonlySet<string> = new Set([\n // recent_mentions 常含本地文件相对路径(@file 条目)——跨机器/跨项目同步会产生\n // 指向不存在文件的幽灵项,且该字段本就是项目级(落 .otto/config.json 项目层,\n // 语义上与 recent_models 的\"用户全局偏好\"不同),不应走远端 user-scope 同步。\n 'recent_mentions',\n // RFC-233:@ 快捷方式是仓库特定的指令模板(同 recent_mentions 语义),不应跨机器/\n // 账号同步——不同仓库的常用指令天然不同。\n 'shortcuts',\n 'mcp_servers',\n 'sandbox',\n 'permission_mode',\n 'permissions',\n 'permission_always_allow',\n 'web_fetch_allowed_hosts',\n 'session',\n 'tool_preset',\n 'config_version',\n 'version',\n '_migrations',\n 'last_update_check',\n 'memory_auto_extract',\n 'skill_loop',\n // RFC-383 D4:内化决策器配置——行为调优参数(同 memory_auto_extract 语义),\n // 不跨机器同步(内化阈值是本机 skill usage 演进的产物,非用户跨机偏好)。\n 'skill_internalization',\n 'workflow',\n 'categories',\n 'agents',\n 'disabled_plugins',\n // RFC-219 D1:插件安装清单是设备绑定项(symlink 目标路径/安装来源与本机状态强相关),\n // 不应经 RemoteSettingStore 同步到远端账号——同一账号的另一台设备可能压根没装同一批插件。\n 'installed_plugins',\n])\n\n/**\n * 从 Partial<Setting> 中过滤出仅 syncable 字段(R2)。\n * NEVER_SYNCED 字段即使传入也被移除。\n */\nexport function filterSyncable(partial: Partial<Setting>): Partial<Setting> {\n const result: Partial<Setting> = {}\n for (const key of Object.keys(partial) as SettingKey[]) {\n if (SYNCABLE_KEYS.has(key) && !NEVER_SYNCED_KEYS.has(key)) {\n ;(result as Record<string, unknown>)[key] = partial[key]\n }\n }\n return result\n}\n","import { filterSyncable } from './remote-sync-keys'\nimport type { SettingStore } from './store'\nimport type { Setting } from './types'\n\n/**\n * 远端 user 级配置同步的 HTTP SettingStore(RFC-052 M71d)。\n *\n * 读:HTTP GET `/config/user` → 还原 syncable 字段子集\n * 写:filterSyncable() 过滤 → HTTP PUT `/config/user` push\n *\n * R5 降级:远端不可达时 read 返回 undefined、write 静默忽略,不阻塞启动。\n * R2:syncable 白名单由 remote-sync-keys.ts 维护。\n */\nexport class RemoteSettingStore implements SettingStore {\n readonly type = 'file' as const\n\n private readonly baseUrl: string\n private readonly userId: string\n private readonly getAuth: () => Promise<{ token: string }>\n private readonly fetchImpl: typeof globalThis.fetch\n private readonly timeoutMs: number\n\n constructor(options: {\n /** 远端服务器 base URL(不尾斜线)。 */\n baseUrl: string\n /** 获取 Bearer token 的工厂。 */\n getAuth: () => Promise<{ token: string }>\n /** fetch 实现(测试注入点)。 */\n fetch: typeof globalThis.fetch\n /** 请求超时 (ms)。 */\n timeoutMs: number\n /** 当前用户 identity id。 */\n userId: string\n }) {\n this.baseUrl = options.baseUrl.replace(/\\/+$/, '')\n this.userId = options.userId\n this.getAuth = options.getAuth\n this.fetchImpl = options.fetch\n this.timeoutMs = options.timeoutMs\n }\n\n /**\n * HTTP GET `/config/user` → JSON 同步字段子集。\n *\n * **失败传播(RFC-354 P5 修正,2026-08-12)**:同 `write()`——此前 `catch {}` 吞掉\n * 一切并返回 undefined,使 `SettingsManager.reload()` 里 S10 新增的\n * `catch → emit('remote-sync-failed', {op:'read'})` 永不触发。现在向调用方抛出;\n * manager 侧已有 try/catch 接住并发事件,**本地设置仍正常加载**(remoteLayer 保持\n * undefined 回退本地层),reload() 契约不变。\n *\n * \"远端读不到 = 无远端层\"与\"远端读失败 = 应当可诊断\"是两件事,此前被混为一谈。\n */\n async read(_path: string): Promise<string | undefined> {\n const auth = await this.getAuth()\n const headers: Record<string, string> = {\n Authorization: `Bearer ${auth.token}`,\n Accept: 'application/json',\n 'X-Otto-User-Id': this.userId,\n }\n\n const controller = new AbortController()\n const timeout = setTimeout(() => controller.abort(), this.timeoutMs)\n\n let response: Response\n try {\n response = await this.fetchImpl(`${this.baseUrl}/config/user`, {\n method: 'GET',\n headers,\n signal: controller.signal,\n })\n } finally {\n clearTimeout(timeout)\n }\n\n // 404 = 该用户尚无远端配置,是正常状态而非失败——返回 undefined 回退本地层,\n // 不发失败事件(否则新用户每次启动都会收到一条噪音诊断)。\n if (response.status === 404) return undefined\n\n if (!response.ok) {\n throw new Error(`remote settings read failed: HTTP ${response.status}`)\n }\n\n const body = await response.json()\n return JSON.stringify(body)\n }\n\n /**\n * HTTP PUT `/config/user` 推送过滤后的 syncable 字段。\n * R2: write 前按 SYNCABLE_KEYS 裁剪。\n *\n * **失败传播(RFC-354 P5 修正,2026-08-12)**:本方法此前用 `catch {}` 吞掉一切\n * (且不检查 `response.ok`),promise 永不 reject——导致 `SettingsManager` 里\n * RFC-353 S10 新增的 `.catch(() => emit('remote-sync-failed'))` **在生产路径永远\n * 触发不到**,S10 的修复实际是死代码(单测只因为注入了会 throw 的 mock store 才绿)。\n * 实证:HTTP 500 与网络异常两种情况下 write() 均不 reject。\n *\n * 现在改为向调用方传播失败。**这不改变\"远端不可达不阻塞本地保存\"的 R5 语义**——\n * 该语义由调用方承担(`SettingsManager` 不 await 本调用、只挂 `.catch()` 发事件,\n * 本地写入仍是权威成功信号)。把\"要不要忽略失败\"的决定权交给调用方,而不是在\n * store 内部单方面吞掉,是 S10 可见性要求的前提。\n */\n async write(_path: string, config: Partial<Setting>): Promise<void> {\n const syncable = filterSyncable(config)\n if (Object.keys(syncable).length === 0) return\n\n const auth = await this.getAuth()\n const headers: Record<string, string> = {\n Authorization: `Bearer ${auth.token}`,\n 'Content-Type': 'application/json',\n 'X-Otto-User-Id': this.userId,\n }\n\n const controller = new AbortController()\n const timeout = setTimeout(() => controller.abort(), this.timeoutMs)\n\n let response: Response\n try {\n response = await this.fetchImpl(`${this.baseUrl}/config/user`, {\n method: 'PUT',\n headers,\n body: JSON.stringify(syncable),\n signal: controller.signal,\n })\n } finally {\n clearTimeout(timeout)\n }\n\n // 非 2xx 同样是失败——此前连 response.ok 都不检查,服务端 500 被当成成功。\n if (!response.ok) {\n throw new Error(`remote settings write failed: HTTP ${response.status}`)\n }\n }\n\n async exists(_path: string): Promise<boolean> {\n return false\n }\n}\n","/** Claude Code settings adapter: read .claude/settings.json → otto config. */\nimport { homedir } from 'node:os'\nimport { existsSync, readFileSync } from 'node:fs'\nimport { join, resolve } from 'node:path'\nimport { createLogger, parseJsonc, mapClaudeToolName } from '@x-otto/shared'\nimport { CLAUDE_HOME, isClaudeSettingsCompatEnabled } from '@x-otto/env'\nimport type { Setting, PermissionRuleConfig, PermissionPolicySetting } from './types'\n\nconst logger = createLogger('@x-otto/setting:claude-adapter')\n\ntype Effect = 'allow' | 'deny' | 'ask'\n\n/** 具备文件路径 specifier 语义的 Claude 工具(specifier=glob 路径)。 */\nconst PATH_TOOLS = new Set(['Read', 'Write', 'Edit', 'MultiEdit', 'Glob', 'Grep'])\n\n/** Claude defaultMode → otto permission_mode(非 1:1,保守取收紧侧)。 */\nconst MODE_MAP: Record<string, Setting['permission_mode']> = {\n default: 'confirm',\n acceptEdits: 'auto',\n bypassPermissions: 'bypass',\n plan: 'readonly',\n}\n\n/** 解析 `Tool(specifier)` / 裸 `Tool`。 */\nexport function parseToolSpecifier(spec: string): { tool: string; specifier?: string } {\n const m = /^([A-Za-z][A-Za-z0-9_]*)(?:\\((.*)\\))?$/.exec(spec.trim())\n if (!m) return { tool: spec.trim() }\n return { tool: m[1]!, specifier: m[2] }\n}\n\n/** glob → 锚定正则字符串(`**`→`.*`,`*`→`[^/]*`,`?`→`.`,`~` 展开 home)。 */\nexport function globToRegexSource(glob: string): string {\n const expanded = glob.startsWith('~/') ? join(homedir(), glob.slice(2)) : glob\n let re = ''\n for (let i = 0; i < expanded.length; i++) {\n const c = expanded[i]!\n if (c === '*') {\n if (expanded[i + 1] === '*') {\n re += '.*'\n i++\n } else re += '[^/]*'\n } else if (c === '?') re += '.'\n else re += c.replace(/[.+^${}()|[\\]\\\\]/g, '\\\\$&')\n }\n return `^${re}$`\n}\n\ninterface TranslateResult {\n rules: PermissionRuleConfig[]\n webFetchHosts: string[]\n warnings: string[]\n}\n\nfunction translateSpecifier(spec: string, effect: Effect, out: TranslateResult, idx: number): void {\n const { tool, specifier } = parseToolSpecifier(spec)\n const ottoTool = mapClaudeToolName(tool)\n if (!ottoTool) {\n out.warnings.push(`未知工具 \"${tool}\"(${spec})→ fail-closed 丢弃`)\n return\n }\n\n if (tool === 'WebFetch' && specifier?.startsWith('domain:')) {\n const host = specifier.slice('domain:'.length).trim()\n if (effect === 'allow' && host) {\n out.webFetchHosts.push(host)\n return\n }\n // deny 降 ask 会放宽语义(ask 比 deny 宽);按 effect fail-closed 分流:deny→工具级 deny,其余→ask。\n const downgraded: Effect = effect === 'deny' ? 'deny' : 'ask'\n out.rules.push({ name: `claude-${idx}`, effect: downgraded, tools: [ottoTool] })\n out.warnings.push(\n `WebFetch(${specifier}) 的 ${effect} 不可按域表达 → ${downgraded === 'deny' ? '降工具级 deny(fail-closed)' : '降 ask'}`,\n )\n return\n }\n\n if (specifier === undefined || specifier === '') {\n out.rules.push({ name: `claude-${idx}`, effect, tools: [ottoTool] })\n return\n }\n\n if (PATH_TOOLS.has(tool)) {\n out.rules.push({\n name: `claude-${idx}`,\n effect,\n tools: [ottoTool],\n paths: [globToRegexSource(specifier)],\n })\n return\n }\n\n out.rules.push({\n name: `claude-${idx}`,\n effect: 'ask',\n tools: [ottoTool],\n ask_prompt: `Claude 规则 \"${spec}\" 含命令参数限定,otto 无法精确表达,已降为询问。`,\n })\n out.warnings.push(`\"${spec}\" 含参数 specifier 不可表达 → fail-closed 降 ask`)\n}\n\n/** 纯翻译:Claude settings 对象 → otto Partial<Setting>。 */\nexport function translateClaudeSettings(raw: unknown): {\n setting: Partial<Setting>\n warnings: string[]\n} {\n const result: TranslateResult = { rules: [], webFetchHosts: [], warnings: [] }\n const perms = (raw as { permissions?: Record<string, unknown> })?.permissions\n let idx = 0\n\n for (const effect of ['allow', 'deny', 'ask'] as Effect[]) {\n const list = perms?.[effect]\n if (!Array.isArray(list)) continue\n for (const spec of list) {\n if (typeof spec === 'string') translateSpecifier(spec, effect, result, idx++)\n }\n }\n\n const setting: Partial<Setting> = {}\n if (result.rules.length > 0) {\n const policy: PermissionPolicySetting = { name: 'claude-compat', rules: result.rules }\n setting.permissions = [policy]\n }\n if (result.webFetchHosts.length > 0) {\n setting.web_fetch_allowed_hosts = [...new Set(result.webFetchHosts)]\n }\n const mode = typeof perms?.['defaultMode'] === 'string' ? perms['defaultMode'] : undefined\n if (mode && MODE_MAP[mode]) {\n setting.permission_mode = MODE_MAP[mode]\n if (mode === 'plan') result.warnings.push('defaultMode=plan otto 无对应 → 降 readonly')\n }\n\n return { setting, warnings: result.warnings }\n}\n\n/**\n * 加载并翻译 `.claude` settings 层(最低优先级,供 SettingsManager 合并)。\n * 顺序(低→高合并):`~/.claude/settings.json` < 项目 `.claude/settings.json` < `.claude/settings.local.json`。\n * 默认关;启用时打印导入清单(条目数 + 降级 warning)供核对。返回 {} 当关闭/无文件。\n */\nexport function loadClaudeSettingsLayer(workspaceDir?: string): Partial<Setting> {\n if (!isClaudeSettingsCompatEnabled()) return {}\n\n const files = [\n join(CLAUDE_HOME, 'settings.json'),\n ...(workspaceDir\n ? [\n join(resolve(workspaceDir), '.claude', 'settings.json'),\n join(resolve(workspaceDir), '.claude', 'settings.local.json'),\n ]\n : []),\n ]\n\n // 深合并:permission 规则跨层累加(浅 spread 会让后层整体覆盖前层、丢前层 deny 规则 → 违 fail-closed)。\n // 评估器 deny 优先且名仅作标签,故累加顺序无关、重名无害;mode 取末层(高优先级)。\n const allRules: PermissionRuleConfig[] = []\n const allHosts: string[] = []\n let permissionMode: Setting['permission_mode'] | undefined\n const allWarnings: string[] = []\n for (const file of files) {\n if (!existsSync(file)) continue\n let raw: unknown\n try {\n raw = parseJsonc(readFileSync(file, 'utf-8'))\n } catch (err) {\n logger.warn({ file, err }, 'Failed to parse .claude settings, skipped')\n continue\n }\n const { setting, warnings } = translateClaudeSettings(raw)\n allWarnings.push(...warnings)\n const rules = setting.permissions?.[0]?.rules\n if (rules) allRules.push(...rules)\n if (setting.web_fetch_allowed_hosts) allHosts.push(...setting.web_fetch_allowed_hosts)\n if (setting.permission_mode) permissionMode = setting.permission_mode\n }\n\n const merged: Partial<Setting> = {}\n if (allRules.length > 0) {\n merged.permissions = [{ name: 'claude-compat', rules: allRules }]\n }\n if (allHosts.length > 0) {\n merged.web_fetch_allowed_hosts = [...new Set(allHosts)]\n }\n if (permissionMode) merged.permission_mode = permissionMode\n const ruleCount = allRules.length\n\n if (ruleCount > 0 || allWarnings.length > 0) {\n logger.info(\n {\n ruleCount,\n hosts: merged.web_fetch_allowed_hosts?.length ?? 0,\n mode: merged.permission_mode,\n },\n 'Imported .claude/settings.json (RFC-032 D4)',\n )\n for (const w of allWarnings) logger.warn({ rule: w }, '.claude settings 翻译降级')\n }\n return merged\n}\n","import { OTTO_CONFIG_FILENAME, OTTO_HOME, OTTO_PROJECT_CONFIG_RELPATH, isShadowModeEnabled } from '@x-otto/env'\nimport { TypedEventEmitter, parseJsonc } from '@x-otto/shared'\nimport { resolve } from 'node:path'\n\nimport { loadSetting } from './setting'\nimport { loadClaudeSettingsLayer } from './claude-settings-adapter'\nimport { RawFileSettingStore } from './raw-file-store'\n\nimport type { Setting } from './types'\nimport type { SettingStore } from './store'\n\nexport interface SettingsOptions {\n workspaceDir?: string\n projectConfigPath?: string\n overrides?: Partial<Setting>\n /** 配置读取后端(M14-06)。缺省原文件 store(.otto/config.json 字面路径读取)。 */\n store?: SettingStore\n /**\n * RFC-052 M71d:远端 user config store。\n * 有 identity 时拉取用户偏好基线(如 model/theme/language)。\n * 优先级:project config > remote config > claude compat layer。\n */\n remoteStore?: SettingStore\n /**\n * 项目配置是否受信任。缺省 `true`(保持既有行为,零破坏)。\n * 传 `false`(或返回 false 的 thunk)时对项目层(最高优先级文件层)剔除高危键\n * (permission_mode/mcp_servers/…,见 project-trust.ts)——用于克隆来的未信任仓库。\n * **thunk 形态每次 reload 重新求值**:`/compat trust` 写入白名单后 `settings.load()` 即时生效。\n * 信任判定/持久化在 coding 层(claude-trust)。\n */\n projectConfigTrusted?: boolean | (() => boolean)\n}\n\ninterface SettingsEvents {\n change: (config: Setting) => void\n /**\n * RFC-353 S10:远端配置同步状态——persist()/reload() 的远端读写失败此前被\n * `.catch(() => {})`/`catch {}` 静默吞掉,本地已保存但远端陈旧无从诊断。\n * 消费方(如 CLI 状态栏/doctor)可订阅本事件在远端失败时提示用户。\n * 不改变 persist() 返回类型(30+ 现有调用点零改动),失败信息走事件旁路。\n *\n * 契约:订阅方 handler 不得抛异常——`TypedEventEmitter.emit` 不捕获 listener 异常,\n * 若 handler 抛出会向上传播穿透 reload()/persist() 的 catch 块(与既有 'change' 事件\n * 同规则,非本事件特有)。\n */\n 'remote-sync-failed': (payload: { op: 'read' | 'write'; error: unknown }) => void\n}\n\nexport class SettingsManager extends TypedEventEmitter<SettingsEvents> {\n private readonly paths: string[]\n private readonly workspaceDir?: string\n /**\n * 项目信任判定:boolean 或 thunk。thunk 每次 reload 重新求值——故 `/compat trust`\n * 写入信任白名单后调 `settings.load()` 即可即时解除剔除,无需重启。缺省 true=全信任。\n */\n private readonly projectConfigTrusted: boolean | (() => boolean)\n private overrides: Partial<Setting>\n\n private store: SettingStore | undefined\n private remoteStore: SettingStore | undefined\n private setting: Setting = {} as Setting\n /**\n * persist 串行化链。`persist` 的读-改-写非原子(read→merge→write 之间有 await),\n * 并发调用会交错丢写无关键(如插件 setup 回调 fire-and-forget 撞用户 /model 改动)。\n * 所有 persist 临界区排队经此链顺序执行;单次失败不毒化后续(catch 吞错续链)。\n */\n private writeChain: Promise<unknown> = Promise.resolve()\n\n constructor(options: SettingsOptions = {}) {\n super()\n this.paths = buildSettingPaths(options)\n this.workspaceDir = options.workspaceDir\n this.projectConfigTrusted = options.projectConfigTrusted ?? true\n this.overrides = { ...options.overrides }\n this.store = options.store ?? new RawFileSettingStore()\n this.remoteStore = options.remoteStore\n }\n\n get<K extends keyof Setting>(key: K): Setting[K] {\n return this.setting[key]\n }\n\n /**\n * 整对象只读快照——供需要**整个 Setting 序列化/差异比对**的场景(配置导出、迁移 diff\n * 计算,见 config-migration.ts/migrate.ts)。单字段读取一律走 `get(key)`(类型安全 +\n * 全仓统一入口),不要新增 `.config.xxx` 调用点。\n */\n get config(): Readonly<Setting> {\n return this.setting\n }\n\n get model(): Setting['model'] {\n return this.setting.model\n }\n\n async load(): Promise<Setting> {\n return this.reload(this.overrides)\n }\n\n async reload(overrides?: Partial<Setting>): Promise<Setting> {\n let remoteLayer: Partial<Setting> | undefined\n if (this.remoteStore) {\n try {\n const raw = await this.remoteStore.read('/config/user')\n if (raw !== undefined) {\n remoteLayer = parseJsonc<Partial<Setting>>(raw)\n }\n } catch (error) {\n // RFC-353 S10:此前静默吞错(catch {})——本地设置仍能加载(remoteLayer 保持\n // undefined,回退本地/claude-compat 层),但远端读失败对调用方完全不可见。\n // 发事件而非吞错:不改变 reload() 契约(仍返回本地可用的 Setting),\n // 但订阅方(如 doctor/状态栏)现在能诊断\"远端配置读取失败\"。\n this.emit('remote-sync-failed', { op: 'read', error })\n }\n }\n\n const setting = await loadSetting({\n store: this.store,\n paths: this.paths,\n untrustedProjectPath: this.resolveUntrustedProjectPath(),\n extraLayers: [\n loadClaudeSettingsLayer(this.workspaceDir),\n ...(remoteLayer ? [remoteLayer] : []),\n ],\n })\n\n this.setting = {\n ...setting,\n ...overrides,\n }\n\n this.emit('change', this.setting)\n\n return this.setting\n }\n\n async update(overrides: Partial<Setting>): Promise<Setting> {\n this.overrides = {\n ...this.overrides,\n ...overrides,\n }\n\n return this.reload(this.overrides)\n }\n\n /**\n * 持久化到磁盘配置文件(读-改-写默认落项目层 path),再 reload 生效。\n * 全局做底为只读 underlay(见 buildSettingPaths),默认写回落在最后一个 path(项目级),\n * 不会污染全局 `${OTTO_HOME}/config.json`。\n * `target`(可选):'global' 显式落 `paths[0]`(全局层),'project' 显式落项目层\n * (与缺省行为一致)。缺省 undefined 保持原有\"恒落项目层\"语义,向后兼容旧调用点。\n * 无对应层 path 时(如 target='global' 但会话无项目上下文导致 paths 为空)静默退化为\n * 内存 update,不抛错。\n * 读取磁盘原文 → 顶层合并 partial(保留 mcp_servers 等其余键)→ 写回 →\n * reload 触发 'change'(下游 permissionSync 等据此重同步)。无 store/path 时退化为内存 update。\n * 注:写回为格式化 JSON,会丢失原文件注释。\n */\n async persist(partial: Partial<Setting>, target?: 'global' | 'project'): Promise<Setting> {\n // RFC-345 §D3:影子态 persist 退化为内存 update——读继承磁盘(reload 的 underlay),\n // 写只进 this.overrides(当前会话生效、退出即失),config.json 逐字节不变。既有 else\n // 分支本就是纯内存 update,此处复用同一语义(§D1 白名单点:SettingsManager 唯一写盘出口)。\n if (isShadowModeEnabled()) {\n return this.update(partial)\n }\n const path = target === 'global' ? this.paths[0] : this.paths[this.paths.length - 1]\n const store = this.store\n if (store && path) {\n // 串行化整个读-改-写-reload 临界区,杜绝并发交错丢写(见 writeChain 注释)。\n return this.enqueueWrite(async () => {\n const raw = await store.read(path)\n let current: Partial<Setting> = {}\n if (raw) {\n try {\n current = parseJsonc<Partial<Setting>>(raw)\n } catch {\n current = {}\n }\n }\n await store.write(path, { ...current, ...partial })\n\n if (this.remoteStore) {\n // RFC-353 S10:此前 `.catch(() => {})` 静默吞掉远端写失败——本地已保存\n // acknowledged,远端悄悄陈旧,调用方与用户都无从诊断。改为发事件旁路\n // (不改变 persist() 返回类型/awaitability,本地写入仍是权威成功信号)。\n this.remoteStore.write('/config/user', { ...current, ...partial }).catch((error: unknown) => {\n this.emit('remote-sync-failed', { op: 'write', error })\n })\n }\n\n return this.reload(this.overrides)\n })\n }\n return this.update(partial)\n }\n\n /**\n * 把一个写临界区追加到 `writeChain` 末尾顺序执行。前一个无论成败都接着跑\n * (`then(fn, fn)`),且把链尾的错误吞掉,避免一次失败毒化整条队列。\n */\n private enqueueWrite<T>(fn: () => Promise<T>): Promise<T> {\n const run = this.writeChain.then(fn, fn)\n this.writeChain = run.then(\n () => undefined,\n () => undefined,\n )\n return run\n }\n\n /**\n * 项目层(paths 末项)未受信任时的路径——loader 据此剔除高危键;undefined = 全信任。\n * 每次 reload 调用:thunk 形态的信任判定即时反映 `/compat trust` 写入。\n * 全局做底层(paths[0]) 用户自有,始终信任、永不进此剔除。\n */\n private resolveUntrustedProjectPath(): string | undefined {\n if (this.paths.length === 0) return undefined\n const trusted =\n typeof this.projectConfigTrusted === 'function'\n ? this.projectConfigTrusted()\n : this.projectConfigTrusted\n return trusted ? undefined : this.paths[this.paths.length - 1]\n }\n\n dispose(): void {\n this.removeAllListeners()\n }\n}\n\n/**\n * 配置层路径(数组序即合并优先级,低→高;见 setting.ts `merge`)。\n *\n * 全局 `${OTTO_HOME}/config.json` 做底(低优先),项目 `.otto/config.json` 覆盖(高优先)。\n * 这样标量(model/theme…)项目级胜出,而对象表(如 `mcp_servers`)跨层深合并——\n * 用户在全局配的 server 与项目 server 自动并表,无需每个项目手抄(对齐业界参考实现的\n * User 层/userSettings 做底设计)。同路径去重(在 OTTO_HOME 内运行或 `--config` 指向全局文件时)。\n *\n * 无项目上下文(既无 workspaceDir 又无 projectConfigPath)时保持仅默认/内存语义,返回空。\n */\nfunction buildSettingPaths(options: SettingsOptions): string[] {\n let projectPath: string | undefined\n if (options.projectConfigPath) {\n projectPath = options.projectConfigPath\n } else if (options.workspaceDir) {\n projectPath = resolve(options.workspaceDir, OTTO_PROJECT_CONFIG_RELPATH)\n }\n\n if (!projectPath) {\n return []\n }\n\n const globalPath = resolve(OTTO_HOME, OTTO_CONFIG_FILENAME)\n if (resolve(projectPath) === globalPath) {\n return [projectPath]\n }\n return [globalPath, projectPath]\n}\n","import type { AgentProfile } from '@x-otto/prompt'\n\n/**\n * 预制 agent persona 已移除(2026-06-13,agent-profiles-removal 方案)。\n *\n * 子代理改为 fork/inherit + 自由任务规约(对齐 Codex spawn / Claude Code fork):\n * 命名未命中声明式 .md 时继承父级工具/模型 + 共享 lead-guidance,不再挑固定 persona。\n * 用户定制走声明式 .md(.otto/agents/*.md)——唯一显式定制路径。\n */\nexport const BUILTIN_AGENT_PROFILES: AgentProfile[] = []\n","/** Config merge strategy: layered config merge (default < project < user < override). */\nimport { HIGH_RISK_CAPABILITIES } from '@x-otto/plugin'\nimport { SETTING_KEYS } from './schema'\nimport { HIGH_RISK_PROJECT_KEYS } from './project-trust'\nimport type { Setting, SettingKey, InstalledPluginEntry } from './schema'\n\n/** 合并策略类别(RFC-219 milestone M2 §设计闸门要点订正后的接口)。 */\nexport type MergeCategory = 'scalar-override' | 'array-merge' | 'record-merge'\n\n/** 单个字段的静态分类(与具体 diff 值无关)。 */\nexport interface FieldClassification {\n category: MergeCategory\n /**\n * 恒定路由 user 级配置存储,不受目标 scope 参数影响(RFC-219 重要事项规则 5)。\n * 仅供 M3/M4 的导入落盘目标判定使用,本模块自身不做任何写入。\n */\n deviceBound: boolean\n}\n\n/**\n * 排除字段——不导出、不导入,`diffConfig` 返回结果中永不出现(RFC-219 字段策略表)。\n * `config_version`/`version` 是元数据字段(导入侧沿用当前 otto 版本自身值,不采用导入\n * 文件里的值);`last_update_check`/`_migrations` 是内部记账字段。\n */\nexport const EXCLUDED_KEYS: ReadonlySet<SettingKey> = new Set([\n 'config_version',\n 'version',\n 'last_update_check',\n '_migrations',\n])\n\n/**\n * 字段静态分类表(RFC-219 §设计·字段策略表,M2 实现期订正版:合并策略与设备绑定分离为\n * 两个独立字段)。`EXCLUDED_KEYS` 里的字段不在此表出现(完整性校验单独处理,见\n * `assertFieldClassificationCoversAllKeys`)。\n */\nexport const FIELD_CLASSIFICATION: ReadonlyMap<SettingKey, FieldClassification> = new Map<\n SettingKey,\n FieldClassification\n>([\n // 标量偏好字段——导入意图即采用,非设备绑定。\n ['model', { category: 'scalar-override', deviceBound: false }],\n ['language', { category: 'scalar-override', deviceBound: false }],\n ['nickname', { category: 'scalar-override', deviceBound: false }],\n ['has_completed_onboarding', { category: 'scalar-override', deviceBound: false }],\n ['has_completed_wizard', { category: 'scalar-override', deviceBound: false }],\n ['wizard_dont_show_again', { category: 'scalar-override', deviceBound: false }],\n ['first_prompt_submitted_at', { category: 'scalar-override', deviceBound: false }],\n ['thinking_level', { category: 'scalar-override', deviceBound: false }],\n ['tool_preset', { category: 'scalar-override', deviceBound: false }],\n ['log_level', { category: 'scalar-override', deviceBound: false }],\n ['feedback_repo', { category: 'scalar-override', deviceBound: false }],\n ['permission_mode', { category: 'scalar-override', deviceBound: false }],\n ['prompt_output_token_budget', { category: 'scalar-override', deviceBound: false }],\n ['prompt_wall_clock_budget_ms', { category: 'scalar-override', deviceBound: false }],\n ['session_cost_budget_usd', { category: 'scalar-override', deviceBound: false }],\n\n // 会话行为调优参数——整体对象替换(非深合并),无安全含义。\n ['memory_auto_extract', { category: 'scalar-override', deviceBound: false }],\n // RFC-383 D4:skill_internalization 同 memory_auto_extract 语义(行为调优参数,整体对象替换)。\n ['skill_internalization', { category: 'scalar-override', deviceBound: false }],\n // RFC-323 residency 预算(并发会话新增 schema 但漏归类,本分类策略全量门禁故\n // 需补齐——终局 review 暴露的 pre-existing 回归,与 behavior 同类)。\n ['residency', { category: 'scalar-override', deviceBound: false }],\n ['skill_loop', { category: 'scalar-override', deviceBound: false }],\n ['history', { category: 'scalar-override', deviceBound: false }],\n ['workflow', { category: 'scalar-override', deviceBound: false }],\n ['todo_continue_max', { category: 'scalar-override', deviceBound: false }],\n ['stall_detection', { category: 'scalar-override', deviceBound: false }],\n ['sandbox', { category: 'scalar-override', deviceBound: false }],\n // RFC-230:统一重试/超时/退避配置——整体对象替换(同 workflow),非设备绑定、无安全含义。\n ['resilience', { category: 'scalar-override', deviceBound: false }],\n\n // 数组并集合并字段——天然无损合并,非设备绑定。\n ['disabled_plugins', { category: 'array-merge', deviceBound: false }],\n ['model_fallback', { category: 'array-merge', deviceBound: false }],\n ['web_fetch_allowed_hosts', { category: 'array-merge', deviceBound: false }],\n ['recent_mentions', { category: 'array-merge', deviceBound: false }],\n // RFC-233:@ 快捷方式(用户自定义文本映射),同 recent_mentions 语义并集合并天然无损。\n ['shortcuts', { category: 'array-merge', deviceBound: false }],\n ['disabled_agents', { category: 'array-merge', deviceBound: false }],\n ['disabled_tools', { category: 'array-merge', deviceBound: false }],\n ['disabled_skills', { category: 'array-merge', deviceBound: false }],\n ['disabled_hooks', { category: 'array-merge', deviceBound: false }],\n ['permissions', { category: 'array-merge', deviceBound: false }],\n ['permission_always_allow', { category: 'array-merge', deviceBound: false }],\n\n // Record 按 key 合并字段——非设备绑定。\n ['agents', { category: 'record-merge', deviceBound: false }],\n ['categories', { category: 'record-merge', deviceBound: false }],\n ['model_slots', { category: 'record-merge', deviceBound: false }],\n ['mcp_servers', { category: 'record-merge', deviceBound: false }],\n\n // 设备绑定字段(RFC-219 重要事项规则 5)——恒定路由 user 级配置,不导出到 project。\n ['recent_models', { category: 'array-merge', deviceBound: true }],\n ['keybinding_overrides', { category: 'record-merge', deviceBound: true }],\n ['installed_plugins', { category: 'array-merge', deviceBound: true }],\n ['plugin_lifecycle', { category: 'scalar-override', deviceBound: true }],\n ['auto_update_check', { category: 'scalar-override', deviceBound: true }],\n ['theme', { category: 'scalar-override', deviceBound: true }],\n // RFC-277 M4:受限主题 override(目前仅 markdown typography)——视觉偏好,同 theme 语义\n // device-bound,不导出到 project、不跨设备同步(RFC-277 D9 settings merge/sync contract)。\n ['theme_overrides', { category: 'scalar-override', deviceBound: true }],\n // RFC-277 D2/M6:preset selection 双槽——同 theme/theme_overrides 语义,device-bound。\n ['theme_preset', { category: 'scalar-override', deviceBound: true }],\n ['proxy', { category: 'scalar-override', deviceBound: true }],\n ['notification', { category: 'scalar-override', deviceBound: true }],\n ['pulse_survey', { category: 'scalar-override', deviceBound: true }],\n])\n\n/**\n * 断言字段分类表覆盖全部 `SETTING_KEYS`(含 excluded 字段)。新增 schema 字段忘记归类\n * 时抛错——供单测调用,防止静默漏判(RFC-219 §风险与验证策略)。\n */\nexport function assertFieldClassificationCoversAllKeys(): void {\n const uncovered = SETTING_KEYS.filter(\n (key) => !EXCLUDED_KEYS.has(key) && !FIELD_CLASSIFICATION.has(key),\n )\n if (uncovered.length > 0) {\n throw new Error(\n `ConfigMergeStrategy 字段分类表遗漏以下 SettingKey(需在 FIELD_CLASSIFICATION 或 ` +\n `EXCLUDED_KEYS 中归类): ${uncovered.join(', ')}`,\n )\n }\n}\n\n/** 高风险配置字段——放宽方向变更触发 D4 安全门(复用 `HIGH_RISK_PROJECT_KEYS`,RFC-219 规则 8b)。 */\nconst SECURITY_SENSITIVE_KEYS: ReadonlySet<string> = new Set(HIGH_RISK_PROJECT_KEYS)\n\n/** 单字段差异。`risk` 是按本次具体 from/to 值计算的实例级结果,非字段静态属性。 */\nexport interface FieldDiff {\n key: SettingKey\n from: unknown\n to: unknown\n category: MergeCategory\n deviceBound: boolean\n risk: 'safe' | 'sensitive'\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\nfunction shallowEqual(a: unknown, b: unknown): boolean {\n if (a === b) return true\n if (Array.isArray(a) && Array.isArray(b)) {\n return a.length === b.length && a.every((v, i) => JSON.stringify(v) === JSON.stringify(b[i]))\n }\n return JSON.stringify(a) === JSON.stringify(b)\n}\n\n/**\n * `PermissionMode` 严格度排名(`@x-otto/hook-contracts` `PERMISSION_MODES` 真实词表:\n * `['bypass', 'auto', 'confirm', 'strict', 'readonly']`,非本模块设计——数值越大越严格)。\n * `bypass`(零确认)最宽松,`readonly`(只读)最严格。\n */\nconst PERMISSION_MODE_STRICTNESS: Record<string, number> = {\n bypass: 0,\n auto: 1,\n confirm: 2,\n strict: 3,\n readonly: 4,\n}\n\n/**\n * 判定字段是否为\"放宽方向\"变更(RFC-219 D4:只有放宽才触发安全门,收紧不触发)。\n * `permission_mode`:按 `PERMISSION_MODE_STRICTNESS` 排名比较,`to` 严格度低于 `from`\n * 视为放宽(未知值保守按放宽处理,宁可多问不可漏问)。\n * `sandbox`:`enabled: true → false` 视为放宽。\n * 其余 `HIGH_RISK_PROJECT_KEYS` 字段(mcp_servers/disabled_hooks/permissions/\n * permission_always_allow):新增/变更内容本身即视为敏感(不存在方向性判断,见\n * `classifyRisk` 对应分支)。\n */\nfunction isPermissionModeRelaxation(from: unknown, to: unknown): boolean {\n if (typeof to !== 'string' || from === to) return false\n const fromRank = typeof from === 'string' ? PERMISSION_MODE_STRICTNESS[from] : undefined\n const toRank = PERMISSION_MODE_STRICTNESS[to]\n // 未知值(非 PERMISSION_MODES 词表内)保守按放宽处理。\n if (fromRank === undefined || toRank === undefined) return true\n return toRank < fromRank\n}\n\nfunction isSandboxRelaxation(from: unknown, to: unknown): boolean {\n const fromEnabled = isPlainObject(from) ? from['enabled'] : undefined\n const toEnabled = isPlainObject(to) ? to['enabled'] : undefined\n return fromEnabled === true && toEnabled === false\n}\n\n/** 数组新增元素集合(`to` 中存在但 `from` 中不存在的项,按 JSON 序列化比较)。 */\nfunction addedArrayItems(from: unknown, to: unknown): unknown[] {\n const fromArr = Array.isArray(from) ? from : []\n const toArr = Array.isArray(to) ? to : []\n const fromSet = new Set(fromArr.map((v) => JSON.stringify(v)))\n return toArr.filter((v) => !fromSet.has(JSON.stringify(v)))\n}\n\n/** 按字段 key + from/to 具体值计算本次差异的安全风险等级(RFC-219 D4/规则 8a/8b)。 */\nfunction classifyRisk(key: SettingKey, from: unknown, to: unknown): 'safe' | 'sensitive' {\n if (!SECURITY_SENSITIVE_KEYS.has(key)) return 'safe'\n\n if (key === 'permission_mode') {\n return isPermissionModeRelaxation(from, to) ? 'sensitive' : 'safe'\n }\n if (key === 'sandbox') {\n return isSandboxRelaxation(from, to) ? 'sensitive' : 'safe'\n }\n if (key === 'mcp_servers') {\n // 新增/变更条目(record-merge:新 key 或同 key 值变化)均视为敏感(RFC-219 规则 8a)。\n const fromRec = isPlainObject(from) ? from : {}\n const toRec = isPlainObject(to) ? to : {}\n for (const [k, v] of Object.entries(toRec)) {\n if (!(k in fromRec) || !shallowEqual(fromRec[k], v)) return 'sensitive'\n }\n return 'safe'\n }\n if (key === 'disabled_hooks') {\n // 可禁用安全钩子——变化本身即敏感(不分方向,缺失钩子和新增钩子都可能改变安全态势)。\n return shallowEqual(from, to) ? 'safe' : 'sensitive'\n }\n if (key === 'permissions' || key === 'permission_always_allow') {\n // 仅新增预授权条目时敏感;纯移除/缩减不触发(RFC-219 规则 8b)。\n return addedArrayItems(from, to).length > 0 ? 'sensitive' : 'safe'\n }\n // 对抗审核 F2 修复(fail-closed):SECURITY_SENSITIVE_KEYS(HIGH_RISK_PROJECT_KEYS)\n // 六个成员已在上方逐一显式处理——若未来 `HIGH_RISK_PROJECT_KEYS` 新增第七个成员而本\n // 函数忘记同步处理,不能静默 fail-open 落到 'safe'(等于安全门对新增高危字段失效)。\n // 显式抛错逼停调用方,而非放行一个未经判断的字段。\n throw new Error(\n `classifyRisk: 字段 \"${key}\" 在 HIGH_RISK_PROJECT_KEYS 中但未被显式处理——` +\n `新增高危字段必须同步补充判定分支,不允许 fail-open`,\n )\n}\n\n/**\n * 计算当前配置与导入配置之间的差异(RFC-219 M2 核心函数)。\n *\n * 排除字段(`EXCLUDED_KEYS`)与值未变化的字段均不出现在返回数组中——只输出真实差异。\n * `installed_plugins` 字段不在此函数处理(插件差异走独立的 `classifyPluginChanges`,\n * 因为它需要\"新增/本地独有/版本不一致\"三分类而非简单的 from/to 覆盖,见下)。\n */\nexport function diffConfig(current: Setting, incoming: Partial<Setting>): FieldDiff[] {\n const diffs: FieldDiff[] = []\n for (const key of SETTING_KEYS) {\n if (EXCLUDED_KEYS.has(key)) continue\n if (key === 'installed_plugins') continue // 走 classifyPluginChanges\n if (!(key in incoming)) continue\n\n const from = current[key]\n const to = incoming[key]\n if (shallowEqual(from, to)) continue\n\n const classification = FIELD_CLASSIFICATION.get(key)\n if (!classification) continue // 理论上不可达(assertFieldClassificationCoversAllKeys 已钉死)\n\n diffs.push({\n key,\n from,\n to,\n category: classification.category,\n deviceBound: classification.deviceBound,\n risk: classifyRisk(key, from, to),\n })\n }\n return diffs\n}\n\n/** 插件清单差异分类(RFC-219 §设计·字段策略表 `installed_plugins` 行)。 */\nexport interface PluginDiff {\n /** 导入清单中有、本地未安装——需自动安装。 */\n toInstall: InstalledPluginEntry[]\n /** 本地已安装、导入清单中无——不受影响,不卸载。 */\n localOnly: InstalledPluginEntry[]\n /** 两边都有但版本不同。 */\n versionMismatch: Array<{ id: string; local: string; incoming: string }>\n}\n\n/**\n * 比对本地与导入的 `installed_plugins` 清单,产出三分类差异。\n *\n * 不做任何写入/安装动作——纯计算,供 M3/M4 的执行层驱动实际安装。\n */\nexport function classifyPluginChanges(\n current: readonly InstalledPluginEntry[],\n incoming: readonly InstalledPluginEntry[],\n): PluginDiff {\n const currentById = new Map(current.map((e) => [e.id, e]))\n const incomingById = new Map(incoming.map((e) => [e.id, e]))\n\n const toInstall: InstalledPluginEntry[] = []\n const versionMismatch: PluginDiff['versionMismatch'] = []\n for (const entry of incoming) {\n const local = currentById.get(entry.id)\n if (!local) {\n toInstall.push(entry)\n continue\n }\n if (entry.version !== undefined && local.version !== undefined && entry.version !== local.version) {\n versionMismatch.push({ id: entry.id, local: local.version, incoming: entry.version })\n }\n }\n\n const localOnly = current.filter((e) => !incomingById.has(e.id))\n\n return { toInstall, localOnly, versionMismatch }\n}\n\n/**\n * 判定本次导入是否包含安全敏感变更(RFC-219 D4 安全门触发条件)。\n *\n * 触发条件(任一即触发):\n * - `diffs` 中存在 `risk === 'sensitive'` 的字段差异。\n * - `pluginDiff.toInstall` 中存在声明 `HIGH_RISK_CAPABILITIES` 的新装插件。\n *\n * `capabilities` 快照字段(RFC-219 D1c)由安装路径调用 `readHighRiskCapabilities()`\n * 计算写入——该函数内部已按 `HIGH_RISK_CAPABILITIES` 过滤,理论上快照字段是其真子集。\n * 但本函数**不依赖这条隐式前提**(对抗审核 F1:不应仅凭\"上游已过滤\"就当作不变量)——\n * 显式 `import { HIGH_RISK_CAPABILITIES } from '@x-otto/plugin'` 权威常量重新交叉核对,\n * 即使未来 `capabilities` 快照字段的写入路径变化(如导出文件被手工篡改塞入非高危\n * 能力字符串),本函数依然只对真正命中 `HIGH_RISK_CAPABILITIES` 的能力判定为敏感。\n */\nexport function hasSecuritySensitiveChanges(diffs: readonly FieldDiff[], pluginDiff: PluginDiff): boolean {\n if (diffs.some((d) => d.risk === 'sensitive')) return true\n return pluginDiff.toInstall.some((p) =>\n (p.capabilities ?? []).some((c) => (HIGH_RISK_CAPABILITIES as readonly string[]).includes(c)),\n )\n}\n\n/**\n * 按接受策略把差异应用为可写入配置的 partial 对象。\n *\n * `'all'`:全部差异按各自 category 合并策略应用。\n * `'safe-only'`:跳过 `risk === 'sensitive'` 的字段(用户在安全门选择\"跳过安全项\"时使用)。\n *\n * 合并语义(`diff.from`/`diff.to` 已由 `diffConfig` 计算好,无需重新传入 current 配置):\n * - `scalar-override`:直接取 `to` 值整体替换。\n * - `array-merge`:`from` 与 `to` 数组按 JSON 序列化去重并集。\n * - `record-merge`:`from` 与 `to` 对象按 key 浅合并(同 key 以 `to` 值覆盖)。\n *\n * 实现期简化(milestone 设计稿原签名含 `current: Setting` 参数):`diff.from` 已是\n * `current[diff.key]`(由 `diffConfig` 计算),重复传入 `current` 是冗余参数,故本函数\n * 签名去掉该参数,只需 `diffs` + `accept` 两个入参即可完整还原合并结果。\n */\nexport function applyMergeStrategy(\n diffs: readonly FieldDiff[],\n accept: 'all' | 'safe-only',\n): Partial<Setting> {\n const result: Partial<Record<SettingKey, unknown>> = {}\n for (const diff of diffs) {\n if (accept === 'safe-only' && diff.risk === 'sensitive') continue\n\n if (diff.category === 'array-merge') {\n const fromArr = Array.isArray(diff.from) ? diff.from : []\n const toArr = Array.isArray(diff.to) ? diff.to : []\n const seen = new Set(fromArr.map((v) => JSON.stringify(v)))\n const merged = [...fromArr]\n for (const item of toArr) {\n const serialized = JSON.stringify(item)\n if (!seen.has(serialized)) {\n seen.add(serialized)\n merged.push(item)\n }\n }\n result[diff.key] = merged\n } else if (diff.category === 'record-merge') {\n const fromRec = isPlainObject(diff.from) ? diff.from : {}\n const toRec = isPlainObject(diff.to) ? diff.to : {}\n result[diff.key] = { ...fromRec, ...toRec }\n } else {\n result[diff.key] = diff.to\n }\n }\n return result as Partial<Setting>\n}\n","/**\n * config-redact.ts —— RFC review D13:配置导出结构化脱敏(DSH redact.ts 移植)。\n *\n * `mcp_servers.<id>.env` 与 `mcp_servers.<id>.headers` 可承载第三方服务 token\n * (API key / Authorization header),导出到可分享文件前必须剥离。语义对齐 DSH:\n * - 脱敏值:敏感字段从产物中移除(保留结构位置);\n * - sidecar:记录每个被剥离的位置(path + 是否曾持有值)——导出界面可显示\n * 「3 个凭据已脱敏」,导入侧知道哪些槽位缺失。\n *\n * 纯函数、零 IO。\n */\n\n/** 被剥离的敏感位置记录(DSH RedactedSecret 同构)。 */\nexport interface RedactedSecret {\n /** 从配置根到被剥离字段的路径(含具体字典键,如 ['mcp_servers','my-server','env'])。 */\n path: string[]\n /** 该字段在脱敏前是否持有值(未持有也记录——导入侧知道槽位存在)。 */\n set: boolean\n}\n\n/** 脱敏产物:剥离后的配置 + sidecar 清单。 */\nexport interface RedactedSetting {\n value: unknown\n secrets: RedactedSecret[]\n}\n\n/** 敏感子字段白名单(在 mcp_servers 条目下)。 */\nconst SENSITIVE_MCP_SUBFIELDS = new Set(['env', 'headers'])\n\nfunction isRecord(v: unknown): v is Record<string, unknown> {\n return typeof v === 'object' && v !== null && !Array.isArray(v)\n}\n\n/**\n * 深拷贝并剥离敏感字段;sidecar 记录每个位置(path + set 状态)。\n *\n * @param value 待脱敏的配置(任意深度的 plain data)。\n */\nexport function redactSetting(value: unknown): RedactedSetting {\n const secrets: RedactedSecret[] = []\n const walk = (node: unknown, path: string[]): unknown => {\n if (!isRecord(node)) return node\n\n const out: Record<string, unknown> = {}\n for (const [key, val] of Object.entries(node)) {\n const childPath = [...path, key]\n const isSensitive = path.length === 2 && path[0] === 'mcp_servers' && SENSITIVE_MCP_SUBFIELDS.has(key)\n if (isSensitive) {\n // 剥离:字段完全不进入产物(DSH「removed before wire boundary」语义)\n secrets.push({ path: childPath, set: val !== undefined })\n continue\n }\n out[key] = walk(val, childPath)\n }\n return out\n }\n\n return { value: walk(value, []), secrets }\n}\n","/**\n * config-export-format.ts —— 配置导出/导入的可移植文件格式(RFC-219 M3)。\n *\n * 纯函数、零 I/O——不碰文件系统。真正的文件读写在 CLI 层\n * (`packages/cli/src/commands/config-migration.ts`)。本模块只负责:\n * ① 从当前配置构建导出产物(应用字段白名单,剔除设备绑定的绝对路径);\n * ② 解析/校验导入文件的结构与版本。\n */\nimport { isAbsolute } from 'node:path'\nimport { EXCLUDED_KEYS } from './config-merge-strategy'\nimport { redactSetting } from './config-redact'\nimport type { Setting, SettingKey, InstalledPluginEntry } from './schema'\n\n/** 导出文件格式版本。结构变化时递增,`parseExportFile` 按此版本做兼容性判断。 */\nexport const CURRENT_EXPORT_VERSION = 1\n\nexport interface ConfigExportFile {\n otto_config_version: number\n exported_at: string\n /** 导出机器标识(`os.hostname()`),仅供人工审阅来源,不参与任何逻辑判断。 */\n exported_from: string\n /** 导出侧的配置 scope('global' = 用户级 `~/.otto/config.json`,'project' = 项目级)。 */\n scope: 'global' | 'project'\n fields: Partial<Setting>\n /**\n * RFC review D13:脱敏 sidecar(可选,向后兼容)——`mcp_servers.*.env/headers`\n * 在导出时被剥离,此处记录位置与「是否曾持有值」。缺省(旧导出产物)无此字段。\n */\n redacted_secrets?: Array<{ path: string[]; set: boolean }>\n}\n\n/**\n * 从当前配置构建导出产物(RFC-219 重要事项规则 9)。\n *\n * 应用规则:\n * - `EXCLUDED_KEYS`(`config_version`/`version`/`last_update_check`/`_migrations`)不导出。\n * - `installed_plugins[].sourceSpec` 若为绝对路径,标准化为空字符串(导入侧走 npm/\n * workspace glob 解析重新定位,见 `resolvePackageDir` 既有机制)——防止把导出机器的\n * 本地路径写入可分享产物。\n * - `undefined` 值的字段不写入(保持导出产物精简,不必要地标注\"这个字段不存在\")。\n */\nexport function buildExportPayload(\n config: Setting,\n scope: 'global' | 'project',\n hostname: string,\n now: () => string = () => new Date().toISOString(),\n): ConfigExportFile {\n const fields: Partial<Record<SettingKey, unknown>> = {}\n for (const [key, value] of Object.entries(config) as Array<[SettingKey, unknown]>) {\n if (EXCLUDED_KEYS.has(key)) continue\n if (value === undefined) continue\n\n if (key === 'installed_plugins') {\n const entries = value as InstalledPluginEntry[]\n fields[key] = entries.map((entry) =>\n entry.sourceSpec && isAbsolute(entry.sourceSpec) ? { ...entry, sourceSpec: '' } : entry,\n )\n continue\n }\n fields[key] = value\n }\n\n // RFC review D13:结构化脱敏——mcp_servers 条目的 env/headers 剥离(token/Authorization\n // 不跨 wire 边界),sidecar 记录位置。无敏感字段时 sidecar 为空数组(产物仍含该字段,\n // 向后兼容 parseExportFile 的缺省容错)。\n const redacted = redactSetting(fields)\n const redactedFields = redacted.value as Partial<Setting>\n\n return {\n otto_config_version: CURRENT_EXPORT_VERSION,\n exported_at: now(),\n exported_from: hostname,\n scope,\n fields: redactedFields,\n ...(redacted.secrets.length > 0\n ? { redacted_secrets: redacted.secrets }\n : { redacted_secrets: [] }),\n }\n}\n\nexport class ExportFileVersionError extends Error {\n constructor(\n public readonly fileVersion: number,\n public readonly supportedVersion: number,\n ) {\n super(\n `导入文件 otto_config_version=${fileVersion},当前 otto 支持=${supportedVersion}。` +\n `请升级 otto 到最新版本后再试。`,\n )\n this.name = 'ExportFileVersionError'\n }\n}\n\nexport class ExportFileParseError extends Error {\n constructor(reason: string) {\n super(`导入文件格式非法:${reason}`)\n this.name = 'ExportFileParseError'\n }\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\n/**\n * 解析并校验导入文件结构。版本不兼容或结构非法时抛出对应错误类型(调用方据此渲染\n * 不同的提示文案,见 RFC-219 §重要事项规则 3 附近的错误处理设计)。\n */\nexport function parseExportFile(raw: string): ConfigExportFile {\n let parsed: unknown\n try {\n parsed = JSON.parse(raw)\n } catch (err) {\n throw new ExportFileParseError(err instanceof Error ? err.message : String(err))\n }\n if (!isPlainObject(parsed)) {\n throw new ExportFileParseError('顶层结构必须是 JSON 对象')\n }\n const { otto_config_version, exported_at, exported_from, scope, fields } = parsed\n if (typeof otto_config_version !== 'number') {\n throw new ExportFileParseError('缺少或非法的 otto_config_version 字段')\n }\n if (otto_config_version !== CURRENT_EXPORT_VERSION) {\n throw new ExportFileVersionError(otto_config_version, CURRENT_EXPORT_VERSION)\n }\n if (typeof exported_at !== 'string' || typeof exported_from !== 'string') {\n throw new ExportFileParseError('缺少或非法的 exported_at/exported_from 字段')\n }\n if (scope !== 'global' && scope !== 'project') {\n throw new ExportFileParseError('scope 字段必须是 \"global\" 或 \"project\"')\n }\n if (!isPlainObject(fields)) {\n throw new ExportFileParseError('fields 字段必须是 JSON 对象')\n }\n return {\n otto_config_version,\n exported_at,\n exported_from,\n scope,\n fields: fields as Partial<Setting>,\n // RFC review D13:sidecar 透传(旧产物无此字段 → undefined,向后兼容)。\n ...(Array.isArray(parsed['redacted_secrets'])\n ? { redacted_secrets: parsed['redacted_secrets'] as ConfigExportFile['redacted_secrets'] }\n : {}),\n }\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@x-otto/setting",
3
- "version": "0.0.1-alpha.2",
3
+ "version": "0.0.1-alpha.4",
4
4
  "files": [
5
5
  "dist"
6
6
  ],
@@ -21,13 +21,13 @@
21
21
  "dependencies": {
22
22
  "semver": "7.7.4",
23
23
  "zod": "4.3.6",
24
- "@x-otto/env": "0.1.0-alpha.5",
25
- "@x-otto/hook-contracts": "0.0.1-alpha.1",
26
- "@x-otto/orchestration-contracts": "0.0.1-alpha.1",
27
- "@x-otto/persistence": "0.0.1-alpha.2",
28
- "@x-otto/plugin": "0.1.0-alpha.3",
29
- "@x-otto/prompt": "0.0.1-alpha.1",
30
- "@x-otto/shared": "0.1.0-alpha.5"
24
+ "@x-otto/persistence": "0.0.1-alpha.4",
25
+ "@x-otto/hook-contracts": "0.0.1-alpha.3",
26
+ "@x-otto/plugin": "0.1.0-alpha.6",
27
+ "@x-otto/orchestration-contracts": "0.0.1-alpha.2",
28
+ "@x-otto/prompt": "0.0.1-alpha.4",
29
+ "@x-otto/shared": "0.1.0-alpha.6",
30
+ "@x-otto/env": "0.1.0-alpha.6"
31
31
  },
32
32
  "devDependencies": {
33
33
  "@types/semver": "7.7.1"