@pinet/agent-goal 0.2.6 → 0.2.7
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 +2 -2
- package/dist/dashboard.d.ts +1 -0
- package/dist/dashboard.js +4 -4
- package/dist/goal-window.d.ts +14 -0
- package/dist/goal-window.js +101 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +19 -1
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -20,7 +20,7 @@ pi -e ./agent-goal/index.ts
|
|
|
20
20
|
|
|
21
21
|
```text
|
|
22
22
|
/goal <objective> Create and immediately start a goal
|
|
23
|
-
/goal
|
|
23
|
+
/goal Open the minimal goal window (text in headless modes)
|
|
24
24
|
/goal pause Pause automatic evaluation and continuation
|
|
25
25
|
/goal resume Resume and immediately continue
|
|
26
26
|
/goal complete Mark complete manually
|
|
@@ -29,7 +29,7 @@ pi -e ./agent-goal/index.ts
|
|
|
29
29
|
/goal show Show the persistent goal dashboard
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
-
Pi's footer shows the status and budget usage. A compact widget below the editor shows the objective,
|
|
32
|
+
Pi's footer shows the status and budget usage. A compact widget below the editor shows passive progress. In interactive mode, `/goal` opens a centered, keyboard-dismissable window with the objective, lifecycle state, budget bars, latest evaluator guidance, and continuation state. Press Escape, Enter, `q`, or Ctrl+C to close it. `/goal` remains a textual fallback in headless sessions.
|
|
33
33
|
|
|
34
34
|
Only one goal may exist per Pi session. Clear the existing goal before creating another.
|
|
35
35
|
|
package/dist/dashboard.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import type { AgentGoal, GoalContinuationClaim } from "./domain.js";
|
|
2
|
+
export declare function displayGoalText(value: string, maxLength: number): string;
|
|
2
3
|
export declare function formatGoalStatus(goal: AgentGoal): string;
|
|
3
4
|
export declare function formatGoalDashboard(goal: AgentGoal, claim?: GoalContinuationClaim): string[];
|
package/dist/dashboard.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
function
|
|
1
|
+
export function displayGoalText(value, maxLength) {
|
|
2
2
|
const normalized = value
|
|
3
3
|
.replaceAll("\n", " ")
|
|
4
4
|
.replaceAll("\r", " ")
|
|
@@ -27,14 +27,14 @@ export function formatGoalDashboard(goal, claim) {
|
|
|
27
27
|
.join(" · ");
|
|
28
28
|
const lines = [
|
|
29
29
|
`Goal · ${goal.status} · v${goal.version}`,
|
|
30
|
-
|
|
30
|
+
displayGoalText(goal.objective, 120),
|
|
31
31
|
usage,
|
|
32
32
|
];
|
|
33
33
|
if (goal.lastEvaluation) {
|
|
34
|
-
lines.push(`Last ${goal.lastEvaluation.outcome.toUpperCase()}: ${
|
|
34
|
+
lines.push(`Last ${goal.lastEvaluation.outcome.toUpperCase()}: ${displayGoalText(goal.lastEvaluation.reason, 100)}`);
|
|
35
35
|
}
|
|
36
36
|
if (goal.blockedReason)
|
|
37
|
-
lines.push(`Reason: ${
|
|
37
|
+
lines.push(`Reason: ${displayGoalText(goal.blockedReason, 100)}`);
|
|
38
38
|
if (claim)
|
|
39
39
|
lines.push(`Continuation: ${claim.state} · attempt ${claim.attempt}`);
|
|
40
40
|
lines.push("/goal pause · resume · complete · clear · hide");
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Theme } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { type Component } from "@earendil-works/pi-tui";
|
|
3
|
+
import type { AgentGoal, GoalContinuationClaim } from "./domain.js";
|
|
4
|
+
export declare class GoalWindow implements Component {
|
|
5
|
+
private readonly goal;
|
|
6
|
+
private readonly claim;
|
|
7
|
+
private readonly theme;
|
|
8
|
+
private readonly onClose;
|
|
9
|
+
private readonly now;
|
|
10
|
+
constructor(goal: AgentGoal | undefined, claim: GoalContinuationClaim | undefined, theme: Theme, onClose: () => void, now?: () => number);
|
|
11
|
+
handleInput(data: string): void;
|
|
12
|
+
render(width: number): string[];
|
|
13
|
+
invalidate(): void;
|
|
14
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { matchesKey, truncateToWidth, visibleWidth, wrapTextWithAnsi, } from "@earendil-works/pi-tui";
|
|
2
|
+
import { displayGoalText } from "./dashboard.js";
|
|
3
|
+
function progressBar(value, maximum, width) {
|
|
4
|
+
const ratio = Math.min(1, Math.max(0, value / maximum));
|
|
5
|
+
const filled = Math.round(ratio * width);
|
|
6
|
+
return `${"━".repeat(filled)}${"─".repeat(width - filled)}`;
|
|
7
|
+
}
|
|
8
|
+
function compactNumber(value) {
|
|
9
|
+
if (value < 1_000)
|
|
10
|
+
return String(value);
|
|
11
|
+
const divisor = value < 1_000_000 ? 1_000 : 1_000_000;
|
|
12
|
+
const suffix = value < 1_000_000 ? "k" : "m";
|
|
13
|
+
const scaled = value / divisor;
|
|
14
|
+
return `${scaled.toFixed(scaled < 100 ? 1 : 0).replace(/\.0$/, "")}${suffix}`;
|
|
15
|
+
}
|
|
16
|
+
export class GoalWindow {
|
|
17
|
+
goal;
|
|
18
|
+
claim;
|
|
19
|
+
theme;
|
|
20
|
+
onClose;
|
|
21
|
+
now;
|
|
22
|
+
constructor(goal, claim, theme, onClose, now = Date.now) {
|
|
23
|
+
this.goal = goal;
|
|
24
|
+
this.claim = claim;
|
|
25
|
+
this.theme = theme;
|
|
26
|
+
this.onClose = onClose;
|
|
27
|
+
this.now = now;
|
|
28
|
+
}
|
|
29
|
+
handleInput(data) {
|
|
30
|
+
if (matchesKey(data, "escape") ||
|
|
31
|
+
matchesKey(data, "ctrl+c") ||
|
|
32
|
+
matchesKey(data, "return") ||
|
|
33
|
+
data.toLowerCase() === "q") {
|
|
34
|
+
this.onClose();
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
render(width) {
|
|
38
|
+
if (width < 8)
|
|
39
|
+
return [truncateToWidth("Goal", Math.max(0, width), "")];
|
|
40
|
+
const innerWidth = width - 2;
|
|
41
|
+
const contentWidth = Math.max(1, innerWidth - 2);
|
|
42
|
+
const borderColor = this.goal?.status === "complete" ? "success" : "borderAccent";
|
|
43
|
+
const border = (text) => this.theme.fg(borderColor, text);
|
|
44
|
+
const row = (content = "") => {
|
|
45
|
+
const truncated = truncateToWidth(content, innerWidth, "", true);
|
|
46
|
+
return `${border("│")}${truncated}${" ".repeat(Math.max(0, innerWidth - visibleWidth(truncated)))}${border("│")}`;
|
|
47
|
+
};
|
|
48
|
+
const title = this.theme.fg("accent", this.theme.bold(" Goal "));
|
|
49
|
+
const titleWidth = visibleWidth(title);
|
|
50
|
+
const lines = [
|
|
51
|
+
`${border("╭")}${title}${border(`${"─".repeat(Math.max(0, innerWidth - titleWidth))}╮`)}`,
|
|
52
|
+
];
|
|
53
|
+
if (!this.goal) {
|
|
54
|
+
lines.push(row(), row(` ${this.theme.fg("muted", "No goal for this session.")}`));
|
|
55
|
+
lines.push(row(` ${this.theme.fg("dim", "/goal <objective> to begin")}`), row());
|
|
56
|
+
lines.push(row(` ${this.theme.fg("dim", "esc · enter · q close")}`));
|
|
57
|
+
lines.push(border(`╰${"─".repeat(innerWidth)}╯`));
|
|
58
|
+
return lines;
|
|
59
|
+
}
|
|
60
|
+
const statusColor = this.goal.status === "complete"
|
|
61
|
+
? "success"
|
|
62
|
+
: this.goal.status === "blocked"
|
|
63
|
+
? "error"
|
|
64
|
+
: this.goal.status === "active"
|
|
65
|
+
? "accent"
|
|
66
|
+
: "warning";
|
|
67
|
+
lines.push(row(` ${this.theme.fg(statusColor, `● ${this.goal.status.toUpperCase()}`)}`));
|
|
68
|
+
const objectiveLines = wrapTextWithAnsi(displayGoalText(this.goal.objective, 500), contentWidth).slice(0, 3);
|
|
69
|
+
for (const objectiveLine of objectiveLines)
|
|
70
|
+
lines.push(row(` ${objectiveLine}`));
|
|
71
|
+
lines.push(row());
|
|
72
|
+
const turnBar = progressBar(this.goal.usage.iterations, this.goal.budget.maxIterations, Math.min(12, Math.max(4, contentWidth - 23)));
|
|
73
|
+
lines.push(row(` Turns ${this.theme.fg("accent", turnBar)} ${this.goal.usage.iterations}/${this.goal.budget.maxIterations}`));
|
|
74
|
+
if (this.goal.budget.maxTokens === undefined) {
|
|
75
|
+
lines.push(row(` Tokens ${compactNumber(this.goal.usage.tokens)}`));
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
const tokenBar = progressBar(this.goal.usage.tokens, this.goal.budget.maxTokens, Math.min(12, Math.max(4, contentWidth - 23)));
|
|
79
|
+
lines.push(row(` Tokens ${this.theme.fg("accent", tokenBar)} ${compactNumber(this.goal.usage.tokens)}/${compactNumber(this.goal.budget.maxTokens)}`));
|
|
80
|
+
}
|
|
81
|
+
if (this.goal.budget.maxRuntimeMs !== undefined) {
|
|
82
|
+
const end = this.goal.status === "active" ? this.now() : Date.parse(this.goal.updatedAt);
|
|
83
|
+
const elapsed = Math.max(0, end - Date.parse(this.goal.createdAt));
|
|
84
|
+
const runtimeBar = progressBar(elapsed, this.goal.budget.maxRuntimeMs, Math.min(12, Math.max(4, contentWidth - 23)));
|
|
85
|
+
lines.push(row(` Time ${this.theme.fg("accent", runtimeBar)} ${Math.floor(elapsed / 60_000)}m/${Math.ceil(this.goal.budget.maxRuntimeMs / 60_000)}m`));
|
|
86
|
+
}
|
|
87
|
+
if (this.goal.lastEvaluation) {
|
|
88
|
+
lines.push(row(), row(` ${this.theme.fg("muted", "Latest")} ${displayGoalText(this.goal.lastEvaluation.reason, Math.max(20, contentWidth - 8))}`));
|
|
89
|
+
}
|
|
90
|
+
else if (this.goal.blockedReason) {
|
|
91
|
+
lines.push(row(), row(` ${this.theme.fg("muted", "Reason")} ${displayGoalText(this.goal.blockedReason, Math.max(20, contentWidth - 8))}`));
|
|
92
|
+
}
|
|
93
|
+
if (this.claim) {
|
|
94
|
+
lines.push(row(` ${this.theme.fg("muted", "Continuation")} ${this.claim.state} · attempt ${this.claim.attempt}`));
|
|
95
|
+
}
|
|
96
|
+
lines.push(row(), row(` ${this.theme.fg("dim", "esc · enter · q close")}`));
|
|
97
|
+
lines.push(border(`╰${"─".repeat(innerWidth)}╯`));
|
|
98
|
+
return lines;
|
|
99
|
+
}
|
|
100
|
+
invalidate() { }
|
|
101
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
import type { GoalBudget, GoalContinuation, GoalEvaluator, GoalEventSink, GoalRetryPolicy, GoalStorage, GoalWakeScheduler } from "./domain.js";
|
|
3
3
|
export type { AgentGoal, GoalBudget, GoalContinuation, GoalContinuationClaim, GoalContinuationRequest, GoalContinuationResult, GoalEvaluation, GoalEvaluationRecord, GoalEvaluator, GoalEvent, GoalEventSink, GoalPendingEvaluation, GoalProgress, GoalRetryPolicy, GoalStatus, GoalStorage, GoalTerminalCandidate, GoalTerminalCandidateRecord, GoalUsage, GoalWakeScheduler, } from "./domain.js";
|
|
4
|
-
export { formatGoalDashboard, formatGoalStatus } from "./dashboard.js";
|
|
4
|
+
export { displayGoalText, formatGoalDashboard, formatGoalStatus } from "./dashboard.js";
|
|
5
|
+
export { GoalWindow } from "./goal-window.js";
|
|
5
6
|
export { MemoryGoalStorage } from "./memory-storage.js";
|
|
6
7
|
export { parseGoalEvaluation, PiGoalEvaluator } from "./pi-evaluator.js";
|
|
7
8
|
export { countGoalProgressTokens, formatGoalProgress, type GoalProgressMessage, } from "./progress.js";
|
package/dist/index.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { homedir } from "node:os";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { formatGoalDashboard, formatGoalStatus } from "./dashboard.js";
|
|
4
|
+
import { GoalWindow } from "./goal-window.js";
|
|
4
5
|
import { PiGoalEvaluator } from "./pi-evaluator.js";
|
|
5
6
|
import { countGoalProgressTokens, formatGoalProgress, } from "./progress.js";
|
|
6
7
|
import { GoalRuntime } from "./runtime.js";
|
|
7
8
|
import { SqliteGoalStorage } from "./sqlite-storage.js";
|
|
8
|
-
export { formatGoalDashboard, formatGoalStatus } from "./dashboard.js";
|
|
9
|
+
export { displayGoalText, formatGoalDashboard, formatGoalStatus } from "./dashboard.js";
|
|
10
|
+
export { GoalWindow } from "./goal-window.js";
|
|
9
11
|
export { MemoryGoalStorage } from "./memory-storage.js";
|
|
10
12
|
export { parseGoalEvaluation, PiGoalEvaluator } from "./pi-evaluator.js";
|
|
11
13
|
export { countGoalProgressTokens, formatGoalProgress, } from "./progress.js";
|
|
@@ -283,6 +285,22 @@ export function registerAgentGoal(pi, options = {}) {
|
|
|
283
285
|
if (!input) {
|
|
284
286
|
const goal = await runtime.get(scopeId);
|
|
285
287
|
const claim = await runtime.getContinuationClaim(scopeId);
|
|
288
|
+
let openedWindow = false;
|
|
289
|
+
await ctx.ui.custom((_tui, theme, _keybindings, done) => {
|
|
290
|
+
openedWindow = true;
|
|
291
|
+
return new GoalWindow(goal, claim, theme, () => done(undefined));
|
|
292
|
+
}, {
|
|
293
|
+
overlay: true,
|
|
294
|
+
overlayOptions: {
|
|
295
|
+
anchor: "center",
|
|
296
|
+
width: 66,
|
|
297
|
+
minWidth: 36,
|
|
298
|
+
maxHeight: "80%",
|
|
299
|
+
margin: 1,
|
|
300
|
+
},
|
|
301
|
+
});
|
|
302
|
+
if (openedWindow)
|
|
303
|
+
return;
|
|
286
304
|
api.sendMessage({
|
|
287
305
|
customType: "agent-goal.status",
|
|
288
306
|
content: goal
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pinet/agent-goal",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.7",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Standalone single-agent durable goal loop for Pi",
|
|
6
6
|
"author": "Will Porcellini <5994936+gugu91@users.noreply.github.com>",
|
|
@@ -48,6 +48,7 @@
|
|
|
48
48
|
"dependencies": {},
|
|
49
49
|
"peerDependencies": {
|
|
50
50
|
"@earendil-works/pi-ai": ">=0.74.0",
|
|
51
|
-
"@earendil-works/pi-coding-agent": ">=0.74.0"
|
|
51
|
+
"@earendil-works/pi-coding-agent": ">=0.74.0",
|
|
52
|
+
"@earendil-works/pi-tui": ">=0.74.0"
|
|
52
53
|
}
|
|
53
54
|
}
|