fix(web): close the GUI update feedback loop

This commit is contained in:
NI0317
2026-07-28 18:04:21 +08:00
parent 34aed0ab09
commit 544d543ad1
15 changed files with 188 additions and 19 deletions

View File

@@ -11,28 +11,47 @@ 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 type {} from '@deepseek-ai/dsh-tool-bash'
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`. */
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.'
const DSH_WEB_URL = 'DSH_WEB_URL' as const
/** Model-visible orientation and acceptance boundary for sessions created through `dsh web`. */
function webSurfacePrompt(webUrl: string): string {
return `You are interacting with the user through the DeepSeek Harness Web GUI at ${webUrl}. `
+ '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. '
+ 'For changes to this GUI, rebuild the affected Web artifacts and verify this existing URL after a refresh; starting another server does not update this GUI. '
+ 'The apps/web Vite entry builds the shell but is not a standalone application because only dsh web injects window.__DSH_BOOT__. '
+ 'Do not start a replacement server unless the user asks; if one is needed, use a managed background task and verify its exact URL.'
}
/**
* 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.
* Add launcher-owned source, Web-surface orientation, and the shell-visible
* canonical URL after the shared config tree settles. The request header logs
* the model-visible sections; each bash execution receives the same URL through
* the managed environment.
* @param ctx - settled Web application context.
* @param sourceRoot - absolute checkout root resolved from the launcher module.
* @param webUrl - canonical loopback URL printed by this Web process.
*/
export function installWebPromptContext(ctx: Context, sourceRoot: string): void {
export function installWebPromptContext(ctx: Context, sourceRoot: string, webUrl: string): void {
const systemPrompt = ctx.get('systemPrompt')
if (systemPrompt === undefined) throw new Error('dsh web: systemPrompt service missing after settled boot')
const bashEnv = ctx.get('bashEnv')
if (bashEnv === undefined) throw new Error('dsh web: bashEnv service missing after settled boot')
addHarnessSourceSection(ctx, sourceRoot)
systemPrompt.section({ name: 'app:web-surface', order: -98, text: WEB_SURFACE_PROMPT })
systemPrompt.section({ name: 'app:web-surface', order: -98, text: webSurfacePrompt(webUrl) })
bashEnv.register({
name: 'web-runtime',
variables: {
[DSH_WEB_URL]: { description: 'Canonical local URL of the DeepSeek Harness Web GUI serving this session.' },
},
resolve: () => ({ [DSH_WEB_URL]: webUrl }),
})
}
// Display-only mirrors of the webserver schema's allowed hosts: the loopback
@@ -63,7 +82,8 @@ export async function runWeb(
...workspaceRoot !== undefined && { workspaceRoot },
})
const { ctx, port: boundPort } = await entry.run()
installWebPromptContext(ctx, SOURCE_ROOT)
const localUrl = `http://${LOOPBACK_HOST}:${boundPort}`
installWebPromptContext(ctx, SOURCE_ROOT, localUrl)
let exiting = false
const shutdown = (code: number): void => {
@@ -76,7 +96,6 @@ export async function runWeb(
? Object.values(networkInterfaces()).flat()
.find(iface => iface !== undefined && iface.family === 'IPv4' && !iface.internal)
: undefined
const localUrl = `http://${LOOPBACK_HOST}:${boundPort}`
console.log(`dsh web: ${localUrl}${lanCandidate === undefined ? '' : ` (LAN: http://${lanCandidate.address}:${boundPort})`}`)
process.on('SIGTERM', () => { shutdown(0) })