pi-studio 0.5.3 → 0.5.4

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/CHANGELOG.md CHANGED
@@ -88,6 +88,18 @@ All notable changes to `pi-studio` are documented here.
88
88
 
89
89
  ## [Unreleased]
90
90
 
91
+ ## [0.5.4] — 2026-03-09
92
+
93
+ ### Added
94
+ - New right-pane **Thinking (Raw)** view for assistant/model thinking when available.
95
+
96
+ ### Changed
97
+ - Response history and latest-response syncing now preserve associated thinking content.
98
+ - In Thinking view, right-pane actions adapt to the selected reasoning trace:
99
+ - **Load thinking into editor**
100
+ - **Copy thinking text**
101
+ - thinking-aware reference/sync badges
102
+
91
103
  ## [0.5.3] — 2026-03-06
92
104
 
93
105
  ### Added
package/README.md CHANGED
@@ -14,11 +14,12 @@ Experimental extension for [pi](https://github.com/badlogic/pi-mono) that opens
14
14
 
15
15
  ## What it does
16
16
 
17
- - Opens a two-pane browser workspace: **Editor** (left) + **Response/Editor Preview** (right)
17
+ - Opens a two-pane browser workspace: **Editor** (left) + **Response/Thinking/Editor Preview** (right)
18
18
  - Runs editor text directly, or asks for structured critique (auto/writing/code focus)
19
19
  - Browses response history (`Prev/Next`) and loads either:
20
20
  - response text
21
21
  - critique notes/full critique
22
+ - assistant thinking (when available)
22
23
  - the prompt that generated a selected response
23
24
  - Supports an annotation workflow for `[an: ...]` markers:
24
25
  - inserts/removes the annotated-reply header
package/index.ts CHANGED
@@ -32,6 +32,7 @@ interface ActiveStudioRequest {
32
32
 
33
33
  interface LastStudioResponse {
34
34
  markdown: string;
35
+ thinking: string | null;
35
36
  timestamp: number;
36
37
  kind: StudioRequestKind;
37
38
  }
@@ -39,6 +40,7 @@ interface LastStudioResponse {
39
40
  interface StudioResponseHistoryItem {
40
41
  id: string;
41
42
  markdown: string;
43
+ thinking: string | null;
42
44
  timestamp: number;
43
45
  kind: StudioRequestKind;
44
46
  prompt: string | null;
@@ -1259,6 +1261,27 @@ function extractAssistantText(message: unknown): string | null {
1259
1261
  return text.length > 0 ? text : null;
1260
1262
  }
1261
1263
 
1264
+ function extractAssistantThinking(message: unknown): string | null {
1265
+ const msg = message as {
1266
+ role?: string;
1267
+ content?: Array<{ type?: string; thinking?: string }> | string;
1268
+ };
1269
+
1270
+ if (!msg || msg.role !== "assistant" || !Array.isArray(msg.content)) return null;
1271
+
1272
+ const blocks: string[] = [];
1273
+ for (const part of msg.content) {
1274
+ if (!part || typeof part !== "object") continue;
1275
+ if (part.type !== "thinking") continue;
1276
+ if (typeof part.thinking === "string" && part.thinking.trim()) {
1277
+ blocks.push(part.thinking);
1278
+ }
1279
+ }
1280
+
1281
+ const thinking = blocks.join("\n\n").trim();
1282
+ return thinking.length > 0 ? thinking : null;
1283
+ }
1284
+
1262
1285
  function extractLatestAssistantFromEntries(entries: SessionEntry[]): string | null {
1263
1286
  for (let i = entries.length - 1; i >= 0; i--) {
1264
1287
  const entry = entries[i];
@@ -1330,9 +1353,11 @@ function buildResponseHistoryFromEntries(entries: SessionEntry[], limit = RESPON
1330
1353
  if (role !== "assistant") continue;
1331
1354
  const markdown = extractAssistantText(message);
1332
1355
  if (!markdown) continue;
1356
+ const thinking = extractAssistantThinking(message);
1333
1357
  history.push({
1334
1358
  id: typeof (entry as { id?: unknown }).id === "string" ? (entry as { id: string }).id : randomUUID(),
1335
1359
  markdown,
1360
+ thinking,
1336
1361
  timestamp: parseEntryTimestamp((entry as { timestamp?: unknown }).timestamp),
1337
1362
  kind: inferStudioResponseKind(markdown),
1338
1363
  prompt: lastUserPrompt,
@@ -2889,6 +2914,7 @@ ${cssVarsBlock}
2889
2914
  <option value="markdown">Response (Raw)</option>
2890
2915
  <option value="preview" selected>Response (Preview)</option>
2891
2916
  <option value="editor-preview">Editor (Preview)</option>
2917
+ <option value="thinking">Thinking (Raw)</option>
2892
2918
  </select>
2893
2919
  </div>
2894
2920
  <div class="section-header-actions">
@@ -3043,11 +3069,13 @@ ${cssVarsBlock}
3043
3069
  let followLatest = true;
3044
3070
  let queuedLatestResponse = null;
3045
3071
  let latestResponseMarkdown = "";
3072
+ let latestResponseThinking = "";
3046
3073
  let latestResponseTimestamp = 0;
3047
3074
  let latestResponseKind = "annotation";
3048
3075
  let latestResponseIsStructuredCritique = false;
3049
3076
  let latestResponseHasContent = false;
3050
3077
  let latestResponseNormalized = "";
3078
+ let latestResponseThinkingNormalized = "";
3051
3079
  let latestCritiqueNotes = "";
3052
3080
  let latestCritiqueNotesNormalized = "";
3053
3081
  let responseHistory = [];
@@ -3672,10 +3700,14 @@ ${cssVarsBlock}
3672
3700
  const prompt = typeof item.prompt === "string"
3673
3701
  ? item.prompt
3674
3702
  : (item.prompt == null ? null : String(item.prompt));
3703
+ const thinking = typeof item.thinking === "string"
3704
+ ? item.thinking
3705
+ : (item.thinking == null ? null : String(item.thinking));
3675
3706
 
3676
3707
  return {
3677
3708
  id,
3678
3709
  markdown,
3710
+ thinking,
3679
3711
  timestamp,
3680
3712
  kind: normalizeHistoryKind(item.kind),
3681
3713
  prompt,
@@ -3690,11 +3722,13 @@ ${cssVarsBlock}
3690
3722
 
3691
3723
  function clearActiveResponseView() {
3692
3724
  latestResponseMarkdown = "";
3725
+ latestResponseThinking = "";
3693
3726
  latestResponseKind = "annotation";
3694
3727
  latestResponseTimestamp = 0;
3695
3728
  latestResponseIsStructuredCritique = false;
3696
3729
  latestResponseHasContent = false;
3697
3730
  latestResponseNormalized = "";
3731
+ latestResponseThinkingNormalized = "";
3698
3732
  latestCritiqueNotes = "";
3699
3733
  latestCritiqueNotesNormalized = "";
3700
3734
  refreshResponseUi();
@@ -3731,7 +3765,7 @@ ${cssVarsBlock}
3731
3765
  clearActiveResponseView();
3732
3766
  return false;
3733
3767
  }
3734
- handleIncomingResponse(item.markdown, item.kind, item.timestamp);
3768
+ handleIncomingResponse(item.markdown, item.kind, item.timestamp, item.thinking);
3735
3769
  return true;
3736
3770
  }
3737
3771
 
@@ -3814,6 +3848,26 @@ ${cssVarsBlock}
3814
3848
  }
3815
3849
 
3816
3850
  const hasResponse = Boolean(latestResponseMarkdown && latestResponseMarkdown.trim());
3851
+ const hasThinking = Boolean(latestResponseThinking && latestResponseThinking.trim());
3852
+ if (rightView === "thinking") {
3853
+ if (!hasResponse && !hasThinking) {
3854
+ referenceBadgeEl.textContent = "Thinking: none";
3855
+ return;
3856
+ }
3857
+
3858
+ const time = formatReferenceTime(latestResponseTimestamp);
3859
+ const total = Array.isArray(responseHistory) ? responseHistory.length : 0;
3860
+ const selected = total > 0 && responseHistoryIndex >= 0 && responseHistoryIndex < total
3861
+ ? responseHistoryIndex + 1
3862
+ : 0;
3863
+ const historyPrefix = total > 0 ? "Response history " + selected + "/" + total + " · " : "";
3864
+ const thinkingLabel = hasThinking ? "assistant thinking" : "assistant thinking unavailable";
3865
+ referenceBadgeEl.textContent = time
3866
+ ? historyPrefix + thinkingLabel + " · " + time
3867
+ : historyPrefix + thinkingLabel;
3868
+ return;
3869
+ }
3870
+
3817
3871
  if (!hasResponse) {
3818
3872
  referenceBadgeEl.textContent = "Latest response: none";
3819
3873
  return;
@@ -3864,8 +3918,13 @@ ${cssVarsBlock}
3864
3918
  function updateSyncBadge(normalizedEditorText) {
3865
3919
  if (!syncBadgeEl) return;
3866
3920
 
3867
- if (!latestResponseHasContent) {
3868
- syncBadgeEl.textContent = "No response loaded";
3921
+ const showingThinking = rightView === "thinking";
3922
+ const hasComparableContent = showingThinking
3923
+ ? Boolean(latestResponseThinking && latestResponseThinking.trim())
3924
+ : latestResponseHasContent;
3925
+
3926
+ if (!hasComparableContent) {
3927
+ syncBadgeEl.textContent = showingThinking ? "No thinking loaded" : "No response loaded";
3869
3928
  syncBadgeEl.classList.remove("sync", "edited");
3870
3929
  return;
3871
3930
  }
@@ -3873,13 +3932,14 @@ ${cssVarsBlock}
3873
3932
  const normalizedEditor = typeof normalizedEditorText === "string"
3874
3933
  ? normalizedEditorText
3875
3934
  : normalizeForCompare(sourceTextEl.value);
3876
- const inSync = normalizedEditor === latestResponseNormalized;
3935
+ const targetNormalized = showingThinking ? latestResponseThinkingNormalized : latestResponseNormalized;
3936
+ const inSync = normalizedEditor === targetNormalized;
3877
3937
  if (inSync) {
3878
- syncBadgeEl.textContent = "In sync with response";
3938
+ syncBadgeEl.textContent = showingThinking ? "In sync with thinking" : "In sync with response";
3879
3939
  syncBadgeEl.classList.add("sync");
3880
3940
  syncBadgeEl.classList.remove("edited");
3881
3941
  } else {
3882
- syncBadgeEl.textContent = "Out of sync with response";
3942
+ syncBadgeEl.textContent = showingThinking ? "Out of sync with thinking" : "Out of sync with response";
3883
3943
  syncBadgeEl.classList.add("edited");
3884
3944
  syncBadgeEl.classList.remove("sync");
3885
3945
  }
@@ -4411,6 +4471,15 @@ ${cssVarsBlock}
4411
4471
  return;
4412
4472
  }
4413
4473
 
4474
+ if (rightView === "thinking") {
4475
+ const thinking = latestResponseThinking;
4476
+ finishPreviewRender(critiqueViewEl);
4477
+ critiqueViewEl.innerHTML = thinking && thinking.trim()
4478
+ ? buildPlainMarkdownHtml(thinking)
4479
+ : "<pre class='plain-markdown'>No thinking available for this response.</pre>";
4480
+ return;
4481
+ }
4482
+
4414
4483
  const markdown = latestResponseMarkdown;
4415
4484
  if (!markdown || !markdown.trim()) {
4416
4485
  finishPreviewRender(critiqueViewEl);
@@ -4446,36 +4515,56 @@ ${cssVarsBlock}
4446
4515
 
4447
4516
  function updateResultActionButtons(normalizedEditorText) {
4448
4517
  const hasResponse = latestResponseHasContent;
4518
+ const hasThinking = Boolean(latestResponseThinking && latestResponseThinking.trim());
4449
4519
  const normalizedEditor = typeof normalizedEditorText === "string"
4450
4520
  ? normalizedEditorText
4451
4521
  : normalizeForCompare(sourceTextEl.value);
4452
4522
  const responseLoaded = hasResponse && normalizedEditor === latestResponseNormalized;
4523
+ const thinkingLoaded = hasThinking && normalizedEditor === latestResponseThinkingNormalized;
4453
4524
  const isCritiqueResponse = hasResponse && latestResponseIsStructuredCritique;
4525
+ const showingThinking = rightView === "thinking";
4454
4526
 
4455
4527
  const critiqueNotes = isCritiqueResponse ? latestCritiqueNotes : "";
4456
4528
  const critiqueNotesLoaded = Boolean(critiqueNotes) && normalizedEditor === latestCritiqueNotesNormalized;
4457
4529
 
4458
- loadResponseBtn.hidden = isCritiqueResponse;
4459
- loadCritiqueNotesBtn.hidden = !isCritiqueResponse;
4460
- loadCritiqueFullBtn.hidden = !isCritiqueResponse;
4530
+ if (showingThinking) {
4531
+ loadResponseBtn.hidden = false;
4532
+ loadCritiqueNotesBtn.hidden = true;
4533
+ loadCritiqueFullBtn.hidden = true;
4534
+
4535
+ loadResponseBtn.disabled = uiBusy || !hasThinking || thinkingLoaded;
4536
+ loadResponseBtn.textContent = !hasThinking
4537
+ ? "Thinking unavailable"
4538
+ : (thinkingLoaded ? "Thinking already in editor" : "Load thinking into editor");
4461
4539
 
4462
- loadResponseBtn.disabled = uiBusy || !hasResponse || responseLoaded || isCritiqueResponse;
4463
- loadResponseBtn.textContent = responseLoaded ? "Response already in editor" : "Load response into editor";
4540
+ copyResponseBtn.disabled = uiBusy || !hasThinking;
4541
+ copyResponseBtn.textContent = "Copy thinking text";
4542
+ } else {
4543
+ loadResponseBtn.hidden = isCritiqueResponse;
4544
+ loadCritiqueNotesBtn.hidden = !isCritiqueResponse;
4545
+ loadCritiqueFullBtn.hidden = !isCritiqueResponse;
4464
4546
 
4465
- loadCritiqueNotesBtn.disabled = uiBusy || !isCritiqueResponse || !critiqueNotes || critiqueNotesLoaded;
4466
- loadCritiqueNotesBtn.textContent = critiqueNotesLoaded ? "Critique notes already in editor" : "Load critique notes into editor";
4547
+ loadResponseBtn.disabled = uiBusy || !hasResponse || responseLoaded || isCritiqueResponse;
4548
+ loadResponseBtn.textContent = responseLoaded ? "Response already in editor" : "Load response into editor";
4467
4549
 
4468
- loadCritiqueFullBtn.disabled = uiBusy || !isCritiqueResponse || responseLoaded;
4469
- loadCritiqueFullBtn.textContent = responseLoaded ? "Full critique already in editor" : "Load full critique into editor";
4550
+ loadCritiqueNotesBtn.disabled = uiBusy || !isCritiqueResponse || !critiqueNotes || critiqueNotesLoaded;
4551
+ loadCritiqueNotesBtn.textContent = critiqueNotesLoaded ? "Critique notes already in editor" : "Load critique notes into editor";
4470
4552
 
4471
- copyResponseBtn.disabled = uiBusy || !hasResponse;
4553
+ loadCritiqueFullBtn.disabled = uiBusy || !isCritiqueResponse || responseLoaded;
4554
+ loadCritiqueFullBtn.textContent = responseLoaded ? "Full critique already in editor" : "Load full critique into editor";
4555
+
4556
+ copyResponseBtn.disabled = uiBusy || !hasResponse;
4557
+ copyResponseBtn.textContent = "Copy response text";
4558
+ }
4472
4559
 
4473
4560
  const rightPaneShowsPreview = rightView === "preview" || rightView === "editor-preview";
4474
4561
  const exportText = rightView === "editor-preview" ? prepareEditorTextForPreview(sourceTextEl.value) : latestResponseMarkdown;
4475
4562
  const canExportPdf = rightPaneShowsPreview && Boolean(String(exportText || "").trim());
4476
4563
  if (exportPdfBtn) {
4477
4564
  exportPdfBtn.disabled = uiBusy || pdfExportInProgress || !canExportPdf;
4478
- if (rightView === "markdown") {
4565
+ if (rightView === "thinking") {
4566
+ exportPdfBtn.title = "Thinking view does not support PDF export yet.";
4567
+ } else if (rightView === "markdown") {
4479
4568
  exportPdfBtn.title = "Switch right pane to Response (Preview) or Editor (Preview) to export PDF.";
4480
4569
  } else if (!canExportPdf) {
4481
4570
  exportPdfBtn.title = "Nothing to export yet.";
@@ -4653,7 +4742,11 @@ ${cssVarsBlock}
4653
4742
  }
4654
4743
 
4655
4744
  function setRightView(nextView) {
4656
- rightView = nextView === "preview" ? "preview" : (nextView === "editor-preview" ? "editor-preview" : "markdown");
4745
+ rightView = nextView === "preview"
4746
+ ? "preview"
4747
+ : (nextView === "editor-preview"
4748
+ ? "editor-preview"
4749
+ : (nextView === "thinking" ? "thinking" : "markdown"));
4657
4750
  rightViewSelect.value = rightView;
4658
4751
 
4659
4752
  if (rightView !== "editor-preview" && responseEditorPreviewTimer) {
@@ -5330,18 +5423,20 @@ ${cssVarsBlock}
5330
5423
  return lower.indexOf("## critiques") !== -1 && lower.indexOf("## document") !== -1;
5331
5424
  }
5332
5425
 
5333
- function handleIncomingResponse(markdown, kind, timestamp) {
5426
+ function handleIncomingResponse(markdown, kind, timestamp, thinking) {
5334
5427
  const responseTimestamp =
5335
5428
  typeof timestamp === "number" && Number.isFinite(timestamp) && timestamp > 0
5336
5429
  ? timestamp
5337
5430
  : Date.now();
5338
5431
 
5339
5432
  latestResponseMarkdown = markdown;
5433
+ latestResponseThinking = typeof thinking === "string" ? thinking : "";
5340
5434
  latestResponseKind = kind === "critique" ? "critique" : "annotation";
5341
5435
  latestResponseTimestamp = responseTimestamp;
5342
5436
  latestResponseIsStructuredCritique = isStructuredCritique(markdown);
5343
5437
  latestResponseHasContent = Boolean(markdown && markdown.trim());
5344
5438
  latestResponseNormalized = normalizeForCompare(markdown);
5439
+ latestResponseThinkingNormalized = normalizeForCompare(latestResponseThinking);
5345
5440
 
5346
5441
  if (latestResponseIsStructuredCritique) {
5347
5442
  latestCritiqueNotes = buildCritiqueNotesMarkdown(markdown);
@@ -5357,7 +5452,7 @@ ${cssVarsBlock}
5357
5452
  function applyLatestPayload(payload) {
5358
5453
  if (!payload || typeof payload.markdown !== "string") return false;
5359
5454
  const responseKind = payload.kind === "critique" ? "critique" : "annotation";
5360
- handleIncomingResponse(payload.markdown, responseKind, payload.timestamp);
5455
+ handleIncomingResponse(payload.markdown, responseKind, payload.timestamp, payload.thinking);
5361
5456
  return true;
5362
5457
  }
5363
5458
 
@@ -5456,7 +5551,7 @@ ${cssVarsBlock}
5456
5551
  message.lastResponse.kind === "critique"
5457
5552
  ? "critique"
5458
5553
  : (isStructuredCritique(lastMarkdown) ? "critique" : "annotation");
5459
- handleIncomingResponse(lastMarkdown, lastResponseKind, message.lastResponse.timestamp);
5554
+ handleIncomingResponse(lastMarkdown, lastResponseKind, message.lastResponse.timestamp, message.lastResponse.thinking);
5460
5555
  }
5461
5556
 
5462
5557
  if (pendingRequestId) {
@@ -5553,7 +5648,7 @@ ${cssVarsBlock}
5553
5648
  }
5554
5649
 
5555
5650
  if (!appliedFromHistory && typeof message.markdown === "string") {
5556
- handleIncomingResponse(message.markdown, responseKind, message.timestamp);
5651
+ handleIncomingResponse(message.markdown, responseKind, message.timestamp, message.thinking);
5557
5652
  }
5558
5653
 
5559
5654
  if (responseKind === "critique") {
@@ -5582,6 +5677,7 @@ ${cssVarsBlock}
5582
5677
  const payload = {
5583
5678
  kind: message.kind === "critique" ? "critique" : "annotation",
5584
5679
  markdown: message.markdown,
5680
+ thinking: typeof message.thinking === "string" ? message.thinking : null,
5585
5681
  timestamp: message.timestamp,
5586
5682
  };
5587
5683
 
@@ -6172,6 +6268,17 @@ ${cssVarsBlock}
6172
6268
  });
6173
6269
 
6174
6270
  loadResponseBtn.addEventListener("click", () => {
6271
+ if (rightView === "thinking") {
6272
+ if (!latestResponseThinking.trim()) {
6273
+ setStatus("No thinking available for the selected response.", "warning");
6274
+ return;
6275
+ }
6276
+ setEditorText(latestResponseThinking, { preserveScroll: false, preserveSelection: false });
6277
+ setSourceState({ source: "blank", label: "assistant thinking", path: null });
6278
+ setStatus("Loaded thinking into editor.", "success");
6279
+ return;
6280
+ }
6281
+
6175
6282
  if (!latestResponseMarkdown.trim()) {
6176
6283
  setStatus("No response available yet.", "warning");
6177
6284
  return;
@@ -6210,14 +6317,15 @@ ${cssVarsBlock}
6210
6317
  });
6211
6318
 
6212
6319
  copyResponseBtn.addEventListener("click", async () => {
6213
- if (!latestResponseMarkdown.trim()) {
6214
- setStatus("No response available yet.", "warning");
6320
+ const content = rightView === "thinking" ? latestResponseThinking : latestResponseMarkdown;
6321
+ if (!content.trim()) {
6322
+ setStatus(rightView === "thinking" ? "No thinking available yet." : "No response available yet.", "warning");
6215
6323
  return;
6216
6324
  }
6217
6325
 
6218
6326
  try {
6219
- await navigator.clipboard.writeText(latestResponseMarkdown);
6220
- setStatus("Copied response text.", "success");
6327
+ await navigator.clipboard.writeText(content);
6328
+ setStatus(rightView === "thinking" ? "Copied thinking text." : "Copied response text.", "success");
6221
6329
  } catch (error) {
6222
6330
  setStatus("Clipboard write failed.", "warning");
6223
6331
  }
@@ -6635,6 +6743,7 @@ export default function (pi: ExtensionAPI) {
6635
6743
  }
6636
6744
  lastStudioResponse = {
6637
6745
  markdown: latest.markdown,
6746
+ thinking: latest.thinking,
6638
6747
  timestamp: latest.timestamp,
6639
6748
  kind: latest.kind,
6640
6749
  };
@@ -6897,6 +7006,7 @@ export default function (pi: ExtensionAPI) {
6897
7006
  type: "latest_response",
6898
7007
  kind: lastStudioResponse.kind,
6899
7008
  markdown: lastStudioResponse.markdown,
7009
+ thinking: lastStudioResponse.thinking,
6900
7010
  timestamp: lastStudioResponse.timestamp,
6901
7011
  responseHistory: studioResponseHistory,
6902
7012
  });
@@ -7711,11 +7821,14 @@ export default function (pi: ExtensionAPI) {
7711
7821
  const stopReason = typeof message.stopReason === "string" ? message.stopReason : "";
7712
7822
  const role = typeof message.role === "string" ? message.role : "";
7713
7823
  const markdown = extractAssistantText(event.message);
7824
+ const thinking = extractAssistantThinking(event.message);
7714
7825
  emitDebugEvent("message_end", {
7715
7826
  role,
7716
7827
  stopReason,
7717
7828
  hasMarkdown: Boolean(markdown),
7718
7829
  markdownLength: markdown ? markdown.length : 0,
7830
+ hasThinking: Boolean(thinking),
7831
+ thinkingLength: thinking ? thinking.length : 0,
7719
7832
  activeRequestId: activeRequest?.id ?? null,
7720
7833
  activeRequestKind: activeRequest?.kind ?? null,
7721
7834
  });
@@ -7742,6 +7855,7 @@ export default function (pi: ExtensionAPI) {
7742
7855
  const fallbackHistoryItem: StudioResponseHistoryItem = {
7743
7856
  id: randomUUID(),
7744
7857
  markdown,
7858
+ thinking,
7745
7859
  timestamp: Date.now(),
7746
7860
  kind: inferStudioResponseKind(markdown),
7747
7861
  prompt: fallbackPrompt,
@@ -7752,12 +7866,14 @@ export default function (pi: ExtensionAPI) {
7752
7866
 
7753
7867
  const latestItem = studioResponseHistory[studioResponseHistory.length - 1];
7754
7868
  const responseTimestamp = latestItem?.timestamp ?? Date.now();
7869
+ const responseThinking = latestItem?.thinking ?? thinking ?? null;
7755
7870
 
7756
7871
  if (activeRequest) {
7757
7872
  const requestId = activeRequest.id;
7758
7873
  const kind = activeRequest.kind;
7759
7874
  lastStudioResponse = {
7760
7875
  markdown,
7876
+ thinking: responseThinking,
7761
7877
  timestamp: responseTimestamp,
7762
7878
  kind,
7763
7879
  };
@@ -7765,6 +7881,7 @@ export default function (pi: ExtensionAPI) {
7765
7881
  requestId,
7766
7882
  kind,
7767
7883
  markdownLength: markdown.length,
7884
+ thinkingLength: responseThinking ? responseThinking.length : 0,
7768
7885
  stopReason,
7769
7886
  });
7770
7887
  broadcast({
@@ -7772,6 +7889,7 @@ export default function (pi: ExtensionAPI) {
7772
7889
  requestId,
7773
7890
  kind,
7774
7891
  markdown,
7892
+ thinking: lastStudioResponse.thinking,
7775
7893
  timestamp: lastStudioResponse.timestamp,
7776
7894
  responseHistory: studioResponseHistory,
7777
7895
  });
@@ -7783,18 +7901,21 @@ export default function (pi: ExtensionAPI) {
7783
7901
  const inferredKind = inferStudioResponseKind(markdown);
7784
7902
  lastStudioResponse = {
7785
7903
  markdown,
7904
+ thinking: responseThinking,
7786
7905
  timestamp: responseTimestamp,
7787
7906
  kind: inferredKind,
7788
7907
  };
7789
7908
  emitDebugEvent("broadcast_latest_response", {
7790
7909
  kind: inferredKind,
7791
7910
  markdownLength: markdown.length,
7911
+ thinkingLength: responseThinking ? responseThinking.length : 0,
7792
7912
  stopReason,
7793
7913
  });
7794
7914
  broadcast({
7795
7915
  type: "latest_response",
7796
7916
  kind: inferredKind,
7797
7917
  markdown,
7918
+ thinking: lastStudioResponse.thinking,
7798
7919
  timestamp: lastStudioResponse.timestamp,
7799
7920
  responseHistory: studioResponseHistory,
7800
7921
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-studio",
3
- "version": "0.5.3",
3
+ "version": "0.5.4",
4
4
  "description": "Browser GUI for structured critique workflows in pi",
5
5
  "type": "module",
6
6
  "license": "MIT",