@athenaintel/react 0.10.26 → 0.10.28

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
@@ -14112,14 +14112,20 @@ const useAssetPanelStore = create()(
14112
14112
  const existing = s.tabs.find((t) => t.id === assetId);
14113
14113
  if (existing) {
14114
14114
  const tabs = meta ? s.tabs.map(
14115
- (t) => t.id === assetId ? { ...t, name: meta.name ?? t.name, type: meta.type ?? t.type } : t
14115
+ (t) => t.id === assetId ? {
14116
+ ...t,
14117
+ name: meta.name ?? t.name,
14118
+ type: meta.type ?? t.type,
14119
+ slideNumber: "slideNumber" in meta ? meta.slideNumber : t.slideNumber
14120
+ } : t
14116
14121
  ) : s.tabs;
14117
14122
  return { isOpen: true, tabs, activeTabId: assetId };
14118
14123
  }
14119
14124
  const newTab = {
14120
14125
  id: assetId,
14121
14126
  name: (meta == null ? void 0 : meta.name) ?? null,
14122
- type: (meta == null ? void 0 : meta.type) ?? "unknown"
14127
+ type: (meta == null ? void 0 : meta.type) ?? "unknown",
14128
+ slideNumber: meta == null ? void 0 : meta.slideNumber
14123
14129
  };
14124
14130
  return { isOpen: true, tabs: [...s.tabs, newTab], activeTabId: assetId };
14125
14131
  }),
