mirror of
https://github.com/deepseek-ai/deepseek-harness.git
synced 2026-10-26 16:55:08 +00:00
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.
69 lines
3.7 KiB
TypeScript
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 }
|
|
}
|