@narumitw/pi-codex-compact 0.50.1 → 0.50.2

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 OpenAI Codex Remote Compaction V2 in Pi instead of generating a local plaintext summary for compatible `openai-codex` sessions.
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
13
  - 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.
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
48
  1. Sign in through Pi's built-in OpenAI Codex OAuth provider.
57
49
  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.
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
55
  When the active model is unsupported, 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
 
@@ -142,35 +133,27 @@ This extension does **not** read `~/.codex/config.toml`.
142
133
  - API `openai-codex-responses` on the active model.
143
134
  - A working OpenAI Codex OAuth login and Remote V2 entitlement.
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
+ OpenAI API-key, Azure, GitHub Copilot, proxies, and arbitrary Responses-compatible providers are not supported.
137
+ Switching to another provider or model leaves Pi's visible fallback marker plus retained recent messages in context.
138
+ Switching back to the checkpoint's original compatible model restores opaque replay.
149
139
 
150
140
  ## 🔄 How it works
151
141
 
152
142
  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.
143
+ 2. The extension projects an earlier compatible checkpoint, if present, into the current Responses input.
144
+ 3. It appends exactly one final `compaction_trigger` and sends a normal authenticated Codex Responses SSE request with cache retention disabled.
157
145
  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.
146
+ 5. It constructs bounded replacement history from recent raw user-role Responses items followed by the opaque item.
147
+ 6. It stores that history and fingerprints of Pi's retained suffix in versioned `CompactionEntry.details`.
148
+ 7. On later compatible requests, it replaces an exactly validated marker with the persisted replacement history immediately before provider dispatch.
164
149
 
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.
150
+ If 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
151
 
168
- ## 🔐 Privacy, storage, and limits
152
+ ## 🔒 Security and privacy
169
153
 
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.
154
+ Remote compaction sends the active conversation context, system prompt, and active tool schemas to the same OpenAI Codex backend used by the selected model.
155
+ The Pi session stores the encrypted compaction item and bounded recent user-role Responses items.
156
+ It does not store OAuth tokens, authorization headers, or request headers in checkpoint details.
174
157
 
175
158
  | Boundary | Limit |
176
159
  | --- | ---: |
@@ -182,22 +165,19 @@ authorization headers, or request headers in checkpoint details.
182
165
  | Transport retries | At most 2 |
183
166
  | Request timeout | At most 10 minutes |
184
167
 
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.
168
+ An individually oversized media item is dropped rather than making the session entry unbounded.
169
+ The oldest fitting text item may be partially truncated to preserve newer context.
170
+ These hard byte ceilings are intentionally not configurable.
188
171
 
189
- ## 🚧 Known limitations
172
+ ## 🚧 Limitations
190
173
 
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.
174
+ - The wire contract is undocumented and can change independently of Pi or this package.
175
+ Keep backups of important sessions.
176
+ - Full older history depends on this extension and the same compatible model.
177
+ Removing the extension exposes only the portability fallback marker and Pi-retained recent messages.
178
+ - 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.
179
+ - Remote failure falls back to Pi's plaintext summary, so a session can contain both remote opaque and native compaction entries over time.
180
+ - Settings concurrency is coordinated only within one Pi process; separate processes rely on the final conflict check.
201
181
 
202
182
  ## 🗂️ Package layout
203
183
 
@@ -208,11 +188,36 @@ src/remote.ts Provider stream invocation, auth payload, timeout, and ret
208
188
  src/protocol.ts Bounded SSE parsing and Remote V2 payload/output validation
209
189
  src/checkpoint.ts Replacement history, fingerprints, persistence, and replay projection
210
190
  src/settings.ts Global settings validation and atomic persistence
211
- src/settings-menu.ts Manual compaction and settings TUI
191
+ src/settings-menu.ts First-use manual compaction and settings TUI
212
192
 
213
- test/ Protocol, checkpoint, lifecycle, remote, settings, and menu coverage
193
+ dist/ Generated source-mapped Jiti runtime and lazy menu chunk
194
+ scripts/ Runtime builder
195
+ benchmark/ Three-arm compaction benchmark, self-test, and methodology
196
+ test/ Protocol, checkpoint, lifecycle, remote, settings, menu, and builder coverage
214
197
  ```
215
198
 
199
+ ## 📊 Benchmark
200
+
201
+ 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.
202
+
203
+ It holds history length nearly fixed while varying information density across five state categories and ten history epochs.
204
+
205
+ 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.
206
+
207
+ 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.
208
+
209
+ Preview its exploratory diagnostic without making a provider request:
210
+
211
+ ```bash
212
+ just benchmark-codex-compact
213
+ ```
214
+
215
+ A live run requires an explicit `--live` flag, reviewed request and cost exposure, OpenAI Codex OAuth, and Remote V2 entitlement.
216
+
217
+ 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.
218
+
219
+ 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.
220
+
216
221
  ## 🧪 Development
217
222
 
218
223
  From the repository root:
@@ -223,14 +228,11 @@ npm test
223
228
  just pack codex-compact
224
229
  ```
