Skip to content

feat(cli): add HTML session export with an interactive viewer - #11977

Merged
alexhancock merged 3 commits into
aaif-goose:mainfrom
salman1993:sm/html-session-viewer
Oct 1, 2026
Merged

alexhancock merged 3 commits into
aaif-goose:mainfrom
salman1993:sm/html-session-viewer

Conversation

@salman1993

@salman1993 salman1993 commented Sep 10, 2026 •

Copy link
Copy Markdown
Contributor

What

Adds goose session export --format html, producing a single self-contained
HTML file with the session embedded as base64 JSON and rendered client-side
(markdown via marked, syntax highlighting via highlight.js).

Highlights:

  • Table-of-contents sidebar with scroll-spy for jumping between turns
  • Semantic tool-call rendering: shell $ prompt, write/todo_write content,
    computed edit diffs, collapsible output with a "show more" button
  • Dense, scannable layout; internal/turn-context messages hidden by default
goose session export --session-id <id> --format html -o session.html

Credit / prior art

The viewer is highly inspired by Pi's HTML session exporter — the overall
approach (one self-contained file, base64-embedded session, client-side
rendering) and the visual language of the transcript follow Pi.

Directly copied from Pi: only the vendored third-party libraries
(marked.min.js, highlight.min.js). The Rust module, template.html,
template.css, and template.js were written for goose's own session schema —
Pi's renderer targets a different, branching session model and was not reused.

Verification

  • cargo test -p goose --lib session::export_html
  • cargo fmt / cargo clippy clean
  • Rendered representative sessions (write/edit/shell/todo_write, thinking,
    tool errors) via headless Chrome

Closes #11978

Add `goose session export --format html`, producing a single
self-contained HTML file with the session embedded as base64 JSON and
rendered client-side (markdown via marked, syntax highlighting via
highlight.js). Includes a table-of-contents sidebar with scroll-spy,
semantic tool-call rendering (shell `$` prompt, write/todo_write content,
computed edit diffs), and collapsible output.

The viewer is highly inspired by Pi's HTML session exporter: the overall
approach (one self-contained file, base64-embedded session, client-side
rendering) and the visual language of the transcript follow Pi.

Directly copied from Pi: only the vendored third-party libraries
(marked.min.js, highlight.min.js). The Rust module, template.html,
template.css, and template.js were written for goose's own session
schema — Pi's renderer targets a different, branching session model and
was not reused.
@salman1993
salman1993 requested a review from a team as a code owner September 10, 2026 03:00
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 10, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-10T03:24:50.669275Z eda98fc New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@salman1993

salman1993 commented Sep 10, 2026 •

Copy link
Copy Markdown
Contributor Author

screenshots of what the session viewer looks like:

Screenshot 2026-09-09 at 11 02 12 PM Screenshot 2026-09-09 at 11 02 27 PM

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8dded84e3b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

