@linimin/pi-letscook 0.1.31 → 0.1.32
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/CHANGELOG.md +7 -0
- package/extensions/completion/index.ts +167 -14
- package/package.json +1 -1
- package/scripts/smoke-test.sh +21 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.32
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
|
|
7
|
+
- made `/cook` auto-continue workflows from canonical state when `continuation_policy == continue`, so the primary driver re-queues the canonical resume prompt after intermediate role turns instead of parking silently on known mandatory steps
|
|
8
|
+
- added smoke coverage for the new auto-resume driver prompt behavior and a guarded parked-state warning path to avoid infinite requeue loops on an unchanged mandatory state
|
|
9
|
+
|
|
3
10
|
## 0.1.31
|
|
4
11
|
|
|
5
12
|
### Changed
|
|
@@ -225,6 +225,16 @@ const liveRoleActivityByRoot = new Map<string, LiveRoleActivity>();
|
|
|
225
225
|
const LIVE_ROLE_WAITING_MS = 15_000;
|
|
226
226
|
const LIVE_ROLE_STALLED_MS = 45_000;
|
|
227
227
|
const LIVE_ROLE_HEARTBEAT_MS = 5_000;
|
|
228
|
+
const DRIVER_AUTO_CONTINUE_MAX_ATTEMPTS = 2;
|
|
229
|
+
|
|
230
|
+
type DriverContinuationTracker = {
|
|
231
|
+
fingerprint: string;
|
|
232
|
+
attempts: number;
|
|
233
|
+
inFlight: boolean;
|
|
234
|
+
warned: boolean;
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
const driverContinuationByRoot = new Map<string, DriverContinuationTracker>();
|
|
228
238
|
|
|
229
239
|
function isRecord(value: unknown): value is JsonRecord {
|
|
230
240
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -529,6 +539,14 @@ function completionTestDriverPromptPath(): string | undefined {
|
|
|
529
539
|
return asString(process.env.PI_COMPLETION_TEST_DRIVER_PROMPT_PATH);
|
|
530
540
|
}
|
|
531
541
|
|
|
542
|
+
function completionTestAutoContinuePromptPath(): string | undefined {
|
|
543
|
+
return asString(process.env.PI_COMPLETION_TEST_AUTO_CONTINUE_PROMPT_PATH);
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
function shouldTestAutoContinueOnSessionStart(): boolean {
|
|
547
|
+
return process.env.PI_COMPLETION_TEST_AUTO_CONTINUE_ON_SESSION_START === "1";
|
|
548
|
+
}
|
|
549
|
+
|
|
532
550
|
function completionTestSystemReminderPath(): string | undefined {
|
|
533
551
|
return asString(process.env.PI_COMPLETION_TEST_SYSTEM_REMINDER_PATH);
|
|
534
552
|
}
|
|
@@ -1641,6 +1659,128 @@ function currentEvaluationProfile(snapshot: CompletionStateSnapshot): string | u
|
|
|
1641
1659
|
);
|
|
1642
1660
|
}
|
|
1643
1661
|
|
|
1662
|
+
function completionContinuationFingerprint(snapshot: CompletionStateSnapshot): string | undefined {
|
|
1663
|
+
if (asString(snapshot.state?.continuation_policy) !== "continue") return undefined;
|
|
1664
|
+
const nextMandatoryRole = asString(snapshot.state?.next_mandatory_role);
|
|
1665
|
+
if (!nextMandatoryRole) return undefined;
|
|
1666
|
+
return JSON.stringify({
|
|
1667
|
+
mission_anchor: asString(snapshot.state?.mission_anchor) ?? asString(snapshot.plan?.mission_anchor) ?? null,
|
|
1668
|
+
task_type: currentTaskType(snapshot) ?? null,
|
|
1669
|
+
evaluation_profile: currentEvaluationProfile(snapshot) ?? null,
|
|
1670
|
+
current_phase: asString(snapshot.state?.current_phase) ?? null,
|
|
1671
|
+
next_mandatory_role: nextMandatoryRole,
|
|
1672
|
+
next_mandatory_action: asString(snapshot.state?.next_mandatory_action) ?? null,
|
|
1673
|
+
active_status: asString(snapshot.active?.status) ?? null,
|
|
1674
|
+
active_slice_id: asString(snapshot.active?.slice_id) ?? asString(snapshot.activeSlice?.slice_id) ?? null,
|
|
1675
|
+
latest_completed_slice: asString(snapshot.state?.latest_completed_slice) ?? null,
|
|
1676
|
+
latest_verified_slice: asString(snapshot.state?.latest_verified_slice) ?? null,
|
|
1677
|
+
});
|
|
1678
|
+
}
|
|
1679
|
+
|
|
1680
|
+
function noteQueuedDriverPrompt(rootKey: string, fingerprint: string): void {
|
|
1681
|
+
const tracker = driverContinuationByRoot.get(rootKey);
|
|
1682
|
+
if (tracker && tracker.fingerprint === fingerprint) {
|
|
1683
|
+
tracker.attempts += 1;
|
|
1684
|
+
tracker.inFlight = false;
|
|
1685
|
+
tracker.warned = false;
|
|
1686
|
+
return;
|
|
1687
|
+
}
|
|
1688
|
+
driverContinuationByRoot.set(rootKey, {
|
|
1689
|
+
fingerprint,
|
|
1690
|
+
attempts: 1,
|
|
1691
|
+
inFlight: false,
|
|
1692
|
+
warned: false,
|
|
1693
|
+
});
|
|
1694
|
+
}
|
|
1695
|
+
|
|
1696
|
+
function markQueuedDriverPromptInFlight(rootKey: string, fingerprint: string): void {
|
|
1697
|
+
const tracker = driverContinuationByRoot.get(rootKey);
|
|
1698
|
+
if (!tracker || tracker.fingerprint !== fingerprint) return;
|
|
1699
|
+
tracker.inFlight = true;
|
|
1700
|
+
}
|
|
1701
|
+
|
|
1702
|
+
function clearDriverContinuationTracker(rootKey: string): void {
|
|
1703
|
+
driverContinuationByRoot.delete(rootKey);
|
|
1704
|
+
}
|
|
1705
|
+
|
|
1706
|
+
function hasRunningCompletionRole(rootKey: string): boolean {
|
|
1707
|
+
return liveRoleActivityByRoot.get(rootKey)?.status === "running";
|
|
1708
|
+
}
|
|
1709
|
+
|
|
1710
|
+
function isWorkflowDriverActive(snapshot: CompletionStateSnapshot | undefined): boolean {
|
|
1711
|
+
return Boolean(snapshot) && asString(snapshot?.state?.continuation_policy) === "continue";
|
|
1712
|
+
}
|
|
1713
|
+
|
|
1714
|
+
function isDriverContinuationStateParked(rootKey: string, fingerprint: string): boolean {
|
|
1715
|
+
const tracker = driverContinuationByRoot.get(rootKey);
|
|
1716
|
+
if (!tracker || tracker.fingerprint !== fingerprint) return false;
|
|
1717
|
+
return tracker.warned;
|
|
1718
|
+
}
|
|
1719
|
+
|
|
1720
|
+
function rememberParkedDriverContinuation(rootKey: string, fingerprint: string): void {
|
|
1721
|
+
const tracker = driverContinuationByRoot.get(rootKey);
|
|
1722
|
+
if (!tracker || tracker.fingerprint !== fingerprint) return;
|
|
1723
|
+
tracker.warned = true;
|
|
1724
|
+
tracker.inFlight = false;
|
|
1725
|
+
}
|
|
1726
|
+
|
|
1727
|
+
async function queueCompletionDriverPrompt(
|
|
1728
|
+
pi: ExtensionAPI,
|
|
1729
|
+
ctx: { cwd: string; hasUI: boolean; ui: any },
|
|
1730
|
+
rootKey: string,
|
|
1731
|
+
fingerprint: string,
|
|
1732
|
+
prompt: string,
|
|
1733
|
+
kind: "kickoff" | "resume" | "auto-resume",
|
|
1734
|
+
): Promise<boolean> {
|
|
1735
|
+
const snapshotPath = kind === "auto-resume" ? completionTestAutoContinuePromptPath() : completionTestDriverPromptPath();
|
|
1736
|
+
maybeWriteTestSnapshot(snapshotPath, `${prompt}\n`);
|
|
1737
|
+
noteQueuedDriverPrompt(rootKey, fingerprint);
|
|
1738
|
+
if (shouldSkipDriverKickoffForTests()) {
|
|
1739
|
+
emitCommandText(ctx, `Skipped completion workflow ${kind} prompt (test mode)`, "info");
|
|
1740
|
+
return false;
|
|
1741
|
+
}
|
|
1742
|
+
pi.sendUserMessage(prompt);
|
|
1743
|
+
emitCommandText(ctx, `Queued completion workflow ${kind}`, "info");
|
|
1744
|
+
return true;
|
|
1745
|
+
}
|
|
1746
|
+
|
|
1747
|
+
async function autoContinueWorkflowIfNeeded(pi: ExtensionAPI, ctx: { cwd: string; hasUI: boolean; ui: any }): Promise<void> {
|
|
1748
|
+
if (roleFromEnv()) return;
|
|
1749
|
+
const snapshot = await loadCompletionSnapshot(getCtxCwd(ctx));
|
|
1750
|
+
const rootKey = completionRootKey(snapshot, getCtxCwd(ctx));
|
|
1751
|
+
if (!snapshot) {
|
|
1752
|
+
clearDriverContinuationTracker(rootKey);
|
|
1753
|
+
return;
|
|
1754
|
+
}
|
|
1755
|
+
const fingerprint = completionContinuationFingerprint(snapshot);
|
|
1756
|
+
if (!fingerprint) {
|
|
1757
|
+
clearDriverContinuationTracker(rootKey);
|
|
1758
|
+
return;
|
|
1759
|
+
}
|
|
1760
|
+
if (!isWorkflowDriverActive(snapshot) || hasRunningCompletionRole(rootKey)) return;
|
|
1761
|
+
const tracker = driverContinuationByRoot.get(rootKey);
|
|
1762
|
+
if (tracker && tracker.fingerprint === fingerprint) {
|
|
1763
|
+
if (tracker.inFlight) {
|
|
1764
|
+
tracker.inFlight = false;
|
|
1765
|
+
if (tracker.attempts >= DRIVER_AUTO_CONTINUE_MAX_ATTEMPTS) {
|
|
1766
|
+
if (!isDriverContinuationStateParked(rootKey, fingerprint)) {
|
|
1767
|
+
rememberParkedDriverContinuation(rootKey, fingerprint);
|
|
1768
|
+
emitCommandText(
|
|
1769
|
+
ctx,
|
|
1770
|
+
`Completion workflow is parked before mandatory role dispatch: ${asString(snapshot.state?.next_mandatory_role) ?? "(unknown)"}. Rerun /cook to continue from canonical state.`,
|
|
1771
|
+
"warning",
|
|
1772
|
+
);
|
|
1773
|
+
}
|
|
1774
|
+
return;
|
|
1775
|
+
}
|
|
1776
|
+
} else {
|
|
1777
|
+
return;
|
|
1778
|
+
}
|
|
1779
|
+
}
|
|
1780
|
+
const resumePrompt = completionResumePrompt(currentTaskType(snapshot) ?? "(missing)", currentEvaluationProfile(snapshot) ?? "(missing)");
|
|
1781
|
+
await queueCompletionDriverPrompt(pi, ctx, rootKey, fingerprint, resumePrompt, "auto-resume");
|
|
1782
|
+
}
|
|
1783
|
+
|
|
1644
1784
|
function isRubricEvaluationRole(role: string | undefined): role is RubricEvaluationRole {
|
|
1645
1785
|
return RUBRIC_EVALUATION_ROLES.includes(role as RubricEvaluationRole);
|
|
1646
1786
|
}
|
|
@@ -3020,6 +3160,9 @@ function completionResumePrompt(taskType: string, evaluationProfile: string): st
|
|
|
3020
3160
|
export default function completionExtension(pi: ExtensionAPI) {
|
|
3021
3161
|
pi.on("session_start", async (_event, ctx) => {
|
|
3022
3162
|
await refreshStatus(ctx);
|
|
3163
|
+
if (shouldTestAutoContinueOnSessionStart()) {
|
|
3164
|
+
await autoContinueWorkflowIfNeeded(pi, ctx);
|
|
3165
|
+
}
|
|
3023
3166
|
});
|
|
3024
3167
|
|
|
3025
3168
|
pi.on("turn_end", async (_event, ctx) => {
|
|
@@ -3032,10 +3175,16 @@ export default function completionExtension(pi: ExtensionAPI) {
|
|
|
3032
3175
|
await fsp.rm(snapshot.files.compactionMarkerPath, { force: true });
|
|
3033
3176
|
}
|
|
3034
3177
|
await refreshStatus(ctx);
|
|
3178
|
+
await autoContinueWorkflowIfNeeded(pi, ctx);
|
|
3035
3179
|
});
|
|
3036
3180
|
|
|
3037
3181
|
pi.on("before_agent_start", async (_event, ctx) => {
|
|
3038
3182
|
const loaded = await loadCompletionDataForReminder(getCtxCwd(ctx));
|
|
3183
|
+
if (loaded) {
|
|
3184
|
+
const rootKey = completionRootKey(loaded.snapshot, getCtxCwd(ctx));
|
|
3185
|
+
const fingerprint = completionContinuationFingerprint(loaded.snapshot);
|
|
3186
|
+
if (fingerprint) markQueuedDriverPromptInFlight(rootKey, fingerprint);
|
|
3187
|
+
}
|
|
3039
3188
|
if (!loaded) return;
|
|
3040
3189
|
const markerText = await readText(loaded.snapshot.files.compactionMarkerPath);
|
|
3041
3190
|
let marker: JsonRecord | undefined;
|
|
@@ -3538,13 +3687,14 @@ export default function completionExtension(pi: ExtensionAPI) {
|
|
|
3538
3687
|
currentTaskType(snapshot) ?? "(missing)",
|
|
3539
3688
|
currentEvaluationProfile(snapshot) ?? "(missing)",
|
|
3540
3689
|
);
|
|
3541
|
-
|
|
3542
|
-
|
|
3543
|
-
|
|
3544
|
-
|
|
3545
|
-
|
|
3546
|
-
|
|
3547
|
-
|
|
3690
|
+
const rootKey = completionRootKey(snapshot, getCtxCwd(ctx));
|
|
3691
|
+
const fingerprint = completionContinuationFingerprint(snapshot) ?? JSON.stringify({
|
|
3692
|
+
kind: "resume",
|
|
3693
|
+
mission_anchor: currentMissionAnchor(snapshot),
|
|
3694
|
+
current_phase: asString(snapshot.state?.current_phase) ?? null,
|
|
3695
|
+
next_mandatory_role: asString(snapshot.state?.next_mandatory_role) ?? null,
|
|
3696
|
+
});
|
|
3697
|
+
await queueCompletionDriverPrompt(pi, ctx, rootKey, fingerprint, resumePrompt, "resume");
|
|
3548
3698
|
return;
|
|
3549
3699
|
}
|
|
3550
3700
|
}
|
|
@@ -3613,13 +3763,16 @@ export default function completionExtension(pi: ExtensionAPI) {
|
|
|
3613
3763
|
kickoffIntent,
|
|
3614
3764
|
kickoffMissionAnchor,
|
|
3615
3765
|
);
|
|
3616
|
-
|
|
3617
|
-
|
|
3618
|
-
|
|
3619
|
-
|
|
3620
|
-
|
|
3621
|
-
|
|
3622
|
-
|
|
3766
|
+
const rootKey = completionRootKey(snapshot, getCtxCwd(ctx));
|
|
3767
|
+
const fingerprint = completionContinuationFingerprint(snapshot) ?? JSON.stringify({
|
|
3768
|
+
kind: "kickoff",
|
|
3769
|
+
mission_anchor: kickoffMissionAnchor,
|
|
3770
|
+
goal,
|
|
3771
|
+
intent: kickoffIntent,
|
|
3772
|
+
task_type: currentTaskType(snapshot) ?? "(missing)",
|
|
3773
|
+
evaluation_profile: currentEvaluationProfile(snapshot) ?? "(missing)",
|
|
3774
|
+
});
|
|
3775
|
+
await queueCompletionDriverPrompt(pi, ctx, rootKey, fingerprint, kickoffPrompt, "kickoff");
|
|
3623
3776
|
},
|
|
3624
3777
|
});
|
|
3625
3778
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@linimin/pi-letscook",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.32",
|
|
4
4
|
"description": "Pi package for long-running completion workflows with canonical .agent state, role-based subagents, continuity, and verification helpers.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"private": false,
|
package/scripts/smoke-test.sh
CHANGED
|
@@ -8,6 +8,7 @@ trap 'rm -rf "$TMPDIR"' EXIT
|
|
|
8
8
|
ROOT="$TMPDIR/repo"
|
|
9
9
|
KICKOFF_PROMPT="$TMPDIR/kickoff-prompt.txt"
|
|
10
10
|
RESUME_PROMPT="$TMPDIR/resume-prompt.txt"
|
|
11
|
+
AUTO_RESUME_PROMPT="$TMPDIR/auto-resume-prompt.txt"
|
|
11
12
|
|
|
12
13
|
mkdir -p "$ROOT"
|
|
13
14
|
cd "$ROOT"
|
|
@@ -72,6 +73,26 @@ assert f'- task_type: {expected_task_type}' in resume, 'resume prompt missing ca
|
|
|
72
73
|
assert f'- evaluation_profile: {expected_eval_profile}' in resume, 'resume prompt missing canonical evaluation_profile'
|
|
73
74
|
PY
|
|
74
75
|
|
|
76
|
+
PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
|
|
77
|
+
PI_COMPLETION_TEST_AUTO_CONTINUE_ON_SESSION_START=1 \
|
|
78
|
+
PI_COMPLETION_TEST_AUTO_CONTINUE_PROMPT_PATH="$AUTO_RESUME_PROMPT" \
|
|
79
|
+
pi -e "$PKG_ROOT" -p "/cook" \
|
|
80
|
+
>"$TMPDIR/pi-completion-smoke-auto-resume.out" 2>"$TMPDIR/pi-completion-smoke-auto-resume.err"
|
|
81
|
+
|
|
82
|
+
python3 - "$AUTO_RESUME_PROMPT" <<'PY'
|
|
83
|
+
import sys
|
|
84
|
+
from pathlib import Path
|
|
85
|
+
|
|
86
|
+
expected_task_type = 'completion-workflow'
|
|
87
|
+
expected_eval_profile = 'completion-rubric-v1'
|
|
88
|
+
auto_resume = Path(sys.argv[1]).read_text()
|
|
89
|
+
|
|
90
|
+
assert 'Resume the completion workflow from canonical state.' in auto_resume, 'auto-resume prompt should use the canonical resume workflow prompt'
|
|
91
|
+
assert 'Canonical routing profile:' in auto_resume, 'auto-resume prompt should expose canonical routing profile'
|
|
92
|
+
assert f'- task_type: {expected_task_type}' in auto_resume, 'auto-resume prompt missing canonical task_type'
|
|
93
|
+
assert f'- evaluation_profile: {expected_eval_profile}' in auto_resume, 'auto-resume prompt missing canonical evaluation_profile'
|
|
94
|
+
PY
|
|
95
|
+
|
|
75
96
|
python3 - <<'PY'
|
|
76
97
|
import json
|
|
77
98
|
from pathlib import Path
|