225
230
 
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.
231
+ 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
232
 
230
233
  ## 🔎 Keywords
231
234
 
232
- Pi extension, Pi coding agent, OpenAI Codex, OAuth, Remote Compaction V2, opaque checkpoint,
233
- Responses API, context compaction.
235
+ Pi extension, Pi coding agent, OpenAI Codex, OAuth, Remote Compaction V2, opaque checkpoint, Responses API, context compaction.
234
236
 
235
237
  ## 📄 License
236
238
 
@@ -0,0 +1,187 @@
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/settings-menu.ts
5
+ function safeText(value) {
6
+ return Array.from(value, (character) => {
7
+ const codePoint = character.codePointAt(0) ?? 0;
8
+ return codePoint < 32 || codePoint >= 127 && codePoint <= 159 ? " " : character;
9
+ }).join("");
10
+ }
11
+ function timeoutLabel(milliseconds) {
12
+ return `${milliseconds / 6e4} min`;
13
+ }
14
+ function retentionLabel(tokens) {
15
+ return `${tokens / 1e3}K tokens`;
16
+ }
17
+ async function update(runtime, ctx, patch, signal) {
18
+ try {
19
+ await runtime.update(patch, signal);
20
+ ctx.ui.notify("Codex compaction settings saved.", "info");
21
+ return { kind: "stay" };
22
+ } catch (error) {
23
+ if (signal.aborted) return { kind: "rejected" };
24
+ ctx.ui.notify(
25
+ `Could not save pi-codex-compact.json: ${safeText(error instanceof Error ? error.message : String(error))}`,
26
+ "error"
27
+ );
28
+ return { kind: "rejected" };
29
+ }
30
+ }
31
+ function createCodexCompactMenu(runtime, options = {}) {
32
+ return {
33
+ start: "main",
34
+ screens: {
35
+ main: ({ state }) => ({
36
+ kind: "actions",
37
+ title: "Codex Remote Compaction",
38
+ lines: [
39
+ `Remote V2: ${state.settings.enabled ? "On" : "Off"}`,
40
+ `Active model: ${safeText(options.status?.model ?? "none")}`,
41
+ `Compact route: ${safeText(compactRoute(state, options.status))}`
42
+ ],
43
+ items: [
44
+ {
45
+ id: "compact-now",
46
+ label: "Compact now",
47
+ description: "Close this menu and compact the active session immediately.",
48
+ action: "compact-now"
49
+ },
50
+ state.kind === "invalid" ? {
51
+ id: "settings",
52
+ label: "Settings",
53
+ description: "Read-only until the invalid settings file is repaired.",
54
+ to: "invalid"
55
+ } : { id: "settings", label: "Settings", to: "settings" },
56
+ { id: "close", label: "Close", close: true }
57
+ ],
58
+ hint: "close"
59
+ }),
60
+ settings: ({ state }) => ({
61
+ kind: "settings",
62
+ title: "Codex Remote Compaction Settings",
63
+ lines: [`User settings \xB7 ${safeText(state.path)}`],
64
+ items: [
65
+ {
66
+ id: "enabled",
67
+ label: "Remote compaction",
68
+ description: "Use Codex Remote V2 when the active model is compatible.",
69
+ currentValue: state.settings.enabled ? "On" : "Off",
70
+ values: ["On", "Off"],
71
+ action: "set-enabled"
72
+ },
73
+ {
74
+ id: "requestTimeoutMs",
75
+ label: "Request timeout",
76
+ description: "Maximum time for the extension-owned remote compaction request.",
77
+ currentValue: timeoutLabel(state.settings.requestTimeoutMs),
78
+ values: ["2 min", "5 min", "10 min"],
79
+ action: "set-timeout"
80
+ },
81
+ {
82
+ id: "maxRetries",
83
+ label: "Transport retries",
84
+ description: "Retry transient provider failures before falling back to Pi.",
85
+ currentValue: String(state.settings.maxRetries),
86
+ values: ["0", "1", "2"],
87
+ action: "set-retries"
88
+ },
89
+ {
90
+ id: "replacementTokenBudget",
91
+ label: "Retained user history",
92
+ description: "Approximate user-message budget kept beside the opaque checkpoint.",
93
+ currentValue: retentionLabel(state.settings.replacementTokenBudget),
94
+ values: ["32K tokens", "64K tokens", "96K tokens", "128K tokens"],
95
+ action: "set-retention"
96
+ },
97
+ {
98
+ id: "notifyOnFallback",
99
+ label: "Fallback notifications",
100
+ description: "Warn when Remote V2 fails and native Pi compaction takes over.",
101
+ currentValue: state.settings.notifyOnFallback ? "On" : "Off",
102
+ values: ["On", "Off"],
103
+ action: "set-notify"
104
+ }
105
+ ]
106
+ }),
107
+ invalid: ({ state }) => ({
108
+ kind: "detail",
109
+ title: "Codex Compact Settings \xB7 Read only",
110
+ lines: [
111
+ `Invalid settings file: ${safeText(state.path)}`,
112
+ `Issue: ${safeText(state.issue ?? "unknown validation error")}`,
113
+ "Built-in defaults are active. Repair the file and run /reload; it will not be overwritten."
114
+ ],
115
+ hint: "back"
116
+ })
117
+ },
118
+ actions: {
119
+ "compact-now": async () => {
120
+ options.onCompactRequested?.();
121
+ return { kind: "close" };
122
+ },
123
+ "set-enabled": ({ ctx, value, signal }) => update(runtime, ctx, { enabled: value === "On" }, signal),
124
+ "set-timeout": ({ ctx, value, signal }) => update(
125
+ runtime,
126
+ ctx,
127
+ { requestTimeoutMs: Number.parseInt(value ?? "5", 10) * 6e4 },
128
+ signal
129
+ ),
130
+ "set-retries": ({ ctx, value, signal }) => update(runtime, ctx, { maxRetries: Number.parseInt(value ?? "2", 10) }, signal),
131
+ "set-retention": ({ ctx, value, signal }) => update(
132
+ runtime,
133
+ ctx,
134
+ { replacementTokenBudget: Number.parseInt(value ?? "64", 10) * 1e3 },
135
+ signal
136
+ ),
137
+ "set-notify": ({ ctx, value, signal }) => update(runtime, ctx, { notifyOnFallback: value === "On" }, signal)
138
+ }
139
+ };
140
+ }
141
+ async function showCodexCompactMenu(runtime, ctx, owner) {
142
+ if (ctx.mode !== "tui") {
143
+ ctx.ui.notify(`Edit Codex compaction settings at ${safeText(runtime.get().path)}.`, "info");
144
+ return;
145
+ }
146
+ const { runMenu } = await import("@narumitw/pi-tui-kit");
147
+ if (owner.signal.aborted || !owner.isCurrent()) return;
148
+ let compactRequested = false;
149
+ await runMenu(
150
+ ctx,
151
+ createCodexCompactMenu(runtime, {
152
+ onCompactRequested: () => {
153
+ compactRequested = true;
154
+ },
155
+ status: compactMenuStatus(ctx)
156
+ }),
157
+ {
158
+ getState: () => runtime.get(),
159
+ signal: owner.signal,
160
+ isCurrent: owner.isCurrent
161
+ }
162
+ );
163
+ if (!compactRequested || owner.signal.aborted || !owner.isCurrent()) return;
164
+ ctx.compact({
165
+ onError: (error) => {
166
+ if (!owner.signal.aborted && owner.isCurrent()) {
167
+ ctx.ui.notify(`Compaction failed: ${safeText(error.message)}`, "error");
168
+ }
169
+ }
170
+ });
171
+ }
172
+ function compactMenuStatus(ctx) {
173
+ const model = ctx.model;
174
+ return {
175
+ model: model ? `${model.provider}/${model.id}` : "none",
176
+ remoteCompatible: model?.provider === "openai-codex" && model.api === "openai-codex-responses"
177
+ };
178
+ }
179
+ function compactRoute(state, status) {
180
+ if (!state.settings.enabled) return "Pi native (Remote V2 off)";
181
+ return status?.remoteCompatible ? "Codex Remote V2" : "Pi native (model not compatible)";
182
+ }
183
+ export {
184
+ createCodexCompactMenu,
185
+ showCodexCompactMenu
186
+ };
187
+ //# sourceMappingURL=settings-menu-BLWCLVPZ.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 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\nfunction compactMenuStatus(ctx: ExtensionCommandContext): CompactMenuStatus {\n\tconst model = ctx.model;\n\treturn {\n\t\tmodel: model ? `${model.provider}/${model.id}` : \"none\",\n\t\tremoteCompatible: model?.provider === \"openai-codex\" && model.api === \"openai-codex-responses\",\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
+ }