@hachej/boring-agent 0.1.33 → 0.1.35

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,85 @@ 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
+ /** 'skill' commands are forwarded to the PI agent as `skill: <name>\n\n<args>` instead of running locally. */
122
+ kind?: 'local' | 'skill';
123
+ handler: SlashCommandHandler;
124
+ }
125
+ interface SlashCommandContext {
126
+ sessionId: string;
127
+ clearMessages: () => void;
128
+ resetSession: () => void;
129
+ listCommands: () => SlashCommand[];
130
+ reloadAgentPlugins: () => Promise<string>;
131
+ openModelPicker?: () => boolean | void;
132
+ selectComposerModel?: (query: string) => string | void;
133
+ openThinkingPicker?: () => boolean | void;
134
+ selectComposerThinking?: (query: string) => string | void;
135
+ /**
136
+ * Drives the PluginUpdateStatus banner above the composer. The `/reload`
137
+ * builtin prefers this over the inline-text path: it calls
138
+ * `pluginUpdate.run()` which (1) sets the banner to "running", (2)
139
+ * hits /api/v1/agent/reload, (3) transitions to "success" or "error"
140
+ * with diagnostics. Returns a short string ack for the assistant
141
+ * message bubble.
142
+ */
143
+ pluginUpdate?: {
144
+ run: () => Promise<string>;
145
+ };
146
+ }
147
+ interface CommandRegistry {
148
+ register(cmd: SlashCommand): void;
149
+ get(name: string): SlashCommand | undefined;
150
+ list(): SlashCommand[];
151
+ }
152
+ declare function createCommandRegistry(initial?: SlashCommand[]): CommandRegistry;
153
+
154
+ declare const builtinCommands: SlashCommand[];
155
+
156
+ type ToolState = 'input-streaming' | 'input-available' | 'approval-requested' | 'approval-responded' | 'output-available' | 'output-error' | 'output-denied' | 'aborted';
157
+
158
+ type BoringToolCallPart = Extract<BoringChatPart, {
159
+ type: 'tool-call';
160
+ }>;
161
+ interface ToolRendererResolution {
162
+ key: string;
163
+ source: 'rendererId' | 'toolName' | 'fallback';
164
+ requestedRendererId?: string;
165
+ }
166
+ interface ToolPart {
167
+ /** Neutral render-model type. Legacy `tool-<name>` values are accepted at the renderer boundary. */
168
+ type: 'tool-call' | `tool-${string}` | 'dynamic-tool';
169
+ toolName: string;
170
+ /** Stable tool call id; for Pi-native BoringChatPart this is BoringChatPart.id. */
171
+ toolCallId: string;
172
+ state: ToolState;
173
+ input?: unknown;
174
+ output?: unknown;
175
+ errorText?: string;
176
+ ui?: ToolUiMetadata;
177
+ rendererResolution?: ToolRendererResolution;
178
+ }
179
+ type ToolRenderablePart = ToolPart | BoringToolCallPart | unknown;
180
+ type ToolRenderer = (part: ToolPart) => ReactNode;
181
+ type ToolRendererOverrides = Partial<Record<string, ToolRenderer>>;
182
+ declare const defaultToolRenderers: Record<string, ToolRenderer>;
183
+ declare function mergeToolRenderers(overrides?: ToolRendererOverrides): Record<string, ToolRenderer>;
184
+ declare function resolveToolRenderer(toolNameOrPart: string | ToolPart, overrides?: ToolRendererOverrides): ToolRenderer;
185
+
167
186
  type ChatPanelRuntimeDependenciesWarmupStatus = {
168
187
  state: 'preparing' | 'ready' | 'failed';
169
188
  message?: string;
@@ -183,126 +202,360 @@ type ChatPanelWorkspaceWarmupStatus = {
183
202
  message?: string;
184
203
  runtimeDependencies?: ChatPanelRuntimeDependenciesWarmupStatus;
185
204
  };
186
- type ChatSubmitSource = 'composer' | 'suggestion';
205
+
206
+ interface OptimisticUserMessage extends BoringChatMessage {
207
+ role: 'user';
208
+ clientNonce: string;
209
+ clientSeq?: number;
210
+ /**
211
+ * Id of the last committed message when this optimistic prompt was submitted.
212
+ * Used to position the placeholder by sequence rather than by wall-clock time,
213
+ * so client/server clock skew can't render a just-sent prompt above the
214
+ * previous reply.
215
+ */
216
+ afterMessageId?: string;
217
+ }
218
+
219
+ type PiChatConnectionState = 'disconnected' | 'connecting' | 'connected' | 'reconnecting';
220
+
221
+ interface PiChatRuntimeNotice {
222
+ id: string;
223
+ level: 'info' | 'warning' | 'error';
224
+ text: string;
225
+ dismissible?: boolean;
226
+ }
227
+ interface PiChatRetryNotice {
228
+ attempt: number;
229
+ maxAttempts: number;
230
+ delayMs: number;
231
+ errorMessage: string;
232
+ }
233
+ interface PiChatSeqGap {
234
+ expectedSeq: number;
235
+ actualSeq: number;
236
+ lastSeq: number;
237
+ }
238
+ interface PiChatHistoryState {
239
+ /** First cut renders full history; this explicit shape leaves room for paged windows later. */
240
+ mode: 'full';
241
+ messageCount: number;
242
+ }
243
+ interface PiChatState {
244
+ sessionId: string;
245
+ workspaceId?: string;
246
+ storageScope: string;
247
+ status: PiChatStatus;
248
+ turnId?: string;
249
+ lastSeq: number;
250
+ committedMessages: BoringChatMessage[];
251
+ streamingMessage?: BoringChatMessage;
252
+ streamingPreservedTextPartKeys?: Set<string>;
253
+ history: PiChatHistoryState;
254
+ queue: {
255
+ followUps: QueuedUserMessage[];
256
+ };
257
+ optimisticOutbox: Record<string, OptimisticUserMessage>;
258
+ pendingToolCallIds: Set<string>;
259
+ connection: {
260
+ state: PiChatConnectionState;
261
+ lastHeartbeatAt?: number;
262
+ };
263
+ error?: ChatError;
264
+ retryNotice?: PiChatRetryNotice;
265
+ notices: PiChatRuntimeNotice[];
266
+ hydrated: boolean;
267
+ needsResync?: PiChatSeqGap;
268
+ }
269
+
270
+ type PiChatStoreListener = () => void;
271
+ interface PiChatStoreOptions {
272
+ scheduleNotify?: (notify: () => void) => unknown;
273
+ cancelNotify?: (handle: unknown) => void;
274
+ }
275
+
276
+ interface RemotePiSessionHeaders {
277
+ [key: string]: string | undefined;
278
+ }
279
+ interface RemotePiSessionOptions {
280
+ sessionId: string;
281
+ workspaceId?: string;
282
+ storageScope?: string;
283
+ apiBaseUrl?: string;
284
+ headers?: RemotePiSessionHeaders | (() => RemotePiSessionHeaders | Promise<RemotePiSessionHeaders>);
285
+ fetch?: typeof globalThis.fetch;
286
+ onEvent?: (event: PiChatEvent) => void;
287
+ storeOptions?: PiChatStoreOptions;
288
+ autoStart?: boolean;
289
+ reconnect?: {
290
+ baseMs?: number;
291
+ maxMs?: number;
292
+ jitterRatio?: number;
293
+ random?: () => number;
294
+ };
295
+ debug?: {
296
+ largeStateWarningBytes?: number;
297
+ largeStateWarningMessages?: number;
298
+ onWarning?: (warning: RemotePiSessionLargeStateWarning) => void;
299
+ };
300
+ setTimeoutFn?: typeof globalThis.setTimeout;
301
+ clearTimeoutFn?: typeof globalThis.clearTimeout;
302
+ }
303
+ interface RemotePiSessionLargeStateWarning {
304
+ type: 'large-state';
305
+ sessionId: string;
306
+ approxBytes: number;
307
+ messageCount: number;
308
+ thresholdBytes: number;
309
+ thresholdMessages: number;
310
+ }
311
+ interface RemotePiSessionDebugState {
312
+ sessionId: string;
313
+ lastSeq: number;
314
+ status: PiChatState['status'];
315
+ connection: PiChatState['connection']['state'];
316
+ lastHeartbeatAt?: number;
317
+ queue: {
318
+ followUps: number;
319
+ optimisticOutbox: number;
320
+ pendingToolCalls: number;
321
+ };
322
+ recentEventTypes: string[];
323
+ gapCount: number;
324
+ retryNotice?: PiChatState['retryNotice'];
325
+ largeStateWarning?: RemotePiSessionLargeStateWarning;
326
+ history: {
327
+ mode: 'full';
328
+ messageCount: number;
329
+ streamingMessageCount: 0 | 1;
330
+ };
331
+ disposed: boolean;
332
+ generation: number;
333
+ streamRunId: number;
334
+ reconnectAttempt: number;
335
+ hasReconnectTimer: boolean;
336
+ inflightFetches: number;
337
+ }
338
+ declare class RemotePiSession {
339
+ private readonly options;
340
+ private readonly store;
341
+ private readonly fetchImpl;
342
+ private readonly apiBaseUrl;
343
+ private readonly storageScope;
344
+ private readonly setTimeoutFn;
345
+ private readonly clearTimeoutFn;
346
+ private generation;
347
+ private streamRunId;
348
+ private reconnectAttempt;
349
+ private started;
350
+ private disposed;
351
+ private streamAbortController?;
352
+ private reconnectTimer?;
353
+ private readonly fetchControllers;
354
+ private readonly recentEventTypes;
355
+ private gapCount;
356
+ private largeStateWarning?;
357
+ constructor(options: RemotePiSessionOptions);
358
+ getState(): PiChatState;
359
+ getDebugState(): RemotePiSessionDebugState;
360
+ subscribe(listener: PiChatStoreListener): () => void;
361
+ start(cursor?: number): Promise<void>;
362
+ prompt(payload: PromptPayload): Promise<PromptReceipt>;
363
+ followUp(payload: FollowUpPayload): Promise<FollowUpReceipt>;
364
+ clearQueue(payload?: QueueClearPayload): Promise<QueueClearReceipt>;
365
+ interrupt(payload?: InterruptPayload): Promise<CommandReceipt>;
366
+ stop(payload?: StopPayload): Promise<StopReceipt>;
367
+ dispose(): void;
368
+ private hydrateAndConnect;
369
+ private connectEvents;
370
+ private runEventStream;
371
+ private rehydrateAfterStreamReset;
372
+ private scheduleReconnect;
373
+ private postCommand;
374
+ private rollbackOptimisticMessage;
375
+ private fetchJson;
376
+ private requestHeaders;
377
+ private stateUrl;
378
+ private sessionUrl;
379
+ private dispatchProtocolError;
380
+ private recordEventType;
381
+ private recordLargeStateWarning;
382
+ private clearReconnectTimer;
383
+ private abortEventStream;
384
+ private isGenerationActive;
385
+ private isStreamActive;
386
+ }
387
+
388
+ interface ActiveSessionStorageLike {
389
+ getItem(key: string): string | null;
390
+ setItem(key: string, value: string): void;
391
+ removeItem(key: string): void;
392
+ }
393
+ interface ActiveSessionStorageOptions {
394
+ storageScope?: string;
395
+ storage?: ActiveSessionStorageLike;
396
+ }
397
+ declare function activeSessionStorageKey(storageScope?: string): string;
398
+ declare function readActiveSessionId(options?: ActiveSessionStorageOptions): string | undefined;
399
+ declare function writeActiveSessionId(sessionId: string | undefined, options?: ActiveSessionStorageOptions): void;
400
+ declare function clearActiveSessionId(options?: ActiveSessionStorageOptions): void;
401
+
402
+ interface PiSessionCreateInit {
403
+ title?: string;
404
+ }
405
+ interface PiSessionRefreshOptions {
406
+ background?: boolean;
407
+ }
408
+ interface UsePiSessionsOptions {
409
+ apiBaseUrl?: string;
410
+ sessionsApiPath?: string;
411
+ workspaceId?: string;
412
+ storageScope?: string;
413
+ requestHeaders?: Record<string, string | undefined>;
414
+ enabled?: boolean;
415
+ refreshKey?: unknown;
416
+ initialActiveSessionId?: string;
417
+ fetch?: typeof globalThis.fetch;
418
+ storage?: ActiveSessionStorageLike;
419
+ createRemoteSession?: (options: RemotePiSessionOptions) => RemotePiSession;
420
+ remoteSessionOptions?: Omit<Partial<RemotePiSessionOptions>, 'sessionId' | 'workspaceId' | 'storageScope' | 'apiBaseUrl' | 'headers' | 'fetch'>;
421
+ connectActiveSession?: boolean;
422
+ retry?: {
423
+ maxRetries?: number;
424
+ baseMs?: number;
425
+ maxMs?: number;
426
+ };
427
+ }
428
+ interface UsePiSessionsResult {
429
+ sessions: SessionSummary[];
430
+ activeSession: SessionSummary | undefined;
431
+ activeSessionId: string | undefined;
432
+ activePiSession: RemotePiSession | undefined;
433
+ dataStorageScope: string;
434
+ loading: boolean;
435
+ loadingMore: boolean;
436
+ hasMore: boolean;
437
+ error: Error | undefined;
438
+ refresh: (options?: PiSessionRefreshOptions) => Promise<void>;
439
+ create: (init?: PiSessionCreateInit) => Promise<SessionSummary>;
440
+ switch: (id: string) => void;
441
+ delete: (id: string) => Promise<void>;
442
+ loadMore: () => Promise<void>;
443
+ reset: () => void;
444
+ }
445
+ declare function usePiSessions(options?: UsePiSessionsOptions): UsePiSessionsResult;
446
+
447
+ interface SessionListProps {
448
+ sessions: SessionSummary[];
449
+ activeId?: string;
450
+ loading?: boolean;
451
+ onSwitch?: (id: string) => void;
452
+ onCreate?: () => void;
453
+ onDelete?: (id: string) => void;
454
+ onLoadMore?: () => void;
455
+ hasMore?: boolean;
456
+ loadingMore?: boolean;
457
+ onClose?: () => void;
458
+ className?: string;
459
+ }
460
+ declare function SessionList({ sessions, activeId, loading, onSwitch, onCreate, onDelete, onLoadMore, hasMore, loadingMore, onClose, className, }: SessionListProps): react_jsx_runtime.JSX.Element;
461
+ declare const SessionBrowser: typeof SessionList;
462
+
463
+ interface ComposerBlockerAction {
464
+ id: string;
465
+ label: string;
466
+ }
467
+ interface ComposerBlocker {
468
+ id: string;
469
+ reason?: string;
470
+ label?: string;
471
+ sessionId?: string;
472
+ actions?: ComposerBlockerAction[];
473
+ surfaceKind?: string;
474
+ target?: unknown;
475
+ }
476
+
477
+ type ChatSubmitSource = 'composer' | 'suggestion' | 'auto-submit';
187
478
  interface ChatSubmitContext {
188
479
  files: FileUIPart[];
189
480
  sessionId: string;
190
481
  source: ChatSubmitSource;
191
482
  }
192
- interface ChatPanelProps {
193
- sessionId: string;
194
- toolRenderers?: ToolRendererOverrides;
483
+ interface ChatPanelEmptyState {
484
+ eyebrow?: string;
485
+ title?: string;
486
+ description?: string;
487
+ }
488
+ interface PiChatPanelProps {
489
+ /** Optional externally selected Pi session id. When provided, session navigation is owned by the host. */
490
+ sessionId?: string;
491
+ /** Alias kept for consumers that still pass the pre-cutover prop name. */
195
492
  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
- */
493
+ apiBaseUrl?: string;
494
+ workspaceId?: string;
495
+ storageScope?: string;
496
+ requestHeaders?: Record<string, string | undefined>;
497
+ storage?: ActiveSessionStorageLike;
498
+ fetch?: typeof globalThis.fetch;
499
+ className?: string;
211
500
  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
501
  debug?: boolean;
266
- /** Draft text restored into the composer on mount/update. */
502
+ showSessions?: boolean;
503
+ hotReloadEnabled?: boolean;
504
+ suggestions?: ChatSuggestion[];
505
+ emptyState?: ChatPanelEmptyState;
506
+ emptyPlacement?: 'default' | 'hero';
507
+ composerPlaceholder?: string;
267
508
  initialDraft?: string;
268
- /** Auto-submit initialDraft once after it is restored. Defaults to false. */
269
509
  autoSubmitInitialDraft?: boolean;
270
- /** Called after initialDraft is applied to the composer. */
271
510
  onDraftRestored?: () => void;
272
- /** Called after an auto-submitted initial draft is accepted for sending. */
273
511
  onAutoSubmitInitialDraftAccepted?: () => void;
274
- /** Called after an auto-submitted initial draft finishes its turn. */
275
512
  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. */
513
+ model?: ModelSelection | null;
514
+ defaultModel?: ModelSelection;
515
+ availableModels?: AvailableModel[];
516
+ thinkingLevel?: ThinkingLevel;
517
+ thinkingControl?: boolean;
282
518
  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. */
519
+ mentionedFiles?: string[] | (() => string[]);
520
+ commands?: SlashCommand[];
521
+ toolRenderers?: ToolRendererOverrides;
522
+ createRemoteSession?: (options: RemotePiSessionOptions) => RemotePiSession;
523
+ remoteSessionOptions?: UsePiSessionsOptions['remoteSessionOptions'];
524
+ hydrateMessages?: boolean;
288
525
  workspaceWarmupStatus?: ChatPanelWorkspaceWarmupStatus;
289
- /** Generic host-provided blockers that prevent starting a new user turn. */
526
+ onSessionReset?: () => void | Promise<void>;
527
+ onBeforeSubmit?: (draft: string, context: ChatSubmitContext) => false | void | boolean | Promise<false | void | boolean>;
528
+ onReloadAgentPlugins?: () => Promise<string>;
529
+ onCommandResult?: (message: string) => void;
530
+ onComposerWarning?: (message: string) => void;
531
+ onMentionedFilesConsumed?: () => void;
532
+ onData?: (part: unknown) => void;
533
+ onOpenArtifact?: (path: string) => void;
290
534
  composerBlockers?: ComposerBlocker[];
291
- /** Called when the user presses Stop in the composer. */
292
535
  onComposerStop?: () => void;
293
536
  onComposerBlockerAction?: (blocker: ComposerBlocker, action: string) => void;
294
- className?: string;
295
537
  }
296
- declare function ChatPanel(props: ChatPanelProps): react_jsx_runtime.JSX.Element;
538
+ 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
539
 
298
540
  interface DebugDrawerProps {
541
+ apiBaseUrl?: string;
542
+ fetch?: typeof globalThis.fetch;
299
543
  sessionId: string;
300
544
  messages: UIMessage[];
301
545
  requestHeaders?: Record<string, string>;
546
+ storageScope?: string;
302
547
  width: number;
303
548
  onWidthChange: (w: number) => void;
304
549
  }
305
- declare function DebugDrawer({ sessionId, messages, requestHeaders, width, onWidthChange }: DebugDrawerProps): react_jsx_runtime.JSX.Element;
550
+ declare function DebugDrawer({ apiBaseUrl, fetch, sessionId, messages, requestHeaders, storageScope, width, onWidthChange }: DebugDrawerProps): react_jsx_runtime.JSX.Element;
551
+
552
+ type OpenArtifactHandler = (path: string) => void;
553
+ interface ArtifactOpenProviderProps {
554
+ onOpenArtifact?: OpenArtifactHandler;
555
+ children: ReactNode;
556
+ }
557
+ declare function ArtifactOpenProvider({ onOpenArtifact, children }: ArtifactOpenProviderProps): react_jsx_runtime.JSX.Element;
558
+ declare function useOpenArtifact(): OpenArtifactHandler | null;
306
559
 
307
560
  interface AgentCommandContribution {
308
561
  id: string;
@@ -321,93 +574,17 @@ interface AgentCommandOptions {
321
574
  }
322
575
  declare function getAgentCommands(options?: AgentCommandOptions): AgentCommandContribution[];
323
576
 
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
577
  declare function mergeShadcnToolRenderers(overrides?: ToolRendererOverrides): Record<string, ToolRenderer>;
401
578
 
402
579
  type GroupedToolEntry = {
403
- part: UIMessage['parts'][number];
580
+ part: ToolRenderablePart;
404
581
  key: string;
405
582
  };
406
583
  interface ToolCallGroupProps {
407
584
  tools: GroupedToolEntry[];
408
585
  mergedToolRenderers: ToolRendererOverrides;
409
586
  }
410
- declare const ToolCallGroup: react.MemoExoticComponent<({ tools, mergedToolRenderers }: ToolCallGroupProps) => react_jsx_runtime.JSX.Element>;
587
+ declare const ToolCallGroup: react.MemoExoticComponent<({ tools, mergedToolRenderers }: ToolCallGroupProps) => react_jsx_runtime.JSX.Element | null>;
411
588
 
412
589
  type MessageProps = HTMLAttributes<HTMLDivElement> & {
413
590
  from: UIMessage["role"];
@@ -440,8 +617,9 @@ type ReasoningProps = ComponentProps<typeof Collapsible> & {
440
617
  defaultOpen?: boolean;
441
618
  onOpenChange?: (open: boolean) => void;
442
619
  duration?: number;
620
+ autoClose?: boolean;
443
621
  };
444
- declare const Reasoning: react.MemoExoticComponent<({ className, isStreaming, open, defaultOpen, onOpenChange, duration: durationProp, children, ...props }: ReasoningProps) => react_jsx_runtime.JSX.Element>;
622
+ declare const Reasoning: react.MemoExoticComponent<({ className, isStreaming, open, defaultOpen, onOpenChange, duration: durationProp, autoClose, children, ...props }: ReasoningProps) => react_jsx_runtime.JSX.Element>;
445
623
  type ReasoningTriggerProps = ComponentProps<typeof CollapsibleTrigger> & {
446
624
  getThinkingMessage?: (isStreaming: boolean, duration?: number) => ReactNode;
447
625
  };
@@ -458,8 +636,8 @@ type CodeBlockProps = HTMLAttributes<HTMLDivElement> & {
458
636
  };
459
637
  declare const CodeBlock: ({ code, language, showLineNumbers, className, children, ...props }: CodeBlockProps) => react_jsx_runtime.JSX.Element;
460
638
 
461
- type PromptInputFooterProps = Omit<ComponentProps<typeof InputGroupAddon>, "align">;
462
- declare const PromptInputFooter: ({ className, ...props }: PromptInputFooterProps) => react_jsx_runtime.JSX.Element;
639
+ type PromptInputFooterProps = ComponentProps<typeof InputGroupAddon>;
640
+ declare const PromptInputFooter: ({ align, className, ...props }: PromptInputFooterProps) => react_jsx_runtime.JSX.Element;
463
641
 
464
642
  interface PromptInputMessage {
465
643
  text: string;
@@ -494,4 +672,4 @@ declare const PromptInputSubmit: ({ className, variant, size, status, onStop, on
494
672
 
495
673
  declare function cn(...inputs: ClassValue[]): string;
496
674
 
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 };
675
+ 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 };