pi-studio 0.5.27 → 0.5.29
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 +11 -0
- package/README.md +3 -1
- package/client/studio-client.js +194 -33
- package/client/studio.css +3 -0
- package/index.ts +381 -36
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,17 @@ All notable changes to `pi-studio` are documented here.
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [0.5.29] — 2026-03-21
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
- Studio keyboard shortcuts now keep `Cmd/Ctrl+Enter` for running editor text while using `Esc` to stop an active request, and the focus-pane hint/button copy now describes focus mode as a toggle via `F10` or `Cmd/Ctrl+Esc`.
|
|
11
|
+
- While **Run editor text** is active, Studio now exposes a separate **Queue steering** action (and `Cmd/Ctrl+Enter` queues steering) while preserving a visible **Stop** control, and response-history prompt loading now preserves the effective prompt chain for steered responses rather than only the last correction message.
|
|
12
|
+
|
|
13
|
+
## [0.5.28] — 2026-03-21
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
- Refreshed the Studio package description and README opening/docs so they describe Studio more accurately as a two-pane browser workspace for prompt/response editing, annotations, history, live preview, and related workflows, and documented `/studio-current` plus the optional Mermaid CLI requirement for Mermaid PDF rendering.
|
|
17
|
+
|
|
7
18
|
## [0.5.27] — 2026-03-21
|
|
8
19
|
|
|
9
20
|
### Fixed
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# pi-studio
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Extension for [pi](https://pi.dev) that opens a local two-pane browser workspace for working with prompts, responses, Markdown and LaTeX documents, code files, and other common text-based files side by side. Annotate responses and files, write, edit, and run prompts, browse prompt and response history, request critiques, and use live preview for code, Markdown, and LaTeX.
|
|
4
4
|
|
|
5
5
|
## Screenshots
|
|
6
6
|
|
|
@@ -42,6 +42,7 @@ Experimental extension for [pi](https://github.com/badlogic/pi-mono) that opens
|
|
|
42
42
|
| `/studio --status` | Show studio server status |
|
|
43
43
|
| `/studio --stop` | Stop studio server |
|
|
44
44
|
| `/studio --help` | Show help |
|
|
45
|
+
| `/studio-current <path>` | Load a file into currently open Studio tab(s) without opening a new browser window |
|
|
45
46
|
| `/studio-pdf <path>` | Export a local file to `<name>.studio.pdf` via the Studio PDF pipeline |
|
|
46
47
|
|
|
47
48
|
## Install
|
|
@@ -68,6 +69,7 @@ pi -e https://github.com/omaclaren/pi-studio
|
|
|
68
69
|
- Full preview/PDF quality depends on `pandoc` (and `xelatex` for PDF):
|
|
69
70
|
- `brew install pandoc`
|
|
70
71
|
- install TeX Live/MacTeX for PDF export
|
|
72
|
+
- Mermaid diagrams in exported PDFs may also require Mermaid CLI (`mmdc` / `@mermaid-js/mermaid-cli`) when you want diagram blocks rendered as diagrams rather than left as code.
|
|
71
73
|
|
|
72
74
|
## License
|
|
73
75
|
|
package/client/studio-client.js
CHANGED
|
@@ -78,6 +78,7 @@
|
|
|
78
78
|
const getEditorBtn = document.getElementById("getEditorBtn");
|
|
79
79
|
const loadGitDiffBtn = document.getElementById("loadGitDiffBtn");
|
|
80
80
|
const sendRunBtn = document.getElementById("sendRunBtn");
|
|
81
|
+
const queueSteerBtn = document.getElementById("queueSteerBtn");
|
|
81
82
|
const copyDraftBtn = document.getElementById("copyDraftBtn");
|
|
82
83
|
const saveAnnotatedBtn = document.getElementById("saveAnnotatedBtn");
|
|
83
84
|
const stripAnnotationsBtn = document.getElementById("stripAnnotationsBtn");
|
|
@@ -120,6 +121,8 @@
|
|
|
120
121
|
let latestCritiqueNotesNormalized = "";
|
|
121
122
|
let responseHistory = [];
|
|
122
123
|
let responseHistoryIndex = -1;
|
|
124
|
+
let studioRunChainActive = false;
|
|
125
|
+
let queuedSteeringCount = 0;
|
|
123
126
|
let agentBusyFromServer = false;
|
|
124
127
|
let terminalActivityPhase = "idle";
|
|
125
128
|
let terminalActivityToolName = "";
|
|
@@ -152,6 +155,23 @@
|
|
|
152
155
|
return trimmed ? trimmed : null;
|
|
153
156
|
}
|
|
154
157
|
|
|
158
|
+
function applyStudioRunQueueStateFromMessage(message) {
|
|
159
|
+
if (!message || typeof message !== "object") return false;
|
|
160
|
+
let changed = false;
|
|
161
|
+
if (typeof message.studioRunChainActive === "boolean" && studioRunChainActive !== message.studioRunChainActive) {
|
|
162
|
+
studioRunChainActive = message.studioRunChainActive;
|
|
163
|
+
changed = true;
|
|
164
|
+
}
|
|
165
|
+
if (typeof message.queuedSteeringCount === "number" && Number.isFinite(message.queuedSteeringCount)) {
|
|
166
|
+
const nextCount = Math.max(0, Math.floor(message.queuedSteeringCount));
|
|
167
|
+
if (queuedSteeringCount !== nextCount) {
|
|
168
|
+
queuedSteeringCount = nextCount;
|
|
169
|
+
changed = true;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return changed;
|
|
173
|
+
}
|
|
174
|
+
|
|
155
175
|
contextTokens = parseFiniteNumber(document.body && document.body.dataset ? document.body.dataset.contextTokens : null);
|
|
156
176
|
contextWindow = parseFiniteNumber(document.body && document.body.dataset ? document.body.dataset.contextWindow : null);
|
|
157
177
|
contextPercent = parseFiniteNumber(document.body && document.body.dataset ? document.body.dataset.contextPercent : null);
|
|
@@ -383,26 +403,80 @@
|
|
|
383
403
|
return "submitting request";
|
|
384
404
|
}
|
|
385
405
|
|
|
406
|
+
function formatQueuedSteeringSuffix() {
|
|
407
|
+
if (!queuedSteeringCount) return "";
|
|
408
|
+
return queuedSteeringCount === 1
|
|
409
|
+
? " · 1 steering queued"
|
|
410
|
+
: " · " + queuedSteeringCount + " steering queued";
|
|
411
|
+
}
|
|
412
|
+
|
|
386
413
|
function getStudioBusyStatus(kind) {
|
|
387
414
|
const action = getStudioActionLabel(kind);
|
|
415
|
+
const queueSuffix = studioRunChainActive ? formatQueuedSteeringSuffix() : "";
|
|
388
416
|
if (terminalActivityPhase === "tool") {
|
|
389
417
|
if (terminalActivityLabel) {
|
|
390
|
-
return "Studio: " + withEllipsis(terminalActivityLabel);
|
|
418
|
+
return "Studio: " + withEllipsis(terminalActivityLabel) + queueSuffix;
|
|
391
419
|
}
|
|
392
420
|
return terminalActivityToolName
|
|
393
|
-
? "Studio: " + action + " (tool: " + terminalActivityToolName + ")…"
|
|
394
|
-
: "Studio: " + action + " (running tool)…";
|
|
421
|
+
? "Studio: " + action + " (tool: " + terminalActivityToolName + ")…" + queueSuffix
|
|
422
|
+
: "Studio: " + action + " (running tool)…" + queueSuffix;
|
|
395
423
|
}
|
|
396
424
|
if (terminalActivityPhase === "responding") {
|
|
397
425
|
if (lastSpecificToolLabel) {
|
|
398
|
-
return "Studio: " + lastSpecificToolLabel + " (generating response)…";
|
|
426
|
+
return "Studio: " + lastSpecificToolLabel + " (generating response)…" + queueSuffix;
|
|
399
427
|
}
|
|
400
|
-
return "Studio: " + action + " (generating response)…";
|
|
428
|
+
return "Studio: " + action + " (generating response)…" + queueSuffix;
|
|
401
429
|
}
|
|
402
430
|
if (terminalActivityPhase === "running" && lastSpecificToolLabel) {
|
|
403
|
-
return "Studio: " + withEllipsis(lastSpecificToolLabel);
|
|
431
|
+
return "Studio: " + withEllipsis(lastSpecificToolLabel) + queueSuffix;
|
|
404
432
|
}
|
|
405
|
-
return "Studio: " + action + "…";
|
|
433
|
+
return "Studio: " + action + "…" + queueSuffix;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
function getHistoryPromptSourceLabel(item) {
|
|
437
|
+
if (!item || !item.promptMode) return null;
|
|
438
|
+
const steeringCount = typeof item.promptSteeringCount === "number" && Number.isFinite(item.promptSteeringCount)
|
|
439
|
+
? Math.max(0, Math.floor(item.promptSteeringCount))
|
|
440
|
+
: 0;
|
|
441
|
+
if (item.promptMode === "run") return "original run";
|
|
442
|
+
if (item.promptMode !== "effective") return null;
|
|
443
|
+
if (steeringCount <= 0) return "original run";
|
|
444
|
+
return steeringCount === 1
|
|
445
|
+
? "original run + 1 steering message"
|
|
446
|
+
: "original run + " + steeringCount + " steering messages";
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
function getHistoryPromptButtonLabel(item) {
|
|
450
|
+
if (!item || !item.prompt || !String(item.prompt).trim()) {
|
|
451
|
+
return "Response prompt unavailable";
|
|
452
|
+
}
|
|
453
|
+
if (item.promptMode === "effective") {
|
|
454
|
+
return "Load effective prompt into editor";
|
|
455
|
+
}
|
|
456
|
+
if (item.promptMode === "run") {
|
|
457
|
+
return "Load run prompt into editor";
|
|
458
|
+
}
|
|
459
|
+
return "Load response prompt into editor";
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
function getHistoryPromptLoadedStatus(item) {
|
|
463
|
+
if (!item || !item.prompt || !String(item.prompt).trim()) {
|
|
464
|
+
return "Prompt unavailable for the selected response.";
|
|
465
|
+
}
|
|
466
|
+
if (item.promptMode === "effective") {
|
|
467
|
+
return "Loaded effective prompt into editor.";
|
|
468
|
+
}
|
|
469
|
+
if (item.promptMode === "run") {
|
|
470
|
+
return "Loaded run prompt into editor.";
|
|
471
|
+
}
|
|
472
|
+
return "Loaded response prompt into editor.";
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
function getHistoryPromptSourceStateLabel(item) {
|
|
476
|
+
if (!item || !item.prompt || !String(item.prompt).trim()) return "response prompt";
|
|
477
|
+
if (item.promptMode === "effective") return "effective prompt";
|
|
478
|
+
if (item.promptMode === "run") return "run prompt";
|
|
479
|
+
return "response prompt";
|
|
406
480
|
}
|
|
407
481
|
|
|
408
482
|
function shouldAnimateFooterSpinner() {
|
|
@@ -715,8 +789,8 @@
|
|
|
715
789
|
btn.setAttribute("aria-pressed", isFocusedPane ? "true" : "false");
|
|
716
790
|
btn.textContent = isFocusedPane ? "Exit focus" : "Focus pane";
|
|
717
791
|
btn.title = isFocusedPane
|
|
718
|
-
? "
|
|
719
|
-
: "Show only the " + paneName + " pane. Shortcut: Cmd/Ctrl+Esc
|
|
792
|
+
? "Return to the two-pane layout. Shortcut: F10 or Cmd/Ctrl+Esc."
|
|
793
|
+
: "Show only the " + paneName + " pane. Shortcut: F10 or Cmd/Ctrl+Esc.";
|
|
720
794
|
});
|
|
721
795
|
}
|
|
722
796
|
|
|
@@ -754,7 +828,7 @@
|
|
|
754
828
|
setActivePane(pane);
|
|
755
829
|
paneFocusTarget = pane;
|
|
756
830
|
applyPaneFocusClasses();
|
|
757
|
-
setStatus("Focus mode: " + paneLabel(pane) + " pane
|
|
831
|
+
setStatus("Focus mode: " + paneLabel(pane) + " pane. Toggle with F10 or Cmd/Ctrl+Esc.");
|
|
758
832
|
}
|
|
759
833
|
|
|
760
834
|
function togglePaneFocus() {
|
|
@@ -777,7 +851,7 @@
|
|
|
777
851
|
}
|
|
778
852
|
|
|
779
853
|
function handlePaneShortcut(event) {
|
|
780
|
-
if (!event) return;
|
|
854
|
+
if (!event || event.defaultPrevented) return;
|
|
781
855
|
|
|
782
856
|
const key = typeof event.key === "string" ? event.key : "";
|
|
783
857
|
const isToggleShortcut =
|
|
@@ -797,9 +871,16 @@
|
|
|
797
871
|
&& !event.altKey
|
|
798
872
|
&& !event.shiftKey
|
|
799
873
|
) {
|
|
874
|
+
const activeKind = getAbortablePendingKind();
|
|
875
|
+
if (activeKind === "direct" || activeKind === "critique") {
|
|
876
|
+
event.preventDefault();
|
|
877
|
+
requestCancelForPendingRequest(activeKind);
|
|
878
|
+
return;
|
|
879
|
+
}
|
|
800
880
|
if (exitPaneFocus()) {
|
|
801
881
|
event.preventDefault();
|
|
802
882
|
}
|
|
883
|
+
return;
|
|
803
884
|
}
|
|
804
885
|
|
|
805
886
|
if (
|
|
@@ -808,11 +889,16 @@
|
|
|
808
889
|
&& !event.altKey
|
|
809
890
|
&& !event.shiftKey
|
|
810
891
|
&& activePane === "left"
|
|
811
|
-
&& sendRunBtn
|
|
812
|
-
&& !sendRunBtn.disabled
|
|
813
892
|
) {
|
|
814
|
-
|
|
815
|
-
|
|
893
|
+
if (queueSteerBtn && !queueSteerBtn.disabled) {
|
|
894
|
+
event.preventDefault();
|
|
895
|
+
queueSteerBtn.click();
|
|
896
|
+
return;
|
|
897
|
+
}
|
|
898
|
+
if (sendRunBtn && !sendRunBtn.disabled) {
|
|
899
|
+
event.preventDefault();
|
|
900
|
+
sendRunBtn.click();
|
|
901
|
+
}
|
|
816
902
|
}
|
|
817
903
|
}
|
|
818
904
|
|
|
@@ -851,6 +937,18 @@
|
|
|
851
937
|
const thinking = typeof item.thinking === "string"
|
|
852
938
|
? item.thinking
|
|
853
939
|
: (item.thinking == null ? null : String(item.thinking));
|
|
940
|
+
const promptMode = item.promptMode === "run" || item.promptMode === "effective"
|
|
941
|
+
? item.promptMode
|
|
942
|
+
: "response";
|
|
943
|
+
const promptTriggerKind = item.promptTriggerKind === "run" || item.promptTriggerKind === "steer"
|
|
944
|
+
? item.promptTriggerKind
|
|
945
|
+
: null;
|
|
946
|
+
const promptSteeringCount = typeof item.promptSteeringCount === "number" && Number.isFinite(item.promptSteeringCount)
|
|
947
|
+
? Math.max(0, Math.floor(item.promptSteeringCount))
|
|
948
|
+
: 0;
|
|
949
|
+
const promptTriggerText = typeof item.promptTriggerText === "string"
|
|
950
|
+
? item.promptTriggerText
|
|
951
|
+
: (item.promptTriggerText == null ? null : String(item.promptTriggerText));
|
|
854
952
|
|
|
855
953
|
return {
|
|
856
954
|
id,
|
|
@@ -859,6 +957,10 @@
|
|
|
859
957
|
timestamp,
|
|
860
958
|
kind: normalizeHistoryKind(item.kind),
|
|
861
959
|
prompt,
|
|
960
|
+
promptMode,
|
|
961
|
+
promptTriggerKind,
|
|
962
|
+
promptSteeringCount,
|
|
963
|
+
promptTriggerText,
|
|
862
964
|
};
|
|
863
965
|
}
|
|
864
966
|
|
|
@@ -904,9 +1006,13 @@
|
|
|
904
1006
|
const hasPrompt = Boolean(selectedItem && typeof selectedItem.prompt === "string" && selectedItem.prompt.trim());
|
|
905
1007
|
if (loadHistoryPromptBtn) {
|
|
906
1008
|
loadHistoryPromptBtn.disabled = uiBusy || !hasPrompt;
|
|
907
|
-
loadHistoryPromptBtn.textContent =
|
|
908
|
-
|
|
909
|
-
|
|
1009
|
+
loadHistoryPromptBtn.textContent = getHistoryPromptButtonLabel(selectedItem);
|
|
1010
|
+
const promptSourceLabel = getHistoryPromptSourceLabel(selectedItem);
|
|
1011
|
+
loadHistoryPromptBtn.title = hasPrompt
|
|
1012
|
+
? (promptSourceLabel
|
|
1013
|
+
? "Load the " + promptSourceLabel + " prompt chain that generated the selected response into the editor."
|
|
1014
|
+
: "Load the prompt that generated the selected response into the editor.")
|
|
1015
|
+
: "Prompt unavailable for the selected response.";
|
|
910
1016
|
}
|
|
911
1017
|
}
|
|
912
1018
|
|
|
@@ -2806,29 +2912,43 @@
|
|
|
2806
2912
|
|
|
2807
2913
|
function syncRunAndCritiqueButtons() {
|
|
2808
2914
|
const activeKind = getAbortablePendingKind();
|
|
2809
|
-
const
|
|
2915
|
+
const directIsStop = activeKind === "direct";
|
|
2810
2916
|
const critiqueIsStop = activeKind === "critique";
|
|
2917
|
+
const canQueueSteering = studioRunChainActive && !critiqueIsStop;
|
|
2811
2918
|
|
|
2812
2919
|
if (sendRunBtn) {
|
|
2813
|
-
sendRunBtn.textContent =
|
|
2814
|
-
sendRunBtn.classList.toggle("request-stop-active",
|
|
2815
|
-
sendRunBtn.disabled =
|
|
2816
|
-
sendRunBtn.title =
|
|
2817
|
-
? "Stop the
|
|
2920
|
+
sendRunBtn.textContent = directIsStop ? "Stop" : "Run editor text";
|
|
2921
|
+
sendRunBtn.classList.toggle("request-stop-active", directIsStop);
|
|
2922
|
+
sendRunBtn.disabled = wsState === "Disconnected" || (!directIsStop && (uiBusy || critiqueIsStop));
|
|
2923
|
+
sendRunBtn.title = directIsStop
|
|
2924
|
+
? "Stop the active run. Shortcut: Esc."
|
|
2818
2925
|
: (annotationsEnabled
|
|
2819
|
-
? "Run editor text as-is (includes [an: ...] markers). Shortcut: Cmd/Ctrl+Enter."
|
|
2820
|
-
: "Run editor text with [an: ...] markers stripped. Shortcut: Cmd/Ctrl+Enter.");
|
|
2926
|
+
? "Run editor text as-is (includes [an: ...] markers). Shortcut: Cmd/Ctrl+Enter. Stop the active request with Esc."
|
|
2927
|
+
: "Run editor text with [an: ...] markers stripped. Shortcut: Cmd/Ctrl+Enter. Stop the active request with Esc.");
|
|
2928
|
+
}
|
|
2929
|
+
|
|
2930
|
+
if (queueSteerBtn) {
|
|
2931
|
+
queueSteerBtn.hidden = false;
|
|
2932
|
+
queueSteerBtn.disabled = wsState === "Disconnected" || !canQueueSteering;
|
|
2933
|
+
queueSteerBtn.classList.remove("request-stop-active");
|
|
2934
|
+
queueSteerBtn.title = canQueueSteering
|
|
2935
|
+
? (annotationsEnabled
|
|
2936
|
+
? "Queue the current editor text as a steering message for the active run. Shortcut: Cmd/Ctrl+Enter."
|
|
2937
|
+
: "Queue the current editor text as a steering message for the active run after stripping [an: ...] markers. Shortcut: Cmd/Ctrl+Enter.")
|
|
2938
|
+
: "Queue steering is available while Run editor text is active.";
|
|
2821
2939
|
}
|
|
2822
2940
|
|
|
2823
2941
|
if (critiqueBtn) {
|
|
2824
2942
|
critiqueBtn.textContent = critiqueIsStop ? "Stop" : "Critique editor text";
|
|
2825
2943
|
critiqueBtn.classList.toggle("request-stop-active", critiqueIsStop);
|
|
2826
|
-
critiqueBtn.disabled = critiqueIsStop ? wsState === "Disconnected" : (uiBusy ||
|
|
2944
|
+
critiqueBtn.disabled = critiqueIsStop ? wsState === "Disconnected" : (uiBusy || canQueueSteering);
|
|
2827
2945
|
critiqueBtn.title = critiqueIsStop
|
|
2828
|
-
? "Stop the running critique request."
|
|
2829
|
-
: (
|
|
2830
|
-
? "Critique editor text
|
|
2831
|
-
:
|
|
2946
|
+
? "Stop the running critique request. Shortcut: Esc."
|
|
2947
|
+
: (canQueueSteering
|
|
2948
|
+
? "Critique queueing is not supported while Run editor text is active."
|
|
2949
|
+
: (annotationsEnabled
|
|
2950
|
+
? "Critique editor text as-is (includes [an: ...] markers)."
|
|
2951
|
+
: "Critique editor text with [an: ...] markers stripped."));
|
|
2832
2952
|
}
|
|
2833
2953
|
}
|
|
2834
2954
|
|
|
@@ -2973,6 +3093,7 @@
|
|
|
2973
3093
|
if (typeof message.terminalSessionLabel === "string") {
|
|
2974
3094
|
terminalSessionLabel = message.terminalSessionLabel;
|
|
2975
3095
|
}
|
|
3096
|
+
applyStudioRunQueueStateFromMessage(message);
|
|
2976
3097
|
updateFooterMeta();
|
|
2977
3098
|
setBusy(busy);
|
|
2978
3099
|
setWsState(busy ? "Submitting" : "Ready");
|
|
@@ -3045,6 +3166,8 @@
|
|
|
3045
3166
|
if (busy) {
|
|
3046
3167
|
if (agentBusyFromServer && stickyStudioKind) {
|
|
3047
3168
|
setStatus(getStudioBusyStatus(stickyStudioKind), "warning");
|
|
3169
|
+
} else if (agentBusyFromServer && studioRunChainActive) {
|
|
3170
|
+
setStatus(getStudioBusyStatus("direct"), "warning");
|
|
3048
3171
|
} else if (agentBusyFromServer) {
|
|
3049
3172
|
setStatus(getTerminalBusyStatus(), "warning");
|
|
3050
3173
|
} else {
|
|
@@ -3065,6 +3188,9 @@
|
|
|
3065
3188
|
pendingRequestId = typeof message.requestId === "string" ? message.requestId : pendingRequestId;
|
|
3066
3189
|
pendingKind = typeof message.kind === "string" ? message.kind : "unknown";
|
|
3067
3190
|
stickyStudioKind = pendingKind;
|
|
3191
|
+
if (pendingKind === "direct") {
|
|
3192
|
+
studioRunChainActive = true;
|
|
3193
|
+
}
|
|
3068
3194
|
if (pendingKind === "compact") {
|
|
3069
3195
|
compactInProgress = true;
|
|
3070
3196
|
}
|
|
@@ -3074,6 +3200,14 @@
|
|
|
3074
3200
|
return;
|
|
3075
3201
|
}
|
|
3076
3202
|
|
|
3203
|
+
if (message.type === "request_queued") {
|
|
3204
|
+
studioRunChainActive = true;
|
|
3205
|
+
applyStudioRunQueueStateFromMessage(message);
|
|
3206
|
+
syncActionButtons();
|
|
3207
|
+
setStatus("Steering queued.", "success");
|
|
3208
|
+
return;
|
|
3209
|
+
}
|
|
3210
|
+
|
|
3077
3211
|
if (message.type === "compaction_completed") {
|
|
3078
3212
|
if (typeof message.requestId === "string" && pendingRequestId === message.requestId) {
|
|
3079
3213
|
pendingRequestId = null;
|
|
@@ -3310,6 +3444,7 @@
|
|
|
3310
3444
|
if (typeof message.terminalSessionLabel === "string") {
|
|
3311
3445
|
terminalSessionLabel = message.terminalSessionLabel;
|
|
3312
3446
|
}
|
|
3447
|
+
applyStudioRunQueueStateFromMessage(message);
|
|
3313
3448
|
updateFooterMeta();
|
|
3314
3449
|
|
|
3315
3450
|
if (typeof message.activeRequestId === "string" && message.activeRequestId.length > 0) {
|
|
@@ -3346,6 +3481,8 @@
|
|
|
3346
3481
|
if (busy) {
|
|
3347
3482
|
if (agentBusyFromServer && stickyStudioKind) {
|
|
3348
3483
|
setStatus(getStudioBusyStatus(stickyStudioKind), "warning");
|
|
3484
|
+
} else if (agentBusyFromServer && studioRunChainActive) {
|
|
3485
|
+
setStatus(getStudioBusyStatus("direct"), "warning");
|
|
3349
3486
|
} else if (agentBusyFromServer) {
|
|
3350
3487
|
setStatus(getTerminalBusyStatus(), "warning");
|
|
3351
3488
|
} else {
|
|
@@ -3816,8 +3953,8 @@
|
|
|
3816
3953
|
}
|
|
3817
3954
|
|
|
3818
3955
|
setEditorText(prompt, { preserveScroll: false, preserveSelection: false });
|
|
3819
|
-
setSourceState({ source: "blank", label:
|
|
3820
|
-
setStatus(
|
|
3956
|
+
setSourceState({ source: "blank", label: getHistoryPromptSourceStateLabel(item), path: null });
|
|
3957
|
+
setStatus(getHistoryPromptLoadedStatus(item), "success");
|
|
3821
3958
|
});
|
|
3822
3959
|
}
|
|
3823
3960
|
|
|
@@ -4118,6 +4255,30 @@
|
|
|
4118
4255
|
}
|
|
4119
4256
|
});
|
|
4120
4257
|
|
|
4258
|
+
if (queueSteerBtn) {
|
|
4259
|
+
queueSteerBtn.addEventListener("click", () => {
|
|
4260
|
+
const prepared = prepareEditorTextForSend(sourceTextEl.value);
|
|
4261
|
+
if (!prepared.trim()) {
|
|
4262
|
+
setStatus("Editor is empty. Nothing to queue.", "warning");
|
|
4263
|
+
return;
|
|
4264
|
+
}
|
|
4265
|
+
if (!studioRunChainActive) {
|
|
4266
|
+
setStatus("Queue steering is only available while Run editor text is active.", "warning");
|
|
4267
|
+
return;
|
|
4268
|
+
}
|
|
4269
|
+
|
|
4270
|
+
const requestId = makeRequestId();
|
|
4271
|
+
clearTitleAttention();
|
|
4272
|
+
const sent = sendMessage({
|
|
4273
|
+
type: "send_run_request",
|
|
4274
|
+
requestId,
|
|
4275
|
+
text: prepared,
|
|
4276
|
+
});
|
|
4277
|
+
if (!sent) return;
|
|
4278
|
+
setStatus("Queueing steering…", "warning");
|
|
4279
|
+
});
|
|
4280
|
+
}
|
|
4281
|
+
|
|
4121
4282
|
copyDraftBtn.addEventListener("click", async () => {
|
|
4122
4283
|
const content = sourceTextEl.value;
|
|
4123
4284
|
if (!content.trim()) {
|
package/client/studio.css
CHANGED
|
@@ -87,6 +87,7 @@
|
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
#sendRunBtn,
|
|
90
|
+
#queueSteerBtn,
|
|
90
91
|
#critiqueBtn {
|
|
91
92
|
min-width: 10rem;
|
|
92
93
|
display: inline-flex;
|
|
@@ -95,6 +96,7 @@
|
|
|
95
96
|
}
|
|
96
97
|
|
|
97
98
|
#sendRunBtn:not(:disabled):not(.request-stop-active),
|
|
99
|
+
#queueSteerBtn:not(:disabled),
|
|
98
100
|
#loadResponseBtn:not(:disabled):not([hidden]) {
|
|
99
101
|
background: var(--accent);
|
|
100
102
|
border-color: var(--accent);
|
|
@@ -103,6 +105,7 @@
|
|
|
103
105
|
}
|
|
104
106
|
|
|
105
107
|
#sendRunBtn:not(:disabled):not(.request-stop-active):hover,
|
|
108
|
+
#queueSteerBtn:not(:disabled):hover,
|
|
106
109
|
#loadResponseBtn:not(:disabled):not([hidden]):hover {
|
|
107
110
|
filter: brightness(0.95);
|
|
108
111
|
}
|
package/index.ts
CHANGED
|
@@ -14,6 +14,8 @@ type RequestedLens = Lens | "auto";
|
|
|
14
14
|
type StudioRequestKind = "critique" | "annotation" | "direct" | "compact";
|
|
15
15
|
type StudioSourceKind = "file" | "last-response" | "blank";
|
|
16
16
|
type TerminalActivityPhase = "idle" | "running" | "tool" | "responding";
|
|
17
|
+
type StudioPromptMode = "response" | "run" | "effective";
|
|
18
|
+
type StudioPromptTriggerKind = "run" | "steer";
|
|
17
19
|
|
|
18
20
|
const STUDIO_CSS_URL = new URL("./client/studio.css", import.meta.url);
|
|
19
21
|
const STUDIO_CLIENT_URL = new URL("./client/studio-client.js", import.meta.url);
|
|
@@ -26,10 +28,17 @@ interface StudioServerState {
|
|
|
26
28
|
token: string;
|
|
27
29
|
}
|
|
28
30
|
|
|
29
|
-
interface
|
|
31
|
+
interface StudioPromptDescriptor {
|
|
32
|
+
prompt: string | null;
|
|
33
|
+
promptMode: StudioPromptMode;
|
|
34
|
+
promptTriggerKind: StudioPromptTriggerKind | null;
|
|
35
|
+
promptSteeringCount: number;
|
|
36
|
+
promptTriggerText: string | null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
interface ActiveStudioRequest extends StudioPromptDescriptor {
|
|
30
40
|
id: string;
|
|
31
41
|
kind: StudioRequestKind;
|
|
32
|
-
prompt: string | null;
|
|
33
42
|
timer: NodeJS.Timeout;
|
|
34
43
|
startedAt: number;
|
|
35
44
|
}
|
|
@@ -41,13 +50,28 @@ interface LastStudioResponse {
|
|
|
41
50
|
kind: StudioRequestKind;
|
|
42
51
|
}
|
|
43
52
|
|
|
44
|
-
interface StudioResponseHistoryItem {
|
|
53
|
+
interface StudioResponseHistoryItem extends StudioPromptDescriptor {
|
|
45
54
|
id: string;
|
|
46
55
|
markdown: string;
|
|
47
56
|
thinking: string | null;
|
|
48
57
|
timestamp: number;
|
|
49
58
|
kind: StudioRequestKind;
|
|
50
|
-
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
interface StudioDirectRunChain {
|
|
62
|
+
id: string;
|
|
63
|
+
basePrompt: string;
|
|
64
|
+
steeringPrompts: string[];
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
interface QueuedStudioDirectRequest extends StudioPromptDescriptor {
|
|
68
|
+
requestId: string;
|
|
69
|
+
queuedAt: number;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
interface PersistedStudioPromptMetadata extends StudioPromptDescriptor {
|
|
73
|
+
version: 1;
|
|
74
|
+
requestKind: "direct";
|
|
51
75
|
}
|
|
52
76
|
|
|
53
77
|
interface StudioContextUsageSnapshot {
|
|
@@ -173,6 +197,7 @@ const STUDIO_TERMINAL_NOTIFY_TITLE = "pi Studio";
|
|
|
173
197
|
const CMUX_STUDIO_STATUS_KEY = "pi_studio";
|
|
174
198
|
const CMUX_STUDIO_STATUS_COLOR_DARK = "#5ea1ff";
|
|
175
199
|
const CMUX_STUDIO_STATUS_COLOR_LIGHT = "#0047ab";
|
|
200
|
+
const STUDIO_PROMPT_METADATA_CUSTOM_TYPE = "pi-studio/direct-prompt";
|
|
176
201
|
|
|
177
202
|
const PDF_PREAMBLE = `\\usepackage{titlesec}
|
|
178
203
|
\\titleformat{\\section}{\\Large\\bfseries\\sffamily}{}{0pt}{}[\\vspace{3pt}\\titlerule\\vspace{12pt}]
|
|
@@ -3617,6 +3642,95 @@ function normalizePromptText(text: string | null | undefined): string | null {
|
|
|
3617
3642
|
return trimmed.length > 0 ? trimmed : null;
|
|
3618
3643
|
}
|
|
3619
3644
|
|
|
3645
|
+
function buildStudioPromptDescriptor(
|
|
3646
|
+
prompt: string | null,
|
|
3647
|
+
promptMode: StudioPromptMode = "response",
|
|
3648
|
+
promptTriggerKind: StudioPromptTriggerKind | null = null,
|
|
3649
|
+
promptSteeringCount = 0,
|
|
3650
|
+
promptTriggerText: string | null = null,
|
|
3651
|
+
): StudioPromptDescriptor {
|
|
3652
|
+
return {
|
|
3653
|
+
prompt: normalizePromptText(prompt),
|
|
3654
|
+
promptMode,
|
|
3655
|
+
promptTriggerKind,
|
|
3656
|
+
promptSteeringCount: Number.isFinite(promptSteeringCount) && promptSteeringCount > 0
|
|
3657
|
+
? Math.max(0, Math.floor(promptSteeringCount))
|
|
3658
|
+
: 0,
|
|
3659
|
+
promptTriggerText: normalizePromptText(promptTriggerText),
|
|
3660
|
+
};
|
|
3661
|
+
}
|
|
3662
|
+
|
|
3663
|
+
function buildStudioEffectivePrompt(basePrompt: string | null | undefined, steeringPrompts: Array<string | null | undefined>): string | null {
|
|
3664
|
+
const normalizedBasePrompt = normalizePromptText(basePrompt);
|
|
3665
|
+
const normalizedSteeringPrompts = steeringPrompts
|
|
3666
|
+
.map((prompt) => normalizePromptText(prompt))
|
|
3667
|
+
.filter((prompt): prompt is string => Boolean(prompt));
|
|
3668
|
+
|
|
3669
|
+
if (!normalizedBasePrompt) {
|
|
3670
|
+
if (normalizedSteeringPrompts.length === 0) return null;
|
|
3671
|
+
return normalizedSteeringPrompts.join("\n\n");
|
|
3672
|
+
}
|
|
3673
|
+
if (normalizedSteeringPrompts.length === 0) return normalizedBasePrompt;
|
|
3674
|
+
|
|
3675
|
+
const sections = ["## Original run prompt\n\n" + normalizedBasePrompt];
|
|
3676
|
+
for (let i = 0; i < normalizedSteeringPrompts.length; i++) {
|
|
3677
|
+
sections.push(`## Steering ${i + 1}\n\n${normalizedSteeringPrompts[i]}`);
|
|
3678
|
+
}
|
|
3679
|
+
return sections.join("\n\n").trim();
|
|
3680
|
+
}
|
|
3681
|
+
|
|
3682
|
+
function buildStudioDirectRunPromptDescriptor(prompt: string): StudioPromptDescriptor {
|
|
3683
|
+
const normalizedPrompt = normalizePromptText(prompt);
|
|
3684
|
+
return buildStudioPromptDescriptor(normalizedPrompt, "run", "run", 0, normalizedPrompt);
|
|
3685
|
+
}
|
|
3686
|
+
|
|
3687
|
+
function buildStudioQueuedSteerPromptDescriptor(chain: StudioDirectRunChain, triggerPrompt: string): StudioPromptDescriptor {
|
|
3688
|
+
const normalizedTriggerPrompt = normalizePromptText(triggerPrompt);
|
|
3689
|
+
const steeringPrompts = [...chain.steeringPrompts, normalizedTriggerPrompt].filter((prompt): prompt is string => Boolean(prompt));
|
|
3690
|
+
const effectivePrompt = buildStudioEffectivePrompt(chain.basePrompt, steeringPrompts);
|
|
3691
|
+
return buildStudioPromptDescriptor(effectivePrompt, "effective", "steer", steeringPrompts.length, normalizedTriggerPrompt);
|
|
3692
|
+
}
|
|
3693
|
+
|
|
3694
|
+
function buildPersistedStudioPromptMetadata(promptDescriptor: StudioPromptDescriptor): PersistedStudioPromptMetadata {
|
|
3695
|
+
return {
|
|
3696
|
+
version: 1,
|
|
3697
|
+
requestKind: "direct",
|
|
3698
|
+
prompt: promptDescriptor.prompt,
|
|
3699
|
+
promptMode: promptDescriptor.promptMode,
|
|
3700
|
+
promptTriggerKind: promptDescriptor.promptTriggerKind,
|
|
3701
|
+
promptSteeringCount: promptDescriptor.promptSteeringCount,
|
|
3702
|
+
promptTriggerText: promptDescriptor.promptTriggerText,
|
|
3703
|
+
};
|
|
3704
|
+
}
|
|
3705
|
+
|
|
3706
|
+
function extractPersistedStudioPromptMetadata(entry: SessionEntry): PersistedStudioPromptMetadata | null {
|
|
3707
|
+
if (!entry || entry.type !== "custom") return null;
|
|
3708
|
+
const customEntry = entry as { customType?: unknown; data?: unknown };
|
|
3709
|
+
if (customEntry.customType !== STUDIO_PROMPT_METADATA_CUSTOM_TYPE) return null;
|
|
3710
|
+
const data = customEntry.data as Partial<PersistedStudioPromptMetadata> | undefined;
|
|
3711
|
+
if (!data || data.requestKind !== "direct") return null;
|
|
3712
|
+
return {
|
|
3713
|
+
version: data.version === 1 ? 1 : 1,
|
|
3714
|
+
requestKind: "direct",
|
|
3715
|
+
...buildStudioPromptDescriptor(
|
|
3716
|
+
typeof data.prompt === "string" ? data.prompt : null,
|
|
3717
|
+
data.promptMode === "run" || data.promptMode === "effective" ? data.promptMode : "response",
|
|
3718
|
+
data.promptTriggerKind === "run" || data.promptTriggerKind === "steer" ? data.promptTriggerKind : null,
|
|
3719
|
+
typeof data.promptSteeringCount === "number" ? data.promptSteeringCount : 0,
|
|
3720
|
+
typeof data.promptTriggerText === "string" ? data.promptTriggerText : null,
|
|
3721
|
+
),
|
|
3722
|
+
};
|
|
3723
|
+
}
|
|
3724
|
+
|
|
3725
|
+
function getStudioPromptSourceLabel(promptMode: StudioPromptMode, promptSteeringCount: number): string | null {
|
|
3726
|
+
if (promptMode === "run") return "original run";
|
|
3727
|
+
if (promptMode !== "effective") return null;
|
|
3728
|
+
if (promptSteeringCount <= 0) return "original run";
|
|
3729
|
+
return promptSteeringCount === 1
|
|
3730
|
+
? "original run + 1 steering message"
|
|
3731
|
+
: `original run + ${promptSteeringCount} steering messages`;
|
|
3732
|
+
}
|
|
3733
|
+
|
|
3620
3734
|
function extractUserText(message: unknown): string | null {
|
|
3621
3735
|
const msg = message as {
|
|
3622
3736
|
role?: string;
|
|
@@ -3673,27 +3787,49 @@ function parseEntryTimestamp(timestamp: unknown): number {
|
|
|
3673
3787
|
function buildResponseHistoryFromEntries(entries: SessionEntry[], limit = RESPONSE_HISTORY_LIMIT): StudioResponseHistoryItem[] {
|
|
3674
3788
|
const history: StudioResponseHistoryItem[] = [];
|
|
3675
3789
|
let lastUserPrompt: string | null = null;
|
|
3790
|
+
let pendingPromptDescriptor: StudioPromptDescriptor | null = null;
|
|
3676
3791
|
|
|
3677
3792
|
for (const entry of entries) {
|
|
3678
|
-
if (!entry
|
|
3793
|
+
if (!entry) continue;
|
|
3794
|
+
|
|
3795
|
+
const persistedPromptMetadata = extractPersistedStudioPromptMetadata(entry);
|
|
3796
|
+
if (persistedPromptMetadata) {
|
|
3797
|
+
pendingPromptDescriptor = buildStudioPromptDescriptor(
|
|
3798
|
+
persistedPromptMetadata.prompt,
|
|
3799
|
+
persistedPromptMetadata.promptMode,
|
|
3800
|
+
persistedPromptMetadata.promptTriggerKind,
|
|
3801
|
+
persistedPromptMetadata.promptSteeringCount,
|
|
3802
|
+
persistedPromptMetadata.promptTriggerText,
|
|
3803
|
+
);
|
|
3804
|
+
continue;
|
|
3805
|
+
}
|
|
3806
|
+
|
|
3807
|
+
if (entry.type !== "message") continue;
|
|
3679
3808
|
const message = (entry as { message?: unknown }).message;
|
|
3680
3809
|
const role = (message as { role?: string } | undefined)?.role;
|
|
3681
3810
|
if (role === "user") {
|
|
3682
3811
|
lastUserPrompt = extractUserText(message);
|
|
3812
|
+
pendingPromptDescriptor = null;
|
|
3683
3813
|
continue;
|
|
3684
3814
|
}
|
|
3685
3815
|
if (role !== "assistant") continue;
|
|
3686
3816
|
const markdown = extractAssistantText(message);
|
|
3687
3817
|
if (!markdown) continue;
|
|
3688
3818
|
const thinking = extractAssistantThinking(message);
|
|
3819
|
+
const promptDescriptor = pendingPromptDescriptor ?? buildStudioPromptDescriptor(lastUserPrompt);
|
|
3689
3820
|
history.push({
|
|
3690
3821
|
id: typeof (entry as { id?: unknown }).id === "string" ? (entry as { id: string }).id : randomUUID(),
|
|
3691
3822
|
markdown,
|
|
3692
3823
|
thinking,
|
|
3693
3824
|
timestamp: parseEntryTimestamp((entry as { timestamp?: unknown }).timestamp),
|
|
3694
3825
|
kind: inferStudioResponseKind(markdown),
|
|
3695
|
-
prompt:
|
|
3826
|
+
prompt: promptDescriptor.prompt,
|
|
3827
|
+
promptMode: promptDescriptor.promptMode,
|
|
3828
|
+
promptTriggerKind: promptDescriptor.promptTriggerKind,
|
|
3829
|
+
promptSteeringCount: promptDescriptor.promptSteeringCount,
|
|
3830
|
+
promptTriggerText: promptDescriptor.promptTriggerText,
|
|
3696
3831
|
});
|
|
3832
|
+
pendingPromptDescriptor = null;
|
|
3697
3833
|
}
|
|
3698
3834
|
|
|
3699
3835
|
if (history.length <= limit) return history;
|
|
@@ -4206,7 +4342,7 @@ ${cssVarsBlock}
|
|
|
4206
4342
|
</select>
|
|
4207
4343
|
</div>
|
|
4208
4344
|
<div class="section-header-actions">
|
|
4209
|
-
<button id="leftFocusBtn" class="pane-focus-btn" type="button" title="Show only the editor pane. Shortcut: Cmd/Ctrl+Esc
|
|
4345
|
+
<button id="leftFocusBtn" class="pane-focus-btn" type="button" title="Show only the editor pane. Shortcut: F10 or Cmd/Ctrl+Esc.">Focus pane</button>
|
|
4210
4346
|
</div>
|
|
4211
4347
|
</div>
|
|
4212
4348
|
<div class="source-wrap">
|
|
@@ -4223,7 +4359,8 @@ ${cssVarsBlock}
|
|
|
4223
4359
|
</div>
|
|
4224
4360
|
<div class="source-actions">
|
|
4225
4361
|
<div class="source-actions-row">
|
|
4226
|
-
<button id="sendRunBtn" type="button" title="
|
|
4362
|
+
<button id="sendRunBtn" type="button" title="Run editor text. While a direct run is active, this button becomes Stop. Cmd/Ctrl+Enter queues steering from the current editor text. Stop the active request with Esc.">Run editor text</button>
|
|
4363
|
+
<button id="queueSteerBtn" type="button" title="Queue steering is available while Run editor text is active." disabled>Queue steering</button>
|
|
4227
4364
|
<button id="copyDraftBtn" type="button">Copy editor text</button>
|
|
4228
4365
|
<button id="sendEditorBtn" type="button">Send to pi editor</button>
|
|
4229
4366
|
</div>
|
|
@@ -4296,7 +4433,7 @@ ${cssVarsBlock}
|
|
|
4296
4433
|
</select>
|
|
4297
4434
|
</div>
|
|
4298
4435
|
<div class="section-header-actions">
|
|
4299
|
-
<button id="rightFocusBtn" class="pane-focus-btn" type="button" title="Show only the response pane. Shortcut: Cmd/Ctrl+Esc
|
|
4436
|
+
<button id="rightFocusBtn" class="pane-focus-btn" type="button" title="Show only the response pane. Shortcut: F10 or Cmd/Ctrl+Esc.">Focus pane</button>
|
|
4300
4437
|
<button id="exportPdfBtn" type="button" title="Export the current right-pane preview as PDF via pandoc + xelatex.">Export right preview as PDF</button>
|
|
4301
4438
|
</div>
|
|
4302
4439
|
</div>
|
|
@@ -4338,7 +4475,7 @@ ${cssVarsBlock}
|
|
|
4338
4475
|
<footer>
|
|
4339
4476
|
<span id="statusLine"><span id="statusSpinner" aria-hidden="true"> </span><span id="status">Booting studio…</span></span>
|
|
4340
4477
|
<span id="footerMeta" class="footer-meta"><span id="footerMetaText" class="footer-meta-text">Model: ${initialModel} · Terminal: ${initialTerminal} · Context: unknown</span><button id="compactBtn" class="footer-compact-btn" type="button" title="Trigger pi context compaction now.">Compact</button></span>
|
|
4341
|
-
<span class="shortcut-hint">Focus pane: Cmd/Ctrl+Esc
|
|
4478
|
+
<span class="shortcut-hint">Focus pane: F10 (or Cmd/Ctrl+Esc) to toggle · Run / queue steering: Cmd/Ctrl+Enter · Stop request: Esc</span>
|
|
4342
4479
|
</footer>
|
|
4343
4480
|
|
|
4344
4481
|
<!-- Defer sanitizer script so studio can boot/connect even if CDN is slow or blocked. -->
|
|
@@ -4354,6 +4491,9 @@ ${cssVarsBlock}
|
|
|
4354
4491
|
export default function (pi: ExtensionAPI) {
|
|
4355
4492
|
let serverState: StudioServerState | null = null;
|
|
4356
4493
|
let activeRequest: ActiveStudioRequest | null = null;
|
|
4494
|
+
let studioDirectRunChain: StudioDirectRunChain | null = null;
|
|
4495
|
+
let queuedStudioDirectRequests: QueuedStudioDirectRequest[] = [];
|
|
4496
|
+
let pendingStudioPromptMetadata: StudioPromptDescriptor | null = null;
|
|
4357
4497
|
let lastStudioResponse: LastStudioResponse | null = null;
|
|
4358
4498
|
let preparedPdfExports = new Map<string, PreparedStudioPdfExport>();
|
|
4359
4499
|
let initialStudioDocument: InitialStudioDocument | null = null;
|
|
@@ -4386,6 +4526,20 @@ export default function (pi: ExtensionAPI) {
|
|
|
4386
4526
|
const installedPackageVersion = packageMetadata?.version ?? null;
|
|
4387
4527
|
let updateAvailableLatestVersion: string | null = null;
|
|
4388
4528
|
|
|
4529
|
+
const isStudioDirectRunChainActive = () => Boolean(studioDirectRunChain);
|
|
4530
|
+
const getQueuedStudioSteeringCount = () => queuedStudioDirectRequests.length;
|
|
4531
|
+
const canQueueStudioSteeringRequest = () => {
|
|
4532
|
+
if (compactInProgress) return false;
|
|
4533
|
+
if (!agentBusy) return false;
|
|
4534
|
+
if (!studioDirectRunChain) return false;
|
|
4535
|
+
return !activeRequest || activeRequest.kind === "direct";
|
|
4536
|
+
};
|
|
4537
|
+
const clearStudioDirectRunState = () => {
|
|
4538
|
+
studioDirectRunChain = null;
|
|
4539
|
+
queuedStudioDirectRequests = [];
|
|
4540
|
+
pendingStudioPromptMetadata = null;
|
|
4541
|
+
};
|
|
4542
|
+
|
|
4389
4543
|
const isStudioBusy = () => agentBusy || activeRequest !== null || compactInProgress;
|
|
4390
4544
|
|
|
4391
4545
|
const getSessionNameSafe = (): string | undefined => {
|
|
@@ -4862,6 +5016,8 @@ export default function (pi: ExtensionAPI) {
|
|
|
4862
5016
|
compactInProgress,
|
|
4863
5017
|
activeRequestId: activeRequest?.id ?? compactRequestId ?? null,
|
|
4864
5018
|
activeRequestKind: activeRequest?.kind ?? (compactInProgress ? "compact" : null),
|
|
5019
|
+
studioRunChainActive: isStudioDirectRunChainActive(),
|
|
5020
|
+
queuedSteeringCount: getQueuedStudioSteeringCount(),
|
|
4865
5021
|
});
|
|
4866
5022
|
};
|
|
4867
5023
|
|
|
@@ -4916,19 +5072,67 @@ export default function (pi: ExtensionAPI) {
|
|
|
4916
5072
|
};
|
|
4917
5073
|
}
|
|
4918
5074
|
|
|
5075
|
+
if (kind === "direct") {
|
|
5076
|
+
clearStudioDirectRunState();
|
|
5077
|
+
}
|
|
4919
5078
|
suppressedStudioResponse = { requestId, kind };
|
|
4920
|
-
emitDebugEvent("cancel_active_request", { requestId, kind });
|
|
5079
|
+
emitDebugEvent("cancel_active_request", { requestId, kind, queuedSteeringCount: getQueuedStudioSteeringCount() });
|
|
4921
5080
|
clearActiveRequest({ notify: "Cancelled request.", level: "warning" });
|
|
4922
5081
|
return { ok: true, kind };
|
|
4923
5082
|
};
|
|
4924
5083
|
|
|
4925
|
-
const
|
|
5084
|
+
const activateRequest = (
|
|
5085
|
+
requestId: string,
|
|
5086
|
+
kind: StudioRequestKind,
|
|
5087
|
+
promptDescriptor?: StudioPromptDescriptor | null,
|
|
5088
|
+
options?: { skipNotificationCleanup?: boolean },
|
|
5089
|
+
): boolean => {
|
|
5090
|
+
const descriptor = promptDescriptor ?? buildStudioPromptDescriptor(null);
|
|
5091
|
+
const timer = setTimeout(() => {
|
|
5092
|
+
if (!activeRequest || activeRequest.id !== requestId) return;
|
|
5093
|
+
emitDebugEvent("request_timeout", { requestId, kind });
|
|
5094
|
+
broadcast({ type: "error", requestId, message: "Studio request timed out. Please try again." });
|
|
5095
|
+
clearActiveRequest();
|
|
5096
|
+
}, REQUEST_TIMEOUT_MS);
|
|
5097
|
+
|
|
5098
|
+
activeRequest = {
|
|
5099
|
+
id: requestId,
|
|
5100
|
+
kind,
|
|
5101
|
+
prompt: descriptor.prompt,
|
|
5102
|
+
promptMode: descriptor.promptMode,
|
|
5103
|
+
promptTriggerKind: descriptor.promptTriggerKind,
|
|
5104
|
+
promptSteeringCount: descriptor.promptSteeringCount,
|
|
5105
|
+
promptTriggerText: descriptor.promptTriggerText,
|
|
5106
|
+
startedAt: Date.now(),
|
|
5107
|
+
timer,
|
|
5108
|
+
};
|
|
5109
|
+
if (!options?.skipNotificationCleanup) {
|
|
5110
|
+
maybeClearStaleCmuxStudioNotifications();
|
|
5111
|
+
}
|
|
5112
|
+
syncCmuxStudioStatus();
|
|
5113
|
+
|
|
5114
|
+
emitDebugEvent("begin_request", {
|
|
5115
|
+
requestId,
|
|
5116
|
+
kind,
|
|
5117
|
+
promptMode: descriptor.promptMode,
|
|
5118
|
+
promptTriggerKind: descriptor.promptTriggerKind,
|
|
5119
|
+
promptSteeringCount: descriptor.promptSteeringCount,
|
|
5120
|
+
queuedSteeringCount: getQueuedStudioSteeringCount(),
|
|
5121
|
+
});
|
|
5122
|
+
broadcast({ type: "request_started", requestId, kind });
|
|
5123
|
+
broadcastState();
|
|
5124
|
+
return true;
|
|
5125
|
+
};
|
|
5126
|
+
|
|
5127
|
+
const beginRequest = (requestId: string, kind: StudioRequestKind, promptDescriptor?: StudioPromptDescriptor | null): boolean => {
|
|
4926
5128
|
suppressedStudioResponse = null;
|
|
4927
5129
|
emitDebugEvent("begin_request_attempt", {
|
|
4928
5130
|
requestId,
|
|
4929
5131
|
kind,
|
|
4930
5132
|
hasActiveRequest: Boolean(activeRequest),
|
|
4931
5133
|
agentBusy,
|
|
5134
|
+
studioDirectRunChainActive: isStudioDirectRunChainActive(),
|
|
5135
|
+
queuedSteeringCount: getQueuedStudioSteeringCount(),
|
|
4932
5136
|
});
|
|
4933
5137
|
if (activeRequest) {
|
|
4934
5138
|
broadcast({ type: "busy", requestId, message: "A studio request is already in progress." });
|
|
@@ -4942,28 +5146,91 @@ export default function (pi: ExtensionAPI) {
|
|
|
4942
5146
|
broadcast({ type: "busy", requestId, message: "pi is currently busy. Wait for the current turn to finish." });
|
|
4943
5147
|
return false;
|
|
4944
5148
|
}
|
|
5149
|
+
return activateRequest(requestId, kind, promptDescriptor);
|
|
5150
|
+
};
|
|
4945
5151
|
|
|
4946
|
-
|
|
4947
|
-
|
|
4948
|
-
|
|
4949
|
-
|
|
4950
|
-
|
|
4951
|
-
|
|
5152
|
+
const getPromptDescriptorForActiveRequest = (request: ActiveStudioRequest | null | undefined): StudioPromptDescriptor => {
|
|
5153
|
+
return buildStudioPromptDescriptor(
|
|
5154
|
+
request?.prompt ?? null,
|
|
5155
|
+
request?.promptMode ?? "response",
|
|
5156
|
+
request?.promptTriggerKind ?? null,
|
|
5157
|
+
request?.promptSteeringCount ?? 0,
|
|
5158
|
+
request?.promptTriggerText ?? null,
|
|
5159
|
+
);
|
|
5160
|
+
};
|
|
4952
5161
|
|
|
4953
|
-
|
|
4954
|
-
|
|
4955
|
-
|
|
4956
|
-
|
|
4957
|
-
|
|
4958
|
-
|
|
5162
|
+
const startStudioDirectRunChain = (prompt: string): StudioPromptDescriptor => {
|
|
5163
|
+
const normalizedPrompt = normalizePromptText(prompt) ?? prompt.trim();
|
|
5164
|
+
studioDirectRunChain = {
|
|
5165
|
+
id: randomUUID(),
|
|
5166
|
+
basePrompt: normalizedPrompt,
|
|
5167
|
+
steeringPrompts: [],
|
|
4959
5168
|
};
|
|
4960
|
-
|
|
4961
|
-
|
|
5169
|
+
queuedStudioDirectRequests = [];
|
|
5170
|
+
pendingStudioPromptMetadata = null;
|
|
5171
|
+
return buildStudioDirectRunPromptDescriptor(normalizedPrompt);
|
|
5172
|
+
};
|
|
4962
5173
|
|
|
4963
|
-
|
|
4964
|
-
|
|
4965
|
-
|
|
4966
|
-
return
|
|
5174
|
+
const enqueueStudioDirectSteeringRequest = (requestId: string, prompt: string): QueuedStudioDirectRequest | null => {
|
|
5175
|
+
if (!studioDirectRunChain) return null;
|
|
5176
|
+
const normalizedPrompt = normalizePromptText(prompt);
|
|
5177
|
+
if (!normalizedPrompt) return null;
|
|
5178
|
+
const descriptor = buildStudioQueuedSteerPromptDescriptor(studioDirectRunChain, normalizedPrompt);
|
|
5179
|
+
studioDirectRunChain.steeringPrompts.push(normalizedPrompt);
|
|
5180
|
+
const queuedRequest: QueuedStudioDirectRequest = {
|
|
5181
|
+
requestId,
|
|
5182
|
+
queuedAt: Date.now(),
|
|
5183
|
+
prompt: descriptor.prompt,
|
|
5184
|
+
promptMode: descriptor.promptMode,
|
|
5185
|
+
promptTriggerKind: descriptor.promptTriggerKind,
|
|
5186
|
+
promptSteeringCount: descriptor.promptSteeringCount,
|
|
5187
|
+
promptTriggerText: descriptor.promptTriggerText,
|
|
5188
|
+
};
|
|
5189
|
+
queuedStudioDirectRequests.push(queuedRequest);
|
|
5190
|
+
return queuedRequest;
|
|
5191
|
+
};
|
|
5192
|
+
|
|
5193
|
+
const claimQueuedStudioDirectRequestForPrompt = (_prompt: string | null): QueuedStudioDirectRequest | null => {
|
|
5194
|
+
if (queuedStudioDirectRequests.length === 0) return null;
|
|
5195
|
+
return queuedStudioDirectRequests.shift() ?? null;
|
|
5196
|
+
};
|
|
5197
|
+
|
|
5198
|
+
const activateQueuedStudioDirectRequestForPrompt = (prompt: string | null): QueuedStudioDirectRequest | null => {
|
|
5199
|
+
if (activeRequest) return null;
|
|
5200
|
+
const queuedRequest = claimQueuedStudioDirectRequestForPrompt(prompt);
|
|
5201
|
+
if (!queuedRequest) return null;
|
|
5202
|
+
activateRequest(queuedRequest.requestId, "direct", queuedRequest, { skipNotificationCleanup: true });
|
|
5203
|
+
return queuedRequest;
|
|
5204
|
+
};
|
|
5205
|
+
|
|
5206
|
+
const stageStudioPromptMetadata = (promptDescriptor: StudioPromptDescriptor | null | undefined) => {
|
|
5207
|
+
const descriptor = promptDescriptor ? buildStudioPromptDescriptor(
|
|
5208
|
+
promptDescriptor.prompt,
|
|
5209
|
+
promptDescriptor.promptMode,
|
|
5210
|
+
promptDescriptor.promptTriggerKind,
|
|
5211
|
+
promptDescriptor.promptSteeringCount,
|
|
5212
|
+
promptDescriptor.promptTriggerText,
|
|
5213
|
+
) : null;
|
|
5214
|
+
pendingStudioPromptMetadata = descriptor && descriptor.prompt ? descriptor : null;
|
|
5215
|
+
};
|
|
5216
|
+
|
|
5217
|
+
const persistPendingStudioPromptMetadata = () => {
|
|
5218
|
+
if (!pendingStudioPromptMetadata) return;
|
|
5219
|
+
const metadata = buildPersistedStudioPromptMetadata(pendingStudioPromptMetadata);
|
|
5220
|
+
try {
|
|
5221
|
+
pi.appendEntry(STUDIO_PROMPT_METADATA_CUSTOM_TYPE, metadata);
|
|
5222
|
+
emitDebugEvent("persist_prompt_metadata", {
|
|
5223
|
+
promptMode: metadata.promptMode,
|
|
5224
|
+
promptTriggerKind: metadata.promptTriggerKind,
|
|
5225
|
+
promptSteeringCount: metadata.promptSteeringCount,
|
|
5226
|
+
});
|
|
5227
|
+
} catch (error) {
|
|
5228
|
+
emitDebugEvent("persist_prompt_metadata_error", {
|
|
5229
|
+
message: error instanceof Error ? error.message : String(error),
|
|
5230
|
+
});
|
|
5231
|
+
} finally {
|
|
5232
|
+
pendingStudioPromptMetadata = null;
|
|
5233
|
+
}
|
|
4967
5234
|
};
|
|
4968
5235
|
|
|
4969
5236
|
const closeAllClients = (code = 4001, reason = "Session invalidated") => {
|
|
@@ -5011,6 +5278,8 @@ export default function (pi: ExtensionAPI) {
|
|
|
5011
5278
|
compactInProgress,
|
|
5012
5279
|
activeRequestId: activeRequest?.id ?? compactRequestId ?? null,
|
|
5013
5280
|
activeRequestKind: activeRequest?.kind ?? (compactInProgress ? "compact" : null),
|
|
5281
|
+
studioRunChainActive: isStudioDirectRunChainActive(),
|
|
5282
|
+
queuedSteeringCount: getQueuedStudioSteeringCount(),
|
|
5014
5283
|
lastResponse: lastStudioResponse,
|
|
5015
5284
|
responseHistory: studioResponseHistory,
|
|
5016
5285
|
initialDocument: initialStudioDocument,
|
|
@@ -5107,7 +5376,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
5107
5376
|
|
|
5108
5377
|
const lens = resolveLens(msg.lens, document);
|
|
5109
5378
|
const prompt = buildCritiquePrompt(document, lens);
|
|
5110
|
-
if (!beginRequest(msg.requestId, "critique", prompt)) return;
|
|
5379
|
+
if (!beginRequest(msg.requestId, "critique", buildStudioPromptDescriptor(prompt))) return;
|
|
5111
5380
|
|
|
5112
5381
|
try {
|
|
5113
5382
|
pi.sendUserMessage(prompt);
|
|
@@ -5134,7 +5403,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
5134
5403
|
return;
|
|
5135
5404
|
}
|
|
5136
5405
|
|
|
5137
|
-
if (!beginRequest(msg.requestId, "annotation", text)) return;
|
|
5406
|
+
if (!beginRequest(msg.requestId, "annotation", buildStudioPromptDescriptor(text))) return;
|
|
5138
5407
|
|
|
5139
5408
|
try {
|
|
5140
5409
|
pi.sendUserMessage(text);
|
|
@@ -5161,11 +5430,53 @@ export default function (pi: ExtensionAPI) {
|
|
|
5161
5430
|
return;
|
|
5162
5431
|
}
|
|
5163
5432
|
|
|
5164
|
-
if (
|
|
5433
|
+
if (canQueueStudioSteeringRequest()) {
|
|
5434
|
+
const queuedRequest = enqueueStudioDirectSteeringRequest(msg.requestId, msg.text);
|
|
5435
|
+
if (!queuedRequest) {
|
|
5436
|
+
sendToClient(client, {
|
|
5437
|
+
type: "error",
|
|
5438
|
+
requestId: msg.requestId,
|
|
5439
|
+
message: "Could not queue steering for the current run.",
|
|
5440
|
+
});
|
|
5441
|
+
return;
|
|
5442
|
+
}
|
|
5443
|
+
|
|
5444
|
+
try {
|
|
5445
|
+
pi.sendUserMessage(msg.text, { deliverAs: "steer" });
|
|
5446
|
+
broadcast({
|
|
5447
|
+
type: "request_queued",
|
|
5448
|
+
requestId: msg.requestId,
|
|
5449
|
+
kind: "direct",
|
|
5450
|
+
queueKind: "steer",
|
|
5451
|
+
studioRunChainActive: isStudioDirectRunChainActive(),
|
|
5452
|
+
queuedSteeringCount: getQueuedStudioSteeringCount(),
|
|
5453
|
+
});
|
|
5454
|
+
broadcastState();
|
|
5455
|
+
} catch (error) {
|
|
5456
|
+
queuedStudioDirectRequests = queuedStudioDirectRequests.filter((request) => request.requestId !== msg.requestId);
|
|
5457
|
+
if (studioDirectRunChain?.steeringPrompts.length) {
|
|
5458
|
+
studioDirectRunChain.steeringPrompts.pop();
|
|
5459
|
+
}
|
|
5460
|
+
sendToClient(client, {
|
|
5461
|
+
type: "error",
|
|
5462
|
+
requestId: msg.requestId,
|
|
5463
|
+
message: `Failed to queue steering request: ${error instanceof Error ? error.message : String(error)}`,
|
|
5464
|
+
});
|
|
5465
|
+
broadcastState();
|
|
5466
|
+
}
|
|
5467
|
+
return;
|
|
5468
|
+
}
|
|
5469
|
+
|
|
5470
|
+
const promptDescriptor = startStudioDirectRunChain(msg.text);
|
|
5471
|
+
if (!beginRequest(msg.requestId, "direct", promptDescriptor)) {
|
|
5472
|
+
clearStudioDirectRunState();
|
|
5473
|
+
return;
|
|
5474
|
+
}
|
|
5165
5475
|
|
|
5166
5476
|
try {
|
|
5167
5477
|
pi.sendUserMessage(msg.text);
|
|
5168
5478
|
} catch (error) {
|
|
5479
|
+
clearStudioDirectRunState();
|
|
5169
5480
|
clearActiveRequest();
|
|
5170
5481
|
sendToClient(client, {
|
|
5171
5482
|
type: "error",
|
|
@@ -5952,6 +6263,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
5952
6263
|
|
|
5953
6264
|
const stopServer = async () => {
|
|
5954
6265
|
if (!serverState) return;
|
|
6266
|
+
clearStudioDirectRunState();
|
|
5955
6267
|
clearActiveRequest();
|
|
5956
6268
|
clearPendingStudioCompletion();
|
|
5957
6269
|
clearPreparedPdfExports();
|
|
@@ -5984,6 +6296,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
5984
6296
|
|
|
5985
6297
|
pi.on("session_start", async (_event, ctx) => {
|
|
5986
6298
|
pendingTurnPrompt = null;
|
|
6299
|
+
clearStudioDirectRunState();
|
|
5987
6300
|
hydrateLatestAssistant(ctx.sessionManager.getBranch());
|
|
5988
6301
|
clearCompactionState();
|
|
5989
6302
|
agentBusy = false;
|
|
@@ -6001,6 +6314,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
6001
6314
|
});
|
|
6002
6315
|
|
|
6003
6316
|
pi.on("session_switch", async (_event, ctx) => {
|
|
6317
|
+
clearStudioDirectRunState();
|
|
6004
6318
|
clearActiveRequest({ notify: "Session switched. Studio request state cleared.", level: "warning" });
|
|
6005
6319
|
clearCompactionState();
|
|
6006
6320
|
pendingTurnPrompt = null;
|
|
@@ -6071,6 +6385,9 @@ export default function (pi: ExtensionAPI) {
|
|
|
6071
6385
|
pi.on("message_start", async (event) => {
|
|
6072
6386
|
const role = (event.message as { role?: string } | undefined)?.role;
|
|
6073
6387
|
emitDebugEvent("message_start", { role: role ?? "", activeRequestId: activeRequest?.id ?? null, activeRequestKind: activeRequest?.kind ?? null });
|
|
6388
|
+
if (role === "assistant") {
|
|
6389
|
+
persistPendingStudioPromptMetadata();
|
|
6390
|
+
}
|
|
6074
6391
|
if (agentBusy && role === "assistant") {
|
|
6075
6392
|
setTerminalActivity("responding");
|
|
6076
6393
|
}
|
|
@@ -6094,7 +6411,21 @@ export default function (pi: ExtensionAPI) {
|
|
|
6094
6411
|
});
|
|
6095
6412
|
|
|
6096
6413
|
if (role === "user") {
|
|
6097
|
-
|
|
6414
|
+
const userPrompt = extractUserText(event.message);
|
|
6415
|
+
pendingTurnPrompt = userPrompt;
|
|
6416
|
+
const activatedQueuedRequest = activateQueuedStudioDirectRequestForPrompt(userPrompt);
|
|
6417
|
+
if (activatedQueuedRequest) {
|
|
6418
|
+
emitDebugEvent("activate_queued_request", {
|
|
6419
|
+
requestId: activatedQueuedRequest.requestId,
|
|
6420
|
+
queuedSteeringCount: getQueuedStudioSteeringCount(),
|
|
6421
|
+
promptSteeringCount: activatedQueuedRequest.promptSteeringCount,
|
|
6422
|
+
});
|
|
6423
|
+
}
|
|
6424
|
+
if (activeRequest?.kind === "direct") {
|
|
6425
|
+
stageStudioPromptMetadata(getPromptDescriptorForActiveRequest(activeRequest));
|
|
6426
|
+
} else {
|
|
6427
|
+
pendingStudioPromptMetadata = null;
|
|
6428
|
+
}
|
|
6098
6429
|
return;
|
|
6099
6430
|
}
|
|
6100
6431
|
|
|
@@ -6125,14 +6456,20 @@ export default function (pi: ExtensionAPI) {
|
|
|
6125
6456
|
refreshContextUsage(ctx);
|
|
6126
6457
|
const latestHistoryItem = studioResponseHistory[studioResponseHistory.length - 1];
|
|
6127
6458
|
if (!latestHistoryItem || latestHistoryItem.markdown !== markdown) {
|
|
6128
|
-
const
|
|
6459
|
+
const fallbackPromptDescriptor = activeRequest
|
|
6460
|
+
? getPromptDescriptorForActiveRequest(activeRequest)
|
|
6461
|
+
: buildStudioPromptDescriptor(pendingTurnPrompt ?? latestSessionUserPrompt ?? null);
|
|
6129
6462
|
const fallbackHistoryItem: StudioResponseHistoryItem = {
|
|
6130
6463
|
id: randomUUID(),
|
|
6131
6464
|
markdown,
|
|
6132
6465
|
thinking,
|
|
6133
6466
|
timestamp: Date.now(),
|
|
6134
6467
|
kind: inferStudioResponseKind(markdown),
|
|
6135
|
-
prompt:
|
|
6468
|
+
prompt: fallbackPromptDescriptor.prompt,
|
|
6469
|
+
promptMode: fallbackPromptDescriptor.promptMode,
|
|
6470
|
+
promptTriggerKind: fallbackPromptDescriptor.promptTriggerKind,
|
|
6471
|
+
promptSteeringCount: fallbackPromptDescriptor.promptSteeringCount,
|
|
6472
|
+
promptTriggerText: fallbackPromptDescriptor.promptTriggerText,
|
|
6136
6473
|
};
|
|
6137
6474
|
const nextHistory = [...studioResponseHistory, fallbackHistoryItem];
|
|
6138
6475
|
studioResponseHistory = nextHistory.slice(-RESPONSE_HISTORY_LIMIT);
|
|
@@ -6201,6 +6538,9 @@ export default function (pi: ExtensionAPI) {
|
|
|
6201
6538
|
pi.on("agent_end", async () => {
|
|
6202
6539
|
agentBusy = false;
|
|
6203
6540
|
pendingTurnPrompt = null;
|
|
6541
|
+
pendingStudioPromptMetadata = null;
|
|
6542
|
+
const hadStudioDirectRunChain = isStudioDirectRunChainActive();
|
|
6543
|
+
const queuedSteeringCount = getQueuedStudioSteeringCount();
|
|
6204
6544
|
refreshContextUsage();
|
|
6205
6545
|
emitDebugEvent("agent_end", {
|
|
6206
6546
|
activeRequestId: activeRequest?.id ?? null,
|
|
@@ -6208,7 +6548,10 @@ export default function (pi: ExtensionAPI) {
|
|
|
6208
6548
|
suppressedRequestId: suppressedStudioResponse?.requestId ?? null,
|
|
6209
6549
|
suppressedRequestKind: suppressedStudioResponse?.kind ?? null,
|
|
6210
6550
|
pendingCompletionKind: pendingStudioCompletionKind,
|
|
6551
|
+
hadStudioDirectRunChain,
|
|
6552
|
+
queuedSteeringCount,
|
|
6211
6553
|
});
|
|
6554
|
+
clearStudioDirectRunState();
|
|
6212
6555
|
setTerminalActivity("idle");
|
|
6213
6556
|
if (activeRequest) {
|
|
6214
6557
|
const requestId = activeRequest.id;
|
|
@@ -6221,6 +6564,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
6221
6564
|
clearPendingStudioCompletion();
|
|
6222
6565
|
} else {
|
|
6223
6566
|
flushPendingStudioCompletionNotification();
|
|
6567
|
+
broadcastState();
|
|
6224
6568
|
}
|
|
6225
6569
|
suppressedStudioResponse = null;
|
|
6226
6570
|
});
|
|
@@ -6228,6 +6572,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
6228
6572
|
pi.on("session_shutdown", async () => {
|
|
6229
6573
|
lastCommandCtx = null;
|
|
6230
6574
|
agentBusy = false;
|
|
6575
|
+
clearStudioDirectRunState();
|
|
6231
6576
|
clearPendingStudioCompletion();
|
|
6232
6577
|
clearPreparedPdfExports();
|
|
6233
6578
|
clearCompactionState();
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-studio",
|
|
3
|
-
"version": "0.5.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.5.29",
|
|
4
|
+
"description": "Two-pane browser workspace for pi with prompt/response editing, annotations, critiques, prompt/response history, and live Markdown/LaTeX/code preview",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"repository": {
|