@agent-plan/mcp 0.2.27 → 0.2.28
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 +82 -76
- package/package.json +3 -3
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAglGA,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC,CAItD"}
|
package/dist/index.js
CHANGED
|
@@ -1748,60 +1748,62 @@ server.registerTool("planner-task-update", {
|
|
|
1748
1748
|
}
|
|
1749
1749
|
let updatedTask;
|
|
1750
1750
|
const timestamp = nowISO();
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
else
|
|
1766
|
-
delete task.descriptionRef;
|
|
1767
|
-
}
|
|
1768
|
-
if (notes !== undefined)
|
|
1769
|
-
task.notes = notes.trim();
|
|
1770
|
-
if (decisions !== undefined)
|
|
1771
|
-
task.decisions = decisions.map((item) => item.trim()).filter(Boolean);
|
|
1772
|
-
if (checklist !== undefined) {
|
|
1773
|
-
task.checklist = checklist.map((item, index) => ({
|
|
1774
|
-
id: createChecklistItemId(task.id, index + 1, item),
|
|
1775
|
-
number: index + 1,
|
|
1776
|
-
title: item.trim(),
|
|
1777
|
-
checked: false,
|
|
1778
|
-
}));
|
|
1779
|
-
}
|
|
1780
|
-
if (subtasks !== undefined) {
|
|
1781
|
-
const existingIds = new Set(task.subtasks.map((item) => item.id));
|
|
1782
|
-
if (subtasks.some((item) => item.id !== undefined && !existingIds.has(item.id))) {
|
|
1783
|
-
throw new PlanStoreError("Subtask IDs must belong to the target task.", undefined, { errorCode: "SUBTASK_ID_INVALID" });
|
|
1751
|
+
try {
|
|
1752
|
+
await st.updateTask(found.phase.id, found.task.id, (task) => {
|
|
1753
|
+
if (title !== undefined)
|
|
1754
|
+
task.title = title.trim();
|
|
1755
|
+
if (priority !== undefined)
|
|
1756
|
+
task.priority = priority;
|
|
1757
|
+
if (description !== undefined)
|
|
1758
|
+
task.description = description.trim();
|
|
1759
|
+
if (rawDescriptionRef !== undefined) {
|
|
1760
|
+
const descriptionRef = normalizeDescriptionRef(rawDescriptionRef);
|
|
1761
|
+
if (descriptionRef)
|
|
1762
|
+
task.descriptionRef = descriptionRef;
|
|
1763
|
+
else
|
|
1764
|
+
delete task.descriptionRef;
|
|
1784
1765
|
}
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
}
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1766
|
+
if (notes !== undefined)
|
|
1767
|
+
task.notes = notes.trim();
|
|
1768
|
+
if (decisions !== undefined)
|
|
1769
|
+
task.decisions = decisions.map((item) => item.trim()).filter(Boolean);
|
|
1770
|
+
if (checklist !== undefined) {
|
|
1771
|
+
task.checklist = checklist.map((item, index) => ({
|
|
1772
|
+
id: createChecklistItemId(task.id, index + 1, item),
|
|
1773
|
+
number: index + 1,
|
|
1774
|
+
title: item.trim(),
|
|
1775
|
+
checked: false,
|
|
1776
|
+
}));
|
|
1777
|
+
}
|
|
1778
|
+
if (subtasks !== undefined) {
|
|
1779
|
+
const existingIds = new Set(task.subtasks.map((item) => item.id));
|
|
1780
|
+
if (subtasks.some((item) => item.id !== undefined && !existingIds.has(item.id))) {
|
|
1781
|
+
throw new PlanStoreError("Subtask IDs must belong to the target task.", undefined, { errorCode: "SUBTASK_ID_INVALID" });
|
|
1782
|
+
}
|
|
1783
|
+
task.subtasks = subtasks.map((item) => ({ id: item.id ?? randomUUID(), title: item.title.trim(), description: item.description?.trim() ?? "", status: item.status ?? "planned", createdAt: task.subtasks.find((candidate) => candidate.id === item.id)?.createdAt ?? timestamp, updatedAt: timestamp }));
|
|
1784
|
+
}
|
|
1785
|
+
if (status !== undefined && status !== task.status) {
|
|
1786
|
+
// Record status change in the incremental statusLog.
|
|
1787
|
+
const entry = {
|
|
1788
|
+
id: createChecklistItemId(task.id, (task.statusLog?.length ?? 0) + 1, `${task.status}-${status}`),
|
|
1789
|
+
date: timestamp,
|
|
1790
|
+
fromStatus: task.status,
|
|
1791
|
+
toStatus: status,
|
|
1792
|
+
title: motivation?.split("\n")[0]?.trim() || `${task.status} → ${status}`,
|
|
1793
|
+
description: motivation?.trim() || "",
|
|
1794
|
+
};
|
|
1795
|
+
task.statusLog = [...(task.statusLog ?? []), entry];
|
|
1796
|
+
applyTaskLifecycleDates(task, status, timestamp);
|
|
1797
|
+
}
|
|
1798
|
+
task.updatedAt = timestamp;
|
|
1799
|
+
updatedTask = task;
|
|
1800
|
+
return task;
|
|
1801
|
+
});
|
|
1802
|
+
}
|
|
1803
|
+
catch (error) {
|
|
1804
|
+
const details = error instanceof PlanStoreError ? error.details : undefined;
|
|
1805
|
+
return { ...text(`Task update failed: ${error instanceof Error ? error.message : "unknown error"}`, { updated: false, errorCode: details?.errorCode ?? "TASK_UPDATE_FAILED" }), isError: true };
|
|
1806
|
+
}
|
|
1805
1807
|
await st.syncTaskStatusRollup(found.phase.id);
|
|
1806
1808
|
const features = (await st.loadFeatures()).features;
|
|
1807
1809
|
const t = updatedTask ?? found.task;
|
|
@@ -2564,8 +2566,8 @@ server.registerTool("planner-handoff-show", {
|
|
|
2564
2566
|
&& phase.handoffAudit.contentLength === phase.handoff.length);
|
|
2565
2567
|
const resumeReady = persistenceVerified && Boolean(phase.handoffAudit?.resumeReadyAt);
|
|
2566
2568
|
const status = resumeReady
|
|
2567
|
-
? "Resume-ready: persisted read-back
|
|
2568
|
-
: "NOT resume-ready: after reading this
|
|
2569
|
+
? "Resume-ready: persisted compact capsule read-back completed."
|
|
2570
|
+
: "NOT resume-ready: after reading this persisted capsule, call planner-handoff-verify with its contentHash. The planner derives legacy evidence from persisted state; rewrite only if the capsule itself omits resume-critical context.";
|
|
2569
2571
|
return text(`Handoff for ${r.compositeRef}\n${status}\nContent hash: ${contentHash}\n\n${phaseWorkMap.content}\n\nBefore proposing new work, reread the canonical phase and relevant sibling task full view; do not duplicate an already-owned capability.\n\n${bounded.content}`, {
|
|
2570
2572
|
phaseRef: r.compositeRef,
|
|
2571
2573
|
phaseId: r.phase.id,
|
|
@@ -2579,15 +2581,15 @@ server.registerTool("planner-handoff-show", {
|
|
|
2579
2581
|
});
|
|
2580
2582
|
});
|
|
2581
2583
|
server.registerTool("planner-handoff-verify", {
|
|
2582
|
-
description: "Finalize one persisted handoff as resume-ready
|
|
2584
|
+
description: "Finalize one persisted handoff as resume-ready after planner-handoff-show confirms the persisted compact resume capsule. Legacy sourceReviews and omissionsFound evidence remains accepted but is optional; the planner derives omitted evidence from persisted state.",
|
|
2583
2585
|
inputSchema: {
|
|
2584
2586
|
phaseRef: z.string().min(1),
|
|
2585
2587
|
expectedContentHash: z.string().regex(/^[a-f0-9]{64}$/),
|
|
2586
2588
|
sourceReviews: z.array(z.object({
|
|
2587
2589
|
source: z.enum(["conversation", "planner-entities", "working-tree", "verification-runtime", "peer-agent-output"]),
|
|
2588
2590
|
detail: z.string().min(12),
|
|
2589
|
-
})),
|
|
2590
|
-
omissionsFound: z.array(z.string().min(1)),
|
|
2591
|
+
})).optional().describe("Optional legacy evidence; omitted for compact handoffs."),
|
|
2592
|
+
omissionsFound: z.array(z.string().min(1)).optional().describe("Optional read-back gaps; omit when the persisted capsule is complete."),
|
|
2591
2593
|
},
|
|
2592
2594
|
}, async ({ phaseRef, expectedContentHash, sourceReviews, omissionsFound }) => {
|
|
2593
2595
|
const st = await requireStore();
|
|
@@ -2595,7 +2597,11 @@ server.registerTool("planner-handoff-verify", {
|
|
|
2595
2597
|
if (!r.ok)
|
|
2596
2598
|
return text(`❌ ${r.error}`, { resumeReady: false });
|
|
2597
2599
|
try {
|
|
2598
|
-
const result = await st.verifyPhaseHandoffReadBack(r.phase.id, {
|
|
2600
|
+
const result = await st.verifyPhaseHandoffReadBack(r.phase.id, {
|
|
2601
|
+
expectedContentHash,
|
|
2602
|
+
...(sourceReviews ? { sourceReviews } : {}),
|
|
2603
|
+
...(omissionsFound ? { omissionsFound } : {}),
|
|
2604
|
+
});
|
|
2599
2605
|
await st.writeGenerated();
|
|
2600
2606
|
return text(`✅ Handoff on ${r.compositeRef} is resume-ready after persisted read-back and source reconciliation.`, {
|
|
2601
2607
|
phaseRef: r.compositeRef,
|
|
@@ -2881,7 +2887,7 @@ server.registerTool("planner-idea-promotion-finalize", {
|
|
|
2881
2887
|
return text(`Idea promoted: ${formatIdeaRef(promoted.number)} → ${promoted.promotion.targetRef}`, { idea: promoted, promoted: true, targetRef: promoted.promotion.targetRef });
|
|
2882
2888
|
});
|
|
2883
2889
|
server.registerTool("planner-handoff-write", {
|
|
2884
|
-
description: "Persist a reconciled handoff candidate with durable context synchronization
|
|
2890
|
+
description: "Persist a compact reconciled handoff candidate with durable context synchronization. The result always has resumeReady=false until a separate planner-handoff-show read-back and planner-handoff-verify succeeds. Run planner-handoff-prepare first; provide only resume-critical context and link optional extended detail from .planner/docs/ Markdown.",
|
|
2885
2891
|
inputSchema: {
|
|
2886
2892
|
phaseRef: z.string().min(1).describe("Exact confirmed phase ref: P00x | P00x(F00x) | UUID | title."),
|
|
2887
2893
|
title: z.string().min(3).optional().describe("Meaningful handoff title summarizing the work."),
|
|
@@ -2897,7 +2903,7 @@ server.registerTool("planner-handoff-write", {
|
|
|
2897
2903
|
status: z.enum(["captured", "not-applicable"]),
|
|
2898
2904
|
detail: z.string().min(1),
|
|
2899
2905
|
})),
|
|
2900
|
-
}).optional().describe("
|
|
2906
|
+
}).optional().describe("Optional legacy completeness evidence. Compact handoffs omit this duplicate prose."),
|
|
2901
2907
|
coldStartInventory: z.object({
|
|
2902
2908
|
version: z.literal(HANDOFF_COLD_START_INVENTORY_VERSION),
|
|
2903
2909
|
sourceReviews: z.array(z.object({ source: z.string().min(1), detail: z.string().min(1) })),
|
|
@@ -2906,7 +2912,7 @@ server.registerTool("planner-handoff-write", {
|
|
|
2906
2912
|
items: z.array(z.string().min(1)),
|
|
2907
2913
|
notApplicableReason: z.string().min(1).optional().describe("Optional explanation only; every category still requires a concrete item, using an explicit verified-negative statement when nothing exists."),
|
|
2908
2914
|
})),
|
|
2909
|
-
}).optional().describe("
|
|
2915
|
+
}).optional().describe("Optional legacy cold-start evidence. Compact handoffs omit this duplicate prose."),
|
|
2910
2916
|
supportingDocuments: z.array(z.object({
|
|
2911
2917
|
path: z.string().min(1).describe("Committed Markdown path under .planner/docs/."),
|
|
2912
2918
|
description: z.string().min(1).describe("What the document contains and why the next agent needs it."),
|
|
@@ -2993,7 +2999,7 @@ server.registerTool("planner-handoff-write", {
|
|
|
2993
2999
|
},
|
|
2994
3000
|
});
|
|
2995
3001
|
await st.writeGenerated();
|
|
2996
|
-
return text(`⚠️ Handoff candidate persisted on ${r.compositeRef}, but it is NOT resume-ready yet. Required next action: call planner-handoff-show ${r.compositeRef}, read the
|
|
3002
|
+
return text(`⚠️ Handoff candidate persisted on ${r.compositeRef}, but it is NOT resume-ready yet. Required next action: call planner-handoff-show ${r.compositeRef}, read the persisted compact capsule, then call planner-handoff-verify with the returned contentHash. Rewrite only if resume-critical context is missing.`, {
|
|
2997
3003
|
phaseRef: r.compositeRef,
|
|
2998
3004
|
phaseId: r.phase.id,
|
|
2999
3005
|
updatedTaskIds: result.updatedTaskIds,
|
|
@@ -3004,8 +3010,7 @@ server.registerTool("planner-handoff-write", {
|
|
|
3004
3010
|
verificationRequired: true,
|
|
3005
3011
|
nextActions: [
|
|
3006
3012
|
`Call planner-handoff-show ${r.compositeRef} and read the entire persisted body.`,
|
|
3007
|
-
"
|
|
3008
|
-
"If any omission exists, run planner-handoff-prepare and planner-handoff-write again; otherwise call planner-handoff-verify with the shown contentHash.",
|
|
3013
|
+
"Call planner-handoff-verify with the shown contentHash; legacy sourceReviews and omissionsFound are optional because the planner derives omitted evidence from persisted state.",
|
|
3009
3014
|
],
|
|
3010
3015
|
});
|
|
3011
3016
|
}
|
|
@@ -3017,7 +3022,7 @@ server.registerTool("planner-handoff-write", {
|
|
|
3017
3022
|
}
|
|
3018
3023
|
});
|
|
3019
3024
|
server.registerTool("planner-handoff-prepare", {
|
|
3020
|
-
description: "Identify or
|
|
3025
|
+
description: "Identify or prepare the exact handoff target. With a confirmed phaseRef, returns a compact resume scaffold, bounded priority-ordered phase work map, current token, missing task evidence, and inline budget. Reread the canonical phase and relevant sibling tasks before proposing work.",
|
|
3021
3026
|
inputSchema: { phaseRef: z.string().min(1).optional() },
|
|
3022
3027
|
}, async ({ phaseRef }) => {
|
|
3023
3028
|
if (!phaseRef)
|
|
@@ -3032,9 +3037,10 @@ server.registerTool("planner-handoff-prepare", {
|
|
|
3032
3037
|
return text(`❌ ${r.error}`);
|
|
3033
3038
|
try {
|
|
3034
3039
|
const audit = await st.preparePhaseHandoff(r.phase.id);
|
|
3040
|
+
const { completenessCategories: _completenessCategories, coldStartSourceReviews: _coldStartSourceReviews, coldStartInventoryCategories: _coldStartInventoryCategories, ...compactAudit } = audit;
|
|
3035
3041
|
const missing = audit.missingCompletionTasks.map((task) => `- T${String(task.number).padStart(3, "0")} — ${task.title}`).join("\n") || "- None";
|
|
3036
3042
|
return text([
|
|
3037
|
-
`Handoff preparation
|
|
3043
|
+
`Handoff preparation for ${r.compositeRef}`,
|
|
3038
3044
|
`Base handoffUpdatedAt: ${audit.handoffUpdatedAt || "(empty)"}`,
|
|
3039
3045
|
"Required human inputs before drafting:",
|
|
3040
3046
|
...audit.requiredHumanInputs.map((input) => `- ${input.id} — ${input.description}`),
|
|
@@ -3045,18 +3051,18 @@ server.registerTool("planner-handoff-prepare", {
|
|
|
3045
3051
|
"Done tasks missing durable completion/verification evidence:",
|
|
3046
3052
|
missing,
|
|
3047
3053
|
"",
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
"",
|
|
3051
|
-
`
|
|
3052
|
-
...audit.coldStartSourceReviews.map((entry) => `- ${entry.id} — ${entry.label}`),
|
|
3053
|
-
"Required cold-start inventory — every category needs a concrete item (use an explicit verified-negative statement when nothing exists), and every item must appear verbatim in the handoff or a validated supporting document:",
|
|
3054
|
-
...audit.coldStartInventoryCategories.map((entry) => `- ${entry.id} — ${entry.label}`),
|
|
3055
|
-
`Maximum canonical inline content: ${audit.maxContentChars} characters. Link extended material from .planner/docs/.`,
|
|
3054
|
+
"Compact handoff contract: include only the exact focus/resume point, current or partial state, constraints, blockers, decisions, verification, and ordered next actions needed by the next agent.",
|
|
3055
|
+
"No Markdown heading is mandatory: concise free-form resume prose is accepted. Use headings only when they make the capsule clearer.",
|
|
3056
|
+
"Completeness audit and cold-start evidence are planner-owned metadata; do not copy their categories or source reviews into Markdown.",
|
|
3057
|
+
`Inline target: ${audit.targetContentChars} characters; absolute compatibility ceiling: ${audit.maxContentChars}. Extended detail is externalized automatically when needed.`,
|
|
3056
3058
|
"",
|
|
3057
3059
|
"Existing active handoff (reconcile all still-relevant content):",
|
|
3058
3060
|
audit.handoff.trim() || "(none)",
|
|
3059
|
-
].join("\n"), {
|
|
3061
|
+
].join("\n"), {
|
|
3062
|
+
phaseRef: r.compositeRef,
|
|
3063
|
+
...compactAudit,
|
|
3064
|
+
evidenceContract: "Planner-owned completeness and cold-start evidence is derived from persisted state; no category inventory is required from the agent.",
|
|
3065
|
+
});
|
|
3060
3066
|
}
|
|
3061
3067
|
catch (error) {
|
|
3062
3068
|
const message = error instanceof Error ? error.message : String(error);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-plan/mcp",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.28",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "MCP server for Agent Plan, exposing planner-* tools over stdio.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -43,8 +43,8 @@
|
|
|
43
43
|
"dependencies": {
|
|
44
44
|
"@modelcontextprotocol/sdk": "^1.17.0",
|
|
45
45
|
"zod": "^3.24.0",
|
|
46
|
-
"@agent-plan/core": "0.2.
|
|
47
|
-
"@agent-plan/server": "0.2.
|
|
46
|
+
"@agent-plan/core": "0.2.28",
|
|
47
|
+
"@agent-plan/server": "0.2.28"
|
|
48
48
|
},
|
|
49
49
|
"scripts": {
|
|
50
50
|
"build": "tsc -p tsconfig.json",
|