sprag-cli 3.52.0 → 3.54.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.
Files changed (42) hide show
  1. package/README.ko.md +46 -3
  2. package/README.md +50 -4
  3. package/bin/cli.js +35 -4
  4. package/docs/CODEX.ko.md +172 -0
  5. package/docs/CODEX.md +485 -0
  6. package/package.json +11 -3
  7. package/presets/delegation/bounds.md +29 -0
  8. package/presets/ratchet-rules.json +4 -1
  9. package/src/agent.js +33 -0
  10. package/src/codex-brief.js +89 -0
  11. package/src/codex-budget.js +73 -0
  12. package/src/codex-cache-policy.js +203 -0
  13. package/src/codex-cache.js +46 -0
  14. package/src/codex-delegation.js +154 -0
  15. package/src/codex-doc2md-ledger.js +45 -0
  16. package/src/codex-doc2md.js +99 -0
  17. package/src/codex-doctor.js +53 -0
  18. package/src/codex-harness.js +200 -0
  19. package/src/codex-hooks.js +131 -0
  20. package/src/codex-installer.js +79 -0
  21. package/src/codex-ledger.js +143 -0
  22. package/src/codex-panel-launcher.js +48 -0
  23. package/src/codex-panel-runner.js +74 -0
  24. package/src/codex-panel-session.js +35 -0
  25. package/src/codex-panel-shell.js +51 -0
  26. package/src/codex-panel-state.js +40 -0
  27. package/src/codex-panel.js +448 -0
  28. package/src/codex-parser.js +397 -0
  29. package/src/codex-route-scan.js +171 -0
  30. package/src/commands/brief.js +8 -1
  31. package/src/commands/codex-delegate.js +59 -0
  32. package/src/commands/codex.js +557 -0
  33. package/src/commands/delegate.js +133 -0
  34. package/src/commands/seed.js +11 -10
  35. package/src/delegation-guard.js +278 -0
  36. package/src/doc2md.cjs +72 -28
  37. package/src/handoff.js +27 -7
  38. package/src/harness.js +26 -17
  39. package/src/installer.js +168 -0
  40. package/src/seed-rules.js +58 -34
  41. package/src/session-paths.js +242 -0
  42. package/src/update-check.js +37 -2