const response = responsesById.get(block.id);
if (response) {
const result = response.toolResult;
status = result && result.status === 'error' ? 'error' : 'success';

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Treat MCP isError results as failures

When a tool returns Ok(CallToolResult::error(...)), its serialized wrapper has status: "success" and the actual failure flag is value.isError: true, so this marks the call as successful. For write, edit, and todo_write, the following suppression also hides the error output entirely, making a failed filesystem operation appear as a successful change in the exported transcript. Determine status from both the wrapper and result.value.isError before suppressing output.

Useful? React with 👍 / 👎.

Comment on lines +366 to +367
const blocks = Array.isArray(msg.content) ? msg.content : [];
const bodyParts = blocks.map(renderContentBlock).filter((h) => h);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Filter assistant-only blocks before rendering visible messages

When a user-visible message contains text, image, or tool-result content annotated for the Assistant audience, this renders every block in the default transcript. Goose normally applies Message::user_visible_content and filters nested tool-result content by audience, so sessions containing model-only MCP or chat-recall content will display that internal content even before the user selects “Show internal messages.” Apply the user-audience projection before mapping blocks to HTML.

Useful? React with 👍 / 👎.

Comment on lines +41 to +44
function formatTimestamp(unixSeconds) {
if (!unixSeconds) return '';
const d = new Date(unixSeconds * 1000);
if (isNaN(d.getTime())) return '';

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Normalize millisecond message timestamps

For persisted sessions whose message timestamps are in milliseconds, multiplying by 1000 again produces dates tens of thousands of years in the future instead of the actual message time. SessionManager::message_timestamp_to_datetime explicitly supports both seconds and milliseconds using MILLISECOND_TIMESTAMP_THRESHOLD, so the HTML formatter needs the same normalization for older or imported sessions.

Useful? React with 👍 / 👎.

@DOsinga

DOsinga commented Sep 10, 2026

Copy link
Copy Markdown
Collaborator

Thanks for contributing. Goose uses an issue-first workflow. Please open or identify the issue for this work, wait until it reaches Ready, and link it from this pull request with Closes #ISSUE_NUMBER.

See From Issue to Pull Request. If no issue is linked and nobody replies here, this pull request will be closed after three days.

@salman1993

Copy link
Copy Markdown
Contributor Author

Thanks for contributing. Goose uses an issue-first workflow. Please open or identify the issue for this work, wait until it reaches Ready, and link it from this pull request with Closes #ISSUE_NUMBER.

See From Issue to Pull Request. If no issue is linked and nobody replies here, this pull request will be closed after three days.

put in an issue here: #11978

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: eda98fc6f3

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +411 to +412
default:
return '';

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Render action-required blocks in exported transcripts

When a persisted message contains actionRequired—for example, an elicitation prompt or a tool-confirmation decision—this falls through to the empty default renderer. If that is the message's only content, renderMessage drops the entire message, so the exported transcript omits an interaction that the CLI and Markdown exporter preserve. Add rendering for the action-required variants instead of silently discarding them.

Useful? React with 👍 / 👎.

Comment on lines +392 to +393
const suppress = status === 'success' &&
(tool === 'write' || tool === 'edit' || tool === 'todo_write');

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Restrict file-tool suppression to built-in tools

When an MCP extension exposes a tool such as storage__write, toolNameParts reduces it to the leaf name write, causing this branch to suppress its successful response as though it were Goose's built-in file writer. Such an extension's result may contain important output unrelated to the submitted content, so the export becomes incomplete; gate suppression on the full unqualified built-in tool name rather than the parsed leaf.

Useful? React with 👍 / 👎.

@alexhancock

Copy link
Copy Markdown
Collaborator

Cool @salman1993! Something simple built in makes sense.

To share something similar, I also built a session inspector as a side project a couple weeks back.

Deployed: https://www.sessioninspector.ai/
Code: https://github.com/alexhancock/session-inspector

Supports drag and drop of goose session export files. A bit more of a debugging tool in its approach. Supports Claude Code and goose, and can easily be expanded to other harnesses too with the implementation of a TS interface over the underlying data file.

Screenshot 2026-09-10 at 8 44 25 AM

@DOsinga

DOsinga commented Sep 14, 2026

Copy link
Copy Markdown
Collaborator

Thanks — the viewer looks useful, but I don't think this should land as a CLI-only feature.

The desktop already exports sessions through _goose/unstable/session/export and offers JSON and Markdown from the session list. Please expose HTML through that same path and add it to the desktop export menu:

  • add Html to SessionExportFormat
  • put the HTML export operation on SessionManager, alongside export_session_markdown
  • handle Html in on_export_session
  • add HTML with the correct MIME type and .html extension in SessionListView

That keeps format support in one shared API instead of having the CLI load a session and call the renderer directly.

More broadly, the browser renderer is reimplementing Goose's message semantics from serialized JSON. At minimum, user-visible projection should happen in Rust before embedding the session, so the HTML viewer does not need to duplicate privacy/audience rules that already exist in Message::user_visible_content and related helpers.

@DOsinga

DOsinga commented Sep 15, 2026

Copy link
Copy Markdown
Collaborator

Thanks for contributing. Goose uses an issue-first workflow. Please open or identify the issue for this work, wait until it reaches Ready, and link it from this pull request with Closes #ISSUE_NUMBER.

See From Issue to Pull Request. If no issue is linked and nobody replies here, this pull request will be closed after three days.

@alexhancock alexhancock left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM if you fix up the red check @salman1993!

@salman1993

salman1993 commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor Author

@alexhancock CI check for MCP Conformance is failing but i don't think its related to this PR. Can you please re-run the failed job and see if that fixes it?

The failing assertion is SSE reconnection timing. The likely cause is the late-reconnection threshold under CI load. The same failure occurred on main yesterday. This PR changes none of the MCP transport, driver, dependencies, or workflow.

@alexhancock
alexhancock added this pull request to the merge queue Oct 1, 2026
Merged via the queue into aaif-goose:main with commit 9012eac Oct 1, 2026
44 of 45 checks passed
lifeizhou-ap added a commit that referenced this pull request Oct 5, 2026
* main:
  fix: support hyphenated GPT 6.1 Sol effort aliases (#12689)
  Resolve extensions into a per-inference lease (#12161)
  fix: fixed unstable tests (#12679)
  fix(desktop): use a system Node.js in the Windows npx wrapper (#12176)
  fix(cli): show thinking when GOOSE_CLI_SHOW_THINKING is set to any value (#12454)
  fix(cost): price Databricks GLM aliases from the zhipuai catalog (#12673)
  fix(tom): read messages from config (#12634)
  fix(bedrock): preserve the service total when folding cache input tokens (#12586)
  fix(provider): refresh revoked Copilot tokens when listing models (#12627)
  feat(cli): prompt for goose mode during first-time configure (#12629)
  fix: refresh revoked GitHub Copilot API token once (#12616)
  docs: update Desktop permission mode instructions (#12626)
  fix(desktop): recognize ASAR when validating update target (#12599)
  docs: clarify self-serve LLM provider path (#12552)
  fix(acp): stamp the implemented protocol version in initialize (#12580)
  feat(cli): add HTML session export with an interactive viewer (#11977)
  fix: session naming/"none" reasoning effort fails for gpt-6.1-sol (#12605)
  fix: reflect loading state of chats (#12603)
  Remove Nostr session sharing from goose core (#12453)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

view goose sessions in human friendly format

4 participants