@matthewfl/pi-contemplator 0.0.2 → 0.0.4
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/README.md +1 -1
- package/package.json +1 -1
- package/src/agents/contemplator/agent.ts +46 -13
- package/src/agents/reviewer/agent.ts +28 -10
- package/src/commands/settings.ts +9 -2
- package/src/commands/status.ts +1 -0
- package/src/config.ts +4 -0
- package/src/runtime.ts +2 -2
package/README.md
CHANGED
|
@@ -96,7 +96,7 @@ pi -e ./src/index.ts
|
|
|
96
96
|
|
|
97
97
|
## Configuration and commands
|
|
98
98
|
|
|
99
|
-
The plugin works with its defaults, including the contemplator and reviewer. Model selection, trigger thresholds, passive mode, compaction behavior, and other settings are documented in [docs/configuration.md](docs/configuration.md).
|
|
99
|
+
The plugin works with its defaults, including the contemplator and reviewer. Contemplator probes and review notices appear as purple cards in the chat by default; use `/om:settings messages off` to hide newly sent cards without stopping their delivery to the agent. Model selection, trigger thresholds, passive mode, compaction behavior, and other settings are documented in [docs/configuration.md](docs/configuration.md).
|
|
100
100
|
|
|
101
101
|
Useful commands include:
|
|
102
102
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@matthewfl/pi-contemplator",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.4",
|
|
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",
|
|
@@ -4,6 +4,7 @@ import type { Static } from "typebox";
|
|
|
4
4
|
import { streamSimple } from "@earendil-works/pi-ai/compat";
|
|
5
5
|
import { generateSummaryWithUsage } from "@earendil-works/pi-coding-agent";
|
|
6
6
|
import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
7
|
+
import { Box, Text } from "@earendil-works/pi-tui";
|
|
7
8
|
import { assistantOutputTokens, assistantToolCallCount, fullProjection, isReviewRequestEntry, isReviewResultEntry, OM_REVIEWER_MESSAGE, OM_REVIEWER_NOTICE, OM_REVIEWER_STATE, OM_REVIEW_REQUEST, OM_REVIEW_RESULT, type Entry, type ReviewResult, type StructuralReviewRequest } from "../../session-ledger/index.js";
|
|
8
9
|
import { hashId } from "../../ids.js";
|
|
9
10
|
import { createSearchMemoriesAgentTool } from "../../tools/search-memories.js";
|
|
@@ -80,9 +81,19 @@ function reviewSummaryLine(review: ReviewResult): string {
|
|
|
80
81
|
function reviewRequestKey(request: RequestReviewArgs): string {
|
|
81
82
|
return `${request.scope}:${hashId(`${request.evidence}\n${request.concern}`)}`;
|
|
82
83
|
}
|
|
84
|
+
|
|
85
|
+
function customMessageText(content: unknown): string {
|
|
86
|
+
if (typeof content === "string") return content;
|
|
87
|
+
if (!Array.isArray(content)) return "";
|
|
88
|
+
return content
|
|
89
|
+
.map((block) => block && typeof block === "object" && "text" in block && typeof block.text === "string" ? block.text : "")
|
|
90
|
+
.filter(Boolean)
|
|
91
|
+
.join("\n");
|
|
92
|
+
}
|
|
83
93
|
const CONTEMPLATOR_MESSAGE = "om.contemplator.message";
|
|
84
94
|
const CONTEMPLATOR_STATE = "om.contemplator.state";
|
|
85
95
|
const CONTEMPLATOR_SUGGESTION = "om.contemplator.suggestion";
|
|
96
|
+
const REVIEW_PROPOSAL_MESSAGE = "om.review.proposal";
|
|
86
97
|
const SendProbeSchema = Type.Object({ question: Type.String({ minLength: 1, description: "One concise, memory-grounded probing question, optionally preceded by one short sentence of context. Cite relevant memory identifiers." }) });
|
|
87
98
|
const ReviewScopeSchema = Type.Union([Type.Literal("workflow"), Type.Literal("software")]);
|
|
88
99
|
export const RequestReviewSchema = Type.Object({
|
|
@@ -143,7 +154,8 @@ export class Contemplator {
|
|
|
143
154
|
private resumedReviewIds = new Set<string>();
|
|
144
155
|
private reviewerSessions = new Map<string, ReviewerSession>();
|
|
145
156
|
private deliveredProbeIds = new Set<string>();
|
|
146
|
-
|
|
157
|
+
/** Probe ids passed to pi.sendMessage by this live extension runtime. */
|
|
158
|
+
private queuedProbeIds = new Set<string>();
|
|
147
159
|
private sessionGeneration = 0;
|
|
148
160
|
private latestCtx: MemoryUpdateCtx | undefined;
|
|
149
161
|
private turnsSinceRun = 0;
|
|
@@ -152,13 +164,31 @@ export class Contemplator {
|
|
|
152
164
|
constructor(private readonly pi: ExtensionAPI, private readonly runtime: Runtime) {}
|
|
153
165
|
|
|
154
166
|
register(): void {
|
|
167
|
+
this.pi.registerMessageRenderer(CONTEMPLATOR_SUGGESTION, (message, _options, theme) => {
|
|
168
|
+
const content = customMessageText(message.content).replace(/^Background contemplator probe \(advisory\):\n?/, "");
|
|
169
|
+
const box = new Box(1, 1, (text) => theme.bg("customMessageBg", text));
|
|
170
|
+
box.addChild(new Text(theme.fg("thinkingHigh", `${theme.bold("◆ CONTEMPLATOR PROBE")}\n${content}`), 0, 0));
|
|
171
|
+
return box;
|
|
172
|
+
});
|
|
173
|
+
this.pi.registerMessageRenderer(REVIEW_PROPOSAL_MESSAGE, (message, _options, theme) => {
|
|
174
|
+
const box = new Box(1, 1, (text) => theme.bg("customMessageBg", text));
|
|
175
|
+
box.addChild(new Text(theme.fg("thinkingHigh", `${theme.bold("◆ CONTEMPLATOR REVIEW")}\n${customMessageText(message.content)}`), 0, 0));
|
|
176
|
+
return box;
|
|
177
|
+
});
|
|
155
178
|
this.runtime.setMemoryUpdateListener((ctx) => this.withDebugContext(ctx, () => this.observeTurn(ctx)));
|
|
156
|
-
|
|
179
|
+
this.pi.on("session_start", (event: any, ctx: ExtensionContext) => {
|
|
157
180
|
this.sessionGeneration++;
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
181
|
+
// AgentSession preserves its steering queue across extension reloads. An
|
|
182
|
+
// undelivered tracking entry therefore still has a live queued message;
|
|
183
|
+
// restoring it here would enqueue the same probe a second time.
|
|
184
|
+
const reload = event?.reason === "reload";
|
|
185
|
+
this.restore(ctx, true, reload, reload);
|
|
186
|
+
});
|
|
187
|
+
this.pi.on("session_tree", (_event: any, ctx: ExtensionContext) => {
|
|
188
|
+
this.sessionGeneration++;
|
|
189
|
+
// Pending steering messages remain queued while navigating the tree.
|
|
190
|
+
this.restore(ctx, true, true);
|
|
191
|
+
});
|
|
162
192
|
this.pi.on("session_shutdown", () => {
|
|
163
193
|
this.sessionGeneration++;
|
|
164
194
|
this.history = [];
|
|
@@ -172,7 +202,7 @@ export class Contemplator {
|
|
|
172
202
|
this.resumedReviewIds.clear();
|
|
173
203
|
this.reviewerSessions.clear();
|
|
174
204
|
this.deliveredProbeIds.clear();
|
|
175
|
-
this.
|
|
205
|
+
this.queuedProbeIds.clear();
|
|
176
206
|
this.latestCtx = undefined;
|
|
177
207
|
this.turnsSinceRun = 0;
|
|
178
208
|
this.restoredTipId = undefined;
|
|
@@ -220,7 +250,7 @@ export class Contemplator {
|
|
|
220
250
|
}, fn);
|
|
221
251
|
}
|
|
222
252
|
|
|
223
|
-
private restore(ctx: MemoryUpdateCtx, resetTracking = false): void {
|
|
253
|
+
private restore(ctx: MemoryUpdateCtx, resetTracking = false, retainQueuedIds = false, skipUndeliveredRestore = false): void {
|
|
224
254
|
this.latestCtx = ctx;
|
|
225
255
|
const entries = ctx.sessionManager.getBranch() as Entry[];
|
|
226
256
|
const tipId = entries.at(-1)?.id;
|
|
@@ -229,7 +259,7 @@ export class Contemplator {
|
|
|
229
259
|
this.history = [];
|
|
230
260
|
if (resetTracking) {
|
|
231
261
|
this.deliveredProbeIds.clear();
|
|
232
|
-
this.
|
|
262
|
+
if (!retainQueuedIds) this.queuedProbeIds.clear();
|
|
233
263
|
this.inFlightReviewIds.clear();
|
|
234
264
|
this.resolvingReviewIds.clear();
|
|
235
265
|
this.resumedReviewIds.clear();
|
|
@@ -312,8 +342,7 @@ export class Contemplator {
|
|
|
312
342
|
}
|
|
313
343
|
this.restoredTipId = tipId;
|
|
314
344
|
for (const [probeId, question] of undeliveredSuggestions) {
|
|
315
|
-
if (queuedProbeIds.has(probeId) || this.
|
|
316
|
-
this.requeuedProbeIds.add(probeId);
|
|
345
|
+
if (skipUndeliveredRestore || queuedProbeIds.has(probeId) || this.queuedProbeIds.has(probeId)) continue;
|
|
317
346
|
this.queueProbe(ctx, question, "restore", probeId);
|
|
318
347
|
}
|
|
319
348
|
if (resetTracking) void this.resumePendingReviews(ctx);
|
|
@@ -568,9 +597,13 @@ export class Contemplator {
|
|
|
568
597
|
this.pi.sendMessage({
|
|
569
598
|
customType: CONTEMPLATOR_SUGGESTION,
|
|
570
599
|
content: `Background contemplator probe (advisory):\n${question}`,
|
|
571
|
-
display:
|
|
600
|
+
display: this.runtime.config.showContemplatorMessages,
|
|
572
601
|
details: { version: 1, question, source, probeId },
|
|
573
602
|
}, { deliverAs: "steer", triggerTurn: false });
|
|
603
|
+
// sendMessage queues synchronously. Mark every source (not only restore)
|
|
604
|
+
// before a later turn_end can rebuild state from the still-undelivered
|
|
605
|
+
// tracking entry and enqueue this probe again.
|
|
606
|
+
this.queuedProbeIds.add(probeId);
|
|
574
607
|
this.pi.appendEntry(CONTEMPLATOR_SUGGESTION, { version: 1, suggestion: question, delivered: false, source, probeId });
|
|
575
608
|
this.markTipPersisted(ctx);
|
|
576
609
|
debugLog("contemplator.suggestion_queued", {
|
|
@@ -646,7 +679,7 @@ export class Contemplator {
|
|
|
646
679
|
debugLog(result.outcome === "proposal" ? "reviewer.proposal_created" : "reviewer.no_proposal", { reviewRequestId: request.id, reviewMemoryId: result.id, scope: result.scope });
|
|
647
680
|
if (result.outcome === "proposal") {
|
|
648
681
|
const notice = `BACKGROUND ${result.scope.toUpperCase()} REVIEW PROPOSAL [${result.id}]\n\n${result.summary}\n\nRecall memory [${result.id}] to read the full conceptual proposal when it is relevant.\n\nThis is advisory. Evaluate it against the actual environment and current work.`;
|
|
649
|
-
this.pi.sendMessage({ customType:
|
|
682
|
+
this.pi.sendMessage({ customType: REVIEW_PROPOSAL_MESSAGE, content: notice, display: this.runtime.config.showContemplatorMessages, details: { version: 1, reviewRequestId: request.id, reviewMemoryId: result.id, scope: result.scope } }, { deliverAs: "steer", triggerTurn: false });
|
|
650
683
|
this.pi.appendEntry(OM_REVIEWER_NOTICE, { version: 1, reviewRequestId: request.id, reviewMemoryId: result.id, scope: result.scope, content: notice });
|
|
651
684
|
this.markTipPersisted(ctx);
|
|
652
685
|
debugLog("reviewer.primary_notice_queued", { reviewRequestId: request.id, reviewMemoryId: result.id });
|
|
@@ -71,6 +71,27 @@ function assistantOutputTokens(messages: AgentMessage[]): number {
|
|
|
71
71
|
return total;
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
+
function completeReview(request: StructuralReviewRequest, terminal: ReviewTerminalResult): ReviewResult {
|
|
75
|
+
return {
|
|
76
|
+
...terminal,
|
|
77
|
+
id: hashId(`${request.id}:${JSON.stringify(terminal)}:${Date.now()}`),
|
|
78
|
+
version: 1,
|
|
79
|
+
reviewRequestId: request.id,
|
|
80
|
+
createdAt: Date.now(),
|
|
81
|
+
requestedBy: "contemplator",
|
|
82
|
+
} as ReviewResult;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function budgetExhaustedResult(request: StructuralReviewRequest): ReviewResult {
|
|
86
|
+
return completeReview(request, {
|
|
87
|
+
outcome: "no_proposal",
|
|
88
|
+
scope: request.scope,
|
|
89
|
+
reason: "The reviewer exhausted its lifetime output-token budget before recording a terminal proposal decision.",
|
|
90
|
+
evidenceReviewed: "The persisted reviewer transcript was retained, but no terminal evidence assessment was recorded before the budget was exhausted.",
|
|
91
|
+
reconsiderIf: "A new review can be requested with a narrower scope or a smaller evidence set.",
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
|
|
74
95
|
export async function runStructuralReview(args: RunStructuralReviewArgs): Promise<ReviewResult | undefined> {
|
|
75
96
|
let terminal: ReviewTerminalResult | undefined;
|
|
76
97
|
const acceptTerminal = (candidate: ReviewTerminalResult): void => {
|
|
@@ -98,7 +119,7 @@ export async function runStructuralReview(args: RunStructuralReviewArgs): Promis
|
|
|
98
119
|
// Usage on persisted assistant messages makes this a lifetime request budget,
|
|
99
120
|
// rather than a fresh allowance on each session/tree resumption.
|
|
100
121
|
let totalOutputTokens = assistantOutputTokens(history);
|
|
101
|
-
if (totalOutputTokens >= REVIEWER_TOTAL_TOKEN_LIMIT) return
|
|
122
|
+
if (totalOutputTokens >= REVIEWER_TOTAL_TOKEN_LIMIT) return budgetExhaustedResult(args.request);
|
|
102
123
|
|
|
103
124
|
// Persist both the user continuation and the returned messages immediately.
|
|
104
125
|
// This makes the transcript sufficient to resume a review after shutdown.
|
|
@@ -175,13 +196,10 @@ export async function runStructuralReview(args: RunStructuralReviewArgs): Promis
|
|
|
175
196
|
progress = await runOnce(keepGoing);
|
|
176
197
|
invocations++;
|
|
177
198
|
}
|
|
178
|
-
if (!terminal)
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
createdAt: Date.now(),
|
|
185
|
-
requestedBy: "contemplator",
|
|
186
|
-
} as ReviewResult;
|
|
199
|
+
if (!terminal) {
|
|
200
|
+
return totalOutputTokens >= REVIEWER_TOTAL_TOKEN_LIMIT
|
|
201
|
+
? budgetExhaustedResult(args.request)
|
|
202
|
+
: undefined;
|
|
203
|
+
}
|
|
204
|
+
return completeReview(args.request, terminal);
|
|
187
205
|
}
|
package/src/commands/settings.ts
CHANGED
|
@@ -10,7 +10,7 @@ type ModelRegistryLike = {
|
|
|
10
10
|
getAll(): Array<{ provider: string; id: string }>;
|
|
11
11
|
};
|
|
12
12
|
type NumberSetting = "observeAfterTokens" | "reflectAfterTokens" | "compactAfterTokens" | "observerChunkMaxTokens" | "observationsPoolMaxTokens" | "observationsPoolTargetTokens" | "agentMaxTurns" | "contemplatorMinNewObservations" | "contemplatorMinNewReflections" | "contemplatorMinTurns";
|
|
13
|
-
type BooleanSetting = "contemplatorEnabled" | "reviewerEnabled" | "compactionObserverEnabled" | "showWorkerNotifications" | "passive" | "debugLog";
|
|
13
|
+
type BooleanSetting = "contemplatorEnabled" | "showContemplatorMessages" | "reviewerEnabled" | "compactionObserverEnabled" | "showWorkerNotifications" | "passive" | "debugLog";
|
|
14
14
|
|
|
15
15
|
function modelLabel(model: ConfiguredModel | undefined): string {
|
|
16
16
|
return model ? `${model.provider}/${model.id}` : "current session model";
|
|
@@ -175,8 +175,13 @@ export function registerSettingsCommand(pi: ExtensionAPI, runtime: Runtime): voi
|
|
|
175
175
|
ctx.ui.notify(`Structural reviewer: ${argument.endsWith("on") ? "enabled" : "disabled"} for this session.`, "info");
|
|
176
176
|
return;
|
|
177
177
|
}
|
|
178
|
+
if (argument === "messages on" || argument === "messages off") {
|
|
179
|
+
appendSettings(pi, runtime, { showContemplatorMessages: argument.endsWith("on") });
|
|
180
|
+
ctx.ui.notify(`Contemplator messages: ${argument.endsWith("on") ? "visible" : "hidden"} for this session.`, "info");
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
178
183
|
if (argument) {
|
|
179
|
-
ctx.ui.notify("Usage: /om:settings [on|off|reviewer on|reviewer off|compaction on|compaction off]", "info");
|
|
184
|
+
ctx.ui.notify("Usage: /om:settings [on|off|messages on|messages off|reviewer on|reviewer off|compaction on|compaction off]", "info");
|
|
180
185
|
return;
|
|
181
186
|
}
|
|
182
187
|
|
|
@@ -185,6 +190,7 @@ export function registerSettingsCommand(pi: ExtensionAPI, runtime: Runtime): voi
|
|
|
185
190
|
const choice = await ctx.ui.select("Observational memory settings (session overrides)", [
|
|
186
191
|
`Contemplation: ${scalarLabel(runtime, "contemplatorEnabled")}`,
|
|
187
192
|
`Contemplation model: ${hasOverride(settings, "contemplatorModel") ? modelLabel(runtime.config.contemplatorModel) : `default (${modelLabel(runtime.getDefaultConfig().contemplatorModel)})`}`,
|
|
193
|
+
`Contemplator messages visible: ${scalarLabel(runtime, "showContemplatorMessages")}`,
|
|
188
194
|
`Structural reviewer: ${scalarLabel(runtime, "reviewerEnabled")}`,
|
|
189
195
|
`Structural reviewer model: ${hasOverride(settings, "reviewerModel") ? modelLabel(runtime.config.reviewerModel) : `default (${modelLabel(runtime.getDefaultConfig().reviewerModel)})`}`,
|
|
190
196
|
`Compaction observer: ${scalarLabel(runtime, "compactionObserverEnabled")}`,
|
|
@@ -208,6 +214,7 @@ export function registerSettingsCommand(pi: ExtensionAPI, runtime: Runtime): voi
|
|
|
208
214
|
]);
|
|
209
215
|
if (!choice || choice === "Done") return;
|
|
210
216
|
if (choice.startsWith("Contemplation:")) appendSettings(pi, runtime, { contemplatorEnabled: !runtime.config.contemplatorEnabled });
|
|
217
|
+
else if (choice.startsWith("Contemplator messages visible:")) appendSettings(pi, runtime, { showContemplatorMessages: !runtime.config.showContemplatorMessages });
|
|
211
218
|
else if (choice.startsWith("Structural reviewer:")) appendSettings(pi, runtime, { reviewerEnabled: !runtime.config.reviewerEnabled });
|
|
212
219
|
else if (choice.startsWith("Compaction observer:")) appendSettings(pi, runtime, { compactionObserverEnabled: !runtime.config.compactionObserverEnabled });
|
|
213
220
|
else if (choice.startsWith("Worker notifications:")) appendSettings(pi, runtime, { showWorkerNotifications: !runtime.config.showWorkerNotifications });
|
package/src/commands/status.ts
CHANGED
|
@@ -102,6 +102,7 @@ export function registerStatusCommand(pi: ExtensionAPI, runtime: Runtime): void
|
|
|
102
102
|
`Compaction observer: ${runtime.config.compactionObserverEnabled === false ? "disabled" : "enabled"}`,
|
|
103
103
|
`Contemplator: ${runtime.config.contemplatorEnabled ? "enabled" : "disabled"}`,
|
|
104
104
|
`Contemplator model: ${runtime.config.contemplatorModel ? `${runtime.config.contemplatorModel.provider}/${runtime.config.contemplatorModel.id}` : "current session model"}`,
|
|
105
|
+
`Contemplator messages: ${runtime.config.showContemplatorMessages ? "visible" : "hidden"}`,
|
|
105
106
|
`Structural reviewer: ${runtime.config.reviewerEnabled === false ? "disabled" : "enabled"}`,
|
|
106
107
|
`Reviewer model: ${runtime.config.reviewerModel ? `${runtime.config.reviewerModel.provider}/${runtime.config.reviewerModel.id}` : "current session model"}`,
|
|
107
108
|
];
|
package/src/config.ts
CHANGED
|
@@ -52,6 +52,8 @@ export interface Config {
|
|
|
52
52
|
compactionObserverEnabled: boolean;
|
|
53
53
|
contemplatorEnabled: boolean;
|
|
54
54
|
contemplatorModel?: ConfiguredModel;
|
|
55
|
+
/** Show contemplator probes and review notices in the chat transcript. */
|
|
56
|
+
showContemplatorMessages: boolean;
|
|
55
57
|
/** Allow the contemplator to commission scoped structural reviewers. */
|
|
56
58
|
reviewerEnabled: boolean;
|
|
57
59
|
/** Optional model override used only by short-lived structural reviewers. */
|
|
@@ -75,6 +77,7 @@ export const DEFAULTS: Config = {
|
|
|
75
77
|
passive: false,
|
|
76
78
|
compactionObserverEnabled: true,
|
|
77
79
|
contemplatorEnabled: true,
|
|
80
|
+
showContemplatorMessages: true,
|
|
78
81
|
reviewerEnabled: true,
|
|
79
82
|
contemplatorMinNewObservations: 8,
|
|
80
83
|
contemplatorMinNewReflections: 1,
|
|
@@ -223,6 +226,7 @@ function normalizeSettingsConfig(value: Record<string, unknown>): Partial<Config
|
|
|
223
226
|
if (typeof value.passive === "boolean") normalized.passive = value.passive;
|
|
224
227
|
if (typeof value.compactionObserverEnabled === "boolean") normalized.compactionObserverEnabled = value.compactionObserverEnabled;
|
|
225
228
|
if (typeof value.contemplatorEnabled === "boolean") normalized.contemplatorEnabled = value.contemplatorEnabled;
|
|
229
|
+
if (typeof value.showContemplatorMessages === "boolean") normalized.showContemplatorMessages = value.showContemplatorMessages;
|
|
226
230
|
if (typeof value.reviewerEnabled === "boolean") normalized.reviewerEnabled = value.reviewerEnabled;
|
|
227
231
|
if (typeof value.debugLog === "boolean") normalized.debugLog = value.debugLog;
|
|
228
232
|
const model = normalizeModel(value.model);
|
package/src/runtime.ts
CHANGED
|
@@ -35,7 +35,7 @@ export type SessionSettings = Partial<Pick<Config,
|
|
|
35
35
|
| "observeAfterTokens" | "reflectAfterTokens" | "observerChunkMaxTokens" | "compactAfterTokens"
|
|
36
36
|
| "compactAfterTokensMode" | "compactAfterTokensRatio"
|
|
37
37
|
| "observationsPoolMaxTokens" | "observationsPoolTargetTokens" | "agentMaxTurns"
|
|
38
|
-
| "showWorkerNotifications" | "passive" | "compactionObserverEnabled" | "contemplatorEnabled" | "reviewerEnabled"
|
|
38
|
+
| "showWorkerNotifications" | "passive" | "compactionObserverEnabled" | "contemplatorEnabled" | "showContemplatorMessages" | "reviewerEnabled"
|
|
39
39
|
| "contemplatorMinNewObservations" | "contemplatorMinNewReflections" | "contemplatorMinTurns" | "debugLog"
|
|
40
40
|
>> & {
|
|
41
41
|
/** null explicitly means use the configured/session model. */
|
|
@@ -108,7 +108,7 @@ export function computeSessionSettings(entries: readonly unknown[]): SessionSett
|
|
|
108
108
|
if (!source || typeof source !== "object") return;
|
|
109
109
|
const data = source as Record<string, unknown>;
|
|
110
110
|
const booleanKeys = [
|
|
111
|
-
"showWorkerNotifications", "passive", "compactionObserverEnabled", "contemplatorEnabled", "reviewerEnabled", "debugLog",
|
|
111
|
+
"showWorkerNotifications", "passive", "compactionObserverEnabled", "contemplatorEnabled", "showContemplatorMessages", "reviewerEnabled", "debugLog",
|
|
112
112
|
] as const;
|
|
113
113
|
const numberKeys = [
|
|
114
114
|
"observeAfterTokens", "reflectAfterTokens", "observerChunkMaxTokens", "compactAfterTokens",
|