feat(skill): move catalogs into session prefixes
This commit is contained in:
@@ -14,12 +14,12 @@ This is the package to read to see **the whole plugin tree at once** — the tea
|
||||
@deepseek-ai/dsh-session event-sourced session log + store
|
||||
@deepseek-ai/dsh-system-prompt prompt-section + tool-schema assembly
|
||||
@deepseek-ai/dsh-tools tool registry + tools/pre-execute/post-execute
|
||||
@deepseek-ai/dsh-skill skill provider registry + prompt listing
|
||||
@deepseek-ai/dsh-skill skill provider registry
|
||||
@deepseek-ai/dsh-skill-local local filesystem skill provider
|
||||
@deepseek-ai/dsh-agent agent registry + agent/* event vocabulary
|
||||
@deepseek-ai/dsh-invariants dev-mode event-contract assertions
|
||||
@deepseek-ai/dsh-tool-bash the model-facing bash/bash_output/bash_kill schemas
|
||||
@deepseek-ai/dsh-tool-skill the model-facing skill loader schema
|
||||
@deepseek-ai/dsh-tool-skill session-prefix skill catalog + model-facing loader schema
|
||||
@deepseek-ai/dsh-agent-loop THE concrete loop (gets the forwarded `agents`)
|
||||
(dsh-system-prompt gets the forwarded `persona`)
|
||||
```
|
||||
@@ -43,7 +43,7 @@ import type { Config } from '@deepseek-ai/dsh-agent-core'
|
||||
// so validation and defaulting can never drift from the owners.
|
||||
```
|
||||
|
||||
The bundle FORWARDS each field to the child that owns it: `agents` to `agent-loop` (default `[]`), so each app supplies its own pre-created agents — a stdio app pre-creates a `main`; the ACP app pre-creates none (it creates agents on demand at `session/new`) — `persona` to `dsh-system-prompt` (default `''`), the deployment's persona section; `toolOrder` to `dsh-system-prompt` (absent — lexicographic), the explicit model-facing tool order; and `skills.registry` / `skills.local` to the skill registry and local provider. Forwarding is exactly why the owners can live in the shared spine even though the apps disagree on what to configure.
|
||||
The bundle FORWARDS each field to the child that owns it: `agents` to `agent-loop` (default `[]`), so each app supplies its own pre-created agents — a stdio app pre-creates a `main`; the ACP app pre-creates none (it creates agents on demand at `session/new`) — `persona` to `dsh-system-prompt` (default `''`), the deployment's persona section; `toolOrder` to `dsh-system-prompt` (absent — lexicographic), the explicit model-facing tool order; and `skills.registry`, `skills.local`, and `skills.tool` to the skill registry, local provider, and model-facing consumer. Forwarding is exactly why the owners can live in the shared spine even though the apps disagree on what to configure.
|
||||
|
||||
## Why a code bundle, not a shared YAML include
|
||||
|
||||
|
||||
@@ -62,12 +62,14 @@ import AgentLoop, { type Config as AgentLoopConfig } from '@deepseek-ai/dsh-agen
|
||||
|
||||
export const name = 'agent-core'
|
||||
|
||||
/** Skill bundle config forwarded to the registry and the local provider. */
|
||||
/** Skill bundle config forwarded to the registry, local provider, and model-facing consumer. */
|
||||
export interface SkillConfig {
|
||||
/** Registry-level prompt/cache settings. */
|
||||
/** Registry-level discovery cache settings. */
|
||||
registry?: SkillRegistryConfig
|
||||
/** Local filesystem skill provider settings. */
|
||||
local?: SkillLocal.Config
|
||||
/** Model-facing skill catalog and tool settings. */
|
||||
tool?: toolSkill.Config
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -75,7 +77,7 @@ export interface SkillConfig {
|
||||
* `agents` to the agent loop (an app that pre-creates no agents, like the ACP
|
||||
* bridge, simply omits it), `persona` and `toolOrder` to the system-prompt
|
||||
* plugin (the deployment's persona section and the explicit model-facing tool
|
||||
* order), and `skills` to the skill registry/local provider. Every field is
|
||||
* order), and `skills` to the skill registry/local provider/tool consumer. Every field is
|
||||
* optional INPUT here because each owner's schema supplies the default (`[]` /
|
||||
* `''` / absent — lexicographic / the DSH skill roots); the schema is the
|
||||
* INTERSECTION of the owners' own schemas, so validation and defaulting can
|
||||
@@ -88,7 +90,7 @@ export interface Config {
|
||||
persona?: SystemPromptConfig['persona']
|
||||
/** The explicit model-facing tool order (see dsh-system-prompt's `Config`). */
|
||||
toolOrder?: SystemPromptConfig['toolOrder']
|
||||
/** Skill registry and local provider config. */
|
||||
/** Skill registry, local provider, and model-facing consumer config. */
|
||||
skills?: SkillConfig
|
||||
}
|
||||
|
||||
@@ -96,6 +98,7 @@ export interface Config {
|
||||
export const SkillConfigSchema: z<SkillConfig> = z.object({
|
||||
registry: SkillService.Config,
|
||||
local: SkillLocal.Config,
|
||||
tool: toolSkill.Config,
|
||||
})
|
||||
|
||||
/** Intersect the owners' schemas so validation + defaulting stay identical. */
|
||||
@@ -134,6 +137,6 @@ export function apply(ctx: Context, config: Config): void {
|
||||
ctx.plugin(AgentRegistry)
|
||||
ctx.plugin(invariants)
|
||||
ctx.plugin(toolBash)
|
||||
ctx.plugin(toolSkill)
|
||||
ctx.plugin(toolSkill, config.skills?.tool ?? {})
|
||||
ctx.plugin(AgentLoop, { agents: config.agents ?? [] })
|
||||
}
|
||||
|
||||
@@ -7,6 +7,15 @@ import Loader from '@cordisjs/plugin-loader'
|
||||
import { TOOL_ORDER_REST } from '@deepseek-ai/dsh-system-prompt'
|
||||
import * as agentCore from '../src/index.ts'
|
||||
import { AgentId } from '@deepseek-ai/dsh-agent'
|
||||
import type { Message } from '@deepseek-ai/dsh-llm'
|
||||
|
||||
async function composePrefix(ctx: Context, cwd: string): Promise<Message[]> {
|
||||
const empty: Message[] = []
|
||||
return await ctx.waterfall(
|
||||
'agent/session-prefix', { session: { header: { cwd } } } as never,
|
||||
empty, new AbortController().signal, () => Promise.resolve(empty),
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Unit coverage for the @deepseek-ai/dsh-agent-core bundle: mounting it brings
|
||||
@@ -120,7 +129,7 @@ describe('dsh-agent-core bundle', () => {
|
||||
await ctx.fiber.dispose()
|
||||
})
|
||||
|
||||
it('forwards skill config to the registry and local provider', async () => {
|
||||
it('forwards skill config to the registry, local provider, and model-facing consumer', async () => {
|
||||
const home = await mkdtemp(join(tmpdir(), 'dsh-agent-core-skill-home-'))
|
||||
const agentsHome = await mkdtemp(join(tmpdir(), 'dsh-agent-core-skill-agents-'))
|
||||
const custom = await mkdtemp(join(tmpdir(), 'dsh-agent-core-skill-custom-'))
|
||||
@@ -129,16 +138,17 @@ describe('dsh-agent-core bundle', () => {
|
||||
const ctx = await mount({
|
||||
agents: [],
|
||||
skills: {
|
||||
registry: { promptFieldMaxLength: 6 },
|
||||
registry: { collectCacheMaxEntries: 4 },
|
||||
local: {
|
||||
dshHome: join(home, '.dsh'),
|
||||
agentsHome: join(agentsHome, '.agents'),
|
||||
customSkillDirs: [custom],
|
||||
},
|
||||
tool: { catalogDescriptionMaxLength: 6 },
|
||||
},
|
||||
})
|
||||
expect((await ctx.skills.list()).map(skill => skill.name)).toEqual(['custom-skill'])
|
||||
expect(await ctx.skills.renderModelListing()).toContain('description: Cus...')
|
||||
expect(JSON.stringify(await composePrefix(ctx, '/tmp'))).toContain('- `custom-skill`: Cus...')
|
||||
await ctx.fiber.dispose()
|
||||
})
|
||||
|
||||
|
||||
@@ -33,13 +33,13 @@
|
||||
"path": "../../core/tools"
|
||||
},
|
||||
{
|
||||
"path": "../../core/skill"
|
||||
"path": "../../skill/skill"
|
||||
},
|
||||
{
|
||||
"path": "../../core/skill-local"
|
||||
"path": "../../skill/skill-local"
|
||||
},
|
||||
{
|
||||
"path": "../../core/tool-skill"
|
||||
"path": "../../skill/tool-skill"
|
||||
},
|
||||
{
|
||||
"path": "../../core/agent"
|
||||
|
||||
Reference in New Issue
Block a user