@dsh-plus/agent-preset-rea 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.
- package/lib/client.js +637 -0
- package/lib/index.d.ts +145 -0
- package/lib/index.js +422 -0
- package/package.json +60 -3
- package/skills/reverse-engineer-anything/LICENSE +21 -0
- package/skills/reverse-engineer-anything/SKILL.md +342 -0
- package/skills/reverse-engineer-anything/references/android-applications.md +34 -0
- package/skills/reverse-engineer-anything/references/connection-and-recovery.md +127 -0
- package/skills/reverse-engineer-anything/references/evidence-workflows.md +71 -0
- package/skills/reverse-engineer-anything/references/javascript-applications.md +175 -0
- package/skills/reverse-engineer-anything/references/native-and-artifacts.md +277 -0
- package/skills/reverse-engineer-anything/references/runtime-observation.md +78 -0
- package/src/client/card.tsx +120 -0
- package/src/client/client.ts +51 -0
- package/src/client/draft.ts +28 -0
- package/src/client/i18n.ts +43 -0
- package/src/client/styles.ts +32 -0
- package/src/config.ts +110 -0
- package/src/definition.ts +188 -0
- package/src/index.ts +95 -0
- package/src/mcp.ts +42 -0
- package/src/ns.ts +9 -0
- package/src/prompt.ts +25 -0
- package/src/registration.ts +86 -0
- package/src/skill.ts +24 -0
- package/README.md +0 -3
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 浏览器半入口:注册 locale 字典 + 注册「逆向工程预设」配置卡片
|
|
3
|
+
* (injectPluginConfigCard 两槽位:插件页 plugins.row.config / plugins.bundle.config)。
|
|
4
|
+
* 构建产物为 window.__ModuleLoader__.load({id, factory}) 形式的 CJS factory
|
|
5
|
+
* (包装见 tsdown.config.ts);样式沿用官方 data-plugin-css 约定,HMR 据此卸载。
|
|
6
|
+
*
|
|
7
|
+
* 卡片只编辑 `personaPrefix`(宿主半唯一的活动字段);配置读写经共享层直连
|
|
8
|
+
* `ctx.remote.settings`(官方 `settingsScope` 服务不存在、由 `configForms` 取代),
|
|
9
|
+
* 与 notify-email 同机制。
|
|
10
|
+
* @module @dsh-plus/agent-preset-rea/client
|
|
11
|
+
*/
|
|
12
|
+
import type { Context } from '@deepseek-ai/cordis'
|
|
13
|
+
import {
|
|
14
|
+
createNamespaceApi,
|
|
15
|
+
createSettingsScope,
|
|
16
|
+
injectPluginConfigCard,
|
|
17
|
+
type PluginClientContext,
|
|
18
|
+
} from '@dsh-plus/shared/client'
|
|
19
|
+
import { SETTINGS_NS } from '../ns.ts'
|
|
20
|
+
import { ReaCard } from './card.tsx'
|
|
21
|
+
import { type DictKey, en, NS, zh } from './i18n.ts'
|
|
22
|
+
import { injectStyle, PLUGIN_ID } from './styles.ts'
|
|
23
|
+
|
|
24
|
+
export const name = 'dsh-plus-agent-preset-rea'
|
|
25
|
+
|
|
26
|
+
/** 浏览器半需要的 cordis 服务 key(loader 据此注入;package.json 的 dsh.client.inject 管包加载顺序)。 */
|
|
27
|
+
export const inject = ['slots', 'locale', 'remote', 'remote.settings'] as const
|
|
28
|
+
|
|
29
|
+
/** 宿主窄面收编在 @dsh-plus/shared/client;TKey 传入本包 DictKey 使键名受检。 */
|
|
30
|
+
type ClientContext = PluginClientContext<DictKey>
|
|
31
|
+
|
|
32
|
+
export function apply(ctx: Context): void {
|
|
33
|
+
const c = ctx as unknown as ClientContext
|
|
34
|
+
const tag = injectStyle()
|
|
35
|
+
c.effect(
|
|
36
|
+
() => () => {
|
|
37
|
+
tag?.remove()
|
|
38
|
+
},
|
|
39
|
+
'agent-preset-rea: style',
|
|
40
|
+
)
|
|
41
|
+
c.effect(() => c.locale.register(NS, { zh, en }), 'agent-preset-rea: locale')
|
|
42
|
+
const scope = createSettingsScope(c, SETTINGS_NS, 'agent-preset-rea: settings scope')
|
|
43
|
+
const api = createNamespaceApi(c.get('remote').settings, SETTINGS_NS)
|
|
44
|
+
injectPluginConfigCard(c.slots, {
|
|
45
|
+
ns: SETTINGS_NS,
|
|
46
|
+
rowId: 'dsh-plus-agent-preset-rea',
|
|
47
|
+
pkg: PLUGIN_ID,
|
|
48
|
+
component: ReaCard,
|
|
49
|
+
inject: () => ({ t: c.locale.bind(NS), scope, api }),
|
|
50
|
+
})
|
|
51
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 卡片草稿的纯逻辑(无 JSX,node --test 可直接测):命名空间解析值 ⇄ 编辑草稿。
|
|
3
|
+
*
|
|
4
|
+
* 命名空间值是整行配置的脱敏视图(本卡片只编辑 `personaPrefix`,其余字段属
|
|
5
|
+
* 部署方平面,不进卡片),因此读取时按字段收窄:值缺席或类型不符一律回落到
|
|
6
|
+
* 空串(= 空 persona),保存时只回写这一个字段(settings.update 深合并)。
|
|
7
|
+
* @module agent-preset-rea/client/draft
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/** 命名空间解析值中本卡片消费的部分。 */
|
|
11
|
+
export interface ConfigValue {
|
|
12
|
+
personaPrefix?: unknown
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** 编辑草稿。 */
|
|
16
|
+
export interface Draft {
|
|
17
|
+
personaPrefix: string
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** 解析值 → 草稿(非字符串一律视为空串)。 */
|
|
21
|
+
export function draftFromValue(value: ConfigValue): Draft {
|
|
22
|
+
return { personaPrefix: typeof value.personaPrefix === 'string' ? value.personaPrefix : '' }
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** 草稿 → 提交形状(只回写本卡片负责的字段)。 */
|
|
26
|
+
export function toPatch(draft: Draft): Record<string, unknown> {
|
|
27
|
+
return { personaPrefix: draft.personaPrefix }
|
|
28
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 配置卡片文案(zh/en)。经 ctx.locale.register 注册、bind 取用,与官方卡片同机制。
|
|
3
|
+
* 公共键(save/discard/unsaved 等)来自 shared 的 common 字典,本文件只维护业务键。
|
|
4
|
+
* @module @dsh-plus/agent-preset-rea/client/i18n
|
|
5
|
+
*/
|
|
6
|
+
import { commonEn, commonZh, mergeDict } from '@dsh-plus/shared/client'
|
|
7
|
+
|
|
8
|
+
export const NS = 'dsh-plus-agent-preset-rea'
|
|
9
|
+
|
|
10
|
+
const ownZh = {
|
|
11
|
+
title: '逆向工程预设',
|
|
12
|
+
description: 'REA 提示词:留空即空 persona,工具指引与运行时上下文照常由官方装配注入。',
|
|
13
|
+
summaryLine: '编辑「逆向工程」预设的 persona 提示词',
|
|
14
|
+
prompt: '系统提示词(persona 前缀)',
|
|
15
|
+
promptHint:
|
|
16
|
+
'写入 system-prompt 的 persona 前缀段,仅对「逆向工程」预设的新会话生效;留空 = 不注入 persona 段(官方工具指引、项目指令与运行时上下文不受影响)。保存后新建会话即用新值。',
|
|
17
|
+
promptPlaceholder: '例如:先做证据分级,再动手改二进制;每一步给出可复核的证据链……',
|
|
18
|
+
promptEmpty: '当前为空:该预设不注入 persona 段。',
|
|
19
|
+
saveFailed: '保存失败:',
|
|
20
|
+
} as const
|
|
21
|
+
|
|
22
|
+
const ownEn = {
|
|
23
|
+
title: 'Reverse-engineering preset',
|
|
24
|
+
description:
|
|
25
|
+
'REA prompt: leave it empty for a blank persona; official tool guidance and runtime context stay in place.',
|
|
26
|
+
summaryLine: 'Edit the persona prompt of the “逆向工程” preset',
|
|
27
|
+
prompt: 'System prompt (persona prefix)',
|
|
28
|
+
promptHint:
|
|
29
|
+
'Written to the persona-prefix section of the system prompt for new sessions of this preset only. Empty means no persona section (official tool guidance, project instructions and runtime context are unaffected). New sessions pick up the saved value.',
|
|
30
|
+
promptPlaceholder:
|
|
31
|
+
'e.g. Grade the evidence first, then patch the binary; every step needs a checkable chain of evidence…',
|
|
32
|
+
promptEmpty: 'Currently empty: this preset injects no persona section.',
|
|
33
|
+
saveFailed: 'Save failed: ',
|
|
34
|
+
} as const
|
|
35
|
+
|
|
36
|
+
export type DictKey = keyof typeof commonZh | keyof typeof ownZh
|
|
37
|
+
|
|
38
|
+
/** 卡片共用的翻译函数类型(slot 注入的 bind 结果按此消费)。 */
|
|
39
|
+
export type Translate = (key: DictKey) => string
|
|
40
|
+
|
|
41
|
+
// 标注为 Record<DictKey, string>:en 缺任一键即编译期报错(中英强制对齐)。
|
|
42
|
+
export const zh: Record<DictKey, string> = mergeDict(commonZh, ownZh)
|
|
43
|
+
export const en: Record<DictKey, string> = mergeDict(commonEn, ownEn)
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 配置卡片样式:基础规则走 @dsh-plus/shared/client 的 cardCss('apr'),
|
|
3
|
+
* 本文件只补插件特有规则(提示词多行输入框)。
|
|
4
|
+
* 沿用官方 data-plugin / data-plugin-css 约定(HMR 据此卸载)。
|
|
5
|
+
* @module agent-preset-rea/client/styles
|
|
6
|
+
*/
|
|
7
|
+
import { cardCss, injectCardStyle } from '@dsh-plus/shared/client'
|
|
8
|
+
|
|
9
|
+
export const PLUGIN_ID = '@dsh-plus/agent-preset-rea'
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* 多行提示词输入框:度量对齐官方 settings-form 的 textarea
|
|
13
|
+
* (与 -input 同边框/底色/圆角,正文 13px/1.6),窄屏 16px 防 iOS 聚焦缩放、
|
|
14
|
+
* 纵向可拖拽但不横向溢出。
|
|
15
|
+
*/
|
|
16
|
+
const extraCss = `
|
|
17
|
+
.apr-textarea{box-sizing:border-box;width:100%;min-height:180px;resize:vertical;font:inherit;font-size:13px;line-height:1.6;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-3);border:1px solid var(--dsw-alias-border-l2);border-radius:8px;padding:10px 12px;transition:border-color .18s,background .18s}
|
|
18
|
+
.apr-textarea:hover:not(:disabled):not(:focus-visible){border-color:var(--dsw-alias-label-dimmed)}
|
|
19
|
+
.apr-textarea:focus-visible{border-color:var(--dsw-alias-brand-primary);outline:none}
|
|
20
|
+
.apr-textarea:disabled{color:var(--dsw-alias-label-tertiary);cursor:default}
|
|
21
|
+
.apr-textarea::placeholder{color:var(--dsw-alias-label-tertiary)}
|
|
22
|
+
@media (max-width:767px){
|
|
23
|
+
.apr-textarea{font-size:16px;min-height:140px}
|
|
24
|
+
}
|
|
25
|
+
`
|
|
26
|
+
|
|
27
|
+
export const cardCssAll = cardCss('apr', extraCss)
|
|
28
|
+
|
|
29
|
+
/** 幂等注入样式标签;返回标签(已存在或环境无 document 时为 null)。 */
|
|
30
|
+
export function injectStyle(): HTMLStyleElement | null {
|
|
31
|
+
return injectCardStyle(PLUGIN_ID, cardCssAll)
|
|
32
|
+
}
|
package/src/config.ts
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 插件配置(schemastery):显示面、persona、REA MCP 行与 skill 根外部化。
|
|
3
|
+
*
|
|
4
|
+
* 单一事实源同时充当两处:cordis 行级 `Config`(组合默认,profile patch 层可覆盖)
|
|
5
|
+
* 与 settings 用户层(GUI 配置卡片写入 profile 行级覆盖层,loader 原位提交活动
|
|
6
|
+
* 引用,消费端 `unwrapVolatile` 现取即热)。
|
|
7
|
+
*
|
|
8
|
+
* **只有 `personaPrefix` 标 `.volatile()`**:它是对用户的承诺(配置卡片必须给出
|
|
9
|
+
* 编辑器,见 src/client/card.tsx),也是本预设里唯一需要热更的字段;其余字段是
|
|
10
|
+
* **部署方平面**(MCP 参数、超时、附加 skill 根等运维旋钮),由 `cordis.patch.yml`
|
|
11
|
+
* 整体覆盖,GUI 不提供入口。
|
|
12
|
+
*
|
|
13
|
+
* **persona 默认为空串**:`dsh-persona` 的空模板照样遮蔽部署级 persona 段,
|
|
14
|
+
* 渲染为空后在提示词里消失;官方装配(harness 身份、工具指引、项目指令、
|
|
15
|
+
* 运行时上下文)不受影响。
|
|
16
|
+
*
|
|
17
|
+
* 预设 id 固定为 `rea`(注册表身份,不开放覆盖——改 id 等于换预设,
|
|
18
|
+
* default/selectedDefault 等引用都会落空)。
|
|
19
|
+
* @module @dsh-plus/agent-preset-rea/config
|
|
20
|
+
*/
|
|
21
|
+
import z from '@deepseek-ai/schemastery'
|
|
22
|
+
import type { UnwrapVolatile } from '@dsh-plus/shared'
|
|
23
|
+
|
|
24
|
+
import {
|
|
25
|
+
DEFAULT_MCP_COMMAND,
|
|
26
|
+
INPUT_SCHEMA_PROFILES,
|
|
27
|
+
OUTPUT_SCHEMA_PROFILES,
|
|
28
|
+
REA_PINNED_VERSION,
|
|
29
|
+
} from './mcp.ts'
|
|
30
|
+
import { SETTINGS_NS as NS_LITERAL } from './ns.ts'
|
|
31
|
+
|
|
32
|
+
/** settings 命名空间(字面量即合法命名空间;配置卡片与运行期读取同一份)。 */
|
|
33
|
+
export const SETTINGS_NS = NS_LITERAL
|
|
34
|
+
|
|
35
|
+
export const Config = z.object({
|
|
36
|
+
enabled: z
|
|
37
|
+
.boolean()
|
|
38
|
+
.description('总开关(false = 不注册 rea 预设,等价插件未安装)')
|
|
39
|
+
.default(true),
|
|
40
|
+
name: z.string().description('预设显示名(设置 → Agent 预设卡片标题)').default('逆向工程'),
|
|
41
|
+
description: z
|
|
42
|
+
.string()
|
|
43
|
+
.description('预设说明(设置 → Agent 预设卡片描述)')
|
|
44
|
+
.default(
|
|
45
|
+
'逆向工程预设:官方 standard 全量行 + 仅本预设可见的 REA MCP 客户端(mcp__rea__*)与随包 REA 官方 skill;persona 默认为空,工具指引沿用官方装配。',
|
|
46
|
+
),
|
|
47
|
+
order: z.number().description('预设名册排序位').default(6),
|
|
48
|
+
personaPrefix: z
|
|
49
|
+
.string()
|
|
50
|
+
.description(
|
|
51
|
+
'persona 前缀(system-prompt 的 deployment:persona-prefix 段);留空 = 空 persona(该段渲染为空后消失),工具指引与运行时上下文保留',
|
|
52
|
+
)
|
|
53
|
+
.default('')
|
|
54
|
+
.volatile(),
|
|
55
|
+
mcpEnabled: z
|
|
56
|
+
.boolean()
|
|
57
|
+
.description('是否挂 REA MCP 客户端行(false = 只保留预设其余能力)')
|
|
58
|
+
.default(true),
|
|
59
|
+
mcpServerName: z
|
|
60
|
+
.string()
|
|
61
|
+
.pattern(/^[A-Za-z0-9_-]{1,32}$/)
|
|
62
|
+
.description(
|
|
63
|
+
'MCP 服务命名空间:模型面工具名为 mcp__<serverName>__<tool>,取值 [A-Za-z0-9_-]{1,32}',
|
|
64
|
+
)
|
|
65
|
+
.default('rea'),
|
|
66
|
+
reaVersion: z
|
|
67
|
+
.string()
|
|
68
|
+
.description('钉版的 rea-agents 版本(npx 拉取)')
|
|
69
|
+
.default(REA_PINNED_VERSION),
|
|
70
|
+
mcpCommand: z
|
|
71
|
+
.string()
|
|
72
|
+
.description('MCP stdio 可执行文件;Windows 需 cmd.exe 配方,见包文档')
|
|
73
|
+
.default(DEFAULT_MCP_COMMAND),
|
|
74
|
+
mcpArgs: z
|
|
75
|
+
.array(z.string())
|
|
76
|
+
.description('MCP stdio 参数;留空 = 按 reaVersion 派生 ["-y","rea-agents@<version>","mcp"]')
|
|
77
|
+
.default([]),
|
|
78
|
+
inputSchemaProfile: z
|
|
79
|
+
.union(INPUT_SCHEMA_PROFILES)
|
|
80
|
+
.description(
|
|
81
|
+
'REA 工具入参 schema 广告档位(REA_MCP_INPUT_SCHEMA_PROFILE):compact 压体积,full 与上游默认一致',
|
|
82
|
+
)
|
|
83
|
+
.default('compact'),
|
|
84
|
+
outputSchemaProfile: z
|
|
85
|
+
.union(OUTPUT_SCHEMA_PROFILES)
|
|
86
|
+
.description(
|
|
87
|
+
'REA 工具出参 schema 广告档位(REA_MCP_OUTPUT_SCHEMA_PROFILE):off 不下发(结果仍由 REA 校验),full 与上游默认一致',
|
|
88
|
+
)
|
|
89
|
+
.default('off'),
|
|
90
|
+
toolCallTimeoutMs: z
|
|
91
|
+
.number()
|
|
92
|
+
.description('单次 MCP 工具调用超时(毫秒);REA 分析类调用常超默认 60 秒')
|
|
93
|
+
.default(600_000),
|
|
94
|
+
failOnStartupError: z
|
|
95
|
+
.boolean()
|
|
96
|
+
.description(
|
|
97
|
+
'MCP 初次连接/同步失败是否让本行激活失败;默认 false:只记日志并按退避重连,不拖垮 boot',
|
|
98
|
+
)
|
|
99
|
+
.default(false),
|
|
100
|
+
extraSkillDirs: z
|
|
101
|
+
.array(z.string())
|
|
102
|
+
.description('附加 skill 根目录(随本预设作用域生效,其他预设不可见)')
|
|
103
|
+
.default([]),
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
/** 活动字段形态(loader 解析产物:`personaPrefix` 为活动引用)。 */
|
|
107
|
+
export type AgentPresetReaConfigFields = Schemastery.TypeT<typeof Config>
|
|
108
|
+
|
|
109
|
+
/** 平面配置形态(消费面的读取形态,由活动引用解包得到)。 */
|
|
110
|
+
export type AgentPresetReaConfig = UnwrapVolatile<AgentPresetReaConfigFields>
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 逆向工程预设声明组装:官方 `standard` 预设**全量行**的逐字镜像 + 三处显式增量。
|
|
3
|
+
*
|
|
4
|
+
* 镜像:每条上游行的 `id`/`name`/`config`(含平台门控 `!!js` 表达式与 plan-mode
|
|
5
|
+
* 段落原文)都与 `@deepseek-ai/dsh-web-app` 的 `presets/standard.patch.yml` 逐字一致;
|
|
6
|
+
* 上游改用新版本后按 docs/ops/新版适配流程.md 刷新夹具,
|
|
7
|
+
* tests/upstream-mirror.test.ts 会逼出对齐。本预设不丢任何上游行(与 lean 相反)。
|
|
8
|
+
*
|
|
9
|
+
* 增量仅三处,测试按 {@link ADDED_ROWS} / {@link CONFIG_DELTAS} 白名单放行:
|
|
10
|
+
* 1. `persona` 行 `prefix` 取配置值(默认空串 = 空 persona);
|
|
11
|
+
* 2. `skill-filesystem` 行加 `customSkillDirs`(随包 REA skill + 配置追加目录);
|
|
12
|
+
* 3. 新增 `mcp-rea` 行:官方 MCP 客户端连 REA stdio 服务,落在本预设作用域内。
|
|
13
|
+
*
|
|
14
|
+
* 组行必须带 `isolate` 归属 realm,否则注册表拒绝挂载(服务泄漏判定)。
|
|
15
|
+
* @module @dsh-plus/agent-preset-rea/definition
|
|
16
|
+
*/
|
|
17
|
+
import type { PresetDefinition } from '@deepseek-ai/dsh-agent-preset-registry'
|
|
18
|
+
|
|
19
|
+
import type { AgentPresetReaConfig } from './config.ts'
|
|
20
|
+
import { reaMcpArgs, reaMcpEnv } from './mcp.ts'
|
|
21
|
+
import { PLAN_MODE_SECTION } from './prompt.ts'
|
|
22
|
+
import { reaSkillDirs } from './skill.ts'
|
|
23
|
+
|
|
24
|
+
/** 预设注册表身份(改 id 等于换预设,default 等引用都会落空)。 */
|
|
25
|
+
export const REA_PRESET_ID = 'rea'
|
|
26
|
+
|
|
27
|
+
/** 相对上游 `standard` 新增的行 id(镜像测试按此白名单放行自造行)。 */
|
|
28
|
+
export const ADDED_ROWS: readonly string[] = ['mcp-rea']
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* 已镜像行 → 允许偏离上游的字段名(其余字段必须逐字一致)。
|
|
32
|
+
* 删除某条等于把该行改回上游原文;改动上游不存在的字段会被测试拦下。
|
|
33
|
+
*/
|
|
34
|
+
export const CONFIG_DELTAS: Readonly<Record<string, readonly string[]>> = {
|
|
35
|
+
persona: ['prefix'],
|
|
36
|
+
'skill-filesystem': ['customSkillDirs'],
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** 平台门控表达式:与上游 `standard` 预设逐字相同,Windows 上由 pwsh 顶替 bash。 */
|
|
40
|
+
const WINDOWS_ONLY = { __jsExpr: "process.platform === 'win32'" } as const
|
|
41
|
+
const POSIX_ONLY = { __jsExpr: "process.platform !== 'win32'" } as const
|
|
42
|
+
|
|
43
|
+
/** 官方 loader 的嵌套组行名。 */
|
|
44
|
+
const GROUP = 'cordis:group'
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* 由配置拼出注册表声明。
|
|
48
|
+
* @param config - 已验证的插件配置。
|
|
49
|
+
* @returns id 固定为 `rea` 的预设声明。
|
|
50
|
+
*/
|
|
51
|
+
export function reaPresetDefinition(config: AgentPresetReaConfig): PresetDefinition {
|
|
52
|
+
return {
|
|
53
|
+
id: REA_PRESET_ID,
|
|
54
|
+
name: config.name,
|
|
55
|
+
description: config.description,
|
|
56
|
+
order: config.order,
|
|
57
|
+
plugins: [
|
|
58
|
+
// 身份:沿用官方装配(非 complete、保留运行时上下文),前缀取配置(默认空串:
|
|
59
|
+
// 空模板照样遮蔽部署级 persona 段,渲染为空后消失,不追加任何自有文案)。
|
|
60
|
+
{
|
|
61
|
+
id: 'persona',
|
|
62
|
+
name: '@deepseek-ai/dsh-persona',
|
|
63
|
+
config: { prefix: config.personaPrefix },
|
|
64
|
+
},
|
|
65
|
+
// 项目指令(AGENTS.md)与逐步时钟上下文(默认 10 分钟一条)。
|
|
66
|
+
{
|
|
67
|
+
id: 'agent-instructions',
|
|
68
|
+
name: '@deepseek-ai/dsh-agent-instructions',
|
|
69
|
+
config: { maxBytes: 65536 },
|
|
70
|
+
},
|
|
71
|
+
{ id: 'time-context', name: '@deepseek-ai/dsh-time-context' },
|
|
72
|
+
// shell:一次性执行,执行器在宿主平面;平台门控与上游一致。
|
|
73
|
+
{ id: 'tool-bash', name: '@deepseek-ai/dsh-tool-bash', disabled: WINDOWS_ONLY },
|
|
74
|
+
{ id: 'tool-pwsh', name: '@deepseek-ai/dsh-tool-pwsh', disabled: POSIX_ONLY },
|
|
75
|
+
// 文件读写、检索与后台任务控制。
|
|
76
|
+
{ id: 'tool-fs', name: '@deepseek-ai/dsh-tool-fs' },
|
|
77
|
+
{
|
|
78
|
+
id: 'tool-fs-search',
|
|
79
|
+
name: '@deepseek-ai/dsh-tool-fs-search',
|
|
80
|
+
config: { sampleOverCapGlobResults: false },
|
|
81
|
+
},
|
|
82
|
+
{ id: 'tool-jobs', name: '@deepseek-ai/dsh-tool-jobs' },
|
|
83
|
+
{ id: 'tool-schedule', name: '@deepseek-ai/dsh-tool-schedule' },
|
|
84
|
+
// 技能:本地目录发现 + 模型面加载器;本预设额外挂上随包 REA 官方 skill 根
|
|
85
|
+
// (customSkillDirs 是 rank 300 的追加根,不动项目/用户/bundled 根)。
|
|
86
|
+
{
|
|
87
|
+
id: 'skill-filesystem',
|
|
88
|
+
name: '@deepseek-ai/dsh-skill-filesystem',
|
|
89
|
+
config: { customSkillDirs: reaSkillDirs(config) },
|
|
90
|
+
},
|
|
91
|
+
{ id: 'tool-skill', name: '@deepseek-ai/dsh-tool-skill' },
|
|
92
|
+
// 同会话目标:/goal 命令与模型面目标工具。
|
|
93
|
+
{ id: 'command-goal', name: '@deepseek-ai/dsh-command-goal' },
|
|
94
|
+
{ id: 'tool-goal', name: '@deepseek-ai/dsh-tool-goal' },
|
|
95
|
+
// 计划模式:状态天然属于单个 agent,条目级 realm 即正确生命周期。
|
|
96
|
+
{
|
|
97
|
+
id: 'planning',
|
|
98
|
+
name: GROUP,
|
|
99
|
+
group: true,
|
|
100
|
+
isolate: { planMode: true },
|
|
101
|
+
config: [
|
|
102
|
+
{
|
|
103
|
+
id: 'plan-mode',
|
|
104
|
+
name: '@deepseek-ai/dsh-plan-mode',
|
|
105
|
+
config: { section: PLAN_MODE_SECTION },
|
|
106
|
+
},
|
|
107
|
+
],
|
|
108
|
+
},
|
|
109
|
+
// 压缩:token 计量器留在宿主平面,本组只挂读取它的后端与结果裁剪器。
|
|
110
|
+
{
|
|
111
|
+
id: 'compaction',
|
|
112
|
+
name: GROUP,
|
|
113
|
+
group: true,
|
|
114
|
+
isolate: { compaction: true, toolResultPruner: true },
|
|
115
|
+
config: [
|
|
116
|
+
{ id: 'compaction-basic', name: '@deepseek-ai/dsh-compaction-basic' },
|
|
117
|
+
{ id: 'command-compact', name: '@deepseek-ai/dsh-command-compact' },
|
|
118
|
+
{
|
|
119
|
+
id: 'tool-result-pruner',
|
|
120
|
+
name: '@deepseek-ai/dsh-compaction-tool-result-pruner',
|
|
121
|
+
config: { thresholdChars: 8192, headChars: 4096, tailChars: 1024 },
|
|
122
|
+
},
|
|
123
|
+
],
|
|
124
|
+
},
|
|
125
|
+
// 委派与编排:子代理、fork、workflow 整组(编排引擎只在本 realm 内共享)。
|
|
126
|
+
{
|
|
127
|
+
id: 'delegation',
|
|
128
|
+
name: GROUP,
|
|
129
|
+
group: true,
|
|
130
|
+
isolate: { workflowEngine: true },
|
|
131
|
+
config: [
|
|
132
|
+
{ id: 'tool-subagent-control', name: '@deepseek-ai/dsh-tool-subagent-control' },
|
|
133
|
+
{
|
|
134
|
+
id: 'tool-subagent-list-agents',
|
|
135
|
+
name: '@deepseek-ai/dsh-tool-subagent-control/list-agents',
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
id: 'tool-subagent',
|
|
139
|
+
name: '@deepseek-ai/dsh-tool-subagent',
|
|
140
|
+
config: { provider: 'spawn', toolName: 'subagent', modelSelectionSettings: true },
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
id: 'tool-subagent-fork',
|
|
144
|
+
name: '@deepseek-ai/dsh-tool-subagent',
|
|
145
|
+
config: { provider: 'fork', toolName: 'subagent_fork' },
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
id: 'workflow-ptc',
|
|
149
|
+
name: '@deepseek-ai/dsh-workflow-ptc',
|
|
150
|
+
config: { provider: 'spawn' },
|
|
151
|
+
},
|
|
152
|
+
{ id: 'tool-workflow', name: '@deepseek-ai/dsh-tool-workflow' },
|
|
153
|
+
],
|
|
154
|
+
},
|
|
155
|
+
// 其余模型面行。
|
|
156
|
+
{ id: 'tool-ask-user', name: '@deepseek-ai/dsh-tool-ask-user' },
|
|
157
|
+
{
|
|
158
|
+
id: 'tool-todo',
|
|
159
|
+
name: '@deepseek-ai/dsh-tool-todo',
|
|
160
|
+
config: { allowParallelInProgress: true },
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
id: 'tool-web',
|
|
164
|
+
name: '@deepseek-ai/dsh-tool-web',
|
|
165
|
+
config: { fetch: true, searchTimeoutMs: 60000 },
|
|
166
|
+
},
|
|
167
|
+
// 本预设新增:REA MCP 客户端(stdio),工具以 mcp__<serverName>__<tool> 注册在
|
|
168
|
+
// 本预设作用域;服务名预留按作用域隔离,其他预设不受影响。
|
|
169
|
+
{
|
|
170
|
+
id: 'mcp-rea',
|
|
171
|
+
name: '@deepseek-ai/dsh-mcp-client',
|
|
172
|
+
disabled: !config.mcpEnabled,
|
|
173
|
+
config: {
|
|
174
|
+
transport: 'stdio',
|
|
175
|
+
serverName: config.mcpServerName,
|
|
176
|
+
command: config.mcpCommand,
|
|
177
|
+
args: reaMcpArgs(config),
|
|
178
|
+
env: reaMcpEnv(config),
|
|
179
|
+
toolCallTimeoutMs: config.toolCallTimeoutMs,
|
|
180
|
+
failOnStartupError: config.failOnStartupError,
|
|
181
|
+
},
|
|
182
|
+
},
|
|
183
|
+
{ id: 'present', name: '@deepseek-ai/dsh-tool-present' },
|
|
184
|
+
// 宿主插件管理:上游 standard 里同样是 disabled 行。
|
|
185
|
+
{ id: 'tool-plugin-manager', name: '@deepseek-ai/dsh-plugin-manager/tools', disabled: true },
|
|
186
|
+
],
|
|
187
|
+
}
|
|
188
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dsh 服务插件:把「逆向工程」注册为官方 agent preset(id=`rea`)。
|
|
3
|
+
*
|
|
4
|
+
* 预设声明经 `ctx.agentPresets.register()` 注册(官方 preset registry 是唯一
|
|
5
|
+
* 读取方),出现在设置 → Agent 预设的名单里,可被会话选择。
|
|
6
|
+
*
|
|
7
|
+
* 预设语义:官方 `standard` 预设**全量行**的逐字镜像(shell/文件读写与检索/
|
|
8
|
+
* 后台任务/定时提醒/技能/目标/计划/压缩/委派与编排/提问/待办/联网/交付/插件管理
|
|
9
|
+
* 全在),另加三处显式增量——persona 前缀取配置(默认空串)、`skill-filesystem`
|
|
10
|
+
* 加随包 REA skill 根、新增 `mcp-rea` 行连 REA stdio 服务。详见 definition.ts。
|
|
11
|
+
*
|
|
12
|
+
* 作用域:三处增量都落在本预设声明内,因此 REA 工具(`mcp__rea__*`)、REA skill
|
|
13
|
+
* 与附加 skill 根只对该预设的会话可见;宿主平面不新增工具/提示段,其他预设不受
|
|
14
|
+
* 影响(见 docs/README.md 的隔离一节)。
|
|
15
|
+
*
|
|
16
|
+
* persona 提示词:`personaPrefix` 是活动字段(`.volatile()`),由设置 → 插件页
|
|
17
|
+
* 的配置卡片写入;保存后 loader 原位提交活动引用并广播 `loader/volatile-update`,
|
|
18
|
+
* 本插件据此换代注册(注销旧声明 → 按新值重建声明),**新建会话**即用新提示词,
|
|
19
|
+
* 已存在会话保持既有 composition(注册表语义:运行中会话的装配不随换代改变)。
|
|
20
|
+
*
|
|
21
|
+
* 设计取舍:
|
|
22
|
+
* - 独立插件而非 cordis.patch.yml 里的裸 `@deepseek-ai/dsh-agent-preset`
|
|
23
|
+
* 行:便于纳入本仓的构建/测试/文档体系,并把显示面与 MCP/skill 参数开放为
|
|
24
|
+
* 可覆盖配置;
|
|
25
|
+
* - 惰性 inject `agentPresets`:服务缺席的 profile(如 headless)里本行
|
|
26
|
+
* 照常 active、只是不注册(等价插件缺席),绝不因硬 inject 永久 pending
|
|
27
|
+
* 拖垮 boot(同 boot-retry 的教训);
|
|
28
|
+
* - 注册与换代在 effect 内后台串行完成、不阻塞 apply:注册会同步挂载预设
|
|
29
|
+
* 子树并等待其激活(含 MCP 首次连接),await 在本行激活内可能与 Host 树推进
|
|
30
|
+
* 互锁;失败仅记 warn(重名、MCP 起不来等由各注册表/客户端负责诊断),
|
|
31
|
+
* 不使 boot 失败。
|
|
32
|
+
* @module @dsh-plus/agent-preset-rea
|
|
33
|
+
*/
|
|
34
|
+
import type { Context } from '@deepseek-ai/cordis'
|
|
35
|
+
import { unwrapVolatile } from '@dsh-plus/shared'
|
|
36
|
+
|
|
37
|
+
import { type AgentPresetReaConfig, type AgentPresetReaConfigFields, Config } from './config.ts'
|
|
38
|
+
import { reaPresetDefinition } from './definition.ts'
|
|
39
|
+
import { createPresetRegistrar } from './registration.ts'
|
|
40
|
+
|
|
41
|
+
export const name = 'dsh-plus-agent-preset-rea'
|
|
42
|
+
|
|
43
|
+
/** 无硬 inject:agentPresets 经 apply 内惰性 inject 接线(缺席即空转)。 */
|
|
44
|
+
export const inject = [] as const
|
|
45
|
+
|
|
46
|
+
export { SETTINGS_NS } from './config.ts'
|
|
47
|
+
export { ADDED_ROWS, CONFIG_DELTAS, REA_PRESET_ID, reaPresetDefinition } from './definition.ts'
|
|
48
|
+
export {
|
|
49
|
+
DEFAULT_MCP_COMMAND,
|
|
50
|
+
INPUT_SCHEMA_PROFILES,
|
|
51
|
+
OUTPUT_SCHEMA_PROFILES,
|
|
52
|
+
REA_PINNED_VERSION,
|
|
53
|
+
reaMcpArgs,
|
|
54
|
+
reaMcpEnv,
|
|
55
|
+
} from './mcp.ts'
|
|
56
|
+
export { PLAN_MODE_SECTION } from './prompt.ts'
|
|
57
|
+
export { createPresetRegistrar, type PresetRegistrar, type Unregister } from './registration.ts'
|
|
58
|
+
export { BUNDLED_SKILL_ROOT, reaSkillDirs } from './skill.ts'
|
|
59
|
+
export type { AgentPresetReaConfig, AgentPresetReaConfigFields }
|
|
60
|
+
export { Config }
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* 注册 rea 预设,并在 persona 热更后换代;卸载时注销当前一代。
|
|
64
|
+
*
|
|
65
|
+
* 注册与换代都在 effect 生命周期内完成:fiber 早于注册完成被卸下时,
|
|
66
|
+
* 换代器自己收掉晚到的 disposer,不泄漏预设子树。
|
|
67
|
+
* @param ctx - 宿主上下文。
|
|
68
|
+
* @param config - 已验证的插件配置(`personaPrefix` 为活动引用)。
|
|
69
|
+
*/
|
|
70
|
+
export function apply(ctx: Context, config: AgentPresetReaConfigFields): void {
|
|
71
|
+
const logger = ctx.logger('agent-preset-rea')
|
|
72
|
+
const current = (): AgentPresetReaConfig => unwrapVolatile(config)
|
|
73
|
+
if (current().enabled !== true) return
|
|
74
|
+
|
|
75
|
+
const onError = (error: unknown): void => {
|
|
76
|
+
const detail = error instanceof Error ? error.message : String(error)
|
|
77
|
+
logger.warn(`register rea preset failed: ${detail}`)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
ctx.inject(['agentPresets'], (scope) => {
|
|
81
|
+
scope.effect(() => {
|
|
82
|
+
const registrar = createPresetRegistrar({
|
|
83
|
+
register: (definition) => scope.agentPresets.register(definition),
|
|
84
|
+
build: () => reaPresetDefinition(current()),
|
|
85
|
+
onError,
|
|
86
|
+
})
|
|
87
|
+
// 监听器挂在插件自身 fiber 上:loader 正是对该 fiber 广播 volatile 提交。
|
|
88
|
+
const off = ctx.events.on('loader/volatile-update', () => registrar.refresh())
|
|
89
|
+
return () => {
|
|
90
|
+
off()
|
|
91
|
+
registrar.dispose()
|
|
92
|
+
}
|
|
93
|
+
}, 'agent-preset-rea: register rea preset')
|
|
94
|
+
})
|
|
95
|
+
}
|
package/src/mcp.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* REA MCP 行的取值派生:钉版版本、stdio 启动命令与参数、入参 schema 档位。
|
|
3
|
+
*
|
|
4
|
+
* REA 的 stdio 入口是 `rea-agents mcp`(`server.json` 的 positional 参数),
|
|
5
|
+
* 包版本钉在 {@link REA_PINNED_VERSION}——升级只改这一个常量,漂移由测试拦住。
|
|
6
|
+
* REA 的 Node 引擎要求为 `^22.19 || ^24.11 || >=26`(见 REA `docs/installation.md`)。
|
|
7
|
+
* @module @dsh-plus/agent-preset-rea/mcp
|
|
8
|
+
*/
|
|
9
|
+
import type { AgentPresetReaConfig } from './config.ts'
|
|
10
|
+
|
|
11
|
+
/** 钉版基线:`rea-agents@6.5.0`(升级时同步测试断言、tests 清单与包文档)。 */
|
|
12
|
+
export const REA_PINNED_VERSION = '6.5.0'
|
|
13
|
+
|
|
14
|
+
/** stdio 启动命令默认值:POSIX 用 `npx`;Windows 需 `cmd.exe /c npx`(见包文档)。 */
|
|
15
|
+
export const DEFAULT_MCP_COMMAND = 'npx'
|
|
16
|
+
|
|
17
|
+
/** REA 工具入参 schema 广告档位:`compact` 压请求前缀体积,`full` 与上游默认一致。 */
|
|
18
|
+
export const INPUT_SCHEMA_PROFILES = ['compact', 'full'] as const
|
|
19
|
+
|
|
20
|
+
/** REA 工具出参 schema 广告档位:`off` 不下发(结果仍由 REA 侧校验),`full` 与上游默认一致。 */
|
|
21
|
+
export const OUTPUT_SCHEMA_PROFILES = ['off', 'full'] as const
|
|
22
|
+
|
|
23
|
+
/** stdio 参数:显式配置优先(Windows/全局安装配方),否则按钉版版本派生。 */
|
|
24
|
+
export function reaMcpArgs(config: AgentPresetReaConfig): string[] {
|
|
25
|
+
return config.mcpArgs.length > 0
|
|
26
|
+
? [...config.mcpArgs]
|
|
27
|
+
: ['-y', `rea-agents@${config.reaVersion}`, 'mcp']
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* 传给 REA 子进程的环境变量(并入被清洗过的宿主环境之上)。
|
|
32
|
+
*
|
|
33
|
+
* REA 有 207 个工具(`server.json` 同版的 skill 元数据 `tool_count`),工具定义
|
|
34
|
+
* 是每轮请求的固定开销,因此两个 schema 档位默认都取最省的一档;REA 自己仍按
|
|
35
|
+
* 规范契约校验结果,`full` 可逐项还原上游默认。
|
|
36
|
+
*/
|
|
37
|
+
export function reaMcpEnv(config: AgentPresetReaConfig): Record<string, string> {
|
|
38
|
+
return {
|
|
39
|
+
REA_MCP_INPUT_SCHEMA_PROFILE: config.inputSchemaProfile,
|
|
40
|
+
REA_MCP_OUTPUT_SCHEMA_PROFILE: config.outputSchemaProfile,
|
|
41
|
+
}
|
|
42
|
+
}
|
package/src/ns.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* settings 命名空间字面量(纯常量,零依赖,浏览器半可安全引入)。
|
|
3
|
+
*
|
|
4
|
+
* 服务端以字面量参与 `Config` 的活动字段(dsh-settings 对命名空间做语法校验),
|
|
5
|
+
* 浏览器半把它作为配置卡片的注册键(插件页配置卡片挂在 row/bundle config 槽位上,
|
|
6
|
+
* 与命名空间解耦,见 shared/client/config-slots);同一字面量两处共用,防止漂移。
|
|
7
|
+
* @module @dsh-plus/agent-preset-rea/ns
|
|
8
|
+
*/
|
|
9
|
+
export const SETTINGS_NS = 'dsh-plus-agent-preset-rea'
|
package/src/prompt.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 本包随包携带的上游提示词文本:`standard` 预设 `plan-mode` 行的 section 原文。
|
|
3
|
+
*
|
|
4
|
+
* `dsh-plan-mode` 要求 `config.section` 非空(部署方所有权),因此该文本必须随包;
|
|
5
|
+
* 逐字相等由 tests/upstream-mirror.test.ts 对着上游夹具守卫,上游一改即失败。
|
|
6
|
+
*
|
|
7
|
+
* persona 不在本文件:本预设的 persona 前缀默认为**空串**(见 config.ts 的
|
|
8
|
+
* `personaPrefix`),不随包任何自有 persona 文案;官方工具指引、项目指令
|
|
9
|
+
* (AGENTS.md)与运行时上下文照常装配。
|
|
10
|
+
* @module @dsh-plus/agent-preset-rea/prompt
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/** 上游 `standard` 预设 `plan-mode` 行的 section 原文(逐字,勿手改)。 */
|
|
14
|
+
export const PLAN_MODE_SECTION = `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.
|
|
15
|
+
|
|
16
|
+
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.
|
|
17
|
+
|
|
18
|
+
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.
|
|
19
|
+
|
|
20
|
+
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.
|
|
21
|
+
|
|
22
|
+
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.
|
|
23
|
+
|
|
24
|
+
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.
|
|
25
|
+
`
|