Files
deepseek-harness/packages/examples/agent-spine-demo/README.zh.md
Turtle f290a8b851 refactor(cli)!: one shared base config with per-surface overlays
`dsh` shipped two config trees that were 43 rows the same: apps/cli/cordis.yml
composed web as 74 flat rows, while the TUI booted examples/tui-agent/cordis.yml
whose single `@deepseek-ai/dsh-tui-demo` row mounted twelve plugins behind a
twenty-key pass-through Config. Neither file was what its location claimed —
apps/cli hardcoded the "example" as the product default and the "demo" bundle
was the application — and every capability change had to be made twice.

- apps/cli/base.cordis.yml holds the 43 shared rows; tui.cordis.yml and
  web.cordis.yml are patch lists stating only what differs per surface
- overlays apply as SIBLING patch lists at one include level, because include
  patches never cross an include boundary. Precedence: base < surface <
  (--config | personal ~/.dsh/config.yaml) < launcher flag/profile patches
- `--config` now applies an overlay INSTEAD OF the personal one, so a demo or
  test tree never inherits the user's route; new `--config-replace` boots a file
  as the entire tree (the old `--config` behaviour). Both survive /resume
- vendor/include: index each `insert`ed row as it is added so a later patch can
  configure or disable it. Upstream built the id index once before the patch
  loop, leaving every surface-only row — the whole TUI front door — silently
  unpatchable from user config. Logged as local modification 8
- session identity moves to dsh-agent-loop's CONFIGURED_AGENT_IDENTITIES_KEY;
  dsh-tui's MAIN_SESSION_ID_KEY is deleted (only the bundle read it)
- delete examples/tui-agent, examples/cordis-agent, packages/examples/tui-demo;
  TUI tests → apps/cli/tests, cordis e2e → packages/cordis/tool-cordis/tests,
  examples/code-mode survives as an overlay leaf
- `dsh web` gains --config, threaded into AppCLIEntry as an extra overlay

Three latent defects surfaced and are fixed here: the TUI captured the optional
sessionQuery service once at construction and could permanently disable /resume
when it won the mount race; the session-store root silently reverted to a
project-local ./.sessions; --config-replace was dropped by the resume handoff.

Verified by booting each tree through the real Loader (TUI 55 entries, web 75,
zero unsettled) rather than reading YAML. All eight terminal snapshots replay
byte-identically; 14/14 PTY smoke, 112/112 snapshots, 25/25 doc-sync, hygiene
and lint clean.
2026-07-29 21:15:42 +08:00

8.7 KiB
Raw Blame History

@deepseek-ai/dsh-agent-spine-demo

English | 中文

默认的不含执行器、不含 UI 的 agent智能体主干 作为一个 Cordis 组合包插件。它加载每个 harness agent 所需的固定服务集合,包括本地 skill技能提供方并将循环的 agents 列表作为自身配置转发。因此应用包package只需添加前端入口和可替换后端就能组合出可工作的 agent。

阅读此包可了解完整插件树及其组合顺序。

它加载的插件树

apply(ctx, config) 将以下每个插件挂载为组合包 fiber 的子节点:

