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
2 changes: 1 addition & 1 deletion apps/api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@anthropic-ai/sdk": "^0.71.2",
"@anthropic-ai/sdk": "^0.72.1",
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.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if version 0.72.1 exists and get package info
curl -s "https://registry.npmjs.org/@anthropic-ai/sdk/0.72.1" | jq -r '.version, .time.["0.72.1"], .dist.tarball // "Not found"' 2>/dev/null || echo "Version check failed"

# Also check the 0.71.2 version for comparison
echo "---"
curl -s "https://registry.npmjs.org/@anthropic-ai/sdk/0.71.2" | jq -r '.version, .time.["0.71.2"] // "Not found"' 2>/dev/null || echo "Version check failed"

Repository: superset-sh/superset

Length of output: 109


🌐 Web query:

@anthropic-ai/sdk 0.72.1 release notes breaking changes changelog

💡 Result:

For @anthropic-ai/sdk v0.72.1 (published ~late Jan 2026 per UNPKG’s “latest” tag), the canonical release notes are in the package’s CHANGELOG.md included in the npm tarball and browsable on UNPKG. (app.unpkg.com)

  • Changelog (v0.72.1): available at the package file CHANGELOG.md (in the v0.72.1 package listing). (app.unpkg.com)
  • Breaking changes: I wasn’t able to reliably extract the contents of the CHANGELOG.md in this environment (the “raw” fetch endpoint errors), so I can’t confirm whether v0.72.1 itself lists any breaking changes. (app.unpkg.com)

If you paste the ## 0.72.1 section from CHANGELOG.md here, I’ll summarize it and call out any breaking changes.

Citations:


🌐 Web query:

@anthropic-ai/sdk 0.72.1 security vulnerabilities advisories

💡 Result:

As of Feb 4, 2026, there are no published security advisories for the GitHub repo that backs @anthropic-ai/sdk (Anthropic’s TypeScript SDK), and no known direct vulnerabilities have been reported for @anthropic-ai/sdk@0.72.1 in major package vulnerability trackers:

  • GitHub Security Advisories (repo: anthropics/anthropic-sdk-typescript): “There aren’t any published security advisories.” [1]
  • Snyk (npm package @anthropic-ai/sdk): “No direct vulnerabilities have been found” and the “latest non vulnerable version” is 0.72.1. [2]
  • ReversingLabs (package scan): reports no known vulnerabilities detected for @anthropic-ai/sdk (latest). [3]

If you need “actionable” assurance, run a dependency scan (e.g., npm audit, Snyk/Dependabot) for transitive issues, since “no direct vulnerabilities” does not cover vulnerable dependencies. [2]


🏁 Script executed:

#!/bin/bash
# Try fetching CHANGELOG.md from UNPKG CDN
curl -s "https://unpkg.com/@anthropic-ai/sdk@0.72.1/CHANGELOG.md" | head -100

echo "---"

# Alternative: search GitHub repository for the changelog
curl -s "https://raw.githubusercontent.com/anthropics/anthropic-sdk-typescript/main/CHANGELOG.md" | grep -A 20 "## 0.72" | head -30

Repository: superset-sh/superset

Length of output: 9095


Test structured output format migration from output_format to output_config.

Version 0.72.0 includes a breaking change: the Messages API migrated from using output_format to output_config for structured outputs. If your code uses structured outputs or references the OutputFormat export, this upgrade will require code changes. Review the commit and test your implementation to ensure compatibility.

🤖 Prompt for AI Agents
In `@apps/api/package.json` at line 14, The upgrade to `@anthropic-ai/sdk` ^0.72.1
changed the Messages API structured output from output_format to output_config,
so search the codebase for the OutputFormat type/export and any uses of
"output_format" (e.g., in calls to the Messages API or helper functions) and
replace them with the new "output_config" shape and types (adjust imports from
OutputFormat to the new types or remove if renamed); update call sites that
build structured output requests to pass output_config instead of output_format
and adapt tests/type assertions to the new schema for structured outputs to
restore compatibility with the SDK v0.72.x API.

