@lincyaw/dsh 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +22 -2
  3. package/build/client.mjs +66 -0
  4. package/build/dist/client.js +90 -0
  5. package/build/dist/client.json +70 -0
  6. package/build/ensure.mjs +30 -0
  7. package/cordis.patch.yml +17 -0
  8. package/package.json +95 -3
  9. package/profile.patch.yml +25 -0
  10. package/rows/context.mjs +7 -0
  11. package/rows/load.mjs +8 -0
  12. package/rows/policy.mjs +7 -0
  13. package/rows/routes.mjs +7 -0
  14. package/rows/service.mjs +7 -0
  15. package/rows/skills.mjs +7 -0
  16. package/rows/tools.mjs +7 -0
  17. package/rows/turns.mjs +7 -0
  18. package/rows/workbench.mjs +9 -0
  19. package/rows/workspaces.mjs +7 -0
  20. package/skills/presmium-author/SKILL.md +95 -0
  21. package/skills/presmium-author/references/assistance.md +27 -0
  22. package/skills/presmium-author/references/code.md +52 -0
  23. package/skills/presmium-author/references/directives.md +592 -0
  24. package/skills/presmium-author/references/publishing.md +60 -0
  25. package/skills/presmium-author/references/writing.md +72 -0
  26. package/skills/presmium-present/SKILL.md +69 -0
  27. package/skills/presmium-present/references/classroom.md +46 -0
  28. package/skills/presmium-present/references/delivery.md +63 -0
  29. package/skills/presmium-present/references/lecture.md +58 -0
  30. package/src/client/Frame.tsx +314 -0
  31. package/src/client/brand.tsx +90 -0
  32. package/src/client/chrome.tsx +79 -0
  33. package/src/client/contract.ts +35 -0
  34. package/src/client/dsh.d.ts +129 -0
  35. package/src/client/fallback.tsx +49 -0
  36. package/src/client/frame.css +345 -0
  37. package/src/client/index.tsx +154 -0
  38. package/src/client/layout.ts +217 -0
  39. package/src/client/messages.ts +78 -0
  40. package/src/client/source.tsx +81 -0
  41. package/src/client/styles.css +3 -0
  42. package/src/client/theme.ts +149 -0
  43. package/src/node/context.ts +55 -0
  44. package/src/node/dsh.ts +93 -0
  45. package/src/node/launch.ts +59 -0
  46. package/src/node/policy.ts +59 -0
  47. package/src/node/routes.ts +46 -0
  48. package/src/node/service.ts +280 -0
  49. package/src/node/skills.ts +46 -0
  50. package/src/node/tools.ts +43 -0
  51. package/src/node/turns/index.ts +165 -0
  52. package/src/node/watchdog.mjs +36 -0
  53. package/src/node/workbench.ts +43 -0
  54. package/src/node/workspaces.ts +10 -0
  55. package/web.patch.yml +190 -0
