@agimon-ai/doompi-plan 0.0.1-alpha.2 → 0.0.1-alpha.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. package/README.md +44 -19
  2. package/dist/adapters/pi/extension.cjs +2 -0
  3. package/dist/adapters/pi/extension.cjs.map +1 -0
  4. package/dist/{extension.d.cts → adapters/pi/extension.d.cts} +1 -1
  5. package/dist/adapters/pi/extension.d.cts.map +1 -0
  6. package/dist/{extension.d.mts → adapters/pi/extension.d.mts} +1 -1
  7. package/dist/adapters/pi/extension.d.mts.map +1 -0
  8. package/dist/adapters/pi/extension.mjs +2 -0
  9. package/dist/adapters/pi/extension.mjs.map +1 -0
  10. package/dist/adapters/telemetry/logSinkTelemetry.cjs +2 -0
  11. package/dist/adapters/telemetry/logSinkTelemetry.cjs.map +1 -0
  12. package/dist/adapters/telemetry/logSinkTelemetry.d.cts +34 -0
  13. package/dist/adapters/telemetry/logSinkTelemetry.d.cts.map +1 -0
  14. package/dist/adapters/telemetry/logSinkTelemetry.d.mts +34 -0
  15. package/dist/adapters/telemetry/logSinkTelemetry.d.mts.map +1 -0
  16. package/dist/adapters/telemetry/logSinkTelemetry.mjs +2 -0
  17. package/dist/adapters/telemetry/logSinkTelemetry.mjs.map +1 -0
  18. package/dist/config.cjs +1 -2
  19. package/dist/config.d.cts +2 -14
  20. package/dist/config.d.mts +2 -14
  21. package/dist/config.mjs +1 -2
  22. package/dist/extensions/doom.cjs +1 -1
  23. package/dist/extensions/doom.cjs.map +1 -1
  24. package/dist/extensions/doom.mjs +1 -1
  25. package/dist/extensions/doom.mjs.map +1 -1
  26. package/dist/extensions/pi.cjs +1 -1
  27. package/dist/extensions/pi.cjs.map +1 -1
  28. package/dist/extensions/pi.d.cts +1 -1
  29. package/dist/extensions/pi.d.mts +1 -1
  30. package/dist/extensions/pi.mjs +1 -1
  31. package/dist/extensions/pi.mjs.map +1 -1
  32. package/dist/fableFlow.cjs +1 -3
  33. package/dist/fableFlow.d.cts +2 -66
  34. package/dist/fableFlow.d.mts +2 -66
  35. package/dist/fableFlow.mjs +1 -3
  36. package/dist/index.cjs +1 -1
  37. package/dist/index.d.cts +4 -4
  38. package/dist/index.d.mts +4 -4
  39. package/dist/index.mjs +1 -1
  40. package/dist/logSinkTelemetry.cjs +1 -2
  41. package/dist/logSinkTelemetry.d.cts +2 -24
  42. package/dist/logSinkTelemetry.d.mts +2 -24
  43. package/dist/logSinkTelemetry.mjs +1 -2
  44. package/dist/planConfig.cjs +1 -2
  45. package/dist/planConfig.d.cts +2 -0
  46. package/dist/planConfig.d.mts +2 -0
  47. package/dist/planConfig.mjs +1 -2
  48. package/dist/planMode.cjs +1 -6
  49. package/dist/planMode.d.cts +3 -75
  50. package/dist/planMode.d.mts +3 -75
  51. package/dist/planMode.mjs +1 -6
  52. package/dist/prompts.cjs +1 -3
  53. package/dist/prompts.d.cts +2 -24
  54. package/dist/prompts.d.mts +2 -24
  55. package/dist/prompts.mjs +1 -3
  56. package/dist/schemas/plan/config.cjs +2 -0
  57. package/dist/schemas/plan/config.cjs.map +1 -0
  58. package/dist/schemas/plan/config.d.cts +14 -0
  59. package/dist/schemas/plan/config.d.cts.map +1 -0
  60. package/dist/schemas/plan/config.d.mts +14 -0
  61. package/dist/schemas/plan/config.d.mts.map +1 -0
  62. package/dist/schemas/plan/config.mjs +2 -0
  63. package/dist/schemas/plan/config.mjs.map +1 -0
  64. package/dist/schemas/plan/planConfig.cjs +2 -0
  65. package/dist/schemas/plan/planConfig.cjs.map +1 -0
  66. package/dist/schemas/plan/planConfig.d.cts +21 -0
  67. package/dist/schemas/plan/planConfig.d.cts.map +1 -0
  68. package/dist/schemas/plan/planConfig.d.mts +21 -0
  69. package/dist/schemas/plan/planConfig.d.mts.map +1 -0
  70. package/dist/schemas/plan/planConfig.mjs +2 -0
  71. package/dist/schemas/plan/planConfig.mjs.map +1 -0
  72. package/dist/services/plan/fableFlow.cjs +3 -0
  73. package/dist/services/plan/fableFlow.cjs.map +1 -0
  74. package/dist/services/plan/fableFlow.d.cts +66 -0
  75. package/dist/services/plan/fableFlow.d.cts.map +1 -0
  76. package/dist/services/plan/fableFlow.d.mts +66 -0
  77. package/dist/services/plan/fableFlow.d.mts.map +1 -0
  78. package/dist/services/plan/fableFlow.mjs +3 -0
  79. package/dist/services/plan/fableFlow.mjs.map +1 -0
  80. package/dist/services/plan/planMode.cjs +6 -0
  81. package/dist/services/plan/planMode.cjs.map +1 -0
  82. package/dist/services/plan/planMode.d.cts +79 -0
  83. package/dist/services/plan/planMode.d.cts.map +1 -0
  84. package/dist/services/plan/planMode.d.mts +79 -0
  85. package/dist/services/plan/planMode.d.mts.map +1 -0
  86. package/dist/services/plan/planMode.mjs +6 -0
  87. package/dist/services/plan/planMode.mjs.map +1 -0
  88. package/dist/services/plan/prompts.cjs +3 -0
  89. package/dist/services/plan/prompts.cjs.map +1 -0
  90. package/dist/services/plan/prompts.d.cts +24 -0
  91. package/dist/services/plan/prompts.d.cts.map +1 -0
  92. package/dist/services/plan/prompts.d.mts +24 -0
  93. package/dist/services/plan/prompts.d.mts.map +1 -0
  94. package/dist/services/plan/prompts.mjs +3 -0
  95. package/dist/services/plan/prompts.mjs.map +1 -0
  96. package/package.json +16 -8
  97. package/dist/config.cjs.map +0 -1
  98. package/dist/config.d.cts.map +0 -1
  99. package/dist/config.d.mts.map +0 -1
  100. package/dist/config.mjs.map +0 -1
  101. package/dist/extension.cjs +0 -2
  102. package/dist/extension.cjs.map +0 -1
  103. package/dist/extension.d.cts.map +0 -1
  104. package/dist/extension.d.mts.map +0 -1
  105. package/dist/extension.mjs +0 -2
  106. package/dist/extension.mjs.map +0 -1
  107. package/dist/fableFlow.cjs.map +0 -1
  108. package/dist/fableFlow.d.cts.map +0 -1
  109. package/dist/fableFlow.d.mts.map +0 -1
  110. package/dist/fableFlow.mjs.map +0 -1
  111. package/dist/logSinkTelemetry.cjs.map +0 -1
  112. package/dist/logSinkTelemetry.d.cts.map +0 -1
  113. package/dist/logSinkTelemetry.d.mts.map +0 -1
  114. package/dist/logSinkTelemetry.mjs.map +0 -1
  115. package/dist/planConfig.cjs.map +0 -1
  116. package/dist/planConfig.mjs.map +0 -1
  117. package/dist/planMode.cjs.map +0 -1
  118. package/dist/planMode.d.cts.map +0 -1
  119. package/dist/planMode.d.mts.map +0 -1
  120. package/dist/planMode.mjs.map +0 -1
  121. package/dist/prompts.cjs.map +0 -1
  122. package/dist/prompts.d.cts.map +0 -1
  123. package/dist/prompts.d.mts.map +0 -1
  124. package/dist/prompts.mjs.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"planMode.cjs","names":["MINOR_MODE_TOOL_NAME","VOICE_MODE_TOOL_NAMES","Buffer","loadDoomConfig","createPlanTelemetry","CONTRACT_FABLE_PLAN_REQUESTER","PLAN_EVENT","planConfigSections","CONFIG_ACTION","planSettingByFieldId","FABLE_PLAN_PROFILE","createFablePlanFlow","os","path","fs","buildFlavorPlanningPrompt","custom"],"sources":["../../../src/services/plan/planMode.ts"],"sourcesContent":["import { Buffer } from 'node:buffer';\nimport { createHash, randomUUID } from 'node:crypto';\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport { globalDoomConfigPath } from '@agimon-ai/doompi-config';\nimport { setDoomConfigValue, unsetDoomConfigValue } from '@agimon-ai/doompi-config/configWriter';\nimport {\n FABLE_PLAN_REQUESTER as CONTRACT_FABLE_PLAN_REQUESTER,\n createFablePlanBrokerClient,\n} from '@agimon-ai/doompi-extension-contracts/fable-plan';\nimport {\n MINOR_MODE_TOOL_NAME,\n type MinorModeOwnerHandle,\n type MinorModeState,\n registerMinorModeOwner,\n} from '@agimon-ai/doompi-extension-contracts/mode';\nimport { createProtocolRuntime } from '@agimon-ai/doompi-extension-contracts/protocol';\nimport {\n registerSubagentPolicy,\n type SubagentPolicyHandle,\n} from '@agimon-ai/doompi-extension-contracts/subagent-policy';\nimport { isSubagentAction, subagentActionAcceptsField } from '@agimon-ai/doompi-extension-contracts/subagent-tool';\nimport {\n registerVoiceToolContribution,\n VOICE_MODE_TOOL_NAMES,\n type VoiceToolDefinition,\n} from '@agimon-ai/doompi-extension-contracts/voice-tools';\nimport { CONFIG_ACTION, registerDoomConfigContribution } from '@agimon-ai/doompi-ui/config';\nimport {\n createDoomLeaderContribution,\n type DoomLeaderBinding,\n registerDoomLeaderActionHandlers,\n} from '@agimon-ai/doompi-ui/leader';\nimport {\n buildVoiceToolResult,\n createVoiceQuestionHandoff,\n type QuestionParams,\n type VoiceQuestionHandoffHandle,\n} from '@agimon-ai/doompi-user-feedback/voice-question-handoff';\nimport type { ExtensionAPI, ExtensionContext, ToolCallEvent } from '@earendil-works/pi-coding-agent';\nimport { createPlanTelemetry, PLAN_EVENT, type PlanTelemetry } from '../../adapters/telemetry/logSinkTelemetry.ts';\nimport {\n getHarnessState,\n loadDoomConfig,\n loadHarnessState,\n type PlanningAgentConfig,\n type PlanningModeConfig,\n type PlanningThinkingLevel,\n resolvePlanningPlansDirectory,\n} from '../../schemas/plan/config.ts';\nimport { planConfigSections, planSettingByFieldId } from '../../schemas/plan/planConfig.ts';\nimport {\n createFablePlanFlow,\n FABLE_PLAN_PROFILE,\n type FablePlanBroker,\n type FablePlanResult,\n type FableStage,\n} from './fableFlow.ts';\nimport { buildFlavorPlanningPrompt, type DebugEvidencePacket, type PlanningFlavor } from './prompts.ts';\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_MODE_CHOICE = 'Exit plan mode and start implementation';\nconst CONTINUE_PLANNING_CHOICE = 'Continue planning';\nconst EXIT_PLAN_DECISION = 'exit';\nconst CONTINUE_PLAN_DECISION = 'continue';\nconst PLAN_REVIEW_QUESTION = {\n questions: [\n {\n question: 'Plan complete. What would you like to do?',\n header: 'Plan review',\n options: [\n {\n label: EXIT_PLAN_MODE_CHOICE,\n description: 'Leave read-only plan mode and begin implementing the approved plan.',\n },\n {\n label: CONTINUE_PLANNING_CHOICE,\n description: 'Remain in read-only plan mode and refine the implementation plan.',\n },\n ],\n },\n ],\n} satisfies QuestionParams;\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 ...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;\nconst PLAN_TRANSIENT_TOOL_NAMES = new Set([\n COMPLETE_PLAN_TOOL,\n WRITE_PLAN_TOOL,\n RECORD_DEBUG_EVIDENCE_TOOL,\n RUN_FABLE_PLAN_TOOL,\n MINOR_MODE_TOOL_NAME,\n ...VOICE_MODE_TOOL_NAMES,\n]);\n\nexport type { PlanningFlavor } from './prompts.ts';\n\nexport interface ModelIdentity {\n provider: string;\n id: string;\n}\n\nexport interface PlanSnapshot {\n tools: string[];\n model?: ModelIdentity;\n thinking: PlanningThinkingLevel;\n}\n\ninterface PersistedPlanModeState {\n version: typeof PLAN_STATE_VERSION;\n activeFlavor?: PlanningFlavor;\n originalSnapshot?: PlanSnapshot;\n planId?: string;\n interruptedFableStage?: FableStage;\n}\n\ninterface PlanDocument {\n content: string;\n path: string;\n sessionId: string;\n planId?: string;\n title: string;\n writtenAt: string;\n}\n\ntype PlanWritePhase = 'checking' | 'writing';\n\n/** Why plan mode changed state, so the sink can tell a user toggle from a session restore. */\ntype PlanModeTrigger =\n | typeof PLAN_MODE_TRIGGER_LEADER\n | typeof PLAN_MODE_TRIGGER_SESSION_RESTORE\n | typeof PLAN_MODE_TRIGGER_PLAN_APPROVED\n | typeof PLAN_MODE_TRIGGER_SESSION_SHUTDOWN\n | typeof PLAN_MODE_TRIGGER_VOICE_TOOL;\n\nexport interface PlanModeExtensionOptions {\n fableBroker?: FablePlanBroker;\n debugDiagnosticTools?: readonly string[];\n doomIntegrations?: boolean;\n}\n\nexport function planTitleSlug(markdown: string): string {\n const heading = markdown.match(/^#\\s+(.+?)\\s*$/m)?.[1] ?? DEFAULT_PLAN_TITLE;\n return (\n heading\n .normalize('NFKD')\n .replace(/[\\u0300-\\u036f]/g, '')\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '')\n .slice(0, PLAN_TITLE_MAX_LENGTH) || DEFAULT_PLAN_TITLE\n );\n}\n\nexport function planSessionIdentifier(sessionId: string): string {\n const readable = encodeURIComponent(sessionId).slice(0, SESSION_ID_MAX_LENGTH) || 'session';\n const digest = createHash('sha256').update(sessionId).digest('hex');\n return `${readable}--${digest}`;\n}\n\nexport function createPlanIdentifier(now = new Date(), id = randomUUID()): string {\n const timestamp = now\n .toISOString()\n .replace(/[-:]/g, '')\n .replace(/\\.\\d{3}Z$/, 'Z');\n return `${timestamp}--${id}`;\n}\n\nexport function visiblePlanForToolCall(entries: readonly unknown[], toolCallId: string): string | undefined {\n for (let index = entries.length - 1; index >= 0; index -= 1) {\n const entry = entries[index];\n if (!entry || typeof entry !== 'object') continue;\n const candidate = entry as {\n type?: string;\n message?: { role?: string; content?: unknown };\n };\n if (candidate.type !== 'message' || candidate.message?.role !== 'assistant') continue;\n if (!Array.isArray(candidate.message.content)) continue;\n\n const toolIndex = candidate.message.content.findIndex((block) => {\n if (!block || typeof block !== 'object') return false;\n const content = block as { type?: string; id?: string; name?: string };\n return content.type === 'toolCall' && content.id === toolCallId && content.name === WRITE_PLAN_TOOL;\n });\n if (toolIndex < 0) continue;\n\n const plan = candidate.message.content\n .slice(0, toolIndex)\n .flatMap((block) => {\n if (!block || typeof block !== 'object') return [];\n const content = block as { type?: string; text?: unknown };\n return content.type === 'text' && typeof content.text === 'string' ? [content.text] : [];\n })\n .join('\\n\\n')\n .trim();\n return plan || undefined;\n }\n return undefined;\n}\n\nfunction abortReason(signal: AbortSignal): Error {\n return signal.reason instanceof Error ? signal.reason : new Error('Plan write cancelled.');\n}\n\nasync function waitForAbortable<T>(operation: () => Promise<T>, signal: AbortSignal): Promise<T> {\n if (signal.aborted) throw abortReason(signal);\n let onAbort: (() => void) | undefined;\n const aborted = new Promise<never>((_resolve, reject) => {\n onAbort = () => reject(abortReason(signal));\n signal.addEventListener('abort', onAbort, { once: true });\n });\n try {\n return await Promise.race([operation(), aborted]);\n } finally {\n if (onAbort) signal.removeEventListener('abort', onAbort);\n }\n}\n\ntype MutableSubagentStep = {\n model?: string;\n output?: string | boolean;\n progress?: boolean;\n worktree?: boolean;\n parallel?: MutableSubagentStep | MutableSubagentStep[];\n};\n\ntype MutableSubagentRunRequest = {\n [key: string]: unknown;\n model?: string;\n};\n\ntype MutableSubagentInput = MutableSubagentStep & {\n action?: string;\n artifacts?: boolean;\n outputSchema?: Record<string, unknown>;\n share?: boolean;\n tasks?: MutableSubagentStep[];\n chain?: MutableSubagentStep[];\n chainDir?: string;\n sessionDir?: string;\n requests?: MutableSubagentRunRequest[];\n};\n\ntype 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\nexport function planModeTools(activeTools: string[], availableTools: string[]): string[] {\n const available = new Set(availableTools);\n const retained = activeTools.filter((name) => PLAN_MODE_PARENT_TOOLS.has(name) && available.has(name));\n const unrelated = activeTools.filter(\n (name) =>\n !PLAN_MODE_PARENT_TOOLS.has(name) &&\n !PLAN_MODE_EXCLUDED_TOOLS.has(name) &&\n !PLAN_TRANSIENT_TOOL_NAMES.has(name) &&\n available.has(name),\n );\n const exploration = PLAN_MODE_EXPLORATION_TOOLS.filter((name) => available.has(name));\n const planTools = [COMPLETE_PLAN_TOOL, TASK_TOOL, WRITE_PLAN_TOOL].filter((name) => available.has(name));\n return [...new Set([...retained, ...unrelated, ...exploration, ...planTools])];\n}\n\nfunction planningToolsForFlavor(\n snapshotTools: string[],\n liveTools: string[],\n availableTools: string[],\n flavor: PlanningFlavor,\n diagnosticTools: ReadonlySet<string>,\n): string[] {\n const available = new Set(availableTools);\n const liveUnrelatedTools = liveTools.filter(\n (name) =>\n !PLAN_MODE_PARENT_TOOLS.has(name) && !PLAN_MODE_EXCLUDED_TOOLS.has(name) && !PLAN_TRANSIENT_TOOL_NAMES.has(name),\n );\n const activeTools = [...new Set([...snapshotTools, ...liveUnrelatedTools])];\n const base = planModeTools(\n activeTools.filter((name) => !PLAN_TRANSIENT_TOOL_NAMES.has(name)),\n availableTools,\n );\n const extras =\n flavor === 'debug' ? [RECORD_DEBUG_EVIDENCE_TOOL, ...diagnosticTools].filter((name) => available.has(name)) : [];\n if (flavor === 'fable' && available.has(RUN_FABLE_PLAN_TOOL)) extras.push(RUN_FABLE_PLAN_TOOL);\n for (const name of [MINOR_MODE_TOOL_NAME, ...VOICE_MODE_TOOL_NAMES]) {\n if (liveTools.includes(name) && available.has(name)) extras.push(name);\n }\n return [...new Set([...base, ...extras])];\n}\n\nfunction restoreSnapshotTools(snapshotTools: string[], liveTools: string[], availableTools: string[]): string[] {\n const available = new Set(availableTools);\n const live = new Set(liveTools);\n const restored = snapshotTools.filter(\n (name) => !PLAN_TRANSIENT_TOOL_NAMES.has(name) && available.has(name) && (name !== ASK_USER_TOOL || live.has(name)),\n );\n const unrelatedLiveTools = liveTools.filter(\n (name) =>\n !PLAN_MODE_PARENT_TOOLS.has(name) &&\n !PLAN_MODE_EXCLUDED_TOOLS.has(name) &&\n !PLAN_TRANSIENT_TOOL_NAMES.has(name) &&\n available.has(name),\n );\n const liveToolsToRestore = [MINOR_MODE_TOOL_NAME, ...VOICE_MODE_TOOL_NAMES].filter(\n (name) => live.has(name) && available.has(name),\n );\n return [...new Set([...restored, ...unrelatedLiveTools, ...liveToolsToRestore])];\n}\n\nfunction disableStepOutput(step: MutableSubagentStep): void {\n step.output = false;\n step.progress = false;\n step.worktree = false;\n if (Array.isArray(step.parallel)) {\n for (const child of step.parallel) disableStepOutput(child);\n } else if (step.parallel) {\n disableStepOutput(step.parallel);\n }\n}\n\nexport function constrainSubagentInput(input: MutableSubagentInput): void {\n if (input.action !== undefined) {\n if (isSubagentAction(input.action) && subagentActionAcceptsField(input.action, 'artifacts')) {\n input.artifacts = false;\n }\n return;\n }\n\n input.artifacts = false;\n input.output = false;\n input.share = false;\n input.progress = false;\n input.worktree = false;\n delete input.chainDir;\n delete input.outputSchema;\n delete input.sessionDir;\n for (const task of input.tasks ?? []) disableStepOutput(task);\n for (const step of input.chain ?? []) disableStepOutput(step);\n}\n\nexport function planningSubagentModel(\n config: PlanningAgentConfig | undefined,\n currentModel: ModelIdentity | undefined,\n): string | undefined {\n if (!config?.model && !config?.thinking) return undefined;\n const baseModel = config.model ?? (currentModel ? `${currentModel.provider}/${currentModel.id}` : undefined);\n if (!baseModel) return undefined;\n if (!config.thinking) return baseModel;\n return `${baseModel.replace(/:(?:off|minimal|low|medium|high|xhigh|max)$/, '')}:${config.thinking}`;\n}\n\nexport function configurePlanningSubagentInput(input: MutableSubagentInput, model: string): void {\n if (input.action !== 'run') return;\n for (const request of input.requests ?? []) request.model = model;\n}\n\nexport function configurePlanningTaskInput(input: MutableTaskInput, model: string): void {\n if (input.action !== 'assign') 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 * `exit` is published only while the mode is on. It is the one entry that can\n * do nothing otherwise, and a menu offering a way out of a mode reads as\n * though you are in it.\n */\nfunction planLeaderBindings(active: boolean): DoomLeaderBinding[] {\n const group = { key: 'p', label: 'plan', order: PLAN_LEADER_GROUP_ORDER } as const;\n return [\n {\n id: 'plan.normal',\n path: [group, { key: 'p', label: 'normal', detail: 'read-only planning' }],\n action: { name: 'plan.normal' },\n },\n {\n id: 'plan.debug',\n path: [group, { key: 'd', label: 'debug', detail: 'adaptive debug planning' }],\n action: { name: 'plan.debug' },\n },\n {\n id: 'plan.fable',\n path: [group, { key: 'f', label: 'fable', detail: 'repository-aware draft' }],\n action: { name: 'plan.fable' },\n },\n ...(active\n ? [\n {\n id: 'plan.exit',\n path: [group, { key: 'e', label: 'exit', detail: 'restore and exit' }] as DoomLeaderBinding['path'],\n action: { name: 'plan.exit' },\n },\n ]\n : []),\n ];\n}\n\nexport function planModeExtension(\n pi: ExtensionAPI,\n planningConfigProvider: PlanningConfigProvider = loadPlanningModeConfig,\n telemetry: PlanTelemetry = createPlanTelemetry(),\n options: PlanModeExtensionOptions = {},\n): void {\n let enabled = false;\n let activeFlavor: PlanningFlavor | undefined;\n let planSnapshot: PlanSnapshot | undefined;\n let activePlanningConfig: PlanningModeConfig | undefined;\n let capabilityCeiling: SubagentPolicyHandle | undefined;\n let currentPlan: PlanDocument | undefined;\n let activePlanId: string | undefined;\n let planReadyForReview = false;\n let awaitingVoicePlanDecision = false;\n let voiceQuestionHandoff: VoiceQuestionHandoffHandle | 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 transitionQueue: Promise<void> = Promise.resolve();\n const doomIntegrations = options.doomIntegrations ?? true;\n const diagnosticTools = new Set(options.debugDiagnosticTools ?? DEBUG_DIAGNOSTIC_TOOLS);\n const sessionBroker = createFablePlanBrokerClient(() => activeContext?.sessionManager.getSessionId());\n const fableBroker: FablePlanBroker = options.fableBroker ?? {\n start: (request, signal) => sessionBroker.start(request, signal),\n cancel: (operationId, reason) =>\n sessionBroker.cancel({ requester: CONTRACT_FABLE_PLAN_REQUESTER, operationId, reason }),\n };\n const isFableBrokerAvailable = (): boolean => options.fableBroker !== undefined || sessionBroker.isAvailable();\n const modeOwner: Pick<MinorModeOwnerHandle, 'publish'> = doomIntegrations\n ? registerMinorModeOwner(pi, {\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: {\n activation: 'inactive',\n condition: 'ready',\n actions: [\n { id: MODE_ACTIVATE_ACTION, enabled: true },\n { id: MODE_DEACTIVATE_ACTION, enabled: false, disabledReason: 'Plan mode is inactive.' },\n ],\n },\n async handleAction(actionId, argumentsValue, execution) {\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(() => activateFlavor(execution.context, flavor, PLAN_MODE_TRIGGER_LEADER, false));\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 : { publish: () => undefined };\n const leaderContribution = doomIntegrations\n ? createDoomLeaderContribution(pi, {\n source: PLAN_LEADER_SOURCE,\n bindings: planLeaderBindings(false),\n })\n : { update: () => undefined };\n let planConfigNotice: string | undefined;\n const planConfigContribution = doomIntegrations\n ? registerDoomConfigContribution(pi, {\n source: PLAN_LEADER_SOURCE,\n // Read at publish time, not cached: plan mode re-reads this file on every\n // activation anyway, so the panel should show whatever is on disk now.\n listSections: () => {\n try {\n return planConfigSections(planningConfigProvider(), planConfigNotice);\n } catch (error) {\n // A malformed config file must not take session start down with it. The\n // panel is where someone would go to repair it, so it has to still draw.\n return planConfigSections(undefined, error instanceof Error ? error.message : String(error));\n }\n },\n handlers: {\n [CONFIG_ACTION.set]: async ({ fieldId, value }) => {\n const setting = planSettingByFieldId(fieldId);\n if (!setting || !value) return;\n planConfigNotice = undefined;\n await setDoomConfigValue(globalDoomConfigPath(), [...setting.keyPath], value);\n },\n [CONFIG_ACTION.clear]: async ({ fieldId }) => {\n const setting = planSettingByFieldId(fieldId);\n if (!setting) return;\n planConfigNotice = undefined;\n await unsetDoomConfigValue(globalDoomConfigPath(), [...setting.keyPath]);\n },\n },\n onError: (error) => {\n planConfigNotice = error instanceof Error ? error.message : String(error);\n },\n })\n : { dispose: () => undefined, update: () => undefined };\n\n /** Identical bindings are dropped by the handle, so this can ride along with every state change. */\n function updateLeader(): void {\n leaderContribution.update(planLeaderBindings(enabled));\n }\n\n function updateStatus(ctx: ExtensionContext): void {\n updateLeader();\n const flavor = activeFlavor;\n const status = enabled && flavor ? `plan:${flavor}` : undefined;\n const stage = flavor === 'fable' && fableStage !== 'idle' ? ` · ${fableStage}` : '';\n ctx.ui.setStatus(PLAN_MODE_STATUS_KEY, status ? ctx.ui.theme.fg(WARNING_STYLE, status) : undefined);\n const modeState: MinorModeState = {\n activation: enabled ? 'active' : 'inactive',\n condition: fableStage === 'failed' ? 'failed' : fableStage === 'interrupted' ? 'degraded' : 'ready',\n ...(enabled && flavor ? { detail: `${flavor}${stage} - ${READ_ONLY_DETAIL}`, color: WARNING_STYLE } : {}),\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 modeOwner.publish(modeState);\n }\n\n function updateCapabilityCeiling(): void {\n if (!doomIntegrations || !enabled || !activeFlavor || !activeContext) return;\n const allowedTools = [...CHILD_EXPLORATION_TOOLS];\n const allowedExternalProfiles = activeFlavor === 'fable' ? [FABLE_PLAN_PROFILE] : [];\n const policy = {\n owner: PLAN_LEADER_SOURCE,\n allowedTools,\n requiredTools: [BASH_TOOL, MCP_TOOL],\n allowMcpTools: true,\n allowedExternalProfiles,\n denyExtensions: false,\n };\n if (capabilityCeiling) capabilityCeiling.update(policy);\n else {\n capabilityCeiling = registerSubagentPolicy(\n createProtocolRuntime({\n bus: pi.events,\n source: PLAN_LEADER_SOURCE,\n sessionId: activeContext.sessionManager.getSessionId(),\n }),\n policy,\n );\n }\n }\n\n function persistState(): void {\n pi.appendEntry(PLAN_MODE_ENTRY, {\n version: PLAN_STATE_VERSION,\n ...(enabled && activeFlavor ? { activeFlavor } : {}),\n ...(planSnapshot\n ? {\n originalSnapshot: {\n tools: [...planSnapshot.tools],\n ...(planSnapshot.model ? { model: { ...planSnapshot.model } } : {}),\n thinking: planSnapshot.thinking,\n },\n }\n : {}),\n ...(activePlanId ? { planId: activePlanId } : {}),\n ...(interruptedFableStage ? { interruptedFableStage } : {}),\n } satisfies PersistedPlanModeState);\n }\n\n function reportModelFailure(\n phase: 'apply' | 'restore',\n model: string,\n reason: typeof MODEL_NOT_FOUND_REASON | typeof MODEL_UNAUTHENTICATED_REASON | typeof MODEL_RESTORE_FAILED_REASON,\n ): void {\n void telemetry.recordWarning(PLAN_EVENT.modelResolveFailed, `Planning model ${reason}: ${model}`, {\n [PLAN_MODEL_ATTRIBUTE]: model,\n 'plan.model.phase': phase,\n 'plan.model.reason': reason,\n });\n }\n\n async function applyMainPlanningConfig(ctx: ExtensionContext): Promise<void> {\n const config = activePlanningConfig?.main;\n if (!config) return;\n if (config.model) {\n const model = resolvePlanningModel(config.model, ctx);\n if (!model) {\n ctx.ui.notify(`Planning model not found: ${config.model}`, WARNING_STYLE);\n reportModelFailure('apply', config.model, MODEL_NOT_FOUND_REASON);\n } else if (!(await pi.setModel(model))) {\n ctx.ui.notify(`Planning model is unavailable without authentication: ${config.model}`, WARNING_STYLE);\n reportModelFailure('apply', config.model, MODEL_UNAUTHENTICATED_REASON);\n }\n }\n if (config.thinking) pi.setThinkingLevel(config.thinking);\n }\n\n async function restoreMainAgent(ctx: ExtensionContext, snapshot: PlanSnapshot | undefined): Promise<boolean> {\n if (!snapshot) return false;\n if (snapshot.model) {\n const identity = `${snapshot.model.provider}/${snapshot.model.id}`;\n const model = ctx.modelRegistry.find(snapshot.model.provider, snapshot.model.id);\n if (!model) {\n ctx.ui.notify(`Previous model not found: ${identity}`, WARNING_STYLE);\n reportModelFailure('restore', identity, MODEL_NOT_FOUND_REASON);\n return false;\n }\n try {\n if (!(await pi.setModel(model))) {\n ctx.ui.notify(`Previous model is unavailable without authentication: ${identity}`, WARNING_STYLE);\n reportModelFailure('restore', identity, MODEL_UNAUTHENTICATED_REASON);\n return false;\n }\n } catch (error) {\n ctx.ui.notify(`Previous model could not be restored: ${identity}`, WARNING_STYLE);\n reportModelFailure('restore', identity, MODEL_RESTORE_FAILED_REASON);\n void telemetry.recordError(PLAN_EVENT.modelResolveFailed, error, {\n [PLAN_MODEL_ATTRIBUTE]: identity,\n 'plan.model.phase': 'restore',\n 'plan.model.reason': MODEL_RESTORE_FAILED_REASON,\n });\n return false;\n }\n }\n pi.setThinkingLevel(snapshot.thinking);\n return true;\n }\n\n function reportCurrentFlavor(ctx: ExtensionContext): void {\n if (enabled && activeFlavor) ctx.ui.notify(`Already in plan:${activeFlavor}.`, INFO_STYLE);\n else ctx.ui.notify('Plan mode is inactive.', INFO_STYLE);\n }\n\n function queueTransition<T>(transition: () => Promise<T>): Promise<T> {\n const queued = transitionQueue.then(transition, transition);\n transitionQueue = queued.then(\n () => undefined,\n () => undefined,\n );\n return queued;\n }\n\n async function cancelFableOperation(reason: string): Promise<void> {\n if (!activeFableRun && !fableStage.match(/^(draft|review)$/u)) return;\n if (fableStage === 'draft' || fableStage === 'review') interruptedFableStage = fableStage;\n fableFlow.cancel(reason);\n const pending = activeFableRun;\n if (pending) {\n try {\n await pending;\n } catch (error) {\n void telemetry.recordError(PLAN_EVENT.modeDisabled, error, {\n [PLAN_FLAVOR_ATTRIBUTE]: 'fable',\n 'plan.fable.reason': 'cancel_failed',\n });\n }\n }\n activeFableRun = undefined;\n if (interruptedFableStage) fableStage = 'interrupted';\n if (activeContext) updateStatus(activeContext);\n persistState();\n }\n\n async function activateFlavor(\n ctx: ExtensionContext,\n flavor: PlanningFlavor,\n trigger: PlanModeTrigger,\n restoring: boolean,\n ): Promise<void> {\n activeContext = ctx;\n if (enabled) {\n if (activeFlavor === flavor) {\n reportCurrentFlavor(ctx);\n return;\n }\n if (activeFlavor === 'fable') await cancelFableOperation(`Leaving Fable for plan:${flavor}.`);\n activeFlavor = flavor;\n updateCapabilityCeiling();\n pi.setActiveTools(\n planningToolsForFlavor(\n planSnapshot?.tools ?? [],\n pi.getActiveTools(),\n pi.getAllTools().map((tool) => tool.name),\n flavor,\n diagnosticTools,\n ),\n );\n updateStatus(ctx);\n persistState();\n void telemetry.recordEvent(PLAN_EVENT.modeEnabled, {\n [PLAN_TRIGGER_ATTRIBUTE]: trigger,\n [PLAN_FLAVOR_ATTRIBUTE]: flavor,\n 'plan.tool.count': pi.getActiveTools().length,\n });\n return;\n }\n\n let nextPlanningConfig: PlanningModeConfig | undefined;\n try {\n nextPlanningConfig = planningConfigProvider();\n } catch (error) {\n void telemetry.recordError(PLAN_EVENT.configLoadFailed, error, { [PLAN_TRIGGER_ATTRIBUTE]: trigger });\n throw error;\n }\n\n if (!restoring) {\n currentPlan = undefined;\n planReadyForReview = false;\n 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 pi.setActiveTools(\n planningToolsForFlavor(\n planSnapshot.tools,\n pi.getActiveTools(),\n pi.getAllTools().map((tool) => tool.name),\n flavor,\n diagnosticTools,\n ),\n );\n updateCapabilityCeiling();\n await applyMainPlanningConfig(ctx);\n updateStatus(ctx);\n persistState();\n void telemetry.recordEvent(PLAN_EVENT.modeEnabled, {\n [PLAN_TRIGGER_ATTRIBUTE]: trigger,\n [PLAN_FLAVOR_ATTRIBUTE]: flavor,\n 'plan.tool.count': pi.getActiveTools().length,\n ...(activePlanningConfig?.main?.model ? { [PLAN_MODEL_ATTRIBUTE]: activePlanningConfig.main.model } : {}),\n ...(activePlanningConfig?.main?.thinking ? { 'plan.thinking': activePlanningConfig.main.thinking } : {}),\n });\n }\n\n async function exitPlanMode(ctx: ExtensionContext, trigger: PlanModeTrigger): Promise<boolean> {\n if (!enabled) {\n reportCurrentFlavor(ctx);\n return false;\n }\n await cancelFableOperation('Plan mode is exiting.');\n const snapshot = planSnapshot;\n const restored = await restoreMainAgent(ctx, snapshot);\n if (!restored) {\n ctx.ui.notify('Previous model was not restored. Plan mode remains read-only.', WARNING_STYLE);\n updateStatus(ctx);\n persistState();\n void telemetry.recordWarning(PLAN_EVENT.modeDisabled, 'Plan mode restoration failed.', {\n [PLAN_TRIGGER_ATTRIBUTE]: trigger,\n [PLAN_FLAVOR_ATTRIBUTE]: activeFlavor ?? 'unknown',\n 'plan.restored': false,\n });\n return false;\n }\n\n const hadPlan = Boolean(currentPlan);\n pi.setActiveTools(\n restoreSnapshotTools(\n snapshot?.tools ?? [],\n pi.getActiveTools(),\n pi.getAllTools().map((tool) => tool.name),\n ),\n );\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 const planVoiceContributions: Array<{ dispose(): void }> = [];\n if (doomIntegrations) {\n const voiceHost = pi as unknown as Parameters<typeof registerVoiceToolContribution<ExtensionContext>>[0];\n const assertVoiceTransitionNotCancelled = (signal: AbortSignal): void => {\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 planVoiceContributions.push(registerVoiceToolContribution<ExtensionContext>(voiceHost, activatePlanVoiceTool));\n planVoiceContributions.push(registerVoiceToolContribution<ExtensionContext>(voiceHost, exitPlanVoiceTool));\n }\n\n const fableFlow = createFablePlanFlow({\n broker: fableBroker,\n isAuthorized: () => enabled && activeFlavor === 'fable' && isFableBrokerAvailable(),\n onStage: (stage) => {\n fableStage = stage;\n if (stage === 'completed' || stage === 'failed' || stage === 'cancelled') interruptedFableStage = undefined;\n if (activeContext) updateStatus(activeContext);\n if (enabled) persistState();\n },\n onError: (error) => {\n void telemetry.recordError(PLAN_EVENT.modeEnabled, error, {\n [PLAN_FLAVOR_ATTRIBUTE]: 'fable',\n 'plan.fable.reason': 'flow_error',\n });\n },\n });\n\n pi.registerTool({\n name: RECORD_DEBUG_EVIDENCE_TOOL,\n label: 'Record Debug Evidence',\n description: 'Record optional bounded debug evidence while planning a repository fix.',\n promptSnippet: 'Record the issue and any relevant reproduction, logs, traces, process output, or browser evidence',\n parameters: {\n type: 'object',\n properties: {\n issue: { type: 'string' },\n expectedBehavior: { type: 'string' },\n reproductionAttempt: { type: 'string' },\n actualBehavior: { type: 'string' },\n logs: { type: 'array', items: { type: 'string' } },\n correlatedTraceEvidence: { type: 'array', items: { type: 'string' } },\n processOutput: { type: 'array', items: { type: 'string' } },\n browserConsoleEvidence: { type: 'array', items: { type: 'string' } },\n correlationIds: { type: 'array', items: { type: 'string' } },\n timestamps: { type: 'array', items: { type: 'string' } },\n verifiedFacts: { type: 'array', items: { type: 'string' } },\n hypotheses: { type: 'array', items: { type: 'string' } },\n unavailableEvidence: { type: 'array', items: { type: 'string' } },\n },\n required: ['issue'],\n additionalProperties: false,\n },\n async execute(_toolCallId, params) {\n if (!enabled || activeFlavor !== 'debug') {\n return { content: [{ type: 'text', text: 'Debug planning is not active.' }], details: { recorded: false } };\n }\n try {\n debugEvidence = parseDebugEvidencePacket(params);\n } catch (error) {\n void telemetry.recordWarning(PLAN_EVENT.modeEnabled, error, {\n [PLAN_FLAVOR_ATTRIBUTE]: 'debug',\n 'plan.debug.reason': 'invalid_evidence',\n });\n throw error;\n }\n if (activeContext) updateStatus(activeContext);\n return {\n content: [{ type: 'text', text: 'Debug evidence recorded as optional planning context.' }],\n details: { recorded: true },\n };\n },\n });\n\n pi.registerTool({\n name: RUN_FABLE_PLAN_TOOL,\n label: 'Run Fable Plan',\n description: 'Send a bounded, sanitized planning packet to the local Fable broker for one repository-aware draft.',\n promptSnippet: 'Run the local Fable draft with the bounded planning packet',\n parameters: {\n type: 'object',\n properties: {\n goal: { type: 'array', items: { type: 'string' } },\n constraints: { type: 'array', items: { type: 'string' } },\n decisions: { type: 'array', items: { type: 'string' } },\n verifiedFindings: { type: 'array', items: { type: 'object' } },\n inferredFindings: { type: 'array', items: { type: 'string' } },\n unresolvedQuestions: { type: 'array', items: { type: 'string' } },\n currentPlan: { type: 'string' },\n },\n additionalProperties: false,\n },\n async execute(_toolCallId, params, signal) {\n if (!enabled || activeFlavor !== 'fable') {\n return {\n content: [{ type: 'text', text: 'Fable planning is not active.' }],\n details: { started: false } as Record<string, unknown>,\n };\n }\n if (!isFableBrokerAvailable()) {\n return {\n content: [{ type: 'text', text: 'The local Fable broker is unavailable. Fable planning remains disabled.' }],\n details: { started: false, errorCode: 'broker_unavailable' } as Record<string, unknown>,\n };\n }\n const operation = fableFlow.run(params, signal);\n activeFableRun = operation;\n try {\n const result = await operation;\n const text = result.draft\n ? `Fable draft:\\n${result.draft}`\n : `Fable planning ${result.status}: ${result.errorCode ?? 'no output'}.`;\n return {\n content: [{ type: 'text', text }],\n details: { ...result, started: true } as Record<string, unknown>,\n };\n } finally {\n if (activeFableRun === operation) activeFableRun = undefined;\n }\n },\n });\n\n pi.registerTool({\n name: WRITE_PLAN_TOOL,\n label: 'Write Plan',\n description:\n 'After presenting the complete implementation plan as Markdown in chat, save that visible plan to a unique Markdown file in the configured plans directory.',\n promptSnippet: 'Save the implementation plan already presented in chat to the configured plans directory',\n parameters: { type: 'object', properties: {}, additionalProperties: false },\n async execute(toolCallId, _params, signal, onUpdate, ctx) {\n if (!enabled) {\n return {\n content: [{ type: 'text', text: 'Plan mode is disabled. Use normal file tools instead.' }],\n details: { written: false },\n };\n }\n const content = visiblePlanForToolCall(ctx.sessionManager.getBranch(), toolCallId);\n if (!content) {\n const error = new Error(\n 'Present the complete implementation plan as visible Markdown in chat before calling write_plan.',\n );\n void telemetry.recordError(PLAN_EVENT.writePlanFailed, error, { 'plan.reason': 'missing_visible_plan' });\n throw error;\n }\n\n const startedAt = Date.now();\n const repoRoot = getHarnessState().root ?? ctx.cwd;\n const plansDirectory = resolvePlanningPlansDirectory(\n activePlanningConfig?.plansDirectory,\n repoRoot,\n os.homedir(),\n );\n const sessionId = planSessionIdentifier(ctx.sessionManager.getSessionId());\n activePlanId ??= createPlanIdentifier();\n const storedPlanPath =\n currentPlan?.planId === activePlanId && path.dirname(currentPlan.path) === plansDirectory\n ? currentPlan.path\n : undefined;\n const title = planTitleSlug(content);\n let planId = activePlanId;\n let planPath = storedPlanPath ?? path.join(plansDirectory, `${title}--${planId}.md`);\n const timeoutController = new AbortController();\n const timeoutError = new Error(`Writing ${planPath} timed out after ${WRITE_PLAN_TIMEOUT_MS}ms.`);\n const timeout = setTimeout(() => timeoutController.abort(timeoutError), WRITE_PLAN_TIMEOUT_MS);\n const writeSignal = signal ? AbortSignal.any([signal, timeoutController.signal]) : timeoutController.signal;\n let phase: PlanWritePhase = 'checking';\n const reportPhase = (): void => {\n onUpdate?.({\n content: [{ type: 'text', text: `${phase === 'checking' ? 'Checking' : 'Writing'} ${planPath}...` }],\n details: { phase, path: planPath, startedAt },\n });\n };\n const recordRefusal = (refusal: string, message: string): void => {\n void telemetry.recordWarning(PLAN_EVENT.writePlanUnsafePath, message, {\n 'plan.phase': phase,\n 'plan.refusal': refusal,\n });\n };\n\n try {\n reportPhase();\n try {\n await waitForAbortable(\n () => fs.promises.mkdir(plansDirectory, { recursive: true, mode: PRIVATE_DIRECTORY_MODE }),\n writeSignal,\n );\n } catch (error) {\n if (writeSignal.aborted) throw abortReason(writeSignal);\n throw new Error(`Could not create the configured plans directory ${plansDirectory}.`, { cause: error });\n }\n const directoryStats = await waitForAbortable(() => fs.promises.lstat(plansDirectory), writeSignal);\n const resolvedPlansDirectory = await waitForAbortable(() => fs.promises.realpath(plansDirectory), writeSignal);\n if (directoryStats.isSymbolicLink() || !directoryStats.isDirectory()) {\n recordRefusal('unsafe_plans_directory', 'Refused to write the plan because the configured path is unsafe.');\n return {\n content: [{ type: 'text', text: 'Refused to write the plan because the configured path is unsafe.' }],\n details: { written: false, path: planPath, phase, durationMs: Date.now() - startedAt },\n };\n }\n\n phase = 'writing';\n reportPhase();\n let planFile: Awaited<ReturnType<typeof fs.promises.open>> | undefined;\n try {\n for (let attempt = 0; attempt < PLAN_ALLOCATION_ATTEMPTS; attempt += 1) {\n try {\n const createFlags = storedPlanPath ? 0 : fs.constants.O_EXCL;\n planFile = await waitForAbortable(\n () =>\n fs.promises.open(\n planPath,\n fs.constants.O_CREAT |\n fs.constants.O_WRONLY |\n fs.constants.O_NONBLOCK |\n fs.constants.O_NOFOLLOW |\n createFlags,\n PRIVATE_FILE_MODE,\n ),\n writeSignal,\n );\n break;\n } catch (error) {\n const collision =\n !storedPlanPath && error instanceof Error && 'code' in error && String(error.code) === 'EEXIST';\n if (!collision || attempt === PLAN_ALLOCATION_ATTEMPTS - 1) throw error;\n planId = createPlanIdentifier();\n activePlanId = planId;\n planPath = path.join(plansDirectory, `${title}--${planId}.md`);\n persistState();\n }\n }\n if (!planFile) throw new Error(`Could not allocate a unique plan filename in ${plansDirectory}.`);\n const resolvedAfterOpen = await waitForAbortable(() => fs.promises.realpath(plansDirectory), writeSignal);\n const stats = await waitForAbortable(() => planFile!.stat(), writeSignal);\n if (resolvedAfterOpen !== resolvedPlansDirectory || !stats.isFile() || stats.nlink !== 1) {\n recordRefusal('invalid_destination', INVALID_PLAN_DESTINATION);\n return {\n content: [{ type: 'text', text: INVALID_PLAN_DESTINATION }],\n details: { written: false, path: planPath, phase, durationMs: Date.now() - startedAt },\n };\n }\n await waitForAbortable(() => planFile!.truncate(0), writeSignal);\n await waitForAbortable(\n () => planFile!.writeFile(content, { encoding: 'utf8', signal: writeSignal }),\n writeSignal,\n );\n } catch (error) {\n if (error instanceof Error && 'code' in error && ['EISDIR', 'ELOOP', 'ENXIO'].includes(String(error.code))) {\n recordRefusal('invalid_destination', INVALID_PLAN_DESTINATION);\n return {\n content: [{ type: 'text', text: INVALID_PLAN_DESTINATION }],\n details: { written: false, path: planPath, phase, durationMs: Date.now() - startedAt },\n };\n }\n throw error;\n } finally {\n await planFile?.close();\n }\n currentPlan = { content, path: planPath, sessionId, planId, title, writtenAt: new Date().toISOString() };\n pi.appendEntry(PLAN_DOCUMENT_ENTRY, currentPlan);\n planReadyForReview = true;\n 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 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 async function applyPlanReviewDecision(\n decision: typeof EXIT_PLAN_DECISION | typeof CONTINUE_PLAN_DECISION,\n ctx: ExtensionContext,\n ) {\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 pi.registerTool({\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 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 const voiceResult = voiceQuestionHandoff?.handoff(PLAN_REVIEW_QUESTION);\n if (voiceResult) {\n awaitingVoicePlanDecision = true;\n return buildVoiceToolResult(voiceResult);\n }\n\n const choice = await ctx.ui.select('Plan complete. What would you like to do?', [\n EXIT_PLAN_MODE_CHOICE,\n CONTINUE_PLANNING_CHOICE,\n ]);\n return applyPlanReviewDecision(\n choice === EXIT_PLAN_MODE_CHOICE ? EXIT_PLAN_DECISION : CONTINUE_PLAN_DECISION,\n ctx,\n );\n },\n });\n\n if (doomIntegrations) {\n registerDoomLeaderActionHandlers(pi, {\n source: PLAN_LEADER_SOURCE,\n handlers: {\n 'plan.normal': () => {\n const ctx = activeContext;\n return ctx\n ? queueTransition(() => activateFlavor(ctx, 'normal', PLAN_MODE_TRIGGER_LEADER, false))\n : undefined;\n },\n 'plan.exit': () => {\n const ctx = activeContext;\n return ctx\n ? queueTransition(async () => {\n await exitPlanMode(ctx, PLAN_MODE_TRIGGER_LEADER);\n })\n : undefined;\n },\n 'plan.debug': () => {\n const ctx = activeContext;\n return ctx ? queueTransition(() => activateFlavor(ctx, 'debug', PLAN_MODE_TRIGGER_LEADER, false)) : undefined;\n },\n 'plan.fable': () => {\n const ctx = activeContext;\n return ctx ? queueTransition(() => activateFlavor(ctx, 'fable', PLAN_MODE_TRIGGER_LEADER, false)) : undefined;\n },\n },\n onError: (error, actionName) => {\n if (activeContext?.hasUI) {\n activeContext.ui.notify(`Leader action ${actionName} failed. Plan mode remains read-only.`, WARNING_STYLE);\n }\n void telemetry.recordError(PLAN_EVENT.modeEnabled, error, {\n [PLAN_TRIGGER_ATTRIBUTE]: PLAN_MODE_TRIGGER_LEADER,\n 'plan.action': actionName,\n });\n },\n });\n }\n\n pi.on('tool_call', async (event: ToolCallEvent, ctx) => {\n if (!enabled) return undefined;\n const isFlavorTool =\n (activeFlavor === 'debug' &&\n (event.toolName === RECORD_DEBUG_EVIDENCE_TOOL || diagnosticTools.has(event.toolName))) ||\n (activeFlavor === 'fable' && event.toolName === RUN_FABLE_PLAN_TOOL);\n if (!PLAN_MODE_PARENT_TOOLS.has(event.toolName) && !isFlavorTool) {\n return { block: true, reason: `Plan mode blocks the ${event.toolName} tool. Use SPC p e to restore access.` };\n }\n const model = planningSubagentModel(activePlanningConfig?.subagents, ctx.model);\n if (event.toolName === TASK_TOOL) {\n if (model) configurePlanningTaskInput(event.input as MutableTaskInput, model);\n return undefined;\n }\n if (event.toolName !== SUBAGENT_TOOL) return undefined;\n\n const input = event.input as MutableSubagentInput;\n if (isBlockedSubagentManagementAction(input.action)) {\n return {\n block: true,\n reason: `Plan mode blocks subagent management action '${input.action}'. Use SPC p e first.`,\n };\n }\n constrainSubagentInput(input);\n if (model) configurePlanningSubagentInput(input, model);\n return undefined;\n });\n\n pi.on('before_agent_start', async (event, ctx) => {\n const sections: string[] = [];\n if (enabled) {\n const repoRoot = getHarnessState().root ?? ctx.cwd;\n const plansDirectory = resolvePlanningPlansDirectory(\n activePlanningConfig?.plansDirectory,\n repoRoot,\n os.homedir(),\n );\n sections.push(\n `[PLAN MODE ACTIVE]\\nYou are in repository read-only plan mode. The dedicated write_plan tool may write one unique Markdown plan file under ${plansDirectory}.\\n\\nExplore the codebase and produce a concrete implementation plan. For every plan, first call subagent with action \"agents\", cluster the exploration by independent domain, subsystem, or integration boundary, and create a provisional task graph with the task tool. Select specialized agents by matching their names and descriptions to each cluster. Assign every unblocked delegated task through the task tool, and use a one-shot inlineAgent with a focused systemPrompt when no discovered specialist fits. Treat the initial graph as provisional, not as a fixed contract. After findings arrive, review the entire graph at least once and perform one to three review passes in total. In each pass, use the evidence to add, rewrite, delete, cancel, reassign, or change blockedBy relationships for tasks when warranted. Do not keep following tasks that new information has made stale. Stop revising early when the graph is stable, or after the third pass.\\n\\nEvery plan must end with a delegated planning-draft stage blocked by all exploration and decision tasks. A single-boundary plan gets one planning draft. A complex plan spanning multiple subsystems, domains, packages, apps, or integration boundaries gets two planning drafts concurrently. Use three concurrent drafts instead when the work is cross-layer, migration-sensitive, security-sensitive, or similarly high risk. Assign each draft through the task tool to the discovered \"planner\" agent with context \"fork\" so it receives the conversation and gathered evidence. If the planner agent is unavailable, assign the same draft task through the task tool with a focused inlineAgent. All children receive read, Bash, grep, find, ls, and configured MCP tools with artifacts disabled. Bash and MCP tools are for read-only inspection and must not modify files, external systems, or repository state. Doom Team runs asynchronously, so do not poll. After launch, continue non-overlapping exploration or end 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 sections.push(`[CURRENT PLAN]\\nSource: ${currentPlan.path}\\n\\n${currentPlan.content}`);\n }\n if (sections.length === 0) return undefined;\n return { systemPrompt: `${event.systemPrompt}\\n\\n${sections.join('\\n\\n')}` };\n });\n\n pi.on('context', async (event) => ({\n messages: event.messages.filter((message) => {\n const custom = message as { customType?: string };\n return custom.customType !== PLAN_MODE_CONTEXT;\n }),\n }));\n\n pi.on('session_start', async (_event, ctx) => {\n const entries = ctx.sessionManager.getEntries();\n const state = entries\n .flatMap((entry) =>\n entry.type === 'custom' && entry.customType === PLAN_MODE_ENTRY ? [parsePersistedPlanState(entry.data)] : [],\n )\n .filter((value): value is PersistedPlanModeState => value !== undefined)\n .at(-1);\n const piSessionId = ctx.sessionManager.getSessionId();\n const sessionId = planSessionIdentifier(piSessionId);\n voiceQuestionHandoff?.dispose();\n voiceQuestionHandoff = doomIntegrations\n ? createVoiceQuestionHandoff({ bus: pi.events, source: PLAN_LEADER_SOURCE, sessionId: piSessionId })\n : undefined;\n const storedPlan = entries\n .flatMap((entry) =>\n entry.type === 'custom' && entry.customType === PLAN_DOCUMENT_ENTRY ? [parsePlanDocument(entry.data)] : [],\n )\n .filter((value): value is PlanDocument => value !== undefined)\n .at(-1);\n\n const snapshotFromPreviousSession = planSnapshot;\n capabilityCeiling?.dispose();\n capabilityCeiling = undefined;\n currentPlan = storedPlan?.sessionId === sessionId ? storedPlan : undefined;\n activePlanId = state?.planId ?? currentPlan?.planId;\n planReadyForReview = false;\n 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 } else {\n const snapshotToRestore = state?.originalSnapshot ?? snapshotFromPreviousSession;\n if (snapshotToRestore) {\n const restored = await restoreMainAgent(ctx, snapshotToRestore);\n if (restored) {\n pi.setActiveTools(\n restoreSnapshotTools(\n snapshotToRestore.tools,\n pi.getActiveTools(),\n pi.getAllTools().map((tool) => tool.name),\n ),\n );\n }\n } else {\n pi.setActiveTools(\n pi\n .getActiveTools()\n .filter(\n (name) =>\n name !== COMPLETE_PLAN_TOOL &&\n name !== WRITE_PLAN_TOOL &&\n name !== RECORD_DEBUG_EVIDENCE_TOOL &&\n name !== RUN_FABLE_PLAN_TOOL,\n ),\n );\n }\n planSnapshot = undefined;\n updateStatus(ctx);\n }\n });\n\n pi.on('session_shutdown', async (_event, ctx) => {\n await cancelFableOperation('Plan host session is shutting down.');\n if (enabled && planSnapshot) {\n const restored = await restoreMainAgent(ctx, planSnapshot);\n if (restored) {\n pi.setActiveTools(\n restoreSnapshotTools(\n planSnapshot.tools,\n pi.getActiveTools(),\n pi.getAllTools().map((tool) => tool.name),\n ),\n );\n }\n }\n capabilityCeiling?.dispose();\n capabilityCeiling = undefined;\n voiceQuestionHandoff?.dispose();\n voiceQuestionHandoff = undefined;\n awaitingVoicePlanDecision = false;\n planConfigContribution.dispose();\n for (const contribution of planVoiceContributions) contribution.dispose();\n activeContext = undefined;\n await telemetry.shutdown();\n });\n}\n"],"mappings":"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"}
