Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
72 commits
Select commit Hold shift + click to select a range
9eaacc0
feat(catalog): add global access summary page (#2939)
adityathebe Mar 21, 2026
ab17c42
chore(release): 1.4.218 [skip ci]
semantic-release-bot Mar 21, 2026
6af8c27
Add GitHub and GCP icon mapping for config scrapers (#2961)
Codex Mar 23, 2026
b969d43
chore(release): 1.4.219 [skip ci]
semantic-release-bot Mar 23, 2026
04fa7d8
feat: add loading indicator between view pages transition
adityathebe Mar 16, 2026
161fc76
chore(release): 1.4.220 [skip ci]
semantic-release-bot Mar 24, 2026
6f89c2f
fix: unified millicore formatter
adityathebe Mar 25, 2026
de1fbbd
fix: barGauage min
adityathebe Mar 25, 2026
695b15e
chore(release): 1.4.221 [skip ci]
semantic-release-bot Mar 25, 2026
ff75f1b
fix(auth): include method=password in Kratos login state on init
adityathebe Mar 26, 2026
42763c2
chore(release): 1.4.222 [skip ci]
semantic-release-bot Mar 26, 2026
75baffa
feat: properties in insights modal
adityathebe Mar 26, 2026
a427c65
chore(release): 1.4.223 [skip ci]
semantic-release-bot Mar 27, 2026
0e715bd
chore: Insights filter and modal improvements (#2964)
adityathebe Mar 27, 2026
5bfdb42
chore(release): 1.4.224 [skip ci]
semantic-release-bot Mar 27, 2026
d2d8066
feat: permission table filters
adityathebe Apr 1, 2026
f5ddcc9
chore(release): 1.4.225 [skip ci]
semantic-release-bot Apr 1, 2026
72cce35
feat(auth): proxy OIDC endpoints (#2959)
adityathebe Apr 1, 2026
4917325
chore(release): 1.4.226 [skip ci]
semantic-release-bot Apr 1, 2026
6d89662
feat: mcp:use action
adityathebe Apr 1, 2026
c4647ac
chore(release): 1.4.227 [skip ci]
semantic-release-bot Apr 1, 2026
d8011d7
fix: SKILL to work with data table and filters
adityathebe Mar 27, 2026
ef4bfa7
feat(config-insights): use RPC filter options and add catalog
adityathebe Mar 29, 2026
1fdb9c1
fix: coderabbit comments
adityathebe Mar 30, 2026
1b588ce
chore(release): 1.4.228 [skip ci]
semantic-release-bot Apr 1, 2026
e74c4d3
feat: scope impersonation (#2934)
yashmehrotra Apr 1, 2026
c33f734
chore(release): 1.4.229 [skip ci]
semantic-release-bot Apr 1, 2026
7e155ad
fix: show error in the invitation modal
adityathebe Apr 2, 2026
6540fdb
chore(release): 1.4.230 [skip ci]
semantic-release-bot Apr 2, 2026
7a8af0f
Heatmap Panel (#2945)
adityathebe Apr 3, 2026
6676117
chore(release): 1.4.231 [skip ci]
semantic-release-bot Apr 3, 2026
b554d65
chore: document view table filter
adityathebe Apr 1, 2026
85f79f9
feat(playbooks): split permissions into dedicated modal
adityathebe Apr 1, 2026
f642313
style(menu): use compact spacing for action menu items
adityathebe Apr 1, 2026
4a80586
style(card): remove default shadow and increase padding
adityathebe Apr 1, 2026
37454bb
fix(playbooks): center delete confirm and require confirmation
adityathebe Apr 1, 2026
ba7df4c
fix(ui): refine flat tabs header spacing and typography
adityathebe Apr 1, 2026
4650ba6
fix: padding in modals
adityathebe Apr 1, 2026
8ad5bb1
feat(playbooks): improve permissions modal tabs and table layout
adityathebe Apr 1, 2026
e19784f
fix: playbook card padding
adityathebe Apr 1, 2026
a0f8bee
fix(permissions): surface fetch errors in permissions view
adityathebe Apr 2, 2026
a94f8f6
fix(permissions): refresh playbook permission tabs on open and switch
adityathebe Apr 2, 2026
6b5c4ea
fix(permissions): correct playbook modal column visibility
adityathebe Apr 3, 2026
564f8d1
refactor: PermissionResourceCell
adityathebe Apr 3, 2026
f345da1
feat(permissions): render selector connections with link resolution
adityathebe Apr 3, 2026
a3c4833
fix permission review comments for resource rendering and confirm loa…
adityathebe Apr 3, 2026
8f6de84
chore(release): 1.4.232 [skip ci]
semantic-release-bot Apr 3, 2026
ad99c5d
feat(permissions): MCP settings page (#2978)
adityathebe Apr 14, 2026
8dcad2e
chore(release): 1.4.233 [skip ci]
semantic-release-bot Apr 14, 2026
80e293f
chore: live tail mode for config changes (#2980)
yashmehrotra Apr 14, 2026
3a0dfb6
chore(release): 1.4.234 [skip ci]
semantic-release-bot Apr 14, 2026
5000ff8
chore: reduce notification summary latency from repeated refetches (#…
adityathebe Apr 15, 2026
b7129b8
chore(release): 1.4.235 [skip ci]
semantic-release-bot Apr 15, 2026
1b26a66
fix(views): resolve useEffect anti-patterns across Views and Audit-Re…
Copilot Apr 17, 2026
762874f
chore(release): 1.4.236 [skip ci]
semantic-release-bot Apr 17, 2026
412ec9b
fix: improve error handling in ai chat
adityathebe Apr 16, 2026
f31334d
chore(release): 1.4.237 [skip ci]
semantic-release-bot Apr 19, 2026
a0edde9
feat: scrape snapshot UI (#2988)
adityathebe Apr 21, 2026
deb3ae0
chore(release): 1.4.238 [skip ci]
semantic-release-bot Apr 21, 2026
bbed220
feat: add jobs summary page with drilldown (#2987)
adityathebe Apr 22, 2026
10ccd0b
chore(release): 1.4.239 [skip ci]
semantic-release-bot Apr 22, 2026
4cd3438
feat: Permission workbench (#2983)
adityathebe Apr 22, 2026
8644485
chore(release): 1.4.240 [skip ci]
semantic-release-bot Apr 22, 2026
3fd37a4
fix: sync job overrides modal with persisted properties
adityathebe Apr 23, 2026
850797f
chore(release): 1.4.241 [skip ci]
semantic-release-bot Apr 23, 2026
ec7cc39
fix: show in-progress notification counts in send history summary
adityathebe Apr 23, 2026
099f784
chore(release): 1.4.242 [skip ci]
semantic-release-bot Apr 23, 2026
c666756
fix(feature-flags): refetch settings flags on page mount
adityathebe Apr 23, 2026
a4d8975
chore(release): 1.4.243 [skip ci]
semantic-release-bot Apr 23, 2026
eca25d7
feat: improve notification rules table event badges and tooltip behavior
adityathebe Apr 23, 2026
51dad2b
chore(release): 1.4.244 [skip ci]
semantic-release-bot Apr 23, 2026
a72a98c
fix(connections): show URL field and fix insecure_tls mapping in AWS …
adityathebe Apr 23, 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
8 changes: 8 additions & 0 deletions .agents/skills/data-table-builder/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
name: data-table-builder
description: Read when working with filters in the data table
---

- Never query the entire table and then filter client-side to populate the filter dropdown options.
Create a view in duty/ repo instead that returns options for all the filters at once.
Reference: `config_access_summary_by_user` view in views/038_config_access.sql
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
- Do not hand-write Shadcn components. Use the shadcn CLI to add new components.
- Use npm run build to build
- Do not run `npm run build` when dev server is running. use `npm run typecheck` instead.
137 changes: 106 additions & 31 deletions app/api/chat/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,50 +141,101 @@ function buildLLMModel(connection: LLMConnection): LanguageModelV3 {
}
}

function tryParseJSON(value?: string) {
if (!value) {
return undefined;
}

try {
return JSON.parse(value);
} catch {
return value;
}
}

function getErrorDetail(error: unknown): unknown {
if (error instanceof HttpError) {
return tryParseJSON(error.body);
}

if (!(error instanceof Error) || !error.cause) {
return undefined;
}

if (error.cause instanceof Error) {
const cause = error.cause as Error & {
code?: string;
errno?: string | number;
address?: string;
port?: number;
};

return {
message: cause.message,
code: cause.code,
errno: cause.errno,
address: cause.address,
port: cause.port
};
}

return error.cause;
}

export async function POST(req: Request) {
let mcpClient: Awaited<ReturnType<typeof createMCPClient>> | undefined;

const wideEvent: Record<string, any> = {
event: "llm-conversation",
timestamp: new Date().toISOString(),
status: "started"
};

const {
messages,
alwaysAllowedTools = []
}: { messages?: UIMessage[]; alwaysAllowedTools?: string[] } =
await req.json();
if (!Array.isArray(messages)) {
return new Response("Invalid request body", { status: 400 });
}
try {
const {
messages,
alwaysAllowedTools = []
}: { messages?: UIMessage[]; alwaysAllowedTools?: string[] } =
await req.json();

if (!Array.isArray(messages)) {
return new Response(JSON.stringify({ error: "Invalid request body" }), {
status: 400,
headers: {
"content-type": "application/json"
}
});
}

wideEvent.messages = messages.length;
wideEvent.messages = messages.length;

try {
const backendUrl = await getBackendUrl();
wideEvent.backendURL = backendUrl;

const cookies = req.headers.get("cookie") ?? "";
wideEvent.cookies = cookies.length;

const llmConnection = await fetchLLMConnection(backendUrl, cookies);
const model = buildLLMModel(llmConnection);
wideEvent.llm = {
model: llmConnection.properties?.model,
provider: llmConnection.type
};

const mcpClient = await createMCPClient({
const model = buildLLMModel(llmConnection);

mcpClient = await createMCPClient({
transport: {
type: "http",
url: buildURL(backendUrl, "/mcp").toString(),
headers: {
// Use the user's cookie to authenticate for now.
// We need to add the more fine-grained MCP tokens
Cookie: cookies
}
}
});
wideEvent.mcpCreated = true;

const tools = await buildChatTools(mcpClient, alwaysAllowedTools);
wideEvent.totalTools = Object.entries(tools).length;

const loadedSkillTool = await loadSkillTool();
wideEvent.skills = {
Expand All @@ -201,7 +252,6 @@ export async function POST(req: Request) {
(tools as Record<string, unknown>).skill = loadedSkillTool.skillTool;
}

// Build tools first so convertToModelMessages can resolve tool schemas
const modelMessages = await convertToModelMessages(messages, { tools });

const result = streamText({
Expand All @@ -213,35 +263,60 @@ export async function POST(req: Request) {
experimental_transform: truncateToolResultTransform,
onError: async (error) => {
wideEvent.status = "error";
wideEvent.error =
error instanceof Error ? error.message : String(error);
wideEvent.error = {
error:
error instanceof Error ? error.message : "Internal Server Error",
detail: getErrorDetail(error),
provider: wideEvent.llm?.provider,
model: wideEvent.llm?.model
};

await mcpClient?.close();
console.error(JSON.stringify(wideEvent));
},
onFinish: async (result) => {
wideEvent.status = "completed";
wideEvent.usage = {
totalTokens: result.usage?.totalTokens
};
wideEvent.finishReason = result.finishReason;

await mcpClient?.close();
console.log(JSON.stringify(wideEvent));
}
});

return result.toUIMessageStreamResponse({ sendReasoning: true });
return result.toUIMessageStreamResponse({
sendReasoning: true,
onError: (error) =>
JSON.stringify({
error:
error instanceof Error ? error.message : "Internal Server Error",
detail: getErrorDetail(error),
provider: wideEvent.llm?.provider,
model: wideEvent.llm?.model
})
});
} catch (error) {
wideEvent.status = "error";
wideEvent.error = error instanceof Error ? error.message : String(error);
if (error instanceof HttpError) {
return new Response(error.body ?? error.message, {
status: error.status
});
}
return new Response("Internal Server Error", { status: 500 });
} finally {
if (wideEvent.status === "error") {
console.error(JSON.stringify(wideEvent));
} else {
console.log(JSON.stringify(wideEvent));
}
wideEvent.error = {
error: error instanceof Error ? error.message : "Internal Server Error",
detail: getErrorDetail(error),
provider: wideEvent.llm?.provider,
model: wideEvent.llm?.model
};

try {
await mcpClient?.close();
} catch {}

console.error(JSON.stringify(wideEvent));

return new Response(JSON.stringify(wideEvent.error), {
status: error instanceof HttpError ? error.status : 500,
headers: {
"content-type": "application/json"
}
});
}
}
13 changes: 12 additions & 1 deletion clerk.middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,18 @@ import { NextResponse } from "next/server";
const isPubliclyAccessibleRoute = createRouteMatcher([
// all pages except the ones listed below are protected
"/login(.*)",
"/registration(.*)"
"/registration(.*)",
"/.well-known(.*)",
"/authorize(.*)",
"/oauth(.*)",
"/userinfo",
"/revoke",
"/device_authorization",
"/keys",
"/end_session",
"/endsession",
"/oidc(.*)",
"/mcp(.*)"
]);

export default clerkMiddleware(
Expand Down
54 changes: 47 additions & 7 deletions next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,22 +39,61 @@ const config = {
];
},
async rewrites() {
// if clerk is enabled, we will use next API routes to proxy requests to
// the backend
if (process.env.NEXT_PUBLIC_AUTH_IS_CLERK === "true") {
return [];
}
const isClerkAuth = process.env.NEXT_PUBLIC_AUTH_IS_CLERK === "true";
const isBasicAuth = process.env.NEXT_PUBLIC_AUTH_IS_BASIC === "true";

// Read at build time. See Dockerfile for deployment related steps.
const backendURL = process.env.BACKEND_URL || "http://localhost:3000/";
const isCanary =
process.env.NEXT_PUBLIC_APP_DEPLOYMENT === "CANARY_CHECKER";
const canaryPrefix = isCanary ? "" : "/canary";
// OIDC protocol endpoints are mounted at the root of the backend (matching the
// issuer URL). These rewrites let the browser reach those endpoints through the
// Next.js server without authentication interference.
const OIDC_REWRITES = [
{
source: "/.well-known/:path*",
destination: `${backendURL}/.well-known/:path*`
},
{ source: "/authorize", destination: `${backendURL}/authorize` },
{
source: "/authorize/:path*",
destination: `${backendURL}/authorize/:path*`
},
{ source: "/oauth/token", destination: `${backendURL}/oauth/token` },
{
source: "/oauth/introspect",
destination: `${backendURL}/oauth/introspect`
},
{ source: "/userinfo", destination: `${backendURL}/userinfo` },
{ source: "/revoke", destination: `${backendURL}/revoke` },
{
source: "/device_authorization",
destination: `${backendURL}/device_authorization`
},
{ source: "/keys", destination: `${backendURL}/keys` },
{ source: "/end_session", destination: `${backendURL}/end_session` },
// Some clients use /endsession (no underscore) — proxy both.
{ source: "/endsession", destination: `${backendURL}/endsession` },
// All OIDC sub-routes (login, callback, …)
{ source: "/oidc/:path*", destination: `${backendURL}/oidc/:path*` },
// MCP transport endpoint
{ source: "/mcp", destination: `${backendURL}/mcp` },
{ source: "/mcp/:path*", destination: `${backendURL}/mcp/:path*` }
];

// clerk and basic auth use next API routes for app endpoints, but OIDC protocol
// endpoints still need explicit rewrites.
if (isClerkAuth || isBasicAuth) {
return OIDC_REWRITES;
}

const LOCALHOST_ENV_URL_REWRITES = [
{
source: "/api/:path*",
destination: `${backendURL}/api/:path*`
}
},
...OIDC_REWRITES
];

const URL_REWRITES = [
Expand All @@ -71,7 +110,8 @@ const config = {
{
source: "/api/:path*",
destination: `${backendURL}/:path*`
}
},
...OIDC_REWRITES
];
// NODE_ENV is set to "development" when running locally, so we can use it
// to determine if we are running in a local environment.
Expand Down
Loading
Loading