<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Bob Lee</title>
    <description>The latest articles on DEV Community by Bob Lee (@bobleer).</description>
    <link>https://dev.to/bobleer</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4065857%2F1e3b9fd6-c7c1-429a-a5a5-4d2695197bf2.jpg</url>
      <title>DEV Community: Bob Lee</title>
      <link>https://dev.to/bobleer</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/bobleer"/>
    <language>en</language>
    <item>
      <title>I made DeepSeek Harness plugins available to any MCP agent (without handing it install rights)</title>
      <dc:creator>Bob Lee</dc:creator>
      <pubDate>Thu, 13 Aug 2026 16:23:43 +0000</pubDate>
      <link>https://dev.to/bobleer/i-made-deepseek-harness-plugins-available-to-any-mcp-agent-without-handing-it-install-rights-1f18</link>
      <guid>https://dev.to/bobleer/i-made-deepseek-harness-plugins-available-to-any-mcp-agent-without-handing-it-install-rights-1f18</guid>
      <description>&lt;p&gt;DeepSeek Harness takes “everything is a plugin” unusually far. Models, tools, sessions, the agent loop, and UI surfaces all live in a Cordis composition.&lt;/p&gt;

&lt;p&gt;That freedom creates an interoperability problem: what if your primary agent is &lt;em&gt;not&lt;/em&gt; DSH?&lt;/p&gt;

&lt;p&gt;An MCP-capable desktop agent should not need to replace its runtime just to inspect or use a useful DSH plugin. It should be able to discover the plugin, review what it is, explicitly choose whether to install it, and—only when allowed—call its live tools.&lt;/p&gt;

&lt;p&gt;I contribute to &lt;a href="https://github.com/GCWing/BitFun" rel="noopener noreferrer"&gt;BitFun&lt;/a&gt;. We built an early bridge to test that idea: &lt;a href="https://github.com/bobleer/deepseek-harness-plugin-mcp" rel="noopener noreferrer"&gt;deepseek-harness-plugin-mcp&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This is &lt;strong&gt;not an official DeepSeek component&lt;/strong&gt;. It is source preview. The npm package is not published yet, so do not copy the &lt;code&gt;npx&lt;/code&gt; example from the current README and expect it to work.&lt;/p&gt;

&lt;h2&gt;
  
  
  One bridge, three trust boundaries
&lt;/h2&gt;

&lt;p&gt;The first design mistake would be exposing one magical “install and run this plugin” tool. Discovery, local mutation, and execution do not deserve the same authority.&lt;/p&gt;

&lt;p&gt;The server therefore has three planes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Catalog: useful while read-only
&lt;/h3&gt;

&lt;p&gt;The catalog plane searches repositories carrying the &lt;code&gt;dsh-plugin&lt;/code&gt; GitHub topic, caches a compact index, and lets an MCP client inspect a candidate before touching local DSH state.&lt;/p&gt;

&lt;p&gt;It can answer questions such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Is this repository an installable Cordis bundle, an MCP server, a UI extension, or a directory?&lt;/li&gt;
&lt;li&gt;Does &lt;code&gt;package.json&lt;/code&gt; declare a DSH bundle?&lt;/li&gt;
&lt;li&gt;Is there a &lt;code&gt;cordis.patch.yml&lt;/code&gt;?&lt;/li&gt;
&lt;li&gt;What GitHub install spec would the DSH CLI accept?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Catalog access is enabled by default. Install and runtime access are not.&lt;/p&gt;

&lt;p&gt;That separation matters because a repository description is discovery metadata, not compatibility proof. The bridge also treats Cordis &lt;code&gt;!!js&lt;/code&gt; config as opaque inspection data; it does not evaluate arbitrary config while cataloguing a repository.&lt;/p&gt;

&lt;p&gt;Relevant source:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;src/github/catalog.ts&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;src/plugin/inspect.ts&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;src/plugin/classify.ts&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;src/mcp/resources.ts&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Profile: mutation requires an explicit opt-in
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;dsh_plugin_install&lt;/code&gt; and &lt;code&gt;dsh_plugin_uninstall&lt;/code&gt; are disabled unless the operator starts the bridge with &lt;code&gt;--allow-install&lt;/code&gt; (or the equivalent environment flag).&lt;/p&gt;

&lt;p&gt;When enabled, the bridge delegates to the official command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dsh plugin --profile &amp;lt;name&amp;gt; add|remove ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It does not quietly edit a profile manifest itself. After the command returns, it reads the observed profile state back and returns the installed bundles and dependencies.&lt;/p&gt;

