@matthewfl/pi-contemplator 0.1.16 → 0.1.17

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.16",
3
+ "version": "0.1.17",
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 lengthRetryAttempted = false;
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 (accumulated.size === 0 && terminalFailure?.stopReason === "length") {
269
- lengthRetryAttempted = true;
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. A second length stop fails forward at the bounded-chunk level.
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" && lengthRetryAttempted
302
- ? `provider reached the output limit twice without recording an observation (effective max output request: ${baseConfig.maxTokens} tokens)`
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
  }