fix(server): empty CLAUDE_MAX_PROMPT_CHARS falls back to derived default (PR #179 review)

Reviewer regression: `!= null` treated an EMPTY env value ("CLAUDE_MAX_PROMPT_CHARS="
in an EnvironmentFile/.env) as explicit -> parseInt("") = NaN -> guard disabled + a
false "[System] Note: 0 older messages were truncated" injected into every prompt.
Extracted resolvePromptCharBudget() (truthiness contract, matching the old
`parseInt(env || default)` behavior) into lib/prompt.mjs so the semantics are
mutation-tested: switching back to != null fails the empty-string test. +2 tests, 349/0.

Co-Authored-By: Claude <claude-opus-4-8> <noreply@anthropic.com>
This commit is contained in:
2026-07-18 10:11:29 +10:00
co-authored by Claude <claude-opus-4-8> <noreply@anthropic.com>
parent 8405e30d0e
commit e912903ba5
3 changed files with 29 additions and 5 deletions
+2 -4
View File
@@ -49,7 +49,7 @@ import { TuiSemaphore, SemaphoreAbortError, recordTuiEntrypoint, buildTuiHealthB
import { TuiPanePool, resolvePoolSize, POOL_MAX_SIZE } from "./lib/tui/pool.mjs";
import { TuiDeltaAssembler, DEFAULT_HOLDBACK_CHARS, resolveStreamHoldback } from "./lib/tui/stream.mjs";
import { createSerialMutex, createTtlCache, isTokenExpiring, orderLabelsLastGoodFirst } from "./lib/spawn-auth.mjs";
import { appendOperatorPrompt, derivePromptCharBudget } from "./lib/prompt.mjs";
import { appendOperatorPrompt, resolvePromptCharBudget } from "./lib/prompt.mjs";
const __dirname = dirname(fileURLToPath(import.meta.url));
const _pkg = JSON.parse(readFileSync(join(__dirname, "package.json"), "utf8"));
@@ -1154,9 +1154,7 @@ function buildCliArgs(cliModel, systemPrompt) {
// English tokens), which silently under-delivered the advertised window by ~5×. The env
// var (and the runtime settings API below) remain absolute operator overrides. If
// models.json ever advertises a bigger window, this budget follows automatically.
let MAX_PROMPT_CHARS = process.env.CLAUDE_MAX_PROMPT_CHARS != null
? parseInt(process.env.CLAUDE_MAX_PROMPT_CHARS, 10)
: derivePromptCharBudget(modelsConfig.models);
let MAX_PROMPT_CHARS = resolvePromptCharBudget(process.env.CLAUDE_MAX_PROMPT_CHARS, modelsConfig.models);
// Flatten OpenAI content (string | array of parts) to plain text for the prompt.
// Array content: concatenate text parts; replace non-text parts (e.g. image_url)