@linimin/pi-letscook 0.1.67 → 0.1.69
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/.agent/README.md +2 -3
- package/.agent/verify_completion_control_plane.sh +21 -34
- package/CHANGELOG.md +17 -0
- package/README.md +23 -26
- package/agents/completion-bootstrapper.md +1 -2
- package/agents/completion-regrounder.md +10 -16
- package/extensions/completion/driver.ts +49 -117
- package/extensions/completion/index.ts +74 -123
- package/extensions/completion/policy-guards.ts +1 -1
- package/extensions/completion/prompt-surfaces.ts +15 -156
- package/extensions/completion/proposal.ts +21 -21
- package/extensions/completion/role-runner.ts +10 -11
- package/extensions/completion/state-store.ts +43 -85
- package/extensions/completion/types.ts +2 -3
- package/package.json +1 -1
- package/scripts/active-slice-contract-test.sh +28 -12
- package/scripts/canonical-evidence-artifact-test.sh +46 -59
- package/scripts/context-proposal-test.sh +58 -57
- package/scripts/refocus-test.sh +17 -17
- package/scripts/release-check.sh +14 -13
- package/scripts/role-runner-contract-test.sh +2 -2
- package/scripts/smoke-test.sh +24 -71
- package/skills/completion-protocol/SKILL.md +8 -9
- package/skills/completion-protocol/references/completion.md +2 -37
- package/skills/cook-handoff-boundary/SKILL.md +18 -16
|
@@ -9,8 +9,6 @@ import { Container, matchesKey, SelectList, Text } from "@mariozechner/pi-tui";
|
|
|
9
9
|
import { Type } from "typebox";
|
|
10
10
|
import {
|
|
11
11
|
autoContinueWorkflowIfNeeded,
|
|
12
|
-
completionContinuationFingerprint,
|
|
13
|
-
markQueuedDriverPromptInFlight,
|
|
14
12
|
registerCookCommand,
|
|
15
13
|
} from "./driver";
|
|
16
14
|
import {
|
|
@@ -18,9 +16,7 @@ import {
|
|
|
18
16
|
collectRecentDiscussionEntries,
|
|
19
17
|
collectRecentSessionMessages,
|
|
20
18
|
assessLatestCookHandoffProposal,
|
|
21
|
-
deriveCookContextProposalFromRecentDiscussion,
|
|
22
19
|
finalizeContextProposalAnalysis,
|
|
23
|
-
hasStructuredContextProposalSignal,
|
|
24
20
|
isWeakMissionAnchor,
|
|
25
21
|
missionAnchorsLikelyEquivalent,
|
|
26
22
|
missionAnchorsStrictlyEquivalent,
|
|
@@ -49,8 +45,7 @@ import {
|
|
|
49
45
|
maybeWriteContextProposalSnapshot,
|
|
50
46
|
} from "./prompt-surfaces";
|
|
51
47
|
import { toolCallBlockReason } from "./policy-guards";
|
|
52
|
-
import { analyzeContextProposalWithAgent, runCompletionRole } from "./role-runner";
|
|
53
|
-
import { generateCookHandoffWithAgent } from "./role-runner";
|
|
48
|
+
import { analyzeContextProposalWithAgent, generateCookHandoffWithAgent, runCompletionRole } from "./role-runner";
|
|
54
49
|
import {
|
|
55
50
|
applyLiveRoleEvent,
|
|
56
51
|
buildInlineRunningLines,
|
|
@@ -107,7 +102,6 @@ const RUBRIC_EVALUATION_ROLES = ["completion-reviewer", "completion-auditor", "c
|
|
|
107
102
|
type RubricEvaluationRole = (typeof RUBRIC_EVALUATION_ROLES)[number];
|
|
108
103
|
|
|
109
104
|
const liveRoleActivityByRoot = new Map<string, LiveRoleActivity>();
|
|
110
|
-
const activatedCompletionRoutingRoots = new Set<string>();
|
|
111
105
|
const LIVE_ROLE_HEARTBEAT_MS = 5_000;
|
|
112
106
|
const COOK_HANDOFF_BLOCK_REGEX = /```cook_handoff\s*[\s\S]*?```/giu;
|
|
113
107
|
|
|
@@ -212,44 +206,76 @@ function maybeWriteTestSnapshot(targetPath: string | undefined, content: string)
|
|
|
212
206
|
|
|
213
207
|
const COOK_MAIN_CHAT_RERUN_GUIDANCE = "Discuss changes in the main chat and rerun /cook.";
|
|
214
208
|
const COOK_STRUCTURED_DISCUSSION_FAILURE_DETAIL =
|
|
215
|
-
"/cook failed closed because the
|
|
209
|
+
"/cook failed closed because the primary-agent handoff step could not prepare a concrete startup handoff from the current task context. Clarify the mission, first slice, or verification intent in the main chat, then rerun /cook.";
|
|
216
210
|
|
|
217
211
|
function isWorkflowDone(snapshot: CompletionStateSnapshot | undefined): boolean {
|
|
218
212
|
return asString(snapshot?.state?.continuation_policy) === "done";
|
|
219
213
|
}
|
|
220
214
|
|
|
221
|
-
function activateCompletionRoutingForRoot(
|
|
222
|
-
|
|
223
|
-
|
|
215
|
+
function activateCompletionRoutingForRoot(_root: string | undefined): void {
|
|
216
|
+
// Workflow-entry legitimacy is derived from canonical .agent state rather than in-memory routing activation.
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function hasActiveWorkflowEntry(snapshot: CompletionStateSnapshot | undefined): boolean {
|
|
220
|
+
if (!snapshot) return false;
|
|
221
|
+
if (isWorkflowDone(snapshot)) return false;
|
|
222
|
+
return asString(snapshot.state?.workflow_entry_status) === "active" || isRecord(snapshot.startupBrief) || isRecord(snapshot.state?.advisory_startup_brief);
|
|
224
223
|
}
|
|
225
224
|
|
|
226
225
|
function hasCompletionRoutingActivation(snapshot: CompletionStateSnapshot | undefined): boolean {
|
|
227
226
|
if (!snapshot) return false;
|
|
228
227
|
if (roleFromEnv()) return true;
|
|
229
|
-
return
|
|
228
|
+
return false;
|
|
230
229
|
}
|
|
231
230
|
|
|
232
231
|
function latestUserOrCustomTurnText(ctx: { sessionManager?: any }): string | undefined {
|
|
233
|
-
|
|
232
|
+
const messages = collectRecentSessionMessages(ctx as { sessionManager: any }, { isRecord, asString, asNumber, isStaleContextError }, 4);
|
|
233
|
+
return messages.find((entry) => entry.role === "user" || entry.role === "custom")?.text;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function isCookCommandTurn(ctx: { sessionManager?: any }): boolean {
|
|
237
|
+
const latest = latestUserOrCustomTurnText(ctx);
|
|
238
|
+
if (!latest) return false;
|
|
239
|
+
return /^\/cook\b/.test(latest.trim());
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
function extractWorkflowSessionIdFromPrompt(text: string): string | undefined {
|
|
243
|
+
const match = text.match(/^- workflow_session_id:\s*(.+)$/m);
|
|
244
|
+
return match?.[1]?.trim() || undefined;
|
|
234
245
|
}
|
|
235
246
|
|
|
236
|
-
function isCompletionDriverPromptTurn(ctx: { sessionManager?: any }): boolean {
|
|
247
|
+
function isCompletionDriverPromptTurn(snapshot: CompletionStateSnapshot | undefined, ctx: { sessionManager?: any }): boolean {
|
|
237
248
|
const latest = latestUserOrCustomTurnText(ctx);
|
|
238
249
|
if (!latest) return false;
|
|
239
|
-
|
|
240
|
-
|
|
250
|
+
const isLegacySkillPrompt = /^\/skill:completion-protocol\b/.test(latest);
|
|
251
|
+
const isWorkflowDriverPrompt = /^COMPLETION WORKFLOW DRIVER\b/m.test(latest);
|
|
252
|
+
if (!isLegacySkillPrompt && !isWorkflowDriverPrompt) return false;
|
|
253
|
+
if (!/(?:Start or continue the completion workflow for this repo\.|Resume the completion workflow from canonical state\.)/.test(latest)) return false;
|
|
254
|
+
const canonicalSessionId = asString(snapshot?.state?.workflow_session_id);
|
|
255
|
+
const promptSessionId = extractWorkflowSessionIdFromPrompt(latest);
|
|
256
|
+
if (canonicalSessionId && promptSessionId && canonicalSessionId !== promptSessionId) return false;
|
|
257
|
+
return true;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function isCompletionWorkflowSessionTurn(snapshot: CompletionStateSnapshot | undefined, ctx: { sessionManager?: any }): boolean {
|
|
261
|
+
if (!(hasCompletionRoutingActivation(snapshot) || hasActiveWorkflowEntry(snapshot))) return false;
|
|
262
|
+
return isCompletionDriverPromptTurn(snapshot, ctx) || isCookCommandTurn(ctx);
|
|
241
263
|
}
|
|
242
264
|
|
|
243
265
|
function shouldInjectCompletionWorkflowContext(snapshot: CompletionStateSnapshot | undefined, ctx: { sessionManager?: any }): boolean {
|
|
244
|
-
return
|
|
266
|
+
return isCompletionWorkflowSessionTurn(snapshot, ctx);
|
|
245
267
|
}
|
|
246
268
|
|
|
247
|
-
function shouldInjectCookHandoffBoundary(
|
|
269
|
+
function shouldInjectCookHandoffBoundary(
|
|
270
|
+
event: { prompt?: string },
|
|
271
|
+
ctx: { sessionManager?: any },
|
|
272
|
+
snapshot?: CompletionStateSnapshot,
|
|
273
|
+
): boolean {
|
|
248
274
|
if (roleFromEnv()) return false;
|
|
249
|
-
if (
|
|
275
|
+
if (isCompletionWorkflowSessionTurn(snapshot, ctx)) return false;
|
|
250
276
|
const prompt = typeof event.prompt === "string" ? event.prompt.trim() : "";
|
|
251
277
|
if (!prompt) return false;
|
|
252
|
-
if (prompt.startsWith("/")) return false;
|
|
278
|
+
if (prompt.startsWith("/") || /^COMPLETION WORKFLOW DRIVER\b/m.test(prompt)) return false;
|
|
253
279
|
return true;
|
|
254
280
|
}
|
|
255
281
|
|
|
@@ -421,23 +447,10 @@ async function deriveCookContextProposal(
|
|
|
421
447
|
`latest verified slice: ${asString(snapshot.state?.latest_verified_slice) ?? "(none)"}`,
|
|
422
448
|
`active slice goal: ${asString(snapshot.active?.goal) ?? "(none)"}`,
|
|
423
449
|
`active slice why_now: ${asString(snapshot.active?.why_now) ?? "(none)"}`,
|
|
424
|
-
`approved startup plan summary: ${asString(snapshot.startupPlan?.goal_text) ?? "(none)"}`,
|
|
425
450
|
`verification goal: ${asString(snapshot.verificationEvidence?.goal) ?? "(none)"}`,
|
|
426
451
|
`verification summary: ${asString(snapshot.verificationEvidence?.summary) ?? "(none)"}`,
|
|
427
452
|
]
|
|
428
453
|
: [];
|
|
429
|
-
const workflowContext = snapshot
|
|
430
|
-
? {
|
|
431
|
-
currentMissionAnchor: asString(snapshot.state?.mission_anchor) ?? asString(snapshot.plan?.mission_anchor) ?? asString(snapshot.active?.mission_anchor),
|
|
432
|
-
latestCompletedSlice: asString(snapshot.state?.latest_completed_slice),
|
|
433
|
-
latestVerifiedSlice: asString(snapshot.state?.latest_verified_slice),
|
|
434
|
-
activeSliceGoal: asString(snapshot.active?.goal),
|
|
435
|
-
activeSliceWhyNow: asString(snapshot.active?.why_now),
|
|
436
|
-
verificationGoal: asString(snapshot.verificationEvidence?.goal),
|
|
437
|
-
verificationSummary: asString(snapshot.verificationEvidence?.summary),
|
|
438
|
-
continuationPolicy: asString(snapshot.state?.continuation_policy),
|
|
439
|
-
}
|
|
440
|
-
: undefined;
|
|
441
454
|
const raw = await generateCookHandoffWithAgent({
|
|
442
455
|
ctx,
|
|
443
456
|
projectName,
|
|
@@ -450,49 +463,20 @@ async function deriveCookContextProposal(
|
|
|
450
463
|
getCtxHasUI,
|
|
451
464
|
getCtxUi,
|
|
452
465
|
});
|
|
453
|
-
if (raw) {
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
}
|
|
468
|
-
const canFallbackToDiscussionProposal =
|
|
469
|
-
recentEntries.length >= 2 || recentEntries.some((entry) => hasStructuredContextProposalSignal(entry.text, stripCodeBlocks));
|
|
470
|
-
if (canFallbackToDiscussionProposal) {
|
|
471
|
-
const fromDiscussion = await deriveCookContextProposalFromRecentDiscussion(projectName, recentEntries, {
|
|
472
|
-
asString,
|
|
473
|
-
asStringArray,
|
|
474
|
-
assessMissionAnchor,
|
|
475
|
-
normalizeMissionAnchorText,
|
|
476
|
-
isWeakMissionAnchor,
|
|
477
|
-
missionAnchorsStrictlyEquivalent,
|
|
478
|
-
stripCodeBlocks,
|
|
479
|
-
analyzeContextProposal: async (candidateEntries) =>
|
|
480
|
-
await analyzeContextProposalWithAgent({
|
|
481
|
-
ctx,
|
|
482
|
-
projectName,
|
|
483
|
-
recentEntries: candidateEntries,
|
|
484
|
-
workflowContextLines,
|
|
485
|
-
liveRoleActivityByRoot,
|
|
486
|
-
completionStatusKey: COMPLETION_STATUS_KEY,
|
|
487
|
-
safeUiCall,
|
|
488
|
-
getCtxCwd,
|
|
489
|
-
getCtxHasUI,
|
|
490
|
-
getCtxUi,
|
|
491
|
-
}),
|
|
492
|
-
workflowContext,
|
|
493
|
-
});
|
|
494
|
-
if (fromDiscussion) return { proposal: fromDiscussion };
|
|
495
|
-
}
|
|
466
|
+
if (!raw) return {};
|
|
467
|
+
const generated = assessLatestCookHandoffProposal([
|
|
468
|
+
{ role: "assistant", text: raw, messageId: "generated-primary-agent-handoff", timestampMs: Date.now(), isCommand: false },
|
|
469
|
+
], projectName, {
|
|
470
|
+
asString,
|
|
471
|
+
asStringArray,
|
|
472
|
+
assessMissionAnchor,
|
|
473
|
+
normalizeMissionAnchorText,
|
|
474
|
+
isWeakMissionAnchor,
|
|
475
|
+
missionAnchorsStrictlyEquivalent,
|
|
476
|
+
stripCodeBlocks,
|
|
477
|
+
});
|
|
478
|
+
if (generated.status === "startable") return { proposal: generated.proposal };
|
|
479
|
+
if (generated.status === "fresh_but_not_startable") return { blockedFailureMessage: generated.message };
|
|
496
480
|
return {};
|
|
497
481
|
}
|
|
498
482
|
|
|
@@ -530,19 +514,13 @@ async function confirmContextProposal(
|
|
|
530
514
|
async function scaffoldCompletionFiles(
|
|
531
515
|
root: string,
|
|
532
516
|
missionAnchor: string,
|
|
533
|
-
options?: {
|
|
534
|
-
analysis?: ContextProposalAnalysis;
|
|
535
|
-
continuationReason?: string;
|
|
536
|
-
advisoryStartupBrief?: JsonRecord;
|
|
537
|
-
approvedStartupPlan?: JsonRecord;
|
|
538
|
-
},
|
|
517
|
+
options?: { analysis?: ContextProposalAnalysis; continuationReason?: string; advisoryStartupBrief?: JsonRecord },
|
|
539
518
|
) {
|
|
540
519
|
const routing = finalizeContextProposalAnalysis(options?.analysis, [missionAnchor]);
|
|
541
520
|
return await scaffoldCompletionFilesOnDisk(root, missionAnchor, {
|
|
542
521
|
analysis: { taskType: routing.taskType, evaluationProfile: routing.evaluationProfile },
|
|
543
522
|
continuationReason: options?.continuationReason,
|
|
544
523
|
advisoryStartupBrief: options?.advisoryStartupBrief,
|
|
545
|
-
approvedStartupPlan: options?.approvedStartupPlan,
|
|
546
524
|
});
|
|
547
525
|
}
|
|
548
526
|
|
|
@@ -705,20 +683,6 @@ function verificationEvidenceContext(snapshot: CompletionStateSnapshot) {
|
|
|
705
683
|
};
|
|
706
684
|
}
|
|
707
685
|
|
|
708
|
-
function startupPlanContext(snapshot: CompletionStateSnapshot) {
|
|
709
|
-
const startupPlan = snapshot.startupPlan;
|
|
710
|
-
return {
|
|
711
|
-
path: path.relative(snapshot.files.root, snapshot.files.startupPlanPath) || ".agent/startup-plan.json",
|
|
712
|
-
status: startupPlan ? "present" : "missing",
|
|
713
|
-
source: asString(startupPlan?.source),
|
|
714
|
-
plannedSurfaces: asStringArray(startupPlan?.planned_surfaces),
|
|
715
|
-
verificationIntent: asStringArray(startupPlan?.verification_intent),
|
|
716
|
-
summary:
|
|
717
|
-
asString(startupPlan?.goal_text) ??
|
|
718
|
-
(startupPlan ? "Approved startup plan is present but its goal_text is missing." : "Approved startup plan is missing."),
|
|
719
|
-
};
|
|
720
|
-
}
|
|
721
|
-
|
|
722
686
|
function buildEvaluationRoleContextLines(snapshot: CompletionStateSnapshot, role: RubricEvaluationRole): string[] {
|
|
723
687
|
return buildExtractedEvaluationRoleContextLines(snapshot, role, {
|
|
724
688
|
asString,
|
|
@@ -749,7 +713,6 @@ function composeSystemReminder(snapshot: CompletionStateSnapshot, sliceHistory:
|
|
|
749
713
|
const exactActiveContract = activeCarriesExactHandoff(snapshot.active);
|
|
750
714
|
const activeContractDrift = activeSliceContractDriftSummary(snapshot);
|
|
751
715
|
const evidence = verificationEvidenceContext(snapshot);
|
|
752
|
-
const startupPlan = startupPlanContext(snapshot);
|
|
753
716
|
const activePriorityLine = activePriority !== undefined ? `Active slice priority: ${activePriority}` : undefined;
|
|
754
717
|
const activeWhyNowLine = activeWhyNow ? `Active slice why_now: ${activeWhyNow}` : undefined;
|
|
755
718
|
const implementationSurfacesLine =
|
|
@@ -779,7 +742,6 @@ function composeSystemReminder(snapshot: CompletionStateSnapshot, sliceHistory:
|
|
|
779
742
|
implementationSurfacesLine,
|
|
780
743
|
verificationCommandsLine,
|
|
781
744
|
evidence,
|
|
782
|
-
startupPlan,
|
|
783
745
|
evaluationRoleReminderText: isRubricEvaluationRole(nextRole) ? buildEvaluationRoleReminderText(snapshot, nextRole) : undefined,
|
|
784
746
|
});
|
|
785
747
|
}
|
|
@@ -799,19 +761,17 @@ function buildPostCompactionDriverInstructions(snapshot: CompletionStateSnapshot
|
|
|
799
761
|
const exactActiveContract = activeCarriesExactHandoff(snapshot.active);
|
|
800
762
|
const activeContractDrift = activeSliceContractDriftSummary(snapshot);
|
|
801
763
|
const evidence = verificationEvidenceContext(snapshot);
|
|
802
|
-
const startupPlan = startupPlanContext(snapshot);
|
|
803
764
|
const lines = [
|
|
804
765
|
"POST-COMPACTION RECOVERY MODE is active.",
|
|
805
766
|
`Compaction marker time: ${markerAt}`,
|
|
806
767
|
"Treat the previous conversation as lossy continuity support only.",
|
|
807
|
-
"Before taking any substantive action, re-read .agent/state.json, .agent/
|
|
768
|
+
"Before taking any substantive action, re-read .agent/state.json, .agent/plan.json, .agent/active-slice.json, .agent/slice-history.jsonl, .agent/stop-check-history.jsonl, and .agent/verification-evidence.json from disk.",
|
|
808
769
|
`Canonical task_type is currently: ${taskType}`,
|
|
809
770
|
`Canonical evaluation_profile is currently: ${evaluationProfile}`,
|
|
810
771
|
`Canonical next mandatory role is currently: ${nextRole}`,
|
|
811
772
|
`Canonical next mandatory action is currently: ${nextAction}`,
|
|
812
773
|
`Canonical continuation policy is currently: ${continuation}`,
|
|
813
774
|
`Canonical active slice is currently: ${activeSliceId}`,
|
|
814
|
-
`Canonical approved startup plan is currently: ${startupPlan.path} (${startupPlan.status})`,
|
|
815
775
|
`Canonical verification evidence artifact is currently: ${evidence.path} (${evidence.status})`,
|
|
816
776
|
"Do not trust pre-compaction memory over canonical files.",
|
|
817
777
|
"If the canonical state is ambiguous, inconsistent, missing, or stale after re-reading it, your first mandatory action is to dispatch completion-regrounder rather than guessing.",
|
|
@@ -826,10 +786,6 @@ function buildPostCompactionDriverInstructions(snapshot: CompletionStateSnapshot
|
|
|
826
786
|
if (activeWhyNow) lines.push(`Canonical active-slice why_now is currently: ${activeWhyNow}`);
|
|
827
787
|
if (implementationSurfaces.length > 0) lines.push(`Canonical implementation surfaces are currently: ${implementationSurfaces.join(", ")}`);
|
|
828
788
|
if (verificationCommands.length > 0) lines.push(`Canonical verification commands are currently: ${verificationCommands.join(" | ")}`);
|
|
829
|
-
if (startupPlan.source) lines.push(`Canonical approved startup plan source is currently: ${startupPlan.source}`);
|
|
830
|
-
if (startupPlan.plannedSurfaces.length > 0) lines.push(`Canonical approved startup plan surfaces are currently: ${startupPlan.plannedSurfaces.join(" | ")}`);
|
|
831
|
-
if (startupPlan.verificationIntent.length > 0) lines.push(`Canonical approved startup plan verification intent is currently: ${startupPlan.verificationIntent.join(" | ")}`);
|
|
832
|
-
lines.push(`Canonical approved startup plan summary is currently: ${startupPlan.summary}`);
|
|
833
789
|
if (evidence.subjectType) lines.push(`Canonical verification evidence subject is currently: ${evidence.subjectType}`);
|
|
834
790
|
if (evidence.outcome) lines.push(`Canonical verification evidence outcome is currently: ${evidence.outcome}`);
|
|
835
791
|
if (evidence.recordedAt) lines.push(`Canonical verification evidence recorded_at is currently: ${evidence.recordedAt}`);
|
|
@@ -920,7 +876,6 @@ function composeResumeCapsule(snapshot: CompletionStateSnapshot, sliceHistory: J
|
|
|
920
876
|
const verificationCommands = asStringArray(snapshot.active?.verification_commands);
|
|
921
877
|
const remainingBefore = asStringArray(snapshot.active?.remaining_contract_ids_before);
|
|
922
878
|
const evidence = verificationEvidenceContext(snapshot);
|
|
923
|
-
const startupPlan = startupPlanContext(snapshot);
|
|
924
879
|
const implementationSurfacesLine =
|
|
925
880
|
implementationSurfaces.length > 0 ? `- implementation_surfaces: ${implementationSurfaces.join(" | ")}` : undefined;
|
|
926
881
|
const verificationCommandsLine =
|
|
@@ -941,7 +896,6 @@ function composeResumeCapsule(snapshot: CompletionStateSnapshot, sliceHistory: J
|
|
|
941
896
|
activeSliceMatchesPlan: activeSliceMatchesPlan(snapshot),
|
|
942
897
|
activeSliceContractDrift: activeSliceContractDriftSummary(snapshot),
|
|
943
898
|
implementerHandoffSnapshot: handoffSnapshotState(snapshot.active),
|
|
944
|
-
startupPlan,
|
|
945
899
|
evidence,
|
|
946
900
|
activeSlice: {
|
|
947
901
|
sliceId: asString(snapshot.active?.slice_id) ?? asString(snapshot.activeSlice?.slice_id),
|
|
@@ -972,6 +926,7 @@ function completionKickoff(
|
|
|
972
926
|
evaluationProfile: string,
|
|
973
927
|
intent: "auto" | "continue" | "refocus" = "auto",
|
|
974
928
|
missionAnchor?: string,
|
|
929
|
+
workflowSessionId?: string,
|
|
975
930
|
): string {
|
|
976
931
|
const intentBlock =
|
|
977
932
|
intent === "continue" && missionAnchor
|
|
@@ -979,11 +934,13 @@ function completionKickoff(
|
|
|
979
934
|
: intent === "refocus" && missionAnchor
|
|
980
935
|
? `Updated canonical mission anchor:\n${missionAnchor}\n\nWorkflow intent:\n- The user explicitly refocused the workflow before this kickoff.\n- Re-read canonical .agent/** state and continue from the refocused mission.\n\n`
|
|
981
936
|
: "";
|
|
982
|
-
|
|
937
|
+
const sessionBlock = workflowSessionId ? `Workflow session:\n- workflow_session_id: ${workflowSessionId}\n\n` : "";
|
|
938
|
+
return `COMPLETION WORKFLOW DRIVER\nStart or continue the completion workflow for this repo.\n\nBefore acting, read:\n- ${SKILL_PATH}\n- ${REFERENCE_PATH}\n\nCanonical routing profile:\n- task_type: ${taskType}\n- evaluation_profile: ${evaluationProfile}\n\n${sessionBlock}User goal:\n${goal}\n\n${intentBlock}Driver instructions:\n- Canonical truth is in .agent/**. Re-read .agent/state.json, .agent/startup-brief.json, .agent/plan.json, .agent/active-slice.json, and .agent/verification-evidence.json before acting when they exist.\n- If tracked completion contract files are missing or onboarding is required, invoke completion_role with role completion-bootstrapper.\n- Otherwise follow the mandatory dispatch rules from completion-protocol.\n- Treat .agent/startup-brief.json as canonical intake, not as the canonical slice plan.\n- For selected, in-progress, committed, or done slices, treat .agent/active-slice.json as the canonical implementation contract and route to completion-regrounder if it drifts from the selected plan slice or the exact handoff is unclear.\n- Consume .agent/verification-evidence.json instead of temp-only verification summaries when it is populated.\n- Use completion_role for all completion-* role work. Do not directly implement tracked product changes yourself.\n- Continue dispatching mandatory roles while continuation_policy == continue.\n- Only stop for the user when continuation_policy is await_user_input, blocked, paused, or done.`;
|
|
983
939
|
}
|
|
984
940
|
|
|
985
|
-
function completionResumePrompt(taskType: string, evaluationProfile: string): string {
|
|
986
|
-
|
|
941
|
+
function completionResumePrompt(taskType: string, evaluationProfile: string, workflowSessionId?: string): string {
|
|
942
|
+
const sessionBlock = workflowSessionId ? `Workflow session:\n- workflow_session_id: ${workflowSessionId}\n\n` : "";
|
|
943
|
+
return `COMPLETION WORKFLOW DRIVER\nResume the completion workflow from canonical state.\n\nBefore acting, read:\n- ${SKILL_PATH}\n- ${REFERENCE_PATH}\n\nCanonical routing profile:\n- task_type: ${taskType}\n- evaluation_profile: ${evaluationProfile}\n\n${sessionBlock}Resume instructions:\n- Re-read .agent/state.json, .agent/startup-brief.json, .agent/plan.json, .agent/active-slice.json, and .agent/verification-evidence.json before acting.\n- If canonical state is missing, invalid, contradictory, stale, or ambiguous, route to completion-regrounder first.\n- Treat .agent/startup-brief.json as canonical intake, not as the canonical slice plan.\n- For selected, in-progress, committed, or done slices, treat .agent/active-slice.json as the canonical implementation contract and route to completion-regrounder if it drifts from the selected plan slice or the exact handoff is unclear.\n- Consume .agent/verification-evidence.json instead of temp-only verification summaries when it is populated.\n- Continue from next_mandatory_role and next_mandatory_action.\n- Use completion_role for all completion-* role work.\n- Continue dispatching mandatory roles while continuation_policy == continue.\n- Only stop for the user when continuation_policy is await_user_input, blocked, paused, or done.`;
|
|
987
944
|
}
|
|
988
945
|
|
|
989
946
|
export default function completionExtension(pi: ExtensionAPI) {
|
|
@@ -999,7 +956,7 @@ export default function completionExtension(pi: ExtensionAPI) {
|
|
|
999
956
|
structuredDiscussionFailureDetail: COOK_STRUCTURED_DISCUSSION_FAILURE_DETAIL,
|
|
1000
957
|
mainChatRerunGuidance: COOK_MAIN_CHAT_RERUN_GUIDANCE,
|
|
1001
958
|
cookCommandSpec: {
|
|
1002
|
-
description: "/cook workflow:
|
|
959
|
+
description: "/cook workflow: start or replace workflow only from an explicit primary-agent handoff, or resume the current workflow from canonical state",
|
|
1003
960
|
},
|
|
1004
961
|
buildContextProposalContinuationReason,
|
|
1005
962
|
completionKickoff,
|
|
@@ -1034,7 +991,7 @@ export default function completionExtension(pi: ExtensionAPI) {
|
|
|
1034
991
|
await refreshCompletionStatus({ ctx, ...statusSurfaceArgs });
|
|
1035
992
|
if (shouldTestAutoContinueOnSessionStart()) {
|
|
1036
993
|
const snapshot = await loadCompletionSnapshot(getCtxCwd(ctx));
|
|
1037
|
-
if (
|
|
994
|
+
if (isCompletionWorkflowSessionTurn(snapshot, ctx)) {
|
|
1038
995
|
await autoContinueWorkflowIfNeeded(pi, ctx, driverDeps);
|
|
1039
996
|
}
|
|
1040
997
|
}
|
|
@@ -1050,19 +1007,13 @@ export default function completionExtension(pi: ExtensionAPI) {
|
|
|
1050
1007
|
await fsp.rm(snapshot.files.compactionMarkerPath, { force: true });
|
|
1051
1008
|
}
|
|
1052
1009
|
await refreshCompletionStatus({ ctx, ...statusSurfaceArgs });
|
|
1053
|
-
if (
|
|
1010
|
+
if (isCompletionWorkflowSessionTurn(snapshot, ctx)) {
|
|
1054
1011
|
await autoContinueWorkflowIfNeeded(pi, ctx, driverDeps);
|
|
1055
1012
|
}
|
|
1056
1013
|
});
|
|
1057
1014
|
|
|
1058
1015
|
pi.on("before_agent_start", async (event, ctx) => {
|
|
1059
1016
|
const loaded = await loadCompletionDataForReminder(getCtxCwd(ctx));
|
|
1060
|
-
const driverPromptTurn = isCompletionDriverPromptTurn(ctx);
|
|
1061
|
-
if (loaded && driverPromptTurn) {
|
|
1062
|
-
const rootKey = completionRootKey(loaded.snapshot, getCtxCwd(ctx));
|
|
1063
|
-
const fingerprint = completionContinuationFingerprint(loaded.snapshot);
|
|
1064
|
-
if (fingerprint) markQueuedDriverPromptInFlight(rootKey, fingerprint);
|
|
1065
|
-
}
|
|
1066
1017
|
const systemPrompt = getSystemPromptSafe(ctx);
|
|
1067
1018
|
if (!systemPrompt) return;
|
|
1068
1019
|
if (loaded && shouldInjectCompletionWorkflowContext(loaded.snapshot, ctx)) {
|
|
@@ -1087,7 +1038,7 @@ export default function completionExtension(pi: ExtensionAPI) {
|
|
|
1087
1038
|
systemPrompt: `${systemPrompt}\n\n${additions.join("\n\n")}`,
|
|
1088
1039
|
};
|
|
1089
1040
|
}
|
|
1090
|
-
if (!shouldInjectCookHandoffBoundary(event, ctx)) return;
|
|
1041
|
+
if (!shouldInjectCookHandoffBoundary(event, ctx, loaded?.snapshot)) return;
|
|
1091
1042
|
const handoffReminder = buildCookHandoffBoundaryReminder();
|
|
1092
1043
|
maybeWriteTestSnapshot(completionTestCookHandoffReminderPath(), handoffReminder);
|
|
1093
1044
|
return {
|
|
@@ -1130,7 +1081,7 @@ export default function completionExtension(pi: ExtensionAPI) {
|
|
|
1130
1081
|
const snapshot = await loadCompletionSnapshot(cwd);
|
|
1131
1082
|
const completionActive = Boolean(snapshot) && asString(snapshot?.state?.continuation_policy) !== "done";
|
|
1132
1083
|
const root = snapshot?.files.root ?? findRepoRoot(cwd) ?? cwd;
|
|
1133
|
-
const completionRoleDispatchAllowed = Boolean(role) || (
|
|
1084
|
+
const completionRoleDispatchAllowed = Boolean(role) || isCompletionWorkflowSessionTurn(snapshot, ctx);
|
|
1134
1085
|
const reason = toolCallBlockReason({
|
|
1135
1086
|
toolName: event.toolName,
|
|
1136
1087
|
input: isRecord(event.input) ? event.input : undefined,
|
|
@@ -1151,7 +1102,7 @@ export default function completionExtension(pi: ExtensionAPI) {
|
|
|
1151
1102
|
"Use completion_role when driving the completion workflow and a mandatory completion role must act next.",
|
|
1152
1103
|
"Use completion_role only for completion-bootstrapper, completion-regrounder, completion-implementer, completion-reviewer, completion-auditor, or completion-stop-judge.",
|
|
1153
1104
|
"Do not use completion_role from inside a completion role; only the workflow driver may dispatch roles.",
|
|
1154
|
-
"Do not call completion_role from ordinary chat; it is reserved for
|
|
1105
|
+
"Do not call completion_role from ordinary chat; it is reserved for active /cook workflow sessions.",
|
|
1155
1106
|
],
|
|
1156
1107
|
parameters: Type.Object({
|
|
1157
1108
|
role: StringEnum(ROLE_NAMES, { description: "The completion role to invoke." }),
|
|
@@ -72,7 +72,7 @@ export function toolCallBlockReason(args: {
|
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
if (toolName === "completion_role" && !completionRoleDispatchAllowed) {
|
|
75
|
-
return "completion_role may only be used from an
|
|
75
|
+
return "completion_role may only be used from an active /cook workflow session.";
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
if (toolName === "edit" || toolName === "write") {
|