chore(release): finalize v3.7.8 stabilization, fix a2a status codes and open issues

This commit is contained in:
Antigravity Assistant
2026-05-01 09:30:21 -03:00
parent f7d45ef31f
commit 7b575f38aa
10 changed files with 69 additions and 30 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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.

View File

@@ -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 {

View File

@@ -240,7 +240,7 @@ export default function CompressionAnalyticsTab() {
<div className="card p-5">
<h3 className="font-semibold text-text mb-4 flex items-center gap-2">
<span className="material-symbols-outlined text-primary text-[20px]">show_chart</span>
Last 24 Hours
Last 24 Hours (Activity)
</h3>
<div className="flex items-end gap-2 h-48">
{stats.last24h.map((entry, idx) => {
@@ -258,7 +258,7 @@ export default function CompressionAnalyticsTab() {
</div>
</div>
<div className="text-xs text-text-muted rotate-45 origin-left">
{entry.hour.substring(0, 2)}
{entry.hour.substring(11, 13)}
</div>
</div>
);

View File

@@ -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<CacheMetrics | null>(null);
const [resetting, setResetting] = useState(false);
@@ -117,19 +124,19 @@ export default function CacheStatsCard() {
<div>
<p className="text-text-muted">{t("inputTokens")}</p>
<p className="font-mono text-lg text-text-main">
{metrics.totalInputTokens.toLocaleString()}
{formatNumberCompact(metrics.totalInputTokens)}
</p>
</div>
<div>
<p className="text-text-muted">{t("cachedTokensRead")}</p>
<p className="font-mono text-lg text-green-400">
{metrics.totalCachedTokens.toLocaleString()}
{formatNumberCompact(metrics.totalCachedTokens)}
</p>
</div>
<div>
<p className="text-text-muted">{t("cacheCreationWrite")}</p>
<p className="font-mono text-lg text-blue-400">
{metrics.totalCacheCreationTokens.toLocaleString()}
{formatNumberCompact(metrics.totalCacheCreationTokens)}
</p>
</div>
</div>
@@ -157,7 +164,7 @@ export default function CacheStatsCard() {
<div>
<p className="text-text-muted">{t("tokensSaved")}</p>
<p className="font-mono text-lg text-green-400">
{metrics.tokensSaved.toLocaleString()}
{formatNumberCompact(metrics.tokensSaved)}
</p>
</div>
<div>
@@ -189,13 +196,13 @@ export default function CacheStatsCard() {
</div>
<div className="flex items-center gap-4 font-mono">
<span className="text-text-muted" title={t("inputTokens")}>
{t("inputShort")}: {stats.inputTokens.toLocaleString()}
{t("inputShort")}: {formatNumberCompact(stats.inputTokens)}
</span>
<span className="text-green-400" title={t("cachedTokensRead")}>
{t("cachedShort")}: {stats.cachedTokens.toLocaleString()}
{t("cachedShort")}: {formatNumberCompact(stats.cachedTokens)}
</span>
<span className="text-blue-400" title={t("cacheCreationWrite")}>
{t("writeShort")}: {stats.cacheCreationTokens.toLocaleString()}
{t("writeShort")}: {formatNumberCompact(stats.cacheCreationTokens)}
</span>
<span className="text-green-400 w-12 text-right">
{providerCacheRate.toFixed(0)}%

View File

@@ -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}`);

View File

@@ -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<string, { hour: string; count: number; tokensSaved: number }>();
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,

View File

@@ -349,7 +349,7 @@ export function getSafeOutboundFetchErrorStatus(error: unknown) {
error.code === "URL_GUARD_BLOCKED" ||
error.code === "REDIRECT_BLOCKED"
) {
return 400;
return 503;
}
return null;

View File

@@ -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 () => {