refactor: codebase audit cleanup — dead code, dedup, module splits
Multi-agent audit + aggressive cleanup across server/web/coder/booterm, delivered behind a DEFER discipline so none of the in-flight files were touched. Removes dead code/deps/columns, dedups server + coder helpers, and splits the oversized modules (tools.ts, opencode-server.ts, sentinel-summaries, turn.ts, TerminalPane.tsx) behind stable contracts. Adds 78 parity/unit tests (server 587, coder 323); fixes two latent bugs (ChatPane queue keys, FileViewerOverlay blank-line parity). Intended tag: v2.7.12-audit-cleanup. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -3,6 +3,7 @@ import { join } from 'node:path';
|
||||
import type { Agent, AgentsResponse, AgentParseError } from '../types/api.js';
|
||||
import { ALL_TOOLS, resolveToolTier } from './tools.js';
|
||||
import { validateExtraArgs } from './inference/llama-args-validator.js';
|
||||
import { stripQuotes } from '../utils/string-utils.js';
|
||||
|
||||
// v1.8.1: global agents live at /data/AGENTS.md inside the container
|
||||
// (./data:/data:ro mount on the host). Per-project AGENTS.md at the project
|
||||
@@ -107,17 +108,50 @@ interface ParsedFrontmatter {
|
||||
llama_extra_args?: string[];
|
||||
}
|
||||
|
||||
function stripQuotes(s: string): string {
|
||||
if (
|
||||
s.length >= 2 &&
|
||||
(s[0] === '"' || s[0] === "'") &&
|
||||
s[0] === s[s.length - 1]
|
||||
) {
|
||||
return s.slice(1, -1);
|
||||
}
|
||||
return s;
|
||||
// P5: table-driven validation for the "soft-range" numeric frontmatter fields.
|
||||
// Each was a near-identical Number() + finite/integer + range-warn + push-error
|
||||
// block. "Soft-range" = the value is STORED whenever the type checks out; an
|
||||
// out-of-range value only emits a console.warn (it is NOT skipped). A type
|
||||
// mismatch hard-fails the block. The range descriptor in the warn message is
|
||||
// `min-max` when both bounds exist, else `(≥min)` — matching the original
|
||||
// hand-written strings byte-for-byte.
|
||||
//
|
||||
// max_tool_calls and steps are deliberately NOT in this table: they are
|
||||
// "hard-range" (store ONLY if in range; an in-type-but-out-of-range value is
|
||||
// warned AND skipped) with bespoke messages, so they stay explicit below.
|
||||
type NumericFieldKey =
|
||||
| 'temperature'
|
||||
| 'top_p'
|
||||
| 'top_k'
|
||||
| 'min_p'
|
||||
| 'presence_penalty'
|
||||
| 'top_n_sigma'
|
||||
| 'dry_multiplier'
|
||||
| 'dry_base'
|
||||
| 'dry_allowed_length'
|
||||
| 'dry_penalty_last_n';
|
||||
|
||||
interface NumericFieldSpec {
|
||||
key: NumericFieldKey;
|
||||
isInt: boolean;
|
||||
min?: number;
|
||||
max?: number;
|
||||
}
|
||||
|
||||
const NUMERIC_FIELDS: readonly NumericFieldSpec[] = [
|
||||
{ key: 'temperature', isInt: false },
|
||||
{ key: 'top_p', isInt: false, min: 0, max: 1 },
|
||||
{ key: 'top_k', isInt: true, min: 0, max: 200 },
|
||||
{ key: 'min_p', isInt: false, min: 0, max: 1 },
|
||||
{ key: 'presence_penalty', isInt: false, min: -2, max: 2 },
|
||||
// v2.6 sampling-streamjson-tokens (#11): llama.cpp sampler extensions.
|
||||
{ key: 'top_n_sigma', isInt: false, min: 0 },
|
||||
{ key: 'dry_multiplier', isInt: false, min: 0 },
|
||||
{ key: 'dry_base', isInt: false, min: 0 },
|
||||
{ key: 'dry_allowed_length', isInt: true, min: 0 },
|
||||
{ key: 'dry_penalty_last_n', isInt: true, min: -1 },
|
||||
];
|
||||
|
||||
function parseFrontmatter(yaml: string): { data: ParsedFrontmatter; errors: string[] } {
|
||||
const data: ParsedFrontmatter = {};
|
||||
const errors: string[] = [];
|
||||
@@ -140,108 +174,33 @@ function parseFrontmatter(yaml: string): { data: ParsedFrontmatter; errors: stri
|
||||
const key = line.slice(0, colonIdx).trim();
|
||||
const valueRaw = line.slice(colonIdx + 1).trim();
|
||||
|
||||
if (key === 'temperature') {
|
||||
const numericSpec = NUMERIC_FIELDS.find((f) => f.key === key);
|
||||
if (numericSpec) {
|
||||
const n = Number(valueRaw);
|
||||
if (Number.isFinite(n)) data.temperature = n;
|
||||
else errors.push(`temperature must be a number (got "${valueRaw}")`);
|
||||
} else if (key === 'top_p') {
|
||||
const n = Number(valueRaw);
|
||||
if (Number.isFinite(n)) {
|
||||
data.top_p = n;
|
||||
if (n < 0 || n > 1) {
|
||||
console.warn(`agents: top_p ${n} out of range 0-1, ignoring (falling back to default)`);
|
||||
const typeOk = numericSpec.isInt ? Number.isInteger(n) : Number.isFinite(n);
|
||||
if (typeOk) {
|
||||
// Soft-range: store regardless of range; out-of-range only warns.
|
||||
data[numericSpec.key] = n;
|
||||
const below = numericSpec.min !== undefined && n < numericSpec.min;
|
||||
const above = numericSpec.max !== undefined && n > numericSpec.max;
|
||||
if (below || above) {
|
||||
const range =
|
||||
numericSpec.max !== undefined
|
||||
? `${numericSpec.min}-${numericSpec.max}`
|
||||
: `(≥${numericSpec.min})`;
|
||||
console.warn(
|
||||
`agents: ${numericSpec.key} ${n} out of range ${range}, ignoring (falling back to default)`,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
errors.push(`top_p must be a number (got "${valueRaw}")`);
|
||||
errors.push(
|
||||
`${numericSpec.key} must be ${numericSpec.isInt ? 'an integer' : 'a number'} (got "${valueRaw}")`,
|
||||
);
|
||||
}
|
||||
} else if (key === 'top_k') {
|
||||
const n = Number(valueRaw);
|
||||
if (Number.isInteger(n)) {
|
||||
data.top_k = n;
|
||||
if (n < 0 || n > 200) {
|
||||
console.warn(`agents: top_k ${n} out of range 0-200, ignoring (falling back to default)`);
|
||||
}
|
||||
} else {
|
||||
errors.push(`top_k must be an integer (got "${valueRaw}")`);
|
||||
}
|
||||
} else if (key === 'min_p') {
|
||||
const n = Number(valueRaw);
|
||||
if (Number.isFinite(n)) {
|
||||
data.min_p = n;
|
||||
if (n < 0 || n > 1) {
|
||||
console.warn(`agents: min_p ${n} out of range 0-1, ignoring (falling back to default)`);
|
||||
}
|
||||
} else {
|
||||
errors.push(`min_p must be a number (got "${valueRaw}")`);
|
||||
}
|
||||
} else if (key === 'presence_penalty') {
|
||||
const n = Number(valueRaw);
|
||||
if (Number.isFinite(n)) {
|
||||
data.presence_penalty = n;
|
||||
if (n < -2 || n > 2) {
|
||||
console.warn(`agents: presence_penalty ${n} out of range -2-2, ignoring (falling back to default)`);
|
||||
}
|
||||
} else {
|
||||
errors.push(`presence_penalty must be a number (got "${valueRaw}")`);
|
||||
}
|
||||
} else if (key === 'top_n_sigma') {
|
||||
// v2.6 #11: llama.cpp top-n-sigma sampler. Float ≥ 0 (typical 0-3).
|
||||
// Mirrors top_p/min_p: store then warn on out-of-range (non-numeric
|
||||
// hard-fails the block).
|
||||
const n = Number(valueRaw);
|
||||
if (Number.isFinite(n)) {
|
||||
data.top_n_sigma = n;
|
||||
if (n < 0) {
|
||||
console.warn(`agents: top_n_sigma ${n} out of range (≥0), ignoring (falling back to default)`);
|
||||
}
|
||||
} else {
|
||||
errors.push(`top_n_sigma must be a number (got "${valueRaw}")`);
|
||||
}
|
||||
} else if (key === 'dry_multiplier') {
|
||||
// v2.6 #11: DRY repetition-penalty multiplier. Float ≥ 0 (0 disables DRY).
|
||||
const n = Number(valueRaw);
|
||||
if (Number.isFinite(n)) {
|
||||
data.dry_multiplier = n;
|
||||
if (n < 0) {
|
||||
console.warn(`agents: dry_multiplier ${n} out of range (≥0), ignoring (falling back to default)`);
|
||||
}
|
||||
} else {
|
||||
errors.push(`dry_multiplier must be a number (got "${valueRaw}")`);
|
||||
}
|
||||
} else if (key === 'dry_base') {
|
||||
// v2.6 #11: DRY penalty growth base. Float ≥ 0.
|
||||
const n = Number(valueRaw);
|
||||
if (Number.isFinite(n)) {
|
||||
data.dry_base = n;
|
||||
if (n < 0) {
|
||||
console.warn(`agents: dry_base ${n} out of range (≥0), ignoring (falling back to default)`);
|
||||
}
|
||||
} else {
|
||||
errors.push(`dry_base must be a number (got "${valueRaw}")`);
|
||||
}
|
||||
} else if (key === 'dry_allowed_length') {
|
||||
// v2.6 #11: DRY max sequence length not penalized. Integer ≥ 0.
|
||||
const n = Number(valueRaw);
|
||||
if (Number.isInteger(n)) {
|
||||
data.dry_allowed_length = n;
|
||||
if (n < 0) {
|
||||
console.warn(`agents: dry_allowed_length ${n} out of range (≥0), ignoring (falling back to default)`);
|
||||
}
|
||||
} else {
|
||||
errors.push(`dry_allowed_length must be an integer (got "${valueRaw}")`);
|
||||
}
|
||||
} else if (key === 'dry_penalty_last_n') {
|
||||
// v2.6 #11: DRY lookback window. Integer ≥ -1 (-1 = whole context, 0 = off).
|
||||
const n = Number(valueRaw);
|
||||
if (Number.isInteger(n)) {
|
||||
data.dry_penalty_last_n = n;
|
||||
if (n < -1) {
|
||||
console.warn(`agents: dry_penalty_last_n ${n} out of range (≥-1), ignoring (falling back to default)`);
|
||||
}
|
||||
} else {
|
||||
errors.push(`dry_penalty_last_n must be an integer (got "${valueRaw}")`);
|
||||
}
|
||||
} else if (key === 'tools') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (key === 'tools') {
|
||||
if (valueRaw === '') {
|
||||
data.tools = [];
|
||||
arrayKey = 'tools';
|
||||
@@ -478,14 +437,6 @@ interface CacheEntry {
|
||||
// corresponding mtime so the next read sees a miss without a watcher.
|
||||
const cache = new Map<string, CacheEntry>();
|
||||
|
||||
export function invalidateAgentsCache(projectPath?: string): void {
|
||||
if (projectPath === undefined) {
|
||||
cache.clear();
|
||||
} else {
|
||||
cache.delete(projectPath);
|
||||
}
|
||||
}
|
||||
|
||||
// v1.13.8: cache-read accessor for the system-prompt prefix-fingerprint log.
|
||||
// Returns the AGENTS.md mtimes that getAgentsForProject() observed on its
|
||||
// last cache fill for this projectPath. Both fields are null when the cache
|
||||
|
||||
Reference in New Issue
Block a user