@agimon-ai/doompi-plan 0.0.1-alpha.76 → 0.0.1-alpha.77

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.
@@ -0,0 +1,73 @@
1
+ Object.defineProperties(exports, {
2
+ __esModule: { value: true },
3
+ [Symbol.toStringTag]: { value: "Module" }
4
+ });
5
+ let _agimon_ai_doompi_core_mcp_facet = require("@agimon-ai/doompi-core/mcp-facet");
6
+ let _agimon_ai_doompi_core_server_facet = require("@agimon-ai/doompi-core/server-facet");
7
+ //#region src/extensions/workspaces/sessions/(backend)/skill/doompi-use-plan.mcp.ts
8
+ var doompi_use_plan_mcp_default = (0, _agimon_ai_doompi_core_mcp_facet.defineMcpSkill)({
9
+ name: "doompi-use-plan",
10
+ description: "Use Doom Pi Plan to draft reviewable normal, debug, or Fable-assisted plans, persist them, and exit safely.",
11
+ read: () => (0, _agimon_ai_doompi_core_server_facet.readPackageResource)(require("url").pathToFileURL(__filename).href, "src/prompts/doompi-use-plan/SKILL.md")
12
+ });
13
+ //#endregion
14
+ //#region src/services/mcpTools/index.ts
15
+ const PLAN_MCP_TOOLS_SERVICE = "doom/plan-mcp-tools";
16
+ const MODE_ID = "plan";
17
+ const MODE_LABEL = "Plan";
18
+ function bindMcpTool(context, name) {
19
+ const catalog = context.services.get(PLAN_MCP_TOOLS_SERVICE);
20
+ const tool = catalog?.get(name);
21
+ if (!tool || !catalog) throw new Error(`Plan tool is unavailable: ${name}`);
22
+ return {
23
+ ...tool,
24
+ execute(toolCallId, parameters, signal, onUpdate) {
25
+ if (!(context.selection.read().state?.["minor-mode"] ?? []).includes(MODE_ID)) {
26
+ const inactiveTools = [...catalog.names()].sort().join(", ");
27
+ throw new Error(`${MODE_LABEL} minor mode is inactive. Inactive ${MODE_LABEL} tools: ${inactiveTools}.`);
28
+ }
29
+ const executionSignal = AbortSignal.any([context.signal, signal ?? context.signal]);
30
+ return tool.execute(toolCallId, parameters, executionSignal, onUpdate, context.execution);
31
+ }
32
+ };
33
+ }
34
+ //#endregion
35
+ //#region src/extensions/workspaces/sessions/(backend)/tool/complete_plan.mcp.ts
36
+ var complete_plan_mcp_default = (0, _agimon_ai_doompi_core_mcp_facet.defineMcpTool)((context) => bindMcpTool(context, "complete_plan"));
37
+ //#endregion
38
+ //#region src/extensions/workspaces/sessions/(backend)/tool/record_debug_evidence.mcp.ts
39
+ var record_debug_evidence_mcp_default = (0, _agimon_ai_doompi_core_mcp_facet.defineMcpTool)((context) => bindMcpTool(context, "record_debug_evidence"));
40
+ //#endregion
41
+ //#region src/extensions/workspaces/sessions/(backend)/tool/run_fable_plan.mcp.ts
42
+ var run_fable_plan_mcp_default = (0, _agimon_ai_doompi_core_mcp_facet.defineMcpTool)((context) => bindMcpTool(context, "run_fable_plan"));
43
+ //#endregion
44
+ //#region src/extensions/workspaces/sessions/(backend)/tool/write_plan.mcp.ts
45
+ var write_plan_mcp_default = (0, _agimon_ai_doompi_core_mcp_facet.defineMcpTool)((context) => bindMcpTool(context, "write_plan"));
46
+ //#endregion
47
+ //#region generated/mcp.ts
48
+ const at = (value, context) => typeof value === "function" ? value(context) : value;
49
+ const via = (identity, value) => ({
50
+ ...identity,
51
+ ...value
52
+ });
53
+ const mcp = (0, _agimon_ai_doompi_core_mcp_facet.defineMcpPlugin)({
54
+ name: "@agimon-ai/doompi-plan",
55
+ session: (context) => ({
56
+ get skills() {
57
+ return [at(doompi_use_plan_mcp_default, context)];
58
+ },
59
+ get tools() {
60
+ return [
61
+ via({ name: "complete_plan" }, at(complete_plan_mcp_default, context)),
62
+ via({ name: "record_debug_evidence" }, at(record_debug_evidence_mcp_default, context)),
63
+ via({ name: "run_fable_plan" }, at(run_fable_plan_mcp_default, context)),
64
+ via({ name: "write_plan" }, at(write_plan_mcp_default, context))
65
+ ];
66
+ }
67
+ })
68
+ });
69
+ //#endregion
70
+ exports.default = mcp;
71
+ exports.mcp = mcp;
72
+
73
+ //# sourceMappingURL=mcp.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp.cjs","names":["defineMcpSkill","readPackageResource","defineMcpTool","defineMcpTool","defineMcpTool","defineMcpTool","defineMcpPlugin","skillDoompiUsePlan","toolCompletePlan","toolRecordDebugEvidence","toolRunFablePlan","toolWritePlan"],"sources":["../../src/extensions/workspaces/sessions/(backend)/skill/doompi-use-plan.mcp.ts","../../src/services/mcpTools/index.ts","../../src/extensions/workspaces/sessions/(backend)/tool/complete_plan.mcp.ts","../../src/extensions/workspaces/sessions/(backend)/tool/record_debug_evidence.mcp.ts","../../src/extensions/workspaces/sessions/(backend)/tool/run_fable_plan.mcp.ts","../../src/extensions/workspaces/sessions/(backend)/tool/write_plan.mcp.ts","../../generated/mcp.ts"],"sourcesContent":["import { defineMcpSkill } from '@agimon-ai/doompi-core/mcp-facet';\nimport { readPackageResource } from '@agimon-ai/doompi-core/server-facet';\n\nexport default defineMcpSkill({\n name: 'doompi-use-plan',\n description:\n 'Use Doom Pi Plan to draft reviewable normal, debug, or Fable-assisted plans, persist them, and exit safely.',\n read: () => readPackageResource(import.meta.url, 'src/prompts/doompi-use-plan/SKILL.md'),\n});\n","import type { DoomHeadlessTool } from '@agimon-ai/doompi-core/headless';\nimport type { DoomMcpPluginContext } from '@agimon-ai/doompi-core/mcp-facet';\nimport type { Context } from '@deepseek-ai/cordis';\n\nexport const PLAN_MCP_TOOLS_SERVICE = 'doom/plan-mcp-tools';\n\nconst MODE_ID = 'plan';\nconst MODE_LABEL = 'Plan';\n\nexport interface McpToolCatalog {\n get(name: string): DoomHeadlessTool | undefined;\n names(): readonly string[];\n}\n\nexport function mountMcpTools(tools: readonly DoomHeadlessTool[]) {\n return (context: Context): void => {\n context.plugin((providerContext) => {\n providerContext.provide(PLAN_MCP_TOOLS_SERVICE, {\n get: (name) => tools.find((tool) => tool.name === name),\n names: () => tools.map((tool) => tool.name),\n } satisfies McpToolCatalog);\n });\n };\n}\n\nexport function bindMcpTool(context: DoomMcpPluginContext, name: string): DoomHeadlessTool {\n const catalog = context.services.get<McpToolCatalog>(PLAN_MCP_TOOLS_SERVICE);\n const tool = catalog?.get(name);\n if (!tool || !catalog) throw new Error(`Plan tool is unavailable: ${name}`);\n return {\n ...tool,\n execute(toolCallId, parameters, signal, onUpdate) {\n const activeModes = context.selection.read().state?.['minor-mode'] ?? [];\n if (!activeModes.includes(MODE_ID)) {\n const inactiveTools = [...catalog.names()].sort().join(', ');\n throw new Error(`${MODE_LABEL} minor mode is inactive. Inactive ${MODE_LABEL} tools: ${inactiveTools}.`);\n }\n const executionSignal = AbortSignal.any([context.signal, signal ?? context.signal]);\n return tool.execute(toolCallId, parameters, executionSignal, onUpdate, context.execution);\n },\n };\n}\n","import { defineMcpTool, type DoomMcpPluginContext } from '@agimon-ai/doompi-core/mcp-facet';\n\nimport { bindMcpTool } from '../../../../../services/mcpTools';\n\nexport default defineMcpTool((context: DoomMcpPluginContext) => bindMcpTool(context, 'complete_plan'));\n","import { defineMcpTool, type DoomMcpPluginContext } from '@agimon-ai/doompi-core/mcp-facet';\n\nimport { bindMcpTool } from '../../../../../services/mcpTools';\n\nexport default defineMcpTool((context: DoomMcpPluginContext) => bindMcpTool(context, 'record_debug_evidence'));\n","import { defineMcpTool, type DoomMcpPluginContext } from '@agimon-ai/doompi-core/mcp-facet';\n\nimport { bindMcpTool } from '../../../../../services/mcpTools';\n\nexport default defineMcpTool((context: DoomMcpPluginContext) => bindMcpTool(context, 'run_fable_plan'));\n","import { defineMcpTool, type DoomMcpPluginContext } from '@agimon-ai/doompi-core/mcp-facet';\n\nimport { bindMcpTool } from '../../../../../services/mcpTools';\n\nexport default defineMcpTool((context: DoomMcpPluginContext) => bindMcpTool(context, 'write_plan'));\n","// Generated by @agimon-ai/doompi-build. Do not edit by hand.\nimport { defineMcpPlugin, type DoomMcpSessionPlugin } from '@agimon-ai/doompi-core/mcp-facet';\n\nimport skillDoompiUsePlan from '../src/extensions/workspaces/sessions/(backend)/skill/doompi-use-plan.mcp';\nimport toolCompletePlan from '../src/extensions/workspaces/sessions/(backend)/tool/complete_plan.mcp';\nimport toolRecordDebugEvidence from '../src/extensions/workspaces/sessions/(backend)/tool/record_debug_evidence.mcp';\nimport toolRunFablePlan from '../src/extensions/workspaces/sessions/(backend)/tool/run_fable_plan.mcp';\nimport toolWritePlan from '../src/extensions/workspaces/sessions/(backend)/tool/write_plan.mcp';\n\ntype Factory<T, C> = (context: C) => T;\nconst at = <T, C>(value: T | Factory<T, C>, context: C): T =>\n typeof value === 'function' ? (value as Factory<T, C>)(context) : value;\nconst via = <T>(identity: Record<string, unknown>, value: unknown): T => ({ ...identity, ...(value as object) }) as T;\n\nexport const mcp = defineMcpPlugin({\n name: '@agimon-ai/doompi-plan',\n session: (context) => ({\n get skills(): DoomMcpSessionPlugin['skills'] { return [at(skillDoompiUsePlan, context)]; },\n get tools(): DoomMcpSessionPlugin['tools'] { return [via({ name: 'complete_plan' }, at(toolCompletePlan, context)), via({ name: 'record_debug_evidence' }, at(toolRecordDebugEvidence, context)), via({ name: 'run_fable_plan' }, at(toolRunFablePlan, context)), via({ name: 'write_plan' }, at(toolWritePlan, context))]; },\n }) satisfies DoomMcpSessionPlugin,\n});\n\nexport default mcp;\n"],"mappings":";;;;;;;AAGA,IAAA,+BAAA,GAAeA,iCAAAA,eAAAA,CAAe;CAC5B,MAAM;CACN,aACE;CACF,aAAA,GAAYC,oCAAAA,oBAAAA,CAAAA,QAAAA,KAAAA,CAAAA,CAAAA,cAAAA,UAAAA,CAAAA,CAAAA,MAAqC,sCAAsC;AACzF,CAAC;;;ACJD,MAAa,yBAAyB;AAEtC,MAAM,UAAU;AAChB,MAAM,aAAa;AAkBnB,SAAgB,YAAY,SAA+B,MAAgC;CACzF,MAAM,UAAU,QAAQ,SAAS,IAAoB,sBAAsB;CAC3E,MAAM,OAAO,SAAS,IAAI,IAAI;CAC9B,IAAI,CAAC,QAAQ,CAAC,SAAS,MAAM,IAAI,MAAM,6BAA6B,MAAM;CAC1E,OAAO;EACL,GAAG;EACH,QAAQ,YAAY,YAAY,QAAQ,UAAU;GAEhD,IAAI,EADgB,QAAQ,UAAU,KAAK,CAAC,CAAC,QAAQ,iBAAiB,CAAC,EAAA,CACtD,SAAS,OAAO,GAAG;IAClC,MAAM,gBAAgB,CAAC,GAAG,QAAQ,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,IAAI;IAC3D,MAAM,IAAI,MAAM,GAAG,WAAW,oCAAoC,WAAW,UAAU,cAAc,EAAE;GACzG;GACA,MAAM,kBAAkB,YAAY,IAAI,CAAC,QAAQ,QAAQ,UAAU,QAAQ,MAAM,CAAC;GAClF,OAAO,KAAK,QAAQ,YAAY,YAAY,iBAAiB,UAAU,QAAQ,SAAS;EAC1F;CACF;AACF;;;ACrCA,IAAA,6BAAA,GAAeC,iCAAAA,cAAAA,EAAe,YAAkC,YAAY,SAAS,eAAe,CAAC;;;ACArG,IAAA,qCAAA,GAAeC,iCAAAA,cAAAA,EAAe,YAAkC,YAAY,SAAS,uBAAuB,CAAC;;;ACA7G,IAAA,8BAAA,GAAeC,iCAAAA,cAAAA,EAAe,YAAkC,YAAY,SAAS,gBAAgB,CAAC;;;ACAtG,IAAA,0BAAA,GAAeC,iCAAAA,cAAAA,EAAe,YAAkC,YAAY,SAAS,YAAY,CAAC;;;ACMlG,MAAM,MAAY,OAA0B,YAC1C,OAAO,UAAU,aAAc,MAAwB,OAAO,IAAI;AACpE,MAAM,OAAU,UAAmC,WAAuB;CAAE,GAAG;CAAU,GAAI;AAAiB;AAE9G,MAAa,OAAA,GAAMC,iCAAAA,gBAAAA,CAAgB;CACjC,MAAM;CACN,UAAU,aAAa;EACrB,IAAI,SAAyC;GAAE,OAAO,CAAC,GAAGC,6BAAoB,OAAO,CAAC;EAAG;EACzF,IAAI,QAAuC;GAAE,OAAO;IAAC,IAAI,EAAE,MAAM,gBAAgB,GAAG,GAAGC,2BAAkB,OAAO,CAAC;IAAG,IAAI,EAAE,MAAM,wBAAwB,GAAG,GAAGC,mCAAyB,OAAO,CAAC;IAAG,IAAI,EAAE,MAAM,iBAAiB,GAAG,GAAGC,4BAAkB,OAAO,CAAC;IAAG,IAAI,EAAE,MAAM,aAAa,GAAG,GAAGC,wBAAe,OAAO,CAAC;GAAC;EAAG;CAC9T;AACF,CAAC"}
@@ -0,0 +1,5 @@
1
+ //#region generated/mcp.d.ts
2
+ export declare const mcp: import("@agimon-ai/doompi-core/mcp-facet").DoomMcpPluginDefinition;
3
+ //#endregion
4
+ export { mcp as default };
5
+ //# sourceMappingURL=mcp.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp.d.cts","names":[],"sources":["../../generated/mcp.ts"],"mappings":";qBAca,gDAAG"}
@@ -0,0 +1,5 @@
1
+ //#region generated/mcp.d.ts
2
+ export declare const mcp: import("@agimon-ai/doompi-core/mcp-facet").DoomMcpPluginDefinition;
3
+ //#endregion
4
+ export { mcp as default };
5
+ //# sourceMappingURL=mcp.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp.d.mts","names":[],"sources":["../../generated/mcp.ts"],"mappings":";qBAca,gDAAG"}
@@ -0,0 +1,68 @@
1
+ import { defineMcpPlugin, defineMcpSkill, defineMcpTool } from "@agimon-ai/doompi-core/mcp-facet";
2
+ import { readPackageResource } from "@agimon-ai/doompi-core/server-facet";
3
+ //#region src/extensions/workspaces/sessions/(backend)/skill/doompi-use-plan.mcp.ts
4
+ var doompi_use_plan_mcp_default = defineMcpSkill({
5
+ name: "doompi-use-plan",
6
+ description: "Use Doom Pi Plan to draft reviewable normal, debug, or Fable-assisted plans, persist them, and exit safely.",
7
+ read: () => readPackageResource(import.meta.url, "src/prompts/doompi-use-plan/SKILL.md")
8
+ });
9
+ //#endregion
10
+ //#region src/services/mcpTools/index.ts
11
+ const PLAN_MCP_TOOLS_SERVICE = "doom/plan-mcp-tools";
12
+ const MODE_ID = "plan";
13
+ const MODE_LABEL = "Plan";
14
+ function bindMcpTool(context, name) {
15
+ const catalog = context.services.get(PLAN_MCP_TOOLS_SERVICE);
16
+ const tool = catalog?.get(name);
17
+ if (!tool || !catalog) throw new Error(`Plan tool is unavailable: ${name}`);
18
+ return {
19
+ ...tool,
20
+ execute(toolCallId, parameters, signal, onUpdate) {
21
+ if (!(context.selection.read().state?.["minor-mode"] ?? []).includes(MODE_ID)) {
22
+ const inactiveTools = [...catalog.names()].sort().join(", ");
23
+ throw new Error(`${MODE_LABEL} minor mode is inactive. Inactive ${MODE_LABEL} tools: ${inactiveTools}.`);
24
+ }
25
+ const executionSignal = AbortSignal.any([context.signal, signal ?? context.signal]);
26
+ return tool.execute(toolCallId, parameters, executionSignal, onUpdate, context.execution);
27
+ }
28
+ };
29
+ }
30
+ //#endregion
31
+ //#region src/extensions/workspaces/sessions/(backend)/tool/complete_plan.mcp.ts
32
+ var complete_plan_mcp_default = defineMcpTool((context) => bindMcpTool(context, "complete_plan"));
33
+ //#endregion
34
+ //#region src/extensions/workspaces/sessions/(backend)/tool/record_debug_evidence.mcp.ts
35
+ var record_debug_evidence_mcp_default = defineMcpTool((context) => bindMcpTool(context, "record_debug_evidence"));
36
+ //#endregion
37
+ //#region src/extensions/workspaces/sessions/(backend)/tool/run_fable_plan.mcp.ts
38
+ var run_fable_plan_mcp_default = defineMcpTool((context) => bindMcpTool(context, "run_fable_plan"));
39
+ //#endregion
40
+ //#region src/extensions/workspaces/sessions/(backend)/tool/write_plan.mcp.ts
41
+ var write_plan_mcp_default = defineMcpTool((context) => bindMcpTool(context, "write_plan"));
42
+ //#endregion
43
+ //#region generated/mcp.ts
44
+ const at = (value, context) => typeof value === "function" ? value(context) : value;
45
+ const via = (identity, value) => ({
46
+ ...identity,
47
+ ...value
48
+ });
49
+ const mcp = defineMcpPlugin({
50
+ name: "@agimon-ai/doompi-plan",
51
+ session: (context) => ({
52
+ get skills() {
53
+ return [at(doompi_use_plan_mcp_default, context)];
54
+ },
55
+ get tools() {
56
+ return [
57
+ via({ name: "complete_plan" }, at(complete_plan_mcp_default, context)),
58
+ via({ name: "record_debug_evidence" }, at(record_debug_evidence_mcp_default, context)),
59
+ via({ name: "run_fable_plan" }, at(run_fable_plan_mcp_default, context)),
60
+ via({ name: "write_plan" }, at(write_plan_mcp_default, context))
61
+ ];
62
+ }
63
+ })
64
+ });
65
+ //#endregion
66
+ export { mcp as default, mcp };
67
+
68
+ //# sourceMappingURL=mcp.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp.mjs","names":["skillDoompiUsePlan","toolCompletePlan","toolRecordDebugEvidence","toolRunFablePlan","toolWritePlan"],"sources":["../../src/extensions/workspaces/sessions/(backend)/skill/doompi-use-plan.mcp.ts","../../src/services/mcpTools/index.ts","../../src/extensions/workspaces/sessions/(backend)/tool/complete_plan.mcp.ts","../../src/extensions/workspaces/sessions/(backend)/tool/record_debug_evidence.mcp.ts","../../src/extensions/workspaces/sessions/(backend)/tool/run_fable_plan.mcp.ts","../../src/extensions/workspaces/sessions/(backend)/tool/write_plan.mcp.ts","../../generated/mcp.ts"],"sourcesContent":["import { defineMcpSkill } from '@agimon-ai/doompi-core/mcp-facet';\nimport { readPackageResource } from '@agimon-ai/doompi-core/server-facet';\n\nexport default defineMcpSkill({\n name: 'doompi-use-plan',\n description:\n 'Use Doom Pi Plan to draft reviewable normal, debug, or Fable-assisted plans, persist them, and exit safely.',\n read: () => readPackageResource(import.meta.url, 'src/prompts/doompi-use-plan/SKILL.md'),\n});\n","import type { DoomHeadlessTool } from '@agimon-ai/doompi-core/headless';\nimport type { DoomMcpPluginContext } from '@agimon-ai/doompi-core/mcp-facet';\nimport type { Context } from '@deepseek-ai/cordis';\n\nexport const PLAN_MCP_TOOLS_SERVICE = 'doom/plan-mcp-tools';\n\nconst MODE_ID = 'plan';\nconst MODE_LABEL = 'Plan';\n\nexport interface McpToolCatalog {\n get(name: string): DoomHeadlessTool | undefined;\n names(): readonly string[];\n}\n\nexport function mountMcpTools(tools: readonly DoomHeadlessTool[]) {\n return (context: Context): void => {\n context.plugin((providerContext) => {\n providerContext.provide(PLAN_MCP_TOOLS_SERVICE, {\n get: (name) => tools.find((tool) => tool.name === name),\n names: () => tools.map((tool) => tool.name),\n } satisfies McpToolCatalog);\n });\n };\n}\n\nexport function bindMcpTool(context: DoomMcpPluginContext, name: string): DoomHeadlessTool {\n const catalog = context.services.get<McpToolCatalog>(PLAN_MCP_TOOLS_SERVICE);\n const tool = catalog?.get(name);\n if (!tool || !catalog) throw new Error(`Plan tool is unavailable: ${name}`);\n return {\n ...tool,\n execute(toolCallId, parameters, signal, onUpdate) {\n const activeModes = context.selection.read().state?.['minor-mode'] ?? [];\n if (!activeModes.includes(MODE_ID)) {\n const inactiveTools = [...catalog.names()].sort().join(', ');\n throw new Error(`${MODE_LABEL} minor mode is inactive. Inactive ${MODE_LABEL} tools: ${inactiveTools}.`);\n }\n const executionSignal = AbortSignal.any([context.signal, signal ?? context.signal]);\n return tool.execute(toolCallId, parameters, executionSignal, onUpdate, context.execution);\n },\n };\n}\n","import { defineMcpTool, type DoomMcpPluginContext } from '@agimon-ai/doompi-core/mcp-facet';\n\nimport { bindMcpTool } from '../../../../../services/mcpTools';\n\nexport default defineMcpTool((context: DoomMcpPluginContext) => bindMcpTool(context, 'complete_plan'));\n","import { defineMcpTool, type DoomMcpPluginContext } from '@agimon-ai/doompi-core/mcp-facet';\n\nimport { bindMcpTool } from '../../../../../services/mcpTools';\n\nexport default defineMcpTool((context: DoomMcpPluginContext) => bindMcpTool(context, 'record_debug_evidence'));\n","import { defineMcpTool, type DoomMcpPluginContext } from '@agimon-ai/doompi-core/mcp-facet';\n\nimport { bindMcpTool } from '../../../../../services/mcpTools';\n\nexport default defineMcpTool((context: DoomMcpPluginContext) => bindMcpTool(context, 'run_fable_plan'));\n","import { defineMcpTool, type DoomMcpPluginContext } from '@agimon-ai/doompi-core/mcp-facet';\n\nimport { bindMcpTool } from '../../../../../services/mcpTools';\n\nexport default defineMcpTool((context: DoomMcpPluginContext) => bindMcpTool(context, 'write_plan'));\n","// Generated by @agimon-ai/doompi-build. Do not edit by hand.\nimport { defineMcpPlugin, type DoomMcpSessionPlugin } from '@agimon-ai/doompi-core/mcp-facet';\n\nimport skillDoompiUsePlan from '../src/extensions/workspaces/sessions/(backend)/skill/doompi-use-plan.mcp';\nimport toolCompletePlan from '../src/extensions/workspaces/sessions/(backend)/tool/complete_plan.mcp';\nimport toolRecordDebugEvidence from '../src/extensions/workspaces/sessions/(backend)/tool/record_debug_evidence.mcp';\nimport toolRunFablePlan from '../src/extensions/workspaces/sessions/(backend)/tool/run_fable_plan.mcp';\nimport toolWritePlan from '../src/extensions/workspaces/sessions/(backend)/tool/write_plan.mcp';\n\ntype Factory<T, C> = (context: C) => T;\nconst at = <T, C>(value: T | Factory<T, C>, context: C): T =>\n typeof value === 'function' ? (value as Factory<T, C>)(context) : value;\nconst via = <T>(identity: Record<string, unknown>, value: unknown): T => ({ ...identity, ...(value as object) }) as T;\n\nexport const mcp = defineMcpPlugin({\n name: '@agimon-ai/doompi-plan',\n session: (context) => ({\n get skills(): DoomMcpSessionPlugin['skills'] { return [at(skillDoompiUsePlan, context)]; },\n get tools(): DoomMcpSessionPlugin['tools'] { return [via({ name: 'complete_plan' }, at(toolCompletePlan, context)), via({ name: 'record_debug_evidence' }, at(toolRecordDebugEvidence, context)), via({ name: 'run_fable_plan' }, at(toolRunFablePlan, context)), via({ name: 'write_plan' }, at(toolWritePlan, context))]; },\n }) satisfies DoomMcpSessionPlugin,\n});\n\nexport default mcp;\n"],"mappings":";;;AAGA,IAAA,8BAAe,eAAe;CAC5B,MAAM;CACN,aACE;CACF,YAAY,oBAAoB,YAAY,KAAK,sCAAsC;AACzF,CAAC;;;ACJD,MAAa,yBAAyB;AAEtC,MAAM,UAAU;AAChB,MAAM,aAAa;AAkBnB,SAAgB,YAAY,SAA+B,MAAgC;CACzF,MAAM,UAAU,QAAQ,SAAS,IAAoB,sBAAsB;CAC3E,MAAM,OAAO,SAAS,IAAI,IAAI;CAC9B,IAAI,CAAC,QAAQ,CAAC,SAAS,MAAM,IAAI,MAAM,6BAA6B,MAAM;CAC1E,OAAO;EACL,GAAG;EACH,QAAQ,YAAY,YAAY,QAAQ,UAAU;GAEhD,IAAI,EADgB,QAAQ,UAAU,KAAK,CAAC,CAAC,QAAQ,iBAAiB,CAAC,EAAA,CACtD,SAAS,OAAO,GAAG;IAClC,MAAM,gBAAgB,CAAC,GAAG,QAAQ,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,IAAI;IAC3D,MAAM,IAAI,MAAM,GAAG,WAAW,oCAAoC,WAAW,UAAU,cAAc,EAAE;GACzG;GACA,MAAM,kBAAkB,YAAY,IAAI,CAAC,QAAQ,QAAQ,UAAU,QAAQ,MAAM,CAAC;GAClF,OAAO,KAAK,QAAQ,YAAY,YAAY,iBAAiB,UAAU,QAAQ,SAAS;EAC1F;CACF;AACF;;;ACrCA,IAAA,4BAAe,eAAe,YAAkC,YAAY,SAAS,eAAe,CAAC;;;ACArG,IAAA,oCAAe,eAAe,YAAkC,YAAY,SAAS,uBAAuB,CAAC;;;ACA7G,IAAA,6BAAe,eAAe,YAAkC,YAAY,SAAS,gBAAgB,CAAC;;;ACAtG,IAAA,yBAAe,eAAe,YAAkC,YAAY,SAAS,YAAY,CAAC;;;ACMlG,MAAM,MAAY,OAA0B,YAC1C,OAAO,UAAU,aAAc,MAAwB,OAAO,IAAI;AACpE,MAAM,OAAU,UAAmC,WAAuB;CAAE,GAAG;CAAU,GAAI;AAAiB;AAE9G,MAAa,MAAM,gBAAgB;CACjC,MAAM;CACN,UAAU,aAAa;EACrB,IAAI,SAAyC;GAAE,OAAO,CAAC,GAAGA,6BAAoB,OAAO,CAAC;EAAG;EACzF,IAAI,QAAuC;GAAE,OAAO;IAAC,IAAI,EAAE,MAAM,gBAAgB,GAAG,GAAGC,2BAAkB,OAAO,CAAC;IAAG,IAAI,EAAE,MAAM,wBAAwB,GAAG,GAAGC,mCAAyB,OAAO,CAAC;IAAG,IAAI,EAAE,MAAM,iBAAiB,GAAG,GAAGC,4BAAkB,OAAO,CAAC;IAAG,IAAI,EAAE,MAAM,aAAa,GAAG,GAAGC,wBAAe,OAAO,CAAC;GAAC;EAAG;CAC9T;AACF,CAAC"}
@@ -1,14 +1,15 @@
1
- const require_index = require("../../../../services/planApi/index.cjs");
2
- const require_index$1 = require("../../../../services/planServerSession/index.cjs");
1
+ const require_index = require("../../../../services/mcpTools/index.cjs");
2
+ const require_index$1 = require("../../../../services/planApi/index.cjs");
3
+ const require_index$2 = require("../../../../services/planServerSession/index.cjs");
3
4
  //#region src/extensions/workspaces/sessions/(backend)/root.server.ts
