killeros 2.1.27 → 2.1.28
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/CHANGELOG.md +18 -0
- package/README.md +3 -3
- package/killeros/activity.ts +16 -7
- package/killeros/footer.ts +6 -4
- package/killeros/goal-interface.ts +195 -48
- package/killeros/goal-runtime.ts +190 -44
- package/killeros/goal-settlement.ts +129 -30
- package/killeros/goal-state.ts +205 -32
- package/killeros/runtime.ts +54 -0
- package/killeros/worked-for.ts +1 -1
- package/package.json +1 -1
- package/themes/killeros.json +3 -2
|
@@ -1,9 +1,18 @@
|
|
|
1
1
|
import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
import type { AutoCompactionGoalHandlers } from "./auto-compaction.ts";
|
|
3
3
|
import { reportError } from "./errors.ts";
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
import {
|
|
5
|
+
isGoalModeSupported,
|
|
6
|
+
isSavedSession,
|
|
7
|
+
pauseGoalAfterFailure,
|
|
8
|
+
pauseGoalAtTurnLimit,
|
|
9
|
+
resumeInterruptedGoalTurn,
|
|
10
|
+
scheduleGoalContinuation,
|
|
11
|
+
syncGoalUpdateTool,
|
|
12
|
+
transitionGoal,
|
|
13
|
+
} from "./goal-runtime.ts";
|
|
14
|
+
import { boundGoalText, pauseGoalState } from "./goal-state.ts";
|
|
15
|
+
import type { GoalRuntime, GoalState } from "./runtime.ts";
|
|
7
16
|
import { safeTerminalText } from "./safe-terminal-text.ts";
|
|
8
17
|
|
|
9
18
|
function pauseGoalForPossibleManualCompaction(
|
|
@@ -13,14 +22,16 @@ function pauseGoalForPossibleManualCompaction(
|
|
|
13
22
|
reason: string,
|
|
14
23
|
): void {
|
|
15
24
|
if (runtime.state?.status !== "active") return;
|
|
16
|
-
const safeReason =
|
|
25
|
+
const safeReason = boundGoalText(reason) || "the agent turn was aborted";
|
|
17
26
|
try {
|
|
18
27
|
transitionGoal(pi, runtime, "error", "paused", safeReason, {
|
|
19
28
|
resumeAfterManualCompaction: true,
|
|
29
|
+
keepTurnForRecovery: true,
|
|
30
|
+
preserveTurnAuthorization: true,
|
|
20
31
|
});
|
|
21
32
|
} catch {
|
|
22
33
|
const current = runtime.state;
|
|
23
|
-
runtime.state = current ? pauseGoalState(current, safeReason, Date.now(), true) : undefined;
|
|
34
|
+
runtime.state = current ? pauseGoalState(current, safeReason, Date.now(), true, true) : undefined;
|
|
24
35
|
syncGoalUpdateTool(pi, runtime);
|
|
25
36
|
runtime.persistenceRetryNeeded = true;
|
|
26
37
|
runtime.continuationScheduled = false;
|
|
@@ -28,7 +39,7 @@ function pauseGoalForPossibleManualCompaction(
|
|
|
28
39
|
runtime.requestRender?.();
|
|
29
40
|
}
|
|
30
41
|
ctx.ui.notify(
|
|
31
|
-
"Goal paused because the turn was aborted. If /compact is running, KillerOS will resume after Pi saves the summary. Run /goal pause to keep it paused.",
|
|
42
|
+
"Goal paused because the turn was aborted. If /compact is running, KillerOS will resume the same goal turn after Pi saves the summary. Run /goal pause to keep it paused.",
|
|
32
43
|
"warning",
|
|
33
44
|
);
|
|
34
45
|
}
|
|
@@ -40,8 +51,16 @@ function recoverGoalAfterManualCompaction(
|
|
|
40
51
|
): boolean {
|
|
41
52
|
if (runtime.state?.status !== "paused"
|
|
42
53
|
|| runtime.state.resumeAfterManualCompaction !== true) return false;
|
|
54
|
+
const state = runtime.state;
|
|
55
|
+
const sameTurn = state.turnDecision === undefined;
|
|
56
|
+
const turn = runtime.goalTurn?.turn ?? state.turns;
|
|
57
|
+
const generation = runtime.lifecycleGeneration;
|
|
58
|
+
let resumed: GoalState;
|
|
43
59
|
try {
|
|
44
|
-
transitionGoal(pi, runtime, "resume", "active", undefined, {
|
|
60
|
+
resumed = transitionGoal(pi, runtime, "resume", "active", undefined, {
|
|
61
|
+
...(sameTurn ? { resumeInterruptedTurn: true as const } : {}),
|
|
62
|
+
preserveTurnAuthorization: true,
|
|
63
|
+
});
|
|
45
64
|
} catch (error) {
|
|
46
65
|
runtime.persistenceRetryNeeded = true;
|
|
47
66
|
reportError(ctx, "Manual compaction succeeded, but the goal could not be resumed", error);
|
|
@@ -49,12 +68,24 @@ function recoverGoalAfterManualCompaction(
|
|
|
49
68
|
}
|
|
50
69
|
runtime.continuationScheduled = false;
|
|
51
70
|
runtime.automaticCompaction = undefined;
|
|
52
|
-
|
|
53
|
-
|
|
71
|
+
if (sameTurn) {
|
|
72
|
+
runtime.goalTurn = { turn, revision: runtime.state?.revision ?? state.revision };
|
|
73
|
+
runtime.goalTurnInFlight = true;
|
|
74
|
+
runtime.agentEndObserved = false;
|
|
75
|
+
if (ctx.mode !== "tui") ctx.ui.notify("Manual compaction complete. The interrupted goal turn resumed.", "info");
|
|
76
|
+
setImmediate(() => {
|
|
77
|
+
if (runtime.lifecycleGeneration !== generation || runtime.state !== resumed) return;
|
|
78
|
+
resumeInterruptedGoalTurn(pi, runtime, ctx);
|
|
79
|
+
});
|
|
80
|
+
} else {
|
|
81
|
+
runtime.goalTurn = undefined;
|
|
82
|
+
runtime.goalTurnInFlight = false;
|
|
83
|
+
if (scheduleGoalContinuation(pi, runtime, ctx) && ctx.mode !== "tui") ctx.ui.notify("Manual compaction complete. Goal resumed.", "info");
|
|
84
|
+
}
|
|
54
85
|
return true;
|
|
55
86
|
}
|
|
56
87
|
|
|
57
|
-
/** Resumes the paused revision after
|
|
88
|
+
/** Resumes the paused revision only after compaction and the interrupted turn have both settled. */
|
|
58
89
|
function finalizeAutomaticCompaction(
|
|
59
90
|
pi: ExtensionAPI,
|
|
60
91
|
runtime: GoalRuntime,
|
|
@@ -62,24 +93,48 @@ function finalizeAutomaticCompaction(
|
|
|
62
93
|
): void {
|
|
63
94
|
const recovery = runtime.automaticCompaction;
|
|
64
95
|
if (!recovery || recovery.outcome === "pending" || !recovery.turnSettled) return;
|
|
65
|
-
const skipped = recovery.outcome === "skipped";
|
|
66
|
-
runtime.automaticCompaction = undefined;
|
|
67
96
|
if (runtime.state?.status !== "paused"
|
|
68
|
-
|| runtime.state.revision !== recovery.pausedRevision)
|
|
97
|
+
|| runtime.state.revision !== recovery.pausedRevision) {
|
|
98
|
+
runtime.automaticCompaction = undefined;
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
const state = runtime.state;
|
|
102
|
+
const sameTurn = recovery.resumeSameTurn;
|
|
103
|
+
const generation = runtime.lifecycleGeneration;
|
|
104
|
+
runtime.automaticCompaction = undefined;
|
|
105
|
+
let resumed: GoalState;
|
|
69
106
|
try {
|
|
70
|
-
transitionGoal(pi, runtime, "resume", "active", undefined, {
|
|
107
|
+
resumed = transitionGoal(pi, runtime, "resume", "active", undefined, {
|
|
108
|
+
...(sameTurn ? { resumeInterruptedTurn: true as const } : {}),
|
|
109
|
+
...(!sameTurn && state.turnDecision !== undefined ? { preserveTurnAuthorization: true as const } : {}),
|
|
110
|
+
});
|
|
71
111
|
} catch (error) {
|
|
72
112
|
runtime.persistenceRetryNeeded = true;
|
|
73
|
-
reportError(ctx,
|
|
74
|
-
? "Automatic compaction
|
|
113
|
+
reportError(ctx, sameTurn
|
|
114
|
+
? "Automatic compaction succeeded, but the interrupted goal turn could not be resumed"
|
|
75
115
|
: "Automatic compaction succeeded, but the goal could not be resumed", error);
|
|
76
116
|
return;
|
|
77
117
|
}
|
|
78
118
|
runtime.continuationScheduled = false;
|
|
79
|
-
|
|
119
|
+
if (sameTurn) {
|
|
120
|
+
runtime.goalTurn = { turn: recovery.turn, revision: runtime.state?.revision ?? state.revision };
|
|
121
|
+
runtime.goalTurnInFlight = false;
|
|
122
|
+
runtime.agentEndObserved = false;
|
|
123
|
+
setImmediate(() => {
|
|
124
|
+
if (runtime.lifecycleGeneration !== generation
|
|
125
|
+
|| runtime.state !== resumed
|
|
126
|
+
|| runtime.goalTurn?.turn !== recovery.turn) return;
|
|
127
|
+
runtime.goalTurnInFlight = true;
|
|
128
|
+
resumeInterruptedGoalTurn(pi, runtime, ctx);
|
|
129
|
+
});
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
runtime.goalTurn = undefined;
|
|
133
|
+
runtime.goalTurnInFlight = false;
|
|
134
|
+
if (pauseGoalAtTurnLimit(pi, runtime, ctx)) return;
|
|
135
|
+
setImmediate(() => scheduleGoalContinuation(pi, runtime, ctx, { generation, state: resumed }));
|
|
80
136
|
}
|
|
81
137
|
|
|
82
|
-
/** Records Pi's successful compaction callback and attempts guarded recovery. */
|
|
83
138
|
function completeAutomaticCompaction(
|
|
84
139
|
pi: ExtensionAPI,
|
|
85
140
|
runtime: GoalRuntime,
|
|
@@ -90,7 +145,6 @@ function completeAutomaticCompaction(
|
|
|
90
145
|
finalizeAutomaticCompaction(pi, runtime, ctx);
|
|
91
146
|
}
|
|
92
147
|
|
|
93
|
-
/** Records Pi's expected session-too-small rejection and resumes without claiming compaction succeeded. */
|
|
94
148
|
function skipAutomaticCompaction(
|
|
95
149
|
pi: ExtensionAPI,
|
|
96
150
|
runtime: GoalRuntime,
|
|
@@ -101,7 +155,6 @@ function skipAutomaticCompaction(
|
|
|
101
155
|
finalizeAutomaticCompaction(pi, runtime, ctx);
|
|
102
156
|
}
|
|
103
157
|
|
|
104
|
-
/** Consumes automatic recovery and records its failure on the eligible paused goal. */
|
|
105
158
|
function stopAutomaticCompactionRecovery(
|
|
106
159
|
pi: ExtensionAPI,
|
|
107
160
|
runtime: GoalRuntime,
|
|
@@ -110,13 +163,17 @@ function stopAutomaticCompactionRecovery(
|
|
|
110
163
|
): void {
|
|
111
164
|
const recovery = runtime.automaticCompaction;
|
|
112
165
|
runtime.automaticCompaction = undefined;
|
|
113
|
-
const safeReason =
|
|
166
|
+
const safeReason = boundGoalText(reason) || "automatic compaction failed";
|
|
114
167
|
if (runtime.state?.status !== "paused" || runtime.state.revision !== recovery?.pausedRevision) return;
|
|
115
168
|
try {
|
|
116
169
|
transitionGoal(pi, runtime, "error", "paused", safeReason);
|
|
117
170
|
} catch {
|
|
118
|
-
runtime.state =
|
|
171
|
+
runtime.state = pauseGoalState(runtime.state, safeReason, Date.now());
|
|
172
|
+
syncGoalUpdateTool(pi, runtime);
|
|
119
173
|
runtime.persistenceRetryNeeded = true;
|
|
174
|
+
runtime.continuationScheduled = false;
|
|
175
|
+
runtime.goalTurn = undefined;
|
|
176
|
+
runtime.goalTurnInFlight = false;
|
|
120
177
|
runtime.requestRender?.();
|
|
121
178
|
}
|
|
122
179
|
ctx.ui.notify(
|
|
@@ -125,7 +182,6 @@ function stopAutomaticCompactionRecovery(
|
|
|
125
182
|
);
|
|
126
183
|
}
|
|
127
184
|
|
|
128
|
-
/** Leaves the goal paused when Pi rejects automatic compaction. */
|
|
129
185
|
function failAutomaticCompaction(
|
|
130
186
|
pi: ExtensionAPI,
|
|
131
187
|
runtime: GoalRuntime,
|
|
@@ -142,6 +198,23 @@ function failAutomaticCompaction(
|
|
|
142
198
|
stopAutomaticCompactionRecovery(pi, runtime, ctx, `automatic compaction failed: ${reason}`);
|
|
143
199
|
}
|
|
144
200
|
|
|
201
|
+
function settleGoalTurn(
|
|
202
|
+
pi: ExtensionAPI,
|
|
203
|
+
runtime: GoalRuntime,
|
|
204
|
+
ctx: ExtensionContext,
|
|
205
|
+
settledTurn: number,
|
|
206
|
+
): void {
|
|
207
|
+
const state = runtime.state;
|
|
208
|
+
if (state?.status !== "active") return;
|
|
209
|
+
const decision = state.turnDecision;
|
|
210
|
+
if (!decision || decision.turn !== settledTurn) {
|
|
211
|
+
pauseGoalAfterFailure(pi, runtime, ctx, "no turn decision");
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
if (pauseGoalAtTurnLimit(pi, runtime, ctx)) return;
|
|
215
|
+
scheduleGoalContinuation(pi, runtime, ctx);
|
|
216
|
+
}
|
|
217
|
+
|
|
145
218
|
export function registerGoalSettlement(
|
|
146
219
|
pi: ExtensionAPI,
|
|
147
220
|
runtime: GoalRuntime,
|
|
@@ -150,6 +223,7 @@ export function registerGoalSettlement(
|
|
|
150
223
|
const wasGoalTurn = runtime.goalTurnInFlight;
|
|
151
224
|
const continuationWasScheduled = runtime.continuationScheduled;
|
|
152
225
|
const agentEndObserved = runtime.agentEndObserved;
|
|
226
|
+
const settledTurn = runtime.goalTurn?.turn;
|
|
153
227
|
runtime.goalTurnInFlight = false;
|
|
154
228
|
runtime.agentEndObserved = false;
|
|
155
229
|
runtime.continuationScheduled = false;
|
|
@@ -159,6 +233,12 @@ export function registerGoalSettlement(
|
|
|
159
233
|
const error = safeTerminalText(runtime.lastError ?? "");
|
|
160
234
|
runtime.lastStopReason = undefined;
|
|
161
235
|
runtime.lastError = undefined;
|
|
236
|
+
if (runtime.automaticCompaction.turnSettled
|
|
237
|
+
&& (settledTurn === undefined || settledTurn === runtime.automaticCompaction.turn)) {
|
|
238
|
+
// The interrupted turn already settled; a repeated settlement carries no new
|
|
239
|
+
// information, so keep waiting for compaction instead of failing the recovery.
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
162
242
|
const expectedInterruption = stopReason === "aborted"
|
|
163
243
|
|| stopReason === "error" && error === "This operation was aborted";
|
|
164
244
|
if (!wasGoalTurn || !agentEndObserved) {
|
|
@@ -174,7 +254,12 @@ export function registerGoalSettlement(
|
|
|
174
254
|
);
|
|
175
255
|
return;
|
|
176
256
|
}
|
|
177
|
-
runtime.automaticCompaction.
|
|
257
|
+
if (settledTurn !== undefined && runtime.automaticCompaction.turn === settledTurn) {
|
|
258
|
+
runtime.automaticCompaction.turnSettled = true;
|
|
259
|
+
} else {
|
|
260
|
+
stopAutomaticCompactionRecovery(pi, runtime, ctx, "the goal turn identity changed during compaction");
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
178
263
|
finalizeAutomaticCompaction(pi, runtime, ctx);
|
|
179
264
|
return;
|
|
180
265
|
}
|
|
@@ -182,11 +267,16 @@ export function registerGoalSettlement(
|
|
|
182
267
|
if (!wasGoalTurn || runtime.state?.status !== "active") {
|
|
183
268
|
if (continuationWasScheduled && runtime.state?.status === "active") {
|
|
184
269
|
pauseGoalAfterFailure(pi, runtime, ctx, "the goal continuation ended before an agent turn started");
|
|
185
|
-
} else if (runtime.state?.status === "active"
|
|
270
|
+
} else if (runtime.state?.status === "active"
|
|
271
|
+
&& (runtime.state.turnPhase === "ready" || runtime.state.turnPhase === "authorized")) {
|
|
186
272
|
scheduleGoalContinuation(pi, runtime, ctx);
|
|
187
273
|
}
|
|
188
274
|
return;
|
|
189
275
|
}
|
|
276
|
+
if (settledTurn === undefined) {
|
|
277
|
+
pauseGoalAfterFailure(pi, runtime, ctx, "the goal turn identity was lost");
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
190
280
|
if (!agentEndObserved) {
|
|
191
281
|
pauseGoalAfterFailure(pi, runtime, ctx, "the goal turn ended without an agent result");
|
|
192
282
|
return;
|
|
@@ -207,8 +297,7 @@ export function registerGoalSettlement(
|
|
|
207
297
|
}
|
|
208
298
|
runtime.lastStopReason = undefined;
|
|
209
299
|
runtime.lastError = undefined;
|
|
210
|
-
|
|
211
|
-
scheduleGoalContinuation(pi, runtime, ctx);
|
|
300
|
+
settleGoalTurn(pi, runtime, ctx, settledTurn);
|
|
212
301
|
});
|
|
213
302
|
|
|
214
303
|
pi.on("session_compact", (event, ctx) => {
|
|
@@ -223,21 +312,31 @@ export function registerGoalSettlement(
|
|
|
223
312
|
&& runtime.state?.status === "active",
|
|
224
313
|
onRequested: (): void => {
|
|
225
314
|
if (runtime.state?.status !== "active") return;
|
|
315
|
+
const current = runtime.state;
|
|
316
|
+
// A turn that already accepted continue or a blocker audit owns a pending
|
|
317
|
+
// authorization; recovery must take the next-turn path, never resume the decided turn.
|
|
318
|
+
const resumeSameTurn = runtime.goalTurnInFlight && current.turnDecision === undefined;
|
|
226
319
|
try {
|
|
227
|
-
const paused = transitionGoal(pi, runtime, "pause", "paused"
|
|
320
|
+
const paused = transitionGoal(pi, runtime, "pause", "paused", undefined, {
|
|
321
|
+
keepTurnForRecovery: true,
|
|
322
|
+
preserveTurnAuthorization: true,
|
|
323
|
+
});
|
|
228
324
|
runtime.automaticCompaction = {
|
|
229
325
|
pausedRevision: paused.revision,
|
|
230
326
|
outcome: "pending",
|
|
231
327
|
turnSettled: false,
|
|
328
|
+
turn: current.turns,
|
|
329
|
+
resumeSameTurn,
|
|
232
330
|
};
|
|
233
331
|
} catch (error) {
|
|
234
|
-
const
|
|
235
|
-
const reason = safeTerminalText(`automatic compaction pause could not be saved: ${error instanceof Error ? error.message : String(error)}`);
|
|
332
|
+
const reason = boundGoalText(`automatic compaction pause could not be saved: ${error instanceof Error ? error.message : String(error)}`);
|
|
236
333
|
runtime.state = current ? pauseGoalState(current, reason, Date.now()) : undefined;
|
|
237
334
|
syncGoalUpdateTool(pi, runtime);
|
|
238
335
|
runtime.persistenceRetryNeeded = true;
|
|
239
336
|
runtime.continuationScheduled = false;
|
|
240
337
|
runtime.automaticCompaction = undefined;
|
|
338
|
+
runtime.goalTurn = undefined;
|
|
339
|
+
runtime.goalTurnInFlight = false;
|
|
241
340
|
runtime.requestRender?.();
|
|
242
341
|
throw error;
|
|
243
342
|
}
|