@astrosheep/pi-goal-next 0.1.1 → 0.1.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astrosheep/pi-goal-next",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "Persistent autonomous goals for pi, with Codex-verbatim goal semantics: continuation prompts, self-audited completion, budgets, and CAS-journaled state.",
5
5
  "type": "module",
6
6
  "files": [
@@ -43,6 +43,8 @@ export function createContinuation(deps: ContinuationDeps): Continuation {
43
43
  const seq = goal.continuationSeq + 1;
44
44
  const result = await deps.commit({ type: "continuation_sent", generation: leaseGeneration }, revision);
45
45
  if (result.kind !== "ok" || generation !== leaseGeneration) return;
46
+ const latest = deps.getSnapshot();
47
+ if (!latest || latest.goal.id !== goal.id || latest.goal.status !== "active") return; // user may have cleared/paused during the commit await
46
48
 
47
49
  deps.send({
48
50
  customType: "pi-goal-next/continuation",
package/src/lifecycle.ts CHANGED
@@ -29,7 +29,7 @@ function messageFromEvent(event: any, ctx: any): Message | null {
29
29
  return m?.role === message.role;
30
30
  });
31
31
  const m = candidate?.message ?? candidate ?? message;
32
- const entryId = m?.id ?? m?.entryId ?? message.id ?? message.entryId;
32
+ const entryId = candidate?.id ?? candidate?.entryId ?? m?.id ?? m?.entryId ?? message.id ?? message.entryId;
33
33
  // Entry identity is heuristic-by-position because branch entries may omit message ids.
34
34
  return typeof entryId === "string"
35
35
  ? { entryId, role: message.role, usage: m?.usage ?? message.usage, toolName: m?.toolName ?? message.toolName, stopReason: m?.stopReason ?? message.stopReason }
@@ -95,6 +95,9 @@ export function registerLifecycle(pi: PiEvents, deps: LifecycleDeps): void {
95
95
  await before(ctx);
96
96
  const snapshot = deps.goalCommit.current();
97
97
  if (!snapshot) { steeredGoalId = null; steered = false; return; }
98
+ // Cancellation gates every automatic send, including budget-limit steering.
99
+ if (lastAssistantStop === "error" || lastAssistantStop === "aborted") return;
100
+ if (snapshot.goal.status !== "active" && snapshot.goal.status !== "budget_limited") return;
98
101
  const verdict = deps.accounting.settleTurn(snapshot.goal);
99
102
  if (verdict.kind !== "ok") {
100
103
  const latest = deps.goalCommit.current();
@@ -108,8 +111,6 @@ export function registerLifecycle(pi: PiEvents, deps: LifecycleDeps): void {
108
111
  steered = true;
109
112
  deps.send({ customType: "pi-goal-next/budget_limit", content: budgetLimitPrompt(goal), display: false, details: { goalId: goal.id } }, { triggerTurn: true });
110
113
  }
111
- // Error/aborted turns produced no completed work; only continue after a normally-finished turn (Codex parity).
112
- if (lastAssistantStop === "error" || lastAssistantStop === "aborted") return;
113
114
  await deps.continuation.onSettled();
114
115
  });
115
116
  pi.on("message_start", async (event: MessageStartEvent, ctx: any) => { await before(ctx); await deps.continuation.onMessageStart(event?.message ?? event); });