@agimon-ai/doompi-plan 0.0.1-alpha.0

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.
Files changed (74) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +42 -0
  3. package/dist/_virtual/_rolldown/runtime.cjs +1 -0
  4. package/dist/config.cjs +2 -0
  5. package/dist/config.cjs.map +1 -0
  6. package/dist/config.d.cts +14 -0
  7. package/dist/config.d.cts.map +1 -0
  8. package/dist/config.d.mts +14 -0
  9. package/dist/config.d.mts.map +1 -0
  10. package/dist/config.mjs +2 -0
  11. package/dist/config.mjs.map +1 -0
  12. package/dist/extension.cjs +2 -0
  13. package/dist/extension.cjs.map +1 -0
  14. package/dist/extension.d.cts +8 -0
  15. package/dist/extension.d.cts.map +1 -0
  16. package/dist/extension.d.mts +8 -0
  17. package/dist/extension.d.mts.map +1 -0
  18. package/dist/extension.mjs +2 -0
  19. package/dist/extension.mjs.map +1 -0
  20. package/dist/extensions/doom.cjs +2 -0
  21. package/dist/extensions/doom.cjs.map +1 -0
  22. package/dist/extensions/doom.d.cts +8 -0
  23. package/dist/extensions/doom.d.cts.map +1 -0
  24. package/dist/extensions/doom.d.mts +8 -0
  25. package/dist/extensions/doom.d.mts.map +1 -0
  26. package/dist/extensions/doom.mjs +2 -0
  27. package/dist/extensions/doom.mjs.map +1 -0
  28. package/dist/extensions/pi.cjs +2 -0
  29. package/dist/extensions/pi.cjs.map +1 -0
  30. package/dist/extensions/pi.d.cts +2 -0
  31. package/dist/extensions/pi.d.mts +2 -0
  32. package/dist/extensions/pi.mjs +2 -0
  33. package/dist/extensions/pi.mjs.map +1 -0
  34. package/dist/fableFlow.cjs +3 -0
  35. package/dist/fableFlow.cjs.map +1 -0
  36. package/dist/fableFlow.d.cts +66 -0
  37. package/dist/fableFlow.d.cts.map +1 -0
  38. package/dist/fableFlow.d.mts +66 -0
  39. package/dist/fableFlow.d.mts.map +1 -0
  40. package/dist/fableFlow.mjs +3 -0
  41. package/dist/fableFlow.mjs.map +1 -0
  42. package/dist/index.cjs +1 -0
  43. package/dist/index.d.cts +5 -0
  44. package/dist/index.d.mts +5 -0
  45. package/dist/index.mjs +1 -0
  46. package/dist/logSinkTelemetry.cjs +2 -0
  47. package/dist/logSinkTelemetry.cjs.map +1 -0
  48. package/dist/logSinkTelemetry.d.cts +24 -0
  49. package/dist/logSinkTelemetry.d.cts.map +1 -0
  50. package/dist/logSinkTelemetry.d.mts +24 -0
  51. package/dist/logSinkTelemetry.d.mts.map +1 -0
  52. package/dist/logSinkTelemetry.mjs +2 -0
  53. package/dist/logSinkTelemetry.mjs.map +1 -0
  54. package/dist/planConfig.cjs +2 -0
  55. package/dist/planConfig.cjs.map +1 -0
  56. package/dist/planConfig.mjs +2 -0
  57. package/dist/planConfig.mjs.map +1 -0
  58. package/dist/planMode.cjs +6 -0
  59. package/dist/planMode.cjs.map +1 -0
  60. package/dist/planMode.d.cts +75 -0
  61. package/dist/planMode.d.cts.map +1 -0
  62. package/dist/planMode.d.mts +75 -0
  63. package/dist/planMode.d.mts.map +1 -0
  64. package/dist/planMode.mjs +6 -0
  65. package/dist/planMode.mjs.map +1 -0
  66. package/dist/prompts.cjs +3 -0
  67. package/dist/prompts.cjs.map +1 -0
  68. package/dist/prompts.d.cts +24 -0
  69. package/dist/prompts.d.cts.map +1 -0
  70. package/dist/prompts.d.mts +24 -0
  71. package/dist/prompts.d.mts.map +1 -0
  72. package/dist/prompts.mjs +3 -0
  73. package/dist/prompts.mjs.map +1 -0
  74. package/package.json +61 -0