&lt;p&gt;This is better than a boolean success response, but not enough for production. A mutating bridge should return a receipt containing at least:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the immutable source commit;&lt;/li&gt;
&lt;li&gt;the profile diff;&lt;/li&gt;
&lt;li&gt;installed, skipped, and rejected components;&lt;/li&gt;
&lt;li&gt;requested capabilities;&lt;/li&gt;
&lt;li&gt;the first health check;&lt;/li&gt;
&lt;li&gt;an exact rollback action.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;An external reviewer raised exactly this point on X, so it is now &lt;a href="https://github.com/bobleer/deepseek-harness-plugin-mcp/issues/1" rel="noopener noreferrer"&gt;public issue #1&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Runtime: project live DSH tools into MCP
&lt;/h3&gt;

&lt;p&gt;Runtime access is a second, independent opt-in: &lt;code&gt;--allow-runtime&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;When enabled, the host starts a dedicated DSH profile, reads the live &lt;code&gt;ctx.tools&lt;/code&gt; registry, and projects compatible tools into the MCP server under deterministic &lt;code&gt;dsh__*&lt;/code&gt; names.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;MCP client
  -&amp;gt; dsh__some_tool
  -&amp;gt; bridge lookup and argument forwarding
  -&amp;gt; live DSH ctx.tools.execute(...)
  -&amp;gt; DSH policy and result pipeline
  -&amp;gt; MCP result
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The DSH runtime remains the execution owner. The bridge does not import arbitrary plugin executors into the MCP process.&lt;/p&gt;

&lt;p&gt;This also means “plugin” does not automatically mean “MCP tool.” UI, TUI, theme, and configuration-only plugins can appear in the catalog without pretending to expose model-callable functions.&lt;/p&gt;

&lt;p&gt;Relevant source:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;src/runtime/host.ts&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;src/runtime/bridge.ts&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;src/dsh-plugin.ts&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;src/mcp/server.ts&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why we built this from BitFun
&lt;/h2&gt;

&lt;p&gt;BitFun already uses MCP as one extension layer, while keeping workspace execution, permissions, sessions, remote control, and its desktop UI inside typed product boundaries.&lt;/p&gt;

&lt;p&gt;DSH is interesting for the opposite reason: its runtime graph is deeply replaceable.&lt;/p&gt;

&lt;p&gt;The bridge lets us test a middle path:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;BitFun keeps ownership of the workstation and user-facing control plane;&lt;/li&gt;
&lt;li&gt;DSH keeps ownership of a plugin composition and its live tool pipeline;&lt;/li&gt;
&lt;li&gt;MCP becomes the explicit boundary between them.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The same bridge can be used by any MCP-capable agent. It is not a BitFun-only protocol.&lt;/p&gt;

&lt;p&gt;What we &lt;strong&gt;cannot&lt;/strong&gt; say yet is “BitFun supports every DSH plugin.” Compatibility varies, packaging is unfinished, and install receipts and cancellation evidence still need work.&lt;/p&gt;

&lt;h2&gt;
  
  
  The production checklist
&lt;/h2&gt;

&lt;p&gt;Before calling this mature, I want the project to prove:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;source provenance is immutable;&lt;/li&gt;
&lt;li&gt;profile mutations produce auditable receipts and tested rollback;&lt;/li&gt;
&lt;li&gt;bridged calls preserve DSH and host permission decisions;&lt;/li&gt;
&lt;li&gt;MCP cancellation reaches DSH and the underlying capability;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;dsh__*&lt;/code&gt; names stay stable across reloads and collisions;&lt;/li&gt;
&lt;li&gt;compatibility is tested against named DSH revisions;&lt;/li&gt;
&lt;li&gt;the npm instructions match an actually published artifact.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Until then, it is an architecture experiment with real code—not a polished package.&lt;/p&gt;

&lt;p&gt;Source: &lt;a href="https://github.com/bobleer/deepseek-harness-plugin-mcp" rel="noopener noreferrer"&gt;https://github.com/bobleer/deepseek-harness-plugin-mcp&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;BitFun: &lt;a href="https://github.com/GCWing/BitFun" rel="noopener noreferrer"&gt;https://github.com/GCWing/BitFun&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you use another MCP agent and have a specific DSH plugin in mind, open an issue with that exact pair. A concrete compatibility case is much more useful than “support all plugins.” If the direction is useful, a star tells us which side of the bridge to harden first.&lt;/p&gt;

