@agimon-ai/doompi-plan 0.0.1-alpha.69 → 0.0.1-alpha.70
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -2
- package/dist/api-contracts.cjs +1 -0
- package/dist/api-contracts.d.cts +2 -0
- package/dist/api-contracts.d.mts +2 -0
- package/dist/api-contracts.mjs +1 -0
- package/dist/controllers/planApi.cjs +2 -0
- package/dist/controllers/planApi.cjs.map +1 -0
- package/dist/controllers/planApi.mjs +2 -0
- package/dist/controllers/planApi.mjs.map +1 -0
- package/dist/controllers/planMode.cjs +7 -0
- package/dist/controllers/planMode.cjs.map +1 -0
- package/dist/{services → controllers}/planMode.d.cts +16 -7
- package/dist/controllers/planMode.d.cts.map +1 -0
- package/dist/{services → controllers}/planMode.d.mts +16 -7
- package/dist/controllers/planMode.d.mts.map +1 -0
- package/dist/controllers/planMode.mjs +7 -0
- package/dist/controllers/planMode.mjs.map +1 -0
- package/dist/controllers/planServerSession.cjs +2 -0
- package/dist/controllers/planServerSession.cjs.map +1 -0
- package/dist/controllers/planServerSession.mjs +2 -0
- package/dist/controllers/planServerSession.mjs.map +1 -0
- package/dist/extensions/pi.cjs +2 -1
- package/dist/extensions/pi.cjs.map +1 -0
- package/dist/extensions/pi.d.cts +5 -2
- package/dist/extensions/pi.d.cts.map +1 -0
- package/dist/extensions/pi.d.mts +5 -2
- package/dist/extensions/pi.d.mts.map +1 -0
- package/dist/extensions/pi.mjs +2 -1
- package/dist/extensions/pi.mjs.map +1 -0
- package/dist/extensions/server.cjs +2 -0
- package/dist/extensions/server.cjs.map +1 -0
- package/dist/extensions/server.d.cts +5 -0
- package/dist/extensions/server.d.cts.map +1 -0
- package/dist/extensions/server.d.mts +5 -0
- package/dist/extensions/server.d.mts.map +1 -0
- package/dist/extensions/server.mjs +2 -0
- package/dist/extensions/server.mjs.map +1 -0
- package/dist/fable-flow.cjs +1 -0
- package/dist/{fableFlow.d.cts → fable-flow.d.cts} +1 -1
- package/dist/{fableFlow.d.mts → fable-flow.d.mts} +1 -1
- package/dist/fable-flow.mjs +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +4 -4
- package/dist/index.d.mts +4 -4
- package/dist/index.mjs +1 -1
- package/dist/log-sink-telemetry.cjs +1 -0
- package/dist/log-sink-telemetry.d.cts +2 -0
- package/dist/log-sink-telemetry.d.mts +2 -0
- package/dist/log-sink-telemetry.mjs +1 -0
- package/dist/plan-mode.cjs +1 -0
- package/dist/plan-mode.d.cts +3 -0
- package/dist/plan-mode.d.mts +3 -0
- package/dist/plan-mode.mjs +1 -0
- package/dist/prompts.cjs +1 -1
- package/dist/prompts.d.cts +2 -2
- package/dist/prompts.d.mts +2 -2
- package/dist/prompts.mjs +1 -1
- package/dist/schemas/apiContracts.cjs +2 -0
- package/dist/schemas/apiContracts.cjs.map +1 -0
- package/dist/schemas/apiContracts.d.cts +53 -0
- package/dist/schemas/apiContracts.d.cts.map +1 -0
- package/dist/schemas/apiContracts.d.mts +53 -0
- package/dist/schemas/apiContracts.d.mts.map +1 -0
- package/dist/schemas/apiContracts.mjs +2 -0
- package/dist/schemas/apiContracts.mjs.map +1 -0
- package/dist/schemas/plan/planConfig.cjs +1 -1
- package/dist/schemas/plan/planConfig.cjs.map +1 -1
- package/dist/schemas/plan/planConfig.d.cts +1 -1
- package/dist/schemas/plan/planConfig.d.cts.map +1 -1
- package/dist/schemas/plan/planConfig.d.mts +1 -1
- package/dist/schemas/plan/planConfig.d.mts.map +1 -1
- package/dist/schemas/plan/planConfig.mjs +1 -1
- package/dist/schemas/plan/planConfig.mjs.map +1 -1
- package/dist/services/{fableFlow.cjs → fableFlow/index.cjs} +1 -1
- package/dist/services/fableFlow/index.cjs.map +1 -0
- package/dist/services/{fableFlow.d.cts → fableFlow/index.d.cts} +2 -2
- package/dist/services/fableFlow/index.d.cts.map +1 -0
- package/dist/services/{fableFlow.d.mts → fableFlow/index.d.mts} +2 -2
- package/dist/services/fableFlow/index.d.mts.map +1 -0
- package/dist/services/{fableFlow.mjs → fableFlow/index.mjs} +1 -1
- package/dist/services/fableFlow/index.mjs.map +1 -0
- package/dist/{adapters/telemetry/logSinkTelemetry.cjs → services/logSinkTelemetry/index.cjs} +1 -1
- package/dist/services/logSinkTelemetry/index.cjs.map +1 -0
- package/dist/{adapters/telemetry/logSinkTelemetry.d.cts → services/logSinkTelemetry/index.d.cts} +2 -2
- package/dist/services/logSinkTelemetry/index.d.cts.map +1 -0
- package/dist/{adapters/telemetry/logSinkTelemetry.d.mts → services/logSinkTelemetry/index.d.mts} +2 -2
- package/dist/services/logSinkTelemetry/index.d.mts.map +1 -0
- package/dist/{adapters/telemetry/logSinkTelemetry.mjs → services/logSinkTelemetry/index.mjs} +1 -1
- package/dist/services/logSinkTelemetry/index.mjs.map +1 -0
- package/dist/services/optionalTeamServices/index.cjs +2 -0
- package/dist/services/optionalTeamServices/index.cjs.map +1 -0
- package/dist/services/optionalTeamServices/index.mjs +2 -0
- package/dist/services/optionalTeamServices/index.mjs.map +1 -0
- package/dist/services/planPointer/index.cjs +2 -0
- package/dist/services/planPointer/index.cjs.map +1 -0
- package/dist/services/planPointer/index.mjs +2 -0
- package/dist/services/planPointer/index.mjs.map +1 -0
- package/dist/services/{prompts.mjs → prompts/index.cjs} +3 -3
- package/dist/services/prompts/index.cjs.map +1 -0
- package/dist/services/{prompts.d.cts → prompts/index.d.cts} +4 -2
- package/dist/services/prompts/index.d.cts.map +1 -0
- package/dist/services/{prompts.d.mts → prompts/index.d.mts} +4 -2
- package/dist/services/prompts/index.d.mts.map +1 -0
- package/dist/services/{prompts.cjs → prompts/index.mjs} +3 -3
- package/dist/services/prompts/index.mjs.map +1 -0
- package/dist/types/planApi.cjs.map +1 -1
- package/dist/types/planApi.mjs.map +1 -1
- package/package.json +45 -35
- package/src/extensions/web.ts +62 -0
- package/src/types/planApi.ts +2 -2
- package/src/web/api/planApi.ts +2 -1
- package/src/web/components/PlanActivitySection.tsx +4 -3
- package/src/web/components/PlanPanel.tsx +4 -3
- package/src/web/components/PlanReviewPrompt.tsx +3 -2
- package/src/web/components/PlanToolMessage.tsx +3 -2
- package/src/web/lib/planSettings.ts +3 -2
- package/src/web/lib/planToolRender.ts +1 -1
- package/src/web/tsconfig.json +1 -1
- package/dist/adapters/node/planPointer.cjs +0 -2
- package/dist/adapters/node/planPointer.cjs.map +0 -1
- package/dist/adapters/node/planPointer.mjs +0 -2
- package/dist/adapters/node/planPointer.mjs.map +0 -1
- package/dist/adapters/pi/extension.cjs +0 -2
- package/dist/adapters/pi/extension.cjs.map +0 -1
- package/dist/adapters/pi/extension.d.cts +0 -8
- package/dist/adapters/pi/extension.d.cts.map +0 -1
- package/dist/adapters/pi/extension.d.mts +0 -8
- package/dist/adapters/pi/extension.d.mts.map +0 -1
- package/dist/adapters/pi/extension.mjs +0 -2
- package/dist/adapters/pi/extension.mjs.map +0 -1
- package/dist/adapters/planApi.cjs +0 -2
- package/dist/adapters/planApi.cjs.map +0 -1
- package/dist/adapters/planApi.d.cts +0 -14
- package/dist/adapters/planApi.d.cts.map +0 -1
- package/dist/adapters/planApi.d.mts +0 -14
- package/dist/adapters/planApi.d.mts.map +0 -1
- package/dist/adapters/planApi.mjs +0 -2
- package/dist/adapters/planApi.mjs.map +0 -1
- package/dist/adapters/telemetry/logSinkTelemetry.cjs.map +0 -1
- package/dist/adapters/telemetry/logSinkTelemetry.d.cts.map +0 -1
- package/dist/adapters/telemetry/logSinkTelemetry.d.mts.map +0 -1
- package/dist/adapters/telemetry/logSinkTelemetry.mjs.map +0 -1
- package/dist/fableFlow.cjs +0 -1
- package/dist/fableFlow.mjs +0 -1
- package/dist/logSinkTelemetry.cjs +0 -1
- package/dist/logSinkTelemetry.d.cts +0 -2
- package/dist/logSinkTelemetry.d.mts +0 -2
- package/dist/logSinkTelemetry.mjs +0 -1
- package/dist/planMode.cjs +0 -1
- package/dist/planMode.d.cts +0 -3
- package/dist/planMode.d.mts +0 -3
- package/dist/planMode.mjs +0 -1
- package/dist/services/fableFlow.cjs.map +0 -1
- package/dist/services/fableFlow.d.cts.map +0 -1
- package/dist/services/fableFlow.d.mts.map +0 -1
- package/dist/services/fableFlow.mjs.map +0 -1
- package/dist/services/planMode.cjs +0 -7
- package/dist/services/planMode.cjs.map +0 -1
- package/dist/services/planMode.d.cts.map +0 -1
- package/dist/services/planMode.d.mts.map +0 -1
- package/dist/services/planMode.mjs +0 -7
- package/dist/services/planMode.mjs.map +0 -1
- package/dist/services/prompts.cjs.map +0 -1
- package/dist/services/prompts.d.cts.map +0 -1
- package/dist/services/prompts.d.mts.map +0 -1
- package/dist/services/prompts.mjs.map +0 -1
- package/dist/sessionApi.cjs +0 -1
- package/dist/sessionApi.d.cts +0 -2
- package/dist/sessionApi.d.mts +0 -2
- package/dist/sessionApi.mjs +0 -1
- package/src/exports/webClient.ts +0 -1
- package/src/web/index.ts +0 -52
- /package/dist/{planConfig.cjs → plan-config.cjs} +0 -0
- /package/dist/{planConfig.d.cts → plan-config.d.cts} +0 -0
- /package/dist/{planConfig.d.mts → plan-config.d.mts} +0 -0
- /package/dist/{planConfig.mjs → plan-config.mjs} +0 -0
package/README.md
CHANGED
|
@@ -98,8 +98,16 @@ calls consume provider quota.
|
|
|
98
98
|
## Public API
|
|
99
99
|
|
|
100
100
|
The root exports planning configuration schemas, prompts, Fable flow helpers, and the Plan mode
|
|
101
|
-
service. Focused exports include `/config`, `/
|
|
102
|
-
|
|
101
|
+
service. Focused exports include `/config`, `/plan-config`, `/plan-mode`, `/fable-flow`, and `/prompts`.
|
|
102
|
+
The session-scoped server plugin owns the cockpit plan API. Pi and server entries live in
|
|
103
|
+
`src/extensions`; their public modules are `/extensions/pi` and `/extensions/server`.
|
|
104
|
+
Controllers compose tools and services. Services live in named folders, and `src/exports`
|
|
105
|
+
contains only flat public exports.
|
|
106
|
+
|
|
107
|
+
The Pi plugin constructs declarations, installs optional Cordis services, and then becomes ready.
|
|
108
|
+
Its `onStop` hook cancels pending work and restores the previous agent configuration before
|
|
109
|
+
registrations are removed. `onDispose` also handles partial startup cleanup. Session events and
|
|
110
|
+
Plan flavor changes remain separate from plugin startup and shutdown.
|
|
103
111
|
|
|
104
112
|
## Development
|
|
105
113
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:`Module`}});const e=require("./schemas/apiContracts.cjs");exports.apiContracts=e.apiContracts,exports.default=e.apiContracts;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{apiContracts as e}from"./schemas/apiContracts.mjs";export{e as apiContracts,e as default};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=require("../_virtual/_rolldown/runtime.cjs"),t=require("../services/planPointer/index.cjs"),n=require("../types/planApi.cjs");let r=require("node:crypto"),i=require("node:fs/promises");i=e.__toESM(i,1);let a=require("hono");const o=1048576,s=`This session has not written a plan yet.`;function c(e){return(0,r.createHash)(`sha256`).update(e,`utf8`).digest(`hex`)}async function l(e){let t;try{t=await i.default.readFile(e)}catch{return{content:``,hash:``,unavailable:!0,reason:`the plan file no longer exists`}}if(t.byteLength>1048576)return{content:``,hash:``,unavailable:!0,reason:`the plan is larger than ${o} bytes`};let n=t.toString(`utf8`);return{content:n,hash:c(n),unavailable:!1}}function u(e={}){let r=new a.Hono,o=e.pointers??new t.PlanPointerService({env:process.env}),u=()=>e.sessionId===void 0?void 0:o.read(e.sessionId);return r.get(n.currentPath(),async e=>{let t=u();if(t===void 0)return e.json({error:s},404);let n={path:t.path,title:t.title,writtenAt:t.writtenAt,...t.planId===void 0?{}:{planId:t.planId},...await l(t.path)};return e.json(n)}),r.put(n.contentPath(),async e=>{let t;try{t=await e.req.json()}catch{return e.json({error:`The save body is not JSON.`},400)}if(typeof t.content!=`string`||typeof t.expectedHash!=`string`)return e.json({error:`A save carries the content to write and the hash it was read at.`},400);let n=u();if(n===void 0)return e.json({error:s},404);let r=await l(n.path);if(r.unavailable)return e.json({error:r.reason??s},409);if(r.hash!==t.expectedHash)return e.json({error:`The plan changed since it was opened.`,hash:r.hash},409);let a=await i.default.lstat(n.path);return!a.isFile()||a.nlink!==1?e.json({error:`The plan file is no longer a regular file, so it was not written.`},409):(await i.default.writeFile(n.path,t.content,{encoding:`utf8`,mode:384}),e.json({hash:c(t.content)}))}),r}const d={basePath:n.API_BASE_PATH,start(e){let t=u(e.sessionId===void 0?{}:{sessionId:e.sessionId});return{fetch:e=>t.fetch(e),close:()=>void 0}}};exports.MAX_PLAN_BYTES=o,exports.api=d,exports.createPlanApi=u;
|
|
2
|
+
//# sourceMappingURL=planApi.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"planApi.cjs","names":["createHash","fs","Hono","PlanPointerService","currentPath","contentPath","API_BASE_PATH"],"sources":["../../src/controllers/planApi.ts"],"sourcesContent":["import { createHash } from 'node:crypto';\nimport fs from 'node:fs/promises';\n\nimport type { DoomApi, DoomApiContext, DoomApiHandler } from '@agimon-ai/doompi-core/package-api';\nimport { Hono } from 'hono';\n\nimport { PlanPointerService } from '../services/planPointer';\nimport {\n API_BASE_PATH,\n contentPath,\n currentPath,\n type PlanDetailView,\n type PlanSaveRequest,\n type PlanSaveView,\n} from '../types/planApi';\nimport type { PlanPointerPort } from '../types/planPointer';\n\n/**\n * This package's HTTP surface: the session's current plan, and the manual save.\n *\n * Routes are relative to the mount its host gives it, so nothing here repeats\n * where it was mounted. The host is one session's own server, which is what\n * makes the session id available without the page supplying it.\n *\n * Neither route takes a path. A session has exactly one current plan, and the\n * file both routes act on is the one the pointer names, so a page can address\n * the plan and nothing else; there is no traversal question to answer because\n * there is no path to traverse.\n *\n * The save refuses unless the reader proves they were looking at the content\n * that is still on disk. The agent may rewrite the plan while a tab is open,\n * and silently discarding whichever side finished second is the one outcome\n * worth ruling out.\n */\n\n/** Past this, the plan is reported rather than carried. */\nexport const MAX_PLAN_BYTES = 1024 * 1024;\n\nconst NO_PLAN = 'This session has not written a plan yet.';\nconst STALE_SAVE = 'The plan changed since it was opened.';\nconst NOT_A_FILE = 'The plan file is no longer a regular file, so it was not written.';\n\nexport interface PlanApiOptions {\n /** The session these routes answer for; absent when the host is the hub. */\n sessionId?: string;\n pointers?: PlanPointerPort;\n}\n\nfunction hashOf(content: string): string {\n return createHash('sha256').update(content, 'utf8').digest('hex');\n}\n\n/** Reads the plan as the source view will show it, or says why it cannot. */\nasync function readPlan(\n planPath: string,\n): Promise<Pick<PlanDetailView, 'content' | 'hash' | 'unavailable' | 'reason'>> {\n let raw: Buffer;\n try {\n raw = await fs.readFile(planPath);\n } catch {\n return { content: '', hash: '', unavailable: true, reason: 'the plan file no longer exists' };\n }\n if (raw.byteLength > MAX_PLAN_BYTES) {\n return { content: '', hash: '', unavailable: true, reason: `the plan is larger than ${MAX_PLAN_BYTES} bytes` };\n }\n const content = raw.toString('utf8');\n return { content, hash: hashOf(content), unavailable: false };\n}\n\nexport function createPlanApi(options: PlanApiOptions = {}): Hono {\n const app = new Hono();\n const pointers = options.pointers ?? new PlanPointerService({ env: process.env });\n const pointerFor = (): ReturnType<PlanPointerPort['read']> =>\n options.sessionId === undefined ? undefined : pointers.read(options.sessionId);\n\n app.get(currentPath(), async (context) => {\n const pointer = pointerFor();\n if (pointer === undefined) return context.json({ error: NO_PLAN }, 404);\n const view: PlanDetailView = {\n path: pointer.path,\n title: pointer.title,\n writtenAt: pointer.writtenAt,\n ...(pointer.planId === undefined ? {} : { planId: pointer.planId }),\n ...(await readPlan(pointer.path)),\n };\n return context.json(view);\n });\n\n app.put(contentPath(), async (context) => {\n let request: PlanSaveRequest;\n try {\n request = (await context.req.json()) as PlanSaveRequest;\n } catch {\n return context.json({ error: 'The save body is not JSON.' }, 400);\n }\n if (typeof request.content !== 'string' || typeof request.expectedHash !== 'string') {\n return context.json({ error: 'A save carries the content to write and the hash it was read at.' }, 400);\n }\n const pointer = pointerFor();\n if (pointer === undefined) return context.json({ error: NO_PLAN }, 404);\n\n const current = await readPlan(pointer.path);\n if (current.unavailable) return context.json({ error: current.reason ?? NO_PLAN }, 409);\n if (current.hash !== request.expectedHash) {\n return context.json({ error: STALE_SAVE, hash: current.hash }, 409);\n }\n // The same guard write_plan applies before it writes: a plan whose\n // destination has become a symlink or gained a second name is not written\n // through, whatever the pointer still says.\n const stats = await fs.lstat(pointer.path);\n if (!stats.isFile() || stats.nlink !== 1) return context.json({ error: NOT_A_FILE }, 409);\n\n await fs.writeFile(pointer.path, request.content, { encoding: 'utf8', mode: 0o600 });\n return context.json({ hash: hashOf(request.content) } satisfies PlanSaveView);\n });\n\n return app;\n}\n\n/** The named export a host imports from this package's built entry. */\nexport const api: DoomApi = {\n basePath: API_BASE_PATH,\n start(context: DoomApiContext): DoomApiHandler {\n const app = createPlanApi(context.sessionId === undefined ? {} : { sessionId: context.sessionId });\n return {\n fetch: (request) => app.fetch(request),\n // Nothing outlives a request here; an API that starts a watch or a timer\n // tears it down in this callback.\n close: () => undefined,\n };\n },\n};\n"],"mappings":"wOAoCA,MAAa,EAAiB,QAExB,EAAU,2CAUhB,SAAS,EAAO,EAAyB,CACvC,OAAA,EAAOA,EAAAA,WAAAA,CAAW,QAAQ,CAAC,CAAC,OAAO,EAAS,MAAM,CAAC,CAAC,OAAO,KAAK,CAClE,CAGA,eAAe,EACb,EAC8E,CAC9E,IAAI,EACJ,GAAI,CACF,EAAM,MAAMC,EAAAA,QAAG,SAAS,CAAQ,CAClC,MAAQ,CACN,MAAO,CAAE,QAAS,GAAI,KAAM,GAAI,YAAa,GAAM,OAAQ,gCAAiC,CAC9F,CACA,GAAI,EAAI,WAAA,QACN,MAAO,CAAE,QAAS,GAAI,KAAM,GAAI,YAAa,GAAM,OAAQ,2BAA2B,EAAe,OAAQ,EAE/G,IAAM,EAAU,EAAI,SAAS,MAAM,EACnC,MAAO,CAAE,UAAS,KAAM,EAAO,CAAO,EAAG,YAAa,EAAM,CAC9D,CAEA,SAAgB,EAAc,EAA0B,CAAC,EAAS,CAChE,IAAM,EAAM,IAAIC,EAAAA,KACV,EAAW,EAAQ,UAAY,IAAIC,EAAAA,mBAAmB,CAAE,IAAK,QAAQ,GAAI,CAAC,EAC1E,MACJ,EAAQ,YAAc,IAAA,GAAY,IAAA,GAAY,EAAS,KAAK,EAAQ,SAAS,EA2C/E,OAzCA,EAAI,IAAIC,EAAAA,YAAY,EAAG,KAAO,IAAY,CACxC,IAAM,EAAU,EAAW,EAC3B,GAAI,IAAY,IAAA,GAAW,OAAO,EAAQ,KAAK,CAAE,MAAO,CAAQ,EAAG,GAAG,EACtE,IAAM,EAAuB,CAC3B,KAAM,EAAQ,KACd,MAAO,EAAQ,MACf,UAAW,EAAQ,UACnB,GAAI,EAAQ,SAAW,IAAA,GAAY,CAAC,EAAI,CAAE,OAAQ,EAAQ,MAAO,EACjE,GAAI,MAAM,EAAS,EAAQ,IAAI,CACjC,EACA,OAAO,EAAQ,KAAK,CAAI,CAC1B,CAAC,EAED,EAAI,IAAIC,EAAAA,YAAY,EAAG,KAAO,IAAY,CACxC,IAAI,EACJ,GAAI,CACF,EAAW,MAAM,EAAQ,IAAI,KAAK,CACpC,MAAQ,CACN,OAAO,EAAQ,KAAK,CAAE,MAAO,4BAA6B,EAAG,GAAG,CAClE,CACA,GAAI,OAAO,EAAQ,SAAY,UAAY,OAAO,EAAQ,cAAiB,SACzE,OAAO,EAAQ,KAAK,CAAE,MAAO,kEAAmE,EAAG,GAAG,EAExG,IAAM,EAAU,EAAW,EAC3B,GAAI,IAAY,IAAA,GAAW,OAAO,EAAQ,KAAK,CAAE,MAAO,CAAQ,EAAG,GAAG,EAEtE,IAAM,EAAU,MAAM,EAAS,EAAQ,IAAI,EAC3C,GAAI,EAAQ,YAAa,OAAO,EAAQ,KAAK,CAAE,MAAO,EAAQ,QAAU,CAAQ,EAAG,GAAG,EACtF,GAAI,EAAQ,OAAS,EAAQ,aAC3B,OAAO,EAAQ,KAAK,CAAE,MAAO,wCAAY,KAAM,EAAQ,IAAK,EAAG,GAAG,EAKpE,IAAM,EAAQ,MAAMJ,EAAAA,QAAG,MAAM,EAAQ,IAAI,EAIzC,MAHI,CAAC,EAAM,OAAO,GAAK,EAAM,QAAU,EAAU,EAAQ,KAAK,CAAE,MAAO,mEAAW,EAAG,GAAG,GAExF,MAAMA,EAAAA,QAAG,UAAU,EAAQ,KAAM,EAAQ,QAAS,CAAE,SAAU,OAAQ,KAAM,GAAM,CAAC,EAC5E,EAAQ,KAAK,CAAE,KAAM,EAAO,EAAQ,OAAO,CAAE,CAAwB,EAC9E,CAAC,EAEM,CACT,CAGA,MAAa,EAAe,CAC1B,SAAUK,EAAAA,cACV,MAAM,EAAyC,CAC7C,IAAM,EAAM,EAAc,EAAQ,YAAc,IAAA,GAAY,CAAC,EAAI,CAAE,UAAW,EAAQ,SAAU,CAAC,EACjG,MAAO,CACL,MAAQ,GAAY,EAAI,MAAM,CAAO,EAGrC,UAAa,IAAA,EACf,CACF,CACF"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{PlanPointerService as e}from"../services/planPointer/index.mjs";import{API_BASE_PATH as t,contentPath as n,currentPath as r}from"../types/planApi.mjs";import{createHash as i}from"node:crypto";import a from"node:fs/promises";import{Hono as o}from"hono";const s=1048576,c=`This session has not written a plan yet.`;function l(e){return i(`sha256`).update(e,`utf8`).digest(`hex`)}async function u(e){let t;try{t=await a.readFile(e)}catch{return{content:``,hash:``,unavailable:!0,reason:`the plan file no longer exists`}}if(t.byteLength>1048576)return{content:``,hash:``,unavailable:!0,reason:`the plan is larger than ${s} bytes`};let n=t.toString(`utf8`);return{content:n,hash:l(n),unavailable:!1}}function d(t={}){let i=new o,s=t.pointers??new e({env:process.env}),d=()=>t.sessionId===void 0?void 0:s.read(t.sessionId);return i.get(r(),async e=>{let t=d();if(t===void 0)return e.json({error:c},404);let n={path:t.path,title:t.title,writtenAt:t.writtenAt,...t.planId===void 0?{}:{planId:t.planId},...await u(t.path)};return e.json(n)}),i.put(n(),async e=>{let t;try{t=await e.req.json()}catch{return e.json({error:`The save body is not JSON.`},400)}if(typeof t.content!=`string`||typeof t.expectedHash!=`string`)return e.json({error:`A save carries the content to write and the hash it was read at.`},400);let n=d();if(n===void 0)return e.json({error:c},404);let r=await u(n.path);if(r.unavailable)return e.json({error:r.reason??c},409);if(r.hash!==t.expectedHash)return e.json({error:`The plan changed since it was opened.`,hash:r.hash},409);let i=await a.lstat(n.path);return!i.isFile()||i.nlink!==1?e.json({error:`The plan file is no longer a regular file, so it was not written.`},409):(await a.writeFile(n.path,t.content,{encoding:`utf8`,mode:384}),e.json({hash:l(t.content)}))}),i}const f={basePath:t,start(e){let t=d(e.sessionId===void 0?{}:{sessionId:e.sessionId});return{fetch:e=>t.fetch(e),close:()=>void 0}}};export{s as MAX_PLAN_BYTES,f as api,d as createPlanApi};
|
|
2
|
+
//# sourceMappingURL=planApi.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"planApi.mjs","names":[],"sources":["../../src/controllers/planApi.ts"],"sourcesContent":["import { createHash } from 'node:crypto';\nimport fs from 'node:fs/promises';\n\nimport type { DoomApi, DoomApiContext, DoomApiHandler } from '@agimon-ai/doompi-core/package-api';\nimport { Hono } from 'hono';\n\nimport { PlanPointerService } from '../services/planPointer';\nimport {\n API_BASE_PATH,\n contentPath,\n currentPath,\n type PlanDetailView,\n type PlanSaveRequest,\n type PlanSaveView,\n} from '../types/planApi';\nimport type { PlanPointerPort } from '../types/planPointer';\n\n/**\n * This package's HTTP surface: the session's current plan, and the manual save.\n *\n * Routes are relative to the mount its host gives it, so nothing here repeats\n * where it was mounted. The host is one session's own server, which is what\n * makes the session id available without the page supplying it.\n *\n * Neither route takes a path. A session has exactly one current plan, and the\n * file both routes act on is the one the pointer names, so a page can address\n * the plan and nothing else; there is no traversal question to answer because\n * there is no path to traverse.\n *\n * The save refuses unless the reader proves they were looking at the content\n * that is still on disk. The agent may rewrite the plan while a tab is open,\n * and silently discarding whichever side finished second is the one outcome\n * worth ruling out.\n */\n\n/** Past this, the plan is reported rather than carried. */\nexport const MAX_PLAN_BYTES = 1024 * 1024;\n\nconst NO_PLAN = 'This session has not written a plan yet.';\nconst STALE_SAVE = 'The plan changed since it was opened.';\nconst NOT_A_FILE = 'The plan file is no longer a regular file, so it was not written.';\n\nexport interface PlanApiOptions {\n /** The session these routes answer for; absent when the host is the hub. */\n sessionId?: string;\n pointers?: PlanPointerPort;\n}\n\nfunction hashOf(content: string): string {\n return createHash('sha256').update(content, 'utf8').digest('hex');\n}\n\n/** Reads the plan as the source view will show it, or says why it cannot. */\nasync function readPlan(\n planPath: string,\n): Promise<Pick<PlanDetailView, 'content' | 'hash' | 'unavailable' | 'reason'>> {\n let raw: Buffer;\n try {\n raw = await fs.readFile(planPath);\n } catch {\n return { content: '', hash: '', unavailable: true, reason: 'the plan file no longer exists' };\n }\n if (raw.byteLength > MAX_PLAN_BYTES) {\n return { content: '', hash: '', unavailable: true, reason: `the plan is larger than ${MAX_PLAN_BYTES} bytes` };\n }\n const content = raw.toString('utf8');\n return { content, hash: hashOf(content), unavailable: false };\n}\n\nexport function createPlanApi(options: PlanApiOptions = {}): Hono {\n const app = new Hono();\n const pointers = options.pointers ?? new PlanPointerService({ env: process.env });\n const pointerFor = (): ReturnType<PlanPointerPort['read']> =>\n options.sessionId === undefined ? undefined : pointers.read(options.sessionId);\n\n app.get(currentPath(), async (context) => {\n const pointer = pointerFor();\n if (pointer === undefined) return context.json({ error: NO_PLAN }, 404);\n const view: PlanDetailView = {\n path: pointer.path,\n title: pointer.title,\n writtenAt: pointer.writtenAt,\n ...(pointer.planId === undefined ? {} : { planId: pointer.planId }),\n ...(await readPlan(pointer.path)),\n };\n return context.json(view);\n });\n\n app.put(contentPath(), async (context) => {\n let request: PlanSaveRequest;\n try {\n request = (await context.req.json()) as PlanSaveRequest;\n } catch {\n return context.json({ error: 'The save body is not JSON.' }, 400);\n }\n if (typeof request.content !== 'string' || typeof request.expectedHash !== 'string') {\n return context.json({ error: 'A save carries the content to write and the hash it was read at.' }, 400);\n }\n const pointer = pointerFor();\n if (pointer === undefined) return context.json({ error: NO_PLAN }, 404);\n\n const current = await readPlan(pointer.path);\n if (current.unavailable) return context.json({ error: current.reason ?? NO_PLAN }, 409);\n if (current.hash !== request.expectedHash) {\n return context.json({ error: STALE_SAVE, hash: current.hash }, 409);\n }\n // The same guard write_plan applies before it writes: a plan whose\n // destination has become a symlink or gained a second name is not written\n // through, whatever the pointer still says.\n const stats = await fs.lstat(pointer.path);\n if (!stats.isFile() || stats.nlink !== 1) return context.json({ error: NOT_A_FILE }, 409);\n\n await fs.writeFile(pointer.path, request.content, { encoding: 'utf8', mode: 0o600 });\n return context.json({ hash: hashOf(request.content) } satisfies PlanSaveView);\n });\n\n return app;\n}\n\n/** The named export a host imports from this package's built entry. */\nexport const api: DoomApi = {\n basePath: API_BASE_PATH,\n start(context: DoomApiContext): DoomApiHandler {\n const app = createPlanApi(context.sessionId === undefined ? {} : { sessionId: context.sessionId });\n return {\n fetch: (request) => app.fetch(request),\n // Nothing outlives a request here; an API that starts a watch or a timer\n // tears it down in this callback.\n close: () => undefined,\n };\n },\n};\n"],"mappings":"mQAoCA,MAAa,EAAiB,QAExB,EAAU,2CAUhB,SAAS,EAAO,EAAyB,CACvC,OAAO,EAAW,QAAQ,CAAC,CAAC,OAAO,EAAS,MAAM,CAAC,CAAC,OAAO,KAAK,CAClE,CAGA,eAAe,EACb,EAC8E,CAC9E,IAAI,EACJ,GAAI,CACF,EAAM,MAAM,EAAG,SAAS,CAAQ,CAClC,MAAQ,CACN,MAAO,CAAE,QAAS,GAAI,KAAM,GAAI,YAAa,GAAM,OAAQ,gCAAiC,CAC9F,CACA,GAAI,EAAI,WAAA,QACN,MAAO,CAAE,QAAS,GAAI,KAAM,GAAI,YAAa,GAAM,OAAQ,2BAA2B,EAAe,OAAQ,EAE/G,IAAM,EAAU,EAAI,SAAS,MAAM,EACnC,MAAO,CAAE,UAAS,KAAM,EAAO,CAAO,EAAG,YAAa,EAAM,CAC9D,CAEA,SAAgB,EAAc,EAA0B,CAAC,EAAS,CAChE,IAAM,EAAM,IAAI,EACV,EAAW,EAAQ,UAAY,IAAI,EAAmB,CAAE,IAAK,QAAQ,GAAI,CAAC,EAC1E,MACJ,EAAQ,YAAc,IAAA,GAAY,IAAA,GAAY,EAAS,KAAK,EAAQ,SAAS,EA2C/E,OAzCA,EAAI,IAAI,EAAY,EAAG,KAAO,IAAY,CACxC,IAAM,EAAU,EAAW,EAC3B,GAAI,IAAY,IAAA,GAAW,OAAO,EAAQ,KAAK,CAAE,MAAO,CAAQ,EAAG,GAAG,EACtE,IAAM,EAAuB,CAC3B,KAAM,EAAQ,KACd,MAAO,EAAQ,MACf,UAAW,EAAQ,UACnB,GAAI,EAAQ,SAAW,IAAA,GAAY,CAAC,EAAI,CAAE,OAAQ,EAAQ,MAAO,EACjE,GAAI,MAAM,EAAS,EAAQ,IAAI,CACjC,EACA,OAAO,EAAQ,KAAK,CAAI,CAC1B,CAAC,EAED,EAAI,IAAI,EAAY,EAAG,KAAO,IAAY,CACxC,IAAI,EACJ,GAAI,CACF,EAAW,MAAM,EAAQ,IAAI,KAAK,CACpC,MAAQ,CACN,OAAO,EAAQ,KAAK,CAAE,MAAO,4BAA6B,EAAG,GAAG,CAClE,CACA,GAAI,OAAO,EAAQ,SAAY,UAAY,OAAO,EAAQ,cAAiB,SACzE,OAAO,EAAQ,KAAK,CAAE,MAAO,kEAAmE,EAAG,GAAG,EAExG,IAAM,EAAU,EAAW,EAC3B,GAAI,IAAY,IAAA,GAAW,OAAO,EAAQ,KAAK,CAAE,MAAO,CAAQ,EAAG,GAAG,EAEtE,IAAM,EAAU,MAAM,EAAS,EAAQ,IAAI,EAC3C,GAAI,EAAQ,YAAa,OAAO,EAAQ,KAAK,CAAE,MAAO,EAAQ,QAAU,CAAQ,EAAG,GAAG,EACtF,GAAI,EAAQ,OAAS,EAAQ,aAC3B,OAAO,EAAQ,KAAK,CAAE,MAAO,wCAAY,KAAM,EAAQ,IAAK,EAAG,GAAG,EAKpE,IAAM,EAAQ,MAAM,EAAG,MAAM,EAAQ,IAAI,EAIzC,MAHI,CAAC,EAAM,OAAO,GAAK,EAAM,QAAU,EAAU,EAAQ,KAAK,CAAE,MAAO,mEAAW,EAAG,GAAG,GAExF,MAAM,EAAG,UAAU,EAAQ,KAAM,EAAQ,QAAS,CAAE,SAAU,OAAQ,KAAM,GAAM,CAAC,EAC5E,EAAQ,KAAK,CAAE,KAAM,EAAO,EAAQ,OAAO,CAAE,CAAwB,EAC9E,CAAC,EAEM,CACT,CAGA,MAAa,EAAe,CAC1B,SAAU,EACV,MAAM,EAAyC,CAC7C,IAAM,EAAM,EAAc,EAAQ,YAAc,IAAA,GAAY,CAAC,EAAI,CAAE,UAAW,EAAQ,SAAU,CAAC,EACjG,MAAO,CACL,MAAQ,GAAY,EAAI,MAAM,CAAO,EAGrC,UAAa,IAAA,EACf,CACF,CACF"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
const e=require("../_virtual/_rolldown/runtime.cjs"),t=require("../schemas/plan/config.cjs"),n=require("../services/fableFlow/index.cjs"),r=require("../schemas/plan/planConfig.cjs"),i=require("../services/logSinkTelemetry/index.cjs"),a=require("../services/optionalTeamServices/index.cjs"),o=require("../services/planPointer/index.cjs"),s=require("../services/prompts/index.cjs"),c=require("../types/planApi.cjs");let l=require("@agimon-ai/doompi-config"),ee=require("node:buffer"),u=require("node:crypto"),d=require("node:fs");d=e.__toESM(d,1);let te=require("node:os");te=e.__toESM(te,1);let ne=require("node:path");ne=e.__toESM(ne,1);let f=require("@agimon-ai/doompi-config/configWriter"),p=require("@agimon-ai/doompi-core/author-tools"),m=require("@agimon-ai/doompi-core/config"),re=require("@agimon-ai/doompi-core/narration"),h=require("@agimon-ai/doompi-core/tool-surface"),g=require("@agimon-ai/doompi-core/ui-hub"),_=require("@agimon-ai/doompi-core/voice-tools"),v=require("@agimon-ai/doompi-minor-mode");const ie=`agent-harness-plan-mode`,ae=`agent-harness-plan-document`,oe=`complete_plan`,se=`write_plan`,y=`bash`,b=`find`,x=`grep`,S=`read`,ce=`subagent`,le=`task`,ue=`record_debug_evidence`,de=`run_fable_plan`,C=`implementation-plan`,w=`@agimon-ai/doompi-plan`,fe=`activate`,pe=`deactivate`,T=`warning`,me=`info`,he=`Refused to write the plan because its destination is not a regular file.`,E=`plan.trigger`,ge=`plan.model`,D=`plan.flavor`,_e=`not_found`,ve=`unauthenticated`,ye=`restore_failed`,be=new Set,xe=5e3,Se=`exit`,Ce=`continue`,we=[c.PLAN_REVIEW_TITLE,` 1. ${c.EXIT_PLAN_MODE_CHOICE}`,` 2. ${c.CONTINUE_PLANNING_CHOICE}`,` Type something.`].join(`
|
|
2
|
+
`),O=`${c.PLAN_REVIEW_TITLE} Options: 1, ${c.EXIT_PLAN_MODE_CHOICE}; 2, ${c.CONTINUE_PLANNING_CHOICE}. Please choose one option. You may also answer in your own words.`,Te=(0,re.createNarrationRequest)(O),k=[`open_authoring_file`,...p.AUTHOR_FACADE_TOOL_NAMES],Ee=new Set([`add_directory`,`ask_user_question`,y,oe,b,x,`intercom`,`ls`,v.MINOR_MODE_TOOL_NAME,...k,..._.VOICE_MODE_TOOL_NAMES,S,`search_external_files`,ce,`subagent_supervisor`,`subagent_wait`,le,se]),De=new Set([`edit`,`write`,`mcp`]),Oe=[S,y,x,b,`ls`,`mcp`],A=`leader`,ke=`voice_tool`,Ae=[`off`,`minimal`,`low`,`medium`,`high`,`xhigh`,`max`],je=[`idle`,`draft`,`review`,`completed`,`failed`,`cancelled`,`interrupted`],Me=`@agimon-ai/doompi-plan`,Ne={type:`string`,enum:[`normal`,`debug`,`fable`]},Pe={type:`object`,properties:{flavor:Ne},required:[`flavor`],additionalProperties:!1},Fe={type:`object`,properties:{},additionalProperties:!1},Ie={type:`object`,properties:{active:{type:`boolean`},flavor:{anyOf:[{...Ne},{type:`null`}]},changed:{type:`boolean`}},required:[`active`,`flavor`,`changed`],additionalProperties:!1};function Le(e){return(e.match(/^#\s+(.+?)\s*$/m)?.[1]??C).normalize(`NFKD`).replace(/[\u0300-\u036f]/g,``).toLowerCase().replace(/[^a-z0-9]+/g,`-`).replace(/^-+|-+$/g,``).slice(0,64)||C}function Re(e){let t=new Date(e);return Number.isNaN(t.getTime())?e:t.toLocaleTimeString()}function ze(e){return`${encodeURIComponent(e).slice(0,48)||`session`}--${(0,u.createHash)(`sha256`).update(e).digest(`hex`)}`}function Be(e=new Date,t=(0,u.randomUUID)()){return`${e.toISOString().replace(/[-:]/g,``).replace(/\.\d{3}Z$/,`Z`)}--${t}`}function Ve(e,t){for(let n=e.length-1;n>=0;--n){let r=e[n];if(!r||typeof r!=`object`)continue;let i=r;if(i.type!==`message`||i.message?.role!==`assistant`||!Array.isArray(i.message.content))continue;let a=i.message.content.findIndex(e=>{if(!e||typeof e!=`object`)return!1;let n=e;return n.type===`toolCall`&&n.id===t&&n.name===se});if(!(a<0))return i.message.content.slice(0,a).flatMap(e=>{if(!e||typeof e!=`object`)return[];let t=e;return t.type===`text`&&typeof t.text==`string`?[t.text]:[]}).join(`
|
|
3
|
+
|
|
4
|
+
`).trim()||void 0}}function He(e){return e.reason instanceof Error?e.reason:Error(`Plan write cancelled.`)}async function j(e,t){if(t.aborted)throw He(t);let n,r=new Promise((e,r)=>{n=()=>r(He(t)),t.addEventListener(`abort`,n,{once:!0})});try{return await Promise.race([e(),r])}finally{n&&t.removeEventListener(`abort`,n)}}function M(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function N(e){return typeof e==`string`}function P(e){if(M(e)&&N(e.provider)&&N(e.id)&&!(!e.provider||!e.id||e.provider.length>128||e.id.length>128))return{provider:e.provider,id:e.id}}function F(e){return N(e)&&Ae.includes(e)?e:void 0}function I(e){return N(e)&&je.includes(e)?e:void 0}function L(e){if(Array.isArray(e)&&!e.some(e=>!N(e)))return e.map(e=>e)}function Ue(e){if(!M(e))return;let t=L(e.tools),n=F(e.thinking);if(!t||n===void 0)return;let r=e.model===void 0?void 0:P(e.model);if(e.model===void 0||r)return{tools:t,...r?{model:r}:{},thinking:n}}function We(e){return e===`normal`||e===`debug`||e===`fable`?e:void 0}function R(e){if(e.version!==2)return;let t=e.activeFlavor===void 0?void 0:We(e.activeFlavor);if(e.activeFlavor!==void 0&&!t)return;let n=e.originalSnapshot===void 0?void 0:Ue(e.originalSnapshot);if(e.originalSnapshot!==void 0&&!n)return;let r=e.planId===void 0?void 0:N(e.planId)?e.planId:void 0;if(e.planId!==void 0&&r===void 0)return;let i=e.interruptedFableStage===void 0?void 0:I(e.interruptedFableStage);if(e.interruptedFableStage===void 0||i)return{version:2,...t?{activeFlavor:t}:{},...n?{originalSnapshot:n}:{},...r?{planId:r}:{},...i?{interruptedFableStage:i}:{}}}function z(e){if(e.enabled!==!0&&e.enabled!==!1)return;let t=e.toolsBeforePlanMode===void 0?void 0:L(e.toolsBeforePlanMode);if(e.toolsBeforePlanMode!==void 0&&!t)return;let n=e.modelBeforePlanMode===void 0?void 0:P(e.modelBeforePlanMode);if(e.modelBeforePlanMode!==void 0&&!n)return;let r=e.thinkingBeforePlanMode===void 0?void 0:F(e.thinkingBeforePlanMode);if(e.thinkingBeforePlanMode!==void 0&&r===void 0)return;let i=e.planId===void 0?void 0:N(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 Ge(e){if(M(e))return R(e)??z(e)}function Ke(e){if(!M(e)||!N(e.content)||!N(e.path)||!N(e.sessionId)||!N(e.title)||!N(e.writtenAt))return;let t=e.planId===void 0?void 0:N(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 B(e,t,n){if(!N(e))throw Error(`${t} must be a string.`);let r=e.trim();if(n&&!r)throw Error(`${t} must not be empty.`);if(ee.Buffer.byteLength(r,`utf8`)>4096)throw Error(`${t} exceeds the bounded debug evidence limit.`);return r}function V(e,t){if(!Array.isArray(e)||e.length>32)throw Error(`${t} must be a bounded string array.`);return e.map((e,n)=>B(e,`${t}[${n}]`,!1))}function H(e,t){return e===void 0?``:B(e,t,!1)}function U(e,t){return e===void 0?[]:V(e,t)}function W(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 qe(e){if(!M(e))throw Error(`Debug evidence must be an object.`);W(e,[`issue`,`expectedBehavior`,`reproductionAttempt`,`actualBehavior`,`logs`,`correlatedTraceEvidence`,`processOutput`,`browserConsoleEvidence`,`correlationIds`,`timestamps`,`verifiedFacts`,`hypotheses`,`unavailableEvidence`],`Debug evidence`);let t={issue:B(e.issue,`issue`,!0),expectedBehavior:H(e.expectedBehavior,`expectedBehavior`),reproductionAttempt:H(e.reproductionAttempt,`reproductionAttempt`),actualBehavior:H(e.actualBehavior,`actualBehavior`),logs:U(e.logs,`logs`),correlatedTraceEvidence:U(e.correlatedTraceEvidence,`correlatedTraceEvidence`),processOutput:U(e.processOutput,`processOutput`),browserConsoleEvidence:U(e.browserConsoleEvidence,`browserConsoleEvidence`),correlationIds:U(e.correlationIds,`correlationIds`),timestamps:U(e.timestamps,`timestamps`),verifiedFacts:U(e.verifiedFacts,`verifiedFacts`),hypotheses:U(e.hypotheses,`hypotheses`),unavailableEvidence:U(e.unavailableEvidence,`unavailableEvidence`)};if(ee.Buffer.byteLength(JSON.stringify(t),`utf8`)>32768)throw Error(`Debug evidence exceeds the bounded packet size.`);return t}function Je(e,t){return n=>n.filter(n=>n===oe||n===se?e!==void 0:n===de?e===`fable`:n===ue?e===`debug`:t.has(n)?e===void 0||e===`debug`:e===void 0||!De.has(n))}function G(e){if(e.output=!1,e.progress=!1,e.worktree=!1,Array.isArray(e.parallel))for(let t of e.parallel)G(t);else e.parallel&&G(e.parallel)}function Ye(e){if(e.action!==void 0){e.action===`run`&&(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??[])G(t);for(let t of e.chain??[])G(t)}function Xe(e,t){if(!e?.model&&!e?.thinking)return;let n=e.model??(t?`${t.provider}/${t.id}`:void 0);if(n)return e.thinking?`${n.replace(/:(?:off|minimal|low|medium|high|xhigh|max)$/,``)}:${e.thinking}`:n}function Ze(e,t){if(e.action===`run`)for(let n of e.requests??[])n.model=t}function Qe(e,t){if(e.action===`assign`)for(let n of e.assignments??[])n.model=t}function $e(e){return new Set([`append-step`,`create`,`delete`,`disable`,`eject`,`enable`,`grant-spawn-budget`,`reset`,`schedule`,`schedule-cancel`,`update`,`watchdog.configure`]).has(e??``)}function et(e,t){let n=e.indexOf(`/`);if(n>0)return t.modelRegistry.find(e.slice(0,n),e.slice(n+1));let r=t.modelRegistry.getAvailable().filter(t=>t.id===e);return r.find(e=>e.provider===t.model?.provider)??(r.length===1?r[0]:void 0)}function tt(e=process.env,n=process.cwd()){return t.loadDoomConfig((0,l.loadHarnessState)(e).state.root??n).modes?.planning}function nt(e){let t={key:`p`,label:`plan`,detail:`read-only planning modes`,order:60};return[e?{id:`plan.exit`,path:[t,{key:`e`,label:`exit`,detail:`restore and exit`,tone:`exit`}],action:{name:`plan.exit`}}:{id:`plan.normal`,path:[t,{key:`e`,label:`enter`,detail:`read-only planning`}],action:{name:`plan.normal`}},{id:`plan.debug`,path:[t,{key:`d`,label:`debug`,detail:`adaptive debug planning`}],action:{name:`plan.debug`}},{id:`plan.fable`,path:[t,{key:`f`,label:`fable`,detail:`repository-aware draft`}],action:{name:`plan.fable`}}]}function K(e,t=tt,u=i.createPlanTelemetry(),p={}){let b=!0,x=0,S,C,O,k,De,Ae,je,Ne=()=>{let e=je?.modelRegistry;return e?e.getAvailable().filter(t=>e.hasConfiguredAuth(t)).map(e=>({provider:e.provider,id:e.id})):[]},N=!1,P,F,I,L,Ue,R,z,B,V=!1,H=!1,U=!1,W,G,K=`idle`,q,J,Y,rt,it=Promise.resolve(),at=p.doomIntegrations??!0,ot=p.planPointers??new o.PlanPointerService({env:process.env}),st=new Set(p.debugDiagnosticTools??be),ct,lt=()=>Je(N?P:void 0,st),ut=()=>{ct?.update(lt())},dt=new AbortController,ft=p.fableBroker??{start:(e,t)=>{let n=R;if(!n)throw Error(`The session-bound Fable broker is unavailable.`);return n.start(e,t)},cancel:(e,t)=>{R?.cancel({requester:`@agimon-ai/doompi-plan`,operationId:e,reason:t})}},pt=()=>p.fableBroker!==void 0||R!==void 0;function X(e=x){if(!b||e!==x)throw Error(`Plan runtime is disposed or stale.`)}async function mt(){let e=W;if(!e||!e.modes.list().some(e=>e.descriptor.source===_.DOOM_VOICE_SOURCE&&e.descriptor.id===_.DOOM_VOICE_AUTO_MODE_ID&&e.state.activation===`active`))return!1;try{return await e.narration.request(Te),!0}catch(e){return u.recordWarning(i.PLAN_EVENT.planReviewCompleted,e,{"plan.outcome":`narration_failed`}),!1}}function ht(){return{content:[{type:`text`,text:`${we}\n\nThe choices were spoken through autonomous voice. Stop now and wait for the user's next message; it will arrive as an ordinary user message.`}],details:{answers:[],awaitingResponse:!0,cancelled:!1,delivery:`voice`,voicePrompt:we},terminate:!0}}function gt(){return S??=(async()=>{b=!1,x+=1,dt.abort(Error(`Plan runtime is shutting down.`));let e=J,t=F,n=[];rt?.cancel(`Plan runtime is shutting down.`);let r=[it,...Y?[Y]:[]],i=await Promise.allSettled(r);for(let e of i)e.status===`rejected`&&n.push(e.reason);if(N&&t&&e)try{await Tt(e,t,!0)&&ct?.update(Je(void 0,st))}catch(e){n.push(e)}let a=[()=>xt(e),()=>L?.dispose(),()=>De?.(),()=>k?.dispose(),()=>O?.dispose(),()=>C?.dispose()];for(let e of a)try{e()}catch(e){n.push(e)}L=void 0,W=void 0,De=void 0,k=void 0,O=void 0,C=void 0,Y=void 0,J=void 0,U=!1,N=!1,P=void 0,F=void 0,I=void 0;try{await u.shutdown()}catch(e){n.push(e)}if(n.length>0)throw AggregateError(n,`Plan runtime cleanup failed.`)})(),S}function _t(){b&&O?.update(nt(N))}function vt(){let e=P,t=e===`fable`&&K!==`idle`?` · ${K}`:``;return{activation:N?`active`:`inactive`,condition:K===`failed`?`failed`:K===`interrupted`?`degraded`:`ready`,...N&&e?{detail:`${e}${t} - read only`,color:T,modelContextVariant:e}:{},actions:[{id:fe,enabled:!0},...N?[{id:pe,enabled:!0}]:[{id:pe,enabled:!1,disabledReason:`Plan mode is inactive.`}]]}}function yt(e,t){try{return e.ui.theme.fg(T,t)}catch{return t}}function Z(e){if(!b)return;_t();let t=P,n=N&&t?`plan:${t}`:void 0;e.ui.setStatus(`plan-mode`,n?yt(e,n):void 0),C?.publish(vt())}function bt(e,t){try{ot.write(e.sessionManager.getSessionId(),{path:t.path,title:t.title,writtenAt:t.writtenAt,...t.planId===void 0?{}:{planId:t.planId}})}catch(e){u.recordWarning(i.PLAN_EVENT.planPointerFailed,`The plan pointer was not recorded.`,{"plan.reason":e instanceof Error?e.name:`unknown`})}e.hasUI&&(e.ui.setStatus(c.PLAN_STATUS_KEY,c.formatPlanStatus(t.title,Re(t.writtenAt))),H=!0)}function xt(e){if(e){try{ot.clear(e.sessionManager.getSessionId())}catch{}H&&(H=!1,e.hasUI&&e.ui.setStatus(c.PLAN_STATUS_KEY,void 0))}}function St(){if(!b||!at||!N||!P||!J)return;let e=[...Oe],t={owner:w,allowedTools:e,requiredTools:[y],allowMcpTools:!0,allowedExternalProfiles:P===`fable`?[n.FABLE_PLAN_PROFILE]:[],denyExtensions:!1};L?L.update(t):L=Ue?.register(t)}function Q(){b&&e.appendEntry(ie,{version:2,...N&&P?{activeFlavor:P}:{},...F?{originalSnapshot:{tools:[...F.tools],...F.model?{model:{...F.model}}:{},thinking:F.thinking}}:{},...B?{planId:B}:{},...q?{interruptedFableStage:q}:{}})}function Ct(e,t,n){u.recordWarning(i.PLAN_EVENT.modelResolveFailed,`Planning model ${n}: ${t}`,{[ge]:t,"plan.model.phase":e,"plan.model.reason":n})}async function wt(t){let n=x;X(n);let r=I?.main;if(r){if(r.model){let i=et(r.model,t);if(!i)t.ui.notify(`Planning model not found: ${r.model}`,T),Ct(`apply`,r.model,_e);else{let a=await e.setModel(i);X(n),a||(t.ui.notify(`Planning model is unavailable without authentication: ${r.model}`,T),Ct(`apply`,r.model,ve))}}X(n),r.thinking&&e.setThinkingLevel(r.thinking)}}async function Tt(t,n,r=!1){let a=x;if(r||X(a),!n)return!1;if(n.model){let o=`${n.model.provider}/${n.model.id}`,s=t.modelRegistry.find(n.model.provider,n.model.id);if(!s)return t.ui.notify(`Previous model not found: ${o}`,T),Ct(`restore`,o,_e),!1;try{let n=await e.setModel(s);if(r||X(a),!n)return t.ui.notify(`Previous model is unavailable without authentication: ${o}`,T),Ct(`restore`,o,ve),!1}catch(e){return r||X(a),t.ui.notify(`Previous model could not be restored: ${o}`,T),Ct(`restore`,o,ye),u.recordError(i.PLAN_EVENT.modelResolveFailed,e,{[ge]:o,"plan.model.phase":`restore`,"plan.model.reason":ye}),!1}}return r||X(a),e.setThinkingLevel(n.thinking),!0}function Et(e){N&&P?e.ui.notify(`Already in plan:${P}.`,me):e.ui.notify(`Plan mode is inactive.`,me)}function $(e){let t=x,n=async()=>{X(t);let n=await e();return X(t),n},r=it.then(n,n);return it=r.then(()=>void 0,()=>void 0),r}async function Dt(e){let t=x;if(X(t),!Y&&!K.match(/^(draft|review)$/u))return;(K===`draft`||K===`review`)&&(q=K),rt?.cancel(e);let n=Y;if(n)try{await n,X(t)}catch(e){X(t),u.recordError(i.PLAN_EVENT.modeDisabled,e,{[D]:`fable`,"plan.fable.reason":`cancel_failed`})}X(t),Y=void 0,q&&(K=`interrupted`),J&&Z(J),Q()}async function Ot(n,r,a,o){let s=x;if(X(s),J=n,N){if(P===r){Et(n);return}P===`fable`&&await Dt(`Leaving Fable for plan:${r}.`),X(s),P=r,St(),ut(),Z(n),Q(),u.recordEvent(i.PLAN_EVENT.modeEnabled,{[E]:a,[D]:r,"plan.tool.count":e.getActiveTools().length});return}let c;try{c=t()}catch(e){throw u.recordError(i.PLAN_EVENT.configLoadFailed,e,{[E]:a}),e}o?B??=Be():(z=void 0,V=!1,U=!1,B=Be(),q=void 0,K=`idle`),I=c,F??={tools:[...e.getActiveTools()],...n.model?{model:{provider:n.model.provider,id:n.model.id}}:{},thinking:e.getThinkingLevel()},N=!0,P=r,ut(),St(),await wt(n),X(s),Z(n),Q(),u.recordEvent(i.PLAN_EVENT.modeEnabled,{[E]:a,[D]:r,"plan.tool.count":e.getActiveTools().length,...I?.main?.model?{[ge]:I.main.model}:{},...I?.main?.thinking?{"plan.thinking":I.main.thinking}:{}})}async function kt(e,t){let n=x;if(X(n),!N)return Et(e),!1;await Dt(`Plan mode is exiting.`),X(n);let r=await Tt(e,F);if(X(n),!r)return e.ui.notify(`Previous model was not restored. Plan mode remains read-only.`,T),Z(e),Q(),u.recordWarning(i.PLAN_EVENT.modeDisabled,`Plan mode restoration failed.`,{[E]:t,[D]:P??`unknown`,"plan.restored":!1}),!1;let a=!!z;return ct?.update(Je(void 0,st)),L?.dispose(),L=void 0,N=!1,P=void 0,I=void 0,U=!1,F=void 0,G=void 0,K=`idle`,q=void 0,Z(e),Q(),u.recordEvent(i.PLAN_EVENT.modeDisabled,{[E]:t,[D]:`normal`,"plan.written":a,"plan.restored":!0}),!0}rt=n.createFablePlanFlow({broker:ft,isAuthorized:()=>b&&N&&P===`fable`&&pt(),onStage:e=>{b&&(K=e,(e===`completed`||e===`failed`||e===`cancelled`)&&(q=void 0),J&&Z(J),N&&Q())},onError:e=>{b&&u.recordError(i.PLAN_EVENT.modeEnabled,e,{[D]:`fable`,"plan.fable.reason":`flow_error`})}});async function At(e,t){if(X(),u.recordEvent(i.PLAN_EVENT.planReviewCompleted,{"plan.outcome":e===Se?`exited`:`continued`}),e===Se){let e=await $(()=>kt(t,`plan_approved`));return{content:[{type:`text`,text:e?`The user approved exiting plan mode. Full tool access is restored. Begin implementing the approved plan.`:`The previous model could not be restored. Plan mode remains read-only.`}],details:{exited:e}}}return V=!1,{content:[{type:`text`,text:`The user chose to continue planning. Remain read-only and refine the plan.`}],details:{exited:!1}}}return{services:[e=>{if(e.inject([h.DOOM_TOOL_SURFACE_SERVICE],e=>{let t=(0,h.readDoomToolSurface)(e);if(!t)return;let n=t.register({source:w,restrict:lt()});return ct=n,()=>{ct===n&&(ct=void 0,n.dispose())}}),e.inject([a.DOOM_FABLE_PLAN_SERVICE],e=>{let t=a.readDoomFablePlanService(e);if(t)return R=t,()=>{R===t&&(R=void 0,Y&&rt?.cancel(`The Team Fable service was unloaded.`))}}),e.inject([a.DOOM_SUBAGENT_POLICY_SERVICE],e=>{let t=a.readDoomSubagentPolicyService(e);if(t)return Ue=t,St(),()=>{Ue===t&&(L?.dispose(),L=void 0,Ue=void 0)}}),at&&(e.inject([v.DOOM_MINOR_MODE_CATALOG_SERVICE,re.DOOM_NARRATION_SERVICE],e=>{let t={modes:(0,v.requireMinorModeCatalog)(e),narration:(0,re.requireDoomNarrationService)(e)};return W=t,()=>{W===t&&(W=void 0)}}),e.inject([v.DOOM_MINOR_MODE_CATALOG_SERVICE],e=>{let t=(0,v.registerMinorModeOwner)((0,v.requireMinorModeCatalog)(e),{descriptor:{source:w,id:`plan`,label:`Plan`,description:`Read-only planning with normal, debug, and Fable flavors.`,order:40,actions:[{id:fe,label:`Activate`,description:`Activate plan mode or switch its planning flavor.`,contexts:[`tui`,`headless`],parameters:[{name:`flavor`,label:`Flavor`,kind:`enum`,required:!0,choices:[{value:`normal`,label:`Normal`},{value:`debug`,label:`Debug`},{value:`fable`,label:`Fable`}]}]},{id:pe,label:`Deactivate`,description:`Exit plan mode and restore the previous agent configuration.`,contexts:[`tui`,`headless`],parameters:[]}]},initialState:vt(),async handleAction(e,t,n){if(!b)throw Error(`Plan runtime is disposed.`);if(e===fe){let e=t.flavor;if(e!==`normal`&&e!==`debug`&&e!==`fable`)throw Error(`A valid plan flavor is required.`);return await $(()=>Ot(n.context,e,A,!1)),{message:`Plan mode is active with the ${e} flavor.`}}if(e===pe)return{message:await $(()=>kt(n.context,A))?`Plan mode deactivated.`:`Plan mode remains active.`};throw Error(`Unknown plan mode action: ${e}`)},onError:e=>void u.recordError(i.PLAN_EVENT.configLoadFailed,e)});return C=t,()=>{t.dispose(),C===t&&(C=void 0)}}),e.inject([g.DOOM_UI_HUB_SERVICE],e=>{let n=(0,g.requireDoomUiHub)(e),i=n.registerLeader({source:w,bindings:nt(N)}),a=n.registerConfig({source:w,listSections:()=>{let e=Ne();try{return r.planConfigSections(t(),Ae,e)}catch(t){return r.planConfigSections(void 0,t instanceof Error?t.message:String(t),e)}},handlers:{[m.CONFIG_ACTION.set]:async({fieldId:e,value:t})=>{let n=r.planSettingByFieldId(e);n&&t&&(Ae=void 0,await(0,f.setDoomConfigValue)((0,l.globalDoomConfigPath)(),[...n.keyPath],t))},[m.CONFIG_ACTION.clear]:async({fieldId:e})=>{let t=r.planSettingByFieldId(e);t&&(Ae=void 0,await(0,f.unsetDoomConfigValue)((0,l.globalDoomConfigPath)(),[...t.keyPath]))}},onError:e=>{b&&(Ae=e instanceof Error?e.message:String(e))}});return O=i,k=a,()=>{a.dispose(),i.dispose(),k===a&&(k=void 0),O===i&&(O=void 0)}})),at){let t=e=>{if(X(),e.aborted)throw e.reason instanceof Error?e.reason:Error(`Plan voice transition was cancelled.`)},n={descriptor:{source:Me,id:`plan-activate`,name:`activate_plan`,label:`Activate Plan`,description:`Activate or switch the serialized read-only planning mode flavor.`,order:10,inputSchema:Pe,resultSchema:Ie},execute(e,n){let r=We(M(e)?e.flavor:void 0);if(!r)throw Error(`A valid plan flavor is required.`);return t(n.signal),$(async()=>{t(n.signal);let e=N,i=P;return await Ot(n.context,r,ke,!1),{active:N,flavor:P??r,changed:e!==N||i!==P}})}},r={descriptor:{source:Me,id:`plan-exit`,name:`exit_plan`,label:`Exit Plan`,description:`Exit read-only planning mode and restore the previous agent configuration.`,order:20,inputSchema:Fe,resultSchema:Ie},execute(e,n){return t(n.signal),$(async()=>{t(n.signal);let e=N,r=await kt(n.context,ke);return{active:N,flavor:P??null,changed:e!==N||r}})}};e.inject([_.DOOM_VOICE_TOOLS_SERVICE],e=>{let t=(0,_.requireDoomVoiceToolsService)(e),i=t.register(n),a=t.register(r);return()=>{a.dispose(),i.dispose()}})}at&&e.inject([g.DOOM_UI_HUB_SERVICE],e=>{let t=(0,g.requireDoomUiHub)(e).registerLeaderActions({source:w,handlers:{"plan.normal":()=>{let e=b?J:void 0;return e?$(()=>Ot(e,`normal`,A,!1)):void 0},"plan.exit":()=>{let e=b?J:void 0;return e?$(async()=>{await kt(e,A)}):void 0},"plan.debug":()=>{let e=b?J:void 0;return e?$(()=>Ot(e,`debug`,A,!1)):void 0},"plan.fable":()=>{let e=b?J:void 0;return e?$(()=>Ot(e,`fable`,A,!1)):void 0}},onError:(e,t)=>{b&&(J?.hasUI&&J.ui.notify(`Leader action ${t} failed. Plan mode remains read-only.`,T),u.recordError(i.PLAN_EVENT.modeEnabled,e,{[E]:A,"plan.action":t}))}});return De=t,()=>{t(),De===t&&(De=void 0)}})}],onStop:gt,onDispose:gt,tools:[{name:ue,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(),!N||P!==`debug`)return{content:[{type:`text`,text:`Debug planning is not active.`}],details:{recorded:!1}};try{G=qe(t)}catch(e){throw u.recordWarning(i.PLAN_EVENT.modeEnabled,e,{[D]:`debug`,"plan.debug.reason":`invalid_evidence`}),e}return J&&Z(J),{content:[{type:`text`,text:`Debug evidence recorded as optional planning context.`}],details:{recorded:!0}}}},{name:de,label:`Run Fable Plan`,description:`Send a bounded, sanitized planning packet to the local Fable broker for one repository-aware draft.`,promptSnippet:`Run the local Fable draft with the bounded planning packet`,parameters:{type:`object`,properties:{goal:{type:`array`,items:{type:`string`}},constraints:{type:`array`,items:{type:`string`}},decisions:{type:`array`,items:{type:`string`}},verifiedFindings:{type:`array`,items:{type:`object`}},inferredFindings:{type:`array`,items:{type:`string`}},unresolvedQuestions:{type:`array`,items:{type:`string`}},currentPlan:{type:`string`}},additionalProperties:!1},async execute(e,t,n){let r=x;if(X(r),!N||P!==`fable`)return{content:[{type:`text`,text:`Fable planning is not active.`}],details:{started:!1}};if(!pt())return{content:[{type:`text`,text:`The local Fable broker is unavailable. Fable planning remains disabled.`}],details:{started:!1,errorCode:`broker_unavailable`}};let i=rt;if(!i)throw Error(`Fable planning flow is unavailable.`);let a=i.run(t,n);Y=a;try{let e=await a;return X(r),{content:[{type:`text`,text:e.draft?`Fable draft:\n${e.draft}`:`Fable planning ${e.status}: ${e.errorCode??`no output`}.`}],details:{...e,started:!0}}}finally{b&&r===x&&Y===a&&(Y=void 0)}}},{name:se,label:`Write Plan`,description:`After presenting the complete implementation plan as Markdown in chat, save that visible plan to a unique Markdown file in the configured plans directory.`,promptSnippet:`Save the implementation plan already presented in chat to the configured plans directory`,parameters:{type:`object`,properties:{},additionalProperties:!1},async execute(t,n,r,a,o){let s=x;if(X(s),!N)return{content:[{type:`text`,text:`Plan mode is disabled. Use normal file tools instead.`}],details:{written:!1}};let c=Ve(o.sessionManager.getBranch(),t);if(!c){let e=Error(`Present the complete implementation plan as visible Markdown in chat before calling write_plan.`);throw u.recordError(i.PLAN_EVENT.writePlanFailed,e,{"plan.reason":`missing_visible_plan`}),e}let f=Date.now(),p=(0,l.getHarnessState)().root??o.cwd,m=(0,l.resolvePlanningPlansDirectory)(I?.plansDirectory,p,te.default.homedir()),re=ze(o.sessionManager.getSessionId());B??=Be();let h=z?.planId===B&&ne.default.dirname(z.path)===m?z.path:void 0,g=Le(c),_=B,v=h??ne.default.join(m,`${g}--${_}.md`),ie=new AbortController,oe=Error(`Writing ${v} timed out after ${xe}ms.`),se=setTimeout(()=>ie.abort(oe),xe),y=AbortSignal.any([ie.signal,dt.signal,...r?[r]:[]]),S=`checking`,ce=()=>{a?.({content:[{type:`text`,text:`${S===`checking`?`Checking`:`Writing`} ${v}...`}],details:{phase:S,path:v,startedAt:f}})},le=(e,t)=>{u.recordWarning(i.PLAN_EVENT.writePlanUnsafePath,t,{"plan.phase":S,"plan.refusal":e})};try{ce();try{await j(()=>d.default.promises.mkdir(m,{recursive:!0,mode:448}),y)}catch(e){throw y.aborted?He(y):Error(`Could not create the configured plans directory ${m}.`,{cause:e})}let t=await j(()=>d.default.promises.lstat(m),y),n=await j(()=>d.default.promises.realpath(m),y);if(t.isSymbolicLink()||!t.isDirectory())return le(`unsafe_plans_directory`,`Refused to write the plan because the configured path is unsafe.`),{content:[{type:`text`,text:`Refused to write the plan because the configured path is unsafe.`}],details:{written:!1,path:v,phase:S,durationMs:Date.now()-f}};X(s),S=`writing`,ce();let r;try{for(let e=0;e<3;e+=1)try{let e=h?0:d.default.constants.O_EXCL;r=await j(()=>d.default.promises.open(v,d.default.constants.O_CREAT|d.default.constants.O_WRONLY|d.default.constants.O_NONBLOCK|d.default.constants.O_NOFOLLOW|e,384),y);break}catch(t){if(!(!h&&t instanceof Error&&`code`in t&&String(t.code)===`EEXIST`)||e===2)throw t;_=Be(),B=_,v=ne.default.join(m,`${g}--${_}.md`),Q()}if(!r)throw Error(`Could not allocate a unique plan filename in ${m}.`);let e=await j(()=>d.default.promises.realpath(m),y),t=await j(()=>r.stat(),y);if(e!==n||!t.isFile()||t.nlink!==1)return le(`invalid_destination`,he),{content:[{type:`text`,text:he}],details:{written:!1,path:v,phase:S,durationMs:Date.now()-f}};await j(()=>r.truncate(0),y),await j(()=>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 le(`invalid_destination`,he),{content:[{type:`text`,text:he}],details:{written:!1,path:v,phase:S,durationMs:Date.now()-f}};throw e}finally{await r?.close()}return X(s),z={content:c,path:v,sessionId:re,planId:_,title:g,writtenAt:new Date().toISOString()},e.appendEntry(ae,z),bt(o,z),V=!0,U=!1,u.recordEvent(i.PLAN_EVENT.planWritten,{"plan.written":!0,"plan.bytes":ee.Buffer.byteLength(c,`utf8`),"plan.duration_ms":Date.now()-f,"plan.revised":!!h}),{content:[{type:`text`,text:`Wrote implementation plan to ${v}.`}],details:{written:!0,path:v,phase:S,durationMs:Date.now()-f}}}catch(e){if(!b||s!==x)throw He(dt.signal);let t={"plan.phase":S,"plan.duration_ms":Date.now()-f};if(ie.signal.aborted&&!r?.aborted){let n=Error(`write_plan timed out during ${S} for ${v}.`,{cause:e});throw u.recordError(i.PLAN_EVENT.writePlanTimedOut,n,{...t,"plan.timeout_ms":xe}),n}throw u.recordError(i.PLAN_EVENT.writePlanFailed,e,{...t,"plan.cancelled":r?.aborted??!1}),e}finally{clearTimeout(se)}}},{name:oe,label:`Complete Plan`,description:`Present the saved implementation plan for exit-or-continue approval. Interactive sessions use the normal selector; autonomous voice narrates a plain-text handoff and waits for the next user message.`,promptSnippet:`Request explicit exit-or-continue approval after the saved plan has been presented`,promptGuidelines:[`After write_plan succeeds, call complete_plan without a decision so it can present the review question.`,`When autonomous voice returns an awaiting-response result, stop. Interpret the next ordinary user message, then call complete_plan again with decision "exit" or "continue".`,`Never pass decision "${Se}" without the user explicitly choosing "${c.EXIT_PLAN_MODE_CHOICE}".`],parameters:{type:`object`,properties:{decision:{type:`string`,enum:[Se,Ce]}},additionalProperties:!1},async execute(e,t,n,r,a){let o=x;if(X(o),!N)return{content:[{type:`text`,text:`Plan mode is already disabled.`}],details:{exited:!1}};if(!V)return{content:[{type:`text`,text:`Present the complete plan in chat and save it with write_plan before requesting approval.`}],details:{exited:!1}};if(!a.hasUI)return u.recordEvent(i.PLAN_EVENT.planReviewCompleted,{"plan.outcome":`no_ui`}),{content:[{type:`text`,text:`The plan is complete, but this run cannot ask the user interactively. Remain in plan mode until the user uses SPC p e.`}],details:{exited:!1}};let s=M(t)?t.decision:void 0,l=s===Se||s===Ce;if(U){if(!l)throw Error(`The narrated plan review is waiting for an explicit exit or continue decision.`);return U=!1,At(s,a)}if(s!==void 0)throw Error(`Call complete_plan without a decision before submitting a narrated plan-review answer.`);if(await mt())return X(o),U=!0,ht();let ee=await a.ui.select(c.PLAN_REVIEW_TITLE,[...c.PLAN_REVIEW_OPTIONS]);return X(o),At(ee===`Exit plan mode and start implementation`?Se:Ce,a)}}],events:{tool_call:async(e,t)=>{if(!b||!N)return;let n=P===`debug`&&(e.toolName===ue||st.has(e.toolName))||P===`fable`&&e.toolName===de;if(!Ee.has(e.toolName)&&!n)return{block:!0,reason:`Plan mode blocks the ${e.toolName} tool. Use SPC p e to restore access.`};let r=Xe(I?.subagents,t.model);if(e.toolName===le){r&&Qe(e.input,r);return}if(e.toolName!==ce)return;let i=e.input;if($e(i.action))return{block:!0,reason:`Plan mode blocks subagent management action '${i.action}'. Use SPC p e first.`};Ye(i),r&&Ze(i,r)},before_agent_start:async(e,t)=>{if(!b)return;let n=[];if(N){let e=(0,l.getHarnessState)().root??t.cwd,r=(0,l.resolvePlanningPlansDirectory)(I?.plansDirectory,e,te.default.homedir());n.push(`[PLAN MODE ACTIVE]\nYou are in repository read-only plan mode. The dedicated write_plan tool may write one unique Markdown plan file under ${r}.\n\nExplore the codebase and produce a concrete implementation plan. For every plan, first call subagent with action "agents", cluster the exploration by independent domain, subsystem, or integration boundary, and create a provisional task graph with the task tool. Select specialized agents by matching their names and descriptions to each cluster. Assign every unblocked delegated task through the task tool, and use a one-shot inlineAgent with a focused systemPrompt when no discovered specialist fits. Treat the initial graph as provisional, not as a fixed contract. After findings arrive, review the entire graph at least once and perform one to three review passes in total. In each pass, use the evidence to add, rewrite, delete, cancel, reassign, or change blockedBy relationships for tasks when warranted. Do not keep following tasks that new information has made stale. Stop revising early when the graph is stable, or after the third pass.\n\nEvery plan must end with a delegated planning-draft stage blocked by all exploration and decision tasks. A single-boundary plan gets one planning draft. A complex plan spanning multiple subsystems, domains, packages, apps, or integration boundaries gets two planning drafts concurrently. Use three concurrent drafts instead when the work is cross-layer, migration-sensitive, security-sensitive, or similarly high risk. Assign each draft through the task tool to the discovered "planner" agent with context "fork" so it receives the conversation and gathered evidence. If the planner agent is unavailable, assign the same draft task through the task tool with a focused inlineAgent. All children receive read, Bash, grep, find, ls, and configured MCP tools with artifacts disabled. Bash and MCP tools are for read-only inspection and must not modify files, external systems, or repository state. Doom Team runs asynchronously, so do not poll. After launch, continue non-overlapping exploration or end your turn; completion notifications wake the parent session.\n\nAfter all planning drafts complete, the main agent must compare the candidates, pick the strongest draft, cross-check it against the gathered evidence and the other drafts, resolve conflicts and gaps, and ask the user only for product decisions. A child produces the draft, but the main agent owns and synthesizes the final plan. Do not modify files or repository state except through write_plan. Start the final plan with a meaningful Markdown H1 because write_plan derives the filename from it. Present the complete plan as visible Markdown in chat, then call write_plan with no arguments. After write_plan succeeds, clear the completed durable task graph and call complete_plan without a decision. In an interactive text session, complete_plan uses the normal exit-or-continue selector. Under autonomous voice, it displays and narrates the choices without opening a blocking dialog, then ends the turn. Interpret the user's next ordinary message and call complete_plan again with decision "exit" or "continue". Do not exit plan mode without explicit approval.`,s.buildFlavorPlanningPrompt(P,r,G,K))}if(z){let e=z.content;try{e=await d.default.promises.readFile(z.path,`utf8`)}catch{e=z.content}n.push(`[CURRENT PLAN]\nSource: ${z.path}\n\n${e}`)}if(n.length!==0)return{systemPrompt:`${e.systemPrompt}\n\n${n.join(`
|
|
5
|
+
|
|
6
|
+
`)}`}},context:async e=>{if(b)return{messages:e.messages.filter(e=>e.customType!==`agent-harness-plan-mode-context`)}},session_start:async(e,t)=>{if(!b)return;je=t;let n=++x,r=t.sessionManager.getEntries(),i=r.flatMap(e=>e.type===`custom`&&e.customType===ie?[Ge(e.data)]:[]).filter(e=>e!==void 0).at(-1),a=ze(t.sessionManager.getSessionId()),o=r.flatMap(e=>e.type===`custom`&&e.customType===ae?[Ke(e.data)]:[]).filter(e=>e!==void 0).at(-1),s=F;if(L?.dispose(),L=void 0,z=o?.sessionId===a?o:void 0,z?bt(t,z):xt(t),B=i?.planId??z?.planId,V=!1,U=!1,I=void 0,G=void 0,N=!1,P=void 0,_t(),F=i?.originalSnapshot,q=i?.interruptedFableStage,K=q?`interrupted`:`idle`,J=t,i?.activeFlavor)await Ot(t,i.activeFlavor,`session_restore`,!0),X(n);else{let e=i?.originalSnapshot??s;e&&(await Tt(t,e),X(n)),ut(),X(n),F=void 0,Z(t)}}}}}exports.WRITE_PLAN_TIMEOUT_MS=xe,exports.configurePlanningSubagentInput=Ze,exports.configurePlanningTaskInput=Qe,exports.constrainSubagentInput=Ye,exports.createPlanIdentifier=Be,exports.createPlanModeRuntime=K,exports.isBlockedSubagentManagementAction=$e,exports.loadPlanningModeConfig=tt,exports.parseDebugEvidencePacket=qe,exports.parsePersistedPlanState=Ge,exports.planSessionIdentifier=ze,exports.planTitleSlug=Le,exports.planToolRestriction=Je,exports.planningSubagentModel=Xe,exports.visiblePlanForToolCall=Ve;
|
|
7
|
+
//# sourceMappingURL=planMode.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"planMode.cjs","names":["PLAN_REVIEW_TITLE","EXIT_PLAN_MODE_CHOICE","CONTINUE_PLANNING_CHOICE","createNarrationRequest","AUTHOR_FACADE_TOOL_NAMES","MINOR_MODE_TOOL_NAME","VOICE_MODE_TOOL_NAMES","createHash","randomUUID","Buffer","loadDoomConfig","loadHarnessState","createPlanTelemetry","PlanPointerService","DOOM_VOICE_SOURCE","DOOM_VOICE_AUTO_MODE_ID","PLAN_EVENT","PLAN_STATUS_KEY","formatPlanStatus","FABLE_PLAN_PROFILE","snapshot","createFablePlanFlow","DOOM_TOOL_SURFACE_SERVICE","readDoomToolSurface","DOOM_FABLE_PLAN_SERVICE","readDoomFablePlanService","DOOM_SUBAGENT_POLICY_SERVICE","readDoomSubagentPolicyService","DOOM_MINOR_MODE_CATALOG_SERVICE","DOOM_NARRATION_SERVICE","requireMinorModeCatalog","requireDoomNarrationService","registerMinorModeOwner","DOOM_UI_HUB_SERVICE","requireDoomUiHub","planConfigSections","CONFIG_ACTION","planSettingByFieldId","setDoomConfigValue","globalDoomConfigPath","unsetDoomConfigValue","DOOM_VOICE_TOOLS_SERVICE","requireDoomVoiceToolsService","getHarnessState","resolvePlanningPlansDirectory","os","path","fs","PLAN_REVIEW_OPTIONS","buildFlavorPlanningPrompt","custom"],"sources":["../../src/controllers/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';\n\nimport { globalDoomConfigPath } from '@agimon-ai/doompi-config';\nimport { setDoomConfigValue, unsetDoomConfigValue } from '@agimon-ai/doompi-config/configWriter';\nimport { AUTHOR_FACADE_TOOL_NAMES } from '@agimon-ai/doompi-core/author-tools';\nimport { CONFIG_ACTION, type DoomConfigContributionHandle } from '@agimon-ai/doompi-core/config';\nimport type { LeaderBinding, DoomLeaderContributionHandle } from '@agimon-ai/doompi-core/leader';\nimport {\n createNarrationRequest,\n DOOM_NARRATION_SERVICE,\n type DoomNarrationService,\n requireDoomNarrationService,\n} from '@agimon-ai/doompi-core/narration';\nimport type { PiPluginContributions } from '@agimon-ai/doompi-core/pi-extension';\nimport {\n DOOM_TOOL_SURFACE_SERVICE,\n type DoomToolRestriction,\n type DoomToolRestrictionHandle,\n readDoomToolSurface,\n} from '@agimon-ai/doompi-core/tool-surface';\nimport { DOOM_UI_HUB_SERVICE, requireDoomUiHub } from '@agimon-ai/doompi-core/ui-hub';\nimport {\n DOOM_VOICE_AUTO_MODE_ID,\n DOOM_VOICE_SOURCE,\n DOOM_VOICE_TOOLS_SERVICE,\n requireDoomVoiceToolsService,\n VOICE_MODE_TOOL_NAMES,\n type VoiceToolDefinition,\n} from '@agimon-ai/doompi-core/voice-tools';\nimport {\n DOOM_MINOR_MODE_CATALOG_SERVICE,\n MINOR_MODE_TOOL_NAME,\n type MinorModeCatalogService,\n type MinorModeOwnerHandle,\n type MinorModeState,\n registerMinorModeOwner,\n requireMinorModeCatalog,\n} from '@agimon-ai/doompi-minor-mode';\nimport type { Context } from '@deepseek-ai/cordis';\nimport type { ExtensionAPI, ExtensionContext, ToolCallEvent } from '@earendil-works/pi-coding-agent';\n\nimport {\n getHarnessState,\n loadDoomConfig,\n loadHarnessState,\n type PlanningAgentConfig,\n type PlanningModeConfig,\n type PlanningThinkingLevel,\n resolvePlanningPlansDirectory,\n} from '../schemas/plan/config';\nimport { type PlanModelChoice, planConfigSections, planSettingByFieldId } from '../schemas/plan/planConfig';\nimport {\n createFablePlanFlow,\n FABLE_PLAN_PROFILE,\n FABLE_PLAN_REQUESTER,\n type FablePlanBroker,\n type FablePlanResult,\n type FableStage,\n} from '../services/fableFlow';\nimport { createPlanTelemetry, PLAN_EVENT, type PlanTelemetry } from '../services/logSinkTelemetry';\nimport {\n DOOM_FABLE_PLAN_SERVICE,\n DOOM_SUBAGENT_POLICY_SERVICE,\n type DoomFablePlanService,\n type DoomSubagentPolicyService,\n readDoomFablePlanService,\n readDoomSubagentPolicyService,\n type SubagentPolicyHandle,\n} from '../services/optionalTeamServices';\nimport { PlanPointerService } from '../services/planPointer';\nimport { buildFlavorPlanningPrompt, type DebugEvidencePacket, type PlanningFlavor } from '../services/prompts';\nimport {\n CONTINUE_PLANNING_CHOICE,\n EXIT_PLAN_MODE_CHOICE,\n formatPlanStatus,\n PLAN_REVIEW_OPTIONS,\n PLAN_REVIEW_TITLE,\n PLAN_STATUS_KEY,\n} from '../types/planApi';\nimport type { PlanPointerPort } from '../types/planPointer';\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;\nconst MODE_ID = 'plan';\nconst MODE_LABEL = 'Plan';\nconst READ_ONLY_DETAIL = 'read only';\nconst MODE_ORDER = 40;\nconst MODE_ACTIVATE_ACTION = 'activate';\nconst MODE_DEACTIVATE_ACTION = 'deactivate';\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_DECISION = 'exit';\nconst CONTINUE_PLAN_DECISION = 'continue';\nconst PLAN_REVIEW_PROMPT = [\n PLAN_REVIEW_TITLE,\n ` 1. ${EXIT_PLAN_MODE_CHOICE}`,\n ` 2. ${CONTINUE_PLANNING_CHOICE}`,\n ' Type something.',\n].join('\\n');\nconst PLAN_REVIEW_NARRATION = `${PLAN_REVIEW_TITLE} Options: 1, ${EXIT_PLAN_MODE_CHOICE}; 2, ${CONTINUE_PLANNING_CHOICE}. Please choose one option. You may also answer in your own words.`;\nconst PLAN_REVIEW_NARRATION_REQUEST = createNarrationRequest(PLAN_REVIEW_NARRATION)!;\nconst PLAN_REVIEW_WAIT_MESSAGE =\n \"The choices were spoken through autonomous voice. Stop now and wait for the user's next message; it will arrive as an ordinary user message.\";\nconst AUTHOR_MODE_TOOL_NAMES = ['open_authoring_file', ...AUTHOR_FACADE_TOOL_NAMES] as const;\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 MINOR_MODE_TOOL_NAME,\n ...AUTHOR_MODE_TOOL_NAMES,\n ...VOICE_MODE_TOOL_NAMES,\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_EXCLUDED_TOOLS = new Set(['edit', 'write', MCP_TOOL]);\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_MODE_TRIGGER_VOICE_TOOL = 'voice_tool' 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];\nconst PLAN_VOICE_SOURCE = '@agimon-ai/doompi-plan';\nconst PLAN_VOICE_ACTIVATE_ID = 'plan-activate';\nconst PLAN_VOICE_EXIT_ID = 'plan-exit';\nconst PLAN_VOICE_ACTIVATE_NAME = 'activate_plan';\nconst PLAN_VOICE_EXIT_NAME = 'exit_plan';\nconst PLAN_VOICE_FLAVORS = ['normal', 'debug', 'fable'] as const;\nconst PLAN_VOICE_FLAVOR_SCHEMA = { type: 'string', enum: [...PLAN_VOICE_FLAVORS] } as const;\nconst PLAN_VOICE_ACTIVATE_INPUT_SCHEMA = {\n type: 'object',\n properties: { flavor: PLAN_VOICE_FLAVOR_SCHEMA },\n required: ['flavor'],\n additionalProperties: false,\n} as const;\nconst PLAN_VOICE_EXIT_INPUT_SCHEMA = {\n type: 'object',\n properties: {},\n additionalProperties: false,\n} as const;\nconst PLAN_VOICE_RESULT_SCHEMA = {\n type: 'object',\n properties: {\n active: { type: 'boolean' },\n flavor: { anyOf: [{ ...PLAN_VOICE_FLAVOR_SCHEMA }, { type: 'null' }] },\n changed: { type: 'boolean' },\n },\n required: ['active', 'flavor', 'changed'],\n additionalProperties: false,\n} as const;\nexport type { PlanningFlavor } from '../services/prompts';\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 | typeof PLAN_MODE_TRIGGER_VOICE_TOOL;\n\nexport interface PlanModeExtensionOptions {\n fableBroker?: FablePlanBroker;\n debugDiagnosticTools?: readonly string[];\n doomIntegrations?: boolean;\n /** Where the written plan is recorded for the session's API server to find. */\n planPointers?: PlanPointerPort;\n}\n\ninterface PlanVoiceReviewServices {\n readonly modes: Pick<MinorModeCatalogService, 'list'>;\n readonly narration: Pick<DoomNarrationService, 'request'>;\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\n/**\n * How the status line words when a plan was written. A malformed timestamp is\n * shown as it came rather than as an invalid date: the stamp's other job is to\n * change on a rewrite, and it can still do that.\n */\nfunction planStampOf(writtenAt: string): string {\n const at = new Date(writtenAt);\n return Number.isNaN(at.getTime()) ? writtenAt : at.toLocaleTimeString();\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 MutableTaskAssignment = {\n [key: string]: unknown;\n model?: string;\n};\n\ntype MutableTaskInput = {\n action?: string;\n assignments?: MutableTaskAssignment[];\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\n/**\n * The only thing plan mode does to the tool surface: hide what planning must not touch.\n *\n * `flavor` undefined means plan mode is off, which still hides plan's own tools\n * because they are registered for the whole session and only mean something inside it.\n */\nexport function planToolRestriction(\n flavor: PlanningFlavor | undefined,\n diagnosticTools: ReadonlySet<string>,\n): DoomToolRestriction {\n return (incoming) =>\n incoming.filter((name) => {\n if (name === COMPLETE_PLAN_TOOL || name === WRITE_PLAN_TOOL) return flavor !== undefined;\n if (name === RUN_FABLE_PLAN_TOOL) return flavor === 'fable';\n if (name === RECORD_DEBUG_EVIDENCE_TOOL) return flavor === 'debug';\n if (diagnosticTools.has(name)) return flavor === undefined || flavor === 'debug';\n if (flavor === undefined) return true;\n return !PLAN_MODE_EXCLUDED_TOOLS.has(name);\n });\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 (input.action === 'run') input.artifacts = false;\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') return;\n for (const assignment of input.assignments ?? []) assignment.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 * One entry on `e` rather than an enter row beside an exit row: only one of the\n * two ever does anything, and a menu that prints both makes the reader check the\n * mode line to find out which. `d` and `f` stay on their own keys because they\n * are flavors of the same mode, reachable whether or not it is already on.\n */\nfunction planLeaderBindings(active: boolean): LeaderBinding[] {\n const group = {\n key: 'p',\n label: 'plan',\n detail: 'read-only planning modes',\n order: PLAN_LEADER_GROUP_ORDER,\n } as const;\n return [\n active\n ? {\n id: 'plan.exit',\n path: [group, { key: 'e', label: 'exit', detail: 'restore and exit', tone: 'exit' }],\n action: { name: 'plan.exit' },\n }\n : {\n id: 'plan.normal',\n path: [group, { key: 'e', label: 'enter', 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 ];\n}\n\nexport function createPlanModeRuntime(\n pi: ExtensionAPI,\n planningConfigProvider: PlanningConfigProvider = loadPlanningModeConfig,\n telemetry: PlanTelemetry = createPlanTelemetry(),\n options: PlanModeExtensionOptions = {},\n): Omit<PiPluginContributions, 'tools'> & { tools: Parameters<ExtensionAPI['registerTool']>[0][] } {\n let active = true;\n let sessionGeneration = 0;\n let shutdownPromise: Promise<void> | undefined;\n let modeOwner: Pick<MinorModeOwnerHandle, 'publish' | 'dispose'> | undefined;\n let leaderContribution: DoomLeaderContributionHandle | undefined;\n let planConfigContribution: DoomConfigContributionHandle | undefined;\n let disposeLeaderActions: (() => void) | undefined;\n let planConfigNotice: string | undefined;\n /** The session context, kept only to read its model registry for the config panel. */\n let configSessionCtx: ExtensionContext | undefined;\n const sessionModels = (): PlanModelChoice[] => {\n const registry = configSessionCtx?.modelRegistry;\n if (!registry) return [];\n return registry\n .getAvailable()\n .filter((model) => registry.hasConfiguredAuth(model))\n .map((model) => ({ provider: model.provider, id: model.id }));\n };\n\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 subagentPolicyService: DoomSubagentPolicyService | undefined;\n let fablePlanService: DoomFablePlanService | undefined;\n let currentPlan: PlanDocument | undefined;\n let activePlanId: string | undefined;\n let planReadyForReview = false;\n /** Whether this runtime has published the plan status a cockpit keys its group off. */\n let planAnnounced = false;\n let awaitingVoicePlanDecision = false;\n let voiceReviewServices: PlanVoiceReviewServices | undefined;\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 fableFlow: ReturnType<typeof createFablePlanFlow> | undefined;\n let transitionQueue: Promise<void> = Promise.resolve();\n const doomIntegrations = options.doomIntegrations ?? true;\n const planPointers: PlanPointerPort = options.planPointers ?? new PlanPointerService({ env: process.env });\n const diagnosticTools = new Set(options.debugDiagnosticTools ?? DEBUG_DIAGNOSTIC_TOOLS);\n let toolRestriction: DoomToolRestrictionHandle | undefined;\n const currentToolRestriction = (): DoomToolRestriction =>\n planToolRestriction(enabled ? activeFlavor : undefined, diagnosticTools);\n const applyToolRestriction = (): void => {\n toolRestriction?.update(currentToolRestriction());\n };\n\n const runtimeAbortController = new AbortController();\n\n const fableBroker: FablePlanBroker = options.fableBroker ?? {\n start: (request, signal) => {\n const service = fablePlanService;\n if (!service) throw new Error('The session-bound Fable broker is unavailable.');\n return service.start(request, signal);\n },\n cancel: (operationId, reason) => {\n fablePlanService?.cancel({ requester: FABLE_PLAN_REQUESTER, operationId, reason });\n },\n };\n const isFableBrokerAvailable = (): boolean => options.fableBroker !== undefined || fablePlanService !== undefined;\n\n function assertRuntimeActive(generation = sessionGeneration): void {\n if (!active || generation !== sessionGeneration) throw new Error('Plan runtime is disposed or stale.');\n }\n\n async function requestVoicePlanReview(): Promise<boolean> {\n const services = voiceReviewServices;\n if (\n !services ||\n !services.modes\n .list()\n .some(\n (record) =>\n record.descriptor.source === DOOM_VOICE_SOURCE &&\n record.descriptor.id === DOOM_VOICE_AUTO_MODE_ID &&\n record.state.activation === 'active',\n )\n ) {\n return false;\n }\n try {\n await services.narration.request(PLAN_REVIEW_NARRATION_REQUEST);\n return true;\n } catch (error) {\n void telemetry.recordWarning(PLAN_EVENT.planReviewCompleted, error, {\n 'plan.outcome': 'narration_failed',\n });\n return false;\n }\n }\n\n function voicePlanReviewResult(): {\n readonly content: Array<{ readonly type: 'text'; readonly text: string }>;\n readonly details: {\n readonly answers: readonly [];\n readonly awaitingResponse: true;\n readonly cancelled: false;\n readonly delivery: 'voice';\n readonly voicePrompt: string;\n };\n readonly terminate: true;\n } {\n return {\n content: [{ type: 'text', text: `${PLAN_REVIEW_PROMPT}\\n\\n${PLAN_REVIEW_WAIT_MESSAGE}` }],\n details: {\n answers: [],\n awaitingResponse: true,\n cancelled: false,\n delivery: 'voice',\n voicePrompt: PLAN_REVIEW_PROMPT,\n },\n terminate: true,\n };\n }\n\n function shutdownPlanRuntime(): Promise<void> {\n shutdownPromise ??= (async () => {\n active = false;\n sessionGeneration += 1;\n runtimeAbortController.abort(new Error('Plan runtime is shutting down.'));\n const cleanupContext = activeContext;\n const cleanupSnapshot = planSnapshot;\n const cleanupErrors: unknown[] = [];\n fableFlow?.cancel('Plan runtime is shutting down.');\n const pendingWork = [transitionQueue, ...(activeFableRun ? [activeFableRun] : [])];\n const settledWork = await Promise.allSettled(pendingWork);\n for (const result of settledWork) {\n if (result.status === 'rejected') cleanupErrors.push(result.reason);\n }\n\n if (enabled && cleanupSnapshot && cleanupContext) {\n try {\n const restored = await restoreMainAgent(cleanupContext, cleanupSnapshot, true);\n if (restored) {\n toolRestriction?.update(planToolRestriction(undefined, diagnosticTools));\n }\n } catch (error) {\n cleanupErrors.push(error);\n }\n }\n\n const disposers = [\n () => retirePlanDocument(cleanupContext),\n () => capabilityCeiling?.dispose(),\n () => disposeLeaderActions?.(),\n () => planConfigContribution?.dispose(),\n () => leaderContribution?.dispose(),\n () => modeOwner?.dispose(),\n ];\n for (const disposeResource of disposers) {\n try {\n disposeResource();\n } catch (error) {\n cleanupErrors.push(error);\n }\n }\n\n capabilityCeiling = undefined;\n voiceReviewServices = undefined;\n disposeLeaderActions = undefined;\n planConfigContribution = undefined;\n leaderContribution = undefined;\n modeOwner = undefined;\n activeFableRun = undefined;\n activeContext = undefined;\n awaitingVoicePlanDecision = false;\n enabled = false;\n activeFlavor = undefined;\n planSnapshot = undefined;\n activePlanningConfig = undefined;\n try {\n await telemetry.shutdown();\n } catch (error) {\n cleanupErrors.push(error);\n }\n if (cleanupErrors.length > 0) throw new AggregateError(cleanupErrors, 'Plan runtime cleanup failed.');\n })();\n return shutdownPromise;\n }\n\n /** Identical bindings are dropped by the handle, so this can ride along with every state change. */\n function updateLeader(): void {\n if (!active) return;\n leaderContribution?.update(planLeaderBindings(enabled));\n }\n\n function currentPlanModeState(): MinorModeState {\n const flavor = activeFlavor;\n const stage = flavor === 'fable' && fableStage !== 'idle' ? ` · ${fableStage}` : '';\n return {\n activation: enabled ? 'active' : 'inactive',\n condition: fableStage === 'failed' ? 'failed' : fableStage === 'interrupted' ? 'degraded' : 'ready',\n ...(enabled && flavor\n ? { detail: `${flavor}${stage} - ${READ_ONLY_DETAIL}`, color: WARNING_STYLE, modelContextVariant: flavor }\n : {}),\n actions: [\n { id: MODE_ACTIVATE_ACTION, enabled: true },\n ...(enabled\n ? [{ id: MODE_DEACTIVATE_ACTION, enabled: true } as const]\n : [{ id: MODE_DEACTIVATE_ACTION, enabled: false, disabledReason: 'Plan mode is inactive.' } as const]),\n ],\n };\n }\n\n /**\n * Pi exposes `ui.theme` as a proxy over a global the TUI installs, so every\n * property access throws `Theme not initialized` in a headless runtime (an\n * SDK child, a web-hosted session). Colour on a status label is not worth\n * failing the hook that calls this, so styling is best effort and the plain\n * label is the fallback.\n */\n function styleStatus(ctx: ExtensionContext, status: string): string {\n try {\n return ctx.ui.theme.fg(WARNING_STYLE, status);\n } catch {\n return status;\n }\n }\n\n function updateStatus(ctx: ExtensionContext): void {\n if (!active) return;\n updateLeader();\n const flavor = activeFlavor;\n const status = enabled && flavor ? `plan:${flavor}` : undefined;\n ctx.ui.setStatus(PLAN_MODE_STATUS_KEY, status ? styleStatus(ctx, status) : undefined);\n modeOwner?.publish(currentPlanModeState());\n }\n\n /**\n * Announces the written plan: the pointer for the session's API server, and\n * the status the cockpit's activity group appears on.\n *\n * This deliberately outlives plan mode. Exiting is when the agent starts\n * implementing, which is exactly when the plan is most worth having open, so\n * the announcement is retired with the session rather than with the mode.\n * The stamp is what makes the line change on a rewrite, which is how an open\n * tab learns to re-read.\n *\n * A failure here is reported and dropped: the plan is on disk either way,\n * and losing a cockpit view is not worth failing the write that produced it.\n */\n function announcePlanDocument(ctx: ExtensionContext, plan: PlanDocument): void {\n try {\n planPointers.write(ctx.sessionManager.getSessionId(), {\n path: plan.path,\n title: plan.title,\n writtenAt: plan.writtenAt,\n ...(plan.planId === undefined ? {} : { planId: plan.planId }),\n });\n } catch (error) {\n void telemetry.recordWarning(PLAN_EVENT.planPointerFailed, 'The plan pointer was not recorded.', {\n 'plan.reason': error instanceof Error ? error.name : 'unknown',\n });\n }\n if (!ctx.hasUI) return;\n ctx.ui.setStatus(PLAN_STATUS_KEY, formatPlanStatus(plan.title, planStampOf(plan.writtenAt)));\n planAnnounced = true;\n }\n\n /**\n * Retires the announcement; a session that wrote no plan has nothing to\n * retire.\n *\n * The status is only cleared when this runtime published one. A cockpit\n * records a status key the first time it hears of it and never drops it\n * again, so clearing a key it has not seen is not a no-op there: it puts the\n * key on the session as an empty string, which is enough to show the plan\n * group on a session that has written nothing.\n */\n function retirePlanDocument(ctx: ExtensionContext | undefined): void {\n if (!ctx) return;\n try {\n planPointers.clear(ctx.sessionManager.getSessionId());\n } catch {\n // A pointer that cannot be removed is stale data for a session id that\n // will not come back, not a reason to fail a shutdown.\n }\n if (!planAnnounced) return;\n planAnnounced = false;\n if (ctx.hasUI) ctx.ui.setStatus(PLAN_STATUS_KEY, undefined);\n }\n\n function updateCapabilityCeiling(): void {\n if (!active || !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],\n allowMcpTools: true,\n allowedExternalProfiles,\n denyExtensions: false,\n };\n if (capabilityCeiling) capabilityCeiling.update(policy);\n else capabilityCeiling = subagentPolicyService?.register(policy);\n }\n\n function persistState(): void {\n if (!active) return;\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 generation = sessionGeneration;\n assertRuntimeActive(generation);\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 {\n const authenticated = await pi.setModel(model);\n assertRuntimeActive(generation);\n if (!authenticated) {\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 }\n assertRuntimeActive(generation);\n if (config.thinking) pi.setThinkingLevel(config.thinking);\n }\n\n async function restoreMainAgent(\n ctx: ExtensionContext,\n snapshot: PlanSnapshot | undefined,\n allowInactive = false,\n ): Promise<boolean> {\n const generation = sessionGeneration;\n if (!allowInactive) assertRuntimeActive(generation);\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 const authenticated = await pi.setModel(model);\n if (!allowInactive) assertRuntimeActive(generation);\n if (!authenticated) {\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 if (!allowInactive) assertRuntimeActive(generation);\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 if (!allowInactive) assertRuntimeActive(generation);\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 generation = sessionGeneration;\n const run = async (): Promise<T> => {\n assertRuntimeActive(generation);\n const result = await transition();\n assertRuntimeActive(generation);\n return result;\n };\n const queued = transitionQueue.then(run, run);\n transitionQueue = queued.then(\n () => undefined,\n () => undefined,\n );\n return queued;\n }\n\n async function cancelFableOperation(reason: string): Promise<void> {\n const generation = sessionGeneration;\n assertRuntimeActive(generation);\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 assertRuntimeActive(generation);\n } catch (error) {\n assertRuntimeActive(generation);\n void telemetry.recordError(PLAN_EVENT.modeDisabled, error, {\n [PLAN_FLAVOR_ATTRIBUTE]: 'fable',\n 'plan.fable.reason': 'cancel_failed',\n });\n }\n }\n assertRuntimeActive(generation);\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 const generation = sessionGeneration;\n assertRuntimeActive(generation);\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 assertRuntimeActive(generation);\n activeFlavor = flavor;\n updateCapabilityCeiling();\n applyToolRestriction();\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 awaitingVoicePlanDecision = 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 applyToolRestriction();\n updateCapabilityCeiling();\n await applyMainPlanningConfig(ctx);\n assertRuntimeActive(generation);\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 const generation = sessionGeneration;\n assertRuntimeActive(generation);\n if (!enabled) {\n reportCurrentFlavor(ctx);\n return false;\n }\n await cancelFableOperation('Plan mode is exiting.');\n assertRuntimeActive(generation);\n const snapshot = planSnapshot;\n const restored = await restoreMainAgent(ctx, snapshot);\n assertRuntimeActive(generation);\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 toolRestriction?.update(planToolRestriction(undefined, diagnosticTools));\n capabilityCeiling?.dispose();\n capabilityCeiling = undefined;\n enabled = false;\n activeFlavor = undefined;\n activePlanningConfig = undefined;\n awaitingVoicePlanDecision = false;\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 fableFlow = createFablePlanFlow({\n broker: fableBroker,\n isAuthorized: () => active && enabled && activeFlavor === 'fable' && isFableBrokerAvailable(),\n onStage: (stage) => {\n if (!active) return;\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 if (!active) return;\n void telemetry.recordError(PLAN_EVENT.modeEnabled, error, {\n [PLAN_FLAVOR_ATTRIBUTE]: 'fable',\n 'plan.fable.reason': 'flow_error',\n });\n },\n });\n\n async function applyPlanReviewDecision(\n decision: typeof EXIT_PLAN_DECISION | typeof CONTINUE_PLAN_DECISION,\n ctx: ExtensionContext,\n ) {\n assertRuntimeActive();\n void telemetry.recordEvent(PLAN_EVENT.planReviewCompleted, {\n 'plan.outcome': decision === EXIT_PLAN_DECISION ? 'exited' : 'continued',\n });\n if (decision === EXIT_PLAN_DECISION) {\n const exited = await queueTransition(() => exitPlanMode(ctx, PLAN_MODE_TRIGGER_PLAN_APPROVED));\n return {\n content: [\n {\n type: 'text' as const,\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: [\n { type: 'text' as const, text: 'The user chose to continue planning. Remain read-only and refine the plan.' },\n ],\n details: { exited: false },\n };\n }\n\n return {\n services: [\n (cordis: Context) => {\n cordis.inject([DOOM_TOOL_SURFACE_SERVICE], (surfaceContext) => {\n const surface = readDoomToolSurface(surfaceContext);\n if (!surface) return undefined;\n const handle = surface.register({ source: PLAN_LEADER_SOURCE, restrict: currentToolRestriction() });\n toolRestriction = handle;\n return () => {\n if (toolRestriction !== handle) return;\n toolRestriction = undefined;\n handle.dispose();\n };\n });\n cordis.inject([DOOM_FABLE_PLAN_SERVICE], (serviceContext) => {\n const service = readDoomFablePlanService(serviceContext);\n if (!service) return undefined;\n fablePlanService = service;\n return () => {\n if (fablePlanService !== service) return;\n fablePlanService = undefined;\n if (activeFableRun) fableFlow?.cancel('The Team Fable service was unloaded.');\n };\n });\n cordis.inject([DOOM_SUBAGENT_POLICY_SERVICE], (serviceContext) => {\n const service = readDoomSubagentPolicyService(serviceContext);\n if (!service) return undefined;\n subagentPolicyService = service;\n updateCapabilityCeiling();\n return () => {\n if (subagentPolicyService !== service) return;\n capabilityCeiling?.dispose();\n capabilityCeiling = undefined;\n subagentPolicyService = undefined;\n };\n });\n if (doomIntegrations) {\n cordis.inject([DOOM_MINOR_MODE_CATALOG_SERVICE, DOOM_NARRATION_SERVICE], (voiceContext) => {\n const services: PlanVoiceReviewServices = {\n modes: requireMinorModeCatalog(voiceContext),\n narration: requireDoomNarrationService(voiceContext),\n };\n voiceReviewServices = services;\n return () => {\n if (voiceReviewServices === services) voiceReviewServices = undefined;\n };\n });\n cordis.inject([DOOM_MINOR_MODE_CATALOG_SERVICE], (modeContext) => {\n const owner = registerMinorModeOwner<ExtensionContext>(requireMinorModeCatalog(modeContext), {\n descriptor: {\n source: PLAN_LEADER_SOURCE,\n id: MODE_ID,\n label: MODE_LABEL,\n description: 'Read-only planning with normal, debug, and Fable flavors.',\n order: MODE_ORDER,\n actions: [\n {\n id: MODE_ACTIVATE_ACTION,\n label: 'Activate',\n description: 'Activate plan mode or switch its planning flavor.',\n contexts: ['tui', 'headless'],\n parameters: [\n {\n name: 'flavor',\n label: 'Flavor',\n kind: 'enum',\n required: true,\n choices: [\n { value: 'normal', label: 'Normal' },\n { value: 'debug', label: 'Debug' },\n { value: 'fable', label: 'Fable' },\n ],\n },\n ],\n },\n {\n id: MODE_DEACTIVATE_ACTION,\n label: 'Deactivate',\n description: 'Exit plan mode and restore the previous agent configuration.',\n contexts: ['tui', 'headless'],\n parameters: [],\n },\n ],\n },\n initialState: currentPlanModeState(),\n async handleAction(actionId, argumentsValue, execution) {\n if (!active) throw new Error('Plan runtime is disposed.');\n if (actionId === MODE_ACTIVATE_ACTION) {\n const flavor = argumentsValue.flavor;\n if (flavor !== 'normal' && flavor !== 'debug' && flavor !== 'fable') {\n throw new Error('A valid plan flavor is required.');\n }\n await queueTransition(() =>\n activateFlavor(execution.context, flavor, PLAN_MODE_TRIGGER_LEADER, false),\n );\n return { message: `Plan mode is active with the ${flavor} flavor.` };\n }\n if (actionId === MODE_DEACTIVATE_ACTION) {\n const exited = await queueTransition(() => exitPlanMode(execution.context, PLAN_MODE_TRIGGER_LEADER));\n return { message: exited ? 'Plan mode deactivated.' : 'Plan mode remains active.' };\n }\n throw new Error(`Unknown plan mode action: ${actionId}`);\n },\n onError: (error) => void telemetry.recordError(PLAN_EVENT.configLoadFailed, error),\n });\n modeOwner = owner;\n return () => {\n owner.dispose();\n if (modeOwner === owner) modeOwner = undefined;\n };\n });\n cordis.inject([DOOM_UI_HUB_SERVICE], (uiContext) => {\n const hub = requireDoomUiHub(uiContext);\n const leader = hub.registerLeader({\n source: PLAN_LEADER_SOURCE,\n bindings: planLeaderBindings(enabled),\n });\n const config = hub.registerConfig<ExtensionContext>({\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 // Read from the live session so the list is the models this session\n // can actually switch to, not a table compiled into the package.\n const models = sessionModels();\n try {\n return planConfigSections(planningConfigProvider(), planConfigNotice, models);\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), models);\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 if (active) planConfigNotice = error instanceof Error ? error.message : String(error);\n },\n });\n leaderContribution = leader;\n planConfigContribution = config;\n return () => {\n config.dispose();\n leader.dispose();\n if (planConfigContribution === config) planConfigContribution = undefined;\n if (leaderContribution === leader) leaderContribution = undefined;\n };\n });\n }\n if (doomIntegrations) {\n const assertVoiceTransitionNotCancelled = (signal: AbortSignal): void => {\n assertRuntimeActive();\n if (signal.aborted) {\n throw signal.reason instanceof Error ? signal.reason : new Error('Plan voice transition was cancelled.');\n }\n };\n const activatePlanVoiceTool: VoiceToolDefinition<ExtensionContext> = {\n descriptor: {\n source: PLAN_VOICE_SOURCE,\n id: PLAN_VOICE_ACTIVATE_ID,\n name: PLAN_VOICE_ACTIVATE_NAME,\n label: 'Activate Plan',\n description: 'Activate or switch the serialized read-only planning mode flavor.',\n order: 10,\n inputSchema: PLAN_VOICE_ACTIVATE_INPUT_SCHEMA,\n resultSchema: PLAN_VOICE_RESULT_SCHEMA,\n },\n execute(input, execution) {\n const flavor = parsePlanningFlavor(isRecord(input) ? input.flavor : undefined);\n if (!flavor) throw new Error('A valid plan flavor is required.');\n assertVoiceTransitionNotCancelled(execution.signal);\n return queueTransition(async () => {\n assertVoiceTransitionNotCancelled(execution.signal);\n const previousEnabled = enabled;\n const previousFlavor = activeFlavor;\n await activateFlavor(execution.context, flavor, PLAN_MODE_TRIGGER_VOICE_TOOL, false);\n return {\n active: enabled,\n flavor: activeFlavor ?? flavor,\n changed: previousEnabled !== enabled || previousFlavor !== activeFlavor,\n };\n });\n },\n };\n const exitPlanVoiceTool: VoiceToolDefinition<ExtensionContext> = {\n descriptor: {\n source: PLAN_VOICE_SOURCE,\n id: PLAN_VOICE_EXIT_ID,\n name: PLAN_VOICE_EXIT_NAME,\n label: 'Exit Plan',\n description: 'Exit read-only planning mode and restore the previous agent configuration.',\n order: 20,\n inputSchema: PLAN_VOICE_EXIT_INPUT_SCHEMA,\n resultSchema: PLAN_VOICE_RESULT_SCHEMA,\n },\n execute(_input, execution) {\n assertVoiceTransitionNotCancelled(execution.signal);\n return queueTransition(async () => {\n assertVoiceTransitionNotCancelled(execution.signal);\n const previousEnabled = enabled;\n const exited = await exitPlanMode(execution.context, PLAN_MODE_TRIGGER_VOICE_TOOL);\n return {\n active: enabled,\n flavor: activeFlavor ?? null,\n changed: previousEnabled !== enabled || exited,\n };\n });\n },\n };\n cordis.inject([DOOM_VOICE_TOOLS_SERVICE], (voiceContext) => {\n const service = requireDoomVoiceToolsService(voiceContext);\n const activate = service.register(activatePlanVoiceTool);\n const exit = service.register(exitPlanVoiceTool);\n return () => {\n exit.dispose();\n activate.dispose();\n };\n });\n }\n if (doomIntegrations) {\n cordis.inject([DOOM_UI_HUB_SERVICE], (uiContext) => {\n const dispose = requireDoomUiHub(uiContext).registerLeaderActions<ExtensionContext>({\n source: PLAN_LEADER_SOURCE,\n handlers: {\n 'plan.normal': () => {\n const ctx = active ? activeContext : undefined;\n return ctx\n ? queueTransition(() => activateFlavor(ctx, 'normal', PLAN_MODE_TRIGGER_LEADER, false))\n : undefined;\n },\n 'plan.exit': () => {\n const ctx = active ? activeContext : undefined;\n return ctx\n ? queueTransition(async () => {\n await exitPlanMode(ctx, PLAN_MODE_TRIGGER_LEADER);\n })\n : undefined;\n },\n 'plan.debug': () => {\n const ctx = active ? activeContext : undefined;\n return ctx\n ? queueTransition(() => activateFlavor(ctx, 'debug', PLAN_MODE_TRIGGER_LEADER, false))\n : undefined;\n },\n 'plan.fable': () => {\n const ctx = active ? activeContext : undefined;\n return ctx\n ? queueTransition(() => activateFlavor(ctx, 'fable', PLAN_MODE_TRIGGER_LEADER, false))\n : undefined;\n },\n },\n onError: (error, actionName) => {\n if (!active) return;\n if (activeContext?.hasUI) {\n activeContext.ui.notify(\n `Leader action ${actionName} failed. Plan mode remains read-only.`,\n WARNING_STYLE,\n );\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 disposeLeaderActions = dispose;\n return () => {\n dispose();\n if (disposeLeaderActions === dispose) disposeLeaderActions = undefined;\n };\n });\n }\n },\n ],\n onStop: shutdownPlanRuntime,\n onDispose: shutdownPlanRuntime,\n tools: [\n {\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:\n '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 assertRuntimeActive();\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 name: RUN_FABLE_PLAN_TOOL,\n label: 'Run Fable Plan',\n description:\n '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 const generation = sessionGeneration;\n assertRuntimeActive(generation);\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: [\n { type: 'text', text: 'The local Fable broker is unavailable. Fable planning remains disabled.' },\n ],\n details: { started: false, errorCode: 'broker_unavailable' } as Record<string, unknown>,\n };\n }\n const flow = fableFlow;\n if (!flow) throw new Error('Fable planning flow is unavailable.');\n const operation = flow.run(params, signal);\n activeFableRun = operation;\n try {\n const result = await operation;\n assertRuntimeActive(generation);\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 (active && generation === sessionGeneration && activeFableRun === operation) activeFableRun = undefined;\n }\n },\n },\n {\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 const generation = sessionGeneration;\n assertRuntimeActive(generation);\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 = AbortSignal.any([\n timeoutController.signal,\n runtimeAbortController.signal,\n ...(signal ? [signal] : []),\n ]);\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(\n () => fs.promises.realpath(plansDirectory),\n writeSignal,\n );\n if (directoryStats.isSymbolicLink() || !directoryStats.isDirectory()) {\n recordRefusal(\n 'unsafe_plans_directory',\n 'Refused to write the plan because the configured path is unsafe.',\n );\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 assertRuntimeActive(generation);\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 (\n error instanceof Error &&\n 'code' in error &&\n ['EISDIR', 'ELOOP', 'ENXIO'].includes(String(error.code))\n ) {\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 assertRuntimeActive(generation);\n currentPlan = { content, path: planPath, sessionId, planId, title, writtenAt: new Date().toISOString() };\n pi.appendEntry(PLAN_DOCUMENT_ENTRY, currentPlan);\n announcePlanDocument(ctx, currentPlan);\n planReadyForReview = true;\n awaitingVoicePlanDecision = false;\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 if (!active || generation !== sessionGeneration) throw abortReason(runtimeAbortController.signal);\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 name: COMPLETE_PLAN_TOOL,\n label: 'Complete Plan',\n description:\n 'Present the saved implementation plan for exit-or-continue approval. Interactive sessions use the normal selector; autonomous voice narrates a plain-text handoff and waits for the next user message.',\n promptSnippet: 'Request explicit exit-or-continue approval after the saved plan has been presented',\n promptGuidelines: [\n 'After write_plan succeeds, call complete_plan without a decision so it can present the review question.',\n 'When autonomous voice returns an awaiting-response result, stop. Interpret the next ordinary user message, then call complete_plan again with decision \"exit\" or \"continue\".',\n `Never pass decision \"${EXIT_PLAN_DECISION}\" without the user explicitly choosing \"${EXIT_PLAN_MODE_CHOICE}\".`,\n ],\n parameters: {\n type: 'object',\n properties: {\n decision: { type: 'string', enum: [EXIT_PLAN_DECISION, CONTINUE_PLAN_DECISION] },\n },\n additionalProperties: false,\n },\n async execute(_toolCallId, params, _signal, _onUpdate, ctx) {\n const generation = sessionGeneration;\n assertRuntimeActive(generation);\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 decision = isRecord(params) ? params.decision : undefined;\n const hasDecision = decision === EXIT_PLAN_DECISION || decision === CONTINUE_PLAN_DECISION;\n if (awaitingVoicePlanDecision) {\n if (!hasDecision) {\n throw new Error('The narrated plan review is waiting for an explicit exit or continue decision.');\n }\n awaitingVoicePlanDecision = false;\n return applyPlanReviewDecision(decision, ctx);\n }\n if (decision !== undefined) {\n throw new Error('Call complete_plan without a decision before submitting a narrated plan-review answer.');\n }\n\n if (await requestVoicePlanReview()) {\n assertRuntimeActive(generation);\n awaitingVoicePlanDecision = true;\n return voicePlanReviewResult();\n }\n\n const choice = await ctx.ui.select(PLAN_REVIEW_TITLE, [...PLAN_REVIEW_OPTIONS]);\n assertRuntimeActive(generation);\n return applyPlanReviewDecision(\n choice === EXIT_PLAN_MODE_CHOICE ? EXIT_PLAN_DECISION : CONTINUE_PLAN_DECISION,\n ctx,\n );\n },\n },\n ],\n events: {\n tool_call: async (event: ToolCallEvent, ctx) => {\n if (!active || !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 before_agent_start: async (event, ctx) => {\n if (!active) return undefined;\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 your turn; completion notifications wake the parent session.\\n\\nAfter all planning drafts complete, the main agent must compare the candidates, pick the strongest draft, cross-check it against the gathered evidence and the other drafts, resolve conflicts and gaps, and ask the user only for product decisions. A child produces the draft, but the main agent owns and synthesizes the final plan. Do not modify files or repository state except through write_plan. Start the final plan with a meaningful Markdown H1 because write_plan derives the filename from it. Present the complete plan as visible Markdown in chat, then call write_plan with no arguments. After write_plan succeeds, clear the completed durable task graph and call complete_plan without a decision. In an interactive text session, complete_plan uses the normal exit-or-continue selector. Under autonomous voice, it displays and narrates the choices without opening a blocking dialog, then ends the turn. Interpret the user's next ordinary message and call complete_plan again with decision \"exit\" or \"continue\". Do not exit plan mode without explicit approval.`,\n buildFlavorPlanningPrompt(activeFlavor!, plansDirectory, debugEvidence, fableStage),\n );\n }\n if (currentPlan) {\n // Read from disk rather than from what write_plan remembered. The plan is\n // editable in the cockpit, and a reader who corrected it expects the\n // agent to implement the correction, not the draft it wrote. A file that\n // has since been moved or removed falls back to the remembered text, so\n // the plan never silently disappears from the prompt.\n let content = currentPlan.content;\n try {\n content = await fs.promises.readFile(currentPlan.path, 'utf8');\n } catch {\n content = currentPlan.content;\n }\n sections.push(`[CURRENT PLAN]\\nSource: ${currentPlan.path}\\n\\n${content}`);\n }\n if (sections.length === 0) return undefined;\n return { systemPrompt: `${event.systemPrompt}\\n\\n${sections.join('\\n\\n')}` };\n },\n context: async (event) => {\n if (!active) return undefined;\n return {\n messages: event.messages.filter((message) => {\n const custom = message as { customType?: string };\n return custom.customType !== PLAN_MODE_CONTEXT;\n }),\n };\n },\n session_start: async (_event, ctx) => {\n if (!active) return;\n configSessionCtx = ctx;\n const generation = ++sessionGeneration;\n const entries = ctx.sessionManager.getEntries();\n const state = entries\n .flatMap((entry) =>\n entry.type === 'custom' && entry.customType === PLAN_MODE_ENTRY\n ? [parsePersistedPlanState(entry.data)]\n : [],\n )\n .filter((value): value is PersistedPlanModeState => value !== undefined)\n .at(-1);\n const piSessionId = ctx.sessionManager.getSessionId();\n const sessionId = planSessionIdentifier(piSessionId);\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 // A resumed session keeps its plan, so it keeps the announcement too; one\n // that restored none clears whatever a previous run left behind under this\n // session id.\n if (currentPlan) announcePlanDocument(ctx, currentPlan);\n else retirePlanDocument(ctx);\n activePlanId = state?.planId ?? currentPlan?.planId;\n planReadyForReview = false;\n awaitingVoicePlanDecision = 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 assertRuntimeActive(generation);\n } else {\n const snapshotToRestore = state?.originalSnapshot ?? snapshotFromPreviousSession;\n if (snapshotToRestore) {\n await restoreMainAgent(ctx, snapshotToRestore);\n assertRuntimeActive(generation);\n }\n applyToolRestriction();\n assertRuntimeActive(generation);\n planSnapshot = undefined;\n updateStatus(ctx);\n }\n },\n },\n };\n}\n"],"mappings":"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"}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { PlanningAgentConfig, PlanningModeConfig, PlanningThinkingLevel } from "../schemas/plan/config.cjs";
|
|
2
|
-
import { FablePlanBroker, FableStage } from "
|
|
3
|
-
import { PlanTelemetry } from "../
|
|
2
|
+
import { FablePlanBroker, FableStage } from "../services/fableFlow/index.cjs";
|
|
3
|
+
import { PlanTelemetry } from "../services/logSinkTelemetry/index.cjs";
|
|
4
|
+
import { DebugEvidencePacket, PlanningFlavor } from "../services/prompts/index.cjs";
|
|
4
5
|
import { PlanPointerPort } from "../types/planPointer.cjs";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
6
|
+
import { PiPluginContributions } from "@agimon-ai/doompi-core/pi-extension";
|
|
7
|
+
import { DoomToolRestriction } from "@agimon-ai/doompi-core/tool-surface";
|
|
7
8
|
import { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
8
|
-
//#region src/
|
|
9
|
+
//#region src/controllers/planMode.d.ts
|
|
9
10
|
export declare const WRITE_PLAN_TIMEOUT_MS = 5000;
|
|
10
11
|
declare const PLAN_STATE_VERSION: 2;
|
|
11
12
|
export interface ModelIdentity {
|
|
@@ -67,7 +68,13 @@ type MutableTaskInput = {
|
|
|
67
68
|
};
|
|
68
69
|
export declare function parsePersistedPlanState(value: unknown): PersistedPlanModeState | undefined;
|
|
69
70
|
export declare function parseDebugEvidencePacket(value: unknown): DebugEvidencePacket;
|
|
70
|
-
|
|
71
|
+
/**
|
|
72
|
+
* The only thing plan mode does to the tool surface: hide what planning must not touch.
|
|
73
|
+
*
|
|
74
|
+
* `flavor` undefined means plan mode is off, which still hides plan's own tools
|
|
75
|
+
* because they are registered for the whole session and only mean something inside it.
|
|
76
|
+
*/
|
|
77
|
+
export declare function planToolRestriction(flavor: PlanningFlavor | undefined, diagnosticTools: ReadonlySet<string>): DoomToolRestriction;
|
|
71
78
|
export declare function constrainSubagentInput(input: MutableSubagentInput): void;
|
|
72
79
|
export declare function planningSubagentModel(config: PlanningAgentConfig | undefined, currentModel: ModelIdentity | undefined): string | undefined;
|
|
73
80
|
export declare function configurePlanningSubagentInput(input: MutableSubagentInput, model: string): void;
|
|
@@ -76,7 +83,9 @@ export declare function isBlockedSubagentManagementAction(action: string | undef
|
|
|
76
83
|
export type PlanningConfigProvider = () => PlanningModeConfig | undefined;
|
|
77
84
|
/** Reads Doom settings from disk so each plan-mode activation sees current configuration. */
|
|
78
85
|
export declare function loadPlanningModeConfig(environment?: NodeJS.ProcessEnv, currentDirectory?: string): PlanningModeConfig | undefined;
|
|
79
|
-
export declare function
|
|
86
|
+
export declare function createPlanModeRuntime(pi: ExtensionAPI, planningConfigProvider?: PlanningConfigProvider, telemetry?: PlanTelemetry, options?: PlanModeExtensionOptions): Omit<PiPluginContributions, 'tools'> & {
|
|
87
|
+
tools: Parameters<ExtensionAPI['registerTool']>[0][];
|
|
88
|
+
};
|
|
80
89
|
//#endregion
|
|
81
90
|
export type { PlanningFlavor };
|
|
82
91
|
//# sourceMappingURL=planMode.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"planMode.d.cts","names":[],"sources":["../../src/controllers/planMode.ts"],"mappings":";;;;;;;;;qBAiIa;cA0CP;iBAiDW;EACf;EACA;;iBAGe;EACf;EACA,QAAQ;EACR,UAAU;;UAGF;EACR,gBAAgB;EAChB,eAAe;EACf,mBAAmB;EACnB;EACA,wBAAwB;;iBAsBT;EACf,cAAc;EACd;EACA;;EAEA,eAAe;;wBAQD,cAAc;wBAuBd,sBAAsB;wBAMtB,qBAAqB,MAAG,MAAe;wBAQvC,uBAAuB,6BAA6B;KAkD/D;EACH;EACA;EACA;EACA;EACA,WAAW,sBAAsB;;KAG9B;GACF;EACD;;KAGG,uBAAuB;EAC1B;EACA;EACA,eAAe;EACf;EACA,QAAQ;EACR,QAAQ;EACR;EACA;EACA,WAAW;;KAGR;GACF;EACD;;KAGG;EACH;EACA,cAAc;;wBAuFA,wBAAwB,iBAAiB;wBAsDzC,yBAAyB,iBAAiB;;;;;;;wBAgD1C,oBACd,QAAQ,4BACR,iBAAiB,sBAChB;wBAuBa,uBAAuB,OAAO;wBAkB9B,sBACd,QAAQ,iCACR,cAAc;wBASA,+BAA+B,OAAO,sBAAsB;wBAK5D,2BAA2B,OAAO,kBAAkB;wBAKpD,kCAAkC;YAgCtC,+BAA+B;;wBAG3B,uBACd,cAAa,OAAO,YACpB,4BACC;wBA4Ca,sBACd,IAAI,cACJ,yBAAwB,wBACxB,YAAW,eACX,UAAS,2BACR,KAAK;EAAoC,OAAO,WAAW"}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { PlanningAgentConfig, PlanningModeConfig, PlanningThinkingLevel } from "../schemas/plan/config.mjs";
|
|
2
|
-
import { FablePlanBroker, FableStage } from "
|
|
3
|
-
import { PlanTelemetry } from "../
|
|
2
|
+
import { FablePlanBroker, FableStage } from "../services/fableFlow/index.mjs";
|
|
3
|
+
import { PlanTelemetry } from "../services/logSinkTelemetry/index.mjs";
|
|
4
|
+
import { DebugEvidencePacket, PlanningFlavor } from "../services/prompts/index.mjs";
|
|
4
5
|
import { PlanPointerPort } from "../types/planPointer.mjs";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
6
|
+
import { DoomToolRestriction } from "@agimon-ai/doompi-core/tool-surface";
|
|
7
|
+
import { PiPluginContributions } from "@agimon-ai/doompi-core/pi-extension";
|
|
7
8
|
import { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
8
|
-
//#region src/
|
|
9
|
+
//#region src/controllers/planMode.d.ts
|
|
9
10
|
export declare const WRITE_PLAN_TIMEOUT_MS = 5000;
|
|
10
11
|
declare const PLAN_STATE_VERSION: 2;
|
|
11
12
|
export interface ModelIdentity {
|
|
@@ -67,7 +68,13 @@ type MutableTaskInput = {
|
|
|
67
68
|
};
|
|
68
69
|
export declare function parsePersistedPlanState(value: unknown): PersistedPlanModeState | undefined;
|
|
69
70
|
export declare function parseDebugEvidencePacket(value: unknown): DebugEvidencePacket;
|
|
70
|
-
|
|
71
|
+
/**
|
|
72
|
+
* The only thing plan mode does to the tool surface: hide what planning must not touch.
|
|
73
|
+
*
|
|
74
|
+
* `flavor` undefined means plan mode is off, which still hides plan's own tools
|
|
75
|
+
* because they are registered for the whole session and only mean something inside it.
|
|
76
|
+
*/
|
|
77
|
+
export declare function planToolRestriction(flavor: PlanningFlavor | undefined, diagnosticTools: ReadonlySet<string>): DoomToolRestriction;
|
|
71
78
|
export declare function constrainSubagentInput(input: MutableSubagentInput): void;
|
|
72
79
|
export declare function planningSubagentModel(config: PlanningAgentConfig | undefined, currentModel: ModelIdentity | undefined): string | undefined;
|
|
73
80
|
export declare function configurePlanningSubagentInput(input: MutableSubagentInput, model: string): void;
|
|
@@ -76,7 +83,9 @@ export declare function isBlockedSubagentManagementAction(action: string | undef
|
|
|
76
83
|
export type PlanningConfigProvider = () => PlanningModeConfig | undefined;
|
|
77
84
|
/** Reads Doom settings from disk so each plan-mode activation sees current configuration. */
|
|
78
85
|
export declare function loadPlanningModeConfig(environment?: NodeJS.ProcessEnv, currentDirectory?: string): PlanningModeConfig | undefined;
|
|
79
|
-
export declare function
|
|
86
|
+
export declare function createPlanModeRuntime(pi: ExtensionAPI, planningConfigProvider?: PlanningConfigProvider, telemetry?: PlanTelemetry, options?: PlanModeExtensionOptions): Omit<PiPluginContributions, 'tools'> & {
|
|
87
|
+
tools: Parameters<ExtensionAPI['registerTool']>[0][];
|
|
88
|
+
};
|
|
80
89
|
//#endregion
|
|
81
90
|
export type { PlanningFlavor };
|
|
82
91
|
//# sourceMappingURL=planMode.d.mts.map
|