mirror of
https://github.com/deepseek-ai/deepseek-harness
synced 2026-08-15 21:04:50 +00:00
Machine-produced by `pnpm run rescope-vendor --apply` plus the regeneration it prints: `pnpm install` for the lockfile, `pnpm run gen-third-party-notices`, `verify-translation-pairing --write` for the touched bilingual pairs, `gen-doc-graphs`, and one typert snapshot whose ids embed character offsets. `pnpm run rescope-vendor --check` verifies the result. Renames nine vendored packages (cordis, cosmokit, schemastery and the six @cordisjs plugins) and every reference that resolves them: manifest names and dependency keys, module specifiers including declare-module merges, cordis.yml plugin names, tsconfig paths, every Markdown fence, and `docs/` prose. Directory names, upstream versions, and dependency ranges are unchanged, so vendor/README.md still reads as an upstream snapshot; its manifest table gains an upstream-name column so THIRD_PARTY_NOTICES keeps MIT attribution pointed at each fork's origin. The tutorial tier follows the rename end to end: its yaml fences named plugins the Loader can no longer resolve, its `ts ignore-check` fences disagreed with the compiled fences beside them, and its prose quoted both. The contracts that told readers to keep upstream names — the root convention and the vendoring cookbook's tree comment and manifest invariant — now say to rescope instead. Two rules read `@deepseek-ai/` as "another workspace plugin": the client bundle purity gate now names the vendored libraries a browser bundle inlines, and the files where a bare `cordis` is an agent-preset id keep that product data.
155 lines
6.4 KiB
TypeScript
155 lines
6.4 KiB
TypeScript
import { describe, expect, it, vi } from 'vitest'
|
|
import { Context } from '@deepseek-ai/cordis'
|
|
import AgentRegistry from '@deepseek-ai/dsh-agent'
|
|
import type { Agent } from '@deepseek-ai/dsh-agent'
|
|
import SessionStore from '@deepseek-ai/dsh-session'
|
|
import type { Session, SessionEvent, SessionHeader, SessionId } from '@deepseek-ai/dsh-session'
|
|
import { createApiRemoteAgentResolver } from '@deepseek-ai/dsh-api-remotes'
|
|
import { TypeRTLookupFailure } from '@deepseek-ai/dsh-type-meta'
|
|
import TypertRegistry from '@deepseek-ai/dsh-typert-registry'
|
|
|
|
const sid = (value: string): SessionId => value as SessionId
|
|
|
|
function header(id: SessionId): SessionHeader {
|
|
return { version: 0, id, createdAt: 1, cwd: '/proj' }
|
|
}
|
|
|
|
async function createContext(): Promise<Context> {
|
|
const ctx = new Context()
|
|
await ctx.plugin(TypertRegistry)
|
|
await ctx.plugin(SessionStore)
|
|
await ctx.plugin(AgentRegistry)
|
|
return ctx
|
|
}
|
|
|
|
function provideSession(
|
|
ctx: Context,
|
|
meta: SessionHeader,
|
|
inspect: () => Promise<{ meta: SessionHeader; events: SessionEvent[] }>,
|
|
): void {
|
|
ctx.provide('sessionPersistence', {
|
|
list: () => Promise.resolve([meta]),
|
|
inspect,
|
|
locate: () => undefined,
|
|
} as never)
|
|
}
|
|
|
|
function stubAgent(ctx: Context, session: Session): Agent {
|
|
return { id: session.id, session, status: 'idle', ctx } as Agent
|
|
}
|
|
|
|
describe('API Remote Agent resolver races', () => {
|
|
it('maps an inspected session without a cwd to session-not-found', async () => {
|
|
const ctx = await createContext()
|
|
const sessionId = sid('missing-after-inspect')
|
|
const meta = header(sessionId)
|
|
provideSession(ctx, meta, () => Promise.resolve({
|
|
meta: { ...meta, cwd: undefined } as unknown as SessionHeader,
|
|
events: [],
|
|
}))
|
|
|
|
const result = await createApiRemoteAgentResolver(ctx, {})(sessionId)
|
|
|
|
expect(result).toMatchObject({ error: { code: 'session-not-found', details: { sessionId } } })
|
|
await ctx.fiber.dispose()
|
|
})
|
|
|
|
it('resumes through a concurrently attached ordinary Session without optional defaults', async () => {
|
|
const ctx = await createContext()
|
|
const sessionId = sid('ordinary-attach-race')
|
|
const meta = header(sessionId)
|
|
let published: Session | undefined
|
|
provideSession(ctx, meta, () => {
|
|
published = ctx.sessions.create(sessionId, { meta: { cwd: '/proj' } })
|
|
return Promise.resolve({ meta, events: [] })
|
|
})
|
|
const resume = vi.spyOn(ctx.agents, 'resume').mockImplementation(async () => {
|
|
if (published === undefined) throw new Error('Session was not published')
|
|
return { agent: stubAgent(ctx, published), dispose: () => Promise.resolve() }
|
|
})
|
|
|
|
const result = await createApiRemoteAgentResolver(ctx, {})(sessionId)
|
|
|
|
expect(result).toMatchObject({ agent: { id: sessionId } })
|
|
expect(resume).toHaveBeenCalledWith({ resumeSessionId: sessionId })
|
|
await ctx.fiber.dispose()
|
|
})
|
|
|
|
it('rejects a subagent Session published after durable inspection', async () => {
|
|
const ctx = await createContext()
|
|
const sessionId = sid('owned-attach-race')
|
|
const meta = header(sessionId)
|
|
provideSession(ctx, meta, () => {
|
|
ctx.sessions.create(sessionId, { meta: { cwd: '/proj', origin: 'subagent' } })
|
|
return Promise.resolve({ meta, events: [] })
|
|
})
|
|
const resume = vi.spyOn(ctx.agents, 'resume')
|
|
|
|
const result = await createApiRemoteAgentResolver(ctx, {})(sessionId)
|
|
|
|
expect(result).toMatchObject({ error: { code: 'agent-busy' } })
|
|
expect(resume).not.toHaveBeenCalled()
|
|
await ctx.fiber.dispose()
|
|
})
|
|
|
|
it('reclassifies failed resumes after a live or attached subagent wins publication', async () => {
|
|
for (const winner of ['agent', 'session'] as const) {
|
|
const ctx = await createContext()
|
|
const sessionId = sid(`owned-${winner}-resume-race`)
|
|
const meta = header(sessionId)
|
|
provideSession(ctx, meta, () => Promise.resolve({ meta, events: [] }))
|
|
vi.spyOn(ctx.agents, 'resume').mockImplementationOnce(async () => {
|
|
const session = ctx.sessions.create(sessionId, { meta: { cwd: '/proj', origin: 'subagent' } })
|
|
if (winner === 'agent') ctx.agents.register(stubAgent(ctx, session))
|
|
throw new Error('session id already published')
|
|
})
|
|
|
|
const result = await createApiRemoteAgentResolver(ctx, {})(sessionId)
|
|
|
|
expect(result).toMatchObject({ error: { code: 'agent-busy' } })
|
|
await ctx.fiber.dispose()
|
|
}
|
|
})
|
|
|
|
it('uses the shared cold-resume policy for the Agent Host Context', async () => {
|
|
const ctx = await createContext()
|
|
const sessionId = sid('context-cold-resume')
|
|
const meta = header(sessionId)
|
|
let published: Session | undefined
|
|
provideSession(ctx, meta, () => {
|
|
published = ctx.sessions.create(sessionId, { meta: { cwd: '/proj' } })
|
|
return Promise.resolve({ meta, events: [] })
|
|
})
|
|
const agentCtx = ctx.extend()
|
|
vi.spyOn(ctx.agents, 'resume').mockImplementation(async () => {
|
|
if (published === undefined) throw new Error('Session was not published')
|
|
return { agent: stubAgent(agentCtx, published), dispose: () => Promise.resolve() }
|
|
})
|
|
const defaultProvider = ctx.typert.contexts.getHost('agent')
|
|
createApiRemoteAgentResolver(ctx, {})
|
|
await vi.waitFor(() => { expect(ctx.typert.contexts.getHost('agent')).not.toBe(defaultProvider) })
|
|
const provider = ctx.typert.contexts.getHost('agent')
|
|
if (provider === undefined) throw new Error('Agent Host Context provider was not mounted')
|
|
|
|
await expect(provider.resolve(sessionId)).resolves.toBe(agentCtx)
|
|
await ctx.fiber.dispose()
|
|
})
|
|
|
|
it('applies the subagent ownership fence to the Agent Host Context', async () => {
|
|
const ctx = await createContext()
|
|
const sessionId = sid('context-owned-subagent')
|
|
const session = ctx.sessions.create(sessionId, { meta: { cwd: '/proj', origin: 'subagent' } })
|
|
ctx.agents.register(stubAgent(ctx.extend(), session))
|
|
const defaultProvider = ctx.typert.contexts.getHost('agent')
|
|
createApiRemoteAgentResolver(ctx, {})
|
|
await vi.waitFor(() => { expect(ctx.typert.contexts.getHost('agent')).not.toBe(defaultProvider) })
|
|
const provider = ctx.typert.contexts.getHost('agent')
|
|
if (provider === undefined) throw new Error('Agent Host Context provider was not mounted')
|
|
|
|
const resolution = provider.resolve(sessionId)
|
|
await expect(resolution).rejects.toBeInstanceOf(TypeRTLookupFailure)
|
|
await expect(resolution).rejects.toMatchObject({ failure: { code: 'agent-busy' } })
|
|
await ctx.fiber.dispose()
|
|
})
|
|
})
|