</description>
      <category>deepseek</category>
      <category>mcp</category>
      <category>agents</category>
      <category>opensource</category>
    </item>
    <item>
      <title>DeepSeek V4 Pro is cheap. Your agent harness can still waste the cache</title>
      <dc:creator>Bob Lee</dc:creator>
      <pubDate>Thu, 13 Aug 2026 06:54:24 +0000</pubDate>
      <link>https://dev.to/bobleer/deepseek-v4-pro-is-cheap-your-agent-harness-can-still-waste-the-cache-1inb</link>
      <guid>https://dev.to/bobleer/deepseek-v4-pro-is-cheap-your-agent-harness-can-still-waste-the-cache-1inb</guid>
      <description>&lt;p&gt;DeepSeek V4 Pro 0813 has made cost comparisons fun again. The model is capable, the API is cheap, and the first wave of coding demos looks good.&lt;/p&gt;

&lt;p&gt;But model price is not the same thing as &lt;strong&gt;agent task price&lt;/strong&gt;. A harness can quietly erase much of the advantage before the model gets to do useful work.&lt;/p&gt;

&lt;h2&gt;
  
  
  The one-byte problem
&lt;/h2&gt;

&lt;p&gt;A coding agent resends a large prefix on every turn: system instructions, tool schemas, repository context, and conversation history. Prefix caching only helps while that prefix stays identical. Add a timestamp, reorder the tools, or serialize one object differently and the cache can miss from that byte onward.&lt;/p&gt;

&lt;p&gt;That matters more on real repository work than on a one-shot demo. Search → patch → test → read the failure → repeat can run for dozens of turns. If every turn rebills the same prefix, a cheap model starts behaving like an expensive workflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we measured in BitFun
&lt;/h2&gt;

&lt;p&gt;We build &lt;a href="https://github.com/GCWing/BitFun" rel="noopener noreferrer"&gt;BitFun&lt;/a&gt;, an MIT-licensed desktop agent workbench with a Rust runtime. Prompt assembly is kept byte-stable across turns. In one SWE-Bench-Pro run using DeepSeek V4 Pro, the average KV-cache hit rate was &lt;strong&gt;98.67%&lt;/strong&gt;. This is a single-run engineering signal, not a universal benchmark claim, but it tells us the optimization is doing its job.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3nxdky486hcrgu22ykxp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3nxdky486hcrgu22ykxp.png" alt="KV-cache hit-rate distribution" width="800" height="408"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The other half is persistence. Long repository tasks rarely finish in one clean response, so BitFun's Goal mode persists the objective across turns and automatically continues while it remains active. The runtime can schedule up to 100 continuation turns for one objective, with bounded retry backoff when a continuation submission fails. It does not promise that every task magically succeeds; it is designed so the agent does not confuse “I wrote a plausible answer” with “the repository task is actually done.”&lt;/p&gt;

&lt;h2&gt;
  
  
  Why V4 Pro and the harness should be evaluated together
&lt;/h2&gt;

&lt;p&gt;The same model can look very different in two coding tools because the model only supplies part of the system. Tool selection, error recovery, context packaging, repository search, and stopping policy all live in the harness.&lt;/p&gt;

&lt;p&gt;BitFun already includes DeepSeek V4 Pro in its model catalog, but support is the boring part. The interesting question is whether the surrounding runtime preserves DeepSeek's cost advantage through a long search/patch/test loop. That is the problem the 98.67% measurement answers.&lt;/p&gt;

&lt;p&gt;BitFun runs on macOS, Windows, and Linux, and the source plus installers are here: &lt;a href="https://github.com/GCWing/BitFun" rel="noopener noreferrer"&gt;github.com/GCWing/BitFun&lt;/a&gt;. If this is the kind of open agent workbench you want to see grow, a star helps more builders find it.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>opensource</category>
      <category>rust</category>
      <category>agents</category>
    </item>
    <item>
      <title>A Remote Coding Agent Can Deadlock on a Local Permission Dialog</title>
      <dc:creator>Bob Lee</dc:creator>
      <pubDate>Thu, 13 Aug 2026 00:07:48 +0000</pubDate>
      <link>https://dev.to/bobleer/a-remote-coding-agent-can-deadlock-on-a-local-permission-dialog-o44</link>
      <guid>https://dev.to/bobleer/a-remote-coding-agent-can-deadlock-on-a-local-permission-dialog-o44</guid>
      <description>&lt;p&gt;The nastiest failure mode in a remote coding agent is not a bad patch.&lt;/p&gt;

