@agent-plan/core 0.2.29 → 0.3.1
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/accepted-decision-guard.d.ts +94 -0
- package/dist/accepted-decision-guard.d.ts.map +1 -0
- package/dist/accepted-decision-guard.js +110 -0
- package/dist/checklist.d.ts +28 -0
- package/dist/checklist.d.ts.map +1 -1
- package/dist/checklist.js +59 -0
- package/dist/feature-delete.d.ts +62 -0
- package/dist/feature-delete.d.ts.map +1 -0
- package/dist/feature-delete.js +49 -0
- package/dist/guard-decision.d.ts +128 -0
- package/dist/guard-decision.d.ts.map +1 -0
- package/dist/guard-decision.js +269 -0
- package/dist/guard-state.d.ts +14 -0
- package/dist/guard-state.d.ts.map +1 -0
- package/dist/guard-state.js +35 -0
- package/dist/handoff-context.d.ts +130 -1
- package/dist/handoff-context.d.ts.map +1 -1
- package/dist/handoff-context.js +167 -9
- package/dist/handoff-reply.d.ts +113 -0
- package/dist/handoff-reply.d.ts.map +1 -0
- package/dist/handoff-reply.js +279 -0
- package/dist/index.d.ts +18 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +18 -1
- package/dist/load-reply.d.ts +63 -0
- package/dist/load-reply.d.ts.map +1 -0
- package/dist/load-reply.js +64 -0
- package/dist/mutation-reply.d.ts +105 -0
- package/dist/mutation-reply.d.ts.map +1 -0
- package/dist/mutation-reply.js +203 -0
- package/dist/naming.d.ts +1 -1
- package/dist/naming.d.ts.map +1 -1
- package/dist/naming.js +1 -1
- package/dist/on-hold.d.ts +55 -0
- package/dist/on-hold.d.ts.map +1 -0
- package/dist/on-hold.js +123 -0
- package/dist/package-version.d.ts +13 -0
- package/dist/package-version.d.ts.map +1 -1
- package/dist/package-version.js +32 -0
- package/dist/pagination.d.ts +19 -0
- package/dist/pagination.d.ts.map +1 -0
- package/dist/pagination.js +15 -0
- package/dist/payload-fallback.js +1 -1
- package/dist/plan-store.d.ts +141 -8
- package/dist/plan-store.d.ts.map +1 -1
- package/dist/plan-store.js +459 -87
- package/dist/project-context-migration.d.ts +5 -0
- package/dist/project-context-migration.d.ts.map +1 -1
- package/dist/project-context-migration.js +13 -0
- package/dist/project-context.d.ts +397 -0
- package/dist/project-context.d.ts.map +1 -0
- package/dist/project-context.js +294 -0
- package/dist/recap.d.ts.map +1 -1
- package/dist/recap.js +33 -0
- package/dist/recommend-reply.d.ts +51 -0
- package/dist/recommend-reply.d.ts.map +1 -0
- package/dist/recommend-reply.js +93 -0
- package/dist/refs.d.ts +16 -0
- package/dist/refs.d.ts.map +1 -1
- package/dist/refs.js +38 -0
- package/dist/renderer.d.ts.map +1 -1
- package/dist/schema.d.ts +7 -2
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +8 -3
- package/dist/stranded-phase.d.ts +73 -0
- package/dist/stranded-phase.d.ts.map +1 -0
- package/dist/stranded-phase.js +93 -0
- package/dist/task-completion-gate.d.ts +84 -0
- package/dist/task-completion-gate.d.ts.map +1 -0
- package/dist/task-completion-gate.js +72 -0
- package/dist/task-context.d.ts +1 -0
- package/dist/task-context.d.ts.map +1 -1
- package/dist/task-context.js +42 -14
- package/dist/task-lifecycle.d.ts +12 -0
- package/dist/task-lifecycle.d.ts.map +1 -0
- package/dist/task-lifecycle.js +23 -0
- package/dist/task-order-context.d.ts +72 -0
- package/dist/task-order-context.d.ts.map +1 -0
- package/dist/task-order-context.js +82 -0
- package/dist/task-selection.d.ts +4 -1
- package/dist/task-selection.d.ts.map +1 -1
- package/dist/task-selection.js +36 -14
- package/dist/task-start-outcome.d.ts +26 -0
- package/dist/task-start-outcome.d.ts.map +1 -1
- package/dist/task-start-outcome.js +23 -0
- package/dist/text-bounds.d.ts +32 -0
- package/dist/text-bounds.d.ts.map +1 -0
- package/dist/text-bounds.js +43 -0
- package/dist/write-coordination.d.ts +2 -2
- package/dist/write-coordination.js +2 -2
- package/package.json +1 -1
- package/planner-skill.md +30 -12
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
import { MAX_HANDOFF_CONTENT_CHARS, handoffContentHash, } from "./handoff-context.js";
|
|
2
|
+
import { boundedContentForTransport } from "./text-bounds.js";
|
|
3
|
+
/**
|
|
4
|
+
* Render a HandoffContractError (handoff refresh/prepare denial) as a
|
|
5
|
+
* text/structured pair. Both adapters carried an identical copy; kept here
|
|
6
|
+
* so the message and the `errorCode`/details shape can never drift between
|
|
7
|
+
* MCP and Pi. Each adapter still owns its own envelope (`isError` +
|
|
8
|
+
* `structuredContent` for MCP, `details` for Pi) — see AGENTS.md rule 4.
|
|
9
|
+
*/
|
|
10
|
+
export function handoffContractFailureReply(error, action = "refresh") {
|
|
11
|
+
const recovery = typeof error.details.recovery === "string" ? ` Recovery: ${error.details.recovery}` : "";
|
|
12
|
+
return {
|
|
13
|
+
text: `❌ Handoff ${action} denied [${error.code}]: ${error.message}${recovery}`,
|
|
14
|
+
structured: { errorCode: error.code, ...error.details },
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
/** Bound raw handoff content to the absolute compatibility ceiling for
|
|
18
|
+
* transport. Legacy or externally-written content can exceed the ceiling;
|
|
19
|
+
* everything produced through the normal write path already respects it.
|
|
20
|
+
* Cuts at a safe boundary (paragraph, then line, then word — see
|
|
21
|
+
* boundedContentForTransport / truncateAtSafeBoundary in handoff-context.ts)
|
|
22
|
+
* rather than an arbitrary character offset, so the visible remainder never
|
|
23
|
+
* ends mid-word. */
|
|
24
|
+
export function boundedHandoffForTransport(content) {
|
|
25
|
+
const suffix = "\n\n[Legacy handoff truncated for transport safety. Move extended detail to a linked file under .planner/docs/ and refresh the handoff.]";
|
|
26
|
+
if (content.length <= MAX_HANDOFF_CONTENT_CHARS)
|
|
27
|
+
return { content, fullLength: content.length, truncated: false };
|
|
28
|
+
const bounded = boundedContentForTransport(content, Math.max(0, MAX_HANDOFF_CONTENT_CHARS - suffix.length));
|
|
29
|
+
return { content: `${bounded.content}${suffix}`, fullLength: content.length, truncated: true };
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Render the existing handoff's auto-externalized documents (see
|
|
33
|
+
* HandoffExternalizedDocumentAudit) for the prepare text channel. Shares one
|
|
34
|
+
* budget across every document rather than bounding each independently to
|
|
35
|
+
* MAX_HANDOFF_CONTENT_CHARS, so several documents from successive rewrites
|
|
36
|
+
* cannot each spend the full ceiling — the reporting phase's own P082 capsule
|
|
37
|
+
* carried two linked documents at once, and a prior capsule's externalized
|
|
38
|
+
* document can by itself already be as large as the ceiling (a large draft
|
|
39
|
+
* is exactly what got externalized). Every document is still named — path,
|
|
40
|
+
* description, and heading table of contents — even when its content does
|
|
41
|
+
* not fit, so "elided" never again means "invisible": inlining degrades to
|
|
42
|
+
* naming, it never degrades to silence.
|
|
43
|
+
*/
|
|
44
|
+
function renderExternalizedHandoffDocuments(documents) {
|
|
45
|
+
let remaining = MAX_HANDOFF_CONTENT_CHARS;
|
|
46
|
+
const blocks = [];
|
|
47
|
+
const structured = [];
|
|
48
|
+
for (const document of documents) {
|
|
49
|
+
const headingsNote = document.headings.length > 0 ? ` (sections: ${document.headings.join(", ")})` : "";
|
|
50
|
+
const header = `- ${document.path} — ${document.description}${headingsNote}`;
|
|
51
|
+
if (document.missing) {
|
|
52
|
+
blocks.push(`${header}\n [Not found on disk; its content is unavailable. Treat any facts it carried as unverified until confirmed another way.]`);
|
|
53
|
+
structured.push({ path: document.path, headings: document.headings, contentLength: document.contentLength, missing: true, inlined: false, truncated: false });
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
if (remaining <= 0) {
|
|
57
|
+
blocks.push(`${header}\n [Not inlined: the reconciliation budget for externalized detail is exhausted. Read this document directly before drafting.]`);
|
|
58
|
+
structured.push({ path: document.path, headings: document.headings, contentLength: document.contentLength, missing: false, inlined: false, truncated: false });
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
const bounded = boundedContentForTransport(document.content ?? "", remaining);
|
|
62
|
+
remaining -= bounded.content.length;
|
|
63
|
+
const continuation = bounded.truncated
|
|
64
|
+
? "\n\n[Not fully inlined: remaining detail exceeds the reconciliation budget. Read this document directly for the rest.]"
|
|
65
|
+
: "";
|
|
66
|
+
blocks.push(`${header}\n${bounded.content}${continuation}`);
|
|
67
|
+
structured.push({ path: document.path, headings: document.headings, contentLength: document.contentLength, missing: false, inlined: true, truncated: bounded.truncated });
|
|
68
|
+
}
|
|
69
|
+
return { text: blocks.join("\n\n"), structured };
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Transport shape of the phase work map. `entries` is the machine-readable
|
|
73
|
+
* twin of the rendered `content` — the same siblings spelled as JSON — and
|
|
74
|
+
* no caller reads it off a reply, so only the counts travel. Pass
|
|
75
|
+
* `includeContent` for the reply that renders the map in no other channel.
|
|
76
|
+
*/
|
|
77
|
+
function workMapForTransport(map, includeContent) {
|
|
78
|
+
return {
|
|
79
|
+
...(includeContent ? { content: map.content } : {}),
|
|
80
|
+
total: map.total,
|
|
81
|
+
truncated: map.truncated,
|
|
82
|
+
maxChars: map.maxChars,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Transport shape of the persisted audit. `entries`, `coldStartInventory`
|
|
87
|
+
* and `readBackSourceReviews` are planner-owned legacy evidence that agents
|
|
88
|
+
* are told not to reproduce (P100(F021)/T396) and that nothing reads back
|
|
89
|
+
* off a reply; only the verification facts travel.
|
|
90
|
+
*/
|
|
91
|
+
function handoffAuditForTransport(audit) {
|
|
92
|
+
if (!audit)
|
|
93
|
+
return null;
|
|
94
|
+
return {
|
|
95
|
+
version: audit.version,
|
|
96
|
+
contentHash: audit.contentHash,
|
|
97
|
+
contentLength: audit.contentLength,
|
|
98
|
+
verifiedAt: audit.verifiedAt,
|
|
99
|
+
resumeReadyAt: audit.resumeReadyAt,
|
|
100
|
+
supportingDocuments: audit.supportingDocuments,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Build the reply for `planner-handoff-show` / `handoff_show`, covering the
|
|
105
|
+
* empty, archived, and active branches through one shared shape so no
|
|
106
|
+
* surface keeps its own construction.
|
|
107
|
+
*
|
|
108
|
+
* Placement: the resume capsule body is prose meant to be read, and every
|
|
109
|
+
* existing consumer reads it from `text` (never from `structured`), so
|
|
110
|
+
* `text` is its one home; `structured` carries only `fullLength`/
|
|
111
|
+
* `truncated` flags plus the identifiers, hash, and verification flags a
|
|
112
|
+
* structuredContent-only host needs to drive the candidate-then-verify
|
|
113
|
+
* contract (call verify with the right hash) without needing the prose
|
|
114
|
+
* itself.
|
|
115
|
+
*/
|
|
116
|
+
export function buildHandoffShowReply(input) {
|
|
117
|
+
if (input.kind === "empty") {
|
|
118
|
+
return {
|
|
119
|
+
text: `No handoff set on ${input.phaseRef}.`,
|
|
120
|
+
structured: {
|
|
121
|
+
phaseRef: input.phaseRef,
|
|
122
|
+
phaseId: input.phaseId,
|
|
123
|
+
content: "",
|
|
124
|
+
empty: true,
|
|
125
|
+
resumeReady: false,
|
|
126
|
+
handoffAudit: handoffAuditForTransport(input.handoffAudit),
|
|
127
|
+
},
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
if (input.kind === "archived") {
|
|
131
|
+
const bounded = boundedHandoffForTransport(input.content);
|
|
132
|
+
const supportingDocuments = input.handoffAudit?.supportingDocuments ?? [];
|
|
133
|
+
const documentLines = supportingDocuments.map((document) => `- ${document.path} — ${document.description}`);
|
|
134
|
+
return {
|
|
135
|
+
text: [
|
|
136
|
+
`Archived terminal-phase handoff for ${input.phaseRef} (${input.archiveReason}; archived ${input.archivedAt})`,
|
|
137
|
+
...(documentLines.length ? ["Supporting documents:", ...documentLines] : []),
|
|
138
|
+
"",
|
|
139
|
+
bounded.content,
|
|
140
|
+
].join("\n"),
|
|
141
|
+
structured: {
|
|
142
|
+
phaseRef: input.phaseRef,
|
|
143
|
+
phaseId: input.phaseId,
|
|
144
|
+
active: false,
|
|
145
|
+
archived: true,
|
|
146
|
+
archiveReason: input.archiveReason,
|
|
147
|
+
archivedAt: input.archivedAt,
|
|
148
|
+
archiveFile: input.archiveFile,
|
|
149
|
+
fullLength: bounded.fullLength,
|
|
150
|
+
truncated: bounded.truncated,
|
|
151
|
+
supportingDocuments,
|
|
152
|
+
empty: false,
|
|
153
|
+
resumeReady: false,
|
|
154
|
+
handoffAudit: handoffAuditForTransport(input.handoffAudit),
|
|
155
|
+
},
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
const { phase, phaseRef, phaseWorkMap } = input;
|
|
159
|
+
const bounded = boundedHandoffForTransport(phase.handoff);
|
|
160
|
+
const contentHash = handoffContentHash(phase.handoff);
|
|
161
|
+
const persistenceVerified = Boolean(phase.handoffAudit
|
|
162
|
+
&& phase.handoffAudit.contentHash === contentHash
|
|
163
|
+
&& phase.handoffAudit.contentLength === phase.handoff.length);
|
|
164
|
+
const resumeReady = persistenceVerified && Boolean(phase.handoffAudit?.resumeReadyAt);
|
|
165
|
+
const status = resumeReady
|
|
166
|
+
? "Resume-ready: persisted compact capsule read-back completed."
|
|
167
|
+
: "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.";
|
|
168
|
+
return {
|
|
169
|
+
text: `Handoff for ${phaseRef}\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}`,
|
|
170
|
+
structured: {
|
|
171
|
+
phaseRef,
|
|
172
|
+
phaseId: phase.id,
|
|
173
|
+
// The rendered map is already in `text`, so only the counts travel.
|
|
174
|
+
phaseWorkMap: workMapForTransport(phaseWorkMap, false),
|
|
175
|
+
fullLength: bounded.fullLength,
|
|
176
|
+
truncated: bounded.truncated,
|
|
177
|
+
contentHash,
|
|
178
|
+
persistenceVerified,
|
|
179
|
+
resumeReady,
|
|
180
|
+
verificationRequired: !resumeReady,
|
|
181
|
+
handoffAudit: handoffAuditForTransport(phase.handoffAudit),
|
|
182
|
+
},
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
/** Single source of truth for the prepare-reply evidence-contract sentence,
|
|
186
|
+
* previously hardcoded identically in both adapters. */
|
|
187
|
+
export const HANDOFF_PREPARE_EVIDENCE_CONTRACT = "Planner-owned completeness and cold-start evidence is derived from persisted state; no category inventory is required from the agent.";
|
|
188
|
+
/**
|
|
189
|
+
* Build the reply for `planner-handoff-prepare` / `handoff_prepare`.
|
|
190
|
+
*
|
|
191
|
+
* Placement: `draftTemplate` has no consumer in `text` (only the intro line
|
|
192
|
+
* pointing at it is read there) and every existing consumer reads it from
|
|
193
|
+
* `structured`, so `structured` is its one home; `text` gets a pointer
|
|
194
|
+
* instead of a second copy. The existing active handoff being reconciled is
|
|
195
|
+
* the opposite: nothing reads it from `structured`, so it stays prose in
|
|
196
|
+
* `text` only, bounded for transport, with `structured` carrying just its
|
|
197
|
+
* length/truncated flags. `missingCompletionTasks` duplicates the same
|
|
198
|
+
* titles already rendered into `text`; `structured` keeps only the task ids
|
|
199
|
+
* needed to address `taskUpdates`. The existing handoff's auto-externalized
|
|
200
|
+
* documents follow the same rule as the existing handoff itself: their
|
|
201
|
+
* bodies are prose for `text` (see renderExternalizedHandoffDocuments),
|
|
202
|
+
* `structured` keeps only path/headings/size/inlined/truncated per document
|
|
203
|
+
* (existingHandoffExternalizedDocuments) — see P104(F005)/T415's accepted
|
|
204
|
+
* decision for why inlining (budget permitting) was chosen over a
|
|
205
|
+
* separate-field or names-only reply.
|
|
206
|
+
*/
|
|
207
|
+
export function buildHandoffPrepareReply(input) {
|
|
208
|
+
const { phaseRef, audit } = input;
|
|
209
|
+
const { completenessCategories: _completenessCategories, coldStartSourceReviews: _coldStartSourceReviews, coldStartInventoryCategories: _coldStartInventoryCategories, handoff, missingCompletionTasks,
|
|
210
|
+
// Guidance is instruction the agent reads; the text channel renders it,
|
|
211
|
+
// so it must not travel again in the structured payload.
|
|
212
|
+
supportingDocumentsGuidance: _supportingDocumentsGuidance,
|
|
213
|
+
// Raw document bodies; never spread as-is into structured (see below —
|
|
214
|
+
// content stays text-only, structured keeps only the small facts).
|
|
215
|
+
externalizedHandoffDocuments, ...structuredAudit } = audit;
|
|
216
|
+
const missingLines = missingCompletionTasks
|
|
217
|
+
.map((task) => `- T${String(task.number).padStart(3, "0")} — ${task.title}`)
|
|
218
|
+
.join("\n") || "- None";
|
|
219
|
+
const existingBounded = boundedHandoffForTransport(handoff);
|
|
220
|
+
const existingText = existingBounded.content.trim() || "(none)";
|
|
221
|
+
const externalized = renderExternalizedHandoffDocuments(externalizedHandoffDocuments);
|
|
222
|
+
const text = [
|
|
223
|
+
`Handoff preparation for ${phaseRef}`,
|
|
224
|
+
`Base handoffUpdatedAt: ${audit.handoffUpdatedAt || "(empty)"}`,
|
|
225
|
+
"Required human inputs before drafting:",
|
|
226
|
+
...audit.requiredHumanInputs.map((requiredInput) => `- ${requiredInput.id} — ${requiredInput.description}`),
|
|
227
|
+
"Planner-generated metadata (do not add these to Markdown): Created at, Updated at, Reason.",
|
|
228
|
+
"The exact drafting scaffold is provided in the structured result's draftTemplate field; replace every angle-bracket placeholder and retain every heading.",
|
|
229
|
+
"",
|
|
230
|
+
"Done tasks missing durable completion/verification evidence:",
|
|
231
|
+
missingLines,
|
|
232
|
+
"",
|
|
233
|
+
"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.",
|
|
234
|
+
"No Markdown heading is mandatory: concise free-form resume prose is accepted. Use headings only when they make the capsule clearer.",
|
|
235
|
+
"Completeness audit and cold-start evidence are planner-owned metadata; do not copy their categories or source reviews into Markdown.",
|
|
236
|
+
`Inline target: ${audit.targetContentChars} characters; absolute compatibility ceiling: ${audit.maxContentChars}. Extended detail is externalized automatically when needed.`,
|
|
237
|
+
`Supporting documents: ${audit.supportingDocumentsGuidance}`,
|
|
238
|
+
"",
|
|
239
|
+
"Existing active handoff (reconcile all still-relevant content):",
|
|
240
|
+
existingText,
|
|
241
|
+
// Content this same active handoff previously moved out automatically.
|
|
242
|
+
// Reconciling "the existing handoff" means reconciling this too — it is
|
|
243
|
+
// not optional background reading, it is the part most likely to hold a
|
|
244
|
+
// fact no task holds (see AGENTS.md rule 4: this belongs here once, not
|
|
245
|
+
// rediscovered per adapter).
|
|
246
|
+
...(externalizedHandoffDocuments.length > 0
|
|
247
|
+
? [
|
|
248
|
+
"",
|
|
249
|
+
"Externalized detail from the existing handoff (auto-moved out when the capsule exceeded the inline target; reconcile before drafting — this is part of \"the existing handoff\", not optional background):",
|
|
250
|
+
externalized.text,
|
|
251
|
+
]
|
|
252
|
+
: []),
|
|
253
|
+
...(audit.priorSectionHeadings.length > 0
|
|
254
|
+
? [
|
|
255
|
+
"",
|
|
256
|
+
`Prior sections to account for: ${audit.priorSectionHeadings.join(", ")}. Any of these missing as a "##" heading from the written content will be refused (HANDOFF_SECTION_RECONCILIATION_REQUIRED) unless you also submit sectionDispositions naming what happened to it (dropped, superseded, or folded into another section).`,
|
|
257
|
+
]
|
|
258
|
+
: []),
|
|
259
|
+
].join("\n");
|
|
260
|
+
return {
|
|
261
|
+
text,
|
|
262
|
+
structured: {
|
|
263
|
+
phaseRef,
|
|
264
|
+
...structuredAudit,
|
|
265
|
+
// Prepare renders the map in no text channel, so it keeps `content`
|
|
266
|
+
// here — but not the JSON twin of the same siblings.
|
|
267
|
+
phaseWorkMap: workMapForTransport(structuredAudit.phaseWorkMap, true),
|
|
268
|
+
existingCompletenessAudit: handoffAuditForTransport(structuredAudit.existingCompletenessAudit),
|
|
269
|
+
existingHandoffLength: existingBounded.fullLength,
|
|
270
|
+
existingHandoffTruncated: existingBounded.truncated,
|
|
271
|
+
// Bodies are text-only (see externalized.text above); structured keeps
|
|
272
|
+
// only the facts a structuredContent-only host needs to know more
|
|
273
|
+
// exists than it can see: path, headings, size, and whether this call
|
|
274
|
+
// inlined it.
|
|
275
|
+
existingHandoffExternalizedDocuments: externalized.structured,
|
|
276
|
+
evidenceContract: HANDOFF_PREPARE_EVIDENCE_CONTRACT,
|
|
277
|
+
},
|
|
278
|
+
};
|
|
279
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
export * from "./naming.js";
|
|
2
2
|
export * from "./refs.js";
|
|
3
|
+
export * from "./render-utils.js";
|
|
4
|
+
export * from "./pagination.js";
|
|
5
|
+
export * from "./task-lifecycle.js";
|
|
3
6
|
export * from "./schema.js";
|
|
4
7
|
export * from "./checklist.js";
|
|
5
8
|
export * from "./recap.js";
|
|
@@ -7,18 +10,32 @@ export * from "./planner-rules.js";
|
|
|
7
10
|
export * from "./planner-skill.js";
|
|
8
11
|
export * from "./display-status.js";
|
|
9
12
|
export * from "./description-freshness.js";
|
|
13
|
+
export * from "./text-bounds.js";
|
|
10
14
|
export * from "./task-context.js";
|
|
11
15
|
export * from "./task-selection.js";
|
|
12
16
|
export * from "./task-start-outcome.js";
|
|
17
|
+
export * from "./task-order-context.js";
|
|
18
|
+
export * from "./task-completion-gate.js";
|
|
19
|
+
export * from "./stranded-phase.js";
|
|
13
20
|
export * from "./read-tracking.js";
|
|
14
21
|
export * from "./payload-fallback.js";
|
|
15
22
|
export * from "./handoff-context.js";
|
|
23
|
+
export * from "./handoff-reply.js";
|
|
24
|
+
export * from "./mutation-reply.js";
|
|
25
|
+
export * from "./recommend-reply.js";
|
|
26
|
+
export * from "./feature-delete.js";
|
|
27
|
+
export * from "./accepted-decision-guard.js";
|
|
16
28
|
export * from "./project-context-migration.js";
|
|
29
|
+
export * from "./project-context.js";
|
|
30
|
+
export * from "./load-reply.js";
|
|
31
|
+
export * from "./on-hold.js";
|
|
17
32
|
export * from "./requirement-macro-tasks.js";
|
|
18
33
|
export * from "./package-version.js";
|
|
19
34
|
export * from "./runtime-diagnostics.js";
|
|
20
35
|
export * from "./write-coordination.js";
|
|
21
|
-
export
|
|
36
|
+
export * from "./guard-decision.js";
|
|
37
|
+
export * from "./guard-state.js";
|
|
38
|
+
export { PlanStore, PlanStoreError, PlanStaleWriteError, PlanUnsupportedAllocationKindError, assertPlannerRevision, setWriteBusyHook, setWriteNotifyHook, migrateToUuids, migrateToGlobalSequence, withFeatureLock, nowISO, type IdeaCreateInput, type IdeaUpdateInput, type IdeaPromotionTargetInput, type AcceptedDecisionOwner, type AcceptedDecisionCreateInput, type AcceptedDecisionUpdateInput, type RecordProjectContextReadInput, type RecordProjectContextReadResult, type PhaseHandoffSummary, type OrphanPhaseSummary } from "./plan-store.js";
|
|
22
39
|
export { PlanRenderer } from "./renderer.js";
|
|
23
40
|
export { ExportService } from "./export-service.js";
|
|
24
41
|
export type { CodebaseProfile, ResumeFocus, ActivityEntry, ActivityLog, AmbientFacts } from "./schema.js";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,aAAa,CAAC;AAC5B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,YAAY,CAAC;AAC3B,cAAc,oBAAoB,CAAC;AACnC,cAAc,oBAAoB,CAAC;AACnC,cAAc,qBAAqB,CAAC;AACpC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,mBAAmB,CAAC;AAClC,cAAc,qBAAqB,CAAC;AACpC,cAAc,yBAAyB,CAAC;AACxC,cAAc,oBAAoB,CAAC;AACnC,cAAc,uBAAuB,CAAC;AACtC,cAAc,sBAAsB,CAAC;AACrC,cAAc,gCAAgC,CAAC;AAC/C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,sBAAsB,CAAC;AACrC,cAAc,0BAA0B,CAAC;AACzC,cAAc,yBAAyB,CAAC;AACxC,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,mBAAmB,EAAE,kCAAkC,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,cAAc,EAAE,uBAAuB,EAAE,eAAe,EAAE,KAAK,eAAe,EAAE,KAAK,eAAe,EAAE,KAAK,wBAAwB,EAAE,KAAK,qBAAqB,EAAE,KAAK,2BAA2B,EAAE,KAAK,2BAA2B,EAAE,KAAK,mBAAmB,EAAE,KAAK,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,mBAAmB,CAAC;AAClC,cAAc,iBAAiB,CAAC;AAChC,cAAc,qBAAqB,CAAC;AACpC,cAAc,aAAa,CAAC;AAC5B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,YAAY,CAAC;AAC3B,cAAc,oBAAoB,CAAC;AACnC,cAAc,oBAAoB,CAAC;AACnC,cAAc,qBAAqB,CAAC;AACpC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,kBAAkB,CAAC;AACjC,cAAc,mBAAmB,CAAC;AAClC,cAAc,qBAAqB,CAAC;AACpC,cAAc,yBAAyB,CAAC;AACxC,cAAc,yBAAyB,CAAC;AACxC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,qBAAqB,CAAC;AACpC,cAAc,oBAAoB,CAAC;AACnC,cAAc,uBAAuB,CAAC;AACtC,cAAc,sBAAsB,CAAC;AACrC,cAAc,oBAAoB,CAAC;AACnC,cAAc,qBAAqB,CAAC;AACpC,cAAc,sBAAsB,CAAC;AACrC,cAAc,qBAAqB,CAAC;AACpC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,gCAAgC,CAAC;AAC/C,cAAc,sBAAsB,CAAC;AACrC,cAAc,iBAAiB,CAAC;AAChC,cAAc,cAAc,CAAC;AAC7B,cAAc,8BAA8B,CAAC;AAC7C,cAAc,sBAAsB,CAAC;AACrC,cAAc,0BAA0B,CAAC;AACzC,cAAc,yBAAyB,CAAC;AACxC,cAAc,qBAAqB,CAAC;AACpC,cAAc,kBAAkB,CAAC;AACjC,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,mBAAmB,EAAE,kCAAkC,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,cAAc,EAAE,uBAAuB,EAAE,eAAe,EAAE,MAAM,EAAE,KAAK,eAAe,EAAE,KAAK,eAAe,EAAE,KAAK,wBAAwB,EAAE,KAAK,qBAAqB,EAAE,KAAK,2BAA2B,EAAE,KAAK,2BAA2B,EAAE,KAAK,6BAA6B,EAAE,KAAK,8BAA8B,EAAE,KAAK,mBAAmB,EAAE,KAAK,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAC3hB,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,YAAY,EAAE,eAAe,EAAE,WAAW,EAAE,aAAa,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
export * from "./naming.js";
|
|
2
2
|
export * from "./refs.js";
|
|
3
|
+
export * from "./render-utils.js";
|
|
4
|
+
export * from "./pagination.js";
|
|
5
|
+
export * from "./task-lifecycle.js";
|
|
3
6
|
export * from "./schema.js";
|
|
4
7
|
export * from "./checklist.js";
|
|
5
8
|
export * from "./recap.js";
|
|
@@ -7,17 +10,31 @@ export * from "./planner-rules.js";
|
|
|
7
10
|
export * from "./planner-skill.js";
|
|
8
11
|
export * from "./display-status.js";
|
|
9
12
|
export * from "./description-freshness.js";
|
|
13
|
+
export * from "./text-bounds.js";
|
|
10
14
|
export * from "./task-context.js";
|
|
11
15
|
export * from "./task-selection.js";
|
|
12
16
|
export * from "./task-start-outcome.js";
|
|
17
|
+
export * from "./task-order-context.js";
|
|
18
|
+
export * from "./task-completion-gate.js";
|
|
19
|
+
export * from "./stranded-phase.js";
|
|
13
20
|
export * from "./read-tracking.js";
|
|
14
21
|
export * from "./payload-fallback.js";
|
|
15
22
|
export * from "./handoff-context.js";
|
|
23
|
+
export * from "./handoff-reply.js";
|
|
24
|
+
export * from "./mutation-reply.js";
|
|
25
|
+
export * from "./recommend-reply.js";
|
|
26
|
+
export * from "./feature-delete.js";
|
|
27
|
+
export * from "./accepted-decision-guard.js";
|
|
16
28
|
export * from "./project-context-migration.js";
|
|
29
|
+
export * from "./project-context.js";
|
|
30
|
+
export * from "./load-reply.js";
|
|
31
|
+
export * from "./on-hold.js";
|
|
17
32
|
export * from "./requirement-macro-tasks.js";
|
|
18
33
|
export * from "./package-version.js";
|
|
19
34
|
export * from "./runtime-diagnostics.js";
|
|
20
35
|
export * from "./write-coordination.js";
|
|
21
|
-
export
|
|
36
|
+
export * from "./guard-decision.js";
|
|
37
|
+
export * from "./guard-state.js";
|
|
38
|
+
export { PlanStore, PlanStoreError, PlanStaleWriteError, PlanUnsupportedAllocationKindError, assertPlannerRevision, setWriteBusyHook, setWriteNotifyHook, migrateToUuids, migrateToGlobalSequence, withFeatureLock, nowISO } from "./plan-store.js";
|
|
22
39
|
export { PlanRenderer } from "./renderer.js";
|
|
23
40
|
export { ExportService } from "./export-service.js";
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { ProjectContextReply } from "./project-context.js";
|
|
2
|
+
import type { PlanWorkspace } from "./schema.js";
|
|
3
|
+
/**
|
|
4
|
+
* Reply shaping for an explicit planner load, shared by every adapter
|
|
5
|
+
* (MCP planner-load, Pi's planner-load tool and /planner load command).
|
|
6
|
+
*
|
|
7
|
+
* Every piece is sent once, in one text the agent reads top to bottom: the
|
|
8
|
+
* recap to show the user, then the agent-only context. `structured` carries
|
|
9
|
+
* only status fields; hosts that show the agent only structured content
|
|
10
|
+
* still get the text because the MCP server folds it in.
|
|
11
|
+
*/
|
|
12
|
+
/** How the planner operating guide reaches the agent on this load. */
|
|
13
|
+
export type PlannerGuideDelivery =
|
|
14
|
+
/** The guide body is included (a host with no other way to deliver it). */
|
|
15
|
+
{
|
|
16
|
+
mode: "include";
|
|
17
|
+
content: string;
|
|
18
|
+
customized: boolean;
|
|
19
|
+
message: string;
|
|
20
|
+
}
|
|
21
|
+
/** The host already delivers the guide (e.g. Claude Code's /planner
|
|
22
|
+
* command); send only where it lives, never the body again. */
|
|
23
|
+
| {
|
|
24
|
+
mode: "pointer";
|
|
25
|
+
path: string;
|
|
26
|
+
customized: boolean;
|
|
27
|
+
message: string;
|
|
28
|
+
};
|
|
29
|
+
export interface PlannerLoadReplyInput {
|
|
30
|
+
recap: string;
|
|
31
|
+
projectContext: ProjectContextReply;
|
|
32
|
+
/** Accepted Decisions below project scope (feature/phase/task). Project
|
|
33
|
+
* decisions are already inside the project context. */
|
|
34
|
+
scopedDecisions?: {
|
|
35
|
+
content: string;
|
|
36
|
+
total: number;
|
|
37
|
+
truncated: boolean;
|
|
38
|
+
};
|
|
39
|
+
plannerGuide: PlannerGuideDelivery;
|
|
40
|
+
}
|
|
41
|
+
export interface PlannerLoadReply {
|
|
42
|
+
text: string;
|
|
43
|
+
structured: Record<string, unknown>;
|
|
44
|
+
}
|
|
45
|
+
export declare const PLANNER_LOAD_RECAP_HEADING = "## Recap \u2014 show this to the user verbatim";
|
|
46
|
+
export declare const PLANNER_LOAD_AGENT_HEADING = "## Agent-only context \u2014 read and keep it; never quote it in the recap";
|
|
47
|
+
/** Split a load reply's text back into the recap the user sees and the
|
|
48
|
+
* agent-only context. Any client (or test) that must show only the recap
|
|
49
|
+
* uses this instead of guessing at the layout. */
|
|
50
|
+
export declare function splitPlannerLoadText(text: string): {
|
|
51
|
+
recap: string;
|
|
52
|
+
agentContext: string;
|
|
53
|
+
};
|
|
54
|
+
export declare function buildPlannerLoadReply(input: PlannerLoadReplyInput): PlannerLoadReply;
|
|
55
|
+
/** Accepted Decisions owned by features, phases and tasks, bounded for a
|
|
56
|
+
* load reply. Project decisions are left out: the project context already
|
|
57
|
+
* carries them. */
|
|
58
|
+
export declare function buildScopedDecisionContext(plan: PlanWorkspace): {
|
|
59
|
+
content: string;
|
|
60
|
+
total: number;
|
|
61
|
+
truncated: boolean;
|
|
62
|
+
};
|
|
63
|
+
//# sourceMappingURL=load-reply.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"load-reply.d.ts","sourceRoot":"","sources":["../src/load-reply.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAChE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAIjD;;;;;;;;GAQG;AAEH,sEAAsE;AACtE,MAAM,MAAM,oBAAoB;AAC9B,2EAA2E;AACzE;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE;AAC5E;+DAC+D;GAC7D;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAE5E,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,mBAAmB,CAAC;IACpC;2DACuD;IACvD,eAAe,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,OAAO,CAAA;KAAE,CAAC;IACzE,YAAY,EAAE,oBAAoB,CAAC;CACpC;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACrC;AAED,eAAO,MAAM,0BAA0B,mDAA8C,CAAC;AACtF,eAAO,MAAM,0BAA0B,+EAA0E,CAAC;AAElH;;kDAEkD;AAClD,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,CAK1F;AAED,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,qBAAqB,GAAG,gBAAgB,CAmCpF;AAED;;mBAEmB;AACnB,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,aAAa,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,CAYtH"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { featureNumberOfPhase, formatFeatureRef, formatPhaseRef } from "./naming.js";
|
|
2
|
+
import { buildBoundedAcceptedDecisionContext } from "./task-context.js";
|
|
3
|
+
export const PLANNER_LOAD_RECAP_HEADING = "## Recap — show this to the user verbatim";
|
|
4
|
+
export const PLANNER_LOAD_AGENT_HEADING = "## Agent-only context — read and keep it; never quote it in the recap";
|
|
5
|
+
/** Split a load reply's text back into the recap the user sees and the
|
|
6
|
+
* agent-only context. Any client (or test) that must show only the recap
|
|
7
|
+
* uses this instead of guessing at the layout. */
|
|
8
|
+
export function splitPlannerLoadText(text) {
|
|
9
|
+
const agentAt = text.indexOf(PLANNER_LOAD_AGENT_HEADING);
|
|
10
|
+
const recapStart = text.startsWith(PLANNER_LOAD_RECAP_HEADING) ? PLANNER_LOAD_RECAP_HEADING.length : 0;
|
|
11
|
+
if (agentAt < 0)
|
|
12
|
+
return { recap: text.slice(recapStart).trim(), agentContext: "" };
|
|
13
|
+
return { recap: text.slice(recapStart, agentAt).trim(), agentContext: text.slice(agentAt + PLANNER_LOAD_AGENT_HEADING.length).trim() };
|
|
14
|
+
}
|
|
15
|
+
export function buildPlannerLoadReply(input) {
|
|
16
|
+
const guide = input.plannerGuide;
|
|
17
|
+
const guideBlock = guide.mode === "include"
|
|
18
|
+
? ["### Agent Plan operating guide", guide.customized ? guide.message : "", guide.content].filter(Boolean).join("\n\n")
|
|
19
|
+
: [
|
|
20
|
+
"### Agent Plan operating guide",
|
|
21
|
+
`Follow the Agent Plan operating guide. If it is not already in this conversation (the /planner command includes it), read ${guide.path}.`,
|
|
22
|
+
guide.customized ? guide.message : "",
|
|
23
|
+
].filter(Boolean).join("\n");
|
|
24
|
+
const blocks = [
|
|
25
|
+
PLANNER_LOAD_RECAP_HEADING,
|
|
26
|
+
input.recap,
|
|
27
|
+
PLANNER_LOAD_AGENT_HEADING,
|
|
28
|
+
"### Project context",
|
|
29
|
+
input.projectContext.text,
|
|
30
|
+
];
|
|
31
|
+
if (input.scopedDecisions && input.scopedDecisions.total > 0) {
|
|
32
|
+
blocks.push("### Accepted decisions for features, phases and tasks", input.scopedDecisions.content);
|
|
33
|
+
}
|
|
34
|
+
blocks.push(guideBlock);
|
|
35
|
+
return {
|
|
36
|
+
text: blocks.join("\n\n"),
|
|
37
|
+
structured: {
|
|
38
|
+
loaded: true,
|
|
39
|
+
projectContext: input.projectContext.structured,
|
|
40
|
+
plannerGuide: guide.mode === "include"
|
|
41
|
+
? { mode: "include", customized: guide.customized }
|
|
42
|
+
: { mode: "pointer", path: guide.path, customized: guide.customized },
|
|
43
|
+
scopedDecisions: input.scopedDecisions
|
|
44
|
+
? { total: input.scopedDecisions.total, truncated: input.scopedDecisions.truncated }
|
|
45
|
+
: { total: 0, truncated: false },
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
/** Accepted Decisions owned by features, phases and tasks, bounded for a
|
|
50
|
+
* load reply. Project decisions are left out: the project context already
|
|
51
|
+
* carries them. */
|
|
52
|
+
export function buildScopedDecisionContext(plan) {
|
|
53
|
+
const features = plan.features.features;
|
|
54
|
+
const phaseRef = (phase) => formatPhaseRef(phase.number, featureNumberOfPhase(phase, features));
|
|
55
|
+
const { content, total, truncated } = buildBoundedAcceptedDecisionContext([
|
|
56
|
+
...features.map((feature) => ({ scope: `feature ${formatFeatureRef(feature.number)}`, decisions: feature.acceptedDecisions })),
|
|
57
|
+
...plan.phases.map((phase) => ({ scope: `phase ${phaseRef(phase)}`, decisions: phase.acceptedDecisions })),
|
|
58
|
+
...plan.phases.flatMap((phase) => phase.tasks.map((task) => ({
|
|
59
|
+
scope: `task ${phaseRef(phase)}/T${String(task.number).padStart(3, "0")}`,
|
|
60
|
+
decisions: task.acceptedDecisions,
|
|
61
|
+
}))),
|
|
62
|
+
]);
|
|
63
|
+
return { content, total, truncated };
|
|
64
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import type { PlannerPayloadEntity, PlannerPayloadOperation } from "./payload-fallback.js";
|
|
2
|
+
/**
|
|
3
|
+
* Per-field echo budget. A changed field is worth showing back so the caller
|
|
4
|
+
* can confirm what landed without a second call; a changed field the size of
|
|
5
|
+
* a task description is not. 2,000 characters is about three screens — long
|
|
6
|
+
* enough to read a title, a status, a checklist or a short note whole, short
|
|
7
|
+
* enough that echoing a full description is impossible.
|
|
8
|
+
*/
|
|
9
|
+
export declare const MUTATION_ECHO_MAX_FIELD_CHARS = 2000;
|
|
10
|
+
/**
|
|
11
|
+
* Ceiling for the whole structured payload of a mutation reply. Chosen to
|
|
12
|
+
* match MAX_PHASE_WORK_MAP_CHARS and MAX_ACCEPTED_DECISION_CONTEXT_CHARS so
|
|
13
|
+
* an agent has one number in mind for planner payloads rather than four.
|
|
14
|
+
* When the assembled payload exceeds it, echoed values are dropped to names
|
|
15
|
+
* (see `boundMutationEcho`) — the caller still learns exactly which fields
|
|
16
|
+
* changed, which is the part it cannot get anywhere else.
|
|
17
|
+
*/
|
|
18
|
+
export declare const MUTATION_REPLY_MAX_CHARS = 8000;
|
|
19
|
+
/**
|
|
20
|
+
* The long-text size a planner tool accepts in one call, stated up front so
|
|
21
|
+
* a caller does not discover it by being rejected. This is not a limit the
|
|
22
|
+
* planner imposes for its own sake: a long `description` can be dropped by
|
|
23
|
+
* the transport before it ever reaches the planner, which arrives here as
|
|
24
|
+
* "no mutable fields received" and is why DESCRIPTION_MARKDOWN_FALLBACK_REQUIRED
|
|
25
|
+
* exists. Measured: ~13,500 characters accepted, ~17,000 rejected. 12,000 is
|
|
26
|
+
* under the observed floor with room to spare.
|
|
27
|
+
*
|
|
28
|
+
* Above this, write the prose to a committed file under `.planner/docs/` and
|
|
29
|
+
* pass `descriptionRef` — see suggestedDescriptionRefPath in payload-fallback.ts.
|
|
30
|
+
*/
|
|
31
|
+
export declare const PLANNER_LONG_TEXT_FIELD_MAX_CHARS = 12000;
|
|
32
|
+
/**
|
|
33
|
+
* One sentence for a tool description, so the limit is discoverable at the
|
|
34
|
+
* point of the call instead of after it fails. Every tool taking a long text
|
|
35
|
+
* field appends this to its own description.
|
|
36
|
+
*/
|
|
37
|
+
export declare function longTextFieldLimitNotice(field?: string): string;
|
|
38
|
+
/** A tool reply as one result: the human-readable body and the structured
|
|
39
|
+
* payload, with no field present in both. */
|
|
40
|
+
export interface MutationReply {
|
|
41
|
+
text: string;
|
|
42
|
+
structured: Record<string, unknown>;
|
|
43
|
+
}
|
|
44
|
+
/** Who was mutated, in the terms a caller refers to it by. The canonical
|
|
45
|
+
* `ref` is what travels — never the raw text the caller happened to type,
|
|
46
|
+
* which may be a short id, a title or a UUID. */
|
|
47
|
+
export interface MutationIdentity {
|
|
48
|
+
entity: PlannerPayloadEntity;
|
|
49
|
+
ref: string;
|
|
50
|
+
id?: string;
|
|
51
|
+
shortId?: string;
|
|
52
|
+
title?: string;
|
|
53
|
+
status?: string;
|
|
54
|
+
}
|
|
55
|
+
export interface MutationReplyInput {
|
|
56
|
+
identity: MutationIdentity;
|
|
57
|
+
operation: PlannerPayloadOperation;
|
|
58
|
+
/** Fields the caller actually changed, in the order they were received. */
|
|
59
|
+
updatedFields: readonly string[];
|
|
60
|
+
/** The persisted values of those fields, to be echoed back within budget.
|
|
61
|
+
* Keys outside `updatedFields` are ignored: a mutation reply never carries
|
|
62
|
+
* a field the mutation did not touch. */
|
|
63
|
+
changedValues?: Record<string, unknown>;
|
|
64
|
+
/** Diagnostics that are not the entity — description freshness, stale
|
|
65
|
+
* parents, lost checklist ticks, resume-required notices. These are the
|
|
66
|
+
* reason to read a mutation result at all, so they are never trimmed. */
|
|
67
|
+
extras?: Record<string, unknown>;
|
|
68
|
+
/** Extra lines for the human channel, such as a lost-tick warning. */
|
|
69
|
+
notices?: readonly string[];
|
|
70
|
+
/** The command that returns the full entity, for a caller that wants what
|
|
71
|
+
* this reply deliberately leaves out. */
|
|
72
|
+
readBackCommand?: string;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Build the one reply every mutation tool returns.
|
|
76
|
+
*
|
|
77
|
+
* What a caller gets: that the write happened, which fields it touched, the
|
|
78
|
+
* canonical ref and identity of what it touched, the changed values within
|
|
79
|
+
* budget, and every diagnostic. What it does not get: the rest of the
|
|
80
|
+
* entity, which it can read with the command named in `readBack`.
|
|
81
|
+
*
|
|
82
|
+
* `updated` (or `discussed`), `updatedFields` and any `errorCode` keep their
|
|
83
|
+
* exact prior shape — mutation-integrity checking across the whole project
|
|
84
|
+
* reads those three and nothing else.
|
|
85
|
+
*/
|
|
86
|
+
export declare function buildMutationReply(input: MutationReplyInput): MutationReply;
|
|
87
|
+
export interface DerivedStatusReadOnlyInput {
|
|
88
|
+
entity: "feature" | "phase";
|
|
89
|
+
ref: string;
|
|
90
|
+
attemptedStatus: string;
|
|
91
|
+
effectiveStatus: string;
|
|
92
|
+
/** Full "retry without the status field" instruction. Each adapter names
|
|
93
|
+
* its own update tool (`planner-feature-update` vs `feature_update`), so
|
|
94
|
+
* this one line stays adapter-supplied rather than templated here —
|
|
95
|
+
* moving it into the core would restate a tool name the core doesn't own. */
|
|
96
|
+
retryAction: string;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* A feature/phase `status` field cannot be set directly — it is derived
|
|
100
|
+
* from child phases/tasks. Both adapters carried an identical copy of this
|
|
101
|
+
* refusal for feature_update/phase_update; only the retry line's tool name
|
|
102
|
+
* differs, which is why it is the one caller-supplied piece.
|
|
103
|
+
*/
|
|
104
|
+
export declare function derivedStatusReadOnlyReply(input: DerivedStatusReadOnlyInput): MutationReply;
|
|
105
|
+
//# sourceMappingURL=mutation-reply.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mutation-reply.d.ts","sourceRoot":"","sources":["../src/mutation-reply.ts"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EAAE,oBAAoB,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AAE3F;;;;;;GAMG;AACH,eAAO,MAAM,6BAA6B,OAAQ,CAAC;AAEnD;;;;;;;GAOG;AACH,eAAO,MAAM,wBAAwB,OAAQ,CAAC;AAE9C;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,iCAAiC,QAAS,CAAC;AAExD;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,SAAgB,GAAG,MAAM,CAEtE;AAED;6CAC6C;AAC7C,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACrC;AAED;;iDAEiD;AACjD,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,oBAAoB,CAAC;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,SAAS,EAAE,uBAAuB,CAAC;IACnC,2EAA2E;IAC3E,aAAa,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC;;6CAEyC;IACzC,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACxC;;6EAEyE;IACzE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,sEAAsE;IACtE,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5B;6CACyC;IACzC,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAoED;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,kBAAkB,GAAG,aAAa,CA6C3E;AAED,MAAM,WAAW,0BAA0B;IACzC,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,eAAe,EAAE,MAAM,CAAC;IACxB,eAAe,EAAE,MAAM,CAAC;IACxB;;;kFAG8E;IAC9E,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;;;GAKG;AACH,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,0BAA0B,GAAG,aAAa,CA4B3F"}
|