@yieldfabric/terminal 1.0.0 → 1.1.1

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.js CHANGED
@@ -1,8 +1,15 @@
1
1
  "use client";
2
- import React12, { createContext, memo, useMemo, useRef, useEffect, useCallback, useContext, useState, useReducer } from 'react';
2
+ /*!
3
+ * Copyright (c) 2026 YieldFabric Technologies Ltd, DIFC, UAE.
4
+ * All rights reserved. Proprietary and confidential.
5
+ * Source review requires written authorization; see package LICENSE.md and NOTICE.md.
6
+ * Runtime use requires a separate express license.
7
+ */
8
+ import React15, { createContext, memo, useMemo, useRef, useEffect, useCallback, useContext, useState, useReducer } from 'react';
3
9
  import { X, Check, Plus, FileText, MessageCircle, ChevronRight, Paperclip, FolderOpen, Brain, XCircle, Loader2, Minimize2, Maximize2, AlertTriangle, HelpCircle, ChevronLeft, Pencil, ChevronDown, ExternalLink, Search, Network, Boxes, Settings, Terminal, CheckCircle, ArrowRight, Users, Eye, AlertCircle, CheckCircle2, Copy, Zap, Download, Trash2, Link2 } from 'lucide-react';
4
10
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
5
11
  import ReactMarkdown from 'react-markdown';
12
+ import remarkGfm from 'remark-gfm';
6
13
  import { createAvatar } from '@dicebear/core';
7
14
  import { avataaars } from '@dicebear/collection';
8
15
  import { motion } from 'framer-motion';
@@ -68,6 +75,7 @@ var BUILT_IN_COMMANDS = [
68
75
  { id: "files", label: "/files", description: "List uploaded files for this page working group", category: "built-in" },
69
76
  { id: "ask", label: "/ask", description: "Ask the AI assistant for analysis (streaming)", category: "built-in" },
70
77
  { id: "note", label: "/note", description: "Save a free-form note in this workspace. Type @name to link an entity.", category: "built-in" },
78
+ { id: "log", label: "/log", description: "Capture what you did or decided as a work item on this workspace\u2019s board.", category: "built-in" },
71
79
  { id: "personal", label: "/personal", description: "Save a free-form note in your personal notebook (visible only to you, even from a team workspace).", category: "built-in" }
72
80
  ];
