@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.
- package/dist/extensions/mcp.cjs +73 -0
- package/dist/extensions/mcp.cjs.map +1 -0
- package/dist/extensions/mcp.d.cts +5 -0
- package/dist/extensions/mcp.d.cts.map +1 -0
- package/dist/extensions/mcp.d.mts +5 -0
- package/dist/extensions/mcp.d.mts.map +1 -0
- package/dist/extensions/mcp.mjs +68 -0
- package/dist/extensions/mcp.mjs.map +1 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/root.server.cjs +6 -5
- package/dist/src/extensions/workspaces/sessions/(backend)/root.server.cjs.map +1 -1
- package/dist/src/extensions/workspaces/sessions/(backend)/root.server.mjs +2 -1
- package/dist/src/extensions/workspaces/sessions/(backend)/root.server.mjs.map +1 -1
- package/dist/src/services/mcpTools/index.cjs +17 -0
- package/dist/src/services/mcpTools/index.cjs.map +1 -0
- package/dist/src/services/mcpTools/index.mjs +16 -0
- package/dist/src/services/mcpTools/index.mjs.map +1 -0
- package/dist/src/services/planServerSession/index.cjs +58 -18
- package/dist/src/services/planServerSession/index.cjs.map +1 -1
- package/dist/src/services/planServerSession/index.mjs +45 -5
- package/dist/src/services/planServerSession/index.mjs.map +1 -1
- package/package.json +17 -9
|
@@ -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 @@
|
|
|
1
|
+
{"version":3,"file":"mcp.d.cts","names":[],"sources":["../../generated/mcp.ts"],"mappings":";qBAca,gDAAG"}
|
|
@@ -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/
|
|
2
|
-
const require_index$1 = require("../../../../services/
|
|
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$
|
|
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 {
|
|
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 {
|
|
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("../
|
|
4
|
-
const require_index$1 = require("../
|
|
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$
|
|
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
|
|
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$
|
|
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 =
|
|
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 =
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
455
|
+
const title = require_index$3.planTitleOf(content);
|
|
416
456
|
const writtenAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
417
|
-
const filename = `${require_index$
|
|
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$
|
|
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$
|
|
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$
|
|
512
|
-
if (!
|
|
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
|
|
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 =
|
|
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 =
|
|
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 (!
|
|
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.
|
|
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.
|
|
99
|
-
"@agimon-ai/doompi-core": "0.0.1-alpha.
|
|
100
|
-
"@agimon-ai/doompi-minor-mode": "0.0.1-alpha.
|
|
101
|
-
"@agimon-ai/doompi-telemetry": "0.0.1-alpha.
|
|
102
|
-
"@agimon-ai/doompi-web-components": "0.0.1-alpha.
|
|
103
|
-
"@agimon-ai/doompi-web-security": "0.0.1-alpha.
|
|
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.
|
|
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",
|