&lt;p&gt;It is a permission prompt that nobody can see.&lt;/p&gt;

&lt;p&gt;You start a long-running job on a workstation, leave the desk, and check it from a phone later. The agent reaches a command that needs approval. If that request only exists as a modal in the desktop UI, the job has not technically failed. It has just stopped forever.&lt;/p&gt;

&lt;p&gt;That is worse. A failed job is observable. A hidden wait looks healthy until someone notices no work has moved.&lt;/p&gt;

&lt;h2&gt;
  
  
  The permission prompt is protocol state
&lt;/h2&gt;

&lt;p&gt;The fix starts with a small change in how you model approval.&lt;/p&gt;

&lt;p&gt;A permission request is not UI state. It is durable state owned by the job that is doing the work.&lt;/p&gt;

&lt;p&gt;The lifecycle should look more like this:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;asked → persisted → surfaced → answered → applied → resolved&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The desktop dialog, phone screen, CLI, or web controller is only one view over that state. Closing a window must not erase it. Reconnecting must not create a second request. Two controllers must not be able to resolve different requests because a stale button happened to be on screen.&lt;/p&gt;

&lt;p&gt;This also changes what a remote-control protocol needs. A controller should be able to fetch job status with pending approvals, submit an answer for one request ID, and observe the resulting event. It should not become a filesystem or runtime proxy just to click “allow.”&lt;/p&gt;

&lt;h2&gt;
  
  
  What needs to survive a disconnect
&lt;/h2&gt;

&lt;p&gt;At minimum, the pending request needs a stable request ID, its owning job/session, the requested action and resources, and enough ordering information to render concurrent requests deterministically.&lt;/p&gt;

&lt;p&gt;The answer also needs an identity.&lt;/p&gt;

&lt;p&gt;If request &lt;code&gt;abc&lt;/code&gt; is pending, an answer for &lt;code&gt;xyz&lt;/code&gt; must fail. Replaying the same answer for &lt;code&gt;abc&lt;/code&gt; should be harmless. Replaying a different answer under the same ID should not quietly overwrite the first decision.&lt;/p&gt;

&lt;p&gt;That sounds fussy until a phone reconnects on a flaky network and retries the last command. Then it is the difference between idempotence and “the agent ran it twice.”&lt;/p&gt;

&lt;p&gt;The mailbox also needs limits. A broken or hostile tool should not be able to fill an unattended host with an unbounded number of serialized approval requests.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resuming the worker is a separate step
&lt;/h2&gt;

&lt;p&gt;Persisting an “approved” flag is not enough.&lt;/p&gt;

&lt;p&gt;The running worker has to consume the answer, apply it to the exact pending request, record where the reply came from, and only then remove the request from the mailbox. If the worker crashes between those steps, recovery should be able to tell whether the answer is queued, applied, or fully resolved.&lt;/p&gt;

&lt;p&gt;Reply source matters too. A user approval, an automatic policy, and a system rejection are not the same audit event, even if they all unblock the same future.&lt;/p&gt;

&lt;p&gt;When the permission event stream disappears, the safe behavior is not to assume approval. Pending work should fail closed or be cancelled with an explicit reason. Otherwise a transport failure silently turns into broader authority.&lt;/p&gt;

&lt;h2&gt;
  
  
  The UI is the easy part
&lt;/h2&gt;

&lt;p&gt;Once the protocol exists, the phone UI really can be two buttons: approve and reject.&lt;/p&gt;

&lt;p&gt;But those buttons are the last five percent. The hard part is making the request durable, routed to the correct session, replay-safe, auditable, bounded, and fail-closed.&lt;/p&gt;

&lt;p&gt;There is also a boundary worth keeping explicit: an application-level tool approval is not an operating-system security grant. A remote controller can approve an agent action that the app is already capable of performing. It cannot legitimately manufacture Accessibility, Screen Recording, or similar host privileges that the operating system has not granted.&lt;/p&gt;

&lt;p&gt;I am building BitFun, and this is how we ended up treating detached-job approvals: the target owns a persisted permission mailbox; the controller answers by request ID; the worker applies the reply with its source and marks it resolved. The same permission events can then reach desktop and remote-control surfaces without making the controller the runtime.&lt;/p&gt;

