dsh-subagent-profile 0.4.0 → 0.5.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.
@@ -1,4 +1,4 @@
1
- # `orchestrator` 编排者预设:主 Agent 协调模式。
1
+ # `orchestrator-v2` 编排者预设 V2:主 Agent 协调模式 + 梁神 V2 两阶段引导。
2
2
  #
3
3
  # 与官方 `standard` 预设逐行一致,仅 persona 段改写为「主协调 Agent」身份:
4
4
  # 拆解任务、经宿主注册的 `dispatch` 工具(按场景选方案/预设/模型/推理强度)
@@ -6,8 +6,27 @@
6
6
  # 再集成结果。`dispatch` 工具本身由 dsh-subagent-profile 宿主行注册,对所有
7
7
  # 预设可见;本预设只补下面的委派工具。
8
8
  #
9
- # 改编自官方 `standard` 预设(仅 persona 段不同);原始 DeepSeek 版权与
10
- # MIT 许可声明见开源仓库。
9
+ # V2 改造(模仿梁神模式 V2,适配当前 DSH >= 0.1.2):
10
+ # - 引入两阶段引导 `./tool-bootstrap.mjs`:第一个持久工具/调用事件之前,
11
+ # 模型只看到 Minimal 双工具(`bash` + `str_replace_editor`)与本预设的
12
+ # persona 段,无运行时上下文、只放行白名单消息来源;锚定(或四步兜底、
13
+ # 或首轮结束)后升入 PTC 模式(单 `run_code` 工具)并恢复全量目录,
14
+ # 工作区指令与技能目录延迟一步注入。压缩后回落到受控阶段
15
+ # (双工具 + compactionTools 核心工作集,含 dispatch/subagent 以维持委派),
16
+ # 等待新的升态信号。
17
+ # - tool-bootstrap.mjs 已适配 0.1.2:经 `session.snapshotEvents()` 读事件日志
18
+ # (旧 `session.events` 属性已移除,直接读会每轮崩 "Cannot read properties
19
+ # of undefined (reading 'length')");persona 已含 `{{cwd}}` 模板时不再追加
20
+ # 字面工作目录行。
21
+ # - 补 `str_replace_editor`(phase-1 编辑器);win32 下补 `custom-bash`
22
+ # (Git Bash `bash` 工具)以保住 phase-1 的 `bash` 锚点——官方 tool-bash
23
+ # 在 win32 禁用、tool-pwsh 不叫 bash,没有它 phase-1 会退化成全量目录。
24
+ # posix 下官方 tool-bash 本身就叫 bash,无需补行。
25
+ # - 补 `command-goal`(/goal 人机命令,对齐当前官方 standard);spawn 行补
26
+ # `modelSelectionSettings: true`。
27
+ #
28
+ # 改编自官方 `standard` 预设与梁神模式 V2(liangshen-v2);原始 DeepSeek
29
+ # 版权与 MIT 许可声明见开源仓库与随附 NOTICE。
11
30
  #
12
31
  # 本文件是 agent 平面组成:由 roster 挂载一次到 standing scope,引用它的会话
13
32
  # 经 scope 父系加入,本文件注册的工具与 prompt 段覆盖每个加入的 agent;会话
@@ -37,6 +56,35 @@
37
56
 
38
57
  边界:子 Agent 能力 ⊆ 你,派发不豁免父会话的审批要求;子 Agent 失败或超出权限时自己接手重做,或用 ask_user_question 向用户澄清。
39
58
 
