fix: tighten token optimization edge cases
Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
@@ -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,
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
Reference in New Issue
Block a user