@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/dist/index.cjs ADDED
@@ -0,0 +1,3145 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var src_exports = {};
32
+ __export(src_exports, {
33
+ ANNOUNCE_FULL_TEXT_MAX_CHARS: () => ANNOUNCE_FULL_TEXT_MAX_CHARS,
34
+ ActionCard: () => ActionCard,
35
+ ActionResult: () => ActionResult,
36
+ AgentAppShell: () => AgentAppShell,
37
+ AgentAvatar: () => AgentAvatar,
38
+ AgentKitHostProvider: () => AgentKitHostProvider,
39
+ AgentOpsPanel: () => AgentOpsPanel,
40
+ AgentPicker: () => AgentPicker,
41
+ AgentStatusBadge: () => AgentStatusBadge,
42
+ AnswerWithCitations: () => AnswerWithCitations,
43
+ AppRail: () => AppRail,
44
+ ApprovalCard: () => ApprovalCard,
45
+ AvailabilityBanner: () => AvailabilityBanner,
46
+ CitationChip: () => CitationChip,
47
+ CitationList: () => CitationList,
48
+ Composer: () => Composer,
49
+ ConfirmInline: () => ConfirmInline,
50
+ ConversationHeader: () => ConversationHeader,
51
+ ConversationList: () => ConversationList,
52
+ ConversationListItem: () => ConversationListItem,
53
+ DEFAULT_GROUP_WINDOW_MS: () => DEFAULT_GROUP_WINDOW_MS,
54
+ DayDivider: () => DayDivider,
55
+ EmptyConversation: () => EmptyConversation,
56
+ GlobalSearchBar: () => GlobalSearchBar,
57
+ GroundingPanel: () => GroundingPanel,
58
+ LiveAnnouncer: () => LiveAnnouncer,
59
+ MessageActions: () => MessageActions,
60
+ MessageBubble: () => MessageBubble,
61
+ MessageList: () => MessageList,
62
+ MockAgentService: () => MockAgentService,
63
+ ModelBadge: () => ModelBadge,
64
+ NO_ANNOUNCEMENT: () => NO_ANNOUNCEMENT,
65
+ NullAgentService: () => NullAgentService,
66
+ ParameterReview: () => ParameterReview,
67
+ ProviderAttribution: () => ProviderAttribution,
68
+ RightPane: () => RightPane,
69
+ RunHistoryPanel: () => RunHistoryPanel,
70
+ RunTimeline: () => RunTimeline,
71
+ STICK_TO_BOTTOM_THRESHOLD_PX: () => STICK_TO_BOTTOM_THRESHOLD_PX,
72
+ SourceCard: () => SourceCard,
73
+ StreamingText: () => StreamingText,
74
+ SuggestedPrompts: () => SuggestedPrompts,
75
+ SystemNotice: () => SystemNotice,
76
+ ThrottleNotice: () => ThrottleNotice,
77
+ ToolCallCard: () => ToolCallCard,
78
+ TypingIndicator: () => TypingIndicator,
79
+ UsageMeter: () => UsageMeter,
80
+ agentDarkTheme: () => agentDarkTheme,
81
+ agentTheme: () => agentTheme,
82
+ appendUserMessage: () => appendUserMessage,
83
+ applyAgentEvent: () => applyAgentEvent,
84
+ bucketByDay: () => bucketByDay,
85
+ classifyTransportFailure: () => classifyTransportFailure,
86
+ conversationAccessibleName: () => conversationAccessibleName,
87
+ createAgentDarkTheme: () => createAgentDarkTheme,
88
+ createAgentService: () => createAgentService,
89
+ createAgentTheme: () => createAgentTheme,
90
+ createIdPrefixCounter: () => createIdPrefixCounter,
91
+ dayKeyOf: () => dayKeyOf,
92
+ decide: () => decide,
93
+ deriveAnnouncement: () => deriveAnnouncement,
94
+ extractCitationMarkers: () => extractCitationMarkers,
95
+ formatDayLabel: () => formatDayLabel,
96
+ fromClaudeStreamEvent: () => fromClaudeStreamEvent,
97
+ fromFoundryChatResult: () => fromFoundryChatResult,
98
+ fromOpenAiChatChunk: () => fromOpenAiChatChunk,
99
+ fromSseLine: () => fromSseLine,
100
+ groupMessages: () => groupMessages,
101
+ immediateScheduler: () => immediateScheduler,
102
+ initialConversation: () => initialConversation,
103
+ isSameAuthorRun: () => isSameAuthorRun,
104
+ isTerminalStatus: () => isTerminalStatus,
105
+ messageAttribution: () => messageAttribution,
106
+ mockScenarios: () => mockScenarios,
107
+ nextAgentIdPrefix: () => nextAgentIdPrefix,
108
+ realScheduler: () => realScheduler,
109
+ renumberCitations: () => renumberCitations,
110
+ resolveHostTheme: () => resolveHostTheme,
111
+ runStatusLabel: () => runStatusLabel,
112
+ runStatusSeverity: () => runStatusSeverity,
113
+ shouldStickToBottom: () => shouldStickToBottom,
114
+ splitTextByCitations: () => splitTextByCitations,
115
+ useConversation: () => useConversation,
116
+ useFocusRestore: () => useFocusRestore,
117
+ useRovingTabindex: () => useRovingTabindex,
118
+ useStickyScroll: () => useStickyScroll,
119
+ useVisuallyHiddenStyles: () => useVisuallyHiddenStyles
120
+ });
121
+ module.exports = __toCommonJS(src_exports);
122
+
123
+ // src/conversation/MessageList.tsx
124
+ var React10 = __toESM(require("react"), 1);
125
+ var import_react_components10 = require("@fluentui/react-components");
126
+ var import_react_icons3 = require("@fluentui/react-icons");
127
+
128
+ // src/core/runStatus.ts
129
+ var TERMINAL = /* @__PURE__ */ new Set([
130
+ "succeeded",
131
+ "blocked",
132
+ "refused",
133
+ "error",
134
+ "notProvisioned",
135
+ "cancelled"
136
+ ]);
137
+ function isTerminalStatus(status) {
138
+ return TERMINAL.has(status);
139
+ }
140
+ function runStatusSeverity(status) {
141
+ switch (status) {
142
+ case "succeeded":
143
+ return "success";
144
+ case "blocked":
145
+ case "refused":
146
+ return "warning";
147
+ case "error":
148
+ return "error";
149
+ case "notProvisioned":
150
+ case "cancelled":
151
+ return "info";
152
+ }
153
+ }
154
+ function classifyTransportFailure(input) {
155
+ const { httpStatus, code, retryAfterMs } = input;
156
+ const message = input.message ?? "The agent could not be reached.";
157
+ if (httpStatus === 503) {
158
+ return {
159
+ status: "notProvisioned",
160
+ message,
161
+ code: code ?? "not_provisioned",
162
+ recoverable: false,
163
+ correlationId: void 0
164
+ };
165
+ }
166
+ if (httpStatus === 429) {
167
+ return { status: "error", message, code: code ?? "rate_limited", recoverable: true, retryAfterMs };
168
+ }
169
+ if (code === "content_filter") {
170
+ return { status: "blocked", message, code, recoverable: false };
171
+ }
172
+ return { status: "error", message, code, recoverable: httpStatus !== void 0 && httpStatus >= 500 };
173
+ }
174
+
175
+ // src/core/grouping.ts
176
+ var DEFAULT_GROUP_WINDOW_MS = 5 * 60 * 1e3;
177
+ function dayKey(epochMs, timeZone) {
178
+ return new Intl.DateTimeFormat("en-CA", {
179
+ timeZone,
180
+ year: "numeric",
181
+ month: "2-digit",
182
+ day: "2-digit"
183
+ }).format(new Date(epochMs));
184
+ }
185
+ function isSameAuthorRun(a, b, windowMs = DEFAULT_GROUP_WINDOW_MS, timeZone) {
186
+ if (a.authorId !== b.authorId) return false;
187
+ if (a.role !== b.role) return false;
188
+ if (a.role === "system" || b.role === "system") return false;
189
+ if (b.createdAt < a.createdAt) return false;
190
+ if (b.createdAt - a.createdAt > windowMs) return false;
191
+ return dayKey(a.createdAt, timeZone) === dayKey(b.createdAt, timeZone);
192
+ }
193
+ function groupMessages(messages, options = {}) {
194
+ const windowMs = options.windowMs ?? DEFAULT_GROUP_WINDOW_MS;
195
+ const groups = [];
196
+ for (const message of messages) {
197
+ const current = groups[groups.length - 1];
198
+ const previousId = current?.messageIds[current.messageIds.length - 1];
199
+ const previous = previousId ? messages.find((m) => m.id === previousId) : void 0;
200
+ if (current && previous && isSameAuthorRun(previous, message, windowMs, options.timeZone)) {
201
+ current.messageIds.push(message.id);
202
+ continue;
203
+ }
204
+ groups.push({
205
+ key: `${message.authorId}:${message.id}`,
206
+ authorId: message.authorId,
207
+ role: message.role,
208
+ startedAt: message.createdAt,
209
+ messageIds: [message.id]
210
+ });
211
+ }
212
+ return groups;
213
+ }
214
+
215
+ // src/core/dayDividers.ts
216
+ function dayKeyOf(epochMs, timeZone) {
217
+ return new Intl.DateTimeFormat("en-CA", {
218
+ timeZone,
219
+ year: "numeric",
220
+ month: "2-digit",
221
+ day: "2-digit"
222
+ }).format(new Date(epochMs));
223
+ }
224
+ var DAY_MS = 24 * 60 * 60 * 1e3;
225
+ function labelFor(dayKey2, nowMs, timeZone) {
226
+ const todayKey = dayKeyOf(nowMs, timeZone);
227
+ if (dayKey2 === todayKey) return { kind: "today" };
228
+ if (dayKey2 === dayKeyOf(nowMs - DAY_MS, timeZone)) return { kind: "yesterday" };
229
+ const asDate = /* @__PURE__ */ new Date(`${dayKey2}T00:00:00Z`);
230
+ for (let back = 2; back <= 6; back++) {
231
+ if (dayKey2 === dayKeyOf(nowMs - back * DAY_MS, timeZone)) {
232
+ return { kind: "weekday", weekday: asDate.getUTCDay(), iso: dayKey2 };
233
+ }
234
+ }
235
+ return { kind: "date", iso: dayKey2 };
236
+ }
237
+ function bucketByDay(messages, options = {}) {
238
+ const nowMs = options.now ?? Date.now();
239
+ const buckets = [];
240
+ for (const message of messages) {
241
+ const key = dayKeyOf(message.createdAt, options.timeZone);
242
+ const current = buckets[buckets.length - 1];
243
+ if (current && current.dayKey === key) {
244
+ current.messageIds.push(message.id);
245
+ continue;
246
+ }
247
+ buckets.push({
248
+ dayKey: key,
249
+ label: labelFor(key, nowMs, options.timeZone),
250
+ messageIds: [message.id]
251
+ });
252
+ }
253
+ return buckets;
254
+ }
255
+
256
+ // src/core/citations.ts
257
+ var BRACKET_MARKER = /\[(\d{1,3})\]/g;
258
+ var AOAI_MARKER = /【(\d{1,3}):\d+†[^】]*】/g;
259
+ function extractCitationMarkers(text) {
260
+ const found = [];
261
+ for (const re of [BRACKET_MARKER, AOAI_MARKER]) {
262
+ re.lastIndex = 0;
263
+ let m;
264
+ while ((m = re.exec(text)) !== null) {
265
+ found.push({ marker: m[1], index: m.index, length: m[0].length });
266
+ }
267
+ }
268
+ return found.sort((a, b) => a.index - b.index);
269
+ }
270
+ function renumberCitations(text, sources) {
271
+ const markers = extractCitationMarkers(text);
272
+ const order = [];
273
+ for (const { marker } of markers) if (!order.includes(marker)) order.push(marker);
274
+ const byOriginal = /* @__PURE__ */ new Map();
275
+ order.forEach((marker, i) => byOriginal.set(marker, i + 1));
276
+ const used = [];
277
+ const citations = [];
278
+ for (const marker of order) {
279
+ const source = sources[Number(marker) - 1];
280
+ if (!source) continue;
281
+ used.push(source);
282
+ citations.push({ marker: String(byOriginal.get(marker)), sourceId: source.id });
283
+ }
284
+ let out = "";
285
+ let cursor = 0;
286
+ for (const { marker, index, length } of markers) {
287
+ out += text.slice(cursor, index);
288
+ const renumbered = byOriginal.get(marker);
289
+ out += renumbered === void 0 ? "" : `[${renumbered}]`;
290
+ cursor = index + length;
291
+ }
292
+ out += text.slice(cursor);
293
+ return { text: out, used, citations };
294
+ }
295
+ function splitTextByCitations(text, citations) {
296
+ const markers = extractCitationMarkers(text);
297
+ if (markers.length === 0) return text ? [{ kind: "text", text }] : [];
298
+ const segments = [];
299
+ let cursor = 0;
300
+ for (const { marker, index, length } of markers) {
301
+ if (index > cursor) segments.push({ kind: "text", text: text.slice(cursor, index) });
302
+ const citation = citations.find((c) => c.marker === marker);
303
+ segments.push({
304
+ kind: "citation",
305
+ text: `[${marker}]`,
306
+ marker,
307
+ sourceId: citation?.sourceId
308
+ });
309
+ cursor = index + length;
310
+ }
311
+ if (cursor < text.length) segments.push({ kind: "text", text: text.slice(cursor) });
312
+ return segments;
313
+ }
314
+
315
+ // src/core/conversationReducer.ts
316
+ function initialConversation(seed = []) {
317
+ return { messages: [...seed], streamingMessageId: null, status: "idle" };
318
+ }
319
+ function patch(state, messageId, update) {
320
+ const i = state.messages.findIndex((m) => m.id === messageId);
321
+ if (i === -1) return state.messages;
322
+ const next = state.messages.slice();
323
+ next[i] = update(next[i]);
324
+ return next;
325
+ }
326
+ function appendUserMessage(state, input) {
327
+ const message = {
328
+ id: input.id,
329
+ role: "user",
330
+ authorId: input.authorId ?? "user",
331
+ text: input.text,
332
+ createdAt: input.now ?? Date.now(),
333
+ attachments: input.attachments
334
+ };
335
+ return { ...state, messages: [...state.messages, message], status: "connecting", failure: void 0 };
336
+ }
337
+ function applyAgentEvent(state, event) {
338
+ switch (event.type) {
339
+ case "turn_start": {
340
+ const message = {
341
+ id: event.messageId,
342
+ role: "assistant",
343
+ authorId: event.provider ?? "agent",
344
+ text: "",
345
+ createdAt: Date.now(),
346
+ streaming: true,
347
+ provider: event.provider
348
+ };
349
+ return {
350
+ ...state,
351
+ messages: [...state.messages, message],
352
+ streamingMessageId: event.messageId,
353
+ status: "streaming",
354
+ statusLabel: void 0,
355
+ failure: void 0,
356
+ suggestions: void 0
357
+ };
358
+ }
359
+ case "text_delta":
360
+ return {
361
+ ...state,
362
+ messages: patch(state, event.messageId, (m) => ({ ...m, text: m.text + event.text }))
363
+ };
364
+ case "thinking_delta":
365
+ return {
366
+ ...state,
367
+ messages: patch(state, event.messageId, (m) => ({
368
+ ...m,
369
+ thinking: (m.thinking ?? "") + event.text
370
+ }))
371
+ };
372
+ case "tool_call":
373
+ return {
374
+ ...state,
375
+ messages: patch(state, event.messageId, (m) => ({
376
+ ...m,
377
+ toolCalls: [...m.toolCalls ?? [], event.call]
378
+ }))
379
+ };
380
+ case "tool_result":
381
+ return {
382
+ ...state,
383
+ messages: patch(state, event.messageId, (m) => ({
384
+ ...m,
385
+ toolCalls: (m.toolCalls ?? []).map(
386
+ (c) => c.id === event.callId ? { ...c, status: event.status, resultText: event.resultText, failure: event.failure } : c
387
+ )
388
+ }))
389
+ };
390
+ case "sources":
391
+ return {
392
+ ...state,
393
+ messages: patch(state, event.messageId, (m) => ({ ...m, sources: event.sources }))
394
+ };
395
+ case "citation":
396
+ return {
397
+ ...state,
398
+ messages: patch(state, event.messageId, (m) => ({
399
+ ...m,
400
+ citations: [...m.citations ?? [], event.citation]
401
+ }))
402
+ };
403
+ case "suggestions":
404
+ return { ...state, suggestions: event.prompts };
405
+ case "status":
406
+ return { ...state, statusLabel: event.label };
407
+ case "turn_end": {
408
+ const { result } = event;
409
+ return {
410
+ ...state,
411
+ messages: patch(state, event.messageId, (m) => ({
412
+ ...m,
413
+ // A non-streaming provider sends no deltas, so the assembled text arrives here.
414
+ text: m.text.length > 0 ? m.text : result.text,
415
+ streaming: false,
416
+ guarded: result.guarded,
417
+ provider: result.provider,
418
+ usage: result.usage,
419
+ failure: result.failure
420
+ })),
421
+ streamingMessageId: null,
422
+ status: result.status,
423
+ statusLabel: void 0,
424
+ failure: result.failure,
425
+ usage: result.usage
426
+ };
427
+ }
428
+ }
429
+ }
430
+
431
+ // src/core/normalize.ts
432
+ function fromSseLine(line) {
433
+ const trimmed = line.trim();
434
+ if (trimmed === "" || trimmed.startsWith(":")) return void 0;
435
+ if (!trimmed.startsWith("data:")) return void 0;
436
+ const payload = trimmed.slice("data:".length).trim();
437
+ if (payload === "") return void 0;
438
+ if (payload === "[DONE]") return "[DONE]";
439
+ try {
440
+ return JSON.parse(payload);
441
+ } catch {
442
+ return void 0;
443
+ }
444
+ }
445
+ function fromOpenAiChatChunk(chunk, ctx) {
446
+ const events = [];
447
+ const delta = chunk?.choices?.[0]?.delta;
448
+ if (!delta) return events;
449
+ if (typeof delta.content === "string" && delta.content.length > 0) {
450
+ events.push({ type: "text_delta", messageId: ctx.messageId, text: delta.content });
451
+ }
452
+ for (const call of delta.tool_calls ?? []) {
453
+ if (!call.id) continue;
454
+ const toolCall = {
455
+ id: call.id,
456
+ name: call.function?.name ?? "",
457
+ argumentsJson: call.function?.arguments ?? "",
458
+ status: "proposed"
459
+ };
460
+ events.push({ type: "tool_call", messageId: ctx.messageId, call: toolCall });
461
+ }
462
+ return events;
463
+ }
464
+ function fromClaudeStreamEvent(event, ctx) {
465
+ switch (event.type) {
466
+ case "init":
467
+ return [
468
+ {
469
+ type: "turn_start",
470
+ turnId: ctx.turnId,
471
+ messageId: ctx.messageId,
472
+ model: event.model,
473
+ provider: "claude-bridge",
474
+ sessionId: event.sessionId
475
+ }
476
+ ];
477
+ case "text_delta":
478
+ return event.text ? [{ type: "text_delta", messageId: ctx.messageId, text: event.text }] : [];
479
+ case "thinking_delta":
480
+ return event.text ? [{ type: "thinking_delta", messageId: ctx.messageId, text: event.text }] : [];
481
+ case "done":
482
+ return [
483
+ {
484
+ type: "turn_end",
485
+ turnId: ctx.turnId,
486
+ messageId: ctx.messageId,
487
+ result: {
488
+ status: "succeeded",
489
+ text: event.fullText ?? "",
490
+ provider: "claude-bridge",
491
+ model: event.model,
492
+ usage: {
493
+ inputTokens: event.inputTokens,
494
+ outputTokens: event.outputTokens,
495
+ cachedTokens: event.cacheReadTokens,
496
+ costUsd: event.costUsd,
497
+ durationMs: event.durationMs,
498
+ model: event.model
499
+ }
500
+ }
501
+ }
502
+ ];
503
+ case "error":
504
+ return [
505
+ {
506
+ type: "turn_end",
507
+ turnId: ctx.turnId,
508
+ messageId: ctx.messageId,
509
+ result: {
510
+ status: "error",
511
+ text: "",
512
+ provider: "claude-bridge",
513
+ failure: {
514
+ status: "error",
515
+ message: event.message ?? "The agent failed.",
516
+ recoverable: event.recoverable ?? false
517
+ }
518
+ }
519
+ }
520
+ ];
521
+ }
522
+ }
523
+ function fromFoundryChatResult(result, ctx, provider = "foundry") {
524
+ return [
525
+ { type: "turn_start", turnId: ctx.turnId, messageId: ctx.messageId, model: result.model, provider },
526
+ {
527
+ type: "turn_end",
528
+ turnId: ctx.turnId,
529
+ messageId: ctx.messageId,
530
+ result: {
531
+ status: "succeeded",
532
+ text: result.text,
533
+ provider,
534
+ model: result.model,
535
+ usage: {
536
+ inputTokens: result.promptTokens,
537
+ outputTokens: result.completionTokens,
538
+ model: result.model
539
+ }
540
+ }
541
+ }
542
+ ];
543
+ }
544
+
545
+ // src/core/announce.ts
546
+ var ANNOUNCE_FULL_TEXT_MAX_CHARS = 240;
547
+ var NO_ANNOUNCEMENT = { polite: "", assertive: "" };
548
+ var TERMINAL2 = /* @__PURE__ */ new Set(["succeeded", "blocked", "refused", "error", "notProvisioned", "cancelled"]);
549
+ function deriveAnnouncement(state, previous) {
550
+ const wasTerminal = previous ? TERMINAL2.has(previous.status) : false;
551
+ const isTerminal = TERMINAL2.has(state.status);
552
+ if (!isTerminal || wasTerminal) return NO_ANNOUNCEMENT;
553
+ if (state.failure) {
554
+ return { polite: "", assertive: state.failure.message };
555
+ }
556
+ const last = state.messages[state.messages.length - 1];
557
+ const text = last?.text ?? "";
558
+ if (!text) return NO_ANNOUNCEMENT;
559
+ const guarded = last?.guarded ? "The response was withheld. " : "";
560
+ if (text.length <= ANNOUNCE_FULL_TEXT_MAX_CHARS) {
561
+ return { polite: `${guarded}${text}`, assertive: "" };
562
+ }
563
+ const words = text.trim().split(/\s+/).length;
564
+ return {
565
+ polite: `${guarded}Response complete, ${words} words.`,
566
+ assertive: ""
567
+ };
568
+ }
569
+
570
+ // src/core/scroll.ts
571
+ var STICK_TO_BOTTOM_THRESHOLD_PX = 32;
572
+ function shouldStickToBottom(geometry, thresholdPx = STICK_TO_BOTTOM_THRESHOLD_PX) {
573
+ const { scrollTop, scrollHeight, clientHeight } = geometry;
574
+ const distanceFromBottom = scrollHeight - scrollTop - clientHeight;
575
+ return distanceFromBottom <= thresholdPx;
576
+ }
577
+
578
+ // src/core/ids.ts
579
+ function createIdPrefixCounter(tag) {
580
+ let n = 0;
581
+ return () => `${tag}${++n}-`;
582
+ }
583
+
584
+ // src/a11y/LiveAnnouncer.tsx
585
+ var React = __toESM(require("react"), 1);
586
+
587
+ // src/a11y/visuallyHidden.ts
588
+ var import_react_components = require("@fluentui/react-components");
589
+ var useVisuallyHiddenStyles = (0, import_react_components.makeStyles)({
590
+ root: {
591
+ position: "absolute",
592
+ width: "1px",
593
+ height: "1px",
594
+ margin: "-1px",
595
+ padding: "0",
596
+ overflow: "hidden",
597
+ clip: "rect(0 0 0 0)",
598
+ clipPath: "inset(50%)",
599
+ whiteSpace: "nowrap"
600
+ }
601
+ });
602
+
603
+ // src/a11y/LiveAnnouncer.tsx
604
+ var import_jsx_runtime = require("react/jsx-runtime");
605
+ function useAnnounceWithRepeat(next, set) {
606
+ React.useEffect(() => {
607
+ if (!next) return;
608
+ let cancelled = false;
609
+ set("");
610
+ const id = setTimeout(() => {
611
+ if (!cancelled) set(next);
612
+ }, 0);
613
+ return () => {
614
+ cancelled = true;
615
+ clearTimeout(id);
616
+ };
617
+ }, [next, set]);
618
+ }
619
+ function LiveAnnouncer({
620
+ polite = "",
621
+ assertive = ""
622
+ }) {
623
+ const styles = useVisuallyHiddenStyles();
624
+ const [politeText, setPoliteText] = React.useState("");
625
+ const [assertiveText, setAssertiveText] = React.useState("");
626
+ useAnnounceWithRepeat(polite, setPoliteText);
627
+ useAnnounceWithRepeat(assertive, setAssertiveText);
628
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
629
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
630
+ "div",
631
+ {
632
+ className: styles.root,
633
+ "aria-live": "polite",
634
+ "aria-atomic": "true",
635
+ "data-testid": "agentkit-live-polite",
636
+ children: politeText
637
+ }
638
+ ),
639
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
640
+ "div",
641
+ {
642
+ className: styles.root,
643
+ role: "alert",
644
+ "aria-live": "assertive",
645
+ "aria-atomic": "true",
646
+ "data-testid": "agentkit-live-assertive",
647
+ children: assertiveText
648
+ }
649
+ )
650
+ ] });
651
+ }
652
+
653
+ // src/a11y/useFocusRestore.ts
654
+ var React2 = __toESM(require("react"), 1);
655
+ function useFocusRestore(containerRef) {
656
+ return React2.useCallback(() => {
657
+ const container = containerRef.current;
658
+ if (!container) return;
659
+ const doc = container.ownerDocument;
660
+ if (doc.activeElement && doc.activeElement !== doc.body) return;
661
+ const focusable = container.querySelector(
662
+ 'a[href],button:not([disabled]),input:not([disabled]),textarea:not([disabled]),select:not([disabled]),[tabindex]:not([tabindex="-1"])'
663
+ );
664
+ (focusable ?? container).focus();
665
+ }, [containerRef]);
666
+ }
667
+
668
+ // src/a11y/useRovingTabindex.ts
669
+ var React3 = __toESM(require("react"), 1);
670
+ function useRovingTabindex(options) {
671
+ const { orientation = "horizontal", loop = true, itemCount, onActivate } = options;
672
+ const [activeIndex, setActiveIndex] = React3.useState(0);
673
+ const clamp = React3.useCallback(
674
+ (index) => {
675
+ if (itemCount === 0) return 0;
676
+ return loop ? (index + itemCount) % itemCount : Math.min(Math.max(index, 0), itemCount - 1);
677
+ },
678
+ [itemCount, loop]
679
+ );
680
+ const getItemProps = React3.useCallback(
681
+ (index) => ({
682
+ tabIndex: index === activeIndex ? 0 : -1,
683
+ "data-roving-item": true,
684
+ onFocus: () => setActiveIndex(index),
685
+ onKeyDown: (event) => {
686
+ const prevKey = orientation === "horizontal" ? "ArrowLeft" : "ArrowUp";
687
+ const nextKey = orientation === "horizontal" ? "ArrowRight" : "ArrowDown";
688
+ let target = null;
689
+ if (event.key === nextKey) target = clamp(index + 1);
690
+ else if (event.key === prevKey) target = clamp(index - 1);
691
+ else if (event.key === "Home") target = 0;
692
+ else if (event.key === "End") target = itemCount - 1;
693
+ else if (event.key === "Enter" || event.key === " ") {
694
+ onActivate?.(index);
695
+ return;
696
+ }
697
+ if (target === null) return;
698
+ event.preventDefault();
699
+ event.stopPropagation();
700
+ setActiveIndex(target);
701
+ const group = event.currentTarget.parentElement;
702
+ group?.querySelectorAll("[data-roving-item]")[target]?.focus();
703
+ }
704
+ }),
705
+ [activeIndex, clamp, itemCount, onActivate, orientation]
706
+ );
707
+ return { activeIndex, setActiveIndex, getItemProps };
708
+ }
709
+
710
+ // src/hooks/useStickyScroll.ts
711
+ var React4 = __toESM(require("react"), 1);
712
+ function useStickyScroll(ref, messageCount) {
713
+ const wasPinnedRef = React4.useRef(true);
714
+ const [isPinned, setIsPinned] = React4.useState(true);
715
+ const onScroll = React4.useCallback(() => {
716
+ const el = ref.current;
717
+ if (!el) return;
718
+ const pinned = shouldStickToBottom({
719
+ scrollTop: el.scrollTop,
720
+ scrollHeight: el.scrollHeight,
721
+ clientHeight: el.clientHeight
722
+ });
723
+ wasPinnedRef.current = pinned;
724
+ setIsPinned(pinned);
725
+ }, [ref]);
726
+ const scrollToBottom = React4.useCallback(() => {
727
+ const el = ref.current;
728
+ if (!el) return;
729
+ el.scrollTo({ top: el.scrollHeight, behavior: "auto" });
730
+ wasPinnedRef.current = true;
731
+ setIsPinned(true);
732
+ }, [ref]);
733
+ React4.useEffect(() => {
734
+ if (!wasPinnedRef.current) return;
735
+ const el = ref.current;
736
+ if (!el) return;
737
+ el.scrollTo({ top: el.scrollHeight, behavior: "auto" });
738
+ }, [messageCount, ref]);
739
+ return { onScroll, scrollToBottom, isPinned };
740
+ }
741
+
742
+ // src/hooks/useConversation.ts
743
+ var React5 = __toESM(require("react"), 1);
744
+ var fallbackId = 0;
745
+ function useConversation(options) {
746
+ const { service, seed, grounding, conversationId, createId } = options;
747
+ const [state, setState] = React5.useState(() => initialConversation(seed));
748
+ const [announcement, setAnnouncement] = React5.useState(NO_ANNOUNCEMENT);
749
+ const abortRef = React5.useRef(null);
750
+ const stateRef = React5.useRef(state);
751
+ stateRef.current = state;
752
+ const nextId = React5.useCallback(
753
+ () => createId?.() ?? `m${++fallbackId}-${Date.now().toString(36)}`,
754
+ [createId]
755
+ );
756
+ const commit = React5.useCallback((next) => {
757
+ const previous = stateRef.current;
758
+ stateRef.current = next;
759
+ setState(next);
760
+ const derived = deriveAnnouncement(next, previous);
761
+ if (derived.polite || derived.assertive) setAnnouncement(derived);
762
+ }, []);
763
+ const cancel = React5.useCallback(() => {
764
+ abortRef.current?.abort();
765
+ }, []);
766
+ const send = React5.useCallback(
767
+ async (text, attachments) => {
768
+ if (!text.trim()) return;
769
+ abortRef.current?.abort();
770
+ const controller = new AbortController();
771
+ abortRef.current = controller;
772
+ let next = appendUserMessage(stateRef.current, { id: nextId(), text, attachments });
773
+ commit(next);
774
+ try {
775
+ for await (const event of service.send({
776
+ text,
777
+ history: next.messages,
778
+ grounding,
779
+ attachments,
780
+ conversationId,
781
+ cancelSignal: controller.signal
782
+ })) {
783
+ next = applyAgentEvent(next, event);
784
+ commit(next);
785
+ }
786
+ } catch (error) {
787
+ commit({
788
+ ...next,
789
+ streamingMessageId: null,
790
+ status: "error",
791
+ failure: {
792
+ status: "error",
793
+ message: error instanceof Error ? error.message : String(error),
794
+ recoverable: true
795
+ }
796
+ });
797
+ } finally {
798
+ if (abortRef.current === controller) abortRef.current = null;
799
+ }
800
+ },
801
+ [service, grounding, conversationId, nextId, commit]
802
+ );
803
+ React5.useEffect(() => () => abortRef.current?.abort(), []);
804
+ const busy = state.status === "connecting" || state.status === "streaming";
805
+ return { state, announcement, busy, send, cancel };
806
+ }
807
+
808
+ // src/conversation/MessageBubble.tsx
809
+ var React9 = __toESM(require("react"), 1);
810
+ var import_react_components7 = require("@fluentui/react-components");
811
+
812
+ // src/conversation/StreamingText.tsx
813
+ var import_react_components2 = require("@fluentui/react-components");
814
+ var import_jsx_runtime2 = require("react/jsx-runtime");
815
+ var useStyles = (0, import_react_components2.makeStyles)({
816
+ root: { whiteSpace: "pre-wrap", wordBreak: "break-word" },
817
+ caret: {
818
+ display: "inline-block",
819
+ width: "2px",
820
+ height: "1em",
821
+ marginLeft: "2px",
822
+ verticalAlign: "text-bottom",
823
+ backgroundColor: import_react_components2.tokens.colorBrandForeground1,
824
+ animationName: { "0%,49%": { opacity: 1 }, "50%,100%": { opacity: 0 } },
825
+ animationDuration: "1s",
826
+ animationIterationCount: "infinite",
827
+ // WCAG 2.2.2: a blinking caret is motion, and some readers cannot tolerate it.
828
+ "@media (prefers-reduced-motion: reduce)": { animationName: "none", opacity: 1 }
829
+ }
830
+ });
831
+ function StreamingText({ text, streaming }) {
832
+ const styles = useStyles();
833
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: styles.root, "data-testid": "agentkit-streaming-text", children: [
834
+ text,
835
+ streaming ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: styles.caret, "aria-hidden": "true" }) : null
836
+ ] });
837
+ }
838
+
839
+ // src/conversation/MessageActions.tsx
840
+ var import_react_icons2 = require("@fluentui/react-icons");
841
+
842
+ // src/internal/ActionToolbar.tsx
843
+ var import_react_components3 = require("@fluentui/react-components");
844
+ var import_jsx_runtime3 = require("react/jsx-runtime");
845
+ var useStyles2 = (0, import_react_components3.makeStyles)({
846
+ root: {
847
+ display: "inline-flex",
848
+ gap: import_react_components3.tokens.spacingHorizontalXXS,
849
+ alignItems: "center",
850
+ backgroundColor: import_react_components3.tokens.colorNeutralBackground1,
851
+ borderRadius: import_react_components3.tokens.borderRadiusMedium,
852
+ boxShadow: import_react_components3.tokens.shadow4,
853
+ padding: import_react_components3.tokens.spacingHorizontalXXS
854
+ },
855
+ button: {
856
+ // ★ A focus ring that survives Windows forced-colors. Once the kit owns its own focus
857
+ // management it owns the indicator too, and `outline: none` + box-shadow — the usual
858
+ // custom-focus recipe — disappears entirely under forced-colors.
859
+ ":focus-visible": {
860
+ outline: `2px solid ${import_react_components3.tokens.colorStrokeFocus2}`,
861
+ outlineOffset: "1px"
862
+ }
863
+ }
864
+ });
865
+ function ActionToolbar({ actions, label, className }) {
866
+ const styles = useStyles2();
867
+ const roving = useRovingTabindex({ itemCount: actions.length, orientation: "horizontal" });
868
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { role: "toolbar", "aria-label": label, "aria-orientation": "horizontal", className: (0, import_react_components3.mergeClasses)(styles.root, className), children: actions.map((action, index) => {
869
+ const item = roving.getItemProps(index);
870
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_components3.Tooltip, { content: action.label, relationship: "label", withArrow: true, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
871
+ import_react_components3.Button,
872
+ {
873
+ appearance: "subtle",
874
+ size: "small",
875
+ icon: action.icon,
876
+ disabled: action.disabled,
877
+ onClick: action.onClick,
878
+ className: styles.button,
879
+ ...item
880
+ }
881
+ ) }, action.key);
882
+ }) });
883
+ }
884
+
885
+ // src/internal/CardSurface.tsx
886
+ var React6 = __toESM(require("react"), 1);
887
+ var import_react_components4 = require("@fluentui/react-components");
888
+ var import_jsx_runtime4 = require("react/jsx-runtime");
889
+ var useStyles3 = (0, import_react_components4.makeStyles)({
890
+ root: {
891
+ display: "flex",
892
+ flexDirection: "column",
893
+ gap: import_react_components4.tokens.spacingVerticalXS,
894
+ padding: import_react_components4.tokens.spacingVerticalM,
895
+ borderRadius: import_react_components4.tokens.borderRadiusMedium,
896
+ backgroundColor: import_react_components4.tokens.colorNeutralBackground1,
897
+ border: `1px solid ${import_react_components4.tokens.colorNeutralStroke2}`,
898
+ boxShadow: import_react_components4.tokens.shadow2
899
+ },
900
+ attention: { borderLeft: `3px solid ${import_react_components4.tokens.colorBrandStroke1}` }
901
+ });
902
+ var CardSurface = React6.forwardRef(
903
+ function CardSurface2({ emphasis = "none", className, children, ...rest }, ref) {
904
+ const styles = useStyles3();
905
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
906
+ "div",
907
+ {
908
+ ref,
909
+ className: (0, import_react_components4.mergeClasses)(styles.root, emphasis === "attention" && styles.attention, className),
910
+ ...rest,
911
+ children
912
+ }
913
+ );
914
+ }
915
+ );
916
+
917
+ // src/internal/ConfirmInline.tsx
918
+ var React7 = __toESM(require("react"), 1);
919
+ var import_react_components5 = require("@fluentui/react-components");
920
+ var import_jsx_runtime5 = require("react/jsx-runtime");
921
+ var useStyles4 = (0, import_react_components5.makeStyles)({
922
+ root: { maxWidth: "420px" },
923
+ actions: { display: "flex", gap: import_react_components5.tokens.spacingHorizontalS, justifyContent: "flex-end", marginTop: import_react_components5.tokens.spacingVerticalS }
924
+ });
925
+ var FOCUSABLE = 'a[href],button:not([disabled]),input:not([disabled]),textarea:not([disabled]),select:not([disabled]),[tabindex]:not([tabindex="-1"])';
926
+ function ConfirmInline(props) {
927
+ const { open, title, body, confirmLabel = "Confirm", cancelLabel = "Cancel", destructive, onConfirm, onCancel } = props;
928
+ const styles = useStyles4();
929
+ const ref = React7.useRef(null);
930
+ const returnTo = React7.useRef(null);
931
+ const titleId = React7.useId();
932
+ const bodyId = React7.useId();
933
+ React7.useEffect(() => {
934
+ if (!open) return;
935
+ returnTo.current = document.activeElement ?? null;
936
+ const first = ref.current?.querySelector(FOCUSABLE);
937
+ first?.focus();
938
+ return () => {
939
+ const target = returnTo.current;
940
+ if (target && target.isConnected) target.focus();
941
+ };
942
+ }, [open]);
943
+ if (!open) return null;
944
+ const onKeyDown = (event) => {
945
+ if (event.key === "Escape") {
946
+ event.stopPropagation();
947
+ onCancel();
948
+ return;
949
+ }
950
+ if (event.key !== "Tab") return;
951
+ const items = [...ref.current?.querySelectorAll(FOCUSABLE) ?? []];
952
+ if (items.length === 0) return;
953
+ const first = items[0];
954
+ const last = items[items.length - 1];
955
+ const active = document.activeElement;
956
+ if (event.shiftKey && active === first) {
957
+ event.preventDefault();
958
+ last.focus();
959
+ } else if (!event.shiftKey && active === last) {
960
+ event.preventDefault();
961
+ first.focus();
962
+ }
963
+ };
964
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
965
+ CardSurface,
966
+ {
967
+ ref,
968
+ role: "alertdialog",
969
+ "aria-modal": "true",
970
+ "aria-labelledby": titleId,
971
+ "aria-describedby": bodyId,
972
+ onKeyDown,
973
+ className: styles.root,
974
+ emphasis: "attention",
975
+ children: [
976
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_components5.Text, { id: titleId, weight: "semibold", children: title }),
977
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_components5.Text, { id: bodyId, size: 200, children: body }),
978
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: styles.actions, children: [
979
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_components5.Button, { size: "small", onClick: onCancel, children: cancelLabel }),
980
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_components5.Button, { size: "small", appearance: destructive ? "primary" : "primary", onClick: onConfirm, children: confirmLabel })
981
+ ] })
982
+ ]
983
+ }
984
+ );
985
+ }
986
+
987
+ // src/internal/Disclosure.tsx
988
+ var React8 = __toESM(require("react"), 1);
989
+ var import_react_components6 = require("@fluentui/react-components");
990
+ var import_react_icons = require("@fluentui/react-icons");
991
+ var import_jsx_runtime6 = require("react/jsx-runtime");
992
+ var useStyles5 = (0, import_react_components6.makeStyles)({
993
+ trigger: {
994
+ display: "flex",
995
+ alignItems: "center",
996
+ gap: import_react_components6.tokens.spacingHorizontalXS,
997
+ background: "none",
998
+ border: "none",
999
+ cursor: "pointer",
1000
+ padding: `${import_react_components6.tokens.spacingVerticalXXS} 0`,
1001
+ color: import_react_components6.tokens.colorNeutralForeground2,
1002
+ font: "inherit",
1003
+ fontSize: import_react_components6.tokens.fontSizeBase200,
1004
+ ":focus-visible": { outline: `2px solid ${import_react_components6.tokens.colorStrokeFocus2}`, outlineOffset: "1px" }
1005
+ },
1006
+ panel: { paddingTop: import_react_components6.tokens.spacingVerticalXS }
1007
+ });
1008
+ function Disclosure({
1009
+ id,
1010
+ summary,
1011
+ children,
1012
+ defaultOpen = false,
1013
+ className
1014
+ }) {
1015
+ const styles = useStyles5();
1016
+ const [open, setOpen] = React8.useState(defaultOpen);
1017
+ const panelId = `${id}-panel`;
1018
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className, children: [
1019
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1020
+ "button",
1021
+ {
1022
+ type: "button",
1023
+ id,
1024
+ className: styles.trigger,
1025
+ "aria-expanded": open,
1026
+ "aria-controls": panelId,
1027
+ onClick: () => setOpen((v) => !v),
1028
+ children: [
1029
+ open ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_icons.ChevronDownRegular, {}) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_icons.ChevronRightRegular, {}),
1030
+ summary
1031
+ ]
1032
+ }
1033
+ ),
1034
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1035
+ "div",
1036
+ {
1037
+ id: panelId,
1038
+ role: "region",
1039
+ "aria-labelledby": id,
1040
+ hidden: !open,
1041
+ className: (0, import_react_components6.mergeClasses)(styles.panel),
1042
+ children: open ? children : null
1043
+ }
1044
+ )
1045
+ ] });
1046
+ }
1047
+
1048
+ // src/conversation/MessageActions.tsx
1049
+ var import_jsx_runtime7 = require("react/jsx-runtime");
1050
+ function MessageActions({
1051
+ message,
1052
+ onCopy,
1053
+ onRetry,
1054
+ onFeedback
1055
+ }) {
1056
+ const actions = [];
1057
+ if (onCopy) {
1058
+ actions.push({ key: "copy", label: "Copy message", icon: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_icons2.CopyRegular, {}), onClick: () => onCopy(message) });
1059
+ }
1060
+ if (onRetry && message.role === "assistant") {
1061
+ actions.push({ key: "retry", label: "Regenerate", icon: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_icons2.ArrowClockwiseRegular, {}), onClick: () => onRetry(message) });
1062
+ }
1063
+ if (onFeedback && message.role === "assistant") {
1064
+ actions.push(
1065
+ { key: "up", label: "Good response", icon: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_icons2.ThumbLikeRegular, {}), onClick: () => onFeedback(message, "up") },
1066
+ { key: "down", label: "Bad response", icon: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_icons2.ThumbDislikeRegular, {}), onClick: () => onFeedback(message, "down") }
1067
+ );
1068
+ }
1069
+ if (actions.length === 0) return null;
1070
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ActionToolbar, { actions, label: `Actions for this message` });
1071
+ }
1072
+
1073
+ // src/conversation/MessageBubble.tsx
1074
+ var import_jsx_runtime8 = require("react/jsx-runtime");
1075
+ var useStyles6 = (0, import_react_components7.makeStyles)({
1076
+ row: {
1077
+ display: "flex",
1078
+ gap: import_react_components7.tokens.spacingHorizontalS,
1079
+ position: "relative",
1080
+ paddingRight: import_react_components7.tokens.spacingHorizontalXXL
1081
+ },
1082
+ own: { flexDirection: "row-reverse", paddingRight: 0, paddingLeft: import_react_components7.tokens.spacingHorizontalXXL },
1083
+ gutter: { width: "32px", flexShrink: 0 },
1084
+ stack: { display: "flex", flexDirection: "column", gap: "2px", minWidth: 0, maxWidth: "80%" },
1085
+ name: { fontSize: import_react_components7.tokens.fontSizeBase200, color: import_react_components7.tokens.colorNeutralForeground3 },
1086
+ bubble: {
1087
+ padding: `${import_react_components7.tokens.spacingVerticalS} ${import_react_components7.tokens.spacingHorizontalM}`,
1088
+ borderRadius: import_react_components7.tokens.borderRadiusLarge,
1089
+ backgroundColor: import_react_components7.tokens.colorNeutralBackground3,
1090
+ color: import_react_components7.tokens.colorNeutralForeground1,
1091
+ fontSize: import_react_components7.tokens.fontSizeBase300
1092
+ },
1093
+ ownBubble: {
1094
+ backgroundColor: import_react_components7.tokens.colorBrandBackground,
1095
+ color: import_react_components7.tokens.colorNeutralForegroundOnBrand
1096
+ },
1097
+ actions: {
1098
+ position: "absolute",
1099
+ top: "-12px",
1100
+ right: import_react_components7.tokens.spacingHorizontalS,
1101
+ opacity: 0,
1102
+ pointerEvents: "none",
1103
+ transitionProperty: "opacity",
1104
+ transitionDuration: import_react_components7.tokens.durationFaster
1105
+ },
1106
+ // ★ hover OR focus-within. Without the second selector the row is unreachable by keyboard.
1107
+ actionsVisible: { opacity: 1, pointerEvents: "auto" }
1108
+ });
1109
+ function MessageBubble(props) {
1110
+ const { message, author, isRunStart = true, locale, ...actionHandlers } = props;
1111
+ const styles = useStyles6();
1112
+ const hidden = useVisuallyHiddenStyles();
1113
+ const [revealed, setRevealed] = React9.useState(false);
1114
+ const own = message.role === "user";
1115
+ const name = author?.displayName ?? (own ? "You" : message.provider ?? "Assistant");
1116
+ const time = new Intl.DateTimeFormat(locale, { timeStyle: "short" }).format(new Date(message.createdAt));
1117
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
1118
+ "div",
1119
+ {
1120
+ className: (0, import_react_components7.mergeClasses)(styles.row, own && styles.own),
1121
+ onMouseEnter: () => setRevealed(true),
1122
+ onMouseLeave: () => setRevealed(false),
1123
+ onFocus: () => setRevealed(true),
1124
+ onBlur: (e) => {
1125
+ if (!e.currentTarget.contains(e.relatedTarget)) setRevealed(false);
1126
+ },
1127
+ "data-testid": "agentkit-message",
1128
+ children: [
1129
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: styles.gutter, children: isRunStart ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_components7.Avatar, { name, size: 32, image: author?.imageUrl ? { src: author.imageUrl } : void 0 }) : null }),
1130
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: styles.stack, children: [
1131
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { className: hidden.root, children: [
1132
+ name,
1133
+ ", ",
1134
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("time", { dateTime: new Date(message.createdAt).toISOString(), children: time })
1135
+ ] }),
1136
+ isRunStart ? /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { className: styles.name, "aria-hidden": "true", children: [
1137
+ name,
1138
+ " \xB7 ",
1139
+ time
1140
+ ] }) : null,
1141
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1142
+ "div",
1143
+ {
1144
+ className: (0, import_react_components7.mergeClasses)(styles.bubble, own && styles.ownBubble),
1145
+ "aria-busy": message.streaming || void 0,
1146
+ children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(StreamingText, { text: message.text, streaming: message.streaming })
1147
+ }
1148
+ )
1149
+ ] }),
1150
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: (0, import_react_components7.mergeClasses)(styles.actions, revealed && styles.actionsVisible), children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(MessageActions, { message, ...actionHandlers }) })
1151
+ ]
1152
+ }
1153
+ );
1154
+ }
1155
+
1156
+ // src/conversation/DayDivider.tsx
1157
+ var import_react_components8 = require("@fluentui/react-components");
1158
+ var import_jsx_runtime9 = require("react/jsx-runtime");
1159
+ var useStyles7 = (0, import_react_components8.makeStyles)({
1160
+ root: {
1161
+ display: "flex",
1162
+ alignItems: "center",
1163
+ gap: import_react_components8.tokens.spacingHorizontalM,
1164
+ margin: `${import_react_components8.tokens.spacingVerticalM} 0`,
1165
+ color: import_react_components8.tokens.colorNeutralForeground3,
1166
+ fontSize: import_react_components8.tokens.fontSizeBase200
1167
+ },
1168
+ rule: { flex: 1, height: "1px", backgroundColor: import_react_components8.tokens.colorNeutralStroke2 }
1169
+ });
1170
+ function formatDayLabel(label, locale) {
1171
+ switch (label.kind) {
1172
+ case "today":
1173
+ return "Today";
1174
+ case "yesterday":
1175
+ return "Yesterday";
1176
+ case "weekday":
1177
+ return new Intl.DateTimeFormat(locale, { weekday: "long", timeZone: "UTC" }).format(
1178
+ /* @__PURE__ */ new Date(`${label.iso}T00:00:00Z`)
1179
+ );
1180
+ case "date":
1181
+ return new Intl.DateTimeFormat(locale, { dateStyle: "medium", timeZone: "UTC" }).format(
1182
+ /* @__PURE__ */ new Date(`${label.iso}T00:00:00Z`)
1183
+ );
1184
+ }
1185
+ }
1186
+ function DayDivider({ label, locale }) {
1187
+ const styles = useStyles7();
1188
+ const text = formatDayLabel(label, locale);
1189
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: styles.root, role: "separator", "aria-label": text, children: [
1190
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: styles.rule, "aria-hidden": "true" }),
1191
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { children: text }),
1192
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: styles.rule, "aria-hidden": "true" })
1193
+ ] });
1194
+ }
1195
+
1196
+ // src/conversation/TypingIndicator.tsx
1197
+ var import_react_components9 = require("@fluentui/react-components");
1198
+ var import_jsx_runtime10 = require("react/jsx-runtime");
1199
+ var useStyles8 = (0, import_react_components9.makeStyles)({
1200
+ root: { display: "inline-flex", gap: "4px", alignItems: "center", padding: import_react_components9.tokens.spacingVerticalXS },
1201
+ dot: {
1202
+ width: "6px",
1203
+ height: "6px",
1204
+ borderRadius: import_react_components9.tokens.borderRadiusCircular,
1205
+ backgroundColor: import_react_components9.tokens.colorNeutralForeground3,
1206
+ animationName: { "0%,80%,100%": { opacity: 0.3 }, "40%": { opacity: 1 } },
1207
+ animationDuration: "1.4s",
1208
+ animationIterationCount: "infinite",
1209
+ "@media (prefers-reduced-motion: reduce)": { animationName: "none", opacity: 0.6 }
1210
+ },
1211
+ d2: { animationDelay: "0.2s" },
1212
+ d3: { animationDelay: "0.4s" }
1213
+ });
1214
+ function TypingIndicator({ label = "Assistant is responding" }) {
1215
+ const styles = useStyles8();
1216
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: styles.root, role: "status", "aria-label": label, "data-testid": "agentkit-typing", children: [
1217
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: styles.dot, "aria-hidden": "true" }),
1218
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: `${styles.dot} ${styles.d2}`, "aria-hidden": "true" }),
1219
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: `${styles.dot} ${styles.d3}`, "aria-hidden": "true" })
1220
+ ] });
1221
+ }
1222
+
1223
+ // src/conversation/MessageList.tsx
1224
+ var import_jsx_runtime11 = require("react/jsx-runtime");
1225
+ var useStyles9 = (0, import_react_components10.makeStyles)({
1226
+ root: { position: "relative", height: "100%", minHeight: 0, display: "flex", flexDirection: "column" },
1227
+ scroller: {
1228
+ flex: 1,
1229
+ overflowY: "auto",
1230
+ padding: import_react_components10.tokens.spacingVerticalM,
1231
+ display: "flex",
1232
+ flexDirection: "column",
1233
+ gap: import_react_components10.tokens.spacingVerticalS
1234
+ },
1235
+ jump: { position: "absolute", bottom: import_react_components10.tokens.spacingVerticalM, alignSelf: "center", zIndex: 1 }
1236
+ });
1237
+ function MessageList(props) {
1238
+ const {
1239
+ messages,
1240
+ participants,
1241
+ busy = false,
1242
+ announcement,
1243
+ groupWindowMs,
1244
+ timeZone,
1245
+ locale,
1246
+ now,
1247
+ emptyState,
1248
+ ...actionHandlers
1249
+ } = props;
1250
+ const styles = useStyles9();
1251
+ const scrollerRef = React10.useRef(null);
1252
+ const { onScroll, scrollToBottom, isPinned } = useStickyScroll(scrollerRef, messages.length);
1253
+ const byId = React10.useMemo(
1254
+ () => new Map((participants ?? []).map((p) => [p.id, p])),
1255
+ [participants]
1256
+ );
1257
+ const runStarts = React10.useMemo(() => {
1258
+ const groups = groupMessages(messages, { windowMs: groupWindowMs, timeZone });
1259
+ return new Set(groups.map((g) => g.messageIds[0]));
1260
+ }, [messages, groupWindowMs, timeZone]);
1261
+ const buckets = React10.useMemo(
1262
+ () => bucketByDay(messages, { timeZone, now }),
1263
+ [messages, timeZone, now]
1264
+ );
1265
+ const dividerBefore = React10.useMemo(() => {
1266
+ const map = /* @__PURE__ */ new Map();
1267
+ for (const bucket of buckets) map.set(bucket.messageIds[0], bucket.label);
1268
+ return map;
1269
+ }, [buckets]);
1270
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: styles.root, children: [
1271
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(LiveAnnouncer, { polite: announcement?.polite, assertive: announcement?.assertive }),
1272
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
1273
+ "div",
1274
+ {
1275
+ ref: scrollerRef,
1276
+ className: styles.scroller,
1277
+ onScroll,
1278
+ role: "log",
1279
+ "aria-label": "Conversation",
1280
+ tabIndex: 0,
1281
+ children: [
1282
+ messages.length === 0 ? emptyState ?? null : messages.map((message) => /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(React10.Fragment, { children: [
1283
+ dividerBefore.has(message.id) ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(DayDivider, { label: dividerBefore.get(message.id), locale }) : null,
1284
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1285
+ MessageBubble,
1286
+ {
1287
+ message,
1288
+ author: byId.get(message.authorId),
1289
+ isRunStart: runStarts.has(message.id),
1290
+ locale,
1291
+ ...actionHandlers
1292
+ }
1293
+ )
1294
+ ] }, message.id)),
1295
+ busy ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(TypingIndicator, {}) : null
1296
+ ]
1297
+ }
1298
+ ),
1299
+ !isPinned && messages.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1300
+ import_react_components10.Button,
1301
+ {
1302
+ className: styles.jump,
1303
+ size: "small",
1304
+ appearance: "primary",
1305
+ icon: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_react_icons3.ArrowDownRegular, {}),
1306
+ onClick: scrollToBottom,
1307
+ children: "Jump to latest"
1308
+ }
1309
+ ) : null
1310
+ ] });
1311
+ }
1312
+
1313
+ // src/conversation/Composer.tsx
1314
+ var React11 = __toESM(require("react"), 1);
1315
+ var import_react_components11 = require("@fluentui/react-components");
1316
+ var import_react_icons4 = require("@fluentui/react-icons");
1317
+ var import_jsx_runtime12 = require("react/jsx-runtime");
1318
+ var useStyles10 = (0, import_react_components11.makeStyles)({
1319
+ root: {
1320
+ display: "flex",
1321
+ gap: import_react_components11.tokens.spacingHorizontalS,
1322
+ alignItems: "flex-end",
1323
+ padding: import_react_components11.tokens.spacingVerticalS,
1324
+ borderTop: `1px solid ${import_react_components11.tokens.colorNeutralStroke2}`
1325
+ },
1326
+ input: { flex: 1 },
1327
+ hint: { fontSize: import_react_components11.tokens.fontSizeBase100, color: import_react_components11.tokens.colorNeutralForeground3 }
1328
+ });
1329
+ function Composer(props) {
1330
+ const {
1331
+ value,
1332
+ onChange,
1333
+ onSend,
1334
+ onStop,
1335
+ busy = false,
1336
+ disabled = false,
1337
+ placeholder = "Type a message",
1338
+ maxLength,
1339
+ sendOn = "enter",
1340
+ toolbar
1341
+ } = props;
1342
+ const styles = useStyles10();
1343
+ const hintId = React11.useId();
1344
+ const submit = () => {
1345
+ const text = value.trim();
1346
+ if (!text || busy || disabled) return;
1347
+ onSend(text);
1348
+ };
1349
+ const onKeyDown = (event) => {
1350
+ if (event.key !== "Enter") return;
1351
+ const wants = sendOn === "enter" ? !event.shiftKey : event.metaKey || event.ctrlKey;
1352
+ if (!wants) return;
1353
+ event.preventDefault();
1354
+ submit();
1355
+ };
1356
+ const hint = sendOn === "enter" ? "Press Enter to send, Shift plus Enter for a new line." : "Press Control or Command plus Enter to send.";
1357
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: styles.root, children: [
1358
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: styles.input, children: [
1359
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1360
+ import_react_components11.Textarea,
1361
+ {
1362
+ value,
1363
+ onChange: (_, data) => onChange(data.value),
1364
+ onKeyDown,
1365
+ placeholder,
1366
+ disabled,
1367
+ maxLength,
1368
+ resize: "vertical",
1369
+ "aria-label": placeholder,
1370
+ "aria-describedby": hintId,
1371
+ textarea: { "aria-multiline": true }
1372
+ }
1373
+ ),
1374
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { id: hintId, className: styles.hint, children: hint })
1375
+ ] }),
1376
+ toolbar,
1377
+ busy && onStop ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react_components11.Tooltip, { content: "Stop generating", relationship: "label", withArrow: true, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react_components11.Button, { appearance: "secondary", icon: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react_icons4.StopRegular, {}), onClick: onStop }) }) : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react_components11.Tooltip, { content: "Send", relationship: "label", withArrow: true, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1378
+ import_react_components11.Button,
1379
+ {
1380
+ appearance: "primary",
1381
+ icon: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react_icons4.SendRegular, {}),
1382
+ onClick: submit,
1383
+ disabled: !value.trim() || busy || disabled
1384
+ }
1385
+ ) })
1386
+ ] });
1387
+ }
1388
+
1389
+ // src/conversation/SuggestedPrompts.tsx
1390
+ var import_react_components12 = require("@fluentui/react-components");
1391
+ var import_jsx_runtime13 = require("react/jsx-runtime");
1392
+ var useStyles11 = (0, import_react_components12.makeStyles)({
1393
+ root: { display: "flex", flexWrap: "wrap", gap: import_react_components12.tokens.spacingHorizontalXS, padding: import_react_components12.tokens.spacingVerticalXS },
1394
+ heading: { width: "100%", fontSize: import_react_components12.tokens.fontSizeBase200, color: import_react_components12.tokens.colorNeutralForeground3 }
1395
+ });
1396
+ function SuggestedPrompts({
1397
+ prompts,
1398
+ onPick,
1399
+ heading = "Suggested follow-ups"
1400
+ }) {
1401
+ const styles = useStyles11();
1402
+ if (prompts.length === 0) return null;
1403
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: styles.root, role: "group", "aria-label": heading, children: [
1404
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: styles.heading, children: heading }),
1405
+ prompts.map((prompt) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_react_components12.Button, { size: "small", appearance: "outline", onClick: () => onPick(prompt), children: prompt }, prompt))
1406
+ ] });
1407
+ }
1408
+
1409
+ // src/conversation/EmptyConversation.tsx
1410
+ var import_react_components13 = require("@fluentui/react-components");
1411
+ var import_jsx_runtime14 = require("react/jsx-runtime");
1412
+ var useStyles12 = (0, import_react_components13.makeStyles)({
1413
+ root: {
1414
+ display: "flex",
1415
+ flexDirection: "column",
1416
+ alignItems: "center",
1417
+ justifyContent: "center",
1418
+ gap: import_react_components13.tokens.spacingVerticalS,
1419
+ height: "100%",
1420
+ padding: import_react_components13.tokens.spacingVerticalXXL,
1421
+ textAlign: "center",
1422
+ color: import_react_components13.tokens.colorNeutralForeground3
1423
+ }
1424
+ });
1425
+ function EmptyConversation({
1426
+ title = "Ask the agent",
1427
+ description = "It answers from the records you have open.",
1428
+ prompts = [],
1429
+ onPick
1430
+ }) {
1431
+ const styles = useStyles12();
1432
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: styles.root, children: [
1433
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_components13.Text, { as: "h2", size: 500, weight: "semibold", children: title }),
1434
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_components13.Text, { children: description }),
1435
+ onPick && prompts.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(SuggestedPrompts, { prompts, onPick, heading: "Try one of these" }) : null
1436
+ ] });
1437
+ }
1438
+
1439
+ // src/conversation/SystemNotice.tsx
1440
+ var import_react_components14 = require("@fluentui/react-components");
1441
+ var import_jsx_runtime15 = require("react/jsx-runtime");
1442
+ var INTENT = { success: "success", warning: "warning", error: "error", info: "info" };
1443
+ function SystemNotice({ failure, guarded, onRetry }) {
1444
+ if (!failure && !guarded) return null;
1445
+ if (!failure && guarded) {
1446
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_react_components14.MessageBar, { intent: "warning", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_react_components14.MessageBarBody, { children: [
1447
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_react_components14.MessageBarTitle, { children: "Response withheld" }),
1448
+ "The provider substituted safe text for the generated answer."
1449
+ ] }) });
1450
+ }
1451
+ if (!failure) return null;
1452
+ const title = {
1453
+ blocked: "Response withheld",
1454
+ refused: "The agent declined",
1455
+ error: "Something went wrong",
1456
+ notProvisioned: "Not configured",
1457
+ cancelled: "Stopped"
1458
+ };
1459
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_react_components14.MessageBar, { intent: INTENT[runStatusSeverity(failure.status)], children: [
1460
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_react_components14.MessageBarBody, { children: [
1461
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_react_components14.MessageBarTitle, { children: title[failure.status] ?? "Notice" }),
1462
+ failure.message
1463
+ ] }),
1464
+ failure.recoverable && onRetry ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_react_components14.MessageBarActions, { children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_react_components14.Button, { size: "small", onClick: onRetry, children: "Try again" }) }) : null
1465
+ ] });
1466
+ }
1467
+
1468
+ // src/ops/AgentStatusBadge.tsx
1469
+ var import_react_components15 = require("@fluentui/react-components");
1470
+ var import_react_icons5 = require("@fluentui/react-icons");
1471
+ var import_jsx_runtime16 = require("react/jsx-runtime");
1472
+ var PRESENTATION = {
1473
+ succeeded: { label: "Succeeded", color: "success", icon: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_react_icons5.CheckmarkCircleRegular, {}) },
1474
+ blocked: { label: "Blocked by content filter", color: "warning", icon: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_react_icons5.ShieldErrorRegular, {}) },
1475
+ refused: { label: "Declined", color: "warning", icon: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_react_icons5.DismissCircleRegular, {}) },
1476
+ error: { label: "Error", color: "danger", icon: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_react_icons5.ErrorCircleRegular, {}) },
1477
+ notProvisioned: { label: "Not configured", color: "informative", icon: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_react_icons5.PlugDisconnectedRegular, {}) },
1478
+ cancelled: { label: "Stopped", color: "subtle", icon: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_react_icons5.StopRegular, {}) }
1479
+ };
1480
+ function runStatusLabel(status) {
1481
+ return PRESENTATION[status].label;
1482
+ }
1483
+ function AgentStatusBadge({ status, reason, size = "medium" }) {
1484
+ const { label, color, icon } = PRESENTATION[status];
1485
+ const badge = /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_react_components15.Badge, { appearance: "tint", color, icon, size, children: label });
1486
+ return reason ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_react_components15.Tooltip, { content: reason, relationship: "description", withArrow: true, children: badge }) : badge;
1487
+ }
1488
+
1489
+ // src/ops/AvailabilityBanner.tsx
1490
+ var import_react_components16 = require("@fluentui/react-components");
1491
+ var import_jsx_runtime17 = require("react/jsx-runtime");
1492
+ function AvailabilityBanner({
1493
+ availability,
1494
+ onRetry,
1495
+ actions
1496
+ }) {
1497
+ if (!availability || availability.available) return null;
1498
+ const notProvisioned = availability.status === "notProvisioned";
1499
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_react_components16.MessageBar, { intent: notProvisioned ? "info" : "warning", children: [
1500
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_react_components16.MessageBarBody, { children: [
1501
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_react_components16.MessageBarTitle, { children: notProvisioned ? "No agent configured" : "The agent is unavailable" }),
1502
+ availability.reason ?? "No reason was reported."
1503
+ ] }),
1504
+ onRetry || actions ? /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_react_components16.MessageBarActions, { children: [
1505
+ actions,
1506
+ onRetry ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_react_components16.Button, { size: "small", onClick: onRetry, children: "Check again" }) : null
1507
+ ] }) : null
1508
+ ] });
1509
+ }
1510
+
1511
+ // src/ops/UsageMeter.tsx
1512
+ var import_react_components17 = require("@fluentui/react-components");
1513
+ var import_jsx_runtime18 = require("react/jsx-runtime");
1514
+ var useStyles13 = (0, import_react_components17.makeStyles)({
1515
+ root: {
1516
+ display: "flex",
1517
+ flexWrap: "wrap",
1518
+ gap: import_react_components17.tokens.spacingHorizontalM,
1519
+ fontSize: import_react_components17.tokens.fontSizeBase100,
1520
+ color: import_react_components17.tokens.colorNeutralForeground3
1521
+ },
1522
+ item: { display: "flex", gap: import_react_components17.tokens.spacingHorizontalXXS },
1523
+ value: { fontFamily: import_react_components17.tokens.fontFamilyMonospace },
1524
+ unknown: { fontStyle: "italic" }
1525
+ });
1526
+ function UsageMeter({
1527
+ usage,
1528
+ show = ["tokens", "cost", "duration"]
1529
+ }) {
1530
+ const styles = useStyles13();
1531
+ if (!usage) return null;
1532
+ const items = [];
1533
+ if (show.includes("tokens")) {
1534
+ const known = usage.inputTokens !== void 0 || usage.outputTokens !== void 0;
1535
+ items.push({
1536
+ key: "tokens",
1537
+ label: "Tokens",
1538
+ value: known ? `${usage.inputTokens ?? 0} in / ${usage.outputTokens ?? 0} out${usage.cachedTokens ? ` / ${usage.cachedTokens} cached` : ""}` : "not reported",
1539
+ known
1540
+ });
1541
+ }
1542
+ if (show.includes("cost")) {
1543
+ const known = usage.costUsd !== void 0;
1544
+ items.push({
1545
+ key: "cost",
1546
+ label: "Cost",
1547
+ value: known ? `$${usage.costUsd.toFixed(6)}` : "not reported",
1548
+ known
1549
+ });
1550
+ }
1551
+ if (show.includes("duration")) {
1552
+ const known = usage.durationMs !== void 0;
1553
+ items.push({
1554
+ key: "duration",
1555
+ label: "Duration",
1556
+ value: known ? `${usage.durationMs} ms` : "not reported",
1557
+ known
1558
+ });
1559
+ }
1560
+ if (show.includes("model") && usage.model) {
1561
+ items.push({ key: "model", label: "Model", value: usage.model, known: true });
1562
+ }
1563
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: styles.root, children: items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("span", { className: styles.item, children: [
1564
+ item.label,
1565
+ ":",
1566
+ " ",
1567
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: item.known ? styles.value : styles.unknown, children: item.value })
1568
+ ] }, item.key)) });
1569
+ }
1570
+
1571
+ // src/ops/ThrottleNotice.tsx
1572
+ var React12 = __toESM(require("react"), 1);
1573
+ var import_react_components18 = require("@fluentui/react-components");
1574
+ var import_jsx_runtime19 = require("react/jsx-runtime");
1575
+ function ThrottleNotice({ failure, onRetry, now = Date.now }) {
1576
+ const retryAfterMs = failure?.retryAfterMs;
1577
+ const eligible = Boolean(failure?.recoverable && retryAfterMs && retryAfterMs > 0);
1578
+ const [remaining, setRemaining] = React12.useState(retryAfterMs ?? 0);
1579
+ React12.useEffect(() => {
1580
+ if (!eligible) return;
1581
+ const deadline = now() + (retryAfterMs ?? 0);
1582
+ setRemaining(retryAfterMs ?? 0);
1583
+ const id = setInterval(() => {
1584
+ const left = deadline - now();
1585
+ setRemaining(left > 0 ? left : 0);
1586
+ if (left <= 0) clearInterval(id);
1587
+ }, 250);
1588
+ return () => clearInterval(id);
1589
+ }, [eligible, retryAfterMs, now]);
1590
+ if (!eligible || !failure) return null;
1591
+ const seconds = Math.ceil(remaining / 1e3);
1592
+ const ready = remaining <= 0;
1593
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_react_components18.MessageBar, { intent: "warning", children: [
1594
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_react_components18.MessageBarBody, { children: [
1595
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_react_components18.MessageBarTitle, { children: "Rate limited" }),
1596
+ ready ? "You can try again now." : `${failure.message} Try again in ${seconds}s.`
1597
+ ] }),
1598
+ onRetry ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_react_components18.MessageBarActions, { children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_react_components18.Button, { size: "small", onClick: onRetry, disabled: !ready, children: "Try again" }) }) : null
1599
+ ] });
1600
+ }
1601
+
1602
+ // src/ops/ModelBadge.tsx
1603
+ var import_react_components19 = require("@fluentui/react-components");
1604
+ var import_jsx_runtime20 = require("react/jsx-runtime");
1605
+ function ModelBadge({ provider, model, version }) {
1606
+ const parts = [provider, model, version ? `v${version}` : void 0].filter(Boolean);
1607
+ if (parts.length === 0) return null;
1608
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_react_components19.Badge, { appearance: "outline", color: "informative", children: parts.join(" \xB7 ") });
1609
+ }
1610
+
1611
+ // src/ops/AgentPicker.tsx
1612
+ var import_react_components20 = require("@fluentui/react-components");
1613
+ var import_jsx_runtime21 = require("react/jsx-runtime");
1614
+ function AgentPicker({
1615
+ agents,
1616
+ selectedId,
1617
+ onSelect,
1618
+ label = "Agent",
1619
+ disabled
1620
+ }) {
1621
+ const selected = agents.find((a) => a.id === selectedId);
1622
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_react_components20.Field, { label, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
1623
+ import_react_components20.Dropdown,
1624
+ {
1625
+ disabled,
1626
+ value: selected ? selected.name : "",
1627
+ selectedOptions: selectedId ? [selectedId] : [],
1628
+ onOptionSelect: (_, data) => {
1629
+ if (data.optionValue) onSelect(data.optionValue);
1630
+ },
1631
+ placeholder: "Select an agent",
1632
+ children: agents.map((agent) => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_react_components20.Option, { value: agent.id, text: agent.name, children: agent.model ? `${agent.name} \u2014 ${agent.model}` : agent.name }, agent.id))
1633
+ }
1634
+ ) });
1635
+ }
1636
+
1637
+ // src/ops/RunTimeline.tsx
1638
+ var React13 = __toESM(require("react"), 1);
1639
+ var import_react_components21 = require("@fluentui/react-components");
1640
+ var import_jsx_runtime22 = require("react/jsx-runtime");
1641
+ var useStyles14 = (0, import_react_components21.makeStyles)({
1642
+ list: { listStyle: "none", margin: 0, padding: 0, display: "flex", flexDirection: "column", gap: import_react_components21.tokens.spacingVerticalXS },
1643
+ step: { display: "flex", gap: import_react_components21.tokens.spacingHorizontalS, alignItems: "baseline" },
1644
+ index: { fontFamily: import_react_components21.tokens.fontFamilyMonospace, fontSize: import_react_components21.tokens.fontSizeBase100, color: import_react_components21.tokens.colorNeutralForeground3, minWidth: "18px" },
1645
+ name: { fontFamily: import_react_components21.tokens.fontFamilyMonospace, fontSize: import_react_components21.tokens.fontSizeBase200 },
1646
+ status: { fontSize: import_react_components21.tokens.fontSizeBase100, color: import_react_components21.tokens.colorNeutralForeground3 },
1647
+ section: { marginTop: import_react_components21.tokens.spacingVerticalS }
1648
+ });
1649
+ var TOOL_STATUS = {
1650
+ proposed: "proposed",
1651
+ running: "running",
1652
+ succeeded: "succeeded",
1653
+ failed: "failed",
1654
+ denied: "denied"
1655
+ };
1656
+ function RunTimeline({ message, defaultOpen = false }) {
1657
+ const styles = useStyles14();
1658
+ const id = React13.useId();
1659
+ const calls = message.toolCalls ?? [];
1660
+ const sources = message.sources ?? [];
1661
+ const hasAnything = calls.length > 0 || sources.length > 0 || message.usage || message.failure;
1662
+ if (!hasAnything) return null;
1663
+ const status = message.failure?.status ?? (message.guarded ? "blocked" : "succeeded");
1664
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
1665
+ Disclosure,
1666
+ {
1667
+ id,
1668
+ defaultOpen,
1669
+ summary: `Trace \u2014 ${calls.length} tool call${calls.length === 1 ? "" : "s"}`,
1670
+ children: [
1671
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: styles.step, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(AgentStatusBadge, { status, reason: message.failure?.message, size: "small" }) }),
1672
+ calls.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("ol", { className: styles.list, "aria-label": "Tool calls", children: calls.map((call, index) => /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("li", { className: styles.step, children: [
1673
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("span", { className: styles.index, children: [
1674
+ index + 1,
1675
+ "."
1676
+ ] }),
1677
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: styles.name, children: call.name }),
1678
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: styles.status, children: TOOL_STATUS[call.status] })
1679
+ ] }, call.id)) }) : null,
1680
+ sources.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_react_components21.Text, { as: "p", size: 200, className: styles.section, children: [
1681
+ sources.length,
1682
+ " source",
1683
+ sources.length === 1 ? "" : "s",
1684
+ " retrieved"
1685
+ ] }) : null,
1686
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: styles.section, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(UsageMeter, { usage: message.usage, show: ["tokens", "cost", "duration", "model"] }) })
1687
+ ]
1688
+ }
1689
+ );
1690
+ }
1691
+
1692
+ // src/ops/RunHistoryPanel.tsx
1693
+ var import_react_components22 = require("@fluentui/react-components");
1694
+ var import_jsx_runtime23 = require("react/jsx-runtime");
1695
+ var useStyles15 = (0, import_react_components22.makeStyles)({
1696
+ list: { listStyle: "none", margin: 0, padding: 0, display: "flex", flexDirection: "column", gap: import_react_components22.tokens.spacingVerticalXS },
1697
+ row: {
1698
+ display: "flex",
1699
+ flexDirection: "column",
1700
+ gap: "2px",
1701
+ padding: import_react_components22.tokens.spacingVerticalXS,
1702
+ borderBottom: `1px solid ${import_react_components22.tokens.colorNeutralStroke2}`
1703
+ },
1704
+ head: { display: "flex", alignItems: "center", gap: import_react_components22.tokens.spacingHorizontalS, flexWrap: "wrap" },
1705
+ time: { fontSize: import_react_components22.tokens.fontSizeBase100, color: import_react_components22.tokens.colorNeutralForeground3 },
1706
+ prompt: { fontSize: import_react_components22.tokens.fontSizeBase200, color: import_react_components22.tokens.colorNeutralForeground2, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" },
1707
+ id: { fontFamily: import_react_components22.tokens.fontFamilyMonospace, fontSize: import_react_components22.tokens.fontSizeBase100, color: import_react_components22.tokens.colorNeutralForeground3 },
1708
+ empty: { padding: import_react_components22.tokens.spacingVerticalM, color: import_react_components22.tokens.colorNeutralForeground3 }
1709
+ });
1710
+ function RunHistoryPanel({
1711
+ runs,
1712
+ grid,
1713
+ locale,
1714
+ emptyLabel = "No runs yet."
1715
+ }) {
1716
+ const styles = useStyles15();
1717
+ if (grid) return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_jsx_runtime23.Fragment, { children: grid });
1718
+ if (runs.length === 0) {
1719
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_react_components22.Text, { className: styles.empty, size: 200, children: emptyLabel });
1720
+ }
1721
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("ul", { className: styles.list, "aria-label": "Run history", children: runs.map((run) => /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("li", { className: styles.row, children: [
1722
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: styles.head, children: [
1723
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(AgentStatusBadge, { status: run.status, size: "small" }),
1724
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: styles.time, children: new Intl.DateTimeFormat(locale, { dateStyle: "short", timeStyle: "short" }).format(
1725
+ new Date(run.startedAt)
1726
+ ) }),
1727
+ run.correlationId ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: styles.id, children: run.correlationId }) : null
1728
+ ] }),
1729
+ run.prompt ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: styles.prompt, children: run.prompt }) : null,
1730
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(UsageMeter, { usage: run.usage })
1731
+ ] }, run.id)) });
1732
+ }
1733
+
1734
+ // src/ops/AgentOpsPanel.tsx
1735
+ var import_react_components23 = require("@fluentui/react-components");
1736
+ var import_jsx_runtime24 = require("react/jsx-runtime");
1737
+ var useStyles16 = (0, import_react_components23.makeStyles)({
1738
+ root: { display: "flex", flexDirection: "column", gap: import_react_components23.tokens.spacingVerticalM, minWidth: 0 },
1739
+ head: { display: "flex", alignItems: "center", gap: import_react_components23.tokens.spacingHorizontalS, flexWrap: "wrap" },
1740
+ section: { display: "flex", flexDirection: "column", gap: import_react_components23.tokens.spacingVerticalXS },
1741
+ caps: { fontSize: import_react_components23.tokens.fontSizeBase100, color: import_react_components23.tokens.colorNeutralForeground3 }
1742
+ });
1743
+ function AgentOpsPanel(props) {
1744
+ const { availability, agents, selectedAgentId, onSelectAgent, runs = [], runsGrid, onRefresh, locale } = props;
1745
+ const styles = useStyles16();
1746
+ const selected = agents?.find((a) => a.id === selectedAgentId);
1747
+ const caps = availability?.capabilities;
1748
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: styles.root, children: [
1749
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(AvailabilityBanner, { availability, onRetry: onRefresh }),
1750
+ agents && agents.length > 0 && onSelectAgent ? /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: styles.section, children: [
1751
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(AgentPicker, { agents, selectedId: selectedAgentId, onSelect: onSelectAgent }),
1752
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: styles.head, children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(ModelBadge, { provider: selected?.name, model: selected?.model, version: selected?.version }) })
1753
+ ] }) : null,
1754
+ caps ? /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("span", { className: styles.caps, children: [
1755
+ "Supports:",
1756
+ " ",
1757
+ Object.entries(caps).filter(([, on]) => on).map(([name]) => name).join(", ") || "nothing reported"
1758
+ ] }) : null,
1759
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: styles.section, children: [
1760
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_react_components23.Text, { weight: "semibold", size: 300, children: "Recent runs" }),
1761
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(RunHistoryPanel, { runs, grid: runsGrid, locale })
1762
+ ] })
1763
+ ] });
1764
+ }
1765
+
1766
+ // src/shell/AgentAppShell.tsx
1767
+ var React14 = __toESM(require("react"), 1);
1768
+ var import_react_components24 = require("@fluentui/react-components");
1769
+ var import_react_icons6 = require("@fluentui/react-icons");
1770
+ var import_v9 = require("@dataverse-kit/surface-kit/v9");
1771
+ var import_jsx_runtime25 = require("react/jsx-runtime");
1772
+ var useStyles17 = (0, import_react_components24.makeStyles)({
1773
+ root: {
1774
+ display: "flex",
1775
+ height: "100%",
1776
+ minHeight: 0,
1777
+ backgroundColor: import_react_components24.tokens.colorNeutralBackground2,
1778
+ position: "relative",
1779
+ overflow: "hidden"
1780
+ },
1781
+ sidebar: {
1782
+ display: "flex",
1783
+ flexDirection: "column",
1784
+ minHeight: 0,
1785
+ backgroundColor: import_react_components24.tokens.colorNeutralBackground1,
1786
+ borderRight: `1px solid ${import_react_components24.tokens.colorNeutralStroke2}`
1787
+ },
1788
+ // Below `md` the list overlays the conversation instead of shrinking it to nothing.
1789
+ sidebarOverlay: {
1790
+ position: "absolute",
1791
+ top: 0,
1792
+ bottom: 0,
1793
+ left: 0,
1794
+ zIndex: 2,
1795
+ boxShadow: import_react_components24.tokens.shadow16
1796
+ },
1797
+ scrim: { position: "absolute", inset: "0", zIndex: 1, backgroundColor: import_react_components24.tokens.colorBackgroundOverlay },
1798
+ main: { flex: 1, display: "flex", flexDirection: "column", minWidth: 0, minHeight: 0 },
1799
+ body: { flex: 1, minHeight: 0, display: "flex", flexDirection: "column" },
1800
+ toggle: { position: "absolute", top: import_react_components24.tokens.spacingVerticalS, left: import_react_components24.tokens.spacingHorizontalS, zIndex: 3 }
1801
+ });
1802
+ function AgentAppShell(props) {
1803
+ const {
1804
+ rail,
1805
+ sidebar,
1806
+ header,
1807
+ children,
1808
+ rightPane,
1809
+ sidebarWidth = 280,
1810
+ sidebarOpen,
1811
+ onSidebarOpenChange,
1812
+ label = "Agent workspace"
1813
+ } = props;
1814
+ const styles = useStyles17();
1815
+ const [containerRef, measuredWidth] = (0, import_v9.useContainerWidth)();
1816
+ const width = props.containerWidth ?? (measuredWidth > 0 ? measuredWidth : void 0);
1817
+ const effective = width ?? import_v9.BREAKPOINTS.xl;
1818
+ const breakpoint = (0, import_v9.resolveBreakpoint)(effective);
1819
+ const railVisible = effective >= import_v9.BREAKPOINTS.sm;
1820
+ const sidebarInline = effective >= import_v9.BREAKPOINTS.md;
1821
+ const rightPaneVisible = effective >= import_v9.BREAKPOINTS.lg;
1822
+ const [uncontrolledOpen, setUncontrolledOpen] = React14.useState(false);
1823
+ const overlayOpen = sidebarOpen ?? uncontrolledOpen;
1824
+ const setOverlayOpen = (open) => {
1825
+ onSidebarOpenChange?.(open);
1826
+ if (sidebarOpen === void 0) setUncontrolledOpen(open);
1827
+ };
1828
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { ref: containerRef, className: styles.root, "data-breakpoint": breakpoint, "aria-label": label, children: [
1829
+ rail && railVisible ? rail : null,
1830
+ sidebar && sidebarInline ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: styles.sidebar, style: { width: sidebarWidth }, children: sidebar }) : null,
1831
+ sidebar && !sidebarInline && overlayOpen ? /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(import_jsx_runtime25.Fragment, { children: [
1832
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
1833
+ "div",
1834
+ {
1835
+ className: styles.scrim,
1836
+ onClick: () => setOverlayOpen(false),
1837
+ "aria-hidden": "true"
1838
+ }
1839
+ ),
1840
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: `${styles.sidebar} ${styles.sidebarOverlay}`, style: { width: sidebarWidth }, children: sidebar })
1841
+ ] }) : null,
1842
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: styles.main, children: [
1843
+ sidebar && !sidebarInline ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
1844
+ import_react_components24.Button,
1845
+ {
1846
+ className: styles.toggle,
1847
+ size: "small",
1848
+ appearance: "subtle",
1849
+ icon: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_react_icons6.NavigationRegular, {}),
1850
+ "aria-label": overlayOpen ? "Hide conversations" : "Show conversations",
1851
+ "aria-expanded": overlayOpen,
1852
+ onClick: () => setOverlayOpen(!overlayOpen)
1853
+ }
1854
+ ) : null,
1855
+ header,
1856
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: styles.body, children })
1857
+ ] }),
1858
+ rightPaneVisible ? rightPane : null
1859
+ ] });
1860
+ }
1861
+
1862
+ // src/shell/AppRail.tsx
1863
+ var import_react_components25 = require("@fluentui/react-components");
1864
+ var import_react_icons7 = require("@fluentui/react-icons");
1865
+ var import_jsx_runtime26 = require("react/jsx-runtime");
1866
+ var useStyles18 = (0, import_react_components25.makeStyles)({
1867
+ root: {
1868
+ display: "flex",
1869
+ flexDirection: "column",
1870
+ alignItems: "center",
1871
+ gap: import_react_components25.tokens.spacingVerticalXS,
1872
+ padding: import_react_components25.tokens.spacingVerticalS,
1873
+ backgroundColor: import_react_components25.tokens.colorNeutralBackground3,
1874
+ borderRight: `1px solid ${import_react_components25.tokens.colorNeutralStroke2}`,
1875
+ minHeight: 0
1876
+ },
1877
+ spacer: { flex: 1 },
1878
+ button: {
1879
+ position: "relative",
1880
+ ":focus-visible": { outline: `2px solid ${import_react_components25.tokens.colorStrokeFocus2}`, outlineOffset: "1px" }
1881
+ },
1882
+ selected: { backgroundColor: import_react_components25.tokens.colorNeutralBackground1Selected },
1883
+ badge: {
1884
+ position: "absolute",
1885
+ top: "2px",
1886
+ right: "2px",
1887
+ minWidth: "16px",
1888
+ height: "16px",
1889
+ padding: "0 3px",
1890
+ borderRadius: import_react_components25.tokens.borderRadiusCircular,
1891
+ backgroundColor: import_react_components25.tokens.colorPaletteRedBackground3,
1892
+ color: import_react_components25.tokens.colorNeutralForegroundOnBrand,
1893
+ fontSize: "10px",
1894
+ lineHeight: "16px"
1895
+ }
1896
+ });
1897
+ function AppRail(props) {
1898
+ const { items, selectedId, onSelect, maxVisible = 6, label = "Apps", footer } = props;
1899
+ const styles = useStyles18();
1900
+ const visible = items.slice(0, maxVisible);
1901
+ const overflow = items.slice(maxVisible);
1902
+ return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("nav", { className: styles.root, "aria-label": label, children: [
1903
+ visible.map((item) => /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_react_components25.Tooltip, { content: item.label, relationship: "label", positioning: "after", withArrow: true, children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
1904
+ import_react_components25.Button,
1905
+ {
1906
+ appearance: "subtle",
1907
+ icon: item.icon,
1908
+ className: (0, import_react_components25.mergeClasses)(styles.button, item.id === selectedId && styles.selected),
1909
+ "aria-pressed": item.id === selectedId,
1910
+ "aria-label": item.badgeCount ? `${item.label}, ${item.badgeCount} unread` : item.label,
1911
+ onClick: () => onSelect(item.id),
1912
+ children: item.badgeCount ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { className: styles.badge, "aria-hidden": "true", children: item.badgeCount }) : null
1913
+ }
1914
+ ) }, item.id)),
1915
+ overflow.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(import_react_components25.Menu, { children: [
1916
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_react_components25.MenuTrigger, { disableButtonEnhancement: true, children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
1917
+ import_react_components25.Button,
1918
+ {
1919
+ appearance: "subtle",
1920
+ icon: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_react_icons7.MoreHorizontalRegular, {}),
1921
+ className: styles.button,
1922
+ "aria-label": `More apps, ${overflow.length} hidden`
1923
+ }
1924
+ ) }),
1925
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_react_components25.MenuPopover, { children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_react_components25.MenuList, { children: overflow.map((item) => /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_react_components25.MenuItem, { icon: item.icon, onClick: () => onSelect(item.id), children: item.label }, item.id)) }) })
1926
+ ] }) : null,
1927
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("div", { className: styles.spacer }),
1928
+ footer
1929
+ ] });
1930
+ }
1931
+
1932
+ // src/shell/ConversationList.tsx
1933
+ var React15 = __toESM(require("react"), 1);
1934
+ var import_react_components28 = require("@fluentui/react-components");
1935
+
1936
+ // src/shell/ConversationListItem.tsx
1937
+ var import_react_components27 = require("@fluentui/react-components");
1938
+
1939
+ // src/shell/AgentAvatar.tsx
1940
+ var import_react_components26 = require("@fluentui/react-components");
1941
+ var import_jsx_runtime27 = require("react/jsx-runtime");
1942
+ var STATUS = {
1943
+ available: "available",
1944
+ busy: "busy",
1945
+ away: "away",
1946
+ offline: "offline",
1947
+ dnd: "do-not-disturb",
1948
+ unknown: "unknown"
1949
+ };
1950
+ function AgentAvatar({ participant, presence, size = 32, name }) {
1951
+ const displayName = name ?? participant?.displayName ?? "Unknown";
1952
+ return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
1953
+ import_react_components26.Avatar,
1954
+ {
1955
+ name: displayName,
1956
+ initials: participant?.initials,
1957
+ image: participant?.imageUrl ? { src: participant.imageUrl } : void 0,
1958
+ size,
1959
+ badge: presence ? { status: STATUS[presence] ?? "unknown" } : void 0,
1960
+ "aria-hidden": void 0
1961
+ }
1962
+ );
1963
+ }
1964
+
1965
+ // src/shell/ConversationListItem.tsx
1966
+ var import_jsx_runtime28 = require("react/jsx-runtime");
1967
+ var useStyles19 = (0, import_react_components27.makeStyles)({
1968
+ row: {
1969
+ display: "flex",
1970
+ gap: import_react_components27.tokens.spacingHorizontalS,
1971
+ alignItems: "center",
1972
+ padding: `${import_react_components27.tokens.spacingVerticalS} ${import_react_components27.tokens.spacingHorizontalS}`,
1973
+ borderRadius: import_react_components27.tokens.borderRadiusMedium,
1974
+ cursor: "pointer",
1975
+ ":hover": { backgroundColor: import_react_components27.tokens.colorNeutralBackground1Hover }
1976
+ },
1977
+ // ★ The ACTIVE row (aria-activedescendant target) gets a visible focus indicator, because
1978
+ // real DOM focus stays on the listbox — without this the keyboard user sees nothing move.
1979
+ active: { outline: `2px solid ${import_react_components27.tokens.colorStrokeFocus2}`, outlineOffset: "-2px" },
1980
+ selected: { backgroundColor: import_react_components27.tokens.colorNeutralBackground1Selected },
1981
+ body: { flex: 1, minWidth: 0 },
1982
+ title: {
1983
+ display: "block",
1984
+ fontSize: import_react_components27.tokens.fontSizeBase300,
1985
+ fontWeight: import_react_components27.tokens.fontWeightSemibold,
1986
+ overflow: "hidden",
1987
+ textOverflow: "ellipsis",
1988
+ whiteSpace: "nowrap"
1989
+ },
1990
+ muted: { fontWeight: import_react_components27.tokens.fontWeightRegular, color: import_react_components27.tokens.colorNeutralForeground3 },
1991
+ preview: {
1992
+ display: "block",
1993
+ fontSize: import_react_components27.tokens.fontSizeBase200,
1994
+ color: import_react_components27.tokens.colorNeutralForeground3,
1995
+ overflow: "hidden",
1996
+ textOverflow: "ellipsis",
1997
+ whiteSpace: "nowrap"
1998
+ },
1999
+ meta: { display: "flex", flexDirection: "column", alignItems: "flex-end", gap: "2px" },
2000
+ time: { fontSize: import_react_components27.tokens.fontSizeBase100, color: import_react_components27.tokens.colorNeutralForeground3 },
2001
+ unread: {
2002
+ minWidth: "18px",
2003
+ height: "18px",
2004
+ padding: `0 ${import_react_components27.tokens.spacingHorizontalXXS}`,
2005
+ borderRadius: import_react_components27.tokens.borderRadiusCircular,
2006
+ backgroundColor: import_react_components27.tokens.colorBrandBackground,
2007
+ color: import_react_components27.tokens.colorNeutralForegroundOnBrand,
2008
+ fontSize: import_react_components27.tokens.fontSizeBase100,
2009
+ lineHeight: "18px",
2010
+ textAlign: "center"
2011
+ }
2012
+ });
2013
+ function conversationAccessibleName(summary, locale) {
2014
+ const parts = [summary.title];
2015
+ if (summary.presence && summary.presence !== "unknown") parts.push(summary.presence);
2016
+ if (!summary.muted && summary.unreadCount) {
2017
+ parts.push(`${summary.unreadCount} unread`);
2018
+ }
2019
+ if (summary.muted) parts.push("muted");
2020
+ if (summary.timestamp) {
2021
+ parts.push(new Intl.DateTimeFormat(locale, { timeStyle: "short" }).format(new Date(summary.timestamp)));
2022
+ }
2023
+ return parts.join(", ");
2024
+ }
2025
+ function ConversationListItem(props) {
2026
+ const { summary, selected, active, id, locale, onSelect } = props;
2027
+ const styles = useStyles19();
2028
+ const time = summary.timestamp ? new Intl.DateTimeFormat(locale, { timeStyle: "short" }).format(new Date(summary.timestamp)) : void 0;
2029
+ return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(
2030
+ "div",
2031
+ {
2032
+ id,
2033
+ role: "option",
2034
+ "aria-selected": selected,
2035
+ "aria-label": conversationAccessibleName(summary, locale),
2036
+ className: (0, import_react_components27.mergeClasses)(styles.row, selected && styles.selected, active && styles.active),
2037
+ onClick: () => onSelect(summary.id),
2038
+ "data-conversation-option": true,
2039
+ children: [
2040
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(AgentAvatar, { participant: summary.participant, presence: summary.presence, size: 32 }),
2041
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: styles.body, "aria-hidden": "true", children: [
2042
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { className: (0, import_react_components27.mergeClasses)(styles.title, summary.muted && styles.muted), children: summary.title }),
2043
+ summary.preview ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { className: styles.preview, children: summary.preview }) : null
2044
+ ] }),
2045
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: styles.meta, "aria-hidden": "true", children: [
2046
+ time ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { className: styles.time, children: time }) : null,
2047
+ !summary.muted && summary.unreadCount ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { className: styles.unread, children: summary.unreadCount }) : null
2048
+ ] })
2049
+ ]
2050
+ }
2051
+ );
2052
+ }
2053
+
2054
+ // src/shell/ConversationList.tsx
2055
+ var import_jsx_runtime29 = require("react/jsx-runtime");
2056
+ var useStyles20 = (0, import_react_components28.makeStyles)({
2057
+ root: {
2058
+ display: "flex",
2059
+ flexDirection: "column",
2060
+ gap: "2px",
2061
+ padding: import_react_components28.tokens.spacingHorizontalXS,
2062
+ overflowY: "auto",
2063
+ minHeight: 0,
2064
+ ":focus-visible": { outline: `2px solid ${import_react_components28.tokens.colorStrokeFocus2}`, outlineOffset: "-2px" }
2065
+ },
2066
+ empty: { padding: import_react_components28.tokens.spacingVerticalM, color: import_react_components28.tokens.colorNeutralForeground3 }
2067
+ });
2068
+ function ConversationList(props) {
2069
+ const { items, selectedId, onSelect, label = "Conversations", locale, empty } = props;
2070
+ const styles = useStyles20();
2071
+ const listRef = React15.useRef(null);
2072
+ const idBase = React15.useId();
2073
+ const selectedIndex = Math.max(0, items.findIndex((i) => i.id === selectedId));
2074
+ const [active, setActive] = React15.useState(selectedIndex);
2075
+ React15.useEffect(() => {
2076
+ setActive((current) => {
2077
+ if (items.length === 0) return 0;
2078
+ if (selectedIndex >= 0 && items[selectedIndex]) return selectedIndex;
2079
+ return Math.min(current, items.length - 1);
2080
+ });
2081
+ }, [selectedIndex, items]);
2082
+ const optionId = (index) => `${idBase}-opt-${index}`;
2083
+ React15.useEffect(() => {
2084
+ const el = listRef.current?.ownerDocument.getElementById(optionId(active));
2085
+ el?.scrollIntoView?.({ block: "nearest" });
2086
+ }, [active, idBase]);
2087
+ const onKeyDown = (event) => {
2088
+ const last = items.length - 1;
2089
+ if (last < 0) return;
2090
+ let next = null;
2091
+ if (event.key === "ArrowDown") next = Math.min(last, active + 1);
2092
+ else if (event.key === "ArrowUp") next = Math.max(0, active - 1);
2093
+ else if (event.key === "Home") next = 0;
2094
+ else if (event.key === "End") next = last;
2095
+ else if (event.key === "Enter" || event.key === " ") {
2096
+ event.preventDefault();
2097
+ const picked = items[active];
2098
+ if (picked) onSelect(picked.id);
2099
+ return;
2100
+ }
2101
+ if (next === null) return;
2102
+ event.preventDefault();
2103
+ setActive(next);
2104
+ };
2105
+ if (items.length === 0) {
2106
+ return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: styles.root, children: empty ?? /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_react_components28.Text, { className: styles.empty, size: 200, children: "No conversations yet." }) });
2107
+ }
2108
+ return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
2109
+ "div",
2110
+ {
2111
+ ref: listRef,
2112
+ className: styles.root,
2113
+ role: "listbox",
2114
+ "aria-label": label,
2115
+ tabIndex: 0,
2116
+ "aria-activedescendant": optionId(active),
2117
+ onKeyDown,
2118
+ children: items.map((summary, index) => /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
2119
+ ConversationListItem,
2120
+ {
2121
+ id: optionId(index),
2122
+ summary,
2123
+ selected: summary.id === selectedId,
2124
+ active: index === active,
2125
+ locale,
2126
+ onSelect
2127
+ },
2128
+ summary.id
2129
+ ))
2130
+ }
2131
+ );
2132
+ }
2133
+
2134
+ // src/shell/ConversationHeader.tsx
2135
+ var import_react_components29 = require("@fluentui/react-components");
2136
+ var import_react_icons8 = require("@fluentui/react-icons");
2137
+ var import_jsx_runtime30 = require("react/jsx-runtime");
2138
+ var useStyles21 = (0, import_react_components29.makeStyles)({
2139
+ root: {
2140
+ display: "flex",
2141
+ flexDirection: "column",
2142
+ gap: import_react_components29.tokens.spacingVerticalXS,
2143
+ padding: `${import_react_components29.tokens.spacingVerticalS} ${import_react_components29.tokens.spacingHorizontalM} 0`,
2144
+ borderBottom: `1px solid ${import_react_components29.tokens.colorNeutralStroke2}`,
2145
+ backgroundColor: import_react_components29.tokens.colorNeutralBackground1
2146
+ },
2147
+ top: { display: "flex", alignItems: "center", gap: import_react_components29.tokens.spacingHorizontalS },
2148
+ identity: { display: "flex", alignItems: "center", gap: import_react_components29.tokens.spacingHorizontalS, minWidth: 0 },
2149
+ titles: { display: "flex", flexDirection: "column", minWidth: 0 },
2150
+ subtitle: { color: import_react_components29.tokens.colorNeutralForeground3, fontSize: import_react_components29.tokens.fontSizeBase200 },
2151
+ spacer: { flex: 1 },
2152
+ tabRow: { display: "flex", alignItems: "flex-end", gap: import_react_components29.tokens.spacingHorizontalS }
2153
+ });
2154
+ function ConversationHeader(props) {
2155
+ const {
2156
+ title,
2157
+ subtitle,
2158
+ participant,
2159
+ presence,
2160
+ tabs,
2161
+ selectedTabId,
2162
+ onTabSelect,
2163
+ actions,
2164
+ trailing
2165
+ } = props;
2166
+ const styles = useStyles21();
2167
+ const inline = (actions ?? []).slice(0, 2);
2168
+ const overflow = (actions ?? []).slice(2);
2169
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("header", { className: styles.root, children: [
2170
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { className: styles.top, children: [
2171
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { className: styles.identity, children: [
2172
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(AgentAvatar, { participant, presence, size: 32, name: title }),
2173
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { className: styles.titles, children: [
2174
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_react_components29.Text, { weight: "semibold", truncate: true, wrap: false, children: title }),
2175
+ subtitle ? /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("span", { className: styles.subtitle, children: subtitle }) : null
2176
+ ] })
2177
+ ] }),
2178
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("div", { className: styles.spacer }),
2179
+ trailing,
2180
+ inline.map((action) => /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
2181
+ import_react_components29.Button,
2182
+ {
2183
+ appearance: "subtle",
2184
+ size: "small",
2185
+ icon: action.icon,
2186
+ "aria-label": action.label,
2187
+ onClick: action.onClick
2188
+ },
2189
+ action.key
2190
+ )),
2191
+ overflow.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(import_react_components29.Menu, { children: [
2192
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_react_components29.MenuTrigger, { disableButtonEnhancement: true, children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
2193
+ import_react_components29.Button,
2194
+ {
2195
+ appearance: "subtle",
2196
+ size: "small",
2197
+ icon: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_react_icons8.MoreHorizontalRegular, {}),
2198
+ "aria-label": `More actions, ${overflow.length} available`
2199
+ }
2200
+ ) }),
2201
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_react_components29.MenuPopover, { children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_react_components29.MenuList, { children: overflow.map((action) => /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_react_components29.MenuItem, { icon: action.icon, onClick: action.onClick, children: action.label }, action.key)) }) })
2202
+ ] }) : null
2203
+ ] }),
2204
+ tabs && tabs.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("div", { className: styles.tabRow, children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
2205
+ import_react_components29.TabList,
2206
+ {
2207
+ selectedValue: selectedTabId,
2208
+ onTabSelect: (_, data) => onTabSelect?.(data.value),
2209
+ size: "small",
2210
+ children: tabs.map((tab) => /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_react_components29.Tab, { value: tab.id, children: tab.label }, tab.id))
2211
+ }
2212
+ ) }) : null
2213
+ ] });
2214
+ }
2215
+
2216
+ // src/shell/RightPane.tsx
2217
+ var React16 = __toESM(require("react"), 1);
2218
+ var import_react_components30 = require("@fluentui/react-components");
2219
+ var import_react_icons9 = require("@fluentui/react-icons");
2220
+ var import_jsx_runtime31 = require("react/jsx-runtime");
2221
+ var useStyles22 = (0, import_react_components30.makeStyles)({
2222
+ root: {
2223
+ display: "flex",
2224
+ flexDirection: "column",
2225
+ borderLeft: `1px solid ${import_react_components30.tokens.colorNeutralStroke2}`,
2226
+ backgroundColor: import_react_components30.tokens.colorNeutralBackground1,
2227
+ minHeight: 0
2228
+ },
2229
+ head: {
2230
+ display: "flex",
2231
+ alignItems: "center",
2232
+ justifyContent: "space-between",
2233
+ gap: import_react_components30.tokens.spacingHorizontalS,
2234
+ padding: import_react_components30.tokens.spacingVerticalS,
2235
+ borderBottom: `1px solid ${import_react_components30.tokens.colorNeutralStroke2}`
2236
+ },
2237
+ body: { flex: 1, overflowY: "auto", padding: import_react_components30.tokens.spacingVerticalM }
2238
+ });
2239
+ function RightPane({ open, title, onClose, children, width = 320 }) {
2240
+ const styles = useStyles22();
2241
+ const labelId = React16.useId();
2242
+ if (!open) return null;
2243
+ return /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("aside", { className: styles.root, style: { width }, "aria-labelledby": labelId, children: [
2244
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("div", { className: styles.head, children: [
2245
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_react_components30.Text, { id: labelId, weight: "semibold", children: title }),
2246
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
2247
+ import_react_components30.Button,
2248
+ {
2249
+ appearance: "subtle",
2250
+ size: "small",
2251
+ icon: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_react_icons9.DismissRegular, {}),
2252
+ onClick: onClose,
2253
+ "aria-label": `Close ${title}`
2254
+ }
2255
+ )
2256
+ ] }),
2257
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("div", { className: styles.body, children })
2258
+ ] });
2259
+ }
2260
+
2261
+ // src/shell/GlobalSearchBar.tsx
2262
+ var import_react_components31 = require("@fluentui/react-components");
2263
+ var import_jsx_runtime32 = require("react/jsx-runtime");
2264
+ var useStyles23 = (0, import_react_components31.makeStyles)({
2265
+ root: { width: "100%", maxWidth: "640px" }
2266
+ });
2267
+ function GlobalSearchBar({
2268
+ value,
2269
+ onChange,
2270
+ onSubmit,
2271
+ placeholder = "Search conversations"
2272
+ }) {
2273
+ const styles = useStyles23();
2274
+ return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
2275
+ import_react_components31.SearchBox,
2276
+ {
2277
+ className: styles.root,
2278
+ value,
2279
+ placeholder,
2280
+ "aria-label": placeholder,
2281
+ onChange: (_, data) => onChange(data.value),
2282
+ onKeyDown: (e) => {
2283
+ if (e.key === "Enter" && onSubmit) onSubmit(value);
2284
+ }
2285
+ }
2286
+ );
2287
+ }
2288
+
2289
+ // src/grounding/CitationChip.tsx
2290
+ var import_react_components32 = require("@fluentui/react-components");
2291
+ var import_jsx_runtime33 = require("react/jsx-runtime");
2292
+ var useStyles24 = (0, import_react_components32.makeStyles)({
2293
+ chip: {
2294
+ display: "inline-flex",
2295
+ alignItems: "center",
2296
+ justifyContent: "center",
2297
+ minWidth: "18px",
2298
+ height: "18px",
2299
+ padding: `0 ${import_react_components32.tokens.spacingHorizontalXXS}`,
2300
+ marginLeft: "2px",
2301
+ verticalAlign: "super",
2302
+ fontSize: import_react_components32.tokens.fontSizeBase100,
2303
+ lineHeight: "18px",
2304
+ borderRadius: import_react_components32.tokens.borderRadiusSmall,
2305
+ border: `1px solid ${import_react_components32.tokens.colorNeutralStroke2}`,
2306
+ backgroundColor: import_react_components32.tokens.colorNeutralBackground3,
2307
+ color: import_react_components32.tokens.colorNeutralForeground2,
2308
+ cursor: "pointer",
2309
+ ":hover": { backgroundColor: import_react_components32.tokens.colorNeutralBackground3Hover },
2310
+ ":focus-visible": { outline: `2px solid ${import_react_components32.tokens.colorStrokeFocus2}`, outlineOffset: "1px" }
2311
+ },
2312
+ inert: { cursor: "default" }
2313
+ });
2314
+ function CitationChip({ marker, source, onOpen }) {
2315
+ const styles = useStyles24();
2316
+ const label = source ? `Source ${marker}: ${source.title}` : `Source ${marker}`;
2317
+ if (!source || !onOpen) {
2318
+ return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { className: `${styles.chip} ${styles.inert}`, "aria-label": label, children: marker });
2319
+ }
2320
+ return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_react_components32.Tooltip, { content: source.title, relationship: "description", withArrow: true, children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("button", { type: "button", className: styles.chip, "aria-label": label, onClick: () => onOpen(source), children: marker }) });
2321
+ }
2322
+
2323
+ // src/grounding/CitationList.tsx
2324
+ var React17 = __toESM(require("react"), 1);
2325
+ var import_react_components34 = require("@fluentui/react-components");
2326
+
2327
+ // src/grounding/SourceCard.tsx
2328
+ var import_react_components33 = require("@fluentui/react-components");
2329
+ var import_react_icons10 = require("@fluentui/react-icons");
2330
+ var import_jsx_runtime34 = require("react/jsx-runtime");
2331
+ var ICONS = {
2332
+ dataverse: import_react_icons10.DatabaseRegular,
2333
+ document: import_react_icons10.DocumentRegular,
2334
+ web: import_react_icons10.GlobeRegular,
2335
+ knowledge: import_react_icons10.BookRegular,
2336
+ other: import_react_icons10.DocumentBulletListRegular
2337
+ };
2338
+ var useStyles25 = (0, import_react_components33.makeStyles)({
2339
+ root: { gap: import_react_components33.tokens.spacingVerticalXXS },
2340
+ head: { display: "flex", alignItems: "center", gap: import_react_components33.tokens.spacingHorizontalXS },
2341
+ marker: { color: import_react_components33.tokens.colorNeutralForeground3, fontSize: import_react_components33.tokens.fontSizeBase200 },
2342
+ snippet: { color: import_react_components33.tokens.colorNeutralForeground2, fontSize: import_react_components33.tokens.fontSizeBase200 },
2343
+ meta: { display: "flex", alignItems: "center", gap: import_react_components33.tokens.spacingHorizontalS, marginTop: import_react_components33.tokens.spacingVerticalXXS }
2344
+ });
2345
+ function SourceCard({ source, marker, onOpen, showSnippet = true }) {
2346
+ const styles = useStyles25();
2347
+ const Icon = ICONS[source.kind] ?? ICONS.other;
2348
+ return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(CardSurface, { className: styles.root, children: [
2349
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: styles.head, children: [
2350
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(Icon, { "aria-hidden": "true" }),
2351
+ marker ? /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("span", { className: styles.marker, children: [
2352
+ "[",
2353
+ marker,
2354
+ "]"
2355
+ ] }) : null,
2356
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_react_components33.Text, { weight: "semibold", children: source.title })
2357
+ ] }),
2358
+ showSnippet && source.snippet ? /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: styles.snippet, children: source.snippet }) : null,
2359
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: styles.meta, children: [
2360
+ onOpen && (source.recordId || source.url) ? /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_react_components33.Button, { size: "small", appearance: "subtle", onClick: () => onOpen(source), children: source.recordId ? "Open record" : "Open" }) : null,
2361
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: styles.snippet, children: source.score === void 0 ? "Relevance not reported" : `Relevance ${Math.round(source.score * 100)}%` })
2362
+ ] })
2363
+ ] });
2364
+ }
2365
+
2366
+ // src/grounding/CitationList.tsx
2367
+ var import_jsx_runtime35 = require("react/jsx-runtime");
2368
+ var useStyles26 = (0, import_react_components34.makeStyles)({
2369
+ list: { display: "flex", flexDirection: "column", gap: import_react_components34.tokens.spacingVerticalXS, listStyle: "none", margin: 0, padding: 0 }
2370
+ });
2371
+ function CitationList({
2372
+ text,
2373
+ sources,
2374
+ onOpenSource,
2375
+ defaultOpen = false
2376
+ }) {
2377
+ const styles = useStyles26();
2378
+ const id = React17.useId();
2379
+ const { used, citations } = React17.useMemo(() => renumberCitations(text, sources), [text, sources]);
2380
+ if (used.length === 0) return null;
2381
+ const markerFor = (sourceId) => citations.find((c) => c.sourceId === sourceId)?.marker;
2382
+ return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
2383
+ Disclosure,
2384
+ {
2385
+ id,
2386
+ defaultOpen,
2387
+ summary: `${used.length} source${used.length === 1 ? "" : "s"}`,
2388
+ children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("ul", { className: styles.list, children: used.map((source) => /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(SourceCard, { source, marker: markerFor(source.id), onOpen: onOpenSource }) }, source.id)) })
2389
+ }
2390
+ );
2391
+ }
2392
+
2393
+ // src/grounding/AnswerWithCitations.tsx
2394
+ var React18 = __toESM(require("react"), 1);
2395
+ var import_react_components35 = require("@fluentui/react-components");
2396
+ var import_jsx_runtime36 = require("react/jsx-runtime");
2397
+ var useStyles27 = (0, import_react_components35.makeStyles)({ root: { whiteSpace: "pre-wrap", wordBreak: "break-word" } });
2398
+ function AnswerWithCitations({
2399
+ text,
2400
+ citations = [],
2401
+ sources = [],
2402
+ onOpenSource
2403
+ }) {
2404
+ const styles = useStyles27();
2405
+ const segments = React18.useMemo(() => splitTextByCitations(text, citations), [text, citations]);
2406
+ const byId = React18.useMemo(() => new Map(sources.map((s) => [s.id, s])), [sources]);
2407
+ return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("span", { className: styles.root, children: segments.map(
2408
+ (segment, i) => segment.kind === "text" ? /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(React18.Fragment, { children: segment.text }, i) : /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
2409
+ CitationChip,
2410
+ {
2411
+ marker: segment.marker ?? "",
2412
+ source: segment.sourceId ? byId.get(segment.sourceId) : void 0,
2413
+ onOpen: onOpenSource
2414
+ },
2415
+ i
2416
+ )
2417
+ ) });
2418
+ }
2419
+
2420
+ // src/grounding/GroundingPanel.tsx
2421
+ var React19 = __toESM(require("react"), 1);
2422
+ var import_react_components36 = require("@fluentui/react-components");
2423
+ var import_jsx_runtime37 = require("react/jsx-runtime");
2424
+ var useStyles28 = (0, import_react_components36.makeStyles)({
2425
+ grid: {
2426
+ display: "grid",
2427
+ gridTemplateColumns: "minmax(96px, max-content) 1fr",
2428
+ gap: `${import_react_components36.tokens.spacingVerticalXXS} ${import_react_components36.tokens.spacingHorizontalM}`,
2429
+ margin: 0,
2430
+ fontSize: import_react_components36.tokens.fontSizeBase200
2431
+ },
2432
+ key: { color: import_react_components36.tokens.colorNeutralForeground3, margin: 0 },
2433
+ value: { margin: 0, wordBreak: "break-word", fontFamily: import_react_components36.tokens.fontFamilyMonospace },
2434
+ empty: { fontSize: import_react_components36.tokens.fontSizeBase200, color: import_react_components36.tokens.colorNeutralForeground3 }
2435
+ });
2436
+ function render(value) {
2437
+ if (value === null || value === void 0) return "\u2014";
2438
+ if (typeof value === "string") return value;
2439
+ return JSON.stringify(value);
2440
+ }
2441
+ function GroundingPanel({
2442
+ grounding,
2443
+ title = "What this answer used",
2444
+ defaultOpen = false
2445
+ }) {
2446
+ const styles = useStyles28();
2447
+ const id = React19.useId();
2448
+ const entries = Object.entries(grounding ?? {});
2449
+ if (entries.length === 0) return null;
2450
+ return /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(Disclosure, { id, defaultOpen, summary: title, children: [
2451
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("dl", { className: styles.grid, children: entries.map(([key, value]) => /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(React19.Fragment, { children: [
2452
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("dt", { className: styles.key, children: key }),
2453
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("dd", { className: styles.value, children: render(value) })
2454
+ ] }, key)) }),
2455
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_react_components36.Text, { as: "p", className: styles.empty, children: "These facts were supplied by the app, not produced by the model." })
2456
+ ] });
2457
+ }
2458
+
2459
+ // src/grounding/ProviderAttribution.tsx
2460
+ var import_react_components37 = require("@fluentui/react-components");
2461
+ var import_react_icons11 = require("@fluentui/react-icons");
2462
+ var import_jsx_runtime38 = require("react/jsx-runtime");
2463
+ var useStyles29 = (0, import_react_components37.makeStyles)({
2464
+ root: {
2465
+ display: "flex",
2466
+ alignItems: "center",
2467
+ gap: import_react_components37.tokens.spacingHorizontalXXS,
2468
+ fontSize: import_react_components37.tokens.fontSizeBase100,
2469
+ color: import_react_components37.tokens.colorNeutralForeground3
2470
+ },
2471
+ guarded: { color: import_react_components37.tokens.colorPaletteDarkOrangeForeground1 }
2472
+ });
2473
+ function ProviderAttribution({ provider, model, guarded }) {
2474
+ const styles = useStyles29();
2475
+ if (!provider && !model && !guarded) return null;
2476
+ const who = [provider, model].filter(Boolean).join(" \xB7 ");
2477
+ if (guarded) {
2478
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("span", { className: `${styles.root} ${styles.guarded}`, children: [
2479
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_react_icons11.ShieldErrorRegular, { "aria-hidden": "true" }),
2480
+ "Withheld and replaced with safe text",
2481
+ who ? ` \u2014 not ${who}'s words` : ""
2482
+ ] });
2483
+ }
2484
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("span", { className: styles.root, children: [
2485
+ "Answered by ",
2486
+ who || "the agent"
2487
+ ] });
2488
+ }
2489
+ function messageAttribution(message) {
2490
+ return { provider: message.provider, model: message.usage?.model, guarded: message.guarded };
2491
+ }
2492
+
2493
+ // src/actions/ActionCard.tsx
2494
+ var React20 = __toESM(require("react"), 1);
2495
+ var import_react_components38 = require("@fluentui/react-components");
2496
+ var import_jsx_runtime39 = require("react/jsx-runtime");
2497
+ var useStyles30 = (0, import_react_components38.makeStyles)({
2498
+ head: { display: "flex", alignItems: "center", gap: import_react_components38.tokens.spacingHorizontalXS },
2499
+ description: { color: import_react_components38.tokens.colorNeutralForeground2, fontSize: import_react_components38.tokens.fontSizeBase200 },
2500
+ footer: { display: "flex", gap: import_react_components38.tokens.spacingHorizontalS, justifyContent: "flex-end", marginTop: import_react_components38.tokens.spacingVerticalXS }
2501
+ });
2502
+ function ActionCard({ title, description, icon, pending, children, footer }) {
2503
+ const styles = useStyles30();
2504
+ return /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(CardSurface, { emphasis: pending ? "attention" : "none", children: [
2505
+ /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("div", { className: styles.head, children: [
2506
+ icon ? React20.cloneElement(icon, { "aria-hidden": "true" }) : null,
2507
+ /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_react_components38.Text, { weight: "semibold", children: title })
2508
+ ] }),
2509
+ description ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("span", { className: styles.description, children: description }) : null,
2510
+ children,
2511
+ footer ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("div", { className: styles.footer, children: footer }) : null
2512
+ ] });
2513
+ }
2514
+
2515
+ // src/actions/ApprovalCard.tsx
2516
+ var React22 = __toESM(require("react"), 1);
2517
+ var import_react_components41 = require("@fluentui/react-components");
2518
+ var import_react_icons12 = require("@fluentui/react-icons");
2519
+
2520
+ // src/actions/ParameterReview.tsx
2521
+ var React21 = __toESM(require("react"), 1);
2522
+ var import_react_components39 = require("@fluentui/react-components");
2523
+ var import_jsx_runtime40 = require("react/jsx-runtime");
2524
+ var useStyles31 = (0, import_react_components39.makeStyles)({
2525
+ grid: {
2526
+ display: "grid",
2527
+ gridTemplateColumns: "minmax(110px, max-content) 1fr",
2528
+ gap: `${import_react_components39.tokens.spacingVerticalXS} ${import_react_components39.tokens.spacingHorizontalM}`,
2529
+ alignItems: "center",
2530
+ margin: 0
2531
+ },
2532
+ key: { margin: 0, color: import_react_components39.tokens.colorNeutralForeground2, fontSize: import_react_components39.tokens.fontSizeBase200 },
2533
+ value: { margin: 0 },
2534
+ raw: {
2535
+ whiteSpace: "pre-wrap",
2536
+ wordBreak: "break-all",
2537
+ fontFamily: import_react_components39.tokens.fontFamilyMonospace,
2538
+ fontSize: import_react_components39.tokens.fontSizeBase200,
2539
+ color: import_react_components39.tokens.colorNeutralForeground2
2540
+ }
2541
+ });
2542
+ function ParameterReview({ argumentsJson, readOnly = true, onChange }) {
2543
+ const styles = useStyles31();
2544
+ const parsed = React21.useMemo(() => {
2545
+ try {
2546
+ const value = JSON.parse(argumentsJson);
2547
+ return value && typeof value === "object" && !Array.isArray(value) ? value : null;
2548
+ } catch {
2549
+ return null;
2550
+ }
2551
+ }, [argumentsJson]);
2552
+ if (!parsed) {
2553
+ return /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { children: [
2554
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_react_components39.Text, { size: 200, children: "Arguments (unparsed)" }),
2555
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("pre", { className: styles.raw, children: argumentsJson || "(empty)" })
2556
+ ] });
2557
+ }
2558
+ const set = (key, next) => {
2559
+ if (!onChange) return;
2560
+ onChange(JSON.stringify({ ...parsed, [key]: next }));
2561
+ };
2562
+ return /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("dl", { className: styles.grid, children: Object.entries(parsed).map(([key, value]) => /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(React21.Fragment, { children: [
2563
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("dt", { className: styles.key, children: key }),
2564
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("dd", { className: styles.value, children: readOnly ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("span", { className: styles.raw, children: typeof value === "string" ? value : JSON.stringify(value) }) : /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
2565
+ import_react_components39.Input,
2566
+ {
2567
+ size: "small",
2568
+ "aria-label": key,
2569
+ value: typeof value === "string" ? value : JSON.stringify(value),
2570
+ onChange: (_, data) => set(key, data.value)
2571
+ }
2572
+ ) })
2573
+ ] }, key)) });
2574
+ }
2575
+
2576
+ // src/actions/ActionResult.tsx
2577
+ var import_react_components40 = require("@fluentui/react-components");
2578
+ var import_jsx_runtime41 = require("react/jsx-runtime");
2579
+ var INTENT2 = { succeeded: "success", failed: "error", denied: "warning" };
2580
+ var TITLE = { succeeded: "Done", failed: "Failed", denied: "Not approved" };
2581
+ var useStyles32 = (0, import_react_components40.makeStyles)({
2582
+ id: { display: "block", marginTop: import_react_components40.tokens.spacingVerticalXXS, fontSize: import_react_components40.tokens.fontSizeBase100, color: import_react_components40.tokens.colorNeutralForeground3, fontFamily: import_react_components40.tokens.fontFamilyMonospace }
2583
+ });
2584
+ function ActionResult({ status, message, correlationId, onRetry }) {
2585
+ const styles = useStyles32();
2586
+ return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(import_react_components40.MessageBar, { intent: INTENT2[status], children: [
2587
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(import_react_components40.MessageBarBody, { children: [
2588
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_react_components40.MessageBarTitle, { children: TITLE[status] }),
2589
+ message,
2590
+ correlationId ? /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("span", { className: styles.id, children: [
2591
+ "Correlation id: ",
2592
+ correlationId
2593
+ ] }) : null
2594
+ ] }),
2595
+ status === "failed" && onRetry ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_react_components40.MessageBarActions, { children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_react_components40.Button, { size: "small", onClick: onRetry, children: "Try again" }) }) : null
2596
+ ] });
2597
+ }
2598
+
2599
+ // src/actions/ApprovalCard.tsx
2600
+ var import_jsx_runtime42 = require("react/jsx-runtime");
2601
+ var useStyles33 = (0, import_react_components41.makeStyles)({
2602
+ busy: { display: "flex", alignItems: "center", gap: import_react_components41.tokens.spacingHorizontalXS, fontSize: import_react_components41.tokens.fontSizeBase200 }
2603
+ });
2604
+ function ApprovalCard({
2605
+ toolCall,
2606
+ onApprove,
2607
+ onDeny,
2608
+ allowEdit = false,
2609
+ busy = false
2610
+ }) {
2611
+ const styles = useStyles33();
2612
+ const [args, setArgs] = React22.useState(toolCall.argumentsJson);
2613
+ const [editing, setEditing] = React22.useState(false);
2614
+ const [decided, setDecided] = React22.useState(false);
2615
+ const settled = toolCall.status === "succeeded" || toolCall.status === "failed" || toolCall.status === "denied";
2616
+ if (settled) {
2617
+ return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(ActionCard, { title: toolCall.name, description: "Tool call", children: [
2618
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(ParameterReview, { argumentsJson: toolCall.argumentsJson }),
2619
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
2620
+ ActionResult,
2621
+ {
2622
+ status: toolCall.status === "succeeded" ? "succeeded" : toolCall.status === "denied" ? "denied" : "failed",
2623
+ message: toolCall.resultText ?? toolCall.failure?.message ?? "Completed.",
2624
+ correlationId: toolCall.failure?.correlationId
2625
+ }
2626
+ )
2627
+ ] });
2628
+ }
2629
+ const locked = decided || busy || toolCall.status === "running";
2630
+ const approve = () => {
2631
+ if (locked) return;
2632
+ setDecided(true);
2633
+ onApprove(toolCall, args);
2634
+ };
2635
+ const deny = () => {
2636
+ if (locked) return;
2637
+ setDecided(true);
2638
+ onDeny(toolCall);
2639
+ };
2640
+ return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
2641
+ ActionCard,
2642
+ {
2643
+ title: toolCall.name,
2644
+ description: "The agent is asking to run this. Check the details before approving.",
2645
+ pending: !locked,
2646
+ footer: locked ? /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("span", { className: styles.busy, children: [
2647
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_react_components41.Spinner, { size: "tiny" }),
2648
+ " Submitting\u2026"
2649
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(import_jsx_runtime42.Fragment, { children: [
2650
+ allowEdit ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_react_components41.Button, { size: "small", icon: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_react_icons12.EditRegular, {}), onClick: () => setEditing((v) => !v), children: editing ? "Done editing" : "Edit" }) : null,
2651
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_react_components41.Button, { size: "small", icon: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_react_icons12.DismissRegular, {}), onClick: deny, disabled: locked, children: "Deny" }),
2652
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_react_components41.Button, { size: "small", appearance: "primary", icon: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_react_icons12.CheckmarkRegular, {}), onClick: approve, disabled: locked, children: "Approve" })
2653
+ ] }),
2654
+ children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(ParameterReview, { argumentsJson: args, readOnly: !editing, onChange: setArgs })
2655
+ }
2656
+ );
2657
+ }
2658
+
2659
+ // src/actions/ToolCallCard.tsx
2660
+ var React23 = __toESM(require("react"), 1);
2661
+ var import_react_components42 = require("@fluentui/react-components");
2662
+ var import_jsx_runtime43 = require("react/jsx-runtime");
2663
+ var LABEL = {
2664
+ proposed: "Proposed",
2665
+ running: "Running",
2666
+ succeeded: "Succeeded",
2667
+ failed: "Failed",
2668
+ denied: "Denied"
2669
+ };
2670
+ var COLOUR = {
2671
+ proposed: "informative",
2672
+ running: "informative",
2673
+ succeeded: "success",
2674
+ failed: "danger",
2675
+ denied: "warning"
2676
+ };
2677
+ var useStyles34 = (0, import_react_components42.makeStyles)({
2678
+ head: { display: "flex", alignItems: "center", gap: import_react_components42.tokens.spacingHorizontalXS },
2679
+ name: { fontFamily: import_react_components42.tokens.fontFamilyMonospace, fontSize: import_react_components42.tokens.fontSizeBase200 },
2680
+ result: {
2681
+ whiteSpace: "pre-wrap",
2682
+ wordBreak: "break-all",
2683
+ fontFamily: import_react_components42.tokens.fontFamilyMonospace,
2684
+ fontSize: import_react_components42.tokens.fontSizeBase200,
2685
+ marginTop: import_react_components42.tokens.spacingVerticalXS
2686
+ }
2687
+ });
2688
+ function ToolCallCard({ toolCall, defaultExpanded = false }) {
2689
+ const styles = useStyles34();
2690
+ const id = React23.useId();
2691
+ return /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(CardSurface, { children: /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(
2692
+ Disclosure,
2693
+ {
2694
+ id,
2695
+ defaultOpen: defaultExpanded,
2696
+ summary: /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("span", { className: styles.head, children: [
2697
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("span", { className: styles.name, children: toolCall.name }),
2698
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_react_components42.Badge, { appearance: "tint", color: COLOUR[toolCall.status], children: LABEL[toolCall.status] }),
2699
+ toolCall.status === "running" ? /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_react_components42.Spinner, { size: "extra-tiny" }) : null
2700
+ ] }),
2701
+ children: [
2702
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(ParameterReview, { argumentsJson: toolCall.argumentsJson }),
2703
+ toolCall.resultText ? /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("div", { className: styles.result, children: toolCall.resultText }) : null,
2704
+ toolCall.failure ? /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("div", { className: styles.result, children: toolCall.failure.message }) : null
2705
+ ]
2706
+ }
2707
+ ) });
2708
+ }
2709
+
2710
+ // src/host/AgentKitHostProvider.tsx
2711
+ var React24 = __toESM(require("react"), 1);
2712
+ var import_react_components43 = require("@fluentui/react-components");
2713
+
2714
+ // src/theme/index.ts
2715
+ var import_fluent_theme = require("@dataverse-kit/fluent-theme");
2716
+ var import_design_tokens = require("@dataverse-kit/design-tokens");
2717
+ function applyAgentOverrides(base) {
2718
+ return {
2719
+ ...base,
2720
+ // Fluent's web theme is close to CE but does not pin the Web variant of Segoe.
2721
+ fontFamilyBase: import_design_tokens.typography.fontFamily,
2722
+ // CE is squarer than Fluent web's default. Bubbles opt back up to borderRadiusLarge.
2723
+ borderRadiusMedium: import_design_tokens.radius.card
2724
+ };
2725
+ }
2726
+ function createAgentTheme(brand = import_fluent_theme.dynamicsBrandRamp) {
2727
+ return applyAgentOverrides((0, import_fluent_theme.createDynamicsV9Theme)(brand));
2728
+ }
2729
+ function createAgentDarkTheme(brand = import_fluent_theme.dynamicsBrandRamp) {
2730
+ return applyAgentOverrides((0, import_fluent_theme.createDynamicsV9DarkTheme)(brand));
2731
+ }
2732
+ var agentTheme = createAgentTheme();
2733
+ var agentDarkTheme = createAgentDarkTheme();
2734
+ function resolveHostTheme(input = {}) {
2735
+ if (input.theme) return input.theme;
2736
+ if (input.hostTheme) return input.hostTheme;
2737
+ return input.isDarkTheme ? agentDarkTheme : agentTheme;
2738
+ }
2739
+
2740
+ // src/host/idPrefix.ts
2741
+ var nextAgentIdPrefix = createIdPrefixCounter("dvkak");
2742
+
2743
+ // src/host/AgentKitHostProvider.tsx
2744
+ var import_jsx_runtime44 = require("react/jsx-runtime");
2745
+ function AgentKitHostProvider(props) {
2746
+ const { children, theme, hostTheme, isDarkTheme, targetDocument, className, style } = props;
2747
+ const [generated] = React24.useState(nextAgentIdPrefix);
2748
+ const idPrefix = props.idPrefix ?? generated;
2749
+ const resolved = React24.useMemo(
2750
+ () => resolveHostTheme({ theme, hostTheme, isDarkTheme }),
2751
+ [theme, hostTheme, isDarkTheme]
2752
+ );
2753
+ return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_react_components43.IdPrefixProvider, { value: idPrefix, children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
2754
+ import_react_components43.FluentProvider,
2755
+ {
2756
+ theme: resolved,
2757
+ targetDocument,
2758
+ className,
2759
+ style,
2760
+ children
2761
+ }
2762
+ ) });
2763
+ }
2764
+
2765
+ // src/services/scenarios.ts
2766
+ var SOURCES = [
2767
+ { id: "src-acct", title: "Contoso Ltd", kind: "dataverse", entityLogicalName: "account", recordId: "11111111-1111-1111-1111-111111111111", snippet: "Annual revenue \xA34.2m, 38 employees." },
2768
+ { id: "src-case", title: "Case 4417 \u2014 invoice mismatch", kind: "dataverse", entityLogicalName: "incident", recordId: "22222222-2222-2222-2222-222222222222", snippet: "Raised 12 Mar, priority High." },
2769
+ { id: "src-kb", title: "KB-208: credit note policy", kind: "knowledge", url: "https://example.invalid/kb/208", snippet: "Credit notes above \xA35,000 require finance approval.", score: 0.82 },
2770
+ { id: "src-doc", title: "Statement-Mar.pdf", kind: "document", snippet: "Page 3 lists the disputed line." },
2771
+ { id: "src-web", title: "Companies House filing", kind: "web", url: "https://example.invalid/ch" }
2772
+ ];
2773
+ var mockScenarios = {
2774
+ happy: {
2775
+ text: "Contoso has three open cases. The oldest, case 4417, has been waiting eleven days for a credit note.",
2776
+ status: "succeeded",
2777
+ suggestions: ["Draft a credit note", "Show all three cases", "Who owns case 4417?"]
2778
+ },
2779
+ // ★ FIVE sources, but the text cites only [3] and [1] — and out of order. The panel must
2780
+ // show TWO, renumbered 1 and 2 in first-appearance order. A fixture where every source
2781
+ // is cited cannot tell a correct implementation from one that lists everything it was given.
2782
+ grounded: {
2783
+ text: "Credit notes over \xA35,000 need finance approval [3], and case 4417 is for \xA36,200 [1]. So this one needs sign-off.",
2784
+ status: "succeeded",
2785
+ sources: SOURCES
2786
+ },
2787
+ toolApproval: {
2788
+ text: "I can raise the credit note for you \u2014 approve the details below and I will submit it.",
2789
+ status: "succeeded",
2790
+ toolCall: {
2791
+ id: "call-1",
2792
+ name: "create_credit_note",
2793
+ argumentsJson: '{"accountId":"11111111-1111-1111-1111-111111111111","amount":6200,"currency":"GBP","reason":"Invoice mismatch, case 4417"}',
2794
+ status: "proposed",
2795
+ requiresApproval: true
2796
+ }
2797
+ },
2798
+ toolAuto: {
2799
+ text: "I checked the balance for you.",
2800
+ status: "succeeded",
2801
+ toolCall: { id: "call-2", name: "get_account_balance", argumentsJson: '{"accountId":"1111"}', status: "running" },
2802
+ autoToolResult: '{"balance":6200,"currency":"GBP"}'
2803
+ },
2804
+ // ★ blocked and refused MUST look different on screen: a content filter fired, versus the
2805
+ // model declining. They need different fixes, so they are different states.
2806
+ blocked: {
2807
+ text: "I can\u2019t help with that request.",
2808
+ status: "blocked",
2809
+ guarded: true,
2810
+ failureMessage: "The response was withheld by the content filter.",
2811
+ failureCode: "content_filter",
2812
+ recoverable: false
2813
+ },
2814
+ refused: {
2815
+ text: "I don\u2019t have enough information to answer that safely. Could you say which account you mean?",
2816
+ status: "refused",
2817
+ failureMessage: "The agent declined to answer.",
2818
+ recoverable: true
2819
+ },
2820
+ error: {
2821
+ text: "",
2822
+ status: "error",
2823
+ failureMessage: "The agent is rate limited. Try again shortly.",
2824
+ failureCode: "rate_limited",
2825
+ recoverable: true,
2826
+ retryAfterMs: 4e3
2827
+ },
2828
+ notProvisioned: {
2829
+ text: "",
2830
+ status: "notProvisioned",
2831
+ failureMessage: "No Foundry project is configured for this environment.",
2832
+ failureCode: "not_provisioned",
2833
+ recoverable: false,
2834
+ unavailable: true
2835
+ },
2836
+ cancelled: {
2837
+ text: "Let me pull the last six months of activity for Contoso and work through it case by case, starting with the oldest\u2026",
2838
+ status: "cancelled",
2839
+ failureMessage: "Stopped."
2840
+ },
2841
+ // The fixture the autoscroll and live-region gates run against: enough deltas that
2842
+ // "once, not per token" is a meaningful distinction.
2843
+ longAnswer: {
2844
+ text: Array.from({ length: 200 }, (_, i) => `word${i + 1}`).join(" "),
2845
+ status: "succeeded"
2846
+ },
2847
+ throws: {
2848
+ text: "partial\u2026",
2849
+ status: "error",
2850
+ throwsMidStream: true,
2851
+ failureMessage: "never reached \u2014 the iterator throws instead"
2852
+ }
2853
+ };
2854
+
2855
+ // src/services/MockAgentService.ts
2856
+ var realScheduler = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
2857
+ var immediateScheduler = () => Promise.resolve();
2858
+ function mulberry32(seed) {
2859
+ let a = seed >>> 0;
2860
+ return () => {
2861
+ a = a + 1831565813 >>> 0;
2862
+ let t = Math.imul(a ^ a >>> 15, 1 | a);
2863
+ t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
2864
+ return ((t ^ t >>> 14) >>> 0) / 4294967296;
2865
+ };
2866
+ }
2867
+ var MockAgentService = class {
2868
+ constructor(options = {}) {
2869
+ this.name = "mock";
2870
+ this.scenarioName = options.scenario ?? "happy";
2871
+ this.scenario = mockScenarios[this.scenarioName];
2872
+ this.scheduler = options.scheduler ?? realScheduler;
2873
+ this.firstTokenMs = options.firstTokenMs ?? 400;
2874
+ this.chunkMs = options.chunkMs ?? 28;
2875
+ this.rand = mulberry32(options.seed ?? 1);
2876
+ this.availabilityOverride = options.availability;
2877
+ }
2878
+ async isAvailable() {
2879
+ if (this.availabilityOverride) return this.availabilityOverride;
2880
+ if (this.scenario.unavailable) {
2881
+ return {
2882
+ available: false,
2883
+ status: "notProvisioned",
2884
+ reason: this.scenario.failureMessage ?? "Not provisioned."
2885
+ };
2886
+ }
2887
+ return {
2888
+ available: true,
2889
+ capabilities: { streaming: true, tools: true, citations: true, attachments: true, cancel: true }
2890
+ };
2891
+ }
2892
+ /**
2893
+ * A prompt can select a scenario (`/blocked`, `/tool`, `/error`), so one story can walk
2894
+ * every state without remounting.
2895
+ */
2896
+ resolveScenario(promptText) {
2897
+ const slash = promptText.trim().match(/^\/(\w+)/)?.[1];
2898
+ if (slash && slash in mockScenarios) {
2899
+ return { name: slash, scenario: mockScenarios[slash] };
2900
+ }
2901
+ return { name: this.scenarioName, scenario: this.scenario };
2902
+ }
2903
+ async *send(turn) {
2904
+ const { scenario } = this.resolveScenario(turn.text);
2905
+ const messageId = `mock-${Math.floor(this.rand() * 1e9).toString(36)}`;
2906
+ const turnId = `turn-${messageId}`;
2907
+ const provider = this.name;
2908
+ const finish = (result) => ({
2909
+ type: "turn_end",
2910
+ turnId,
2911
+ messageId,
2912
+ result: { ...result, provider }
2913
+ });
2914
+ yield { type: "turn_start", turnId, messageId, provider, model: "mock-1" };
2915
+ if (turn.cancelSignal?.aborted) {
2916
+ yield finish({ status: "cancelled", text: "", failure: { status: "cancelled", message: "Stopped." } });
2917
+ return;
2918
+ }
2919
+ if (scenario.status === "notProvisioned" || scenario.status === "error" && !scenario.text) {
2920
+ await this.scheduler(this.firstTokenMs);
2921
+ yield finish({
2922
+ status: scenario.status,
2923
+ text: "",
2924
+ failure: {
2925
+ status: scenario.status,
2926
+ message: scenario.failureMessage ?? "Failed.",
2927
+ code: scenario.failureCode,
2928
+ recoverable: scenario.recoverable,
2929
+ retryAfterMs: scenario.retryAfterMs
2930
+ }
2931
+ });
2932
+ return;
2933
+ }
2934
+ if (scenario.sources) {
2935
+ yield { type: "status", messageId, label: "Searching Dataverse\u2026" };
2936
+ await this.scheduler(this.firstTokenMs);
2937
+ yield { type: "sources", messageId, sources: scenario.sources };
2938
+ }
2939
+ if (scenario.thinking) {
2940
+ yield { type: "thinking_delta", messageId, text: scenario.thinking };
2941
+ }
2942
+ if (scenario.toolCall) {
2943
+ yield { type: "tool_call", messageId, call: scenario.toolCall };
2944
+ if (scenario.autoToolResult !== void 0) {
2945
+ await this.scheduler(this.chunkMs);
2946
+ yield {
2947
+ type: "tool_result",
2948
+ messageId,
2949
+ callId: scenario.toolCall.id,
2950
+ status: "succeeded",
2951
+ resultText: scenario.autoToolResult
2952
+ };
2953
+ }
2954
+ }
2955
+ const chunks = scenario.chunks ?? scenario.text.split(/(?<=\s)/);
2956
+ await this.scheduler(this.firstTokenMs);
2957
+ let emitted = "";
2958
+ for (const [i, chunk] of chunks.entries()) {
2959
+ if (turn.cancelSignal?.aborted) {
2960
+ yield finish({
2961
+ status: "cancelled",
2962
+ text: emitted,
2963
+ failure: { status: "cancelled", message: "Stopped." }
2964
+ });
2965
+ return;
2966
+ }
2967
+ if (scenario.throwsMidStream && i === Math.floor(chunks.length / 2)) {
2968
+ throw new Error("mock transport exploded mid-stream");
2969
+ }
2970
+ emitted += chunk;
2971
+ yield { type: "text_delta", messageId, text: chunk };
2972
+ await this.scheduler(this.chunkMs);
2973
+ }
2974
+ if (scenario.suggestions) {
2975
+ yield { type: "suggestions", messageId, prompts: scenario.suggestions };
2976
+ }
2977
+ const failed = scenario.status !== "succeeded";
2978
+ yield finish({
2979
+ status: scenario.status,
2980
+ text: emitted,
2981
+ guarded: scenario.guarded,
2982
+ usage: {
2983
+ inputTokens: 128,
2984
+ outputTokens: chunks.length,
2985
+ costUsd: Number((chunks.length * 2e-6).toFixed(6)),
2986
+ durationMs: this.firstTokenMs + chunks.length * this.chunkMs,
2987
+ model: "mock-1"
2988
+ },
2989
+ failure: failed ? {
2990
+ status: scenario.status,
2991
+ message: scenario.failureMessage ?? "Failed.",
2992
+ code: scenario.failureCode,
2993
+ recoverable: scenario.recoverable,
2994
+ retryAfterMs: scenario.retryAfterMs
2995
+ } : void 0
2996
+ });
2997
+ }
2998
+ };
2999
+
3000
+ // src/services/NullAgentService.ts
3001
+ var NullAgentService = class {
3002
+ constructor() {
3003
+ this.name = "none";
3004
+ }
3005
+ async isAvailable() {
3006
+ return {
3007
+ available: false,
3008
+ status: "notProvisioned",
3009
+ reason: "No agent provider is configured for this host."
3010
+ };
3011
+ }
3012
+ async *send(turn) {
3013
+ const messageId = `null-${Date.now()}`;
3014
+ const turnId = `turn-${messageId}`;
3015
+ void turn;
3016
+ yield { type: "turn_start", turnId, messageId, provider: this.name };
3017
+ yield {
3018
+ type: "turn_end",
3019
+ turnId,
3020
+ messageId,
3021
+ result: {
3022
+ status: "notProvisioned",
3023
+ text: "",
3024
+ provider: this.name,
3025
+ failure: {
3026
+ status: "notProvisioned",
3027
+ message: "No agent provider is configured for this host.",
3028
+ code: "not_provisioned",
3029
+ recoverable: false
3030
+ }
3031
+ }
3032
+ };
3033
+ }
3034
+ };
3035
+
3036
+ // src/services/createAgentService.ts
3037
+ function decide(config) {
3038
+ switch (config.provider) {
3039
+ case "custom":
3040
+ return config.factory ? { provider: config.factory(), reason: "explicit factory (provider: custom)" } : { provider: new NullAgentService(), reason: "provider: custom but no factory supplied" };
3041
+ case "mock":
3042
+ return { provider: new MockAgentService(), reason: "provider: mock" };
3043
+ case "none":
3044
+ return { provider: new NullAgentService(), reason: "provider: none (the default)" };
3045
+ default:
3046
+ return {
3047
+ provider: new NullAgentService(),
3048
+ reason: `unrecognised provider ${String(config.provider)} \u2014 falling back to none`
3049
+ };
3050
+ }
3051
+ }
3052
+ function createAgentService(config = { provider: "none" }) {
3053
+ return decide(config).provider;
3054
+ }
3055
+ // Annotate the CommonJS export names for ESM import in node:
3056
+ 0 && (module.exports = {
3057
+ ANNOUNCE_FULL_TEXT_MAX_CHARS,
3058
+ ActionCard,
3059
+ ActionResult,
3060
+ AgentAppShell,
3061
+ AgentAvatar,
3062
+ AgentKitHostProvider,
3063
+ AgentOpsPanel,
3064
+ AgentPicker,
3065
+ AgentStatusBadge,
3066
+ AnswerWithCitations,
3067
+ AppRail,
3068
+ ApprovalCard,
3069
+ AvailabilityBanner,
3070
+ CitationChip,
3071
+ CitationList,
3072
+ Composer,
3073
+ ConfirmInline,
3074
+ ConversationHeader,
3075
+ ConversationList,
3076
+ ConversationListItem,
3077
+ DEFAULT_GROUP_WINDOW_MS,
3078
+ DayDivider,
3079
+ EmptyConversation,
3080
+ GlobalSearchBar,
3081
+ GroundingPanel,
3082
+ LiveAnnouncer,
3083
+ MessageActions,
3084
+ MessageBubble,
3085
+ MessageList,
3086
+ MockAgentService,
3087
+ ModelBadge,
3088
+ NO_ANNOUNCEMENT,
3089
+ NullAgentService,
3090
+ ParameterReview,
3091
+ ProviderAttribution,
3092
+ RightPane,
3093
+ RunHistoryPanel,
3094
+ RunTimeline,
3095
+ STICK_TO_BOTTOM_THRESHOLD_PX,
3096
+ SourceCard,
3097
+ StreamingText,
3098
+ SuggestedPrompts,
3099
+ SystemNotice,
3100
+ ThrottleNotice,
3101
+ ToolCallCard,
3102
+ TypingIndicator,
3103
+ UsageMeter,
3104
+ agentDarkTheme,
3105
+ agentTheme,
3106
+ appendUserMessage,
3107
+ applyAgentEvent,
3108
+ bucketByDay,
3109
+ classifyTransportFailure,
3110
+ conversationAccessibleName,
3111
+ createAgentDarkTheme,
3112
+ createAgentService,
3113
+ createAgentTheme,
3114
+ createIdPrefixCounter,
3115
+ dayKeyOf,
3116
+ decide,
3117
+ deriveAnnouncement,
3118
+ extractCitationMarkers,
3119
+ formatDayLabel,
3120
+ fromClaudeStreamEvent,
3121
+ fromFoundryChatResult,
3122
+ fromOpenAiChatChunk,
3123
+ fromSseLine,
3124
+ groupMessages,
3125
+ immediateScheduler,
3126
+ initialConversation,
3127
+ isSameAuthorRun,
3128
+ isTerminalStatus,
3129
+ messageAttribution,
3130
+ mockScenarios,
3131
+ nextAgentIdPrefix,
3132
+ realScheduler,
3133
+ renumberCitations,
3134
+ resolveHostTheme,
3135
+ runStatusLabel,
3136
+ runStatusSeverity,
3137
+ shouldStickToBottom,
3138
+ splitTextByCitations,
3139
+ useConversation,
3140
+ useFocusRestore,
3141
+ useRovingTabindex,
3142
+ useStickyScroll,
3143
+ useVisuallyHiddenStyles
3144
+ });
3145
+ //# sourceMappingURL=index.cjs.map