@x-otto/setting 0.1.0-alpha.13 → 0.1.0-alpha.15

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/dist/index.d.ts CHANGED
@@ -1428,17 +1428,6 @@ declare const SettingSchema: z.ZodObject<{
1428
1428
  similarity_threshold: z.ZodCatch<z.ZodOptional<z.ZodNumber>>;
1429
1429
  min_relevant_hits: z.ZodCatch<z.ZodOptional<z.ZodNumber>>;
1430
1430
  }, z.core.$strip>>>;
1431
- skill_loop: z.ZodCatch<z.ZodOptional<z.ZodObject<{
1432
- enabled: z.ZodCatch<z.ZodOptional<z.ZodBoolean>>;
1433
- min_messages: z.ZodCatch<z.ZodOptional<z.ZodNumber>>;
1434
- dismiss_cooldown_days: z.ZodCatch<z.ZodOptional<z.ZodNumber>>;
1435
- }, z.core.$strip>>>;
1436
- skill_internalization: z.ZodCatch<z.ZodOptional<z.ZodObject<{
1437
- enabled: z.ZodCatch<z.ZodOptional<z.ZodBoolean>>;
1438
- min_executions: z.ZodCatch<z.ZodOptional<z.ZodNumber>>;
1439
- min_success_rate: z.ZodCatch<z.ZodOptional<z.ZodNumber>>;
1440
- max_internalized: z.ZodCatch<z.ZodOptional<z.ZodNumber>>;
1441
- }, z.core.$strip>>>;
1442
1431
  notification: z.ZodCatch<z.ZodOptional<z.ZodObject<{
1443
1432
  enabled: z.ZodCatch<z.ZodOptional<z.ZodBoolean>>;
1444
1433
  sound: z.ZodCatch<z.ZodOptional<z.ZodBoolean>>;
@@ -1686,7 +1675,7 @@ type SandboxConfig = z.infer<typeof sandboxConfigSchema>;
1686
1675
  type ProxyConfig = z.infer<typeof proxyConfigSchema>;
1687
1676
  type McpServerSetting = z.infer<typeof mcpServerSettingSchema>;
1688
1677
  type McpServersConfig = z.infer<typeof SettingSchema>['mcp_servers'];
1689
- 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" | "feedback_platform" | "feedback_coding_base_url" | "feedback_coding_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" | "provider_overrides" | "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")[];
1678
+ 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" | "feedback_platform" | "feedback_coding_base_url" | "feedback_coding_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" | "notification" | "pulse_survey" | "history" | "workflow" | "model_slots" | "provider_overrides" | "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")[];
1690
1679
  type SettingKey = keyof Setting;
1691
1680
  //#endregion
1692
1681
  //#region src/types.d.ts
@@ -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/field-meta.ts","../src/remote-sync-keys.ts","../src/settings-manager.ts","../src/claude-settings-adapter.ts","../src/project-trust.ts","../src/project-trust-gate.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/setting.ts","../src/raw-file-store.ts","../src/remote-setting-store.ts","../src/field-meta.ts","../src/remote-sync-keys.ts","../src/settings-manager.ts","../src/claude-settings-adapter.ts","../src/project-trust.ts","../src/project-trust-gate.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 f,OTTO_PROJECT_CONFIG_RELPATH as p,isClaudeSettingsCompatEnabled as ee,isShadowModeEnabled as te}from"@x-otto/env";import{isAbsolute as ne,join as m,resolve as h}from"node:path";import{homedir as g}from"node:os";import{existsSync as _,readFileSync as v}from"node:fs";import{HIGH_RISK_CAPABILITIES as re,isPathTrusted as ie,trustPath as ae,untrustPath as oe}from"@x-otto/plugin";const y=[`minimal`,`standard`,`full`],b=[`trace`,`debug`,`info`,`warn`,`error`,`fatal`],x=e=>e.optional().catch(void 0),S=t.enum(n),se=t.object({model:x(t.string()),description:x(t.string()),system_prompt:x(t.string()),tools:x(t.array(t.string())),capabilities:x(t.array(t.string())),categories:x(t.array(t.string())),default_workflow_slot:x(S),max_tool_turns:x(t.number()),max_tool_turn_extensions:x(t.number()),prompt_output_token_budget:x(t.number()),prompt_wall_clock_budget_ms:x(t.number()),session_cost_budget_usd:x(t.number()),stall_detection:x(t.union([t.literal(!1),t.object({window_turns:x(t.number()),repeat_threshold:x(t.number())})])),temperature:x(t.number()),max_tokens:x(t.number()),disabled:x(t.boolean())}),ce=t.object({preferred_models:x(t.array(t.string())),default_model:x(t.string())}),le=t.strictObject({baseUrl:t.string().min(1)}),ue=t.record(t.string().min(1),le),de=t.object({enabled:x(t.boolean()),every_n_turns:x(t.number()),max_lessons:x(t.number()),similarity_threshold:x(t.number()),min_relevant_hits:x(t.number())}),fe=t.object({enabled:x(t.boolean()),min_messages:x(t.number()),dismiss_cooldown_days:x(t.number())}),pe=t.object({enabled:x(t.boolean()),min_executions:x(t.number()),min_success_rate:x(t.number()),max_internalized:x(t.number())}),me=t.object({slots:x(t.partialRecord(S,t.union([t.string(),t.array(t.string())]))),default:x(t.string()),subagent_default_slot:x(S)}),he=t.object({enabled:x(t.boolean()),sound:x(t.boolean()),on_completion:x(t.boolean()),on_error:x(t.boolean()),on_idle:x(t.boolean()),channel:x(t.enum([`auto`,`terminal_bell`,`iterm2`,`iterm2_with_bell`,`kitty`,`ghostty`,`disabled`])),condition:x(t.enum([`unfocused`,`always`])),idle_threshold_ms:x(t.number()),command:x(t.array(t.string()))}),ge=t.object({enabled:x(t.boolean()),probability:x(t.number()),min_turn_gap:x(t.number()),sink:x(t.string())}),_e=t.object({id:x(t.string()),path:x(t.string()),enabled:x(t.boolean())}),ve=t.object({enabled:x(t.boolean()),maxEntries:x(t.number()),sources:x(t.array(_e))}),ye=t.object({external:x(ve)}),be=t.object({enabled:t.boolean(),network:x(t.enum([`allow`,`deny`])),writablePaths:x(t.array(t.string())),protectCredentials:x(t.boolean()),autoAllowBashIfSandboxed:x(t.boolean()),semanticReview:x(t.boolean())}),xe=t.object({total_bytes:x(t.number()),min_per_session_bytes:x(t.number()),max_per_session_bytes:x(t.number()),max_history_messages:x(t.number()),resume_window_messages:x(t.number()),max_content_bytes:x(t.number()),warn_threshold_pct:x(t.number()),hard_threshold_pct:x(t.number()),compaction_timeout_ms:x(t.number()),coverage_warn_threshold:x(t.number()),coverage_error_threshold:x(t.number()),coverage_floor_threshold:x(t.number())});t.object({tool_preset:x(t.enum(y)),disabled_tools:x(t.array(t.string()))});const Se=t.object({enabled:x(t.boolean())}),Ce=t.object({enabled:x(t.boolean()),maxAttempts:x(t.number().int().min(1)),backoffMs:x(t.number().min(0)),backoffMultiplier:x(t.number().min(0))}),we=t.object({enabled:x(t.boolean()),failureThreshold:x(t.number().int().min(1)),timeoutMs:x(t.number().min(0))}),Te=t.object({enabled:x(t.boolean())}),Ee=t.object({maxActiveTasks:x(t.number().int().min(1)),maxStoredTasks:x(t.number().int().min(1)),maxDelegationDepth:x(t.number().int().min(0)),maxFanOutPerParent:x(t.number().int().min(1))}),De=t.object({enabled:x(t.boolean()),review:x(Se),retry:x(Ce),circuitBreaker:x(we),routing:x(Te),orchestration:x(Ee)}),Oe=t.object({name:t.string(),effect:t.enum([`allow`,`deny`,`ask`]),tools:x(t.array(t.string())),paths:x(t.array(t.string())),commands:x(t.array(t.string())),deny_reason:x(t.string()),ask_prompt:x(t.string())}),ke=t.object({name:t.string(),priority:x(t.number()),enabled:x(t.boolean()),scope:x(t.object({agents:x(t.array(t.string())),sessions:x(t.array(t.string()))})),rules:t.array(Oe)}),Ae=t.object({url:x(t.string()),enabled:x(t.boolean())}),je=t.object({command:x(t.string()),args:x(t.array(t.string())),env:x(t.record(t.string(),t.string())),url:x(t.string()),type:x(t.enum([`stdio`,`sse`,`http`])),headers:x(t.record(t.string(),t.string())),requestTimeoutMs:x(t.number()),autoReconnect:x(t.boolean()),disabled:x(t.boolean())}),Me=t.object({maxRetries:x(t.number().int().positive()),initialDelayMs:x(t.number().positive()),backoffFactor:x(t.number().positive()),maxDelayMs:x(t.number().positive())}),Ne=t.object({maxRetries:x(t.number().int().positive()),intervalMs:x(t.number().positive())}),Pe=t.object({maxReconnectAttempts:x(t.number().int().nonnegative()),reconnectDelayMs:x(t.number().positive()),requestTimeoutMs:x(t.number().positive())}),Fe=t.object({retryBackoffMs:x(t.number().nonnegative())}),Ie=t.object({maxAttempts:x(t.number().int().positive()),intervalMs:x(t.number().positive())}),Le=t.object({refreshLockTimeoutMs:x(t.number().min(1e3))}),C=t.object({stream:x(Me),networkDisconnect:x(Ne),mcp:x(Pe),schedule:x(Fe),errorRetry:x(Ie),oauth:x(Le)});function w(e){if(typeof e!=`object`||!e||Array.isArray(e))return;let t=C.parse(e),n={};for(let[e,r]of Object.entries(t)){if(!r||typeof r!=`object`)continue;let t={};for(let[e,n]of Object.entries(r))typeof n==`number`&&(t[e]=n);Object.keys(t).length>0&&(n[e]=t)}return n}function Re(e,t=e=>console.warn(`[resilience-config] ${e}`)){if(!e)return;let n;try{n=JSON.parse(e)}catch(e){t(`OTTO_RESILIENCE_CONFIG is not valid JSON, ignoring entirely: ${e instanceof Error?e.message:String(e)}`);return}return w(n)}const ze=t.object({id:t.string(),source:t.enum([`npm`,`dir`,`url`,`git`]),sourceSpec:x(t.string()),installedAt:t.number(),version:x(t.string()),scope:t.enum([`global`,`project`,`repository`]),capabilities:x(t.array(t.string()))}),Be=t.object({label:t.string(),value:t.string(),kind:t.enum([`hashtag`,`file`,`person`,`agent`,`skill`,`mcp`,`plugin`,`model`,`resource`,`shortcut`,`effort`]),description:x(t.string())}),Ve=t.object({name:t.string().min(1).max(64),text:t.string().min(1).max(4e3)}),T=`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(`.`),E=t.enum(T),He=t.strictObject({heading1:x(E),heading2:x(E),headingWeak:x(E),listMarker:x(E),listMarkerMuted:x(E),quoteBar:x(E),quoteText:x(E),link:x(E),inlineCode:x(E),codeFence:x(E),tableHeader:x(E),tableDivider:x(E),listIndent:x(t.union([t.literal(2),t.literal(3)])),listMarkers:x(t.tuple([t.string().min(1),t.string().min(1),t.string().min(1),t.string().min(1)])),codeBlockDivider:x(t.enum([`hr`,`none`]))}),Ue=t.strictObject({markdown:x(He)}),We=t.strictObject({dark:x(t.string().min(1).max(128)),light:x(t.string().min(1).max(128))}),D=t.object({config_version:x(t.string()),version:x(t.string()),log_level:x(t.enum(b)),model:x(t.string()),model_fallback:x(t.array(t.string())),recent_models:x(t.array(t.string())),recent_mentions:x(t.array(Be)),shortcuts:x(t.array(Ve)),feedback_repo:x(t.string()),feedback_platform:x(t.enum([`auto`,`github`,`coding`])),feedback_coding_base_url:x(t.string()),feedback_coding_repo:x(t.string()),keybinding_overrides:x(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:x(t.string()),theme:x(t.enum([`dark`,`light`,`system`])),theme_overrides:x(Ue),theme_preset:x(We),nickname:x(t.string().max(64)),has_completed_onboarding:x(t.boolean()),has_completed_wizard:x(t.boolean()),wizard_dont_show_again:x(t.boolean()),first_prompt_submitted_at:x(t.number()),thinking_level:x(t.string()),agents:x(t.record(t.string(),se)),categories:x(t.record(t.string(),ce)),tool_preset:x(t.enum(y)),memory_auto_extract:x(de),skill_loop:x(fe),skill_internalization:x(pe),notification:x(he),pulse_survey:x(ge),history:x(ye),workflow:x(De),model_slots:x(me),provider_overrides:x(ue),mcp_servers:x(t.record(t.string(),je)),resilience:x(C),sandbox:x(be),residency:x(xe),disabled_agents:x(t.array(t.string())),disabled_hooks:x(t.array(t.string())),disabled_tools:x(t.array(t.string())),disabled_skills:x(t.array(t.string())),disabled_plugins:x(t.array(t.string())),installed_plugins:x(t.array(ze)),auto_update_check:x(t.boolean()),last_update_check:x(t.number()),prompt_output_token_budget:x(t.number()),prompt_wall_clock_budget_ms:x(t.number()),session_cost_budget_usd:x(t.number()),stall_detection:x(t.union([t.literal(!1),t.object({window_turns:x(t.number()),repeat_threshold:x(t.number())})])),todo_continue_max:x(t.number()),permission_mode:x(t.enum(e)),permissions:x(t.array(ke)),permission_always_allow:x(t.array(t.string())),plugin_lifecycle:x(t.object({setupDone:x(t.array(t.object({id:t.string(),version:x(t.string())})))})),proxy:x(Ae),web_fetch_allowed_hosts:x(t.array(t.string())),_migrations:x(t.array(t.string()))}),Ge=T,O=D.keyof().options,k={"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`}},A={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:{...k},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},version:void 0,model_fallback:[],feedback_repo:void 0,feedback_platform:`auto`,feedback_coding_base_url:void 0,feedback_coding_repo:void 0,keybinding_overrides:{},theme_overrides:void 0,theme_preset:{dark:void 0,light:void 0},memory_auto_extract:{enabled:!1},skill_loop:{enabled:!1},skill_internalization:{enabled:!1},pulse_survey:{enabled:!0,probability:.05,min_turn_gap:20,sink:`local`},history:{external:{enabled:!1,maxEntries:1e3,sources:[]}},model_slots:{slots:void 0,default:void 0,subagent_default_slot:void 0},provider_overrides:{},mcp_servers:{},resilience:{},sandbox:{enabled:!0},residency:{},prompt_output_token_budget:void 0,prompt_wall_clock_budget_ms:void 0,session_cost_budget_usd:void 0,stall_detection:void 0,todo_continue_max:void 0,plugin_lifecycle:{setupDone:[]},proxy:{url:void 0,enabled:void 0},_migrations:[]},Ke=a(`@x-otto/setting:migrator`);function j(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}`),Ke.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 M=[`permission_mode`,`permissions`,`permission_always_allow`,`mcp_servers`,`disabled_hooks`,`sandbox`,`provider_overrides`];function N(e){let t={...e},n=[];for(let e of M)e in t&&(delete t[e],n.push(e));return{value:t,stripped:n}}const P=a(`@x-otto/setting`),qe=new Set(b),Je=[{key:`mcp`,message:`Removed legacy config key is ignored`},{key:`skills`,message:`Removed legacy config key is ignored`},{key:`disabled_mcps`,message:`Removed legacy config key is ignored`},{key:`disabled_skills`,message:`Removed legacy config key is ignored`},{key:`theme_file`,message:`RFC-205 legacy theme override key is ignored`},{key:`theme_spacing`,message:`RFC-205 legacy theme override key is ignored`},{key:`theme_border`,message:`RFC-205 legacy theme override key is ignored`},{key:`theme_figures`,message:`RFC-205 legacy theme override key is ignored`}],Ye=new Set([`accent`,`accentDim`,`accentBright`,`colors`,`spacing`,`border`,`figures`,`text`,`inactive`,`secondary`,`success`,`error`,`warning`,`special`]);function F(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function I(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}F(a)&&F(e)&&I(a,e,[...n,i],r)}}function L(e,t){let n={...e};for(let[e,r]of Object.entries(t))r!==void 0&&(F(r)&&F(n[e])?n[e]=L(n[e],r):n[e]=r);return n}function Xe(e,t,n){let r=new Map;for(let i of[...e,...t])F(i)&&typeof i[n]==`string`&&r.set(i[n],i);return[...r.values()]}function R(...e){let[t,n,...r]=e,i={...t};if(n){for(let[e,t]of Object.entries(n))if(t!==void 0)if(e===`permissions`&&Array.isArray(i[e])&&Array.isArray(t))i[e]=Xe(i[e],t,`name`);else if(Array.isArray(i[e])&&Array.isArray(t)){let n=i[e],r=t;i[e]=[...new Set([...n,...r])]}else F(t)&&F(i[e])?i[e]=L(i[e],t):i[e]=t}return r.length>0?R(i,...r):i}function Ze(){let e={},t=process.env.OTTO_MODEL;t&&(e.model=t);let n=process.env.OTTO_LOG_LEVEL;return n&&qe.has(n)&&(e.log_level=n),e}function Qe(e){let t={...e},n=[];for(let{key:e,message:r}of Je)t[e]!==void 0&&(n.push({key:e,message:r}),delete t[e]);if(F(t.theme_overrides)){let e=Object.keys(t.theme_overrides).filter(e=>Ye.has(e));e.length>0&&n.push({key:`theme_overrides`,message:`RFC-205 legacy theme_overrides shape (keys: ${e.join(`, `)}) is ignored — use theme_overrides.markdown instead`})}return{cleaned:t,warnings:n}}function $e(e){let t=D.loose().safeParse(e),n=t.success?t.data:e,r={};for(let[e,t]of Object.entries(n))t!==void 0&&(r[e]=t);return r}function et(e,t){let n=[];for(let r of O){let i=e[r],a=t[r];if(i!==void 0&&a===void 0){n.push({key:r,message:`Invalid value for "${r}" ignored`});continue}F(i)&&F(a)&&I(i,a,[r],n)}return n}function tt(e){let{cleaned:t,warnings:n}=Qe(e),r=$e(t),i=et(t,r);return{value:r,warnings:[...n,...i]}}async function nt(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=N(e);e=t.value,i.push(...t.stripped)}r.push(e),P.debug({path:a},`Config loaded from store`)}}catch(e){P.warn({path:a,err:e},`Failed to parse config file, skipping`)}return{layers:r,stripped:i}}var z=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 nt(t,n,i):{layers:[],stripped:[]};for(let e of o)P.warn({key:e,path:i},`未受信任项目配置剔除高危键(permission_mode/mcp_servers 等需先信任该目录)`);let s=Ze(),{config:c,applied:l}=j(R(A,...r,...a,s));l.length>0&&P.info({applied:l},`Config migrations applied`);let{value:u,warnings:d}=tt(c);for(let e of d)P.warn({key:e.key,message:e.message},`Config validation issue`);return{...A,...u}}};async function B(e={}){return new z(e.store).load(e)}var V=class{type=`file`;async read(e){let{readFile:t}=await import(`node:fs/promises`);try{return await t(await H(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 H(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 H(e)),!0}catch{return!1}}};async function H(e){if(!e.startsWith(`~/`))return e;let{homedir:t}=await import(`node:os`);return`${t()}${e.slice(1)}`}const U=(e={})=>({merge:`scalar-override`,deviceBound:!1,export:`include`,sync:`local-only`,...e}),W={config_version:U({export:`exclude`,sync:`never-synced`}),version:U({export:`exclude`,sync:`never-synced`}),last_update_check:U({export:`exclude`,sync:`never-synced`}),_migrations:U({export:`exclude`,sync:`never-synced`}),model:U({sync:`syncable`}),model_fallback:U({merge:`array-merge`,sync:`syncable`}),recent_models:U({merge:`array-merge`,deviceBound:!0,sync:`never-synced`}),recent_mentions:U({merge:`array-merge`,sync:`never-synced`}),shortcuts:U({merge:`array-merge`,sync:`never-synced`}),feedback_repo:U({sync:`local-only`}),feedback_platform:U({sync:`local-only`}),feedback_coding_base_url:U({sync:`local-only`}),feedback_coding_repo:U({sync:`local-only`}),keybinding_overrides:U({merge:`record-merge`,deviceBound:!0,sync:`never-synced`}),language:U({sync:`syncable`}),theme:U({deviceBound:!0,sync:`never-synced`}),theme_overrides:U({deviceBound:!0,sync:`never-synced`}),theme_preset:U({deviceBound:!0,sync:`never-synced`}),nickname:U({sync:`syncable`}),has_completed_onboarding:U({sync:`syncable`}),has_completed_wizard:U({sync:`syncable`}),wizard_dont_show_again:U({sync:`syncable`}),first_prompt_submitted_at:U({sync:`syncable`}),thinking_level:U({sync:`local-only`}),log_level:U({sync:`syncable`}),tool_preset:U({sync:`local-only`}),agents:U({merge:`record-merge`,sync:`never-synced`}),categories:U({merge:`record-merge`,sync:`never-synced`}),model_slots:U({merge:`record-merge`,sync:`syncable`}),provider_overrides:U({merge:`record-merge`,sync:`never-synced`}),memory_auto_extract:U({sync:`never-synced`}),skill_loop:U({sync:`never-synced`}),skill_internalization:U({sync:`never-synced`}),notification:U({deviceBound:!0,sync:`syncable`}),pulse_survey:U({deviceBound:!0,sync:`never-synced`}),history:U({sync:`never-synced`}),workflow:U({sync:`never-synced`}),residency:U({sync:`never-synced`}),resilience:U({sync:`never-synced`}),sandbox:U({sync:`never-synced`}),permission_mode:U({sync:`local-only`}),permissions:U({merge:`array-merge-by-name`,sync:`never-synced`}),permission_always_allow:U({merge:`array-merge`,sync:`never-synced`}),mcp_servers:U({merge:`record-merge`,sync:`never-synced`}),proxy:U({deviceBound:!0,sync:`never-synced`}),web_fetch_allowed_hosts:U({merge:`array-merge`,sync:`never-synced`}),prompt_output_token_budget:U({sync:`local-only`}),prompt_wall_clock_budget_ms:U({sync:`local-only`}),session_cost_budget_usd:U({sync:`local-only`}),stall_detection:U({sync:`never-synced`}),todo_continue_max:U({sync:`local-only`}),disabled_agents:U({merge:`array-merge`,sync:`syncable`}),disabled_hooks:U({merge:`array-merge`,sync:`syncable`}),disabled_tools:U({merge:`array-merge`,sync:`syncable`}),disabled_skills:U({merge:`array-merge`,sync:`syncable`}),disabled_plugins:U({merge:`array-merge`,sync:`never-synced`}),installed_plugins:U({merge:`array-merge`,deviceBound:!0,sync:`never-synced`}),plugin_lifecycle:U({deviceBound:!0,sync:`never-synced`}),auto_update_check:U({deviceBound:!0,sync:`syncable`})},G=new Set(Object.entries(W).filter(([,e])=>e.export===`exclude`).map(([e])=>e)),rt=new Set(Object.entries(W).filter(([,e])=>e.sync===`syncable`).map(([e])=>e)),it=new Set(Object.entries(W).filter(([,e])=>e.sync===`never-synced`).map(([e])=>e)),at=new Map(Object.entries(W).filter(([,e])=>e.export!==`exclude`).map(([e,t])=>[e,{category:t.merge,deviceBound:t.deviceBound}]));function ot(){let e=O.filter(e=>!W[e]);if(e.length>0)throw Error(`FIELD_META 遗漏以下 SettingKey(satisfies 门禁应已拦截): ${e.join(`, `)}`)}function st(e){let t={};for(let n of Object.keys(e))rt.has(n)&&!it.has(n)&&(t[n]=e[n]);return t}var ct=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=st(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 K=a(`@x-otto/setting:claude-adapter`),lt=new Set([`Read`,`Write`,`Edit`,`MultiEdit`,`Glob`,`Grep`]),ut={default:`confirm`,acceptEdits:`auto`,bypassPermissions:`bypass`,plan:`readonly`};function dt(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 ft(e){let t=e.startsWith(`~/`)?m(g(),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 pt(e,t,n,r){let{tool:i,specifier:a}=dt(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(lt.has(i)){n.rules.push({name:`claude-${r}`,effect:t,tools:[s],paths:[ft(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 mt(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`&&pt(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&&ut[a]&&(i.permission_mode=ut[a],a===`plan`&&t.warnings.push(`defaultMode=plan otto 无对应 → 降 readonly`)),{setting:i,warnings:t.warnings}}function ht(e){if(!ee())return{};let t=[m(u,`settings.json`),...e?[m(h(e),`.claude`,`settings.json`),m(h(e),`.claude`,`settings.local.json`)]:[]],n=[],r=[],i,a=[];for(let e of t){if(!_(e))continue;let t;try{t=s(v(e,`utf-8`))}catch(t){K.warn({file:e,err:t},`Failed to parse .claude settings, skipped`);continue}let{setting:o,warnings:c}=mt(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){K.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)K.warn({rule:e},`.claude settings 翻译降级`)}return o}var gt=class extends i{paths;workspaceDir;projectConfigTrusted;overrides;store;remoteStore;setting={};writeChain=Promise.resolve();constructor(e={}){super(),this.paths=_t(e),this.workspaceDir=e.workspaceDir,this.projectConfigTrusted=e.projectConfigTrusted??!0,this.overrides={...e.overrides},this.store=e.store??new V,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 B({store:this.store,paths:this.paths,untrustedProjectPath:this.resolveUntrustedProjectPath(),extraLayers:[ht(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(te())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 _t(e){let t;if(e.projectConfigPath?t=e.projectConfigPath:e.workspaceDir&&(t=h(e.workspaceDir,p)),!t)return[];let n=h(f,d);return h(t)===n?[t]:[n,t]}const vt=a(`@x-otto/setting:project-trust-gate`);function q(){return process.env.OTTO_CLAUDE_TRUST_PATH||m(f,`claude-trust.json`)}function J(e,t=q()){return ie(t,e)}function yt(e,t=q()){ae(t,e,`project-trust`)&&vt.info({root:h(e)},`Project trusted`)}function bt(e,t=q()){oe(t,e,`project-trust`)&&vt.info({root:h(e)},`Project trust revoked`)}function Y(){return process.env.OTTO_STRICT_PROJECT_CONFIG!==`0`}function xt(e,t={}){return!Y()||!t.interactive?!0:J(e)}function St(e,t={}){if(!Y())return{highRiskKeys:[],gated:!1};let n;try{n=s(v(m(e,p),`utf-8`))}catch{return{highRiskKeys:[],gated:!1}}let r=N(n).stripped;return{highRiskKeys:r,gated:r.length>0&&!xt(e,t)}}function Ct(e){return e===h(g())||e===h(`/`)?!1:[`.git`,`.claude`,`.mcp.json`].some(t=>_(m(e,t)))}function wt(e,t={}){let n=h(e);return t.interactive?Y()?J(e)?{shouldPrompt:!1,root:n,reason:`trusted`}:Ct(n)?{shouldPrompt:!0,root:n,reason:`untrusted`}:{shouldPrompt:!1,root:n,reason:`not-a-project`}:{shouldPrompt:!1,root:n,reason:`strict-off`}:{shouldPrompt:!1,root:n,reason:`not-interactive`}}const Tt=[],Et=new Set(M);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 Dt={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`?Dt[e]:void 0,r=Dt[t];return n===void 0||r===void 0?!0:r<n}function kt(e,t){let n=X(e)?e.enabled:void 0,r=X(t)?t.enabled:void 0;return n===!0&&r===!1}function At(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 jt(e,t,n){if(!Et.has(e))return`safe`;if(e===`permission_mode`)return Ot(t,n)?`sensitive`:`safe`;if(e===`sandbox`)return kt(t,n)?`sensitive`:`safe`;if(e===`mcp_servers`||e===`provider_overrides`){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 At(t,n).length>0?`sensitive`:`safe`;throw Error(`classifyRisk: 字段 "${e}" 在 HIGH_RISK_PROJECT_KEYS 中但未被显式处理——新增高危字段必须同步补充判定分支,不允许 fail-open`)}function Mt(e,t){let n=[];for(let r of O){if(G.has(r)||r===`installed_plugins`||!(r in t))continue;let i=e[r],a=t[r];if(Z(i,a))continue;let o=at.get(r);o&&n.push({key:r,from:i,to:a,category:o.category,deviceBound:o.deviceBound,risk:jt(r,i,a)})}return n}function Nt(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 Pt(e,t){return e.some(e=>e.risk===`sensitive`)?!0:t.toInstall.some(e=>(e.capabilities??[]).some(e=>re.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===`array-merge-by-name`){let e=Array.isArray(r.from)?r.from:[],t=Array.isArray(r.to)?r.to:[],i=new Map;for(let n of[...e,...t])X(n)&&typeof n.name==`string`&&i.set(n.name,n);n[r.key]=[...i.values()]}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 It=new Set([`env`,`headers`]);function Lt(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function Rt(e){let t=[],n=(e,r)=>{if(!Lt(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`&&It.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 zt=1;function Bt(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&&ne(e.sourceSpec)?{...e,sourceSpec:``}:e);continue}i[t]=n}let a=Rt(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 Vt=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 Vt(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{Tt as BUILTIN_AGENT_PROFILES,k as BUILTIN_REVIEWER_AGENTS,zt as CURRENT_EXPORT_VERSION,A as DEFAULT_CONFIG,G as EXCLUDED_KEYS,Q as ExportFileParseError,Vt as ExportFileVersionError,at as FIELD_CLASSIFICATION,W as FIELD_META,M as HIGH_RISK_PROJECT_KEYS,b as LOG_LEVELS,it as NEVER_SYNCED_KEYS,e as PERMISSION_MODES,V as RawFileSettingStore,ct as RemoteSettingStore,O as SETTING_KEYS,rt as SYNCABLE_KEYS,z as SettingLoader,gt as SettingsManager,Ge as THEME_COLOR_ROLES,y as TOOL_PRESETS,Ft as applyMergeStrategy,ot as assertFieldClassificationCoversAllKeys,Bt as buildExportPayload,Nt as classifyPluginChanges,q as claudeTrustStorePath,St as detectProjectConfigGating,Mt as diffConfig,wt as evaluateWorkspaceTrustPrompt,st as filterSyncable,ft as globToRegexSource,Pt as hasSecuritySensitiveChanges,J as isProjectTrusted,Y as isStrictProjectConfigEnabled,ht as loadClaudeSettingsLayer,B as loadSetting,j as migrate,Ht as parseExportFile,Re as parseResilienceEnvOverride,dt as parseToolSpecifier,xt as resolveProjectConfigTrusted,N as sanitizeUntrustedProjectLayer,mt as translateClaudeSettings,yt as trustProject,bt as untrustProject,w as validateResilienceOverride};
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 ee,OTTO_HOME as d,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 m}from"node:os";import{existsSync as ae,readFileSync as oe}from"node:fs";import{HIGH_RISK_CAPABILITIES as se,isPathTrusted as ce,trustPath as le,untrustPath as ue}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),de=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())}),fe=t.object({preferred_models:_(t.array(t.string())),default_model:_(t.string())}),pe=t.strictObject({baseUrl:t.string().min(1)}),me=t.record(t.string().min(1),pe),he=t.object({enabled:_(t.boolean()),every_n_turns:_(t.number()),max_lessons:_(t.number()),similarity_threshold:_(t.number()),min_relevant_hits:_(t.number())}),ge=t.object({slots:_(t.partialRecord(v,t.union([t.string(),t.array(t.string())]))),default:_(t.string()),subagent_default_slot:_(v)}),_e=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()))}),ve=t.object({enabled:_(t.boolean()),probability:_(t.number()),min_turn_gap:_(t.number()),sink:_(t.string())}),ye=t.object({id:_(t.string()),path:_(t.string()),enabled:_(t.boolean())}),be=t.object({enabled:_(t.boolean()),maxEntries:_(t.number()),sources:_(t.array(ye))}),xe=t.object({external:_(be)}),Se=t.object({enabled:t.boolean(),network:_(t.enum([`allow`,`deny`])),writablePaths:_(t.array(t.string())),protectCredentials:_(t.boolean()),autoAllowBashIfSandboxed:_(t.boolean()),semanticReview:_(t.boolean())}),Ce=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 we=t.object({enabled:_(t.boolean())}),Te=t.object({enabled:_(t.boolean()),maxAttempts:_(t.number().int().min(1)),backoffMs:_(t.number().min(0)),backoffMultiplier:_(t.number().min(0))}),Ee=t.object({enabled:_(t.boolean()),failureThreshold:_(t.number().int().min(1)),timeoutMs:_(t.number().min(0))}),De=t.object({enabled:_(t.boolean())}),Oe=t.object({maxActiveTasks:_(t.number().int().min(1)),maxStoredTasks:_(t.number().int().min(1)),maxDelegationDepth:_(t.number().int().min(0)),maxFanOutPerParent:_(t.number().int().min(1))}),ke=t.object({enabled:_(t.boolean()),review:_(we),retry:_(Te),circuitBreaker:_(Ee),routing:_(De),orchestration:_(Oe)}),Ae=t.object({name:t.string(),effect:t.enum([`allow`,`deny`,`ask`]),tools:_(t.array(t.string())),paths:_(t.array(t.string())),commands:_(t.array(t.string())),deny_reason:_(t.string()),ask_prompt:_(t.string())}),je=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(Ae)}),Me=t.object({url:_(t.string()),enabled:_(t.boolean())}),Ne=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())}),Pe=t.object({maxRetries:_(t.number().int().positive()),initialDelayMs:_(t.number().positive()),backoffFactor:_(t.number().positive()),maxDelayMs:_(t.number().positive())}),Fe=t.object({maxRetries:_(t.number().int().positive()),intervalMs:_(t.number().positive())}),Ie=t.object({maxReconnectAttempts:_(t.number().int().nonnegative()),reconnectDelayMs:_(t.number().positive()),requestTimeoutMs:_(t.number().positive())}),Le=t.object({retryBackoffMs:_(t.number().nonnegative())}),Re=t.object({maxAttempts:_(t.number().int().positive()),intervalMs:_(t.number().positive())}),ze=t.object({refreshLockTimeoutMs:_(t.number().min(1e3))}),Be=t.object({stream:_(Pe),networkDisconnect:_(Fe),mcp:_(Ie),schedule:_(Le),errorRetry:_(Re),oauth:_(ze)});function y(e){if(typeof e!=`object`||!e||Array.isArray(e))return;let t=Be.parse(e),n={};for(let[e,r]of Object.entries(t)){if(!r||typeof r!=`object`)continue;let t={};for(let[e,n]of Object.entries(r))typeof n==`number`&&(t[e]=n);Object.keys(t).length>0&&(n[e]=t)}return n}function Ve(e,t=e=>console.warn(`[resilience-config] ${e}`)){if(!e)return;let n;try{n=JSON.parse(e)}catch(e){t(`OTTO_RESILIENCE_CONFIG is not valid JSON, ignoring entirely: ${e instanceof Error?e.message:String(e)}`);return}return y(n)}const He=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()))}),Ue=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())}),We=t.object({name:t.string().min(1).max(64),text:t.string().min(1).max(4e3)}),Ge=`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(Ge),Ke=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`]))}),qe=t.strictObject({markdown:_(Ke)}),Je=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(Ue)),shortcuts:_(t.array(We)),feedback_repo:_(t.string()),feedback_platform:_(t.enum([`auto`,`github`,`coding`])),feedback_coding_base_url:_(t.string()),feedback_coding_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:_(qe),theme_preset:_(Je),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(),de)),categories:_(t.record(t.string(),fe)),tool_preset:_(t.enum(h)),memory_auto_extract:_(he),notification:_(_e),pulse_survey:_(ve),history:_(xe),workflow:_(ke),model_slots:_(ge),provider_overrides:_(me),mcp_servers:_(t.record(t.string(),Ne)),resilience:_(Be),sandbox:_(Se),residency:_(Ce),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(He)),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(je)),permission_always_allow:_(t.array(t.string())),plugin_lifecycle:_(t.object({setupDone:_(t.array(t.object({id:t.string(),version:_(t.string())})))})),proxy:_(Me),web_fetch_allowed_hosts:_(t.array(t.string())),_migrations:_(t.array(t.string()))}),Ye=Ge,S=x.keyof().options,Xe={"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:{...Xe},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},version:void 0,model_fallback:[],feedback_repo:void 0,feedback_platform:`auto`,feedback_coding_base_url:void 0,feedback_coding_repo:void 0,keybinding_overrides:{},theme_overrides:void 0,theme_preset:{dark:void 0,light:void 0},memory_auto_extract:{enabled:!1},pulse_survey:{enabled:!0,probability:.05,min_turn_gap:20,sink:`local`},history:{external:{enabled:!1,maxEntries:1e3,sources:[]}},model_slots:{slots:void 0,default:void 0,subagent_default_slot:void 0},provider_overrides:{},mcp_servers:{},resilience:{},sandbox:{enabled:!0},residency:{},prompt_output_token_budget:void 0,prompt_wall_clock_budget_ms:void 0,session_cost_budget_usd:void 0,stall_detection:void 0,todo_continue_max:void 0,plugin_lifecycle:{setupDone:[]},proxy:{url:void 0,enabled:void 0},_migrations:[]},Ze=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}`),Ze.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`,`provider_overrides`];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`),Qe=new Set(g),$e=[{key:`mcp`,message:`Removed legacy config key is ignored`},{key:`skills`,message:`Removed legacy config key is ignored`},{key:`disabled_mcps`,message:`Removed legacy config key is ignored`},{key:`disabled_skills`,message:`Removed legacy config key is ignored`},{key:`theme_file`,message:`RFC-205 legacy theme override key is ignored`},{key:`theme_spacing`,message:`RFC-205 legacy theme override key is ignored`},{key:`theme_border`,message:`RFC-205 legacy theme override key is ignored`},{key:`theme_figures`,message:`RFC-205 legacy theme override key is ignored`}],et=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 tt(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)&&tt(a,e,[...n,i],r)}}function k(e,t){let n={...e};for(let[e,r]of Object.entries(t))r!==void 0&&(O(r)&&O(n[e])?n[e]=k(n[e],r):n[e]=r);return n}function nt(e,t,n){let r=new Map;for(let i of[...e,...t])O(i)&&typeof i[n]==`string`&&r.set(i[n],i);return[...r.values()]}function A(...e){let[t,n,...r]=e,i={...t};if(n){for(let[e,t]of Object.entries(n))if(t!==void 0)if(e===`permissions`&&Array.isArray(i[e])&&Array.isArray(t))i[e]=nt(i[e],t,`name`);else 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]=k(i[e],t):i[e]=t}return r.length>0?A(i,...r):i}function rt(){let e={},t=process.env.OTTO_MODEL;t&&(e.model=t);let n=process.env.OTTO_LOG_LEVEL;return n&&Qe.has(n)&&(e.log_level=n),e}function it(e){let t={...e},n=[];for(let{key:e,message:r}of $e)t[e]!==void 0&&(n.push({key:e,message:r}),delete t[e]);if(O(t.theme_overrides)){let e=Object.keys(t.theme_overrides).filter(e=>et.has(e));e.length>0&&n.push({key:`theme_overrides`,message:`RFC-205 legacy theme_overrides shape (keys: ${e.join(`, `)}) is ignored — use theme_overrides.markdown instead`})}return{cleaned:t,warnings:n}}function at(e){let t=x.loose().safeParse(e),n=t.success?t.data:e,r={};for(let[e,t]of Object.entries(n))t!==void 0&&(r[e]=t);return r}function ot(e,t){let n=[];for(let r of S){let i=e[r],a=t[r];if(i!==void 0&&a===void 0){n.push({key:r,message:`Invalid value for "${r}" ignored`});continue}O(i)&&O(a)&&tt(i,a,[r],n)}return n}function st(e){let{cleaned:t,warnings:n}=it(e),r=at(t),i=ot(t,r);return{value:r,warnings:[...n,...i]}}async function ct(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 j=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 ct(t,n,i):{layers:[],stripped:[]};for(let e of o)D.warn({key:e,path:i},`未受信任项目配置剔除高危键(permission_mode/mcp_servers 等需先信任该目录)`);let s=rt(),{config:c,applied:l}=w(A(C,...r,...a,s));l.length>0&&D.info({applied:l},`Config migrations applied`);let{value:u,warnings:ee}=st(c);for(let e of ee)D.warn({key:e.key,message:e.message},`Config validation issue`);return{...C,...u}}};async function M(e={}){return new j(e.store).load(e)}var N=class{type=`file`;async read(e){let{readFile:t}=await import(`node:fs/promises`);try{return await t(await P(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 P(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 P(e)),!0}catch{return!1}}};async function P(e){if(!e.startsWith(`~/`))return e;let{homedir:t}=await import(`node:os`);return`${t()}${e.slice(1)}`}const F=(e={})=>({merge:`scalar-override`,deviceBound:!1,export:`include`,sync:`local-only`,...e}),I={config_version:F({export:`exclude`,sync:`never-synced`}),version:F({export:`exclude`,sync:`never-synced`}),last_update_check:F({export:`exclude`,sync:`never-synced`}),_migrations:F({export:`exclude`,sync:`never-synced`}),model:F({sync:`syncable`}),model_fallback:F({merge:`array-merge`,sync:`syncable`}),recent_models:F({merge:`array-merge`,deviceBound:!0,sync:`never-synced`}),recent_mentions:F({merge:`array-merge`,sync:`never-synced`}),shortcuts:F({merge:`array-merge`,sync:`never-synced`}),feedback_repo:F({sync:`local-only`}),feedback_platform:F({sync:`local-only`}),feedback_coding_base_url:F({sync:`local-only`}),feedback_coding_repo:F({sync:`local-only`}),keybinding_overrides:F({merge:`record-merge`,deviceBound:!0,sync:`never-synced`}),language:F({sync:`syncable`}),theme:F({deviceBound:!0,sync:`never-synced`}),theme_overrides:F({deviceBound:!0,sync:`never-synced`}),theme_preset:F({deviceBound:!0,sync:`never-synced`}),nickname:F({sync:`syncable`}),has_completed_onboarding:F({sync:`syncable`}),has_completed_wizard:F({sync:`syncable`}),wizard_dont_show_again:F({sync:`syncable`}),first_prompt_submitted_at:F({sync:`syncable`}),thinking_level:F({sync:`local-only`}),log_level:F({sync:`syncable`}),tool_preset:F({sync:`local-only`}),agents:F({merge:`record-merge`,sync:`never-synced`}),categories:F({merge:`record-merge`,sync:`never-synced`}),model_slots:F({merge:`record-merge`,sync:`syncable`}),provider_overrides:F({merge:`record-merge`,sync:`never-synced`}),memory_auto_extract:F({sync:`never-synced`}),notification:F({deviceBound:!0,sync:`syncable`}),pulse_survey:F({deviceBound:!0,sync:`never-synced`}),history:F({sync:`never-synced`}),workflow:F({sync:`never-synced`}),residency:F({sync:`never-synced`}),resilience:F({sync:`never-synced`}),sandbox:F({sync:`never-synced`}),permission_mode:F({sync:`local-only`}),permissions:F({merge:`array-merge-by-name`,sync:`never-synced`}),permission_always_allow:F({merge:`array-merge`,sync:`never-synced`}),mcp_servers:F({merge:`record-merge`,sync:`never-synced`}),proxy:F({deviceBound:!0,sync:`never-synced`}),web_fetch_allowed_hosts:F({merge:`array-merge`,sync:`never-synced`}),prompt_output_token_budget:F({sync:`local-only`}),prompt_wall_clock_budget_ms:F({sync:`local-only`}),session_cost_budget_usd:F({sync:`local-only`}),stall_detection:F({sync:`never-synced`}),todo_continue_max:F({sync:`local-only`}),disabled_agents:F({merge:`array-merge`,sync:`syncable`}),disabled_hooks:F({merge:`array-merge`,sync:`syncable`}),disabled_tools:F({merge:`array-merge`,sync:`syncable`}),disabled_skills:F({merge:`array-merge`,sync:`syncable`}),disabled_plugins:F({merge:`array-merge`,sync:`never-synced`}),installed_plugins:F({merge:`array-merge`,deviceBound:!0,sync:`never-synced`}),plugin_lifecycle:F({deviceBound:!0,sync:`never-synced`}),auto_update_check:F({deviceBound:!0,sync:`syncable`})},L=new Set(Object.entries(I).filter(([,e])=>e.export===`exclude`).map(([e])=>e)),R=new Set(Object.entries(I).filter(([,e])=>e.sync===`syncable`).map(([e])=>e)),z=new Set(Object.entries(I).filter(([,e])=>e.sync===`never-synced`).map(([e])=>e)),B=new Map(Object.entries(I).filter(([,e])=>e.export!==`exclude`).map(([e,t])=>[e,{category:t.merge,deviceBound:t.deviceBound}]));function lt(){let e=S.filter(e=>!I[e]);if(e.length>0)throw Error(`FIELD_META 遗漏以下 SettingKey(satisfies 门禁应已拦截): ${e.join(`, `)}`)}function V(e){let t={};for(let n of Object.keys(e))R.has(n)&&!z.has(n)&&(t[n]=e[n]);return t}var ut=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`),dt=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(`~/`)?f(m(),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 ft(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(dt.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`&&ft(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 pt(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(!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 mt=class extends i{paths;workspaceDir;projectConfigTrusted;overrides;store;remoteStore;setting={};writeChain=Promise.resolve();constructor(e={}){super(),this.paths=ht(e),this.workspaceDir=e.workspaceDir,this.projectConfigTrusted=e.projectConfigTrusted??!0,this.overrides={...e.overrides},this.store=e.store??new N,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 M({store:this.store,paths:this.paths,untrustedProjectPath:this.resolveUntrustedProjectPath(),extraLayers:[pt(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 ht(e){let t;if(e.projectConfigPath?t=e.projectConfigPath:e.workspaceDir&&(t=p(e.workspaceDir,te)),!t)return[];let n=p(d,ee);return p(t)===n?[t]:[n,t]}const gt=a(`@x-otto/setting:project-trust-gate`);function q(){return process.env.OTTO_CLAUDE_TRUST_PATH||f(d,`claude-trust.json`)}function J(e,t=q()){return ce(t,e)}function _t(e,t=q()){le(t,e,`project-trust`)&&gt.info({root:p(e)},`Project trusted`)}function vt(e,t=q()){ue(t,e,`project-trust`)&&gt.info({root:p(e)},`Project trust revoked`)}function Y(){return process.env.OTTO_STRICT_PROJECT_CONFIG!==`0`}function X(e,t={}){return!Y()||!t.interactive?!0:J(e)}function yt(e,t={}){if(!Y())return{highRiskKeys:[],gated:!1};let n;try{n=s(oe(f(e,te),`utf-8`))}catch{return{highRiskKeys:[],gated:!1}}let r=E(n).stripped;return{highRiskKeys:r,gated:r.length>0&&!X(e,t)}}function bt(e){return e===p(m())||e===p(`/`)?!1:[`.git`,`.claude`,`.mcp.json`].some(t=>ae(f(e,t)))}function xt(e,t={}){let n=p(e);return t.interactive?Y()?J(e)?{shouldPrompt:!1,root:n,reason:`trusted`}:bt(n)?{shouldPrompt:!0,root:n,reason:`untrusted`}:{shouldPrompt:!1,root:n,reason:`not-a-project`}:{shouldPrompt:!1,root:n,reason:`strict-off`}:{shouldPrompt:!1,root:n,reason:`not-interactive`}}const St=[],Ct=new Set(T);function Z(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function Q(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 wt={bypass:0,auto:1,confirm:2,strict:3,readonly:4};function Tt(e,t){if(typeof t!=`string`||e===t)return!1;let n=typeof e==`string`?wt[e]:void 0,r=wt[t];return n===void 0||r===void 0?!0:r<n}function Et(e,t){let n=Z(e)?e.enabled:void 0,r=Z(t)?t.enabled:void 0;return n===!0&&r===!1}function Dt(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 Ot(e,t,n){if(!Ct.has(e))return`safe`;if(e===`permission_mode`)return Tt(t,n)?`sensitive`:`safe`;if(e===`sandbox`)return Et(t,n)?`sensitive`:`safe`;if(e===`mcp_servers`||e===`provider_overrides`){let e=Z(t)?t:{},r=Z(n)?n:{};for(let[t,n]of Object.entries(r))if(!(t in e)||!Q(e[t],n))return`sensitive`;return`safe`}if(e===`disabled_hooks`)return Q(t,n)?`safe`:`sensitive`;if(e===`permissions`||e===`permission_always_allow`)return Dt(t,n).length>0?`sensitive`:`safe`;throw Error(`classifyRisk: 字段 "${e}" 在 HIGH_RISK_PROJECT_KEYS 中但未被显式处理——新增高危字段必须同步补充判定分支,不允许 fail-open`)}function kt(e,t){let n=[];for(let r of S){if(L.has(r)||r===`installed_plugins`||!(r in t))continue;let i=e[r],a=t[r];if(Q(i,a))continue;let o=B.get(r);o&&n.push({key:r,from:i,to:a,category:o.category,deviceBound:o.deviceBound,risk:Ot(r,i,a)})}return n}function At(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 jt(e,t){return e.some(e=>e.risk===`sensitive`)?!0:t.toInstall.some(e=>(e.capabilities??[]).some(e=>se.includes(e)))}function Mt(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===`array-merge-by-name`){let e=Array.isArray(r.from)?r.from:[],t=Array.isArray(r.to)?r.to:[],i=new Map;for(let n of[...e,...t])Z(n)&&typeof n.name==`string`&&i.set(n.name,n);n[r.key]=[...i.values()]}else if(r.category===`record-merge`){let e=Z(r.from)?r.from:{},t=Z(r.to)?r.to:{};n[r.key]={...e,...t}}else n[r.key]=r.to;return n}const Nt=new Set([`env`,`headers`]);function Pt(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function Ft(e){let t=[],n=(e,r)=>{if(!Pt(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`&&Nt.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 It=1;function Lt(e,t,n,r=()=>new Date().toISOString()){let i={};for(let[t,n]of Object.entries(e))if(!L.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}let a=Ft(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 Rt=class extends Error{constructor(e,t){super(`导入文件 otto_config_version=${e},当前 otto 支持=${t}。请升级 otto 到最新版本后再试。`),this.fileVersion=e,this.supportedVersion=t,this.name=`ExportFileVersionError`}},$=class extends Error{constructor(e){super(`导入文件格式非法:${e}`),this.name=`ExportFileParseError`}};function zt(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function Bt(e){let t;try{t=JSON.parse(e)}catch(e){throw new $(e instanceof Error?e.message:String(e))}if(!zt(t))throw new $(`顶层结构必须是 JSON 对象`);let{otto_config_version:n,exported_at:r,exported_from:i,scope:a,fields:o}=t;if(typeof n!=`number`)throw new $(`缺少或非法的 otto_config_version 字段`);if(n!==1)throw new Rt(n,1);if(typeof r!=`string`||typeof i!=`string`)throw new $(`缺少或非法的 exported_at/exported_from 字段`);if(a!==`global`&&a!==`project`)throw new $(`scope 字段必须是 "global" 或 "project"`);if(!zt(o))throw new $(`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{St as BUILTIN_AGENT_PROFILES,Xe as BUILTIN_REVIEWER_AGENTS,It as CURRENT_EXPORT_VERSION,C as DEFAULT_CONFIG,L as EXCLUDED_KEYS,$ as ExportFileParseError,Rt as ExportFileVersionError,B as FIELD_CLASSIFICATION,I as FIELD_META,T as HIGH_RISK_PROJECT_KEYS,g as LOG_LEVELS,z as NEVER_SYNCED_KEYS,e as PERMISSION_MODES,N as RawFileSettingStore,ut as RemoteSettingStore,S as SETTING_KEYS,R as SYNCABLE_KEYS,j as SettingLoader,mt as SettingsManager,Ye as THEME_COLOR_ROLES,h as TOOL_PRESETS,Mt as applyMergeStrategy,lt as assertFieldClassificationCoversAllKeys,Lt as buildExportPayload,At as classifyPluginChanges,q as claudeTrustStorePath,yt as detectProjectConfigGating,kt as diffConfig,xt as evaluateWorkspaceTrustPrompt,V as filterSyncable,G as globToRegexSource,jt as hasSecuritySensitiveChanges,J as isProjectTrusted,Y as isStrictProjectConfigEnabled,pt as loadClaudeSettingsLayer,M as loadSetting,w as migrate,Bt as parseExportFile,Ve as parseResilienceEnvOverride,W as parseToolSpecifier,X as resolveProjectConfigTrusted,E as sanitizeUntrustedProjectLayer,K as translateClaudeSettings,_t as trustProject,vt as untrustProject,y as validateResilienceOverride};
2
2
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["logger","logger","isPlainObject","logger","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/field-meta.ts","../src/remote-sync-keys.ts","../src/remote-setting-store.ts","../src/claude-settings-adapter.ts","../src/settings-manager.ts","../src/project-trust-gate.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([\n z.literal(false),\n z.object({ window_turns: lenient(z.number()), repeat_threshold: lenient(z.number()) }),\n ]),\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\nconst providerOverrideSchema = z.strictObject({\n baseUrl: z.string().min(1),\n})\n\nconst providerOverridesSchema = z.record(z.string().min(1), providerOverrideSchema)\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 * RFC-380 T4:LLM 语义审批(opt-in,默认 false)。\n *\n * 开启后,**沙箱升级审批**(命令在沙箱内被拒 → escalate-on-deny,description 含\n * `[sandbox-escalation]`)在弹用户窗口之前先问一次便宜模型,由它判 allow/deny/abstain:\n * allow 自动放行并记命令 bypass、deny 自动拒绝、abstain(含超时/异常/非法输出)\n * → 回落既有用户弹窗(行为与关闭时一致)。\n *\n * 范围克制(RFC §2 D3):**仅**升级审批可进语义裁决,普通 ask 一律仍由用户裁决——\n * 让模型替用户点确认与 safety 护栏精神冲突。全自动(bypass)模式不受此开关影响:\n * 那条路径由规则直接放行,不花模型调用。默认关以维持既有安全姿态不变。\n */\n semanticReview: 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().int().min(1)),\n backoffMs: lenient(z.number().min(0)),\n backoffMultiplier: lenient(z.number().min(0)),\n})\nconst workflowCircuitBreakerSchema = z.object({\n enabled: lenient(z.boolean()),\n failureThreshold: lenient(z.number().int().min(1)),\n timeoutMs: lenient(z.number().min(0)),\n})\nconst workflowRoutingSchema = z.object({ enabled: lenient(z.boolean()) })\nconst workflowOrchestrationSchema = z.object({\n maxActiveTasks: lenient(z.number().int().min(1)),\n maxStoredTasks: lenient(z.number().int().min(1)),\n maxDelegationDepth: lenient(z.number().int().min(0)),\n maxFanOutPerParent: lenient(z.number().int().min(1)),\n})\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 orchestration: lenient(workflowOrchestrationSchema),\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 commands: 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-412 M3:resilience override 的**唯一校验真源**(此前 env 手写递归校验器与本 zod schema\n * 双实现,已收敛至此)。把任意 `unknown`(通常来自 `JSON.parse` 或 settings/programmatic 注入)\n * 经 `resilienceConfigSchema` 校验,剔除非法字段/子对象,返回可安全传给\n * `@x-otto/env` `mergeResilienceConfig` 的干净覆盖对象。\n *\n * 剪除 undefined 的必要性:`lenient = optional().catch(undefined)` 对非法字段产出 `key: undefined`,\n * 而 `mergeResilienceConfig` 用 `Object.assign` 深合并——若保留 `key: undefined` 会把默认值覆盖成\n * undefined。故 parse 后递归剪除 undefined 字段与空子对象,复刻 env 原\"非法字段整体剔除、\n * 兄弟字段不受影响\"语义。非对象输入返回 undefined。\n */\nexport function validateResilienceOverride(input: unknown): ResilienceConfigOverride | undefined {\n if (typeof input !== 'object' || input === null || Array.isArray(input)) return undefined\n const parsed = resilienceConfigSchema.parse(input) as Record<\n string,\n Record<string, unknown> | undefined\n >\n const result: Record<string, Record<string, number>> = {}\n for (const [domain, sub] of Object.entries(parsed)) {\n if (!sub || typeof sub !== 'object') continue\n const cleanedSub: Record<string, number> = {}\n for (const [field, value] of Object.entries(sub)) {\n if (typeof value === 'number') cleanedSub[field] = value\n }\n if (Object.keys(cleanedSub).length > 0) result[domain] = cleanedSub\n }\n return result as ResilienceConfigOverride\n}\n\n/**\n * RFC-412 M3:解析 `OTTO_RESILIENCE_CONFIG` 环境变量(JSON 字符串)为已校验的覆盖对象。\n * 整体 JSON 解析失败时经 `warn` 上报并返回 undefined(重要诊断,不静默吞);解析成功后走\n * `validateResilienceOverride` 做字段级校验。\n */\nexport function parseResilienceEnvOverride(\n raw: string | undefined,\n warn: (message: string) => void = (msg) => console.warn(`[resilience-config] ${msg}`),\n): ResilienceConfigOverride | undefined {\n if (!raw) return undefined\n let parsed: unknown\n try {\n parsed = JSON.parse(raw)\n } catch (error) {\n warn(\n `OTTO_RESILIENCE_CONFIG is not valid JSON, ignoring entirely: ${error instanceof Error ? error.message : String(error)}`,\n )\n return undefined\n }\n return validateResilienceOverride(parsed)\n}\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([\n 'hashtag',\n 'file',\n 'person',\n 'agent',\n 'skill',\n 'mcp',\n 'plugin',\n 'model',\n 'resource',\n 'shortcut',\n 'effort',\n ]),\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',\n 'brandShimmer',\n 'accent',\n 'accentDim',\n 'accentBright',\n 'text',\n 'inactive',\n 'secondary',\n 'subtle',\n 'replyPrefix',\n 'success',\n 'error',\n 'warning',\n 'special',\n 'suggestion',\n 'permission',\n 'codeText',\n 'diffAdd',\n 'diffRemove',\n 'diffAddBg',\n 'diffRemoveBg',\n 'panelBorder',\n 'overlayBg',\n 'tagBg',\n 'toolText',\n 'focusBorder',\n '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(\n z.tuple([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-430 §2.1:反馈面板目标平台。`'auto'`(缺省语义)= 按判定链自动选择\n * (`feedback_coding_base_url` 已配置且内网可达 → coding,否则 github);\n * 显式 `'github'`/`'coding'` 时跳过探测直接生效。\n */\n feedback_platform: lenient(z.enum(['auto', 'github', 'coding'])),\n /**\n * RFC-430 §2.6:Coding 平台 base URL(企业内网地址,运行时注入——公网源码\n * 零内网域名硬编码)。未配置时 coding 源不可用(unavailable 提示)。\n */\n feedback_coding_base_url: lenient(z.string()),\n /** RFC-430 §2.6:Coding 平台目标仓库标识(Coding 侧项目/仓库路径)。 */\n feedback_coding_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(\n z.record(\n z.string(),\n 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 ),\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 provider_overrides: lenient(providerOverridesSchema),\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([\n z.literal(false),\n z.object({ window_turns: lenient(z.number()), repeat_threshold: lenient(z.number()) }),\n ]),\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>\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 ProviderOverrides = z.infer<typeof providerOverridesSchema>\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 { validateResilienceOverride, parseResilienceEnvOverride } from './schema'\nexport type {\n Setting,\n SettingKey,\n RecentMentionEntry,\n InstalledPluginEntry,\n AgentOptions,\n AgentsConfig,\n CategoryConfig,\n CategoriesConfig,\n ProviderOverrides,\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}\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 // RFC-405 D2:补齐有结构默认值意图的缺失字段。\n version: undefined,\n model_fallback: [],\n feedback_repo: undefined,\n // RFC-430 §2.1/§2.6:反馈面板双平台配置(缺省语义 auto 探测 + coding 未配置)。\n feedback_platform: 'auto',\n feedback_coding_base_url: undefined,\n feedback_coding_repo: undefined,\n keybinding_overrides: {},\n theme_overrides: undefined,\n theme_preset: { dark: undefined, light: undefined },\n memory_auto_extract: { enabled: false },\n skill_loop: { enabled: false },\n skill_internalization: { enabled: false },\n pulse_survey: { enabled: true, probability: 0.05, min_turn_gap: 20, sink: 'local' },\n history: { external: { enabled: false, maxEntries: 1000, sources: [] } },\n model_slots: { slots: undefined, default: undefined, subagent_default_slot: undefined },\n provider_overrides: {},\n mcp_servers: {},\n resilience: {},\n sandbox: { enabled: true },\n residency: {},\n prompt_output_token_budget: undefined,\n prompt_wall_clock_budget_ms: undefined,\n session_cost_budget_usd: undefined,\n stall_detection: undefined,\n todo_continue_max: undefined,\n plugin_lifecycle: { setupDone: [] },\n proxy: { url: undefined, enabled: undefined },\n _migrations: [],\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 * - `provider_overrides` —— 可把模型请求与认证凭据转发到任意服务端。\n *\n * 刻意不含的键:`model`/`model_slots`(只选择已注册模型)、`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 'provider_overrides',\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\n/**\n * RFC-404 D6:legacy key 收敛为单一表。每条声明 key + 丢弃 warning 文案。\n * 取代此前三组时代遗留常量(REMOVED_LEGACY_KEYS / REMOVED_LEGACY_THEME_KEYS / LEGACY_THEME_OVERRIDES_KEYS)。\n */\nconst LEGACY_KEYS: ReadonlyArray<{ key: string; message: string }> = [\n { key: 'mcp', message: 'Removed legacy config key is ignored' },\n { key: 'skills', message: 'Removed legacy config key is ignored' },\n { key: 'disabled_mcps', message: 'Removed legacy config key is ignored' },\n { key: 'disabled_skills', message: 'Removed legacy config key is ignored' },\n { key: 'theme_file', message: 'RFC-205 legacy theme override key is ignored' },\n { key: 'theme_spacing', message: 'RFC-205 legacy theme override key is ignored' },\n { key: 'theme_border', message: 'RFC-205 legacy theme override key is ignored' },\n { key: 'theme_figures', message: 'RFC-205 legacy theme override key is ignored' },\n]\n\n/** RFC-277 M4:theme_overrides 旧 shape 的顶层键(非新 shape `{ markdown: {...} }`)。 */\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 mergeByName(existing: unknown[], incoming: unknown[], nameField: string): unknown[] {\n const byName = new Map<string, unknown>()\n for (const item of [...existing, ...incoming]) {\n if (isPlainObject(item) && typeof item[nameField] === 'string') {\n byName.set(item[nameField] as string, item)\n }\n }\n return [...byName.values()]\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 // RFC-404 D4:permissions 是对象数组,按 policy.name 键合并(同 name 高层覆盖低层),\n // 不走通用数组并集(Set 对对象引用去重无效,会累积重复条目)。\n if (key === 'permissions' && Array.isArray(result[key]) && Array.isArray(value)) {\n result[key] = mergeByName(result[key] as unknown[], value as unknown[], 'name')\n } else 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/** RFC-404 D6 ①:剥离 legacy key,产出 warning。返回新对象,不改入参。 */\nfunction stripLegacyKeys(input: Record<string, unknown>): { cleaned: Record<string, unknown>; warnings: SettingWarning[] } {\n const cleaned: Record<string, unknown> = { ...input }\n const warnings: SettingWarning[] = []\n for (const { key, message } of LEGACY_KEYS) {\n if (cleaned[key] !== undefined) {\n warnings.push({ key, message })\n delete cleaned[key]\n }\n }\n if (isPlainObject(cleaned.theme_overrides)) {\n const legacyKeys = Object.keys(cleaned.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 return { cleaned, warnings }\n}\n\n/** RFC-404 D6 ②:zod shape 校验,产出解析后的有效值。 */\nfunction validateShape(input: Record<string, unknown>): Record<string, unknown> {\n const result = SettingSchema.loose().safeParse(input)\n const parsed: Record<string, unknown> = result.success ? result.data : input\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 return value\n}\n\n/** RFC-404 D6 ③:逐字段比对面 vs 解析结果,收集 lenient 静默丢弃的 warning。 */\nfunction collectWarnings(input: Record<string, unknown>, value: Record<string, unknown>): SettingWarning[] {\n const warnings: SettingWarning[] = []\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 if (isPlainObject(inputVal) && isPlainObject(valueVal)) {\n collectDroppedNestedFields(inputVal, valueVal, [key], warnings)\n }\n }\n return warnings\n}\n\n/** 导出供单测直接验证 lenient() 静默丢弃字段的告警覆盖(F9),非公开配置加载 API 的一部分。 */\nexport function validate(setting: Record<string, unknown>): {\n value: Partial<Setting>\n warnings: SettingWarning[]\n} {\n const { cleaned, warnings: legacyWarnings } = stripLegacyKeys(setting)\n const value = validateShape(cleaned)\n const shapeWarnings = collectWarnings(cleaned, value)\n return { value: value as Partial<Setting>, warnings: [...legacyWarnings, ...shapeWarnings] }\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","/**\n * field-meta.ts —— RFC-405 D1:字段元数据单表(satisfies 编译期穷举门)。\n *\n * 取代此前 4 张手工平行表(SYNCABLE_KEYS / NEVER_SYNCED_KEYS / EXCLUDED_KEYS /\n * FIELD_CLASSIFICATION),消除\"每加一个 schema 字段要手工同步 4 个方向\"的熵增模式。\n * satisfies 门禁让 tsc 报错逼停,不允许漏归类(先例:capabilities.ts 的\n * CAPABILITY_RISK_CLASSIFICATION satisfies Record<PluginCapability, boolean>)。\n *\n * SYNCABLE_KEYS / NEVER_SYNCED_KEYS / EXCLUDED_KEYS / FIELD_CLASSIFICATION 全部退化为\n * 从本表派生的视图函数(消费方零感知)。\n */\nimport { SETTING_KEYS } from './schema'\nimport type { SettingKey } from './schema'\n\n/** 字段合并策略类别(与 config-merge-strategy 的 MergeCategory 对齐)。 */\nexport type FieldMergeCategory = 'scalar-override' | 'array-merge' | 'array-merge-by-name' | 'record-merge'\n\n/** 是否参与导出/导入。 */\nexport type FieldExportPolicy = 'include' | 'exclude'\n\n/** 是否可跨设备远端同步。 */\nexport type FieldSyncPolicy = 'syncable' | 'never-synced' | 'local-only'\n\n/** 单字段元数据。 */\nexport interface FieldMeta {\n /** 跨层合并 / 导入合并策略。 */\n merge: FieldMergeCategory\n /** 是否恒定路由 user 级配置存储(RFC-219 重要事项规则 5)。 */\n deviceBound: boolean\n /** 导出/导入策略:exclude = 不导出不导入(元数据/内部记账字段)。 */\n export: FieldExportPolicy\n /** 远端同步策略:syncable = 可跨设备同步;never-synced = 绝不同步(安全面/凭据/本地路径);local-only = 当前不支持同步但未来可能加入。 */\n sync: FieldSyncPolicy\n}\n\n// ─────────────────────────── 分类常量(表内引用,减少重复) ───────────────────────────\n\nconst S = (overrides: Partial<FieldMeta> = {}): FieldMeta => ({\n merge: 'scalar-override',\n deviceBound: false,\n export: 'include',\n sync: 'local-only',\n ...overrides,\n})\n\n/**\n * RFC-405 D1:字段元数据单表。每个 SettingKey 必须显式声明——satisfies 编译期穷举门\n * 钉死覆盖(新增 schema 字段后 tsc 报错逼停,不允许\"先加字段后补元数据\")。\n */\nexport const FIELD_META: Readonly<Record<SettingKey, FieldMeta>> = {\n // ── 元数据/内部记账字段——不导出、不同步 ──\n config_version: S({ export: 'exclude', sync: 'never-synced' }),\n version: S({ export: 'exclude', sync: 'never-synced' }),\n last_update_check: S({ export: 'exclude', sync: 'never-synced' }),\n _migrations: S({ export: 'exclude', sync: 'never-synced' }),\n\n // ── 标量偏好字段——导入即采用,可同步 ──\n model: S({ sync: 'syncable' }),\n model_fallback: S({ merge: 'array-merge', sync: 'syncable' }),\n recent_models: S({ merge: 'array-merge', deviceBound: true, sync: 'never-synced' }),\n recent_mentions: S({ merge: 'array-merge', sync: 'never-synced' }),\n shortcuts: S({ merge: 'array-merge', sync: 'never-synced' }),\n feedback_repo: S({ sync: 'local-only' }),\n // RFC-430 §2.1/§2.6:反馈面板平台裁决与 Coding 平台定位。同 feedback_repo 的\n // `local-only`——仓库/平台指向随机器与网络环境而异(base_url 是企业内网地址),\n // 不跨设备同步。\n feedback_platform: S({ sync: 'local-only' }),\n feedback_coding_base_url: S({ sync: 'local-only' }),\n feedback_coding_repo: S({ sync: 'local-only' }),\n keybinding_overrides: S({ merge: 'record-merge', deviceBound: true, sync: 'never-synced' }),\n language: S({ sync: 'syncable' }),\n theme: S({ deviceBound: true, sync: 'never-synced' }),\n theme_overrides: S({ deviceBound: true, sync: 'never-synced' }),\n theme_preset: S({ deviceBound: true, sync: 'never-synced' }),\n nickname: S({ sync: 'syncable' }),\n has_completed_onboarding: S({ sync: 'syncable' }),\n has_completed_wizard: S({ sync: 'syncable' }),\n wizard_dont_show_again: S({ sync: 'syncable' }),\n first_prompt_submitted_at: S({ sync: 'syncable' }),\n thinking_level: S({ sync: 'local-only' }),\n log_level: S({ sync: 'syncable' }),\n tool_preset: S({ sync: 'local-only' }),\n\n // ── Agent/Category 配置——record-merge,不同步 ──\n agents: S({ merge: 'record-merge', sync: 'never-synced' }),\n categories: S({ merge: 'record-merge', sync: 'never-synced' }),\n model_slots: S({ merge: 'record-merge', sync: 'syncable' }),\n provider_overrides: S({ merge: 'record-merge', sync: 'never-synced' }),\n\n // ── 行为调优参数——scalar-override(整体对象替换),不同步 ──\n memory_auto_extract: S({ sync: 'never-synced' }),\n skill_loop: S({ sync: 'never-synced' }),\n skill_internalization: S({ sync: 'never-synced' }),\n notification: S({ deviceBound: true, sync: 'syncable' }),\n pulse_survey: S({ deviceBound: true, sync: 'never-synced' }),\n history: S({ sync: 'never-synced' }),\n workflow: S({ sync: 'never-synced' }),\n residency: S({ sync: 'never-synced' }),\n resilience: S({ sync: 'never-synced' }),\n\n // ── 安全面字段——scalar-override 或特殊合并,绝不同步 ──\n sandbox: S({ sync: 'never-synced' }),\n permission_mode: S({ sync: 'local-only' }),\n // RFC-405 D4:permissions 按 policy.name 键合并(同 name 高层覆盖低层)。\n permissions: S({ merge: 'array-merge-by-name', sync: 'never-synced' }),\n permission_always_allow: S({ merge: 'array-merge', sync: 'never-synced' }),\n\n // ── MCP / 网络 / 代理——record-merge 或标量,绝不同步 ──\n mcp_servers: S({ merge: 'record-merge', sync: 'never-synced' }),\n proxy: S({ deviceBound: true, sync: 'never-synced' }),\n web_fetch_allowed_hosts: S({ merge: 'array-merge', sync: 'never-synced' }),\n\n // ── 预算字段──标量,不同步 ──\n prompt_output_token_budget: S({ sync: 'local-only' }),\n prompt_wall_clock_budget_ms: S({ sync: 'local-only' }),\n session_cost_budget_usd: S({ sync: 'local-only' }),\n stall_detection: S({ sync: 'never-synced' }),\n todo_continue_max: S({ sync: 'local-only' }),\n\n // ── 禁用列表——array-merge(并集),部分可同步 ──\n disabled_agents: S({ merge: 'array-merge', sync: 'syncable' }),\n disabled_hooks: S({ merge: 'array-merge', sync: 'syncable' }),\n disabled_tools: S({ merge: 'array-merge', sync: 'syncable' }),\n disabled_skills: S({ merge: 'array-merge', sync: 'syncable' }),\n disabled_plugins: S({ merge: 'array-merge', sync: 'never-synced' }),\n\n // ── 插件清单——array-merge,device-bound,绝不同步 ──\n installed_plugins: S({ merge: 'array-merge', deviceBound: true, sync: 'never-synced' }),\n plugin_lifecycle: S({ deviceBound: true, sync: 'never-synced' }),\n\n // ── 其他 ──\n auto_update_check: S({ deviceBound: true, sync: 'syncable' }),\n} as const satisfies Record<SettingKey, FieldMeta>\n\n// ─────────────────────────── 派生视图(取代 4 张手工表) ───────────────────────────\n\n/** 不导出/不导入的字段集合(取代 EXCLUDED_KEYS)。 */\nexport const EXCLUDED_KEYS: ReadonlySet<SettingKey> = new Set(\n (Object.entries(FIELD_META) as Array<[SettingKey, FieldMeta]>)\n .filter(([, meta]) => meta.export === 'exclude')\n .map(([key]) => key),\n)\n\n/** 可跨设备同步的字段集合(取代 SYNCABLE_KEYS)。 */\nexport const SYNCABLE_KEYS: ReadonlySet<SettingKey> = new Set(\n (Object.entries(FIELD_META) as Array<[SettingKey, FieldMeta]>)\n .filter(([, meta]) => meta.sync === 'syncable')\n .map(([key]) => key),\n)\n\n/** 绝不可远端同步的字段集合(取代 NEVER_SYNCED_KEYS)。 */\nexport const NEVER_SYNCED_KEYS: ReadonlySet<string> = new Set(\n (Object.entries(FIELD_META) as Array<[SettingKey, FieldMeta]>)\n .filter(([, meta]) => meta.sync === 'never-synced')\n .map(([key]) => key),\n)\n\n/** 字段静态分类表(取代 FIELD_CLASSIFICATION)。 */\nexport const FIELD_CLASSIFICATION: ReadonlyMap<SettingKey, FieldClassification> = new Map(\n (Object.entries(FIELD_META) as Array<[SettingKey, FieldMeta]>)\n .filter(([, meta]) => meta.export !== 'exclude')\n .map(([key, meta]) => [key, { category: meta.merge, deviceBound: meta.deviceBound }] as const),\n)\n\n/** 单字段的静态分类(与具体 diff 值无关)。 */\nexport interface FieldClassification {\n category: FieldMergeCategory\n deviceBound: boolean\n}\n\n/**\n * 断言字段元数据表覆盖全部 `SETTING_KEYS`。satisfies 已在编译期保证全覆盖,\n * 此函数保留供运行时测试调用(向后兼容现有测试)。\n */\nexport function assertFieldClassificationCoversAllKeys(): void {\n const uncovered = SETTING_KEYS.filter((key) => !FIELD_META[key])\n if (uncovered.length > 0) {\n throw new Error(\n `FIELD_META 遗漏以下 SettingKey(satisfies 门禁应已拦截): ${uncovered.join(', ')}`,\n )\n }\n}\n","/**\n * RFC-052 D2 + RFC-405 D1:可跨机器同步的用户偏好字段白名单。\n * 从 FIELD_META 派生(sync === 'syncable' 的字段集合),不再手工维护。\n */\nexport { SYNCABLE_KEYS, NEVER_SYNCED_KEYS } from './field-meta'\n// re-export filterSyncable(本模块仍保留实现)\nimport { SYNCABLE_KEYS, NEVER_SYNCED_KEYS } from './field-meta'\nimport type { Setting, SettingKey } from './schema'\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","/**\n * project-trust-gate.ts — 项目信任决策层(RFC-421 D3,原 claude-compat project-config-trust.ts).\n *\n * 与 `project-trust.ts`(纯函数层:高危键定义 + sanitize)同域,补齐**决策层**:\n * env 门控 + 交互态判定 + 信任白名单读取 + 工作区信任弹窗纯决策。\n *\n * **归位理由**:原在 `@x-otto/claude-compat`(兼容层),但严格项目配置门是**默认开启**\n * 的宿主核心安全策略,核心安全决策不应住在可选兼容层。决策与执行(sanitize)同域,\n * 消除\"核心安全依赖兼容层存在\"的结构倒置。\n *\n * 信任判定(isProjectTrusted/trustProject/untrustProject/claudeTrustStorePath)也一并迁入:\n * 原 claude-compat/claude-trust.ts 中,但这些函数是通用项目信任(非 Claude 方言),\n * 复用 @x-otto/plugin 的 trust-store(setting→plugin 边已存在)。\n *\n * Claude 方言的信任面(detectClaudeExecutableSurface/evaluateClaudeTrust)留在 compat,\n * 从本模块取底层信任判定函数。\n */\nimport { existsSync, readFileSync } from 'node:fs'\nimport { homedir } from 'node:os'\nimport { join, resolve } from 'node:path'\nimport { OTTO_HOME, OTTO_PROJECT_CONFIG_RELPATH } from '@x-otto/env'\nimport { createLogger, parseJsonc } from '@x-otto/shared'\nimport { isPathTrusted, trustPath, untrustPath } from '@x-otto/plugin'\nimport { sanitizeUntrustedProjectLayer } from './project-trust'\nimport type { Setting } from './schema'\n\nconst logger = createLogger('@x-otto/setting:project-trust-gate')\n\n// ── 信任白名单持久化(原 claude-compat/claude-trust.ts 通用部分)──\n\n/**\n * 信任白名单落盘路径(用户级)。`OTTO_CLAUDE_TRUST_PATH` 可覆盖(测试/隔离用,\n * 避免污染真实 `~/.otto`——参照 auth-store 真路径教训)。调用时求值。\n */\nexport function claudeTrustStorePath(): string {\n return process.env['OTTO_CLAUDE_TRUST_PATH'] || join(OTTO_HOME, 'claude-trust.json')\n}\n\n/** 该项目根是否已被用户显式信任。 */\nexport function isProjectTrusted(\n workspaceDir: string,\n storePath = claudeTrustStorePath(),\n): boolean {\n return isPathTrusted(storePath, workspaceDir)\n}\n\n/** 显式信任某项目根(幂等,持久化)。 */\nexport function trustProject(workspaceDir: string, storePath = claudeTrustStorePath()): void {\n if (trustPath(storePath, workspaceDir, 'project-trust')) {\n logger.info({ root: resolve(workspaceDir) }, 'Project trusted')\n }\n}\n\n/** 撤销对某项目根的信任(持久化)。 */\nexport function untrustProject(workspaceDir: string, storePath = claudeTrustStorePath()): void {\n if (untrustPath(storePath, workspaceDir, 'project-trust')) {\n logger.info({ root: resolve(workspaceDir) }, 'Project trust revoked')\n }\n}\n\n// ── 严格项目配置门控(原 claude-compat/project-config-trust.ts)──\n\n/**\n * 严格项目配置门控是否开启(env-only,项目 config 不可影响,杜绝恶意仓库自关门)。\n * **默认开**(安全默认):克隆仓库的 `.otto/config.json` 高危键默认不生效,须 `/trust trust`。\n * `OTTO_STRICT_PROJECT_CONFIG=0` 是逃生口(关门控、恢复旧的全信任行为)。\n * 注:仅交互态实际剔除(headless fail-open,见 resolveProjectConfigTrusted),故 CI/脚本/测试不受影响。\n */\nexport function isStrictProjectConfigEnabled(): boolean {\n return process.env['OTTO_STRICT_PROJECT_CONFIG'] !== '0'\n}\n\nexport interface ProjectConfigTrustContext {\n /** 是否交互(TUI)态。缺省 false=headless → fail-open 放行。 */\n interactive?: boolean\n}\n\n/**\n * 该 workspace 的项目配置是否受信任。\n * 严格模式关 → 恒 `true`;headless → 恒 `true`(fail-open);交互+严格 → 取信任白名单判定。\n */\nexport function resolveProjectConfigTrusted(\n workspaceDir: string,\n ctx: ProjectConfigTrustContext = {},\n): boolean {\n if (!isStrictProjectConfigEnabled()) return true\n if (!ctx.interactive) return true\n return isProjectTrusted(workspaceDir)\n}\n\nexport interface ProjectConfigGating {\n /** 项目 `.otto/config.json` 实际含有的高危键(空 = 无可门控内容)。 */\n highRiskKeys: string[]\n /** 这些高危键当前是否被剔除(严格开 + 交互 + 未信任)。 */\n gated: boolean\n}\n\n/**\n * 探测项目 `.otto/config.json` 的高危键门控状态(供 `/compat status` 呈现)。\n * 严格模式关 → 返空(特性未启用,无需呈现噪声)。读文件失败/无文件 → 返空。\n */\nexport function detectProjectConfigGating(\n workspaceDir: string,\n ctx: ProjectConfigTrustContext = {},\n): ProjectConfigGating {\n if (!isStrictProjectConfigEnabled()) return { highRiskKeys: [], gated: false }\n let raw: Partial<Setting>\n try {\n const content = readFileSync(join(workspaceDir, OTTO_PROJECT_CONFIG_RELPATH), 'utf-8')\n raw = parseJsonc<Partial<Setting>>(content)\n } catch {\n return { highRiskKeys: [], gated: false }\n }\n const highRiskKeys = sanitizeUntrustedProjectLayer(raw).stripped\n const gated = highRiskKeys.length > 0 && !resolveProjectConfigTrusted(workspaceDir, ctx)\n return { highRiskKeys, gated }\n}\n\n// ── 工作区信任弹窗纯决策 ──\n\n/** 用户对工作区信任弹窗的选择。 */\nexport type WorkspaceTrustAction = 'trust' | 'readonly' | 'exit'\n\nexport interface WorkspaceTrustPromptDecision {\n /** 是否应弹\"信任此文件夹吗\"。 */\n shouldPrompt: boolean\n /** 解析到的工作区根(信任按它记录)。 */\n root: string\n /** 不弹的原因(诊断/测试)。 */\n reason: 'trusted' | 'not-interactive' | 'strict-off' | 'not-a-project' | 'untrusted'\n}\n\n/**\n * \"真项目\"判定:值得门控的目录。排除 $HOME / 文件系统根(随手 cd 不该弹),\n * 要求有 vcs/config marker(.git/.claude/.mcp.json)——即 otto/git 实际会作用的项目。\n * 纯 scratch 目录(无 marker)不弹,避免过度打扰。\n *\n * 重要:`.otto` 已从标记列表移除(根因修复,真实用户反馈:首次启动不弹信任提示,\n * 第二次才弹)。`.otto/workspace.json` 是 otto 自身建立 workspace 身份时创建的产物,\n * 用它做\"是否真项目\"的判据是自我指涉——任何目录只要跑过一次 otto 就会自动\"自证\"\n * 成真项目。团队共享 workspace.json 场景必然存在 `.git`,已被其余标记覆盖。\n */\nfunction isGateworthyProject(root: string): boolean {\n if (root === resolve(homedir()) || root === resolve('/')) return false\n return ['.git', '.claude', '.mcp.json'].some((m) => existsSync(join(root, m)))\n}\n\n/**\n * 工作区信任弹窗的纯决策:启动时是否要弹\"信任此文件夹吗\"。\n * 弹 ⟺ 交互 且 严格开 且 根未信任 且 是真项目。其余一律不弹。\n * 纯函数:只读 FS + env,不做 IO 副作用、不渲染。\n */\nexport function evaluateWorkspaceTrustPrompt(\n workspaceDir: string,\n ctx: ProjectConfigTrustContext = {},\n): WorkspaceTrustPromptDecision {\n const root = resolve(workspaceDir)\n if (!ctx.interactive) return { shouldPrompt: false, root, reason: 'not-interactive' }\n if (!isStrictProjectConfigEnabled()) return { shouldPrompt: false, root, reason: 'strict-off' }\n if (isProjectTrusted(workspaceDir)) return { shouldPrompt: false, root, reason: 'trusted' }\n if (!isGateworthyProject(root)) return { shouldPrompt: false, root, reason: 'not-a-project' }\n return { shouldPrompt: true, root, reason: 'untrusted' }\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 { EXCLUDED_KEYS, FIELD_CLASSIFICATION } from './field-meta'\nimport type { FieldMergeCategory } from './field-meta'\nimport type { Setting, SettingKey, InstalledPluginEntry } from './schema'\n\n/**\n * RFC-405 D1:MergeCategory 从 field-meta 的 FieldMergeCategory 派生(单源)。\n * 保留原类型名向后兼容(消费方 import MergeCategory)。\n */\nexport type MergeCategory = FieldMergeCategory\n\n// EXCLUDED_KEYS、FIELD_CLASSIFICATION 从 field-meta.ts 导入(派生视图),\n// 此处 re-export 保持消费方 import 不变。\nexport { EXCLUDED_KEYS, FIELD_CLASSIFICATION, assertFieldClassificationCoversAllKeys } from './field-meta'\nexport type { FieldClassification, FieldMeta } from './field-meta'\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' || key === 'provider_overrides') {\n // 新增/变更条目(record-merge:新 key 或同 key 值变化)均视为敏感。\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 === 'array-merge-by-name') {\n // RFC-404 D4:对象数组按 name 键合并(同 name 高层覆盖低层),用于 permissions。\n const fromArr = Array.isArray(diff.from) ? diff.from : []\n const toArr = Array.isArray(diff.to) ? diff.to : []\n const byName = new Map<string, unknown>()\n for (const item of [...fromArr, ...toArr]) {\n if (isPlainObject(item) && typeof item['name'] === 'string') {\n byName.set(item['name'] as string, item)\n }\n }\n result[diff.key] = [...byName.values()]\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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"}
1
+ {"version":3,"file":"index.js","names":["logger","logger","isPlainObject","logger","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/field-meta.ts","../src/remote-sync-keys.ts","../src/remote-setting-store.ts","../src/claude-settings-adapter.ts","../src/settings-manager.ts","../src/project-trust-gate.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([\n z.literal(false),\n z.object({ window_turns: lenient(z.number()), repeat_threshold: lenient(z.number()) }),\n ]),\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\nconst providerOverrideSchema = z.strictObject({\n baseUrl: z.string().min(1),\n})\n\nconst providerOverridesSchema = z.record(z.string().min(1), providerOverrideSchema)\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\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 * RFC-380 T4:LLM 语义审批(opt-in,默认 false)。\n *\n * 开启后,**沙箱升级审批**(命令在沙箱内被拒 → escalate-on-deny,description 含\n * `[sandbox-escalation]`)在弹用户窗口之前先问一次便宜模型,由它判 allow/deny/abstain:\n * allow 自动放行并记命令 bypass、deny 自动拒绝、abstain(含超时/异常/非法输出)\n * → 回落既有用户弹窗(行为与关闭时一致)。\n *\n * 范围克制(RFC §2 D3):**仅**升级审批可进语义裁决,普通 ask 一律仍由用户裁决——\n * 让模型替用户点确认与 safety 护栏精神冲突。全自动(bypass)模式不受此开关影响:\n * 那条路径由规则直接放行,不花模型调用。默认关以维持既有安全姿态不变。\n */\n semanticReview: 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().int().min(1)),\n backoffMs: lenient(z.number().min(0)),\n backoffMultiplier: lenient(z.number().min(0)),\n})\nconst workflowCircuitBreakerSchema = z.object({\n enabled: lenient(z.boolean()),\n failureThreshold: lenient(z.number().int().min(1)),\n timeoutMs: lenient(z.number().min(0)),\n})\nconst workflowRoutingSchema = z.object({ enabled: lenient(z.boolean()) })\nconst workflowOrchestrationSchema = z.object({\n maxActiveTasks: lenient(z.number().int().min(1)),\n maxStoredTasks: lenient(z.number().int().min(1)),\n maxDelegationDepth: lenient(z.number().int().min(0)),\n maxFanOutPerParent: lenient(z.number().int().min(1)),\n})\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 orchestration: lenient(workflowOrchestrationSchema),\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 commands: 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-412 M3:resilience override 的**唯一校验真源**(此前 env 手写递归校验器与本 zod schema\n * 双实现,已收敛至此)。把任意 `unknown`(通常来自 `JSON.parse` 或 settings/programmatic 注入)\n * 经 `resilienceConfigSchema` 校验,剔除非法字段/子对象,返回可安全传给\n * `@x-otto/env` `mergeResilienceConfig` 的干净覆盖对象。\n *\n * 剪除 undefined 的必要性:`lenient = optional().catch(undefined)` 对非法字段产出 `key: undefined`,\n * 而 `mergeResilienceConfig` 用 `Object.assign` 深合并——若保留 `key: undefined` 会把默认值覆盖成\n * undefined。故 parse 后递归剪除 undefined 字段与空子对象,复刻 env 原\"非法字段整体剔除、\n * 兄弟字段不受影响\"语义。非对象输入返回 undefined。\n */\nexport function validateResilienceOverride(input: unknown): ResilienceConfigOverride | undefined {\n if (typeof input !== 'object' || input === null || Array.isArray(input)) return undefined\n const parsed = resilienceConfigSchema.parse(input) as Record<\n string,\n Record<string, unknown> | undefined\n >\n const result: Record<string, Record<string, number>> = {}\n for (const [domain, sub] of Object.entries(parsed)) {\n if (!sub || typeof sub !== 'object') continue\n const cleanedSub: Record<string, number> = {}\n for (const [field, value] of Object.entries(sub)) {\n if (typeof value === 'number') cleanedSub[field] = value\n }\n if (Object.keys(cleanedSub).length > 0) result[domain] = cleanedSub\n }\n return result as ResilienceConfigOverride\n}\n\n/**\n * RFC-412 M3:解析 `OTTO_RESILIENCE_CONFIG` 环境变量(JSON 字符串)为已校验的覆盖对象。\n * 整体 JSON 解析失败时经 `warn` 上报并返回 undefined(重要诊断,不静默吞);解析成功后走\n * `validateResilienceOverride` 做字段级校验。\n */\nexport function parseResilienceEnvOverride(\n raw: string | undefined,\n warn: (message: string) => void = (msg) => console.warn(`[resilience-config] ${msg}`),\n): ResilienceConfigOverride | undefined {\n if (!raw) return undefined\n let parsed: unknown\n try {\n parsed = JSON.parse(raw)\n } catch (error) {\n warn(\n `OTTO_RESILIENCE_CONFIG is not valid JSON, ignoring entirely: ${error instanceof Error ? error.message : String(error)}`,\n )\n return undefined\n }\n return validateResilienceOverride(parsed)\n}\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([\n 'hashtag',\n 'file',\n 'person',\n 'agent',\n 'skill',\n 'mcp',\n 'plugin',\n 'model',\n 'resource',\n 'shortcut',\n 'effort',\n ]),\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',\n 'brandShimmer',\n 'accent',\n 'accentDim',\n 'accentBright',\n 'text',\n 'inactive',\n 'secondary',\n 'subtle',\n 'replyPrefix',\n 'success',\n 'error',\n 'warning',\n 'special',\n 'suggestion',\n 'permission',\n 'codeText',\n 'diffAdd',\n 'diffRemove',\n 'diffAddBg',\n 'diffRemoveBg',\n 'panelBorder',\n 'overlayBg',\n 'tagBg',\n 'toolText',\n 'focusBorder',\n '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(\n z.tuple([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-430 §2.1:反馈面板目标平台。`'auto'`(缺省语义)= 按判定链自动选择\n * (`feedback_coding_base_url` 已配置且内网可达 → coding,否则 github);\n * 显式 `'github'`/`'coding'` 时跳过探测直接生效。\n */\n feedback_platform: lenient(z.enum(['auto', 'github', 'coding'])),\n /**\n * RFC-430 §2.6:Coding 平台 base URL(企业内网地址,运行时注入——公网源码\n * 零内网域名硬编码)。未配置时 coding 源不可用(unavailable 提示)。\n */\n feedback_coding_base_url: lenient(z.string()),\n /** RFC-430 §2.6:Coding 平台目标仓库标识(Coding 侧项目/仓库路径)。 */\n feedback_coding_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(\n z.record(\n z.string(),\n 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 ),\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 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 provider_overrides: lenient(providerOverridesSchema),\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([\n z.literal(false),\n z.object({ window_turns: lenient(z.number()), repeat_threshold: lenient(z.number()) }),\n ]),\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>\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 ProviderOverrides = z.infer<typeof providerOverridesSchema>\nexport type MemoryAutoExtractConfig = z.infer<typeof memoryAutoExtractConfigSchema>\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 { validateResilienceOverride, parseResilienceEnvOverride } from './schema'\nexport type {\n Setting,\n SettingKey,\n RecentMentionEntry,\n InstalledPluginEntry,\n AgentOptions,\n AgentsConfig,\n CategoryConfig,\n CategoriesConfig,\n ProviderOverrides,\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}\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 // RFC-405 D2:补齐有结构默认值意图的缺失字段。\n version: undefined,\n model_fallback: [],\n feedback_repo: undefined,\n // RFC-430 §2.1/§2.6:反馈面板双平台配置(缺省语义 auto 探测 + coding 未配置)。\n feedback_platform: 'auto',\n feedback_coding_base_url: undefined,\n feedback_coding_repo: undefined,\n keybinding_overrides: {},\n theme_overrides: undefined,\n theme_preset: { dark: undefined, light: undefined },\n memory_auto_extract: { enabled: false },\n pulse_survey: { enabled: true, probability: 0.05, min_turn_gap: 20, sink: 'local' },\n history: { external: { enabled: false, maxEntries: 1000, sources: [] } },\n model_slots: { slots: undefined, default: undefined, subagent_default_slot: undefined },\n provider_overrides: {},\n mcp_servers: {},\n resilience: {},\n sandbox: { enabled: true },\n residency: {},\n prompt_output_token_budget: undefined,\n prompt_wall_clock_budget_ms: undefined,\n session_cost_budget_usd: undefined,\n stall_detection: undefined,\n todo_continue_max: undefined,\n plugin_lifecycle: { setupDone: [] },\n proxy: { url: undefined, enabled: undefined },\n _migrations: [],\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 * - `provider_overrides` —— 可把模型请求与认证凭据转发到任意服务端。\n *\n * 刻意不含的键:`model`/`model_slots`(只选择已注册模型)、`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 'provider_overrides',\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\n/**\n * RFC-404 D6:legacy key 收敛为单一表。每条声明 key + 丢弃 warning 文案。\n * 取代此前三组时代遗留常量(REMOVED_LEGACY_KEYS / REMOVED_LEGACY_THEME_KEYS / LEGACY_THEME_OVERRIDES_KEYS)。\n */\nconst LEGACY_KEYS: ReadonlyArray<{ key: string; message: string }> = [\n { key: 'mcp', message: 'Removed legacy config key is ignored' },\n { key: 'skills', message: 'Removed legacy config key is ignored' },\n { key: 'disabled_mcps', message: 'Removed legacy config key is ignored' },\n { key: 'disabled_skills', message: 'Removed legacy config key is ignored' },\n { key: 'theme_file', message: 'RFC-205 legacy theme override key is ignored' },\n { key: 'theme_spacing', message: 'RFC-205 legacy theme override key is ignored' },\n { key: 'theme_border', message: 'RFC-205 legacy theme override key is ignored' },\n { key: 'theme_figures', message: 'RFC-205 legacy theme override key is ignored' },\n]\n\n/** RFC-277 M4:theme_overrides 旧 shape 的顶层键(非新 shape `{ markdown: {...} }`)。 */\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 mergeByName(existing: unknown[], incoming: unknown[], nameField: string): unknown[] {\n const byName = new Map<string, unknown>()\n for (const item of [...existing, ...incoming]) {\n if (isPlainObject(item) && typeof item[nameField] === 'string') {\n byName.set(item[nameField] as string, item)\n }\n }\n return [...byName.values()]\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 // RFC-404 D4:permissions 是对象数组,按 policy.name 键合并(同 name 高层覆盖低层),\n // 不走通用数组并集(Set 对对象引用去重无效,会累积重复条目)。\n if (key === 'permissions' && Array.isArray(result[key]) && Array.isArray(value)) {\n result[key] = mergeByName(result[key] as unknown[], value as unknown[], 'name')\n } else 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/** RFC-404 D6 ①:剥离 legacy key,产出 warning。返回新对象,不改入参。 */\nfunction stripLegacyKeys(input: Record<string, unknown>): { cleaned: Record<string, unknown>; warnings: SettingWarning[] } {\n const cleaned: Record<string, unknown> = { ...input }\n const warnings: SettingWarning[] = []\n for (const { key, message } of LEGACY_KEYS) {\n if (cleaned[key] !== undefined) {\n warnings.push({ key, message })\n delete cleaned[key]\n }\n }\n if (isPlainObject(cleaned.theme_overrides)) {\n const legacyKeys = Object.keys(cleaned.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 return { cleaned, warnings }\n}\n\n/** RFC-404 D6 ②:zod shape 校验,产出解析后的有效值。 */\nfunction validateShape(input: Record<string, unknown>): Record<string, unknown> {\n const result = SettingSchema.loose().safeParse(input)\n const parsed: Record<string, unknown> = result.success ? result.data : input\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 return value\n}\n\n/** RFC-404 D6 ③:逐字段比对面 vs 解析结果,收集 lenient 静默丢弃的 warning。 */\nfunction collectWarnings(input: Record<string, unknown>, value: Record<string, unknown>): SettingWarning[] {\n const warnings: SettingWarning[] = []\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 if (isPlainObject(inputVal) && isPlainObject(valueVal)) {\n collectDroppedNestedFields(inputVal, valueVal, [key], warnings)\n }\n }\n return warnings\n}\n\n/** 导出供单测直接验证 lenient() 静默丢弃字段的告警覆盖(F9),非公开配置加载 API 的一部分。 */\nexport function validate(setting: Record<string, unknown>): {\n value: Partial<Setting>\n warnings: SettingWarning[]\n} {\n const { cleaned, warnings: legacyWarnings } = stripLegacyKeys(setting)\n const value = validateShape(cleaned)\n const shapeWarnings = collectWarnings(cleaned, value)\n return { value: value as Partial<Setting>, warnings: [...legacyWarnings, ...shapeWarnings] }\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","/**\n * field-meta.ts —— RFC-405 D1:字段元数据单表(satisfies 编译期穷举门)。\n *\n * 取代此前 4 张手工平行表(SYNCABLE_KEYS / NEVER_SYNCED_KEYS / EXCLUDED_KEYS /\n * FIELD_CLASSIFICATION),消除\"每加一个 schema 字段要手工同步 4 个方向\"的熵增模式。\n * satisfies 门禁让 tsc 报错逼停,不允许漏归类(先例:capabilities.ts 的\n * CAPABILITY_RISK_CLASSIFICATION satisfies Record<PluginCapability, boolean>)。\n *\n * SYNCABLE_KEYS / NEVER_SYNCED_KEYS / EXCLUDED_KEYS / FIELD_CLASSIFICATION 全部退化为\n * 从本表派生的视图函数(消费方零感知)。\n */\nimport { SETTING_KEYS } from './schema'\nimport type { SettingKey } from './schema'\n\n/** 字段合并策略类别(与 config-merge-strategy 的 MergeCategory 对齐)。 */\nexport type FieldMergeCategory = 'scalar-override' | 'array-merge' | 'array-merge-by-name' | 'record-merge'\n\n/** 是否参与导出/导入。 */\nexport type FieldExportPolicy = 'include' | 'exclude'\n\n/** 是否可跨设备远端同步。 */\nexport type FieldSyncPolicy = 'syncable' | 'never-synced' | 'local-only'\n\n/** 单字段元数据。 */\nexport interface FieldMeta {\n /** 跨层合并 / 导入合并策略。 */\n merge: FieldMergeCategory\n /** 是否恒定路由 user 级配置存储(RFC-219 重要事项规则 5)。 */\n deviceBound: boolean\n /** 导出/导入策略:exclude = 不导出不导入(元数据/内部记账字段)。 */\n export: FieldExportPolicy\n /** 远端同步策略:syncable = 可跨设备同步;never-synced = 绝不同步(安全面/凭据/本地路径);local-only = 当前不支持同步但未来可能加入。 */\n sync: FieldSyncPolicy\n}\n\n// ─────────────────────────── 分类常量(表内引用,减少重复) ───────────────────────────\n\nconst S = (overrides: Partial<FieldMeta> = {}): FieldMeta => ({\n merge: 'scalar-override',\n deviceBound: false,\n export: 'include',\n sync: 'local-only',\n ...overrides,\n})\n\n/**\n * RFC-405 D1:字段元数据单表。每个 SettingKey 必须显式声明——satisfies 编译期穷举门\n * 钉死覆盖(新增 schema 字段后 tsc 报错逼停,不允许\"先加字段后补元数据\")。\n */\nexport const FIELD_META: Readonly<Record<SettingKey, FieldMeta>> = {\n // ── 元数据/内部记账字段——不导出、不同步 ──\n config_version: S({ export: 'exclude', sync: 'never-synced' }),\n version: S({ export: 'exclude', sync: 'never-synced' }),\n last_update_check: S({ export: 'exclude', sync: 'never-synced' }),\n _migrations: S({ export: 'exclude', sync: 'never-synced' }),\n\n // ── 标量偏好字段——导入即采用,可同步 ──\n model: S({ sync: 'syncable' }),\n model_fallback: S({ merge: 'array-merge', sync: 'syncable' }),\n recent_models: S({ merge: 'array-merge', deviceBound: true, sync: 'never-synced' }),\n recent_mentions: S({ merge: 'array-merge', sync: 'never-synced' }),\n shortcuts: S({ merge: 'array-merge', sync: 'never-synced' }),\n feedback_repo: S({ sync: 'local-only' }),\n // RFC-430 §2.1/§2.6:反馈面板平台裁决与 Coding 平台定位。同 feedback_repo 的\n // `local-only`——仓库/平台指向随机器与网络环境而异(base_url 是企业内网地址),\n // 不跨设备同步。\n feedback_platform: S({ sync: 'local-only' }),\n feedback_coding_base_url: S({ sync: 'local-only' }),\n feedback_coding_repo: S({ sync: 'local-only' }),\n keybinding_overrides: S({ merge: 'record-merge', deviceBound: true, sync: 'never-synced' }),\n language: S({ sync: 'syncable' }),\n theme: S({ deviceBound: true, sync: 'never-synced' }),\n theme_overrides: S({ deviceBound: true, sync: 'never-synced' }),\n theme_preset: S({ deviceBound: true, sync: 'never-synced' }),\n nickname: S({ sync: 'syncable' }),\n has_completed_onboarding: S({ sync: 'syncable' }),\n has_completed_wizard: S({ sync: 'syncable' }),\n wizard_dont_show_again: S({ sync: 'syncable' }),\n first_prompt_submitted_at: S({ sync: 'syncable' }),\n thinking_level: S({ sync: 'local-only' }),\n log_level: S({ sync: 'syncable' }),\n tool_preset: S({ sync: 'local-only' }),\n\n // ── Agent/Category 配置——record-merge,不同步 ──\n agents: S({ merge: 'record-merge', sync: 'never-synced' }),\n categories: S({ merge: 'record-merge', sync: 'never-synced' }),\n model_slots: S({ merge: 'record-merge', sync: 'syncable' }),\n provider_overrides: S({ merge: 'record-merge', sync: 'never-synced' }),\n\n // ── 行为调优参数——scalar-override(整体对象替换),不同步 ──\n memory_auto_extract: S({ sync: 'never-synced' }),\n notification: S({ deviceBound: true, sync: 'syncable' }),\n pulse_survey: S({ deviceBound: true, sync: 'never-synced' }),\n history: S({ sync: 'never-synced' }),\n workflow: S({ sync: 'never-synced' }),\n residency: S({ sync: 'never-synced' }),\n resilience: S({ sync: 'never-synced' }),\n\n // ── 安全面字段——scalar-override 或特殊合并,绝不同步 ──\n sandbox: S({ sync: 'never-synced' }),\n permission_mode: S({ sync: 'local-only' }),\n // RFC-405 D4:permissions 按 policy.name 键合并(同 name 高层覆盖低层)。\n permissions: S({ merge: 'array-merge-by-name', sync: 'never-synced' }),\n permission_always_allow: S({ merge: 'array-merge', sync: 'never-synced' }),\n\n // ── MCP / 网络 / 代理——record-merge 或标量,绝不同步 ──\n mcp_servers: S({ merge: 'record-merge', sync: 'never-synced' }),\n proxy: S({ deviceBound: true, sync: 'never-synced' }),\n web_fetch_allowed_hosts: S({ merge: 'array-merge', sync: 'never-synced' }),\n\n // ── 预算字段──标量,不同步 ──\n prompt_output_token_budget: S({ sync: 'local-only' }),\n prompt_wall_clock_budget_ms: S({ sync: 'local-only' }),\n session_cost_budget_usd: S({ sync: 'local-only' }),\n stall_detection: S({ sync: 'never-synced' }),\n todo_continue_max: S({ sync: 'local-only' }),\n\n // ── 禁用列表——array-merge(并集),部分可同步 ──\n disabled_agents: S({ merge: 'array-merge', sync: 'syncable' }),\n disabled_hooks: S({ merge: 'array-merge', sync: 'syncable' }),\n disabled_tools: S({ merge: 'array-merge', sync: 'syncable' }),\n disabled_skills: S({ merge: 'array-merge', sync: 'syncable' }),\n disabled_plugins: S({ merge: 'array-merge', sync: 'never-synced' }),\n\n // ── 插件清单——array-merge,device-bound,绝不同步 ──\n installed_plugins: S({ merge: 'array-merge', deviceBound: true, sync: 'never-synced' }),\n plugin_lifecycle: S({ deviceBound: true, sync: 'never-synced' }),\n\n // ── 其他 ──\n auto_update_check: S({ deviceBound: true, sync: 'syncable' }),\n} as const satisfies Record<SettingKey, FieldMeta>\n\n// ─────────────────────────── 派生视图(取代 4 张手工表) ───────────────────────────\n\n/** 不导出/不导入的字段集合(取代 EXCLUDED_KEYS)。 */\nexport const EXCLUDED_KEYS: ReadonlySet<SettingKey> = new Set(\n (Object.entries(FIELD_META) as Array<[SettingKey, FieldMeta]>)\n .filter(([, meta]) => meta.export === 'exclude')\n .map(([key]) => key),\n)\n\n/** 可跨设备同步的字段集合(取代 SYNCABLE_KEYS)。 */\nexport const SYNCABLE_KEYS: ReadonlySet<SettingKey> = new Set(\n (Object.entries(FIELD_META) as Array<[SettingKey, FieldMeta]>)\n .filter(([, meta]) => meta.sync === 'syncable')\n .map(([key]) => key),\n)\n\n/** 绝不可远端同步的字段集合(取代 NEVER_SYNCED_KEYS)。 */\nexport const NEVER_SYNCED_KEYS: ReadonlySet<string> = new Set(\n (Object.entries(FIELD_META) as Array<[SettingKey, FieldMeta]>)\n .filter(([, meta]) => meta.sync === 'never-synced')\n .map(([key]) => key),\n)\n\n/** 字段静态分类表(取代 FIELD_CLASSIFICATION)。 */\nexport const FIELD_CLASSIFICATION: ReadonlyMap<SettingKey, FieldClassification> = new Map(\n (Object.entries(FIELD_META) as Array<[SettingKey, FieldMeta]>)\n .filter(([, meta]) => meta.export !== 'exclude')\n .map(([key, meta]) => [key, { category: meta.merge, deviceBound: meta.deviceBound }] as const),\n)\n\n/** 单字段的静态分类(与具体 diff 值无关)。 */\nexport interface FieldClassification {\n category: FieldMergeCategory\n deviceBound: boolean\n}\n\n/**\n * 断言字段元数据表覆盖全部 `SETTING_KEYS`。satisfies 已在编译期保证全覆盖,\n * 此函数保留供运行时测试调用(向后兼容现有测试)。\n */\nexport function assertFieldClassificationCoversAllKeys(): void {\n const uncovered = SETTING_KEYS.filter((key) => !FIELD_META[key])\n if (uncovered.length > 0) {\n throw new Error(\n `FIELD_META 遗漏以下 SettingKey(satisfies 门禁应已拦截): ${uncovered.join(', ')}`,\n )\n }\n}\n","/**\n * RFC-052 D2 + RFC-405 D1:可跨机器同步的用户偏好字段白名单。\n * 从 FIELD_META 派生(sync === 'syncable' 的字段集合),不再手工维护。\n */\nexport { SYNCABLE_KEYS, NEVER_SYNCED_KEYS } from './field-meta'\n// re-export filterSyncable(本模块仍保留实现)\nimport { SYNCABLE_KEYS, NEVER_SYNCED_KEYS } from './field-meta'\nimport type { Setting, SettingKey } from './schema'\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","/**\n * project-trust-gate.ts — 项目信任决策层(RFC-421 D3,原 claude-compat project-config-trust.ts).\n *\n * 与 `project-trust.ts`(纯函数层:高危键定义 + sanitize)同域,补齐**决策层**:\n * env 门控 + 交互态判定 + 信任白名单读取 + 工作区信任弹窗纯决策。\n *\n * **归位理由**:原在 `@x-otto/claude-compat`(兼容层),但严格项目配置门是**默认开启**\n * 的宿主核心安全策略,核心安全决策不应住在可选兼容层。决策与执行(sanitize)同域,\n * 消除\"核心安全依赖兼容层存在\"的结构倒置。\n *\n * 信任判定(isProjectTrusted/trustProject/untrustProject/claudeTrustStorePath)也一并迁入:\n * 原 claude-compat/claude-trust.ts 中,但这些函数是通用项目信任(非 Claude 方言),\n * 复用 @x-otto/plugin 的 trust-store(setting→plugin 边已存在)。\n *\n * Claude 方言的信任面(detectClaudeExecutableSurface/evaluateClaudeTrust)留在 compat,\n * 从本模块取底层信任判定函数。\n */\nimport { existsSync, readFileSync } from 'node:fs'\nimport { homedir } from 'node:os'\nimport { join, resolve } from 'node:path'\nimport { OTTO_HOME, OTTO_PROJECT_CONFIG_RELPATH } from '@x-otto/env'\nimport { createLogger, parseJsonc } from '@x-otto/shared'\nimport { isPathTrusted, trustPath, untrustPath } from '@x-otto/plugin'\nimport { sanitizeUntrustedProjectLayer } from './project-trust'\nimport type { Setting } from './schema'\n\nconst logger = createLogger('@x-otto/setting:project-trust-gate')\n\n// ── 信任白名单持久化(原 claude-compat/claude-trust.ts 通用部分)──\n\n/**\n * 信任白名单落盘路径(用户级)。`OTTO_CLAUDE_TRUST_PATH` 可覆盖(测试/隔离用,\n * 避免污染真实 `~/.otto`——参照 auth-store 真路径教训)。调用时求值。\n */\nexport function claudeTrustStorePath(): string {\n return process.env['OTTO_CLAUDE_TRUST_PATH'] || join(OTTO_HOME, 'claude-trust.json')\n}\n\n/** 该项目根是否已被用户显式信任。 */\nexport function isProjectTrusted(\n workspaceDir: string,\n storePath = claudeTrustStorePath(),\n): boolean {\n return isPathTrusted(storePath, workspaceDir)\n}\n\n/** 显式信任某项目根(幂等,持久化)。 */\nexport function trustProject(workspaceDir: string, storePath = claudeTrustStorePath()): void {\n if (trustPath(storePath, workspaceDir, 'project-trust')) {\n logger.info({ root: resolve(workspaceDir) }, 'Project trusted')\n }\n}\n\n/** 撤销对某项目根的信任(持久化)。 */\nexport function untrustProject(workspaceDir: string, storePath = claudeTrustStorePath()): void {\n if (untrustPath(storePath, workspaceDir, 'project-trust')) {\n logger.info({ root: resolve(workspaceDir) }, 'Project trust revoked')\n }\n}\n\n// ── 严格项目配置门控(原 claude-compat/project-config-trust.ts)──\n\n/**\n * 严格项目配置门控是否开启(env-only,项目 config 不可影响,杜绝恶意仓库自关门)。\n * **默认开**(安全默认):克隆仓库的 `.otto/config.json` 高危键默认不生效,须 `/trust trust`。\n * `OTTO_STRICT_PROJECT_CONFIG=0` 是逃生口(关门控、恢复旧的全信任行为)。\n * 注:仅交互态实际剔除(headless fail-open,见 resolveProjectConfigTrusted),故 CI/脚本/测试不受影响。\n */\nexport function isStrictProjectConfigEnabled(): boolean {\n return process.env['OTTO_STRICT_PROJECT_CONFIG'] !== '0'\n}\n\nexport interface ProjectConfigTrustContext {\n /** 是否交互(TUI)态。缺省 false=headless → fail-open 放行。 */\n interactive?: boolean\n}\n\n/**\n * 该 workspace 的项目配置是否受信任。\n * 严格模式关 → 恒 `true`;headless → 恒 `true`(fail-open);交互+严格 → 取信任白名单判定。\n */\nexport function resolveProjectConfigTrusted(\n workspaceDir: string,\n ctx: ProjectConfigTrustContext = {},\n): boolean {\n if (!isStrictProjectConfigEnabled()) return true\n if (!ctx.interactive) return true\n return isProjectTrusted(workspaceDir)\n}\n\nexport interface ProjectConfigGating {\n /** 项目 `.otto/config.json` 实际含有的高危键(空 = 无可门控内容)。 */\n highRiskKeys: string[]\n /** 这些高危键当前是否被剔除(严格开 + 交互 + 未信任)。 */\n gated: boolean\n}\n\n/**\n * 探测项目 `.otto/config.json` 的高危键门控状态(供 `/compat status` 呈现)。\n * 严格模式关 → 返空(特性未启用,无需呈现噪声)。读文件失败/无文件 → 返空。\n */\nexport function detectProjectConfigGating(\n workspaceDir: string,\n ctx: ProjectConfigTrustContext = {},\n): ProjectConfigGating {\n if (!isStrictProjectConfigEnabled()) return { highRiskKeys: [], gated: false }\n let raw: Partial<Setting>\n try {\n const content = readFileSync(join(workspaceDir, OTTO_PROJECT_CONFIG_RELPATH), 'utf-8')\n raw = parseJsonc<Partial<Setting>>(content)\n } catch {\n return { highRiskKeys: [], gated: false }\n }\n const highRiskKeys = sanitizeUntrustedProjectLayer(raw).stripped\n const gated = highRiskKeys.length > 0 && !resolveProjectConfigTrusted(workspaceDir, ctx)\n return { highRiskKeys, gated }\n}\n\n// ── 工作区信任弹窗纯决策 ──\n\n/** 用户对工作区信任弹窗的选择。 */\nexport type WorkspaceTrustAction = 'trust' | 'readonly' | 'exit'\n\nexport interface WorkspaceTrustPromptDecision {\n /** 是否应弹\"信任此文件夹吗\"。 */\n shouldPrompt: boolean\n /** 解析到的工作区根(信任按它记录)。 */\n root: string\n /** 不弹的原因(诊断/测试)。 */\n reason: 'trusted' | 'not-interactive' | 'strict-off' | 'not-a-project' | 'untrusted'\n}\n\n/**\n * \"真项目\"判定:值得门控的目录。排除 $HOME / 文件系统根(随手 cd 不该弹),\n * 要求有 vcs/config marker(.git/.claude/.mcp.json)——即 otto/git 实际会作用的项目。\n * 纯 scratch 目录(无 marker)不弹,避免过度打扰。\n *\n * 重要:`.otto` 已从标记列表移除(根因修复,真实用户反馈:首次启动不弹信任提示,\n * 第二次才弹)。`.otto/workspace.json` 是 otto 自身建立 workspace 身份时创建的产物,\n * 用它做\"是否真项目\"的判据是自我指涉——任何目录只要跑过一次 otto 就会自动\"自证\"\n * 成真项目。团队共享 workspace.json 场景必然存在 `.git`,已被其余标记覆盖。\n */\nfunction isGateworthyProject(root: string): boolean {\n if (root === resolve(homedir()) || root === resolve('/')) return false\n return ['.git', '.claude', '.mcp.json'].some((m) => existsSync(join(root, m)))\n}\n\n/**\n * 工作区信任弹窗的纯决策:启动时是否要弹\"信任此文件夹吗\"。\n * 弹 ⟺ 交互 且 严格开 且 根未信任 且 是真项目。其余一律不弹。\n * 纯函数:只读 FS + env,不做 IO 副作用、不渲染。\n */\nexport function evaluateWorkspaceTrustPrompt(\n workspaceDir: string,\n ctx: ProjectConfigTrustContext = {},\n): WorkspaceTrustPromptDecision {\n const root = resolve(workspaceDir)\n if (!ctx.interactive) return { shouldPrompt: false, root, reason: 'not-interactive' }\n if (!isStrictProjectConfigEnabled()) return { shouldPrompt: false, root, reason: 'strict-off' }\n if (isProjectTrusted(workspaceDir)) return { shouldPrompt: false, root, reason: 'trusted' }\n if (!isGateworthyProject(root)) return { shouldPrompt: false, root, reason: 'not-a-project' }\n return { shouldPrompt: true, root, reason: 'untrusted' }\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 { EXCLUDED_KEYS, FIELD_CLASSIFICATION } from './field-meta'\nimport type { FieldMergeCategory } from './field-meta'\nimport type { Setting, SettingKey, InstalledPluginEntry } from './schema'\n\n/**\n * RFC-405 D1:MergeCategory 从 field-meta 的 FieldMergeCategory 派生(单源)。\n * 保留原类型名向后兼容(消费方 import MergeCategory)。\n */\nexport type MergeCategory = FieldMergeCategory\n\n// EXCLUDED_KEYS、FIELD_CLASSIFICATION 从 field-meta.ts 导入(派生视图),\n// 此处 re-export 保持消费方 import 不变。\nexport { EXCLUDED_KEYS, FIELD_CLASSIFICATION, assertFieldClassificationCoversAllKeys } from './field-meta'\nexport type { FieldClassification, FieldMeta } from './field-meta'\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' || key === 'provider_overrides') {\n // 新增/变更条目(record-merge:新 key 或同 key 值变化)均视为敏感。\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 === 'array-merge-by-name') {\n // RFC-404 D4:对象数组按 name 键合并(同 name 高层覆盖低层),用于 permissions。\n const fromArr = Array.isArray(diff.from) ? diff.from : []\n const toArr = Array.isArray(diff.to) ? diff.to : []\n const byName = new Map<string, unknown>()\n for (const item of [...fromArr, ...toArr]) {\n if (isPlainObject(item) && typeof item['name'] === 'string') {\n byName.set(item['name'] as string, item)\n }\n }\n result[diff.key] = [...byName.values()]\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.1.0-alpha.13",
3
+ "version": "0.1.0-alpha.15",
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/hook-contracts": "0.1.0-alpha.13",
25
- "@x-otto/prompt": "0.1.0-alpha.13",
26
- "@x-otto/persistence": "0.1.0-alpha.13",
27
- "@x-otto/orchestration-contracts": "0.1.0-alpha.13",
28
- "@x-otto/plugin": "0.1.0-alpha.13",
29
- "@x-otto/shared": "0.1.0-alpha.13",
30
- "@x-otto/env": "0.1.0-alpha.13"
24
+ "@x-otto/hook-contracts": "0.1.0-alpha.15",
25
+ "@x-otto/orchestration-contracts": "0.1.0-alpha.15",
26
+ "@x-otto/shared": "0.1.0-alpha.15",
27
+ "@x-otto/persistence": "0.1.0-alpha.15",
28
+ "@x-otto/env": "0.1.0-alpha.15",
29
+ "@x-otto/plugin": "0.1.0-alpha.15",
30
+ "@x-otto/prompt": "0.1.0-alpha.15"
31
31
  },
32
32
  "devDependencies": {
33
33
  "@types/semver": "7.7.1"