4
5
  var root_server_default = (0, require("@agimon-ai/doompi-core/extension-file").defineRoot)(({ agent }) => {
5
6
  const value = {
6
- api: [require_index.api],
7
- ...agent ? require_index$1.createPlanServerSession(agent) : {}
7
+ api: [require_index$1.api],
8
+ ...agent ? require_index$2.createPlanServerSession(agent) : {}
8
9
  };
9
10
  return {
10
11
  value,
11
- services: value.services
12
+ services: [...value.services ?? [], ...value.tools?.length ? [require_index.mountMcpTools(value.tools)] : []]
12
13
  };
13
14
  });
14
15
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"root.server.cjs","names":["defineRoot","api","createPlanServerSession"],"sources":["../../../../../../src/extensions/workspaces/sessions/(backend)/root.server.ts"],"sourcesContent":["import { defineRoot, type RootDeclaration } from '@agimon-ai/doompi-core/extension-file';\nimport type { DoomServerPluginContext, DoomServerSessionPlugin } from '@agimon-ai/doompi-core/server-facet';\n\nimport { api } from '../../../../services/planApi';\nimport { createPlanServerSession } from '../../../../services/planServerSession';\n\nexport default defineRoot(\n ({ agent }: DoomServerPluginContext): RootDeclaration<DoomServerSessionPlugin, DoomServerPluginContext> => {\n const value = {\n api: [api],\n ...(agent ? createPlanServerSession(agent) : {}),\n };\n return { value, services: value.services };\n },\n);\n"],"mappings":";;;AAMA,IAAA,uBAAA,kDAAeA,CAAAA,CAAAA,WAAAA,EACZ,EAAE,YAAwG;CACzG,MAAM,QAAQ;EACZ,KAAK,CAACC,cAAAA,GAAG;EACT,GAAI,QAAQC,gBAAAA,wBAAwB,KAAK,IAAI,CAAC;CAChD;CACA,OAAO;EAAE;EAAO,UAAU,MAAM;CAAS;AAC3C,CACF"}
1
+ {"version":3,"file":"root.server.cjs","names":["defineRoot","api","createPlanServerSession","mountMcpTools"],"sources":["../../../../../../src/extensions/workspaces/sessions/(backend)/root.server.ts"],"sourcesContent":["import { defineRoot, type RootDeclaration } from '@agimon-ai/doompi-core/extension-file';\nimport type { DoomServerPluginContext, DoomServerSessionPlugin } from '@agimon-ai/doompi-core/server-facet';\n\nimport { mountMcpTools } from '../../../../services/mcpTools';\nimport { api } from '../../../../services/planApi';\nimport { createPlanServerSession } from '../../../../services/planServerSession';\n\nexport default defineRoot(\n ({ agent }: DoomServerPluginContext): RootDeclaration<DoomServerSessionPlugin, DoomServerPluginContext> => {\n const value = {\n api: [api],\n ...(agent ? createPlanServerSession(agent) : {}),\n };\n return {\n value,\n services: [...(value.services ?? []), ...(value.tools?.length ? [mountMcpTools(value.tools)] : [])],\n };\n },\n);\n"],"mappings":";;;;AAOA,IAAA,uBAAA,kDAAeA,CAAAA,CAAAA,WAAAA,EACZ,EAAE,YAAwG;CACzG,MAAM,QAAQ;EACZ,KAAK,CAACC,gBAAAA,GAAG;EACT,GAAI,QAAQC,gBAAAA,wBAAwB,KAAK,IAAI,CAAC;CAChD;CACA,OAAO;EACL;EACA,UAAU,CAAC,GAAI,MAAM,YAAY,CAAC,GAAI,GAAI,MAAM,OAAO,SAAS,CAACC,cAAAA,cAAc,MAAM,KAAK,CAAC,IAAI,CAAC,CAAE;CACpG;AACF,CACF"}
@@ -1,3 +1,4 @@
1
+ import { mountMcpTools } from "../../../../services/mcpTools/index.mjs";
1
2
  import { api } from "../../../../services/planApi/index.mjs";
2
3
  import { createPlanServerSession } from "../../../../services/planServerSession/index.mjs";
3
4
  import { defineRoot } from "@agimon-ai/doompi-core/extension-file";
@@ -9,7 +10,7 @@ var root_server_default = defineRoot(({ agent }) => {
9
10
  };
10
11
  return {
11
12
  value,
12
- services: value.services
13
+ services: [...value.services ?? [], ...value.tools?.length ? [mountMcpTools(value.tools)] : []]
13
14
  };
14
15
  });
