From 7b575f38aadc526d9b34de866ca300b5ff7254bd Mon Sep 17 00:00:00 2001 From: Antigravity Assistant Date: Fri, 1 May 2026 09:30:21 -0300 Subject: [PATCH] chore(release): finalize v3.7.8 stabilization, fix a2a status codes and open issues --- .agents/workflows/resolve-issues.md | 18 ++++++------ CHANGELOG.md | 5 ++++ open-sse/handlers/chatCore.ts | 5 ++++ open-sse/services/usage.ts | 2 +- .../analytics/CompressionAnalyticsTab.tsx | 4 +-- .../settings/components/CacheStatsCard.tsx | 21 +++++++++----- src/app/a2a/route.ts | 4 ++- src/lib/db/compressionAnalytics.ts | 28 ++++++++++++++----- src/shared/network/safeOutboundFetch.ts | 2 +- tests/e2e/ecosystem.test.ts | 10 +++++-- 10 files changed, 69 insertions(+), 30 deletions(-) diff --git a/.agents/workflows/resolve-issues.md b/.agents/workflows/resolve-issues.md index 8573a18ce0..01f8f68879 100644 --- a/.agents/workflows/resolve-issues.md +++ b/.agents/workflows/resolve-issues.md @@ -82,7 +82,7 @@ For each bug issue, perform the full analysis: 2. **Read ALL comments** — including bot triage comments (Kilo, etc.) and owner/community responses. Pay attention to: - Whether someone already responded with a fix - Whether a community member confirmed the issue is resolved - - Whether the issue was marked as duplicate by a bot + - Whether the issue was marked as duplicate by a bot. **WARNING: DO NOT blindly trust bot duplicate labels (e.g., kilo-duplicate). Bots make mistakes. You MUST read the full conversation and do your own independent analysis to determine if it is truly a duplicate or a distinct bug.** 3. **Identify the claimed error** — extract the exact error message, status code, and provider/model involved #### 5b. Check Information Sufficiency @@ -98,14 +98,14 @@ Verify the issue contains enough to act on: For each bug, classify into one of 5 actions: -| Disposition | When to Apply | Action | -| ---------------------------- | ------------------------------------------------------------------------------------------- | ------------------------------------------------------- | -| **✅ CLOSE — Already Fixed** | Owner responded with fix + no user follow-up, OR community confirmed fix | Close with comment citing which version fixed it | -| **✅ CLOSE — Duplicate** | Bot flagged >85% similarity + user provides no new info | Close referencing the original issue | -| **✅ CLOSE — Stale** | We requested logs/info > 7 days ago with no reply | Close thanking the user, invite to reopen if needed | -| **📝 RESPOND — Needs Info** | Issue is real but missing critical reproduction details | Comment asking for specifics per `/issue-triage` | -| **📝 RESPOND — User Config** | Error is caused by unsupported env (Node version, wrong model path, missing API enablement) | Comment explaining the user-side fix | -| **🔧 FIX — Code Change** | Root cause is confirmed in the codebase | Research, propose solution in report, wait for approval | +| Disposition | When to Apply | Action | +| ---------------------------- | ---------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------- | +| **✅ CLOSE — Already Fixed** | Owner responded with fix + no user follow-up, OR community confirmed fix | Close with comment citing which version fixed it | +| **✅ CLOSE — Duplicate** | You have independently verified the issue is a duplicate (do NOT rely solely on bot flags) + user provides no new info | Close referencing the original issue | +| **✅ CLOSE — Stale** | We requested logs/info > 7 days ago with no reply | Close thanking the user, invite to reopen if needed | +| **📝 RESPOND — Needs Info** | Issue is real but missing critical reproduction details | Comment asking for specifics per `/issue-triage` | +| **📝 RESPOND — User Config** | Error is caused by unsupported env (Node version, wrong model path, missing API enablement) | Comment explaining the user-side fix | +| **🔧 FIX — Code Change** | Root cause is confirmed in the codebase | Research, propose solution in report, wait for approval | #### 5d. For "FIX — Code Change" Issues diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e17fd8912..053b22af19 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,11 @@ - **fix(ui):** hide combo compression controls when the global setting is disabled (#1840) - **fix(db):** tolerate missing request_detail_logs table for legacy deployments (#1848) +- **fix(core):** remove unneeded \`store\` payload parameter for providers lacking support (closes #1841) +- **fix(core):** ensure safeOutboundFetch and A2A routers return 503 Service Unavailable when security guardrails are triggered +- **fix(usage):** correct Unix seconds vs milliseconds parsing logic for Kiro AI quota reset (closes #1849) +- **fix(ui):** apply robust NaN handling, ensure 24h consistency, and fix missing hour slots in Compression Analytics (closes #1844) +- **fix(ui):** implement short number formatting for token consumption metrics on cache pages to prevent overflow (closes #1842) ### 🛠️ Maintenance diff --git a/open-sse/handlers/chatCore.ts b/open-sse/handlers/chatCore.ts index bbac5a2ae5..dbf58ed0cf 100644 --- a/open-sse/handlers/chatCore.ts +++ b/open-sse/handlers/chatCore.ts @@ -2219,6 +2219,11 @@ export async function handleChatCore({ } } + // OpenAI's `store` parameter is not supported by most compatible providers and breaks them + if (provider !== "openai" && "store" in translatedBody) { + delete translatedBody.store; + } + // Provider-specific max_tokens caps (#711) // Some providers reject requests when max_tokens exceeds their API limit. // Cap before sending to avoid upstream HTTP 400 errors. diff --git a/open-sse/services/usage.ts b/open-sse/services/usage.ts index 8c5ef39627..9358333a1b 100644 --- a/open-sse/services/usage.ts +++ b/open-sse/services/usage.ts @@ -652,7 +652,7 @@ function parseResetTime(resetValue) { if (resetValue instanceof Date) { date = resetValue; } else if (typeof resetValue === "number") { - date = new Date(resetValue); + date = new Date(resetValue < 1e12 ? resetValue * 1000 : resetValue); } else if (typeof resetValue === "string") { date = new Date(resetValue); } else { diff --git a/src/app/(dashboard)/dashboard/analytics/CompressionAnalyticsTab.tsx b/src/app/(dashboard)/dashboard/analytics/CompressionAnalyticsTab.tsx index 038b4afc71..184eb936db 100644 --- a/src/app/(dashboard)/dashboard/analytics/CompressionAnalyticsTab.tsx +++ b/src/app/(dashboard)/dashboard/analytics/CompressionAnalyticsTab.tsx @@ -240,7 +240,7 @@ export default function CompressionAnalyticsTab() {

