@astrosheep/pi-goal-next 0.1.8 → 0.1.10

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # pi-goal
2
2
 
3
- `pi-goal` (published by this manifest as `pi-goal-next`) is a Pi 0.85.1 extension that adds a long-running `/goal`. It follows the Codex Goal semantics: the same model that performs the work audits its own progress and declares `complete` or `blocked` with `update_goal`. There is no independent auditor or human approval step.
3
+ `pi-goal` (published by this manifest as `pi-goal-next`) is a Pi 0.85.1 extension that adds a long-running `/goal`. It follows the Codex Goal semantics: the same model that performs the work audits its own progress and declares `complete`, `blocked`, or — at the user's explicit request — `paused` with `update_goal`. There is no independent auditor or human approval step.
4
4
 
5
5
  ## Installation
6
6
 
@@ -35,7 +35,7 @@ With no goal, or when the current goal is complete, `/goal <objective>` creates
35
35
 
36
36
  - `get_goal` returns the current snapshot plus `remainingBudget` and `elapsedSeconds`, or reports that no goal exists.
37
37
  - `create_goal` creates an active goal after an explicit request. It accepts `objective` and optional `token_budget` (a positive integer).
38
- - `update_goal` accepts only `complete` or `blocked` after the model's self-audit. `complete` is permitted from `active` and `budget_limited`, not `paused` or `blocked`; the host does not validate the declaration. On `complete` the result reports the final token usage.
38
+ - `update_goal` accepts `complete`, `blocked`, or `paused` after the model's self-audit. `paused` is honored only at the user's explicit request (prompt-level rule; the host enforces only the state machine). `complete` is permitted from `active` and `budget_limited`, not `paused` or `blocked`; the host does not validate declarations. On `complete` the result reports the final token usage.
39
39
 
40
40
  The prompt text is copied byte-for-byte from Codex Goal (`continuation.md`, `budget_limit.md`, `objective_updated.md`); the only deletion is Codex's `update_plan` "Progress visibility" paragraph, because Pi has no `update_plan` tool. Continuation prompts state the objective inside `<objective>` as user-provided data and carry the budget, evidence, fidelity, completion-audit, and blocked-audit rules. The three-consecutive-turn blocked audit is prompt-level guidance; the runtime does not enforce it.
41
41
 
