Artifact Sharing (shareable HTML of sessions, messages, selections)
Turning any session, message or highlighted selection into a self-contained public HTML page: how to share one, where the Shares list lives, what the generated page contains, and how expiration, passwords and view limits are enforced.
What it is
Artifact Sharing turns any session, message, or highlighted selection into a self-contained shareable HTML page hosted publicly. You right-click → Share, pick a scope, pick an expiration, and Omniscio generates a URL that renders the content — fully styled, with syntax highlighting, light/dark theme support, and arrow-key navigation between messages — in anyone's browser without them needing Omniscio or an account. Useful for showing a teammate what an agent did, sending a code walkthrough, or archiving a conversation. The URL uses a random 64-char hex token (32 random bytes, 256-bit entropy) so it's only guessable to the degree of the token entropy, and expiration is enforced server-side (expired shares return 410 Gone, not just a stale page).
Where to find it
How to use it
- Share a whole session. Click the overflow menu on any session → Share. The share dialog opens with Conversation selected. Pick an expiration — 1 hour, 24 hours, 7 days, or 30 days (all public shares must carry a bounded expiry). Click Create link. A URL like
https://…/s/<64-hex-token>is copied to your clipboard. - Share a single message. Right-click any message bubble → Share. The dialog shows a This message radio option (only visible when a messageId is provided). Pick it → confirm → copied.
- Share a selection. Highlight any text in the conversation, right-click the selection → Share. The dialog shows a Selected text radio option. Good for sending just the code snippet you were discussing without the conversation wrapper.
- Manage your shares. Open the Shares entry in the left sidebar (Omniscio group) to list everything you've published: scope, expiration, view count, Protected badge for password-gated links. You can revoke any share manually (immediate 404), delete expired ones to clean up, or click Edit on an active share to change its label, expiration, password, view cap, or notify-on-view setting — full UX in shares-view.md § Editing a share.
- Live-updating (preview). Each share has a Live-updating checkbox — when on (implementation in progress), the published HTML re-syncs from the session so viewers always see the latest state. Otherwise it's a snapshot of the moment you created the link.
- Protect a share. From the same Edit modal you can add a password, cap the number of views, or turn on a desktop notification fired the first time the share is opened. Passwords are PBKDF2-SHA256-hashed in your machine (600,000 iterations, fresh 16-byte salt per share) — plaintext never leaves the renderer. View cap, view count, last-viewed timestamp, and notify-on-view are mirrored back from the Cloud Function via the 60-second view-event poller. See share-view-events.md for the privacy model and shares-view.md § How protection enforcement works for the end-to-end flow.
How it behaves
How it works
The HTML builder is /src/main/services/share/share-engine.ts — it collects the selected scope's conversation, renders markdown + syntax highlighting with highlight.js, and inlines all CSS for both light and dark themes (so the page looks right regardless of the viewer's prefers-color-scheme). The page mirrors the Omniscio app's chat look — glass-depth (3D) message bubbles, each headed by the app's own role icon (lucide User for you, Bot for the agent), drawn as CSS-masked SVGs (the same technique as the Dark Mode toggle) so they need no script and stay CSP-safe. Assistant messages are shared as prose only — the mechanical ▸/← tool-call/result lines are stripped fence-aware (a ▸ shown inside a code block survives), and the agent's [[OMNISCIO_FINAL]] final-answer marker is honored: when a message carries it, only the prose after it is shared and the working notes above are dropped; the raw marker never appears, and a turn that was pure tool activity is skipped rather than rendered as an empty bubble (renderAssistantContent reuses the same splitOnFinalBoundary + stripToolLinesFenceAware the on-screen renderer and the visible-only Markdown export use, so a share matches what you see in Omniscio). It also embeds two tiny inline scripts: a theme-toggle — the header Dark Mode button that flips the page light/dark and remembers the choice in the viewer's browser — and keyboard navigation, where Arrow (or Shift+Arrow) moves a highlight to the previous/next message and scrolls it into view (mouse/trackpad scrolling, OS shortcuts like Cmd/Ctrl+Arrow, and typing in the comment box are all left alone; it no-ops on a single-selection share). The served page's CSP otherwise runs no script, so each is allowed only by its exact SHA-256 hash, pinned in the Cloud Function's SHARE_CSP (see share-csp-theme-script-contract.md); expiration is enforced purely server-side via the Firestore mirror (no share-expires meta tag in the published HTML). No external stylesheets, no fonts fetched at render time — entirely self-contained. The publisher is /src/main/services/share/share-publisher.ts, which uploads the HTML to Firebase Cloud Storage at shares/{token}.html in the bucket agentmc-shares-artifacts. The 16-character hex token is the URL's path segment; there's no separate authentication layer — the token is the credential. The Firebase Cloud Function /firebase/functions/src/index.ts exports serveShare at path /s/:token, which: (1) fetches the HTML blob, (2) reads the expiration timestamp from its meta tag, (3) if expired, deletes the blob and returns 410 Gone; otherwise returns the HTML with the right content-type headers. IPC surface is create/list/revoke/delete via /src/main/ipc/share-handlers.ts; the dialog UI is /src/renderer/src/features/shares/ShareDialog.tsx. Persistence + audit trail in /src/main/db/queries-share.ts. 709 tests keep the HTML rendering + expiration + token collision behavior pinned down.
Sharing a file as the file
A share can hand back the FILE itself instead of a page about it — the link returns the artifact's own bytes, under its own name, typed from its extension by a table that deliberately cannot express HTML or SVG. Both of those are scriptable, and a share link is public and anonymous, so nothing about the artifact is ever allowed to choose how a browser treats it.
- The file decides, unless you say otherwise. A data file (
.json.csv.tsv.txt.xml.log) hands itself back; Markdown, code and everything else keeps its page. Override it either way — the file viewer has a second publish action, and both the API and the CLI take arawflag. - Nothing about the sharing rules changes. A file share is gated, expirable, password-protectable, view-capped and revocable exactly like a page, and unpublishing removes its bytes.
- Sharing one file both ways gives two links. The mode is part of what decides whether an identical publish reuses an existing link, so the page and the file are separate shares.
Related
- shares-view.md — the top-level sidebar view that lists / edits / revokes / deletes every share you've created
- share-view-events.md — privacy model + audit-log shape for the view-tracking pipeline (counts, notify-on-view, poller mechanics)
- share-artifacts.md — the sibling feature for publishing local files + pasted content
- use-recipes.md — share the output of a recipe run as an artifact
- cli-control.md — the same conversation is also reachable via
omniscio://session/<id>for Omniscio users (private)
Last verified 2026-10-01