@@ -0,0 +1,6 @@
1
+ import{getHarnessState as e,loadDoomConfig as t,loadHarnessState as n,resolvePlanningPlansDirectory as r}from"./config.mjs";import{FABLE_PLAN_PROFILE as i,createFablePlanFlow as a}from"./fableFlow.mjs";import{planConfigSections as o,planSettingByFieldId as s}from"./planConfig.mjs";import{buildFlavorPlanningPrompt as c}from"./prompts.mjs";import{PLAN_EVENT as l,createPlanTelemetry as u}from"./logSinkTelemetry.mjs";import{globalDoomConfigPath as ee}from"@agimon-ai/doompi-config";import{Buffer as te}from"node:buffer";import{createHash as d,randomUUID as f}from"node:crypto";import p from"node:fs";import ne from"node:os";import re from"node:path";import{setDoomConfigValue as ie,unsetDoomConfigValue as m}from"@agimon-ai/doompi-config/configWriter";import{CONFIG_ACTION as h,registerDoomConfigContribution as g}from"@agimon-ai/doompi-ui/config";import{createDoomLeaderContribution as _,registerDoomLeaderActionHandlers as v}from"@agimon-ai/doompi-ui/leader";import{registerDoomModeContribution as ae}from"@agimon-ai/doompi-ui/mode";import{createProtocolRuntime as oe}from"@agimon-ai/doompi-extension-contracts/protocol";import{FABLE_PLAN_REQUESTER as y,createFablePlanBrokerClient as b}from"@agimon-ai/doompi-extension-contracts/fable-plan";import{registerSubagentPolicy as se}from"@agimon-ai/doompi-extension-contracts/subagent-policy";import{isSubagentAction as x,subagentActionAcceptsField as S}from"@agimon-ai/doompi-extension-contracts/subagent-tool";const ce=`agent-harness-plan-mode`,le=`agent-harness-plan-document`,ue=`complete_plan`,C=`write_plan`,w=`ask_user_question`,de=`bash`,T=`find`,E=`grep`,D=`read`,fe=`subagent`,pe=`task`,O=`record_debug_evidence`,k=`run_fable_plan`,A=`implementation-plan`,j=`@agimon-ai/doompi-plan`,M=`warning`,me=`info`,he=`Refused to write the plan because its destination is not a regular file.`,N=`plan.trigger`,ge=`plan.model`,P=`plan.flavor`,_e=`not_found`,ve=`unauthenticated`,ye=`restore_failed`,be=new Set,F=5e3,xe=`Exit plan mode and start implementation`,Se=new Set([`add_directory`,w,de,ue,T,E,`intercom`,`ls`,D,`search_external_files`,fe,`subagent_supervisor`,`subagent_wait`,pe,C]),I=[D,de,E,T,`ls`],Ce=[...I,`mcp`],L=`leader`,R=[`off`,`minimal`,`low`,`medium`,`high`,`xhigh`,`max`],z=[`idle`,`draft`,`review`,`completed`,`failed`,`cancelled`,`interrupted`];function we(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 Te(e){return`${encodeURIComponent(e).slice(0,48)||`session`}--${d(`sha256`).update(e).digest(`hex`)}`}function B(e=new Date,t=f()){return`${e.toISOString().replace(/[-:]/g,``).replace(/\.\d{3}Z$/,`Z`)}--${t}`}function Ee(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===C});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
+
3
+ `).trim()||void 0}}function De(e){return e.reason instanceof Error?e.reason:Error(`Plan write cancelled.`)}async function V(e,t){if(t.aborted)throw De(t);let n,r=new Promise((e,r)=>{n=()=>r(De(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 Oe(e){return U(e)&&R.includes(e)?e:void 0}function G(e){return U(e)&&z.includes(e)?e:void 0}function K(e){if(!(!Array.isArray(e)||e.some(e=>!U(e))))return e.map(e=>e)}function ke(e){if(!H(e))return;let t=K(e.tools),n=Oe(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 Ae(e){return e===`normal`||e===`debug`||e===`fable`?e:void 0}function je(e){if(e.version!==2)return;let t=e.activeFlavor===void 0?void 0:Ae(e.activeFlavor);if(e.activeFlavor!==void 0&&!t)return;let n=e.originalSnapshot===void 0?void 0:ke(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: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 Me(e){if(e.enabled!==!0&&e.enabled!==!1)return;let t=e.toolsBeforePlanMode===void 0?void 0:K(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:Oe(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 Ne(e){if(H(e))return je(e)??Me(e)}function Pe(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 Fe(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(te.byteLength(r,`utf8`)>4096)throw Error(`${t} exceeds the bounded debug evidence limit.`);return r}function q(e,t){if(!Array.isArray(e)||e.length>32)throw Error(`${t} must be a bounded string array.`);return e.map((e,n)=>Fe(e,`${t}[${n}]`,!1))}function Ie(e,t){return e===void 0?``:Fe(e,t,!1)}function J(e,t){return e===void 0?[]:q(e,t)}function Y(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 Le(e){if(!H(e))throw Error(`Debug evidence must be an object.`);Y(e,[`issue`,`expectedBehavior`,`reproductionAttempt`,`actualBehavior`,`logs`,`correlatedTraceEvidence`,`processOutput`,`browserConsoleEvidence`,`correlationIds`,`timestamps`,`verifiedFacts`,`hypotheses`,`unavailableEvidence`],`Debug evidence`);let t={issue:Fe(e.issue,`issue`,!0),expectedBehavior:Ie(e.expectedBehavior,`expectedBehavior`),reproductionAttempt:Ie(e.reproductionAttempt,`reproductionAttempt`),actualBehavior:Ie(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(te.byteLength(JSON.stringify(t),`utf8`)>32768)throw Error(`Debug evidence exceeds the bounded packet size.`);return t}function Re(e,t){let n=new Set(t),r=e.filter(e=>Se.has(e)),i=I.filter(e=>n.has(e)),a=[w,ue,pe,C].filter(e=>n.has(e));return[...new Set([...r,...i,...a])]}function ze(e,t,n,r){let i=new Set(t),a=Re(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 Be(e){if(e.action!==void 0){x(e.action)&&S(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 Ve(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 He(e,t){if(e.action===`run`)for(let n of e.requests??[])n.model=t}function Ue(e,t){e.action===`assign`&&(e.model=t)}function We(e){return new Set([`append-step`,`create`,`delete`,`disable`,`eject`,`enable`,`grant-spawn-budget`,`reset`,`schedule`,`schedule-cancel`,`update`,`watchdog.configure`]).has(e??``)}function Ge(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 Ke(e=process.env,r=process.cwd()){return t(n(e).state.root??r).modes?.planning}function qe(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 Z(t,n=Ke,d=u(),f={}){let x=!1,S,w,T,E,D,A,I=!1,R,z=`idle`,H,U,W,Oe=Promise.resolve(),G=f.doomIntegrations??!0,K=new Set(f.debugDiagnosticTools??be),ke=b(()=>U?.sessionManager.getSessionId()),Ae=f.fableBroker??{start:(e,t)=>ke.start(e,t),cancel:(e,t)=>ke.cancel({requester:y,operationId:e,reason:t})},je=()=>f.fableBroker!==void 0||ke.isAvailable(),Me=G?ae(t,{source:j,id:`plan`,order:40}):{update:()=>void 0},Fe=G?_(t,{source:j,bindings:qe(!1)}):{update:()=>void 0},q,Ie=G?g(t,{source:j,listSections:()=>{try{return o(n(),q)}catch(e){return o(void 0,e instanceof Error?e.message:String(e))}},handlers:{[h.set]:async({fieldId:e,value:t})=>{let n=s(e);!n||!t||(q=void 0,await ie(ee(),[...n.keyPath],t))},[h.clear]:async({fieldId:e})=>{let t=s(e);t&&(q=void 0,await m(ee(),[...t.keyPath]))}},onError:e=>{q=e instanceof Error?e.message:String(e)}}):{dispose:()=>void 0,update:()=>void 0};function J(){Fe.update(qe(x))}function Y(e){J();let t=S,n=x&&t?`plan:${t}`:void 0,r=t===`fable`&&z!==`idle`?` · ${z}`:``;e.ui.setStatus(`plan-mode`,n?e.ui.theme.fg(M,n):void 0),Me.update(x&&t?{label:`PLAN`,detail:`${t}${r} - read only`,color:M}:void 0)}function Re(){if(!G||!x||!S||!U)return;let e={owner:j,allowedTools:[...Ce],requiredTools:[de,`mcp`],allowMcpTools:!0,allowedExternalProfiles:S===`fable`?[i]:[],denyExtensions:!1};E?E.update(e):E=se(oe({bus:t.events,source:j,sessionId:U.sessionManager.getSessionId()}),e)}function X(){t.appendEntry(ce,{version:2,...x&&S?{activeFlavor:S}:{},...w?{originalSnapshot:{tools:[...w.tools],...w.model?{model:{...w.model}}:{},thinking:w.thinking}}:{},...A?{planId:A}:{},...H?{interruptedFableStage:H}:{}})}function Z(e,t,n){d.recordWarning(l.modelResolveFailed,`Planning model ${n}: ${t}`,{[ge]:t,"plan.model.phase":e,"plan.model.reason":n})}async function Je(e){let n=T?.main;if(n){if(n.model){let r=Ge(n.model,e);r?await t.setModel(r)||(e.ui.notify(`Planning model is unavailable without authentication: ${n.model}`,M),Z(`apply`,n.model,ve)):(e.ui.notify(`Planning model not found: ${n.model}`,M),Z(`apply`,n.model,_e))}n.thinking&&t.setThinkingLevel(n.thinking)}}async function Ye(e,n){if(!n)return!1;if(n.model){let r=`${n.model.provider}/${n.model.id}`,i=e.modelRegistry.find(n.model.provider,n.model.id);if(!i)return e.ui.notify(`Previous model not found: ${r}`,M),Z(`restore`,r,_e),!1;try{if(!await t.setModel(i))return e.ui.notify(`Previous model is unavailable without authentication: ${r}`,M),Z(`restore`,r,ve),!1}catch(t){return e.ui.notify(`Previous model could not be restored: ${r}`,M),Z(`restore`,r,ye),d.recordError(l.modelResolveFailed,t,{[ge]:r,"plan.model.phase":`restore`,"plan.model.reason":ye}),!1}}return t.setThinkingLevel(n.thinking),!0}function Xe(e){x&&S?e.ui.notify(`Already in plan:${S}.`,me):e.ui.notify(`Plan mode is inactive.`,me)}function Q(e){let t=Oe.then(e,e);return Oe=t.then(()=>void 0,()=>void 0),t}async function Ze(e){if(!W&&!z.match(/^(draft|review)$/u))return;(z===`draft`||z===`review`)&&(H=z),$e.cancel(e);let t=W;if(t)try{await t}catch(e){d.recordError(l.modeDisabled,e,{[P]:`fable`,"plan.fable.reason":`cancel_failed`})}W=void 0,H&&(z=`interrupted`),U&&Y(U),X()}async function $(e,r,i,a){if(U=e,x){if(S===r){Xe(e);return}S===`fable`&&await Ze(`Leaving Fable for plan:${r}.`),S=r,Re(),t.setActiveTools(ze(w?.tools??[],t.getAllTools().map(e=>e.name),r,K)),Y(e),X(),d.recordEvent(l.modeEnabled,{[N]:i,[P]:r,"plan.tool.count":t.getActiveTools().length});return}let o;try{o=n()}catch(e){throw d.recordError(l.configLoadFailed,e,{[N]:i}),e}a?A??=B():(D=void 0,I=!1,A=B(),H=void 0,z=`idle`),T=o,w??={tools:[...t.getActiveTools()],...e.model?{model:{provider:e.model.provider,id:e.model.id}}:{},thinking:t.getThinkingLevel()},x=!0,S=r,t.setActiveTools(ze(w.tools,t.getAllTools().map(e=>e.name),r,K)),Re(),await Je(e),Y(e),X(),d.recordEvent(l.modeEnabled,{[N]:i,[P]:r,"plan.tool.count":t.getActiveTools().length,...T?.main?.model?{[ge]:T.main.model}:{},...T?.main?.thinking?{"plan.thinking":T.main.thinking}:{}})}async function Qe(e,n){if(!x)return Xe(e),!1;await Ze(`Plan mode is exiting.`);let r=w;if(!await Ye(e,r))return e.ui.notify(`Previous model was not restored. Plan mode remains read-only.`,M),Y(e),X(),d.recordWarning(l.modeDisabled,`Plan mode restoration failed.`,{[N]:n,[P]:S??`unknown`,"plan.restored":!1}),!1;let i=!!D;return t.setActiveTools([...r?.tools??[]]),E?.dispose(),E=void 0,x=!1,S=void 0,T=void 0,w=void 0,R=void 0,z=`idle`,H=void 0,Y(e),X(),d.recordEvent(l.modeDisabled,{[N]:n,[P]:`normal`,"plan.written":i,"plan.restored":!0}),!0}let $e=a({broker:Ae,isAuthorized:()=>x&&S===`fable`&&je(),onStage:e=>{z=e,(e===`completed`||e===`failed`||e===`cancelled`)&&(H=void 0),U&&Y(U),x&&X()},onError:e=>{d.recordError(l.modeEnabled,e,{[P]:`fable`,"plan.fable.reason":`flow_error`})}});t.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||S!==`debug`)return{content:[{type:`text`,text:`Debug planning is not active.`}],details:{recorded:!1}};try{R=Le(t)}catch(e){throw d.recordWarning(l.modeEnabled,e,{[P]:`debug`,"plan.debug.reason":`invalid_evidence`}),e}return U&&Y(U),{content:[{type:`text`,text:`Debug evidence recorded as optional planning context.`}],details:{recorded:!0}}}}),t.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||S!==`fable`)return{content:[{type:`text`,text:`Fable planning is not active.`}],details:{started:!1}};if(!je())return{content:[{type:`text`,text:`The local Fable broker is unavailable. Fable planning remains disabled.`}],details:{started:!1,errorCode:`broker_unavailable`}};let r=$e.run(t,n);W=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{W===r&&(W=void 0)}}}),t.registerTool({name:C,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(n,i,a,o,s){if(!x)return{content:[{type:`text`,text:`Plan mode is disabled. Use normal file tools instead.`}],details:{written:!1}};let c=Ee(s.sessionManager.getBranch(),n);if(!c){let e=Error(`Present the complete implementation plan as visible Markdown in chat before calling write_plan.`);throw d.recordError(l.writePlanFailed,e,{"plan.reason":`missing_visible_plan`}),e}let u=Date.now(),ee=e().root??s.cwd,f=r(T?.plansDirectory,ee,ne.homedir()),ie=Te(s.sessionManager.getSessionId());A??=B();let m=D?.planId===A&&re.dirname(D.path)===f?D.path:void 0,h=we(c),g=A,_=m??re.join(f,`${h}--${g}.md`),v=new AbortController,ae=Error(`Writing ${_} timed out after ${F}ms.`),oe=setTimeout(()=>v.abort(ae),F),y=a?AbortSignal.any([a,v.signal]):v.signal,b=`checking`,se=()=>{o?.({content:[{type:`text`,text:`${b===`checking`?`Checking`:`Writing`} ${_}...`}],details:{phase:b,path:_,startedAt:u}})},S=(e,t)=>{d.recordWarning(l.writePlanUnsafePath,t,{"plan.phase":b,"plan.refusal":e})};try{se();try{await V(()=>p.promises.mkdir(f,{recursive:!0,mode:448}),y)}catch(e){throw y.aborted?De(y):Error(`Could not create the configured plans directory ${f}.`,{cause:e})}let e=await V(()=>p.promises.lstat(f),y),n=await V(()=>p.promises.realpath(f),y);if(e.isSymbolicLink()||!e.isDirectory())return S(`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:_,phase:b,durationMs:Date.now()-u}};b=`writing`,se();let r;try{for(let e=0;e<3;e+=1)try{let e=m?0:p.constants.O_EXCL;r=await V(()=>p.promises.open(_,p.constants.O_CREAT|p.constants.O_WRONLY|p.constants.O_NONBLOCK|p.constants.O_NOFOLLOW|e,384),y);break}catch(t){if(!(!m&&t instanceof Error&&`code`in t&&String(t.code)===`EEXIST`)||e===2)throw t;g=B(),A=g,_=re.join(f,`${h}--${g}.md`),X()}if(!r)throw Error(`Could not allocate a unique plan filename in ${f}.`);let e=await V(()=>p.promises.realpath(f),y),t=await V(()=>r.stat(),y);if(e!==n||!t.isFile()||t.nlink!==1)return S(`invalid_destination`,he),{content:[{type:`text`,text:he}],details:{written:!1,path:_,phase:b,durationMs:Date.now()-u}};await V(()=>r.truncate(0),y),await V(()=>r.writeFile(c,{encoding:`utf8`,signal:y}),y)}catch(e){if(e instanceof Error&&`code`in e&&[`EISDIR`,`ELOOP`,`ENXIO`].includes(String(e.code)))return S(`invalid_destination`,he),{content:[{type:`text`,text:he}],details:{written:!1,path:_,phase:b,durationMs:Date.now()-u}};throw e}finally{await r?.close()}return D={content:c,path:_,sessionId:ie,planId:g,title:h,writtenAt:new Date().toISOString()},t.appendEntry(le,D),I=!0,d.recordEvent(l.planWritten,{"plan.written":!0,"plan.bytes":te.byteLength(c,`utf8`),"plan.duration_ms":Date.now()-u,"plan.revised":!!m}),{content:[{type:`text`,text:`Wrote implementation plan to ${_}.`}],details:{written:!0,path:_,phase:b,durationMs:Date.now()-u}}}catch(e){let t={"plan.phase":b,"plan.duration_ms":Date.now()-u};if(v.signal.aborted&&!a?.aborted){let n=Error(`write_plan timed out during ${b} for ${_}.`,{cause:e});throw d.recordError(l.writePlanTimedOut,n,{...t,"plan.timeout_ms":F}),n}throw d.recordError(l.writePlanFailed,e,{...t,"plan.cancelled":a?.aborted??!1}),e}finally{clearTimeout(oe)}}}),t.registerTool({name:ue,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,r,i){if(!x)return{content:[{type:`text`,text:`Plan mode is already disabled.`}],details:{exited:!1}};if(!I)return{content:[{type:`text`,text:`Present the complete plan in chat and save it with write_plan before requesting approval.`}],details:{exited:!1}};if(!i.hasUI)return d.recordEvent(l.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 a=await i.ui.select(`Plan complete. What would you like to do?`,[xe,`Continue planning`]);if(d.recordEvent(l.planReviewCompleted,{"plan.outcome":a===xe?`exited`:`continued`}),a===xe){let e=await Q(()=>Qe(i,`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 I=!1,{content:[{type:`text`,text:`The user chose to continue planning. Remain read-only and refine the plan.`}],details:{exited:!1}}}}),G&&v(t,{source:j,handlers:{"plan.normal":()=>{let e=U;return e?Q(()=>$(e,`normal`,L,!1)):void 0},"plan.exit":()=>{let e=U;return e?Q(async()=>{await Qe(e,L)}):void 0},"plan.debug":()=>{let e=U;return e?Q(()=>$(e,`debug`,L,!1)):void 0},"plan.fable":()=>{let e=U;return e?Q(()=>$(e,`fable`,L,!1)):void 0}},onError:(e,t)=>{U?.hasUI&&U.ui.notify(`Leader action ${t} failed. Plan mode remains read-only.`,M),d.recordError(l.modeEnabled,e,{[N]:L,"plan.action":t})}}),t.on(`tool_call`,async(e,t)=>{if(!x)return;let n=S===`debug`&&(e.toolName===O||K.has(e.toolName))||S===`fable`&&e.toolName===k;if(!Se.has(e.toolName)&&!n)return{block:!0,reason:`Plan mode blocks the ${e.toolName} tool. Use SPC p e to restore access.`};let r=Ve(T?.subagents,t.model);if(e.toolName===pe){r&&Ue(e.input,r);return}if(e.toolName!==fe)return;let i=e.input;if(We(i.action))return{block:!0,reason:`Plan mode blocks subagent management action '${i.action}'. Use SPC p e first.`};Be(i),r&&He(i,r)}),t.on(`before_agent_start`,async(t,n)=>{let i=[];if(x){let t=e().root??n.cwd,a=r(T?.plansDirectory,t,ne.homedir());i.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 ${a}.\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 the turn and wait for completion notifications.\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.`,c(S,a,R,z))}if(D&&i.push(`[CURRENT PLAN]\nSource: ${D.path}\n\n${D.content}`),i.length!==0)return{systemPrompt:`${t.systemPrompt}\n\n${i.join(`
4
+
5
+ `)}`}}),t.on(`context`,async e=>({messages:e.messages.filter(e=>e.customType!==`agent-harness-plan-mode-context`)})),t.on(`session_start`,async(e,n)=>{let r=n.sessionManager.getEntries(),i=r.flatMap(e=>e.type===`custom`&&e.customType===ce?[Ne(e.data)]:[]).filter(e=>e!==void 0).at(-1),a=Te(n.sessionManager.getSessionId()),o=r.flatMap(e=>e.type===`custom`&&e.customType===le?[Pe(e.data)]:[]).filter(e=>e!==void 0).at(-1),s=w;if(E?.dispose(),E=void 0,D=o?.sessionId===a?o:void 0,A=i?.planId??D?.planId,I=!1,T=void 0,R=void 0,x=!1,S=void 0,J(),w=i?.originalSnapshot,H=i?.interruptedFableStage,z=H?`interrupted`:`idle`,U=n,i?.activeFlavor)await $(n,i.activeFlavor,`session_restore`,!0);else{let e=i?.originalSnapshot??s;e?await Ye(n,e)&&t.setActiveTools([...e.tools]):t.setActiveTools(t.getActiveTools().filter(e=>e!==ue&&e!==C&&e!==O&&e!==k)),w=void 0,Y(n)}}),t.on(`session_shutdown`,async(e,n)=>{await Ze(`Plan host session is shutting down.`),x&&w&&await Ye(n,w)&&t.setActiveTools([...w.tools]),E?.dispose(),E=void 0,Ie.dispose(),U=void 0,await d.shutdown()})}export{F as WRITE_PLAN_TIMEOUT_MS,He as configurePlanningSubagentInput,Ue as configurePlanningTaskInput,Be as constrainSubagentInput,B as createPlanIdentifier,We as isBlockedSubagentManagementAction,Ke as loadPlanningModeConfig,Le as parseDebugEvidencePacket,Ne as parsePersistedPlanState,Z as planModeExtension,Re as planModeTools,Te as planSessionIdentifier,we as planTitleSlug,Ve as planningSubagentModel,Ee as visiblePlanForToolCall};
6
+ //# sourceMappingURL=planMode.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"planMode.mjs","names":["loadDoomConfig","CONTRACT_FABLE_PLAN_REQUESTER","custom"],"sources":["../src/planMode.ts"],"sourcesContent":["import { Buffer } from 'node:buffer';\nimport { createHash, randomUUID } from 'node:crypto';\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport { globalDoomConfigPath } from '@agimon-ai/doompi-config';\nimport { setDoomConfigValue, unsetDoomConfigValue } from '@agimon-ai/doompi-config/configWriter';\nimport { CONFIG_ACTION, registerDoomConfigContribution } from '@agimon-ai/doompi-ui/config';\nimport {\n createDoomLeaderContribution,\n registerDoomLeaderActionHandlers,\n type DoomLeaderBinding,\n} from '@agimon-ai/doompi-ui/leader';\nimport { registerDoomModeContribution } from '@agimon-ai/doompi-ui/mode';\nimport { createProtocolRuntime } from '@agimon-ai/doompi-extension-contracts/protocol';\nimport {\n createFablePlanBrokerClient,\n FABLE_PLAN_REQUESTER as CONTRACT_FABLE_PLAN_REQUESTER,\n} from '@agimon-ai/doompi-extension-contracts/fable-plan';\nimport {\n registerSubagentPolicy,\n type SubagentPolicyHandle,\n} from '@agimon-ai/doompi-extension-contracts/subagent-policy';\nimport { isSubagentAction, subagentActionAcceptsField } from '@agimon-ai/doompi-extension-contracts/subagent-tool';\nimport type { ExtensionAPI, ExtensionContext, ToolCallEvent } from '@earendil-works/pi-coding-agent';\nimport {\n getHarnessState,\n loadDoomConfig,\n loadHarnessState,\n resolvePlanningPlansDirectory,\n type PlanningAgentConfig,\n type PlanningModeConfig,\n type PlanningThinkingLevel,\n} from './config.ts';\nimport { planConfigSections, planSettingByFieldId } from './planConfig.ts';\nimport {\n createFablePlanFlow,\n FABLE_PLAN_PROFILE,\n type FablePlanBroker,\n type FablePlanResult,\n type FableStage,\n} from './fableFlow.ts';\nimport { buildFlavorPlanningPrompt, type DebugEvidencePacket, type PlanningFlavor } from './prompts.ts';\nimport { createPlanTelemetry, PLAN_EVENT, type PlanTelemetry } from './logSinkTelemetry.ts';\n\nconst PLAN_MODE_ENTRY = 'agent-harness-plan-mode';\nconst PLAN_MODE_CONTEXT = 'agent-harness-plan-mode-context';\nconst PLAN_DOCUMENT_ENTRY = 'agent-harness-plan-document';\nconst COMPLETE_PLAN_TOOL = 'complete_plan';\nconst WRITE_PLAN_TOOL = 'write_plan';\nconst ASK_USER_TOOL = 'ask_user_question';\nconst BASH_TOOL = 'bash';\nconst FIND_TOOL = 'find';\nconst GREP_TOOL = 'grep';\nconst LIST_TOOL = 'ls';\nconst MCP_TOOL = 'mcp';\nconst READ_TOOL = 'read';\nconst SUBAGENT_TOOL = 'subagent';\nconst TASK_TOOL = 'task';\nconst RECORD_DEBUG_EVIDENCE_TOOL = 'record_debug_evidence';\nconst RUN_FABLE_PLAN_TOOL = 'run_fable_plan';\nconst DEFAULT_PLAN_TITLE = 'implementation-plan';\nconst PLAN_TITLE_MAX_LENGTH = 64;\nconst SESSION_ID_MAX_LENGTH = 48;\nconst PLAN_MODE_STATUS_KEY = 'plan-mode';\nconst PLAN_LEADER_SOURCE = '@agimon-ai/doompi-plan';\nconst PLAN_LEADER_GROUP_ORDER = 60;\n/** This mode's slot on the shared mode line doom-pi-ui renders. */\nconst MODE_CONTRIBUTION_ID = 'plan';\n/** Just the name; the flavour and read-only state ride along as detail. */\nconst MODE_LABEL = 'PLAN';\nconst READ_ONLY_DETAIL = 'read only';\n/** Between workflow mode and the loop, matching the leader order. */\nconst MODE_CONTRIBUTION_ORDER = 40;\nconst WARNING_STYLE = 'warning';\nconst INFO_STYLE = 'info';\nconst PRIVATE_FILE_MODE = 0o600;\nconst PRIVATE_DIRECTORY_MODE = 0o700;\nconst PLAN_ALLOCATION_ATTEMPTS = 3;\nconst INVALID_PLAN_DESTINATION = 'Refused to write the plan because its destination is not a regular file.';\nconst PLAN_TRIGGER_ATTRIBUTE = 'plan.trigger';\nconst PLAN_MODEL_ATTRIBUTE = 'plan.model';\nconst PLAN_FLAVOR_ATTRIBUTE = 'plan.flavor';\nconst MODEL_NOT_FOUND_REASON = 'not_found';\nconst MODEL_UNAUTHENTICATED_REASON = 'unauthenticated';\nconst MODEL_RESTORE_FAILED_REASON = 'restore_failed';\nconst DEBUG_EVIDENCE_MAX_BYTES = 32 * 1024;\nconst DEBUG_EVIDENCE_MAX_TEXT_BYTES = 4 * 1024;\nconst DEBUG_EVIDENCE_MAX_ITEMS = 32;\nconst DEBUG_DIAGNOSTIC_TOOLS = new Set<string>();\nexport const WRITE_PLAN_TIMEOUT_MS = 5_000;\nconst EXIT_PLAN_MODE_CHOICE = 'Exit plan mode and start implementation';\nconst CONTINUE_PLANNING_CHOICE = 'Continue planning';\nconst PLAN_MODE_PARENT_TOOLS = new Set([\n 'add_directory',\n ASK_USER_TOOL,\n BASH_TOOL,\n COMPLETE_PLAN_TOOL,\n FIND_TOOL,\n GREP_TOOL,\n 'intercom',\n LIST_TOOL,\n READ_TOOL,\n 'search_external_files',\n SUBAGENT_TOOL,\n 'subagent_supervisor',\n 'subagent_wait',\n TASK_TOOL,\n WRITE_PLAN_TOOL,\n]);\nconst PLAN_MODE_EXPLORATION_TOOLS = [READ_TOOL, BASH_TOOL, GREP_TOOL, FIND_TOOL, LIST_TOOL] as const;\nconst CHILD_EXPLORATION_TOOLS = [...PLAN_MODE_EXPLORATION_TOOLS, MCP_TOOL] as const;\nconst PLAN_MODE_TRIGGER_LEADER = 'leader' as const;\nconst PLAN_MODE_TRIGGER_SESSION_RESTORE = 'session_restore' as const;\nconst PLAN_MODE_TRIGGER_PLAN_APPROVED = 'plan_approved' as const;\nconst PLAN_MODE_TRIGGER_SESSION_SHUTDOWN = 'session_shutdown' as const;\nconst PLAN_STATE_VERSION = 2 as const;\nconst VALID_THINKING_LEVELS: readonly PlanningThinkingLevel[] = [\n 'off',\n 'minimal',\n 'low',\n 'medium',\n 'high',\n 'xhigh',\n 'max',\n];\nconst VALID_FABLE_STAGES: readonly FableStage[] = [\n 'idle',\n 'draft',\n 'review',\n 'completed',\n 'failed',\n 'cancelled',\n 'interrupted',\n];\n\nexport type { PlanningFlavor } from './prompts.ts';\n\nexport interface ModelIdentity {\n provider: string;\n id: string;\n}\n\nexport interface PlanSnapshot {\n tools: string[];\n model?: ModelIdentity;\n thinking: PlanningThinkingLevel;\n}\n\ninterface PersistedPlanModeState {\n version: typeof PLAN_STATE_VERSION;\n activeFlavor?: PlanningFlavor;\n originalSnapshot?: PlanSnapshot;\n planId?: string;\n interruptedFableStage?: FableStage;\n}\n\ninterface PlanDocument {\n content: string;\n path: string;\n sessionId: string;\n planId?: string;\n title: string;\n writtenAt: string;\n}\n\ntype PlanWritePhase = 'checking' | 'writing';\n\n/** Why plan mode changed state, so the sink can tell a user toggle from a session restore. */\ntype PlanModeTrigger =\n | typeof PLAN_MODE_TRIGGER_LEADER\n | typeof PLAN_MODE_TRIGGER_SESSION_RESTORE\n | typeof PLAN_MODE_TRIGGER_PLAN_APPROVED\n | typeof PLAN_MODE_TRIGGER_SESSION_SHUTDOWN;\n\nexport interface PlanModeExtensionOptions {\n fableBroker?: FablePlanBroker;\n debugDiagnosticTools?: readonly string[];\n doomIntegrations?: boolean;\n}\n\nexport function planTitleSlug(markdown: string): string {\n const heading = markdown.match(/^#\\s+(.+?)\\s*$/m)?.[1] ?? DEFAULT_PLAN_TITLE;\n return (\n heading\n .normalize('NFKD')\n .replace(/[\\u0300-\\u036f]/g, '')\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '')\n .slice(0, PLAN_TITLE_MAX_LENGTH) || DEFAULT_PLAN_TITLE\n );\n}\n\nexport function planSessionIdentifier(sessionId: string): string {\n const readable = encodeURIComponent(sessionId).slice(0, SESSION_ID_MAX_LENGTH) || 'session';\n const digest = createHash('sha256').update(sessionId).digest('hex');\n return `${readable}--${digest}`;\n}\n\nexport function createPlanIdentifier(now = new Date(), id = randomUUID()): string {\n const timestamp = now\n .toISOString()\n .replace(/[-:]/g, '')\n .replace(/\\.\\d{3}Z$/, 'Z');\n return `${timestamp}--${id}`;\n}\n\nexport function visiblePlanForToolCall(entries: readonly unknown[], toolCallId: string): string | undefined {\n for (let index = entries.length - 1; index >= 0; index -= 1) {\n const entry = entries[index];\n if (!entry || typeof entry !== 'object') continue;\n const candidate = entry as {\n type?: string;\n message?: { role?: string; content?: unknown };\n };\n if (candidate.type !== 'message' || candidate.message?.role !== 'assistant') continue;\n if (!Array.isArray(candidate.message.content)) continue;\n\n const toolIndex = candidate.message.content.findIndex((block) => {\n if (!block || typeof block !== 'object') return false;\n const content = block as { type?: string; id?: string; name?: string };\n return content.type === 'toolCall' && content.id === toolCallId && content.name === WRITE_PLAN_TOOL;\n });\n if (toolIndex < 0) continue;\n\n const plan = candidate.message.content\n .slice(0, toolIndex)\n .flatMap((block) => {\n if (!block || typeof block !== 'object') return [];\n const content = block as { type?: string; text?: unknown };\n return content.type === 'text' && typeof content.text === 'string' ? [content.text] : [];\n })\n .join('\\n\\n')\n .trim();\n return plan || undefined;\n }\n return undefined;\n}\n\nfunction abortReason(signal: AbortSignal): Error {\n return signal.reason instanceof Error ? signal.reason : new Error('Plan write cancelled.');\n}\n\nasync function waitForAbortable<T>(operation: () => Promise<T>, signal: AbortSignal): Promise<T> {\n if (signal.aborted) throw abortReason(signal);\n let onAbort: (() => void) | undefined;\n const aborted = new Promise<never>((_resolve, reject) => {\n onAbort = () => reject(abortReason(signal));\n signal.addEventListener('abort', onAbort, { once: true });\n });\n try {\n return await Promise.race([operation(), aborted]);\n } finally {\n if (onAbort) signal.removeEventListener('abort', onAbort);\n }\n}\n\ntype MutableSubagentStep = {\n model?: string;\n output?: string | boolean;\n progress?: boolean;\n worktree?: boolean;\n parallel?: MutableSubagentStep | MutableSubagentStep[];\n};\n\ntype MutableSubagentRunRequest = {\n [key: string]: unknown;\n model?: string;\n};\n\ntype MutableSubagentInput = MutableSubagentStep & {\n action?: string;\n artifacts?: boolean;\n outputSchema?: Record<string, unknown>;\n share?: boolean;\n tasks?: MutableSubagentStep[];\n chain?: MutableSubagentStep[];\n chainDir?: string;\n sessionDir?: string;\n requests?: MutableSubagentRunRequest[];\n};\n\ntype MutableTaskInput = {\n action?: string;\n model?: string;\n};\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction isString(value: unknown): value is string {\n return typeof value === 'string';\n}\n\nfunction parseModelIdentity(value: unknown): ModelIdentity | undefined {\n if (!isRecord(value) || !isString(value.provider) || !isString(value.id)) return undefined;\n if (!value.provider || !value.id || value.provider.length > 128 || value.id.length > 128) return undefined;\n return { provider: value.provider, id: value.id };\n}\n\nfunction parseThinkingLevel(value: unknown): PlanningThinkingLevel | undefined {\n return isString(value) && VALID_THINKING_LEVELS.includes(value as PlanningThinkingLevel)\n ? (value as PlanningThinkingLevel)\n : undefined;\n}\n\nfunction parseFableStage(value: unknown): FableStage | undefined {\n return isString(value) && VALID_FABLE_STAGES.includes(value as FableStage) ? (value as FableStage) : undefined;\n}\n\nfunction parseStringArray(value: unknown): string[] | undefined {\n if (!Array.isArray(value) || value.some((item) => !isString(item))) return undefined;\n return value.map((item) => item);\n}\n\nfunction parsePlanSnapshot(value: unknown): PlanSnapshot | undefined {\n if (!isRecord(value)) return undefined;\n const tools = parseStringArray(value.tools);\n const thinking = parseThinkingLevel(value.thinking);\n if (!tools || thinking === undefined) return undefined;\n const model = value.model === undefined ? undefined : parseModelIdentity(value.model);\n if (value.model !== undefined && !model) return undefined;\n return { tools, ...(model ? { model } : {}), thinking };\n}\n\nfunction parsePlanningFlavor(value: unknown): PlanningFlavor | undefined {\n return value === 'normal' || value === 'debug' || value === 'fable' ? value : undefined;\n}\n\nfunction parseVersionedPlanState(value: Record<string, unknown>): PersistedPlanModeState | undefined {\n if (value.version !== PLAN_STATE_VERSION) return undefined;\n const activeFlavor = value.activeFlavor === undefined ? undefined : parsePlanningFlavor(value.activeFlavor);\n if (value.activeFlavor !== undefined && !activeFlavor) return undefined;\n const originalSnapshot = value.originalSnapshot === undefined ? undefined : parsePlanSnapshot(value.originalSnapshot);\n if (value.originalSnapshot !== undefined && !originalSnapshot) return undefined;\n const planId = value.planId === undefined ? undefined : isString(value.planId) ? value.planId : undefined;\n if (value.planId !== undefined && planId === undefined) return undefined;\n const interruptedFableStage =\n value.interruptedFableStage === undefined ? undefined : parseFableStage(value.interruptedFableStage);\n if (value.interruptedFableStage !== undefined && !interruptedFableStage) return undefined;\n return {\n version: PLAN_STATE_VERSION,\n ...(activeFlavor ? { activeFlavor } : {}),\n ...(originalSnapshot ? { originalSnapshot } : {}),\n ...(planId ? { planId } : {}),\n ...(interruptedFableStage ? { interruptedFableStage } : {}),\n };\n}\n\nfunction parseLegacyPlanState(value: Record<string, unknown>): PersistedPlanModeState | undefined {\n if (value.enabled !== true && value.enabled !== false) return undefined;\n const tools = value.toolsBeforePlanMode === undefined ? undefined : parseStringArray(value.toolsBeforePlanMode);\n if (value.toolsBeforePlanMode !== undefined && !tools) return undefined;\n const model = value.modelBeforePlanMode === undefined ? undefined : parseModelIdentity(value.modelBeforePlanMode);\n if (value.modelBeforePlanMode !== undefined && !model) return undefined;\n const thinking =\n value.thinkingBeforePlanMode === undefined ? undefined : parseThinkingLevel(value.thinkingBeforePlanMode);\n if (value.thinkingBeforePlanMode !== undefined && thinking === undefined) return undefined;\n const planId = value.planId === undefined ? undefined : isString(value.planId) ? value.planId : undefined;\n if (value.planId !== undefined && planId === undefined) return undefined;\n const originalSnapshot =\n tools && thinking !== undefined ? { tools, ...(model ? { model } : {}), thinking } : undefined;\n return {\n version: PLAN_STATE_VERSION,\n ...(value.enabled ? { activeFlavor: 'normal' as const } : {}),\n ...(originalSnapshot ? { originalSnapshot } : {}),\n ...(planId ? { planId } : {}),\n };\n}\n\nexport function parsePersistedPlanState(value: unknown): PersistedPlanModeState | undefined {\n if (!isRecord(value)) return undefined;\n return parseVersionedPlanState(value) ?? parseLegacyPlanState(value);\n}\n\nfunction parsePlanDocument(value: unknown): PlanDocument | undefined {\n if (!isRecord(value)) return undefined;\n if (!isString(value.content) || !isString(value.path) || !isString(value.sessionId) || !isString(value.title)) {\n return undefined;\n }\n if (!isString(value.writtenAt)) return undefined;\n const planId = value.planId === undefined ? undefined : isString(value.planId) ? value.planId : undefined;\n if (value.planId !== undefined && planId === undefined) return undefined;\n return {\n content: value.content,\n path: value.path,\n sessionId: value.sessionId,\n ...(planId ? { planId } : {}),\n title: value.title,\n writtenAt: value.writtenAt,\n };\n}\n\nfunction boundedDebugText(value: unknown, field: string, required: boolean): string {\n if (!isString(value)) throw new Error(`${field} must be a string.`);\n const text = value.trim();\n if (required && !text) throw new Error(`${field} must not be empty.`);\n if (Buffer.byteLength(text, 'utf8') > DEBUG_EVIDENCE_MAX_TEXT_BYTES) {\n throw new Error(`${field} exceeds the bounded debug evidence limit.`);\n }\n return text;\n}\n\nfunction boundedDebugList(value: unknown, field: string): string[] {\n if (!Array.isArray(value) || value.length > DEBUG_EVIDENCE_MAX_ITEMS) {\n throw new Error(`${field} must be a bounded string array.`);\n }\n return value.map((item, index) => boundedDebugText(item, `${field}[${index}]`, false));\n}\n\nfunction optionalDebugText(value: unknown, field: string): string {\n return value === undefined ? '' : boundedDebugText(value, field, false);\n}\n\nfunction optionalDebugList(value: unknown, field: string): string[] {\n return value === undefined ? [] : boundedDebugList(value, field);\n}\n\nfunction assertExactObjectKeys(value: Record<string, unknown>, keys: readonly string[], field: string): void {\n const allowed = new Set(keys);\n const unexpected = Object.keys(value).find((key) => !allowed.has(key));\n if (unexpected) throw new Error(`${field} contains unsupported field '${unexpected}'.`);\n}\n\nexport function parseDebugEvidencePacket(value: unknown): DebugEvidencePacket {\n if (!isRecord(value)) throw new Error('Debug evidence must be an object.');\n assertExactObjectKeys(\n value,\n [\n 'issue',\n 'expectedBehavior',\n 'reproductionAttempt',\n 'actualBehavior',\n 'logs',\n 'correlatedTraceEvidence',\n 'processOutput',\n 'browserConsoleEvidence',\n 'correlationIds',\n 'timestamps',\n 'verifiedFacts',\n 'hypotheses',\n 'unavailableEvidence',\n ],\n 'Debug evidence',\n );\n const packet: DebugEvidencePacket = {\n issue: boundedDebugText(value.issue, 'issue', true),\n expectedBehavior: optionalDebugText(value.expectedBehavior, 'expectedBehavior'),\n reproductionAttempt: optionalDebugText(value.reproductionAttempt, 'reproductionAttempt'),\n actualBehavior: optionalDebugText(value.actualBehavior, 'actualBehavior'),\n logs: optionalDebugList(value.logs, 'logs'),\n correlatedTraceEvidence: optionalDebugList(value.correlatedTraceEvidence, 'correlatedTraceEvidence'),\n processOutput: optionalDebugList(value.processOutput, 'processOutput'),\n browserConsoleEvidence: optionalDebugList(value.browserConsoleEvidence, 'browserConsoleEvidence'),\n correlationIds: optionalDebugList(value.correlationIds, 'correlationIds'),\n timestamps: optionalDebugList(value.timestamps, 'timestamps'),\n verifiedFacts: optionalDebugList(value.verifiedFacts, 'verifiedFacts'),\n hypotheses: optionalDebugList(value.hypotheses, 'hypotheses'),\n unavailableEvidence: optionalDebugList(value.unavailableEvidence, 'unavailableEvidence'),\n };\n if (Buffer.byteLength(JSON.stringify(packet), 'utf8') > DEBUG_EVIDENCE_MAX_BYTES) {\n throw new Error('Debug evidence exceeds the bounded packet size.');\n }\n return packet;\n}\n\nexport function planModeTools(activeTools: string[], availableTools: string[]): string[] {\n const available = new Set(availableTools);\n const retained = activeTools.filter((name) => PLAN_MODE_PARENT_TOOLS.has(name));\n const exploration = PLAN_MODE_EXPLORATION_TOOLS.filter((name) => available.has(name));\n const planTools = [ASK_USER_TOOL, COMPLETE_PLAN_TOOL, TASK_TOOL, WRITE_PLAN_TOOL].filter((name) =>\n available.has(name),\n );\n return [...new Set([...retained, ...exploration, ...planTools])];\n}\n\nfunction planningToolsForFlavor(\n snapshotTools: string[],\n availableTools: string[],\n flavor: PlanningFlavor,\n diagnosticTools: ReadonlySet<string>,\n): string[] {\n const available = new Set(availableTools);\n const base = planModeTools(snapshotTools, availableTools);\n const extras =\n flavor === 'debug' ? [RECORD_DEBUG_EVIDENCE_TOOL, ...diagnosticTools].filter((name) => available.has(name)) : [];\n if (flavor === 'fable' && available.has(RUN_FABLE_PLAN_TOOL)) extras.push(RUN_FABLE_PLAN_TOOL);\n return [...new Set([...base, ...extras])];\n}\n\nfunction disableStepOutput(step: MutableSubagentStep): void {\n step.output = false;\n step.progress = false;\n step.worktree = false;\n if (Array.isArray(step.parallel)) {\n for (const child of step.parallel) disableStepOutput(child);\n } else if (step.parallel) {\n disableStepOutput(step.parallel);\n }\n}\n\nexport function constrainSubagentInput(input: MutableSubagentInput): void {\n if (input.action !== undefined) {\n if (isSubagentAction(input.action) && subagentActionAcceptsField(input.action, 'artifacts')) {\n input.artifacts = false;\n }\n return;\n }\n\n input.artifacts = false;\n input.output = false;\n input.share = false;\n input.progress = false;\n input.worktree = false;\n delete input.chainDir;\n delete input.outputSchema;\n delete input.sessionDir;\n for (const task of input.tasks ?? []) disableStepOutput(task);\n for (const step of input.chain ?? []) disableStepOutput(step);\n}\n\nexport function planningSubagentModel(\n config: PlanningAgentConfig | undefined,\n currentModel: ModelIdentity | undefined,\n): string | undefined {\n if (!config?.model && !config?.thinking) return undefined;\n const baseModel = config.model ?? (currentModel ? `${currentModel.provider}/${currentModel.id}` : undefined);\n if (!baseModel) return undefined;\n if (!config.thinking) return baseModel;\n return `${baseModel.replace(/:(?:off|minimal|low|medium|high|xhigh|max)$/, '')}:${config.thinking}`;\n}\n\nexport function configurePlanningSubagentInput(input: MutableSubagentInput, model: string): void {\n if (input.action !== 'run') return;\n for (const request of input.requests ?? []) request.model = model;\n}\n\nexport function configurePlanningTaskInput(input: MutableTaskInput, model: string): void {\n if (input.action === 'assign') input.model = model;\n}\n\nexport function isBlockedSubagentManagementAction(action: string | undefined): boolean {\n return new Set([\n 'append-step',\n 'create',\n 'delete',\n 'disable',\n 'eject',\n 'enable',\n 'grant-spawn-budget',\n 'reset',\n 'schedule',\n 'schedule-cancel',\n 'update',\n 'watchdog.configure',\n ]).has(action ?? '');\n}\n\nfunction resolvePlanningModel(\n modelSpec: string,\n ctx: ExtensionContext,\n): NonNullable<ExtensionContext['model']> | undefined {\n const separator = modelSpec.indexOf('/');\n if (separator > 0) {\n return ctx.modelRegistry.find(modelSpec.slice(0, separator), modelSpec.slice(separator + 1));\n }\n\n const matches = ctx.modelRegistry.getAvailable().filter((model) => model.id === modelSpec);\n return (\n matches.find((model) => model.provider === ctx.model?.provider) ?? (matches.length === 1 ? matches[0] : undefined)\n );\n}\n\nexport type PlanningConfigProvider = () => PlanningModeConfig | undefined;\n\n/** Reads Doom settings from disk so each plan-mode activation sees current configuration. */\nexport function loadPlanningModeConfig(\n environment: NodeJS.ProcessEnv = process.env,\n currentDirectory = process.cwd(),\n): PlanningModeConfig | undefined {\n return loadDoomConfig(loadHarnessState(environment).state.root ?? currentDirectory).modes?.planning;\n}\n\n/**\n * The plan menu as it stands with the mode on or off.\n *\n * `exit` is published only while the mode is on. It is the one entry that can\n * do nothing otherwise, and a menu offering a way out of a mode reads as\n * though you are in it.\n */\nfunction planLeaderBindings(active: boolean): DoomLeaderBinding[] {\n const group = { key: 'p', label: 'plan', order: PLAN_LEADER_GROUP_ORDER } as const;\n return [\n {\n id: 'plan.normal',\n path: [group, { key: 'p', label: 'normal', detail: 'read-only planning' }],\n action: { name: 'plan.normal' },\n },\n {\n id: 'plan.debug',\n path: [group, { key: 'd', label: 'debug', detail: 'adaptive debug planning' }],\n action: { name: 'plan.debug' },\n },\n {\n id: 'plan.fable',\n path: [group, { key: 'f', label: 'fable', detail: 'repository-aware draft' }],\n action: { name: 'plan.fable' },\n },\n ...(active\n ? [\n {\n id: 'plan.exit',\n path: [group, { key: 'e', label: 'exit', detail: 'restore and exit' }] as DoomLeaderBinding['path'],\n action: { name: 'plan.exit' },\n },\n ]\n : []),\n ];\n}\n\nexport function planModeExtension(\n pi: ExtensionAPI,\n planningConfigProvider: PlanningConfigProvider = loadPlanningModeConfig,\n telemetry: PlanTelemetry = createPlanTelemetry(),\n options: PlanModeExtensionOptions = {},\n): void {\n let enabled = false;\n let activeFlavor: PlanningFlavor | undefined;\n let planSnapshot: PlanSnapshot | undefined;\n let activePlanningConfig: PlanningModeConfig | undefined;\n let capabilityCeiling: SubagentPolicyHandle | undefined;\n let currentPlan: PlanDocument | undefined;\n let activePlanId: string | undefined;\n let planReadyForReview = false;\n let debugEvidence: DebugEvidencePacket | undefined;\n let fableStage: FableStage = 'idle';\n let interruptedFableStage: FableStage | undefined;\n let activeContext: ExtensionContext | undefined;\n let activeFableRun: Promise<FablePlanResult> | undefined;\n let transitionQueue: Promise<void> = Promise.resolve();\n const doomIntegrations = options.doomIntegrations ?? true;\n const diagnosticTools = new Set(options.debugDiagnosticTools ?? DEBUG_DIAGNOSTIC_TOOLS);\n const sessionBroker = createFablePlanBrokerClient(() => activeContext?.sessionManager.getSessionId());\n const fableBroker: FablePlanBroker = options.fableBroker ?? {\n start: (request, signal) => sessionBroker.start(request, signal),\n cancel: (operationId, reason) =>\n sessionBroker.cancel({ requester: CONTRACT_FABLE_PLAN_REQUESTER, operationId, reason }),\n };\n const isFableBrokerAvailable = (): boolean => options.fableBroker !== undefined || sessionBroker.isAvailable();\n const modeContribution = doomIntegrations\n ? registerDoomModeContribution(pi, {\n source: PLAN_LEADER_SOURCE,\n id: MODE_CONTRIBUTION_ID,\n order: MODE_CONTRIBUTION_ORDER,\n })\n : { update: () => undefined };\n const leaderContribution = doomIntegrations\n ? createDoomLeaderContribution(pi, {\n source: PLAN_LEADER_SOURCE,\n bindings: planLeaderBindings(false),\n })\n : { update: () => undefined };\n let planConfigNotice: string | undefined;\n const planConfigContribution = doomIntegrations\n ? registerDoomConfigContribution(pi, {\n source: PLAN_LEADER_SOURCE,\n // Read at publish time, not cached: plan mode re-reads this file on every\n // activation anyway, so the panel should show whatever is on disk now.\n listSections: () => {\n try {\n return planConfigSections(planningConfigProvider(), planConfigNotice);\n } catch (error) {\n // A malformed config file must not take session start down with it. The\n // panel is where someone would go to repair it, so it has to still draw.\n return planConfigSections(undefined, error instanceof Error ? error.message : String(error));\n }\n },\n handlers: {\n [CONFIG_ACTION.set]: async ({ fieldId, value }) => {\n const setting = planSettingByFieldId(fieldId);\n if (!setting || !value) return;\n planConfigNotice = undefined;\n await setDoomConfigValue(globalDoomConfigPath(), [...setting.keyPath], value);\n },\n [CONFIG_ACTION.clear]: async ({ fieldId }) => {\n const setting = planSettingByFieldId(fieldId);\n if (!setting) return;\n planConfigNotice = undefined;\n await unsetDoomConfigValue(globalDoomConfigPath(), [...setting.keyPath]);\n },\n },\n onError: (error) => {\n planConfigNotice = error instanceof Error ? error.message : String(error);\n },\n })\n : { dispose: () => undefined, update: () => undefined };\n\n /** Identical bindings are dropped by the handle, so this can ride along with every state change. */\n function updateLeader(): void {\n leaderContribution.update(planLeaderBindings(enabled));\n }\n\n function updateStatus(ctx: ExtensionContext): void {\n updateLeader();\n const flavor = activeFlavor;\n const status = enabled && flavor ? `plan:${flavor}` : undefined;\n const stage = flavor === 'fable' && fableStage !== 'idle' ? ` · ${fableStage}` : '';\n ctx.ui.setStatus(PLAN_MODE_STATUS_KEY, status ? ctx.ui.theme.fg(WARNING_STYLE, status) : undefined);\n // The flavour, the fable stage and the read-only warning are all sub-state\n // of one mode, so they ride in the detail rather than each claiming a slot\n // on a line shared with every other mode: `PLAN (normal - read only)`.\n modeContribution.update(\n enabled && flavor\n ? {\n label: MODE_LABEL,\n detail: `${flavor}${stage} - ${READ_ONLY_DETAIL}`,\n color: WARNING_STYLE,\n }\n : undefined,\n );\n }\n\n function updateCapabilityCeiling(): void {\n if (!doomIntegrations || !enabled || !activeFlavor || !activeContext) return;\n const allowedTools = [...CHILD_EXPLORATION_TOOLS];\n const allowedExternalProfiles = activeFlavor === 'fable' ? [FABLE_PLAN_PROFILE] : [];\n const policy = {\n owner: PLAN_LEADER_SOURCE,\n allowedTools,\n requiredTools: [BASH_TOOL, MCP_TOOL],\n allowMcpTools: true,\n allowedExternalProfiles,\n denyExtensions: false,\n };\n if (capabilityCeiling) capabilityCeiling.update(policy);\n else {\n capabilityCeiling = registerSubagentPolicy(\n createProtocolRuntime({\n bus: pi.events,\n source: PLAN_LEADER_SOURCE,\n sessionId: activeContext.sessionManager.getSessionId(),\n }),\n policy,\n );\n }\n }\n\n function persistState(): void {\n pi.appendEntry(PLAN_MODE_ENTRY, {\n version: PLAN_STATE_VERSION,\n ...(enabled && activeFlavor ? { activeFlavor } : {}),\n ...(planSnapshot\n ? {\n originalSnapshot: {\n tools: [...planSnapshot.tools],\n ...(planSnapshot.model ? { model: { ...planSnapshot.model } } : {}),\n thinking: planSnapshot.thinking,\n },\n }\n : {}),\n ...(activePlanId ? { planId: activePlanId } : {}),\n ...(interruptedFableStage ? { interruptedFableStage } : {}),\n } satisfies PersistedPlanModeState);\n }\n\n function reportModelFailure(\n phase: 'apply' | 'restore',\n model: string,\n reason: typeof MODEL_NOT_FOUND_REASON | typeof MODEL_UNAUTHENTICATED_REASON | typeof MODEL_RESTORE_FAILED_REASON,\n ): void {\n void telemetry.recordWarning(PLAN_EVENT.modelResolveFailed, `Planning model ${reason}: ${model}`, {\n [PLAN_MODEL_ATTRIBUTE]: model,\n 'plan.model.phase': phase,\n 'plan.model.reason': reason,\n });\n }\n\n async function applyMainPlanningConfig(ctx: ExtensionContext): Promise<void> {\n const config = activePlanningConfig?.main;\n if (!config) return;\n if (config.model) {\n const model = resolvePlanningModel(config.model, ctx);\n if (!model) {\n ctx.ui.notify(`Planning model not found: ${config.model}`, WARNING_STYLE);\n reportModelFailure('apply', config.model, MODEL_NOT_FOUND_REASON);\n } else if (!(await pi.setModel(model))) {\n ctx.ui.notify(`Planning model is unavailable without authentication: ${config.model}`, WARNING_STYLE);\n reportModelFailure('apply', config.model, MODEL_UNAUTHENTICATED_REASON);\n }\n }\n if (config.thinking) pi.setThinkingLevel(config.thinking);\n }\n\n async function restoreMainAgent(ctx: ExtensionContext, snapshot: PlanSnapshot | undefined): Promise<boolean> {\n if (!snapshot) return false;\n if (snapshot.model) {\n const identity = `${snapshot.model.provider}/${snapshot.model.id}`;\n const model = ctx.modelRegistry.find(snapshot.model.provider, snapshot.model.id);\n if (!model) {\n ctx.ui.notify(`Previous model not found: ${identity}`, WARNING_STYLE);\n reportModelFailure('restore', identity, MODEL_NOT_FOUND_REASON);\n return false;\n }\n try {\n if (!(await pi.setModel(model))) {\n ctx.ui.notify(`Previous model is unavailable without authentication: ${identity}`, WARNING_STYLE);\n reportModelFailure('restore', identity, MODEL_UNAUTHENTICATED_REASON);\n return false;\n }\n } catch (error) {\n ctx.ui.notify(`Previous model could not be restored: ${identity}`, WARNING_STYLE);\n reportModelFailure('restore', identity, MODEL_RESTORE_FAILED_REASON);\n void telemetry.recordError(PLAN_EVENT.modelResolveFailed, error, {\n [PLAN_MODEL_ATTRIBUTE]: identity,\n 'plan.model.phase': 'restore',\n 'plan.model.reason': MODEL_RESTORE_FAILED_REASON,\n });\n return false;\n }\n }\n pi.setThinkingLevel(snapshot.thinking);\n return true;\n }\n\n function reportCurrentFlavor(ctx: ExtensionContext): void {\n if (enabled && activeFlavor) ctx.ui.notify(`Already in plan:${activeFlavor}.`, INFO_STYLE);\n else ctx.ui.notify('Plan mode is inactive.', INFO_STYLE);\n }\n\n function queueTransition<T>(transition: () => Promise<T>): Promise<T> {\n const queued = transitionQueue.then(transition, transition);\n transitionQueue = queued.then(\n () => undefined,\n () => undefined,\n );\n return queued;\n }\n\n async function cancelFableOperation(reason: string): Promise<void> {\n if (!activeFableRun && !fableStage.match(/^(draft|review)$/u)) return;\n if (fableStage === 'draft' || fableStage === 'review') interruptedFableStage = fableStage;\n fableFlow.cancel(reason);\n const pending = activeFableRun;\n if (pending) {\n try {\n await pending;\n } catch (error) {\n void telemetry.recordError(PLAN_EVENT.modeDisabled, error, {\n [PLAN_FLAVOR_ATTRIBUTE]: 'fable',\n 'plan.fable.reason': 'cancel_failed',\n });\n }\n }\n activeFableRun = undefined;\n if (interruptedFableStage) fableStage = 'interrupted';\n if (activeContext) updateStatus(activeContext);\n persistState();\n }\n\n async function activateFlavor(\n ctx: ExtensionContext,\n flavor: PlanningFlavor,\n trigger: PlanModeTrigger,\n restoring: boolean,\n ): Promise<void> {\n activeContext = ctx;\n if (enabled) {\n if (activeFlavor === flavor) {\n reportCurrentFlavor(ctx);\n return;\n }\n if (activeFlavor === 'fable') await cancelFableOperation(`Leaving Fable for plan:${flavor}.`);\n activeFlavor = flavor;\n updateCapabilityCeiling();\n pi.setActiveTools(\n planningToolsForFlavor(\n planSnapshot?.tools ?? [],\n pi.getAllTools().map((tool) => tool.name),\n flavor,\n diagnosticTools,\n ),\n );\n updateStatus(ctx);\n persistState();\n void telemetry.recordEvent(PLAN_EVENT.modeEnabled, {\n [PLAN_TRIGGER_ATTRIBUTE]: trigger,\n [PLAN_FLAVOR_ATTRIBUTE]: flavor,\n 'plan.tool.count': pi.getActiveTools().length,\n });\n return;\n }\n\n let nextPlanningConfig: PlanningModeConfig | undefined;\n try {\n nextPlanningConfig = planningConfigProvider();\n } catch (error) {\n void telemetry.recordError(PLAN_EVENT.configLoadFailed, error, { [PLAN_TRIGGER_ATTRIBUTE]: trigger });\n throw error;\n }\n\n if (!restoring) {\n currentPlan = undefined;\n planReadyForReview = false;\n activePlanId = createPlanIdentifier();\n interruptedFableStage = undefined;\n fableStage = 'idle';\n } else {\n activePlanId ??= createPlanIdentifier();\n }\n activePlanningConfig = nextPlanningConfig;\n planSnapshot ??= {\n tools: [...pi.getActiveTools()],\n ...(ctx.model ? { model: { provider: ctx.model.provider, id: ctx.model.id } } : {}),\n thinking: pi.getThinkingLevel(),\n };\n enabled = true;\n activeFlavor = flavor;\n pi.setActiveTools(\n planningToolsForFlavor(\n planSnapshot.tools,\n pi.getAllTools().map((tool) => tool.name),\n flavor,\n diagnosticTools,\n ),\n );\n updateCapabilityCeiling();\n await applyMainPlanningConfig(ctx);\n updateStatus(ctx);\n persistState();\n void telemetry.recordEvent(PLAN_EVENT.modeEnabled, {\n [PLAN_TRIGGER_ATTRIBUTE]: trigger,\n [PLAN_FLAVOR_ATTRIBUTE]: flavor,\n 'plan.tool.count': pi.getActiveTools().length,\n ...(activePlanningConfig?.main?.model ? { [PLAN_MODEL_ATTRIBUTE]: activePlanningConfig.main.model } : {}),\n ...(activePlanningConfig?.main?.thinking ? { 'plan.thinking': activePlanningConfig.main.thinking } : {}),\n });\n }\n\n async function exitPlanMode(ctx: ExtensionContext, trigger: PlanModeTrigger): Promise<boolean> {\n if (!enabled) {\n reportCurrentFlavor(ctx);\n return false;\n }\n await cancelFableOperation('Plan mode is exiting.');\n const snapshot = planSnapshot;\n const restored = await restoreMainAgent(ctx, snapshot);\n if (!restored) {\n ctx.ui.notify('Previous model was not restored. Plan mode remains read-only.', WARNING_STYLE);\n updateStatus(ctx);\n persistState();\n void telemetry.recordWarning(PLAN_EVENT.modeDisabled, 'Plan mode restoration failed.', {\n [PLAN_TRIGGER_ATTRIBUTE]: trigger,\n [PLAN_FLAVOR_ATTRIBUTE]: activeFlavor ?? 'unknown',\n 'plan.restored': false,\n });\n return false;\n }\n\n const hadPlan = Boolean(currentPlan);\n pi.setActiveTools([...(snapshot?.tools ?? [])]);\n capabilityCeiling?.dispose();\n capabilityCeiling = undefined;\n enabled = false;\n activeFlavor = undefined;\n activePlanningConfig = undefined;\n planSnapshot = undefined;\n debugEvidence = undefined;\n fableStage = 'idle';\n interruptedFableStage = undefined;\n updateStatus(ctx);\n persistState();\n void telemetry.recordEvent(PLAN_EVENT.modeDisabled, {\n [PLAN_TRIGGER_ATTRIBUTE]: trigger,\n [PLAN_FLAVOR_ATTRIBUTE]: 'normal',\n 'plan.written': hadPlan,\n 'plan.restored': true,\n });\n return true;\n }\n\n const fableFlow = createFablePlanFlow({\n broker: fableBroker,\n isAuthorized: () => enabled && activeFlavor === 'fable' && isFableBrokerAvailable(),\n onStage: (stage) => {\n fableStage = stage;\n if (stage === 'completed' || stage === 'failed' || stage === 'cancelled') interruptedFableStage = undefined;\n if (activeContext) updateStatus(activeContext);\n if (enabled) persistState();\n },\n onError: (error) => {\n void telemetry.recordError(PLAN_EVENT.modeEnabled, error, {\n [PLAN_FLAVOR_ATTRIBUTE]: 'fable',\n 'plan.fable.reason': 'flow_error',\n });\n },\n });\n\n pi.registerTool({\n name: RECORD_DEBUG_EVIDENCE_TOOL,\n label: 'Record Debug Evidence',\n description: 'Record optional bounded debug evidence while planning a repository fix.',\n promptSnippet: 'Record the issue and any relevant reproduction, logs, traces, process output, or browser evidence',\n parameters: {\n type: 'object',\n properties: {\n issue: { type: 'string' },\n expectedBehavior: { type: 'string' },\n reproductionAttempt: { type: 'string' },\n actualBehavior: { type: 'string' },\n logs: { type: 'array', items: { type: 'string' } },\n correlatedTraceEvidence: { type: 'array', items: { type: 'string' } },\n processOutput: { type: 'array', items: { type: 'string' } },\n browserConsoleEvidence: { type: 'array', items: { type: 'string' } },\n correlationIds: { type: 'array', items: { type: 'string' } },\n timestamps: { type: 'array', items: { type: 'string' } },\n verifiedFacts: { type: 'array', items: { type: 'string' } },\n hypotheses: { type: 'array', items: { type: 'string' } },\n unavailableEvidence: { type: 'array', items: { type: 'string' } },\n },\n required: ['issue'],\n additionalProperties: false,\n },\n async execute(_toolCallId, params) {\n if (!enabled || activeFlavor !== 'debug') {\n return { content: [{ type: 'text', text: 'Debug planning is not active.' }], details: { recorded: false } };\n }\n try {\n debugEvidence = parseDebugEvidencePacket(params);\n } catch (error) {\n void telemetry.recordWarning(PLAN_EVENT.modeEnabled, error, {\n [PLAN_FLAVOR_ATTRIBUTE]: 'debug',\n 'plan.debug.reason': 'invalid_evidence',\n });\n throw error;\n }\n if (activeContext) updateStatus(activeContext);\n return {\n content: [{ type: 'text', text: 'Debug evidence recorded as optional planning context.' }],\n details: { recorded: true },\n };\n },\n });\n\n pi.registerTool({\n name: RUN_FABLE_PLAN_TOOL,\n label: 'Run Fable Plan',\n description: 'Send a bounded, sanitized planning packet to the local Fable broker for one repository-aware draft.',\n promptSnippet: 'Run the local Fable draft with the bounded planning packet',\n parameters: {\n type: 'object',\n properties: {\n goal: { type: 'array', items: { type: 'string' } },\n constraints: { type: 'array', items: { type: 'string' } },\n decisions: { type: 'array', items: { type: 'string' } },\n verifiedFindings: { type: 'array', items: { type: 'object' } },\n inferredFindings: { type: 'array', items: { type: 'string' } },\n unresolvedQuestions: { type: 'array', items: { type: 'string' } },\n currentPlan: { type: 'string' },\n },\n additionalProperties: false,\n },\n async execute(_toolCallId, params, signal) {\n if (!enabled || activeFlavor !== 'fable') {\n return {\n content: [{ type: 'text', text: 'Fable planning is not active.' }],\n details: { started: false } as Record<string, unknown>,\n };\n }\n if (!isFableBrokerAvailable()) {\n return {\n content: [{ type: 'text', text: 'The local Fable broker is unavailable. Fable planning remains disabled.' }],\n details: { started: false, errorCode: 'broker_unavailable' } as Record<string, unknown>,\n };\n }\n const operation = fableFlow.run(params, signal);\n activeFableRun = operation;\n try {\n const result = await operation;\n const text = result.draft\n ? `Fable draft:\\n${result.draft}`\n : `Fable planning ${result.status}: ${result.errorCode ?? 'no output'}.`;\n return {\n content: [{ type: 'text', text }],\n details: { ...result, started: true } as Record<string, unknown>,\n };\n } finally {\n if (activeFableRun === operation) activeFableRun = undefined;\n }\n },\n });\n\n pi.registerTool({\n name: WRITE_PLAN_TOOL,\n label: 'Write Plan',\n description:\n 'After presenting the complete implementation plan as Markdown in chat, save that visible plan to a unique Markdown file in the configured plans directory.',\n promptSnippet: 'Save the implementation plan already presented in chat to the configured plans directory',\n parameters: { type: 'object', properties: {}, additionalProperties: false },\n async execute(toolCallId, _params, signal, onUpdate, ctx) {\n if (!enabled) {\n return {\n content: [{ type: 'text', text: 'Plan mode is disabled. Use normal file tools instead.' }],\n details: { written: false },\n };\n }\n const content = visiblePlanForToolCall(ctx.sessionManager.getBranch(), toolCallId);\n if (!content) {\n const error = new Error(\n 'Present the complete implementation plan as visible Markdown in chat before calling write_plan.',\n );\n void telemetry.recordError(PLAN_EVENT.writePlanFailed, error, { 'plan.reason': 'missing_visible_plan' });\n throw error;\n }\n\n const startedAt = Date.now();\n const repoRoot = getHarnessState().root ?? ctx.cwd;\n const plansDirectory = resolvePlanningPlansDirectory(\n activePlanningConfig?.plansDirectory,\n repoRoot,\n os.homedir(),\n );\n const sessionId = planSessionIdentifier(ctx.sessionManager.getSessionId());\n activePlanId ??= createPlanIdentifier();\n const storedPlanPath =\n currentPlan?.planId === activePlanId && path.dirname(currentPlan.path) === plansDirectory\n ? currentPlan.path\n : undefined;\n const title = planTitleSlug(content);\n let planId = activePlanId;\n let planPath = storedPlanPath ?? path.join(plansDirectory, `${title}--${planId}.md`);\n const timeoutController = new AbortController();\n const timeoutError = new Error(`Writing ${planPath} timed out after ${WRITE_PLAN_TIMEOUT_MS}ms.`);\n const timeout = setTimeout(() => timeoutController.abort(timeoutError), WRITE_PLAN_TIMEOUT_MS);\n const writeSignal = signal ? AbortSignal.any([signal, timeoutController.signal]) : timeoutController.signal;\n let phase: PlanWritePhase = 'checking';\n const reportPhase = (): void => {\n onUpdate?.({\n content: [{ type: 'text', text: `${phase === 'checking' ? 'Checking' : 'Writing'} ${planPath}...` }],\n details: { phase, path: planPath, startedAt },\n });\n };\n const recordRefusal = (refusal: string, message: string): void => {\n void telemetry.recordWarning(PLAN_EVENT.writePlanUnsafePath, message, {\n 'plan.phase': phase,\n 'plan.refusal': refusal,\n });\n };\n\n try {\n reportPhase();\n try {\n await waitForAbortable(\n () => fs.promises.mkdir(plansDirectory, { recursive: true, mode: PRIVATE_DIRECTORY_MODE }),\n writeSignal,\n );\n } catch (error) {\n if (writeSignal.aborted) throw abortReason(writeSignal);\n throw new Error(`Could not create the configured plans directory ${plansDirectory}.`, { cause: error });\n }\n const directoryStats = await waitForAbortable(() => fs.promises.lstat(plansDirectory), writeSignal);\n const resolvedPlansDirectory = await waitForAbortable(() => fs.promises.realpath(plansDirectory), writeSignal);\n if (directoryStats.isSymbolicLink() || !directoryStats.isDirectory()) {\n recordRefusal('unsafe_plans_directory', 'Refused to write the plan because the configured path is unsafe.');\n return {\n content: [{ type: 'text', text: 'Refused to write the plan because the configured path is unsafe.' }],\n details: { written: false, path: planPath, phase, durationMs: Date.now() - startedAt },\n };\n }\n\n phase = 'writing';\n reportPhase();\n let planFile: Awaited<ReturnType<typeof fs.promises.open>> | undefined;\n try {\n for (let attempt = 0; attempt < PLAN_ALLOCATION_ATTEMPTS; attempt += 1) {\n try {\n const createFlags = storedPlanPath ? 0 : fs.constants.O_EXCL;\n planFile = await waitForAbortable(\n () =>\n fs.promises.open(\n planPath,\n fs.constants.O_CREAT |\n fs.constants.O_WRONLY |\n fs.constants.O_NONBLOCK |\n fs.constants.O_NOFOLLOW |\n createFlags,\n PRIVATE_FILE_MODE,\n ),\n writeSignal,\n );\n break;\n } catch (error) {\n const collision =\n !storedPlanPath && error instanceof Error && 'code' in error && String(error.code) === 'EEXIST';\n if (!collision || attempt === PLAN_ALLOCATION_ATTEMPTS - 1) throw error;\n planId = createPlanIdentifier();\n activePlanId = planId;\n planPath = path.join(plansDirectory, `${title}--${planId}.md`);\n persistState();\n }\n }\n if (!planFile) throw new Error(`Could not allocate a unique plan filename in ${plansDirectory}.`);\n const resolvedAfterOpen = await waitForAbortable(() => fs.promises.realpath(plansDirectory), writeSignal);\n const stats = await waitForAbortable(() => planFile!.stat(), writeSignal);\n if (resolvedAfterOpen !== resolvedPlansDirectory || !stats.isFile() || stats.nlink !== 1) {\n recordRefusal('invalid_destination', INVALID_PLAN_DESTINATION);\n return {\n content: [{ type: 'text', text: INVALID_PLAN_DESTINATION }],\n details: { written: false, path: planPath, phase, durationMs: Date.now() - startedAt },\n };\n }\n await waitForAbortable(() => planFile!.truncate(0), writeSignal);\n await waitForAbortable(\n () => planFile!.writeFile(content, { encoding: 'utf8', signal: writeSignal }),\n writeSignal,\n );\n } catch (error) {\n if (error instanceof Error && 'code' in error && ['EISDIR', 'ELOOP', 'ENXIO'].includes(String(error.code))) {\n recordRefusal('invalid_destination', INVALID_PLAN_DESTINATION);\n return {\n content: [{ type: 'text', text: INVALID_PLAN_DESTINATION }],\n details: { written: false, path: planPath, phase, durationMs: Date.now() - startedAt },\n };\n }\n throw error;\n } finally {\n await planFile?.close();\n }\n currentPlan = { content, path: planPath, sessionId, planId, title, writtenAt: new Date().toISOString() };\n pi.appendEntry(PLAN_DOCUMENT_ENTRY, currentPlan);\n planReadyForReview = true;\n void telemetry.recordEvent(PLAN_EVENT.planWritten, {\n 'plan.written': true,\n 'plan.bytes': Buffer.byteLength(content, 'utf8'),\n 'plan.duration_ms': Date.now() - startedAt,\n 'plan.revised': Boolean(storedPlanPath),\n });\n return {\n content: [{ type: 'text', text: `Wrote implementation plan to ${planPath}.` }],\n details: { written: true, path: planPath, phase, durationMs: Date.now() - startedAt },\n };\n } catch (error) {\n const attributes = {\n 'plan.phase': phase,\n 'plan.duration_ms': Date.now() - startedAt,\n };\n if (timeoutController.signal.aborted && !signal?.aborted) {\n const timedOut = new Error(`write_plan timed out during ${phase} for ${planPath}.`, { cause: error });\n void telemetry.recordError(PLAN_EVENT.writePlanTimedOut, timedOut, {\n ...attributes,\n 'plan.timeout_ms': WRITE_PLAN_TIMEOUT_MS,\n });\n throw timedOut;\n }\n void telemetry.recordError(PLAN_EVENT.writePlanFailed, error, {\n ...attributes,\n 'plan.cancelled': signal?.aborted ?? false,\n });\n throw error;\n } finally {\n clearTimeout(timeout);\n }\n },\n });\n\n pi.registerTool({\n name: COMPLETE_PLAN_TOOL,\n label: 'Complete Plan',\n description:\n 'Use only after presenting a concrete implementation plan. Ask the user whether to exit plan mode and begin implementation or continue planning.',\n promptSnippet: 'Ask the user whether to exit plan mode after the implementation plan is complete',\n parameters: { type: 'object', properties: {}, additionalProperties: false },\n async execute(_toolCallId, _params, _signal, _onUpdate, ctx) {\n if (!enabled) {\n return {\n content: [{ type: 'text', text: 'Plan mode is already disabled.' }],\n details: { exited: false },\n };\n }\n if (!planReadyForReview) {\n return {\n content: [\n {\n type: 'text',\n text: 'Present the complete plan in chat and save it with write_plan before requesting approval.',\n },\n ],\n details: { exited: false },\n };\n }\n if (!ctx.hasUI) {\n void telemetry.recordEvent(PLAN_EVENT.planReviewCompleted, { 'plan.outcome': 'no_ui' });\n return {\n content: [\n {\n type: 'text',\n text: 'The plan is complete, but this run cannot ask the user interactively. Remain in plan mode until the user uses SPC p e.',\n },\n ],\n details: { exited: false },\n };\n }\n\n const choice = await ctx.ui.select('Plan complete. What would you like to do?', [\n EXIT_PLAN_MODE_CHOICE,\n CONTINUE_PLANNING_CHOICE,\n ]);\n void telemetry.recordEvent(PLAN_EVENT.planReviewCompleted, {\n 'plan.outcome': choice === EXIT_PLAN_MODE_CHOICE ? 'exited' : 'continued',\n });\n if (choice === EXIT_PLAN_MODE_CHOICE) {\n const exited = await queueTransition(() => exitPlanMode(ctx, PLAN_MODE_TRIGGER_PLAN_APPROVED));\n return {\n content: [\n {\n type: 'text',\n text: exited\n ? 'The user approved exiting plan mode. Full tool access is restored. Begin implementing the approved plan.'\n : 'The previous model could not be restored. Plan mode remains read-only.',\n },\n ],\n details: { exited },\n };\n }\n\n planReadyForReview = false;\n return {\n content: [{ type: 'text', text: 'The user chose to continue planning. Remain read-only and refine the plan.' }],\n details: { exited: false },\n };\n },\n });\n\n if (doomIntegrations) {\n registerDoomLeaderActionHandlers(pi, {\n source: PLAN_LEADER_SOURCE,\n handlers: {\n 'plan.normal': () => {\n const ctx = activeContext;\n return ctx\n ? queueTransition(() => activateFlavor(ctx, 'normal', PLAN_MODE_TRIGGER_LEADER, false))\n : undefined;\n },\n 'plan.exit': () => {\n const ctx = activeContext;\n return ctx\n ? queueTransition(async () => {\n await exitPlanMode(ctx, PLAN_MODE_TRIGGER_LEADER);\n })\n : undefined;\n },\n 'plan.debug': () => {\n const ctx = activeContext;\n return ctx ? queueTransition(() => activateFlavor(ctx, 'debug', PLAN_MODE_TRIGGER_LEADER, false)) : undefined;\n },\n 'plan.fable': () => {\n const ctx = activeContext;\n return ctx ? queueTransition(() => activateFlavor(ctx, 'fable', PLAN_MODE_TRIGGER_LEADER, false)) : undefined;\n },\n },\n onError: (error, actionName) => {\n if (activeContext?.hasUI) {\n activeContext.ui.notify(`Leader action ${actionName} failed. Plan mode remains read-only.`, WARNING_STYLE);\n }\n void telemetry.recordError(PLAN_EVENT.modeEnabled, error, {\n [PLAN_TRIGGER_ATTRIBUTE]: PLAN_MODE_TRIGGER_LEADER,\n 'plan.action': actionName,\n });\n },\n });\n }\n\n pi.on('tool_call', async (event: ToolCallEvent, ctx) => {\n if (!enabled) return undefined;\n const isFlavorTool =\n (activeFlavor === 'debug' &&\n (event.toolName === RECORD_DEBUG_EVIDENCE_TOOL || diagnosticTools.has(event.toolName))) ||\n (activeFlavor === 'fable' && event.toolName === RUN_FABLE_PLAN_TOOL);\n if (!PLAN_MODE_PARENT_TOOLS.has(event.toolName) && !isFlavorTool) {\n return { block: true, reason: `Plan mode blocks the ${event.toolName} tool. Use SPC p e to restore access.` };\n }\n const model = planningSubagentModel(activePlanningConfig?.subagents, ctx.model);\n if (event.toolName === TASK_TOOL) {\n if (model) configurePlanningTaskInput(event.input as MutableTaskInput, model);\n return undefined;\n }\n if (event.toolName !== SUBAGENT_TOOL) return undefined;\n\n const input = event.input as MutableSubagentInput;\n if (isBlockedSubagentManagementAction(input.action)) {\n return {\n block: true,\n reason: `Plan mode blocks subagent management action '${input.action}'. Use SPC p e first.`,\n };\n }\n constrainSubagentInput(input);\n if (model) configurePlanningSubagentInput(input, model);\n return undefined;\n });\n\n pi.on('before_agent_start', async (event, ctx) => {\n const sections: string[] = [];\n if (enabled) {\n const repoRoot = getHarnessState().root ?? ctx.cwd;\n const plansDirectory = resolvePlanningPlansDirectory(\n activePlanningConfig?.plansDirectory,\n repoRoot,\n os.homedir(),\n );\n sections.push(\n `[PLAN MODE ACTIVE]\\nYou are in repository read-only plan mode. The dedicated write_plan tool may write one unique Markdown plan file under ${plansDirectory}.\\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 the turn and wait for completion notifications.\\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.`,\n buildFlavorPlanningPrompt(activeFlavor!, plansDirectory, debugEvidence, fableStage),\n );\n }\n if (currentPlan) {\n sections.push(`[CURRENT PLAN]\\nSource: ${currentPlan.path}\\n\\n${currentPlan.content}`);\n }\n if (sections.length === 0) return undefined;\n return { systemPrompt: `${event.systemPrompt}\\n\\n${sections.join('\\n\\n')}` };\n });\n\n pi.on('context', async (event) => ({\n messages: event.messages.filter((message) => {\n const custom = message as { customType?: string };\n return custom.customType !== PLAN_MODE_CONTEXT;\n }),\n }));\n\n pi.on('session_start', async (_event, ctx) => {\n const entries = ctx.sessionManager.getEntries();\n const state = entries\n .flatMap((entry) =>\n entry.type === 'custom' && entry.customType === PLAN_MODE_ENTRY ? [parsePersistedPlanState(entry.data)] : [],\n )\n .filter((value): value is PersistedPlanModeState => value !== undefined)\n .at(-1);\n const sessionId = planSessionIdentifier(ctx.sessionManager.getSessionId());\n const storedPlan = entries\n .flatMap((entry) =>\n entry.type === 'custom' && entry.customType === PLAN_DOCUMENT_ENTRY ? [parsePlanDocument(entry.data)] : [],\n )\n .filter((value): value is PlanDocument => value !== undefined)\n .at(-1);\n\n const snapshotFromPreviousSession = planSnapshot;\n capabilityCeiling?.dispose();\n capabilityCeiling = undefined;\n currentPlan = storedPlan?.sessionId === sessionId ? storedPlan : undefined;\n activePlanId = state?.planId ?? currentPlan?.planId;\n planReadyForReview = false;\n activePlanningConfig = undefined;\n debugEvidence = undefined;\n enabled = false;\n activeFlavor = undefined;\n // A session that restores nothing never reaches updateStatus, and the menu\n // would otherwise still offer the exit the previous session left on it.\n updateLeader();\n planSnapshot = state?.originalSnapshot;\n interruptedFableStage = state?.interruptedFableStage;\n fableStage = interruptedFableStage ? 'interrupted' : 'idle';\n activeContext = ctx;\n if (state?.activeFlavor) {\n await activateFlavor(ctx, state.activeFlavor, PLAN_MODE_TRIGGER_SESSION_RESTORE, true);\n } else {\n const snapshotToRestore = state?.originalSnapshot ?? snapshotFromPreviousSession;\n if (snapshotToRestore) {\n const restored = await restoreMainAgent(ctx, snapshotToRestore);\n if (restored) pi.setActiveTools([...snapshotToRestore.tools]);\n } else {\n pi.setActiveTools(\n pi\n .getActiveTools()\n .filter(\n (name) =>\n name !== COMPLETE_PLAN_TOOL &&\n name !== WRITE_PLAN_TOOL &&\n name !== RECORD_DEBUG_EVIDENCE_TOOL &&\n name !== RUN_FABLE_PLAN_TOOL,\n ),\n );\n }\n planSnapshot = undefined;\n updateStatus(ctx);\n }\n });\n\n pi.on('session_shutdown', async (_event, ctx) => {\n await cancelFableOperation('Plan host session is shutting down.');\n if (enabled && planSnapshot) {\n const restored = await restoreMainAgent(ctx, planSnapshot);\n if (restored) pi.setActiveTools([...planSnapshot.tools]);\n }\n capabilityCeiling?.dispose();\n capabilityCeiling = undefined;\n planConfigContribution.dispose();\n activeContext = undefined;\n await telemetry.shutdown();\n });\n}\n"],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ const e=[`issue`,`expectedBehavior`,`reproductionAttempt`,`actualBehavior`,`logs`,`correlatedTraceEvidence`,`processOutput`,`browserConsoleEvidence`,`correlationIds`,`timestamps`,`verifiedFacts`,`hypotheses`,`unavailableEvidence`];function t(t){return e.flatMap(e=>{let n=t[e];return Array.isArray(n)&&n.length===0||typeof n==`string`&&n.length===0?[]:`${e}: ${Array.isArray(n)?n.join(` | `):n}`}).join(`
2
+ `)}function n(e){return`[PLAN MODE ACTIVE: NORMAL]\nYou are in repository read-only normal planning. The dedicated write_plan tool may write one unique Markdown plan file under ${e}.\n\nExplore the codebase and produce a concrete implementation plan. For every plan, first call subagent with action "agents", cluster 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 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 the turn and wait for completion notifications.\n\nAfter all planning drafts complete, compare the candidates, pick the strongest draft, cross-check it against 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, 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.`}function r(e,n){return`[PLAN MODE ACTIVE: DEBUG]\nYou are in repository read-only adaptive debug planning. The dedicated write_plan tool may write one unique Markdown plan file under ${e}.\n\n${n?`A bounded debug evidence packet is recorded below. Cite its verified facts when making source findings. Keep verified facts and hypotheses distinct. Do not claim a verified root cause when reproduction or supporting evidence is unavailable.\n\n[DEBUG EVIDENCE]\n${t(n)}`:`No structured debug evidence packet is recorded yet. First understand the reported issue, then gather only the evidence relevant to it. Recording evidence is optional and must not block source exploration, delegation, writing a plan, or completing a plan.`}\n\nUse the exact read-only tools available for the issue. Bash is available for repository inspection, but commands must not modify files or repository state. Treat unavailable evidence as unavailable, never invent its contents, and keep verified facts separate from hypotheses. ${n?`The final plan must separate verified facts from hypotheses and must not present an unsupported root cause as verified.`:`You may call record_debug_evidence when structured notes would help, but continue investigating without it when it is not relevant.`}`}function i(e,t){return`[PLAN MODE ACTIVE: FABLE]\nYou are in repository read-only Fable planning. Pi owns final verification and synthesis. The dedicated write_plan tool may write one unique Markdown plan file under ${e}.\n\nUse Pi read-only exploration first. When the bounded packet is ready, call run_fable_plan. Fable runs one fresh draft with repository inspection access and returns untrusted text. Cross-check every Fable claim against repository evidence, keep verified findings separate from explicitly inferred findings, resolve conflicts yourself, and present the final visible plan before calling write_plan. Never treat Fable output as repository evidence and never expose credentials, transcripts, commands, tools, or session data in the packet.\n\nCurrent Fable stage: ${t}. An interrupted stage is not relaunched automatically.`}function a(e,t,a,o){return e===`debug`?r(t,a):e===`fable`?i(t,o):n(t)}exports.buildDebugPlanningPrompt=r,exports.buildFablePlanningPrompt=i,exports.buildFlavorPlanningPrompt=a,exports.buildNormalPlanningPrompt=n;
3
+ //# sourceMappingURL=prompts.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompts.cjs","names":[],"sources":["../src/prompts.ts"],"sourcesContent":["export type PlanningFlavor = 'normal' | 'debug' | 'fable';\n\nexport interface DebugEvidencePacket {\n issue: string;\n expectedBehavior: string;\n reproductionAttempt: string;\n actualBehavior: string;\n logs: string[];\n correlatedTraceEvidence: string[];\n processOutput: string[];\n browserConsoleEvidence: string[];\n correlationIds: string[];\n timestamps: string[];\n verifiedFacts: string[];\n hypotheses: string[];\n unavailableEvidence: string[];\n}\n\nconst EVIDENCE_FIELDS: readonly (keyof DebugEvidencePacket)[] = [\n 'issue',\n 'expectedBehavior',\n 'reproductionAttempt',\n 'actualBehavior',\n 'logs',\n 'correlatedTraceEvidence',\n 'processOutput',\n 'browserConsoleEvidence',\n 'correlationIds',\n 'timestamps',\n 'verifiedFacts',\n 'hypotheses',\n 'unavailableEvidence',\n];\n\nfunction evidenceLines(packet: DebugEvidencePacket): string {\n return EVIDENCE_FIELDS.flatMap((field) => {\n const value = packet[field];\n if (Array.isArray(value) && value.length === 0) return [];\n if (typeof value === 'string' && value.length === 0) return [];\n return `${field}: ${Array.isArray(value) ? value.join(' | ') : value}`;\n }).join('\\n');\n}\n\nexport function buildNormalPlanningPrompt(plansDirectory: string): string {\n return `[PLAN MODE ACTIVE: NORMAL]\\nYou are in repository read-only normal planning. The dedicated write_plan tool may write one unique Markdown plan file under ${plansDirectory}.\\n\\nExplore the codebase and produce a concrete implementation plan. For every plan, first call subagent with action \"agents\", cluster 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 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 the turn and wait for completion notifications.\\n\\nAfter all planning drafts complete, compare the candidates, pick the strongest draft, cross-check it against 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, 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.`;\n}\n\nexport function buildDebugPlanningPrompt(plansDirectory: string, evidence: DebugEvidencePacket | undefined): string {\n const guidance = evidence\n ? `A bounded debug evidence packet is recorded below. Cite its verified facts when making source findings. Keep verified facts and hypotheses distinct. Do not claim a verified root cause when reproduction or supporting evidence is unavailable.\\n\\n[DEBUG EVIDENCE]\\n${evidenceLines(evidence)}`\n : 'No structured debug evidence packet is recorded yet. First understand the reported issue, then gather only the evidence relevant to it. Recording evidence is optional and must not block source exploration, delegation, writing a plan, or completing a plan.';\n return `[PLAN MODE ACTIVE: DEBUG]\\nYou are in repository read-only adaptive debug planning. The dedicated write_plan tool may write one unique Markdown plan file under ${plansDirectory}.\\n\\n${guidance}\\n\\nUse the exact read-only tools available for the issue. Bash is available for repository inspection, but commands must not modify files or repository state. Treat unavailable evidence as unavailable, never invent its contents, and keep verified facts separate from hypotheses. ${evidence ? 'The final plan must separate verified facts from hypotheses and must not present an unsupported root cause as verified.' : 'You may call record_debug_evidence when structured notes would help, but continue investigating without it when it is not relevant.'}`;\n}\n\nexport function buildFablePlanningPrompt(plansDirectory: string, stage: string): string {\n return `[PLAN MODE ACTIVE: FABLE]\\nYou are in repository read-only Fable planning. Pi owns final verification and synthesis. The dedicated write_plan tool may write one unique Markdown plan file under ${plansDirectory}.\\n\\nUse Pi read-only exploration first. When the bounded packet is ready, call run_fable_plan. Fable runs one fresh draft with repository inspection access and returns untrusted text. Cross-check every Fable claim against repository evidence, keep verified findings separate from explicitly inferred findings, resolve conflicts yourself, and present the final visible plan before calling write_plan. Never treat Fable output as repository evidence and never expose credentials, transcripts, commands, tools, or session data in the packet.\\n\\nCurrent Fable stage: ${stage}. An interrupted stage is not relaunched automatically.`;\n}\n\nexport function buildFlavorPlanningPrompt(\n flavor: PlanningFlavor,\n plansDirectory: string,\n evidence: DebugEvidencePacket | undefined,\n fableStage: string,\n): string {\n if (flavor === 'debug') return buildDebugPlanningPrompt(plansDirectory, evidence);\n if (flavor === 'fable') return buildFablePlanningPrompt(plansDirectory, fableStage);\n return buildNormalPlanningPrompt(plansDirectory);\n}\n"],"mappings":"AAkBA,MAAM,EAA0D,CAC9D,QACA,mBACA,sBACA,iBACA,OACA,0BACA,gBACA,yBACA,iBACA,aACA,gBACA,aACA,sBACD,CAED,SAAS,EAAc,EAAqC,CAC1D,OAAO,EAAgB,QAAS,GAAU,CACxC,IAAM,EAAQ,EAAO,GAGrB,OAFI,MAAM,QAAQ,EAAM,EAAI,EAAM,SAAW,GACzC,OAAO,GAAU,UAAY,EAAM,SAAW,EAAU,EAAE,CACvD,GAAG,EAAM,IAAI,MAAM,QAAQ,EAAM,CAAG,EAAM,KAAK,MAAM,CAAG,KAC/D,CAAC,KAAK;EAAK,CAGf,SAAgB,EAA0B,EAAgC,CACxE,MAAO,4JAA4J,EAAe,ouFAGpL,SAAgB,EAAyB,EAAwB,EAAmD,CAIlH,MAAO,mKAAmK,EAAe,OAHxK,EACb,yQAAyQ,EAAc,EAAS,GAChS,kQACqM,0RAA0R,EAAW,0HAA4H,wIAG5mB,SAAgB,EAAyB,EAAwB,EAAuB,CACtF,MAAO,oMAAoM,EAAe,sjBAAsjB,EAAM,yDAGxxB,SAAgB,EACd,EACA,EACA,EACA,EACQ,CAGR,OAFI,IAAW,QAAgB,EAAyB,EAAgB,EAAS,CAC7E,IAAW,QAAgB,EAAyB,EAAgB,EAAW,CAC5E,EAA0B,EAAe"}
@@ -0,0 +1,24 @@
1
+ //#region src/prompts.d.ts
2
+ type PlanningFlavor = 'normal' | 'debug' | 'fable';
3
+ interface DebugEvidencePacket {
4
+ issue: string;
5
+ expectedBehavior: string;
6
+ reproductionAttempt: string;
7
+ actualBehavior: string;
8
+ logs: string[];
9
+ correlatedTraceEvidence: string[];
10
+ processOutput: string[];
11
+ browserConsoleEvidence: string[];
12
+ correlationIds: string[];
13
+ timestamps: string[];
14
+ verifiedFacts: string[];
15
+ hypotheses: string[];
16
+ unavailableEvidence: string[];
17
+ }
18
+ declare function buildNormalPlanningPrompt(plansDirectory: string): string;
19
+ declare function buildDebugPlanningPrompt(plansDirectory: string, evidence: DebugEvidencePacket | undefined): string;
20
+ declare function buildFablePlanningPrompt(plansDirectory: string, stage: string): string;
21
+ declare function buildFlavorPlanningPrompt(flavor: PlanningFlavor, plansDirectory: string, evidence: DebugEvidencePacket | undefined, fableStage: string): string;
22
+ //#endregion
23
+ export { DebugEvidencePacket, PlanningFlavor, buildDebugPlanningPrompt, buildFablePlanningPrompt, buildFlavorPlanningPrompt, buildNormalPlanningPrompt };
24
+ //# sourceMappingURL=prompts.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompts.d.cts","names":[],"sources":["../src/prompts.ts"],"mappings":";KAAY,cAAA;AAAA,UAEK,mBAAA;EACf,KAAA;EACA,gBAAA;EACA,mBAAA;EACA,cAAA;EACA,IAAA;EACA,uBAAA;EACA,aAAA;EACA,sBAAA;EACA,cAAA;EACA,UAAA;EACA,aAAA;EACA,UAAA;EACA,mBAAA;AAAA;AAAA,iBA4Bc,yBAAA,CAA0B,cAAA;AAAA,iBAI1B,wBAAA,CAAyB,cAAA,UAAwB,QAAA,EAAU,mBAAA;AAAA,iBAO3D,wBAAA,CAAyB,cAAA,UAAwB,KAAA;AAAA,iBAIjD,yBAAA,CACd,MAAA,EAAQ,cAAA,EACR,cAAA,UACA,QAAA,EAAU,mBAAA,cACV,UAAA"}
@@ -0,0 +1,24 @@
1
+ //#region src/prompts.d.ts
2
+ type PlanningFlavor = 'normal' | 'debug' | 'fable';
3
+ interface DebugEvidencePacket {
4
+ issue: string;
5
+ expectedBehavior: string;
6
+ reproductionAttempt: string;
7
+ actualBehavior: string;
8
+ logs: string[];
9
+ correlatedTraceEvidence: string[];
10
+ processOutput: string[];
11
+ browserConsoleEvidence: string[];
12
+ correlationIds: string[];
13
+ timestamps: string[];
14
+ verifiedFacts: string[];
15
+ hypotheses: string[];
16
+ unavailableEvidence: string[];
17
+ }
18
+ declare function buildNormalPlanningPrompt(plansDirectory: string): string;
19
+ declare function buildDebugPlanningPrompt(plansDirectory: string, evidence: DebugEvidencePacket | undefined): string;
20
+ declare function buildFablePlanningPrompt(plansDirectory: string, stage: string): string;
21
+ declare function buildFlavorPlanningPrompt(flavor: PlanningFlavor, plansDirectory: string, evidence: DebugEvidencePacket | undefined, fableStage: string): string;
22
+ //#endregion
23
+ export { DebugEvidencePacket, PlanningFlavor, buildDebugPlanningPrompt, buildFablePlanningPrompt, buildFlavorPlanningPrompt, buildNormalPlanningPrompt };
24
+ //# sourceMappingURL=prompts.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompts.d.mts","names":[],"sources":["../src/prompts.ts"],"mappings":";KAAY,cAAA;AAAA,UAEK,mBAAA;EACf,KAAA;EACA,gBAAA;EACA,mBAAA;EACA,cAAA;EACA,IAAA;EACA,uBAAA;EACA,aAAA;EACA,sBAAA;EACA,cAAA;EACA,UAAA;EACA,aAAA;EACA,UAAA;EACA,mBAAA;AAAA;AAAA,iBA4Bc,yBAAA,CAA0B,cAAA;AAAA,iBAI1B,wBAAA,CAAyB,cAAA,UAAwB,QAAA,EAAU,mBAAA;AAAA,iBAO3D,wBAAA,CAAyB,cAAA,UAAwB,KAAA;AAAA,iBAIjD,yBAAA,CACd,MAAA,EAAQ,cAAA,EACR,cAAA,UACA,QAAA,EAAU,mBAAA,cACV,UAAA"}
@@ -0,0 +1,3 @@
1
+ const e=[`issue`,`expectedBehavior`,`reproductionAttempt`,`actualBehavior`,`logs`,`correlatedTraceEvidence`,`processOutput`,`browserConsoleEvidence`,`correlationIds`,`timestamps`,`verifiedFacts`,`hypotheses`,`unavailableEvidence`];function t(t){return e.flatMap(e=>{let n=t[e];return Array.isArray(n)&&n.length===0||typeof n==`string`&&n.length===0?[]:`${e}: ${Array.isArray(n)?n.join(` | `):n}`}).join(`
2
+ `)}function n(e){return`[PLAN MODE ACTIVE: NORMAL]\nYou are in repository read-only normal planning. The dedicated write_plan tool may write one unique Markdown plan file under ${e}.\n\nExplore the codebase and produce a concrete implementation plan. For every plan, first call subagent with action "agents", cluster 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 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 the turn and wait for completion notifications.\n\nAfter all planning drafts complete, compare the candidates, pick the strongest draft, cross-check it against 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, 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.`}function r(e,n){return`[PLAN MODE ACTIVE: DEBUG]\nYou are in repository read-only adaptive debug planning. The dedicated write_plan tool may write one unique Markdown plan file under ${e}.\n\n${n?`A bounded debug evidence packet is recorded below. Cite its verified facts when making source findings. Keep verified facts and hypotheses distinct. Do not claim a verified root cause when reproduction or supporting evidence is unavailable.\n\n[DEBUG EVIDENCE]\n${t(n)}`:`No structured debug evidence packet is recorded yet. First understand the reported issue, then gather only the evidence relevant to it. Recording evidence is optional and must not block source exploration, delegation, writing a plan, or completing a plan.`}\n\nUse the exact read-only tools available for the issue. Bash is available for repository inspection, but commands must not modify files or repository state. Treat unavailable evidence as unavailable, never invent its contents, and keep verified facts separate from hypotheses. ${n?`The final plan must separate verified facts from hypotheses and must not present an unsupported root cause as verified.`:`You may call record_debug_evidence when structured notes would help, but continue investigating without it when it is not relevant.`}`}function i(e,t){return`[PLAN MODE ACTIVE: FABLE]\nYou are in repository read-only Fable planning. Pi owns final verification and synthesis. The dedicated write_plan tool may write one unique Markdown plan file under ${e}.\n\nUse Pi read-only exploration first. When the bounded packet is ready, call run_fable_plan. Fable runs one fresh draft with repository inspection access and returns untrusted text. Cross-check every Fable claim against repository evidence, keep verified findings separate from explicitly inferred findings, resolve conflicts yourself, and present the final visible plan before calling write_plan. Never treat Fable output as repository evidence and never expose credentials, transcripts, commands, tools, or session data in the packet.\n\nCurrent Fable stage: ${t}. An interrupted stage is not relaunched automatically.`}function a(e,t,a,o){return e===`debug`?r(t,a):e===`fable`?i(t,o):n(t)}export{r as buildDebugPlanningPrompt,i as buildFablePlanningPrompt,a as buildFlavorPlanningPrompt,n as buildNormalPlanningPrompt};
3
+ //# sourceMappingURL=prompts.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompts.mjs","names":[],"sources":["../src/prompts.ts"],"sourcesContent":["export type PlanningFlavor = 'normal' | 'debug' | 'fable';\n\nexport interface DebugEvidencePacket {\n issue: string;\n expectedBehavior: string;\n reproductionAttempt: string;\n actualBehavior: string;\n logs: string[];\n correlatedTraceEvidence: string[];\n processOutput: string[];\n browserConsoleEvidence: string[];\n correlationIds: string[];\n timestamps: string[];\n verifiedFacts: string[];\n hypotheses: string[];\n unavailableEvidence: string[];\n}\n\nconst EVIDENCE_FIELDS: readonly (keyof DebugEvidencePacket)[] = [\n 'issue',\n 'expectedBehavior',\n 'reproductionAttempt',\n 'actualBehavior',\n 'logs',\n 'correlatedTraceEvidence',\n 'processOutput',\n 'browserConsoleEvidence',\n 'correlationIds',\n 'timestamps',\n 'verifiedFacts',\n 'hypotheses',\n 'unavailableEvidence',\n];\n\nfunction evidenceLines(packet: DebugEvidencePacket): string {\n return EVIDENCE_FIELDS.flatMap((field) => {\n const value = packet[field];\n if (Array.isArray(value) && value.length === 0) return [];\n if (typeof value === 'string' && value.length === 0) return [];\n return `${field}: ${Array.isArray(value) ? value.join(' | ') : value}`;\n }).join('\\n');\n}\n\nexport function buildNormalPlanningPrompt(plansDirectory: string): string {\n return `[PLAN MODE ACTIVE: NORMAL]\\nYou are in repository read-only normal planning. The dedicated write_plan tool may write one unique Markdown plan file under ${plansDirectory}.\\n\\nExplore the codebase and produce a concrete implementation plan. For every plan, first call subagent with action \"agents\", cluster 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 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 the turn and wait for completion notifications.\\n\\nAfter all planning drafts complete, compare the candidates, pick the strongest draft, cross-check it against 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, 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.`;\n}\n\nexport function buildDebugPlanningPrompt(plansDirectory: string, evidence: DebugEvidencePacket | undefined): string {\n const guidance = evidence\n ? `A bounded debug evidence packet is recorded below. Cite its verified facts when making source findings. Keep verified facts and hypotheses distinct. Do not claim a verified root cause when reproduction or supporting evidence is unavailable.\\n\\n[DEBUG EVIDENCE]\\n${evidenceLines(evidence)}`\n : 'No structured debug evidence packet is recorded yet. First understand the reported issue, then gather only the evidence relevant to it. Recording evidence is optional and must not block source exploration, delegation, writing a plan, or completing a plan.';\n return `[PLAN MODE ACTIVE: DEBUG]\\nYou are in repository read-only adaptive debug planning. The dedicated write_plan tool may write one unique Markdown plan file under ${plansDirectory}.\\n\\n${guidance}\\n\\nUse the exact read-only tools available for the issue. Bash is available for repository inspection, but commands must not modify files or repository state. Treat unavailable evidence as unavailable, never invent its contents, and keep verified facts separate from hypotheses. ${evidence ? 'The final plan must separate verified facts from hypotheses and must not present an unsupported root cause as verified.' : 'You may call record_debug_evidence when structured notes would help, but continue investigating without it when it is not relevant.'}`;\n}\n\nexport function buildFablePlanningPrompt(plansDirectory: string, stage: string): string {\n return `[PLAN MODE ACTIVE: FABLE]\\nYou are in repository read-only Fable planning. Pi owns final verification and synthesis. The dedicated write_plan tool may write one unique Markdown plan file under ${plansDirectory}.\\n\\nUse Pi read-only exploration first. When the bounded packet is ready, call run_fable_plan. Fable runs one fresh draft with repository inspection access and returns untrusted text. Cross-check every Fable claim against repository evidence, keep verified findings separate from explicitly inferred findings, resolve conflicts yourself, and present the final visible plan before calling write_plan. Never treat Fable output as repository evidence and never expose credentials, transcripts, commands, tools, or session data in the packet.\\n\\nCurrent Fable stage: ${stage}. An interrupted stage is not relaunched automatically.`;\n}\n\nexport function buildFlavorPlanningPrompt(\n flavor: PlanningFlavor,\n plansDirectory: string,\n evidence: DebugEvidencePacket | undefined,\n fableStage: string,\n): string {\n if (flavor === 'debug') return buildDebugPlanningPrompt(plansDirectory, evidence);\n if (flavor === 'fable') return buildFablePlanningPrompt(plansDirectory, fableStage);\n return buildNormalPlanningPrompt(plansDirectory);\n}\n"],"mappings":"AAkBA,MAAM,EAA0D,CAC9D,QACA,mBACA,sBACA,iBACA,OACA,0BACA,gBACA,yBACA,iBACA,aACA,gBACA,aACA,sBACD,CAED,SAAS,EAAc,EAAqC,CAC1D,OAAO,EAAgB,QAAS,GAAU,CACxC,IAAM,EAAQ,EAAO,GAGrB,OAFI,MAAM,QAAQ,EAAM,EAAI,EAAM,SAAW,GACzC,OAAO,GAAU,UAAY,EAAM,SAAW,EAAU,EAAE,CACvD,GAAG,EAAM,IAAI,MAAM,QAAQ,EAAM,CAAG,EAAM,KAAK,MAAM,CAAG,KAC/D,CAAC,KAAK;EAAK,CAGf,SAAgB,EAA0B,EAAgC,CACxE,MAAO,4JAA4J,EAAe,ouFAGpL,SAAgB,EAAyB,EAAwB,EAAmD,CAIlH,MAAO,mKAAmK,EAAe,OAHxK,EACb,yQAAyQ,EAAc,EAAS,GAChS,kQACqM,0RAA0R,EAAW,0HAA4H,wIAG5mB,SAAgB,EAAyB,EAAwB,EAAuB,CACtF,MAAO,oMAAoM,EAAe,sjBAAsjB,EAAM,yDAGxxB,SAAgB,EACd,EACA,EACA,EACA,EACQ,CAGR,OAFI,IAAW,QAAgB,EAAyB,EAAgB,EAAS,CAC7E,IAAW,QAAgB,EAAyB,EAAgB,EAAW,CAC5E,EAA0B,EAAe"}
package/package.json ADDED
@@ -0,0 +1,61 @@
1
+ {
2
+ "name": "@agimon-ai/doompi-plan",
3
+ "version": "0.0.1-alpha.0",
4
+ "description": "Reusable Pi read-only planning mode extension",
5
+ "homepage": "https://agimon.ai",
6
+ "license": "MIT",
7
+ "author": "Vuong Ngo",
8
+ "files": [
9
+ "dist"
10
+ ],
11
+ "type": "module",
12
+ "types": "./dist/index.d.mts",
13
+ "jsnext:main": "./dist/index.mjs",
14
+ "exports": {
15
+ ".": "./dist/index.mjs",
16
+ "./config": "./dist/config.mjs",
17
+ "./extensions/pi": "./dist/extensions/pi.mjs",
18
+ "./extensions/doom": "./dist/extensions/doom.mjs",
19
+ "./fableFlow": "./dist/fableFlow.mjs",
20
+ "./logSinkTelemetry": "./dist/logSinkTelemetry.mjs",
21
+ "./planConfig": "./dist/planConfig.mjs",
22
+ "./planMode": "./dist/planMode.mjs",
23
+ "./prompts": "./dist/prompts.mjs",
24
+ "./package.json": "./package.json"
25
+ },
26
+ "publishConfig": {
27
+ "access": "public",
28
+ "registry": "https://registry.npmjs.org/"
29
+ },
30
+ "dependencies": {
31
+ "@agimon-ai/doompi-config": "0.0.1-alpha.0",
32
+ "@agimon-ai/doompi-telemetry": "0.0.1-alpha.0",
33
+ "@agimon-ai/doompi-extension-contracts": "0.0.1-alpha.0",
34
+ "@agimon-ai/doompi-ui": "0.0.1-alpha.0"
35
+ },
36
+ "devDependencies": {
37
+ "@earendil-works/pi-coding-agent": "0.84.1",
38
+ "@types/node": "26.1.1",
39
+ "@vitest/coverage-v8": "4.1.10",
40
+ "typescript": "6.0.3",
41
+ "vitest": "4.1.10"
42
+ },
43
+ "peerDependencies": {
44
+ "@earendil-works/pi-coding-agent": "0.84.1"
45
+ },
46
+ "engines": {
47
+ "node": ">=22.19.0"
48
+ },
49
+ "pi": {
50
+ "extensions": [
51
+ "./dist/extensions/pi.mjs"
52
+ ]
53
+ },
54
+ "scripts": {
55
+ "build": "tsdown",
56
+ "test": "vitest --run",
57
+ "typecheck": "tsc --noEmit",
58
+ "lint": "oxlint . && oxfmt . --check",
59
+ "fixcode": "oxlint . --fix && oxfmt ."
60
+ }
61
+ }