@agimon-ai/po-plan 0.0.0-alpha.45 → 0.0.0-alpha.47
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.
|
@@ -32,15 +32,13 @@ let _agimon_ai_po_core_extensionFile = require("@agimon-ai/po-core/extensionFile
|
|
|
32
32
|
let typebox = require("typebox");
|
|
33
33
|
let node_crypto = require("node:crypto");
|
|
34
34
|
let hono = require("hono");
|
|
35
|
-
let
|
|
36
|
-
|
|
35
|
+
let node_fs_promises = require("node:fs/promises");
|
|
36
|
+
node_fs_promises = __toESM(node_fs_promises, 1);
|
|
37
37
|
let node_os = require("node:os");
|
|
38
38
|
node_os = __toESM(node_os, 1);
|
|
39
39
|
let node_path = require("node:path");
|
|
40
40
|
node_path = __toESM(node_path, 1);
|
|
41
41
|
let _agimon_ai_po_core_piSettings = require("@agimon-ai/po-core/piSettings");
|
|
42
|
-
let node_fs_promises = require("node:fs/promises");
|
|
43
|
-
node_fs_promises = __toESM(node_fs_promises, 1);
|
|
44
42
|
let _agimon_ai_po_core_platform = require("@agimon-ai/po-core/platform");
|
|
45
43
|
let node_buffer = require("node:buffer");
|
|
46
44
|
let _agimon_ai_po_core_voiceTools = require("@agimon-ai/po-core/voiceTools");
|
|
@@ -99,10 +97,10 @@ var PlanPointerService = class {
|
|
|
99
97
|
pathFor(sessionId) {
|
|
100
98
|
return node_path.default.join(this.directory(), `${this.hash(sessionId)}.json`);
|
|
101
99
|
}
|
|
102
|
-
read(sessionId) {
|
|
100
|
+
async read(sessionId) {
|
|
103
101
|
let raw;
|
|
104
102
|
try {
|
|
105
|
-
raw =
|
|
103
|
+
raw = await node_fs_promises.default.readFile(this.pathFor(sessionId), "utf8");
|
|
106
104
|
} catch {
|
|
107
105
|
return;
|
|
108
106
|
}
|
|
@@ -123,15 +121,15 @@ var PlanPointerService = class {
|
|
|
123
121
|
...typeof planId === "string" ? { planId } : {}
|
|
124
122
|
};
|
|
125
123
|
}
|
|
126
|
-
write(sessionId, record) {
|
|
127
|
-
|
|
124
|
+
async write(sessionId, record) {
|
|
125
|
+
await node_fs_promises.default.mkdir(this.directory(), {
|
|
128
126
|
recursive: true,
|
|
129
127
|
mode: PRIVATE_DIRECTORY_MODE
|
|
130
128
|
});
|
|
131
|
-
|
|
129
|
+
await node_fs_promises.default.writeFile(this.pathFor(sessionId), JSON.stringify(record), { mode: PRIVATE_FILE_MODE });
|
|
132
130
|
}
|
|
133
|
-
clear(sessionId) {
|
|
134
|
-
|
|
131
|
+
async clear(sessionId) {
|
|
132
|
+
await node_fs_promises.default.rm(this.pathFor(sessionId), { force: true });
|
|
135
133
|
}
|
|
136
134
|
hash(value) {
|
|
137
135
|
return (0, node_crypto.createHash)("sha256").update(value).digest("hex").slice(0, 16);
|
|
@@ -343,7 +341,7 @@ function createPlanApi(options = {}) {
|
|
|
343
341
|
const pointers = options.pointers ?? new PlanPointerService();
|
|
344
342
|
const pointerFor = () => options.sessionId === void 0 ? void 0 : pointers.read(options.sessionId);
|
|
345
343
|
app.get(routes.current.path, async (context) => {
|
|
346
|
-
const pointer = pointerFor();
|
|
344
|
+
const pointer = await pointerFor();
|
|
347
345
|
if (pointer === void 0) return context.json({ error: NO_PLAN }, 404);
|
|
348
346
|
const view = {
|
|
349
347
|
path: pointer.path,
|
|
@@ -362,7 +360,7 @@ function createPlanApi(options = {}) {
|
|
|
362
360
|
return context.json({ error: "The save body is not JSON." }, 400);
|
|
363
361
|
}
|
|
364
362
|
if (typeof request !== "object" || request === null || Array.isArray(request) || !("content" in request) || typeof request.content !== "string" || !("expectedHash" in request) || typeof request.expectedHash !== "string") return context.json({ error: "A save carries the content to write and the hash it was read at." }, 400);
|
|
365
|
-
const pointer = pointerFor();
|
|
363
|
+
const pointer = await pointerFor();
|
|
366
364
|
if (pointer === void 0) return context.json({ error: NO_PLAN }, 404);
|
|
367
365
|
if (Buffer.byteLength(request.content, "utf8") > 1048576) return context.json({ error: `The plan is larger than ${MAX_PLAN_BYTES} bytes.` }, 413);
|
|
368
366
|
try {
|
|
@@ -482,10 +480,10 @@ function createPlanServerSession(host, options) {
|
|
|
482
480
|
* only on the Pi side: the group never appeared, and when it did its panel
|
|
483
481
|
* had no pointer to answer from.
|
|
484
482
|
*/
|
|
485
|
-
const announcePlanDocument = (filePath, title, writtenAt) => {
|
|
483
|
+
const announcePlanDocument = async (filePath, title, writtenAt) => {
|
|
486
484
|
const status = formatPlanStatus(title, planStampOf(writtenAt));
|
|
487
485
|
try {
|
|
488
|
-
pointers.write(host.context.sessionId, {
|
|
486
|
+
await pointers.write(host.context.sessionId, {
|
|
489
487
|
path: filePath,
|
|
490
488
|
title,
|
|
491
489
|
writtenAt
|
|
@@ -809,7 +807,7 @@ function createPlanServerSession(host, options) {
|
|
|
809
807
|
title,
|
|
810
808
|
writtenAt
|
|
811
809
|
});
|
|
812
|
-
announcePlanDocument(filePath, title, writtenAt);
|
|
810
|
+
await announcePlanDocument(filePath, title, writtenAt);
|
|
813
811
|
await selectPlan(true);
|
|
814
812
|
const saved = {
|
|
815
813
|
path: filePath,
|
|
@@ -897,7 +895,7 @@ function createPlanServerSession(host, options) {
|
|
|
897
895
|
event: "session_start",
|
|
898
896
|
async handle() {
|
|
899
897
|
await restorePlanState();
|
|
900
|
-
const pointer = pointers.read(host.context.sessionId);
|
|
898
|
+
const pointer = await pointers.read(host.context.sessionId);
|
|
901
899
|
if (pointer) host.context.client.setStatus(PLAN_STATUS_KEY, formatPlanStatus(pointer.title, planStampOf(pointer.writtenAt)));
|
|
902
900
|
if (!selected()) await restoreModel();
|
|
903
901
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.cjs","names":["Type","isRecord","os","path","piAgentDirectory","fs","createHash","fs","path","openGuardedFile","fileMutationRunner","createHash","createHash","Hono","path","resolvePlanningPlansDirectory","defineMinorMode","serverMinorModes","PO_CONFIG_SERVICE","readPackageResource","PO_VOICE_AUTO_MODE_ID","buildPlanModeBasePrompt","buildFlavorPlanningPrompt","Buffer","defineRoot","defineRoute","defineRoutedContribution","defineRoutedContribution","defineRoutedContribution","defineRoutedContribution","defineToolRestriction","PO_VOICE_AUTO_MODE_ID","defineToolRestriction","defineRoutedContribution","defineRoutedContribution","defineRoutedContribution","defineServerPlugin","composeServerProviderOverrides","rootSession","composeRootInjects","composeRootHooks","apiRoute","hookBeforeAgentStart","hookContext","hookSessionStart","resourcePoUsePlan","toolRestrictionPlan","toolRestrictionPlanReadOnly","toolCompletePlan","toolRecordDebugEvidence","toolWritePlan"],"sources":["../../src/schemas/mcpTools.ts","../../src/services/mcpTools/index.ts","../../src/services/planPointer/index.ts","../../src/types/apiRoutes.ts","../../src/types/planApi.ts","../../src/services/planFiles/index.ts","../../src/services/planApi/index.ts","../../src/services/optionalTeamServices/index.ts","../../src/services/planServerSession/index.ts","../../src/extensions/workspaces/sessions/(backend)/root.server.ts","../../src/extensions/workspaces/sessions/(backend)/api/plans/route.server.ts","../../src/extensions/workspaces/sessions/(backend)/hook/beforeAgentStart.server.ts","../../src/extensions/workspaces/sessions/(backend)/hook/context.server.ts","../../src/extensions/workspaces/sessions/(backend)/hook/sessionStart.server.ts","../../src/extensions/workspaces/sessions/(backend)/resource/poUsePlan.server.ts","../../src/extensions/workspaces/sessions/(backend)/tool-restriction/plan.server.ts","../../src/extensions/workspaces/sessions/(backend)/tool-restriction/planReadOnly.server.ts","../../src/extensions/workspaces/sessions/(backend)/tool/completePlan.server.ts","../../src/extensions/workspaces/sessions/(backend)/tool/recordDebugEvidence.server.ts","../../src/extensions/workspaces/sessions/(backend)/tool/writePlan.server.ts","../../generated/server.ts"],"sourcesContent":["import { Type } from 'typebox';\n\nconst markdown = Type.String({ minLength: 1 });\n\nexport const remoteWritePlanParameters = Type.Object({ markdown }, { additionalProperties: false });\nexport const writePlanParameters = Type.Object({ markdown: Type.Optional(markdown) }, { additionalProperties: false });\n","import type { PoHeadlessTool } from '@agimon-ai/po-core/headless';\nimport type { PoMcpPluginContext } from '@agimon-ai/po-core/mcpFacet';\nimport type { Context } from '@deepseek-ai/cordis';\n\nimport { remoteWritePlanParameters } from '../../schemas/mcpTools';\n\nexport const PLAN_MCP_TOOLS_SERVICE = 'po/plan-mcp-tools';\n\nconst MODE_ID = 'plan';\nconst MODE_LABEL = 'Plan';\n\nexport interface McpToolCatalog {\n get(name: string): PoHeadlessTool | undefined;\n names(): readonly string[];\n}\n\nexport function mountMcpTools(tools: readonly PoHeadlessTool[]) {\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: PoMcpPluginContext, name: string): PoHeadlessTool {\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 const writesPlan = name === 'write_plan';\n return {\n ...tool,\n ...(writesPlan\n ? {\n description:\n 'Save the supplied Markdown plan in the bound session. Requires active Plan mode. Saving does not approve implementation.',\n parameters: remoteWritePlanParameters,\n annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: false },\n }\n : {}),\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 if (\n writesPlan &&\n (typeof parameters !== 'object' ||\n parameters === null ||\n !('markdown' in parameters) ||\n typeof parameters.markdown !== 'string' ||\n !parameters.markdown.trim())\n ) {\n throw new Error(\n 'Supply the complete visible plan in the markdown argument. Remote calls cannot read the ChatGPT transcript.',\n );\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 { createHash } from 'node:crypto';\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\n\nimport { piAgentDirectory } from '@agimon-ai/po-core/piSettings';\n\nimport type { PlanPointerRecord } from '../../types/planApi';\nimport type { PlanPointerPort } from '../../types/planPointer';\n\n/**\n * Record and read where write_plan left this session's plan file.\n *\n * Pi already gives every session an agent directory, so the records live under\n * it rather than in a second place a reader would have to know about. The\n * posture matches the plan file itself, 0700 on the directory and 0600 on the\n * record: a plan is a private working document, not something to leave\n * world-readable in a shared home.\n */\n\nconst POINTER_DIRECTORY = 'po-plan';\nconst PRIVATE_DIRECTORY_MODE = 0o700;\nconst PRIVATE_FILE_MODE = 0o600;\n\nexport interface NodePlanPointerOptions {\n env?: NodeJS.ProcessEnv;\n homeDirectory?: string;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nexport class PlanPointerService implements PlanPointerPort {\n private readonly env: NodeJS.ProcessEnv;\n private readonly homeDirectory: string;\n\n constructor(options: NodePlanPointerOptions = {}) {\n this.env = options.env ?? {};\n this.homeDirectory = options.homeDirectory ?? os.homedir();\n }\n\n /** Where the records live; exposed so a test can look without guessing. */\n directory(): string {\n return path.join(piAgentDirectory(this.env, this.homeDirectory), POINTER_DIRECTORY);\n }\n\n /**\n * The record for one session. The id is hashed rather than used as written:\n * it reaches here from a host that may word it however it likes, and a\n * filename is not the place to find that out.\n */\n pathFor(sessionId: string): string {\n return path.join(this.directory(), `${this.hash(sessionId)}.json`);\n }\n\n read(sessionId: string): PlanPointerRecord | undefined {\n let raw: string;\n try {\n raw = fs.readFileSync(this.pathFor(sessionId), 'utf8');\n } catch {\n return undefined;\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n return undefined;\n }\n if (!isRecord(parsed)) return undefined;\n const { path: planPath, title, writtenAt, planId } = parsed;\n // A record half-written by an interrupted run reads as no plan at all,\n // which is a state the cockpit already handles.\n if (typeof planPath !== 'string' || !path.isAbsolute(planPath)) return undefined;\n if (typeof title !== 'string' || typeof writtenAt !== 'string') return undefined;\n return { path: planPath, title, writtenAt, ...(typeof planId === 'string' ? { planId } : {}) };\n }\n\n write(sessionId: string, record: PlanPointerRecord): void {\n fs.mkdirSync(this.directory(), { recursive: true, mode: PRIVATE_DIRECTORY_MODE });\n fs.writeFileSync(this.pathFor(sessionId), JSON.stringify(record), { mode: PRIVATE_FILE_MODE });\n }\n\n clear(sessionId: string): void {\n fs.rmSync(this.pathFor(sessionId), { force: true });\n }\n\n private hash(value: string): string {\n return createHash('sha256').update(value).digest('hex').slice(0, 16);\n }\n}\n","import type { ApiResponseOf, ApiRoutes } from '@agimon-ai/po-core/web';\n\nimport type { PlanDetailView, PlanSaveView } from './planApi';\n\n/**\n * This package's routes, as data.\n *\n * No scope and no base path. The build reads both off\n * `src/extensions/workspaces/sessions/(backend)/api/plans/`, so the mount is\n * stated once, by the folder that creates it. The paths used to be written\n * here as helpers, again in the contract, and again inside every URL the page\n * built, and the copies were free to disagree.\n *\n * This is the one file the routes, the Hono app and the browser all read, so a\n * path cannot move on one side alone.\n *\n * Neither route takes a query or a path parameter: a session has one current\n * plan, so `current` answers it and `save` takes the manual write back.\n */\nconst routes = {\n current: {\n method: 'GET',\n path: '/current',\n response: undefined as unknown as ApiResponseOf<PlanDetailView>,\n },\n save: {\n method: 'PUT',\n path: '/content',\n response: undefined as unknown as ApiResponseOf<PlanSaveView>,\n },\n} as const satisfies ApiRoutes;\n\nexport default routes;\n","/**\n * The wire vocabulary this package's API shares with whatever calls it.\n *\n * It lives under src/types because that is the one server root a browser\n * bundle may read: the routes answer with these shapes and the cockpit plugin\n * reads them, so neither half can drift from the other. The paths themselves\n * are not here; they are declared once in `./apiRoutes`, which the Hono app\n * and the generated client both read.\n *\n * Two routes, and neither takes a path. A session has one current plan, so\n * `current` answers it and `content` takes the manual save back; the file they\n * act on is the one write_plan recorded, never one the page named. That is\n * also why there is nothing to escape here: a reader cannot address a second\n * file through these URLs.\n */\n\n/** Where a host mounts this package's API; the segment after /api/plugins/. */\nexport const API_BASE_PATH = 'plans';\n\n/**\n * Footer status key the session publishes once a plan exists, which is what\n * makes the cockpit's activity group appear at all. It outlives plan mode: the\n * plan is most worth reading while the agent implements it.\n */\nexport const PLAN_STATUS_KEY = 'po-plan-document';\n\n/** The selector contract shared by complete_plan and its cockpit composer prompt. */\nexport const PLAN_REVIEW_TITLE = 'Plan complete. What would you like to do?';\nexport const EXIT_PLAN_MODE_CHOICE = 'Exit plan mode and start implementation';\nexport const CONTINUE_PLANNING_CHOICE = 'Continue planning';\nexport const EXIT_PLAN_DECISION = 'exit';\nexport const CONTINUE_PLAN_DECISION = 'continue';\n\n/**\n * The label the reader sees paired with the decision the tool acts on. A headless\n * client answers with `value`, so a server plugin never has to recognise the wording; Pi's\n * own `ui.select` carries labels only, so its half maps the label back itself.\n */\nexport const PLAN_REVIEW_CHOICES = [\n { label: EXIT_PLAN_MODE_CHOICE, value: EXIT_PLAN_DECISION },\n { label: CONTINUE_PLANNING_CHOICE, value: CONTINUE_PLAN_DECISION },\n] as const;\n\n/** The same options as bare labels, which is all Pi's select and the composer prompt need. */\nexport const PLAN_REVIEW_OPTIONS = PLAN_REVIEW_CHOICES.map((choice) => choice.label);\n\n/** Between the plan's title and the stamp that marks a rewrite. */\nconst STATUS_SEPARATOR = ' · ';\n\n/** Strips the colour a session may have themed a status with. */\nconst ANSI = /\u001b\\[[0-9;]*m/gu;\n\n/** The status line the session publishes; the stamp changes on every write. */\nexport function formatPlanStatus(title: string, stamp: string): string {\n return `${title}${STATUS_SEPARATOR}${stamp}`;\n}\n\n/** What a reader gets out of the status line. */\nexport interface PlanStatusView {\n title: string;\n /** When the plan was last written, as the session worded it; empty when it said nothing. */\n stamp: string;\n}\n\n/**\n * Reading the status line back. Statuses reach a plugin raw, so the colour a\n * session may have added comes off here. A title can itself contain the\n * separator, so the stamp is taken from the last one rather than the first.\n */\nexport function parsePlanStatus(raw: string | undefined): PlanStatusView | undefined {\n if (raw === undefined) return undefined;\n const text = raw.replace(ANSI, '').trim();\n if (text === '') return undefined;\n const cut = text.lastIndexOf(STATUS_SEPARATOR);\n if (cut === -1) return { title: text, stamp: '' };\n return { title: text.slice(0, cut).trim(), stamp: text.slice(cut + STATUS_SEPARATOR.length).trim() };\n}\n\n/**\n * What write_plan recorded about this session's plan.\n *\n * The absolute path is the point of it: the extension resolves the plans\n * directory against the harness root, which no other process knows, so it\n * writes down where the plan landed rather than leaving the API to derive a\n * location it would sometimes get wrong.\n */\nexport interface PlanPointerRecord {\n path: string;\n title: string;\n writtenAt: string;\n planId?: string;\n}\n\n/** The plan as it stands on disk right now, which is what the source view edits. */\nexport interface PlanDetailView {\n path: string;\n title: string;\n writtenAt: string;\n planId?: string;\n content: string;\n /** sha256 of the content; the token a save hands back to prove it is not stale. */\n hash: string;\n /** True when the plan is missing or past the size cap, so `content` is empty. */\n unavailable: boolean;\n reason?: string;\n}\n\n/** What the save route takes. */\nexport interface PlanSaveRequest {\n /** The hash the reader loaded; a mismatch means the plan moved underneath them. */\n expectedHash: string;\n content: string;\n}\n\n/** What the save route answers with. */\nexport interface PlanSaveView {\n hash: string;\n}\n\n/** What a route reports when it refuses; the page shows `error` verbatim. */\nexport interface PlanErrorView {\n error: string;\n /** On a stale save, the hash the plan actually has now. */\n hash?: string;\n}\n","import { createHash } from 'node:crypto';\nimport fs, { type FileHandle } from 'node:fs/promises';\nimport path from 'node:path';\n\nimport { fileMutationRunner, openGuardedFile } from '@agimon-ai/po-core/platform';\n\nexport async function writeNewPlanFile(directory: string, filename: string, content: string): Promise<string> {\n await fs.mkdir(directory, { recursive: true, mode: 0o700 });\n const planPath = path.join(directory, filename);\n await fs.writeFile(planPath, content, { encoding: 'utf8', flag: 'wx', mode: 0o600 });\n return planPath;\n}\n\nexport async function readPlanText(planPath: string): Promise<string> {\n return await fs.readFile(planPath, 'utf8');\n}\n\n/** The extra byte detects growth without ever reading or allocating the whole file. */\nasync function readBounded(handle: FileHandle, maxBytes: number): Promise<Buffer> {\n const stats = await handle.stat();\n if (!stats.isFile()) throw new Error('The plan is not a regular file.');\n const buffer = Buffer.alloc(maxBytes + 1);\n if (stats.size > maxBytes) return buffer;\n let offset = 0;\n while (offset < buffer.length) {\n const { bytesRead } = await handle.read(buffer, offset, buffer.length - offset, offset);\n if (bytesRead === 0) break;\n offset += bytesRead;\n }\n return buffer.subarray(0, offset);\n}\n\nexport async function readPlanBytes(planPath: string, maxBytes: number): Promise<Buffer> {\n const handle = await openGuardedFile(planPath);\n try {\n return await readBounded(handle, maxBytes);\n } finally {\n await handle.close();\n }\n}\n\n/** Compare and write the same open inode. The queue protects cooperating API writers, not external processes. */\nexport async function overwritePlanFile(\n planPath: string,\n content: string,\n expectedHash: string,\n maxBytes: number,\n): Promise<{ written: boolean; hash: string } | undefined> {\n const canonical = await fs.realpath(planPath);\n return await fileMutationRunner.forKey(canonical)(async () => {\n const handle = await openGuardedFile(planPath, { write: true });\n try {\n if ((await fs.realpath(planPath)) !== canonical) return undefined;\n const raw = await readBounded(handle, maxBytes);\n if (raw.length > maxBytes) return undefined;\n const hash = createHash('sha256').update(raw.toString('utf8'), 'utf8').digest('hex');\n if (hash !== expectedHash) return { written: false, hash };\n const opened = await handle.stat();\n const current = await fs.lstat(planPath);\n if (\n !opened.isFile() ||\n opened.nlink !== 1 ||\n !current.isFile() ||\n current.nlink !== 1 ||\n opened.dev !== current.dev ||\n opened.ino !== current.ino ||\n (await fs.realpath(planPath)) !== canonical\n )\n return undefined;\n await handle.writeFile(content, 'utf8');\n await handle.truncate(Buffer.byteLength(content, 'utf8'));\n return { written: true, hash: createHash('sha256').update(content, 'utf8').digest('hex') };\n } finally {\n await handle.close();\n }\n });\n}\n","import { createHash } from 'node:crypto';\n\nimport type { PoApi, PoApiContext, PoApiHandler } from '@agimon-ai/po-core/packageApi';\nimport { Hono } from 'hono';\n\nimport { PlanPointerService } from '../../services/planPointer';\nimport routes from '../../types/apiRoutes';\nimport { API_BASE_PATH, type PlanDetailView, type PlanSaveView } from '../../types/planApi';\nimport type { PlanPointerPort } from '../../types/planPointer';\nimport { overwritePlanFile, readPlanBytes } from '../planFiles';\n\n/**\n * This package's HTTP surface: the session's current plan, and the manual save.\n *\n * Each path comes from the route table the browser reads, so a route the page\n * calls and the route this serves cannot be spelled apart. Both are relative\n * to the mount its host gives it, so nothing here repeats where it was mounted. The host is one session's own server, which is what\n * makes the session id available without the page supplying it.\n *\n * Neither route takes a path. A session has exactly one current plan, and the\n * file both routes act on is the one the pointer names, so a page can address\n * the plan and nothing else; there is no traversal question to answer because\n * there is no path to traverse.\n *\n * The save refuses unless the reader proves they were looking at the content\n * that is still on disk. The agent may rewrite the plan while a tab is open,\n * and silently discarding whichever side finished second is the one outcome\n * worth ruling out.\n */\n\n/** Past this, the plan is reported rather than carried. */\nexport const MAX_PLAN_BYTES = 1024 * 1024;\n\nconst NO_PLAN = 'This session has not written a plan yet.';\nconst STALE_SAVE = 'The plan changed since it was opened.';\nconst NOT_A_FILE = 'The plan file is no longer a regular file, so it was not written.';\n\nexport interface PlanApiOptions {\n /** The session these routes answer for; absent when the host is the hub. */\n sessionId?: string;\n pointers?: PlanPointerPort;\n}\n\nfunction hashOf(content: string): string {\n return createHash('sha256').update(content, 'utf8').digest('hex');\n}\n\n/** Reads the plan as the source view will show it, or says why it cannot. */\nasync function readPlan(\n planPath: string,\n): Promise<Pick<PlanDetailView, 'content' | 'hash' | 'unavailable' | 'reason'>> {\n let raw: Buffer;\n try {\n raw = await readPlanBytes(planPath, MAX_PLAN_BYTES);\n } catch {\n return { content: '', hash: '', unavailable: true, reason: 'the plan file no longer exists' };\n }\n if (raw.byteLength > MAX_PLAN_BYTES) {\n return { content: '', hash: '', unavailable: true, reason: `the plan is larger than ${MAX_PLAN_BYTES} bytes` };\n }\n const content = raw.toString('utf8');\n return { content, hash: hashOf(content), unavailable: false };\n}\n\nexport function createPlanApi(options: PlanApiOptions = {}): Hono {\n const app = new Hono();\n const pointers = options.pointers ?? new PlanPointerService();\n const pointerFor = (): ReturnType<PlanPointerPort['read']> =>\n options.sessionId === undefined ? undefined : pointers.read(options.sessionId);\n\n app.get(routes.current.path, async (context) => {\n const pointer = pointerFor();\n if (pointer === undefined) return context.json({ error: NO_PLAN }, 404);\n const view: PlanDetailView = {\n path: pointer.path,\n title: pointer.title,\n writtenAt: pointer.writtenAt,\n ...(pointer.planId === undefined ? {} : { planId: pointer.planId }),\n ...(await readPlan(pointer.path)),\n };\n return context.json(view);\n });\n\n app.put(routes.save.path, async (context) => {\n let request: unknown;\n try {\n request = await context.req.json();\n } catch {\n return context.json({ error: 'The save body is not JSON.' }, 400);\n }\n if (\n typeof request !== 'object' ||\n request === null ||\n Array.isArray(request) ||\n !('content' in request) ||\n typeof request.content !== 'string' ||\n !('expectedHash' in request) ||\n typeof request.expectedHash !== 'string'\n ) {\n return context.json({ error: 'A save carries the content to write and the hash it was read at.' }, 400);\n }\n const pointer = pointerFor();\n if (pointer === undefined) return context.json({ error: NO_PLAN }, 404);\n\n if (Buffer.byteLength(request.content, 'utf8') > MAX_PLAN_BYTES) {\n return context.json({ error: `The plan is larger than ${MAX_PLAN_BYTES} bytes.` }, 413);\n }\n try {\n const saved = await overwritePlanFile(pointer.path, request.content, request.expectedHash, MAX_PLAN_BYTES);\n if (saved === undefined) return context.json({ error: NOT_A_FILE }, 409);\n if (!saved.written) return context.json({ error: STALE_SAVE, hash: saved.hash }, 409);\n return context.json({ hash: saved.hash } satisfies PlanSaveView);\n } catch {\n return context.json({ error: NOT_A_FILE }, 409);\n }\n });\n\n return app;\n}\n\n/** The named export a host imports from this package's built entry. */\nexport const api: PoApi = {\n basePath: API_BASE_PATH,\n start(context: PoApiContext): PoApiHandler {\n const app = createPlanApi({\n ...(context.sessionId === undefined ? {} : { sessionId: context.sessionId }),\n pointers: new PlanPointerService({\n env: context.environment ?? {},\n homeDirectory: context.homeDirectory,\n }),\n });\n return {\n fetch: (request) => app.fetch(request),\n // Nothing outlives a request here; an API that starts a watch or a timer\n // tears it down in this callback.\n close: () => undefined,\n };\n },\n};\n","import type { Context } from '@deepseek-ai/cordis';\n\nexport const PO_SUBAGENT_POLICY_SERVICE = 'po/subagent-policy';\n\nexport interface SubagentPolicy {\n owner: string;\n allowedTools?: string[];\n requiredTools?: string[];\n allowMcpTools?: boolean;\n denyExtensions?: boolean;\n}\n\nexport interface SubagentPolicyHandle {\n update(policy: SubagentPolicy): void;\n dispose(): void;\n}\n\nexport interface PoSubagentPolicyService {\n register(policy: SubagentPolicy): SubagentPolicyHandle;\n}\n\ntype StructuralServiceContext = {\n get(name: string): unknown;\n};\n\nexport function readPoSubagentPolicyService(ctx: Context): PoSubagentPolicyService | undefined {\n return (ctx as unknown as StructuralServiceContext).get(PO_SUBAGENT_POLICY_SERVICE) as\n | PoSubagentPolicyService\n | undefined;\n}\n","import { Buffer } from 'node:buffer';\nimport path from 'node:path';\n\nimport type { IPoConfigService } from '@agimon-ai/po-config';\nimport { PO_CONFIG_SERVICE } from '@agimon-ai/po-core/config';\nimport { type PoHeadlessHostService, type PoHeadlessModelSettings } from '@agimon-ai/po-core/headless';\nimport { type PoHeadlessToolResult } from '@agimon-ai/po-core/headless';\nimport { readPackageResource, type PoServerSessionPlugin } from '@agimon-ai/po-core/serverPlugin';\nimport { PO_VOICE_AUTO_MODE_ID } from '@agimon-ai/po-core/voiceTools';\nimport { serverMinorModes } from '@agimon-ai/po-minor-mode';\nimport { defineMinorMode, type MinorModeOwner, type MinorModeState } from '@agimon-ai/po-minor-mode';\nimport type { Context } from '@deepseek-ai/cordis';\n\nimport { writePlanParameters } from '../../schemas/mcpTools';\nimport { 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 PO_SUBAGENT_POLICY_SERVICE,\n readPoSubagentPolicyService,\n type SubagentPolicy,\n type SubagentPolicyHandle,\n} from '../optionalTeamServices';\nimport { readPlanText, writeNewPlanFile } from '../planFiles';\nimport { PlanPointerService } from '../planPointer';\n\nconst PLAN_ARTIFACT_SERVICE = 'po/artifacts';\ninterface PlanArtifactStorage {\n rootForSession(\n sessionId: string,\n environment: Readonly<Record<string, string | undefined>>,\n ): Promise<string | undefined>;\n}\n\nconst RECORD_DEBUG_EVIDENCE_TOOL = 'record_debug_evidence';\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/** This host supports UI review without narrated decisions. */\nexport const PLAN_SERVER_CAPABILITIES: PlanHostCapabilities = {\n completePlanTakesDecision: false,\n narratesReview: false,\n};\nconst PLAN_SUBAGENT_POLICY_OWNER = '@agimon-ai/po-plan';\n// Subagents keep every tool but `edit` and `write`, which the plan-mode restriction already withdraws from the\n// planner and, through it, from its native Team subagents.\nconst PLAN_SUBAGENT_POLICY: SubagentPolicy = {\n owner: PLAN_SUBAGENT_POLICY_OWNER,\n allowMcpTools: true,\n denyExtensions: false,\n};\n\nfunction serverPlanSubagentPolicy(host: PoHeadlessHostService): (context: Context) => void {\n return (context) => {\n context.inject([PO_SUBAGENT_POLICY_SERVICE], (child) => {\n const service = readPoSubagentPolicyService(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: PoHeadlessHostService): boolean {\n return (host.context.selection.state?.['minor-mode'] ?? []).includes(PLAN_MODE_ID);\n}\n\nfunction output(value: unknown, isError = false): PoHeadlessToolResult {\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: PoHeadlessHostService,\n options: { homeDirectory?: string },\n): Omit<PoServerSessionPlugin, 'tools'> & { tools: Parameters<PoHeadlessHostService['registerTool']>[0][] } {\n let flavor: PlanningFlavor = 'normal';\n let debugEvidence: DebugEvidencePacket | undefined;\n let modeOwner: MinorModeOwner | undefined;\n const selected = (): boolean => modeSelected(host);\n let configService: IPoConfigService | undefined;\n let artifactStorage: PlanArtifactStorage | undefined;\n const planningConfig = () => configService?.getSnapshot().settings.modes?.planning;\n // One resolution for both the prompt and write_plan.\n const plansDirectory = async (): Promise<string> => {\n const root = await artifactStorage?.rootForSession(host.context.sessionId, host.context.environment);\n if (root !== undefined) return path.join(root, 'plans');\n if (options.homeDirectory === undefined) throw new Error('Po needs a home directory to choose the plans folder.');\n return resolvePlanningPlansDirectory(\n planningConfig()?.plansDirectory,\n host.context.repoRoot,\n options.homeDirectory,\n );\n };\n const pointers = new PlanPointerService({\n env: host.context.environment,\n homeDirectory: options.homeDirectory,\n });\n /**\n * The activity group's line, and the pointer its panel reads.\n *\n * A cockpit drives this server plugin 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 const status = formatPlanStatus(title, planStampOf(writtenAt));\n try {\n pointers.write(host.context.sessionId, { path: filePath, title, writtenAt });\n host.context.client.setStatus(PLAN_STATUS_KEY, status);\n } catch (error) {\n // The plan is on disk either way, so say so in the status line rather than fail the write that produced it.\n const reason = error instanceof Error ? error.message : String(error);\n host.context.client.setStatus(PLAN_STATUS_KEY, `${status} (plan panel not updated: ${reason})`);\n }\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 PoHeadlessModelSettings);\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 = planningConfig()?.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<PoHeadlessModelSettings> = {};\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 === 'fable') flavor = 'normal';\n else if (candidate === 'normal' || candidate === 'debug' || candidate === 'code-review') 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/po-plan',\n id: PLAN_MODE_ID,\n label: 'Plan',\n description: 'Read-only planning with normal, debug, and code review 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: 'code-review', label: 'Code Review' },\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 !== 'code-review')\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: [\n serverMinorModes([modeOwner]),\n serverPlanSubagentPolicy(host),\n (context) => {\n context.inject([PO_CONFIG_SERVICE], (child) => {\n const service = child.get(PO_CONFIG_SERVICE) as IPoConfigService;\n configService = service;\n return () => {\n if (configService === service) configService = undefined;\n };\n });\n context.inject([PLAN_ARTIFACT_SERVICE], (child) => {\n const storage = child.get(PLAN_ARTIFACT_SERVICE) as PlanArtifactStorage;\n artifactStorage = storage;\n return () => {\n if (artifactStorage === storage) artifactStorage = undefined;\n };\n });\n },\n ],\n resources: [\n {\n when: { state: { 'minor-mode': PLAN_MODE_ID }, attribution: { kind: 'minor', mode: PLAN_MODE_ID } },\n name: 'po-use-plan',\n kind: 'skill',\n read: () => readPackageResource(import.meta.url, 'src/prompts/po-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: WRITE_PLAN_TOOL,\n label: 'Write Plan',\n mainAgentOnly: true,\n description: 'Save the supplied Markdown plan, or the plan already presented in the local session.',\n parameters: writePlanParameters,\n executionMode: 'serial',\n async execute(toolCallId, parameters, signal) {\n try {\n signal?.throwIfAborted();\n // Remote callers supply their own visible Markdown. Only local calls may use the journal.\n let content =\n typeof parameters === 'object' && parameters !== null && 'markdown' in parameters\n ? parameters.markdown\n : undefined;\n if (content !== undefined && typeof content !== 'string')\n throw new Error('Plan Markdown must be a string.');\n if (content === undefined) {\n for await (const entry of host.context.session.entriesNewestFirst({ type: 'message' })) {\n const message = entry.message as { role?: unknown; content?: unknown } | undefined;\n if (message?.role !== 'assistant' || !Array.isArray(message.content)) continue;\n if (\n !message.content.some((block: unknown) => {\n if (!block || typeof block !== 'object') return false;\n const call = block as { type?: unknown; id?: unknown; name?: unknown };\n return call.type === 'toolCall' && call.id === toolCallId && call.name === WRITE_PLAN_TOOL;\n })\n )\n continue;\n content = visiblePlanForToolCall([entry], toolCallId);\n break;\n }\n }\n if (content === undefined) {\n if (host.context.selection.state?.['minor-mode']?.includes(PO_VOICE_AUTO_MODE_ID))\n throw new Error('Supply Markdown to write_plan during autonomous Voice; UI input is unavailable.');\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 (typeof content !== 'string' || !content.trim())\n throw new Error('No implementation plan content was supplied.');\n const directory = await plansDirectory();\n const title = planTitleOf(content);\n const writtenAt = new Date().toISOString();\n const filename = `${planTitleSlug(content)}-${Date.now()}.md`;\n const filePath = await writeNewPlanFile(directory, filename, `${content.trim()}\\n`);\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 const saved = { path: filePath, written: true };\n return { ...output(saved), structuredContent: saved };\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 mainAgentOnly: true,\n description:\n 'Request exit-or-continue approval in the local Po UI. Requires the user to interact there; this is not a ChatGPT approval dialog.',\n // No decision parameter: this server plugin 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 if (host.context.selection.state?.['minor-mode']?.includes(PO_VOICE_AUTO_MODE_ID))\n throw new Error(\n 'UI plan review is unavailable during autonomous Voice. Wait for explicit user direction.',\n );\n // The client answers with the option's `value`, so the decision travels with the\n // question instead of the server plugin 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 ? planExitText(hasTaskTracking(host)) : 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 = await plansDirectory();\n const sections = [\n buildPlanModeBasePrompt(directory, PLAN_SERVER_CAPABILITIES),\n buildFlavorPlanningPrompt(flavor, directory, debugEvidence),\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 readPlanText(document.path);\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 // Older harness sessions persisted plan-mode context messages; keep them out of the model context.\n event: 'context',\n async handle(event) {\n if (!Array.isArray(event.messages)) return;\n return {\n messages: event.messages.filter(\n (message) => (message as { customType?: string }).customType !== PLAN_MODE_CONTEXT,\n ),\n };\n },\n },\n ],\n };\n}\n\nconst DEFAULT_PLAN_TITLE = 'implementation-plan';\nconst PLAN_TITLE_MAX_LENGTH = 64;\nconst DEBUG_EVIDENCE_MAX_BYTES = 32 * 1024;\nconst DEBUG_EVIDENCE_MAX_TEXT_BYTES = 4 * 1024;\nconst DEBUG_EVIDENCE_MAX_ITEMS = 32;\nexport const PLAN_MODE_CONTEXT = 'agent-harness-plan-mode-context';\nexport const PLAN_EXIT_APPROVED_TEXT =\n 'The user approved exiting plan mode. Full tool access is restored. Begin implementing the approved plan.';\nexport const PLAN_CONTINUE_TEXT = 'The user chose to continue planning. Remain read-only and refine the plan.';\nexport const PLAN_EXIT_TASK_TRACKING_TEXT =\n 'Track implementation progress with po-task: create one task per plan step (the `tasks` code-mode namespace, or the `task` tool without code mode), mark each in progress when you start it and done when it is verified.';\n\n/** po-task registers the `task` tool and its code-mode guide; either one means the session can track tasks. */\nfunction hasTaskTracking(host: PoHeadlessHostService): boolean {\n return host\n .inspectCapabilities()\n .capabilities.some(\n (capability) =>\n (capability.kind === 'tool' && capability.name === 'task') ||\n (capability.kind === 'skill' && capability.name === 'codemode-how-to-task'),\n );\n}\n\nexport function planExitText(taskTracking: boolean): string {\n return taskTracking ? `${PLAN_EXIT_APPROVED_TEXT} ${PLAN_EXIT_TASK_TRACKING_TEXT}` : PLAN_EXIT_APPROVED_TEXT;\n}\n/** The plan's own heading, as the status line and the pointer record word it. */\nexport function planTitleOf(markdown: string): string {\n return markdown.match(/^#\\s+(.+?)\\s*$/m)?.[1] ?? DEFAULT_PLAN_TITLE;\n}\n\nexport function planTitleSlug(markdown: string): string {\n const heading = planTitleOf(markdown);\n return (\n heading\n .normalize('NFKD')\n .replace(/[\\u0300-\\u036f]/g, '')\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '')\n .slice(0, PLAN_TITLE_MAX_LENGTH) || DEFAULT_PLAN_TITLE\n );\n}\n\n/**\n * How the status line words when a plan was written. A malformed timestamp is\n * shown as it came rather than as an invalid date: the stamp's other job is to\n * change on a rewrite, and it can still do that.\n */\nexport function planStampOf(writtenAt: string): string {\n const at = new Date(writtenAt);\n return Number.isNaN(at.getTime()) ? writtenAt : at.toLocaleTimeString();\n}\n\nexport function visiblePlanForToolCall(entries: readonly unknown[], toolCallId: string): string | undefined {\n for (let index = entries.length - 1; index >= 0; index -= 1) {\n const entry = entries[index];\n if (!entry || typeof entry !== 'object') continue;\n const candidate = entry as {\n type?: string;\n message?: { role?: string; content?: unknown };\n };\n if (candidate.type !== 'message' || candidate.message?.role !== 'assistant') continue;\n if (!Array.isArray(candidate.message.content)) continue;\n\n const toolIndex = candidate.message.content.findIndex((block) => {\n if (!block || typeof block !== 'object') return false;\n const content = block as { type?: string; id?: string; name?: string };\n return content.type === 'toolCall' && content.id === toolCallId && content.name === WRITE_PLAN_TOOL;\n });\n if (toolIndex < 0) continue;\n\n const plan = candidate.message.content\n .slice(0, toolIndex)\n .flatMap((block) => {\n if (!block || typeof block !== 'object') return [];\n const content = block as { type?: string; text?: unknown };\n return content.type === 'text' && typeof content.text === 'string' ? [content.text] : [];\n })\n .join('\\n\\n')\n .trim();\n return plan || undefined;\n }\n return undefined;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction isString(value: unknown): value is string {\n return typeof value === 'string';\n}\n\nfunction boundedDebugText(value: unknown, field: string, required: boolean): string {\n if (!isString(value)) throw new Error(`${field} must be a string.`);\n const text = value.trim();\n if (required && !text) throw new Error(`${field} must not be empty.`);\n if (Buffer.byteLength(text, 'utf8') > DEBUG_EVIDENCE_MAX_TEXT_BYTES) {\n throw new Error(`${field} exceeds the bounded debug evidence limit.`);\n }\n return text;\n}\n\nfunction boundedDebugList(value: unknown, field: string): string[] {\n if (!Array.isArray(value) || value.length > DEBUG_EVIDENCE_MAX_ITEMS) {\n throw new Error(`${field} must be a bounded string array.`);\n }\n return value.map((item, index) => boundedDebugText(item, `${field}[${index}]`, false));\n}\n\nfunction optionalDebugText(value: unknown, field: string): string {\n return value === undefined ? '' : boundedDebugText(value, field, false);\n}\n\nfunction optionalDebugList(value: unknown, field: string): string[] {\n return value === undefined ? [] : boundedDebugList(value, field);\n}\n\nfunction assertExactObjectKeys(value: Record<string, unknown>, keys: readonly string[], field: string): void {\n const allowed = new Set(keys);\n const unexpected = Object.keys(value).find((key) => !allowed.has(key));\n if (unexpected) throw new Error(`${field} contains unsupported field '${unexpected}'.`);\n}\n\nexport function parseDebugEvidencePacket(value: unknown): DebugEvidencePacket {\n if (!isRecord(value)) throw new Error('Debug evidence must be an object.');\n assertExactObjectKeys(\n value,\n [\n 'issue',\n 'expectedBehavior',\n 'reproductionAttempt',\n 'actualBehavior',\n 'logs',\n 'correlatedTraceEvidence',\n 'processOutput',\n 'browserConsoleEvidence',\n 'correlationIds',\n 'timestamps',\n 'verifiedFacts',\n 'hypotheses',\n 'unavailableEvidence',\n ],\n 'Debug evidence',\n );\n const packet: DebugEvidencePacket = {\n issue: boundedDebugText(value.issue, 'issue', true),\n expectedBehavior: optionalDebugText(value.expectedBehavior, 'expectedBehavior'),\n reproductionAttempt: optionalDebugText(value.reproductionAttempt, 'reproductionAttempt'),\n actualBehavior: optionalDebugText(value.actualBehavior, 'actualBehavior'),\n logs: optionalDebugList(value.logs, 'logs'),\n correlatedTraceEvidence: optionalDebugList(value.correlatedTraceEvidence, 'correlatedTraceEvidence'),\n processOutput: optionalDebugList(value.processOutput, 'processOutput'),\n browserConsoleEvidence: optionalDebugList(value.browserConsoleEvidence, 'browserConsoleEvidence'),\n correlationIds: optionalDebugList(value.correlationIds, 'correlationIds'),\n timestamps: optionalDebugList(value.timestamps, 'timestamps'),\n verifiedFacts: optionalDebugList(value.verifiedFacts, 'verifiedFacts'),\n hypotheses: optionalDebugList(value.hypotheses, 'hypotheses'),\n unavailableEvidence: optionalDebugList(value.unavailableEvidence, 'unavailableEvidence'),\n };\n if (Buffer.byteLength(JSON.stringify(packet), 'utf8') > DEBUG_EVIDENCE_MAX_BYTES) {\n throw new Error('Debug evidence exceeds the bounded packet size.');\n }\n return packet;\n}\n","import { defineRoot, type RootDeclaration } from '@agimon-ai/po-core/extensionFile';\nimport type { PoServerPluginContext, PoServerSessionPlugin } from '@agimon-ai/po-core/serverPlugin';\n\nimport { mountMcpTools } from '../../../../services/mcpTools';\nimport { api } from '../../../../services/planApi';\nimport { createPlanServerSession } from '../../../../services/planServerSession';\n\nexport default defineRoot(\n ({ agent, host }: PoServerPluginContext): RootDeclaration<PoServerSessionPlugin, PoServerPluginContext> => {\n const value = {\n api: [api],\n ...(agent\n ? createPlanServerSession(agent, {\n homeDirectory: host.context.homeDirectory ?? host.context.environment?.HOME,\n })\n : {}),\n };\n return {\n value,\n services: [...(value.services ?? []), ...(value.tools?.length ? [mountMcpTools(value.tools)] : [])],\n };\n },\n);\n","import { defineRoute, type WithRoot } from '@agimon-ai/po-core/extensionFile';\n\ntype Root = Awaited<ReturnType<typeof import('../../root.server').default>>['value'];\n\nexport default defineRoute((context: WithRoot<unknown, Root>) => context.root.api![0]!);\n","import { defineRoutedContribution, type WithRoot } from '@agimon-ai/po-core/extensionFile';\n\ntype Root = Awaited<ReturnType<typeof import('../root.server').default>>['value'];\n\nexport default defineRoutedContribution((context: WithRoot<unknown, Root>) => context.root.hooks?.[0], {\n cardinality: 'optional',\n});\n","import { defineRoutedContribution, type WithRoot } from '@agimon-ai/po-core/extensionFile';\n\ntype Root = Awaited<ReturnType<typeof import('../root.server').default>>['value'];\n\nexport default defineRoutedContribution((context: WithRoot<unknown, Root>) => context.root.hooks?.[2], {\n cardinality: 'optional',\n});\n","import { defineRoutedContribution, type WithRoot } from '@agimon-ai/po-core/extensionFile';\n\ntype Root = Awaited<ReturnType<typeof import('../root.server').default>>['value'];\n\nexport default defineRoutedContribution((context: WithRoot<unknown, Root>) => context.root.hooks?.[1], {\n cardinality: 'optional',\n});\n","import { defineRoutedContribution, type WithRoot } from '@agimon-ai/po-core/extensionFile';\n\ntype Root = Awaited<ReturnType<typeof import('../root.server').default>>['value'];\n\nexport default defineRoutedContribution((context: WithRoot<unknown, Root>) => context.root.resources?.[0], {\n cardinality: 'optional',\n});\n","import { defineToolRestriction } from '@agimon-ai/po-core/extensionFile';\nimport type { PoHeadlessToolRestriction } from '@agimon-ai/po-core/headless';\nimport { PO_VOICE_AUTO_MODE_ID } from '@agimon-ai/po-core/voiceTools';\n\nexport default defineToolRestriction({\n when: { state: { 'minor-mode': PO_VOICE_AUTO_MODE_ID } },\n excludedTools: ['complete_plan'],\n} satisfies PoHeadlessToolRestriction);\n","import { defineToolRestriction } from '@agimon-ai/po-core/extensionFile';\nimport type { PoHeadlessToolRestriction } from '@agimon-ai/po-core/headless';\n\nexport default defineToolRestriction({\n when: { state: { 'minor-mode': 'plan' } },\n excludedTools: ['edit', 'write'],\n} satisfies PoHeadlessToolRestriction);\n","import { defineRoutedContribution, type WithRoot } from '@agimon-ai/po-core/extensionFile';\n\ntype Root = Awaited<ReturnType<typeof import('../root.server').default>>['value'];\n\nexport default defineRoutedContribution((context: WithRoot<unknown, Root>) => context.root.tools?.[2], {\n cardinality: 'optional',\n});\n","import { defineRoutedContribution, type WithRoot } from '@agimon-ai/po-core/extensionFile';\n\ntype Root = Awaited<ReturnType<typeof import('../root.server').default>>['value'];\n\nexport default defineRoutedContribution((context: WithRoot<unknown, Root>) => context.root.tools?.[0], {\n cardinality: 'optional',\n});\n","import { defineRoutedContribution, type WithRoot } from '@agimon-ai/po-core/extensionFile';\n\ntype Root = Awaited<ReturnType<typeof import('../root.server').default>>['value'];\n\nexport default defineRoutedContribution((context: WithRoot<unknown, Root>) => context.root.tools?.[1], {\n cardinality: 'optional',\n});\n","// Generated by @agimon-ai/po-build. Do not edit by hand.\nimport { defineServerPlugin, type PoServerSessionPlugin } from '@agimon-ai/po-core/serverPlugin';\nimport { composeServerProviderOverrides } from '@agimon-ai/po-core/serverPlugin';\nimport { composeRootHooks, composeRootInjects } from '@agimon-ai/po-core/extensionFile';\n\nimport rootSession from '../src/extensions/workspaces/sessions/(backend)/root.server';\nimport apiRoute from '../src/extensions/workspaces/sessions/(backend)/api/plans/route.server';\nimport hookBeforeAgentStart from '../src/extensions/workspaces/sessions/(backend)/hook/beforeAgentStart.server';\nimport hookContext from '../src/extensions/workspaces/sessions/(backend)/hook/context.server';\nimport hookSessionStart from '../src/extensions/workspaces/sessions/(backend)/hook/sessionStart.server';\nimport resourcePoUsePlan from '../src/extensions/workspaces/sessions/(backend)/resource/poUsePlan.server';\nimport toolRestrictionPlan from '../src/extensions/workspaces/sessions/(backend)/tool-restriction/plan.server';\nimport toolRestrictionPlanReadOnly from '../src/extensions/workspaces/sessions/(backend)/tool-restriction/planReadOnly.server';\nimport toolCompletePlan from '../src/extensions/workspaces/sessions/(backend)/tool/completePlan.server';\nimport toolRecordDebugEvidence from '../src/extensions/workspaces/sessions/(backend)/tool/recordDebugEvidence.server';\nimport toolWritePlan from '../src/extensions/workspaces/sessions/(backend)/tool/writePlan.server';\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;\n/* v8 ignore next -- generated normalization branch */\nconst defined = <T>(values: readonly (T | undefined)[]): T[] =>\n values.filter((value): value is T => value !== undefined);\nconst via = <T>(identity: Record<string, unknown>, value: unknown): T =>\n (value === undefined ? undefined : { ...identity, ...(value as object) }) as T;\n\nexport const serverPlugin = defineServerPlugin({\n name: '@agimon-ai/po-plan',\n providerOverrides: composeServerProviderOverrides(rootSession),\n injects: composeRootInjects({ session: [rootSession] }),\n session: async (context) => {\n const scopeSession = await context.scopeRoot('session', rootSession, context);\n const contextSession = { ...context, root: scopeSession.value };\n return {\n ...composeRootHooks(scopeSession),\n get api(): PoServerSessionPlugin['api'] { return [at(apiRoute, contextSession)]; },\n get hooks(): PoServerSessionPlugin['hooks'] { return defined([via({ event: 'before_agent_start' }, at(hookBeforeAgentStart, contextSession)), via({ event: 'context' }, at(hookContext, contextSession)), via({ event: 'session_start' }, at(hookSessionStart, contextSession))]); },\n get resources(): PoServerSessionPlugin['resources'] { return defined([at(resourcePoUsePlan, contextSession)]); },\n get toolRestrictions(): PoServerSessionPlugin['toolRestrictions'] { return [at(toolRestrictionPlan, contextSession), at(toolRestrictionPlanReadOnly, contextSession)]; },\n get tools(): PoServerSessionPlugin['tools'] { return defined([via({ name: 'complete_plan' }, at(toolCompletePlan, contextSession)), via({ name: 'record_debug_evidence' }, at(toolRecordDebugEvidence, contextSession)), via({ name: 'write_plan' }, at(toolWritePlan, contextSession))]); },\n services: [...(scopeSession.services ?? [])],\n get activities(): PoServerSessionPlugin['activities'] { return [...(scopeSession.activities ?? [])]; },\n };\n },\n});\n\nexport default serverPlugin;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"server.cjs","names":["Type","isRecord","os","path","piAgentDirectory","fs","createHash","fs","path","openGuardedFile","fileMutationRunner","createHash","createHash","Hono","path","resolvePlanningPlansDirectory","defineMinorMode","serverMinorModes","PO_CONFIG_SERVICE","readPackageResource","PO_VOICE_AUTO_MODE_ID","buildPlanModeBasePrompt","buildFlavorPlanningPrompt","Buffer","defineRoot","defineRoute","defineRoutedContribution","defineRoutedContribution","defineRoutedContribution","defineRoutedContribution","defineToolRestriction","PO_VOICE_AUTO_MODE_ID","defineToolRestriction","defineRoutedContribution","defineRoutedContribution","defineRoutedContribution","defineServerPlugin","composeServerProviderOverrides","rootSession","composeRootInjects","composeRootHooks","apiRoute","hookBeforeAgentStart","hookContext","hookSessionStart","resourcePoUsePlan","toolRestrictionPlan","toolRestrictionPlanReadOnly","toolCompletePlan","toolRecordDebugEvidence","toolWritePlan"],"sources":["../../src/schemas/mcpTools.ts","../../src/services/mcpTools/index.ts","../../src/services/planPointer/index.ts","../../src/types/apiRoutes.ts","../../src/types/planApi.ts","../../src/services/planFiles/index.ts","../../src/services/planApi/index.ts","../../src/services/optionalTeamServices/index.ts","../../src/services/planServerSession/index.ts","../../src/extensions/workspaces/sessions/(backend)/root.server.ts","../../src/extensions/workspaces/sessions/(backend)/api/plans/route.server.ts","../../src/extensions/workspaces/sessions/(backend)/hook/beforeAgentStart.server.ts","../../src/extensions/workspaces/sessions/(backend)/hook/context.server.ts","../../src/extensions/workspaces/sessions/(backend)/hook/sessionStart.server.ts","../../src/extensions/workspaces/sessions/(backend)/resource/poUsePlan.server.ts","../../src/extensions/workspaces/sessions/(backend)/tool-restriction/plan.server.ts","../../src/extensions/workspaces/sessions/(backend)/tool-restriction/planReadOnly.server.ts","../../src/extensions/workspaces/sessions/(backend)/tool/completePlan.server.ts","../../src/extensions/workspaces/sessions/(backend)/tool/recordDebugEvidence.server.ts","../../src/extensions/workspaces/sessions/(backend)/tool/writePlan.server.ts","../../generated/server.ts"],"sourcesContent":["import { Type } from 'typebox';\n\nconst markdown = Type.String({ minLength: 1 });\n\nexport const remoteWritePlanParameters = Type.Object({ markdown }, { additionalProperties: false });\nexport const writePlanParameters = Type.Object({ markdown: Type.Optional(markdown) }, { additionalProperties: false });\n","import type { PoHeadlessTool } from '@agimon-ai/po-core/headless';\nimport type { PoMcpPluginContext } from '@agimon-ai/po-core/mcpFacet';\nimport type { Context } from '@deepseek-ai/cordis';\n\nimport { remoteWritePlanParameters } from '../../schemas/mcpTools';\n\nexport const PLAN_MCP_TOOLS_SERVICE = 'po/plan-mcp-tools';\n\nconst MODE_ID = 'plan';\nconst MODE_LABEL = 'Plan';\n\nexport interface McpToolCatalog {\n get(name: string): PoHeadlessTool | undefined;\n names(): readonly string[];\n}\n\nexport function mountMcpTools(tools: readonly PoHeadlessTool[]) {\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: PoMcpPluginContext, name: string): PoHeadlessTool {\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 const writesPlan = name === 'write_plan';\n return {\n ...tool,\n ...(writesPlan\n ? {\n description:\n 'Save the supplied Markdown plan in the bound session. Requires active Plan mode. Saving does not approve implementation.',\n parameters: remoteWritePlanParameters,\n annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: false },\n }\n : {}),\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 if (\n writesPlan &&\n (typeof parameters !== 'object' ||\n parameters === null ||\n !('markdown' in parameters) ||\n typeof parameters.markdown !== 'string' ||\n !parameters.markdown.trim())\n ) {\n throw new Error(\n 'Supply the complete visible plan in the markdown argument. Remote calls cannot read the ChatGPT transcript.',\n );\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 { createHash } from 'node:crypto';\nimport fs from 'node:fs/promises';\nimport os from 'node:os';\nimport path from 'node:path';\n\nimport { piAgentDirectory } from '@agimon-ai/po-core/piSettings';\n\nimport type { PlanPointerRecord } from '../../types/planApi';\nimport type { PlanPointerPort } from '../../types/planPointer';\n\n/**\n * Record and read where write_plan left this session's plan file.\n *\n * Pi already gives every session an agent directory, so the records live under\n * it rather than in a second place a reader would have to know about. The\n * posture matches the plan file itself, 0700 on the directory and 0600 on the\n * record: a plan is a private working document, not something to leave\n * world-readable in a shared home.\n */\n\nconst POINTER_DIRECTORY = 'po-plan';\nconst PRIVATE_DIRECTORY_MODE = 0o700;\nconst PRIVATE_FILE_MODE = 0o600;\n\nexport interface NodePlanPointerOptions {\n env?: NodeJS.ProcessEnv;\n homeDirectory?: string;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nexport class PlanPointerService implements PlanPointerPort {\n private readonly env: NodeJS.ProcessEnv;\n private readonly homeDirectory: string;\n\n constructor(options: NodePlanPointerOptions = {}) {\n this.env = options.env ?? {};\n this.homeDirectory = options.homeDirectory ?? os.homedir();\n }\n\n /** Where the records live; exposed so a test can look without guessing. */\n directory(): string {\n return path.join(piAgentDirectory(this.env, this.homeDirectory), POINTER_DIRECTORY);\n }\n\n /**\n * The record for one session. The id is hashed rather than used as written:\n * it reaches here from a host that may word it however it likes, and a\n * filename is not the place to find that out.\n */\n pathFor(sessionId: string): string {\n return path.join(this.directory(), `${this.hash(sessionId)}.json`);\n }\n\n async read(sessionId: string): Promise<PlanPointerRecord | undefined> {\n let raw: string;\n try {\n raw = await fs.readFile(this.pathFor(sessionId), 'utf8');\n } catch {\n return undefined;\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n return undefined;\n }\n if (!isRecord(parsed)) return undefined;\n const { path: planPath, title, writtenAt, planId } = parsed;\n // A record half-written by an interrupted run reads as no plan at all,\n // which is a state the cockpit already handles.\n if (typeof planPath !== 'string' || !path.isAbsolute(planPath)) return undefined;\n if (typeof title !== 'string' || typeof writtenAt !== 'string') return undefined;\n return { path: planPath, title, writtenAt, ...(typeof planId === 'string' ? { planId } : {}) };\n }\n\n async write(sessionId: string, record: PlanPointerRecord): Promise<void> {\n await fs.mkdir(this.directory(), { recursive: true, mode: PRIVATE_DIRECTORY_MODE });\n await fs.writeFile(this.pathFor(sessionId), JSON.stringify(record), { mode: PRIVATE_FILE_MODE });\n }\n\n async clear(sessionId: string): Promise<void> {\n await fs.rm(this.pathFor(sessionId), { force: true });\n }\n\n private hash(value: string): string {\n return createHash('sha256').update(value).digest('hex').slice(0, 16);\n }\n}\n","import type { ApiResponseOf, ApiRoutes } from '@agimon-ai/po-core/web';\n\nimport type { PlanDetailView, PlanSaveView } from './planApi';\n\n/**\n * This package's routes, as data.\n *\n * No scope and no base path. The build reads both off\n * `src/extensions/workspaces/sessions/(backend)/api/plans/`, so the mount is\n * stated once, by the folder that creates it. The paths used to be written\n * here as helpers, again in the contract, and again inside every URL the page\n * built, and the copies were free to disagree.\n *\n * This is the one file the routes, the Hono app and the browser all read, so a\n * path cannot move on one side alone.\n *\n * Neither route takes a query or a path parameter: a session has one current\n * plan, so `current` answers it and `save` takes the manual write back.\n */\nconst routes = {\n current: {\n method: 'GET',\n path: '/current',\n response: undefined as unknown as ApiResponseOf<PlanDetailView>,\n },\n save: {\n method: 'PUT',\n path: '/content',\n response: undefined as unknown as ApiResponseOf<PlanSaveView>,\n },\n} as const satisfies ApiRoutes;\n\nexport default routes;\n","/**\n * The wire vocabulary this package's API shares with whatever calls it.\n *\n * It lives under src/types because that is the one server root a browser\n * bundle may read: the routes answer with these shapes and the cockpit plugin\n * reads them, so neither half can drift from the other. The paths themselves\n * are not here; they are declared once in `./apiRoutes`, which the Hono app\n * and the generated client both read.\n *\n * Two routes, and neither takes a path. A session has one current plan, so\n * `current` answers it and `content` takes the manual save back; the file they\n * act on is the one write_plan recorded, never one the page named. That is\n * also why there is nothing to escape here: a reader cannot address a second\n * file through these URLs.\n */\n\n/** Where a host mounts this package's API; the segment after /api/plugins/. */\nexport const API_BASE_PATH = 'plans';\n\n/**\n * Footer status key the session publishes once a plan exists, which is what\n * makes the cockpit's activity group appear at all. It outlives plan mode: the\n * plan is most worth reading while the agent implements it.\n */\nexport const PLAN_STATUS_KEY = 'po-plan-document';\n\n/** The selector contract shared by complete_plan and its cockpit composer prompt. */\nexport const PLAN_REVIEW_TITLE = 'Plan complete. What would you like to do?';\nexport const EXIT_PLAN_MODE_CHOICE = 'Exit plan mode and start implementation';\nexport const CONTINUE_PLANNING_CHOICE = 'Continue planning';\nexport const EXIT_PLAN_DECISION = 'exit';\nexport const CONTINUE_PLAN_DECISION = 'continue';\n\n/**\n * The label the reader sees paired with the decision the tool acts on. A headless\n * client answers with `value`, so a server plugin never has to recognise the wording; Pi's\n * own `ui.select` carries labels only, so its half maps the label back itself.\n */\nexport const PLAN_REVIEW_CHOICES = [\n { label: EXIT_PLAN_MODE_CHOICE, value: EXIT_PLAN_DECISION },\n { label: CONTINUE_PLANNING_CHOICE, value: CONTINUE_PLAN_DECISION },\n] as const;\n\n/** The same options as bare labels, which is all Pi's select and the composer prompt need. */\nexport const PLAN_REVIEW_OPTIONS = PLAN_REVIEW_CHOICES.map((choice) => choice.label);\n\n/** Between the plan's title and the stamp that marks a rewrite. */\nconst STATUS_SEPARATOR = ' · ';\n\n/** Strips the colour a session may have themed a status with. */\nconst ANSI = /\u001b\\[[0-9;]*m/gu;\n\n/** The status line the session publishes; the stamp changes on every write. */\nexport function formatPlanStatus(title: string, stamp: string): string {\n return `${title}${STATUS_SEPARATOR}${stamp}`;\n}\n\n/** What a reader gets out of the status line. */\nexport interface PlanStatusView {\n title: string;\n /** When the plan was last written, as the session worded it; empty when it said nothing. */\n stamp: string;\n}\n\n/**\n * Reading the status line back. Statuses reach a plugin raw, so the colour a\n * session may have added comes off here. A title can itself contain the\n * separator, so the stamp is taken from the last one rather than the first.\n */\nexport function parsePlanStatus(raw: string | undefined): PlanStatusView | undefined {\n if (raw === undefined) return undefined;\n const text = raw.replace(ANSI, '').trim();\n if (text === '') return undefined;\n const cut = text.lastIndexOf(STATUS_SEPARATOR);\n if (cut === -1) return { title: text, stamp: '' };\n return { title: text.slice(0, cut).trim(), stamp: text.slice(cut + STATUS_SEPARATOR.length).trim() };\n}\n\n/**\n * What write_plan recorded about this session's plan.\n *\n * The absolute path is the point of it: the extension resolves the plans\n * directory against the harness root, which no other process knows, so it\n * writes down where the plan landed rather than leaving the API to derive a\n * location it would sometimes get wrong.\n */\nexport interface PlanPointerRecord {\n path: string;\n title: string;\n writtenAt: string;\n planId?: string;\n}\n\n/** The plan as it stands on disk right now, which is what the source view edits. */\nexport interface PlanDetailView {\n path: string;\n title: string;\n writtenAt: string;\n planId?: string;\n content: string;\n /** sha256 of the content; the token a save hands back to prove it is not stale. */\n hash: string;\n /** True when the plan is missing or past the size cap, so `content` is empty. */\n unavailable: boolean;\n reason?: string;\n}\n\n/** What the save route takes. */\nexport interface PlanSaveRequest {\n /** The hash the reader loaded; a mismatch means the plan moved underneath them. */\n expectedHash: string;\n content: string;\n}\n\n/** What the save route answers with. */\nexport interface PlanSaveView {\n hash: string;\n}\n\n/** What a route reports when it refuses; the page shows `error` verbatim. */\nexport interface PlanErrorView {\n error: string;\n /** On a stale save, the hash the plan actually has now. */\n hash?: string;\n}\n","import { createHash } from 'node:crypto';\nimport fs, { type FileHandle } from 'node:fs/promises';\nimport path from 'node:path';\n\nimport { fileMutationRunner, openGuardedFile } from '@agimon-ai/po-core/platform';\n\nexport async function writeNewPlanFile(directory: string, filename: string, content: string): Promise<string> {\n await fs.mkdir(directory, { recursive: true, mode: 0o700 });\n const planPath = path.join(directory, filename);\n await fs.writeFile(planPath, content, { encoding: 'utf8', flag: 'wx', mode: 0o600 });\n return planPath;\n}\n\nexport async function readPlanText(planPath: string): Promise<string> {\n return await fs.readFile(planPath, 'utf8');\n}\n\n/** The extra byte detects growth without ever reading or allocating the whole file. */\nasync function readBounded(handle: FileHandle, maxBytes: number): Promise<Buffer> {\n const stats = await handle.stat();\n if (!stats.isFile()) throw new Error('The plan is not a regular file.');\n const buffer = Buffer.alloc(maxBytes + 1);\n if (stats.size > maxBytes) return buffer;\n let offset = 0;\n while (offset < buffer.length) {\n const { bytesRead } = await handle.read(buffer, offset, buffer.length - offset, offset);\n if (bytesRead === 0) break;\n offset += bytesRead;\n }\n return buffer.subarray(0, offset);\n}\n\nexport async function readPlanBytes(planPath: string, maxBytes: number): Promise<Buffer> {\n const handle = await openGuardedFile(planPath);\n try {\n return await readBounded(handle, maxBytes);\n } finally {\n await handle.close();\n }\n}\n\n/** Compare and write the same open inode. The queue protects cooperating API writers, not external processes. */\nexport async function overwritePlanFile(\n planPath: string,\n content: string,\n expectedHash: string,\n maxBytes: number,\n): Promise<{ written: boolean; hash: string } | undefined> {\n const canonical = await fs.realpath(planPath);\n return await fileMutationRunner.forKey(canonical)(async () => {\n const handle = await openGuardedFile(planPath, { write: true });\n try {\n if ((await fs.realpath(planPath)) !== canonical) return undefined;\n const raw = await readBounded(handle, maxBytes);\n if (raw.length > maxBytes) return undefined;\n const hash = createHash('sha256').update(raw.toString('utf8'), 'utf8').digest('hex');\n if (hash !== expectedHash) return { written: false, hash };\n const opened = await handle.stat();\n const current = await fs.lstat(planPath);\n if (\n !opened.isFile() ||\n opened.nlink !== 1 ||\n !current.isFile() ||\n current.nlink !== 1 ||\n opened.dev !== current.dev ||\n opened.ino !== current.ino ||\n (await fs.realpath(planPath)) !== canonical\n )\n return undefined;\n await handle.writeFile(content, 'utf8');\n await handle.truncate(Buffer.byteLength(content, 'utf8'));\n return { written: true, hash: createHash('sha256').update(content, 'utf8').digest('hex') };\n } finally {\n await handle.close();\n }\n });\n}\n","import { createHash } from 'node:crypto';\n\nimport type { PoApi, PoApiContext, PoApiHandler } from '@agimon-ai/po-core/packageApi';\nimport { Hono } from 'hono';\n\nimport { PlanPointerService } from '../../services/planPointer';\nimport routes from '../../types/apiRoutes';\nimport { API_BASE_PATH, type PlanDetailView, type PlanSaveView } from '../../types/planApi';\nimport type { PlanPointerPort } from '../../types/planPointer';\nimport { overwritePlanFile, readPlanBytes } from '../planFiles';\n\n/**\n * This package's HTTP surface: the session's current plan, and the manual save.\n *\n * Each path comes from the route table the browser reads, so a route the page\n * calls and the route this serves cannot be spelled apart. Both are relative\n * to the mount its host gives it, so nothing here repeats where it was mounted. The host is one session's own server, which is what\n * makes the session id available without the page supplying it.\n *\n * Neither route takes a path. A session has exactly one current plan, and the\n * file both routes act on is the one the pointer names, so a page can address\n * the plan and nothing else; there is no traversal question to answer because\n * there is no path to traverse.\n *\n * The save refuses unless the reader proves they were looking at the content\n * that is still on disk. The agent may rewrite the plan while a tab is open,\n * and silently discarding whichever side finished second is the one outcome\n * worth ruling out.\n */\n\n/** Past this, the plan is reported rather than carried. */\nexport const MAX_PLAN_BYTES = 1024 * 1024;\n\nconst NO_PLAN = 'This session has not written a plan yet.';\nconst STALE_SAVE = 'The plan changed since it was opened.';\nconst NOT_A_FILE = 'The plan file is no longer a regular file, so it was not written.';\n\nexport interface PlanApiOptions {\n /** The session these routes answer for; absent when the host is the hub. */\n sessionId?: string;\n pointers?: PlanPointerPort;\n}\n\nfunction hashOf(content: string): string {\n return createHash('sha256').update(content, 'utf8').digest('hex');\n}\n\n/** Reads the plan as the source view will show it, or says why it cannot. */\nasync function readPlan(\n planPath: string,\n): Promise<Pick<PlanDetailView, 'content' | 'hash' | 'unavailable' | 'reason'>> {\n let raw: Buffer;\n try {\n raw = await readPlanBytes(planPath, MAX_PLAN_BYTES);\n } catch {\n return { content: '', hash: '', unavailable: true, reason: 'the plan file no longer exists' };\n }\n if (raw.byteLength > MAX_PLAN_BYTES) {\n return { content: '', hash: '', unavailable: true, reason: `the plan is larger than ${MAX_PLAN_BYTES} bytes` };\n }\n const content = raw.toString('utf8');\n return { content, hash: hashOf(content), unavailable: false };\n}\n\nexport function createPlanApi(options: PlanApiOptions = {}): Hono {\n const app = new Hono();\n const pointers = options.pointers ?? new PlanPointerService();\n const pointerFor = (): ReturnType<PlanPointerPort['read']> =>\n options.sessionId === undefined ? undefined : pointers.read(options.sessionId);\n\n app.get(routes.current.path, async (context) => {\n const pointer = await pointerFor();\n if (pointer === undefined) return context.json({ error: NO_PLAN }, 404);\n const view: PlanDetailView = {\n path: pointer.path,\n title: pointer.title,\n writtenAt: pointer.writtenAt,\n ...(pointer.planId === undefined ? {} : { planId: pointer.planId }),\n ...(await readPlan(pointer.path)),\n };\n return context.json(view);\n });\n\n app.put(routes.save.path, async (context) => {\n let request: unknown;\n try {\n request = await context.req.json();\n } catch {\n return context.json({ error: 'The save body is not JSON.' }, 400);\n }\n if (\n typeof request !== 'object' ||\n request === null ||\n Array.isArray(request) ||\n !('content' in request) ||\n typeof request.content !== 'string' ||\n !('expectedHash' in request) ||\n typeof request.expectedHash !== 'string'\n ) {\n return context.json({ error: 'A save carries the content to write and the hash it was read at.' }, 400);\n }\n const pointer = await pointerFor();\n if (pointer === undefined) return context.json({ error: NO_PLAN }, 404);\n\n if (Buffer.byteLength(request.content, 'utf8') > MAX_PLAN_BYTES) {\n return context.json({ error: `The plan is larger than ${MAX_PLAN_BYTES} bytes.` }, 413);\n }\n try {\n const saved = await overwritePlanFile(pointer.path, request.content, request.expectedHash, MAX_PLAN_BYTES);\n if (saved === undefined) return context.json({ error: NOT_A_FILE }, 409);\n if (!saved.written) return context.json({ error: STALE_SAVE, hash: saved.hash }, 409);\n return context.json({ hash: saved.hash } satisfies PlanSaveView);\n } catch {\n return context.json({ error: NOT_A_FILE }, 409);\n }\n });\n\n return app;\n}\n\n/** The named export a host imports from this package's built entry. */\nexport const api: PoApi = {\n basePath: API_BASE_PATH,\n start(context: PoApiContext): PoApiHandler {\n const app = createPlanApi({\n ...(context.sessionId === undefined ? {} : { sessionId: context.sessionId }),\n pointers: new PlanPointerService({\n env: context.environment ?? {},\n homeDirectory: context.homeDirectory,\n }),\n });\n return {\n fetch: (request) => app.fetch(request),\n // Nothing outlives a request here; an API that starts a watch or a timer\n // tears it down in this callback.\n close: () => undefined,\n };\n },\n};\n","import type { Context } from '@deepseek-ai/cordis';\n\nexport const PO_SUBAGENT_POLICY_SERVICE = 'po/subagent-policy';\n\nexport interface SubagentPolicy {\n owner: string;\n allowedTools?: string[];\n requiredTools?: string[];\n allowMcpTools?: boolean;\n denyExtensions?: boolean;\n}\n\nexport interface SubagentPolicyHandle {\n update(policy: SubagentPolicy): void;\n dispose(): void;\n}\n\nexport interface PoSubagentPolicyService {\n register(policy: SubagentPolicy): SubagentPolicyHandle;\n}\n\ntype StructuralServiceContext = {\n get(name: string): unknown;\n};\n\nexport function readPoSubagentPolicyService(ctx: Context): PoSubagentPolicyService | undefined {\n return (ctx as unknown as StructuralServiceContext).get(PO_SUBAGENT_POLICY_SERVICE) as\n | PoSubagentPolicyService\n | undefined;\n}\n","import { Buffer } from 'node:buffer';\nimport path from 'node:path';\n\nimport type { IPoConfigService } from '@agimon-ai/po-config';\nimport { PO_CONFIG_SERVICE } from '@agimon-ai/po-core/config';\nimport { type PoHeadlessHostService, type PoHeadlessModelSettings } from '@agimon-ai/po-core/headless';\nimport { type PoHeadlessToolResult } from '@agimon-ai/po-core/headless';\nimport { readPackageResource, type PoServerSessionPlugin } from '@agimon-ai/po-core/serverPlugin';\nimport { PO_VOICE_AUTO_MODE_ID } from '@agimon-ai/po-core/voiceTools';\nimport { serverMinorModes } from '@agimon-ai/po-minor-mode';\nimport { defineMinorMode, type MinorModeOwner, type MinorModeState } from '@agimon-ai/po-minor-mode';\nimport type { Context } from '@deepseek-ai/cordis';\n\nimport { writePlanParameters } from '../../schemas/mcpTools';\nimport { 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 PO_SUBAGENT_POLICY_SERVICE,\n readPoSubagentPolicyService,\n type SubagentPolicy,\n type SubagentPolicyHandle,\n} from '../optionalTeamServices';\nimport { readPlanText, writeNewPlanFile } from '../planFiles';\nimport { PlanPointerService } from '../planPointer';\n\nconst PLAN_ARTIFACT_SERVICE = 'po/artifacts';\ninterface PlanArtifactStorage {\n rootForSession(\n sessionId: string,\n environment: Readonly<Record<string, string | undefined>>,\n ): Promise<string | undefined>;\n}\n\nconst RECORD_DEBUG_EVIDENCE_TOOL = 'record_debug_evidence';\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/** This host supports UI review without narrated decisions. */\nexport const PLAN_SERVER_CAPABILITIES: PlanHostCapabilities = {\n completePlanTakesDecision: false,\n narratesReview: false,\n};\nconst PLAN_SUBAGENT_POLICY_OWNER = '@agimon-ai/po-plan';\n// Subagents keep every tool but `edit` and `write`, which the plan-mode restriction already withdraws from the\n// planner and, through it, from its native Team subagents.\nconst PLAN_SUBAGENT_POLICY: SubagentPolicy = {\n owner: PLAN_SUBAGENT_POLICY_OWNER,\n allowMcpTools: true,\n denyExtensions: false,\n};\n\nfunction serverPlanSubagentPolicy(host: PoHeadlessHostService): (context: Context) => void {\n return (context) => {\n context.inject([PO_SUBAGENT_POLICY_SERVICE], (child) => {\n const service = readPoSubagentPolicyService(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: PoHeadlessHostService): boolean {\n return (host.context.selection.state?.['minor-mode'] ?? []).includes(PLAN_MODE_ID);\n}\n\nfunction output(value: unknown, isError = false): PoHeadlessToolResult {\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: PoHeadlessHostService,\n options: { homeDirectory?: string },\n): Omit<PoServerSessionPlugin, 'tools'> & { tools: Parameters<PoHeadlessHostService['registerTool']>[0][] } {\n let flavor: PlanningFlavor = 'normal';\n let debugEvidence: DebugEvidencePacket | undefined;\n let modeOwner: MinorModeOwner | undefined;\n const selected = (): boolean => modeSelected(host);\n let configService: IPoConfigService | undefined;\n let artifactStorage: PlanArtifactStorage | undefined;\n const planningConfig = () => configService?.getSnapshot().settings.modes?.planning;\n // One resolution for both the prompt and write_plan.\n const plansDirectory = async (): Promise<string> => {\n const root = await artifactStorage?.rootForSession(host.context.sessionId, host.context.environment);\n if (root !== undefined) return path.join(root, 'plans');\n if (options.homeDirectory === undefined) throw new Error('Po needs a home directory to choose the plans folder.');\n return resolvePlanningPlansDirectory(\n planningConfig()?.plansDirectory,\n host.context.repoRoot,\n options.homeDirectory,\n );\n };\n const pointers = new PlanPointerService({\n env: host.context.environment,\n homeDirectory: options.homeDirectory,\n });\n /**\n * The activity group's line, and the pointer its panel reads.\n *\n * A cockpit drives this server plugin 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 = async (filePath: string, title: string, writtenAt: string): Promise<void> => {\n const status = formatPlanStatus(title, planStampOf(writtenAt));\n try {\n await pointers.write(host.context.sessionId, { path: filePath, title, writtenAt });\n host.context.client.setStatus(PLAN_STATUS_KEY, status);\n } catch (error) {\n // The plan is on disk either way, so say so in the status line rather than fail the write that produced it.\n const reason = error instanceof Error ? error.message : String(error);\n host.context.client.setStatus(PLAN_STATUS_KEY, `${status} (plan panel not updated: ${reason})`);\n }\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 PoHeadlessModelSettings);\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 = planningConfig()?.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<PoHeadlessModelSettings> = {};\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 === 'fable') flavor = 'normal';\n else if (candidate === 'normal' || candidate === 'debug' || candidate === 'code-review') 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/po-plan',\n id: PLAN_MODE_ID,\n label: 'Plan',\n description: 'Read-only planning with normal, debug, and code review 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: 'code-review', label: 'Code Review' },\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 !== 'code-review')\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: [\n serverMinorModes([modeOwner]),\n serverPlanSubagentPolicy(host),\n (context) => {\n context.inject([PO_CONFIG_SERVICE], (child) => {\n const service = child.get(PO_CONFIG_SERVICE) as IPoConfigService;\n configService = service;\n return () => {\n if (configService === service) configService = undefined;\n };\n });\n context.inject([PLAN_ARTIFACT_SERVICE], (child) => {\n const storage = child.get(PLAN_ARTIFACT_SERVICE) as PlanArtifactStorage;\n artifactStorage = storage;\n return () => {\n if (artifactStorage === storage) artifactStorage = undefined;\n };\n });\n },\n ],\n resources: [\n {\n when: { state: { 'minor-mode': PLAN_MODE_ID }, attribution: { kind: 'minor', mode: PLAN_MODE_ID } },\n name: 'po-use-plan',\n kind: 'skill',\n read: () => readPackageResource(import.meta.url, 'src/prompts/po-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: WRITE_PLAN_TOOL,\n label: 'Write Plan',\n mainAgentOnly: true,\n description: 'Save the supplied Markdown plan, or the plan already presented in the local session.',\n parameters: writePlanParameters,\n executionMode: 'serial',\n async execute(toolCallId, parameters, signal) {\n try {\n signal?.throwIfAborted();\n // Remote callers supply their own visible Markdown. Only local calls may use the journal.\n let content =\n typeof parameters === 'object' && parameters !== null && 'markdown' in parameters\n ? parameters.markdown\n : undefined;\n if (content !== undefined && typeof content !== 'string')\n throw new Error('Plan Markdown must be a string.');\n if (content === undefined) {\n for await (const entry of host.context.session.entriesNewestFirst({ type: 'message' })) {\n const message = entry.message as { role?: unknown; content?: unknown } | undefined;\n if (message?.role !== 'assistant' || !Array.isArray(message.content)) continue;\n if (\n !message.content.some((block: unknown) => {\n if (!block || typeof block !== 'object') return false;\n const call = block as { type?: unknown; id?: unknown; name?: unknown };\n return call.type === 'toolCall' && call.id === toolCallId && call.name === WRITE_PLAN_TOOL;\n })\n )\n continue;\n content = visiblePlanForToolCall([entry], toolCallId);\n break;\n }\n }\n if (content === undefined) {\n if (host.context.selection.state?.['minor-mode']?.includes(PO_VOICE_AUTO_MODE_ID))\n throw new Error('Supply Markdown to write_plan during autonomous Voice; UI input is unavailable.');\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 (typeof content !== 'string' || !content.trim())\n throw new Error('No implementation plan content was supplied.');\n const directory = await plansDirectory();\n const title = planTitleOf(content);\n const writtenAt = new Date().toISOString();\n const filename = `${planTitleSlug(content)}-${Date.now()}.md`;\n const filePath = await writeNewPlanFile(directory, filename, `${content.trim()}\\n`);\n await host.context.session.appendCustomEntry(PLAN_DOCUMENT, {\n path: filePath,\n content: content.trim(),\n title,\n writtenAt,\n });\n await announcePlanDocument(filePath, title, writtenAt);\n await selectPlan(true);\n const saved = { path: filePath, written: true };\n return { ...output(saved), structuredContent: saved };\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 mainAgentOnly: true,\n description:\n 'Request exit-or-continue approval in the local Po UI. Requires the user to interact there; this is not a ChatGPT approval dialog.',\n // No decision parameter: this server plugin 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 if (host.context.selection.state?.['minor-mode']?.includes(PO_VOICE_AUTO_MODE_ID))\n throw new Error(\n 'UI plan review is unavailable during autonomous Voice. Wait for explicit user direction.',\n );\n // The client answers with the option's `value`, so the decision travels with the\n // question instead of the server plugin 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 ? planExitText(hasTaskTracking(host)) : 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 = await plansDirectory();\n const sections = [\n buildPlanModeBasePrompt(directory, PLAN_SERVER_CAPABILITIES),\n buildFlavorPlanningPrompt(flavor, directory, debugEvidence),\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 readPlanText(document.path);\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 = await 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 // Older harness sessions persisted plan-mode context messages; keep them out of the model context.\n event: 'context',\n async handle(event) {\n if (!Array.isArray(event.messages)) return;\n return {\n messages: event.messages.filter(\n (message) => (message as { customType?: string }).customType !== PLAN_MODE_CONTEXT,\n ),\n };\n },\n },\n ],\n };\n}\n\nconst DEFAULT_PLAN_TITLE = 'implementation-plan';\nconst PLAN_TITLE_MAX_LENGTH = 64;\nconst DEBUG_EVIDENCE_MAX_BYTES = 32 * 1024;\nconst DEBUG_EVIDENCE_MAX_TEXT_BYTES = 4 * 1024;\nconst DEBUG_EVIDENCE_MAX_ITEMS = 32;\nexport const PLAN_MODE_CONTEXT = 'agent-harness-plan-mode-context';\nexport const PLAN_EXIT_APPROVED_TEXT =\n 'The user approved exiting plan mode. Full tool access is restored. Begin implementing the approved plan.';\nexport const PLAN_CONTINUE_TEXT = 'The user chose to continue planning. Remain read-only and refine the plan.';\nexport const PLAN_EXIT_TASK_TRACKING_TEXT =\n 'Track implementation progress with po-task: create one task per plan step (the `tasks` code-mode namespace, or the `task` tool without code mode), mark each in progress when you start it and done when it is verified.';\n\n/** po-task registers the `task` tool and its code-mode guide; either one means the session can track tasks. */\nfunction hasTaskTracking(host: PoHeadlessHostService): boolean {\n return host\n .inspectCapabilities()\n .capabilities.some(\n (capability) =>\n (capability.kind === 'tool' && capability.name === 'task') ||\n (capability.kind === 'skill' && capability.name === 'codemode-how-to-task'),\n );\n}\n\nexport function planExitText(taskTracking: boolean): string {\n return taskTracking ? `${PLAN_EXIT_APPROVED_TEXT} ${PLAN_EXIT_TASK_TRACKING_TEXT}` : PLAN_EXIT_APPROVED_TEXT;\n}\n/** The plan's own heading, as the status line and the pointer record word it. */\nexport function planTitleOf(markdown: string): string {\n return markdown.match(/^#\\s+(.+?)\\s*$/m)?.[1] ?? DEFAULT_PLAN_TITLE;\n}\n\nexport function planTitleSlug(markdown: string): string {\n const heading = planTitleOf(markdown);\n return (\n heading\n .normalize('NFKD')\n .replace(/[\\u0300-\\u036f]/g, '')\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '')\n .slice(0, PLAN_TITLE_MAX_LENGTH) || DEFAULT_PLAN_TITLE\n );\n}\n\n/**\n * How the status line words when a plan was written. A malformed timestamp is\n * shown as it came rather than as an invalid date: the stamp's other job is to\n * change on a rewrite, and it can still do that.\n */\nexport function planStampOf(writtenAt: string): string {\n const at = new Date(writtenAt);\n return Number.isNaN(at.getTime()) ? writtenAt : at.toLocaleTimeString();\n}\n\nexport function visiblePlanForToolCall(entries: readonly unknown[], toolCallId: string): string | undefined {\n for (let index = entries.length - 1; index >= 0; index -= 1) {\n const entry = entries[index];\n if (!entry || typeof entry !== 'object') continue;\n const candidate = entry as {\n type?: string;\n message?: { role?: string; content?: unknown };\n };\n if (candidate.type !== 'message' || candidate.message?.role !== 'assistant') continue;\n if (!Array.isArray(candidate.message.content)) continue;\n\n const toolIndex = candidate.message.content.findIndex((block) => {\n if (!block || typeof block !== 'object') return false;\n const content = block as { type?: string; id?: string; name?: string };\n return content.type === 'toolCall' && content.id === toolCallId && content.name === WRITE_PLAN_TOOL;\n });\n if (toolIndex < 0) continue;\n\n const plan = candidate.message.content\n .slice(0, toolIndex)\n .flatMap((block) => {\n if (!block || typeof block !== 'object') return [];\n const content = block as { type?: string; text?: unknown };\n return content.type === 'text' && typeof content.text === 'string' ? [content.text] : [];\n })\n .join('\\n\\n')\n .trim();\n return plan || undefined;\n }\n return undefined;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction isString(value: unknown): value is string {\n return typeof value === 'string';\n}\n\nfunction boundedDebugText(value: unknown, field: string, required: boolean): string {\n if (!isString(value)) throw new Error(`${field} must be a string.`);\n const text = value.trim();\n if (required && !text) throw new Error(`${field} must not be empty.`);\n if (Buffer.byteLength(text, 'utf8') > DEBUG_EVIDENCE_MAX_TEXT_BYTES) {\n throw new Error(`${field} exceeds the bounded debug evidence limit.`);\n }\n return text;\n}\n\nfunction boundedDebugList(value: unknown, field: string): string[] {\n if (!Array.isArray(value) || value.length > DEBUG_EVIDENCE_MAX_ITEMS) {\n throw new Error(`${field} must be a bounded string array.`);\n }\n return value.map((item, index) => boundedDebugText(item, `${field}[${index}]`, false));\n}\n\nfunction optionalDebugText(value: unknown, field: string): string {\n return value === undefined ? '' : boundedDebugText(value, field, false);\n}\n\nfunction optionalDebugList(value: unknown, field: string): string[] {\n return value === undefined ? [] : boundedDebugList(value, field);\n}\n\nfunction assertExactObjectKeys(value: Record<string, unknown>, keys: readonly string[], field: string): void {\n const allowed = new Set(keys);\n const unexpected = Object.keys(value).find((key) => !allowed.has(key));\n if (unexpected) throw new Error(`${field} contains unsupported field '${unexpected}'.`);\n}\n\nexport function parseDebugEvidencePacket(value: unknown): DebugEvidencePacket {\n if (!isRecord(value)) throw new Error('Debug evidence must be an object.');\n assertExactObjectKeys(\n value,\n [\n 'issue',\n 'expectedBehavior',\n 'reproductionAttempt',\n 'actualBehavior',\n 'logs',\n 'correlatedTraceEvidence',\n 'processOutput',\n 'browserConsoleEvidence',\n 'correlationIds',\n 'timestamps',\n 'verifiedFacts',\n 'hypotheses',\n 'unavailableEvidence',\n ],\n 'Debug evidence',\n );\n const packet: DebugEvidencePacket = {\n issue: boundedDebugText(value.issue, 'issue', true),\n expectedBehavior: optionalDebugText(value.expectedBehavior, 'expectedBehavior'),\n reproductionAttempt: optionalDebugText(value.reproductionAttempt, 'reproductionAttempt'),\n actualBehavior: optionalDebugText(value.actualBehavior, 'actualBehavior'),\n logs: optionalDebugList(value.logs, 'logs'),\n correlatedTraceEvidence: optionalDebugList(value.correlatedTraceEvidence, 'correlatedTraceEvidence'),\n processOutput: optionalDebugList(value.processOutput, 'processOutput'),\n browserConsoleEvidence: optionalDebugList(value.browserConsoleEvidence, 'browserConsoleEvidence'),\n correlationIds: optionalDebugList(value.correlationIds, 'correlationIds'),\n timestamps: optionalDebugList(value.timestamps, 'timestamps'),\n verifiedFacts: optionalDebugList(value.verifiedFacts, 'verifiedFacts'),\n hypotheses: optionalDebugList(value.hypotheses, 'hypotheses'),\n unavailableEvidence: optionalDebugList(value.unavailableEvidence, 'unavailableEvidence'),\n };\n if (Buffer.byteLength(JSON.stringify(packet), 'utf8') > DEBUG_EVIDENCE_MAX_BYTES) {\n throw new Error('Debug evidence exceeds the bounded packet size.');\n }\n return packet;\n}\n","import { defineRoot, type RootDeclaration } from '@agimon-ai/po-core/extensionFile';\nimport type { PoServerPluginContext, PoServerSessionPlugin } from '@agimon-ai/po-core/serverPlugin';\n\nimport { mountMcpTools } from '../../../../services/mcpTools';\nimport { api } from '../../../../services/planApi';\nimport { createPlanServerSession } from '../../../../services/planServerSession';\n\nexport default defineRoot(\n ({ agent, host }: PoServerPluginContext): RootDeclaration<PoServerSessionPlugin, PoServerPluginContext> => {\n const value = {\n api: [api],\n ...(agent\n ? createPlanServerSession(agent, {\n homeDirectory: host.context.homeDirectory ?? host.context.environment?.HOME,\n })\n : {}),\n };\n return {\n value,\n services: [...(value.services ?? []), ...(value.tools?.length ? [mountMcpTools(value.tools)] : [])],\n };\n },\n);\n","import { defineRoute, type WithRoot } from '@agimon-ai/po-core/extensionFile';\n\ntype Root = Awaited<ReturnType<typeof import('../../root.server').default>>['value'];\n\nexport default defineRoute((context: WithRoot<unknown, Root>) => context.root.api![0]!);\n","import { defineRoutedContribution, type WithRoot } from '@agimon-ai/po-core/extensionFile';\n\ntype Root = Awaited<ReturnType<typeof import('../root.server').default>>['value'];\n\nexport default defineRoutedContribution((context: WithRoot<unknown, Root>) => context.root.hooks?.[0], {\n cardinality: 'optional',\n});\n","import { defineRoutedContribution, type WithRoot } from '@agimon-ai/po-core/extensionFile';\n\ntype Root = Awaited<ReturnType<typeof import('../root.server').default>>['value'];\n\nexport default defineRoutedContribution((context: WithRoot<unknown, Root>) => context.root.hooks?.[2], {\n cardinality: 'optional',\n});\n","import { defineRoutedContribution, type WithRoot } from '@agimon-ai/po-core/extensionFile';\n\ntype Root = Awaited<ReturnType<typeof import('../root.server').default>>['value'];\n\nexport default defineRoutedContribution((context: WithRoot<unknown, Root>) => context.root.hooks?.[1], {\n cardinality: 'optional',\n});\n","import { defineRoutedContribution, type WithRoot } from '@agimon-ai/po-core/extensionFile';\n\ntype Root = Awaited<ReturnType<typeof import('../root.server').default>>['value'];\n\nexport default defineRoutedContribution((context: WithRoot<unknown, Root>) => context.root.resources?.[0], {\n cardinality: 'optional',\n});\n","import { defineToolRestriction } from '@agimon-ai/po-core/extensionFile';\nimport type { PoHeadlessToolRestriction } from '@agimon-ai/po-core/headless';\nimport { PO_VOICE_AUTO_MODE_ID } from '@agimon-ai/po-core/voiceTools';\n\nexport default defineToolRestriction({\n when: { state: { 'minor-mode': PO_VOICE_AUTO_MODE_ID } },\n excludedTools: ['complete_plan'],\n} satisfies PoHeadlessToolRestriction);\n","import { defineToolRestriction } from '@agimon-ai/po-core/extensionFile';\nimport type { PoHeadlessToolRestriction } from '@agimon-ai/po-core/headless';\n\nexport default defineToolRestriction({\n when: { state: { 'minor-mode': 'plan' } },\n excludedTools: ['edit', 'write'],\n} satisfies PoHeadlessToolRestriction);\n","import { defineRoutedContribution, type WithRoot } from '@agimon-ai/po-core/extensionFile';\n\ntype Root = Awaited<ReturnType<typeof import('../root.server').default>>['value'];\n\nexport default defineRoutedContribution((context: WithRoot<unknown, Root>) => context.root.tools?.[2], {\n cardinality: 'optional',\n});\n","import { defineRoutedContribution, type WithRoot } from '@agimon-ai/po-core/extensionFile';\n\ntype Root = Awaited<ReturnType<typeof import('../root.server').default>>['value'];\n\nexport default defineRoutedContribution((context: WithRoot<unknown, Root>) => context.root.tools?.[0], {\n cardinality: 'optional',\n});\n","import { defineRoutedContribution, type WithRoot } from '@agimon-ai/po-core/extensionFile';\n\ntype Root = Awaited<ReturnType<typeof import('../root.server').default>>['value'];\n\nexport default defineRoutedContribution((context: WithRoot<unknown, Root>) => context.root.tools?.[1], {\n cardinality: 'optional',\n});\n","// Generated by @agimon-ai/po-build. Do not edit by hand.\nimport { defineServerPlugin, type PoServerSessionPlugin } from '@agimon-ai/po-core/serverPlugin';\nimport { composeServerProviderOverrides } from '@agimon-ai/po-core/serverPlugin';\nimport { composeRootHooks, composeRootInjects } from '@agimon-ai/po-core/extensionFile';\n\nimport rootSession from '../src/extensions/workspaces/sessions/(backend)/root.server';\nimport apiRoute from '../src/extensions/workspaces/sessions/(backend)/api/plans/route.server';\nimport hookBeforeAgentStart from '../src/extensions/workspaces/sessions/(backend)/hook/beforeAgentStart.server';\nimport hookContext from '../src/extensions/workspaces/sessions/(backend)/hook/context.server';\nimport hookSessionStart from '../src/extensions/workspaces/sessions/(backend)/hook/sessionStart.server';\nimport resourcePoUsePlan from '../src/extensions/workspaces/sessions/(backend)/resource/poUsePlan.server';\nimport toolRestrictionPlan from '../src/extensions/workspaces/sessions/(backend)/tool-restriction/plan.server';\nimport toolRestrictionPlanReadOnly from '../src/extensions/workspaces/sessions/(backend)/tool-restriction/planReadOnly.server';\nimport toolCompletePlan from '../src/extensions/workspaces/sessions/(backend)/tool/completePlan.server';\nimport toolRecordDebugEvidence from '../src/extensions/workspaces/sessions/(backend)/tool/recordDebugEvidence.server';\nimport toolWritePlan from '../src/extensions/workspaces/sessions/(backend)/tool/writePlan.server';\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;\n/* v8 ignore next -- generated normalization branch */\nconst defined = <T>(values: readonly (T | undefined)[]): T[] =>\n values.filter((value): value is T => value !== undefined);\nconst via = <T>(identity: Record<string, unknown>, value: unknown): T =>\n (value === undefined ? undefined : { ...identity, ...(value as object) }) as T;\n\nexport const serverPlugin = defineServerPlugin({\n name: '@agimon-ai/po-plan',\n providerOverrides: composeServerProviderOverrides(rootSession),\n injects: composeRootInjects({ session: [rootSession] }),\n session: async (context) => {\n const scopeSession = await context.scopeRoot('session', rootSession, context);\n const contextSession = { ...context, root: scopeSession.value };\n return {\n ...composeRootHooks(scopeSession),\n get api(): PoServerSessionPlugin['api'] { return [at(apiRoute, contextSession)]; },\n get hooks(): PoServerSessionPlugin['hooks'] { return defined([via({ event: 'before_agent_start' }, at(hookBeforeAgentStart, contextSession)), via({ event: 'context' }, at(hookContext, contextSession)), via({ event: 'session_start' }, at(hookSessionStart, contextSession))]); },\n get resources(): PoServerSessionPlugin['resources'] { return defined([at(resourcePoUsePlan, contextSession)]); },\n get toolRestrictions(): PoServerSessionPlugin['toolRestrictions'] { return [at(toolRestrictionPlan, contextSession), at(toolRestrictionPlanReadOnly, contextSession)]; },\n get tools(): PoServerSessionPlugin['tools'] { return defined([via({ name: 'complete_plan' }, at(toolCompletePlan, contextSession)), via({ name: 'record_debug_evidence' }, at(toolRecordDebugEvidence, contextSession)), via({ name: 'write_plan' }, at(toolWritePlan, contextSession))]); },\n services: [...(scopeSession.services ?? [])],\n get activities(): PoServerSessionPlugin['activities'] { return [...(scopeSession.activities ?? [])]; },\n };\n },\n});\n\nexport default serverPlugin;\n"],"mappings":"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"}
|
|
@@ -6,11 +6,10 @@ import { composeRootHooks, composeRootInjects, defineRoot, defineRoute, defineRo
|
|
|
6
6
|
import { Type } from "typebox";
|
|
7
7
|
import { createHash } from "node:crypto";
|
|
8
8
|
import { Hono } from "hono";
|
|
9
|
-
import fs from "node:fs";
|
|
9
|
+
import fs from "node:fs/promises";
|
|
10
10
|
import os from "node:os";
|
|
11
11
|
import path from "node:path";
|
|
12
12
|
import { piAgentDirectory } from "@agimon-ai/po-core/piSettings";
|
|
13
|
-
import fs$1 from "node:fs/promises";
|
|
14
13
|
import { fileMutationRunner, openGuardedFile } from "@agimon-ai/po-core/platform";
|
|
15
14
|
import { Buffer as Buffer$1 } from "node:buffer";
|
|
16
15
|
import { PO_VOICE_AUTO_MODE_ID } from "@agimon-ai/po-core/voiceTools";
|
|
@@ -68,10 +67,10 @@ var PlanPointerService = class {
|
|
|
68
67
|
pathFor(sessionId) {
|
|
69
68
|
return path.join(this.directory(), `${this.hash(sessionId)}.json`);
|
|
70
69
|
}
|
|
71
|
-
read(sessionId) {
|
|
70
|
+
async read(sessionId) {
|
|
72
71
|
let raw;
|
|
73
72
|
try {
|
|
74
|
-
raw = fs.
|
|
73
|
+
raw = await fs.readFile(this.pathFor(sessionId), "utf8");
|
|
75
74
|
} catch {
|
|
76
75
|
return;
|
|
77
76
|
}
|
|
@@ -92,15 +91,15 @@ var PlanPointerService = class {
|
|
|
92
91
|
...typeof planId === "string" ? { planId } : {}
|
|
93
92
|
};
|
|
94
93
|
}
|
|
95
|
-
write(sessionId, record) {
|
|
96
|
-
fs.
|
|
94
|
+
async write(sessionId, record) {
|
|
95
|
+
await fs.mkdir(this.directory(), {
|
|
97
96
|
recursive: true,
|
|
98
97
|
mode: PRIVATE_DIRECTORY_MODE
|
|
99
98
|
});
|
|
100
|
-
fs.
|
|
99
|
+
await fs.writeFile(this.pathFor(sessionId), JSON.stringify(record), { mode: PRIVATE_FILE_MODE });
|
|
101
100
|
}
|
|
102
|
-
clear(sessionId) {
|
|
103
|
-
fs.
|
|
101
|
+
async clear(sessionId) {
|
|
102
|
+
await fs.rm(this.pathFor(sessionId), { force: true });
|
|
104
103
|
}
|
|
105
104
|
hash(value) {
|
|
106
105
|
return createHash("sha256").update(value).digest("hex").slice(0, 16);
|
|
@@ -188,12 +187,12 @@ function formatPlanStatus(title, stamp) {
|
|
|
188
187
|
//#endregion
|
|
189
188
|
//#region src/services/planFiles/index.ts
|
|
190
189
|
async function writeNewPlanFile(directory, filename, content) {
|
|
191
|
-
await fs
|
|
190
|
+
await fs.mkdir(directory, {
|
|
192
191
|
recursive: true,
|
|
193
192
|
mode: 448
|
|
194
193
|
});
|
|
195
194
|
const planPath = path.join(directory, filename);
|
|
196
|
-
await fs
|
|
195
|
+
await fs.writeFile(planPath, content, {
|
|
197
196
|
encoding: "utf8",
|
|
198
197
|
flag: "wx",
|
|
199
198
|
mode: 384
|
|
@@ -201,7 +200,7 @@ async function writeNewPlanFile(directory, filename, content) {
|
|
|
201
200
|
return planPath;
|
|
202
201
|
}
|
|
203
202
|
async function readPlanText(planPath) {
|
|
204
|
-
return await fs
|
|
203
|
+
return await fs.readFile(planPath, "utf8");
|
|
205
204
|
}
|
|
206
205
|
/** The extra byte detects growth without ever reading or allocating the whole file. */
|
|
207
206
|
async function readBounded(handle, maxBytes) {
|
|
@@ -227,11 +226,11 @@ async function readPlanBytes(planPath, maxBytes) {
|
|
|
227
226
|
}
|
|
228
227
|
/** Compare and write the same open inode. The queue protects cooperating API writers, not external processes. */
|
|
229
228
|
async function overwritePlanFile(planPath, content, expectedHash, maxBytes) {
|
|
230
|
-
const canonical = await fs
|
|
229
|
+
const canonical = await fs.realpath(planPath);
|
|
231
230
|
return await fileMutationRunner.forKey(canonical)(async () => {
|
|
232
231
|
const handle = await openGuardedFile(planPath, { write: true });
|
|
233
232
|
try {
|
|
234
|
-
if (await fs
|
|
233
|
+
if (await fs.realpath(planPath) !== canonical) return void 0;
|
|
235
234
|
const raw = await readBounded(handle, maxBytes);
|
|
236
235
|
if (raw.length > maxBytes) return void 0;
|
|
237
236
|
const hash = createHash("sha256").update(raw.toString("utf8"), "utf8").digest("hex");
|
|
@@ -240,8 +239,8 @@ async function overwritePlanFile(planPath, content, expectedHash, maxBytes) {
|
|
|
240
239
|
hash
|
|
241
240
|
};
|
|
242
241
|
const opened = await handle.stat();
|
|
243
|
-
const current = await fs
|
|
244
|
-
if (!opened.isFile() || opened.nlink !== 1 || !current.isFile() || current.nlink !== 1 || opened.dev !== current.dev || opened.ino !== current.ino || await fs
|
|
242
|
+
const current = await fs.lstat(planPath);
|
|
243
|
+
if (!opened.isFile() || opened.nlink !== 1 || !current.isFile() || current.nlink !== 1 || opened.dev !== current.dev || opened.ino !== current.ino || await fs.realpath(planPath) !== canonical) return void 0;
|
|
245
244
|
await handle.writeFile(content, "utf8");
|
|
246
245
|
await handle.truncate(Buffer.byteLength(content, "utf8"));
|
|
247
246
|
return {
|
|
@@ -312,7 +311,7 @@ function createPlanApi(options = {}) {
|
|
|
312
311
|
const pointers = options.pointers ?? new PlanPointerService();
|
|
313
312
|
const pointerFor = () => options.sessionId === void 0 ? void 0 : pointers.read(options.sessionId);
|
|
314
313
|
app.get(routes.current.path, async (context) => {
|
|
315
|
-
const pointer = pointerFor();
|
|
314
|
+
const pointer = await pointerFor();
|
|
316
315
|
if (pointer === void 0) return context.json({ error: NO_PLAN }, 404);
|
|
317
316
|
const view = {
|
|
318
317
|
path: pointer.path,
|
|
@@ -331,7 +330,7 @@ function createPlanApi(options = {}) {
|
|
|
331
330
|
return context.json({ error: "The save body is not JSON." }, 400);
|
|
332
331
|
}
|
|
333
332
|
if (typeof request !== "object" || request === null || Array.isArray(request) || !("content" in request) || typeof request.content !== "string" || !("expectedHash" in request) || typeof request.expectedHash !== "string") return context.json({ error: "A save carries the content to write and the hash it was read at." }, 400);
|
|
334
|
-
const pointer = pointerFor();
|
|
333
|
+
const pointer = await pointerFor();
|
|
335
334
|
if (pointer === void 0) return context.json({ error: NO_PLAN }, 404);
|
|
336
335
|
if (Buffer.byteLength(request.content, "utf8") > 1048576) return context.json({ error: `The plan is larger than ${MAX_PLAN_BYTES} bytes.` }, 413);
|
|
337
336
|
try {
|
|
@@ -451,10 +450,10 @@ function createPlanServerSession(host, options) {
|
|
|
451
450
|
* only on the Pi side: the group never appeared, and when it did its panel
|
|
452
451
|
* had no pointer to answer from.
|
|
453
452
|
*/
|
|
454
|
-
const announcePlanDocument = (filePath, title, writtenAt) => {
|
|
453
|
+
const announcePlanDocument = async (filePath, title, writtenAt) => {
|
|
455
454
|
const status = formatPlanStatus(title, planStampOf(writtenAt));
|
|
456
455
|
try {
|
|
457
|
-
pointers.write(host.context.sessionId, {
|
|
456
|
+
await pointers.write(host.context.sessionId, {
|
|
458
457
|
path: filePath,
|
|
459
458
|
title,
|
|
460
459
|
writtenAt
|
|
@@ -778,7 +777,7 @@ function createPlanServerSession(host, options) {
|
|
|
778
777
|
title,
|
|
779
778
|
writtenAt
|
|
780
779
|
});
|
|
781
|
-
announcePlanDocument(filePath, title, writtenAt);
|
|
780
|
+
await announcePlanDocument(filePath, title, writtenAt);
|
|
782
781
|
await selectPlan(true);
|
|
783
782
|
const saved = {
|
|
784
783
|
path: filePath,
|
|
@@ -866,7 +865,7 @@ function createPlanServerSession(host, options) {
|
|
|
866
865
|
event: "session_start",
|
|
867
866
|
async handle() {
|
|
868
867
|
await restorePlanState();
|
|
869
|
-
const pointer = pointers.read(host.context.sessionId);
|
|
868
|
+
const pointer = await pointers.read(host.context.sessionId);
|
|
870
869
|
if (pointer) host.context.client.setStatus(PLAN_STATUS_KEY, formatPlanStatus(pointer.title, planStampOf(pointer.writtenAt)));
|
|
871
870
|
if (!selected()) await restoreModel();
|
|
872
871
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.mjs","names":["isRecord","fs","Buffer","rootSession","apiRoute","hookBeforeAgentStart","hookContext","hookSessionStart","resourcePoUsePlan","toolRestrictionPlan","toolRestrictionPlanReadOnly","toolCompletePlan","toolRecordDebugEvidence","toolWritePlan"],"sources":["../../src/schemas/mcpTools.ts","../../src/services/mcpTools/index.ts","../../src/services/planPointer/index.ts","../../src/types/apiRoutes.ts","../../src/types/planApi.ts","../../src/services/planFiles/index.ts","../../src/services/planApi/index.ts","../../src/services/optionalTeamServices/index.ts","../../src/services/planServerSession/index.ts","../../src/extensions/workspaces/sessions/(backend)/root.server.ts","../../src/extensions/workspaces/sessions/(backend)/api/plans/route.server.ts","../../src/extensions/workspaces/sessions/(backend)/hook/beforeAgentStart.server.ts","../../src/extensions/workspaces/sessions/(backend)/hook/context.server.ts","../../src/extensions/workspaces/sessions/(backend)/hook/sessionStart.server.ts","../../src/extensions/workspaces/sessions/(backend)/resource/poUsePlan.server.ts","../../src/extensions/workspaces/sessions/(backend)/tool-restriction/plan.server.ts","../../src/extensions/workspaces/sessions/(backend)/tool-restriction/planReadOnly.server.ts","../../src/extensions/workspaces/sessions/(backend)/tool/completePlan.server.ts","../../src/extensions/workspaces/sessions/(backend)/tool/recordDebugEvidence.server.ts","../../src/extensions/workspaces/sessions/(backend)/tool/writePlan.server.ts","../../generated/server.ts"],"sourcesContent":["import { Type } from 'typebox';\n\nconst markdown = Type.String({ minLength: 1 });\n\nexport const remoteWritePlanParameters = Type.Object({ markdown }, { additionalProperties: false });\nexport const writePlanParameters = Type.Object({ markdown: Type.Optional(markdown) }, { additionalProperties: false });\n","import type { PoHeadlessTool } from '@agimon-ai/po-core/headless';\nimport type { PoMcpPluginContext } from '@agimon-ai/po-core/mcpFacet';\nimport type { Context } from '@deepseek-ai/cordis';\n\nimport { remoteWritePlanParameters } from '../../schemas/mcpTools';\n\nexport const PLAN_MCP_TOOLS_SERVICE = 'po/plan-mcp-tools';\n\nconst MODE_ID = 'plan';\nconst MODE_LABEL = 'Plan';\n\nexport interface McpToolCatalog {\n get(name: string): PoHeadlessTool | undefined;\n names(): readonly string[];\n}\n\nexport function mountMcpTools(tools: readonly PoHeadlessTool[]) {\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: PoMcpPluginContext, name: string): PoHeadlessTool {\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 const writesPlan = name === 'write_plan';\n return {\n ...tool,\n ...(writesPlan\n ? {\n description:\n 'Save the supplied Markdown plan in the bound session. Requires active Plan mode. Saving does not approve implementation.',\n parameters: remoteWritePlanParameters,\n annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: false },\n }\n : {}),\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 if (\n writesPlan &&\n (typeof parameters !== 'object' ||\n parameters === null ||\n !('markdown' in parameters) ||\n typeof parameters.markdown !== 'string' ||\n !parameters.markdown.trim())\n ) {\n throw new Error(\n 'Supply the complete visible plan in the markdown argument. Remote calls cannot read the ChatGPT transcript.',\n );\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 { createHash } from 'node:crypto';\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\n\nimport { piAgentDirectory } from '@agimon-ai/po-core/piSettings';\n\nimport type { PlanPointerRecord } from '../../types/planApi';\nimport type { PlanPointerPort } from '../../types/planPointer';\n\n/**\n * Record and read where write_plan left this session's plan file.\n *\n * Pi already gives every session an agent directory, so the records live under\n * it rather than in a second place a reader would have to know about. The\n * posture matches the plan file itself, 0700 on the directory and 0600 on the\n * record: a plan is a private working document, not something to leave\n * world-readable in a shared home.\n */\n\nconst POINTER_DIRECTORY = 'po-plan';\nconst PRIVATE_DIRECTORY_MODE = 0o700;\nconst PRIVATE_FILE_MODE = 0o600;\n\nexport interface NodePlanPointerOptions {\n env?: NodeJS.ProcessEnv;\n homeDirectory?: string;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nexport class PlanPointerService implements PlanPointerPort {\n private readonly env: NodeJS.ProcessEnv;\n private readonly homeDirectory: string;\n\n constructor(options: NodePlanPointerOptions = {}) {\n this.env = options.env ?? {};\n this.homeDirectory = options.homeDirectory ?? os.homedir();\n }\n\n /** Where the records live; exposed so a test can look without guessing. */\n directory(): string {\n return path.join(piAgentDirectory(this.env, this.homeDirectory), POINTER_DIRECTORY);\n }\n\n /**\n * The record for one session. The id is hashed rather than used as written:\n * it reaches here from a host that may word it however it likes, and a\n * filename is not the place to find that out.\n */\n pathFor(sessionId: string): string {\n return path.join(this.directory(), `${this.hash(sessionId)}.json`);\n }\n\n read(sessionId: string): PlanPointerRecord | undefined {\n let raw: string;\n try {\n raw = fs.readFileSync(this.pathFor(sessionId), 'utf8');\n } catch {\n return undefined;\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n return undefined;\n }\n if (!isRecord(parsed)) return undefined;\n const { path: planPath, title, writtenAt, planId } = parsed;\n // A record half-written by an interrupted run reads as no plan at all,\n // which is a state the cockpit already handles.\n if (typeof planPath !== 'string' || !path.isAbsolute(planPath)) return undefined;\n if (typeof title !== 'string' || typeof writtenAt !== 'string') return undefined;\n return { path: planPath, title, writtenAt, ...(typeof planId === 'string' ? { planId } : {}) };\n }\n\n write(sessionId: string, record: PlanPointerRecord): void {\n fs.mkdirSync(this.directory(), { recursive: true, mode: PRIVATE_DIRECTORY_MODE });\n fs.writeFileSync(this.pathFor(sessionId), JSON.stringify(record), { mode: PRIVATE_FILE_MODE });\n }\n\n clear(sessionId: string): void {\n fs.rmSync(this.pathFor(sessionId), { force: true });\n }\n\n private hash(value: string): string {\n return createHash('sha256').update(value).digest('hex').slice(0, 16);\n }\n}\n","import type { ApiResponseOf, ApiRoutes } from '@agimon-ai/po-core/web';\n\nimport type { PlanDetailView, PlanSaveView } from './planApi';\n\n/**\n * This package's routes, as data.\n *\n * No scope and no base path. The build reads both off\n * `src/extensions/workspaces/sessions/(backend)/api/plans/`, so the mount is\n * stated once, by the folder that creates it. The paths used to be written\n * here as helpers, again in the contract, and again inside every URL the page\n * built, and the copies were free to disagree.\n *\n * This is the one file the routes, the Hono app and the browser all read, so a\n * path cannot move on one side alone.\n *\n * Neither route takes a query or a path parameter: a session has one current\n * plan, so `current` answers it and `save` takes the manual write back.\n */\nconst routes = {\n current: {\n method: 'GET',\n path: '/current',\n response: undefined as unknown as ApiResponseOf<PlanDetailView>,\n },\n save: {\n method: 'PUT',\n path: '/content',\n response: undefined as unknown as ApiResponseOf<PlanSaveView>,\n },\n} as const satisfies ApiRoutes;\n\nexport default routes;\n","/**\n * The wire vocabulary this package's API shares with whatever calls it.\n *\n * It lives under src/types because that is the one server root a browser\n * bundle may read: the routes answer with these shapes and the cockpit plugin\n * reads them, so neither half can drift from the other. The paths themselves\n * are not here; they are declared once in `./apiRoutes`, which the Hono app\n * and the generated client both read.\n *\n * Two routes, and neither takes a path. A session has one current plan, so\n * `current` answers it and `content` takes the manual save back; the file they\n * act on is the one write_plan recorded, never one the page named. That is\n * also why there is nothing to escape here: a reader cannot address a second\n * file through these URLs.\n */\n\n/** Where a host mounts this package's API; the segment after /api/plugins/. */\nexport const API_BASE_PATH = 'plans';\n\n/**\n * Footer status key the session publishes once a plan exists, which is what\n * makes the cockpit's activity group appear at all. It outlives plan mode: the\n * plan is most worth reading while the agent implements it.\n */\nexport const PLAN_STATUS_KEY = 'po-plan-document';\n\n/** The selector contract shared by complete_plan and its cockpit composer prompt. */\nexport const PLAN_REVIEW_TITLE = 'Plan complete. What would you like to do?';\nexport const EXIT_PLAN_MODE_CHOICE = 'Exit plan mode and start implementation';\nexport const CONTINUE_PLANNING_CHOICE = 'Continue planning';\nexport const EXIT_PLAN_DECISION = 'exit';\nexport const CONTINUE_PLAN_DECISION = 'continue';\n\n/**\n * The label the reader sees paired with the decision the tool acts on. A headless\n * client answers with `value`, so a server plugin never has to recognise the wording; Pi's\n * own `ui.select` carries labels only, so its half maps the label back itself.\n */\nexport const PLAN_REVIEW_CHOICES = [\n { label: EXIT_PLAN_MODE_CHOICE, value: EXIT_PLAN_DECISION },\n { label: CONTINUE_PLANNING_CHOICE, value: CONTINUE_PLAN_DECISION },\n] as const;\n\n/** The same options as bare labels, which is all Pi's select and the composer prompt need. */\nexport const PLAN_REVIEW_OPTIONS = PLAN_REVIEW_CHOICES.map((choice) => choice.label);\n\n/** Between the plan's title and the stamp that marks a rewrite. */\nconst STATUS_SEPARATOR = ' · ';\n\n/** Strips the colour a session may have themed a status with. */\nconst ANSI = /\u001b\\[[0-9;]*m/gu;\n\n/** The status line the session publishes; the stamp changes on every write. */\nexport function formatPlanStatus(title: string, stamp: string): string {\n return `${title}${STATUS_SEPARATOR}${stamp}`;\n}\n\n/** What a reader gets out of the status line. */\nexport interface PlanStatusView {\n title: string;\n /** When the plan was last written, as the session worded it; empty when it said nothing. */\n stamp: string;\n}\n\n/**\n * Reading the status line back. Statuses reach a plugin raw, so the colour a\n * session may have added comes off here. A title can itself contain the\n * separator, so the stamp is taken from the last one rather than the first.\n */\nexport function parsePlanStatus(raw: string | undefined): PlanStatusView | undefined {\n if (raw === undefined) return undefined;\n const text = raw.replace(ANSI, '').trim();\n if (text === '') return undefined;\n const cut = text.lastIndexOf(STATUS_SEPARATOR);\n if (cut === -1) return { title: text, stamp: '' };\n return { title: text.slice(0, cut).trim(), stamp: text.slice(cut + STATUS_SEPARATOR.length).trim() };\n}\n\n/**\n * What write_plan recorded about this session's plan.\n *\n * The absolute path is the point of it: the extension resolves the plans\n * directory against the harness root, which no other process knows, so it\n * writes down where the plan landed rather than leaving the API to derive a\n * location it would sometimes get wrong.\n */\nexport interface PlanPointerRecord {\n path: string;\n title: string;\n writtenAt: string;\n planId?: string;\n}\n\n/** The plan as it stands on disk right now, which is what the source view edits. */\nexport interface PlanDetailView {\n path: string;\n title: string;\n writtenAt: string;\n planId?: string;\n content: string;\n /** sha256 of the content; the token a save hands back to prove it is not stale. */\n hash: string;\n /** True when the plan is missing or past the size cap, so `content` is empty. */\n unavailable: boolean;\n reason?: string;\n}\n\n/** What the save route takes. */\nexport interface PlanSaveRequest {\n /** The hash the reader loaded; a mismatch means the plan moved underneath them. */\n expectedHash: string;\n content: string;\n}\n\n/** What the save route answers with. */\nexport interface PlanSaveView {\n hash: string;\n}\n\n/** What a route reports when it refuses; the page shows `error` verbatim. */\nexport interface PlanErrorView {\n error: string;\n /** On a stale save, the hash the plan actually has now. */\n hash?: string;\n}\n","import { createHash } from 'node:crypto';\nimport fs, { type FileHandle } from 'node:fs/promises';\nimport path from 'node:path';\n\nimport { fileMutationRunner, openGuardedFile } from '@agimon-ai/po-core/platform';\n\nexport async function writeNewPlanFile(directory: string, filename: string, content: string): Promise<string> {\n await fs.mkdir(directory, { recursive: true, mode: 0o700 });\n const planPath = path.join(directory, filename);\n await fs.writeFile(planPath, content, { encoding: 'utf8', flag: 'wx', mode: 0o600 });\n return planPath;\n}\n\nexport async function readPlanText(planPath: string): Promise<string> {\n return await fs.readFile(planPath, 'utf8');\n}\n\n/** The extra byte detects growth without ever reading or allocating the whole file. */\nasync function readBounded(handle: FileHandle, maxBytes: number): Promise<Buffer> {\n const stats = await handle.stat();\n if (!stats.isFile()) throw new Error('The plan is not a regular file.');\n const buffer = Buffer.alloc(maxBytes + 1);\n if (stats.size > maxBytes) return buffer;\n let offset = 0;\n while (offset < buffer.length) {\n const { bytesRead } = await handle.read(buffer, offset, buffer.length - offset, offset);\n if (bytesRead === 0) break;\n offset += bytesRead;\n }\n return buffer.subarray(0, offset);\n}\n\nexport async function readPlanBytes(planPath: string, maxBytes: number): Promise<Buffer> {\n const handle = await openGuardedFile(planPath);\n try {\n return await readBounded(handle, maxBytes);\n } finally {\n await handle.close();\n }\n}\n\n/** Compare and write the same open inode. The queue protects cooperating API writers, not external processes. */\nexport async function overwritePlanFile(\n planPath: string,\n content: string,\n expectedHash: string,\n maxBytes: number,\n): Promise<{ written: boolean; hash: string } | undefined> {\n const canonical = await fs.realpath(planPath);\n return await fileMutationRunner.forKey(canonical)(async () => {\n const handle = await openGuardedFile(planPath, { write: true });\n try {\n if ((await fs.realpath(planPath)) !== canonical) return undefined;\n const raw = await readBounded(handle, maxBytes);\n if (raw.length > maxBytes) return undefined;\n const hash = createHash('sha256').update(raw.toString('utf8'), 'utf8').digest('hex');\n if (hash !== expectedHash) return { written: false, hash };\n const opened = await handle.stat();\n const current = await fs.lstat(planPath);\n if (\n !opened.isFile() ||\n opened.nlink !== 1 ||\n !current.isFile() ||\n current.nlink !== 1 ||\n opened.dev !== current.dev ||\n opened.ino !== current.ino ||\n (await fs.realpath(planPath)) !== canonical\n )\n return undefined;\n await handle.writeFile(content, 'utf8');\n await handle.truncate(Buffer.byteLength(content, 'utf8'));\n return { written: true, hash: createHash('sha256').update(content, 'utf8').digest('hex') };\n } finally {\n await handle.close();\n }\n });\n}\n","import { createHash } from 'node:crypto';\n\nimport type { PoApi, PoApiContext, PoApiHandler } from '@agimon-ai/po-core/packageApi';\nimport { Hono } from 'hono';\n\nimport { PlanPointerService } from '../../services/planPointer';\nimport routes from '../../types/apiRoutes';\nimport { API_BASE_PATH, type PlanDetailView, type PlanSaveView } from '../../types/planApi';\nimport type { PlanPointerPort } from '../../types/planPointer';\nimport { overwritePlanFile, readPlanBytes } from '../planFiles';\n\n/**\n * This package's HTTP surface: the session's current plan, and the manual save.\n *\n * Each path comes from the route table the browser reads, so a route the page\n * calls and the route this serves cannot be spelled apart. Both are relative\n * to the mount its host gives it, so nothing here repeats where it was mounted. The host is one session's own server, which is what\n * makes the session id available without the page supplying it.\n *\n * Neither route takes a path. A session has exactly one current plan, and the\n * file both routes act on is the one the pointer names, so a page can address\n * the plan and nothing else; there is no traversal question to answer because\n * there is no path to traverse.\n *\n * The save refuses unless the reader proves they were looking at the content\n * that is still on disk. The agent may rewrite the plan while a tab is open,\n * and silently discarding whichever side finished second is the one outcome\n * worth ruling out.\n */\n\n/** Past this, the plan is reported rather than carried. */\nexport const MAX_PLAN_BYTES = 1024 * 1024;\n\nconst NO_PLAN = 'This session has not written a plan yet.';\nconst STALE_SAVE = 'The plan changed since it was opened.';\nconst NOT_A_FILE = 'The plan file is no longer a regular file, so it was not written.';\n\nexport interface PlanApiOptions {\n /** The session these routes answer for; absent when the host is the hub. */\n sessionId?: string;\n pointers?: PlanPointerPort;\n}\n\nfunction hashOf(content: string): string {\n return createHash('sha256').update(content, 'utf8').digest('hex');\n}\n\n/** Reads the plan as the source view will show it, or says why it cannot. */\nasync function readPlan(\n planPath: string,\n): Promise<Pick<PlanDetailView, 'content' | 'hash' | 'unavailable' | 'reason'>> {\n let raw: Buffer;\n try {\n raw = await readPlanBytes(planPath, MAX_PLAN_BYTES);\n } catch {\n return { content: '', hash: '', unavailable: true, reason: 'the plan file no longer exists' };\n }\n if (raw.byteLength > MAX_PLAN_BYTES) {\n return { content: '', hash: '', unavailable: true, reason: `the plan is larger than ${MAX_PLAN_BYTES} bytes` };\n }\n const content = raw.toString('utf8');\n return { content, hash: hashOf(content), unavailable: false };\n}\n\nexport function createPlanApi(options: PlanApiOptions = {}): Hono {\n const app = new Hono();\n const pointers = options.pointers ?? new PlanPointerService();\n const pointerFor = (): ReturnType<PlanPointerPort['read']> =>\n options.sessionId === undefined ? undefined : pointers.read(options.sessionId);\n\n app.get(routes.current.path, async (context) => {\n const pointer = pointerFor();\n if (pointer === undefined) return context.json({ error: NO_PLAN }, 404);\n const view: PlanDetailView = {\n path: pointer.path,\n title: pointer.title,\n writtenAt: pointer.writtenAt,\n ...(pointer.planId === undefined ? {} : { planId: pointer.planId }),\n ...(await readPlan(pointer.path)),\n };\n return context.json(view);\n });\n\n app.put(routes.save.path, async (context) => {\n let request: unknown;\n try {\n request = await context.req.json();\n } catch {\n return context.json({ error: 'The save body is not JSON.' }, 400);\n }\n if (\n typeof request !== 'object' ||\n request === null ||\n Array.isArray(request) ||\n !('content' in request) ||\n typeof request.content !== 'string' ||\n !('expectedHash' in request) ||\n typeof request.expectedHash !== 'string'\n ) {\n return context.json({ error: 'A save carries the content to write and the hash it was read at.' }, 400);\n }\n const pointer = pointerFor();\n if (pointer === undefined) return context.json({ error: NO_PLAN }, 404);\n\n if (Buffer.byteLength(request.content, 'utf8') > MAX_PLAN_BYTES) {\n return context.json({ error: `The plan is larger than ${MAX_PLAN_BYTES} bytes.` }, 413);\n }\n try {\n const saved = await overwritePlanFile(pointer.path, request.content, request.expectedHash, MAX_PLAN_BYTES);\n if (saved === undefined) return context.json({ error: NOT_A_FILE }, 409);\n if (!saved.written) return context.json({ error: STALE_SAVE, hash: saved.hash }, 409);\n return context.json({ hash: saved.hash } satisfies PlanSaveView);\n } catch {\n return context.json({ error: NOT_A_FILE }, 409);\n }\n });\n\n return app;\n}\n\n/** The named export a host imports from this package's built entry. */\nexport const api: PoApi = {\n basePath: API_BASE_PATH,\n start(context: PoApiContext): PoApiHandler {\n const app = createPlanApi({\n ...(context.sessionId === undefined ? {} : { sessionId: context.sessionId }),\n pointers: new PlanPointerService({\n env: context.environment ?? {},\n homeDirectory: context.homeDirectory,\n }),\n });\n return {\n fetch: (request) => app.fetch(request),\n // Nothing outlives a request here; an API that starts a watch or a timer\n // tears it down in this callback.\n close: () => undefined,\n };\n },\n};\n","import type { Context } from '@deepseek-ai/cordis';\n\nexport const PO_SUBAGENT_POLICY_SERVICE = 'po/subagent-policy';\n\nexport interface SubagentPolicy {\n owner: string;\n allowedTools?: string[];\n requiredTools?: string[];\n allowMcpTools?: boolean;\n denyExtensions?: boolean;\n}\n\nexport interface SubagentPolicyHandle {\n update(policy: SubagentPolicy): void;\n dispose(): void;\n}\n\nexport interface PoSubagentPolicyService {\n register(policy: SubagentPolicy): SubagentPolicyHandle;\n}\n\ntype StructuralServiceContext = {\n get(name: string): unknown;\n};\n\nexport function readPoSubagentPolicyService(ctx: Context): PoSubagentPolicyService | undefined {\n return (ctx as unknown as StructuralServiceContext).get(PO_SUBAGENT_POLICY_SERVICE) as\n | PoSubagentPolicyService\n | undefined;\n}\n","import { Buffer } from 'node:buffer';\nimport path from 'node:path';\n\nimport type { IPoConfigService } from '@agimon-ai/po-config';\nimport { PO_CONFIG_SERVICE } from '@agimon-ai/po-core/config';\nimport { type PoHeadlessHostService, type PoHeadlessModelSettings } from '@agimon-ai/po-core/headless';\nimport { type PoHeadlessToolResult } from '@agimon-ai/po-core/headless';\nimport { readPackageResource, type PoServerSessionPlugin } from '@agimon-ai/po-core/serverPlugin';\nimport { PO_VOICE_AUTO_MODE_ID } from '@agimon-ai/po-core/voiceTools';\nimport { serverMinorModes } from '@agimon-ai/po-minor-mode';\nimport { defineMinorMode, type MinorModeOwner, type MinorModeState } from '@agimon-ai/po-minor-mode';\nimport type { Context } from '@deepseek-ai/cordis';\n\nimport { writePlanParameters } from '../../schemas/mcpTools';\nimport { 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 PO_SUBAGENT_POLICY_SERVICE,\n readPoSubagentPolicyService,\n type SubagentPolicy,\n type SubagentPolicyHandle,\n} from '../optionalTeamServices';\nimport { readPlanText, writeNewPlanFile } from '../planFiles';\nimport { PlanPointerService } from '../planPointer';\n\nconst PLAN_ARTIFACT_SERVICE = 'po/artifacts';\ninterface PlanArtifactStorage {\n rootForSession(\n sessionId: string,\n environment: Readonly<Record<string, string | undefined>>,\n ): Promise<string | undefined>;\n}\n\nconst RECORD_DEBUG_EVIDENCE_TOOL = 'record_debug_evidence';\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/** This host supports UI review without narrated decisions. */\nexport const PLAN_SERVER_CAPABILITIES: PlanHostCapabilities = {\n completePlanTakesDecision: false,\n narratesReview: false,\n};\nconst PLAN_SUBAGENT_POLICY_OWNER = '@agimon-ai/po-plan';\n// Subagents keep every tool but `edit` and `write`, which the plan-mode restriction already withdraws from the\n// planner and, through it, from its native Team subagents.\nconst PLAN_SUBAGENT_POLICY: SubagentPolicy = {\n owner: PLAN_SUBAGENT_POLICY_OWNER,\n allowMcpTools: true,\n denyExtensions: false,\n};\n\nfunction serverPlanSubagentPolicy(host: PoHeadlessHostService): (context: Context) => void {\n return (context) => {\n context.inject([PO_SUBAGENT_POLICY_SERVICE], (child) => {\n const service = readPoSubagentPolicyService(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: PoHeadlessHostService): boolean {\n return (host.context.selection.state?.['minor-mode'] ?? []).includes(PLAN_MODE_ID);\n}\n\nfunction output(value: unknown, isError = false): PoHeadlessToolResult {\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: PoHeadlessHostService,\n options: { homeDirectory?: string },\n): Omit<PoServerSessionPlugin, 'tools'> & { tools: Parameters<PoHeadlessHostService['registerTool']>[0][] } {\n let flavor: PlanningFlavor = 'normal';\n let debugEvidence: DebugEvidencePacket | undefined;\n let modeOwner: MinorModeOwner | undefined;\n const selected = (): boolean => modeSelected(host);\n let configService: IPoConfigService | undefined;\n let artifactStorage: PlanArtifactStorage | undefined;\n const planningConfig = () => configService?.getSnapshot().settings.modes?.planning;\n // One resolution for both the prompt and write_plan.\n const plansDirectory = async (): Promise<string> => {\n const root = await artifactStorage?.rootForSession(host.context.sessionId, host.context.environment);\n if (root !== undefined) return path.join(root, 'plans');\n if (options.homeDirectory === undefined) throw new Error('Po needs a home directory to choose the plans folder.');\n return resolvePlanningPlansDirectory(\n planningConfig()?.plansDirectory,\n host.context.repoRoot,\n options.homeDirectory,\n );\n };\n const pointers = new PlanPointerService({\n env: host.context.environment,\n homeDirectory: options.homeDirectory,\n });\n /**\n * The activity group's line, and the pointer its panel reads.\n *\n * A cockpit drives this server plugin 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 const status = formatPlanStatus(title, planStampOf(writtenAt));\n try {\n pointers.write(host.context.sessionId, { path: filePath, title, writtenAt });\n host.context.client.setStatus(PLAN_STATUS_KEY, status);\n } catch (error) {\n // The plan is on disk either way, so say so in the status line rather than fail the write that produced it.\n const reason = error instanceof Error ? error.message : String(error);\n host.context.client.setStatus(PLAN_STATUS_KEY, `${status} (plan panel not updated: ${reason})`);\n }\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 PoHeadlessModelSettings);\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 = planningConfig()?.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<PoHeadlessModelSettings> = {};\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 === 'fable') flavor = 'normal';\n else if (candidate === 'normal' || candidate === 'debug' || candidate === 'code-review') 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/po-plan',\n id: PLAN_MODE_ID,\n label: 'Plan',\n description: 'Read-only planning with normal, debug, and code review 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: 'code-review', label: 'Code Review' },\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 !== 'code-review')\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: [\n serverMinorModes([modeOwner]),\n serverPlanSubagentPolicy(host),\n (context) => {\n context.inject([PO_CONFIG_SERVICE], (child) => {\n const service = child.get(PO_CONFIG_SERVICE) as IPoConfigService;\n configService = service;\n return () => {\n if (configService === service) configService = undefined;\n };\n });\n context.inject([PLAN_ARTIFACT_SERVICE], (child) => {\n const storage = child.get(PLAN_ARTIFACT_SERVICE) as PlanArtifactStorage;\n artifactStorage = storage;\n return () => {\n if (artifactStorage === storage) artifactStorage = undefined;\n };\n });\n },\n ],\n resources: [\n {\n when: { state: { 'minor-mode': PLAN_MODE_ID }, attribution: { kind: 'minor', mode: PLAN_MODE_ID } },\n name: 'po-use-plan',\n kind: 'skill',\n read: () => readPackageResource(import.meta.url, 'src/prompts/po-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: WRITE_PLAN_TOOL,\n label: 'Write Plan',\n mainAgentOnly: true,\n description: 'Save the supplied Markdown plan, or the plan already presented in the local session.',\n parameters: writePlanParameters,\n executionMode: 'serial',\n async execute(toolCallId, parameters, signal) {\n try {\n signal?.throwIfAborted();\n // Remote callers supply their own visible Markdown. Only local calls may use the journal.\n let content =\n typeof parameters === 'object' && parameters !== null && 'markdown' in parameters\n ? parameters.markdown\n : undefined;\n if (content !== undefined && typeof content !== 'string')\n throw new Error('Plan Markdown must be a string.');\n if (content === undefined) {\n for await (const entry of host.context.session.entriesNewestFirst({ type: 'message' })) {\n const message = entry.message as { role?: unknown; content?: unknown } | undefined;\n if (message?.role !== 'assistant' || !Array.isArray(message.content)) continue;\n if (\n !message.content.some((block: unknown) => {\n if (!block || typeof block !== 'object') return false;\n const call = block as { type?: unknown; id?: unknown; name?: unknown };\n return call.type === 'toolCall' && call.id === toolCallId && call.name === WRITE_PLAN_TOOL;\n })\n )\n continue;\n content = visiblePlanForToolCall([entry], toolCallId);\n break;\n }\n }\n if (content === undefined) {\n if (host.context.selection.state?.['minor-mode']?.includes(PO_VOICE_AUTO_MODE_ID))\n throw new Error('Supply Markdown to write_plan during autonomous Voice; UI input is unavailable.');\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 (typeof content !== 'string' || !content.trim())\n throw new Error('No implementation plan content was supplied.');\n const directory = await plansDirectory();\n const title = planTitleOf(content);\n const writtenAt = new Date().toISOString();\n const filename = `${planTitleSlug(content)}-${Date.now()}.md`;\n const filePath = await writeNewPlanFile(directory, filename, `${content.trim()}\\n`);\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 const saved = { path: filePath, written: true };\n return { ...output(saved), structuredContent: saved };\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 mainAgentOnly: true,\n description:\n 'Request exit-or-continue approval in the local Po UI. Requires the user to interact there; this is not a ChatGPT approval dialog.',\n // No decision parameter: this server plugin 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 if (host.context.selection.state?.['minor-mode']?.includes(PO_VOICE_AUTO_MODE_ID))\n throw new Error(\n 'UI plan review is unavailable during autonomous Voice. Wait for explicit user direction.',\n );\n // The client answers with the option's `value`, so the decision travels with the\n // question instead of the server plugin 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 ? planExitText(hasTaskTracking(host)) : 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 = await plansDirectory();\n const sections = [\n buildPlanModeBasePrompt(directory, PLAN_SERVER_CAPABILITIES),\n buildFlavorPlanningPrompt(flavor, directory, debugEvidence),\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 readPlanText(document.path);\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 // Older harness sessions persisted plan-mode context messages; keep them out of the model context.\n event: 'context',\n async handle(event) {\n if (!Array.isArray(event.messages)) return;\n return {\n messages: event.messages.filter(\n (message) => (message as { customType?: string }).customType !== PLAN_MODE_CONTEXT,\n ),\n };\n },\n },\n ],\n };\n}\n\nconst DEFAULT_PLAN_TITLE = 'implementation-plan';\nconst PLAN_TITLE_MAX_LENGTH = 64;\nconst DEBUG_EVIDENCE_MAX_BYTES = 32 * 1024;\nconst DEBUG_EVIDENCE_MAX_TEXT_BYTES = 4 * 1024;\nconst DEBUG_EVIDENCE_MAX_ITEMS = 32;\nexport const PLAN_MODE_CONTEXT = 'agent-harness-plan-mode-context';\nexport const PLAN_EXIT_APPROVED_TEXT =\n 'The user approved exiting plan mode. Full tool access is restored. Begin implementing the approved plan.';\nexport const PLAN_CONTINUE_TEXT = 'The user chose to continue planning. Remain read-only and refine the plan.';\nexport const PLAN_EXIT_TASK_TRACKING_TEXT =\n 'Track implementation progress with po-task: create one task per plan step (the `tasks` code-mode namespace, or the `task` tool without code mode), mark each in progress when you start it and done when it is verified.';\n\n/** po-task registers the `task` tool and its code-mode guide; either one means the session can track tasks. */\nfunction hasTaskTracking(host: PoHeadlessHostService): boolean {\n return host\n .inspectCapabilities()\n .capabilities.some(\n (capability) =>\n (capability.kind === 'tool' && capability.name === 'task') ||\n (capability.kind === 'skill' && capability.name === 'codemode-how-to-task'),\n );\n}\n\nexport function planExitText(taskTracking: boolean): string {\n return taskTracking ? `${PLAN_EXIT_APPROVED_TEXT} ${PLAN_EXIT_TASK_TRACKING_TEXT}` : PLAN_EXIT_APPROVED_TEXT;\n}\n/** The plan's own heading, as the status line and the pointer record word it. */\nexport function planTitleOf(markdown: string): string {\n return markdown.match(/^#\\s+(.+?)\\s*$/m)?.[1] ?? DEFAULT_PLAN_TITLE;\n}\n\nexport function planTitleSlug(markdown: string): string {\n const heading = planTitleOf(markdown);\n return (\n heading\n .normalize('NFKD')\n .replace(/[\\u0300-\\u036f]/g, '')\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '')\n .slice(0, PLAN_TITLE_MAX_LENGTH) || DEFAULT_PLAN_TITLE\n );\n}\n\n/**\n * How the status line words when a plan was written. A malformed timestamp is\n * shown as it came rather than as an invalid date: the stamp's other job is to\n * change on a rewrite, and it can still do that.\n */\nexport function planStampOf(writtenAt: string): string {\n const at = new Date(writtenAt);\n return Number.isNaN(at.getTime()) ? writtenAt : at.toLocaleTimeString();\n}\n\nexport function visiblePlanForToolCall(entries: readonly unknown[], toolCallId: string): string | undefined {\n for (let index = entries.length - 1; index >= 0; index -= 1) {\n const entry = entries[index];\n if (!entry || typeof entry !== 'object') continue;\n const candidate = entry as {\n type?: string;\n message?: { role?: string; content?: unknown };\n };\n if (candidate.type !== 'message' || candidate.message?.role !== 'assistant') continue;\n if (!Array.isArray(candidate.message.content)) continue;\n\n const toolIndex = candidate.message.content.findIndex((block) => {\n if (!block || typeof block !== 'object') return false;\n const content = block as { type?: string; id?: string; name?: string };\n return content.type === 'toolCall' && content.id === toolCallId && content.name === WRITE_PLAN_TOOL;\n });\n if (toolIndex < 0) continue;\n\n const plan = candidate.message.content\n .slice(0, toolIndex)\n .flatMap((block) => {\n if (!block || typeof block !== 'object') return [];\n const content = block as { type?: string; text?: unknown };\n return content.type === 'text' && typeof content.text === 'string' ? [content.text] : [];\n })\n .join('\\n\\n')\n .trim();\n return plan || undefined;\n }\n return undefined;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction isString(value: unknown): value is string {\n return typeof value === 'string';\n}\n\nfunction boundedDebugText(value: unknown, field: string, required: boolean): string {\n if (!isString(value)) throw new Error(`${field} must be a string.`);\n const text = value.trim();\n if (required && !text) throw new Error(`${field} must not be empty.`);\n if (Buffer.byteLength(text, 'utf8') > DEBUG_EVIDENCE_MAX_TEXT_BYTES) {\n throw new Error(`${field} exceeds the bounded debug evidence limit.`);\n }\n return text;\n}\n\nfunction boundedDebugList(value: unknown, field: string): string[] {\n if (!Array.isArray(value) || value.length > DEBUG_EVIDENCE_MAX_ITEMS) {\n throw new Error(`${field} must be a bounded string array.`);\n }\n return value.map((item, index) => boundedDebugText(item, `${field}[${index}]`, false));\n}\n\nfunction optionalDebugText(value: unknown, field: string): string {\n return value === undefined ? '' : boundedDebugText(value, field, false);\n}\n\nfunction optionalDebugList(value: unknown, field: string): string[] {\n return value === undefined ? [] : boundedDebugList(value, field);\n}\n\nfunction assertExactObjectKeys(value: Record<string, unknown>, keys: readonly string[], field: string): void {\n const allowed = new Set(keys);\n const unexpected = Object.keys(value).find((key) => !allowed.has(key));\n if (unexpected) throw new Error(`${field} contains unsupported field '${unexpected}'.`);\n}\n\nexport function parseDebugEvidencePacket(value: unknown): DebugEvidencePacket {\n if (!isRecord(value)) throw new Error('Debug evidence must be an object.');\n assertExactObjectKeys(\n value,\n [\n 'issue',\n 'expectedBehavior',\n 'reproductionAttempt',\n 'actualBehavior',\n 'logs',\n 'correlatedTraceEvidence',\n 'processOutput',\n 'browserConsoleEvidence',\n 'correlationIds',\n 'timestamps',\n 'verifiedFacts',\n 'hypotheses',\n 'unavailableEvidence',\n ],\n 'Debug evidence',\n );\n const packet: DebugEvidencePacket = {\n issue: boundedDebugText(value.issue, 'issue', true),\n expectedBehavior: optionalDebugText(value.expectedBehavior, 'expectedBehavior'),\n reproductionAttempt: optionalDebugText(value.reproductionAttempt, 'reproductionAttempt'),\n actualBehavior: optionalDebugText(value.actualBehavior, 'actualBehavior'),\n logs: optionalDebugList(value.logs, 'logs'),\n correlatedTraceEvidence: optionalDebugList(value.correlatedTraceEvidence, 'correlatedTraceEvidence'),\n processOutput: optionalDebugList(value.processOutput, 'processOutput'),\n browserConsoleEvidence: optionalDebugList(value.browserConsoleEvidence, 'browserConsoleEvidence'),\n correlationIds: optionalDebugList(value.correlationIds, 'correlationIds'),\n timestamps: optionalDebugList(value.timestamps, 'timestamps'),\n verifiedFacts: optionalDebugList(value.verifiedFacts, 'verifiedFacts'),\n hypotheses: optionalDebugList(value.hypotheses, 'hypotheses'),\n unavailableEvidence: optionalDebugList(value.unavailableEvidence, 'unavailableEvidence'),\n };\n if (Buffer.byteLength(JSON.stringify(packet), 'utf8') > DEBUG_EVIDENCE_MAX_BYTES) {\n throw new Error('Debug evidence exceeds the bounded packet size.');\n }\n return packet;\n}\n","import { defineRoot, type RootDeclaration } from '@agimon-ai/po-core/extensionFile';\nimport type { PoServerPluginContext, PoServerSessionPlugin } from '@agimon-ai/po-core/serverPlugin';\n\nimport { mountMcpTools } from '../../../../services/mcpTools';\nimport { api } from '../../../../services/planApi';\nimport { createPlanServerSession } from '../../../../services/planServerSession';\n\nexport default defineRoot(\n ({ agent, host }: PoServerPluginContext): RootDeclaration<PoServerSessionPlugin, PoServerPluginContext> => {\n const value = {\n api: [api],\n ...(agent\n ? createPlanServerSession(agent, {\n homeDirectory: host.context.homeDirectory ?? host.context.environment?.HOME,\n })\n : {}),\n };\n return {\n value,\n services: [...(value.services ?? []), ...(value.tools?.length ? [mountMcpTools(value.tools)] : [])],\n };\n },\n);\n","import { defineRoute, type WithRoot } from '@agimon-ai/po-core/extensionFile';\n\ntype Root = Awaited<ReturnType<typeof import('../../root.server').default>>['value'];\n\nexport default defineRoute((context: WithRoot<unknown, Root>) => context.root.api![0]!);\n","import { defineRoutedContribution, type WithRoot } from '@agimon-ai/po-core/extensionFile';\n\ntype Root = Awaited<ReturnType<typeof import('../root.server').default>>['value'];\n\nexport default defineRoutedContribution((context: WithRoot<unknown, Root>) => context.root.hooks?.[0], {\n cardinality: 'optional',\n});\n","import { defineRoutedContribution, type WithRoot } from '@agimon-ai/po-core/extensionFile';\n\ntype Root = Awaited<ReturnType<typeof import('../root.server').default>>['value'];\n\nexport default defineRoutedContribution((context: WithRoot<unknown, Root>) => context.root.hooks?.[2], {\n cardinality: 'optional',\n});\n","import { defineRoutedContribution, type WithRoot } from '@agimon-ai/po-core/extensionFile';\n\ntype Root = Awaited<ReturnType<typeof import('../root.server').default>>['value'];\n\nexport default defineRoutedContribution((context: WithRoot<unknown, Root>) => context.root.hooks?.[1], {\n cardinality: 'optional',\n});\n","import { defineRoutedContribution, type WithRoot } from '@agimon-ai/po-core/extensionFile';\n\ntype Root = Awaited<ReturnType<typeof import('../root.server').default>>['value'];\n\nexport default defineRoutedContribution((context: WithRoot<unknown, Root>) => context.root.resources?.[0], {\n cardinality: 'optional',\n});\n","import { defineToolRestriction } from '@agimon-ai/po-core/extensionFile';\nimport type { PoHeadlessToolRestriction } from '@agimon-ai/po-core/headless';\nimport { PO_VOICE_AUTO_MODE_ID } from '@agimon-ai/po-core/voiceTools';\n\nexport default defineToolRestriction({\n when: { state: { 'minor-mode': PO_VOICE_AUTO_MODE_ID } },\n excludedTools: ['complete_plan'],\n} satisfies PoHeadlessToolRestriction);\n","import { defineToolRestriction } from '@agimon-ai/po-core/extensionFile';\nimport type { PoHeadlessToolRestriction } from '@agimon-ai/po-core/headless';\n\nexport default defineToolRestriction({\n when: { state: { 'minor-mode': 'plan' } },\n excludedTools: ['edit', 'write'],\n} satisfies PoHeadlessToolRestriction);\n","import { defineRoutedContribution, type WithRoot } from '@agimon-ai/po-core/extensionFile';\n\ntype Root = Awaited<ReturnType<typeof import('../root.server').default>>['value'];\n\nexport default defineRoutedContribution((context: WithRoot<unknown, Root>) => context.root.tools?.[2], {\n cardinality: 'optional',\n});\n","import { defineRoutedContribution, type WithRoot } from '@agimon-ai/po-core/extensionFile';\n\ntype Root = Awaited<ReturnType<typeof import('../root.server').default>>['value'];\n\nexport default defineRoutedContribution((context: WithRoot<unknown, Root>) => context.root.tools?.[0], {\n cardinality: 'optional',\n});\n","import { defineRoutedContribution, type WithRoot } from '@agimon-ai/po-core/extensionFile';\n\ntype Root = Awaited<ReturnType<typeof import('../root.server').default>>['value'];\n\nexport default defineRoutedContribution((context: WithRoot<unknown, Root>) => context.root.tools?.[1], {\n cardinality: 'optional',\n});\n","// Generated by @agimon-ai/po-build. Do not edit by hand.\nimport { defineServerPlugin, type PoServerSessionPlugin } from '@agimon-ai/po-core/serverPlugin';\nimport { composeServerProviderOverrides } from '@agimon-ai/po-core/serverPlugin';\nimport { composeRootHooks, composeRootInjects } from '@agimon-ai/po-core/extensionFile';\n\nimport rootSession from '../src/extensions/workspaces/sessions/(backend)/root.server';\nimport apiRoute from '../src/extensions/workspaces/sessions/(backend)/api/plans/route.server';\nimport hookBeforeAgentStart from '../src/extensions/workspaces/sessions/(backend)/hook/beforeAgentStart.server';\nimport hookContext from '../src/extensions/workspaces/sessions/(backend)/hook/context.server';\nimport hookSessionStart from '../src/extensions/workspaces/sessions/(backend)/hook/sessionStart.server';\nimport resourcePoUsePlan from '../src/extensions/workspaces/sessions/(backend)/resource/poUsePlan.server';\nimport toolRestrictionPlan from '../src/extensions/workspaces/sessions/(backend)/tool-restriction/plan.server';\nimport toolRestrictionPlanReadOnly from '../src/extensions/workspaces/sessions/(backend)/tool-restriction/planReadOnly.server';\nimport toolCompletePlan from '../src/extensions/workspaces/sessions/(backend)/tool/completePlan.server';\nimport toolRecordDebugEvidence from '../src/extensions/workspaces/sessions/(backend)/tool/recordDebugEvidence.server';\nimport toolWritePlan from '../src/extensions/workspaces/sessions/(backend)/tool/writePlan.server';\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;\n/* v8 ignore next -- generated normalization branch */\nconst defined = <T>(values: readonly (T | undefined)[]): T[] =>\n values.filter((value): value is T => value !== undefined);\nconst via = <T>(identity: Record<string, unknown>, value: unknown): T =>\n (value === undefined ? undefined : { ...identity, ...(value as object) }) as T;\n\nexport const serverPlugin = defineServerPlugin({\n name: '@agimon-ai/po-plan',\n providerOverrides: composeServerProviderOverrides(rootSession),\n injects: composeRootInjects({ session: [rootSession] }),\n session: async (context) => {\n const scopeSession = await context.scopeRoot('session', rootSession, context);\n const contextSession = { ...context, root: scopeSession.value };\n return {\n ...composeRootHooks(scopeSession),\n get api(): PoServerSessionPlugin['api'] { return [at(apiRoute, contextSession)]; },\n get hooks(): PoServerSessionPlugin['hooks'] { return defined([via({ event: 'before_agent_start' }, at(hookBeforeAgentStart, contextSession)), via({ event: 'context' }, at(hookContext, contextSession)), via({ event: 'session_start' }, at(hookSessionStart, contextSession))]); },\n get resources(): PoServerSessionPlugin['resources'] { return defined([at(resourcePoUsePlan, contextSession)]); },\n get toolRestrictions(): PoServerSessionPlugin['toolRestrictions'] { return [at(toolRestrictionPlan, contextSession), at(toolRestrictionPlanReadOnly, contextSession)]; },\n get tools(): PoServerSessionPlugin['tools'] { return defined([via({ name: 'complete_plan' }, at(toolCompletePlan, contextSession)), via({ name: 'record_debug_evidence' }, at(toolRecordDebugEvidence, contextSession)), via({ name: 'write_plan' }, at(toolWritePlan, contextSession))]); },\n services: [...(scopeSession.services ?? [])],\n get activities(): PoServerSessionPlugin['activities'] { return [...(scopeSession.activities ?? [])]; },\n };\n },\n});\n\nexport default serverPlugin;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"server.mjs","names":["isRecord","Buffer","rootSession","apiRoute","hookBeforeAgentStart","hookContext","hookSessionStart","resourcePoUsePlan","toolRestrictionPlan","toolRestrictionPlanReadOnly","toolCompletePlan","toolRecordDebugEvidence","toolWritePlan"],"sources":["../../src/schemas/mcpTools.ts","../../src/services/mcpTools/index.ts","../../src/services/planPointer/index.ts","../../src/types/apiRoutes.ts","../../src/types/planApi.ts","../../src/services/planFiles/index.ts","../../src/services/planApi/index.ts","../../src/services/optionalTeamServices/index.ts","../../src/services/planServerSession/index.ts","../../src/extensions/workspaces/sessions/(backend)/root.server.ts","../../src/extensions/workspaces/sessions/(backend)/api/plans/route.server.ts","../../src/extensions/workspaces/sessions/(backend)/hook/beforeAgentStart.server.ts","../../src/extensions/workspaces/sessions/(backend)/hook/context.server.ts","../../src/extensions/workspaces/sessions/(backend)/hook/sessionStart.server.ts","../../src/extensions/workspaces/sessions/(backend)/resource/poUsePlan.server.ts","../../src/extensions/workspaces/sessions/(backend)/tool-restriction/plan.server.ts","../../src/extensions/workspaces/sessions/(backend)/tool-restriction/planReadOnly.server.ts","../../src/extensions/workspaces/sessions/(backend)/tool/completePlan.server.ts","../../src/extensions/workspaces/sessions/(backend)/tool/recordDebugEvidence.server.ts","../../src/extensions/workspaces/sessions/(backend)/tool/writePlan.server.ts","../../generated/server.ts"],"sourcesContent":["import { Type } from 'typebox';\n\nconst markdown = Type.String({ minLength: 1 });\n\nexport const remoteWritePlanParameters = Type.Object({ markdown }, { additionalProperties: false });\nexport const writePlanParameters = Type.Object({ markdown: Type.Optional(markdown) }, { additionalProperties: false });\n","import type { PoHeadlessTool } from '@agimon-ai/po-core/headless';\nimport type { PoMcpPluginContext } from '@agimon-ai/po-core/mcpFacet';\nimport type { Context } from '@deepseek-ai/cordis';\n\nimport { remoteWritePlanParameters } from '../../schemas/mcpTools';\n\nexport const PLAN_MCP_TOOLS_SERVICE = 'po/plan-mcp-tools';\n\nconst MODE_ID = 'plan';\nconst MODE_LABEL = 'Plan';\n\nexport interface McpToolCatalog {\n get(name: string): PoHeadlessTool | undefined;\n names(): readonly string[];\n}\n\nexport function mountMcpTools(tools: readonly PoHeadlessTool[]) {\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: PoMcpPluginContext, name: string): PoHeadlessTool {\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 const writesPlan = name === 'write_plan';\n return {\n ...tool,\n ...(writesPlan\n ? {\n description:\n 'Save the supplied Markdown plan in the bound session. Requires active Plan mode. Saving does not approve implementation.',\n parameters: remoteWritePlanParameters,\n annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: false },\n }\n : {}),\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 if (\n writesPlan &&\n (typeof parameters !== 'object' ||\n parameters === null ||\n !('markdown' in parameters) ||\n typeof parameters.markdown !== 'string' ||\n !parameters.markdown.trim())\n ) {\n throw new Error(\n 'Supply the complete visible plan in the markdown argument. Remote calls cannot read the ChatGPT transcript.',\n );\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 { createHash } from 'node:crypto';\nimport fs from 'node:fs/promises';\nimport os from 'node:os';\nimport path from 'node:path';\n\nimport { piAgentDirectory } from '@agimon-ai/po-core/piSettings';\n\nimport type { PlanPointerRecord } from '../../types/planApi';\nimport type { PlanPointerPort } from '../../types/planPointer';\n\n/**\n * Record and read where write_plan left this session's plan file.\n *\n * Pi already gives every session an agent directory, so the records live under\n * it rather than in a second place a reader would have to know about. The\n * posture matches the plan file itself, 0700 on the directory and 0600 on the\n * record: a plan is a private working document, not something to leave\n * world-readable in a shared home.\n */\n\nconst POINTER_DIRECTORY = 'po-plan';\nconst PRIVATE_DIRECTORY_MODE = 0o700;\nconst PRIVATE_FILE_MODE = 0o600;\n\nexport interface NodePlanPointerOptions {\n env?: NodeJS.ProcessEnv;\n homeDirectory?: string;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nexport class PlanPointerService implements PlanPointerPort {\n private readonly env: NodeJS.ProcessEnv;\n private readonly homeDirectory: string;\n\n constructor(options: NodePlanPointerOptions = {}) {\n this.env = options.env ?? {};\n this.homeDirectory = options.homeDirectory ?? os.homedir();\n }\n\n /** Where the records live; exposed so a test can look without guessing. */\n directory(): string {\n return path.join(piAgentDirectory(this.env, this.homeDirectory), POINTER_DIRECTORY);\n }\n\n /**\n * The record for one session. The id is hashed rather than used as written:\n * it reaches here from a host that may word it however it likes, and a\n * filename is not the place to find that out.\n */\n pathFor(sessionId: string): string {\n return path.join(this.directory(), `${this.hash(sessionId)}.json`);\n }\n\n async read(sessionId: string): Promise<PlanPointerRecord | undefined> {\n let raw: string;\n try {\n raw = await fs.readFile(this.pathFor(sessionId), 'utf8');\n } catch {\n return undefined;\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n return undefined;\n }\n if (!isRecord(parsed)) return undefined;\n const { path: planPath, title, writtenAt, planId } = parsed;\n // A record half-written by an interrupted run reads as no plan at all,\n // which is a state the cockpit already handles.\n if (typeof planPath !== 'string' || !path.isAbsolute(planPath)) return undefined;\n if (typeof title !== 'string' || typeof writtenAt !== 'string') return undefined;\n return { path: planPath, title, writtenAt, ...(typeof planId === 'string' ? { planId } : {}) };\n }\n\n async write(sessionId: string, record: PlanPointerRecord): Promise<void> {\n await fs.mkdir(this.directory(), { recursive: true, mode: PRIVATE_DIRECTORY_MODE });\n await fs.writeFile(this.pathFor(sessionId), JSON.stringify(record), { mode: PRIVATE_FILE_MODE });\n }\n\n async clear(sessionId: string): Promise<void> {\n await fs.rm(this.pathFor(sessionId), { force: true });\n }\n\n private hash(value: string): string {\n return createHash('sha256').update(value).digest('hex').slice(0, 16);\n }\n}\n","import type { ApiResponseOf, ApiRoutes } from '@agimon-ai/po-core/web';\n\nimport type { PlanDetailView, PlanSaveView } from './planApi';\n\n/**\n * This package's routes, as data.\n *\n * No scope and no base path. The build reads both off\n * `src/extensions/workspaces/sessions/(backend)/api/plans/`, so the mount is\n * stated once, by the folder that creates it. The paths used to be written\n * here as helpers, again in the contract, and again inside every URL the page\n * built, and the copies were free to disagree.\n *\n * This is the one file the routes, the Hono app and the browser all read, so a\n * path cannot move on one side alone.\n *\n * Neither route takes a query or a path parameter: a session has one current\n * plan, so `current` answers it and `save` takes the manual write back.\n */\nconst routes = {\n current: {\n method: 'GET',\n path: '/current',\n response: undefined as unknown as ApiResponseOf<PlanDetailView>,\n },\n save: {\n method: 'PUT',\n path: '/content',\n response: undefined as unknown as ApiResponseOf<PlanSaveView>,\n },\n} as const satisfies ApiRoutes;\n\nexport default routes;\n","/**\n * The wire vocabulary this package's API shares with whatever calls it.\n *\n * It lives under src/types because that is the one server root a browser\n * bundle may read: the routes answer with these shapes and the cockpit plugin\n * reads them, so neither half can drift from the other. The paths themselves\n * are not here; they are declared once in `./apiRoutes`, which the Hono app\n * and the generated client both read.\n *\n * Two routes, and neither takes a path. A session has one current plan, so\n * `current` answers it and `content` takes the manual save back; the file they\n * act on is the one write_plan recorded, never one the page named. That is\n * also why there is nothing to escape here: a reader cannot address a second\n * file through these URLs.\n */\n\n/** Where a host mounts this package's API; the segment after /api/plugins/. */\nexport const API_BASE_PATH = 'plans';\n\n/**\n * Footer status key the session publishes once a plan exists, which is what\n * makes the cockpit's activity group appear at all. It outlives plan mode: the\n * plan is most worth reading while the agent implements it.\n */\nexport const PLAN_STATUS_KEY = 'po-plan-document';\n\n/** The selector contract shared by complete_plan and its cockpit composer prompt. */\nexport const PLAN_REVIEW_TITLE = 'Plan complete. What would you like to do?';\nexport const EXIT_PLAN_MODE_CHOICE = 'Exit plan mode and start implementation';\nexport const CONTINUE_PLANNING_CHOICE = 'Continue planning';\nexport const EXIT_PLAN_DECISION = 'exit';\nexport const CONTINUE_PLAN_DECISION = 'continue';\n\n/**\n * The label the reader sees paired with the decision the tool acts on. A headless\n * client answers with `value`, so a server plugin never has to recognise the wording; Pi's\n * own `ui.select` carries labels only, so its half maps the label back itself.\n */\nexport const PLAN_REVIEW_CHOICES = [\n { label: EXIT_PLAN_MODE_CHOICE, value: EXIT_PLAN_DECISION },\n { label: CONTINUE_PLANNING_CHOICE, value: CONTINUE_PLAN_DECISION },\n] as const;\n\n/** The same options as bare labels, which is all Pi's select and the composer prompt need. */\nexport const PLAN_REVIEW_OPTIONS = PLAN_REVIEW_CHOICES.map((choice) => choice.label);\n\n/** Between the plan's title and the stamp that marks a rewrite. */\nconst STATUS_SEPARATOR = ' · ';\n\n/** Strips the colour a session may have themed a status with. */\nconst ANSI = /\u001b\\[[0-9;]*m/gu;\n\n/** The status line the session publishes; the stamp changes on every write. */\nexport function formatPlanStatus(title: string, stamp: string): string {\n return `${title}${STATUS_SEPARATOR}${stamp}`;\n}\n\n/** What a reader gets out of the status line. */\nexport interface PlanStatusView {\n title: string;\n /** When the plan was last written, as the session worded it; empty when it said nothing. */\n stamp: string;\n}\n\n/**\n * Reading the status line back. Statuses reach a plugin raw, so the colour a\n * session may have added comes off here. A title can itself contain the\n * separator, so the stamp is taken from the last one rather than the first.\n */\nexport function parsePlanStatus(raw: string | undefined): PlanStatusView | undefined {\n if (raw === undefined) return undefined;\n const text = raw.replace(ANSI, '').trim();\n if (text === '') return undefined;\n const cut = text.lastIndexOf(STATUS_SEPARATOR);\n if (cut === -1) return { title: text, stamp: '' };\n return { title: text.slice(0, cut).trim(), stamp: text.slice(cut + STATUS_SEPARATOR.length).trim() };\n}\n\n/**\n * What write_plan recorded about this session's plan.\n *\n * The absolute path is the point of it: the extension resolves the plans\n * directory against the harness root, which no other process knows, so it\n * writes down where the plan landed rather than leaving the API to derive a\n * location it would sometimes get wrong.\n */\nexport interface PlanPointerRecord {\n path: string;\n title: string;\n writtenAt: string;\n planId?: string;\n}\n\n/** The plan as it stands on disk right now, which is what the source view edits. */\nexport interface PlanDetailView {\n path: string;\n title: string;\n writtenAt: string;\n planId?: string;\n content: string;\n /** sha256 of the content; the token a save hands back to prove it is not stale. */\n hash: string;\n /** True when the plan is missing or past the size cap, so `content` is empty. */\n unavailable: boolean;\n reason?: string;\n}\n\n/** What the save route takes. */\nexport interface PlanSaveRequest {\n /** The hash the reader loaded; a mismatch means the plan moved underneath them. */\n expectedHash: string;\n content: string;\n}\n\n/** What the save route answers with. */\nexport interface PlanSaveView {\n hash: string;\n}\n\n/** What a route reports when it refuses; the page shows `error` verbatim. */\nexport interface PlanErrorView {\n error: string;\n /** On a stale save, the hash the plan actually has now. */\n hash?: string;\n}\n","import { createHash } from 'node:crypto';\nimport fs, { type FileHandle } from 'node:fs/promises';\nimport path from 'node:path';\n\nimport { fileMutationRunner, openGuardedFile } from '@agimon-ai/po-core/platform';\n\nexport async function writeNewPlanFile(directory: string, filename: string, content: string): Promise<string> {\n await fs.mkdir(directory, { recursive: true, mode: 0o700 });\n const planPath = path.join(directory, filename);\n await fs.writeFile(planPath, content, { encoding: 'utf8', flag: 'wx', mode: 0o600 });\n return planPath;\n}\n\nexport async function readPlanText(planPath: string): Promise<string> {\n return await fs.readFile(planPath, 'utf8');\n}\n\n/** The extra byte detects growth without ever reading or allocating the whole file. */\nasync function readBounded(handle: FileHandle, maxBytes: number): Promise<Buffer> {\n const stats = await handle.stat();\n if (!stats.isFile()) throw new Error('The plan is not a regular file.');\n const buffer = Buffer.alloc(maxBytes + 1);\n if (stats.size > maxBytes) return buffer;\n let offset = 0;\n while (offset < buffer.length) {\n const { bytesRead } = await handle.read(buffer, offset, buffer.length - offset, offset);\n if (bytesRead === 0) break;\n offset += bytesRead;\n }\n return buffer.subarray(0, offset);\n}\n\nexport async function readPlanBytes(planPath: string, maxBytes: number): Promise<Buffer> {\n const handle = await openGuardedFile(planPath);\n try {\n return await readBounded(handle, maxBytes);\n } finally {\n await handle.close();\n }\n}\n\n/** Compare and write the same open inode. The queue protects cooperating API writers, not external processes. */\nexport async function overwritePlanFile(\n planPath: string,\n content: string,\n expectedHash: string,\n maxBytes: number,\n): Promise<{ written: boolean; hash: string } | undefined> {\n const canonical = await fs.realpath(planPath);\n return await fileMutationRunner.forKey(canonical)(async () => {\n const handle = await openGuardedFile(planPath, { write: true });\n try {\n if ((await fs.realpath(planPath)) !== canonical) return undefined;\n const raw = await readBounded(handle, maxBytes);\n if (raw.length > maxBytes) return undefined;\n const hash = createHash('sha256').update(raw.toString('utf8'), 'utf8').digest('hex');\n if (hash !== expectedHash) return { written: false, hash };\n const opened = await handle.stat();\n const current = await fs.lstat(planPath);\n if (\n !opened.isFile() ||\n opened.nlink !== 1 ||\n !current.isFile() ||\n current.nlink !== 1 ||\n opened.dev !== current.dev ||\n opened.ino !== current.ino ||\n (await fs.realpath(planPath)) !== canonical\n )\n return undefined;\n await handle.writeFile(content, 'utf8');\n await handle.truncate(Buffer.byteLength(content, 'utf8'));\n return { written: true, hash: createHash('sha256').update(content, 'utf8').digest('hex') };\n } finally {\n await handle.close();\n }\n });\n}\n","import { createHash } from 'node:crypto';\n\nimport type { PoApi, PoApiContext, PoApiHandler } from '@agimon-ai/po-core/packageApi';\nimport { Hono } from 'hono';\n\nimport { PlanPointerService } from '../../services/planPointer';\nimport routes from '../../types/apiRoutes';\nimport { API_BASE_PATH, type PlanDetailView, type PlanSaveView } from '../../types/planApi';\nimport type { PlanPointerPort } from '../../types/planPointer';\nimport { overwritePlanFile, readPlanBytes } from '../planFiles';\n\n/**\n * This package's HTTP surface: the session's current plan, and the manual save.\n *\n * Each path comes from the route table the browser reads, so a route the page\n * calls and the route this serves cannot be spelled apart. Both are relative\n * to the mount its host gives it, so nothing here repeats where it was mounted. The host is one session's own server, which is what\n * makes the session id available without the page supplying it.\n *\n * Neither route takes a path. A session has exactly one current plan, and the\n * file both routes act on is the one the pointer names, so a page can address\n * the plan and nothing else; there is no traversal question to answer because\n * there is no path to traverse.\n *\n * The save refuses unless the reader proves they were looking at the content\n * that is still on disk. The agent may rewrite the plan while a tab is open,\n * and silently discarding whichever side finished second is the one outcome\n * worth ruling out.\n */\n\n/** Past this, the plan is reported rather than carried. */\nexport const MAX_PLAN_BYTES = 1024 * 1024;\n\nconst NO_PLAN = 'This session has not written a plan yet.';\nconst STALE_SAVE = 'The plan changed since it was opened.';\nconst NOT_A_FILE = 'The plan file is no longer a regular file, so it was not written.';\n\nexport interface PlanApiOptions {\n /** The session these routes answer for; absent when the host is the hub. */\n sessionId?: string;\n pointers?: PlanPointerPort;\n}\n\nfunction hashOf(content: string): string {\n return createHash('sha256').update(content, 'utf8').digest('hex');\n}\n\n/** Reads the plan as the source view will show it, or says why it cannot. */\nasync function readPlan(\n planPath: string,\n): Promise<Pick<PlanDetailView, 'content' | 'hash' | 'unavailable' | 'reason'>> {\n let raw: Buffer;\n try {\n raw = await readPlanBytes(planPath, MAX_PLAN_BYTES);\n } catch {\n return { content: '', hash: '', unavailable: true, reason: 'the plan file no longer exists' };\n }\n if (raw.byteLength > MAX_PLAN_BYTES) {\n return { content: '', hash: '', unavailable: true, reason: `the plan is larger than ${MAX_PLAN_BYTES} bytes` };\n }\n const content = raw.toString('utf8');\n return { content, hash: hashOf(content), unavailable: false };\n}\n\nexport function createPlanApi(options: PlanApiOptions = {}): Hono {\n const app = new Hono();\n const pointers = options.pointers ?? new PlanPointerService();\n const pointerFor = (): ReturnType<PlanPointerPort['read']> =>\n options.sessionId === undefined ? undefined : pointers.read(options.sessionId);\n\n app.get(routes.current.path, async (context) => {\n const pointer = await pointerFor();\n if (pointer === undefined) return context.json({ error: NO_PLAN }, 404);\n const view: PlanDetailView = {\n path: pointer.path,\n title: pointer.title,\n writtenAt: pointer.writtenAt,\n ...(pointer.planId === undefined ? {} : { planId: pointer.planId }),\n ...(await readPlan(pointer.path)),\n };\n return context.json(view);\n });\n\n app.put(routes.save.path, async (context) => {\n let request: unknown;\n try {\n request = await context.req.json();\n } catch {\n return context.json({ error: 'The save body is not JSON.' }, 400);\n }\n if (\n typeof request !== 'object' ||\n request === null ||\n Array.isArray(request) ||\n !('content' in request) ||\n typeof request.content !== 'string' ||\n !('expectedHash' in request) ||\n typeof request.expectedHash !== 'string'\n ) {\n return context.json({ error: 'A save carries the content to write and the hash it was read at.' }, 400);\n }\n const pointer = await pointerFor();\n if (pointer === undefined) return context.json({ error: NO_PLAN }, 404);\n\n if (Buffer.byteLength(request.content, 'utf8') > MAX_PLAN_BYTES) {\n return context.json({ error: `The plan is larger than ${MAX_PLAN_BYTES} bytes.` }, 413);\n }\n try {\n const saved = await overwritePlanFile(pointer.path, request.content, request.expectedHash, MAX_PLAN_BYTES);\n if (saved === undefined) return context.json({ error: NOT_A_FILE }, 409);\n if (!saved.written) return context.json({ error: STALE_SAVE, hash: saved.hash }, 409);\n return context.json({ hash: saved.hash } satisfies PlanSaveView);\n } catch {\n return context.json({ error: NOT_A_FILE }, 409);\n }\n });\n\n return app;\n}\n\n/** The named export a host imports from this package's built entry. */\nexport const api: PoApi = {\n basePath: API_BASE_PATH,\n start(context: PoApiContext): PoApiHandler {\n const app = createPlanApi({\n ...(context.sessionId === undefined ? {} : { sessionId: context.sessionId }),\n pointers: new PlanPointerService({\n env: context.environment ?? {},\n homeDirectory: context.homeDirectory,\n }),\n });\n return {\n fetch: (request) => app.fetch(request),\n // Nothing outlives a request here; an API that starts a watch or a timer\n // tears it down in this callback.\n close: () => undefined,\n };\n },\n};\n","import type { Context } from '@deepseek-ai/cordis';\n\nexport const PO_SUBAGENT_POLICY_SERVICE = 'po/subagent-policy';\n\nexport interface SubagentPolicy {\n owner: string;\n allowedTools?: string[];\n requiredTools?: string[];\n allowMcpTools?: boolean;\n denyExtensions?: boolean;\n}\n\nexport interface SubagentPolicyHandle {\n update(policy: SubagentPolicy): void;\n dispose(): void;\n}\n\nexport interface PoSubagentPolicyService {\n register(policy: SubagentPolicy): SubagentPolicyHandle;\n}\n\ntype StructuralServiceContext = {\n get(name: string): unknown;\n};\n\nexport function readPoSubagentPolicyService(ctx: Context): PoSubagentPolicyService | undefined {\n return (ctx as unknown as StructuralServiceContext).get(PO_SUBAGENT_POLICY_SERVICE) as\n | PoSubagentPolicyService\n | undefined;\n}\n","import { Buffer } from 'node:buffer';\nimport path from 'node:path';\n\nimport type { IPoConfigService } from '@agimon-ai/po-config';\nimport { PO_CONFIG_SERVICE } from '@agimon-ai/po-core/config';\nimport { type PoHeadlessHostService, type PoHeadlessModelSettings } from '@agimon-ai/po-core/headless';\nimport { type PoHeadlessToolResult } from '@agimon-ai/po-core/headless';\nimport { readPackageResource, type PoServerSessionPlugin } from '@agimon-ai/po-core/serverPlugin';\nimport { PO_VOICE_AUTO_MODE_ID } from '@agimon-ai/po-core/voiceTools';\nimport { serverMinorModes } from '@agimon-ai/po-minor-mode';\nimport { defineMinorMode, type MinorModeOwner, type MinorModeState } from '@agimon-ai/po-minor-mode';\nimport type { Context } from '@deepseek-ai/cordis';\n\nimport { writePlanParameters } from '../../schemas/mcpTools';\nimport { 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 PO_SUBAGENT_POLICY_SERVICE,\n readPoSubagentPolicyService,\n type SubagentPolicy,\n type SubagentPolicyHandle,\n} from '../optionalTeamServices';\nimport { readPlanText, writeNewPlanFile } from '../planFiles';\nimport { PlanPointerService } from '../planPointer';\n\nconst PLAN_ARTIFACT_SERVICE = 'po/artifacts';\ninterface PlanArtifactStorage {\n rootForSession(\n sessionId: string,\n environment: Readonly<Record<string, string | undefined>>,\n ): Promise<string | undefined>;\n}\n\nconst RECORD_DEBUG_EVIDENCE_TOOL = 'record_debug_evidence';\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/** This host supports UI review without narrated decisions. */\nexport const PLAN_SERVER_CAPABILITIES: PlanHostCapabilities = {\n completePlanTakesDecision: false,\n narratesReview: false,\n};\nconst PLAN_SUBAGENT_POLICY_OWNER = '@agimon-ai/po-plan';\n// Subagents keep every tool but `edit` and `write`, which the plan-mode restriction already withdraws from the\n// planner and, through it, from its native Team subagents.\nconst PLAN_SUBAGENT_POLICY: SubagentPolicy = {\n owner: PLAN_SUBAGENT_POLICY_OWNER,\n allowMcpTools: true,\n denyExtensions: false,\n};\n\nfunction serverPlanSubagentPolicy(host: PoHeadlessHostService): (context: Context) => void {\n return (context) => {\n context.inject([PO_SUBAGENT_POLICY_SERVICE], (child) => {\n const service = readPoSubagentPolicyService(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: PoHeadlessHostService): boolean {\n return (host.context.selection.state?.['minor-mode'] ?? []).includes(PLAN_MODE_ID);\n}\n\nfunction output(value: unknown, isError = false): PoHeadlessToolResult {\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: PoHeadlessHostService,\n options: { homeDirectory?: string },\n): Omit<PoServerSessionPlugin, 'tools'> & { tools: Parameters<PoHeadlessHostService['registerTool']>[0][] } {\n let flavor: PlanningFlavor = 'normal';\n let debugEvidence: DebugEvidencePacket | undefined;\n let modeOwner: MinorModeOwner | undefined;\n const selected = (): boolean => modeSelected(host);\n let configService: IPoConfigService | undefined;\n let artifactStorage: PlanArtifactStorage | undefined;\n const planningConfig = () => configService?.getSnapshot().settings.modes?.planning;\n // One resolution for both the prompt and write_plan.\n const plansDirectory = async (): Promise<string> => {\n const root = await artifactStorage?.rootForSession(host.context.sessionId, host.context.environment);\n if (root !== undefined) return path.join(root, 'plans');\n if (options.homeDirectory === undefined) throw new Error('Po needs a home directory to choose the plans folder.');\n return resolvePlanningPlansDirectory(\n planningConfig()?.plansDirectory,\n host.context.repoRoot,\n options.homeDirectory,\n );\n };\n const pointers = new PlanPointerService({\n env: host.context.environment,\n homeDirectory: options.homeDirectory,\n });\n /**\n * The activity group's line, and the pointer its panel reads.\n *\n * A cockpit drives this server plugin 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 = async (filePath: string, title: string, writtenAt: string): Promise<void> => {\n const status = formatPlanStatus(title, planStampOf(writtenAt));\n try {\n await pointers.write(host.context.sessionId, { path: filePath, title, writtenAt });\n host.context.client.setStatus(PLAN_STATUS_KEY, status);\n } catch (error) {\n // The plan is on disk either way, so say so in the status line rather than fail the write that produced it.\n const reason = error instanceof Error ? error.message : String(error);\n host.context.client.setStatus(PLAN_STATUS_KEY, `${status} (plan panel not updated: ${reason})`);\n }\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 PoHeadlessModelSettings);\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 = planningConfig()?.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<PoHeadlessModelSettings> = {};\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 === 'fable') flavor = 'normal';\n else if (candidate === 'normal' || candidate === 'debug' || candidate === 'code-review') 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/po-plan',\n id: PLAN_MODE_ID,\n label: 'Plan',\n description: 'Read-only planning with normal, debug, and code review 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: 'code-review', label: 'Code Review' },\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 !== 'code-review')\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: [\n serverMinorModes([modeOwner]),\n serverPlanSubagentPolicy(host),\n (context) => {\n context.inject([PO_CONFIG_SERVICE], (child) => {\n const service = child.get(PO_CONFIG_SERVICE) as IPoConfigService;\n configService = service;\n return () => {\n if (configService === service) configService = undefined;\n };\n });\n context.inject([PLAN_ARTIFACT_SERVICE], (child) => {\n const storage = child.get(PLAN_ARTIFACT_SERVICE) as PlanArtifactStorage;\n artifactStorage = storage;\n return () => {\n if (artifactStorage === storage) artifactStorage = undefined;\n };\n });\n },\n ],\n resources: [\n {\n when: { state: { 'minor-mode': PLAN_MODE_ID }, attribution: { kind: 'minor', mode: PLAN_MODE_ID } },\n name: 'po-use-plan',\n kind: 'skill',\n read: () => readPackageResource(import.meta.url, 'src/prompts/po-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: WRITE_PLAN_TOOL,\n label: 'Write Plan',\n mainAgentOnly: true,\n description: 'Save the supplied Markdown plan, or the plan already presented in the local session.',\n parameters: writePlanParameters,\n executionMode: 'serial',\n async execute(toolCallId, parameters, signal) {\n try {\n signal?.throwIfAborted();\n // Remote callers supply their own visible Markdown. Only local calls may use the journal.\n let content =\n typeof parameters === 'object' && parameters !== null && 'markdown' in parameters\n ? parameters.markdown\n : undefined;\n if (content !== undefined && typeof content !== 'string')\n throw new Error('Plan Markdown must be a string.');\n if (content === undefined) {\n for await (const entry of host.context.session.entriesNewestFirst({ type: 'message' })) {\n const message = entry.message as { role?: unknown; content?: unknown } | undefined;\n if (message?.role !== 'assistant' || !Array.isArray(message.content)) continue;\n if (\n !message.content.some((block: unknown) => {\n if (!block || typeof block !== 'object') return false;\n const call = block as { type?: unknown; id?: unknown; name?: unknown };\n return call.type === 'toolCall' && call.id === toolCallId && call.name === WRITE_PLAN_TOOL;\n })\n )\n continue;\n content = visiblePlanForToolCall([entry], toolCallId);\n break;\n }\n }\n if (content === undefined) {\n if (host.context.selection.state?.['minor-mode']?.includes(PO_VOICE_AUTO_MODE_ID))\n throw new Error('Supply Markdown to write_plan during autonomous Voice; UI input is unavailable.');\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 (typeof content !== 'string' || !content.trim())\n throw new Error('No implementation plan content was supplied.');\n const directory = await plansDirectory();\n const title = planTitleOf(content);\n const writtenAt = new Date().toISOString();\n const filename = `${planTitleSlug(content)}-${Date.now()}.md`;\n const filePath = await writeNewPlanFile(directory, filename, `${content.trim()}\\n`);\n await host.context.session.appendCustomEntry(PLAN_DOCUMENT, {\n path: filePath,\n content: content.trim(),\n title,\n writtenAt,\n });\n await announcePlanDocument(filePath, title, writtenAt);\n await selectPlan(true);\n const saved = { path: filePath, written: true };\n return { ...output(saved), structuredContent: saved };\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 mainAgentOnly: true,\n description:\n 'Request exit-or-continue approval in the local Po UI. Requires the user to interact there; this is not a ChatGPT approval dialog.',\n // No decision parameter: this server plugin 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 if (host.context.selection.state?.['minor-mode']?.includes(PO_VOICE_AUTO_MODE_ID))\n throw new Error(\n 'UI plan review is unavailable during autonomous Voice. Wait for explicit user direction.',\n );\n // The client answers with the option's `value`, so the decision travels with the\n // question instead of the server plugin 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 ? planExitText(hasTaskTracking(host)) : 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 = await plansDirectory();\n const sections = [\n buildPlanModeBasePrompt(directory, PLAN_SERVER_CAPABILITIES),\n buildFlavorPlanningPrompt(flavor, directory, debugEvidence),\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 readPlanText(document.path);\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 = await 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 // Older harness sessions persisted plan-mode context messages; keep them out of the model context.\n event: 'context',\n async handle(event) {\n if (!Array.isArray(event.messages)) return;\n return {\n messages: event.messages.filter(\n (message) => (message as { customType?: string }).customType !== PLAN_MODE_CONTEXT,\n ),\n };\n },\n },\n ],\n };\n}\n\nconst DEFAULT_PLAN_TITLE = 'implementation-plan';\nconst PLAN_TITLE_MAX_LENGTH = 64;\nconst DEBUG_EVIDENCE_MAX_BYTES = 32 * 1024;\nconst DEBUG_EVIDENCE_MAX_TEXT_BYTES = 4 * 1024;\nconst DEBUG_EVIDENCE_MAX_ITEMS = 32;\nexport const PLAN_MODE_CONTEXT = 'agent-harness-plan-mode-context';\nexport const PLAN_EXIT_APPROVED_TEXT =\n 'The user approved exiting plan mode. Full tool access is restored. Begin implementing the approved plan.';\nexport const PLAN_CONTINUE_TEXT = 'The user chose to continue planning. Remain read-only and refine the plan.';\nexport const PLAN_EXIT_TASK_TRACKING_TEXT =\n 'Track implementation progress with po-task: create one task per plan step (the `tasks` code-mode namespace, or the `task` tool without code mode), mark each in progress when you start it and done when it is verified.';\n\n/** po-task registers the `task` tool and its code-mode guide; either one means the session can track tasks. */\nfunction hasTaskTracking(host: PoHeadlessHostService): boolean {\n return host\n .inspectCapabilities()\n .capabilities.some(\n (capability) =>\n (capability.kind === 'tool' && capability.name === 'task') ||\n (capability.kind === 'skill' && capability.name === 'codemode-how-to-task'),\n );\n}\n\nexport function planExitText(taskTracking: boolean): string {\n return taskTracking ? `${PLAN_EXIT_APPROVED_TEXT} ${PLAN_EXIT_TASK_TRACKING_TEXT}` : PLAN_EXIT_APPROVED_TEXT;\n}\n/** The plan's own heading, as the status line and the pointer record word it. */\nexport function planTitleOf(markdown: string): string {\n return markdown.match(/^#\\s+(.+?)\\s*$/m)?.[1] ?? DEFAULT_PLAN_TITLE;\n}\n\nexport function planTitleSlug(markdown: string): string {\n const heading = planTitleOf(markdown);\n return (\n heading\n .normalize('NFKD')\n .replace(/[\\u0300-\\u036f]/g, '')\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '')\n .slice(0, PLAN_TITLE_MAX_LENGTH) || DEFAULT_PLAN_TITLE\n );\n}\n\n/**\n * How the status line words when a plan was written. A malformed timestamp is\n * shown as it came rather than as an invalid date: the stamp's other job is to\n * change on a rewrite, and it can still do that.\n */\nexport function planStampOf(writtenAt: string): string {\n const at = new Date(writtenAt);\n return Number.isNaN(at.getTime()) ? writtenAt : at.toLocaleTimeString();\n}\n\nexport function visiblePlanForToolCall(entries: readonly unknown[], toolCallId: string): string | undefined {\n for (let index = entries.length - 1; index >= 0; index -= 1) {\n const entry = entries[index];\n if (!entry || typeof entry !== 'object') continue;\n const candidate = entry as {\n type?: string;\n message?: { role?: string; content?: unknown };\n };\n if (candidate.type !== 'message' || candidate.message?.role !== 'assistant') continue;\n if (!Array.isArray(candidate.message.content)) continue;\n\n const toolIndex = candidate.message.content.findIndex((block) => {\n if (!block || typeof block !== 'object') return false;\n const content = block as { type?: string; id?: string; name?: string };\n return content.type === 'toolCall' && content.id === toolCallId && content.name === WRITE_PLAN_TOOL;\n });\n if (toolIndex < 0) continue;\n\n const plan = candidate.message.content\n .slice(0, toolIndex)\n .flatMap((block) => {\n if (!block || typeof block !== 'object') return [];\n const content = block as { type?: string; text?: unknown };\n return content.type === 'text' && typeof content.text === 'string' ? [content.text] : [];\n })\n .join('\\n\\n')\n .trim();\n return plan || undefined;\n }\n return undefined;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction isString(value: unknown): value is string {\n return typeof value === 'string';\n}\n\nfunction boundedDebugText(value: unknown, field: string, required: boolean): string {\n if (!isString(value)) throw new Error(`${field} must be a string.`);\n const text = value.trim();\n if (required && !text) throw new Error(`${field} must not be empty.`);\n if (Buffer.byteLength(text, 'utf8') > DEBUG_EVIDENCE_MAX_TEXT_BYTES) {\n throw new Error(`${field} exceeds the bounded debug evidence limit.`);\n }\n return text;\n}\n\nfunction boundedDebugList(value: unknown, field: string): string[] {\n if (!Array.isArray(value) || value.length > DEBUG_EVIDENCE_MAX_ITEMS) {\n throw new Error(`${field} must be a bounded string array.`);\n }\n return value.map((item, index) => boundedDebugText(item, `${field}[${index}]`, false));\n}\n\nfunction optionalDebugText(value: unknown, field: string): string {\n return value === undefined ? '' : boundedDebugText(value, field, false);\n}\n\nfunction optionalDebugList(value: unknown, field: string): string[] {\n return value === undefined ? [] : boundedDebugList(value, field);\n}\n\nfunction assertExactObjectKeys(value: Record<string, unknown>, keys: readonly string[], field: string): void {\n const allowed = new Set(keys);\n const unexpected = Object.keys(value).find((key) => !allowed.has(key));\n if (unexpected) throw new Error(`${field} contains unsupported field '${unexpected}'.`);\n}\n\nexport function parseDebugEvidencePacket(value: unknown): DebugEvidencePacket {\n if (!isRecord(value)) throw new Error('Debug evidence must be an object.');\n assertExactObjectKeys(\n value,\n [\n 'issue',\n 'expectedBehavior',\n 'reproductionAttempt',\n 'actualBehavior',\n 'logs',\n 'correlatedTraceEvidence',\n 'processOutput',\n 'browserConsoleEvidence',\n 'correlationIds',\n 'timestamps',\n 'verifiedFacts',\n 'hypotheses',\n 'unavailableEvidence',\n ],\n 'Debug evidence',\n );\n const packet: DebugEvidencePacket = {\n issue: boundedDebugText(value.issue, 'issue', true),\n expectedBehavior: optionalDebugText(value.expectedBehavior, 'expectedBehavior'),\n reproductionAttempt: optionalDebugText(value.reproductionAttempt, 'reproductionAttempt'),\n actualBehavior: optionalDebugText(value.actualBehavior, 'actualBehavior'),\n logs: optionalDebugList(value.logs, 'logs'),\n correlatedTraceEvidence: optionalDebugList(value.correlatedTraceEvidence, 'correlatedTraceEvidence'),\n processOutput: optionalDebugList(value.processOutput, 'processOutput'),\n browserConsoleEvidence: optionalDebugList(value.browserConsoleEvidence, 'browserConsoleEvidence'),\n correlationIds: optionalDebugList(value.correlationIds, 'correlationIds'),\n timestamps: optionalDebugList(value.timestamps, 'timestamps'),\n verifiedFacts: optionalDebugList(value.verifiedFacts, 'verifiedFacts'),\n hypotheses: optionalDebugList(value.hypotheses, 'hypotheses'),\n unavailableEvidence: optionalDebugList(value.unavailableEvidence, 'unavailableEvidence'),\n };\n if (Buffer.byteLength(JSON.stringify(packet), 'utf8') > DEBUG_EVIDENCE_MAX_BYTES) {\n throw new Error('Debug evidence exceeds the bounded packet size.');\n }\n return packet;\n}\n","import { defineRoot, type RootDeclaration } from '@agimon-ai/po-core/extensionFile';\nimport type { PoServerPluginContext, PoServerSessionPlugin } from '@agimon-ai/po-core/serverPlugin';\n\nimport { mountMcpTools } from '../../../../services/mcpTools';\nimport { api } from '../../../../services/planApi';\nimport { createPlanServerSession } from '../../../../services/planServerSession';\n\nexport default defineRoot(\n ({ agent, host }: PoServerPluginContext): RootDeclaration<PoServerSessionPlugin, PoServerPluginContext> => {\n const value = {\n api: [api],\n ...(agent\n ? createPlanServerSession(agent, {\n homeDirectory: host.context.homeDirectory ?? host.context.environment?.HOME,\n })\n : {}),\n };\n return {\n value,\n services: [...(value.services ?? []), ...(value.tools?.length ? [mountMcpTools(value.tools)] : [])],\n };\n },\n);\n","import { defineRoute, type WithRoot } from '@agimon-ai/po-core/extensionFile';\n\ntype Root = Awaited<ReturnType<typeof import('../../root.server').default>>['value'];\n\nexport default defineRoute((context: WithRoot<unknown, Root>) => context.root.api![0]!);\n","import { defineRoutedContribution, type WithRoot } from '@agimon-ai/po-core/extensionFile';\n\ntype Root = Awaited<ReturnType<typeof import('../root.server').default>>['value'];\n\nexport default defineRoutedContribution((context: WithRoot<unknown, Root>) => context.root.hooks?.[0], {\n cardinality: 'optional',\n});\n","import { defineRoutedContribution, type WithRoot } from '@agimon-ai/po-core/extensionFile';\n\ntype Root = Awaited<ReturnType<typeof import('../root.server').default>>['value'];\n\nexport default defineRoutedContribution((context: WithRoot<unknown, Root>) => context.root.hooks?.[2], {\n cardinality: 'optional',\n});\n","import { defineRoutedContribution, type WithRoot } from '@agimon-ai/po-core/extensionFile';\n\ntype Root = Awaited<ReturnType<typeof import('../root.server').default>>['value'];\n\nexport default defineRoutedContribution((context: WithRoot<unknown, Root>) => context.root.hooks?.[1], {\n cardinality: 'optional',\n});\n","import { defineRoutedContribution, type WithRoot } from '@agimon-ai/po-core/extensionFile';\n\ntype Root = Awaited<ReturnType<typeof import('../root.server').default>>['value'];\n\nexport default defineRoutedContribution((context: WithRoot<unknown, Root>) => context.root.resources?.[0], {\n cardinality: 'optional',\n});\n","import { defineToolRestriction } from '@agimon-ai/po-core/extensionFile';\nimport type { PoHeadlessToolRestriction } from '@agimon-ai/po-core/headless';\nimport { PO_VOICE_AUTO_MODE_ID } from '@agimon-ai/po-core/voiceTools';\n\nexport default defineToolRestriction({\n when: { state: { 'minor-mode': PO_VOICE_AUTO_MODE_ID } },\n excludedTools: ['complete_plan'],\n} satisfies PoHeadlessToolRestriction);\n","import { defineToolRestriction } from '@agimon-ai/po-core/extensionFile';\nimport type { PoHeadlessToolRestriction } from '@agimon-ai/po-core/headless';\n\nexport default defineToolRestriction({\n when: { state: { 'minor-mode': 'plan' } },\n excludedTools: ['edit', 'write'],\n} satisfies PoHeadlessToolRestriction);\n","import { defineRoutedContribution, type WithRoot } from '@agimon-ai/po-core/extensionFile';\n\ntype Root = Awaited<ReturnType<typeof import('../root.server').default>>['value'];\n\nexport default defineRoutedContribution((context: WithRoot<unknown, Root>) => context.root.tools?.[2], {\n cardinality: 'optional',\n});\n","import { defineRoutedContribution, type WithRoot } from '@agimon-ai/po-core/extensionFile';\n\ntype Root = Awaited<ReturnType<typeof import('../root.server').default>>['value'];\n\nexport default defineRoutedContribution((context: WithRoot<unknown, Root>) => context.root.tools?.[0], {\n cardinality: 'optional',\n});\n","import { defineRoutedContribution, type WithRoot } from '@agimon-ai/po-core/extensionFile';\n\ntype Root = Awaited<ReturnType<typeof import('../root.server').default>>['value'];\n\nexport default defineRoutedContribution((context: WithRoot<unknown, Root>) => context.root.tools?.[1], {\n cardinality: 'optional',\n});\n","// Generated by @agimon-ai/po-build. Do not edit by hand.\nimport { defineServerPlugin, type PoServerSessionPlugin } from '@agimon-ai/po-core/serverPlugin';\nimport { composeServerProviderOverrides } from '@agimon-ai/po-core/serverPlugin';\nimport { composeRootHooks, composeRootInjects } from '@agimon-ai/po-core/extensionFile';\n\nimport rootSession from '../src/extensions/workspaces/sessions/(backend)/root.server';\nimport apiRoute from '../src/extensions/workspaces/sessions/(backend)/api/plans/route.server';\nimport hookBeforeAgentStart from '../src/extensions/workspaces/sessions/(backend)/hook/beforeAgentStart.server';\nimport hookContext from '../src/extensions/workspaces/sessions/(backend)/hook/context.server';\nimport hookSessionStart from '../src/extensions/workspaces/sessions/(backend)/hook/sessionStart.server';\nimport resourcePoUsePlan from '../src/extensions/workspaces/sessions/(backend)/resource/poUsePlan.server';\nimport toolRestrictionPlan from '../src/extensions/workspaces/sessions/(backend)/tool-restriction/plan.server';\nimport toolRestrictionPlanReadOnly from '../src/extensions/workspaces/sessions/(backend)/tool-restriction/planReadOnly.server';\nimport toolCompletePlan from '../src/extensions/workspaces/sessions/(backend)/tool/completePlan.server';\nimport toolRecordDebugEvidence from '../src/extensions/workspaces/sessions/(backend)/tool/recordDebugEvidence.server';\nimport toolWritePlan from '../src/extensions/workspaces/sessions/(backend)/tool/writePlan.server';\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;\n/* v8 ignore next -- generated normalization branch */\nconst defined = <T>(values: readonly (T | undefined)[]): T[] =>\n values.filter((value): value is T => value !== undefined);\nconst via = <T>(identity: Record<string, unknown>, value: unknown): T =>\n (value === undefined ? undefined : { ...identity, ...(value as object) }) as T;\n\nexport const serverPlugin = defineServerPlugin({\n name: '@agimon-ai/po-plan',\n providerOverrides: composeServerProviderOverrides(rootSession),\n injects: composeRootInjects({ session: [rootSession] }),\n session: async (context) => {\n const scopeSession = await context.scopeRoot('session', rootSession, context);\n const contextSession = { ...context, root: scopeSession.value };\n return {\n ...composeRootHooks(scopeSession),\n get api(): PoServerSessionPlugin['api'] { return [at(apiRoute, contextSession)]; },\n get hooks(): PoServerSessionPlugin['hooks'] { return defined([via({ event: 'before_agent_start' }, at(hookBeforeAgentStart, contextSession)), via({ event: 'context' }, at(hookContext, contextSession)), via({ event: 'session_start' }, at(hookSessionStart, contextSession))]); },\n get resources(): PoServerSessionPlugin['resources'] { return defined([at(resourcePoUsePlan, contextSession)]); },\n get toolRestrictions(): PoServerSessionPlugin['toolRestrictions'] { return [at(toolRestrictionPlan, contextSession), at(toolRestrictionPlanReadOnly, contextSession)]; },\n get tools(): PoServerSessionPlugin['tools'] { return defined([via({ name: 'complete_plan' }, at(toolCompletePlan, contextSession)), via({ name: 'record_debug_evidence' }, at(toolRecordDebugEvidence, contextSession)), via({ name: 'write_plan' }, at(toolWritePlan, contextSession))]); },\n services: [...(scopeSession.services ?? [])],\n get activities(): PoServerSessionPlugin['activities'] { return [...(scopeSession.activities ?? [])]; },\n };\n },\n});\n\nexport default serverPlugin;\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agimon-ai/po-plan",
|
|
3
|
-
"version": "0.0.0-alpha.
|
|
3
|
+
"version": "0.0.0-alpha.47",
|
|
4
4
|
"description": "Reviewable Pi planning mode that removes file-editing tools and persists implementation plans.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -84,18 +84,18 @@
|
|
|
84
84
|
"registry": "https://registry.npmjs.org/"
|
|
85
85
|
},
|
|
86
86
|
"dependencies": {
|
|
87
|
-
"@agimon-ai/po-config": "0.0.0-alpha.
|
|
88
|
-
"@agimon-ai/po-core": "0.0.0-alpha.
|
|
89
|
-
"@agimon-ai/po-minor-mode": "0.0.0-alpha.
|
|
90
|
-
"@agimon-ai/po-web-components": "0.0.0-alpha.
|
|
91
|
-
"@agimon-ai/po-web-security": "0.0.0-alpha.
|
|
87
|
+
"@agimon-ai/po-config": "0.0.0-alpha.47",
|
|
88
|
+
"@agimon-ai/po-core": "0.0.0-alpha.47",
|
|
89
|
+
"@agimon-ai/po-minor-mode": "0.0.0-alpha.47",
|
|
90
|
+
"@agimon-ai/po-web-components": "0.0.0-alpha.47",
|
|
91
|
+
"@agimon-ai/po-web-security": "0.0.0-alpha.47",
|
|
92
92
|
"@deepseek-ai/cordis": "4.0.5",
|
|
93
93
|
"hono": "4.13.13",
|
|
94
94
|
"typebox": "1.4.2"
|
|
95
95
|
},
|
|
96
96
|
"devDependencies": {
|
|
97
|
-
"@agimon-ai/po-build": "0.0.0-alpha.
|
|
98
|
-
"@agimon-ai/po-native-components": "0.0.0-alpha.
|
|
97
|
+
"@agimon-ai/po-build": "0.0.0-alpha.47",
|
|
98
|
+
"@agimon-ai/po-native-components": "0.0.0-alpha.47",
|
|
99
99
|
"@earendil-works/pi-coding-agent": "1.4.0",
|
|
100
100
|
"@tanstack/react-store": "0.11.2",
|
|
101
101
|
"@tanstack/store": "0.11.2",
|
|
@@ -111,7 +111,7 @@
|
|
|
111
111
|
"vitest": "5.0.4"
|
|
112
112
|
},
|
|
113
113
|
"peerDependencies": {
|
|
114
|
-
"@agimon-ai/po-native-components": "0.0.0-alpha.
|
|
114
|
+
"@agimon-ai/po-native-components": "0.0.0-alpha.47",
|
|
115
115
|
"@earendil-works/pi-coding-agent": "1.4.0",
|
|
116
116
|
"react-native": "0.88.0",
|
|
117
117
|
"react-native-unistyles": "^3.5.3"
|