Merge remote-tracking branch 'origin/master' into worktree/web-multimodal-image-input
# Conflicts: # .agents/notes/implemented/architecture/2026-07-05-reconstructable-requests.i18n.yaml # docs/core-data-structures/core.i18n.yaml # docs/module-graph.md # packages/client/ui-conversation/src/client/chat/AssistantMarkdown.tsx # packages/client/ui-conversation/src/client/index.ts # packages/compact/compact-basic/README.i18n.yaml
This commit is contained in:
@@ -228,7 +228,10 @@ describe('skill.list', () => {
|
||||
// touch (or resume through) the Agent registry.
|
||||
const session = ctx.sessions.create(undefined, { meta: { cwd: '/proj' } })
|
||||
const value = expectOk(await api.skills.list(request({ sessionId: session.id })))
|
||||
expect(value.skills).toEqual([{ name: 'commit-helper', description: 'Git commits', whenToUse: 'when committing' }])
|
||||
expect(value.skills).toEqual([
|
||||
{ name: 'commit-helper', description: 'Git commits', whenToUse: 'when committing', modelInvocable: true },
|
||||
{ name: 'user-only', description: 'User-only', modelInvocable: false },
|
||||
])
|
||||
expect(seenCwds).toEqual(['/proj'])
|
||||
expect(ctx.agents.get(session.id)).toBeUndefined()
|
||||
})
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { Context } from 'cordis'
|
||||
import type { Agent } from '@deepseek-ai/dsh-agent'
|
||||
import AgentRegistry, { type Agent } from '@deepseek-ai/dsh-agent'
|
||||
import SessionStore from '@deepseek-ai/dsh-session'
|
||||
import UserInteractionService from '@deepseek-ai/dsh-user-interaction'
|
||||
import type { ApiProxy, MuxFrame, RpcRequest } from '@deepseek-ai/dsh-host-apiproxy/api'
|
||||
@@ -10,6 +10,7 @@ import { createApiProxy } from '../src/api-proxy.ts'
|
||||
async function harness(): Promise<{ ctx: Context; api: ApiProxy }> {
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(SessionStore)
|
||||
await ctx.plugin(AgentRegistry)
|
||||
await ctx.plugin(UserInteractionService)
|
||||
return {
|
||||
ctx,
|
||||
@@ -17,8 +18,11 @@ async function harness(): Promise<{ ctx: Context; api: ApiProxy }> {
|
||||
}
|
||||
}
|
||||
|
||||
function agent(id: string): Agent {
|
||||
return { id } as unknown as Agent
|
||||
function agent(ctx: Context): Agent {
|
||||
const session = ctx.sessions.create()
|
||||
const value = { id: session.id, session, status: 'idle', ctx } as Agent
|
||||
ctx.agents.register(value)
|
||||
return value
|
||||
}
|
||||
|
||||
function openMux(api: ApiProxy, abort: AbortController): {
|
||||
@@ -71,7 +75,7 @@ describe('question response validation', () => {
|
||||
const abort = new AbortController()
|
||||
const mux = openMux(api, abort)
|
||||
const asked = ctx.userInteraction.ask({
|
||||
agent: agent('session-multi'),
|
||||
agent: agent(ctx),
|
||||
questions: [{
|
||||
id: 'targets',
|
||||
question: 'Choose targets and add another',
|
||||
@@ -95,7 +99,7 @@ describe('question response validation', () => {
|
||||
const abort = new AbortController()
|
||||
const mux = openMux(api, abort)
|
||||
const asked = ctx.userInteraction.ask({
|
||||
agent: agent('session-single'),
|
||||
agent: agent(ctx),
|
||||
questions: [{
|
||||
id: 'target',
|
||||
question: 'Choose one target',
|
||||
|
||||
@@ -202,7 +202,7 @@ function fakeApi(overrides: Partial<{ muxFrames: MuxFrame[]; hostFrames: HostFra
|
||||
},
|
||||
skills: {
|
||||
async list(request) {
|
||||
return { rpcId: request.rpcId, result: { ok: true, value: { skills: [{ name: 'commit-helper', description: 'Git commits' }] } } }
|
||||
return { rpcId: request.rpcId, result: { ok: true, value: { skills: [{ name: 'commit-helper', description: 'Git commits', modelInvocable: true }] } } }
|
||||
},
|
||||
},
|
||||
goals: {
|
||||
@@ -388,7 +388,7 @@ describe('unary round trip (handler ⇄ client, no network)', () => {
|
||||
const miss = await c.commands.execute({ sessionId: 's' as never, line: '/nope' })
|
||||
expect(miss.result).toEqual({ ok: true, value: { matched: false } })
|
||||
const skills = await c.skills.list({ sessionId: 's' as never })
|
||||
expect(skills.result).toEqual({ ok: true, value: { skills: [{ name: 'commit-helper', description: 'Git commits' }] } })
|
||||
expect(skills.result).toEqual({ ok: true, value: { skills: [{ name: 'commit-helper', description: 'Git commits', modelInvocable: true }] } })
|
||||
})
|
||||
|
||||
it('lets command.execute finish after the 30-second default unary deadline', async () => {
|
||||
|
||||
@@ -395,12 +395,15 @@ describe('skills domain schemas', () => {
|
||||
expect(() => skillListRequestSchema.parse({})).toThrow()
|
||||
expect(skillListValueSchema.parse({ skills: [] }).skills).toEqual([])
|
||||
const value = skillListValueSchema.parse({ skills: [
|
||||
{ name: 'commit-helper', description: 'Git commits', whenToUse: 'when committing' },
|
||||
{ name: 'bare', description: 'No guidance' },
|
||||
{ name: 'commit-helper', description: 'Git commits', whenToUse: 'when committing', modelInvocable: true },
|
||||
{ name: 'bare', description: 'No guidance', modelInvocable: false },
|
||||
] })
|
||||
expect(value.skills[0]?.whenToUse).toBe('when committing')
|
||||
expect(value.skills[1]?.whenToUse).toBeUndefined()
|
||||
expect(() => skillEntrySchema.parse({ name: '', description: 'd' })).toThrow()
|
||||
expect(value.skills[1]?.modelInvocable).toBe(false)
|
||||
expect(() => skillEntrySchema.parse({ name: '', description: 'd', modelInvocable: true })).toThrow()
|
||||
// modelInvocable is required wire data: an entry without it fails.
|
||||
expect(() => skillEntrySchema.parse({ name: 'n', description: 'd' })).toThrow()
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user