@agimon-ai/po-plan 0.0.0-alpha.28 → 0.0.0-alpha.29

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -77,8 +77,9 @@ modes:
77
77
  ```
78
78
 
79
79
  `main` controls the planning model, `subagents` controls delegated research defaults, and
80
- `plansDirectory` can be absolute, repository-relative, or under `~`. Planning and delegated model
81
- calls consume provider quota.
80
+ `plansDirectory` can be absolute, repository-relative, or under `~`. When session artifacts are
81
+ available, plans are stored under `<artifactRoot>/plans/` instead; `plansDirectory` is the fallback.
82
+ Planning and delegated model calls consume provider quota.
82
83
 
83
84
  ## Public API
84
85
 
@@ -24,15 +24,14 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
24
24
  enumerable: true
25
25
  }) : target, mod));
26
26
  //#endregion
27
- const require_config = require("../config-BjNpZeNO.cjs");
27
+ require("../config-BjNpZeNO.cjs");
28
28
  const require_prompts = require("../prompts-xBLz0h6f.cjs");
29
29
  let node_buffer = require("node:buffer");
30
+ let _agimon_ai_po_core_config = require("@agimon-ai/po-core/config");
30
31
  let _agimon_ai_po_core_serverPlugin = require("@agimon-ai/po-core/serverPlugin");
31
32
  let _agimon_ai_po_core_extensionFile = require("@agimon-ai/po-core/extensionFile");
32
33
  let typebox = require("typebox");
33
34
  let node_crypto = require("node:crypto");
34
- let node_fs_promises = require("node:fs/promises");
35
- node_fs_promises = __toESM(node_fs_promises, 1);
36
35
  let hono = require("hono");
37
36
  let node_fs = require("node:fs");
38
37
  node_fs = __toESM(node_fs, 1);
@@ -41,6 +40,8 @@ node_os = __toESM(node_os, 1);
41
40
  let node_path = require("node:path");
42
41
  node_path = __toESM(node_path, 1);
43
42
  let _agimon_ai_po_core_piSettings = require("@agimon-ai/po-core/piSettings");
43
+ let node_fs_promises = require("node:fs/promises");
44
+ node_fs_promises = __toESM(node_fs_promises, 1);
44
45
  let _agimon_ai_po_core_voiceTools = require("@agimon-ai/po-core/voiceTools");
45
46
  let _agimon_ai_po_minor_mode = require("@agimon-ai/po-minor-mode");
46
47
  let _agimon_ai_po_config = require("@agimon-ai/po-config");
@@ -215,6 +216,36 @@ function formatPlanStatus(title, stamp) {
215
216
  return `${title}${STATUS_SEPARATOR}${stamp}`;
216
217
  }
217
218
  //#endregion
219
+ //#region src/services/planFiles/index.ts
220
+ async function writeNewPlanFile(directory, filename, content) {
221
+ await node_fs_promises.default.mkdir(directory, {
222
+ recursive: true,
223
+ mode: 448
224
+ });
225
+ const planPath = node_path.default.join(directory, filename);
226
+ await node_fs_promises.default.writeFile(planPath, content, {
227
+ encoding: "utf8",
228
+ flag: "wx",
229
+ mode: 384
230
+ });
231
+ return planPath;
232
+ }
233
+ async function readPlanText(planPath) {
234
+ return await node_fs_promises.default.readFile(planPath, "utf8");
235
+ }
236
+ async function readPlanBytes(planPath) {
237
+ return await node_fs_promises.default.readFile(planPath);
238
+ }
239
+ async function overwritePlanFile(planPath, content) {
240
+ const stats = await node_fs_promises.default.lstat(planPath);
241
+ if (!stats.isFile() || stats.nlink !== 1) return false;
242
+ await node_fs_promises.default.writeFile(planPath, content, {
243
+ encoding: "utf8",
244
+ mode: 384
245
+ });
246
+ return true;
247
+ }
248
+ //#endregion
218
249
  //#region src/services/planApi/index.ts
219
250
  /**
220
251
  * This package's HTTP surface: the session's current plan, and the manual save.
@@ -246,7 +277,7 @@ function hashOf(content) {
246
277
  async function readPlan(planPath) {
247
278
  let raw;
248
279
  try {
249
- raw = await node_fs_promises.default.readFile(planPath);
280
+ raw = await readPlanBytes(planPath);
250
281
  } catch {
251
282
  return {
252
283
  content: "",
@@ -270,7 +301,7 @@ async function readPlan(planPath) {
270
301
  }
271
302
  function createPlanApi(options = {}) {
272
303
  const app = new hono.Hono();
273
- const pointers = options.pointers ?? new PlanPointerService({ env: process.env });
304
+ const pointers = options.pointers ?? new PlanPointerService();
274
305
  const pointerFor = () => options.sessionId === void 0 ? void 0 : pointers.read(options.sessionId);
275
306
  app.get(routes.current.path, async (context) => {
276
307
  const pointer = pointerFor();
@@ -300,12 +331,7 @@ function createPlanApi(options = {}) {
300
331
  error: STALE_SAVE,
301
332
  hash: current.hash
302
333
  }, 409);
303
- const stats = await node_fs_promises.default.lstat(pointer.path);
304
- if (!stats.isFile() || stats.nlink !== 1) return context.json({ error: NOT_A_FILE }, 409);
305
- await node_fs_promises.default.writeFile(pointer.path, request.content, {
306
- encoding: "utf8",
307
- mode: 384
308
- });
334
+ if (!await overwritePlanFile(pointer.path, request.content)) return context.json({ error: NOT_A_FILE }, 409);
309
335
  return context.json({ hash: hashOf(request.content) });
310
336
  });
311
337
  return app;
@@ -314,7 +340,13 @@ function createPlanApi(options = {}) {
314
340
  const api = {
315
341
  basePath: API_BASE_PATH,
316
342
  start(context) {
317
- const app = createPlanApi(context.sessionId === void 0 ? {} : { sessionId: context.sessionId });
343
+ const app = createPlanApi({
344
+ ...context.sessionId === void 0 ? {} : { sessionId: context.sessionId },
345
+ pointers: new PlanPointerService({
346
+ env: context.environment ?? {},
347
+ homeDirectory: context.homeDirectory
348
+ })
349
+ });
318
350
  return {
319
351
  fetch: (request) => app.fetch(request),
320
352
  close: () => void 0
@@ -329,6 +361,7 @@ function readPoSubagentPolicyService(ctx) {
329
361
  }
330
362
  //#endregion
331
363
  //#region src/services/planServerSession/index.ts
364
+ const PLAN_ARTIFACT_SERVICE = "po/artifacts";
332
365
  const RECORD_DEBUG_EVIDENCE_TOOL = "record_debug_evidence";
333
366
  const WRITE_PLAN_TOOL = "write_plan";
334
367
  const COMPLETE_PLAN_TOOL = "complete_plan";
@@ -345,15 +378,6 @@ const PLAN_SERVER_CAPABILITIES = {
345
378
  };
346
379
  const PLAN_SUBAGENT_POLICY = {
347
380
  owner: "@agimon-ai/po-plan",
348
- allowedTools: [
349
- "read",
350
- "bash",
351
- "grep",
352
- "find",
353
- "ls",
354
- "mcp"
355
- ],
356
- requiredTools: ["bash"],
357
381
  allowMcpTools: true,
358
382
  allowedExternalProfiles: [],
359
383
  denyExtensions: false
@@ -392,14 +416,24 @@ function output(value, isError = false) {
392
416
  ...isError ? { isError: true } : {}
393
417
  };
394
418
  }
395
- function createPlanServerSession(host) {
419
+ function createPlanServerSession(host, options) {
396
420
  let flavor = "normal";
397
421
  let debugEvidence;
398
422
  let modeOwner;
399
423
  const selected = () => modeSelected(host);
400
- const homeDirectory = () => host.context.environment.HOME ?? node_os.default.homedir();
401
- const plansDirectory = () => (0, _agimon_ai_po_config.resolvePlanningPlansDirectory)(require_config.loadPoConfig(host.context.repoRoot, homeDirectory()).modes?.planning?.plansDirectory, host.context.repoRoot, homeDirectory());
402
- const pointers = new PlanPointerService({ env: host.context.environment });
424
+ let configService;
425
+ let artifactStorage;
426
+ const planningConfig = () => configService?.getSnapshot().settings.modes?.planning;
427
+ const plansDirectory = async () => {
428
+ const root = await artifactStorage?.rootForSession(host.context.sessionId, host.context.environment);
429
+ if (root !== void 0) return node_path.default.join(root, "plans");
430
+ if (options.homeDirectory === void 0) throw new Error("Po needs a home directory to choose the plans folder.");
431
+ return (0, _agimon_ai_po_config.resolvePlanningPlansDirectory)(planningConfig()?.plansDirectory, host.context.repoRoot, options.homeDirectory);
432
+ };
433
+ const pointers = new PlanPointerService({
434
+ env: host.context.environment,
435
+ homeDirectory: options.homeDirectory
436
+ });
403
437
  /**
404
438
  * The activity group's line, and the pointer its panel reads.
405
439
  *
@@ -462,7 +496,7 @@ function createPlanServerSession(host) {
462
496
  const wasEnabled = selected();
463
497
  const session = host.context.session;
464
498
  if (enabled && !wasEnabled) {
465
- const config = require_config.loadPoConfig(host.context.repoRoot, homeDirectory()).modes?.planning?.main;
499
+ const config = planningConfig()?.main;
466
500
  if (config?.model || config?.thinking) {
467
501
  if (!session.readModelSettings || !session.setModelSettings) throw new Error("This session cannot apply planning model settings.");
468
502
  const previous = await session.readModelSettings();
@@ -577,7 +611,26 @@ function createPlanServerSession(host) {
577
611
  }
578
612
  }).createOwner(void 0);
579
613
  return {
580
- services: [(0, _agimon_ai_po_minor_mode.serverMinorModes)([modeOwner]), serverPlanSubagentPolicy(host)],
614
+ services: [
615
+ (0, _agimon_ai_po_minor_mode.serverMinorModes)([modeOwner]),
616
+ serverPlanSubagentPolicy(host),
617
+ (context) => {
618
+ context.inject([_agimon_ai_po_core_config.PO_CONFIG_SERVICE], (child) => {
619
+ const service = child.get(_agimon_ai_po_core_config.PO_CONFIG_SERVICE);
620
+ configService = service;
621
+ return () => {
622
+ if (configService === service) configService = void 0;
623
+ };
624
+ });
625
+ context.inject([PLAN_ARTIFACT_SERVICE], (child) => {
626
+ const storage = child.get(PLAN_ARTIFACT_SERVICE);
627
+ artifactStorage = storage;
628
+ return () => {
629
+ if (artifactStorage === storage) artifactStorage = void 0;
630
+ };
631
+ });
632
+ }
633
+ ],
581
634
  resources: [{
582
635
  when: {
583
636
  state: { "minor-mode": PLAN_MODE_ID },
@@ -671,6 +724,7 @@ function createPlanServerSession(host) {
671
724
  },
672
725
  name: WRITE_PLAN_TOOL,
673
726
  label: "Write Plan",
727
+ mainAgentOnly: true,
674
728
  description: "Save the supplied Markdown plan, or the plan already presented in the local session.",
675
729
  parameters: writePlanParameters,
676
730
  executionMode: "serial",
@@ -690,20 +744,10 @@ function createPlanServerSession(host) {
690
744
  content = typeof requested === "string" ? requested : "";
691
745
  }
692
746
  if (typeof content !== "string" || !content.trim()) throw new Error("No implementation plan content was supplied.");
693
- const directory = plansDirectory();
694
- await (0, node_fs_promises.mkdir)(directory, {
695
- recursive: true,
696
- mode: 448
697
- });
747
+ const directory = await plansDirectory();
698
748
  const title = planTitleOf(content);
699
749
  const writtenAt = (/* @__PURE__ */ new Date()).toISOString();
700
- const filename = `${planTitleSlug(content)}-${Date.now()}.md`;
701
- const filePath = node_path.default.join(directory, filename);
702
- await (0, node_fs_promises.writeFile)(filePath, `${content.trim()}\n`, {
703
- encoding: "utf8",
704
- mode: 384,
705
- flag: "wx"
706
- });
750
+ const filePath = await writeNewPlanFile(directory, `${planTitleSlug(content)}-${Date.now()}.md`, `${content.trim()}\n`);
707
751
  await host.context.session.appendCustomEntry(PLAN_DOCUMENT, {
708
752
  path: filePath,
709
753
  content: content.trim(),
@@ -735,6 +779,7 @@ function createPlanServerSession(host) {
735
779
  },
736
780
  name: COMPLETE_PLAN_TOOL,
737
781
  label: "Complete Plan",
782
+ mainAgentOnly: true,
738
783
  description: "Request exit-or-continue approval in the local Po UI. Requires the user to interact there; this is not a ChatGPT approval dialog.",
739
784
  parameters: {
740
785
  type: "object",
@@ -777,14 +822,14 @@ function createPlanServerSession(host) {
777
822
  event: "before_agent_start",
778
823
  async handle(event) {
779
824
  const prompt = typeof event.systemPrompt === "string" ? event.systemPrompt : "";
780
- const directory = plansDirectory();
825
+ const directory = await plansDirectory();
781
826
  const sections = [require_prompts.buildPlanModeBasePrompt(directory, PLAN_SERVER_CAPABILITIES), require_prompts.buildFlavorPlanningPrompt(flavor, directory, debugEvidence)];
782
827
  const saved = await latestEntry(PLAN_DOCUMENT);
783
828
  if (saved !== void 0 && saved !== null && typeof saved === "object" && "path" in saved) {
784
829
  const document = saved;
785
830
  let content = document.content ?? "";
786
831
  try {
787
- content = await (0, node_fs_promises.readFile)(document.path, "utf8");
832
+ content = await readPlanText(document.path);
788
833
  } catch {
789
834
  content = document.content ?? "";
790
835
  }
@@ -921,10 +966,10 @@ function parseDebugEvidencePacket(value) {
921
966
  }
922
967
  //#endregion
923
968
  //#region src/extensions/workspaces/sessions/(backend)/root.server.ts
924
- var root_server_default = (0, _agimon_ai_po_core_extensionFile.defineRoot)(({ agent }) => {
969
+ var root_server_default = (0, _agimon_ai_po_core_extensionFile.defineRoot)(({ agent, host }) => {
925
970
  const value = {
926
971
  api: [api],
927
- ...agent ? createPlanServerSession(agent) : {}
972
+ ...agent ? createPlanServerSession(agent, { homeDirectory: host.context.homeDirectory ?? host.context.environment?.HOME }) : {}
928
973
  };
929
974
  return {
930
975
  value,