@copilotkit/web-inspector 1.69.3 → 1.70.0

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.mjs CHANGED
@@ -8,7 +8,8 @@ import { projectInspectorMetadata } from "./lib/inspector-metadata.mjs";
8
8
  import { buildHomeModel, runtimeConnectionNeedsAttention } from "./lib/home-briefing.mjs";
9
9
  import { INSPECTOR_GROUPS, INSPECTOR_NAV_SECTIONS, getGroupForMenu, isInspectorMenuKey, shouldUseIconRail } from "./lib/inspector-nav.mjs";
10
10
  import { selectVisibleRealThreadId } from "./lib/thread-selection.mjs";
11
- import { TELEMETRY_DOCS_URL, ensureTelemetryDistinctId, getRuntimeUrlType, getTelemetryDistinctIdForUrl, maybeShowDisclosure, trackErrorSignalViewed, trackHomeCtaClicked, trackHomeViewed, trackInspectorOpened, trackMemoriesTabClicked, trackMetadataActionClicked, trackMetadataModuleViewed, trackTalkToEngineerClicked, trackThreadsEmptyEnabledViewed, trackThreadsEnabledViewed, trackThreadsExampleSelected, trackThreadsExampleTourCompleted, trackThreadsExampleTourDismissed, trackThreadsExampleTourReopened, trackThreadsExampleTourStarted, trackThreadsExampleTourStepViewed, trackThreadsExampleViewed, trackThreadsIntelligenceSignupClicked, trackThreadsLockedViewed, trackThreadsTabClicked, trackThreadsTalkToEngineerClicked, trackWhatsNewClicked, trackWhatsNewSignalViewed, trackWhatsNewViewed } from "./lib/telemetry.mjs";
11
+ import { TELEMETRY_DOCS_URL, ensureTelemetryDistinctId, getRuntimeUrlType, getTelemetryDistinctIdForUrl, maybeShowDisclosure, trackErrorSignalViewed, trackHomeCtaClicked, trackHomePromptCopied, trackHomeStoryBeatSelected, trackHomeViewed, trackInspectorOpened, trackMemoriesTabClicked, trackMetadataActionClicked, trackMetadataModuleViewed, trackTalkToEngineerClicked, trackThreadsEmptyEnabledViewed, trackThreadsEnabledViewed, trackThreadsExampleSelected, trackThreadsExampleTourCompleted, trackThreadsExampleTourDismissed, trackThreadsExampleTourReopened, trackThreadsExampleTourStarted, trackThreadsExampleTourStepViewed, trackThreadsExampleViewed, trackThreadsIntelligenceSignupClicked, trackThreadsLockedViewed, trackThreadsTabClicked, trackThreadsTalkToEngineerClicked, trackThreadsTryFromHereClicked, trackWhatsNewClicked, trackWhatsNewSignalViewed, trackWhatsNewViewed } from "./lib/telemetry.mjs";
12
+ import { createOnboardingPrompt, createOnboardingRunId } from "./lib/onboarding-prompt.mjs";
12
13
  import { LitElement, css, html, nothing, render, unsafeCSS } from "lit";
13
14
  import { marked } from "marked";
14
15
  import { styleMap } from "lit/directives/style-map.js";
@@ -296,6 +297,108 @@ const THREADS_RUNTIME_SETUP_PROMPT = [
296
297
  "Start the app and verify GET {basePath}/info reports threadEndpoints.list, inspect, mutations, and realtimeMetadata as true. Run focused tests, lint, and typecheck. Report the files changed, commands run, and verification result. If blocked, explain the missing input; do not invent setup."
297
298
  ].join("\n");
298
299
  const SELF_HOSTED_INTELLIGENCE_URL = "https://docs.copilotkit.ai/premium/self-hosting";
300
+ const INTELLIGENCE_STORY_BEATS = [
301
+ {
302
+ id: "threads",
303
+ label: "Threads",
304
+ duration: 6500,
305
+ lead: "You only see this session. Your users have all the others.",
306
+ support: "Rich Threads keep every conversation and its state, so you can open the one that broke instead of reproducing it."
307
+ },
308
+ {
309
+ id: "learning",
310
+ label: "Learning",
311
+ duration: 6e3,
312
+ lead: "Your users already told you what to fix.",
313
+ support: "Learning reads the runs behind those threads and finds the patterns — every Insight linked to the messages that back it."
314
+ },
315
+ {
316
+ id: "skill",
317
+ label: "Skills",
318
+ duration: 5500,
319
+ lead: "An Insight becomes a skill you own.",
320
+ support: "A SKILL.md built from that evidence — yours to review, edit and ship with your project."
321
+ },
322
+ {
323
+ id: "intelligence",
324
+ label: "Intelligence",
325
+ duration: 6e3,
326
+ lead: "Every round of real use leaves your agent better.",
327
+ support: "Approve a skill, pull it into your project, and the next run starts from what already worked."
328
+ }
329
+ ];
330
+ /**
331
+ * The threads the first beat shows.
332
+ *
333
+ * One of them failed, because that is the row a developer actually wants and
334
+ * the reason durable threads are worth paying for. It is a user's thread that
335
+ * went wrong, not a demo of our agent failing — the distinction matters for a
336
+ * card that has to argue for the product.
337
+ */
338
+ const INTELLIGENCE_STORY_THREADS = [
339
+ {
340
+ title: "Reschedule the Tuesday sync",
341
+ meta: "2 min ago",
342
+ failed: false
343
+ },
344
+ {
345
+ title: "Book time with the design team",
346
+ meta: "18 min ago",
347
+ failed: false
348
+ },
349
+ {
350
+ title: "Booked the wrong slot",
351
+ meta: "Needs a look",
352
+ failed: true
353
+ }
354
+ ];
355
+ /** The three rules the story derives, shown verbatim across all three beats. */
356
+ const INTELLIGENCE_STORY_RULES = [
357
+ "Check both calendars.",
358
+ "Propose several times.",
359
+ "Ask before booking."
360
+ ];
361
+ /**
362
+ * The raw thread signals the first beat collapses into those rules.
363
+ *
364
+ * Kept at real-message length and varied on purpose — these have to read as
365
+ * things people actually typed, not as three tidy bullet points. The longest
366
+ * one truncates in a narrow panel, which is honest: it is an excerpt.
367
+ */
368
+ const INTELLIGENCE_STORY_SIGNALS = [
369
+ "Check our calendars and find a time for both of us.",
370
+ "Could you share a few options?",
371
+ "Ask me before you book it."
372
+ ];
373
+ const INTELLIGENCE_STORY_SKILL_FILE = "meeting-scheduling/SKILL.md";
374
+ /**
375
+ * How long the copied confirmation stands before the button invites a second
376
+ * press. Longer than the 2s the Threads setup prompt uses, because this state
377
+ * also carries an instruction that has to be read, not just an acknowledgement.
378
+ */
379
+ const PROMPT_COPY_RESET_MS = 4e3;
380
+ const INTELLIGENCE_STORY_CHAIN = [
381
+ {
382
+ icon: "MessagesSquare",
383
+ name: "Threads",
384
+ detail: "Every conversation"
385
+ },
386
+ {
387
+ icon: "Lightbulb",
388
+ name: "Insights",
389
+ detail: "Backed by evidence"
390
+ },
391
+ {
392
+ icon: "FileText",
393
+ name: "Skills",
394
+ detail: "You approve"
395
+ },
396
+ {
397
+ icon: "Wand2",
398
+ name: "Your agent",
399
+ detail: "Starts from what worked"
400
+ }
401
+ ];
299
402
  const THREADS_EXAMPLE_OVERVIEW_VIDEO_URL = "https://cdn.copilotkit.ai/corp-site/videos/copilotkit-generative-ui-agentic-frontend-demo.webm";
300
403
  const THREADS_EXAMPLE_OVERVIEW_VIDEO_FALLBACK = "The demo video is unavailable. Use the example threads to explore Messages, AG-UI Events, and State.";
301
404
  const THREADS_EXAMPLE_TOUR_STORAGE_KEY = "cpk:inspector:threads-example-tour:v1";
@@ -1105,6 +1208,9 @@ var CpkThreadInspector = class CpkThreadInspector extends PortableLitElement {
1105
1208
  this.viewInAppError = null;
1106
1209
  this.focusMessageId = null;
1107
1210
  this.focusRequestId = 0;
1211
+ this.tryFromHereAvailable = false;
1212
+ this.tryFromHereBusy = false;
1213
+ this.tryFromHereError = null;
1108
1214
  this._tab = "timeline";
1109
1215
  this._fetchedMetadata = null;
1110
1216
  this._conversation = [];
@@ -1164,6 +1270,15 @@ var CpkThreadInspector = class CpkThreadInspector extends PortableLitElement {
1164
1270
  if (target.hasPointerCapture(this._dividerPointerId)) target.releasePointerCapture(this._dividerPointerId);
1165
1271
  this._dividerResizing = false;
1166
1272
  };
1273
+ this.onTryFromHere = (event) => {
1274
+ event.preventDefault();
1275
+ if (!this.tryFromHereAvailable || this.tryFromHereBusy) return;
1276
+ this.dispatchEvent(new CustomEvent("tryFromHere", {
1277
+ detail: this.threadId,
1278
+ bubbles: true,
1279
+ composed: true
1280
+ }));
1281
+ };
1167
1282
  }
