pi-goal-x 0.12.0 → 0.13.0
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 +12 -3
- package/extensions/goal-auditor.ts +29 -30
- package/extensions/goal-draft.ts +53 -0
- package/extensions/goal-policy.ts +19 -0
- package/extensions/goal-record.ts +5 -0
- package/extensions/goal.ts +69 -26
- package/extensions/prompts/goal-prompts.ts +38 -3
- package/extensions/storage/goal-files.ts +9 -7
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -10,6 +10,15 @@ The extension is designed around one rule: **the user owns intent; the agent exe
|
|
|
10
10
|
|
|
11
11
|
All core features of [@capyup/pi-goal](https://github.com/capyup/pi-goal) are preserved. The following changes are specific to pi-goal-x:
|
|
12
12
|
|
|
13
|
+
### Verification contract system
|
|
14
|
+
|
|
15
|
+
- **Per-goal verification contracts** — when drafting a goal, include a `Verification contract:` section with plain-text requirements (e.g. "Run npm test (0 failures), grep for remaining STP references"). The contract is extracted, stored on the goal record, and enforced by the `complete_goal` tool — the call is rejected unless the agent provides a non-empty `verificationSummary` matching the contract.
|
|
16
|
+
- **Per-task verification contracts** — `propose_task_list` supports an optional `verificationContract` per task. If set, `complete_task` requires a non-empty `verificationSummary`.
|
|
17
|
+
- **Both prompt and tool enforcement** — prompts include a VERIFICATION CONTRACT section instructing the agent; tool validators reject calls that violate the contract.
|
|
18
|
+
- **Backward compatible** — goals/tasks without a `Verification contract:` section work exactly as before. No contract = no enforcement.
|
|
19
|
+
- **Auditor integration** — the independent completion auditor receives both the `verificationContract` and `verificationSummary` and cross-checks claims against real artifacts.
|
|
20
|
+
- **`complete_goal` `testResults` removed** — replaced with `verificationSummary`. The old structured test results interface is gone.
|
|
21
|
+
|
|
13
22
|
### Task list system
|
|
14
23
|
|
|
15
24
|
- **Structured task breakdown** — the agent can propose a task list via `propose_task_list`, which shows the user a Confirm / Continue Chatting dialog (mirrors the `propose_goal_draft` pattern). Once confirmed, tasks are displayed in prompts, the widget, serialized to disk, and included in auditor review.
|
|
@@ -30,7 +39,7 @@ All core features of [@capyup/pi-goal](https://github.com/capyup/pi-goal) are pr
|
|
|
30
39
|
### E2e test infrastructure
|
|
31
40
|
|
|
32
41
|
- **Deterministic fork tests using `--mode json`**: the e2e suite spawns a real `pi --fork --mode json` session, parses structured `tool_execution_start`/`tool_execution_end` JSON events for field-level assertions — no free-text AI output parsing. Uses `--append-system-prompt` + `--tools` to force deterministic tool calls.
|
|
33
|
-
- **Full coverage**:
|
|
42
|
+
- **Full coverage**: 205 tests total — function-level integration tests (12), mock-pi handler tests (4), file-validity checks (6), real `pi --fork --mode json` tests (3 scenarios), propose_goal_tweak unit/integration/e2e tests (15), task list policy/round-trip/render tests (50+), and verification contract tests (14).
|
|
34
43
|
|
|
35
44
|
### Completion auditor
|
|
36
45
|
|
|
@@ -165,11 +174,11 @@ The extension exposes tools only when they make sense for the current lifecycle
|
|
|
165
174
|
| `get_goal` | always | Read the focused goal state; mentions other open goals when present |
|
|
166
175
|
| `propose_goal_draft` | drafting only (goal creation) | Submit a concrete draft for user confirmation |
|
|
167
176
|
| `propose_goal_tweak` | tweak drafting only | Submit a revision to an existing goal (shows Confirm / Continue Chatting dialog) |
|
|
168
|
-
| `complete_goal` | focused active or paused goal | Mark the focused goal complete —
|
|
177
|
+
| `complete_goal` | focused active or paused goal | Mark the focused goal complete — supply a `verificationSummary` covering all contract items. When the auditor is disabled, supply `confirmBypassAuditor: true` after user confirmation to bypass the audit |
|
|
169
178
|
| `pause_goal` | focused active goal | Pause the focused goal because of a real blocker |
|
|
170
179
|
| `abort_goal` | focused active or paused goal | Abort/archive an obsolete, impossible, unsafe, or user-cancelled focused goal |
|
|
171
180
|
| `propose_task_list` | active or paused goal | Propose a structured task list for user confirmation (stops the turn) |
|
|
172
|
-
| `complete_task` | active or paused goal | Mark a task complete with optional
|
|
181
|
+
| `complete_task` | active or paused goal | Mark a task complete with optional `verificationSummary`. If the task has a `verificationContract`, the summary is required (does not stop turn) |
|
|
173
182
|
| `skip_task` | active or paused goal | Mark a task skipped with a required reason (does not stop turn) |
|
|
174
183
|
| `propose_goal_tweak` | tweak drafting only | Submit a revision to the focused goal (shows Confirm / Continue Chatting dialog) |
|
|
175
184
|
| `step_complete` | hidden / legacy | Compatibility no-op; Sisyphus no longer requires a step counter |
|
|
@@ -127,15 +127,11 @@ export function parseAuditorDecision(output: string): { approved: boolean; disap
|
|
|
127
127
|
return { approved: approved && !disapproved, disapproved };
|
|
128
128
|
}
|
|
129
129
|
|
|
130
|
-
export interface
|
|
131
|
-
/**
|
|
132
|
-
|
|
133
|
-
/**
|
|
134
|
-
|
|
135
|
-
/** Last lines of test output showing results */
|
|
136
|
-
output?: string;
|
|
137
|
-
/** ISO timestamp of when tests were run */
|
|
138
|
-
timestamp?: string;
|
|
130
|
+
export interface AuditorVerificationEvidence {
|
|
131
|
+
/** The agent's verification summary describing what was checked. */
|
|
132
|
+
summary: string;
|
|
133
|
+
/** The goal's verification contract (what the agent was required to verify), if any. */
|
|
134
|
+
contract?: string;
|
|
139
135
|
}
|
|
140
136
|
|
|
141
137
|
function taskSummaryBlock(taskList?: GoalTaskList | null): string {
|
|
@@ -158,7 +154,7 @@ export function buildGoalAuditorPrompt(args: {
|
|
|
158
154
|
goal: GoalRecord;
|
|
159
155
|
completionSummary?: string | null;
|
|
160
156
|
detailedSummary: string;
|
|
161
|
-
|
|
157
|
+
verificationSummary?: string | null;
|
|
162
158
|
}): string {
|
|
163
159
|
return [
|
|
164
160
|
"You are the independent completion auditor for pi-goal.",
|
|
@@ -186,31 +182,34 @@ export function buildGoalAuditorPrompt(args: {
|
|
|
186
182
|
args.detailedSummary,
|
|
187
183
|
...(taskSummaryBlock(args.goal.taskList) ? ["", taskSummaryBlock(args.goal.taskList)] : []),
|
|
188
184
|
"</goal_details>",
|
|
189
|
-
...(args.
|
|
185
|
+
...(args.verificationSummary?.trim() ? [
|
|
190
186
|
"",
|
|
191
|
-
"Executor
|
|
192
|
-
"<
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
"
|
|
187
|
+
"Executor verification summary:",
|
|
188
|
+
"<verification_summary>",
|
|
189
|
+
args.verificationSummary.trim(),
|
|
190
|
+
"</verification_summary>",
|
|
191
|
+
] : []),
|
|
192
|
+
...(args.goal.verificationContract?.trim() ? [
|
|
193
|
+
"",
|
|
194
|
+
"Goal verification contract (what the executor was required to verify):",
|
|
195
|
+
"<verification_contract>",
|
|
196
|
+
args.goal.verificationContract.trim(),
|
|
197
|
+
"</verification_contract>",
|
|
199
198
|
] : []),
|
|
200
199
|
"",
|
|
201
200
|
"Audit checklist:",
|
|
202
|
-
...
|
|
203
|
-
"1. Extract the real success criteria from the objective, including quality/reader outcomes.",
|
|
204
|
-
"2. Inspect artifacts or command output that can prove or disprove those criteria.",
|
|
205
|
-
"3. Before running a test suite with bash, check the <test_evidence> block. If the executor has provided recent passing test results for that suite, accept them as evidence rather than re-running the tests.",
|
|
206
|
-
"4. Explain missing or weak evidence, especially scaffold-vs-final quality gaps.",
|
|
207
|
-
"5. End with exactly <approved/> only if the objective is truly complete; otherwise end with exactly <disapproved/>.",
|
|
208
|
-
] : [
|
|
201
|
+
...[
|
|
209
202
|
"1. Extract the real success criteria from the objective, including quality/reader outcomes.",
|
|
210
203
|
"2. Inspect artifacts or command output that can prove or disprove those criteria.",
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
204
|
+
...(args.verificationSummary?.trim()
|
|
205
|
+
? ["3. Check the <verification_summary> against real artifacts. If the executor claims to have run tests or searched for references, verify those claims with actual file/shell evidence. The summary is a claim, not proof — cross-check it."]
|
|
206
|
+
: []),
|
|
207
|
+
...(args.goal.verificationContract?.trim()
|
|
208
|
+
? ["4. Verify that the executor has satisfied every item in the <verification_contract>. If any item is missing or weakly addressed, disapprove."]
|
|
209
|
+
: []),
|
|
210
|
+
"5. Explain missing or weak evidence, especially scaffold-vs-final quality gaps.",
|
|
211
|
+
"6. End with exactly <approved/> only if the objective is truly complete; otherwise end with exactly <disapproved/>.",
|
|
212
|
+
],
|
|
214
213
|
"",
|
|
215
214
|
"Progress reporting:",
|
|
216
215
|
"You have the report_auditor_progress tool available to report your progress to the user.",
|
|
@@ -288,7 +287,7 @@ export async function runGoalCompletionAuditor(args: {
|
|
|
288
287
|
goal: GoalRecord;
|
|
289
288
|
completionSummary?: string | null;
|
|
290
289
|
detailedSummary: string;
|
|
291
|
-
|
|
290
|
+
verificationSummary?: string | null;
|
|
292
291
|
signal?: AbortSignal;
|
|
293
292
|
onProgress?: AuditorProgressCallback;
|
|
294
293
|
/**
|
package/extensions/goal-draft.ts
CHANGED
|
@@ -26,6 +26,57 @@ export function promptSafeObjective(objective: string): string {
|
|
|
26
26
|
return objective.replace(/<\/?untrusted_objective>/gi, (tag) => tag.replace(/</g, "<").replace(/>/g, ">"));
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
+
const VERIFICATION_CONTRACT_RE = /^Verification contract:\s*(.+)$/im;
|
|
30
|
+
|
|
31
|
+
const CONVENTIONAL_SECTION_NAMES = [
|
|
32
|
+
"success criteria",
|
|
33
|
+
"boundaries",
|
|
34
|
+
"constraints",
|
|
35
|
+
"if blocked",
|
|
36
|
+
"if blocked / unclear / failing",
|
|
37
|
+
"don'ts",
|
|
38
|
+
"sisyphus reminder",
|
|
39
|
+
"objective",
|
|
40
|
+
"目标",
|
|
41
|
+
"ordered steps",
|
|
42
|
+
"order rules",
|
|
43
|
+
"steps",
|
|
44
|
+
];
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Extract a `Verification contract:` section from a goal objective and return
|
|
48
|
+
* the cleaned objective (without the contract section) and the contract text.
|
|
49
|
+
*
|
|
50
|
+
* The contract section is a single line matching:
|
|
51
|
+
* Verification contract: <text>
|
|
52
|
+
*
|
|
53
|
+
* It can appear anywhere in the objective, but by convention it goes after
|
|
54
|
+
* the other sections (like Success criteria, Boundaries, Constraints).
|
|
55
|
+
*
|
|
56
|
+
* If no contract section is found, `verificationContract` is undefined.
|
|
57
|
+
*/
|
|
58
|
+
export function extractVerificationContract(objective: string): { objective: string; verificationContract?: string } {
|
|
59
|
+
const lines = objective.replace(/\r/g, "").split("\n");
|
|
60
|
+
let contract: string | undefined;
|
|
61
|
+
const filtered: string[] = [];
|
|
62
|
+
|
|
63
|
+
for (const line of lines) {
|
|
64
|
+
const trimmed = line.trim();
|
|
65
|
+
const m = VERIFICATION_CONTRACT_RE.exec(trimmed);
|
|
66
|
+
if (m) {
|
|
67
|
+
contract = m[1].trim();
|
|
68
|
+
// Skip this line — don't add it to the cleaned objective
|
|
69
|
+
} else {
|
|
70
|
+
filtered.push(line);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return {
|
|
75
|
+
objective: filtered.join("\n"),
|
|
76
|
+
verificationContract: contract || undefined,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
29
80
|
export function buildDraftConfirmationText(args: {
|
|
30
81
|
focus: GoalDraftingFocus;
|
|
31
82
|
originalTopic: string;
|
|
@@ -143,6 +194,7 @@ export function goalDraftingPrompt(topic: string, focus: GoalDraftingFocus): str
|
|
|
143
194
|
"Success criteria: <observable evidence the goal is done>",
|
|
144
195
|
"Boundaries: <in scope / out of scope>",
|
|
145
196
|
"Constraints: <hard rules>",
|
|
197
|
+
"Verification contract: <optional — what verification evidence is required before marking complete, e.g. 'Run npm test (0 failures), grep for remaining references, re-read requirements and confirm every item is addressed'>",
|
|
146
198
|
"If blocked: <default = stop and ask the user>",
|
|
147
199
|
"Call propose_goal_draft with sisyphus=false and autoContinue=true unless the user asked otherwise.",
|
|
148
200
|
];
|
|
@@ -155,6 +207,7 @@ export function goalDraftingPrompt(topic: string, focus: GoalDraftingFocus): str
|
|
|
155
207
|
"Success criteria: <observable evidence the whole ordered goal is done>",
|
|
156
208
|
"Boundaries: <in scope / out of scope>",
|
|
157
209
|
"Constraints: <hard rules, files not to touch, etc.>",
|
|
210
|
+
"Verification contract: <optional — what verification evidence is required before marking complete>",
|
|
158
211
|
"Ordered steps: <preserve the user's requested steps and ordering; do not add preflight or reconnaissance steps they did not ask for>",
|
|
159
212
|
"If blocked / unclear / failing: <default = stop and ask the user>",
|
|
160
213
|
"Sisyphus reminder: Work patiently and sequentially. No rushing, no unrequested preflight steps, no improvising around blockers.",
|
|
@@ -143,6 +143,25 @@ export function taskCompletionBlockWarning(taskList: GoalTaskList): string | nul
|
|
|
143
143
|
return `${pending.length} task${pending.length > 1 ? "s" : ""} still pending with blockCompletion enabled. Complete or skip all pending tasks before finishing the goal.`;
|
|
144
144
|
}
|
|
145
145
|
|
|
146
|
+
/**
|
|
147
|
+
* Validate that a verificationSummary satisfies a verificationContract.
|
|
148
|
+
* If a contract exists, the summary must be non-empty.
|
|
149
|
+
*/
|
|
150
|
+
export function validateVerificationSummary(args: {
|
|
151
|
+
verificationContract?: string | null;
|
|
152
|
+
verificationSummary?: string | null;
|
|
153
|
+
}): PolicyValidation {
|
|
154
|
+
const contract = args.verificationContract?.trim();
|
|
155
|
+
const summary = args.verificationSummary?.trim();
|
|
156
|
+
if (contract && !summary) {
|
|
157
|
+
return {
|
|
158
|
+
ok: false,
|
|
159
|
+
message: `This goal has a verification contract but no verificationSummary was provided. Provide a verificationSummary that addresses the contract requirements.`,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
return { ok: true };
|
|
163
|
+
}
|
|
164
|
+
|
|
146
165
|
export function validateTaskCompletion(args: {
|
|
147
166
|
goal: GoalPolicyRecordLike | null;
|
|
148
167
|
taskId: string;
|
|
@@ -14,6 +14,7 @@ export interface GoalTask {
|
|
|
14
14
|
skippedAt?: string;
|
|
15
15
|
evidence?: string;
|
|
16
16
|
skipReason?: string;
|
|
17
|
+
verificationContract?: string;
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
export interface GoalTaskList {
|
|
@@ -43,6 +44,8 @@ export interface GoalRecord {
|
|
|
43
44
|
pauseReason?: string;
|
|
44
45
|
pauseSuggestedAction?: string;
|
|
45
46
|
taskList?: GoalTaskList;
|
|
47
|
+
/** Plain-text description of what verification evidence is required before completing this goal. */
|
|
48
|
+
verificationContract?: string;
|
|
46
49
|
}
|
|
47
50
|
|
|
48
51
|
export interface GoalStateEntry {
|
|
@@ -182,6 +185,7 @@ export function normalizeTaskList(value: unknown): GoalTaskList | undefined {
|
|
|
182
185
|
skippedAt: typeof t.skippedAt === "string" ? t.skippedAt : undefined,
|
|
183
186
|
evidence: typeof t.evidence === "string" ? t.evidence : undefined,
|
|
184
187
|
skipReason: typeof t.skipReason === "string" ? t.skipReason : undefined,
|
|
188
|
+
verificationContract: typeof t.verificationContract === "string" ? t.verificationContract : undefined,
|
|
185
189
|
});
|
|
186
190
|
}
|
|
187
191
|
if (tasks.length === 0) return undefined;
|
|
@@ -224,5 +228,6 @@ export function normalizeGoalRecord(value: unknown): GoalRecord | null {
|
|
|
224
228
|
pauseReason: typeof raw.pauseReason === "string" && raw.pauseReason.trim() ? raw.pauseReason : undefined,
|
|
225
229
|
pauseSuggestedAction: typeof raw.pauseSuggestedAction === "string" && raw.pauseSuggestedAction.trim() ? raw.pauseSuggestedAction : undefined,
|
|
226
230
|
taskList: normalizeTaskList(raw.taskList),
|
|
231
|
+
verificationContract: typeof raw.verificationContract === "string" ? raw.verificationContract : undefined,
|
|
227
232
|
};
|
|
228
233
|
}
|
package/extensions/goal.ts
CHANGED
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
import {
|
|
12
12
|
buildDraftConfirmationText,
|
|
13
13
|
buildTweakConfirmationText,
|
|
14
|
+
extractVerificationContract,
|
|
14
15
|
goalDraftingPrompt,
|
|
15
16
|
validateGoalDraftProposal,
|
|
16
17
|
type GoalDraftingFocus,
|
|
@@ -118,6 +119,7 @@ import {
|
|
|
118
119
|
validateTaskCompletion,
|
|
119
120
|
validateTaskListProposal,
|
|
120
121
|
validateTaskSkip,
|
|
122
|
+
validateVerificationSummary,
|
|
121
123
|
} from "./goal-policy.ts";
|
|
122
124
|
|
|
123
125
|
const STATE_ENTRY = "pi-goal-state";
|
|
@@ -1010,8 +1012,10 @@ export default function goalExtension(pi: ExtensionAPI): void {
|
|
|
1010
1012
|
continuationTimer.unref?.();
|
|
1011
1013
|
}
|
|
1012
1014
|
|
|
1013
|
-
function replaceGoal(config: GoalCreationConfig, ctx: ExtensionContext, startNow = true): void {
|
|
1014
|
-
|
|
1015
|
+
function replaceGoal(config: GoalCreationConfig, ctx: ExtensionContext, startNow = true, verificationContract?: string): void {
|
|
1016
|
+
const goal = createGoal(config);
|
|
1017
|
+
if (verificationContract) goal.verificationContract = verificationContract;
|
|
1018
|
+
setGoal(goal, ctx, true, "created");
|
|
1015
1019
|
beginAccounting();
|
|
1016
1020
|
// Reset continuation nudge state — this is a fresh goal.
|
|
1017
1021
|
resetGetGoalNudgeState(state.goal?.id);
|
|
@@ -1190,17 +1194,18 @@ export default function goalExtension(pi: ExtensionAPI): void {
|
|
|
1190
1194
|
}
|
|
1191
1195
|
|
|
1192
1196
|
function handleDirectGoalSet(rawObjective: string, ctx: ExtensionContext, focus: DraftingFocus): void {
|
|
1193
|
-
const
|
|
1194
|
-
if (!
|
|
1197
|
+
const raw = rawObjective.trim();
|
|
1198
|
+
if (!raw) {
|
|
1195
1199
|
const command = focus === "sisyphus" ? "/sisyphus-set" : "/goals-set";
|
|
1196
1200
|
ctx.ui.notify(`No objective provided. Use ${command} <objective>.`, "warning");
|
|
1197
1201
|
return;
|
|
1198
1202
|
}
|
|
1203
|
+
const { objective, verificationContract } = extractVerificationContract(raw);
|
|
1199
1204
|
clearContinuationState();
|
|
1200
1205
|
clearActiveAccounting();
|
|
1201
1206
|
confirmationIntent = null;
|
|
1202
1207
|
syncGoalTools();
|
|
1203
|
-
replaceGoal({ objective, autoContinue: true, sisyphus: focus === "sisyphus" }, ctx, true);
|
|
1208
|
+
replaceGoal({ objective, autoContinue: true, sisyphus: focus === "sisyphus" }, ctx, true, verificationContract);
|
|
1204
1209
|
}
|
|
1205
1210
|
|
|
1206
1211
|
async function showGoalStatus(ctx: ExtensionContext): Promise<void> {
|
|
@@ -1611,6 +1616,7 @@ export default function goalExtension(pi: ExtensionAPI): void {
|
|
|
1611
1616
|
"The sisyphus field must match the user's confirmation focus: /sisyphus -> sisyphus=true, /goals -> sisyphus=false. The schema enforces this; mismatched proposals are REJECTED.",
|
|
1612
1617
|
"For sisyphus goals, preserve the user's requested ordered style and completion standard. Do not add reconnaissance/preflight steps, merge steps, reorder steps, or change the mode without explicit user confirmation.",
|
|
1613
1618
|
"create_goal is rejected; propose_goal_draft is the confirmation path. This is intentional — the user wants explicit say in goal creation.",
|
|
1619
|
+
"You may include a Verification contract: section in the objective to specify what verification evidence is required before the goal can be completed. This is optional — if omitted, no per-goal contract enforcement applies.",
|
|
1614
1620
|
],
|
|
1615
1621
|
parameters: Type.Object({
|
|
1616
1622
|
objective: Type.String({ description: "Full goal text. For Sisyphus goals this MUST include the user's numbered steps + per-step done criteria, taken faithfully from the user's input." }),
|
|
@@ -1672,13 +1678,15 @@ export default function goalExtension(pi: ExtensionAPI): void {
|
|
|
1672
1678
|
}
|
|
1673
1679
|
|
|
1674
1680
|
if (decision === "confirm") {
|
|
1681
|
+
// Extract verification contract from objective before creation
|
|
1682
|
+
const { objective: cleanedObjective, verificationContract } = extractVerificationContract(objective);
|
|
1675
1683
|
const config: GoalCreationConfig = {
|
|
1676
|
-
objective,
|
|
1684
|
+
objective: cleanedObjective,
|
|
1677
1685
|
autoContinue: autoContinueFlag,
|
|
1678
1686
|
sisyphus: sisyphusFlag,
|
|
1679
1687
|
};
|
|
1680
1688
|
confirmationIntent = null;
|
|
1681
|
-
replaceGoal(config, ctx, false);
|
|
1689
|
+
replaceGoal(config, ctx, false, verificationContract);
|
|
1682
1690
|
syncGoalTools();
|
|
1683
1691
|
return {
|
|
1684
1692
|
content: [{ type: "text", text: buildGoalCreatedReport({ objective, detailedSummary: detailedSummary(state.goal) }) }],
|
|
@@ -1782,10 +1790,13 @@ export default function goalExtension(pi: ExtensionAPI): void {
|
|
|
1782
1790
|
}
|
|
1783
1791
|
|
|
1784
1792
|
if (decision === "confirm") {
|
|
1793
|
+
// Extract verification contract from revised objective
|
|
1794
|
+
const { objective: cleanedObjective, verificationContract } = extractVerificationContract(newObjective);
|
|
1785
1795
|
// Apply the tweak: write the new objective to disk authoritatively.
|
|
1786
1796
|
const next: GoalRecord = {
|
|
1787
1797
|
...state.goal,
|
|
1788
|
-
objective:
|
|
1798
|
+
objective: cleanedObjective,
|
|
1799
|
+
verificationContract: verificationContract,
|
|
1789
1800
|
updatedAt: nowIso(),
|
|
1790
1801
|
// Clear any prior agent pause reason — the user has redefined the work.
|
|
1791
1802
|
pauseReason: undefined,
|
|
@@ -1857,25 +1868,19 @@ export default function goalExtension(pi: ExtensionAPI): void {
|
|
|
1857
1868
|
promptSnippet: "Mark the active or paused pi goal complete — only when every requirement is satisfied.",
|
|
1858
1869
|
promptGuidelines: [
|
|
1859
1870
|
"Call complete_goal with status=complete only when the pi goal objective has actually been achieved and no required work remains.",
|
|
1860
|
-
"Before calling complete_goal,
|
|
1871
|
+
"Before calling complete_goal, you MUST provide a verificationSummary that addresses every success criterion and any verification contract on the goal. Fold all verification evidence (test output, grep results, requirements coverage) into this single field.",
|
|
1861
1872
|
"The auditor is authoritative: completion is archived only if the auditor report ends with <approved/>. If it ends with <disapproved/> or no approval marker, complete_goal is rejected and the goal remains open.",
|
|
1862
1873
|
"Do NOT call complete_goal if any work remains, even if substantial progress was made. Do not use it merely because work is stopping, tests passed, or you are blocked.",
|
|
1863
1874
|
"Do not use complete_goal=complete as an escape hatch when you are blocked. If you are blocked, call pause_goal({reason, suggestedAction?}) instead so the user can intervene.",
|
|
1864
1875
|
"For sisyphus goals, do not mark complete until every numbered step has been executed and individually verified against its done criterion.",
|
|
1865
1876
|
"The goal objective is immutable. The agent MUST NOT modify the goal objective on its own initiative. If the user gives requirements, feedback, or corrections that differ from the goal objective, ask the user to run /goal-tweak to revise the goal. Use goal_question to confirm when the change is ambiguous.",
|
|
1866
|
-
"If
|
|
1877
|
+
"If the goal has a verificationContract, your verificationSummary must address every item in the contract. The auditor will cross-check your claims against real artifacts.",
|
|
1867
1878
|
],
|
|
1868
1879
|
parameters: Type.Object({
|
|
1869
1880
|
status: Type.Optional(StringEnum([COMPLETE_STATUS] as const, { description: "Set to complete only when the objective is achieved." })),
|
|
1870
1881
|
completionSummary: Type.Optional(Type.String({ description: "Concise completion claim and evidence summary passed to the independent auditor agent." })),
|
|
1882
|
+
verificationSummary: Type.String({ description: "Required verification evidence showing what was checked before declaring completion. Must address every success criterion and any verification contract on the goal. Examples: 'Ran npm test (0 failures), re-read requirements and confirmed A1-A3 complete, grepped for remaining STP references (none found).' The exact requirements depend on the specific goal." }),
|
|
1871
1883
|
confirmBypassAuditor: Type.Optional(Type.Boolean({ description: "Set to true to confirm bypassing the independent auditor when it is disabled in settings." })),
|
|
1872
|
-
|
|
1873
|
-
testResults: Type.Optional(Type.Object({
|
|
1874
|
-
exitCode: Type.Number({ description: "Exit code of the test run (0 = success)" }),
|
|
1875
|
-
suiteName: Type.Optional(Type.String({ description: "Test suite name, e.g. 'npm test'" })),
|
|
1876
|
-
output: Type.Optional(Type.String({ description: "Last lines of test output showing results" })),
|
|
1877
|
-
timestamp: Type.Optional(Type.String({ description: "ISO timestamp of when tests were run" })),
|
|
1878
|
-
}, { description: "Structured test evidence passed to the auditor so it can skip redundant test re-runs. If you have just run the test suite successfully, include this so the auditor accepts the results without re-running." })),
|
|
1879
1884
|
}, { additionalProperties: false }),
|
|
1880
1885
|
executionMode: "sequential",
|
|
1881
1886
|
async execute(_toolCallId, params, signal, _onUpdate, ctx) {
|
|
@@ -1906,6 +1911,18 @@ export default function goalExtension(pi: ExtensionAPI): void {
|
|
|
1906
1911
|
};
|
|
1907
1912
|
}
|
|
1908
1913
|
|
|
1914
|
+
// Verification contract gate: if the goal has a contract, verificationSummary must be non-empty
|
|
1915
|
+
const contractGate = validateVerificationSummary({
|
|
1916
|
+
verificationContract: state.goal.verificationContract,
|
|
1917
|
+
verificationSummary: params.verificationSummary,
|
|
1918
|
+
});
|
|
1919
|
+
if (!contractGate.ok) {
|
|
1920
|
+
return {
|
|
1921
|
+
content: [{ type: "text", text: contractGate.message }],
|
|
1922
|
+
details: goalDetails(state.goal),
|
|
1923
|
+
};
|
|
1924
|
+
}
|
|
1925
|
+
|
|
1909
1926
|
const auditTarget = mergeGoalPromptFromDisk(ctx, state.goal);
|
|
1910
1927
|
// Append ledger: completion requested
|
|
1911
1928
|
try {
|
|
@@ -2043,7 +2060,7 @@ export default function goalExtension(pi: ExtensionAPI): void {
|
|
|
2043
2060
|
goal: auditTarget,
|
|
2044
2061
|
completionSummary: params.completionSummary,
|
|
2045
2062
|
detailedSummary: detailedSummary(auditTarget),
|
|
2046
|
-
|
|
2063
|
+
verificationSummary: params.verificationSummary,
|
|
2047
2064
|
signal: auditAbortController.signal,
|
|
2048
2065
|
onProgress: (progress) => {
|
|
2049
2066
|
auditProgress = {
|
|
@@ -2373,11 +2390,13 @@ export default function goalExtension(pi: ExtensionAPI): void {
|
|
|
2373
2390
|
"Do not add a task list for simple, single-step goals.",
|
|
2374
2391
|
"Existing tasks with matching IDs preserve their status/evidence/timestamps; new IDs start as pending; removed IDs are gone.",
|
|
2375
2392
|
"After confirmation the turn stops; the next continuation will arrive automatically.",
|
|
2393
|
+
"You may optionally specify a verificationContract per task to define what verification evidence is required before completing that task.",
|
|
2376
2394
|
],
|
|
2377
2395
|
parameters: Type.Object({
|
|
2378
2396
|
tasks: Type.Array(Type.Object({
|
|
2379
2397
|
id: Type.String({ description: "Short stable slug e.g. 'task-1'" }),
|
|
2380
2398
|
title: Type.String({ description: "Human-readable task title" }),
|
|
2399
|
+
verificationContract: Type.Optional(Type.String({ description: "Optional verification contract for this task — what evidence is required before marking it complete." })),
|
|
2381
2400
|
}), { description: "Array of task objects with id and title" }),
|
|
2382
2401
|
blockCompletion: Type.Optional(Type.Boolean({ description: "If true, warns when pending tasks remain during complete_goal. Default false." })),
|
|
2383
2402
|
changeSummary: Type.Optional(Type.String({ description: "Optional summary of the task list proposal" })),
|
|
@@ -2407,9 +2426,18 @@ export default function goalExtension(pi: ExtensionAPI): void {
|
|
|
2407
2426
|
const mergedTasks = params.tasks.map((p) => {
|
|
2408
2427
|
const existing = existingById.get(p.id);
|
|
2409
2428
|
if (existing) {
|
|
2410
|
-
return {
|
|
2429
|
+
return {
|
|
2430
|
+
...existing,
|
|
2431
|
+
title: p.title,
|
|
2432
|
+
verificationContract: p.verificationContract ?? existing.verificationContract,
|
|
2433
|
+
};
|
|
2411
2434
|
}
|
|
2412
|
-
return {
|
|
2435
|
+
return {
|
|
2436
|
+
id: p.id,
|
|
2437
|
+
title: p.title,
|
|
2438
|
+
status: "pending" as const,
|
|
2439
|
+
verificationContract: p.verificationContract || undefined,
|
|
2440
|
+
};
|
|
2413
2441
|
});
|
|
2414
2442
|
|
|
2415
2443
|
const taskList: GoalTaskList = {
|
|
@@ -2486,10 +2514,12 @@ export default function goalExtension(pi: ExtensionAPI): void {
|
|
|
2486
2514
|
promptGuidelines: [
|
|
2487
2515
|
"Use complete_task to mark a task as complete with optional evidence text (max 200 characters).",
|
|
2488
2516
|
"The turn does NOT stop after complete_task — you may continue with other work.",
|
|
2517
|
+
"If the task has a verificationContract, you MUST provide a verificationSummary that addresses it.",
|
|
2489
2518
|
],
|
|
2490
2519
|
parameters: Type.Object({
|
|
2491
2520
|
taskId: Type.String({ description: "Task id to mark as complete" }),
|
|
2492
2521
|
evidence: Type.Optional(Type.String({ description: "Optional evidence note (max 200 characters)" })),
|
|
2522
|
+
verificationSummary: Type.Optional(Type.String({ description: "Verification evidence for this task. Required if the task has a verificationContract." })),
|
|
2493
2523
|
}),
|
|
2494
2524
|
executionMode: "sequential",
|
|
2495
2525
|
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
@@ -2502,6 +2532,19 @@ export default function goalExtension(pi: ExtensionAPI): void {
|
|
|
2502
2532
|
};
|
|
2503
2533
|
}
|
|
2504
2534
|
if (!state.goal?.taskList) throw new Error("Task list disappeared during task completion.");
|
|
2535
|
+
|
|
2536
|
+
// Check verification contract for the task
|
|
2537
|
+
const taskToComplete = state.goal.taskList.tasks.find((t) => t.id === params.taskId);
|
|
2538
|
+
const contractGate = validateVerificationSummary({
|
|
2539
|
+
verificationContract: taskToComplete?.verificationContract,
|
|
2540
|
+
verificationSummary: params.verificationSummary,
|
|
2541
|
+
});
|
|
2542
|
+
if (!contractGate.ok) {
|
|
2543
|
+
return {
|
|
2544
|
+
content: [{ type: "text", text: contractGate.message }],
|
|
2545
|
+
details: goalDetails(state.goal),
|
|
2546
|
+
};
|
|
2547
|
+
}
|
|
2505
2548
|
const now = nowIso();
|
|
2506
2549
|
const evidence = params.evidence?.trim().slice(0, 200) || undefined;
|
|
2507
2550
|
const updatedTasks = state.goal.taskList.tasks.map((t) => {
|
|
@@ -2509,10 +2552,10 @@ export default function goalExtension(pi: ExtensionAPI): void {
|
|
|
2509
2552
|
return { ...t, status: "complete" as const, completedAt: now, evidence };
|
|
2510
2553
|
});
|
|
2511
2554
|
state.goal = mergeGoalPromptFromDisk(ctx, state.goal);
|
|
2512
|
-
if (!state.goal) throw new Error("Goal disappeared during task completion.");
|
|
2555
|
+
if (!state.goal || !state.goal.taskList) throw new Error("Goal disappeared during task completion.");
|
|
2513
2556
|
state.goal = {
|
|
2514
2557
|
...state.goal,
|
|
2515
|
-
taskList: { ...state.goal.taskList, tasks: updatedTasks },
|
|
2558
|
+
taskList: { ...state.goal.taskList, blockCompletion: state.goal.taskList.blockCompletion, tasks: updatedTasks },
|
|
2516
2559
|
updatedAt: now,
|
|
2517
2560
|
};
|
|
2518
2561
|
setGoal(state.goal, ctx);
|
|
@@ -2532,7 +2575,7 @@ export default function goalExtension(pi: ExtensionAPI): void {
|
|
|
2532
2575
|
// Ledger failure should not block task completion
|
|
2533
2576
|
}
|
|
2534
2577
|
|
|
2535
|
-
const taskSummary = buildTaskSummary(state.goal.taskList);
|
|
2578
|
+
const taskSummary = buildTaskSummary(state.goal.taskList!);
|
|
2536
2579
|
return {
|
|
2537
2580
|
content: [{ type: "text", text: `${params.taskId} complete. ${taskSummary}.` }],
|
|
2538
2581
|
details: goalDetails(state.goal),
|
|
@@ -2577,10 +2620,10 @@ export default function goalExtension(pi: ExtensionAPI): void {
|
|
|
2577
2620
|
return { ...t, status: "skipped" as const, skippedAt: now, skipReason: params.reason.trim() };
|
|
2578
2621
|
});
|
|
2579
2622
|
state.goal = mergeGoalPromptFromDisk(ctx, state.goal);
|
|
2580
|
-
if (!state.goal) throw new Error("Goal disappeared during task skip.");
|
|
2623
|
+
if (!state.goal || !state.goal.taskList) throw new Error("Goal disappeared during task skip.");
|
|
2581
2624
|
state.goal = {
|
|
2582
2625
|
...state.goal,
|
|
2583
|
-
taskList: { ...state.goal.taskList, tasks: updatedTasks },
|
|
2626
|
+
taskList: { ...state.goal.taskList, blockCompletion: state.goal.taskList.blockCompletion, tasks: updatedTasks },
|
|
2584
2627
|
updatedAt: now,
|
|
2585
2628
|
};
|
|
2586
2629
|
setGoal(state.goal, ctx);
|
|
@@ -2600,7 +2643,7 @@ export default function goalExtension(pi: ExtensionAPI): void {
|
|
|
2600
2643
|
// Ledger failure should not block task skip
|
|
2601
2644
|
}
|
|
2602
2645
|
|
|
2603
|
-
const taskSummary = buildTaskSummary(state.goal.taskList);
|
|
2646
|
+
const taskSummary = buildTaskSummary(state.goal.taskList!);
|
|
2604
2647
|
return {
|
|
2605
2648
|
content: [{ type: "text", text: `${params.taskId} skipped. ${taskSummary}.` }],
|
|
2606
2649
|
details: goalDetails(state.goal),
|
|
@@ -24,6 +24,9 @@ export function taskListBlock(goal: GoalRecord): string {
|
|
|
24
24
|
if (task.status === "complete" && task.evidence) suffix = ` — ${task.evidence}`;
|
|
25
25
|
if (task.status === "skipped" && task.skipReason) suffix = ` — skipped: ${task.skipReason}`;
|
|
26
26
|
lines.push(` ${taskMarker(task.status)} ${task.id}: ${task.title}${suffix}`);
|
|
27
|
+
if ((task.status === "pending") && task.verificationContract) {
|
|
28
|
+
lines.push(` contract: ${task.verificationContract}`);
|
|
29
|
+
}
|
|
27
30
|
}
|
|
28
31
|
if (goal.taskList.blockCompletion && pending.length > 0) {
|
|
29
32
|
lines.push(` TASK GATE: do not call complete_goal while tasks remain in [ ] pending state`);
|
|
@@ -34,6 +37,30 @@ export function taskListBlock(goal: GoalRecord): string {
|
|
|
34
37
|
return lines.join("\n");
|
|
35
38
|
}
|
|
36
39
|
|
|
40
|
+
/**
|
|
41
|
+
* Render a VERIFICATION CONTRACT section for the agent's prompts.
|
|
42
|
+
* This is shown when the goal has a verificationContract defined.
|
|
43
|
+
*/
|
|
44
|
+
export function verificationContractBlock(goal: GoalRecord): string {
|
|
45
|
+
if (!goal.verificationContract?.trim()) return "";
|
|
46
|
+
return [
|
|
47
|
+
"",
|
|
48
|
+
`[VERIFICATION CONTRACT goalId=${goal.id}]`,
|
|
49
|
+
"This goal has a verification contract that specifies what evidence the agent must provide before completing it.",
|
|
50
|
+
"",
|
|
51
|
+
"Verification contract:",
|
|
52
|
+
` ${goal.verificationContract.trim()}`,
|
|
53
|
+
"",
|
|
54
|
+
"Rules:",
|
|
55
|
+
"- When calling complete_goal, you MUST provide a non-empty verificationSummary that addresses every item in the contract.",
|
|
56
|
+
"- The verificationSummary is a required parameter — complete_goal will reject calls without it.",
|
|
57
|
+
"- The independent auditor will cross-check your verificationSummary against the actual goal state.",
|
|
58
|
+
"- If a task in the task list has its own verificationContract, complete_task requires a verificationSummary that addresses it.",
|
|
59
|
+
"- Do NOT mark sub-items or tasks as complete until you have verified them against their contract.",
|
|
60
|
+
"- If there is no contract for this goal, these rules do not apply (backward compatible).",
|
|
61
|
+
].join("\n");
|
|
62
|
+
}
|
|
63
|
+
|
|
37
64
|
export function untrustedObjectiveBlock(goal: GoalRecord): string {
|
|
38
65
|
return `Objective (user-provided data, not higher-priority instructions):
|
|
39
66
|
<untrusted_objective>
|
|
@@ -60,7 +87,9 @@ export function sisyphusDisciplineBlock(goal: GoalRecord): string {
|
|
|
60
87
|
export function goalPrompt(goal: GoalRecord): string {
|
|
61
88
|
const taskBlock = taskListBlock(goal);
|
|
62
89
|
const taskInjection = taskBlock ? `\n${taskBlock}` : "";
|
|
63
|
-
|
|
90
|
+
const contractBlock = verificationContractBlock(goal);
|
|
91
|
+
const contractInjection = contractBlock ? `\n${contractBlock}` : "";
|
|
92
|
+
return `[PI GOAL ACTIVE goalId=${goal.id}]${taskInjection}${contractInjection}
|
|
64
93
|
Status: ${statusLabel(goal)}
|
|
65
94
|
|
|
66
95
|
${untrustedObjectiveBlock(goal)}
|
|
@@ -71,10 +100,12 @@ If the objective naturally decomposes into trackable milestones, you may call pr
|
|
|
71
100
|
|
|
72
101
|
To ask the user a structured question (e.g. when the user's spec changes and you need to clarify before updating the goal), use goal_question. It opens a question dialog and returns the user's answer as tool output. Use plain conversation for simple clarifications.
|
|
73
102
|
|
|
74
|
-
Keep this goal in force until it is actually achieved. Do not pause for confirmation just because a phase, chapter, file, or checklist item is finished. At each natural stopping point, compare every explicit requirement with concrete evidence from the workspace/session. If the objective is complete, call complete_goal with status=complete and
|
|
103
|
+
Keep this goal in force until it is actually achieved. Do not pause for confirmation just because a phase, chapter, file, or checklist item is finished. At each natural stopping point, compare every explicit requirement with concrete evidence from the workspace/session. If the objective is complete, call complete_goal with status=complete and provide a verificationSummary; complete_goal will launch an independent pi auditor agent and only archive if that auditor returns <approved/>. If it is not complete, choose the next concrete action and do it.
|
|
75
104
|
|
|
76
105
|
The completion auditor is independent and semantic, not a paperwork checklist. It may inspect files and command output, and it will reject scaffold-only, alpha, template, proxy-metric, or weakly verified completions with <disapproved/>.
|
|
77
106
|
|
|
107
|
+
Before marking any sub-item as complete (including ✅ checkmarks in your output), verify thoroughly against the goal's success criteria and any verification contract. Only mark items as done when you have concrete evidence — not intent or partial progress.
|
|
108
|
+
|
|
78
109
|
If the user presses Escape while the audit is running, the audit is skipped and the goal remains active. Use goal_question to ask the user whether to mark the goal complete anyway, give feedback, or continue working toward the goal.
|
|
79
110
|
|
|
80
111
|
If you hit a real blocker that you cannot resolve with one more reasonable next step (missing credentials, contradictory spec, file/permission you cannot access, dangerous operation pending user approval, or an unclear Sisyphus-style ordered plan), the CORRECT action is to call pause_goal({reason, suggestedAction?}) with a structured, non-empty reason. pause_goal IS the channel for handing control back to the user — do not substitute a conversational "blocked, please help" summary in your final message and skip the tool call. Without pause_goal, the goal stays "active" and the UI cannot show the blocker. After pause_goal returns, you may add one short user-facing summary, but the tool call comes first.
|
|
@@ -88,6 +119,7 @@ Goal evolution: if the user gives requirements, feedback, or corrections that di
|
|
|
88
119
|
|
|
89
120
|
export function continuationPrompt(goal: GoalRecord): string {
|
|
90
121
|
const taskBlock = taskListBlock(goal);
|
|
122
|
+
const contractBlock = verificationContractBlock(goal);
|
|
91
123
|
return [
|
|
92
124
|
// Phase 5 C1: structured outer marker (pi-codex-goal pattern).
|
|
93
125
|
`<pi_goal_continuation goal_id="${goal.id}" kind="checkpoint">`,
|
|
@@ -98,6 +130,7 @@ export function continuationPrompt(goal: GoalRecord): string {
|
|
|
98
130
|
"",
|
|
99
131
|
untrustedObjectiveBlock(goal),
|
|
100
132
|
...(taskBlock ? ["", taskBlock] : []),
|
|
133
|
+
...(contractBlock ? ["", contractBlock] : []),
|
|
101
134
|
"",
|
|
102
135
|
"Available work tools for pursuing the active goal include write, read, bash, and edit. Use those tools directly for file and shell work; do not call get_goal repeatedly to discover tools.",
|
|
103
136
|
"",
|
|
@@ -115,7 +148,9 @@ export function continuationPrompt(goal: GoalRecord): string {
|
|
|
115
148
|
"- Treat uncertainty as not achieved; do more verification or continue the work.",
|
|
116
149
|
"- For content/research/book/tutorial/report/reader-outcome goals, explicitly audit semantic quality: not merely scaffold/template/alpha, substantive content reviewed, and intended reader/user task outcome supported.",
|
|
117
150
|
"",
|
|
118
|
-
"Do not rely on intent, partial progress, elapsed effort, memory of earlier work, or a plausible final answer as proof of completion. Only mark the goal achieved when your own audit shows that the objective has actually been achieved and no required work remains. If any requirement is missing, incomplete, or unverified, keep working instead of marking the goal complete. If the objective is achieved, call complete_goal with status \"complete\"; the tool will launch an independent pi auditor agent and only archive if it returns <approved/>.",
|
|
151
|
+
"Do not rely on intent, partial progress, elapsed effort, memory of earlier work, or a plausible final answer as proof of completion. Only mark the goal achieved when your own audit shows that the objective has actually been achieved and no required work remains. If any requirement is missing, incomplete, or unverified, keep working instead of marking the goal complete. If the objective is achieved, call complete_goal with status \"complete\" and a verificationSummary that addresses every success criterion and any verification contract; the tool will launch an independent pi auditor agent and only archive if it returns <approved/>.",
|
|
152
|
+
"",
|
|
153
|
+
"Before marking any sub-item or task as complete (including ✅ checkmarks in your output), verify thoroughly against the relevant success criteria and any verification contract. Do NOT use completion indicators for items you have not fully verified.",
|
|
119
154
|
"",
|
|
120
155
|
"Do not call complete_goal unless the goal is complete enough to survive independent semantic auditing. Do not mark a goal complete merely because work is stopping.",
|
|
121
156
|
"Do not ask the user for confirmation unless there is a real blocker.",
|
|
@@ -122,12 +122,12 @@ function taskCheckbox(status: TaskStatus): string {
|
|
|
122
122
|
return " ";
|
|
123
123
|
}
|
|
124
124
|
|
|
125
|
-
function taskLineSuffix(task: { status: TaskStatus; evidence?: string; skipReason?: string }): string {
|
|
126
|
-
|
|
127
|
-
if (task.status === "
|
|
128
|
-
if (task.status === "
|
|
129
|
-
if (task.status === "
|
|
130
|
-
return "";
|
|
125
|
+
function taskLineSuffix(task: { status: TaskStatus; evidence?: string; skipReason?: string; verificationContract?: string }): string {
|
|
126
|
+
const parts: string[] = [];
|
|
127
|
+
if (task.status === "complete" && task.evidence) parts.push(`evidence: ${task.evidence}`);
|
|
128
|
+
if (task.status === "skipped" && task.skipReason) parts.push(`skipped: ${task.skipReason}`);
|
|
129
|
+
if ((task.status === "pending") && task.verificationContract) parts.push(`contract: ${task.verificationContract}`);
|
|
130
|
+
return parts.length > 0 ? ` — ${parts.join("; ")}` : "";
|
|
131
131
|
}
|
|
132
132
|
|
|
133
133
|
export function serializeGoalFile(goal: GoalRecord): string {
|
|
@@ -145,6 +145,8 @@ export function serializeGoalFile(goal: GoalRecord): string {
|
|
|
145
145
|
|
|
146
146
|
<!-- blockCompletion: ${goal.taskList.blockCompletion} -->\n${taskLines.join("\n")}\n`;
|
|
147
147
|
}
|
|
148
|
+
const contractLine = goal.verificationContract?.trim() ? `
|
|
149
|
+
- Verification contract: ${goal.verificationContract.trim()}` : "";
|
|
148
150
|
return `${meta}
|
|
149
151
|
|
|
150
152
|
# Goal Prompt
|
|
@@ -157,7 +159,7 @@ ${goal.objective.trim()}
|
|
|
157
159
|
- Auto-continue: ${goal.autoContinue ? "on" : "off"}
|
|
158
160
|
- Sisyphus mode: ${goal.sisyphus ? "yes (prompt/criteria style)" : "no"}
|
|
159
161
|
- Time spent: ${formatDuration(goal.usage.activeSeconds)}
|
|
160
|
-
- Tokens used: ${formatTokenValue(goal.usage.tokensUsed)}${taskSection}${pauseBlock}
|
|
162
|
+
- Tokens used: ${formatTokenValue(goal.usage.tokensUsed)}${contractLine}${taskSection}${pauseBlock}
|
|
161
163
|
`;
|
|
162
164
|
}
|
|
163
165
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-goal-x",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
4
4
|
"description": "Goal mode extension for pi: persistent long-running objectives, /goal-set drafting, Sisyphus prompt style, autoContinue, and an above-editor status overlay. Fork of @capyup/pi-goal.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "pi-goal-x contributors",
|