@matthewfl/pi-contemplator 0.1.2 → 0.1.3
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.3",
|
|
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",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"typecheck": "tsc --noEmit",
|
|
42
42
|
"test": "npm run test:unit && npm run test:e2e",
|
|
43
43
|
"test:unit": "vitest run",
|
|
44
|
-
"test:e2e": "node tests/e2e/rpc-contemplator.mjs && node tests/e2e/rpc-summarizer.mjs && node tests/e2e/rpc-delivery.mjs && node tests/e2e/rpc-restore-review.mjs && node tests/e2e/rpc-compaction.mjs && node tests/e2e/rpc-compaction-resilience.mjs && node tests/e2e/rpc-memory-edges.mjs && node tests/e2e/rpc-routing-isolation.mjs"
|
|
44
|
+
"test:e2e": "node tests/e2e/rpc-contemplator.mjs && node tests/e2e/rpc-summarizer.mjs && node tests/e2e/rpc-delivery.mjs && node tests/e2e/rpc-restore-review.mjs && node tests/e2e/rpc-compaction.mjs && node tests/e2e/rpc-compaction-resilience.mjs && node tests/e2e/rpc-memory-edges.mjs && node tests/e2e/rpc-observer-length.mjs && node tests/e2e/rpc-routing-isolation.mjs"
|
|
45
45
|
},
|
|
46
46
|
"peerDependencies": {
|
|
47
47
|
"@earendil-works/pi-agent-core": "*",
|
|
@@ -216,13 +216,17 @@ IMPORTANT: Now call record_observations to record the useful new observations fr
|
|
|
216
216
|
const loop = args.agentLoop ?? agentLoop;
|
|
217
217
|
const history: AgentMessage[] = [];
|
|
218
218
|
let terminalFailure: { stopReason: string; errorMessage?: string } | undefined;
|
|
219
|
-
|
|
219
|
+
let lengthRetryAttempted = false;
|
|
220
|
+
const runInvocation = async (prompt: Message, afterLength = false): Promise<void> => {
|
|
220
221
|
const context: AgentContext = {
|
|
221
222
|
systemPrompt: OBSERVER_SYSTEM,
|
|
222
223
|
messages: history.slice(),
|
|
223
224
|
tools: [recordObservations as AgentTool<any>, doneTool],
|
|
224
225
|
};
|
|
225
|
-
const
|
|
226
|
+
const invocationConfig: AgentLoopConfig = afterLength && reasoning
|
|
227
|
+
? { ...baseConfig, reasoning: "minimal" }
|
|
228
|
+
: baseConfig;
|
|
229
|
+
const stream = loop([prompt], context, invocationConfig, signal, streamSimple);
|
|
226
230
|
for await (const event of stream) {
|
|
227
231
|
args.onProgress?.();
|
|
228
232
|
logAgentStreamError("observer", event);
|
|
@@ -243,6 +247,23 @@ IMPORTANT: Now call record_observations to record the useful new observations fr
|
|
|
243
247
|
};
|
|
244
248
|
|
|
245
249
|
await runInvocation(initialPrompt);
|
|
250
|
+
if (accumulated.size === 0 && terminalFailure?.stopReason === "length") {
|
|
251
|
+
lengthRetryAttempted = true;
|
|
252
|
+
// A provider can impose a lower output ceiling than the advertised model
|
|
253
|
+
// maximum. agentLoop stops on `length` when no tool call was completed; it
|
|
254
|
+
// does not automatically send a continuation request. Preserve the partial
|
|
255
|
+
// response so the model can continue from work it already performed rather
|
|
256
|
+
// than paying to reproduce it, then append a short tool-focused instruction
|
|
257
|
+
// and reduce reasoning to minimal. A second length stop fails forward at the
|
|
258
|
+
// bounded-chunk level.
|
|
259
|
+
terminalFailure = undefined;
|
|
260
|
+
const retryPrompt: Message = {
|
|
261
|
+
role: "user",
|
|
262
|
+
content: [{ type: "text", text: "IMPORTANT: The previous response reached the provider output limit before recording anything. Continue from the work already above and call record_observations now instead of spending another response budget analyzing." }],
|
|
263
|
+
timestamp: Date.now(),
|
|
264
|
+
};
|
|
265
|
+
await runInvocation(retryPrompt, true);
|
|
266
|
+
}
|
|
246
267
|
if (accumulated.size === 0 && !doneCalled && !terminalFailure && rejectedTotal === 0) {
|
|
247
268
|
const reminder: Message = {
|
|
248
269
|
role: "user",
|
|
@@ -257,7 +278,10 @@ IMPORTANT: Now call record_observations to record the useful new observations fr
|
|
|
257
278
|
// zero-observation stop is also a valid empty result after the reminder;
|
|
258
279
|
// actual stream failures, truncation, and malformed records still throw.
|
|
259
280
|
if (accumulated.size === 0 && terminalFailure) {
|
|
260
|
-
|
|
281
|
+
const detail = terminalFailure.stopReason === "length" && lengthRetryAttempted
|
|
282
|
+
? `provider reached the output limit twice without recording an observation (effective max output request: ${baseConfig.maxTokens} tokens)`
|
|
283
|
+
: terminalFailure.errorMessage;
|
|
284
|
+
throw new ObserverStreamError(terminalFailure.stopReason, detail);
|
|
261
285
|
}
|
|
262
286
|
if (accumulated.size === 0 && rejectedTotal > 0) {
|
|
263
287
|
throw new ObserverStreamError("invalid_observations", `${rejectedTotal} proposed observation${rejectedTotal === 1 ? " was" : "s were"} rejected`);
|
|
@@ -5,6 +5,7 @@ import { debugLog, withDebugLogContext } from "../debug-log.js";
|
|
|
5
5
|
import { resolveObserverChunkMaxTokens } from "../config.js";
|
|
6
6
|
import type { ResolveResult, Runtime } from "../runtime.js";
|
|
7
7
|
import { createWorkerStallWatchdog } from "../worker-watchdog.js";
|
|
8
|
+
import { boundedMaxTokens, OBSERVER_AGENT_LOOP_MAX_TOKENS } from "../model-budget.js";
|
|
8
9
|
import { serializeSourceAddressedBranchEntries } from "../serialize.js";
|
|
9
10
|
import {
|
|
10
11
|
OM_SUMMARIZER_COMMIT,
|
|
@@ -466,6 +467,9 @@ async function runObserverStage(
|
|
|
466
467
|
debugLog("observer.start", {
|
|
467
468
|
tokens,
|
|
468
469
|
chunkTokens,
|
|
470
|
+
requestedMaxOutputTokens: OBSERVER_AGENT_LOOP_MAX_TOKENS,
|
|
471
|
+
effectiveMaxOutputTokens: boundedMaxTokens(resolved.model as any, OBSERVER_AGENT_LOOP_MAX_TOKENS),
|
|
472
|
+
advertisedModelMaxTokens: (resolved.model as { maxTokens?: number }).maxTokens,
|
|
469
473
|
coversUpToId,
|
|
470
474
|
sourceEntryIds,
|
|
471
475
|
sourceEntryCount: sourceEntryIds.length,
|