1168
1283
  static {
1169
1284
  this.properties = {
@@ -1181,6 +1296,9 @@ var CpkThreadInspector = class CpkThreadInspector extends PortableLitElement {
1181
1296
  viewInAppError: { attribute: false },
1182
1297
  focusMessageId: { attribute: false },
1183
1298
  focusRequestId: { attribute: false },
1299
+ tryFromHereAvailable: { attribute: false },
1300
+ tryFromHereBusy: { attribute: false },
1301
+ tryFromHereError: { attribute: false },
1184
1302
  _tab: { state: true },
1185
1303
  _fetchedMetadata: { state: true },
1186
1304
  _conversation: { state: true },
@@ -1257,7 +1375,7 @@ var CpkThreadInspector = class CpkThreadInspector extends PortableLitElement {
1257
1375
  return JSON.stringify(Object.entries(headers).sort(([leftKey], [rightKey]) => leftKey.localeCompare(rightKey)));
1258
1376
  }
1259
1377
  renderTabContent(id) {
1260
- if (id === "timeline") return this.renderTimeline();
1378
+ if (id === "timeline") return this.withMessagesToolbar(this.renderTimeline());
1261
1379
  if (id === "state") return this.renderState();
1262
1380
  return this.renderEvents();
1263
1381
  }
@@ -1572,6 +1690,55 @@ var CpkThreadInspector = class CpkThreadInspector extends PortableLitElement {
1572
1690
  }
1573
1691
  }
1574
1692
 
1693
+ .cpk-td__try-from-here {
1694
+ display: inline-flex;
1695
+ align-items: center;
1696
+ gap: 4px;
1697
+ margin-inline-start: auto;
1698
+ padding: 6px 10px;
1699
+ border: 1px solid #dbdbe5;
1700
+ border-radius: 8px;
1701
+ background: #ffffff;
1702
+ color: #36363a;
1703
+ font-family: "Spline Sans Mono", monospace;
1704
+ font-size: 11px;
1705
+ font-weight: 600;
1706
+ line-height: 1.2;
1707
+ cursor: pointer;
1708
+ }
1709
+
1710
+ .cpk-td__try-from-here-icon {
1711
+ display: block;
1712
+ flex-shrink: 0;
1713
+ }
1714
+
1715
+ :host([dir="rtl"]) .cpk-td__try-from-here-icon {
1716
+ scale: -1 1;
1717
+ }
1718
+
1719
+ .cpk-td__try-from-here:hover:not(:disabled) {
1720
+ border-color: rgba(85, 88, 178, 0.38);
1721
+ background: #f7f7ff;
1722
+ color: #010507;
1723
+ }
1724
+
1725
+ .cpk-td__try-from-here:focus-visible {
1726
+ outline: 2px solid #a78bfa;
1727
+ outline-offset: 1px;
1728
+ }
1729
+
1730
+ .cpk-td__try-from-here:disabled {
1731
+ cursor: not-allowed;
1732
+ opacity: 0.55;
1733
+ }
1734
+
1735
+ .cpk-td__try-from-here-error {
1736
+ flex-basis: 100%;
1737
+ color: #c0333a;
1738
+ font-size: 11px;
1739
+ line-height: 1.4;
1740
+ }
1741
+
1575
1742
  /*
1576
1743
  * Each tab's content is wrapped in this panel so the keep-mounted
1577
1744
  * inactive panels can be hidden via display:none without disturbing
@@ -1956,6 +2123,8 @@ var CpkThreadInspector = class CpkThreadInspector extends PortableLitElement {
1956
2123
 
1957
2124
  .cpk-td__timeline-toolbar {
1958
2125
  display: flex;
2126
+ flex-wrap: wrap;
2127
+ align-items: center;
1959
2128
  gap: 6px;
1960
2129
  }
1961
2130
 
@@ -2298,6 +2467,7 @@ var CpkThreadInspector = class CpkThreadInspector extends PortableLitElement {
2298
2467
  :host([data-color-scheme="dark"]) .cpk-td__panel-toggle,
2299
2468
  :host([data-color-scheme="dark"]) .cpk-td__metadata-strip,
2300
2469
  :host([data-color-scheme="dark"]) .cpk-td__metadata-pill,
2470
+ :host([data-color-scheme="dark"]) .cpk-td__try-from-here,
2301
2471
  :host([data-color-scheme="dark"]) .cpk-td__tool-block,
2302
2472
  :host([data-color-scheme="dark"]) .cpk-td__tool-header,
2303
2473
  :host([data-color-scheme="dark"]) .cpk-td__tool-body,
@@ -2315,6 +2485,7 @@ var CpkThreadInspector = class CpkThreadInspector extends PortableLitElement {
2315
2485
  }
2316
2486
 
2317
2487
  :host([data-color-scheme="dark"]) .cpk-td__metadata-pill,
2488
+ :host([data-color-scheme="dark"]) .cpk-td__try-from-here,
2318
2489
  :host([data-color-scheme="dark"]) .cpk-td__bubble-inner--assistant,
2319
2490
  :host([data-color-scheme="dark"]) .cpk-td__tool-block,
2320
2491
  :host([data-color-scheme="dark"]) .cpk-td__event,
@@ -2473,6 +2644,7 @@ var CpkThreadInspector = class CpkThreadInspector extends PortableLitElement {
2473
2644
  :host([data-color-scheme="dark"]) .cpk-td__timeline-title,
2474
2645
  :host([data-color-scheme="dark"]) .cpk-td__timeline-bulk-toggle,
2475
2646
  :host([data-color-scheme="dark"]) .cpk-td__timeline-details-toggle,
2647
+ :host([data-color-scheme="dark"]) .cpk-td__try-from-here,
2476
2648
  :host([data-color-scheme="dark"]) .cpk-tdp__value {
2477
2649
  color: #f3f4f8;
2478
2650
  }
@@ -3405,15 +3577,55 @@ var CpkThreadInspector = class CpkThreadInspector extends PortableLitElement {
3405
3577
  ${isStop ? "Stop viewing" : "View in your app"}
3406
3578
  </button>
3407
3579
  ${this.viewInAppError ? html`<span class="cpk-td__view-in-app-error" role="alert"
3408
- >${this.viewInAppError}</span
3409
- >` : nothing}
3580
+ >${this.viewInAppError}</span
3581
+ >` : nothing}
3582
+ `;
3583
+ }
3584
+ renderTryFromHereControl() {
3585
+ if (!this.tryFromHereAvailable) return nothing;
3586
+ return html`
3587
+ <button
3588
+ type="button"
3589
+ class="cpk-td__try-from-here"
3590
+ aria-label="Try from here"
3591
+ ?disabled=${this.tryFromHereBusy}
3592
+ @click=${this.onTryFromHere}
3593
+ >
3594
+ ${this.tryFromHereBusy ? "Loading…" : "Try from here"}
3595
+ <svg
3596
+ class="cpk-td__try-from-here-icon"
3597
+ aria-hidden="true"
3598
+ width="12"
3599
+ height="12"
3600
+ viewBox="0 0 24 24"
3601
+ fill="none"
3602
+ stroke="currentColor"
3603
+ stroke-width="2"
3604
+ stroke-linecap="round"
3605
+ stroke-linejoin="round"
3606
+ >
3607
+ <path d="M7 7h10v10" />
3608
+ <path d="M7 17 17 7" />
3609
+ </svg>
3610
+ </button>
3611
+ ${this.tryFromHereError ? html`<span class="cpk-td__try-from-here-error" role="alert"
3612
+ >${this.tryFromHereError}</span
3613
+ >` : nothing}
3410
3614
  `;
3411
3615
  }
3412
3616
  renderTimelineBulkControls() {
3617
+ const bulk = this.renderTimelineBulkButtons();
3618
+ const tryFromHere = this.renderTryFromHereControl();
3619
+ if (bulk === nothing && tryFromHere === nothing) return nothing;
3620
+ return html`<div class="cpk-td__timeline-toolbar">
3621
+ ${bulk}${tryFromHere}
3622
+ </div>`;
3623
+ }
3624
+ renderTimelineBulkButtons() {
3413
3625
  if (this._eventsNotAvailable) return nothing;
3414
3626
  const detailIds = this.timelineItemsForEvents(this.activeEvents).filter((item) => item.details).map((item) => item.id);
3415
3627
  if (detailIds.length <= 1) return nothing;
3416
- return html`<div class="cpk-td__timeline-toolbar">
3628
+ return html`
3417
3629
  <button
3418
3630
  type="button"
3419
3631
  class="cpk-td__timeline-bulk-toggle"
@@ -3430,7 +3642,7 @@ var CpkThreadInspector = class CpkThreadInspector extends PortableLitElement {
3430
3642
  >
3431
3643
  Collapse all
3432
3644
  </button>
3433
- </div>`;
3645
+ `;
3434
3646
  }
3435
3647
  renderRawEventBulkControls() {
3436
3648
  const eventIds = this.activeEvents.map((event) => this.rawEventId(event));
@@ -3462,6 +3674,9 @@ var CpkThreadInspector = class CpkThreadInspector extends PortableLitElement {
3462
3674
  (this.shadowRoot?.querySelector(`[data-source-index="${sourceIndex}"]`))?.scrollIntoView?.({ block: "center" });
3463
3675
  });
3464
3676
  }
3677
+ withMessagesToolbar(content) {
3678
+ return html`${this.renderTimelineBulkControls()}${content}`;
3679
+ }
3465
3680
  renderTimeline() {
3466
3681
  if (this._loadingEvents) return html`
3467
3682
  <div class="cpk-td__status">Loading timeline…</div>
@@ -3509,7 +3724,7 @@ var CpkThreadInspector = class CpkThreadInspector extends PortableLitElement {
3509
3724
  this._conversation,
3510
3725
  this.agentMessagesInput,
3511
3726
  this._expandedTimelineDetails
3512
- ], () => html`${this.renderTimelineBulkControls()}${timelineItems.map((item) => this.renderTimelineItem(item))}`);
3727
+ ], () => html`${timelineItems.map((item) => this.renderTimelineItem(item))}`);
3513
3728
  }
3514
3729
  timelineItemClass(item) {
3515
3730
  const classes = ["cpk-td__timeline-item", `cpk-td__timeline-item--${item.kind}`];
@@ -3534,14 +3749,14 @@ var CpkThreadInspector = class CpkThreadInspector extends PortableLitElement {
3534
3749
  >
3535
3750
  <span class="cpk-td__timeline-title">${item.title}</span>
3536
3751
  ${item.sourceIndex ? html`
3537
- <button
3538
- type="button"
3539
- class="cpk-td__source-link"
3540
- @click=${() => this.revealSourceEvent(item.sourceIndex)}
3541
- >
3542
- Source event #${item.sourceIndex}
3543
- </button>
3544
- ` : nothing}
3752
+ <button
3753
+ type="button"
3754
+ class="cpk-td__source-link"
3755
+ @click=${() => this.revealSourceEvent(item.sourceIndex)}
3756
+ >
3757
+ Source event #${item.sourceIndex}
3758
+ </button>
3759
+ ` : nothing}
3545
3760
  <span class="cpk-td__timeline-time"
3546
3761
  >${formatTimestamp(item.timestamp)}</span
3547
3762
  >
@@ -3835,54 +4050,54 @@ var CpkThreadInspector = class CpkThreadInspector extends PortableLitElement {
3835
4050
  const eventId = this.rawEventId(event);
3836
4051
  const detailsExpanded = this._expandedRawEvents.has(eventId);
3837
4052
  return html`
3838
- <div
3839
- class="cpk-td__event cpk-td__event--${eventCategory(event.type)}"
3840
- data-source-index=${event.sourceIndex}
3841
- >
3842
- <div class="cpk-td__event-header">
3843
- <span class="cpk-td__event-type" title=${event.type}
3844
- >${humanizeEventType(event.type)}</span
3845
- >
3846
- <span class="cpk-td__event-time"
3847
- >${formatTimestamp(event.timestamp)}</span
3848
- >
3849
- </div>
3850
- <button
3851
- type="button"
3852
- class="cpk-td__timeline-details-toggle"
3853
- aria-expanded=${detailsExpanded ? "true" : "false"}
3854
- @click=${() => this.toggleRawEventDetails(eventId)}
4053
+ <div
4054
+ class="cpk-td__event cpk-td__event--${eventCategory(event.type)}"
4055
+ data-source-index=${event.sourceIndex}
3855
4056
  >
3856
- ${detailsExpanded ? html`
3857
- <svg
3858
- aria-hidden="true"
3859
- viewBox="0 0 24 24"
3860
- fill="none"
3861
- stroke="currentColor"
3862
- stroke-linecap="round"
3863
- stroke-linejoin="round"
3864
- >
3865
- <path d="m6 9 6 6 6-6" />
3866
- </svg>
3867
- ` : html`
3868
- <svg
3869
- aria-hidden="true"
3870
- viewBox="0 0 24 24"
3871
- fill="none"
3872
- stroke="currentColor"
3873
- stroke-linecap="round"
3874
- stroke-linejoin="round"
3875
- >
3876
- <path d="m9 18 6-6-6-6" />
3877
- </svg>
3878
- `}
3879
- <span
3880
- >${detailsExpanded ? "Hide details" : "Show details"}</span
4057
+ <div class="cpk-td__event-header">
4058
+ <span class="cpk-td__event-type" title=${event.type}
4059
+ >${humanizeEventType(event.type)}</span
4060
+ >
4061
+ <span class="cpk-td__event-time"
4062
+ >${formatTimestamp(event.timestamp)}</span
4063
+ >
4064
+ </div>
4065
+ <button
4066
+ type="button"
4067
+ class="cpk-td__timeline-details-toggle"
4068
+ aria-expanded=${detailsExpanded ? "true" : "false"}
4069
+ @click=${() => this.toggleRawEventDetails(eventId)}
3881
4070
  >
3882
- </button>
3883
- ${detailsExpanded ? renderHighlightedJsonBlock(event.rawEvent ?? event) : nothing}
3884
- </div>
3885
- `;
4071
+ ${detailsExpanded ? html`
4072
+ <svg
4073
+ aria-hidden="true"
4074
+ viewBox="0 0 24 24"
4075
+ fill="none"
4076
+ stroke="currentColor"
4077
+ stroke-linecap="round"
4078
+ stroke-linejoin="round"
4079
+ >
4080
+ <path d="m6 9 6 6 6-6" />
4081
+ </svg>
4082
+ ` : html`
4083
+ <svg
4084
+ aria-hidden="true"
4085
+ viewBox="0 0 24 24"
4086
+ fill="none"
4087
+ stroke="currentColor"
4088
+ stroke-linecap="round"
4089
+ stroke-linejoin="round"
4090
+ >
4091
+ <path d="m9 18 6-6-6-6" />
4092
+ </svg>
4093
+ `}
4094
+ <span
4095
+ >${detailsExpanded ? "Hide details" : "Show details"}</span
4096
+ >
4097
+ </button>
4098
+ ${detailsExpanded ? renderHighlightedJsonBlock(event.rawEvent ?? event) : nothing}
4099
+ </div>
4100
+ `;
3886
4101
  })}`;
3887
4102
  });
3888
4103
  }
@@ -4691,6 +4906,13 @@ var WebInspectorElement = class WebInspectorElement extends LitElement {
4691
4906
  this.systemColorSchemeMediaQuery = null;
4692
4907
  this.briefingRestoreMenu = null;
4693
4908
  this.homeViewedThisOpen = false;
4909
+ this.onboardingRunId = null;
4910
+ this.promptCopyState = "idle";
4911
+ this.promptCopyResetTimer = null;
4912
+ this.intelStoryBeat = 0;
4913
+ this.intelStoryUserPinned = false;
4914
+ this.intelStoryTimer = null;
4915
+ this.intelStoryReducedMotion = null;
4694
4916
  this.hasResolvedCore = false;
4695
4917
  this.settingsOpen = false;
4696
4918
  this.lastSelectedMenuByGroup = {
@@ -4736,6 +4958,8 @@ var WebInspectorElement = class WebInspectorElement extends LitElement {
4736
4958
  this.playgroundError = null;
4737
4959
  this.playgroundSourceThreadId = null;
4738
4960
  this.playgroundShowEphemeralNotice = false;
4961
+ this.tryFromHereBusy = false;
4962
+ this.tryFromHereError = null;
4739
4963
  this.threadCapabilityEnabled = null;
4740
4964
  this.threadCapabilityGeneration = 0;
4741
4965
  this.contextMenuOpen = false;
@@ -4976,6 +5200,26 @@ var WebInspectorElement = class WebInspectorElement extends LitElement {
4976
5200
  this.applyEventErrorLanding(key);
4977
5201
  this.requestUpdate();
4978
5202
  };
5203
+ this.handleIntelligencePromptCopy = async (event) => {
5204
+ this.clearIntelligencePromptReset();
5205
+ const runId = this.getOnboardingRunId();
5206
+ const clipboard = this.getClipboard(event);
5207
+ let outcome = "failed";
5208
+ if (clipboard?.writeText) try {
5209
+ await clipboard.writeText(createOnboardingPrompt(runId));
5210
+ outcome = "copied";
5211
+ } catch {
5212
+ outcome = "failed";
5213
+ }
5214
+ if (!this.isConnected) return;
5215
+ this.promptCopyState = outcome;
5216
+ this.requestUpdate();
5217
+ if (outcome === "copied") this.scheduleIntelligencePromptReset();
5218
+ if (!this.core?.telemetryDisabled) trackHomePromptCopied({
5219
+ onboarding_run_id: runId,
5220
+ outcome
5221
+ });
5222
+ };
4979
5223
  this.handleSystemColorSchemeChange = (event) => {
4980
5224
  if (this.hasExplicitColorScheme) return;
4981
5225
  this.colorScheme = event.matches ? "dark" : "light";
@@ -5220,21 +5464,14 @@ var WebInspectorElement = class WebInspectorElement extends LitElement {
5220
5464
  this.startPlaygroundSession(false);
5221
5465
  return;
5222
5466
  }
5223
- const core = this._core;
5224
5467
  const thread = this._threads.find((candidate) => candidate.id === threadId);
5225
- if (!core?.runtimeUrl || !thread) return;
5468
+ if (!thread) return;
5226
5469
  this.playgroundIsLoadingThread = true;
5227
5470
  this.playgroundError = null;
5228
5471
  this.requestUpdate();
5229
5472
  try {
5230
- const baseUrl = core.runtimeUrl.replace(/\/+$/, "");
5231
- const encodedThreadId = encodeURIComponent(threadId);
5232
- const [messagesResponse, stateResponse] = await Promise.all([fetch(`${baseUrl}/threads/${encodedThreadId}/messages`, { headers: { ...core.headers } }), fetch(`${baseUrl}/threads/${encodedThreadId}/state`, { headers: { ...core.headers } })]);
5233
- if (!messagesResponse.ok) throw new Error(`Failed to load thread (HTTP ${messagesResponse.status}).`);
5234
- const messagesBody = await messagesResponse.json();
5235
- const stateBody = stateResponse.ok ? await stateResponse.json() : { state: {} };
5236
- this.startPlaygroundSession(false, this.mapThreadMessagesToPlayground(messagesBody.messages ?? []), stateBody.state ?? {}, thread.agentId);
5237
- this.playgroundSourceThreadId = threadId;
5473
+ const snapshot = await this.loadThreadSnapshot(thread);
5474
+ this.applyThreadSnapshotToPlayground(thread, snapshot);
5238
5475
  } catch (error) {
5239
5476
  this.playgroundError = error instanceof Error ? error.message : "Failed to load thread.";
5240
5477
  } finally {
@@ -5242,6 +5479,35 @@ var WebInspectorElement = class WebInspectorElement extends LitElement {
5242
5479
  this.requestUpdate();
5243
5480
  }
5244
5481
  };
5482
+ this.handleTryFromHere = async (threadId) => {
5483
+ if (!threadId || this.tryFromHereBusy) return;
5484
+ const thread = this._threads.find((candidate) => candidate.id === threadId) ?? null;
5485
+ if (!thread) return;
5486
+ this.tryFromHereBusy = true;
5487
+ this.tryFromHereError = null;
5488
+ this.requestUpdate();
5489
+ const isCurrentTryFromHereRequest = () => this.selectedThreadId === threadId && this.selectedMenu === "threads";
5490
+ try {
5491
+ const snapshot = await this.loadThreadSnapshot(thread);
5492
+ if (isCurrentTryFromHereRequest()) {
5493
+ this.applyThreadSnapshotToPlayground(thread, snapshot);
5494
+ this.handleMenuSelect("playground");
5495
+ }
5496
+ if (!this.core?.telemetryDisabled) trackThreadsTryFromHereClicked({
5497
+ ...this.getThreadsTelemetryProps(),
5498
+ outcome: "success"
5499
+ });
5500
+ } catch (error) {
5501
+ if (isCurrentTryFromHereRequest()) this.tryFromHereError = error instanceof Error ? error.message : "Failed to load thread.";
5502
+ if (!this.core?.telemetryDisabled) trackThreadsTryFromHereClicked({
5503
+ ...this.getThreadsTelemetryProps(),
5504
+ outcome: "failure"
5505
+ });
5506
+ } finally {
5507
+ this.tryFromHereBusy = false;
5508
+ this.requestUpdate();
5509
+ }
5510
+ };
5245
5511
  this.runPlaygroundAgent = async () => {
5246
5512
  const core = this._core;
5247
5513
  const agent = this.playgroundAgent;
@@ -5892,7 +6158,7 @@ var WebInspectorElement = class WebInspectorElement extends LitElement {
5892
6158
  if (this.core?.getThreadStore(agentId)) return;
5893
6159
  const core = this.core;
5894
6160
  if (!core?.runtimeUrl) return;
5895
- const store = ɵcreateThreadStore({ fetch: globalThis.fetch });
6161
+ const store = ɵcreateThreadStore({ fetch: typeof core.ɵruntimeFetch === "function" ? core.ɵruntimeFetch : globalThis.fetch });
5896
6162
  store.start();
5897
6163
  store.setContext({
5898
6164
  runtimeUrl: core.runtimeUrl,
@@ -6603,7 +6869,9 @@ var WebInspectorElement = class WebInspectorElement extends LitElement {
6603
6869
  <span>${functionName}${isFailedCall ? " failed" : ""}</span>
6604
6870
  <span class="text-[10px] text-gray-600">ID: ${callId}</span>
6605
6871
  </div>
6606
- ${isFailedCall && toolError?.message ? html`<p class="mt-2 break-words leading-relaxed text-gray-800">
6872
+ ${isFailedCall && toolError?.message ? html`<p
6873
+ class="mt-2 break-words leading-relaxed text-gray-800"
6874
+ >
6607
6875
  ${toolError.message}
6608
6876
  </p>` : nothing}
6609
6877
  ${argsString ? html`<div class="mt-2">
@@ -6832,13 +7100,15 @@ var WebInspectorElement = class WebInspectorElement extends LitElement {
6832
7100
 
6833
7101
  .cpk-playground-composer {
6834
7102
  border: 1px solid #dcdce8;
6835
- box-shadow: 0 8px 22px rgba(31, 23, 57, 0.08),
7103
+ box-shadow:
7104
+ 0 8px 22px rgba(31, 23, 57, 0.08),
6836
7105
  0 1px 2px rgba(31, 23, 57, 0.1);
6837
7106
  }
6838
7107
 
6839
7108
  .cpk-playground-composer:focus-within {
6840
7109
  border-color: #aaa4d4;
6841
- box-shadow: 0 10px 26px rgba(86, 53, 155, 0.13),
7110
+ box-shadow:
7111
+ 0 10px 26px rgba(86, 53, 155, 0.13),
6842
7112
  0 0 0 3px rgba(190, 194, 255, 0.3);
6843
7113
  }
6844
7114
 
@@ -6855,20 +7125,22 @@ var WebInspectorElement = class WebInspectorElement extends LitElement {
6855
7125
  .inspector-window[data-color-scheme="dark"] .cpk-playground-composer {
6856
7126
  border-color: #464957;
6857
7127
  background: #15171e !important;
6858
- box-shadow: 0 8px 22px rgba(0, 0, 0, 0.26),
7128
+ box-shadow:
7129
+ 0 8px 22px rgba(0, 0, 0, 0.26),
6859
7130
  0 1px 2px rgba(0, 0, 0, 0.36);
6860
7131
  }
6861
7132
 
6862
7133
  .inspector-window[data-color-scheme="dark"]
6863
7134
  .cpk-playground-composer:focus-within {
6864
7135
  border-color: #777aae;
6865
- box-shadow: 0 10px 26px rgba(0, 0, 0, 0.34),
7136
+ box-shadow:
7137
+ 0 10px 26px rgba(0, 0, 0, 0.34),
6866
7138
  0 0 0 3px rgba(102, 106, 158, 0.3);
6867
7139
  }
6868
7140
 
6869
7141
  .cpk-playground-message-enter {
6870
- animation: cpk-playground-message-enter 0.24s cubic-bezier(0.16, 1, 0.3, 1)
6871
- both;
7142
+ animation: cpk-playground-message-enter 0.24s
7143
+ cubic-bezier(0.16, 1, 0.3, 1) both;
6872
7144
  }
6873
7145
 
6874
7146
  .cpk-playground-thinking-dot {
@@ -6887,8 +7159,7 @@ var WebInspectorElement = class WebInspectorElement extends LitElement {
6887
7159
  display: none;
6888
7160
  }
6889
7161
 
6890
- .cpk-playground-reasoning[open]
6891
- .cpk-playground-reasoning-chevron {
7162
+ .cpk-playground-reasoning[open] .cpk-playground-reasoning-chevron {
6892
7163
  transform: rotate(90deg);
6893
7164
  }
6894
7165
 
@@ -7934,8 +8205,10 @@ var WebInspectorElement = class WebInspectorElement extends LitElement {
7934
8205
  }
7935
8206
 
7936
8207
  .cpk-launcher-hud[data-color-scheme="light"] .cpk-launcher-hud__row:hover,
7937
- .cpk-launcher-hud[data-color-scheme="light"] .cpk-launcher-hud__row:focus-within,
7938
- .cpk-launcher-hud[data-color-scheme="light"] .cpk-launcher-hud__row[data-cpk-hud-help="open"] {
8208
+ .cpk-launcher-hud[data-color-scheme="light"]
8209
+ .cpk-launcher-hud__row:focus-within,
8210
+ .cpk-launcher-hud[data-color-scheme="light"]
8211
+ .cpk-launcher-hud__row[data-cpk-hud-help="open"] {
7939
8212
  background: #f0f0f4;
7940
8213
  }
7941
8214
 
@@ -8039,7 +8312,8 @@ var WebInspectorElement = class WebInspectorElement extends LitElement {
8039
8312
 
8040
8313
  .cpk-launcher-hud__row:hover .cpk-launcher-hud__detail,
8041
8314
  .cpk-launcher-hud__row:focus-within .cpk-launcher-hud__detail,
8042
- .cpk-launcher-hud__row[data-cpk-hud-help="open"] .cpk-launcher-hud__detail {
8315
+ .cpk-launcher-hud__row[data-cpk-hud-help="open"]
8316
+ .cpk-launcher-hud__detail {
8043
8317
  max-height: 72px;
8044
8318
  padding: 0 8px 7px;
8045
8319
  opacity: 1;
@@ -8114,8 +8388,7 @@ var WebInspectorElement = class WebInspectorElement extends LitElement {
8114
8388
  }
8115
8389
 
8116
8390
  .cpk-launcher-hud[data-cpk-hud-intro="true"] {
8117
- animation: cpk-launcher-hud-intro
8118
- var(--cpk-launcher-hud-intro-duration)
8391
+ animation: cpk-launcher-hud-intro var(--cpk-launcher-hud-intro-duration)
8119
8392
  cubic-bezier(0.16, 1, 0.3, 1) both;
8120
8393
  }
8121
8394
 
@@ -8123,16 +8396,14 @@ var WebInspectorElement = class WebInspectorElement extends LitElement {
8123
8396
  animation-name: cpk-launcher-hud-intro-right;
8124
8397
  }
8125
8398
 
8126
- .cpk-launcher-hud[data-cpk-hud-intro="true"]
8127
- .cpk-launcher-hud__row {
8399
+ .cpk-launcher-hud[data-cpk-hud-intro="true"] .cpk-launcher-hud__row {
8128
8400
  animation: cpk-launcher-hud-row-online
8129
- var(--cpk-launcher-hud-row-duration)
8130
- cubic-bezier(0.16, 1, 0.3, 1) both;
8401
+ var(--cpk-launcher-hud-row-duration) cubic-bezier(0.16, 1, 0.3, 1)
8402
+ both;
8131
8403
  animation-delay: var(--cpk-hud-row-delay);
8132
8404
  }
8133
8405
 
8134
- .cpk-launcher-hud[data-cpk-hud-intro="true"]
8135
- .cpk-launcher-hud__check {
8406
+ .cpk-launcher-hud[data-cpk-hud-intro="true"] .cpk-launcher-hud__check {
8136
8407
  animation: cpk-launcher-hud-check-online 220ms
8137
8408
  cubic-bezier(0.16, 1, 0.3, 1) both;
8138
8409
  animation-delay: calc(var(--cpk-hud-row-delay) + 90ms);
@@ -8140,10 +8411,8 @@ var WebInspectorElement = class WebInspectorElement extends LitElement {
8140
8411
 
8141
8412
  @media (prefers-reduced-motion: reduce) {
8142
8413
  .cpk-launcher-hud[data-cpk-hud-intro="true"],
8143
- .cpk-launcher-hud[data-cpk-hud-intro="true"]
8144
- .cpk-launcher-hud__row,
8145
- .cpk-launcher-hud[data-cpk-hud-intro="true"]
8146
- .cpk-launcher-hud__check {
8414
+ .cpk-launcher-hud[data-cpk-hud-intro="true"] .cpk-launcher-hud__row,
8415
+ .cpk-launcher-hud[data-cpk-hud-intro="true"] .cpk-launcher-hud__check {
8147
8416
  animation: none !important;
8148
8417
  opacity: 1;
8149
8418
  transform: none;
@@ -8310,7 +8579,9 @@ var WebInspectorElement = class WebInspectorElement extends LitElement {
8310
8579
  .inspector-sidebar[data-icon-rail="true"]
8311
8580
  .inspector-context-dropdown-icon
8312
8581
  svg,
8313
- .inspector-sidebar[data-icon-rail="true"] .inspector-agent-placeholder svg {
8582
+ .inspector-sidebar[data-icon-rail="true"]
8583
+ .inspector-agent-placeholder
8584
+ svg {
8314
8585
  width: 18px !important;
8315
8586
  height: 18px !important;
8316
8587
  overflow: visible !important;
@@ -8586,6 +8857,8 @@ var WebInspectorElement = class WebInspectorElement extends LitElement {
8586
8857
  }
8587
8858
  this.clearIconRailContextCloseTimer();
8588
8859
  this.unsubscribeFromInspectorThreadBridge();
8860
+ this.stopIntelligenceStory();
8861
+ this.clearIntelligencePromptReset();
8589
8862
  this.threadsSetupPromptCopyGeneration += 1;
8590
8863
  if (this.threadsSetupPromptCopyResetTimeoutId !== null) {
8591
8864
  window.clearTimeout(this.threadsSetupPromptCopyResetTimeoutId);
@@ -8655,6 +8928,7 @@ var WebInspectorElement = class WebInspectorElement extends LitElement {
8655
8928
  this.maybeCompleteEventErrorView();
8656
8929
  this.flushErrorLandingScroll();
8657
8930
  this.maybeTrackHomeViewed();
8931
+ this.syncIntelligenceStory();
8658
8932
  if (!this.isOpen) {
8659
8933
  this.lastScrolledAgentNavigationLayout = null;
8660
8934
  return;
@@ -9083,12 +9357,12 @@ var WebInspectorElement = class WebInspectorElement extends LitElement {
9083
9357
  </span>
9084
9358
  <span class="inspector-nav-label">${item.label}</span>
9085
9359
  ${marker ? html`
9086
- <span
9087
- class="inspector-nav-signal-dot"
9088
- data-cpk-signal-tone=${marker.tone}
9089
- aria-hidden="true"
9090
- ></span>
9091
- ` : nothing}
9360
+ <span
9361
+ class="inspector-nav-signal-dot"
9362
+ data-cpk-signal-tone=${marker.tone}
9363
+ aria-hidden="true"
9364
+ ></span>
9365
+ ` : nothing}
9092
9366
  </button>
9093
9367
  `;
9094
9368
  })}
@@ -9329,23 +9603,37 @@ var WebInspectorElement = class WebInspectorElement extends LitElement {
9329
9603
  const connected = model.hero.connection === "connected";
9330
9604
  const action = model.hero.action;
9331
9605
  const renewing = action?.kind === "renew";
9606
+ const installing = !connected && !renewing;
9332
9607
  return html`
9333
9608
  <section
9334
9609
  class="inspector-home-section inspector-intelligence-hud"
9335
9610
  data-inspector-home-card="intelligence"
9336
9611
  data-state=${connected ? "connected" : "disconnected"}
9612
+ data-mode=${connected ? "connected" : renewing ? "renew" : "install"}
9337
9613
  aria-label="Intelligence ${connected ? "connected" : renewing ? "plan expired" : "not enabled"}"
9338
9614
  >
9339
9615
  <header class="inspector-intelligence-hud-header">
9340
9616
  <div class="inspector-intelligence-hud-heading">
9341
9617
  <h2 class="inspector-home-section-title">
9618
+ ${installing ? html`
9619
+ <img
9620
+ class="inspector-intelligence-mark"
9621
+ src=${inspector_logo_kite_default}
9622
+ alt=""
9623
+ aria-hidden="true"
9624
+ />
9625
+ ` : nothing}
9342
9626
  ${connected ? "Intelligence" : model.hero.title}
9343
9627
  </h2>
9344
- ${connected ? nothing : html`
9345
- <p class="inspector-intelligence-hud-description">
9346
- ${model.hero.body}
9347
- </p>
9348
- `}
9628
+ ${connected ? nothing : installing ? html`
9629
+ <p class="inspector-intelligence-sr-summary">
9630
+ ${model.hero.body}
9631
+ </p>
9632
+ ` : html`
9633
+ <p class="inspector-intelligence-hud-description">
9634
+ ${model.hero.body}
9635
+ </p>
9636
+ `}
9349
9637
  </div>
9350
9638
  <div class="inspector-intelligence-hud-header-actions">
9351
9639
  ${connected || renewing ? html`
@@ -9357,7 +9645,7 @@ var WebInspectorElement = class WebInspectorElement extends LitElement {
9357
9645
  ${connected ? "Connected" : "Plan expired"}
9358
9646
  </span>
9359
9647
  ` : nothing}
9360
- ${!connected && action ? html`
9648
+ ${renewing && action ? html`
9361
9649
  <a
9362
9650
  class="inspector-intelligence-hud-action inspector-intelligence-hud-connect-action"
9363
9651
  data-inspector-home-intelligence-action=${action.kind}
@@ -9371,9 +9659,18 @@ var WebInspectorElement = class WebInspectorElement extends LitElement {
9371
9659
  ${action.label} ${this.renderIcon("ArrowUpRight")}
9372
9660
  </a>
9373
9661
  ` : nothing}
9662
+ ${installing ? this.renderIntelligenceInstallActions(action) : nothing}
9374
9663
  </div>
9375
9664
  </header>
9376
9665
 
9666
+ ${installing && this.promptCopyState === "failed" ? html`
9667
+ <code
9668
+ class="inspector-intelligence-install-fallback"
9669
+ tabindex="0"
9670
+ >${createOnboardingPrompt(this.getOnboardingRunId())}</code
9671
+ >
9672
+ ` : nothing}
9673
+ ${installing ? this.renderIntelligenceStory() : nothing}
9377
9674
  ${connected ? html`
9378
9675
  <div
9379
9676
  class="inspector-intelligence-hud-details"
@@ -9675,23 +9972,393 @@ var WebInspectorElement = class WebInspectorElement extends LitElement {
9675
9972
  if (this.core?.telemetryDisabled) return;
9676
9973
  trackHomeCtaClicked({ action_kind: action.kind });
9677
9974
  }
9678
- maybeTrackHomeViewed() {
9679
- if (this.selectedMenu !== "home" || this.settingsOpen || !this.isOpen) return;
9680
- if (!this.homeViewedThisOpen && !this.core?.telemetryDisabled) {
9681
- this.homeViewedThisOpen = true;
9682
- trackHomeViewed();
9975
+ /**
9976
+ * The install row: copy the prompt, or fall back to the signup page.
9977
+ *
9978
+ * The prompt is primary and the link is secondary, which is the inversion
9979
+ * this card exists for. Leaving for a signup page is where developers drop
9980
+ * out; pasting into the editor they are already in is not.
9981
+ *
9982
+ * No third-party coding-agent logos here, unlike the Intelligence app. That
9983
+ * app is a private hosted surface; this one is a published npm package
9984
+ * embedded in other people's sites, and shipping Anthropic's and OpenAI's
9985
+ * marks inside it is a trademark call that is not ours to make quietly. The
9986
+ * helper line names the agents in text instead.
9987
+ */
9988
+ renderIntelligenceInstallActions(action) {
9989
+ const copied = this.promptCopyState === "copied";
9990
+ const failed = this.promptCopyState === "failed";
9991
+ return html`
9992
+ <div
9993
+ class="inspector-intelligence-install"
9994
+ data-copy-state=${this.promptCopyState}
9995
+ >
9996
+ ${this.promptCopyState === "idle" ? action ? html`
9997
+ <a
9998
+ class="inspector-intelligence-install-secondary"
9999
+ data-inspector-home-intelligence-action=${action.kind}
10000
+ href=${action.url}
10001
+ target="_blank"
10002
+ rel="noopener noreferrer"
10003
+ aria-label="Set Intelligence up yourself (opens in a new tab)"
10004
+ style=${INTERACTIVE_FOCUS_BASE_STYLE}
10005
+ @click=${() => this.handleHomeHeroCta(action)}
10006
+ >
10007
+ Set it up yourself ${this.renderIcon("ArrowUpRight")}
10008
+ </a>
10009
+ ` : nothing : html`
10010
+ <p
10011
+ class="inspector-intelligence-install-hint"
10012
+ data-tone=${failed ? "error" : "success"}
10013
+ role="status"
10014
+ >
10015
+ ${failed ? "Clipboard blocked — copy the prompt below." : "Paste it into your coding agent."}
10016
+ </p>
10017
+ `}
10018
+ <button
10019
+ type="button"
10020
+ class="inspector-intelligence-hud-action inspector-intelligence-install-copy"
10021
+ data-inspector-intelligence-copy-prompt
10022
+ aria-label=${copied ? "Install prompt copied to clipboard. Paste it into your coding agent." : "Copy the Intelligence install prompt"}
10023
+ style=${INTERACTIVE_FOCUS_BASE_STYLE}
10024
+ @click=${this.handleIntelligencePromptCopy}
10025
+ >
10026
+ ${this.renderIcon(copied ? "Check" : "ClipboardCopy")}
10027
+ ${copied ? "Prompt copied" : "Copy setup prompt"}
10028
+ </button>
10029
+ </div>
10030
+ `;
10031
+ }
10032
+ /** One run id per element lifetime; minted on first use, never rotated. */
10033
+ getOnboardingRunId() {
10034
+ this.onboardingRunId ??= createOnboardingRunId();
10035
+ return this.onboardingRunId;
10036
+ }
10037
+ /**
10038
+ * Return the button and its secondary line to the idle state.
10039
+ *
10040
+ * Long enough to read six words, short enough that a developer who went to
10041
+ * their editor and came back — because the paste went somewhere wrong, or
10042
+ * the terminal is gone — finds a button that plainly invites a second press
10043
+ * rather than a spent one wearing a checkmark.
10044
+ *
10045
+ * Only the copied state resets. A failed copy has the prompt on screen for
10046
+ * manual selection, and yanking that away mid-drag would be worse than the
10047
+ * clipboard failing in the first place.
10048
+ */
10049
+ scheduleIntelligencePromptReset() {
10050
+ this.clearIntelligencePromptReset();
10051
+ this.promptCopyResetTimer = setTimeout(() => {
10052
+ this.promptCopyResetTimer = null;
10053
+ if (!this.isConnected || this.promptCopyState !== "copied") return;
10054
+ this.promptCopyState = "idle";
10055
+ this.requestUpdate();
10056
+ }, PROMPT_COPY_RESET_MS);
10057
+ }
10058
+ clearIntelligencePromptReset() {
10059
+ if (this.promptCopyResetTimer !== null) {
10060
+ clearTimeout(this.promptCopyResetTimer);
10061
+ this.promptCopyResetTimer = null;
9683
10062
  }
9684
10063
  }
9685
- renderWindow() {
9686
- const windowState = this.contextState.window;
9687
- const isDocked = this.dockMode !== "floating";
9688
- const isPoppedOut = this.isPoppedOut;
9689
- const isTransitioning = this.hasAttribute("data-transitioning");
9690
- const disableDrag = isDocked || isPoppedOut;
9691
- const windowStyles = isPoppedOut ? {
9692
- position: "fixed",
9693
- inset: "0",
9694
- width: "100%",
10064
+ /**
10065
+ * The three-beat Intelligence story.
10066
+ *
10067
+ * Every beat is in the DOM at all times and switched by opacity, so the
10068
+ * strip never reflows and screen readers get one stable structure. The
10069
+ * caption is the live text; the beats themselves are decorative and hidden
10070
+ * from assistive tech, because reading out a mocked code listing helps
10071
+ * nobody.
10072
+ */
10073
+ /**
10074
+ * The rotating argument, paired to whatever the picture below is showing.
10075
+ *
10076
+ * Hidden from assistive tech: a sentence that replaces itself every few
10077
+ * seconds is noise in a screen reader, so the stable summary above carries
10078
+ * the message there instead. Both sentences are always in the DOM and only
10079
+ * their opacity changes, so the block cannot reflow and the card never jumps
10080
+ * height mid-loop.
10081
+ */
10082
+ /**
10083
+ * Where a slide sits relative to the one on screen.
10084
+ *
10085
+ * This is what makes the motion agree with the rail: the rail reads left to
10086
+ * right, so a slide that has not been reached yet waits to the right, and one
10087
+ * already passed leaves to the left. Deriving it from the indices rather than
10088
+ * remembering a direction means clicking backwards through the tabs animates
10089
+ * backwards for free, with no state to keep in sync. The loop's wrap from the
10090
+ * last tab to the first therefore reads as a rewind, which is what it is.
10091
+ */
10092
+ intelligenceSlidePosition(index) {
10093
+ if (index === this.intelStoryBeat) return "active";
10094
+ return index < this.intelStoryBeat ? "before" : "after";
10095
+ }
10096
+ /** Same rule, addressed by beat id, for the picture halves. */
10097
+ intelligenceBeatPosition(beatId) {
10098
+ return this.intelligenceSlidePosition(INTELLIGENCE_STORY_BEATS.findIndex((beat) => beat.id === beatId));
10099
+ }
10100
+ renderIntelligenceStoryCopy() {
10101
+ return html`
10102
+ <div
10103
+ class="inspector-intelligence-copy"
10104
+ data-inspector-intelligence-copy
10105
+ data-beat=${INTELLIGENCE_STORY_BEATS[this.intelStoryBeat]?.id ?? "threads"}
10106
+ aria-hidden="true"
10107
+ >
10108
+ ${INTELLIGENCE_STORY_BEATS.map((beat, index) => html`
10109
+ <div
10110
+ class="inspector-intelligence-copy-slide"
10111
+ data-beat-id=${beat.id}
10112
+ data-active=${index === this.intelStoryBeat}
10113
+ data-position=${this.intelligenceSlidePosition(index)}
10114
+ >
10115
+ <strong>${beat.lead}</strong>
10116
+ <span>${beat.support}</span>
10117
+ </div>
10118
+ `)}
10119
+ </div>
10120
+ `;
10121
+ }
10122
+ renderIntelligenceStory() {
10123
+ return html`
10124
+ <section
10125
+ class="inspector-intelligence-story"
10126
+ data-inspector-intelligence-story
10127
+ data-beat=${INTELLIGENCE_STORY_BEATS[this.intelStoryBeat]?.id ?? "threads"}
10128
+ >
10129
+ ${this.renderIntelligenceStoryCopy()}
10130
+ <div class="inspector-intelligence-story-stage" aria-hidden="true">
10131
+ ${this.renderIntelligenceStoryThreads()}
10132
+ ${this.renderIntelligenceStoryLearning()}
10133
+ ${this.renderIntelligenceStorySkill()}
10134
+ ${this.renderIntelligenceStoryReuse()}
10135
+ </div>
10136
+ <div
10137
+ class="inspector-intelligence-story-rail"
10138
+ role="tablist"
10139
+ aria-label="What Intelligence adds"
10140
+ >
10141
+ ${INTELLIGENCE_STORY_BEATS.map((beat, index) => html`
10142
+ <button
10143
+ type="button"
10144
+ role="tab"
10145
+ class="inspector-intelligence-story-tab"
10146
+ aria-selected=${index === this.intelStoryBeat}
10147
+ data-active=${index === this.intelStoryBeat}
10148
+ style=${INTERACTIVE_FOCUS_BASE_STYLE}
10149
+ @click=${() => this.pinIntelligenceStoryBeat(index)}
10150
+ >
10151
+ ${beat.label}
10152
+ </button>
10153
+ `)}
10154
+ </div>
10155
+ </section>
10156
+ `;
10157
+ }
10158
+ /** Beat 1 — the developer's own users' conversations, one of them broken. */
10159
+ renderIntelligenceStoryThreads() {
10160
+ return html`
10161
+ <div
10162
+ class="inspector-intelligence-beat"
10163
+ data-beat-id="threads"
10164
+ data-position=${this.intelligenceBeatPosition("threads")}
10165
+ >
10166
+ <div class="inspector-intelligence-threads">
10167
+ ${INTELLIGENCE_STORY_THREADS.map((thread, index) => html`
10168
+ <span
10169
+ class="inspector-intelligence-thread"
10170
+ data-failed=${thread.failed}
10171
+ style="--thread-index:${index}"
10172
+ >
10173
+ <i></i>
10174
+ <strong>${thread.title}</strong>
10175
+ <small>${thread.meta}</small>
10176
+ </span>
10177
+ `)}
10178
+ </div>
10179
+ </div>
10180
+ `;
10181
+ }
10182
+ renderIntelligenceStoryLearning() {
10183
+ return html`
10184
+ <div
10185
+ class="inspector-intelligence-beat"
10186
+ data-beat-id="learning"
10187
+ data-position=${this.intelligenceBeatPosition("learning")}
10188
+ >
10189
+ <div class="inspector-intelligence-beat-col">
10190
+ <span class="inspector-intelligence-beat-label">
10191
+ Signals from ${INTELLIGENCE_STORY_SIGNALS.length} threads
10192
+ </span>
10193
+ ${INTELLIGENCE_STORY_SIGNALS.map((signal, index) => html`
10194
+ <span
10195
+ class="inspector-intelligence-signal"
10196
+ style="--signal-index:${index}"
10197
+ >${signal}</span
10198
+ >
10199
+ `)}
10200
+ </div>
10201
+ <div class="inspector-intelligence-beat-flow">
10202
+ ${this.renderIcon("ArrowRight")}
10203
+ </div>
10204
+ <div class="inspector-intelligence-beat-col">
10205
+ <span class="inspector-intelligence-beat-label">
10206
+ Reusable pattern
10207
+ </span>
10208
+ ${INTELLIGENCE_STORY_RULES.map((rule, index) => html`
10209
+ <span
10210
+ class="inspector-intelligence-rule"
10211
+ style="--rule-index:${index}"
10212
+ >
10213
+ <i>${this.renderIcon("Check")}</i>${rule}
10214
+ </span>
10215
+ `)}
10216
+ </div>
10217
+ </div>
10218
+ `;
10219
+ }
10220
+ renderIntelligenceStorySkill() {
10221
+ return html`
10222
+ <div
10223
+ class="inspector-intelligence-beat"
10224
+ data-beat-id="skill"
10225
+ data-position=${this.intelligenceBeatPosition("skill")}
10226
+ >
10227
+ <div class="inspector-intelligence-skill-file">
10228
+ <header>
10229
+ ${this.renderIcon("FileText")}
10230
+ <strong>${INTELLIGENCE_STORY_SKILL_FILE}</strong>
10231
+ <em>Pending review</em>
10232
+ </header>
10233
+ <div class="inspector-intelligence-skill-code">
10234
+ <span data-line="1"><b># Meeting scheduling</b></span>
10235
+ <span data-line="2">When planning a meeting:</span>
10236
+ ${INTELLIGENCE_STORY_RULES.map((rule, index) => html`
10237
+ <span data-line=${index + 3} style="--rule-index:${index}"
10238
+ >${index + 1}. ${rule}</span
10239
+ >
10240
+ `)}
10241
+ </div>
10242
+ </div>
10243
+ </div>
10244
+ `;
10245
+ }
10246
+ renderIntelligenceStoryReuse() {
10247
+ return html`
10248
+ <div
10249
+ class="inspector-intelligence-beat"
10250
+ data-beat-id="intelligence"
10251
+ data-position=${this.intelligenceBeatPosition("intelligence")}
10252
+ >
10253
+ <div class="inspector-intelligence-chain">
10254
+ ${INTELLIGENCE_STORY_CHAIN.map((step, index) => html`
10255
+ <span
10256
+ class="inspector-intelligence-chain-step"
10257
+ style="--step-index:${index}"
10258
+ >
10259
+ <i>${this.renderIcon(step.icon)}</i>
10260
+ <strong>${step.name}</strong>
10261
+ <small>${step.detail}</small>
10262
+ </span>
10263
+ ${index === INTELLIGENCE_STORY_CHAIN.length - 1 ? nothing : html`
10264
+ <span
10265
+ class="inspector-intelligence-chain-arrow"
10266
+ style="--step-index:${index}"
10267
+ >${this.renderIcon("ChevronRight")}</span
10268
+ >
10269
+ `}
10270
+ `)}
10271
+ </div>
10272
+ <span class="inspector-intelligence-chain-proof">
10273
+ ${this.renderIcon("Sparkles")} Next run starts with
10274
+ <code>${INTELLIGENCE_STORY_SKILL_FILE}</code>
10275
+ </span>
10276
+ </div>
10277
+ `;
10278
+ }
10279
+ /** A press pins that beat and stops the loop; the developer is now driving. */
10280
+ pinIntelligenceStoryBeat(index) {
10281
+ this.intelStoryUserPinned = true;
10282
+ this.intelStoryBeat = index;
10283
+ this.stopIntelligenceStory();
10284
+ this.requestUpdate();
10285
+ if (!this.core?.telemetryDisabled) {
10286
+ const beat = INTELLIGENCE_STORY_BEATS[index];
10287
+ if (beat) trackHomeStoryBeatSelected({
10288
+ beat: beat.id,
10289
+ beat_index: index
10290
+ });
10291
+ }
10292
+ }
10293
+ /**
10294
+ * Advance the story only while it is actually on screen.
10295
+ *
10296
+ * Gated on the panel being open, Home being the visible tab, settings being
10297
+ * closed and the document being visible. Anything less and a debugging tool
10298
+ * would be holding a repeating timer behind a closed panel.
10299
+ */
10300
+ syncIntelligenceStory() {
10301
+ if (!(this.isOpen && !this.settingsOpen && this.selectedMenu === "home" && !this._core?.intelligence && typeof document !== "undefined" && document.visibilityState !== "hidden")) {
10302
+ this.intelStoryUserPinned = false;
10303
+ this.stopIntelligenceStory();
10304
+ return;
10305
+ }
10306
+ if (this.intelStoryUserPinned || this.prefersReducedMotion()) {
10307
+ this.stopIntelligenceStory();
10308
+ return;
10309
+ }
10310
+ if (this.intelStoryTimer !== null) return;
10311
+ const advance = () => {
10312
+ const current = INTELLIGENCE_STORY_BEATS[this.intelStoryBeat];
10313
+ this.intelStoryTimer = setTimeout(() => {
10314
+ this.intelStoryTimer = null;
10315
+ if (!this.isConnected) return;
10316
+ this.intelStoryBeat = (this.intelStoryBeat + 1) % INTELLIGENCE_STORY_BEATS.length;
10317
+ this.requestUpdate();
10318
+ advance();
10319
+ }, current?.duration ?? 3800);
10320
+ };
10321
+ advance();
10322
+ }
10323
+ stopIntelligenceStory() {
10324
+ if (this.intelStoryTimer !== null) {
10325
+ clearTimeout(this.intelStoryTimer);
10326
+ this.intelStoryTimer = null;
10327
+ }
10328
+ }
10329
+ /**
10330
+ * Reduced motion parks the story on the closing beat.
10331
+ *
10332
+ * That beat is the whole argument in one static frame, so a developer who
10333
+ * asked their OS for less motion still gets the point rather than a
10334
+ * fragment of it.
10335
+ */
10336
+ prefersReducedMotion() {
10337
+ if (typeof window === "undefined" || !window.matchMedia) return false;
10338
+ this.intelStoryReducedMotion ??= window.matchMedia("(prefers-reduced-motion: reduce)");
10339
+ if (this.intelStoryReducedMotion.matches) {
10340
+ this.intelStoryBeat = INTELLIGENCE_STORY_BEATS.length - 1;
10341
+ return true;
10342
+ }
10343
+ return false;
10344
+ }
10345
+ maybeTrackHomeViewed() {
10346
+ if (this.selectedMenu !== "home" || this.settingsOpen || !this.isOpen) return;
10347
+ if (!this.homeViewedThisOpen && !this.core?.telemetryDisabled) {
10348
+ this.homeViewedThisOpen = true;
10349
+ trackHomeViewed();
10350
+ }
10351
+ }
10352
+ renderWindow() {
10353
+ const windowState = this.contextState.window;
10354
+ const isDocked = this.dockMode !== "floating";
10355
+ const isPoppedOut = this.isPoppedOut;
10356
+ const isTransitioning = this.hasAttribute("data-transitioning");
10357
+ const disableDrag = isDocked || isPoppedOut;
10358
+ const windowStyles = isPoppedOut ? {
10359
+ position: "fixed",
10360
+ inset: "0",
10361
+ width: "100%",
9695
10362
  height: "100%",
9696
10363
  minWidth: "0",
9697
10364
  minHeight: "0",
@@ -10680,6 +11347,24 @@ var WebInspectorElement = class WebInspectorElement extends LitElement {
10680
11347
  });
10681
11348
  return mapped;
10682
11349
  }
11350
+ async loadThreadSnapshot(thread) {
11351
+ const core = this._core;
11352
+ if (!core?.runtimeUrl) throw new Error("Failed to load thread.");
11353
+ const baseUrl = core.runtimeUrl.replace(/\/+$/, "");
11354
+ const encodedThreadId = encodeURIComponent(thread.id);
11355
+ const [messagesResponse, stateResponse] = await Promise.all([fetch(`${baseUrl}/threads/${encodedThreadId}/messages`, { headers: { ...core.headers } }), fetch(`${baseUrl}/threads/${encodedThreadId}/state`, { headers: { ...core.headers } })]);
11356
+ if (!messagesResponse.ok) throw new Error(`Failed to load thread (HTTP ${messagesResponse.status}).`);
11357
+ const messagesBody = await messagesResponse.json();
11358
+ const stateBody = stateResponse.ok ? await stateResponse.json() : { state: {} };
11359
+ return {
11360
+ messages: this.mapThreadMessagesToPlayground(messagesBody.messages ?? []),
11361
+ state: stateBody.state ?? {}
11362
+ };
11363
+ }
11364
+ applyThreadSnapshotToPlayground(thread, snapshot) {
11365
+ this.startPlaygroundSession(false, snapshot.messages, snapshot.state, thread.agentId);
11366
+ this.playgroundSourceThreadId = thread.id;
11367
+ }
10683
11368
  sendPlaygroundMessage(content) {
10684
11369
  if (!content || this.playgroundIsRunning || this.playgroundIsLoadingThread) return;
10685
11370
  const selectedAgentId = this.resolvePlaygroundAgentId(this.selectedContext);
@@ -10703,30 +11388,30 @@ var WebInspectorElement = class WebInspectorElement extends LitElement {
10703
11388
  @submit=${this.handlePlaygroundSubmit}
10704
11389
  >
10705
11390
  ${this.playgroundError ? html`<div
10706
- class="mx-auto mb-2 flex max-w-3xl items-start gap-2 rounded-lg border border-rose-200 bg-rose-50 px-2.5 py-2 text-[10px] text-rose-950"
10707
- role="alert"
10708
- data-playground-error
11391
+ class="mx-auto mb-2 flex max-w-3xl items-start gap-2 rounded-lg border border-rose-200 bg-rose-50 px-2.5 py-2 text-[10px] text-rose-950"
11392
+ role="alert"
11393
+ data-playground-error
11394
+ >
11395
+ <span class="mt-0.5 shrink-0"
11396
+ >${this.renderIcon("TriangleAlert")}</span
10709
11397
  >
10710
- <span class="mt-0.5 shrink-0"
10711
- >${this.renderIcon("TriangleAlert")}</span
10712
- >
10713
- <div class="min-w-0 flex-1">
10714
- <p class="font-semibold">Agent run failed</p>
10715
- <p class="mt-0.5 break-words leading-relaxed">
10716
- ${this.playgroundError}
10717
- </p>
10718
- </div>
10719
- ${hasRetry ? html`
10720
- <button
10721
- type="button"
10722
- class="shrink-0 rounded-md border border-rose-200 bg-white px-2 py-1 font-medium text-rose-700 transition hover:bg-rose-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-rose-300 focus-visible:ring-offset-1 disabled:opacity-50"
10723
- ?disabled=${busy}
10724
- @click=${this.handlePlaygroundRetry}
10725
- >
10726
- Retry
10727
- </button>
10728
- ` : nothing}
10729
- </div>` : nothing}
11398
+ <div class="min-w-0 flex-1">
11399
+ <p class="font-semibold">Agent run failed</p>
11400
+ <p class="mt-0.5 break-words leading-relaxed">
11401
+ ${this.playgroundError}
11402
+ </p>
11403
+ </div>
11404
+ ${hasRetry ? html`
11405
+ <button
11406
+ type="button"
11407
+ class="shrink-0 rounded-md border border-rose-200 bg-white px-2 py-1 font-medium text-rose-700 transition hover:bg-rose-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-rose-300 focus-visible:ring-offset-1 disabled:opacity-50"
11408
+ ?disabled=${busy}
11409
+ @click=${this.handlePlaygroundRetry}
11410
+ >
11411
+ Retry
11412
+ </button>
11413
+ ` : nothing}
11414
+ </div>` : nothing}
10730
11415
  <div
10731
11416
  class="cpk-playground-composer mx-auto flex max-w-3xl items-end gap-1.5 rounded-[28px] bg-white px-2.5 py-1.5 shadow-[0_4px_4px_0_#0000000a,0_0_1px_0_#0000009e] transition-shadow duration-200 focus-within:shadow-[0_6px_18px_0_#00000014,0_0_1px_0_#0000009e]"
10732
11417
  >
@@ -10814,31 +11499,41 @@ var WebInspectorElement = class WebInspectorElement extends LitElement {
10814
11499
  class="h-3 w-px shrink-0 bg-gray-200"
10815
11500
  aria-hidden="true"
10816
11501
  ></span>
10817
- <span class="truncate" title=${runtimeLabel}>${runtimeLabel}</span>
11502
+ <span class="truncate" title=${runtimeLabel}
11503
+ >${runtimeLabel}</span
11504
+ >
10818
11505
  </div>
10819
11506
  </div>
10820
11507
  <div
10821
11508
  class="cpk-playground-actions ml-auto flex min-w-0 items-center gap-2"
10822
11509
  >
10823
11510
  ${sourceThreads.length > 0 ? html`
10824
- <label class="sr-only" for="cpk-playground-thread-source"
10825
- >Start from a thread</label
10826
- >
10827
- <select
10828
- id="cpk-playground-thread-source"
10829
- class="cpk-playground-thread-select max-w-[200px] rounded-md border border-gray-200 bg-white px-2 py-1 text-[10px] text-gray-700 outline-none transition hover:border-gray-300 focus:border-violet-400 focus:ring-2 focus:ring-violet-100 disabled:cursor-not-allowed disabled:opacity-50"
10830
- .value=${this.playgroundSourceThreadId ?? ""}
10831
- ?disabled=${busy}
10832
- @change=${this.handlePlaygroundThreadSourceChange}
11511
+ <label class="sr-only" for="cpk-playground-thread-source"
11512
+ >Start from a thread</label
11513
+ >
11514
+ <select
11515
+ id="cpk-playground-thread-source"
11516
+ class="cpk-playground-thread-select max-w-[200px] rounded-md border border-gray-200 bg-white px-2 py-1 text-[10px] text-gray-700 outline-none transition hover:border-gray-300 focus:border-violet-400 focus:ring-2 focus:ring-violet-100 disabled:cursor-not-allowed disabled:opacity-50"
11517
+ .value=${this.playgroundSourceThreadId ?? ""}
11518
+ ?disabled=${busy}
11519
+ @change=${this.handlePlaygroundThreadSourceChange}
11520
+ >
11521
+ <option
11522
+ value=""
11523
+ ?selected=${!this.playgroundSourceThreadId}
10833
11524
  >
10834
- <option value="">Load a thread...</option>
10835
- ${sourceThreads.map((thread) => html`
10836
- <option value=${thread.id}>
10837
- ${thread.name?.trim() || `Thread ${thread.id.slice(0, 8)}`}
10838
- </option>
10839
- `)}
10840
- </select>
10841
- ` : nothing}
11525
+ Load a thread...
11526
+ </option>
11527
+ ${sourceThreads.map((thread) => html`
11528
+ <option
11529
+ value=${thread.id}
11530
+ ?selected=${this.playgroundSourceThreadId === thread.id}
11531
+ >
11532
+ ${thread.name?.trim() || `Thread ${thread.id.slice(0, 8)}`}
11533
+ </option>
11534
+ `)}
11535
+ </select>
11536
+ ` : nothing}
10842
11537
  <button
10843
11538
  type="button"
10844
11539
  class="inline-flex shrink-0 items-center gap-1 rounded-md border border-gray-200 bg-white px-2 py-1 text-[10px] font-medium text-gray-700 transition hover:border-gray-300 hover:bg-gray-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-violet-300 focus-visible:ring-offset-1 disabled:cursor-not-allowed disabled:opacity-50 [&>svg]:h-3.5 [&>svg]:w-3.5"
@@ -10851,67 +11546,66 @@ var WebInspectorElement = class WebInspectorElement extends LitElement {
10851
11546
  </header>
10852
11547
 
10853
11548
  ${this.playgroundShowEphemeralNotice && runtimeMode !== "intelligence" ? html`
10854
- <div
10855
- role="alert"
10856
- class="mx-3 mt-2 flex items-start gap-2 rounded-lg border border-violet-200 bg-violet-50 px-2.5 py-2 text-[10px] text-violet-950"
10857
- data-playground-ephemeral-notice
11549
+ <div
11550
+ role="alert"
11551
+ class="mx-3 mt-2 flex items-start gap-2 rounded-lg border border-violet-200 bg-violet-50 px-2.5 py-2 text-[10px] text-violet-950"
11552
+ data-playground-ephemeral-notice
11553
+ >
11554
+ <span class="mt-0.5 text-violet-600 [&>svg]:h-3.5 [&>svg]:w-3.5"
11555
+ >${this.renderIcon("Clock3")}</span
10858
11556
  >
10859
- <span
10860
- class="mt-0.5 text-violet-600 [&>svg]:h-3.5 [&>svg]:w-3.5"
10861
- >${this.renderIcon("Clock3")}</span
10862
- >
10863
- <p class="min-w-0 flex-1 leading-relaxed">
10864
- Scratch threads are ephemeral and will be deleted when your
10865
- local session ends. Need durable history?
10866
- <a
10867
- class="font-semibold underline decoration-violet-300 underline-offset-2 hover:decoration-violet-700 focus-visible:rounded-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-violet-400 focus-visible:ring-offset-1"
10868
- href=${this.getThreadsIntelligenceSignupUrl()}
10869
- target="_blank"
10870
- rel="noopener noreferrer"
10871
- >Set up Intelligence</a
10872
- >.
10873
- </p>
10874
- <button
10875
- type="button"
10876
- class="rounded p-0.5 text-violet-500 transition hover:bg-violet-100 hover:text-violet-800 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-violet-400 focus-visible:ring-offset-1"
10877
- aria-label="Dismiss ephemeral thread notice"
10878
- @click=${() => {
11557
+ <p class="min-w-0 flex-1 leading-relaxed">
11558
+ Scratch threads are ephemeral and will be deleted when your
11559
+ local session ends. Need durable history?
11560
+ <a
11561
+ class="font-semibold underline decoration-violet-300 underline-offset-2 hover:decoration-violet-700 focus-visible:rounded-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-violet-400 focus-visible:ring-offset-1"
11562
+ href=${this.getThreadsIntelligenceSignupUrl()}
11563
+ target="_blank"
11564
+ rel="noopener noreferrer"
11565
+ >Set up Intelligence</a
11566
+ >.
11567
+ </p>
11568
+ <button
11569
+ type="button"
11570
+ class="rounded p-0.5 text-violet-500 transition hover:bg-violet-100 hover:text-violet-800 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-violet-400 focus-visible:ring-offset-1"
11571
+ aria-label="Dismiss ephemeral thread notice"
11572
+ @click=${() => {
10879
11573
  this.playgroundShowEphemeralNotice = false;
10880
11574
  this.requestUpdate();
10881
11575
  }}
10882
- >
10883
- ${this.renderIcon("X")}
10884
- </button>
10885
- </div>
10886
- ` : nothing}
11576
+ >
11577
+ ${this.renderIcon("X")}
11578
+ </button>
11579
+ </div>
11580
+ ` : nothing}
10887
11581
 
10888
11582
  <div
10889
11583
  class="min-h-0 flex-1 overflow-y-auto px-3 py-3"
10890
11584
  data-playground-messages
10891
11585
  >
10892
11586
  ${this.playgroundIsLoadingThread ? html`
11587
+ <div
11588
+ class="flex h-full items-center justify-center gap-1.5 text-[10px] text-gray-600"
11589
+ >
11590
+ <span
11591
+ class="text-gray-500 [&>svg]:animate-spin"
11592
+ aria-hidden="true"
11593
+ >${this.renderIcon("LoaderCircle")}</span
11594
+ >
11595
+ Loading thread into a scratch session...
11596
+ </div>
11597
+ ` : visibleMessages.length === 0 ? html`
10893
11598
  <div
10894
- class="flex h-full items-center justify-center gap-1.5 text-[10px] text-gray-600"
11599
+ class="cpk-playground-welcome mx-auto flex h-full w-full flex-col items-center justify-center text-center"
10895
11600
  >
10896
- <span
10897
- class="text-gray-500 [&>svg]:animate-spin"
10898
- aria-hidden="true"
10899
- >${this.renderIcon("LoaderCircle")}</span
10900
- >
10901
- Loading thread into a scratch session...
11601
+ <p class="cpk-playground-welcome-title">
11602
+ How can I help you today?
11603
+ </p>
11604
+ ${this.renderPlaygroundComposer(agentId, busy, hasRetry, true)}
10902
11605
  </div>
10903
- ` : visibleMessages.length === 0 ? html`
10904
- <div
10905
- class="cpk-playground-welcome mx-auto flex h-full w-full flex-col items-center justify-center text-center"
10906
- >
10907
- <p class="cpk-playground-welcome-title">
10908
- How can I help you today?
10909
- </p>
10910
- ${this.renderPlaygroundComposer(agentId, busy, hasRetry, true)}
10911
- </div>
10912
- ` : html`
10913
- <div class="mx-auto flex max-w-3xl flex-col pb-5">
10914
- ${visibleMessages.map((message, index) => {
11606
+ ` : html`
11607
+ <div class="mx-auto flex max-w-3xl flex-col pb-5">
11608
+ ${visibleMessages.map((message, index) => {
10915
11609
  const isUser = message.role === "user";
10916
11610
  const isReasoning = message.role === "reasoning";
10917
11611
  const isActivity = message.role === "activity";
@@ -10923,152 +11617,156 @@ var WebInspectorElement = class WebInspectorElement extends LitElement {
10923
11617
  const durationLabel = duration === void 0 || duration < 1e3 ? "a few seconds" : `${Math.round(duration / 1e3)} seconds`;
10924
11618
  const label = isStreaming ? "Thinking…" : `Thought for ${durationLabel}`;
10925
11619
  if (isStreaming) return html`
10926
- <section
10927
- class="cpk-playground-message-enter my-1 text-[11px] text-gray-500"
11620
+ <section
11621
+ class="cpk-playground-message-enter my-1 text-[11px] text-gray-500"
11622
+ data-playground-message-role="reasoning"
11623
+ >
11624
+ <div
11625
+ class="inline-flex items-center gap-1 py-1 font-medium"
11626
+ >
11627
+ <span>${label}</span>
11628
+ ${content ? nothing : html`
11629
+ <span
11630
+ class="cpk-playground-thinking-dot ml-1 h-1.5 w-1.5 rounded-full bg-gray-500"
11631
+ aria-hidden="true"
11632
+ ></span>
11633
+ `}
11634
+ </div>
11635
+ ${content ? html`<div
11636
+ class="pb-2 pt-1 leading-5 text-gray-500"
11637
+ >
11638
+ ${content}
11639
+ </div>` : nothing}
11640
+ </section>
11641
+ `;
11642
+ return content ? html`
11643
+ <details
11644
+ class="cpk-playground-message-enter cpk-playground-reasoning my-1 text-[11px] text-gray-500"
10928
11645
  data-playground-message-role="reasoning"
10929
11646
  >
10930
- <div
10931
- class="inline-flex items-center gap-1 py-1 font-medium"
11647
+ <summary
11648
+ class="inline-flex cursor-pointer list-none items-center gap-1 py-1 font-medium transition-colors hover:text-gray-900 focus-visible:rounded focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-violet-300 focus-visible:ring-offset-1"
10932
11649
  >
10933
11650
  <span>${label}</span>
10934
- ${content ? nothing : html`
10935
- <span
10936
- class="cpk-playground-thinking-dot ml-1 h-1.5 w-1.5 rounded-full bg-gray-500"
10937
- aria-hidden="true"
10938
- ></span>
10939
- `}
11651
+ <span
11652
+ class="cpk-playground-reasoning-chevron transition-transform duration-200 [&>svg]:h-3 [&>svg]:w-3"
11653
+ >${this.renderIcon("ChevronRight")}</span
11654
+ >
11655
+ </summary>
11656
+ <div class="pb-2 pt-1 leading-5 text-gray-500">
11657
+ ${content}
10940
11658
  </div>
10941
- ${content ? html`<div
10942
- class="pb-2 pt-1 leading-5 text-gray-500"
10943
- >
10944
- ${content}
10945
- </div>` : nothing}
10946
- </section>
11659
+ </details>
11660
+ ` : html`
11661
+ <div
11662
+ class="cpk-playground-message-enter my-1 py-1 text-[11px] font-medium text-gray-500"
11663
+ data-playground-message-role="reasoning"
11664
+ >
11665
+ ${label}
11666
+ </div>
10947
11667
  `;
10948
- return content ? html`
10949
- <details
10950
- class="cpk-playground-message-enter cpk-playground-reasoning my-1 text-[11px] text-gray-500"
10951
- data-playground-message-role="reasoning"
11668
+ }
11669
+ const isMultiline = content.includes("\n") || content.length > 72;
11670
+ const copyKey = `playground-message-${message.id ?? index}`;
11671
+ const showToolbar = !isUser && !isActivity && Boolean(content) && !(this.playgroundIsRunning && index === lastAssistantIndex);
11672
+ return html`
11673
+ <article
11674
+ class=${isActivity ? "cpk-playground-message-enter mr-auto mt-3 flex max-w-full items-center gap-1.5 rounded-lg border border-gray-200 bg-gray-50 px-2.5 py-1.5 text-[10px] text-gray-600" : isUser ? "cpk-playground-message-enter flex w-full flex-col items-end pt-8" : "cpk-playground-message-enter w-full"}
11675
+ data-playground-message-role=${message.role}
11676
+ >
11677
+ ${isActivity ? html`
11678
+ <span class="text-gray-500"
11679
+ >${this.renderIcon("Activity")}</span
11680
+ >
11681
+ <span class="font-medium text-gray-700"
11682
+ >Activity</span
10952
11683
  >
10953
- <summary
10954
- class="inline-flex cursor-pointer list-none items-center gap-1 py-1 font-medium transition-colors hover:text-gray-900 focus-visible:rounded focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-violet-300 focus-visible:ring-offset-1"
11684
+ <span class="truncate">${content}</span>
11685
+ ` : isUser ? html`
11686
+ <div
11687
+ class=${`max-w-[80%] whitespace-pre-wrap break-words rounded-[16px] bg-gray-100 px-3 text-[13px] leading-5 text-gray-900 ${isMultiline ? "py-2.5" : "py-1"}`}
11688
+ >
11689
+ ${content}
11690
+ </div>
11691
+ ` : html`
11692
+ <div
11693
+ class="whitespace-pre-wrap break-words py-3 text-[13px] leading-[22px] text-gray-800"
10955
11694
  >
10956
- <span>${label}</span>
10957
- <span
10958
- class="cpk-playground-reasoning-chevron transition-transform duration-200 [&>svg]:h-3 [&>svg]:w-3"
10959
- >${this.renderIcon("ChevronRight")}</span
10960
- >
10961
- </summary>
10962
- <div class="pb-2 pt-1 leading-5 text-gray-500">
10963
11695
  ${content}
10964
11696
  </div>
10965
- </details>
10966
- ` : html`
11697
+ `}
11698
+ ${!isUser && message.toolCalls.length > 0 ? this.renderToolCallDetails(message.toolCalls) : nothing}
11699
+ ${showToolbar ? html`
10967
11700
  <div
10968
- class="cpk-playground-message-enter my-1 py-1 text-[11px] font-medium text-gray-500"
10969
- data-playground-message-role="reasoning"
11701
+ class="-ml-1 flex min-h-7 w-full items-center gap-1 bg-transparent"
11702
+ data-playground-assistant-toolbar
10970
11703
  >
10971
- ${label}
11704
+ <button
11705
+ type="button"
11706
+ class="flex h-7 w-7 items-center justify-center rounded-md text-gray-500 transition hover:bg-gray-100 hover:text-gray-900 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-violet-300 focus-visible:ring-offset-1 [&>svg]:h-3.5 [&>svg]:w-3.5"
11707
+ title="Copy message"
11708
+ aria-label="Copy message"
11709
+ @click=${(event) => this.copyToClipboard(content, copyKey, event)}
11710
+ >
11711
+ ${this.copiedEvents.has(copyKey) ? this.renderIcon("Check") : this.renderIcon("Copy")}
11712
+ </button>
11713
+ ${index === lastAssistantIndex && hasRetry && !busy && !this.playgroundError ? html`
11714
+ <button
11715
+ type="button"
11716
+ class="flex h-7 w-7 items-center justify-center rounded-md text-gray-500 transition hover:bg-gray-100 hover:text-gray-900 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-violet-300 focus-visible:ring-offset-1 [&>svg]:h-3.5 [&>svg]:w-3.5"
11717
+ title="Retry last prompt"
11718
+ aria-label="Retry last prompt"
11719
+ @click=${this.handlePlaygroundRetry}
11720
+ >
11721
+ ${this.renderIcon("RotateCcw")}
11722
+ </button>
11723
+ ` : nothing}
10972
11724
  </div>
10973
- `;
10974
- }
10975
- const isMultiline = content.includes("\n") || content.length > 72;
10976
- const copyKey = `playground-message-${message.id ?? index}`;
10977
- const showToolbar = !isUser && !isActivity && Boolean(content) && !(this.playgroundIsRunning && index === lastAssistantIndex);
10978
- return html`
10979
- <article
10980
- class=${isActivity ? "cpk-playground-message-enter mr-auto mt-3 flex max-w-full items-center gap-1.5 rounded-lg border border-gray-200 bg-gray-50 px-2.5 py-1.5 text-[10px] text-gray-600" : isUser ? "cpk-playground-message-enter flex w-full flex-col items-end pt-8" : "cpk-playground-message-enter w-full"}
10981
- data-playground-message-role=${message.role}
10982
- >
10983
- ${isActivity ? html`
10984
- <span class="text-gray-500"
10985
- >${this.renderIcon("Activity")}</span
10986
- >
10987
- <span class="font-medium text-gray-700"
10988
- >Activity</span
10989
- >
10990
- <span class="truncate">${content}</span>
10991
- ` : isUser ? html`
10992
- <div
10993
- class=${`max-w-[80%] whitespace-pre-wrap break-words rounded-[16px] bg-gray-100 px-3 text-[13px] leading-5 text-gray-900 ${isMultiline ? "py-2.5" : "py-1"}`}
10994
- >${content}</div>
10995
- ` : html`
10996
- <div
10997
- class="whitespace-pre-wrap break-words py-3 text-[13px] leading-[22px] text-gray-800"
10998
- >${content}</div>
10999
- `}
11000
- ${!isUser && message.toolCalls.length > 0 ? this.renderToolCallDetails(message.toolCalls) : nothing}
11001
- ${showToolbar ? html`
11002
- <div
11003
- class="-ml-1 flex min-h-7 w-full items-center gap-1 bg-transparent"
11004
- data-playground-assistant-toolbar
11005
- >
11006
- <button
11007
- type="button"
11008
- class="flex h-7 w-7 items-center justify-center rounded-md text-gray-500 transition hover:bg-gray-100 hover:text-gray-900 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-violet-300 focus-visible:ring-offset-1 [&>svg]:h-3.5 [&>svg]:w-3.5"
11009
- title="Copy message"
11010
- aria-label="Copy message"
11011
- @click=${(event) => this.copyToClipboard(content, copyKey, event)}
11012
- >
11013
- ${this.copiedEvents.has(copyKey) ? this.renderIcon("Check") : this.renderIcon("Copy")}
11014
- </button>
11015
- ${index === lastAssistantIndex && hasRetry && !busy && !this.playgroundError ? html`
11016
- <button
11017
- type="button"
11018
- class="flex h-7 w-7 items-center justify-center rounded-md text-gray-500 transition hover:bg-gray-100 hover:text-gray-900 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-violet-300 focus-visible:ring-offset-1 [&>svg]:h-3.5 [&>svg]:w-3.5"
11019
- title="Retry last prompt"
11020
- aria-label="Retry last prompt"
11021
- @click=${this.handlePlaygroundRetry}
11022
- >
11023
- ${this.renderIcon("RotateCcw")}
11024
- </button>
11025
- ` : nothing}
11026
- </div>
11027
- ` : nothing}
11028
- </article>
11029
- `;
11725
+ ` : nothing}
11726
+ </article>
11727
+ `;
11030
11728
  })}
11031
- ${this.playgroundIsRunning && lastReasoningIndex < 0 ? html`
11032
- <div
11033
- class="cpk-playground-message-enter mt-3 flex items-center gap-1 px-1 py-1"
11034
- aria-label="Agent is working"
11035
- >
11036
- <span
11037
- class="cpk-playground-thinking-dot h-1.5 w-1.5 rounded-full bg-gray-500"
11038
- ></span>
11039
- <span
11040
- class="cpk-playground-thinking-dot h-1.5 w-1.5 rounded-full bg-gray-500"
11041
- ></span>
11042
- <span
11043
- class="cpk-playground-thinking-dot h-1.5 w-1.5 rounded-full bg-gray-500"
11044
- ></span>
11045
- </div>
11046
- ` : nothing}
11047
- ${!busy && lastAssistantIndex >= 0 && suggestions.length > 0 ? html`
11048
- <div
11049
- class="mt-3 flex flex-wrap items-center gap-1.5"
11050
- data-playground-suggestions
11051
- >
11052
- ${suggestions.map((suggestion) => html`
11053
- <button
11054
- type="button"
11055
- class="inline-flex h-7 items-center gap-1 rounded-full border border-gray-200 bg-white px-2.5 text-[10px] font-medium leading-none text-gray-900 transition-colors hover:bg-gray-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-violet-300 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:text-gray-500"
11056
- ?disabled=${suggestion.isLoading}
11057
- aria-busy=${suggestion.isLoading ? "true" : "false"}
11058
- @click=${() => this.handlePlaygroundSuggestion(suggestion.message)}
11059
- >
11060
- ${suggestion.isLoading ? html`<span
11061
- class="[&>svg]:animate-spin"
11062
- aria-hidden="true"
11063
- >${this.renderIcon("LoaderCircle")}</span
11064
- >` : nothing}
11065
- <span>${suggestion.title}</span>
11066
- </button>
11067
- `)}
11068
- </div>
11069
- ` : nothing}
11070
- </div>
11071
- `}
11729
+ ${this.playgroundIsRunning && lastReasoningIndex < 0 ? html`
11730
+ <div
11731
+ class="cpk-playground-message-enter mt-3 flex items-center gap-1 px-1 py-1"
11732
+ aria-label="Agent is working"
11733
+ >
11734
+ <span
11735
+ class="cpk-playground-thinking-dot h-1.5 w-1.5 rounded-full bg-gray-500"
11736
+ ></span>
11737
+ <span
11738
+ class="cpk-playground-thinking-dot h-1.5 w-1.5 rounded-full bg-gray-500"
11739
+ ></span>
11740
+ <span
11741
+ class="cpk-playground-thinking-dot h-1.5 w-1.5 rounded-full bg-gray-500"
11742
+ ></span>
11743
+ </div>
11744
+ ` : nothing}
11745
+ ${!busy && lastAssistantIndex >= 0 && suggestions.length > 0 ? html`
11746
+ <div
11747
+ class="mt-3 flex flex-wrap items-center gap-1.5"
11748
+ data-playground-suggestions
11749
+ >
11750
+ ${suggestions.map((suggestion) => html`
11751
+ <button
11752
+ type="button"
11753
+ class="inline-flex h-7 items-center gap-1 rounded-full border border-gray-200 bg-white px-2.5 text-[10px] font-medium leading-none text-gray-900 transition-colors hover:bg-gray-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-violet-300 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:text-gray-500"
11754
+ ?disabled=${suggestion.isLoading}
11755
+ aria-busy=${suggestion.isLoading ? "true" : "false"}
11756
+ @click=${() => this.handlePlaygroundSuggestion(suggestion.message)}
11757
+ >
11758
+ ${suggestion.isLoading ? html`<span
11759
+ class="[&>svg]:animate-spin"
11760
+ aria-hidden="true"
11761
+ >${this.renderIcon("LoaderCircle")}</span
11762
+ >` : nothing}
11763
+ <span>${suggestion.title}</span>
11764
+ </button>
11765
+ `)}
11766
+ </div>
11767
+ ` : nothing}
11768
+ </div>
11769
+ `}
11072
11770
  </div>
11073
11771
 
11074
11772
  ${showWelcome ? nothing : this.renderPlaygroundComposer(agentId, busy, hasRetry)}
@@ -11455,6 +12153,7 @@ var WebInspectorElement = class WebInspectorElement extends LitElement {
11455
12153
  this.requestUpdate();
11456
12154
  return;
11457
12155
  }
12156
+ this.tryFromHereError = null;
11458
12157
  this.selectedThreadId = threadId;
11459
12158
  if (showingExamples && this.isExampleThreadId(threadId)) {
11460
12159
  this.selectedRealThreadIsExplicit = false;
@@ -11718,6 +12417,7 @@ var WebInspectorElement = class WebInspectorElement extends LitElement {
11718
12417
  <p class="cpk-threads-overview-copy">
11719
12418
  ${lockedCopy?.description ?? "Take a tour with the example threads in the sidebar. Then, start chatting in your app to create the first real thread."}
11720
12419
  </p>
12420
+ ${locked ? this.renderRuntimeEntitlementDiagnostic(this.getRuntimeEntitlementDiagnostic()) : nothing}
11721
12421
  <div class="cpk-threads-overview-actions">
11722
12422
  ${locked ? html`
11723
12423
  ${this.inspectorMetadataProjection.licenseState === "valid" ? html`
@@ -11896,6 +12596,74 @@ var WebInspectorElement = class WebInspectorElement extends LitElement {
11896
12596
  }
11897
12597
  }
11898
12598
  /**
12599
+ * Prefer the Runtime's structured entitlement diagnostic and derive a
12600
+ * compatibility diagnostic only when an older Core exposes legacy status.
12601
+ */
12602
+ getRuntimeEntitlementDiagnostic() {
12603
+ const runtimeEntitlements = this.core?.runtimeEntitlements;
12604
+ if (runtimeEntitlements) return runtimeEntitlements.status === "ready" ? { status: runtimeEntitlements.status } : {
12605
+ status: runtimeEntitlements.status,
12606
+ error: runtimeEntitlements.error
12607
+ };
12608
+ switch (this.core?.licenseStatus) {
12609
+ case "valid": return { status: "ready" };
12610
+ case "expired": return {
12611
+ status: "degraded",
12612
+ error: {
12613
+ code: "legacy_license_expired",
12614
+ message: "Legacy Runtime license has expired.",
12615
+ retryable: false
12616
+ }
12617
+ };
12618
+ case "expiring": return {
12619
+ status: "degraded",
12620
+ error: {
12621
+ code: "legacy_license_expiring",
12622
+ message: "Legacy Runtime license is expiring.",
12623
+ retryable: false
12624
+ }
12625
+ };
12626
+ case "invalid": return {
12627
+ status: "misconfigured",
12628
+ error: {
12629
+ code: "legacy_license_invalid",
12630
+ message: "Legacy Runtime license is invalid.",
12631
+ retryable: false
12632
+ }
12633
+ };
12634
+ case "none":
12635
+ case "unknown": return { status: "unavailable" };
12636
+ default: return null;
12637
+ }
12638
+ }
12639
+ /** Render exact Runtime entitlement correlation details without gating UI. */
12640
+ renderRuntimeEntitlementDiagnostic(diagnostic) {
12641
+ if (!diagnostic) return nothing;
12642
+ return html`
12643
+ <div
12644
+ role="status"
12645
+ data-runtime-entitlement-status=${diagnostic.status}
12646
+ style="
12647
+ margin: -8px auto 18px;
12648
+ max-width: 380px;
12649
+ font-size: 11px;
12650
+ line-height: 1.5;
12651
+ color: #57575b;
12652
+ "
12653
+ >
12654
+ <div style="font-weight: 600;">
12655
+ Runtime entitlement: ${diagnostic.status}
12656
+ </div>
12657
+ ${diagnostic.error ? html`
12658
+ <div>${diagnostic.error.message}</div>
12659
+ <div>Code: ${diagnostic.error.code}</div>
12660
+ ${diagnostic.error.requestId ? html`<div>Request ID: ${diagnostic.error.requestId}</div>` : nothing}
12661
+ ${diagnostic.error.traceId ? html`<div>Trace ID: ${diagnostic.error.traceId}</div>` : nothing}
12662
+ ` : nothing}
12663
+ </div>
12664
+ `;
12665
+ }
12666
+ /**
11899
12667
  * Renders the realtime-connection indicator in the memory-store header.
11900
12668
  * Only `"connected"` shows the live (green-dot) state; `"connecting"` shows a
11901
12669
  * muted amber "reconnecting" and `"unavailable"` a muted grey "offline", so
@@ -12280,6 +13048,12 @@ var WebInspectorElement = class WebInspectorElement extends LitElement {
12280
13048
  .agentStateInput=${this.getLatestStateForAgent(selectedThread.agentId)}
12281
13049
  .agentEventsInput=${this.agentEvents.get(selectedThread.agentId) ?? []}
12282
13050
  .agentMessagesInput=${selectedThreadIsLocalExample ? EMPTY_INSPECTOR_MESSAGES : this.getLiveAgentMessagesForThread(selectedThread)}
13051
+ .tryFromHereAvailable=${!selectedThreadIsLocalExample && this.areThreadEndpointsAvailable() && this._core?.threadEndpoints?.inspect !== false}
13052
+ .tryFromHereBusy=${this.tryFromHereBusy}
13053
+ .tryFromHereError=${this.tryFromHereError}
13054
+ @tryFromHere=${(event) => {
13055
+ this.handleTryFromHere(event.detail);
13056
+ }}
12283
13057
  ></cpk-thread-details>
12284
13058
  ${selectedThreadIsLocalExample ? this.renderThreadsExampleTour() : nothing}` : showingExamples ? this.renderThreadsExampleOverview(locked) : html`
12285
13059
  <div
@@ -12781,9 +13555,10 @@ var WebInspectorElement = class WebInspectorElement extends LitElement {
12781
13555
  </span>
12782
13556
  </div>
12783
13557
  <div class="flex-1 px-4 py-2">
12784
- ${hasContent ? html`<div
12785
- class="whitespace-pre-wrap break-words text-gray-700"
12786
- >${rawContent}</div>` : html`<div class="italic text-gray-400">
13558
+ ${hasContent ? html`
13559
+ <!-- prettier-ignore -->
13560
+ <div class="whitespace-pre-wrap break-words text-gray-700">${rawContent}</div>
13561
+ ` : html`<div class="italic text-gray-400">
12787
13562
  ${contentFallback}
12788
13563
  </div>`}
12789
13564
  ${role === "assistant" && toolCalls.length > 0 ? this.renderToolCallDetails(toolCalls) : nothing}