&lt;p&gt;The implementation is open source here: &lt;a href="https://github.com/GCWing/BitFun" rel="noopener noreferrer"&gt;https://github.com/GCWing/BitFun&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>security</category>
      <category>opensource</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Your Coding Agent Shouldn't Grade Its Own Patch</title>
      <dc:creator>Bob Lee</dc:creator>
      <pubDate>Wed, 12 Aug 2026 17:35:53 +0000</pubDate>
      <link>https://dev.to/bobleer/your-coding-agent-shouldnt-grade-its-own-patch-1gi0</link>
      <guid>https://dev.to/bobleer/your-coding-agent-shouldnt-grade-its-own-patch-1gi0</guid>
      <description>&lt;p&gt;A coding agent finishes a patch, runs the tests, reads its own diff, and tells you the change looks good.&lt;/p&gt;

&lt;p&gt;That sounds efficient. It is also a subtle conflict of interest.&lt;/p&gt;

&lt;p&gt;The problem is not that the model is dishonest. The problem is that the same context which produced the patch also contains the assumptions that justified it. When the agent reviews its own work, it is likely to reuse those assumptions instead of challenging them.&lt;/p&gt;

&lt;p&gt;I have seen the failure mode look like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the implementation silently narrows the original requirement&lt;/li&gt;
&lt;li&gt;the tests only cover the path the agent chose&lt;/li&gt;
&lt;li&gt;the review praises consistency with that implementation&lt;/li&gt;
&lt;li&gt;nobody asks whether the implementation was the right one&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A fresh reviewer helps, but "add another agent" is not a complete design. If both agents receive a vague prompt and unlimited access to the repository, you have mostly doubled the cost and created two confident narratives.&lt;/p&gt;

&lt;p&gt;Here is the review loop that has been more reliable for me.&lt;/p&gt;

&lt;h2&gt;
  
  
  Give the reviewer a bounded target
&lt;/h2&gt;

&lt;p&gt;The reviewer should know exactly what is being reviewed: a prepared diff, a commit range, or a small packet of changed files.&lt;/p&gt;

&lt;p&gt;Treat that diff as the source of truth. Repository context can explain a finding, but it should not let the reviewer wander into a broad redesign. A bounded target makes the final report auditable: every claim can point back to a concrete change.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep missing evidence visible
&lt;/h2&gt;

&lt;p&gt;A timeout, unavailable file, stale hint, or truncated diff is not a clean review.&lt;/p&gt;

&lt;p&gt;This sounds obvious, but review systems often collapse "I did not find a problem" and "I could not inspect the thing" into the same result. They are very different outcomes.&lt;/p&gt;

&lt;p&gt;Coverage limitations should survive all the way into the visible report. If part of the target was not inspected, say so. Confidence should go down instead of silently turning green.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use specialists for questions, not coverage theatre
&lt;/h2&gt;

&lt;p&gt;Launching a security reviewer, performance reviewer, architecture reviewer, and test reviewer on every patch looks thorough. In practice it creates duplicate findings, higher latency, and a new aggregation problem.&lt;/p&gt;

&lt;p&gt;A specialist is useful when the primary reviewer has a concrete unresolved question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Does this authorization check still protect the remote path?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That is much better than:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Review this code for security issues.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The assignment should be narrow, the expected evidence should be explicit, and the primary reviewer should still decide whether the answer is supported.&lt;/p&gt;

&lt;h2&gt;
  
  
  Make the judge conditional
&lt;/h2&gt;

&lt;p&gt;A third model should not rubber-stamp every clean review. That turns "independent validation" into a mandatory tax.&lt;/p&gt;

&lt;p&gt;Call a judge when a potentially serious finding needs validation, two reviewers conflict, or the final recommendation is still low-confidence. Ask it to inspect the disputed claim, not to repeat the entire review.&lt;/p&gt;

&lt;p&gt;The judge should be allowed to keep, downgrade, or reject a finding. Otherwise it is not a judge; it is another source of prose.&lt;/p&gt;

&lt;h2&gt;
  
  
  Separate review from fixing
&lt;/h2&gt;

&lt;p&gt;The review stage should be read-only.&lt;/p&gt;

&lt;p&gt;Once an agent starts editing while it is still reviewing, the evidence changes underneath the report. It also becomes harder for a human to approve the actual remediation scope.&lt;/p&gt;

&lt;p&gt;Produce the findings first. Let the user approve them. Then hand only the validated findings to a separate fixer that makes the smallest safe changes.&lt;/p&gt;