73
81
  function getBuiltInCommands() {
@@ -89,23 +97,45 @@ function processStreamLines(buffer, onJsonLine) {
89
97
  for (const line of lines) {
90
98
  const trimmed = line.trim();
91
99
  if (!trimmed) continue;
92
- if (trimmed.startsWith("data: ")) {
93
- const jsonStr = trimmed.substring(6);
100
+ if (trimmed.startsWith("data:")) {
101
+ const jsonStr = trimmed.substring(5).trimStart();
102
+ let parsed2;
94
103
  try {
95
- onJsonLine(JSON.parse(jsonStr));
104
+ parsed2 = JSON.parse(jsonStr);
96
105
  } catch {
106
+ continue;
97
107
  }
108
+ onJsonLine(parsed2);
98
109
  continue;
99
110
  }
100
111
  if (trimmed.startsWith(":")) continue;
112
+ let parsed;
101
113
  try {
102
- onJsonLine(JSON.parse(trimmed));
114
+ parsed = JSON.parse(trimmed);
103
115
  } catch {
116
+ continue;
104
117
  }
118
+ onJsonLine(parsed);
105
119
  }
106
120
  return remainder;
107
121
  }
108
122
 
123
+ // src/utils/streamStall.ts
124
+ var SSE_KEEPALIVE_MS = 15e3;
125
+ var SSE_STALL_MS = SSE_KEEPALIVE_MS * 3;
126
+ async function withStallTimeout(work, ms = SSE_STALL_MS, sleep = (d) => new Promise((wake) => setTimeout(wake, d))) {
127
+ let done = false;
128
+ const settled = work.then((value) => {
129
+ done = true;
130
+ return value;
131
+ });
132
+ const stalled = sleep(ms).then(() => {
133
+ if (!done) throw new Error("the connection went quiet");
134
+ return void 0;
135
+ });
136
+ return Promise.race([settled, stalled]);
137
+ }
138
+
109
139
  // src/utils/streamingHandler.ts
110
140
  async function handleStreamingResponse(url, requestBody, onChunk, onComplete, onError) {
111
141
  try {
@@ -147,6 +177,14 @@ async function handleStreamingResponse(url, requestBody, onChunk, onComplete, on
147
177
  var handleSSEStream = async (url, body, onChunk, onComplete, onError, options) => {
148
178
  const signal = options?.signal;
149
179
  const method = options?.method || "POST";
180
+ const stallMs = options?.stallMs ?? SSE_STALL_MS;
181
+ const stallAbort = new AbortController();
182
+ const abortFromCaller = () => stallAbort.abort();
183
+ let reader;
184
+ if (signal) {
185
+ if (signal.aborted) stallAbort.abort();
186
+ else signal.addEventListener("abort", abortFromCaller, { once: true });
187
+ }
150
188
  try {
151
189
  const requestHeaders = {
152
190
  Accept: "text/event-stream",
@@ -154,12 +192,12 @@ var handleSSEStream = async (url, body, onChunk, onComplete, onError, options) =
154
192
  ...method !== "GET" && method !== "HEAD" ? { "Content-Type": "application/json" } : {},
155
193
  ...options?.headers
156
194
  };
157
- const response = await fetch(url, {
195
+ const response = await (options?.fetcher ?? fetch)(url, {
158
196
  method,
159
197
  headers: requestHeaders,
160
198
  body: method === "GET" || method === "HEAD" ? void 0 : JSON.stringify(body),
161
199
  cache: "no-store",
162
- signal
200
+ signal: stallAbort.signal
163
201
  });
164
202
  if (response.status === 410 && options?.onGone) {
165
203
  options.onGone(await response.text());
@@ -169,19 +207,32 @@ var handleSSEStream = async (url, body, onChunk, onComplete, onError, options) =
169
207
  const errorText = await response.text();
170
208
  throw new Error(`HTTP ${response.status}: ${errorText}`);
171
209
  }
172
- const reader = response.body?.getReader();
210
+ reader = response.body?.getReader();
173
211
  if (!reader) {
174
212
  throw new Error("No response body reader available");
175
213
  }
176
214
  const decoder = new TextDecoder();
177
215
  let buffer = "";
178
216
  let receivedData = false;
217
+ let finalSeen = false;
218
+ let failed = false;
219
+ const dispatch = (chunk) => {
220
+ if (failed) return;
221
+ if (chunk.error) {
222
+ failed = true;
223
+ onError(typeof chunk.error === "string" ? chunk.error : "The assistant could not finish this answer.");
224
+ return;
225
+ }
226
+ if (chunk.is_final === true) finalSeen = true;
227
+ onChunk(chunk);
228
+ };
179
229
  while (true) {
180
230
  let readResult;
181
231
  try {
182
- readResult = await reader.read();
232
+ readResult = stallMs > 0 ? await withStallTimeout(reader.read(), stallMs) : await reader.read();
183
233
  } catch (readError) {
184
234
  if (signal?.aborted) return;
235
+ stallAbort.abort();
185
236
  console.warn(
186
237
  "SSE reader error (connection may have been interrupted):",
187
238
  readError.message
@@ -195,16 +246,31 @@ var handleSSEStream = async (url, body, onChunk, onComplete, onError, options) =
195
246
  }
196
247
  const { done, value } = readResult;
197
248
  if (done) {
198
- onComplete();
249
+ buffer += decoder.decode();
250
+ if (buffer.trim()) processStreamLines(buffer + "\n", dispatch);
251
+ if (failed) return;
252
+ if ((options?.requireFinal ?? true) && !finalSeen) {
253
+ onError("Connection interrupted before the answer was complete. The assistant may still be working.");
254
+ } else onComplete();
199
255
  break;
200
256
  }
201
257
  buffer += decoder.decode(value, { stream: true });
202
258
  receivedData = true;
203
- buffer = processStreamLines(buffer, onChunk);
259
+ buffer = processStreamLines(buffer, dispatch);
260
+ if (failed) return;
204
261
  }
205
262
  } catch (error) {
206
263
  if (signal?.aborted) return;
207
264
  onError(error.message || "Streaming error");
265
+ } finally {
266
+ signal?.removeEventListener("abort", abortFromCaller);
267
+ if (reader) {
268
+ try {
269
+ await reader.cancel();
270
+ } catch {
271
+ }
272
+ reader.releaseLock();
273
+ }
208
274
  }
209
275
  };
210
276
 
@@ -850,7 +916,7 @@ function ScopedReasoningProvider({
850
916
  () => ({ hasOwnedKgId }),
851
917
  [hasOwnedKgId]
852
918
  );
853
- return React12.createElement(
919
+ return React15.createElement(
854
920
  ScopedReasoningContext.Provider,
855
921
  { value },
856
922
  children
@@ -1079,12 +1145,13 @@ function ShareButton({ content, title }) {
1079
1145
  "button",
1080
1146
  {
1081
1147
  onClick: handleShare,
1082
- className: "p-1 rounded-md transition-all duration-200 hover:scale-110",
1148
+ className: "inline-flex h-6 w-6 items-center justify-center rounded-md transition-all duration-200 hover:scale-110",
1083
1149
  style: {
1084
1150
  backgroundColor: done ? "rgba(74,222,128,0.15)" : t.bgRaisedHover,
1085
1151
  color: done ? t.success : t.textMuted
1086
1152
  },
1087
1153
  title: state === "copied" ? "Copied!" : state === "shared" ? "Shared!" : "Share",
1154
+ "aria-label": state === "copied" ? "Link copied" : state === "shared" ? "Shared" : "Share message",
1088
1155
  children: done ? /* @__PURE__ */ jsx("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx("path", { d: "M5 12l5 5L20 7" }) }) : /* @__PURE__ */ jsxs("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
1089
1156
  /* @__PURE__ */ jsx("path", { d: "M4 12v7a2 2 0 002 2h12a2 2 0 002-2v-7" }),
1090
1157
  /* @__PURE__ */ jsx("polyline", { points: "16 6 12 2 8 6" }),
@@ -1093,11 +1160,30 @@ function ShareButton({ content, title }) {
1093
1160
  }
1094
1161
  );
1095
1162
  }
1163
+
1164
+ // src/utils/stripInternalIds.ts
1165
+ var UUID = "[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}";
1166
+ var PREFIXED = `(?:KG|DOC|FRAME|SET|NODE|CHUNK):${UUID}`;
1167
+ var WRAP = "[*_`]*";
1168
+ var PAREN_PREFIXED = new RegExp(`\\s*\\(\\s*${WRAP}${PREFIXED}${WRAP}\\s*\\)`, "g");
1169
+ var PAREN_BARE = new RegExp(`\\s*\\(\\s*${WRAP}${UUID}${WRAP}\\s*\\)`, "g");
1170
+ var BARE_PREFIXED = new RegExp(`${WRAP}${PREFIXED}${WRAP}`, "g");
1171
+ var ID_PHRASE = new RegExp(
1172
+ `\\s*(?:\\(\\s*)?(?:for\\s+|with\\s+)?(?:suggestion|proposal|item|thread|entity|message|document)\\s+id[:\\s]+${WRAP}${UUID}${WRAP}(?:\\s*\\))?`,
1173
+ "gi"
1174
+ );
1175
+ var BARE_UUID = new RegExp(`${WRAP}${UUID}${WRAP}`, "g");
1176
+ function stripInternalIds(text) {
1177
+ if (!text || !/(?:KG|DOC|FRAME|SET|NODE|CHUNK):|-[0-9a-fA-F]{4}-/.test(text)) return text;
1178
+ return text.split(/(```[\s\S]*?```)/g).map(
1179
+ (part) => part.startsWith("```") ? part : part.replace(PAREN_PREFIXED, "").replace(PAREN_BARE, "").replace(BARE_PREFIXED, "").replace(ID_PHRASE, "").replace(BARE_UUID, "").replace(/[ \t]+([,.;:)])/g, "$1").replace(/([^\n ]) {2,}(?=\S)/g, "$1 ")
1180
+ ).join("");
1181
+ }
1096
1182
  function TimestampRenderer({ timestamp }) {
1097
1183
  const t = useTheme();
1098
1184
  const [timeString, setTimeString] = useState("");
1099
1185
  useEffect(() => {
1100
- setTimeString(timestamp.toLocaleTimeString());
1186
+ setTimeString(timestamp.toLocaleTimeString([], { hour: "numeric", minute: "2-digit" }));
1101
1187
  }, [timestamp]);
1102
1188
  return /* @__PURE__ */ jsx("div", { className: "text-xs ml-6 mt-1", style: { color: t.textMuted }, children: timeString });
1103
1189
  }
@@ -1118,20 +1204,20 @@ async function gql(url, token, query, variables) {
1118
1204
  return res.json();
1119
1205
  }
1120
1206
  function InteractiveActionCard({
1121
- action,
1207
+ action: action2,
1122
1208
  onExecute,
1123
1209
  graphqlUrl = "",
1124
1210
  authToken = ""
1125
1211
  }) {
1126
1212
  const t = useTheme();
1127
1213
  const initialValues = useMemo(() => {
1128
- const amount2 = action.metadata?.formatted_amount || (action.payload?.arguments?.amount && action.payload.arguments.amount !== "0" ? action.payload.arguments.amount : "") || "";
1129
- const assetId = action.payload?.arguments?.asset_id || "";
1130
- const entityId = action.payload?.arguments?.destination_id || "";
1131
- const walletId = action.payload?.arguments?.destination_wallet_id || "";
1214
+ const amount2 = action2.metadata?.formatted_amount || (action2.payload?.arguments?.amount && action2.payload.arguments.amount !== "0" ? action2.payload.arguments.amount : "") || "";
1215
+ const assetId = action2.payload?.arguments?.asset_id || "";
1216
+ const entityId = action2.payload?.arguments?.destination_id || "";
1217
+ const walletId = action2.payload?.arguments?.destination_wallet_id || "";
1132
1218
  return { amount: amount2, assetId, entityId, walletId };
1133
- }, [action]);
1134
- const [editedAction, setEditedAction] = useState(action);
1219
+ }, [action2]);
1220
+ const [editedAction, setEditedAction] = useState(action2);
1135
1221
  const [entities, setEntities] = useState([]);
1136
1222
  const [wallets, setWallets] = useState([]);
1137
1223
  const [assets, setAssets] = useState([]);
@@ -1245,14 +1331,14 @@ function InteractiveActionCard({
1245
1331
  [assets, entities, wallets]
1246
1332
  );
1247
1333
  useEffect(() => {
1248
- if (action.payload?.tool_name === "yieldfabric/send_instant_payment") {
1334
+ if (action2.payload?.tool_name === "yieldfabric/send_instant_payment") {
1249
1335
  fetchEntities();
1250
1336
  fetchAssets();
1251
1337
  if (initialValues.entityId) fetchWallets(initialValues.entityId);
1252
1338
  }
1253
1339
  }, []);
1254
1340
  useEffect(() => {
1255
- if (selectedEntityId && action.payload?.tool_name === "yieldfabric/send_instant_payment") {
1341
+ if (selectedEntityId && action2.payload?.tool_name === "yieldfabric/send_instant_payment") {
1256
1342
  const shouldFetch = selectedEntityId !== initialValues.entityId || wallets.length === 0;
1257
1343
  if (shouldFetch) fetchWallets(selectedEntityId);
1258
1344
  if (selectedEntityId !== initialValues.entityId) {
@@ -1262,14 +1348,14 @@ function InteractiveActionCard({
1262
1348
  }
1263
1349
  }, [selectedEntityId]);
1264
1350
  useEffect(() => {
1265
- if (action.payload?.tool_name !== "yieldfabric/send_instant_payment") return;
1351
+ if (action2.payload?.tool_name !== "yieldfabric/send_instant_payment") return;
1266
1352
  if (initialValues.assetId && assets.length > 0) {
1267
1353
  const match = assets.find((a) => a.id === initialValues.assetId) || assets.find(
1268
1354
  (a) => a.id.toUpperCase() === initialValues.assetId.toUpperCase()
1269
1355
  ) || assets.find(
1270
1356
  (a) => initialValues.assetId.toUpperCase() === "CBDC" && a.name?.toUpperCase().includes("CBDC")
1271
- ) || action.metadata?.asset_id && assets.find(
1272
- (a) => a.id === action.metadata.asset_id || a.id.toUpperCase() === action.metadata.asset_id.toUpperCase()
1357
+ ) || action2.metadata?.asset_id && assets.find(
1358
+ (a) => a.id === action2.metadata.asset_id || a.id.toUpperCase() === action2.metadata.asset_id.toUpperCase()
1273
1359
  );
1274
1360
  if (match && selectedAssetId !== match.id) {
1275
1361
  setSelectedAssetId(match.id);
@@ -1293,11 +1379,11 @@ function InteractiveActionCard({
1293
1379
  updateActionField("amount", initialValues.amount);
1294
1380
  }
1295
1381
  }, [assets, entities, wallets]);
1296
- if (action.payload?.tool_name === "yieldfabric/swap_credit_for_cash") {
1382
+ if (action2.payload?.tool_name === "yieldfabric/swap_credit_for_cash") {
1297
1383
  return /* @__PURE__ */ jsx(
1298
1384
  RedeemCreditCard,
1299
1385
  {
1300
- action,
1386
+ action: action2,
1301
1387
  onExecute,
1302
1388
  graphqlUrl,
1303
1389
  authToken,
@@ -1308,20 +1394,20 @@ function InteractiveActionCard({
1308
1394
  }
1309
1395
  );
1310
1396
  }
1311
- if (action.payload?.tool_name === "yieldfabric/complete_swap") {
1312
- return /* @__PURE__ */ jsx(CompleteSwapCard, { action, onExecute });
1397
+ if (action2.payload?.tool_name === "yieldfabric/complete_swap") {
1398
+ return /* @__PURE__ */ jsx(CompleteSwapCard, { action: action2, onExecute });
1313
1399
  }
1314
- if (action.payload?.tool_name === "yieldfabric/accept_payment") {
1315
- return /* @__PURE__ */ jsx(AcceptPaymentCard, { action, onExecute });
1400
+ if (action2.payload?.tool_name === "yieldfabric/accept_payment") {
1401
+ return /* @__PURE__ */ jsx(AcceptPaymentCard, { action: action2, onExecute });
1316
1402
  }
1317
- if (action.action_type === "file_controls") {
1318
- return /* @__PURE__ */ jsx(FileControlsCard, { action, onExecute });
1403
+ if (action2.action_type === "file_controls") {
1404
+ return /* @__PURE__ */ jsx(FileControlsCard, { action: action2, onExecute });
1319
1405
  }
1320
- if (action.action_type === "download_source") {
1321
- return /* @__PURE__ */ jsx(DownloadSourceCard, { action, onExecute });
1406
+ if (action2.action_type === "download_source") {
1407
+ return /* @__PURE__ */ jsx(DownloadSourceCard, { action: action2, onExecute });
1322
1408
  }
1323
- if (action.payload?.tool_name !== "yieldfabric/send_instant_payment") {
1324
- return /* @__PURE__ */ jsx(GenericActionCard, { action, onExecute });
1409
+ if (action2.payload?.tool_name !== "yieldfabric/send_instant_payment") {
1410
+ return /* @__PURE__ */ jsx(GenericActionCard, { action: action2, onExecute });
1325
1411
  }
1326
1412
  return /* @__PURE__ */ jsxs(
1327
1413
  "div",
@@ -1468,7 +1554,7 @@ function InteractiveActionCard({
1468
1554
  );
1469
1555
  }
1470
1556
  function RedeemCreditCard({
1471
- action,
1557
+ action: action2,
1472
1558
  onExecute,
1473
1559
  graphqlUrl,
1474
1560
  authToken,
@@ -1478,14 +1564,14 @@ function RedeemCreditCard({
1478
1564
  setLoadingObligor
1479
1565
  }) {
1480
1566
  const t = useTheme();
1481
- const metadata = action.metadata || {};
1567
+ const metadata = action2.metadata || {};
1482
1568
  const formattedAmount = metadata.formatted_amount || metadata.amount || "0.00";
1483
1569
  const currency = metadata.currency || "Unknown";
1484
1570
  const obligor = metadata.obligor || metadata.obligor_id || "Unknown";
1485
1571
  const obligorId = metadata.obligor_id || obligor;
1486
1572
  const assetDisplay = formatAssetDisplay(metadata, currency);
1487
1573
  useEffect(() => {
1488
- if (action.payload?.tool_name !== "yieldfabric/swap_credit_for_cash") return;
1574
+ if (action2.payload?.tool_name !== "yieldfabric/swap_credit_for_cash") return;
1489
1575
  if (obligorId === "Unknown" || obligor.includes("@")) return;
1490
1576
  if (metadata.obligor_name && metadata.obligor_name !== obligorId && !obligorId.includes("-"))
1491
1577
  return;
@@ -1516,7 +1602,7 @@ function RedeemCreditCard({
1516
1602
  setLoadingObligor(false);
1517
1603
  }
1518
1604
  })();
1519
- }, [obligorId, action.payload?.tool_name]);
1605
+ }, [obligorId, action2.payload?.tool_name]);
1520
1606
  const obligorDisplay = (() => {
1521
1607
  if (obligorEntity?.name) return obligorEntity.name;
1522
1608
  if (metadata.obligor_name && metadata.obligor_name !== obligorId && metadata.obligor_name !== obligor)
@@ -1568,7 +1654,7 @@ function RedeemCreditCard({
1568
1654
  onClick: (e) => {
1569
1655
  e.preventDefault();
1570
1656
  e.stopPropagation();
1571
- onExecute(action);
1657
+ onExecute(action2);
1572
1658
  },
1573
1659
  className: "w-full py-2 px-4 text-sm font-medium rounded transition-colors flex items-center justify-center gap-2",
1574
1660
  style: { backgroundColor: t.success, color: t.textInverse },
@@ -1583,12 +1669,12 @@ function RedeemCreditCard({
1583
1669
  );
1584
1670
  }
1585
1671
  function CompleteSwapCard({
1586
- action,
1672
+ action: action2,
1587
1673
  onExecute
1588
1674
  }) {
1589
1675
  const t = useTheme();
1590
- const metadata = action.metadata || {};
1591
- const swapId = metadata.swap_id || action.payload?.arguments?.swap_id || "Unknown";
1676
+ const metadata = action2.metadata || {};
1677
+ const swapId = metadata.swap_id || action2.payload?.arguments?.swap_id || "Unknown";
1592
1678
  const initiator = metadata.initiator || "Unknown";
1593
1679
  const status = metadata.status || "PENDING";
1594
1680
  const deadline = metadata.deadline ? new Date(metadata.deadline).toLocaleString() : "N/A";
@@ -1650,7 +1736,7 @@ function CompleteSwapCard({
1650
1736
  onClick: (e) => {
1651
1737
  e.preventDefault();
1652
1738
  e.stopPropagation();
1653
- onExecute(action);
1739
+ onExecute(action2);
1654
1740
  },
1655
1741
  className: "w-full py-2 px-4 text-sm font-medium rounded transition-colors flex items-center justify-center gap-2",
1656
1742
  style: { backgroundColor: t.success, color: t.textInverse },
@@ -1692,11 +1778,11 @@ function PaymentLeg({
1692
1778
  ] });
1693
1779
  }
1694
1780
  function AcceptPaymentCard({
1695
- action,
1781
+ action: action2,
1696
1782
  onExecute
1697
1783
  }) {
1698
1784
  const t = useTheme();
1699
- const metadata = action.metadata || {};
1785
+ const metadata = action2.metadata || {};
1700
1786
  const formattedAmount = metadata.formatted_amount || metadata.amount || "0.00";
1701
1787
  const currency = metadata.currency || "Unknown";
1702
1788
  const from = metadata.from || "Unknown";
@@ -1756,7 +1842,7 @@ function AcceptPaymentCard({
1756
1842
  onClick: (e) => {
1757
1843
  e.preventDefault();
1758
1844
  e.stopPropagation();
1759
- onExecute(action);
1845
+ onExecute(action2);
1760
1846
  },
1761
1847
  className: "w-full py-1.5 px-3 text-xs font-medium rounded transition-colors flex items-center justify-center gap-1.5 mt-auto",
1762
1848
  style: { backgroundColor: t.success, color: t.textInverse },
@@ -1771,12 +1857,12 @@ function AcceptPaymentCard({
1771
1857
  );
1772
1858
  }
1773
1859
  function FileControlsCard({
1774
- action,
1860
+ action: action2,
1775
1861
  onExecute
1776
1862
  }) {
1777
1863
  const t = useTheme();
1778
- const title = action.payload?.title || "Document";
1779
- const documentId = action.payload?.document_id;
1864
+ const title = action2.payload?.title || "Document";
1865
+ const documentId = action2.payload?.document_id;
1780
1866
  const doDownload = (e) => {
1781
1867
  e.preventDefault();
1782
1868
  e.stopPropagation();
@@ -1835,18 +1921,18 @@ function FileControlsCard({
1835
1921
  );
1836
1922
  }
1837
1923
  function DownloadSourceCard({
1838
- action,
1924
+ action: action2,
1839
1925
  onExecute
1840
1926
  }) {
1841
1927
  const t = useTheme();
1842
- const title = action.payload?.title || action.metadata?.display_text || action.description || "Document";
1928
+ const title = action2.payload?.title || action2.metadata?.display_text || action2.description || "Document";
1843
1929
  return /* @__PURE__ */ jsx(
1844
1930
  "button",
1845
1931
  {
1846
1932
  onClick: (e) => {
1847
1933
  e.preventDefault();
1848
1934
  e.stopPropagation();
1849
- onExecute(action);
1935
+ onExecute(action2);
1850
1936
  },
1851
1937
  className: "relative overflow-hidden w-full p-3 rounded-lg transition-all duration-200 text-left group",
1852
1938
  style: { backgroundColor: t.buttonBg, border: `1px solid ${t.border}` },
@@ -1866,7 +1952,7 @@ function DownloadSourceCard({
1866
1952
  );
1867
1953
  }
1868
1954
  function GenericActionCard({
1869
- action,
1955
+ action: action2,
1870
1956
  onExecute
1871
1957
  }) {
1872
1958
  const t = useTheme();
@@ -1876,14 +1962,14 @@ function GenericActionCard({
1876
1962
  onClick: (e) => {
1877
1963
  e.preventDefault();
1878
1964
  e.stopPropagation();
1879
- onExecute(action);
1965
+ onExecute(action2);
1880
1966
  },
1881
1967
  className: "flex items-center space-x-3 p-3 rounded-lg transition-colors text-left group",
1882
1968
  style: { backgroundColor: GREEN_ACTION.bg, border: GREEN_ACTION.borderStyle },
1883
1969
  onMouseEnter: (e) => e.currentTarget.style.backgroundColor = GREEN_ACTION.bgHover,
1884
1970
  onMouseLeave: (e) => e.currentTarget.style.backgroundColor = GREEN_ACTION.bg,
1885
1971
  children: [
1886
- /* @__PURE__ */ jsx("div", { className: "flex-1 min-w-0", children: /* @__PURE__ */ jsx("div", { className: "font-medium text-sm mb-1.5", style: { color: t.success }, children: action.metadata?.display_text || action.description || `Execute ${action.action_type}` }) }),
1972
+ /* @__PURE__ */ jsx("div", { className: "flex-1 min-w-0", children: /* @__PURE__ */ jsx("div", { className: "font-medium text-sm mb-1.5", style: { color: t.success }, children: action2.metadata?.display_text || action2.description || `Execute ${action2.action_type}` }) }),
1887
1973
  /* @__PURE__ */ jsx(ArrowRight, { className: "w-5 h-5 transition-colors flex-shrink-0 ml-2", style: { color: t.success } })
1888
1974
  ]
1889
1975
  }
@@ -2131,13 +2217,29 @@ function CopyButton({ content }) {
2131
2217
  "button",
2132
2218
  {
2133
2219
  onClick: handleCopy,
2134
- className: "p-1 rounded transition-colors hover:scale-110",
2220
+ className: "inline-flex h-6 w-6 items-center justify-center rounded transition-colors hover:scale-110",
2135
2221
  style: { backgroundColor: t.bgRaisedHover, color: t.textSecondary },
2136
2222
  title: "Copy",
2223
+ "aria-label": copied ? "Copied" : "Copy message",
2137
2224
  children: copied ? /* @__PURE__ */ jsx(Check, { className: "w-3.5 h-3.5", style: { color: t.success } }) : /* @__PURE__ */ jsx(Copy, { className: "w-3.5 h-3.5" })
2138
2225
  }
2139
2226
  );
2140
2227
  }
2228
+ function AgentCompletionNotice({ completion }) {
2229
+ const t = useTheme();
2230
+ if (!completion || completion.stop_reason === "answered") return null;
2231
+ const message = {
2232
+ rounds_exhausted: "Partial answer \u2014 I reached the step limit. Ask a narrower follow-up to continue.",
2233
+ budget_exhausted: "Partial answer \u2014 this question needs more context than fits in one answer. Try narrowing the question.",
2234
+ awaiting_tool_results: "This answer paused before its next step. Ask again to review a fresh answer.",
2235
+ empty: "No answer was returned. Please try again.",
2236
+ failed: "The answer could not be completed. What arrived so far is kept here.",
2237
+ interrupted: "Connection interrupted. The assistant may still be working; check the conversation before sending again.",
2238
+ cancelled: "Stopped. You can change your question and ask again."
2239
+ }[completion.stop_reason];
2240
+ if (!message) return null;
2241
+ return /* @__PURE__ */ jsx("p", { role: "status", className: "mt-2 rounded-md px-3 py-2 text-xs", style: { color: t.textSecondary, backgroundColor: t.bgRaisedAlt, border: `1px solid ${t.border}` }, children: message });
2242
+ }
2141
2243
  var AGENT_PALETTE = [
2142
2244
  "a78bfa",
2143
2245
  // violet-400
@@ -2213,7 +2315,7 @@ function TimeLabel({ timestamp }) {
2213
2315
  const t = useTheme();
2214
2316
  const [timeStr, setTimeStr] = useState("");
2215
2317
  useEffect(() => {
2216
- setTimeStr(timestamp.toLocaleTimeString());
2318
+ setTimeStr(timestamp.toLocaleTimeString([], { hour: "numeric", minute: "2-digit" }));
2217
2319
  }, [timestamp]);
2218
2320
  return /* @__PURE__ */ jsx("span", { className: "text-[10px] mt-0.5", style: { color: t.textSecondary }, children: timeStr });
2219
2321
  }
@@ -2531,7 +2633,10 @@ function hashTypeColor(type) {
2531
2633
  for (let i = 0; i < type.length; i++) h = (h << 5) - h + type.charCodeAt(i) | 0;
2532
2634
  return SOURCE_TYPE_PALETTE[Math.abs(h) % SOURCE_TYPE_PALETTE.length];
2533
2635
  }
2534
- function SourceCard({ source }) {
2636
+ function SourceCard({
2637
+ source,
2638
+ onActivate
2639
+ }) {
2535
2640
  const t = useTheme();
2536
2641
  const [expanded, setExpanded] = useState(false);
2537
2642
  const showType = source.type && source.type.toLowerCase() !== "unknown";
@@ -2539,13 +2644,25 @@ function SourceCard({ source }) {
2539
2644
  const displayLabel = formatSourceLabel(source.label);
2540
2645
  return /* @__PURE__ */ jsxs("div", { className: "rounded", style: { border: `1px solid ${t.border}`, backgroundColor: t.bgRaisedAlt }, children: [
2541
2646
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 px-2.5 py-1.5", children: [
2542
- source.description ? /* @__PURE__ */ jsx("button", { onClick: () => setExpanded(!expanded), className: "flex-shrink-0", children: /* @__PURE__ */ jsx(
2543
- ChevronRight,
2647
+ source.description ? /* @__PURE__ */ jsx(
2648
+ "button",
2544
2649
  {
2545
- className: "w-3 h-3 transition-transform",
2546
- style: { color: t.textMuted, transform: expanded ? "rotate(90deg)" : "none" }
2650
+ type: "button",
2651
+ onClick: (event) => {
2652
+ event.stopPropagation();
2653
+ setExpanded(!expanded);
2654
+ },
2655
+ className: "flex-shrink-0 rounded-sm focus:outline-none focus-visible:ring-2",
2656
+ "aria-label": expanded ? `Collapse ${displayLabel} details` : `Expand ${displayLabel} details`,
2657
+ children: /* @__PURE__ */ jsx(
2658
+ ChevronRight,
2659
+ {
2660
+ className: "w-3 h-3 transition-transform",
2661
+ style: { color: t.textMuted, transform: expanded ? "rotate(90deg)" : "none" }
2662
+ }
2663
+ )
2547
2664
  }
2548
- ) }) : /* @__PURE__ */ jsx("div", { className: "w-3 h-3 flex-shrink-0 flex items-center justify-center", children: /* @__PURE__ */ jsx("div", { className: "w-1.5 h-1.5 rounded-full", style: { backgroundColor: badgeColor } }) }),
2665
+ ) : /* @__PURE__ */ jsx("div", { className: "w-3 h-3 flex-shrink-0 flex items-center justify-center", children: /* @__PURE__ */ jsx("div", { className: "w-1.5 h-1.5 rounded-full", style: { backgroundColor: badgeColor } }) }),
2549
2666
  showType && /* @__PURE__ */ jsx(
2550
2667
  "span",
2551
2668
  {
@@ -2554,11 +2671,28 @@ function SourceCard({ source }) {
2554
2671
  children: source.type
2555
2672
  }
2556
2673
  ),
2557
- /* @__PURE__ */ jsx("span", { className: "text-[11px] truncate flex-1", style: { color: t.text }, children: displayLabel })
2674
+ onActivate ? /* @__PURE__ */ jsx(
2675
+ "button",
2676
+ {
2677
+ type: "button",
2678
+ className: "text-[11px] truncate flex-1 cursor-pointer text-left rounded-sm hover:underline focus:outline-none focus-visible:ring-2",
2679
+ style: { color: t.text },
2680
+ onClick: (event) => {
2681
+ event.stopPropagation();
2682
+ onActivate(source);
2683
+ },
2684
+ "aria-label": `Open ${displayLabel}`,
2685
+ children: displayLabel
2686
+ }
2687
+ ) : /* @__PURE__ */ jsx("span", { className: "text-[11px] truncate flex-1", style: { color: t.text }, children: displayLabel })
2558
2688
  ] }),
2559
2689
  expanded && source.description && /* @__PURE__ */ jsx("div", { className: "px-2.5 pb-2 text-[10px] leading-relaxed", style: { color: t.textSecondary, borderTop: `1px solid ${t.border}` }, children: source.description })
2560
2690
  ] });
2561
2691
  }
2692
+ var REMARK_PLUGINS = [remarkGfm];
2693
+ function isBlockCode(className, text) {
2694
+ return /language-/.test(typeof className === "string" ? className : "") || text.includes("\n");
2695
+ }
2562
2696
  function getMarkdownComponents(t) {
2563
2697
  return {
2564
2698
  h1: ({ node, ...props }) => /* @__PURE__ */ jsx("h1", { className: "text-xl font-bold mt-4 mb-2", style: { color: t.headingText }, ...props }),
@@ -2570,10 +2704,38 @@ function getMarkdownComponents(t) {
2570
2704
  li: ({ node, ...props }) => /* @__PURE__ */ jsx("li", { className: "ml-2", ...props }),
2571
2705
  strong: ({ node, ...props }) => /* @__PURE__ */ jsx("strong", { className: "font-bold", style: { color: t.text }, ...props }),
2572
2706
  em: ({ node, ...props }) => /* @__PURE__ */ jsx("em", { className: "italic", style: { color: t.textSecondary }, ...props }),
2573
- code: ({ node, inline, children, ...props }) => inline ? /* @__PURE__ */ jsx("code", { className: "px-1 py-0.5 rounded font-mono text-xs", style: { backgroundColor: t.codeBg, color: t.codeText }, ...props, children }) : /* @__PURE__ */ jsxs("div", { className: "relative group/code", children: [
2574
- /* @__PURE__ */ jsx(CopyButton, { content: String(children || "") }),
2575
- /* @__PURE__ */ jsx("code", { className: "block p-2 rounded my-2 font-mono text-xs overflow-x-auto", style: { backgroundColor: t.codeBg, color: t.codeText }, ...props, children })
2576
- ] })
2707
+ code: ({ node, inline, children, ...props }) => {
2708
+ const text = String(children ?? "");
2709
+ const isInline = inline ?? !isBlockCode(props.className, text);
2710
+ return isInline ? /* @__PURE__ */ jsx("code", { className: "px-1 py-0.5 rounded font-mono text-xs", style: { backgroundColor: t.codeBg, color: t.codeText }, ...props, children }) : /* @__PURE__ */ jsxs("div", { className: "relative group/code", children: [
2711
+ /* @__PURE__ */ jsx(CopyButton, { content: text }),
2712
+ /* @__PURE__ */ jsx("code", { className: "block p-2 rounded my-2 font-mono text-xs overflow-x-auto", style: { backgroundColor: t.codeBg, color: t.codeText }, ...props, children })
2713
+ ] });
2714
+ },
2715
+ // Tables are why remark-gfm is here. The wrapper is not decoration:
2716
+ // this renders inside a chat rail the user can drag down to ~360px,
2717
+ // and a table holding a term plus a few numbers will exceed that. It
2718
+ // has to scroll INSIDE its own box — without the wrapper the reply
2719
+ // column itself widens and every other line in the conversation
2720
+ // starts scrolling sideways along with it.
2721
+ table: ({ node, ...p }) => /* @__PURE__ */ jsx(
2722
+ "div",
2723
+ {
2724
+ className: "my-2 max-w-full overflow-x-auto rounded",
2725
+ style: { border: `1px solid ${t.codeBg}` },
2726
+ children: /* @__PURE__ */ jsx("table", { className: "w-full border-collapse text-xs", ...p })
2727
+ }
2728
+ ),
2729
+ thead: ({ node, ...p }) => /* @__PURE__ */ jsx("thead", { style: { backgroundColor: t.codeBg }, ...p }),
2730
+ th: ({ node, ...p }) => /* @__PURE__ */ jsx(
2731
+ "th",
2732
+ {
2733
+ className: "px-2.5 py-1.5 text-left font-semibold whitespace-nowrap",
2734
+ style: { color: t.headingText, borderBottom: `1px solid ${t.codeBg}` },
2735
+ ...p
2736
+ }
2737
+ ),
2738
+ td: ({ node, ...p }) => /* @__PURE__ */ jsx("td", { className: "px-2.5 py-1.5 align-top", style: { borderBottom: `1px solid ${t.codeBg}` }, ...p })
2577
2739
  };
2578
2740
  }
2579
2741
  function TerminalEntryRenderer({
@@ -2588,6 +2750,7 @@ function TerminalEntryRenderer({
2588
2750
  authToken,
2589
2751
  onPageNavigation,
2590
2752
  onSourcesActivate,
2753
+ onSourceActivate,
2591
2754
  renderEntryExtra,
2592
2755
  renderEntry,
2593
2756
  widgets,
@@ -2605,7 +2768,7 @@ function TerminalEntryRenderer({
2605
2768
  const isError = entry.type === "error";
2606
2769
  const isSuccess = entry.type === "success";
2607
2770
  const isEmpty = !entry.content || entry.content.trim() === "";
2608
- const useAttribution = isCollaborative && entry.sender && !isSystem && !isEmpty;
2771
+ const useAttribution = (isCollaborative || entry.sender?.type === "agent") && entry.sender && !isSystem && !isEmpty;
2609
2772
  const isGrouped = useAttribution && !!prevEntry?.sender && !!entry.sender && prevEntry.sender.entity_id === entry.sender.entity_id && prevEntry.sender.type === entry.sender.type && !(!prevEntry.content || prevEntry.content.trim() === "") && entry.timestamp.getTime() - prevEntry.timestamp.getTime() < 5 * 60 * 1e3 && prevEntry.type !== "system";
2610
2773
  const beforeWidgets = widgets?.filter((w) => w.position === "before" && w.match(entry)) || [];
2611
2774
  const afterWidgets = widgets?.filter((w) => (!w.position || w.position === "after") && w.match(entry)) || [];
@@ -2618,12 +2781,12 @@ function TerminalEntryRenderer({
2618
2781
  timestamp: entry.timestamp,
2619
2782
  isOwnMessage: entry.sender.entity_id === currentUser?.entity_id,
2620
2783
  grouped: isGrouped,
2621
- children: entry.sender?.type === "agent" ? /* @__PURE__ */ jsxs("div", { className: "text-sm prose prose-invert prose-sm max-w-none group relative", style: { color: t.text }, children: [
2784
+ children: entry.sender?.type === "agent" ? /* @__PURE__ */ jsxs("div", { className: "text-sm prose prose-invert prose-sm max-w-[72ch] group relative", style: { color: t.text }, children: [
2622
2785
  /* @__PURE__ */ jsxs("div", { className: "absolute -top-9 right-0 flex items-center gap-0.5 opacity-0 group-hover:opacity-100 transition-opacity z-10", children: [
2623
2786
  /* @__PURE__ */ jsx(CopyButton, { content: entry.content }),
2624
2787
  /* @__PURE__ */ jsx(ShareButton, { content: entry.content })
2625
2788
  ] }),
2626
- /* @__PURE__ */ jsx(ReactMarkdown, { components: markdownComponents, children: entry.content })
2789
+ /* @__PURE__ */ jsx(ReactMarkdown, { remarkPlugins: REMARK_PLUGINS, components: markdownComponents, children: stripInternalIds(entry.content) })
2627
2790
  ] }) : /* @__PURE__ */ jsxs("div", { className: "text-sm whitespace-pre-wrap group relative", style: { color: isError ? t.error : t.text }, children: [
2628
2791
  /* @__PURE__ */ jsxs("div", { className: "absolute -top-9 right-0 flex items-center gap-0.5 opacity-0 group-hover:opacity-100 transition-opacity z-10", children: [
2629
2792
  /* @__PURE__ */ jsx(CopyButton, { content: entry.content }),
@@ -2634,7 +2797,6 @@ function TerminalEntryRenderer({
2634
2797
  }
2635
2798
  ) : /* @__PURE__ */ jsxs("div", { className: "flex items-start space-x-2", children: [
2636
2799
  isSystem && !isEmpty && /* @__PURE__ */ jsx(Terminal, { className: "w-4 h-4 mt-0.5 flex-shrink-0", style: { color: t.textSystem } }),
2637
- isCommand && !isEmpty && /* @__PURE__ */ jsx(Brain, { className: "w-4 h-4 mt-0.5 flex-shrink-0", style: { color: t.info } }),
2638
2800
  isInfo && !isEmpty && /* @__PURE__ */ jsx(CheckCircle, { className: "w-4 h-4 mt-0.5 flex-shrink-0", style: { color: t.info } }),
2639
2801
  isSuccess && !isEmpty && /* @__PURE__ */ jsx(CheckCircle, { className: "w-4 h-4 mt-0.5 flex-shrink-0", style: { color: t.success } }),
2640
2802
  isError && !isEmpty && /* @__PURE__ */ jsx(XCircle, { className: "w-4 h-4 mt-0.5 flex-shrink-0", style: { color: t.error } }),
@@ -2647,12 +2809,30 @@ function TerminalEntryRenderer({
2647
2809
  /* @__PURE__ */ jsx(
2648
2810
  ReactMarkdown,
2649
2811
  {
2812
+ remarkPlugins: REMARK_PLUGINS,
2650
2813
  components: markdownComponents,
2651
- children: entry.content
2814
+ children: stripInternalIds(entry.content)
2652
2815
  },
2653
2816
  `${entry.id || index}-${entry.content.length}`
2654
2817
  )
2655
- ] }) : /* @__PURE__ */ jsxs("div", { className: "group relative", children: [
2818
+ ] }) : isCommand && !isSystem ? (
2819
+ // What the person typed: a quiet bubble on the right, the way the
2820
+ // console shows it — not a monospace line that reads as a log.
2821
+ /* @__PURE__ */ jsxs("div", { className: "group relative flex w-full flex-1 justify-end", children: [
2822
+ /* @__PURE__ */ jsxs("div", { className: "absolute -top-5 right-0 flex items-center gap-0.5 opacity-0 group-hover:opacity-100 transition-opacity z-10", children: [
2823
+ /* @__PURE__ */ jsx(CopyButton, { content: entry.content }),
2824
+ /* @__PURE__ */ jsx(ShareButton, { content: entry.content })
2825
+ ] }),
2826
+ /* @__PURE__ */ jsx(
2827
+ "p",
2828
+ {
2829
+ className: "max-w-[80%] whitespace-pre-wrap rounded-lg border px-4 py-2.5 text-sm leading-relaxed",
2830
+ style: { color: t.text, borderColor: t.borderStrong, backgroundColor: "rgba(255,255,255,0.05)" },
2831
+ children: entry.content
2832
+ }
2833
+ )
2834
+ ] })
2835
+ ) : /* @__PURE__ */ jsxs("div", { className: "group relative", children: [
2656
2836
  /* @__PURE__ */ jsxs("div", { className: "absolute -top-5 right-0 flex items-center gap-0.5 opacity-0 group-hover:opacity-100 transition-opacity z-10", children: [
2657
2837
  /* @__PURE__ */ jsx(CopyButton, { content: entry.content }),
2658
2838
  /* @__PURE__ */ jsx(ShareButton, { content: entry.content })
@@ -2660,9 +2840,9 @@ function TerminalEntryRenderer({
2660
2840
  /* @__PURE__ */ jsx(
2661
2841
  "pre",
2662
2842
  {
2663
- className: "font-mono text-sm whitespace-pre-wrap",
2843
+ className: "font-mono text-sm whitespace-pre-wrap max-w-[72ch]",
2664
2844
  style: {
2665
- color: isSystem ? t.textSystem : isCommand ? t.textCommand : isSuccess ? t.success : isError ? t.error : t.text
2845
+ color: isSystem ? t.textSystem : isSuccess ? t.success : isError ? t.error : t.text
2666
2846
  },
2667
2847
  children: entry.content
2668
2848
  }
@@ -2793,7 +2973,14 @@ function TerminalEntryRenderer({
2793
2973
  {
2794
2974
  summary: `${entry.sources.length} source${entry.sources.length !== 1 ? "s" : ""}`,
2795
2975
  defaultExpanded: false,
2796
- children: /* @__PURE__ */ jsx("div", { className: "space-y-1 py-1", children: entry.sources.map((src) => /* @__PURE__ */ jsx(SourceCard, { source: src }, src.id)) })
2976
+ children: /* @__PURE__ */ jsx("div", { className: "space-y-1 py-1", children: entry.sources.map((src) => /* @__PURE__ */ jsx(
2977
+ SourceCard,
2978
+ {
2979
+ source: src,
2980
+ onActivate: onSourceActivate ? () => onSourceActivate(src, entry) : void 0
2981
+ },
2982
+ src.id
2983
+ )) })
2797
2984
  }
2798
2985
  )
2799
2986
  }
@@ -2813,11 +3000,11 @@ function TerminalEntryRenderer({
2813
3000
  /* @__PURE__ */ jsx("span", { className: "font-medium text-sm", style: { color: t.success }, children: "Actions" }),
2814
3001
  /* @__PURE__ */ jsx("span", { className: "text-xs", style: { color: t.textMuted }, children: "click to execute" })
2815
3002
  ] }),
2816
- /* @__PURE__ */ jsx("div", { className: "grid grid-cols-1 gap-2", children: entry.suggested_actions.map((action, idx) => /* @__PURE__ */ jsxs("div", { children: [
3003
+ /* @__PURE__ */ jsx("div", { className: "grid grid-cols-1 gap-2", children: entry.suggested_actions.map((action2, idx) => /* @__PURE__ */ jsxs("div", { children: [
2817
3004
  /* @__PURE__ */ jsx(
2818
3005
  InteractiveActionCard,
2819
3006
  {
2820
- action,
3007
+ action: action2,
2821
3008
  graphqlUrl: config.graphqlUrl,
2822
3009
  authToken: authToken || "",
2823
3010
  onExecute: (updatedAction) => {
@@ -2826,15 +3013,15 @@ function TerminalEntryRenderer({
2826
3013
  }
2827
3014
  }
2828
3015
  ),
2829
- action.approval && /* @__PURE__ */ jsx(
3016
+ action2.approval && /* @__PURE__ */ jsx(
2830
3017
  ApprovalTracker,
2831
3018
  {
2832
- approval: action.approval,
3019
+ approval: action2.approval,
2833
3020
  currentUserId: currentUser?.entity_id,
2834
3021
  onApprove: () => {
2835
3022
  window.dispatchEvent(
2836
3023
  new CustomEvent("terminal-approval-decision", {
2837
- detail: { action, decision: "approved", entity_id: currentUser?.entity_id },
3024
+ detail: { action: action2, decision: "approved", entity_id: currentUser?.entity_id },
2838
3025
  bubbles: true
2839
3026
  })
2840
3027
  );
@@ -2842,7 +3029,7 @@ function TerminalEntryRenderer({
2842
3029
  onReject: () => {
2843
3030
  window.dispatchEvent(
2844
3031
  new CustomEvent("terminal-approval-decision", {
2845
- detail: { action, decision: "rejected", entity_id: currentUser?.entity_id },
3032
+ detail: { action: action2, decision: "rejected", entity_id: currentUser?.entity_id },
2846
3033
  bubbles: true
2847
3034
  })
2848
3035
  );
@@ -2890,9 +3077,10 @@ function TerminalEntryRenderer({
2890
3077
  ] });
2891
3078
  const isTurnStart = !!prevEntry && isCommand && prevEntry.type !== "system";
2892
3079
  const defaultRender = () => /* @__PURE__ */ jsxs("div", { className: `${isGrouped ? "" : "mb-5"} ${isTurnStart ? "mt-6" : ""}`, children: [
2893
- beforeWidgets.map((w) => /* @__PURE__ */ jsx(React12.Fragment, { children: w.render({ entry, index }) }, w.name)),
2894
- replaceWidget ? /* @__PURE__ */ jsx(React12.Fragment, { children: replaceWidget.render({ entry, index }) }, replaceWidget.name) : builtInContent,
2895
- afterWidgets.map((w) => /* @__PURE__ */ jsx(React12.Fragment, { children: w.render({ entry, index }) }, w.name)),
3080
+ beforeWidgets.map((w) => /* @__PURE__ */ jsx(React15.Fragment, { children: w.render({ entry, index }) }, w.name)),
3081
+ replaceWidget ? /* @__PURE__ */ jsx(React15.Fragment, { children: replaceWidget.render({ entry, index }) }, replaceWidget.name) : builtInContent,
3082
+ afterWidgets.map((w) => /* @__PURE__ */ jsx(React15.Fragment, { children: w.render({ entry, index }) }, w.name)),
3083
+ /* @__PURE__ */ jsx(AgentCompletionNotice, { completion: entry.metadata?.completion }),
2896
3084
  renderEntryExtra?.(entry, index)
2897
3085
  ] });
2898
3086
  if (renderEntry) {
@@ -2901,6 +3089,9 @@ function TerminalEntryRenderer({
2901
3089
  }
2902
3090
  return defaultRender();
2903
3091
  }
3092
+
3093
+ // src/types/terminal.types.ts
3094
+ var DEFAULT_AGENT_LABEL = "YieldFabric Agent";
2904
3095
  function LiveKGBridge({ progressState, onLiveKGUpdate }) {
2905
3096
  const onUpdateRef = useRef(onLiveKGUpdate);
2906
3097
  onUpdateRef.current = onLiveKGUpdate;
@@ -2929,14 +3120,17 @@ function TerminalShell({
2929
3120
  className = "",
2930
3121
  style,
2931
3122
  showHeader = true,
3123
+ agentLabel = DEFAULT_AGENT_LABEL,
2932
3124
  focusInput,
2933
3125
  setTerminalHistory,
2934
3126
  terminalBodyRef,
2935
3127
  workingGroupContext,
3128
+ showWorkingGroupBanner = true,
2936
3129
  progressState,
2937
3130
  streamTransparency,
2938
3131
  streamNowMs,
2939
3132
  isExecuting,
3133
+ showBusyIndicator,
2940
3134
  handleCancelReasoning,
2941
3135
  onViewKnowledgeGraph,
2942
3136
  onLiveKGUpdate,
@@ -2954,6 +3148,9 @@ function TerminalShell({
2954
3148
  className: `border-0 flex flex-col ${className}`,
2955
3149
  style: { minHeight: "300px", borderRadius: "12px", backgroundColor: "transparent", ...style },
2956
3150
  onClick: () => {
3151
+ if (typeof window !== "undefined" && window.matchMedia?.("(pointer: coarse)").matches) {
3152
+ return;
3153
+ }
2957
3154
  const sel = window.getSelection();
2958
3155
  if (!sel || sel.toString().length === 0) focusInput();
2959
3156
  },
@@ -2972,7 +3169,7 @@ function TerminalShell({
2972
3169
  "button",
2973
3170
  {
2974
3171
  onClick: () => setTerminalHistory([
2975
- { type: "system", content: "YieldFabric Agent", timestamp: /* @__PURE__ */ new Date() },
3172
+ { type: "system", content: agentLabel, timestamp: /* @__PURE__ */ new Date() },
2976
3173
  { type: "system", content: "Terminal cleared.", timestamp: /* @__PURE__ */ new Date() }
2977
3174
  ]),
2978
3175
  className: "hover:opacity-80",
@@ -2990,7 +3187,7 @@ function TerminalShell({
2990
3187
  className: "p-6 flex-1 overflow-y-auto scrollbar-thin scrollbar-thumb-gray-700 scrollbar-track-transparent",
2991
3188
  style: { minHeight: "200px", display: "flex", flexDirection: "column", backgroundColor: "transparent" },
2992
3189
  children: [
2993
- workingGroupContext && /* @__PURE__ */ jsxs(
3190
+ workingGroupContext && showWorkingGroupBanner && /* @__PURE__ */ jsxs(
2994
3191
  "div",
2995
3192
  {
2996
3193
  className: "mb-3 ml-1 mr-1 px-3 py-2 rounded-md text-xs flex items-center justify-between",
@@ -3015,7 +3212,7 @@ function TerminalShell({
3015
3212
  }
3016
3213
  ),
3017
3214
  /* @__PURE__ */ jsx(LiveKGBridge, { progressState, onLiveKGUpdate }),
3018
- isExecuting && !progressState && /* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-2 px-6 py-2 flex-shrink-0 border-t", style: { color: t.text, borderColor: t.border }, children: [
3215
+ isExecuting && !progressState && showBusyIndicator !== false && /* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-2 px-6 py-2 flex-shrink-0 border-t", style: { color: t.text, borderColor: t.border }, children: [
3019
3216
  /* @__PURE__ */ jsxs("div", { className: "flex space-x-1", children: [
3020
3217
  /* @__PURE__ */ jsx("div", { className: "w-2 h-2 rounded-full animate-pulse", style: { backgroundColor: t.info } }),
3021
3218
  /* @__PURE__ */ jsx("div", { className: "w-2 h-2 rounded-full animate-pulse", style: { backgroundColor: t.info, animationDelay: "150ms" } }),
@@ -3090,7 +3287,7 @@ function KGScopeDropdown({ items, onSelect, compact = false }) {
3090
3287
  }
3091
3288
  function ReasoningModeSelector({ current, onChange, compact = false }) {
3092
3289
  const t = useTheme();
3093
- const isLight = t.bgRaised === "#ffffff";
3290
+ const isLight = t.mode ? t.mode === "light" : t.bgRaised === "#ffffff";
3094
3291
  const isReason = current === "reason";
3095
3292
  const label = isReason ? "Reason" : "Auto";
3096
3293
  const title = isReason ? "Force deep reasoning \u2014 click to let the AI decide" : "AI decides \u2014 click to force deep reasoning";
@@ -3145,11 +3342,24 @@ function splitInputByMentions(command, knownMentionNames) {
3145
3342
  segments.push({ kind: "text", value: command.slice(cursor) });
3146
3343
  return segments;
3147
3344
  }
3345
+ var INPUT_MAX_HEIGHT = 120;
3346
+ function fitToContent(input) {
3347
+ resetHeight(input);
3348
+ if (!input.value.trim()) return;
3349
+ const needed = input.scrollHeight;
3350
+ input.style.height = `${Math.min(needed, INPUT_MAX_HEIGHT)}px`;
3351
+ if (needed > INPUT_MAX_HEIGHT) input.style.overflowY = "auto";
3352
+ }
3353
+ function resetHeight(input) {
3354
+ input.style.height = "auto";
3355
+ input.style.overflowY = "";
3356
+ }
3148
3357
  function TerminalInputBar({
3149
3358
  inputRef,
3150
3359
  fileInputRef,
3151
3360
  currentCommand,
3152
3361
  isExecuting,
3362
+ disabled = false,
3153
3363
  placeholder = "Ask anything...",
3154
3364
  onInputChange,
3155
3365
  onKeyDown,
@@ -3172,20 +3382,28 @@ function TerminalInputBar({
3172
3382
  inputBarExtra,
3173
3383
  modeSelector,
3174
3384
  compactToolbar = false,
3175
- submitTitle = "Send"
3385
+ submitTitle = "Send",
3386
+ autoFocus = true
3176
3387
  }) {
3177
3388
  const t = useTheme();
3178
- const isLight = t.bgRaised === "#ffffff";
3389
+ useEffect(() => {
3390
+ if (!autoFocus) return;
3391
+ inputRef.current?.focus({ preventScroll: true });
3392
+ }, []);
3393
+ const [focused, setFocused] = React15.useState(false);
3394
+ const chipsVisible = focused || currentCommand.trim().length > 0;
3395
+ const isTouch = typeof window !== "undefined" && "ontouchstart" in window;
3396
+ const isLight = t.mode ? t.mode === "light" : t.bgRaised === "#ffffff";
3179
3397
  const hasCommand = Boolean(currentCommand.trim());
3180
- const placeholderColor = isLight ? "rgba(30,30,58,0.44)" : "rgba(255,255,255,0.40)";
3181
- const panelBorder = isLight ? "rgba(30,30,58,0.12)" : "rgba(255,255,255,0.10)";
3182
- const panelBg = isLight ? "rgba(255,255,255,0.82)" : "rgba(24,24,24,0.82)";
3183
- const toolbarButtonBg = isLight ? "rgba(30,30,58,0.06)" : "rgba(255,255,255,0.05)";
3184
- const toolbarButtonHover = isLight ? "rgba(30,30,58,0.11)" : "rgba(255,255,255,0.14)";
3185
- const toolbarButtonText = isLight ? "rgba(30,30,58,0.58)" : "rgba(255,255,255,0.42)";
3398
+ const placeholderColor = isLight ? t.textMuted : "rgba(255,255,255,0.52)";
3399
+ const panelBorder = isLight ? t.border : "rgba(255,255,255,0.10)";
3400
+ const panelBg = t.bgInput;
3401
+ const toolbarButtonBg = isLight ? "rgba(255,255,255,0.72)" : "rgba(255,255,255,0.05)";
3402
+ const toolbarButtonHover = isLight ? t.bgRaised : "rgba(255,255,255,0.14)";
3403
+ const toolbarButtonText = isLight ? t.textSecondary : "rgba(255,255,255,0.52)";
3186
3404
  const toolbarButtonStyle = {
3187
3405
  backgroundColor: toolbarButtonBg,
3188
- border: `1px solid ${isLight ? "rgba(30,30,58,0.08)" : "rgba(255,255,255,0.08)"}`,
3406
+ border: `1px solid ${isLight ? t.border : "rgba(255,255,255,0.08)"}`,
3189
3407
  boxShadow: "none",
3190
3408
  color: toolbarButtonText
3191
3409
  };
@@ -3198,8 +3416,11 @@ function TerminalInputBar({
3198
3416
  }
3199
3417
  const el = toolbarRef.current;
3200
3418
  if (!el || typeof ResizeObserver === "undefined") return;
3419
+ let lastWidth = -1;
3201
3420
  const ro = new ResizeObserver((entries) => {
3202
- const w = entries[0]?.contentRect.width ?? 0;
3421
+ const w = Math.round(entries[0]?.contentRect.width ?? 0);
3422
+ if (w === lastWidth) return;
3423
+ lastWidth = w;
3203
3424
  setCompact((prev) => prev ? w < 520 : w < 480);
3204
3425
  });
3205
3426
  ro.observe(el);
@@ -3209,13 +3430,10 @@ function TerminalInputBar({
3209
3430
  useEffect(() => {
3210
3431
  const input = inputRef.current;
3211
3432
  if (!input) return;
3212
- input.style.height = "auto";
3213
- if (currentCommand.trim()) {
3214
- input.style.height = Math.min(input.scrollHeight, 120) + "px";
3215
- }
3433
+ fitToContent(input);
3216
3434
  }, [currentCommand, inputRef]);
3217
3435
  return /* @__PURE__ */ jsxs(Fragment, { children: [
3218
- /* @__PURE__ */ jsx("style", { children: `.yf-input-bar::placeholder { color: ${placeholderColor}; }` }),
3436
+ /* @__PURE__ */ jsx("style", { children: `.yf-input-bar::placeholder { color: ${placeholderColor}; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }` }),
3219
3437
  commandPaletteItems.length > 0 && /* @__PURE__ */ jsxs(
3220
3438
  "div",
3221
3439
  {
@@ -3254,17 +3472,24 @@ function TerminalInputBar({
3254
3472
  /* @__PURE__ */ jsx(
3255
3473
  "div",
3256
3474
  {
3257
- className: `relative z-20 flex-shrink-0 px-3 pb-3 pt-2 transition-opacity duration-300 sm:px-4 ${isExecuting ? "opacity-40 pointer-events-none" : ""}`,
3475
+ className: `relative z-20 flex-shrink-0 px-3 pb-3 pt-2 transition-opacity duration-300 sm:px-4 ${isExecuting || disabled ? "opacity-40 pointer-events-none" : ""}`,
3258
3476
  children: /* @__PURE__ */ jsxs(
3259
3477
  "div",
3260
3478
  {
3261
- className: "rounded-xl border px-3 transition-colors focus-within:border-white/20 sm:px-4",
3479
+ className: `rounded-xl border px-3 transition-[box-shadow,border-color] sm:px-4 ${focused ? "yf-input-focused" : ""}`,
3262
3480
  style: {
3263
3481
  backgroundColor: panelBg,
3264
- backdropFilter: "blur(18px)",
3265
- borderColor: panelBorder,
3266
- boxShadow: isLight ? "0 18px 70px -38px rgba(30,30,58,0.45)" : "0 18px 70px -38px rgba(0,0,0,0.95)",
3267
- WebkitBackdropFilter: "blur(18px)"
3482
+ // No backdrop blur on a touch screen. It is the one compositing
3483
+ // layer between the textarea and the page, and on iPhone the
3484
+ // caret was drawn a line below and a character right of where
3485
+ // typing lands — the caret rect resolved against the wrong
3486
+ // layer — while WebKit's own layout (checked in WebKit 26) puts
3487
+ // it on the line. The box keeps its colour; only the frosted
3488
+ // fabric behind it goes, which at this size is barely a change.
3489
+ backdropFilter: isTouch ? void 0 : "blur(18px)",
3490
+ borderColor: focused ? t.primary : panelBorder,
3491
+ boxShadow: focused ? `0 0 0 3px ${t.primaryMuted || "rgba(5,78,146,0.18)"}` : isLight ? "0 18px 70px -38px rgba(30,30,58,0.45)" : "0 18px 70px -38px rgba(0,0,0,0.95)",
3492
+ WebkitBackdropFilter: isTouch ? void 0 : "blur(18px)"
3268
3493
  },
3269
3494
  children: [
3270
3495
  /* @__PURE__ */ jsx(
@@ -3333,8 +3558,7 @@ function TerminalInputBar({
3333
3558
  onInputChange(`${prefixRaw}${e.target.value}`);
3334
3559
  onCaretChange?.(prefixRaw.length + (e.target.selectionStart ?? e.target.value.length));
3335
3560
  onTyping?.();
3336
- e.target.style.height = "auto";
3337
- e.target.style.height = Math.min(e.target.scrollHeight, 120) + "px";
3561
+ fitToContent(e.target);
3338
3562
  },
3339
3563
  onSelect: (e) => {
3340
3564
  const ta = e.currentTarget;
@@ -3361,17 +3585,34 @@ function TerminalInputBar({
3361
3585
  if (e.key === "Enter" && !e.shiftKey && !e.defaultPrevented) {
3362
3586
  e.preventDefault();
3363
3587
  onSubmit();
3364
- if (inputRef.current) inputRef.current.style.height = "auto";
3588
+ if (inputRef.current) resetHeight(inputRef.current);
3365
3589
  }
3366
3590
  },
3367
- disabled: isExecuting,
3591
+ disabled: isExecuting || disabled,
3592
+ "aria-label": placeholder ?? "Message",
3368
3593
  placeholder: isExecuting ? "Processing..." : placeholder,
3369
3594
  rows: 1,
3370
- className: "yf-input-bar flex-1 min-w-[100px] bg-transparent focus:outline-none text-[16px] leading-6 resize-none overflow-hidden sm:text-[13px] sm:leading-normal",
3595
+ className: "yf-input-bar flex-1 min-w-[100px] bg-transparent focus:outline-none text-[16px] leading-6 resize-none overflow-hidden sm:text-sm sm:leading-5",
3596
+ onFocus: () => {
3597
+ setFocused(true);
3598
+ if (!("ontouchstart" in window)) return;
3599
+ window.setTimeout(() => {
3600
+ const ta = inputRef.current;
3601
+ if (!ta || document.activeElement !== ta) return;
3602
+ let scroller = ta.parentElement;
3603
+ while (scroller && !/(auto|scroll)/.test(getComputedStyle(scroller).overflowY)) scroller = scroller.parentElement;
3604
+ window.scrollBy(0, 1);
3605
+ if (scroller) scroller.scrollTop += 1;
3606
+ window.requestAnimationFrame(() => {
3607
+ window.scrollBy(0, -1);
3608
+ if (scroller) scroller.scrollTop -= 1;
3609
+ });
3610
+ }, 400);
3611
+ },
3612
+ onBlur: () => setFocused(false),
3371
3613
  style: { color: t.text, caretColor: t.cursor },
3372
3614
  spellCheck: false,
3373
3615
  autoComplete: "off",
3374
- autoFocus: true,
3375
3616
  tabIndex: 0
3376
3617
  }
3377
3618
  )
@@ -3380,67 +3621,85 @@ function TerminalInputBar({
3380
3621
  "button",
3381
3622
  {
3382
3623
  type: "submit",
3383
- disabled: !hasCommand || isExecuting,
3384
- className: "h-10 w-10 rounded-xl disabled:opacity-45 disabled:cursor-not-allowed flex items-center justify-center transition-all duration-200 shrink-0 sm:h-8 sm:w-8",
3624
+ disabled: !hasCommand || isExecuting || disabled,
3625
+ className: "h-10 w-10 rounded-xl disabled:opacity-60 disabled:cursor-not-allowed flex items-center justify-center transition-all duration-200 shrink-0 sm:h-8 sm:w-8",
3385
3626
  style: {
3386
- backgroundColor: hasCommand ? "rgba(255,140,66,0.95)" : isLight ? "rgba(30,30,58,0.07)" : "rgba(255,255,255,0.08)",
3387
- color: hasCommand ? "#181818" : isLight ? "rgba(30,30,58,0.58)" : "rgba(255,255,255,0.72)"
3627
+ backgroundColor: isLight ? t.primary : hasCommand ? t.primary : "rgba(255,255,255,0.08)",
3628
+ // `textInverse` is the theme's "text on a filled button".
3629
+ // `bgBase` was used here before and is `transparent` in
3630
+ // every app theme — so the chevron vanished the moment
3631
+ // there was something to send.
3632
+ // In LIGHT mode this is always a solid accent fill with white
3633
+ // on it (6.1:1). It is the app's primary verb and the single
3634
+ // most-pressed control; a pale disc — grey, or a 10% tint —
3635
+ // read as disabled even when it was not. The empty state is
3636
+ // already expressed by `disabled:opacity-60`, so the fill does
3637
+ // not also have to whisper. Dark keeps its white-alpha rest,
3638
+ // where a solid orange disc would shout on near-black.
3639
+ color: isLight ? t.textInverse : hasCommand ? t.textInverse : "rgba(255,255,255,0.72)"
3388
3640
  },
3389
3641
  onMouseEnter: (e) => {
3390
- e.currentTarget.style.backgroundColor = hasCommand ? "#ffb37a" : toolbarButtonHover;
3642
+ if (hasCommand) {
3643
+ e.currentTarget.style.filter = "brightness(1.15)";
3644
+ } else {
3645
+ e.currentTarget.style.backgroundColor = toolbarButtonHover;
3646
+ }
3391
3647
  },
3392
3648
  onMouseLeave: (e) => {
3393
- e.currentTarget.style.backgroundColor = hasCommand ? "rgba(255,140,66,0.95)" : isLight ? "rgba(30,30,58,0.07)" : "rgba(255,255,255,0.08)";
3649
+ e.currentTarget.style.filter = "";
3650
+ e.currentTarget.style.backgroundColor = isLight ? t.primary : hasCommand ? t.primary : "rgba(255,255,255,0.08)";
3394
3651
  },
3395
3652
  title: submitTitle,
3396
3653
  "aria-label": submitTitle,
3397
- children: isExecuting ? /* @__PURE__ */ jsx("div", { className: "w-3 h-3 rounded-full animate-spin", style: { border: `1.5px solid ${hasCommand ? "rgba(24,24,24,0.7)" : t.textMuted}`, borderTopColor: "transparent" } }) : /* @__PURE__ */ jsx(ChevronRight, { className: "h-5 w-5 sm:h-4 sm:w-4" })
3654
+ children: isExecuting ? /* @__PURE__ */ jsx("div", { className: "w-3 h-3 rounded-full animate-spin", style: { border: `1.5px solid ${hasCommand ? t.textInverse : t.textMuted}`, borderTopColor: "transparent" } }) : /* @__PURE__ */ jsx(ChevronRight, { className: "h-5 w-5 sm:h-4 sm:w-4" })
3398
3655
  }
3399
3656
  )
3400
3657
  ] });
3401
3658
  })(),
3402
3659
  /* @__PURE__ */ jsxs("div", { ref: toolbarRef, className: "flex items-center justify-between pt-2.5 gap-2", children: [
3403
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 text-[10px] min-w-0 flex-wrap", children: [
3404
- /* @__PURE__ */ jsx(
3405
- "button",
3406
- {
3407
- type: "button",
3408
- onClick: () => {
3409
- onInputChange("@");
3410
- inputRef.current?.focus();
3411
- },
3412
- title: "Insert @ mention",
3413
- className: `${toolbarIsCompact ? "px-2" : "px-2.5"} py-1 rounded-lg transition-colors font-medium flex-shrink-0`,
3414
- style: toolbarButtonStyle,
3415
- onMouseEnter: (e) => {
3416
- e.currentTarget.style.backgroundColor = toolbarButtonHover;
3417
- },
3418
- onMouseLeave: (e) => {
3419
- e.currentTarget.style.backgroundColor = toolbarButtonBg;
3420
- },
3421
- children: toolbarIsCompact ? "@" : "@ mention"
3422
- }
3423
- ),
3424
- /* @__PURE__ */ jsx(
3425
- "button",
3426
- {
3427
- type: "button",
3428
- onClick: () => {
3429
- onInputChange("/");
3430
- inputRef.current?.focus();
3431
- },
3432
- title: "Insert / command",
3433
- className: `${toolbarIsCompact ? "px-2" : "px-2.5"} py-1 rounded-lg transition-colors font-medium flex-shrink-0`,
3434
- style: toolbarButtonStyle,
3435
- onMouseEnter: (e) => {
3436
- e.currentTarget.style.backgroundColor = toolbarButtonHover;
3437
- },
3438
- onMouseLeave: (e) => {
3439
- e.currentTarget.style.backgroundColor = toolbarButtonBg;
3440
- },
3441
- children: toolbarIsCompact ? "/" : "/ commands"
3442
- }
3443
- ),
3660
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 text-[11px] min-w-0 flex-wrap", children: [
3661
+ chipsVisible && /* @__PURE__ */ jsxs(Fragment, { children: [
3662
+ /* @__PURE__ */ jsx(
3663
+ "button",
3664
+ {
3665
+ type: "button",
3666
+ onClick: () => {
3667
+ onInputChange("@");
3668
+ inputRef.current?.focus();
3669
+ },
3670
+ title: "Insert @ mention",
3671
+ className: `${toolbarIsCompact ? "px-2" : "px-2.5"} py-1 min-h-[40px] sm:min-h-0 inline-flex items-center rounded-lg transition-colors font-semibold flex-shrink-0`,
3672
+ style: toolbarButtonStyle,
3673
+ onMouseEnter: (e) => {
3674
+ e.currentTarget.style.backgroundColor = toolbarButtonHover;
3675
+ },
3676
+ onMouseLeave: (e) => {
3677
+ e.currentTarget.style.backgroundColor = toolbarButtonBg;
3678
+ },
3679
+ children: toolbarIsCompact ? "@" : "@ mention"
3680
+ }
3681
+ ),
3682
+ /* @__PURE__ */ jsx(
3683
+ "button",
3684
+ {
3685
+ type: "button",
3686
+ onClick: () => {
3687
+ onInputChange("/");
3688
+ inputRef.current?.focus();
3689
+ },
3690
+ title: "Insert / command",
3691
+ className: `${toolbarIsCompact ? "px-2" : "px-2.5"} py-1 min-h-[40px] sm:min-h-0 inline-flex items-center rounded-lg transition-colors font-semibold flex-shrink-0`,
3692
+ style: toolbarButtonStyle,
3693
+ onMouseEnter: (e) => {
3694
+ e.currentTarget.style.backgroundColor = toolbarButtonHover;
3695
+ },
3696
+ onMouseLeave: (e) => {
3697
+ e.currentTarget.style.backgroundColor = toolbarButtonBg;
3698
+ },
3699
+ children: toolbarIsCompact ? "/" : "/ commands"
3700
+ }
3701
+ )
3702
+ ] }),
3444
3703
  availableKGs && availableKGs.length > 0 && /* @__PURE__ */ jsx(Fragment, { children: activeKGScope ? /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1.5 px-2 py-0.5 rounded-md text-[10px] min-w-0", title: activeKGScope.label, style: { backgroundColor: "rgba(255,140,66,0.08)", border: "1px solid rgba(255,140,66,0.15)", color: "rgba(255,140,66,0.8)" }, children: [
3445
3704
  /* @__PURE__ */ jsxs("svg", { className: "w-3 h-3 opacity-60 shrink-0", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2, children: [
3446
3705
  /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M13.828 10.172a4 4 0 00-5.656 0l-4 4a4 4 0 105.656 5.656l1.102-1.101" }),
@@ -3454,7 +3713,7 @@ function TerminalInputBar({
3454
3713
  ] }),
3455
3714
  /* @__PURE__ */ jsx("button", { type: "button", onClick: () => onKGScopeChange?.(null), className: "ml-0.5 opacity-40 hover:opacity-100 transition-opacity", children: "\u2715" })
3456
3715
  ] }) : /* @__PURE__ */ jsx(KGScopeDropdown, { items: availableKGs, onSelect: (scope) => onKGScopeChange?.(scope), compact: toolbarIsCompact }) }),
3457
- modeSelector ?? (onReasoningModeChange && /* @__PURE__ */ jsx(ReasoningModeSelector, { current: reasoningMode || null, onChange: onReasoningModeChange, compact: toolbarIsCompact }))
3716
+ chipsVisible && (modeSelector ?? (onReasoningModeChange && /* @__PURE__ */ jsx(ReasoningModeSelector, { current: reasoningMode || null, onChange: onReasoningModeChange, compact: toolbarIsCompact })))
3458
3717
  ] }),
3459
3718
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 flex-shrink-0", children: [
3460
3719
  inputBarExtra,
@@ -3462,8 +3721,8 @@ function TerminalInputBar({
3462
3721
  "button",
3463
3722
  {
3464
3723
  type: "button",
3465
- disabled: isExecuting,
3466
- className: "h-8 w-8 rounded-xl disabled:opacity-30 disabled:cursor-not-allowed flex items-center justify-center transition-all duration-200",
3724
+ disabled: isExecuting || disabled,
3725
+ className: "h-10 w-10 sm:h-8 sm:w-8 rounded-xl disabled:opacity-30 disabled:cursor-not-allowed flex items-center justify-center transition-all duration-200",
3467
3726
  style: toolbarButtonStyle,
3468
3727
  title: "Upload files",
3469
3728
  onClick: () => fileInputRef.current?.click(),
@@ -3480,8 +3739,8 @@ function TerminalInputBar({
3480
3739
  "button",
3481
3740
  {
3482
3741
  type: "button",
3483
- disabled: isExecuting,
3484
- className: "h-8 w-8 rounded-xl disabled:opacity-30 disabled:cursor-not-allowed flex items-center justify-center transition-all duration-200",
3742
+ disabled: isExecuting || disabled,
3743
+ className: "h-10 w-10 sm:h-8 sm:w-8 rounded-xl disabled:opacity-30 disabled:cursor-not-allowed flex items-center justify-center transition-all duration-200",
3485
3744
  style: toolbarButtonStyle,
3486
3745
  title: "List files",
3487
3746
  onClick: () => onListFilesCommand(),
@@ -3512,7 +3771,7 @@ var KIND_DOT = {
3512
3771
  };
3513
3772
  function SlashMentionPicker({ candidates, selectedIndex, onPick }) {
3514
3773
  const t = useTheme();
3515
- const isLight = t.bgRaised === "#ffffff";
3774
+ const isLight = t.mode ? t.mode === "light" : t.bgRaised === "#ffffff";
3516
3775
  if (candidates.length === 0) return null;
3517
3776
  return /* @__PURE__ */ jsxs(
3518
3777
  "div",
@@ -3601,16 +3860,15 @@ function useCommandHistory() {
3601
3860
  }, []);
3602
3861
  return { commandHistory, historyIndex, pushCommand, navigateUp, navigateDown, resetIndex };
3603
3862
  }
3604
- function useFocusManagement() {
3863
+ function useFocusManagement(autoFocus = true) {
3605
3864
  const inputRef = useRef(null);
3606
3865
  const focusInput = useCallback(() => {
3607
3866
  const selection = window.getSelection();
3608
3867
  if (selection && selection.toString().length > 0) return;
3609
- if (inputRef.current) {
3610
- inputRef.current.focus();
3611
- }
3868
+ inputRef.current?.focus({ preventScroll: true });
3612
3869
  }, []);
3613
3870
  useEffect(() => {
3871
+ if (!autoFocus) return;
3614
3872
  const timer = setTimeout(focusInput, 100);
3615
3873
  const handleVisibilityChange = () => {
3616
3874
  if (!document.hidden) {
@@ -3622,11 +3880,12 @@ function useFocusManagement() {
3622
3880
  clearTimeout(timer);
3623
3881
  document.removeEventListener("visibilitychange", handleVisibilityChange);
3624
3882
  };
3625
- }, [focusInput]);
3883
+ }, [focusInput, autoFocus]);
3626
3884
  return { inputRef, focusInput };
3627
3885
  }
3628
3886
  function useStreamingChat({
3629
3887
  config,
3888
+ authToken,
3630
3889
  onStreamRequest,
3631
3890
  reasoningMode,
3632
3891
  setTerminalHistory,
@@ -3635,11 +3894,14 @@ function useStreamingChat({
3635
3894
  currentUser,
3636
3895
  sourceKGNodes
3637
3896
  }) {
3897
+ const authTokenRef = useRef(authToken);
3898
+ authTokenRef.current = authToken;
3638
3899
  const sourceKGNodesRef = useRef(sourceKGNodes);
3639
3900
  sourceKGNodesRef.current = sourceKGNodes;
3640
3901
  const reasoningModeRef = useRef(reasoningMode);
3641
3902
  reasoningModeRef.current = reasoningMode;
3642
3903
  const [isExecuting, setIsExecuting] = useState(false);
3904
+ const turnSequence = useRef(0);
3643
3905
  const [isStreaming, setIsStreaming] = useState(false);
3644
3906
  const [progressState, setProgressState] = useState(null);
3645
3907
  const [activityEvents, setActivityEvents] = useState([]);
@@ -3655,6 +3917,15 @@ function useStreamingChat({
3655
3917
  const currentStreamingLineId = useRef(null);
3656
3918
  const liveRunIdRef = useRef(null);
3657
3919
  liveRunIdRef.current = liveRunId;
3920
+ const mountEpoch = useRef(0);
3921
+ useEffect(() => {
3922
+ const epoch = ++mountEpoch.current;
3923
+ return () => {
3924
+ void Promise.resolve().then(() => {
3925
+ if (mountEpoch.current === epoch) currentStreamingLineId.current = null;
3926
+ });
3927
+ };
3928
+ }, []);
3658
3929
  useEffect(() => {
3659
3930
  progressStateRef.current = progressState;
3660
3931
  }, [progressState]);
@@ -3707,45 +3978,54 @@ function useStreamingChat({
3707
3978
  currentStreamingLineId.current = null;
3708
3979
  setTimeout(() => focusInput(), 50);
3709
3980
  }, [focusInput]);
3710
- const handleHilSubmit = useCallback(
3711
- async (message) => {
3712
- const runId = liveRunIdRef.current;
3713
- if (!runId) return;
3714
- const apiUrl = config.agentsApiUrl || "http://localhost:3001";
3715
- try {
3716
- if (message && message.trim().length > 0) {
3717
- await fetch(`${apiUrl}/pipelines/${runId}/input`, {
3718
- method: "POST",
3719
- headers: { "Content-Type": "application/json" },
3720
- body: JSON.stringify({ kind: "guidance", content: message })
3721
- });
3722
- }
3723
- await fetch(`${apiUrl}/pipelines/${runId}/input`, {
3724
- method: "POST",
3725
- headers: { "Content-Type": "application/json" },
3726
- body: JSON.stringify({ kind: "proceed" })
3727
- });
3728
- } catch (e) {
3729
- console.error("Failed to send pipeline input:", e);
3730
- }
3731
- },
3732
- [config.agentsApiUrl]
3733
- );
3981
+ const postPipelineAction = useCallback(async (runId, action2, body) => {
3982
+ const headers = config.getAuthHeaders?.() ?? (authTokenRef.current ? { Authorization: `Bearer ${authTokenRef.current}` } : {});
3983
+ const response = await fetch(`${config.agentsApiUrl || "http://localhost:3001"}/pipelines/${runId}/${action2}`, {
3984
+ method: "POST",
3985
+ headers: { "Content-Type": "application/json", ...headers },
3986
+ body: body === void 0 ? void 0 : JSON.stringify(body)
3987
+ });
3988
+ if (!response.ok) throw new Error(`The request was not accepted (${response.status}). Please try again.`);
3989
+ }, [config]);
3990
+ const handleHilSubmit = useCallback(async (message) => {
3991
+ const runId = liveRunIdRef.current;
3992
+ if (!runId) return;
3993
+ try {
3994
+ if (message.trim()) await postPipelineAction(runId, "input", { kind: "guidance", content: message });
3995
+ await postPipelineAction(runId, "input", { kind: "proceed" });
3996
+ } catch (error) {
3997
+ addTerminalLine("error", `Your input was not sent. ${error.message}`);
3998
+ }
3999
+ }, [postPipelineAction, addTerminalLine]);
3734
4000
  const handleCancelReasoning = useCallback(async () => {
3735
4001
  const runId = liveRunIdRef.current;
3736
4002
  if (!runId) return;
3737
- const apiUrl = config.agentsApiUrl || "http://localhost:3001";
3738
- setProgressState(null);
3739
- resetStreamState();
3740
4003
  try {
3741
- await fetch(`${apiUrl}/pipelines/${runId}/cancel`, {
3742
- method: "POST",
3743
- headers: { "Content-Type": "application/json" }
3744
- });
3745
- } catch (e) {
3746
- console.error("Failed to cancel pipeline:", e);
4004
+ await postPipelineAction(runId, "cancel");
4005
+ setProgressState(null);
4006
+ resetStreamState();
4007
+ addTerminalLine("info", "Stopped the analysis.");
4008
+ } catch (error) {
4009
+ addTerminalLine("error", `The analysis is still running. ${error.message}`);
3747
4010
  }
3748
- }, [config.agentsApiUrl, resetStreamState]);
4011
+ }, [postPipelineAction, resetStreamState, addTerminalLine]);
4012
+ const settleAgentSteps = useCallback(
4013
+ (lineId) => {
4014
+ setTerminalHistory((prev) => {
4015
+ const idx = prev.findIndex((line) => line.id === lineId);
4016
+ if (idx === -1) return prev;
4017
+ const meta = prev[idx].metadata;
4018
+ if (!meta?.agentStepsLive) return prev;
4019
+ const updated = [...prev];
4020
+ updated[idx] = {
4021
+ ...updated[idx],
4022
+ metadata: { ...updated[idx].metadata || {}, agentStepsLive: false }
4023
+ };
4024
+ return updated;
4025
+ });
4026
+ },
4027
+ [setTerminalHistory]
4028
+ );
3749
4029
  const handleStreamingChat = useCallback(
3750
4030
  async (cmd) => {
3751
4031
  if (isProcessingRef.current || !onStreamRequest) return;
@@ -3757,8 +4037,9 @@ function useStreamingChat({
3757
4037
  const startMs = Date.now();
3758
4038
  setStreamTransparency({ startedAtMs: startMs, lastEventAtMs: startMs });
3759
4039
  setActivityEvents([]);
3760
- const userCommandId = Date.now().toString();
3761
- const streamingLineId = (Date.now() + 1).toString();
4040
+ const turnKey = `${Date.now()}-${++turnSequence.current}`;
4041
+ const userCommandId = `${turnKey}:question`;
4042
+ const streamingLineId = `${turnKey}:answer`;
3762
4043
  currentStreamingLineId.current = streamingLineId;
3763
4044
  const userCommandEntry = {
3764
4045
  id: userCommandId,
@@ -3784,14 +4065,14 @@ function useStreamingChat({
3784
4065
  setTerminalHistory((prev) => [
3785
4066
  ...prev,
3786
4067
  {
3787
- id: (Date.now() + 2).toString(),
4068
+ id: `${turnKey}:before-question`,
3788
4069
  type: "info",
3789
4070
  content: "",
3790
4071
  timestamp: /* @__PURE__ */ new Date()
3791
4072
  },
3792
4073
  userCommandEntry,
3793
4074
  {
3794
- id: (Date.now() + 3).toString(),
4075
+ id: `${turnKey}:before-answer`,
3795
4076
  type: "info",
3796
4077
  content: "",
3797
4078
  timestamp: /* @__PURE__ */ new Date()
@@ -3805,10 +4086,37 @@ function useStreamingChat({
3805
4086
  let sources;
3806
4087
  let kgId;
3807
4088
  let runId;
4089
+ const steps = /* @__PURE__ */ new Map();
4090
+ const fail = (error) => {
4091
+ if (currentStreamingLineId.current !== streamingLineId) return;
4092
+ settleAgentSteps(streamingLineId);
4093
+ updateStreamingLine(
4094
+ streamingLineId,
4095
+ fullResponse,
4096
+ pageSuggestions,
4097
+ suggestedActions,
4098
+ progressStateRef.current ? captureReasoningSnapshot(progressStateRef.current, true) : void 0,
4099
+ pipelineDraft,
4100
+ sources,
4101
+ kgId,
4102
+ runId
4103
+ );
4104
+ setTerminalHistory((prev) => prev.map((entry) => entry.id === streamingLineId ? { ...entry, metadata: { ...entry.metadata, completion: { stop_reason: /connection|interrupt/i.test(error) ? "interrupted" : "failed" } } } : entry));
4105
+ addTerminalLine("error", error);
4106
+ setProgressState(null);
4107
+ resetStreamState();
4108
+ };
3808
4109
  try {
3809
4110
  await onStreamRequest(cmd, {
3810
4111
  onChunk: (chunk) => {
4112
+ if (currentStreamingLineId.current !== streamingLineId) return;
4113
+ if (chunk.entryMetadata) {
4114
+ setTerminalHistory((prev) => prev.map((entry) => entry.id === streamingLineId ? { ...entry, metadata: { ...entry.metadata, ...chunk.entryMetadata } } : entry));
4115
+ }
3811
4116
  const eventMs = Date.now();
4117
+ if (chunk.completion) {
4118
+ setTerminalHistory((prev) => prev.map((entry) => entry.id === streamingLineId ? { ...entry, metadata: { ...entry.metadata, completion: chunk.completion } } : entry));
4119
+ }
3812
4120
  if (chunk.sender) {
3813
4121
  setStreamingLineSender(streamingLineId, chunk.sender);
3814
4122
  if (currentUser) {
@@ -3821,6 +4129,28 @@ function useStreamingChat({
3821
4129
  if (chunk.collaboration) {
3822
4130
  setCollaborationState(chunk.collaboration);
3823
4131
  }
4132
+ if (chunk.step) {
4133
+ steps.set(chunk.step.seq, chunk.step);
4134
+ const ordered = Array.from(steps.values()).sort((a, b) => a.seq - b.seq);
4135
+ setTerminalHistory((prev) => {
4136
+ const idx = prev.findIndex((line) => line.id === streamingLineId);
4137
+ if (idx === -1) return prev;
4138
+ const updated = [...prev];
4139
+ updated[idx] = {
4140
+ ...updated[idx],
4141
+ metadata: {
4142
+ ...updated[idx].metadata || {},
4143
+ agentSteps: ordered,
4144
+ // Explicit rather than inferred from the streaming cursor:
4145
+ // a tool loop runs to completion BEFORE the first answer
4146
+ // token, so during the whole loop the entry's content is
4147
+ // empty and carries no cursor to read.
4148
+ agentStepsLive: true
4149
+ }
4150
+ };
4151
+ return updated;
4152
+ });
4153
+ }
3824
4154
  const ckgId = extractKgId(chunk, chunk.progress);
3825
4155
  const cRunId = chunk.run_id || chunk.progress?.run_id;
3826
4156
  const ctId = chunk.thread_id || chunk.progress?.thread_id || chunk.reasoning_thread_id || chunk.progress?.reasoning_thread_id;
@@ -3964,49 +4294,14 @@ function useStreamingChat({
3964
4294
  kgId,
3965
4295
  runId
3966
4296
  );
4297
+ settleAgentSteps(streamingLineId);
3967
4298
  resetStreamState();
3968
4299
  }
3969
4300
  },
3970
- onError: (error) => {
3971
- if (currentStreamingLineId.current === streamingLineId) {
3972
- if (progressStateRef.current) {
3973
- const snap = captureReasoningSnapshot(
3974
- progressStateRef.current,
3975
- true
3976
- );
3977
- updateStreamingLine(
3978
- streamingLineId,
3979
- fullResponse || "",
3980
- void 0,
3981
- void 0,
3982
- snap
3983
- );
3984
- setProgressState(null);
3985
- } else {
3986
- addTerminalLine("error", `STREAMING ERROR: ${error}`);
3987
- setProgressState(null);
3988
- }
3989
- resetStreamState();
3990
- }
3991
- }
4301
+ onError: fail
3992
4302
  }, reasoningModeRef.current ? { reasoningMode: reasoningModeRef.current } : void 0);
3993
4303
  } catch (error) {
3994
- if (progressStateRef.current && currentStreamingLineId.current) {
3995
- const snap = captureReasoningSnapshot(
3996
- progressStateRef.current,
3997
- true
3998
- );
3999
- updateStreamingLine(
4000
- currentStreamingLineId.current,
4001
- fullResponse || "",
4002
- void 0,
4003
- void 0,
4004
- snap
4005
- );
4006
- }
4007
- addTerminalLine("error", `STREAMING EXCEPTION: ${error.message}`);
4008
- setProgressState(null);
4009
- resetStreamState();
4304
+ fail(error?.message || "The answer could not be completed.");
4010
4305
  }
4011
4306
  },
4012
4307
  [
@@ -4015,7 +4310,9 @@ function useStreamingChat({
4015
4310
  updateStreamingLine,
4016
4311
  setStreamingLineSender,
4017
4312
  resetStreamState,
4018
- addTerminalLine
4313
+ addTerminalLine,
4314
+ settleAgentSteps,
4315
+ currentUser
4019
4316
  ]
4020
4317
  );
4021
4318
  const clearReasoningSession = useCallback(() => {
@@ -4108,6 +4405,7 @@ function useTerminalCore({
4108
4405
  onReasoningModeChangeProp,
4109
4406
  reasoningRef,
4110
4407
  workingGroupContextProp = null,
4408
+ autoFocusInput = true,
4111
4409
  onResolveWorkingGroup,
4112
4410
  onRagFileUpload,
4113
4411
  onListRagFiles,
@@ -4125,12 +4423,13 @@ function useTerminalCore({
4125
4423
  pollIntervalMs,
4126
4424
  authToken
4127
4425
  }) {
4426
+ const agentLabel = config.agentLabel?.trim() || DEFAULT_AGENT_LABEL;
4128
4427
  const [internalReasoningMode, setInternalReasoningMode] = useState(null);
4129
4428
  const reasoningMode = reasoningModeProp !== void 0 ? reasoningModeProp : internalReasoningMode;
4130
4429
  const onReasoningModeChange = onReasoningModeChangeProp || setInternalReasoningMode;
4131
4430
  const [terminalHistory, setTerminalHistory] = useState(() => {
4132
4431
  if (initialHistory && initialHistory.length > 0) return initialHistory;
4133
- return [{ type: "system", content: "YieldFabric Agent", timestamp: /* @__PURE__ */ new Date() }];
4432
+ return [{ type: "system", content: agentLabel, timestamp: /* @__PURE__ */ new Date() }];
4134
4433
  });
4135
4434
  useEffect(() => {
4136
4435
  if (historyRef) {
@@ -4143,7 +4442,7 @@ function useTerminalCore({
4143
4442
  const enrichedHistory = useMemo(() => {
4144
4443
  if (!sourceKGNodes?.length) return terminalHistory;
4145
4444
  let changed = false;
4146
- const result = terminalHistory.map((entry) => {
4445
+ const result2 = terminalHistory.map((entry) => {
4147
4446
  if ((entry.type === "response" || entry.type === "success") && entry.content && entry.content.length > 20 && !entry.sources?.length) {
4148
4447
  const sources = extractKGSources(entry.content, sourceKGNodes);
4149
4448
  if (sources.length > 0) {
@@ -4153,7 +4452,7 @@ function useTerminalCore({
4153
4452
  }
4154
4453
  return entry;
4155
4454
  });
4156
- return changed ? result : terminalHistory;
4455
+ return changed ? result2 : terminalHistory;
4157
4456
  }, [terminalHistory, sourceKGNodes]);
4158
4457
  useEffect(() => {
4159
4458
  if (initialHistory && initialHistory.length > 0) {
@@ -4173,6 +4472,7 @@ function useTerminalCore({
4173
4472
  setTerminalHistory((prev) => [...prev, { id: Date.now().toString(), type: "response", content, timestamp: /* @__PURE__ */ new Date(), suggested_actions: actions }]);
4174
4473
  }, []);
4175
4474
  const [currentCommand, setCurrentCommand] = useState("");
4475
+ const [pendingSubmit, setPendingSubmit] = useState(null);
4176
4476
  const [workingGroupContext, setWorkingGroupContext] = useState(workingGroupContextProp);
4177
4477
  const [hasShownWelcome, setHasShownWelcome] = useState(false);
4178
4478
  const [isInputFocused, setIsInputFocused] = useState(true);
@@ -4185,13 +4485,16 @@ function useTerminalCore({
4185
4485
  for (const ctrl of uploadAbortsRef.current) ctrl.abort();
4186
4486
  uploadAbortsRef.current = [];
4187
4487
  }, []);
4188
- const { inputRef, focusInput } = useFocusManagement();
4488
+ const { inputRef, focusInput } = useFocusManagement(autoFocusInput);
4189
4489
  useEffect(() => {
4190
4490
  function onPrefill(e) {
4191
4491
  const detail = e.detail;
4192
4492
  if (!detail || typeof detail.value !== "string") return;
4193
4493
  const value = detail.value;
4194
4494
  setCurrentCommand(value);
4495
+ if (e.detail?.submit) {
4496
+ setPendingSubmit(value);
4497
+ }
4195
4498
  requestAnimationFrame(() => {
4196
4499
  focusInput();
4197
4500
  const el = inputRef.current;
@@ -4205,6 +4508,9 @@ function useTerminalCore({
4205
4508
  window.removeEventListener("yieldfabric:terminal:prefill", onPrefill);
4206
4509
  };
4207
4510
  }, [focusInput, inputRef]);
4511
+ const restoreInputFocus = useCallback(() => {
4512
+ if (autoFocusInput) focusInput();
4513
+ }, [autoFocusInput, focusInput]);
4208
4514
  const {
4209
4515
  isExecuting,
4210
4516
  setIsExecuting,
@@ -4224,11 +4530,12 @@ function useTerminalCore({
4224
4530
  activityEvents
4225
4531
  } = useStreamingChat({
4226
4532
  config,
4533
+ authToken,
4227
4534
  onStreamRequest,
4228
4535
  reasoningMode,
4229
4536
  setTerminalHistory,
4230
4537
  addTerminalLine,
4231
- focusInput,
4538
+ focusInput: restoreInputFocus,
4232
4539
  currentUser,
4233
4540
  sourceKGNodes
4234
4541
  });
@@ -4291,12 +4598,12 @@ function useTerminalCore({
4291
4598
  }, [onResolveWorkingGroup]);
4292
4599
  useEffect(() => {
4293
4600
  const handleActionResult = (event) => {
4294
- const result = event.detail;
4295
- if (result.success) {
4601
+ const result2 = event.detail;
4602
+ if (result2.success) {
4296
4603
  addTerminalLine("success", "Action executed successfully!");
4297
- if (result.data) addTerminalLine("response", JSON.stringify(result.data, null, 2));
4604
+ if (result2.data) addTerminalLine("response", JSON.stringify(result2.data, null, 2));
4298
4605
  } else {
4299
- addTerminalLine("error", `Action failed: ${result.error || "Unknown error"}`);
4606
+ addTerminalLine("error", `Action failed: ${result2.error || "Unknown error"}`);
4300
4607
  }
4301
4608
  };
4302
4609
  window.addEventListener("terminal-action-result", handleActionResult);
@@ -4308,7 +4615,7 @@ function useTerminalCore({
4308
4615
  const fullMessage = config.welcomeMessage;
4309
4616
  const typingLineId = Date.now().toString();
4310
4617
  setTerminalHistory([
4311
- { type: "system", content: "YieldFabric Agent", timestamp: /* @__PURE__ */ new Date() },
4618
+ { type: "system", content: agentLabel, timestamp: /* @__PURE__ */ new Date() },
4312
4619
  { id: typingLineId, type: "info", content: "", timestamp: /* @__PURE__ */ new Date() }
4313
4620
  ]);
4314
4621
  let i = 0;
@@ -4364,7 +4671,7 @@ function useTerminalCore({
4364
4671
  }, [commandPaletteItems, selectedPaletteItem]);
4365
4672
  const handleClearCommand = useCallback(() => {
4366
4673
  setTerminalHistory([
4367
- { type: "system", content: "YieldFabric Agent", timestamp: /* @__PURE__ */ new Date() },
4674
+ { type: "system", content: agentLabel, timestamp: /* @__PURE__ */ new Date() },
4368
4675
  { type: "system", content: "Terminal cleared.", timestamp: /* @__PURE__ */ new Date() }
4369
4676
  ]);
4370
4677
  setCurrentCommand("");
@@ -4387,11 +4694,11 @@ function useTerminalCore({
4387
4694
  addTerminalLine("info", `Uploading ${files.length} file(s)...`);
4388
4695
  setIsExecuting(true);
4389
4696
  try {
4390
- const result = await onRagFileUpload(files, workingGroupContext);
4391
- if (result.success) {
4392
- addTerminalLine("success", `Started ${result.pipelineRuns?.length ?? result.documentsIngested ?? files.length} ingestion pipeline(s)`);
4393
- if (result.message) addTerminalLine("response", result.message);
4394
- const runs = result.pipelineRuns ?? [];
4697
+ const result2 = await onRagFileUpload(files, workingGroupContext);
4698
+ if (result2.success) {
4699
+ addTerminalLine("success", `Started ${result2.pipelineRuns?.length ?? result2.documentsIngested ?? files.length} ingestion pipeline(s)`);
4700
+ if (result2.message) addTerminalLine("response", result2.message);
4701
+ const runs = result2.pipelineRuns ?? [];
4395
4702
  if (runs.length > 0) {
4396
4703
  const apiUrl = config.agentsApiUrl || "http://localhost:3001";
4397
4704
  const remaining = new Set(runs.map((r) => r.run_id));
@@ -4478,7 +4785,7 @@ function useTerminalCore({
4478
4785
  await handleListFilesCommand();
4479
4786
  }
4480
4787
  } else {
4481
- addTerminalLine("error", `Upload failed${result.message ? `: ${result.message}` : ""}`);
4788
+ addTerminalLine("error", `Upload failed${result2.message ? `: ${result2.message}` : ""}`);
4482
4789
  }
4483
4790
  } catch (error) {
4484
4791
  addTerminalLine("error", `Upload exception: ${error?.message || "Unknown error"}`);
@@ -4487,7 +4794,7 @@ function useTerminalCore({
4487
4794
  if (fileInputRef.current) fileInputRef.current.value = "";
4488
4795
  setTimeout(() => focusInput(), 50);
4489
4796
  }
4490
- }, [onRagFileUpload, onListRagFiles, workingGroupContext, addTerminalLine, setIsExecuting, focusInput, config.agentsApiUrl]);
4797
+ }, [onRagFileUpload, onListRagFiles, workingGroupContext, addTerminalLine, setIsExecuting, focusInput, config.agentsApiUrl, authToken]);
4491
4798
  const handleListFilesCommand = useCallback(async () => {
4492
4799
  addTerminalLine("info", "");
4493
4800
  addTerminalLine("command", "files");
@@ -4497,12 +4804,12 @@ function useTerminalCore({
4497
4804
  }
4498
4805
  setIsExecuting(true);
4499
4806
  try {
4500
- const result = await onListRagFiles(workingGroupContext);
4501
- if (!result.success) {
4502
- addTerminalLine("error", `Could not load files${result.message ? `: ${result.message}` : ""}`);
4807
+ const result2 = await onListRagFiles(workingGroupContext);
4808
+ if (!result2.success) {
4809
+ addTerminalLine("error", `Could not load files${result2.message ? `: ${result2.message}` : ""}`);
4503
4810
  return;
4504
4811
  }
4505
- const files = result.files || [];
4812
+ const files = result2.files || [];
4506
4813
  if (files.length === 0) {
4507
4814
  addTerminalLine("info", "No files uploaded yet for this page working group.");
4508
4815
  return;
@@ -4531,12 +4838,12 @@ function useTerminalCore({
4531
4838
  return;
4532
4839
  }
4533
4840
  const title = payload.title || payload.document_id;
4534
- const result = await onDeleteRagFile(String(payload.document_id), workingGroupContext);
4535
- if (result.success) {
4841
+ const result2 = await onDeleteRagFile(String(payload.document_id), workingGroupContext);
4842
+ if (result2.success) {
4536
4843
  addTerminalLine("success", `Removed ${title}`);
4537
4844
  await handleListFilesCommand();
4538
4845
  } else {
4539
- addTerminalLine("error", `Failed to remove ${title}${result.message ? `: ${result.message}` : ""}`);
4846
+ addTerminalLine("error", `Failed to remove ${title}${result2.message ? `: ${result2.message}` : ""}`);
4540
4847
  }
4541
4848
  return;
4542
4849
  }
@@ -4546,9 +4853,9 @@ function useTerminalCore({
4546
4853
  return;
4547
4854
  }
4548
4855
  const title = payload.title || payload.document_id;
4549
- const result = await onDownloadRagFile(String(payload.document_id), title, workingGroupContext);
4550
- if (result.success) addTerminalLine("success", `Download started: ${title}`);
4551
- else addTerminalLine("error", `Download failed for ${title}${result.message ? `: ${result.message}` : ""}`);
4856
+ const result2 = await onDownloadRagFile(String(payload.document_id), title, workingGroupContext);
4857
+ if (result2.success) addTerminalLine("success", `Download started: ${title}`);
4858
+ else addTerminalLine("error", `Download failed for ${title}${result2.message ? `: ${result2.message}` : ""}`);
4552
4859
  return;
4553
4860
  }
4554
4861
  if (actionType === "resume_pipeline_run") {
@@ -4587,6 +4894,8 @@ function useTerminalCore({
4587
4894
  window.dispatchEvent(new CustomEvent("terminal-action-execute", { detail: updatedAction, bubbles: true }));
4588
4895
  }, [onDeleteRagFile, onDownloadRagFile, workingGroupContext, addTerminalLine, handleListFilesCommand, config.agentsApiUrl]);
4589
4896
  return {
4897
+ // Identity
4898
+ agentLabel,
4590
4899
  // History
4591
4900
  terminalHistory,
4592
4901
  setTerminalHistory,
@@ -4615,6 +4924,8 @@ function useTerminalCore({
4615
4924
  // Command input
4616
4925
  currentCommand,
4617
4926
  setCurrentCommand,
4927
+ pendingSubmit,
4928
+ setPendingSubmit,
4618
4929
  commandPaletteItems,
4619
4930
  setCommandPaletteItems,
4620
4931
  selectedPaletteItem,
@@ -4737,6 +5048,7 @@ function extractQuery(input, caret, enabledCommands, requireSlashCommand) {
4737
5048
  return { query: tail, command: cmd };
4738
5049
  }
4739
5050
  function StandardTerminal({
5051
+ showBusyIndicator,
4740
5052
  theme: themeOverrides,
4741
5053
  config,
4742
5054
  onCommandExecute,
@@ -4756,9 +5068,15 @@ function StandardTerminal({
4756
5068
  onDeleteRagFile,
4757
5069
  onDownloadRagFile,
4758
5070
  workingGroupContext: workingGroupContextProp = null,
5071
+ showWorkingGroupBanner = true,
5072
+ autoFocusInput = true,
5073
+ inputPlaceholder,
5074
+ submitRequest,
5075
+ onSubmitRequest,
4759
5076
  onPipelineDraftEdit,
4760
5077
  onPipelineDraftReject,
4761
5078
  onSubmitNote,
5079
+ onSubmitLog,
4762
5080
  onMentionResolve,
4763
5081
  onMentionPicked,
4764
5082
  availableKGs,
@@ -4779,6 +5097,7 @@ function StandardTerminal({
4779
5097
  widgets,
4780
5098
  slots,
4781
5099
  onSourcesActivate,
5100
+ onSourceActivate,
4782
5101
  onPollMessages,
4783
5102
  pollIntervalMs,
4784
5103
  historyRef
@@ -4794,6 +5113,7 @@ function StandardTerminal({
4794
5113
  reasoningRef,
4795
5114
  workingGroupContextProp,
4796
5115
  onResolveWorkingGroup,
5116
+ autoFocusInput,
4797
5117
  onRagFileUpload,
4798
5118
  onListRagFiles,
4799
5119
  onDeleteRagFile,
@@ -4809,17 +5129,32 @@ function StandardTerminal({
4809
5129
  showWelcomeMessage
4810
5130
  });
4811
5131
  const { pushCommand, navigateUp, navigateDown, resetIndex } = useCommandHistory();
5132
+ const consumedRequest = useRef(null);
5133
+ React15.useEffect(() => {
5134
+ if (!submitRequest || consumedRequest.current === submitRequest.id || !submitRequest.value.trim() || !onStreamRequest || core.isExecuting || core.isStreaming || core.currentStreamingLineId.current) return;
5135
+ consumedRequest.current = submitRequest.id;
5136
+ onSubmitRequest?.(submitRequest.id);
5137
+ void core.handleStreamingChat(submitRequest.value.trim());
5138
+ }, [
5139
+ submitRequest,
5140
+ onSubmitRequest,
5141
+ onStreamRequest,
5142
+ core.isExecuting,
5143
+ core.isStreaming,
5144
+ core.currentStreamingLineId,
5145
+ core.handleStreamingChat
5146
+ ]);
4812
5147
  const [suggestions, setSuggestions] = useState([]);
4813
5148
  const [selectedSuggestion, setSelectedSuggestion] = useState(0);
4814
5149
  const [internalAuthToken, setInternalAuthToken] = useState(authToken);
4815
5150
  const authTokenRef = useRef(internalAuthToken);
4816
5151
  authTokenRef.current = internalAuthToken;
4817
- React12.useEffect(() => {
5152
+ React15.useEffect(() => {
4818
5153
  if (authToken !== internalAuthToken) setInternalAuthToken(authToken);
4819
5154
  }, [authToken]);
4820
5155
  const [caretIndex, setCaretIndex] = useState(0);
4821
5156
  const [slashPickedNames, setSlashPickedNames] = useState([]);
4822
- const liveSlashPickedNames = React12.useMemo(
5157
+ const liveSlashPickedNames = React15.useMemo(
4823
5158
  () => slashPickedNames.filter(
4824
5159
  (n) => core.currentCommand.toLowerCase().includes(`@${n.toLowerCase()}`)
4825
5160
  ),
@@ -4906,6 +5241,11 @@ function StandardTerminal({
4906
5241
  setSuggestions([]);
4907
5242
  }
4908
5243
  };
5244
+ React15.useEffect(() => {
5245
+ if (!core.pendingSubmit) return;
5246
+ core.setPendingSubmit(null);
5247
+ void handleSubmit();
5248
+ }, [core.pendingSubmit]);
4909
5249
  const handleSubmit = async () => {
4910
5250
  if (!core.currentCommand.trim() || core.isExecuting || core.isStreaming || core.currentStreamingLineId.current) return;
4911
5251
  const rawCmd = core.currentCommand.trim();
@@ -4953,6 +5293,21 @@ function StandardTerminal({
4953
5293
  setTimeout(() => core.focusInput(), 50);
4954
5294
  return;
4955
5295
  }
5296
+ if (command === "log") {
5297
+ const prose = args.join(" ");
5298
+ core.addTerminalLine("command", prose.trim() ? `log ${prose}` : "log");
5299
+ if (!onSubmitLog) {
5300
+ core.addTerminalLine("error", "Work capture is not available in this context.");
5301
+ } else if (!prose.trim()) {
5302
+ core.addTerminalLine("info", "Usage: /log <what you did or decided>");
5303
+ } else {
5304
+ const result2 = await onSubmitLog(prose);
5305
+ core.addTerminalLine(result2.ok ? "success" : "error", result2.message);
5306
+ }
5307
+ core.setCurrentCommand("");
5308
+ setTimeout(() => core.focusInput(), 50);
5309
+ return;
5310
+ }
4956
5311
  if (isNaturalLanguage(cmd, config.endpoints) && onStreamRequest) {
4957
5312
  await core.handleStreamingChat(cmd);
4958
5313
  core.setCurrentCommand("");
@@ -5008,8 +5363,8 @@ function StandardTerminal({
5008
5363
  );
5009
5364
  return;
5010
5365
  }
5011
- const result = await onSubmitNote(prose, scope);
5012
- core.addTerminalLine(result.ok ? "success" : "error", result.message);
5366
+ const result2 = await onSubmitNote(prose, scope);
5367
+ core.addTerminalLine(result2.ok ? "success" : "error", result2.message);
5013
5368
  };
5014
5369
  const handleTokenCommand = (args) => {
5015
5370
  core.addTerminalLine("command", `token ${args.join(" ")}`);
@@ -5037,18 +5392,18 @@ Method: ${parsed.endpoint.method}
5037
5392
  Path: ${parsed.endpoint.path}`);
5038
5393
  core.addTerminalLine("info", "");
5039
5394
  try {
5040
- const result = await onCommandExecute(cmd, parsed.endpoint, parsed.args);
5041
- if (result.success) {
5042
- core.addTerminalLine("success", result.message || "RESPONSE: SUCCESS");
5043
- if (result.data) core.addTerminalLine("response", JSON.stringify(result.data, null, 2));
5044
- if (parsed.endpoint.id.includes("login") && result.data?.token) {
5045
- setInternalAuthToken(result.data.token);
5046
- onAuthTokenChange?.(result.data.token);
5395
+ const result2 = await onCommandExecute(cmd, parsed.endpoint, parsed.args);
5396
+ if (result2.success) {
5397
+ core.addTerminalLine("success", result2.message || "RESPONSE: SUCCESS");
5398
+ if (result2.data) core.addTerminalLine("response", JSON.stringify(result2.data, null, 2));
5399
+ if (parsed.endpoint.id.includes("login") && result2.data?.token) {
5400
+ setInternalAuthToken(result2.data.token);
5401
+ onAuthTokenChange?.(result2.data.token);
5047
5402
  core.addTerminalLine("success", "AUTH TOKEN AUTO-SAVED");
5048
5403
  }
5049
5404
  } else {
5050
- core.addTerminalLine("error", `ERROR: ${result.error || "Request failed"}`);
5051
- if (result.data) core.addTerminalLine("response", JSON.stringify(result.data, null, 2));
5405
+ core.addTerminalLine("error", `ERROR: ${result2.error || "Request failed"}`);
5406
+ if (result2.data) core.addTerminalLine("response", JSON.stringify(result2.data, null, 2));
5052
5407
  }
5053
5408
  } catch (error) {
5054
5409
  core.addTerminalLine("error", `SYSTEM ERROR: ${error.message}`);
@@ -5071,14 +5426,17 @@ Path: ${parsed.endpoint.path}`);
5071
5426
  className,
5072
5427
  style,
5073
5428
  showHeader,
5429
+ agentLabel: core.agentLabel,
5074
5430
  focusInput: core.focusInput,
5075
5431
  setTerminalHistory: core.setTerminalHistory,
5076
5432
  terminalBodyRef: core.terminalBodyRef,
5077
5433
  workingGroupContext: core.workingGroupContext,
5434
+ showWorkingGroupBanner,
5078
5435
  progressState: core.progressState,
5079
5436
  streamTransparency: core.streamTransparency,
5080
5437
  streamNowMs: core.streamNowMs,
5081
5438
  isExecuting: core.isExecuting,
5439
+ showBusyIndicator,
5082
5440
  handleCancelReasoning: core.handleCancelReasoning,
5083
5441
  onViewKnowledgeGraph,
5084
5442
  onLiveKGUpdate,
@@ -5153,11 +5511,12 @@ Path: ${parsed.endpoint.path}`);
5153
5511
  inputBar: /* @__PURE__ */ jsx(
5154
5512
  TerminalInputBar,
5155
5513
  {
5514
+ autoFocus: autoFocusInput,
5156
5515
  inputRef: core.inputRef,
5157
5516
  fileInputRef: core.fileInputRef,
5158
5517
  currentCommand: core.currentCommand,
5159
5518
  isExecuting: core.isExecuting,
5160
- placeholder: activeKGScope ? `Ask about ${activeKGScope.label}...` : "Ask anything...",
5519
+ placeholder: activeKGScope ? `Ask about ${activeKGScope.label}...` : inputPlaceholder ?? "Ask anything...",
5161
5520
  onInputChange: handleInputChange,
5162
5521
  onKeyDown: handleKeyDown,
5163
5522
  onSubmit: handleSubmit,
@@ -5194,6 +5553,7 @@ Path: ${parsed.endpoint.path}`);
5194
5553
  authToken: authTokenRef.current,
5195
5554
  onPageNavigation,
5196
5555
  onSourcesActivate,
5556
+ onSourceActivate,
5197
5557
  renderEntryExtra,
5198
5558
  renderEntry,
5199
5559
  widgets,
@@ -5484,6 +5844,7 @@ function useMentions(currentCommand, participants, mentionTargets) {
5484
5844
  };
5485
5845
  }
5486
5846
  function ChatTerminal({
5847
+ showBusyIndicator,
5487
5848
  theme: themeOverrides,
5488
5849
  config,
5489
5850
  className = "",
@@ -5499,6 +5860,8 @@ function ChatTerminal({
5499
5860
  onDeleteRagFile,
5500
5861
  onDownloadRagFile,
5501
5862
  workingGroupContext: workingGroupContextProp = null,
5863
+ showWorkingGroupBanner = true,
5864
+ autoFocusInput = true,
5502
5865
  onPipelineDraftEdit,
5503
5866
  onPipelineDraftReject,
5504
5867
  onPageNavigation,
@@ -5521,6 +5884,7 @@ function ChatTerminal({
5521
5884
  widgets,
5522
5885
  slots,
5523
5886
  onSourcesActivate,
5887
+ onSourceActivate,
5524
5888
  historyRef,
5525
5889
  currentUser,
5526
5890
  onSendMessage,
@@ -5530,8 +5894,10 @@ function ChatTerminal({
5530
5894
  typingUsers,
5531
5895
  onTyping,
5532
5896
  onSubmitNote,
5897
+ onSubmitLog,
5533
5898
  onMentionResolve,
5534
- onMentionPicked
5899
+ onMentionPicked,
5900
+ authToken
5535
5901
  }) {
5536
5902
  const core = useTerminalCore({
5537
5903
  config,
@@ -5544,6 +5910,7 @@ function ChatTerminal({
5544
5910
  reasoningRef,
5545
5911
  workingGroupContextProp,
5546
5912
  onResolveWorkingGroup,
5913
+ autoFocusInput,
5547
5914
  onRagFileUpload,
5548
5915
  onListRagFiles,
5549
5916
  onDeleteRagFile,
@@ -5557,7 +5924,8 @@ function ChatTerminal({
5557
5924
  showWelcomeMessage,
5558
5925
  currentUser,
5559
5926
  onPollMessages,
5560
- pollIntervalMs
5927
+ pollIntervalMs,
5928
+ authToken
5561
5929
  });
5562
5930
  useEffect(() => {
5563
5931
  onActivityEventsChange?.(core.activityEvents);
@@ -5701,11 +6069,26 @@ function ChatTerminal({
5701
6069
  `Usage: /${command} <prose>. Use @name to link an entity.`
5702
6070
  );
5703
6071
  } else {
5704
- const result = await onSubmitNote(
6072
+ const result2 = await onSubmitNote(
5705
6073
  prose,
5706
6074
  command === "personal" ? "personal" : "workspace"
5707
6075
  );
5708
- core.addTerminalLine(result.ok ? "success" : "error", result.message);
6076
+ core.addTerminalLine(result2.ok ? "success" : "error", result2.message);
6077
+ }
6078
+ core.setCurrentCommand("");
6079
+ setTimeout(() => core.focusInput(), 50);
6080
+ return;
6081
+ }
6082
+ if (command === "log") {
6083
+ const prose = args.join(" ");
6084
+ core.addTerminalLine("command", prose.trim() ? `log ${prose}` : "log");
6085
+ if (!onSubmitLog) {
6086
+ core.addTerminalLine("error", "Work capture is not available in this context.");
6087
+ } else if (!prose.trim()) {
6088
+ core.addTerminalLine("info", "Usage: /log <what you did or decided>");
6089
+ } else {
6090
+ const result2 = await onSubmitLog(prose);
6091
+ core.addTerminalLine(result2.ok ? "success" : "error", result2.message);
5709
6092
  }
5710
6093
  core.setCurrentCommand("");
5711
6094
  setTimeout(() => core.focusInput(), 50);
@@ -5752,14 +6135,17 @@ function ChatTerminal({
5752
6135
  className,
5753
6136
  style,
5754
6137
  showHeader,
6138
+ agentLabel: core.agentLabel,
5755
6139
  focusInput: core.focusInput,
5756
6140
  setTerminalHistory: core.setTerminalHistory,
5757
6141
  terminalBodyRef: core.terminalBodyRef,
5758
6142
  workingGroupContext: core.workingGroupContext,
6143
+ showWorkingGroupBanner,
5759
6144
  progressState: core.progressState,
5760
6145
  streamTransparency: core.streamTransparency,
5761
6146
  streamNowMs: core.streamNowMs,
5762
6147
  isExecuting: core.isExecuting,
6148
+ showBusyIndicator,
5763
6149
  handleCancelReasoning: core.handleCancelReasoning,
5764
6150
  onViewKnowledgeGraph,
5765
6151
  onLiveKGUpdate,
@@ -5796,6 +6182,7 @@ function ChatTerminal({
5796
6182
  inputBar: /* @__PURE__ */ jsx(
5797
6183
  TerminalInputBar,
5798
6184
  {
6185
+ autoFocus: autoFocusInput,
5799
6186
  inputRef: core.inputRef,
5800
6187
  fileInputRef: core.fileInputRef,
5801
6188
  currentCommand: core.currentCommand,
@@ -5848,6 +6235,7 @@ function ChatTerminal({
5848
6235
  config,
5849
6236
  onPageNavigation,
5850
6237
  onSourcesActivate,
6238
+ onSourceActivate,
5851
6239
  renderEntryExtra,
5852
6240
  renderEntry,
5853
6241
  widgets,
@@ -7004,6 +7392,227 @@ function ReasoningHistoryCard({
7004
7392
  }
7005
7393
  );
7006
7394
  }
7395
+ function subject(step) {
7396
+ const idx = step.detail.indexOf(": ");
7397
+ const s = idx > -1 ? step.detail.slice(idx + 2).trim() : "";
7398
+ return s.length ? s : null;
7399
+ }
7400
+ function bare(tool) {
7401
+ return tool.replace(/^yf_kg_/, "").replace(/^yf_/, "");
7402
+ }
7403
+ function action(step) {
7404
+ const subj = subject(step);
7405
+ const quoted = subj ? `\u201C${subj}\u201D` : null;
7406
+ switch (bare(step.tool)) {
7407
+ case "graphs":
7408
+ return "Listed knowledge collections";
7409
+ case "search":
7410
+ return quoted ? `Searched for ${quoted}` : "Searched the documents";
7411
+ case "count":
7412
+ return quoted ? `Counted mentions of ${quoted}` : "Counted mentions";
7413
+ case "enumerate":
7414
+ return quoted ? `Listed every match for ${quoted}` : "Listed every match";
7415
+ case "fetch":
7416
+ return "Read a document";
7417
+ case "entities":
7418
+ return "Listed the people and things found";
7419
+ case "explore":
7420
+ return quoted ? `Followed connections from ${quoted}` : "Followed connections";
7421
+ case "sets":
7422
+ return "Listed frozen document sets";
7423
+ case "get_set":
7424
+ return "Read a frozen document set";
7425
+ case "count_in_set":
7426
+ return quoted ? `Counted ${quoted} inside a set` : "Counted inside a set";
7427
+ case "think":
7428
+ return "Thought it through";
7429
+ default:
7430
+ return subj ? `${bare(step.tool).replace(/_/g, " ")} ${subj}` : bare(step.tool).replace(/_/g, " ");
7431
+ }
7432
+ }
7433
+ function took(step) {
7434
+ if (step.status === "running") return null;
7435
+ const ms = Number(step.ms);
7436
+ if (!Number.isFinite(ms) || ms < 1e3) return null;
7437
+ return ms < 1e4 ? `${(ms / 1e3).toFixed(1)}s` : `${Math.round(ms / 1e3)}s`;
7438
+ }
7439
+ function groupTook(g) {
7440
+ const slowest = g.steps.reduce(
7441
+ (worst, s) => Number(s.ms) > Number(worst.ms) ? s : worst,
7442
+ g.steps[0]
7443
+ );
7444
+ return took(slowest);
7445
+ }
7446
+ function result(step) {
7447
+ if (step.status === "running") return null;
7448
+ if (step.status === "error") return "unavailable";
7449
+ const n = Number(step.outcome);
7450
+ if (!Number.isFinite(n)) return null;
7451
+ const [one, many] = {
7452
+ graphs: ["collection", "collections"],
7453
+ search: ["result", "results"],
7454
+ count: ["document", "documents"],
7455
+ enumerate: ["match", "matches"],
7456
+ entities: ["entity", "entities"],
7457
+ explore: ["connection", "connections"],
7458
+ sets: ["set", "sets"]
7459
+ }[bare(step.tool)] ?? ["result", "results"];
7460
+ if (n === 0) return "nothing found";
7461
+ return `${n} ${n === 1 ? one : many}`;
7462
+ }
7463
+ function group(steps) {
7464
+ const out = [];
7465
+ const byKey = /* @__PURE__ */ new Map();
7466
+ for (const step of steps) {
7467
+ const label = action(step);
7468
+ const key = `${step.round ?? ""}\0${label}`;
7469
+ const existing = byKey.get(key);
7470
+ if (existing) {
7471
+ existing.steps.push(step);
7472
+ continue;
7473
+ }
7474
+ const g = { key: step.seq, label, round: step.round, steps: [step] };
7475
+ byKey.set(key, g);
7476
+ out.push(g);
7477
+ }
7478
+ return out;
7479
+ }
7480
+ function groupResult(g) {
7481
+ if (g.steps.length === 1) return result(g.steps[0]);
7482
+ const done = g.steps.filter((s) => s.status !== "running");
7483
+ if (!done.length) return null;
7484
+ const hits = done.filter((s) => Number(s.outcome) > 0).length;
7485
+ if (hits === 0) return "nothing found";
7486
+ return `found in ${hits} of ${done.length}`;
7487
+ }
7488
+ function roundCount(steps) {
7489
+ const seen = /* @__PURE__ */ new Set();
7490
+ for (const s of steps) if (typeof s.round === "number") seen.add(s.round);
7491
+ return seen.size ? seen.size : null;
7492
+ }
7493
+ function AgentStepTrace({ steps, working }) {
7494
+ const t = useTheme();
7495
+ const failed = steps.some((s) => s.status === "error");
7496
+ const prominent = working || failed;
7497
+ const [open, setOpen] = useState(prominent);
7498
+ const [touched, setTouched] = useState(false);
7499
+ const [hover, setHover] = useState(false);
7500
+ useEffect(() => {
7501
+ if (!touched) setOpen(prominent);
7502
+ }, [prominent, touched]);
7503
+ if (!steps.length) return null;
7504
+ const groups = group(steps);
7505
+ const rounds = roundCount(steps);
7506
+ const n = rounds ?? groups.length;
7507
+ const count = `${n} ${rounds ? "round" : "step"}${n === 1 ? "" : "s"}`;
7508
+ const only = groups.length === 1 ? groups[0] : null;
7509
+ const summary = working ? `${action(steps[steps.length - 1])}\u2026` : failed ? `${count}, some unavailable` : only ? [
7510
+ only.label + (only.steps.length > 1 ? ` \xD7${only.steps.length}` : ""),
7511
+ groupResult(only)
7512
+ ].filter(Boolean).join(" \u2014 ") : count;
7513
+ const toggle = /* @__PURE__ */ jsxs(
7514
+ "button",
7515
+ {
7516
+ type: "button",
7517
+ onClick: () => {
7518
+ setTouched(true);
7519
+ setOpen((o) => !o);
7520
+ },
7521
+ onMouseEnter: () => setHover(true),
7522
+ onMouseLeave: () => setHover(false),
7523
+ "aria-expanded": open,
7524
+ className: "flex w-full items-center gap-2 text-left text-xs",
7525
+ style: { color: failed ? t.error : hover ? t.text : t.textMuted },
7526
+ children: [
7527
+ working && /* @__PURE__ */ jsx(
7528
+ "span",
7529
+ {
7530
+ "aria-hidden": true,
7531
+ className: "inline-block h-1.5 w-1.5 shrink-0 rounded-full motion-safe:animate-pulse",
7532
+ style: { backgroundColor: t.primary }
7533
+ }
7534
+ ),
7535
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate", children: summary }),
7536
+ !(only && !working && !failed) && /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "shrink-0", children: open ? "\u2212" : "+" })
7537
+ ]
7538
+ }
7539
+ );
7540
+ const rows = /* @__PURE__ */ jsx("ol", { className: "mt-2 space-y-1", children: groups.map((g, i) => {
7541
+ const res = groupResult(g);
7542
+ const slow = groupTook(g);
7543
+ const anyError = g.steps.some((s) => s.status === "error");
7544
+ const running = g.steps.some((s) => s.status === "running");
7545
+ const startsRound = g.round !== void 0 && g.round !== groups[i - 1]?.round;
7546
+ return /* @__PURE__ */ jsxs("li", { className: "flex items-baseline gap-2 text-xs", children: [
7547
+ rounds !== null && /* @__PURE__ */ jsx(
7548
+ "span",
7549
+ {
7550
+ className: "w-3 shrink-0 text-right tabular-nums",
7551
+ style: { color: t.textMuted },
7552
+ "aria-hidden": !startsRound,
7553
+ children: startsRound ? g.round : ""
7554
+ }
7555
+ ),
7556
+ /* @__PURE__ */ jsx(
7557
+ "span",
7558
+ {
7559
+ "aria-hidden": true,
7560
+ className: "w-3 shrink-0 text-[10px]",
7561
+ style: { color: anyError ? t.error : t.textMuted },
7562
+ children: running ? "\xB7" : anyError ? "!" : "\u2713"
7563
+ }
7564
+ ),
7565
+ /* @__PURE__ */ jsxs(
7566
+ "span",
7567
+ {
7568
+ className: "min-w-0 flex-1 truncate",
7569
+ style: { color: t.textSecondary },
7570
+ title: g.label,
7571
+ children: [
7572
+ g.label,
7573
+ g.steps.length > 1 && /* @__PURE__ */ jsxs("span", { style: { color: t.textMuted }, children: [
7574
+ " \xD7",
7575
+ g.steps.length
7576
+ ] })
7577
+ ]
7578
+ }
7579
+ ),
7580
+ slow && /* @__PURE__ */ jsx(
7581
+ "span",
7582
+ {
7583
+ className: "shrink-0 tabular-nums",
7584
+ style: { color: t.textMuted },
7585
+ children: slow
7586
+ }
7587
+ ),
7588
+ res && /* @__PURE__ */ jsx("span", { className: "shrink-0", style: { color: anyError ? t.error : t.textMuted }, children: res })
7589
+ ] }, g.key);
7590
+ }) });
7591
+ if (!prominent) {
7592
+ return /* @__PURE__ */ jsxs("div", { className: "mt-2", children: [
7593
+ toggle,
7594
+ open && !only && rows
7595
+ ] });
7596
+ }
7597
+ return /* @__PURE__ */ jsxs(
7598
+ "div",
7599
+ {
7600
+ className: "mt-2 rounded-lg p-3",
7601
+ style: { backgroundColor: t.bgRaisedAlt, border: `1px solid ${t.border}` },
7602
+ children: [
7603
+ toggle,
7604
+ open && rows
7605
+ ]
7606
+ }
7607
+ );
7608
+ }
7609
+ function agentStepTraceExtra(entry) {
7610
+ const meta = entry.metadata;
7611
+ const steps = meta?.agentSteps;
7612
+ if (!steps?.length) return null;
7613
+ const working = meta?.agentStepsLive ?? entry.content.endsWith("\u258B");
7614
+ return /* @__PURE__ */ jsx(AgentStepTrace, { steps, working });
7615
+ }
7007
7616
 
7008
7617
  // src/knowledge/graphTheme.ts
7009
7618
  var NODE_TYPE_COLORS = {
@@ -7479,7 +8088,7 @@ var MarkdownContent = memo(function MarkdownContent2({
7479
8088
  if (!entities || entities.length === 0) return MARKDOWN_COMPONENTS;
7480
8089
  return { ...MARKDOWN_COMPONENTS, ...buildHighlightOverrides(entities, onEntityClick) };
7481
8090
  }, [entities, onEntityClick]);
7482
- return /* @__PURE__ */ jsx("div", { className: `text-xs leading-relaxed text-gray-300 break-words ${className}`, children: /* @__PURE__ */ jsx(ReactMarkdown, { components, children }) });
8091
+ return /* @__PURE__ */ jsx("div", { className: `text-xs leading-relaxed text-gray-300 break-words ${className}`, children: /* @__PURE__ */ jsx(ReactMarkdown, { remarkPlugins: [remarkGfm], components, children }) });
7483
8092
  });
7484
8093
  function escapeRegex(s) {
7485
8094
  return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
@@ -7545,13 +8154,13 @@ function highlightText(text, regex, entityMap, onEntityClick) {
7545
8154
  return parts;
7546
8155
  }
7547
8156
  function highlightChildren(children, regex, entityMap, onEntityClick) {
7548
- return React12.Children.map(children, (child) => {
8157
+ return React15.Children.map(children, (child) => {
7549
8158
  if (typeof child === "string") {
7550
8159
  const highlighted = highlightText(child, regex, entityMap, onEntityClick);
7551
8160
  return highlighted.length === 1 && typeof highlighted[0] === "string" ? child : /* @__PURE__ */ jsx(Fragment, { children: highlighted });
7552
8161
  }
7553
- if (React12.isValidElement(child) && child.props.children) {
7554
- return React12.cloneElement(child, {
8162
+ if (React15.isValidElement(child) && child.props.children) {
8163
+ return React15.cloneElement(child, {
7555
8164
  ...child.props,
7556
8165
  children: highlightChildren(child.props.children, regex, entityMap, onEntityClick)
7557
8166
  });
@@ -8022,10 +8631,10 @@ var SAVE_STAGE_MESSAGES = {
8022
8631
  extracting: "re-analyzing entities with AI\u2026",
8023
8632
  finishing: "almost done \xB7 finalizing graph\u2026"
8024
8633
  };
8025
- function summarizeSaveResult(result) {
8026
- if (!result?.diff) return "Saved";
8027
- const added = result.diff.added_node_keys?.length ?? 0;
8028
- const removed = result.diff.removed_node_keys?.length ?? 0;
8634
+ function summarizeSaveResult(result2) {
8635
+ if (!result2?.diff) return "Saved";
8636
+ const added = result2.diff.added_node_keys?.length ?? 0;
8637
+ const removed = result2.diff.removed_node_keys?.length ?? 0;
8029
8638
  if (added === 0 && removed === 0) return "Saved \xB7 graph unchanged";
8030
8639
  const parts = [];
8031
8640
  if (added > 0) parts.push(`+${added}`);
@@ -8104,11 +8713,11 @@ function NodeDetailDrawer({ node, connectedNodes, chunks = [], annotations, onCl
8104
8713
  setIsSaving(true);
8105
8714
  try {
8106
8715
  const rawResult = await onSaveDescription(draftAtSaveTime);
8107
- const result = rawResult ?? null;
8716
+ const result2 = rawResult ?? null;
8108
8717
  if (currentNodeIdRef.current === savingForNodeId) {
8109
8718
  setLocalDescription(draftAtSaveTime);
8110
8719
  setIsEditing(false);
8111
- setLastSaveResult(result);
8720
+ setLastSaveResult(result2);
8112
8721
  if (savedPillTimerRef.current) clearTimeout(savedPillTimerRef.current);
8113
8722
  savedPillTimerRef.current = setTimeout(() => {
8114
8723
  setLastSaveResult(null);
@@ -8644,11 +9253,11 @@ function NodeDetailDrawer({ node, connectedNodes, chunks = [], annotations, onCl
8644
9253
  renderedClaims.length,
8645
9254
  ")"
8646
9255
  ] }),
8647
- /* @__PURE__ */ jsx("div", { className: "space-y-2", children: claimGroups.map((group) => {
8648
- const isRelated = group.predicate === "(related)";
9256
+ /* @__PURE__ */ jsx("div", { className: "space-y-2", children: claimGroups.map((group2) => {
9257
+ const isRelated = group2.predicate === "(related)";
8649
9258
  return /* @__PURE__ */ jsxs("div", { children: [
8650
- group.items.length > 1 && /* @__PURE__ */ jsx("div", { className: "text-[10px] text-gray-500 italic mb-0.5 px-2", children: group.predicate }),
8651
- /* @__PURE__ */ jsx("div", { className: "space-y-0.5", children: group.items.map((claim) => {
9259
+ group2.items.length > 1 && /* @__PURE__ */ jsx("div", { className: "text-[10px] text-gray-500 italic mb-0.5 px-2", children: group2.predicate }),
9260
+ /* @__PURE__ */ jsx("div", { className: "space-y-0.5", children: group2.items.map((claim) => {
8652
9261
  const cnColors = getNodeColors(claim.targetType);
8653
9262
  return /* @__PURE__ */ jsxs(
8654
9263
  "button",
@@ -8686,7 +9295,7 @@ function NodeDetailDrawer({ node, connectedNodes, chunks = [], annotations, onCl
8686
9295
  claim.key
8687
9296
  );
8688
9297
  }) })
8689
- ] }, group.predicate);
9298
+ ] }, group2.predicate);
8690
9299
  }) })
8691
9300
  ] }),
8692
9301
  Object.keys(visibleProperties).length > 0 && /* @__PURE__ */ jsxs("div", { children: [
@@ -9301,7 +9910,7 @@ var RECRUITING_LINES = [
9301
9910
  ];
9302
9911
  function RecruitingHint() {
9303
9912
  const [idx, setIdx] = useState(0);
9304
- React12.useEffect(() => {
9913
+ React15.useEffect(() => {
9305
9914
  const t = setInterval(() => setIdx((i) => (i + 1) % RECRUITING_LINES.length), 2500);
9306
9915
  return () => clearInterval(t);
9307
9916
  }, []);
@@ -9404,7 +10013,7 @@ function RelationshipSentence({ rel }) {
9404
10013
  const hasRange = rel.range.length > 0;
9405
10014
  return /* @__PURE__ */ jsxs("div", { style: { lineHeight: 2, color: "#888", fontSize: 13, wordBreak: "break-word" }, children: [
9406
10015
  hasDomain && /* @__PURE__ */ jsxs(Fragment, { children: [
9407
- rel.domain.map((d, i) => /* @__PURE__ */ jsxs(React12.Fragment, { children: [
10016
+ rel.domain.map((d, i) => /* @__PURE__ */ jsxs(React15.Fragment, { children: [
9408
10017
  i > 0 && /* @__PURE__ */ jsx("span", { style: { color: "#666" }, children: " or " }),
9409
10018
  /* @__PURE__ */ jsx(TypeChip, { name: d })
9410
10019
  ] }, d)),
@@ -9413,7 +10022,7 @@ function RelationshipSentence({ rel }) {
9413
10022
  /* @__PURE__ */ jsx("span", { style: { color: "#ddd", fontStyle: "italic", fontWeight: 500 }, children: verb }),
9414
10023
  hasRange && /* @__PURE__ */ jsxs(Fragment, { children: [
9415
10024
  " ",
9416
- rel.range.map((r, i) => /* @__PURE__ */ jsxs(React12.Fragment, { children: [
10025
+ rel.range.map((r, i) => /* @__PURE__ */ jsxs(React15.Fragment, { children: [
9417
10026
  i > 0 && /* @__PURE__ */ jsx("span", { style: { color: "#666" }, children: " and " }),
9418
10027
  /* @__PURE__ */ jsx(TypeChip, { name: r })
9419
10028
  ] }, r))
@@ -9933,7 +10542,7 @@ function DocumentProcessingProgress() {
9933
10542
  if (!state) return null;
9934
10543
  const currentIdx = DOCUMENT_PIPELINE_STEPS.findIndex((s) => s.key === state.step);
9935
10544
  return /* @__PURE__ */ jsxs("div", { className: "px-3 py-1.5 flex flex-col gap-1", style: { borderBottom: "1px solid #282828", backgroundColor: "#1a1a1a" }, children: [
9936
- /* @__PURE__ */ jsx("div", { className: "flex items-center gap-0", children: DOCUMENT_PIPELINE_STEPS.map((step, i) => /* @__PURE__ */ jsxs(React12.Fragment, { children: [
10545
+ /* @__PURE__ */ jsx("div", { className: "flex items-center gap-0", children: DOCUMENT_PIPELINE_STEPS.map((step, i) => /* @__PURE__ */ jsxs(React15.Fragment, { children: [
9937
10546
  i > 0 && /* @__PURE__ */ jsx("div", { className: "w-2 h-px mx-0.5", style: { backgroundColor: i <= currentIdx ? "#10b981" : "#333" } }),
9938
10547
  /* @__PURE__ */ jsx(
9939
10548
  "span",
@@ -10217,10 +10826,10 @@ function hydrateAgentIds(ids) {
10217
10826
  };
10218
10827
  });
10219
10828
  }
10220
- function stageReducer(state, action) {
10221
- switch (action.type) {
10829
+ function stageReducer(state, action2) {
10830
+ switch (action2.type) {
10222
10831
  case "TEAM_SETUP": {
10223
- const incomingIds = new Set(action.agents.map((a) => a.id));
10832
+ const incomingIds = new Set(action2.agents.map((a) => a.id));
10224
10833
  let isWholesaleReplace = state.lastBackendTeamIds.size > 0;
10225
10834
  if (isWholesaleReplace) {
10226
10835
  state.lastBackendTeamIds.forEach((id) => {
@@ -10238,48 +10847,48 @@ function stageReducer(state, action) {
10238
10847
  return {
10239
10848
  ...state,
10240
10849
  stage: "team_formation",
10241
- team: [...action.agents, ...surviving],
10850
+ team: [...action2.agents, ...surviving],
10242
10851
  localOnlyAgentIds: nextLocalOnly,
10243
10852
  lastBackendTeamIds: incomingIds
10244
10853
  };
10245
10854
  }
10246
10855
  case "PHASE_ADVANCED": {
10247
- const to = action.to;
10856
+ const to = action2.to;
10248
10857
  if (to === "team_formation") return { ...state, stage: "team_formation" };
10249
10858
  if (to === "schema_seeding") return { ...state, stage: "schema_seeding" };
10250
10859
  if (to === "construction" || to === "synthesis" || to === "done") return { ...state, stage: "graph" };
10251
10860
  return state;
10252
10861
  }
10253
10862
  case "UPDATE_AGENT":
10254
- return { ...state, team: state.team.map((a) => a.id === action.id ? { ...a, ...action.updates } : a) };
10863
+ return { ...state, team: state.team.map((a) => a.id === action2.id ? { ...a, ...action2.updates } : a) };
10255
10864
  case "REMOVE_AGENT": {
10256
10865
  const nextLocalOnly = new Set(state.localOnlyAgentIds);
10257
- nextLocalOnly.delete(action.id);
10866
+ nextLocalOnly.delete(action2.id);
10258
10867
  return {
10259
10868
  ...state,
10260
- team: state.team.filter((a) => a.id !== action.id),
10869
+ team: state.team.filter((a) => a.id !== action2.id),
10261
10870
  localOnlyAgentIds: nextLocalOnly
10262
10871
  };
10263
10872
  }
10264
10873
  case "ADD_AGENT": {
10265
10874
  const nextLocalOnly = new Set(state.localOnlyAgentIds);
10266
- nextLocalOnly.add(action.agent.id);
10875
+ nextLocalOnly.add(action2.agent.id);
10267
10876
  return {
10268
10877
  ...state,
10269
- team: [...state.team, action.agent],
10878
+ team: [...state.team, action2.agent],
10270
10879
  localOnlyAgentIds: nextLocalOnly
10271
10880
  };
10272
10881
  }
10273
10882
  case "SCHEMA_SEEDED":
10274
- return { ...state, schemaNodeTypeCount: action.nodeTypeCount, schemaEdgeTypeCount: action.edgeTypeCount };
10883
+ return { ...state, schemaNodeTypeCount: action2.nodeTypeCount, schemaEdgeTypeCount: action2.edgeTypeCount };
10275
10884
  case "SCHEMA_TYPE_ADDED":
10276
- return { ...state, schemaNodeTypes: state.schemaNodeTypes.includes(action.typeName) ? state.schemaNodeTypes : [...state.schemaNodeTypes, action.typeName] };
10885
+ return { ...state, schemaNodeTypes: state.schemaNodeTypes.includes(action2.typeName) ? state.schemaNodeTypes : [...state.schemaNodeTypes, action2.typeName] };
10277
10886
  case "SCHEMA_TEXT": {
10278
10887
  const rels = [];
10279
10888
  const nodeTypes2 = new Set(state.schemaNodeTypes);
10280
10889
  const relPattern = /(\w+)\s*:\s*\[([^\]]*)\]\s*→\s*\[([^\]]*)\]/g;
10281
10890
  let m;
10282
- while ((m = relPattern.exec(action.text)) !== null) {
10891
+ while ((m = relPattern.exec(action2.text)) !== null) {
10283
10892
  const name = m[1];
10284
10893
  const domain = m[2].split(",").map((s) => s.replace(/"/g, "").trim()).filter(Boolean);
10285
10894
  const range = m[3].split(",").map((s) => s.replace(/"/g, "").trim()).filter(Boolean);
@@ -10294,7 +10903,7 @@ function stageReducer(state, action) {
10294
10903
  };
10295
10904
  }
10296
10905
  case "DOC_STEP": {
10297
- const step = action.step;
10906
+ const step = action2.step;
10298
10907
  if (step === "complete") return { ...state, stage: "graph" };
10299
10908
  if (step === "processing" || step === "vocabulary") return { ...state, stage: "doc_preparing" };
10300
10909
  if (step === "extracting") return { ...state, stage: "doc_extracting" };
@@ -10303,7 +10912,7 @@ function stageReducer(state, action) {
10303
10912
  return state;
10304
10913
  }
10305
10914
  case "DOC_ACTIVITY":
10306
- return { ...state, docActivity: [...state.docActivity, action.item] };
10915
+ return { ...state, docActivity: [...state.docActivity, action2.item] };
10307
10916
  case "FORCE_GRAPH":
10308
10917
  return { ...state, stage: "graph" };
10309
10918
  default:
@@ -11169,7 +11778,7 @@ function PipelineProgressBar({ listenForEvents = true, currentPhase: currentPhas
11169
11778
  PIPELINE_STEPS.map((step, i) => {
11170
11779
  const isCompleted = completed.has(step.key) || i < currentIdx;
11171
11780
  const isCurrent = step.key === currentPhase;
11172
- return /* @__PURE__ */ jsxs(React12.Fragment, { children: [
11781
+ return /* @__PURE__ */ jsxs(React15.Fragment, { children: [
11173
11782
  i > 0 && /* @__PURE__ */ jsx(
11174
11783
  "div",
11175
11784
  {
@@ -12957,19 +13566,19 @@ function KnowledgeGraphView({
12957
13566
  setSearching(true);
12958
13567
  searchTimerRef.current = setTimeout(async () => {
12959
13568
  try {
12960
- const result = data.onSearchNodes ? await data.onSearchNodes(groupId, kgId, q, 10, 2) : { hits: [], traversal: [] };
12961
- setSearchResult(result);
12962
- if (result.hits.length === 0 && localMatchIds.size === 0) {
13569
+ const result2 = data.onSearchNodes ? await data.onSearchNodes(groupId, kgId, q, 10, 2) : { hits: [], traversal: [] };
13570
+ setSearchResult(result2);
13571
+ if (result2.hits.length === 0 && localMatchIds.size === 0) {
12963
13572
  restoreFullGraph();
12964
13573
  setSearching(false);
12965
13574
  return;
12966
13575
  }
12967
- if (result.hits.length === 0) {
13576
+ if (result2.hits.length === 0) {
12968
13577
  setSearching(false);
12969
13578
  return;
12970
13579
  }
12971
13580
  const scoreMap = /* @__PURE__ */ new Map();
12972
- result.hits.forEach((h) => {
13581
+ result2.hits.forEach((h) => {
12973
13582
  const existing = scoreMap.get(h.node_key) ?? 0;
12974
13583
  if ((h.score ?? 0) > existing) scoreMap.set(h.node_key, h.score ?? 0);
12975
13584
  });
@@ -12977,9 +13586,9 @@ function KnowledgeGraphView({
12977
13586
  if (!scoreMap.has(id)) scoreMap.set(id, 0.5);
12978
13587
  });
12979
13588
  const traversalIds = /* @__PURE__ */ new Set();
12980
- result.traversal.forEach((t) => traversalIds.add(t.node_key));
13589
+ result2.traversal.forEach((t) => traversalIds.add(t.node_key));
12981
13590
  const neighborIds = /* @__PURE__ */ new Set();
12982
- result.hits.forEach((h) => {
13591
+ result2.hits.forEach((h) => {
12983
13592
  h.neighbors?.forEach((nk) => {
12984
13593
  if (!scoreMap.has(nk)) neighborIds.add(nk);
12985
13594
  });
@@ -13468,9 +14077,9 @@ function KnowledgeGraphView({
13468
14077
  if (!data.onCreateNote || !draftNote || !draftNote.trim()) return;
13469
14078
  setSavingDraft(true);
13470
14079
  try {
13471
- const result = await data.onCreateNote(groupId, kgId, draftNote);
14080
+ const result2 = await data.onCreateNote(groupId, kgId, draftNote);
13472
14081
  setDraftNote(null);
13473
- setSelectedNodeId(`chunk:${result.chunk_id}`);
14082
+ setSelectedNodeId(`chunk:${result2.chunk_id}`);
13474
14083
  if (data.onLoadAllChunks) {
13475
14084
  try {
13476
14085
  const fresh = await data.onLoadAllChunks(groupId, kgId);
@@ -13479,10 +14088,10 @@ function KnowledgeGraphView({
13479
14088
  console.warn("[KGV] post-create chunk refetch failed", err);
13480
14089
  }
13481
14090
  }
13482
- if (result.run_id && config?.agentsApiUrl) {
14091
+ if (result2.run_id && config?.agentsApiUrl) {
13483
14092
  setNoteRuns((prev) => [
13484
14093
  ...prev,
13485
- { run_id: result.run_id, kg_id: kgId }
14094
+ { run_id: result2.run_id, kg_id: kgId }
13486
14095
  ]);
13487
14096
  }
13488
14097
  } catch (err) {
@@ -14037,4 +14646,4 @@ var terminalClassNames = {
14037
14646
  promptSymbol: "terminal-prompt-symbol"
14038
14647
  };
14039
14648
 
14040
- export { ADVANCE_PHASE_SIGNAL, AGENT_PALETTE, AgentAvatar, AgentPresenceBar, AnnotationThread, CLAIM_STATUS_COLORS, ChatTerminal, CheckpointCard, ClaimNode, ConstructionProgress, CoreTerminal, DOCUMENT_PIPELINE_STEPS, DocumentProcessingProgress, EDGE_TYPE_COLORS, EdgeDetailDrawer, EdgeMarkerDefs, CoreTerminal as EmbeddedTerminal, ExpandableTerminal, GraphChatPanel, GraphEdge, GraphNode, InlineTerminal, KnowledgeGraphView, LIVE_KG_ID, LiveActivityTicker, MarkdownContent, MarkdownEditor, NODE_TYPE_COLORS, NODE_TYPE_LABELS, NodeDetailDrawer, PROGRESS_EVENT_NAME, PipelineProgressBar, ReasoningHistoryCard, ReasoningOverlay, ReasoningView, SYNTHESIS_DONE_SIGNAL, ScopedReasoningProvider, SettlementBar, SlashMentionPicker, StageCanvas, StandardTerminal, TerminalInputBar, TerminalThemeProvider, agentSeed, avatarColor, blueScope, buildClaimsGraph, buildTerminalCommand, buttonShadow, captureReasoningSnapshot, defaultTheme, dispatchProgressEvent, documentEventToActivity, emeraldCollab, emeraldRevised, eventToActivity, extractKGSources, extractKgId, filterCommands, formatAssetDisplay, formatPaymentAmount, formatTokenAssetId, getAgentColor, getBuiltInCommands, getClaimStatusColors, getEdgeColor, getEdgeTypeLabel, getNodeColors, getNodeTypeLabel, getVocabTypeColors, getVocabTypeLabel, greenAction, handleSSEStream, handleStreamingResponse, hostApp, initials, isFixedKgId, isNaturalLanguage, layoutGraph, mergeTheme, openPipelineEventStream, orangeRebuttal, parseTerminalCommand, pipelineBg, processStreamLines, purple, reduceProgress, renderMarkdownBlock, resolvePaymentAssetDisplay, snapshotToSubgraph, terminalClassNames, terminalColors, useScopedProgressEvent, useSlashMentionPicker, useTheme, warmBg };
14649
+ export { ADVANCE_PHASE_SIGNAL, AGENT_PALETTE, AgentAvatar, AgentPresenceBar, AgentStepTrace, AnnotationThread, CLAIM_STATUS_COLORS, ChatTerminal, CheckpointCard, ClaimNode, ConstructionProgress, CoreTerminal, DEFAULT_AGENT_LABEL, DOCUMENT_PIPELINE_STEPS, DocumentProcessingProgress, EDGE_TYPE_COLORS, EdgeDetailDrawer, EdgeMarkerDefs, CoreTerminal as EmbeddedTerminal, ExpandableTerminal, GraphChatPanel, GraphEdge, GraphNode, InlineTerminal, KnowledgeGraphView, LIVE_KG_ID, LiveActivityTicker, MarkdownContent, MarkdownEditor, NODE_TYPE_COLORS, NODE_TYPE_LABELS, NodeDetailDrawer, PROGRESS_EVENT_NAME, PipelineProgressBar, ReasoningHistoryCard, ReasoningOverlay, ReasoningView, SSE_KEEPALIVE_MS, SSE_STALL_MS, SYNTHESIS_DONE_SIGNAL, ScopedReasoningProvider, SettlementBar, SlashMentionPicker, StageCanvas, StandardTerminal, TerminalInputBar, TerminalThemeProvider, agentSeed, agentStepTraceExtra, avatarColor, blueScope, buildClaimsGraph, buildTerminalCommand, buttonShadow, captureReasoningSnapshot, defaultTheme, dispatchProgressEvent, documentEventToActivity, emeraldCollab, emeraldRevised, eventToActivity, extractKGSources, extractKgId, filterCommands, formatAssetDisplay, formatPaymentAmount, formatTokenAssetId, getAgentColor, getBuiltInCommands, getClaimStatusColors, getEdgeColor, getEdgeTypeLabel, getNodeColors, getNodeTypeLabel, getVocabTypeColors, getVocabTypeLabel, greenAction, handleSSEStream, handleStreamingResponse, hostApp, initials, isFixedKgId, isNaturalLanguage, layoutGraph, mergeTheme, openPipelineEventStream, orangeRebuttal, parseTerminalCommand, pipelineBg, processStreamLines, purple, reduceProgress, renderMarkdownBlock, resolvePaymentAssetDisplay, snapshotToSubgraph, terminalClassNames, terminalColors, useScopedProgressEvent, useSlashMentionPicker, useTheme, warmBg, withStallTimeout };