59
+ # 两阶段输入引导(同梁神 V2 的 tool-bootstrap):在第一个持久 tool/call 事件
60
+ # 之前,模型只看到 `bash` + `str_replace_editor` 双工具与本预设 persona 段、
61
+ # 无运行时上下文、只放行白名单消息来源(直接用户消息 + goal 自动轮次)。
62
+ # anchorGate 让升态保持到首个 minimal-like 推理块(四步兜底);
63
+ # promoteAfterFirstResponse 避免无工具首答后永久双工具,并在首轮结束时释放
64
+ # 锚门会话。升态后目录以 PTC 模式呈现(线上单一 run_code 工具,步边界切换),
65
+ # 工作区指令与技能目录延迟一步;bootstrapMaxTokens 封顶 phase-1 输出预算
66
+ # (1024),升态后再剥离。压缩后回落到受控阶段(双工具 + compactionTools),
67
+ # 直到新升态信号——本预设把 dispatch/subagent 加进 compactionTools,
68
+ # 让压缩后的编排者仍能继续委派。
69
+ - id: tool-bootstrap
70
+ name: ./tool-bootstrap.mjs
71
+ config:
72
+ shellTools: [bash]
73
+ commonTools: [str_replace_editor]
74
+ messageSources: [user, goal]
75
+ anchorGate: true
76
+ maxBootstrapSteps: 4
77
+ promoteAfterFirstResponse: true
78
+ bootstrapMaxTokens: 1024
79
+ compactionTools: [read, write, edit, glob, grep, todo_write, ask_user_question, dispatch, subagent]
80
+ deferredSources: [agent-instructions, skill-catalog]
81
+ deferredGraceSteps: 1
82
+ promotedPresentation: code
83
+ # 升态后以一次性非指令式提示替代 AGENTS.md 全文注入(梁神 V2 保留的
84
+ # issue #388 设定),知识经 read / skill_load 可达。设 false 恢复全文注入。
85
+ instructionHint: true
86
+
87
+ # 工作区指令在 phase 1 被 tool-bootstrap 隔离,升态后延迟一步注入并保持更新。
40
88
  - id: agent-instructions
41
89
  name: '@deepseek-ai/dsh-agent-instructions'
42
90
  config:
@@ -57,8 +105,25 @@
57
105
  name: '@deepseek-ai/dsh-tool-pwsh'
58
106
  disabled: !!js process.platform !== 'win32'
59
107
 
108
+ # win32 专用 `bash` 工具(custom-bash.mjs,同梁神 V2):官方 tool-bash 在
109
+ # win32 禁用、tool-pwsh 又不叫 bash,phase-1 的 Minimal 锚点(shellTools:
110
+ # [bash])会找不到 shell 而退化成全量目录。此工具经跨平台 subprocess 缝隙
111
+ # 执行 Git Bash(`bash -c`),保住锚点;posix 下官方 tool-bash 已提供
112
+ # 同名工具,此行走禁用。非零退出上报不抛;调用间状态不持久;Windows 无
113
+ # OS 沙箱限制。`bashPath` 可选(缺省按 Git 安装根推断)。
114
+ - id: custom-bash
115
+ name: ./custom-bash.mjs
116
+ disabled: !!js process.platform !== 'win32'
117
+
60
118
  # ── filesystem ──────────────────────────────────────────────────────────────
61
119
 
120
+ # Phase-1 编辑器(同梁神 V2):顶层直排,复用宿主沙箱 ctx.fs 而非裸本地
121
+ # 文件系统,schema 与官方 Minimal `str_replace_editor` 一致。
122
+ - id: str-replace-editor
123
+ name: '@deepseek-ai/dsh-tool-str-replace-editor'
124
+ config:
125
+ maxOutputChars: 16000
126
+
62
127
  # 两者都注册进宿主 tools 注册表且不提供服务,无需 realm;fs 服务与其策略留在宿主。
63
128
  - id: tool-fs
64
129
  name: '@deepseek-ai/dsh-tool-fs'
@@ -90,10 +155,15 @@
90
155
 
91
156
  # ── goals ───────────────────────────────────────────────────────────────────
92
157
 
93
- # 只放模型可见的工具。goal 服务、会话驱动与 /goal 命令留在宿主平面:
94
- # Gateway 以 Remote endpoint 服务 goal 域,接收方来自生成描述符,条目本地
95
- # realm 会隐藏它。注册表本就按会话键控,一个宿主实例服务所有会话;预设
96
- # 只决定其 agent 能否调用 goal 工具。
158
+ # goal 服务与会话驱动留在宿主平面,Gateway 在那里解析它们。人机命令与
159
+ # 模型可见工具注册进本预设的 scope 层。
160
+ - id: command-goal
161
+ name: '@deepseek-ai/dsh-command-goal'
162
+
163
+ # 只放模型可见的工具。goal 服务与会话驱动留在宿主平面:Gateway 以 Remote
164
+ # endpoint 服务 goal 域,接收方来自生成描述符,条目本地 realm 会隐藏它。
165
+ # 注册表本就按会话键控,一个宿主实例服务所有会话;预设只决定其 agent
166
+ # 能否调用 goal 工具。
97
167
  - id: tool-goal
