@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 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
- maybeWriteTestSnapshot(completionTestDriverPromptPath(), `${resumePrompt}\n`);
3542
- if (shouldSkipDriverKickoffForTests()) {
3543
- emitCommandText(ctx, "Skipped completion workflow resume kickoff (test mode)", "info");
3544
- return;
3545
- }
3546
- pi.sendUserMessage(resumePrompt);
3547
- emitCommandText(ctx, "Queued completion workflow resume", "info");
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
- maybeWriteTestSnapshot(completionTestDriverPromptPath(), `${kickoffPrompt}\n`);
3617
- if (shouldSkipDriverKickoffForTests()) {
3618
- emitCommandText(ctx, "Skipped completion workflow kickoff (test mode)", "info");
3619
- return;
3620
- }
3621
- pi.sendUserMessage(kickoffPrompt);
3622
- emitCommandText(ctx, "Queued completion workflow kickoff", "info");
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.31",
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,
@@ -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