@pinet/agent-goal 0.2.14 → 0.2.15
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 +11 -5
- package/dist/goal-window.d.ts +5 -2
- package/dist/goal-window.js +72 -30
- package/dist/index.js +99 -16
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -15,23 +15,29 @@ For local development: `pi -e ./agent-goal/index.ts`.
|
|
|
15
15
|
## Commands and terminal UI
|
|
16
16
|
|
|
17
17
|
```text
|
|
18
|
-
/goal <
|
|
18
|
+
/goal <idea> Ask the agent to refine a possible goal with you
|
|
19
|
+
/goal demo Start an agent-guided walkthrough
|
|
19
20
|
/goal Open the create/details overlay
|
|
21
|
+
/goal update Open the edit form
|
|
22
|
+
/goal update <objective> Apply a new objective to the next continuation
|
|
20
23
|
/goal update name <name> Rename immediately
|
|
21
24
|
/goal update objective <objective> Apply a new objective to the next continuation
|
|
22
25
|
/goal update budget turns <n> Set an optional total-turn limit
|
|
23
26
|
/goal update budget runtime <2h> Set an optional total-runtime limit
|
|
24
27
|
/goal update budget off Disable continuation limits
|
|
25
28
|
/goal snooze <30m|2h|1d> Snooze, then continue automatically
|
|
26
|
-
/goal close
|
|
29
|
+
/goal close Complete and clear the current goal
|
|
30
|
+
/goal clear Clear the current goal immediately
|
|
27
31
|
/goal hide | /goal show Hide or show compact status
|
|
28
32
|
```
|
|
29
33
|
|
|
30
|
-
The persistent row is deliberately compact: `🎯 name elapsed`. `/goal` opens a terminal-native overlay. An empty session gets a create form; an existing goal gets details and `e` edit, `b` limits, `s` timed snooze, and `x` close controls. Escape cancels a form
|
|
34
|
+
`/goal <idea>` starts a normal agent turn to clarify the outcome, scope, constraints, completion evidence, and optional limits; it does not create anything until you confirm the resulting goal. The persistent row is deliberately compact: `🎯 name elapsed`. `/goal` opens a terminal-native overlay. An empty session gets a create form; an existing goal gets details and `e` edit, `b` limits, `s` timed snooze, and `x` close controls. Escape cancels a form or closes the details overlay; Ctrl+C also closes the overlay. Closing a goal requires confirmation and remains available for blocked and budget-limited goals.
|
|
35
|
+
|
|
36
|
+
`/goal demo` asks the agent to walk through a small example, checkpointing, inspection, and verified completion. Demo and idea discussions require a session without an existing goal; otherwise the command reports an error without starting a turn. The walkthrough asks for consent before creating its example goal.
|
|
31
37
|
|
|
32
38
|
Name changes are visible immediately. Objective changes are fenced from stale evaluations and are used by the next continuation. Snooze is timed only: there is no indefinite pause or manual resume action, and a compare-and-swap wake prevents duplicate continuation.
|
|
33
39
|
|
|
34
|
-
Only one goal may exist per Pi session.
|
|
40
|
+
Only one current goal may exist per Pi session. A verified completion clears that current goal so another can be created; `/goal close` records completion before clearing, while `/goal clear` removes it immediately. Clearing removes its persisted checkpoints as well.
|
|
35
41
|
|
|
36
42
|
## Agent tools and checkpoints
|
|
37
43
|
|
|
@@ -43,7 +49,7 @@ The extension registers five model-visible tools:
|
|
|
43
49
|
- `get_goal` — inspect current durable state
|
|
44
50
|
- `update_goal` — attach a `complete` or `blocked` candidate for independent evaluation
|
|
45
51
|
|
|
46
|
-
Checkpoints are agent-reported progress records, not recovery snapshots. They persist with the goal. The overlay and headless dashboard
|
|
52
|
+
Checkpoints are agent-reported progress records, not recovery snapshots. They persist with the goal. The overlay initially shows the newest three checkpoints. Tab/Shift+Tab selects across the full history; Enter opens the selected checkpoint's full summary, evidence, next step, and blocker. Use ↑/↓ to scroll and Escape to return. The headless dashboard shows the newest three and `… and X more`.
|
|
47
53
|
|
|
48
54
|
## Optional continuation limits
|
|
49
55
|
|
package/dist/goal-window.d.ts
CHANGED
|
@@ -46,9 +46,11 @@ export declare class GoalWindow implements Component {
|
|
|
46
46
|
private snoozeDuration;
|
|
47
47
|
private inputError;
|
|
48
48
|
private confirmClose;
|
|
49
|
-
private
|
|
49
|
+
private selectedCheckpoint;
|
|
50
50
|
private checkpointOffset;
|
|
51
|
-
|
|
51
|
+
private checkpointLineCount;
|
|
52
|
+
private refreshTimer;
|
|
53
|
+
constructor(goal: AgentGoal | undefined, claim: GoalContinuationClaim | undefined, theme: Theme, onAction: (action: GoalWindowAction) => void, requestRender?: () => void, now?: () => number, actionError?: string | undefined, checkpoints?: GoalCheckpoint[], initialMode?: "details" | "edit");
|
|
52
54
|
handleInput(data: string): void;
|
|
53
55
|
private openTextForm;
|
|
54
56
|
private handleTextForm;
|
|
@@ -58,5 +60,6 @@ export declare class GoalWindow implements Component {
|
|
|
58
60
|
render(width: number): string[];
|
|
59
61
|
private finish;
|
|
60
62
|
invalidate(): void;
|
|
63
|
+
dispose(): void;
|
|
61
64
|
}
|
|
62
65
|
export declare function parseDuration(value: string): number | undefined;
|
package/dist/goal-window.js
CHANGED
|
@@ -21,9 +21,11 @@ export class GoalWindow {
|
|
|
21
21
|
snoozeDuration = "30m";
|
|
22
22
|
inputError;
|
|
23
23
|
confirmClose = false;
|
|
24
|
-
|
|
24
|
+
selectedCheckpoint = -1;
|
|
25
25
|
checkpointOffset = 0;
|
|
26
|
-
|
|
26
|
+
checkpointLineCount = 0;
|
|
27
|
+
refreshTimer;
|
|
28
|
+
constructor(goal, claim, theme, onAction, requestRender = () => undefined, now = Date.now, actionError, checkpoints = [], initialMode = "details") {
|
|
27
29
|
this.goal = goal;
|
|
28
30
|
this.claim = claim;
|
|
29
31
|
this.theme = theme;
|
|
@@ -32,10 +34,16 @@ export class GoalWindow {
|
|
|
32
34
|
this.now = now;
|
|
33
35
|
this.actionError = actionError;
|
|
34
36
|
this.checkpoints = checkpoints;
|
|
37
|
+
if (initialMode === "edit" && goal)
|
|
38
|
+
this.openTextForm("edit");
|
|
39
|
+
if (goal?.status === "active") {
|
|
40
|
+
this.refreshTimer = setInterval(this.requestRender, 1_000);
|
|
41
|
+
this.refreshTimer.unref();
|
|
42
|
+
}
|
|
35
43
|
}
|
|
36
44
|
handleInput(data) {
|
|
37
45
|
const key = data.toLowerCase();
|
|
38
|
-
if (matchesKey(data, "ctrl+c")
|
|
46
|
+
if (matchesKey(data, "ctrl+c")) {
|
|
39
47
|
this.onAction("close");
|
|
40
48
|
return;
|
|
41
49
|
}
|
|
@@ -70,15 +78,33 @@ export class GoalWindow {
|
|
|
70
78
|
this.openTextForm("create");
|
|
71
79
|
return;
|
|
72
80
|
}
|
|
73
|
-
if (this.
|
|
74
|
-
|
|
81
|
+
if (this.mode === "checkpoint") {
|
|
82
|
+
if (matchesKey(data, "down"))
|
|
83
|
+
this.checkpointOffset = Math.min(Math.max(0, this.checkpointLineCount - 8), this.checkpointOffset + 1);
|
|
84
|
+
else if (matchesKey(data, "up"))
|
|
85
|
+
this.checkpointOffset = Math.max(0, this.checkpointOffset - 1);
|
|
75
86
|
this.requestRender();
|
|
76
87
|
return;
|
|
77
88
|
}
|
|
78
|
-
if (this.
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
89
|
+
if (!this.confirmClose && this.checkpoints.length) {
|
|
90
|
+
if (matchesKey(data, "tab") || matchesKey(data, "shift+tab")) {
|
|
91
|
+
const backwards = matchesKey(data, "shift+tab");
|
|
92
|
+
this.selectedCheckpoint =
|
|
93
|
+
this.selectedCheckpoint < 0
|
|
94
|
+
? backwards
|
|
95
|
+
? this.checkpoints.length - 1
|
|
96
|
+
: 0
|
|
97
|
+
: (this.selectedCheckpoint + (backwards ? -1 : 1) + this.checkpoints.length) %
|
|
98
|
+
this.checkpoints.length;
|
|
99
|
+
this.requestRender();
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
if (matchesKey(data, "enter") && this.selectedCheckpoint >= 0) {
|
|
103
|
+
this.mode = "checkpoint";
|
|
104
|
+
this.checkpointOffset = 0;
|
|
105
|
+
this.requestRender();
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
82
108
|
}
|
|
83
109
|
if (this.confirmClose) {
|
|
84
110
|
if (key === "x")
|
|
@@ -98,11 +124,6 @@ export class GoalWindow {
|
|
|
98
124
|
this.inputError = undefined;
|
|
99
125
|
this.requestRender();
|
|
100
126
|
}
|
|
101
|
-
else if (key === "h" && this.checkpoints.length > 3) {
|
|
102
|
-
this.showAllCheckpoints = !this.showAllCheckpoints;
|
|
103
|
-
this.checkpointOffset = 0;
|
|
104
|
-
this.requestRender();
|
|
105
|
-
}
|
|
106
127
|
else if (key === "x") {
|
|
107
128
|
this.confirmClose = true;
|
|
108
129
|
this.requestRender();
|
|
@@ -276,14 +297,16 @@ export class GoalWindow {
|
|
|
276
297
|
];
|
|
277
298
|
if (!this.goal && this.mode === "details") {
|
|
278
299
|
lines.push(row(), row(` ${this.theme.fg("muted", "No goal for this session.")}`));
|
|
279
|
-
lines.push(row(` ${this.theme.fg("dim", "n · enter create ·
|
|
300
|
+
lines.push(row(` ${this.theme.fg("dim", "n · enter create · esc close")}`));
|
|
280
301
|
lines.push(border(`╰${"─".repeat(innerWidth)}╯`));
|
|
281
302
|
return lines;
|
|
282
303
|
}
|
|
283
304
|
if (this.mode === "create" || this.mode === "edit") {
|
|
284
305
|
const displayedName = displayGoalText(this.name, 500);
|
|
285
306
|
const displayedObjective = displayGoalText(this.objective, 500);
|
|
286
|
-
|
|
307
|
+
const objectivePrefix = ` ${this.textField === "objective" ? "›" : " "} Objective `;
|
|
308
|
+
const objectiveLines = wrapTextWithAnsi(displayedObjective || "_", Math.max(1, innerWidth - visibleWidth(objectivePrefix))).slice(0, 4);
|
|
309
|
+
lines.push(row(` ${this.textField === "name" ? "›" : " "} Name ${displayedName || "_"}`), ...objectiveLines.map((line, index) => row(`${index === 0 ? objectivePrefix : " ".repeat(visibleWidth(objectivePrefix))}${line}`)), ...(this.mode === "create"
|
|
287
310
|
? [
|
|
288
311
|
row(` ${this.textField === "turns" ? "›" : " "} Turns ${this.budgetTurns || "off"}`),
|
|
289
312
|
row(` ${this.textField === "runtime" ? "›" : " "} Runtime ${this.budgetRuntime || "off"}`),
|
|
@@ -294,6 +317,27 @@ export class GoalWindow {
|
|
|
294
317
|
this.finish(lines, row, border, innerWidth);
|
|
295
318
|
return lines;
|
|
296
319
|
}
|
|
320
|
+
if (this.mode === "checkpoint") {
|
|
321
|
+
const checkpoint = this.checkpoints[this.selectedCheckpoint];
|
|
322
|
+
const details = [];
|
|
323
|
+
for (const [label, value] of [
|
|
324
|
+
["Summary", checkpoint.summary],
|
|
325
|
+
["Evidence", checkpoint.evidence],
|
|
326
|
+
["Next", checkpoint.nextStep],
|
|
327
|
+
["Blocker", checkpoint.blocker],
|
|
328
|
+
]) {
|
|
329
|
+
if (value)
|
|
330
|
+
details.push(...wrapTextWithAnsi(`${label}: ${displayGoalText(value, value.length)}`, contentWidth));
|
|
331
|
+
}
|
|
332
|
+
this.checkpointLineCount = details.length;
|
|
333
|
+
this.checkpointOffset = Math.min(this.checkpointOffset, Math.max(0, details.length - 8));
|
|
334
|
+
lines.push(...details
|
|
335
|
+
.slice(this.checkpointOffset, this.checkpointOffset + 8)
|
|
336
|
+
.map((line) => row(` ${line}`)));
|
|
337
|
+
lines.push(row(), row(" ↑↓ scroll · esc back"));
|
|
338
|
+
this.finish(lines, row, border, innerWidth);
|
|
339
|
+
return lines;
|
|
340
|
+
}
|
|
297
341
|
if (this.mode === "budget") {
|
|
298
342
|
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
343
|
this.finish(lines, row, border, innerWidth);
|
|
@@ -327,26 +371,18 @@ export class GoalWindow {
|
|
|
327
371
|
lines.push(row(` ${this.theme.fg("muted", `Continuation ${this.claim.state}`)}`));
|
|
328
372
|
if (this.checkpoints.length) {
|
|
329
373
|
lines.push(row(), row(` ${this.theme.fg("accent", "Checkpoints · agent-reported")}`));
|
|
330
|
-
const
|
|
331
|
-
|
|
332
|
-
: this.checkpoints.slice(0, 3);
|
|
374
|
+
const start = Math.max(0, this.selectedCheckpoint - 2);
|
|
375
|
+
const shown = this.checkpoints.slice(start, start + 3);
|
|
333
376
|
for (const checkpoint of shown) {
|
|
334
|
-
lines.push(row(` ${checkpoint.createdAt.slice(11, 16)} · ${displayGoalText(checkpoint.summary, contentWidth -
|
|
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)}`));
|
|
377
|
+
lines.push(row(` ${checkpoint === this.checkpoints[this.selectedCheckpoint] ? "›" : " "} ${checkpoint.createdAt.slice(11, 16)} · ${displayGoalText(checkpoint.summary, contentWidth - 12)}`));
|
|
339
378
|
}
|
|
340
|
-
|
|
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`)}`));
|
|
379
|
+
lines.push(row(` ${this.theme.fg("dim", "tab/shift+tab select · enter open")}`));
|
|
344
380
|
}
|
|
345
381
|
const footer = this.confirmClose
|
|
346
382
|
? "x again to close goal · esc cancel"
|
|
347
383
|
: goal.status === "complete"
|
|
348
|
-
? "x close goal ·
|
|
349
|
-
: "e edit · b limits · s snooze · x close goal ·
|
|
384
|
+
? "x close goal · esc close overlay"
|
|
385
|
+
: "e edit · b limits · s snooze · x close goal · esc close overlay";
|
|
350
386
|
lines.push(row(), row(` ${this.theme.fg("dim", footer)}`));
|
|
351
387
|
this.finish(lines, row, border, innerWidth);
|
|
352
388
|
return lines;
|
|
@@ -358,6 +394,12 @@ export class GoalWindow {
|
|
|
358
394
|
lines.push(border(`╰${"─".repeat(innerWidth)}╯`));
|
|
359
395
|
}
|
|
360
396
|
invalidate() { }
|
|
397
|
+
dispose() {
|
|
398
|
+
if (!this.refreshTimer)
|
|
399
|
+
return;
|
|
400
|
+
clearInterval(this.refreshTimer);
|
|
401
|
+
this.refreshTimer = undefined;
|
|
402
|
+
}
|
|
361
403
|
}
|
|
362
404
|
export function parseDuration(value) {
|
|
363
405
|
const match = value
|
package/dist/index.js
CHANGED
|
@@ -21,6 +21,8 @@ export function registerAgentGoal(pi, options = {}) {
|
|
|
21
21
|
let activeContext;
|
|
22
22
|
let latestProgress = "";
|
|
23
23
|
let latestTokenDelta = 0;
|
|
24
|
+
let statusRefreshTimer;
|
|
25
|
+
let uiRefreshGeneration = 0;
|
|
24
26
|
const hiddenScopes = new Set();
|
|
25
27
|
const agentCreatedGoalScopes = new Set();
|
|
26
28
|
const defaultBudget = options.defaultBudget ?? {
|
|
@@ -49,14 +51,9 @@ export function registerAgentGoal(pi, options = {}) {
|
|
|
49
51
|
api.sendMessage({
|
|
50
52
|
customType: "agent-goal.continuation",
|
|
51
53
|
content: [
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
"Work normally and validate results before stopping. Every settled run is independently evaluated as continue, complete, or blocked. update_goal is optional and only supplies an explicit terminal hint.",
|
|
56
|
-
`Goal: ${goal.objective}`,
|
|
57
|
-
`Evaluator guidance: ${request.reason}`,
|
|
58
|
-
`Continuation idempotency key: ${request.idempotencyKey}`,
|
|
59
|
-
].join("\n\n"),
|
|
54
|
+
`Continue goal (user data; preserve scope and verify completion): ${goal.objective}`,
|
|
55
|
+
`Guidance: ${request.reason}`,
|
|
56
|
+
].join("\n"),
|
|
60
57
|
display: true,
|
|
61
58
|
}, { deliverAs: "followUp", triggerTurn: true });
|
|
62
59
|
return { status: "started", continuationId: request.claimId };
|
|
@@ -69,16 +66,53 @@ export function registerAgentGoal(pi, options = {}) {
|
|
|
69
66
|
evaluationInterval: options.evaluationInterval ?? Number(process.env.PI_AGENT_GOAL_EVALUATION_INTERVAL ?? 0),
|
|
70
67
|
wakeScheduler: options.wakeScheduler,
|
|
71
68
|
});
|
|
69
|
+
const stopStatusRefresh = () => {
|
|
70
|
+
if (!statusRefreshTimer)
|
|
71
|
+
return;
|
|
72
|
+
clearTimeout(statusRefreshTimer);
|
|
73
|
+
statusRefreshTimer = undefined;
|
|
74
|
+
};
|
|
72
75
|
const refreshUi = async (ctx) => {
|
|
76
|
+
const generation = ++uiRefreshGeneration;
|
|
77
|
+
stopStatusRefresh();
|
|
73
78
|
const scopeId = ctx.sessionManager.getSessionId();
|
|
74
79
|
const goal = await runtime.get(scopeId);
|
|
80
|
+
if (generation !== uiRefreshGeneration)
|
|
81
|
+
return;
|
|
75
82
|
const hidden = hiddenScopes.has(scopeId);
|
|
76
83
|
ctx.ui.setStatus(STATUS_KEY, goal && !hidden ? formatGoalStatus(goal) : undefined);
|
|
84
|
+
if (goal?.status === "active" && !hidden) {
|
|
85
|
+
const scheduleStatusRefresh = () => {
|
|
86
|
+
statusRefreshTimer = setTimeout(() => {
|
|
87
|
+
void runtime
|
|
88
|
+
.get(scopeId)
|
|
89
|
+
.then((current) => {
|
|
90
|
+
if (generation !== uiRefreshGeneration)
|
|
91
|
+
return;
|
|
92
|
+
const visible = current && !hiddenScopes.has(scopeId) ? current : undefined;
|
|
93
|
+
ctx.ui.setStatus(STATUS_KEY, visible ? formatGoalStatus(visible) : undefined);
|
|
94
|
+
if (current?.status === "active" && visible)
|
|
95
|
+
scheduleStatusRefresh();
|
|
96
|
+
else
|
|
97
|
+
stopStatusRefresh();
|
|
98
|
+
})
|
|
99
|
+
.catch((error) => {
|
|
100
|
+
console.error(`[agent-goal] elapsed refresh failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
101
|
+
if (generation === uiRefreshGeneration)
|
|
102
|
+
scheduleStatusRefresh();
|
|
103
|
+
});
|
|
104
|
+
}, 1_000);
|
|
105
|
+
statusRefreshTimer.unref();
|
|
106
|
+
};
|
|
107
|
+
scheduleStatusRefresh();
|
|
108
|
+
}
|
|
77
109
|
ctx.ui.setWidget(WIDGET_KEY, undefined);
|
|
78
110
|
};
|
|
79
111
|
const applyGoalAction = async (scopeId, action) => {
|
|
80
112
|
if (action === "closeGoal") {
|
|
81
113
|
await runtime.closeGoal(scopeId);
|
|
114
|
+
if (!(await runtime.clear(scopeId)))
|
|
115
|
+
throw new Error("This session has no goal");
|
|
82
116
|
return;
|
|
83
117
|
}
|
|
84
118
|
if (action === "pause" || action === "resume") {
|
|
@@ -112,8 +146,14 @@ export function registerAgentGoal(pi, options = {}) {
|
|
|
112
146
|
activeContext = ctx;
|
|
113
147
|
latestProgress = "";
|
|
114
148
|
latestTokenDelta = 0;
|
|
115
|
-
|
|
116
|
-
await runtime.
|
|
149
|
+
const scopeId = ctx.sessionManager.getSessionId();
|
|
150
|
+
const goal = await runtime.get(scopeId);
|
|
151
|
+
if (goal?.status === "complete")
|
|
152
|
+
await runtime.clear(scopeId);
|
|
153
|
+
await runtime.recover(scopeId);
|
|
154
|
+
const recoveredGoal = await runtime.get(scopeId);
|
|
155
|
+
if (recoveredGoal?.status === "complete")
|
|
156
|
+
await runtime.clear(scopeId);
|
|
117
157
|
await refreshUi(ctx);
|
|
118
158
|
});
|
|
119
159
|
pi.on("agent_start", async (_event, rawCtx) => {
|
|
@@ -146,6 +186,9 @@ export function registerAgentGoal(pi, options = {}) {
|
|
|
146
186
|
if (agentCreatedGoal)
|
|
147
187
|
agentCreatedGoalScopes.delete(scopeId);
|
|
148
188
|
}
|
|
189
|
+
const settledGoal = await runtime.get(scopeId);
|
|
190
|
+
if (settledGoal?.status === "complete")
|
|
191
|
+
await runtime.clear(scopeId);
|
|
149
192
|
await refreshUi(ctx);
|
|
150
193
|
}
|
|
151
194
|
catch (error) {
|
|
@@ -157,6 +200,8 @@ export function registerAgentGoal(pi, options = {}) {
|
|
|
157
200
|
});
|
|
158
201
|
pi.on("session_shutdown", () => {
|
|
159
202
|
activeContext = undefined;
|
|
203
|
+
uiRefreshGeneration += 1;
|
|
204
|
+
stopStatusRefresh();
|
|
160
205
|
runtime.close(!options.storage);
|
|
161
206
|
});
|
|
162
207
|
pi.registerTool({
|
|
@@ -345,23 +390,37 @@ export function registerAgentGoal(pi, options = {}) {
|
|
|
345
390
|
},
|
|
346
391
|
});
|
|
347
392
|
pi.registerCommand("goal", {
|
|
348
|
-
description: "
|
|
393
|
+
description: "Discuss or inspect a goal; update its name, objective, or limits; snooze, close, show, or hide it",
|
|
349
394
|
handler: async (args, rawCtx) => {
|
|
350
395
|
const ctx = rawCtx;
|
|
351
396
|
activeContext = ctx;
|
|
352
397
|
const scopeId = ctx.sessionManager.getSessionId();
|
|
353
398
|
const input = args.trim();
|
|
399
|
+
const command = input.toLowerCase();
|
|
354
400
|
try {
|
|
355
|
-
if (
|
|
401
|
+
if (command === "demo") {
|
|
402
|
+
if (await runtime.get(scopeId))
|
|
403
|
+
throw new Error("Use a session without a goal for /goal demo; your current goal is unchanged.");
|
|
404
|
+
api.sendMessage({
|
|
405
|
+
customType: "agent-goal.demo",
|
|
406
|
+
content: "Walk me through goals: agree a tiny example, create it, record a checkpoint, inspect /goal, then verify completion. Ask before changing any goal; preserve existing work.",
|
|
407
|
+
display: true,
|
|
408
|
+
}, { triggerTurn: true });
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
411
|
+
if (!input || command === "update") {
|
|
356
412
|
let openedWindow = false;
|
|
357
413
|
let actionError;
|
|
414
|
+
let initialMode = command === "update" ? "edit" : "details";
|
|
358
415
|
while (true) {
|
|
359
416
|
const goal = await runtime.get(scopeId);
|
|
417
|
+
if (initialMode === "edit" && !goal)
|
|
418
|
+
throw new Error("This session has no goal");
|
|
360
419
|
const claim = await runtime.getContinuationClaim(scopeId);
|
|
361
420
|
const checkpoints = await runtime.listCheckpoints(scopeId);
|
|
362
421
|
const action = await ctx.ui.custom((tui, theme, _keybindings, done) => {
|
|
363
422
|
openedWindow = true;
|
|
364
|
-
return new GoalWindow(goal, claim, theme, done, () => tui.requestRender(), Date.now, actionError, checkpoints);
|
|
423
|
+
return new GoalWindow(goal, claim, theme, done, () => tui.requestRender(), Date.now, actionError, checkpoints, initialMode);
|
|
365
424
|
}, {
|
|
366
425
|
overlay: true,
|
|
367
426
|
overlayOptions: {
|
|
@@ -382,6 +441,7 @@ export function registerAgentGoal(pi, options = {}) {
|
|
|
382
441
|
}, { triggerTurn: false });
|
|
383
442
|
return;
|
|
384
443
|
}
|
|
444
|
+
initialMode = "details";
|
|
385
445
|
if (!action || action === "close")
|
|
386
446
|
return;
|
|
387
447
|
try {
|
|
@@ -394,7 +454,6 @@ export function registerAgentGoal(pi, options = {}) {
|
|
|
394
454
|
}
|
|
395
455
|
}
|
|
396
456
|
}
|
|
397
|
-
const command = input.toLowerCase();
|
|
398
457
|
if (command.startsWith("update name ")) {
|
|
399
458
|
await runtime.updateDetails(scopeId, { name: input.slice("update name ".length) });
|
|
400
459
|
}
|
|
@@ -417,6 +476,15 @@ export function registerAgentGoal(pi, options = {}) {
|
|
|
417
476
|
throw new Error("Runtime must use m, h, or d");
|
|
418
477
|
await runtime.updateBudget(scopeId, { maxRuntimeMs: duration });
|
|
419
478
|
}
|
|
479
|
+
else if (command === "update name" ||
|
|
480
|
+
command === "update objective" ||
|
|
481
|
+
command === "update budget" ||
|
|
482
|
+
command.startsWith("update budget ")) {
|
|
483
|
+
throw new Error("Use /goal update, /goal update <objective>, or a complete update name/objective/budget command");
|
|
484
|
+
}
|
|
485
|
+
else if (command.startsWith("update ")) {
|
|
486
|
+
await runtime.updateDetails(scopeId, { objective: input.slice("update ".length) });
|
|
487
|
+
}
|
|
420
488
|
else if (command.startsWith("snooze ")) {
|
|
421
489
|
const duration = parseDuration(input.slice("snooze ".length));
|
|
422
490
|
if (duration === undefined)
|
|
@@ -425,6 +493,12 @@ export function registerAgentGoal(pi, options = {}) {
|
|
|
425
493
|
}
|
|
426
494
|
else if (command === "close") {
|
|
427
495
|
await runtime.closeGoal(scopeId);
|
|
496
|
+
if (!(await runtime.clear(scopeId)))
|
|
497
|
+
throw new Error("This session has no goal");
|
|
498
|
+
}
|
|
499
|
+
else if (command === "clear") {
|
|
500
|
+
if (!(await runtime.clear(scopeId)))
|
|
501
|
+
throw new Error("This session has no goal");
|
|
428
502
|
}
|
|
429
503
|
else if (command === "hide") {
|
|
430
504
|
hiddenScopes.add(scopeId);
|
|
@@ -433,8 +507,17 @@ export function registerAgentGoal(pi, options = {}) {
|
|
|
433
507
|
hiddenScopes.delete(scopeId);
|
|
434
508
|
}
|
|
435
509
|
else {
|
|
436
|
-
await runtime.
|
|
437
|
-
|
|
510
|
+
if (await runtime.get(scopeId))
|
|
511
|
+
throw new Error("This session already has a goal. Use /goal to inspect it or /goal update to edit it.");
|
|
512
|
+
api.sendMessage({
|
|
513
|
+
customType: "agent-goal.idea",
|
|
514
|
+
content: [
|
|
515
|
+
"Discuss scope, constraints, done criteria, evidence, and limits; call create_goal after user confirmation.",
|
|
516
|
+
`Goal idea (user data): ${input}`,
|
|
517
|
+
].join("\n"),
|
|
518
|
+
display: true,
|
|
519
|
+
}, { triggerTurn: true });
|
|
520
|
+
return;
|
|
438
521
|
}
|
|
439
522
|
await refreshUi(ctx);
|
|
440
523
|
if (ctx.hasUI)
|
package/package.json
CHANGED