98
168
  name: '@deepseek-ai/dsh-tool-goal'
99
169
 
@@ -182,6 +252,7 @@
182
252
  config:
183
253
  provider: spawn
184
254
  toolName: subagent
255
+ modelSelectionSettings: true
185
256
  backgroundMode: continuable
186
257
 
187
258
  - id: tool-subagent-fork
@@ -0,0 +1,213 @@
1
+ /**
2
+ * custom-bash — a Windows-capable `bash` tool for the liangshen preset. It
3
+ * registers under the SAME name (`bash`) as the persistent shell so the
4
+ * phase-1 Minimal anchor (bash + str_replace_editor) holds on every platform,
5
+ * but executes through `ctx.subprocess.spawn` instead of a PTY.
6
+ *
7
+ * WHY: DSH's PTY backend is linux/darwin-only — subprocess-local throws
8
+ * "terminal inspection is unsupported on platform win32", so the
9
+ * persistent-shell group (dsh-terminal + dsh-terminal-bash +
10
+ * dsh-tool-bash-persistent) cannot spawn on Windows. agent.cordis.yml disables
11
+ * that group on win32 and enables this tool instead; both platforms end up
12
+ * with exactly one `bash` tool and the byte-exact schema anchor is kept.
13
+ *
14
+ * Executable resolution (config `bashPath`, no hardcoded install path):
15
+ * an explicit non-empty `bashPath` wins unconditionally. Unset, the Git Bash
16
+ * executable is INFERRED in probe order:
17
+ * 1. the `git` executable on PATH — its install root carries `bin\bash.exe`
18
+ * one level up from `cmd\`, beside `bin\`, or two levels up from
19
+ * `mingw64\bin\` (the standard installer, choco, and winget all resolve
20
+ * here);
21
+ * 2. the well-known Git-for-Windows roots derived from environment variables
22
+ * (ProgramFiles, ProgramFiles(x86), LOCALAPPDATA\Programs\Git, scoop's
23
+ * ~\scoop\apps\git\current junction);
24
+ * 3. plain `bash` through `ctx.subprocess.resolveExecutable` (PATH lookup —
25
+ * last resort; on Windows that may pick the WSL shim, which is still true
26
+ * bash even though paths shift to /mnt/…).
27
+ *
28
+ * If NOTHING resolves the tool fails with an actionable error — it never
29
+ * silently executes under pwsh/cmd, which are different command languages.
30
+ *
31
+ * Semantics mirror the official bash tool: `bash -c <command>` in a fresh
32
+ * process, bounded output, non-zero exit reported not thrown. No sandbox
33
+ * confinement on Windows (the sandbox backend is linux-only); the description
34
+ * says so.
35
+ *
36
+ * Ported from xiaobright/dsh-anchored-standard (MIT) — see NOTICE.
37
+ */
38
+
39
+ import { access } from 'node:fs/promises'
40
+ import { dirname, join } from 'node:path'
41
+
42
+ /** Cordis plugin name used by loader diagnostics. */
43
+ export const name = 'custom-bash'
44
+
45
+ /** The subprocess and tools services must exist before this tool can register. */
46
+ export const inject = ['subprocess', 'tools']
47
+
48
+ const DEFAULT_TIMEOUT_MS = 120000
49
+ const DEFAULT_MAX_OUTPUT_BYTES = 64000
50
+
51
+ /**
52
+ * Git Bash candidate paths, in probe order (see the header): the `git`
53
+ * executable's install root first, then the well-known env-derived roots.
54
+ * Exported for tests; pure — existence probing happens at the call site.
55
+ */
56
+ export function bashCandidates(env, gitExe) {
57
+ const candidates = []
58
+ // git at <root>\cmd\git.exe (installer/scoop) or <root>\bin\git.exe →
59
+ // <root>\bin\bash.exe; <root>\mingw64\bin\git.exe (portable) → two up.
60
+ // A bare relative name means `git` did not actually resolve to a path.
61
+ if (typeof gitExe === 'string' && /[/\\]/.test(gitExe)) {
62
+ const dir = dirname(gitExe)
63
+ const root = dirname(dir)
64
+ candidates.push(
65
+ join(root, 'bin', 'bash.exe'),
66
+ join(dir, 'bash.exe'),
67
+ join(dirname(root), 'bin', 'bash.exe'),
68
+ )
69
+ }
70
+ if (env.ProgramFiles) candidates.push(join(env.ProgramFiles, 'Git', 'bin', 'bash.exe'))
71
+ if (env['ProgramFiles(x86)']) candidates.push(join(env['ProgramFiles(x86)'], 'Git', 'bin', 'bash.exe'))
72
+ if (env.LOCALAPPDATA) candidates.push(join(env.LOCALAPPDATA, 'Programs', 'Git', 'bin', 'bash.exe'))
73
+ if (env.USERPROFILE) candidates.push(join(env.USERPROFILE, 'scoop', 'apps', 'git', 'current', 'bin', 'bash.exe'))
74
+ // Layouts overlap (a `bin` git.exe derives the same bash twice) — probe
75
+ // order survives the dedupe, insertion order is preserved.
76
+ return [...new Set(candidates)]
77
+ }
78
+
79
+ /** Tool parameter schema for the model-facing command. */
80
+ const commandSchema = {
81
+ type: 'object',
82
+ properties: {
83
+ command: {
84
+ type: 'string',
85
+ description: 'The bash command to execute (`bash -c` string domain).',
86
+ },
87
+ workdir: {
88
+ type: 'string',
89
+ description: 'Optional working directory; defaults to the session cwd.',
90
+ },
91
+ },
92
+ required: ['command'],
93
+ additionalProperties: false,
94
+ }
95
+
96
+ /** Register the model-facing `bash` tool. */
97
+ export function apply(ctx, config) {
98
+ const explicitBashPath = typeof config?.bashPath === 'string' && config.bashPath.length > 0 ? config.bashPath : undefined
99
+ const timeoutMs = Number.isSafeInteger(config?.timeoutMs) && config.timeoutMs > 0 ? config.timeoutMs : DEFAULT_TIMEOUT_MS
100
+ const maxOutputBytes = Number.isSafeInteger(config?.maxOutputBytes) && config.maxOutputBytes > 0 ? config.maxOutputBytes : DEFAULT_MAX_OUTPUT_BYTES
101
+
102
+ // The inferred executable is memoized per plugin instance: candidate probing
103
+ // walks the filesystem, and the answer cannot change within a mount. A
104
+ // failed inference is NOT memoized — the plain `bash` fallback resolves
105
+ // fresh on every execute until some probe succeeds.
106
+ let inferredShell
107
+ const exists = (path) => access(path).then(() => true, () => false)
108
+ const resolveShell = async (signal) => {
109
+ if (explicitBashPath !== undefined) {
110
+ // A misconfigured explicit path must fail as itself, not as a
111
+ // discovery miss — the raw resolution error says which path failed.
112
+ return ctx.subprocess.resolveExecutable(explicitBashPath, undefined, signal)
113
+ }
114
+ if (inferredShell !== undefined) {
115
+ return ctx.subprocess.resolveExecutable(inferredShell, undefined, signal)
116
+ }
117
+ let gitExe
118
+ try {
119
+ gitExe = await ctx.subprocess.resolveExecutable('git', undefined, signal)
120
+ } catch {
121
+ // git unresolvable → the env-derived candidates below still apply
122
+ }
123
+ for (const candidate of bashCandidates(process.env, gitExe)) {
124
+ if (!(await exists(candidate))) continue
125
+ try {
126
+ inferredShell = await ctx.subprocess.resolveExecutable(candidate, undefined, signal)
127
+ return inferredShell
128
+ } catch {
129
+ // Exists but unresolvable (EPERM, a broken scoop junction): keep
130
+ // probing — one bad root must not block the rest of the chain, and
131
+ // nothing is memoized so later executes can still find a good one.
132
+ continue
133
+ }
134
+ }
135
+ try {
136
+ return await ctx.subprocess.resolveExecutable('bash', undefined, signal)
137
+ } catch (error) {
138
+ // Total discovery failure (no Git Bash root, no env root, no bash on
139
+ // PATH): name the remedies instead of leaking a raw ENOENT. Never
140
+ // fall back to pwsh/cmd here — the schema promises `bash -c`
141
+ // semantics; a different shell would silently break every command.
142
+ throw new Error(`bash executable not found — install Git for Windows, expose a bash on PATH, or set the custom-bash \`bashPath\` config (${String((error && error.message) || error)})`)
143
+ }
144
+ }
145
+
146
+ ctx.tools.register({
147
+ name: 'bash',
148
+ description: [
149
+ 'Run commands in a bash shell (Git Bash on Windows)',
150
+ '* When invoking this tool, the contents of the "command" parameter does NOT need to be XML-escaped.',
151
+ "* You don't have access to the internet via this tool.",
152
+ '* You do have access to a mirror of common linux and python packages via apt and pip.',
153
+ '* State does NOT persist across command calls: each call runs in a fresh shell.',
154
+ "* To inspect a particular line range of a file, e.g. lines 10-25, try 'sed -n 10,25p /path/to/the/file'.",
155
+ '* Please avoid commands that may produce a very large amount of output.',
156
+ '* NOTE: runs without OS sandbox confinement on Windows (no landlock); treat output as untrusted.',
157
+ ].join('\n'),
158
+ parameters: commandSchema,
159
+ output: {
160
+ schema: {
161
+ type: 'object',
162
+ additionalProperties: false,
163
+ properties: {
164
+ text: { type: 'string' },
165
+ },
166
+ required: ['text'],
167
+ },
168
+ render: (_args, value) => [{ type: 'text', text: value.text }],
169
+ },
170
+ async execute(args, exec) {
171
+ const shell = await resolveShell(exec?.signal)
172
+ const workdir = typeof args.workdir === 'string' && args.workdir.length > 0
173
+ ? args.workdir
174
+ : exec?.agent?.session?.header?.cwd
175
+ const signal = exec?.signal
176
+ const handle = ctx.subprocess.spawn({
177
+ argv: [shell, '-c', args.command],
178
+ ...workdir !== undefined ? { cwd: workdir } : {},
179
+ stdio: {
180
+ stdin: 'ignore',
181
+ stdout: { maxBytes: maxOutputBytes },
182
+ stderr: { maxBytes: maxOutputBytes },
183
+ },
184
+ ...signal !== undefined ? { signal } : {},
185
+ graceMs: 3000,
186
+ })
187
+ let outcome
188
+ try {
189
+ outcome = await handle.done
190
+ } catch (error) {
191
+ // A spawn-level failure (bad executable, EPERM) surfaces as a throw,
192
+ // which the runtime turns into an isError result.
193
+ throw new Error(`bash spawn failed: ${String(error)}`)
194
+ }
195
+ let stdout = ''
196
+ let stderr = ''
197
+ try {
198
+ stdout = handle.collected.stdout.readFrom(0).text
199
+ stderr = handle.collected.stderr.readFrom(0).text
200
+ } catch {
201
+ // Collected readers may be unavailable on some backends; tolerate.
202
+ }
203
+ const text = [stdout, stderr].filter((part) => part.length > 0).join('\n')
204
+ const tail = text.length > 0 ? text : `exit code: ${outcome.exitCode} (no output)`
205
+ if (outcome.exitCode !== 0) {
206
+ // Non-zero exit is a reported failure, not a throw: the model sees the
207
+ // command output plus the exit code.
208
+ throw new Error(tail)
209
+ }
210
+ return { text: tail }
211
+ },
212
+ })
213
+ }
@@ -0,0 +1,2 @@
1
+ name: 编排者模式 V2
2
+ description: 编排者模式(orchestrator)的梁神 V2 改造版:第一阶段只暴露 Minimal 双工具(bash + str_replace_editor)与编排者 persona 锚定轨迹,稳定后升入 PTC 模式恢复全量编排能力(dispatch / subagent / workflow / ralph)。已适配当前 DSH 0.1.2+。