@dataverse-kit/agent-kit 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +166 -0
- package/dist/core.cjs +532 -0
- package/dist/core.cjs.map +1 -0
- package/dist/core.d.cts +278 -0
- package/dist/core.d.ts +278 -0
- package/dist/core.mjs +482 -0
- package/dist/core.mjs.map +1 -0
- package/dist/host.cjs +113 -0
- package/dist/host.cjs.map +1 -0
- package/dist/host.d.cts +3 -0
- package/dist/host.d.ts +3 -0
- package/dist/host.mjs +74 -0
- package/dist/host.mjs.map +1 -0
- package/dist/index-DGN6eYnp.d.cts +103 -0
- package/dist/index-DGN6eYnp.d.ts +103 -0
- package/dist/index.cjs +3145 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +863 -0
- package/dist/index.d.ts +863 -0
- package/dist/index.mjs +3068 -0
- package/dist/index.mjs.map +1 -0
- package/dist/protocol-CZJ1v9Hq.d.cts +270 -0
- package/dist/protocol-CZJ1v9Hq.d.ts +270 -0
- package/dist/services.cjs +333 -0
- package/dist/services.cjs.map +1 -0
- package/dist/services.d.cts +118 -0
- package/dist/services.d.ts +118 -0
- package/dist/services.mjs +300 -0
- package/dist/services.mjs.map +1 -0
- package/package.json +89 -0
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,863 @@
|
|
|
1
|
+
import * as React$1 from 'react';
|
|
2
|
+
import { A as AgentMessage, a as AgentParticipant, b as AgentFailure, c as AgentRunStatus, d as AgentAvailability, e as AgentUsage, f as AgentSource, g as AgentCitation, h as AgentToolCall, i as AgentAttachment, I as IAgentService } from './protocol-CZJ1v9Hq.cjs';
|
|
3
|
+
export { j as AgentCapabilities, k as AgentEvent, l as AgentProviderName, m as AgentRole, n as AgentServiceConfig, o as AgentTurn, p as AgentTurnResult, T as ToolCallStatus } from './protocol-CZJ1v9Hq.cjs';
|
|
4
|
+
import { Announcement, DayDividerLabel, ConversationState } from './core.cjs';
|
|
5
|
+
export { ANNOUNCE_FULL_TEXT_MAX_CHARS, CitationSegment, ClaudeLikeStreamEvent, ConversationStatus, DEFAULT_GROUP_WINDOW_MS, DayBucket, ExtractedMarker, FoundryLikeResult, GroupingOptions, MessageGroup, NO_ANNOUNCEMENT, NormalizeCtx, STICK_TO_BOTTOM_THRESHOLD_PX, ScrollGeometry, appendUserMessage, applyAgentEvent, bucketByDay, classifyTransportFailure, createIdPrefixCounter, dayKeyOf, deriveAnnouncement, extractCitationMarkers, fromClaudeStreamEvent, fromFoundryChatResult, fromOpenAiChatChunk, fromSseLine, groupMessages, initialConversation, isSameAuthorRun, isTerminalStatus, renumberCitations, runStatusSeverity, shouldStickToBottom, splitTextByCitations } from './core.cjs';
|
|
6
|
+
import { BadgeProps } from '@fluentui/react-components';
|
|
7
|
+
export { A as AgentKitHostProvider, a as AgentKitHostProviderProps, R as ResolveHostThemeInput, b as agentDarkTheme, c as agentTheme, d as createAgentDarkTheme, e as createAgentTheme, n as nextAgentIdPrefix, r as resolveHostTheme } from './index-DGN6eYnp.cjs';
|
|
8
|
+
export { MockAgentService, MockAgentServiceOptions, MockScenario, MockScenarioName, NullAgentService, Scheduler, createAgentService, decide, immediateScheduler, mockScenarios, realScheduler } from './services.cjs';
|
|
9
|
+
|
|
10
|
+
interface MessageActionsProps {
|
|
11
|
+
message: AgentMessage;
|
|
12
|
+
onCopy?: (message: AgentMessage) => void;
|
|
13
|
+
onRetry?: (message: AgentMessage) => void;
|
|
14
|
+
onFeedback?: (message: AgentMessage, vote: 'up' | 'down') => void;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* The hover row on a message.
|
|
18
|
+
*
|
|
19
|
+
* ★★ It is revealed by `:hover` **or `:focus-within`** — see MessageBubble's styles. A
|
|
20
|
+
* hover-only affordance is a WCAG 2.1.1 (Keyboard, Level A) failure outright: there is no
|
|
21
|
+
* pointer-free route to it at all. The other naive choice, leaving the row permanently in
|
|
22
|
+
* the tab order, adds N tab stops per message. Reveal-on-focus plus a roving tabindex gives
|
|
23
|
+
* exactly one tab stop per message and a keyboard route to every action.
|
|
24
|
+
*/
|
|
25
|
+
declare function MessageActions({ message, onCopy, onRetry, onFeedback, }: MessageActionsProps): React$1.ReactElement | null;
|
|
26
|
+
|
|
27
|
+
interface MessageListProps extends Omit<MessageActionsProps, 'message'> {
|
|
28
|
+
messages: readonly AgentMessage[];
|
|
29
|
+
participants?: readonly AgentParticipant[];
|
|
30
|
+
/** Renders the typing indicator and suppresses the "jump to latest" nudge. */
|
|
31
|
+
busy?: boolean;
|
|
32
|
+
announcement?: Announcement;
|
|
33
|
+
groupWindowMs?: number;
|
|
34
|
+
timeZone?: string;
|
|
35
|
+
locale?: string;
|
|
36
|
+
/** Injected so day labels are deterministic in tests and Chromatic. */
|
|
37
|
+
now?: number;
|
|
38
|
+
emptyState?: React$1.ReactNode;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* The transcript.
|
|
42
|
+
*
|
|
43
|
+
* ★ It is NOT a live region. Announcements go through `LiveAnnouncer`, fed by
|
|
44
|
+
* `deriveAnnouncement` on state TRANSITIONS — a live region over the streaming node queues
|
|
45
|
+
* every token and a screen reader never finishes reading.
|
|
46
|
+
*
|
|
47
|
+
* ★ Grouping (avatar + name once per run) is VISUAL. Every `MessageBubble` still carries its
|
|
48
|
+
* author and timestamp in the accessible tree.
|
|
49
|
+
*/
|
|
50
|
+
declare function MessageList(props: MessageListProps): React$1.ReactElement;
|
|
51
|
+
|
|
52
|
+
interface MessageBubbleProps extends Omit<MessageActionsProps, 'message'> {
|
|
53
|
+
message: AgentMessage;
|
|
54
|
+
author?: AgentParticipant;
|
|
55
|
+
/** First message of a run — renders the avatar and name. */
|
|
56
|
+
isRunStart?: boolean;
|
|
57
|
+
locale?: string;
|
|
58
|
+
}
|
|
59
|
+
declare function MessageBubble(props: MessageBubbleProps): React$1.ReactElement;
|
|
60
|
+
|
|
61
|
+
interface StreamingTextProps {
|
|
62
|
+
text: string;
|
|
63
|
+
streaming?: boolean;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* The in-flight answer.
|
|
67
|
+
*
|
|
68
|
+
* ★ A STABLE element, deliberately. If this subtree remounted per token, any focus inside the
|
|
69
|
+
* streaming bubble (a citation chip, a copy button) would be thrown to `<body>` mid-answer.
|
|
70
|
+
* Only the text node changes; the element identity does not. `useStreamingText.test.tsx`
|
|
71
|
+
* pins that as an invariant rather than leaving it to a comment.
|
|
72
|
+
*/
|
|
73
|
+
declare function StreamingText({ text, streaming }: StreamingTextProps): React$1.ReactElement;
|
|
74
|
+
|
|
75
|
+
interface TypingIndicatorProps {
|
|
76
|
+
/** Announced. Keep it short — it interrupts nothing but is read on appearance. */
|
|
77
|
+
label?: string;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* ★ This carries "the assistant is responding" for screen readers, via its own `role="status"`
|
|
81
|
+
* firing when it MOUNTS. That is why `deriveAnnouncement` does not also announce the start —
|
|
82
|
+
* doing both would say it twice.
|
|
83
|
+
*/
|
|
84
|
+
declare function TypingIndicator({ label }: TypingIndicatorProps): React$1.ReactElement;
|
|
85
|
+
|
|
86
|
+
interface ComposerProps {
|
|
87
|
+
value: string;
|
|
88
|
+
onChange: (value: string) => void;
|
|
89
|
+
onSend: (value: string) => void;
|
|
90
|
+
onStop?: () => void;
|
|
91
|
+
busy?: boolean;
|
|
92
|
+
disabled?: boolean;
|
|
93
|
+
placeholder?: string;
|
|
94
|
+
maxLength?: number;
|
|
95
|
+
/** 'enter' sends on Enter; 'mod+enter' needs Ctrl/Cmd. */
|
|
96
|
+
sendOn?: 'enter' | 'mod+enter';
|
|
97
|
+
/** Rendered left of the send button — attachments, formatting, etc. */
|
|
98
|
+
toolbar?: React$1.ReactNode;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* The send box.
|
|
102
|
+
*
|
|
103
|
+
* ★ `sendOn` is configurable, and defaults to Enter only because that is what users expect
|
|
104
|
+
* from Teams. Enter-to-send in a multiline field is hostile to dictation and voice control,
|
|
105
|
+
* and Shift+Enter is undiscoverable — hence the `aria-describedby` hint, which makes the
|
|
106
|
+
* contract audible instead of folklore.
|
|
107
|
+
*
|
|
108
|
+
* ★ Deliberately NOT Fluent's `Toolbar` for the affordance row: it reaches
|
|
109
|
+
* `useArrowNavigationGroup`. Consumers pass plain buttons, or the in-tree ActionToolbar.
|
|
110
|
+
*/
|
|
111
|
+
declare function Composer(props: ComposerProps): React$1.ReactElement;
|
|
112
|
+
|
|
113
|
+
interface SuggestedPromptsProps {
|
|
114
|
+
prompts: readonly string[];
|
|
115
|
+
onPick: (prompt: string) => void;
|
|
116
|
+
heading?: string;
|
|
117
|
+
}
|
|
118
|
+
/** ★ Plain Buttons in a labelled list, not Fluent's `TagGroup` — `react-tags` reaches
|
|
119
|
+
* `useArrowNavigationGroup`, and chips gain nothing from it here. */
|
|
120
|
+
declare function SuggestedPrompts({ prompts, onPick, heading, }: SuggestedPromptsProps): React$1.ReactElement | null;
|
|
121
|
+
|
|
122
|
+
interface EmptyConversationProps {
|
|
123
|
+
title?: string;
|
|
124
|
+
description?: string;
|
|
125
|
+
prompts?: readonly string[];
|
|
126
|
+
onPick?: (prompt: string) => void;
|
|
127
|
+
}
|
|
128
|
+
declare function EmptyConversation({ title, description, prompts, onPick, }: EmptyConversationProps): React$1.ReactElement;
|
|
129
|
+
|
|
130
|
+
interface SystemNoticeProps {
|
|
131
|
+
failure?: AgentFailure;
|
|
132
|
+
guarded?: boolean;
|
|
133
|
+
onRetry?: () => void;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* A terminal state the user needs to see.
|
|
137
|
+
*
|
|
138
|
+
* ★ `blocked` / `refused` / `notProvisioned` render as WARNING or INFO, not error — they are
|
|
139
|
+
* different outcomes needing different fixes, and flattening them into "something went
|
|
140
|
+
* wrong" is what makes an unprovisioned environment look broken.
|
|
141
|
+
* ★ The state is in the TEXT, never only in the colour (WCAG 1.4.1).
|
|
142
|
+
*/
|
|
143
|
+
declare function SystemNotice({ failure, guarded, onRetry }: SystemNoticeProps): React$1.ReactElement | null;
|
|
144
|
+
|
|
145
|
+
interface DayDividerProps {
|
|
146
|
+
label: DayDividerLabel;
|
|
147
|
+
locale?: string;
|
|
148
|
+
}
|
|
149
|
+
/** Map the pure descriptor to words. ★ The formatting lives HERE, not in ./core, so the host
|
|
150
|
+
* can localise it and so the core function stays deterministic across CI locales. */
|
|
151
|
+
declare function formatDayLabel(label: DayDividerLabel, locale?: string): string;
|
|
152
|
+
/** ★ `role="separator"` with an accessible name. A bare text node floating between list items
|
|
153
|
+
* reads as a message. */
|
|
154
|
+
declare function DayDivider({ label, locale }: DayDividerProps): React$1.ReactElement;
|
|
155
|
+
|
|
156
|
+
interface AgentStatusBadgeProps {
|
|
157
|
+
status: AgentRunStatus;
|
|
158
|
+
/** Shown in a tooltip — the provider's own message, when there is one. */
|
|
159
|
+
reason?: string;
|
|
160
|
+
size?: BadgeProps['size'];
|
|
161
|
+
}
|
|
162
|
+
/** Exported so the label contract can be asserted directly rather than scraped from markup. */
|
|
163
|
+
declare function runStatusLabel(status: AgentRunStatus): string;
|
|
164
|
+
declare function AgentStatusBadge({ status, reason, size }: AgentStatusBadgeProps): React$1.ReactElement;
|
|
165
|
+
|
|
166
|
+
interface AvailabilityBannerProps {
|
|
167
|
+
availability?: AgentAvailability;
|
|
168
|
+
onRetry?: () => void;
|
|
169
|
+
/** Rendered beside Retry — a link to the setup doc, a "Configure" button. */
|
|
170
|
+
actions?: React$1.ReactNode;
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Whether the agent can answer here, and if not, why.
|
|
174
|
+
*
|
|
175
|
+
* ★★ `notProvisioned` renders as INFORMATION, not an error. Nobody having configured the service
|
|
176
|
+
* yet is a normal state in an on-demand Azure estate; dressing it as a failure is what makes a
|
|
177
|
+
* working app look broken, and it needs a completely different response from an outage.
|
|
178
|
+
*
|
|
179
|
+
* ★ `reason` is displayed rather than summarised. "Not licensed" and "not on this host" need
|
|
180
|
+
* different fixes, and the seam keeps them distinct precisely so the UI can say which.
|
|
181
|
+
*/
|
|
182
|
+
declare function AvailabilityBanner({ availability, onRetry, actions, }: AvailabilityBannerProps): React$1.ReactElement | null;
|
|
183
|
+
|
|
184
|
+
interface UsageMeterProps {
|
|
185
|
+
usage?: AgentUsage;
|
|
186
|
+
show?: readonly ('tokens' | 'cost' | 'duration' | 'model')[];
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Tokens, cost, latency.
|
|
190
|
+
*
|
|
191
|
+
* ★★ "Not reported" is rendered as such, never as 0. A provider that does not report cost and
|
|
192
|
+
* one that cost nothing are completely different facts, and showing `$0.00` for the first is
|
|
193
|
+
* a confident lie — the kind that gets believed because it looks like data.
|
|
194
|
+
*/
|
|
195
|
+
declare function UsageMeter({ usage, show, }: UsageMeterProps): React$1.ReactElement | null;
|
|
196
|
+
|
|
197
|
+
interface ThrottleNoticeProps {
|
|
198
|
+
failure?: AgentFailure;
|
|
199
|
+
onRetry?: () => void;
|
|
200
|
+
/** Injected so the countdown is testable without fake timers. */
|
|
201
|
+
now?: () => number;
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* A rate-limit notice that counts down.
|
|
205
|
+
*
|
|
206
|
+
* ★ It renders only for a failure that is BOTH retriable and carries `retryAfterMs`. A countdown
|
|
207
|
+
* over an unknown wait would be invented, and a retry button that fires into an active throttle
|
|
208
|
+
* just burns the next window.
|
|
209
|
+
*/
|
|
210
|
+
declare function ThrottleNotice({ failure, onRetry, now }: ThrottleNoticeProps): React$1.ReactElement | null;
|
|
211
|
+
|
|
212
|
+
interface ModelBadgeProps {
|
|
213
|
+
provider?: string;
|
|
214
|
+
model?: string;
|
|
215
|
+
version?: string;
|
|
216
|
+
}
|
|
217
|
+
/** Which agent, which model, which version — so a run can be traced to a configuration. */
|
|
218
|
+
declare function ModelBadge({ provider, model, version }: ModelBadgeProps): React$1.ReactElement | null;
|
|
219
|
+
|
|
220
|
+
/** An agent a host can offer. Names WHICH agent answers — never a credential. */
|
|
221
|
+
interface AgentDescriptor {
|
|
222
|
+
id: string;
|
|
223
|
+
name: string;
|
|
224
|
+
model?: string;
|
|
225
|
+
version?: string;
|
|
226
|
+
description?: string;
|
|
227
|
+
/** Last known health. `undefined` means "not probed", which is not the same as healthy. */
|
|
228
|
+
status?: AgentRunStatus;
|
|
229
|
+
}
|
|
230
|
+
/** One completed turn, for the run history. */
|
|
231
|
+
interface AgentRunRecord {
|
|
232
|
+
id: string;
|
|
233
|
+
status: AgentRunStatus;
|
|
234
|
+
startedAt: number;
|
|
235
|
+
prompt?: string;
|
|
236
|
+
usage?: AgentUsage;
|
|
237
|
+
correlationId?: string;
|
|
238
|
+
model?: string;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
interface AgentPickerProps {
|
|
242
|
+
agents: readonly AgentDescriptor[];
|
|
243
|
+
selectedId?: string;
|
|
244
|
+
onSelect: (id: string) => void;
|
|
245
|
+
label?: string;
|
|
246
|
+
disabled?: boolean;
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Choose which agent answers.
|
|
250
|
+
*
|
|
251
|
+
* ★ Fluent's `Dropdown` — measured tabster-safe. `react-combobox` imports only
|
|
252
|
+
* `useTabsterAttributes`, `useMergedTabsterAttributes_unstable`, `useSetKeyboardNavigation` and
|
|
253
|
+
* `useOnKeyboardNavigationChange`; `useTabsterAttributes` calls `useTabster()` with NO argument,
|
|
254
|
+
* and `useTabster`'s default factory is the identity, so it never reaches a `get*` factory or
|
|
255
|
+
* `attrHandlers.set`. Two reviewers disagreed about this and the source settled it.
|
|
256
|
+
*
|
|
257
|
+
* ★ Selecting an agent is a CONFIG change, not a capability change: the seam still has no write
|
|
258
|
+
* method whichever agent is chosen.
|
|
259
|
+
*/
|
|
260
|
+
declare function AgentPicker({ agents, selectedId, onSelect, label, disabled, }: AgentPickerProps): React$1.ReactElement;
|
|
261
|
+
|
|
262
|
+
interface RunTimelineProps {
|
|
263
|
+
message: AgentMessage;
|
|
264
|
+
/** Collapsed by default: a trace is diagnostic, not the primary reading. */
|
|
265
|
+
defaultOpen?: boolean;
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* What one turn actually did: its outcome, the tools it called, what it cited, what it cost.
|
|
269
|
+
*
|
|
270
|
+
* ★ An ORDERED LIST, not Fluent's `Tree`. `react-tree` reaches `useArrowNavigationGroup`, and a
|
|
271
|
+
* trace is a sequence — the ordering is the information. If steps ever nest, revisit rather
|
|
272
|
+
* than flattening a tree into a list.
|
|
273
|
+
*
|
|
274
|
+
* ★ Each step's status is in TEXT beside it. A row of coloured dots conveys nothing to a
|
|
275
|
+
* screen reader and little to a colour-blind reader.
|
|
276
|
+
*/
|
|
277
|
+
declare function RunTimeline({ message, defaultOpen }: RunTimelineProps): React$1.ReactElement | null;
|
|
278
|
+
|
|
279
|
+
interface RunHistoryPanelProps {
|
|
280
|
+
runs: readonly AgentRunRecord[];
|
|
281
|
+
/**
|
|
282
|
+
* A grid to render instead of the built-in list.
|
|
283
|
+
*
|
|
284
|
+
* ★★ THIS SLOT IS WHY THERE IS NO `RunHistoryList`. A run history is tabular, and this repo
|
|
285
|
+
* already ships a grid — `@dataverse-kit/grid-kit/v9` — with sorting, virtualisation and
|
|
286
|
+
* column chooser. Re-implementing a thin version here would be a second grid to maintain
|
|
287
|
+
* and a worse one.
|
|
288
|
+
*
|
|
289
|
+
* It is a SLOT rather than a dependency because grid-kit/v9's `DataGrid` builds on
|
|
290
|
+
* `react-table` and its toolbar on `Menu`, both of which reach tabster. A host that accepts
|
|
291
|
+
* that passes a grid in; a host that does not gets the fallback list below. That is
|
|
292
|
+
* surface-kit's documented "pass grids in as children" pattern, and it moves the tabster
|
|
293
|
+
* decision to the host, where it belongs.
|
|
294
|
+
*/
|
|
295
|
+
grid?: React$1.ReactNode;
|
|
296
|
+
locale?: string;
|
|
297
|
+
emptyLabel?: string;
|
|
298
|
+
}
|
|
299
|
+
declare function RunHistoryPanel({ runs, grid, locale, emptyLabel, }: RunHistoryPanelProps): React$1.ReactElement;
|
|
300
|
+
|
|
301
|
+
interface AgentOpsPanelProps {
|
|
302
|
+
availability?: AgentAvailability;
|
|
303
|
+
agents?: readonly AgentDescriptor[];
|
|
304
|
+
selectedAgentId?: string;
|
|
305
|
+
onSelectAgent?: (id: string) => void;
|
|
306
|
+
runs?: readonly AgentRunRecord[];
|
|
307
|
+
/** Passed straight through to RunHistoryPanel — see that component for why it is a slot. */
|
|
308
|
+
runsGrid?: React$1.ReactNode;
|
|
309
|
+
onRefresh?: () => void;
|
|
310
|
+
locale?: string;
|
|
311
|
+
}
|
|
312
|
+
/** The composed ops surface: is it available, which agent, what has it done. */
|
|
313
|
+
declare function AgentOpsPanel(props: AgentOpsPanelProps): React$1.ReactElement;
|
|
314
|
+
|
|
315
|
+
interface AgentAppShellProps {
|
|
316
|
+
/** The left icon rail. Hidden below `sm`. */
|
|
317
|
+
rail?: React$1.ReactNode;
|
|
318
|
+
/** The conversation list. Collapses to an overlay below `md`. */
|
|
319
|
+
sidebar?: React$1.ReactNode;
|
|
320
|
+
/** The conversation header, above the main region. */
|
|
321
|
+
header?: React$1.ReactNode;
|
|
322
|
+
/** The transcript + composer. */
|
|
323
|
+
children?: React$1.ReactNode;
|
|
324
|
+
/** The detail pane. Hidden below `lg` — the consumer decides what to do with it there. */
|
|
325
|
+
rightPane?: React$1.ReactNode;
|
|
326
|
+
sidebarWidth?: number;
|
|
327
|
+
/**
|
|
328
|
+
* Drive the layout from a KNOWN width instead of measuring.
|
|
329
|
+
*
|
|
330
|
+
* ★ Mirrors `FormSurface.containerWidth` in surface-kit/v9, and exists for the same reason: a
|
|
331
|
+
* PCF host already knows its `context.allocatedWidth`, so making it wait for a
|
|
332
|
+
* ResizeObserver round-trip is a needless reflow. It is also what makes the collapse
|
|
333
|
+
* TESTABLE — jsdom's ResizeObserver is a stub that never fires, so a measured width stays 0
|
|
334
|
+
* forever and only the roomy branch would ever run.
|
|
335
|
+
*/
|
|
336
|
+
containerWidth?: number;
|
|
337
|
+
/** Controlled sidebar visibility on narrow widths. */
|
|
338
|
+
sidebarOpen?: boolean;
|
|
339
|
+
onSidebarOpenChange?: (open: boolean) => void;
|
|
340
|
+
label?: string;
|
|
341
|
+
}
|
|
342
|
+
/**
|
|
343
|
+
* The Teams-like frame: rail │ list │ conversation │ detail pane.
|
|
344
|
+
*
|
|
345
|
+
* ★★ BOUNDARY WITH surface-kit, in one sentence: **surface-kit owns record/form geometry;
|
|
346
|
+
* agent-kit owns conversation geometry.** `FormSurface`/`Section`/`PivotSurface` are for a
|
|
347
|
+
* Dataverse record; this is for a thread. `MasterDetailSurface` is not this — it is v8-only
|
|
348
|
+
* and two-pane. When surface-kit's deferred v9 composites land, this row is the one to check
|
|
349
|
+
* for overlap.
|
|
350
|
+
*
|
|
351
|
+
* ★★ IT IS NOT A SECOND BREAKPOINT AUTHORITY. The cutoffs come from
|
|
352
|
+
* `@dataverse-kit/surface-kit/v9` (`BREAKPOINTS`, `resolveBreakpoint`), which surface-kit's
|
|
353
|
+
* CLAUDE.md states as an invariant: "every responsive stacking decision flows through
|
|
354
|
+
* responsive/builders.ts". Redeclaring 480/768 here is how two files start disagreeing.
|
|
355
|
+
*
|
|
356
|
+
* ★ It measures its CONTAINER, not the viewport (`useContainerWidth`). A PCF control or a
|
|
357
|
+
* custom page occupies a pane, not a window — `context.allocatedWidth`, not `window.innerWidth`
|
|
358
|
+
* — so a viewport media query would collapse the wrong way in the host this kit targets.
|
|
359
|
+
*/
|
|
360
|
+
declare function AgentAppShell(props: AgentAppShellProps): React$1.ReactElement;
|
|
361
|
+
|
|
362
|
+
type PresenceState = 'available' | 'busy' | 'away' | 'offline' | 'dnd' | 'unknown';
|
|
363
|
+
/**
|
|
364
|
+
* One row in the conversation list.
|
|
365
|
+
*
|
|
366
|
+
* ★★ THE LIST IS FLAT, AND THAT IS A DECISION.
|
|
367
|
+
*
|
|
368
|
+
* A Teams *chat* list — which is what this models, and what the reference screenshot shows —
|
|
369
|
+
* is flat: a sequence of conversations. A Teams *teams/channels* tree is not, and it would
|
|
370
|
+
* need `role="tree"` with `treeitem`/`group`/`aria-level`/`aria-expanded` and Right/Left
|
|
371
|
+
* expand-collapse. That is a genuinely different widget, and hand-rolling APG Tree is large;
|
|
372
|
+
* if nesting is ever needed, use Fluent's `Tree` behind the host's `targetDocument` isolation
|
|
373
|
+
* rather than growing this one.
|
|
374
|
+
*/
|
|
375
|
+
interface ConversationSummary {
|
|
376
|
+
id: string;
|
|
377
|
+
title: string;
|
|
378
|
+
/** Last message, one line. */
|
|
379
|
+
preview?: string;
|
|
380
|
+
timestamp?: number;
|
|
381
|
+
unreadCount?: number;
|
|
382
|
+
participant?: AgentParticipant;
|
|
383
|
+
presence?: PresenceState;
|
|
384
|
+
/** Renders a muted style and omits unread from the accessible name. */
|
|
385
|
+
muted?: boolean;
|
|
386
|
+
}
|
|
387
|
+
interface RailItem {
|
|
388
|
+
id: string;
|
|
389
|
+
label: string;
|
|
390
|
+
icon: React.ReactElement;
|
|
391
|
+
badgeCount?: number;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
interface AppRailProps {
|
|
395
|
+
items: readonly RailItem[];
|
|
396
|
+
selectedId?: string;
|
|
397
|
+
onSelect: (id: string) => void;
|
|
398
|
+
/** Items beyond this many move into the overflow menu. */
|
|
399
|
+
maxVisible?: number;
|
|
400
|
+
label?: string;
|
|
401
|
+
footer?: React$1.ReactNode;
|
|
402
|
+
}
|
|
403
|
+
/**
|
|
404
|
+
* The left icon rail.
|
|
405
|
+
*
|
|
406
|
+
* ★★ The overflow uses Fluent's real `Menu`, which reaches `useArrowNavigationGroup` → tabster.
|
|
407
|
+
* That is DELIBERATE, per the split-by-cost decision: a menu is a genuine APG widget (focus
|
|
408
|
+
* trap, typeahead, Escape, roving) and hand-rolling it is 100+ LOC, whereas a PCF host closes
|
|
409
|
+
* the tabster gap in one line with `targetDocument`. The coexistence spike (see CLAUDE.md)
|
|
410
|
+
* measured two cores on one window and found no double-handling, so this is safe under
|
|
411
|
+
* isolation. `Menu` is the ONE tabster-touching component in the shell.
|
|
412
|
+
*
|
|
413
|
+
* ★ Each rail button is its own tab stop deliberately: the rail is app-level navigation, not a
|
|
414
|
+
* composite widget, and roving it would hide destinations from a Tab-only user.
|
|
415
|
+
*/
|
|
416
|
+
declare function AppRail(props: AppRailProps): React$1.ReactElement;
|
|
417
|
+
|
|
418
|
+
interface ConversationListProps {
|
|
419
|
+
items: readonly ConversationSummary[];
|
|
420
|
+
selectedId?: string;
|
|
421
|
+
onSelect: (id: string) => void;
|
|
422
|
+
label?: string;
|
|
423
|
+
locale?: string;
|
|
424
|
+
empty?: React$1.ReactNode;
|
|
425
|
+
}
|
|
426
|
+
/**
|
|
427
|
+
* The chat list.
|
|
428
|
+
*
|
|
429
|
+
* ★★ A LISTBOX with `aria-activedescendant`, not a stack of buttons and not a `Tree`.
|
|
430
|
+
*
|
|
431
|
+
* A bare button list is not a simpler tree — it is a widget with no collection semantics at
|
|
432
|
+
* all: N tab stops, no selected state, no set size or position, no arrow navigation. A
|
|
433
|
+
* listbox is ONE tab stop, Up/Down/Home/End move an active option, and `aria-selected` says
|
|
434
|
+
* which conversation is open.
|
|
435
|
+
*
|
|
436
|
+
* `Tree` would be right if this nested (teams → channels); it does not — see ./types.ts,
|
|
437
|
+
* where that decision is written down.
|
|
438
|
+
*
|
|
439
|
+
* ★ Modelled on the estate's existing activedescendant listbox
|
|
440
|
+
* (`shared/pcf-gallery/controls/choice-grid/.../ChoiceGridApp.tsx`), including the detail that
|
|
441
|
+
* `scrollIntoView` is guarded on the METHOD (`?.()`), not just the element: it is not
|
|
442
|
+
* implemented everywhere a React tree runs, and an unguarded call inside an effect does not
|
|
443
|
+
* degrade — it throws, React unmounts, and the list renders nothing.
|
|
444
|
+
*/
|
|
445
|
+
declare function ConversationList(props: ConversationListProps): React$1.ReactElement;
|
|
446
|
+
|
|
447
|
+
interface ConversationListItemProps {
|
|
448
|
+
summary: ConversationSummary;
|
|
449
|
+
selected: boolean;
|
|
450
|
+
active: boolean;
|
|
451
|
+
id: string;
|
|
452
|
+
locale?: string;
|
|
453
|
+
onSelect: (id: string) => void;
|
|
454
|
+
}
|
|
455
|
+
/**
|
|
456
|
+
* Build the row's accessible name.
|
|
457
|
+
*
|
|
458
|
+
* ★★ Unread count and presence go in the NAME, not only in a badge and a colour dot. A screen
|
|
459
|
+
* reader hearing "Contoso" forty times learns nothing about which conversations are waiting,
|
|
460
|
+
* and a colour-only unread indicator is a WCAG 1.4.1 failure. Exported so it can be asserted
|
|
461
|
+
* directly rather than inferred from rendered markup.
|
|
462
|
+
*/
|
|
463
|
+
declare function conversationAccessibleName(summary: ConversationSummary, locale?: string): string;
|
|
464
|
+
declare function ConversationListItem(props: ConversationListItemProps): React$1.ReactElement;
|
|
465
|
+
|
|
466
|
+
interface ConversationTab {
|
|
467
|
+
id: string;
|
|
468
|
+
label: string;
|
|
469
|
+
}
|
|
470
|
+
interface HeaderAction {
|
|
471
|
+
key: string;
|
|
472
|
+
label: string;
|
|
473
|
+
icon?: React$1.ReactElement;
|
|
474
|
+
onClick: () => void;
|
|
475
|
+
}
|
|
476
|
+
interface ConversationHeaderProps {
|
|
477
|
+
title: string;
|
|
478
|
+
subtitle?: string;
|
|
479
|
+
participant?: AgentParticipant;
|
|
480
|
+
presence?: PresenceState;
|
|
481
|
+
tabs?: readonly ConversationTab[];
|
|
482
|
+
selectedTabId?: string;
|
|
483
|
+
onTabSelect?: (id: string) => void;
|
|
484
|
+
actions?: readonly HeaderAction[];
|
|
485
|
+
/** Rendered right of the tabs — a search box, a right-pane toggle. */
|
|
486
|
+
trailing?: React$1.ReactNode;
|
|
487
|
+
}
|
|
488
|
+
/**
|
|
489
|
+
* The bar above the transcript: who you are talking to, the tab strip, and the actions.
|
|
490
|
+
*
|
|
491
|
+
* ★★ The tab strip is Fluent's real `TabList`, and that is DELIBERATE. `react-tabs` reaches
|
|
492
|
+
* `useArrowNavigationGroup` → `getMover` → tabster, but rebuilding it by hand is not the
|
|
493
|
+
* "simple horizontal list" it looks like: tabster's Mover carries RTL inversion,
|
|
494
|
+
* Home/End/PageUp/PageDown, cyclic wrap, disabled-item skipping, and a caret-position guard
|
|
495
|
+
* that declines arrow keys while the caret is inside a text input — roughly 120-180 LOC to
|
|
496
|
+
* reproduce, against a widget nobody has tested. Per the split-by-cost decision the kit uses
|
|
497
|
+
* Fluent's and the PCF host supplies an isolated `targetDocument`; the coexistence spike
|
|
498
|
+
* measured that two tabster cores share a window without double-handling.
|
|
499
|
+
*
|
|
500
|
+
* ★ Manual activation (Fluent's default, `selectTabOnFocus` off): arrowing through the tabs must
|
|
501
|
+
* not fire N conversation loads on the way past.
|
|
502
|
+
*
|
|
503
|
+
* ★ `aria-controls` is NOT set here. The panel these tabs govern is rendered by the consumer,
|
|
504
|
+
* elsewhere in the tree, so the id contract would have to cross components — and an
|
|
505
|
+
* `aria-controls` pointing at nothing is worse than none. Pass `panelId` when that contract
|
|
506
|
+
* exists; until then the relationship is left unstated rather than stated falsely.
|
|
507
|
+
*/
|
|
508
|
+
declare function ConversationHeader(props: ConversationHeaderProps): React$1.ReactElement;
|
|
509
|
+
|
|
510
|
+
interface RightPaneProps {
|
|
511
|
+
open: boolean;
|
|
512
|
+
title: string;
|
|
513
|
+
onClose: () => void;
|
|
514
|
+
children?: React$1.ReactNode;
|
|
515
|
+
width?: number;
|
|
516
|
+
}
|
|
517
|
+
/**
|
|
518
|
+
* The detail pane beside the conversation — sources, a record, a trace.
|
|
519
|
+
*
|
|
520
|
+
* ★ A plain `<aside>`, NOT Fluent's `Drawer`. `react-drawer` composes dialog internals, and an
|
|
521
|
+
* inline pane needs none of a drawer's modality: it sits in the layout rather than over it.
|
|
522
|
+
*/
|
|
523
|
+
declare function RightPane({ open, title, onClose, children, width }: RightPaneProps): React$1.ReactElement | null;
|
|
524
|
+
|
|
525
|
+
interface GlobalSearchBarProps {
|
|
526
|
+
value: string;
|
|
527
|
+
onChange: (value: string) => void;
|
|
528
|
+
onSubmit?: (value: string) => void;
|
|
529
|
+
placeholder?: string;
|
|
530
|
+
}
|
|
531
|
+
/** ★ `SearchBox` is `react-search`, which has no tabster dependency — safe. */
|
|
532
|
+
declare function GlobalSearchBar({ value, onChange, onSubmit, placeholder, }: GlobalSearchBarProps): React$1.ReactElement;
|
|
533
|
+
|
|
534
|
+
interface AgentAvatarProps {
|
|
535
|
+
participant?: AgentParticipant;
|
|
536
|
+
presence?: PresenceState;
|
|
537
|
+
size?: 24 | 28 | 32 | 36 | 40 | 48;
|
|
538
|
+
name?: string;
|
|
539
|
+
}
|
|
540
|
+
/**
|
|
541
|
+
* ★ `Avatar` and `PresenceBadge` are measured tabster-safe (`react-avatar` imports only
|
|
542
|
+
* `createCustomFocusIndicatorStyle`; `react-badge` has no tabster dependency at all), so
|
|
543
|
+
* these are Fluent's rather than in-tree.
|
|
544
|
+
*
|
|
545
|
+
* ★ `AvatarGroup` is NOT — it pulls `react-popover`, which reaches `useModalAttributes`. If a
|
|
546
|
+
* facepile is ever wanted, that is the component to check first.
|
|
547
|
+
*/
|
|
548
|
+
declare function AgentAvatar({ participant, presence, size, name }: AgentAvatarProps): React$1.ReactElement;
|
|
549
|
+
|
|
550
|
+
interface CitationChipProps {
|
|
551
|
+
marker: string;
|
|
552
|
+
source?: AgentSource;
|
|
553
|
+
onOpen?: (source: AgentSource) => void;
|
|
554
|
+
}
|
|
555
|
+
/** An inline `[1]`. ★ `Tooltip` is measured tabster-safe (`react-tooltip` imports only
|
|
556
|
+
* `useIsNavigatingWithKeyboard`), so this one is Fluent's rather than in-tree. */
|
|
557
|
+
declare function CitationChip({ marker, source, onOpen }: CitationChipProps): React$1.ReactElement;
|
|
558
|
+
|
|
559
|
+
interface CitationListProps {
|
|
560
|
+
/** The answer text — used to work out which sources were actually cited. */
|
|
561
|
+
text: string;
|
|
562
|
+
sources: readonly AgentSource[];
|
|
563
|
+
onOpenSource?: (source: AgentSource) => void;
|
|
564
|
+
defaultOpen?: boolean;
|
|
565
|
+
}
|
|
566
|
+
/**
|
|
567
|
+
* The sources under an answer.
|
|
568
|
+
*
|
|
569
|
+
* ★★ IT SHOWS ONLY THE SOURCES THE TEXT ACTUALLY CITES. A retriever commonly returns a dozen
|
|
570
|
+
* chunks for a two-citation answer, and listing all twelve implies the answer rests on
|
|
571
|
+
* twelve things when it rests on two. That overstated provenance is what makes a citation
|
|
572
|
+
* panel decorative instead of trustworthy, so `renumberCitations` drops the uncited ones
|
|
573
|
+
* and renumbers what remains into first-appearance order.
|
|
574
|
+
*
|
|
575
|
+
* ★ A Disclosure, not Fluent's `Accordion`: APG's disclosure pattern is `<button aria-expanded
|
|
576
|
+
* aria-controls>` and a labelled region, and `Accordion` reaches `useArrowNavigationGroup`
|
|
577
|
+
* to add arrow-key movement between headers that a single source list does not need.
|
|
578
|
+
*/
|
|
579
|
+
declare function CitationList({ text, sources, onOpenSource, defaultOpen, }: CitationListProps): React$1.ReactElement | null;
|
|
580
|
+
|
|
581
|
+
interface SourceCardProps {
|
|
582
|
+
source: AgentSource;
|
|
583
|
+
/** The marker this source was cited as, if any. */
|
|
584
|
+
marker?: string;
|
|
585
|
+
onOpen?: (source: AgentSource) => void;
|
|
586
|
+
showSnippet?: boolean;
|
|
587
|
+
}
|
|
588
|
+
declare function SourceCard({ source, marker, onOpen, showSnippet }: SourceCardProps): React$1.ReactElement;
|
|
589
|
+
|
|
590
|
+
interface AnswerWithCitationsProps {
|
|
591
|
+
text: string;
|
|
592
|
+
citations?: readonly AgentCitation[];
|
|
593
|
+
sources?: readonly AgentSource[];
|
|
594
|
+
onOpenSource?: (source: AgentSource) => void;
|
|
595
|
+
}
|
|
596
|
+
/** Renders the answer with a chip where each marker was. */
|
|
597
|
+
declare function AnswerWithCitations({ text, citations, sources, onOpenSource, }: AnswerWithCitationsProps): React$1.ReactElement;
|
|
598
|
+
|
|
599
|
+
interface GroundingPanelProps {
|
|
600
|
+
/** The SAME object passed as `AgentTurn.grounding`. */
|
|
601
|
+
grounding?: Record<string, unknown>;
|
|
602
|
+
title?: string;
|
|
603
|
+
defaultOpen?: boolean;
|
|
604
|
+
}
|
|
605
|
+
/**
|
|
606
|
+
* What the model was told.
|
|
607
|
+
*
|
|
608
|
+
* ★★ It renders the SAME object the turn carried (`AgentTurn.grounding`), which is the point:
|
|
609
|
+
* the facts the host computed deterministically and the facts shown on screen cannot drift
|
|
610
|
+
* apart, because there is only one of them. The model EXPLAINS these; it must never be the
|
|
611
|
+
* thing that derives them — a hallucinated identifier is worse than no answer.
|
|
612
|
+
*
|
|
613
|
+
* ★ A definition list on a CSS grid, not a `Table`: `react-table` reaches
|
|
614
|
+
* `useArrowNavigationGroup`, and key/value pairs are not tabular data.
|
|
615
|
+
*/
|
|
616
|
+
declare function GroundingPanel({ grounding, title, defaultOpen, }: GroundingPanelProps): React$1.ReactElement | null;
|
|
617
|
+
|
|
618
|
+
interface ProviderAttributionProps {
|
|
619
|
+
provider?: string;
|
|
620
|
+
model?: string;
|
|
621
|
+
guarded?: boolean;
|
|
622
|
+
}
|
|
623
|
+
/**
|
|
624
|
+
* Who produced this answer.
|
|
625
|
+
*
|
|
626
|
+
* ★★ WHEN `guarded` IS SET THE VISIBLE TEXT MUST CHANGE, and that is the whole contract.
|
|
627
|
+
* `guarded` means the provider WITHHELD its generated answer and substituted safe text.
|
|
628
|
+
* Attributing that substitute to the model as if it were the model's words is the
|
|
629
|
+
* misattribution this component exists to prevent — so the difference is in words a reader
|
|
630
|
+
* sees, not a colour, an icon or a `data-` attribute. The gate asserts BOTH directions.
|
|
631
|
+
*/
|
|
632
|
+
declare function ProviderAttribution({ provider, model, guarded }: ProviderAttributionProps): React$1.ReactElement | null;
|
|
633
|
+
/** Convenience: read the attribution straight off a message. */
|
|
634
|
+
declare function messageAttribution(message: AgentMessage): ProviderAttributionProps;
|
|
635
|
+
|
|
636
|
+
interface ActionCardProps {
|
|
637
|
+
title: string;
|
|
638
|
+
description?: React$1.ReactNode;
|
|
639
|
+
icon?: React$1.ReactElement;
|
|
640
|
+
/** Renders an emphasised edge while a decision is outstanding. */
|
|
641
|
+
pending?: boolean;
|
|
642
|
+
children?: React$1.ReactNode;
|
|
643
|
+
footer?: React$1.ReactNode;
|
|
644
|
+
}
|
|
645
|
+
/** The container the action family shares. ★ Built on the in-tree `CardSurface`, never Fluent's
|
|
646
|
+
* `Card` — see internal/CardSurface.tsx for why that one unmounts the tree on a form. */
|
|
647
|
+
declare function ActionCard({ title, description, icon, pending, children, footer }: ActionCardProps): React$1.ReactElement;
|
|
648
|
+
|
|
649
|
+
interface ApprovalCardProps {
|
|
650
|
+
toolCall: AgentToolCall;
|
|
651
|
+
onApprove: (call: AgentToolCall, argumentsJson: string) => void;
|
|
652
|
+
onDeny: (call: AgentToolCall) => void;
|
|
653
|
+
/** Enables in-place editing of the proposed arguments before approval. */
|
|
654
|
+
allowEdit?: boolean;
|
|
655
|
+
busy?: boolean;
|
|
656
|
+
}
|
|
657
|
+
/**
|
|
658
|
+
* A tool call awaiting a human decision.
|
|
659
|
+
*
|
|
660
|
+
* ★★ THE KIT DOES NOT RUN THE TOOL. It renders the request and raises a callback; the HOST
|
|
661
|
+
* executes and feeds the outcome back as a `tool_result` event. That is what keeps "the kit
|
|
662
|
+
* has no tools" a structural statement about this package rather than a promise about a
|
|
663
|
+
* prompt — `IAgentService` has no method that could invoke one.
|
|
664
|
+
*
|
|
665
|
+
* ★★ IT CANNOT DOUBLE-SUBMIT, and that is asserted two ways, because counting the callback
|
|
666
|
+
* alone is a one-field signature: it shows what the component SENT, and a card that
|
|
667
|
+
* re-enables after an async settle would still submit twice later. The gate asserts one
|
|
668
|
+
* `onApprove` across two synchronous clicks AND that the control is disabled afterwards.
|
|
669
|
+
*/
|
|
670
|
+
declare function ApprovalCard({ toolCall, onApprove, onDeny, allowEdit, busy, }: ApprovalCardProps): React$1.ReactElement;
|
|
671
|
+
|
|
672
|
+
interface ToolCallCardProps {
|
|
673
|
+
toolCall: AgentToolCall;
|
|
674
|
+
defaultExpanded?: boolean;
|
|
675
|
+
}
|
|
676
|
+
/** A tool call that needed no approval — collapsed by default, expandable for the arguments.
|
|
677
|
+
* ★ A Disclosure rather than `Accordion`: see internal/Disclosure.tsx. */
|
|
678
|
+
declare function ToolCallCard({ toolCall, defaultExpanded }: ToolCallCardProps): React$1.ReactElement;
|
|
679
|
+
|
|
680
|
+
interface ParameterReviewProps {
|
|
681
|
+
/** RAW arguments as the model emitted them. */
|
|
682
|
+
argumentsJson: string;
|
|
683
|
+
readOnly?: boolean;
|
|
684
|
+
onChange?: (next: string) => void;
|
|
685
|
+
}
|
|
686
|
+
/**
|
|
687
|
+
* The arguments a tool call is proposing, laid out for a human to check before approving.
|
|
688
|
+
*
|
|
689
|
+
* ★ A definition list on a CSS grid, NOT a `Table`/`DataGrid` — both reach
|
|
690
|
+
* `useArrowNavigationGroup`, and this is key/value data rather than a grid.
|
|
691
|
+
*
|
|
692
|
+
* ★★ It falls back to showing the RAW string when the JSON does not parse, and that matters:
|
|
693
|
+
* a half-streamed arguments blob is not valid JSON, and an approval UI that silently
|
|
694
|
+
* rendered nothing would ask the user to approve something they cannot see. Showing the raw
|
|
695
|
+
* text is honest; showing an empty table is not.
|
|
696
|
+
*/
|
|
697
|
+
declare function ParameterReview({ argumentsJson, readOnly, onChange }: ParameterReviewProps): React$1.ReactElement;
|
|
698
|
+
|
|
699
|
+
interface ActionResultProps {
|
|
700
|
+
status: 'succeeded' | 'failed' | 'denied';
|
|
701
|
+
message: string;
|
|
702
|
+
correlationId?: string;
|
|
703
|
+
onRetry?: () => void;
|
|
704
|
+
}
|
|
705
|
+
/** ★ The correlation id is surfaced, not hidden: it is the only thing that lets someone paged
|
|
706
|
+
* at 3am find this specific run in App Insights. */
|
|
707
|
+
declare function ActionResult({ status, message, correlationId, onRetry }: ActionResultProps): React$1.ReactElement;
|
|
708
|
+
|
|
709
|
+
interface ConfirmInlineProps {
|
|
710
|
+
open: boolean;
|
|
711
|
+
title: string;
|
|
712
|
+
body: React$1.ReactNode;
|
|
713
|
+
confirmLabel?: string;
|
|
714
|
+
cancelLabel?: string;
|
|
715
|
+
destructive?: boolean;
|
|
716
|
+
onConfirm: () => void;
|
|
717
|
+
onCancel: () => void;
|
|
718
|
+
}
|
|
719
|
+
/**
|
|
720
|
+
* An in-tree confirmation, replacing Fluent's `Dialog`.
|
|
721
|
+
*
|
|
722
|
+
* ★ `react-dialog` reaches `useModalAttributes` → `getModalizer` + `getRestorer` →
|
|
723
|
+
* `core.attrHandlers.set`. The gallery's `image-gallery-field` removed `Dialog` for exactly
|
|
724
|
+
* this and cut its bundle 431,706 → 317,346 bytes, so the trade is well established.
|
|
725
|
+
*
|
|
726
|
+
* ★★ BUT THAT PRECEDENT SHIPS THREE FOCUS DEFECTS, AND THIS FIXES THEM. Read against it:
|
|
727
|
+
* it focuses into the dialog and handles Escape, but it (1) never returns focus to the
|
|
728
|
+
* trigger on close, (2) has no focus trap, so Tab walks straight out of a `role="alertdialog"`,
|
|
729
|
+
* and (3) omits `aria-modal` — so the role asserts a modality the implementation does not
|
|
730
|
+
* provide, which is an ARIA lie rather than a missing nicety. Removing tabster means
|
|
731
|
+
* removing its Modalizer and Restorer, and whatever they were doing has to be done here.
|
|
732
|
+
*/
|
|
733
|
+
declare function ConfirmInline(props: ConfirmInlineProps): React$1.ReactElement | null;
|
|
734
|
+
|
|
735
|
+
interface LiveAnnouncerProps {
|
|
736
|
+
/** Read at the next pause. Changing this to a new non-empty value announces it. */
|
|
737
|
+
polite?: string;
|
|
738
|
+
/** Interrupts. Failures only. */
|
|
739
|
+
assertive?: string;
|
|
740
|
+
}
|
|
741
|
+
/**
|
|
742
|
+
* The two live regions for a conversation surface.
|
|
743
|
+
*
|
|
744
|
+
* ★ The transcript itself is NOT a live region, and that is the point. A live region on the
|
|
745
|
+
* streaming node queues every token for announcement — NVDA and JAWS re-read the growing
|
|
746
|
+
* string and never finish. The streaming bubble carries `aria-busy`, the typing indicator's
|
|
747
|
+
* own `role="status"` covers "responding", and this announces once per turn: the settled
|
|
748
|
+
* answer (see `deriveAnnouncement` in ./core, which is where the rule lives).
|
|
749
|
+
*
|
|
750
|
+
* ★ `aria-atomic` so the whole message is read, not just the changed fragment.
|
|
751
|
+
*/
|
|
752
|
+
declare function LiveAnnouncer({ polite, assertive, }: LiveAnnouncerProps): React$1.ReactElement;
|
|
753
|
+
|
|
754
|
+
/**
|
|
755
|
+
* Content available to assistive technology and not to sight.
|
|
756
|
+
*
|
|
757
|
+
* ★ NOT `display:none` and NOT `visibility:hidden` — both remove the node from the
|
|
758
|
+
* accessibility tree, which is the opposite of the intent. The clip-rect recipe is the one
|
|
759
|
+
* that keeps it announced.
|
|
760
|
+
*/
|
|
761
|
+
declare const useVisuallyHiddenStyles: () => Record<"root", string>;
|
|
762
|
+
|
|
763
|
+
/**
|
|
764
|
+
* Put focus somewhere sensible when the focused element disappears.
|
|
765
|
+
*
|
|
766
|
+
* ★ Fluent's tabster ships a `Restorer` and a `Deloser` for exactly this, and the kit needs
|
|
767
|
+
* its own because the affordances that own focus in a transcript are the in-tree ones. A
|
|
768
|
+
* chat surface removes focusable nodes constantly — a message is deleted, an approval card
|
|
769
|
+
* resolves, a conversation closes. When that happens the browser puts focus on `<body>`:
|
|
770
|
+
* the keyboard user's Tab position resets to the top of the page and the screen reader
|
|
771
|
+
* loses its place.
|
|
772
|
+
*
|
|
773
|
+
* ★ It reads `document.activeElement` rather than tracking what it believes had focus,
|
|
774
|
+
* because the interesting case is the one where focus has ALREADY been lost.
|
|
775
|
+
*/
|
|
776
|
+
declare function useFocusRestore(containerRef: React$1.RefObject<HTMLElement>): () => void;
|
|
777
|
+
|
|
778
|
+
interface RovingTabindexOptions {
|
|
779
|
+
orientation?: 'horizontal' | 'vertical';
|
|
780
|
+
loop?: boolean;
|
|
781
|
+
itemCount: number;
|
|
782
|
+
onActivate?: (index: number) => void;
|
|
783
|
+
}
|
|
784
|
+
interface RovingTabindex {
|
|
785
|
+
activeIndex: number;
|
|
786
|
+
setActiveIndex: (index: number) => void;
|
|
787
|
+
/** Spread onto each item. Exactly ONE item is tabbable at a time. */
|
|
788
|
+
getItemProps: (index: number) => {
|
|
789
|
+
tabIndex: 0 | -1;
|
|
790
|
+
'data-roving-item': true;
|
|
791
|
+
onFocus: () => void;
|
|
792
|
+
onKeyDown: (event: React$1.KeyboardEvent) => void;
|
|
793
|
+
};
|
|
794
|
+
}
|
|
795
|
+
/**
|
|
796
|
+
* One tab stop for a group, arrow keys within it — the WAI-ARIA roving tabindex.
|
|
797
|
+
*
|
|
798
|
+
* ★ Why this rather than Fluent's `Toolbar`: `react-toolbar` reaches
|
|
799
|
+
* `useArrowNavigationGroup` → `getMover` → `core.attrHandlers.set`, which throws against a
|
|
800
|
+
* Dynamics form's tabster (8.2.0, no `attrHandlers`) and unmounts the React tree. For a row
|
|
801
|
+
* of buttons the ARIA pattern is ~40 lines, so this is the cheap side of the split-by-cost
|
|
802
|
+
* decision; `TabList` and `Tree` are the expensive side and the kit uses Fluent's.
|
|
803
|
+
*
|
|
804
|
+
* ★ Without a roving index a transcript of 20 messages × 4 hover actions is 80 tab stops.
|
|
805
|
+
*/
|
|
806
|
+
declare function useRovingTabindex(options: RovingTabindexOptions): RovingTabindex;
|
|
807
|
+
|
|
808
|
+
interface StickyScroll {
|
|
809
|
+
/** Put on the scrolling element. */
|
|
810
|
+
onScroll: () => void;
|
|
811
|
+
/** Call to jump to the bottom regardless (e.g. a "jump to latest" button). */
|
|
812
|
+
scrollToBottom: () => void;
|
|
813
|
+
/** False once the reader scrolls up — drives a "jump to latest" affordance. */
|
|
814
|
+
isPinned: boolean;
|
|
815
|
+
}
|
|
816
|
+
/**
|
|
817
|
+
* Keep a transcript pinned to the bottom — but only while the reader is already there.
|
|
818
|
+
*
|
|
819
|
+
* ★★ THIS CARRIES A MEASURED DEFECT FORWARD. apps/form-builder's ChatPane once keyed its
|
|
820
|
+
* scroll effect on the streaming message's CONTENT LENGTH, which queued a smooth
|
|
821
|
+
* `scrollTo` per token — often 10+ per second — and jittered the viewport. Its fix was to
|
|
822
|
+
* key on message COUNT and use `behavior: 'auto'`; both are reproduced here.
|
|
823
|
+
*
|
|
824
|
+
* ★★ AND IT ADDS THE HALF THAT WAS STILL MISSING. Sticking unconditionally means a reader who
|
|
825
|
+
* scrolls up to re-read something mid-stream is yanked back to the bottom on the next
|
|
826
|
+
* append. `wasPinnedRef` is sampled from SCROLL EVENTS, so it reflects where the reader
|
|
827
|
+
* was before the append, not after.
|
|
828
|
+
*
|
|
829
|
+
* ★ `behavior: 'auto'`, never `'smooth'`: a queued smooth scroll is what produced the
|
|
830
|
+
* jitter, and an involuntary animation is a WCAG 2.2.2 hazard besides.
|
|
831
|
+
*/
|
|
832
|
+
declare function useStickyScroll(ref: React$1.RefObject<HTMLElement>, messageCount: number): StickyScroll;
|
|
833
|
+
|
|
834
|
+
interface UseConversationOptions {
|
|
835
|
+
service: IAgentService;
|
|
836
|
+
seed?: readonly AgentMessage[];
|
|
837
|
+
grounding?: Record<string, unknown>;
|
|
838
|
+
conversationId?: string;
|
|
839
|
+
/** Ids are injected so tests and Storybook are deterministic. */
|
|
840
|
+
createId?: () => string;
|
|
841
|
+
}
|
|
842
|
+
interface Conversation {
|
|
843
|
+
state: ConversationState;
|
|
844
|
+
announcement: Announcement;
|
|
845
|
+
busy: boolean;
|
|
846
|
+
send: (text: string, attachments?: AgentAttachment[]) => Promise<void>;
|
|
847
|
+
cancel: () => void;
|
|
848
|
+
}
|
|
849
|
+
/**
|
|
850
|
+
* Drive an `IAgentService` into a `ConversationState`.
|
|
851
|
+
*
|
|
852
|
+
* ★ The reducer and the announcement rule are both PURE and live in ./core — this hook only
|
|
853
|
+
* owns the effectful parts: the abort controller, the iteration, and holding the previous
|
|
854
|
+
* state so a transition can be detected. That split is what lets the interesting rules be
|
|
855
|
+
* tested without a DOM.
|
|
856
|
+
*
|
|
857
|
+
* ★ `send()` NEVER throws. A transport that throws mid-stream (which an AsyncIterable can do,
|
|
858
|
+
* and a Promise-shaped seam cannot) is converted into terminal state, because the
|
|
859
|
+
* alternative is an unmounted tree — the React-layer analogue of the tabster crash.
|
|
860
|
+
*/
|
|
861
|
+
declare function useConversation(options: UseConversationOptions): Conversation;
|
|
862
|
+
|
|
863
|
+
export { ActionCard, type ActionCardProps, ActionResult, type ActionResultProps, AgentAppShell, type AgentAppShellProps, AgentAttachment, AgentAvailability, AgentAvatar, type AgentAvatarProps, AgentCitation, type AgentDescriptor, AgentFailure, AgentMessage, AgentOpsPanel, type AgentOpsPanelProps, AgentParticipant, AgentPicker, type AgentPickerProps, type AgentRunRecord, AgentRunStatus, AgentSource, AgentStatusBadge, type AgentStatusBadgeProps, AgentToolCall, AgentUsage, Announcement, AnswerWithCitations, type AnswerWithCitationsProps, AppRail, type AppRailProps, ApprovalCard, type ApprovalCardProps, AvailabilityBanner, type AvailabilityBannerProps, CitationChip, type CitationChipProps, CitationList, type CitationListProps, Composer, type ComposerProps, ConfirmInline, type ConfirmInlineProps, type Conversation, ConversationHeader, type ConversationHeaderProps, ConversationList, ConversationListItem, type ConversationListItemProps, type ConversationListProps, ConversationState, type ConversationSummary, type ConversationTab, DayDivider, DayDividerLabel, type DayDividerProps, EmptyConversation, type EmptyConversationProps, GlobalSearchBar, type GlobalSearchBarProps, GroundingPanel, type GroundingPanelProps, type HeaderAction, IAgentService, LiveAnnouncer, type LiveAnnouncerProps, MessageActions, type MessageActionsProps, MessageBubble, type MessageBubbleProps, MessageList, type MessageListProps, ModelBadge, type ModelBadgeProps, ParameterReview, type ParameterReviewProps, type PresenceState, ProviderAttribution, type ProviderAttributionProps, type RailItem, RightPane, type RightPaneProps, type RovingTabindex, type RovingTabindexOptions, RunHistoryPanel, type RunHistoryPanelProps, RunTimeline, type RunTimelineProps, SourceCard, type SourceCardProps, type StickyScroll, StreamingText, type StreamingTextProps, SuggestedPrompts, type SuggestedPromptsProps, SystemNotice, type SystemNoticeProps, ThrottleNotice, type ThrottleNoticeProps, ToolCallCard, type ToolCallCardProps, TypingIndicator, type TypingIndicatorProps, UsageMeter, type UsageMeterProps, type UseConversationOptions, conversationAccessibleName, formatDayLabel, messageAttribution, runStatusLabel, useConversation, useFocusRestore, useRovingTabindex, useStickyScroll, useVisuallyHiddenStyles };
|