@pinet/agent-goal 0.2.13 → 0.2.14
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 +38 -84
- package/dist/dashboard.d.ts +5 -3
- package/dist/dashboard.js +42 -24
- package/dist/domain.d.ts +34 -1
- package/dist/goal-window.d.ts +43 -9
- package/dist/goal-window.js +300 -152
- package/dist/index.d.ts +1 -1
- package/dist/index.js +107 -100
- package/dist/memory-storage.d.ts +4 -1
- package/dist/memory-storage.js +21 -1
- package/dist/runtime.d.ts +15 -2
- package/dist/runtime.js +154 -13
- package/dist/sqlite-storage.d.ts +3 -1
- package/dist/sqlite-storage.js +70 -18
- package/package.json +1 -1
package/dist/goal-window.js
CHANGED
|
@@ -1,18 +1,5 @@
|
|
|
1
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
|
-
}
|
|
2
|
+
import { displayGoalText, formatElapsed, goalDisplayName } from "./dashboard.js";
|
|
16
3
|
export class GoalWindow {
|
|
17
4
|
goal;
|
|
18
5
|
claim;
|
|
@@ -21,13 +8,22 @@ export class GoalWindow {
|
|
|
21
8
|
requestRender;
|
|
22
9
|
now;
|
|
23
10
|
actionError;
|
|
24
|
-
|
|
25
|
-
|
|
11
|
+
checkpoints;
|
|
12
|
+
mode = "details";
|
|
13
|
+
textField = "name";
|
|
14
|
+
name = "";
|
|
15
|
+
objective = "";
|
|
16
|
+
initialName = "";
|
|
17
|
+
initialObjective = "";
|
|
18
|
+
budgetField = "turns";
|
|
26
19
|
budgetTurns = "";
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
20
|
+
budgetRuntime = "";
|
|
21
|
+
snoozeDuration = "30m";
|
|
22
|
+
inputError;
|
|
23
|
+
confirmClose = false;
|
|
24
|
+
showAllCheckpoints = false;
|
|
25
|
+
checkpointOffset = 0;
|
|
26
|
+
constructor(goal, claim, theme, onAction, requestRender = () => undefined, now = Date.now, actionError, checkpoints = []) {
|
|
31
27
|
this.goal = goal;
|
|
32
28
|
this.claim = claim;
|
|
33
29
|
this.theme = theme;
|
|
@@ -35,191 +31,343 @@ export class GoalWindow {
|
|
|
35
31
|
this.requestRender = requestRender;
|
|
36
32
|
this.now = now;
|
|
37
33
|
this.actionError = actionError;
|
|
34
|
+
this.checkpoints = checkpoints;
|
|
38
35
|
}
|
|
39
36
|
handleInput(data) {
|
|
40
37
|
const key = data.toLowerCase();
|
|
41
|
-
if (matchesKey(data, "ctrl+c") || key === "q") {
|
|
38
|
+
if (matchesKey(data, "ctrl+c") || (this.mode === "details" && key === "q")) {
|
|
42
39
|
this.onAction("close");
|
|
43
40
|
return;
|
|
44
41
|
}
|
|
45
42
|
if (matchesKey(data, "escape")) {
|
|
46
|
-
if (this.
|
|
47
|
-
this.
|
|
43
|
+
if (this.mode !== "details") {
|
|
44
|
+
this.mode = "details";
|
|
45
|
+
this.inputError = undefined;
|
|
48
46
|
this.requestRender();
|
|
49
47
|
}
|
|
50
|
-
else if (this.
|
|
51
|
-
this.
|
|
52
|
-
this.budgetInputError = undefined;
|
|
48
|
+
else if (this.confirmClose) {
|
|
49
|
+
this.confirmClose = false;
|
|
53
50
|
this.requestRender();
|
|
54
51
|
}
|
|
55
|
-
else
|
|
52
|
+
else
|
|
56
53
|
this.onAction("close");
|
|
57
|
-
}
|
|
58
54
|
return;
|
|
59
55
|
}
|
|
60
|
-
if (
|
|
56
|
+
if (this.mode === "create" || this.mode === "edit") {
|
|
57
|
+
this.handleTextForm(data);
|
|
61
58
|
return;
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
const maxIterations = Number(this.budgetTurns);
|
|
79
|
-
if (!Number.isInteger(maxIterations) || maxIterations <= 0) {
|
|
80
|
-
this.budgetInputError = "Turns must be a positive integer";
|
|
81
|
-
}
|
|
82
|
-
else {
|
|
83
|
-
const maxTokens = this.budgetTokens ? Number(this.budgetTokens) : undefined;
|
|
84
|
-
this.onAction({ type: "budget", maxIterations, maxTokens });
|
|
85
|
-
return;
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
else if (/^\d+$/.test(data)) {
|
|
89
|
-
const value = this.replaceBudgetValue
|
|
90
|
-
? data
|
|
91
|
-
: `${this.budgetField === "turns" ? this.budgetTurns : this.budgetTokens}${data}`;
|
|
92
|
-
if (this.budgetField === "turns")
|
|
93
|
-
this.budgetTurns = value;
|
|
94
|
-
else
|
|
95
|
-
this.budgetTokens = value;
|
|
96
|
-
this.replaceBudgetValue = false;
|
|
97
|
-
this.budgetInputError = undefined;
|
|
98
|
-
}
|
|
99
|
-
else {
|
|
100
|
-
return;
|
|
101
|
-
}
|
|
59
|
+
}
|
|
60
|
+
if (this.mode === "budget") {
|
|
61
|
+
this.handleBudgetForm(data);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
if (this.mode === "snooze") {
|
|
65
|
+
this.handleSnoozeForm(data);
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
if (!this.goal) {
|
|
69
|
+
if (key === "n" || matchesKey(data, "enter"))
|
|
70
|
+
this.openTextForm("create");
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
if (this.showAllCheckpoints && matchesKey(data, "down")) {
|
|
74
|
+
this.checkpointOffset = Math.min(Math.max(0, this.checkpoints.length - 1), this.checkpointOffset + 1);
|
|
102
75
|
this.requestRender();
|
|
103
76
|
return;
|
|
104
77
|
}
|
|
105
|
-
if (this.
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
78
|
+
if (this.showAllCheckpoints && matchesKey(data, "up")) {
|
|
79
|
+
this.checkpointOffset = Math.max(0, this.checkpointOffset - 1);
|
|
80
|
+
this.requestRender();
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
if (this.confirmClose) {
|
|
84
|
+
if (key === "x")
|
|
85
|
+
this.onAction("closeGoal");
|
|
110
86
|
else {
|
|
111
|
-
this.
|
|
87
|
+
this.confirmClose = false;
|
|
112
88
|
this.requestRender();
|
|
113
89
|
}
|
|
114
90
|
return;
|
|
115
91
|
}
|
|
116
|
-
if (key === "
|
|
117
|
-
this.
|
|
118
|
-
|
|
119
|
-
this.
|
|
120
|
-
|
|
121
|
-
this.
|
|
122
|
-
this.
|
|
92
|
+
if (key === "e")
|
|
93
|
+
this.openTextForm("edit");
|
|
94
|
+
else if (key === "b" && this.goal.status !== "complete")
|
|
95
|
+
this.openBudgetForm();
|
|
96
|
+
else if (key === "s" && this.goal.status !== "complete") {
|
|
97
|
+
this.mode = "snooze";
|
|
98
|
+
this.inputError = undefined;
|
|
99
|
+
this.requestRender();
|
|
100
|
+
}
|
|
101
|
+
else if (key === "h" && this.checkpoints.length > 3) {
|
|
102
|
+
this.showAllCheckpoints = !this.showAllCheckpoints;
|
|
103
|
+
this.checkpointOffset = 0;
|
|
104
|
+
this.requestRender();
|
|
105
|
+
}
|
|
106
|
+
else if (key === "x") {
|
|
107
|
+
this.confirmClose = true;
|
|
123
108
|
this.requestRender();
|
|
124
109
|
}
|
|
125
|
-
|
|
126
|
-
|
|
110
|
+
}
|
|
111
|
+
openTextForm(mode) {
|
|
112
|
+
this.mode = mode;
|
|
113
|
+
this.textField = "name";
|
|
114
|
+
this.name = mode === "edit" && this.goal ? (this.goal.name ?? this.goal.objective) : "";
|
|
115
|
+
this.objective = mode === "edit" && this.goal ? this.goal.objective : "";
|
|
116
|
+
this.initialName = this.name;
|
|
117
|
+
this.initialObjective = this.objective;
|
|
118
|
+
if (mode === "create") {
|
|
119
|
+
this.budgetTurns = "";
|
|
120
|
+
this.budgetRuntime = "";
|
|
121
|
+
}
|
|
122
|
+
this.inputError = undefined;
|
|
123
|
+
this.requestRender();
|
|
124
|
+
}
|
|
125
|
+
handleTextForm(data) {
|
|
126
|
+
if (matchesKey(data, "tab") || matchesKey(data, "down")) {
|
|
127
|
+
const fields = this.mode === "create" ? ["name", "objective", "turns", "runtime"] : ["name", "objective"];
|
|
128
|
+
this.textField = fields[(fields.indexOf(this.textField) + 1) % fields.length];
|
|
127
129
|
}
|
|
128
|
-
else if (
|
|
129
|
-
this.
|
|
130
|
+
else if (matchesKey(data, "up")) {
|
|
131
|
+
const fields = this.mode === "create" ? ["name", "objective", "turns", "runtime"] : ["name", "objective"];
|
|
132
|
+
this.textField =
|
|
133
|
+
fields[(fields.indexOf(this.textField) + fields.length - 1) % fields.length];
|
|
130
134
|
}
|
|
131
|
-
else if (
|
|
132
|
-
this.
|
|
135
|
+
else if (matchesKey(data, "backspace")) {
|
|
136
|
+
if (this.textField === "name")
|
|
137
|
+
this.name = this.name.slice(0, -1);
|
|
138
|
+
else if (this.textField === "objective")
|
|
139
|
+
this.objective = this.objective.slice(0, -1);
|
|
140
|
+
else if (this.textField === "turns")
|
|
141
|
+
this.budgetTurns = this.budgetTurns.slice(0, -1);
|
|
142
|
+
else
|
|
143
|
+
this.budgetRuntime = this.budgetRuntime.slice(0, -1);
|
|
144
|
+
}
|
|
145
|
+
else if (matchesKey(data, "enter")) {
|
|
146
|
+
const name = this.name.trim();
|
|
147
|
+
const objective = this.objective.trim();
|
|
148
|
+
const maxIterations = this.budgetTurns ? Number(this.budgetTurns) : undefined;
|
|
149
|
+
const maxRuntimeMs = this.budgetRuntime ? parseDuration(this.budgetRuntime) : undefined;
|
|
150
|
+
if (!name || !objective)
|
|
151
|
+
this.inputError = "Name and objective are required";
|
|
152
|
+
else if (maxIterations !== undefined &&
|
|
153
|
+
(!Number.isInteger(maxIterations) || maxIterations <= 0))
|
|
154
|
+
this.inputError = "Turns must be a positive integer";
|
|
155
|
+
else if (this.budgetRuntime && maxRuntimeMs === undefined)
|
|
156
|
+
this.inputError = "Runtime must use m, h, or d";
|
|
157
|
+
else if (this.mode === "create")
|
|
158
|
+
this.onAction({ type: "create", name, objective, maxIterations, maxRuntimeMs });
|
|
159
|
+
else {
|
|
160
|
+
const update = {
|
|
161
|
+
...(this.name === this.initialName ? {} : { name }),
|
|
162
|
+
...(this.objective === this.initialObjective ? {} : { objective }),
|
|
163
|
+
};
|
|
164
|
+
if (update.name === undefined && update.objective === undefined)
|
|
165
|
+
this.inputError = "Change the name or objective before saving";
|
|
166
|
+
else
|
|
167
|
+
this.onAction({ type: "edit", ...update });
|
|
168
|
+
}
|
|
133
169
|
this.requestRender();
|
|
170
|
+
return;
|
|
134
171
|
}
|
|
135
|
-
else if (
|
|
136
|
-
|
|
172
|
+
else if (data.length > 0 &&
|
|
173
|
+
Array.from(data).every((character) => {
|
|
174
|
+
const code = character.codePointAt(0) ?? 0;
|
|
175
|
+
return code > 31 && code !== 127;
|
|
176
|
+
})) {
|
|
177
|
+
if (this.textField === "name")
|
|
178
|
+
this.name += data;
|
|
179
|
+
else if (this.textField === "objective")
|
|
180
|
+
this.objective += data;
|
|
181
|
+
else if (this.textField === "turns" && /^\d+$/.test(data))
|
|
182
|
+
this.budgetTurns += data;
|
|
183
|
+
else if (this.textField === "runtime" && /^[0-9mhd]+$/i.test(data))
|
|
184
|
+
this.budgetRuntime += data;
|
|
185
|
+
else
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
else
|
|
189
|
+
return;
|
|
190
|
+
this.inputError = undefined;
|
|
191
|
+
this.requestRender();
|
|
192
|
+
}
|
|
193
|
+
openBudgetForm() {
|
|
194
|
+
this.mode = "budget";
|
|
195
|
+
this.budgetField = "turns";
|
|
196
|
+
this.budgetTurns = this.goal?.budget.maxIterations?.toString() ?? "";
|
|
197
|
+
this.budgetRuntime = this.goal?.budget.maxRuntimeMs
|
|
198
|
+
? `${Math.round(this.goal.budget.maxRuntimeMs / 60_000)}m`
|
|
199
|
+
: "";
|
|
200
|
+
this.inputError = undefined;
|
|
201
|
+
this.requestRender();
|
|
202
|
+
}
|
|
203
|
+
handleBudgetForm(data) {
|
|
204
|
+
if (data.toLowerCase() === "o") {
|
|
205
|
+
this.onAction({ type: "budget", disabled: true });
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
if (matchesKey(data, "tab") || matchesKey(data, "up") || matchesKey(data, "down")) {
|
|
209
|
+
this.budgetField = this.budgetField === "turns" ? "runtime" : "turns";
|
|
210
|
+
}
|
|
211
|
+
else if (matchesKey(data, "backspace")) {
|
|
212
|
+
if (this.budgetField === "turns")
|
|
213
|
+
this.budgetTurns = this.budgetTurns.slice(0, -1);
|
|
214
|
+
else
|
|
215
|
+
this.budgetRuntime = this.budgetRuntime.slice(0, -1);
|
|
216
|
+
}
|
|
217
|
+
else if (matchesKey(data, "enter")) {
|
|
218
|
+
const maxIterations = this.budgetTurns ? Number(this.budgetTurns) : undefined;
|
|
219
|
+
const maxRuntimeMs = this.budgetRuntime ? parseDuration(this.budgetRuntime) : undefined;
|
|
220
|
+
if (maxIterations !== undefined && (!Number.isInteger(maxIterations) || maxIterations <= 0))
|
|
221
|
+
this.inputError = "Turns must be a positive integer";
|
|
222
|
+
else if (this.budgetRuntime && maxRuntimeMs === undefined)
|
|
223
|
+
this.inputError = "Runtime must use m, h, or d (for example 2h)";
|
|
224
|
+
else if (maxIterations === undefined && maxRuntimeMs === undefined)
|
|
225
|
+
this.inputError = "Set a limit or press o to turn limits off";
|
|
226
|
+
else
|
|
227
|
+
this.onAction({ type: "budget", maxIterations, maxRuntimeMs });
|
|
228
|
+
this.requestRender();
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
else if (/^[0-9mhd]+$/i.test(data)) {
|
|
232
|
+
if (this.budgetField === "turns" && /^\d+$/.test(data))
|
|
233
|
+
this.budgetTurns += data;
|
|
234
|
+
else if (this.budgetField === "runtime")
|
|
235
|
+
this.budgetRuntime += data;
|
|
236
|
+
else
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
else
|
|
240
|
+
return;
|
|
241
|
+
this.inputError = undefined;
|
|
242
|
+
this.requestRender();
|
|
243
|
+
}
|
|
244
|
+
handleSnoozeForm(data) {
|
|
245
|
+
if (matchesKey(data, "backspace"))
|
|
246
|
+
this.snoozeDuration = this.snoozeDuration.slice(0, -1);
|
|
247
|
+
else if (matchesKey(data, "enter")) {
|
|
248
|
+
const durationMs = parseDuration(this.snoozeDuration);
|
|
249
|
+
if (durationMs === undefined)
|
|
250
|
+
this.inputError = "Duration must use m, h, or d";
|
|
251
|
+
else
|
|
252
|
+
this.onAction({ type: "snooze", durationMs });
|
|
137
253
|
this.requestRender();
|
|
254
|
+
return;
|
|
138
255
|
}
|
|
256
|
+
else if (/^[0-9mhd]+$/i.test(data))
|
|
257
|
+
this.snoozeDuration += data;
|
|
258
|
+
else
|
|
259
|
+
return;
|
|
260
|
+
this.inputError = undefined;
|
|
261
|
+
this.requestRender();
|
|
139
262
|
}
|
|
140
263
|
render(width) {
|
|
141
264
|
if (width < 8)
|
|
142
265
|
return [truncateToWidth("Goal", Math.max(0, width), "")];
|
|
143
266
|
const innerWidth = width - 2;
|
|
144
267
|
const contentWidth = Math.max(1, innerWidth - 2);
|
|
145
|
-
const
|
|
146
|
-
const border = (text) => this.theme.fg(borderColor, text);
|
|
268
|
+
const border = (text) => this.theme.fg("borderAccent", text);
|
|
147
269
|
const row = (content = "") => {
|
|
148
270
|
const truncated = truncateToWidth(content, innerWidth, "", true);
|
|
149
271
|
return `${border("│")}${truncated}${" ".repeat(Math.max(0, innerWidth - visibleWidth(truncated)))}${border("│")}`;
|
|
150
272
|
};
|
|
151
|
-
const title = this.theme.fg("accent", this.theme.bold(
|
|
152
|
-
const titleWidth = visibleWidth(title);
|
|
273
|
+
const title = this.theme.fg("accent", this.theme.bold(` Goal${this.mode === "details" ? "" : ` · ${this.mode}`} `));
|
|
153
274
|
const lines = [
|
|
154
|
-
`${border("╭")}${title}${border(`${"─".repeat(Math.max(0, innerWidth -
|
|
275
|
+
`${border("╭")}${title}${border(`${"─".repeat(Math.max(0, innerWidth - visibleWidth(title)))}╮`)}`,
|
|
155
276
|
];
|
|
156
|
-
if (!this.goal) {
|
|
277
|
+
if (!this.goal && this.mode === "details") {
|
|
157
278
|
lines.push(row(), row(` ${this.theme.fg("muted", "No goal for this session.")}`));
|
|
158
|
-
lines.push(row(` ${this.theme.fg("dim", "
|
|
159
|
-
lines.push(row(` ${this.theme.fg("dim", "esc · q close")}`));
|
|
279
|
+
lines.push(row(` ${this.theme.fg("dim", "n · enter create · q close")}`));
|
|
160
280
|
lines.push(border(`╰${"─".repeat(innerWidth)}╯`));
|
|
161
281
|
return lines;
|
|
162
282
|
}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
? "
|
|
169
|
-
: "
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
lines.push(row(` Turns ${this.theme.fg("accent", turnBar)} ${this.goal.usage.iterations}/${this.goal.budget.maxIterations}`));
|
|
177
|
-
if (this.goal.budget.maxTokens === undefined) {
|
|
178
|
-
lines.push(row(` Tokens ${compactNumber(this.goal.usage.tokens)}`));
|
|
179
|
-
}
|
|
180
|
-
else {
|
|
181
|
-
const tokenBar = progressBar(this.goal.usage.tokens, this.goal.budget.maxTokens, Math.min(12, Math.max(4, contentWidth - 23)));
|
|
182
|
-
lines.push(row(` Tokens ${this.theme.fg("accent", tokenBar)} ${compactNumber(this.goal.usage.tokens)}/${compactNumber(this.goal.budget.maxTokens)}`));
|
|
183
|
-
}
|
|
184
|
-
if (this.goal.budget.maxRuntimeMs !== undefined) {
|
|
185
|
-
const end = this.goal.status === "active" ? this.now() : Date.parse(this.goal.updatedAt);
|
|
186
|
-
const elapsed = Math.max(0, end - Date.parse(this.goal.createdAt));
|
|
187
|
-
const runtimeBar = progressBar(elapsed, this.goal.budget.maxRuntimeMs, Math.min(12, Math.max(4, contentWidth - 23)));
|
|
188
|
-
lines.push(row(` Time ${this.theme.fg("accent", runtimeBar)} ${Math.floor(elapsed / 60_000)}m/${Math.ceil(this.goal.budget.maxRuntimeMs / 60_000)}m`));
|
|
189
|
-
}
|
|
190
|
-
if (this.goal.lastEvaluation) {
|
|
191
|
-
lines.push(row(), row(` ${this.theme.fg("muted", "Latest")} ${displayGoalText(this.goal.lastEvaluation.reason, Math.max(20, contentWidth - 8))}`));
|
|
283
|
+
if (this.mode === "create" || this.mode === "edit") {
|
|
284
|
+
const displayedName = displayGoalText(this.name, 500);
|
|
285
|
+
const displayedObjective = displayGoalText(this.objective, 500);
|
|
286
|
+
lines.push(row(` ${this.textField === "name" ? "›" : " "} Name ${displayedName || "_"}`), row(` ${this.textField === "objective" ? "›" : " "} Objective ${displayedObjective || "_"}`), ...(this.mode === "create"
|
|
287
|
+
? [
|
|
288
|
+
row(` ${this.textField === "turns" ? "›" : " "} Turns ${this.budgetTurns || "off"}`),
|
|
289
|
+
row(` ${this.textField === "runtime" ? "›" : " "} Runtime ${this.budgetRuntime || "off"}`),
|
|
290
|
+
]
|
|
291
|
+
: []), row(), row(` ${this.theme.fg("dim", "tab field · enter save · esc cancel")}`));
|
|
292
|
+
if (this.mode === "edit")
|
|
293
|
+
lines.push(row(` ${this.theme.fg("warning", "Objective applies on the next continuation.")}`));
|
|
294
|
+
this.finish(lines, row, border, innerWidth);
|
|
295
|
+
return lines;
|
|
192
296
|
}
|
|
193
|
-
|
|
194
|
-
lines.push(row(), row(` ${this.
|
|
297
|
+
if (this.mode === "budget") {
|
|
298
|
+
lines.push(row(` › Limits are opt-in and stop continuation, not in-flight work.`), row(` ${this.budgetField === "turns" ? "›" : " "} Turns ${this.budgetTurns || "off"}`), row(` ${this.budgetField === "runtime" ? "›" : " "} Runtime ${this.budgetRuntime || "off"}`), row(), row(` ${this.theme.fg("dim", "tab field · enter save · o off · esc cancel")}`));
|
|
299
|
+
this.finish(lines, row, border, innerWidth);
|
|
300
|
+
return lines;
|
|
195
301
|
}
|
|
196
|
-
if (this.
|
|
197
|
-
lines.push(row(` ${this.
|
|
302
|
+
if (this.mode === "snooze") {
|
|
303
|
+
lines.push(row(` Snooze for ${this.snoozeDuration || "_"}`), row(` ${this.theme.fg("dim", "Automatically continues when due · enter save · esc cancel")}`));
|
|
304
|
+
this.finish(lines, row, border, innerWidth);
|
|
305
|
+
return lines;
|
|
198
306
|
}
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
307
|
+
const goal = this.goal;
|
|
308
|
+
const status = goal.snoozedUntil
|
|
309
|
+
? `SNOOZED UNTIL ${goal.snoozedUntil}`
|
|
310
|
+
: goal.status.toUpperCase();
|
|
311
|
+
lines.push(row(` ${this.theme.fg(goal.status === "complete" ? "success" : "accent", `● ${status}`)}`));
|
|
312
|
+
lines.push(row(` ${this.theme.bold(goalDisplayName(goal))}`));
|
|
313
|
+
for (const objectiveLine of wrapTextWithAnsi(displayGoalText(goal.objective, 500), contentWidth).slice(0, 3))
|
|
314
|
+
lines.push(row(` ${objectiveLine}`));
|
|
315
|
+
const elapsedEnd = goal.status === "active" ? this.now() : Date.parse(goal.updatedAt);
|
|
316
|
+
lines.push(row(` ${this.theme.fg("muted", `Elapsed ${formatElapsed(elapsedEnd - Date.parse(goal.createdAt))}`)}`));
|
|
317
|
+
const limits = [
|
|
318
|
+
goal.budget.maxIterations === undefined
|
|
319
|
+
? undefined
|
|
320
|
+
: `${goal.usage.iterations}/${goal.budget.maxIterations} turns`,
|
|
321
|
+
goal.budget.maxRuntimeMs === undefined
|
|
322
|
+
? undefined
|
|
323
|
+
: `${Math.round(goal.budget.maxRuntimeMs / 60_000)}m runtime`,
|
|
324
|
+
].filter((value) => value !== undefined);
|
|
325
|
+
lines.push(row(` ${this.theme.fg("muted", `Limits ${limits.length ? limits.join(" · ") : "off"}`)}`));
|
|
326
|
+
if (this.claim)
|
|
327
|
+
lines.push(row(` ${this.theme.fg("muted", `Continuation ${this.claim.state}`)}`));
|
|
328
|
+
if (this.checkpoints.length) {
|
|
329
|
+
lines.push(row(), row(` ${this.theme.fg("accent", "Checkpoints · agent-reported")}`));
|
|
330
|
+
const shown = this.showAllCheckpoints
|
|
331
|
+
? this.checkpoints.slice(this.checkpointOffset, this.checkpointOffset + 3)
|
|
332
|
+
: this.checkpoints.slice(0, 3);
|
|
333
|
+
for (const checkpoint of shown) {
|
|
334
|
+
lines.push(row(` ${checkpoint.createdAt.slice(11, 16)} · ${displayGoalText(checkpoint.summary, contentWidth - 10)}`));
|
|
335
|
+
if (this.showAllCheckpoints && checkpoint.evidence)
|
|
336
|
+
lines.push(row(` evidence · ${displayGoalText(checkpoint.evidence, contentWidth - 14)}`));
|
|
337
|
+
if (this.showAllCheckpoints && (checkpoint.blocker || checkpoint.nextStep))
|
|
338
|
+
lines.push(row(` ${checkpoint.blocker ? "blocker" : "next"} · ${displayGoalText(checkpoint.blocker ?? checkpoint.nextStep ?? "", contentWidth - 11)}`));
|
|
339
|
+
}
|
|
340
|
+
if (!this.showAllCheckpoints && this.checkpoints.length > 3)
|
|
341
|
+
lines.push(row(` ${this.theme.fg("dim", `… and ${this.checkpoints.length - 3} more · h show all`)}`));
|
|
342
|
+
else if (this.showAllCheckpoints && this.checkpoints.length > 3)
|
|
343
|
+
lines.push(row(` ${this.theme.fg("dim", `history ${this.checkpointOffset + 1}-${Math.min(this.checkpointOffset + shown.length, this.checkpoints.length)}/${this.checkpoints.length} · ↑↓ scroll · h newest 3`)}`));
|
|
203
344
|
}
|
|
204
|
-
const
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
this.goal.status === "paused" || this.goal.status === "blocked" ? "r resume" : undefined,
|
|
210
|
-
this.goal.status !== "complete" ? "b budget" : undefined,
|
|
211
|
-
this.goal.status !== "complete" ? "c complete" : undefined,
|
|
212
|
-
"x clear",
|
|
213
|
-
"q close",
|
|
214
|
-
].filter((action) => action !== undefined);
|
|
215
|
-
const footer = this.pendingConfirmation
|
|
216
|
-
? `${this.pendingConfirmation === "complete" ? "c" : "x"} again to confirm ${this.pendingConfirmation} · esc cancel`
|
|
217
|
-
: this.budgetField
|
|
218
|
-
? "digits edit · tab field · enter save · esc cancel"
|
|
219
|
-
: actions.join(" · ");
|
|
345
|
+
const footer = this.confirmClose
|
|
346
|
+
? "x again to close goal · esc cancel"
|
|
347
|
+
: goal.status === "complete"
|
|
348
|
+
? "x close goal · q close overlay"
|
|
349
|
+
: "e edit · b limits · s snooze · x close goal · q overlay";
|
|
220
350
|
lines.push(row(), row(` ${this.theme.fg("dim", footer)}`));
|
|
221
|
-
|
|
351
|
+
this.finish(lines, row, border, innerWidth);
|
|
222
352
|
return lines;
|
|
223
353
|
}
|
|
354
|
+
finish(lines, row, border, innerWidth) {
|
|
355
|
+
const error = this.inputError ?? this.actionError;
|
|
356
|
+
if (error)
|
|
357
|
+
lines.push(row(` ${this.theme.fg("error", displayGoalText(error, innerWidth - 2))}`));
|
|
358
|
+
lines.push(border(`╰${"─".repeat(innerWidth)}╯`));
|
|
359
|
+
}
|
|
224
360
|
invalidate() { }
|
|
225
361
|
}
|
|
362
|
+
export function parseDuration(value) {
|
|
363
|
+
const match = value
|
|
364
|
+
.trim()
|
|
365
|
+
.toLowerCase()
|
|
366
|
+
.match(/^(\d+)(m|h|d)$/);
|
|
367
|
+
if (!match)
|
|
368
|
+
return undefined;
|
|
369
|
+
const amount = Number(match[1]);
|
|
370
|
+
if (!Number.isSafeInteger(amount) || amount <= 0)
|
|
371
|
+
return undefined;
|
|
372
|
+
return amount * (match[2] === "m" ? 60_000 : match[2] === "h" ? 3_600_000 : 86_400_000);
|
|
373
|
+
}
|
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,
|
|
5
|
+
export { GoalWindow, parseDuration, 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";
|