@hachej/boring-agent 0.1.34 → 0.1.36

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.
@@ -1,14 +1,14 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import * as ai from 'ai';
3
2
  import { FileUIPart, UIMessage, ChatStatus } from 'ai';
4
3
  import * as react from 'react';
5
- import { ReactNode, ComponentType, HTMLAttributes, ComponentProps, FormEvent } from 'react';
6
- import { T as ToolUiMetadata } from '../tool-ui-DIFNGwYd.js';
7
- import { S as SendMessageInput, e as SessionSummary } from '../harness-dYuMzxmp.js';
4
+ import { ComponentType, ReactNode, HTMLAttributes, ComponentProps, FormEvent } from 'react';
5
+ import { T as ToolUiMetadata, B as BoringChatPart, d as BoringChatMessage, p as PiChatStatus, u as QueuedUserMessage, e as ChatError, P as PiChatEvent, r as PromptPayload, s as PromptReceipt, F as FollowUpPayload, l as FollowUpReceipt, Q as QueueClearPayload, t as QueueClearReceipt, I as InterruptPayload, h as CommandReceipt, S as StopPayload, v as StopReceipt } from '../piChatEvent-Ck1BAE_m.js';
6
+ import { c as SessionSummary } from '../session-BRovhe0D.js';
8
7
  import { Button, Collapsible, CollapsibleContent, CollapsibleTrigger, InputGroupAddon, InputGroupButton, InputGroupTextarea } from '@hachej/boring-ui-kit';
9
8
  import { Streamdown } from 'streamdown';
10
9
  import { StickToBottom } from 'use-stick-to-bottom';
11
10
  import { ClassValue } from 'clsx';
11
+ import 'zod';
12
12
 
