Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
cf3a204
feat(appkit): agents() plugin, createAgent(def), and markdown-driven …
MarioCadenas Apr 29, 2026
3107741
refactor(appkit): generalize default base system prompt
MarioCadenas Apr 23, 2026
2ba1577
feat(appkit): optional serving_endpoint on agents plugin manifest
MarioCadenas Apr 23, 2026
1c4f8d2
fix(appkit): agents manifest uses DATABRICKS_AGENT_ENDPOINT
MarioCadenas Apr 23, 2026
68dcd9c
feat(agents): folder-based markdown discovery (<id>/agent.md)
MarioCadenas Apr 23, 2026
e4c1b01
refactor(appkit): promote MCP client + host policy to connectors/mcp
MarioCadenas Apr 23, 2026
1710809
refactor(appkit): extract normalizeToolResult, consumeAdapterStream, …
MarioCadenas Apr 23, 2026
fbd6552
fix(agents): propagate tool annotations through tool() → FunctionTool…
MarioCadenas Apr 24, 2026
3300ff3
feat(agents): semantic ToolEffect — write/update/destructive tiers
MarioCadenas Apr 24, 2026
a677c0e
feat(appkit): fromPlugin() DX, runAgent plugins arg, shared toolkit-r…
MarioCadenas Apr 21, 2026
f45ac92
refactor(appkit): use consumeAdapterStream in core/run-agent
MarioCadenas Apr 23, 2026
3e23d0c
fix(agents): update agents.ts imports to core/agent/ paths after Opti…
MarioCadenas May 4, 2026
e22db62
feat(appkit): sub-agent approval gate and HttpToolExecutor path (SDK)
MarioCadenas May 4, 2026
62dc7f3
fix(appkit): forward all sub-agent events except metadata (SDK)
MarioCadenas May 4, 2026
732498d
refactor(appkit): move agent runtime to core/agent/
MarioCadenas Apr 29, 2026
3e4eaca
refactor(appkit): extract AgentRunner + ToolExecutor strategy
MarioCadenas Apr 29, 2026
8993db2
refactor(appkit): split agents.ts helpers into separate modules
MarioCadenas Apr 29, 2026
b739024
feat(appkit): unify on DATABRICKS_SERVING_ENDPOINT_NAME (SDK + templa…
MarioCadenas May 4, 2026
e460364
feat(appkit): reference agent-app, dev-playground chat UI, docs, and …
MarioCadenas Apr 21, 2026
6689715
fix(appkit): align chat clients + template with renamed 'agents' plugin
MarioCadenas Apr 22, 2026
8300070
docs(agents): folder layout on disk, migrate samples, sync API refs
MarioCadenas Apr 23, 2026
730cc3f
docs(appkit): regenerate typedoc API reference for folder-agents loader
MarioCadenas Apr 23, 2026
db9a621
feat(dev-playground): port Smart Dashboard as /smart-dashboard route;…
MarioCadenas Apr 24, 2026
85f221a
feat(dev-playground): stage 2-4 of smart-dashboard demo
MarioCadenas Apr 24, 2026
60c8ea6
feat(appkit): sub-agent approval gate + save view to volume + saved v…
MarioCadenas May 4, 2026
63a19d1
fix(playground): treat missing saved-views dir as empty list, not 500
MarioCadenas Apr 24, 2026
014e529
fix(appkit): forward all sub-agent events except metadata
MarioCadenas May 4, 2026
6879ece
fix(playground): use html2canvas-pro to support oklch() colors
MarioCadenas Apr 24, 2026
af760e2
fix(playground): unwrap DownloadResponse when serving saved-view PNGs
MarioCadenas Apr 24, 2026
887af6f
fix(playground): apply saved view directly from metadata on thumbnail…
MarioCadenas Apr 24, 2026
912fbda
docs(appkit): regenerate typedoc for tool annotations
MarioCadenas Apr 24, 2026
c888e30
feat(playground): revamp smart dashboard with denser charts and actio…
MarioCadenas Apr 24, 2026
789f657
feat(playground): hamburger nav with shared catalog and redesigned home
MarioCadenas Apr 24, 2026
499b20e
feat(playground): tiered approval card — writes vs updates vs destruc…
MarioCadenas Apr 24, 2026
78149a0
fix(playground): pin agent-feed card tints to sRGB hex
MarioCadenas Apr 24, 2026
8b2ee55
fix(playground): gate Tailwind dark: variant on the theme class
MarioCadenas Apr 24, 2026
fb49ee5
fix(playground): stop streaming chat bubbles from pulsing
MarioCadenas Apr 27, 2026
b8be147
chore(playground): migrate dev-playground server to onPluginsReady
MarioCadenas Apr 27, 2026
137d12a
feat(template): scaffold a working starter agent
MarioCadenas Apr 29, 2026
cefe28d
fix(playground, template): import agents from @databricks/appkit/beta
MarioCadenas May 4, 2026
c43aacf
docs: beta agents banner, template stability, and unified typedoc entry
MarioCadenas May 4, 2026
b8db81b
chore: remove plans scratch docs from agents stack branch
MarioCadenas May 4, 2026
4dbe99e
chore(appkit): Biome format load-agents imports
MarioCadenas May 5, 2026
db77efa
chore(appkit): regenerate typedoc and sync lockfile after rebase
MarioCadenas May 7, 2026
e2ded5a
feat(appkit): agent chat UI primitives
hubertzub-db May 8, 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
19 changes: 16 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,24 @@

All notable changes to this project will be documented in this file.

# Changelog
## Unreleased

# Changelog
### appkit

# Changelog
* **appkit:** **Breaking change:** markdown agents must live under `config/agents/<id>/agent.md`. Top-level `config/agents/*.md` is no longer discovered; migrate each file to `<stem>/agent.md`. The reserved folder `config/agents/skills` is ignored until per-agent skills ship.

## [0.25.1](https://github.com/databricks/appkit/compare/v0.25.0...v0.25.1) (2026-04-27)

### appkit

* **appkit:** check isRetryable before retrying in interceptor ([#276](https://github.com/databricks/appkit/issues/276)) ([1c994a6](https://github.com/databricks/appkit/commit/1c994a6d99f397b56e90f1b53df06a61f02b9e82))


## [0.25.0](https://github.com/databricks/appkit/compare/v0.24.0...v0.25.0) (2026-04-23)

### files

* **files:** per-volume in-app policy enforcement ([#197](https://github.com/databricks/appkit/issues/197)) ([f54dca5](https://github.com/databricks/appkit/commit/f54dca5da5af5368c7bcb18745715b54a99d47e9))

# Changelog

Expand Down
64 changes: 64 additions & 0 deletions apps/dev-playground/client/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion apps/dev-playground/client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
"@tanstack/router-plugin": "1.133.22",
"class-variance-authority": "0.7.1",
"clsx": "2.1.1",
"html2canvas": "1.4.1",
"html2canvas-pro": "2.0.2",
"lucide-react": "0.546.0",
"react": "19.2.0",
"react-dom": "19.2.0",
Expand All @@ -30,6 +32,7 @@
},
"devDependencies": {
"@eslint/js": "9.36.0",
"@tailwindcss/postcss": "4.1.17",
"@tanstack/router-cli": "1.133.20",
"@types/node": "24.6.0",
"@types/react": "19.2.2",
Expand All @@ -43,7 +46,6 @@
"postcss": "8.5.6",
"shiki": "3.15.0",
"tailwindcss": "4.1.17",
"@tailwindcss/postcss": "4.1.17",
"typescript": "5.9.3",
"typescript-eslint": "8.45.0",
"vite": "npm:rolldown-vite@7.1.14"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { CheckCircle2Icon } from "lucide-react";
import { useEffect, useState } from "react";

interface ActionToastProps {
/**
* Latest dispatcher-surfaced action summary. Each new value bumps a
* render key so the toast re-animates even if the same message arrives
* twice (e.g. two identical filter calls in a row).
*/
message: string | null;
durationMs?: number;
}

/**
* Non-intrusive bottom-left toast that confirms every agent-driven UI
* action. Silent success was the worst failure mode before: an action
* silently not-applied looked identical to one that worked but didn't
* show its effect.
*/
export function ActionToast({ message, durationMs = 2800 }: ActionToastProps) {
const [visible, setVisible] = useState<{ key: number; text: string } | null>(
null,
);

useEffect(() => {
if (!message) return;
const key = Date.now();
setVisible({ key, text: message });
const t = setTimeout(() => {
setVisible((v) => (v?.key === key ? null : v));
}, durationMs);
return () => {
clearTimeout(t);
};
}, [message, durationMs]);

if (!visible) return null;

return (
<div
key={visible.key}
className="fixed bottom-20 left-4 z-30 rounded-full bg-card border border-border shadow-lg px-3 py-1.5 flex items-center gap-2 animate-in fade-in slide-in-from-bottom-2 duration-200"
>
<CheckCircle2Icon className="h-3.5 w-3.5 text-green-500 shrink-0" />
<span className="text-xs text-foreground">{visible.text}</span>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
import {
AlertTriangleIcon,
ArrowRightIcon,
CalendarIcon,
CrosshairIcon,
DollarSignIcon,
HighlighterIcon,
LightbulbIcon,
MapPinIcon,
MessageSquareIcon,
} from "lucide-react";
import type { FeedAction } from "../lib/feed-actions";

type Variant = "insight" | "anomaly";
type Severity = "low" | "medium" | "high";

interface ActionableCardProps {
variant: Variant;
severity?: Severity;
title: string;
description: string;
actions: FeedAction[];
/** Fired for non-ask actions. Route applies them to dashboard state. */
onAction: (action: FeedAction) => void;
/** Fired for `ask` actions. Route forwards the prompt to the chat drawer. */
onAsk: (prompt: string) => void;
}

// Backgrounds are written as arbitrary 8-digit hex (e.g. `bg-[#eff6ff80]`)
// instead of Tailwind's `/N` alpha shorthand. Rationale: `bg-blue-50/50`
// compiles in Tailwind v4 to a pair — an sRGB hex fallback and a
// `@supports (color-mix)` override that re-mixes in oklab over the oklch
// palette token. Browsers that support `color-mix` (recent Chrome/Arc) take
// the oklab path; older embedded Chromiums (e.g. Cursor's built-in browser
// at the time of writing) fall through to the sRGB hex. Because oklab and
// sRGB interpolation produce visibly different tints — especially against
// the dark `--card` token — the same card ends up looking different in each
// browser. Pinning the colour to a literal hex (no `/N`, no @supports
// override) keeps all browsers on the same sRGB path and therefore the same
// visual result.
const INSIGHT_STYLES = {
border: "border-blue-200 dark:border-blue-900",
bg: "bg-[#eff6ff80] dark:bg-[#1624564d]",
icon: "text-blue-500",
};

const ANOMALY_STYLES: Record<
Severity,
{ border: string; bg: string; icon: string; badge: string }
> = {
low: {
border: "border-yellow-200 dark:border-yellow-900",
bg: "bg-[#fefce880] dark:bg-[#4320044d]",
icon: "text-yellow-500",
badge:
"bg-yellow-100 text-yellow-700 dark:bg-yellow-900/50 dark:text-yellow-400",
},
medium: {
border: "border-orange-200 dark:border-orange-900",
bg: "bg-[#fff7ed80] dark:bg-[#4413064d]",
icon: "text-orange-500",
badge:
"bg-orange-100 text-orange-700 dark:bg-orange-900/50 dark:text-orange-400",
},
high: {
border: "border-red-200 dark:border-red-900",
bg: "bg-[#fef2f280] dark:bg-[#4608094d]",
icon: "text-red-500",
badge: "bg-red-100 text-red-700 dark:bg-red-900/50 dark:text-red-400",
},
};

function iconForAction(kind: FeedAction["kind"]): React.ReactNode {
const cls = "h-3 w-3";
switch (kind) {
case "filter_date":
return <CalendarIcon className={cls} />;
case "filter_zip":
return <MapPinIcon className={cls} />;
case "filter_fare":
return <DollarSignIcon className={cls} />;
case "highlight_period":
return <HighlighterIcon className={cls} />;
case "highlight_zone":
return <MapPinIcon className={cls} />;
case "focus_chart":
return <CrosshairIcon className={cls} />;
case "ask":
return <MessageSquareIcon className={cls} />;
}
}

/**
* Action chip for a single feed suggestion. The chip's visual weight depends
* on its kind: structural mutations (filter/highlight/focus) use the primary
* tint, `ask` uses a neutral outline so the user can tell "this opens the
* chat" from "this changes the dashboard" without reading the label.
*/
function ActionChip({
action,
onAction,
onAsk,
}: {
action: FeedAction;
onAction: (a: FeedAction) => void;
onAsk: (prompt: string) => void;
}) {
const isAsk = action.kind === "ask";
const isHighlight =
action.kind === "highlight_period" || action.kind === "highlight_zone";

return (
<button
type="button"
onClick={() => {
if (isAsk) onAsk(action.prompt);
else onAction(action);
}}
className={`inline-flex items-center gap-1 text-[11px] font-medium px-2 py-1 rounded-md transition-colors ${
isAsk
? "border border-border bg-background text-foreground/80 hover:bg-muted hover:text-foreground"
: isHighlight
? "bg-amber-100 text-amber-800 hover:bg-amber-200 dark:bg-amber-900/40 dark:text-amber-200 dark:hover:bg-amber-900/60"
: "bg-primary/10 text-primary hover:bg-primary/20"
}`}
>
{iconForAction(action.kind)}
<span>{action.label}</span>
{isAsk && <ArrowRightIcon className="h-3 w-3 opacity-70" />}
</button>
);
}

export function ActionableCard({
variant,
severity,
title,
description,
actions,
onAction,
onAsk,
}: ActionableCardProps) {
const isAnomaly = variant === "anomaly";
const styles = isAnomaly
? ANOMALY_STYLES[severity ?? "low"]
: { ...INSIGHT_STYLES, badge: "" };

return (
<div className={`rounded-lg border ${styles.border} ${styles.bg} p-3`}>
<div className="flex items-start gap-2 mb-2">
{isAnomaly ? (
<AlertTriangleIcon
className={`h-4 w-4 ${styles.icon} mt-0.5 shrink-0`}
/>
) : (
<LightbulbIcon className={`h-4 w-4 ${styles.icon} mt-0.5 shrink-0`} />
)}
<div className="min-w-0 flex-1">
<div className="flex items-start gap-2">
<p className="text-sm font-medium text-foreground leading-tight flex-1">
{title}
</p>
{isAnomaly && severity && (
<span
className={`text-[10px] font-medium px-1.5 py-0.5 rounded shrink-0 ${styles.badge}`}
>
{severity}
</span>
)}
</div>
<p className="text-xs text-muted-foreground mt-1 leading-relaxed">
{description}
</p>
</div>
</div>

{actions.length > 0 && (
<div className="flex flex-wrap gap-1.5 pl-6">
{actions.map((action, i) => (
<ActionChip
key={`${action.kind}-${i}-${action.label}`}
action={action}
onAction={onAction}
onAsk={onAsk}
/>
))}
</div>
)}
</div>
);
}
Loading