From 27e79f76b31c7e62849bba1a4b60ab68bfff738c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?T=C3=82CHES?= Date: Tue, 17 Mar 2026 18:45:43 -0600 Subject: [PATCH] refactor: centralize magic numbers into constants.ts (#1044) Extracts 11 hardcoded timeout, retry, compaction, and tool-default values from 9 source files into a single constants.ts module. Each source file now imports from the central definition, eliminating duplicated literals and making tuning a single-file change. Co-authored-by: Claude Opus 4.6 (1M context) --- .../pi-coding-agent/src/core/auth-storage.ts | 3 +- .../core/compaction/branch-summarization.ts | 3 +- .../src/core/compaction/compaction.ts | 5 +- .../src/core/compaction/utils.ts | 4 +- .../pi-coding-agent/src/core/constants.ts | 59 +++++++++++++++++++ .../pi-coding-agent/src/core/lsp/lspmux.ts | 7 +-- .../src/core/resolve-config-value.ts | 3 +- .../src/core/settings-manager.ts | 16 +++-- .../pi-coding-agent/src/core/tools/find.ts | 3 +- .../src/core/tools/truncate.ts | 4 +- 10 files changed, 89 insertions(+), 18 deletions(-) create mode 100644 packages/pi-coding-agent/src/core/constants.ts diff --git a/packages/pi-coding-agent/src/core/auth-storage.ts b/packages/pi-coding-agent/src/core/auth-storage.ts index 6028b085c..5ebffc12a 100644 --- a/packages/pi-coding-agent/src/core/auth-storage.ts +++ b/packages/pi-coding-agent/src/core/auth-storage.ts @@ -20,6 +20,7 @@ import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "f import { dirname, join } from "path"; import lockfile from "proper-lockfile"; import { getAgentDir } from "../config.js"; +import { AUTH_LOCK_STALE_MS } from "./constants.js"; import { resolveConfigValue } from "./resolve-config-value.js"; export type ApiKeyCredential = { @@ -136,7 +137,7 @@ export class FileAuthStorageBackend implements AuthStorageBackend { maxTimeout: 10000, randomize: true, }, - stale: 30000, + stale: AUTH_LOCK_STALE_MS, onCompromised: (err) => { lockCompromised = true; lockCompromisedError = err; diff --git a/packages/pi-coding-agent/src/core/compaction/branch-summarization.ts b/packages/pi-coding-agent/src/core/compaction/branch-summarization.ts index ce2077ac0..fa26df91f 100644 --- a/packages/pi-coding-agent/src/core/compaction/branch-summarization.ts +++ b/packages/pi-coding-agent/src/core/compaction/branch-summarization.ts @@ -8,6 +8,7 @@ import type { AgentMessage } from "@gsd/pi-agent-core"; import type { Model } from "@gsd/pi-ai"; import { completeSimple } from "@gsd/pi-ai"; +import { COMPACTION_RESERVE_TOKENS } from "../constants.js"; import { convertToLlm, createBranchSummaryMessage, @@ -281,7 +282,7 @@ export async function generateBranchSummary( entries: SessionEntry[], options: GenerateBranchSummaryOptions, ): Promise { - const { model, apiKey, signal, customInstructions, replaceInstructions, reserveTokens = 16384 } = options; + const { model, apiKey, signal, customInstructions, replaceInstructions, reserveTokens = COMPACTION_RESERVE_TOKENS } = options; // Token budget = context window minus reserved space for prompt + response const contextWindow = model.contextWindow || 128000; diff --git a/packages/pi-coding-agent/src/core/compaction/compaction.ts b/packages/pi-coding-agent/src/core/compaction/compaction.ts index cca57276d..4494b2d56 100644 --- a/packages/pi-coding-agent/src/core/compaction/compaction.ts +++ b/packages/pi-coding-agent/src/core/compaction/compaction.ts @@ -8,6 +8,7 @@ import type { AgentMessage } from "@gsd/pi-agent-core"; import type { AssistantMessage, Model, Usage } from "@gsd/pi-ai"; import { completeSimple } from "@gsd/pi-ai"; +import { COMPACTION_KEEP_RECENT_TOKENS, COMPACTION_RESERVE_TOKENS } from "../constants.js"; import { convertToLlm, createBranchSummaryMessage, @@ -113,8 +114,8 @@ export interface CompactionSettings { export const DEFAULT_COMPACTION_SETTINGS: CompactionSettings = { enabled: true, - reserveTokens: 16384, - keepRecentTokens: 20000, + reserveTokens: COMPACTION_RESERVE_TOKENS, + keepRecentTokens: COMPACTION_KEEP_RECENT_TOKENS, }; // ============================================================================ diff --git a/packages/pi-coding-agent/src/core/compaction/utils.ts b/packages/pi-coding-agent/src/core/compaction/utils.ts index 45fd347e5..e609fbde4 100644 --- a/packages/pi-coding-agent/src/core/compaction/utils.ts +++ b/packages/pi-coding-agent/src/core/compaction/utils.ts @@ -4,6 +4,7 @@ import type { AgentMessage } from "@gsd/pi-agent-core"; import type { Message } from "@gsd/pi-ai"; +import { TOOL_RESULT_MAX_CHARS } from "../constants.js"; // ============================================================================ // File Operation Tracking @@ -85,8 +86,7 @@ export function formatFileOperations(readFiles: string[], modifiedFiles: string[ // Message Serialization // ============================================================================ -/** Maximum characters for a tool result in serialized summaries. */ -const TOOL_RESULT_MAX_CHARS = 2000; +// TOOL_RESULT_MAX_CHARS imported from ../constants.js /** * Truncate text to a maximum character length for summarization. diff --git a/packages/pi-coding-agent/src/core/constants.ts b/packages/pi-coding-agent/src/core/constants.ts new file mode 100644 index 000000000..e24ba23b1 --- /dev/null +++ b/packages/pi-coding-agent/src/core/constants.ts @@ -0,0 +1,59 @@ +/** + * Centralized configuration constants for the coding agent. + * + * Values grouped by subsystem. Each constant documents where it is consumed + * so that changes can be audited in one place. + */ + +// ============================================================================= +// Timeouts +// ============================================================================= + +/** Shell command execution timeout used by resolve-config-value. */ +export const COMMAND_EXECUTION_TIMEOUT_MS = 10_000; + +/** LSP server liveness check timeout (lspmux). */ +export const LSP_LIVENESS_TIMEOUT_MS = 1_000; + +/** Staleness threshold for the async auth-storage file lock. */ +export const AUTH_LOCK_STALE_MS = 30_000; + +// ============================================================================= +// Caches +// ============================================================================= + +/** TTL for the cached lspmux state detection result. */ +export const LSP_STATE_CACHE_TTL_MS = 5 * 60 * 1_000; + +// ============================================================================= +// Compaction & Summarization +// ============================================================================= + +/** Tokens reserved for the LLM prompt + response during compaction and branch summarization. */ +export const COMPACTION_RESERVE_TOKENS = 16_384; + +/** Tokens from the tail of the conversation kept verbatim after compaction. */ +export const COMPACTION_KEEP_RECENT_TOKENS = 20_000; + +/** Max characters kept per tool-result block when serializing for summarization. */ +export const TOOL_RESULT_MAX_CHARS = 2_000; + +// ============================================================================= +// Retry +// ============================================================================= + +/** Base delay for exponential back-off retries (2 s, 4 s, 8 s ...). */ +export const RETRY_BASE_DELAY_MS = 2_000; + +/** Maximum server-requested delay before the retry loop gives up. */ +export const RETRY_MAX_DELAY_MS = 300_000; + +// ============================================================================= +// Tool Defaults +// ============================================================================= + +/** Default result-count cap for the find/glob tool. */ +export const FIND_DEFAULT_LIMIT = 1_000; + +/** Default line-count cap for tool-output truncation. */ +export const TRUNCATE_DEFAULT_MAX_LINES = 2_000; diff --git a/packages/pi-coding-agent/src/core/lsp/lspmux.ts b/packages/pi-coding-agent/src/core/lsp/lspmux.ts index c70b67e31..13e5c84ec 100644 --- a/packages/pi-coding-agent/src/core/lsp/lspmux.ts +++ b/packages/pi-coding-agent/src/core/lsp/lspmux.ts @@ -2,6 +2,7 @@ import { execSync, spawn } from "node:child_process"; import * as fsPromises from "node:fs/promises"; import * as os from "node:os"; import * as path from "node:path"; +import { LSP_LIVENESS_TIMEOUT_MS, LSP_STATE_CACHE_TTL_MS } from "../constants.js"; /** * lspmux integration for LSP server multiplexing. @@ -41,8 +42,6 @@ const DEFAULT_SUPPORTED_SERVERS = new Set([ "rust-analyzer", ]); -const LIVENESS_TIMEOUT_MS = 1000; -const STATE_CACHE_TTL_MS = 5 * 60 * 1000; // ============================================================================= // Helpers @@ -108,7 +107,7 @@ async function checkServerRunning(binaryPath: string): Promise { new Promise((resolve) => { proc.on("exit", (code: number | null) => resolve(code ?? 1)); }), - new Promise(resolve => setTimeout(() => resolve(null), LIVENESS_TIMEOUT_MS)), + new Promise(resolve => setTimeout(() => resolve(null), LSP_LIVENESS_TIMEOUT_MS)), ]); if (exited === null) { @@ -124,7 +123,7 @@ async function checkServerRunning(binaryPath: string): Promise { export async function detectLspmux(): Promise { const now = Date.now(); - if (cachedState && now - cacheTimestamp < STATE_CACHE_TTL_MS) { + if (cachedState && now - cacheTimestamp < LSP_STATE_CACHE_TTL_MS) { return cachedState; } diff --git a/packages/pi-coding-agent/src/core/resolve-config-value.ts b/packages/pi-coding-agent/src/core/resolve-config-value.ts index dd65cc2b0..e12c4c2ae 100644 --- a/packages/pi-coding-agent/src/core/resolve-config-value.ts +++ b/packages/pi-coding-agent/src/core/resolve-config-value.ts @@ -4,6 +4,7 @@ */ import { execFileSync } from "child_process"; +import { COMMAND_EXECUTION_TIMEOUT_MS } from "./constants.js"; const SHELL_OPERATORS = /[;|&`$><]/; @@ -60,7 +61,7 @@ function executeCommand(commandConfig: string): string | undefined { try { const output = execFileSync(firstToken, tokens.slice(1), { encoding: "utf-8", - timeout: 10000, + timeout: COMMAND_EXECUTION_TIMEOUT_MS, stdio: ["ignore", "pipe", "ignore"], }); result = output.trim() || undefined; diff --git a/packages/pi-coding-agent/src/core/settings-manager.ts b/packages/pi-coding-agent/src/core/settings-manager.ts index 8000e2a61..d9ae1c2ca 100644 --- a/packages/pi-coding-agent/src/core/settings-manager.ts +++ b/packages/pi-coding-agent/src/core/settings-manager.ts @@ -3,6 +3,12 @@ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs"; import { dirname, join } from "path"; import lockfile from "proper-lockfile"; import { CONFIG_DIR_NAME, getAgentDir } from "../config.js"; +import { + COMPACTION_KEEP_RECENT_TOKENS, + COMPACTION_RESERVE_TOKENS, + RETRY_BASE_DELAY_MS, + RETRY_MAX_DELAY_MS, +} from "./constants.js"; import type { BashInterceptorRule } from "./tools/bash-interceptor.js"; export interface CompactionSettings { @@ -710,11 +716,11 @@ export class SettingsManager { } getCompactionReserveTokens(): number { - return this.settings.compaction?.reserveTokens ?? 16384; + return this.settings.compaction?.reserveTokens ?? COMPACTION_RESERVE_TOKENS; } getCompactionKeepRecentTokens(): number { - return this.settings.compaction?.keepRecentTokens ?? 20000; + return this.settings.compaction?.keepRecentTokens ?? COMPACTION_KEEP_RECENT_TOKENS; } getCompactionSettings(): { enabled: boolean; reserveTokens: number; keepRecentTokens: number } { @@ -727,7 +733,7 @@ export class SettingsManager { getBranchSummarySettings(): { reserveTokens: number; skipPrompt: boolean } { return { - reserveTokens: this.settings.branchSummary?.reserveTokens ?? 16384, + reserveTokens: this.settings.branchSummary?.reserveTokens ?? COMPACTION_RESERVE_TOKENS, skipPrompt: this.settings.branchSummary?.skipPrompt ?? false, }; } @@ -753,8 +759,8 @@ export class SettingsManager { return { enabled: this.getRetryEnabled(), maxRetries: this.settings.retry?.maxRetries ?? 3, - baseDelayMs: this.settings.retry?.baseDelayMs ?? 2000, - maxDelayMs: this.settings.retry?.maxDelayMs ?? 300000, + baseDelayMs: this.settings.retry?.baseDelayMs ?? RETRY_BASE_DELAY_MS, + maxDelayMs: this.settings.retry?.maxDelayMs ?? RETRY_MAX_DELAY_MS, }; } diff --git a/packages/pi-coding-agent/src/core/tools/find.ts b/packages/pi-coding-agent/src/core/tools/find.ts index 8e21cee9d..fc4d6be40 100644 --- a/packages/pi-coding-agent/src/core/tools/find.ts +++ b/packages/pi-coding-agent/src/core/tools/find.ts @@ -3,6 +3,7 @@ import { glob as nativeGlob } from "@gsd/native/glob"; import { type Static, Type } from "@sinclair/typebox"; import { existsSync } from "fs"; import path from "path"; +import { FIND_DEFAULT_LIMIT } from "../constants.js"; import { resolveToCwd } from "./path-utils.js"; import { DEFAULT_MAX_BYTES, formatSize, type TruncationResult, truncateHead } from "./truncate.js"; @@ -16,7 +17,7 @@ const findSchema = Type.Object({ export type FindToolInput = Static; -const DEFAULT_LIMIT = 1000; +const DEFAULT_LIMIT = FIND_DEFAULT_LIMIT; export interface FindToolDetails { truncation?: TruncationResult; diff --git a/packages/pi-coding-agent/src/core/tools/truncate.ts b/packages/pi-coding-agent/src/core/tools/truncate.ts index 18ac5d74d..989e5d2f0 100644 --- a/packages/pi-coding-agent/src/core/tools/truncate.ts +++ b/packages/pi-coding-agent/src/core/tools/truncate.ts @@ -8,7 +8,9 @@ * Never returns partial lines (except bash tail truncation edge case). */ -export const DEFAULT_MAX_LINES = 2000; +import { TRUNCATE_DEFAULT_MAX_LINES } from "../constants.js"; + +export const DEFAULT_MAX_LINES = TRUNCATE_DEFAULT_MAX_LINES; export const DEFAULT_MAX_BYTES = 50 * 1024; // 50KB export const GREP_MAX_LINE_LENGTH = 500; // Max chars per grep match line