@hachej/boring-agent 0.1.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.
@@ -0,0 +1,394 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import * as react from 'react';
3
+ import { ReactNode, ComponentType, HTMLAttributes, ComponentProps, FormEvent, ElementType } from 'react';
4
+ import * as ai from 'ai';
5
+ import { UIMessage, FileUIPart, ChatStatus } from 'ai';
6
+ import * as _ai_sdk_react from '@ai-sdk/react';
7
+ import { S as SendMessageInput, d as SessionSummary } from '../harness-CMiJ4kok.js';
8
+ import { Button, Collapsible, CollapsibleContent, CollapsibleTrigger, InputGroupButton, TooltipContent, InputGroupAddon, InputGroupTextarea } from '@hachej/boring-ui-kit';
9
+ import { Streamdown } from 'streamdown';
10
+ import { StickToBottom } from 'use-stick-to-bottom';
11
+ import { ClassValue } from 'clsx';
12
+
13
+ type SlashCommandHandler = (args: string, ctx: SlashCommandContext) => string | void;
14
+ interface SlashCommand {
15
+ name: string;
16
+ description: string;
17
+ /** 'skill' commands are forwarded to the PI agent as `skill: <name>\n\n<args>` instead of running locally. */
18
+ kind?: 'local' | 'skill';
19
+ handler: SlashCommandHandler;
20
+ }
21
+ interface SlashCommandContext {
22
+ sessionId: string;
23
+ clearMessages: () => void;
24
+ resetSession: () => void;
25
+ setModel: (model: string) => boolean;
26
+ listCommands: () => SlashCommand[];
27
+ }
28
+ interface CommandRegistry {
29
+ register(cmd: SlashCommand): void;
30
+ get(name: string): SlashCommand | undefined;
31
+ list(): SlashCommand[];
32
+ }
33
+ declare function createCommandRegistry(initial?: SlashCommand[]): CommandRegistry;
34
+
35
+ interface DiffViewProps {
36
+ oldString: string;
37
+ newString: string;
38
+ path: string;
39
+ replaceAll?: boolean;
40
+ }
41
+ declare function DiffView({ oldString, newString, path, replaceAll }: DiffViewProps): react_jsx_runtime.JSX.Element;
42
+
43
+ type ToolState = 'input-streaming' | 'input-available' | 'approval-requested' | 'approval-responded' | 'output-available' | 'output-error' | 'output-denied';
44
+
45
+ interface ToolPart {
46
+ type: string;
47
+ toolName: string;
48
+ toolCallId: string;
49
+ state: ToolState;
50
+ input?: unknown;
51
+ output?: unknown;
52
+ errorText?: string;
53
+ }
54
+ type ToolRenderer = (part: ToolPart) => ReactNode;
55
+ type ToolRendererOverrides = Partial<Record<string, ToolRenderer>>;
56
+ declare const defaultToolRenderers: Record<string, ToolRenderer>;
57
+ declare function mergeToolRenderers(overrides?: ToolRendererOverrides): Record<string, ToolRenderer>;
58
+ declare function resolveToolRenderer(toolName: string, overrides?: ToolRendererOverrides): ToolRenderer;
59
+
60
+ type OpenArtifactHandler = (path: string) => void;
61
+ interface ArtifactOpenProviderProps {
62
+ onOpenArtifact?: OpenArtifactHandler;
63
+ children: ReactNode;
64
+ }
65
+ declare function ArtifactOpenProvider({ onOpenArtifact, children }: ArtifactOpenProviderProps): react_jsx_runtime.JSX.Element;
66
+ declare function useOpenArtifact(): OpenArtifactHandler | null;
67
+
68
+ /**
69
+ * Suggested action shown in a chat empty state. Customizable per child app
70
+ * via `ChatPanel.suggestions` / `ChatCenteredShell.chatSuggestions`.
71
+ *
72
+ * Click behavior: if `onSelect` is provided it wins; otherwise `prompt`
73
+ * (or `label` as a fallback) is sent as the next user message.
74
+ */
75
+ interface ChatSuggestion {
76
+ /** Title shown on the suggestion card. */
77
+ label: string;
78
+ /** Smaller hint/subtitle line below the label. */
79
+ hint?: string;
80
+ /**
81
+ * Lucide-compatible icon component (any component that accepts className
82
+ * + strokeWidth). Pass `BookOpen`, `Code2`, etc. directly from lucide-react.
83
+ */
84
+ icon?: ComponentType<{
85
+ className?: string;
86
+ strokeWidth?: number;
87
+ }>;
88
+ /**
89
+ * Text inserted as the user's next message when the card is clicked. If
90
+ * omitted, falls back to `label`.
91
+ */
92
+ prompt?: string;
93
+ /**
94
+ * Override the click handler entirely. When set, `prompt` is ignored —
95
+ * the host decides what happens (e.g., open a wizard, prefill the
96
+ * composer, route somewhere).
97
+ */
98
+ onSelect?: () => void;
99
+ }
100
+ /**
101
+ * Sensible defaults that mirror the suggestions the chat-centered shell
102
+ * showed before the agent was wired into the workspace. Child apps that
103
+ * pass nothing inherit these; passing an empty array hides the grid.
104
+ */
105
+ declare const defaultChatSuggestions: ChatSuggestion[];
106
+ interface ChatEmptyStateProps {
107
+ /** Small uppercase eyebrow above the headline. */
108
+ eyebrow?: string;
109
+ /** Large headline. Editorial tone. */
110
+ title?: string;
111
+ /** Single-paragraph description below the headline. */
112
+ description?: string;
113
+ /**
114
+ * Suggestion cards. Pass `[]` to hide the grid entirely (headline still
115
+ * renders). Defaults to `defaultChatSuggestions`.
116
+ */
117
+ suggestions?: ChatSuggestion[];
118
+ /**
119
+ * Fired when a suggestion card is clicked. The default ChatPanel wiring
120
+ * resolves this to `sendMessage` with the suggestion's prompt.
121
+ */
122
+ onSelect?: (suggestion: ChatSuggestion) => void;
123
+ /** Optional content rendered below the suggestion grid. */
124
+ footer?: ReactNode;
125
+ className?: string;
126
+ }
127
+ declare function ChatEmptyState({ eyebrow, title, description, suggestions, onSelect, footer, className, }: ChatEmptyStateProps): react_jsx_runtime.JSX.Element;
128
+
129
+ /**
130
+ * Selected model, stored as { provider, id } so the composer can speak
131
+ * pi-coding-agent's real registered IDs (claude-sonnet-4-6, gpt-5.2-codex,
132
+ * …) rather than a 3-alias shorthand. Legacy single-string values
133
+ * (sonnet/haiku/opus) are still honoured for back-compat.
134
+ */
135
+ interface ModelSelection {
136
+ provider: string;
137
+ id: string;
138
+ }
139
+ interface ChatPanelProps {
140
+ sessionId: string;
141
+ toolRenderers?: ToolRendererOverrides;
142
+ extraCommands?: SlashCommand[];
143
+ onSessionReset?: () => void | Promise<void>;
144
+ /**
145
+ * Render flush, without the outer canvas tint or the inner mx/my rounded
146
+ * card chrome. Use when embedding ChatPanel inside a parent that already
147
+ * provides its own card surface (e.g. workspace's ChatCenteredShell).
148
+ * Defaults to `true` (standalone chrome on).
149
+ */
150
+ chrome?: boolean;
151
+ /**
152
+ * Cards shown when the conversation is empty. Click → sendMessage with the
153
+ * suggestion's `prompt` (or `label` as fallback). Pass `[]` to hide the
154
+ * grid; omit to inherit `defaultChatSuggestions`. Customizable per child
155
+ * app — e.g. a data-app might offer "Build a chart from a CSV" instead.
156
+ */
157
+ suggestions?: ChatSuggestion[];
158
+ /** Eyebrow above the empty-state headline. */
159
+ emptyEyebrow?: string;
160
+ /** Empty-state headline. */
161
+ emptyTitle?: string;
162
+ /** Empty-state description below the headline. */
163
+ emptyDescription?: string;
164
+ /**
165
+ * Render the extended-thinking selector in the composer footer (off / low
166
+ * / medium / high). When enabled, the selected level is persisted in
167
+ * localStorage and sent through to the agent on every turn. Default off
168
+ * — opt-in because not every host wants users tweaking model knobs, and
169
+ * thinking budget consumes more tokens.
170
+ */
171
+ thinkingControl?: boolean;
172
+ /**
173
+ * Model selected before any local user choice exists. Usually supplied by
174
+ * the host's /api/v1/agent/models payload, so deployment env can choose
175
+ * the default without rebuilding consumers.
176
+ */
177
+ defaultModel?: ModelSelection;
178
+ /**
179
+ * Tap into the SSE data stream. Called for every `onData` part the
180
+ * agent emits — host apps use this to bridge agent-driven file
181
+ * changes into their own UI plumbing (see
182
+ * `useAgentFileChangeBridge` in `@hachej/boring-workspace` for the
183
+ * canonical wire-up).
184
+ */
185
+ onData?: (part: unknown) => void;
186
+ /** Headers sent with chat and chat-history requests. */
187
+ requestHeaders?: Record<string, string>;
188
+ /**
189
+ * Called with a file path when the user clicks the path label inside
190
+ * a read / write / edit tool card. Hosts (e.g. @hachej/boring-workspace)
191
+ * supply this to open the file in the surrounding workbench. Without
192
+ * it the path renders as plain text. Mounted via context so any
193
+ * future renderer can consume it without a prop drill.
194
+ */
195
+ onOpenArtifact?: OpenArtifactHandler;
196
+ /**
197
+ * Enable the admin debug drawer — system prompt, raw messages JSON, and
198
+ * live onData stream events. Intended for development and ops; keep off
199
+ * in production consumer UIs.
200
+ */
201
+ debug?: boolean;
202
+ className?: string;
203
+ }
204
+ declare function ChatPanel(props: ChatPanelProps): react_jsx_runtime.JSX.Element;
205
+
206
+ interface DebugDrawerProps {
207
+ sessionId: string;
208
+ messages: UIMessage[];
209
+ requestHeaders?: Record<string, string>;
210
+ width: number;
211
+ onWidthChange: (w: number) => void;
212
+ }
213
+ declare function DebugDrawer({ sessionId, messages, requestHeaders, width, onWidthChange }: DebugDrawerProps): react_jsx_runtime.JSX.Element;
214
+
215
+ interface AgentCommandContribution {
216
+ id: string;
217
+ title: string;
218
+ run: () => void;
219
+ keywords?: string[];
220
+ shortcut?: string;
221
+ when?: () => boolean;
222
+ pluginId?: string;
223
+ }
224
+ interface AgentCommandOptions {
225
+ focusComposer?: () => void;
226
+ newChat?: () => void;
227
+ stopGeneration?: () => void;
228
+ canStopGeneration?: () => boolean;
229
+ }
230
+ declare function getAgentCommands(options?: AgentCommandOptions): AgentCommandContribution[];
231
+
232
+ type UseAgentChatOptions = Pick<SendMessageInput, 'sessionId' | 'model' | 'thinkingLevel'> & {
233
+ onData?: (part: unknown) => void;
234
+ requestHeaders?: Record<string, string>;
235
+ };
236
+ declare function useAgentChat(opts: UseAgentChatOptions): _ai_sdk_react.UseChatHelpers<UIMessage<unknown, ai.UIDataTypes, ai.UITools>>;
237
+
238
+ interface UseSessionsOptions {
239
+ requestHeaders?: Record<string, string>;
240
+ storageKey?: string;
241
+ }
242
+ interface UseSessionsResult {
243
+ sessions: SessionSummary[];
244
+ activeSession: SessionSummary | undefined;
245
+ activeSessionId: string | undefined;
246
+ loading: boolean;
247
+ error: Error | undefined;
248
+ create: (init?: {
249
+ title?: string;
250
+ }) => Promise<SessionSummary>;
251
+ switch: (id: string) => void;
252
+ delete: (id: string) => Promise<void>;
253
+ }
254
+ declare function useSessions(opts?: UseSessionsOptions): UseSessionsResult;
255
+
256
+ interface ParsedCommand {
257
+ name: string;
258
+ args: string;
259
+ }
260
+ declare function parseSlashCommand(text: string): ParsedCommand | null;
261
+
262
+ declare const builtinCommands: SlashCommand[];
263
+
264
+ declare function mergeShadcnToolRenderers(overrides?: ToolRendererOverrides): Record<string, ToolRenderer>;
265
+
266
+ type GroupedToolEntry = {
267
+ part: UIMessage['parts'][number];
268
+ key: string;
269
+ };
270
+ interface ToolCallGroupProps {
271
+ tools: GroupedToolEntry[];
272
+ mergedToolRenderers: ToolRendererOverrides;
273
+ }
274
+ declare const ToolCallGroup: react.MemoExoticComponent<({ tools, mergedToolRenderers }: ToolCallGroupProps) => react_jsx_runtime.JSX.Element>;
275
+
276
+ type MessageProps = HTMLAttributes<HTMLDivElement> & {
277
+ from: UIMessage["role"];
278
+ };
279
+ declare const Message: ({ className, from, ...props }: MessageProps) => react_jsx_runtime.JSX.Element;
280
+ type MessageContentProps = HTMLAttributes<HTMLDivElement>;
281
+ declare const MessageContent: ({ children, className, ...props }: MessageContentProps) => react_jsx_runtime.JSX.Element;
282
+ type MessageActionsProps = ComponentProps<"div">;
283
+ declare const MessageActions: ({ className, children, ...props }: MessageActionsProps) => react_jsx_runtime.JSX.Element;
284
+ type MessageActionProps = ComponentProps<typeof Button> & {
285
+ tooltip?: string;
286
+ label?: string;
287
+ };
288
+ declare const MessageAction: ({ tooltip, children, label, variant, size, ...props }: MessageActionProps) => react_jsx_runtime.JSX.Element;
289
+ type MessageResponseProps = ComponentProps<typeof Streamdown>;
290
+ declare const MessageResponse: react.MemoExoticComponent<({ className, shikiTheme, components, ...props }: MessageResponseProps) => react_jsx_runtime.JSX.Element>;
291
+ type MessageToolbarProps = ComponentProps<"div">;
292
+ declare const MessageToolbar: ({ className, children, ...props }: MessageToolbarProps) => react_jsx_runtime.JSX.Element;
293
+
294
+ type ConversationProps = ComponentProps<typeof StickToBottom>;
295
+ declare const Conversation: ({ className, ...props }: ConversationProps) => react_jsx_runtime.JSX.Element;
296
+ type ConversationContentProps = ComponentProps<typeof StickToBottom.Content>;
297
+ declare const ConversationContent: ({ className, ...props }: ConversationContentProps) => react_jsx_runtime.JSX.Element;
298
+ type ConversationEmptyStateProps = ComponentProps<"div"> & {
299
+ title?: string;
300
+ description?: string;
301
+ icon?: React.ReactNode;
302
+ };
303
+ declare const ConversationEmptyState: ({ className, title, description, icon, children, ...props }: ConversationEmptyStateProps) => react_jsx_runtime.JSX.Element;
304
+ type ConversationScrollButtonProps = ComponentProps<typeof Button>;
305
+ declare const ConversationScrollButton: ({ className, ...props }: ConversationScrollButtonProps) => false | react_jsx_runtime.JSX.Element;
306
+
307
+ type ReasoningProps = ComponentProps<typeof Collapsible> & {
308
+ isStreaming?: boolean;
309
+ open?: boolean;
310
+ defaultOpen?: boolean;
311
+ onOpenChange?: (open: boolean) => void;
312
+ duration?: number;
313
+ };
314
+ declare const Reasoning: react.MemoExoticComponent<({ className, isStreaming, open, defaultOpen, onOpenChange, duration: durationProp, children, ...props }: ReasoningProps) => react_jsx_runtime.JSX.Element>;
315
+ type ReasoningTriggerProps = ComponentProps<typeof CollapsibleTrigger> & {
316
+ getThinkingMessage?: (isStreaming: boolean, duration?: number) => ReactNode;
317
+ };
318
+ declare const ReasoningTrigger: react.MemoExoticComponent<({ className, children, getThinkingMessage, ...props }: ReasoningTriggerProps) => react_jsx_runtime.JSX.Element>;
319
+ type ReasoningContentProps = ComponentProps<typeof CollapsibleContent> & {
320
+ children: string;
321
+ };
322
+ declare const ReasoningContent: react.MemoExoticComponent<({ className, children, ...props }: ReasoningContentProps) => react_jsx_runtime.JSX.Element>;
323
+
324
+ type CodeBlockProps = HTMLAttributes<HTMLDivElement> & {
325
+ code: string;
326
+ language: string;
327
+ showLineNumbers?: boolean;
328
+ };
329
+ declare const CodeBlockContainer: ({ className, language, style, ...props }: HTMLAttributes<HTMLDivElement> & {
330
+ language: string;
331
+ }) => react_jsx_runtime.JSX.Element;
332
+ declare const CodeBlockHeader: ({ children, className, ...props }: HTMLAttributes<HTMLDivElement>) => react_jsx_runtime.JSX.Element;
333
+ declare const CodeBlockContent: ({ code, language, showLineNumbers, }: {
334
+ code: string;
335
+ language: string;
336
+ showLineNumbers?: boolean;
337
+ }) => react_jsx_runtime.JSX.Element;
338
+ declare const CodeBlock: ({ code, language, showLineNumbers, className, children, ...props }: CodeBlockProps) => react_jsx_runtime.JSX.Element;
339
+ type CodeBlockCopyButtonProps = ComponentProps<typeof Button> & {
340
+ onCopy?: () => void;
341
+ onError?: (error: Error) => void;
342
+ timeout?: number;
343
+ };
344
+ declare const CodeBlockCopyButton: ({ onCopy, onError, timeout, children, className, ...props }: CodeBlockCopyButtonProps) => react_jsx_runtime.JSX.Element;
345
+
346
+ interface PromptInputMessage {
347
+ text: string;
348
+ files: FileUIPart[];
349
+ }
350
+ type PromptInputProps = Omit<HTMLAttributes<HTMLFormElement>, "onSubmit" | "onError"> & {
351
+ accept?: string;
352
+ multiple?: boolean;
353
+ globalDrop?: boolean;
354
+ syncHiddenInput?: boolean;
355
+ maxFiles?: number;
356
+ maxFileSize?: number;
357
+ onError?: (err: {
358
+ code: "max_files" | "max_file_size" | "accept";
359
+ message: string;
360
+ }) => void;
361
+ onSubmit: (message: PromptInputMessage, event: FormEvent<HTMLFormElement>) => void | Promise<void>;
362
+ };
363
+ declare const PromptInput: ({ className, accept, multiple, globalDrop, syncHiddenInput, maxFiles, maxFileSize, onError, onSubmit, children, ...props }: PromptInputProps) => react_jsx_runtime.JSX.Element;
364
+ type PromptInputTextareaProps = ComponentProps<typeof InputGroupTextarea>;
365
+ declare const PromptInputTextarea: ({ onChange, onKeyDown, className, placeholder, ...props }: PromptInputTextareaProps) => react_jsx_runtime.JSX.Element;
366
+ type PromptInputFooterProps = Omit<ComponentProps<typeof InputGroupAddon>, "align">;
367
+ declare const PromptInputFooter: ({ className, ...props }: PromptInputFooterProps) => react_jsx_runtime.JSX.Element;
368
+ type PromptInputButtonTooltip = string | {
369
+ content: ReactNode;
370
+ shortcut?: string;
371
+ side?: ComponentProps<typeof TooltipContent>["side"];
372
+ };
373
+ type PromptInputButtonProps = ComponentProps<typeof InputGroupButton> & {
374
+ tooltip?: PromptInputButtonTooltip;
375
+ };
376
+ declare const PromptInputButton: ({ variant, className, size, tooltip, ...props }: PromptInputButtonProps) => react_jsx_runtime.JSX.Element;
377
+ type PromptInputSubmitProps = ComponentProps<typeof InputGroupButton> & {
378
+ status?: ChatStatus;
379
+ onStop?: () => void;
380
+ };
381
+ declare const PromptInputSubmit: ({ className, variant, size, status, onStop, onClick, children, ...props }: PromptInputSubmitProps) => react_jsx_runtime.JSX.Element;
382
+
383
+ interface TextShimmerProps {
384
+ children: string;
385
+ as?: ElementType;
386
+ className?: string;
387
+ duration?: number;
388
+ spread?: number;
389
+ }
390
+ declare const Shimmer: react.MemoExoticComponent<({ children, as: Component, className, duration, spread, }: TextShimmerProps) => react_jsx_runtime.JSX.Element>;
391
+
392
+ declare function cn(...inputs: ClassValue[]): string;
393
+
394
+ export { type AgentCommandContribution, type AgentCommandOptions, ArtifactOpenProvider, ChatEmptyState, type ChatEmptyStateProps, ChatPanel, type ChatPanelProps, type ChatSuggestion, CodeBlock, CodeBlockContainer, CodeBlockContent, CodeBlockCopyButton, CodeBlockHeader, type CommandRegistry, Conversation, ConversationContent, ConversationEmptyState, ConversationScrollButton, DebugDrawer, DiffView, type GroupedToolEntry, Message, MessageAction, MessageActions, MessageContent, MessageResponse, MessageToolbar, type OpenArtifactHandler, type ParsedCommand, PromptInput, PromptInputButton, PromptInputFooter, PromptInputSubmit, PromptInputTextarea, Reasoning, ReasoningContent, ReasoningTrigger, Shimmer, type SlashCommand, type SlashCommandContext, ToolCallGroup, type ToolPart, type ToolRenderer, type ToolRendererOverrides, type UseAgentChatOptions, type UseSessionsOptions, type UseSessionsResult, builtinCommands, cn, createCommandRegistry, defaultChatSuggestions, defaultToolRenderers, getAgentCommands, mergeShadcnToolRenderers, mergeToolRenderers, parseSlashCommand, resolveToolRenderer, useAgentChat, useOpenArtifact, useSessions };