13
13
  interface UploadFileOptions {
14
14
  apiBaseUrl?: string;
@@ -22,64 +22,26 @@ interface UploadFileResult {
22
22
  }
23
23
  declare function uploadFile(file: File, opts?: UploadFileOptions): Promise<UploadFileResult>;
24
24
 
25
- type SlashCommandHandler = (args: string, ctx: SlashCommandContext) => string | void | Promise<string | void>;
26
- interface SlashCommand {
27
- name: string;
28
- description: string;
29
- /** 'skill' commands are forwarded to the PI agent as `skill: <name>\n\n<args>` instead of running locally. */
30
- kind?: 'local' | 'skill';
31
- handler: SlashCommandHandler;
32
- }
33
- interface SlashCommandContext {
34
- sessionId: string;
35
- clearMessages: () => void;
36
- resetSession: () => void;
37
- listCommands: () => SlashCommand[];
38
- reloadAgentPlugins: () => Promise<string>;
39
- /**
40
- * Drives the PluginUpdateStatus banner above the composer. The `/reload`
41
- * builtin prefers this over the inline-text path: it calls
42
- * `pluginUpdate.run()` which (1) sets the banner to "running", (2)
43
- * hits /api/v1/agent/reload, (3) transitions to "success" or "error"
44
- * with diagnostics. Returns a short string ack for the assistant
45
- * message bubble.
46
- */
47
- pluginUpdate?: {
48
- run: () => Promise<string>;
49
- };
50
- }
51
- interface CommandRegistry {
52
- register(cmd: SlashCommand): void;
53
- get(name: string): SlashCommand | undefined;
54
- list(): SlashCommand[];
55
- }
56
- declare function createCommandRegistry(initial?: SlashCommand[]): CommandRegistry;
57
-
58
- type ToolState = 'input-streaming' | 'input-available' | 'approval-requested' | 'approval-responded' | 'output-available' | 'output-error' | 'output-denied';
59
-
60
- interface ToolPart {
61
- type: string;
62
- toolName: string;
63
- toolCallId: string;
64
- state: ToolState;
65
- input?: unknown;
66
- output?: unknown;
67
- errorText?: string;
68
- ui?: ToolUiMetadata;
25
+ /**
26
+ * Extended-thinking budget. Sent through the agent runtime to providers that
27
+ * support reasoning controls. 'off' means no reasoning chunks; the higher
28
+ * tiers progressively allow more think-time.
29
+ */
30
+ type ThinkingLevel = 'off' | 'low' | 'medium' | 'high';
31
+ /**
32
+ * Selected model, stored as { provider, id } so the composer can pass through
33
+ * the agent runtime's registered model IDs rather than a local alias table.
34
+ * Unqualified legacy aliases are ignored: Boring must not infer a provider
35
+ * when the runtime owns model selection.
36
+ */
37
+ interface ModelSelection {
38
+ provider: string;
39
+ id: string;
69
40
  }
70
- type ToolRenderer = (part: ToolPart) => ReactNode;
71
- type ToolRendererOverrides = Partial<Record<string, ToolRenderer>>;
72
- declare const defaultToolRenderers: Record<string, ToolRenderer>;
73
- declare function mergeToolRenderers(overrides?: ToolRendererOverrides): Record<string, ToolRenderer>;
74
- declare function resolveToolRenderer(toolName: string, overrides?: ToolRendererOverrides): ToolRenderer;
75
-
76
- type OpenArtifactHandler = (path: string) => void;
77
- interface ArtifactOpenProviderProps {
78
- onOpenArtifact?: OpenArtifactHandler;
79
- children: ReactNode;
41
+ interface AvailableModel extends ModelSelection {
42
+ label: string;
43
+ available: boolean;
80
44
  }
81
- declare function ArtifactOpenProvider({ onOpenArtifact, children }: ArtifactOpenProviderProps): react_jsx_runtime.JSX.Element;
82
- declare function useOpenArtifact(): OpenArtifactHandler | null;
83
45
 
84
46
  /**
85
47
  * Suggested action shown in a chat empty state. Customizable per child app
@@ -142,28 +104,100 @@ interface ChatEmptyStateProps {
142
104
  }
143
105
  declare function ChatEmptyState({ eyebrow, title, description, suggestions, onSelect, footer, className, }: ChatEmptyStateProps): react_jsx_runtime.JSX.Element;
144
106
 
145
- /**
146
- * Selected model, stored as { provider, id } so the composer can pass through
147
- * the agent runtime's registered model IDs rather than a local alias table.
148
- * Unqualified legacy aliases are ignored: Boring must not infer a provider
149
- * when the runtime owns model selection.
150
- */
151
- interface ModelSelection {
152
- provider: string;
153
- id: string;
107
+ interface ParsedCommand {
108
+ name: string;
109
+ args: string;
154
110
  }
111
+ declare function parseSlashCommand(text: string): ParsedCommand | null;
155
112
 
156
- type ComposerBlockerAction = {
157
- id: string;
158
- label: string;
159
- };
160
- type ComposerBlocker = {
161
- id: string;
162
- reason: string;
163
- label?: string;
164
- sessionId?: string;
165
- actions?: ComposerBlockerAction[];
113
+ type SlashCommandHandlerResult = string | void | {
114
+ message?: string;
115
+ preserveDraft?: boolean;
166
116
  };
117
+ type SlashCommandHandler = (args: string, ctx: SlashCommandContext) => SlashCommandHandlerResult | Promise<SlashCommandHandlerResult>;
118
+ interface SlashCommand {
119
+ name: string;
120
+ description: string;
121
+ /**
122
+ * - local: handled in the browser.
123
+ * - skill: forwarded to the PI agent as `skill: <name>\n\n<args>`.
124
+ * Server commands registered via `useServerCommands` omit `kind` (or set
125
+ * it to `local`) — Pi handles execution natively so no frontend kind-check
126
+ * is needed.
127
+ */
128
+ kind?: 'local' | 'skill';
129
+ /**
130
+ * Origin of the command, surfaced as a tag in the slash-command picker.
131
+ * Mirrors Pi's command sources for server commands; `local` for built-in
132
+ * browser commands. Display only.
133
+ */
134
+ source?: 'local' | 'extension' | 'prompt' | 'skill';
135
+ /** Originating plugin/package name (when known), shown as a tag. */
136
+ sourcePlugin?: string;
137
+ handler: SlashCommandHandler;
138
+ }
139
+ interface SlashCommandContext {
140
+ sessionId: string;
141
+ clearMessages: () => void;
142
+ resetSession: () => void;
143
+ listCommands: () => SlashCommand[];
144
+ reloadAgentPlugins: () => Promise<string>;
145
+ openModelPicker?: () => boolean | void;
146
+ selectComposerModel?: (query: string) => string | void;
147
+ openThinkingPicker?: () => boolean | void;
148
+ selectComposerThinking?: (query: string) => string | void;
149
+ /**
150
+ * Drives the PluginUpdateStatus banner above the composer. The `/reload`
151
+ * builtin prefers this over the inline-text path: it calls
152
+ * `pluginUpdate.run()` which (1) sets the banner to "running", (2)
153
+ * hits /api/v1/agent/reload, (3) transitions to "success" or "error"
154
+ * with diagnostics. Returns a short string ack for the assistant
155
+ * message bubble.
156
+ */
157
+ pluginUpdate?: {
158
+ run: () => Promise<string>;
159
+ };
160
+ }
161
+ interface CommandRegistry {
162
+ register(cmd: SlashCommand): void;
163
+ unregister(name: string): void;
164
+ get(name: string): SlashCommand | undefined;
165
+ list(): SlashCommand[];
166
+ }
167
+ declare function createCommandRegistry(initial?: SlashCommand[]): CommandRegistry;
168
+
169
+ declare const builtinCommands: SlashCommand[];
170
+
171
+ type ToolState = 'input-streaming' | 'input-available' | 'approval-requested' | 'approval-responded' | 'output-available' | 'output-error' | 'output-denied' | 'aborted';
172
+
173
+ type BoringToolCallPart = Extract<BoringChatPart, {
174
+ type: 'tool-call';
175
+ }>;
176
+ interface ToolRendererResolution {
177
+ key: string;
178
+ source: 'rendererId' | 'toolName' | 'fallback';
179
+ requestedRendererId?: string;
180
+ }
181
+ interface ToolPart {
182
+ /** Neutral render-model type. Legacy `tool-<name>` values are accepted at the renderer boundary. */
183
+ type: 'tool-call' | `tool-${string}` | 'dynamic-tool';
184
+ toolName: string;
185
+ /** Stable tool call id; for Pi-native BoringChatPart this is BoringChatPart.id. */
186
+ toolCallId: string;
187
+ state: ToolState;
188
+ input?: unknown;
189
+ output?: unknown;
190
+ errorText?: string;
191
+ ui?: ToolUiMetadata;
192
+ rendererResolution?: ToolRendererResolution;
193
+ }
194
+ type ToolRenderablePart = ToolPart | BoringToolCallPart | unknown;
195
+ type ToolRenderer = (part: ToolPart) => ReactNode;
196
+ type ToolRendererOverrides = Partial<Record<string, ToolRenderer>>;
197
+ declare const defaultToolRenderers: Record<string, ToolRenderer>;
198
+ declare function mergeToolRenderers(overrides?: ToolRendererOverrides): Record<string, ToolRenderer>;
199
+ declare function resolveToolRenderer(toolNameOrPart: string | ToolPart, overrides?: ToolRendererOverrides): ToolRenderer;
200
+
167
201
  type ChatPanelRuntimeDependenciesWarmupStatus = {
168
202
  state: 'preparing' | 'ready' | 'failed';
169
203
  message?: string;
@@ -183,126 +217,360 @@ type ChatPanelWorkspaceWarmupStatus = {
183
217
  message?: string;
184
218
  runtimeDependencies?: ChatPanelRuntimeDependenciesWarmupStatus;
185
219
  };
186
- type ChatSubmitSource = 'composer' | 'suggestion';
220
+
221
+ interface OptimisticUserMessage extends BoringChatMessage {
222
+ role: 'user';
223
+ clientNonce: string;
224
+ clientSeq?: number;
225
+ /**
226
+ * Id of the last committed message when this optimistic prompt was submitted.
227
+ * Used to position the placeholder by sequence rather than by wall-clock time,
228
+ * so client/server clock skew can't render a just-sent prompt above the
229
+ * previous reply.
230
+ */
231
+ afterMessageId?: string;
232
+ }
233
+
234
+ type PiChatConnectionState = 'disconnected' | 'connecting' | 'connected' | 'reconnecting';
235
+
236
+ interface PiChatRuntimeNotice {
237
+ id: string;
238
+ level: 'info' | 'warning' | 'error';
239
+ text: string;
240
+ dismissible?: boolean;
241
+ }
242
+ interface PiChatRetryNotice {
243
+ attempt: number;
244
+ maxAttempts: number;
245
+ delayMs: number;
246
+ errorMessage: string;
247
+ }
248
+ interface PiChatSeqGap {
249
+ expectedSeq: number;
250
+ actualSeq: number;
251
+ lastSeq: number;
252
+ }
253
+ interface PiChatHistoryState {
254
+ /** First cut renders full history; this explicit shape leaves room for paged windows later. */
255
+ mode: 'full';
256
+ messageCount: number;
257
+ }
258
+ interface PiChatState {
259
+ sessionId: string;
260
+ workspaceId?: string;
261
+ storageScope: string;
262
+ status: PiChatStatus;
263
+ turnId?: string;
264
+ lastSeq: number;
265
+ committedMessages: BoringChatMessage[];
266
+ streamingMessage?: BoringChatMessage;
267
+ streamingPreservedTextPartKeys?: Set<string>;
268
+ history: PiChatHistoryState;
269
+ queue: {
270
+ followUps: QueuedUserMessage[];
271
+ };
272
+ optimisticOutbox: Record<string, OptimisticUserMessage>;
273
+ pendingToolCallIds: Set<string>;
274
+ connection: {
275
+ state: PiChatConnectionState;
276
+ lastHeartbeatAt?: number;
277
+ };
278
+ error?: ChatError;
279
+ retryNotice?: PiChatRetryNotice;
280
+ notices: PiChatRuntimeNotice[];
281
+ hydrated: boolean;
282
+ needsResync?: PiChatSeqGap;
283
+ }
284
+
285
+ type PiChatStoreListener = () => void;
286
+ interface PiChatStoreOptions {
287
+ scheduleNotify?: (notify: () => void) => unknown;
288
+ cancelNotify?: (handle: unknown) => void;
289
+ }
290
+
291
+ interface RemotePiSessionHeaders {
292
+ [key: string]: string | undefined;
293
+ }
294
+ interface RemotePiSessionOptions {
295
+ sessionId: string;
296
+ workspaceId?: string;
297
+ storageScope?: string;
298
+ apiBaseUrl?: string;
299
+ headers?: RemotePiSessionHeaders | (() => RemotePiSessionHeaders | Promise<RemotePiSessionHeaders>);
300
+ fetch?: typeof globalThis.fetch;
301
+ onEvent?: (event: PiChatEvent) => void;
302
+ storeOptions?: PiChatStoreOptions;
303
+ autoStart?: boolean;
304
+ reconnect?: {
305
+ baseMs?: number;
306
+ maxMs?: number;
307
+ jitterRatio?: number;
308
+ random?: () => number;
309
+ };
310
+ debug?: {
311
+ largeStateWarningBytes?: number;
312
+ largeStateWarningMessages?: number;
313
+ onWarning?: (warning: RemotePiSessionLargeStateWarning) => void;
314
+ };
315
+ setTimeoutFn?: typeof globalThis.setTimeout;
316
+ clearTimeoutFn?: typeof globalThis.clearTimeout;
317
+ }
318
+ interface RemotePiSessionLargeStateWarning {
319
+ type: 'large-state';
320
+ sessionId: string;
321
+ approxBytes: number;
322
+ messageCount: number;
323
+ thresholdBytes: number;
324
+ thresholdMessages: number;
325
+ }
326
+ interface RemotePiSessionDebugState {
327
+ sessionId: string;
328
+ lastSeq: number;
329
+ status: PiChatState['status'];
330
+ connection: PiChatState['connection']['state'];
331
+ lastHeartbeatAt?: number;
332
+ queue: {
333
+ followUps: number;
334
+ optimisticOutbox: number;
335
+ pendingToolCalls: number;
336
+ };
337
+ recentEventTypes: string[];
338
+ gapCount: number;
339
+ retryNotice?: PiChatState['retryNotice'];
340
+ largeStateWarning?: RemotePiSessionLargeStateWarning;
341
+ history: {
342
+ mode: 'full';
343
+ messageCount: number;
344
+ streamingMessageCount: 0 | 1;
345
+ };
346
+ disposed: boolean;
347
+ generation: number;
348
+ streamRunId: number;
349
+ reconnectAttempt: number;
350
+ hasReconnectTimer: boolean;
351
+ inflightFetches: number;
352
+ }
353
+ declare class RemotePiSession {
354
+ private readonly options;
355
+ private readonly store;
356
+ private readonly fetchImpl;
357
+ private readonly apiBaseUrl;
358
+ private readonly storageScope;
359
+ private readonly setTimeoutFn;
360
+ private readonly clearTimeoutFn;
361
+ private generation;
362
+ private streamRunId;
363
+ private reconnectAttempt;
364
+ private started;
365
+ private disposed;
366
+ private streamAbortController?;
367
+ private reconnectTimer?;
368
+ private readonly fetchControllers;
369
+ private readonly recentEventTypes;
370
+ private gapCount;
371
+ private largeStateWarning?;
372
+ constructor(options: RemotePiSessionOptions);
373
+ getState(): PiChatState;
374
+ getDebugState(): RemotePiSessionDebugState;
375
+ subscribe(listener: PiChatStoreListener): () => void;
376
+ start(cursor?: number): Promise<void>;
377
+ prompt(payload: PromptPayload): Promise<PromptReceipt>;
378
+ followUp(payload: FollowUpPayload): Promise<FollowUpReceipt>;
379
+ clearQueue(payload?: QueueClearPayload): Promise<QueueClearReceipt>;
380
+ interrupt(payload?: InterruptPayload): Promise<CommandReceipt>;
381
+ stop(payload?: StopPayload): Promise<StopReceipt>;
382
+ dispose(): void;
383
+ private hydrateAndConnect;
384
+ private connectEvents;
385
+ private runEventStream;
386
+ private rehydrateAfterStreamReset;
387
+ private scheduleReconnect;
388
+ private postCommand;
389
+ private rollbackOptimisticMessage;
390
+ private fetchJson;
391
+ private requestHeaders;
392
+ private stateUrl;
393
+ private sessionUrl;
394
+ private dispatchProtocolError;
395
+ private recordEventType;
396
+ private recordLargeStateWarning;
397
+ private clearReconnectTimer;
398
+ private abortEventStream;
399
+ private isGenerationActive;
400
+ private isStreamActive;
401
+ }
402
+
403
+ interface ActiveSessionStorageLike {
404
+ getItem(key: string): string | null;
405
+ setItem(key: string, value: string): void;
406
+ removeItem(key: string): void;
407
+ }
408
+ interface ActiveSessionStorageOptions {
409
+ storageScope?: string;
410
+ storage?: ActiveSessionStorageLike;
411
+ }
412
+ declare function activeSessionStorageKey(storageScope?: string): string;
413
+ declare function readActiveSessionId(options?: ActiveSessionStorageOptions): string | undefined;
414
+ declare function writeActiveSessionId(sessionId: string | undefined, options?: ActiveSessionStorageOptions): void;
415
+ declare function clearActiveSessionId(options?: ActiveSessionStorageOptions): void;
416
+
417
+ interface PiSessionCreateInit {
418
+ title?: string;
419
+ }
420
+ interface PiSessionRefreshOptions {
421
+ background?: boolean;
422
+ }
423
+ interface UsePiSessionsOptions {
424
+ apiBaseUrl?: string;
425
+ sessionsApiPath?: string;
426
+ workspaceId?: string;
427
+ storageScope?: string;
428
+ requestHeaders?: Record<string, string | undefined>;
429
+ enabled?: boolean;
430
+ refreshKey?: unknown;
431
+ initialActiveSessionId?: string;
432
+ fetch?: typeof globalThis.fetch;
433
+ storage?: ActiveSessionStorageLike;
434
+ createRemoteSession?: (options: RemotePiSessionOptions) => RemotePiSession;
435
+ remoteSessionOptions?: Omit<Partial<RemotePiSessionOptions>, 'sessionId' | 'workspaceId' | 'storageScope' | 'apiBaseUrl' | 'headers' | 'fetch'>;
436
+ connectActiveSession?: boolean;
437
+ retry?: {
438
+ maxRetries?: number;
439
+ baseMs?: number;
440
+ maxMs?: number;
441
+ };
442
+ }
443
+ interface UsePiSessionsResult {
444
+ sessions: SessionSummary[];
445
+ activeSession: SessionSummary | undefined;
446
+ activeSessionId: string | undefined;
447
+ activePiSession: RemotePiSession | undefined;
448
+ dataStorageScope: string;
449
+ loading: boolean;
450
+ loadingMore: boolean;
451
+ hasMore: boolean;
452
+ error: Error | undefined;
453
+ refresh: (options?: PiSessionRefreshOptions) => Promise<void>;
454
+ create: (init?: PiSessionCreateInit) => Promise<SessionSummary>;
455
+ switch: (id: string) => void;
456
+ delete: (id: string) => Promise<void>;
457
+ loadMore: () => Promise<void>;
458
+ reset: () => void;
459
+ }
460
+ declare function usePiSessions(options?: UsePiSessionsOptions): UsePiSessionsResult;
461
+
462
+ interface SessionListProps {
463
+ sessions: SessionSummary[];
464
+ activeId?: string;
465
+ loading?: boolean;
466
+ onSwitch?: (id: string) => void;
467
+ onCreate?: () => void;
468
+ onDelete?: (id: string) => void;
469
+ onLoadMore?: () => void;
470
+ hasMore?: boolean;
471
+ loadingMore?: boolean;
472
+ onClose?: () => void;
473
+ className?: string;
474
+ }
475
+ declare function SessionList({ sessions, activeId, loading, onSwitch, onCreate, onDelete, onLoadMore, hasMore, loadingMore, onClose, className, }: SessionListProps): react_jsx_runtime.JSX.Element;
476
+ declare const SessionBrowser: typeof SessionList;
477
+
478
+ interface ComposerBlockerAction {
479
+ id: string;
480
+ label: string;
481
+ }
482
+ interface ComposerBlocker {
483
+ id: string;
484
+ reason?: string;
485
+ label?: string;
486
+ sessionId?: string;
487
+ actions?: ComposerBlockerAction[];
488
+ surfaceKind?: string;
489
+ target?: unknown;
490
+ }
491
+
492
+ type ChatSubmitSource = 'composer' | 'suggestion' | 'auto-submit';
187
493
  interface ChatSubmitContext {
188
494
  files: FileUIPart[];
189
495
  sessionId: string;
190
496
  source: ChatSubmitSource;
191
497
  }
192
- interface ChatPanelProps {
193
- sessionId: string;
194
- toolRenderers?: ToolRendererOverrides;
498
+ interface ChatPanelEmptyState {
499
+ eyebrow?: string;
500
+ title?: string;
501
+ description?: string;
502
+ }
503
+ interface PiChatPanelProps {
504
+ /** Optional externally selected Pi session id. When provided, session navigation is owned by the host. */
505
+ sessionId?: string;
506
+ /** Alias kept for consumers that still pass the pre-cutover prop name. */
195
507
  extraCommands?: SlashCommand[];
196
- /**
197
- * App-level hot-reload toggle. When `false`, the `/reload` slash
198
- * command is hidden from the picker and the `/help` listing, and the
199
- * PluginUpdateStatus banner above the composer never renders.
200
- * Production apps that don't expose live plugin editing should pass
201
- * `false`. Defaults to `true`.
202
- */
203
- hotReloadEnabled?: boolean;
204
- onSessionReset?: () => void | Promise<void>;
205
- /**
206
- * Render flush, without the outer canvas tint or the inner mx/my rounded
207
- * card chrome. Use when embedding ChatPanel inside a parent that already
208
- * provides its own card surface (e.g. workspace's ChatCenteredShell).
209
- * Defaults to `true` (standalone chrome on).
210
- */
508
+ apiBaseUrl?: string;
509
+ workspaceId?: string;
510
+ storageScope?: string;
511
+ requestHeaders?: Record<string, string | undefined>;
512
+ storage?: ActiveSessionStorageLike;
513
+ fetch?: typeof globalThis.fetch;
514
+ className?: string;
211
515
  chrome?: boolean;
212
- /**
213
- * Cards shown when the conversation is empty. Click → sendMessage with the
214
- * suggestion's `prompt` (or `label` as fallback). Pass `[]` to hide the
215
- * grid; omit to inherit `defaultChatSuggestions`. Customizable per child
216
- * app — e.g. a data-app might offer "Build a chart from a CSV" instead.
217
- */
218
- suggestions?: ChatSuggestion[];
219
- /** Custom empty-state text. Omit to use defaults. */
220
- emptyState?: {
221
- eyebrow?: string;
222
- title?: string;
223
- description?: string;
224
- };
225
- /**
226
- * Render the extended-thinking selector in the composer footer (off / low
227
- * / medium / high). When enabled, the selected level is persisted in
228
- * localStorage and sent through to the agent on every turn. Default off
229
- * — opt-in because not every host wants users tweaking model knobs, and
230
- * thinking budget consumes more tokens.
231
- */
232
- thinkingControl?: boolean;
233
- /**
234
- * Model selected before any local user choice exists. Usually supplied by
235
- * the host's /api/v1/agent/models payload, so deployment env can choose
236
- * the default without rebuilding consumers.
237
- */
238
- defaultModel?: ModelSelection;
239
- /**
240
- * Tap into the SSE data stream. Called for every `onData` part the
241
- * agent emits — host apps use this to bridge agent-driven file
242
- * changes into their own UI plumbing (see
243
- * `useAgentFileChangeBridge` in `@hachej/boring-workspace` for the
244
- * canonical wire-up).
245
- */
246
- onData?: (part: unknown) => void;
247
- /** Headers sent with chat and chat-history requests. */
248
- requestHeaders?: Record<string, string>;
249
- /** When false, skip initial chat history/stream resume requests until the user sends. */
250
- hydrateMessages?: boolean;
251
- /**
252
- * Called with a file path when the user clicks the path label inside
253
- * a read / write / edit tool card. Hosts (e.g. @hachej/boring-workspace)
254
- * supply this to open the file in the surrounding workbench. Without
255
- * it the path renders as plain text. Mounted via context so any
256
- * future renderer can consume it without a prop drill.
257
- */
258
- onOpenArtifact?: OpenArtifactHandler;
259
- /**
260
- * Enable the admin debug drawer — system prompt, raw messages JSON, and
261
- * session activity. Intended for development and ops; keep off in
262
- * production consumer UIs. The drawer chunk is lazy-loaded so this prop
263
- * is zero-cost when off.
264
- */
265
516
  debug?: boolean;
266
- /** Draft text restored into the composer on mount/update. */
517
+ showSessions?: boolean;
518
+ hotReloadEnabled?: boolean;
519
+ suggestions?: ChatSuggestion[];
520
+ emptyState?: ChatPanelEmptyState;
521
+ emptyPlacement?: 'default' | 'hero';
522
+ composerPlaceholder?: string;
267
523
  initialDraft?: string;
268
- /** Auto-submit initialDraft once after it is restored. Defaults to false. */
269
524
  autoSubmitInitialDraft?: boolean;
270
- /** Called after initialDraft is applied to the composer. */
271
525
  onDraftRestored?: () => void;
272
- /** Called after an auto-submitted initial draft is accepted for sending. */
273
526
  onAutoSubmitInitialDraftAccepted?: () => void;
274
- /** Called after an auto-submitted initial draft finishes its turn. */
275
527
  onAutoSubmitInitialDraftSettled?: () => void;
276
- /**
277
- * Runs before any agent/model/session-history network send. Return false to
278
- * cancel submission and keep the draft in the composer.
279
- */
280
- onBeforeSubmit?: (draft: string, ctx: ChatSubmitContext) => false | void | Promise<false | void>;
281
- /** Disable server-backed model/skill discovery for public/local-only shells. */
528
+ model?: ModelSelection | null;
529
+ defaultModel?: ModelSelection;
530
+ availableModels?: AvailableModel[];
531
+ thinkingLevel?: ThinkingLevel;
532
+ thinkingControl?: boolean;
282
533
  serverResourcesEnabled?: boolean;
283
- /** Center the empty-state prompt/composer for public landing experiences. */
284
- emptyPlacement?: 'default' | 'hero';
285
- /** Placeholder shown in the composer textarea. */
286
- composerPlaceholder?: string;
287
- /** Current workspace warmup state. Preparing/failed states keep submits local so drafts are not lost. */
534
+ mentionedFiles?: string[] | (() => string[]);
535
+ commands?: SlashCommand[];
536
+ toolRenderers?: ToolRendererOverrides;
537
+ createRemoteSession?: (options: RemotePiSessionOptions) => RemotePiSession;
538
+ remoteSessionOptions?: UsePiSessionsOptions['remoteSessionOptions'];
539
+ hydrateMessages?: boolean;
288
540
  workspaceWarmupStatus?: ChatPanelWorkspaceWarmupStatus;
289
- /** Generic host-provided blockers that prevent starting a new user turn. */
541
+ onSessionReset?: () => void | Promise<void>;
542
+ onBeforeSubmit?: (draft: string, context: ChatSubmitContext) => false | void | boolean | Promise<false | void | boolean>;
543
+ onReloadAgentPlugins?: () => Promise<string>;
544
+ onCommandResult?: (message: string) => void;
545
+ onComposerWarning?: (message: string) => void;
546
+ onMentionedFilesConsumed?: () => void;
547
+ onData?: (part: unknown) => void;
548
+ onOpenArtifact?: (path: string) => void;
290
549
  composerBlockers?: ComposerBlocker[];
291
- /** Called when the user presses Stop in the composer. */
292
550
  onComposerStop?: () => void;
293
551
  onComposerBlockerAction?: (blocker: ComposerBlocker, action: string) => void;
294
- className?: string;
295
552
  }
296
- declare function ChatPanel(props: ChatPanelProps): react_jsx_runtime.JSX.Element;
553
+ declare function PiChatPanel({ sessionId, extraCommands, apiBaseUrl, workspaceId, storageScope, requestHeaders, storage, fetch, className, chrome, debug, showSessions, hotReloadEnabled, suggestions, emptyState, emptyPlacement, composerPlaceholder, initialDraft, autoSubmitInitialDraft, onDraftRestored, onAutoSubmitInitialDraftAccepted, onAutoSubmitInitialDraftSettled, model, defaultModel, availableModels, thinkingLevel, thinkingControl, serverResourcesEnabled, mentionedFiles, commands, toolRenderers, createRemoteSession, remoteSessionOptions, hydrateMessages, workspaceWarmupStatus, onSessionReset, onBeforeSubmit, onReloadAgentPlugins, onCommandResult, onComposerWarning, onMentionedFilesConsumed, onData, onOpenArtifact, composerBlockers, onComposerStop, onComposerBlockerAction, }: PiChatPanelProps): react_jsx_runtime.JSX.Element;
297
554
 
298
555
  interface DebugDrawerProps {
556
+ apiBaseUrl?: string;
557
+ fetch?: typeof globalThis.fetch;
299
558
  sessionId: string;
300
559
  messages: UIMessage[];
301
560
  requestHeaders?: Record<string, string>;
561
+ storageScope?: string;
302
562
  width: number;
303
563
  onWidthChange: (w: number) => void;
304
564
  }
305
- declare function DebugDrawer({ sessionId, messages, requestHeaders, width, onWidthChange }: DebugDrawerProps): react_jsx_runtime.JSX.Element;
565
+ declare function DebugDrawer({ apiBaseUrl, fetch, sessionId, messages, requestHeaders, storageScope, width, onWidthChange }: DebugDrawerProps): react_jsx_runtime.JSX.Element;
566
+
567
+ type OpenArtifactHandler = (path: string) => void;
568
+ interface ArtifactOpenProviderProps {
569
+ onOpenArtifact?: OpenArtifactHandler;
570
+ children: ReactNode;
571
+ }
572
+ declare function ArtifactOpenProvider({ onOpenArtifact, children }: ArtifactOpenProviderProps): react_jsx_runtime.JSX.Element;
573
+ declare function useOpenArtifact(): OpenArtifactHandler | null;
306
574
 
307
575
  interface AgentCommandContribution {
308
576
  id: string;
@@ -321,93 +589,17 @@ interface AgentCommandOptions {
321
589
  }
322
590
  declare function getAgentCommands(options?: AgentCommandOptions): AgentCommandContribution[];
323
591
 
324
- type UseAgentChatOptions = Pick<SendMessageInput, 'sessionId' | 'model' | 'thinkingLevel'> & {
325
- onData?: (part: unknown) => void;
326
- requestHeaders?: Record<string, string>;
327
- persistMessages?: boolean;
328
- hydrateMessages?: boolean;
329
- };
330
- declare function useAgentChat(opts: UseAgentChatOptions): {
331
- messages: UIMessage<unknown, ai.UIDataTypes, ai.UITools>[];
332
- sendMessage: (message?: (Omit<UIMessage<unknown, ai.UIDataTypes, ai.UITools>, "role" | "id"> & {
333
- id?: string | undefined;
334
- role?: "system" | "user" | "assistant" | undefined;
335
- } & {
336
- text?: never;
337
- files?: never;
338
- messageId?: string;
339
- }) | {
340
- text: string;
341
- files?: FileList | ai.FileUIPart[];
342
- metadata?: unknown;
343
- parts?: never;
344
- messageId?: string;
345
- } | {
346
- files: FileList | ai.FileUIPart[];
347
- metadata?: unknown;
348
- parts?: never;
349
- messageId?: string;
350
- } | undefined, options?: ai.ChatRequestOptions | undefined) => Promise<void>;
351
- stop: () => void;
352
- status: ai.ChatStatus;
353
- hydrated: boolean;
354
- hydratingMessages: boolean;
355
- id: string;
356
- setMessages: (messages: UIMessage<unknown, ai.UIDataTypes, ai.UITools>[] | ((messages: UIMessage<unknown, ai.UIDataTypes, ai.UITools>[]) => UIMessage<unknown, ai.UIDataTypes, ai.UITools>[])) => void;
357
- error: Error | undefined;
358
- regenerate: ({ messageId, ...options }?: {
359
- messageId?: string;
360
- } & ai.ChatRequestOptions) => Promise<void>;
361
- resumeStream: (options?: ai.ChatRequestOptions) => Promise<void>;
362
- addToolResult: ai.ChatAddToolOutputFunction<UIMessage<unknown, ai.UIDataTypes, ai.UITools>>;
363
- addToolOutput: ai.ChatAddToolOutputFunction<UIMessage<unknown, ai.UIDataTypes, ai.UITools>>;
364
- addToolApprovalResponse: ai.ChatAddToolApproveResponseFunction;
365
- clearError: () => void;
366
- };
367
-
368
- interface UseSessionsOptions {
369
- requestHeaders?: Record<string, string>;
370
- storageKey?: string;
371
- enabled?: boolean;
372
- refreshKey?: unknown;
373
- initialActiveSessionId?: string;
374
- }
375
- interface UseSessionsResult {
376
- sessions: SessionSummary[];
377
- activeSession: SessionSummary | undefined;
378
- activeSessionId: string | undefined;
379
- loading: boolean;
380
- loadingMore: boolean;
381
- hasMore: boolean;
382
- error: Error | undefined;
383
- create: (init?: {
384
- title?: string;
385
- }) => Promise<SessionSummary>;
386
- switch: (id: string) => void;
387
- delete: (id: string) => Promise<void>;
388
- loadMore: () => Promise<void>;
389
- }
390
- declare function useSessions(opts?: UseSessionsOptions): UseSessionsResult;
391
-
392
- interface ParsedCommand {
393
- name: string;
394
- args: string;
395
- }
396
- declare function parseSlashCommand(text: string): ParsedCommand | null;
397
-
398
- declare const builtinCommands: SlashCommand[];
399
-
400
592
  declare function mergeShadcnToolRenderers(overrides?: ToolRendererOverrides): Record<string, ToolRenderer>;
401
593
 
402
594
  type GroupedToolEntry = {
403
- part: UIMessage['parts'][number];
595
+ part: ToolRenderablePart;
404
596
  key: string;
405
597
  };
406
598
  interface ToolCallGroupProps {
407
599
  tools: GroupedToolEntry[];
408
600
  mergedToolRenderers: ToolRendererOverrides;
409
601
  }
410
- declare const ToolCallGroup: react.MemoExoticComponent<({ tools, mergedToolRenderers }: ToolCallGroupProps) => react_jsx_runtime.JSX.Element>;
602
+ declare const ToolCallGroup: react.MemoExoticComponent<({ tools, mergedToolRenderers }: ToolCallGroupProps) => react_jsx_runtime.JSX.Element | null>;
411
603
 
412
604
  type MessageProps = HTMLAttributes<HTMLDivElement> & {
413
605
  from: UIMessage["role"];
@@ -440,8 +632,9 @@ type ReasoningProps = ComponentProps<typeof Collapsible> & {
440
632
  defaultOpen?: boolean;
441
633
  onOpenChange?: (open: boolean) => void;
442
634
  duration?: number;
635
+ autoClose?: boolean;
443
636
  };
444
- declare const Reasoning: react.MemoExoticComponent<({ className, isStreaming, open, defaultOpen, onOpenChange, duration: durationProp, children, ...props }: ReasoningProps) => react_jsx_runtime.JSX.Element>;
637
+ declare const Reasoning: react.MemoExoticComponent<({ className, isStreaming, open, defaultOpen, onOpenChange, duration: durationProp, autoClose, children, ...props }: ReasoningProps) => react_jsx_runtime.JSX.Element>;
445
638
  type ReasoningTriggerProps = ComponentProps<typeof CollapsibleTrigger> & {
446
639
  getThinkingMessage?: (isStreaming: boolean, duration?: number) => ReactNode;
447
640
  };
@@ -458,8 +651,8 @@ type CodeBlockProps = HTMLAttributes<HTMLDivElement> & {
458
651
  };
459
652
  declare const CodeBlock: ({ code, language, showLineNumbers, className, children, ...props }: CodeBlockProps) => react_jsx_runtime.JSX.Element;
460
653
 
461
- type PromptInputFooterProps = Omit<ComponentProps<typeof InputGroupAddon>, "align">;
462
- declare const PromptInputFooter: ({ className, ...props }: PromptInputFooterProps) => react_jsx_runtime.JSX.Element;
654
+ type PromptInputFooterProps = ComponentProps<typeof InputGroupAddon>;
655
+ declare const PromptInputFooter: ({ align, className, ...props }: PromptInputFooterProps) => react_jsx_runtime.JSX.Element;
463
656
 
464
657
  interface PromptInputMessage {
465
658
  text: string;
@@ -494,4 +687,4 @@ declare const PromptInputSubmit: ({ className, variant, size, status, onStop, on
494
687
 
495
688
  declare function cn(...inputs: ClassValue[]): string;
496
689
 
497
- export { type AgentCommandContribution, type AgentCommandOptions, ArtifactOpenProvider, ChatEmptyState, type ChatEmptyStateProps, ChatPanel, type ChatPanelProps, type ChatSuggestion, CodeBlock, type CommandRegistry, Conversation, ConversationContent, ConversationScrollButton, DebugDrawer, type GroupedToolEntry, Message, MessageAction, MessageActions, MessageContent, MessageResponse, type OpenArtifactHandler, type ParsedCommand, PromptInput, PromptInputFooter, PromptInputSubmit, PromptInputTextarea, Reasoning, ReasoningContent, ReasoningTrigger, type SlashCommand, type SlashCommandContext, ToolCallGroup, type ToolPart, type ToolRenderer, type ToolRendererOverrides, type UploadFileOptions, type UploadFileResult, type UseAgentChatOptions, type UseSessionsOptions, type UseSessionsResult, builtinCommands, cn, createCommandRegistry, defaultChatSuggestions, defaultToolRenderers, getAgentCommands, mergeShadcnToolRenderers, mergeToolRenderers, parseSlashCommand, resolveToolRenderer, uploadFile, useAgentChat, useOpenArtifact, useSessions };
690
+ export { type AgentCommandContribution, type AgentCommandOptions, ArtifactOpenProvider, ChatEmptyState, type ChatEmptyStateProps, PiChatPanel as ChatPanel, type PiChatPanelProps as ChatPanelProps, type ChatSuggestion, CodeBlock, type CommandRegistry, Conversation, ConversationContent, ConversationScrollButton, DebugDrawer, type GroupedToolEntry, Message, MessageAction, MessageActions, MessageContent, MessageResponse, type OpenArtifactHandler, type ParsedCommand, PiChatPanel, type PiChatPanelProps, SessionBrowser as PiSessionBrowser, type PiSessionCreateInit, SessionList as PiSessionList, PromptInput, PromptInputFooter, PromptInputSubmit, PromptInputTextarea, Reasoning, ReasoningContent, ReasoningTrigger, type SessionListProps, type SlashCommand, type SlashCommandContext, ToolCallGroup, type ToolPart, type ToolRenderer, type ToolRendererOverrides, type UploadFileOptions, type UploadFileResult, type UsePiSessionsOptions, type UsePiSessionsResult, activeSessionStorageKey, builtinCommands, clearActiveSessionId, cn, createCommandRegistry, defaultChatSuggestions, defaultToolRenderers, getAgentCommands, mergeShadcnToolRenderers, mergeToolRenderers, parseSlashCommand, readActiveSessionId, resolveToolRenderer, uploadFile, useOpenArtifact, usePiSessions, writeActiveSessionId };