chore: some more improvements to try and get as close to original as possible

This commit is contained in:
2026-05-31 16:36:22 -05:00
parent 37d15a338d
commit 11dc00b2b8
3 changed files with 130 additions and 11 deletions
+30 -1
View File
@@ -69,6 +69,32 @@ async function callMcpTool(
} }
} }
/**
* When context approaches the limit, truncate the content of old tool-result
* messages to free space. Keeps the most recent N tool results intact so the
* model still has fresh context; replaces earlier ones with a size notice.
* Never touches system/user/assistant messages — only tool messages.
*/
function pruneToolMessages(messages: Message[], keepRecent = 6): Message[] {
const toolIndices: number[] = [];
for (let i = 0; i < messages.length; i++) {
if (messages[i].role === "tool") toolIndices.push(i);
}
const pruneCount = Math.max(0, toolIndices.length - keepRecent);
if (pruneCount === 0) return messages;
const toPrune = new Set(toolIndices.slice(0, pruneCount));
let pruned = 0;
const result = messages.map((msg, i) => {
if (!toPrune.has(i)) return msg;
const originalLen = typeof msg.content === "string" ? msg.content.length : 0;
pruned++;
return { ...msg, content: `[pruned: was ${originalLen} chars — context limit approached]` };
});
log.info(`» pruned ${pruned} old tool message(s) to reduce context`);
return result;
}
async function runOllamaLoop(ctx: AgentRunContext): Promise<AgentResult> { async function runOllamaLoop(ctx: AgentRunContext): Promise<AgentResult> {
const ollamaHost = process.env.OLLAMA_HOST ?? ""; const ollamaHost = process.env.OLLAMA_HOST ?? "";
if (!ollamaHost) { if (!ollamaHost) {
@@ -89,7 +115,7 @@ async function runOllamaLoop(ctx: AgentRunContext): Promise<AgentResult> {
const tools = await getOllamaTools(mcpClient); const tools = await getOllamaTools(mcpClient);
log.info(`» ${tools.length} tools available`); log.info(`» ${tools.length} tools available`);
const messages: Message[] = [ let messages: Message[] = [
{ {
role: "user", role: "user",
content: ctx.instructions.full, content: ctx.instructions.full,
@@ -149,6 +175,9 @@ async function runOllamaLoop(ctx: AgentRunContext): Promise<AgentResult> {
log.info( log.info(
` context: ${promptTokens} prompt + ${evalTokens ?? 0} eval = ${total} tokens (${pct}% of limit)`, ` context: ${promptTokens} prompt + ${evalTokens ?? 0} eval = ${total} tokens (${pct}% of limit)`,
); );
if (promptTokens > 200_000) {
messages = pruneToolMessages(messages);
}
} }
const assistantMessage = response.message; const assistantMessage = response.message;
+88 -9
View File
@@ -181,7 +181,20 @@ export function formatDroppedCommentsNote(dropped: DroppedComment[]): string {
export const CreatePullRequestReview = type({ export const CreatePullRequestReview = type({
pull_number: type.number.describe("The pull request number to review"), pull_number: type.number.describe("The pull request number to review"),
body: type.string.describe("1-2 sentence high-level summary").optional(), preamble: type.string.describe(
"One sentence describing what was reviewed (e.g. 'This PR adds device management behind a feature flag'). " +
"The server prepends '**Reviewed changes** — ' automatically. " +
"When provided, do NOT repeat the preamble inside 'body'."
).optional(),
changes: type.string.array().describe(
"Bullet list of substantive changes — neutral descriptions of what the PR added/changed/removed. " +
"Each entry is one formatted bullet, e.g. '**Feature X** — 1 sentence description'. " +
"These must describe CHANGES only, not findings or issues."
).optional(),
body: type.string.describe(
"When 'preamble'/'changes' are used: include ONLY the metadata HTML comment and any non-anchored ### sections. " +
"When used alone (legacy): full review body including preamble."
).optional(),
approved: type.boolean.describe("Set to true to submit as an approval.").optional(), approved: type.boolean.describe("Set to true to submit as an approval.").optional(),
commit_id: type.string.describe("Optional SHA of the commit being reviewed.").optional(), commit_id: type.string.describe("Optional SHA of the commit being reviewed.").optional(),
comments: type({ comments: type({
@@ -200,25 +213,86 @@ export const CreatePullRequestReview = type({
.optional(), .optional(),
}); });
/** Assemble the **Reviewed changes** preamble block from structured params. */
function assemblePreamble(preamble: string, changes: string[]): string {
const lines = [`**Reviewed changes** — ${preamble}`];
if (changes.length > 0) {
lines.push("");
for (const change of changes) {
lines.push(change.startsWith("- ") ? change : `- ${change}`);
}
}
return lines.join("\n");
}
/**
* Strip body ### sections whose file paths are already covered by inline
* comments. Prevents the model from duplicating inline findings in the body.
*/
function postProcessBody(
body: string,
validComments: ReviewCommentInput[],
): string {
if (validComments.length === 0) return body;
const commentedPaths = new Set(validComments.map((c) => c.path));
const sectionRegex = /^### .+$/gm;
const matches: Array<{ index: number; heading: string }> = [];
let m: RegExpExecArray | null;
while ((m = sectionRegex.exec(body)) !== null) {
matches.push({ index: m.index, heading: m[0] });
}
if (matches.length === 0) return body;
const sections = matches.map((match, i) => ({
start: match.index,
end: matches[i + 1]?.index ?? body.length,
heading: match.heading,
}));
const toRemove: Array<{ start: number; end: number }> = [];
for (const section of sections) {
const content = body.slice(section.start, section.end);
for (const path of commentedPaths) {
if (content.includes(path)) {
toRemove.push({ start: section.start, end: section.end });
log.info(`stripped duplicate body section "${section.heading.slice(0, 80)}" — already covered by inline comment on ${path}`);
break;
}
}
}
if (toRemove.length === 0) return body;
let result = body;
for (const { start, end } of [...toRemove].reverse()) {
result = result.slice(0, start) + result.slice(end);
}
return result.replace(/\n{3,}/g, "\n\n").trim();
}
export function CreatePullRequestReviewTool(ctx: ToolContext) { export function CreatePullRequestReviewTool(ctx: ToolContext) {
return tool({ return tool({
name: "create_pull_request_review", name: "create_pull_request_review",
description: description:
"Submit a review for an existing pull request. " + "Submit a review for an existing pull request. " +
"Reviews with no body AND no comments are silently skipped. " + "PREFERRED: use 'preamble' + 'changes' for the reviewed-changes block, and 'body' for ONLY the metadata HTML comment and non-anchored ### sections. " +
"IMPORTANT: 95%+ of feedback should be in 'comments' with file paths and line numbers. " + "IMPORTANT: 95%+ of feedback must be in 'comments' with file paths and line numbers — not in 'body'. " +
"Only use 'body' for a 1-2 sentence summary. " +
"For every actionable issue, the comment 'body' should explain the problem AND include a markdown " +
"code block showing the corrected code. For example: " +
"'SQL injection: id is interpolated directly into the query string.\\n\\n```ts\\nconst result = await db.query(\\'SELECT * FROM users WHERE id = $1\\', [id]);\\n```'. " +
"The first submission may error once with a diff-coverage nudge — retry with the same arguments. " + "The first submission may error once with a diff-coverage nudge — retry with the same arguments. " +
"Inline comments: 'path' must be the SOURCE FILE path (e.g. 'apps/foo/bar.ts') from the diff --git header, NOT the diffPath returned by checkout_pr. " + "Inline comments: 'path' must be the SOURCE FILE path (e.g. 'apps/foo/bar.ts') from the diff --git header, NOT the diffPath. " +
"'line' must be the actual file line number from the '| newLine |' column in the formatted diff (not the TOC line range). " + "'line' must be the actual file line number from the '| newLine |' column in the formatted diff (not the TOC line range). " +
"Inline comments can ONLY target files and lines that appear in the PR diff.", "Inline comments can ONLY target files and lines that appear in the PR diff.",
parameters: CreatePullRequestReview, parameters: CreatePullRequestReview,
execute: execute(async ({ pull_number, body, approved, commit_id, comments = [] }) => { execute: execute(async ({ pull_number, preamble, changes, body, approved, commit_id, comments = [] }) => {
if (body) body = fixDoubleEscapedString(body); if (body) body = fixDoubleEscapedString(body);
// Assemble structured preamble if provided
if (preamble || (changes && changes.length > 0)) {
const preambleBlock = assemblePreamble(preamble ?? "", changes ?? []);
body = body ? `${preambleBlock}\n\n${body}` : preambleBlock;
}
ctx.toolState.issueNumber = pull_number; ctx.toolState.issueNumber = pull_number;
const dup = duplicateReviewDecision({ const dup = duplicateReviewDecision({
@@ -287,6 +361,11 @@ export function CreatePullRequestReviewTool(ctx: ToolContext) {
} }
} }
// Strip body ### sections that duplicate inline comments (#1 post-processing)
if (body && validComments.length > 0) {
body = postProcessBody(body, validComments);
}
if (droppedComments.length > 0) { if (droppedComments.length > 0) {
const note = formatDroppedCommentsNote(droppedComments); const note = formatDroppedCommentsNote(droppedComments);
body = body ? body + note : note.replace(/^\n\n/, ""); body = body ? body + note : note.replace(/^\n\n/, "");
+12 -1
View File
@@ -397,7 +397,18 @@ For simple, well-defined tasks, skip the plan phase and go straight to build.`,
note: the first create_pull_request_review submission may error with a one-time diff-coverage nudge listing unread TOC regions. retry the same call to proceed — optionally after reading the listed ranges. the pre-flight will not block again this session. note: the first create_pull_request_review submission may error with a one-time diff-coverage nudge listing unread TOC regions. retry the same call to proceed — optionally after reading the listed ranges. the pre-flight will not block again this session.
The review body is structured as: \`[optional alert blockquote]\`\`[PR summary using the default format below]\`. Inline comments are passed via the \`comments\` parameter, not in the body. **Structured submission (preferred)**: use \`preamble\` + \`changes\` for the reviewed-changes block instead of writing it in \`body\`. Pass \`body\` for ONLY the metadata HTML comment and non-anchored \`### \` sections (if any). The server assembles the full preamble block for you. Example call shape:
\`\`\`
create_pull_request_review({
pull_number: N,
preamble: "one sentence on what the PR does",
changes: ["**Feature X** — description", "**Migration Y** — description"],
body: "<!-- shockbot review metadata ... -->\\n\\n### ⚠️ Non-anchored concern...\\n\\n### ️ Nitpicks\\n...",
comments: [{ path: "src/foo.ts", line: 42, body: "..." }, ...],
approved: false,
})
\`\`\`
Inline comments are passed via the \`comments\` parameter, not in the body.
The opening callout is what the author sees first — pick the one that matches what you want them to do. Five tiers, from loudest to friendliest: The opening callout is what the author sees first — pick the one that matches what you want them to do. Five tiers, from loudest to friendliest: