@opengeni/react 0.34.2 → 0.34.3
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/{chunk-A2WITZTI.js → chunk-4NMH6UJ3.js} +2 -2
- package/dist/{chunk-GZ4KR67U.js → chunk-ALA3UGWB.js} +2 -2
- package/dist/chunk-ATSTR5P3.js +138 -0
- package/dist/chunk-ATSTR5P3.js.map +1 -0
- package/dist/{chunk-KZRUYCL4.js → chunk-FT2TXNGZ.js} +5 -2
- package/dist/chunk-FT2TXNGZ.js.map +1 -0
- package/dist/{chunk-4QOQ2DSR.js → chunk-LAKLF4PA.js} +14 -1
- package/dist/chunk-LAKLF4PA.js.map +1 -0
- package/dist/{chunk-TCCE5ZU5.js → chunk-MEGI4FS6.js} +38 -9
- package/dist/chunk-MEGI4FS6.js.map +1 -0
- package/dist/{chunk-MS3FBZ6A.js → chunk-PVW56EVR.js} +105 -62
- package/dist/chunk-PVW56EVR.js.map +1 -0
- package/dist/{chunk-L7R5RK6A.js → chunk-QRYJCWGD.js} +1199 -897
- package/dist/chunk-QRYJCWGD.js.map +1 -0
- package/dist/components/chat-composer.d.ts +2 -1
- package/dist/components/composer.d.ts +8 -2
- package/dist/components/copy-button.d.ts +4 -2
- package/dist/components/tip-follow.d.ts +3 -0
- package/dist/composer.d.ts +2 -1
- package/dist/composer.js +4 -3
- package/dist/index.d.ts +2 -2
- package/dist/index.js +105 -101
- package/dist/index.js.map +1 -1
- package/dist/lib/format.d.ts +2 -0
- package/dist/machines.js +2 -2
- package/dist/session-ui.js +4 -3
- package/dist/session.js +4 -4
- package/dist/timeline/index.d.ts +1 -1
- package/dist/timeline/parsers.d.ts +5 -0
- package/dist/timeline/projection.d.ts +1 -12
- package/dist/timeline/tool-display-name.d.ts +17 -6
- package/dist/workstream-control-event.d.ts +6 -0
- package/package.json +1 -1
- package/src/components/chat-composer.tsx +1 -1
- package/src/components/composer-transcription-control.tsx +70 -57
- package/src/components/composer.tsx +197 -126
- package/src/components/copy-button.tsx +27 -8
- package/src/components/message-timeline.tsx +236 -39
- package/src/components/tip-follow.ts +15 -0
- package/src/composer.ts +1 -1
- package/src/hooks/use-composer.ts +7 -1
- package/src/index.ts +2 -1
- package/src/lib/format.ts +14 -0
- package/src/timeline/index.ts +2 -0
- package/src/timeline/parsers.ts +18 -1
- package/src/timeline/projection.ts +50 -20
- package/src/timeline/registry.ts +15 -2
- package/src/timeline/shared.tsx +30 -28
- package/src/timeline/tool-display-name.ts +30 -11
- package/src/timeline/tool-renderers.tsx +347 -16
- package/src/timeline/turn-summary.tsx +5 -2
- package/src/workstream-control-event.ts +6 -0
- package/dist/chunk-4QOQ2DSR.js.map +0 -1
- package/dist/chunk-KZRUYCL4.js.map +0 -1
- package/dist/chunk-L7R5RK6A.js.map +0 -1
- package/dist/chunk-MS3FBZ6A.js.map +0 -1
- package/dist/chunk-TCCE5ZU5.js.map +0 -1
- /package/dist/{chunk-A2WITZTI.js.map → chunk-4NMH6UJ3.js.map} +0 -0
- /package/dist/{chunk-GZ4KR67U.js.map → chunk-ALA3UGWB.js.map} +0 -0
|
@@ -1,23 +1,35 @@
|
|
|
1
1
|
import type { GitFileDiff } from "@opengeni/sdk";
|
|
2
2
|
import {
|
|
3
|
+
BoxIcon,
|
|
4
|
+
BrainCircuitIcon,
|
|
5
|
+
CalendarClockIcon,
|
|
3
6
|
CameraIcon,
|
|
4
7
|
CameraOffIcon,
|
|
5
8
|
FileDiffIcon,
|
|
9
|
+
FileSearchIcon,
|
|
10
|
+
FolderGitIcon,
|
|
6
11
|
GlobeIcon,
|
|
7
12
|
ImageIcon,
|
|
8
13
|
KeyboardIcon,
|
|
9
14
|
KeyRoundIcon,
|
|
10
15
|
LockIcon,
|
|
16
|
+
MessagesSquareIcon,
|
|
17
|
+
MessageSquareIcon,
|
|
11
18
|
MousePointer2Icon,
|
|
19
|
+
PanelsTopLeftIcon,
|
|
12
20
|
PlugIcon,
|
|
13
21
|
SearchIcon,
|
|
22
|
+
ServerCogIcon,
|
|
23
|
+
ServerIcon,
|
|
24
|
+
Share2Icon,
|
|
25
|
+
TargetIcon,
|
|
14
26
|
TerminalIcon,
|
|
15
27
|
WrenchIcon,
|
|
16
28
|
} from "lucide-react";
|
|
17
29
|
import type { ReactNode } from "react";
|
|
18
30
|
import { stringifyPayload, tryParseJson } from "../lib/format";
|
|
19
31
|
import {
|
|
20
|
-
|
|
32
|
+
applyPatchOpsFromToolItem,
|
|
21
33
|
controlCaret,
|
|
22
34
|
execTruncated,
|
|
23
35
|
isExecSessionLostBanner,
|
|
@@ -52,7 +64,7 @@ import {
|
|
|
52
64
|
type DisclosureChip,
|
|
53
65
|
} from "./shared";
|
|
54
66
|
import { RawPatch, ToolDiff } from "./tool-diff";
|
|
55
|
-
import { toolDisplayName } from "./tool-display-name";
|
|
67
|
+
import { mcpToolLeaf, toolDisplayName } from "./tool-display-name";
|
|
56
68
|
|
|
57
69
|
/* ----------------------------------------------------------------------------
|
|
58
70
|
Per-tool renderers
|
|
@@ -318,7 +330,7 @@ function PathPreview({
|
|
|
318
330
|
}
|
|
319
331
|
|
|
320
332
|
function ApplyPatchRenderer({ item }: ToolRendererProps) {
|
|
321
|
-
const ops =
|
|
333
|
+
const ops = applyPatchOpsFromToolItem(item);
|
|
322
334
|
const failed = item.status === "failed";
|
|
323
335
|
const cancelled = item.status === "cancelled";
|
|
324
336
|
const running = item.status === "running";
|
|
@@ -1008,21 +1020,264 @@ function SecretSetRenderer({ item }: ToolRendererProps) {
|
|
|
1008
1020
|
);
|
|
1009
1021
|
}
|
|
1010
1022
|
|
|
1023
|
+
/* ---- docs / knowledge search ----------------------------------------------- */
|
|
1024
|
+
|
|
1025
|
+
function DocsSearchRenderer({ item }: ToolRendererProps) {
|
|
1026
|
+
const args = parseToolArgs(item.arguments);
|
|
1027
|
+
const query = typeof args.query === "string" ? args.query.trim() : "";
|
|
1028
|
+
const title = query ? `Search “${truncatePreview(query, 48)}”` : toolDisplayName(item.name);
|
|
1029
|
+
const running = item.status === "running";
|
|
1030
|
+
|
|
1031
|
+
if (running) {
|
|
1032
|
+
return (
|
|
1033
|
+
<ActivityDisclosure
|
|
1034
|
+
icon={<FileSearchIcon className={ICON_SIZE} />}
|
|
1035
|
+
iconTone="running"
|
|
1036
|
+
title={title}
|
|
1037
|
+
running
|
|
1038
|
+
preview={<RunningPreview>Searching…</RunningPreview>}
|
|
1039
|
+
>
|
|
1040
|
+
<PayloadBlock label="Arguments" value={redactSecrets(args)} />
|
|
1041
|
+
</ActivityDisclosure>
|
|
1042
|
+
);
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
const { text: outText, isError } = unwrapMcpOutput(item.output);
|
|
1046
|
+
if ((isError || item.status === "failed") && item.status !== "cancelled") {
|
|
1047
|
+
return (
|
|
1048
|
+
<ActivityDisclosure
|
|
1049
|
+
icon={<FileSearchIcon className={ICON_SIZE} />}
|
|
1050
|
+
iconTone="failed"
|
|
1051
|
+
title={title}
|
|
1052
|
+
failed
|
|
1053
|
+
preview={truncatePreview(outText, 80) || "Search failed"}
|
|
1054
|
+
>
|
|
1055
|
+
<PayloadBlock label="Arguments" value={redactSecrets(args)} />
|
|
1056
|
+
<PayloadBlock label="Error" value={outText} failed />
|
|
1057
|
+
</ActivityDisclosure>
|
|
1058
|
+
);
|
|
1059
|
+
}
|
|
1060
|
+
|
|
1061
|
+
const hits = parseSearchHits(outText);
|
|
1062
|
+
const preview =
|
|
1063
|
+
item.status === "cancelled"
|
|
1064
|
+
? undefined
|
|
1065
|
+
: hits
|
|
1066
|
+
? hits.length === 0
|
|
1067
|
+
? "No hits"
|
|
1068
|
+
: `${hits.length} hit${hits.length === 1 ? "" : "s"}`
|
|
1069
|
+
: "Done";
|
|
1070
|
+
|
|
1071
|
+
return (
|
|
1072
|
+
<ActivityDisclosure
|
|
1073
|
+
icon={<FileSearchIcon className={ICON_SIZE} />}
|
|
1074
|
+
iconTone="muted"
|
|
1075
|
+
title={title}
|
|
1076
|
+
cancelled={item.status === "cancelled"}
|
|
1077
|
+
preview={preview}
|
|
1078
|
+
>
|
|
1079
|
+
{hits && hits.length > 0 ? (
|
|
1080
|
+
<ul className="grid gap-2">
|
|
1081
|
+
{hits.slice(0, 8).map((hit) => (
|
|
1082
|
+
<li key={`${hit.title}\u0000${hit.snippet}`} className="min-w-0">
|
|
1083
|
+
<div className="truncate text-og-sm font-medium text-og-fg">{hit.title}</div>
|
|
1084
|
+
{hit.snippet ? (
|
|
1085
|
+
<div className="mt-0.5 line-clamp-2 text-og-xs text-og-fg-muted">{hit.snippet}</div>
|
|
1086
|
+
) : null}
|
|
1087
|
+
</li>
|
|
1088
|
+
))}
|
|
1089
|
+
</ul>
|
|
1090
|
+
) : null}
|
|
1091
|
+
<PayloadBlock label="Arguments" value={redactSecrets(args)} />
|
|
1092
|
+
<PayloadBlock label="Result" value={outText} />
|
|
1093
|
+
</ActivityDisclosure>
|
|
1094
|
+
);
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
/* ---- set_session_title / set_other_session_title --------------------------- */
|
|
1098
|
+
|
|
1099
|
+
function SetSessionTitleRenderer({ item }: ToolRendererProps) {
|
|
1100
|
+
const args = parseToolArgs(item.arguments);
|
|
1101
|
+
const titleArg = typeof args.title === "string" ? args.title.trim() : "";
|
|
1102
|
+
const display = toolDisplayName(item.name);
|
|
1103
|
+
const previewTitle = titleArg ? truncatePreview(titleArg, 72) : "";
|
|
1104
|
+
const icon = <MessagesSquareIcon className={ICON_SIZE} />;
|
|
1105
|
+
|
|
1106
|
+
if (item.status === "running") {
|
|
1107
|
+
return (
|
|
1108
|
+
<ActivityDisclosure
|
|
1109
|
+
icon={icon}
|
|
1110
|
+
iconTone="running"
|
|
1111
|
+
title={display}
|
|
1112
|
+
running
|
|
1113
|
+
preview={
|
|
1114
|
+
previewTitle ? (
|
|
1115
|
+
<RunningPreview>{previewTitle}</RunningPreview>
|
|
1116
|
+
) : (
|
|
1117
|
+
<RunningPreview>Setting title…</RunningPreview>
|
|
1118
|
+
)
|
|
1119
|
+
}
|
|
1120
|
+
>
|
|
1121
|
+
<PayloadBlock label="Arguments" value={redactSecrets(args)} />
|
|
1122
|
+
</ActivityDisclosure>
|
|
1123
|
+
);
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
const { text: outText, isError } = unwrapMcpOutput(item.output);
|
|
1127
|
+
if ((isError || item.status === "failed") && item.status !== "cancelled") {
|
|
1128
|
+
return (
|
|
1129
|
+
<ActivityDisclosure
|
|
1130
|
+
icon={icon}
|
|
1131
|
+
iconTone="failed"
|
|
1132
|
+
title={display}
|
|
1133
|
+
failed
|
|
1134
|
+
preview={truncatePreview(outText, 80) || "Rename failed"}
|
|
1135
|
+
>
|
|
1136
|
+
<PayloadBlock label="Arguments" value={redactSecrets(args)} />
|
|
1137
|
+
<PayloadBlock label="Error" value={outText} failed />
|
|
1138
|
+
</ActivityDisclosure>
|
|
1139
|
+
);
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1142
|
+
// Prefer the submitted title; fall back to a title field in the tool result.
|
|
1143
|
+
let settledTitle = previewTitle;
|
|
1144
|
+
if (!settledTitle) {
|
|
1145
|
+
const parsed = tryParseJson(outText);
|
|
1146
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
1147
|
+
const fromResult = (parsed as { title?: unknown }).title;
|
|
1148
|
+
if (typeof fromResult === "string" && fromResult.trim()) {
|
|
1149
|
+
settledTitle = truncatePreview(fromResult.trim(), 72);
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1154
|
+
return (
|
|
1155
|
+
<ActivityDisclosure
|
|
1156
|
+
icon={icon}
|
|
1157
|
+
iconTone="muted"
|
|
1158
|
+
title={display}
|
|
1159
|
+
cancelled={item.status === "cancelled"}
|
|
1160
|
+
preview={item.status === "cancelled" ? undefined : settledTitle || undefined}
|
|
1161
|
+
>
|
|
1162
|
+
<PayloadBlock label="Arguments" value={redactSecrets(args)} />
|
|
1163
|
+
{outText ? <PayloadBlock label="Result" value={outText} /> : null}
|
|
1164
|
+
</ActivityDisclosure>
|
|
1165
|
+
);
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1168
|
+
type SearchHit = { title: string; snippet: string };
|
|
1169
|
+
|
|
1170
|
+
function parseSearchHits(outText: string): SearchHit[] | null {
|
|
1171
|
+
const parsed = tryParseJson(outText);
|
|
1172
|
+
if (parsed == null) {
|
|
1173
|
+
return null;
|
|
1174
|
+
}
|
|
1175
|
+
const list = Array.isArray(parsed)
|
|
1176
|
+
? parsed
|
|
1177
|
+
: parsed &&
|
|
1178
|
+
typeof parsed === "object" &&
|
|
1179
|
+
Array.isArray((parsed as { results?: unknown }).results)
|
|
1180
|
+
? (parsed as { results: unknown[] }).results
|
|
1181
|
+
: parsed && typeof parsed === "object" && Array.isArray((parsed as { hits?: unknown }).hits)
|
|
1182
|
+
? (parsed as { hits: unknown[] }).hits
|
|
1183
|
+
: null;
|
|
1184
|
+
if (!list) {
|
|
1185
|
+
return null;
|
|
1186
|
+
}
|
|
1187
|
+
return list.map((row) => {
|
|
1188
|
+
const r = row && typeof row === "object" ? (row as Record<string, unknown>) : {};
|
|
1189
|
+
const title =
|
|
1190
|
+
(typeof r.title === "string" && r.title) ||
|
|
1191
|
+
(typeof r.name === "string" && r.name) ||
|
|
1192
|
+
(typeof r.documentTitle === "string" && r.documentTitle) ||
|
|
1193
|
+
(typeof r.path === "string" && r.path) ||
|
|
1194
|
+
(typeof r.id === "string" && r.id) ||
|
|
1195
|
+
"Result";
|
|
1196
|
+
const snippet =
|
|
1197
|
+
(typeof r.snippet === "string" && r.snippet) ||
|
|
1198
|
+
(typeof r.text === "string" && r.text) ||
|
|
1199
|
+
(typeof r.content === "string" && r.content) ||
|
|
1200
|
+
"";
|
|
1201
|
+
return { title, snippet: truncatePreview(snippet, 160) };
|
|
1202
|
+
});
|
|
1203
|
+
}
|
|
1204
|
+
|
|
1205
|
+
/* ---- company memory propose (docs MCP) ------------------------------------- */
|
|
1206
|
+
|
|
1207
|
+
function MemoryProposeRenderer({ item }: ToolRendererProps) {
|
|
1208
|
+
const args = parseToolArgs(item.arguments);
|
|
1209
|
+
const text = typeof args.text === "string" ? args.text.trim() : "";
|
|
1210
|
+
const title = "Propose memory";
|
|
1211
|
+
const running = item.status === "running";
|
|
1212
|
+
|
|
1213
|
+
if (running) {
|
|
1214
|
+
return (
|
|
1215
|
+
<ActivityDisclosure
|
|
1216
|
+
icon={<BrainCircuitIcon className={ICON_SIZE} />}
|
|
1217
|
+
iconTone="running"
|
|
1218
|
+
title={title}
|
|
1219
|
+
running
|
|
1220
|
+
preview={<RunningPreview>Proposing…</RunningPreview>}
|
|
1221
|
+
>
|
|
1222
|
+
<PayloadBlock label="Arguments" value={redactSecrets(args)} />
|
|
1223
|
+
</ActivityDisclosure>
|
|
1224
|
+
);
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1227
|
+
const { text: outText, isError } = unwrapMcpOutput(item.output);
|
|
1228
|
+
if ((isError || item.status === "failed") && item.status !== "cancelled") {
|
|
1229
|
+
return (
|
|
1230
|
+
<ActivityDisclosure
|
|
1231
|
+
icon={<BrainCircuitIcon className={ICON_SIZE} />}
|
|
1232
|
+
iconTone="failed"
|
|
1233
|
+
title={title}
|
|
1234
|
+
failed
|
|
1235
|
+
preview={truncatePreview(outText, 80) || "Propose failed"}
|
|
1236
|
+
>
|
|
1237
|
+
{text ? <BodyNote>{text}</BodyNote> : null}
|
|
1238
|
+
<PayloadBlock label="Error" value={outText} failed />
|
|
1239
|
+
</ActivityDisclosure>
|
|
1240
|
+
);
|
|
1241
|
+
}
|
|
1242
|
+
|
|
1243
|
+
return (
|
|
1244
|
+
<ActivityDisclosure
|
|
1245
|
+
icon={<BrainCircuitIcon className={ICON_SIZE} />}
|
|
1246
|
+
iconTone="muted"
|
|
1247
|
+
title={title}
|
|
1248
|
+
cancelled={item.status === "cancelled"}
|
|
1249
|
+
preview={text ? truncatePreview(text, 90) : "Done"}
|
|
1250
|
+
>
|
|
1251
|
+
{text ? <BodyNote>{text}</BodyNote> : null}
|
|
1252
|
+
<PayloadBlock label="Result" value={outText} />
|
|
1253
|
+
</ActivityDisclosure>
|
|
1254
|
+
);
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1011
1257
|
/* ---- generic fallback (first-party MCP, external MCP, unknown) ------------- */
|
|
1012
1258
|
|
|
1259
|
+
/**
|
|
1260
|
+
* Baseline craft for unmatched tools: family icon + title-cased leaf + honest
|
|
1261
|
+
* status preview (Running… / Done / error snippet). No argument-field sniffing —
|
|
1262
|
+
* JSON stays in the expandable body only.
|
|
1263
|
+
*/
|
|
1013
1264
|
function GenericRenderer({ item }: ToolRendererProps) {
|
|
1014
1265
|
const running = item.status === "running";
|
|
1015
1266
|
const args = redactSecrets(parseToolArgs(item.arguments));
|
|
1016
1267
|
const display = toolDisplayName(item.name);
|
|
1268
|
+
const icon = <GenericToolIcon name={item.name} />;
|
|
1269
|
+
// Goal tools: surface the objective text on the collapsed row so the in-cluster
|
|
1270
|
+
// tool replaces the old breakaway GoalRow pill without losing the gist.
|
|
1271
|
+
const goalPreview = goalToolPreview(item.name, args);
|
|
1017
1272
|
|
|
1018
1273
|
if (running) {
|
|
1019
1274
|
return (
|
|
1020
1275
|
<ActivityDisclosure
|
|
1021
|
-
icon={
|
|
1276
|
+
icon={icon}
|
|
1022
1277
|
iconTone="running"
|
|
1023
1278
|
title={display}
|
|
1024
1279
|
running
|
|
1025
|
-
preview={<RunningPreview>
|
|
1280
|
+
preview={goalPreview ?? <RunningPreview>Running…</RunningPreview>}
|
|
1026
1281
|
>
|
|
1027
1282
|
<PayloadBlock label="Arguments" value={args} />
|
|
1028
1283
|
</ActivityDisclosure>
|
|
@@ -1036,11 +1291,11 @@ function GenericRenderer({ item }: ToolRendererProps) {
|
|
|
1036
1291
|
if ((isError || item.status === "failed") && item.status !== "cancelled") {
|
|
1037
1292
|
return (
|
|
1038
1293
|
<ActivityDisclosure
|
|
1039
|
-
icon={
|
|
1294
|
+
icon={icon}
|
|
1040
1295
|
iconTone="failed"
|
|
1041
1296
|
title={display}
|
|
1042
1297
|
chip={{ tone: "bad", text: "error" }}
|
|
1043
|
-
preview={outText
|
|
1298
|
+
preview={truncatePreview(outText, 80) || "Error"}
|
|
1044
1299
|
>
|
|
1045
1300
|
<PayloadBlock label="Arguments" value={args} />
|
|
1046
1301
|
<PayloadBlock label="Error" value={outText} failed />
|
|
@@ -1050,11 +1305,11 @@ function GenericRenderer({ item }: ToolRendererProps) {
|
|
|
1050
1305
|
|
|
1051
1306
|
return (
|
|
1052
1307
|
<ActivityDisclosure
|
|
1053
|
-
icon={
|
|
1308
|
+
icon={icon}
|
|
1054
1309
|
iconTone="muted"
|
|
1055
1310
|
title={display}
|
|
1056
1311
|
cancelled={item.status === "cancelled"}
|
|
1057
|
-
preview={
|
|
1312
|
+
preview={item.status === "cancelled" ? undefined : (goalPreview ?? "Done")}
|
|
1058
1313
|
>
|
|
1059
1314
|
<PayloadBlock label="Arguments" value={args} />
|
|
1060
1315
|
<PayloadBlock label="Result" value={outText} />
|
|
@@ -1062,9 +1317,81 @@ function GenericRenderer({ item }: ToolRendererProps) {
|
|
|
1062
1317
|
);
|
|
1063
1318
|
}
|
|
1064
1319
|
|
|
1065
|
-
function
|
|
1066
|
-
const
|
|
1067
|
-
|
|
1320
|
+
function goalToolPreview(name: string, args: unknown): string | null {
|
|
1321
|
+
const leaf = mcpToolLeaf(name);
|
|
1322
|
+
if (
|
|
1323
|
+
leaf !== "goal_set" &&
|
|
1324
|
+
leaf !== "goal_update" &&
|
|
1325
|
+
leaf !== "goal_complete" &&
|
|
1326
|
+
leaf !== "goal_pause"
|
|
1327
|
+
) {
|
|
1328
|
+
return null;
|
|
1329
|
+
}
|
|
1330
|
+
const record = args && typeof args === "object" ? (args as Record<string, unknown>) : null;
|
|
1331
|
+
if (!record) {
|
|
1332
|
+
return null;
|
|
1333
|
+
}
|
|
1334
|
+
const text =
|
|
1335
|
+
typeof record.text === "string"
|
|
1336
|
+
? record.text
|
|
1337
|
+
: typeof record.evidence === "string"
|
|
1338
|
+
? record.evidence
|
|
1339
|
+
: typeof record.rationale === "string"
|
|
1340
|
+
? record.rationale
|
|
1341
|
+
: typeof record.progressNote === "string"
|
|
1342
|
+
? record.progressNote
|
|
1343
|
+
: null;
|
|
1344
|
+
return text ? truncatePreview(text, 90) : null;
|
|
1345
|
+
}
|
|
1346
|
+
|
|
1347
|
+
function truncatePreview(text: string, max: number): string {
|
|
1348
|
+
const cleaned = text.replace(/\s+/g, " ").trim();
|
|
1349
|
+
if (!cleaned) {
|
|
1350
|
+
return "";
|
|
1351
|
+
}
|
|
1352
|
+
return cleaned.length > max ? `${cleaned.slice(0, max - 1)}…` : cleaned;
|
|
1353
|
+
}
|
|
1354
|
+
|
|
1355
|
+
/** Explicit first-party leaf/prefix → canonical product icons (match nav/pages). */
|
|
1356
|
+
function GenericToolIcon({ name }: { name: string }) {
|
|
1357
|
+
const leaf = mcpToolLeaf(name);
|
|
1358
|
+
const Icon = leaf.startsWith("goal_")
|
|
1359
|
+
? TargetIcon
|
|
1360
|
+
: leaf.startsWith("memory_") ||
|
|
1361
|
+
leaf === "preference_registry_summary" ||
|
|
1362
|
+
leaf === "preference_registry_get"
|
|
1363
|
+
? BrainCircuitIcon
|
|
1364
|
+
: leaf.startsWith("session_") ||
|
|
1365
|
+
leaf === "sessions_list" ||
|
|
1366
|
+
leaf === "set_session_title" ||
|
|
1367
|
+
leaf === "set_other_session_title"
|
|
1368
|
+
? MessagesSquareIcon
|
|
1369
|
+
: leaf.startsWith("sandbox") || leaf === "sandboxes_list" || leaf === "run_on"
|
|
1370
|
+
? ServerIcon
|
|
1371
|
+
: leaf.startsWith("rig_")
|
|
1372
|
+
? ServerCogIcon
|
|
1373
|
+
: leaf.startsWith("scheduled_")
|
|
1374
|
+
? CalendarClockIcon
|
|
1375
|
+
: leaf.startsWith("artifacts_")
|
|
1376
|
+
? PanelsTopLeftIcon
|
|
1377
|
+
: leaf.startsWith("social_")
|
|
1378
|
+
? Share2Icon
|
|
1379
|
+
: leaf.startsWith("slack_")
|
|
1380
|
+
? MessageSquareIcon
|
|
1381
|
+
: leaf.startsWith("github_")
|
|
1382
|
+
? FolderGitIcon
|
|
1383
|
+
: leaf.startsWith("variable_")
|
|
1384
|
+
? BoxIcon
|
|
1385
|
+
: leaf.startsWith("environment_")
|
|
1386
|
+
? KeyRoundIcon
|
|
1387
|
+
: leaf.includes("document") ||
|
|
1388
|
+
leaf.includes("knowledge") ||
|
|
1389
|
+
leaf === "list_document_bases"
|
|
1390
|
+
? FileSearchIcon
|
|
1391
|
+
: leaf === "tool_search" || leaf === "load_skill"
|
|
1392
|
+
? PlugIcon
|
|
1393
|
+
: WrenchIcon;
|
|
1394
|
+
return <Icon className={ICON_SIZE} />;
|
|
1068
1395
|
}
|
|
1069
1396
|
|
|
1070
1397
|
/* ---- the default registry -------------------------------------------------- */
|
|
@@ -1075,13 +1402,11 @@ const BASE_ENTRIES: ToolRegistryEntry[] = [
|
|
|
1075
1402
|
{ match: "rawType", type: "apply_patch_call", render: ApplyPatchRenderer },
|
|
1076
1403
|
{ match: "rawType", type: "computer_call", render: ComputerCallRenderer },
|
|
1077
1404
|
{ match: "rawType", type: "hosted_tool_call", render: WebSearchRenderer },
|
|
1078
|
-
// First-party sandbox + MCP tools resolve by name
|
|
1079
|
-
// `computer_call` are intentionally repeated by name as a fallback only for
|
|
1080
|
-
// first-party replays that omit `raw` (the rawType entries above win whenever
|
|
1081
|
-
// `raw.type` is present, which is the live-wire case).
|
|
1405
|
+
// First-party sandbox + MCP tools resolve by name (exact or MCP leaf).
|
|
1082
1406
|
{ match: "name", name: "exec_command", render: ExecRenderer },
|
|
1083
1407
|
{ match: "name", name: "write_stdin", render: WriteStdinRenderer },
|
|
1084
1408
|
{ match: "name", name: "apply_patch_call", render: ApplyPatchRenderer },
|
|
1409
|
+
{ match: "name", name: "apply_patch", render: ApplyPatchRenderer },
|
|
1085
1410
|
{ match: "name", name: "computer_call", render: ComputerCallRenderer },
|
|
1086
1411
|
// Function-mode computer tools (codex / chat-wire transports).
|
|
1087
1412
|
{ match: "name", name: "computer_screenshot", render: ComputerCallRenderer },
|
|
@@ -1095,6 +1420,12 @@ const BASE_ENTRIES: ToolRegistryEntry[] = [
|
|
|
1095
1420
|
{ match: "name", name: "web_search_call", render: WebSearchRenderer },
|
|
1096
1421
|
{ match: "name", name: "view_image", render: ViewImageRenderer },
|
|
1097
1422
|
{ match: "name", name: "environment_set_variable", render: SecretSetRenderer },
|
|
1423
|
+
{ match: "name", name: "variable_set_set_variable", render: SecretSetRenderer },
|
|
1424
|
+
{ match: "name", name: "search_documents", render: DocsSearchRenderer },
|
|
1425
|
+
{ match: "name", name: "knowledge_search", render: DocsSearchRenderer },
|
|
1426
|
+
{ match: "name", name: "memory_propose", render: MemoryProposeRenderer },
|
|
1427
|
+
{ match: "name", name: "set_session_title", render: SetSessionTitleRenderer },
|
|
1428
|
+
{ match: "name", name: "set_other_session_title", render: SetSessionTitleRenderer },
|
|
1098
1429
|
];
|
|
1099
1430
|
|
|
1100
1431
|
/** The built-in tool renderer registry: every first-party tool plus a fallback. */
|
|
@@ -375,7 +375,10 @@ export function TurnSummary({
|
|
|
375
375
|
);
|
|
376
376
|
|
|
377
377
|
// Live open shell: keep the chip in-flow (so settle never inserts layout)
|
|
378
|
-
//
|
|
378
|
+
// and quieter until there is an outcome or a settle beat — but still
|
|
379
|
+
// clickable. `pointer-events-none` here trapped readers who expanded (or
|
|
380
|
+
// who landed on the default-open live rail) with no way to collapse while
|
|
381
|
+
// the turn was still running.
|
|
379
382
|
const liveShell = outcome === undefined && open && !settlePhase && !bare;
|
|
380
383
|
// Settle CSS phase OR cancel-close latch — see useTurnSettleOpen.
|
|
381
384
|
// Nested chips stay force-open for this window (stable height).
|
|
@@ -419,7 +422,7 @@ export function TurnSummary({
|
|
|
419
422
|
outcome === "failed"
|
|
420
423
|
? "hover:bg-og-status-failed/[0.06] hover:text-og-fg"
|
|
421
424
|
: "hover:bg-og-surface-1 hover:text-og-fg",
|
|
422
|
-
liveShell && "
|
|
425
|
+
liveShell && "text-og-fg-subtle",
|
|
423
426
|
)}
|
|
424
427
|
>
|
|
425
428
|
{/* Disclosure grammar matches the rows: chevron leads (far left), then any
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Document event hosts dispatch to open the workstream Pause/Resume surface.
|
|
3
|
+
* Kept in a leaf module so session chrome can import the name without pulling
|
|
4
|
+
* the ChatComposer / Radix tooltip graph into the app shell.
|
|
5
|
+
*/
|
|
6
|
+
export const OPEN_WORKSTREAM_CONTROL_EVENT = "opengeni:open-workstream-control";
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/lib/format.ts"],"sourcesContent":["import { OpenGeniApiError } from \"@opengeni/sdk\";\n\n/** Compact relative time: \"now\", \"42s\", \"7m\", \"3h\", \"2d\", then a date. */\nexport function formatRelativeTime(iso: string, now: Date = new Date()): string {\n const then = new Date(iso).getTime();\n if (Number.isNaN(then)) {\n return \"\";\n }\n const seconds = Math.max(0, Math.floor((now.getTime() - then) / 1000));\n if (seconds < 10) {\n return \"now\";\n }\n if (seconds < 60) {\n return `${seconds}s`;\n }\n const minutes = Math.floor(seconds / 60);\n if (minutes < 60) {\n return `${minutes}m`;\n }\n const hours = Math.floor(minutes / 60);\n if (hours < 24) {\n return `${hours}h`;\n }\n const days = Math.floor(hours / 24);\n if (days < 14) {\n return `${days}d`;\n }\n return new Date(iso).toLocaleDateString();\n}\n\n/** Human-readable byte size: \"512 B\", \"8.0 KB\", \"1.4 MB\", \"3 GB\". */\nexport function formatBytes(bytes: number): string {\n if (bytes < 1024) {\n return `${bytes} B`;\n }\n const units = [\"KB\", \"MB\", \"GB\"] as const;\n let value = bytes / 1024;\n for (const unit of units) {\n if (value < 1024 || unit === \"GB\") {\n return `${value.toFixed(value < 10 ? 1 : 0)} ${unit}`;\n }\n value /= 1024;\n }\n return `${bytes} B`;\n}\n\n/** Single-line preview of arbitrary text, for tiles and collapsed rows. */\nexport function truncate(text: string, maxLength: number): string {\n const collapsed = text.replace(/\\s+/g, \" \").trim();\n if (collapsed.length <= maxLength) {\n return collapsed;\n }\n return `${collapsed.slice(0, Math.max(0, maxLength - 1)).trimEnd()}…`;\n}\n\n/** Render an unknown payload as readable text (pretty JSON when possible). */\nexport function stringifyPayload(value: unknown): string {\n if (value === null || value === undefined) {\n return \"\";\n }\n if (typeof value === \"string\") {\n const parsed = tryParseJson(value);\n if (parsed !== undefined && typeof parsed === \"object\") {\n return stringifyPayload(parsed);\n }\n return value;\n }\n try {\n return JSON.stringify(value, null, 2) ?? String(value);\n } catch {\n return String(value);\n }\n}\n\n/** JSON.parse that returns `undefined` instead of throwing. */\nexport function tryParseJson(text: string): unknown {\n const trimmed = text.trim();\n if (!trimmed.startsWith(\"{\") && !trimmed.startsWith(\"[\") && !trimmed.startsWith('\"')) {\n return undefined;\n }\n try {\n return JSON.parse(trimmed) as unknown;\n } catch {\n return undefined;\n }\n}\n\n/**\n * The canonical credit-death sentence. Credit exhaustion is the one failure a\n * user can fix themselves — the copy must say what happened (empty balance),\n * what to do (add credits), and what is safe (nothing was lost). Crucially it\n * must NOT say \"send a message to revive\": a revive turn burns credits the\n * workspace no longer has.\n */\nexport const CREDIT_EXHAUSTION_MESSAGE =\n \"Out of OpenGeni credits — this workspace's balance is empty. Add credits to continue; the conversation is preserved.\";\n\n/**\n * Actionable composer copy for an edge rejection before a turn is accepted.\n * Unlike an in-flight credit exhaustion, this path has not consumed the\n * actor-private draft or any finalized attachment.\n */\nexport const COMPOSER_PAYMENT_REQUIRED_MESSAGE =\n \"This turn requires OpenGeni managed credits, but the account balance is empty. Add credits or choose a connected Codex subscription model, then retry. Your draft and attachments are preserved.\";\n\nexport function composerSubmissionErrorMessage(error: Error): string {\n return error instanceof OpenGeniApiError &&\n error.status === 402 &&\n error.code === \"payment_required\"\n ? COMPOSER_PAYMENT_REQUIRED_MESSAGE\n : error.message;\n}\n\n/**\n * Does this failure/completion payload (or raw error string) mean the\n * workspace ran out of OpenGeni credits? Matches the engine's\n * \"insufficient OpenGeni credits\" text (case-insensitive, substring — it\n * arrives both bare and wrapped in \"Activity task failed: …\") and the\n * budget-exhausted segment limit the engine stamps on a turn it ended early.\n */\nexport function isCreditExhaustion(\n input: { error?: string | null; detail?: string | null; segmentLimit?: string | null } | string,\n): boolean {\n if (typeof input === \"string\") {\n return input.toLowerCase().includes(\"insufficient opengeni credits\");\n }\n if (input.segmentLimit === \"budget_exhausted\") {\n return true;\n }\n for (const text of [input.error, input.detail]) {\n if (typeof text === \"string\" && text.toLowerCase().includes(\"insufficient opengeni credits\")) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * Humanize engine/provider failure text before it reaches the timeline or a\n * failure banner. Raw provider errors leak the wrong audience's instructions —\n * \"Incorrect API key … find your API key at platform.openai.com\" tells a\n * managed-deployment USER to fix credentials only an OPERATOR controls (and is\n * flatly wrong for Azure or subscription-backed engines). Auth, quota, and\n * credit-exhaustion failures collapse to one neutral, honest sentence; every\n * other reason passes through untouched. Raw payloads stay available in the\n * debug surfaces.\n */\nexport function humanizeFailureReason(reason: string | null): string | null {\n if (!reason) {\n return reason;\n }\n if (isCreditExhaustion(reason)) {\n return CREDIT_EXHAUSTION_MESSAGE;\n }\n const normalized = reason.toLowerCase();\n const authFailure =\n normalized.includes(\"incorrect api key\") ||\n normalized.includes(\"invalid api key\") ||\n normalized.includes(\"invalid_api_key\") ||\n normalized.includes(\"platform.openai.com/account/api-keys\") ||\n (normalized.includes(\"401\") &&\n (normalized.includes(\"api key\") || normalized.includes(\"unauthorized\")));\n if (authFailure) {\n return \"The model provider rejected this deployment's engine credentials. Sending messages won't help until the deployment's engine configuration is fixed.\";\n }\n const quotaFailure =\n normalized.includes(\"insufficient_quota\") || normalized.includes(\"exceeded your current quota\");\n if (quotaFailure) {\n return \"The model provider refused the request: this deployment's provider quota is exhausted.\";\n }\n return reason;\n}\n"],"mappings":"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","names":[]}
|