@athenaintel/react 0.11.0 → 0.11.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/README.md CHANGED
@@ -103,9 +103,83 @@ What the statewire transport wires up in `AthenaChat`:
103
103
  statewire session to the selected thread. `ThreadList` and
104
104
  `useAthenaThreadManager` work in both modes.
105
105
 
106
- Remaining limitations: the `agent` prop is legacy-only (the statewire host
107
- always runs the Athena deep agent), and `model` defaults to the deep-agent
108
- default model in this mode.
106
+ Remaining limitations: on statewire the `agent` prop only accepts
107
+ `collab_agent:<asset_id>` refs (see below) any other value is ignored,
108
+ because the statewire host always runs the Athena deep agent. `model`
109
+ defaults to the deep-agent default model unless a collab agent supplies one.
110
+
111
+ ## Collab Agents
112
+
113
+ A **collab agent** is an agent configuration authored in Athena (prompt,
114
+ model, toolkits, behavior) and addressed as `collab_agent:<asset_id>`. Point
115
+ the provider at one and the chat runs **as** that agent:
116
+
117
+ ```tsx
118
+ <AthenaProvider
119
+ transport="statewire"
120
+ agent="collab_agent:asset_432af46f-293d-480b-a518-30b1f42a9ef7"
121
+ channel="askbob_web"
122
+ >
123
+ <AthenaChat />
124
+ </AthenaProvider>
125
+ ```
126
+
127
+ Copy the snippet with real ids from the agent's **Channels** tab in Athena.
128
+
129
+ `channel` is optional. It selects a **channel override layer** — a built-in
130
+ channel (`email`, `sms`, …) or a custom channel defined on that agent — so one
131
+ agent can present different prompts, models, and tools per surface. Omit it to
132
+ run the agent's base configuration.
133
+
134
+ ### Requirements
135
+
136
+ - **`transport="statewire"`.** The legacy transport ignores `collab_agent:` refs.
137
+ - **Publish the agent.** Resolution reads the *published* snapshot, not the
138
+ live draft, so a channel that exists only in the editor is rejected with
139
+ `collab_agent_channel_unknown`.
140
+ - **The acting user needs VIEW access** to the agent asset (admins bypass).
141
+ Running as an agent exposes its prompt and tool policy, so the same gate that
142
+ governs opening the asset governs running it.
143
+
144
+ ### Do not also pass `model`, `systemPrompt`, or `tools`
145
+
146
+ Request keys override the agent definition, so **anything you pass here
147
+ silently replaces what the agent's author configured** — the run succeeds and
148
+ returns a plausible answer using your config instead of theirs.
149
+
150
+ ```tsx
151
+ // ❌ the agent's prompt, model, and tools are all discarded
152
+ <AthenaProvider
153
+ transport="statewire"
154
+ agent="collab_agent:asset_1234"
155
+ model="claude-opus-4-6"
156
+ systemPrompt="You are a helpful assistant."
157
+ tools={['web_search_browse_toolkit']}
158
+ />
159
+
160
+ // ✅ the agent's own configuration wins
161
+ <AthenaProvider transport="statewire" agent="collab_agent:asset_1234" />
162
+ ```
163
+
164
+ Pass them only when you deliberately want to override the agent — an explicit
165
+ `model` is honoured as a caller override, with the agent's model as the default
166
+ beneath it.
167
+
168
+ ### Failure codes
169
+
170
+ A refused selection surfaces as an `AthenaSdkError` with
171
+ `code: 'collab_agent_rejected'`; the specific reason below rides in the error's
172
+ `detail`. Subscribe with `onError` (see
173
+ [Debugging](#debugging-and-diagnostics)) rather than guessing from messages:
174
+
175
+ | Code | Meaning |
176
+ |---|---|
177
+ | `collab_agent_not_found` | No such asset, or it is not a `collab_agent` |
178
+ | `collab_agent_forbidden` | The acting user lacks VIEW on the asset |
179
+ | `collab_agent_channel_unknown` | No such channel (the message lists the known ones) |
180
+ | `collab_agent_channel_disabled` | The channel exists but is toggled off |
181
+ | `collab_agent_channel_kind_mismatch` | A voice channel was selected for a text run |
182
+ | `collab_agent_runs_disabled` | The deployment has the SDK seam turned off |
109
183
 
110
184
  ## Authentication
111
185
 
@@ -323,6 +397,52 @@ function WorkflowButton() {
323
397
  }
324
398
  ```
325
399
 
400
+ ## Debugging and Diagnostics
401
+
402
+ The SDK emits structured diagnostic events — auth handshake, thread list,
403
+ statewire attach, sends, errors — with timings. Nothing is logged by default;
404
+ turn it on with the `debug` prop:
405
+
406
+ ```tsx
407
+ <AthenaProvider
408
+ debug // or 'debug' | 'info' | { console: 'debug', posthog: true }
409
+ onDiagnostic={(event) => console.log(event.name, event.durationMs)}
410
+ onError={(error) => reportToSentry(error)}
411
+ >
412
+ ```
413
+
414
+ `onError` receives an `AthenaSdkError` with a stable `code`, a human `hint`,
415
+ and the originating `status`/`detail` — match on `code`, never on message text:
416
+
417
+ ```tsx
418
+ import { ATHENA_SDK_ERROR_CODES } from '@athenaintel/react';
419
+
420
+ onError={(error) => {
421
+ if (error.code === ATHENA_SDK_ERROR_CODES.collab_agent_rejected) {
422
+ // the backend's granular reason rides in `detail`, e.g.
423
+ // 'collab_agent_channel_unknown' — whose message lists the known channels
424
+ console.warn(error.detail, error.hint);
425
+ }
426
+ }}
427
+ ```
428
+
429
+ Each provider registers its own consumer, so multiple mounted providers all
430
+ receive every event; a callback that throws is caught and cannot break the chat.
431
+
432
+ Without a rebuild, from the browser console:
433
+
434
+ ```js
435
+ localStorage.setItem('athena:debug', 'debug'); // console echo on, survives reload
436
+ __ATHENA_SDK__.diagnostics.snapshot(); // recent events + timings
437
+ __ATHENA_SDK__.diagnostics.export(); // JSON, for attaching to a bug report
438
+ ```
439
+
440
+ Credentials are redacted everywhere (buffer, console, PostHog): any key
441
+ matching `token`, `secret`, `api[-_]?key`, `authorization`, `cookie`, or
442
+ `password` is stripped before an event is recorded. Spans also emit
443
+ `performance.mark`/`measure` entries prefixed `athena-sdk:`, so they show up on
444
+ the browser Performance timeline.
445
+
326
446
  ## License
327
447
 
328
448
  Proprietary. For licensed enterprise customers only.
package/dist/index.cjs CHANGED
@@ -72,7 +72,7 @@ function _interopNamespaceDefault(e) {
72
72
  }
73
73
  const React__namespace = /* @__PURE__ */ _interopNamespaceDefault(React);
74
74
  const ReactDOM__namespace = /* @__PURE__ */ _interopNamespaceDefault(ReactDOM);
75
- const version$1 = "0.11.0";
75
+ const version$1 = "0.11.1";
76
76
  const packageJson = {
77
77
  version: version$1
78
78
  };
@@ -12070,6 +12070,9 @@ function useDeepAgentThread({
12070
12070
  return statewireTransport(
12071
12071
  {
12072
12072
  url: `${baseUrl.replace(/\/+$/, "")}/${encodeURIComponent(threadId)}`,
12073
+ // A never-started chat has no server session yet: the transport
12074
+ // makes no network calls until the first command send.
12075
+ ...preloadRef.current.preload && { isNew: true },
12073
12076
  headers: (ctx) => headersRef.current(ctx),
12074
12077
  ...sessionStore && { sessionStore }
12075
12078
  },
@@ -56038,13 +56041,13 @@ const TiptapComposer = ({ tools = [], rootCategories }) => {
56038
56041
  });
56039
56042
  if (fullMessage) {
56040
56043
  aui.composer.setText(fullMessage);
56041
- aui.composer.send();
56044
+ aui.composer.send({ steer: false });
56042
56045
  clearAttachments();
56043
56046
  clearQuote();
56044
56047
  }
56045
56048
  } else {
56046
56049
  aui.composer.setText(markdown);
56047
- aui.composer.send();
56050
+ aui.composer.send({ steer: false });
56048
56051
  }
56049
56052
  editor2.commands.clearContent();
56050
56053
  }, [aui, clearAttachments, clearQuote, appUrl]);
@@ -56681,6 +56684,10 @@ const TOOL_META = {
56681
56684
  // Presentations
56682
56685
  create_powerpoint_deck: { displayName: "Building presentation", icon: Monitor },
56683
56686
  execute_presentation_code: { displayName: "Generating slides", icon: Monitor },
56687
+ execute_word_document_code: { displayName: "Editing Word document", icon: FileText },
56688
+ execute_spreadsheet_code: { displayName: "Editing spreadsheet", icon: ChartColumn },
56689
+ execute_spreadsheet_commands: { displayName: "Editing spreadsheet", icon: ChartColumn },
56690
+ unified_email_list_accounts: { displayName: "Checking email accounts", icon: Mail },
56684
56691
  // Code & Data
56685
56692
  run_python_code: { displayName: "Running analysis", icon: Code },
56686
56693
  run_sql_query_tool: { displayName: "Running SQL query", icon: Database, describer: (a) => a.sql_query ?? a.query ?? a.sql ? `"${truncateLine(a.sql_query ?? a.query ?? a.sql)}"` : "" },
@@ -56728,14 +56735,14 @@ function extractResultMessage(result) {
56728
56735
  return null;
56729
56736
  }
56730
56737
  function isResultSuccess(result) {
56731
- if (typeof result === "string") {
56732
- const parsed = tryParseJson$1(result);
56733
- if (parsed) return parsed.success === true;
56734
- }
56735
- if (typeof result === "object" && result !== null) {
56736
- return result.success === true;
56738
+ const obj = typeof result === "string" ? tryParseJson$1(result) : typeof result === "object" && result !== null ? result : null;
56739
+ if (obj) {
56740
+ if (obj.success === false || obj.ok === false) return false;
56741
+ if (typeof obj.error === "string" && obj.error.length > 0) return false;
56742
+ return true;
56737
56743
  }
56738
- return false;
56744
+ if (typeof result === "string") return !/^\s*error\b/i.test(result);
56745
+ return true;
56739
56746
  }
56740
56747
  function extractAssetId$1(result) {
56741
56748
  if (!result) return null;
@@ -56900,7 +56907,7 @@ function ToolFallbackTrigger({
56900
56907
  "span",
56901
56908
  {
56902
56909
  className: cn(
56903
- "relative inline-block leading-tight",
56910
+ "relative inline-block text-[13px] font-medium leading-tight",
56904
56911
  isCancelled && "text-muted-foreground line-through"
56905
56912
  ),
56906
56913
  children: [
@@ -57885,17 +57892,103 @@ BrowseToolUI.displayName = "BrowseToolUI";
57885
57892
  function parseEmailResults(data) {
57886
57893
  const results = data.limited_results ?? data.results ?? data.emails ?? data.messages;
57887
57894
  if (!Array.isArray(results)) return [];
57888
- return results.slice(0, 10).map((r2) => {
57889
- const item = r2;
57890
- return {
57891
- subject: item.subject ?? item.title,
57892
- from: item.from ?? item.sender ?? item.from_email,
57893
- date: item.date ?? item.received_at ?? item.sent_at,
57894
- snippet: item.snippet ?? item.preview ?? item.body_preview,
57895
- isDraft: Boolean(item.is_draft)
57896
- };
57895
+ return results.slice(0, 50).flatMap((r2) => {
57896
+ const item = asRecord(r2);
57897
+ if (!item) return [];
57898
+ return [
57899
+ {
57900
+ subject: asString(item.subject) ?? asString(item.title),
57901
+ from: asString(item.from) ?? asString(item.sender) ?? asString(item.from_email),
57902
+ date: asString(item.date) ?? asString(item.received_at) ?? asString(item.sent_at),
57903
+ snippet: asString(item.snippet) ?? asString(item.preview) ?? asString(item.body_preview),
57904
+ isDraft: item.is_draft === true
57905
+ }
57906
+ ];
57897
57907
  });
57898
57908
  }
57909
+ function formatEmailDate(raw) {
57910
+ if (!raw) return void 0;
57911
+ const dateOnly = /^(\d{4})-(\d{2})-(\d{2})$/.exec(raw.trim());
57912
+ const date2 = dateOnly ? new Date(Number(dateOnly[1]), Number(dateOnly[2]) - 1, Number(dateOnly[3])) : new Date(raw);
57913
+ if (Number.isNaN(date2.getTime())) return raw;
57914
+ const now = /* @__PURE__ */ new Date();
57915
+ if (!dateOnly && date2.toDateString() === now.toDateString()) {
57916
+ return date2.toLocaleTimeString(void 0, {
57917
+ hour: "numeric",
57918
+ minute: "2-digit"
57919
+ });
57920
+ }
57921
+ const sameYear = date2.getFullYear() === now.getFullYear();
57922
+ return date2.toLocaleDateString(void 0, {
57923
+ month: "short",
57924
+ day: "numeric",
57925
+ ...sameYear ? {} : { year: "numeric" }
57926
+ });
57927
+ }
57928
+ const EMAIL_QUERY_OPERATOR = /(?:^|\s)(received|after|before|from|to|subject|has|in|is):(?:"([^"]+)"|(\S+))/gi;
57929
+ function humanEmailDate(raw) {
57930
+ return formatEmailDate(raw.replace(/\//g, "-")) ?? raw;
57931
+ }
57932
+ function humanizeEmailQuery(query) {
57933
+ if (!query.trim()) return "";
57934
+ const phrases = [];
57935
+ let freeText = query;
57936
+ for (const match2 of query.matchAll(EMAIL_QUERY_OPERATOR)) {
57937
+ const [token, op, quoted, bare] = match2;
57938
+ const value = quoted ?? bare ?? "";
57939
+ freeText = freeText.replace(token, " ");
57940
+ switch (op.toLowerCase()) {
57941
+ case "received": {
57942
+ const [start, end] = value.split("..");
57943
+ phrases.push(
57944
+ end ? `${humanEmailDate(start)} – ${humanEmailDate(end)}` : humanEmailDate(start)
57945
+ );
57946
+ break;
57947
+ }
57948
+ case "after":
57949
+ phrases.push(`after ${humanEmailDate(value)}`);
57950
+ break;
57951
+ case "before":
57952
+ phrases.push(`before ${humanEmailDate(value)}`);
57953
+ break;
57954
+ case "from":
57955
+ phrases.push(`from ${value}`);
57956
+ break;
57957
+ case "to":
57958
+ phrases.push(`to ${value}`);
57959
+ break;
57960
+ case "subject":
57961
+ phrases.push(`subject “${value}”`);
57962
+ break;
57963
+ case "has":
57964
+ phrases.push(value === "attachment" ? "with attachments" : `has ${value}`);
57965
+ break;
57966
+ case "in":
57967
+ phrases.push(value === "drafts" ? "drafts only" : `in ${value}`);
57968
+ break;
57969
+ case "is":
57970
+ phrases.push(value);
57971
+ break;
57972
+ }
57973
+ }
57974
+ const remainder = freeText.trim().replace(/\s+/g, " ");
57975
+ if (remainder) phrases.push(`“${remainder}”`);
57976
+ return phrases.join(" · ");
57977
+ }
57978
+ const INLINE_EMAIL_ROWS = 3;
57979
+ function EmailResultRow({ email: email2 }) {
57980
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-0.5 py-2 first:pt-0 last:pb-0", children: [
57981
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-baseline justify-between gap-2", children: [
57982
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex min-w-0 items-baseline gap-1.5 text-[12px] font-medium text-foreground", children: [
57983
+ email2.isDraft && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0 rounded bg-amber-100 px-1 py-px text-[9px] font-semibold uppercase tracking-wide text-amber-700", children: "Draft" }),
57984
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: email2.subject || "No subject" })
57985
+ ] }),
57986
+ email2.date && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0 text-[10px] text-muted-foreground", children: formatEmailDate(email2.date) })
57987
+ ] }),
57988
+ email2.from && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate text-[11px] text-muted-foreground", children: email2.from }),
57989
+ email2.snippet && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[11px] leading-relaxed text-muted-foreground/80", children: truncate(email2.snippet, 150) })
57990
+ ] });
57991
+ }
57899
57992
  const EmailSearchToolUIImpl = ({
57900
57993
  toolName,
57901
57994
  args,
@@ -57906,35 +57999,96 @@ const EmailSearchToolUIImpl = ({
57906
57999
  const query = (typedArgs == null ? void 0 : typedArgs.query) ?? (typedArgs == null ? void 0 : typedArgs.search_query) ?? "";
57907
58000
  const data = React.useMemo(() => normalizeResult(result), [result]);
57908
58001
  const emails = React.useMemo(() => data ? parseEmailResults(data) : [], [data]);
58002
+ const provider = typeof (data == null ? void 0 : data.provider) === "string" ? data.provider : void 0;
58003
+ const totalCount = typeof (data == null ? void 0 : data.count) === "number" ? data.count : emails.length;
57909
58004
  const isRunning = (status == null ? void 0 : status.type) === "running";
57910
58005
  const isComplete = (status == null ? void 0 : status.type) === "complete";
57911
58006
  const errorMsg = (status == null ? void 0 : status.type) === "incomplete" ? status.error : null;
58007
+ const readableQuery = React.useMemo(() => humanizeEmailQuery(query), [query]);
58008
+ const inlineEmails = emails.slice(0, INLINE_EMAIL_ROWS);
58009
+ const overflowEmails = emails.slice(INLINE_EMAIL_ROWS);
58010
+ const countLabel = emails.length > 0 && totalCount > emails.length ? `${emails.length} of ${totalCount}` : `${totalCount}`;
57912
58011
  return /* @__PURE__ */ jsxRuntime.jsx(
57913
58012
  ToolCard,
57914
58013
  {
57915
58014
  icon: Mail,
57916
58015
  status: (status == null ? void 0 : status.type) ?? "complete",
57917
- title: isRunning ? "Searching emails..." : "Email search",
57918
- subtitle: query ? truncate(query, 80) : void 0,
58016
+ title: isRunning ? "Searching email…" : "Email search",
58017
+ subtitle: readableQuery ? truncate(readableQuery, 90) : void 0,
57919
58018
  toolName,
57920
58019
  args: typedArgs,
57921
58020
  result,
57922
- badge: isComplete && emails.length > 0 ? `${emails.length} emails` : void 0,
58021
+ badge: isComplete ? totalCount > 0 ? `${countLabel} email${totalCount === 1 ? "" : "s"}${provider ? ` · ${provider}` : ""}` : "No matches" : void 0,
57923
58022
  error: errorMsg,
57924
- children: isComplete && emails.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(ExpandableSection, { label: "Show email results", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col divide-y divide-border/30", children: emails.map((email2, i) => /* @__PURE__ */ jsxRuntime.jsxs(
58023
+ children: isComplete && emails.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "border-t border-border/40 px-4 py-2.5", children: [
58024
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col divide-y divide-border/30", children: inlineEmails.map((email2, i) => /* @__PURE__ */ jsxRuntime.jsx(EmailResultRow, { email: email2 }, i)) }),
58025
+ overflowEmails.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
58026
+ ExpandableSection,
58027
+ {
58028
+ label: `Show ${overflowEmails.length} more`,
58029
+ children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col divide-y divide-border/30", children: overflowEmails.map((email2, i) => /* @__PURE__ */ jsxRuntime.jsx(EmailResultRow, { email: email2 }, i)) })
58030
+ }
58031
+ )
58032
+ ] })
58033
+ }
58034
+ );
58035
+ };
58036
+ const EmailSearchToolUI = React.memo(
58037
+ EmailSearchToolUIImpl
58038
+ );
58039
+ EmailSearchToolUI.displayName = "EmailSearchToolUI";
58040
+ function parseEmailAccounts(data) {
58041
+ const accounts = data.accounts ?? data.results;
58042
+ if (!Array.isArray(accounts)) return [];
58043
+ return accounts.slice(0, 10).flatMap((raw) => {
58044
+ const item = asRecord(raw);
58045
+ if (!item) return [];
58046
+ const defaultFlag = typeof item.is_default === "boolean" ? item.is_default : item.default;
58047
+ return [
58048
+ {
58049
+ address: asString(item.email_address) ?? asString(item.email) ?? asString(item.address),
58050
+ provider: asString(item.provider) ?? asString(item.provider_name),
58051
+ name: asString(item.account_name) ?? asString(item.name),
58052
+ isDefault: defaultFlag === true
58053
+ }
58054
+ ];
58055
+ });
58056
+ }
58057
+ const EmailAccountsToolUIImpl = ({
58058
+ toolName,
58059
+ args,
58060
+ result,
58061
+ status
58062
+ }) => {
58063
+ const data = React.useMemo(() => normalizeResult(result), [result]);
58064
+ const accounts = React.useMemo(
58065
+ () => data ? parseEmailAccounts(data) : [],
58066
+ [data]
58067
+ );
58068
+ const isRunning = (status == null ? void 0 : status.type) === "running";
58069
+ const isComplete = (status == null ? void 0 : status.type) === "complete";
58070
+ const errorMsg = (status == null ? void 0 : status.type) === "incomplete" ? status.error : null;
58071
+ return /* @__PURE__ */ jsxRuntime.jsx(
58072
+ ToolCard,
58073
+ {
58074
+ icon: Mail,
58075
+ status: (status == null ? void 0 : status.type) ?? "complete",
58076
+ title: isRunning ? "Checking connected email accounts…" : "Email accounts",
58077
+ toolName,
58078
+ args,
58079
+ result,
58080
+ badge: isComplete && accounts.length > 0 ? `${accounts.length} connected` : void 0,
58081
+ error: errorMsg,
58082
+ children: isComplete && accounts.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "border-t border-border/40 px-4 py-2.5", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col divide-y divide-border/30", children: accounts.map((account, i) => /* @__PURE__ */ jsxRuntime.jsxs(
57925
58083
  "div",
57926
58084
  {
57927
- className: cn("flex flex-col gap-0.5 py-2", i === 0 && "pt-0"),
58085
+ className: "flex items-center justify-between gap-2 py-1.5 first:pt-0 last:pb-0",
57928
58086
  children: [
57929
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-baseline justify-between gap-2", children: [
57930
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex items-baseline gap-1.5 text-[12px] font-medium text-foreground", children: [
57931
- email2.isDraft && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0 rounded bg-amber-100 px-1 py-px text-[9px] font-semibold uppercase tracking-wide text-amber-700", children: "Draft" }),
57932
- email2.subject || "No subject"
57933
- ] }),
57934
- email2.date && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0 text-[10px] text-muted-foreground", children: email2.date })
57935
- ] }),
57936
- email2.from && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[11px] text-muted-foreground", children: email2.from }),
57937
- email2.snippet && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[11px] leading-relaxed text-muted-foreground/80", children: truncate(email2.snippet, 150) })
58087
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 truncate text-[12px] text-foreground", children: account.address || account.name || "Unknown account" }),
58088
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex shrink-0 items-center gap-1.5", children: [
58089
+ account.isDefault && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "rounded-full bg-emerald-50 px-1.5 py-px text-[9px] font-semibold uppercase tracking-wide text-emerald-700", children: "Default" }),
58090
+ account.provider && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "rounded-md bg-muted/60 px-1.5 py-0.5 text-[10px] text-muted-foreground", children: account.provider })
58091
+ ] })
57938
58092
  ]
57939
58093
  },
57940
58094
  i
@@ -57942,10 +58096,10 @@ const EmailSearchToolUIImpl = ({
57942
58096
  }
57943
58097
  );
57944
58098
  };
57945
- const EmailSearchToolUI = React.memo(
57946
- EmailSearchToolUIImpl
58099
+ const EmailAccountsToolUI = React.memo(
58100
+ EmailAccountsToolUIImpl
57947
58101
  );
57948
- EmailSearchToolUI.displayName = "EmailSearchToolUI";
58102
+ EmailAccountsToolUI.displayName = "EmailAccountsToolUI";
57949
58103
  function extractAssetId(result) {
57950
58104
  const data = normalizeResult(result);
57951
58105
  if (!data) return null;
@@ -57982,6 +58136,9 @@ function asRecord(value) {
57982
58136
  }
57983
58137
  return value;
57984
58138
  }
58139
+ function asString(value) {
58140
+ return typeof value === "string" ? value : void 0;
58141
+ }
57985
58142
  const PRESENTATION_CODE_SLIDE_NUMBER_KEYS = [
57986
58143
  "slideNumber",
57987
58144
  "slide_number",
@@ -58078,22 +58235,51 @@ function CreateAssetToolUIImpl({
58078
58235
  args: typedArgs,
58079
58236
  result,
58080
58237
  error: errorMsg,
58081
- children: assetId && isComplete && !isCancelled && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "border-t border-border/40 px-4 py-2", children: /* @__PURE__ */ jsxRuntime.jsxs(
58238
+ children: assetId && isComplete && !isCancelled && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "border-t border-border/40 px-4 py-2.5", children: /* @__PURE__ */ jsxRuntime.jsxs(
58082
58239
  "button",
58083
58240
  {
58084
58241
  type: "button",
58085
58242
  onClick: handleOpen,
58086
- className: "flex items-center gap-1.5 rounded-md border border-border/60 px-3 py-1.5 text-xs font-medium text-muted-foreground transition-colors hover:bg-muted/50 hover:text-foreground",
58243
+ className: "group flex w-full items-center gap-3 rounded-lg border border-border/60 bg-muted/20 px-3 py-2.5 text-left transition-colors hover:border-border hover:bg-muted/40",
58087
58244
  children: [
58088
- /* @__PURE__ */ jsxRuntime.jsx(ExternalLink, { className: "size-3" }),
58089
- "Open ",
58090
- assetType === "unknown" ? "asset" : assetType
58245
+ /* @__PURE__ */ jsxRuntime.jsx(
58246
+ "div",
58247
+ {
58248
+ className: cn(
58249
+ "flex size-10 shrink-0 items-center justify-center rounded-lg",
58250
+ ASSET_TILE_STYLE[assetType] ?? ASSET_TILE_STYLE.unknown
58251
+ ),
58252
+ children: /* @__PURE__ */ jsxRuntime.jsx(Icon2, { className: "size-5" })
58253
+ }
58254
+ ),
58255
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 flex-1", children: [
58256
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "truncate text-[13px] font-medium text-foreground", children: createdName || name || "Untitled" }),
58257
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-[11px] text-muted-foreground", children: [
58258
+ ASSET_TILE_LABEL[assetType] ?? "Asset",
58259
+ " · Click to open"
58260
+ ] })
58261
+ ] }),
58262
+ /* @__PURE__ */ jsxRuntime.jsx(ExternalLink, { className: "size-3.5 shrink-0 text-muted-foreground opacity-0 transition-opacity group-hover:opacity-100" })
58091
58263
  ]
58092
58264
  }
58093
58265
  ) })
58094
58266
  }
58095
58267
  );
58096
58268
  }
58269
+ const ASSET_TILE_STYLE = {
58270
+ document: "bg-blue-500/10 text-blue-600",
58271
+ spreadsheet: "bg-emerald-500/10 text-emerald-600",
58272
+ presentation: "bg-orange-500/10 text-orange-600",
58273
+ notebook: "bg-violet-500/10 text-violet-600",
58274
+ unknown: "bg-muted text-muted-foreground"
58275
+ };
58276
+ const ASSET_TILE_LABEL = {
58277
+ document: "Document",
58278
+ spreadsheet: "Spreadsheet",
58279
+ presentation: "Presentation",
58280
+ notebook: "Notebook",
58281
+ unknown: "Asset"
58282
+ };
58097
58283
  const CreateDocumentToolUIImpl = (props) => /* @__PURE__ */ jsxRuntime.jsx(
58098
58284
  CreateAssetToolUIImpl,
58099
58285
  {
@@ -60516,6 +60702,8 @@ const TOOL_UI_REGISTRY = {
60516
60702
  open_browser: OpenBrowserToolUI,
60517
60703
  search_email: EmailSearchToolUI,
60518
60704
  unified_email_search: EmailSearchToolUI,
60705
+ unified_email_list_accounts: EmailAccountsToolUI,
60706
+ email_list_accounts: EmailAccountsToolUI,
60519
60707
  create_email_draft: CreateEmailDraftToolUI,
60520
60708
  unified_email_create_draft: CreateEmailDraftToolUI,
60521
60709
  create_new_document: CreateDocumentToolUI,
@@ -61274,7 +61462,7 @@ function useSendMessage() {
61274
61462
  shouldReplace || !currentText ? text2 : `${currentText}
61275
61463
  ${text2}`
61276
61464
  );
61277
- await aui.composer.send();
61465
+ await aui.composer.send({ steer: false });
61278
61466
  },
61279
61467
  [aui]
61280
61468
  );
@@ -61874,13 +62062,13 @@ const ComposerSendWithQuote = () => {
61874
62062
  });
61875
62063
  if (!fullMessage) return;
61876
62064
  aui.composer.setText(fullMessage);
61877
- aui.composer.send();
62065
+ aui.composer.send({ steer: false });
61878
62066
  clearQuote();
61879
62067
  clearAttachments();
61880
62068
  } else {
61881
62069
  if (!userText) return;
61882
62070
  aui.composer.setText(userText);
61883
- aui.composer.send();
62071
+ aui.composer.send({ steer: false });
61884
62072
  }
61885
62073
  editor == null ? void 0 : editor.clear();
61886
62074
  }, [aui, quote, attachments, hasExtras, isUploading, isThreadRunning, clearQuote, clearAttachments, editorRef, appUrl]);