Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 3 additions & 2 deletions app/modules/assistant/system-prompt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const STATIC_SYSTEM_PROMPT = [
'Look up users, organizations, and projects across the entire platform.',
'ALWAYS prefer these tools for fetching user, org, and project data — never infer this data from activity logs or other tools.',
'- Use `searchUsers` to find users by email or resource name, `searchOrganizations` / `searchProjects` by name',
'- Use `listUsers` to browse users or filter by approval status',
'- Use `listUsers` to browse users or filter by approval status. NOTE: `listUsers` does NOT sort by creation date — for "newest", "latest", or "most recent" users, use `queryActivityLogs` with verb="create" and resourceType="users" instead.',
'- Use `getUser`, `getOrganization`, `getProject` for detail lookups',
'- Use `listUserOrganizations` to find all orgs a user belongs to, then `listOrgProjects` for each org to get their projects',
'- Use `listOrgMembers` to see who is in an org',
Expand All @@ -33,8 +33,9 @@ const STATIC_SYSTEM_PROMPT = [
'These require a `projectName` parameter — ask the operator or look it up first.',
'',
'### Activity / audit tools',
'Query audit logs at the platform level with optional CEL filters. Only use this for audit trail questions (e.g. "what changed recently?", "who did what?") — NOT for looking up user/org/project data.',
'Query audit logs at the platform level with optional CEL filters.',
'Use `queryActivityLogs` — it always queries at platform scope. Use the optional filter parameters to narrow by user, namespace (org/project name), resource type, API group, or verb.',
'Use this for audit trail questions ("what changed recently?", "who did what?") AND for time-ordered queries like "newest users" or "recently created orgs" (filter by verb="create" and the relevant resourceType).',
'Example: to see what happened in org "acme-corp", set namespace to "acme-corp". To see a user\'s actions, set user to their email.',
'',
'### Fraud tools',
Expand Down
5 changes: 4 additions & 1 deletion app/modules/assistant/tools/customer-tools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,9 @@ export function createCustomerTools({ accessToken }: CustomerToolDeps) {
listUsers: tool({
description:
'List platform users with optional filters.' +
' Use this to browse users or filter by approval status.',
' Use this to browse users or filter by approval status.' +
' WARNING: This returns users in arbitrary order — it does NOT sort by creation date.' +
' For "newest" / "most recent" / "latest" users, use `queryActivityLogs` instead with verb="create" and resourceType="users".',
inputSchema: z.object({
limit: z.number().int().min(1).max(50).default(20).describe('Max results to return'),
registrationApproval: z
Expand Down Expand Up @@ -130,6 +132,7 @@ export function createCustomerTools({ accessToken }: CustomerToolDeps) {
email: u.spec?.email,
givenName: u.spec?.givenName,
familyName: u.spec?.familyName,
createdAt: u.metadata?.creationTimestamp,
registrationApproval: u.status?.registrationApproval,
url: `/customers/users/${encodeURIComponent(u.metadata?.name)}`,
}))
Expand Down
1 change: 1 addition & 0 deletions app/server/iface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ import { SecureHeadersVariables } from 'hono/secure-headers';
export type EnvVariables = SecureHeadersVariables &
RequestIdVariables & {
token: string;
userId: string;
};
7 changes: 5 additions & 2 deletions app/server/middleware/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ export function authMiddleware() {
throw new AuthenticationError('No access token available', reqId);
}

// Set access token in context using proper Hono way
c.set('token', session.accessToken);
c.set('userId', session.sub);

await next();
} catch (error) {
Expand Down Expand Up @@ -71,7 +71,10 @@ export function authMiddleware() {
});
}

// Helper function to safely get token from context
export const getToken = (c: Context<{ Variables: EnvVariables }>) => {
return c.get('token') as string;
};

export const getUserId = (c: Context<{ Variables: EnvVariables }>) => {
return c.get('userId') as string;
};
2 changes: 1 addition & 1 deletion app/server/middleware/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export { loggerMiddleware, honoLoggerMiddleware } from './logger';
export { requestContextMiddleware } from './request-context';
export { cacheMiddleware } from './cache';
export { authMiddleware, getToken } from './auth';
export { authMiddleware, getToken, getUserId } from './auth';
13 changes: 11 additions & 2 deletions app/server/routes/assistant.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { buildSystemPrompt, createAssistantTools } from '@/modules/assistant';
import { EnvVariables } from '@/server/iface';
import { authMiddleware, getToken } from '@/server/middleware';
import { authMiddleware, getToken, getUserId } from '@/server/middleware';
import { env } from '@/utils/config/env.server';
import { logger } from '@/utils/logger';
import { createAnthropic } from '@ai-sdk/anthropic';
Expand Down Expand Up @@ -33,14 +33,20 @@ assistantRoutes.post('/', authMiddleware(), async (c) => {
}

const token = getToken(c);
const userId = getUserId(c);

const body = await c.req.json();
const { messages, clientOs } = body as {
messages: UIMessage[];
clientOs?: string;
};

const userId = 'staff-user';
const lastUserMessage = messages.findLast((m) => m.role === 'user');
const userMessage = lastUserMessage?.parts
?.filter((p): p is { type: 'text'; text: string } => p.type === 'text')
.map((p) => p.text)
.join(' ')
.slice(0, 500);

if (!checkRateLimit(userId)) {
return c.json({ error: 'Too Many Requests' }, 429);
Expand Down Expand Up @@ -73,6 +79,7 @@ assistantRoutes.post('/', authMiddleware(), async (c) => {
logger.error('assistant stream failed', {
userId,
model,
userMessage,
error: err instanceof Error ? err.message : String(err),
stack: err instanceof Error ? err.stack : undefined,
});
Expand All @@ -83,6 +90,7 @@ assistantRoutes.post('/', authMiddleware(), async (c) => {
logger.info('assistant request completed', {
userId,
model,
userMessage,
inputTokens: usage.inputTokens,
outputTokens: usage.outputTokens,
totalTokens: usage.totalTokens,
Expand All @@ -95,6 +103,7 @@ assistantRoutes.post('/', authMiddleware(), async (c) => {
} catch (err) {
logger.error('assistant request failed', {
userId,
userMessage,
error: err instanceof Error ? err.message : String(err),
stack: err instanceof Error ? err.stack : undefined,
});
Expand Down
Loading