Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
4353d19
fix(auth): make local-email verification explicit and raise password …
izadoesdev May 29, 2026
0362fd2
fix(auth): purge reset tokens and revoke sessions on password change
izadoesdev May 29, 2026
f89da70
fix(auth): disable session cookie cache so revocation is immediate
izadoesdev May 29, 2026
7f01245
feat(basket): log quota-exceeded events with billing context
izadoesdev May 29, 2026
01c1a15
refactor(basket): drop redundant JSDoc and centralize event input types
izadoesdev May 29, 2026
796a03a
refactor(api): sanitize Autumn request body and split out session loa…
izadoesdev May 29, 2026
ac30575
refactor(rpc): dedupe billing-control handlers via shared upsert helper
izadoesdev May 29, 2026
6efb97d
feat(insights): add funnel and goal conversion signal detection
izadoesdev May 29, 2026
33b6246
fix(rpc): stop swallowing DB errors in workspace lookups
izadoesdev May 29, 2026
93d92a9
feat(auth): forward Better-Auth internal logs to evlog
izadoesdev May 29, 2026
29ce1cb
fix(rpc): stop swallowing DB errors when resolving billing owner
izadoesdev May 29, 2026
a9c4c8b
refactor(rpc): drop redundant JSDoc from billing plan helpers
izadoesdev May 29, 2026
a61d668
refactor(api): parse Autumn webhook payloads with Zod at the boundary
izadoesdev May 29, 2026
d14b453
feat(insights): bill agent usage and gate generation on credits
izadoesdev May 30, 2026
b73337a
fix(ai): allow "insights" as an agent usage source
izadoesdev May 30, 2026
7a21642
style(docs): sort pricing table interface members and attributes
izadoesdev May 30, 2026
f236aad
fix(api): use namespace import for zod in Autumn webhook
izadoesdev May 30, 2026
cfbe7c3
feat(db): scope agent chats to organization
izadoesdev May 30, 2026
db05d7a
feat(rpc): list agent chats by organization
izadoesdev May 30, 2026
350c91e
feat(ai): give the agent multi-website workspace context
izadoesdev May 30, 2026
4477437
feat(api): serve the agent over an organization workspace
izadoesdev May 30, 2026
bfdf951
feat(dashboard): rebuild the agent as an organization workspace
izadoesdev May 30, 2026
036abda
style(api): format agent route
izadoesdev May 30, 2026
078a3d5
fix(ai): stop the agent from emitting emojis
izadoesdev May 30, 2026
a214b35
fix(dashboard): smooth out the website mention menu
izadoesdev May 30, 2026
b2011c5
refactor(dashboard): drive agent chat navigation through callbacks
izadoesdev May 30, 2026
5581d7e
fix(ai): enforce website membership in resolveToolWebsite
izadoesdev May 30, 2026
3e8fced
fix(ai): harden agent tool context and result keying
izadoesdev May 30, 2026
396f01e
fix(insights): bump createdAt on insight refresh to preserve dedupe c…
izadoesdev May 30, 2026
fbd5144
fix(insights): make funnel/goal detection deterministic and type-safe
izadoesdev May 30, 2026
f3e5ddb
fix(dashboard): allow WebAssembly in production CSP
izadoesdev May 30, 2026
1c96cb3
fix(dashboard): use stable en-US locale in formatNumber
izadoesdev May 30, 2026
f9e31bf
fix(dashboard): gate per-website agent page on org load
izadoesdev May 30, 2026
f206521
refactor(dashboard): extract shared agent gate guard
izadoesdev May 30, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 42 additions & 47 deletions apps/api/src/billing/autumn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,69 +48,64 @@ async function stripPrivilegedBody(request: Request): Promise<Request> {
}

const text = await request.text();
if (!text) {
return new Request(request.url, request);
}

let parsed: unknown;
try {
parsed = JSON.parse(text);
} catch {
return new Request(request.url, {
method: request.method,
headers: request.headers,
body: text,
});
let body: string | null = text || null;
if (text) {
try {
body = JSON.stringify(sanitize(JSON.parse(text)));
} catch {
body = text;
}
}

return new Request(request.url, {
method: request.method,
headers: request.headers,
body: JSON.stringify(sanitize(parsed)),
body,
});
}

const autumn = autumnHandler({ identify: identifyAutumnCustomer });

export async function handleAutumnRequest(request: Request) {
const sanitized = await stripPrivilegedBody(request);
return autumnHandler({
identify: identifyAutumnCustomer,
})(withAutumnApiPath(sanitized));
return autumn(withAutumnApiPath(sanitized));
}

async function identifyAutumnCustomer(request: Request) {
async function loadSession(request: Request) {
try {
const session = await auth.api.getSession({ headers: request.headers });
if (!session?.user) {
return null;
}
return await auth.api.getSession({ headers: request.headers });
} catch (error) {
const err = error instanceof Error ? error : new Error(String(error));
useLogger().error(err, {
autumn: "identify",
autumn_stage: "getSession",
});
throw err;
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot May 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Rethrowing session lookup errors makes Autumn requests fail hard instead of degrading to anonymous customer resolution.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/api/src/billing/autumn.ts, line 83:

<comment>Rethrowing session lookup errors makes Autumn requests fail hard instead of degrading to anonymous customer resolution.</comment>

<file context>
@@ -48,69 +48,64 @@ async function stripPrivilegedBody(request: Request): Promise<Request> {
+			autumn: "identify",
+			autumn_stage: "getSession",
+		});
+		throw err;
+	}
+}
</file context>
Suggested change
throw err;
return null;
Fix with Cubic

}
}

async function identifyAutumnCustomer(request: Request) {
const session = await loadSession(request);
if (!session?.user) {
return null;
}

const activeOrgId = (
session.session as { activeOrganizationId?: string | null }
)?.activeOrganizationId;
const activeOrgId = session.session.activeOrganizationId ?? null;

if (activeOrgId) {
const role = await getMemberRole(session.user.id, activeOrgId);
if (role !== "owner" && role !== "admin") {
return null;
}
if (activeOrgId) {
const role = await getMemberRole(session.user.id, activeOrgId);
if (role !== "owner" && role !== "admin") {
return null;
}
}

const customerId = await getBillingCustomerId(session.user.id, activeOrgId);
const customerId = await getBillingCustomerId(session.user.id, activeOrgId);

return {
customerId,
customerData: {
name: session.user.name,
email: session.user.email,
},
};
} catch (error) {
useLogger().error(
error instanceof Error ? error : new Error(String(error)),
{
autumn: "identify",
}
);
return null;
}
return {
customerId,
customerData: {
name: session.user.name,
email: session.user.email,
},
};
}
Comment on lines +87 to 111
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Error propagation contract change for identify callback

loadSession now throws on any getSession failure, and identifyAutumnCustomer no longer wraps the call. The old code always returned null | CustomerData, meaning the autumnHandler's identify callback could never throw. The autumn SDK isn't guaranteed to catch exceptions thrown by the identify function — if it doesn't, a transient session-store error will produce a 500 response instead of gracefully treating the request as unauthenticated. This affects every Autumn billing request during auth-service instability.

Loading
Loading