@matthewfl/pi-contemplator 0.0.6 → 0.0.7

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.6",
3
+ "version": "0.0.7",
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",
@@ -30,13 +30,22 @@ function isCurrentWatch(runtime: Runtime, generation: number): boolean {
30
30
  return runtime.compactionResumePending && runtime.compactionResumeGeneration === generation;
31
31
  }
32
32
 
33
- function sendResumeMessage(pi: ExtensionAPI, ctx: ResumeCtx, afterFailure: boolean): void {
33
+ function continuationMessage(afterFailure: boolean, shortContinuationPrompt?: string): string {
34
+ if (shortContinuationPrompt) {
35
+ return afterFailure
36
+ ? `Context compaction failed. Continue with these instructions:\n\n${shortContinuationPrompt}`
37
+ : shortContinuationPrompt;
38
+ }
39
+ return afterFailure
40
+ ? "Context compaction failed. Continue the current task without waiting for another user message."
41
+ : "Continue the current task from the compacted context without waiting for another user message.";
42
+ }
43
+
44
+ function sendResumeMessage(pi: ExtensionAPI, ctx: ResumeCtx, afterFailure: boolean, shortContinuationPrompt?: string): void {
34
45
  try {
35
46
  pi.sendMessage({
36
47
  customType: "om.compaction.resume",
37
- content: afterFailure
38
- ? "Context compaction failed. Continue the current task without waiting for another user message."
39
- : "Continue the current task from the compacted context without waiting for another user message.",
48
+ content: continuationMessage(afterFailure, shortContinuationPrompt),
40
49
  display: false,
41
50
  }, {
42
51
  deliverAs: "followUp",
@@ -54,6 +63,7 @@ function scheduleResumeRetries(
54
63
  ctx: ResumeCtx,
55
64
  generation: number,
56
65
  afterFailure: boolean,
66
+ shortContinuationPrompt: string | undefined,
57
67
  retryIndex = 0,
58
68
  ): void {
59
69
  if (!isCurrentWatch(runtime, generation)) return;
@@ -75,8 +85,8 @@ function scheduleResumeRetries(
75
85
  `Observational memory: continuation did not start; retrying (${retryIndex + 1}/${RESUME_RETRY_DELAYS_MS.length})`,
76
86
  "warning",
77
87
  );
78
- sendResumeMessage(pi, ctx, afterFailure);
79
- scheduleResumeRetries(pi, runtime, ctx, generation, afterFailure, retryIndex + 1);
88
+ sendResumeMessage(pi, ctx, afterFailure, shortContinuationPrompt);
89
+ scheduleResumeRetries(pi, runtime, ctx, generation, afterFailure, shortContinuationPrompt, retryIndex + 1);
80
90
  }, RESUME_RETRY_DELAYS_MS[retryIndex]);
81
91
  }
82
92
 
@@ -86,10 +96,11 @@ export function resumeAfterCompaction(
86
96
  runtime: Runtime,
87
97
  ctx: ResumeCtx,
88
98
  afterFailure = false,
99
+ shortContinuationPrompt?: string,
89
100
  ): void {
90
101
  const generation = beginResumeWatch(runtime);
91
- sendResumeMessage(pi, ctx, afterFailure);
92
- scheduleResumeRetries(pi, runtime, ctx, generation, afterFailure);
102
+ sendResumeMessage(pi, ctx, afterFailure, shortContinuationPrompt);
103
+ scheduleResumeRetries(pi, runtime, ctx, generation, afterFailure, shortContinuationPrompt);
93
104
  }
94
105
 
95
106
  /**
@@ -111,7 +122,7 @@ export function watchForNativeCompactionResume(
111
122
  "warning",
112
123
  );
113
124
  sendResumeMessage(pi, ctx, false);
114
- scheduleResumeRetries(pi, runtime, ctx, generation, false);
125
+ scheduleResumeRetries(pi, runtime, ctx, generation, false, undefined);
115
126
  }, NATIVE_RESUME_GRACE_MS);
116
127
  }
117
128
 
@@ -17,6 +17,7 @@ export function registerCompactionTrigger(pi: ExtensionAPI, runtime: Runtime): v
17
17
  if (runtime.compactInFlight) return;
18
18
 
19
19
  const agentRequested = runtime.compactRequested;
20
+ const shortContinuationPrompt = agentRequested ? runtime.compactContinuationPrompt : undefined;
20
21
  if (agentRequested) runtime.compactRequested = false;
21
22
  else if (runtime.config.passive === true) return;
22
23
 
@@ -88,6 +89,7 @@ export function registerCompactionTrigger(pi: ExtensionAPI, runtime: Runtime): v
88
89
  "info",
89
90
  );
90
91
  }
92
+ if (agentRequested) runtime.compactContinuationPrompt = undefined;
91
93
  ctx.compact({
92
94
  onComplete: () => {
93
95
  runtime.compactInFlight = false;
@@ -95,7 +97,7 @@ export function registerCompactionTrigger(pi: ExtensionAPI, runtime: Runtime): v
95
97
  // Both explicit and proactive OM compactions are manual from Pi's
96
98
  // perspective (willRetry=false), so Pi will not continue either one.
97
99
  // Always enqueue a hidden continuation after OM finishes compacting.
98
- resumeAfterCompaction(pi, runtime, { hasUI, ui });
100
+ resumeAfterCompaction(pi, runtime, { hasUI, ui }, false, shortContinuationPrompt);
99
101
  },
100
102
  onError: (error: { message: string }) => {
101
103
  runtime.compactInFlight = false;
@@ -104,18 +106,19 @@ export function registerCompactionTrigger(pi: ExtensionAPI, runtime: Runtime): v
104
106
  if (error.message !== "Compaction cancelled" && hasUI) {
105
107
  ui?.notify(`Observational memory: ${error.message}`, "error");
106
108
  }
107
- resumeAfterCompaction(pi, runtime, { hasUI, ui }, true);
109
+ resumeAfterCompaction(pi, runtime, { hasUI, ui }, true, shortContinuationPrompt);
108
110
  },
109
111
  });
110
112
  } catch (error) {
111
113
  runtime.compactInFlight = false;
114
+ if (agentRequested) runtime.compactContinuationPrompt = undefined;
112
115
  runtime.compactOrigin = undefined;
113
116
  const msg = error instanceof Error ? error.message : String(error);
114
117
  if (hasUI) {
115
118
  ui?.setStatus?.(COMPACTION_STATUS_KEY, undefined);
116
119
  ui?.notify(`Observational memory: compact threw: ${msg}`, "error");
117
120
  }
118
- resumeAfterCompaction(pi, runtime, { hasUI, ui }, true);
121
+ resumeAfterCompaction(pi, runtime, { hasUI, ui }, true, shortContinuationPrompt);
119
122
  }
120
123
  }, 0);
121
124
  });
package/src/runtime.ts CHANGED
@@ -145,6 +145,8 @@ export class Runtime {
145
145
  consolidationPhase: ConsolidationPhase | undefined;
146
146
  compactInFlight = false;
147
147
  compactRequested = false;
148
+ /** Agent-authored instructions to deliver after an explicit compact_context request. */
149
+ compactContinuationPrompt: string | undefined;
148
150
  compactOrigin: "proactive" | "agent-requested" | undefined;
149
151
  compactHookInFlight = false;
150
152
  compactionResumePending = false;
@@ -212,6 +214,7 @@ export class Runtime {
212
214
  // never-cleared compactInFlight bricking all future compactions).
213
215
  this.compactInFlight = false;
214
216
  this.compactRequested = false;
217
+ this.compactContinuationPrompt = undefined;
215
218
  this.compactOrigin = undefined;
216
219
  this.compactionResumePending = false;
217
220
  this.compactionResumeGeneration += 1;
@@ -4,7 +4,7 @@ import type { Runtime } from "../runtime.js";
4
4
 
5
5
  export const COMPACT_CONTEXT_TOOL_NAME = "compact_context";
6
6
  export const COMPACT_CONTEXT_DESCRIPTION =
7
- "Force manual compaction. Use sparingly when substantial work remains but the remaining context is insufficient, or when accumulated context has become noisy or stale enough to impair focus and reliable reasoning.";
7
+ "Force manual compaction and resume with agent-authored next-step instructions. Use sparingly when substantial work remains but the remaining context is insufficient, or when accumulated context has become noisy or stale enough to impair focus and reliable reasoning.";
8
8
 
9
9
  export type CompactContextDetails = {
10
10
  status: "scheduled" | "already_pending" | "in_progress";
@@ -20,10 +20,17 @@ export function createCompactContextTool(runtime: Runtime) {
20
20
  promptGuidelines: [
21
21
  "Use compact_context sparingly when either substantial additional work remains and there is not enough context left to complete it, or accumulated past context has become noisy, stale, or distracting enough that you are struggling to focus on the current task or reason about it reliably.",
22
22
  "Do not use compact_context routinely, for short tasks, or merely because the conversation is long; use it for genuine context-capacity pressure or context degradation that is interfering with the work.",
23
- "Call compact_context by itself and stop the current turn; observational memory will compact the context and automatically resume the task.",
23
+ "Call compact_context by itself, provide short_continuation_prompt with concrete instructions for the next agent step, and stop the current turn; observational memory will compact the context and automatically resume with those instructions.",
24
24
  ],
25
- parameters: Type.Object({}),
26
- async execute() {
25
+ parameters: Type.Object({
26
+ short_continuation_prompt: Type.String({
27
+ minLength: 1,
28
+ maxLength: 1_000,
29
+ pattern: "\\S",
30
+ description: "Short, concrete instructions to your post-compaction self describing the next action and any critical immediate constraint. Do not summarize the whole conversation.",
31
+ }),
32
+ }),
33
+ async execute(_toolCallId, params) {
27
34
  if (runtime.compactInFlight) {
28
35
  return {
29
36
  content: [{ type: "text" as const, text: "Context compaction is already in progress. Stop this turn and wait for automatic resume." }],
@@ -40,6 +47,7 @@ export function createCompactContextTool(runtime: Runtime) {
40
47
  }
41
48
 
42
49
  runtime.compactRequested = true;
50
+ runtime.compactContinuationPrompt = params.short_continuation_prompt.trim();
43
51
  return {
44
52
  content: [{ type: "text" as const, text: "Context compaction scheduled. Stop this turn; the task will resume automatically after compaction." }],
45
53
  details: { status: "scheduled" } as CompactContextDetails,