@menghuan1918/dsh-apollo 0.3.0 → 0.4.1
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 +45 -24
- package/cordis.patch.yml +3 -3
- package/docs/DEVELOPMENT.md +97 -45
- package/package.json +5 -4
- package/presets/eng/NOTICE +3 -2
- package/presets/eng/plugins/exit-guard/index.js +6 -6
- package/presets/eng/plugins/mode-instructions/index.js +111 -35
- package/presets/eng/plugins/wait-subagent/index.js +9 -4
- package/presets/eng-ptc.patch.yml +186 -0
- package/presets/eng.patch.yml +14 -17
- package/skills/brainstorming/SKILL.md +54 -54
- package/skills/brainstorming/spec-document-reviewer-prompt.md +6 -6
- package/skills/code-review/SKILL.md +1 -1
- package/skills/dev/SKILL.md +16 -18
- package/skills/exec/SKILL.md +56 -69
- package/skills/handoff/SKILL.md +14 -14
- package/skills/main-agent-driven-development/SKILL.md +33 -33
- package/src/cli-adapter.js +1 -1
- package/src/client.js +329 -16
- package/src/config.js +30 -4
- package/src/panel.js +406 -14
- package/src/subagents.js +3 -4
- package/presets/eng-noask/NOTICE +0 -2
- package/presets/eng-noask/plugins/exit-guard/index.js +0 -111
- package/presets/eng-noask/plugins/exit-guard/package.json +0 -7
- package/presets/eng-noask/plugins/mode-instructions/index.js +0 -48
- package/presets/eng-noask/plugins/mode-instructions/package.json +0 -7
- package/presets/eng-noask/plugins/wait-subagent/index.js +0 -228
- package/presets/eng-noask/plugins/wait-subagent/package.json +0 -7
- package/presets/eng-noask.patch.yml +0 -341
|
@@ -1,228 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `wait_subagent` tool — explicit wait for continuable children to settle.
|
|
3
|
-
*
|
|
4
|
-
* Preset-local plugin for the eng preset (resolved relative to this preset's
|
|
5
|
-
* directory). Continuable subagents live in the `subagents` registry, not the
|
|
6
|
-
* `jobs` registry, so the only settlement signal is the scoped `subagent/end`
|
|
7
|
-
* event. The tool takes an ARRAY of durable child ids (`subagent_id`), awaits
|
|
8
|
-
* them all concurrently (duplicates deduped, order preserved), and returns one
|
|
9
|
-
* SHORT line per id (done + stopReason).
|
|
10
|
-
*
|
|
11
|
-
* The children's content is none of this tool's business: the subagent manager
|
|
12
|
-
* unconditionally delivers a settlement notice to the parent BEFORE
|
|
13
|
-
* `subagent/end` fires (dispose order: notifySettlement() →
|
|
14
|
-
* observer.settle()), carrying the one-line account and the closing message.
|
|
15
|
-
* A busy parent is steered, so the notice sits in the pending next-step queue
|
|
16
|
-
* and reaches the model right after this tool result — exactly once, per
|
|
17
|
-
* child, by the framework. Earlier designs returned the child's final message
|
|
18
|
-
* here AND spliced the duplicate notice (and any mid-run `report`) out of the
|
|
19
|
-
* parent inbox; that coupled this plugin to `agent.inbox` internals and to
|
|
20
|
-
* the `subagent-report` source kind, which upstream removed in
|
|
21
|
-
* 0.1.2-alpha.4 (bidirectional `send_message` replaced `report`). Returning
|
|
22
|
-
* "done" lines and leaving delivery to the framework has no such coupling.
|
|
23
|
-
* Mid-run messages the child sends (`send_message`, source kind
|
|
24
|
-
* `agent-message`) likewise stay in place.
|
|
25
|
-
*
|
|
26
|
-
* `timeout_ms` is ONE shared window over the whole batch (all timers start at
|
|
27
|
-
* the same instant), matching the semantics same-message parallel calls had.
|
|
28
|
-
* An id that is not a direct child fails the whole call fast — nobody is
|
|
29
|
-
* waited for — keeping the contract the single-id version had. A child
|
|
30
|
-
* dispatched moments ago may not have started its first turn yet: its status
|
|
31
|
-
* is not `running` and no settlement exists. Returning early with a "not
|
|
32
|
-
* running" note there (the pre-grace behavior) ended the turn before the
|
|
33
|
-
* child even started and forced a re-wait on the next turn — session audit
|
|
34
|
-
* 2f5dd618. Instead, a not-yet-started child gets a startup grace window
|
|
35
|
-
* (START_GRACE_MS): the tool keeps waiting while the child spins up,
|
|
36
|
-
* settles, the optional timeout elapses, or the grace expires without the
|
|
37
|
-
* child ever starting (only then a retryable informational line).
|
|
38
|
-
*
|
|
39
|
-
* Zero imports on purpose: the module loads from the preset directory, where
|
|
40
|
-
* Node's upward node_modules walk never reaches the harness install, so bare
|
|
41
|
-
* package imports would fail. Everything needed comes from the context.
|
|
42
|
-
*/
|
|
43
|
-
|
|
44
|
-
export const name = 'tool-wait-subagent'
|
|
45
|
-
|
|
46
|
-
export const inject = ['tools', 'subagents', 'agents']
|
|
47
|
-
|
|
48
|
-
/** Grace window for a just-dispatched child that has not started running yet. */
|
|
49
|
-
const START_GRACE_MS = 30_000
|
|
50
|
-
|
|
51
|
-
/** Status polling interval while waiting for a not-yet-started child. */
|
|
52
|
-
const POLL_MS = 250
|
|
53
|
-
|
|
54
|
-
export function apply(ctx) {
|
|
55
|
-
/** childId -> { time, info } — latest settlement observed since mount. */
|
|
56
|
-
const settled = new Map()
|
|
57
|
-
/** childId -> finish(info) callbacks awaiting settlement. */
|
|
58
|
-
const waiters = new Map()
|
|
59
|
-
|
|
60
|
-
ctx.on('subagent/end', (info) => {
|
|
61
|
-
const id = info && info.id
|
|
62
|
-
if (typeof id !== 'string') return
|
|
63
|
-
settled.set(id, { time: Date.now(), info })
|
|
64
|
-
const list = waiters.get(id)
|
|
65
|
-
if (list !== undefined) {
|
|
66
|
-
waiters.delete(id)
|
|
67
|
-
for (const finish of [...list]) finish({ kind: 'settled', info })
|
|
68
|
-
}
|
|
69
|
-
})
|
|
70
|
-
|
|
71
|
-
ctx.tools.register({
|
|
72
|
-
name: 'wait_subagent',
|
|
73
|
-
description: 'Wait for one or more background continuable subagents (spawned by subagent or subagent_fork) to finish their current turn. Pass their durable ids as an array in subagent_id — all are awaited concurrently and the result carries one short line per id (done + stop reason); a child\'s closing message arrives as the framework settlement notice immediately after this result; do not expect it here. Blocks until every listed child settles, the optional timeout_ms elapses (one shared window over all ids), or the user interrupts — without timeout_ms, until completion. A just-dispatched child that has not started yet is awaited through a brief startup grace rather than reported as not running. Use it when your next step depends on those results; with independent work remaining, prefer background plus completion notices. Do not pass bash job ids — those use job_output.',
|
|
74
|
-
parameters: {
|
|
75
|
-
type: 'object',
|
|
76
|
-
properties: {
|
|
77
|
-
subagent_id: {
|
|
78
|
-
type: 'array',
|
|
79
|
-
items: { type: 'string' },
|
|
80
|
-
minItems: 1,
|
|
81
|
-
description: 'One or more durable subagent ids returned by the subagent or subagent_fork tool call; every id is awaited concurrently.',
|
|
82
|
-
},
|
|
83
|
-
timeout_ms: {
|
|
84
|
-
type: 'integer',
|
|
85
|
-
description: 'Optional maximum wait in milliseconds applied as one shared window over all ids; omit to wait until every child settles. On expiry the tool returns while some children are still running.',
|
|
86
|
-
},
|
|
87
|
-
},
|
|
88
|
-
required: ['subagent_id'],
|
|
89
|
-
},
|
|
90
|
-
output: {
|
|
91
|
-
schema: { type: 'string' },
|
|
92
|
-
render: (_args, value) => [{ type: 'text', text: value }],
|
|
93
|
-
},
|
|
94
|
-
isConcurrencySafe: () => true,
|
|
95
|
-
async execute(args, exec) {
|
|
96
|
-
// Dedupe preserving input order; the schema already enforces a
|
|
97
|
-
// non-empty array, the guard below only covers malformed direct calls.
|
|
98
|
-
const ids = [...new Set(Array.isArray(args.subagent_id) ? args.subagent_id : [])]
|
|
99
|
-
if (ids.length === 0) return 'subagent_id must be a non-empty array of subagent ids.'
|
|
100
|
-
const parent = exec.agent
|
|
101
|
-
if (parent === undefined) throw new Error('wait_subagent requires a calling agent (exec.agent was undefined)')
|
|
102
|
-
|
|
103
|
-
// Verify every id is one of this agent's direct children — fail fast,
|
|
104
|
-
// waiting for nobody (the contract the single-id version had).
|
|
105
|
-
let entries
|
|
106
|
-
try {
|
|
107
|
-
entries = await ctx.subagents.listChildren(parent.id, exec.signal)
|
|
108
|
-
} catch (error) {
|
|
109
|
-
return 'listChildren failed: ' + (error && error.message ? error.message : String(error))
|
|
110
|
-
}
|
|
111
|
-
const known = new Set(entries.map((entry) => entry.id))
|
|
112
|
-
const unknown = ids.filter((id) => !known.has(id))
|
|
113
|
-
if (unknown.length > 0) {
|
|
114
|
-
return `unknown subagent ${unknown.map((id) => `"${id}"`).join(', ')}: not a direct child of this agent (list_agents shows your children; only depth-1 children can be awaited).`
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
const statusOf = (id) => {
|
|
118
|
-
const agent = ctx.agents.get(id)
|
|
119
|
-
return agent === undefined ? 'ready' : agent.status === 'running' ? 'running' : 'idle'
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
/** Settlement line: short done line; content belongs to the framework notice. */
|
|
123
|
-
const doneLine = (id, info) => `subagent ${id} done (${info && info.stopReason ? info.stopReason : 'completed'}); its closing message follows as the settlement notice.`
|
|
124
|
-
|
|
125
|
-
const startedAt = Date.now()
|
|
126
|
-
const capMs = typeof args.timeout_ms === 'number' && args.timeout_ms > 0 ? args.timeout_ms : undefined
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* Wait for one child. Resolves with {kind:'settled',info} |
|
|
130
|
-
* {kind:'timedOut'} | {kind:'aborted'} | {kind:'notStarted'}. The
|
|
131
|
-
* per-id machinery (settled fast path, waiter registration, timeout
|
|
132
|
-
* timer, startup-grace poller) is the single-id logic of old,
|
|
133
|
-
* unchanged; Promise.all over ids gives the concurrent batch.
|
|
134
|
-
*/
|
|
135
|
-
const waitOne = (id) => {
|
|
136
|
-
// Fast path: the child is not running and a settlement is already on
|
|
137
|
-
// record — resolve immediately (the notice was already delivered when
|
|
138
|
-
// the child settled, so it is in the conversation history).
|
|
139
|
-
if (statusOf(id) !== 'running') {
|
|
140
|
-
const hit = settled.get(id)
|
|
141
|
-
if (hit !== undefined) return Promise.resolve({ kind: 'settled', info: hit.info })
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
return new Promise((resolve) => {
|
|
145
|
-
let done = false
|
|
146
|
-
let timer
|
|
147
|
-
let poller
|
|
148
|
-
const finish = (value) => {
|
|
149
|
-
if (done) return
|
|
150
|
-
done = true
|
|
151
|
-
if (poller !== undefined) clearInterval(poller)
|
|
152
|
-
const list = waiters.get(id)
|
|
153
|
-
if (list !== undefined) {
|
|
154
|
-
const index = list.indexOf(finish)
|
|
155
|
-
if (index >= 0) list.splice(index, 1)
|
|
156
|
-
if (list.length === 0) waiters.delete(id)
|
|
157
|
-
}
|
|
158
|
-
if (timer !== undefined) clearTimeout(timer)
|
|
159
|
-
if (exec.signal !== undefined) exec.signal.removeEventListener('abort', onAbort)
|
|
160
|
-
resolve(value)
|
|
161
|
-
}
|
|
162
|
-
const onAbort = () => finish({ kind: 'aborted' })
|
|
163
|
-
|
|
164
|
-
const list = waiters.get(id) ?? []
|
|
165
|
-
list.push(finish)
|
|
166
|
-
waiters.set(id, list)
|
|
167
|
-
// All ids share startedAt, so one capMs value is one shared window.
|
|
168
|
-
if (capMs !== undefined) timer = setTimeout(() => finish({ kind: 'timedOut' }), capMs)
|
|
169
|
-
if (exec.signal !== undefined) {
|
|
170
|
-
if (exec.signal.aborted) {
|
|
171
|
-
finish({ kind: 'aborted' })
|
|
172
|
-
return
|
|
173
|
-
}
|
|
174
|
-
exec.signal.addEventListener('abort', onAbort, { once: true })
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
// Startup grace: a freshly dispatched child sits between spawn and
|
|
178
|
-
// its first turn (status not `running`, no settlement yet). Keep the
|
|
179
|
-
// wait alive while it spins up; once `running`, only `subagent/end`
|
|
180
|
-
// (the listener above) resolves us. If the grace expires without the
|
|
181
|
-
// child ever starting or settling, fall back to a retryable note
|
|
182
|
-
// instead of the old misleading "is not running" early return.
|
|
183
|
-
poller = setInterval(() => {
|
|
184
|
-
if (done) return
|
|
185
|
-
const hit = settled.get(id)
|
|
186
|
-
if (hit !== undefined) {
|
|
187
|
-
finish({ kind: 'settled', info: hit.info })
|
|
188
|
-
return
|
|
189
|
-
}
|
|
190
|
-
if (statusOf(id) !== 'running' && Date.now() - startedAt >= START_GRACE_MS) {
|
|
191
|
-
finish({ kind: 'notStarted' })
|
|
192
|
-
}
|
|
193
|
-
}, POLL_MS)
|
|
194
|
-
|
|
195
|
-
// Re-check: the child may have settled between the fast path above
|
|
196
|
-
// and waiter registration. Only a settlement AFTER this wait started
|
|
197
|
-
// counts as a live result.
|
|
198
|
-
const hit = settled.get(id)
|
|
199
|
-
if (hit !== undefined && hit.time >= startedAt) finish({ kind: 'settled', info: hit.info })
|
|
200
|
-
})
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
const outcomes = await Promise.all(ids.map((id) => waitOne(id)))
|
|
204
|
-
|
|
205
|
-
// Every waiter shares one abort signal, so an interruption either
|
|
206
|
-
// aborts the whole batch or lands after some children already settled.
|
|
207
|
-
// Collapse the all-aborted case into one line; mixed cases keep
|
|
208
|
-
// per-id lines so the settled ones are not lost.
|
|
209
|
-
if (outcomes.length > 0 && outcomes.every((outcome) => outcome.kind === 'aborted')) {
|
|
210
|
-
return `wait for ${ids.length} subagent${ids.length === 1 ? '' : 's'} was aborted by user interruption; the children may still be running.`
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
const lines = outcomes.map((outcome, index) => {
|
|
214
|
-
const id = ids[index]
|
|
215
|
-
if (outcome.kind === 'aborted') return `wait for subagent ${id} was aborted by user interruption; the child may still be running.`
|
|
216
|
-
if (outcome.kind === 'timedOut') return `timed out waiting for subagent ${id}; it is still running. You will be notified when it finishes — you can wait_subagent it again or continue with other work.`
|
|
217
|
-
if (outcome.kind === 'notStarted') {
|
|
218
|
-
return `subagent ${id} has not started after ${Math.round(START_GRACE_MS / 1000)}s (status: ${statusOf(id)}). It may still be spinning up — call wait_subagent again, or continue with other work; you will be notified when it finishes.`
|
|
219
|
-
}
|
|
220
|
-
// Real settlement: short done line; the framework's settlement notice
|
|
221
|
-
// (already delivered to the parent inbox before `subagent/end`)
|
|
222
|
-
// carries the closing message — nothing to consume or deduplicate.
|
|
223
|
-
return doneLine(id, outcome.info)
|
|
224
|
-
})
|
|
225
|
-
return lines.join('\n')
|
|
226
|
-
},
|
|
227
|
-
})
|
|
228
|
-
}
|
|
@@ -1,341 +0,0 @@
|
|
|
1
|
-
# dsh-apollo bundle patch: the `eng-noask` (工程模式(无提问)) agent preset —
|
|
2
|
-
# a copy of the `eng` plugins block with exactly one capability removed: the
|
|
3
|
-
# `tool-ask-user` row is gone (sessions expose no ask_user_question tool),
|
|
4
|
-
# plus a no-ask discipline line in the preset-local mode-instructions section
|
|
5
|
-
# and the plan-mode section's two ask_user_question mentions rewritten for a
|
|
6
|
-
# preset that cannot ask. Everything else matches `eng`;
|
|
7
|
-
# tests/upstream-drift.test.mjs fails when this block drifts from `eng`
|
|
8
|
-
# beyond the removed row. Keep the two in sync by editing `eng` first, then
|
|
9
|
-
# redoing the copy.
|
|
10
|
-
#
|
|
11
|
-
# Declared as one `@deepseek-ai/dsh-agent-preset` insert row (the DSH 0.2.1
|
|
12
|
-
# preset mechanism; the `<dshHome>/.agent-presets` scan root is gone).
|
|
13
|
-
# `config.id` MUST stay `eng-noask`: it is the preset identity historical
|
|
14
|
-
# sessions resume by. Preset-local plugin modules are referenced by bare
|
|
15
|
-
# package subpaths (`@menghuan1918/dsh-apollo/presets/eng-noask/plugins/...`) resolved
|
|
16
|
-
# from the profile directory's node_modules, not the preset directory.
|
|
17
|
-
|
|
18
|
-
- insert:
|
|
19
|
-
- id: preset-eng-noask
|
|
20
|
-
name: '@deepseek-ai/dsh-agent-preset'
|
|
21
|
-
config:
|
|
22
|
-
id: eng-noask
|
|
23
|
-
name: 工程模式(无提问)
|
|
24
|
-
description: >-
|
|
25
|
-
工程模式的无人值守变体:剔除 ask_user_question 工具,配无提问纪律——全程自主决策不提问,关键假设与替代方案写进最终报告。
|
|
26
|
-
order: 11
|
|
27
|
-
plugins:
|
|
28
|
-
#
|
|
29
|
-
# This plugins block is an AGENT-PLANE composition. The roster mounts it ONCE under a
|
|
30
|
-
# standing scope; every session naming it joins by scope parentage, so the
|
|
31
|
-
# tools and prompt sections registered here cover each joined agent while a
|
|
32
|
-
# session's own state stays keyed per Session/Agent inside the plugins. The
|
|
33
|
-
# host composition (`base.cordis.yml` + `web.cordis.yml`) keeps everything a
|
|
34
|
-
# preset must not own: the registries themselves, the sandbox and approval
|
|
35
|
-
# stack, persistence, and the model route.
|
|
36
|
-
#
|
|
37
|
-
# A service row here MUST sit inside a group carrying an `isolate` realm.
|
|
38
|
-
# Without one it publishes into the root realm, where it is process-global —
|
|
39
|
-
# another preset publishing the same name collides, and a host reader would
|
|
40
|
-
# resolve one preset's instance for every session; `dsh-agent-preset` rejects
|
|
41
|
-
# that at mount. `true` means an entry-local realm: this standing mount's own
|
|
42
|
-
# private instance, apart from every other preset's. (A shared label does NOT
|
|
43
|
-
# pool instances — `provide()` throws on the second registration under the
|
|
44
|
-
# same realm symbol; labels join REALMS, and are not what this file needs.)
|
|
45
|
-
|
|
46
|
-
# ── identity ────────────────────────────────────────────────────────────────
|
|
47
|
-
|
|
48
|
-
# The preset's own persona, shadowing the deployment default for this agent.
|
|
49
|
-
# One row on purpose: the rich engineering rules live in the user-global
|
|
50
|
-
# instructions (~/.dsh/AGENTS.md) and the preset's `eng:mode-instructions`
|
|
51
|
-
# section, not in the persona. `eng:mode-instructions` owns the delegation
|
|
52
|
-
# policy; the tiers stay global because they track local route config.
|
|
53
|
-
# 0.2.1's upstream `standard` preset carries `prefix` only: the working-
|
|
54
|
-
# directory `suffix` line it used to set is gone, and eng follows suit.
|
|
55
|
-
- id: persona
|
|
56
|
-
name: '@deepseek-ai/dsh-persona'
|
|
57
|
-
config:
|
|
58
|
-
prefix: >-
|
|
59
|
-
You are a coding agent powered by the {{model}} model.
|
|
60
|
-
|
|
61
|
-
# Workspace instructions (repository AGENTS.md files) are injected from the
|
|
62
|
-
# first request, capped at 64 KiB.
|
|
63
|
-
- id: agent-instructions
|
|
64
|
-
name: '@deepseek-ai/dsh-agent-instructions'
|
|
65
|
-
config:
|
|
66
|
-
maxBytes: 65536
|
|
67
|
-
|
|
68
|
-
# 0.2.1 standard adds the per-step time context (sampled current time,
|
|
69
|
-
# browser time zone and elapsed time) so date/time reasoning does not depend
|
|
70
|
-
# on the model's own clock. It consumes host services and provides nothing,
|
|
71
|
-
# so it needs no realm.
|
|
72
|
-
- id: time-context
|
|
73
|
-
name: '@deepseek-ai/dsh-time-context'
|
|
74
|
-
|
|
75
|
-
# ── shell ───────────────────────────────────────────────────────────────────
|
|
76
|
-
|
|
77
|
-
# `shell-env` stays in the HOST composition: `apps/cli/src/web.ts` injects it to
|
|
78
|
-
# publish `DSH_WEB_URL`/`DSH_WEB_MODE`, and a host row that injects a service is
|
|
79
|
-
# the criterion for host-plane ownership — injection resolves before any session
|
|
80
|
-
# exists, so there is no agent to key by. Behind a preset realm those variables
|
|
81
|
-
# never reached the model's shell at all. Both shell tools consume the host
|
|
82
|
-
# registry from here; their executors (`bash-sandbox`/`pwsh-sandbox`) are
|
|
83
|
-
# host-plane too.
|
|
84
|
-
- id: tool-bash
|
|
85
|
-
name: '@deepseek-ai/dsh-tool-bash'
|
|
86
|
-
disabled: !!js process.platform === 'win32'
|
|
87
|
-
|
|
88
|
-
- id: tool-pwsh
|
|
89
|
-
name: '@deepseek-ai/dsh-tool-pwsh'
|
|
90
|
-
disabled: !!js process.platform !== 'win32'
|
|
91
|
-
|
|
92
|
-
# ── filesystem ──────────────────────────────────────────────────────────────
|
|
93
|
-
|
|
94
|
-
# Both register into the host `tools` registry and provide nothing, so
|
|
95
|
-
# they need no realm. The `fs` service and its policy stay in the host.
|
|
96
|
-
- id: tool-fs
|
|
97
|
-
name: '@deepseek-ai/dsh-tool-fs'
|
|
98
|
-
|
|
99
|
-
- id: tool-fs-search
|
|
100
|
-
name: '@deepseek-ai/dsh-tool-fs-search'
|
|
101
|
-
config:
|
|
102
|
-
sampleOverCapGlobResults: false
|
|
103
|
-
|
|
104
|
-
# ── background jobs ────────────────────────────────────────────────────────
|
|
105
|
-
|
|
106
|
-
# Only the model-facing controls. The task REGISTRY stays on the host plane:
|
|
107
|
-
# its producers sit outside any realm this file could put it in — `tool-bash`
|
|
108
|
-
# above resolves it with `ctx.get`, and an entry-local realm here is invisible
|
|
109
|
-
# to every sibling row, so `run_in_background` would answer "background jobs
|
|
110
|
-
# unavailable" while these controls sat in the catalog. The registry is keyed by
|
|
111
|
-
# owning agent anyway, so one host instance serves every session. What a preset
|
|
112
|
-
# chooses is whether its agent can collect and stop background work at all.
|
|
113
|
-
#
|
|
114
|
-
# LOCAL OVERRIDE (2026-09-22): `maxConsecutiveWakes` is 5000 instead of the
|
|
115
|
-
# bundled 3. The budget is spent by every completion that opens a turn on an
|
|
116
|
-
# idle owner and is restored ONLY by a user-authored message, so a long
|
|
117
|
-
# unattended chain of background jobs parks after its third wake — from the
|
|
118
|
-
# fourth completion on, the notice degrades to injection and waits for the
|
|
119
|
-
# user's next input. This row and its config are copied verbatim from the
|
|
120
|
-
# eng patch; keep the two in sync (the upstream-drift test compares
|
|
121
|
-
# same-id row configs).
|
|
122
|
-
- id: tool-jobs
|
|
123
|
-
name: '@deepseek-ai/dsh-tool-jobs'
|
|
124
|
-
config:
|
|
125
|
-
maxConsecutiveWakes: 5000
|
|
126
|
-
|
|
127
|
-
# The automation-task tools (schedule_create/list/update/delete) are
|
|
128
|
-
# deliberately omitted from engineering mode: coding/infrastructure work
|
|
129
|
-
# does not need scheduled reminders. The host `schedule` service and the
|
|
130
|
-
# sidebar Automation page stay available to other presets.
|
|
131
|
-
|
|
132
|
-
# ── skills ──────────────────────────────────────────────────────────────────
|
|
133
|
-
|
|
134
|
-
# The skill REGISTRY lives in the host composition and is layered per scope:
|
|
135
|
-
# these rows register into THIS preset's layer of it, so they need no realm.
|
|
136
|
-
# `skill-filesystem` contributes local-root discovery for agents on this preset, and
|
|
137
|
-
# `tool-skill` gives them the catalog and loader; the merged catalog also
|
|
138
|
-
# carries whatever the deployment registered globally (repository plugins).
|
|
139
|
-
- id: skill-filesystem
|
|
140
|
-
name: '@deepseek-ai/dsh-skill-filesystem'
|
|
141
|
-
|
|
142
|
-
- id: tool-skill
|
|
143
|
-
name: '@deepseek-ai/dsh-tool-skill'
|
|
144
|
-
|
|
145
|
-
# ── goals ───────────────────────────────────────────────────────────────────
|
|
146
|
-
|
|
147
|
-
# Native command-goal/tool-goal rows are deliberately omitted. The goal
|
|
148
|
-
# domain is owned by the independent dsh-better-goal plugin row in the
|
|
149
|
-
# top-level patch: it registers one `goal` tool and one `/goal` command
|
|
150
|
-
# per Agent, hides any legacy goal tools a preset still carries, and has
|
|
151
|
-
# its own Plugins row for round limits and question timeouts. The two
|
|
152
|
-
# removed rows remain deliberate removals in tests/upstream-drift.test.mjs,
|
|
153
|
-
# which asserts they still exist upstream before filtering them out.
|
|
154
|
-
|
|
155
|
-
# ── plan mode ───────────────────────────────────────────────────────────────
|
|
156
|
-
|
|
157
|
-
# Plan state is per-agent by nature, so an entry-local realm is not a
|
|
158
|
-
# workaround here — it is the correct lifetime.
|
|
159
|
-
- id: planning
|
|
160
|
-
name: cordis:group
|
|
161
|
-
group: true
|
|
162
|
-
isolate:
|
|
163
|
-
planMode: true
|
|
164
|
-
config:
|
|
165
|
-
- id: plan-mode
|
|
166
|
-
name: '@deepseek-ai/dsh-plan-mode'
|
|
167
|
-
config:
|
|
168
|
-
section: |
|
|
169
|
-
You are in plan mode. Stay in plan mode until the user approves your plan through exit_plan_mode or switches the session mode. Imperative language to implement changes means plan the implementation, not execute it. A user's conversational agreement — including an answer confirming something you asked — approves nothing and does not end plan mode; fold the confirmed decision into the plan and submit it through exit_plan_mode.
|
|
170
|
-
|
|
171
|
-
Explore first. Use non-mutating reads, searches, static analysis, and checks to ground the plan in the actual repository. Do not edit or write files, change configuration, run formatters or code generation that rewrites tracked files, commit, or otherwise carry out the plan. Prefer existing functions and patterns over new machinery.
|
|
172
|
-
|
|
173
|
-
The tool catalog stays the same across modes for request-cache stability. These plan-mode rules override any later tool description or guidance that suggests using mutation tools; those tools remain listed to keep the tool catalog unchanged. Do not use todo_write to track this planning phase: it tracks implementation after an approved plan, while the plan itself belongs in exit_plan_mode.
|
|
174
|
-
|
|
175
|
-
Resolve discoverable facts by inspection. This preset exposes no ask-user tool: never wait on the user for a choice — decide autonomously, and record the decision, its key assumptions, and the alternatives you rejected in the plan so review can catch them. Do not ask the user where code lives or how current behavior works when you can find out.
|
|
176
|
-
|
|
177
|
-
Make the plan decision-complete: state the goal and success criteria; group implementation changes by subsystem; identify public API, schema, and data-flow changes; cover edge cases, failure modes, tests, acceptance criteria, and explicit assumptions. Keep it concise enough to review but detailed enough that another engineer can implement it without making design decisions.
|
|
178
|
-
|
|
179
|
-
When ready, call exit_plan_mode with the complete plan markdown, starting with a # title. Make exit_plan_mode the only and final tool call in that assistant response: it presents the plan for approval, and implementation begins only in a later step after approval. Do not paste the final plan as a plain reply or ask "should I proceed?" through prose. If review rejects it, incorporate the feedback and present again. If the review channel is unavailable or aborted, stay in plan mode and ask the user to switch modes manually; do not proceed with implementation.
|
|
180
|
-
|
|
181
|
-
# ── compaction ──────────────────────────────────────────────────────────────
|
|
182
|
-
|
|
183
|
-
# `compaction-basic` reads `toolResultPrune` through `ctx.get`, so the pruner must
|
|
184
|
-
# share this realm rather than sit outside it.
|
|
185
|
-
#
|
|
186
|
-
# `tokenMeter` is deliberately NOT in this realm: the meter stays on the HOST
|
|
187
|
-
# plane, and the rows here resolve that one instance. It takes no configuration,
|
|
188
|
-
# keys every fold by Session, and owns the context-meter projection units the
|
|
189
|
-
# browser reads for every session — behind a realm those units would come and go
|
|
190
|
-
# with whichever presets happen to be mounted. What a preset chooses is whether
|
|
191
|
-
# its agent compacts at all, which is `compaction-basic` below.
|
|
192
|
-
- id: compaction
|
|
193
|
-
name: cordis:group
|
|
194
|
-
group: true
|
|
195
|
-
isolate:
|
|
196
|
-
compaction: true
|
|
197
|
-
toolResultPruner: true
|
|
198
|
-
config:
|
|
199
|
-
- id: compaction-basic
|
|
200
|
-
name: '@deepseek-ai/dsh-compaction-basic'
|
|
201
|
-
|
|
202
|
-
- id: command-compact
|
|
203
|
-
name: '@deepseek-ai/dsh-command-compact'
|
|
204
|
-
|
|
205
|
-
- id: tool-result-pruner
|
|
206
|
-
name: '@deepseek-ai/dsh-compaction-tool-result-pruner'
|
|
207
|
-
config:
|
|
208
|
-
# Deliberately 2x the upstream `standard` preset (8192/4096/1024): eng
|
|
209
|
-
# sessions emit large build/test/log tool results, and the extra
|
|
210
|
-
# head/tail retention preserves the failure context the agent acts on.
|
|
211
|
-
thresholdChars: 16384
|
|
212
|
-
headChars: 8192
|
|
213
|
-
tailChars: 2048
|
|
214
|
-
|
|
215
|
-
# ── delegation and workflows ────────────────────────────────────────────────
|
|
216
|
-
|
|
217
|
-
# The `subagents` registry and its spawn/fork backends live in the HOST
|
|
218
|
-
# composition: the registry is a process singleton whose cross-session queries
|
|
219
|
-
# the api-proxy serves to the browser, and a provider name may only be
|
|
220
|
-
# registered once. This preset contributes the delegation TOOLS, which resolve
|
|
221
|
-
# that host registry.
|
|
222
|
-
#
|
|
223
|
-
# `workflows` is different — nothing outside an agent reads it — so every row
|
|
224
|
-
# that reaches it shares one entry-local realm here, and a consumer left
|
|
225
|
-
# outside would resolve a host registry this preset does not populate.
|
|
226
|
-
#
|
|
227
|
-
# 0.1.2-alpha.4 note: the one-way child `report` tool is gone — parent/child
|
|
228
|
-
# messaging now rides the globally named `send_message` registered by
|
|
229
|
-
# `tool-subagent-control` below (children inherit it too), so this preset
|
|
230
|
-
# mounts no messaging row of its own.
|
|
231
|
-
- id: delegation
|
|
232
|
-
name: cordis:group
|
|
233
|
-
group: true
|
|
234
|
-
isolate:
|
|
235
|
-
workflowEngine: true
|
|
236
|
-
config:
|
|
237
|
-
- id: tool-subagent-control
|
|
238
|
-
name: '@deepseek-ai/dsh-tool-subagent-control'
|
|
239
|
-
|
|
240
|
-
- id: tool-subagent-list-agents
|
|
241
|
-
name: '@deepseek-ai/dsh-tool-subagent-control/list-agents'
|
|
242
|
-
|
|
243
|
-
- id: tool-subagent
|
|
244
|
-
name: '@deepseek-ai/dsh-tool-subagent'
|
|
245
|
-
config:
|
|
246
|
-
provider: spawn
|
|
247
|
-
toolName: subagent
|
|
248
|
-
modelSelectionSettings: true
|
|
249
|
-
|
|
250
|
-
# Fork omits model selection so provider/model stay equal to the parent and
|
|
251
|
-
# the inherited history remains eligible for KV Cache reuse.
|
|
252
|
-
- id: tool-subagent-fork
|
|
253
|
-
name: '@deepseek-ai/dsh-tool-subagent'
|
|
254
|
-
config:
|
|
255
|
-
provider: fork
|
|
256
|
-
toolName: subagent_fork
|
|
257
|
-
|
|
258
|
-
# 0.1.6-alpha.2 renamed `dsh-workflow-worker-thread` to `dsh-workflow-ptc`
|
|
259
|
-
# (workflow orchestration in the shared sandboxed Node PTC runtime).
|
|
260
|
-
- id: workflow-ptc
|
|
261
|
-
name: '@deepseek-ai/dsh-workflow-ptc'
|
|
262
|
-
config:
|
|
263
|
-
provider: spawn
|
|
264
|
-
|
|
265
|
-
- id: tool-workflow
|
|
266
|
-
name: '@deepseek-ai/dsh-tool-workflow'
|
|
267
|
-
|
|
268
|
-
# ── remaining model-facing rows ─────────────────────────────────────────────
|
|
269
|
-
|
|
270
|
-
# No `tool-ask-user` here: this preset runs unattended. The no-ask discipline
|
|
271
|
-
# lives in the preset-local mode-instructions section.
|
|
272
|
-
|
|
273
|
-
- id: tool-todo
|
|
274
|
-
name: '@deepseek-ai/dsh-tool-todo'
|
|
275
|
-
config:
|
|
276
|
-
allowParallelInProgress: true
|
|
277
|
-
|
|
278
|
-
# The `web` service and its search/fetch providers stay in the host
|
|
279
|
-
# composition; only the model-facing tool is per-session. `fetch` reads the
|
|
280
|
-
# host-mounted SSRF-guarded `web-fetch-http` provider.
|
|
281
|
-
- id: tool-web
|
|
282
|
-
name: '@deepseek-ai/dsh-tool-web'
|
|
283
|
-
config:
|
|
284
|
-
fetch: true
|
|
285
|
-
searchTimeoutMs: 60000
|
|
286
|
-
|
|
287
|
-
# Declares deliverable files to the user (file-card surface). Upstream
|
|
288
|
-
# `standard` mounts it; eng is a fork-copy of that block, so this row has to be
|
|
289
|
-
# carried by hand — `tests/upstream-drift.test.mjs` fails when a shipped
|
|
290
|
-
# preset gains rows that eng never picked up.
|
|
291
|
-
- id: present
|
|
292
|
-
name: '@deepseek-ai/dsh-tool-present'
|
|
293
|
-
|
|
294
|
-
# 0.2.1 ships both plugin surfaces disabled by default; enable either only in a
|
|
295
|
-
# copy of this plugins block (edits saved from the Web editor override
|
|
296
|
-
# config.plugins by row id from the profile patch).
|
|
297
|
-
- id: tool-plugin-manager
|
|
298
|
-
name: '@deepseek-ai/dsh-plugin-manager/tools'
|
|
299
|
-
disabled: true
|
|
300
|
-
|
|
301
|
-
- id: tool-plugin-market
|
|
302
|
-
name: '@deepseek-ai/dsh-plugin-market/tools'
|
|
303
|
-
disabled: true
|
|
304
|
-
|
|
305
|
-
# ── subagent waiting ─────────────────────────────────────────────────────────
|
|
306
|
-
|
|
307
|
-
# Preset-local plugin: explicit wait for one continuable child to settle, so
|
|
308
|
-
# the agent can block on a specific subagent (pi-style await_task semantics)
|
|
309
|
-
# without giving up send_message continuations. The module specifier is a
|
|
310
|
-
# bare subpath of this package (@menghuan1918/dsh-apollo/presets/eng-noask/plugins/...):
|
|
311
|
-
# config.plugins names resolve from the profile directory's node_modules,
|
|
312
|
-
# where this package is installed or linked; it has zero bare imports on
|
|
313
|
-
# purpose.
|
|
314
|
-
- id: tool-wait-subagent
|
|
315
|
-
name: '@menghuan1918/dsh-apollo/presets/eng-noask/plugins/wait-subagent/index.js'
|
|
316
|
-
|
|
317
|
-
# ── exit guard ───────────────────────────────────────────────────────────────
|
|
318
|
-
#
|
|
319
|
-
# Preset-local plugin: pi-atlas 式退出守卫。回合结束(agent/turn-stopping)
|
|
320
|
-
# 时若有运行中的直接子代理或后台任务,注入 next-step 提醒(agent.steer)
|
|
321
|
-
# 强制回合继续,直到 wait_subagent / job_output / job_kill 处理完毕或用户
|
|
322
|
-
# 中断(被中断回合不触发)。开关为正式 Config.exitGuard,默认关闭,
|
|
323
|
-
# 每次事件读取当前值,设置保存后即时生效。监听器注册型
|
|
324
|
-
# 插件,无服务发布,无需 realm。
|
|
325
|
-
- id: eng-exit-guard
|
|
326
|
-
name: '@menghuan1918/dsh-apollo/presets/eng-noask/plugins/exit-guard/index.js'
|
|
327
|
-
|
|
328
|
-
# ── mode instructions ────────────────────────────────────────────────────────
|
|
329
|
-
|
|
330
|
-
# Preset-local prompt section carrying the two eng-only rules: the exit guard
|
|
331
|
-
# contract and the delegation/orchestration policy (the latter moved out of the
|
|
332
|
-
# user-global ~/.dsh/AGENTS.md on 2026-09-11 — the model tiers stay there
|
|
333
|
-
# because they mirror this host's `subagent-model-selection` route list).
|
|
334
|
-
# Registered as a system-prompt section (order 1, right after the persona slot)
|
|
335
|
-
# so it is scoped to sessions mounted on this preset and nothing else; injected
|
|
336
|
-
# from the first request alongside the other sections.
|
|
337
|
-
# This plugins block (presets/eng-noask.patch.yml) is the single source of truth;
|
|
338
|
-
# the 0.2.1 bundle-patch mechanism loads it straight from this file, with no
|
|
339
|
-
# boot-time copy in between.
|
|
340
|
-
- id: eng-mode-instructions
|
|
341
|
-
name: '@menghuan1918/dsh-apollo/presets/eng-noask/plugins/mode-instructions/index.js'
|