@invergent/agent-chat-react 1.6.6 → 1.6.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -2291,10 +2291,7 @@ function WebToolBlock({ tc }) {
2291
2291
  let displayText = "";
2292
2292
  try {
2293
2293
  const args = JSON.parse(tc.args);
2294
- if (tc.toolName === "web_extract") {
2295
- const urls = args.urls ?? [];
2296
- displayText = urls[0] ?? "";
2297
- } else if (tc.toolName === "web_search") {
2294
+ if (tc.toolName === "web_search") {
2298
2295
  displayText = args.query ?? "";
2299
2296
  } else if (tc.toolName === "web_crawl") {
2300
2297
  displayText = args.url ?? "";
@@ -2302,12 +2299,10 @@ function WebToolBlock({ tc }) {
2302
2299
  } catch {
2303
2300
  }
2304
2301
  const toolLabel2 = {
2305
- web_extract: "Web Fetch",
2306
2302
  web_search: "Web Search",
2307
2303
  web_crawl: "Web Crawl"
2308
2304
  }[tc.toolName] ?? tc.toolName;
2309
2305
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-sm py-0.5", children: [
2310
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.GlobeIcon, { className: "size-3.5 text-muted-foreground/60 shrink-0" }),
2311
2306
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-foreground", children: toolLabel2 }),
2312
2307
  displayText && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground/70 truncate text-xs", children: displayText })
2313
2308
  ] });
@@ -2327,6 +2322,89 @@ function VisionAnalyzeBlock({ tc }) {
2327
2322
  ] });
2328
2323
  }
2329
2324
  init_utils();
