sprag-cli 3.66.0 → 3.68.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.ko.md +29 -62
- package/README.md +26 -61
- package/bin/cli.js +130 -62
- package/docs/CODEX.ko.md +32 -0
- package/docs/CODEX.md +34 -0
- package/package.json +11 -4
- package/presets/ratchet-rules.json +7 -0
- package/src/agents.js +33 -11
- package/src/{codex-brief.js → codex/brief.js} +7 -7
- package/src/{codex-budget.js → codex/budget.js} +2 -2
- package/src/{codex-cache-policy.js → codex/cache-policy.js} +9 -9
- package/src/{codex-delegation-diagnostics.js → codex/delegation-diagnostics.js} +2 -2
- package/src/{codex-delegation.js → codex/delegation.js} +11 -11
- package/src/{codex-doc2md-ledger.js → codex/doc2md-ledger.js} +5 -5
- package/src/{codex-doc2md.js → codex/doc2md.js} +4 -4
- package/src/{codex-doctor.js → codex/doctor.js} +4 -4
- package/src/{codex-harness.js → codex/harness.js} +5 -5
- package/src/{codex-hooks.js → codex/hooks.js} +20 -20
- package/src/{codex-installer.js → codex/installer.js} +2 -2
- package/src/{codex-ledger.js → codex/ledger.js} +6 -6
- package/src/{codex-panel-launcher.js → codex/panel-launcher.js} +2 -2
- package/src/{codex-panel-runner.js → codex/panel-runner.js} +2 -2
- package/src/{codex-panel-session.js → codex/panel-session.js} +1 -1
- package/src/{codex-panel-setup.js → codex/panel-setup.js} +1 -1
- package/src/{codex-panel-state.js → codex/panel-state.js} +3 -3
- package/src/{codex-panel.js → codex/panel.js} +43 -34
- package/src/{codex-parser.js → codex/parser.js} +5 -9
- package/src/{codex-reply-language.js → codex/reply-language.js} +2 -2
- package/src/{codex-route-scan.js → codex/route-scan.js} +10 -10
- package/src/{codex-rule-health.js → codex/rule-health.js} +4 -4
- package/src/commands/codex-delegate.js +6 -6
- package/src/commands/codex.js +44 -44
- package/src/commands/cowork.js +107 -0
- package/src/commands/delegate.js +43 -0
- package/src/commands/install.js +21 -7
- package/src/commands/korean.js +1 -1
- package/src/commands/litellm-budget.js +56 -0
- package/src/commands/route-scan.js +6 -0
- package/src/commands/saved.js +60 -5
- package/src/commands/uninstall.js +7 -3
- package/src/config.js +4 -0
- package/src/cowork-sync.js +361 -0
- package/src/delegation-agents.js +133 -0
- package/src/formatters/statusline.js +713 -504
- package/src/glyphs.js +2 -0
- package/src/harness.js +1 -1
- package/src/implementation-delegation.js +5 -2
- package/src/installer.js +77 -1
- package/src/model-alias.js +45 -60
- package/src/model-rules.js +12 -5
- package/src/model-share.js +136 -0
- package/src/parser.js +4 -13
- package/src/route-inject.js +8 -3
- package/src/session-records.js +2 -8
- package/src/statusline-base.js +9 -3
- package/src/stdin-payload.js +1 -1
- package/src/transcript-lines.js +40 -0
- /package/src/{codex-cache.js → codex/cache.js} +0 -0
- /package/src/{codex-panel-shell.js → codex/panel-shell.js} +0 -0
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from 'node:fs';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
import { parse } from 'smol-toml';
|
|
4
|
-
import { codexUserDir } from '
|
|
5
|
-
import { codexHookStatus } from './
|
|
6
|
-
import { createPanelReader } from './
|
|
7
|
-
import { panelHealth } from './
|
|
4
|
+
import { codexUserDir } from '../agent.js';
|
|
5
|
+
import { codexHookStatus } from './installer.js';
|
|
6
|
+
import { createPanelReader } from './panel.js';
|
|
7
|
+
import { panelHealth } from './panel-state.js';
|
|
8
8
|
|
|
9
9
|
export const CODEX_CAPABILITIES = [
|
|
10
10
|
{ feature: 'statusline', support: 'supported', command: 'sprag --statusline --agent codex', note: 'Local terminal output; native footer accepts only Codex item identifiers.' },
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs';
|
|
2
2
|
import { dirname, join, resolve } from 'node:path';
|
|
3
3
|
import { homedir } from 'node:os';
|
|
4
|
-
import { codexUserDir } from '
|
|
5
|
-
import { loadConfig } from '
|
|
6
|
-
import { koreanStyleEnabled, koreanStyleInjection } from '
|
|
7
|
-
import { codexToolEvents, codexLegacyToolEvent, readCodexTailLines, forkedCopyFilter } from './
|
|
8
|
-
import { implementationDelegationRule } from '
|
|
4
|
+
import { codexUserDir } from '../agent.js';
|
|
5
|
+
import { loadConfig } from '../config.js';
|
|
6
|
+
import { koreanStyleEnabled, koreanStyleInjection } from '../korean-style.js';
|
|
7
|
+
import { codexToolEvents, codexLegacyToolEvent, readCodexTailLines, forkedCopyFilter } from './parser.js';
|
|
8
|
+
import { implementationDelegationRule } from '../implementation-delegation.js';
|
|
9
9
|
|
|
10
10
|
export const CODEX_BEGIN = '<!-- sprag:codex:harness:begin -->';
|
|
11
11
|
export const CODEX_END = '<!-- sprag:codex:harness:end -->';
|
|
@@ -3,25 +3,25 @@ import { spawn } from 'node:child_process';
|
|
|
3
3
|
import { createHash } from 'node:crypto';
|
|
4
4
|
import { mkdirSync, openSync, readFileSync, readSync, closeSync, statSync } from 'node:fs';
|
|
5
5
|
import { join, resolve } from 'node:path';
|
|
6
|
-
import { loadConfig, userLanguage } from '
|
|
7
|
-
import { koreanStyleInjection, koreanStyleEnabled } from '
|
|
8
|
-
import { cohesionInjection } from '
|
|
9
|
-
import { codexHarnessPaths, syncCodexKoreanBlock } from './
|
|
10
|
-
import { writeViaTmp } from '
|
|
11
|
-
import { findProjectRoot } from '
|
|
12
|
-
import { runCodexBrief } from './
|
|
13
|
-
import { codexDocumentTool, codexDocumentNote, convertCodexDocument, codexDocumentContext } from './
|
|
14
|
-
import { codexDelegationTool, codexRouteHint, codexDelegateEnabled } from './
|
|
15
|
-
import { bindCodexSubagent, closeStaleCodexRoutes, unsettledCodexRoutes } from './
|
|
16
|
-
import { seedOfferBlock } from '
|
|
17
|
-
import { readCodexRouteScan, openCodexCandidates, shouldRescanCodex } from './
|
|
18
|
-
import { userDataDir } from '
|
|
19
|
-
import { debug } from '
|
|
20
|
-
import { updateOffer } from '
|
|
6
|
+
import { loadConfig, userLanguage } from '../config.js';
|
|
7
|
+
import { koreanStyleInjection, koreanStyleEnabled } from '../korean-style.js';
|
|
8
|
+
import { cohesionInjection } from '../cohesion.js';
|
|
9
|
+
import { codexHarnessPaths, syncCodexKoreanBlock } from './harness.js';
|
|
10
|
+
import { writeViaTmp } from '../state-file.js';
|
|
11
|
+
import { findProjectRoot } from '../harness.js';
|
|
12
|
+
import { runCodexBrief } from './brief.js';
|
|
13
|
+
import { codexDocumentTool, codexDocumentNote, convertCodexDocument, codexDocumentContext } from './doc2md.js';
|
|
14
|
+
import { codexDelegationTool, codexRouteHint, codexDelegateEnabled } from './delegation.js';
|
|
15
|
+
import { bindCodexSubagent, closeStaleCodexRoutes, unsettledCodexRoutes } from './ledger.js';
|
|
16
|
+
import { seedOfferBlock } from '../seed-rules.js';
|
|
17
|
+
import { readCodexRouteScan, openCodexCandidates, shouldRescanCodex } from './route-scan.js';
|
|
18
|
+
import { userDataDir } from '../paths.js';
|
|
19
|
+
import { debug } from '../debug.js';
|
|
20
|
+
import { updateOffer } from '../update-notice.js';
|
|
21
21
|
|
|
22
22
|
const require = createRequire(import.meta.url);
|
|
23
|
-
const lint = require('
|
|
24
|
-
const doc2md = require('
|
|
23
|
+
const lint = require('../korean-lint.cjs');
|
|
24
|
+
const doc2md = require('../doc2md.cjs');
|
|
25
25
|
|
|
26
26
|
function context(event, text) {
|
|
27
27
|
return text ? { hookSpecificOutput: { hookEventName: event, additionalContext: text } } : null;
|
|
@@ -167,7 +167,7 @@ export async function codexHookOutput(event, payload, { cfg = loadConfig(), refr
|
|
|
167
167
|
if (!payload || typeof payload !== 'object') return null;
|
|
168
168
|
if (event === 'stop') {
|
|
169
169
|
if (payload.stop_hook_active || !koreanStyleEnabled(cfg) || cfg?.koreanReplyCheck === false) return null;
|
|
170
|
-
const { checkCodexReplyLanguage } = await import('./
|
|
170
|
+
const { checkCodexReplyLanguage } = await import('./reply-language.js');
|
|
171
171
|
if (!checkCodexReplyLanguage(payload)) return null;
|
|
172
172
|
return { decision: 'block', reason: '사용자는 한국어로 질문했는데 마지막 답변이 영어로 작성됐습니다. 같은 내용을 한국어로 다시 답하십시오. 코드, 명령어, 오류 원문, 커밋 메시지는 그대로 유지하십시오.' };
|
|
173
173
|
}
|
|
@@ -180,9 +180,9 @@ export async function codexHookOutput(event, payload, { cfg = loadConfig(), refr
|
|
|
180
180
|
const parts = [];
|
|
181
181
|
// Keep the preset file current with this version's wording before it is read.
|
|
182
182
|
if (event === 'session-start') {
|
|
183
|
-
try { (await import('
|
|
183
|
+
try { (await import('../preset-ratchet.js')).preparePresetRatchet({ agent: 'codex' }); } catch (e) { debug('codex:preset', e); }
|
|
184
184
|
}
|
|
185
|
-
const { presetRatchetPath } = await import('
|
|
185
|
+
const { presetRatchetPath } = await import('../preset-ratchet.js');
|
|
186
186
|
// Lower priority survives the budget first. Rules and style move to a file
|
|
187
187
|
// when they do not fit; offers and notices are simply left out.
|
|
188
188
|
for (const [scope, ratchet] of [['global', codexHarnessPaths(root, 'global').ratchet],
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs';
|
|
2
2
|
import { join, dirname } from 'node:path';
|
|
3
|
-
import { codexUserDir } from '
|
|
4
|
-
import { writeViaTmp } from '
|
|
3
|
+
import { codexUserDir } from '../agent.js';
|
|
4
|
+
import { writeViaTmp } from '../state-file.js';
|
|
5
5
|
|
|
6
6
|
export const CODEX_HANDLERS = {
|
|
7
7
|
SessionStart: { matcher: 'startup|resume|clear|compact', command: 'sprag codex-hook --agent codex --event session-start', timeout: 15 },
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { appendFileSync, mkdirSync, readFileSync } from 'node:fs';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
import { randomBytes, randomUUID } from 'node:crypto';
|
|
4
|
-
import { userDataDir } from '
|
|
5
|
-
import { writeViaTmp } from '
|
|
6
|
-
import { roundUsd } from '
|
|
7
|
-
import { readResetMarks, isBeforeReset } from '
|
|
8
|
-
import { discoverCodexSessionFiles, parseCodexTurns } from './
|
|
9
|
-
import { codexRunCost, codexPriceBook, readCodexPrices } from './
|
|
4
|
+
import { userDataDir } from '../paths.js';
|
|
5
|
+
import { writeViaTmp } from '../state-file.js';
|
|
6
|
+
import { roundUsd } from '../savings-ledger.js';
|
|
7
|
+
import { readResetMarks, isBeforeReset } from '../saved-reset.cjs';
|
|
8
|
+
import { discoverCodexSessionFiles, parseCodexTurns } from './parser.js';
|
|
9
|
+
import { codexRunCost, codexPriceBook, readCodexPrices } from './cache-policy.js';
|
|
10
10
|
|
|
11
11
|
/*
|
|
12
12
|
* Codex routing savings, kept apart from Claude's delegation ledger.
|
|
@@ -2,11 +2,11 @@ import { execFileSync } from 'node:child_process';
|
|
|
2
2
|
import { createHash } from 'node:crypto';
|
|
3
3
|
import { fileURLToPath } from 'node:url';
|
|
4
4
|
import { resolve } from 'node:path';
|
|
5
|
-
import { codexUserDir } from '
|
|
5
|
+
import { codexUserDir } from '../agent.js';
|
|
6
6
|
|
|
7
7
|
const quote = (value) => `'${String(value).replace(/'/g, `'"'"'`)}'`;
|
|
8
8
|
|
|
9
|
-
export function panelLaunchCommand(root, { node = process.execPath, cli = fileURLToPath(new URL('
|
|
9
|
+
export function panelLaunchCommand(root, { node = process.execPath, cli = fileURLToPath(new URL('../../bin/cli.js', import.meta.url)), home = codexUserDir(), sessionId, sessionFile } = {}) {
|
|
10
10
|
return ['env', `CODEX_HOME=${home}`, node, cli, 'panel', '--agent', 'codex', '--project', resolve(root),
|
|
11
11
|
...(sessionId ? ['--session', sessionId] : []), ...(sessionFile ? ['--session-file', sessionFile] : [])]
|
|
12
12
|
.map(quote).join(' ');
|
|
@@ -3,8 +3,8 @@ import { mkdtempSync, readFileSync, rmSync } from 'node:fs';
|
|
|
3
3
|
import { tmpdir, homedir } from 'node:os';
|
|
4
4
|
import { join, resolve } from 'node:path';
|
|
5
5
|
import { randomUUID } from 'node:crypto';
|
|
6
|
-
import { panelLaunchCommand } from './
|
|
7
|
-
import { createPanelBinding } from './
|
|
6
|
+
import { panelLaunchCommand } from './panel-launcher.js';
|
|
7
|
+
import { createPanelBinding } from './panel-session.js';
|
|
8
8
|
|
|
9
9
|
const quote = (s) => `'${String(s).replace(/'/g, `'"'"'`)}'`;
|
|
10
10
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { readFileSync, writeFileSync } from 'node:fs';
|
|
2
2
|
import { isAbsolute } from 'node:path';
|
|
3
|
-
import { writeViaTmp } from '
|
|
3
|
+
import { writeViaTmp } from '../state-file.js';
|
|
4
4
|
|
|
5
5
|
export function createPanelBinding(file) {
|
|
6
6
|
writeFileSync(file, JSON.stringify({ version: 1, sessionId: null, transcriptPath: null }), { mode: 0o600 });
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
import { execFileSync } from 'node:child_process';
|
|
12
12
|
import { existsSync } from 'node:fs';
|
|
13
13
|
import { basename, dirname, join } from 'node:path';
|
|
14
|
-
import { installPanelShell } from './
|
|
14
|
+
import { installPanelShell } from './panel-shell.js';
|
|
15
15
|
|
|
16
16
|
/** Whether a tmux binary answers `-V`. */
|
|
17
17
|
export function tmuxAvailable({ exec = execFileSync } = {}) {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto';
|
|
2
2
|
import { readFileSync, mkdirSync, readdirSync, lstatSync, unlinkSync } from 'node:fs';
|
|
3
3
|
import { join, resolve } from 'node:path';
|
|
4
|
-
import { userDataDir } from '
|
|
5
|
-
import { codexUserDir } from '
|
|
6
|
-
import { writeViaTmp, runHousekeeping, sweepStaleTmp } from '
|
|
4
|
+
import { userDataDir } from '../paths.js';
|
|
5
|
+
import { codexUserDir } from '../agent.js';
|
|
6
|
+
import { writeViaTmp, runHousekeeping, sweepStaleTmp } from '../state-file.js';
|
|
7
7
|
|
|
8
8
|
// Every panel session leaves a `-frame` and a `-hook` file behind, keyed by a
|
|
9
9
|
// hash that is not reused once the session is gone. They are diagnostics for a
|
|
@@ -1,33 +1,39 @@
|
|
|
1
1
|
import { resolve, relative, isAbsolute, basename } from 'node:path';
|
|
2
2
|
import { realpathSync, statSync } from 'node:fs';
|
|
3
3
|
import { stat } from 'node:fs/promises';
|
|
4
|
-
import { readPanelBinding } from './
|
|
5
|
-
import { discoverCodexSessionFiles, parseCodexSessionFile } from './
|
|
6
|
-
import { codexHarnessStatus, codexRatchetCandidates } from './
|
|
7
|
-
import { harnessListRules, findProjectRoot } from '
|
|
8
|
-
import { loadConfig, statuslineDefaults } from '
|
|
9
|
-
import { glyphsFor } from '
|
|
10
|
-
import { gaugeBar, formatMoney } from '
|
|
11
|
-
import { codexCacheTimer } from './
|
|
12
|
-
import { readCodexCachePolicy, readCodexPrices, isDirectOpenAI } from './
|
|
13
|
-
import { codexIssues } from './
|
|
14
|
-
import { labelForKey } from '
|
|
15
|
-
import { resolveLabelMode } from '
|
|
16
|
-
import { formatResetIn } from '
|
|
17
|
-
import { codexDocumentTotals } from './
|
|
18
|
-
import { codexHookStatus } from './
|
|
19
|
-
import { koreanStyleEnabled } from '
|
|
20
|
-
import { codexRoutingSavedTotals, loadCodexLedger, codexDelegationStatus } from './
|
|
21
|
-
import { loadCodexModelRules, codexDelegateEnabled } from './
|
|
22
|
-
import { codexRulesInReview, codexSharedRuleHealth } from './
|
|
23
|
-
import { readCodexRouteScan, openCodexCandidates } from './
|
|
24
|
-
import { codexBudgetProvider } from './
|
|
25
|
-
import { readUpdateChip } from '
|
|
4
|
+
import { readPanelBinding } from './panel-session.js';
|
|
5
|
+
import { discoverCodexSessionFiles, parseCodexSessionFile } from './parser.js';
|
|
6
|
+
import { codexHarnessStatus, codexRatchetCandidates } from './harness.js';
|
|
7
|
+
import { harnessListRules, findProjectRoot } from '../harness.js';
|
|
8
|
+
import { loadConfig, statuslineDefaults } from '../config.js';
|
|
9
|
+
import { glyphsFor } from '../glyphs.js';
|
|
10
|
+
import { gaugeBar, formatMoney, DEFAULT_SEGMENT_ORDER } from '../formatters/statusline.js';
|
|
11
|
+
import { codexCacheTimer } from './cache.js';
|
|
12
|
+
import { readCodexCachePolicy, readCodexPrices, isDirectOpenAI } from './cache-policy.js';
|
|
13
|
+
import { codexIssues } from './brief.js';
|
|
14
|
+
import { labelForKey } from '../window-labels.js';
|
|
15
|
+
import { resolveLabelMode } from '../statusline-mode.js';
|
|
16
|
+
import { formatResetIn } from '../format-time.js';
|
|
17
|
+
import { codexDocumentTotals } from './doc2md-ledger.js';
|
|
18
|
+
import { codexHookStatus } from './installer.js';
|
|
19
|
+
import { koreanStyleEnabled } from '../korean-style.js';
|
|
20
|
+
import { codexRoutingSavedTotals, loadCodexLedger, codexDelegationStatus } from './ledger.js';
|
|
21
|
+
import { loadCodexModelRules, codexDelegateEnabled } from './delegation.js';
|
|
22
|
+
import { codexRulesInReview, codexSharedRuleHealth } from './rule-health.js';
|
|
23
|
+
import { readCodexRouteScan, openCodexCandidates } from './route-scan.js';
|
|
24
|
+
import { codexBudgetProvider } from './budget.js';
|
|
25
|
+
import { readUpdateChip } from '../update-notice.js';
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
'cap-warn', '
|
|
29
|
-
'
|
|
30
|
-
];
|
|
27
|
+
const CODEX_SUPPORTED_SEGMENTS = new Set([
|
|
28
|
+
'cap-warn', 'spike', 'usage', 'ctx', 'ttl', 'model', 'effort',
|
|
29
|
+
'delegated', 'doc2md', 'hit', 'saved', 'harness', 'korean', 'version',
|
|
30
|
+
]);
|
|
31
|
+
// The Claude statusline order, restricted to what Codex draws. Codex keeps its
|
|
32
|
+
// own upgrade chip right after the cap warning; the Claude line has no such
|
|
33
|
+
// segment and moves `version` forward instead.
|
|
34
|
+
export const CODEX_SEGMENT_ORDER = DEFAULT_SEGMENT_ORDER
|
|
35
|
+
.filter((name) => CODEX_SUPPORTED_SEGMENTS.has(name))
|
|
36
|
+
.flatMap((name) => (name === 'cap-warn' ? [name, 'update'] : [name]));
|
|
31
37
|
|
|
32
38
|
// Only cache full-session parses. A moving time cutoff would make an unchanged
|
|
33
39
|
// file's cached totals stale. The panel reports session totals, not window totals.
|
|
@@ -233,12 +239,13 @@ function panelGroups(data, { now, version = '', full = false, mode = full ? 'tex
|
|
|
233
239
|
const level = (n) => n >= 90 ? 31 : n >= 70 ? 33 : 32;
|
|
234
240
|
const bar = (n) => full ? gauge(n) : gaugeBar(n, mode === 'text' ? 'narrow' : mode);
|
|
235
241
|
const warnings = [];
|
|
242
|
+
const contextWarnings = [];
|
|
236
243
|
if (data.error) warnings.push(chip(`ERROR: ${terminalText(data.error)}`, 31));
|
|
237
244
|
for (const issue of codexIssues(s, { now })) {
|
|
238
245
|
const key = issue.key.slice(6), win = s.rateLimits?.[key];
|
|
239
246
|
const text = issue.key === 'context' ? `WARNING: Ctx ${pct(context)}`
|
|
240
247
|
: `WARNING: ${windowLabel(win.window_minutes, key)} limit ${pct(win.used_percent)}`;
|
|
241
|
-
warnings.push(chip(text, issue.level === 'critical' ? 31 : 33));
|
|
248
|
+
(issue.key === 'context' ? contextWarnings : warnings).push(chip(text, issue.level === 'critical' ? 31 : 33));
|
|
242
249
|
}
|
|
243
250
|
register('ctx', 'priority', context !== null ? [chip(full
|
|
244
251
|
? `Ctx ${bar(context)} ${pct(context)} ${short(s.lastContextTokens)} / ${short(s.contextWindow)}`
|
|
@@ -274,6 +281,7 @@ function panelGroups(data, { now, version = '', full = false, mode = full ? 'tex
|
|
|
274
281
|
}
|
|
275
282
|
if (limits.length && s?.rateLimitsAt) limits.push(chip(`snapshot ${age(s.rateLimitsAt, now)}`));
|
|
276
283
|
register('cap-warn', 'priority', warnings);
|
|
284
|
+
register('spike', 'priority', contextWarnings);
|
|
277
285
|
register('usage', 'priority', limits);
|
|
278
286
|
register('hit', 'totals', input > 0 ? [chip(`${icon('hit')}Cache hit ${pct(s.totals.cacheRead / input * 100)} (session)`,
|
|
279
287
|
s.totals.cacheRead / input >= 0.85 ? 32 : s.totals.cacheRead / input >= 0.7 ? 33 : 31)] : []);
|
|
@@ -283,8 +291,14 @@ function panelGroups(data, { now, version = '', full = false, mode = full ? 'tex
|
|
|
283
291
|
chip(`Reasoning ${short(s.reasoningOutputTokens)} (in output)`),
|
|
284
292
|
] : []);
|
|
285
293
|
const docs = data.documents;
|
|
294
|
+
// Lifetime figures, in the Claude statusline's order: routing, then documents.
|
|
295
|
+
const delegation = data.delegation;
|
|
296
|
+
register('delegated', 'lifetime', [routingChip(data.routingSaved, chip),
|
|
297
|
+
delegation?.offers ? chip(`Delegation ${delegation.settled} completed / ${delegation.bound} bound / ${delegation.pending} pending / ${delegation.unclaimed} unclaimed (session)`,
|
|
298
|
+
delegation.unclaimed && !delegation.settled && !delegation.bound ? 33 : 90) : null,
|
|
299
|
+
]);
|
|
286
300
|
const docSavings = docs?.scope === 'codex-total' ? docs.savings : null;
|
|
287
|
-
register('doc2md', '
|
|
301
|
+
register('doc2md', 'lifetime', [
|
|
288
302
|
chip(`Doc2md saved ${docSavings?.priced ? `${signedMoney(docSavings.total)} est. (XML vs MD)`
|
|
289
303
|
: `n/a (${docs?.docs ? `${docs.docs} unpriced docs` : 'no documents'})`}`, docSavings?.priced ? (docSavings.total < 0 ? 33 : 32) : 90),
|
|
290
304
|
...(docSavings?.priced && docSavings.unpriced ? [chip(`${docSavings.unpriced} docs unpriced`)] : []),
|
|
@@ -292,11 +306,6 @@ function panelGroups(data, { now, version = '', full = false, mode = full ? 'tex
|
|
|
292
306
|
...(docs.byExt || []).map((row) => chip(`${terminalText(row.ext)} ${number(row.docs)}x`)),
|
|
293
307
|
] : []),
|
|
294
308
|
]);
|
|
295
|
-
const delegation = data.delegation;
|
|
296
|
-
register('routing', 'savings', [routingChip(data.routingSaved, chip),
|
|
297
|
-
delegation?.offers ? chip(`Delegation ${delegation.settled} completed / ${delegation.bound} bound / ${delegation.pending} pending / ${delegation.unclaimed} unclaimed (session)`,
|
|
298
|
-
delegation.unclaimed && !delegation.settled && !delegation.bound ? 33 : 90) : null,
|
|
299
|
-
]);
|
|
300
309
|
register('harness', 'preferences', [
|
|
301
310
|
chip(`${icon('harness')}Harness ${h?.error ? 'ERROR' : h ? `${h.configured}/${h.total}` : 'n/a'}`, h?.configured === 5 ? 32 : 33),
|
|
302
311
|
chip(`Ratchet ${data.rules?.global ?? '?'}/${data.rules?.project ?? '?'}`),
|
|
@@ -322,7 +331,7 @@ function panelGroups(data, { now, version = '', full = false, mode = full ? 'tex
|
|
|
322
331
|
chip(`SPRAG${version ? ` v${terminalText(version)}` : ''} / ${s ? age(s.lastActivity, now) : 'waiting'}`,
|
|
323
332
|
s && (!s.lastActivity || now - new Date(s.lastActivity) > 300000) ? 33 : 90),
|
|
324
333
|
]);
|
|
325
|
-
//
|
|
334
|
+
// Adjacent entries with the same timeframe share a wrapping group.
|
|
326
335
|
const groups = [];
|
|
327
336
|
let lastGroup;
|
|
328
337
|
for (const name of CODEX_SEGMENT_ORDER) {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { openSync, readSync, closeSync, fstatSync } from 'node:fs';
|
|
2
2
|
import { readdir, stat } from 'node:fs/promises';
|
|
3
|
-
import {
|
|
3
|
+
import { transcriptLines } from '../transcript-lines.js';
|
|
4
4
|
import { join, resolve } from 'node:path';
|
|
5
|
-
import { codexUserDir } from '
|
|
5
|
+
import { codexUserDir } from '../agent.js';
|
|
6
6
|
|
|
7
7
|
const count = (n) => Number.isSafeInteger(n) && n >= 0 ? n : 0;
|
|
8
8
|
const emptyTotals = () => ({ input: 0, cacheRead: 0, cacheCreation: 0, ephemeral5m: 0, ephemeral1h: 0, output: 0 });
|
|
@@ -212,9 +212,7 @@ export async function parseCodexTurns(filePath, { cutoffMs = -Infinity } = {}) {
|
|
|
212
212
|
let previousUsage = null;
|
|
213
213
|
let current = null;
|
|
214
214
|
let context = { model: null, effort: null, cwd: null };
|
|
215
|
-
const
|
|
216
|
-
const lines = createInterface({ input: stream, crlfDelay: Infinity });
|
|
217
|
-
for await (const line of lines) {
|
|
215
|
+
for await (const line of transcriptLines(filePath)) {
|
|
218
216
|
let e;
|
|
219
217
|
try { e = JSON.parse(line); } catch { continue; }
|
|
220
218
|
const p = e?.payload;
|
|
@@ -341,9 +339,7 @@ export async function parseCodexSessionFile(filePath, { cutoffMs = -Infinity } =
|
|
|
341
339
|
};
|
|
342
340
|
const copied = forkedCopyFilter();
|
|
343
341
|
let previous = null;
|
|
344
|
-
const
|
|
345
|
-
const lines = createInterface({ input: stream, crlfDelay: Infinity });
|
|
346
|
-
for await (const line of lines) {
|
|
342
|
+
for await (const line of transcriptLines(filePath)) {
|
|
347
343
|
let e;
|
|
348
344
|
try { e = JSON.parse(line); } catch { continue; }
|
|
349
345
|
const p = e?.payload;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createRequire } from 'node:module';
|
|
2
|
-
import { forkedCopyFilter, codexToolEvents } from './
|
|
2
|
+
import { forkedCopyFilter, codexToolEvents } from './parser.js';
|
|
3
3
|
|
|
4
|
-
const { checkReplyLanguage } = createRequire(import.meta.url)('
|
|
4
|
+
const { checkReplyLanguage } = createRequire(import.meta.url)('../reply-language.cjs');
|
|
5
5
|
|
|
6
6
|
/** Ignore copied history and commentary; only the current turn's final reply counts. */
|
|
7
7
|
export function lastCodexTurn(lines, { turnId, sessionId } = {}) {
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import { readFileSync, mkdirSync } from 'node:fs';
|
|
2
2
|
import { join, resolve } from 'node:path';
|
|
3
3
|
import { randomUUID } from 'node:crypto';
|
|
4
|
-
import { userDataDir } from '
|
|
5
|
-
import { writeViaTmp } from '
|
|
6
|
-
import { loadConfig } from '
|
|
7
|
-
import { findProjectRoot } from '
|
|
8
|
-
import { discoverCodexSessionFiles, parseCodexTurns } from './
|
|
4
|
+
import { userDataDir } from '../paths.js';
|
|
5
|
+
import { writeViaTmp } from '../state-file.js';
|
|
6
|
+
import { loadConfig } from '../config.js';
|
|
7
|
+
import { findProjectRoot } from '../harness.js';
|
|
8
|
+
import { discoverCodexSessionFiles, parseCodexTurns } from './parser.js';
|
|
9
9
|
import { categorize, isSkippable, tierOf, calibrateThresholds, MIN_RECURRENCE,
|
|
10
|
-
RESCAN_MIN_INTERVAL_MS, RESCAN_BIG_DELTA_BYTES, RESCAN_MAX_AGE_MS } from '
|
|
11
|
-
import { codexPromptCategory, loadCodexModelRules, sharedCodexModelRules, codexSharedTargets } from './
|
|
12
|
-
import { codexRunCost, codexPriceBook, isDirectOpenAI, readCodexPrices } from './
|
|
13
|
-
import { OPENAI_PRICES_CHECKED_AT } from '
|
|
14
|
-
import { refreshCodexLedger } from './
|
|
10
|
+
RESCAN_MIN_INTERVAL_MS, RESCAN_BIG_DELTA_BYTES, RESCAN_MAX_AGE_MS } from '../route-scan.js';
|
|
11
|
+
import { codexPromptCategory, loadCodexModelRules, sharedCodexModelRules, codexSharedTargets } from './delegation.js';
|
|
12
|
+
import { codexRunCost, codexPriceBook, isDirectOpenAI, readCodexPrices } from './cache-policy.js';
|
|
13
|
+
import { OPENAI_PRICES_CHECKED_AT } from '../openai-prices.js';
|
|
14
|
+
import { refreshCodexLedger } from './ledger.js';
|
|
15
15
|
|
|
16
16
|
/*
|
|
17
17
|
* Codex route-scan: recurring easy turns become delegation-rule candidates.
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { resolve } from 'node:path';
|
|
2
|
-
import { loadCodexLedger } from './
|
|
3
|
-
import { isFailedRun } from '
|
|
4
|
-
import { wilsonLowerBound, HEALTH_ERR_RATE, HEALTH_MIN_SAMPLE_DELEGATED } from '
|
|
5
|
-
import { loadSharedModelRules, sharedModelRulesForProject, realProjectRoot } from '
|
|
2
|
+
import { loadCodexLedger } from './ledger.js';
|
|
3
|
+
import { isFailedRun } from '../route-scan.js';
|
|
4
|
+
import { wilsonLowerBound, HEALTH_ERR_RATE, HEALTH_MIN_SAMPLE_DELEGATED } from '../model-rules.js';
|
|
5
|
+
import { loadSharedModelRules, sharedModelRulesForProject, realProjectRoot } from '../shared-model-rules.js';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Measured outcome of one Codex rule, judged as Claude's rule health judges
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import { loadConfig, saveConfig } from '../config.js';
|
|
2
|
-
import { configureCodexHooks } from '../codex
|
|
2
|
+
import { configureCodexHooks } from '../codex/installer.js';
|
|
3
3
|
import { validateCodexTarget, loadCodexModelRules, addCodexModelRule, removeCodexModelRule,
|
|
4
4
|
validateCodexSharedTarget, codexSharedTargets, codexDelegateMinContext, DEFAULT_DELEGATE_MIN_CONTEXT,
|
|
5
|
-
autoCodexSharedTargets, codexDelegateEnabled, codexImplementationHint } from '../codex
|
|
6
|
-
import { findCodexCandidate, resolveCodexCandidate } from '../codex
|
|
7
|
-
import { codexRoutingSavedTotals, loadCodexLedger } from '../codex
|
|
8
|
-
import { codexRuleHealth, codexRulesInReview, codexSharedRuleHealth } from '../codex
|
|
5
|
+
autoCodexSharedTargets, codexDelegateEnabled, codexImplementationHint } from '../codex/delegation.js';
|
|
6
|
+
import { findCodexCandidate, resolveCodexCandidate } from '../codex/route-scan.js';
|
|
7
|
+
import { codexRoutingSavedTotals, loadCodexLedger } from '../codex/ledger.js';
|
|
8
|
+
import { codexRuleHealth, codexRulesInReview, codexSharedRuleHealth } from '../codex/rule-health.js';
|
|
9
9
|
import { signedUsd } from '../money.js';
|
|
10
10
|
import { codexProviderName, codexModelName } from '../agent.js';
|
|
11
11
|
import { loadSharedModelRules, sharedModelRulesForProject, SHARED_RULE_TIERS } from '../shared-model-rules.js';
|
|
12
|
-
import { codexDelegationDiagnostics } from '../codex
|
|
12
|
+
import { codexDelegationDiagnostics } from '../codex/delegation-diagnostics.js';
|
|
13
13
|
|
|
14
14
|
// Starts from the working directory, as the hooks do, so a project policy
|
|
15
15
|
// stored under either agent's project root is counted.
|