@calliopelabs/ui 0.12.0 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +68 -3
- package/dist/agent/AgentChat.d.ts +56 -0
- package/dist/agent/AgentChat.js +76 -0
- package/dist/agent/AgentLauncher.d.ts +25 -0
- package/dist/agent/AgentLauncher.js +25 -0
- package/dist/agent/AgentProvider.d.ts +82 -0
- package/dist/agent/AgentProvider.js +176 -0
- package/dist/agent/CalliopeAgent.d.ts +43 -0
- package/dist/agent/CalliopeAgent.js +111 -0
- package/dist/agent/controller.d.ts +148 -0
- package/dist/agent/controller.js +463 -0
- package/dist/agent/copilotkit/CopilotKitAgentProvider.d.ts +41 -0
- package/dist/agent/copilotkit/CopilotKitAgentProvider.js +145 -0
- package/dist/agent/copilotkit/index.d.ts +6 -0
- package/dist/agent/copilotkit/index.js +6 -0
- package/dist/agent/engine.d.ts +41 -0
- package/dist/agent/engine.js +93 -0
- package/dist/agent/index.d.ts +15 -0
- package/dist/agent/index.js +15 -0
- package/dist/agent/mock.d.ts +34 -0
- package/dist/agent/mock.js +112 -0
- package/dist/agent/surface.d.ts +30 -0
- package/dist/agent/surface.js +90 -0
- package/dist/agent/tool.d.ts +98 -0
- package/dist/agent/tool.js +81 -0
- package/dist/appearance/brands.d.ts +14 -12
- package/dist/appearance/brands.js +18 -51
- package/dist/chat/AgentProgress.d.ts +19 -0
- package/dist/chat/AgentProgress.js +34 -0
- package/dist/chat/Composer.d.ts +31 -0
- package/dist/chat/Composer.js +44 -0
- package/dist/chat/MessageBubble.d.ts +23 -0
- package/dist/chat/MessageBubble.js +26 -0
- package/dist/chat/MessageList.d.ts +36 -0
- package/dist/chat/MessageList.js +68 -0
- package/dist/chat/ModelPicker.d.ts +17 -0
- package/dist/chat/ModelPicker.js +13 -0
- package/dist/chat/ToolCallCard.d.ts +27 -0
- package/dist/chat/ToolCallCard.js +52 -0
- package/dist/chat/model.d.ts +123 -0
- package/dist/chat/model.js +337 -0
- package/dist/chat/patch.d.ts +12 -0
- package/dist/chat/patch.js +149 -0
- package/dist/chat/useAgUiRun.d.ts +52 -0
- package/dist/chat/useAgUiRun.js +137 -0
- package/dist/index.d.ts +11 -2
- package/dist/index.js +11 -1
- package/dist/primitives/Button.d.ts +1 -1
- package/dist/provider/messages.d.ts +83 -1
- package/dist/provider/messages.js +91 -0
- package/dist/theme/mappings/palette.d.ts +52 -0
- package/dist/theme/mappings/palette.js +208 -0
- package/dist/theme/mappings/render.d.ts +9 -0
- package/dist/theme/mappings/render.js +520 -0
- package/dist/theme/mappings/targets.d.ts +43 -0
- package/dist/theme/mappings/targets.js +222 -0
- package/dist/theme/tokens.d.ts +10 -9
- package/dist/theme/tokens.generated.d.ts +353 -0
- package/dist/theme/tokens.generated.js +357 -0
- package/dist/theme/tokens.js +20 -264
- package/dist/tokens/README.jupyterlab.md +125 -0
- package/dist/tokens/README.md +15 -0
- package/dist/tokens/README.native.md +29 -0
- package/dist/tokens/README.plain.md +84 -0
- package/dist/tokens/README.python.md +33 -0
- package/dist/tokens/README.vscode.md +114 -0
- package/dist/tokens/jupyterlab.css +1710 -0
- package/dist/tokens/plain.css +930 -0
- package/dist/tokens/tokens.native.d.ts +85 -0
- package/dist/tokens/tokens.native.js +773 -0
- package/dist/tokens/tokens.py +1043 -0
- package/dist/tokens/vscode.css +1138 -0
- package/dist/workflow/canvas/WorkflowCanvas.js +41 -1
- package/dist/workflow/definition.d.ts +20 -0
- package/dist/workflow/definition.js +44 -0
- package/dist/workflow/editor/WorkflowInspector.d.ts +21 -3
- package/dist/workflow/editor/WorkflowInspector.js +13 -5
- package/dist/workflow/editor/WorkflowWorkspace.d.ts +28 -3
- package/dist/workflow/editor/WorkflowWorkspace.js +75 -16
- package/package.json +35 -2
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { useProduct } from "../appearance/AppearanceProvider.js";
|
|
5
|
+
import { cn } from "../lib/cn.js";
|
|
6
|
+
import { Drawer, DrawerContent, DrawerTitle } from "../overlays/Drawer.js";
|
|
7
|
+
import { useUI } from "../provider/UIProvider.js";
|
|
8
|
+
import { useRailPlacement } from "../shell/placement.js";
|
|
9
|
+
import { RailPanel } from "../shell/RailPanel.js";
|
|
10
|
+
import { AgentChat, NewConversationButton } from "./AgentChat.js";
|
|
11
|
+
import { AgentLauncher } from "./AgentLauncher.js";
|
|
12
|
+
import { useAgentController } from "./AgentProvider.js";
|
|
13
|
+
import { useMediaQuery, useOpenState, useShortcut } from "./surface.js";
|
|
14
|
+
/**
|
|
15
|
+
* The embedded agent of a Calliope AI product, one primitive for every
|
|
16
|
+
* surface: a floating bubble, the shell's right rail, inline in a page or a
|
|
17
|
+
* page of its own. Reads the nearest `CalliopeAgentProvider` (or the
|
|
18
|
+
* CopilotKit one); draws everything with the chat kit and the product's
|
|
19
|
+
* tokens, so it carries each product's and each deployment's brand. Answers
|
|
20
|
+
* that land while it is closed count as unread on the launcher.
|
|
21
|
+
*/
|
|
22
|
+
export function CalliopeAgent({ mode, open: openProp, defaultOpen, onOpenChange, persistKey, shortcut = "mod+/", launcherIcon, hideLauncher = false, railWidth = 380, onRailWidthChange, expanded, onExpandedChange, hideHeader, title, className, ...chat }) {
|
|
23
|
+
const controller = useAgentController();
|
|
24
|
+
if (!controller) {
|
|
25
|
+
throw new Error("CalliopeAgent needs CalliopeAgentProvider or CopilotKitAgentProvider above it.");
|
|
26
|
+
}
|
|
27
|
+
const { messages } = useUI();
|
|
28
|
+
const product = useProduct();
|
|
29
|
+
const name = title ?? messages.agent.title(product?.name);
|
|
30
|
+
const floating = mode === "bubble" || mode === "rail";
|
|
31
|
+
const [open, setStoredOpen] = useOpenState({
|
|
32
|
+
...(openProp !== undefined ? { open: openProp } : {}),
|
|
33
|
+
defaultOpen: defaultOpen ?? !floating,
|
|
34
|
+
...(onOpenChange ? { onOpenChange } : {}),
|
|
35
|
+
...(persistKey ? { persistKey } : {}),
|
|
36
|
+
});
|
|
37
|
+
// The composer takes focus when the person opens the assistant, never on a page load that restores it open.
|
|
38
|
+
const [focusComposer, setFocusComposer] = React.useState(false);
|
|
39
|
+
const setOpen = React.useCallback((next) => {
|
|
40
|
+
setFocusComposer(next);
|
|
41
|
+
setStoredOpen(next);
|
|
42
|
+
}, [setStoredOpen]);
|
|
43
|
+
const shown = !floating || open;
|
|
44
|
+
React.useEffect(() => controller.setVisible(shown), [controller, shown]);
|
|
45
|
+
useShortcut(floating ? shortcut : false, () => setOpen(!open));
|
|
46
|
+
const unread = React.useSyncExternalStore(controller.subscribe, () => controller.getSnapshot().unread, () => 0);
|
|
47
|
+
const panelId = React.useId();
|
|
48
|
+
const launcher = React.useRef(null);
|
|
49
|
+
const refocus = React.useRef(false);
|
|
50
|
+
const close = () => {
|
|
51
|
+
refocus.current = true;
|
|
52
|
+
setOpen(false);
|
|
53
|
+
};
|
|
54
|
+
// Closing from inside the panel hands focus back to the launcher once it is back.
|
|
55
|
+
React.useEffect(() => {
|
|
56
|
+
if (open || !refocus.current)
|
|
57
|
+
return;
|
|
58
|
+
refocus.current = false;
|
|
59
|
+
launcher.current?.focus();
|
|
60
|
+
}, [open]);
|
|
61
|
+
if (mode === "inline" || mode === "page") {
|
|
62
|
+
return (_jsx(AgentChat, { ...chat, title: name, hideHeader: hideHeader ?? mode === "inline", progress: chat.progress ?? (mode === "page" ? "steps" : "line"), className: cn(mode === "inline" ? "h-full rounded-lg border" : "mx-auto h-full w-full max-w-3xl", className) }));
|
|
63
|
+
}
|
|
64
|
+
const launcherButton = !hideLauncher && !open ? (_jsx(AgentLauncher, { ref: launcher, open: open, onOpenChange: setOpen, title: name, unread: unread, controls: panelId, shortcut: shortcut, icon: launcherIcon, className: "fixed right-4 bottom-4 z-40" })) : null;
|
|
65
|
+
if (mode === "rail") {
|
|
66
|
+
return (_jsx(RailSurface, { id: panelId, name: name, open: open, onClose: close, railWidth: railWidth, onRailWidthChange: onRailWidthChange, expanded: expanded, onExpandedChange: onExpandedChange, launcher: launcherButton, className: className, chat: { ...chat, autoFocus: chat.autoFocus ?? focusComposer } }));
|
|
67
|
+
}
|
|
68
|
+
return (_jsx(BubbleSurface, { id: panelId, name: name, open: open, onOpenChange: setOpen, onClose: close, launcher: launcherButton, className: className, chat: { ...chat, autoFocus: chat.autoFocus ?? focusComposer } }));
|
|
69
|
+
}
|
|
70
|
+
function RailSurface({ id, name, open, onClose, railWidth, onRailWidthChange, expanded, onExpandedChange, launcher, className, chat, }) {
|
|
71
|
+
const inDrawer = useRailPlacement() === "drawer";
|
|
72
|
+
return (_jsxs(_Fragment, { children: [_jsx(RailPanel, { label: name, collapsed: !open, onCollapsedChange: (collapsed) => (collapsed ? onClose() : undefined), collapsedMode: "hidden", actions: chat.hideNewConversation ? undefined : (_jsx(NewConversationButton, { className: "text-sidebar-muted-foreground hover:bg-sidebar-accent hover:text-sidebar-foreground" })), width: railWidth, minWidth: 300, maxWidth: 720, ...(onRailWidthChange ? { onWidthChange: onRailWidthChange } : {}), expanded: expanded ?? false, ...(onExpandedChange ? { onExpandedChange } : {}), className: className, children: _jsx("div", { id: id, className: "flex h-full min-h-0 flex-col", children: _jsx(AgentChat, { ...chat, title: name, hideHeader: true,
|
|
73
|
+
// eslint-disable-next-line jsx-a11y/no-autofocus -- the person just opened the assistant to type
|
|
74
|
+
autoFocus: chat.autoFocus, className: "min-h-0 flex-1" }) }) }), !inDrawer && launcher] }));
|
|
75
|
+
}
|
|
76
|
+
function BubbleSurface({ id, name, open, onOpenChange, onClose, launcher, className, chat, }) {
|
|
77
|
+
const wide = useMediaQuery("(min-width: 640px)");
|
|
78
|
+
const panel = React.useRef(null);
|
|
79
|
+
// A non-modal dialog closes on Escape (WAI-ARIA dialog pattern): from inside it, or from
|
|
80
|
+
// nowhere in particular (focus left on the page after a chip it held went away).
|
|
81
|
+
const latestClose = React.useRef(onClose);
|
|
82
|
+
React.useEffect(() => {
|
|
83
|
+
latestClose.current = onClose;
|
|
84
|
+
});
|
|
85
|
+
React.useEffect(() => {
|
|
86
|
+
if (!open || !wide)
|
|
87
|
+
return;
|
|
88
|
+
const onKey = (event) => {
|
|
89
|
+
if (event.key !== "Escape" || event.defaultPrevented)
|
|
90
|
+
return;
|
|
91
|
+
const target = event.target instanceof Node ? event.target : null;
|
|
92
|
+
const inside = target !== null && panel.current?.contains(target);
|
|
93
|
+
if (!inside && target !== document.body)
|
|
94
|
+
return;
|
|
95
|
+
// A confirm dialog inside handles its own Escape.
|
|
96
|
+
if (target instanceof Element &&
|
|
97
|
+
target.closest("[role='alertdialog'], [role='dialog'][aria-modal='true']"))
|
|
98
|
+
return;
|
|
99
|
+
event.preventDefault();
|
|
100
|
+
latestClose.current();
|
|
101
|
+
};
|
|
102
|
+
document.addEventListener("keydown", onKey);
|
|
103
|
+
return () => document.removeEventListener("keydown", onKey);
|
|
104
|
+
}, [open, wide]);
|
|
105
|
+
if (!wide) {
|
|
106
|
+
return (_jsxs(_Fragment, { children: [launcher, _jsx(Drawer, { open: open, onOpenChange: onOpenChange, children: _jsxs(DrawerContent, { side: "bottom", hideCloseButton: true, "aria-describedby": undefined, className: "h-dvh max-h-dvh gap-0 p-0", children: [_jsx(DrawerTitle, { className: "sr-only", children: name }), _jsx(AgentChat, { ...chat, title: name, onClose: onClose, className: "min-h-0 flex-1" })] }) })] }));
|
|
107
|
+
}
|
|
108
|
+
return (_jsxs(_Fragment, { children: [launcher, open && (_jsx("div", { id: id, role: "dialog", "aria-modal": "false", "aria-label": name, "data-slot": "agent-bubble", ref: panel, className: cn("fixed right-4 bottom-4 z-40 flex h-[min(40rem,calc(100dvh-2rem))] w-[min(26rem,calc(100vw-2rem))] flex-col overflow-hidden rounded-lg border bg-card shadow-xl", className), children: _jsx(AgentChat, { ...chat, title: name,
|
|
109
|
+
// eslint-disable-next-line jsx-a11y/no-autofocus -- the person just opened the assistant to type
|
|
110
|
+
autoFocus: chat.autoFocus, onClose: onClose, className: "min-h-0 flex-1" }) }))] }));
|
|
111
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The agent controller: one conversation with one AG-UI agent, as a store a
|
|
3
|
+
* React hook reads. It folds every event the agent emits into the chat kit's
|
|
4
|
+
* `ChatView` (the same reducer every chat surface reads), and owns what the
|
|
5
|
+
* person does: send, stop, regenerate, approve or deny, answer a tool that
|
|
6
|
+
* waits on them, edit the shared state, switch threads. The engine does the
|
|
7
|
+
* running; the controller never touches the network itself.
|
|
8
|
+
*/
|
|
9
|
+
import type { AbstractAgent } from "@ag-ui/client";
|
|
10
|
+
import { type InputContent, type Message } from "@ag-ui/core";
|
|
11
|
+
import { type AgUiEvent, type ChatToolCall, type ChatView } from "../chat/model.js";
|
|
12
|
+
import type { AgentEngine, ToolInvoker } from "./engine.js";
|
|
13
|
+
import { type AgentTool } from "./tool.js";
|
|
14
|
+
/** A prompt offered as a chip: the title shown, the message sent. */
|
|
15
|
+
export interface AgentSuggestion {
|
|
16
|
+
title: string;
|
|
17
|
+
message: string;
|
|
18
|
+
}
|
|
19
|
+
/** Something the agent should know about the page: a readable. */
|
|
20
|
+
export interface AgentContextEntry {
|
|
21
|
+
/** What it is, for the agent ("The open project"). */
|
|
22
|
+
description: string;
|
|
23
|
+
/** Sent as JSON text unless it is a string. */
|
|
24
|
+
value: unknown;
|
|
25
|
+
}
|
|
26
|
+
/** A saved conversation, as a host's history endpoint returns it. */
|
|
27
|
+
export interface AgentThread {
|
|
28
|
+
messages: Message[];
|
|
29
|
+
state?: unknown;
|
|
30
|
+
}
|
|
31
|
+
export interface AgentControllerOptions {
|
|
32
|
+
/** Sent with every run: the model picked, the page. */
|
|
33
|
+
forwardedProps?: () => Record<string, unknown> | undefined;
|
|
34
|
+
/** Loads a thread's history on `switchThread`. Without it a switched-to thread starts empty. */
|
|
35
|
+
loadThread?: (threadId: string) => Promise<AgentThread> | AgentThread;
|
|
36
|
+
/** A run that failed (also shown in the conversation). */
|
|
37
|
+
onError?: (error: unknown) => void;
|
|
38
|
+
/** Every AG-UI event, for the host's telemetry. */
|
|
39
|
+
onEvent?: (event: AgUiEvent) => void;
|
|
40
|
+
}
|
|
41
|
+
export interface AgentSnapshot {
|
|
42
|
+
view: ChatView;
|
|
43
|
+
/** True from send until the agent is done, follow-ups and browser tools included. */
|
|
44
|
+
running: boolean;
|
|
45
|
+
/** Tool calls waiting on the person (human-in-the-loop tools), by id. */
|
|
46
|
+
waiting: readonly string[];
|
|
47
|
+
threadId: string;
|
|
48
|
+
/** Answers that finished while the surface was not visible. */
|
|
49
|
+
unread: number;
|
|
50
|
+
suggestions: readonly AgentSuggestion[];
|
|
51
|
+
tools: readonly AgentTool[];
|
|
52
|
+
}
|
|
53
|
+
export interface SendOptions {
|
|
54
|
+
/** Attachments and other parts after the text (AG-UI input content). */
|
|
55
|
+
parts?: readonly InputContent[];
|
|
56
|
+
}
|
|
57
|
+
type Listener = () => void;
|
|
58
|
+
/** An id unique in this page: crypto when there is one, a counter otherwise. */
|
|
59
|
+
export declare function agentId(prefix?: string): string;
|
|
60
|
+
export declare class AgentController {
|
|
61
|
+
private options;
|
|
62
|
+
private engine;
|
|
63
|
+
private unsubscribe;
|
|
64
|
+
private listeners;
|
|
65
|
+
private view;
|
|
66
|
+
private running;
|
|
67
|
+
private runSeq;
|
|
68
|
+
private tools;
|
|
69
|
+
private toolOrder;
|
|
70
|
+
private context;
|
|
71
|
+
private suggestionSets;
|
|
72
|
+
private waiting;
|
|
73
|
+
private executing;
|
|
74
|
+
private failed;
|
|
75
|
+
private decisions;
|
|
76
|
+
private visible;
|
|
77
|
+
private read;
|
|
78
|
+
private snapshot;
|
|
79
|
+
private disposed;
|
|
80
|
+
private connected;
|
|
81
|
+
private threadSeq;
|
|
82
|
+
/** Set by stop until the next run: a stopped run's late events (its abort error) are dropped. */
|
|
83
|
+
private halted;
|
|
84
|
+
constructor(createEngine: (invoke: ToolInvoker) => AgentEngine, options?: AgentControllerOptions);
|
|
85
|
+
/**
|
|
86
|
+
* Starts reading the agent's events; returns the disconnect, which stops a
|
|
87
|
+
* run in flight. Called from an effect, so nothing subscribes on the server
|
|
88
|
+
* and a StrictMode remount reconnects cleanly.
|
|
89
|
+
*/
|
|
90
|
+
connect(): () => void;
|
|
91
|
+
get agent(): AbstractAgent;
|
|
92
|
+
setOptions(options: AgentControllerOptions): void;
|
|
93
|
+
/** Swaps the engine (CopilotKit hands over a new agent once its runtime connects). */
|
|
94
|
+
attach(createEngine: (invoke: ToolInvoker) => AgentEngine): void;
|
|
95
|
+
subscribe: (listener: Listener) => (() => void);
|
|
96
|
+
getSnapshot: () => AgentSnapshot;
|
|
97
|
+
private emit;
|
|
98
|
+
private finishedAnswers;
|
|
99
|
+
private build;
|
|
100
|
+
private bind;
|
|
101
|
+
/** The view the agent's own messages and state make, for a fresh start or a thread switch. */
|
|
102
|
+
private fromAgent;
|
|
103
|
+
private fold;
|
|
104
|
+
/** Tool results the agent's messages gained outside the event stream: the browser's own tools. */
|
|
105
|
+
private syncResults;
|
|
106
|
+
private invoke;
|
|
107
|
+
private run;
|
|
108
|
+
/** Answers every interrupt still open as cancelled, so a new message can start a run. */
|
|
109
|
+
private cancelOpen;
|
|
110
|
+
/** Sends the person's message and runs the agent. Ignored while a run is in flight. */
|
|
111
|
+
send: (text: string, options?: SendOptions) => Promise<void>;
|
|
112
|
+
/** Stops the run in flight and any tool waiting on the person. */
|
|
113
|
+
stop: () => void;
|
|
114
|
+
/** Drops the answer to the last message and asks again. */
|
|
115
|
+
regenerate: () => Promise<void>;
|
|
116
|
+
/**
|
|
117
|
+
* Answers one interrupt. When every open interrupt has an answer, the run
|
|
118
|
+
* resumes with them (AG-UI `resume`).
|
|
119
|
+
*/
|
|
120
|
+
resolveInterrupt: (interruptId: string, response: {
|
|
121
|
+
status: "resolved";
|
|
122
|
+
payload?: unknown;
|
|
123
|
+
} | {
|
|
124
|
+
status: "cancelled";
|
|
125
|
+
}) => void;
|
|
126
|
+
/** Approves a tool call the run paused on. */
|
|
127
|
+
approve: (call: ChatToolCall, payload?: Record<string, unknown>) => void;
|
|
128
|
+
/** Denies it, with the person's reason. */
|
|
129
|
+
deny: (call: ChatToolCall, reason: string) => void;
|
|
130
|
+
/** Answers a human-in-the-loop tool call: the result goes back to the agent. */
|
|
131
|
+
respond: (toolCallId: string, result: unknown) => void;
|
|
132
|
+
getState: <T = unknown>() => T;
|
|
133
|
+
/** Replaces the shared state: the next run sends it, and the view shows it now. */
|
|
134
|
+
setState: (state: unknown) => void;
|
|
135
|
+
registerTool: <Args>(given: AgentTool<Args>) => (() => void);
|
|
136
|
+
private syncTools;
|
|
137
|
+
addContext: (entry: AgentContextEntry) => (() => void);
|
|
138
|
+
private syncContext;
|
|
139
|
+
addSuggestions: (suggestions: readonly AgentSuggestion[]) => (() => void);
|
|
140
|
+
/** Opens another conversation: stops this one, loads the other's history. */
|
|
141
|
+
switchThread: (threadId: string) => Promise<void>;
|
|
142
|
+
/** Starts a new, empty conversation and returns its id. */
|
|
143
|
+
newThread: () => Promise<string>;
|
|
144
|
+
/** Whether the person can see the conversation: answers that land while hidden count as unread. */
|
|
145
|
+
setVisible: (visible: boolean) => void;
|
|
146
|
+
dispose(): void;
|
|
147
|
+
}
|
|
148
|
+
export {};
|