@@ -70,7 +70,7 @@ The prompt text is copied byte-for-byte from Codex Goal (`continuation.md`, `bud
70
70
 
71
71
  ## Completion and blocking contract
72
72
 
73
- The model calls `update_goal` after auditing the current goal against the Codex completion audit carried in every continuation prompt. The host accepts `complete` and `blocked` at face value and performs no independent verification; the prompt is the only guard. `complete` may transition an `active` or `budget_limited` goal and reports final token usage; it is prohibited from `paused` and `blocked`. `blocked` is meant to follow three consecutive turns with the same blocker. `paused` is user-only (`/goal pause`). Runtime accounting may instead move an active goal to `budget_limited`; the model cannot declare that state, and the transition triggers one `budget_limit.md` steering message.
73
+ The model calls `update_goal` after auditing the current goal against the Codex completion audit carried in every continuation prompt. The host accepts `complete` and `blocked` at face value and performs no independent verification; the prompt is the only guard. `paused` via the tool requires the user's explicit request and is otherwise indistinguishable from a user `/goal pause`. `complete` may transition an `active` or `budget_limited` goal and reports final token usage; it is prohibited from `paused` and `blocked`. `blocked` is meant to follow three consecutive turns with the same blocker. Runtime accounting may instead move an active goal to `budget_limited`; the model cannot declare that state, and the transition triggers one `budget_limit.md` steering message.
74
74
 
75
75
  Automatic continuation waits for a fresh normal assistant completion and for the entire Pi run to settle, including tools, retries, compaction, and queued input. Errors, cancellation (including cancellation after text finishes), and runs ending at a tool boundary do not trigger continuation or budget steering. Ordinary user input has no continuation attached; once the response to that input finishes normally, an active goal may continue.
76
76
 
@@ -10,7 +10,7 @@ Pi 0.85.1 extension package adding a long-running `/goal`. Behavior follows Code
10
10
 
11
11
  `active | paused | blocked | budget_limited | complete`
12
12
 
13
- - Model's `update_goal` accepts only: `complete` | `blocked`. `complete` is permitted from `active` and `budget_limited`, but not from `paused` or `blocked`. The model self-audits; the host does not validate the declaration.
13
+ - Model's `update_goal` accepts only: `complete` | `blocked` | `paused` (paused at the user's explicit request, prompt-level only). `complete` is permitted from `active` and `budget_limited`, but not from `paused` or `blocked`. The model self-audits; the host does not validate the declaration.
14
14
  - `budget_limited` is set only by runtime/system paths (accounting).
15
15
  - `clear` is a journal entry `goal.cleared`; `fold()` yields null. Deletion is semantic.
16
16
  - One goal per session branch. `create_goal` refuses when an unfinished goal exists.
@@ -25,7 +25,7 @@ Pi 0.85.1 extension package adding a long-running `/goal`. Behavior follows Code
25
25
  | `accounting.ts` | usage attribution keyed by **message id** (assistant and toolResult separately), `settleTurn() → verdict: ok \| budget_limited`, `summary()` | triggering continuation, editing goal state directly (returns verdict; lifecycle commits it) |
26
26
  | `continuation.ts` | `generation` lease, `agent_settled` decision, commit-then-sendMessage, stale handling. The ONLY sender of continuation messages. | building UI, reading store, deciding acceptance |
27
27
  | `prompts.ts` | Pure: the three Codex-verbatim goal templates — `continuationPrompt(goal)`, `budgetLimitPrompt(goal)`, `objectiveUpdatedPrompt(goal)`; `escapeXmlText` applies to the objective only. | IO, model calls, host-side validation |
28
- | `tools.ts` | Codex-verbatim tool descriptions, TypeBox schema → `goalCommit.commit` → tool result. Three tools: get_goal / create_goal / update_goal. `update_goal` accepts only complete\|blocked and reports final usage on complete. | writing rules text, touching store/continuation |
28
+ | `tools.ts` | Codex-verbatim tool descriptions, TypeBox schema → `goalCommit.commit` → tool result. Three tools: get_goal / create_goal / update_goal. `update_goal` accepts complete\|blocked\|paused and reports final usage on complete. | writing rules text, touching store/continuation |
29
29
  | `commands.ts` | `/goal` subcommands → goal-commit. | direct store access |
30
30
  | `lifecycle.ts` | Event ordering, live-message identities, run cancellation signal, and input/settlement fences. Session/branch/compaction policies plus budget-limit steering, see below. | holding Goal state |
31
31
  | `ui.ts` | status/widget text from `current()` + `accounting.summary()`. Read-only. | writes |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astrosheep/pi-goal-next",
3
- "version": "0.1.8",
3
+ "version": "0.1.10",
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": [
package/src/goal.ts CHANGED
@@ -67,8 +67,8 @@ export function transition(state: Goal | null, intent: Intent): Goal | null {
67
67
  }
68
68
  if (intent.type === "transition") {
69
69
  if (!statuses.includes(intent.to)) throw new GoalError("invalid", "unknown status");
70
- if (intent.to === "paused" && !intent.userRequest?.trim()) throw new GoalError("forbidden", "paused requires user request evidence");
71
- if (intent.by === "agent" && !["complete", "blocked"].includes(intent.to)) throw new GoalError("forbidden", "agent cannot set this status");
70
+ if (intent.to === "paused" && intent.by === "user" && !intent.userRequest?.trim()) throw new GoalError("forbidden", "paused requires user request evidence");
71
+ if (intent.by === "agent" && !["complete", "blocked", "paused"].includes(intent.to)) throw new GoalError("forbidden", "agent cannot set this status");
72
72
  if (intent.by === "agent" && intent.to === "complete" && !["active", "budget_limited"].includes(state.status)) throw new GoalError("forbidden", "agent can complete only an active or budget-limited goal");
73
73
  const reset = intent.resetContinuations === true;
74
74
  if (reset && (intent.by !== "user" || intent.to !== "active")) throw new GoalError("forbidden", "only user resume can reset continuations");
package/src/tools.ts CHANGED
@@ -9,7 +9,7 @@ export type PiLike = { registerTool(tool: any): void };
9
9
 
10
10
  const text = (value: unknown) => typeof value === "string" ? value : JSON.stringify(value);
11
11
  const tokensUsed = (goal: Goal) => goal.usage.input + goal.usage.output + goal.usage.cacheRead + goal.usage.cacheWrite;
12
- function result(message: string, terminate = false): any { return { content: [{ type: "text", text: message }], ...(terminate ? { terminate: true } : {}) }; }
12
+ function result(message: string): any { return { content: [{ type: "text", text: message }] }; }
13
13
  function commitMessage(r: CommitResult): string {
14
14
  if (r.kind === "conflict") return "Goal update conflict: goal changed; retry with the current goal.";
15
15
  if (r.kind === "error") return `Goal update failed: ${r.error instanceof Error ? r.error.message : text(r.error)}`;
@@ -48,20 +48,21 @@ export function registerGoalTools(piLike: PiLike, deps: GoalToolDeps): void {
48
48
  });
49
49
  piLike.registerTool({
50
50
  name: "update_goal", label: "Update goal",
51
- description: "Update the existing goal.\nUse this tool only to mark the goal achieved or genuinely blocked.\nSet status to `complete` only when the objective has actually been achieved and no required work remains.\nSet status to `blocked` only when the same blocking condition has repeated for at least three consecutive goal turns, counting the original/user-triggered turn and any automatic continuations, and the agent cannot make meaningful progress without user input or an external-state change.\nIf the user resumes a goal that was previously marked `blocked`, treat the resumed run as a fresh blocked audit. If the same blocking condition then repeats for at least three consecutive resumed goal turns, set status to `blocked` again.\nOnce the blocked threshold is satisfied, do not keep reporting that you are still blocked while leaving the goal active; set status to `blocked`.\nDo not use `blocked` merely because the work is hard, slow, uncertain, incomplete, or would benefit from clarification.\nDo not mark a goal complete merely because its budget is nearly exhausted or because you are stopping work.\nYou cannot use this tool to pause, resume, budget-limit, or usage-limit a goal; those status changes are controlled by the user or system.\nWhen marking a budgeted goal achieved with status `complete`, report the final token usage from the tool result to the user.",
52
- parameters: Type.Object({ status: Type.Union([Type.Literal("complete"), Type.Literal("blocked")], { description: "Required. Set to `complete` only when the objective is achieved and no required work remains. Set to `blocked` only after the same blocking condition has recurred for at least three consecutive goal turns and the agent is at an impasse. After a previously blocked goal is resumed, the resumed run starts a fresh blocked audit." }) }),
51
+ description: "Update the existing goal.\nSet status to `paused` only at the user's explicit request to pause this goal, never on your own initiative. Ask if unclear; a later resume revokes that request. Report the returned status and stop goal work. Budget limits take precedence over pausing.\nSet status to `complete` only when the objective has actually been achieved and no required work remains.\nSet status to `blocked` only when the same blocking condition has repeated for at least three consecutive goal turns, counting the original/user-triggered turn and any automatic continuations, and the agent cannot make meaningful progress without user input or an external-state change.\nIf the user resumes a goal that was previously marked `blocked`, treat the resumed run as a fresh blocked audit. If the same blocking condition then repeats for at least three consecutive resumed goal turns, set status to `blocked` again.\nOnce the blocked threshold is satisfied, do not keep reporting that you are still blocked while leaving the goal active; set status to `blocked`.\nDo not use `blocked` merely because the work is hard, slow, uncertain, incomplete, or would benefit from clarification.\nDo not mark a goal complete merely because its budget is nearly exhausted or because you are stopping work.\nYou cannot use this tool to resume, budget-limit, or usage-limit a goal; those status changes are controlled by the user or system.\nWhen marking a budgeted goal achieved with status `complete`, report the final token usage from the tool result to the user.",
52
+ parameters: Type.Object({ status: Type.Union([Type.Literal("complete"), Type.Literal("blocked"), Type.Literal("paused")], { description: "Required. `paused` requires an explicit user request. Set to `complete` only when the objective is achieved and no required work remains. Set to `blocked` only after the same blocking condition has recurred for at least three consecutive goal turns and the agent is at an impasse. After a previously blocked goal is resumed, the resumed run starts a fresh blocked audit." }) }),
53
53
  execute: async (_id: string, params: any) => {
54
54
  const status = params?.status as Status;
55
- const expected = status === "complete" || status === "blocked";
55
+ const expected = status === "complete" || status === "blocked" || status === "paused";
56
56
  const current = goalCommit.current();
57
57
  if (!current) return result("Goal update failed: no goal exists.");
58
58
  if (!expected) return result(`Invalid status: ${text(params?.status)} is not complete or blocked.`);
59
59
  const r = await goalCommit.commit({ type: "transition", to: status, by: "agent" }, current.revision);
60
- if (r.kind !== "ok") return result(commitMessage(r), expected);
61
- if (status === "blocked") return result("Goal marked blocked.", true);
60
+ if (r.kind !== "ok") return result(commitMessage(r));
61
+ if (status === "blocked") return result("Goal marked blocked.");
62
+ if (status === "paused") return result("Goal marked paused.");
62
63
  const u = (r.snapshot?.goal ?? current.goal).usage;
63
64
  const total = u.input + u.output + u.cacheRead + u.cacheWrite;
64
- return result(`Goal marked complete. Final token usage: input=${u.input} output=${u.output} cacheRead=${u.cacheRead} cacheWrite=${u.cacheWrite} (total=${total}).`, true);
65
+ return result(`Goal marked complete. Final token usage: input=${u.input} output=${u.output} cacheRead=${u.cacheRead} cacheWrite=${u.cacheWrite} (total=${total}).`);
65
66
  }
66
67
  });
67
68
  }