mirror of
https://github.com/deepseek-ai/deepseek-harness
synced 2026-08-15 21:04:50 +00:00
The browser e2e lane had been failing wholesale since this stack moved the agent plane into presets, and nothing caught it: 34 of 48 files. Two of the causes are product defects, not test breakage. `bashEnv` goes back to the host plane. `apps/cli/src/web.ts` injects it to publish `DSH_WEB_URL`/`DSH_WEB_MODE`, so the earlier note that "nothing outside the agent plane injects bashEnv" was simply wrong — behind a preset's `shell` realm those variables reached no shell at all, and a `dsh web` agent could not find the address of its own interface. This is the same criterion that returned `subagents`: a host row that injects a service resolves before any session exists and has no agent to key by, so the service is host-plane. `tool-bash` consumes the host registry from inside the preset, which works because an agent context chains to the host; only the reverse is invisible. `tool-subagent-report` goes back with it. It is not a tool this agent calls: it registers a continuable SETUP on the host `subagents` singleton, and that list is not scope-aware. One copy per mounted preset meant every child was handed `report` once per live session, so the second registration threw and a cold subagent resume failed with `subagent-not-resumable` — a diagnostic three layers removed from the cause. The lane's own composition facts follow. Skill roots resolve inside a preset now, a subtree include patches cannot reach, so the scaffold pins the roots' documented environment fallback for its whole lifetime rather than for the boot — presets mount per session. Without it the developer's real `~/.dsh/skills` enters replay requests and goldens while CI sees none. The `apps/cli` composition test pins `storage-json` for the same reason: unpinned it wrote, and then read back, the developer's own `~/.dsh/storages/`. Three tests now address through an agent what they used to read off the root context, because that is where the thing lives: the tool catalog, the skill registry, and the token meter. The seeded-history projection baseline asserts the opposite of what it did — a detached session yields a preset-plane projection only from a durable checkpoint written while it was live, and this seed was written straight to persistence and never ran. Goldens re-recorded for the hero's preset chip and the settings nav entry.
76 lines
3.1 KiB
TypeScript
76 lines
3.1 KiB
TypeScript
import { mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises'
|
|
import { tmpdir } from 'node:os'
|
|
import { join } from 'node:path'
|
|
import { expect, it } from 'vitest'
|
|
import type {} from '@deepseek-ai/dsh-skill'
|
|
import { SessionId } from '@deepseek-ai/dsh-session'
|
|
import type {} from '@deepseek-ai/dsh-agent-presets'
|
|
import { launchWebScaffold, type WebScaffold } from './scaffold.ts'
|
|
|
|
async function writeSkill(root: string, name: string): Promise<void> {
|
|
const bundle = join(root, name)
|
|
await mkdir(bundle, { recursive: true })
|
|
await writeFile(join(bundle, 'SKILL.md'), `---
|
|
name: ${name}
|
|
description: Must not enter the Web replay scaffold
|
|
---
|
|
|
|
Ambient host state.
|
|
`)
|
|
}
|
|
|
|
it('isolates replay skill discovery from every ambient host root', async () => {
|
|
const ambient = await mkdtemp(join(tmpdir(), 'dsh-web-ambient-skills-'))
|
|
const dshHome = join(ambient, 'dsh-home')
|
|
const agentsHome = join(ambient, 'agents-home')
|
|
const bundled = join(ambient, 'bundled')
|
|
await Promise.all([
|
|
writeSkill(join(dshHome, 'skills'), 'ambient-dsh'),
|
|
writeSkill(join(agentsHome, 'skills'), 'ambient-agents'),
|
|
writeSkill(bundled, 'ambient-bundled'),
|
|
])
|
|
|
|
const originalDshHome = process.env.DSH_HOME
|
|
const originalAgentsHome = process.env.DSH_AGENTS_HOME
|
|
const originalBundled = process.env.DSH_BUNDLED_SKILL_DIR
|
|
process.env.DSH_HOME = dshHome
|
|
process.env.DSH_AGENTS_HOME = agentsHome
|
|
process.env.DSH_BUNDLED_SKILL_DIR = bundled
|
|
let scaffold: WebScaffold | undefined
|
|
try {
|
|
scaffold = await launchWebScaffold()
|
|
const ctx = scaffold.ctx
|
|
// The skill registry belongs to one agent's preset, behind an `isolate`
|
|
// realm the host cannot resolve by name — so the roots under test are only
|
|
// reachable through a composed agent, which is also the only shape that
|
|
// ever asks. `serviceFor` is the same addressing the gateway's `skill.list`
|
|
// uses for a browser request about a session.
|
|
const handle = await ctx.agents.create({
|
|
sessionId: SessionId('hermetic-skills'),
|
|
setup: agentCtx => ctx.agentPresets.mount(agentCtx).then(() => undefined),
|
|
})
|
|
try {
|
|
const skills = ctx.agentPresets.serviceFor(handle.agent, 'skills')
|
|
if (skills === undefined) throw new Error('composed agent mounts no skill registry')
|
|
const names = (await skills.list({ cwd: scaffold.workspaceCwd })).map(skill => skill.name)
|
|
expect(names).not.toContain('ambient-dsh')
|
|
expect(names).not.toContain('ambient-agents')
|
|
expect(names).not.toContain('ambient-bundled')
|
|
} finally {
|
|
await handle.dispose()
|
|
}
|
|
} finally {
|
|
try {
|
|
await scaffold?.close()
|
|
} finally {
|
|
if (originalDshHome === undefined) delete process.env.DSH_HOME
|
|
else process.env.DSH_HOME = originalDshHome
|
|
if (originalAgentsHome === undefined) delete process.env.DSH_AGENTS_HOME
|
|
else process.env.DSH_AGENTS_HOME = originalAgentsHome
|
|
if (originalBundled === undefined) delete process.env.DSH_BUNDLED_SKILL_DIR
|
|
else process.env.DSH_BUNDLED_SKILL_DIR = originalBundled
|
|
await rm(ambient, { recursive: true, force: true })
|
|
}
|
|
}
|
|
})
|