&lt;p&gt;This structure does not remove the need for human judgment. It makes the judgment cheaper because the evidence, uncertainty, and proposed remediation are no longer mixed together.&lt;/p&gt;

&lt;p&gt;I am building this loop into &lt;a href="https://github.com/GCWing/BitFun" rel="noopener noreferrer"&gt;BitFun&lt;/a&gt;: the Deep Review orchestrator is read-only, missing evidence stays visible as a coverage limitation, specialist calls are bounded, the quality judge is conditional, and remediation is a separate stage.&lt;/p&gt;

&lt;p&gt;The useful mental model is not "one agent writes and another agent reviews." It is:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;one process proposes a change; a separate process tests the proposal against bounded evidence.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That separation is where most of the value comes from.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>opensource</category>
      <category>programming</category>
      <category>agents</category>
    </item>
    <item>
      <title>Your AI Task Should Not End as a Chat Transcript</title>
      <dc:creator>Bob Lee</dc:creator>
      <pubDate>Thu, 06 Aug 2026 11:59:47 +0000</pubDate>
      <link>https://dev.to/bobleer/your-ai-task-should-not-end-as-a-chat-transcript-ljg</link>
      <guid>https://dev.to/bobleer/your-ai-task-should-not-end-as-a-chat-transcript-ljg</guid>
      <description>&lt;p&gt;Chat is an excellent way to begin a task. It is a surprisingly poor place for many tasks to end.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;This is not only a model problem. It is an interface problem.&lt;/p&gt;

&lt;p&gt;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 &lt;strong&gt;task surface&lt;/strong&gt; that owns its state and exposes the right actions.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Disclosure:&lt;/strong&gt; I maintain &lt;a href="https://github.com/GCWing/BitFun" rel="noopener noreferrer"&gt;BitFun&lt;/a&gt;, 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.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The missing object between chat and automation
&lt;/h2&gt;

&lt;p&gt;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 &lt;em&gt;now&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;That interface should own:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;domain objects and their current status;&lt;/li&gt;
&lt;li&gt;selections, filters, view mode, and revision;&lt;/li&gt;
&lt;li&gt;the relationship between an agent action and the object it affects;&lt;/li&gt;
&lt;li&gt;a place to inspect, accept, reject, or revise the result.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  A reference architecture
&lt;/h2&gt;

&lt;p&gt;BitFun's current Mini App implementation separates four responsibilities:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The Mini App owns presentation and domain state.&lt;/strong&gt; Its source model has an HTML/CSS/ESM browser layer and can support worker logic in non-marketplace profiles.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The desktop host owns privileged capabilities.&lt;/strong&gt; The iframe calls an injected &lt;code&gt;window.app&lt;/code&gt; bridge; filesystem, network, shell, AI, Agent, notifications, and host UI are represented as explicit capability groups.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;An owned agent session supplies continuity.&lt;/strong&gt; 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.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The normal scheduler owns execution.&lt;/strong&gt; A Mini App agent turn is submitted through the same dialog scheduler used by the desktop runtime; it may start immediately or be queued.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The data flow is compact:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;shared composer
      │  token-scoped user message
      ▼
sandboxed task UI ── relevant state snapshot ──► host bridge
      ▲                                             │
      │  session-filtered progress                  │ permission + ownership checks
      └──────────── owned agent session ◄───────────┘
                                  │
                                  ▼
                           dialog scheduler
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are two boundaries worth noticing.&lt;/p&gt;

&lt;p&gt;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 &lt;code&gt;agent.ensureSession&lt;/code&gt;, &lt;code&gt;agent.run&lt;/code&gt;, &lt;code&gt;chat.claimComposer&lt;/code&gt;, and &lt;code&gt;chat.focusSession&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  Binding conversation to the task surface
&lt;/h2&gt;

&lt;p&gt;The current API makes the binding explicit rather than implicit.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Claim the shared composer
&lt;/h3&gt;

&lt;p&gt;An agent-backed Mini App can call &lt;code&gt;app.chat.claimComposer()&lt;/code&gt;. While its tab is active, messages from the shared input are routed to that exact iframe as &lt;code&gt;chat:userMessage&lt;/code&gt; events.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Create or restore an owned session
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;app.agent.ensureSession()&lt;/code&gt; creates a dedicated session or validates a requested existing session. &lt;code&gt;app.chat.focusSession()&lt;/code&gt; then associates that known session with the Mini App's composer claim.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Snapshot state at submission time
&lt;/h3&gt;

