@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 +3 -2
- package/dist/extensions/server.cjs +88 -43
- package/dist/extensions/server.cjs.map +1 -1
- package/dist/extensions/server.mjs +92 -47
- package/dist/extensions/server.mjs.map +1 -1
- package/package.json +10 -10
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 `~`.
|
|
81
|
-
|
|
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
|
-
|
|
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
|
|
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(
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
401
|
-
|
|
402
|
-
const
|
|
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 =
|
|
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: [
|
|
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
|
|
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 (
|
|
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,
|