@pinet/agent-goal 0.2.8 → 0.2.9
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 +3 -3
- package/dist/goal-window.d.ts +5 -2
- package/dist/goal-window.js +59 -10
- package/dist/index.d.ts +1 -1
- package/dist/index.js +54 -41
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -25,11 +25,11 @@ pi -e ./agent-goal/index.ts
|
|
|
25
25
|
/goal resume Resume and immediately continue
|
|
26
26
|
/goal complete Mark complete manually
|
|
27
27
|
/goal clear Delete the goal
|
|
28
|
-
/goal hide Hide the persistent goal
|
|
29
|
-
/goal show Show the persistent goal
|
|
28
|
+
/goal hide Hide the compact persistent goal status
|
|
29
|
+
/goal show Show the compact persistent goal status
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
-
Pi's footer
|
|
32
|
+
Pi's footer is the only persistent goal UI and shows compact status and budget usage without duplicating the detailed dashboard. In interactive mode, `/goal` opens the detailed control window with the objective, lifecycle state, budget bars, latest evaluator guidance, and continuation state. Its visible keyboard actions pause or resume the goal, mark it complete, clear it, or close the window; complete and clear require a second keypress, and the window refreshes after state changes. Press Escape, `q`, or Ctrl+C to close it. `/goal` retains the detailed textual dashboard 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/goal-window.d.ts
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import type { Theme } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
import { type Component } from "@earendil-works/pi-tui";
|
|
3
3
|
import type { AgentGoal, GoalContinuationClaim } from "./domain.js";
|
|
4
|
+
export type GoalWindowAction = "pause" | "resume" | "complete" | "clear" | "close";
|
|
4
5
|
export declare class GoalWindow implements Component {
|
|
5
6
|
private readonly goal;
|
|
6
7
|
private readonly claim;
|
|
7
8
|
private readonly theme;
|
|
8
|
-
private readonly
|
|
9
|
+
private readonly onAction;
|
|
10
|
+
private readonly requestRender;
|
|
9
11
|
private readonly now;
|
|
10
|
-
|
|
12
|
+
private pendingConfirmation;
|
|
13
|
+
constructor(goal: AgentGoal | undefined, claim: GoalContinuationClaim | undefined, theme: Theme, onAction: (action: GoalWindowAction) => void, requestRender?: () => void, now?: () => number);
|
|
11
14
|
handleInput(data: string): void;
|
|
12
15
|
render(width: number): string[];
|
|
13
16
|
invalidate(): void;
|
package/dist/goal-window.js
CHANGED
|
@@ -17,21 +17,60 @@ export class GoalWindow {
|
|
|
17
17
|
goal;
|
|
18
18
|
claim;
|
|
19
19
|
theme;
|
|
20
|
-
|
|
20
|
+
onAction;
|
|
21
|
+
requestRender;
|
|
21
22
|
now;
|
|
22
|
-
|
|
23
|
+
pendingConfirmation;
|
|
24
|
+
constructor(goal, claim, theme, onAction, requestRender = () => undefined, now = Date.now) {
|
|
23
25
|
this.goal = goal;
|
|
24
26
|
this.claim = claim;
|
|
25
27
|
this.theme = theme;
|
|
26
|
-
this.
|
|
28
|
+
this.onAction = onAction;
|
|
29
|
+
this.requestRender = requestRender;
|
|
27
30
|
this.now = now;
|
|
28
31
|
}
|
|
29
32
|
handleInput(data) {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
const key = data.toLowerCase();
|
|
34
|
+
if (matchesKey(data, "ctrl+c") || key === "q") {
|
|
35
|
+
this.onAction("close");
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
if (matchesKey(data, "escape")) {
|
|
39
|
+
if (this.pendingConfirmation) {
|
|
40
|
+
this.pendingConfirmation = undefined;
|
|
41
|
+
this.requestRender();
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
this.onAction("close");
|
|
45
|
+
}
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
if (!this.goal)
|
|
49
|
+
return;
|
|
50
|
+
if (this.pendingConfirmation) {
|
|
51
|
+
const confirmationKey = this.pendingConfirmation === "complete" ? "c" : "x";
|
|
52
|
+
if (key === confirmationKey) {
|
|
53
|
+
this.onAction(this.pendingConfirmation);
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
this.pendingConfirmation = undefined;
|
|
57
|
+
this.requestRender();
|
|
58
|
+
}
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
if (key === "p" && this.goal.status === "active") {
|
|
62
|
+
this.onAction("pause");
|
|
63
|
+
}
|
|
64
|
+
else if (key === "r" && (this.goal.status === "paused" || this.goal.status === "blocked")) {
|
|
65
|
+
this.onAction("resume");
|
|
66
|
+
}
|
|
67
|
+
else if (key === "c" && this.goal.status !== "complete") {
|
|
68
|
+
this.pendingConfirmation = "complete";
|
|
69
|
+
this.requestRender();
|
|
70
|
+
}
|
|
71
|
+
else if (key === "x") {
|
|
72
|
+
this.pendingConfirmation = "clear";
|
|
73
|
+
this.requestRender();
|
|
35
74
|
}
|
|
36
75
|
}
|
|
37
76
|
render(width) {
|
|
@@ -53,7 +92,7 @@ export class GoalWindow {
|
|
|
53
92
|
if (!this.goal) {
|
|
54
93
|
lines.push(row(), row(` ${this.theme.fg("muted", "No goal for this session.")}`));
|
|
55
94
|
lines.push(row(` ${this.theme.fg("dim", "/goal <objective> to begin")}`), row());
|
|
56
|
-
lines.push(row(` ${this.theme.fg("dim", "esc ·
|
|
95
|
+
lines.push(row(` ${this.theme.fg("dim", "esc · q close")}`));
|
|
57
96
|
lines.push(border(`╰${"─".repeat(innerWidth)}╯`));
|
|
58
97
|
return lines;
|
|
59
98
|
}
|
|
@@ -93,7 +132,17 @@ export class GoalWindow {
|
|
|
93
132
|
if (this.claim) {
|
|
94
133
|
lines.push(row(` ${this.theme.fg("muted", "Continuation")} ${this.claim.state} · attempt ${this.claim.attempt}`));
|
|
95
134
|
}
|
|
96
|
-
|
|
135
|
+
const actions = [
|
|
136
|
+
this.goal.status === "active" ? "p pause" : undefined,
|
|
137
|
+
this.goal.status === "paused" || this.goal.status === "blocked" ? "r resume" : undefined,
|
|
138
|
+
this.goal.status !== "complete" ? "c complete" : undefined,
|
|
139
|
+
"x clear",
|
|
140
|
+
"q close",
|
|
141
|
+
].filter((action) => action !== undefined);
|
|
142
|
+
const footer = this.pendingConfirmation
|
|
143
|
+
? `${this.pendingConfirmation === "complete" ? "c" : "x"} again to confirm ${this.pendingConfirmation} · esc cancel`
|
|
144
|
+
: actions.join(" · ");
|
|
145
|
+
lines.push(row(), row(` ${this.theme.fg("dim", footer)}`));
|
|
97
146
|
lines.push(border(`╰${"─".repeat(innerWidth)}╯`));
|
|
98
147
|
return lines;
|
|
99
148
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ 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
4
|
export { displayGoalText, formatGoalDashboard, formatGoalStatus } from "./dashboard.js";
|
|
5
|
-
export { GoalWindow } from "./goal-window.js";
|
|
5
|
+
export { GoalWindow, type GoalWindowAction } from "./goal-window.js";
|
|
6
6
|
export { MemoryGoalStorage } from "./memory-storage.js";
|
|
7
7
|
export { parseGoalEvaluation, PiGoalEvaluator } from "./pi-evaluator.js";
|
|
8
8
|
export { countGoalProgressTokens, formatGoalProgress, type GoalProgressMessage, } from "./progress.js";
|
package/dist/index.js
CHANGED
|
@@ -73,13 +73,27 @@ export function registerAgentGoal(pi, options = {}) {
|
|
|
73
73
|
const refreshUi = async (ctx) => {
|
|
74
74
|
const scopeId = ctx.sessionManager.getSessionId();
|
|
75
75
|
const goal = await runtime.get(scopeId);
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
76
|
+
const hidden = hiddenScopes.has(scopeId);
|
|
77
|
+
ctx.ui.setStatus(STATUS_KEY, goal && !hidden ? formatGoalStatus(goal) : undefined);
|
|
78
|
+
ctx.ui.setWidget(WIDGET_KEY, undefined);
|
|
79
|
+
};
|
|
80
|
+
const applyGoalAction = async (scopeId, action) => {
|
|
81
|
+
switch (action) {
|
|
82
|
+
case "pause":
|
|
83
|
+
await runtime.setStatus(scopeId, "paused");
|
|
84
|
+
break;
|
|
85
|
+
case "resume":
|
|
86
|
+
await runtime.setStatus(scopeId, "active");
|
|
87
|
+
await runtime.start(scopeId, "Resume the goal from current state.");
|
|
88
|
+
break;
|
|
89
|
+
case "complete":
|
|
90
|
+
await runtime.setStatus(scopeId, "complete");
|
|
91
|
+
break;
|
|
92
|
+
case "clear":
|
|
93
|
+
if (!(await runtime.clear(scopeId)))
|
|
94
|
+
throw new Error("This session has no goal");
|
|
95
|
+
break;
|
|
80
96
|
}
|
|
81
|
-
const claim = await runtime.getContinuationClaim(scopeId);
|
|
82
|
-
ctx.ui.setWidget(WIDGET_KEY, formatGoalDashboard(goal, claim), { placement: "belowEditor" });
|
|
83
97
|
};
|
|
84
98
|
pi.on("session_start", async (_event, rawCtx) => {
|
|
85
99
|
const ctx = rawCtx;
|
|
@@ -277,47 +291,46 @@ export function registerAgentGoal(pi, options = {}) {
|
|
|
277
291
|
const input = args.trim();
|
|
278
292
|
try {
|
|
279
293
|
if (!input) {
|
|
280
|
-
const goal = await runtime.get(scopeId);
|
|
281
|
-
const claim = await runtime.getContinuationClaim(scopeId);
|
|
282
294
|
let openedWindow = false;
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
295
|
+
while (true) {
|
|
296
|
+
const goal = await runtime.get(scopeId);
|
|
297
|
+
const claim = await runtime.getContinuationClaim(scopeId);
|
|
298
|
+
const action = await ctx.ui.custom((tui, theme, _keybindings, done) => {
|
|
299
|
+
openedWindow = true;
|
|
300
|
+
return new GoalWindow(goal, claim, theme, done, () => tui.requestRender());
|
|
301
|
+
}, {
|
|
302
|
+
overlay: true,
|
|
303
|
+
overlayOptions: {
|
|
304
|
+
anchor: "center",
|
|
305
|
+
width: 66,
|
|
306
|
+
minWidth: 36,
|
|
307
|
+
maxHeight: "80%",
|
|
308
|
+
margin: 1,
|
|
309
|
+
},
|
|
310
|
+
});
|
|
311
|
+
if (!openedWindow) {
|
|
312
|
+
api.sendMessage({
|
|
313
|
+
customType: "agent-goal.status",
|
|
314
|
+
content: goal
|
|
315
|
+
? formatGoalDashboard(goal, claim).join("\n")
|
|
316
|
+
: "This session has no goal.",
|
|
317
|
+
display: true,
|
|
318
|
+
}, { triggerTurn: false });
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
if (!action || action === "close")
|
|
322
|
+
return;
|
|
323
|
+
await applyGoalAction(scopeId, action);
|
|
324
|
+
await refreshUi(ctx);
|
|
325
|
+
}
|
|
306
326
|
}
|
|
307
|
-
|
|
327
|
+
const command = input.toLowerCase();
|
|
328
|
+
switch (command) {
|
|
308
329
|
case "pause":
|
|
309
|
-
await runtime.setStatus(scopeId, "paused");
|
|
310
|
-
break;
|
|
311
330
|
case "resume":
|
|
312
|
-
await runtime.setStatus(scopeId, "active");
|
|
313
|
-
await runtime.start(scopeId, "Resume the goal from current state.");
|
|
314
|
-
break;
|
|
315
331
|
case "complete":
|
|
316
|
-
await runtime.setStatus(scopeId, "complete");
|
|
317
|
-
break;
|
|
318
332
|
case "clear":
|
|
319
|
-
|
|
320
|
-
throw new Error("This session has no goal");
|
|
333
|
+
await applyGoalAction(scopeId, command);
|
|
321
334
|
break;
|
|
322
335
|
case "hide":
|
|
323
336
|
hiddenScopes.add(scopeId);
|