pi-studio 0.5.15 → 0.5.17
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 +10 -0
- package/index.ts +40 -15
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,16 @@ All notable changes to `pi-studio` are documented here.
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [0.5.17] — 2026-03-17
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
- Studio preview and PDF rendering now accept Markdown lists without a preceding blank line, so common model output like `What I read:\n- item` renders as a real list instead of collapsing into a paragraph.
|
|
11
|
+
|
|
12
|
+
## [0.5.16] — 2026-03-17
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
- Response-history prompt loading now keeps the correct generating prompt for both Studio editor-sent requests and prompts entered directly in the terminal, instead of sometimes reusing stale editor text.
|
|
16
|
+
|
|
7
17
|
## [0.5.15] — 2026-03-16
|
|
8
18
|
|
|
9
19
|
### Added
|
package/index.ts
CHANGED
|
@@ -29,6 +29,7 @@ interface StudioServerState {
|
|
|
29
29
|
interface ActiveStudioRequest {
|
|
30
30
|
id: string;
|
|
31
31
|
kind: StudioRequestKind;
|
|
32
|
+
prompt: string | null;
|
|
32
33
|
timer: NodeJS.Timeout;
|
|
33
34
|
startedAt: number;
|
|
34
35
|
}
|
|
@@ -1589,7 +1590,7 @@ async function preprocessStudioMermaidForPdf(markdown: string, workDir: string):
|
|
|
1589
1590
|
|
|
1590
1591
|
async function renderStudioMarkdownWithPandoc(markdown: string, isLatex?: boolean, resourcePath?: string): Promise<string> {
|
|
1591
1592
|
const pandocCommand = process.env.PANDOC_PATH?.trim() || "pandoc";
|
|
1592
|
-
const inputFormat = isLatex ? "latex" : "markdown+tex_math_dollars+tex_math_single_backslash+tex_math_double_backslash+autolink_bare_uris-raw_html";
|
|
1593
|
+
const inputFormat = isLatex ? "latex" : "markdown+lists_without_preceding_blankline+tex_math_dollars+tex_math_single_backslash+tex_math_double_backslash+autolink_bare_uris-raw_html";
|
|
1593
1594
|
const args = ["-f", inputFormat, "-t", "html5", "--mathml", "--wrap=none"];
|
|
1594
1595
|
if (resourcePath) {
|
|
1595
1596
|
args.push(`--resource-path=${resourcePath}`);
|
|
@@ -1818,7 +1819,7 @@ async function renderStudioPdfWithPandoc(
|
|
|
1818
1819
|
};
|
|
1819
1820
|
|
|
1820
1821
|
if (!isLatex && effectiveEditorLanguage === "diff") {
|
|
1821
|
-
const inputFormat = "markdown+tex_math_dollars+autolink_bare_uris+superscript+subscript-raw_html";
|
|
1822
|
+
const inputFormat = "markdown+lists_without_preceding_blankline+tex_math_dollars+autolink_bare_uris+superscript+subscript-raw_html";
|
|
1822
1823
|
const diffMarkdown = prepareStudioPdfMarkdown(markdown, false, effectiveEditorLanguage);
|
|
1823
1824
|
try {
|
|
1824
1825
|
return await runPandocPdfExport(inputFormat, diffMarkdown);
|
|
@@ -1834,7 +1835,7 @@ async function renderStudioPdfWithPandoc(
|
|
|
1834
1835
|
|
|
1835
1836
|
const inputFormat = isLatex
|
|
1836
1837
|
? "latex"
|
|
1837
|
-
: "markdown+tex_math_dollars+tex_math_single_backslash+tex_math_double_backslash+autolink_bare_uris+superscript+subscript-raw_html";
|
|
1838
|
+
: "markdown+lists_without_preceding_blankline+tex_math_dollars+tex_math_single_backslash+tex_math_double_backslash+autolink_bare_uris+superscript+subscript-raw_html";
|
|
1838
1839
|
const normalizedMarkdown = prepareStudioPdfMarkdown(markdown, isLatex, effectiveEditorLanguage);
|
|
1839
1840
|
|
|
1840
1841
|
const tempDir = join(tmpdir(), `pi-studio-pdf-${Date.now()}-${randomUUID()}`);
|
|
@@ -2199,6 +2200,12 @@ function extractLatestAssistantFromEntries(entries: SessionEntry[]): string | nu
|
|
|
2199
2200
|
return null;
|
|
2200
2201
|
}
|
|
2201
2202
|
|
|
2203
|
+
function normalizePromptText(text: string | null | undefined): string | null {
|
|
2204
|
+
if (typeof text !== "string") return null;
|
|
2205
|
+
const trimmed = text.trim();
|
|
2206
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
2207
|
+
}
|
|
2208
|
+
|
|
2202
2209
|
function extractUserText(message: unknown): string | null {
|
|
2203
2210
|
const msg = message as {
|
|
2204
2211
|
role?: string;
|
|
@@ -2207,8 +2214,7 @@ function extractUserText(message: unknown): string | null {
|
|
|
2207
2214
|
if (!msg || msg.role !== "user") return null;
|
|
2208
2215
|
|
|
2209
2216
|
if (typeof msg.content === "string") {
|
|
2210
|
-
|
|
2211
|
-
return text.length > 0 ? text : null;
|
|
2217
|
+
return normalizePromptText(msg.content);
|
|
2212
2218
|
}
|
|
2213
2219
|
|
|
2214
2220
|
if (!Array.isArray(msg.content)) return null;
|
|
@@ -2230,8 +2236,16 @@ function extractUserText(message: unknown): string | null {
|
|
|
2230
2236
|
}
|
|
2231
2237
|
}
|
|
2232
2238
|
|
|
2233
|
-
|
|
2234
|
-
|
|
2239
|
+
return normalizePromptText(blocks.join("\n\n"));
|
|
2240
|
+
}
|
|
2241
|
+
|
|
2242
|
+
function findLatestUserPrompt(entries: SessionEntry[]): string | null {
|
|
2243
|
+
let latestPrompt: string | null = null;
|
|
2244
|
+
for (const entry of entries) {
|
|
2245
|
+
if (!entry || entry.type !== "message") continue;
|
|
2246
|
+
latestPrompt = extractUserText((entry as { message?: unknown }).message) ?? latestPrompt;
|
|
2247
|
+
}
|
|
2248
|
+
return latestPrompt;
|
|
2235
2249
|
}
|
|
2236
2250
|
|
|
2237
2251
|
function parseEntryTimestamp(timestamp: unknown): number {
|
|
@@ -2945,6 +2959,8 @@ export default function (pi: ExtensionAPI) {
|
|
|
2945
2959
|
let currentModelLabel = "none";
|
|
2946
2960
|
let terminalSessionLabel = buildTerminalSessionLabel(studioCwd);
|
|
2947
2961
|
let studioResponseHistory: StudioResponseHistoryItem[] = [];
|
|
2962
|
+
let latestSessionUserPrompt: string | null = null;
|
|
2963
|
+
let pendingTurnPrompt: string | null = null;
|
|
2948
2964
|
let contextUsageSnapshot: StudioContextUsageSnapshot = {
|
|
2949
2965
|
tokens: null,
|
|
2950
2966
|
contextWindow: null,
|
|
@@ -3261,6 +3277,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
3261
3277
|
};
|
|
3262
3278
|
|
|
3263
3279
|
const syncStudioResponseHistory = (entries: SessionEntry[]) => {
|
|
3280
|
+
latestSessionUserPrompt = findLatestUserPrompt(entries);
|
|
3264
3281
|
studioResponseHistory = buildResponseHistoryFromEntries(entries, RESPONSE_HISTORY_LIMIT);
|
|
3265
3282
|
const latest = studioResponseHistory[studioResponseHistory.length - 1];
|
|
3266
3283
|
if (!latest) {
|
|
@@ -3470,7 +3487,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
3470
3487
|
return { ok: true, kind };
|
|
3471
3488
|
};
|
|
3472
3489
|
|
|
3473
|
-
const beginRequest = (requestId: string, kind: StudioRequestKind): boolean => {
|
|
3490
|
+
const beginRequest = (requestId: string, kind: StudioRequestKind, prompt?: string | null): boolean => {
|
|
3474
3491
|
suppressedStudioResponse = null;
|
|
3475
3492
|
emitDebugEvent("begin_request_attempt", {
|
|
3476
3493
|
requestId,
|
|
@@ -3501,6 +3518,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
3501
3518
|
activeRequest = {
|
|
3502
3519
|
id: requestId,
|
|
3503
3520
|
kind,
|
|
3521
|
+
prompt: normalizePromptText(prompt),
|
|
3504
3522
|
startedAt: Date.now(),
|
|
3505
3523
|
timer,
|
|
3506
3524
|
};
|
|
@@ -3652,10 +3670,9 @@ export default function (pi: ExtensionAPI) {
|
|
|
3652
3670
|
return;
|
|
3653
3671
|
}
|
|
3654
3672
|
|
|
3655
|
-
if (!beginRequest(msg.requestId, "critique")) return;
|
|
3656
|
-
|
|
3657
3673
|
const lens = resolveLens(msg.lens, document);
|
|
3658
3674
|
const prompt = buildCritiquePrompt(document, lens);
|
|
3675
|
+
if (!beginRequest(msg.requestId, "critique", prompt)) return;
|
|
3659
3676
|
|
|
3660
3677
|
try {
|
|
3661
3678
|
pi.sendUserMessage(prompt);
|
|
@@ -3682,7 +3699,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
3682
3699
|
return;
|
|
3683
3700
|
}
|
|
3684
3701
|
|
|
3685
|
-
if (!beginRequest(msg.requestId, "annotation")) return;
|
|
3702
|
+
if (!beginRequest(msg.requestId, "annotation", text)) return;
|
|
3686
3703
|
|
|
3687
3704
|
try {
|
|
3688
3705
|
pi.sendUserMessage(text);
|
|
@@ -3709,7 +3726,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
3709
3726
|
return;
|
|
3710
3727
|
}
|
|
3711
3728
|
|
|
3712
|
-
if (!beginRequest(msg.requestId, "direct")) return;
|
|
3729
|
+
if (!beginRequest(msg.requestId, "direct", msg.text)) return;
|
|
3713
3730
|
|
|
3714
3731
|
try {
|
|
3715
3732
|
pi.sendUserMessage(msg.text);
|
|
@@ -4419,6 +4436,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
4419
4436
|
};
|
|
4420
4437
|
|
|
4421
4438
|
pi.on("session_start", async (_event, ctx) => {
|
|
4439
|
+
pendingTurnPrompt = null;
|
|
4422
4440
|
hydrateLatestAssistant(ctx.sessionManager.getBranch());
|
|
4423
4441
|
clearCompactionState();
|
|
4424
4442
|
agentBusy = false;
|
|
@@ -4436,6 +4454,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
4436
4454
|
pi.on("session_switch", async (_event, ctx) => {
|
|
4437
4455
|
clearActiveRequest({ notify: "Session switched. Studio request state cleared.", level: "warning" });
|
|
4438
4456
|
clearCompactionState();
|
|
4457
|
+
pendingTurnPrompt = null;
|
|
4439
4458
|
lastCommandCtx = null;
|
|
4440
4459
|
hydrateLatestAssistant(ctx.sessionManager.getBranch());
|
|
4441
4460
|
agentBusy = false;
|
|
@@ -4523,6 +4542,11 @@ export default function (pi: ExtensionAPI) {
|
|
|
4523
4542
|
activeRequestKind: activeRequest?.kind ?? null,
|
|
4524
4543
|
});
|
|
4525
4544
|
|
|
4545
|
+
if (role === "user") {
|
|
4546
|
+
pendingTurnPrompt = extractUserText(event.message);
|
|
4547
|
+
return;
|
|
4548
|
+
}
|
|
4549
|
+
|
|
4526
4550
|
// Assistant is handing off to tool calls; request is still in progress.
|
|
4527
4551
|
if (stopReason === "toolUse") {
|
|
4528
4552
|
emitDebugEvent("message_end_tool_use", {
|
|
@@ -4536,6 +4560,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
4536
4560
|
if (!markdown) return;
|
|
4537
4561
|
|
|
4538
4562
|
if (suppressedStudioResponse) {
|
|
4563
|
+
pendingTurnPrompt = null;
|
|
4539
4564
|
emitDebugEvent("suppressed_cancelled_response", {
|
|
4540
4565
|
requestId: suppressedStudioResponse.requestId,
|
|
4541
4566
|
kind: suppressedStudioResponse.kind,
|
|
@@ -4549,9 +4574,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
4549
4574
|
refreshContextUsage(ctx);
|
|
4550
4575
|
const latestHistoryItem = studioResponseHistory[studioResponseHistory.length - 1];
|
|
4551
4576
|
if (!latestHistoryItem || latestHistoryItem.markdown !== markdown) {
|
|
4552
|
-
const fallbackPrompt =
|
|
4553
|
-
? studioResponseHistory[studioResponseHistory.length - 1]?.prompt ?? null
|
|
4554
|
-
: null;
|
|
4577
|
+
const fallbackPrompt = activeRequest?.prompt ?? pendingTurnPrompt ?? latestSessionUserPrompt ?? null;
|
|
4555
4578
|
const fallbackHistoryItem: StudioResponseHistoryItem = {
|
|
4556
4579
|
id: randomUUID(),
|
|
4557
4580
|
markdown,
|
|
@@ -4567,6 +4590,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
4567
4590
|
const latestItem = studioResponseHistory[studioResponseHistory.length - 1];
|
|
4568
4591
|
const responseTimestamp = latestItem?.timestamp ?? Date.now();
|
|
4569
4592
|
const responseThinking = latestItem?.thinking ?? thinking ?? null;
|
|
4593
|
+
pendingTurnPrompt = null;
|
|
4570
4594
|
|
|
4571
4595
|
if (activeRequest) {
|
|
4572
4596
|
const requestId = activeRequest.id;
|
|
@@ -4627,6 +4651,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
4627
4651
|
|
|
4628
4652
|
pi.on("agent_end", async () => {
|
|
4629
4653
|
agentBusy = false;
|
|
4654
|
+
pendingTurnPrompt = null;
|
|
4630
4655
|
refreshContextUsage();
|
|
4631
4656
|
emitDebugEvent("agent_end", {
|
|
4632
4657
|
activeRequestId: activeRequest?.id ?? null,
|