package/docs/CODEX.md ADDED
@@ -0,0 +1,485 @@
1
+ # Codex Integration
2
+
3
+ [← README](../README.md) · [한국어](./CODEX.ko.md)
4
+
5
+ Sprag supports Codex through its native `AGENTS.md` and `hooks.json` interfaces.
6
+ The default agent remains Claude Code. Pass `--agent codex` before or after the
7
+ subcommand to select Codex explicitly; this does not change a global default.
8
+
9
+ ## Install
10
+
11
+ For a Codex-only installation, skip npm's existing Claude Code postinstall:
12
+
13
+ ```sh
14
+ npm i -g sprag-cli --ignore-scripts
15
+ sprag install --agent codex
16
+ ```
17
+
18
+ If Sprag is already installed for Claude Code, just run the second command.
19
+ Both integrations can coexist. Codex installation uses `$CODEX_HOME` when set,
20
+ otherwise `~/.codex`. It adds the integration hooks to `hooks.json` and a marked harness
21
+ block to the global `AGENTS.md`. A non-empty `AGENTS.override.md` takes precedence,
22
+ so the block is added there instead when present. Existing instructions and
23
+ other hooks are preserved; changed files are backed up. `config.toml`, hook
24
+ trust settings, and Claude Code settings are never changed by this command.
25
+
26
+ Open **`/hooks` in Codex**, review the Sprag commands, and trust them. New or
27
+ changed hooks do not run until trusted. Use a Codex release that supports the
28
+ events documented below. Admin policies or `features.hooks = false` may disable
29
+ hooks. The `sprag` executable and Node.js must be on Codex's PATH.
30
+
31
+ On macOS, installation also registers the panel-start hook in the same step,
32
+ so all six hooks can be reviewed together (five without the optional panel).
33
+ `install --no-panel --agent codex`
34
+ skips it; an explicit `panel auto off` preference survives reinstall. Installation
35
+ reports registration, not successful activation. After approval and restarting
36
+ Codex, run `sprag panel doctor --agent codex` to check the last hook execution,
37
+ any launch failure, and recent rendered frames. These diagnostics are local;
38
+ they do not read or change Codex's trust database.
39
+
40
+ `CTS_NO_HARNESS=1` skips the global harness. Korean guidance is opt-in, using the
41
+ existing shared Sprag preference:
42
+
43
+ ```sh
44
+ sprag korean on --agent codex
45
+ sprag korean lint warn --agent codex
46
+ sprag doc2md install-converter --agent codex
47
+ ```
48
+
49
+ `korean on`, `cohesion on`, `brief on`, `doc2md on`, and `delegate on`
50
+ register Codex hooks before saving the enabled preference. If hook registration
51
+ fails, the preference stays unchanged. Review new or changed definitions in
52
+ `/hooks`; registration does not grant trust. Turning a feature off keeps the
53
+ shared hooks needed by other features.
54
+
55
+ ## Supported Workflows
56
+
57
+ | Feature | Codex behavior |
58
+ |---|---|
59
+ | Harness | Five principles in global or project `AGENTS.md`, with override-file support |
60
+ | Ratchet rules | Separate Codex files; global and project rules injected at SessionStart, including resume and compaction |
61
+ | Korean guidance / English cohesion | SessionStart injection using shared Sprag preferences |
62
+ | Korean write-time lint | PostToolUse feedback for `apply_patch` and recognized shell write targets; block, warn, and off modes |
63
+ | Documents | Shared converter, prompt paths, supported file reads, and recognized document/cache write guards; `sprag doc2md <file> --agent codex` also works manually |
64
+ | Token report | Table, JSON, or CSV from local Codex rollout logs, with time and project filters |
65
+ | Statusline | `--statusline` or `--format statusline` renders the compact panel once, with wrapping and label/color preferences |
66
+ | Briefing | UserPromptSubmit reports fresh context/limit threshold crossings once per session and threshold cycle |
67
+ | Warning history | `last` and `history` read Codex-only, per-session warning records |
68
+ | Handoff | Git state, session/model, recorded context/tokens/limits, and fillable remaining-work sections |
69
+ | Subagents | Opt-in native spawn model routing, scoped category rules, and SubagentStart guidance |
70
+ | Cache timer | Resolved provider retention policy after recorded cache usage; exact expiry is unknown |
71
+ | Route discovery | Repeated simple turns grouped by project, provider, and parent model; no automatic registration |
72
+ | Ratchet detection | Recent repeated command/patch failures produce `ratchet?` candidates |
73
+ | Seed presets | Compatible ratchet rules, separate decisions, explicit registration scope |
74
+ | Routing saved | Sprag-attributed child tokens priced with matching LiteLLM rates; an estimate, not billed savings |
75
+ | Diagnostics | `doctor` checks user config, hook registration, harness, logs, panel activity, and preferences |
76
+ | Capability matrix | `capabilities` lists supported, partial, native, and unsupported areas without invoking a model |
77
+
78
+ The document converter is shared with Claude Code and requires its optional
79
+ converter dependencies. Disable automatic Codex conversion and write guards with
80
+ `sprag doc2md off --agent codex` or `CTS_NO_DOC2MD=1`. PreToolUse handles supported
81
+ file-read calls and literal single-file `cat`, `head`, and `tail` reads. Successful
82
+ conversion blocks the original read with the Markdown path for a retry. Conversion
83
+ failures are reported; unsafe archives are denied. Patches and recognized shell
84
+ text writes cannot overwrite binary documents or conversion caches. Arbitrary
85
+ extraction scripts and compound shell reads are not intercepted; use the manual
86
+ command. Text extraction never verifies charts, images, or layout.
87
+
88
+ Conversion hooks have a 600-second outer timeout to accommodate interpreter
89
+ probes and multiple conversions; each converter invocation retains its own
90
+ 120-second limit. An unavailable Codex cache view is reported as
91
+ `cache-unavailable` without suppressing other document results or the context
92
+ briefing. Both byte-clipped output and converter-reported truncation are marked
93
+ as incomplete in read and prompt feedback.
94
+
95
+ Codex keeps document accounting separate from Claude. `Doc2md N docs (Codex total)`
96
+ counts distinct source paths successfully prepared through Codex, including reuse
97
+ of an existing conversion. Reusing or reconverting a source does not increment its
98
+ count. The total is scoped to Codex home, not the current session. Old shared
99
+ history is not imported. Counts are sorted by frequency, then extension, with no
100
+ Claude pricing involved. Codex reads a neutral Markdown view without the shared
101
+ cache's Claude-based savings banner; the shared cache remains unchanged.
102
+
103
+ ## Diagnostics And Continuity
104
+
105
+ ```sh
106
+ sprag doctor --agent codex
107
+ sprag doctor --agent codex --format json
108
+ sprag capabilities --agent codex
109
+ sprag brief --agent codex
110
+ sprag brief on --agent codex
111
+ sprag last --agent codex
112
+ sprag history --agent codex --days 7 --format json
113
+ sprag history --agent codex --session SESSION_ID
114
+ sprag handoff --agent codex --cwd /path/to/repo
115
+ sprag delegate on --agent codex
116
+ ```
117
+
118
+ The doctor is read-only. It reports missing/outdated hook definitions, including
119
+ insufficient timeouts or asynchronous handlers, disabled
120
+ hooks in user config, and unavailable logs without treating registration as
121
+ proof of trust. Its config report excludes provider credentials and is not an
122
+ effective-config resolver: active profiles, project config, managed policy,
123
+ and invocation overrides can differ. Check Codex `/hooks` for approval.
124
+
125
+ Briefing is enabled by default after installing and trusting the prompt hook.
126
+ It reads at most the latest 256 KiB of the current transcript and warns when
127
+ recorded input reaches 80% or 95% of the recorded context window, or a reported
128
+ rate-limit window reaches 90%. Missing measurements, measurements older than
129
+ five minutes, and expired limits do not generate new warnings. Lower fresh
130
+ context measurements re-arm the warning after compaction. `brief off` disables
131
+ automatic briefing without disabling document conversion; bare `brief` shows
132
+ current warnings without recording or consuming the automatic notification.
133
+
134
+ History records automatic warning entries, not every tool call or a complete
135
+ audit log. Files are separated by Codex home and session beneath Sprag's state
136
+ directory; each keeps up to 200 warning entries. `--project` filters history by
137
+ substring and `--session` by exact ID. The history remains separate from Claude
138
+ warning records. A handoff file is never overwritten and identifies quota
139
+ values as recorded snapshots, not live responses. Fill in its remaining-work
140
+ sections before using it to continue in another session.
141
+
142
+ Delegation is independently opt-in via `delegate on`. PreToolUse rewrites native
143
+ `spawn_agent` arguments using Codex's `message`, `model`, and `reasoning_effort`
144
+ fields. It preserves explicit model choices and custom roles, including custom
145
+ overrides of built-in roles. SubagentStart injects scoped ratchets and enabled
146
+ writing guidance. Bounds are instructions, not an enforced tool-call budget.
147
+ Normal Codex sandbox and approval rules still apply. Review new hooks in `/hooks`.
148
+
149
+ ```sh
150
+ sprag delegate on --agent codex
151
+ sprag delegate model gpt-6-luna --effort high --agent codex
152
+ sprag delegate model off --agent codex
153
+ sprag delegate rules add explore --from gpt-6-astra --model gpt-6-luna --project --agent codex
154
+ sprag delegate rules --agent codex
155
+ sprag delegate rules rm 1 --agent codex
156
+ ```
157
+
158
+ Use model IDs available from your provider. A default target applies to built-in
159
+ spawns without an explicit model. Category rules (`paste`, `translate`, `explore`,
160
+ `read`, `check`, `run`) reuse Sprag's classifier and escalation gates, require an
161
+ exact parent model, and prefer project rules over global rules. Matching prompts
162
+ request native delegation; unavailable subagent tools leave work on the main
163
+ agent. Claude tier presets and history are not imported.
164
+
165
+ ## Harness Commands
166
+
167
+ ```sh
168
+ sprag harness init --agent codex # current project
169
+ sprag harness check --agent codex # project, or global fallback
170
+ sprag harness init --agent codex --global
171
+ sprag harness promote --agent codex --project "Run the changed module's tests before reporting completion."
172
+ sprag harness list --agent codex --project
173
+ sprag harness rm 1 --agent codex --project
174
+ sprag harness prune --agent codex --global --older-than 6 --dry-run
175
+ sprag harness uninit --agent codex --project
176
+ ```
177
+
178
+ Rules live in `<project>/.codex/ratchet.md` or `$CODEX_HOME/ratchet.md`.
179
+ The harness also instructs Codex to retrieve both files, since Claude's `@file`
180
+ import syntax is not used. Promotion always requires an explicit scope.
181
+ `rm` and `prune` preserve backups; `uninit` removes only the managed block.
182
+
183
+ The panel detects repeated command or patch failures in the recent rollout tail
184
+ (up to 2 MiB, 30 tool-bearing turns, 30 minutes). Lookup misses and ordinary
185
+ `grep`/`diff` exit-1 results are excluded. A `ratchet? #1 x2` chip identifies a
186
+ candidate in that session. Inspect it before promoting:
187
+
188
+ ```sh
189
+ sprag harness promote 1 --session SESSION_ID --project --agent codex
190
+ ```
191
+
192
+ This appends an editable TODO for the cause and prevention, not a diagnosed fix.
193
+ Prefer promoting a complete condition/action rule after confirming its cause.
194
+ Candidate IDs are session-local and temporary; Claude candidate IDs are not imported.
195
+
196
+ ## Route Discovery And Presets
197
+
198
+ ```sh
199
+ sprag route-scan --refresh --days 14 --agent codex
200
+ sprag delegate rules add R1 --project --agent codex
201
+ sprag route-scan dismiss R1 --agent codex
202
+ sprag seed --agent codex
203
+ sprag seed accept all --project --agent codex
204
+ sprag seed skip all --agent codex
205
+ sprag seed reset --agent codex
206
+ ```
207
+
208
+ Route candidates need at least three completed simple turns with the same
209
+ category, provider, parent model, and project. Aborted, already delegated,
210
+ high-error, complex, and mid-turn steering requests are excluded. Archived
211
+ copies of one session do not count twice. Modern turn usage takes precedence;
212
+ older cumulative `token_count` records are supported without double-counting.
213
+
214
+ Targets need a lower price at the observed token mix and evidence that the model
215
+ is usable: a recorded run, an existing provider-scoped rule, or the configured
216
+ default target. Without matching prices, the candidate remains available but
217
+ approval requires `--model <id>`. Availability still depends on the active provider.
218
+ SessionStart reads cached candidates and schedules an eligible refresh outside
219
+ the hook. Small log changes refresh at most daily; a 5 MiB increase can refresh
220
+ after an hour. `--refresh` bypasses that schedule.
221
+
222
+ `seed` offers only compatible ratchet presets, not Claude model-tier presets.
223
+ Accept and skip decisions are separate from Claude. Both seed acceptance and
224
+ route approval require the user's choice of global or project scope. A project
225
+ route is saved for the project where it was observed. Enable delegation with
226
+ `sprag delegate on --agent codex` and trust the hooks before expecting rules to run.
227
+
228
+ ## Reports
229
+
230
+ ### One-Shot Statusline
231
+
232
+ ```sh
233
+ sprag --statusline --agent codex
234
+ sprag --statusline --agent codex --text --no-color --columns 80
235
+ sprag --format statusline --agent codex --session SESSION_ID
236
+ sprag --statusline --agent codex --single-line
237
+ ```
238
+
239
+ This is terminal output, not a shell command installed into Codex's native
240
+ footer. It reads local logs, does not fetch gateway budgets, and exits after
241
+ one frame. Chips wrap to terminal width (100 columns when piped), with an
242
+ explicit `--columns` override. `--single-line` produces one unwrapped line for
243
+ external terminal integrations. It shares `mode text|narrow|icon`, timer, and color
244
+ preferences with the panel; explicit `--text`, `--narrow`, and `--no-color`
245
+ flags are supported, along with `--timer` and `--no-timer`.
246
+ Claude's TTL buckets and routing options do not apply.
247
+
248
+ ### Cache Timer And Display Order
249
+
250
+ Both panel layouts and the one-shot statusline use a Codex-specific order:
251
+ warnings, usage limits/budget, context, cache timer, model/effort, session
252
+ cache/token totals, Codex lifetime document counts, then harness/preferences and
253
+ version. Session measurements stay together before lifetime totals. Claude's
254
+ statusline order is unchanged.
255
+
256
+ The cache clock starts from a new recorded cache read or write, not the latest
257
+ tool event, prompt, or repeated quota snapshot. Each session has its own clock;
258
+ changing models invalidates the old model's clock until new cache usage arrives.
259
+ `Cache 29:59 (Bedrock 30m)` uses the documented minimum retention for a resolved
260
+ supported model, not a server expiration timestamp or a promise of a cache hit.
261
+ For LiteLLM, Sprag queries the configured `/model/info` endpoint and checks the
262
+ actual deployment/base model. All deployments behind an alias must agree.
263
+ Only sanitized policy and price fields are persisted; policy freshness is five minutes. Secrets
264
+ and raw deployment records are not stored. The live panel refreshes policies;
265
+ one-shot statuslines remain offline. Unresolved, earlier, or unknown models show
266
+ cache age with `TTL unknown`; no observed cache activity shows `Cache timer n/a`.
267
+ The tilde and old unverified-policy labels are removed. After the window passes, the display
268
+ says `window elapsed` and `expiry unknown`, never `EXPIRED`.
269
+
270
+ ```sh
271
+ sprag cache status --agent codex
272
+ sprag cache status --refresh --agent codex
273
+ sprag cache 30m --agent codex
274
+ sprag cache auto --agent codex
275
+ sprag cache off --agent codex
276
+ sprag --statusline --agent codex --no-timer
277
+ ```
278
+
279
+ A configured duration (1 minute through 24 hours) changes only Sprag's display
280
+ estimate, not the request or provider retention. It is stored separately from
281
+ Claude's `mode ttl=5m|1h|auto`. The shared `mode no-timer` also hides the Codex
282
+ clock; `--timer` overrides that preference, but not `cache off`.
283
+
284
+ ### Routing Saved
285
+
286
+ ```sh
287
+ sprag route-scan savings --refresh --agent codex
288
+ sprag route-scan savings --format json --agent codex
289
+ ```
290
+
291
+ The separate Codex ledger joins a route ID recorded by Sprag at spawn time to
292
+ the child's own rollout. An explicit model choice or an unrelated cheaper
293
+ subagent earns no credit. The estimate holds the child's measured tokens
294
+ constant and prices them at the parent and child rates. Negative differences
295
+ remain negative; this is not a comparison of actual billed totals.
296
+
297
+ Rates come from the configured LiteLLM `/model/info` endpoint, with agreement
298
+ required across deployments behind an alias. Price snapshots last seven days.
299
+ Providers are kept separate; direct OpenAI/subscription pricing is not inferred.
300
+ Missing rates or usage produce unpriced entries, never invented zeroes. A priced
301
+ entry keeps its rate snapshot across later outages. Pending route records are
302
+ matched for seven days; saved ledger entries remain afterward. Run `--refresh`
303
+ after delegation to update the ledger without waiting for the next route scan.
304
+
305
+ Claude's ledger is never imported. Document conversion shows distinct document
306
+ counts rather than a Codex dollar saving or billing measurement.
307
+
308
+ ### Live Companion Panel
309
+
310
+ Codex 0.157.1 was observed to defer SessionStart until the first submitted prompt,
311
+ even after hook trust is granted. For immediate opening when typing `codex` in
312
+ macOS zsh, install the optional shell integration:
313
+
314
+ ```sh
315
+ sprag panel shell install --agent codex
316
+ ```
317
+
318
+ Install tmux first (`brew install tmux` on macOS), then open a new terminal.
319
+ This adds a backed-up, marked function to `$ZDOTDIR/.zshrc` (or `~/.zshrc`).
320
+ Interactive Codex runs above a compact Sprag panel in the SAME terminal.
321
+ An isolated tmux server leaves existing tmux sessions and configuration alone.
322
+ Each inline launch has its own session binding. The runner adds `--no-daemon`
323
+ so the trusted SessionStart hook receives that launch's environment instead of
324
+ a shared app-server's environment. The hook supplies the exact session ID and
325
+ transcript path; another active session in the same project cannot take over
326
+ the panel. Before the hook runs, the panel shows **Waiting for session binding**,
327
+ not another session's usage. On versions that defer SessionStart, submit the
328
+ first prompt to bind the panel. Install and trust the Sprag SessionStart hook
329
+ to enable binding; disabling hooks leaves the panel waiting.
330
+ Resume, clear, and compaction hooks refresh the binding. Each compact frame
331
+ identifies its session by the last eight ID characters. Inline remote-server
332
+ invocations are rejected because their hook environment and logs are not local;
333
+ use standalone Codex and an explicitly pinned panel with accessible local logs.
334
+ Codex arguments and exit status are preserved. The inline runner prepends
335
+ `-c 'tui.status_line=[]'` so Codex's model/effort/directory row does not duplicate
336
+ the Sprag panel. This is an invocation-only override: it does not edit
337
+ `config.toml`, change the model or effort, or affect standalone Codex runs.
338
+ Codex's startup banner, shortcut hints, approval prompts, and other notices
339
+ remain visible. To keep the native row, run
340
+ `sprag panel run --agent codex --keep-native-statusline --` followed by Codex
341
+ arguments. An explicit later Codex `-c 'tui.status_line=["current-dir"]'` also
342
+ overrides Sprag's default. Restart the inline session to apply a runner update.
343
+ Exiting Codex closes the panel;
344
+ detaching also ends this temporary session. For explicit launch without shell
345
+ integration, run `sprag panel run --agent codex --` followed by Codex arguments.
346
+ An existing codex alias/function is not overwritten. It does not bypass hook
347
+ trust: it starts the local display independently. The panel-start hook skips
348
+ opening a separate window inside this layout. Remove the shell function with
349
+ `sprag panel shell remove --agent codex` and open a new terminal.
350
+ The shell integration currently targets common plain, prompt, and resume
351
+ invocations; use the explicit panel command for unusual global-option/subcommand
352
+ combinations. Non-TTY invocations and recognized utility commands do not open windows.
353
+
354
+ On macOS, enable automatic opening when Codex starts or resumes:
355
+
356
+ ```sh
357
+ sprag panel auto on --agent codex
358
+ sprag panel --open --agent codex
359
+ ```
360
+
361
+ The first command registers a separate `panel-start` SessionStart hook. Review
362
+ and trust that new hook in Codex `/hooks`, then restart or resume Codex. It opens
363
+ a macOS Terminal window pinned to the hook's session ID; only a panel for that
364
+ same session and Codex home is reused. Concurrent sessions get separate windows.
365
+ Reused windows are unminimized, their panel tab is selected, and Terminal is
366
+ brought forward so a background panel does not look like a failed launch.
367
+ Clear and compaction events do not open windows. macOS may request Terminal
368
+ automation permission. Codex's native footer is unchanged. Disable automatic
369
+ opening with `sprag panel auto off --agent codex`; check registration with
370
+ `sprag panel auto status --agent codex`. Reinstall preserves this preference.
371
+ `--open --session SESSION_ID` opens or reuses a pinned window immediately.
372
+ Without `--session`, `--open` creates a project-following window. Other platforms use a manual
373
+ terminal or tmux split as below. Hook trust is never bypassed by Sprag.
374
+
375
+ Run this in a separate terminal window or a tmux split alongside Codex:
376
+
377
+ ```sh
378
+ sprag panel --agent codex
379
+ sprag panel --agent codex --project /path/to/repo
380
+ sprag panel --agent codex --session SESSION_ID --interval 2
381
+ sprag panel --agent codex --once --no-color
382
+ ```
383
+
384
+ The manually launched panel refreshes every two seconds and follows the latest main session in
385
+ the current project (including subdirectories and filesystem path aliases).
386
+ Subagent rollouts do not replace the main display. `--session` pins an exact
387
+ session ID, including a child when explicitly selected. Pinned and bound sessions
388
+ are looked up even when their logs are older than seven days or have been archived.
389
+ `--days` controls project-following discovery (default seven days). `--project` is a directory
390
+ path in panel mode, not the report's substring filter. Press `r` to refresh and
391
+ `q` or Ctrl-C to exit. The terminal's screen and cursor are restored on exit.
392
+ Piped output requires `--once` so scripts cannot accidentally start an endless loop.
393
+ Use `sprag panel doctor --agent codex --session SESSION_ID` to inspect rendering
394
+ for one session; without an ID, diagnostics show the last project-wide activity.
395
+
396
+ The display shows model/effort, recorded working/idle/interrupted state when
397
+ available, latest recorded input context, cache hit rate,
398
+ session token totals, recorded rate-limit gauges/reset times, harness score,
399
+ ratchet counts, hook registration, and Korean/cohesion/doc2md/brief/delegate
400
+ preferences. Preference labels do not claim
401
+ that hooks are trusted. Logs older than five minutes are marked stale. Reset
402
+ times that have elapsed are marked as awaiting a new usage event, not zero usage.
403
+ Unreported limits, session cost, exact cache expiry, and unpriced routing estimates remain unknown.
404
+ Non-ASCII dynamic labels are replaced with `?` to maintain terminal cell widths.
405
+
406
+ The inline compact panel reuses Claude's icon/narrow/text preferences and
407
+ segmented gauges. Chips wrap as units instead of losing their trailing values;
408
+ the pane grows with the content while keeping at least ten rows for Codex.
409
+ If the terminal is too small, a remaining-row count marks hidden content.
410
+ Cache reuse is shown in tokens, not an inferred dollar saving. Document counts
411
+ and format breakdowns come from Codex's own document records and are labelled
412
+ `Codex total`; Claude-based dollar estimates are not attributed to Codex.
413
+ Unavailable cost and limit placeholders are hidden in compact mode. Cache timer
414
+ availability and unpriced routing accounting remain explicit.
415
+
416
+ The panel is a separate terminal UI, not an injection into Codex's native footer.
417
+ It consumes local rollout logs without invoking a model or modifying Codex config.
418
+ For a configured LiteLLM provider, the existing budget reader also queries that
419
+ provider's budget and model-info endpoints, using its configured environment key or auth helper.
420
+ Both full and compact views show a returned budget, with its source and freshness,
421
+ separately from unavailable per-session cost. Document counts cover Codex history
422
+ in both views; Claude dollar-saving estimates are excluded.
423
+ Unchanged files are cached for the panel's lifetime; changed logs are reparsed.
424
+
425
+ ```sh
426
+ sprag --agent codex --days 7
427
+ sprag --agent codex --hours 6 --format json
428
+ sprag --agent codex --project my-project --format csv
429
+ sprag --agent codex --session SESSION_ID --format json
430
+ ```
431
+
432
+ Sprag reads `.jsonl` files under `$CODEX_HOME/sessions` and
433
+ `$CODEX_HOME/archived_sessions`, not `history.jsonl`. The project filter matches
434
+ the session metadata's working directory. Usage is filtered within each session,
435
+ not just by file modification time. A session without token-count records does
436
+ not appear in the report.
437
+
438
+ Cumulative token-count deltas prevent repeated usage events from being counted
439
+ twice. Cached input is included in total input, and reasoning is included in
440
+ output; neither subset is added twice. Usage updates are **not an exact API call
441
+ count**. JSON retains the existing summary field `apiCalls` for compatibility,
442
+ but its Codex value counts usage updates. Context size comes from recorded
443
+ `last_token_usage`, not Claude's 200k/1M heuristic.
444
+ The top-level JSON field `usageUpdates` names that count explicitly.
445
+
446
+ Rollout JSONL is not a stable public Codex interface. Missing or malformed records
447
+ are skipped, and incomplete/copied/reset logs can only give best-effort totals.
448
+ Reports show **cost and exact cache TTL as unavailable** (`null` in JSON). They do not
449
+ apply Anthropic prices, infer subscription charges, or claim Claude's cache TTL.
450
+ Document counts are not a measurement of Codex billing or avoided tokens.
451
+
452
+ ## Limits And Removal
453
+
454
+ Claude's cap/TTL heuristics, Haiku/Sonnet tier presets, and the Claude
455
+ `compact-window` command do not apply to Codex. Codex provides its own
456
+ `model_auto_compact_token_limit` setting; Sprag does not apply Claude's 1M
457
+ heuristic or change that setting. Codex's native `tui.status_line` accepts
458
+ item identifiers, not a Sprag shell command. Unsupported integration commands
459
+ fail explicitly rather than modifying Claude settings.
460
+
461
+ Use `sprag panel --agent codex` for the live display or
462
+ `sprag --statusline --agent codex` for one-shot terminal output.
463
+ Rate-limit gauges use Codex's recorded values only. Run
464
+ `sprag capabilities --agent codex` for the complete support matrix.
465
+
466
+ ```sh
467
+ sprag uninstall --agent codex
468
+ ```
469
+
470
+ Removal takes out Sprag's Codex hooks and global harness block. It keeps project
471
+ harnesses, ratchet rules, backups, other hooks, and shared Sprag state.
472
+ `--purge` is intentionally unsupported for Codex. Remove a project block with
473
+ `sprag harness uninit --agent codex --project` in that project.
474
+
475
+ Normal npm upgrades still run the package's Claude postinstall. Codex-only users
476
+ should keep using `npm i -g sprag-cli --ignore-scripts` followed by
477
+ `sprag install --agent codex` when upgrading.
478
+
479
+ ## Official References
480
+
481
+ - [Codex hooks, event schemas, and trust](https://developers.openai.com/codex/hooks/)
482
+ - [AGENTS.md discovery and overrides](https://developers.openai.com/codex/guides/agents-md/)
483
+ - [Codex configuration reference](https://developers.openai.com/codex/config-reference/)
484
+ - [Prompt cache lifetime](https://developers.openai.com/api/docs/guides/prompt-caching#cache-lifetime)
485
+ - [Codex subagent configuration](https://developers.openai.com/codex/subagents/)
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "sprag-cli",
3
- "version": "3.52.0",
4
- "description": "Sprag - a quality ratchet harness for AI coding agents: model-fitting delegation, ratchet rules, doc2md, Korean style gate. Same tool as claude-token-saver, under its new name.",
3
+ "version": "3.54.0",
4
+ "description": "Sprag - a local harness for Claude Code and Codex: ratchet rules, model delegation, token telemetry, companion panels, document conversion, and Korean writing checks.",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "sprag": "bin/cli.js",
@@ -23,6 +23,8 @@
23
23
  "examples/",
24
24
  "README.md",
25
25
  "README.ko.md",
26
+ "docs/CODEX.md",
27
+ "docs/CODEX.ko.md",
26
28
  "LICENSE",
27
29
  "NOTICE"
28
30
  ],
@@ -36,6 +38,9 @@
36
38
  "ai-agent",
37
39
  "claude",
38
40
  "claude-code",
41
+ "codex",
42
+ "openai-codex",
43
+ "agents-md",
39
44
  "anthropic",
40
45
  "model-routing",
41
46
  "llm-router",
@@ -63,5 +68,8 @@
63
68
  "bugs": {
64
69
  "url": "https://github.com/rootstudioyaml/sprag/issues"
65
70
  },
66
- "author": "DeepPulse (https://www.youtube.com/@DeepPulseKR)"
71
+ "author": "DeepPulse (https://www.youtube.com/@DeepPulseKR)",
72
+ "dependencies": {
73
+ "smol-toml": "^1.9.0"
74
+ }
67
75
  }
@@ -0,0 +1,29 @@
1
+ # Delegation bounds
2
+
3
+ You were spawned by a delegation call from another session. These four rules
4
+ keep your exploration finite and your output usable by the caller that spawned
5
+ you, regardless of what the rest of your prompt asks for.
6
+
7
+ 1. **Stop at the cap, report what you finished.** If you are about to exceed
8
+ the tool-call cap for this delegation, or a call fails and retrying would
9
+ put you over it, stop where you are and report only the work you actually
10
+ completed. The caller runs at a higher tier and can pick up from a partial
11
+ result; pushing past the cap spends tokens nobody asked you to spend.
12
+
13
+ 2. **Decide the output shape first, then only fill it.** Settle the form your
14
+ answer should take before you start writing, and answer exactly what was
15
+ asked in that form. Do not grow extra steps, alternatives, or caveats the
16
+ caller never requested — the caller is paying for a finished answer to its
17
+ question, not for the task to be widened on your own initiative.
18
+
19
+ 3. **State what you could not verify; never guess in its place.** When a fact
20
+ is out of reach — a file you could not read, a command you could not run —
21
+ say so plainly instead of filling the gap with a plausible-sounding guess.
22
+ A guess presented as a fact is worse than a stated unknown, because once it
23
+ merges into the caller's context there is no way left to tell them apart.
24
+
25
+ 4. **Back every claim with a path, a line number, or a command you ran.** Cite
26
+ the file and line, or paste the command output, rather than paraphrasing
27
+ from memory. A citation lets the caller check your claim in one step; an
28
+ unattributed claim forces the caller to redo the very search you were
29
+ delegated to save it from doing.
@@ -32,7 +32,10 @@
32
32
  },
33
33
  {
34
34
  "ko": "탐색·조회·상태 확인·명령 실행 같은 단순 요청은 `model: haiku` 서브에이전트로 위임해 상위 모델 토큰을 아낀다 (읽기 전용 preset 에이전트가 이미 있으면 그걸 쓰고, 없으면 티어만 내려서 위임)",
35
- "en": "Delegate simple requests — lookup, status checks, running commands — to a `model: haiku` subagent to keep the expensive model's tokens for real work (use an existing read-only preset agent when there is one; otherwise delegate with the tier lowered)"
35
+ "en": "Delegate simple requests — lookup, status checks, running commands — to a `model: haiku` subagent to keep the expensive model's tokens for real work (use an existing read-only preset agent when there is one; otherwise delegate with the tier lowered)",
36
+ "agents": [
37
+ "claude"
38
+ ]
36
39
  },
37
40
  {
38
41
  "ko": "지침이나 룰이 지켜지지 않아 같은 품질 문제가 재발하면 문구를 다시 쓰지 말 것 → 그 조항이 기계로 판정 가능한지부터 따지고(문자열 비교나 정규식으로 결정되면 판정 가능), 가능하면 쓰기 시점 훅이나 테스트 같은 차단 지점을 추가한다. 판단이 필요한 잔여 부분만 지침에 남기고 그 경계를 지침에 적는다. 실제 코퍼스에 돌려 오탐 건수를 센 뒤 좁히고, 회귀 테스트를 같은 커밋에 넣는다",
package/src/agent.js ADDED
@@ -0,0 +1,33 @@
1
+ import { homedir } from 'node:os';
2
+ import { join, resolve } from 'node:path';
3
+ import { readFileSync } from 'node:fs';
4
+ import { parse } from 'smol-toml';
5
+
6
+ export function codexUserDir() {
7
+ return process.env.CODEX_HOME ? resolve(process.env.CODEX_HOME) : join(homedir(), '.codex');
8
+ }
9
+
10
+ export function codexProviderName({ home = codexUserDir() } = {}) {
11
+ try {
12
+ const cfg = parse(readFileSync(join(home, 'config.toml'), 'utf8'));
13
+ return cfg.profiles?.[cfg.profile]?.model_provider || cfg.model_provider || 'openai';
14
+ } catch (e) { return e.code === 'ENOENT' ? 'openai' : null; }
15
+ }
16
+
17
+ /** Remove the global selector before positional subcommand parsers see it. */
18
+ export function selectAgent(argv) {
19
+ const args = [];
20
+ let agent = 'claude';
21
+ for (let i = 0; i < argv.length; i++) {
22
+ const arg = argv[i];
23
+ if (arg === '--') { args.push(...argv.slice(i)); break; }
24
+ if (arg === '--agent' || arg.startsWith('--agent=')) {
25
+ const value = arg === '--agent' ? argv[++i] : arg.slice('--agent='.length);
26
+ if (!['claude', 'codex'].includes(value)) {
27
+ throw new Error('--agent expects claude or codex');
28
+ }
29
+ agent = value;
30
+ } else args.push(arg);
31
+ }
32
+ return { agent, args };
33
+ }