REFERENCE
SDK Demos
How to use @asgard-js/core and @asgard-js/react, with demos you can try on the page.
sandbox:// Handoff Cards
When the agent has opened a browser session inside a sandbox, produced a file, or
assembled a whole directory, it needs a way to hand the user off to it. That is done by pointing an attachment
card's defaultAction at the custom sandbox:// scheme.
The SDK intercepts it before the open-in-a-new-tab fallback, resolves it into a
typed intent, and dispatches it to the matching handler. The raw sandbox:// URI is
never passed to window.open().
URI Shape
sandbox://<sandboxName>/<action>?<query>Three actions are supported today:
URI Resolves to sandbox://sbx-a/open-browser{ kind: 'open-browser', sandboxName: 'sbx-a' }sandbox://sbx-a/open-file?absolute_path=%2Fhome%2Fuser%2Freport.md{ kind: 'open-file', sandboxName: 'sbx-a', absolutePath: '/home/user/report.md' }sandbox://sbx-a/open-folder?absolute_path=%2Fwork%2Foutput{ kind: 'open-folder', sandboxName: 'sbx-a', absolutePath: '/work/output' }absolute_pathmust be URL-encoded (absolute paths carry many/).Why a Folder Needs Its Own Action
The two destinations are not interchangeable: a file goes into the viewer (its content is read, then a change watch is attached), while a folder only unfolds the tree down to that level and selects it — nothing read, nothing watched.
So why not reuse
open-fileand let the panel work it out? Because the panel has not listed that level yet when the card arrives, so it cannot tell a file from a directory. "Try it as a file and fall back to a folder" is not a workable substitute either — the backend answers500forfs/fileon a directory and drops thefs/watchconnection. The destination therefore comes from the card itself, which is also whyRequestedFile.kindis required.Both card kinds share one auto-reveal flagWhether an arriving card also pulls the built-in aside out is governed by
autoRevealOnOpenFileCard(defaulttrue) for both kinds. TheOpenFileCardin the name predates the folder card and is kept for compatibility — "may a card pull the panel out" is one question, not two.Taking Over the Handling
All three actions can be overridden by the consumer:
<Chatbot
config={{ botProviderEndpoint: "..." }}
customChannelId="my-channel"
// When set, the SDK defers to you; otherwise it fetches a one-time URL and opens a tab
onSandboxOpenBrowser={(sandboxName) => { /* … */ }}
// open-file usually lands in the File Explorer viewer
onSandboxOpenFile={(sandboxName, absolutePath) => {
controller.requestFile(sandboxName, absolutePath);
}}
// open-folder lands on the file tree, not in the viewer
onSandboxOpenFolder={(sandboxName, absolutePath) => {
controller.requestFolder(sandboxName, absolutePath);
}}
// Required if you really mean to take over open-file — see below
fileExplorer="off"
// Where the default handler opens the tab; defaults to '_blank'
sandboxBrowserOpenTarget="_blank"
/>The three handlers do not behave the same wayonSandboxOpenBrowseris a genuine either/or: when set the SDK defers to you, and only falls back to its own handler when you omit it.onSandboxOpenFileandonSandboxOpenFolderare not. They are additive, not replacements — your handler runs, but the built-in File Explorer aside is still revealed as well, because that branch only checks thefileExplorerprop and never whether you supplied a callback. AndfileExplorerdefaults to'builtin'.The result is that your own controller and the Chatbot's internal one both receive the same
requestFileorrequestFolder. To actually take over, setfileExplorerto"off".Resolving It Yourself
resolveSandboxUrican also be used standalone — for instance when you render the cards yourself:import { resolveSandboxUri } from "@asgard-js/core";
const intent = resolveSandboxUri(uri);
if (intent === null) {
// Not sandbox://, unknown action, or missing absolute_path
// → treat it as a plain card, and never window.open() the raw URI
}The return type:
type SandboxUriIntent =
| { kind: "open-browser"; sandboxName: string }
| { kind: "open-file"; sandboxName: string; absolutePath: string }
| { kind: "open-folder"; sandboxName: string; absolutePath: string };noteParsing uses a hand-written regex rather than
new URL(). Absolute paths carry many/, and a non-special scheme's host casing is unreliable under the WHATWG URL parser; the query is still handed toURLSearchParams, which decodes.Plain Links Are Untouched
An
https://defaultActionbehaves exactly as before — interception happens only forsandbox://.See Also
- File Explorer — where
open-fileandopen-folderland - Cold-start HUD — sandbox launch and
launchedSandboxes - Templates — the attachment card structure itself
- File Explorer — where