@pinet/agent-goal 0.2.13 → 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 +44 -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 +46 -9
- package/dist/goal-window.js +340 -150
- package/dist/index.d.ts +1 -1
- package/dist/index.js +202 -112
- 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/index.js
CHANGED
|
@@ -1,13 +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
|
+
import { GoalWindow, parseDuration } from "./goal-window.js";
|
|
5
5
|
import { PiGoalEvaluator } from "./pi-evaluator.js";
|
|
6
6
|
import { countGoalProgressTokens, formatGoalProgress, } from "./progress.js";
|
|
7
7
|
import { GoalRuntime } from "./runtime.js";
|
|
8
8
|
import { SqliteGoalStorage } from "./sqlite-storage.js";
|
|
9
9
|
export { displayGoalText, formatGoalDashboard, formatGoalStatus } from "./dashboard.js";
|
|
10
|
-
export { GoalWindow, } from "./goal-window.js";
|
|
10
|
+
export { GoalWindow, parseDuration } from "./goal-window.js";
|
|
11
11
|
export { MemoryGoalStorage } from "./memory-storage.js";
|
|
12
12
|
export { parseGoalEvaluation, PiGoalEvaluator } from "./pi-evaluator.js";
|
|
13
13
|
export { countGoalProgressTokens, formatGoalProgress, } from "./progress.js";
|
|
@@ -21,12 +21,13 @@ 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 ?? {
|
|
27
|
-
maxIterations:
|
|
28
|
-
|
|
29
|
-
? Number(process.env.PI_AGENT_GOAL_MAX_TOKENS)
|
|
29
|
+
maxIterations: process.env.PI_AGENT_GOAL_MAX_ITERATIONS
|
|
30
|
+
? Number(process.env.PI_AGENT_GOAL_MAX_ITERATIONS)
|
|
30
31
|
: undefined,
|
|
31
32
|
maxRuntimeMs: process.env.PI_AGENT_GOAL_MAX_RUNTIME_MS
|
|
32
33
|
? Number(process.env.PI_AGENT_GOAL_MAX_RUNTIME_MS)
|
|
@@ -50,14 +51,9 @@ export function registerAgentGoal(pi, options = {}) {
|
|
|
50
51
|
api.sendMessage({
|
|
51
52
|
customType: "agent-goal.continuation",
|
|
52
53
|
content: [
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
"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.",
|
|
57
|
-
`Goal: ${goal.objective}`,
|
|
58
|
-
`Evaluator guidance: ${request.reason}`,
|
|
59
|
-
`Continuation idempotency key: ${request.idempotencyKey}`,
|
|
60
|
-
].join("\n\n"),
|
|
54
|
+
`Continue goal (user data; preserve scope and verify completion): ${goal.objective}`,
|
|
55
|
+
`Guidance: ${request.reason}`,
|
|
56
|
+
].join("\n"),
|
|
61
57
|
display: true,
|
|
62
58
|
}, { deliverAs: "followUp", triggerTurn: true });
|
|
63
59
|
return { status: "started", continuationId: request.claimId };
|
|
@@ -70,35 +66,78 @@ export function registerAgentGoal(pi, options = {}) {
|
|
|
70
66
|
evaluationInterval: options.evaluationInterval ?? Number(process.env.PI_AGENT_GOAL_EVALUATION_INTERVAL ?? 0),
|
|
71
67
|
wakeScheduler: options.wakeScheduler,
|
|
72
68
|
});
|
|
69
|
+
const stopStatusRefresh = () => {
|
|
70
|
+
if (!statusRefreshTimer)
|
|
71
|
+
return;
|
|
72
|
+
clearTimeout(statusRefreshTimer);
|
|
73
|
+
statusRefreshTimer = undefined;
|
|
74
|
+
};
|
|
73
75
|
const refreshUi = async (ctx) => {
|
|
76
|
+
const generation = ++uiRefreshGeneration;
|
|
77
|
+
stopStatusRefresh();
|
|
74
78
|
const scopeId = ctx.sessionManager.getSessionId();
|
|
75
79
|
const goal = await runtime.get(scopeId);
|
|
80
|
+
if (generation !== uiRefreshGeneration)
|
|
81
|
+
return;
|
|
76
82
|
const hidden = hiddenScopes.has(scopeId);
|
|
77
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
|
+
}
|
|
78
109
|
ctx.ui.setWidget(WIDGET_KEY, undefined);
|
|
79
110
|
};
|
|
80
111
|
const applyGoalAction = async (scopeId, action) => {
|
|
81
|
-
if (
|
|
82
|
-
await runtime.
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
});
|
|
112
|
+
if (action === "closeGoal") {
|
|
113
|
+
await runtime.closeGoal(scopeId);
|
|
114
|
+
if (!(await runtime.clear(scopeId)))
|
|
115
|
+
throw new Error("This session has no goal");
|
|
86
116
|
return;
|
|
87
117
|
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
break;
|
|
92
|
-
case "resume":
|
|
93
|
-
await runtime.setStatus(scopeId, "active");
|
|
118
|
+
if (action === "pause" || action === "resume") {
|
|
119
|
+
await runtime.setStatus(scopeId, action === "pause" ? "paused" : "active");
|
|
120
|
+
if (action === "resume")
|
|
94
121
|
await runtime.start(scopeId, "Resume the goal from current state.");
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
switch (action.type) {
|
|
125
|
+
case "create":
|
|
126
|
+
await runtime.create(scopeId, action.objective, {
|
|
127
|
+
...defaultBudget,
|
|
128
|
+
...(action.maxIterations === undefined ? {} : { maxIterations: action.maxIterations }),
|
|
129
|
+
...(action.maxRuntimeMs === undefined ? {} : { maxRuntimeMs: action.maxRuntimeMs }),
|
|
130
|
+
}, action.name);
|
|
131
|
+
await runtime.start(scopeId);
|
|
132
|
+
break;
|
|
133
|
+
case "edit":
|
|
134
|
+
await runtime.updateDetails(scopeId, action);
|
|
95
135
|
break;
|
|
96
|
-
case "
|
|
97
|
-
await runtime.
|
|
136
|
+
case "budget":
|
|
137
|
+
await runtime.updateBudget(scopeId, action);
|
|
98
138
|
break;
|
|
99
|
-
case "
|
|
100
|
-
|
|
101
|
-
throw new Error("This session has no goal");
|
|
139
|
+
case "snooze":
|
|
140
|
+
await runtime.snooze(scopeId, action.durationMs);
|
|
102
141
|
break;
|
|
103
142
|
}
|
|
104
143
|
};
|
|
@@ -107,8 +146,14 @@ export function registerAgentGoal(pi, options = {}) {
|
|
|
107
146
|
activeContext = ctx;
|
|
108
147
|
latestProgress = "";
|
|
109
148
|
latestTokenDelta = 0;
|
|
110
|
-
|
|
111
|
-
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);
|
|
112
157
|
await refreshUi(ctx);
|
|
113
158
|
});
|
|
114
159
|
pi.on("agent_start", async (_event, rawCtx) => {
|
|
@@ -141,6 +186,9 @@ export function registerAgentGoal(pi, options = {}) {
|
|
|
141
186
|
if (agentCreatedGoal)
|
|
142
187
|
agentCreatedGoalScopes.delete(scopeId);
|
|
143
188
|
}
|
|
189
|
+
const settledGoal = await runtime.get(scopeId);
|
|
190
|
+
if (settledGoal?.status === "complete")
|
|
191
|
+
await runtime.clear(scopeId);
|
|
144
192
|
await refreshUi(ctx);
|
|
145
193
|
}
|
|
146
194
|
catch (error) {
|
|
@@ -152,6 +200,8 @@ export function registerAgentGoal(pi, options = {}) {
|
|
|
152
200
|
});
|
|
153
201
|
pi.on("session_shutdown", () => {
|
|
154
202
|
activeContext = undefined;
|
|
203
|
+
uiRefreshGeneration += 1;
|
|
204
|
+
stopStatusRefresh();
|
|
155
205
|
runtime.close(!options.storage);
|
|
156
206
|
});
|
|
157
207
|
pi.registerTool({
|
|
@@ -167,14 +217,8 @@ export function registerAgentGoal(pi, options = {}) {
|
|
|
167
217
|
parameters: {
|
|
168
218
|
type: "object",
|
|
169
219
|
properties: {
|
|
220
|
+
name: { type: "string", description: "Short terminal-friendly goal name." },
|
|
170
221
|
objective: { type: "string", description: "The complete user-aligned outcome to achieve." },
|
|
171
|
-
maxIterations: {
|
|
172
|
-
type: "integer",
|
|
173
|
-
minimum: 1,
|
|
174
|
-
maximum: defaultBudget.maxIterations,
|
|
175
|
-
},
|
|
176
|
-
maxTokens: { type: "number", exclusiveMinimum: 0 },
|
|
177
|
-
maxRuntimeMs: { type: "number", exclusiveMinimum: 0 },
|
|
178
222
|
},
|
|
179
223
|
required: ["objective"],
|
|
180
224
|
additionalProperties: false,
|
|
@@ -183,25 +227,7 @@ export function registerAgentGoal(pi, options = {}) {
|
|
|
183
227
|
const params = rawParams;
|
|
184
228
|
const ctx = rawCtx;
|
|
185
229
|
const scopeId = ctx.sessionManager.getSessionId();
|
|
186
|
-
|
|
187
|
-
params.maxIterations > defaultBudget.maxIterations) {
|
|
188
|
-
throw new Error(`Goal maxIterations cannot exceed the configured limit of ${defaultBudget.maxIterations}`);
|
|
189
|
-
}
|
|
190
|
-
if (params.maxTokens !== undefined &&
|
|
191
|
-
defaultBudget.maxTokens !== undefined &&
|
|
192
|
-
params.maxTokens > defaultBudget.maxTokens) {
|
|
193
|
-
throw new Error(`Goal maxTokens cannot exceed the configured limit of ${defaultBudget.maxTokens}`);
|
|
194
|
-
}
|
|
195
|
-
if (params.maxRuntimeMs !== undefined &&
|
|
196
|
-
defaultBudget.maxRuntimeMs !== undefined &&
|
|
197
|
-
params.maxRuntimeMs > defaultBudget.maxRuntimeMs) {
|
|
198
|
-
throw new Error(`Goal maxRuntimeMs cannot exceed the configured limit of ${defaultBudget.maxRuntimeMs}`);
|
|
199
|
-
}
|
|
200
|
-
const goal = await runtime.create(scopeId, params.objective, {
|
|
201
|
-
maxIterations: params.maxIterations ?? defaultBudget.maxIterations,
|
|
202
|
-
maxTokens: params.maxTokens ?? defaultBudget.maxTokens,
|
|
203
|
-
maxRuntimeMs: params.maxRuntimeMs ?? defaultBudget.maxRuntimeMs,
|
|
204
|
-
});
|
|
230
|
+
const goal = await runtime.create(scopeId, params.objective, defaultBudget, params.name);
|
|
205
231
|
agentCreatedGoalScopes.add(scopeId);
|
|
206
232
|
await refreshUi(ctx);
|
|
207
233
|
return {
|
|
@@ -217,9 +243,9 @@ export function registerAgentGoal(pi, options = {}) {
|
|
|
217
243
|
});
|
|
218
244
|
pi.registerTool({
|
|
219
245
|
name: "update_goal_budget",
|
|
220
|
-
label: "Update goal
|
|
221
|
-
description: "
|
|
222
|
-
promptSnippet: "Adjust the active session goal's
|
|
246
|
+
label: "Update goal limits",
|
|
247
|
+
description: "Set optional turns/runtime continuation limits, or turn limits off. Limits never interrupt in-flight work and changes preserve accounted usage.",
|
|
248
|
+
promptSnippet: "Adjust the active session goal's optional continuation limits.",
|
|
223
249
|
promptGuidelines: [
|
|
224
250
|
"Only change a goal budget when more or less capacity is genuinely needed for the existing user-aligned objective.",
|
|
225
251
|
"Never use budget changes to broaden the objective or evade configured hard limits.",
|
|
@@ -231,15 +257,17 @@ export function registerAgentGoal(pi, options = {}) {
|
|
|
231
257
|
maxTurns: {
|
|
232
258
|
type: "integer",
|
|
233
259
|
minimum: 1,
|
|
234
|
-
|
|
260
|
+
...(defaultBudget.maxIterations === undefined
|
|
261
|
+
? {}
|
|
262
|
+
: { maximum: defaultBudget.maxIterations }),
|
|
235
263
|
description: "New total settled-turn ceiling, including turns already accounted.",
|
|
236
264
|
},
|
|
237
|
-
|
|
265
|
+
maxRuntimeMs: {
|
|
238
266
|
type: "number",
|
|
239
267
|
exclusiveMinimum: 0,
|
|
240
|
-
|
|
241
|
-
description: "New total token ceiling, including tokens already accounted.",
|
|
268
|
+
description: "New total runtime ceiling in milliseconds.",
|
|
242
269
|
},
|
|
270
|
+
off: { type: "boolean", description: "Disable all continuation limits." },
|
|
243
271
|
},
|
|
244
272
|
additionalProperties: false,
|
|
245
273
|
},
|
|
@@ -248,14 +276,15 @@ export function registerAgentGoal(pi, options = {}) {
|
|
|
248
276
|
const ctx = rawCtx;
|
|
249
277
|
const goal = await runtime.updateBudget(ctx.sessionManager.getSessionId(), {
|
|
250
278
|
maxIterations: params.maxTurns,
|
|
251
|
-
|
|
279
|
+
maxRuntimeMs: params.maxRuntimeMs,
|
|
280
|
+
disabled: params.off,
|
|
252
281
|
});
|
|
253
282
|
await refreshUi(ctx);
|
|
254
283
|
return {
|
|
255
284
|
content: [
|
|
256
285
|
{
|
|
257
286
|
type: "text",
|
|
258
|
-
text: `Updated goal
|
|
287
|
+
text: `Updated goal limits: ${goal.budget.maxIterations ?? "unlimited"} turns, ${goal.budget.maxRuntimeMs ?? "unlimited"}ms runtime.`,
|
|
259
288
|
},
|
|
260
289
|
],
|
|
261
290
|
details: { goal },
|
|
@@ -336,23 +365,62 @@ export function registerAgentGoal(pi, options = {}) {
|
|
|
336
365
|
};
|
|
337
366
|
},
|
|
338
367
|
});
|
|
368
|
+
pi.registerTool({
|
|
369
|
+
name: "checkpoint_goal",
|
|
370
|
+
label: "Checkpoint goal",
|
|
371
|
+
description: "Record durable agent-reported progress, evidence, and the next step or blocker.",
|
|
372
|
+
promptSnippet: "Record a concise durable progress checkpoint for the active goal.",
|
|
373
|
+
parameters: {
|
|
374
|
+
type: "object",
|
|
375
|
+
properties: {
|
|
376
|
+
summary: { type: "string" },
|
|
377
|
+
evidence: { type: "string" },
|
|
378
|
+
nextStep: { type: "string" },
|
|
379
|
+
blocker: { type: "string" },
|
|
380
|
+
},
|
|
381
|
+
required: ["summary"],
|
|
382
|
+
additionalProperties: false,
|
|
383
|
+
},
|
|
384
|
+
async execute(_toolCallId, rawParams, _signal, _onUpdate, rawCtx) {
|
|
385
|
+
const checkpoint = await runtime.addCheckpoint(rawCtx.sessionManager.getSessionId(), rawParams);
|
|
386
|
+
return {
|
|
387
|
+
content: [{ type: "text", text: `Checkpoint recorded: ${checkpoint.summary}` }],
|
|
388
|
+
details: { checkpoint },
|
|
389
|
+
};
|
|
390
|
+
},
|
|
391
|
+
});
|
|
339
392
|
pi.registerCommand("goal", {
|
|
340
|
-
description: "
|
|
393
|
+
description: "Discuss or inspect a goal; update its name, objective, or limits; snooze, close, show, or hide it",
|
|
341
394
|
handler: async (args, rawCtx) => {
|
|
342
395
|
const ctx = rawCtx;
|
|
343
396
|
activeContext = ctx;
|
|
344
397
|
const scopeId = ctx.sessionManager.getSessionId();
|
|
345
398
|
const input = args.trim();
|
|
399
|
+
const command = input.toLowerCase();
|
|
346
400
|
try {
|
|
347
|
-
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") {
|
|
348
412
|
let openedWindow = false;
|
|
349
413
|
let actionError;
|
|
414
|
+
let initialMode = command === "update" ? "edit" : "details";
|
|
350
415
|
while (true) {
|
|
351
416
|
const goal = await runtime.get(scopeId);
|
|
417
|
+
if (initialMode === "edit" && !goal)
|
|
418
|
+
throw new Error("This session has no goal");
|
|
352
419
|
const claim = await runtime.getContinuationClaim(scopeId);
|
|
420
|
+
const checkpoints = await runtime.listCheckpoints(scopeId);
|
|
353
421
|
const action = await ctx.ui.custom((tui, theme, _keybindings, done) => {
|
|
354
422
|
openedWindow = true;
|
|
355
|
-
return new GoalWindow(goal, claim, theme, done, () => tui.requestRender(), Date.now, actionError);
|
|
423
|
+
return new GoalWindow(goal, claim, theme, done, () => tui.requestRender(), Date.now, actionError, checkpoints, initialMode);
|
|
356
424
|
}, {
|
|
357
425
|
overlay: true,
|
|
358
426
|
overlayOptions: {
|
|
@@ -367,12 +435,13 @@ export function registerAgentGoal(pi, options = {}) {
|
|
|
367
435
|
api.sendMessage({
|
|
368
436
|
customType: "agent-goal.status",
|
|
369
437
|
content: goal
|
|
370
|
-
? formatGoalDashboard(goal, claim).join("\n")
|
|
438
|
+
? formatGoalDashboard(goal, claim, checkpoints).join("\n")
|
|
371
439
|
: "This session has no goal.",
|
|
372
440
|
display: true,
|
|
373
441
|
}, { triggerTurn: false });
|
|
374
442
|
return;
|
|
375
443
|
}
|
|
444
|
+
initialMode = "details";
|
|
376
445
|
if (!action || action === "close")
|
|
377
446
|
return;
|
|
378
447
|
try {
|
|
@@ -385,49 +454,70 @@ export function registerAgentGoal(pi, options = {}) {
|
|
|
385
454
|
}
|
|
386
455
|
}
|
|
387
456
|
}
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
await
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
457
|
+
if (command.startsWith("update name ")) {
|
|
458
|
+
await runtime.updateDetails(scopeId, { name: input.slice("update name ".length) });
|
|
459
|
+
}
|
|
460
|
+
else if (command.startsWith("update objective ")) {
|
|
461
|
+
await runtime.updateDetails(scopeId, {
|
|
462
|
+
objective: input.slice("update objective ".length),
|
|
463
|
+
});
|
|
464
|
+
}
|
|
465
|
+
else if (command === "update budget off") {
|
|
466
|
+
await runtime.updateBudget(scopeId, { disabled: true });
|
|
467
|
+
}
|
|
468
|
+
else if (command.startsWith("update budget turns ")) {
|
|
469
|
+
await runtime.updateBudget(scopeId, {
|
|
470
|
+
maxIterations: Number(input.slice("update budget turns ".length)),
|
|
471
|
+
});
|
|
472
|
+
}
|
|
473
|
+
else if (command.startsWith("update budget runtime ")) {
|
|
474
|
+
const duration = parseDuration(input.slice("update budget runtime ".length));
|
|
475
|
+
if (duration === undefined)
|
|
476
|
+
throw new Error("Runtime must use m, h, or d");
|
|
477
|
+
await runtime.updateBudget(scopeId, { maxRuntimeMs: duration });
|
|
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) });
|
|
413
487
|
}
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
488
|
+
else if (command.startsWith("snooze ")) {
|
|
489
|
+
const duration = parseDuration(input.slice("snooze ".length));
|
|
490
|
+
if (duration === undefined)
|
|
491
|
+
throw new Error("Snooze must use m, h, or d");
|
|
492
|
+
await runtime.snooze(scopeId, duration);
|
|
493
|
+
}
|
|
494
|
+
else if (command === "close") {
|
|
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");
|
|
502
|
+
}
|
|
503
|
+
else if (command === "hide") {
|
|
504
|
+
hiddenScopes.add(scopeId);
|
|
505
|
+
}
|
|
506
|
+
else if (command === "show") {
|
|
507
|
+
hiddenScopes.delete(scopeId);
|
|
508
|
+
}
|
|
509
|
+
else {
|
|
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;
|
|
431
521
|
}
|
|
432
522
|
await refreshUi(ctx);
|
|
433
523
|
if (ctx.hasUI)
|
package/dist/memory-storage.d.ts
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
|
-
import type { AgentGoal, GoalContinuationClaim, GoalPendingEvaluation, GoalStorage, GoalTerminalCandidateRecord } from "./domain.js";
|
|
1
|
+
import type { AgentGoal, GoalCheckpoint, GoalContinuationClaim, GoalPendingEvaluation, GoalStorage, GoalTerminalCandidateRecord } from "./domain.js";
|
|
2
2
|
export declare class MemoryGoalStorage implements GoalStorage {
|
|
3
3
|
private readonly goals;
|
|
4
4
|
private readonly pendingEvaluations;
|
|
5
|
+
private readonly checkpoints;
|
|
5
6
|
private readonly terminalCandidates;
|
|
6
7
|
private readonly claims;
|
|
7
8
|
get(scopeId: string): Promise<AgentGoal | undefined>;
|
|
8
9
|
create(goal: AgentGoal): Promise<void>;
|
|
9
10
|
replace(goal: AgentGoal, expectedVersion: number): Promise<boolean>;
|
|
10
11
|
updateBudget(goal: AgentGoal, expectedVersion: number): Promise<boolean>;
|
|
12
|
+
addCheckpoint(checkpoint: GoalCheckpoint): Promise<boolean>;
|
|
13
|
+
listCheckpoints(scopeId: string): Promise<GoalCheckpoint[]>;
|
|
11
14
|
delete(scopeId: string, expectedVersion: number): Promise<boolean>;
|
|
12
15
|
getPendingEvaluation(scopeId: string): Promise<GoalPendingEvaluation | undefined>;
|
|
13
16
|
appendPendingEvaluation(pending: GoalPendingEvaluation): Promise<boolean>;
|
package/dist/memory-storage.js
CHANGED
|
@@ -9,6 +9,7 @@ function cloneGoal(goal) {
|
|
|
9
9
|
export class MemoryGoalStorage {
|
|
10
10
|
goals = new Map();
|
|
11
11
|
pendingEvaluations = new Map();
|
|
12
|
+
checkpoints = new Map();
|
|
12
13
|
terminalCandidates = new Map();
|
|
13
14
|
claims = new Map();
|
|
14
15
|
async get(scopeId) {
|
|
@@ -46,6 +47,18 @@ export class MemoryGoalStorage {
|
|
|
46
47
|
}
|
|
47
48
|
return true;
|
|
48
49
|
}
|
|
50
|
+
async addCheckpoint(checkpoint) {
|
|
51
|
+
const goal = this.goals.get(checkpoint.scopeId);
|
|
52
|
+
if (!goal || goal.id !== checkpoint.goalId || goal.status === "complete")
|
|
53
|
+
return false;
|
|
54
|
+
const checkpoints = this.checkpoints.get(checkpoint.scopeId) ?? [];
|
|
55
|
+
checkpoints.push({ ...checkpoint });
|
|
56
|
+
this.checkpoints.set(checkpoint.scopeId, checkpoints);
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
async listCheckpoints(scopeId) {
|
|
60
|
+
return (this.checkpoints.get(scopeId) ?? []).map((checkpoint) => ({ ...checkpoint })).reverse();
|
|
61
|
+
}
|
|
49
62
|
async delete(scopeId, expectedVersion) {
|
|
50
63
|
const current = this.goals.get(scopeId);
|
|
51
64
|
if (!current || current.version !== expectedVersion)
|
|
@@ -53,6 +66,7 @@ export class MemoryGoalStorage {
|
|
|
53
66
|
this.pendingEvaluations.delete(scopeId);
|
|
54
67
|
this.terminalCandidates.delete(scopeId);
|
|
55
68
|
this.claims.delete(scopeId);
|
|
69
|
+
this.checkpoints.delete(scopeId);
|
|
56
70
|
return this.goals.delete(scopeId);
|
|
57
71
|
}
|
|
58
72
|
async getPendingEvaluation(scopeId) {
|
|
@@ -124,7 +138,13 @@ export class MemoryGoalStorage {
|
|
|
124
138
|
pending?.evaluationId !== expectedEvaluationId) {
|
|
125
139
|
return false;
|
|
126
140
|
}
|
|
127
|
-
this.goals.set(goal.scopeId,
|
|
141
|
+
this.goals.set(goal.scopeId, {
|
|
142
|
+
...cloneGoal(goal),
|
|
143
|
+
name: current.name,
|
|
144
|
+
objective: current.objective,
|
|
145
|
+
budget: { ...current.budget },
|
|
146
|
+
snoozedUntil: current.snoozedUntil,
|
|
147
|
+
});
|
|
128
148
|
this.pendingEvaluations.delete(goal.scopeId);
|
|
129
149
|
return true;
|
|
130
150
|
}
|
package/dist/runtime.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AgentGoal, GoalBudget, GoalBudgetUpdate, GoalContinuation, GoalContinuationClaim, GoalEvaluator, GoalEventSink, GoalProgress, GoalRetryPolicy, GoalStatus, GoalStorage, GoalTerminalCandidate, GoalTerminalCandidateRecord, GoalWakeScheduler } from "./domain.js";
|
|
1
|
+
import type { AgentGoal, GoalBudget, GoalCheckpoint, GoalBudgetUpdate, GoalContinuation, GoalContinuationClaim, GoalEvaluator, GoalEventSink, GoalProgress, GoalRetryPolicy, GoalStatus, GoalStorage, GoalTerminalCandidate, GoalTerminalCandidateRecord, GoalWakeScheduler } from "./domain.js";
|
|
2
2
|
export interface GoalRuntimeOptions {
|
|
3
3
|
defaultBudget?: GoalBudget;
|
|
4
4
|
retryPolicy?: GoalRetryPolicy;
|
|
@@ -28,8 +28,21 @@ export declare class GoalRuntime {
|
|
|
28
28
|
get(scopeId: string): Promise<AgentGoal | undefined>;
|
|
29
29
|
getContinuationClaim(scopeId: string): Promise<GoalContinuationClaim | undefined>;
|
|
30
30
|
getTerminalCandidate(scopeId: string): Promise<GoalTerminalCandidateRecord | undefined>;
|
|
31
|
-
|
|
31
|
+
listCheckpoints(scopeId: string): Promise<GoalCheckpoint[]>;
|
|
32
|
+
create(scopeId: string, objective: string, budget?: GoalBudget, name?: string): Promise<AgentGoal>;
|
|
32
33
|
updateBudget(scopeId: string, update: GoalBudgetUpdate): Promise<AgentGoal>;
|
|
34
|
+
updateDetails(scopeId: string, update: {
|
|
35
|
+
name?: string;
|
|
36
|
+
objective?: string;
|
|
37
|
+
}): Promise<AgentGoal>;
|
|
38
|
+
addCheckpoint(scopeId: string, input: {
|
|
39
|
+
summary: string;
|
|
40
|
+
evidence?: string;
|
|
41
|
+
nextStep?: string;
|
|
42
|
+
blocker?: string;
|
|
43
|
+
}): Promise<GoalCheckpoint>;
|
|
44
|
+
snooze(scopeId: string, durationMs: number): Promise<AgentGoal>;
|
|
45
|
+
closeGoal(scopeId: string): Promise<AgentGoal>;
|
|
33
46
|
setStatus(scopeId: string, status: Extract<GoalStatus, "active" | "paused" | "complete">): Promise<AgentGoal>;
|
|
34
47
|
clear(scopeId: string): Promise<boolean>;
|
|
35
48
|
requestTerminalCandidate(scopeId: string, candidate: GoalTerminalCandidate): Promise<GoalTerminalCandidateRecord>;
|