@agimon-ai/doompi-plan 0.0.1-alpha.34 → 0.0.1-alpha.36

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.
@@ -1,2 +1,2 @@
1
- import { PLAN_CONFIG_SECTION_ID, PLAN_SETTINGS, planConfigSections, planSettingByFieldId } from "./schemas/plan/planConfig.cjs";
2
- export { PLAN_CONFIG_SECTION_ID, PLAN_SETTINGS, planConfigSections, planSettingByFieldId };
1
+ import { PLAN_CONFIG_SECTION_ID, PLAN_SETTINGS, PlanModelChoice, planConfigSections, planSettingByFieldId } from "./schemas/plan/planConfig.cjs";
2
+ export { PLAN_CONFIG_SECTION_ID, PLAN_SETTINGS, PlanModelChoice, planConfigSections, planSettingByFieldId };
@@ -1,2 +1,2 @@
1
- import { PLAN_CONFIG_SECTION_ID, PLAN_SETTINGS, planConfigSections, planSettingByFieldId } from "./schemas/plan/planConfig.mjs";
2
- export { PLAN_CONFIG_SECTION_ID, PLAN_SETTINGS, planConfigSections, planSettingByFieldId };
1
+ import { PLAN_CONFIG_SECTION_ID, PLAN_SETTINGS, PlanModelChoice, planConfigSections, planSettingByFieldId } from "./schemas/plan/planConfig.mjs";
2
+ export { PLAN_CONFIG_SECTION_ID, PLAN_SETTINGS, PlanModelChoice, planConfigSections, planSettingByFieldId };
@@ -1,2 +1,2 @@
1
- let e=require("@agimon-ai/doompi-config"),t=require("@agimon-ai/doompi-extension-contracts/config");const n=`planning`,r=[`modes`,n],i=`main`,a=`subagents`,o=`inherit the session model`,s=`inherit`,c=[{id:`main.model`,label:`main model`,keyPath:[...r,i,`model`],placeholder:o,detail:`Model the main agent switches to while plan mode is on.`,read:e=>e?.main?.model},{id:`main.thinking`,label:`main thinking`,keyPath:[...r,i,`thinking`],placeholder:s,detail:`Thinking level appended to the main model while planning.`,options:e.DOOM_PLANNING_THINKING_LEVELS,read:e=>e?.main?.thinking},{id:`subagents.model`,label:`subagent model`,keyPath:[...r,a,`model`],placeholder:o,detail:`Model forced onto delegated planning subagents.`,read:e=>e?.subagents?.model},{id:`subagents.thinking`,label:`subagent thinking`,keyPath:[...r,a,`thinking`],placeholder:s,detail:`Thinking level appended to the subagent model.`,options:e.DOOM_PLANNING_THINKING_LEVELS,read:e=>e?.subagents?.thinking},{id:`plansdirectory`,label:`plans directory`,keyPath:[...r,`plansDirectory`],placeholder:`~/.pi/plans`,detail:`Absolute, repo-relative, or under ~. Written plans land here.`,read:e=>e?.plansDirectory}];function l(e){return c.find(t=>t.id===e)}function u(e){return e.options?.map(e=>({id:e,label:e,action:t.CONFIG_ACTION.set}))}function d(e,t){let r=c.map(t=>{let n=t.read(e),r=u(t);return{id:t.id,label:t.label,kind:r?`enum`:`text`,keyPath:t.keyPath.join(`.`),placeholder:t.placeholder,detail:t.detail,...n?{value:n}:{},...r?{choices:r}:{}}});return[{id:n,title:`planning`,order:30,detail:`plan mode`,fields:r,...t?{notice:t,noticeLevel:`error`}:{}}]}exports.PLAN_CONFIG_SECTION_ID=n,exports.PLAN_SETTINGS=c,exports.planConfigSections=d,exports.planSettingByFieldId=l;
1
+ let e=require("@agimon-ai/doompi-config"),t=require("@agimon-ai/doompi-extension-contracts/config");const n=`planning`,r=[`modes`,n],i=`main`,a=`subagents`,o=`inherit the session model`,s=`inherit`,c=[{id:`main.model`,label:`main model`,keyPath:[...r,i,`model`],placeholder:o,detail:`Model the main agent switches to while plan mode is on.`,models:!0,read:e=>e?.main?.model},{id:`main.thinking`,label:`main thinking`,keyPath:[...r,i,`thinking`],placeholder:s,detail:`Thinking level appended to the main model while planning.`,options:e.DOOM_PLANNING_THINKING_LEVELS,read:e=>e?.main?.thinking},{id:`subagents.model`,label:`subagent model`,keyPath:[...r,a,`model`],placeholder:o,detail:`Model forced onto delegated planning subagents.`,models:!0,read:e=>e?.subagents?.model},{id:`subagents.thinking`,label:`subagent thinking`,keyPath:[...r,a,`thinking`],placeholder:s,detail:`Thinking level appended to the subagent model.`,options:e.DOOM_PLANNING_THINKING_LEVELS,read:e=>e?.subagents?.thinking},{id:`plansdirectory`,label:`plans directory`,keyPath:[...r,`plansDirectory`],placeholder:`~/.pi/plans`,detail:`Absolute, repo-relative, or under ~. Written plans land here.`,read:e=>e?.plansDirectory}];function l(e){return c.find(t=>t.id===e)}function u(e){return[{id:`inherit`,label:o,detail:`Leaves the setting unset.`,action:t.CONFIG_ACTION.clear},...e.map(e=>({id:`${e.provider}/${e.id}`,label:e.id,group:e.provider,detail:e.provider,action:t.CONFIG_ACTION.set}))]}function d(e,n){if(e.options)return{kind:`enum`,choices:e.options.map(e=>({id:e,label:e,action:t.CONFIG_ACTION.set}))};if(!(!e.models||n.length===0))return{kind:`choice`,choices:u(n)}}function f(e,t,r=[]){let i=c.map(t=>{let n=t.read(e),i=d(t,r);return{id:t.id,label:t.label,kind:i?.kind??`text`,keyPath:t.keyPath.join(`.`),placeholder:t.placeholder,detail:t.detail,...n?{value:n}:{},...i?{choices:i.choices}:{}}});return[{id:n,title:`planning`,order:30,detail:`plan mode`,fields:i,...t?{notice:t,noticeLevel:`error`}:{}}]}exports.PLAN_CONFIG_SECTION_ID=n,exports.PLAN_SETTINGS=c,exports.planConfigSections=f,exports.planSettingByFieldId=l;
2
2
  //# sourceMappingURL=planConfig.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"planConfig.cjs","names":["DOOM_PLANNING_THINKING_LEVELS","CONFIG_ACTION"],"sources":["../../../src/schemas/plan/planConfig.ts"],"sourcesContent":["/**\n * Plan mode's slice of the `SPC e c` config panel.\n *\n * Plan mode reads `modes.planning` from disk on every activation, so an edit\n * here lands on the next time the mode is turned on with no restart and nothing\n * to invalidate.\n *\n * The descriptor table is the whole design: the panel renders it and the write\n * handler looks the key path back up in it, so adding a setting means adding a\n * row rather than a case.\n */\n\nimport { DOOM_PLANNING_THINKING_LEVELS } from '@agimon-ai/doompi-config';\nimport {\n CONFIG_ACTION,\n type ConfigChoice,\n type ConfigField,\n type ConfigSection,\n} from '@agimon-ai/doompi-extension-contracts/config';\nimport type { PlanningModeConfig } from './config.ts';\n\nexport const PLAN_CONFIG_SECTION_ID = 'planning';\nconst SECTION_ORDER = 30;\nconst PLANNING_PATH = ['modes', PLAN_CONFIG_SECTION_ID] as const;\nconst MAIN_KEY = 'main';\nconst SUBAGENTS_KEY = 'subagents';\n/** Both agents fall back to the session's own settings when left unset. */\nconst INHERIT_MODEL = 'inherit the session model';\nconst INHERIT_THINKING = 'inherit';\n\ninterface PlanSettingDescriptor {\n readonly id: string;\n readonly label: string;\n readonly keyPath: readonly string[];\n readonly placeholder: string;\n readonly detail: string;\n /** Present for a closed set; the panel offers these rather than free text. */\n readonly options?: readonly string[];\n readonly read: (config: PlanningModeConfig | undefined) => string | undefined;\n}\n\nexport const PLAN_SETTINGS: readonly PlanSettingDescriptor[] = [\n {\n id: 'main.model',\n label: 'main model',\n keyPath: [...PLANNING_PATH, MAIN_KEY, 'model'],\n placeholder: INHERIT_MODEL,\n detail: 'Model the main agent switches to while plan mode is on.',\n read: (config) => config?.main?.model,\n },\n {\n id: 'main.thinking',\n label: 'main thinking',\n keyPath: [...PLANNING_PATH, MAIN_KEY, 'thinking'],\n placeholder: INHERIT_THINKING,\n detail: 'Thinking level appended to the main model while planning.',\n options: DOOM_PLANNING_THINKING_LEVELS,\n read: (config) => config?.main?.thinking,\n },\n {\n id: 'subagents.model',\n label: 'subagent model',\n keyPath: [...PLANNING_PATH, SUBAGENTS_KEY, 'model'],\n placeholder: INHERIT_MODEL,\n detail: 'Model forced onto delegated planning subagents.',\n read: (config) => config?.subagents?.model,\n },\n {\n id: 'subagents.thinking',\n label: 'subagent thinking',\n keyPath: [...PLANNING_PATH, SUBAGENTS_KEY, 'thinking'],\n placeholder: INHERIT_THINKING,\n detail: 'Thinking level appended to the subagent model.',\n options: DOOM_PLANNING_THINKING_LEVELS,\n read: (config) => config?.subagents?.thinking,\n },\n {\n id: 'plansdirectory',\n label: 'plans directory',\n keyPath: [...PLANNING_PATH, 'plansDirectory'],\n placeholder: '~/.pi/plans',\n detail: 'Absolute, repo-relative, or under ~. Written plans land here.',\n read: (config) => config?.plansDirectory,\n },\n];\n\nexport function planSettingByFieldId(fieldId: string): PlanSettingDescriptor | undefined {\n return PLAN_SETTINGS.find((setting) => setting.id === fieldId);\n}\n\nfunction choicesFor(setting: PlanSettingDescriptor): ConfigChoice[] | undefined {\n // `set` rather than a bespoke action: choosing a level is the same write as\n // typing one, so it shares the handler.\n return setting.options?.map((option) => ({ id: option, label: option, action: CONFIG_ACTION.set }));\n}\n\nexport function planConfigSections(config: PlanningModeConfig | undefined, notice?: string): readonly ConfigSection[] {\n const fields: ConfigField[] = PLAN_SETTINGS.map((setting) => {\n const value = setting.read(config);\n const choices = choicesFor(setting);\n return {\n id: setting.id,\n label: setting.label,\n kind: choices ? ('enum' as const) : ('text' as const),\n keyPath: setting.keyPath.join('.'),\n placeholder: setting.placeholder,\n detail: setting.detail,\n ...(value ? { value } : {}),\n ...(choices ? { choices } : {}),\n };\n });\n return [\n {\n id: PLAN_CONFIG_SECTION_ID,\n title: 'planning',\n order: SECTION_ORDER,\n detail: 'plan mode',\n fields,\n ...(notice ? { notice, noticeLevel: 'error' as const } : {}),\n },\n ];\n}\n"],"mappings":"oGAqBA,MAAa,EAAyB,WAEhC,EAAgB,CAAC,QAAS,CAAsB,EAChD,EAAW,OACX,EAAgB,YAEhB,EAAgB,4BAChB,EAAmB,UAaZ,EAAkD,CAC7D,CACE,GAAI,aACJ,MAAO,aACP,QAAS,CAAC,GAAG,EAAe,EAAU,OAAO,EAC7C,YAAa,EACb,OAAQ,0DACR,KAAO,GAAW,GAAQ,MAAM,KAClC,EACA,CACE,GAAI,gBACJ,MAAO,gBACP,QAAS,CAAC,GAAG,EAAe,EAAU,UAAU,EAChD,YAAa,EACb,OAAQ,4DACR,QAASA,EAAAA,8BACT,KAAO,GAAW,GAAQ,MAAM,QAClC,EACA,CACE,GAAI,kBACJ,MAAO,iBACP,QAAS,CAAC,GAAG,EAAe,EAAe,OAAO,EAClD,YAAa,EACb,OAAQ,kDACR,KAAO,GAAW,GAAQ,WAAW,KACvC,EACA,CACE,GAAI,qBACJ,MAAO,oBACP,QAAS,CAAC,GAAG,EAAe,EAAe,UAAU,EACrD,YAAa,EACb,OAAQ,iDACR,QAASA,EAAAA,8BACT,KAAO,GAAW,GAAQ,WAAW,QACvC,EACA,CACE,GAAI,iBACJ,MAAO,kBACP,QAAS,CAAC,GAAG,EAAe,gBAAgB,EAC5C,YAAa,cACb,OAAQ,gEACR,KAAO,GAAW,GAAQ,cAC5B,CACF,EAEA,SAAgB,EAAqB,EAAoD,CACvF,OAAO,EAAc,KAAM,GAAY,EAAQ,KAAO,CAAO,CAC/D,CAEA,SAAS,EAAW,EAA4D,CAG9E,OAAO,EAAQ,SAAS,IAAK,IAAY,CAAE,GAAI,EAAQ,MAAO,EAAQ,OAAQC,EAAAA,cAAc,GAAI,EAAE,CACpG,CAEA,SAAgB,EAAmB,EAAwC,EAA2C,CACpH,IAAM,EAAwB,EAAc,IAAK,GAAY,CAC3D,IAAM,EAAQ,EAAQ,KAAK,CAAM,EAC3B,EAAU,EAAW,CAAO,EAClC,MAAO,CACL,GAAI,EAAQ,GACZ,MAAO,EAAQ,MACf,KAAM,EAAW,OAAoB,OACrC,QAAS,EAAQ,QAAQ,KAAK,GAAG,EACjC,YAAa,EAAQ,YACrB,OAAQ,EAAQ,OAChB,GAAI,EAAQ,CAAE,OAAM,EAAI,CAAC,EACzB,GAAI,EAAU,CAAE,SAAQ,EAAI,CAAC,CAC/B,CACF,CAAC,EACD,MAAO,CACL,CACE,GAAI,EACJ,MAAO,WACP,MAAO,GACP,OAAQ,YACR,SACA,GAAI,EAAS,CAAE,SAAQ,YAAa,OAAiB,EAAI,CAAC,CAC5D,CACF,CACF"}
