You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Chat is an excellent way to begin a task. It is a surprisingly poor place for many tasks to end.
Ask an assistant to investigate a repository, compare options, or manage a review queue, and useful state soon disappears into a vertical transcript: what is selected, what changed, which result is current, and what remains blocked. Returning later means reconstructing an application-shaped problem from messages written for an earlier moment.
This is not only a model problem. It is an interface problem.
A calendar wants a calendar. A dependency graph wants a graph. A review queue wants rows, filters, and explicit status. Conversation can remain the command surface, but structured work needs a task surface that owns its state and exposes the right actions.
Disclosure: I maintain BitFun, the implementation used as the source-linked case study below. I also used AI assistance to organize and edit this article, then checked every product-specific claim against pinned source code. The design pattern is the point; this is not an independent security audit or performance evaluation.
The missing object between chat and automation
Traditional chat has messages and attachments. Traditional automation has fixed inputs and outputs. Stateful agent work needs a third object between them: an interface that represents the task as it exists now.
That interface should own:
domain objects and their current status;
selections, filters, view mode, and revision;
the relationship between an agent action and the object it affects;
a place to inspect, accept, reject, or revise the result.
The key design decision is that the model does not magically “see the UI.” The application chooses a small, explicit snapshot of relevant state when the user submits a request. That snapshot becomes part of a versionable protocol.
This distinction matters. “What happens if I remove this node?” is ambiguous in a transcript. A dependency explorer can make it precise by sending the selected node ID, visible dependency set, active filters, and graph revision. It does not need to send the entire DOM, a screenshot, or every object in memory.
A reference architecture
BitFun's current Mini App implementation separates four responsibilities:
The Mini App owns presentation and domain state. Its source model has an HTML/CSS/ESM browser layer and can support worker logic in non-marketplace profiles.
The desktop host owns privileged capabilities. The iframe calls an injected window.app bridge; filesystem, network, shell, AI, Agent, notifications, and host UI are represented as explicit capability groups.
An owned agent session supplies continuity. A Mini App can create or restore a dedicated session, reuse it for later turns, and receive progress only for sessions associated with that app instance.
The normal scheduler owns execution. A Mini App agent turn is submitted through the same dialog scheduler used by the desktop runtime; it may start immediately or be queued.
First, the bridge is an API boundary, not an invitation to expose the whole desktop object graph. The public contract offers named operations such as agent.ensureSession, agent.run, chat.claimComposer, and chat.focusSession.
Second, session identity is part of authorization. Reusing a session is accepted only when its recorded owner matches the Mini App and its workspace matches the expected path. The web host separately tracks which sessions the current iframe started before allowing one to appear in the shared conversation surface.
Binding conversation to the task surface
The current API makes the binding explicit rather than implicit.
1. Claim the shared composer
An agent-backed Mini App can call app.chat.claimComposer(). While its tab is active, messages from the shared input are routed to that exact iframe as chat:userMessage events.
The claim is scoped by a per-runner token, not only an app ID. That matters because an installed app and a draft preview can share an ID while both are alive. The token prevents one submission from reaching both runners.
The Mini App may contribute bounded text such as a title, placeholder, and example prompts. The host still renders and owns the input component; the iframe cannot replace it with arbitrary host markup.
2. Create or restore an owned session
app.agent.ensureSession() creates a dedicated session or validates a requested existing session. app.chat.focusSession() then associates that known session with the Mini App's composer claim.
The separation is useful: “which interface receives this input?” and “which agent history should be visible?” are related questions, but they are not the same question.
3. Snapshot state at submission time
The host accepts a user-facing displayText separately from the Mini App's internal prompt. The transcript can therefore preserve what the user actually wrote while the agent receives a structured task protocol.
Here is the pattern in simplified form:
awaitapp.chat.claimComposer({title: "Dependency Explorer",composer: {placeholder: "Ask about the current graph…"}});consttopic=awaitapp.agent.ensureSession({sessionName: "Dependency Explorer",appDataWorkspace: "topics/current"});awaitapp.chat.focusSession(topic.sessionId);app.chat.onUserMessage(async({ text, displayText })=>{conststate=collectRelevantState();// app-owned model, not DOM scrapingconstprompt=JSON.stringify({protocol: "dependency-explorer/v1",request: text,
state
});awaitapp.agent.run(prompt,{sessionId: topic.sessionId,displayText: displayText??text});});
collectRelevantState() is deliberately application-specific. A production Mini App also needs an output contract: parse the result, reject malformed or stale revisions, and apply only validated changes. Starting an agent turn is asynchronous; progress and completion arrive as session-filtered events rather than as a magical synchronous answer from agent.run().
Why explicit state beats a giant prompt
The goal is not to serialize the entire application on every turn. A useful task surface creates a narrow boundary:
Keep durable domain state in the application.
Send only the state relevant to the current action.
Preserve the user's words separately from the internal protocol.
Reuse a session only when conversational continuity is useful.
Include a revision or other freshness signal when stale output would be harmful.
Validate agent output before changing application state or external resources.
This makes context inspectable. Developers can unit-test the snapshot, users can see the selected object, and maintainers can version the protocol. It also gives you a clear place to redact secrets and cap payload size.
Capability should follow the task, not the iframe
A stateful interface is useful only if its authority is equally specific.
BitFun's manifest model separates filesystem, shell, network, Node, direct AI, full Agent, notifications, and host-UI permissions. Shell access is expressed as a command allowlist, network access as a domain allowlist, and Agent access has its own enabled flag and optional per-minute limit. Host-side handlers check these gates before servicing bridge calls.
The current public-market profile is intentionally stricter than the general source model:
marketplace packages must explicitly disable Node;
remote imports and dynamic code evaluation are rejected during package validation;
broad home-directory and absolute filesystem scopes are rejected;
marketplace iframes run with sandbox="allow-scripts", without same-origin access;
hidden Agent turns from marketplace Mini Apps use a small allowlist centered on read-only web research rather than filesystem, shell, or host control.
These are concrete controls, not a claim of perfect isolation. Human review, manifests, iframe sandboxing, and allowlists reduce different risks; none of them makes untrusted code inherently safe.
The implementation also retains a compatibility profile for built-in or local Mini Apps with a broader iframe sandbox and optional worker support. Therefore, statements about the marketplace strict profile should not be generalized to every Mini App. That split is visible in the runner code and is an important part of the threat model.
What this pattern does—and does not—solve
It solves a real interface problem:
the user can point at an object instead of redescribing it;
follow-up turns can continue in the task's own agent session;
progress can appear beside the state it affects;
permissions and ownership checks have explicit enforcement points.
It does not give the model automatic access to arbitrary UI state. It does not remove the need to design state and output schemas. It does not guarantee correct model output. It does not turn an iframe sandbox into a complete security boundary, and it does not make every task better as an app.
A one-off explanation probably belongs in chat. A task becomes a strong Mini App candidate when it has several of these properties:
state changes over multiple turns;
the user repeatedly selects or compares objects;
the output needs review before it is applied;
the same workflow will be reopened;
a domain-specific visualization reveals more than prose.
From answer-shaped output to software-shaped work
Chat should not disappear. It is flexible, forgiving, and often the fastest way to express intent. But a transcript should be one view of the work, not its only container.
The reusable idea is simple:
conversation supplies intent;
the task surface supplies structure and controls;
an owned session supplies continuity;
explicit state and host-mediated capabilities connect them.
BitFun is one current implementation of that pattern, not proof that the pattern is finished. Its public gallery is still early, which makes the source more useful than adoption claims for evaluating the architecture.
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
Your AI Task Should Not End as a Chat Transcript
Chat is an excellent way to begin a task. It is a surprisingly poor place for many tasks to end.
Ask an assistant to investigate a repository, compare options, or manage a review queue, and useful state soon disappears into a vertical transcript: what is selected, what changed, which result is current, and what remains blocked. Returning later means reconstructing an application-shaped problem from messages written for an earlier moment.
This is not only a model problem. It is an interface problem.
A calendar wants a calendar. A dependency graph wants a graph. A review queue wants rows, filters, and explicit status. Conversation can remain the command surface, but structured work needs a task surface that owns its state and exposes the right actions.
The missing object between chat and automation
Traditional chat has messages and attachments. Traditional automation has fixed inputs and outputs. Stateful agent work needs a third object between them: an interface that represents the task as it exists now.
That interface should own:
The key design decision is that the model does not magically “see the UI.” The application chooses a small, explicit snapshot of relevant state when the user submits a request. That snapshot becomes part of a versionable protocol.
This distinction matters. “What happens if I remove this node?” is ambiguous in a transcript. A dependency explorer can make it precise by sending the selected node ID, visible dependency set, active filters, and graph revision. It does not need to send the entire DOM, a screenshot, or every object in memory.
A reference architecture
BitFun's current Mini App implementation separates four responsibilities:
window.appbridge; filesystem, network, shell, AI, Agent, notifications, and host UI are represented as explicit capability groups.The data flow is compact:
There are two boundaries worth noticing.
First, the bridge is an API boundary, not an invitation to expose the whole desktop object graph. The public contract offers named operations such as
agent.ensureSession,agent.run,chat.claimComposer, andchat.focusSession.Second, session identity is part of authorization. Reusing a session is accepted only when its recorded owner matches the Mini App and its workspace matches the expected path. The web host separately tracks which sessions the current iframe started before allowing one to appear in the shared conversation surface.
Binding conversation to the task surface
The current API makes the binding explicit rather than implicit.
1. Claim the shared composer
An agent-backed Mini App can call
app.chat.claimComposer(). While its tab is active, messages from the shared input are routed to that exact iframe aschat:userMessageevents.The claim is scoped by a per-runner token, not only an app ID. That matters because an installed app and a draft preview can share an ID while both are alive. The token prevents one submission from reaching both runners.
The Mini App may contribute bounded text such as a title, placeholder, and example prompts. The host still renders and owns the input component; the iframe cannot replace it with arbitrary host markup.
2. Create or restore an owned session
app.agent.ensureSession()creates a dedicated session or validates a requested existing session.app.chat.focusSession()then associates that known session with the Mini App's composer claim.The separation is useful: “which interface receives this input?” and “which agent history should be visible?” are related questions, but they are not the same question.
3. Snapshot state at submission time
The host accepts a user-facing
displayTextseparately from the Mini App's internalprompt. The transcript can therefore preserve what the user actually wrote while the agent receives a structured task protocol.Here is the pattern in simplified form:
collectRelevantState()is deliberately application-specific. A production Mini App also needs an output contract: parse the result, reject malformed or stale revisions, and apply only validated changes. Starting an agent turn is asynchronous; progress and completion arrive as session-filtered events rather than as a magical synchronous answer fromagent.run().Why explicit state beats a giant prompt
The goal is not to serialize the entire application on every turn. A useful task surface creates a narrow boundary:
This makes context inspectable. Developers can unit-test the snapshot, users can see the selected object, and maintainers can version the protocol. It also gives you a clear place to redact secrets and cap payload size.
Capability should follow the task, not the iframe
A stateful interface is useful only if its authority is equally specific.
BitFun's manifest model separates filesystem, shell, network, Node, direct AI, full Agent, notifications, and host-UI permissions. Shell access is expressed as a command allowlist, network access as a domain allowlist, and Agent access has its own enabled flag and optional per-minute limit. Host-side handlers check these gates before servicing bridge calls.
The current public-market profile is intentionally stricter than the general source model:
sandbox="allow-scripts", without same-origin access;These are concrete controls, not a claim of perfect isolation. Human review, manifests, iframe sandboxing, and allowlists reduce different risks; none of them makes untrusted code inherently safe.
The implementation also retains a compatibility profile for built-in or local Mini Apps with a broader iframe sandbox and optional worker support. Therefore, statements about the marketplace strict profile should not be generalized to every Mini App. That split is visible in the runner code and is an important part of the threat model.
What this pattern does—and does not—solve
It solves a real interface problem:
It does not give the model automatic access to arbitrary UI state. It does not remove the need to design state and output schemas. It does not guarantee correct model output. It does not turn an iframe sandbox into a complete security boundary, and it does not make every task better as an app.
A one-off explanation probably belongs in chat. A task becomes a strong Mini App candidate when it has several of these properties:
From answer-shaped output to software-shaped work
Chat should not disappear. It is flexible, forgiving, and often the fastest way to express intent. But a transcript should be one view of the work, not its only container.
The reusable idea is simple:
BitFun is one current implementation of that pattern, not proof that the pattern is finished. Its public gallery is still early, which makes the source more useful than adoption claims for evaluating the architecture.
For verification rather than endorsement:
Source notes
The product-specific statements above were checked against these source locations, pinned so future changes do not silently move the evidence:
window.appAgent and chat bridge contractAll reactions