@8monkey/pi-context-history 0.1.0 → 0.2.0

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # pi-context-history
2
2
 
3
- A [Pi](https://github.com/earendil-works/pi-coding-agent) extension that manages conversation context end-to-end. It trims old history, strips stale tool chatter from earlier turns, keeps a rolling session summary, and folds that summary back into the system prompt — so long-running conversations stay lean, focused, and continuous.
3
+ A [Pi](https://github.com/earendil-works/pi-coding-agent) extension that manages conversation context end-to-end. It trims old history, strips stale tool chatter from earlier turns, keeps a rolling session summary, folds that summary back into the system prompt, and lets you append messages to the history by hand — so long-running conversations stay lean, focused, and continuous.
4
4
 
5
5
  Each feature is independent and can be turned off individually, so you can run the whole pipeline or just the parts you want.
6
6
 
@@ -12,6 +12,7 @@ Each feature is independent and can be turned off individually, so you can run t
12
12
  | **Strip tool history** | `context` | Removes tool calls and results from prior turns, keeping the current turn's tool interactions intact. |
13
13
  | **Generate summary** | `session_start` | Regenerates a stale rolling summary in the background when a session resumes; also available on demand via `/summarize-session`. |
14
14
  | **Inject summary** | `before_agent_start` | Folds the rolling summary into the system prompt so the model continues with prior context. |
15
+ | **Append message** | command | Adds a user or assistant message to the end of the history via `/add-user-message` and `/add-assistant-message`. |
15
16
 
16
17
  The summary features are a producer/consumer pair: **Generate summary** writes `~/.pi/agent/summary.md`, and **Inject summary** reads it.
17
18
 
@@ -27,13 +28,16 @@ pi install npm:@8monkey/pi-context-history
27
28
  - **Strip tool history.** Tool calls and results from earlier turns are removed; the current turn is left untouched. At the start of each agent loop the extension marks where the current turn begins (scanning back past the latest tool exchange) and holds that mark steady across the loop, so a running turn is never stripped mid-flight. Before the mark, tool results are dropped and assistant messages lose their tool-call blocks (if nothing else remains, the message goes too).
28
29
  - **Generate summary.** When you resume a session whose summary has gone stale — older than `PI_SUMMARY_STALENESS_DAYS`, with a first message older than the window too — the extension rebuilds `~/.pi/agent/summary.md` by shelling out to `pi -p` (extensions, context files, and skills disabled). New and empty sessions are skipped. To override the built-in prompt, drop your own at `prompts/session-summary.md` (project `.pi/` wins over `~/.pi/`); it must contain the `{conversation_history}` placeholder.
29
30
  - **Inject summary.** Before each agent run, when `~/.pi/agent/summary.md` exists, its contents are wrapped in a `<summary date="…">` block (the date is the file's modified time) followed by an `<additional_context>` note telling the model to maintain continuity and match the existing language and tone.
31
+ - **Append message.** `/add-user-message <text>` and `/add-assistant-message <text>` write a message with the matching role to the end of the session history. The entry is persisted immediately but enters the live context on the next rebuild (resume or branch), since Pi has no public way to inject into a running turn. Appending an assistant message requires a model to be selected, as the message records its identity.
30
32
 
31
33
  Zero runtime dependencies. Pi loads the TypeScript directly, so there's no build step. Runs under Node or Bun.
32
34
 
33
- ## Command
35
+ ## Commands
34
36
 
35
37
  | Command | Description |
36
38
  |---|---|
39
+ | `/add-assistant-message <text>` | Append an assistant message to the end of the history. Persisted immediately; enters context on the next rebuild. Available only when the **Append message** feature is enabled. |
40
+ | `/add-user-message <text>` | Append a user message to the end of the history. Persisted immediately; enters context on the next rebuild. Available only when the **Append message** feature is enabled. |
37
41
  | `/summarize-session` | Regenerate the current session's summary on demand, ignoring the staleness window. Writes `~/.pi/agent/summary.md` and reports success, failure, or an empty session. Available only when the **Generate summary** feature is enabled. |
38
42
 
39
43
  ## Configuration
@@ -50,6 +54,7 @@ Every feature is **on by default** and switched off by setting its flag to `fals
50
54
  | `PI_STRIP_TOOL_HISTORY` | on | Strip tool history |
51
55
  | `PI_GENERATE_SUMMARY` | on | Generate summary |
52
56
  | `PI_INJECT_SUMMARY` | on | Inject summary |
57
+ | `PI_APPEND_MESSAGE` | on | Append message |
53
58
 
54
59
  ### Settings
55
60
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@8monkey/pi-context-history",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Pi extension that manages conversation context: trims old history, strips stale tool calls, and keeps a rolling summary fed back into the system prompt. Each feature toggles independently.",
5
5
  "license": "MIT",
6
6
  "type": "module",
package/src/index.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { writeFileSync } from "node:fs";
2
2
  import { homedir } from "node:os";
3
3
  import { join } from "node:path";
4
- import type { ExtensionAPI, ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
4
+ import type { ExtensionAPI, SessionManager } from "@earendil-works/pi-coding-agent";
5
5
  import { buildContextPrompt } from "./build-prompt.ts";
6
6
  import { featureEnabled, HISTORY_DAYS, SUMMARY_STALENESS_DAYS } from "./config.ts";
7
7
  import { filterByAge } from "./filter.ts";
@@ -17,6 +17,14 @@ import {
17
17
 
18
18
  const HISTORY_MS = HISTORY_DAYS * 86_400_000;
19
19
  const SUMMARY_PATH = join(homedir(), ".pi", "agent", "summary.md");
20
+ const ZERO_USAGE = {
21
+ input: 0,
22
+ output: 0,
23
+ cacheRead: 0,
24
+ cacheWrite: 0,
25
+ totalTokens: 0,
26
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
27
+ };
20
28
 
21
29
  function registerTrimHistory(pi: ExtensionAPI) {
22
30
  pi.on("context", (event) => {
@@ -76,7 +84,7 @@ function registerGenerateSummary(pi: ExtensionAPI) {
76
84
 
77
85
  pi.registerCommand("summarize-session", {
78
86
  description: "Regenerate the current session's summary now (ignores staleness)",
79
- handler: async (_args, ctx: ExtensionCommandContext) => {
87
+ handler: async (_args, ctx) => {
80
88
  const entries = ctx.sessionManager.getEntries() as TranscriptEntry[];
81
89
  let result: string | "empty";
82
90
  try {
@@ -106,9 +114,56 @@ function registerInjectSummary(pi: ExtensionAPI) {
106
114
  });
107
115
  }
108
116
 
117
+ function registerAppendMessage(pi: ExtensionAPI) {
118
+ pi.registerCommand("add-user-message", {
119
+ description: "Append a user message to the end of the conversation history",
120
+ handler: async (args, ctx) => {
121
+ const text = args.trim();
122
+ if (!text) {
123
+ if (ctx.hasUI) ctx.ui.notify("Usage: /add-user-message <text>", "warning");
124
+ return;
125
+ }
126
+ const session = ctx.sessionManager as SessionManager;
127
+ session.appendMessage({ role: "user", content: [{ type: "text", text }], timestamp: Date.now() });
128
+ if (ctx.hasUI) ctx.ui.notify("Appended a user message; it applies on the next session rebuild.", "info");
129
+ },
130
+ });
131
+
132
+ pi.registerCommand("add-assistant-message", {
133
+ description: "Append an assistant message to the end of the conversation history",
134
+ handler: async (args, ctx) => {
135
+ const text = args.trim();
136
+ if (!text) {
137
+ if (ctx.hasUI) ctx.ui.notify("Usage: /add-assistant-message <text>", "warning");
138
+ return;
139
+ }
140
+ if (!ctx.model) {
141
+ if (ctx.hasUI) ctx.ui.notify("Cannot append an assistant message: no model selected.", "error");
142
+ return;
143
+ }
144
+ const { api, provider, id } = ctx.model;
145
+ const session = ctx.sessionManager as SessionManager;
146
+ session.appendMessage({
147
+ role: "assistant",
148
+ content: [{ type: "text", text }],
149
+ api,
150
+ provider,
151
+ model: id,
152
+ usage: ZERO_USAGE,
153
+ stopReason: "stop",
154
+ timestamp: Date.now(),
155
+ });
156
+ if (ctx.hasUI) {
157
+ ctx.ui.notify("Appended an assistant message; it applies on the next session rebuild.", "info");
158
+ }
159
+ },
160
+ });
161
+ }
162
+
109
163
  export default function (pi: ExtensionAPI) {
110
164
  if (featureEnabled("PI_TRIM_HISTORY")) registerTrimHistory(pi);
111
165
  if (featureEnabled("PI_STRIP_TOOL_HISTORY")) registerStripToolHistory(pi);
112
166
  if (featureEnabled("PI_GENERATE_SUMMARY")) registerGenerateSummary(pi);
113
167
  if (featureEnabled("PI_INJECT_SUMMARY")) registerInjectSummary(pi);
168
+ if (featureEnabled("PI_APPEND_MESSAGE")) registerAppendMessage(pi);
114
169
  }