@matthewfl/pi-contemplator 0.1.16 → 0.1.18
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@matthewfl/pi-contemplator",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.18",
|
|
4
4
|
"description": "A Pi extension that keeps long-running agentic sessions on track with background memory, contemplation, and structural review.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -74,6 +74,8 @@ const RecordObservationsSchema = Type.Object({
|
|
|
74
74
|
|
|
75
75
|
type RecordObservationsArgs = Static<typeof RecordObservationsSchema>;
|
|
76
76
|
|
|
77
|
+
export const OBSERVER_MAX_LENGTH_ATTEMPTS = 4;
|
|
78
|
+
|
|
77
79
|
/** A terminal provider/agent-loop failure that must not advance observation coverage. */
|
|
78
80
|
export class ObserverStreamError extends Error {
|
|
79
81
|
readonly stopReason: string;
|
|
@@ -218,7 +220,7 @@ IMPORTANT: Now call record_observations to record the useful new observations fr
|
|
|
218
220
|
const loop = args.agentLoop ?? agentLoop;
|
|
219
221
|
const history: AgentMessage[] = [];
|
|
220
222
|
let terminalFailure: { stopReason: string; errorMessage?: string } | undefined;
|
|
221
|
-
let
|
|
223
|
+
let lengthAttempts = 0;
|
|
222
224
|
const runInvocation = async (prompt: Message, afterLength = false): Promise<void> => {
|
|
223
225
|
const context: AgentContext = {
|
|
224
226
|
systemPrompt: OBSERVER_SYSTEM,
|
|
@@ -265,8 +267,8 @@ IMPORTANT: Now call record_observations to record the useful new observations fr
|
|
|
265
267
|
};
|
|
266
268
|
|
|
267
269
|
await runInvocation(initialPrompt);
|
|
268
|
-
if (
|
|
269
|
-
|
|
270
|
+
if (terminalFailure?.stopReason === "length") lengthAttempts = 1;
|
|
271
|
+
while (accumulated.size === 0 && terminalFailure?.stopReason === "length" && lengthAttempts < OBSERVER_MAX_LENGTH_ATTEMPTS) {
|
|
270
272
|
// A provider can impose a lower output ceiling than the advertised model
|
|
271
273
|
// maximum. agentLoop stops on `length` when no tool call was completed; it
|
|
272
274
|
// does not automatically send a continuation request. Preserve the partial
|
|
@@ -275,7 +277,8 @@ IMPORTANT: Now call record_observations to record the useful new observations fr
|
|
|
275
277
|
// assistant text at the LLM boundary because some provider templates strip
|
|
276
278
|
// historical reasoning; encrypted thinking retains its opaque structure.
|
|
277
279
|
// Then append a short tool-focused instruction and reduce reasoning to
|
|
278
|
-
// minimal.
|
|
280
|
+
// minimal. The bounded attempt limit prevents pathological chunks from
|
|
281
|
+
// consuming background tokens forever.
|
|
279
282
|
terminalFailure = undefined;
|
|
280
283
|
const retryPrompt: Message = {
|
|
281
284
|
role: "user",
|
|
@@ -283,6 +286,10 @@ IMPORTANT: Now call record_observations to record the useful new observations fr
|
|
|
283
286
|
timestamp: Date.now(),
|
|
284
287
|
};
|
|
285
288
|
await runInvocation(retryPrompt, true);
|
|
289
|
+
// runInvocation mutates this through its async stream callbacks; TypeScript
|
|
290
|
+
// cannot infer that mutation after the explicit reset above.
|
|
291
|
+
const retryFailure = terminalFailure as { stopReason: string; errorMessage?: string } | undefined;
|
|
292
|
+
if (retryFailure?.stopReason === "length") lengthAttempts++;
|
|
286
293
|
}
|
|
287
294
|
if (accumulated.size === 0 && !doneCalled && !terminalFailure && rejectedTotal === 0) {
|
|
288
295
|
const reminder: Message = {
|
|
@@ -298,8 +305,8 @@ IMPORTANT: Now call record_observations to record the useful new observations fr
|
|
|
298
305
|
// zero-observation stop is also a valid empty result after the reminder;
|
|
299
306
|
// actual stream failures, truncation, and malformed records still throw.
|
|
300
307
|
if (accumulated.size === 0 && terminalFailure) {
|
|
301
|
-
const detail = terminalFailure.stopReason === "length" &&
|
|
302
|
-
? `provider reached the output limit
|
|
308
|
+
const detail = terminalFailure.stopReason === "length" && lengthAttempts > 0
|
|
309
|
+
? `provider reached the output limit ${lengthAttempts} times without recording an observation (effective max output request: ${baseConfig.maxTokens} tokens)`
|
|
303
310
|
: terminalFailure.errorMessage;
|
|
304
311
|
throw new ObserverStreamError(terminalFailure.stopReason, detail);
|
|
305
312
|
}
|
|
@@ -20,12 +20,11 @@ export function createCompactContextTool(runtime: Runtime) {
|
|
|
20
20
|
promptGuidelines: [
|
|
21
21
|
"Use compact_context sparingly when either substantial additional work remains and there is not enough context left to complete it, or accumulated past context has become noisy, stale, or distracting enough that you are struggling to focus on the current task or reason about it reliably.",
|
|
22
22
|
"Do not use compact_context routinely, for short tasks, or merely because the conversation is long; use it for genuine context-capacity pressure or context degradation that is interfering with the work.",
|
|
23
|
-
"Call compact_context by itself
|
|
23
|
+
"Call compact_context by itself and provide short_continuation_prompt with concrete instructions for the next agent step. The tool ends the current turn automatically; pi-contemplator will compact the context and resume with those instructions, so do not add a separate response after the tool call.",
|
|
24
24
|
],
|
|
25
25
|
parameters: Type.Object({
|
|
26
26
|
short_continuation_prompt: Type.String({
|
|
27
27
|
minLength: 1,
|
|
28
|
-
maxLength: 1_000,
|
|
29
28
|
pattern: "\\S",
|
|
30
29
|
description: "Short, concrete instructions to your post-compaction self describing the next action and any critical immediate constraint. Do not summarize the whole conversation.",
|
|
31
30
|
}),
|
|
@@ -33,14 +32,14 @@ export function createCompactContextTool(runtime: Runtime) {
|
|
|
33
32
|
async execute(_toolCallId, params) {
|
|
34
33
|
if (runtime.compactInFlight) {
|
|
35
34
|
return {
|
|
36
|
-
content: [{ type: "text" as const, text: "Context compaction is already in progress.
|
|
35
|
+
content: [{ type: "text" as const, text: "Context compaction is already in progress. This turn is ending automatically; wait for automatic resume." }],
|
|
37
36
|
details: { status: "in_progress" } as CompactContextDetails,
|
|
38
37
|
terminate: true,
|
|
39
38
|
};
|
|
40
39
|
}
|
|
41
40
|
if (runtime.compactRequested) {
|
|
42
41
|
return {
|
|
43
|
-
content: [{ type: "text" as const, text: "Context compaction is already scheduled.
|
|
42
|
+
content: [{ type: "text" as const, text: "Context compaction is already scheduled. This turn is ending automatically; wait for automatic resume." }],
|
|
44
43
|
details: { status: "already_pending" } as CompactContextDetails,
|
|
45
44
|
terminate: true,
|
|
46
45
|
};
|
|
@@ -49,7 +48,7 @@ export function createCompactContextTool(runtime: Runtime) {
|
|
|
49
48
|
runtime.compactRequested = true;
|
|
50
49
|
runtime.compactContinuationPrompt = params.short_continuation_prompt.trim();
|
|
51
50
|
return {
|
|
52
|
-
content: [{ type: "text" as const, text: "Context compaction scheduled.
|
|
51
|
+
content: [{ type: "text" as const, text: "Context compaction scheduled. This turn is ending automatically, and the task will resume after compaction." }],
|
|
53
52
|
details: { status: "scheduled" } as CompactContextDetails,
|
|
54
53
|
terminate: true,
|
|
55
54
|
};
|