@opengeni/react 3.8.0-canary.1 → 4.0.1-canary.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -24
- package/dist/artifacts.js +7 -7
- package/dist/{browser-viewer-SS46LUAC.js → browser-viewer-NTD6UIPR.js} +3 -3
- package/dist/{chunk-VEPNNDNG.js → chunk-24M4YBCL.js} +4 -4
- package/dist/{chunk-ELIYWBZR.js → chunk-BV5A4OTE.js} +4 -4
- package/dist/{chunk-7W6PVVTK.js → chunk-HO6X4FHG.js} +2559 -1037
- package/dist/chunk-HO6X4FHG.js.map +1 -0
- package/dist/{chunk-BXJOQFCD.js → chunk-HUMIRRYI.js} +84 -221
- package/dist/chunk-HUMIRRYI.js.map +1 -0
- package/dist/{chunk-7BWFU6H4.js → chunk-Z7WBDB4E.js} +395 -287
- package/dist/chunk-Z7WBDB4E.js.map +1 -0
- package/dist/components/human-input-form.d.ts +3 -1
- package/dist/components/human-input-surface.d.ts +3 -2
- package/dist/components/session-conversation.d.ts +2 -0
- package/dist/composer.js +2 -3
- package/dist/{computer-viewer-FWEZISW4.js → computer-viewer-7TEZC6Y2.js} +3 -3
- package/dist/hooks/use-codex-accounts.d.ts +11 -1
- package/dist/hooks/use-session-events.d.ts +2 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +44 -34
- package/dist/index.js.map +1 -1
- package/dist/interaction.js +8 -8
- package/dist/older-history.d.ts +3 -2
- package/dist/{platform-activity-row-BJHUHXAT.js → platform-activity-row-27LKAWZD.js} +2 -2
- package/dist/platform-activity-row-27LKAWZD.js.map +1 -0
- package/dist/realtime.js +3 -3
- package/dist/session-ui.js +6 -9
- package/dist/session.js +1 -1
- package/dist/timeline/types.d.ts +3 -0
- package/package.json +2 -6
- package/src/components/human-input-form.tsx +185 -53
- package/src/components/human-input-surface.tsx +3 -0
- package/src/components/message-timeline.tsx +41 -6
- package/src/components/session-commands-panel.tsx +7 -1
- package/src/components/session-conversation.tsx +4 -1
- package/src/hooks/use-codex-accounts.ts +32 -5
- package/src/hooks/use-session-events.ts +16 -277
- package/src/index.ts +0 -1
- package/src/older-history.ts +22 -4
- package/src/timeline/platform-activity-row.tsx +4 -1
- package/src/timeline/projection.ts +99 -9
- package/src/timeline/types.ts +3 -0
- package/dist/chat.d.ts +0 -37
- package/dist/chat.js +0 -315
- package/dist/chat.js.map +0 -1
- package/dist/chunk-7BWFU6H4.js.map +0 -1
- package/dist/chunk-7W6PVVTK.js.map +0 -1
- package/dist/chunk-BXJOQFCD.js.map +0 -1
- package/dist/chunk-HQV2I5HV.js +0 -124
- package/dist/chunk-HQV2I5HV.js.map +0 -1
- package/dist/chunk-YAIPFO5D.js +0 -1416
- package/dist/chunk-YAIPFO5D.js.map +0 -1
- package/dist/platform-activity-row-BJHUHXAT.js.map +0 -1
- package/src/chat.tsx +0 -393
- /package/dist/{browser-viewer-SS46LUAC.js.map → browser-viewer-NTD6UIPR.js.map} +0 -0
- /package/dist/{chunk-VEPNNDNG.js.map → chunk-24M4YBCL.js.map} +0 -0
- /package/dist/{chunk-ELIYWBZR.js.map → chunk-BV5A4OTE.js.map} +0 -0
- /package/dist/{computer-viewer-FWEZISW4.js.map → computer-viewer-7TEZC6Y2.js.map} +0 -0
|
@@ -135,6 +135,7 @@ export function buildTimeline(events: SessionEvent[]): TimelineItem[] {
|
|
|
135
135
|
const ordered = orderTimelineEvents(events, prescan);
|
|
136
136
|
const pendingWaitOutcomeByTurn = new Map<string | null, PendingWaitOutcome>();
|
|
137
137
|
const latestAgentResponseByTurn = new Map<string | null, TrackedAgentResponse>();
|
|
138
|
+
const identifiedMessages = new Map<string, AgentMessageItem>();
|
|
138
139
|
const humanInputRequests = humanInputRequestsById(events);
|
|
139
140
|
const humanInputToolCallIds = new Set(
|
|
140
141
|
[...humanInputRequests.values()]
|
|
@@ -157,6 +158,7 @@ export function buildTimeline(events: SessionEvent[]): TimelineItem[] {
|
|
|
157
158
|
mode = 0,
|
|
158
159
|
explicitStartedAt?: string,
|
|
159
160
|
explicitId?: string,
|
|
161
|
+
blockedReason?: StartupPhaseItem["blockedReason"],
|
|
160
162
|
): void => {
|
|
161
163
|
const key = `${startupTurnId}:${phase}`;
|
|
162
164
|
const priorState = startupPhases.get(key);
|
|
@@ -183,6 +185,7 @@ export function buildTimeline(events: SessionEvent[]): TimelineItem[] {
|
|
|
183
185
|
? elapsedDurationMs(open.startedAt, startupEvent.occurredAt)
|
|
184
186
|
: Math.max(0, duration);
|
|
185
187
|
open.outcome = outcome ?? open.outcome;
|
|
188
|
+
open.blockedReason = blockedReason;
|
|
186
189
|
return;
|
|
187
190
|
}
|
|
188
191
|
const running = status === "running";
|
|
@@ -201,6 +204,7 @@ export function buildTimeline(events: SessionEvent[]): TimelineItem[] {
|
|
|
201
204
|
completedAt: running ? null : startupEvent.occurredAt,
|
|
202
205
|
durationMs,
|
|
203
206
|
outcome,
|
|
207
|
+
blockedReason,
|
|
204
208
|
occurredAt: startedAt,
|
|
205
209
|
} satisfies Partial<StartupPhaseItem>);
|
|
206
210
|
startupPhases.set(key, [prior, startupAttemptId, startupRecoveryRevision]);
|
|
@@ -216,6 +220,7 @@ export function buildTimeline(events: SessionEvent[]): TimelineItem[] {
|
|
|
216
220
|
completedAt: running ? null : startupEvent.occurredAt,
|
|
217
221
|
durationMs,
|
|
218
222
|
outcome,
|
|
223
|
+
blockedReason,
|
|
219
224
|
occurredAt: startedAt,
|
|
220
225
|
};
|
|
221
226
|
items.push(item);
|
|
@@ -408,8 +413,43 @@ export function buildTimeline(events: SessionEvent[]): TimelineItem[] {
|
|
|
408
413
|
if (!text) {
|
|
409
414
|
break;
|
|
410
415
|
}
|
|
416
|
+
const messageId = stringValue(payload.messageId);
|
|
417
|
+
const messageKey = messageId ? JSON.stringify([turnId, messageId]) : null;
|
|
418
|
+
const identified = messageKey ? identifiedMessages.get(messageKey) : undefined;
|
|
419
|
+
if (identified) {
|
|
420
|
+
if (identified.annotationSource?.eventType !== "agent.message.completed") {
|
|
421
|
+
identified.text += text;
|
|
422
|
+
rememberAgentResponse(turnId, identified, false);
|
|
423
|
+
}
|
|
424
|
+
break;
|
|
425
|
+
}
|
|
411
426
|
const open = last();
|
|
412
|
-
if (open?.kind === "
|
|
427
|
+
if (!messageKey && open?.kind === "tool-call" && open.status === "running") {
|
|
428
|
+
const previous = latestAgentResponseByTurn.get(turnId);
|
|
429
|
+
const previousIndex = previous ? items.indexOf(previous.item) : -1;
|
|
430
|
+
if (
|
|
431
|
+
previous &&
|
|
432
|
+
!previous.completed &&
|
|
433
|
+
previousIndex >= 0 &&
|
|
434
|
+
items
|
|
435
|
+
.slice(previousIndex + 1)
|
|
436
|
+
.every(
|
|
437
|
+
(item) =>
|
|
438
|
+
item.kind === "tool-call" && item.turnId === turnId && item.status === "running",
|
|
439
|
+
)
|
|
440
|
+
) {
|
|
441
|
+
// Legacy deltas have no provider message identity. Tool creation
|
|
442
|
+
// alone is not a text boundary; completed output still is.
|
|
443
|
+
previous.item.text += text;
|
|
444
|
+
break;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
if (
|
|
448
|
+
!messageKey &&
|
|
449
|
+
open?.kind === "agent-message" &&
|
|
450
|
+
open.streaming &&
|
|
451
|
+
open.turnId === turnId
|
|
452
|
+
) {
|
|
413
453
|
open.text += text;
|
|
414
454
|
rememberAgentResponse(turnId, open, false);
|
|
415
455
|
break;
|
|
@@ -424,18 +464,21 @@ export function buildTimeline(events: SessionEvent[]): TimelineItem[] {
|
|
|
424
464
|
occurredAt: event.occurredAt,
|
|
425
465
|
};
|
|
426
466
|
items.push(item);
|
|
467
|
+
if (messageKey) identifiedMessages.set(messageKey, item);
|
|
427
468
|
rememberAgentResponse(turnId, item, false);
|
|
428
469
|
break;
|
|
429
470
|
}
|
|
430
471
|
|
|
431
472
|
case "agent.message.completed": {
|
|
432
473
|
const text = stringValue(payload.text);
|
|
474
|
+
const messageId = stringValue(payload.messageId);
|
|
475
|
+
const messageKey = messageId ? JSON.stringify([turnId, messageId]) : null;
|
|
433
476
|
const phase = assistantMessagePhase(payload.phase);
|
|
434
477
|
// Reconcile the most recent same-turn agent message — even when
|
|
435
478
|
// activity (tool calls, reasoning) landed after its deltas — so the
|
|
436
479
|
// completed text never duplicates the streamed one.
|
|
437
|
-
let openIndex = -1;
|
|
438
|
-
for (let index = items.length - 1; index >= 0; index -= 1) {
|
|
480
|
+
let openIndex = messageKey ? items.indexOf(identifiedMessages.get(messageKey)!) : -1;
|
|
481
|
+
for (let index = messageKey ? -1 : items.length - 1; index >= 0; index -= 1) {
|
|
439
482
|
const candidate = items[index];
|
|
440
483
|
if (candidate?.kind === "agent-message" && candidate.turnId === turnId) {
|
|
441
484
|
openIndex = index;
|
|
@@ -447,10 +490,15 @@ export function buildTimeline(events: SessionEvent[]): TimelineItem[] {
|
|
|
447
490
|
candidate?.kind === "agent-message" ? candidate : undefined;
|
|
448
491
|
if (
|
|
449
492
|
open &&
|
|
450
|
-
(
|
|
493
|
+
(messageKey ||
|
|
494
|
+
open.streaming ||
|
|
495
|
+
!open.text ||
|
|
496
|
+
text === open.text ||
|
|
497
|
+
text.startsWith(open.text))
|
|
451
498
|
) {
|
|
452
|
-
//
|
|
453
|
-
|
|
499
|
+
// Identity makes the final text authoritative even if it corrects the
|
|
500
|
+
// draft; legacy receipts still require an extension match.
|
|
501
|
+
if (!open.text || (text && (messageKey || text.startsWith(open.text)))) {
|
|
454
502
|
open.text = text || open.text;
|
|
455
503
|
}
|
|
456
504
|
open.streaming = false;
|
|
@@ -502,6 +550,7 @@ export function buildTimeline(events: SessionEvent[]): TimelineItem[] {
|
|
|
502
550
|
},
|
|
503
551
|
};
|
|
504
552
|
items.push(item);
|
|
553
|
+
if (messageKey) identifiedMessages.set(messageKey, item);
|
|
505
554
|
rememberAgentResponse(turnId, item, true);
|
|
506
555
|
}
|
|
507
556
|
break;
|
|
@@ -693,7 +742,18 @@ export function buildTimeline(events: SessionEvent[]): TimelineItem[] {
|
|
|
693
742
|
const startupPhase = startupPhaseForSandboxOperation(name);
|
|
694
743
|
if (startupPhase) {
|
|
695
744
|
closeStreamingTail();
|
|
696
|
-
settleStartupPhase(
|
|
745
|
+
settleStartupPhase(
|
|
746
|
+
startupPhase,
|
|
747
|
+
status,
|
|
748
|
+
numberOrNull(payload.durationMs),
|
|
749
|
+
origin,
|
|
750
|
+
0,
|
|
751
|
+
undefined,
|
|
752
|
+
undefined,
|
|
753
|
+
status === "cancelled" && payload.failureCode === "rotation_in_progress"
|
|
754
|
+
? "rotation_in_progress"
|
|
755
|
+
: undefined,
|
|
756
|
+
);
|
|
697
757
|
break;
|
|
698
758
|
}
|
|
699
759
|
// Routine per-turn platform plumbing that runs before EVERY turn to
|
|
@@ -1039,11 +1099,17 @@ export function buildTimeline(events: SessionEvent[]): TimelineItem[] {
|
|
|
1039
1099
|
}
|
|
1040
1100
|
}
|
|
1041
1101
|
finalizeOpen(turnId, "complete", event.occurredAt);
|
|
1042
|
-
|
|
1102
|
+
const completedTurn = turnEndItem(event, "complete", null);
|
|
1103
|
+
items.push(completedTurn);
|
|
1043
1104
|
const hasCompletedFinalResponse =
|
|
1044
1105
|
latestAgentResponse?.completed === true &&
|
|
1045
1106
|
latestAgentResponse.item.phase !== "commentary" &&
|
|
1046
1107
|
Boolean(visibleTrackedResponse);
|
|
1108
|
+
if (!hasAuthoritativeFinalOutput && pendingWaitOutcome) {
|
|
1109
|
+
// Preserve existing prose, including delta-only streams, when a
|
|
1110
|
+
// trailing wait yields without a terminal output receipt.
|
|
1111
|
+
completedTurn.preserveWaitResponse = true;
|
|
1112
|
+
}
|
|
1047
1113
|
if (!hasAuthoritativeFinalOutput && !hasCompletedFinalResponse && pendingWaitOutcome) {
|
|
1048
1114
|
items.push({
|
|
1049
1115
|
kind: "notice",
|
|
@@ -1808,7 +1874,31 @@ function foldSettledTurn(groups: TimelineGroup[], turnEnd: TurnEndItem): void {
|
|
|
1808
1874
|
return;
|
|
1809
1875
|
}
|
|
1810
1876
|
|
|
1811
|
-
|
|
1877
|
+
// Resolve only inside this turn's collected suffix. A user/machine-input
|
|
1878
|
+
// boundary or an interleaved foreign turn must never donate its answer.
|
|
1879
|
+
const waitMessages = turnEnd.preserveWaitResponse
|
|
1880
|
+
? collected.filter(
|
|
1881
|
+
(group): group is Extract<TimelineGroup, { kind: "item" }> =>
|
|
1882
|
+
group.kind === "item" &&
|
|
1883
|
+
group.item.kind === "agent-message" &&
|
|
1884
|
+
!group.item.streaming &&
|
|
1885
|
+
group.item.text.trim().length > 0 &&
|
|
1886
|
+
belongsToTurn(group.item, turnEnd.turnId),
|
|
1887
|
+
)
|
|
1888
|
+
: [];
|
|
1889
|
+
// A completed answer outranks later commentary. Otherwise use the latest
|
|
1890
|
+
// visible existing prose, ignoring whitespace and opaque-only stream tails.
|
|
1891
|
+
const waitResponse =
|
|
1892
|
+
waitMessages
|
|
1893
|
+
.slice()
|
|
1894
|
+
.reverse()
|
|
1895
|
+
.find(
|
|
1896
|
+
(group) =>
|
|
1897
|
+
group.item.kind === "agent-message" &&
|
|
1898
|
+
group.item.phase !== "commentary" &&
|
|
1899
|
+
group.item.annotationSource?.eventType === "agent.message.completed",
|
|
1900
|
+
) ?? waitMessages.at(-1);
|
|
1901
|
+
const finalMessage = waitResponse ?? extractFinalAgentMessage(collected, turnEnd);
|
|
1812
1902
|
const fallbackMessage =
|
|
1813
1903
|
finalMessage || hasOrdinaryFinalAgentMessage(collected, turnEnd)
|
|
1814
1904
|
? null
|
package/src/timeline/types.ts
CHANGED
|
@@ -215,6 +215,7 @@ export type StartupPhaseItem = {
|
|
|
215
215
|
durationMs: number | null;
|
|
216
216
|
/** Sandbox origin and rig marker outcomes refine the settled label only. */
|
|
217
217
|
outcome: "created" | "restored" | "resumed" | "skipped" | null;
|
|
218
|
+
blockedReason?: "rotation_in_progress" | undefined;
|
|
218
219
|
occurredAt: string;
|
|
219
220
|
};
|
|
220
221
|
|
|
@@ -451,6 +452,8 @@ export type TurnOutcome = "complete" | "failed" | "cancelled";
|
|
|
451
452
|
|
|
452
453
|
export type TurnEndItem = {
|
|
453
454
|
kind: "turn-end";
|
|
455
|
+
/** Keep an existing answer visible when a recorded input wait ends without final output. */
|
|
456
|
+
preserveWaitResponse?: true;
|
|
454
457
|
id: string;
|
|
455
458
|
turnId: string | null;
|
|
456
459
|
outcome: TurnOutcome;
|
package/dist/chat.d.ts
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import { type ReactNode } from "react";
|
|
2
|
-
/**
|
|
3
|
-
* Chat component that talks only to the host's `createChatHandler` endpoint:
|
|
4
|
-
* it restores the conversation with `GET` on mount, POSTs `{ message }`
|
|
5
|
-
* (native format), renders the streamed reply, shows a card for a pending
|
|
6
|
-
* approval or human-input request, and answers it through
|
|
7
|
-
* `${handlerUrl}/respond`. No provider or OpenGeni credentials in the browser.
|
|
8
|
-
*/
|
|
9
|
-
export type OpenGeniChatMessage = {
|
|
10
|
-
id: string;
|
|
11
|
-
role: "user" | "assistant";
|
|
12
|
-
text: string;
|
|
13
|
-
streaming: boolean;
|
|
14
|
-
/** Tool names the agent used while producing this reply. */
|
|
15
|
-
tools: string[];
|
|
16
|
-
};
|
|
17
|
-
export type OpenGeniChatProps = {
|
|
18
|
-
/** The host endpoint served by `createChatHandler`. */
|
|
19
|
-
handlerUrl: string;
|
|
20
|
-
/** Sent as the `x-opengeni-conversation` header so the host's `resolve` can pick it up. */
|
|
21
|
-
conversation?: string | undefined;
|
|
22
|
-
/**
|
|
23
|
-
* Host authentication headers, resolved on each host render. Changed values
|
|
24
|
-
* reset the chat. Re-render the host when a callback's credentials change.
|
|
25
|
-
*/
|
|
26
|
-
headers?: Record<string, string> | (() => Record<string, string>) | undefined;
|
|
27
|
-
/**
|
|
28
|
-
* Stable authenticated user/tenant identity. Change this on sign-in, sign-out
|
|
29
|
-
* or tenant switches when authentication uses cookies or otherwise changes
|
|
30
|
-
* without changing the headers. This resets local state; it is not sent.
|
|
31
|
-
*/
|
|
32
|
-
authKey?: string | undefined;
|
|
33
|
-
placeholder?: string | undefined;
|
|
34
|
-
className?: string | undefined;
|
|
35
|
-
renderMessage?: ((message: OpenGeniChatMessage) => ReactNode) | undefined;
|
|
36
|
-
};
|
|
37
|
-
export declare function OpenGeniChat(props: OpenGeniChatProps): import("react").JSX.Element;
|
package/dist/chat.js
DELETED
|
@@ -1,315 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
HumanInputForm,
|
|
3
|
-
Markdown
|
|
4
|
-
} from "./chunk-YAIPFO5D.js";
|
|
5
|
-
import "./chunk-JVYQRRC4.js";
|
|
6
|
-
import "./chunk-HQV2I5HV.js";
|
|
7
|
-
import "./chunk-VZTQ73XT.js";
|
|
8
|
-
import "./chunk-22KP6LR5.js";
|
|
9
|
-
|
|
10
|
-
// src/chat.tsx
|
|
11
|
-
import {
|
|
12
|
-
CHAT_CONVERSATION_HEADER,
|
|
13
|
-
CHAT_FORMAT_HEADER,
|
|
14
|
-
parseChatChunkStream
|
|
15
|
-
} from "@opengeni/sdk/chat";
|
|
16
|
-
import {
|
|
17
|
-
useCallback,
|
|
18
|
-
useEffect,
|
|
19
|
-
useRef,
|
|
20
|
-
useState
|
|
21
|
-
} from "react";
|
|
22
|
-
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
23
|
-
var styles = {
|
|
24
|
-
root: {
|
|
25
|
-
display: "flex",
|
|
26
|
-
flexDirection: "column",
|
|
27
|
-
gap: "0.75rem",
|
|
28
|
-
minHeight: 0,
|
|
29
|
-
color: "var(--og-color-fg)",
|
|
30
|
-
fontFamily: "var(--og-font-sans)",
|
|
31
|
-
fontSize: "var(--og-font-size-base)"
|
|
32
|
-
},
|
|
33
|
-
list: { display: "flex", flexDirection: "column", gap: "0.5rem", overflowY: "auto" },
|
|
34
|
-
user: {
|
|
35
|
-
alignSelf: "flex-end",
|
|
36
|
-
maxWidth: "80%",
|
|
37
|
-
whiteSpace: "pre-wrap",
|
|
38
|
-
padding: "0.5rem 0.75rem",
|
|
39
|
-
borderRadius: "var(--og-radius-lg)",
|
|
40
|
-
background: "var(--og-color-accent-soft)"
|
|
41
|
-
},
|
|
42
|
-
assistant: { alignSelf: "stretch", padding: "0.25rem 0" },
|
|
43
|
-
meta: { color: "var(--og-color-fg-muted)", fontSize: "var(--og-font-size-xs)" },
|
|
44
|
-
card: {
|
|
45
|
-
display: "flex",
|
|
46
|
-
flexDirection: "column",
|
|
47
|
-
gap: "0.5rem",
|
|
48
|
-
padding: "0.75rem",
|
|
49
|
-
border: "1px solid var(--og-color-border)",
|
|
50
|
-
borderRadius: "var(--og-radius-md)",
|
|
51
|
-
background: "var(--og-color-surface-2)"
|
|
52
|
-
},
|
|
53
|
-
row: { display: "flex", gap: "0.5rem", alignItems: "flex-end" },
|
|
54
|
-
input: {
|
|
55
|
-
flex: 1,
|
|
56
|
-
minHeight: "2.5rem",
|
|
57
|
-
padding: "0.5rem 0.75rem",
|
|
58
|
-
border: "1px solid var(--og-color-border)",
|
|
59
|
-
borderRadius: "var(--og-radius-md)",
|
|
60
|
-
background: "var(--og-color-surface-1)",
|
|
61
|
-
color: "inherit",
|
|
62
|
-
font: "inherit",
|
|
63
|
-
resize: "vertical"
|
|
64
|
-
},
|
|
65
|
-
button: {
|
|
66
|
-
padding: "0.5rem 0.9rem",
|
|
67
|
-
border: "1px solid var(--og-color-border-strong)",
|
|
68
|
-
borderRadius: "var(--og-radius-md)",
|
|
69
|
-
background: "var(--og-color-accent)",
|
|
70
|
-
color: "var(--og-color-accent-fg)",
|
|
71
|
-
font: "inherit",
|
|
72
|
-
cursor: "pointer"
|
|
73
|
-
},
|
|
74
|
-
secondary: {
|
|
75
|
-
padding: "0.5rem 0.9rem",
|
|
76
|
-
border: "1px solid var(--og-color-border)",
|
|
77
|
-
borderRadius: "var(--og-radius-md)",
|
|
78
|
-
background: "var(--og-color-surface-1)",
|
|
79
|
-
color: "inherit",
|
|
80
|
-
font: "inherit",
|
|
81
|
-
cursor: "pointer"
|
|
82
|
-
},
|
|
83
|
-
error: { color: "var(--og-color-danger)", fontSize: "var(--og-font-size-sm)" }
|
|
84
|
-
};
|
|
85
|
-
function uid() {
|
|
86
|
-
return typeof crypto !== "undefined" && "randomUUID" in crypto ? crypto.randomUUID() : Math.random().toString(36).slice(2);
|
|
87
|
-
}
|
|
88
|
-
function requestHeaders(conversation, headers) {
|
|
89
|
-
return {
|
|
90
|
-
[CHAT_FORMAT_HEADER]: "native",
|
|
91
|
-
...conversation ? { [CHAT_CONVERSATION_HEADER]: conversation } : {},
|
|
92
|
-
...headers
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
function restoredMessages(payload) {
|
|
96
|
-
const list = payload?.messages;
|
|
97
|
-
if (!Array.isArray(list)) return [];
|
|
98
|
-
const restored = [];
|
|
99
|
-
for (const entry of list) {
|
|
100
|
-
const record = entry ?? {};
|
|
101
|
-
if (record.role !== "user" && record.role !== "assistant" || typeof record.text !== "string")
|
|
102
|
-
continue;
|
|
103
|
-
restored.push({ id: uid(), role: record.role, text: record.text, streaming: false, tools: [] });
|
|
104
|
-
}
|
|
105
|
-
return restored;
|
|
106
|
-
}
|
|
107
|
-
function OpenGeniChat(props) {
|
|
108
|
-
const headers = Object.fromEntries(
|
|
109
|
-
[...new Headers(typeof props.headers === "function" ? props.headers() : props.headers)].map(([name, value]) => [name.toLowerCase(), value]).sort(([left], [right]) => left.localeCompare(right))
|
|
110
|
-
);
|
|
111
|
-
const identity = JSON.stringify([
|
|
112
|
-
props.handlerUrl,
|
|
113
|
-
props.conversation ?? null,
|
|
114
|
-
props.authKey ?? null,
|
|
115
|
-
headers
|
|
116
|
-
]);
|
|
117
|
-
return /* @__PURE__ */ jsx(ChatConversation, { ...props, headers }, identity);
|
|
118
|
-
}
|
|
119
|
-
function ChatConversation({
|
|
120
|
-
handlerUrl,
|
|
121
|
-
conversation,
|
|
122
|
-
headers,
|
|
123
|
-
placeholder = "Message",
|
|
124
|
-
className,
|
|
125
|
-
renderMessage
|
|
126
|
-
}) {
|
|
127
|
-
const [messages, setMessages] = useState([]);
|
|
128
|
-
const [pendingRequests, setPendingRequests] = useState([]);
|
|
129
|
-
const pending = pendingRequests[0] ?? null;
|
|
130
|
-
const [restoring, setRestoring] = useState(true);
|
|
131
|
-
const [draft, setDraft] = useState("");
|
|
132
|
-
const [busy, setBusy] = useState(false);
|
|
133
|
-
const [error, setError] = useState(null);
|
|
134
|
-
const abortRef = useRef(null);
|
|
135
|
-
const headersRef = useRef(headers);
|
|
136
|
-
headersRef.current = headers;
|
|
137
|
-
useEffect(() => {
|
|
138
|
-
const controller = new AbortController();
|
|
139
|
-
abortRef.current?.abort();
|
|
140
|
-
setBusy(false);
|
|
141
|
-
setRestoring(true);
|
|
142
|
-
setError(null);
|
|
143
|
-
setMessages([]);
|
|
144
|
-
setPendingRequests([]);
|
|
145
|
-
void (async () => {
|
|
146
|
-
try {
|
|
147
|
-
const response = await fetch(handlerUrl, {
|
|
148
|
-
method: "GET",
|
|
149
|
-
headers: requestHeaders(conversation, headersRef.current),
|
|
150
|
-
signal: controller.signal
|
|
151
|
-
});
|
|
152
|
-
if (!response.ok) return;
|
|
153
|
-
const payload = await response.json();
|
|
154
|
-
if (controller.signal.aborted) return;
|
|
155
|
-
setMessages(restoredMessages(payload));
|
|
156
|
-
setPendingRequests(Array.isArray(payload.pending) ? payload.pending : []);
|
|
157
|
-
} catch {
|
|
158
|
-
} finally {
|
|
159
|
-
if (!controller.signal.aborted) setRestoring(false);
|
|
160
|
-
}
|
|
161
|
-
})();
|
|
162
|
-
return () => {
|
|
163
|
-
controller.abort();
|
|
164
|
-
abortRef.current?.abort();
|
|
165
|
-
};
|
|
166
|
-
}, [handlerUrl, conversation]);
|
|
167
|
-
const update = useCallback(
|
|
168
|
-
(id, patch) => setMessages((prev) => prev.map((message) => message.id === id ? patch(message) : message)),
|
|
169
|
-
[]
|
|
170
|
-
);
|
|
171
|
-
const consume = useCallback(
|
|
172
|
-
async (url, body, assistantId) => {
|
|
173
|
-
abortRef.current?.abort();
|
|
174
|
-
const controller = new AbortController();
|
|
175
|
-
abortRef.current = controller;
|
|
176
|
-
setBusy(true);
|
|
177
|
-
setError(null);
|
|
178
|
-
try {
|
|
179
|
-
const response = await fetch(url, {
|
|
180
|
-
method: "POST",
|
|
181
|
-
headers: { "Content-Type": "application/json", ...requestHeaders(conversation, headers) },
|
|
182
|
-
body: JSON.stringify(body),
|
|
183
|
-
signal: controller.signal
|
|
184
|
-
});
|
|
185
|
-
if (!response.ok || !response.body) {
|
|
186
|
-
throw new Error(`Chat request failed (${response.status}).`);
|
|
187
|
-
}
|
|
188
|
-
for await (const chunk of parseChatChunkStream(response.body)) {
|
|
189
|
-
if (controller.signal.aborted) return;
|
|
190
|
-
if (chunk.type === "text") {
|
|
191
|
-
update(assistantId, (m) => ({ ...m, text: m.text + chunk.text }));
|
|
192
|
-
} else if (chunk.type === "tool" && chunk.status === "started") {
|
|
193
|
-
update(assistantId, (m) => ({ ...m, tools: [...m.tools, chunk.name] }));
|
|
194
|
-
} else if (chunk.type === "pending") {
|
|
195
|
-
setPendingRequests([chunk.pending]);
|
|
196
|
-
} else if (chunk.type === "done") {
|
|
197
|
-
setPendingRequests(chunk.reply.pending ? [chunk.reply.pending] : []);
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
} catch (caught) {
|
|
201
|
-
if (!controller.signal.aborted && !(caught instanceof Error && caught.name === "AbortError")) {
|
|
202
|
-
setError(caught instanceof Error ? caught.message : "Chat request failed.");
|
|
203
|
-
}
|
|
204
|
-
} finally {
|
|
205
|
-
if (!controller.signal.aborted) {
|
|
206
|
-
update(assistantId, (m) => ({ ...m, streaming: false }));
|
|
207
|
-
setBusy(false);
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
},
|
|
211
|
-
[conversation, headers, update]
|
|
212
|
-
);
|
|
213
|
-
const submit = (event) => {
|
|
214
|
-
event.preventDefault();
|
|
215
|
-
const text = draft.trim();
|
|
216
|
-
if (!text || busy || restoring) return;
|
|
217
|
-
setDraft("");
|
|
218
|
-
const assistantId = uid();
|
|
219
|
-
setMessages((prev) => [
|
|
220
|
-
...prev,
|
|
221
|
-
{ id: uid(), role: "user", text, streaming: false, tools: [] },
|
|
222
|
-
{ id: assistantId, role: "assistant", text: "", streaming: true, tools: [] }
|
|
223
|
-
]);
|
|
224
|
-
void consume(handlerUrl, { message: text }, assistantId);
|
|
225
|
-
};
|
|
226
|
-
const respond = (input) => {
|
|
227
|
-
if (!pending || busy || restoring) return;
|
|
228
|
-
const last = [...messages].reverse().find((m) => m.role === "assistant");
|
|
229
|
-
const assistantId = last?.id ?? uid();
|
|
230
|
-
if (last) update(assistantId, (m) => ({ ...m, streaming: true }));
|
|
231
|
-
else
|
|
232
|
-
setMessages((prev) => [
|
|
233
|
-
...prev,
|
|
234
|
-
{ id: assistantId, role: "assistant", text: "", streaming: true, tools: [] }
|
|
235
|
-
]);
|
|
236
|
-
void consume(
|
|
237
|
-
`${handlerUrl.replace(/\/+$/, "")}/respond`,
|
|
238
|
-
{ requestId: pending.requestId, ...input },
|
|
239
|
-
assistantId
|
|
240
|
-
);
|
|
241
|
-
};
|
|
242
|
-
return /* @__PURE__ */ jsxs("div", { className: `og-root og-chat${className ? ` ${className}` : ""}`, style: styles.root, children: [
|
|
243
|
-
/* @__PURE__ */ jsx("div", { className: "og-chat-messages", style: styles.list, children: messages.map(
|
|
244
|
-
(message) => renderMessage ? /* @__PURE__ */ jsx("div", { children: renderMessage(message) }, message.id) : message.role === "user" ? /* @__PURE__ */ jsx("div", { className: "og-chat-user", style: styles.user, children: message.text }, message.id) : /* @__PURE__ */ jsxs("div", { className: "og-chat-assistant", style: styles.assistant, children: [
|
|
245
|
-
message.tools.length > 0 ? /* @__PURE__ */ jsxs("div", { style: styles.meta, children: [
|
|
246
|
-
"Used ",
|
|
247
|
-
message.tools.join(", ")
|
|
248
|
-
] }) : null,
|
|
249
|
-
message.text ? /* @__PURE__ */ jsx(Markdown, { streaming: message.streaming, children: message.text }) : message.streaming ? /* @__PURE__ */ jsx("div", { style: styles.meta, children: "Thinking" }) : null
|
|
250
|
-
] }, message.id)
|
|
251
|
-
) }),
|
|
252
|
-
pending ? /* @__PURE__ */ jsx("div", { className: "og-chat-pending", style: styles.card, children: pending.kind === "approval" ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
253
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
254
|
-
"The agent wants to run ",
|
|
255
|
-
pending.name ?? "a tool",
|
|
256
|
-
"."
|
|
257
|
-
] }),
|
|
258
|
-
/* @__PURE__ */ jsxs("div", { style: styles.row, children: [
|
|
259
|
-
/* @__PURE__ */ jsx(
|
|
260
|
-
"button",
|
|
261
|
-
{
|
|
262
|
-
type: "button",
|
|
263
|
-
style: styles.button,
|
|
264
|
-
onClick: () => respond({ decision: "approve" }),
|
|
265
|
-
children: "Approve"
|
|
266
|
-
}
|
|
267
|
-
),
|
|
268
|
-
/* @__PURE__ */ jsx(
|
|
269
|
-
"button",
|
|
270
|
-
{
|
|
271
|
-
type: "button",
|
|
272
|
-
style: styles.secondary,
|
|
273
|
-
onClick: () => respond({ decision: "reject" }),
|
|
274
|
-
children: "Reject"
|
|
275
|
-
}
|
|
276
|
-
)
|
|
277
|
-
] })
|
|
278
|
-
] }) : /* @__PURE__ */ jsx(
|
|
279
|
-
HumanInputForm,
|
|
280
|
-
{
|
|
281
|
-
request: pending.payload,
|
|
282
|
-
submitting: busy || restoring,
|
|
283
|
-
error,
|
|
284
|
-
onSubmit: (response) => respond(
|
|
285
|
-
response.outcome === "answered" ? { answers: response.answers } : { skip: true }
|
|
286
|
-
)
|
|
287
|
-
}
|
|
288
|
-
) }) : null,
|
|
289
|
-
error ? /* @__PURE__ */ jsx("div", { className: "og-chat-error", style: styles.error, children: error }) : null,
|
|
290
|
-
/* @__PURE__ */ jsxs("form", { className: "og-chat-composer", style: styles.row, onSubmit: submit, children: [
|
|
291
|
-
/* @__PURE__ */ jsx(
|
|
292
|
-
"textarea",
|
|
293
|
-
{
|
|
294
|
-
style: styles.input,
|
|
295
|
-
value: draft,
|
|
296
|
-
placeholder,
|
|
297
|
-
rows: 1,
|
|
298
|
-
onChange: (e) => setDraft(e.target.value),
|
|
299
|
-
onInput: (e) => setDraft(e.currentTarget.value),
|
|
300
|
-
onKeyDown: (e) => {
|
|
301
|
-
if (e.key === "Enter" && !e.shiftKey) {
|
|
302
|
-
e.preventDefault();
|
|
303
|
-
e.currentTarget.form?.requestSubmit();
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
),
|
|
308
|
-
/* @__PURE__ */ jsx("button", { type: "submit", style: styles.button, disabled: busy || restoring || !draft.trim(), children: "Send" })
|
|
309
|
-
] })
|
|
310
|
-
] });
|
|
311
|
-
}
|
|
312
|
-
export {
|
|
313
|
-
OpenGeniChat
|
|
314
|
-
};
|
|
315
|
-
//# sourceMappingURL=chat.js.map
|
package/dist/chat.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/chat.tsx"],"sourcesContent":["import {\n CHAT_CONVERSATION_HEADER,\n CHAT_FORMAT_HEADER,\n parseChatChunkStream,\n type ChatPending,\n} from \"@opengeni/sdk/chat\";\nimport {\n useCallback,\n useEffect,\n useRef,\n useState,\n type CSSProperties,\n type FormEvent,\n type ReactNode,\n} from \"react\";\nimport { HumanInputForm, type HumanInputFormProps } from \"./components/human-input-form\";\nimport { Markdown } from \"./components/markdown\";\n\n/**\n * Chat component that talks only to the host's `createChatHandler` endpoint:\n * it restores the conversation with `GET` on mount, POSTs `{ message }`\n * (native format), renders the streamed reply, shows a card for a pending\n * approval or human-input request, and answers it through\n * `${handlerUrl}/respond`. No provider or OpenGeni credentials in the browser.\n */\n\nexport type OpenGeniChatMessage = {\n id: string;\n role: \"user\" | \"assistant\";\n text: string;\n streaming: boolean;\n /** Tool names the agent used while producing this reply. */\n tools: string[];\n};\n\nexport type OpenGeniChatProps = {\n /** The host endpoint served by `createChatHandler`. */\n handlerUrl: string;\n /** Sent as the `x-opengeni-conversation` header so the host's `resolve` can pick it up. */\n conversation?: string | undefined;\n /**\n * Host authentication headers, resolved on each host render. Changed values\n * reset the chat. Re-render the host when a callback's credentials change.\n */\n headers?: Record<string, string> | (() => Record<string, string>) | undefined;\n /**\n * Stable authenticated user/tenant identity. Change this on sign-in, sign-out\n * or tenant switches when authentication uses cookies or otherwise changes\n * without changing the headers. This resets local state; it is not sent.\n */\n authKey?: string | undefined;\n placeholder?: string | undefined;\n className?: string | undefined;\n renderMessage?: ((message: OpenGeniChatMessage) => ReactNode) | undefined;\n};\n\nconst styles = {\n root: {\n display: \"flex\",\n flexDirection: \"column\",\n gap: \"0.75rem\",\n minHeight: 0,\n color: \"var(--og-color-fg)\",\n fontFamily: \"var(--og-font-sans)\",\n fontSize: \"var(--og-font-size-base)\",\n },\n list: { display: \"flex\", flexDirection: \"column\", gap: \"0.5rem\", overflowY: \"auto\" },\n user: {\n alignSelf: \"flex-end\",\n maxWidth: \"80%\",\n whiteSpace: \"pre-wrap\",\n padding: \"0.5rem 0.75rem\",\n borderRadius: \"var(--og-radius-lg)\",\n background: \"var(--og-color-accent-soft)\",\n },\n assistant: { alignSelf: \"stretch\", padding: \"0.25rem 0\" },\n meta: { color: \"var(--og-color-fg-muted)\", fontSize: \"var(--og-font-size-xs)\" },\n card: {\n display: \"flex\",\n flexDirection: \"column\",\n gap: \"0.5rem\",\n padding: \"0.75rem\",\n border: \"1px solid var(--og-color-border)\",\n borderRadius: \"var(--og-radius-md)\",\n background: \"var(--og-color-surface-2)\",\n },\n row: { display: \"flex\", gap: \"0.5rem\", alignItems: \"flex-end\" },\n input: {\n flex: 1,\n minHeight: \"2.5rem\",\n padding: \"0.5rem 0.75rem\",\n border: \"1px solid var(--og-color-border)\",\n borderRadius: \"var(--og-radius-md)\",\n background: \"var(--og-color-surface-1)\",\n color: \"inherit\",\n font: \"inherit\",\n resize: \"vertical\",\n },\n button: {\n padding: \"0.5rem 0.9rem\",\n border: \"1px solid var(--og-color-border-strong)\",\n borderRadius: \"var(--og-radius-md)\",\n background: \"var(--og-color-accent)\",\n color: \"var(--og-color-accent-fg)\",\n font: \"inherit\",\n cursor: \"pointer\",\n },\n secondary: {\n padding: \"0.5rem 0.9rem\",\n border: \"1px solid var(--og-color-border)\",\n borderRadius: \"var(--og-radius-md)\",\n background: \"var(--og-color-surface-1)\",\n color: \"inherit\",\n font: \"inherit\",\n cursor: \"pointer\",\n },\n error: { color: \"var(--og-color-danger)\", fontSize: \"var(--og-font-size-sm)\" },\n} satisfies Record<string, CSSProperties>;\n\nfunction uid(): string {\n return typeof crypto !== \"undefined\" && \"randomUUID\" in crypto\n ? crypto.randomUUID()\n : Math.random().toString(36).slice(2);\n}\n\ntype HostHeaders = Record<string, string>;\n\nfunction requestHeaders(conversation: string | undefined, headers: HostHeaders) {\n return {\n [CHAT_FORMAT_HEADER]: \"native\",\n ...(conversation ? { [CHAT_CONVERSATION_HEADER]: conversation } : {}),\n ...headers,\n };\n}\n\n/** Messages from the handler's `GET` history response; anything unexpected is dropped. */\nfunction restoredMessages(payload: unknown): OpenGeniChatMessage[] {\n const list = (payload as { messages?: unknown } | null)?.messages;\n if (!Array.isArray(list)) return [];\n const restored: OpenGeniChatMessage[] = [];\n for (const entry of list) {\n const record = (entry ?? {}) as { role?: unknown; text?: unknown };\n if ((record.role !== \"user\" && record.role !== \"assistant\") || typeof record.text !== \"string\")\n continue;\n restored.push({ id: uid(), role: record.role, text: record.text, streaming: false, tools: [] });\n }\n return restored;\n}\n\nexport function OpenGeniChat(props: OpenGeniChatProps) {\n // Normalize actual HTTP values so an inline object/callback or header casing\n // change does not erase a draft. Resolve callbacks once per host render: GET\n // and POST must use the same authentication snapshot as the displayed state.\n const headers = Object.fromEntries(\n [...new Headers(typeof props.headers === \"function\" ? props.headers() : props.headers)]\n .map(([name, value]) => [name.toLowerCase(), value] as const)\n .sort(([left], [right]) => left.localeCompare(right)),\n );\n const identity = JSON.stringify([\n props.handlerUrl,\n props.conversation ?? null,\n props.authKey ?? null,\n headers,\n ]);\n // A keyed boundary drops private state in the same commit, before effects\n // run. Unmount cleanup aborts old history and streams; late completions can\n // only address the unmounted instance, never the new user's conversation.\n return <ChatConversation key={identity} {...props} headers={headers} />;\n}\n\nfunction ChatConversation({\n handlerUrl,\n conversation,\n headers,\n placeholder = \"Message\",\n className,\n renderMessage,\n}: Omit<OpenGeniChatProps, \"headers\"> & { headers: HostHeaders }) {\n const [messages, setMessages] = useState<OpenGeniChatMessage[]>([]);\n const [pendingRequests, setPendingRequests] = useState<ChatPending[]>([]);\n const pending = pendingRequests[0] ?? null;\n const [restoring, setRestoring] = useState(true);\n const [draft, setDraft] = useState(\"\");\n const [busy, setBusy] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const abortRef = useRef<AbortController | null>(null);\n const headersRef = useRef<HostHeaders>(headers);\n headersRef.current = headers;\n\n // Restore the conversation on mount and whenever it changes. The composer\n // waits for restoration so a late snapshot cannot resurrect a decided card.\n useEffect(() => {\n const controller = new AbortController();\n abortRef.current?.abort();\n setBusy(false);\n setRestoring(true);\n setError(null);\n setMessages([]);\n setPendingRequests([]);\n void (async () => {\n try {\n const response = await fetch(handlerUrl, {\n method: \"GET\",\n headers: requestHeaders(conversation, headersRef.current),\n signal: controller.signal,\n });\n if (!response.ok) return;\n const payload = (await response.json()) as { pending?: ChatPending[] };\n if (controller.signal.aborted) return;\n setMessages(restoredMessages(payload));\n setPendingRequests(Array.isArray(payload.pending) ? payload.pending : []);\n } catch {\n // Hosts without history may still accept sends.\n } finally {\n if (!controller.signal.aborted) setRestoring(false);\n }\n })();\n return () => {\n controller.abort();\n abortRef.current?.abort();\n };\n }, [handlerUrl, conversation]);\n\n const update = useCallback(\n (id: string, patch: (message: OpenGeniChatMessage) => OpenGeniChatMessage) =>\n setMessages((prev) => prev.map((message) => (message.id === id ? patch(message) : message))),\n [],\n );\n\n const consume = useCallback(\n async (url: string, body: unknown, assistantId: string) => {\n abortRef.current?.abort();\n const controller = new AbortController();\n abortRef.current = controller;\n setBusy(true);\n setError(null);\n try {\n const response = await fetch(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\", ...requestHeaders(conversation, headers) },\n body: JSON.stringify(body),\n signal: controller.signal,\n });\n if (!response.ok || !response.body) {\n throw new Error(`Chat request failed (${response.status}).`);\n }\n for await (const chunk of parseChatChunkStream(response.body)) {\n if (controller.signal.aborted) return;\n if (chunk.type === \"text\") {\n update(assistantId, (m) => ({ ...m, text: m.text + chunk.text }));\n } else if (chunk.type === \"tool\" && chunk.status === \"started\") {\n update(assistantId, (m) => ({ ...m, tools: [...m.tools, chunk.name] }));\n } else if (chunk.type === \"pending\") {\n setPendingRequests([chunk.pending]);\n } else if (chunk.type === \"done\") {\n setPendingRequests(chunk.reply.pending ? [chunk.reply.pending] : []);\n }\n }\n } catch (caught) {\n if (\n !controller.signal.aborted &&\n !(caught instanceof Error && caught.name === \"AbortError\")\n ) {\n setError(caught instanceof Error ? caught.message : \"Chat request failed.\");\n }\n } finally {\n if (!controller.signal.aborted) {\n update(assistantId, (m) => ({ ...m, streaming: false }));\n setBusy(false);\n }\n }\n },\n [conversation, headers, update],\n );\n\n const submit = (event: FormEvent) => {\n event.preventDefault();\n const text = draft.trim();\n if (!text || busy || restoring) return;\n setDraft(\"\");\n const assistantId = uid();\n setMessages((prev) => [\n ...prev,\n { id: uid(), role: \"user\", text, streaming: false, tools: [] },\n { id: assistantId, role: \"assistant\", text: \"\", streaming: true, tools: [] },\n ]);\n void consume(handlerUrl, { message: text }, assistantId);\n };\n\n const respond = (input: Record<string, unknown>) => {\n if (!pending || busy || restoring) return;\n const last = [...messages].reverse().find((m) => m.role === \"assistant\");\n const assistantId = last?.id ?? uid();\n if (last) update(assistantId, (m) => ({ ...m, streaming: true }));\n else\n setMessages((prev) => [\n ...prev,\n { id: assistantId, role: \"assistant\", text: \"\", streaming: true, tools: [] },\n ]);\n void consume(\n `${handlerUrl.replace(/\\/+$/, \"\")}/respond`,\n { requestId: pending.requestId, ...input },\n assistantId,\n );\n };\n\n return (\n <div className={`og-root og-chat${className ? ` ${className}` : \"\"}`} style={styles.root}>\n <div className=\"og-chat-messages\" style={styles.list}>\n {messages.map((message) =>\n renderMessage ? (\n <div key={message.id}>{renderMessage(message)}</div>\n ) : message.role === \"user\" ? (\n <div key={message.id} className=\"og-chat-user\" style={styles.user}>\n {message.text}\n </div>\n ) : (\n <div key={message.id} className=\"og-chat-assistant\" style={styles.assistant}>\n {message.tools.length > 0 ? (\n <div style={styles.meta}>Used {message.tools.join(\", \")}</div>\n ) : null}\n {message.text ? (\n <Markdown streaming={message.streaming}>{message.text}</Markdown>\n ) : message.streaming ? (\n <div style={styles.meta}>Thinking</div>\n ) : null}\n </div>\n ),\n )}\n </div>\n {pending ? (\n <div className=\"og-chat-pending\" style={styles.card}>\n {pending.kind === \"approval\" ? (\n <>\n <div>The agent wants to run {pending.name ?? \"a tool\"}.</div>\n <div style={styles.row}>\n <button\n type=\"button\"\n style={styles.button}\n onClick={() => respond({ decision: \"approve\" })}\n >\n Approve\n </button>\n <button\n type=\"button\"\n style={styles.secondary}\n onClick={() => respond({ decision: \"reject\" })}\n >\n Reject\n </button>\n </div>\n </>\n ) : (\n <HumanInputForm\n request={pending.payload as HumanInputFormProps[\"request\"]}\n submitting={busy || restoring}\n error={error}\n onSubmit={(response) =>\n respond(\n response.outcome === \"answered\" ? { answers: response.answers } : { skip: true },\n )\n }\n />\n )}\n </div>\n ) : null}\n {error ? (\n <div className=\"og-chat-error\" style={styles.error}>\n {error}\n </div>\n ) : null}\n <form className=\"og-chat-composer\" style={styles.row} onSubmit={submit}>\n <textarea\n style={styles.input}\n value={draft}\n placeholder={placeholder}\n rows={1}\n onChange={(e) => setDraft(e.target.value)}\n onInput={(e) => setDraft(e.currentTarget.value)}\n onKeyDown={(e) => {\n if (e.key === \"Enter\" && !e.shiftKey) {\n e.preventDefault();\n e.currentTarget.form?.requestSubmit();\n }\n }}\n />\n <button type=\"submit\" style={styles.button} disabled={busy || restoring || !draft.trim()}>\n Send\n </button>\n </form>\n </div>\n );\n}\n"],"mappings":"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","names":[]}
|