@agimon-ai/doompi-plan 0.0.1-alpha.13 → 0.0.1-alpha.14

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,6 +1,6 @@
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 u=require(`node:path`);u=e.__toESM(u,1);let d=require(`@agimon-ai/doompi-config/configWriter`),f=require(`@agimon-ai/doompi-extension-contracts/fable-plan`),p=require(`@agimon-ai/doompi-extension-contracts/protocol`),te=require(`@agimon-ai/doompi-extension-contracts/subagent-policy`),m=require(`@agimon-ai/doompi-extension-contracts/subagent-tool`),h=require(`@agimon-ai/doompi-ui/config`),g=require(`@agimon-ai/doompi-ui/leader`),_=require(`@agimon-ai/doompi-ui/mode`);const v=`agent-harness-plan-mode`,ne=`agent-harness-plan-document`,y=`complete_plan`,b=`write_plan`,x=`ask_user_question`,S=`bash`,C=`find`,w=`grep`,T=`read`,E=`subagent`,D=`task`,O=`record_debug_evidence`,k=`run_fable_plan`,A=`implementation-plan`,j=`@agimon-ai/doompi-plan`,M=`warning`,re=`info`,N=`Refused to write the plan because its destination is not a regular file.`,P=`plan.trigger`,ie=`plan.model`,F=`plan.flavor`,ae=`not_found`,oe=`unauthenticated`,se=`restore_failed`,ce=new Set,le=5e3,ue=`Exit plan mode and start implementation`,de=new Set([`add_directory`,x,S,y,C,w,`intercom`,`ls`,T,`search_external_files`,E,`subagent_supervisor`,`subagent_wait`,D,b]),I=[T,S,w,C,`ls`],fe=[...I,`mcp`],L=`leader`,R=[`off`,`minimal`,`low`,`medium`,`high`,`xhigh`,`max`],z=[`idle`,`draft`,`review`,`completed`,`failed`,`cancelled`,`interrupted`];function pe(e){return(e.match(/^#\s+(.+?)\s*$/m)?.[1]??A).normalize(`NFKD`).replace(/[\u0300-\u036f]/g,``).toLowerCase().replace(/[^a-z0-9]+/g,`-`).replace(/^-+|-+$/g,``).slice(0,64)||A}function me(e){return`${encodeURIComponent(e).slice(0,48)||`session`}--${(0,c.createHash)(`sha256`).update(e).digest(`hex`)}`}function B(e=new Date,t=(0,c.randomUUID)()){return`${e.toISOString().replace(/[-:]/g,``).replace(/\.\d{3}Z$/,`Z`)}--${t}`}function he(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===b});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 u=require(`node:path`);u=e.__toESM(u,1);let d=require(`@agimon-ai/doompi-config/configWriter`),f=require(`@agimon-ai/doompi-extension-contracts/fable-plan`),p=require(`@agimon-ai/doompi-extension-contracts/mode`),te=require(`@agimon-ai/doompi-extension-contracts/protocol`),m=require(`@agimon-ai/doompi-extension-contracts/subagent-policy`),h=require(`@agimon-ai/doompi-extension-contracts/subagent-tool`),g=require(`@agimon-ai/doompi-ui/config`),_=require(`@agimon-ai/doompi-ui/leader`);const v=`agent-harness-plan-mode`,ne=`agent-harness-plan-document`,y=`complete_plan`,b=`write_plan`,x=`ask_user_question`,re=`bash`,S=`find`,C=`grep`,w=`read`,T=`subagent`,E=`task`,D=`record_debug_evidence`,O=`run_fable_plan`,k=`implementation-plan`,A=`@agimon-ai/doompi-plan`,ie=`activate`,j=`deactivate`,M=`warning`,ae=`info`,oe=`Refused to write the plan because its destination is not a regular file.`,N=`plan.trigger`,se=`plan.model`,P=`plan.flavor`,ce=`not_found`,le=`unauthenticated`,ue=`restore_failed`,de=new Set,fe=5e3,pe=`Exit plan mode and start implementation`,me=new Set([`add_directory`,x,re,y,S,C,`intercom`,`ls`,p.MINOR_MODE_TOOL_NAME,w,`search_external_files`,T,`subagent_supervisor`,`subagent_wait`,E,b]),F=[w,re,C,S,`ls`],he=[...F,`mcp`],I=`leader`,L=[`off`,`minimal`,`low`,`medium`,`high`,`xhigh`,`max`],R=[`idle`,`draft`,`review`,`completed`,`failed`,`cancelled`,`interrupted`];function ge(e){return(e.match(/^#\s+(.+?)\s*$/m)?.[1]??k).normalize(`NFKD`).replace(/[\u0300-\u036f]/g,``).toLowerCase().replace(/[^a-z0-9]+/g,`-`).replace(/^-+|-+$/g,``).slice(0,64)||k}function _e(e){return`${encodeURIComponent(e).slice(0,48)||`session`}--${(0,c.createHash)(`sha256`).update(e).digest(`hex`)}`}function z(e=new Date,t=(0,c.randomUUID)()){return`${e.toISOString().replace(/[-:]/g,``).replace(/\.\d{3}Z$/,`Z`)}--${t}`}function ve(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===b});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(`
2
2
 
3
- `).trim()||void 0}}function ge(e){return e.reason instanceof Error?e.reason:Error(`Plan write cancelled.`)}async function V(e,t){if(t.aborted)throw ge(t);let n,r=new Promise((e,r)=>{n=()=>r(ge(t)),t.addEventListener(`abort`,n,{once:!0})});try{return await Promise.race([e(),r])}finally{n&&t.removeEventListener(`abort`,n)}}function H(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function U(e){return typeof e==`string`}function W(e){if(!(!H(e)||!U(e.provider)||!U(e.id))&&!(!e.provider||!e.id||e.provider.length>128||e.id.length>128))return{provider:e.provider,id:e.id}}function G(e){return U(e)&&R.includes(e)?e:void 0}function K(e){return U(e)&&z.includes(e)?e:void 0}function _e(e){if(!(!Array.isArray(e)||e.some(e=>!U(e))))return e.map(e=>e)}function q(e){if(!H(e))return;let t=_e(e.tools),n=G(e.thinking);if(!t||n===void 0)return;let r=e.model===void 0?void 0:W(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 ye(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:q(e.originalSnapshot);if(e.originalSnapshot!==void 0&&!n)return;let r=e.planId===void 0?void 0:U(e.planId)?e.planId:void 0;if(e.planId!==void 0&&r===void 0)return;let i=e.interruptedFableStage===void 0?void 0:K(e.interruptedFableStage);if(!(e.interruptedFableStage!==void 0&&!i))return{version:2,...t?{activeFlavor:t}:{},...n?{originalSnapshot:n}:{},...r?{planId:r}:{},...i?{interruptedFableStage:i}:{}}}function be(e){if(e.enabled!==!0&&e.enabled!==!1)return;let t=e.toolsBeforePlanMode===void 0?void 0:_e(e.toolsBeforePlanMode);if(e.toolsBeforePlanMode!==void 0&&!t)return;let n=e.modelBeforePlanMode===void 0?void 0:W(e.modelBeforePlanMode);if(e.modelBeforePlanMode!==void 0&&!n)return;let r=e.thinkingBeforePlanMode===void 0?void 0:G(e.thinkingBeforePlanMode);if(e.thinkingBeforePlanMode!==void 0&&r===void 0)return;let i=e.planId===void 0?void 0:U(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 xe(e){if(H(e))return ye(e)??be(e)}function Se(e){if(!H(e)||!U(e.content)||!U(e.path)||!U(e.sessionId)||!U(e.title)||!U(e.writtenAt))return;let t=e.planId===void 0?void 0:U(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 J(e,t,n){if(!U(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 Ce(e,t){if(!Array.isArray(e)||e.length>32)throw Error(`${t} must be a bounded string array.`);return e.map((e,n)=>J(e,`${t}[${n}]`,!1))}function we(e,t){return e===void 0?``:J(e,t,!1)}function Y(e,t){return e===void 0?[]:Ce(e,t)}function Te(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 Ee(e){if(!H(e))throw Error(`Debug evidence must be an object.`);Te(e,[`issue`,`expectedBehavior`,`reproductionAttempt`,`actualBehavior`,`logs`,`correlatedTraceEvidence`,`processOutput`,`browserConsoleEvidence`,`correlationIds`,`timestamps`,`verifiedFacts`,`hypotheses`,`unavailableEvidence`],`Debug evidence`);let t={issue:J(e.issue,`issue`,!0),expectedBehavior:we(e.expectedBehavior,`expectedBehavior`),reproductionAttempt:we(e.reproductionAttempt,`reproductionAttempt`),actualBehavior:we(e.actualBehavior,`actualBehavior`),logs:Y(e.logs,`logs`),correlatedTraceEvidence:Y(e.correlatedTraceEvidence,`correlatedTraceEvidence`),processOutput:Y(e.processOutput,`processOutput`),browserConsoleEvidence:Y(e.browserConsoleEvidence,`browserConsoleEvidence`),correlationIds:Y(e.correlationIds,`correlationIds`),timestamps:Y(e.timestamps,`timestamps`),verifiedFacts:Y(e.verifiedFacts,`verifiedFacts`),hypotheses:Y(e.hypotheses,`hypotheses`),unavailableEvidence:Y(e.unavailableEvidence,`unavailableEvidence`)};if(s.Buffer.byteLength(JSON.stringify(t),`utf8`)>32768)throw Error(`Debug evidence exceeds the bounded packet size.`);return t}function De(e,t){let n=new Set(t),r=e.filter(e=>de.has(e)),i=I.filter(e=>n.has(e)),a=[x,y,D,b].filter(e=>n.has(e));return[...new Set([...r,...i,...a])]}function Oe(e,t,n,r){let i=new Set(t),a=De(e,t),o=n===`debug`?[O,...r].filter(e=>i.has(e)):[];return n===`fable`&&i.has(k)&&o.push(k),[...new Set([...a,...o])]}function X(e){if(e.output=!1,e.progress=!1,e.worktree=!1,Array.isArray(e.parallel))for(let t of e.parallel)X(t);else e.parallel&&X(e.parallel)}function ke(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??[])X(t);for(let t of e.chain??[])X(t)}function Ae(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 je(e,t){if(e.action===`run`)for(let n of e.requests??[])n.model=t}function Me(e,t){e.action===`assign`&&(e.model=t)}function Ne(e){return new Set([`append-step`,`create`,`delete`,`disable`,`eject`,`enable`,`grant-spawn-budget`,`reset`,`schedule`,`schedule-cancel`,`update`,`watchdog.configure`]).has(e??``)}function Pe(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 Fe(e=process.env,n=process.cwd()){return t.loadDoomConfig((0,o.loadHarnessState)(e).state.root??n).modes?.planning}function Ie(e){let t={key:`p`,label:`plan`,order:60};return[{id:`plan.normal`,path:[t,{key:`p`,label:`normal`,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`}},...e?[{id:`plan.exit`,path:[t,{key:`e`,label:`exit`,detail:`restore and exit`}],action:{name:`plan.exit`}}]:[]]}function Le(e,t=Fe,c=r.createPlanTelemetry(),m={}){let x=!1,C,w,T,A,I,R,z=!1,H,U=`idle`,W,G,K,_e=Promise.resolve(),q=m.doomIntegrations??!0,ve=new Set(m.debugDiagnosticTools??ce),ye=(0,f.createFablePlanBrokerClient)(()=>G?.sessionManager.getSessionId()),be=m.fableBroker??{start:(e,t)=>ye.start(e,t),cancel:(e,t)=>ye.cancel({requester:f.FABLE_PLAN_REQUESTER,operationId:e,reason:t})},J=()=>m.fableBroker!==void 0||ye.isAvailable(),Ce=q?(0,_.registerDoomModeContribution)(e,{source:j,id:`plan`,order:40}):{update:()=>void 0},we=q?(0,g.createDoomLeaderContribution)(e,{source:j,bindings:Ie(!1)}):{update:()=>void 0},Y,Te=q?(0,h.registerDoomConfigContribution)(e,{source:j,listSections:()=>{try{return i.planConfigSections(t(),Y)}catch(e){return i.planConfigSections(void 0,e instanceof Error?e.message:String(e))}},handlers:{[h.CONFIG_ACTION.set]:async({fieldId:e,value:t})=>{let n=i.planSettingByFieldId(e);!n||!t||(Y=void 0,await(0,d.setDoomConfigValue)((0,o.globalDoomConfigPath)(),[...n.keyPath],t))},[h.CONFIG_ACTION.clear]:async({fieldId:e})=>{let t=i.planSettingByFieldId(e);t&&(Y=void 0,await(0,d.unsetDoomConfigValue)((0,o.globalDoomConfigPath)(),[...t.keyPath]))}},onError:e=>{Y=e instanceof Error?e.message:String(e)}}):{dispose:()=>void 0,update:()=>void 0};function De(){we.update(Ie(x))}function X(e){De();let t=C,n=x&&t?`plan:${t}`:void 0,r=t===`fable`&&U!==`idle`?` · ${U}`:``;e.ui.setStatus(`plan-mode`,n?e.ui.theme.fg(M,n):void 0),Ce.update(x&&t?{label:`PLAN`,detail:`${t}${r} - read only`,color:M}:void 0)}function Le(){if(!q||!x||!C||!G)return;let t={owner:j,allowedTools:[...fe],requiredTools:[S,`mcp`],allowMcpTools:!0,allowedExternalProfiles:C===`fable`?[n.FABLE_PLAN_PROFILE]:[],denyExtensions:!1};A?A.update(t):A=(0,te.registerSubagentPolicy)((0,p.createProtocolRuntime)({bus:e.events,source:j,sessionId:G.sessionManager.getSessionId()}),t)}function Z(){e.appendEntry(v,{version:2,...x&&C?{activeFlavor:C}:{},...w?{originalSnapshot:{tools:[...w.tools],...w.model?{model:{...w.model}}:{},thinking:w.thinking}}:{},...R?{planId:R}:{},...W?{interruptedFableStage:W}:{}})}function Q(e,t,n){c.recordWarning(r.PLAN_EVENT.modelResolveFailed,`Planning model ${n}: ${t}`,{[ie]:t,"plan.model.phase":e,"plan.model.reason":n})}async function Re(t){let n=T?.main;if(n){if(n.model){let r=Pe(n.model,t);r?await e.setModel(r)||(t.ui.notify(`Planning model is unavailable without authentication: ${n.model}`,M),Q(`apply`,n.model,oe)):(t.ui.notify(`Planning model not found: ${n.model}`,M),Q(`apply`,n.model,ae))}n.thinking&&e.setThinkingLevel(n.thinking)}}async function ze(t,n){if(!n)return!1;if(n.model){let i=`${n.model.provider}/${n.model.id}`,a=t.modelRegistry.find(n.model.provider,n.model.id);if(!a)return t.ui.notify(`Previous model not found: ${i}`,M),Q(`restore`,i,ae),!1;try{if(!await e.setModel(a))return t.ui.notify(`Previous model is unavailable without authentication: ${i}`,M),Q(`restore`,i,oe),!1}catch(e){return t.ui.notify(`Previous model could not be restored: ${i}`,M),Q(`restore`,i,se),c.recordError(r.PLAN_EVENT.modelResolveFailed,e,{[ie]:i,"plan.model.phase":`restore`,"plan.model.reason":se}),!1}}return e.setThinkingLevel(n.thinking),!0}function Be(e){x&&C?e.ui.notify(`Already in plan:${C}.`,re):e.ui.notify(`Plan mode is inactive.`,re)}function $(e){let t=_e.then(e,e);return _e=t.then(()=>void 0,()=>void 0),t}async function Ve(e){if(!K&&!U.match(/^(draft|review)$/u))return;(U===`draft`||U===`review`)&&(W=U),We.cancel(e);let t=K;if(t)try{await t}catch(e){c.recordError(r.PLAN_EVENT.modeDisabled,e,{[F]:`fable`,"plan.fable.reason":`cancel_failed`})}K=void 0,W&&(U=`interrupted`),G&&X(G),Z()}async function He(n,i,a,o){if(G=n,x){if(C===i){Be(n);return}C===`fable`&&await Ve(`Leaving Fable for plan:${i}.`),C=i,Le(),e.setActiveTools(Oe(w?.tools??[],e.getAllTools().map(e=>e.name),i,ve)),X(n),Z(),c.recordEvent(r.PLAN_EVENT.modeEnabled,{[P]:a,[F]:i,"plan.tool.count":e.getActiveTools().length});return}let s;try{s=t()}catch(e){throw c.recordError(r.PLAN_EVENT.configLoadFailed,e,{[P]:a}),e}o?R??=B():(I=void 0,z=!1,R=B(),W=void 0,U=`idle`),T=s,w??={tools:[...e.getActiveTools()],...n.model?{model:{provider:n.model.provider,id:n.model.id}}:{},thinking:e.getThinkingLevel()},x=!0,C=i,e.setActiveTools(Oe(w.tools,e.getAllTools().map(e=>e.name),i,ve)),Le(),await Re(n),X(n),Z(),c.recordEvent(r.PLAN_EVENT.modeEnabled,{[P]:a,[F]:i,"plan.tool.count":e.getActiveTools().length,...T?.main?.model?{[ie]:T.main.model}:{},...T?.main?.thinking?{"plan.thinking":T.main.thinking}:{}})}async function Ue(t,n){if(!x)return Be(t),!1;await Ve(`Plan mode is exiting.`);let i=w;if(!await ze(t,i))return t.ui.notify(`Previous model was not restored. Plan mode remains read-only.`,M),X(t),Z(),c.recordWarning(r.PLAN_EVENT.modeDisabled,`Plan mode restoration failed.`,{[P]:n,[F]:C??`unknown`,"plan.restored":!1}),!1;let a=!!I;return e.setActiveTools([...i?.tools??[]]),A?.dispose(),A=void 0,x=!1,C=void 0,T=void 0,w=void 0,H=void 0,U=`idle`,W=void 0,X(t),Z(),c.recordEvent(r.PLAN_EVENT.modeDisabled,{[P]:n,[F]:`normal`,"plan.written":a,"plan.restored":!0}),!0}let We=n.createFablePlanFlow({broker:be,isAuthorized:()=>x&&C===`fable`&&J(),onStage:e=>{U=e,(e===`completed`||e===`failed`||e===`cancelled`)&&(W=void 0),G&&X(G),x&&Z()},onError:e=>{c.recordError(r.PLAN_EVENT.modeEnabled,e,{[F]:`fable`,"plan.fable.reason":`flow_error`})}});e.registerTool({name:O,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||C!==`debug`)return{content:[{type:`text`,text:`Debug planning is not active.`}],details:{recorded:!1}};try{H=Ee(t)}catch(e){throw c.recordWarning(r.PLAN_EVENT.modeEnabled,e,{[F]:`debug`,"plan.debug.reason":`invalid_evidence`}),e}return G&&X(G),{content:[{type:`text`,text:`Debug evidence recorded as optional planning context.`}],details:{recorded:!0}}}}),e.registerTool({name:k,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){if(!x||C!==`fable`)return{content:[{type:`text`,text:`Fable planning is not active.`}],details:{started:!1}};if(!J())return{content:[{type:`text`,text:`The local Fable broker is unavailable. Fable planning remains disabled.`}],details:{started:!1,errorCode:`broker_unavailable`}};let r=We.run(t,n);K=r;try{let e=await r;return{content:[{type:`text`,text:e.draft?`Fable draft:\n${e.draft}`:`Fable planning ${e.status}: ${e.errorCode??`no output`}.`}],details:{...e,started:!0}}}finally{K===r&&(K=void 0)}}}),e.registerTool({name:b,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(t,n,i,a,d){if(!x)return{content:[{type:`text`,text:`Plan mode is disabled. Use normal file tools instead.`}],details:{written:!1}};let f=he(d.sessionManager.getBranch(),t);if(!f){let e=Error(`Present the complete implementation plan as visible Markdown in chat before calling write_plan.`);throw c.recordError(r.PLAN_EVENT.writePlanFailed,e,{"plan.reason":`missing_visible_plan`}),e}let p=Date.now(),te=(0,o.getHarnessState)().root??d.cwd,m=(0,o.resolvePlanningPlansDirectory)(T?.plansDirectory,te,ee.default.homedir()),h=me(d.sessionManager.getSessionId());R??=B();let g=I?.planId===R&&u.default.dirname(I.path)===m?I.path:void 0,_=pe(f),v=R,y=g??u.default.join(m,`${_}--${v}.md`),b=new AbortController,S=Error(`Writing ${y} timed out after ${le}ms.`),C=setTimeout(()=>b.abort(S),le),w=i?AbortSignal.any([i,b.signal]):b.signal,E=`checking`,D=()=>{a?.({content:[{type:`text`,text:`${E===`checking`?`Checking`:`Writing`} ${y}...`}],details:{phase:E,path:y,startedAt:p}})},O=(e,t)=>{c.recordWarning(r.PLAN_EVENT.writePlanUnsafePath,t,{"plan.phase":E,"plan.refusal":e})};try{D();try{await V(()=>l.default.promises.mkdir(m,{recursive:!0,mode:448}),w)}catch(e){throw w.aborted?ge(w):Error(`Could not create the configured plans directory ${m}.`,{cause:e})}let t=await V(()=>l.default.promises.lstat(m),w),n=await V(()=>l.default.promises.realpath(m),w);if(t.isSymbolicLink()||!t.isDirectory())return O(`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:y,phase:E,durationMs:Date.now()-p}};E=`writing`,D();let i;try{for(let e=0;e<3;e+=1)try{let e=g?0:l.default.constants.O_EXCL;i=await V(()=>l.default.promises.open(y,l.default.constants.O_CREAT|l.default.constants.O_WRONLY|l.default.constants.O_NONBLOCK|l.default.constants.O_NOFOLLOW|e,384),w);break}catch(t){if(!(!g&&t instanceof Error&&`code`in t&&String(t.code)===`EEXIST`)||e===2)throw t;v=B(),R=v,y=u.default.join(m,`${_}--${v}.md`),Z()}if(!i)throw Error(`Could not allocate a unique plan filename in ${m}.`);let e=await V(()=>l.default.promises.realpath(m),w),t=await V(()=>i.stat(),w);if(e!==n||!t.isFile()||t.nlink!==1)return O(`invalid_destination`,N),{content:[{type:`text`,text:N}],details:{written:!1,path:y,phase:E,durationMs:Date.now()-p}};await V(()=>i.truncate(0),w),await V(()=>i.writeFile(f,{encoding:`utf8`,signal:w}),w)}catch(e){if(e instanceof Error&&`code`in e&&[`EISDIR`,`ELOOP`,`ENXIO`].includes(String(e.code)))return O(`invalid_destination`,N),{content:[{type:`text`,text:N}],details:{written:!1,path:y,phase:E,durationMs:Date.now()-p}};throw e}finally{await i?.close()}return I={content:f,path:y,sessionId:h,planId:v,title:_,writtenAt:new Date().toISOString()},e.appendEntry(ne,I),z=!0,c.recordEvent(r.PLAN_EVENT.planWritten,{"plan.written":!0,"plan.bytes":s.Buffer.byteLength(f,`utf8`),"plan.duration_ms":Date.now()-p,"plan.revised":!!g}),{content:[{type:`text`,text:`Wrote implementation plan to ${y}.`}],details:{written:!0,path:y,phase:E,durationMs:Date.now()-p}}}catch(e){let t={"plan.phase":E,"plan.duration_ms":Date.now()-p};if(b.signal.aborted&&!i?.aborted){let n=Error(`write_plan timed out during ${E} for ${y}.`,{cause:e});throw c.recordError(r.PLAN_EVENT.writePlanTimedOut,n,{...t,"plan.timeout_ms":le}),n}throw c.recordError(r.PLAN_EVENT.writePlanFailed,e,{...t,"plan.cancelled":i?.aborted??!1}),e}finally{clearTimeout(C)}}}),e.registerTool({name:y,label:`Complete Plan`,description:`Use only after presenting a concrete implementation plan. Ask the user whether to exit plan mode and begin implementation or continue planning.`,promptSnippet:`Ask the user whether to exit plan mode after the implementation plan is complete`,parameters:{type:`object`,properties:{},additionalProperties:!1},async execute(e,t,n,i,a){if(!x)return{content:[{type:`text`,text:`Plan mode is already disabled.`}],details:{exited:!1}};if(!z)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 c.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 o=await a.ui.select(`Plan complete. What would you like to do?`,[ue,`Continue planning`]);if(c.recordEvent(r.PLAN_EVENT.planReviewCompleted,{"plan.outcome":o===ue?`exited`:`continued`}),o===ue){let e=await $(()=>Ue(a,`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 z=!1,{content:[{type:`text`,text:`The user chose to continue planning. Remain read-only and refine the plan.`}],details:{exited:!1}}}}),q&&(0,g.registerDoomLeaderActionHandlers)(e,{source:j,handlers:{"plan.normal":()=>{let e=G;return e?$(()=>He(e,`normal`,L,!1)):void 0},"plan.exit":()=>{let e=G;return e?$(async()=>{await Ue(e,L)}):void 0},"plan.debug":()=>{let e=G;return e?$(()=>He(e,`debug`,L,!1)):void 0},"plan.fable":()=>{let e=G;return e?$(()=>He(e,`fable`,L,!1)):void 0}},onError:(e,t)=>{G?.hasUI&&G.ui.notify(`Leader action ${t} failed. Plan mode remains read-only.`,M),c.recordError(r.PLAN_EVENT.modeEnabled,e,{[P]:L,"plan.action":t})}}),e.on(`tool_call`,async(e,t)=>{if(!x)return;let n=C===`debug`&&(e.toolName===O||ve.has(e.toolName))||C===`fable`&&e.toolName===k;if(!de.has(e.toolName)&&!n)return{block:!0,reason:`Plan mode blocks the ${e.toolName} tool. Use SPC p e to restore access.`};let r=Ae(T?.subagents,t.model);if(e.toolName===D){r&&Me(e.input,r);return}if(e.toolName!==E)return;let i=e.input;if(Ne(i.action))return{block:!0,reason:`Plan mode blocks subagent management action '${i.action}'. Use SPC p e first.`};ke(i),r&&je(i,r)}),e.on(`before_agent_start`,async(e,t)=>{let n=[];if(x){let e=(0,o.getHarnessState)().root??t.cwd,r=(0,o.resolvePlanningPlansDirectory)(T?.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 so the user can review the plan and choose whether to begin implementation or continue planning. Do not exit plan mode without that approval.`,a.buildFlavorPlanningPrompt(C,r,H,U))}if(I&&n.push(`[CURRENT PLAN]\nSource: ${I.path}\n\n${I.content}`),n.length!==0)return{systemPrompt:`${e.systemPrompt}\n\n${n.join(`
3
+ `).trim()||void 0}}function ye(e){return e.reason instanceof Error?e.reason:Error(`Plan write cancelled.`)}async function B(e,t){if(t.aborted)throw ye(t);let n,r=new Promise((e,r)=>{n=()=>r(ye(t)),t.addEventListener(`abort`,n,{once:!0})});try{return await Promise.race([e(),r])}finally{n&&t.removeEventListener(`abort`,n)}}function V(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function H(e){return typeof e==`string`}function U(e){if(!(!V(e)||!H(e.provider)||!H(e.id))&&!(!e.provider||!e.id||e.provider.length>128||e.id.length>128))return{provider:e.provider,id:e.id}}function W(e){return H(e)&&L.includes(e)?e:void 0}function G(e){return H(e)&&R.includes(e)?e:void 0}function be(e){if(!(!Array.isArray(e)||e.some(e=>!H(e))))return e.map(e=>e)}function K(e){if(!V(e))return;let t=be(e.tools),n=W(e.thinking);if(!t||n===void 0)return;let r=e.model===void 0?void 0:U(e.model);if(!(e.model!==void 0&&!r))return{tools:t,...r?{model:r}:{},thinking:n}}function xe(e){return e===`normal`||e===`debug`||e===`fable`?e:void 0}function Se(e){if(e.version!==2)return;let t=e.activeFlavor===void 0?void 0:xe(e.activeFlavor);if(e.activeFlavor!==void 0&&!t)return;let n=e.originalSnapshot===void 0?void 0:K(e.originalSnapshot);if(e.originalSnapshot!==void 0&&!n)return;let r=e.planId===void 0?void 0:H(e.planId)?e.planId:void 0;if(e.planId!==void 0&&r===void 0)return;let i=e.interruptedFableStage===void 0?void 0:G(e.interruptedFableStage);if(!(e.interruptedFableStage!==void 0&&!i))return{version:2,...t?{activeFlavor:t}:{},...n?{originalSnapshot:n}:{},...r?{planId:r}:{},...i?{interruptedFableStage:i}:{}}}function Ce(e){if(e.enabled!==!0&&e.enabled!==!1)return;let t=e.toolsBeforePlanMode===void 0?void 0:be(e.toolsBeforePlanMode);if(e.toolsBeforePlanMode!==void 0&&!t)return;let n=e.modelBeforePlanMode===void 0?void 0:U(e.modelBeforePlanMode);if(e.modelBeforePlanMode!==void 0&&!n)return;let r=e.thinkingBeforePlanMode===void 0?void 0:W(e.thinkingBeforePlanMode);if(e.thinkingBeforePlanMode!==void 0&&r===void 0)return;let i=e.planId===void 0?void 0:H(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 we(e){if(V(e))return Se(e)??Ce(e)}function Te(e){if(!V(e)||!H(e.content)||!H(e.path)||!H(e.sessionId)||!H(e.title)||!H(e.writtenAt))return;let t=e.planId===void 0?void 0:H(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 q(e,t,n){if(!H(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 Ee(e,t){if(!Array.isArray(e)||e.length>32)throw Error(`${t} must be a bounded string array.`);return e.map((e,n)=>q(e,`${t}[${n}]`,!1))}function De(e,t){return e===void 0?``:q(e,t,!1)}function J(e,t){return e===void 0?[]:Ee(e,t)}function Oe(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(!V(e))throw Error(`Debug evidence must be an object.`);Oe(e,[`issue`,`expectedBehavior`,`reproductionAttempt`,`actualBehavior`,`logs`,`correlatedTraceEvidence`,`processOutput`,`browserConsoleEvidence`,`correlationIds`,`timestamps`,`verifiedFacts`,`hypotheses`,`unavailableEvidence`],`Debug evidence`);let t={issue:q(e.issue,`issue`,!0),expectedBehavior:De(e.expectedBehavior,`expectedBehavior`),reproductionAttempt:De(e.reproductionAttempt,`reproductionAttempt`),actualBehavior:De(e.actualBehavior,`actualBehavior`),logs:J(e.logs,`logs`),correlatedTraceEvidence:J(e.correlatedTraceEvidence,`correlatedTraceEvidence`),processOutput:J(e.processOutput,`processOutput`),browserConsoleEvidence:J(e.browserConsoleEvidence,`browserConsoleEvidence`),correlationIds:J(e.correlationIds,`correlationIds`),timestamps:J(e.timestamps,`timestamps`),verifiedFacts:J(e.verifiedFacts,`verifiedFacts`),hypotheses:J(e.hypotheses,`hypotheses`),unavailableEvidence:J(e.unavailableEvidence,`unavailableEvidence`)};if(s.Buffer.byteLength(JSON.stringify(t),`utf8`)>32768)throw Error(`Debug evidence exceeds the bounded packet size.`);return t}function Ae(e,t){let n=new Set(t),r=e.filter(e=>me.has(e)),i=F.filter(e=>n.has(e)),a=[x,y,E,b].filter(e=>n.has(e));return[...new Set([...r,...i,...a])]}function je(e,t,n,r,i){let a=new Set(n),o=Ae(e.filter(e=>e!==p.MINOR_MODE_TOOL_NAME),n),s=r===`debug`?[D,...i].filter(e=>a.has(e)):[];return r===`fable`&&a.has(O)&&s.push(O),t.includes(p.MINOR_MODE_TOOL_NAME)&&a.has(p.MINOR_MODE_TOOL_NAME)&&s.push(p.MINOR_MODE_TOOL_NAME),[...new Set([...o,...s])]}function Me(e,t){let n=e.filter(e=>e!==p.MINOR_MODE_TOOL_NAME);return t.includes(p.MINOR_MODE_TOOL_NAME)?[...n,p.MINOR_MODE_TOOL_NAME]:n}function Y(e){if(e.output=!1,e.progress=!1,e.worktree=!1,Array.isArray(e.parallel))for(let t of e.parallel)Y(t);else e.parallel&&Y(e.parallel)}function Ne(e){if(e.action!==void 0){(0,h.isSubagentAction)(e.action)&&(0,h.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??[])Y(t);for(let t of e.chain??[])Y(t)}function Pe(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 Fe(e,t){if(e.action===`run`)for(let n of e.requests??[])n.model=t}function Ie(e,t){if(e.action===`assign`)for(let n of e.assignments??[])n.model=t}function Le(e){return new Set([`append-step`,`create`,`delete`,`disable`,`eject`,`enable`,`grant-spawn-budget`,`reset`,`schedule`,`schedule-cancel`,`update`,`watchdog.configure`]).has(e??``)}function Re(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 ze(e=process.env,n=process.cwd()){return t.loadDoomConfig((0,o.loadHarnessState)(e).state.root??n).modes?.planning}function Be(e){let t={key:`p`,label:`plan`,order:60};return[{id:`plan.normal`,path:[t,{key:`p`,label:`normal`,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`}},...e?[{id:`plan.exit`,path:[t,{key:`e`,label:`exit`,detail:`restore and exit`}],action:{name:`plan.exit`}}]:[]]}function Ve(e,t=ze,c=r.createPlanTelemetry(),h={}){let x=!1,S,C,w,k,F,L,R=!1,V,H=`idle`,U,W,G,be=Promise.resolve(),K=h.doomIntegrations??!0,xe=new Set(h.debugDiagnosticTools??de),Se=(0,f.createFablePlanBrokerClient)(()=>W?.sessionManager.getSessionId()),Ce=h.fableBroker??{start:(e,t)=>Se.start(e,t),cancel:(e,t)=>Se.cancel({requester:f.FABLE_PLAN_REQUESTER,operationId:e,reason:t})},q=()=>h.fableBroker!==void 0||Se.isAvailable(),Ee=K?(0,p.registerMinorModeOwner)(e,{descriptor:{source:A,id:`plan`,label:`PLAN`,description:`Read-only planning with normal, debug, and Fable flavors.`,order:40,actions:[{id:ie,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:j,label:`Deactivate`,description:`Exit plan mode and restore the previous agent configuration.`,contexts:[`tui`,`headless`],parameters:[]}]},initialState:{activation:`inactive`,condition:`ready`,actions:[{id:ie,enabled:!0},{id:j,enabled:!1,disabledReason:`Plan mode is inactive.`}]},async handleAction(e,t,n){if(e===ie){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,I,!1)),{message:`Plan mode is active with the ${e} flavor.`}}if(e===j)return{message:await Q(()=>Ke(n.context,I))?`Plan mode deactivated.`:`Plan mode remains active.`};throw Error(`Unknown plan mode action: ${e}`)},onError:e=>void c.recordError(r.PLAN_EVENT.configLoadFailed,e)}):{publish:()=>void 0},De=K?(0,_.createDoomLeaderContribution)(e,{source:A,bindings:Be(!1)}):{update:()=>void 0},J,Oe=K?(0,g.registerDoomConfigContribution)(e,{source:A,listSections:()=>{try{return i.planConfigSections(t(),J)}catch(e){return i.planConfigSections(void 0,e instanceof Error?e.message:String(e))}},handlers:{[g.CONFIG_ACTION.set]:async({fieldId:e,value:t})=>{let n=i.planSettingByFieldId(e);!n||!t||(J=void 0,await(0,d.setDoomConfigValue)((0,o.globalDoomConfigPath)(),[...n.keyPath],t))},[g.CONFIG_ACTION.clear]:async({fieldId:e})=>{let t=i.planSettingByFieldId(e);t&&(J=void 0,await(0,d.unsetDoomConfigValue)((0,o.globalDoomConfigPath)(),[...t.keyPath]))}},onError:e=>{J=e instanceof Error?e.message:String(e)}}):{dispose:()=>void 0,update:()=>void 0};function Ae(){De.update(Be(x))}function Y(e){Ae();let t=S,n=x&&t?`plan:${t}`:void 0,r=t===`fable`&&H!==`idle`?` · ${H}`:``;e.ui.setStatus(`plan-mode`,n?e.ui.theme.fg(M,n):void 0);let i={activation:x?`active`:`inactive`,condition:H===`failed`?`failed`:H===`interrupted`?`degraded`:`ready`,...x&&t?{detail:`${t}${r} - read only`,color:M}:{},actions:[{id:ie,enabled:!0},...x?[{id:j,enabled:!0}]:[{id:j,enabled:!1,disabledReason:`Plan mode is inactive.`}]]};Ee.publish(i)}function Ve(){if(!K||!x||!S||!W)return;let t={owner:A,allowedTools:[...he],requiredTools:[re,`mcp`],allowMcpTools:!0,allowedExternalProfiles:S===`fable`?[n.FABLE_PLAN_PROFILE]:[],denyExtensions:!1};k?k.update(t):k=(0,m.registerSubagentPolicy)((0,te.createProtocolRuntime)({bus:e.events,source:A,sessionId:W.sessionManager.getSessionId()}),t)}function X(){e.appendEntry(v,{version:2,...x&&S?{activeFlavor:S}:{},...C?{originalSnapshot:{tools:[...C.tools],...C.model?{model:{...C.model}}:{},thinking:C.thinking}}:{},...L?{planId:L}:{},...U?{interruptedFableStage:U}:{}})}function Z(e,t,n){c.recordWarning(r.PLAN_EVENT.modelResolveFailed,`Planning model ${n}: ${t}`,{[se]:t,"plan.model.phase":e,"plan.model.reason":n})}async function He(t){let n=w?.main;if(n){if(n.model){let r=Re(n.model,t);r?await e.setModel(r)||(t.ui.notify(`Planning model is unavailable without authentication: ${n.model}`,M),Z(`apply`,n.model,le)):(t.ui.notify(`Planning model not found: ${n.model}`,M),Z(`apply`,n.model,ce))}n.thinking&&e.setThinkingLevel(n.thinking)}}async function Ue(t,n){if(!n)return!1;if(n.model){let i=`${n.model.provider}/${n.model.id}`,a=t.modelRegistry.find(n.model.provider,n.model.id);if(!a)return t.ui.notify(`Previous model not found: ${i}`,M),Z(`restore`,i,ce),!1;try{if(!await e.setModel(a))return t.ui.notify(`Previous model is unavailable without authentication: ${i}`,M),Z(`restore`,i,le),!1}catch(e){return t.ui.notify(`Previous model could not be restored: ${i}`,M),Z(`restore`,i,ue),c.recordError(r.PLAN_EVENT.modelResolveFailed,e,{[se]:i,"plan.model.phase":`restore`,"plan.model.reason":ue}),!1}}return e.setThinkingLevel(n.thinking),!0}function We(e){x&&S?e.ui.notify(`Already in plan:${S}.`,ae):e.ui.notify(`Plan mode is inactive.`,ae)}function Q(e){let t=be.then(e,e);return be=t.then(()=>void 0,()=>void 0),t}async function Ge(e){if(!G&&!H.match(/^(draft|review)$/u))return;(H===`draft`||H===`review`)&&(U=H),qe.cancel(e);let t=G;if(t)try{await t}catch(e){c.recordError(r.PLAN_EVENT.modeDisabled,e,{[P]:`fable`,"plan.fable.reason":`cancel_failed`})}G=void 0,U&&(H=`interrupted`),W&&Y(W),X()}async function $(n,i,a,o){if(W=n,x){if(S===i){We(n);return}S===`fable`&&await Ge(`Leaving Fable for plan:${i}.`),S=i,Ve(),e.setActiveTools(je(C?.tools??[],e.getActiveTools(),e.getAllTools().map(e=>e.name),i,xe)),Y(n),X(),c.recordEvent(r.PLAN_EVENT.modeEnabled,{[N]:a,[P]:i,"plan.tool.count":e.getActiveTools().length});return}let s;try{s=t()}catch(e){throw c.recordError(r.PLAN_EVENT.configLoadFailed,e,{[N]:a}),e}o?L??=z():(F=void 0,R=!1,L=z(),U=void 0,H=`idle`),w=s,C??={tools:[...e.getActiveTools()],...n.model?{model:{provider:n.model.provider,id:n.model.id}}:{},thinking:e.getThinkingLevel()},x=!0,S=i,e.setActiveTools(je(C.tools,e.getActiveTools(),e.getAllTools().map(e=>e.name),i,xe)),Ve(),await He(n),Y(n),X(),c.recordEvent(r.PLAN_EVENT.modeEnabled,{[N]:a,[P]:i,"plan.tool.count":e.getActiveTools().length,...w?.main?.model?{[se]:w.main.model}:{},...w?.main?.thinking?{"plan.thinking":w.main.thinking}:{}})}async function Ke(t,n){if(!x)return We(t),!1;await Ge(`Plan mode is exiting.`);let i=C;if(!await Ue(t,i))return t.ui.notify(`Previous model was not restored. Plan mode remains read-only.`,M),Y(t),X(),c.recordWarning(r.PLAN_EVENT.modeDisabled,`Plan mode restoration failed.`,{[N]:n,[P]:S??`unknown`,"plan.restored":!1}),!1;let a=!!F;return e.setActiveTools(Me(i?.tools??[],e.getActiveTools())),k?.dispose(),k=void 0,x=!1,S=void 0,w=void 0,C=void 0,V=void 0,H=`idle`,U=void 0,Y(t),X(),c.recordEvent(r.PLAN_EVENT.modeDisabled,{[N]:n,[P]:`normal`,"plan.written":a,"plan.restored":!0}),!0}let qe=n.createFablePlanFlow({broker:Ce,isAuthorized:()=>x&&S===`fable`&&q(),onStage:e=>{H=e,(e===`completed`||e===`failed`||e===`cancelled`)&&(U=void 0),W&&Y(W),x&&X()},onError:e=>{c.recordError(r.PLAN_EVENT.modeEnabled,e,{[P]:`fable`,"plan.fable.reason":`flow_error`})}});e.registerTool({name:D,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||S!==`debug`)return{content:[{type:`text`,text:`Debug planning is not active.`}],details:{recorded:!1}};try{V=ke(t)}catch(e){throw c.recordWarning(r.PLAN_EVENT.modeEnabled,e,{[P]:`debug`,"plan.debug.reason":`invalid_evidence`}),e}return W&&Y(W),{content:[{type:`text`,text:`Debug evidence recorded as optional planning context.`}],details:{recorded:!0}}}}),e.registerTool({name:O,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){if(!x||S!==`fable`)return{content:[{type:`text`,text:`Fable planning is not active.`}],details:{started:!1}};if(!q())return{content:[{type:`text`,text:`The local Fable broker is unavailable. Fable planning remains disabled.`}],details:{started:!1,errorCode:`broker_unavailable`}};let r=qe.run(t,n);G=r;try{let e=await r;return{content:[{type:`text`,text:e.draft?`Fable draft:\n${e.draft}`:`Fable planning ${e.status}: ${e.errorCode??`no output`}.`}],details:{...e,started:!0}}}finally{G===r&&(G=void 0)}}}),e.registerTool({name:b,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(t,n,i,a,d){if(!x)return{content:[{type:`text`,text:`Plan mode is disabled. Use normal file tools instead.`}],details:{written:!1}};let f=ve(d.sessionManager.getBranch(),t);if(!f){let e=Error(`Present the complete implementation plan as visible Markdown in chat before calling write_plan.`);throw c.recordError(r.PLAN_EVENT.writePlanFailed,e,{"plan.reason":`missing_visible_plan`}),e}let p=Date.now(),te=(0,o.getHarnessState)().root??d.cwd,m=(0,o.resolvePlanningPlansDirectory)(w?.plansDirectory,te,ee.default.homedir()),h=_e(d.sessionManager.getSessionId());L??=z();let g=F?.planId===L&&u.default.dirname(F.path)===m?F.path:void 0,_=ge(f),v=L,y=g??u.default.join(m,`${_}--${v}.md`),b=new AbortController,re=Error(`Writing ${y} timed out after ${fe}ms.`),S=setTimeout(()=>b.abort(re),fe),C=i?AbortSignal.any([i,b.signal]):b.signal,T=`checking`,E=()=>{a?.({content:[{type:`text`,text:`${T===`checking`?`Checking`:`Writing`} ${y}...`}],details:{phase:T,path:y,startedAt:p}})},D=(e,t)=>{c.recordWarning(r.PLAN_EVENT.writePlanUnsafePath,t,{"plan.phase":T,"plan.refusal":e})};try{E();try{await B(()=>l.default.promises.mkdir(m,{recursive:!0,mode:448}),C)}catch(e){throw C.aborted?ye(C):Error(`Could not create the configured plans directory ${m}.`,{cause:e})}let t=await B(()=>l.default.promises.lstat(m),C),n=await B(()=>l.default.promises.realpath(m),C);if(t.isSymbolicLink()||!t.isDirectory())return D(`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:y,phase:T,durationMs:Date.now()-p}};T=`writing`,E();let i;try{for(let e=0;e<3;e+=1)try{let e=g?0:l.default.constants.O_EXCL;i=await B(()=>l.default.promises.open(y,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(!(!g&&t instanceof Error&&`code`in t&&String(t.code)===`EEXIST`)||e===2)throw t;v=z(),L=v,y=u.default.join(m,`${_}--${v}.md`),X()}if(!i)throw Error(`Could not allocate a unique plan filename in ${m}.`);let e=await B(()=>l.default.promises.realpath(m),C),t=await B(()=>i.stat(),C);if(e!==n||!t.isFile()||t.nlink!==1)return D(`invalid_destination`,oe),{content:[{type:`text`,text:oe}],details:{written:!1,path:y,phase:T,durationMs:Date.now()-p}};await B(()=>i.truncate(0),C),await B(()=>i.writeFile(f,{encoding:`utf8`,signal:C}),C)}catch(e){if(e instanceof Error&&`code`in e&&[`EISDIR`,`ELOOP`,`ENXIO`].includes(String(e.code)))return D(`invalid_destination`,oe),{content:[{type:`text`,text:oe}],details:{written:!1,path:y,phase:T,durationMs:Date.now()-p}};throw e}finally{await i?.close()}return F={content:f,path:y,sessionId:h,planId:v,title:_,writtenAt:new Date().toISOString()},e.appendEntry(ne,F),R=!0,c.recordEvent(r.PLAN_EVENT.planWritten,{"plan.written":!0,"plan.bytes":s.Buffer.byteLength(f,`utf8`),"plan.duration_ms":Date.now()-p,"plan.revised":!!g}),{content:[{type:`text`,text:`Wrote implementation plan to ${y}.`}],details:{written:!0,path:y,phase:T,durationMs:Date.now()-p}}}catch(e){let t={"plan.phase":T,"plan.duration_ms":Date.now()-p};if(b.signal.aborted&&!i?.aborted){let n=Error(`write_plan timed out during ${T} for ${y}.`,{cause:e});throw c.recordError(r.PLAN_EVENT.writePlanTimedOut,n,{...t,"plan.timeout_ms":fe}),n}throw c.recordError(r.PLAN_EVENT.writePlanFailed,e,{...t,"plan.cancelled":i?.aborted??!1}),e}finally{clearTimeout(S)}}}),e.registerTool({name:y,label:`Complete Plan`,description:`Use only after presenting a concrete implementation plan. Ask the user whether to exit plan mode and begin implementation or continue planning.`,promptSnippet:`Ask the user whether to exit plan mode after the implementation plan is complete`,parameters:{type:`object`,properties:{},additionalProperties:!1},async execute(e,t,n,i,a){if(!x)return{content:[{type:`text`,text:`Plan mode is already disabled.`}],details:{exited:!1}};if(!R)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 c.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 o=await a.ui.select(`Plan complete. What would you like to do?`,[pe,`Continue planning`]);if(c.recordEvent(r.PLAN_EVENT.planReviewCompleted,{"plan.outcome":o===pe?`exited`:`continued`}),o===pe){let e=await Q(()=>Ke(a,`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 R=!1,{content:[{type:`text`,text:`The user chose to continue planning. Remain read-only and refine the plan.`}],details:{exited:!1}}}}),K&&(0,_.registerDoomLeaderActionHandlers)(e,{source:A,handlers:{"plan.normal":()=>{let e=W;return e?Q(()=>$(e,`normal`,I,!1)):void 0},"plan.exit":()=>{let e=W;return e?Q(async()=>{await Ke(e,I)}):void 0},"plan.debug":()=>{let e=W;return e?Q(()=>$(e,`debug`,I,!1)):void 0},"plan.fable":()=>{let e=W;return e?Q(()=>$(e,`fable`,I,!1)):void 0}},onError:(e,t)=>{W?.hasUI&&W.ui.notify(`Leader action ${t} failed. Plan mode remains read-only.`,M),c.recordError(r.PLAN_EVENT.modeEnabled,e,{[N]:I,"plan.action":t})}}),e.on(`tool_call`,async(e,t)=>{if(!x)return;let n=S===`debug`&&(e.toolName===D||xe.has(e.toolName))||S===`fable`&&e.toolName===O;if(!me.has(e.toolName)&&!n)return{block:!0,reason:`Plan mode blocks the ${e.toolName} tool. Use SPC p e to restore access.`};let r=Pe(w?.subagents,t.model);if(e.toolName===E){r&&Ie(e.input,r);return}if(e.toolName!==T)return;let i=e.input;if(Le(i.action))return{block:!0,reason:`Plan mode blocks subagent management action '${i.action}'. Use SPC p e first.`};Ne(i),r&&Fe(i,r)}),e.on(`before_agent_start`,async(e,t)=>{let n=[];if(x){let e=(0,o.getHarnessState)().root??t.cwd,r=(0,o.resolvePlanningPlansDirectory)(w?.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 so the user can review the plan and choose whether to begin implementation or continue planning. Do not exit plan mode without that approval.`,a.buildFlavorPlanningPrompt(S,r,V,H))}if(F&&n.push(`[CURRENT PLAN]\nSource: ${F.path}\n\n${F.content}`),n.length!==0)return{systemPrompt:`${e.systemPrompt}\n\n${n.join(`
4
4
 
5
- `)}`}}),e.on(`context`,async e=>({messages:e.messages.filter(e=>e.customType!==`agent-harness-plan-mode-context`)})),e.on(`session_start`,async(t,n)=>{let r=n.sessionManager.getEntries(),i=r.flatMap(e=>e.type===`custom`&&e.customType===v?[xe(e.data)]:[]).filter(e=>e!==void 0).at(-1),a=me(n.sessionManager.getSessionId()),o=r.flatMap(e=>e.type===`custom`&&e.customType===ne?[Se(e.data)]:[]).filter(e=>e!==void 0).at(-1),s=w;if(A?.dispose(),A=void 0,I=o?.sessionId===a?o:void 0,R=i?.planId??I?.planId,z=!1,T=void 0,H=void 0,x=!1,C=void 0,De(),w=i?.originalSnapshot,W=i?.interruptedFableStage,U=W?`interrupted`:`idle`,G=n,i?.activeFlavor)await He(n,i.activeFlavor,`session_restore`,!0);else{let t=i?.originalSnapshot??s;t?await ze(n,t)&&e.setActiveTools([...t.tools]):e.setActiveTools(e.getActiveTools().filter(e=>e!==y&&e!==b&&e!==O&&e!==k)),w=void 0,X(n)}}),e.on(`session_shutdown`,async(t,n)=>{await Ve(`Plan host session is shutting down.`),x&&w&&await ze(n,w)&&e.setActiveTools([...w.tools]),A?.dispose(),A=void 0,Te.dispose(),G=void 0,await c.shutdown()})}exports.WRITE_PLAN_TIMEOUT_MS=le,exports.configurePlanningSubagentInput=je,exports.configurePlanningTaskInput=Me,exports.constrainSubagentInput=ke,exports.createPlanIdentifier=B,exports.isBlockedSubagentManagementAction=Ne,exports.loadPlanningModeConfig=Fe,exports.parseDebugEvidencePacket=Ee,exports.parsePersistedPlanState=xe,exports.planModeExtension=Le,exports.planModeTools=De,exports.planSessionIdentifier=me,exports.planTitleSlug=pe,exports.planningSubagentModel=Ae,exports.visiblePlanForToolCall=he;
5
+ `)}`}}),e.on(`context`,async e=>({messages:e.messages.filter(e=>e.customType!==`agent-harness-plan-mode-context`)})),e.on(`session_start`,async(t,n)=>{let r=n.sessionManager.getEntries(),i=r.flatMap(e=>e.type===`custom`&&e.customType===v?[we(e.data)]:[]).filter(e=>e!==void 0).at(-1),a=_e(n.sessionManager.getSessionId()),o=r.flatMap(e=>e.type===`custom`&&e.customType===ne?[Te(e.data)]:[]).filter(e=>e!==void 0).at(-1),s=C;if(k?.dispose(),k=void 0,F=o?.sessionId===a?o:void 0,L=i?.planId??F?.planId,R=!1,w=void 0,V=void 0,x=!1,S=void 0,Ae(),C=i?.originalSnapshot,U=i?.interruptedFableStage,H=U?`interrupted`:`idle`,W=n,i?.activeFlavor)await $(n,i.activeFlavor,`session_restore`,!0);else{let t=i?.originalSnapshot??s;t?await Ue(n,t)&&e.setActiveTools(Me(t.tools,e.getActiveTools())):e.setActiveTools(e.getActiveTools().filter(e=>e!==y&&e!==b&&e!==D&&e!==O)),C=void 0,Y(n)}}),e.on(`session_shutdown`,async(t,n)=>{await Ge(`Plan host session is shutting down.`),x&&C&&await Ue(n,C)&&e.setActiveTools(Me(C.tools,e.getActiveTools())),k?.dispose(),k=void 0,Oe.dispose(),W=void 0,await c.shutdown()})}exports.WRITE_PLAN_TIMEOUT_MS=fe,exports.configurePlanningSubagentInput=Fe,exports.configurePlanningTaskInput=Ie,exports.constrainSubagentInput=Ne,exports.createPlanIdentifier=z,exports.isBlockedSubagentManagementAction=Le,exports.loadPlanningModeConfig=ze,exports.parseDebugEvidencePacket=ke,exports.parsePersistedPlanState=we,exports.planModeExtension=Ve,exports.planModeTools=Ae,exports.planSessionIdentifier=_e,exports.planTitleSlug=ge,exports.planningSubagentModel=Pe,exports.visiblePlanForToolCall=ve;
6
6
  //# sourceMappingURL=planMode.cjs.map