@matthewfl/pi-contemplator 0.1.13 → 0.1.14
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.14",
|
|
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",
|
|
@@ -106,6 +106,27 @@ function textResult(text: string, details: Record<string, unknown> = {}, termina
|
|
|
106
106
|
return { content: [{ type: "text" as const, text }], details, ...(terminate ? { terminate: true } : {}) };
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
+
/**
|
|
110
|
+
* Many provider chat templates discard historical reasoning blocks. Preserve
|
|
111
|
+
* unfinished plaintext work after an output-length stop by replaying it as
|
|
112
|
+
* ordinary assistant text; unlike provider-specific thinking metadata, text
|
|
113
|
+
* survives every supported conversation serializer. Redacted/encrypted blocks
|
|
114
|
+
* must remain structured so their opaque provider payload stays replayable.
|
|
115
|
+
* The durable/in-memory transcript remains unchanged—this transformation is
|
|
116
|
+
* only applied at the LLM boundary.
|
|
117
|
+
*/
|
|
118
|
+
export function replayTruncatedThinkingAsText(messages: readonly AgentMessage[]): Message[] {
|
|
119
|
+
return messages.map((message) => {
|
|
120
|
+
if (message.role !== "assistant" || message.stopReason !== "length" || !message.content.some((part) => part.type === "thinking" && !part.redacted)) return message as Message;
|
|
121
|
+
return {
|
|
122
|
+
...message,
|
|
123
|
+
content: message.content.map((part) => part.type === "thinking" && !part.redacted
|
|
124
|
+
? { type: "text" as const, text: `[Incomplete analysis from the preceding truncated response]\n${part.thinking}` }
|
|
125
|
+
: part),
|
|
126
|
+
} as Message;
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
109
130
|
function preview(content: string): string {
|
|
110
131
|
const compact = content.replace(/\s+/g, " ").trim();
|
|
111
132
|
return compact.length <= 100 ? compact : `${compact.slice(0, 100)}…`;
|
|
@@ -540,7 +561,7 @@ export async function runSummarizer(args: RunSummarizerArgs): Promise<Summarizer
|
|
|
540
561
|
apiKey: args.apiKey,
|
|
541
562
|
headers: args.headers,
|
|
542
563
|
maxTokens: boundedMaxTokens(args.model, maxOutputTokens),
|
|
543
|
-
convertToLlm:
|
|
564
|
+
convertToLlm: replayTruncatedThinkingAsText,
|
|
544
565
|
toolExecution: "sequential",
|
|
545
566
|
beforeToolCall: async ({ toolCall, context: toolContext }) => {
|
|
546
567
|
if (toolCall.name !== "done") return undefined;
|
|
@@ -597,7 +618,7 @@ export async function runSummarizer(args: RunSummarizerArgs): Promise<Summarizer
|
|
|
597
618
|
let stopReason = await runOnce("The preceding summarize call and receipt are an illustrative example only. Its placeholder ids are not real and it did not create a summary. Now pick any five coherent groups of actual memories worth combining and use summarize to create about five summary memories. Do not rank the whole pool; the first five worthwhile groups are good enough, and you will have more chances afterward. Record a summary as soon as it looks reasonable rather than drafting all five in prose or thinking. If you create one in prose or thinking, record it with summarize immediately. Memories not combined remain verbatim. If fewer than five are worthwhile, create only those; if none are safe, call done.", false);
|
|
598
619
|
for (let invocation = 1; !completedWithDone && invocation < SUMMARIZER_MAX_INVOCATIONS; invocation++) {
|
|
599
620
|
const continuingTruncatedResponse = stopReason === "length";
|
|
600
|
-
stopReason = await runOnce(continuingTruncatedResponse ? "Continue working." : summarizerContinue(drafts.size, invocation), !continuingTruncatedResponse);
|
|
621
|
+
stopReason = await runOnce(continuingTruncatedResponse ? "Continue working from the incomplete analysis above. Do not restart it." : summarizerContinue(drafts.size, invocation), !continuingTruncatedResponse);
|
|
601
622
|
}
|
|
602
623
|
} catch (error) {
|
|
603
624
|
debugLog("summarizer.error", { error: error instanceof Error ? error.message : String(error), acceptedSummaries: drafts.size });
|