1
+ {"version":3,"file":"planConfig.cjs","names":["DOOM_PLANNING_THINKING_LEVELS","CONFIG_ACTION"],"sources":["../../../src/schemas/plan/planConfig.ts"],"sourcesContent":["/**\n * Plan mode's slice of the `SPC e c` config panel.\n *\n * Plan mode reads `modes.planning` from disk on every activation, so an edit\n * here lands on the next time the mode is turned on with no restart and nothing\n * to invalidate.\n *\n * The descriptor table is the whole design: the panel renders it and the write\n * handler looks the key path back up in it, so adding a setting means adding a\n * row rather than a case.\n */\n\nimport { DOOM_PLANNING_THINKING_LEVELS } from '@agimon-ai/doompi-config';\nimport {\n CONFIG_ACTION,\n type ConfigChoice,\n type ConfigField,\n type ConfigSection,\n} from '@agimon-ai/doompi-extension-contracts/config';\nimport type { PlanningModeConfig } from './config.ts';\n\nexport const PLAN_CONFIG_SECTION_ID = 'planning';\nconst SECTION_ORDER = 30;\nconst PLANNING_PATH = ['modes', PLAN_CONFIG_SECTION_ID] as const;\nconst MAIN_KEY = 'main';\nconst SUBAGENTS_KEY = 'subagents';\n/** Both agents fall back to the session's own settings when left unset. */\nconst INHERIT_MODEL = 'inherit the session model';\nconst INHERIT_THINKING = 'inherit';\n/** Selecting this clears the setting, which is how the file spells \"inherit\". */\nconst INHERIT_CHOICE_ID = 'inherit';\n\ninterface PlanSettingDescriptor {\n readonly id: string;\n readonly label: string;\n readonly keyPath: readonly string[];\n readonly placeholder: string;\n readonly detail: string;\n /** Present for a closed set; the panel offers these rather than free text. */\n readonly options?: readonly string[];\n /** Offers the session's own models rather than free text. See `planConfigSections`. */\n readonly models?: boolean;\n readonly read: (config: PlanningModeConfig | undefined) => string | undefined;\n}\n\nexport const PLAN_SETTINGS: readonly PlanSettingDescriptor[] = [\n {\n id: 'main.model',\n label: 'main model',\n keyPath: [...PLANNING_PATH, MAIN_KEY, 'model'],\n placeholder: INHERIT_MODEL,\n detail: 'Model the main agent switches to while plan mode is on.',\n models: true,\n read: (config) => config?.main?.model,\n },\n {\n id: 'main.thinking',\n label: 'main thinking',\n keyPath: [...PLANNING_PATH, MAIN_KEY, 'thinking'],\n placeholder: INHERIT_THINKING,\n detail: 'Thinking level appended to the main model while planning.',\n options: DOOM_PLANNING_THINKING_LEVELS,\n read: (config) => config?.main?.thinking,\n },\n {\n id: 'subagents.model',\n label: 'subagent model',\n keyPath: [...PLANNING_PATH, SUBAGENTS_KEY, 'model'],\n placeholder: INHERIT_MODEL,\n detail: 'Model forced onto delegated planning subagents.',\n models: true,\n read: (config) => config?.subagents?.model,\n },\n {\n id: 'subagents.thinking',\n label: 'subagent thinking',\n keyPath: [...PLANNING_PATH, SUBAGENTS_KEY, 'thinking'],\n placeholder: INHERIT_THINKING,\n detail: 'Thinking level appended to the subagent model.',\n options: DOOM_PLANNING_THINKING_LEVELS,\n read: (config) => config?.subagents?.thinking,\n },\n {\n id: 'plansdirectory',\n label: 'plans directory',\n keyPath: [...PLANNING_PATH, 'plansDirectory'],\n placeholder: '~/.pi/plans',\n detail: 'Absolute, repo-relative, or under ~. Written plans land here.',\n read: (config) => config?.plansDirectory,\n },\n];\n\nexport function planSettingByFieldId(fieldId: string): PlanSettingDescriptor | undefined {\n return PLAN_SETTINGS.find((setting) => setting.id === fieldId);\n}\n\n/** A model this session could actually switch to, as the panel offers it. */\nexport interface PlanModelChoice {\n readonly provider: string;\n readonly id: string;\n}\n\n/**\n * The models the session can reach, plus the entry that clears the setting.\n *\n * Offered as a list because a model spec is `provider/id` and typing one from\n * memory is how a setting ends up naming a model this session cannot use. The\n * inherit entry CLEARS rather than writing a value: unset is what \"use the\n * session's own model\" means in the file.\n */\nfunction modelChoices(models: readonly PlanModelChoice[]): ConfigChoice[] {\n const inherit: ConfigChoice = {\n id: INHERIT_CHOICE_ID,\n label: INHERIT_MODEL,\n detail: 'Leaves the setting unset.',\n action: CONFIG_ACTION.clear,\n };\n return [\n inherit,\n ...models.map((model): ConfigChoice => ({\n id: `${model.provider}/${model.id}`,\n label: model.id,\n group: model.provider,\n detail: model.provider,\n action: CONFIG_ACTION.set,\n })),\n ];\n}\n\nfunction choicesFor(\n setting: PlanSettingDescriptor,\n models: readonly PlanModelChoice[],\n): { choices: ConfigChoice[]; kind: 'enum' | 'choice' } | undefined {\n // `set` rather than a bespoke action: choosing a level is the same write as\n // typing one, so it shares the handler.\n if (setting.options) {\n return {\n kind: 'enum',\n choices: setting.options.map((option) => ({ id: option, label: option, action: CONFIG_ACTION.set })),\n };\n }\n // A model setting with nothing to offer stays free text rather than opening\n // an empty list: a session with no authenticated provider can still be edited.\n if (!setting.models || models.length === 0) return undefined;\n return { kind: 'choice', choices: modelChoices(models) };\n}\n\nexport function planConfigSections(\n config: PlanningModeConfig | undefined,\n notice?: string,\n models: readonly PlanModelChoice[] = [],\n): readonly ConfigSection[] {\n const fields: ConfigField[] = PLAN_SETTINGS.map((setting) => {\n const value = setting.read(config);\n const offered = choicesFor(setting, models);\n return {\n id: setting.id,\n label: setting.label,\n kind: offered?.kind ?? ('text' as const),\n keyPath: setting.keyPath.join('.'),\n placeholder: setting.placeholder,\n detail: setting.detail,\n ...(value ? { value } : {}),\n ...(offered ? { choices: offered.choices } : {}),\n };\n });\n return [\n {\n id: PLAN_CONFIG_SECTION_ID,\n title: 'planning',\n order: SECTION_ORDER,\n detail: 'plan mode',\n fields,\n ...(notice ? { notice, noticeLevel: 'error' as const } : {}),\n },\n ];\n}\n"],"mappings":"oGAqBA,MAAa,EAAyB,WAEhC,EAAgB,CAAC,QAAS,CAAsB,EAChD,EAAW,OACX,EAAgB,YAEhB,EAAgB,4BAChB,EAAmB,UAiBZ,EAAkD,CAC7D,CACE,GAAI,aACJ,MAAO,aACP,QAAS,CAAC,GAAG,EAAe,EAAU,OAAO,EAC7C,YAAa,EACb,OAAQ,0DACR,OAAQ,GACR,KAAO,GAAW,GAAQ,MAAM,KAClC,EACA,CACE,GAAI,gBACJ,MAAO,gBACP,QAAS,CAAC,GAAG,EAAe,EAAU,UAAU,EAChD,YAAa,EACb,OAAQ,4DACR,QAASA,EAAAA,8BACT,KAAO,GAAW,GAAQ,MAAM,QAClC,EACA,CACE,GAAI,kBACJ,MAAO,iBACP,QAAS,CAAC,GAAG,EAAe,EAAe,OAAO,EAClD,YAAa,EACb,OAAQ,kDACR,OAAQ,GACR,KAAO,GAAW,GAAQ,WAAW,KACvC,EACA,CACE,GAAI,qBACJ,MAAO,oBACP,QAAS,CAAC,GAAG,EAAe,EAAe,UAAU,EACrD,YAAa,EACb,OAAQ,iDACR,QAASA,EAAAA,8BACT,KAAO,GAAW,GAAQ,WAAW,QACvC,EACA,CACE,GAAI,iBACJ,MAAO,kBACP,QAAS,CAAC,GAAG,EAAe,gBAAgB,EAC5C,YAAa,cACb,OAAQ,gEACR,KAAO,GAAW,GAAQ,cAC5B,CACF,EAEA,SAAgB,EAAqB,EAAoD,CACvF,OAAO,EAAc,KAAM,GAAY,EAAQ,KAAO,CAAO,CAC/D,CAgBA,SAAS,EAAa,EAAoD,CAOxE,MAAO,CACL,CANA,GAAI,UACJ,MAAO,EACP,OAAQ,4BACR,OAAQC,EAAAA,cAAc,KAGhB,EACN,GAAG,EAAO,IAAK,IAAyB,CACtC,GAAI,GAAG,EAAM,SAAS,GAAG,EAAM,KAC/B,MAAO,EAAM,GACb,MAAO,EAAM,SACb,OAAQ,EAAM,SACd,OAAQA,EAAAA,cAAc,GACxB,EAAE,CACJ,CACF,CAEA,SAAS,EACP,EACA,EACkE,CAGlE,GAAI,EAAQ,QACV,MAAO,CACL,KAAM,OACN,QAAS,EAAQ,QAAQ,IAAK,IAAY,CAAE,GAAI,EAAQ,MAAO,EAAQ,OAAQA,EAAAA,cAAc,GAAI,EAAE,CACrG,EAIE,MAAC,EAAQ,QAAU,EAAO,SAAW,GACzC,MAAO,CAAE,KAAM,SAAU,QAAS,EAAa,CAAM,CAAE,CACzD,CAEA,SAAgB,EACd,EACA,EACA,EAAqC,CAAC,EACZ,CAC1B,IAAM,EAAwB,EAAc,IAAK,GAAY,CAC3D,IAAM,EAAQ,EAAQ,KAAK,CAAM,EAC3B,EAAU,EAAW,EAAS,CAAM,EAC1C,MAAO,CACL,GAAI,EAAQ,GACZ,MAAO,EAAQ,MACf,KAAM,GAAS,MAAS,OACxB,QAAS,EAAQ,QAAQ,KAAK,GAAG,EACjC,YAAa,EAAQ,YACrB,OAAQ,EAAQ,OAChB,GAAI,EAAQ,CAAE,OAAM,EAAI,CAAC,EACzB,GAAI,EAAU,CAAE,QAAS,EAAQ,OAAQ,EAAI,CAAC,CAChD,CACF,CAAC,EACD,MAAO,CACL,CACE,GAAI,EACJ,MAAO,WACP,MAAO,GACP,OAAQ,YACR,SACA,GAAI,EAAS,CAAE,SAAQ,YAAa,OAAiB,EAAI,CAAC,CAC5D,CACF,CACF"}
@@ -10,11 +10,18 @@ interface PlanSettingDescriptor {
10
10
  readonly detail: string;
11
11
  /** Present for a closed set; the panel offers these rather than free text. */
12
12
  readonly options?: readonly string[];
13
+ /** Offers the session's own models rather than free text. See `planConfigSections`. */
14
+ readonly models?: boolean;
13
15
  readonly read: (config: PlanningModeConfig | undefined) => string | undefined;
14
16
  }
15
17
  declare const PLAN_SETTINGS: readonly PlanSettingDescriptor[];
16
18
  declare function planSettingByFieldId(fieldId: string): PlanSettingDescriptor | undefined;
17
- declare function planConfigSections(config: PlanningModeConfig | undefined, notice?: string): readonly ConfigSection[];
19
+ /** A model this session could actually switch to, as the panel offers it. */
20
+ interface PlanModelChoice {
21
+ readonly provider: string;
22
+ readonly id: string;
23
+ }
24
+ declare function planConfigSections(config: PlanningModeConfig | undefined, notice?: string, models?: readonly PlanModelChoice[]): readonly ConfigSection[];
18
25
  //#endregion
19
- export { PLAN_CONFIG_SECTION_ID, PLAN_SETTINGS, planConfigSections, planSettingByFieldId };
26
+ export { PLAN_CONFIG_SECTION_ID, PLAN_SETTINGS, PlanModelChoice, planConfigSections, planSettingByFieldId };
20
27
  //# sourceMappingURL=planConfig.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"planConfig.d.cts","names":[],"sources":["../../../src/schemas/plan/planConfig.ts"],"mappings":";;;cAqBa;UASH;WACC;WACA;WACA;WACA;WACA;;WAEA;WACA,OAAO,QAAQ;;cAGb,wBAAwB;iBA6CrB,qBAAqB,kBAAkB;iBAUvC,mBAAmB,QAAQ,gCAAgC,2BAA2B"}
1
+ {"version":3,"file":"planConfig.d.cts","names":[],"sources":["../../../src/schemas/plan/planConfig.ts"],"mappings":";;;cAqBa;UAWH;WACC;WACA;WACA;WACA;WACA;;WAEA;;WAEA;WACA,OAAO,QAAQ;;cAGb,wBAAwB;iBA+CrB,qBAAqB,kBAAkB;;UAKtC;WACN;WACA;;iBAgDK,mBACd,QAAQ,gCACR,iBACA,kBAAiB,6BACP"}
@@ -10,11 +10,18 @@ interface PlanSettingDescriptor {
10
10
  readonly detail: string;
11
11
  /** Present for a closed set; the panel offers these rather than free text. */
12
12
  readonly options?: readonly string[];
13
+ /** Offers the session's own models rather than free text. See `planConfigSections`. */
14
+ readonly models?: boolean;
13
15
  readonly read: (config: PlanningModeConfig | undefined) => string | undefined;
14
16
  }
15
17
  declare const PLAN_SETTINGS: readonly PlanSettingDescriptor[];
16
18
  declare function planSettingByFieldId(fieldId: string): PlanSettingDescriptor | undefined;
17
- declare function planConfigSections(config: PlanningModeConfig | undefined, notice?: string): readonly ConfigSection[];
19
+ /** A model this session could actually switch to, as the panel offers it. */
20
+ interface PlanModelChoice {
21
+ readonly provider: string;
22
+ readonly id: string;
23
+ }
24
+ declare function planConfigSections(config: PlanningModeConfig | undefined, notice?: string, models?: readonly PlanModelChoice[]): readonly ConfigSection[];
18
25
  //#endregion
19
- export { PLAN_CONFIG_SECTION_ID, PLAN_SETTINGS, planConfigSections, planSettingByFieldId };
26
+ export { PLAN_CONFIG_SECTION_ID, PLAN_SETTINGS, PlanModelChoice, planConfigSections, planSettingByFieldId };
20
27
  //# sourceMappingURL=planConfig.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"planConfig.d.mts","names":[],"sources":["../../../src/schemas/plan/planConfig.ts"],"mappings":";;;cAqBa;UASH;WACC;WACA;WACA;WACA;WACA;;WAEA;WACA,OAAO,QAAQ;;cAGb,wBAAwB;iBA6CrB,qBAAqB,kBAAkB;iBAUvC,mBAAmB,QAAQ,gCAAgC,2BAA2B"}
1
+ {"version":3,"file":"planConfig.d.mts","names":[],"sources":["../../../src/schemas/plan/planConfig.ts"],"mappings":";;;cAqBa;UAWH;WACC;WACA;WACA;WACA;WACA;;WAEA;;WAEA;WACA,OAAO,QAAQ;;cAGb,wBAAwB;iBA+CrB,qBAAqB,kBAAkB;;UAKtC;WACN;WACA;;iBAgDK,mBACd,QAAQ,gCACR,iBACA,kBAAiB,6BACP"}
@@ -1,2 +1,2 @@
1
- import{DOOM_PLANNING_THINKING_LEVELS as e}from"@agimon-ai/doompi-config";import{CONFIG_ACTION as t}from"@agimon-ai/doompi-extension-contracts/config";const n=`planning`,r=[`modes`,n],i=`main`,a=`subagents`,o=`inherit the session model`,s=`inherit`,c=[{id:`main.model`,label:`main model`,keyPath:[...r,i,`model`],placeholder:o,detail:`Model the main agent switches to while plan mode is on.`,read:e=>e?.main?.model},{id:`main.thinking`,label:`main thinking`,keyPath:[...r,i,`thinking`],placeholder:s,detail:`Thinking level appended to the main model while planning.`,options:e,read:e=>e?.main?.thinking},{id:`subagents.model`,label:`subagent model`,keyPath:[...r,a,`model`],placeholder:o,detail:`Model forced onto delegated planning subagents.`,read:e=>e?.subagents?.model},{id:`subagents.thinking`,label:`subagent thinking`,keyPath:[...r,a,`thinking`],placeholder:s,detail:`Thinking level appended to the subagent model.`,options:e,read:e=>e?.subagents?.thinking},{id:`plansdirectory`,label:`plans directory`,keyPath:[...r,`plansDirectory`],placeholder:`~/.pi/plans`,detail:`Absolute, repo-relative, or under ~. Written plans land here.`,read:e=>e?.plansDirectory}];function l(e){return c.find(t=>t.id===e)}function u(e){return e.options?.map(e=>({id:e,label:e,action:t.set}))}function d(e,t){let r=c.map(t=>{let n=t.read(e),r=u(t);return{id:t.id,label:t.label,kind:r?`enum`:`text`,keyPath:t.keyPath.join(`.`),placeholder:t.placeholder,detail:t.detail,...n?{value:n}:{},...r?{choices:r}:{}}});return[{id:n,title:`planning`,order:30,detail:`plan mode`,fields:r,...t?{notice:t,noticeLevel:`error`}:{}}]}export{n as PLAN_CONFIG_SECTION_ID,c as PLAN_SETTINGS,d as planConfigSections,l as planSettingByFieldId};
1
+ import{DOOM_PLANNING_THINKING_LEVELS as e}from"@agimon-ai/doompi-config";import{CONFIG_ACTION as t}from"@agimon-ai/doompi-extension-contracts/config";const n=`planning`,r=[`modes`,n],i=`main`,a=`subagents`,o=`inherit the session model`,s=`inherit`,c=[{id:`main.model`,label:`main model`,keyPath:[...r,i,`model`],placeholder:o,detail:`Model the main agent switches to while plan mode is on.`,models:!0,read:e=>e?.main?.model},{id:`main.thinking`,label:`main thinking`,keyPath:[...r,i,`thinking`],placeholder:s,detail:`Thinking level appended to the main model while planning.`,options:e,read:e=>e?.main?.thinking},{id:`subagents.model`,label:`subagent model`,keyPath:[...r,a,`model`],placeholder:o,detail:`Model forced onto delegated planning subagents.`,models:!0,read:e=>e?.subagents?.model},{id:`subagents.thinking`,label:`subagent thinking`,keyPath:[...r,a,`thinking`],placeholder:s,detail:`Thinking level appended to the subagent model.`,options:e,read:e=>e?.subagents?.thinking},{id:`plansdirectory`,label:`plans directory`,keyPath:[...r,`plansDirectory`],placeholder:`~/.pi/plans`,detail:`Absolute, repo-relative, or under ~. Written plans land here.`,read:e=>e?.plansDirectory}];function l(e){return c.find(t=>t.id===e)}function u(e){return[{id:`inherit`,label:o,detail:`Leaves the setting unset.`,action:t.clear},...e.map(e=>({id:`${e.provider}/${e.id}`,label:e.id,group:e.provider,detail:e.provider,action:t.set}))]}function d(e,n){if(e.options)return{kind:`enum`,choices:e.options.map(e=>({id:e,label:e,action:t.set}))};if(!(!e.models||n.length===0))return{kind:`choice`,choices:u(n)}}function f(e,t,r=[]){let i=c.map(t=>{let n=t.read(e),i=d(t,r);return{id:t.id,label:t.label,kind:i?.kind??`text`,keyPath:t.keyPath.join(`.`),placeholder:t.placeholder,detail:t.detail,...n?{value:n}:{},...i?{choices:i.choices}:{}}});return[{id:n,title:`planning`,order:30,detail:`plan mode`,fields:i,...t?{notice:t,noticeLevel:`error`}:{}}]}export{n as PLAN_CONFIG_SECTION_ID,c as PLAN_SETTINGS,f as planConfigSections,l as planSettingByFieldId};
2
2
  //# sourceMappingURL=planConfig.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"planConfig.mjs","names":[],"sources":["../../../src/schemas/plan/planConfig.ts"],"sourcesContent":["/**\n * Plan mode's slice of the `SPC e c` config panel.\n *\n * Plan mode reads `modes.planning` from disk on every activation, so an edit\n * here lands on the next time the mode is turned on with no restart and nothing\n * to invalidate.\n *\n * The descriptor table is the whole design: the panel renders it and the write\n * handler looks the key path back up in it, so adding a setting means adding a\n * row rather than a case.\n */\n\nimport { DOOM_PLANNING_THINKING_LEVELS } from '@agimon-ai/doompi-config';\nimport {\n CONFIG_ACTION,\n type ConfigChoice,\n type ConfigField,\n type ConfigSection,\n} from '@agimon-ai/doompi-extension-contracts/config';\nimport type { PlanningModeConfig } from './config.ts';\n\nexport const PLAN_CONFIG_SECTION_ID = 'planning';\nconst SECTION_ORDER = 30;\nconst PLANNING_PATH = ['modes', PLAN_CONFIG_SECTION_ID] as const;\nconst MAIN_KEY = 'main';\nconst SUBAGENTS_KEY = 'subagents';\n/** Both agents fall back to the session's own settings when left unset. */\nconst INHERIT_MODEL = 'inherit the session model';\nconst INHERIT_THINKING = 'inherit';\n\ninterface PlanSettingDescriptor {\n readonly id: string;\n readonly label: string;\n readonly keyPath: readonly string[];\n readonly placeholder: string;\n readonly detail: string;\n /** Present for a closed set; the panel offers these rather than free text. */\n readonly options?: readonly string[];\n readonly read: (config: PlanningModeConfig | undefined) => string | undefined;\n}\n\nexport const PLAN_SETTINGS: readonly PlanSettingDescriptor[] = [\n {\n id: 'main.model',\n label: 'main model',\n keyPath: [...PLANNING_PATH, MAIN_KEY, 'model'],\n placeholder: INHERIT_MODEL,\n detail: 'Model the main agent switches to while plan mode is on.',\n read: (config) => config?.main?.model,\n },\n {\n id: 'main.thinking',\n label: 'main thinking',\n keyPath: [...PLANNING_PATH, MAIN_KEY, 'thinking'],\n placeholder: INHERIT_THINKING,\n detail: 'Thinking level appended to the main model while planning.',\n options: DOOM_PLANNING_THINKING_LEVELS,\n read: (config) => config?.main?.thinking,\n },\n {\n id: 'subagents.model',\n label: 'subagent model',\n keyPath: [...PLANNING_PATH, SUBAGENTS_KEY, 'model'],\n placeholder: INHERIT_MODEL,\n detail: 'Model forced onto delegated planning subagents.',\n read: (config) => config?.subagents?.model,\n },\n {\n id: 'subagents.thinking',\n label: 'subagent thinking',\n keyPath: [...PLANNING_PATH, SUBAGENTS_KEY, 'thinking'],\n placeholder: INHERIT_THINKING,\n detail: 'Thinking level appended to the subagent model.',\n options: DOOM_PLANNING_THINKING_LEVELS,\n read: (config) => config?.subagents?.thinking,\n },\n {\n id: 'plansdirectory',\n label: 'plans directory',\n keyPath: [...PLANNING_PATH, 'plansDirectory'],\n placeholder: '~/.pi/plans',\n detail: 'Absolute, repo-relative, or under ~. Written plans land here.',\n read: (config) => config?.plansDirectory,\n },\n];\n\nexport function planSettingByFieldId(fieldId: string): PlanSettingDescriptor | undefined {\n return PLAN_SETTINGS.find((setting) => setting.id === fieldId);\n}\n\nfunction choicesFor(setting: PlanSettingDescriptor): ConfigChoice[] | undefined {\n // `set` rather than a bespoke action: choosing a level is the same write as\n // typing one, so it shares the handler.\n return setting.options?.map((option) => ({ id: option, label: option, action: CONFIG_ACTION.set }));\n}\n\nexport function planConfigSections(config: PlanningModeConfig | undefined, notice?: string): readonly ConfigSection[] {\n const fields: ConfigField[] = PLAN_SETTINGS.map((setting) => {\n const value = setting.read(config);\n const choices = choicesFor(setting);\n return {\n id: setting.id,\n label: setting.label,\n kind: choices ? ('enum' as const) : ('text' as const),\n keyPath: setting.keyPath.join('.'),\n placeholder: setting.placeholder,\n detail: setting.detail,\n ...(value ? { value } : {}),\n ...(choices ? { choices } : {}),\n };\n });\n return [\n {\n id: PLAN_CONFIG_SECTION_ID,\n title: 'planning',\n order: SECTION_ORDER,\n detail: 'plan mode',\n fields,\n ...(notice ? { notice, noticeLevel: 'error' as const } : {}),\n },\n ];\n}\n"],"mappings":"sJAqBA,MAAa,EAAyB,WAEhC,EAAgB,CAAC,QAAS,CAAsB,EAChD,EAAW,OACX,EAAgB,YAEhB,EAAgB,4BAChB,EAAmB,UAaZ,EAAkD,CAC7D,CACE,GAAI,aACJ,MAAO,aACP,QAAS,CAAC,GAAG,EAAe,EAAU,OAAO,EAC7C,YAAa,EACb,OAAQ,0DACR,KAAO,GAAW,GAAQ,MAAM,KAClC,EACA,CACE,GAAI,gBACJ,MAAO,gBACP,QAAS,CAAC,GAAG,EAAe,EAAU,UAAU,EAChD,YAAa,EACb,OAAQ,4DACR,QAAS,EACT,KAAO,GAAW,GAAQ,MAAM,QAClC,EACA,CACE,GAAI,kBACJ,MAAO,iBACP,QAAS,CAAC,GAAG,EAAe,EAAe,OAAO,EAClD,YAAa,EACb,OAAQ,kDACR,KAAO,GAAW,GAAQ,WAAW,KACvC,EACA,CACE,GAAI,qBACJ,MAAO,oBACP,QAAS,CAAC,GAAG,EAAe,EAAe,UAAU,EACrD,YAAa,EACb,OAAQ,iDACR,QAAS,EACT,KAAO,GAAW,GAAQ,WAAW,QACvC,EACA,CACE,GAAI,iBACJ,MAAO,kBACP,QAAS,CAAC,GAAG,EAAe,gBAAgB,EAC5C,YAAa,cACb,OAAQ,gEACR,KAAO,GAAW,GAAQ,cAC5B,CACF,EAEA,SAAgB,EAAqB,EAAoD,CACvF,OAAO,EAAc,KAAM,GAAY,EAAQ,KAAO,CAAO,CAC/D,CAEA,SAAS,EAAW,EAA4D,CAG9E,OAAO,EAAQ,SAAS,IAAK,IAAY,CAAE,GAAI,EAAQ,MAAO,EAAQ,OAAQ,EAAc,GAAI,EAAE,CACpG,CAEA,SAAgB,EAAmB,EAAwC,EAA2C,CACpH,IAAM,EAAwB,EAAc,IAAK,GAAY,CAC3D,IAAM,EAAQ,EAAQ,KAAK,CAAM,EAC3B,EAAU,EAAW,CAAO,EAClC,MAAO,CACL,GAAI,EAAQ,GACZ,MAAO,EAAQ,MACf,KAAM,EAAW,OAAoB,OACrC,QAAS,EAAQ,QAAQ,KAAK,GAAG,EACjC,YAAa,EAAQ,YACrB,OAAQ,EAAQ,OAChB,GAAI,EAAQ,CAAE,OAAM,EAAI,CAAC,EACzB,GAAI,EAAU,CAAE,SAAQ,EAAI,CAAC,CAC/B,CACF,CAAC,EACD,MAAO,CACL,CACE,GAAI,EACJ,MAAO,WACP,MAAO,GACP,OAAQ,YACR,SACA,GAAI,EAAS,CAAE,SAAQ,YAAa,OAAiB,EAAI,CAAC,CAC5D,CACF,CACF"}