@@ -0,0 +1,79 @@
1
+ import { PlanningAgentConfig, PlanningModeConfig, PlanningThinkingLevel } from "../../schemas/plan/config.cjs";
2
+ import { FablePlanBroker, FableStage } from "./fableFlow.cjs";
3
+ import { PlanTelemetry } from "../../adapters/telemetry/logSinkTelemetry.cjs";
4
+ import { DebugEvidencePacket, PlanningFlavor } from "./prompts.cjs";
5
+ import { ExtensionAPI } from "@earendil-works/pi-coding-agent";
6
+
7
+ //#region src/services/plan/planMode.d.ts
8
+ declare const WRITE_PLAN_TIMEOUT_MS = 5000;
9
+ declare const PLAN_STATE_VERSION: 2;
10
+ interface ModelIdentity {
11
+ provider: string;
12
+ id: string;
13
+ }
14
+ interface PlanSnapshot {
15
+ tools: string[];
16
+ model?: ModelIdentity;
17
+ thinking: PlanningThinkingLevel;
18
+ }
19
+ interface PersistedPlanModeState {
20
+ version: typeof PLAN_STATE_VERSION;
21
+ activeFlavor?: PlanningFlavor;
22
+ originalSnapshot?: PlanSnapshot;
23
+ planId?: string;
24
+ interruptedFableStage?: FableStage;
25
+ }
26
+ interface PlanModeExtensionOptions {
27
+ fableBroker?: FablePlanBroker;
28
+ debugDiagnosticTools?: readonly string[];
29
+ doomIntegrations?: boolean;
30
+ }
31
+ declare function planTitleSlug(markdown: string): string;
32
+ declare function planSessionIdentifier(sessionId: string): string;
33
+ declare function createPlanIdentifier(now?: Date, id?: `${string}-${string}-${string}-${string}-${string}`): string;
34
+ declare function visiblePlanForToolCall(entries: readonly unknown[], toolCallId: string): string | undefined;
35
+ type MutableSubagentStep = {
36
+ model?: string;
37
+ output?: string | boolean;
38
+ progress?: boolean;
39
+ worktree?: boolean;
40
+ parallel?: MutableSubagentStep | MutableSubagentStep[];
41
+ };
42
+ type MutableSubagentRunRequest = {
43
+ [key: string]: unknown;
44
+ model?: string;
45
+ };
46
+ type MutableSubagentInput = MutableSubagentStep & {
47
+ action?: string;
48
+ artifacts?: boolean;
49
+ outputSchema?: Record<string, unknown>;
50
+ share?: boolean;
51
+ tasks?: MutableSubagentStep[];
52
+ chain?: MutableSubagentStep[];
53
+ chainDir?: string;
54
+ sessionDir?: string;
55
+ requests?: MutableSubagentRunRequest[];
56
+ };
57
+ type MutableTaskAssignment = {
58
+ [key: string]: unknown;
59
+ model?: string;
60
+ };
61
+ type MutableTaskInput = {
62
+ action?: string;
63
+ assignments?: MutableTaskAssignment[];
64
+ };
65
+ declare function parsePersistedPlanState(value: unknown): PersistedPlanModeState | undefined;
66
+ declare function parseDebugEvidencePacket(value: unknown): DebugEvidencePacket;
67
+ declare function planModeTools(activeTools: string[], availableTools: string[]): string[];
68
+ declare function constrainSubagentInput(input: MutableSubagentInput): void;
69
+ declare function planningSubagentModel(config: PlanningAgentConfig | undefined, currentModel: ModelIdentity | undefined): string | undefined;
70
+ declare function configurePlanningSubagentInput(input: MutableSubagentInput, model: string): void;
71
+ declare function configurePlanningTaskInput(input: MutableTaskInput, model: string): void;
72
+ declare function isBlockedSubagentManagementAction(action: string | undefined): boolean;
73
+ type PlanningConfigProvider = () => PlanningModeConfig | undefined;
74
+ /** Reads Doom settings from disk so each plan-mode activation sees current configuration. */
75
+ declare function loadPlanningModeConfig(environment?: NodeJS.ProcessEnv, currentDirectory?: string): PlanningModeConfig | undefined;
76
+ declare function planModeExtension(pi: ExtensionAPI, planningConfigProvider?: PlanningConfigProvider, telemetry?: PlanTelemetry, options?: PlanModeExtensionOptions): void;
77
+ //#endregion
78
+ export { ModelIdentity, PlanModeExtensionOptions, PlanSnapshot, PlanningConfigProvider, WRITE_PLAN_TIMEOUT_MS, configurePlanningSubagentInput, configurePlanningTaskInput, constrainSubagentInput, createPlanIdentifier, isBlockedSubagentManagementAction, loadPlanningModeConfig, parseDebugEvidencePacket, parsePersistedPlanState, planModeExtension, planModeTools, planSessionIdentifier, planTitleSlug, planningSubagentModel, visiblePlanForToolCall };
79
+ //# sourceMappingURL=planMode.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"planMode.d.cts","names":[],"sources":["../../../src/services/plan/planMode.ts"],"mappings":";;;;;;;cAyGa,qBAAA;AAAA,cAkDP,kBAAA;AAAA,UA0DW,aAAA;EACf,QAAA;EACA,EAAA;AAAA;AAAA,UAGe,YAAA;EACf,KAAA;EACA,KAAA,GAAQ,aAAA;EACR,QAAA,EAAU,qBAAA;AAAA;AAAA,UAGF,sBAAA;EACR,OAAA,SAAgB,kBAAA;EAChB,YAAA,GAAe,cAAA;EACf,gBAAA,GAAmB,YAAA;EACnB,MAAA;EACA,qBAAA,GAAwB,UAAA;AAAA;AAAA,UAsBT,wBAAA;EACf,WAAA,GAAc,eAAA;EACd,oBAAA;EACA,gBAAA;AAAA;AAAA,iBAGc,aAAA,CAAc,QAAA;AAAA,iBAad,qBAAA,CAAsB,SAAA;AAAA,iBAMtB,oBAAA,CAAqB,GAAA,GAAG,IAAA,EAAe,EAAA;AAAA,iBAQvC,sBAAA,CAAuB,OAAA,sBAA6B,UAAA;AAAA,KAkD/D,mBAAA;EACH,KAAA;EACA,MAAA;EACA,QAAA;EACA,QAAA;EACA,QAAA,GAAW,mBAAA,GAAsB,mBAAA;AAAA;AAAA,KAG9B,yBAAA;EAAA,CACF,GAAA;EACD,KAAA;AAAA;AAAA,KAGG,oBAAA,GAAuB,mBAAA;EAC1B,MAAA;EACA,SAAA;EACA,YAAA,GAAe,MAAA;EACf,KAAA;EACA,KAAA,GAAQ,mBAAA;EACR,KAAA,GAAQ,mBAAA;EACR,QAAA;EACA,UAAA;EACA,QAAA,GAAW,yBAAA;AAAA;AAAA,KAGR,qBAAA;EAAA,CACF,GAAA;EACD,KAAA;AAAA;AAAA,KAGG,gBAAA;EACH,MAAA;EACA,WAAA,GAAc,qBAAA;AAAA;AAAA,iBAuFA,uBAAA,CAAwB,KAAA,YAAiB,sBAAA;AAAA,iBAsDzC,wBAAA,CAAyB,KAAA,YAAiB,mBAAA;AAAA,iBA0C1C,aAAA,CAAc,WAAA,YAAuB,cAAA;AAAA,iBAuErC,sBAAA,CAAuB,KAAA,EAAO,oBAAA;AAAA,iBAoB9B,qBAAA,CACd,MAAA,EAAQ,mBAAA,cACR,YAAA,EAAc,aAAA;AAAA,iBASA,8BAAA,CAA+B,KAAA,EAAO,oBAAA,EAAsB,KAAA;AAAA,iBAK5D,0BAAA,CAA2B,KAAA,EAAO,gBAAA,EAAkB,KAAA;AAAA,iBAKpD,iCAAA,CAAkC,MAAA;AAAA,KAgCtC,sBAAA,SAA+B,kBAAA;;iBAG3B,sBAAA,CACd,WAAA,GAAa,MAAA,CAAO,UAAA,EACpB,gBAAA,YACC,kBAAA;AAAA,iBAyCa,iBAAA,CACd,EAAA,EAAI,YAAA,EACJ,sBAAA,GAAwB,sBAAA,EACxB,SAAA,GAAW,aAAA,EACX,OAAA,GAAS,wBAAA"}
@@ -0,0 +1,79 @@
1
+ import { PlanningAgentConfig, PlanningModeConfig, PlanningThinkingLevel } from "../../schemas/plan/config.mjs";
2
+ import { FablePlanBroker, FableStage } from "./fableFlow.mjs";
3
+ import { PlanTelemetry } from "../../adapters/telemetry/logSinkTelemetry.mjs";
4
+ import { DebugEvidencePacket, PlanningFlavor } from "./prompts.mjs";
5
+ import { ExtensionAPI } from "@earendil-works/pi-coding-agent";
6
+
7
+ //#region src/services/plan/planMode.d.ts
8
+ declare const WRITE_PLAN_TIMEOUT_MS = 5000;
9
+ declare const PLAN_STATE_VERSION: 2;
10
+ interface ModelIdentity {
11
+ provider: string;
12
+ id: string;
13
+ }
14
+ interface PlanSnapshot {
15
+ tools: string[];
16
+ model?: ModelIdentity;
17
+ thinking: PlanningThinkingLevel;
18
+ }
19
+ interface PersistedPlanModeState {
20
+ version: typeof PLAN_STATE_VERSION;
21
+ activeFlavor?: PlanningFlavor;
22
+ originalSnapshot?: PlanSnapshot;
23
+ planId?: string;
24
+ interruptedFableStage?: FableStage;
25
+ }
26
+ interface PlanModeExtensionOptions {
27
+ fableBroker?: FablePlanBroker;
28
+ debugDiagnosticTools?: readonly string[];
29
+ doomIntegrations?: boolean;
30
+ }
31
+ declare function planTitleSlug(markdown: string): string;
32
+ declare function planSessionIdentifier(sessionId: string): string;
33
+ declare function createPlanIdentifier(now?: Date, id?: `${string}-${string}-${string}-${string}-${string}`): string;
34
+ declare function visiblePlanForToolCall(entries: readonly unknown[], toolCallId: string): string | undefined;
35
+ type MutableSubagentStep = {
36
+ model?: string;
37
+ output?: string | boolean;
38
+ progress?: boolean;
39
+ worktree?: boolean;
40
+ parallel?: MutableSubagentStep | MutableSubagentStep[];
41
+ };
42
+ type MutableSubagentRunRequest = {
43
+ [key: string]: unknown;
44
+ model?: string;
45
+ };
46
+ type MutableSubagentInput = MutableSubagentStep & {
47
+ action?: string;
48
+ artifacts?: boolean;
49
+ outputSchema?: Record<string, unknown>;
50
+ share?: boolean;
51
+ tasks?: MutableSubagentStep[];
52
+ chain?: MutableSubagentStep[];
53
+ chainDir?: string;
54
+ sessionDir?: string;
55
+ requests?: MutableSubagentRunRequest[];
56
+ };
57
+ type MutableTaskAssignment = {
58
+ [key: string]: unknown;
59
+ model?: string;
60
+ };
61
+ type MutableTaskInput = {
62
+ action?: string;
63
+ assignments?: MutableTaskAssignment[];
64
+ };
65
+ declare function parsePersistedPlanState(value: unknown): PersistedPlanModeState | undefined;
66
+ declare function parseDebugEvidencePacket(value: unknown): DebugEvidencePacket;
67
+ declare function planModeTools(activeTools: string[], availableTools: string[]): string[];
68
+ declare function constrainSubagentInput(input: MutableSubagentInput): void;
69
+ declare function planningSubagentModel(config: PlanningAgentConfig | undefined, currentModel: ModelIdentity | undefined): string | undefined;
70
+ declare function configurePlanningSubagentInput(input: MutableSubagentInput, model: string): void;
71
+ declare function configurePlanningTaskInput(input: MutableTaskInput, model: string): void;
72
+ declare function isBlockedSubagentManagementAction(action: string | undefined): boolean;
73
+ type PlanningConfigProvider = () => PlanningModeConfig | undefined;
74
+ /** Reads Doom settings from disk so each plan-mode activation sees current configuration. */
75
+ declare function loadPlanningModeConfig(environment?: NodeJS.ProcessEnv, currentDirectory?: string): PlanningModeConfig | undefined;
76
+ declare function planModeExtension(pi: ExtensionAPI, planningConfigProvider?: PlanningConfigProvider, telemetry?: PlanTelemetry, options?: PlanModeExtensionOptions): void;
77
+ //#endregion
78
+ export { ModelIdentity, PlanModeExtensionOptions, PlanSnapshot, PlanningConfigProvider, WRITE_PLAN_TIMEOUT_MS, configurePlanningSubagentInput, configurePlanningTaskInput, constrainSubagentInput, createPlanIdentifier, isBlockedSubagentManagementAction, loadPlanningModeConfig, parseDebugEvidencePacket, parsePersistedPlanState, planModeExtension, planModeTools, planSessionIdentifier, planTitleSlug, planningSubagentModel, visiblePlanForToolCall };
79
+ //# sourceMappingURL=planMode.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"planMode.d.mts","names":[],"sources":["../../../src/services/plan/planMode.ts"],"mappings":";;;;;;;cAyGa,qBAAA;AAAA,cAkDP,kBAAA;AAAA,UA0DW,aAAA;EACf,QAAA;EACA,EAAA;AAAA;AAAA,UAGe,YAAA;EACf,KAAA;EACA,KAAA,GAAQ,aAAA;EACR,QAAA,EAAU,qBAAA;AAAA;AAAA,UAGF,sBAAA;EACR,OAAA,SAAgB,kBAAA;EAChB,YAAA,GAAe,cAAA;EACf,gBAAA,GAAmB,YAAA;EACnB,MAAA;EACA,qBAAA,GAAwB,UAAA;AAAA;AAAA,UAsBT,wBAAA;EACf,WAAA,GAAc,eAAA;EACd,oBAAA;EACA,gBAAA;AAAA;AAAA,iBAGc,aAAA,CAAc,QAAA;AAAA,iBAad,qBAAA,CAAsB,SAAA;AAAA,iBAMtB,oBAAA,CAAqB,GAAA,GAAG,IAAA,EAAe,EAAA;AAAA,iBAQvC,sBAAA,CAAuB,OAAA,sBAA6B,UAAA;AAAA,KAkD/D,mBAAA;EACH,KAAA;EACA,MAAA;EACA,QAAA;EACA,QAAA;EACA,QAAA,GAAW,mBAAA,GAAsB,mBAAA;AAAA;AAAA,KAG9B,yBAAA;EAAA,CACF,GAAA;EACD,KAAA;AAAA;AAAA,KAGG,oBAAA,GAAuB,mBAAA;EAC1B,MAAA;EACA,SAAA;EACA,YAAA,GAAe,MAAA;EACf,KAAA;EACA,KAAA,GAAQ,mBAAA;EACR,KAAA,GAAQ,mBAAA;EACR,QAAA;EACA,UAAA;EACA,QAAA,GAAW,yBAAA;AAAA;AAAA,KAGR,qBAAA;EAAA,CACF,GAAA;EACD,KAAA;AAAA;AAAA,KAGG,gBAAA;EACH,MAAA;EACA,WAAA,GAAc,qBAAA;AAAA;AAAA,iBAuFA,uBAAA,CAAwB,KAAA,YAAiB,sBAAA;AAAA,iBAsDzC,wBAAA,CAAyB,KAAA,YAAiB,mBAAA;AAAA,iBA0C1C,aAAA,CAAc,WAAA,YAAuB,cAAA;AAAA,iBAuErC,sBAAA,CAAuB,KAAA,EAAO,oBAAA;AAAA,iBAoB9B,qBAAA,CACd,MAAA,EAAQ,mBAAA,cACR,YAAA,EAAc,aAAA;AAAA,iBASA,8BAAA,CAA+B,KAAA,EAAO,oBAAA,EAAsB,KAAA;AAAA,iBAK5D,0BAAA,CAA2B,KAAA,EAAO,gBAAA,EAAkB,KAAA;AAAA,iBAKpD,iCAAA,CAAkC,MAAA;AAAA,KAgCtC,sBAAA,SAA+B,kBAAA;;iBAG3B,sBAAA,CACd,WAAA,GAAa,MAAA,CAAO,UAAA,EACpB,gBAAA,YACC,kBAAA;AAAA,iBAyCa,iBAAA,CACd,EAAA,EAAI,YAAA,EACJ,sBAAA,GAAwB,sBAAA,EACxB,SAAA,GAAW,aAAA,EACX,OAAA,GAAS,wBAAA"}
@@ -0,0 +1,6 @@
1
+ import{getHarnessState as e,loadDoomConfig as t,loadHarnessState as n,resolvePlanningPlansDirectory as r}from"../../schemas/plan/config.mjs";import{FABLE_PLAN_PROFILE as i,createFablePlanFlow as a}from"./fableFlow.mjs";import{PLAN_EVENT as o,createPlanTelemetry as s}from"../../adapters/telemetry/logSinkTelemetry.mjs";import{planConfigSections as c,planSettingByFieldId as l}from"../../schemas/plan/planConfig.mjs";import{buildFlavorPlanningPrompt as u}from"./prompts.mjs";import{globalDoomConfigPath as ee}from"@agimon-ai/doompi-config";import{Buffer as te}from"node:buffer";import{createHash as d,randomUUID as f}from"node:crypto";import p from"node:fs";import ne from"node:os";import re from"node:path";import{setDoomConfigValue as ie,unsetDoomConfigValue as ae}from"@agimon-ai/doompi-config/configWriter";import{FABLE_PLAN_REQUESTER as oe,createFablePlanBrokerClient as m}from"@agimon-ai/doompi-extension-contracts/fable-plan";import{MINOR_MODE_TOOL_NAME as h,registerMinorModeOwner as g}from"@agimon-ai/doompi-extension-contracts/mode";import{createProtocolRuntime as se}from"@agimon-ai/doompi-extension-contracts/protocol";import{registerSubagentPolicy as ce}from"@agimon-ai/doompi-extension-contracts/subagent-policy";import{isSubagentAction as _,subagentActionAcceptsField as v}from"@agimon-ai/doompi-extension-contracts/subagent-tool";import{VOICE_MODE_TOOL_NAMES as y,registerVoiceToolContribution as b}from"@agimon-ai/doompi-extension-contracts/voice-tools";import{CONFIG_ACTION as le,registerDoomConfigContribution as ue}from"@agimon-ai/doompi-ui/config";import{createDoomLeaderContribution as de,registerDoomLeaderActionHandlers as fe}from"@agimon-ai/doompi-ui/leader";import{buildVoiceToolResult as pe,createVoiceQuestionHandoff as me}from"@agimon-ai/doompi-user-feedback/voice-question-handoff";const he=`agent-harness-plan-mode`,ge=`agent-harness-plan-document`,x=`complete_plan`,S=`write_plan`,C=`ask_user_question`,_e=`bash`,w=`find`,T=`grep`,E=`read`,ve=`subagent`,ye=`task`,D=`record_debug_evidence`,O=`run_fable_plan`,k=`implementation-plan`,A=`@agimon-ai/doompi-plan`,be=`activate`,xe=`deactivate`,j=`warning`,Se=`info`,Ce=`Refused to write the plan because its destination is not a regular file.`,M=`plan.trigger`,we=`plan.model`,N=`plan.flavor`,Te=`not_found`,Ee=`unauthenticated`,De=`restore_failed`,Oe=new Set,ke=5e3,Ae=`Exit plan mode and start implementation`,je=`Continue planning`,P=`exit`,Me=`continue`,Ne={questions:[{question:`Plan complete. What would you like to do?`,header:`Plan review`,options:[{label:Ae,description:`Leave read-only plan mode and begin implementing the approved plan.`},{label:je,description:`Remain in read-only plan mode and refine the implementation plan.`}]}]},F=new Set([`add_directory`,C,_e,x,w,T,`intercom`,`ls`,h,...y,E,`search_external_files`,ve,`subagent_supervisor`,`subagent_wait`,ye,S]),I=new Set([`edit`,`write`,`mcp`]),L=[E,_e,T,w,`ls`],Pe=[...L,`mcp`],R=`leader`,Fe=`voice_tool`,z=[`off`,`minimal`,`low`,`medium`,`high`,`xhigh`,`max`],B=[`idle`,`draft`,`review`,`completed`,`failed`,`cancelled`,`interrupted`],Ie=`@agimon-ai/doompi-plan`,V={type:`string`,enum:[...[`normal`,`debug`,`fable`]]},Le={type:`object`,properties:{flavor:V},required:[`flavor`],additionalProperties:!1},Re={type:`object`,properties:{},additionalProperties:!1},ze={type:`object`,properties:{active:{type:`boolean`},flavor:{anyOf:[{...V},{type:`null`}]},changed:{type:`boolean`}},required:[`active`,`flavor`,`changed`],additionalProperties:!1},H=new Set([x,S,D,O,h,...y]);function Be(e){return(e.match(/^#\s+(.+?)\s*$/m)?.[1]??k).normalize(`NFKD`).replace(/[\u0300-\u036f]/g,``).toLowerCase().replace(/[^a-z0-9]+/g,`-`).replace(/^-+|-+$/g,``).slice(0,64)||k}function Ve(e){return`${encodeURIComponent(e).slice(0,48)||`session`}--${d(`sha256`).update(e).digest(`hex`)}`}function U(e=new Date,t=f()){return`${e.toISOString().replace(/[-:]/g,``).replace(/\.\d{3}Z$/,`Z`)}--${t}`}function He(e,t){for(let n=e.length-1;n>=0;--n){let r=e[n];if(!r||typeof r!=`object`)continue;let i=r;if(i.type!==`message`||i.message?.role!==`assistant`||!Array.isArray(i.message.content))continue;let a=i.message.content.findIndex(e=>{if(!e||typeof e!=`object`)return!1;let n=e;return n.type===`toolCall`&&n.id===t&&n.name===S});if(!(a<0))return i.message.content.slice(0,a).flatMap(e=>{if(!e||typeof e!=`object`)return[];let t=e;return t.type===`text`&&typeof t.text==`string`?[t.text]:[]}).join(`
2
+
3
+ `).trim()||void 0}}function Ue(e){return e.reason instanceof Error?e.reason:Error(`Plan write cancelled.`)}async function W(e,t){if(t.aborted)throw Ue(t);let n,r=new Promise((e,r)=>{n=()=>r(Ue(t)),t.addEventListener(`abort`,n,{once:!0})});try{return await Promise.race([e(),r])}finally{n&&t.removeEventListener(`abort`,n)}}function G(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function K(e){return typeof e==`string`}function q(e){if(!(!G(e)||!K(e.provider)||!K(e.id))&&!(!e.provider||!e.id||e.provider.length>128||e.id.length>128))return{provider:e.provider,id:e.id}}function We(e){return K(e)&&z.includes(e)?e:void 0}function Ge(e){return K(e)&&B.includes(e)?e:void 0}function Ke(e){if(!(!Array.isArray(e)||e.some(e=>!K(e))))return e.map(e=>e)}function qe(e){if(!G(e))return;let t=Ke(e.tools),n=We(e.thinking);if(!t||n===void 0)return;let r=e.model===void 0?void 0:q(e.model);if(!(e.model!==void 0&&!r))return{tools:t,...r?{model:r}:{},thinking:n}}function Je(e){return e===`normal`||e===`debug`||e===`fable`?e:void 0}function Ye(e){if(e.version!==2)return;let t=e.activeFlavor===void 0?void 0:Je(e.activeFlavor);if(e.activeFlavor!==void 0&&!t)return;let n=e.originalSnapshot===void 0?void 0:qe(e.originalSnapshot);if(e.originalSnapshot!==void 0&&!n)return;let r=e.planId===void 0?void 0:K(e.planId)?e.planId:void 0;if(e.planId!==void 0&&r===void 0)return;let i=e.interruptedFableStage===void 0?void 0:Ge(e.interruptedFableStage);if(!(e.interruptedFableStage!==void 0&&!i))return{version:2,...t?{activeFlavor:t}:{},...n?{originalSnapshot:n}:{},...r?{planId:r}:{},...i?{interruptedFableStage:i}:{}}}function Xe(e){if(e.enabled!==!0&&e.enabled!==!1)return;let t=e.toolsBeforePlanMode===void 0?void 0:Ke(e.toolsBeforePlanMode);if(e.toolsBeforePlanMode!==void 0&&!t)return;let n=e.modelBeforePlanMode===void 0?void 0:q(e.modelBeforePlanMode);if(e.modelBeforePlanMode!==void 0&&!n)return;let r=e.thinkingBeforePlanMode===void 0?void 0:We(e.thinkingBeforePlanMode);if(e.thinkingBeforePlanMode!==void 0&&r===void 0)return;let i=e.planId===void 0?void 0:K(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 Ze(e){if(G(e))return Ye(e)??Xe(e)}function Qe(e){if(!G(e)||!K(e.content)||!K(e.path)||!K(e.sessionId)||!K(e.title)||!K(e.writtenAt))return;let t=e.planId===void 0?void 0:K(e.planId)?e.planId:void 0;if(!(e.planId!==void 0&&t===void 0))return{content:e.content,path:e.path,sessionId:e.sessionId,...t?{planId:t}:{},title:e.title,writtenAt:e.writtenAt}}function J(e,t,n){if(!K(e))throw Error(`${t} must be a string.`);let r=e.trim();if(n&&!r)throw Error(`${t} must not be empty.`);if(te.byteLength(r,`utf8`)>4096)throw Error(`${t} exceeds the bounded debug evidence limit.`);return r}function $e(e,t){if(!Array.isArray(e)||e.length>32)throw Error(`${t} must be a bounded string array.`);return e.map((e,n)=>J(e,`${t}[${n}]`,!1))}function et(e,t){return e===void 0?``:J(e,t,!1)}function Y(e,t){return e===void 0?[]:$e(e,t)}function tt(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 nt(e){if(!G(e))throw Error(`Debug evidence must be an object.`);tt(e,[`issue`,`expectedBehavior`,`reproductionAttempt`,`actualBehavior`,`logs`,`correlatedTraceEvidence`,`processOutput`,`browserConsoleEvidence`,`correlationIds`,`timestamps`,`verifiedFacts`,`hypotheses`,`unavailableEvidence`],`Debug evidence`);let t={issue:J(e.issue,`issue`,!0),expectedBehavior:et(e.expectedBehavior,`expectedBehavior`),reproductionAttempt:et(e.reproductionAttempt,`reproductionAttempt`),actualBehavior:et(e.actualBehavior,`actualBehavior`),logs:Y(e.logs,`logs`),correlatedTraceEvidence:Y(e.correlatedTraceEvidence,`correlatedTraceEvidence`),processOutput:Y(e.processOutput,`processOutput`),browserConsoleEvidence:Y(e.browserConsoleEvidence,`browserConsoleEvidence`),correlationIds:Y(e.correlationIds,`correlationIds`),timestamps:Y(e.timestamps,`timestamps`),verifiedFacts:Y(e.verifiedFacts,`verifiedFacts`),hypotheses:Y(e.hypotheses,`hypotheses`),unavailableEvidence:Y(e.unavailableEvidence,`unavailableEvidence`)};if(te.byteLength(JSON.stringify(t),`utf8`)>32768)throw Error(`Debug evidence exceeds the bounded packet size.`);return t}function X(e,t){let n=new Set(t),r=e.filter(e=>F.has(e)&&n.has(e)),i=e.filter(e=>!F.has(e)&&!I.has(e)&&!H.has(e)&&n.has(e)),a=L.filter(e=>n.has(e)),o=[x,ye,S].filter(e=>n.has(e));return[...new Set([...r,...i,...a,...o])]}function rt(e,t,n,r,i){let a=new Set(n),o=t.filter(e=>!F.has(e)&&!I.has(e)&&!H.has(e)),s=X([...new Set([...e,...o])].filter(e=>!H.has(e)),n),c=r===`debug`?[D,...i].filter(e=>a.has(e)):[];r===`fable`&&a.has(O)&&c.push(O);for(let e of[h,...y])t.includes(e)&&a.has(e)&&c.push(e);return[...new Set([...s,...c])]}function it(e,t,n){let r=new Set(n),i=new Set(t),a=e.filter(e=>!H.has(e)&&r.has(e)&&(e!==C||i.has(e))),o=t.filter(e=>!F.has(e)&&!I.has(e)&&!H.has(e)&&r.has(e)),s=[h,...y].filter(e=>i.has(e)&&r.has(e));return[...new Set([...a,...o,...s])]}function Z(e){if(e.output=!1,e.progress=!1,e.worktree=!1,Array.isArray(e.parallel))for(let t of e.parallel)Z(t);else e.parallel&&Z(e.parallel)}function at(e){if(e.action!==void 0){_(e.action)&&v(e.action,`artifacts`)&&(e.artifacts=!1);return}e.artifacts=!1,e.output=!1,e.share=!1,e.progress=!1,e.worktree=!1,delete e.chainDir,delete e.outputSchema,delete e.sessionDir;for(let t of e.tasks??[])Z(t);for(let t of e.chain??[])Z(t)}function ot(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 st(e,t){if(e.action===`run`)for(let n of e.requests??[])n.model=t}function ct(e,t){if(e.action===`assign`)for(let n of e.assignments??[])n.model=t}function lt(e){return new Set([`append-step`,`create`,`delete`,`disable`,`eject`,`enable`,`grant-spawn-budget`,`reset`,`schedule`,`schedule-cancel`,`update`,`watchdog.configure`]).has(e??``)}function ut(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 dt(e=process.env,r=process.cwd()){return t(n(e).state.root??r).modes?.planning}function ft(e){let t={key:`p`,label:`plan`,order:60};return[{id:`plan.normal`,path:[t,{key:`p`,label:`normal`,detail:`read-only planning`}],action:{name:`plan.normal`}},{id:`plan.debug`,path:[t,{key:`d`,label:`debug`,detail:`adaptive debug planning`}],action:{name:`plan.debug`}},{id:`plan.fable`,path:[t,{key:`f`,label:`fable`,detail:`repository-aware draft`}],action:{name:`plan.fable`}},...e?[{id:`plan.exit`,path:[t,{key:`e`,label:`exit`,detail:`restore and exit`}],action:{name:`plan.exit`}}]:[]]}function pt(t,n=dt,d=s(),f={}){let h=!1,_,v,y,C,w,T,E=!1,k=!1,I,L,z=`idle`,B,V,H,K=Promise.resolve(),q=f.doomIntegrations??!0,We=new Set(f.debugDiagnosticTools??Oe),Ge=m(()=>V?.sessionManager.getSessionId()),Ke=f.fableBroker??{start:(e,t)=>Ge.start(e,t),cancel:(e,t)=>Ge.cancel({requester:oe,operationId:e,reason:t})},qe=()=>f.fableBroker!==void 0||Ge.isAvailable(),Ye=q?g(t,{descriptor:{source:A,id:`plan`,label:`Plan`,description:`Read-only planning with normal, debug, and Fable flavors.`,order:40,actions:[{id:be,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:xe,label:`Deactivate`,description:`Exit plan mode and restore the previous agent configuration.`,contexts:[`tui`,`headless`],parameters:[]}]},initialState:{activation:`inactive`,condition:`ready`,actions:[{id:be,enabled:!0},{id:xe,enabled:!1,disabledReason:`Plan mode is inactive.`}]},async handleAction(e,t,n){if(e===be){let e=t.flavor;if(e!==`normal`&&e!==`debug`&&e!==`fable`)throw Error(`A valid plan flavor is required.`);return await Q(()=>$(n.context,e,R,!1)),{message:`Plan mode is active with the ${e} flavor.`}}if(e===xe)return{message:await Q(()=>_t(n.context,R))?`Plan mode deactivated.`:`Plan mode remains active.`};throw Error(`Unknown plan mode action: ${e}`)},onError:e=>void d.recordError(o.configLoadFailed,e)}):{publish:()=>void 0},Xe=q?de(t,{source:A,bindings:ft(!1)}):{update:()=>void 0},J,$e=q?ue(t,{source:A,listSections:()=>{try{return c(n(),J)}catch(e){return c(void 0,e instanceof Error?e.message:String(e))}},handlers:{[le.set]:async({fieldId:e,value:t})=>{let n=l(e);!n||!t||(J=void 0,await ie(ee(),[...n.keyPath],t))},[le.clear]:async({fieldId:e})=>{let t=l(e);t&&(J=void 0,await ae(ee(),[...t.keyPath]))}},onError:e=>{J=e instanceof Error?e.message:String(e)}}):{dispose:()=>void 0,update:()=>void 0};function et(){Xe.update(ft(h))}function Y(e){et();let t=_,n=h&&t?`plan:${t}`:void 0,r=t===`fable`&&z!==`idle`?` · ${z}`:``;e.ui.setStatus(`plan-mode`,n?e.ui.theme.fg(j,n):void 0);let i={activation:h?`active`:`inactive`,condition:z===`failed`?`failed`:z===`interrupted`?`degraded`:`ready`,...h&&t?{detail:`${t}${r} - read only`,color:j}:{},actions:[{id:be,enabled:!0},...h?[{id:xe,enabled:!0}]:[{id:xe,enabled:!1,disabledReason:`Plan mode is inactive.`}]]};Ye.publish(i)}function tt(){if(!q||!h||!_||!V)return;let e={owner:A,allowedTools:[...Pe],requiredTools:[_e,`mcp`],allowMcpTools:!0,allowedExternalProfiles:_===`fable`?[i]:[],denyExtensions:!1};C?C.update(e):C=ce(se({bus:t.events,source:A,sessionId:V.sessionManager.getSessionId()}),e)}function X(){t.appendEntry(he,{version:2,...h&&_?{activeFlavor:_}:{},...v?{originalSnapshot:{tools:[...v.tools],...v.model?{model:{...v.model}}:{},thinking:v.thinking}}:{},...T?{planId:T}:{},...B?{interruptedFableStage:B}:{}})}function Z(e,t,n){d.recordWarning(o.modelResolveFailed,`Planning model ${n}: ${t}`,{[we]:t,"plan.model.phase":e,"plan.model.reason":n})}async function pt(e){let n=y?.main;if(n){if(n.model){let r=ut(n.model,e);r?await t.setModel(r)||(e.ui.notify(`Planning model is unavailable without authentication: ${n.model}`,j),Z(`apply`,n.model,Ee)):(e.ui.notify(`Planning model not found: ${n.model}`,j),Z(`apply`,n.model,Te))}n.thinking&&t.setThinkingLevel(n.thinking)}}async function mt(e,n){if(!n)return!1;if(n.model){let r=`${n.model.provider}/${n.model.id}`,i=e.modelRegistry.find(n.model.provider,n.model.id);if(!i)return e.ui.notify(`Previous model not found: ${r}`,j),Z(`restore`,r,Te),!1;try{if(!await t.setModel(i))return e.ui.notify(`Previous model is unavailable without authentication: ${r}`,j),Z(`restore`,r,Ee),!1}catch(t){return e.ui.notify(`Previous model could not be restored: ${r}`,j),Z(`restore`,r,De),d.recordError(o.modelResolveFailed,t,{[we]:r,"plan.model.phase":`restore`,"plan.model.reason":De}),!1}}return t.setThinkingLevel(n.thinking),!0}function ht(e){h&&_?e.ui.notify(`Already in plan:${_}.`,Se):e.ui.notify(`Plan mode is inactive.`,Se)}function Q(e){let t=K.then(e,e);return K=t.then(()=>void 0,()=>void 0),t}async function gt(e){if(!H&&!z.match(/^(draft|review)$/u))return;(z===`draft`||z===`review`)&&(B=z),yt.cancel(e);let t=H;if(t)try{await t}catch(e){d.recordError(o.modeDisabled,e,{[N]:`fable`,"plan.fable.reason":`cancel_failed`})}H=void 0,B&&(z=`interrupted`),V&&Y(V),X()}async function $(e,r,i,a){if(V=e,h){if(_===r){ht(e);return}_===`fable`&&await gt(`Leaving Fable for plan:${r}.`),_=r,tt(),t.setActiveTools(rt(v?.tools??[],t.getActiveTools(),t.getAllTools().map(e=>e.name),r,We)),Y(e),X(),d.recordEvent(o.modeEnabled,{[M]:i,[N]:r,"plan.tool.count":t.getActiveTools().length});return}let s;try{s=n()}catch(e){throw d.recordError(o.configLoadFailed,e,{[M]:i}),e}a?T??=U():(w=void 0,E=!1,k=!1,T=U(),B=void 0,z=`idle`),y=s,v??={tools:[...t.getActiveTools()],...e.model?{model:{provider:e.model.provider,id:e.model.id}}:{},thinking:t.getThinkingLevel()},h=!0,_=r,t.setActiveTools(rt(v.tools,t.getActiveTools(),t.getAllTools().map(e=>e.name),r,We)),tt(),await pt(e),Y(e),X(),d.recordEvent(o.modeEnabled,{[M]:i,[N]:r,"plan.tool.count":t.getActiveTools().length,...y?.main?.model?{[we]:y.main.model}:{},...y?.main?.thinking?{"plan.thinking":y.main.thinking}:{}})}async function _t(e,n){if(!h)return ht(e),!1;await gt(`Plan mode is exiting.`);let r=v;if(!await mt(e,r))return e.ui.notify(`Previous model was not restored. Plan mode remains read-only.`,j),Y(e),X(),d.recordWarning(o.modeDisabled,`Plan mode restoration failed.`,{[M]:n,[N]:_??`unknown`,"plan.restored":!1}),!1;let i=!!w;return t.setActiveTools(it(r?.tools??[],t.getActiveTools(),t.getAllTools().map(e=>e.name))),C?.dispose(),C=void 0,h=!1,_=void 0,y=void 0,k=!1,v=void 0,L=void 0,z=`idle`,B=void 0,Y(e),X(),d.recordEvent(o.modeDisabled,{[M]:n,[N]:`normal`,"plan.written":i,"plan.restored":!0}),!0}let vt=[];if(q){let e=t,n=e=>{if(e.aborted)throw e.reason instanceof Error?e.reason:Error(`Plan voice transition was cancelled.`)},r={descriptor:{source:Ie,id:`plan-activate`,name:`activate_plan`,label:`Activate Plan`,description:`Activate or switch the serialized read-only planning mode flavor.`,order:10,inputSchema:Le,resultSchema:ze},execute(e,t){let r=Je(G(e)?e.flavor:void 0);if(!r)throw Error(`A valid plan flavor is required.`);return n(t.signal),Q(async()=>{n(t.signal);let e=h,i=_;return await $(t.context,r,Fe,!1),{active:h,flavor:_??r,changed:e!==h||i!==_}})}},i={descriptor:{source:Ie,id:`plan-exit`,name:`exit_plan`,label:`Exit Plan`,description:`Exit read-only planning mode and restore the previous agent configuration.`,order:20,inputSchema:Re,resultSchema:ze},execute(e,t){return n(t.signal),Q(async()=>{n(t.signal);let e=h,r=await _t(t.context,Fe);return{active:h,flavor:_??null,changed:e!==h||r}})}};vt.push(b(e,r)),vt.push(b(e,i))}let yt=a({broker:Ke,isAuthorized:()=>h&&_===`fable`&&qe(),onStage:e=>{z=e,(e===`completed`||e===`failed`||e===`cancelled`)&&(B=void 0),V&&Y(V),h&&X()},onError:e=>{d.recordError(o.modeEnabled,e,{[N]:`fable`,"plan.fable.reason":`flow_error`})}});t.registerTool({name:D,label:`Record Debug Evidence`,description:`Record optional bounded debug evidence while planning a repository fix.`,promptSnippet:`Record the issue and any relevant reproduction, logs, traces, process output, or browser evidence`,parameters:{type:`object`,properties:{issue:{type:`string`},expectedBehavior:{type:`string`},reproductionAttempt:{type:`string`},actualBehavior:{type:`string`},logs:{type:`array`,items:{type:`string`}},correlatedTraceEvidence:{type:`array`,items:{type:`string`}},processOutput:{type:`array`,items:{type:`string`}},browserConsoleEvidence:{type:`array`,items:{type:`string`}},correlationIds:{type:`array`,items:{type:`string`}},timestamps:{type:`array`,items:{type:`string`}},verifiedFacts:{type:`array`,items:{type:`string`}},hypotheses:{type:`array`,items:{type:`string`}},unavailableEvidence:{type:`array`,items:{type:`string`}}},required:[`issue`],additionalProperties:!1},async execute(e,t){if(!h||_!==`debug`)return{content:[{type:`text`,text:`Debug planning is not active.`}],details:{recorded:!1}};try{L=nt(t)}catch(e){throw d.recordWarning(o.modeEnabled,e,{[N]:`debug`,"plan.debug.reason":`invalid_evidence`}),e}return V&&Y(V),{content:[{type:`text`,text:`Debug evidence recorded as optional planning context.`}],details:{recorded:!0}}}}),t.registerTool({name:O,label:`Run Fable Plan`,description:`Send a bounded, sanitized planning packet to the local Fable broker for one repository-aware draft.`,promptSnippet:`Run the local Fable draft with the bounded planning packet`,parameters:{type:`object`,properties:{goal:{type:`array`,items:{type:`string`}},constraints:{type:`array`,items:{type:`string`}},decisions:{type:`array`,items:{type:`string`}},verifiedFindings:{type:`array`,items:{type:`object`}},inferredFindings:{type:`array`,items:{type:`string`}},unresolvedQuestions:{type:`array`,items:{type:`string`}},currentPlan:{type:`string`}},additionalProperties:!1},async execute(e,t,n){if(!h||_!==`fable`)return{content:[{type:`text`,text:`Fable planning is not active.`}],details:{started:!1}};if(!qe())return{content:[{type:`text`,text:`The local Fable broker is unavailable. Fable planning remains disabled.`}],details:{started:!1,errorCode:`broker_unavailable`}};let r=yt.run(t,n);H=r;try{let e=await r;return{content:[{type:`text`,text:e.draft?`Fable draft:\n${e.draft}`:`Fable planning ${e.status}: ${e.errorCode??`no output`}.`}],details:{...e,started:!0}}}finally{H===r&&(H=void 0)}}}),t.registerTool({name:S,label:`Write Plan`,description:`After presenting the complete implementation plan as Markdown in chat, save that visible plan to a unique Markdown file in the configured plans directory.`,promptSnippet:`Save the implementation plan already presented in chat to the configured plans directory`,parameters:{type:`object`,properties:{},additionalProperties:!1},async execute(n,i,a,s,c){if(!h)return{content:[{type:`text`,text:`Plan mode is disabled. Use normal file tools instead.`}],details:{written:!1}};let l=He(c.sessionManager.getBranch(),n);if(!l){let e=Error(`Present the complete implementation plan as visible Markdown in chat before calling write_plan.`);throw d.recordError(o.writePlanFailed,e,{"plan.reason":`missing_visible_plan`}),e}let u=Date.now(),ee=e().root??c.cwd,f=r(y?.plansDirectory,ee,ne.homedir()),ie=Ve(c.sessionManager.getSessionId());T??=U();let ae=w?.planId===T&&re.dirname(w.path)===f?w.path:void 0,oe=Be(l),m=T,g=ae??re.join(f,`${oe}--${m}.md`),se=new AbortController,ce=Error(`Writing ${g} timed out after ${ke}ms.`),_=setTimeout(()=>se.abort(ce),ke),v=a?AbortSignal.any([a,se.signal]):se.signal,b=`checking`,le=()=>{s?.({content:[{type:`text`,text:`${b===`checking`?`Checking`:`Writing`} ${g}...`}],details:{phase:b,path:g,startedAt:u}})},ue=(e,t)=>{d.recordWarning(o.writePlanUnsafePath,t,{"plan.phase":b,"plan.refusal":e})};try{le();try{await W(()=>p.promises.mkdir(f,{recursive:!0,mode:448}),v)}catch(e){throw v.aborted?Ue(v):Error(`Could not create the configured plans directory ${f}.`,{cause:e})}let e=await W(()=>p.promises.lstat(f),v),n=await W(()=>p.promises.realpath(f),v);if(e.isSymbolicLink()||!e.isDirectory())return ue(`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:g,phase:b,durationMs:Date.now()-u}};b=`writing`,le();let r;try{for(let e=0;e<3;e+=1)try{let e=ae?0:p.constants.O_EXCL;r=await W(()=>p.promises.open(g,p.constants.O_CREAT|p.constants.O_WRONLY|p.constants.O_NONBLOCK|p.constants.O_NOFOLLOW|e,384),v);break}catch(t){if(!(!ae&&t instanceof Error&&`code`in t&&String(t.code)===`EEXIST`)||e===2)throw t;m=U(),T=m,g=re.join(f,`${oe}--${m}.md`),X()}if(!r)throw Error(`Could not allocate a unique plan filename in ${f}.`);let e=await W(()=>p.promises.realpath(f),v),t=await W(()=>r.stat(),v);if(e!==n||!t.isFile()||t.nlink!==1)return ue(`invalid_destination`,Ce),{content:[{type:`text`,text:Ce}],details:{written:!1,path:g,phase:b,durationMs:Date.now()-u}};await W(()=>r.truncate(0),v),await W(()=>r.writeFile(l,{encoding:`utf8`,signal:v}),v)}catch(e){if(e instanceof Error&&`code`in e&&[`EISDIR`,`ELOOP`,`ENXIO`].includes(String(e.code)))return ue(`invalid_destination`,Ce),{content:[{type:`text`,text:Ce}],details:{written:!1,path:g,phase:b,durationMs:Date.now()-u}};throw e}finally{await r?.close()}return w={content:l,path:g,sessionId:ie,planId:m,title:oe,writtenAt:new Date().toISOString()},t.appendEntry(ge,w),E=!0,k=!1,d.recordEvent(o.planWritten,{"plan.written":!0,"plan.bytes":te.byteLength(l,`utf8`),"plan.duration_ms":Date.now()-u,"plan.revised":!!ae}),{content:[{type:`text`,text:`Wrote implementation plan to ${g}.`}],details:{written:!0,path:g,phase:b,durationMs:Date.now()-u}}}catch(e){let t={"plan.phase":b,"plan.duration_ms":Date.now()-u};if(se.signal.aborted&&!a?.aborted){let n=Error(`write_plan timed out during ${b} for ${g}.`,{cause:e});throw d.recordError(o.writePlanTimedOut,n,{...t,"plan.timeout_ms":ke}),n}throw d.recordError(o.writePlanFailed,e,{...t,"plan.cancelled":a?.aborted??!1}),e}finally{clearTimeout(_)}}});async function bt(e,t){if(d.recordEvent(o.planReviewCompleted,{"plan.outcome":e===P?`exited`:`continued`}),e===P){let e=await Q(()=>_t(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 E=!1,{content:[{type:`text`,text:`The user chose to continue planning. Remain read-only and refine the plan.`}],details:{exited:!1}}}t.registerTool({name:x,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 "${P}" without the user explicitly choosing "${Ae}".`],parameters:{type:`object`,properties:{decision:{type:`string`,enum:[P,Me]}},additionalProperties:!1},async execute(e,t,n,r,i){if(!h)return{content:[{type:`text`,text:`Plan mode is already disabled.`}],details:{exited:!1}};if(!E)return{content:[{type:`text`,text:`Present the complete plan in chat and save it with write_plan before requesting approval.`}],details:{exited:!1}};if(!i.hasUI)return d.recordEvent(o.planReviewCompleted,{"plan.outcome":`no_ui`}),{content:[{type:`text`,text:`The plan is complete, but this run cannot ask the user interactively. Remain in plan mode until the user uses SPC p e.`}],details:{exited:!1}};let a=G(t)?t.decision:void 0,s=a===P||a===Me;if(k){if(!s)throw Error(`The narrated plan review is waiting for an explicit exit or continue decision.`);return k=!1,bt(a,i)}if(a!==void 0)throw Error(`Call complete_plan without a decision before submitting a narrated plan-review answer.`);let c=I?.handoff(Ne);return c?(k=!0,pe(c)):bt(await i.ui.select(`Plan complete. What would you like to do?`,[Ae,je])===Ae?P:Me,i)}}),q&&fe(t,{source:A,handlers:{"plan.normal":()=>{let e=V;return e?Q(()=>$(e,`normal`,R,!1)):void 0},"plan.exit":()=>{let e=V;return e?Q(async()=>{await _t(e,R)}):void 0},"plan.debug":()=>{let e=V;return e?Q(()=>$(e,`debug`,R,!1)):void 0},"plan.fable":()=>{let e=V;return e?Q(()=>$(e,`fable`,R,!1)):void 0}},onError:(e,t)=>{V?.hasUI&&V.ui.notify(`Leader action ${t} failed. Plan mode remains read-only.`,j),d.recordError(o.modeEnabled,e,{[M]:R,"plan.action":t})}}),t.on(`tool_call`,async(e,t)=>{if(!h)return;let n=_===`debug`&&(e.toolName===D||We.has(e.toolName))||_===`fable`&&e.toolName===O;if(!F.has(e.toolName)&&!n)return{block:!0,reason:`Plan mode blocks the ${e.toolName} tool. Use SPC p e to restore access.`};let r=ot(y?.subagents,t.model);if(e.toolName===ye){r&&ct(e.input,r);return}if(e.toolName!==ve)return;let i=e.input;if(lt(i.action))return{block:!0,reason:`Plan mode blocks subagent management action '${i.action}'. Use SPC p e first.`};at(i),r&&st(i,r)}),t.on(`before_agent_start`,async(t,n)=>{let i=[];if(h){let t=e().root??n.cwd,a=r(y?.plansDirectory,t,ne.homedir());i.push(`[PLAN MODE ACTIVE]\nYou are in repository read-only plan mode. The dedicated write_plan tool may write one unique Markdown plan file under ${a}.\n\nExplore the codebase and produce a concrete implementation plan. For every plan, first call subagent with action "agents", cluster the exploration by independent domain, subsystem, or integration boundary, and create a provisional task graph with the task tool. Select specialized agents by matching their names and descriptions to each cluster. Assign every unblocked delegated task through the task tool, and use a one-shot inlineAgent with a focused systemPrompt when no discovered specialist fits. Treat the initial graph as provisional, not as a fixed contract. After findings arrive, review the entire graph at least once and perform one to three review passes in total. In each pass, use the evidence to add, rewrite, delete, cancel, reassign, or change blockedBy relationships for tasks when warranted. Do not keep following tasks that new information has made stale. Stop revising early when the graph is stable, or after the third pass.\n\nEvery plan must end with a delegated planning-draft stage blocked by all exploration and decision tasks. A single-boundary plan gets one planning draft. A complex plan spanning multiple subsystems, domains, packages, apps, or integration boundaries gets two planning drafts concurrently. Use three concurrent drafts instead when the work is cross-layer, migration-sensitive, security-sensitive, or similarly high risk. Assign each draft through the task tool to the discovered "planner" agent with context "fork" so it receives the conversation and gathered evidence. If the planner agent is unavailable, assign the same draft task through the task tool with a focused inlineAgent. All children receive read, Bash, grep, find, ls, and configured MCP tools with artifacts disabled. Bash and MCP tools are for read-only inspection and must not modify files, external systems, or repository state. Doom Team runs asynchronously, so do not poll. After launch, continue non-overlapping exploration or end 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.`,u(_,a,L,z))}if(w&&i.push(`[CURRENT PLAN]\nSource: ${w.path}\n\n${w.content}`),i.length!==0)return{systemPrompt:`${t.systemPrompt}\n\n${i.join(`
4
+
5
+ `)}`}}),t.on(`context`,async e=>({messages:e.messages.filter(e=>e.customType!==`agent-harness-plan-mode-context`)})),t.on(`session_start`,async(e,n)=>{let r=n.sessionManager.getEntries(),i=r.flatMap(e=>e.type===`custom`&&e.customType===he?[Ze(e.data)]:[]).filter(e=>e!==void 0).at(-1),a=n.sessionManager.getSessionId(),o=Ve(a);I?.dispose(),I=q?me({bus:t.events,source:A,sessionId:a}):void 0;let s=r.flatMap(e=>e.type===`custom`&&e.customType===ge?[Qe(e.data)]:[]).filter(e=>e!==void 0).at(-1),c=v;if(C?.dispose(),C=void 0,w=s?.sessionId===o?s:void 0,T=i?.planId??w?.planId,E=!1,k=!1,y=void 0,L=void 0,h=!1,_=void 0,et(),v=i?.originalSnapshot,B=i?.interruptedFableStage,z=B?`interrupted`:`idle`,V=n,i?.activeFlavor)await $(n,i.activeFlavor,`session_restore`,!0);else{let e=i?.originalSnapshot??c;e?await mt(n,e)&&t.setActiveTools(it(e.tools,t.getActiveTools(),t.getAllTools().map(e=>e.name))):t.setActiveTools(t.getActiveTools().filter(e=>e!==x&&e!==S&&e!==D&&e!==O)),v=void 0,Y(n)}}),t.on(`session_shutdown`,async(e,n)=>{await gt(`Plan host session is shutting down.`),h&&v&&await mt(n,v)&&t.setActiveTools(it(v.tools,t.getActiveTools(),t.getAllTools().map(e=>e.name))),C?.dispose(),C=void 0,I?.dispose(),I=void 0,k=!1,$e.dispose();for(let e of vt)e.dispose();V=void 0,await d.shutdown()})}export{ke as WRITE_PLAN_TIMEOUT_MS,st as configurePlanningSubagentInput,ct as configurePlanningTaskInput,at as constrainSubagentInput,U as createPlanIdentifier,lt as isBlockedSubagentManagementAction,dt as loadPlanningModeConfig,nt as parseDebugEvidencePacket,Ze as parsePersistedPlanState,pt as planModeExtension,X as planModeTools,Ve as planSessionIdentifier,Be as planTitleSlug,ot as planningSubagentModel,He as visiblePlanForToolCall};
6
+ //# sourceMappingURL=planMode.mjs.map