Skip to content
Open
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
55 changes: 55 additions & 0 deletions examples/playground/components/playground/AlphaFeaturesSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,12 @@ import {
LayoutList,
BarChart2,
ChevronRight,
Layers,
KeyRound,
} from "lucide-react";
import { Switch } from "@/components/ui/switch";
import { Label } from "@/components/ui/label";
import { Input } from "@/components/ui/input";
import { Badge } from "@/components/ui/badge";
import {
Select,
Expand Down Expand Up @@ -225,6 +228,58 @@ export function AlphaFeaturesSection({

<div className="h-px bg-zinc-100 dark:bg-zinc-800 my-2" />

{/* Threads */}
<p className="text-[10px] font-semibold text-zinc-500 dark:text-zinc-400 uppercase tracking-wide mb-1 flex items-center gap-1">
<Layers className="w-3 h-3" /> Threads
</p>
<FeatureRow
id="alpha-concurrent-threads"
icon={Layers}
label="Concurrent Threads"
description="Each thread streams independently — switch away mid-response"
checked={alphaConfig.concurrentThreads}
onCheckedChange={(v) => onUpdate("concurrentThreads", v)}
/>

<div className="h-px bg-zinc-100 dark:bg-zinc-800 my-2" />

{/* YourGPT Auth */}
<p className="text-[10px] font-semibold text-zinc-500 dark:text-zinc-400 uppercase tracking-wide mb-1 flex items-center gap-1">
<KeyRound className="w-3 h-3" /> YourGPT Auth
</p>
<FeatureRow
id="alpha-ygpt-auth"
icon={KeyRound}
label="Use yourgptConfig"
description="Creates sessions via YourGPT createSession API (overrides onCreateSession)"
checked={alphaConfig.yourgptAuthEnabled}
onCheckedChange={(v) => onUpdate("yourgptAuthEnabled", v)}
/>
{alphaConfig.yourgptAuthEnabled && (
<div className="space-y-1.5 pl-8 pr-1 pb-1">
<Label className="text-[10px] text-zinc-500">API Key</Label>
<Input
type="password"
value={alphaConfig.yourgptApiKey}
onChange={(e) => onUpdate("yourgptApiKey", e.target.value)}
placeholder="ygpt_..."
className="h-7 text-xs"
/>
<Label className="text-[10px] text-zinc-500">Widget UID</Label>
<Input
value={alphaConfig.yourgptWidgetUid}
onChange={(e) => onUpdate("yourgptWidgetUid", e.target.value)}
placeholder="wgt_..."
className="h-7 text-xs"
/>
<p className="text-[9px] text-zinc-400">
Stored in localStorage. Reload chat after saving.
</p>
</div>
)}

<div className="h-px bg-zinc-100 dark:bg-zinc-800 my-2" />

{/* Tools */}
<p className="text-[10px] font-semibold text-zinc-500 dark:text-zinc-400 uppercase tracking-wide mb-1 flex items-center gap-1">
<Zap className="w-3 h-3" /> Advanced Tools
Expand Down
13 changes: 12 additions & 1 deletion examples/playground/components/playground/CopilotSidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -96,12 +96,23 @@ export function CopilotSidebar({
<div className="flex-1 min-h-0 h-full rounded-2xl overflow-hidden shadow-[0_0_10px_0_rgba(0,0,0,0.05)] border">
<CopilotProvider
debug={true}
key={`${selectedProvider}-${selectedOpenRouterModel}`} // Force re-mount when provider or model changes
key={`${selectedProvider}-${selectedOpenRouterModel}-${alphaConfig.concurrentThreads ? "ct" : "st"}-${alphaConfig.yourgptAuthEnabled ? "ygpt" : "local"}`} // Force re-mount when provider, model, thread mode, or auth mode changes
runtimeUrl={runtimeUrl}
headers={runtimeHeaders}
systemPrompt={systemPrompt}
maxIterations={5}
onError={handleError}
{...(alphaConfig.yourgptAuthEnabled &&
alphaConfig.yourgptApiKey &&
alphaConfig.yourgptWidgetUid
? {
yourgptConfig: {
apiKey: alphaConfig.yourgptApiKey,
widgetUid: alphaConfig.yourgptWidgetUid,
},
}
: {})}
concurrentThreads={alphaConfig.concurrentThreads}
messageHistory={
alphaConfig.compactionStrategy !== "none"
? { strategy: alphaConfig.compactionStrategy }
Expand Down
4 changes: 4 additions & 0 deletions examples/playground/lib/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,10 @@ export const INITIAL_ALPHA_CONFIG: AlphaConfig = {
compactionStrategy: "none",
sessionPersistence: false,
contextStats: false,
concurrentThreads: false,
yourgptAuthEnabled: false,
yourgptApiKey: "",
yourgptWidgetUid: "",
hiddenAnalytics: false,
deferredSearch: false,
customMessageView: false,
Expand Down
6 changes: 6 additions & 0 deletions examples/playground/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,12 @@ export interface AlphaConfig {
compactionStrategy: CompactionStrategy;
sessionPersistence: boolean;
contextStats: boolean;
// Threads
concurrentThreads: boolean;
// YourGPT session auth (replaces onCreateSession callback on the provider)
yourgptAuthEnabled: boolean;
yourgptApiKey: string;
yourgptWidgetUid: string;
// Tools
hiddenAnalytics: boolean;
deferredSearch: boolean;
Expand Down
4 changes: 2 additions & 2 deletions examples/playground/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@
"@radix-ui/react-switch": "^1.2.3",
"@radix-ui/react-tabs": "^1.1.13",
"@tailwindcss/typography": "^0.5.19",
"@yourgpt/copilot-sdk": "^2.1.8",
"@yourgpt/llm-sdk": "^2.1.8",
"@yourgpt/copilot-sdk": "workspace:*",
"@yourgpt/llm-sdk": "workspace:*",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"cmdk": "^1.1.1",
Expand Down
5 changes: 4 additions & 1 deletion packages/copilot-sdk/src/chat/AbstractAgentLoop.ts
Original file line number Diff line number Diff line change
Expand Up @@ -399,9 +399,12 @@ export class AbstractAgentLoop implements AgentLoopActions {
throw new Error("Tool execution cancelled");
}

// Pass signal and approvalData to handler via context
// Pass signal, threadId, and approvalData to handler via context.
// threadId lets handlers scope per-run state (e.g. per-thread tab
// pinning when multiple threads stream concurrently).
const result = await tool.handler(toolCall.args, {
signal: this.abortController?.signal,
threadId: this.config.getThreadId?.(),
data: { toolCallId: toolCall.id },
approvalData,
});
Expand Down
16 changes: 16 additions & 0 deletions packages/copilot-sdk/src/chat/ChatWithTools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,14 @@ export interface ChatWithToolsConfig {
transport?: ChatTransport;
/** Custom error message extractor for non-2xx API responses */
parseError?: (status: number, body: unknown) => string | null | undefined;
/**
* Override the thread id exposed to tool handlers (`context.threadId`).
* Called once per tool invocation. Defaults to `chat.threadId` (the backend
* session id). Useful for framework adapters that maintain a stable
* UI-level thread id separate from the backend session id — e.g. when a
* local id is assigned client-side before the server issues its own.
*/
getThreadId?: () => string | undefined;
}

/**
Expand Down Expand Up @@ -128,6 +136,14 @@ export class ChatWithTools {
{
maxIterations: config.maxIterations ?? 20,
tools: config.tools,
// Expose this chat's current threadId to tool handlers via
// ToolContext.threadId. Read lazily per invocation so it reflects
// the id assigned by the server mid-stream (thread:created).
// If the caller provided a getThreadId override (e.g. the React
// provider exposing its stable registry key), prefer that.
getThreadId: config.getThreadId
? () => config.getThreadId!()
: () => this.chat?.threadId,
},
{
onExecutionsChange: (executions) => {
Expand Down
5 changes: 5 additions & 0 deletions packages/copilot-sdk/src/chat/classes/AbstractChat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,11 @@ export class AbstractChat<T extends UIMessage = UIMessage> {
return this.transport.isStreaming();
}

/** The thread id currently associated with this chat, if any. */
get threadId(): string | undefined {
return this.config.threadId;
}

// ============================================
// Public Actions
// ============================================
Expand Down
7 changes: 7 additions & 0 deletions packages/copilot-sdk/src/chat/types/tool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,13 @@ export interface AgentLoopConfig {
tools?: ToolDefinition[];
/** Max tool executions to keep in memory (default: 100). Oldest are pruned. */
maxExecutionHistory?: number;
/**
* Returns the current thread id for tool handler context. Called once per
* tool invocation. Needed by consumers that need to scope per-tool state
* (e.g. per-thread browser-tab pinning in the Chrome extension). When
* omitted, `context.threadId` is undefined in handlers.
*/
getThreadId?: () => string | undefined;
}

/**
Expand Down
Loading