"@electric-sql/client": "https://pkg.pr.new/@electric-sql/client@3724",
"@linear/sdk": "^68.1.0",
"@modelcontextprotocol/sdk": "^1.25.3",
Expand Down
9 changes: 6 additions & 3 deletions apps/desktop/src/renderer/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,9 @@ const handleDeepLink = (path: string) => {
console.log("[deep-link] Navigating to:", path);
router.navigate({ to: path });
};
if (window.ipcRenderer) {
window.ipcRenderer.on("deep-link-navigate", handleDeepLink);
const ipcRenderer = window.ipcRenderer as typeof window.ipcRenderer | undefined;
if (ipcRenderer) {
ipcRenderer.on("deep-link-navigate", handleDeepLink);
} else {
reportBootError(
"Renderer preload not available (window.ipcRenderer missing)",
Expand All @@ -52,7 +53,9 @@ if (window.ipcRenderer) {
if (import.meta.hot) {
import.meta.hot.dispose(() => {
unsubscribe();
window.ipcRenderer?.off?.("deep-link-navigate", handleDeepLink);
if (ipcRenderer) {
ipcRenderer.off("deep-link-navigate", handleDeepLink);
}
cleanupBootErrorHandling();
});
}
Expand Down
2 changes: 1 addition & 1 deletion apps/docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
"fumadocs-core": "16.4.7",
"fumadocs-mdx": "14.2.5",
"fumadocs-ui": "16.4.7",
"lucide-react": "^0.560.0",
"lucide-react": "^0.563.0",
"next": "^16.0.10",
"posthog-js": "1.310.1",
"react": "19.1.0",
Expand Down
2 changes: 1 addition & 1 deletion apps/marketing/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
"geist": "^1.5.1",
"gray-matter": "^4.0.3",
"import-in-the-middle": "2.0.1",
"lucide-react": "^0.560.0",
"lucide-react": "^0.563.0",
"next": "^16.0.10",
"next-mdx-remote": "^5.0.0",
"next-themes": "^0.4.6",
Expand Down
2 changes: 1 addition & 1 deletion apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"geist": "^1.5.1",
"import-in-the-middle": "2.0.1",
"jose": "^6.1.3",
"lucide-react": "^0.560.0",
"lucide-react": "^0.563.0",
"next": "^16.0.10",
"next-themes": "^0.4.6",
"posthog-js": "1.310.1",
Expand Down
96 changes: 80 additions & 16 deletions bun.lock

Large diffs are not rendered by default.

38 changes: 38 additions & 0 deletions packages/ai-chat/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"name": "@superset/ai-chat",
"version": "0.0.1",
"description": "Shared AI chat hooks and utilities",
"type": "module",
"main": "./src/index.ts",
"exports": {
".": "./src/index.ts",
"./components": "./src/components/index.ts",
"./stream": "./src/stream/index.ts"
},
"scripts": {
"test": "bun test",
"typecheck": "tsc --noEmit --emitDeclarationOnly false"
},
"dependencies": {
"@anthropic-ai/claude-agent-sdk": "^0.2.19",
"@anthropic-ai/sdk": "^0.72.1",
"@durable-streams/client": "^0.2.0",
"@durable-streams/state": "^0.2.0",
"@superset/ui": "workspace:*",
"@tanstack/db": "0.5.22",
"@tanstack/react-db": "0.1.66",
"lucide-react": "^0.563.0",
"zod": "^4.3.5"
},
"devDependencies": {
"@superset/typescript": "workspace:*",
"@types/node": "^24.9.1",
"@types/react": "~19.1.0",
"bun-types": "^1.3.1",
"react": "19.1.0",
"typescript": "^5.9.3"
},
"peerDependencies": {
"react": "^18.0.0 || ^19.0.0"
}
}
137 changes: 137 additions & 0 deletions packages/ai-chat/src/components/ChatInput/ChatInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
/**
* Chat input component with send button
*/

import { Button } from "@superset/ui/button";
import { Textarea } from "@superset/ui/textarea";
import { cn } from "@superset/ui/utils";
import { Send } from "lucide-react";
import { type KeyboardEvent, useCallback, useRef, useState } from "react";

export interface ChatInputProps {
onSend: (content: string) => void;
onTypingChange?: (isTyping: boolean) => void;
disabled?: boolean;
placeholder?: string;
className?: string;
/** Button style: "icon" shows Send icon, "text" shows "Send" label */
buttonVariant?: "icon" | "text";
/** Auto-resize textarea as content grows (default: true) */
autoResize?: boolean;
/** Controlled value (optional - if provided, component is controlled) */
value?: string;
/** Controlled onChange (optional - required if value is provided) */
onChange?: (value: string) => void;
}

export function ChatInput({
onSend,
onTypingChange,
disabled = false,
placeholder = "Type a message...",
className,
buttonVariant = "icon",
autoResize = true,
value: controlledValue,
onChange: controlledOnChange,
}: ChatInputProps) {
const [internalValue, setInternalValue] = useState("");
const textareaRef = useRef<HTMLTextAreaElement>(null);
const typingTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);

// Support both controlled and uncontrolled modes
const isControlled = controlledValue !== undefined;
const value = isControlled ? controlledValue : internalValue;
const setValue = isControlled
? (v: string) => controlledOnChange?.(v)
: setInternalValue;

const handleSubmit = useCallback(() => {
const trimmed = value.trim();
if (!trimmed || disabled) return;

onSend(trimmed);
setValue("");
onTypingChange?.(false);

// Reset textarea height and focus
if (textareaRef.current) {
if (autoResize) {
textareaRef.current.style.height = "auto";
}
textareaRef.current.focus();
}
}, [value, disabled, onSend, onTypingChange, autoResize, setValue]);

const handleKeyDown = useCallback(
(e: KeyboardEvent<HTMLTextAreaElement>) => {
if (e.key === "Enter" && !e.shiftKey) {
e.preventDefault();
handleSubmit();
}
},
[handleSubmit],
);

const handleChange = useCallback(
(e: React.ChangeEvent<HTMLTextAreaElement>) => {
const newValue = e.target.value;
setValue(newValue);

// Auto-resize textarea
if (autoResize) {
const textarea = e.target;
textarea.style.height = "auto";
textarea.style.height = `${Math.min(textarea.scrollHeight, 200)}px`;
}

// Typing indicator with debounce
if (typingTimeoutRef.current) {
clearTimeout(typingTimeoutRef.current);
}

if (newValue.trim()) {
onTypingChange?.(true);
typingTimeoutRef.current = setTimeout(() => {
onTypingChange?.(false);
}, 2000);
} else {
onTypingChange?.(false);
}
Comment on lines +88 to +100
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.

⚠️ Potential issue | 🟡 Minor

Memory leak: Clear typing timeout on unmount.

The typingTimeoutRef timeout is never cleared when the component unmounts. If unmount occurs while the timeout is pending, the callback fires on an unmounted component, potentially causing state updates on unmounted components or memory leaks.

Proposed fix: Add cleanup effect
+import { type KeyboardEvent, useCallback, useEffect, useRef, useState } from "react";
-import { type KeyboardEvent, useCallback, useRef, useState } from "react";

Add a cleanup effect after the refs:

 	const textareaRef = useRef<HTMLTextAreaElement>(null);
 	const typingTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);

+	// Cleanup timeout on unmount
+	useEffect(() => {
+		return () => {
+			if (typingTimeoutRef.current) {
+				clearTimeout(typingTimeoutRef.current);
+			}
+		};
+	}, []);
+
 	// Support both controlled and uncontrolled modes
🤖 Prompt for AI Agents
In `@packages/ai-chat/src/components/ChatInput/ChatInput.tsx` around lines 88 -
100, The typing timeout stored in typingTimeoutRef inside the ChatInput
component is never cleared on unmount; add a cleanup effect in the ChatInput
component that checks typingTimeoutRef.current and calls clearTimeout(...) and
sets typingTimeoutRef.current = null so any pending timeout is cancelled on
unmount and the onTypingChange callback cannot fire after unmounting. Ensure the
effect has an empty dependency array so it only runs on mount/unmount and
references typingTimeoutRef and onTypingChange by stable refs or directly if
they are stable in the component.

},
[onTypingChange, autoResize, setValue],
);

return (
<div className={cn("flex gap-2 items-end", className)}>
<Textarea
ref={textareaRef}
value={value}
onChange={handleChange}
onKeyDown={handleKeyDown}
placeholder={placeholder}
disabled={disabled}
rows={1}
className="min-h-[40px] max-h-[200px] resize-none"
/>
{buttonVariant === "icon" ? (
<Button
size="icon"
onClick={handleSubmit}
disabled={disabled || !value.trim()}
className="shrink-0"
>
<Send className="h-4 w-4" />
</Button>
) : (
<Button
onClick={handleSubmit}
disabled={disabled || !value.trim()}
size="default"
>
Send
</Button>
)}
</div>
);
}
1 change: 1 addition & 0 deletions packages/ai-chat/src/components/ChatInput/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { ChatInput, type ChatInputProps } from "./ChatInput";
82 changes: 82 additions & 0 deletions packages/ai-chat/src/components/PresenceBar/PresenceBar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/**
* Presence bar showing viewers and typing indicators
*/

import { Avatar, AvatarFallback, AvatarImage } from "@superset/ui/avatar";
import { cn } from "@superset/ui/utils";
import type { PresenceUser } from "../../types";

export interface PresenceBarProps {
viewers: PresenceUser[];
typingUsers: PresenceUser[];
className?: string;
}

function getInitials(name: string): string {
return name
.split(" ")
.map((n) => n[0])
.join("")
.toUpperCase()
.slice(0, 2);
}

export function PresenceBar({
viewers,
typingUsers,
className,
}: PresenceBarProps) {
if (viewers.length === 0 && typingUsers.length === 0) {
return null;
}

return (
<div
className={cn(
"flex items-center gap-2 px-4 py-2 border-b border-border bg-muted/50",
className,
)}
>
{/* Viewers */}
{viewers.length > 0 && (
<div className="flex items-center gap-1">
<div className="flex -space-x-2">
{viewers.slice(0, 5).map((user) => (
<Avatar
key={user.userId}
className="h-6 w-6 border-2 border-background"
>
{user.image && <AvatarImage src={user.image} />}
<AvatarFallback className="text-xs">
{getInitials(user.name)}
</AvatarFallback>
</Avatar>
))}
</div>
{viewers.length > 5 && (
<span className="text-xs text-muted-foreground">
+{viewers.length - 5}
</span>
)}
<span className="text-xs text-muted-foreground ml-1">viewing</span>
</div>
)}

{/* Typing indicator */}
{typingUsers.length > 0 && (
<div className="flex items-center gap-1 ml-auto">
<span className="text-xs text-muted-foreground">
{typingUsers.length === 1
? `${typingUsers[0]?.name} is typing`
: `${typingUsers.length} people typing`}
</span>
<span className="flex gap-0.5">
<span className="h-1.5 w-1.5 rounded-full bg-primary animate-bounce [animation-delay:-0.3s]" />
<span className="h-1.5 w-1.5 rounded-full bg-primary animate-bounce [animation-delay:-0.15s]" />
<span className="h-1.5 w-1.5 rounded-full bg-primary animate-bounce" />
</span>
</div>
)}
</div>
);
}
1 change: 1 addition & 0 deletions packages/ai-chat/src/components/PresenceBar/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { PresenceBar, type PresenceBarProps } from "./PresenceBar";
2 changes: 2 additions & 0 deletions packages/ai-chat/src/components/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { ChatInput, type ChatInputProps } from "./ChatInput";
export { PresenceBar, type PresenceBarProps } from "./PresenceBar";
3 changes: 3 additions & 0 deletions packages/ai-chat/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from "./components";
export * from "./stream";
export * from "./types";
Loading