@narumitw/pi-codex-compact 0.50.1 → 0.51.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,30 +1,22 @@
1
- # 🗜️ pi-codex-compact — Remote Compaction V2 for Pi
1
+ # 🗜️ pi-codex-compact — Use Codex Remote Compaction in Pi
2
2
 
3
3
  [![npm](https://img.shields.io/npm/v/@narumitw/pi-codex-compact)](https://www.npmjs.com/package/@narumitw/pi-codex-compact) [![Pi extension](https://img.shields.io/badge/Pi-extension-blue)](https://pi.dev) [![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](./LICENSE)
4
4
 
5
- `@narumitw/pi-codex-compact` adds Codex Remote Compaction V2 to the
6
- [Pi Coding Agent](https://pi.dev) for the built-in `openai-codex` OAuth provider. It replaces Pi's
7
- plaintext summary-generation call with a server-generated opaque compaction item and safely replays
8
- that item in later OpenAI Codex Responses requests.
5
+ Use Codex Remote Compaction V2 in Pi instead of generating a local plaintext summary for models that use the `openai-codex-responses` API.
9
6
 
10
- Pi remains responsible for deciding when compaction runs, including automatic thresholds, the
11
- built-in `/compact` command, overflow retries, retained-message selection, and append-only session
12
- publication. This package owns only the Remote V2 request, checkpoint persistence, and later payload
13
- replay.
7
+ The extension requests and stores an opaque server-generated checkpoint, then safely replays it in later Codex Responses requests.
8
+
9
+ Pi still decides when compaction runs and retains its normal `/compact`, threshold, overflow, and session-publication behavior.
14
10
 
15
11
  ## ✨ Features
16
12
 
17
- - Uses the active `openai-codex` OAuth credentials without persisting tokens or request headers.
18
- - Handles Pi's manual, threshold, and overflow compaction reasons through the same lifecycle hook.
19
- - Validates one bounded opaque `compaction` item from a completed Responses SSE stream.
20
- - Persists a versioned checkpoint in `CompactionEntry.details` and restores it after reload, resume,
21
- or a fork that retains the checkpoint.
22
- - Replays the newest compatible checkpoint while preserving later conversation and extension-added
23
- context.
24
- - Supports repeated compaction by carrying the previous opaque item into the next Remote V2 request.
25
- - Falls back to Pi's native plaintext compaction after authentication, transport, protocol, or
26
- validation failure; user cancellation does not launch the fallback.
27
- - Provides `/codex-compact` for manual compaction, effective-route visibility, and bounded settings.
13
+ - Uses the active Codex Responses provider and credentials without persisting secrets or request headers.
14
+ - Handles manual, threshold, and overflow compaction through Pi's existing lifecycle.
15
+ - Validates and persists one bounded opaque checkpoint that survives compatible reloads, resumes, and forks.
16
+ - Replays the latest checkpoint while preserving newer conversation and extension context.
17
+ - Supports repeated compaction by carrying the previous checkpoint into the next request.
18
+ - Falls back to Pi's native plaintext compaction on non-cancellation failures.
19
+ - Provides `/codex-compact` for route status, settings, and manual compaction.
28
20
 
29
21
  ## 📦 Install
30
22
 
@@ -43,32 +35,33 @@ pi -e npm:@narumitw/pi-codex-compact
43
35
  Try a local checkout from the repository root:
44
36
 
45
37
  ```bash
38
+ npm --workspace @narumitw/pi-codex-compact run build
46
39
  pi -e ./packages/pi-codex-compact
47
- # or
48
- just try codex-compact
49
40
  ```
50
41
 
51
- Loading the package enables Remote V2 with safe defaults. Avoid loading a global npm installation
52
- and the local workspace at the same time.
42
+ The package declares `dist/index.ts`, so an unbuilt local checkout must be built before Pi loads the package directory.
43
+ Loading the package enables Remote V2 with safe defaults.
44
+ Avoid loading a global npm installation and the local workspace at the same time.
53
45
 
54
46
  ## 🚀 Quick start
55
47
 
56
- 1. Sign in through Pi's built-in OpenAI Codex OAuth provider.
57
- 2. Select a model whose provider is `openai-codex` and API is `openai-codex-responses`.
58
- 3. Work normally. Pi's automatic compaction and built-in `/compact` continue to operate.
48
+ 1. Sign in through Pi's built-in OpenAI Codex provider, or configure a custom provider that routes to a compatible Codex backend.
49
+ 2. Select a model whose API is `openai-codex-responses`.
50
+ 3. Work normally.
51
+ Pi's automatic compaction and built-in `/compact` continue to operate.
59
52
  4. Run `/codex-compact` to inspect the effective route or choose **Compact now**.
60
53
  5. After compaction, continue the session normally; compatible requests replay the opaque checkpoint.
61
54
 
62
- When the active model is unsupported, compaction remains entirely Pi-native.
55
+ When the active model uses another API, compaction remains entirely Pi-native.
63
56
 
64
- ## 💬 Command
57
+ ## 💬 Commands
65
58
 
66
59
  ```text
67
60
  /codex-compact
68
61
  ```
69
62
 
70
- In TUI mode, the root menu shows whether Remote V2 is enabled, the active model, and whether a
71
- manual compact will use **Codex Remote V2** or **Pi native**. It contains:
63
+ In TUI mode, the root menu shows whether Remote V2 is enabled, the active model, and whether a manual compact will use **Codex Remote V2** or **Pi native**.
64
+ It contains:
72
65
 
73
66
  ```text
74
67
  Compact now
@@ -76,13 +69,12 @@ Settings
76
69
  Close
77
70
  ```
78
71
 
79
- **Compact now** closes the menu before asking Pi to compact the active session. Escape or Ctrl+C
80
- closes without compacting, and an obsolete menu cannot trigger work after session replacement or
81
- shutdown. **Settings** opens the bounded settings editor. In non-TUI modes, the command reports the
82
- manual settings path instead of opening custom UI or compacting.
72
+ **Compact now** closes the menu before asking Pi to compact the active session.
73
+ Escape or Ctrl+C closes without compacting, and an obsolete menu cannot trigger work after session replacement or shutdown.
74
+ **Settings** opens the bounded settings editor.
75
+ In non-TUI modes, the command reports the manual settings path instead of opening custom UI or compacting.
83
76
 
84
- Pi's built-in `/compact` remains available and follows the same extension hook when the active model
85
- is compatible.
77
+ Pi's built-in `/compact` remains available and follows the same extension hook when the active model is compatible.
86
78
 
87
79
  ## ⚙️ Settings
88
80
 
@@ -92,8 +84,8 @@ The extension has one optional, global-only JSON settings file:
92
84
  <getAgentDir()>/pi-codex-compact.json
93
85
  ```
94
86
 
95
- The normal path is `~/.pi/agent/pi-codex-compact.json`. There is no environment-variable or
96
- project-level override.
87
+ The normal path is `~/.pi/agent/pi-codex-compact.json`.
88
+ There is no environment-variable or project-level override.
97
89
 
98
90
  ```json
99
91
  {
@@ -113,15 +105,14 @@ project-level override.
113
105
  | `replacementTokenBudget` | `64000` | Integer from 8,000 to 128,000 tokens | Bound approximate retained user-message text beside the opaque item. | Keep 64K; lower it to reduce session size or raise it only when recent user context is being lost. |
114
106
  | `notifyOnFallback` | `true` | Boolean | Warn when Remote V2 fails and Pi-native compaction takes over. | Keep enabled so silent fallback does not hide protocol or entitlement problems. |
115
107
 
116
- Missing fields use defaults. Settings reload on every `session_start`, including `/reload`, resume,
117
- and fork. Menu writes apply immediately, preserve unknown JSON fields, serialize within the current
118
- Pi process, and use a final conflict check plus same-directory atomic rename. On Unix, temporary
119
- files use mode `0600`.
108
+ Missing fields use defaults.
109
+ Settings reload on every `session_start`, including `/reload`, resume, and fork.
110
+ Menu writes apply immediately, preserve unknown JSON fields, serialize within the current Pi process, and use a final conflict check plus same-directory atomic rename.
111
+ On Unix, temporary files use mode `0600`.
120
112
 
121
- Malformed, invalid, oversized, or symlinked settings files are never overwritten. Safe defaults stay
122
- active, and the menu provides read-only repair guidance until the file is fixed and Pi is reloaded.
123
- Separate Pi processes do not share a mutation lock; a detected concurrent edit is rejected so the
124
- user can reopen Settings and retry.
113
+ Malformed, invalid, oversized, or symlinked settings files are never overwritten.
114
+ Safe defaults stay active, and the menu provides read-only repair guidance until the file is fixed and Pi is reloaded.
115
+ Separate Pi processes do not share a mutation lock; a detected concurrent edit is rejected so the user can reopen Settings and retry.
125
116
 
126
117
  ### Relationship to Codex configuration
127
118
 
@@ -138,39 +129,35 @@ This extension does **not** read `~/.codex/config.toml`.
138
129
  ## ✅ Requirements and compatibility
139
130
 
140
131
  - Pi APIs compatible with the package's declared peer dependencies.
141
- - The built-in provider `openai-codex`.
142
132
  - API `openai-codex-responses` on the active model.
143
- - A working OpenAI Codex OAuth login and Remote V2 entitlement.
133
+ - A built-in or custom provider that routes that API to a backend supporting `compaction_trigger` and opaque `compaction` replay.
134
+ - Working credentials and Remote V2 entitlement for that backend.
144
135
 
145
- OpenAI API-key, Azure, GitHub Copilot, proxies, and arbitrary Responses-compatible providers are not
146
- supported. Switching to another provider or model leaves Pi's visible fallback marker plus retained
147
- recent messages in context. Switching back to the checkpoint's original compatible model restores
148
- opaque replay.
136
+ The built-in `openai-codex` provider is supported, and a custom provider or proxy is eligible when its model explicitly uses `openai-codex-responses`.
137
+ Generic `openai-responses`, `azure-openai-responses`, GitHub Copilot, and other merely Responses-compatible API labels are not eligible.
138
+ The extension does not probe provider capabilities before compaction; a provider that declares `openai-codex-responses` owns compatible routing, authentication, request transforms, SSE transport, and opaque replay.
139
+ A failed Remote V2 compaction attempt falls back to Pi native, but an ordinary request cannot transparently recover after an incompatible provider has already received an existing opaque checkpoint.
140
+ Provider provenance is stored for diagnosis but is not a replay gate.
141
+ Switching providers can replay a checkpoint only when the API and exact model ID still match; switching model IDs leaves Pi's visible fallback marker plus retained recent messages in context.
149
142
 
150
143
  ## 🔄 How it works
151
144
 
152
145
  1. Pi prepares compaction and selects the recent message suffix it will retain.
153
- 2. The extension projects an earlier compatible checkpoint, if present, into the current Responses
154
- input.
155
- 3. It appends exactly one final `compaction_trigger` and sends a normal authenticated Codex Responses
156
- SSE request with cache retention disabled.
146
+ 2. If an earlier compatible checkpoint is present, the extension identifies its boundary from the summary persisted on the active `CompactionEntry` and validates the retained suffix fingerprints.
147
+ 3. It projects that checkpoint into the current Responses input, appends exactly one final `compaction_trigger`, and sends a normal authenticated Codex Responses SSE request with cache retention disabled.
157
148
  4. It requires a completed response containing exactly one non-empty opaque `compaction` item.
158
- 5. It constructs bounded replacement history from recent raw user-role Responses items followed by
159
- the opaque item.
160
- 6. It stores that history and fingerprints of Pi's retained suffix in versioned
161
- `CompactionEntry.details`.
162
- 7. On later compatible requests, it replaces an exactly validated marker with the persisted
163
- replacement history immediately before provider dispatch.
149
+ 5. It constructs bounded replacement history from recent raw user-role Responses items followed by the opaque item.
150
+ 6. It stores that history and fingerprints of Pi's retained suffix in versioned `CompactionEntry.details`.
151
+ 7. On later compatible requests, it replaces an exactly validated marker with the persisted replacement history immediately before provider dispatch.
164
152
 
165
- If fingerprints, model identity, payload shape, or marker count do not match exactly, the extension
166
- leaves Pi's visible fallback context unchanged instead of guessing.
153
+ The persisted entry remains the summary identity source, so replay does not depend on the wording generated by the currently installed extension version.
154
+ If persisted summary identity, fingerprints, model identity, payload shape, or marker count do not match exactly, the extension leaves Pi's visible fallback context unchanged instead of guessing.
167
155
 
168
- ## 🔐 Privacy, storage, and limits
156
+ ## 🔒 Security and privacy
169
157
 
170
- Remote compaction sends the active conversation context, system prompt, and active tool schemas to
171
- the same OpenAI Codex backend used by the selected model. The Pi session stores the encrypted
172
- compaction item and bounded recent user-role Responses items. It does not store OAuth tokens,
173
- authorization headers, or request headers in checkpoint details.
158
+ Remote compaction sends the active conversation context, system prompt, and active tool schemas to the configured backend used by the selected Codex Responses provider.
159
+ The Pi session stores the producing provider ID, encrypted compaction item, and bounded recent user-role Responses items.
160
+ It does not store credentials, authorization headers, or request headers in checkpoint details.
174
161
 
175
162
  | Boundary | Limit |
176
163
  | --- | ---: |
@@ -182,22 +169,19 @@ authorization headers, or request headers in checkpoint details.
182
169
  | Transport retries | At most 2 |
183
170
  | Request timeout | At most 10 minutes |
184
171
 
185
- An individually oversized media item is dropped rather than making the session entry unbounded. The
186
- oldest fitting text item may be partially truncated to preserve newer context. These hard byte
187
- ceilings are intentionally not configurable.
172
+ An individually oversized media item is dropped rather than making the session entry unbounded.
173
+ The oldest fitting text item may be partially truncated to preserve newer context.
174
+ These hard byte ceilings are intentionally not configurable.
188
175
 
189
- ## 🚧 Known limitations
176
+ ## 🚧 Limitations
190
177
 
191
- - The wire contract is undocumented and can change independently of Pi or this package. Keep
192
- backups of important sessions.
193
- - Full older history depends on this extension and the same compatible model. Removing the extension
194
- exposes only the portability fallback marker and Pi-retained recent messages.
195
- - The package does not reproduce Codex core's context-window UUID/number lineage, previous-model
196
- compatibility fallback, exact pre-turn ordering, or exact mid-turn model-session ownership.
197
- - Remote failure falls back to Pi's plaintext summary, so a session can contain both remote opaque
198
- and native compaction entries over time.
199
- - Settings concurrency is coordinated only within one Pi process; separate processes rely on the
200
- final conflict check.
178
+ - The wire contract is undocumented and can change independently of Pi or this package.
179
+ Keep backups of important sessions.
180
+ - Full older history depends on this extension, the `openai-codex-responses` API, the exact checkpoint model ID, and a provider route whose backend accepts the opaque item.
181
+ Removing the extension exposes only the portability fallback marker and Pi-retained recent messages.
182
+ - The package does not reproduce Codex core's context-window UUID/number lineage, previous-model compatibility fallback, exact pre-turn ordering, or exact mid-turn model-session ownership.
183
+ - Remote failure falls back to Pi's plaintext summary, so a session can contain both remote opaque and native compaction entries over time.
184
+ - Settings concurrency is coordinated only within one Pi process; separate processes rely on the final conflict check.
201
185
 
202
186
  ## 🗂️ Package layout
203
187
 
@@ -207,12 +191,38 @@ src/codex-compact.ts Pi lifecycle, command, provider projection, and fallback
207
191
  src/remote.ts Provider stream invocation, auth payload, timeout, and retry controls
208
192
  src/protocol.ts Bounded SSE parsing and Remote V2 payload/output validation
209
193
  src/checkpoint.ts Replacement history, fingerprints, persistence, and replay projection
194
+ src/model-compatibility.ts API-based Remote V2 model eligibility
210
195
  src/settings.ts Global settings validation and atomic persistence
211
- src/settings-menu.ts Manual compaction and settings TUI
196
+ src/settings-menu.ts First-use manual compaction and settings TUI
212
197
 
213
- test/ Protocol, checkpoint, lifecycle, remote, settings, and menu coverage
198
+ dist/ Generated source-mapped Jiti runtime and lazy menu chunk
199
+ scripts/ Runtime builder
200
+ benchmark/ Three-arm compaction benchmark, self-test, and methodology
201
+ test/ Protocol, checkpoint, lifecycle, remote, settings, menu, and builder coverage
214
202
  ```
215
203
 
204
+ ## 📊 Benchmark
205
+
206
+ The repository includes a seeded three-arm benchmark for uncompressed full context, Pi-native plaintext compaction, and this extension's Codex Remote Compaction V2 path.
207
+
208
+ It holds history length nearly fixed while varying information density across five state categories and ten history epochs.
209
+
210
+ Benchmark v3 uses repeated artifacts, isolated evaluator probes, seed-level paired statistics, one Pi SDK estimator for dry and live fixtures, and committed protocol manifests for confirmatory candidates.
211
+
212
+ It never treats nominal Pi 20K and Codex 20K settings as equal information capacity or automatically claims that protocol-conformant evidence was genuinely held out.
213
+
214
+ Preview its exploratory diagnostic without making a provider request:
215
+
216
+ ```bash
217
+ just benchmark-codex-compact
218
+ ```
219
+
220
+ A live run requires an explicit `--live` flag, reviewed request and cost exposure, OpenAI Codex OAuth, and Remote V2 entitlement.
221
+
222
+ The repository preserves the explicitly labeled v2 matched-tail diagnostic and the v3 calibration evidence, while seeds 301–304 remain consumed and unavailable for future confirmatory protocols.
223
+
224
+ See the [benchmark guide](https://github.com/narumiruna/pi-extensions/tree/main/packages/pi-codex-compact/benchmark) for manifests, repetitions, commands, privacy, cost semantics, and interpretation limits.
225
+
216
226
  ## 🧪 Development
217
227
 
218
228
  From the repository root:
@@ -223,14 +233,11 @@ npm test
223
233
  just pack codex-compact
224
234
  ```
225
235
 
226
- See
227
- [`docs/implementation-notes/codex-compaction-mechanism.md`](../../docs/implementation-notes/codex-compaction-mechanism.md)
228
- for the underlying Codex mechanism research and the extension boundary.
236
+ See [`docs/implementation-notes/codex-compaction-mechanism.md`](../../docs/implementation-notes/codex-compaction-mechanism.md) for the underlying Codex mechanism research and the extension boundary.
229
237
 
230
238
  ## 🔎 Keywords
231
239
 
232
- Pi extension, Pi coding agent, OpenAI Codex, OAuth, Remote Compaction V2, opaque checkpoint,
233
- Responses API, context compaction.
240
+ Pi extension, Pi coding agent, OpenAI Codex, custom provider, proxy, Remote Compaction V2, opaque checkpoint, Responses API, context compaction.
234
241
 
235
242
  ## 📄 License
236
243
 
@@ -0,0 +1,13 @@
1
+ // @generated by scripts/build-runtime.mjs; do not edit.
2
+ // @ts-nocheck -- generated JavaScript uses a .ts extension for Pi's Jiti loader.
3
+
4
+ // src/model-compatibility.ts
5
+ import { hasApi } from "@earendil-works/pi-ai";
6
+ function isCodexRemoteCompactionModel(model) {
7
+ return model !== void 0 && hasApi(model, "openai-codex-responses");
8
+ }
9
+
10
+ export {
11
+ isCodexRemoteCompactionModel
12
+ };
13
+ //# sourceMappingURL=chunk-3WRPJHG2.ts.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/model-compatibility.ts"],
4
+ "sourcesContent": ["import type { Api, Model } from \"@earendil-works/pi-ai\";\nimport { hasApi } from \"@earendil-works/pi-ai\";\n\nexport function isCodexRemoteCompactionModel(\n\tmodel: Model<Api> | undefined,\n): model is Model<\"openai-codex-responses\"> {\n\treturn model !== undefined && hasApi(model, \"openai-codex-responses\");\n}\n"],
5
+ "mappings": ";;;;AACA,SAAS,cAAc;AAEhB,SAAS,6BACf,OAC2C;AAC3C,SAAO,UAAU,UAAa,OAAO,OAAO,wBAAwB;AACrE;",
6
+ "names": []
7
+ }
@@ -0,0 +1,191 @@
1
+ // @generated by scripts/build-runtime.mjs; do not edit.
2
+ // @ts-nocheck -- generated JavaScript uses a .ts extension for Pi's Jiti loader.
3
+ import {
4
+ isCodexRemoteCompactionModel
5
+ } from "./chunk-3WRPJHG2.js";
6
+
7
+ // src/settings-menu.ts
8
+ function safeText(value) {
9
+ return Array.from(value, (character) => {
10
+ const codePoint = character.codePointAt(0) ?? 0;
11
+ return codePoint < 32 || codePoint >= 127 && codePoint <= 159 ? " " : character;
12
+ }).join("");
13
+ }
14
+ function timeoutLabel(milliseconds) {
15
+ return `${milliseconds / 6e4} min`;
16
+ }
17
+ function retentionLabel(tokens) {
18
+ return `${tokens / 1e3}K tokens`;
19
+ }
20
+ async function update(runtime, ctx, patch, signal) {
21
+ try {
22
+ await runtime.update(patch, signal);
23
+ ctx.ui.notify("Codex compaction settings saved.", "info");
24
+ return { kind: "stay" };
25
+ } catch (error) {
26
+ if (signal.aborted) return { kind: "rejected" };
27
+ ctx.ui.notify(
28
+ `Could not save pi-codex-compact.json: ${safeText(error instanceof Error ? error.message : String(error))}`,
29
+ "error"
30
+ );
31
+ return { kind: "rejected" };
32
+ }
33
+ }
34
+ function createCodexCompactMenu(runtime, options = {}) {
35
+ return {
36
+ start: "main",
37
+ screens: {
38
+ main: ({ state }) => ({
39
+ kind: "actions",
40
+ title: "Codex Remote Compaction",
41
+ lines: [
42
+ `Remote V2: ${state.settings.enabled ? "On" : "Off"}`,
43
+ `Active model: ${safeText(options.status?.model ?? "none")}`,
44
+ `Compact route: ${safeText(compactRoute(state, options.status))}`
45
+ ],
46
+ items: [
47
+ {
48
+ id: "compact-now",
49
+ label: "Compact now",
50
+ description: "Close this menu and compact the active session immediately.",
51
+ action: "compact-now"
52
+ },
53
+ state.kind === "invalid" ? {
54
+ id: "settings",
55
+ label: "Settings",
56
+ description: "Read-only until the invalid settings file is repaired.",
57
+ to: "invalid"
58
+ } : { id: "settings", label: "Settings", to: "settings" },
59
+ { id: "close", label: "Close", close: true }
60
+ ],
61
+ hint: "close"
62
+ }),
63
+ settings: ({ state }) => ({
64
+ kind: "settings",
65
+ title: "Codex Remote Compaction Settings",
66
+ lines: [`User settings \xB7 ${safeText(state.path)}`],
67
+ items: [
68
+ {
69
+ id: "enabled",
70
+ label: "Remote compaction",
71
+ description: "Use Codex Remote V2 when the active model is compatible.",
72
+ currentValue: state.settings.enabled ? "On" : "Off",
73
+ values: ["On", "Off"],
74
+ action: "set-enabled"
75
+ },
76
+ {
77
+ id: "requestTimeoutMs",
78
+ label: "Request timeout",
79
+ description: "Maximum time for the extension-owned remote compaction request.",
80
+ currentValue: timeoutLabel(state.settings.requestTimeoutMs),
81
+ values: ["2 min", "5 min", "10 min"],
82
+ action: "set-timeout"
83
+ },
84
+ {
85
+ id: "maxRetries",
86
+ label: "Transport retries",
87
+ description: "Retry transient provider failures before falling back to Pi.",
88
+ currentValue: String(state.settings.maxRetries),
89
+ values: ["0", "1", "2"],
90
+ action: "set-retries"
91
+ },
92
+ {
93
+ id: "replacementTokenBudget",
94
+ label: "Retained user history",
95
+ description: "Approximate user-message budget kept beside the opaque checkpoint.",
96
+ currentValue: retentionLabel(state.settings.replacementTokenBudget),
97
+ values: ["32K tokens", "64K tokens", "96K tokens", "128K tokens"],
98
+ action: "set-retention"
99
+ },
100
+ {
101
+ id: "notifyOnFallback",
102
+ label: "Fallback notifications",
103
+ description: "Warn when Remote V2 fails and native Pi compaction takes over.",
104
+ currentValue: state.settings.notifyOnFallback ? "On" : "Off",
105
+ values: ["On", "Off"],
106
+ action: "set-notify"
107
+ }
108
+ ]
109
+ }),
110
+ invalid: ({ state }) => ({
111
+ kind: "detail",
112
+ title: "Codex Compact Settings \xB7 Read only",
113
+ lines: [
114
+ `Invalid settings file: ${safeText(state.path)}`,
115
+ `Issue: ${safeText(state.issue ?? "unknown validation error")}`,
116
+ "Built-in defaults are active. Repair the file and run /reload; it will not be overwritten."
117
+ ],
118
+ hint: "back"
119
+ })
120
+ },
121
+ actions: {
122
+ "compact-now": async () => {
123
+ options.onCompactRequested?.();
124
+ return { kind: "close" };
125
+ },
126
+ "set-enabled": ({ ctx, value, signal }) => update(runtime, ctx, { enabled: value === "On" }, signal),
127
+ "set-timeout": ({ ctx, value, signal }) => update(
128
+ runtime,
129
+ ctx,
130
+ { requestTimeoutMs: Number.parseInt(value ?? "5", 10) * 6e4 },
131
+ signal
132
+ ),
133
+ "set-retries": ({ ctx, value, signal }) => update(runtime, ctx, { maxRetries: Number.parseInt(value ?? "2", 10) }, signal),
134
+ "set-retention": ({ ctx, value, signal }) => update(
135
+ runtime,
136
+ ctx,
137
+ { replacementTokenBudget: Number.parseInt(value ?? "64", 10) * 1e3 },
138
+ signal
139
+ ),
140
+ "set-notify": ({ ctx, value, signal }) => update(runtime, ctx, { notifyOnFallback: value === "On" }, signal)
141
+ }
142
+ };
143
+ }
144
+ async function showCodexCompactMenu(runtime, ctx, owner) {
145
+ if (ctx.mode !== "tui") {
146
+ ctx.ui.notify(`Edit Codex compaction settings at ${safeText(runtime.get().path)}.`, "info");
147
+ return;
148
+ }
149
+ const { runMenu } = await import("@narumitw/pi-tui-kit");
150
+ if (owner.signal.aborted || !owner.isCurrent()) return;
151
+ let compactRequested = false;
152
+ await runMenu(
153
+ ctx,
154
+ createCodexCompactMenu(runtime, {
155
+ onCompactRequested: () => {
156
+ compactRequested = true;
157
+ },
158
+ status: compactMenuStatus(ctx)
159
+ }),
160
+ {
161
+ getState: () => runtime.get(),
162
+ signal: owner.signal,
163
+ isCurrent: owner.isCurrent
164
+ }
165
+ );
166
+ if (!compactRequested || owner.signal.aborted || !owner.isCurrent()) return;
167
+ ctx.compact({
168
+ onError: (error) => {
169
+ if (!owner.signal.aborted && owner.isCurrent()) {
170
+ ctx.ui.notify(`Compaction failed: ${safeText(error.message)}`, "error");
171
+ }
172
+ }
173
+ });
174
+ }
175
+ function compactMenuStatus(ctx) {
176
+ const model = ctx.model;
177
+ return {
178
+ model: model ? `${model.provider}/${model.id}` : "none",
179
+ remoteCompatible: isCodexRemoteCompactionModel(model)
180
+ };
181
+ }
182
+ function compactRoute(state, status) {
183
+ if (!state.settings.enabled) return "Pi native (Remote V2 off)";
184
+ return status?.remoteCompatible ? "Codex Remote V2" : "Pi native (model not compatible)";
185
+ }
186
+ export {
187
+ compactMenuStatus,
188
+ createCodexCompactMenu,
189
+ showCodexCompactMenu
190
+ };
191
+ //# sourceMappingURL=settings-menu-4YW2GOKW.ts.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/settings-menu.ts"],
4
+ "sourcesContent": ["import type { ExtensionCommandContext } from \"@earendil-works/pi-coding-agent\";\nimport type { MenuDefinition } from \"@narumitw/pi-tui-kit\";\nimport { isCodexRemoteCompactionModel } from \"./model-compatibility.js\";\nimport type {\n\tCodexCompactSettings,\n\tCodexCompactSettingsRuntime,\n\tCodexCompactSettingsState,\n} from \"./settings.js\";\n\ntype Screen = \"main\" | \"settings\" | \"invalid\";\ntype Action =\n\t| \"compact-now\"\n\t| \"set-enabled\"\n\t| \"set-timeout\"\n\t| \"set-retries\"\n\t| \"set-retention\"\n\t| \"set-notify\";\n\nexport interface SettingsMenuOwner {\n\tsignal: AbortSignal;\n\tisCurrent(): boolean;\n}\n\ninterface CompactMenuStatus {\n\tmodel: string;\n\tremoteCompatible: boolean;\n}\n\nfunction safeText(value: string): string {\n\treturn Array.from(value, (character) => {\n\t\tconst codePoint = character.codePointAt(0) ?? 0;\n\t\treturn codePoint < 32 || (codePoint >= 127 && codePoint <= 159) ? \" \" : character;\n\t}).join(\"\");\n}\n\nfunction timeoutLabel(milliseconds: number): string {\n\treturn `${milliseconds / 60_000} min`;\n}\n\nfunction retentionLabel(tokens: number): string {\n\treturn `${tokens / 1000}K tokens`;\n}\n\nasync function update(\n\truntime: CodexCompactSettingsRuntime,\n\tctx: ExtensionCommandContext,\n\tpatch: Partial<CodexCompactSettings>,\n\tsignal: AbortSignal,\n) {\n\ttry {\n\t\tawait runtime.update(patch, signal);\n\t\tctx.ui.notify(\"Codex compaction settings saved.\", \"info\");\n\t\treturn { kind: \"stay\" as const };\n\t} catch (error) {\n\t\tif (signal.aborted) return { kind: \"rejected\" as const };\n\t\tctx.ui.notify(\n\t\t\t`Could not save pi-codex-compact.json: ${safeText(error instanceof Error ? error.message : String(error))}`,\n\t\t\t\"error\",\n\t\t);\n\t\treturn { kind: \"rejected\" as const };\n\t}\n}\n\nexport function createCodexCompactMenu(\n\truntime: CodexCompactSettingsRuntime,\n\toptions: { onCompactRequested?: () => void; status?: CompactMenuStatus } = {},\n): MenuDefinition<CodexCompactSettingsState, Screen, Action, ExtensionCommandContext> {\n\treturn {\n\t\tstart: \"main\",\n\t\tscreens: {\n\t\t\tmain: ({ state }) => ({\n\t\t\t\tkind: \"actions\",\n\t\t\t\ttitle: \"Codex Remote Compaction\",\n\t\t\t\tlines: [\n\t\t\t\t\t`Remote V2: ${state.settings.enabled ? \"On\" : \"Off\"}`,\n\t\t\t\t\t`Active model: ${safeText(options.status?.model ?? \"none\")}`,\n\t\t\t\t\t`Compact route: ${safeText(compactRoute(state, options.status))}`,\n\t\t\t\t],\n\t\t\t\titems: [\n\t\t\t\t\t{\n\t\t\t\t\t\tid: \"compact-now\",\n\t\t\t\t\t\tlabel: \"Compact now\",\n\t\t\t\t\t\tdescription: \"Close this menu and compact the active session immediately.\",\n\t\t\t\t\t\taction: \"compact-now\",\n\t\t\t\t\t},\n\t\t\t\t\tstate.kind === \"invalid\"\n\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\tid: \"settings\",\n\t\t\t\t\t\t\t\tlabel: \"Settings\",\n\t\t\t\t\t\t\t\tdescription: \"Read-only until the invalid settings file is repaired.\",\n\t\t\t\t\t\t\t\tto: \"invalid\" as const,\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t: { id: \"settings\", label: \"Settings\", to: \"settings\" as const },\n\t\t\t\t\t{ id: \"close\", label: \"Close\", close: true },\n\t\t\t\t],\n\t\t\t\thint: \"close\",\n\t\t\t}),\n\t\t\tsettings: ({ state }) => ({\n\t\t\t\tkind: \"settings\",\n\t\t\t\ttitle: \"Codex Remote Compaction Settings\",\n\t\t\t\tlines: [`User settings \u00B7 ${safeText(state.path)}`],\n\t\t\t\titems: [\n\t\t\t\t\t{\n\t\t\t\t\t\tid: \"enabled\",\n\t\t\t\t\t\tlabel: \"Remote compaction\",\n\t\t\t\t\t\tdescription: \"Use Codex Remote V2 when the active model is compatible.\",\n\t\t\t\t\t\tcurrentValue: state.settings.enabled ? \"On\" : \"Off\",\n\t\t\t\t\t\tvalues: [\"On\", \"Off\"],\n\t\t\t\t\t\taction: \"set-enabled\",\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tid: \"requestTimeoutMs\",\n\t\t\t\t\t\tlabel: \"Request timeout\",\n\t\t\t\t\t\tdescription: \"Maximum time for the extension-owned remote compaction request.\",\n\t\t\t\t\t\tcurrentValue: timeoutLabel(state.settings.requestTimeoutMs),\n\t\t\t\t\t\tvalues: [\"2 min\", \"5 min\", \"10 min\"],\n\t\t\t\t\t\taction: \"set-timeout\",\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tid: \"maxRetries\",\n\t\t\t\t\t\tlabel: \"Transport retries\",\n\t\t\t\t\t\tdescription: \"Retry transient provider failures before falling back to Pi.\",\n\t\t\t\t\t\tcurrentValue: String(state.settings.maxRetries),\n\t\t\t\t\t\tvalues: [\"0\", \"1\", \"2\"],\n\t\t\t\t\t\taction: \"set-retries\",\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tid: \"replacementTokenBudget\",\n\t\t\t\t\t\tlabel: \"Retained user history\",\n\t\t\t\t\t\tdescription: \"Approximate user-message budget kept beside the opaque checkpoint.\",\n\t\t\t\t\t\tcurrentValue: retentionLabel(state.settings.replacementTokenBudget),\n\t\t\t\t\t\tvalues: [\"32K tokens\", \"64K tokens\", \"96K tokens\", \"128K tokens\"],\n\t\t\t\t\t\taction: \"set-retention\",\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tid: \"notifyOnFallback\",\n\t\t\t\t\t\tlabel: \"Fallback notifications\",\n\t\t\t\t\t\tdescription: \"Warn when Remote V2 fails and native Pi compaction takes over.\",\n\t\t\t\t\t\tcurrentValue: state.settings.notifyOnFallback ? \"On\" : \"Off\",\n\t\t\t\t\t\tvalues: [\"On\", \"Off\"],\n\t\t\t\t\t\taction: \"set-notify\",\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t}),\n\t\t\tinvalid: ({ state }) => ({\n\t\t\t\tkind: \"detail\",\n\t\t\t\ttitle: \"Codex Compact Settings \u00B7 Read only\",\n\t\t\t\tlines: [\n\t\t\t\t\t`Invalid settings file: ${safeText(state.path)}`,\n\t\t\t\t\t`Issue: ${safeText(state.issue ?? \"unknown validation error\")}`,\n\t\t\t\t\t\"Built-in defaults are active. Repair the file and run /reload; it will not be overwritten.\",\n\t\t\t\t],\n\t\t\t\thint: \"back\",\n\t\t\t}),\n\t\t},\n\t\tactions: {\n\t\t\t\"compact-now\": async () => {\n\t\t\t\toptions.onCompactRequested?.();\n\t\t\t\treturn { kind: \"close\" };\n\t\t\t},\n\t\t\t\"set-enabled\": ({ ctx, value, signal }) =>\n\t\t\t\tupdate(runtime, ctx, { enabled: value === \"On\" }, signal),\n\t\t\t\"set-timeout\": ({ ctx, value, signal }) =>\n\t\t\t\tupdate(\n\t\t\t\t\truntime,\n\t\t\t\t\tctx,\n\t\t\t\t\t{ requestTimeoutMs: Number.parseInt(value ?? \"5\", 10) * 60_000 },\n\t\t\t\t\tsignal,\n\t\t\t\t),\n\t\t\t\"set-retries\": ({ ctx, value, signal }) =>\n\t\t\t\tupdate(runtime, ctx, { maxRetries: Number.parseInt(value ?? \"2\", 10) }, signal),\n\t\t\t\"set-retention\": ({ ctx, value, signal }) =>\n\t\t\t\tupdate(\n\t\t\t\t\truntime,\n\t\t\t\t\tctx,\n\t\t\t\t\t{ replacementTokenBudget: Number.parseInt(value ?? \"64\", 10) * 1000 },\n\t\t\t\t\tsignal,\n\t\t\t\t),\n\t\t\t\"set-notify\": ({ ctx, value, signal }) =>\n\t\t\t\tupdate(runtime, ctx, { notifyOnFallback: value === \"On\" }, signal),\n\t\t},\n\t};\n}\n\nexport async function showCodexCompactMenu(\n\truntime: CodexCompactSettingsRuntime,\n\tctx: ExtensionCommandContext,\n\towner: SettingsMenuOwner,\n): Promise<void> {\n\tif (ctx.mode !== \"tui\") {\n\t\tctx.ui.notify(`Edit Codex compaction settings at ${safeText(runtime.get().path)}.`, \"info\");\n\t\treturn;\n\t}\n\tconst { runMenu } = await import(\"@narumitw/pi-tui-kit\");\n\tif (owner.signal.aborted || !owner.isCurrent()) return;\n\tlet compactRequested = false;\n\tawait runMenu(\n\t\tctx,\n\t\tcreateCodexCompactMenu(runtime, {\n\t\t\tonCompactRequested: () => {\n\t\t\t\tcompactRequested = true;\n\t\t\t},\n\t\t\tstatus: compactMenuStatus(ctx),\n\t\t}),\n\t\t{\n\t\t\tgetState: () => runtime.get(),\n\t\t\tsignal: owner.signal,\n\t\t\tisCurrent: owner.isCurrent,\n\t\t},\n\t);\n\tif (!compactRequested || owner.signal.aborted || !owner.isCurrent()) return;\n\tctx.compact({\n\t\tonError: (error) => {\n\t\t\tif (!owner.signal.aborted && owner.isCurrent()) {\n\t\t\t\tctx.ui.notify(`Compaction failed: ${safeText(error.message)}`, \"error\");\n\t\t\t}\n\t\t},\n\t});\n}\n\nexport function compactMenuStatus(ctx: ExtensionCommandContext): CompactMenuStatus {\n\tconst model = ctx.model;\n\treturn {\n\t\tmodel: model ? `${model.provider}/${model.id}` : \"none\",\n\t\tremoteCompatible: isCodexRemoteCompactionModel(model),\n\t};\n}\n\nfunction compactRoute(\n\tstate: Readonly<CodexCompactSettingsState>,\n\tstatus: CompactMenuStatus | undefined,\n): string {\n\tif (!state.settings.enabled) return \"Pi native (Remote V2 off)\";\n\treturn status?.remoteCompatible ? \"Codex Remote V2\" : \"Pi native (model not compatible)\";\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }