dsh-plugin-dev-kb 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (234) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +56 -0
  3. package/cordis.patch.yml +12 -0
  4. package/kb/INDEX.md +210 -0
  5. package/kb/README.md +69 -0
  6. package/kb/extra/AGENTS.md +75 -0
  7. package/kb/extra/api-gateway.md +164 -0
  8. package/kb/extra/api-gateway.zh.md +164 -0
  9. package/kb/extra/cookbook/adding-a-vendored-package.md +59 -0
  10. package/kb/extra/cookbook/adding-a-vendored-package.zh.md +59 -0
  11. package/kb/extra/cookbook/maintaining-dsh-code-review.md +64 -0
  12. package/kb/extra/cookbook/maintaining-dsh-code-review.zh.md +64 -0
  13. package/kb/extra/cookbook/responding-to-pr-review-on-a-stack.md +32 -0
  14. package/kb/extra/cookbook/responding-to-pr-review-on-a-stack.zh.md +32 -0
  15. package/kb/extra/defensive-patterns.md +33 -0
  16. package/kb/extra/defensive-patterns.zh.md +33 -0
  17. package/kb/extra/development.md +171 -0
  18. package/kb/extra/development.zh.md +171 -0
  19. package/kb/extra/event-producer-consumer.md +76 -0
  20. package/kb/extra/event-producer-consumer.zh.md +78 -0
  21. package/kb/extra/glossary.md +45 -0
  22. package/kb/extra/glossary.zh.md +45 -0
  23. package/kb/extra/graph-atlas.md +24 -0
  24. package/kb/extra/graph-atlas.zh.md +26 -0
  25. package/kb/extra/i18n/README.md +60 -0
  26. package/kb/extra/i18n/README.zh.md +60 -0
  27. package/kb/extra/i18n/style-samples.md +87 -0
  28. package/kb/extra/i18n/terminology.md +214 -0
  29. package/kb/extra/i18n/translation-prompt.md +263 -0
  30. package/kb/extra/i18n/translation-rules.md +69 -0
  31. package/kb/extra/i18n/translation-rules.zh.md +69 -0
  32. package/kb/extra/module-graph.md +1641 -0
  33. package/kb/extra/module-graph.zh.md +1643 -0
  34. package/kb/extra/postmortem/0001-acp-default-export-drops-inject.md +113 -0
  35. package/kb/extra/postmortem/0001-acp-default-export-drops-inject.zh.md +113 -0
  36. package/kb/extra/postmortem/0002-js-expression-disabled-filesystem-tools.md +47 -0
  37. package/kb/extra/postmortem/0002-js-expression-disabled-filesystem-tools.zh.md +47 -0
  38. package/kb/extra/postmortem/0003-web-agent-gui-feedback-loop.md +53 -0
  39. package/kb/extra/postmortem/0003-web-agent-gui-feedback-loop.zh.md +53 -0
  40. package/kb/extra/postmortem/0004-landlock-partial-notice-misclassified-child-failures.md +55 -0
  41. package/kb/extra/postmortem/0004-landlock-partial-notice-misclassified-child-failures.zh.md +55 -0
  42. package/kb/extra/postmortem/README.md +18 -0
  43. package/kb/extra/postmortem/README.zh.md +18 -0
  44. package/kb/extra/rescope.md +53 -0
  45. package/kb/extra/rescope.zh.md +53 -0
  46. package/kb/extra/subsystems/attachment.md +125 -0
  47. package/kb/extra/subsystems/attachment.zh.md +125 -0
  48. package/kb/extra/subsystems/extensions.md +364 -0
  49. package/kb/extra/subsystems/extensions.zh.md +364 -0
  50. package/kb/extra/subsystems/feedback.md +266 -0
  51. package/kb/extra/subsystems/feedback.zh.md +266 -0
  52. package/kb/extra/testing.md +49 -0
  53. package/kb/extra/testing.zh.md +49 -0
  54. package/kb/extra/web-styling.md +25 -0
  55. package/kb/extra/web-styling.zh.md +25 -0
  56. package/kb/meta/search-index.json +1328 -0
  57. package/kb/meta/site-pages.txt +168 -0
  58. package/kb/meta/source.json +13 -0
  59. package/kb/meta/topics.md +75 -0
  60. package/kb/site/develop/basic/config.md +108 -0
  61. package/kb/site/develop/basic/index.md +146 -0
  62. package/kb/site/develop/basic/publish.md +185 -0
  63. package/kb/site/develop/basic/tool.md +54 -0
  64. package/kb/site/develop/cordis-tutorial/01-first-plugin.md +95 -0
  65. package/kb/site/develop/cordis-tutorial/02-lifecycle-and-effects.md +98 -0
  66. package/kb/site/develop/cordis-tutorial/03-services.md +98 -0
  67. package/kb/site/develop/cordis-tutorial/04-events.md +144 -0
  68. package/kb/site/develop/cordis-tutorial/05-config.md +84 -0
  69. package/kb/site/develop/cordis-tutorial/06-composition-and-hmr.md +113 -0
  70. package/kb/site/develop/cordis-tutorial/07-into-the-harness.md +107 -0
  71. package/kb/site/develop/cordis-tutorial/index.md +62 -0
  72. package/kb/site/develop/framework/events.md +145 -0
  73. package/kb/site/develop/framework/index.md +139 -0
  74. package/kb/site/develop/framework/service.md +152 -0
  75. package/kb/site/develop/practice/index.md +157 -0
  76. package/kb/site/develop/practice/llm-adapter.md +190 -0
  77. package/kb/site/en/develop/basic/config.md +108 -0
  78. package/kb/site/en/develop/basic/index.md +146 -0
  79. package/kb/site/en/develop/basic/publish.md +185 -0
  80. package/kb/site/en/develop/basic/tool.md +54 -0
  81. package/kb/site/en/develop/cordis-tutorial/01-first-plugin.md +95 -0
  82. package/kb/site/en/develop/cordis-tutorial/02-lifecycle-and-effects.md +98 -0
  83. package/kb/site/en/develop/cordis-tutorial/03-services.md +98 -0
  84. package/kb/site/en/develop/cordis-tutorial/04-events.md +144 -0
  85. package/kb/site/en/develop/cordis-tutorial/05-config.md +84 -0
  86. package/kb/site/en/develop/cordis-tutorial/06-composition-and-hmr.md +113 -0
  87. package/kb/site/en/develop/cordis-tutorial/07-into-the-harness.md +107 -0
  88. package/kb/site/en/develop/cordis-tutorial/index.md +60 -0
  89. package/kb/site/en/develop/framework/events.md +145 -0
  90. package/kb/site/en/develop/framework/index.md +139 -0
  91. package/kb/site/en/develop/framework/service.md +150 -0
  92. package/kb/site/en/develop/practice/index.md +157 -0
  93. package/kb/site/en/develop/practice/llm-adapter.md +190 -0
  94. package/kb/site/en/guide/providers-custom-form.png +0 -0
  95. package/kb/site/en/guide/providers-models-page.png +0 -0
  96. package/kb/site/en/guide/providers.md +100 -0
  97. package/kb/site/en/guide/python-sdk.md +106 -0
  98. package/kb/site/en/guide/quickstart.md +32 -0
  99. package/kb/site/en/index.md +8 -0
  100. package/kb/site/en/reference/agent-lifecycle.md +86 -0
  101. package/kb/site/en/reference/capability-seams.md +475 -0
  102. package/kb/site/en/reference/config-catalog.md +3155 -0
  103. package/kb/site/en/reference/cookbook/adding-a-conversation-node.md +235 -0
  104. package/kb/site/en/reference/cookbook/adding-a-package.md +120 -0
  105. package/kb/site/en/reference/cookbook/adding-a-settings-card.md +102 -0
  106. package/kb/site/en/reference/cookbook/adding-a-tool.md +96 -0
  107. package/kb/site/en/reference/cookbook/adding-an-llm-adapter.md +45 -0
  108. package/kb/site/en/reference/cookbook/extension-cookbook.md +131 -0
  109. package/kb/site/en/reference/cordis-api/context.md +368 -0
  110. package/kb/site/en/reference/cordis-api/events.md +211 -0
  111. package/kb/site/en/reference/cordis-api/fiber.md +379 -0
  112. package/kb/site/en/reference/cordis-api/inherited.md +43 -0
  113. package/kb/site/en/reference/cordis-api/registry.md +156 -0
  114. package/kb/site/en/reference/cordis-api/service.md +106 -0
  115. package/kb/site/en/reference/cordis-primer.md +46 -0
  116. package/kb/site/en/reference/index.md +131 -0
  117. package/kb/site/en/reference/persistence-catalog.md +949 -0
  118. package/kb/site/en/reference/subsystems/approval.md +173 -0
  119. package/kb/site/en/reference/subsystems/client-modules.md +121 -0
  120. package/kb/site/en/reference/subsystems/code-runtime.md +194 -0
  121. package/kb/site/en/reference/subsystems/commands.md +190 -0
  122. package/kb/site/en/reference/subsystems/compaction.md +241 -0
  123. package/kb/site/en/reference/subsystems/core.md +1073 -0
  124. package/kb/site/en/reference/subsystems/credentials.md +136 -0
  125. package/kb/site/en/reference/subsystems/filesystem.md +498 -0
  126. package/kb/site/en/reference/subsystems/goal.md +280 -0
  127. package/kb/site/en/reference/subsystems/index.md +58 -0
  128. package/kb/site/en/reference/subsystems/invariants.md +91 -0
  129. package/kb/site/en/reference/subsystems/jobs.md +293 -0
  130. package/kb/site/en/reference/subsystems/llm-streaming.md +920 -0
  131. package/kb/site/en/reference/subsystems/lsp.md +205 -0
  132. package/kb/site/en/reference/subsystems/permission-presets.md +134 -0
  133. package/kb/site/en/reference/subsystems/persistence.md +388 -0
  134. package/kb/site/en/reference/subsystems/plan.md +90 -0
  135. package/kb/site/en/reference/subsystems/sandbox.md +221 -0
  136. package/kb/site/en/reference/subsystems/schedule.md +189 -0
  137. package/kb/site/en/reference/subsystems/scope.md +62 -0
  138. package/kb/site/en/reference/subsystems/session-projection.md +265 -0
  139. package/kb/site/en/reference/subsystems/session-query.md +498 -0
  140. package/kb/site/en/reference/subsystems/session-reference.md +111 -0
  141. package/kb/site/en/reference/subsystems/session-telemetry.md +197 -0
  142. package/kb/site/en/reference/subsystems/session-title.md +207 -0
  143. package/kb/site/en/reference/subsystems/session.md +852 -0
  144. package/kb/site/en/reference/subsystems/settings.md +313 -0
  145. package/kb/site/en/reference/subsystems/shell.md +306 -0
  146. package/kb/site/en/reference/subsystems/skills.md +334 -0
  147. package/kb/site/en/reference/subsystems/spill.md +120 -0
  148. package/kb/site/en/reference/subsystems/storage.md +232 -0
  149. package/kb/site/en/reference/subsystems/subagent.md +737 -0
  150. package/kb/site/en/reference/subsystems/subprocess.md +327 -0
  151. package/kb/site/en/reference/subsystems/system-prompt.md +210 -0
  152. package/kb/site/en/reference/subsystems/terminal.md +187 -0
  153. package/kb/site/en/reference/subsystems/token-meter.md +93 -0
  154. package/kb/site/en/reference/subsystems/tools.md +723 -0
  155. package/kb/site/en/reference/subsystems/typert.md +339 -0
  156. package/kb/site/en/reference/subsystems/user-questions.md +181 -0
  157. package/kb/site/en/reference/subsystems/web-server.md +111 -0
  158. package/kb/site/en/reference/subsystems/web.md +202 -0
  159. package/kb/site/en/reference/subsystems/workflow.md +281 -0
  160. package/kb/site/en/reference/subsystems/workspace.md +231 -0
  161. package/kb/site/en/reference/tool-catalog.md +1877 -0
  162. package/kb/site/en/reference/tool-execution-pipeline.md +66 -0
  163. package/kb/site/guide/providers-custom-form.zh.png +0 -0
  164. package/kb/site/guide/providers-models-page.zh.png +0 -0
  165. package/kb/site/guide/providers.md +100 -0
  166. package/kb/site/guide/python-sdk.md +106 -0
  167. package/kb/site/guide/quickstart.md +32 -0
  168. package/kb/site/index.md +8 -0
  169. package/kb/site/reference/agent-lifecycle.md +86 -0
  170. package/kb/site/reference/capability-seams.md +475 -0
  171. package/kb/site/reference/config-catalog.md +3154 -0
  172. package/kb/site/reference/cookbook/adding-a-conversation-node.md +235 -0
  173. package/kb/site/reference/cookbook/adding-a-package.md +120 -0
  174. package/kb/site/reference/cookbook/adding-a-settings-card.md +102 -0
  175. package/kb/site/reference/cookbook/adding-a-tool.md +98 -0
  176. package/kb/site/reference/cookbook/adding-an-llm-adapter.md +45 -0
  177. package/kb/site/reference/cookbook/extension-cookbook.md +133 -0
  178. package/kb/site/reference/cordis-api/context.md +368 -0
  179. package/kb/site/reference/cordis-api/events.md +211 -0
  180. package/kb/site/reference/cordis-api/fiber.md +379 -0
  181. package/kb/site/reference/cordis-api/inherited.md +43 -0
  182. package/kb/site/reference/cordis-api/registry.md +156 -0
  183. package/kb/site/reference/cordis-api/service.md +106 -0
  184. package/kb/site/reference/cordis-primer.md +52 -0
  185. package/kb/site/reference/index.md +135 -0
  186. package/kb/site/reference/persistence-catalog.md +949 -0
  187. package/kb/site/reference/subsystems/approval.md +173 -0
  188. package/kb/site/reference/subsystems/client-modules.md +121 -0
  189. package/kb/site/reference/subsystems/code-runtime.md +194 -0
  190. package/kb/site/reference/subsystems/commands.md +190 -0
  191. package/kb/site/reference/subsystems/compaction.md +241 -0
  192. package/kb/site/reference/subsystems/core.md +1081 -0
  193. package/kb/site/reference/subsystems/credentials.md +136 -0
  194. package/kb/site/reference/subsystems/filesystem.md +498 -0
  195. package/kb/site/reference/subsystems/goal.md +280 -0
  196. package/kb/site/reference/subsystems/index.md +58 -0
  197. package/kb/site/reference/subsystems/invariants.md +91 -0
  198. package/kb/site/reference/subsystems/jobs.md +293 -0
  199. package/kb/site/reference/subsystems/llm-streaming.md +926 -0
  200. package/kb/site/reference/subsystems/lsp.md +205 -0
  201. package/kb/site/reference/subsystems/permission-presets.md +134 -0
  202. package/kb/site/reference/subsystems/persistence.md +388 -0
  203. package/kb/site/reference/subsystems/plan.md +90 -0
  204. package/kb/site/reference/subsystems/sandbox.md +221 -0
  205. package/kb/site/reference/subsystems/schedule.md +189 -0
  206. package/kb/site/reference/subsystems/scope.md +62 -0
  207. package/kb/site/reference/subsystems/session-projection.md +265 -0
  208. package/kb/site/reference/subsystems/session-query.md +498 -0
  209. package/kb/site/reference/subsystems/session-reference.md +111 -0
  210. package/kb/site/reference/subsystems/session-telemetry.md +197 -0
  211. package/kb/site/reference/subsystems/session-title.md +207 -0
  212. package/kb/site/reference/subsystems/session.md +854 -0
  213. package/kb/site/reference/subsystems/settings.md +313 -0
  214. package/kb/site/reference/subsystems/shell.md +306 -0
  215. package/kb/site/reference/subsystems/skills.md +334 -0
  216. package/kb/site/reference/subsystems/spill.md +120 -0
  217. package/kb/site/reference/subsystems/storage.md +232 -0
  218. package/kb/site/reference/subsystems/subagent.md +739 -0
  219. package/kb/site/reference/subsystems/subprocess.md +327 -0
  220. package/kb/site/reference/subsystems/system-prompt.md +210 -0
  221. package/kb/site/reference/subsystems/terminal.md +187 -0
  222. package/kb/site/reference/subsystems/token-meter.md +93 -0
  223. package/kb/site/reference/subsystems/tools.md +723 -0
  224. package/kb/site/reference/subsystems/typert.md +339 -0
  225. package/kb/site/reference/subsystems/user-questions.md +181 -0
  226. package/kb/site/reference/subsystems/web-server.md +111 -0
  227. package/kb/site/reference/subsystems/web.md +202 -0
  228. package/kb/site/reference/subsystems/workflow.md +281 -0
  229. package/kb/site/reference/subsystems/workspace.md +231 -0
  230. package/kb/site/reference/tool-catalog.md +1880 -0
  231. package/kb/site/reference/tool-execution-pipeline.md +66 -0
  232. package/package.json +40 -0
  233. package/scripts/rebuild-index.mjs +88 -0
  234. package/skills/dsh-plugin-dev-kb.md +66 -0