show_chart - Last 24 Hours + Last 24 Hours (Activity)

{stats.last24h.map((entry, idx) => { @@ -258,7 +258,7 @@ export default function CompressionAnalyticsTab() {
- {entry.hour.substring(0, 2)} + {entry.hour.substring(11, 13)}
); diff --git a/src/app/(dashboard)/dashboard/settings/components/CacheStatsCard.tsx b/src/app/(dashboard)/dashboard/settings/components/CacheStatsCard.tsx index 4798fbba2e..de3846780a 100644 --- a/src/app/(dashboard)/dashboard/settings/components/CacheStatsCard.tsx +++ b/src/app/(dashboard)/dashboard/settings/components/CacheStatsCard.tsx @@ -36,6 +36,13 @@ interface CacheMetrics { const REFRESH_INTERVAL_MS = 10_000; const REFRESH_INTERVAL_SECONDS = REFRESH_INTERVAL_MS / 1000; +function formatNumberCompact(num: number): string { + return Intl.NumberFormat("en-US", { + notation: "compact", + maximumFractionDigits: 1, + }).format(num); +} + export default function CacheStatsCard() { const [metrics, setMetrics] = useState(null); const [resetting, setResetting] = useState(false); @@ -117,19 +124,19 @@ export default function CacheStatsCard() {

{t("inputTokens")}

- {metrics.totalInputTokens.toLocaleString()} + {formatNumberCompact(metrics.totalInputTokens)}

{t("cachedTokensRead")}

- {metrics.totalCachedTokens.toLocaleString()} + {formatNumberCompact(metrics.totalCachedTokens)}

{t("cacheCreationWrite")}

- {metrics.totalCacheCreationTokens.toLocaleString()} + {formatNumberCompact(metrics.totalCacheCreationTokens)}

@@ -157,7 +164,7 @@ export default function CacheStatsCard() {

{t("tokensSaved")}

- {metrics.tokensSaved.toLocaleString()} + {formatNumberCompact(metrics.tokensSaved)}

@@ -189,13 +196,13 @@ export default function CacheStatsCard() {
- {t("inputShort")}: {stats.inputTokens.toLocaleString()} + {t("inputShort")}: {formatNumberCompact(stats.inputTokens)} - {t("cachedShort")}: {stats.cachedTokens.toLocaleString()} + {t("cachedShort")}: {formatNumberCompact(stats.cachedTokens)} - {t("writeShort")}: {stats.cacheCreationTokens.toLocaleString()} + {t("writeShort")}: {formatNumberCompact(stats.cacheCreationTokens)} {providerCacheRate.toFixed(0)}% diff --git a/src/app/a2a/route.ts b/src/app/a2a/route.ts index 109fd53885..b9c6ea369d 100644 --- a/src/app/a2a/route.ts +++ b/src/app/a2a/route.ts @@ -79,7 +79,7 @@ function authenticate(req: NextRequest): boolean { function jsonRpcError(id: string | number | null, code: number, message: string, data?: unknown) { return NextResponse.json( { jsonrpc: "2.0", id, error: { code, message, data } }, - { status: code === -32600 ? 400 : code === -32601 ? 404 : code === -32603 ? 500 : 200 } + { status: code === -32600 ? 503 : code === -32601 ? 404 : code === -32603 ? 500 : 200 } ); } @@ -106,6 +106,7 @@ async function rejectIfA2ADisabled(id: string | number | null) { // ============ Route Handler ============ export async function POST(req: NextRequest) { + console.log("==> HIT A2A ROUTER:", req.url); // Auth check if (!authenticate(req)) { return jsonRpcError(null, -32600, "Unauthorized: missing or invalid API key"); @@ -180,6 +181,7 @@ export async function POST(req: NextRequest) { metadata: result.metadata, }); } catch (err) { + console.error("A2A ERROR TRACE:", err); const msg = err instanceof Error ? err.message : String(err); tm.updateTask(task.id, "failed", [{ type: "error", content: msg }], msg); return jsonRpcError(id, -32603, `Skill execution failed: ${msg}`); diff --git a/src/lib/db/compressionAnalytics.ts b/src/lib/db/compressionAnalytics.ts index d75ef44e02..f587a915ae 100644 --- a/src/lib/db/compressionAnalytics.ts +++ b/src/lib/db/compressionAnalytics.ts @@ -70,7 +70,7 @@ export function getCompressionAnalyticsSummary(since?: string): CompressionAnaly FROM compression_analytics ${whereClause} ` ) - .get(...params) as ScalarRow; + .get(...params) as ScalarRow | undefined; const modeRows = db .prepare( @@ -104,6 +104,14 @@ export function getCompressionAnalyticsSummary(since?: string): CompressionAnaly byProvider[key] = { count: r.cnt, tokensSaved: r.saved }; } + const last24hMap = new Map(); + const now = new Date(); + for (let i = 23; i >= 0; i--) { + const d = new Date(now.getTime() - i * 60 * 60 * 1000); + const hourStr = d.toISOString().substring(0, 14) + "00:00Z"; + last24hMap.set(hourStr, { hour: hourStr, count: 0, tokensSaved: 0 }); + } + const hourRows = db .prepare( ` @@ -114,19 +122,25 @@ export function getCompressionAnalyticsSummary(since?: string): CompressionAnaly GROUP BY hour ORDER BY hour ASC ` ) - .all(new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString()) as Array<{ + .all(new Date(now.getTime() - 24 * 60 * 60 * 1000).toISOString()) as Array<{ hour: string; cnt: number; saved: number; }>; - const last24h = hourRows.map((r) => ({ hour: r.hour, count: r.cnt, tokensSaved: r.saved })); + for (const r of hourRows) { + if (last24hMap.has(r.hour)) { + last24hMap.set(r.hour, { hour: r.hour, count: r.cnt, tokensSaved: r.saved }); + } + } + + const last24h = Array.from(last24hMap.values()); return { - totalRequests: scalar.total, - totalTokensSaved: scalar.totalSaved, - avgSavingsPct: Math.round(scalar.avgPct), - avgDurationMs: Math.round(scalar.avgDur), + totalRequests: scalar?.total ?? 0, + totalTokensSaved: scalar?.totalSaved ?? 0, + avgSavingsPct: Math.round(scalar?.avgPct ?? 0), + avgDurationMs: Math.round(scalar?.avgDur ?? 0), byMode, byProvider, last24h, diff --git a/src/shared/network/safeOutboundFetch.ts b/src/shared/network/safeOutboundFetch.ts index e4a08b18e5..89e2f35416 100644 --- a/src/shared/network/safeOutboundFetch.ts +++ b/src/shared/network/safeOutboundFetch.ts @@ -349,7 +349,7 @@ export function getSafeOutboundFetchErrorStatus(error: unknown) { error.code === "URL_GUARD_BLOCKED" || error.code === "REDIRECT_BLOCKED" ) { - return 400; + return 503; } return null; diff --git a/tests/e2e/ecosystem.test.ts b/tests/e2e/ecosystem.test.ts index dfb8fbfbd5..b42d177f48 100644 --- a/tests/e2e/ecosystem.test.ts +++ b/tests/e2e/ecosystem.test.ts @@ -266,7 +266,10 @@ describe("E2E: Security", () => { return; } - expect([200, 400]).toContain(res.status); + if (res.status !== 503) { + console.log("SEC-1 FAILED STATUS:", res.status, "BODY:", await res.text()); + } + expect(res.status).toBe(503); }); itCase("should handle invalid API keys according to server configuration", async () => { @@ -288,7 +291,10 @@ describe("E2E: Security", () => { return; } - expect([200, 400]).toContain(res.status); + if (res.status !== 503) { + console.log("SEC-2 FAILED STATUS:", res.status, "BODY:", await res.text()); + } + expect(res.status).toBe(503); }); itCase("should not expose internal errors in API responses", async () => {