2nd1st/dsh-plugin-open-app ↗★ 7
@2nd1st/dsh-plugin-open-app
侧边栏聚合应用会话,支持每应用独立工作区与工具内嵌交互 适合需要同界面切换多个外部应用并保持独立上下文的场景,需配置应用引擎。
安裝
npx -p @deepseek-ai/dsh dsh plugin --profile web add github:2nd1st/dsh-plugin-open-app說明文件
閱讀完整 README ↗Configuration
Every setting is optional; the defaults match a stock local engine and the mcp-oma row
the bundle ships.
Configure it from your profile's own cordis.patch.yml by naming the bundle's row id —
never by inserting the row again. An id-targeted patch replaces the key it names
rather than merging into it, so write the whole config you want:
- id: open-app
config:
# Where the engine's HTTP face listens.
engineBase: 'http://127.0.0.1:8787'
# The `serverName` of the engine's mcp-client entry. It decides the
# `mcp____` prefix the inline tool views are keyed on.
serverName: 'oma'
# Where the per-app workspace directories are made. One directory per app,
# named after it. Defaults to $DSH_HOME/storages/open-app/apps.
appsRoot: '~/.dsh/storages/open-app/apps'
# The container's rules, carried as a system-prompt section on every
# request a container's agent makes. Replaces the shipped app-only
# prompt entirely (see below for what it has to keep doing). `{app}` is
# the app's name and `{card}` the declaration card the host half builds
# from the engine's registry. An empty string retires the rules: the
# containers then run as ordinary dsh sessions that happen to have an
# app in a tab.
containerPrompt: |
You live in the {app} app — this conversation is its container.
{card}
Never call open_app, app_html or any open_* tool here; the panel beside
this chat is already showing the app. Answer in one sentence — that line
is what the user reads on the status bar under it.
# The one line a brand-new container opens with, which is only there to
# end the blank state and to draw the first receipt onto the strip. It
# carries no rules. `{app}` is substituted. Set it to an empty string to
# open containers silently — they then stay blank until you speak, and
# the app waits in a row above the composer instead of in its own tab.
installMessage: 'What is in the app right now?'
Pinned apps and what each app's place is made of are kept per-browser in
localStorage, under dsh-plugin-open-app:pins and dsh-plugin-open-app:containers
(unscoped, and staying that way — the package moved to a scope in 0.1.1, but renaming a
storage key would silently unpin every app somebody had pinned).
Only the pins are irreplaceable: the workspace registration is the durable binding, so
clearing the container map costs nothing — the next visit re-adopts the same directory,
the same workspace and the conversation already accounted under it.
Usage
An app is a place, not a page. Opening shopping-list opens the shopping list and
the talk about it; opening it tomorrow resumes both. So each app gets a dsh
workspace of its own — a directory the plugin makes under its apps root, registered
with dsh — and its conversation is created inside that workspace, never in one of
yours. Inside it:
- Apps — the app's UI, first in the view ring, with a one-line strip under it saying what the agent is doing right now.
- Chat — this app's conversation, still there when you come back.
- Trajectory — unchanged.
- The composer stays docked below all of them, so you can be looking at the app and telling the model to change it in the same breath.
In an app container the app is the reply: you say "mark the milk as bought" and the row ticks itself, because the widget holds its own live connection to the engine. That is why the strip under it is not a small chat window — it is the agent's presence. It shows the tool being called in the same glance as the change it causes, and it makes the two things you could otherwise miss impossible to miss: a question waiting for you, and a failure.
A container is exclusive: inside an app's session the Apps tab shows that app and nothing else. There is no way to browse out of it, because a container you can escape is not a container — switching apps means going to the other app's node. The directory of apps lives in the two places that are nobody's container: the sidebar's own overlay, and the Apps tab of an ordinary chat session.
What it adds
| Apps section in the sidebar | All apps (the directory), App Store (where new ones come from), and one node per pinned app. |
| App containers | Clicking an app node opens that app's workspace and its own session, with the app's UI on top and its history underneath. |
| App mode | A container's agent gets a prompt of its own — who it is here, the app's card (what it is, the collections its rows live in, their shape, the functions it declares), what it must not call, how short a reply should be. It is a system-prompt section, assembled fresh for every request in a session that lives in an app directory, and it costs an ordinary chat session nothing. The container's session also runs on an App mode preset, so dsh names the mode where dsh names modes. |
| An opening line | A brand-new container is asked one question — "What is in the app right now?" — because a session dsh has never been spoken to gets no view ring, and because the answer is the first line of the strip under the app. |
| Apps tab ahead of Chat | Registered at order -10, so the app leads and the conversation follows. Chat and Trajectory keep their seats. |
| The agent's presence | A one-line strip under the app: what the agent is doing right now (the tool it is calling, the answer arriving), what it last said, and — unmissably — when it is waiting for you or has failed. It reads as speech, not as source: the line is set in dsh's own content column and the model's markdown is read out flat, since nothing here can typeset it. Click it for the whole conversation. |
| App workspaces stay out of your way | The directories the plugin registers are hidden from the workspace tree and the New Session picker, and New Session never lands inside an app. |
| App → app stays inside the model | "Open X" from the App Store opens X's own container instead of navigating the store's frame (engine ?nav=intent). |
| Inline app rendering | In an ordinary chat session, when the model calls open_app (or a per-app open_* tool), the tool card becomes the running app, with an Open in Apps button under it that takes you into the app's own container. That seat is for ordinary chats: inside a container the same call is what the opening prompt forbids, because the panel is already showing the app. |
That last seat is the one an ordinary chat sees most: no container, no workspace of its own — the app simply arrives where the tool call was, and the conversation goes on around it. The Open in Apps pill under the frame is the way onward: the card is a live app in somebody else's conversation, and everything you can do to it there you can also do in its own place, with its history under it and a composer aimed at it.