@cordisjs/plugin-timer            timer service (writes nothing to stdout)
@deepseek-ai/dsh-llm              abstract LLM service + content-block vocabulary
@deepseek-ai/dsh-session          event-sourced session log + store
@deepseek-ai/dsh-session-title    log-backed title service + deterministic fallback
@deepseek-ai/dsh-system-prompt    prompt-section + tool-schema assembly
@deepseek-ai/dsh-tools            registry + guarded pre/around/post/final-result pipeline
@deepseek-ai/dsh-skill            skill provider registry
@deepseek-ai/dsh-skill-local      local filesystem skill provider
@deepseek-ai/dsh-agent            agent registry + initiator scope + agent/* events
@deepseek-ai/dsh-goal             optional persisted same-session goal domain
@deepseek-ai/dsh-tool-goal        optional model-facing goal controls
@deepseek-ai/dsh-goal-session     optional same-session goal-round driver
@deepseek-ai/dsh-llm-retry        provider-routed request retry policy
@deepseek-ai/dsh-tasks-local      generic background-task registry
@deepseek-ai/dsh-invariants       configurable invariant registry service
@deepseek-ai/dsh-session/invariant
@deepseek-ai/dsh-agent/invariant
@deepseek-ai/dsh-scope/invariant
@deepseek-ai/dsh-agent-loop/invariant
                                  package-owned relational checks
@deepseek-ai/dsh-tool-bash        the model-facing bash schema
@deepseek-ai/dsh-workspace-context  AGENTS.md/CLAUDE.md workspace context loader
@deepseek-ai/dsh-tool-skill       session-prefix skill catalog + model-facing loader schema
@deepseek-ai/dsh-tool-tasks       task_output/task_list/task_kill schemas + completion notices
@deepseek-ai/dsh-agent-loop       THE concrete loop (gets the forwarded `agents`)
                                  (dsh-system-prompt gets the forwarded `persona`)

有意留在组合包外的组件

主干包含每个前端入口都共有的全部组件。可替换组件和与前端入口耦合的组件留在外部,由加载组合包的一方选择:

  • LLM大语言模型适配器:组合包交付抽象 llm 服务;叶节点在 ctx.llm 上注册具体适配器(llm-deepseekllm-pi-aillm-replay)。
  • 基于模型的会话标题提供方组合包挂载带可覆盖示例限制的后备服务5 个词、40 个后备字节、80 个可接受标题字节);叶节点可以恰好选用一个首消息或全消息 LLM 提供方。
  • bash 执行器:组合包交付 tool-bash(消费方 schema叶节点提供 ctx.bashbash-local 或沙箱化实现)。
  • 非本地 skill 提供方:组合包交付 skill 注册表、本地文件系统提供方和 skill 工具;部署可以把嵌入式目录或远程目录等其他提供方作为同级插件添加。 <<<<<<< HEAD
  • 前端入口与各应用基础设施:终端 TUI 或 ACPAgent Client Protocol自动化传输以及 hmr。应用包(dsh-tui-demodsh-acp-demo)拥有这些选择。timer 位于主干中,因为它是共有组件且不写 stdout前端入口拥有 stdout因此留在组合包外。 =======
  • 前端入口与各应用基础设施:终端 TUI 或 ACP 自动化传输,以及 hmr。应用包(dsh-cli-demodsh-acp-demo)拥有这些选择。timer 位于主干中,因为它是共有组件且不写 stdout前端入口拥有 stdout因此留在组合包外。

a1c6a2c3f (refactor(cli)!: one shared base config with per-surface overlays)

这把接口/实现/消费方 seam 提升到组合层:组合包拥有共享主干,叶节点拥有后端,应用包拥有前端入口。

配置

import type { Config } from '@deepseek-ai/dsh-agent-spine-demo'
// { agents?, maxParallelToolCalls?, persona?, toolOrder?, tools?, dshHome?, sessionTitle?, skills?, workspaceContext, toolBash?, toolTasks?, goals?, invariants? }
// workspaceContext requires { maxBytes } or false; the other owner schemas supply defaults.

组合包将每个字段转发给拥有它的子节点:agentsmaxParallelToolCalls 交给 agent-loopagents 默认为 [],上限在该处默认),因此每个应用提供自己的预创建 agentTUI 和无头应用预创建 mainACP 应用则在 session/new 按需创建 agentpersonatoolOrder 交给 dsh-system-prompttools 交给工具注册表以配置呈现模式;sessionTitle 交给后备标题服务;skills.registryskills.localskills.tool 分别交给 skill 注册表、本地提供方和面向模型的消费方;必填的 workspaceContext 选择交给 dsh-workspace-context{ maxBytes } 启用加载,false 禁用);invariants 交给不变式服务;toolBash/toolTasks 交给组合包拥有的两个面向模型工具插件。组合包始终挂载 dsh-llm-retry,而每个叶节点适配器拥有自己的嵌套 retryPolicy。省略 sessionTitle 时采用显式示例策略5 个词、40 个后备字节、80 个可接受标题字节。goals 对象会选用持久化领域、模型工具和同会话 Goal Round 驱动器,并将 goals.domaingoals.tool 转发给各自拥有者;省略或设为 false 会让整个栈缺席,使无头调用方继续以单轮次结算。设置 skills.enabled: false 会同时省略本地提供方和面向模型的 skill 工具;设置 toolTasks: false 会保留供前台生产方使用的任务服务,但不公开 task_output/task_list/task_kill。它对 dshHome 只解析一次,解析通过 @deepseek-ai/dsh-paths 完成,并将所得绝对值转发给 tool-bash 的托管环境和已启用的本地 skill 发现。顶层 dshHome 缺席时采用 skills.local.dshHome;两者同时提供但解析后的路径不同会明确失败。toolBash.enableRunInBackground 只控制 bash 生产方;独立加载的生产方保留各自配置。工作区指令先于 skill 目录注册,因此其会话前缀消息先渲染。应用包使用 pickSpineConfig(),只复制这些由组合包拥有的字段。

例如,{ invariants: { enabled: true, package_allowlist: ['^@deepseek-ai/dsh-'], package_blocklist: ['agent-loop$'] } } 会让包拥有的配套插件保持挂载但抑制被阻止的拥有者。Blocklist 匹配优先于 allowlist 匹配;正则表达式与生命周期规则见 dsh-invariants

为何使用代码组合包,而非共享 YAML include

YAML include 可以去重配置,却无法拥有 bin 或提供前端入口默认值。ACP 应用包默认接出协议纯净的 stdout但叶节点仍可添加不安全的 logger。组合包子节点把服务注册到根 isolate-keyed store因此叶节点的同级插件无需依赖加载顺序即可通过注入看到它们。

重试策略可能在新的编号步骤中重复失败的请求。重试状态、提供方错误和失败的部分分片不进入模型历史每次提供方尝试仍可能产生计费always 模式没有尝试次数上限;前端入口从所有已记录步骤推导用量;重建的请求保留先前前缀,以便复用提供方缓存。

模型体验

模型通过 dsh-system-promptdsh-tool-skilldsh-tool-bashdsh-toolsdsh-llm-retry 间接获得体验;还会通过 dsh-tool-goal 与 Goal Round 提示词获得体验,前提是启用 goals。组合包自身不添加面向模型的包装内容。

KV Cache 影响

不会直接失效;上述消费方负责请求前缀的任何变更。

已知限制与暂缓事项

  • 大部分主干集合固定在代码中apply() 始终挂载核心服务与 tool-bash配置可以省略组合包内的目标、skill 与任务控制工具,但要替换循环或删除其他主干成员,就必须组合另一个组合包。
  • 不变式 seam 与配套插件仍是固定成员invariants.enabled: false 或包筛选器会抑制检查但不会移除服务或配套插件注册Session 始终启用的校验与冻结是另一套机制。