@@ -50001,18 +50007,6 @@ function AssetToolCard({
50001
50007
  const assetId = extractAssetId$1(result);
50002
50008
  const title = extractTitle(argsText, result);
50003
50009
  const assetType = toolMetaToAssetType(toolName);
50004
- const wasCompleteAtMount = useRef(isComplete);
50005
- useEffect(() => {
50006
- if (isComplete && !isCancelled && assetId && !wasCompleteAtMount.current) {
50007
- const store = useAssetPanelStore.getState();
50008
- if (store.markAutoOpened(assetId)) {
50009
- store.openAsset(assetId, {
50010
- name: title ?? void 0,
50011
- type: assetType
50012
- });
50013
- }
50014
- }
50015
- }, [isComplete, isCancelled, assetId, title, assetType]);
50016
50010
  const success = isComplete && isResultSuccess(result);
50017
50011
  return /* @__PURE__ */ jsxs("div", { className: cn(
50018
50012
  "aui-tool-fallback-root my-3 w-full rounded-xl border border-border/60 bg-background py-2.5 shadow-sm",
@@ -50574,6 +50568,32 @@ function normalizeResult(result) {
50574
50568
  function truncate(text2, max2) {
50575
50569
  return text2.length > max2 ? `${text2.slice(0, max2)}...` : text2;
50576
50570
  }
50571
+ function isSdkToolDebugEnabled() {
50572
+ var _a2, _b;
50573
+ try {
50574
+ const hostname = (_a2 = globalThis.location) == null ? void 0 : _a2.hostname;
50575
+ if (hostname === "localhost" || hostname === "127.0.0.1" || hostname === "::1") {
50576
+ return true;
50577
+ }
50578
+ return ((_b = globalThis.localStorage) == null ? void 0 : _b.getItem("athena:sdk:debug")) === "1";
50579
+ } catch {
50580
+ return false;
50581
+ }
50582
+ }
50583
+ function stringifyDebugPayload(payload) {
50584
+ return JSON.stringify(payload, (_key, value) => {
50585
+ if (typeof value === "string" && value.length > 800) {
50586
+ return `${value.slice(0, 800)}... [truncated ${value.length - 800} chars]`;
50587
+ }
50588
+ return value;
50589
+ });
50590
+ }
50591
+ function logDebugPayload(label, payload) {
50592
+ if (!isSdkToolDebugEnabled()) {
50593
+ return;
50594
+ }
50595
+ console.info(`${label} ${stringifyDebugPayload(payload)}`);
50596
+ }
50577
50597
  function formatToolName(name) {
50578
50598
  return name.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
50579
50599
  }
@@ -50864,6 +50884,83 @@ function extractAssetId(result) {
50864
50884
  function resetAssetAutoOpen() {
50865
50885
  useAssetPanelStore.getState().resetAutoOpen();
50866
50886
  }
50887
+ function getStringField(source, keys2) {
50888
+ if (!source) return null;
50889
+ for (const key of keys2) {
50890
+ const value = source[key];
50891
+ if (typeof value === "string" && value.trim()) {
50892
+ return value;
50893
+ }
50894
+ }
50895
+ return null;
50896
+ }
50897
+ function getNumberField(source, keys2) {
50898
+ if (!source) return null;
50899
+ for (const key of keys2) {
50900
+ const value = source[key];
50901
+ if (typeof value === "number" && Number.isFinite(value)) {
50902
+ return value;
50903
+ }
50904
+ }
50905
+ return null;
50906
+ }
50907
+ const PRESENTATION_CODE_SLIDE_NUMBER_KEYS = [
50908
+ "slideNumber",
50909
+ "slide_number",
50910
+ "targetSlideNumber",
50911
+ "target_slide_number",
50912
+ "targetSlide",
50913
+ "target_slide"
50914
+ ];
50915
+ const PRESENTATION_CODE_SLIDE_INDEX_KEYS = [
50916
+ "slideIndex",
50917
+ "slide_index",
50918
+ "targetSlideIndex",
50919
+ "target_slide_index"
50920
+ ];
50921
+ function getStudioAssetId({
50922
+ args,
50923
+ result
50924
+ }) {
50925
+ const id = getStringField(result, ["asset_id", "assetId", "id"]) ?? getStringField(args, ["asset_id", "assetId", "id"]);
50926
+ return (id == null ? void 0 : id.startsWith("asset_")) ? id : null;
50927
+ }
50928
+ function getPresentationCodeDeckId(code2) {
50929
+ const match2 = code2.match(/\bdeck_id\s*=\s*["']([^"']+)["']/);
50930
+ const deckId = match2 == null ? void 0 : match2[1];
50931
+ return (deckId == null ? void 0 : deckId.startsWith("asset_")) ? deckId : null;
50932
+ }
50933
+ function getPresentationCodeSlideNumberFromFields(source) {
50934
+ const explicit = getNumberField(source, PRESENTATION_CODE_SLIDE_NUMBER_KEYS);
50935
+ if (explicit != null && Number.isInteger(explicit) && explicit >= 1) {
50936
+ return explicit;
50937
+ }
50938
+ const zeroBasedIndex = getNumberField(source, PRESENTATION_CODE_SLIDE_INDEX_KEYS);
50939
+ if (zeroBasedIndex != null && Number.isInteger(zeroBasedIndex) && zeroBasedIndex >= 0) {
50940
+ return zeroBasedIndex + 1;
50941
+ }
50942
+ return void 0;
50943
+ }
50944
+ function getPresentationCodeSlideNumberFromText(text2) {
50945
+ const match2 = text2 == null ? void 0 : text2.match(/\bslide\s*(?:#|number\s*)?(\d+)\b/i);
50946
+ if (!match2) {
50947
+ return void 0;
50948
+ }
50949
+ const slideNumber = Number.parseInt(match2[1] ?? "", 10);
50950
+ return Number.isInteger(slideNumber) && slideNumber >= 1 ? slideNumber : void 0;
50951
+ }
50952
+ function getPresentationCodeSlideNumber({
50953
+ args,
50954
+ result
50955
+ }) {
50956
+ return getPresentationCodeSlideNumberFromFields(args) ?? getPresentationCodeSlideNumberFromFields(result) ?? getPresentationCodeSlideNumberFromText(
50957
+ getStringField(args, ["summary", "title", "description"])
50958
+ );
50959
+ }
50960
+ function getAddSlideNumber(args) {
50961
+ const index2 = getNumberField(args, ["index"]);
50962
+ return index2 != null && Number.isInteger(index2) && index2 >= 0 ? index2 + 1 : void 0;
50963
+ }
50867
50964
  function CreateAssetToolUIImpl({
50868
50965
  icon: Icon2,
50869
50966
  assetType,
@@ -50884,18 +50981,6 @@ function CreateAssetToolUIImpl({
50884
50981
  const isCancelled = (status == null ? void 0 : status.type) === "incomplete" && status.reason === "cancelled";
50885
50982
  const errorMsg = (status == null ? void 0 : status.type) === "incomplete" ? status.error : null;
50886
50983
  const openAsset = useAssetPanelStore((s) => s.openAsset);
50887
- const wasCompleteAtMount = useRef(isComplete);
50888
- useEffect(() => {
50889
- if (isComplete && !isCancelled && assetId && !wasCompleteAtMount.current) {
50890
- const store = useAssetPanelStore.getState();
50891
- if (store.markAutoOpened(assetId)) {
50892
- store.openAsset(assetId, {
50893
- name: createdName || name || void 0,
50894
- type: assetType
50895
- });
50896
- }
50897
- }
50898
- }, [isComplete, isCancelled, assetId, createdName, name, assetType]);
50899
50984
  const handleOpen = () => {
50900
50985
  if (assetId) {
50901
50986
  openAsset(assetId, {
@@ -50973,6 +51058,258 @@ const CreatePresentationToolUI = memo(
50973
51058
  CreatePresentationToolUIImpl
50974
51059
  );
50975
51060
  CreatePresentationToolUI.displayName = "CreatePresentationToolUI";
51061
+ function openStudioAsset({
51062
+ assetId,
51063
+ assetType,
51064
+ slideNumber,
51065
+ preserveExistingSlide
51066
+ }) {
51067
+ const store = useAssetPanelStore.getState();
51068
+ const existing = store.tabs.find((tab) => tab.id === assetId);
51069
+ const shouldKeepCurrentSlide = preserveExistingSlide && existing;
51070
+ store.openAsset(assetId, {
51071
+ type: assetType,
51072
+ ...!shouldKeepCurrentSlide && slideNumber !== void 0 ? { slideNumber } : {}
51073
+ });
51074
+ }
51075
+ function StudioPtcToolUI({
51076
+ config: config2,
51077
+ toolName,
51078
+ args,
51079
+ result,
51080
+ status
51081
+ }) {
51082
+ var _a2, _b;
51083
+ const typedArgs = args;
51084
+ const data = useMemo(() => normalizeResult(result), [result]);
51085
+ const slideNumber = (_a2 = config2.getSlideNumber) == null ? void 0 : _a2.call(config2, typedArgs);
51086
+ const assetId = getStudioAssetId({ args: typedArgs, result: data });
51087
+ const subtitle = (_b = config2.getSubtitle) == null ? void 0 : _b.call(config2, typedArgs, data, slideNumber);
51088
+ const isRunning = (status == null ? void 0 : status.type) === "running";
51089
+ const isComplete = (status == null ? void 0 : status.type) === "complete" || !status;
51090
+ const isCancelled = (status == null ? void 0 : status.type) === "incomplete" && status.reason === "cancelled";
51091
+ const errorMsg = (status == null ? void 0 : status.type) === "incomplete" ? String(status.error ?? status.reason ?? "Tool failed") : null;
51092
+ const handleOpen = useCallback(() => {
51093
+ if (!assetId) return;
51094
+ if (toolName === "AddSlide") {
51095
+ logDebugPayload("[Athena SDK AddSlide] manual open", {
51096
+ args: typedArgs,
51097
+ parsedResult: data,
51098
+ rawResult: result,
51099
+ assetId,
51100
+ slideNumber
51101
+ });
51102
+ }
51103
+ openStudioAsset({
51104
+ assetId,
51105
+ assetType: config2.assetType,
51106
+ slideNumber,
51107
+ preserveExistingSlide: config2.preserveExistingSlide
51108
+ });
51109
+ }, [
51110
+ assetId,
51111
+ config2.assetType,
51112
+ config2.preserveExistingSlide,
51113
+ data,
51114
+ result,
51115
+ slideNumber,
51116
+ toolName,
51117
+ typedArgs
51118
+ ]);
51119
+ useEffect(() => {
51120
+ if (toolName !== "AddSlide") {
51121
+ return;
51122
+ }
51123
+ logDebugPayload("[Athena SDK AddSlide] tool state", {
51124
+ args: typedArgs,
51125
+ parsedResult: data,
51126
+ rawResult: result,
51127
+ status,
51128
+ assetId,
51129
+ slideNumber,
51130
+ isComplete,
51131
+ isCancelled
51132
+ });
51133
+ }, [
51134
+ assetId,
51135
+ data,
51136
+ isCancelled,
51137
+ isComplete,
51138
+ result,
51139
+ slideNumber,
51140
+ status,
51141
+ toolName,
51142
+ typedArgs
51143
+ ]);
51144
+ return /* @__PURE__ */ jsx(
51145
+ ToolCard,
51146
+ {
51147
+ icon: config2.icon,
51148
+ status: (status == null ? void 0 : status.type) ?? "complete",
51149
+ title: isRunning ? config2.runningLabel(typedArgs) : config2.doneLabel(typedArgs, slideNumber),
51150
+ subtitle,
51151
+ badge: config2.badge,
51152
+ toolName,
51153
+ args: typedArgs,
51154
+ result,
51155
+ error: errorMsg,
51156
+ children: assetId && isComplete && !isCancelled && /* @__PURE__ */ jsx("div", { className: "border-t border-border/40 px-4 py-2", children: /* @__PURE__ */ jsxs(
51157
+ "button",
51158
+ {
51159
+ type: "button",
51160
+ onClick: handleOpen,
51161
+ 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",
51162
+ children: [
51163
+ /* @__PURE__ */ jsx(ExternalLink, { className: "size-3" }),
51164
+ config2.openLabel(slideNumber)
51165
+ ]
51166
+ }
51167
+ ) })
51168
+ }
51169
+ );
51170
+ }
51171
+ function createStudioPtcToolUI(displayName, config2) {
51172
+ const Component = (props) => /* @__PURE__ */ jsx(StudioPtcToolUI, { config: config2, ...props });
51173
+ const Memoized = memo(Component);
51174
+ Memoized.displayName = displayName;
51175
+ return Memoized;
51176
+ }
51177
+ const getNameSubtitle = (args, result) => getStringField(result, ["title", "name", "asset_name"]) ?? getStringField(args, ["title", "name"]) ?? void 0;
51178
+ const getParagraphSubtitle = (args) => {
51179
+ const text2 = getStringField(args, ["text"]);
51180
+ return text2 ? truncate(text2, 80) : void 0;
51181
+ };
51182
+ const getSlideSubtitle = (args, _result, slideNumber) => {
51183
+ const layout = getStringField(args, ["layoutName", "layout_name"]) ?? getStringField(args, ["layoutPath", "layout_path"]);
51184
+ const slideLabel = slideNumber ? `Slide ${slideNumber}` : null;
51185
+ if (slideLabel && layout) return `${slideLabel} - ${truncate(layout, 48)}`;
51186
+ return slideLabel ?? (layout ? truncate(layout, 64) : void 0);
51187
+ };
51188
+ const StudioCreateWorkbookToolUI = createStudioPtcToolUI(
51189
+ "StudioCreateWorkbookToolUI",
51190
+ {
51191
+ icon: ChartColumn,
51192
+ assetType: "spreadsheet",
51193
+ badge: "Sheet",
51194
+ runningLabel: (args) => {
51195
+ const title = getStringField(args, ["title", "name"]);
51196
+ return title ? `Creating workbook "${title}"...` : "Creating workbook...";
51197
+ },
51198
+ doneLabel: () => "Workbook created",
51199
+ getSubtitle: getNameSubtitle,
51200
+ openLabel: () => "Open workbook"
51201
+ }
51202
+ );
51203
+ const StudioAddSheetToolUI = createStudioPtcToolUI(
51204
+ "StudioAddSheetToolUI",
51205
+ {
51206
+ icon: ChartColumn,
51207
+ assetType: "spreadsheet",
51208
+ badge: "Sheet",
51209
+ runningLabel: (args) => {
51210
+ const name = getStringField(args, ["name", "sheetName", "sheet_name"]);
51211
+ return name ? `Adding sheet "${name}"...` : "Adding sheet...";
51212
+ },
51213
+ doneLabel: (args) => {
51214
+ const name = getStringField(args, ["name", "sheetName", "sheet_name"]);
51215
+ return name ? `Sheet added: ${name}` : "Sheet added";
51216
+ },
51217
+ getSubtitle: (args) => getStringField(args, ["name", "sheetName", "sheet_name"]) ?? void 0,
51218
+ openLabel: () => "Open workbook"
51219
+ }
51220
+ );
51221
+ const StudioOpenWorkbookToolUI = createStudioPtcToolUI(
51222
+ "StudioOpenWorkbookToolUI",
51223
+ {
51224
+ icon: ChartColumn,
51225
+ assetType: "spreadsheet",
51226
+ badge: "Sheet",
51227
+ runningLabel: () => "Opening workbook...",
51228
+ doneLabel: () => "Workbook opened",
51229
+ openLabel: () => "Open workbook"
51230
+ }
51231
+ );
51232
+ const StudioCreatePresentationToolUI = createStudioPtcToolUI(
51233
+ "StudioCreatePresentationToolUI",
51234
+ {
51235
+ icon: Presentation,
51236
+ assetType: "presentation",
51237
+ badge: "Deck",
51238
+ runningLabel: (args) => {
51239
+ const title = getStringField(args, ["title", "name"]);
51240
+ return title ? `Creating presentation "${title}"...` : "Creating presentation...";
51241
+ },
51242
+ doneLabel: () => "Presentation created",
51243
+ getSubtitle: getNameSubtitle,
51244
+ getSlideNumber: () => 1,
51245
+ openLabel: () => "Open presentation"
51246
+ }
51247
+ );
51248
+ const StudioOpenPresentationToolUI = createStudioPtcToolUI(
51249
+ "StudioOpenPresentationToolUI",
51250
+ {
51251
+ icon: Presentation,
51252
+ assetType: "presentation",
51253
+ badge: "Deck",
51254
+ runningLabel: () => "Opening presentation...",
51255
+ doneLabel: () => "Presentation opened",
51256
+ getSlideNumber: () => 1,
51257
+ getSubtitle: (_args, _result, slideNumber) => slideNumber ? `Slide ${slideNumber}` : void 0,
51258
+ openLabel: () => "Open presentation",
51259
+ preserveExistingSlide: true
51260
+ }
51261
+ );
51262
+ const StudioAddSlideToolUI = createStudioPtcToolUI(
51263
+ "StudioAddSlideToolUI",
51264
+ {
51265
+ icon: Presentation,
51266
+ assetType: "presentation",
51267
+ badge: "Deck",
51268
+ runningLabel: () => "Adding slide...",
51269
+ doneLabel: (_args, slideNumber) => slideNumber ? `Slide ${slideNumber} added` : "Slide added",
51270
+ getSubtitle: getSlideSubtitle,
51271
+ getSlideNumber: (args) => getAddSlideNumber(args) ?? 1,
51272
+ openLabel: (slideNumber) => slideNumber ? `Open slide ${slideNumber}` : "Open presentation"
51273
+ }
51274
+ );
51275
+ const StudioCreateDocumentToolUI = createStudioPtcToolUI(
51276
+ "StudioCreateDocumentToolUI",
51277
+ {
51278
+ icon: FileText,
51279
+ assetType: "document",
51280
+ badge: "Doc",
51281
+ runningLabel: (args) => {
51282
+ const title = getStringField(args, ["title", "name"]);
51283
+ return title ? `Creating document "${title}"...` : "Creating document...";
51284
+ },
51285
+ doneLabel: () => "Document created",
51286
+ getSubtitle: getNameSubtitle,
51287
+ openLabel: () => "Open document"
51288
+ }
51289
+ );
51290
+ const StudioCreateParagraphToolUI = createStudioPtcToolUI(
51291
+ "StudioCreateParagraphToolUI",
51292
+ {
51293
+ icon: FileText,
51294
+ assetType: "document",
51295
+ badge: "Doc",
51296
+ runningLabel: () => "Adding paragraph...",
51297
+ doneLabel: () => "Paragraph added",
51298
+ getSubtitle: getParagraphSubtitle,
51299
+ openLabel: () => "Open document"
51300
+ }
51301
+ );
51302
+ const StudioOpenDocumentToolUI = createStudioPtcToolUI(
51303
+ "StudioOpenDocumentToolUI",
51304
+ {
51305
+ icon: FileText,
51306
+ assetType: "document",
51307
+ badge: "Doc",
51308
+ runningLabel: () => "Opening document...",
51309
+ doneLabel: () => "Document opened",
51310
+ openLabel: () => "Open document"
51311
+ }
51312
+ );
50976
51313
  const CreateEmailDraftToolUIImpl = ({
50977
51314
  toolName,
50978
51315
  args,
@@ -51478,7 +51815,7 @@ const ExecutePresentationCodeToolUIImpl = ({
51478
51815
  const typedArgs = args;
51479
51816
  const code2 = (typedArgs == null ? void 0 : typedArgs.code) ?? "";
51480
51817
  const summary = (typedArgs == null ? void 0 : typedArgs.summary) ?? null;
51481
- const deckIdFromArgs = typeof (typedArgs == null ? void 0 : typedArgs.deck_id) === "string" ? typedArgs.deck_id : null;
51818
+ const deckIdFromArgs = getStringField(typedArgs, ["deck_id", "deckId", "asset_id", "assetId"]) ?? getPresentationCodeDeckId(code2);
51482
51819
  const isRunning = (status == null ? void 0 : status.type) === "running";
51483
51820
  const isComplete = (status == null ? void 0 : status.type) === "complete";
51484
51821
  const errorMsg = (status == null ? void 0 : status.type) === "incomplete" ? status.error : null;
@@ -51486,21 +51823,55 @@ const ExecutePresentationCodeToolUIImpl = ({
51486
51823
  () => isComplete ? parsePythonResult(result) : null,
51487
51824
  [result, isComplete]
51488
51825
  );
51826
+ const resultData = useMemo(() => normalizeResult(result), [result]);
51489
51827
  const outputText = parsed ? getExecutionTextOutput(parsed) : null;
51490
- const assetId = (parsed == null ? void 0 : parsed.assetId) ?? null;
51828
+ const targetSlideNumber = getPresentationCodeSlideNumber({
51829
+ args: typedArgs,
51830
+ result: resultData
51831
+ });
51491
51832
  const deckId = (parsed == null ? void 0 : parsed.deckId) ?? deckIdFromArgs;
51833
+ const assetId = (parsed == null ? void 0 : parsed.assetId) ?? ((deckId == null ? void 0 : deckId.startsWith("asset_")) ? deckId : null);
51492
51834
  const hasError = parsed && (parsed.error || parsed.exception);
51493
51835
  const openAsset = useAssetPanelStore((s) => s.openAsset);
51494
- const wasCompleteAtMount = useRef(isComplete);
51495
51836
  useEffect(() => {
51496
- if (!isComplete || !assetId || wasCompleteAtMount.current) {
51837
+ if (!isComplete) {
51497
51838
  return;
51498
51839
  }
51499
- const store = useAssetPanelStore.getState();
51500
- if (store.markAutoOpened(assetId)) {
51501
- store.openAsset(assetId, { type: "presentation" });
51840
+ logDebugPayload("[Athena SDK execute_presentation_code] tool state", {
51841
+ args: typedArgs,
51842
+ parsedResult: resultData,
51843
+ status,
51844
+ assetId,
51845
+ deckId,
51846
+ targetSlideNumber,
51847
+ outputText
51848
+ });
51849
+ }, [
51850
+ assetId,
51851
+ deckId,
51852
+ isComplete,
51853
+ outputText,
51854
+ resultData,
51855
+ status,
51856
+ targetSlideNumber,
51857
+ typedArgs
51858
+ ]);
51859
+ const openPresentation = useCallback(() => {
51860
+ if (!assetId) {
51861
+ return;
51502
51862
  }
51503
- }, [assetId, isComplete]);
51863
+ logDebugPayload("[Athena SDK execute_presentation_code] open deck", {
51864
+ args: typedArgs,
51865
+ parsedResult: resultData,
51866
+ assetId,
51867
+ deckId,
51868
+ targetSlideNumber
51869
+ });
51870
+ openAsset(assetId, {
51871
+ type: "presentation",
51872
+ ...targetSlideNumber !== void 0 ? { slideNumber: targetSlideNumber } : {}
51873
+ });
51874
+ }, [assetId, deckId, openAsset, resultData, targetSlideNumber, typedArgs]);
51504
51875
  return /* @__PURE__ */ jsxs(
51505
51876
  ToolCard,
51506
51877
  {
@@ -51523,11 +51894,11 @@ const ExecutePresentationCodeToolUIImpl = ({
51523
51894
  "button",
51524
51895
  {
51525
51896
  type: "button",
51526
- onClick: () => openAsset(assetId, { type: "presentation" }),
51897
+ onClick: openPresentation,
51527
51898
  className: "flex shrink-0 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",
51528
51899
  children: [
51529
51900
  /* @__PURE__ */ jsx(ExternalLink, { className: "size-3" }),
51530
- "Open deck"
51901
+ targetSlideNumber ? `Open slide ${targetSlideNumber}` : "Open deck"
51531
51902
  ]
51532
51903
  }
51533
51904
  )
@@ -51586,15 +51957,6 @@ const OpenAssetToolUIImpl = ({
51586
51957
  const isCancelled = (status == null ? void 0 : status.type) === "incomplete" && status.reason === "cancelled";
51587
51958
  const errorMsg = (status == null ? void 0 : status.type) === "incomplete" ? status.error : null;
51588
51959
  const openAsset = useAssetPanelStore((s) => s.openAsset);
51589
- const wasCompleteAtMount = useRef(isComplete);
51590
- useEffect(() => {
51591
- if (isComplete && !isCancelled && assetId && !wasCompleteAtMount.current) {
51592
- const store = useAssetPanelStore.getState();
51593
- if (store.markAutoOpened(assetId)) {
51594
- store.openAsset(assetId);
51595
- }
51596
- }
51597
- }, [isComplete, isCancelled, assetId]);
51598
51960
  return /* @__PURE__ */ jsx(
51599
51961
  ToolCard,
51600
51962
  {
@@ -52333,18 +52695,6 @@ const CaptureMomentToolUIImpl = ({
52333
52695
  const isComplete = (status == null ? void 0 : status.type) === "complete" || !status;
52334
52696
  const isCancelled = (status == null ? void 0 : status.type) === "incomplete" && status.reason === "cancelled";
52335
52697
  const openAsset = useAssetPanelStore((s) => s.openAsset);
52336
- const wasCompleteAtMount = useRef(isComplete);
52337
- useEffect(() => {
52338
- if (isComplete && !isCancelled && createdAssetId && !wasCompleteAtMount.current) {
52339
- const store = useAssetPanelStore.getState();
52340
- if (store.markAutoOpened(createdAssetId)) {
52341
- store.openAsset(createdAssetId, {
52342
- name: void 0,
52343
- type: "unknown"
52344
- });
52345
- }
52346
- }
52347
- }, [isComplete, isCancelled, createdAssetId, resultType]);
52348
52698
  const handleOpen = () => {
52349
52699
  if (createdAssetId) {
52350
52700
  openAsset(createdAssetId, {
@@ -52404,6 +52754,153 @@ const CaptureMomentToolUI = memo(
52404
52754
  CaptureMomentToolUIImpl
52405
52755
  );
52406
52756
  CaptureMomentToolUI.displayName = "CaptureMomentToolUI";
52757
+ function normalizeContentResult(result) {
52758
+ if (Array.isArray(result)) {
52759
+ return result;
52760
+ }
52761
+ if (typeof result === "string") {
52762
+ try {
52763
+ const parsed = JSON.parse(result);
52764
+ return normalizeContentResult(parsed);
52765
+ } catch {
52766
+ return [];
52767
+ }
52768
+ }
52769
+ if (result && typeof result === "object") {
52770
+ const obj = result;
52771
+ if (Array.isArray(obj.result)) {
52772
+ return obj.result;
52773
+ }
52774
+ if (typeof obj.result === "string") {
52775
+ return normalizeContentResult(obj.result);
52776
+ }
52777
+ if (Array.isArray(obj.content)) {
52778
+ return obj.content;
52779
+ }
52780
+ }
52781
+ return [];
52782
+ }
52783
+ function getSlideScreenshotResult(result) {
52784
+ let text2 = null;
52785
+ let imageUrl = null;
52786
+ let mediaType = null;
52787
+ let s3Key = null;
52788
+ for (const item of normalizeContentResult(result)) {
52789
+ if (!item || typeof item !== "object") {
52790
+ continue;
52791
+ }
52792
+ const part = item;
52793
+ if (!text2 && part.type === "text" && typeof part.text === "string") {
52794
+ text2 = part.text;
52795
+ continue;
52796
+ }
52797
+ if (part.type !== "image") {
52798
+ continue;
52799
+ }
52800
+ const source = part.source && typeof part.source === "object" ? part.source : {};
52801
+ if (!imageUrl) {
52802
+ imageUrl = getStringField(source, ["presigned_url", "url"]) ?? getStringField(part, ["url", "image_url"]);
52803
+ }
52804
+ if (!mediaType) {
52805
+ mediaType = getStringField(source, ["media_type", "mime_type"]) ?? getStringField(part, ["media_type", "mime_type"]);
52806
+ }
52807
+ if (!s3Key) {
52808
+ s3Key = getStringField(source, ["data", "s3_key"]);
52809
+ }
52810
+ }
52811
+ return { text: text2, imageUrl, mediaType, s3Key };
52812
+ }
52813
+ const CaptureSlideScreenshotToolUIImpl = ({
52814
+ toolName,
52815
+ args,
52816
+ result,
52817
+ status
52818
+ }) => {
52819
+ const typedArgs = args;
52820
+ const assetId = getStringField(typedArgs, ["asset_id", "assetId"]);
52821
+ const slideNumber = getNumberField(typedArgs, ["slide_number", "slideNumber"]) ?? void 0;
52822
+ const { text: text2, imageUrl, mediaType } = useMemo(
52823
+ () => getSlideScreenshotResult(result),
52824
+ [result]
52825
+ );
52826
+ const isRunning = (status == null ? void 0 : status.type) === "running";
52827
+ const isComplete = (status == null ? void 0 : status.type) === "complete" || !status;
52828
+ const isCancelled = (status == null ? void 0 : status.type) === "incomplete" && status.reason === "cancelled";
52829
+ const errorMsg = (status == null ? void 0 : status.type) === "incomplete" ? String(status.error ?? status.reason ?? "Capture failed") : null;
52830
+ const openAsset = useAssetPanelStore((s) => s.openAsset);
52831
+ const handleOpenSlide = useCallback(() => {
52832
+ if (!assetId) {
52833
+ return;
52834
+ }
52835
+ openAsset(assetId, {
52836
+ type: "presentation",
52837
+ ...slideNumber ? { slideNumber } : {}
52838
+ });
52839
+ }, [assetId, openAsset, slideNumber]);
52840
+ const subtitle = [
52841
+ slideNumber ? `Slide ${slideNumber}` : null,
52842
+ assetId ? truncate(assetId, 34) : null
52843
+ ].filter((value) => Boolean(value)).join(" · ");
52844
+ return /* @__PURE__ */ jsxs(
52845
+ ToolCard,
52846
+ {
52847
+ icon: Camera,
52848
+ status: (status == null ? void 0 : status.type) ?? "complete",
52849
+ title: isRunning ? "Capturing slide screenshot..." : errorMsg ? "Slide screenshot failed" : "Slide screenshot captured",
52850
+ subtitle: subtitle || void 0,
52851
+ badge: slideNumber ? `Slide ${slideNumber}` : mediaType ?? void 0,
52852
+ toolName,
52853
+ args: typedArgs,
52854
+ result,
52855
+ error: errorMsg,
52856
+ children: [
52857
+ isComplete && !isCancelled && imageUrl && /* @__PURE__ */ jsxs("div", { className: "border-t border-border/40 p-3", children: [
52858
+ /* @__PURE__ */ jsx("div", { className: "overflow-hidden rounded-lg border border-border/50 bg-muted/20", children: /* @__PURE__ */ jsx(
52859
+ "img",
52860
+ {
52861
+ src: imageUrl,
52862
+ alt: slideNumber ? `Slide ${slideNumber} screenshot` : "Slide screenshot",
52863
+ className: "aspect-video w-full object-contain",
52864
+ loading: "lazy"
52865
+ }
52866
+ ) }),
52867
+ text2 && /* @__PURE__ */ jsx("p", { className: "mt-2 text-[11px] leading-relaxed text-muted-foreground", children: truncate(text2, 160) })
52868
+ ] }),
52869
+ isComplete && !isCancelled && assetId && /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-2 border-t border-border/40 px-4 py-2", children: [
52870
+ /* @__PURE__ */ jsxs(
52871
+ "button",
52872
+ {
52873
+ type: "button",
52874
+ onClick: handleOpenSlide,
52875
+ 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",
52876
+ children: [
52877
+ /* @__PURE__ */ jsx(ExternalLink, { className: "size-3" }),
52878
+ slideNumber ? `Open slide ${slideNumber}` : "Open presentation"
52879
+ ]
52880
+ }
52881
+ ),
52882
+ imageUrl && /* @__PURE__ */ jsxs(
52883
+ "a",
52884
+ {
52885
+ href: imageUrl,
52886
+ target: "_blank",
52887
+ rel: "noreferrer",
52888
+ 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",
52889
+ children: [
52890
+ /* @__PURE__ */ jsx(Image, { className: "size-3" }),
52891
+ "Open image"
52892
+ ]
52893
+ }
52894
+ )
52895
+ ] })
52896
+ ]
52897
+ }
52898
+ );
52899
+ };
52900
+ const CaptureSlideScreenshotToolUI = memo(
52901
+ CaptureSlideScreenshotToolUIImpl
52902
+ );
52903
+ CaptureSlideScreenshotToolUI.displayName = "CaptureSlideScreenshotToolUI";
52407
52904
  const TOOL_UI_REGISTRY = {
52408
52905
  search: WebSearchToolUI,
52409
52906
  browse: BrowseToolUI,
@@ -52416,11 +52913,22 @@ const TOOL_UI_REGISTRY = {
52416
52913
  create_new_sheet: CreateSheetToolUI,
52417
52914
  create_powerpoint_deck: CreatePresentationToolUI,
52418
52915
  create_new_notebook: CreateNotebookToolUI,
52916
+ // Studio SDK nested PTC tools
52917
+ CreateWorkbook: StudioCreateWorkbookToolUI,
52918
+ AddSheet: StudioAddSheetToolUI,
52919
+ OpenWorkbook: StudioOpenWorkbookToolUI,
52920
+ CreatePresentation: StudioCreatePresentationToolUI,
52921
+ OpenPresentation: StudioOpenPresentationToolUI,
52922
+ AddSlide: StudioAddSlideToolUI,
52923
+ CreateDocument: StudioCreateDocumentToolUI,
52924
+ OpenDocument: StudioOpenDocumentToolUI,
52925
+ CreateParagraph: StudioCreateParagraphToolUI,
52419
52926
  execute_presentation_code: ExecutePresentationCodeToolUI,
52420
52927
  run_python_code: RunPythonCodeToolUI,
52421
52928
  open_asset_in_workspace: OpenAssetToolUI,
52422
52929
  // Media capture
52423
52930
  capture_moment: CaptureMomentToolUI,
52931
+ capture_slide_screenshot: CaptureSlideScreenshotToolUI,
52424
52932
  // Spreadsheet toolkit
52425
52933
  update_sheet_range: UpdateSheetRangeToolUI,
52426
52934
  // Database toolkit
@@ -53530,10 +54038,37 @@ const AthenaUserMessage = ({
53530
54038
  }
53531
54039
  );
53532
54040
  const embedCache = /* @__PURE__ */ new Map();
54041
+ function resolveAssetEmbedUrl({
54042
+ embedUrl,
54043
+ appUrl
54044
+ }) {
54045
+ if (!appUrl) {
54046
+ return embedUrl;
54047
+ }
54048
+ try {
54049
+ const resolvedEmbedUrl = new URL(embedUrl);
54050
+ const resolvedAppUrl = new URL(appUrl);
54051
+ if (!resolvedEmbedUrl.pathname.startsWith("/embed/")) {
54052
+ return embedUrl;
54053
+ }
54054
+ resolvedEmbedUrl.protocol = resolvedAppUrl.protocol;
54055
+ resolvedEmbedUrl.host = resolvedAppUrl.host;
54056
+ return resolvedEmbedUrl.toString();
54057
+ } catch {
54058
+ return embedUrl;
54059
+ }
54060
+ }
53533
54061
  function useAssetEmbed(assetId, options = {
53534
54062
  backendUrl: ""
53535
54063
  }) {
53536
- const { readOnly = false, expiresInSeconds = 60 * 60 * 24 * 30, backendUrl, apiKey, token } = options;
54064
+ const {
54065
+ readOnly = false,
54066
+ expiresInSeconds = 60 * 60 * 24 * 30,
54067
+ backendUrl,
54068
+ appUrl,
54069
+ apiKey,
54070
+ token
54071
+ } = options;
53537
54072
  const [embedUrl, setEmbedUrl] = useState(null);
53538
54073
  const [isLoading, setIsLoading] = useState(false);
53539
54074
  const [error2, setError] = useState(null);
@@ -53550,7 +54085,7 @@ function useAssetEmbed(assetId, options = {
53550
54085
  return;
53551
54086
  }
53552
54087
  const authContext = hasToken ? "token" : apiKey ?? "anon";
53553
- const cacheKey = `${assetId}:${readOnly}:${authContext}`;
54088
+ const cacheKey = `${assetId}:${readOnly}:${authContext}:${appUrl ?? ""}`;
53554
54089
  const cached2 = embedCache.get(cacheKey);
53555
54090
  if (cached2 && cached2.expiresAt > Date.now() / 1e3) {
53556
54091
  setEmbedUrl(cached2.url);
@@ -53588,8 +54123,12 @@ function useAssetEmbed(assetId, options = {
53588
54123
  }
53589
54124
  return res.json();
53590
54125
  }).then((data) => {
53591
- embedCache.set(cacheKey, { url: data.embed_url, expiresAt: data.expires_at });
53592
- setEmbedUrl(data.embed_url);
54126
+ const resolvedEmbedUrl = resolveAssetEmbedUrl({
54127
+ embedUrl: data.embed_url,
54128
+ appUrl
54129
+ });
54130
+ embedCache.set(cacheKey, { url: resolvedEmbedUrl, expiresAt: data.expires_at });
54131
+ setEmbedUrl(resolvedEmbedUrl);
53593
54132
  setIsLoading(false);
53594
54133
  }).catch((err) => {
53595
54134
  if (err.name === "AbortError") return;
@@ -53597,9 +54136,44 @@ function useAssetEmbed(assetId, options = {
53597
54136
  setIsLoading(false);
53598
54137
  });
53599
54138
  return () => controller.abort();
53600
- }, [assetId, readOnly, expiresInSeconds, backendUrl, apiKey, hasToken]);
54139
+ }, [assetId, readOnly, expiresInSeconds, backendUrl, appUrl, apiKey, hasToken]);
53601
54140
  return { embedUrl, isLoading, error: error2 };
53602
54141
  }
54142
+ const ABSOLUTE_URL_PATTERN = /^[a-zA-Z][a-zA-Z\d+\-.]*:/;
54143
+ function buildAssetIframeSrc({
54144
+ embedUrl,
54145
+ assetType,
54146
+ slideNumber
54147
+ }) {
54148
+ const targetSlide = slideNumber;
54149
+ if (assetType !== "presentation" || typeof targetSlide !== "number" || !Number.isInteger(targetSlide) || targetSlide < 1) {
54150
+ return embedUrl;
54151
+ }
54152
+ try {
54153
+ const isAbsolute = ABSOLUTE_URL_PATTERN.test(embedUrl);
54154
+ const url = new URL(embedUrl, "https://athena.local");
54155
+ url.searchParams.set("slide", String(targetSlide));
54156
+ return isAbsolute ? url.toString() : `${url.pathname}${url.search}${url.hash}`;
54157
+ } catch {
54158
+ const separator = embedUrl.includes("?") ? "&" : "?";
54159
+ return `${embedUrl}${separator}slide=${targetSlide}`;
54160
+ }
54161
+ }
54162
+ function buildPresentationNavigationMessage({
54163
+ assetId,
54164
+ assetType,
54165
+ slideNumber
54166
+ }) {
54167
+ if (assetType !== "presentation" || typeof slideNumber !== "number" || !Number.isInteger(slideNumber) || slideNumber < 1) {
54168
+ return null;
54169
+ }
54170
+ return {
54171
+ type: "NAVIGATE_TO_SLIDE",
54172
+ assetId,
54173
+ deckId: assetId,
54174
+ slideNumber
54175
+ };
54176
+ }
53603
54177
  const ASSET_TYPE_CONFIG = {
53604
54178
  presentation: { icon: Presentation, label: "Presentation" },
53605
54179
  spreadsheet: { icon: FileSpreadsheet, label: "Spreadsheet" },
@@ -53608,13 +54182,39 @@ const ASSET_TYPE_CONFIG = {
53608
54182
  unknown: { icon: File$1, label: "Asset" }
53609
54183
  };
53610
54184
  const AssetIframe = memo(
53611
- ({ tabId, tabName }) => {
53612
- const { backendUrl, apiKey, token } = useAthenaConfig();
53613
- const { embedUrl, isLoading, error: error2 } = useAssetEmbed(tabId, {
54185
+ ({ tab }) => {
54186
+ const iframeRef = useRef(null);
54187
+ const { backendUrl, appUrl, apiKey, token } = useAthenaConfig();
54188
+ const { embedUrl, isLoading, error: error2 } = useAssetEmbed(tab.id, {
53614
54189
  backendUrl,
54190
+ appUrl,
53615
54191
  apiKey,
53616
54192
  token
53617
54193
  });
54194
+ const iframeSrc = useMemo(
54195
+ () => embedUrl ? buildAssetIframeSrc({
54196
+ embedUrl,
54197
+ assetType: tab.type,
54198
+ slideNumber: tab.slideNumber
54199
+ }) : null,
54200
+ [embedUrl, tab.slideNumber, tab.type]
54201
+ );
54202
+ const navigationMessage = useMemo(
54203
+ () => buildPresentationNavigationMessage({
54204
+ assetId: tab.id,
54205
+ assetType: tab.type,
54206
+ slideNumber: tab.slideNumber
54207
+ }),
54208
+ [tab.id, tab.slideNumber, tab.type]
54209
+ );
54210
+ const postNavigationMessage = useCallback(() => {
54211
+ var _a2, _b;
54212
+ if (!navigationMessage) return;
54213
+ (_b = (_a2 = iframeRef.current) == null ? void 0 : _a2.contentWindow) == null ? void 0 : _b.postMessage(navigationMessage, "*");
54214
+ }, [navigationMessage]);
54215
+ useEffect(() => {
54216
+ postNavigationMessage();
54217
+ }, [postNavigationMessage]);
53618
54218
  if (isLoading) {
53619
54219
  return /* @__PURE__ */ jsx("div", { className: "flex h-full items-center justify-center", children: /* @__PURE__ */ jsxs("div", { className: "text-center", children: [
53620
54220
  /* @__PURE__ */ jsx(LoaderCircle, { className: "mx-auto size-6 animate-spin text-muted-foreground" }),
@@ -53628,20 +54228,23 @@ const AssetIframe = memo(
53628
54228
  /* @__PURE__ */ jsx("p", { className: "mt-0.5 line-clamp-2 break-words text-[10px] text-muted-foreground", children: error2 })
53629
54229
  ] }) });
53630
54230
  }
53631
- if (!embedUrl) return null;
54231
+ if (!iframeSrc) return null;
53632
54232
  return /* @__PURE__ */ jsx(
53633
54233
  "iframe",
53634
54234
  {
53635
- src: embedUrl,
54235
+ ref: iframeRef,
54236
+ src: iframeSrc,
53636
54237
  width: "100%",
53637
54238
  height: "100%",
53638
54239
  frameBorder: "0",
53639
54240
  allow: "fullscreen",
53640
- title: tabName ?? `Asset ${tabId}`,
53641
- className: "h-full w-full"
54241
+ title: tab.name ?? `Asset ${tab.id}`,
54242
+ className: "h-full w-full",
54243
+ onLoad: postNavigationMessage
53642
54244
  }
53643
54245
  );
53644
- }
54246
+ },
54247
+ (prev, next) => prev.tab.id === next.tab.id && prev.tab.name === next.tab.name && prev.tab.type === next.tab.type && prev.tab.slideNumber === next.tab.slideNumber
53645
54248
  );
53646
54249
  AssetIframe.displayName = "AssetIframe";
53647
54250
  const PanelContent = ({
@@ -53699,7 +54302,7 @@ const PanelContent = ({
53699
54302
  }
53700
54303
  )
53701
54304
  ] }),
53702
- /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-hidden", children: /* @__PURE__ */ jsx(AssetIframe, { tabId: tab.id, tabName: tab.name }) })
54305
+ /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-hidden", children: /* @__PURE__ */ jsx(AssetIframe, { tab }) })
53703
54306
  ]
53704
54307
  },
53705
54308
  tab.id
@@ -53709,7 +54312,7 @@ const PanelContent = ({
53709
54312
  "div",
53710
54313
  {
53711
54314
  className: isActive2 ? "h-full w-full" : "hidden",
53712
- children: /* @__PURE__ */ jsx(AssetIframe, { tabId: tab.id, tabName: tab.name })
54315
+ children: /* @__PURE__ */ jsx(AssetIframe, { tab })
53713
54316
  },
53714
54317
  tab.id
53715
54318
  );