@matthewfl/pi-contemplator 0.0.2 → 0.0.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.0.2",
3
+ "version": "0.0.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",
@@ -143,7 +143,8 @@ export class Contemplator {
143
143
  private resumedReviewIds = new Set<string>();
144
144
  private reviewerSessions = new Map<string, ReviewerSession>();
145
145
  private deliveredProbeIds = new Set<string>();
146
- private requeuedProbeIds = new Set<string>();
146
+ /** Probe ids passed to pi.sendMessage by this live extension runtime. */
147
+ private queuedProbeIds = new Set<string>();
147
148
  private sessionGeneration = 0;
148
149
  private latestCtx: MemoryUpdateCtx | undefined;
149
150
  private turnsSinceRun = 0;
@@ -153,12 +154,19 @@ export class Contemplator {
153
154
 
154
155
  register(): void {
155
156
  this.runtime.setMemoryUpdateListener((ctx) => this.withDebugContext(ctx, () => this.observeTurn(ctx)));
156
- const restoreSessionBranch = (_event: any, ctx: ExtensionContext) => {
157
+ this.pi.on("session_start", (event: any, ctx: ExtensionContext) => {
157
158
  this.sessionGeneration++;
158
- this.restore(ctx, true);
159
- };
160
- this.pi.on("session_start", restoreSessionBranch);
161
- this.pi.on("session_tree", restoreSessionBranch);
159
+ // AgentSession preserves its steering queue across extension reloads. An
160
+ // undelivered tracking entry therefore still has a live queued message;
161
+ // restoring it here would enqueue the same probe a second time.
162
+ const reload = event?.reason === "reload";
163
+ this.restore(ctx, true, reload, reload);
164
+ });
165
+ this.pi.on("session_tree", (_event: any, ctx: ExtensionContext) => {
166
+ this.sessionGeneration++;
167
+ // Pending steering messages remain queued while navigating the tree.
168
+ this.restore(ctx, true, true);
169
+ });
162
170
  this.pi.on("session_shutdown", () => {
163
171
  this.sessionGeneration++;
164
172
  this.history = [];
@@ -172,7 +180,7 @@ export class Contemplator {
172
180
  this.resumedReviewIds.clear();
173
181
  this.reviewerSessions.clear();
174
182
  this.deliveredProbeIds.clear();
175
- this.requeuedProbeIds.clear();
183
+ this.queuedProbeIds.clear();
176
184
  this.latestCtx = undefined;
177
185
  this.turnsSinceRun = 0;
178
186
  this.restoredTipId = undefined;
@@ -220,7 +228,7 @@ export class Contemplator {
220
228
  }, fn);
221
229
  }
222
230
 
223
- private restore(ctx: MemoryUpdateCtx, resetTracking = false): void {
231
+ private restore(ctx: MemoryUpdateCtx, resetTracking = false, retainQueuedIds = false, skipUndeliveredRestore = false): void {
224
232
  this.latestCtx = ctx;
225
233
  const entries = ctx.sessionManager.getBranch() as Entry[];
226
234
  const tipId = entries.at(-1)?.id;
@@ -229,7 +237,7 @@ export class Contemplator {
229
237
  this.history = [];
230
238
  if (resetTracking) {
231
239
  this.deliveredProbeIds.clear();
232
- this.requeuedProbeIds.clear();
240
+ if (!retainQueuedIds) this.queuedProbeIds.clear();
233
241
  this.inFlightReviewIds.clear();
234
242
  this.resolvingReviewIds.clear();
235
243
  this.resumedReviewIds.clear();
@@ -312,8 +320,7 @@ export class Contemplator {
312
320
  }
313
321
  this.restoredTipId = tipId;
314
322
  for (const [probeId, question] of undeliveredSuggestions) {
315
- if (queuedProbeIds.has(probeId) || this.requeuedProbeIds.has(probeId)) continue;
316
- this.requeuedProbeIds.add(probeId);
323
+ if (skipUndeliveredRestore || queuedProbeIds.has(probeId) || this.queuedProbeIds.has(probeId)) continue;
317
324
  this.queueProbe(ctx, question, "restore", probeId);
318
325
  }
319
326
  if (resetTracking) void this.resumePendingReviews(ctx);
@@ -571,6 +578,10 @@ export class Contemplator {
571
578
  display: false,
572
579
  details: { version: 1, question, source, probeId },
573
580
  }, { deliverAs: "steer", triggerTurn: false });
581
+ // sendMessage queues synchronously. Mark every source (not only restore)
582
+ // before a later turn_end can rebuild state from the still-undelivered
583
+ // tracking entry and enqueue this probe again.
584
+ this.queuedProbeIds.add(probeId);
574
585
  this.pi.appendEntry(CONTEMPLATOR_SUGGESTION, { version: 1, suggestion: question, delivered: false, source, probeId });
575
586
  this.markTipPersisted(ctx);
576
587
  debugLog("contemplator.suggestion_queued", {