@agent-plan/pi-adapter 0.2.12 ā 0.2.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +148 -26
- package/package.json +4 -4
- package/web-ui-dist/assets/index-Tma57h5W.js +191 -0
- package/web-ui-dist/assets/index-U-dx9Eua.css +1 -0
- package/web-ui-dist/index.html +2 -2
- package/web-ui-dist/assets/index-AkkZ1Syc.css +0 -1
- package/web-ui-dist/assets/index-BXrUndZJ.js +0 -191
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAoB,MAAM,iCAAiC,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAoB,MAAM,iCAAiC,CAAC;AA+yBtF,MAAM,CAAC,OAAO,UAAU,eAAe,CAAC,EAAE,EAAE,YAAY,GAAG,IAAI,CAgyF9D"}
|
package/dist/index.js
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import { Type } from "typebox";
|
|
12
12
|
import { ExportService, PlanStore, setWriteBusyHook, setWriteNotifyHook, migrateToUuids, withFeatureLock, needsMotivation } from "@agent-plan/core";
|
|
13
|
-
import { createChecklistItemId, createFeatureId, createPhaseId, createTaskId, normalizeSlug } from "@agent-plan/core/naming";
|
|
13
|
+
import { createChecklistItemId, createFeatureId, createPhaseId, createTaskId, clampSlug, normalizeSlug } from "@agent-plan/core/naming";
|
|
14
14
|
import { join, dirname } from "node:path";
|
|
15
15
|
import { homedir } from "node:os";
|
|
16
16
|
import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
|
|
@@ -72,6 +72,7 @@ let startupResumePromptPending = false;
|
|
|
72
72
|
let startupResumeSummaryPending = false;
|
|
73
73
|
let plannerHeavyInitDone = false; // runs migrate/heal/refreshResume once per session, not every turn
|
|
74
74
|
let startupResumeSummaryText = "";
|
|
75
|
+
let startupResumeSummaryTimer = null;
|
|
75
76
|
let contextBlockCache = ""; // cached before_agent_start context; rebuilt only when plan changes
|
|
76
77
|
let contextBlockDirty = true; // build on first turn; invalidated by write notify hook
|
|
77
78
|
let editedThisTurn = false; // tracks edit/write activity for the task_complete reminder
|
|
@@ -861,18 +862,31 @@ export default function planPiExtension(pi) {
|
|
|
861
862
|
return;
|
|
862
863
|
if (event.message.role !== "assistant")
|
|
863
864
|
return;
|
|
864
|
-
const
|
|
865
|
-
|
|
865
|
+
const visibleText = event.message.content
|
|
866
|
+
.filter((item) => item.type === "text")
|
|
867
|
+
.map((item) => item.text ?? "")
|
|
868
|
+
.join("");
|
|
869
|
+
if (!visibleText.trim())
|
|
866
870
|
return;
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
startupResumeSummaryText = "";
|
|
870
|
-
if (!summaryText)
|
|
871
|
+
// Dedupe: if the agent already printed the address, don't double-append.
|
|
872
|
+
if (visibleText.includes("š Web UI:"))
|
|
871
873
|
return;
|
|
874
|
+
// Append the web address to EVERY assistant text message during the recap
|
|
875
|
+
// turn (template: "{agent_summary}\n\n{web_address}"). We intentionally do
|
|
876
|
+
// NOT consume the flag here: the recap turn can span several assistant
|
|
877
|
+
// messages (tool calls + final summary), and we must guarantee the address
|
|
878
|
+
// lands on the final summary the user actually reads. The flag is cleared
|
|
879
|
+
// at the start of the next turn (before_agent_start) plus a safety timeout.
|
|
880
|
+
const webServer = server;
|
|
881
|
+
const localUrl = webServer?.localUrl ?? webServer?.url ?? "";
|
|
882
|
+
if (!localUrl)
|
|
883
|
+
return;
|
|
884
|
+
const lanUrl = webServer?.lanUrl ?? "";
|
|
885
|
+
const urlLine = `\n\nš Web UI: ${localUrl}${lanUrl ? ` ā LAN: ${lanUrl}` : ""}`;
|
|
872
886
|
return {
|
|
873
887
|
message: {
|
|
874
888
|
...event.message,
|
|
875
|
-
content: [{ type: "text", text:
|
|
889
|
+
content: [...event.message.content, { type: "text", text: urlLine }],
|
|
876
890
|
},
|
|
877
891
|
};
|
|
878
892
|
});
|
|
@@ -906,6 +920,12 @@ export default function planPiExtension(pi) {
|
|
|
906
920
|
// git pull, build, test, ls, etc. always work.
|
|
907
921
|
if (event.toolName !== "edit" && event.toolName !== "write")
|
|
908
922
|
return;
|
|
923
|
+
// Planner-internal writes (.planner/HANDOFF.md, resume.json, generated plan
|
|
924
|
+
// files, etc.) are planner operations, NOT code edits. They never require a
|
|
925
|
+
// task in-progress ā skip the guard so the handoff can always be written.
|
|
926
|
+
const targetPath = String(event.input?.path ?? event.input?.filePath ?? "");
|
|
927
|
+
if (targetPath && (targetPath.includes("/.planner/") || targetPath.includes("\\.planner\\") || targetPath.startsWith(".planner/")))
|
|
928
|
+
return;
|
|
909
929
|
const st = loadStore(ctx);
|
|
910
930
|
if (!(await st.exists().catch(() => false)))
|
|
911
931
|
return;
|
|
@@ -1802,8 +1822,7 @@ export default function planPiExtension(pi) {
|
|
|
1802
1822
|
ctx.ui.notify("Aborted", "warning");
|
|
1803
1823
|
return;
|
|
1804
1824
|
}
|
|
1805
|
-
const shortName = title.
|
|
1806
|
-
.replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 30);
|
|
1825
|
+
const shortName = clampSlug(title, 30, `task-${Date.now().toString(36)}`);
|
|
1807
1826
|
const taskNum = phase.tasks.length + 1;
|
|
1808
1827
|
const taskId = createTaskId();
|
|
1809
1828
|
const now = nowISO();
|
|
@@ -1976,8 +1995,7 @@ export default function planPiExtension(pi) {
|
|
|
1976
1995
|
return;
|
|
1977
1996
|
}
|
|
1978
1997
|
if (normalizedStatus === "in-progress" && existsSync(join(st.root, "HANDOFF.md"))) {
|
|
1979
|
-
ctx.ui.notify("
|
|
1980
|
-
return;
|
|
1998
|
+
ctx.ui.notify("ā¹ļø A handoff exists at .planner/HANDOFF.md ā read it for context, then delete it with /planner handoff clear (or plan_delete_handoff) when no longer needed. Proceeding with the status change.", "info");
|
|
1981
1999
|
}
|
|
1982
2000
|
applyTaskLifecycleDates(task, normalizedStatus, now);
|
|
1983
2001
|
}
|
|
@@ -2018,10 +2036,12 @@ export default function planPiExtension(pi) {
|
|
|
2018
2036
|
ctx.ui.notify(`Task "${task.title}" is already in-progress.`, "info");
|
|
2019
2037
|
return;
|
|
2020
2038
|
}
|
|
2021
|
-
// Hygiene
|
|
2039
|
+
// Hygiene notice (non-blocking): surface an existing handoff but never
|
|
2040
|
+
// block task_start ā the handoff is a captured-context artifact, not a
|
|
2041
|
+
// lock (a small modification after writing a handoff should start without
|
|
2042
|
+
// forcing deletion, which would lose context).
|
|
2022
2043
|
if (existsSync(join(st.root, "HANDOFF.md"))) {
|
|
2023
|
-
ctx.ui.notify("
|
|
2024
|
-
return;
|
|
2044
|
+
ctx.ui.notify("ā¹ļø A handoff exists at .planner/HANDOFF.md ā read it for context, then delete it with /planner handoff clear (or plan_delete_handoff) when no longer needed. Proceeding with task start.", "info");
|
|
2025
2045
|
}
|
|
2026
2046
|
const now = nowISO();
|
|
2027
2047
|
// Record status change in the incremental statusLog.
|
|
@@ -2263,6 +2283,16 @@ export default function planPiExtension(pi) {
|
|
|
2263
2283
|
// at the END of that summary (not in a notify here) so it stays visible.
|
|
2264
2284
|
startupResumePromptPending = true;
|
|
2265
2285
|
startupResumeSummaryPending = true;
|
|
2286
|
+
// Force the recap turn to rebuild the context (slow path) so the resume
|
|
2287
|
+
// protocol is injected fresh. The protocol is appended per-turn and is
|
|
2288
|
+
// never baked into the cache, so later turns won't see it.
|
|
2289
|
+
contextBlockDirty = true;
|
|
2290
|
+
if (startupResumeSummaryTimer)
|
|
2291
|
+
clearTimeout(startupResumeSummaryTimer);
|
|
2292
|
+
startupResumeSummaryTimer = setTimeout(() => {
|
|
2293
|
+
startupResumeSummaryPending = false;
|
|
2294
|
+
startupResumeSummaryTimer = null;
|
|
2295
|
+
}, 60000);
|
|
2266
2296
|
pi.sendMessage({
|
|
2267
2297
|
customType: "planner-resume-trigger",
|
|
2268
2298
|
content: "planner recap",
|
|
@@ -3153,8 +3183,7 @@ export default function planPiExtension(pi) {
|
|
|
3153
3183
|
catch {
|
|
3154
3184
|
return { content: [{ type: "text", text: `Phase not found: ${params.phaseId}` }], details: {} };
|
|
3155
3185
|
}
|
|
3156
|
-
const
|
|
3157
|
-
const shortName = rawShort.trim() || `task-${Date.now().toString(36)}`; // never empty (fixes regex invalid_string)
|
|
3186
|
+
const shortName = clampSlug(params.shortName ?? params.title, 30, `task-${Date.now().toString(36)}`); // clamp+strip trailing dash; never empty
|
|
3158
3187
|
const taskId = createTaskId();
|
|
3159
3188
|
const now = nowISO();
|
|
3160
3189
|
const initialStatus = params.status ?? "planned";
|
|
@@ -3384,6 +3413,90 @@ export default function planPiExtension(pi) {
|
|
|
3384
3413
|
return { content: [{ type: "text", text: `ā
Task completed: ${completedTask.id} ā ${completedTask.title} (done)` }], details: completedTask };
|
|
3385
3414
|
},
|
|
3386
3415
|
});
|
|
3416
|
+
// āā Web lifecycle tools (agent-callable; parity with @agent-plan/mcp planner-web) āā
|
|
3417
|
+
// These let the agent manage the web dashboard directly, without relying
|
|
3418
|
+
// on the /planner slash command (which is not intercepted when the planner
|
|
3419
|
+
// is disabled by default). Planner operations are NOT code edits.
|
|
3420
|
+
pi.registerTool({
|
|
3421
|
+
name: "planner-web",
|
|
3422
|
+
label: "Planner Web",
|
|
3423
|
+
description: "Manage the planner web dashboard (start/status/stop). The dashboard is LAN-bound (0.0.0.0) with a dynamic OS-assigned port by default. Planner operations are NOT code edits and need no active task. The web does NOT auto-start; call action=start explicitly.",
|
|
3424
|
+
parameters: Type.Object({
|
|
3425
|
+
action: Type.Union([Type.Literal("start"), Type.Literal("stop"), Type.Literal("status")], { description: "start | stop | status. Default: status" }),
|
|
3426
|
+
port: Type.Optional(Type.Number({ description: "Optional requested port for start. Omit/0 ā OS assigns a free port." })),
|
|
3427
|
+
visibility: Type.Optional(Type.Union([Type.Literal("local"), Type.Literal("lan")], { description: "Bind scope for start. Default: lan (0.0.0.0, reachable from other devices)." })),
|
|
3428
|
+
}),
|
|
3429
|
+
async execute(_id, params, _signal, _onUpdate, ctx) {
|
|
3430
|
+
const action = params.action ?? "status";
|
|
3431
|
+
if (action === "start") {
|
|
3432
|
+
const st = await requirePlan(ctx);
|
|
3433
|
+
if (!st)
|
|
3434
|
+
return { content: [{ type: "text", text: "No .planner/ found. Run plan_init first." }], details: { running: false } };
|
|
3435
|
+
const visibility = params.visibility ?? "lan";
|
|
3436
|
+
if (server) {
|
|
3437
|
+
const srv = server;
|
|
3438
|
+
return { content: [{ type: "text", text: `Web UI already running. Local: ${srv.localUrl}${srv.lanUrl ? ` ā LAN: ${srv.lanUrl}` : ""} (mode: ${srv.mode}, port: ${lastKnownWebPort ?? "?"})` }], details: { running: true, localUrl: srv.localUrl, lanUrl: srv.lanUrl, port: lastKnownWebPort, mode: srv.mode } };
|
|
3439
|
+
}
|
|
3440
|
+
await startServer(ctx, params.port && params.port > 0 ? params.port : undefined, visibility);
|
|
3441
|
+
const srv = server;
|
|
3442
|
+
if (!srv)
|
|
3443
|
+
return { content: [{ type: "text", text: "Failed to start web server." }], details: { running: false } };
|
|
3444
|
+
return { content: [{ type: "text", text: `Web UI started. Local: ${srv.localUrl}${srv.lanUrl ? ` ā LAN: ${srv.lanUrl}` : ""} (mode: ${srv.mode}, port: ${lastKnownWebPort ?? "?"})` }], details: { running: true, localUrl: srv.localUrl, lanUrl: srv.lanUrl, port: lastKnownWebPort, mode: srv.mode } };
|
|
3445
|
+
}
|
|
3446
|
+
if (action === "stop") {
|
|
3447
|
+
if (!server)
|
|
3448
|
+
return { content: [{ type: "text", text: "Web UI not running." }], details: { running: false } };
|
|
3449
|
+
const stoppedUrl = server.localUrl;
|
|
3450
|
+
await stopServer();
|
|
3451
|
+
return { content: [{ type: "text", text: `Web UI stopped (was ${stoppedUrl}).` }], details: { running: false } };
|
|
3452
|
+
}
|
|
3453
|
+
// status
|
|
3454
|
+
const srv = server;
|
|
3455
|
+
if (!srv)
|
|
3456
|
+
return { content: [{ type: "text", text: "Web UI not running. Use planner-web with action=start to start the dashboard." }], details: { running: false } };
|
|
3457
|
+
return { content: [{ type: "text", text: `Web UI running. Local: ${srv.localUrl}${srv.lanUrl ? ` ā LAN: ${srv.lanUrl}` : ""} (mode: ${srv.mode}, port: ${lastKnownWebPort ?? "?"})` }], details: { running: true, localUrl: srv.localUrl, lanUrl: srv.lanUrl, port: lastKnownWebPort, mode: srv.mode } };
|
|
3458
|
+
},
|
|
3459
|
+
});
|
|
3460
|
+
pi.registerTool({
|
|
3461
|
+
name: "planner-load",
|
|
3462
|
+
label: "Planner Load",
|
|
3463
|
+
description: "Enable the planner for this project and start the web dashboard (LAN), then return a resume recap (project status + handoff if present + Web UI address). This is the explicit way to enable the planner ā it does NOT auto-start. Planner operations are NOT code edits.",
|
|
3464
|
+
parameters: Type.Object({}),
|
|
3465
|
+
async execute(_id, _params, _signal, _onUpdate, ctx) {
|
|
3466
|
+
const st = await requirePlan(ctx);
|
|
3467
|
+
if (!st)
|
|
3468
|
+
return { content: [{ type: "text", text: "No .planner/ found. Run plan_init first." }], details: { enabled: false, running: false } };
|
|
3469
|
+
plannerSessionEnabled = true;
|
|
3470
|
+
if (!server) {
|
|
3471
|
+
await startServer(ctx, undefined, "lan").catch(() => { });
|
|
3472
|
+
}
|
|
3473
|
+
let recap = "";
|
|
3474
|
+
try {
|
|
3475
|
+
recap = await buildStartupResumeSummary(st);
|
|
3476
|
+
}
|
|
3477
|
+
catch (e) {
|
|
3478
|
+
recap = `(recap unavailable: ${e instanceof Error ? e.message : String(e)})`;
|
|
3479
|
+
}
|
|
3480
|
+
const srv = server;
|
|
3481
|
+
const webLine = srv ? `\nš Web UI: ${srv.localUrl}${lastKnownWebPort ? ` (port ${lastKnownWebPort})` : ""}${srv.lanUrl ? ` ā LAN: ${srv.lanUrl}` : ""}` : "\nš Web UI: not running";
|
|
3482
|
+
return { content: [{ type: "text", text: `${recap}${webLine}` }], details: { enabled: true, running: Boolean(srv), localUrl: srv?.localUrl, lanUrl: srv?.lanUrl, port: lastKnownWebPort } };
|
|
3483
|
+
},
|
|
3484
|
+
});
|
|
3485
|
+
pi.registerTool({
|
|
3486
|
+
name: "planner-stop",
|
|
3487
|
+
label: "Planner Stop",
|
|
3488
|
+
description: "Disable the planner for this project and stop the web dashboard. Alias: planner-disable. Planner operations are NOT code edits.",
|
|
3489
|
+
parameters: Type.Object({}),
|
|
3490
|
+
async execute(_id, _params, _signal, _onUpdate, _ctx) {
|
|
3491
|
+
plannerSessionEnabled = false;
|
|
3492
|
+
await stopServer().catch(() => { });
|
|
3493
|
+
try {
|
|
3494
|
+
capturedPi?.appendEntry("plan-web-state", { running: false });
|
|
3495
|
+
}
|
|
3496
|
+
catch { }
|
|
3497
|
+
return { content: [{ type: "text", text: "Planner disabled. Web UI shut down. Run /planner load or planner-load to re-enable." }], details: { disabled: true, webRunning: false } };
|
|
3498
|
+
},
|
|
3499
|
+
});
|
|
3387
3500
|
// āā Context injection āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
3388
3501
|
pi.on("before_agent_start", async (event, ctx) => {
|
|
3389
3502
|
if (!plannerSessionEnabled)
|
|
@@ -3402,6 +3515,18 @@ export default function planPiExtension(pi) {
|
|
|
3402
3515
|
await st.refreshResume();
|
|
3403
3516
|
plannerHeavyInitDone = true;
|
|
3404
3517
|
}
|
|
3518
|
+
// Resume-flag lifecycle ā MUST run every turn, including cache hits.
|
|
3519
|
+
// /planner load sets both flags and dirties the cache so the recap turn
|
|
3520
|
+
// rebuilds (slow path) and injects the resume protocol. We consume
|
|
3521
|
+
// startupResumePromptPending here (it marks the recap turn only), and we
|
|
3522
|
+
// stop appending the web UI address on every later turn. Doing this
|
|
3523
|
+
// BEFORE the cache fast-path is what prevents the URL from leaking onto
|
|
3524
|
+
// every assistant message after /planner load.
|
|
3525
|
+
const isRecapTurn = startupResumePromptPending;
|
|
3526
|
+
startupResumePromptPending = false;
|
|
3527
|
+
if (startupResumeSummaryPending && !isRecapTurn) {
|
|
3528
|
+
startupResumeSummaryPending = false;
|
|
3529
|
+
}
|
|
3405
3530
|
// Fast path: on steady turns (no plan changes since last build) reuse
|
|
3406
3531
|
// the cached context block and skip all the per-turn I/O + string build.
|
|
3407
3532
|
// The cache is invalidated by the write-notify hook whenever the plan
|
|
@@ -3499,10 +3624,7 @@ export default function planPiExtension(pi) {
|
|
|
3499
3624
|
const ambientSummary = ambient
|
|
3500
3625
|
? `node=${ambient.nodeVersion || "?"} pm=${ambient.packageManager || "?"} lockfile=${ambient.lockfile || "none"} scripts=${Object.entries(ambient.scripts).map(([k, v]) => `${k}="${v}"`).join(", ") || "none"}`
|
|
3501
3626
|
: "(not scanned)";
|
|
3502
|
-
|
|
3503
|
-
startupResumeSummaryText = await buildStartupResumeSummary(st).catch(() => "");
|
|
3504
|
-
}
|
|
3505
|
-
const startupResumeProtocol = startupResumePromptPending ? [
|
|
3627
|
+
const startupResumeProtocol = isRecapTurn ? [
|
|
3506
3628
|
"",
|
|
3507
3629
|
"STARTUP RESUME PROTOCOL (first reply of the session ā mandatory):",
|
|
3508
3630
|
"- You are resuming a planner-backed project session.",
|
|
@@ -3517,7 +3639,7 @@ export default function planPiExtension(pi) {
|
|
|
3517
3639
|
`- Mention the next suggested activity: ${nextActivity}`,
|
|
3518
3640
|
"- THEN ask the user explicitly whether they want to resume that activity now.",
|
|
3519
3641
|
"- Do NOT assume yes. Wait for the user's answer before continuing implementation work.",
|
|
3520
|
-
"- If the user says yes and no task is currently in-progress, your NEXT action must be task_start before any bash/
|
|
3642
|
+
"- If the user says yes and no task is currently in-progress, your NEXT action must be task_start before any CODE edit/write (bash for git/build/test is fine; planner operations like plan_write_handoff are NOT code edits and never require a task).",
|
|
3521
3643
|
].filter(Boolean).join("\n") : "";
|
|
3522
3644
|
// Build/refresh the context block (slow path: cache miss).
|
|
3523
3645
|
const contextBlock = [
|
|
@@ -3531,7 +3653,7 @@ export default function planPiExtension(pi) {
|
|
|
3531
3653
|
"MANDATORY OPERATIONAL PROTOCOL (violation = execution failure):",
|
|
3532
3654
|
"āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā",
|
|
3533
3655
|
"1. HANDOFF HYGIENE: If .planner/HANDOFF.md exists, READ and DELETE it IMMEDIATELY using the handoff delete tool. Do NOT summarize first. Do NOT ask for confirmation. Do NOT defer cleanup. DELETE \u2192 THEN work.",
|
|
3534
|
-
"2. TASK LIFECYCLE: BEFORE coding ANY file: call task_start. AFTER finishing work on ANY task: call task_complete. No exceptions. No 'I'll do it later'.",
|
|
3656
|
+
"2. TASK LIFECYCLE: BEFORE coding ANY file: call task_start. AFTER finishing work on ANY task: call task_complete. No exceptions. No 'I'll do it later'. NOTE: Planner operations (plan_write_handoff / /planner handoff, task_start, task_complete, task_update, status reads) are NOT code edits. They are ALWAYS allowed regardless of task state ā you may write/refresh the handoff even when no task is in-progress (e.g., all tasks done, or capturing state mid-flight). Never refuse to write a handoff because 'no task is open'; that is incorrect.",
|
|
3535
3657
|
"3. IMMEDIATE SYNC: Update task status AT THE EXACT MOMENT of transition. Start = task_start NOW. Done = task_complete NOW. Blocked = task_update with motivation NOW. Never batch status updates.",
|
|
3536
3658
|
"4. BLOCKED MOTIVATION: Transitions to blocked/canceled/rejected/deferred/waiting/planned(from non-planned) MUST include a detailed 'motivation' parameter. Write it as if the next person has zero context.",
|
|
3537
3659
|
"5. NO SHORTCUTS: If a tool blocks you, follow the protocol. Bypasses are for emergencies only, not for convenience.",
|
|
@@ -3609,14 +3731,14 @@ export default function planPiExtension(pi) {
|
|
|
3609
3731
|
handoff?.content ? "If planner state shows no task/phase in-progress, do NOT present the handoff target as the current focus; present it only as a candidate to validate." : "",
|
|
3610
3732
|
handoff?.content ? "When the handoff has been fully consumed and work is safely resumed, delete `.planner/HANDOFF.md` using the dedicated handoff delete command/tool." : "",
|
|
3611
3733
|
handoff?.content ? handoff.content : "",
|
|
3612
|
-
startupResumeProtocol,
|
|
3613
3734
|
"Plan tools available: project_set_language_preferences, plan_init, project_update, requirement_list, requirement_create, requirement_update, requirement_delete, plan_get, feature_list, feature_get, feature_create, feature_update, feature_delete, phase_list, phase_get, phase_create, phase_update, phase_delete, task_list, task_get, task_create, task_update, task_delete, task_start, task_complete, plan_render, plan_get_handoff, plan_write_handoff, plan_delete_handoff, plan_authorize_bypass, plan_clear_bypass",
|
|
3614
3735
|
].filter(Boolean).join("\n");
|
|
3615
3736
|
contextBlockCache = contextBlock;
|
|
3616
3737
|
contextBlockDirty = false;
|
|
3617
|
-
|
|
3738
|
+
// The resume protocol is per-turn only (the recap turn) ā never bake it
|
|
3739
|
+
// into the cached context block, or it would persist on every later turn.
|
|
3618
3740
|
return {
|
|
3619
|
-
systemPrompt: `${event.systemPrompt}\n\n---\n${contextBlockCache}`,
|
|
3741
|
+
systemPrompt: `${event.systemPrompt}\n\n---\n${contextBlockCache}${startupResumeProtocol ? `\n\n${startupResumeProtocol}` : ""}`,
|
|
3620
3742
|
};
|
|
3621
3743
|
}
|
|
3622
3744
|
catch {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-plan/pi-adapter",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.15",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Pi agent extension for Agent Plan ā structured project planning in .planner/",
|
|
6
6
|
"keywords": [
|
|
@@ -26,10 +26,10 @@
|
|
|
26
26
|
]
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@agent-plan/core": "^0.2.0",
|
|
30
|
-
"@agent-plan/server": "^0.2.0",
|
|
31
29
|
"typebox": "^1.1.24",
|
|
32
|
-
"zod": "^3.24.0"
|
|
30
|
+
"zod": "^3.24.0",
|
|
31
|
+
"@agent-plan/core": "0.2.11",
|
|
32
|
+
"@agent-plan/server": "0.2.11"
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
35
|
"@earendil-works/pi-coding-agent": "^0.74.0",
|