@@ -0,0 +1,165 @@
1
+ import { randomUUID } from 'node:crypto'
2
+ import type { DshAgent, DshContext, DshToolExecution } from '../dsh.ts'
3
+ import type { PresmiumSession } from '../service.ts'
4
+
5
+ export const name = 'presmium-turns'
6
+ export const inject = ['presmium', 'tools']
7
+
8
+ export const WRITES = new Set(['write', 'edit', 'str_replace_editor', 'bash', 'pwsh', 'terminal_send'])
9
+
10
+ const REVIEW = '/__presmium/presmium.workspace/review'
11
+ const RETRY_MS = 2000
12
+
13
+ interface LiveAgent extends DshAgent {
14
+ inject(message: unknown): void
15
+ }
16
+
17
+ interface SessionEvent {
18
+ type: string
19
+ data?: { turn?: number }
20
+ }
21
+
22
+ interface Reverted {
23
+ type: 'reverted'
24
+ session: string
25
+ turns?: string
26
+ revision: string
27
+ subject: string
28
+ revertedBy: string
29
+ files: string[]
30
+ }
31
+
32
+ interface Turns {
33
+ turn?: number
34
+ document?: PresmiumSession
35
+ wrote: boolean
36
+ }
37
+
38
+ function labelOf(exec: Readonly<DshToolExecution>): string {
39
+ const args = (exec.arguments ?? {}) as { file_path?: unknown, path?: unknown, command?: unknown, input?: unknown }
40
+ const what = [args.file_path, args.path, args.command, args.input].find(value => typeof value === 'string' && value.trim())
41
+ return what ? `${exec.name} ${String(what).replace(/\s+/g, ' ').trim().slice(0, 160)}` : exec.name
42
+ }
43
+
44
+ export function revertNotice(event: Reverted): unknown {
45
+ const short = (id: string) => id.slice(0, 10)
46
+ const files = event.files.length ? event.files.join(', ') : 'its files'
47
+ const turns = event.turns ? ` (turns ${event.turns})` : ''
48
+ const text = `[Presmium] The author undid the change this session made${turns}: commit ${short(event.revision)} "${event.subject}", undone by commit ${short(event.revertedBy)}. ${files} ${event.files.length === 1 ? 'is' : 'are'} back as before that change. Read a file again before you edit it, and do not make that change again unless the author asks.`
49
+ const summary = `Presmium: the author undid commit ${short(event.revision)}${turns}`
50
+ return Object.freeze({ id: randomUUID(), role: 'user', content: Object.freeze([Object.freeze({ type: 'text', text })]), source: Object.freeze({ kind: 'presmium', form: 'notice', summary: summary.slice(0, 120) }) })
51
+ }
52
+
53
+ export function apply(ctx: DshContext): void {
54
+ const log = ctx.logger?.('presmium') ?? console
55
+ const sessions = new Map<string, Turns>()
56
+ const agents = new Map<string, LiveAgent>()
57
+ const pending = new Map<string, unknown[]>()
58
+ const turnsOf = (id: string) => {
59
+ let known = sessions.get(id)
60
+ if (!known) sessions.set(id, known = { wrote: false })
61
+ return known
62
+ }
63
+ const parents = new Map<string, string>()
64
+ const reviewerOf = (id: string) => {
65
+ let at = id
66
+ for (let depth = 0; depth < 32 && parents.has(at); depth++) at = parents.get(at)!
67
+ return at
68
+ }
69
+
70
+ const post = async (presmium: PresmiumSession, session: string, path: string, body: Record<string, unknown>): Promise<void> => {
71
+ const response = await ctx.presmium.request(`${REVIEW}/${path}`, { body: { session, ...body }, headers: { 'presmium-document': encodeURIComponent(presmium.document.dir) } })
72
+ const text = await response.text()
73
+ if (response.ok) return
74
+ if (path === 'open' && response.status === 404) throw new Error('the workspace is not a git repository, so the author cannot review a change: run git init in it')
75
+ let reason = text
76
+ try {
77
+ reason = (JSON.parse(text) as { error?: string, detail?: string }).error ?? reason
78
+ } catch {
79
+ }
80
+ throw new Error(reason || `the review answered ${response.status}`)
81
+ }
82
+
83
+ ctx.on('session/event', (session: { id: string }, event: SessionEvent) => {
84
+ if (event.type === 'turn/start' && typeof event.data?.turn === 'number') turnsOf(session.id).turn = event.data.turn
85
+ if (event.type !== 'turn/end') return
86
+ const turns = sessions.get(session.id)
87
+ if (!turns?.wrote || !turns.document) return
88
+ turns.wrote = false
89
+ void post(turns.document, session.id, 'step', { ...turns.turn !== undefined ? { turn: turns.turn } : {}, end: true })
90
+ .catch((error: unknown) => log.warn(`presmium-turns: the end of turn ${turns.turn} of ${session.id} was not told to the review: ${error instanceof Error ? error.message : String(error)}`))
91
+ })
92
+
93
+ ctx.on('tools/execute', async (exec: DshToolExecution, next: () => Promise<unknown>) => {
94
+ const presmium = WRITES.has(exec.name) ? ctx.presmium.session(exec.agent) : undefined
95
+ if (!presmium) return next()
96
+ const session = reviewerOf(presmium.id)
97
+ const turns = turnsOf(session)
98
+ try {
99
+ await post(presmium, session, 'open', turns.turn !== undefined ? { turn: turns.turn } : {})
100
+ } catch (error) {
101
+ throw new Error(`Presmium: ${error instanceof Error ? error.message : String(error)}. Nothing was changed.`)
102
+ }
103
+ turns.document = presmium
104
+ turns.wrote = true
105
+ try {
106
+ return await next()
107
+ } finally {
108
+ await post(presmium, session, 'step', { ...turns.turn !== undefined ? { turn: turns.turn } : {}, label: labelOf(exec), tool: exec.name })
109
+ .catch((error: unknown) => log.warn(`presmium-turns: no checkpoint after ${exec.name} in ${session}: ${error instanceof Error ? error.message : String(error)}`))
110
+ }
111
+ })
112
+
113
+ ctx.on('agent/created', ({ agent }: { agent: LiveAgent }) => {
114
+ const header = agent.session.header as { origin?: string, parentSession?: string } | undefined
115
+ if (header?.origin === 'subagent' && header.parentSession) parents.set(agent.session.id, header.parentSession)
116
+ agents.set(agent.session.id, agent)
117
+ for (const notice of pending.get(agent.session.id) ?? []) agent.inject(notice)
118
+ pending.delete(agent.session.id)
119
+ })
120
+ ctx.on('agent/disposed', ({ agent }: { agent: LiveAgent }) => {
121
+ if (agents.get(agent.session.id) === agent) agents.delete(agent.session.id)
122
+ })
123
+ ctx.on('session/disposed', (session: { id: string }) => {
124
+ sessions.delete(session.id)
125
+ parents.delete(session.id)
126
+ })
127
+
128
+ const tell = (event: Reverted) => {
129
+ const notice = revertNotice(event)
130
+ const agent = agents.get(event.session)
131
+ if (agent) agent.inject(notice)
132
+ else pending.set(event.session, [...pending.get(event.session) ?? [], notice])
133
+ log.info(`presmium-turns: told session ${event.session} that commit ${event.revision} was undone`)
134
+ }
135
+
136
+ const stop = new AbortController()
137
+ const follow = async () => {
138
+ while (!stop.signal.aborted) {
139
+ try {
140
+ const response = await ctx.presmium.request(`${REVIEW}/events`, { headers: { accept: 'text/event-stream' }, signal: stop.signal })
141
+ if (!response.ok || !response.body) throw new Error(`review/events answered ${response.status}`)
142
+ const decoder = new TextDecoder()
143
+ let buffer = ''
144
+ for await (const chunk of response.body) {
145
+ buffer += decoder.decode(chunk as Uint8Array, { stream: true })
146
+ let end: number
147
+ while ((end = buffer.indexOf('\n\n')) >= 0) {
148
+ const block = buffer.slice(0, end)
149
+ buffer = buffer.slice(end + 2)
150
+ const data = block.split('\n').filter(line => line.startsWith('data: ')).map(line => line.slice(6)).join('\n')
151
+ if (!data) continue
152
+ const event = JSON.parse(data) as { type?: string }
153
+ if (event.type === 'reverted') tell(event as Reverted)
154
+ }
155
+ }
156
+ } catch (error) {
157
+ if (stop.signal.aborted) return
158
+ log.debug(`presmium-turns: the review events: ${error instanceof Error ? error.message : String(error)}`)
159
+ }
160
+ await new Promise(done => setTimeout(done, RETRY_MS).unref())
161
+ }
162
+ }
163
+ void follow()
164
+ ctx.effect(() => () => stop.abort())
165
+ }
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env node
2
+ import { spawn } from 'node:child_process'
3
+
4
+ const GRACE_MS = 5000
5
+
6
+ const [command, ...args] = process.argv.slice(2)
7
+ if (!command) {
8
+ process.stderr.write('usage: watchdog.mjs <command> [args...]\n')
9
+ process.exit(2)
10
+ }
11
+
12
+ const group = (signal) => {
13
+ try {
14
+ process.kill(-process.pid, signal)
15
+ } catch {
16
+ }
17
+ }
18
+
19
+ const child = spawn(command, args, { stdio: ['ignore', 'inherit', 'inherit'] })
20
+ let ending = false
21
+ const end = () => {
22
+ if (ending) return
23
+ ending = true
24
+ group('SIGTERM')
25
+ setTimeout(() => group('SIGKILL'), GRACE_MS).unref()
26
+ }
27
+
28
+ process.on('SIGTERM', end)
29
+ process.on('SIGINT', end)
30
+ process.stdin.on('end', end).on('close', end).on('error', end)
31
+ process.stdin.resume()
32
+ child.on('error', (error) => {
33
+ process.stderr.write(`watchdog: ${error.message}\n`)
34
+ process.exit(1)
35
+ })
36
+ child.on('exit', () => group('SIGKILL'))
@@ -0,0 +1,43 @@
1
+ import type { DshContext } from './dsh.ts'
2
+
3
+ export const name = 'presmium-workbench'
4
+ export const inject = ['presmium', 'connection', 'webServer']
5
+
6
+ const ICON = `data:image/svg+xml,${encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><rect width="32" height="32" rx="7" fill="#0F5F5C"/><path d="M11 8h7.5a6.5 6.5 0 0 1 0 13H15v4h-4zM15 12v5h3.5a2.5 2.5 0 0 0 0-5z" fill="#F7F5F0" fill-rule="evenodd"/></svg>')}`
7
+
8
+ export function brandIndex(html: string): string {
9
+ return html
10
+ .replace(/<title>[^<]*<\/title>/, '<title>Presmium</title>')
11
+ .replace(/<link rel="(?:icon|manifest)"[^>]*>\s*/g, '')
12
+ .replace('</head>', `<link rel="icon" type="image/svg+xml" href="${ICON}" />\n</head>`)
13
+ }
14
+
15
+ interface WebServer {
16
+ tapIndex(transform: (html: string) => string): () => void
17
+ }
18
+
19
+ export function apply(ctx: DshContext & { webServer: WebServer }): void {
20
+ ctx.effect(() => ctx.webServer.tapIndex(brandIndex))
21
+ ctx.connection.fetch.register({
22
+ path: '/api/presmium/context', methods: ['POST'], requestBody: 'buffered',
23
+ async fetch(request) {
24
+ const body = await request.json().catch(() => null) as { session?: unknown, document?: unknown } | null
25
+ if (!body || typeof body.session !== 'string' || !body.session || (body.document !== null && typeof body.document !== 'string')) return new Response('Invalid context', { status: 400 })
26
+ try {
27
+ await ctx.presmium.selectDocument(body.session, body.document)
28
+ return Response.json({ session: body.session, document: body.document })
29
+ } catch {
30
+ return new Response('Unknown document', { status: 400 })
31
+ }
32
+ },
33
+ })
34
+ ctx.connection.fetch.register({
35
+ path: '/api/presmium/workbench',
36
+ methods: ['GET'],
37
+ requestBody: 'buffered',
38
+ async fetch() {
39
+ const { url, root } = await ctx.presmium.ready
40
+ return new Response(JSON.stringify({ mcp: `${url}/mcp`, root }), { headers: { 'Content-Type': 'application/json' } })
41
+ },
42
+ })
43
+ }
@@ -0,0 +1,10 @@
1
+ import { basename } from 'node:path'
2
+ import type { DshContext } from './dsh.ts'
3
+
4
+ export const name = 'presmium-workspaces'
5
+ export const inject = ['presmium', 'workspaceRegistry']
6
+
7
+ export async function apply(ctx: DshContext): Promise<void> {
8
+ const { root } = await ctx.presmium.ready
9
+ await ctx.workspaceRegistry.create(root, basename(root))
10
+ }
package/web.patch.yml ADDED
@@ -0,0 +1,190 @@
1
+ - insert:
2
+ - id: presmium-workspaces
3
+ name: '@lincyaw/dsh/workspaces'
4
+ - id: presmium-routes
5
+ name: '@lincyaw/dsh/routes'
6
+
7
+ - id: workspace-controller
8
+ config:
9
+ documentsDirectory: !!js (process.env.DSH_HOME || `${process.env.XDG_STATE_HOME || `${process.env.HOME}/.local/state`}/presmium/studio`) + '/documents'
10
+
11
+ - id: harness-dev-context
12
+ disabled: true
13
+
14
+ - insert:
15
+ - id: presmium-workbench
16
+ name: '@lincyaw/dsh'
17
+
18
+ - id: ui-layout
19
+ disabled: true
20
+ - id: ui-sidebar
21
+ disabled: true
22
+ - id: ui-brand-official
23
+ disabled: true
24
+ - id: ui-settings-account
25
+ disabled: true
26
+ - id: account-controller
27
+ disabled: true
28
+ - id: deepseek-account
29
+ disabled: true
30
+ - id: llm-deepseek-account
31
+ disabled: true
32
+ - id: web-search-deepseek
33
+ disabled: true
34
+ - id: ui-settings-web-search
35
+ disabled: true
36
+ - id: ui-settings-session-log
37
+ disabled: true
38
+
39
+ # 内置的「创作」预设:Presmium 的作者人设与发布会原则。预设的 persona 遮蔽部署级
40
+ # 的人设(profile.patch.yml),所以这里重述 Presmium 的角色。
41
+ - insert:
42
+ - id: preset-authoring
43
+ name: '@deepseek-ai/dsh-agent-preset'
44
+ config:
45
+ id: authoring
46
+ name: 创作
47
+ description: 面向受众的讲义与演示创作:信息量优先、受众视角、版式不重复。
48
+ order: 0
49
+ plugins:
50
+ - id: persona
51
+ name: '@deepseek-ai/dsh-persona'
52
+ config:
53
+ prefix: |
54
+ You are Presmium's assistant for the authors of lectures, powered by the {{model}} model. You help write and revise lecture documents (MyST Markdown that Presmium renders as a web page, slides and a notebook), run their code and check them, in the author's language.
55
+
56
+ 创作或编辑面向受众的内容(讲义、幻灯片、文档、文章及其他展示材料)时,你全程遵守发布会原则:以核心观点和受众价值为中心组织内容,并根据呈现载体调整表达方式。
57
+
58
+ **最高优先级:信息量原则**
59
+
60
+ 每一句话都必须有明确的存在意义,对受众理解内容、形成判断或采取行动发挥具体作用。每页、每屏或每个独立内容区块都必须承载有效信息,并承担明确的说明或论证任务。句子、注释、例子、数据和重复说明,都应具有明确作用;删除后不影响受众理解或判断的内容应直接删除。叙事安排、版式变化与语言润色均须服从这一原则,不得为满足形式要求而添加无效内容。
61
+
62
+ 1. 围绕最有价值、最有说服力的观点或优势组织叙事,使全部内容共同支撑核心主张。
63
+ 2. 按照受众理解问题的逻辑安排内容,避免按个人思考、工作开展或方案尝试的过程罗列经历。
64
+ 3. 避免“只是一些浅见”“可能没什么价值”等自我削弱表达,不主动贬低内容的价值。
65
+ 4. 根据内容目的与受众需求选择讨论角度、评价维度和比较口径,集中呈现能够充分证明核心价值的内容。
66
+ 5. 涉及不同观点、方法或结果时,结合各自目标、适用条件与合理取舍解释差异,避免简单作出优劣判断。
67
+ 6. 明确说明核心观点为什么重要、依据是什么,以及能够帮助受众理解或解决什么问题,不依赖受众自行从文字、表格或图形中推导内容价值。
68
+ 7. 对于采用分页、分屏或区块布局的材料,相同的排版结构不得连续出现;任意连续五页、五屏或五个主要展示区块内不得重复使用相同的排版结构。
69
+ 8. 出现排版结构重复时,应根据内容关系重新组织信息,选择适合的流程、对照、时间线、图表或图文组合等呈现方式,避免通过更换颜色、图标或文字维持相同布局。
70
+ 9. 对于幻灯片,单页文字过多时,应优先采用图形化表达,根据内容关系改用流程图、关系图、时间线、数据图表或图文组合,减少连续文字并保留必要信息。图形应承担信息表达和关系说明的作用,避免将整段文字直接放入形状中。
71
+ 10. 开头应尽早呈现受众关心的问题、值得讨论的关键、核心观点及最有说服力的依据。
72
+ 11. 结尾应强化核心观点与最值得记住的信息,避免突然改变立场或加入自我否定的评价。
73
+ - id: agent-instructions
74
+ name: '@deepseek-ai/dsh-agent-instructions'
75
+ config:
76
+ maxBytes: 65536
77
+ - id: time-context
78
+ name: '@deepseek-ai/dsh-time-context'
79
+ - id: tool-bash
80
+ name: '@deepseek-ai/dsh-tool-bash'
81
+ disabled: !!js process.platform === 'win32'
82
+ - id: tool-pwsh
83
+ name: '@deepseek-ai/dsh-tool-pwsh'
84
+ disabled: !!js process.platform !== 'win32'
85
+ - id: tool-fs
86
+ name: '@deepseek-ai/dsh-tool-fs'
87
+ - id: tool-fs-search
88
+ name: '@deepseek-ai/dsh-tool-fs-search'
89
+ config:
90
+ sampleOverCapGlobResults: false
91
+ - id: tool-jobs
92
+ name: '@deepseek-ai/dsh-tool-jobs'
93
+ - id: tool-schedule
94
+ name: '@deepseek-ai/dsh-tool-schedule'
95
+ - id: skill-filesystem
96
+ name: '@deepseek-ai/dsh-skill-filesystem'
97
+ - id: tool-skill
98
+ name: '@deepseek-ai/dsh-tool-skill'
99
+ - id: command-goal
100
+ name: '@deepseek-ai/dsh-command-goal'
101
+ - id: tool-goal
102
+ name: '@deepseek-ai/dsh-tool-goal'
103
+ - id: planning
104
+ name: cordis:group
105
+ group: true
106
+ isolate:
107
+ planMode: true
108
+ config:
109
+ - id: plan-mode
110
+ name: '@deepseek-ai/dsh-plan-mode'
111
+ config:
112
+ section: |
113
+ 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.
114
+
115
+ 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.
116
+
117
+ 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.
118
+
119
+ Resolve discoverable facts by inspection. Use ask_user_question only for user-owned choices or material ambiguity that inspection cannot answer. Do not ask the user where code lives or how current behavior works when you can find out.
120
+
121
+ 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.
122
+
123
+ 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 or ask_user_question. 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.
124
+ - id: compaction
125
+ name: cordis:group
126
+ group: true
127
+ isolate:
128
+ compaction: true
129
+ toolResultPruner: true
130
+ config:
131
+ - id: compaction-basic
132
+ name: '@deepseek-ai/dsh-compaction-basic'
133
+ - id: command-compact
134
+ name: '@deepseek-ai/dsh-command-compact'
135
+ - id: tool-result-pruner
136
+ name: '@deepseek-ai/dsh-compaction-tool-result-pruner'
137
+ config:
138
+ thresholdChars: 8192
139
+ headChars: 4096
140
+ tailChars: 1024
141
+ - id: delegation
142
+ name: cordis:group
143
+ group: true
144
+ isolate:
145
+ workflowEngine: true
146
+ config:
147
+ - id: tool-subagent-control
148
+ name: '@deepseek-ai/dsh-tool-subagent-control'
149
+ - id: tool-subagent-list-agents
150
+ name: '@deepseek-ai/dsh-tool-subagent-control/list-agents'
151
+ - id: tool-subagent
152
+ name: '@deepseek-ai/dsh-tool-subagent'
153
+ config:
154
+ provider: spawn
155
+ toolName: subagent
156
+ modelSelectionSettings: true
157
+ - id: tool-subagent-fork
158
+ name: '@deepseek-ai/dsh-tool-subagent'
159
+ config:
160
+ provider: fork
161
+ toolName: subagent_fork
162
+ - id: workflow-ptc
163
+ name: '@deepseek-ai/dsh-workflow-ptc'
164
+ config:
165
+ provider: spawn
166
+ - id: tool-workflow
167
+ name: '@deepseek-ai/dsh-tool-workflow'
168
+ - id: tool-ask-user
169
+ name: '@deepseek-ai/dsh-tool-ask-user'
170
+ - id: tool-todo
171
+ name: '@deepseek-ai/dsh-tool-todo'
172
+ config:
173
+ allowParallelInProgress: true
174
+ - id: tool-web
175
+ name: '@deepseek-ai/dsh-tool-web'
176
+ config:
177
+ fetch: true
178
+ searchTimeoutMs: 60000
179
+ - id: present
180
+ name: '@deepseek-ai/dsh-tool-present'
181
+ - id: tool-plugin-manager
182
+ name: '@deepseek-ai/dsh-plugin-manager/tools'
183
+ disabled: true
184
+ - id: tool-plugin-market
185
+ name: '@deepseek-ai/dsh-plugin-market/tools'
186
+ disabled: true
187
+
188
+ - id: agent-preset-registry
189
+ config:
190
+ default: authoring