15
16
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"root.server.mjs","names":[],"sources":["../../../../../../src/extensions/workspaces/sessions/(backend)/root.server.ts"],"sourcesContent":["import { defineRoot, type RootDeclaration } from '@agimon-ai/doompi-core/extension-file';\nimport type { DoomServerPluginContext, DoomServerSessionPlugin } from '@agimon-ai/doompi-core/server-facet';\n\nimport { api } from '../../../../services/planApi';\nimport { createPlanServerSession } from '../../../../services/planServerSession';\n\nexport default defineRoot(\n ({ agent }: DoomServerPluginContext): RootDeclaration<DoomServerSessionPlugin, DoomServerPluginContext> => {\n const value = {\n api: [api],\n ...(agent ? createPlanServerSession(agent) : {}),\n };\n return { value, services: value.services };\n },\n);\n"],"mappings":";;;;AAMA,IAAA,sBAAe,YACZ,EAAE,YAAwG;CACzG,MAAM,QAAQ;EACZ,KAAK,CAAC,GAAG;EACT,GAAI,QAAQ,wBAAwB,KAAK,IAAI,CAAC;CAChD;CACA,OAAO;EAAE;EAAO,UAAU,MAAM;CAAS;AAC3C,CACF"}
1
+ {"version":3,"file":"root.server.mjs","names":[],"sources":["../../../../../../src/extensions/workspaces/sessions/(backend)/root.server.ts"],"sourcesContent":["import { defineRoot, type RootDeclaration } from '@agimon-ai/doompi-core/extension-file';\nimport type { DoomServerPluginContext, DoomServerSessionPlugin } from '@agimon-ai/doompi-core/server-facet';\n\nimport { mountMcpTools } from '../../../../services/mcpTools';\nimport { api } from '../../../../services/planApi';\nimport { createPlanServerSession } from '../../../../services/planServerSession';\n\nexport default defineRoot(\n ({ agent }: DoomServerPluginContext): RootDeclaration<DoomServerSessionPlugin, DoomServerPluginContext> => {\n const value = {\n api: [api],\n ...(agent ? createPlanServerSession(agent) : {}),\n };\n return {\n value,\n services: [...(value.services ?? []), ...(value.tools?.length ? [mountMcpTools(value.tools)] : [])],\n };\n },\n);\n"],"mappings":";;;;;AAOA,IAAA,sBAAe,YACZ,EAAE,YAAwG;CACzG,MAAM,QAAQ;EACZ,KAAK,CAAC,GAAG;EACT,GAAI,QAAQ,wBAAwB,KAAK,IAAI,CAAC;CAChD;CACA,OAAO;EACL;EACA,UAAU,CAAC,GAAI,MAAM,YAAY,CAAC,GAAI,GAAI,MAAM,OAAO,SAAS,CAAC,cAAc,MAAM,KAAK,CAAC,IAAI,CAAC,CAAE;CACpG;AACF,CACF"}
@@ -0,0 +1,17 @@
1
+ //#region src/services/mcpTools/index.ts
2
+ const PLAN_MCP_TOOLS_SERVICE = "doom/plan-mcp-tools";
3
+ function mountMcpTools(tools) {
4
+ return (context) => {
5
+ context.plugin((providerContext) => {
6
+ providerContext.provide(PLAN_MCP_TOOLS_SERVICE, {
7
+ get: (name) => tools.find((tool) => tool.name === name),
8
+ names: () => tools.map((tool) => tool.name)
9
+ });
10
+ });
11
+ };
12
+ }
13
+ //#endregion
14
+ exports.PLAN_MCP_TOOLS_SERVICE = PLAN_MCP_TOOLS_SERVICE;
15
+ exports.mountMcpTools = mountMcpTools;
16
+
17
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","names":[],"sources":["../../../../src/services/mcpTools/index.ts"],"sourcesContent":["import type { DoomHeadlessTool } from '@agimon-ai/doompi-core/headless';\nimport type { DoomMcpPluginContext } from '@agimon-ai/doompi-core/mcp-facet';\nimport type { Context } from '@deepseek-ai/cordis';\n\nexport const PLAN_MCP_TOOLS_SERVICE = 'doom/plan-mcp-tools';\n\nconst MODE_ID = 'plan';\nconst MODE_LABEL = 'Plan';\n\nexport interface McpToolCatalog {\n get(name: string): DoomHeadlessTool | undefined;\n names(): readonly string[];\n}\n\nexport function mountMcpTools(tools: readonly DoomHeadlessTool[]) {\n return (context: Context): void => {\n context.plugin((providerContext) => {\n providerContext.provide(PLAN_MCP_TOOLS_SERVICE, {\n get: (name) => tools.find((tool) => tool.name === name),\n names: () => tools.map((tool) => tool.name),\n } satisfies McpToolCatalog);\n });\n };\n}\n\nexport function bindMcpTool(context: DoomMcpPluginContext, name: string): DoomHeadlessTool {\n const catalog = context.services.get<McpToolCatalog>(PLAN_MCP_TOOLS_SERVICE);\n const tool = catalog?.get(name);\n if (!tool || !catalog) throw new Error(`Plan tool is unavailable: ${name}`);\n return {\n ...tool,\n execute(toolCallId, parameters, signal, onUpdate) {\n const activeModes = context.selection.read().state?.['minor-mode'] ?? [];\n if (!activeModes.includes(MODE_ID)) {\n const inactiveTools = [...catalog.names()].sort().join(', ');\n throw new Error(`${MODE_LABEL} minor mode is inactive. Inactive ${MODE_LABEL} tools: ${inactiveTools}.`);\n }\n const executionSignal = AbortSignal.any([context.signal, signal ?? context.signal]);\n return tool.execute(toolCallId, parameters, executionSignal, onUpdate, context.execution);\n },\n };\n}\n"],"mappings":";AAIA,MAAa,yBAAyB;AAUtC,SAAgB,cAAc,OAAoC;CAChE,QAAQ,YAA2B;EACjC,QAAQ,QAAQ,oBAAoB;GAClC,gBAAgB,QAAQ,wBAAwB;IAC9C,MAAM,SAAS,MAAM,MAAM,SAAS,KAAK,SAAS,IAAI;IACtD,aAAa,MAAM,KAAK,SAAS,KAAK,IAAI;GAC5C,CAA0B;EAC5B,CAAC;CACH;AACF"}
@@ -0,0 +1,16 @@
1
+ //#region src/services/mcpTools/index.ts
2
+ const PLAN_MCP_TOOLS_SERVICE = "doom/plan-mcp-tools";
3
+ function mountMcpTools(tools) {
4
+ return (context) => {
5
+ context.plugin((providerContext) => {
6
+ providerContext.provide(PLAN_MCP_TOOLS_SERVICE, {
7
+ get: (name) => tools.find((tool) => tool.name === name),
8
+ names: () => tools.map((tool) => tool.name)
9
+ });
10
+ });
11
+ };
12
+ }
13
+ //#endregion
14
+ export { PLAN_MCP_TOOLS_SERVICE, mountMcpTools };
15
+
16
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../../../src/services/mcpTools/index.ts"],"sourcesContent":["import type { DoomHeadlessTool } from '@agimon-ai/doompi-core/headless';\nimport type { DoomMcpPluginContext } from '@agimon-ai/doompi-core/mcp-facet';\nimport type { Context } from '@deepseek-ai/cordis';\n\nexport const PLAN_MCP_TOOLS_SERVICE = 'doom/plan-mcp-tools';\n\nconst MODE_ID = 'plan';\nconst MODE_LABEL = 'Plan';\n\nexport interface McpToolCatalog {\n get(name: string): DoomHeadlessTool | undefined;\n names(): readonly string[];\n}\n\nexport function mountMcpTools(tools: readonly DoomHeadlessTool[]) {\n return (context: Context): void => {\n context.plugin((providerContext) => {\n providerContext.provide(PLAN_MCP_TOOLS_SERVICE, {\n get: (name) => tools.find((tool) => tool.name === name),\n names: () => tools.map((tool) => tool.name),\n } satisfies McpToolCatalog);\n });\n };\n}\n\nexport function bindMcpTool(context: DoomMcpPluginContext, name: string): DoomHeadlessTool {\n const catalog = context.services.get<McpToolCatalog>(PLAN_MCP_TOOLS_SERVICE);\n const tool = catalog?.get(name);\n if (!tool || !catalog) throw new Error(`Plan tool is unavailable: ${name}`);\n return {\n ...tool,\n execute(toolCallId, parameters, signal, onUpdate) {\n const activeModes = context.selection.read().state?.['minor-mode'] ?? [];\n if (!activeModes.includes(MODE_ID)) {\n const inactiveTools = [...catalog.names()].sort().join(', ');\n throw new Error(`${MODE_LABEL} minor mode is inactive. Inactive ${MODE_LABEL} tools: ${inactiveTools}.`);\n }\n const executionSignal = AbortSignal.any([context.signal, signal ?? context.signal]);\n return tool.execute(toolCallId, parameters, executionSignal, onUpdate, context.execution);\n },\n };\n}\n"],"mappings":";AAIA,MAAa,yBAAyB;AAUtC,SAAgB,cAAc,OAAoC;CAChE,QAAQ,YAA2B;EACjC,QAAQ,QAAQ,oBAAoB;GAClC,gBAAgB,QAAQ,wBAAwB;IAC9C,MAAM,SAAS,MAAM,MAAM,SAAS,KAAK,SAAS,IAAI;IACtD,aAAa,MAAM,KAAK,SAAS,KAAK,IAAI;GAC5C,CAA0B;EAC5B,CAAC;CACH;AACF"}
@@ -1,9 +1,10 @@
1
1
  const require_runtime = require("../../../_virtual/_rolldown/runtime.cjs");
2
2
  const require_config = require("../../schemas/plan/config.cjs");
3
- const require_index = require("../planPointer/index.cjs");
4
- const require_index$1 = require("../prompts/index.cjs");
3
+ const require_index = require("../optionalTeamServices/index.cjs");
4
+ const require_index$1 = require("../planPointer/index.cjs");
5
+ const require_index$2 = require("../prompts/index.cjs");
5
6
  const require_planApi = require("../../types/planApi.cjs");
6
- const require_index$2 = require("../planMode/index.cjs");
7
+ const require_index$3 = require("../planMode/index.cjs");
7
8
  let node_os = require("node:os");
8
9
  node_os = require_runtime.__toESM(node_os, 1);
9
10
  let node_path = require("node:path");
@@ -64,6 +65,45 @@ const PLAN_MODE_ALLOWED_TOOLS = [
64
65
  "use_voice_tools",
65
66
  WRITE_PLAN_TOOL
66
67
  ];