&lt;p&gt;The host accepts a user-facing &lt;code&gt;displayText&lt;/code&gt; separately from the Mini App's internal &lt;code&gt;prompt&lt;/code&gt;. The transcript can therefore preserve what the user actually wrote while the agent receives a structured task protocol.&lt;/p&gt;

&lt;p&gt;Here is the pattern in simplified form:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;claimComposer&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Dependency Explorer&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;composer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;placeholder&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Ask about the current graph…&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;topic&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ensureSession&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;sessionName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Dependency Explorer&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;appDataWorkspace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;topics/current&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;focusSession&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;topic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sessionId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;onUserMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;displayText&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;collectRelevantState&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// app-owned model, not DOM scraping&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;protocol&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dependency-explorer/v1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;state&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;sessionId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;topic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sessionId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;displayText&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;displayText&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;collectRelevantState()&lt;/code&gt; 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 &lt;code&gt;agent.run()&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why explicit state beats a giant prompt
&lt;/h2&gt;

&lt;p&gt;The goal is not to serialize the entire application on every turn. A useful task surface creates a narrow boundary:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Keep durable domain state in the application.&lt;/li&gt;
&lt;li&gt;Send only the state relevant to the current action.&lt;/li&gt;
&lt;li&gt;Preserve the user's words separately from the internal protocol.&lt;/li&gt;
&lt;li&gt;Reuse a session only when conversational continuity is useful.&lt;/li&gt;
&lt;li&gt;Include a revision or other freshness signal when stale output would be harmful.&lt;/li&gt;
&lt;li&gt;Validate agent output before changing application state or external resources.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  Capability should follow the task, not the iframe
&lt;/h2&gt;

&lt;p&gt;A stateful interface is useful only if its authority is equally specific.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;The current public-market profile is intentionally stricter than the general source model:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;marketplace packages must explicitly disable Node;&lt;/li&gt;
&lt;li&gt;remote imports and dynamic code evaluation are rejected during package validation;&lt;/li&gt;
&lt;li&gt;broad home-directory and absolute filesystem scopes are rejected;&lt;/li&gt;
&lt;li&gt;marketplace iframes run with &lt;code&gt;sandbox="allow-scripts"&lt;/code&gt;, without same-origin access;&lt;/li&gt;
&lt;li&gt;hidden Agent turns from marketplace Mini Apps use a small allowlist centered on read-only web research rather than filesystem, shell, or host control.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this pattern does—and does not—solve
&lt;/h2&gt;

&lt;p&gt;It solves a real interface problem:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the user can point at an object instead of redescribing it;&lt;/li&gt;
&lt;li&gt;follow-up turns can continue in the task's own agent session;&lt;/li&gt;
&lt;li&gt;progress can appear beside the state it affects;&lt;/li&gt;
&lt;li&gt;permissions and ownership checks have explicit enforcement points.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It does &lt;strong&gt;not&lt;/strong&gt; 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.&lt;/p&gt;

&lt;p&gt;A one-off explanation probably belongs in chat. A task becomes a strong Mini App candidate when it has several of these properties:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;state changes over multiple turns;&lt;/li&gt;
&lt;li&gt;the user repeatedly selects or compares objects;&lt;/li&gt;
&lt;li&gt;the output needs review before it is applied;&lt;/li&gt;
&lt;li&gt;the same workflow will be reopened;&lt;/li&gt;
&lt;li&gt;a domain-specific visualization reveals more than prose.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  From answer-shaped output to software-shaped work
&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;The reusable idea is simple:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;conversation supplies intent;&lt;/li&gt;
&lt;li&gt;the task surface supplies structure and controls;&lt;/li&gt;
&lt;li&gt;an owned session supplies continuity;&lt;/li&gt;
&lt;li&gt;explicit state and host-mediated capabilities connect them.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;For verification rather than endorsement:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://market.openbitfun.com/miniapp/" rel="noopener noreferrer"&gt;Live Mini App gallery&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/GCWing/BitFun" rel="noopener noreferrer"&gt;BitFun source repository&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/GCWing/BitFun/releases/latest" rel="noopener noreferrer"&gt;Latest public release and platform builds&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/GCWing/BitFun/tree/e1dbe2ac3a7fd853f84b47711169d82c91fec32f/src/crates/contracts/product-domains/src/miniapp" rel="noopener noreferrer"&gt;Pinned Mini App implementation reviewed for this article&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Source notes
&lt;/h2&gt;

