@carljia/omd-dsh 0.1.3 → 0.1.5
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.
- package/README.md +12 -2
- package/lib/index.d.ts +22 -3
- package/lib/index.js +129 -28
- package/lib/plan.d.ts +14 -8
- package/lib/plan.js +61 -8
- package/lib/task.js +8 -1
- package/lib/vendor/omd-mode.mjs +129 -28
- package/lib/vendor/omd-plan.mjs +61 -8
- package/lib/vendor/omd-task.mjs +8 -1
- package/package.json +1 -1
- package/presets/omd-chat/agent.cordis.yml +1 -1
- package/presets/omd-executor/agent.cordis.yml +34 -1
- package/presets/omd-explorer/agent.cordis.yml +1 -1
- package/presets/omd-librarian/agent.cordis.yml +1 -1
- package/presets/omd-planner/agent.cordis.yml +1 -1
- package/presets/omd-reviewer/agent.cordis.yml +1 -1
- package/presets/omd-ultraworker/agent.cordis.yml +1 -1
package/README.md
CHANGED
|
@@ -15,7 +15,14 @@ OMD 理念的 DSH 插件:两个 cordis 行 + 7 个模式 preset + 同步 CLI
|
|
|
15
15
|
|
|
16
16
|
机制:监听 system-prompt/assemble(注入 {{provider}}/{{model}} 提示词变量)与 agent/request(next() 后覆盖路由),两处均 prepend,保证覆盖入口的会话级选择。
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
**与 UI 模型切换的优先级**:矩阵模型是模式的默认路由,但用户在 UI 显式切换模型后,本次任务的顶层路由(与 persona 展示)跟随用户选择。判定规则(依据请求瀑布流解析出的入口选择):
|
|
19
|
+
|
|
20
|
+
1. 入口选择 == 矩阵模型 → 钉住矩阵模型(无操作);
|
|
21
|
+
2. 会话尚无任何请求(blank)且入口选择 == 挂载时的部署默认模型 → 视为未选择,钉住矩阵模型;
|
|
22
|
+
3. 会话已跑过请求且刚发生 preset 切换(/mode 或 UI 选择,日志中 agent-preset/selected 在最后一次 request/header 之后)且入口选择 == 切换前的路由 → 新模式认领矩阵模型;
|
|
23
|
+
4. 其余情况 → 入口选择与矩阵模型不同 = 用户显式切换:请求与 persona 变量均保持用户选择,本行在作用域 ctx 上记录 `omdModeOverride`(provider/model),omd-task 行据此把 deep tier 路由到用户选择的模型。
|
|
24
|
+
|
|
25
|
+
模型体验:persona 文本每次请求固定携带实际路由模型的声明;模式模型路由在 agent 生命周期内稳定(KV 前缀稳定)。
|
|
19
26
|
|
|
20
27
|
## 行:omd-task
|
|
21
28
|
|
|
@@ -32,6 +39,8 @@ tier 差异化子代理委派(等价 OMD 的 task(category=…))。仅限 pr
|
|
|
32
39
|
|
|
33
40
|
模型可见参数:description(展示名)、prompt、tier(枚举见工具描述)、run_in_background(仅 continuable 模式)。工具描述枚举各 tier 的 hint 与模型,模型据此选型。
|
|
34
41
|
|
|
42
|
+
**deep tier 与用户模型覆盖**:用户在 UI 显式切换模型后(omd-mode 让路并记录 `ctx.omdModeOverride`),名为 `deep` 的 tier 改用用户选择的模型(provider/model 整体替换),其余 tier 保持矩阵配置。
|
|
43
|
+
|
|
35
44
|
## CLI:omd-dsh
|
|
36
45
|
|
|
37
46
|
```
|
|
@@ -62,7 +71,8 @@ npm pack # prepack 自动重建,产出 tgz
|
|
|
62
71
|
### 子代理透传语义
|
|
63
72
|
|
|
64
73
|
omd-mode 对 subagentDepth > 0 的子代理一律透传(不覆盖 provider/model/变量):
|
|
65
|
-
omd-task 的 tier 模型通过显式 agentOptions 生效;顶层 agent
|
|
74
|
+
omd-task 的 tier 模型通过显式 agentOptions 生效;顶层 agent 才被钉到模式模型
|
|
75
|
+
(除非用户显式切换模型——此时顶层跟随用户选择,deep tier 通过 `ctx.omdModeOverride` 同步)。
|
|
66
76
|
这保证「强模型顶层 + tier 差异化子代理」的优先级正确。
|
|
67
77
|
|
|
68
78
|
### toolFilter 注意
|
package/lib/index.d.ts
CHANGED
|
@@ -9,9 +9,28 @@
|
|
|
9
9
|
* model and provider template variables), and overrides provider/model
|
|
10
10
|
* on the agent/request waterfall after next(), dropping any inherited
|
|
11
11
|
* reasoningEffort. Both listeners register with prepend: true so this
|
|
12
|
-
* row sits OUTSIDE the entry point per-session selection listener
|
|
13
|
-
*
|
|
14
|
-
*
|
|
12
|
+
* row sits OUTSIDE the entry point per-session selection listener.
|
|
13
|
+
*
|
|
14
|
+
* Precedence vs. the UI model switch: the matrix model is the preset's
|
|
15
|
+
* DEFAULT route, but an explicit user selection wins for the task.
|
|
16
|
+
* The entry selection (installModelSelection) is invisible to this row
|
|
17
|
+
* (it is owned by the host entry point), so the decision is derived
|
|
18
|
+
* from what the waterfall actually resolved:
|
|
19
|
+
*
|
|
20
|
+
* - entry selection == matrix model -> pin (no-op);
|
|
21
|
+
* - entry selection missing -> pin (claim the mode);
|
|
22
|
+
* - session still blank (no request/header)
|
|
23
|
+
* and entry selection == the deployment
|
|
24
|
+
* default captured at mount -> pin (fallback, no pick);
|
|
25
|
+
* - a preset switch (agent-preset/selected)
|
|
26
|
+
* happened after the last request/header and
|
|
27
|
+
* entry selection == the route the session
|
|
28
|
+
* was running before the switch -> pin (new mode claims);
|
|
29
|
+
* - otherwise the user explicitly picked a
|
|
30
|
+
* different model -> yield: the request and
|
|
31
|
+
* the persona variables keep the user's selection, and the row
|
|
32
|
+
* records it on the scoped context as `omdModeOverride` so the
|
|
33
|
+
* omd-task row can route the "deep" tier to the user's model.
|
|
15
34
|
*
|
|
16
35
|
* When provider/model are not configured the row passes everything
|
|
17
36
|
* through and only serves the persona banner variables (inheriting the
|
package/lib/index.js
CHANGED
|
@@ -11,9 +11,28 @@ import { scopeOf } from "@deepseek-ai/dsh-scope";
|
|
|
11
11
|
* model and provider template variables), and overrides provider/model
|
|
12
12
|
* on the agent/request waterfall after next(), dropping any inherited
|
|
13
13
|
* reasoningEffort. Both listeners register with prepend: true so this
|
|
14
|
-
* row sits OUTSIDE the entry point per-session selection listener
|
|
15
|
-
*
|
|
16
|
-
*
|
|
14
|
+
* row sits OUTSIDE the entry point per-session selection listener.
|
|
15
|
+
*
|
|
16
|
+
* Precedence vs. the UI model switch: the matrix model is the preset's
|
|
17
|
+
* DEFAULT route, but an explicit user selection wins for the task.
|
|
18
|
+
* The entry selection (installModelSelection) is invisible to this row
|
|
19
|
+
* (it is owned by the host entry point), so the decision is derived
|
|
20
|
+
* from what the waterfall actually resolved:
|
|
21
|
+
*
|
|
22
|
+
* - entry selection == matrix model -> pin (no-op);
|
|
23
|
+
* - entry selection missing -> pin (claim the mode);
|
|
24
|
+
* - session still blank (no request/header)
|
|
25
|
+
* and entry selection == the deployment
|
|
26
|
+
* default captured at mount -> pin (fallback, no pick);
|
|
27
|
+
* - a preset switch (agent-preset/selected)
|
|
28
|
+
* happened after the last request/header and
|
|
29
|
+
* entry selection == the route the session
|
|
30
|
+
* was running before the switch -> pin (new mode claims);
|
|
31
|
+
* - otherwise the user explicitly picked a
|
|
32
|
+
* different model -> yield: the request and
|
|
33
|
+
* the persona variables keep the user's selection, and the row
|
|
34
|
+
* records it on the scoped context as `omdModeOverride` so the
|
|
35
|
+
* omd-task row can route the "deep" tier to the user's model.
|
|
17
36
|
*
|
|
18
37
|
* When provider/model are not configured the row passes everything
|
|
19
38
|
* through and only serves the persona banner variables (inheriting the
|
|
@@ -30,6 +49,36 @@ const Config = z.object({
|
|
|
30
49
|
model: z.string(),
|
|
31
50
|
reasoningEffort: z.string(),
|
|
32
51
|
});
|
|
52
|
+
/**
|
|
53
|
+
* 子代理(subagentDepth > 0)透传:omd-task 的 tier 模型通过显式 agentOptions
|
|
54
|
+
* 落到子代理的 AgentOptions 上,本行若再覆盖会压回模式模型、破坏差异化委派。
|
|
55
|
+
* 无显式 agentOptions 的子代理按 DSH 原生语义继承父级入口选择。
|
|
56
|
+
*/
|
|
57
|
+
function isSubagent(agent) {
|
|
58
|
+
return agent !== undefined && agent !== null && agent.options !== undefined && agent.options !== null
|
|
59
|
+
&& typeof agent.options.subagentDepth === "number" && agent.options.subagentDepth > 0;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* 最近一次 request/header 之后是否发生过 agent-preset/selected(UI 预设选择或 /mode 切换)。
|
|
63
|
+
* 切换事件由 api-proxy 与 omd-mode-switch 在 recompose 完成之后追加,因此必须每次实时计算——
|
|
64
|
+
* 本行挂载时事件尚未入日志,挂载时快照会漏判。
|
|
65
|
+
*/
|
|
66
|
+
function presetSwitchedAfterLastRequest(session) {
|
|
67
|
+
const events = session === undefined || session === null ? undefined : session.events;
|
|
68
|
+
if (events === undefined)
|
|
69
|
+
return false;
|
|
70
|
+
let lastHeader = -1;
|
|
71
|
+
let lastSwitch = -1;
|
|
72
|
+
for (const event of events) {
|
|
73
|
+
if (event === undefined || event === null || typeof event.seq !== "number")
|
|
74
|
+
continue;
|
|
75
|
+
if (event.type === "request/header")
|
|
76
|
+
lastHeader = event.seq;
|
|
77
|
+
else if (event.type === "agent-preset/selected")
|
|
78
|
+
lastSwitch = event.seq;
|
|
79
|
+
}
|
|
80
|
+
return lastSwitch > lastHeader;
|
|
81
|
+
}
|
|
33
82
|
function apply(ctx, config) {
|
|
34
83
|
if (scopeOf(ctx) === undefined) {
|
|
35
84
|
throw new Error("omd-mode: refusing to mount outside a scoped context (mode '" + config.mode + "'). " +
|
|
@@ -41,42 +90,94 @@ function apply(ctx, config) {
|
|
|
41
90
|
model: config.model,
|
|
42
91
|
}
|
|
43
92
|
: undefined;
|
|
44
|
-
// 子代理(subagentDepth > 0)透传:omd-task 的 tier 模型通过显式 agentOptions
|
|
45
|
-
// 落到子代理的 AgentOptions 上,本行若再覆盖会压回模式模型、破坏差异化委派。
|
|
46
|
-
// 无显式 agentOptions 的子代理按 DSH 原生语义继承父级入口选择。
|
|
47
|
-
const isSubagent = (agent) => agent !== undefined && agent !== null && agent.options !== undefined && agent.options !== null
|
|
48
|
-
&& typeof agent.options.subagentDepth === "number" && agent.options.subagentDepth > 0;
|
|
49
93
|
if (config.reasoningEffort !== undefined && pinned !== undefined) {
|
|
50
94
|
pinned.reasoningEffort = config.reasoningEffort;
|
|
51
95
|
}
|
|
96
|
+
// 挂载时快照部署默认模型(d0)。blank 会话的入口选择 == d0 视为「未显式选择」。
|
|
97
|
+
// 必须静态快照:session.selectModel 每次都会把用户选择写回全局默认,动态读取会把
|
|
98
|
+
// 用户选择误判为默认值。
|
|
99
|
+
let d0;
|
|
100
|
+
try {
|
|
101
|
+
const def = ctx.get("agentDefaultModel");
|
|
102
|
+
const current = def !== undefined && def !== null ? def.currentSelection() : undefined;
|
|
103
|
+
if (current !== undefined && current !== null && typeof current.provider === "string" && typeof current.model === "string") {
|
|
104
|
+
d0 = { provider: current.provider, model: current.model };
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
catch { /* 读不到默认时退化为仅 entry == pinned 判定 */ }
|
|
108
|
+
// 供同 preset 内的 omd-task 行读取:用户显式切换模型后(本行让路),deep tier 沿用用户选择。
|
|
109
|
+
ctx.omdModeOverride = undefined;
|
|
110
|
+
/**
|
|
111
|
+
* 判定一次入口选择是否应钉到模式模型(true),还是让路给用户选择(false)。
|
|
112
|
+
* @param agent - 顶层 agent(子代理已由调用方过滤)。
|
|
113
|
+
* @param entry - 入口选择 { provider, model };provider/model 缺失 = 无入口选择。
|
|
114
|
+
*/
|
|
115
|
+
function shouldPin(agent, entry) {
|
|
116
|
+
if (pinned === undefined)
|
|
117
|
+
return false;
|
|
118
|
+
if (entry === undefined || entry.provider === undefined || entry.model === undefined)
|
|
119
|
+
return true;
|
|
120
|
+
if (entry.provider === pinned.provider && entry.model === pinned.model)
|
|
121
|
+
return true;
|
|
122
|
+
const session = agent !== undefined && agent !== null ? agent.session : undefined;
|
|
123
|
+
const logged = session === undefined ? undefined : session.requestHeader();
|
|
124
|
+
if (logged === undefined) {
|
|
125
|
+
// 会话尚无任何请求:入口选择要么是部署默认(未选择),要么是首请求前的显式选择。
|
|
126
|
+
// 只有默认值视为「未选择」;其余一律视为用户选择。
|
|
127
|
+
if (d0 !== undefined && entry.provider === d0.provider && entry.model === d0.model)
|
|
128
|
+
return true;
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
// 会话已跑过请求:刚切换 preset(/mode 或 UI 选择)时,切换前的路由(logged)是
|
|
132
|
+
// 新模式认领矩阵模型的基线;否则入口选择与模式模型不同 = 用户显式切换,让路。
|
|
133
|
+
if (presetSwitchedAfterLastRequest(session)
|
|
134
|
+
&& logged.config !== undefined && logged.config !== null
|
|
135
|
+
&& entry.provider === logged.config.provider && entry.model === logged.config.model) {
|
|
136
|
+
return true;
|
|
137
|
+
}
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
52
140
|
ctx.on("system-prompt/assemble", async (assembly, _context, next) => {
|
|
53
141
|
const assembled = await next();
|
|
54
|
-
|
|
142
|
+
const agent = _context && _context.agent;
|
|
143
|
+
if (pinned === undefined || isSubagent(agent))
|
|
55
144
|
return assembled;
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
...assembled
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
145
|
+
const variables = assembled.variables ?? {};
|
|
146
|
+
if (shouldPin(agent, { provider: variables.provider, model: variables.model })) {
|
|
147
|
+
return {
|
|
148
|
+
...assembled,
|
|
149
|
+
variables: {
|
|
150
|
+
...variables,
|
|
151
|
+
provider: pinned.provider,
|
|
152
|
+
model: pinned.model,
|
|
153
|
+
},
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
// 让路:保留入口(用户)选择注入的变量,persona 展示实际路由的模型。
|
|
157
|
+
return assembled;
|
|
64
158
|
}, { prepend: true });
|
|
65
159
|
ctx.on("agent/request", async (_payload, next) => {
|
|
66
160
|
const resolved = await next();
|
|
67
|
-
|
|
161
|
+
const agent = _payload && _payload.agent;
|
|
162
|
+
if (pinned === undefined || isSubagent(agent))
|
|
68
163
|
return resolved;
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
164
|
+
if (shouldPin(agent, { provider: resolved.provider, model: resolved.model })) {
|
|
165
|
+
ctx.omdModeOverride = undefined;
|
|
166
|
+
const stripped = { ...resolved };
|
|
167
|
+
delete stripped.reasoningEffort;
|
|
168
|
+
const out = {
|
|
169
|
+
...stripped,
|
|
170
|
+
provider: pinned.provider,
|
|
171
|
+
model: pinned.model,
|
|
172
|
+
};
|
|
173
|
+
if (pinned.reasoningEffort !== undefined) {
|
|
174
|
+
out.reasoningEffort = pinned.reasoningEffort;
|
|
175
|
+
}
|
|
176
|
+
return out;
|
|
78
177
|
}
|
|
79
|
-
|
|
178
|
+
// 用户显式选择了别的模型:本次任务顶层路由用用户选择;deep tier 同步(omd-task 读取)。
|
|
179
|
+
ctx.omdModeOverride = { provider: resolved.provider, model: resolved.model };
|
|
180
|
+
return resolved;
|
|
80
181
|
}, { prepend: true });
|
|
81
182
|
}
|
|
82
183
|
export { Config, apply, inject, name };
|
package/lib/plan.d.ts
CHANGED
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @module @carljia/omd-dsh/plan
|
|
3
3
|
*
|
|
4
|
-
* omd-plan: plan persistence
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
4
|
+
* omd-plan: plan persistence + plan-mode activation + write scope for the OMD
|
|
5
|
+
* planner mode. Three jobs, all scoped to the planner preset:
|
|
6
|
+
* 1. auto-activate plan mode for the top-level agent, so the plan:policy
|
|
7
|
+
* section renders and exit_plan_mode works (DSH leaves plan state
|
|
8
|
+
* inactive until /plan or a programmatic set);
|
|
9
|
+
* 2. enforce a .md-only write guard so the planner stays read-only except
|
|
10
|
+
* for markdown files;
|
|
11
|
+
* 3. wrap `tools/post-execute` and intercept a successful `exit_plan_mode`
|
|
12
|
+
* approval: the approved plan text is written into the workspace's plan
|
|
13
|
+
* directory (a fixed, code-level convention -- never mentioned in any
|
|
14
|
+
* persona/prompt text), and the tool result content is enriched with the
|
|
15
|
+
* saved file name so the planner's fixed Start Work final step can hand
|
|
16
|
+
* it to the user.
|
|
11
17
|
*
|
|
12
18
|
* Plan directory convention (hardcoded here and in omd-start-work only):
|
|
13
19
|
* <session cwd>/.omd/plans/<slug>-<timestamp>.md
|
|
@@ -16,7 +22,7 @@
|
|
|
16
22
|
*/
|
|
17
23
|
/** Cordis plugin name. */
|
|
18
24
|
declare const name = "omd-plan";
|
|
19
|
-
/** No service injection: this row only registers
|
|
25
|
+
/** No service injection: this row only registers scoped event listeners. */
|
|
20
26
|
declare const inject: never[];
|
|
21
27
|
declare function apply(ctx: any): void;
|
|
22
28
|
export { apply, inject, name };
|
package/lib/plan.js
CHANGED
|
@@ -4,13 +4,19 @@ import { scopeOf } from "@deepseek-ai/dsh-scope";
|
|
|
4
4
|
/**
|
|
5
5
|
* @module @carljia/omd-dsh/plan
|
|
6
6
|
*
|
|
7
|
-
* omd-plan: plan persistence
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
7
|
+
* omd-plan: plan persistence + plan-mode activation + write scope for the OMD
|
|
8
|
+
* planner mode. Three jobs, all scoped to the planner preset:
|
|
9
|
+
* 1. auto-activate plan mode for the top-level agent, so the plan:policy
|
|
10
|
+
* section renders and exit_plan_mode works (DSH leaves plan state
|
|
11
|
+
* inactive until /plan or a programmatic set);
|
|
12
|
+
* 2. enforce a .md-only write guard so the planner stays read-only except
|
|
13
|
+
* for markdown files;
|
|
14
|
+
* 3. wrap `tools/post-execute` and intercept a successful `exit_plan_mode`
|
|
15
|
+
* approval: the approved plan text is written into the workspace's plan
|
|
16
|
+
* directory (a fixed, code-level convention -- never mentioned in any
|
|
17
|
+
* persona/prompt text), and the tool result content is enriched with the
|
|
18
|
+
* saved file name so the planner's fixed Start Work final step can hand
|
|
19
|
+
* it to the user.
|
|
14
20
|
*
|
|
15
21
|
* Plan directory convention (hardcoded here and in omd-start-work only):
|
|
16
22
|
* <session cwd>/.omd/plans/<slug>-<timestamp>.md
|
|
@@ -19,7 +25,7 @@ import { scopeOf } from "@deepseek-ai/dsh-scope";
|
|
|
19
25
|
*/
|
|
20
26
|
/** Cordis plugin name. */
|
|
21
27
|
const name = "omd-plan";
|
|
22
|
-
/** No service injection: this row only registers
|
|
28
|
+
/** No service injection: this row only registers scoped event listeners. */
|
|
23
29
|
const inject = [];
|
|
24
30
|
/** Plan directory segments relative to the session workspace root (cwd). */
|
|
25
31
|
const PLAN_DIR_SEGMENTS = [".omd", "plans"];
|
|
@@ -79,10 +85,57 @@ function isSubagent(agent) {
|
|
|
79
85
|
typeof agent.options.subagentDepth === "number" &&
|
|
80
86
|
agent.options.subagentDepth > 0);
|
|
81
87
|
}
|
|
88
|
+
/** Fold the session's plan/mode events (last one wins); mirror mode.ts. */
|
|
89
|
+
function planModeActive(events) {
|
|
90
|
+
let active = false;
|
|
91
|
+
for (const event of events ?? []) {
|
|
92
|
+
if (event !== undefined && event.type === "plan/mode") {
|
|
93
|
+
active = event.data !== undefined && event.data !== null && event.data.active === true;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return active;
|
|
97
|
+
}
|
|
82
98
|
function apply(ctx) {
|
|
83
99
|
if (scopeOf(ctx) === undefined) {
|
|
84
100
|
throw new Error("omd-plan: refusing to mount outside a scoped context; mount this row inside an agent preset");
|
|
85
101
|
}
|
|
102
|
+
// Scoped .md-only write guard: the planner may write/edit only markdown
|
|
103
|
+
// files, keeping it read-only for every other path. The plan file itself is
|
|
104
|
+
// written by this row via node:fs (not through the model's write/edit tools),
|
|
105
|
+
// so plan persistence is unaffected by the guard. Implemented on the
|
|
106
|
+
// tools/pre-execute gate (the row's existing ctx.on style) so no service
|
|
107
|
+
// injection is required.
|
|
108
|
+
ctx.on("tools/pre-execute", async (exec, next) => {
|
|
109
|
+
const toolName = exec !== undefined && exec !== null ? exec.name : undefined;
|
|
110
|
+
if (toolName !== "write" && toolName !== "edit")
|
|
111
|
+
return await next();
|
|
112
|
+
const filePath = exec.arguments !== undefined &&
|
|
113
|
+
exec.arguments !== null &&
|
|
114
|
+
typeof exec.arguments.file_path === "string"
|
|
115
|
+
? exec.arguments.file_path
|
|
116
|
+
: undefined;
|
|
117
|
+
if (filePath !== undefined && filePath.toLowerCase().endsWith(".md"))
|
|
118
|
+
return await next();
|
|
119
|
+
return {
|
|
120
|
+
kind: "deny",
|
|
121
|
+
reason: "omd-plan: the planner preset may write or edit only .md files (refusing " +
|
|
122
|
+
toolName +
|
|
123
|
+
" on a non-.md path)",
|
|
124
|
+
};
|
|
125
|
+
});
|
|
126
|
+
// Auto-activate plan mode for the top-level planner agent. DSH leaves plan
|
|
127
|
+
// state inactive until /plan or a programmatic set, so without this the
|
|
128
|
+
// plan:policy section never renders and exit_plan_mode fails with "only
|
|
129
|
+
// available in plan mode". Mirror mode.ts's direct log append (no narration)
|
|
130
|
+
// so the planner session is in plan mode from its first request onward.
|
|
131
|
+
ctx.on("agent/pre-step", async ({ agent }, next) => {
|
|
132
|
+
if (agent !== undefined && agent !== null && !isSubagent(agent) && agent.session !== undefined && agent.session !== null) {
|
|
133
|
+
if (!planModeActive(agent.session.events)) {
|
|
134
|
+
agent.session.append("plan/mode", { active: true });
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return await next();
|
|
138
|
+
});
|
|
86
139
|
ctx.on("tools/post-execute", async (exec, result, next) => {
|
|
87
140
|
const decision = await next();
|
|
88
141
|
if (decision.kind !== "accept" || decision.value !== undefined)
|
package/lib/task.js
CHANGED
|
@@ -211,7 +211,14 @@ function apply(ctx, config) {
|
|
|
211
211
|
if (!parent)
|
|
212
212
|
throw new Error("omd_task requires a calling agent (exec.agent was undefined)");
|
|
213
213
|
const tierName = resolveTier(config, args.tier);
|
|
214
|
-
|
|
214
|
+
let tier = config.tiers[tierName];
|
|
215
|
+
// 用户显式切换模型后(omd-mode 在 agent/request 让路并在作用域 ctx 上记录
|
|
216
|
+
// omdModeOverride),deep tier 改用用户选择的模型;其余 tier 保持矩阵配置。
|
|
217
|
+
const override = ctx.omdModeOverride;
|
|
218
|
+
if (tierName === "deep" && override !== undefined
|
|
219
|
+
&& typeof override.provider === "string" && typeof override.model === "string") {
|
|
220
|
+
tier = { ...tier, provider: override.provider, model: override.model };
|
|
221
|
+
}
|
|
215
222
|
const maxDepth = typeof config.maxDepth === "number" ? config.maxDepth : undefined;
|
|
216
223
|
const request = {
|
|
217
224
|
label: args.description,
|
package/lib/vendor/omd-mode.mjs
CHANGED
|
@@ -11,9 +11,28 @@ import { scopeOf } from "@deepseek-ai/dsh-scope";
|
|
|
11
11
|
* model and provider template variables), and overrides provider/model
|
|
12
12
|
* on the agent/request waterfall after next(), dropping any inherited
|
|
13
13
|
* reasoningEffort. Both listeners register with prepend: true so this
|
|
14
|
-
* row sits OUTSIDE the entry point per-session selection listener
|
|
15
|
-
*
|
|
16
|
-
*
|
|
14
|
+
* row sits OUTSIDE the entry point per-session selection listener.
|
|
15
|
+
*
|
|
16
|
+
* Precedence vs. the UI model switch: the matrix model is the preset's
|
|
17
|
+
* DEFAULT route, but an explicit user selection wins for the task.
|
|
18
|
+
* The entry selection (installModelSelection) is invisible to this row
|
|
19
|
+
* (it is owned by the host entry point), so the decision is derived
|
|
20
|
+
* from what the waterfall actually resolved:
|
|
21
|
+
*
|
|
22
|
+
* - entry selection == matrix model -> pin (no-op);
|
|
23
|
+
* - entry selection missing -> pin (claim the mode);
|
|
24
|
+
* - session still blank (no request/header)
|
|
25
|
+
* and entry selection == the deployment
|
|
26
|
+
* default captured at mount -> pin (fallback, no pick);
|
|
27
|
+
* - a preset switch (agent-preset/selected)
|
|
28
|
+
* happened after the last request/header and
|
|
29
|
+
* entry selection == the route the session
|
|
30
|
+
* was running before the switch -> pin (new mode claims);
|
|
31
|
+
* - otherwise the user explicitly picked a
|
|
32
|
+
* different model -> yield: the request and
|
|
33
|
+
* the persona variables keep the user's selection, and the row
|
|
34
|
+
* records it on the scoped context as `omdModeOverride` so the
|
|
35
|
+
* omd-task row can route the "deep" tier to the user's model.
|
|
17
36
|
*
|
|
18
37
|
* When provider/model are not configured the row passes everything
|
|
19
38
|
* through and only serves the persona banner variables (inheriting the
|
|
@@ -30,6 +49,36 @@ const Config = z.object({
|
|
|
30
49
|
model: z.string(),
|
|
31
50
|
reasoningEffort: z.string(),
|
|
32
51
|
});
|
|
52
|
+
/**
|
|
53
|
+
* 子代理(subagentDepth > 0)透传:omd-task 的 tier 模型通过显式 agentOptions
|
|
54
|
+
* 落到子代理的 AgentOptions 上,本行若再覆盖会压回模式模型、破坏差异化委派。
|
|
55
|
+
* 无显式 agentOptions 的子代理按 DSH 原生语义继承父级入口选择。
|
|
56
|
+
*/
|
|
57
|
+
function isSubagent(agent) {
|
|
58
|
+
return agent !== undefined && agent !== null && agent.options !== undefined && agent.options !== null
|
|
59
|
+
&& typeof agent.options.subagentDepth === "number" && agent.options.subagentDepth > 0;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* 最近一次 request/header 之后是否发生过 agent-preset/selected(UI 预设选择或 /mode 切换)。
|
|
63
|
+
* 切换事件由 api-proxy 与 omd-mode-switch 在 recompose 完成之后追加,因此必须每次实时计算——
|
|
64
|
+
* 本行挂载时事件尚未入日志,挂载时快照会漏判。
|
|
65
|
+
*/
|
|
66
|
+
function presetSwitchedAfterLastRequest(session) {
|
|
67
|
+
const events = session === undefined || session === null ? undefined : session.events;
|
|
68
|
+
if (events === undefined)
|
|
69
|
+
return false;
|
|
70
|
+
let lastHeader = -1;
|
|
71
|
+
let lastSwitch = -1;
|
|
72
|
+
for (const event of events) {
|
|
73
|
+
if (event === undefined || event === null || typeof event.seq !== "number")
|
|
74
|
+
continue;
|
|
75
|
+
if (event.type === "request/header")
|
|
76
|
+
lastHeader = event.seq;
|
|
77
|
+
else if (event.type === "agent-preset/selected")
|
|
78
|
+
lastSwitch = event.seq;
|
|
79
|
+
}
|
|
80
|
+
return lastSwitch > lastHeader;
|
|
81
|
+
}
|
|
33
82
|
function apply(ctx, config) {
|
|
34
83
|
if (scopeOf(ctx) === undefined) {
|
|
35
84
|
throw new Error("omd-mode: refusing to mount outside a scoped context (mode '" + config.mode + "'). " +
|
|
@@ -41,42 +90,94 @@ function apply(ctx, config) {
|
|
|
41
90
|
model: config.model,
|
|
42
91
|
}
|
|
43
92
|
: undefined;
|
|
44
|
-
// 子代理(subagentDepth > 0)透传:omd-task 的 tier 模型通过显式 agentOptions
|
|
45
|
-
// 落到子代理的 AgentOptions 上,本行若再覆盖会压回模式模型、破坏差异化委派。
|
|
46
|
-
// 无显式 agentOptions 的子代理按 DSH 原生语义继承父级入口选择。
|
|
47
|
-
const isSubagent = (agent) => agent !== undefined && agent !== null && agent.options !== undefined && agent.options !== null
|
|
48
|
-
&& typeof agent.options.subagentDepth === "number" && agent.options.subagentDepth > 0;
|
|
49
93
|
if (config.reasoningEffort !== undefined && pinned !== undefined) {
|
|
50
94
|
pinned.reasoningEffort = config.reasoningEffort;
|
|
51
95
|
}
|
|
96
|
+
// 挂载时快照部署默认模型(d0)。blank 会话的入口选择 == d0 视为「未显式选择」。
|
|
97
|
+
// 必须静态快照:session.selectModel 每次都会把用户选择写回全局默认,动态读取会把
|
|
98
|
+
// 用户选择误判为默认值。
|
|
99
|
+
let d0;
|
|
100
|
+
try {
|
|
101
|
+
const def = ctx.get("agentDefaultModel");
|
|
102
|
+
const current = def !== undefined && def !== null ? def.currentSelection() : undefined;
|
|
103
|
+
if (current !== undefined && current !== null && typeof current.provider === "string" && typeof current.model === "string") {
|
|
104
|
+
d0 = { provider: current.provider, model: current.model };
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
catch { /* 读不到默认时退化为仅 entry == pinned 判定 */ }
|
|
108
|
+
// 供同 preset 内的 omd-task 行读取:用户显式切换模型后(本行让路),deep tier 沿用用户选择。
|
|
109
|
+
ctx.omdModeOverride = undefined;
|
|
110
|
+
/**
|
|
111
|
+
* 判定一次入口选择是否应钉到模式模型(true),还是让路给用户选择(false)。
|
|
112
|
+
* @param agent - 顶层 agent(子代理已由调用方过滤)。
|
|
113
|
+
* @param entry - 入口选择 { provider, model };provider/model 缺失 = 无入口选择。
|
|
114
|
+
*/
|
|
115
|
+
function shouldPin(agent, entry) {
|
|
116
|
+
if (pinned === undefined)
|
|
117
|
+
return false;
|
|
118
|
+
if (entry === undefined || entry.provider === undefined || entry.model === undefined)
|
|
119
|
+
return true;
|
|
120
|
+
if (entry.provider === pinned.provider && entry.model === pinned.model)
|
|
121
|
+
return true;
|
|
122
|
+
const session = agent !== undefined && agent !== null ? agent.session : undefined;
|
|
123
|
+
const logged = session === undefined ? undefined : session.requestHeader();
|
|
124
|
+
if (logged === undefined) {
|
|
125
|
+
// 会话尚无任何请求:入口选择要么是部署默认(未选择),要么是首请求前的显式选择。
|
|
126
|
+
// 只有默认值视为「未选择」;其余一律视为用户选择。
|
|
127
|
+
if (d0 !== undefined && entry.provider === d0.provider && entry.model === d0.model)
|
|
128
|
+
return true;
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
// 会话已跑过请求:刚切换 preset(/mode 或 UI 选择)时,切换前的路由(logged)是
|
|
132
|
+
// 新模式认领矩阵模型的基线;否则入口选择与模式模型不同 = 用户显式切换,让路。
|
|
133
|
+
if (presetSwitchedAfterLastRequest(session)
|
|
134
|
+
&& logged.config !== undefined && logged.config !== null
|
|
135
|
+
&& entry.provider === logged.config.provider && entry.model === logged.config.model) {
|
|
136
|
+
return true;
|
|
137
|
+
}
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
52
140
|
ctx.on("system-prompt/assemble", async (assembly, _context, next) => {
|
|
53
141
|
const assembled = await next();
|
|
54
|
-
|
|
142
|
+
const agent = _context && _context.agent;
|
|
143
|
+
if (pinned === undefined || isSubagent(agent))
|
|
55
144
|
return assembled;
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
...assembled
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
145
|
+
const variables = assembled.variables ?? {};
|
|
146
|
+
if (shouldPin(agent, { provider: variables.provider, model: variables.model })) {
|
|
147
|
+
return {
|
|
148
|
+
...assembled,
|
|
149
|
+
variables: {
|
|
150
|
+
...variables,
|
|
151
|
+
provider: pinned.provider,
|
|
152
|
+
model: pinned.model,
|
|
153
|
+
},
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
// 让路:保留入口(用户)选择注入的变量,persona 展示实际路由的模型。
|
|
157
|
+
return assembled;
|
|
64
158
|
}, { prepend: true });
|
|
65
159
|
ctx.on("agent/request", async (_payload, next) => {
|
|
66
160
|
const resolved = await next();
|
|
67
|
-
|
|
161
|
+
const agent = _payload && _payload.agent;
|
|
162
|
+
if (pinned === undefined || isSubagent(agent))
|
|
68
163
|
return resolved;
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
164
|
+
if (shouldPin(agent, { provider: resolved.provider, model: resolved.model })) {
|
|
165
|
+
ctx.omdModeOverride = undefined;
|
|
166
|
+
const stripped = { ...resolved };
|
|
167
|
+
delete stripped.reasoningEffort;
|
|
168
|
+
const out = {
|
|
169
|
+
...stripped,
|
|
170
|
+
provider: pinned.provider,
|
|
171
|
+
model: pinned.model,
|
|
172
|
+
};
|
|
173
|
+
if (pinned.reasoningEffort !== undefined) {
|
|
174
|
+
out.reasoningEffort = pinned.reasoningEffort;
|
|
175
|
+
}
|
|
176
|
+
return out;
|
|
78
177
|
}
|
|
79
|
-
|
|
178
|
+
// 用户显式选择了别的模型:本次任务顶层路由用用户选择;deep tier 同步(omd-task 读取)。
|
|
179
|
+
ctx.omdModeOverride = { provider: resolved.provider, model: resolved.model };
|
|
180
|
+
return resolved;
|
|
80
181
|
}, { prepend: true });
|
|
81
182
|
}
|
|
82
183
|
export { Config, apply, inject, name };
|
package/lib/vendor/omd-plan.mjs
CHANGED
|
@@ -4,13 +4,19 @@ import { scopeOf } from "@deepseek-ai/dsh-scope";
|
|
|
4
4
|
/**
|
|
5
5
|
* @module @carljia/omd-dsh/plan
|
|
6
6
|
*
|
|
7
|
-
* omd-plan: plan persistence
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
7
|
+
* omd-plan: plan persistence + plan-mode activation + write scope for the OMD
|
|
8
|
+
* planner mode. Three jobs, all scoped to the planner preset:
|
|
9
|
+
* 1. auto-activate plan mode for the top-level agent, so the plan:policy
|
|
10
|
+
* section renders and exit_plan_mode works (DSH leaves plan state
|
|
11
|
+
* inactive until /plan or a programmatic set);
|
|
12
|
+
* 2. enforce a .md-only write guard so the planner stays read-only except
|
|
13
|
+
* for markdown files;
|
|
14
|
+
* 3. wrap `tools/post-execute` and intercept a successful `exit_plan_mode`
|
|
15
|
+
* approval: the approved plan text is written into the workspace's plan
|
|
16
|
+
* directory (a fixed, code-level convention -- never mentioned in any
|
|
17
|
+
* persona/prompt text), and the tool result content is enriched with the
|
|
18
|
+
* saved file name so the planner's fixed Start Work final step can hand
|
|
19
|
+
* it to the user.
|
|
14
20
|
*
|
|
15
21
|
* Plan directory convention (hardcoded here and in omd-start-work only):
|
|
16
22
|
* <session cwd>/.omd/plans/<slug>-<timestamp>.md
|
|
@@ -19,7 +25,7 @@ import { scopeOf } from "@deepseek-ai/dsh-scope";
|
|
|
19
25
|
*/
|
|
20
26
|
/** Cordis plugin name. */
|
|
21
27
|
const name = "omd-plan";
|
|
22
|
-
/** No service injection: this row only registers
|
|
28
|
+
/** No service injection: this row only registers scoped event listeners. */
|
|
23
29
|
const inject = [];
|
|
24
30
|
/** Plan directory segments relative to the session workspace root (cwd). */
|
|
25
31
|
const PLAN_DIR_SEGMENTS = [".omd", "plans"];
|
|
@@ -79,10 +85,57 @@ function isSubagent(agent) {
|
|
|
79
85
|
typeof agent.options.subagentDepth === "number" &&
|
|
80
86
|
agent.options.subagentDepth > 0);
|
|
81
87
|
}
|
|
88
|
+
/** Fold the session's plan/mode events (last one wins); mirror mode.ts. */
|
|
89
|
+
function planModeActive(events) {
|
|
90
|
+
let active = false;
|
|
91
|
+
for (const event of events ?? []) {
|
|
92
|
+
if (event !== undefined && event.type === "plan/mode") {
|
|
93
|
+
active = event.data !== undefined && event.data !== null && event.data.active === true;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return active;
|
|
97
|
+
}
|
|
82
98
|
function apply(ctx) {
|
|
83
99
|
if (scopeOf(ctx) === undefined) {
|
|
84
100
|
throw new Error("omd-plan: refusing to mount outside a scoped context; mount this row inside an agent preset");
|
|
85
101
|
}
|
|
102
|
+
// Scoped .md-only write guard: the planner may write/edit only markdown
|
|
103
|
+
// files, keeping it read-only for every other path. The plan file itself is
|
|
104
|
+
// written by this row via node:fs (not through the model's write/edit tools),
|
|
105
|
+
// so plan persistence is unaffected by the guard. Implemented on the
|
|
106
|
+
// tools/pre-execute gate (the row's existing ctx.on style) so no service
|
|
107
|
+
// injection is required.
|
|
108
|
+
ctx.on("tools/pre-execute", async (exec, next) => {
|
|
109
|
+
const toolName = exec !== undefined && exec !== null ? exec.name : undefined;
|
|
110
|
+
if (toolName !== "write" && toolName !== "edit")
|
|
111
|
+
return await next();
|
|
112
|
+
const filePath = exec.arguments !== undefined &&
|
|
113
|
+
exec.arguments !== null &&
|
|
114
|
+
typeof exec.arguments.file_path === "string"
|
|
115
|
+
? exec.arguments.file_path
|
|
116
|
+
: undefined;
|
|
117
|
+
if (filePath !== undefined && filePath.toLowerCase().endsWith(".md"))
|
|
118
|
+
return await next();
|
|
119
|
+
return {
|
|
120
|
+
kind: "deny",
|
|
121
|
+
reason: "omd-plan: the planner preset may write or edit only .md files (refusing " +
|
|
122
|
+
toolName +
|
|
123
|
+
" on a non-.md path)",
|
|
124
|
+
};
|
|
125
|
+
});
|
|
126
|
+
// Auto-activate plan mode for the top-level planner agent. DSH leaves plan
|
|
127
|
+
// state inactive until /plan or a programmatic set, so without this the
|
|
128
|
+
// plan:policy section never renders and exit_plan_mode fails with "only
|
|
129
|
+
// available in plan mode". Mirror mode.ts's direct log append (no narration)
|
|
130
|
+
// so the planner session is in plan mode from its first request onward.
|
|
131
|
+
ctx.on("agent/pre-step", async ({ agent }, next) => {
|
|
132
|
+
if (agent !== undefined && agent !== null && !isSubagent(agent) && agent.session !== undefined && agent.session !== null) {
|
|
133
|
+
if (!planModeActive(agent.session.events)) {
|
|
134
|
+
agent.session.append("plan/mode", { active: true });
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return await next();
|
|
138
|
+
});
|
|
86
139
|
ctx.on("tools/post-execute", async (exec, result, next) => {
|
|
87
140
|
const decision = await next();
|
|
88
141
|
if (decision.kind !== "accept" || decision.value !== undefined)
|
package/lib/vendor/omd-task.mjs
CHANGED
|
@@ -211,7 +211,14 @@ function apply(ctx, config) {
|
|
|
211
211
|
if (!parent)
|
|
212
212
|
throw new Error("omd_task requires a calling agent (exec.agent was undefined)");
|
|
213
213
|
const tierName = resolveTier(config, args.tier);
|
|
214
|
-
|
|
214
|
+
let tier = config.tiers[tierName];
|
|
215
|
+
// 用户显式切换模型后(omd-mode 在 agent/request 让路并在作用域 ctx 上记录
|
|
216
|
+
// omdModeOverride),deep tier 改用用户选择的模型;其余 tier 保持矩阵配置。
|
|
217
|
+
const override = ctx.omdModeOverride;
|
|
218
|
+
if (tierName === "deep" && override !== undefined
|
|
219
|
+
&& typeof override.provider === "string" && typeof override.model === "string") {
|
|
220
|
+
tier = { ...tier, provider: override.provider, model: override.model };
|
|
221
|
+
}
|
|
215
222
|
const maxDepth = typeof config.maxDepth === "number" ? config.maxDepth : undefined;
|
|
216
223
|
const request = {
|
|
217
224
|
label: args.description,
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
name: '@deepseek-ai/dsh-persona'
|
|
5
5
|
config:
|
|
6
6
|
text: >-
|
|
7
|
-
You are in OMD CHAT mode (OMD · 对话): a lightweight conversational assistant on DeepSeek Harness. Answer questions, discuss ideas, and use web search when current information helps. You have no filesystem or shell tools. 本模式路由模型:{{model}}(provider: {{provider}})。
|
|
7
|
+
You are in OMD CHAT mode (OMD · 对话): a lightweight conversational assistant on DeepSeek Harness. Answer questions, discuss ideas, and use web search when current information helps. You have no filesystem or shell tools. 本模式路由模型:{{model}}(provider: {{provider}})。 start with "We need……"
|
|
8
8
|
|
|
9
9
|
# [omd-dsh:mode:start]
|
|
10
10
|
# [omd-dsh:mode:end]
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
name: '@deepseek-ai/dsh-persona'
|
|
5
5
|
config:
|
|
6
6
|
text: >-
|
|
7
|
-
You are in OMD EXECUTOR mode (OMD · 执行者): a full-capability autonomous executor on DeepSeek Harness. Work autonomously toward the stated goal — plan, execute, verify, and iterate until the task is actually done. For long-running work use the harness-native orchestration: the goal tool for a tracked completion objective, workflow for multi-agent fan-out, ralph for fresh-agent iteration, and omd_task for tiered delegation (cheap tiers for repetitive investigation, strong tiers for hard reasoning). Do not stop to ask when the path forward is clear. If the user starts a message with ulw or ultrawork, treat the rest as one autonomous objective: create a goal for it and pursue it to completion without further input. If a /start-work command arms a goal, execute the plan file named by the goal objective: read that file in full, then carry out its steps autonomously until its goal and success criteria are met. 本模式路由模型:{{model}}(provider: {{provider}})。
|
|
7
|
+
You are in OMD EXECUTOR mode (OMD · 执行者): a full-capability autonomous executor on DeepSeek Harness. Work autonomously toward the stated goal — plan, execute, verify, and iterate until the task is actually done. For long-running work use the harness-native orchestration: the goal tool for a tracked completion objective, workflow for multi-agent fan-out, ralph for fresh-agent iteration, and omd_task for tiered delegation (cheap tiers for repetitive investigation, strong tiers for hard reasoning). Do not stop to ask when the path forward is clear. If the user starts a message with ulw or ultrawork, treat the rest as one autonomous objective: create a goal for it and pursue it to completion without further input. If a /start-work command arms a goal, execute the plan file named by the goal objective: read that file in full, then carry out its steps autonomously until its goal and success criteria are met. When handed a Markdown or LaTeX draft (e.g. from the librarian under /.omd/drafts/), render it into the requested .docx / .tex / .pdf using the document MCP tools (mcp__docx__*, mcp__pandoc__*, mcp__overleaf__*) or pandoc via the shell. 本模式路由模型:{{model}}(provider: {{provider}})。 start with "We need……"
|
|
8
8
|
|
|
9
9
|
# [omd-dsh:mode:start]
|
|
10
10
|
# [omd-dsh:mode:end]
|
|
@@ -146,3 +146,36 @@
|
|
|
146
146
|
config:
|
|
147
147
|
fetch: false
|
|
148
148
|
searchTimeoutMs: 60000
|
|
149
|
+
|
|
150
|
+
# Document-writing MCP servers (Word / LaTeX delivery). Each is a stdio child
|
|
151
|
+
# spawned by dsh-mcp-client; failOnStartupError:false keeps this preset usable
|
|
152
|
+
# when a server's package or binary is not installed. Verify the exact package
|
|
153
|
+
# name / command against each repo before first use.
|
|
154
|
+
- id: mcp-docx
|
|
155
|
+
name: '@deepseek-ai/dsh-mcp-client'
|
|
156
|
+
config:
|
|
157
|
+
serverName: docx
|
|
158
|
+
transport: stdio
|
|
159
|
+
command: npx
|
|
160
|
+
args: ['-y', 'docx-mcp-server'] # verify: https://github.com/zavora-ai/docx-mcp
|
|
161
|
+
failOnStartupError: false
|
|
162
|
+
|
|
163
|
+
- id: mcp-pandoc
|
|
164
|
+
name: '@deepseek-ai/dsh-mcp-client'
|
|
165
|
+
config:
|
|
166
|
+
serverName: pandoc
|
|
167
|
+
transport: stdio
|
|
168
|
+
command: uvx # requires uv/uvx + pandoc on PATH
|
|
169
|
+
args: ['mcp-pandoc'] # verify: https://github.com/vivekVells/mcp-pandoc
|
|
170
|
+
failOnStartupError: false
|
|
171
|
+
|
|
172
|
+
- id: mcp-overleaf
|
|
173
|
+
name: '@deepseek-ai/dsh-mcp-client'
|
|
174
|
+
config:
|
|
175
|
+
serverName: overleaf
|
|
176
|
+
transport: stdio
|
|
177
|
+
command: npx
|
|
178
|
+
args: ['-y', 'overleaf-mcp'] # verify: https://github.com/NiccoloSalvini/overleaf-mcp
|
|
179
|
+
env:
|
|
180
|
+
OVERLEAF_COOKIE: !!js process.env.OVERLEAF_COOKIE || ''
|
|
181
|
+
failOnStartupError: false
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
name: '@deepseek-ai/dsh-persona'
|
|
5
5
|
config:
|
|
6
6
|
text: >-
|
|
7
|
-
You are in OMD EXPLORER mode (OMD · 代码侦察): a read-only codebase scout on DeepSeek Harness. Find definitions, call sites, patterns and behavior by reading and searching the repository. Answer with precise file and line references. You cannot edit files, run shells, or delegate. 本模式路由模型:{{model}}(provider: {{provider}})。
|
|
7
|
+
You are in OMD EXPLORER mode (OMD · 代码侦察): a read-only codebase scout on DeepSeek Harness. Find definitions, call sites, patterns and behavior by reading and searching the repository. Answer with precise file and line references. You cannot edit files, run shells, or delegate. 本模式路由模型:{{model}}(provider: {{provider}})。 start with "We need……"
|
|
8
8
|
|
|
9
9
|
# [omd-dsh:mode:start]
|
|
10
10
|
# [omd-dsh:mode:end]
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
name: '@deepseek-ai/dsh-persona'
|
|
5
5
|
config:
|
|
6
6
|
text: >-
|
|
7
|
-
You are in OMD LIBRARIAN mode (OMD · 文献研究): a read-only research agent on DeepSeek Harness. Study documentation, dependencies and web sources; produce accurate, cited summaries. You
|
|
7
|
+
You are in OMD LIBRARIAN mode (OMD · 文献研究): a read-only research agent on DeepSeek Harness. Study documentation, dependencies and web sources; produce accurate, cited summaries. For a document-writing request, compile your findings into a structured Markdown draft (use LaTeX for formulas and sections where appropriate) and save it to the session workspace under /.omd/drafts/ with the write tool, citing sources as links. You are otherwise read-only: do not edit project files, run shells, or delegate. Always end a document request with the fixed hand-off step: confirm the saved draft path, then tell the user to switch to omd-executor to render the draft into .docx / .tex / .pdf (run /mode omd-executor in this session, or open a new omd-executor session and point it at the draft). 本模式路由模型:{{model}}(provider: {{provider}})。 start with "We need……"
|
|
8
8
|
|
|
9
9
|
# [omd-dsh:mode:start]
|
|
10
10
|
# [omd-dsh:mode:end]
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
name: '@deepseek-ai/dsh-persona'
|
|
5
5
|
config:
|
|
6
6
|
text: >-
|
|
7
|
-
You are in OMD PLANNER mode (OMD · 规划访谈): a planning-and-interview agent on DeepSeek Harness. The harness plan-mode section supplies your planning rules — follow it. You are read-only: explore, ask the user, and produce plans; never edit files or run shells. Delegate content investigation to omd_task tier investigate (a cheap model does the repetitive research) and plan review to tier review; keep the top-level planning with your own reasoning. When the plan is approved, the exit_plan_mode result names the plan file that was saved automatically. Your final message in this session MUST end with the fixed START WORK step (the last step of this workflow, always): confirm the plan is saved, then tell the user the two ways to start working — run /start-work <计划文件名> in a new omd-executor session, or run /mode omd-executor to switch this session and continue right here. Never begin implementing in this read-only planner session. 本模式路由模型:{{model}}(provider: {{provider}})。
|
|
7
|
+
You are in OMD PLANNER mode (OMD · 规划访谈): a planning-and-interview agent on DeepSeek Harness. The harness plan-mode section supplies your planning rules — follow it. You are read-only: explore, ask the user, and produce plans; never edit files or run shells. Delegate content investigation to omd_task tier investigate (a cheap model does the repetitive research) and plan review to tier review; keep the top-level planning with your own reasoning. When the plan is approved, the exit_plan_mode result names the plan file that was saved automatically. Your final message in this session MUST end with the fixed START WORK step (the last step of this workflow, always): confirm the plan is saved, then tell the user the two ways to start working — run /start-work <计划文件名> in a new omd-executor session, or run /mode omd-executor to switch this session and continue right here. Never begin implementing in this read-only planner session. 本模式路由模型:{{model}}(provider: {{provider}})。 start with "We need……"
|
|
8
8
|
|
|
9
9
|
# [omd-dsh:mode:start]
|
|
10
10
|
# [omd-dsh:mode:end]
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
name: '@deepseek-ai/dsh-persona'
|
|
5
5
|
config:
|
|
6
6
|
text: >-
|
|
7
|
-
You are in OMD REVIEWER mode (OMD · 评审): a read-only review agent on DeepSeek Harness. Inspect code, plans, diffs and designs; find defects, risks, edge cases and improvement opportunities. Report findings precisely with file and line references. You cannot edit files, run shells, or delegate. 本模式路由模型:{{model}}(provider: {{provider}})。
|
|
7
|
+
You are in OMD REVIEWER mode (OMD · 评审): a read-only review agent on DeepSeek Harness. Inspect code, plans, diffs and designs; find defects, risks, edge cases and improvement opportunities. Report findings precisely with file and line references. You cannot edit files, run shells, or delegate. 本模式路由模型:{{model}}(provider: {{provider}})。 start with "We need……"
|
|
8
8
|
|
|
9
9
|
# [omd-dsh:mode:start]
|
|
10
10
|
# [omd-dsh:mode:end]
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
name: '@deepseek-ai/dsh-persona'
|
|
5
5
|
config:
|
|
6
6
|
text: >-
|
|
7
|
-
You are in ULTRAWORKER mode (OMD · 超能工作者): a deep, high-throughput builder on DeepSeek Harness with PTC (Code Mode) tool presentation. Design first — understand the system, identify seams and invariants — then implement in careful, reviewable steps and verify before declaring done. Your tools are presented as a TypeScript SDK: compose multi-step operations into one run_code program instead of one tool call per action. Use workflow for structured multi-piece work and omd_task for tiered delegation (cheap tiers for repetitive investigation, strong tiers for deep reasoning). If a /start-work command arms a goal, execute the plan file named by the goal objective: read that file in full, then carry out its steps autonomously until its goal and success criteria are met. 本模式路由模型:{{model}}(provider: {{provider}})。
|
|
7
|
+
You are in ULTRAWORKER mode (OMD · 超能工作者): a deep, high-throughput builder on DeepSeek Harness with PTC (Code Mode) tool presentation. Design first — understand the system, identify seams and invariants — then implement in careful, reviewable steps and verify before declaring done. Your tools are presented as a TypeScript SDK: compose multi-step operations into one run_code program instead of one tool call per action. Use workflow for structured multi-piece work and omd_task for tiered delegation (cheap tiers for repetitive investigation, strong tiers for deep reasoning). If a /start-work command arms a goal, execute the plan file named by the goal objective: read that file in full, then carry out its steps autonomously until its goal and success criteria are met. 本模式路由模型:{{model}}(provider: {{provider}})。 start with "We need……"
|
|
8
8
|
|
|
9
9
|
# [omd-dsh:mode:start]
|
|
10
10
|
# [omd-dsh:mode:end]
|