Files
deepseek-harness/packages/experimental/plugin-recommendations/tests/fixture.ts
T
ZiyaZhang fcb7340be1 refactor(experimental): register plugin recommendations as an action card kind
Plugin recommendations now proposes through the shared propose_action tool
instead of a dsh-action answer block. The Host registers the
enable_official_plugin kind with the action card service: the
official-plugins skill, details holding the plugin name, the Enable verb
with global access, and the enabled, already-enabled and failed phases,
whose data carries the application outcome and the management error code.
The package drops its own projection, Remote act, status notice and
persisted record; it keeps the describe Remote for card text. The bundle
inserts the shared card rows beside its own, as Personalized pages does.
The browser half registers a card body in the shared frame that keeps the
catalog text cache, the reserved loading lines, the single details target
and the Enable and Not now footer. The skill's submission section now asks
for one propose_action call.
2026-10-22 07:18:05 -07:00

69 lines
3.7 KiB
TypeScript

/** Real Agent, Tools, skills, card service and projections with a controlled profile manager. */
import { Context } from '@deepseek-ai/cordis'
import { onTestFinished, vi } from 'vitest'
import { createToolResultMessage, ToolCallId } from '@deepseek-ai/dsh-llm'
import { SessionId, type SessionEvent } from '@deepseek-ai/dsh-session'
import { mountAgentLoopTestDependencies, mountAgentLoopTestHarness } from '@deepseek-ai/dsh-agent-loop-testkit'
import Skills from '@deepseek-ai/dsh-skill'
import * as SkillTool from '@deepseek-ai/dsh-tool-skill'
import RemoteAccess from '@deepseek-ai/dsh-api-remote-access'
import ActionCards from '@deepseek-ai/dsh-experimental-action-cards'
import type { Agent } from '@deepseek-ai/dsh-agent'
import type { BundleInfo, ChangeResult } from '@deepseek-ai/dsh-plugin-manager/types'
import * as PluginRecommendations from '../src/index.ts'
export const ACTION = 'enable_official_plugin'
export const bundle: BundleInfo = {
name: 'official-search', official: true, optional: true, availability: 'installation',
enabled: false, installed: false, removable: false, updatable: false, rows: [], overrides: [],
meta: { title: { en: 'Search', zh: '搜索' }, description: 'Search past conversations.' },
}
export const callId = ToolCallId('call-plugin-1')
export const proposal = { title: 'Search', brief: 'Search your conversation history.', details: { name: bundle.name } }
/**
* Log a successful `propose_action` call, as the card tool would after the skill load.
* @param ctx - context with the agent registry.
* @param agent - agent whose Session receives the call.
* @param id - tool call id of the card.
* @param args - call arguments besides `action`.
* @param action - card action.
* @returns the card's call id.
*/
export function logProposal(ctx: Context, agent: Agent, id = 'call-plugin-1', args: object = proposal, action = ACTION): ToolCallId {
const turn = Math.max(1, ctx.agents.turnBoundaryOf(agent.session).lastTurn)
const toolCallId = ToolCallId(id)
agent.session.append('tool/call', { turn, step: 1, callId: toolCallId, name: 'propose_action', arguments: JSON.stringify({ action, ...args }) })
agent.session.append('tool/result', { turn, step: 1, message: createToolResultMessage({ callId: toolCallId,
content: [{ type: 'text', text: 'Card shown below your answer.' }], isError: false }) }, { surfaceOp: 'append' })
return toolCallId
}
export async function fixture() {
const ctx = new Context()
onTestFinished(() => ctx.fiber.dispose())
await mountAgentLoopTestDependencies(ctx, { workingDirectory: true })
await mountAgentLoopTestHarness(ctx)
await ctx.plugin(RemoteAccess)
await ctx.plugin(Skills)
await ctx.plugin(SkillTool)
await ctx.plugin(ActionCards)
const manager = {
listBundles: vi.fn(async (): Promise<BundleInfo[]> => [bundle]),
setBundleEnabled: vi.fn(async (): Promise<ChangeResult> => ({ application: 'applied', changed: true, stage: 'enable', target: bundle.name, enabled: true })),
}
ctx.provide('pluginManager', manager)
const fiber = await ctx.plugin(PluginRecommendations)
const handle = await ctx.agents.create({ sessionId: SessionId('recommendations') })
const agent = handle.agent
const events: SessionEvent[] = []
ctx.on('session/event', (session, event) => { if (session === agent.session) events.push(event) })
const card = (id: ToolCallId = callId, caller: Agent = agent) => {
const decisions = ctx.actionCards.view(caller.session).decisions
return Object.hasOwn(decisions, id) ? decisions[id] : undefined
}
const propose = (id?: string, args?: object, caller: Agent = agent) => logProposal(ctx, caller, id, args)
return { ctx, agent, handle, manager, fiber, events, card, propose }
}