From 7683467a50b0647281762d6391bac14098b898d6 Mon Sep 17 00:00:00 2001 From: Jasmin Le Roux Date: Sat, 18 Jul 2026 20:51:40 +0200 Subject: [PATCH 1/3] feat(pi): allow hiding sideshow TUI status --- .changeset/quiet-pandas-watch.md | 5 +++ docs/connecting-agents.md | 4 ++ extensions/sideshow.js | 26 +++++++++---- test/piExtension.test.ts | 64 ++++++++++++++++++++++++++++++++ 4 files changed, 92 insertions(+), 7 deletions(-) create mode 100644 .changeset/quiet-pandas-watch.md create mode 100644 test/piExtension.test.ts diff --git a/.changeset/quiet-pandas-watch.md b/.changeset/quiet-pandas-watch.md new file mode 100644 index 0000000..696d132 --- /dev/null +++ b/.changeset/quiet-pandas-watch.md @@ -0,0 +1,5 @@ +--- +"sideshow": patch +--- + +Allow Pi-compatible agents to hide the persistent Sideshow TUI status with `SIDESHOW_TUI_STATUS=0` while retaining native tools and trace synchronization. diff --git a/docs/connecting-agents.md b/docs/connecting-agents.md index 77fb0ae..b312cf7 100644 --- a/docs/connecting-agents.md +++ b/docs/connecting-agents.md @@ -41,6 +41,10 @@ pi install npm:sideshow pi -e npm:sideshow ``` +The extension shows its current server or session in Pi's TUI status area by +default. Set `SIDESHOW_TUI_STATUS=0` before launching Pi to hide that status +without disabling the native tools or automatic trace synchronization. + ## MCP Tools: `publish_post`, `update_post`, `list_posts`, `get_post`, diff --git a/extensions/sideshow.js b/extensions/sideshow.js index ade1cf2..4d42e64 100644 --- a/extensions/sideshow.js +++ b/extensions/sideshow.js @@ -104,6 +104,10 @@ function agentName() { return process.env.SIDESHOW_AGENT || "pi"; } +function showTuiStatus() { + return process.env.SIDESHOW_TUI_STATUS !== "0"; +} + function authHeaders(extra = {}) { return { ...(process.env.SIDESHOW_TOKEN @@ -379,12 +383,16 @@ export default function sideshowExtension(pi) { pi.on("session_start", (_event, ctx) => { state.sessionId = reconstructSession(ctx); - ctx.ui.setStatus( - "sideshow", - state.sessionId - ? `sideshow ${state.sessionId}` - : `sideshow ${baseUrl().replace(/^https?:\/\//, "")}`, - ); + if (showTuiStatus()) { + ctx.ui.setStatus( + "sideshow", + state.sessionId + ? `sideshow ${state.sessionId}` + : `sideshow ${baseUrl().replace(/^https?:\/\//, "")}`, + ); + } else { + ctx.ui.setStatus("sideshow", undefined); + } }); pi.on("turn_end", async (_event, ctx) => { @@ -403,7 +411,11 @@ export default function sideshowExtension(pi) { const command = args.trim(); if (command === "reset") { state.sessionId = process.env.SIDESHOW_SESSION || undefined; - ctx.ui.setStatus("sideshow", `sideshow ${baseUrl().replace(/^https?:\/\//, "")}`); + if (showTuiStatus()) { + ctx.ui.setStatus("sideshow", `sideshow ${baseUrl().replace(/^https?:\/\//, "")}`); + } else { + ctx.ui.setStatus("sideshow", undefined); + } ctx.ui.notify("Reset remembered sideshow session", "info"); return; } diff --git a/test/piExtension.test.ts b/test/piExtension.test.ts new file mode 100644 index 0000000..73c9bdf --- /dev/null +++ b/test/piExtension.test.ts @@ -0,0 +1,64 @@ +import assert from "node:assert/strict"; +import { afterEach, describe, it } from "node:test"; + +import sideshowExtension from "../extensions/sideshow.js"; + +const originalTuiStatus = process.env.SIDESHOW_TUI_STATUS; + +afterEach(() => { + if (originalTuiStatus === undefined) delete process.env.SIDESHOW_TUI_STATUS; + else process.env.SIDESHOW_TUI_STATUS = originalTuiStatus; +}); + +function loadExtension() { + const handlers = new Map(); + const commands = new Map(); + const pi = { + on(event, handler) { + handlers.set(event, handler); + }, + registerCommand(name, command) { + commands.set(name, command); + }, + registerTool() {}, + }; + sideshowExtension(pi); + return { handlers, commands }; +} + +function context(statuses) { + return { + sessionManager: { getBranch: () => [] }, + ui: { + setStatus: (key, text) => statuses.push([key, text]), + notify() {}, + }, + }; +} + +describe("Pi extension TUI status", () => { + it("shows status by default", () => { + delete process.env.SIDESHOW_TUI_STATUS; + const { handlers } = loadExtension(); + const statuses = []; + + handlers.get("session_start")({}, context(statuses)); + + assert.deepEqual(statuses, [["sideshow", "sideshow localhost:8228"]]); + }); + + it("clears status when SIDESHOW_TUI_STATUS=0", async () => { + process.env.SIDESHOW_TUI_STATUS = "0"; + const { handlers, commands } = loadExtension(); + const statuses = []; + const ctx = context(statuses); + + handlers.get("session_start")({}, ctx); + await commands.get("sideshow").handler("reset", ctx); + + assert.deepEqual(statuses, [ + ["sideshow", undefined], + ["sideshow", undefined], + ]); + }); +}); From 750bda438e2a362c2eeb74a9b1397efdde849662 Mon Sep 17 00:00:00 2001 From: Jasmin Le Roux Date: Sun, 19 Jul 2026 12:17:54 +0200 Subject: [PATCH 2/3] fix(pi): type extension status tests --- extensions/sideshow.d.ts | 1 + test/piExtension.test.ts | 33 ++++++++++++++++++++++----------- 2 files changed, 23 insertions(+), 11 deletions(-) create mode 100644 extensions/sideshow.d.ts diff --git a/extensions/sideshow.d.ts b/extensions/sideshow.d.ts new file mode 100644 index 0000000..ed5ee20 --- /dev/null +++ b/extensions/sideshow.d.ts @@ -0,0 +1 @@ +export default function sideshowExtension(pi: Record): void; diff --git a/test/piExtension.test.ts b/test/piExtension.test.ts index 73c9bdf..8a2f7e1 100644 --- a/test/piExtension.test.ts +++ b/test/piExtension.test.ts @@ -5,19 +5,30 @@ import sideshowExtension from "../extensions/sideshow.js"; const originalTuiStatus = process.env.SIDESHOW_TUI_STATUS; +type Status = [key: string, text: string | undefined]; +interface Context { + sessionManager: { getBranch: () => never[] }; + ui: { + setStatus: (key: string, text: string | undefined) => number; + notify: () => void; + }; +} +type Handler = (event: object, context: Context) => void; +type Command = { handler: (args: string, context: Context) => Promise }; + afterEach(() => { if (originalTuiStatus === undefined) delete process.env.SIDESHOW_TUI_STATUS; else process.env.SIDESHOW_TUI_STATUS = originalTuiStatus; }); function loadExtension() { - const handlers = new Map(); - const commands = new Map(); + const handlers = new Map(); + const commands = new Map(); const pi = { - on(event, handler) { + on(event: string, handler: Handler) { handlers.set(event, handler); }, - registerCommand(name, command) { + registerCommand(name: string, command: Command) { commands.set(name, command); }, registerTool() {}, @@ -26,11 +37,11 @@ function loadExtension() { return { handlers, commands }; } -function context(statuses) { +function context(statuses: Status[]) { return { sessionManager: { getBranch: () => [] }, ui: { - setStatus: (key, text) => statuses.push([key, text]), + setStatus: (key: string, text: string | undefined) => statuses.push([key, text]), notify() {}, }, }; @@ -40,9 +51,9 @@ describe("Pi extension TUI status", () => { it("shows status by default", () => { delete process.env.SIDESHOW_TUI_STATUS; const { handlers } = loadExtension(); - const statuses = []; + const statuses: Status[] = []; - handlers.get("session_start")({}, context(statuses)); + handlers.get("session_start")!({}, context(statuses)); assert.deepEqual(statuses, [["sideshow", "sideshow localhost:8228"]]); }); @@ -50,11 +61,11 @@ describe("Pi extension TUI status", () => { it("clears status when SIDESHOW_TUI_STATUS=0", async () => { process.env.SIDESHOW_TUI_STATUS = "0"; const { handlers, commands } = loadExtension(); - const statuses = []; + const statuses: Status[] = []; const ctx = context(statuses); - handlers.get("session_start")({}, ctx); - await commands.get("sideshow").handler("reset", ctx); + handlers.get("session_start")!({}, ctx); + await commands.get("sideshow")!.handler("reset", ctx); assert.deepEqual(statuses, [ ["sideshow", undefined], From 9728646bde3dff8a684ffff561f6388d8775b3c9 Mon Sep 17 00:00:00 2001 From: Ben Vinegar Date: Thu, 23 Jul 2026 10:10:39 -0400 Subject: [PATCH 3/3] feat(pi): make sideshow status session-aware --- .changeset/quiet-pandas-watch.md | 2 +- docs/connecting-agents.md | 8 +- extensions/sideshow.d.ts | 1 - extensions/sideshow.js | 60 ++++---- package.json | 2 +- test/piExtension.test.ts | 237 +++++++++++++++++++++++++++++-- tsconfig.json | 1 + 7 files changed, 264 insertions(+), 47 deletions(-) delete mode 100644 extensions/sideshow.d.ts diff --git a/.changeset/quiet-pandas-watch.md b/.changeset/quiet-pandas-watch.md index 696d132..1193f04 100644 --- a/.changeset/quiet-pandas-watch.md +++ b/.changeset/quiet-pandas-watch.md @@ -2,4 +2,4 @@ "sideshow": patch --- -Allow Pi-compatible agents to hide the persistent Sideshow TUI status with `SIDESHOW_TUI_STATUS=0` while retaining native tools and trace synchronization. +Keep Pi's TUI quiet until the current conversation connects to a Sideshow session, keep the status current when tools or conversation-tree navigation switch sessions, and allow hiding it entirely with `SIDESHOW_TUI_STATUS=0` without disabling native tools or trace synchronization. diff --git a/docs/connecting-agents.md b/docs/connecting-agents.md index b312cf7..5bbb8b8 100644 --- a/docs/connecting-agents.md +++ b/docs/connecting-agents.md @@ -41,9 +41,11 @@ pi install npm:sideshow pi -e npm:sideshow ``` -The extension shows its current server or session in Pi's TUI status area by -default. Set `SIDESHOW_TUI_STATUS=0` before launching Pi to hide that status -without disabling the native tools or automatic trace synchronization. +After the current conversation connects to a Sideshow session, the extension +shows that session in Pi's TUI status area and keeps it current when tools or +conversation-tree navigation switch sessions. Idle conversations stay quiet. Set `SIDESHOW_TUI_STATUS=0` before +launching Pi to hide the status entirely without disabling the native tools or +automatic trace synchronization. ## MCP diff --git a/extensions/sideshow.d.ts b/extensions/sideshow.d.ts deleted file mode 100644 index ed5ee20..0000000 --- a/extensions/sideshow.d.ts +++ /dev/null @@ -1 +0,0 @@ -export default function sideshowExtension(pi: Record): void; diff --git a/extensions/sideshow.js b/extensions/sideshow.js index 4d42e64..5a17797 100644 --- a/extensions/sideshow.js +++ b/extensions/sideshow.js @@ -26,6 +26,13 @@ const CONTENT_TYPES = { const feedbackGuideline = "Sideshow tool results may include userFeedback from browser comments; treat it as user instruction and respond or update the surface."; +const SESSION_REMEMBERING_TOOLS = new Set([ + "sideshow_publish_surface", + "sideshow_update_surface", + "sideshow_reply_to_user", + "sideshow_upload_asset", +]); + const partSchema = { type: "object", properties: { @@ -186,8 +193,17 @@ async function requestText(path) { return text; } -function rememberSession(state, sessionId) { - if (typeof sessionId === "string" && sessionId) state.sessionId = sessionId; +function updateTuiStatus(state, ctx) { + ctx.ui.setStatus( + "sideshow", + showTuiStatus() && state.sessionId ? `sideshow ${state.sessionId}` : undefined, + ); +} + +function rememberSession(state, sessionId, ctx) { + if (typeof sessionId !== "string" || !sessionId) return; + state.sessionId = sessionId; + updateTuiStatus(state, ctx); } function urlForSurface(surfaceId) { @@ -367,7 +383,7 @@ function reconstructSession(ctx) { for (const entry of ctx.sessionManager.getBranch()) { const message = entry?.type === "message" ? entry.message : undefined; if (message?.role !== "toolResult") continue; - if (!String(message.toolName ?? "").startsWith("sideshow_")) continue; + if (!SESSION_REMEMBERING_TOOLS.has(message.toolName)) continue; const details = message.details; if (details && typeof details.sessionId === "string") sessionId = details.sessionId; if (details?.surface && typeof details.surface.sessionId === "string") @@ -381,19 +397,13 @@ function reconstructSession(ctx) { export default function sideshowExtension(pi) { const state = { sessionId: process.env.SIDESHOW_SESSION || undefined }; - pi.on("session_start", (_event, ctx) => { + const restoreSession = (ctx) => { state.sessionId = reconstructSession(ctx); - if (showTuiStatus()) { - ctx.ui.setStatus( - "sideshow", - state.sessionId - ? `sideshow ${state.sessionId}` - : `sideshow ${baseUrl().replace(/^https?:\/\//, "")}`, - ); - } else { - ctx.ui.setStatus("sideshow", undefined); - } - }); + updateTuiStatus(state, ctx); + }; + + pi.on("session_start", (_event, ctx) => restoreSession(ctx)); + pi.on("session_tree", (_event, ctx) => restoreSession(ctx)); pi.on("turn_end", async (_event, ctx) => { if (!state.sessionId) return; @@ -411,11 +421,7 @@ export default function sideshowExtension(pi) { const command = args.trim(); if (command === "reset") { state.sessionId = process.env.SIDESHOW_SESSION || undefined; - if (showTuiStatus()) { - ctx.ui.setStatus("sideshow", `sideshow ${baseUrl().replace(/^https?:\/\//, "")}`); - } else { - ctx.ui.setStatus("sideshow", undefined); - } + updateTuiStatus(state, ctx); ctx.ui.notify("Reset remembered sideshow session", "info"); return; } @@ -510,7 +516,7 @@ export default function sideshowExtension(pi) { method: "POST", body: JSON.stringify(body), }); - rememberSession(state, surface.sessionId); + rememberSession(state, surface.sessionId, ctx); const url = urlForSurface(surface.id); return { content: [ @@ -543,13 +549,13 @@ export default function sideshowExtension(pi) { }, required: ["id"], }, - async execute(_toolCallId, params) { + async execute(_toolCallId, params, _signal, _onUpdate, ctx) { const body = { title: params.title, parts: params.parts }; const surface = await requestJson(`/api/surfaces/${encodeURIComponent(params.id)}`, { method: "PUT", body: JSON.stringify(body), }); - rememberSession(state, surface.sessionId); + rememberSession(state, surface.sessionId, ctx); const url = urlForSurface(surface.id); return { content: [ @@ -626,7 +632,7 @@ export default function sideshowExtension(pi) { }, required: ["message"], }, - async execute(_toolCallId, params) { + async execute(_toolCallId, params, _signal, _onUpdate, ctx) { const body = { text: params.message, surface: params.surfaceId, @@ -637,7 +643,7 @@ export default function sideshowExtension(pi) { method: "POST", body: JSON.stringify(body), }); - rememberSession(state, comment.sessionId); + rememberSession(state, comment.sessionId, ctx); return { content: [ { @@ -776,7 +782,7 @@ export default function sideshowExtension(pi) { body: JSON.stringify({ agent: agentName(), title: params.sessionTitle, cwd: ctx.cwd }), }); session = created.id; - rememberSession(state, session); + rememberSession(state, session, ctx); } const query = new URLSearchParams(); @@ -790,7 +796,7 @@ export default function sideshowExtension(pi) { headers: { "content-type": contentType }, body: bytes, }); - rememberSession(state, asset.sessionId); + rememberSession(state, asset.sessionId, ctx); return { content: [ { diff --git a/package.json b/package.json index 991083f..e3a8fad 100644 --- a/package.json +++ b/package.json @@ -139,7 +139,7 @@ "packageManager": "npm@11.17.0", "pi": { "extensions": [ - "./extensions" + "./extensions/sideshow.js" ], "skills": [ "./skills" diff --git a/test/piExtension.test.ts b/test/piExtension.test.ts index 8a2f7e1..3f31d1e 100644 --- a/test/piExtension.test.ts +++ b/test/piExtension.test.ts @@ -4,26 +4,40 @@ import { afterEach, describe, it } from "node:test"; import sideshowExtension from "../extensions/sideshow.js"; const originalTuiStatus = process.env.SIDESHOW_TUI_STATUS; +const originalSession = process.env.SIDESHOW_SESSION; type Status = [key: string, text: string | undefined]; +type BranchEntry = { + type: "message"; + message: { + role: "toolResult"; + toolName: string; + details?: { sessionId?: string }; + }; +}; interface Context { - sessionManager: { getBranch: () => never[] }; + cwd: string; + sessionManager: { getBranch: () => BranchEntry[] }; ui: { - setStatus: (key: string, text: string | undefined) => number; + setStatus: (key: string, text: string | undefined) => void; notify: () => void; }; } -type Handler = (event: object, context: Context) => void; +type Handler = (event: object, context: Context) => unknown; type Command = { handler: (args: string, context: Context) => Promise }; +type Tool = { name: string; execute: (...args: unknown[]) => Promise }; afterEach(() => { if (originalTuiStatus === undefined) delete process.env.SIDESHOW_TUI_STATUS; else process.env.SIDESHOW_TUI_STATUS = originalTuiStatus; + if (originalSession === undefined) delete process.env.SIDESHOW_SESSION; + else process.env.SIDESHOW_SESSION = originalSession; }); function loadExtension() { const handlers = new Map(); const commands = new Map(); + const tools = new Map(); const pi = { on(event: string, handler: Handler) { handlers.set(event, handler); @@ -31,45 +45,240 @@ function loadExtension() { registerCommand(name: string, command: Command) { commands.set(name, command); }, - registerTool() {}, + registerTool(tool: Tool) { + tools.set(tool.name, tool); + }, }; sideshowExtension(pi); - return { handlers, commands }; + return { handlers, commands, tools }; } -function context(statuses: Status[]) { +function context(statuses: Status[], branch: BranchEntry[] = []): Context { return { - sessionManager: { getBranch: () => [] }, + cwd: "/tmp/sideshow-extension-test", + sessionManager: { getBranch: () => branch }, ui: { - setStatus: (key: string, text: string | undefined) => statuses.push([key, text]), + setStatus(key, text) { + statuses.push([key, text]); + }, notify() {}, }, }; } +function sessionEntry(sessionId: string, toolName = "sideshow_publish_surface"): BranchEntry { + return { + type: "message", + message: { + role: "toolResult", + toolName, + details: { sessionId }, + }, + }; +} + describe("Pi extension TUI status", () => { - it("shows status by default", () => { + it("stays hidden until the conversation has a Sideshow session", () => { + delete process.env.SIDESHOW_SESSION; + delete process.env.SIDESHOW_TUI_STATUS; + const { handlers } = loadExtension(); + const idleStatuses: Status[] = []; + const activeStatuses: Status[] = []; + + handlers.get("session_start")!({}, context(idleStatuses)); + handlers.get("session_start")!({}, context(activeStatuses, [sessionEntry("session-a")])); + + assert.deepEqual(idleStatuses, [["sideshow", undefined]]); + assert.deepEqual(activeStatuses, [["sideshow", "sideshow session-a"]]); + }); + + it("does not restore explicit sessions used only by read tools", () => { + delete process.env.SIDESHOW_SESSION; delete process.env.SIDESHOW_TUI_STATUS; const { handlers } = loadExtension(); const statuses: Status[] = []; + const branch = [ + sessionEntry("active-session"), + sessionEntry("wait-only-session", "sideshow_wait_for_feedback"), + sessionEntry("list-only-session", "sideshow_list_surfaces"), + ]; - handlers.get("session_start")!({}, context(statuses)); + handlers.get("session_start")!({}, context(statuses, branch)); - assert.deepEqual(statuses, [["sideshow", "sideshow localhost:8228"]]); + assert.deepEqual(statuses, [["sideshow", "sideshow active-session"]]); }); - it("clears status when SIDESHOW_TUI_STATUS=0", async () => { - process.env.SIDESHOW_TUI_STATUS = "0"; - const { handlers, commands } = loadExtension(); + it("follows Sideshow sessions across conversation tree branches", () => { + delete process.env.SIDESHOW_SESSION; + delete process.env.SIDESHOW_TUI_STATUS; + const { handlers } = loadExtension(); + const statuses: Status[] = []; + + handlers.get("session_start")!({}, context(statuses, [sessionEntry("session-a")])); + handlers.get("session_tree")!({}, context(statuses, [sessionEntry("session-b")])); + handlers.get("session_tree")!({}, context(statuses)); + + assert.deepEqual(statuses, [ + ["sideshow", "sideshow session-a"], + ["sideshow", "sideshow session-b"], + ["sideshow", undefined], + ]); + }); + + it("resets a restored session to SIDESHOW_SESSION", async () => { + delete process.env.SIDESHOW_TUI_STATUS; + process.env.SIDESHOW_SESSION = "configured-session"; + const { commands, handlers } = loadExtension(); + const statuses: Status[] = []; + const ctx = context(statuses, [sessionEntry("restored-session")]); + + handlers.get("session_start")!({}, ctx); + await commands.get("sideshow")!.handler("reset", ctx); + + assert.deepEqual(statuses, [ + ["sideshow", "sideshow restored-session"], + ["sideshow", "sideshow configured-session"], + ]); + }); + + it("tracks the session returned by each publishing tool call and clears on reset", async (t) => { + delete process.env.SIDESHOW_SESSION; + delete process.env.SIDESHOW_TUI_STATUS; + const returnedSessions = ["session-a", "session-b"]; + t.mock.method(globalThis, "fetch", async () => { + const sessionId = returnedSessions.shift(); + return new Response( + JSON.stringify({ id: `post-${sessionId}`, title: "Status test", sessionId }), + { + status: 200, + headers: { "content-type": "application/json" }, + }, + ); + }); + + const { commands, handlers, tools } = loadExtension(); + const statuses: Status[] = []; + const ctx = context(statuses); + const publish = tools.get("sideshow_publish_surface")!; + + handlers.get("session_start")!({}, ctx); + await publish.execute( + "call-a", + { title: "Status test", parts: [{ kind: "markdown", markdown: "A" }], newSession: true }, + undefined, + undefined, + ctx, + ); + await publish.execute( + "call-b", + { title: "Status test", parts: [{ kind: "markdown", markdown: "B" }], newSession: true }, + undefined, + undefined, + ctx, + ); + await commands.get("sideshow")!.handler("reset", ctx); + + assert.deepEqual(statuses, [ + ["sideshow", undefined], + ["sideshow", "sideshow session-a"], + ["sideshow", "sideshow session-b"], + ["sideshow", undefined], + ]); + }); + + it("tracks sessions returned by update, reply, and upload tools", async (t) => { + delete process.env.SIDESHOW_SESSION; + delete process.env.SIDESHOW_TUI_STATUS; + t.mock.method(globalThis, "fetch", async (input: string | URL | Request) => { + const url = String(input); + let body; + if (url.includes("/api/surfaces/post-a")) { + body = { + id: "post-a", + title: "Updated", + version: 2, + sessionId: "update-session", + }; + } else if (url.includes("/api/comments")) { + body = { id: "comment-a", sessionId: "reply-session" }; + } else { + body = { + id: "asset-a", + contentType: "text/plain", + byteLength: 1, + url: "/a/asset-a", + sessionId: "upload-session", + }; + } + return new Response(JSON.stringify(body), { + status: 200, + headers: { "content-type": "application/json" }, + }); + }); + + const { handlers, tools } = loadExtension(); const statuses: Status[] = []; const ctx = context(statuses); + handlers.get("session_start")!({}, ctx); + + await tools + .get("sideshow_update_surface")! + .execute("call-update", { id: "post-a", title: "Updated" }, undefined, undefined, ctx); + await tools + .get("sideshow_reply_to_user")! + .execute( + "call-reply", + { message: "Done", session: "reply-session" }, + undefined, + undefined, + ctx, + ); + await tools + .get("sideshow_upload_asset")! + .execute("call-upload", { data: "eA==", filename: "x.txt" }, undefined, undefined, ctx); + + assert.deepEqual(statuses, [ + ["sideshow", undefined], + ["sideshow", "sideshow update-session"], + ["sideshow", "sideshow reply-session"], + ["sideshow", "sideshow upload-session"], + ]); + }); + + it("keeps status hidden with SIDESHOW_TUI_STATUS=0", async (t) => { + process.env.SIDESHOW_TUI_STATUS = "0"; + process.env.SIDESHOW_SESSION = "configured-session"; + t.mock.method( + globalThis, + "fetch", + async () => + new Response( + JSON.stringify({ id: "post-hidden", title: "Hidden status", sessionId: "new-session" }), + { status: 200, headers: { "content-type": "application/json" } }, + ), + ); + const { handlers, commands, tools } = loadExtension(); + const statuses: Status[] = []; + const ctx = context(statuses, [sessionEntry("restored-session")]); handlers.get("session_start")!({}, ctx); + await tools.get("sideshow_publish_surface")!.execute( + "call-hidden", + { + title: "Hidden status", + parts: [{ kind: "markdown", markdown: "Hidden" }], + newSession: true, + }, + undefined, + undefined, + ctx, + ); await commands.get("sideshow")!.handler("reset", ctx); assert.deepEqual(statuses, [ ["sideshow", undefined], ["sideshow", undefined], + ["sideshow", undefined], ]); }); }); diff --git a/tsconfig.json b/tsconfig.json index 7562917..b2e65d7 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -6,6 +6,7 @@ "moduleResolution": "nodenext", "types": ["node"], "strict": true, + "allowJs": true, "noEmit": true, "allowImportingTsExtensions": true, "erasableSyntaxOnly": true,