68
+ const PLAN_SUBAGENT_POLICY = {
69
+ owner: "@agimon-ai/doompi-plan",
70
+ allowedTools: [
71
+ "read",
72
+ "bash",
73
+ "grep",
74
+ "find",
75
+ "ls",
76
+ "mcp"
77
+ ],
78
+ requiredTools: ["bash"],
79
+ allowMcpTools: true,
80
+ allowedExternalProfiles: [],
81
+ denyExtensions: false
82
+ };
83
+ function serverPlanSubagentPolicy(host) {
84
+ return (context) => {
85
+ context.inject([require_index.DOOM_SUBAGENT_POLICY_SERVICE], (child) => {
86
+ const service = require_index.readDoomSubagentPolicyService(child);
87
+ if (!service) return void 0;
88
+ let handle;
89
+ const sync = () => {
90
+ if (modeSelected(host)) {
91
+ if (handle) handle.update(PLAN_SUBAGENT_POLICY);
92
+ else handle = service.register(PLAN_SUBAGENT_POLICY);
93
+ } else {
94
+ handle?.dispose();
95
+ handle = void 0;
96
+ }
97
+ };
98
+ sync();
99
+ child.effect(() => host.subscribeSelection(sync));
100
+ return () => handle?.dispose();
101
+ });
102
+ };
103
+ }
104
+ function modeSelected(host) {
105
+ return (host.context.selection.state?.["minor-mode"] ?? []).includes(PLAN_MODE_ID);
106
+ }
67
107
  function output(value, isError = false) {
68
108
  return {
69
109
  content: [{
@@ -78,10 +118,10 @@ function createPlanServerSession(host) {
78
118
  let flavor = "normal";
79
119
  let debugEvidence;
80
120
  let modeOwner;
81
- const modeSelected = () => (host.context.selection.state?.["minor-mode"] ?? []).includes(PLAN_MODE_ID);
121
+ const selected = () => modeSelected(host);
82
122
  const homeDirectory = () => host.context.environment.HOME ?? node_os.default.homedir();
83
123
  const plansDirectory = () => (0, _agimon_ai_doompi_config.resolvePlanningPlansDirectory)(require_config.loadDoomConfig(host.context.repoRoot, homeDirectory()).modes?.planning?.plansDirectory, host.context.repoRoot, homeDirectory());
84
- const pointers = new require_index.PlanPointerService({ env: host.context.environment });
124
+ const pointers = new require_index$1.PlanPointerService({ env: host.context.environment });
85
125
  /**
86
126
  * The activity group's line, and the pointer its panel reads.
87
127
  *
@@ -97,10 +137,10 @@ function createPlanServerSession(host) {
97
137
  writtenAt
98
138
  });
99
139
  } catch {}
100
- host.context.client.setStatus(require_planApi.PLAN_STATUS_KEY, require_planApi.formatPlanStatus(title, require_index$2.planStampOf(writtenAt)));
140
+ host.context.client.setStatus(require_planApi.PLAN_STATUS_KEY, require_planApi.formatPlanStatus(title, require_index$3.planStampOf(writtenAt)));
101
141
  };
102
142
  const modeState = () => {
103
- const active = modeSelected();
143
+ const active = selected();
104
144
  return {
105
145
  activation: active ? "active" : "inactive",
106
146
  condition: "ready",
@@ -141,7 +181,7 @@ function createPlanServerSession(host) {
141
181
  await session.appendCustomEntry(PLAN_MODEL_SNAPSHOT, null);
142
182
  };
143
183
  const selectPlan = async (enabled, nextFlavor = flavor) => {
144
- const wasEnabled = modeSelected();
184
+ const wasEnabled = selected();
145
185
  const session = host.context.session;
146
186
  if (enabled && !wasEnabled) {
147
187
  const config = require_config.loadDoomConfig(host.context.repoRoot, homeDirectory()).modes?.planning?.main;
@@ -196,7 +236,7 @@ function createPlanServerSession(host) {
196
236
  const evidence = await latestEntry(PLAN_DEBUG_EVIDENCE);
197
237
  if (evidence === void 0 || evidence === null) return;
198
238
  try {
199
- debugEvidence = require_index$2.parseDebugEvidencePacket(evidence);
239
+ debugEvidence = require_index$3.parseDebugEvidencePacket(evidence);
200
240
  } catch {
201
241
  debugEvidence = void 0;
202
242
  }
@@ -258,7 +298,7 @@ function createPlanServerSession(host) {
258
298
  }
259
299
  }).createOwner(void 0);
260
300
  return {
261
- services: [(0, _agimon_ai_doompi_minor_mode.serverMinorModes)([modeOwner])],
301
+ services: [(0, _agimon_ai_doompi_minor_mode.serverMinorModes)([modeOwner]), serverPlanSubagentPolicy(host)],
262
302
  toolRestrictions: [{
263
303
  when: { state: { "minor-mode": PLAN_MODE_ID } },
264
304
  allowedTools: PLAN_MODE_ALLOWED_TOOLS
@@ -337,7 +377,7 @@ function createPlanServerSession(host) {
337
377
  executionMode: "serial",
338
378
  async execute(_toolCallId, parameters) {
339
379
  try {
340
- const parsedEvidence = require_index$2.parseDebugEvidencePacket(parameters);
380
+ const parsedEvidence = require_index$3.parseDebugEvidencePacket(parameters);
341
381
  await host.context.session.appendCustomEntry(PLAN_DEBUG_EVIDENCE, parsedEvidence);
342
382
  debugEvidence = parsedEvidence;
343
383
  return output({ recorded: true });
@@ -397,7 +437,7 @@ function createPlanServerSession(host) {
397
437
  async execute(toolCallId, _parameters, signal) {
398
438
  try {
399
439
  signal?.throwIfAborted();
400
- let content = require_index$2.visiblePlanForToolCall(await host.context.session.entries(), toolCallId);
440
+ let content = require_index$3.visiblePlanForToolCall(await host.context.session.entries(), toolCallId);
401
441
  if (content === void 0) {
402
442
  const requested = await host.context.client.request({
403
443
  kind: "input",
@@ -412,9 +452,9 @@ function createPlanServerSession(host) {
412
452
  recursive: true,
413
453
  mode: 448
414
454
  });
415
- const title = require_index$2.planTitleOf(content);
455
+ const title = require_index$3.planTitleOf(content);
416
456
  const writtenAt = (/* @__PURE__ */ new Date()).toISOString();
417
- const filename = `${require_index$2.planTitleSlug(content)}-${Date.now()}.md`;
457
+ const filename = `${require_index$3.planTitleSlug(content)}-${Date.now()}.md`;
418
458
  const filePath = node_path.default.join(directory, filename);
419
459
  await (0, node_fs_promises.writeFile)(filePath, `${content.trim()}\n`, {
420
460
  encoding: "utf8",
@@ -467,7 +507,7 @@ function createPlanServerSession(host) {
467
507
  return {
468
508
  content: [{
469
509
  type: "text",
470
- text: exited ? require_index$2.PLAN_EXIT_APPROVED_TEXT : require_index$2.PLAN_CONTINUE_TEXT
510
+ text: exited ? require_index$3.PLAN_EXIT_APPROVED_TEXT : require_index$3.PLAN_CONTINUE_TEXT
471
511
  }],
472
512
  details: { exited }
473
513
  };
@@ -489,7 +529,7 @@ function createPlanServerSession(host) {
489
529
  async handle(event) {
490
530
  const prompt = typeof event.systemPrompt === "string" ? event.systemPrompt : "";
491
531
  const directory = plansDirectory();
492
- const sections = [require_index$1.buildPlanModeBasePrompt(directory, PLAN_SERVER_CAPABILITIES), require_index$1.buildFlavorPlanningPrompt(flavor, directory, debugEvidence, PLAN_SERVER_FABLE_STAGE, PLAN_SERVER_CAPABILITIES)];
532
+ const sections = [require_index$2.buildPlanModeBasePrompt(directory, PLAN_SERVER_CAPABILITIES), require_index$2.buildFlavorPlanningPrompt(flavor, directory, debugEvidence, PLAN_SERVER_FABLE_STAGE, PLAN_SERVER_CAPABILITIES)];
493
533
  const saved = await latestEntry(PLAN_DOCUMENT);
494
534
  if (saved !== void 0 && saved !== null && typeof saved === "object" && "path" in saved) {
495
535
  const document = saved;
@@ -508,8 +548,8 @@ function createPlanServerSession(host) {
508
548
  async handle() {
509
549
  await restorePlanState();
510
550
  const pointer = pointers.read(host.context.sessionId);
511
- if (pointer) host.context.client.setStatus(require_planApi.PLAN_STATUS_KEY, require_planApi.formatPlanStatus(pointer.title, require_index$2.planStampOf(pointer.writtenAt)));
512
- if (!modeSelected()) await restoreModel();
551
+ if (pointer) host.context.client.setStatus(require_planApi.PLAN_STATUS_KEY, require_planApi.formatPlanStatus(pointer.title, require_index$3.planStampOf(pointer.writtenAt)));
552
+ if (!selected()) await restoreModel();
513
553
  }
514
554
  }]
515
555
  };
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["os","resolvePlanningPlansDirectory","loadDoomConfig","PlanPointerService","PLAN_STATUS_KEY","formatPlanStatus","planStampOf","parseDebugEvidencePacket","defineMinorMode","serverMinorModes","readPackageResource","visiblePlanForToolCall","mkdir","planTitleOf","planTitleSlug","path","writeFile","PLAN_REVIEW_TITLE","PLAN_REVIEW_CHOICES","EXIT_PLAN_DECISION","CONTINUE_PLAN_DECISION","PLAN_EXIT_APPROVED_TEXT","PLAN_CONTINUE_TEXT","buildPlanModeBasePrompt","buildFlavorPlanningPrompt","readFile"],"sources":["../../../../src/services/planServerSession/index.ts"],"sourcesContent":["import { readFile } from 'node:fs/promises';\nimport { mkdir, writeFile } from 'node:fs/promises';\nimport os from 'node:os';\nimport path from 'node:path';\n\nimport { type DoomHeadlessHostService, type DoomHeadlessModelSettings } from '@agimon-ai/doompi-core/headless';\nimport { type DoomHeadlessToolResult } from '@agimon-ai/doompi-core/headless';\nimport { readPackageResource, type DoomServerSessionPlugin } from '@agimon-ai/doompi-core/server-facet';\nimport { serverMinorModes } from '@agimon-ai/doompi-minor-mode';\nimport { defineMinorMode, type MinorModeOwner, type MinorModeState } from '@agimon-ai/doompi-minor-mode';\n\nimport { loadDoomConfig, resolvePlanningPlansDirectory } from '../../schemas/plan/config';\nimport {\n buildFlavorPlanningPrompt,\n buildPlanModeBasePrompt,\n type DebugEvidencePacket,\n type PlanHostCapabilities,\n type PlanningFlavor,\n} from '../../services/prompts';\nimport {\n CONTINUE_PLAN_DECISION,\n EXIT_PLAN_DECISION,\n PLAN_REVIEW_CHOICES,\n PLAN_REVIEW_TITLE,\n PLAN_STATUS_KEY,\n formatPlanStatus,\n} from '../../types/planApi';\nimport {\n parseDebugEvidencePacket,\n PLAN_CONTINUE_TEXT,\n PLAN_EXIT_APPROVED_TEXT,\n planStampOf,\n planTitleOf,\n planTitleSlug,\n visiblePlanForToolCall,\n} from '../planMode';\nimport { PlanPointerService } from '../planPointer';\n\nconst RECORD_DEBUG_EVIDENCE_TOOL = 'record_debug_evidence';\nconst RUN_FABLE_PLAN_TOOL = 'run_fable_plan';\nconst WRITE_PLAN_TOOL = 'write_plan';\nconst COMPLETE_PLAN_TOOL = 'complete_plan';\nconst PLAN_MODE_ID = 'plan';\nconst PLAN_MODEL_SNAPSHOT = 'plan-server-model-snapshot';\nconst PLAN_FLAVOR_SNAPSHOT = 'plan-server-flavor';\nconst PLAN_DEBUG_EVIDENCE = 'plan-debug-evidence';\nconst PLAN_DOCUMENT = 'plan-document';\n/**\n * Fable needs a broker this host does not have, so `run_fable_plan` is a stub here. The stage is\n * reported honestly rather than as an idle run that is about to start.\n */\nconst PLAN_SERVER_FABLE_STAGE = 'unavailable';\n/**\n * What this facet's tools accept: complete_plan declares no parameters, there is no narrated\n * review to answer, and run_fable_plan is a stub because no broker seam is configured.\n */\nexport const PLAN_SERVER_CAPABILITIES: PlanHostCapabilities = {\n completePlanTakesDecision: false,\n narratesReview: false,\n fableAvailable: false,\n};\nconst PLAN_MODE_ALLOWED_TOOLS = [\n 'add_directory',\n 'ask_user_question',\n 'bash',\n COMPLETE_PLAN_TOOL,\n 'describe_author_tools',\n 'describe_voice_tools',\n 'find',\n 'grep',\n 'intercom',\n 'ls',\n 'minor_mode',\n 'mcp',\n 'narrate',\n 'open_authoring_file',\n 'read',\n RECORD_DEBUG_EVIDENCE_TOOL,\n RUN_FABLE_PLAN_TOOL,\n 'search_external_files',\n 'subagent',\n 'subagent_supervisor',\n 'subagent_wait',\n 'task',\n 'transfer_voice',\n 'use_author_tools',\n 'use_voice_tools',\n WRITE_PLAN_TOOL,\n] as const;\nfunction output(value: unknown, isError = false): DoomHeadlessToolResult {\n return {\n content: [{ type: 'text', text: typeof value === 'string' ? value : JSON.stringify(value, null, 2) }],\n details: value,\n ...(isError ? { isError: true } : {}),\n };\n}\n\nexport function createPlanServerSession(\n host: DoomHeadlessHostService,\n): Omit<DoomServerSessionPlugin, 'tools'> & { tools: Parameters<DoomHeadlessHostService['registerTool']>[0][] } {\n let flavor: PlanningFlavor = 'normal';\n let debugEvidence: DebugEvidencePacket | undefined;\n let modeOwner: MinorModeOwner | undefined;\n const modeSelected = (): boolean => (host.context.selection.state?.['minor-mode'] ?? []).includes(PLAN_MODE_ID);\n const homeDirectory = (): string => host.context.environment.HOME ?? os.homedir();\n // One resolution for the prompt and for write_plan. They named different directories before,\n // so the prompt could advertise a path the tool would not write to.\n const plansDirectory = (): string =>\n resolvePlanningPlansDirectory(\n loadDoomConfig(host.context.repoRoot, homeDirectory()).modes?.planning?.plansDirectory,\n host.context.repoRoot,\n homeDirectory(),\n );\n const pointers = new PlanPointerService({ env: host.context.environment });\n /**\n * The activity group's line, and the pointer its panel reads.\n *\n * A cockpit drives this facet and never the Pi runtime, so both were written\n * only on the Pi side: the group never appeared, and when it did its panel\n * had no pointer to answer from.\n */\n const announcePlanDocument = (filePath: string, title: string, writtenAt: string): void => {\n try {\n pointers.write(host.context.sessionId, { path: filePath, title, writtenAt });\n } catch {\n // The plan is on disk either way. Losing the cockpit's view of it is not\n // a reason to fail the write that produced it.\n }\n host.context.client.setStatus(PLAN_STATUS_KEY, formatPlanStatus(title, planStampOf(writtenAt)));\n };\n const modeState = (): MinorModeState => {\n const active = modeSelected();\n return {\n activation: active ? 'active' : 'inactive',\n condition: 'ready',\n ...(active ? { detail: `${flavor} - read only`, modelContextVariant: flavor } : {}),\n actions: [\n {\n id: 'activate',\n enabled: true,\n },\n {\n id: 'deactivate',\n enabled: active,\n ...(active ? {} : { disabledReason: 'Plan mode is inactive.' }),\n },\n ],\n };\n };\n const publishMode = (): void => modeOwner?.publish();\n const restoreModel = async (): Promise<void> => {\n const session = host.context.session;\n const entries = await session.entries({ type: 'custom', customType: PLAN_MODEL_SNAPSHOT, limit: 1 });\n const data = entries[0]?.data;\n if (data === undefined || data === null) return;\n if (\n typeof data !== 'object' ||\n !('thinkingLevel' in data) ||\n !['off', 'minimal', 'low', 'medium', 'high', 'xhigh', 'max'].includes(String(data.thinkingLevel))\n )\n throw new Error('The saved planning model settings are invalid.');\n if (!session.setModelSettings) throw new Error('This session cannot restore its model settings.');\n await session.setModelSettings(data as DoomHeadlessModelSettings);\n await session.appendCustomEntry(PLAN_MODEL_SNAPSHOT, null);\n };\n const selectPlan = async (enabled: boolean, nextFlavor = flavor): Promise<void> => {\n const wasEnabled = modeSelected();\n const session = host.context.session;\n if (enabled && !wasEnabled) {\n const config = loadDoomConfig(host.context.repoRoot, homeDirectory()).modes?.planning?.main;\n if (config?.model || config?.thinking) {\n if (!session.readModelSettings || !session.setModelSettings)\n throw new Error('This session cannot apply planning model settings.');\n const previous = await session.readModelSettings();\n const settings: Partial<DoomHeadlessModelSettings> = {};\n if (config.model) {\n const separator = config.model.indexOf('/');\n const provider = separator > 0 ? config.model.slice(0, separator) : previous.model?.provider;\n if (!provider) throw new Error('The planning model needs a provider.');\n settings.model = { provider, id: separator > 0 ? config.model.slice(separator + 1) : config.model };\n }\n if (config.thinking) settings.thinkingLevel = config.thinking;\n await session.appendCustomEntry(PLAN_MODEL_SNAPSHOT, previous);\n try {\n await session.setModelSettings(settings);\n } catch (error) {\n await session.setModelSettings(previous);\n await session.appendCustomEntry(PLAN_MODEL_SNAPSHOT, null);\n throw error;\n }\n }\n } else if (!enabled && wasEnabled) {\n await restoreModel();\n }\n const modes = (host.context.selection.state?.['minor-mode'] ?? []).filter((mode) => mode !== PLAN_MODE_ID);\n await host.changeSelection({\n axis: 'state',\n key: 'minor-mode',\n values: enabled ? [...modes, PLAN_MODE_ID] : modes,\n });\n flavor = nextFlavor;\n // Persisted because the minor mode survives a server restart while this local does not, and\n // the prompt is built from it. Without this a restored debug session silently plans as normal.\n await session.appendCustomEntry(PLAN_FLAVOR_SNAPSHOT, enabled ? { flavor } : null);\n publishMode();\n };\n\n /** Newest value for a custom entry type, or undefined when none was recorded. */\n const latestEntry = async (customType: string): Promise<unknown> => {\n const entries = await host.context.session.entries({ type: 'custom', customType, limit: 1 });\n return entries[0]?.data ?? undefined;\n };\n\n const restorePlanState = async (): Promise<void> => {\n const saved = await latestEntry(PLAN_FLAVOR_SNAPSHOT);\n if (saved !== undefined && saved !== null && typeof saved === 'object' && 'flavor' in saved) {\n const candidate = String((saved as { flavor: unknown }).flavor);\n if (candidate === 'normal' || candidate === 'debug' || candidate === 'fable') flavor = candidate;\n }\n const evidence = await latestEntry(PLAN_DEBUG_EVIDENCE);\n if (evidence === undefined || evidence === null) return;\n try {\n debugEvidence = parseDebugEvidencePacket(evidence);\n } catch {\n // A packet written by an older shape must not stop the session from planning.\n debugEvidence = undefined;\n }\n };\n modeOwner = defineMinorMode({\n descriptor: {\n source: '@agimon-ai/doompi-plan',\n id: PLAN_MODE_ID,\n label: 'Plan',\n description: 'Read-only planning with normal, debug, and Fable flavors.',\n order: 40,\n actions: [\n {\n id: 'activate',\n label: 'Activate',\n description: 'Activate plan mode or switch its planning flavor.',\n contexts: ['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: 'deactivate',\n label: 'Deactivate',\n description: 'Exit plan mode and restore the previous agent configuration.',\n contexts: ['headless'],\n parameters: [],\n },\n ],\n },\n state: () => modeState(),\n async handleAction(_runtime, actionId, argumentsValue, { signal }) {\n signal.throwIfAborted();\n if (actionId === 'activate') {\n const nextFlavor = argumentsValue.flavor;\n if (nextFlavor !== 'normal' && nextFlavor !== 'debug' && nextFlavor !== 'fable')\n throw new Error('A valid plan flavor is required.');\n await selectPlan(true, nextFlavor);\n return { message: `Plan mode is active with the ${nextFlavor} flavor.` };\n }\n if (actionId === 'deactivate') {\n await selectPlan(false);\n return { message: 'Plan mode deactivated.' };\n }\n throw new Error(`Unknown plan mode action: ${actionId}`);\n },\n }).createOwner(undefined);\n return {\n services: [serverMinorModes([modeOwner])],\n toolRestrictions: [\n {\n when: { state: { 'minor-mode': PLAN_MODE_ID } },\n allowedTools: PLAN_MODE_ALLOWED_TOOLS,\n },\n ],\n resources: [\n {\n when: { state: { 'minor-mode': PLAN_MODE_ID }, attribution: { kind: 'minor', mode: PLAN_MODE_ID } },\n name: 'doompi-use-plan',\n kind: 'skill',\n read: () => readPackageResource(import.meta.url, 'src/prompts/doompi-use-plan/SKILL.md'),\n },\n ],\n tools: [\n {\n when: { state: { 'minor-mode': PLAN_MODE_ID }, attribution: { kind: 'minor', mode: PLAN_MODE_ID } },\n name: RECORD_DEBUG_EVIDENCE_TOOL,\n label: 'Record Debug Evidence',\n description: 'Record bounded debug evidence as optional planning context.',\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 executionMode: 'serial',\n async execute(_toolCallId, parameters) {\n try {\n const parsedEvidence = parseDebugEvidencePacket(parameters);\n await host.context.session.appendCustomEntry(PLAN_DEBUG_EVIDENCE, parsedEvidence);\n // Held in memory too: the debug prompt is rebuilt every turn from this packet.\n debugEvidence = parsedEvidence;\n return output({ recorded: true });\n } catch (error) {\n return output(error instanceof Error ? error.message : String(error), true);\n }\n },\n },\n {\n when: { state: { 'minor-mode': PLAN_MODE_ID }, attribution: { kind: 'minor', mode: PLAN_MODE_ID } },\n name: RUN_FABLE_PLAN_TOOL,\n label: 'Run Fable Plan',\n description: 'Run the configured local Fable planning broker with bounded evidence.',\n parameters: {\n type: 'object',\n properties: {\n goal: { type: 'array', items: { type: 'string' } },\n constraints: { type: 'array', items: { type: 'string' } },\n },\n required: ['goal'],\n additionalProperties: false,\n },\n executionMode: 'serial',\n async execute() {\n return output(\n 'Fable planning is unavailable in the headless host because no broker seam is configured.',\n true,\n );\n },\n },\n {\n when: { state: { 'minor-mode': PLAN_MODE_ID }, attribution: { kind: 'minor', mode: PLAN_MODE_ID } },\n name: WRITE_PLAN_TOOL,\n label: 'Write Plan',\n description: 'Save the implementation plan already presented in the session.',\n parameters: { type: 'object', properties: {}, additionalProperties: false },\n executionMode: 'serial',\n async execute(toolCallId, _parameters, signal) {\n try {\n signal?.throwIfAborted();\n // Same extraction the Pi path uses: the text this very tool call was\n // introduced by, not \"the last assistant message that looks like a heading\".\n let content = visiblePlanForToolCall(await host.context.session.entries(), toolCallId);\n if (content === undefined) {\n const requested = await host.context.client.request(\n {\n kind: 'input',\n title: 'Implementation plan',\n multiline: true,\n },\n signal,\n );\n content = typeof requested === 'string' ? requested : '';\n }\n if (!content.trim()) throw new Error('No implementation plan content was supplied.');\n const directory = plansDirectory();\n await mkdir(directory, { recursive: true, mode: 0o700 });\n const title = planTitleOf(content);\n const writtenAt = new Date().toISOString();\n const filename = `${planTitleSlug(content)}-${Date.now()}.md`;\n const filePath = path.join(directory, filename);\n await writeFile(filePath, `${content.trim()}\\n`, { encoding: 'utf8', mode: 0o600, flag: 'wx' });\n await host.context.session.appendCustomEntry(PLAN_DOCUMENT, {\n path: filePath,\n content: content.trim(),\n title,\n writtenAt,\n });\n announcePlanDocument(filePath, title, writtenAt);\n await selectPlan(true);\n return output({ path: filePath, written: true });\n } catch (error) {\n return output(error instanceof Error ? error.message : String(error), true);\n }\n },\n },\n {\n when: { state: { 'minor-mode': PLAN_MODE_ID }, attribution: { kind: 'minor', mode: PLAN_MODE_ID } },\n name: COMPLETE_PLAN_TOOL,\n label: 'Complete Plan',\n description: 'Ask the user for explicit exit-or-continue approval for the saved implementation plan.',\n // No decision parameter: this facet has no narrated review to answer, so a model-supplied\n // decision would be the agent approving its own plan instead of the reader approving it.\n parameters: { type: 'object', properties: {}, additionalProperties: false },\n executionMode: 'serial',\n async execute(_toolCallId, _parameters, signal) {\n try {\n // The client answers with the option's `value`, so the decision travels with the\n // question instead of the facet having to recognise the label it rendered.\n const decision = await host.context.client.request(\n {\n kind: 'select',\n title: PLAN_REVIEW_TITLE,\n options: PLAN_REVIEW_CHOICES.map((choice) => ({ ...choice })),\n },\n signal,\n );\n // A dismissed or aborted prompt answers with nothing, and that is not approval.\n const exited = decision === EXIT_PLAN_DECISION;\n await host.context.session.appendCustomEntry('plan-review', {\n decision: exited ? EXIT_PLAN_DECISION : CONTINUE_PLAN_DECISION,\n });\n await selectPlan(!exited);\n return {\n content: [{ type: 'text', text: exited ? PLAN_EXIT_APPROVED_TEXT : PLAN_CONTINUE_TEXT }],\n details: { exited },\n };\n } catch (error) {\n return output(error instanceof Error ? error.message : String(error), true);\n }\n },\n },\n ],\n hooks: [\n {\n when: { state: { 'minor-mode': PLAN_MODE_ID }, attribution: { kind: 'minor', mode: PLAN_MODE_ID } },\n event: 'before_agent_start',\n async handle(event) {\n const prompt = typeof event.systemPrompt === 'string' ? event.systemPrompt : '';\n const directory = plansDirectory();\n const sections = [\n buildPlanModeBasePrompt(directory, PLAN_SERVER_CAPABILITIES),\n buildFlavorPlanningPrompt(\n flavor,\n directory,\n debugEvidence,\n PLAN_SERVER_FABLE_STAGE,\n PLAN_SERVER_CAPABILITIES,\n ),\n ];\n const saved = await latestEntry(PLAN_DOCUMENT);\n if (saved !== undefined && saved !== null && typeof saved === 'object' && 'path' in saved) {\n const document = saved as { path: string; content?: string };\n // Read from disk rather than from what write_plan remembered: the plan is editable in\n // the cockpit, and a reader who corrected it expects that correction implemented.\n let content = document.content ?? '';\n try {\n content = await readFile(document.path, 'utf8');\n } catch {\n content = document.content ?? '';\n }\n if (content.trim()) sections.push(`[CURRENT PLAN]\\nSource: ${document.path}\\n\\n${content}`);\n }\n return { systemPrompt: `${prompt}\\n\\n${sections.join('\\n\\n')}`.trim() };\n },\n },\n {\n event: 'session_start',\n async handle() {\n await restorePlanState();\n // A plan written in an earlier session is still the session's plan, so\n // the dock has to hear about it again on every restart.\n const pointer = pointers.read(host.context.sessionId);\n if (pointer)\n host.context.client.setStatus(\n PLAN_STATUS_KEY,\n formatPlanStatus(pointer.title, planStampOf(pointer.writtenAt)),\n );\n // Restore a saved model override if Plan was not restored with the session.\n if (!modeSelected()) await restoreModel();\n },\n },\n ],\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","names":["DOOM_SUBAGENT_POLICY_SERVICE","readDoomSubagentPolicyService","os","resolvePlanningPlansDirectory","loadDoomConfig","PlanPointerService","PLAN_STATUS_KEY","formatPlanStatus","planStampOf","parseDebugEvidencePacket","defineMinorMode","serverMinorModes","readPackageResource","visiblePlanForToolCall","mkdir","planTitleOf","planTitleSlug","path","writeFile","PLAN_REVIEW_TITLE","PLAN_REVIEW_CHOICES","EXIT_PLAN_DECISION","CONTINUE_PLAN_DECISION","PLAN_EXIT_APPROVED_TEXT","PLAN_CONTINUE_TEXT","buildPlanModeBasePrompt","buildFlavorPlanningPrompt","readFile"],"sources":["../../../../src/services/planServerSession/index.ts"],"sourcesContent":["import { readFile } from 'node:fs/promises';\nimport { mkdir, writeFile } from 'node:fs/promises';\nimport os from 'node:os';\nimport path from 'node:path';\n\nimport { type DoomHeadlessHostService, type DoomHeadlessModelSettings } from '@agimon-ai/doompi-core/headless';\nimport { type DoomHeadlessToolResult } from '@agimon-ai/doompi-core/headless';\nimport { readPackageResource, type DoomServerSessionPlugin } from '@agimon-ai/doompi-core/server-facet';\nimport { serverMinorModes } from '@agimon-ai/doompi-minor-mode';\nimport { defineMinorMode, type MinorModeOwner, type MinorModeState } from '@agimon-ai/doompi-minor-mode';\nimport type { Context } from '@deepseek-ai/cordis';\n\nimport { loadDoomConfig, resolvePlanningPlansDirectory } from '../../schemas/plan/config';\nimport {\n buildFlavorPlanningPrompt,\n buildPlanModeBasePrompt,\n type DebugEvidencePacket,\n type PlanHostCapabilities,\n type PlanningFlavor,\n} from '../../services/prompts';\nimport {\n CONTINUE_PLAN_DECISION,\n EXIT_PLAN_DECISION,\n PLAN_REVIEW_CHOICES,\n PLAN_REVIEW_TITLE,\n PLAN_STATUS_KEY,\n formatPlanStatus,\n} from '../../types/planApi';\nimport {\n DOOM_SUBAGENT_POLICY_SERVICE,\n readDoomSubagentPolicyService,\n type SubagentPolicy,\n type SubagentPolicyHandle,\n} from '../optionalTeamServices';\nimport {\n parseDebugEvidencePacket,\n PLAN_CONTINUE_TEXT,\n PLAN_EXIT_APPROVED_TEXT,\n planStampOf,\n planTitleOf,\n planTitleSlug,\n visiblePlanForToolCall,\n} from '../planMode';\nimport { PlanPointerService } from '../planPointer';\n\nconst RECORD_DEBUG_EVIDENCE_TOOL = 'record_debug_evidence';\nconst RUN_FABLE_PLAN_TOOL = 'run_fable_plan';\nconst WRITE_PLAN_TOOL = 'write_plan';\nconst COMPLETE_PLAN_TOOL = 'complete_plan';\nconst PLAN_MODE_ID = 'plan';\nconst PLAN_MODEL_SNAPSHOT = 'plan-server-model-snapshot';\nconst PLAN_FLAVOR_SNAPSHOT = 'plan-server-flavor';\nconst PLAN_DEBUG_EVIDENCE = 'plan-debug-evidence';\nconst PLAN_DOCUMENT = 'plan-document';\n/**\n * Fable needs a broker this host does not have, so `run_fable_plan` is a stub here. The stage is\n * reported honestly rather than as an idle run that is about to start.\n */\nconst PLAN_SERVER_FABLE_STAGE = 'unavailable';\n/**\n * What this facet's tools accept: complete_plan declares no parameters, there is no narrated\n * review to answer, and run_fable_plan is a stub because no broker seam is configured.\n */\nexport const PLAN_SERVER_CAPABILITIES: PlanHostCapabilities = {\n completePlanTakesDecision: false,\n narratesReview: false,\n fableAvailable: false,\n};\nconst PLAN_MODE_ALLOWED_TOOLS = [\n 'add_directory',\n 'ask_user_question',\n 'bash',\n COMPLETE_PLAN_TOOL,\n 'describe_author_tools',\n 'describe_voice_tools',\n 'find',\n 'grep',\n 'intercom',\n 'ls',\n 'minor_mode',\n 'mcp',\n 'narrate',\n 'open_authoring_file',\n 'read',\n RECORD_DEBUG_EVIDENCE_TOOL,\n RUN_FABLE_PLAN_TOOL,\n 'search_external_files',\n 'subagent',\n 'subagent_supervisor',\n 'subagent_wait',\n 'task',\n 'transfer_voice',\n 'use_author_tools',\n 'use_voice_tools',\n WRITE_PLAN_TOOL,\n] as const;\nconst PLAN_SUBAGENT_POLICY_OWNER = '@agimon-ai/doompi-plan';\nconst PLAN_SUBAGENT_POLICY: SubagentPolicy = {\n owner: PLAN_SUBAGENT_POLICY_OWNER,\n allowedTools: ['read', 'bash', 'grep', 'find', 'ls', 'mcp'],\n requiredTools: ['bash'],\n allowMcpTools: true,\n allowedExternalProfiles: [],\n denyExtensions: false,\n};\n\nfunction serverPlanSubagentPolicy(host: DoomHeadlessHostService): (context: Context) => void {\n return (context) => {\n context.inject([DOOM_SUBAGENT_POLICY_SERVICE], (child) => {\n const service = readDoomSubagentPolicyService(child);\n if (!service) return undefined;\n let handle: SubagentPolicyHandle | undefined;\n const sync = (): void => {\n if (modeSelected(host)) {\n if (handle) handle.update(PLAN_SUBAGENT_POLICY);\n else handle = service.register(PLAN_SUBAGENT_POLICY);\n } else {\n handle?.dispose();\n handle = undefined;\n }\n };\n sync();\n child.effect(() => host.subscribeSelection(sync));\n return () => handle?.dispose();\n });\n };\n}\n\nfunction modeSelected(host: DoomHeadlessHostService): boolean {\n return (host.context.selection.state?.['minor-mode'] ?? []).includes(PLAN_MODE_ID);\n}\n\nfunction output(value: unknown, isError = false): DoomHeadlessToolResult {\n return {\n content: [{ type: 'text', text: typeof value === 'string' ? value : JSON.stringify(value, null, 2) }],\n details: value,\n ...(isError ? { isError: true } : {}),\n };\n}\n\nexport function createPlanServerSession(\n host: DoomHeadlessHostService,\n): Omit<DoomServerSessionPlugin, 'tools'> & { tools: Parameters<DoomHeadlessHostService['registerTool']>[0][] } {\n let flavor: PlanningFlavor = 'normal';\n let debugEvidence: DebugEvidencePacket | undefined;\n let modeOwner: MinorModeOwner | undefined;\n const selected = (): boolean => modeSelected(host);\n const homeDirectory = (): string => host.context.environment.HOME ?? os.homedir();\n // One resolution for the prompt and for write_plan. They named different directories before,\n // so the prompt could advertise a path the tool would not write to.\n const plansDirectory = (): string =>\n resolvePlanningPlansDirectory(\n loadDoomConfig(host.context.repoRoot, homeDirectory()).modes?.planning?.plansDirectory,\n host.context.repoRoot,\n homeDirectory(),\n );\n const pointers = new PlanPointerService({ env: host.context.environment });\n /**\n * The activity group's line, and the pointer its panel reads.\n *\n * A cockpit drives this facet and never the Pi runtime, so both were written\n * only on the Pi side: the group never appeared, and when it did its panel\n * had no pointer to answer from.\n */\n const announcePlanDocument = (filePath: string, title: string, writtenAt: string): void => {\n try {\n pointers.write(host.context.sessionId, { path: filePath, title, writtenAt });\n } catch {\n // The plan is on disk either way. Losing the cockpit's view of it is not\n // a reason to fail the write that produced it.\n }\n host.context.client.setStatus(PLAN_STATUS_KEY, formatPlanStatus(title, planStampOf(writtenAt)));\n };\n const modeState = (): MinorModeState => {\n const active = selected();\n return {\n activation: active ? 'active' : 'inactive',\n condition: 'ready',\n ...(active ? { detail: `${flavor} - read only`, modelContextVariant: flavor } : {}),\n actions: [\n {\n id: 'activate',\n enabled: true,\n },\n {\n id: 'deactivate',\n enabled: active,\n ...(active ? {} : { disabledReason: 'Plan mode is inactive.' }),\n },\n ],\n };\n };\n const publishMode = (): void => modeOwner?.publish();\n const restoreModel = async (): Promise<void> => {\n const session = host.context.session;\n const entries = await session.entries({ type: 'custom', customType: PLAN_MODEL_SNAPSHOT, limit: 1 });\n const data = entries[0]?.data;\n if (data === undefined || data === null) return;\n if (\n typeof data !== 'object' ||\n !('thinkingLevel' in data) ||\n !['off', 'minimal', 'low', 'medium', 'high', 'xhigh', 'max'].includes(String(data.thinkingLevel))\n )\n throw new Error('The saved planning model settings are invalid.');\n if (!session.setModelSettings) throw new Error('This session cannot restore its model settings.');\n await session.setModelSettings(data as DoomHeadlessModelSettings);\n await session.appendCustomEntry(PLAN_MODEL_SNAPSHOT, null);\n };\n const selectPlan = async (enabled: boolean, nextFlavor = flavor): Promise<void> => {\n const wasEnabled = selected();\n const session = host.context.session;\n if (enabled && !wasEnabled) {\n const config = loadDoomConfig(host.context.repoRoot, homeDirectory()).modes?.planning?.main;\n if (config?.model || config?.thinking) {\n if (!session.readModelSettings || !session.setModelSettings)\n throw new Error('This session cannot apply planning model settings.');\n const previous = await session.readModelSettings();\n const settings: Partial<DoomHeadlessModelSettings> = {};\n if (config.model) {\n const separator = config.model.indexOf('/');\n const provider = separator > 0 ? config.model.slice(0, separator) : previous.model?.provider;\n if (!provider) throw new Error('The planning model needs a provider.');\n settings.model = { provider, id: separator > 0 ? config.model.slice(separator + 1) : config.model };\n }\n if (config.thinking) settings.thinkingLevel = config.thinking;\n await session.appendCustomEntry(PLAN_MODEL_SNAPSHOT, previous);\n try {\n await session.setModelSettings(settings);\n } catch (error) {\n await session.setModelSettings(previous);\n await session.appendCustomEntry(PLAN_MODEL_SNAPSHOT, null);\n throw error;\n }\n }\n } else if (!enabled && wasEnabled) {\n await restoreModel();\n }\n const modes = (host.context.selection.state?.['minor-mode'] ?? []).filter((mode) => mode !== PLAN_MODE_ID);\n await host.changeSelection({\n axis: 'state',\n key: 'minor-mode',\n values: enabled ? [...modes, PLAN_MODE_ID] : modes,\n });\n flavor = nextFlavor;\n // Persisted because the minor mode survives a server restart while this local does not, and\n // the prompt is built from it. Without this a restored debug session silently plans as normal.\n await session.appendCustomEntry(PLAN_FLAVOR_SNAPSHOT, enabled ? { flavor } : null);\n publishMode();\n };\n\n /** Newest value for a custom entry type, or undefined when none was recorded. */\n const latestEntry = async (customType: string): Promise<unknown> => {\n const entries = await host.context.session.entries({ type: 'custom', customType, limit: 1 });\n return entries[0]?.data ?? undefined;\n };\n\n const restorePlanState = async (): Promise<void> => {\n const saved = await latestEntry(PLAN_FLAVOR_SNAPSHOT);\n if (saved !== undefined && saved !== null && typeof saved === 'object' && 'flavor' in saved) {\n const candidate = String((saved as { flavor: unknown }).flavor);\n if (candidate === 'normal' || candidate === 'debug' || candidate === 'fable') flavor = candidate;\n }\n const evidence = await latestEntry(PLAN_DEBUG_EVIDENCE);\n if (evidence === undefined || evidence === null) return;\n try {\n debugEvidence = parseDebugEvidencePacket(evidence);\n } catch {\n // A packet written by an older shape must not stop the session from planning.\n debugEvidence = undefined;\n }\n };\n modeOwner = defineMinorMode({\n descriptor: {\n source: '@agimon-ai/doompi-plan',\n id: PLAN_MODE_ID,\n label: 'Plan',\n description: 'Read-only planning with normal, debug, and Fable flavors.',\n order: 40,\n actions: [\n {\n id: 'activate',\n label: 'Activate',\n description: 'Activate plan mode or switch its planning flavor.',\n contexts: ['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: 'deactivate',\n label: 'Deactivate',\n description: 'Exit plan mode and restore the previous agent configuration.',\n contexts: ['headless'],\n parameters: [],\n },\n ],\n },\n state: () => modeState(),\n async handleAction(_runtime, actionId, argumentsValue, { signal }) {\n signal.throwIfAborted();\n if (actionId === 'activate') {\n const nextFlavor = argumentsValue.flavor;\n if (nextFlavor !== 'normal' && nextFlavor !== 'debug' && nextFlavor !== 'fable')\n throw new Error('A valid plan flavor is required.');\n await selectPlan(true, nextFlavor);\n return { message: `Plan mode is active with the ${nextFlavor} flavor.` };\n }\n if (actionId === 'deactivate') {\n await selectPlan(false);\n return { message: 'Plan mode deactivated.' };\n }\n throw new Error(`Unknown plan mode action: ${actionId}`);\n },\n }).createOwner(undefined);\n return {\n services: [serverMinorModes([modeOwner]), serverPlanSubagentPolicy(host)],\n toolRestrictions: [\n {\n when: { state: { 'minor-mode': PLAN_MODE_ID } },\n allowedTools: PLAN_MODE_ALLOWED_TOOLS,\n },\n ],\n resources: [\n {\n when: { state: { 'minor-mode': PLAN_MODE_ID }, attribution: { kind: 'minor', mode: PLAN_MODE_ID } },\n name: 'doompi-use-plan',\n kind: 'skill',\n read: () => readPackageResource(import.meta.url, 'src/prompts/doompi-use-plan/SKILL.md'),\n },\n ],\n tools: [\n {\n when: { state: { 'minor-mode': PLAN_MODE_ID }, attribution: { kind: 'minor', mode: PLAN_MODE_ID } },\n name: RECORD_DEBUG_EVIDENCE_TOOL,\n label: 'Record Debug Evidence',\n description: 'Record bounded debug evidence as optional planning context.',\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 executionMode: 'serial',\n async execute(_toolCallId, parameters) {\n try {\n const parsedEvidence = parseDebugEvidencePacket(parameters);\n await host.context.session.appendCustomEntry(PLAN_DEBUG_EVIDENCE, parsedEvidence);\n // Held in memory too: the debug prompt is rebuilt every turn from this packet.\n debugEvidence = parsedEvidence;\n return output({ recorded: true });\n } catch (error) {\n return output(error instanceof Error ? error.message : String(error), true);\n }\n },\n },\n {\n when: { state: { 'minor-mode': PLAN_MODE_ID }, attribution: { kind: 'minor', mode: PLAN_MODE_ID } },\n name: RUN_FABLE_PLAN_TOOL,\n label: 'Run Fable Plan',\n description: 'Run the configured local Fable planning broker with bounded evidence.',\n parameters: {\n type: 'object',\n properties: {\n goal: { type: 'array', items: { type: 'string' } },\n constraints: { type: 'array', items: { type: 'string' } },\n },\n required: ['goal'],\n additionalProperties: false,\n },\n executionMode: 'serial',\n async execute() {\n return output(\n 'Fable planning is unavailable in the headless host because no broker seam is configured.',\n true,\n );\n },\n },\n {\n when: { state: { 'minor-mode': PLAN_MODE_ID }, attribution: { kind: 'minor', mode: PLAN_MODE_ID } },\n name: WRITE_PLAN_TOOL,\n label: 'Write Plan',\n description: 'Save the implementation plan already presented in the session.',\n parameters: { type: 'object', properties: {}, additionalProperties: false },\n executionMode: 'serial',\n async execute(toolCallId, _parameters, signal) {\n try {\n signal?.throwIfAborted();\n // Same extraction the Pi path uses: the text this very tool call was\n // introduced by, not \"the last assistant message that looks like a heading\".\n let content = visiblePlanForToolCall(await host.context.session.entries(), toolCallId);\n if (content === undefined) {\n const requested = await host.context.client.request(\n {\n kind: 'input',\n title: 'Implementation plan',\n multiline: true,\n },\n signal,\n );\n content = typeof requested === 'string' ? requested : '';\n }\n if (!content.trim()) throw new Error('No implementation plan content was supplied.');\n const directory = plansDirectory();\n await mkdir(directory, { recursive: true, mode: 0o700 });\n const title = planTitleOf(content);\n const writtenAt = new Date().toISOString();\n const filename = `${planTitleSlug(content)}-${Date.now()}.md`;\n const filePath = path.join(directory, filename);\n await writeFile(filePath, `${content.trim()}\\n`, { encoding: 'utf8', mode: 0o600, flag: 'wx' });\n await host.context.session.appendCustomEntry(PLAN_DOCUMENT, {\n path: filePath,\n content: content.trim(),\n title,\n writtenAt,\n });\n announcePlanDocument(filePath, title, writtenAt);\n await selectPlan(true);\n return output({ path: filePath, written: true });\n } catch (error) {\n return output(error instanceof Error ? error.message : String(error), true);\n }\n },\n },\n {\n when: { state: { 'minor-mode': PLAN_MODE_ID }, attribution: { kind: 'minor', mode: PLAN_MODE_ID } },\n name: COMPLETE_PLAN_TOOL,\n label: 'Complete Plan',\n description: 'Ask the user for explicit exit-or-continue approval for the saved implementation plan.',\n // No decision parameter: this facet has no narrated review to answer, so a model-supplied\n // decision would be the agent approving its own plan instead of the reader approving it.\n parameters: { type: 'object', properties: {}, additionalProperties: false },\n executionMode: 'serial',\n async execute(_toolCallId, _parameters, signal) {\n try {\n // The client answers with the option's `value`, so the decision travels with the\n // question instead of the facet having to recognise the label it rendered.\n const decision = await host.context.client.request(\n {\n kind: 'select',\n title: PLAN_REVIEW_TITLE,\n options: PLAN_REVIEW_CHOICES.map((choice) => ({ ...choice })),\n },\n signal,\n );\n // A dismissed or aborted prompt answers with nothing, and that is not approval.\n const exited = decision === EXIT_PLAN_DECISION;\n await host.context.session.appendCustomEntry('plan-review', {\n decision: exited ? EXIT_PLAN_DECISION : CONTINUE_PLAN_DECISION,\n });\n await selectPlan(!exited);\n return {\n content: [{ type: 'text', text: exited ? PLAN_EXIT_APPROVED_TEXT : PLAN_CONTINUE_TEXT }],\n details: { exited },\n };\n } catch (error) {\n return output(error instanceof Error ? error.message : String(error), true);\n }\n },\n },\n ],\n hooks: [\n {\n when: { state: { 'minor-mode': PLAN_MODE_ID }, attribution: { kind: 'minor', mode: PLAN_MODE_ID } },\n event: 'before_agent_start',\n async handle(event) {\n const prompt = typeof event.systemPrompt === 'string' ? event.systemPrompt : '';\n const directory = plansDirectory();\n const sections = [\n buildPlanModeBasePrompt(directory, PLAN_SERVER_CAPABILITIES),\n buildFlavorPlanningPrompt(\n flavor,\n directory,\n debugEvidence,\n PLAN_SERVER_FABLE_STAGE,\n PLAN_SERVER_CAPABILITIES,\n ),\n ];\n const saved = await latestEntry(PLAN_DOCUMENT);\n if (saved !== undefined && saved !== null && typeof saved === 'object' && 'path' in saved) {\n const document = saved as { path: string; content?: string };\n // Read from disk rather than from what write_plan remembered: the plan is editable in\n // the cockpit, and a reader who corrected it expects that correction implemented.\n let content = document.content ?? '';\n try {\n content = await readFile(document.path, 'utf8');\n } catch {\n content = document.content ?? '';\n }\n if (content.trim()) sections.push(`[CURRENT PLAN]\\nSource: ${document.path}\\n\\n${content}`);\n }\n return { systemPrompt: `${prompt}\\n\\n${sections.join('\\n\\n')}`.trim() };\n },\n },\n {\n event: 'session_start',\n async handle() {\n await restorePlanState();\n // A plan written in an earlier session is still the session's plan, so\n // the dock has to hear about it again on every restart.\n const pointer = pointers.read(host.context.sessionId);\n if (pointer)\n host.context.client.setStatus(\n PLAN_STATUS_KEY,\n formatPlanStatus(pointer.title, planStampOf(pointer.writtenAt)),\n );\n // Restore a saved model override if Plan was not restored with the session.\n if (!selected()) await restoreModel();\n },\n },\n ],\n };\n}\n"],"mappings":"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"}
@@ -1,4 +1,5 @@
1
1
  import { loadDoomConfig, resolvePlanningPlansDirectory } from "../../schemas/plan/config.mjs";
2
+ import { DOOM_SUBAGENT_POLICY_SERVICE, readDoomSubagentPolicyService } from "../optionalTeamServices/index.mjs";
2
3
  import { PlanPointerService } from "../planPointer/index.mjs";
3
4
  import { buildFlavorPlanningPrompt, buildPlanModeBasePrompt } from "../prompts/index.mjs";
4
5
  import { CONTINUE_PLAN_DECISION, EXIT_PLAN_DECISION, PLAN_REVIEW_CHOICES, PLAN_REVIEW_TITLE, PLAN_STATUS_KEY, formatPlanStatus } from "../../types/planApi.mjs";
@@ -60,6 +61,45 @@ const PLAN_MODE_ALLOWED_TOOLS = [
60
61
  "use_voice_tools",
61
62
  WRITE_PLAN_TOOL
62
63
  ];
64
+ const PLAN_SUBAGENT_POLICY = {
65
+ owner: "@agimon-ai/doompi-plan",
66
+ allowedTools: [
67
+ "read",
68
+ "bash",
69
+ "grep",
70
+ "find",
71
+ "ls",
72
+ "mcp"
73
+ ],
74
+ requiredTools: ["bash"],
75
+ allowMcpTools: true,
76
+ allowedExternalProfiles: [],
77
+ denyExtensions: false
78
+ };
79
+ function serverPlanSubagentPolicy(host) {
80
+ return (context) => {
81
+ context.inject([DOOM_SUBAGENT_POLICY_SERVICE], (child) => {
82
+ const service = readDoomSubagentPolicyService(child);
83
+ if (!service) return void 0;
84
+ let handle;
85
+ const sync = () => {
86
+ if (modeSelected(host)) {
87
+ if (handle) handle.update(PLAN_SUBAGENT_POLICY);
88
+ else handle = service.register(PLAN_SUBAGENT_POLICY);
89
+ } else {
90
+ handle?.dispose();
91
+ handle = void 0;
92
+ }
93
+ };
94
+ sync();
95
+ child.effect(() => host.subscribeSelection(sync));
96
+ return () => handle?.dispose();
97
+ });
98
+ };
99
+ }
100
+ function modeSelected(host) {
101
+ return (host.context.selection.state?.["minor-mode"] ?? []).includes(PLAN_MODE_ID);
102
+ }
63
103
  function output(value, isError = false) {
64
104
  return {
65
105
  content: [{
@@ -74,7 +114,7 @@ function createPlanServerSession(host) {
74
114
  let flavor = "normal";
75
115
  let debugEvidence;
76
116
  let modeOwner;
77
- const modeSelected = () => (host.context.selection.state?.["minor-mode"] ?? []).includes(PLAN_MODE_ID);
117
+ const selected = () => modeSelected(host);
78
118
  const homeDirectory = () => host.context.environment.HOME ?? os.homedir();
79
119
  const plansDirectory = () => resolvePlanningPlansDirectory(loadDoomConfig(host.context.repoRoot, homeDirectory()).modes?.planning?.plansDirectory, host.context.repoRoot, homeDirectory());
80
120
  const pointers = new PlanPointerService({ env: host.context.environment });
@@ -96,7 +136,7 @@ function createPlanServerSession(host) {
96
136
  host.context.client.setStatus(PLAN_STATUS_KEY, formatPlanStatus(title, planStampOf(writtenAt)));
97
137
  };
98
138
  const modeState = () => {
99
- const active = modeSelected();
139
+ const active = selected();
100
140
  return {
101
141
  activation: active ? "active" : "inactive",
102
142
  condition: "ready",
@@ -137,7 +177,7 @@ function createPlanServerSession(host) {
137
177
  await session.appendCustomEntry(PLAN_MODEL_SNAPSHOT, null);
138
178
  };
139
179
  const selectPlan = async (enabled, nextFlavor = flavor) => {
140
- const wasEnabled = modeSelected();
180
+ const wasEnabled = selected();
141
181
  const session = host.context.session;
142
182
  if (enabled && !wasEnabled) {
143
183
  const config = loadDoomConfig(host.context.repoRoot, homeDirectory()).modes?.planning?.main;
@@ -254,7 +294,7 @@ function createPlanServerSession(host) {
254
294
  }
255
295
  }).createOwner(void 0);
256
296
  return {
257
- services: [serverMinorModes([modeOwner])],
297
+ services: [serverMinorModes([modeOwner]), serverPlanSubagentPolicy(host)],
258
298
  toolRestrictions: [{
259
299
  when: { state: { "minor-mode": PLAN_MODE_ID } },
260
300
  allowedTools: PLAN_MODE_ALLOWED_TOOLS
@@ -505,7 +545,7 @@ function createPlanServerSession(host) {
505
545
  await restorePlanState();
506
546
  const pointer = pointers.read(host.context.sessionId);
507
547
  if (pointer) host.context.client.setStatus(PLAN_STATUS_KEY, formatPlanStatus(pointer.title, planStampOf(pointer.writtenAt)));
508
- if (!modeSelected()) await restoreModel();
548
+ if (!selected()) await restoreModel();
509
549
  }
510
550
  }]
511
551
  };
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../../../src/services/planServerSession/index.ts"],"sourcesContent":["import { readFile } from 'node:fs/promises';\nimport { mkdir, writeFile } from 'node:fs/promises';\nimport os from 'node:os';\nimport path from 'node:path';\n\nimport { type DoomHeadlessHostService, type DoomHeadlessModelSettings } from '@agimon-ai/doompi-core/headless';\nimport { type DoomHeadlessToolResult } from '@agimon-ai/doompi-core/headless';\nimport { readPackageResource, type DoomServerSessionPlugin } from '@agimon-ai/doompi-core/server-facet';\nimport { serverMinorModes } from '@agimon-ai/doompi-minor-mode';\nimport { defineMinorMode, type MinorModeOwner, type MinorModeState } from '@agimon-ai/doompi-minor-mode';\n\nimport { loadDoomConfig, resolvePlanningPlansDirectory } from '../../schemas/plan/config';\nimport {\n buildFlavorPlanningPrompt,\n buildPlanModeBasePrompt,\n type DebugEvidencePacket,\n type PlanHostCapabilities,\n type PlanningFlavor,\n} from '../../services/prompts';\nimport {\n CONTINUE_PLAN_DECISION,\n EXIT_PLAN_DECISION,\n PLAN_REVIEW_CHOICES,\n PLAN_REVIEW_TITLE,\n PLAN_STATUS_KEY,\n formatPlanStatus,\n} from '../../types/planApi';\nimport {\n parseDebugEvidencePacket,\n PLAN_CONTINUE_TEXT,\n PLAN_EXIT_APPROVED_TEXT,\n planStampOf,\n planTitleOf,\n planTitleSlug,\n visiblePlanForToolCall,\n} from '../planMode';\nimport { PlanPointerService } from '../planPointer';\n\nconst RECORD_DEBUG_EVIDENCE_TOOL = 'record_debug_evidence';\nconst RUN_FABLE_PLAN_TOOL = 'run_fable_plan';\nconst WRITE_PLAN_TOOL = 'write_plan';\nconst COMPLETE_PLAN_TOOL = 'complete_plan';\nconst PLAN_MODE_ID = 'plan';\nconst PLAN_MODEL_SNAPSHOT = 'plan-server-model-snapshot';\nconst PLAN_FLAVOR_SNAPSHOT = 'plan-server-flavor';\nconst PLAN_DEBUG_EVIDENCE = 'plan-debug-evidence';\nconst PLAN_DOCUMENT = 'plan-document';\n/**\n * Fable needs a broker this host does not have, so `run_fable_plan` is a stub here. The stage is\n * reported honestly rather than as an idle run that is about to start.\n */\nconst PLAN_SERVER_FABLE_STAGE = 'unavailable';\n/**\n * What this facet's tools accept: complete_plan declares no parameters, there is no narrated\n * review to answer, and run_fable_plan is a stub because no broker seam is configured.\n */\nexport const PLAN_SERVER_CAPABILITIES: PlanHostCapabilities = {\n completePlanTakesDecision: false,\n narratesReview: false,\n fableAvailable: false,\n};\nconst PLAN_MODE_ALLOWED_TOOLS = [\n 'add_directory',\n 'ask_user_question',\n 'bash',\n COMPLETE_PLAN_TOOL,\n 'describe_author_tools',\n 'describe_voice_tools',\n 'find',\n 'grep',\n 'intercom',\n 'ls',\n 'minor_mode',\n 'mcp',\n 'narrate',\n 'open_authoring_file',\n 'read',\n RECORD_DEBUG_EVIDENCE_TOOL,\n RUN_FABLE_PLAN_TOOL,\n 'search_external_files',\n 'subagent',\n 'subagent_supervisor',\n 'subagent_wait',\n 'task',\n 'transfer_voice',\n 'use_author_tools',\n 'use_voice_tools',\n WRITE_PLAN_TOOL,\n] as const;\nfunction output(value: unknown, isError = false): DoomHeadlessToolResult {\n return {\n content: [{ type: 'text', text: typeof value === 'string' ? value : JSON.stringify(value, null, 2) }],\n details: value,\n ...(isError ? { isError: true } : {}),\n };\n}\n\nexport function createPlanServerSession(\n host: DoomHeadlessHostService,\n): Omit<DoomServerSessionPlugin, 'tools'> & { tools: Parameters<DoomHeadlessHostService['registerTool']>[0][] } {\n let flavor: PlanningFlavor = 'normal';\n let debugEvidence: DebugEvidencePacket | undefined;\n let modeOwner: MinorModeOwner | undefined;\n const modeSelected = (): boolean => (host.context.selection.state?.['minor-mode'] ?? []).includes(PLAN_MODE_ID);\n const homeDirectory = (): string => host.context.environment.HOME ?? os.homedir();\n // One resolution for the prompt and for write_plan. They named different directories before,\n // so the prompt could advertise a path the tool would not write to.\n const plansDirectory = (): string =>\n resolvePlanningPlansDirectory(\n loadDoomConfig(host.context.repoRoot, homeDirectory()).modes?.planning?.plansDirectory,\n host.context.repoRoot,\n homeDirectory(),\n );\n const pointers = new PlanPointerService({ env: host.context.environment });\n /**\n * The activity group's line, and the pointer its panel reads.\n *\n * A cockpit drives this facet and never the Pi runtime, so both were written\n * only on the Pi side: the group never appeared, and when it did its panel\n * had no pointer to answer from.\n */\n const announcePlanDocument = (filePath: string, title: string, writtenAt: string): void => {\n try {\n pointers.write(host.context.sessionId, { path: filePath, title, writtenAt });\n } catch {\n // The plan is on disk either way. Losing the cockpit's view of it is not\n // a reason to fail the write that produced it.\n }\n host.context.client.setStatus(PLAN_STATUS_KEY, formatPlanStatus(title, planStampOf(writtenAt)));\n };\n const modeState = (): MinorModeState => {\n const active = modeSelected();\n return {\n activation: active ? 'active' : 'inactive',\n condition: 'ready',\n ...(active ? { detail: `${flavor} - read only`, modelContextVariant: flavor } : {}),\n actions: [\n {\n id: 'activate',\n enabled: true,\n },\n {\n id: 'deactivate',\n enabled: active,\n ...(active ? {} : { disabledReason: 'Plan mode is inactive.' }),\n },\n ],\n };\n };\n const publishMode = (): void => modeOwner?.publish();\n const restoreModel = async (): Promise<void> => {\n const session = host.context.session;\n const entries = await session.entries({ type: 'custom', customType: PLAN_MODEL_SNAPSHOT, limit: 1 });\n const data = entries[0]?.data;\n if (data === undefined || data === null) return;\n if (\n typeof data !== 'object' ||\n !('thinkingLevel' in data) ||\n !['off', 'minimal', 'low', 'medium', 'high', 'xhigh', 'max'].includes(String(data.thinkingLevel))\n )\n throw new Error('The saved planning model settings are invalid.');\n if (!session.setModelSettings) throw new Error('This session cannot restore its model settings.');\n await session.setModelSettings(data as DoomHeadlessModelSettings);\n await session.appendCustomEntry(PLAN_MODEL_SNAPSHOT, null);\n };\n const selectPlan = async (enabled: boolean, nextFlavor = flavor): Promise<void> => {\n const wasEnabled = modeSelected();\n const session = host.context.session;\n if (enabled && !wasEnabled) {\n const config = loadDoomConfig(host.context.repoRoot, homeDirectory()).modes?.planning?.main;\n if (config?.model || config?.thinking) {\n if (!session.readModelSettings || !session.setModelSettings)\n throw new Error('This session cannot apply planning model settings.');\n const previous = await session.readModelSettings();\n const settings: Partial<DoomHeadlessModelSettings> = {};\n if (config.model) {\n const separator = config.model.indexOf('/');\n const provider = separator > 0 ? config.model.slice(0, separator) : previous.model?.provider;\n if (!provider) throw new Error('The planning model needs a provider.');\n settings.model = { provider, id: separator > 0 ? config.model.slice(separator + 1) : config.model };\n }\n if (config.thinking) settings.thinkingLevel = config.thinking;\n await session.appendCustomEntry(PLAN_MODEL_SNAPSHOT, previous);\n try {\n await session.setModelSettings(settings);\n } catch (error) {\n await session.setModelSettings(previous);\n await session.appendCustomEntry(PLAN_MODEL_SNAPSHOT, null);\n throw error;\n }\n }\n } else if (!enabled && wasEnabled) {\n await restoreModel();\n }\n const modes = (host.context.selection.state?.['minor-mode'] ?? []).filter((mode) => mode !== PLAN_MODE_ID);\n await host.changeSelection({\n axis: 'state',\n key: 'minor-mode',\n values: enabled ? [...modes, PLAN_MODE_ID] : modes,\n });\n flavor = nextFlavor;\n // Persisted because the minor mode survives a server restart while this local does not, and\n // the prompt is built from it. Without this a restored debug session silently plans as normal.\n await session.appendCustomEntry(PLAN_FLAVOR_SNAPSHOT, enabled ? { flavor } : null);\n publishMode();\n };\n\n /** Newest value for a custom entry type, or undefined when none was recorded. */\n const latestEntry = async (customType: string): Promise<unknown> => {\n const entries = await host.context.session.entries({ type: 'custom', customType, limit: 1 });\n return entries[0]?.data ?? undefined;\n };\n\n const restorePlanState = async (): Promise<void> => {\n const saved = await latestEntry(PLAN_FLAVOR_SNAPSHOT);\n if (saved !== undefined && saved !== null && typeof saved === 'object' && 'flavor' in saved) {\n const candidate = String((saved as { flavor: unknown }).flavor);\n if (candidate === 'normal' || candidate === 'debug' || candidate === 'fable') flavor = candidate;\n }\n const evidence = await latestEntry(PLAN_DEBUG_EVIDENCE);\n if (evidence === undefined || evidence === null) return;\n try {\n debugEvidence = parseDebugEvidencePacket(evidence);\n } catch {\n // A packet written by an older shape must not stop the session from planning.\n debugEvidence = undefined;\n }\n };\n modeOwner = defineMinorMode({\n descriptor: {\n source: '@agimon-ai/doompi-plan',\n id: PLAN_MODE_ID,\n label: 'Plan',\n description: 'Read-only planning with normal, debug, and Fable flavors.',\n order: 40,\n actions: [\n {\n id: 'activate',\n label: 'Activate',\n description: 'Activate plan mode or switch its planning flavor.',\n contexts: ['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: 'deactivate',\n label: 'Deactivate',\n description: 'Exit plan mode and restore the previous agent configuration.',\n contexts: ['headless'],\n parameters: [],\n },\n ],\n },\n state: () => modeState(),\n async handleAction(_runtime, actionId, argumentsValue, { signal }) {\n signal.throwIfAborted();\n if (actionId === 'activate') {\n const nextFlavor = argumentsValue.flavor;\n if (nextFlavor !== 'normal' && nextFlavor !== 'debug' && nextFlavor !== 'fable')\n throw new Error('A valid plan flavor is required.');\n await selectPlan(true, nextFlavor);\n return { message: `Plan mode is active with the ${nextFlavor} flavor.` };\n }\n if (actionId === 'deactivate') {\n await selectPlan(false);\n return { message: 'Plan mode deactivated.' };\n }\n throw new Error(`Unknown plan mode action: ${actionId}`);\n },\n }).createOwner(undefined);\n return {\n services: [serverMinorModes([modeOwner])],\n toolRestrictions: [\n {\n when: { state: { 'minor-mode': PLAN_MODE_ID } },\n allowedTools: PLAN_MODE_ALLOWED_TOOLS,\n },\n ],\n resources: [\n {\n when: { state: { 'minor-mode': PLAN_MODE_ID }, attribution: { kind: 'minor', mode: PLAN_MODE_ID } },\n name: 'doompi-use-plan',\n kind: 'skill',\n read: () => readPackageResource(import.meta.url, 'src/prompts/doompi-use-plan/SKILL.md'),\n },\n ],\n tools: [\n {\n when: { state: { 'minor-mode': PLAN_MODE_ID }, attribution: { kind: 'minor', mode: PLAN_MODE_ID } },\n name: RECORD_DEBUG_EVIDENCE_TOOL,\n label: 'Record Debug Evidence',\n description: 'Record bounded debug evidence as optional planning context.',\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 executionMode: 'serial',\n async execute(_toolCallId, parameters) {\n try {\n const parsedEvidence = parseDebugEvidencePacket(parameters);\n await host.context.session.appendCustomEntry(PLAN_DEBUG_EVIDENCE, parsedEvidence);\n // Held in memory too: the debug prompt is rebuilt every turn from this packet.\n debugEvidence = parsedEvidence;\n return output({ recorded: true });\n } catch (error) {\n return output(error instanceof Error ? error.message : String(error), true);\n }\n },\n },\n {\n when: { state: { 'minor-mode': PLAN_MODE_ID }, attribution: { kind: 'minor', mode: PLAN_MODE_ID } },\n name: RUN_FABLE_PLAN_TOOL,\n label: 'Run Fable Plan',\n description: 'Run the configured local Fable planning broker with bounded evidence.',\n parameters: {\n type: 'object',\n properties: {\n goal: { type: 'array', items: { type: 'string' } },\n constraints: { type: 'array', items: { type: 'string' } },\n },\n required: ['goal'],\n additionalProperties: false,\n },\n executionMode: 'serial',\n async execute() {\n return output(\n 'Fable planning is unavailable in the headless host because no broker seam is configured.',\n true,\n );\n },\n },\n {\n when: { state: { 'minor-mode': PLAN_MODE_ID }, attribution: { kind: 'minor', mode: PLAN_MODE_ID } },\n name: WRITE_PLAN_TOOL,\n label: 'Write Plan',\n description: 'Save the implementation plan already presented in the session.',\n parameters: { type: 'object', properties: {}, additionalProperties: false },\n executionMode: 'serial',\n async execute(toolCallId, _parameters, signal) {\n try {\n signal?.throwIfAborted();\n // Same extraction the Pi path uses: the text this very tool call was\n // introduced by, not \"the last assistant message that looks like a heading\".\n let content = visiblePlanForToolCall(await host.context.session.entries(), toolCallId);\n if (content === undefined) {\n const requested = await host.context.client.request(\n {\n kind: 'input',\n title: 'Implementation plan',\n multiline: true,\n },\n signal,\n );\n content = typeof requested === 'string' ? requested : '';\n }\n if (!content.trim()) throw new Error('No implementation plan content was supplied.');\n const directory = plansDirectory();\n await mkdir(directory, { recursive: true, mode: 0o700 });\n const title = planTitleOf(content);\n const writtenAt = new Date().toISOString();\n const filename = `${planTitleSlug(content)}-${Date.now()}.md`;\n const filePath = path.join(directory, filename);\n await writeFile(filePath, `${content.trim()}\\n`, { encoding: 'utf8', mode: 0o600, flag: 'wx' });\n await host.context.session.appendCustomEntry(PLAN_DOCUMENT, {\n path: filePath,\n content: content.trim(),\n title,\n writtenAt,\n });\n announcePlanDocument(filePath, title, writtenAt);\n await selectPlan(true);\n return output({ path: filePath, written: true });\n } catch (error) {\n return output(error instanceof Error ? error.message : String(error), true);\n }\n },\n },\n {\n when: { state: { 'minor-mode': PLAN_MODE_ID }, attribution: { kind: 'minor', mode: PLAN_MODE_ID } },\n name: COMPLETE_PLAN_TOOL,\n label: 'Complete Plan',\n description: 'Ask the user for explicit exit-or-continue approval for the saved implementation plan.',\n // No decision parameter: this facet has no narrated review to answer, so a model-supplied\n // decision would be the agent approving its own plan instead of the reader approving it.\n parameters: { type: 'object', properties: {}, additionalProperties: false },\n executionMode: 'serial',\n async execute(_toolCallId, _parameters, signal) {\n try {\n // The client answers with the option's `value`, so the decision travels with the\n // question instead of the facet having to recognise the label it rendered.\n const decision = await host.context.client.request(\n {\n kind: 'select',\n title: PLAN_REVIEW_TITLE,\n options: PLAN_REVIEW_CHOICES.map((choice) => ({ ...choice })),\n },\n signal,\n );\n // A dismissed or aborted prompt answers with nothing, and that is not approval.\n const exited = decision === EXIT_PLAN_DECISION;\n await host.context.session.appendCustomEntry('plan-review', {\n decision: exited ? EXIT_PLAN_DECISION : CONTINUE_PLAN_DECISION,\n });\n await selectPlan(!exited);\n return {\n content: [{ type: 'text', text: exited ? PLAN_EXIT_APPROVED_TEXT : PLAN_CONTINUE_TEXT }],\n details: { exited },\n };\n } catch (error) {\n return output(error instanceof Error ? error.message : String(error), true);\n }\n },\n },\n ],\n hooks: [\n {\n when: { state: { 'minor-mode': PLAN_MODE_ID }, attribution: { kind: 'minor', mode: PLAN_MODE_ID } },\n event: 'before_agent_start',\n async handle(event) {\n const prompt = typeof event.systemPrompt === 'string' ? event.systemPrompt : '';\n const directory = plansDirectory();\n const sections = [\n buildPlanModeBasePrompt(directory, PLAN_SERVER_CAPABILITIES),\n buildFlavorPlanningPrompt(\n flavor,\n directory,\n debugEvidence,\n PLAN_SERVER_FABLE_STAGE,\n PLAN_SERVER_CAPABILITIES,\n ),\n ];\n const saved = await latestEntry(PLAN_DOCUMENT);\n if (saved !== undefined && saved !== null && typeof saved === 'object' && 'path' in saved) {\n const document = saved as { path: string; content?: string };\n // Read from disk rather than from what write_plan remembered: the plan is editable in\n // the cockpit, and a reader who corrected it expects that correction implemented.\n let content = document.content ?? '';\n try {\n content = await readFile(document.path, 'utf8');\n } catch {\n content = document.content ?? '';\n }\n if (content.trim()) sections.push(`[CURRENT PLAN]\\nSource: ${document.path}\\n\\n${content}`);\n }\n return { systemPrompt: `${prompt}\\n\\n${sections.join('\\n\\n')}`.trim() };\n },\n },\n {\n event: 'session_start',\n async handle() {\n await restorePlanState();\n // A plan written in an earlier session is still the session's plan, so\n // the dock has to hear about it again on every restart.\n const pointer = pointers.read(host.context.sessionId);\n if (pointer)\n host.context.client.setStatus(\n PLAN_STATUS_KEY,\n formatPlanStatus(pointer.title, planStampOf(pointer.writtenAt)),\n );\n // Restore a saved model override if Plan was not restored with the session.\n if (!modeSelected()) await restoreModel();\n },\n },\n ],\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../../../src/services/planServerSession/index.ts"],"sourcesContent":["import { readFile } from 'node:fs/promises';\nimport { mkdir, writeFile } from 'node:fs/promises';\nimport os from 'node:os';\nimport path from 'node:path';\n\nimport { type DoomHeadlessHostService, type DoomHeadlessModelSettings } from '@agimon-ai/doompi-core/headless';\nimport { type DoomHeadlessToolResult } from '@agimon-ai/doompi-core/headless';\nimport { readPackageResource, type DoomServerSessionPlugin } from '@agimon-ai/doompi-core/server-facet';\nimport { serverMinorModes } from '@agimon-ai/doompi-minor-mode';\nimport { defineMinorMode, type MinorModeOwner, type MinorModeState } from '@agimon-ai/doompi-minor-mode';\nimport type { Context } from '@deepseek-ai/cordis';\n\nimport { loadDoomConfig, resolvePlanningPlansDirectory } from '../../schemas/plan/config';\nimport {\n buildFlavorPlanningPrompt,\n buildPlanModeBasePrompt,\n type DebugEvidencePacket,\n type PlanHostCapabilities,\n type PlanningFlavor,\n} from '../../services/prompts';\nimport {\n CONTINUE_PLAN_DECISION,\n EXIT_PLAN_DECISION,\n PLAN_REVIEW_CHOICES,\n PLAN_REVIEW_TITLE,\n PLAN_STATUS_KEY,\n formatPlanStatus,\n} from '../../types/planApi';\nimport {\n DOOM_SUBAGENT_POLICY_SERVICE,\n readDoomSubagentPolicyService,\n type SubagentPolicy,\n type SubagentPolicyHandle,\n} from '../optionalTeamServices';\nimport {\n parseDebugEvidencePacket,\n PLAN_CONTINUE_TEXT,\n PLAN_EXIT_APPROVED_TEXT,\n planStampOf,\n planTitleOf,\n planTitleSlug,\n visiblePlanForToolCall,\n} from '../planMode';\nimport { PlanPointerService } from '../planPointer';\n\nconst RECORD_DEBUG_EVIDENCE_TOOL = 'record_debug_evidence';\nconst RUN_FABLE_PLAN_TOOL = 'run_fable_plan';\nconst WRITE_PLAN_TOOL = 'write_plan';\nconst COMPLETE_PLAN_TOOL = 'complete_plan';\nconst PLAN_MODE_ID = 'plan';\nconst PLAN_MODEL_SNAPSHOT = 'plan-server-model-snapshot';\nconst PLAN_FLAVOR_SNAPSHOT = 'plan-server-flavor';\nconst PLAN_DEBUG_EVIDENCE = 'plan-debug-evidence';\nconst PLAN_DOCUMENT = 'plan-document';\n/**\n * Fable needs a broker this host does not have, so `run_fable_plan` is a stub here. The stage is\n * reported honestly rather than as an idle run that is about to start.\n */\nconst PLAN_SERVER_FABLE_STAGE = 'unavailable';\n/**\n * What this facet's tools accept: complete_plan declares no parameters, there is no narrated\n * review to answer, and run_fable_plan is a stub because no broker seam is configured.\n */\nexport const PLAN_SERVER_CAPABILITIES: PlanHostCapabilities = {\n completePlanTakesDecision: false,\n narratesReview: false,\n fableAvailable: false,\n};\nconst PLAN_MODE_ALLOWED_TOOLS = [\n 'add_directory',\n 'ask_user_question',\n 'bash',\n COMPLETE_PLAN_TOOL,\n 'describe_author_tools',\n 'describe_voice_tools',\n 'find',\n 'grep',\n 'intercom',\n 'ls',\n 'minor_mode',\n 'mcp',\n 'narrate',\n 'open_authoring_file',\n 'read',\n RECORD_DEBUG_EVIDENCE_TOOL,\n RUN_FABLE_PLAN_TOOL,\n 'search_external_files',\n 'subagent',\n 'subagent_supervisor',\n 'subagent_wait',\n 'task',\n 'transfer_voice',\n 'use_author_tools',\n 'use_voice_tools',\n WRITE_PLAN_TOOL,\n] as const;\nconst PLAN_SUBAGENT_POLICY_OWNER = '@agimon-ai/doompi-plan';\nconst PLAN_SUBAGENT_POLICY: SubagentPolicy = {\n owner: PLAN_SUBAGENT_POLICY_OWNER,\n allowedTools: ['read', 'bash', 'grep', 'find', 'ls', 'mcp'],\n requiredTools: ['bash'],\n allowMcpTools: true,\n allowedExternalProfiles: [],\n denyExtensions: false,\n};\n\nfunction serverPlanSubagentPolicy(host: DoomHeadlessHostService): (context: Context) => void {\n return (context) => {\n context.inject([DOOM_SUBAGENT_POLICY_SERVICE], (child) => {\n const service = readDoomSubagentPolicyService(child);\n if (!service) return undefined;\n let handle: SubagentPolicyHandle | undefined;\n const sync = (): void => {\n if (modeSelected(host)) {\n if (handle) handle.update(PLAN_SUBAGENT_POLICY);\n else handle = service.register(PLAN_SUBAGENT_POLICY);\n } else {\n handle?.dispose();\n handle = undefined;\n }\n };\n sync();\n child.effect(() => host.subscribeSelection(sync));\n return () => handle?.dispose();\n });\n };\n}\n\nfunction modeSelected(host: DoomHeadlessHostService): boolean {\n return (host.context.selection.state?.['minor-mode'] ?? []).includes(PLAN_MODE_ID);\n}\n\nfunction output(value: unknown, isError = false): DoomHeadlessToolResult {\n return {\n content: [{ type: 'text', text: typeof value === 'string' ? value : JSON.stringify(value, null, 2) }],\n details: value,\n ...(isError ? { isError: true } : {}),\n };\n}\n\nexport function createPlanServerSession(\n host: DoomHeadlessHostService,\n): Omit<DoomServerSessionPlugin, 'tools'> & { tools: Parameters<DoomHeadlessHostService['registerTool']>[0][] } {\n let flavor: PlanningFlavor = 'normal';\n let debugEvidence: DebugEvidencePacket | undefined;\n let modeOwner: MinorModeOwner | undefined;\n const selected = (): boolean => modeSelected(host);\n const homeDirectory = (): string => host.context.environment.HOME ?? os.homedir();\n // One resolution for the prompt and for write_plan. They named different directories before,\n // so the prompt could advertise a path the tool would not write to.\n const plansDirectory = (): string =>\n resolvePlanningPlansDirectory(\n loadDoomConfig(host.context.repoRoot, homeDirectory()).modes?.planning?.plansDirectory,\n host.context.repoRoot,\n homeDirectory(),\n );\n const pointers = new PlanPointerService({ env: host.context.environment });\n /**\n * The activity group's line, and the pointer its panel reads.\n *\n * A cockpit drives this facet and never the Pi runtime, so both were written\n * only on the Pi side: the group never appeared, and when it did its panel\n * had no pointer to answer from.\n */\n const announcePlanDocument = (filePath: string, title: string, writtenAt: string): void => {\n try {\n pointers.write(host.context.sessionId, { path: filePath, title, writtenAt });\n } catch {\n // The plan is on disk either way. Losing the cockpit's view of it is not\n // a reason to fail the write that produced it.\n }\n host.context.client.setStatus(PLAN_STATUS_KEY, formatPlanStatus(title, planStampOf(writtenAt)));\n };\n const modeState = (): MinorModeState => {\n const active = selected();\n return {\n activation: active ? 'active' : 'inactive',\n condition: 'ready',\n ...(active ? { detail: `${flavor} - read only`, modelContextVariant: flavor } : {}),\n actions: [\n {\n id: 'activate',\n enabled: true,\n },\n {\n id: 'deactivate',\n enabled: active,\n ...(active ? {} : { disabledReason: 'Plan mode is inactive.' }),\n },\n ],\n };\n };\n const publishMode = (): void => modeOwner?.publish();\n const restoreModel = async (): Promise<void> => {\n const session = host.context.session;\n const entries = await session.entries({ type: 'custom', customType: PLAN_MODEL_SNAPSHOT, limit: 1 });\n const data = entries[0]?.data;\n if (data === undefined || data === null) return;\n if (\n typeof data !== 'object' ||\n !('thinkingLevel' in data) ||\n !['off', 'minimal', 'low', 'medium', 'high', 'xhigh', 'max'].includes(String(data.thinkingLevel))\n )\n throw new Error('The saved planning model settings are invalid.');\n if (!session.setModelSettings) throw new Error('This session cannot restore its model settings.');\n await session.setModelSettings(data as DoomHeadlessModelSettings);\n await session.appendCustomEntry(PLAN_MODEL_SNAPSHOT, null);\n };\n const selectPlan = async (enabled: boolean, nextFlavor = flavor): Promise<void> => {\n const wasEnabled = selected();\n const session = host.context.session;\n if (enabled && !wasEnabled) {\n const config = loadDoomConfig(host.context.repoRoot, homeDirectory()).modes?.planning?.main;\n if (config?.model || config?.thinking) {\n if (!session.readModelSettings || !session.setModelSettings)\n throw new Error('This session cannot apply planning model settings.');\n const previous = await session.readModelSettings();\n const settings: Partial<DoomHeadlessModelSettings> = {};\n if (config.model) {\n const separator = config.model.indexOf('/');\n const provider = separator > 0 ? config.model.slice(0, separator) : previous.model?.provider;\n if (!provider) throw new Error('The planning model needs a provider.');\n settings.model = { provider, id: separator > 0 ? config.model.slice(separator + 1) : config.model };\n }\n if (config.thinking) settings.thinkingLevel = config.thinking;\n await session.appendCustomEntry(PLAN_MODEL_SNAPSHOT, previous);\n try {\n await session.setModelSettings(settings);\n } catch (error) {\n await session.setModelSettings(previous);\n await session.appendCustomEntry(PLAN_MODEL_SNAPSHOT, null);\n throw error;\n }\n }\n } else if (!enabled && wasEnabled) {\n await restoreModel();\n }\n const modes = (host.context.selection.state?.['minor-mode'] ?? []).filter((mode) => mode !== PLAN_MODE_ID);\n await host.changeSelection({\n axis: 'state',\n key: 'minor-mode',\n values: enabled ? [...modes, PLAN_MODE_ID] : modes,\n });\n flavor = nextFlavor;\n // Persisted because the minor mode survives a server restart while this local does not, and\n // the prompt is built from it. Without this a restored debug session silently plans as normal.\n await session.appendCustomEntry(PLAN_FLAVOR_SNAPSHOT, enabled ? { flavor } : null);\n publishMode();\n };\n\n /** Newest value for a custom entry type, or undefined when none was recorded. */\n const latestEntry = async (customType: string): Promise<unknown> => {\n const entries = await host.context.session.entries({ type: 'custom', customType, limit: 1 });\n return entries[0]?.data ?? undefined;\n };\n\n const restorePlanState = async (): Promise<void> => {\n const saved = await latestEntry(PLAN_FLAVOR_SNAPSHOT);\n if (saved !== undefined && saved !== null && typeof saved === 'object' && 'flavor' in saved) {\n const candidate = String((saved as { flavor: unknown }).flavor);\n if (candidate === 'normal' || candidate === 'debug' || candidate === 'fable') flavor = candidate;\n }\n const evidence = await latestEntry(PLAN_DEBUG_EVIDENCE);\n if (evidence === undefined || evidence === null) return;\n try {\n debugEvidence = parseDebugEvidencePacket(evidence);\n } catch {\n // A packet written by an older shape must not stop the session from planning.\n debugEvidence = undefined;\n }\n };\n modeOwner = defineMinorMode({\n descriptor: {\n source: '@agimon-ai/doompi-plan',\n id: PLAN_MODE_ID,\n label: 'Plan',\n description: 'Read-only planning with normal, debug, and Fable flavors.',\n order: 40,\n actions: [\n {\n id: 'activate',\n label: 'Activate',\n description: 'Activate plan mode or switch its planning flavor.',\n contexts: ['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: 'deactivate',\n label: 'Deactivate',\n description: 'Exit plan mode and restore the previous agent configuration.',\n contexts: ['headless'],\n parameters: [],\n },\n ],\n },\n state: () => modeState(),\n async handleAction(_runtime, actionId, argumentsValue, { signal }) {\n signal.throwIfAborted();\n if (actionId === 'activate') {\n const nextFlavor = argumentsValue.flavor;\n if (nextFlavor !== 'normal' && nextFlavor !== 'debug' && nextFlavor !== 'fable')\n throw new Error('A valid plan flavor is required.');\n await selectPlan(true, nextFlavor);\n return { message: `Plan mode is active with the ${nextFlavor} flavor.` };\n }\n if (actionId === 'deactivate') {\n await selectPlan(false);\n return { message: 'Plan mode deactivated.' };\n }\n throw new Error(`Unknown plan mode action: ${actionId}`);\n },\n }).createOwner(undefined);\n return {\n services: [serverMinorModes([modeOwner]), serverPlanSubagentPolicy(host)],\n toolRestrictions: [\n {\n when: { state: { 'minor-mode': PLAN_MODE_ID } },\n allowedTools: PLAN_MODE_ALLOWED_TOOLS,\n },\n ],\n resources: [\n {\n when: { state: { 'minor-mode': PLAN_MODE_ID }, attribution: { kind: 'minor', mode: PLAN_MODE_ID } },\n name: 'doompi-use-plan',\n kind: 'skill',\n read: () => readPackageResource(import.meta.url, 'src/prompts/doompi-use-plan/SKILL.md'),\n },\n ],\n tools: [\n {\n when: { state: { 'minor-mode': PLAN_MODE_ID }, attribution: { kind: 'minor', mode: PLAN_MODE_ID } },\n name: RECORD_DEBUG_EVIDENCE_TOOL,\n label: 'Record Debug Evidence',\n description: 'Record bounded debug evidence as optional planning context.',\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 executionMode: 'serial',\n async execute(_toolCallId, parameters) {\n try {\n const parsedEvidence = parseDebugEvidencePacket(parameters);\n await host.context.session.appendCustomEntry(PLAN_DEBUG_EVIDENCE, parsedEvidence);\n // Held in memory too: the debug prompt is rebuilt every turn from this packet.\n debugEvidence = parsedEvidence;\n return output({ recorded: true });\n } catch (error) {\n return output(error instanceof Error ? error.message : String(error), true);\n }\n },\n },\n {\n when: { state: { 'minor-mode': PLAN_MODE_ID }, attribution: { kind: 'minor', mode: PLAN_MODE_ID } },\n name: RUN_FABLE_PLAN_TOOL,\n label: 'Run Fable Plan',\n description: 'Run the configured local Fable planning broker with bounded evidence.',\n parameters: {\n type: 'object',\n properties: {\n goal: { type: 'array', items: { type: 'string' } },\n constraints: { type: 'array', items: { type: 'string' } },\n },\n required: ['goal'],\n additionalProperties: false,\n },\n executionMode: 'serial',\n async execute() {\n return output(\n 'Fable planning is unavailable in the headless host because no broker seam is configured.',\n true,\n );\n },\n },\n {\n when: { state: { 'minor-mode': PLAN_MODE_ID }, attribution: { kind: 'minor', mode: PLAN_MODE_ID } },\n name: WRITE_PLAN_TOOL,\n label: 'Write Plan',\n description: 'Save the implementation plan already presented in the session.',\n parameters: { type: 'object', properties: {}, additionalProperties: false },\n executionMode: 'serial',\n async execute(toolCallId, _parameters, signal) {\n try {\n signal?.throwIfAborted();\n // Same extraction the Pi path uses: the text this very tool call was\n // introduced by, not \"the last assistant message that looks like a heading\".\n let content = visiblePlanForToolCall(await host.context.session.entries(), toolCallId);\n if (content === undefined) {\n const requested = await host.context.client.request(\n {\n kind: 'input',\n title: 'Implementation plan',\n multiline: true,\n },\n signal,\n );\n content = typeof requested === 'string' ? requested : '';\n }\n if (!content.trim()) throw new Error('No implementation plan content was supplied.');\n const directory = plansDirectory();\n await mkdir(directory, { recursive: true, mode: 0o700 });\n const title = planTitleOf(content);\n const writtenAt = new Date().toISOString();\n const filename = `${planTitleSlug(content)}-${Date.now()}.md`;\n const filePath = path.join(directory, filename);\n await writeFile(filePath, `${content.trim()}\\n`, { encoding: 'utf8', mode: 0o600, flag: 'wx' });\n await host.context.session.appendCustomEntry(PLAN_DOCUMENT, {\n path: filePath,\n content: content.trim(),\n title,\n writtenAt,\n });\n announcePlanDocument(filePath, title, writtenAt);\n await selectPlan(true);\n return output({ path: filePath, written: true });\n } catch (error) {\n return output(error instanceof Error ? error.message : String(error), true);\n }\n },\n },\n {\n when: { state: { 'minor-mode': PLAN_MODE_ID }, attribution: { kind: 'minor', mode: PLAN_MODE_ID } },\n name: COMPLETE_PLAN_TOOL,\n label: 'Complete Plan',\n description: 'Ask the user for explicit exit-or-continue approval for the saved implementation plan.',\n // No decision parameter: this facet has no narrated review to answer, so a model-supplied\n // decision would be the agent approving its own plan instead of the reader approving it.\n parameters: { type: 'object', properties: {}, additionalProperties: false },\n executionMode: 'serial',\n async execute(_toolCallId, _parameters, signal) {\n try {\n // The client answers with the option's `value`, so the decision travels with the\n // question instead of the facet having to recognise the label it rendered.\n const decision = await host.context.client.request(\n {\n kind: 'select',\n title: PLAN_REVIEW_TITLE,\n options: PLAN_REVIEW_CHOICES.map((choice) => ({ ...choice })),\n },\n signal,\n );\n // A dismissed or aborted prompt answers with nothing, and that is not approval.\n const exited = decision === EXIT_PLAN_DECISION;\n await host.context.session.appendCustomEntry('plan-review', {\n decision: exited ? EXIT_PLAN_DECISION : CONTINUE_PLAN_DECISION,\n });\n await selectPlan(!exited);\n return {\n content: [{ type: 'text', text: exited ? PLAN_EXIT_APPROVED_TEXT : PLAN_CONTINUE_TEXT }],\n details: { exited },\n };\n } catch (error) {\n return output(error instanceof Error ? error.message : String(error), true);\n }\n },\n },\n ],\n hooks: [\n {\n when: { state: { 'minor-mode': PLAN_MODE_ID }, attribution: { kind: 'minor', mode: PLAN_MODE_ID } },\n event: 'before_agent_start',\n async handle(event) {\n const prompt = typeof event.systemPrompt === 'string' ? event.systemPrompt : '';\n const directory = plansDirectory();\n const sections = [\n buildPlanModeBasePrompt(directory, PLAN_SERVER_CAPABILITIES),\n buildFlavorPlanningPrompt(\n flavor,\n directory,\n debugEvidence,\n PLAN_SERVER_FABLE_STAGE,\n PLAN_SERVER_CAPABILITIES,\n ),\n ];\n const saved = await latestEntry(PLAN_DOCUMENT);\n if (saved !== undefined && saved !== null && typeof saved === 'object' && 'path' in saved) {\n const document = saved as { path: string; content?: string };\n // Read from disk rather than from what write_plan remembered: the plan is editable in\n // the cockpit, and a reader who corrected it expects that correction implemented.\n let content = document.content ?? '';\n try {\n content = await readFile(document.path, 'utf8');\n } catch {\n content = document.content ?? '';\n }\n if (content.trim()) sections.push(`[CURRENT PLAN]\\nSource: ${document.path}\\n\\n${content}`);\n }\n return { systemPrompt: `${prompt}\\n\\n${sections.join('\\n\\n')}`.trim() };\n },\n },\n {\n event: 'session_start',\n async handle() {\n await restorePlanState();\n // A plan written in an earlier session is still the session's plan, so\n // the dock has to hear about it again on every restart.\n const pointer = pointers.read(host.context.sessionId);\n if (pointer)\n host.context.client.setStatus(\n PLAN_STATUS_KEY,\n formatPlanStatus(pointer.title, planStampOf(pointer.writtenAt)),\n );\n // Restore a saved model override if Plan was not restored with the session.\n if (!selected()) await restoreModel();\n },\n },\n ],\n };\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agimon-ai/doompi-plan",
3
- "version": "0.0.1-alpha.76",
3
+ "version": "0.0.1-alpha.77",
4
4
  "description": "Reviewable Pi planning mode that removes file-editing tools and persists implementation plans.",
5
5
  "keywords": [
6
6
  "ai",
@@ -95,18 +95,18 @@
95
95
  "registry": "https://registry.npmjs.org/"
96
96
  },
97
97
  "dependencies": {
98
- "@agimon-ai/doompi-config": "0.0.1-alpha.75",
99
- "@agimon-ai/doompi-core": "0.0.1-alpha.76",
100
- "@agimon-ai/doompi-minor-mode": "0.0.1-alpha.76",
101
- "@agimon-ai/doompi-telemetry": "0.0.1-alpha.73",
102
- "@agimon-ai/doompi-web-components": "0.0.1-alpha.34",
103
- "@agimon-ai/doompi-web-security": "0.0.1-alpha.36",
98
+ "@agimon-ai/doompi-config": "0.0.1-alpha.76",
99
+ "@agimon-ai/doompi-core": "0.0.1-alpha.77",
100
+ "@agimon-ai/doompi-minor-mode": "0.0.1-alpha.77",
101
+ "@agimon-ai/doompi-telemetry": "0.0.1-alpha.74",
102
+ "@agimon-ai/doompi-web-components": "0.0.1-alpha.35",
103
+ "@agimon-ai/doompi-web-security": "0.0.1-alpha.37",
104
104
  "@deepseek-ai/cordis": "4.0.2",
105
105
  "hono": "4.13.7",
106
106
  "typebox": "1.3.30"
107
107
  },
108
108
  "devDependencies": {
109
- "@agimon-ai/doompi-build": "0.0.1-alpha.5",
109
+ "@agimon-ai/doompi-build": "0.0.1-alpha.6",
110
110
  "@earendil-works/pi-coding-agent": "0.85.1",
111
111
  "@tanstack/react-store": "0.11.1",
112
112
  "@tanstack/store": "0.11.1",
@@ -125,6 +125,13 @@
125
125
  "engines": {
126
126
  "node": ">=22.19.0"
127
127
  },
128
+ "doompiMcp": {
129
+ "entry": "./generated/mcp.ts",
130
+ "dist": "./dist/extensions/mcp.mjs",
131
+ "scopes": [
132
+ "session"
133
+ ]
134
+ },
128
135
  "doompiServer": {
129
136
  "entry": "./generated/server.ts",
130
137
  "dist": "./dist/extensions/server.mjs",
@@ -152,7 +159,8 @@
152
159
  ]
153
160
  },
154
161
  "scripts": {
155
- "build": "tsdown",
162
+ "build": "tsdown && tsdown --config tsdown.mcp.config.ts",
163
+ "build:mcp": "tsdown --config tsdown.mcp.config.ts",
156
164
  "test": "vitest --run",
157
165
  "typecheck": "tsc --noEmit && tsc --noEmit -p tsconfig.web.json",
158
166
  "lint": "oxlint . && oxfmt . --check",