&lt;p&gt;The product-specific statements above were checked against these source locations, pinned so future changes do not silently move the evidence:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/GCWing/BitFun/blob/e1dbe2ac3a7fd853f84b47711169d82c91fec32f/src/crates/contracts/product-domains/src/miniapp/types.rs#L23-L38" rel="noopener noreferrer"&gt;Browser UI and optional worker source model&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/GCWing/BitFun/blob/e1dbe2ac3a7fd853f84b47711169d82c91fec32f/src/crates/contracts/product-domains/src/miniapp/types.rs#L40-L130" rel="noopener noreferrer"&gt;Capability groups, allowlists, and Agent limits&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/GCWing/BitFun/blob/e1dbe2ac3a7fd853f84b47711169d82c91fec32f/src/crates/contracts/product-domains/src/miniapp/types.rs#L139-L159" rel="noopener noreferrer"&gt;Host UI capability flags&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/GCWing/BitFun/blob/e1dbe2ac3a7fd853f84b47711169d82c91fec32f/src/crates/contracts/product-domains/src/miniapp/bridge_builder.rs#L120-L162" rel="noopener noreferrer"&gt;&lt;code&gt;window.app&lt;/code&gt; Agent and chat bridge contract&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/GCWing/BitFun/blob/e1dbe2ac3a7fd853f84b47711169d82c91fec32f/src/web-ui/src/app/scenes/miniapps/components/MiniAppRunner.tsx#L81-L119" rel="noopener noreferrer"&gt;Strict and compatibility iframe profiles&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/GCWing/BitFun/blob/e1dbe2ac3a7fd853f84b47711169d82c91fec32f/src/web-ui/src/app/scenes/miniapps/hooks/useMiniAppBridge.ts#L325-L423" rel="noopener noreferrer"&gt;Agent permission checks and session registration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/GCWing/BitFun/blob/e1dbe2ac3a7fd853f84b47711169d82c91fec32f/src/web-ui/src/app/scenes/miniapps/hooks/useMiniAppBridge.ts#L452-L529" rel="noopener noreferrer"&gt;Composer permission, claim, and session-focus checks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/GCWing/BitFun/blob/e1dbe2ac3a7fd853f84b47711169d82c91fec32f/src/web-ui/src/app/scenes/miniapps/hooks/useMiniAppBridge.ts#L658-L688" rel="noopener noreferrer"&gt;Token-scoped user-message routing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/GCWing/BitFun/blob/e1dbe2ac3a7fd853f84b47711169d82c91fec32f/src/web-ui/src/app/scenes/miniapps/hooks/useMiniAppBridge.ts#L724-L781" rel="noopener noreferrer"&gt;Session-filtered Agent event forwarding&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/GCWing/BitFun/blob/e1dbe2ac3a7fd853f84b47711169d82c91fec32f/src/apps/desktop/src/api/miniapp_agent_api.rs#L90-L135" rel="noopener noreferrer"&gt;Separate internal prompt and user-facing display text&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/GCWing/BitFun/blob/e1dbe2ac3a7fd853f84b47711169d82c91fec32f/src/apps/desktop/src/api/miniapp_agent_api.rs#L455-L510" rel="noopener noreferrer"&gt;Session reuse and scheduler submission&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/GCWing/BitFun/blob/e1dbe2ac3a7fd853f84b47711169d82c91fec32f/src/crates/contracts/product-domains/src/miniapp/agent_bridge.rs#L271-L285" rel="noopener noreferrer"&gt;Reused-session owner and workspace validation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/GCWing/BitFun/blob/e1dbe2ac3a7fd853f84b47711169d82c91fec32f/src/crates/services/miniapp-market-service/src/package.rs#L130-L171" rel="noopener noreferrer"&gt;Marketplace package validation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/GCWing/BitFun/blob/e1dbe2ac3a7fd853f84b47711169d82c91fec32f/src/crates/services/miniapp-market-service/src/package.rs#L300-L385" rel="noopener noreferrer"&gt;Marketplace filesystem and dynamic-code restrictions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/GCWing/BitFun/blob/e1dbe2ac3a7fd853f84b47711169d82c91fec32f/src/crates/execution/tool-contracts/src/framework.rs#L2317-L2357" rel="noopener noreferrer"&gt;Marketplace Agent tool allowlist&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>programming</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