@@ -0,0 +1,152 @@
1
+ ---
2
+ editSource: "docs/user/develop/framework/service.zh.md"
3
+ ---
4
+
5
+ # 服务与依赖
6
+
7
+ 服务是一个插件向其他插件公开的能力。inject 声明插件需要哪些服务。
8
+
9
+ ## 什么是服务
10
+
11
+ 在 Harness 中,`tools`、`llm`、`agents` 都是服务。服务是挂载在 `ctx` 上的命名能力:
12
+
13
+ ```ts ignore-check
14
+ ctx.tools // ToolRuntime service
15
+ ctx.llm // LLM service
16
+ ctx.agents // Agent service
17
+ ```
18
+
19
+ 任何插件都可以提供服务,供其他插件使用。
20
+
21
+ ## 使用服务
22
+
23
+ 声明 `inject` 来使用已有服务:
24
+
25
+ ```ts ignore-check
26
+ export const inject = ['tools']
27
+
28
+ export function apply(ctx: Context) {
29
+ // ctx.tools exists and is ready here.
30
+ ctx.tools.register(/* ... */)
31
+ }
32
+ ```
33
+
34
+ 框架保证:在 `apply` 执行时,`inject` 声明的服务已经全部就绪。如果服务还没准备好,你的插件会等着,不会执行。
35
+
36
+ ## 提供服务
37
+
38
+ ### 使用 Service 基类
39
+
40
+ ```ts
41
+ import { Service, type Context } from '@deepseek-ai/cordis'
42
+
43
+ export default class MetricsService extends Service {
44
+ static inject = ['llm'] // A service may depend on other services.
45
+
46
+ constructor(ctx: Context) {
47
+ super(ctx, 'metrics') // 'metrics' is the service name.
48
+ }
49
+
50
+ // Public service method.
51
+ record(event: string, value: number) {
52
+ // ...
53
+ }
54
+ }
55
+ ```
56
+
57
+ 加载这个插件后,消费方就可以通过 `ctx.metrics` 访问它:
58
+
59
+ ```ts ignore-check
60
+ export const inject = ['metrics']
61
+
62
+ export function apply(ctx: Context) {
63
+ ctx.metrics.record('tool_call', 1)
64
+ }
65
+ ```
66
+
67
+ ### 类型声明
68
+
69
+ 使用 TypeScript 声明合并让 `ctx.metrics` 有正确类型:
70
+
71
+ ```ts
72
+ import { Service, type Context } from '@deepseek-ai/cordis'
73
+
74
+ declare module '@deepseek-ai/cordis' {
75
+ interface Context {
76
+ metrics: MetricsService
77
+ }
78
+ }
79
+
80
+ export default class MetricsService extends Service {
81
+ constructor(ctx: Context) {
82
+ super(ctx, 'metrics')
83
+ }
84
+
85
+ record(event: string, value: number) { /* ... */ }
86
+ }
87
+ ```
88
+
89
+ ## 依赖的行为
90
+
91
+ ### 必需依赖与可选依赖
92
+
93
+ ```ts ignore-check
94
+ // Required: the plugin does not load while the service is absent.
95
+ export const inject = ['tools']
96
+
97
+ // Optional: omit inject and query with ctx.get() at the use site.
98
+ export function apply(ctx: Context) {
99
+ const metrics = ctx.get('metrics')
100
+ metrics?.record('plugin_loaded', 1)
101
+ }
102
+ ```
103
+
104
+ ### 服务消失时的行为
105
+
106
+ 如果应用运行期间某项必需服务消失(例如其提供方卸载):
107
+
108
+ 1. 依赖它的插件会自动 dispose(资源释放)
109
+ 2. 当服务重新出现时,插件自动重新加载
110
+
111
+ 这可以防止插件调用已不存在的服务。
112
+
113
+ <a id="service-isolation"></a>
114
+
115
+ ## 服务隔离
116
+
117
+ `cordis.yml` 支持服务隔离——同一个服务可以有多个实例,不同插件组看到不同实例:
118
+
119
+ ```yaml
120
+ - id: group-a
121
+ name: '@deepseek-ai/cordis-plugin-group'
122
+ group: true
123
+ isolate:
124
+ shell: true
125
+ config:
126
+ - name: '@deepseek-ai/dsh-bash-local'
127
+ config:
128
+ timeoutMs: 5000
129
+ - name: './src/plugin-a.ts'
130
+
131
+ - id: group-b
132
+ name: '@deepseek-ai/cordis-plugin-group'
133
+ group: true
134
+ isolate:
135
+ shell: true
136
+ config:
137
+ - name: '@deepseek-ai/dsh-bash-local'
138
+ config:
139
+ timeoutMs: 60000
140
+ - name: './src/plugin-b.ts'
141
+ ```
142
+
143
+ `plugin-a` 和 `plugin-b` 各自看到自己组内的 Bash 实例,互不影响。
144
+
145
+ ## Harness 内置服务
146
+
147
+ 服务名、公开方法和源码位置由仓库自动生成到各服务的[子系统页面](../../reference/subsystems/core.md)。开发插件时应以这些生成区块和服务的 TypeScript 接口为准,不要维护另一份静态清单。
148
+
149
+ ## 下一步
150
+
151
+ - [事件系统](./events.md) — 插件间松耦合通信
152
+ - [能力分层](../practice/index.md) — 将服务用作能力接口
@@ -0,0 +1,157 @@
1
+ ---
2
+ editSource: "docs/user/develop/practice/index.zh.md"
3
+ ---
4
+
5
+ # 能力的三种角色设计
6
+
7
+ 本文分为两部分:先参考三种角色能力模式的概念,再通过高级教程构建一项能力。请先完成[基础插件路径](../basic/index.md)和[服务教程](../framework/service.md)。
8
+
9
+ ## 概念参考
10
+
11
+ 当一项能力足够通用,需要支持可替换的提供方时(例如 Bash 执行),harness 会区分三种角色:**Service Definition**、**Service Provider** 和 **Consumer**。角色需要独立演进或替换时,将它们放入不同包;否则一个包可以承担多个角色。完整能力构成其 seam。任何单一角色都不是 seam。
12
+
13
+ ## 以 Bash 为例
14
+
15
+ 以 Bash 执行能力为例:
16
+
17
+ - **Service Definition** (`dsh-shell`):定义 Cordis 服务以及 Bash 请求和结果类型
18
+ - **Service Provider** (`dsh-bash-local`):在本地计算机上执行命令
19
+ - **Consumer** (`dsh-tool-bash`):将该能力公开为模型可调用的工具
20
+
21
+ ```
22
+ ┌─────────────┐ ┌──────────────────┐ ┌──────────────┐
23
+ │ dsh-shell │────▶│ dsh-bash-local │ │ dsh-tool-bash│
24
+ │(definition) │ │ (provider) │ │(consumer/tool)│
25
+ └─────────────┘ └──────────────────┘ └──────────────┘
26
+ ▲ │
27
+ └────────────────────────────────────────────┘
28
+ inject: ['shell']
29
+ ```
30
+
31
+ ## 拆分的好处
32
+
33
+ ### 提供方可替换
34
+
35
+ 同一个 Service Definition 可以有多个提供方,可通过 `cordis.yml` 选择:
36
+
37
+ ```yaml
38
+ # Local execution
39
+ - name: '@deepseek-ai/dsh-bash-local'
40
+
41
+ # Replace this row with another package that provides the same service.
42
+ ```
43
+
44
+ 更换提供方时,Service Definition 和工具均保持不变。
45
+
46
+ ### 独立演进
47
+
48
+ - 调用方开始依赖 Service Definition 的约定后,Service Definition 很少改动。
49
+ - Service Provider 可以独立优化性能和安全性。
50
+ - Consumer 可以调整能力向模型呈现的方式。
51
+
52
+ ### 依赖解耦
53
+
54
+ - Service Provider 依赖 Service Definition。
55
+ - Consumer 依赖 Service Definition。
56
+ - Service Provider 和 Consumer **互不依赖**。
57
+
58
+ 当前内置系列及其包链接由[能力 seam 参考](../../reference/capability-seams.md)负责。
59
+
60
+ ## 教程:开发三种角色的能力
61
+
62
+ ### 第一步:编写 Service Definition
63
+
64
+ ```ts ignore-check
65
+ // packages/my-cap/my-cap/src/index.ts
66
+ import { Service, type Context } from '@deepseek-ai/cordis'
67
+
68
+ declare module '@deepseek-ai/cordis' {
69
+ interface Context {
70
+ myCap: MyCapService
71
+ }
72
+ }
73
+
74
+ export abstract class MyCapService extends Service {
75
+ constructor(ctx: Context) {
76
+ super(ctx, 'myCap')
77
+ }
78
+
79
+ /** Execute the capability. */
80
+ abstract execute(request: MyCapRequest): Promise<MyCapResult>
81
+ }
82
+
83
+ export interface MyCapRequest {
84
+ input: string
85
+ }
86
+
87
+ export interface MyCapResult {
88
+ output: string
89
+ }
90
+ ```
91
+
92
+ ### 第二步:编写 Service Provider
93
+
94
+ ```ts ignore-check
95
+ // packages/my-cap/my-cap-local/src/index.ts
96
+ import type { Context } from '@deepseek-ai/cordis'
97
+ import { MyCapService, type MyCapRequest, type MyCapResult } from '@deepseek-ai/dsh-my-cap'
98
+
99
+ class MyCapLocal extends MyCapService {
100
+ async execute(request: MyCapRequest): Promise<MyCapResult> {
101
+ // Local provider behavior.
102
+ return { output: request.input.toUpperCase() }
103
+ }
104
+ }
105
+
106
+ export const name = 'my-cap-local'
107
+
108
+ export function apply(ctx: Context) {
109
+ ctx.plugin(MyCapLocal)
110
+ }
111
+ ```
112
+
113
+ ### 第三步:编写消费方
114
+
115
+ ```ts ignore-check
116
+ // packages/my-cap/tool-my-cap/src/index.ts
117
+ import type { Context } from '@deepseek-ai/cordis'
118
+ import { defineTool } from '@deepseek-ai/dsh-tools'
119
+
120
+ export const name = 'tool-my-cap'
121
+ export const inject = ['tools', 'myCap']
122
+
123
+ export function apply(ctx: Context) {
124
+ ctx.tools.register(defineTool({
125
+ name: 'my_cap',
126
+ description: 'Execute my capability.',
127
+ parameters: {
128
+ input: { type: 'string', required: true },
129
+ },
130
+ output: {
131
+ schema: { type: 'string' },
132
+ render: (_args, value) => [{ type: 'text', text: value }],
133
+ },
134
+ async execute(args) {
135
+ const result = await ctx.myCap.execute({ input: args.input })
136
+ return result.output
137
+ },
138
+ }))
139
+ }
140
+ ```
141
+
142
+ ### 在 cordis.yml 中组合
143
+
144
+ ```yaml
145
+ - name: '@deepseek-ai/dsh-my-cap-local'
146
+ - name: '@deepseek-ai/dsh-tool-my-cap'
147
+ ```
148
+
149
+ ## 设计要点
150
+
151
+ - **不要预防性拆分**:只有角色需要独立演进时,才使用不同包。简单的工具插件无需拆分。
152
+ - **Service Definition 拥有 Request/Result 类型**:Service Provider 和 Consumer 只依赖 Service Definition 包。
153
+ - **显式优于隐式**:实现应通过显式的 `resolve(request): Spec` 步骤处理默认值,而不是在 `run()` 中隐藏 `?? default`。
154
+
155
+ ## 下一步
156
+
157
+ - [LLM(大语言模型)适配器](./llm-adapter.md):实现一个 LLM 提供方
@@ -0,0 +1,190 @@
1
+ ---
2
+ editSource: "docs/user/develop/practice/llm-adapter.zh.md"
3
+ ---
4
+
5
+ # LLM 适配器
6
+
7
+ 本文介绍如何为 Harness 接入新的模型提供方。
8
+
9
+ ## 概述
10
+
11
+ LLM 适配器是一个继承 `LlmAdapter` 并实现 `stream()` 方法的类,它会将 Harness 的提供方无关请求转换为具体提供方的 API 调用,并将响应转换回 Harness 分片。
12
+
13
+ ## 最小实现
14
+
15
+ ```ts
16
+ import type { Context } from '@deepseek-ai/cordis'
17
+ import Schema from '@deepseek-ai/schemastery'
18
+ import { LlmAdapter, type GenerateOptions, type StreamChunk } from '@deepseek-ai/dsh-llm'
19
+
20
+ class MyAdapter extends LlmAdapter {
21
+ private apiKey: string
22
+
23
+ constructor(apiKey: string) {
24
+ super()
25
+ this.apiKey = apiKey
26
+ }
27
+
28
+ async *stream(options: GenerateOptions): AsyncIterable<StreamChunk> {
29
+ // 1. Convert options.messages to the provider format.
30
+ // 2. Call the streaming API.
31
+ // 3. Convert the response into StreamChunk values.
32
+ }
33
+ }
34
+
35
+ export interface Config {
36
+ apiKey: string
37
+ providers: string[]
38
+ }
39
+
40
+ export const Config: Schema<Config> = Schema.object({
41
+ apiKey: Schema.string().required(),
42
+ providers: Schema.array(Schema.string()).required(),
43
+ })
44
+
45
+ export const name = 'my-llm-adapter'
46
+ export const inject = ['llm']
47
+
48
+ export function apply(ctx: Context, config: Config) {
49
+ const adapter = new MyAdapter(config.apiKey)
50
+ ctx.llm.registerAdapter(config.providers, adapter)
51
+ }
52
+ ```
53
+
54
+ ## StreamChunk 协议
55
+
56
+ `stream()` 必须按以下协议生成分片:
57
+
58
+ ```ts
59
+ import { CallId, type StreamChunk } from '@deepseek-ai/dsh-llm'
60
+
61
+ async function* exampleChunks(): AsyncIterable<StreamChunk> {
62
+ // 1. Start each content block with block-start.
63
+ yield { type: 'block-start', index: 0, blockType: 'text' }
64
+
65
+ // 2. Stream text through text-delta.
66
+ yield { type: 'text-delta', index: 0, text: 'Hello' }
67
+ yield { type: 'text-delta', index: 0, text: ' world' }
68
+
69
+ // 3. End each content block with block-end and the complete block.
70
+ yield {
71
+ type: 'block-end',
72
+ index: 0,
73
+ block: { type: 'text', text: 'Hello world' },
74
+ }
75
+
76
+ // 4. Tool-call block.
77
+ yield { type: 'block-start', index: 1, blockType: 'tool-call' }
78
+ yield {
79
+ type: 'tool-call-delta',
80
+ index: 1,
81
+ id: CallId('call-123'),
82
+ name: 'bash',
83
+ argumentsDelta: '{"command":"ls"}',
84
+ }
85
+ yield {
86
+ type: 'block-end',
87
+ index: 1,
88
+ block: {
89
+ type: 'tool-call',
90
+ id: CallId('call-123'),
91
+ name: 'bash',
92
+ arguments: '{"command":"ls"}',
93
+ },
94
+ }
95
+
96
+ // 5. Token usage.
97
+ yield { type: 'usage', usage: { inputTokens: 100, outputTokens: 50 } }
98
+
99
+ // 6. Finish reason.
100
+ yield { type: 'finish', reason: { kind: 'stop' } }
101
+ // Alternatively, { kind: 'tool-calls' } requests tool execution.
102
+ }
103
+ ```
104
+
105
+ ### 关键规则
106
+
107
+ - 每个 `block-start` 都必须有与之对应的 `block-end`。
108
+ - `index` 从 0 开始递增,用于标识内容块的顺序。
109
+ - `tool-call-delta` 的 `argumentsDelta` 是原始 JSON 文本的增量,可以在一个分片中完整生成,也可以分多个分片生成。
110
+ - `finish` 必须是最后一个分片。
111
+ - `usage` 必须在 `finish` 之前生成。
112
+
113
+ ## GenerateOptions
114
+
115
+ `stream()` 接收仓库导出的 `GenerateOptions`。它包含模型、适配器拥有的推理强度 ID、对话历史、系统提示词、工具 schema、生成参数、停止序列和中止信号;完整字段以 `@deepseek-ai/dsh-llm` 导出的 TypeScript 类型为准。适配器必须将支持的字段映射到具体 API;如果无法支持某个字段,应抛出带稳定 code 的 `LlmError`,不得静默丢弃。
116
+
117
+ 请覆写 `resolveModel(provider, model, signal?)`,在一次查询中返回确切的提供方/模型身份以及可选的 `context` 和 `reasoning` 元数据。推理元数据包含有序的不透明 ID、展示名称,以及可选的配置默认值;请保留适配器给出的权威可选列表,包括其上游能力 API 返回的 `off`,不要将这些值提升为核心枚举。异步查询必须响应该可选信号,使取消和资源释放过程完全停稳。服务会校验聚合结果,并在调用 `stream()` 前拒绝显式指定但不受支持的推理强度;省略 `reasoning` 表示该模型没有可选的推理强度能力。
118
+
119
+ ## 注册适配器
120
+
121
+ ```ts ignore-check
122
+ ctx.llm.registerAdapter(['my-provider'], adapter)
123
+ ```
124
+
125
+ 第一个参数是该适配器处理的提供方路由列表。`GenerateOptions.provider` 选择已注册的适配器,`GenerateOptions.model` 则传入由适配器拥有、无需在生命周期启动时注册的模型 id。适配器能够向选择器公布模型选项时,请覆写 `listModels()`。
126
+
127
+ ## 在 cordis.yml 中使用
128
+
129
+ ```yaml
130
+ - id: my-llm
131
+ name: './src/my-llm-adapter.ts'
132
+ config:
133
+ apiKey: !!js process.env.MY_API_KEY
134
+ providers:
135
+ - my-provider
136
+
137
+ - id: agent-loop
138
+ name: '@deepseek-ai/dsh-agent-loop'
139
+ config:
140
+ agents:
141
+ - id: main
142
+ provider: my-provider
143
+ model: my-model-v1
144
+ ```
145
+
146
+ ## 实战参考
147
+
148
+ 仓库中包含以下两个完整实现:
149
+
150
+ - `packages/llm/llm-deepseek/` — DeepSeek API 适配器(OpenAI 兼容格式)
151
+ - `packages/llm/llm-pi-ai/` — Pi AI 适配器(不同的 API 格式)
152
+
153
+ 对比这两个已交付的适配器,可以看到同一套 harness 契约如何在不同提供方 SDK 之上实现。
154
+
155
+ ## 错误处理
156
+
157
+ 适配器应通过带稳定 code 的 `LlmError` 抛出传输和协议故障;agent loop(智能体循环)会保留该错误及其 code,用于诊断和策略处理。不要依赖普通 `Error` 被自动转换。每个提供方 HTTP 请求还必须合并 `attributionHeaders()`,并传递 `options.signal`。
158
+
159
+ ```ts
160
+ import {
161
+ attributionHeaders,
162
+ LlmAdapter,
163
+ LlmError,
164
+ type GenerateOptions,
165
+ type StreamChunk,
166
+ } from '@deepseek-ai/dsh-llm'
167
+
168
+ class HttpAdapter extends LlmAdapter {
169
+ constructor(private readonly endpoint: string) {
170
+ super()
171
+ }
172
+
173
+ async *stream(options: GenerateOptions): AsyncIterable<StreamChunk> {
174
+ const response = await fetch(this.endpoint, {
175
+ method: 'POST',
176
+ headers: {
177
+ 'content-type': 'application/json',
178
+ ...attributionHeaders(),
179
+ },
180
+ body: JSON.stringify({ model: options.model, messages: options.messages }),
181
+ ...options.signal ? { signal: options.signal } : {},
182
+ })
183
+ if (!response.ok) {
184
+ throw new LlmError(`Provider API error: ${response.status}`, 'PROVIDER_HTTP_ERROR')
185
+ }
186
+ // A real adapter parses the response and emits the complete chunk sequence.
187
+ yield { type: 'finish', reason: { kind: 'stop' } }
188
+ }
189
+ }
190
+ ```
@@ -0,0 +1,108 @@
1
+ ---
2
+ editSource: "docs/user/develop/basic/config.md"
3
+ ---
4
+
5
+ # Plugin configuration
6
+
7
+ Accept configuration supplied through `cordis.yml`.
8
+
9
+ ## Define the Config type
10
+
11
+ Export a `Config` type and a same-named Schemastery schema. Put defaults directly on the schema fields:
12
+
13
+ ```ts
14
+ import type { Context } from '@deepseek-ai/cordis'
15
+ import Schema from '@deepseek-ai/schemastery'
16
+
17
+ export const name = 'my-plugin'
18
+
19
+ export interface Config {
20
+ greeting: string
21
+ maxRetries: number
22
+ verbose?: boolean
23
+ }
24
+
25
+ export const Config: Schema<Config> = Schema.object({
26
+ greeting: Schema.string().default('Hello'),
27
+ maxRetries: Schema.number().default(3),
28
+ verbose: Schema.boolean().default(false),
29
+ })
30
+
31
+ export function apply(ctx: Context, config: Config) {
32
+ console.log(config.greeting) // User value or schema default.
33
+ }
34
+ ```
35
+
36
+ Add the configuration to the inserted local plugin row in `scratch-plugin/cordis.yml`:
37
+
38
+ ```yaml
39
+ - insert:
40
+ - id: hello
41
+ name: './src/my-plugin.ts'
42
+ config:
43
+ greeting: 'Hi there'
44
+ maxRetries: 5
45
+ ```
46
+
47
+ When loading the plugin, Cordis uses the exported schema to validate configuration and fill defaults. Do not export a plain object as `Config`; it does not implement the Standard Schema interface required by Cordis.
48
+
49
+ ## Schema validation
50
+
51
+ Use Schemastery to express stricter validation:
52
+
53
+ ```ts
54
+ import type { Context } from '@deepseek-ai/cordis'
55
+ import Schema from '@deepseek-ai/schemastery'
56
+
57
+ export const name = 'validated-plugin'
58
+
59
+ export interface Config {
60
+ apiKey: string
61
+ timeout: number
62
+ mode: 'fast' | 'accurate'
63
+ }
64
+
65
+ export const Config = Schema.object({
66
+ apiKey: Schema.string().required(),
67
+ timeout: Schema.number().default(30000),
68
+ mode: Schema.union(['fast', 'accurate']).default('fast'),
69
+ })
70
+
71
+ export function apply(ctx: Context, config: Config) {
72
+ // config is validated and type-safe.
73
+ }
74
+ ```
75
+
76
+ The schema runs while the plugin loads. Invalid configuration fails the load with an actionable error.
77
+
78
+ ## Design principles
79
+
80
+ ### Do not hardcode tunable values
81
+
82
+ Harness requires **anything that two deployments may want to set differently to be a configuration field**.
83
+
84
+ ```ts
85
+ // Wrong: hardcoded timeout.
86
+ const TIMEOUT = 30000
87
+
88
+ // Correct: configurable.
89
+ export interface Config {
90
+ timeoutMs: number // Defaults to 30000.
91
+ }
92
+ ```
93
+
94
+ The test is whether `cordis.yml` can change the value without a code edit.
95
+
96
+ ### Fail loudly on invalid configuration
97
+
98
+ Express self-contained constraints in the schema so invalid configuration fails while the plugin loads. References to services or registered resources require dependency injection; the [services tutorial](../framework/service.md) introduces that contract.
99
+
100
+ ## Work with HMR
101
+
102
+ A configuration edit hot-replaces the plugin: the framework unloads the old instance and loads a new one. Because registrations are effects and clean themselves up, replacement does not retain the old instance's registrations.
103
+
104
+ ## Next steps
105
+
106
+ - [Package and install a plugin](./publish.md) — ship the plugin as an installable package
107
+ - [Plugins and lifecycle](../framework/index.md) — understand the full plugin lifecycle
108
+ - [Services and dependencies](../framework/service.md) — provide a service to other plugins