Problem
The same "truncate a string to N chars with an ellipsis" logic is reimplemented in four places, each as a local helper or inline expression:
packages/agentctx/src/profile/detect.ts:299 — clip(body):
return body.length <= BODY_MAX_CHARS ? body : `${body.slice(0, BODY_MAX_CHARS - 1)}…`;
packages/agentctx/src/extract/ingest.ts:221 — clampTitle(text) (collapses whitespace first, then the same truncation):
return oneLine.length <= TITLE_MAX_CHARS ? oneLine : `${oneLine.slice(0, TITLE_MAX_CHARS - 1)}…`;
packages/agentctx/src/hooks/post-tool-use.ts:208 — clamp(text, max):
return text.length <= max ? text : `${text.slice(0, max - 1)}…`;
packages/agentctx/src/hooks/pre-compact.ts:33 — inlined:
lastPrompt.length > PROMPT_EXCERPT_MAX_CHARS ? `${lastPrompt.slice(0, PROMPT_EXCERPT_MAX_CHARS)}…` : lastPrompt
Note these aren't even fully consistent: three reserve a char for the ellipsis (max - 1) so the result length stays ≤ max, but the pre-compact.ts copy slices at max and then appends …, yielding max + 1 chars. (This is distinct from the token-based truncateToTokens in hooks/tokens.ts, which should stay as-is.)
What done looks like
- A single shared char-truncation helper, e.g.
truncateChars(text: string, maxChars: number): string, in a dependency-free util module.
clip, clamp, and the pre-compact.ts inline expression call it; clampTitle keeps its whitespace-collapse step and then calls it.
- All four sites produce a result of length
≤ maxChars (fixing the off-by-one in pre-compact.ts).
npm run check stays green; a small unit test covers the boundary (exact length, length+1).
Scope: a small, self-contained refactor with no architectural impact.
Problem
The same "truncate a string to N chars with an ellipsis" logic is reimplemented in four places, each as a local helper or inline expression:
packages/agentctx/src/profile/detect.ts:299—clip(body):packages/agentctx/src/extract/ingest.ts:221—clampTitle(text)(collapses whitespace first, then the same truncation):packages/agentctx/src/hooks/post-tool-use.ts:208—clamp(text, max):packages/agentctx/src/hooks/pre-compact.ts:33— inlined:Note these aren't even fully consistent: three reserve a char for the ellipsis (
max - 1) so the result length stays≤ max, but thepre-compact.tscopy slices atmaxand then appends…, yieldingmax + 1chars. (This is distinct from the token-basedtruncateToTokensinhooks/tokens.ts, which should stay as-is.)What done looks like
truncateChars(text: string, maxChars: number): string, in a dependency-free util module.clip,clamp, and thepre-compact.tsinline expression call it;clampTitlekeeps its whitespace-collapse step and then calls it.≤ maxChars(fixing the off-by-one inpre-compact.ts).npm run checkstays green; a small unit test covers the boundary (exact length, length+1).Scope: a small, self-contained refactor with no architectural impact.