2325
+ function parseUrls(rawArgs) {
2326
+ try {
2327
+ const args = JSON.parse(rawArgs);
2328
+ if (Array.isArray(args.urls)) {
2329
+ return args.urls.filter((u) => typeof u === "string");
2330
+ }
2331
+ } catch {
2332
+ }
2333
+ return [];
2334
+ }
2335
+ function parseResult(rawResult) {
2336
+ const byUrl = /* @__PURE__ */ new Map();
2337
+ if (!rawResult) return { byUrl, toolError: null };
2338
+ try {
2339
+ const parsed = JSON.parse(rawResult);
2340
+ const entries = Array.isArray(parsed?.results) ? parsed.results : [];
2341
+ for (const entry of entries) {
2342
+ if (entry && typeof entry.url === "string") {
2343
+ byUrl.set(entry.url, entry);
2344
+ }
2345
+ }
2346
+ if (byUrl.size === 0 && typeof parsed?.error === "string") {
2347
+ return { byUrl, toolError: parsed.error };
2348
+ }
2349
+ } catch {
2350
+ }
2351
+ return { byUrl, toolError: null };
2352
+ }
2353
+ function domain(url) {
2354
+ try {
2355
+ return new URL(url).hostname.replace(/^www\./, "");
2356
+ } catch {
2357
+ return url;
2358
+ }
2359
+ }
2360
+ function WebExtractToolBlock({ tc }) {
2361
+ const urls = parseUrls(tc.args);
2362
+ const { byUrl, toolError } = parseResult(tc.result);
2363
+ const isComplete = tc.status === "complete" || tc.status === "error";
2364
+ if (urls.length === 0) {
2365
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-sm py-0.5", children: [
2366
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.GlobeIcon, { className: "size-3.5 text-muted-foreground/60 shrink-0" }),
2367
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-foreground", children: "Web Fetch" })
2368
+ ] });
2369
+ }
2370
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
2371
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-sm", children: [
2372
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.GlobeIcon, { className: "size-3.5 text-muted-foreground/60 shrink-0" }),
2373
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-foreground", children: "Web Fetch" }),
2374
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-muted-foreground/70 text-xs", children: [
2375
+ urls.length,
2376
+ " URL",
2377
+ urls.length !== 1 ? "s" : ""
2378
+ ] })
2379
+ ] }),
2380
+ /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "mt-1 ml-5 space-y-0.5", children: urls.map((url, i) => {
2381
+ const entry = byUrl.get(url);
2382
+ const perUrlError = entry?.error;
2383
+ const failure = perUrlError ?? (isComplete && !entry ? toolError : null);
2384
+ const fetched = entry && !perUrlError;
2385
+ const pending = !isComplete && !entry;
2386
+ const row = /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-xs", children: [
2387
+ /* @__PURE__ */ jsxRuntime.jsx(
2388
+ "span",
2389
+ {
2390
+ className: cn(
2391
+ "truncate",
2392
+ pending ? "text-muted-foreground/50" : "text-muted-foreground"
2393
+ ),
2394
+ children: domain(url)
2395
+ }
2396
+ ),
2397
+ fetched && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CheckIcon, { className: "size-3 text-green-500/70 shrink-0" }),
2398
+ failure && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.XIcon, { className: "size-3 text-red-500/80 shrink-0" })
2399
+ ] });
2400
+ return /* @__PURE__ */ jsxRuntime.jsx("li", { children: failure ? /* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
2401
+ /* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: row }),
2402
+ /* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { side: "top", children: failure })
2403
+ ] }) : row }, `${url}-${i}`);
2404
+ }) })
2405
+ ] });
2406
+ }
2407
+ init_utils();
2330
2408
 
2331
2409
  // src/components/chat/diff-viewer.tsx
2332
2410
  init_utils();
@@ -3015,7 +3093,7 @@ function parseArgs2(args) {
3015
3093
  return { action: "unknown" };
3016
3094
  }
3017
3095
  }
3018
- function parseResult(result) {
3096
+ function parseResult2(result) {
3019
3097
  if (!result) return null;
3020
3098
  try {
3021
3099
  return JSON.parse(result);
@@ -3233,7 +3311,7 @@ function OutputPreview({ output }) {
3233
3311
  function ProcessToolBlock({ tc }) {
3234
3312
  const isRunning = tc.status === "running";
3235
3313
  const args = parseArgs2(tc.args);
3236
- const result = parseResult(tc.result);
3314
+ const result = parseResult2(tc.result);
3237
3315
  const action = args.action || "unknown";
3238
3316
  const actionLabel = {
3239
3317
  list: "Process List",
@@ -4289,6 +4367,7 @@ function ToolCallBlock({
4289
4367
  case "session_search":
4290
4368
  return /* @__PURE__ */ jsxRuntime.jsx(SessionSearchBlock, { tc });
4291
4369
  case "web_extract":
4370
+ return /* @__PURE__ */ jsxRuntime.jsx(WebExtractToolBlock, { tc });
4292
4371
  case "web_search":
4293
4372
  case "web_crawl":
4294
4373
  return /* @__PURE__ */ jsxRuntime.jsx(WebToolBlock, { tc });
@@ -4330,6 +4409,51 @@ function ToolCallBlock({
4330
4409
  return /* @__PURE__ */ jsxRuntime.jsx(DefaultToolBlock, { tc });
4331
4410
  }
4332
4411
  }
4412
+ function parseQuery(rawArgs) {
4413
+ try {
4414
+ const args = JSON.parse(rawArgs);
4415
+ return typeof args.query === "string" ? args.query : "";
4416
+ } catch {
4417
+ return "";
4418
+ }
4419
+ }
4420
+ function parseResultCount2(rawResult) {
4421
+ if (!rawResult) return null;
4422
+ try {
4423
+ const parsed = JSON.parse(rawResult);
4424
+ if (Array.isArray(parsed?.data?.web)) return parsed.data.web.length;
4425
+ if (Array.isArray(parsed?.results)) return parsed.results.length;
4426
+ } catch {
4427
+ }
4428
+ return null;
4429
+ }
4430
+ function WebSearchRow({ tc }) {
4431
+ const query = parseQuery(tc.args);
4432
+ const count = parseResultCount2(tc.result);
4433
+ const showCount = count !== null && tc.status === "complete";
4434
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-xs min-w-0", children: [
4435
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground truncate", children: query || "\u2014" }),
4436
+ showCount && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-muted-foreground/50 shrink-0", children: [
4437
+ count,
4438
+ " result",
4439
+ count !== 1 ? "s" : ""
4440
+ ] })
4441
+ ] });
4442
+ }
4443
+ function WebSearchGroupBlock({ tcs }) {
4444
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
4445
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-sm", children: [
4446
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.SearchIcon, { className: "size-3.5 text-muted-foreground/60 shrink-0" }),
4447
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-foreground", children: "Web Search" }),
4448
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-muted-foreground/70 text-xs", children: [
4449
+ tcs.length,
4450
+ " ",
4451
+ tcs.length === 1 ? "query" : "queries"
4452
+ ] })
4453
+ ] }),
4454
+ /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "mt-1 ml-5 space-y-0.5", children: tcs.map((tc) => /* @__PURE__ */ jsxRuntime.jsx("li", { children: /* @__PURE__ */ jsxRuntime.jsx(WebSearchRow, { tc }) }, tc.id)) })
4455
+ ] });
4456
+ }
4333
4457
  var ChatMessage = react.memo(function ChatMessage2({
4334
4458
  message,
4335
4459
  isLast,
@@ -5756,6 +5880,11 @@ function ChatComposerInner({
5756
5880
  const builtin = [
5757
5881
  { value: "/clear", label: "/clear", description: "Clear conversation" },
5758
5882
  { value: "/compress", label: "/compress", description: "Compress context" },
5883
+ { value: "/goal", label: "/goal", description: "Define an outcome goal" },
5884
+ { value: "/goal status", label: "/goal status", description: "Show outcome goal status" },
5885
+ { value: "/goal pause", label: "/goal pause", description: "Pause automatic goal continuation" },
5886
+ { value: "/goal resume", label: "/goal resume", description: "Resume a paused goal" },
5887
+ { value: "/goal clear", label: "/goal clear", description: "Clear the current goal" },
5759
5888
  { value: "/loop", label: "/loop", description: "Schedule recurring prompt" },
5760
5889
  { value: "/loop list", label: "/loop list", description: "List active loops" },
5761
5890
  { value: "/loop cancel", label: "/loop cancel", description: "Cancel a loop by ID" }
@@ -6589,6 +6718,33 @@ function groupBrowserActivityEntries(entries) {
6589
6718
  function isBrowserToolCall(call) {
6590
6719
  return call.toolName.startsWith("browser_");
6591
6720
  }
6721
+ function groupWebSearchEntries(entries) {
6722
+ const grouped = [];
6723
+ let buffer = [];
6724
+ const flush = () => {
6725
+ if (buffer.length === 0) return;
6726
+ if (buffer.length === 1) {
6727
+ grouped.push({ kind: "tool", key: buffer[0].id, tc: buffer[0] });
6728
+ } else {
6729
+ grouped.push({
6730
+ kind: "web_search_group",
6731
+ key: `web-search-${buffer[0].id}-${buffer[buffer.length - 1].id}`,
6732
+ calls: buffer
6733
+ });
6734
+ }
6735
+ buffer = [];
6736
+ };
6737
+ for (const entry of entries) {
6738
+ if (entry.kind === "tool" && entry.tc.toolName === "web_search") {
6739
+ buffer.push(entry.tc);
6740
+ continue;
6741
+ }
6742
+ flush();
6743
+ grouped.push(entry);
6744
+ }
6745
+ flush();
6746
+ return grouped;
6747
+ }
6592
6748
  function groupMessages(messages) {
6593
6749
  const groups = [];
6594
6750
  let pendingSystem = [];
@@ -6754,6 +6910,18 @@ function TimelineEntryItem({
6754
6910
  /* @__PURE__ */ jsxRuntime.jsx(TimelineContent, { children: /* @__PURE__ */ jsxRuntime.jsx(BrowserActivityGroup, { calls: entry.calls }) })
6755
6911
  ] });
6756
6912
  }
6913
+ if (entry.kind === "web_search_group") {
6914
+ const allComplete = entry.calls.every((tc) => tc.status === "complete");
6915
+ const anyRunning = entry.calls.some((tc) => tc.status === "running");
6916
+ const indicatorClass = anyRunning ? "bg-emerald-500 animate-pulse" : allComplete ? "bg-emerald-500" : "bg-red-500";
6917
+ return /* @__PURE__ */ jsxRuntime.jsxs(TimelineItem, { step, children: [
6918
+ /* @__PURE__ */ jsxRuntime.jsxs(TimelineHeader, { children: [
6919
+ /* @__PURE__ */ jsxRuntime.jsx(TimelineSeparator, { style: { backgroundColor: "var(--color-border)" } }),
6920
+ /* @__PURE__ */ jsxRuntime.jsx(TimelineIndicator, { className: cn("size-2 border-none", indicatorClass) })
6921
+ ] }),
6922
+ /* @__PURE__ */ jsxRuntime.jsx(TimelineContent, { children: /* @__PURE__ */ jsxRuntime.jsx(WebSearchGroupBlock, { tcs: entry.calls }) })
6923
+ ] });
6924
+ }
6757
6925
  if (entry.kind === "text") {
6758
6926
  return /* @__PURE__ */ jsxRuntime.jsxs(TimelineItem, { step, children: [
6759
6927
  /* @__PURE__ */ jsxRuntime.jsxs(TimelineHeader, { children: [
@@ -6821,6 +6989,7 @@ function AssistantGroup({
6821
6989
  entries.push(...messageToEntries(messages[i], isLast, artifactFallbacks));
6822
6990
  }
6823
6991
  entries = groupBrowserActivityEntries(entries);
6992
+ entries = groupWebSearchEntries(entries);
6824
6993
  const isTailGroup = lastGlobalIndex === totalMessages - 1;
6825
6994
  const lastEntry = entries[entries.length - 1];
6826
6995
  const hasRunningTool = entries.some(
@@ -8985,11 +9154,12 @@ function useAgentChatRuntime({
8985
9154
  const send = react.useCallback(
8986
9155
  async (content, images) => {
8987
9156
  markSending(content, images);
9157
+ const goal = images?.length ? null : parseGoalDefinition(content);
8988
9158
  if (!sessionId) {
8989
9159
  try {
8990
9160
  const session2 = await adapter.createSession({ agentId });
8991
9161
  onSessionChange?.(session2.id);
8992
- await adapter.sendMessage({ sessionId: session2.id, content, images });
9162
+ await sendTurn(adapter, session2.id, content, images, goal);
8993
9163
  } catch (error) {
8994
9164
  markSendError(error instanceof Error ? error.message : "send failed");
8995
9165
  throw error;
@@ -8998,7 +9168,7 @@ function useAgentChatRuntime({
8998
9168
  }
8999
9169
  try {
9000
9170
  ensureStreamForNewTurn(sessionId);
9001
- await adapter.sendMessage({ sessionId, content, images });
9171
+ await sendTurn(adapter, sessionId, content, images, goal);
9002
9172
  } catch (error) {
9003
9173
  markSendError(error instanceof Error ? error.message : "send failed");
9004
9174
  throw error;
@@ -9071,6 +9241,30 @@ function findLastAssistantIndex2(messages) {
9071
9241
  }
9072
9242
  return -1;
9073
9243
  }
9244
+ var GOAL_RUBRIC_RE = /\n\s*(?:rubric|criteria)\s*:\s*\n/i;
9245
+ var GOAL_CONTROL_COMMANDS = /* @__PURE__ */ new Set(["", "status", "pause", "resume", "clear"]);
9246
+ async function sendTurn(adapter, sessionId, content, images, goal) {
9247
+ if (goal && adapter.defineOutcome) {
9248
+ await adapter.defineOutcome({
9249
+ sessionId,
9250
+ description: goal.description,
9251
+ rubric: goal.rubric
9252
+ });
9253
+ return;
9254
+ }
9255
+ await adapter.sendMessage({ sessionId, content, images });
9256
+ }
9257
+ function parseGoalDefinition(content) {
9258
+ const text = content.trim();
9259
+ if (text !== "/goal" && !text.startsWith("/goal ")) return null;
9260
+ const args = text.slice("/goal".length).trim();
9261
+ if (GOAL_CONTROL_COMMANDS.has(args.toLowerCase())) return null;
9262
+ const match = GOAL_RUBRIC_RE.exec(args);
9263
+ const description = (match ? args.slice(0, match.index) : args).trim();
9264
+ if (!description) return null;
9265
+ const rubric = match ? args.slice(match.index + match[0].length).trim() : "";
9266
+ return rubric ? { description, rubric } : { description };
9267
+ }
9074
9268
  function AgentChat({
9075
9269
  adapter,
9076
9270
  agentId,