1
+ {"version":3,"file":"planConfig.mjs","names":[],"sources":["../../../src/schemas/plan/planConfig.ts"],"sourcesContent":["/**\n * Plan mode's slice of the `SPC e c` config panel.\n *\n * Plan mode reads `modes.planning` from disk on every activation, so an edit\n * here lands on the next time the mode is turned on with no restart and nothing\n * to invalidate.\n *\n * The descriptor table is the whole design: the panel renders it and the write\n * handler looks the key path back up in it, so adding a setting means adding a\n * row rather than a case.\n */\n\nimport { DOOM_PLANNING_THINKING_LEVELS } from '@agimon-ai/doompi-config';\nimport {\n CONFIG_ACTION,\n type ConfigChoice,\n type ConfigField,\n type ConfigSection,\n} from '@agimon-ai/doompi-extension-contracts/config';\nimport type { PlanningModeConfig } from './config.ts';\n\nexport const PLAN_CONFIG_SECTION_ID = 'planning';\nconst SECTION_ORDER = 30;\nconst PLANNING_PATH = ['modes', PLAN_CONFIG_SECTION_ID] as const;\nconst MAIN_KEY = 'main';\nconst SUBAGENTS_KEY = 'subagents';\n/** Both agents fall back to the session's own settings when left unset. */\nconst INHERIT_MODEL = 'inherit the session model';\nconst INHERIT_THINKING = 'inherit';\n/** Selecting this clears the setting, which is how the file spells \"inherit\". */\nconst INHERIT_CHOICE_ID = 'inherit';\n\ninterface PlanSettingDescriptor {\n readonly id: string;\n readonly label: string;\n readonly keyPath: readonly string[];\n readonly placeholder: string;\n readonly detail: string;\n /** Present for a closed set; the panel offers these rather than free text. */\n readonly options?: readonly string[];\n /** Offers the session's own models rather than free text. See `planConfigSections`. */\n readonly models?: boolean;\n readonly read: (config: PlanningModeConfig | undefined) => string | undefined;\n}\n\nexport const PLAN_SETTINGS: readonly PlanSettingDescriptor[] = [\n {\n id: 'main.model',\n label: 'main model',\n keyPath: [...PLANNING_PATH, MAIN_KEY, 'model'],\n placeholder: INHERIT_MODEL,\n detail: 'Model the main agent switches to while plan mode is on.',\n models: true,\n read: (config) => config?.main?.model,\n },\n {\n id: 'main.thinking',\n label: 'main thinking',\n keyPath: [...PLANNING_PATH, MAIN_KEY, 'thinking'],\n placeholder: INHERIT_THINKING,\n detail: 'Thinking level appended to the main model while planning.',\n options: DOOM_PLANNING_THINKING_LEVELS,\n read: (config) => config?.main?.thinking,\n },\n {\n id: 'subagents.model',\n label: 'subagent model',\n keyPath: [...PLANNING_PATH, SUBAGENTS_KEY, 'model'],\n placeholder: INHERIT_MODEL,\n detail: 'Model forced onto delegated planning subagents.',\n models: true,\n read: (config) => config?.subagents?.model,\n },\n {\n id: 'subagents.thinking',\n label: 'subagent thinking',\n keyPath: [...PLANNING_PATH, SUBAGENTS_KEY, 'thinking'],\n placeholder: INHERIT_THINKING,\n detail: 'Thinking level appended to the subagent model.',\n options: DOOM_PLANNING_THINKING_LEVELS,\n read: (config) => config?.subagents?.thinking,\n },\n {\n id: 'plansdirectory',\n label: 'plans directory',\n keyPath: [...PLANNING_PATH, 'plansDirectory'],\n placeholder: '~/.pi/plans',\n detail: 'Absolute, repo-relative, or under ~. Written plans land here.',\n read: (config) => config?.plansDirectory,\n },\n];\n\nexport function planSettingByFieldId(fieldId: string): PlanSettingDescriptor | undefined {\n return PLAN_SETTINGS.find((setting) => setting.id === fieldId);\n}\n\n/** A model this session could actually switch to, as the panel offers it. */\nexport interface PlanModelChoice {\n readonly provider: string;\n readonly id: string;\n}\n\n/**\n * The models the session can reach, plus the entry that clears the setting.\n *\n * Offered as a list because a model spec is `provider/id` and typing one from\n * memory is how a setting ends up naming a model this session cannot use. The\n * inherit entry CLEARS rather than writing a value: unset is what \"use the\n * session's own model\" means in the file.\n */\nfunction modelChoices(models: readonly PlanModelChoice[]): ConfigChoice[] {\n const inherit: ConfigChoice = {\n id: INHERIT_CHOICE_ID,\n label: INHERIT_MODEL,\n detail: 'Leaves the setting unset.',\n action: CONFIG_ACTION.clear,\n };\n return [\n inherit,\n ...models.map((model): ConfigChoice => ({\n id: `${model.provider}/${model.id}`,\n label: model.id,\n group: model.provider,\n detail: model.provider,\n action: CONFIG_ACTION.set,\n })),\n ];\n}\n\nfunction choicesFor(\n setting: PlanSettingDescriptor,\n models: readonly PlanModelChoice[],\n): { choices: ConfigChoice[]; kind: 'enum' | 'choice' } | undefined {\n // `set` rather than a bespoke action: choosing a level is the same write as\n // typing one, so it shares the handler.\n if (setting.options) {\n return {\n kind: 'enum',\n choices: setting.options.map((option) => ({ id: option, label: option, action: CONFIG_ACTION.set })),\n };\n }\n // A model setting with nothing to offer stays free text rather than opening\n // an empty list: a session with no authenticated provider can still be edited.\n if (!setting.models || models.length === 0) return undefined;\n return { kind: 'choice', choices: modelChoices(models) };\n}\n\nexport function planConfigSections(\n config: PlanningModeConfig | undefined,\n notice?: string,\n models: readonly PlanModelChoice[] = [],\n): readonly ConfigSection[] {\n const fields: ConfigField[] = PLAN_SETTINGS.map((setting) => {\n const value = setting.read(config);\n const offered = choicesFor(setting, models);\n return {\n id: setting.id,\n label: setting.label,\n kind: offered?.kind ?? ('text' as const),\n keyPath: setting.keyPath.join('.'),\n placeholder: setting.placeholder,\n detail: setting.detail,\n ...(value ? { value } : {}),\n ...(offered ? { choices: offered.choices } : {}),\n };\n });\n return [\n {\n id: PLAN_CONFIG_SECTION_ID,\n title: 'planning',\n order: SECTION_ORDER,\n detail: 'plan mode',\n fields,\n ...(notice ? { notice, noticeLevel: 'error' as const } : {}),\n },\n ];\n}\n"],"mappings":"sJAqBA,MAAa,EAAyB,WAEhC,EAAgB,CAAC,QAAS,CAAsB,EAChD,EAAW,OACX,EAAgB,YAEhB,EAAgB,4BAChB,EAAmB,UAiBZ,EAAkD,CAC7D,CACE,GAAI,aACJ,MAAO,aACP,QAAS,CAAC,GAAG,EAAe,EAAU,OAAO,EAC7C,YAAa,EACb,OAAQ,0DACR,OAAQ,GACR,KAAO,GAAW,GAAQ,MAAM,KAClC,EACA,CACE,GAAI,gBACJ,MAAO,gBACP,QAAS,CAAC,GAAG,EAAe,EAAU,UAAU,EAChD,YAAa,EACb,OAAQ,4DACR,QAAS,EACT,KAAO,GAAW,GAAQ,MAAM,QAClC,EACA,CACE,GAAI,kBACJ,MAAO,iBACP,QAAS,CAAC,GAAG,EAAe,EAAe,OAAO,EAClD,YAAa,EACb,OAAQ,kDACR,OAAQ,GACR,KAAO,GAAW,GAAQ,WAAW,KACvC,EACA,CACE,GAAI,qBACJ,MAAO,oBACP,QAAS,CAAC,GAAG,EAAe,EAAe,UAAU,EACrD,YAAa,EACb,OAAQ,iDACR,QAAS,EACT,KAAO,GAAW,GAAQ,WAAW,QACvC,EACA,CACE,GAAI,iBACJ,MAAO,kBACP,QAAS,CAAC,GAAG,EAAe,gBAAgB,EAC5C,YAAa,cACb,OAAQ,gEACR,KAAO,GAAW,GAAQ,cAC5B,CACF,EAEA,SAAgB,EAAqB,EAAoD,CACvF,OAAO,EAAc,KAAM,GAAY,EAAQ,KAAO,CAAO,CAC/D,CAgBA,SAAS,EAAa,EAAoD,CAOxE,MAAO,CACL,CANA,GAAI,UACJ,MAAO,EACP,OAAQ,4BACR,OAAQ,EAAc,KAGhB,EACN,GAAG,EAAO,IAAK,IAAyB,CACtC,GAAI,GAAG,EAAM,SAAS,GAAG,EAAM,KAC/B,MAAO,EAAM,GACb,MAAO,EAAM,SACb,OAAQ,EAAM,SACd,OAAQ,EAAc,GACxB,EAAE,CACJ,CACF,CAEA,SAAS,EACP,EACA,EACkE,CAGlE,GAAI,EAAQ,QACV,MAAO,CACL,KAAM,OACN,QAAS,EAAQ,QAAQ,IAAK,IAAY,CAAE,GAAI,EAAQ,MAAO,EAAQ,OAAQ,EAAc,GAAI,EAAE,CACrG,EAIE,MAAC,EAAQ,QAAU,EAAO,SAAW,GACzC,MAAO,CAAE,KAAM,SAAU,QAAS,EAAa,CAAM,CAAE,CACzD,CAEA,SAAgB,EACd,EACA,EACA,EAAqC,CAAC,EACZ,CAC1B,IAAM,EAAwB,EAAc,IAAK,GAAY,CAC3D,IAAM,EAAQ,EAAQ,KAAK,CAAM,EAC3B,EAAU,EAAW,EAAS,CAAM,EAC1C,MAAO,CACL,GAAI,EAAQ,GACZ,MAAO,EAAQ,MACf,KAAM,GAAS,MAAS,OACxB,QAAS,EAAQ,QAAQ,KAAK,GAAG,EACjC,YAAa,EAAQ,YACrB,OAAQ,EAAQ,OAChB,GAAI,EAAQ,CAAE,OAAM,EAAI,CAAC,EACzB,GAAI,EAAU,CAAE,QAAS,EAAQ,OAAQ,EAAI,CAAC,CAChD,CACF,CAAC,EACD,MAAO,CACL,CACE,GAAI,EACJ,MAAO,WACP,MAAO,GACP,OAAQ,YACR,SACA,GAAI,EAAS,CAAE,SAAQ,YAAa,OAAiB,EAAI,CAAC,CAC5D,CACF,CACF"}
@@ -1,7 +1,7 @@
1
- const e=require("../_virtual/_rolldown/runtime.cjs"),t=require("../schemas/plan/config.cjs"),n=require("./fableFlow.cjs"),r=require("../adapters/telemetry/logSinkTelemetry.cjs"),i=require("../schemas/plan/planConfig.cjs"),a=require("./prompts.cjs");let o=require("@agimon-ai/doompi-config"),s=require("node:buffer"),c=require("node:crypto"),l=require("node:fs");l=e.__toESM(l,1);let ee=require("node:os");ee=e.__toESM(ee,1);let te=require("node:path");te=e.__toESM(te,1);let ne=require("@agimon-ai/doompi-config/configWriter"),u=require("@agimon-ai/doompi-extension-contracts/config"),d=require("@agimon-ai/doompi-extension-contracts/fable-plan"),f=require("@agimon-ai/doompi-extension-contracts/mode"),p=require("@agimon-ai/doompi-extension-contracts/narration"),re=require("@agimon-ai/doompi-extension-contracts/subagent-policy"),m=require("@agimon-ai/doompi-extension-contracts/subagent-tool"),h=require("@agimon-ai/doompi-extension-contracts/voice-tools"),g=require("@agimon-ai/doompi-extension-contracts/ui-hub");const _=`agent-harness-plan-mode`,ie=`agent-harness-plan-document`,v=`complete_plan`,y=`write_plan`,b=`ask_user_question`,ae=`bash`,x=`find`,S=`grep`,C=`read`,w=`subagent`,oe=`task`,T=`record_debug_evidence`,se=`run_fable_plan`,E=`implementation-plan`,ce=`@agimon-ai/doompi-plan`,le=`activate`,ue=`deactivate`,D=`warning`,de=`info`,fe=`Refused to write the plan because its destination is not a regular file.`,pe=`plan.trigger`,me=`plan.model`,O=`plan.flavor`,he=`not_found`,ge=`unauthenticated`,_e=`restore_failed`,ve=new Set,ye=5e3,be=`Exit plan mode and start implementation`,xe=`Continue planning`,k=`exit`,Se=`continue`,Ce=[`Plan complete. What would you like to do?`,` 1. ${be}`,` 2. ${xe}`,` Type something.`].join(`
2
- `),we=(0,p.createNarrationRequest)(`Plan complete. What would you like to do? Options: 1, ${be}; 2, ${xe}. Please choose one option. You may also answer in your own words.`),Te=new Set([`add_directory`,b,ae,v,x,S,`intercom`,`ls`,f.MINOR_MODE_TOOL_NAME,...h.VOICE_MODE_TOOL_NAMES,C,`search_external_files`,w,`subagent_supervisor`,`subagent_wait`,oe,y]),A=new Set([`edit`,`write`,`mcp`]),j=[C,ae,S,x,`ls`],Ee=[...j,`mcp`],M=`leader`,De=`voice_tool`,Oe=[`off`,`minimal`,`low`,`medium`,`high`,`xhigh`,`max`],ke=[`idle`,`draft`,`review`,`completed`,`failed`,`cancelled`,`interrupted`],Ae=`@agimon-ai/doompi-plan`,N={type:`string`,enum:[`normal`,`debug`,`fable`]},je={type:`object`,properties:{flavor:N},required:[`flavor`],additionalProperties:!1},Me={type:`object`,properties:{},additionalProperties:!1},Ne={type:`object`,properties:{active:{type:`boolean`},flavor:{anyOf:[{...N},{type:`null`}]},changed:{type:`boolean`}},required:[`active`,`flavor`,`changed`],additionalProperties:!1},P=new Set([v,y,T,se,f.MINOR_MODE_TOOL_NAME,...h.VOICE_MODE_TOOL_NAMES]);function Pe(e){return(e.match(/^#\s+(.+?)\s*$/m)?.[1]??E).normalize(`NFKD`).replace(/[\u0300-\u036f]/g,``).toLowerCase().replace(/[^a-z0-9]+/g,`-`).replace(/^-+|-+$/g,``).slice(0,64)||E}function Fe(e){return`${encodeURIComponent(e).slice(0,48)||`session`}--${(0,c.createHash)(`sha256`).update(e).digest(`hex`)}`}function Ie(e=new Date,t=(0,c.randomUUID)()){return`${e.toISOString().replace(/[-:]/g,``).replace(/\.\d{3}Z$/,`Z`)}--${t}`}function Le(e,t){for(let n=e.length-1;n>=0;--n){let r=e[n];if(!r||typeof r!=`object`)continue;let i=r;if(i.type!==`message`||i.message?.role!==`assistant`||!Array.isArray(i.message.content))continue;let a=i.message.content.findIndex(e=>{if(!e||typeof e!=`object`)return!1;let n=e;return n.type===`toolCall`&&n.id===t&&n.name===y});if(!(a<0))return i.message.content.slice(0,a).flatMap(e=>{if(!e||typeof e!=`object`)return[];let t=e;return t.type===`text`&&typeof t.text==`string`?[t.text]:[]}).join(`
1
+ const e=require("../_virtual/_rolldown/runtime.cjs"),t=require("../schemas/plan/config.cjs"),n=require("./fableFlow.cjs"),r=require("../adapters/telemetry/logSinkTelemetry.cjs"),i=require("../schemas/plan/planConfig.cjs"),a=require("./prompts.cjs");let o=require("@agimon-ai/doompi-config"),s=require("node:buffer"),c=require("node:crypto"),l=require("node:fs");l=e.__toESM(l,1);let ee=require("node:os");ee=e.__toESM(ee,1);let te=require("node:path");te=e.__toESM(te,1);let ne=require("@agimon-ai/doompi-config/configWriter"),u=require("@agimon-ai/doompi-extension-contracts/config"),d=require("@agimon-ai/doompi-extension-contracts/fable-plan"),f=require("@agimon-ai/doompi-extension-contracts/mode"),p=require("@agimon-ai/doompi-extension-contracts/narration"),re=require("@agimon-ai/doompi-extension-contracts/subagent-policy"),m=require("@agimon-ai/doompi-extension-contracts/subagent-tool"),h=require("@agimon-ai/doompi-extension-contracts/voice-tools"),g=require("@agimon-ai/doompi-extension-contracts/ui-hub");const _=`agent-harness-plan-mode`,ie=`agent-harness-plan-document`,v=`complete_plan`,y=`write_plan`,b=`ask_user_question`,ae=`bash`,x=`find`,S=`grep`,C=`read`,w=`subagent`,oe=`task`,T=`record_debug_evidence`,se=`run_fable_plan`,E=`implementation-plan`,ce=`@agimon-ai/doompi-plan`,le=`activate`,ue=`deactivate`,D=`warning`,de=`info`,fe=`Refused to write the plan because its destination is not a regular file.`,O=`plan.trigger`,pe=`plan.model`,k=`plan.flavor`,me=`not_found`,he=`unauthenticated`,ge=`restore_failed`,_e=new Set,ve=5e3,ye=`Exit plan mode and start implementation`,be=`Continue planning`,xe=`exit`,Se=`continue`,Ce=[`Plan complete. What would you like to do?`,` 1. ${ye}`,` 2. ${be}`,` Type something.`].join(`
2
+ `),we=(0,p.createNarrationRequest)(`Plan complete. What would you like to do? Options: 1, ${ye}; 2, ${be}. Please choose one option. You may also answer in your own words.`),Te=new Set([`add_directory`,b,ae,v,x,S,`intercom`,`ls`,f.MINOR_MODE_TOOL_NAME,...h.VOICE_MODE_TOOL_NAMES,C,`search_external_files`,w,`subagent_supervisor`,`subagent_wait`,oe,y]),A=new Set([`edit`,`write`,`mcp`]),j=[C,ae,S,x,`ls`],Ee=[...j,`mcp`],M=`leader`,De=`voice_tool`,N=[`off`,`minimal`,`low`,`medium`,`high`,`xhigh`,`max`],Oe=[`idle`,`draft`,`review`,`completed`,`failed`,`cancelled`,`interrupted`],ke=`@agimon-ai/doompi-plan`,Ae={type:`string`,enum:[`normal`,`debug`,`fable`]},je={type:`object`,properties:{flavor:Ae},required:[`flavor`],additionalProperties:!1},Me={type:`object`,properties:{},additionalProperties:!1},Ne={type:`object`,properties:{active:{type:`boolean`},flavor:{anyOf:[{...Ae},{type:`null`}]},changed:{type:`boolean`}},required:[`active`,`flavor`,`changed`],additionalProperties:!1},Pe=new Set([v,y,T,se,f.MINOR_MODE_TOOL_NAME,...h.VOICE_MODE_TOOL_NAMES]);function Fe(e){return(e.match(/^#\s+(.+?)\s*$/m)?.[1]??E).normalize(`NFKD`).replace(/[\u0300-\u036f]/g,``).toLowerCase().replace(/[^a-z0-9]+/g,`-`).replace(/^-+|-+$/g,``).slice(0,64)||E}function Ie(e){return`${encodeURIComponent(e).slice(0,48)||`session`}--${(0,c.createHash)(`sha256`).update(e).digest(`hex`)}`}function Le(e=new Date,t=(0,c.randomUUID)()){return`${e.toISOString().replace(/[-:]/g,``).replace(/\.\d{3}Z$/,`Z`)}--${t}`}function Re(e,t){for(let n=e.length-1;n>=0;--n){let r=e[n];if(!r||typeof r!=`object`)continue;let i=r;if(i.type!==`message`||i.message?.role!==`assistant`||!Array.isArray(i.message.content))continue;let a=i.message.content.findIndex(e=>{if(!e||typeof e!=`object`)return!1;let n=e;return n.type===`toolCall`&&n.id===t&&n.name===y});if(!(a<0))return i.message.content.slice(0,a).flatMap(e=>{if(!e||typeof e!=`object`)return[];let t=e;return t.type===`text`&&typeof t.text==`string`?[t.text]:[]}).join(`
3
3
 
4
- `).trim()||void 0}}function Re(e){return e.reason instanceof Error?e.reason:Error(`Plan write cancelled.`)}async function F(e,t){if(t.aborted)throw Re(t);let n,r=new Promise((e,r)=>{n=()=>r(Re(t)),t.addEventListener(`abort`,n,{once:!0})});try{return await Promise.race([e(),r])}finally{n&&t.removeEventListener(`abort`,n)}}function I(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function L(e){return typeof e==`string`}function R(e){if(!(!I(e)||!L(e.provider)||!L(e.id))&&!(!e.provider||!e.id||e.provider.length>128||e.id.length>128))return{provider:e.provider,id:e.id}}function z(e){return L(e)&&Oe.includes(e)?e:void 0}function ze(e){return L(e)&&ke.includes(e)?e:void 0}function B(e){if(!(!Array.isArray(e)||e.some(e=>!L(e))))return e.map(e=>e)}function V(e){if(!I(e))return;let t=B(e.tools),n=z(e.thinking);if(!t||n===void 0)return;let r=e.model===void 0?void 0:R(e.model);if(!(e.model!==void 0&&!r))return{tools:t,...r?{model:r}:{},thinking:n}}function Be(e){return e===`normal`||e===`debug`||e===`fable`?e:void 0}function H(e){if(e.version!==2)return;let t=e.activeFlavor===void 0?void 0:Be(e.activeFlavor);if(e.activeFlavor!==void 0&&!t)return;let n=e.originalSnapshot===void 0?void 0:V(e.originalSnapshot);if(e.originalSnapshot!==void 0&&!n)return;let r=e.planId===void 0?void 0:L(e.planId)?e.planId:void 0;if(e.planId!==void 0&&r===void 0)return;let i=e.interruptedFableStage===void 0?void 0:ze(e.interruptedFableStage);if(!(e.interruptedFableStage!==void 0&&!i))return{version:2,...t?{activeFlavor:t}:{},...n?{originalSnapshot:n}:{},...r?{planId:r}:{},...i?{interruptedFableStage:i}:{}}}function Ve(e){if(e.enabled!==!0&&e.enabled!==!1)return;let t=e.toolsBeforePlanMode===void 0?void 0:B(e.toolsBeforePlanMode);if(e.toolsBeforePlanMode!==void 0&&!t)return;let n=e.modelBeforePlanMode===void 0?void 0:R(e.modelBeforePlanMode);if(e.modelBeforePlanMode!==void 0&&!n)return;let r=e.thinkingBeforePlanMode===void 0?void 0:z(e.thinkingBeforePlanMode);if(e.thinkingBeforePlanMode!==void 0&&r===void 0)return;let i=e.planId===void 0?void 0:L(e.planId)?e.planId:void 0;if(e.planId!==void 0&&i===void 0)return;let a=t&&r!==void 0?{tools:t,...n?{model:n}:{},thinking:r}:void 0;return{version:2,...e.enabled?{activeFlavor:`normal`}:{},...a?{originalSnapshot:a}:{},...i?{planId:i}:{}}}function He(e){if(I(e))return H(e)??Ve(e)}function Ue(e){if(!I(e)||!L(e.content)||!L(e.path)||!L(e.sessionId)||!L(e.title)||!L(e.writtenAt))return;let t=e.planId===void 0?void 0:L(e.planId)?e.planId:void 0;if(e.planId===void 0||t!==void 0)return{content:e.content,path:e.path,sessionId:e.sessionId,...t?{planId:t}:{},title:e.title,writtenAt:e.writtenAt}}function U(e,t,n){if(!L(e))throw Error(`${t} must be a string.`);let r=e.trim();if(n&&!r)throw Error(`${t} must not be empty.`);if(s.Buffer.byteLength(r,`utf8`)>4096)throw Error(`${t} exceeds the bounded debug evidence limit.`);return r}function We(e,t){if(!Array.isArray(e)||e.length>32)throw Error(`${t} must be a bounded string array.`);return e.map((e,n)=>U(e,`${t}[${n}]`,!1))}function W(e,t){return e===void 0?``:U(e,t,!1)}function G(e,t){return e===void 0?[]:We(e,t)}function K(e,t,n){let r=new Set(t),i=Object.keys(e).find(e=>!r.has(e));if(i)throw Error(`${n} contains unsupported field '${i}'.`)}function Ge(e){if(!I(e))throw Error(`Debug evidence must be an object.`);K(e,[`issue`,`expectedBehavior`,`reproductionAttempt`,`actualBehavior`,`logs`,`correlatedTraceEvidence`,`processOutput`,`browserConsoleEvidence`,`correlationIds`,`timestamps`,`verifiedFacts`,`hypotheses`,`unavailableEvidence`],`Debug evidence`);let t={issue:U(e.issue,`issue`,!0),expectedBehavior:W(e.expectedBehavior,`expectedBehavior`),reproductionAttempt:W(e.reproductionAttempt,`reproductionAttempt`),actualBehavior:W(e.actualBehavior,`actualBehavior`),logs:G(e.logs,`logs`),correlatedTraceEvidence:G(e.correlatedTraceEvidence,`correlatedTraceEvidence`),processOutput:G(e.processOutput,`processOutput`),browserConsoleEvidence:G(e.browserConsoleEvidence,`browserConsoleEvidence`),correlationIds:G(e.correlationIds,`correlationIds`),timestamps:G(e.timestamps,`timestamps`),verifiedFacts:G(e.verifiedFacts,`verifiedFacts`),hypotheses:G(e.hypotheses,`hypotheses`),unavailableEvidence:G(e.unavailableEvidence,`unavailableEvidence`)};if(s.Buffer.byteLength(JSON.stringify(t),`utf8`)>32768)throw Error(`Debug evidence exceeds the bounded packet size.`);return t}function q(e,t){let n=new Set(t),r=e.filter(e=>Te.has(e)&&n.has(e)),i=e.filter(e=>!Te.has(e)&&!A.has(e)&&!P.has(e)&&n.has(e)),a=j.filter(e=>n.has(e)),o=[v,oe,y].filter(e=>n.has(e));return[...new Set([...r,...i,...a,...o])]}function Ke(e,t,n,r,i){let a=new Set(n),o=t.filter(e=>!Te.has(e)&&!A.has(e)&&!P.has(e)),s=q([...new Set([...e,...o])].filter(e=>!P.has(e)),n),c=r===`debug`?[T,...i].filter(e=>a.has(e)):[];r===`fable`&&a.has(se)&&c.push(se);for(let e of[f.MINOR_MODE_TOOL_NAME,...h.VOICE_MODE_TOOL_NAMES])t.includes(e)&&a.has(e)&&c.push(e);return[...new Set([...s,...c])]}function qe(e,t,n){let r=new Set(n),i=new Set(t),a=e.filter(e=>!P.has(e)&&r.has(e)&&(e!==b||i.has(e))),o=t.filter(e=>!Te.has(e)&&!A.has(e)&&!P.has(e)&&r.has(e)),s=[f.MINOR_MODE_TOOL_NAME,...h.VOICE_MODE_TOOL_NAMES].filter(e=>i.has(e)&&r.has(e));return[...new Set([...a,...o,...s])]}function J(e){if(e.output=!1,e.progress=!1,e.worktree=!1,Array.isArray(e.parallel))for(let t of e.parallel)J(t);else e.parallel&&J(e.parallel)}function Je(e){if(e.action!==void 0){(0,m.isSubagentAction)(e.action)&&(0,m.subagentActionAcceptsField)(e.action,`artifacts`)&&(e.artifacts=!1);return}e.artifacts=!1,e.output=!1,e.share=!1,e.progress=!1,e.worktree=!1,delete e.chainDir,delete e.outputSchema,delete e.sessionDir;for(let t of e.tasks??[])J(t);for(let t of e.chain??[])J(t)}function Ye(e,t){if(!e?.model&&!e?.thinking)return;let n=e.model??(t?`${t.provider}/${t.id}`:void 0);if(n)return e.thinking?`${n.replace(/:(?:off|minimal|low|medium|high|xhigh|max)$/,``)}:${e.thinking}`:n}function Xe(e,t){if(e.action===`run`)for(let n of e.requests??[])n.model=t}function Ze(e,t){if(e.action===`assign`)for(let n of e.assignments??[])n.model=t}function Qe(e){return new Set([`append-step`,`create`,`delete`,`disable`,`eject`,`enable`,`grant-spawn-budget`,`reset`,`schedule`,`schedule-cancel`,`update`,`watchdog.configure`]).has(e??``)}function $e(e,t){let n=e.indexOf(`/`);if(n>0)return t.modelRegistry.find(e.slice(0,n),e.slice(n+1));let r=t.modelRegistry.getAvailable().filter(t=>t.id===e);return r.find(e=>e.provider===t.model?.provider)??(r.length===1?r[0]:void 0)}function et(e=process.env,n=process.cwd()){return t.loadDoomConfig((0,o.loadHarnessState)(e).state.root??n).modes?.planning}function tt(e){let t={key:`p`,label:`plan`,detail:`read-only planning modes`,order:60};return[e?{id:`plan.exit`,path:[t,{key:`e`,label:`exit`,detail:`restore and exit`,tone:`exit`}],action:{name:`plan.exit`}}:{id:`plan.normal`,path:[t,{key:`e`,label:`enter`,detail:`read-only planning`}],action:{name:`plan.normal`}},{id:`plan.debug`,path:[t,{key:`d`,label:`debug`,detail:`adaptive debug planning`}],action:{name:`plan.debug`}},{id:`plan.fable`,path:[t,{key:`f`,label:`fable`,detail:`repository-aware draft`}],action:{name:`plan.fable`}}]}function nt(e,t,c=et,m=r.createPlanTelemetry(),b={}){let x=!0,S=0,C,E,A,j,Oe,ke,N=!1,P,L,R,z,ze,B,V,H,Ve=!1,U=!1,We,W,G=`idle`,K,q,J,nt,rt=Promise.resolve(),it=b.doomIntegrations??!0,at=new Set(b.debugDiagnosticTools??ve),ot=new AbortController;e.effect(()=>()=>dt(),`${ce}/runtime`);let st=b.fableBroker??{start:(e,t)=>{let n=B;if(!n)throw Error(`The session-bound Fable broker is unavailable.`);return n.start(e,t)},cancel:(e,t)=>{B?.cancel({requester:d.FABLE_PLAN_REQUESTER,operationId:e,reason:t})}},ct=()=>b.fableBroker!==void 0||B!==void 0;e.inject([d.DOOM_FABLE_PLAN_SERVICE],e=>{let t=(0,d.readDoomFablePlanService)(e);if(t)return B=t,()=>{B===t&&(B=void 0,J&&nt?.cancel(`The Team Fable service was unloaded.`))}}),e.inject([re.DOOM_SUBAGENT_POLICY_SERVICE],e=>{let t=(0,re.readDoomSubagentPolicyService)(e);if(t)return ze=t,mt(),()=>{ze===t&&(z?.dispose(),z=void 0,ze=void 0)}}),it&&(e.inject([f.DOOM_MINOR_MODE_CATALOG_SERVICE,p.DOOM_NARRATION_SERVICE],e=>{let t={modes:(0,f.requireMinorModeCatalog)(e),narration:(0,p.requireDoomNarrationService)(e)};return We=t,()=>{We===t&&(We=void 0)}}),e.inject([f.DOOM_MINOR_MODE_CATALOG_SERVICE],e=>{let t=(0,f.registerMinorModeOwner)((0,f.requireMinorModeCatalog)(e),{descriptor:{source:ce,id:`plan`,label:`Plan`,description:`Read-only planning with normal, debug, and Fable flavors.`,order:40,actions:[{id:le,label:`Activate`,description:`Activate plan mode or switch its planning flavor.`,contexts:[`tui`,`headless`],parameters:[{name:`flavor`,label:`Flavor`,kind:`enum`,required:!0,choices:[{value:`normal`,label:`Normal`},{value:`debug`,label:`Debug`},{value:`fable`,label:`Fable`}]}]},{id:ue,label:`Deactivate`,description:`Exit plan mode and restore the previous agent configuration.`,contexts:[`tui`,`headless`],parameters:[]}]},initialState:pt(),async handleAction(e,t,n){if(!x)throw Error(`Plan runtime is disposed.`);if(e===le){let e=t.flavor;if(e!==`normal`&&e!==`debug`&&e!==`fable`)throw Error(`A valid plan flavor is required.`);return await Q(()=>$(n.context,e,M,!1)),{message:`Plan mode is active with the ${e} flavor.`}}if(e===ue)return{message:await Q(()=>bt(n.context,M))?`Plan mode deactivated.`:`Plan mode remains active.`};throw Error(`Unknown plan mode action: ${e}`)},onError:e=>void m.recordError(r.PLAN_EVENT.configLoadFailed,e)});return E=t,()=>{t.dispose(),E===t&&(E=void 0)}}),e.inject([g.DOOM_UI_HUB_SERVICE],e=>{let t=(0,g.requireDoomUiHub)(e),n=t.registerLeader({source:ce,bindings:tt(N)}),r=t.registerConfig({source:ce,listSections:()=>{try{return i.planConfigSections(c(),ke)}catch(e){return i.planConfigSections(void 0,e instanceof Error?e.message:String(e))}},handlers:{[u.CONFIG_ACTION.set]:async({fieldId:e,value:t})=>{let n=i.planSettingByFieldId(e);!n||!t||(ke=void 0,await(0,ne.setDoomConfigValue)((0,o.globalDoomConfigPath)(),[...n.keyPath],t))},[u.CONFIG_ACTION.clear]:async({fieldId:e})=>{let t=i.planSettingByFieldId(e);t&&(ke=void 0,await(0,ne.unsetDoomConfigValue)((0,o.globalDoomConfigPath)(),[...t.keyPath]))}},onError:e=>{x&&(ke=e instanceof Error?e.message:String(e))}});return A=n,j=r,()=>{r.dispose(),n.dispose(),j===r&&(j=void 0),A===n&&(A=void 0)}}));function Y(e=S){if(!x||e!==S)throw Error(`Plan runtime is disposed or stale.`)}async function lt(){let e=We;if(!e||!e.modes.list().some(e=>e.descriptor.source===p.DOOM_VOICE_SOURCE&&e.descriptor.id===p.DOOM_VOICE_AUTO_MODE_ID&&e.state.activation===`active`))return!1;try{return await e.narration.request(we),!0}catch(e){return m.recordWarning(r.PLAN_EVENT.planReviewCompleted,e,{"plan.outcome":`narration_failed`}),!1}}function ut(){return{content:[{type:`text`,text:`${Ce}\n\nThe choices were spoken through autonomous voice. Stop now and wait for the user's next message; it will arrive as an ordinary user message.`}],details:{answers:[],awaitingResponse:!0,cancelled:!1,delivery:`voice`,voicePrompt:Ce},terminate:!0}}function dt(){return C??=(async()=>{x=!1,S+=1,ot.abort(Error(`Plan runtime is shutting down.`));let e=q,n=L,r=[];nt?.cancel(`Plan runtime is shutting down.`);let i=[rt,...J?[J]:[]],a=await Promise.allSettled(i);for(let e of a)e.status===`rejected`&&r.push(e.reason);if(N&&n&&e)try{await _t(e,n,!0)&&t.setActiveTools(qe(n.tools,t.getActiveTools(),t.getAllTools().map(e=>e.name)))}catch(e){r.push(e)}let o=[()=>z?.dispose(),()=>Oe?.(),()=>j?.dispose(),()=>A?.dispose(),()=>E?.dispose()];for(let e of o)try{e()}catch(e){r.push(e)}z=void 0,We=void 0,Oe=void 0,j=void 0,A=void 0,E=void 0,J=void 0,q=void 0,U=!1,N=!1,P=void 0,L=void 0,R=void 0;try{await m.shutdown()}catch(e){r.push(e)}if(r.length>0)throw AggregateError(r,`Plan runtime cleanup failed.`)})(),C}function ft(){x&&A?.update(tt(N))}function pt(){let e=P,t=e===`fable`&&G!==`idle`?` · ${G}`:``;return{activation:N?`active`:`inactive`,condition:G===`failed`?`failed`:G===`interrupted`?`degraded`:`ready`,...N&&e?{detail:`${e}${t} - read only`,color:D,modelContextVariant:e}:{},actions:[{id:le,enabled:!0},...N?[{id:ue,enabled:!0}]:[{id:ue,enabled:!1,disabledReason:`Plan mode is inactive.`}]]}}function X(e){if(!x)return;ft();let t=P,n=N&&t?`plan:${t}`:void 0;e.ui.setStatus(`plan-mode`,n?e.ui.theme.fg(D,n):void 0),E?.publish(pt())}function mt(){if(!x||!it||!N||!P||!q)return;let e=[...Ee],t={owner:ce,allowedTools:e,requiredTools:[ae,`mcp`],allowMcpTools:!0,allowedExternalProfiles:P===`fable`?[n.FABLE_PLAN_PROFILE]:[],denyExtensions:!1};z?z.update(t):z=ze?.register(t)}function Z(){x&&t.appendEntry(_,{version:2,...N&&P?{activeFlavor:P}:{},...L?{originalSnapshot:{tools:[...L.tools],...L.model?{model:{...L.model}}:{},thinking:L.thinking}}:{},...H?{planId:H}:{},...K?{interruptedFableStage:K}:{}})}function ht(e,t,n){m.recordWarning(r.PLAN_EVENT.modelResolveFailed,`Planning model ${n}: ${t}`,{[me]:t,"plan.model.phase":e,"plan.model.reason":n})}async function gt(e){let n=S;Y(n);let r=R?.main;if(r){if(r.model){let i=$e(r.model,e);if(!i)e.ui.notify(`Planning model not found: ${r.model}`,D),ht(`apply`,r.model,he);else{let a=await t.setModel(i);Y(n),a||(e.ui.notify(`Planning model is unavailable without authentication: ${r.model}`,D),ht(`apply`,r.model,ge))}}Y(n),r.thinking&&t.setThinkingLevel(r.thinking)}}async function _t(e,n,i=!1){let a=S;if(i||Y(a),!n)return!1;if(n.model){let o=`${n.model.provider}/${n.model.id}`,s=e.modelRegistry.find(n.model.provider,n.model.id);if(!s)return e.ui.notify(`Previous model not found: ${o}`,D),ht(`restore`,o,he),!1;try{let n=await t.setModel(s);if(i||Y(a),!n)return e.ui.notify(`Previous model is unavailable without authentication: ${o}`,D),ht(`restore`,o,ge),!1}catch(t){return i||Y(a),e.ui.notify(`Previous model could not be restored: ${o}`,D),ht(`restore`,o,_e),m.recordError(r.PLAN_EVENT.modelResolveFailed,t,{[me]:o,"plan.model.phase":`restore`,"plan.model.reason":_e}),!1}}return i||Y(a),t.setThinkingLevel(n.thinking),!0}function vt(e){N&&P?e.ui.notify(`Already in plan:${P}.`,de):e.ui.notify(`Plan mode is inactive.`,de)}function Q(e){let t=S,n=async()=>{Y(t);let n=await e();return Y(t),n},r=rt.then(n,n);return rt=r.then(()=>void 0,()=>void 0),r}async function yt(e){let t=S;if(Y(t),!J&&!G.match(/^(draft|review)$/u))return;(G===`draft`||G===`review`)&&(K=G),nt?.cancel(e);let n=J;if(n)try{await n,Y(t)}catch(e){Y(t),m.recordError(r.PLAN_EVENT.modeDisabled,e,{[O]:`fable`,"plan.fable.reason":`cancel_failed`})}Y(t),J=void 0,K&&(G=`interrupted`),q&&X(q),Z()}async function $(e,n,i,a){let o=S;if(Y(o),q=e,N){if(P===n){vt(e);return}P===`fable`&&await yt(`Leaving Fable for plan:${n}.`),Y(o),P=n,mt(),t.setActiveTools(Ke(L?.tools??[],t.getActiveTools(),t.getAllTools().map(e=>e.name),n,at)),X(e),Z(),m.recordEvent(r.PLAN_EVENT.modeEnabled,{[pe]:i,[O]:n,"plan.tool.count":t.getActiveTools().length});return}let s;try{s=c()}catch(e){throw m.recordError(r.PLAN_EVENT.configLoadFailed,e,{[pe]:i}),e}a?H??=Ie():(V=void 0,Ve=!1,U=!1,H=Ie(),K=void 0,G=`idle`),R=s,L??={tools:[...t.getActiveTools()],...e.model?{model:{provider:e.model.provider,id:e.model.id}}:{},thinking:t.getThinkingLevel()},N=!0,P=n,t.setActiveTools(Ke(L.tools,t.getActiveTools(),t.getAllTools().map(e=>e.name),n,at)),mt(),await gt(e),Y(o),X(e),Z(),m.recordEvent(r.PLAN_EVENT.modeEnabled,{[pe]:i,[O]:n,"plan.tool.count":t.getActiveTools().length,...R?.main?.model?{[me]:R.main.model}:{},...R?.main?.thinking?{"plan.thinking":R.main.thinking}:{}})}async function bt(e,n){let i=S;if(Y(i),!N)return vt(e),!1;await yt(`Plan mode is exiting.`),Y(i);let a=L,o=await _t(e,a);if(Y(i),!o)return e.ui.notify(`Previous model was not restored. Plan mode remains read-only.`,D),X(e),Z(),m.recordWarning(r.PLAN_EVENT.modeDisabled,`Plan mode restoration failed.`,{[pe]:n,[O]:P??`unknown`,"plan.restored":!1}),!1;let s=!!V;return t.setActiveTools(qe(a?.tools??[],t.getActiveTools(),t.getAllTools().map(e=>e.name))),z?.dispose(),z=void 0,N=!1,P=void 0,R=void 0,U=!1,L=void 0,W=void 0,G=`idle`,K=void 0,X(e),Z(),m.recordEvent(r.PLAN_EVENT.modeDisabled,{[pe]:n,[O]:`normal`,"plan.written":s,"plan.restored":!0}),!0}if(it){let t=e=>{if(Y(),e.aborted)throw e.reason instanceof Error?e.reason:Error(`Plan voice transition was cancelled.`)},n={descriptor:{source:Ae,id:`plan-activate`,name:`activate_plan`,label:`Activate Plan`,description:`Activate or switch the serialized read-only planning mode flavor.`,order:10,inputSchema:je,resultSchema:Ne},execute(e,n){let r=Be(I(e)?e.flavor:void 0);if(!r)throw Error(`A valid plan flavor is required.`);return t(n.signal),Q(async()=>{t(n.signal);let e=N,i=P;return await $(n.context,r,De,!1),{active:N,flavor:P??r,changed:e!==N||i!==P}})}},r={descriptor:{source:Ae,id:`plan-exit`,name:`exit_plan`,label:`Exit Plan`,description:`Exit read-only planning mode and restore the previous agent configuration.`,order:20,inputSchema:Me,resultSchema:Ne},execute(e,n){return t(n.signal),Q(async()=>{t(n.signal);let e=N,r=await bt(n.context,De);return{active:N,flavor:P??null,changed:e!==N||r}})}};e.inject([h.DOOM_VOICE_TOOLS_SERVICE],e=>{let t=(0,h.requireDoomVoiceToolsService)(e),i=t.register(n),a=t.register(r);return()=>{a.dispose(),i.dispose()}})}nt=n.createFablePlanFlow({broker:st,isAuthorized:()=>x&&N&&P===`fable`&&ct(),onStage:e=>{x&&(G=e,(e===`completed`||e===`failed`||e===`cancelled`)&&(K=void 0),q&&X(q),N&&Z())},onError:e=>{x&&m.recordError(r.PLAN_EVENT.modeEnabled,e,{[O]:`fable`,"plan.fable.reason":`flow_error`})}}),t.registerTool({name:T,label:`Record Debug Evidence`,description:`Record optional bounded debug evidence while planning a repository fix.`,promptSnippet:`Record the issue and any relevant reproduction, logs, traces, process output, or browser evidence`,parameters:{type:`object`,properties:{issue:{type:`string`},expectedBehavior:{type:`string`},reproductionAttempt:{type:`string`},actualBehavior:{type:`string`},logs:{type:`array`,items:{type:`string`}},correlatedTraceEvidence:{type:`array`,items:{type:`string`}},processOutput:{type:`array`,items:{type:`string`}},browserConsoleEvidence:{type:`array`,items:{type:`string`}},correlationIds:{type:`array`,items:{type:`string`}},timestamps:{type:`array`,items:{type:`string`}},verifiedFacts:{type:`array`,items:{type:`string`}},hypotheses:{type:`array`,items:{type:`string`}},unavailableEvidence:{type:`array`,items:{type:`string`}}},required:[`issue`],additionalProperties:!1},async execute(e,t){if(Y(),!N||P!==`debug`)return{content:[{type:`text`,text:`Debug planning is not active.`}],details:{recorded:!1}};try{W=Ge(t)}catch(e){throw m.recordWarning(r.PLAN_EVENT.modeEnabled,e,{[O]:`debug`,"plan.debug.reason":`invalid_evidence`}),e}return q&&X(q),{content:[{type:`text`,text:`Debug evidence recorded as optional planning context.`}],details:{recorded:!0}}}}),t.registerTool({name:se,label:`Run Fable Plan`,description:`Send a bounded, sanitized planning packet to the local Fable broker for one repository-aware draft.`,promptSnippet:`Run the local Fable draft with the bounded planning packet`,parameters:{type:`object`,properties:{goal:{type:`array`,items:{type:`string`}},constraints:{type:`array`,items:{type:`string`}},decisions:{type:`array`,items:{type:`string`}},verifiedFindings:{type:`array`,items:{type:`object`}},inferredFindings:{type:`array`,items:{type:`string`}},unresolvedQuestions:{type:`array`,items:{type:`string`}},currentPlan:{type:`string`}},additionalProperties:!1},async execute(e,t,n){let r=S;if(Y(r),!N||P!==`fable`)return{content:[{type:`text`,text:`Fable planning is not active.`}],details:{started:!1}};if(!ct())return{content:[{type:`text`,text:`The local Fable broker is unavailable. Fable planning remains disabled.`}],details:{started:!1,errorCode:`broker_unavailable`}};let i=nt;if(!i)throw Error(`Fable planning flow is unavailable.`);let a=i.run(t,n);J=a;try{let e=await a;return Y(r),{content:[{type:`text`,text:e.draft?`Fable draft:\n${e.draft}`:`Fable planning ${e.status}: ${e.errorCode??`no output`}.`}],details:{...e,started:!0}}}finally{x&&r===S&&J===a&&(J=void 0)}}}),t.registerTool({name:y,label:`Write Plan`,description:`After presenting the complete implementation plan as Markdown in chat, save that visible plan to a unique Markdown file in the configured plans directory.`,promptSnippet:`Save the implementation plan already presented in chat to the configured plans directory`,parameters:{type:`object`,properties:{},additionalProperties:!1},async execute(e,n,i,a,c){let ne=S;if(Y(ne),!N)return{content:[{type:`text`,text:`Plan mode is disabled. Use normal file tools instead.`}],details:{written:!1}};let u=Le(c.sessionManager.getBranch(),e);if(!u){let e=Error(`Present the complete implementation plan as visible Markdown in chat before calling write_plan.`);throw m.recordError(r.PLAN_EVENT.writePlanFailed,e,{"plan.reason":`missing_visible_plan`}),e}let d=Date.now(),f=(0,o.getHarnessState)().root??c.cwd,p=(0,o.resolvePlanningPlansDirectory)(R?.plansDirectory,f,ee.default.homedir()),re=Fe(c.sessionManager.getSessionId());H??=Ie();let h=V?.planId===H&&te.default.dirname(V.path)===p?V.path:void 0,g=Pe(u),_=H,v=h??te.default.join(p,`${g}--${_}.md`),y=new AbortController,b=Error(`Writing ${v} timed out after ${ye}ms.`),ae=setTimeout(()=>y.abort(b),ye),C=AbortSignal.any([y.signal,ot.signal,...i?[i]:[]]),w=`checking`,oe=()=>{a?.({content:[{type:`text`,text:`${w===`checking`?`Checking`:`Writing`} ${v}...`}],details:{phase:w,path:v,startedAt:d}})},T=(e,t)=>{m.recordWarning(r.PLAN_EVENT.writePlanUnsafePath,t,{"plan.phase":w,"plan.refusal":e})};try{oe();try{await F(()=>l.default.promises.mkdir(p,{recursive:!0,mode:448}),C)}catch(e){throw C.aborted?Re(C):Error(`Could not create the configured plans directory ${p}.`,{cause:e})}let e=await F(()=>l.default.promises.lstat(p),C),n=await F(()=>l.default.promises.realpath(p),C);if(e.isSymbolicLink()||!e.isDirectory())return T(`unsafe_plans_directory`,`Refused to write the plan because the configured path is unsafe.`),{content:[{type:`text`,text:`Refused to write the plan because the configured path is unsafe.`}],details:{written:!1,path:v,phase:w,durationMs:Date.now()-d}};Y(ne),w=`writing`,oe();let i;try{for(let e=0;e<3;e+=1)try{let e=h?0:l.default.constants.O_EXCL;i=await F(()=>l.default.promises.open(v,l.default.constants.O_CREAT|l.default.constants.O_WRONLY|l.default.constants.O_NONBLOCK|l.default.constants.O_NOFOLLOW|e,384),C);break}catch(t){if(!(!h&&t instanceof Error&&`code`in t&&String(t.code)===`EEXIST`)||e===2)throw t;_=Ie(),H=_,v=te.default.join(p,`${g}--${_}.md`),Z()}if(!i)throw Error(`Could not allocate a unique plan filename in ${p}.`);let e=await F(()=>l.default.promises.realpath(p),C),t=await F(()=>i.stat(),C);if(e!==n||!t.isFile()||t.nlink!==1)return T(`invalid_destination`,fe),{content:[{type:`text`,text:fe}],details:{written:!1,path:v,phase:w,durationMs:Date.now()-d}};await F(()=>i.truncate(0),C),await F(()=>i.writeFile(u,{encoding:`utf8`,signal:C}),C)}catch(e){if(e instanceof Error&&`code`in e&&[`EISDIR`,`ELOOP`,`ENXIO`].includes(String(e.code)))return T(`invalid_destination`,fe),{content:[{type:`text`,text:fe}],details:{written:!1,path:v,phase:w,durationMs:Date.now()-d}};throw e}finally{await i?.close()}return Y(ne),V={content:u,path:v,sessionId:re,planId:_,title:g,writtenAt:new Date().toISOString()},t.appendEntry(ie,V),Ve=!0,U=!1,m.recordEvent(r.PLAN_EVENT.planWritten,{"plan.written":!0,"plan.bytes":s.Buffer.byteLength(u,`utf8`),"plan.duration_ms":Date.now()-d,"plan.revised":!!h}),{content:[{type:`text`,text:`Wrote implementation plan to ${v}.`}],details:{written:!0,path:v,phase:w,durationMs:Date.now()-d}}}catch(e){if(!x||ne!==S)throw Re(ot.signal);let t={"plan.phase":w,"plan.duration_ms":Date.now()-d};if(y.signal.aborted&&!i?.aborted){let n=Error(`write_plan timed out during ${w} for ${v}.`,{cause:e});throw m.recordError(r.PLAN_EVENT.writePlanTimedOut,n,{...t,"plan.timeout_ms":ye}),n}throw m.recordError(r.PLAN_EVENT.writePlanFailed,e,{...t,"plan.cancelled":i?.aborted??!1}),e}finally{clearTimeout(ae)}}});async function xt(e,t){if(Y(),m.recordEvent(r.PLAN_EVENT.planReviewCompleted,{"plan.outcome":e===k?`exited`:`continued`}),e===k){let e=await Q(()=>bt(t,`plan_approved`));return{content:[{type:`text`,text:e?`The user approved exiting plan mode. Full tool access is restored. Begin implementing the approved plan.`:`The previous model could not be restored. Plan mode remains read-only.`}],details:{exited:e}}}return Ve=!1,{content:[{type:`text`,text:`The user chose to continue planning. Remain read-only and refine the plan.`}],details:{exited:!1}}}t.registerTool({name:v,label:`Complete Plan`,description:`Present the saved implementation plan for exit-or-continue approval. Interactive sessions use the normal selector; autonomous voice narrates a plain-text handoff and waits for the next user message.`,promptSnippet:`Request explicit exit-or-continue approval after the saved plan has been presented`,promptGuidelines:[`After write_plan succeeds, call complete_plan without a decision so it can present the review question.`,`When autonomous voice returns an awaiting-response result, stop. Interpret the next ordinary user message, then call complete_plan again with decision "exit" or "continue".`,`Never pass decision "${k}" without the user explicitly choosing "${be}".`],parameters:{type:`object`,properties:{decision:{type:`string`,enum:[k,Se]}},additionalProperties:!1},async execute(e,t,n,i,a){let o=S;if(Y(o),!N)return{content:[{type:`text`,text:`Plan mode is already disabled.`}],details:{exited:!1}};if(!Ve)return{content:[{type:`text`,text:`Present the complete plan in chat and save it with write_plan before requesting approval.`}],details:{exited:!1}};if(!a.hasUI)return m.recordEvent(r.PLAN_EVENT.planReviewCompleted,{"plan.outcome":`no_ui`}),{content:[{type:`text`,text:`The plan is complete, but this run cannot ask the user interactively. Remain in plan mode until the user uses SPC p e.`}],details:{exited:!1}};let s=I(t)?t.decision:void 0,c=s===k||s===Se;if(U){if(!c)throw Error(`The narrated plan review is waiting for an explicit exit or continue decision.`);return U=!1,xt(s,a)}if(s!==void 0)throw Error(`Call complete_plan without a decision before submitting a narrated plan-review answer.`);if(await lt())return Y(o),U=!0,ut();let l=await a.ui.select(`Plan complete. What would you like to do?`,[be,xe]);return Y(o),xt(l===be?k:Se,a)}}),it&&e.inject([g.DOOM_UI_HUB_SERVICE],e=>{let t=(0,g.requireDoomUiHub)(e).registerLeaderActions({source:ce,handlers:{"plan.normal":()=>{let e=x?q:void 0;return e?Q(()=>$(e,`normal`,M,!1)):void 0},"plan.exit":()=>{let e=x?q:void 0;return e?Q(async()=>{await bt(e,M)}):void 0},"plan.debug":()=>{let e=x?q:void 0;return e?Q(()=>$(e,`debug`,M,!1)):void 0},"plan.fable":()=>{let e=x?q:void 0;return e?Q(()=>$(e,`fable`,M,!1)):void 0}},onError:(e,t)=>{x&&(q?.hasUI&&q.ui.notify(`Leader action ${t} failed. Plan mode remains read-only.`,D),m.recordError(r.PLAN_EVENT.modeEnabled,e,{[pe]:M,"plan.action":t}))}});return Oe=t,()=>{t(),Oe===t&&(Oe=void 0)}}),t.on(`tool_call`,async(e,t)=>{if(!x||!N)return;let n=P===`debug`&&(e.toolName===T||at.has(e.toolName))||P===`fable`&&e.toolName===se;if(!Te.has(e.toolName)&&!n)return{block:!0,reason:`Plan mode blocks the ${e.toolName} tool. Use SPC p e to restore access.`};let r=Ye(R?.subagents,t.model);if(e.toolName===oe){r&&Ze(e.input,r);return}if(e.toolName!==w)return;let i=e.input;if(Qe(i.action))return{block:!0,reason:`Plan mode blocks subagent management action '${i.action}'. Use SPC p e first.`};Je(i),r&&Xe(i,r)}),t.on(`before_agent_start`,async(e,t)=>{if(!x)return;let n=[];if(N){let e=(0,o.getHarnessState)().root??t.cwd,r=(0,o.resolvePlanningPlansDirectory)(R?.plansDirectory,e,ee.default.homedir());n.push(`[PLAN MODE ACTIVE]\nYou are in repository read-only plan mode. The dedicated write_plan tool may write one unique Markdown plan file under ${r}.\n\nExplore the codebase and produce a concrete implementation plan. For every plan, first call subagent with action "agents", cluster the exploration by independent domain, subsystem, or integration boundary, and create a provisional task graph with the task tool. Select specialized agents by matching their names and descriptions to each cluster. Assign every unblocked delegated task through the task tool, and use a one-shot inlineAgent with a focused systemPrompt when no discovered specialist fits. Treat the initial graph as provisional, not as a fixed contract. After findings arrive, review the entire graph at least once and perform one to three review passes in total. In each pass, use the evidence to add, rewrite, delete, cancel, reassign, or change blockedBy relationships for tasks when warranted. Do not keep following tasks that new information has made stale. Stop revising early when the graph is stable, or after the third pass.\n\nEvery plan must end with a delegated planning-draft stage blocked by all exploration and decision tasks. A single-boundary plan gets one planning draft. A complex plan spanning multiple subsystems, domains, packages, apps, or integration boundaries gets two planning drafts concurrently. Use three concurrent drafts instead when the work is cross-layer, migration-sensitive, security-sensitive, or similarly high risk. Assign each draft through the task tool to the discovered "planner" agent with context "fork" so it receives the conversation and gathered evidence. If the planner agent is unavailable, assign the same draft task through the task tool with a focused inlineAgent. All children receive read, Bash, grep, find, ls, and configured MCP tools with artifacts disabled. Bash and MCP tools are for read-only inspection and must not modify files, external systems, or repository state. Doom Team runs asynchronously, so do not poll. After launch, continue non-overlapping exploration or end your turn; completion notifications wake the parent session.\n\nAfter all planning drafts complete, the main agent must compare the candidates, pick the strongest draft, cross-check it against the gathered evidence and the other drafts, resolve conflicts and gaps, and ask the user only for product decisions. A child produces the draft, but the main agent owns and synthesizes the final plan. Do not modify files or repository state except through write_plan. Start the final plan with a meaningful Markdown H1 because write_plan derives the filename from it. Present the complete plan as visible Markdown in chat, then call write_plan with no arguments. After write_plan succeeds, clear the completed durable task graph and call complete_plan without a decision. In an interactive text session, complete_plan uses the normal exit-or-continue selector. Under autonomous voice, it displays and narrates the choices without opening a blocking dialog, then ends the turn. Interpret the user's next ordinary message and call complete_plan again with decision "exit" or "continue". Do not exit plan mode without explicit approval.`,a.buildFlavorPlanningPrompt(P,r,W,G))}if(V&&n.push(`[CURRENT PLAN]\nSource: ${V.path}\n\n${V.content}`),n.length!==0)return{systemPrompt:`${e.systemPrompt}\n\n${n.join(`
4
+ `).trim()||void 0}}function ze(e){return e.reason instanceof Error?e.reason:Error(`Plan write cancelled.`)}async function P(e,t){if(t.aborted)throw ze(t);let n,r=new Promise((e,r)=>{n=()=>r(ze(t)),t.addEventListener(`abort`,n,{once:!0})});try{return await Promise.race([e(),r])}finally{n&&t.removeEventListener(`abort`,n)}}function F(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function I(e){return typeof e==`string`}function L(e){if(!(!F(e)||!I(e.provider)||!I(e.id))&&!(!e.provider||!e.id||e.provider.length>128||e.id.length>128))return{provider:e.provider,id:e.id}}function R(e){return I(e)&&N.includes(e)?e:void 0}function z(e){return I(e)&&Oe.includes(e)?e:void 0}function B(e){if(!(!Array.isArray(e)||e.some(e=>!I(e))))return e.map(e=>e)}function Be(e){if(!F(e))return;let t=B(e.tools),n=R(e.thinking);if(!t||n===void 0)return;let r=e.model===void 0?void 0:L(e.model);if(!(e.model!==void 0&&!r))return{tools:t,...r?{model:r}:{},thinking:n}}function Ve(e){return e===`normal`||e===`debug`||e===`fable`?e:void 0}function V(e){if(e.version!==2)return;let t=e.activeFlavor===void 0?void 0:Ve(e.activeFlavor);if(e.activeFlavor!==void 0&&!t)return;let n=e.originalSnapshot===void 0?void 0:Be(e.originalSnapshot);if(e.originalSnapshot!==void 0&&!n)return;let r=e.planId===void 0?void 0:I(e.planId)?e.planId:void 0;if(e.planId!==void 0&&r===void 0)return;let i=e.interruptedFableStage===void 0?void 0:z(e.interruptedFableStage);if(!(e.interruptedFableStage!==void 0&&!i))return{version:2,...t?{activeFlavor:t}:{},...n?{originalSnapshot:n}:{},...r?{planId:r}:{},...i?{interruptedFableStage:i}:{}}}function H(e){if(e.enabled!==!0&&e.enabled!==!1)return;let t=e.toolsBeforePlanMode===void 0?void 0:B(e.toolsBeforePlanMode);if(e.toolsBeforePlanMode!==void 0&&!t)return;let n=e.modelBeforePlanMode===void 0?void 0:L(e.modelBeforePlanMode);if(e.modelBeforePlanMode!==void 0&&!n)return;let r=e.thinkingBeforePlanMode===void 0?void 0:R(e.thinkingBeforePlanMode);if(e.thinkingBeforePlanMode!==void 0&&r===void 0)return;let i=e.planId===void 0?void 0:I(e.planId)?e.planId:void 0;if(e.planId!==void 0&&i===void 0)return;let a=t&&r!==void 0?{tools:t,...n?{model:n}:{},thinking:r}:void 0;return{version:2,...e.enabled?{activeFlavor:`normal`}:{},...a?{originalSnapshot:a}:{},...i?{planId:i}:{}}}function He(e){if(F(e))return V(e)??H(e)}function Ue(e){if(!F(e)||!I(e.content)||!I(e.path)||!I(e.sessionId)||!I(e.title)||!I(e.writtenAt))return;let t=e.planId===void 0?void 0:I(e.planId)?e.planId:void 0;if(e.planId===void 0||t!==void 0)return{content:e.content,path:e.path,sessionId:e.sessionId,...t?{planId:t}:{},title:e.title,writtenAt:e.writtenAt}}function U(e,t,n){if(!I(e))throw Error(`${t} must be a string.`);let r=e.trim();if(n&&!r)throw Error(`${t} must not be empty.`);if(s.Buffer.byteLength(r,`utf8`)>4096)throw Error(`${t} exceeds the bounded debug evidence limit.`);return r}function We(e,t){if(!Array.isArray(e)||e.length>32)throw Error(`${t} must be a bounded string array.`);return e.map((e,n)=>U(e,`${t}[${n}]`,!1))}function W(e,t){return e===void 0?``:U(e,t,!1)}function G(e,t){return e===void 0?[]:We(e,t)}function Ge(e,t,n){let r=new Set(t),i=Object.keys(e).find(e=>!r.has(e));if(i)throw Error(`${n} contains unsupported field '${i}'.`)}function Ke(e){if(!F(e))throw Error(`Debug evidence must be an object.`);Ge(e,[`issue`,`expectedBehavior`,`reproductionAttempt`,`actualBehavior`,`logs`,`correlatedTraceEvidence`,`processOutput`,`browserConsoleEvidence`,`correlationIds`,`timestamps`,`verifiedFacts`,`hypotheses`,`unavailableEvidence`],`Debug evidence`);let t={issue:U(e.issue,`issue`,!0),expectedBehavior:W(e.expectedBehavior,`expectedBehavior`),reproductionAttempt:W(e.reproductionAttempt,`reproductionAttempt`),actualBehavior:W(e.actualBehavior,`actualBehavior`),logs:G(e.logs,`logs`),correlatedTraceEvidence:G(e.correlatedTraceEvidence,`correlatedTraceEvidence`),processOutput:G(e.processOutput,`processOutput`),browserConsoleEvidence:G(e.browserConsoleEvidence,`browserConsoleEvidence`),correlationIds:G(e.correlationIds,`correlationIds`),timestamps:G(e.timestamps,`timestamps`),verifiedFacts:G(e.verifiedFacts,`verifiedFacts`),hypotheses:G(e.hypotheses,`hypotheses`),unavailableEvidence:G(e.unavailableEvidence,`unavailableEvidence`)};if(s.Buffer.byteLength(JSON.stringify(t),`utf8`)>32768)throw Error(`Debug evidence exceeds the bounded packet size.`);return t}function K(e,t){let n=new Set(t),r=e.filter(e=>Te.has(e)&&n.has(e)),i=e.filter(e=>!Te.has(e)&&!A.has(e)&&!Pe.has(e)&&n.has(e)),a=j.filter(e=>n.has(e)),o=[v,oe,y].filter(e=>n.has(e));return[...new Set([...r,...i,...a,...o])]}function qe(e,t,n,r,i){let a=new Set(n),o=t.filter(e=>!Te.has(e)&&!A.has(e)&&!Pe.has(e)),s=K([...new Set([...e,...o])].filter(e=>!Pe.has(e)),n),c=r===`debug`?[T,...i].filter(e=>a.has(e)):[];r===`fable`&&a.has(se)&&c.push(se);for(let e of[f.MINOR_MODE_TOOL_NAME,...h.VOICE_MODE_TOOL_NAMES])t.includes(e)&&a.has(e)&&c.push(e);return[...new Set([...s,...c])]}function Je(e,t,n){let r=new Set(n),i=new Set(t),a=e.filter(e=>!Pe.has(e)&&r.has(e)&&(e!==b||i.has(e))),o=t.filter(e=>!Te.has(e)&&!A.has(e)&&!Pe.has(e)&&r.has(e)),s=[f.MINOR_MODE_TOOL_NAME,...h.VOICE_MODE_TOOL_NAMES].filter(e=>i.has(e)&&r.has(e));return[...new Set([...a,...o,...s])]}function q(e){if(e.output=!1,e.progress=!1,e.worktree=!1,Array.isArray(e.parallel))for(let t of e.parallel)q(t);else e.parallel&&q(e.parallel)}function Ye(e){if(e.action!==void 0){(0,m.isSubagentAction)(e.action)&&(0,m.subagentActionAcceptsField)(e.action,`artifacts`)&&(e.artifacts=!1);return}e.artifacts=!1,e.output=!1,e.share=!1,e.progress=!1,e.worktree=!1,delete e.chainDir,delete e.outputSchema,delete e.sessionDir;for(let t of e.tasks??[])q(t);for(let t of e.chain??[])q(t)}function Xe(e,t){if(!e?.model&&!e?.thinking)return;let n=e.model??(t?`${t.provider}/${t.id}`:void 0);if(n)return e.thinking?`${n.replace(/:(?:off|minimal|low|medium|high|xhigh|max)$/,``)}:${e.thinking}`:n}function Ze(e,t){if(e.action===`run`)for(let n of e.requests??[])n.model=t}function Qe(e,t){if(e.action===`assign`)for(let n of e.assignments??[])n.model=t}function $e(e){return new Set([`append-step`,`create`,`delete`,`disable`,`eject`,`enable`,`grant-spawn-budget`,`reset`,`schedule`,`schedule-cancel`,`update`,`watchdog.configure`]).has(e??``)}function et(e,t){let n=e.indexOf(`/`);if(n>0)return t.modelRegistry.find(e.slice(0,n),e.slice(n+1));let r=t.modelRegistry.getAvailable().filter(t=>t.id===e);return r.find(e=>e.provider===t.model?.provider)??(r.length===1?r[0]:void 0)}function tt(e=process.env,n=process.cwd()){return t.loadDoomConfig((0,o.loadHarnessState)(e).state.root??n).modes?.planning}function nt(e){let t={key:`p`,label:`plan`,detail:`read-only planning modes`,order:60};return[e?{id:`plan.exit`,path:[t,{key:`e`,label:`exit`,detail:`restore and exit`,tone:`exit`}],action:{name:`plan.exit`}}:{id:`plan.normal`,path:[t,{key:`e`,label:`enter`,detail:`read-only planning`}],action:{name:`plan.normal`}},{id:`plan.debug`,path:[t,{key:`d`,label:`debug`,detail:`adaptive debug planning`}],action:{name:`plan.debug`}},{id:`plan.fable`,path:[t,{key:`f`,label:`fable`,detail:`repository-aware draft`}],action:{name:`plan.fable`}}]}function J(e,t,c=tt,m=r.createPlanTelemetry(),b={}){let x=!0,S=0,C,E,A,j,N,Oe,Ae,Pe=()=>{let e=Ae?.modelRegistry;return e?e.getAvailable().filter(t=>e.hasConfiguredAuth(t)).map(e=>({provider:e.provider,id:e.id})):[]},I=!1,L,R,z,B,Be,V,H,U,We=!1,W=!1,G,Ge,K=`idle`,q,J,Y,rt,it=Promise.resolve(),at=b.doomIntegrations??!0,ot=new Set(b.debugDiagnosticTools??_e),st=new AbortController;e.effect(()=>()=>ft(),`${ce}/runtime`);let ct=b.fableBroker??{start:(e,t)=>{let n=V;if(!n)throw Error(`The session-bound Fable broker is unavailable.`);return n.start(e,t)},cancel:(e,t)=>{V?.cancel({requester:d.FABLE_PLAN_REQUESTER,operationId:e,reason:t})}},lt=()=>b.fableBroker!==void 0||V!==void 0;e.inject([d.DOOM_FABLE_PLAN_SERVICE],e=>{let t=(0,d.readDoomFablePlanService)(e);if(t)return V=t,()=>{V===t&&(V=void 0,Y&&rt?.cancel(`The Team Fable service was unloaded.`))}}),e.inject([re.DOOM_SUBAGENT_POLICY_SERVICE],e=>{let t=(0,re.readDoomSubagentPolicyService)(e);if(t)return Be=t,ht(),()=>{Be===t&&(B?.dispose(),B=void 0,Be=void 0)}}),at&&(e.inject([f.DOOM_MINOR_MODE_CATALOG_SERVICE,p.DOOM_NARRATION_SERVICE],e=>{let t={modes:(0,f.requireMinorModeCatalog)(e),narration:(0,p.requireDoomNarrationService)(e)};return G=t,()=>{G===t&&(G=void 0)}}),e.inject([f.DOOM_MINOR_MODE_CATALOG_SERVICE],e=>{let t=(0,f.registerMinorModeOwner)((0,f.requireMinorModeCatalog)(e),{descriptor:{source:ce,id:`plan`,label:`Plan`,description:`Read-only planning with normal, debug, and Fable flavors.`,order:40,actions:[{id:le,label:`Activate`,description:`Activate plan mode or switch its planning flavor.`,contexts:[`tui`,`headless`],parameters:[{name:`flavor`,label:`Flavor`,kind:`enum`,required:!0,choices:[{value:`normal`,label:`Normal`},{value:`debug`,label:`Debug`},{value:`fable`,label:`Fable`}]}]},{id:ue,label:`Deactivate`,description:`Exit plan mode and restore the previous agent configuration.`,contexts:[`tui`,`headless`],parameters:[]}]},initialState:mt(),async handleAction(e,t,n){if(!x)throw Error(`Plan runtime is disposed.`);if(e===le){let e=t.flavor;if(e!==`normal`&&e!==`debug`&&e!==`fable`)throw Error(`A valid plan flavor is required.`);return await $(()=>xt(n.context,e,M,!1)),{message:`Plan mode is active with the ${e} flavor.`}}if(e===ue)return{message:await $(()=>St(n.context,M))?`Plan mode deactivated.`:`Plan mode remains active.`};throw Error(`Unknown plan mode action: ${e}`)},onError:e=>void m.recordError(r.PLAN_EVENT.configLoadFailed,e)});return E=t,()=>{t.dispose(),E===t&&(E=void 0)}}),e.inject([g.DOOM_UI_HUB_SERVICE],e=>{let t=(0,g.requireDoomUiHub)(e),n=t.registerLeader({source:ce,bindings:nt(I)}),r=t.registerConfig({source:ce,listSections:()=>{let e=Pe();try{return i.planConfigSections(c(),Oe,e)}catch(t){return i.planConfigSections(void 0,t instanceof Error?t.message:String(t),e)}},handlers:{[u.CONFIG_ACTION.set]:async({fieldId:e,value:t})=>{let n=i.planSettingByFieldId(e);!n||!t||(Oe=void 0,await(0,ne.setDoomConfigValue)((0,o.globalDoomConfigPath)(),[...n.keyPath],t))},[u.CONFIG_ACTION.clear]:async({fieldId:e})=>{let t=i.planSettingByFieldId(e);t&&(Oe=void 0,await(0,ne.unsetDoomConfigValue)((0,o.globalDoomConfigPath)(),[...t.keyPath]))}},onError:e=>{x&&(Oe=e instanceof Error?e.message:String(e))}});return A=n,j=r,()=>{r.dispose(),n.dispose(),j===r&&(j=void 0),A===n&&(A=void 0)}}));function X(e=S){if(!x||e!==S)throw Error(`Plan runtime is disposed or stale.`)}async function ut(){let e=G;if(!e||!e.modes.list().some(e=>e.descriptor.source===p.DOOM_VOICE_SOURCE&&e.descriptor.id===p.DOOM_VOICE_AUTO_MODE_ID&&e.state.activation===`active`))return!1;try{return await e.narration.request(we),!0}catch(e){return m.recordWarning(r.PLAN_EVENT.planReviewCompleted,e,{"plan.outcome":`narration_failed`}),!1}}function dt(){return{content:[{type:`text`,text:`${Ce}\n\nThe choices were spoken through autonomous voice. Stop now and wait for the user's next message; it will arrive as an ordinary user message.`}],details:{answers:[],awaitingResponse:!0,cancelled:!1,delivery:`voice`,voicePrompt:Ce},terminate:!0}}function ft(){return C??=(async()=>{x=!1,S+=1,st.abort(Error(`Plan runtime is shutting down.`));let e=J,n=R,r=[];rt?.cancel(`Plan runtime is shutting down.`);let i=[it,...Y?[Y]:[]],a=await Promise.allSettled(i);for(let e of a)e.status===`rejected`&&r.push(e.reason);if(I&&n&&e)try{await vt(e,n,!0)&&t.setActiveTools(Je(n.tools,t.getActiveTools(),t.getAllTools().map(e=>e.name)))}catch(e){r.push(e)}let o=[()=>B?.dispose(),()=>N?.(),()=>j?.dispose(),()=>A?.dispose(),()=>E?.dispose()];for(let e of o)try{e()}catch(e){r.push(e)}B=void 0,G=void 0,N=void 0,j=void 0,A=void 0,E=void 0,Y=void 0,J=void 0,W=!1,I=!1,L=void 0,R=void 0,z=void 0;try{await m.shutdown()}catch(e){r.push(e)}if(r.length>0)throw AggregateError(r,`Plan runtime cleanup failed.`)})(),C}function pt(){x&&A?.update(nt(I))}function mt(){let e=L,t=e===`fable`&&K!==`idle`?` · ${K}`:``;return{activation:I?`active`:`inactive`,condition:K===`failed`?`failed`:K===`interrupted`?`degraded`:`ready`,...I&&e?{detail:`${e}${t} - read only`,color:D,modelContextVariant:e}:{},actions:[{id:le,enabled:!0},...I?[{id:ue,enabled:!0}]:[{id:ue,enabled:!1,disabledReason:`Plan mode is inactive.`}]]}}function Z(e){if(!x)return;pt();let t=L,n=I&&t?`plan:${t}`:void 0;e.ui.setStatus(`plan-mode`,n?e.ui.theme.fg(D,n):void 0),E?.publish(mt())}function ht(){if(!x||!at||!I||!L||!J)return;let e=[...Ee],t={owner:ce,allowedTools:e,requiredTools:[ae,`mcp`],allowMcpTools:!0,allowedExternalProfiles:L===`fable`?[n.FABLE_PLAN_PROFILE]:[],denyExtensions:!1};B?B.update(t):B=Be?.register(t)}function Q(){x&&t.appendEntry(_,{version:2,...I&&L?{activeFlavor:L}:{},...R?{originalSnapshot:{tools:[...R.tools],...R.model?{model:{...R.model}}:{},thinking:R.thinking}}:{},...U?{planId:U}:{},...q?{interruptedFableStage:q}:{}})}function gt(e,t,n){m.recordWarning(r.PLAN_EVENT.modelResolveFailed,`Planning model ${n}: ${t}`,{[pe]:t,"plan.model.phase":e,"plan.model.reason":n})}async function _t(e){let n=S;X(n);let r=z?.main;if(r){if(r.model){let i=et(r.model,e);if(!i)e.ui.notify(`Planning model not found: ${r.model}`,D),gt(`apply`,r.model,me);else{let a=await t.setModel(i);X(n),a||(e.ui.notify(`Planning model is unavailable without authentication: ${r.model}`,D),gt(`apply`,r.model,he))}}X(n),r.thinking&&t.setThinkingLevel(r.thinking)}}async function vt(e,n,i=!1){let a=S;if(i||X(a),!n)return!1;if(n.model){let o=`${n.model.provider}/${n.model.id}`,s=e.modelRegistry.find(n.model.provider,n.model.id);if(!s)return e.ui.notify(`Previous model not found: ${o}`,D),gt(`restore`,o,me),!1;try{let n=await t.setModel(s);if(i||X(a),!n)return e.ui.notify(`Previous model is unavailable without authentication: ${o}`,D),gt(`restore`,o,he),!1}catch(t){return i||X(a),e.ui.notify(`Previous model could not be restored: ${o}`,D),gt(`restore`,o,ge),m.recordError(r.PLAN_EVENT.modelResolveFailed,t,{[pe]:o,"plan.model.phase":`restore`,"plan.model.reason":ge}),!1}}return i||X(a),t.setThinkingLevel(n.thinking),!0}function yt(e){I&&L?e.ui.notify(`Already in plan:${L}.`,de):e.ui.notify(`Plan mode is inactive.`,de)}function $(e){let t=S,n=async()=>{X(t);let n=await e();return X(t),n},r=it.then(n,n);return it=r.then(()=>void 0,()=>void 0),r}async function bt(e){let t=S;if(X(t),!Y&&!K.match(/^(draft|review)$/u))return;(K===`draft`||K===`review`)&&(q=K),rt?.cancel(e);let n=Y;if(n)try{await n,X(t)}catch(e){X(t),m.recordError(r.PLAN_EVENT.modeDisabled,e,{[k]:`fable`,"plan.fable.reason":`cancel_failed`})}X(t),Y=void 0,q&&(K=`interrupted`),J&&Z(J),Q()}async function xt(e,n,i,a){let o=S;if(X(o),J=e,I){if(L===n){yt(e);return}L===`fable`&&await bt(`Leaving Fable for plan:${n}.`),X(o),L=n,ht(),t.setActiveTools(qe(R?.tools??[],t.getActiveTools(),t.getAllTools().map(e=>e.name),n,ot)),Z(e),Q(),m.recordEvent(r.PLAN_EVENT.modeEnabled,{[O]:i,[k]:n,"plan.tool.count":t.getActiveTools().length});return}let s;try{s=c()}catch(e){throw m.recordError(r.PLAN_EVENT.configLoadFailed,e,{[O]:i}),e}a?U??=Le():(H=void 0,We=!1,W=!1,U=Le(),q=void 0,K=`idle`),z=s,R??={tools:[...t.getActiveTools()],...e.model?{model:{provider:e.model.provider,id:e.model.id}}:{},thinking:t.getThinkingLevel()},I=!0,L=n,t.setActiveTools(qe(R.tools,t.getActiveTools(),t.getAllTools().map(e=>e.name),n,ot)),ht(),await _t(e),X(o),Z(e),Q(),m.recordEvent(r.PLAN_EVENT.modeEnabled,{[O]:i,[k]:n,"plan.tool.count":t.getActiveTools().length,...z?.main?.model?{[pe]:z.main.model}:{},...z?.main?.thinking?{"plan.thinking":z.main.thinking}:{}})}async function St(e,n){let i=S;if(X(i),!I)return yt(e),!1;await bt(`Plan mode is exiting.`),X(i);let a=R,o=await vt(e,a);if(X(i),!o)return e.ui.notify(`Previous model was not restored. Plan mode remains read-only.`,D),Z(e),Q(),m.recordWarning(r.PLAN_EVENT.modeDisabled,`Plan mode restoration failed.`,{[O]:n,[k]:L??`unknown`,"plan.restored":!1}),!1;let s=!!H;return t.setActiveTools(Je(a?.tools??[],t.getActiveTools(),t.getAllTools().map(e=>e.name))),B?.dispose(),B=void 0,I=!1,L=void 0,z=void 0,W=!1,R=void 0,Ge=void 0,K=`idle`,q=void 0,Z(e),Q(),m.recordEvent(r.PLAN_EVENT.modeDisabled,{[O]:n,[k]:`normal`,"plan.written":s,"plan.restored":!0}),!0}if(at){let t=e=>{if(X(),e.aborted)throw e.reason instanceof Error?e.reason:Error(`Plan voice transition was cancelled.`)},n={descriptor:{source:ke,id:`plan-activate`,name:`activate_plan`,label:`Activate Plan`,description:`Activate or switch the serialized read-only planning mode flavor.`,order:10,inputSchema:je,resultSchema:Ne},execute(e,n){let r=Ve(F(e)?e.flavor:void 0);if(!r)throw Error(`A valid plan flavor is required.`);return t(n.signal),$(async()=>{t(n.signal);let e=I,i=L;return await xt(n.context,r,De,!1),{active:I,flavor:L??r,changed:e!==I||i!==L}})}},r={descriptor:{source:ke,id:`plan-exit`,name:`exit_plan`,label:`Exit Plan`,description:`Exit read-only planning mode and restore the previous agent configuration.`,order:20,inputSchema:Me,resultSchema:Ne},execute(e,n){return t(n.signal),$(async()=>{t(n.signal);let e=I,r=await St(n.context,De);return{active:I,flavor:L??null,changed:e!==I||r}})}};e.inject([h.DOOM_VOICE_TOOLS_SERVICE],e=>{let t=(0,h.requireDoomVoiceToolsService)(e),i=t.register(n),a=t.register(r);return()=>{a.dispose(),i.dispose()}})}rt=n.createFablePlanFlow({broker:ct,isAuthorized:()=>x&&I&&L===`fable`&&lt(),onStage:e=>{x&&(K=e,(e===`completed`||e===`failed`||e===`cancelled`)&&(q=void 0),J&&Z(J),I&&Q())},onError:e=>{x&&m.recordError(r.PLAN_EVENT.modeEnabled,e,{[k]:`fable`,"plan.fable.reason":`flow_error`})}}),t.registerTool({name:T,label:`Record Debug Evidence`,description:`Record optional bounded debug evidence while planning a repository fix.`,promptSnippet:`Record the issue and any relevant reproduction, logs, traces, process output, or browser evidence`,parameters:{type:`object`,properties:{issue:{type:`string`},expectedBehavior:{type:`string`},reproductionAttempt:{type:`string`},actualBehavior:{type:`string`},logs:{type:`array`,items:{type:`string`}},correlatedTraceEvidence:{type:`array`,items:{type:`string`}},processOutput:{type:`array`,items:{type:`string`}},browserConsoleEvidence:{type:`array`,items:{type:`string`}},correlationIds:{type:`array`,items:{type:`string`}},timestamps:{type:`array`,items:{type:`string`}},verifiedFacts:{type:`array`,items:{type:`string`}},hypotheses:{type:`array`,items:{type:`string`}},unavailableEvidence:{type:`array`,items:{type:`string`}}},required:[`issue`],additionalProperties:!1},async execute(e,t){if(X(),!I||L!==`debug`)return{content:[{type:`text`,text:`Debug planning is not active.`}],details:{recorded:!1}};try{Ge=Ke(t)}catch(e){throw m.recordWarning(r.PLAN_EVENT.modeEnabled,e,{[k]:`debug`,"plan.debug.reason":`invalid_evidence`}),e}return J&&Z(J),{content:[{type:`text`,text:`Debug evidence recorded as optional planning context.`}],details:{recorded:!0}}}}),t.registerTool({name:se,label:`Run Fable Plan`,description:`Send a bounded, sanitized planning packet to the local Fable broker for one repository-aware draft.`,promptSnippet:`Run the local Fable draft with the bounded planning packet`,parameters:{type:`object`,properties:{goal:{type:`array`,items:{type:`string`}},constraints:{type:`array`,items:{type:`string`}},decisions:{type:`array`,items:{type:`string`}},verifiedFindings:{type:`array`,items:{type:`object`}},inferredFindings:{type:`array`,items:{type:`string`}},unresolvedQuestions:{type:`array`,items:{type:`string`}},currentPlan:{type:`string`}},additionalProperties:!1},async execute(e,t,n){let r=S;if(X(r),!I||L!==`fable`)return{content:[{type:`text`,text:`Fable planning is not active.`}],details:{started:!1}};if(!lt())return{content:[{type:`text`,text:`The local Fable broker is unavailable. Fable planning remains disabled.`}],details:{started:!1,errorCode:`broker_unavailable`}};let i=rt;if(!i)throw Error(`Fable planning flow is unavailable.`);let a=i.run(t,n);Y=a;try{let e=await a;return X(r),{content:[{type:`text`,text:e.draft?`Fable draft:\n${e.draft}`:`Fable planning ${e.status}: ${e.errorCode??`no output`}.`}],details:{...e,started:!0}}}finally{x&&r===S&&Y===a&&(Y=void 0)}}}),t.registerTool({name:y,label:`Write Plan`,description:`After presenting the complete implementation plan as Markdown in chat, save that visible plan to a unique Markdown file in the configured plans directory.`,promptSnippet:`Save the implementation plan already presented in chat to the configured plans directory`,parameters:{type:`object`,properties:{},additionalProperties:!1},async execute(e,n,i,a,c){let ne=S;if(X(ne),!I)return{content:[{type:`text`,text:`Plan mode is disabled. Use normal file tools instead.`}],details:{written:!1}};let u=Re(c.sessionManager.getBranch(),e);if(!u){let e=Error(`Present the complete implementation plan as visible Markdown in chat before calling write_plan.`);throw m.recordError(r.PLAN_EVENT.writePlanFailed,e,{"plan.reason":`missing_visible_plan`}),e}let d=Date.now(),f=(0,o.getHarnessState)().root??c.cwd,p=(0,o.resolvePlanningPlansDirectory)(z?.plansDirectory,f,ee.default.homedir()),re=Ie(c.sessionManager.getSessionId());U??=Le();let h=H?.planId===U&&te.default.dirname(H.path)===p?H.path:void 0,g=Fe(u),_=U,v=h??te.default.join(p,`${g}--${_}.md`),y=new AbortController,b=Error(`Writing ${v} timed out after ${ve}ms.`),ae=setTimeout(()=>y.abort(b),ve),C=AbortSignal.any([y.signal,st.signal,...i?[i]:[]]),w=`checking`,oe=()=>{a?.({content:[{type:`text`,text:`${w===`checking`?`Checking`:`Writing`} ${v}...`}],details:{phase:w,path:v,startedAt:d}})},T=(e,t)=>{m.recordWarning(r.PLAN_EVENT.writePlanUnsafePath,t,{"plan.phase":w,"plan.refusal":e})};try{oe();try{await P(()=>l.default.promises.mkdir(p,{recursive:!0,mode:448}),C)}catch(e){throw C.aborted?ze(C):Error(`Could not create the configured plans directory ${p}.`,{cause:e})}let e=await P(()=>l.default.promises.lstat(p),C),n=await P(()=>l.default.promises.realpath(p),C);if(e.isSymbolicLink()||!e.isDirectory())return T(`unsafe_plans_directory`,`Refused to write the plan because the configured path is unsafe.`),{content:[{type:`text`,text:`Refused to write the plan because the configured path is unsafe.`}],details:{written:!1,path:v,phase:w,durationMs:Date.now()-d}};X(ne),w=`writing`,oe();let i;try{for(let e=0;e<3;e+=1)try{let e=h?0:l.default.constants.O_EXCL;i=await P(()=>l.default.promises.open(v,l.default.constants.O_CREAT|l.default.constants.O_WRONLY|l.default.constants.O_NONBLOCK|l.default.constants.O_NOFOLLOW|e,384),C);break}catch(t){if(!(!h&&t instanceof Error&&`code`in t&&String(t.code)===`EEXIST`)||e===2)throw t;_=Le(),U=_,v=te.default.join(p,`${g}--${_}.md`),Q()}if(!i)throw Error(`Could not allocate a unique plan filename in ${p}.`);let e=await P(()=>l.default.promises.realpath(p),C),t=await P(()=>i.stat(),C);if(e!==n||!t.isFile()||t.nlink!==1)return T(`invalid_destination`,fe),{content:[{type:`text`,text:fe}],details:{written:!1,path:v,phase:w,durationMs:Date.now()-d}};await P(()=>i.truncate(0),C),await P(()=>i.writeFile(u,{encoding:`utf8`,signal:C}),C)}catch(e){if(e instanceof Error&&`code`in e&&[`EISDIR`,`ELOOP`,`ENXIO`].includes(String(e.code)))return T(`invalid_destination`,fe),{content:[{type:`text`,text:fe}],details:{written:!1,path:v,phase:w,durationMs:Date.now()-d}};throw e}finally{await i?.close()}return X(ne),H={content:u,path:v,sessionId:re,planId:_,title:g,writtenAt:new Date().toISOString()},t.appendEntry(ie,H),We=!0,W=!1,m.recordEvent(r.PLAN_EVENT.planWritten,{"plan.written":!0,"plan.bytes":s.Buffer.byteLength(u,`utf8`),"plan.duration_ms":Date.now()-d,"plan.revised":!!h}),{content:[{type:`text`,text:`Wrote implementation plan to ${v}.`}],details:{written:!0,path:v,phase:w,durationMs:Date.now()-d}}}catch(e){if(!x||ne!==S)throw ze(st.signal);let t={"plan.phase":w,"plan.duration_ms":Date.now()-d};if(y.signal.aborted&&!i?.aborted){let n=Error(`write_plan timed out during ${w} for ${v}.`,{cause:e});throw m.recordError(r.PLAN_EVENT.writePlanTimedOut,n,{...t,"plan.timeout_ms":ve}),n}throw m.recordError(r.PLAN_EVENT.writePlanFailed,e,{...t,"plan.cancelled":i?.aborted??!1}),e}finally{clearTimeout(ae)}}});async function Ct(e,t){if(X(),m.recordEvent(r.PLAN_EVENT.planReviewCompleted,{"plan.outcome":e===xe?`exited`:`continued`}),e===xe){let e=await $(()=>St(t,`plan_approved`));return{content:[{type:`text`,text:e?`The user approved exiting plan mode. Full tool access is restored. Begin implementing the approved plan.`:`The previous model could not be restored. Plan mode remains read-only.`}],details:{exited:e}}}return We=!1,{content:[{type:`text`,text:`The user chose to continue planning. Remain read-only and refine the plan.`}],details:{exited:!1}}}t.registerTool({name:v,label:`Complete Plan`,description:`Present the saved implementation plan for exit-or-continue approval. Interactive sessions use the normal selector; autonomous voice narrates a plain-text handoff and waits for the next user message.`,promptSnippet:`Request explicit exit-or-continue approval after the saved plan has been presented`,promptGuidelines:[`After write_plan succeeds, call complete_plan without a decision so it can present the review question.`,`When autonomous voice returns an awaiting-response result, stop. Interpret the next ordinary user message, then call complete_plan again with decision "exit" or "continue".`,`Never pass decision "${xe}" without the user explicitly choosing "${ye}".`],parameters:{type:`object`,properties:{decision:{type:`string`,enum:[xe,Se]}},additionalProperties:!1},async execute(e,t,n,i,a){let o=S;if(X(o),!I)return{content:[{type:`text`,text:`Plan mode is already disabled.`}],details:{exited:!1}};if(!We)return{content:[{type:`text`,text:`Present the complete plan in chat and save it with write_plan before requesting approval.`}],details:{exited:!1}};if(!a.hasUI)return m.recordEvent(r.PLAN_EVENT.planReviewCompleted,{"plan.outcome":`no_ui`}),{content:[{type:`text`,text:`The plan is complete, but this run cannot ask the user interactively. Remain in plan mode until the user uses SPC p e.`}],details:{exited:!1}};let s=F(t)?t.decision:void 0,c=s===xe||s===Se;if(W){if(!c)throw Error(`The narrated plan review is waiting for an explicit exit or continue decision.`);return W=!1,Ct(s,a)}if(s!==void 0)throw Error(`Call complete_plan without a decision before submitting a narrated plan-review answer.`);if(await ut())return X(o),W=!0,dt();let l=await a.ui.select(`Plan complete. What would you like to do?`,[ye,be]);return X(o),Ct(l===ye?xe:Se,a)}}),at&&e.inject([g.DOOM_UI_HUB_SERVICE],e=>{let t=(0,g.requireDoomUiHub)(e).registerLeaderActions({source:ce,handlers:{"plan.normal":()=>{let e=x?J:void 0;return e?$(()=>xt(e,`normal`,M,!1)):void 0},"plan.exit":()=>{let e=x?J:void 0;return e?$(async()=>{await St(e,M)}):void 0},"plan.debug":()=>{let e=x?J:void 0;return e?$(()=>xt(e,`debug`,M,!1)):void 0},"plan.fable":()=>{let e=x?J:void 0;return e?$(()=>xt(e,`fable`,M,!1)):void 0}},onError:(e,t)=>{x&&(J?.hasUI&&J.ui.notify(`Leader action ${t} failed. Plan mode remains read-only.`,D),m.recordError(r.PLAN_EVENT.modeEnabled,e,{[O]:M,"plan.action":t}))}});return N=t,()=>{t(),N===t&&(N=void 0)}}),t.on(`tool_call`,async(e,t)=>{if(!x||!I)return;let n=L===`debug`&&(e.toolName===T||ot.has(e.toolName))||L===`fable`&&e.toolName===se;if(!Te.has(e.toolName)&&!n)return{block:!0,reason:`Plan mode blocks the ${e.toolName} tool. Use SPC p e to restore access.`};let r=Xe(z?.subagents,t.model);if(e.toolName===oe){r&&Qe(e.input,r);return}if(e.toolName!==w)return;let i=e.input;if($e(i.action))return{block:!0,reason:`Plan mode blocks subagent management action '${i.action}'. Use SPC p e first.`};Ye(i),r&&Ze(i,r)}),t.on(`before_agent_start`,async(e,t)=>{if(!x)return;let n=[];if(I){let e=(0,o.getHarnessState)().root??t.cwd,r=(0,o.resolvePlanningPlansDirectory)(z?.plansDirectory,e,ee.default.homedir());n.push(`[PLAN MODE ACTIVE]\nYou are in repository read-only plan mode. The dedicated write_plan tool may write one unique Markdown plan file under ${r}.\n\nExplore the codebase and produce a concrete implementation plan. For every plan, first call subagent with action "agents", cluster the exploration by independent domain, subsystem, or integration boundary, and create a provisional task graph with the task tool. Select specialized agents by matching their names and descriptions to each cluster. Assign every unblocked delegated task through the task tool, and use a one-shot inlineAgent with a focused systemPrompt when no discovered specialist fits. Treat the initial graph as provisional, not as a fixed contract. After findings arrive, review the entire graph at least once and perform one to three review passes in total. In each pass, use the evidence to add, rewrite, delete, cancel, reassign, or change blockedBy relationships for tasks when warranted. Do not keep following tasks that new information has made stale. Stop revising early when the graph is stable, or after the third pass.\n\nEvery plan must end with a delegated planning-draft stage blocked by all exploration and decision tasks. A single-boundary plan gets one planning draft. A complex plan spanning multiple subsystems, domains, packages, apps, or integration boundaries gets two planning drafts concurrently. Use three concurrent drafts instead when the work is cross-layer, migration-sensitive, security-sensitive, or similarly high risk. Assign each draft through the task tool to the discovered "planner" agent with context "fork" so it receives the conversation and gathered evidence. If the planner agent is unavailable, assign the same draft task through the task tool with a focused inlineAgent. All children receive read, Bash, grep, find, ls, and configured MCP tools with artifacts disabled. Bash and MCP tools are for read-only inspection and must not modify files, external systems, or repository state. Doom Team runs asynchronously, so do not poll. After launch, continue non-overlapping exploration or end your turn; completion notifications wake the parent session.\n\nAfter all planning drafts complete, the main agent must compare the candidates, pick the strongest draft, cross-check it against the gathered evidence and the other drafts, resolve conflicts and gaps, and ask the user only for product decisions. A child produces the draft, but the main agent owns and synthesizes the final plan. Do not modify files or repository state except through write_plan. Start the final plan with a meaningful Markdown H1 because write_plan derives the filename from it. Present the complete plan as visible Markdown in chat, then call write_plan with no arguments. After write_plan succeeds, clear the completed durable task graph and call complete_plan without a decision. In an interactive text session, complete_plan uses the normal exit-or-continue selector. Under autonomous voice, it displays and narrates the choices without opening a blocking dialog, then ends the turn. Interpret the user's next ordinary message and call complete_plan again with decision "exit" or "continue". Do not exit plan mode without explicit approval.`,a.buildFlavorPlanningPrompt(L,r,Ge,K))}if(H&&n.push(`[CURRENT PLAN]\nSource: ${H.path}\n\n${H.content}`),n.length!==0)return{systemPrompt:`${e.systemPrompt}\n\n${n.join(`
5
5
 
6
- `)}`}}),t.on(`context`,async e=>{if(x)return{messages:e.messages.filter(e=>e.customType!==`agent-harness-plan-mode-context`)}}),t.on(`session_start`,async(e,n)=>{if(!x)return;let r=++S,i=n.sessionManager.getEntries(),a=i.flatMap(e=>e.type===`custom`&&e.customType===_?[He(e.data)]:[]).filter(e=>e!==void 0).at(-1),o=Fe(n.sessionManager.getSessionId()),s=i.flatMap(e=>e.type===`custom`&&e.customType===ie?[Ue(e.data)]:[]).filter(e=>e!==void 0).at(-1),c=L;if(z?.dispose(),z=void 0,V=s?.sessionId===o?s:void 0,H=a?.planId??V?.planId,Ve=!1,U=!1,R=void 0,W=void 0,N=!1,P=void 0,ft(),L=a?.originalSnapshot,K=a?.interruptedFableStage,G=K?`interrupted`:`idle`,q=n,a?.activeFlavor)await $(n,a.activeFlavor,`session_restore`,!0),Y(r);else{let e=a?.originalSnapshot??c;if(e){let i=await _t(n,e);Y(r),i&&t.setActiveTools(qe(e.tools,t.getActiveTools(),t.getAllTools().map(e=>e.name)))}else t.setActiveTools(t.getActiveTools().filter(e=>e!==v&&e!==y&&e!==T&&e!==se));Y(r),L=void 0,X(n)}})}exports.WRITE_PLAN_TIMEOUT_MS=ye,exports.configurePlanningSubagentInput=Xe,exports.configurePlanningTaskInput=Ze,exports.constrainSubagentInput=Je,exports.createPlanIdentifier=Ie,exports.isBlockedSubagentManagementAction=Qe,exports.loadPlanningModeConfig=et,exports.parseDebugEvidencePacket=Ge,exports.parsePersistedPlanState=He,exports.planModeExtension=nt,exports.planModeTools=q,exports.planSessionIdentifier=Fe,exports.planTitleSlug=Pe,exports.planningSubagentModel=Ye,exports.visiblePlanForToolCall=Le;
6
+ `)}`}}),t.on(`context`,async e=>{if(x)return{messages:e.messages.filter(e=>e.customType!==`agent-harness-plan-mode-context`)}}),t.on(`session_start`,async(e,n)=>{if(!x)return;Ae=n;let r=++S,i=n.sessionManager.getEntries(),a=i.flatMap(e=>e.type===`custom`&&e.customType===_?[He(e.data)]:[]).filter(e=>e!==void 0).at(-1),o=Ie(n.sessionManager.getSessionId()),s=i.flatMap(e=>e.type===`custom`&&e.customType===ie?[Ue(e.data)]:[]).filter(e=>e!==void 0).at(-1),c=R;if(B?.dispose(),B=void 0,H=s?.sessionId===o?s:void 0,U=a?.planId??H?.planId,We=!1,W=!1,z=void 0,Ge=void 0,I=!1,L=void 0,pt(),R=a?.originalSnapshot,q=a?.interruptedFableStage,K=q?`interrupted`:`idle`,J=n,a?.activeFlavor)await xt(n,a.activeFlavor,`session_restore`,!0),X(r);else{let e=a?.originalSnapshot??c;if(e){let i=await vt(n,e);X(r),i&&t.setActiveTools(Je(e.tools,t.getActiveTools(),t.getAllTools().map(e=>e.name)))}else t.setActiveTools(t.getActiveTools().filter(e=>e!==v&&e!==y&&e!==T&&e!==se));X(r),R=void 0,Z(n)}})}exports.WRITE_PLAN_TIMEOUT_MS=ve,exports.configurePlanningSubagentInput=Ze,exports.configurePlanningTaskInput=Qe,exports.constrainSubagentInput=Ye,exports.createPlanIdentifier=Le,exports.isBlockedSubagentManagementAction=$e,exports.loadPlanningModeConfig=tt,exports.parseDebugEvidencePacket=Ke,exports.parsePersistedPlanState=He,exports.planModeExtension=J,exports.planModeTools=K,exports.planSessionIdentifier=Ie,exports.planTitleSlug=Fe,exports.planningSubagentModel=Xe,exports.visiblePlanForToolCall=Re;
7
7
  //# sourceMappingURL=planMode.cjs.map