fix: brainstorm infinite loop, ghost cursor, embedding ::vector cast, semantic search, billing stats, usage meter accordion
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 5s

- Fix useBrainstormSocket: stable guestId via useRef, remove setState in cleanup
- Fix GhostCursor: direct DOM manipulation via refs, no useState re-renders
- Fix all SQL embedding queries: add ::vector cast on text columns
- Fix embedding truncation to 15000 chars (under 8192 token limit)
- Fix NoteEmbedding INSERT: remove non-existent updatedAt column
- Fix billing page: show all quota stats in grid instead of single metric
- Fix usage meter: accordion expand/collapse, per-feature detail
- Fix semantic search: rebuild 103 note embeddings, ::vector cast on vectorSearch
- Fix brainstorm expand/manual-idea/create: ::vector cast on embedding SQL
This commit is contained in:
Antigravity
2026-05-16 18:50:34 +00:00
parent ee8e2bda59
commit 8c7ca69640
117 changed files with 11732 additions and 834 deletions

View File

@@ -31,6 +31,7 @@ export class QuotaExceededError extends Error {
billingOwnerId?: string;
triggeredByUserId?: string;
isGuestActor?: boolean;
currentTier?: string;
constructor(
upgradeTier: 'PRO' | 'BUSINESS',
@@ -42,6 +43,7 @@ export class QuotaExceededError extends Error {
billingOwnerId?: string;
triggeredByUserId?: string;
isGuestActor?: boolean;
currentTier?: string;
},
) {
super(`Quota exceeded for ${feature}`);
@@ -53,6 +55,7 @@ export class QuotaExceededError extends Error {
this.billingOwnerId = sessionMeta?.billingOwnerId;
this.triggeredByUserId = sessionMeta?.triggeredByUserId;
this.isGuestActor = sessionMeta?.isGuestActor;
this.currentTier = sessionMeta?.currentTier;
}
toJSON() {
@@ -62,6 +65,7 @@ export class QuotaExceededError extends Error {
upgradeTier: this.upgradeTier,
byokConfigured: this.byokConfigured,
isGuestActor: this.isGuestActor ?? false,
currentTier: this.currentTier,
};
}
}
@@ -121,6 +125,18 @@ local newCount = tonumber(redis.call('GET', KEYS[1]))
return newCount
`;
const INCREMENT_BY_LUA = `
local count = tonumber(ARGV[1]) or 1
local ttl = tonumber(ARGV[2])
redis.call('INCRBY', KEYS[1], count)
local ttlResult = redis.call('TTL', KEYS[1])
if ttlResult == -1 then
redis.call('EXPIRE', KEYS[1], ttl)
end
local newCount = tonumber(redis.call('GET', KEYS[1]))
return newCount
`;
const RESERVE_LUA = `
local limit = tonumber(ARGV[1])
local ttl = tonumber(ARGV[2])
@@ -252,6 +268,7 @@ export async function checkEntitlementOrThrow(
result.limit,
result.limit > 0 ? result.limit - result.remaining : 0,
result.byokConfigured ?? false,
{ currentTier: result.tier },
);
}
}
@@ -261,7 +278,7 @@ export async function reserveUsageOrThrow(
feature: string,
): Promise<void> {
if (!isValidFeature(feature)) {
throw new QuotaExceededError('PRO', feature, 0, 0, false);
throw new QuotaExceededError('PRO', feature, 0, 0, false, { currentTier: 'BASIC' });
}
const tier = await getEffectiveTier(userId);
@@ -274,6 +291,7 @@ export async function reserveUsageOrThrow(
0,
0,
false,
{ currentTier: tier },
);
}
@@ -297,6 +315,7 @@ export async function reserveUsageOrThrow(
limit,
limit,
byokConfigured,
{ currentTier: tier },
);
}
} catch (err) {
@@ -324,11 +343,11 @@ export async function checkSessionEntitlementOrThrow(
}
}
export function incrementUsageAsync(userId: string, feature: string): Promise<void> {
export function incrementUsageAsync(userId: string, feature: string, count: number = 1): Promise<void> {
if (!isValidFeature(feature)) return Promise.resolve();
const key = getRedisKey(userId, feature);
return redis.eval(INCREMENT_LUA, 1, key, String(TTL_SECONDS)).then(() => {}).catch((err) => {
return redis.eval(INCREMENT_BY_LUA, 1, key, String(count), String(TTL_SECONDS)).then(() => {}).catch((err) => {
console.error('[entitlements] Async increment failed:', err);
});
}