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