fix: tighten token optimization edge cases

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
Dotta
2026-03-13 10:18:00 -05:00
parent 7d1748b3a7
commit d51c4b1a4c
3 changed files with 43 additions and 6 deletions

View File

@@ -28,6 +28,8 @@ import { assertCompanyAccess, getActorInfo } from "./authz.js";
import { shouldWakeAssigneeOnCheckout } from "./issues-checkout-wakeup.js"; import { shouldWakeAssigneeOnCheckout } from "./issues-checkout-wakeup.js";
import { isAllowedContentType, MAX_ATTACHMENT_BYTES } from "../attachment-types.js"; import { isAllowedContentType, MAX_ATTACHMENT_BYTES } from "../attachment-types.js";
const MAX_ISSUE_COMMENT_LIMIT = 500;
export function issueRoutes(db: Db, storage: StorageService) { export function issueRoutes(db: Db, storage: StorageService) {
const router = Router(); const router = Router();
const svc = issueService(db); const svc = issueService(db);
@@ -878,7 +880,10 @@ export function issueRoutes(db: Db, storage: StorageService) {
typeof req.query.limit === "string" && req.query.limit.trim().length > 0 typeof req.query.limit === "string" && req.query.limit.trim().length > 0
? Number(req.query.limit) ? Number(req.query.limit)
: null; : null;
const limit = limitRaw && Number.isFinite(limitRaw) && limitRaw > 0 ? Math.floor(limitRaw) : null; const limit =
limitRaw && Number.isFinite(limitRaw) && limitRaw > 0
? Math.min(Math.floor(limitRaw), MAX_ISSUE_COMMENT_LIMIT)
: null;
const comments = await svc.listComments(id, { const comments = await svc.listComments(id, {
afterCommentId, afterCommentId,
order, order,

View File

@@ -623,6 +623,19 @@ export function heartbeatService(db: Db) {
.then((rows) => rows[0] ?? null); .then((rows) => rows[0] ?? null);
} }
async function getOldestRunForSession(agentId: string, sessionId: string) {
return db
.select({
id: heartbeatRuns.id,
createdAt: heartbeatRuns.createdAt,
})
.from(heartbeatRuns)
.where(and(eq(heartbeatRuns.agentId, agentId), eq(heartbeatRuns.sessionIdAfter, sessionId)))
.orderBy(asc(heartbeatRuns.createdAt), asc(heartbeatRuns.id))
.limit(1)
.then((rows) => rows[0] ?? null);
}
async function resolveNormalizedUsageForSession(input: { async function resolveNormalizedUsageForSession(input: {
agentId: string; agentId: string;
runId: string; runId: string;
@@ -672,6 +685,7 @@ export function heartbeatService(db: Db) {
}; };
} }
const fetchLimit = Math.max(policy.maxSessionRuns > 0 ? policy.maxSessionRuns + 1 : 0, 4);
const runs = await db const runs = await db
.select({ .select({
id: heartbeatRuns.id, id: heartbeatRuns.id,
@@ -683,7 +697,7 @@ export function heartbeatService(db: Db) {
.from(heartbeatRuns) .from(heartbeatRuns)
.where(and(eq(heartbeatRuns.agentId, agent.id), eq(heartbeatRuns.sessionIdAfter, sessionId))) .where(and(eq(heartbeatRuns.agentId, agent.id), eq(heartbeatRuns.sessionIdAfter, sessionId)))
.orderBy(desc(heartbeatRuns.createdAt)) .orderBy(desc(heartbeatRuns.createdAt))
.limit(Math.max(policy.maxSessionRuns + 1, 4)); .limit(fetchLimit);
if (runs.length === 0) { if (runs.length === 0) {
return { return {
@@ -695,7 +709,10 @@ export function heartbeatService(db: Db) {
} }
const latestRun = runs[0] ?? null; const latestRun = runs[0] ?? null;
const oldestRun = runs[runs.length - 1] ?? latestRun; const oldestRun =
policy.maxSessionAgeHours > 0
? await getOldestRunForSession(agent.id, sessionId)
: runs[runs.length - 1] ?? latestRun;
const latestRawUsage = readRawUsageTotals(latestRun?.usageJson); const latestRawUsage = readRawUsageTotals(latestRun?.usageJson);
const sessionAgeHours = const sessionAgeHours =
latestRun && oldestRun latestRun && oldestRun

View File

@@ -27,6 +27,7 @@ import { resolveIssueGoalId, resolveNextIssueGoalId } from "./issue-goal-fallbac
import { getDefaultCompanyGoal } from "./goals.js"; import { getDefaultCompanyGoal } from "./goals.js";
const ALL_ISSUE_STATUSES = ["backlog", "todo", "in_progress", "in_review", "blocked", "done", "cancelled"]; const ALL_ISSUE_STATUSES = ["backlog", "todo", "in_progress", "in_review", "blocked", "done", "cancelled"];
const MAX_ISSUE_COMMENT_PAGE_LIMIT = 500;
function assertTransition(from: string, to: string) { function assertTransition(from: string, to: string) {
if (from === to) return; if (from === to) return;
@@ -1070,7 +1071,10 @@ export function issueService(db: Db) {
) => { ) => {
const order = opts?.order === "asc" ? "asc" : "desc"; const order = opts?.order === "asc" ? "asc" : "desc";
const afterCommentId = opts?.afterCommentId?.trim() || null; const afterCommentId = opts?.afterCommentId?.trim() || null;
const limit = opts?.limit && opts.limit > 0 ? Math.floor(opts.limit) : null; const limit =
opts?.limit && opts.limit > 0
? Math.min(Math.floor(opts.limit), MAX_ISSUE_COMMENT_PAGE_LIMIT)
: null;
const conditions = [eq(issueComments.issueId, issueId)]; const conditions = [eq(issueComments.issueId, issueId)];
if (afterCommentId) { if (afterCommentId) {
@@ -1085,7 +1089,15 @@ export function issueService(db: Db) {
if (!anchor) return []; if (!anchor) return [];
conditions.push( conditions.push(
sql<boolean>`(${issueComments.createdAt} > ${anchor.createdAt} OR (${issueComments.createdAt} = ${anchor.createdAt} AND ${issueComments.id} <> ${anchor.id}))`, order === "asc"
? sql<boolean>`(
${issueComments.createdAt} > ${anchor.createdAt}
OR (${issueComments.createdAt} = ${anchor.createdAt} AND ${issueComments.id} > ${anchor.id})
)`
: sql<boolean>`(
${issueComments.createdAt} < ${anchor.createdAt}
OR (${issueComments.createdAt} = ${anchor.createdAt} AND ${issueComments.id} < ${anchor.id})
)`,
); );
} }
@@ -1093,7 +1105,10 @@ export function issueService(db: Db) {
.select() .select()
.from(issueComments) .from(issueComments)
.where(and(...conditions)) .where(and(...conditions))
.orderBy(order === "asc" ? asc(issueComments.createdAt) : desc(issueComments.createdAt)); .orderBy(
order === "asc" ? asc(issueComments.createdAt) : desc(issueComments.createdAt),
order === "asc" ? asc(issueComments.id) : desc(issueComments.id),
);
const comments = limit ? await query.limit(limit) : await query; const comments = limit ? await query.limit(limit) : await query;
return comments.map(redactIssueComment); return comments.map(redactIssueComment);