@ashlr/hub 3.8.0 → 3.9.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/CHANGELOG.md +201 -0
- package/dist/build-identity.json +2 -2
- package/dist/cli/resource-profile.js +95 -21
- package/dist/cli/resource-profile.js.map +1 -1
- package/dist/core/resources/native-profile.d.ts +56 -0
- package/dist/core/resources/native-profile.js +368 -6
- package/dist/core/resources/native-profile.js.map +1 -1
- package/dist/core/run/model-catalog.js +8 -1
- package/dist/core/run/model-catalog.js.map +1 -1
- package/dist/core/universe/builtins/preparation/manifest.json +1 -1
- package/dist/core/universe/builtins/preparation/preparation-bridge.mjs +9 -2
- package/dist/core/universe/builtins/preparation/preparation-verification-fixtures.mjs +8 -1
- package/dist/core/verse/adapters/claude.d.ts +49 -2
- package/dist/core/verse/adapters/claude.js +317 -56
- package/dist/core/verse/adapters/claude.js.map +1 -1
- package/dist/core/verse/adapters/codex.d.ts +81 -5
- package/dist/core/verse/adapters/codex.js +409 -47
- package/dist/core/verse/adapters/codex.js.map +1 -1
- package/dist/core/verse/adapters/grok.d.ts +13 -1
- package/dist/core/verse/adapters/grok.js +28 -2
- package/dist/core/verse/adapters/grok.js.map +1 -1
- package/dist/core/verse/adapters/index.d.ts +32 -0
- package/dist/core/verse/adapters/index.js +2 -0
- package/dist/core/verse/adapters/index.js.map +1 -1
- package/dist/core/verse/codex-rollout.d.ts +284 -0
- package/dist/core/verse/codex-rollout.js +786 -0
- package/dist/core/verse/codex-rollout.js.map +1 -0
- package/dist/core/verse/context-fit.d.ts +63 -0
- package/dist/core/verse/context-fit.js +355 -0
- package/dist/core/verse/context-fit.js.map +1 -0
- package/dist/core/verse/context-math.d.ts +215 -0
- package/dist/core/verse/context-math.js +365 -0
- package/dist/core/verse/context-math.js.map +1 -0
- package/dist/core/verse/local-models.d.ts +137 -1
- package/dist/core/verse/local-models.js +209 -5
- package/dist/core/verse/local-models.js.map +1 -1
- package/dist/core/verse/model-windows.d.ts +217 -0
- package/dist/core/verse/model-windows.js +616 -0
- package/dist/core/verse/model-windows.js.map +1 -0
- package/dist/core/verse/preferences.d.ts +123 -0
- package/dist/core/verse/preferences.js +403 -0
- package/dist/core/verse/preferences.js.map +1 -0
- package/dist/core/verse/project-memory.d.ts +112 -0
- package/dist/core/verse/project-memory.js +295 -0
- package/dist/core/verse/project-memory.js.map +1 -0
- package/dist/core/verse/seats.d.ts +106 -4
- package/dist/core/verse/seats.js +291 -89
- package/dist/core/verse/seats.js.map +1 -1
- package/dist/core/verse/session-engine.d.ts +76 -2
- package/dist/core/verse/session-engine.js +684 -63
- package/dist/core/verse/session-engine.js.map +1 -1
- package/dist/core/verse/session-handoff.d.ts +94 -0
- package/dist/core/verse/session-handoff.js +551 -0
- package/dist/core/verse/session-handoff.js.map +1 -0
- package/dist/core/verse/session-search.d.ts +63 -0
- package/dist/core/verse/session-search.js +213 -0
- package/dist/core/verse/session-search.js.map +1 -0
- package/dist/core/verse/session-store.d.ts +16 -2
- package/dist/core/verse/session-store.js +80 -6
- package/dist/core/verse/session-store.js.map +1 -1
- package/dist/core/verse/types.d.ts +333 -1
- package/dist/core/verse/types.js +54 -2
- package/dist/core/verse/types.js.map +1 -1
- package/dist/core/verse/verse-api.d.ts +40 -5
- package/dist/core/verse/verse-api.js +724 -25
- package/dist/core/verse/verse-api.js.map +1 -1
- package/dist/core/web/api.d.ts +14 -4
- package/dist/core/web/api.js +26 -11
- package/dist/core/web/api.js.map +1 -1
- package/dist/core/web/public/next/assets/{App-BsbJFA2j.js → App-XURmiAAp.js} +5 -5
- package/dist/core/web/public/next/assets/{App-BsbJFA2j.js.map → App-XURmiAAp.js.map} +1 -1
- package/dist/core/web/public/next/assets/{ApprovalsSection-2CCSoBTJ.js → ApprovalsSection-BriUzoZc.js} +2 -2
- package/dist/core/web/public/next/assets/{ApprovalsSection-2CCSoBTJ.js.map → ApprovalsSection-BriUzoZc.js.map} +1 -1
- package/dist/core/web/public/next/assets/AutonomySection-C4GBsQVc.js +2 -0
- package/dist/core/web/public/next/assets/{AutonomySection-yeNNUPur.js.map → AutonomySection-C4GBsQVc.js.map} +1 -1
- package/dist/core/web/public/next/assets/ChatSection-BpOBOUvg.css +1 -0
- package/dist/core/web/public/next/assets/ChatSection-hlgPBRco.js +84 -0
- package/dist/core/web/public/next/assets/ChatSection-hlgPBRco.js.map +1 -0
- package/dist/core/web/public/next/assets/ConfirmDialog-BdQYI-Ng.js +2 -0
- package/dist/core/web/public/next/assets/ConfirmDialog-BdQYI-Ng.js.map +1 -0
- package/dist/core/web/public/next/assets/{Dialog-nK15L1M0.js → Dialog-BxdIfmFH.js} +2 -2
- package/dist/core/web/public/next/assets/{Dialog-nK15L1M0.js.map → Dialog-BxdIfmFH.js.map} +1 -1
- package/dist/core/web/public/next/assets/{DiffViewer-D369eZe8.js → DiffViewer-DmeRWuc4.js} +2 -2
- package/dist/core/web/public/next/assets/{DiffViewer-D369eZe8.js.map → DiffViewer-DmeRWuc4.js.map} +1 -1
- package/dist/core/web/public/next/assets/{McpSection-DVgiSYG1.js → McpSection-q34T7Tho.js} +2 -2
- package/dist/core/web/public/next/assets/{McpSection-DVgiSYG1.js.map → McpSection-q34T7Tho.js.map} +1 -1
- package/dist/core/web/public/next/assets/{MutationTokenDialog-B9I4PAcp.js → MutationTokenDialog-CFAkwOEt.js} +2 -2
- package/dist/core/web/public/next/assets/{MutationTokenDialog-B9I4PAcp.js.map → MutationTokenDialog-CFAkwOEt.js.map} +1 -1
- package/dist/core/web/public/next/assets/{ResourcePoolConsoleApp-z6tJj3s9.js → ResourcePoolConsoleApp-DHc8GFOY.js} +4 -4
- package/dist/core/web/public/next/assets/{ResourcePoolConsoleApp-z6tJj3s9.js.map → ResourcePoolConsoleApp-DHc8GFOY.js.map} +1 -1
- package/dist/core/web/public/next/assets/{RouteErrorBoundary-OSajL45F.js → RouteErrorBoundary-ZL9F4qUn.js} +2 -2
- package/dist/core/web/public/next/assets/{RouteErrorBoundary-OSajL45F.js.map → RouteErrorBoundary-ZL9F4qUn.js.map} +1 -1
- package/dist/core/web/public/next/assets/{Meter-CLcr-4wS.css → Segmented-8cf0Fypn.css} +1 -1
- package/dist/core/web/public/next/assets/{Meter-Cmo1pLbk.js → Segmented-DbRjVye3.js} +2 -2
- package/dist/core/web/public/next/assets/Segmented-DbRjVye3.js.map +1 -0
- package/dist/core/web/public/next/assets/SettingsSection-nJGGGYqv.js +2 -0
- package/dist/core/web/public/next/assets/{SettingsSection-B3YH2Npi.js.map → SettingsSection-nJGGGYqv.js.map} +1 -1
- package/dist/core/web/public/next/assets/UniverseConsoleApp-6GJydgo4.js +2 -0
- package/dist/core/web/public/next/assets/{UniverseConsoleApp-wEKIzl1M.js.map → UniverseConsoleApp-6GJydgo4.js.map} +1 -1
- package/dist/core/web/public/next/assets/{UniverseView-CQVpJqqI.js → UniverseView-DLDtNYL8.js} +2 -2
- package/dist/core/web/public/next/assets/{UniverseView-CQVpJqqI.js.map → UniverseView-DLDtNYL8.js.map} +1 -1
- package/dist/core/web/public/next/assets/UsageSection-Cf9d9iFH.js +2 -0
- package/dist/core/web/public/next/assets/UsageSection-Cf9d9iFH.js.map +1 -0
- package/dist/core/web/public/next/assets/VerseConsoleApp-DsdKQoTH.css +1 -0
- package/dist/core/web/public/next/assets/VerseConsoleApp-y0uOHixd.js +3 -0
- package/dist/core/web/public/next/assets/VerseConsoleApp-y0uOHixd.js.map +1 -0
- package/dist/core/web/public/next/assets/{charts-BopViIha.js → charts-CVooqJWN.js} +2 -2
- package/dist/core/web/public/next/assets/{charts-BopViIha.js.map → charts-CVooqJWN.js.map} +1 -1
- package/dist/core/web/public/next/assets/context-model-Bqv8DVuH.js +2 -0
- package/dist/core/web/public/next/assets/context-model-Bqv8DVuH.js.map +1 -0
- package/dist/core/web/public/next/assets/global-eaY9GyX3.js +2 -0
- package/dist/core/web/public/next/assets/global-eaY9GyX3.js.map +1 -0
- package/dist/core/web/public/next/assets/{icons-L06pQH6j.js → icons-vex8rclC.js} +2 -2
- package/dist/core/web/public/next/assets/{icons-L06pQH6j.js.map → icons-vex8rclC.js.map} +1 -1
- package/dist/core/web/public/next/assets/{index-CXgVQHek.js → index-D2Eik-zt.js} +3 -3
- package/dist/core/web/public/next/assets/{index-CXgVQHek.js.map → index-D2Eik-zt.js.map} +1 -1
- package/dist/core/web/public/next/assets/{queries-D3sSlan1.js → queries-bAYU0VNy.js} +2 -2
- package/dist/core/web/public/next/assets/{queries-D3sSlan1.js.map → queries-bAYU0VNy.js.map} +1 -1
- package/dist/core/web/public/next/assets/use-guarded-action-C7QrP7Yp.js +2 -0
- package/dist/core/web/public/next/assets/{use-guarded-action-BxfJ9E-n.js.map → use-guarded-action-C7QrP7Yp.js.map} +1 -1
- package/dist/core/web/public/next/assets/verse-model-TfDk9cky.js +2 -0
- package/dist/core/web/public/next/assets/verse-model-TfDk9cky.js.map +1 -0
- package/dist/core/web/public/next/assets/{workspace-model-v0a1gl-C.js → workspace-model-CHol5RVp.js} +2 -2
- package/dist/core/web/public/next/assets/{workspace-model-v0a1gl-C.js.map → workspace-model-CHol5RVp.js.map} +1 -1
- package/dist/core/web/public/next/index.html +1 -1
- package/dist/release-dependency-inventory.json +1 -1
- package/docs/README.md +1 -0
- package/package.json +1 -1
- package/dist/core/web/public/next/assets/AutonomySection-yeNNUPur.js +0 -2
- package/dist/core/web/public/next/assets/ChatSection-BfnLe5dT.css +0 -1
- package/dist/core/web/public/next/assets/ChatSection-DwDDnCZQ.js +0 -80
- package/dist/core/web/public/next/assets/ChatSection-DwDDnCZQ.js.map +0 -1
- package/dist/core/web/public/next/assets/ConfirmDialog-C363ty_c.js +0 -2
- package/dist/core/web/public/next/assets/ConfirmDialog-C363ty_c.js.map +0 -1
- package/dist/core/web/public/next/assets/Meter-Cmo1pLbk.js.map +0 -1
- package/dist/core/web/public/next/assets/SettingsSection-B3YH2Npi.js +0 -2
- package/dist/core/web/public/next/assets/UniverseConsoleApp-wEKIzl1M.js +0 -2
- package/dist/core/web/public/next/assets/UsageSection-dToBThXe.js +0 -2
- package/dist/core/web/public/next/assets/UsageSection-dToBThXe.js.map +0 -1
- package/dist/core/web/public/next/assets/VerseConsoleApp-CMkCizvL.css +0 -1
- package/dist/core/web/public/next/assets/VerseConsoleApp-DHU32JCQ.js +0 -3
- package/dist/core/web/public/next/assets/VerseConsoleApp-DHU32JCQ.js.map +0 -1
- package/dist/core/web/public/next/assets/global-DMWakEhP.js +0 -2
- package/dist/core/web/public/next/assets/global-DMWakEhP.js.map +0 -1
- package/dist/core/web/public/next/assets/use-guarded-action-BxfJ9E-n.js +0 -2
- package/dist/core/web/public/next/assets/verse-model-CvtK6vP6.js +0 -2
- package/dist/core/web/public/next/assets/verse-model-CvtK6vP6.js.map +0 -1
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ashlr Verse — context arithmetic shared by the server and the browser.
|
|
3
|
+
*
|
|
4
|
+
* PURE and BROWSER-SAFE: no node imports, no I/O. The seat catalog, the
|
|
5
|
+
* session engine, the adapters and the UI all compute thresholds through this
|
|
6
|
+
* one module, so "compacts at 367k" can never mean two different numbers in
|
|
7
|
+
* two places. docs/VERSE-CONTEXT.md is the prose authority for every constant.
|
|
8
|
+
*
|
|
9
|
+
* Where each formula comes from (all read from the CLIs themselves, 2026-09-23):
|
|
10
|
+
*
|
|
11
|
+
* Claude Code (2.1.257 and 2.1.280 binaries): the auto-compaction window is
|
|
12
|
+
* `min(--autocompact | autoCompactWindow | auto, model window)`. From it the
|
|
13
|
+
* CLI reserves `min(max output, 20_000)` for the reply and compacts a
|
|
14
|
+
* further 13_000 below that. On a 1M model at `auto` that is 967_000 — the
|
|
15
|
+
* `pre_tokens` observed on real compactions (967_391).
|
|
16
|
+
* Codex (0.136 – 0.155 rollouts): the window the CLI measures against is
|
|
17
|
+
* `context_window × effective_context_window_percent / 100` = 258_400 for
|
|
18
|
+
* every 272k model; auto-compaction triggers at 90% of the RAW window
|
|
19
|
+
* (244_800). The largest uncompacted call across 62,104 was 243_204.
|
|
20
|
+
* Grok (0.2.118 catalog): compacts at `auto_compact_threshold_percent` (80)
|
|
21
|
+
* of `context_window` (500_000) = 400_000.
|
|
22
|
+
* Local seats run the Claude Code binary against Ollama, so they compact by
|
|
23
|
+
* the Claude formula over the window Verse passes in
|
|
24
|
+
* `CLAUDE_CODE_MAX_CONTEXT_TOKENS`.
|
|
25
|
+
*/
|
|
26
|
+
import type { VerseContextBudget, VerseContextMode, VerseEngine, VerseFitVerdict, VerseModelOption, VerseSession, VerseUsage } from './types.js';
|
|
27
|
+
/**
|
|
28
|
+
* Standard-mode compaction window for 1M-context Claude models.
|
|
29
|
+
*
|
|
30
|
+
* WHY 400k and not the CLI's ~967k: every turn re-sends the whole prefix, so a
|
|
31
|
+
* session held near 900k costs ~2.3× per call what one held under 400k does,
|
|
32
|
+
* and long-context recall degrades with length on every published benchmark
|
|
33
|
+
* (docs/VERSE-CONTEXT.md §2). 400k keeps a large working set — far above the
|
|
34
|
+
* 150k Anthropic's own API compacts at by default — while leaving the full
|
|
35
|
+
* window one click away in `expansive` mode. Grok's native budget is also
|
|
36
|
+
* 400k, so the two frontier engines share one standard ceiling.
|
|
37
|
+
*/
|
|
38
|
+
export declare const CLAUDE_STANDARD_AUTOCOMPACT_WINDOW = 400000;
|
|
39
|
+
/** Claude Code reserves min(max output, this) of the window for the reply. */
|
|
40
|
+
export declare const CLAUDE_OUTPUT_RESERVE_CAP = 20000;
|
|
41
|
+
/** …and compacts this many tokens below the reserved window. */
|
|
42
|
+
export declare const CLAUDE_COMPACT_BUFFER = 13000;
|
|
43
|
+
/** Claude Code clamps `--autocompact` to [100k, 1M]. */
|
|
44
|
+
export declare const CLAUDE_AUTOCOMPACT_MIN = 100000;
|
|
45
|
+
export declare const CLAUDE_AUTOCOMPACT_MAX = 1000000;
|
|
46
|
+
/** Codex: default `effective_context_window_percent` in every catalog entry. */
|
|
47
|
+
export declare const CODEX_EFFECTIVE_WINDOW_PERCENT = 95;
|
|
48
|
+
/** Codex: auto-compaction fires at this fraction of the RAW window. */
|
|
49
|
+
export declare const CODEX_AUTO_COMPACT_FRACTION = 0.9;
|
|
50
|
+
/** Grok: `auto_compact_threshold_percent` in the 0.2.118 catalog. */
|
|
51
|
+
export declare const GROK_AUTO_COMPACT_PERCENT = 80;
|
|
52
|
+
/**
|
|
53
|
+
* How long a provider keeps a conversation's prompt cache warm while idle.
|
|
54
|
+
* Claude Code on a subscription: 1h (5 min without it). Past this, the next
|
|
55
|
+
* turn re-reads the whole context at full price — the single most expensive
|
|
56
|
+
* thing an idle long session can do.
|
|
57
|
+
*/
|
|
58
|
+
export declare const CACHE_IDLE_TTL_MS: number;
|
|
59
|
+
/**
|
|
60
|
+
* Fixed prompt overhead (system prompt + tool definitions) a FRESH session
|
|
61
|
+
* already occupies before any work, per engine.
|
|
62
|
+
*
|
|
63
|
+
* local 15k — measured: two real turns through Claude Code 2.1.280 against
|
|
64
|
+
* Ollama with `--exclude-dynamic-system-prompt-sections`
|
|
65
|
+
* reported 14,695 and 14,942 context tokens for a one-word reply.
|
|
66
|
+
* claude 25k — the same CLI without that flag measured 17.8k–23.3k on local
|
|
67
|
+
* seats; rounded up. An ESTIMATE, never shown as a measurement.
|
|
68
|
+
* codex 15k / grok 20k — estimates; neither CLI reports its base prompt.
|
|
69
|
+
*
|
|
70
|
+
* Only used by the fit verdicts, which are labelled as estimates in the UI.
|
|
71
|
+
*/
|
|
72
|
+
export declare const SESSION_BASE_OVERHEAD_BY_ENGINE: Readonly<Record<VerseEngine, number>>;
|
|
73
|
+
/** Fallback overhead when the engine is unknown (the largest estimate). */
|
|
74
|
+
export declare const SESSION_BASE_OVERHEAD_TOKENS = 25000;
|
|
75
|
+
export declare function sessionOverheadTokens(engine: VerseEngine | null | undefined): number;
|
|
76
|
+
/**
|
|
77
|
+
* Smallest local window worth offering as a seat. Local turns run Claude Code
|
|
78
|
+
* with `CLAUDE_CODE_MAX_CONTEXT_TOKENS=<window>`, which compacts at
|
|
79
|
+
* `window − 20k − 13k`; below this the compaction point leaves under 8k of
|
|
80
|
+
* working room above the ~15k base prompt, so every turn would compact.
|
|
81
|
+
*/
|
|
82
|
+
export declare const LOCAL_MIN_WORKING_TOKENS = 8000;
|
|
83
|
+
export declare const LOCAL_MIN_USABLE_WINDOW: number;
|
|
84
|
+
/** True when a local window leaves real working room under Claude Code's compaction rule. */
|
|
85
|
+
export declare function localWindowUsable(window: number | null | undefined): boolean;
|
|
86
|
+
/** chars → tokens estimator used everywhere a count is estimated rather than measured. */
|
|
87
|
+
export declare const CHARS_PER_TOKEN = 4;
|
|
88
|
+
/**
|
|
89
|
+
* Claude Code's auto-compaction point.
|
|
90
|
+
*
|
|
91
|
+
* @param modelWindow the model's context window (1_000_000, 200_000, or
|
|
92
|
+
* the local num_ctx passed in CLAUDE_CODE_MAX_CONTEXT_TOKENS)
|
|
93
|
+
* @param maxOutputTokens the model's default max output; null → assume the cap
|
|
94
|
+
* @param autocompactWindow the `--autocompact` value in force; null → `auto`
|
|
95
|
+
* (the full model window)
|
|
96
|
+
*/
|
|
97
|
+
export declare function claudeAutoCompactAt(modelWindow: number, maxOutputTokens: number | null, autocompactWindow?: number | null): number;
|
|
98
|
+
/** Codex: the window the CLI measures occupancy against. */
|
|
99
|
+
export declare function codexEffectiveWindow(rawWindow: number, percent?: number | null): number;
|
|
100
|
+
/** Codex: where auto-compaction fires for a given RAW window. */
|
|
101
|
+
export declare function codexAutoCompactAt(rawWindow: number): number;
|
|
102
|
+
/** Grok: where auto-compaction fires. */
|
|
103
|
+
export declare function grokAutoCompactAt(window: number, percent?: number | null): number;
|
|
104
|
+
/**
|
|
105
|
+
* Ids Verse once shipped that the CLI does NOT resolve to the model the label
|
|
106
|
+
* promised. `claude-opus-5.5` is parsed by Claude Code as a fuzzy match for
|
|
107
|
+
* `claude-opus-5` (its normaliser rejects the `.5` suffix and falls back to
|
|
108
|
+
* `includes('claude-opus-5')`), so every "Opus 5.5" turn actually ran Opus 5.
|
|
109
|
+
* Old session records keep their stored id; adapters send the canonical one.
|
|
110
|
+
*/
|
|
111
|
+
export declare const VERSE_MODEL_ID_ALIASES: Readonly<Record<string, string>>;
|
|
112
|
+
export declare function canonicalModelId(id: string): string;
|
|
113
|
+
/** The budget a model runs with in a mode, or null when that mode does not exist for it. */
|
|
114
|
+
export declare function budgetFor(option: VerseModelOption | null | undefined, mode: VerseContextMode): VerseContextBudget | null;
|
|
115
|
+
/** True when this model has a genuine expansive budget larger than its standard one. */
|
|
116
|
+
export declare function hasExpansiveMode(option: VerseModelOption | null | undefined): boolean;
|
|
117
|
+
/**
|
|
118
|
+
* The `--autocompact` value Verse passes to Claude Code for a model in a mode,
|
|
119
|
+
* or null for "let the CLI use `auto`". Only 1M-native models are capped in
|
|
120
|
+
* standard mode; a 200k model already compacts near 167k natively.
|
|
121
|
+
*/
|
|
122
|
+
export declare function claudeAutocompactFlag(option: VerseModelOption | null | undefined, mode: VerseContextMode): number | null;
|
|
123
|
+
/**
|
|
124
|
+
* Recompute the compaction point when the CLI reports a window at runtime
|
|
125
|
+
* that differs from the catalog budget (Claude Code clamps a 1M model to 200k
|
|
126
|
+
* when long-context credit runs out; grok can upgrade a window by header).
|
|
127
|
+
*
|
|
128
|
+
* claude/local — fixed-buffer formula over min(runtime window, --autocompact).
|
|
129
|
+
* codex — the runtime figure is the EFFECTIVE window; the compaction
|
|
130
|
+
* point stays 90% of the raw window it implies.
|
|
131
|
+
* grok/other — keep the budget's compaction RATIO.
|
|
132
|
+
*/
|
|
133
|
+
export declare function reconcileAutoCompactAt(input: {
|
|
134
|
+
engine: VerseEngine;
|
|
135
|
+
runtimeWindow: number;
|
|
136
|
+
budget: VerseContextBudget | null;
|
|
137
|
+
/** The `--autocompact` value in force for claude/local; null = auto. */
|
|
138
|
+
autocompactWindow?: number | null;
|
|
139
|
+
maxOutputTokens?: number | null;
|
|
140
|
+
}): number | null;
|
|
141
|
+
export type VerseOccupancyTone = 'unknown' | 'ok' | 'warn' | 'danger' | 'over';
|
|
142
|
+
export interface VerseOccupancy {
|
|
143
|
+
tokens: number;
|
|
144
|
+
window: number | null;
|
|
145
|
+
autoCompactAt: number | null;
|
|
146
|
+
/** tokens / window, unclamped; null when the window is unknown. */
|
|
147
|
+
ofWindow: number | null;
|
|
148
|
+
/** tokens / autoCompactAt, unclamped; null when the compaction point is unknown. */
|
|
149
|
+
ofCompaction: number | null;
|
|
150
|
+
/** Tokens left before auto-compaction (≥ 0), null when unknown. */
|
|
151
|
+
untilCompaction: number | null;
|
|
152
|
+
tone: VerseOccupancyTone;
|
|
153
|
+
/** False when `tokens` is an upper bound (codex before its rollout is readable). */
|
|
154
|
+
exact: boolean;
|
|
155
|
+
}
|
|
156
|
+
/** Warn at 80% of the compaction point, danger from 95% — BEFORE the CLI compacts, not after. */
|
|
157
|
+
export declare const OCCUPANCY_WARN = 0.8;
|
|
158
|
+
export declare const OCCUPANCY_DANGER = 0.95;
|
|
159
|
+
export declare function occupancy(usage: Pick<VerseUsage, 'contextTokens' | 'contextWindow' | 'autoCompactAt' | 'contextTokensExact'>): VerseOccupancy;
|
|
160
|
+
/**
|
|
161
|
+
* Share of prompt tokens served from cache: cacheRead / (input + cacheRead +
|
|
162
|
+
* cacheCreation). Adapters already report `inputTokens` EXCLUDING cached
|
|
163
|
+
* tokens (codex's turn total is split by its adapter), so the buckets are
|
|
164
|
+
* disjoint. Null when nothing has been read yet.
|
|
165
|
+
*/
|
|
166
|
+
export declare function cacheHitRatio(usage: Pick<VerseUsage, 'inputTokens' | 'cacheReadTokens' | 'cacheCreationTokens'>): number | null;
|
|
167
|
+
export declare function estimateTokensFromChars(chars: number): number;
|
|
168
|
+
/**
|
|
169
|
+
* Would this working set fit one context on this model?
|
|
170
|
+
*
|
|
171
|
+
* fits — working set + fixed overhead ≤ 60% of the standard compaction point
|
|
172
|
+
* tight — ≤ 100% of it: it fits, but the session will compact soon
|
|
173
|
+
* expansive — only the expansive budget holds it
|
|
174
|
+
* split — no single context holds it; fan the work out instead
|
|
175
|
+
*
|
|
176
|
+
* Null when the model's budget is unknown (never guessed). Pass the engine's
|
|
177
|
+
* `sessionOverheadTokens(engine)`; the default is the largest estimate.
|
|
178
|
+
*/
|
|
179
|
+
export declare function fitVerdict(workingSetTokens: number, option: VerseModelOption | null | undefined, overheadTokens?: number): VerseFitVerdict | null;
|
|
180
|
+
export type VerseAdviceLevel = 'none' | 'suggest' | 'urge';
|
|
181
|
+
export interface VerseHandoffAdvice {
|
|
182
|
+
level: VerseAdviceLevel;
|
|
183
|
+
/** Plain sentences, most important first. Empty when level is 'none'. */
|
|
184
|
+
reasons: string[];
|
|
185
|
+
}
|
|
186
|
+
/** Compactions after which early-turn detail is mostly summaries. */
|
|
187
|
+
export declare const HANDOFF_COMPACTION_THRESHOLD = 2;
|
|
188
|
+
/** Below this, an expired cache is cheap to re-read and not worth a handoff. */
|
|
189
|
+
export declare const HANDOFF_IDLE_MIN_TOKENS = 100000;
|
|
190
|
+
/**
|
|
191
|
+
* Should the operator continue in a fresh session? Pure: `now` and
|
|
192
|
+
* `lastActivityAt` are passed in, so the verdict is reproducible in tests.
|
|
193
|
+
*/
|
|
194
|
+
export declare function handoffAdvice(input: {
|
|
195
|
+
usage: VerseUsage;
|
|
196
|
+
compactionCount?: number;
|
|
197
|
+
lastActivityAt?: string | null;
|
|
198
|
+
now: number;
|
|
199
|
+
}): VerseHandoffAdvice;
|
|
200
|
+
export interface VerseExpansiveAdvice {
|
|
201
|
+
suggest: boolean;
|
|
202
|
+
reason: string | null;
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Suggest — never switch on — expansive mode. Only for a session in standard
|
|
206
|
+
* mode whose model has a real expansive budget, and only on evidence: it has
|
|
207
|
+
* already compacted repeatedly, or its working set only fits expansive.
|
|
208
|
+
*/
|
|
209
|
+
export declare function expansiveAdvice(input: {
|
|
210
|
+
session: Pick<VerseSession, 'contextMode' | 'compactionCount'>;
|
|
211
|
+
option: VerseModelOption | null | undefined;
|
|
212
|
+
workingSetTokens?: number | null;
|
|
213
|
+
}): VerseExpansiveAdvice;
|
|
214
|
+
/** Engines whose CLIs can be told a compaction budget per invocation. */
|
|
215
|
+
export declare function engineSupportsModes(engine: VerseEngine): boolean;
|
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ashlr Verse — context arithmetic shared by the server and the browser.
|
|
3
|
+
*
|
|
4
|
+
* PURE and BROWSER-SAFE: no node imports, no I/O. The seat catalog, the
|
|
5
|
+
* session engine, the adapters and the UI all compute thresholds through this
|
|
6
|
+
* one module, so "compacts at 367k" can never mean two different numbers in
|
|
7
|
+
* two places. docs/VERSE-CONTEXT.md is the prose authority for every constant.
|
|
8
|
+
*
|
|
9
|
+
* Where each formula comes from (all read from the CLIs themselves, 2026-09-23):
|
|
10
|
+
*
|
|
11
|
+
* Claude Code (2.1.257 and 2.1.280 binaries): the auto-compaction window is
|
|
12
|
+
* `min(--autocompact | autoCompactWindow | auto, model window)`. From it the
|
|
13
|
+
* CLI reserves `min(max output, 20_000)` for the reply and compacts a
|
|
14
|
+
* further 13_000 below that. On a 1M model at `auto` that is 967_000 — the
|
|
15
|
+
* `pre_tokens` observed on real compactions (967_391).
|
|
16
|
+
* Codex (0.136 – 0.155 rollouts): the window the CLI measures against is
|
|
17
|
+
* `context_window × effective_context_window_percent / 100` = 258_400 for
|
|
18
|
+
* every 272k model; auto-compaction triggers at 90% of the RAW window
|
|
19
|
+
* (244_800). The largest uncompacted call across 62,104 was 243_204.
|
|
20
|
+
* Grok (0.2.118 catalog): compacts at `auto_compact_threshold_percent` (80)
|
|
21
|
+
* of `context_window` (500_000) = 400_000.
|
|
22
|
+
* Local seats run the Claude Code binary against Ollama, so they compact by
|
|
23
|
+
* the Claude formula over the window Verse passes in
|
|
24
|
+
* `CLAUDE_CODE_MAX_CONTEXT_TOKENS`.
|
|
25
|
+
*/
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
// Constants
|
|
28
|
+
// ---------------------------------------------------------------------------
|
|
29
|
+
/**
|
|
30
|
+
* Standard-mode compaction window for 1M-context Claude models.
|
|
31
|
+
*
|
|
32
|
+
* WHY 400k and not the CLI's ~967k: every turn re-sends the whole prefix, so a
|
|
33
|
+
* session held near 900k costs ~2.3× per call what one held under 400k does,
|
|
34
|
+
* and long-context recall degrades with length on every published benchmark
|
|
35
|
+
* (docs/VERSE-CONTEXT.md §2). 400k keeps a large working set — far above the
|
|
36
|
+
* 150k Anthropic's own API compacts at by default — while leaving the full
|
|
37
|
+
* window one click away in `expansive` mode. Grok's native budget is also
|
|
38
|
+
* 400k, so the two frontier engines share one standard ceiling.
|
|
39
|
+
*/
|
|
40
|
+
export const CLAUDE_STANDARD_AUTOCOMPACT_WINDOW = 400_000;
|
|
41
|
+
/** Claude Code reserves min(max output, this) of the window for the reply. */
|
|
42
|
+
export const CLAUDE_OUTPUT_RESERVE_CAP = 20_000;
|
|
43
|
+
/** …and compacts this many tokens below the reserved window. */
|
|
44
|
+
export const CLAUDE_COMPACT_BUFFER = 13_000;
|
|
45
|
+
/** Claude Code clamps `--autocompact` to [100k, 1M]. */
|
|
46
|
+
export const CLAUDE_AUTOCOMPACT_MIN = 100_000;
|
|
47
|
+
export const CLAUDE_AUTOCOMPACT_MAX = 1_000_000;
|
|
48
|
+
/** Codex: default `effective_context_window_percent` in every catalog entry. */
|
|
49
|
+
export const CODEX_EFFECTIVE_WINDOW_PERCENT = 95;
|
|
50
|
+
/** Codex: auto-compaction fires at this fraction of the RAW window. */
|
|
51
|
+
export const CODEX_AUTO_COMPACT_FRACTION = 0.9;
|
|
52
|
+
/** Grok: `auto_compact_threshold_percent` in the 0.2.118 catalog. */
|
|
53
|
+
export const GROK_AUTO_COMPACT_PERCENT = 80;
|
|
54
|
+
/**
|
|
55
|
+
* How long a provider keeps a conversation's prompt cache warm while idle.
|
|
56
|
+
* Claude Code on a subscription: 1h (5 min without it). Past this, the next
|
|
57
|
+
* turn re-reads the whole context at full price — the single most expensive
|
|
58
|
+
* thing an idle long session can do.
|
|
59
|
+
*/
|
|
60
|
+
export const CACHE_IDLE_TTL_MS = 60 * 60 * 1000;
|
|
61
|
+
/**
|
|
62
|
+
* Fixed prompt overhead (system prompt + tool definitions) a FRESH session
|
|
63
|
+
* already occupies before any work, per engine.
|
|
64
|
+
*
|
|
65
|
+
* local 15k — measured: two real turns through Claude Code 2.1.280 against
|
|
66
|
+
* Ollama with `--exclude-dynamic-system-prompt-sections`
|
|
67
|
+
* reported 14,695 and 14,942 context tokens for a one-word reply.
|
|
68
|
+
* claude 25k — the same CLI without that flag measured 17.8k–23.3k on local
|
|
69
|
+
* seats; rounded up. An ESTIMATE, never shown as a measurement.
|
|
70
|
+
* codex 15k / grok 20k — estimates; neither CLI reports its base prompt.
|
|
71
|
+
*
|
|
72
|
+
* Only used by the fit verdicts, which are labelled as estimates in the UI.
|
|
73
|
+
*/
|
|
74
|
+
export const SESSION_BASE_OVERHEAD_BY_ENGINE = {
|
|
75
|
+
claude: 25_000,
|
|
76
|
+
codex: 15_000,
|
|
77
|
+
grok: 20_000,
|
|
78
|
+
local: 15_000,
|
|
79
|
+
};
|
|
80
|
+
/** Fallback overhead when the engine is unknown (the largest estimate). */
|
|
81
|
+
export const SESSION_BASE_OVERHEAD_TOKENS = 25_000;
|
|
82
|
+
export function sessionOverheadTokens(engine) {
|
|
83
|
+
return engine ? SESSION_BASE_OVERHEAD_BY_ENGINE[engine] ?? SESSION_BASE_OVERHEAD_TOKENS : SESSION_BASE_OVERHEAD_TOKENS;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Smallest local window worth offering as a seat. Local turns run Claude Code
|
|
87
|
+
* with `CLAUDE_CODE_MAX_CONTEXT_TOKENS=<window>`, which compacts at
|
|
88
|
+
* `window − 20k − 13k`; below this the compaction point leaves under 8k of
|
|
89
|
+
* working room above the ~15k base prompt, so every turn would compact.
|
|
90
|
+
*/
|
|
91
|
+
export const LOCAL_MIN_WORKING_TOKENS = 8_000;
|
|
92
|
+
export const LOCAL_MIN_USABLE_WINDOW = SESSION_BASE_OVERHEAD_BY_ENGINE.local + LOCAL_MIN_WORKING_TOKENS + CLAUDE_OUTPUT_RESERVE_CAP + CLAUDE_COMPACT_BUFFER;
|
|
93
|
+
/** True when a local window leaves real working room under Claude Code's compaction rule. */
|
|
94
|
+
export function localWindowUsable(window) {
|
|
95
|
+
return typeof window === 'number' && Number.isFinite(window) && window >= LOCAL_MIN_USABLE_WINDOW;
|
|
96
|
+
}
|
|
97
|
+
/** chars → tokens estimator used everywhere a count is estimated rather than measured. */
|
|
98
|
+
export const CHARS_PER_TOKEN = 4;
|
|
99
|
+
// ---------------------------------------------------------------------------
|
|
100
|
+
// Per-engine formulas
|
|
101
|
+
// ---------------------------------------------------------------------------
|
|
102
|
+
function positiveInt(value) {
|
|
103
|
+
return typeof value === 'number' && Number.isFinite(value) && value > 0 ? Math.floor(value) : null;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Claude Code's auto-compaction point.
|
|
107
|
+
*
|
|
108
|
+
* @param modelWindow the model's context window (1_000_000, 200_000, or
|
|
109
|
+
* the local num_ctx passed in CLAUDE_CODE_MAX_CONTEXT_TOKENS)
|
|
110
|
+
* @param maxOutputTokens the model's default max output; null → assume the cap
|
|
111
|
+
* @param autocompactWindow the `--autocompact` value in force; null → `auto`
|
|
112
|
+
* (the full model window)
|
|
113
|
+
*/
|
|
114
|
+
export function claudeAutoCompactAt(modelWindow, maxOutputTokens, autocompactWindow = null) {
|
|
115
|
+
const window = Math.min(modelWindow, positiveInt(autocompactWindow) ?? modelWindow);
|
|
116
|
+
const reserve = Math.min(positiveInt(maxOutputTokens) ?? CLAUDE_OUTPUT_RESERVE_CAP, CLAUDE_OUTPUT_RESERVE_CAP);
|
|
117
|
+
return Math.max(0, window - reserve - CLAUDE_COMPACT_BUFFER);
|
|
118
|
+
}
|
|
119
|
+
/** Codex: the window the CLI measures occupancy against. */
|
|
120
|
+
export function codexEffectiveWindow(rawWindow, percent = null) {
|
|
121
|
+
const pct = positiveInt(percent) !== null && percent <= 100 ? percent : CODEX_EFFECTIVE_WINDOW_PERCENT;
|
|
122
|
+
return Math.floor((rawWindow * pct) / 100);
|
|
123
|
+
}
|
|
124
|
+
/** Codex: where auto-compaction fires for a given RAW window. */
|
|
125
|
+
export function codexAutoCompactAt(rawWindow) {
|
|
126
|
+
return Math.floor(rawWindow * CODEX_AUTO_COMPACT_FRACTION);
|
|
127
|
+
}
|
|
128
|
+
/** Grok: where auto-compaction fires. */
|
|
129
|
+
export function grokAutoCompactAt(window, percent = null) {
|
|
130
|
+
const pct = positiveInt(percent) !== null && percent <= 100 ? percent : GROK_AUTO_COMPACT_PERCENT;
|
|
131
|
+
return Math.floor((window * pct) / 100);
|
|
132
|
+
}
|
|
133
|
+
// ---------------------------------------------------------------------------
|
|
134
|
+
// Model ids
|
|
135
|
+
// ---------------------------------------------------------------------------
|
|
136
|
+
/**
|
|
137
|
+
* Ids Verse once shipped that the CLI does NOT resolve to the model the label
|
|
138
|
+
* promised. `claude-opus-5.5` is parsed by Claude Code as a fuzzy match for
|
|
139
|
+
* `claude-opus-5` (its normaliser rejects the `.5` suffix and falls back to
|
|
140
|
+
* `includes('claude-opus-5')`), so every "Opus 5.5" turn actually ran Opus 5.
|
|
141
|
+
* Old session records keep their stored id; adapters send the canonical one.
|
|
142
|
+
*/
|
|
143
|
+
export const VERSE_MODEL_ID_ALIASES = {
|
|
144
|
+
'claude-opus-5.5': 'claude-opus-5-5',
|
|
145
|
+
};
|
|
146
|
+
export function canonicalModelId(id) {
|
|
147
|
+
return VERSE_MODEL_ID_ALIASES[id] ?? id;
|
|
148
|
+
}
|
|
149
|
+
// ---------------------------------------------------------------------------
|
|
150
|
+
// Budgets and occupancy
|
|
151
|
+
// ---------------------------------------------------------------------------
|
|
152
|
+
/** The budget a model runs with in a mode, or null when that mode does not exist for it. */
|
|
153
|
+
export function budgetFor(option, mode) {
|
|
154
|
+
if (!option)
|
|
155
|
+
return null;
|
|
156
|
+
if (mode === 'expansive')
|
|
157
|
+
return option.expansive ?? null;
|
|
158
|
+
const window = positiveInt(option.contextWindow);
|
|
159
|
+
if (window === null)
|
|
160
|
+
return null;
|
|
161
|
+
return { contextWindow: window, autoCompactAt: positiveInt(option.autoCompactAt ?? null) };
|
|
162
|
+
}
|
|
163
|
+
/** True when this model has a genuine expansive budget larger than its standard one. */
|
|
164
|
+
export function hasExpansiveMode(option) {
|
|
165
|
+
const standard = budgetFor(option, 'standard');
|
|
166
|
+
const expansive = budgetFor(option, 'expansive');
|
|
167
|
+
if (!expansive)
|
|
168
|
+
return false;
|
|
169
|
+
if (!standard)
|
|
170
|
+
return true;
|
|
171
|
+
return (expansive.autoCompactAt ?? expansive.contextWindow) > (standard.autoCompactAt ?? standard.contextWindow);
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* The `--autocompact` value Verse passes to Claude Code for a model in a mode,
|
|
175
|
+
* or null for "let the CLI use `auto`". Only 1M-native models are capped in
|
|
176
|
+
* standard mode; a 200k model already compacts near 167k natively.
|
|
177
|
+
*/
|
|
178
|
+
export function claudeAutocompactFlag(option, mode) {
|
|
179
|
+
if (mode === 'expansive')
|
|
180
|
+
return null;
|
|
181
|
+
const window = positiveInt(option?.contextWindow ?? null);
|
|
182
|
+
return window !== null && window > CLAUDE_STANDARD_AUTOCOMPACT_WINDOW && hasExpansiveMode(option)
|
|
183
|
+
? CLAUDE_STANDARD_AUTOCOMPACT_WINDOW
|
|
184
|
+
: null;
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Recompute the compaction point when the CLI reports a window at runtime
|
|
188
|
+
* that differs from the catalog budget (Claude Code clamps a 1M model to 200k
|
|
189
|
+
* when long-context credit runs out; grok can upgrade a window by header).
|
|
190
|
+
*
|
|
191
|
+
* claude/local — fixed-buffer formula over min(runtime window, --autocompact).
|
|
192
|
+
* codex — the runtime figure is the EFFECTIVE window; the compaction
|
|
193
|
+
* point stays 90% of the raw window it implies.
|
|
194
|
+
* grok/other — keep the budget's compaction RATIO.
|
|
195
|
+
*/
|
|
196
|
+
export function reconcileAutoCompactAt(input) {
|
|
197
|
+
const runtime = positiveInt(input.runtimeWindow);
|
|
198
|
+
if (runtime === null)
|
|
199
|
+
return input.budget?.autoCompactAt ?? null;
|
|
200
|
+
if (input.budget && input.budget.contextWindow === runtime)
|
|
201
|
+
return input.budget.autoCompactAt;
|
|
202
|
+
switch (input.engine) {
|
|
203
|
+
case 'claude':
|
|
204
|
+
case 'local':
|
|
205
|
+
return claudeAutoCompactAt(runtime, input.maxOutputTokens ?? null, input.autocompactWindow ?? null);
|
|
206
|
+
case 'codex':
|
|
207
|
+
return codexAutoCompactAt(Math.round((runtime * 100) / CODEX_EFFECTIVE_WINDOW_PERCENT));
|
|
208
|
+
default: {
|
|
209
|
+
const b = input.budget;
|
|
210
|
+
if (!b || b.autoCompactAt === null || b.contextWindow <= 0)
|
|
211
|
+
return null;
|
|
212
|
+
return Math.floor(runtime * (b.autoCompactAt / b.contextWindow));
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
/** Warn at 80% of the compaction point, danger from 95% — BEFORE the CLI compacts, not after. */
|
|
217
|
+
export const OCCUPANCY_WARN = 0.8;
|
|
218
|
+
export const OCCUPANCY_DANGER = 0.95;
|
|
219
|
+
export function occupancy(usage) {
|
|
220
|
+
const tokens = Math.max(0, Math.floor(usage.contextTokens || 0));
|
|
221
|
+
const window = positiveInt(usage.contextWindow);
|
|
222
|
+
const compactAt = positiveInt(usage.autoCompactAt ?? null);
|
|
223
|
+
const ofWindow = window !== null ? tokens / window : null;
|
|
224
|
+
const ofCompaction = compactAt !== null ? tokens / compactAt : null;
|
|
225
|
+
const gauge = ofCompaction ?? ofWindow;
|
|
226
|
+
const exact = usage.contextTokensExact !== false;
|
|
227
|
+
let tone = 'unknown';
|
|
228
|
+
if (!exact && gauge !== null && gauge >= 1) {
|
|
229
|
+
// An UPPER BOUND past the compaction point says nothing about the real
|
|
230
|
+
// prompt: codex's turn total sums every model call (median 29× the last
|
|
231
|
+
// one). Claiming "over the window" from it would be invented precision.
|
|
232
|
+
tone = 'unknown';
|
|
233
|
+
}
|
|
234
|
+
else if (window !== null && tokens > window)
|
|
235
|
+
tone = 'over';
|
|
236
|
+
else if (gauge !== null)
|
|
237
|
+
tone = gauge >= OCCUPANCY_DANGER ? 'danger' : gauge >= OCCUPANCY_WARN ? 'warn' : 'ok';
|
|
238
|
+
return {
|
|
239
|
+
tokens,
|
|
240
|
+
window,
|
|
241
|
+
autoCompactAt: compactAt,
|
|
242
|
+
ofWindow,
|
|
243
|
+
ofCompaction,
|
|
244
|
+
untilCompaction: compactAt !== null ? Math.max(0, compactAt - tokens) : null,
|
|
245
|
+
tone,
|
|
246
|
+
exact,
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
// ---------------------------------------------------------------------------
|
|
250
|
+
// Efficiency
|
|
251
|
+
// ---------------------------------------------------------------------------
|
|
252
|
+
/**
|
|
253
|
+
* Share of prompt tokens served from cache: cacheRead / (input + cacheRead +
|
|
254
|
+
* cacheCreation). Adapters already report `inputTokens` EXCLUDING cached
|
|
255
|
+
* tokens (codex's turn total is split by its adapter), so the buckets are
|
|
256
|
+
* disjoint. Null when nothing has been read yet.
|
|
257
|
+
*/
|
|
258
|
+
export function cacheHitRatio(usage) {
|
|
259
|
+
const denominator = usage.inputTokens + usage.cacheReadTokens + usage.cacheCreationTokens;
|
|
260
|
+
return denominator > 0 ? usage.cacheReadTokens / denominator : null;
|
|
261
|
+
}
|
|
262
|
+
export function estimateTokensFromChars(chars) {
|
|
263
|
+
return Math.ceil(Math.max(0, chars) / CHARS_PER_TOKEN);
|
|
264
|
+
}
|
|
265
|
+
// ---------------------------------------------------------------------------
|
|
266
|
+
// Advice (pure; the UI decides how to show it, nothing here spends)
|
|
267
|
+
// ---------------------------------------------------------------------------
|
|
268
|
+
/**
|
|
269
|
+
* Would this working set fit one context on this model?
|
|
270
|
+
*
|
|
271
|
+
* fits — working set + fixed overhead ≤ 60% of the standard compaction point
|
|
272
|
+
* tight — ≤ 100% of it: it fits, but the session will compact soon
|
|
273
|
+
* expansive — only the expansive budget holds it
|
|
274
|
+
* split — no single context holds it; fan the work out instead
|
|
275
|
+
*
|
|
276
|
+
* Null when the model's budget is unknown (never guessed). Pass the engine's
|
|
277
|
+
* `sessionOverheadTokens(engine)`; the default is the largest estimate.
|
|
278
|
+
*/
|
|
279
|
+
export function fitVerdict(workingSetTokens, option, overheadTokens = SESSION_BASE_OVERHEAD_TOKENS) {
|
|
280
|
+
const standard = budgetFor(option, 'standard');
|
|
281
|
+
if (!standard)
|
|
282
|
+
return null;
|
|
283
|
+
const need = Math.max(0, workingSetTokens) + Math.max(0, overheadTokens);
|
|
284
|
+
const standardCap = standard.autoCompactAt ?? standard.contextWindow;
|
|
285
|
+
if (need <= standardCap * 0.6)
|
|
286
|
+
return 'fits';
|
|
287
|
+
if (need <= standardCap)
|
|
288
|
+
return 'tight';
|
|
289
|
+
const expansive = budgetFor(option, 'expansive');
|
|
290
|
+
if (expansive && need <= (expansive.autoCompactAt ?? expansive.contextWindow))
|
|
291
|
+
return 'expansive';
|
|
292
|
+
return 'split';
|
|
293
|
+
}
|
|
294
|
+
/** Compactions after which early-turn detail is mostly summaries. */
|
|
295
|
+
export const HANDOFF_COMPACTION_THRESHOLD = 2;
|
|
296
|
+
/** Below this, an expired cache is cheap to re-read and not worth a handoff. */
|
|
297
|
+
export const HANDOFF_IDLE_MIN_TOKENS = 100_000;
|
|
298
|
+
function compactTokens(n) {
|
|
299
|
+
if (n >= 1_000_000)
|
|
300
|
+
return `${(n / 1_000_000).toFixed(n % 1_000_000 === 0 ? 0 : 1)}M`;
|
|
301
|
+
if (n >= 1_000)
|
|
302
|
+
return `${Math.round(n / 1_000)}k`;
|
|
303
|
+
return String(n);
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Should the operator continue in a fresh session? Pure: `now` and
|
|
307
|
+
* `lastActivityAt` are passed in, so the verdict is reproducible in tests.
|
|
308
|
+
*/
|
|
309
|
+
export function handoffAdvice(input) {
|
|
310
|
+
const occ = occupancy(input.usage);
|
|
311
|
+
const reasons = [];
|
|
312
|
+
let level = 'none';
|
|
313
|
+
const bump = (to) => {
|
|
314
|
+
// (level only ever rises)
|
|
315
|
+
if (to === 'urge' || (to === 'suggest' && level === 'none'))
|
|
316
|
+
level = to;
|
|
317
|
+
};
|
|
318
|
+
if (!occ.exact) {
|
|
319
|
+
// An upper bound cannot justify "you are near compaction"; wait for a reading.
|
|
320
|
+
}
|
|
321
|
+
else if (occ.tone === 'over') {
|
|
322
|
+
bump('urge');
|
|
323
|
+
reasons.push(`Context (${compactTokens(occ.tokens)}) is past the ${compactTokens(occ.window ?? 0)} window.`);
|
|
324
|
+
}
|
|
325
|
+
else if (occ.ofCompaction !== null && occ.ofCompaction >= OCCUPANCY_WARN) {
|
|
326
|
+
bump('suggest');
|
|
327
|
+
reasons.push(`About ${compactTokens(occ.untilCompaction ?? 0)} tokens left before the CLI auto-compacts.`);
|
|
328
|
+
}
|
|
329
|
+
const compactions = input.compactionCount ?? 0;
|
|
330
|
+
if (compactions >= HANDOFF_COMPACTION_THRESHOLD) {
|
|
331
|
+
bump('suggest');
|
|
332
|
+
reasons.push(`Compacted ${compactions} times — early turns now survive only as summaries.`);
|
|
333
|
+
}
|
|
334
|
+
if (input.lastActivityAt) {
|
|
335
|
+
const idle = input.now - Date.parse(input.lastActivityAt);
|
|
336
|
+
if (occ.exact && Number.isFinite(idle) && idle >= CACHE_IDLE_TTL_MS && occ.tokens >= HANDOFF_IDLE_MIN_TOKENS) {
|
|
337
|
+
bump('suggest');
|
|
338
|
+
reasons.push(`Idle over an hour: the prompt cache has likely expired, so the next turn re-reads ~${compactTokens(occ.tokens)} tokens at full cost.`);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
return { level, reasons };
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* Suggest — never switch on — expansive mode. Only for a session in standard
|
|
345
|
+
* mode whose model has a real expansive budget, and only on evidence: it has
|
|
346
|
+
* already compacted repeatedly, or its working set only fits expansive.
|
|
347
|
+
*/
|
|
348
|
+
export function expansiveAdvice(input) {
|
|
349
|
+
if ((input.session.contextMode ?? 'standard') !== 'standard' || !hasExpansiveMode(input.option)) {
|
|
350
|
+
return { suggest: false, reason: null };
|
|
351
|
+
}
|
|
352
|
+
const compactions = input.session.compactionCount ?? 0;
|
|
353
|
+
if (compactions >= HANDOFF_COMPACTION_THRESHOLD) {
|
|
354
|
+
return { suggest: true, reason: `This session has compacted ${compactions} times; expansive mode keeps more of it in view.` };
|
|
355
|
+
}
|
|
356
|
+
if (typeof input.workingSetTokens === 'number' && fitVerdict(input.workingSetTokens, input.option) === 'expansive') {
|
|
357
|
+
return { suggest: true, reason: `The reachable code (~${compactTokens(input.workingSetTokens)} tokens) only fits the expansive budget.` };
|
|
358
|
+
}
|
|
359
|
+
return { suggest: false, reason: null };
|
|
360
|
+
}
|
|
361
|
+
/** Engines whose CLIs can be told a compaction budget per invocation. */
|
|
362
|
+
export function engineSupportsModes(engine) {
|
|
363
|
+
return engine === 'claude' || engine === 'codex';
|
|
364
|
+
}
|
|
365
|
+
//# sourceMappingURL=context-math.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context-math.js","sourceRoot":"","sources":["../../../src/core/verse/context-math.ts"],"names":[],"mappings":"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"}
|