fix(web): orient agents to the running GUI

This commit is contained in:
NI0317
2026-07-28 17:20:20 +08:00
parent 15ed8e5d55
commit 568866d5a8
11 changed files with 131 additions and 9 deletions

View File

@@ -1,6 +1,6 @@
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write
README.md: 42d2a9641cf5d497c9aae45d9f60fce4498addb9
README.zh.md: 0a62f8bb72e2cf2dbe045d28b81768bf4df800de
# pnpm run verify-translation-pairing --write apps/cli/README.md
README.md: 87cf1597fbb1ce91a7d61f912c42b0d6a68b2eea
README.zh.md: b3743f57a4dafda71e437a04ee0148b32dba2dca

View File

@@ -14,7 +14,7 @@ The TUI surface:
- tells the agent where its own source lives: after boot it adds a prompt section naming this harness checkout, resolved from the launcher's real path so it holds under a PATH symlink and an arbitrary cwd, so the self-referential `cordis` toolset can read and modify it;
- applies the personal overlay from `~/.dsh` (see [app-boot's Personal config](../../packages/ui/app-boot/README.md#personal-config)): `.env` fills environment gaps (ambient > project `.env` > personal `.env`), `config.yaml` patches the booted tree.
The Web and headless surfaces boot one shared composition (`cordis.yml`): both treat the invoking directory as the default project and Workspace root, create named Workspaces beneath that root unless `--workspace-root <path>` overrides it, load applicable `AGENTS.md`/`CLAUDE.md` instructions into each agent-loop request prefix with a 65,536-byte render budget, and opt into first-message model titles. Headless differs only in listening on an OS-assigned port (parallel `dsh -p` runs never collide; the stderr-printed URL opens the live session in a browser). Both need the frontend dist and client bundles built (`pnpm run build && pnpm run build:web`).
The Web and headless surfaces boot one shared composition (`cordis.yml`): both tell the coding agent its model and session working directory, treat the invoking directory as the default project and Workspace root, create named Workspaces beneath that root unless `--workspace-root <path>` overrides it, load applicable `AGENTS.md`/`CLAUDE.md` instructions into each agent-loop request prefix with a 65,536-byte render budget, and opt into first-message model titles. Web additionally names the DeepSeek Harness Web GUI as the interaction surface and this checkout as its own source location; references such as “this page” therefore identify the GUI without claiming access to implicit DOM, route, or screenshot state. Headless differs only in listening on an OS-assigned port (parallel `dsh -p` runs never collide; the stderr-printed URL opens the live session in a browser). Both need the frontend dist and client bundles built (`pnpm run build && pnpm run build:web`).
`DSH_TOOLS_MODE` selects the tool presentation mode for the whole Web/headless process: `native` (the schema default when unset), `code` (the `run_code`-only Code Mode wire), or `both`; any other value fails loud at boot through the `dsh-tools` config schema. It is a TEMPORARY seam — process-wide because Loader composition is static — and is removed once the web UI owns per-session tool-mode selection; the TUI surface ignores it (its config tree pins its own mode).

View File

@@ -14,7 +14,7 @@ TUI 界面:
- 告知 agent 自身源码所在位置:启动后添加一个命名此 harness checkout 的提示词段。该路径从启动器的真实路径解析,因此在 PATH 符号链接和任意 cwd 下仍然有效,使自指的 `cordis` 工具集可以读取并修改它;
- 应用 `~/.dsh` 中的个人覆盖(参见 [app-boot 的个人配置](../../packages/ui/app-boot/README.md#personal-config)`.env` 填补环境缺口(环境中已有的值 > 项目 `.env` > 个人 `.env``config.yaml` 则修补已启动的树。
Web 和无头界面启动同一个共享组合(`cordis.yml`):两者都将调用目录视为默认项目和 Workspace 根目录,除非通过 `--workspace-root <path>` 覆盖,否则会在该根目录下创建具名 Workspace它们会把适用的 `AGENTS.md`/`CLAUDE.md` 指令加载到每个 agent-loop 请求前缀中,渲染预算为 65,536 字节,并选用首条消息模型标题。无头界面唯一的差异是监听操作系统分配的端口(并行 `dsh -p` 运行绝不冲突stderr 打印的 URL 会在浏览器中打开实时会话)。两者都需要先构建前端 dist 和客户端 bundle`pnpm run build && pnpm run build:web`)。
Web 和无头界面启动同一个共享组合(`cordis.yml`):两者都会告知编码 agent 所用模型和会话工作目录,将调用目录视为默认项目和 Workspace 根目录,除非通过 `--workspace-root <path>` 覆盖,否则会在该根目录下创建具名 Workspace它们会把适用的 `AGENTS.md`/`CLAUDE.md` 指令加载到每个 agent-loop 请求前缀中,渲染预算为 65,536 字节,并选用首条消息模型标题。Web 还会明确说明交互界面是 DeepSeek Harness Web GUI并把当前 checkout 标记为自身源码位置;因此,「这个页面」等表述会指向该 GUI但 agent 不会声称可以访问未显式提供的 DOM、路由或截图状态。无头界面唯一的差异是监听操作系统分配的端口(并行 `dsh -p` 运行绝不冲突stderr 打印的 URL 会在浏览器中打开实时会话)。两者都需要先构建前端 dist 和客户端 bundle`pnpm run build && pnpm run build:web`)。
`DSH_TOOLS_MODE` 为整个 Web无头进程选择工具呈现模式可选值为 `native`(未设置时的 schema 默认值)、`code`(仅含 `run_code` 的 Code Mode 协议接口)或 `both`;任何其他值都会经由 `dsh-tools` 配置 schema 在启动时明确报错。它是一个临时 seamLoader 组合是静态的,因此该设置作用于整个进程;待 Web UI 负责逐会话工具模式选择后便会移除。TUI 界面会忽略该变量(其配置树固定了自身模式)。

View File

@@ -40,7 +40,8 @@
- id: system-prompt
name: '@deepseek-ai/dsh-system-prompt'
config:
persona: ''
persona: >-
You are a coding agent powered by the {{model}} model. Your working directory is {{cwd}}.
- id: tools
name: '@deepseek-ai/dsh-tools'

View File

@@ -8,9 +8,32 @@
import { networkInterfaces } from 'node:os'
import { fileURLToPath } from 'node:url'
import type { Context } from 'cordis'
import { addHarnessSourceSection } from '@deepseek-ai/dsh-app-boot'
import type {} from '@deepseek-ai/dsh-system-prompt'
import { AppCLIEntry } from './app-cli-entry.ts'
const CONFIG_PATH = fileURLToPath(new URL('../cordis.yml', import.meta.url))
const SOURCE_ROOT = fileURLToPath(new URL('../../..', import.meta.url))
/** Stable model-visible orientation for sessions created through `dsh web`. */
export const WEB_SURFACE_PROMPT = 'You are interacting with the user through the DeepSeek Harness Web GUI. '
+ 'When the user refers to "this page", "this GUI", or "this app" without naming another target, they mean this GUI. '
+ 'The browser provides no implicit DOM, route, or screenshot context.'
/**
* Add the launcher-owned source location and Web-surface orientation after the
* shared config tree settles. The request header logs both sections with every
* model-visible prompt.
* @param ctx - settled Web application context.
* @param sourceRoot - absolute checkout root resolved from the launcher module.
*/
export function installWebPromptContext(ctx: Context, sourceRoot: string): void {
const systemPrompt = ctx.get('systemPrompt')
if (systemPrompt === undefined) throw new Error('dsh web: systemPrompt service missing after settled boot')
addHarnessSourceSection(ctx, sourceRoot)
systemPrompt.section({ name: 'app:web-surface', order: -98, text: WEB_SURFACE_PROMPT })
}
// Display-only mirrors of the webserver schema's allowed hosts: the loopback
// address the local URL always prints, and the all-interfaces value that gates
@@ -40,6 +63,7 @@ export async function runWeb(
...workspaceRoot !== undefined && { workspaceRoot },
})
const { ctx, port: boundPort } = await entry.run()
installWebPromptContext(ctx, SOURCE_ROOT)
let exiting = false
const shutdown = (code: number): void => {