feat(goal): add human goal command
This commit is contained in:
@@ -12,6 +12,7 @@ stdout is the ACP JSON-RPC channel, so the cluster is defined as much by what it
|
||||
|---|---|
|
||||
| `@deepseek-ai/dsh-agent-spine-demo` | the spine, pre-creating **no** agents (ACP `session/new` creates them on demand) |
|
||||
| `@deepseek-ai/dsh-commands` | the human-command registry used for ACP discovery and direct slash dispatch |
|
||||
| `@deepseek-ai/dsh-command-goal` | the discoverable direct `/goal` producer; the app enables the spine's persisted-goal stack with it |
|
||||
| `@deepseek-ai/dsh-user-interaction` | the human question/answer seam used by clients that can complete ACP elicitation requests |
|
||||
| `@deepseek-ai/dsh-session-persistence-jsonl` | durable JSONL session log (the bridge advertises `loadSession`) |
|
||||
| `@deepseek-ai/dsh-acp` | the bridge that owns stdout for JSON-RPC and provides ACP-backed user answers when a leaf explicitly exposes a user-question tool |
|
||||
@@ -36,6 +37,7 @@ Because the package wires no logger entry, an ACP leaf has **nothing to get wron
|
||||
| `skills` | owner defaults | registry-cache, local-provider, and model-facing skill-tool config, routed through `dsh-agent-spine-demo` |
|
||||
| `toolBash` | owner defaults | model-facing bash config routed through `dsh-agent-spine-demo`, including bash's producer-local `enableRunInBackground` |
|
||||
| `toolTasks` | owner defaults | generic `task_output` wait bounds routed through `dsh-agent-spine-demo` |
|
||||
| `goals` | owner defaults | persisted goal-domain and model-tool config; `false` removes the goal stack and `/goal` producer |
|
||||
| `persistenceRoot` | `./.sessions` | the JSONL backend's root directory |
|
||||
|
||||
The leaf supplies the swappable backends: an LLM adapter (`llm-deepseek` for the real model, `llm-replay` for keyless snapshot replay) and a bash executor.
|
||||
@@ -54,7 +56,7 @@ All diagnostics go to **stderr** — stdout is the protocol.
|
||||
|
||||
## Model Experience
|
||||
|
||||
Indirectly, through `dsh-agent-spine-demo` and `dsh-acp`, which compose each ACP agent's prompt, tools, and message history; this app bundle adds no model-bound content itself.
|
||||
Indirectly, through `dsh-agent-spine-demo` and `dsh-acp`, which compose each ACP agent's prompt, goal tools, and message history. Direct `/goal` input and output remain outside the model, while accepted mutations append domain-owned model-visible snapshots.
|
||||
|
||||
#### KV Cache effect
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
"@deepseek-ai/dsh-app-boot": "^0.0.1",
|
||||
"@deepseek-ai/dsh-acp": "^0.0.1",
|
||||
"@deepseek-ai/dsh-commands": "^0.0.1",
|
||||
"@deepseek-ai/dsh-command-goal": "^0.0.1",
|
||||
"@deepseek-ai/dsh-agent-spine-demo": "^0.0.1",
|
||||
"@deepseek-ai/dsh-workspace-context": "^0.0.1",
|
||||
"@deepseek-ai/dsh-session-persistence-jsonl": "^0.0.1",
|
||||
@@ -49,6 +50,7 @@
|
||||
"@deepseek-ai/dsh-app-boot": "workspace:^",
|
||||
"@deepseek-ai/dsh-acp": "workspace:^",
|
||||
"@deepseek-ai/dsh-commands": "workspace:^",
|
||||
"@deepseek-ai/dsh-command-goal": "workspace:^",
|
||||
"@deepseek-ai/dsh-agent": "workspace:^",
|
||||
"@deepseek-ai/dsh-agent-spine-demo": "workspace:^",
|
||||
"@deepseek-ai/dsh-tools": "workspace:^",
|
||||
|
||||
@@ -13,6 +13,7 @@ import type { Context } from 'cordis'
|
||||
import z from 'schemastery'
|
||||
import * as acp from '@deepseek-ai/dsh-acp'
|
||||
import CommandService from '@deepseek-ai/dsh-commands'
|
||||
import * as commandGoal from '@deepseek-ai/dsh-command-goal'
|
||||
import * as agentCore from '@deepseek-ai/dsh-agent-spine-demo'
|
||||
import * as workspaceContext from '@deepseek-ai/dsh-workspace-context'
|
||||
import ToolRegistry, { type Config as ToolsConfig } from '@deepseek-ai/dsh-tools'
|
||||
@@ -56,6 +57,8 @@ export interface Config {
|
||||
toolBash?: NonNullable<agentCore.Config['toolBash']>
|
||||
/** Generic background-task controls forwarded through agent-core; set false to omit their tool surface. */
|
||||
toolTasks?: NonNullable<agentCore.Config['toolTasks']>
|
||||
/** Persisted same-session goals; owner defaults enable them, or false disables the stack and command. */
|
||||
goals?: agentCore.GoalConfig | false
|
||||
}
|
||||
|
||||
// Each front door owns a complete, directly readable config schema; extracting
|
||||
@@ -77,6 +80,7 @@ export const Config: z<Config> = z.object({
|
||||
skills: agentCore.SkillConfigSchema,
|
||||
toolBash: agentCore.ToolBashConfigSchema,
|
||||
toolTasks: z.union([z.const(false), agentCore.ToolTasksConfigSchema]),
|
||||
goals: z.union([z.const(false), agentCore.GoalConfigSchema]),
|
||||
})
|
||||
/* jscpd:ignore-end */
|
||||
|
||||
@@ -88,8 +92,10 @@ export const Config: z<Config> = z.object({
|
||||
* from the provider/model pair. No logger, no `hmr` — stdout stays pure.
|
||||
*/
|
||||
export function apply(ctx: Context, config: Config): void {
|
||||
const goals = config.goals ?? {}
|
||||
ctx.plugin(CommandService)
|
||||
ctx.plugin(agentCore, agentCore.pickSpineConfig(config))
|
||||
if (goals !== false) ctx.plugin(commandGoal)
|
||||
ctx.plugin(agentCore, { ...agentCore.pickSpineConfig(config), goals })
|
||||
ctx.plugin(UserInteractionService)
|
||||
ctx.plugin(SessionPersistenceJsonl, { root: config.persistenceRoot ?? DEFAULT_PERSISTENCE_ROOT })
|
||||
ctx.plugin(acp, { provider: config.provider, model: config.model })
|
||||
|
||||
@@ -77,11 +77,30 @@ describe('dsh-acp-demo composition', () => {
|
||||
expect(ctx.get('agentLoop')).toBeDefined()
|
||||
expect(ctx.get('userInteraction')).toBeDefined()
|
||||
expect(ctx.get('tools')?.get('ask_user_question')).toBeUndefined()
|
||||
expect(ctx.get('goals')).toBeDefined()
|
||||
expect(ctx.get('tools')?.get('get_goal')).toBeDefined()
|
||||
// No pre-created agents — ACP session/new creates them on demand.
|
||||
expect(ctx.get('agents')!.list()).toHaveLength(0)
|
||||
await ctx.fiber.dispose()
|
||||
})
|
||||
|
||||
it('can explicitly omit the persisted-goal stack and its command', async () => {
|
||||
const ctx = await mount({
|
||||
provider: 'mock',
|
||||
model: 'mock',
|
||||
goals: false,
|
||||
workspaceContext: false,
|
||||
})
|
||||
expect(ctx.get('goals')).toBeUndefined()
|
||||
const handle = await ctx.agents.create({
|
||||
sessionId: 'disabled-goals' as import('@deepseek-ai/dsh-session').SessionId,
|
||||
agentOptions: { provider: 'mock', model: 'mock' },
|
||||
})
|
||||
expect(ctx.commands.find(handle.agent, 'acp', 'goal')).toBeUndefined()
|
||||
await handle.dispose()
|
||||
await ctx.fiber.dispose()
|
||||
})
|
||||
|
||||
it('defaults the persistence root when omitted', async () => {
|
||||
// Exercises the `DEFAULT_PERSISTENCE_ROOT` fallback for a direct-apply caller that
|
||||
// bypasses the schema's `.default(...)`: call `apply` directly (not via
|
||||
@@ -179,7 +198,17 @@ describe('dsh-acp-demo composition', () => {
|
||||
})
|
||||
}
|
||||
const assembly = await ctx.get('systemPrompt')!.assemble()
|
||||
expect(assembly.tools.map(tool => tool.name)).toEqual(['zulu', 'alpha', 'skill', 'task_kill', 'task_list', 'task_output'])
|
||||
expect(assembly.tools.map(tool => tool.name)).toEqual([
|
||||
'zulu',
|
||||
'alpha',
|
||||
'create_goal',
|
||||
'get_goal',
|
||||
'skill',
|
||||
'task_kill',
|
||||
'task_list',
|
||||
'task_output',
|
||||
'update_goal',
|
||||
])
|
||||
await ctx.fiber.dispose()
|
||||
})
|
||||
|
||||
|
||||
@@ -26,6 +26,9 @@
|
||||
{
|
||||
"path": "../../ui/commands"
|
||||
},
|
||||
{
|
||||
"path": "../../goal/command-goal"
|
||||
},
|
||||
{
|
||||
"path": "../../core/agent"
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user