@y9931666/dsh-plugin-crossfire 0.1.1 → 0.1.3
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 +2 -0
- package/lib/client.js +160 -19
- package/lib/index.js +1 -1
- package/package.json +1 -1
- package/release-notes/v0.1.2.md +44 -0
- package/release-notes/v0.1.3.md +21 -0
- package/lib/types/budget.d.ts +0 -86
- package/lib/types/client/ReviewCard.d.ts +0 -41
- package/lib/types/client/SettingsView.d.ts +0 -34
- package/lib/types/client/brand.d.ts +0 -55
- package/lib/types/client/defectTitle.d.ts +0 -23
- package/lib/types/client/index.d.ts +0 -21
- package/lib/types/client/locales.d.ts +0 -19
- package/lib/types/client/merge.d.ts +0 -27
- package/lib/types/client/promptView.d.ts +0 -63
- package/lib/types/client/session-cache.d.ts +0 -49
- package/lib/types/client/slot-boundary.d.ts +0 -46
- package/lib/types/client/wire.d.ts +0 -192
- package/lib/types/dimensions.d.ts +0 -47
- package/lib/types/engine.d.ts +0 -172
- package/lib/types/etag.d.ts +0 -40
- package/lib/types/expert-events.d.ts +0 -110
- package/lib/types/export-md.d.ts +0 -21
- package/lib/types/harness-compat.d.ts +0 -104
- package/lib/types/index.d.ts +0 -89
- package/lib/types/members.d.ts +0 -143
- package/lib/types/noise.d.ts +0 -29
- package/lib/types/preferences.d.ts +0 -134
- package/lib/types/prompt.d.ts +0 -16
- package/lib/types/redteam.d.ts +0 -115
- package/lib/types/review.d.ts +0 -152
- package/lib/types/rpc.d.ts +0 -176
- package/lib/types/split.d.ts +0 -156
- package/lib/types/state.d.ts +0 -84
- package/lib/types/tools.d.ts +0 -59
- package/lib/types/types.d.ts +0 -308
- package/lib/types/version.d.ts +0 -18
- package/lib/types/web-guard.d.ts +0 -55
package/lib/types/members.d.ts
DELETED
|
@@ -1,143 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 红队专家的子代理生命周期:为一位专家拉起一个**可续聊**子代理、把消息投进它的
|
|
3
|
-
* 下一轮、必要时打断它。
|
|
4
|
-
*
|
|
5
|
-
* 与圆桌 meetings 的 members.ts 相比,这里刻意薄得多:
|
|
6
|
-
* 没有拓扑、没有连线、没有转述链路 —— 专家只做一件事:攻击指定维度并 report。
|
|
7
|
-
*
|
|
8
|
-
* ## 安全边界(本文件是它的落点)
|
|
9
|
-
*
|
|
10
|
-
* 专家的 `toolFilter` 是**只读白名单**(见 redteam.ts 的 `EXPERT_TOOL_ALLOW`):
|
|
11
|
-
* 它们能读文件取证,但**不能写**、不能执行命令、不能联网。这条边界是针锋与
|
|
12
|
-
* 普通"多个 agent 分工干活"最本质的区别 —— 验收器不该有改动被测对象的能力。
|
|
13
|
-
*
|
|
14
|
-
* @module dsh-plugin-crossfire/members
|
|
15
|
-
*/
|
|
16
|
-
import type { Context } from '@deepseek-ai/cordis';
|
|
17
|
-
import type { Agent } from '@deepseek-ai/dsh-agent';
|
|
18
|
-
import type { CrossfireRecord, RedTeamExpert } from './types.ts';
|
|
19
|
-
/**
|
|
20
|
-
* 本插件自己声明的 `MessageSourceMap` 条目。
|
|
21
|
-
*
|
|
22
|
-
* 0.2.0-rc.2 移除了宿主 catch-all 的 `plugin` kind(`MessageSourceMap` 只保留
|
|
23
|
-
* `user` / `model` / `tool` / `system-prompt`),其余一律由**生产者在自己的模块里
|
|
24
|
-
* 声明**。这里照同一模式补上 crossfire 的 kind,而不是退化成匿名的 `user` 消息:
|
|
25
|
-
* 转录消费者据此把"插件注入的提示"与"用户自己说的话"分开。运行时无影响。
|
|
26
|
-
*/
|
|
27
|
-
declare module '@deepseek-ai/dsh-llm' {
|
|
28
|
-
interface MessageSourceMap {
|
|
29
|
-
/** 针锋插件注入的评审任务消息。 */
|
|
30
|
-
crossfire: {
|
|
31
|
-
kind: 'crossfire';
|
|
32
|
-
/** 生产者标识,便于在转录里追溯来源。 */
|
|
33
|
-
plugin: string;
|
|
34
|
-
};
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
/** 拉起专家所需的运行时开关(来自插件 Config)。 */
|
|
38
|
-
export interface MemberRuntimeConfig {
|
|
39
|
-
/** 已注册的 `ctx.subagents` provider 名(必须支持 continuable + persona)。 */
|
|
40
|
-
provider: string;
|
|
41
|
-
/** 委派深度上限(默认 1:红队专家不得再招人)。 */
|
|
42
|
-
maxDepth?: number;
|
|
43
|
-
/**
|
|
44
|
-
* 专家**继承**主持人时实际生效的模型路由。
|
|
45
|
-
*
|
|
46
|
-
* 主要用途是可观测性:写回记录后卡片才能把"继承"画成真实品牌(DeepSeek 的蓝色
|
|
47
|
-
* DS 块),而不是一个中性灰 `?`。取不到时不写(界面退化为 `?` + "继承主持人"提示)。
|
|
48
|
-
*
|
|
49
|
-
* 另一处用途(A5):阵容成员**只填了 model** 时,`defaultProvider` 是真正参与路由
|
|
50
|
-
* 的那个 provider(补进 agentOptions 并写回记录)。只在纯继承的情形下它才是纯
|
|
51
|
-
* 可观测值 —— 而且它**不会**被用来顶替某个成员自己指定的 model。
|
|
52
|
-
*/
|
|
53
|
-
defaultProvider?: string;
|
|
54
|
-
defaultModel?: string;
|
|
55
|
-
}
|
|
56
|
-
/** 一位专家的输出限制。 */
|
|
57
|
-
export interface ExpertRuntimeLimits {
|
|
58
|
-
/** 单轮输出 token 上限(0/未设 = provider 默认)。 */
|
|
59
|
-
maxTokens?: number;
|
|
60
|
-
/** 这位专家最多提几条缺陷(0 = 不限制)。 */
|
|
61
|
-
maxOpinions?: number;
|
|
62
|
-
}
|
|
63
|
-
/** 一位专家最终生效的路由决定。 */
|
|
64
|
-
export interface ExpertRoute {
|
|
65
|
-
/** 写回记录的 provider;缺席 = "继承 / 未知"(界面退化为中性块,而不是一个假路由)。 */
|
|
66
|
-
provider?: string;
|
|
67
|
-
/** 写回记录的 model;缺席 = 同上。 */
|
|
68
|
-
model?: string;
|
|
69
|
-
/** **真正**交给 `startContinuable` 的 agentOptions(空对象 = 完全不干预路由)。 */
|
|
70
|
-
agentOptions: {
|
|
71
|
-
provider?: string;
|
|
72
|
-
model?: string;
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
|
-
/**
|
|
76
|
-
* 把"专家显式指定的路由"与"宿主默认路由"收敛成一份确定的路由决定(A5 + B3)。
|
|
77
|
-
*
|
|
78
|
-
* ## 一条铁律
|
|
79
|
-
*
|
|
80
|
-
* **写回记录的值 = 真正下发的值。** 记录与下发必须由**同一次决定**产生,否则界面
|
|
81
|
-
* 会显示一条并非事实的路由(红队抓出的原缺陷:只填了 provider 的成员,记录里却被
|
|
82
|
-
* 写成 `defaultProvider` + `defaultModel` —— 界面说跑 A,实际跑 B)。
|
|
83
|
-
*
|
|
84
|
-
* ## 四种组合的行为(全都是确定的,没有"看情况")
|
|
85
|
-
*
|
|
86
|
-
* | 专家显式指定 | agentOptions(下发) | 写回记录 |
|
|
87
|
-
* |---|---|---|
|
|
88
|
-
* | provider + model | `{provider, model}` | provider、model |
|
|
89
|
-
* | 只有 provider | `{provider}` | provider(**不补 model**) |
|
|
90
|
-
* | 只有 model | `{defaultProvider?, model}` | provider(补上的那个)、model |
|
|
91
|
-
* | 都没有(继承) | `{}`(不干预) | defaultProvider、defaultModel |
|
|
92
|
-
*
|
|
93
|
-
* 逐条理由:
|
|
94
|
-
* - **只有 provider 时刻意不补 model**:`provider` 自己会选它的默认模型,我们没有
|
|
95
|
-
* 任何依据知道那会是哪一个;拿 `config.defaultModel` 去顶替就是编造(那正是原
|
|
96
|
-
* 缺陷的写法)。记录的 `model` 缺席是诚实的"不知道"。
|
|
97
|
-
* - **只有 model 时补 `defaultProvider`**:一个不给 provider 的 model id 在多数路由上
|
|
98
|
-
* 无法解析,而"只写 model、不写 provider"在用户眼里就是"用默认 provider 跑这个
|
|
99
|
-
* 模型"。补上的值同时写回记录 —— 于是界面显示的正是真正下发的。
|
|
100
|
-
* - **都没有时 agentOptions 为空**:这一种情形下我们**不替宿主做决定**,真正的继承
|
|
101
|
-
* 必须保持是继承。记录里那份 `defaultProvider`/`defaultModel` 是配置里声明的
|
|
102
|
-
* "继承主持人时**实际生效**的模型路由"(见 index.ts / ToolsConfig 的同一段注释),
|
|
103
|
-
* 写它只为把"继承"画成真实品牌块(否则头像永远是中性灰 `?`,那是用户报过的缺陷)。
|
|
104
|
-
* 这是**唯一**一处记录值不等于 agentOptions 的字段 —— 而它们仍然同源:记录里出现的
|
|
105
|
-
* 每一个字段,要么是我们显式下发的,要么是宿主默认路由本身。
|
|
106
|
-
* - **取不到生效值的字段一律不写**(保持"缺失 = 继承"的诚实语义),也不写空串。
|
|
107
|
-
*
|
|
108
|
-
* 纯函数:没有 I/O、没有时间、不改入参,所以四种组合都能被单测直接钉住
|
|
109
|
-
* (见 `test/members.test.mjs`)。
|
|
110
|
-
*/
|
|
111
|
-
export declare function resolveExpertRoute(expert: {
|
|
112
|
-
provider?: string;
|
|
113
|
-
model?: string;
|
|
114
|
-
}, config: {
|
|
115
|
-
defaultProvider?: string;
|
|
116
|
-
defaultModel?: string;
|
|
117
|
-
}): ExpertRoute;
|
|
118
|
-
/**
|
|
119
|
-
* 拉起一位红队专家,返回它的子会话 id。
|
|
120
|
-
*
|
|
121
|
-
* 失败一律抛错且**不落任何状态** —— 调用方负责把 `expert.status` 记成 `failed`
|
|
122
|
-
* 并把消息写进 `expert.note`,而不是留下一个"看起来在跑其实根本没起来"的专家。
|
|
123
|
-
*/
|
|
124
|
-
export declare function spawnExpert(ctx: Context, config: MemberRuntimeConfig, record: CrossfireRecord, expert: RedTeamExpert, parent: Agent, signal: AbortSignal, limits?: ExpertRuntimeLimits): Promise<string>;
|
|
125
|
-
/**
|
|
126
|
-
* 把一条消息投进某位专家的下一轮(best effort)。
|
|
127
|
-
*
|
|
128
|
-
* @returns true = 宿主接受了投递;false = 失败(已记日志,由调用方决定怎么办)。
|
|
129
|
-
*/
|
|
130
|
-
export declare function deliverToExpert(ctx: Context, parent: Agent, childId: string, text: string, signal: AbortSignal): Promise<boolean>;
|
|
131
|
-
/** 请求取消某位专家当前这一轮(fire and return)。 */
|
|
132
|
-
export declare function interruptExpert(ctx: Context, parent: Agent, childId: string): void;
|
|
133
|
-
/**
|
|
134
|
-
* 打断一位专家,**不需要**一个活着的 parent Agent。
|
|
135
|
-
*
|
|
136
|
-
* `ctx.subagents.interrupt` 需要祖先 Agent 作为授权凭据,因此在只持有 session id
|
|
137
|
-
* 的路径上(浏览器 RPC、或 parent 已离线的场景)用不了。宿主的 remote face 正是
|
|
138
|
-
* 为这种情况提供的:"what keeps a live child interruptible while its parent Agent
|
|
139
|
-
* is offline"。
|
|
140
|
-
*
|
|
141
|
-
* @returns true = 宿主接受了请求。
|
|
142
|
-
*/
|
|
143
|
-
export declare function interruptExpertByParent(ctx: Context, childId: string, parentSessionId: string): boolean;
|
package/lib/types/noise.d.ts
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 专家发言的噪音过滤(缺陷收集的第一道闸门)。
|
|
3
|
-
*
|
|
4
|
-
* ## 为什么必须有这一层
|
|
5
|
-
*
|
|
6
|
-
* 圆桌的真实数据里,下面这类**过程叙述**被当成缺陷收进了裁决列表:
|
|
7
|
-
*
|
|
8
|
-
* `[当前状态] 已收到主持人任务,完成对 17 项清单的四维红队审查;按…`
|
|
9
|
-
* `[当前状态] 主持人再次派发任务;第一轮 3 条观点已提交(第 7 项语义矛盾 / 批次 10–…`
|
|
10
|
-
*
|
|
11
|
-
* 它们有两个来源:
|
|
12
|
-
* - 宿主的 `subagent/end` 自动补捕(`kind: 'auto-capture'`):专家没主动上报结果就
|
|
13
|
-
* 结束了,宿主把它的最后一段话整段当成发言收下 —— 通常只是收尾寒暄或进度叙述;
|
|
14
|
-
* - 专家自己写的进度汇报:以 `[当前状态]`/`[进度]` 之类的方括号标记开头。
|
|
15
|
-
*
|
|
16
|
-
* 它们不是缺陷,却会挤进用户的表态面:用户被迫对"我收到了任务"点认定或驳回。
|
|
17
|
-
* 判定在这里集中成一处,**缺陷收集侧只调 `isNoiseUtterance`**,规则不外泄。
|
|
18
|
-
*
|
|
19
|
-
* @module dsh-plugin-crossfire/noise
|
|
20
|
-
*/
|
|
21
|
-
/**
|
|
22
|
-
* 命中的噪音规则名(诊断/测试用;不是噪音时返回 `undefined`)。
|
|
23
|
-
*
|
|
24
|
-
* 按顺序判定,命中即返回:`auto-capture` → `progress-report` → `empty`
|
|
25
|
-
* → `acknowledgement`。
|
|
26
|
-
*/
|
|
27
|
-
export declare function noiseReason(kind: string, content: string): string | undefined;
|
|
28
|
-
/** 这条发言是否应当被排除在缺陷收集之外。 */
|
|
29
|
-
export declare function isNoiseUtterance(kind: string, content: string): boolean;
|
|
@@ -1,134 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 运行时偏好的持久化与净化(自持久化,不依赖宿主 settings 命名空间)。
|
|
3
|
-
*
|
|
4
|
-
* ## 为什么自持久化
|
|
5
|
-
*
|
|
6
|
-
* 宿主 0.2.0-rc.2 把 `settings` 服务重构成 `SettingsForms`:它只把 **profile
|
|
7
|
-
* 配置投影成表单**,插件不再有"注册自己的命名空间"这个动作(`SettingsScope`
|
|
8
|
-
* 类型已消失)。而写 Cordis `Config` 会触发 Loader 重载该 entry —— 对"改一个
|
|
9
|
-
* 默认强度就重启插件"来说太重。
|
|
10
|
-
*
|
|
11
|
-
* 因此偏好落在 DSH home 下的 `crossfire/preferences.json`,读写都在插件内部
|
|
12
|
-
* 完成。**命名空间与圆桌会议的 `roundtable/preferences.json` 完全分开**:两个
|
|
13
|
-
* 插件共享一个偏好文件会让任意一方改字段就踩到另一方。
|
|
14
|
-
*
|
|
15
|
-
* ## 三条铁律(与 state.ts 同源)
|
|
16
|
-
*
|
|
17
|
-
* 1. **读失败绝不抛给调用方**:偏好文件损坏或被人手改成非法 JSON,一律退回
|
|
18
|
-
* 默认值并留一条日志。偏好是观察性配置,它不能让整个设置页消失。
|
|
19
|
-
* 2. **写入必须原子**:复用 {@link writeTextAtomic}。
|
|
20
|
-
* 3. **非法字段一律净化**:schemastery 不校验数组条目的 required 字段(圆桌
|
|
21
|
-
* 实测过 `z.object` 对缺 required 在 resolve 阶段不报错),条目级校验只能
|
|
22
|
-
* 手写 —— 这条在设置页写入侧尤其重要。
|
|
23
|
-
*
|
|
24
|
-
* @module dsh-plugin-crossfire/preferences
|
|
25
|
-
*/
|
|
26
|
-
import { type Intensity, type RolePreset, type RoleSquad } from './types.ts';
|
|
27
|
-
/**
|
|
28
|
-
* 解析 DeepSeek Harness home。
|
|
29
|
-
*
|
|
30
|
-
* 与宿主 `@deepseek-ai/dsh-home-paths` 的 `resolveDshHome` 优先级对齐:
|
|
31
|
-
* `configured` > `$DSH_HOME` > `~/.dsh`。第一级做不到(它是宿主进程内部传入的
|
|
32
|
-
* 显式覆盖,插件拿不到),但环境变量与默认值这两级完全一致。
|
|
33
|
-
*
|
|
34
|
-
* 刻意**不**静态 import `@deepseek-ai/dsh-home-paths`:那会把它变成加载门禁,
|
|
35
|
-
* 而本模块只是要一个路径。
|
|
36
|
-
*/
|
|
37
|
-
export declare function dshHomeDir(): string;
|
|
38
|
-
/** 偏好文件的绝对路径(DSH home 下的 `crossfire/preferences.json`)。 */
|
|
39
|
-
export declare function preferencesFilePath(): string;
|
|
40
|
-
/** 偏好的 wire 形状。 */
|
|
41
|
-
export interface CrossfirePreferences {
|
|
42
|
-
/** 新建评审的默认强度档。 */
|
|
43
|
-
readonly defaultIntensity: Intensity;
|
|
44
|
-
/** 默认轮数上限。 */
|
|
45
|
-
readonly maxRounds: number;
|
|
46
|
-
/** 默认 token 预算。 */
|
|
47
|
-
readonly maxTokens: number;
|
|
48
|
-
/** 专家每轮输出 token 上限(模型请求 max_tokens),0 = 不限制。 */
|
|
49
|
-
readonly expertMaxTokens: number;
|
|
50
|
-
/** 专家每轮最多提几条缺陷,0 = 不限制。 */
|
|
51
|
-
readonly expertMaxOpinions: number;
|
|
52
|
-
/** 用户自建维度(固定池之外)。 */
|
|
53
|
-
readonly customDimensions: string[];
|
|
54
|
-
/** 用户自建角色预设(全局;**不预置任何内置角色**)。 */
|
|
55
|
-
readonly rolePresets: RolePreset[];
|
|
56
|
-
/** 用户自建阵容预设(一次套用多位专家;同样不预置)。 */
|
|
57
|
-
readonly squads: RoleSquad[];
|
|
58
|
-
/**
|
|
59
|
-
* 被用户关掉的解释性说明(键 → true)。
|
|
60
|
-
*
|
|
61
|
-
* 用户原话:"一些解释性质的东西可以做一些删除按钮,就比如说在这个对话之前是一次
|
|
62
|
-
* 当用户删了之后就不会再显示,从而减少视觉上的负担"。所以它是**持久的**:刷新
|
|
63
|
-
* 页面、换会话都不该让它回来。恢复入口在设置页(那里列出已隐藏的说明)。
|
|
64
|
-
*/
|
|
65
|
-
readonly dismissedHints: Record<string, boolean>;
|
|
66
|
-
}
|
|
67
|
-
/** 没有任何持久化数据时的偏好。`defaultIntensity` 由调用方按插件 Config 覆盖。 */
|
|
68
|
-
export declare function defaultPreferences(defaultIntensity: Intensity): CrossfirePreferences;
|
|
69
|
-
/** 自定义维度的硬上限(客户端与服务端共用;服务端截断,客户端提前拦截)。 */
|
|
70
|
-
export declare const CUSTOM_DIMENSION_MAX = 12;
|
|
71
|
-
/** 单条维度名的最大长度。 */
|
|
72
|
-
export declare const DIMENSION_NAME_MAX = 12;
|
|
73
|
-
/**
|
|
74
|
-
* 把任意输入收敛成合法的自定义维度清单。
|
|
75
|
-
*
|
|
76
|
-
* 三条规则:空/超长丢弃;与固定池重名的丢弃(池内的不算"自定义",否则界面上
|
|
77
|
-
* 会出现两个"安全");去重且截断到上限。
|
|
78
|
-
*/
|
|
79
|
-
export declare function sanitizeCustomDimensions(value: unknown): string[];
|
|
80
|
-
/** 角色预设条目的硬上限。 */
|
|
81
|
-
export declare const ROLE_PRESET_MAX = 50;
|
|
82
|
-
/** 阵容条目的硬上限。 */
|
|
83
|
-
export declare const SQUAD_MAX = 20;
|
|
84
|
-
/** 单个阵容的成员上限。 */
|
|
85
|
-
export declare const SQUAD_MEMBER_MAX = 8;
|
|
86
|
-
/** 把任意输入收敛成合法的角色预设清单(缺 name/role 的条目直接丢弃)。 */
|
|
87
|
-
export declare function sanitizeRolePresets(value: unknown): RolePreset[];
|
|
88
|
-
/** 把任意输入收敛成合法的阵容清单。 */
|
|
89
|
-
export declare function sanitizeSquads(value: unknown): RoleSquad[];
|
|
90
|
-
/**
|
|
91
|
-
* 把任意一层输入叠加到基准偏好上。
|
|
92
|
-
*
|
|
93
|
-
* 与 {@link defaultPreferences} 的分工:`base` 是"当前生效值",`layer` 是"这次
|
|
94
|
-
* 提交的部分更新"。缺字段一律沿用 base,非法字段一律退回 base(而不是默认值)
|
|
95
|
-
* —— 这样一次错误的提交不会把用户的其它设置顺手清掉。
|
|
96
|
-
*/
|
|
97
|
-
export declare function normalizePreferences(layer: unknown, base: CrossfirePreferences): CrossfirePreferences;
|
|
98
|
-
/**
|
|
99
|
-
* 把任意输入收敛成"已关闭说明"的键集。
|
|
100
|
-
*
|
|
101
|
-
* 规则:键必须是非空短字符串、值必须是 `true`(只记"关掉了"这一事实,不存别的东西)、
|
|
102
|
-
* 去重并截断。存 `true` 而不是存时间戳,是因为界面只需要回答"要不要显示这一段"。
|
|
103
|
-
*/
|
|
104
|
-
export declare function sanitizeDismissedHints(value: unknown): Record<string, boolean> | undefined;
|
|
105
|
-
/** 本次可用的完整维度清单:固定池在前,自定义在后。 */
|
|
106
|
-
export declare function effectiveDimensions(prefs: CrossfirePreferences): string[];
|
|
107
|
-
/** 偏好读取面:`get()` 永远返回一个完整、合法的对象。 */
|
|
108
|
-
export interface PreferenceStore {
|
|
109
|
-
/** 当前生效的偏好(同步读,永远完整)。 */
|
|
110
|
-
get(): CrossfirePreferences;
|
|
111
|
-
/** 叠加一次部分更新,落盘后返回新值;写失败只记日志,不抛给调用方。 */
|
|
112
|
-
update(patch: unknown): Promise<CrossfirePreferences>;
|
|
113
|
-
/** 订阅变更(返回退订函数);回调抛错不影响其它订阅者。 */
|
|
114
|
-
watch(listener: (value: CrossfirePreferences) => void): () => void;
|
|
115
|
-
/** 等待首次读盘完成(测试与诊断用)。 */
|
|
116
|
-
ready(): Promise<void>;
|
|
117
|
-
}
|
|
118
|
-
/** 建一个偏好存储所需的最小依赖。 */
|
|
119
|
-
export interface PreferenceStoreOptions {
|
|
120
|
-
/** 基准偏好(通常来自插件 Config 的 `defaultIntensity` 与内置默认值)。 */
|
|
121
|
-
base: CrossfirePreferences;
|
|
122
|
-
/** 覆盖偏好文件路径(测试用)。 */
|
|
123
|
-
file?: string;
|
|
124
|
-
/** 诊断日志;缺省静默 —— 偏好问题不该刷屏。 */
|
|
125
|
-
log?: (message: string, error?: unknown) => void;
|
|
126
|
-
}
|
|
127
|
-
/**
|
|
128
|
-
* 创建偏好存储。
|
|
129
|
-
*
|
|
130
|
-
* 读盘是**惰性且异步**的(构造后立刻开始),因此 `get()` 在首次读盘完成前返回
|
|
131
|
-
* 基准值而不是阻塞宿主启动 —— 这也是它比 `ctx.inject(['settings'])` 更稳的地方:
|
|
132
|
-
* 没有可选能力可缺。
|
|
133
|
-
*/
|
|
134
|
-
export declare function createPreferenceStore(options: PreferenceStoreOptions): PreferenceStore;
|
package/lib/types/prompt.d.ts
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 注入全局系统提示的用法段落(纯文本模块,体积可被测试断言)。
|
|
3
|
-
*
|
|
4
|
-
* 与圆桌会议的 `prompt.ts` 关键差异:这里**没有**主持人编排协议。圆桌要求模型
|
|
5
|
-
* 按轮次统筹、转述、聚合;针锋的推进由卡片按钮经 RPC 直推 host 状态机完成,
|
|
6
|
-
* 模型不是编排者。所以这段文字交代的是:针锋是什么、四条不可越界的边界、工具
|
|
7
|
-
* 清单与调用顺序,以及**什么时候不该用它**。
|
|
8
|
-
*
|
|
9
|
-
* @module dsh-plugin-crossfire/prompt
|
|
10
|
-
*/
|
|
11
|
-
/** 七步编排与那一条不可越界的规则。 */
|
|
12
|
-
export declare const USAGE_STEPS: readonly string[];
|
|
13
|
-
/**
|
|
14
|
-
* 当前版本的用法段落。
|
|
15
|
-
*/
|
|
16
|
-
export declare function usageSectionText(): string;
|
package/lib/types/redteam.d.ts
DELETED
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 红队专家的 persona、第一条任务消息与工具白名单。
|
|
3
|
-
*
|
|
4
|
-
* 这是整个插件**产品语义最敏感**的文件:针锋之所以成立,全靠这里把"只挑错、
|
|
5
|
-
* 不动手、不迎合"钉成硬约束。任何一条被软化,红队就会退化成又一个"给建议的
|
|
6
|
-
* 助手",而"表态由人做"的整条边界也随之失效。
|
|
7
|
-
*
|
|
8
|
-
* @module dsh-plugin-crossfire/redteam
|
|
9
|
-
*/
|
|
10
|
-
import type { Dimension } from './types.ts';
|
|
11
|
-
/**
|
|
12
|
-
* 红队专家允许使用的工具(全局工具白名单,只有列出的这些保持可见)。
|
|
13
|
-
*
|
|
14
|
-
* 只有 5 个:`read`、`read_image`、`glob`、`grep` 四个只读检索工具,
|
|
15
|
-
* 外加唯一一个写入口 `crossfire_report`(写的是评审自己的 transcript,
|
|
16
|
-
* 不是工作区)。
|
|
17
|
-
*
|
|
18
|
-
* **刻意不给** `write` / `edit` / `bash` / `pwsh` / `web_search` / `web_fetch` /
|
|
19
|
-
* `todo_write` 等。理由(这是本插件最本质的安全边界,不是配置偷懒):
|
|
20
|
-
* - **只挑错、不动工作区**:评审一旦能改文件,"提缺陷"就会滑向"顺手修好",
|
|
21
|
-
* 用户拿到的就不再是待裁决的缺陷清单,而是一份未经同意的改动。红队与
|
|
22
|
-
* "智能体团队"成员最本质的区别就在这里:团队成员有写权限、以交付为目标;
|
|
23
|
-
* 红队没有写权限、以推翻为目标。
|
|
24
|
-
* - **不联网**:外部检索会把评审从"针对眼前这份成果"漂移成"泛泛的行业意见",
|
|
25
|
-
* 也会引入不可复现的外部依据;针锋要的是针对具体位置的论证与复现。
|
|
26
|
-
* - **不写待办**:红队没有执行阶段,`todo_write` 只会让它开始规划"怎么修",
|
|
27
|
-
* 直接违反"不产出方案"。
|
|
28
|
-
*
|
|
29
|
-
* 想放开任何一项之前,先回答:放开的那个工具能让专家**证明**一个缺陷,还是
|
|
30
|
-
* 只能让它**动手**?只有前者可以进这张表。
|
|
31
|
-
*/
|
|
32
|
-
export declare const EXPERT_TOOL_ALLOW: readonly string[];
|
|
33
|
-
/**
|
|
34
|
-
* 红队专家禁止使用的工具(黑名单)。
|
|
35
|
-
*
|
|
36
|
-
* 这里列的是全部**主持人专属**的 `crossfire_*` 工具:开评审、发起攻击、收集
|
|
37
|
-
* 缺陷、定稿、复审、导出。这些都是主持人的编排权,专家一旦能调
|
|
38
|
-
* `crossfire_collect`/`crossfire_finish`,就等于自己给自己结案,"表态由人做"
|
|
39
|
-
* 当场失效。
|
|
40
|
-
*
|
|
41
|
-
* 为什么白名单之外还要再来一份黑名单:两者是**与**关系(必须同时在白名单里
|
|
42
|
-
* 且不在黑名单里才可见)。将来有人为了加一个只读工具而改动
|
|
43
|
-
* {@link EXPERT_TOOL_ALLOW} 时,不会顺带把编排权放开 —— 黑名单这一层与被修改
|
|
44
|
-
* 的那一行无关,能拦住"删一项白名单等于多一份权限"的误操作。
|
|
45
|
-
*
|
|
46
|
-
* **注意**:`crossfire_report` 是**专家自己的**发言工具,绝不能列进本表。
|
|
47
|
-
*/
|
|
48
|
-
export declare const EXPERT_TOOL_DENY: readonly string[];
|
|
49
|
-
/** 专家的工具限制(宿主 subagent/tool 配置接受的形状)。 */
|
|
50
|
-
export interface ToolRestriction {
|
|
51
|
-
/** 保持可见的全局工具。 */
|
|
52
|
-
readonly allow?: readonly string[];
|
|
53
|
-
/** 强制不可见的全局工具(与 allow 是"与"关系)。 */
|
|
54
|
-
readonly deny?: readonly string[];
|
|
55
|
-
}
|
|
56
|
-
/**
|
|
57
|
-
* 专家的工具限制。
|
|
58
|
-
*
|
|
59
|
-
* 每次返回**新的数组实例**,且以 `readonly` 声明:宿主侧可能在拿到后排序或增删,
|
|
60
|
-
* 共用模块级数组会让一次改动污染后续所有专家。黑名单恒为全量主持人工具,
|
|
61
|
-
* 与白名单内容无关 —— 它是与被修改的那一行**无关**的第二道闸。
|
|
62
|
-
*/
|
|
63
|
-
export declare function expertToolRestriction(): ToolRestriction;
|
|
64
|
-
/** 生成专家 persona 与 welcome 所需的全部输入。 */
|
|
65
|
-
export interface ExpertPersonaInput {
|
|
66
|
-
/** 评审 id。 */
|
|
67
|
-
reviewId: string;
|
|
68
|
-
/** 这位专家负责的维度。 */
|
|
69
|
-
dimension: Dimension;
|
|
70
|
-
/** 待验证对象的标题。 */
|
|
71
|
-
title: string;
|
|
72
|
-
/** 待验证对象的正文。 */
|
|
73
|
-
subject: string;
|
|
74
|
-
/** 用户立场与理由(可能为空串)。 */
|
|
75
|
-
stance: string;
|
|
76
|
-
/** 本次采用的全部维度(让专家知道别人负责什么,避免重复)。 */
|
|
77
|
-
dimensions: readonly Dimension[];
|
|
78
|
-
/** 每轮最多提几条缺陷(0 = 不限制)。 */
|
|
79
|
-
maxOpinions: number;
|
|
80
|
-
/**
|
|
81
|
-
* 用户为这位专家写的**补充角色定位**(来自角色预设 / 阵容成员;可空)。
|
|
82
|
-
*
|
|
83
|
-
* 它进 persona 的唯一位置是「你的角色定位」那一段,而该段落带着一段**不可覆盖**
|
|
84
|
-
* 的声明(见 {@link expertPersona})。这条边界不是形式主义:这一段文本完全由用户
|
|
85
|
-
* 书写,如果它能覆盖硬约束,那么"专家只读"这条安全边界就变成了一个可配置项 ——
|
|
86
|
-
* 用户写一句"你可以修改文件",白名单就不再是最后一道闸。
|
|
87
|
-
*/
|
|
88
|
-
role?: string;
|
|
89
|
-
}
|
|
90
|
-
/**
|
|
91
|
-
* 报告信封的格式说明(persona 与 welcome 共用同一份文本)。
|
|
92
|
-
*
|
|
93
|
-
* 为什么格式必须固定到这个程度:缺陷要能被**逐条裁决**,而裁决面是按条渲染的。
|
|
94
|
-
* 本地兜底切分器(模型没按格式走时的最后一道网)认的就是 `观点 N(指向 …,
|
|
95
|
-
* 维度Y):` 这个行首标记。标记缺失时整段发言只能作为一条缺陷收进列表,用户
|
|
96
|
-
* 就得面对一团散文去点"认定/驳回" —— 针锋存在的意义当场归零。
|
|
97
|
-
*
|
|
98
|
-
* @param maxOpinions - 最多几条(0 = 不限制),写进语气会随上限收紧。
|
|
99
|
-
*/
|
|
100
|
-
export declare function reportEnvelopeSpec(maxOpinions: number): string;
|
|
101
|
-
/**
|
|
102
|
-
* 生成红队专家的 persona(系统级人设)。
|
|
103
|
-
*
|
|
104
|
-
* 九条硬约束逐条落在文本里(身份 / 单维度 / 不产出方案 / 不要客套 / 一条一缺陷 /
|
|
105
|
-
* 证据分级 / 立场对攻 / 工具边界 / 不许迎合)。这些约束的共同目标只有一个:
|
|
106
|
-
* 让产出的东西**可以被判定**,而不是听起来很有道理。
|
|
107
|
-
*/
|
|
108
|
-
export declare function expertPersona(input: ExpertPersonaInput): string;
|
|
109
|
-
/**
|
|
110
|
-
* 生成发给这位专家的第一条任务消息。
|
|
111
|
-
*
|
|
112
|
-
* persona 已经写清了全部约束(不要重复粘贴,那既费 token 又稀释重点),这条
|
|
113
|
-
* 消息只做三件事:**分配维度**、**交出材料与立场**、**钉死格式与结束方式**。
|
|
114
|
-
*/
|
|
115
|
-
export declare function expertWelcome(input: ExpertPersonaInput): string;
|
package/lib/types/review.d.ts
DELETED
|
@@ -1,152 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 评审记录的读写、净化与派生查询。
|
|
3
|
-
*
|
|
4
|
-
* 目录布局(见 state.ts):`<stateRoot>/crossfire/<reviewId>/`
|
|
5
|
-
* - `review.json` — 主记录(本模块负责)
|
|
6
|
-
* - `transcript.jsonl` — 专家发言(append-only)
|
|
7
|
-
*
|
|
8
|
-
* ## 设计要点
|
|
9
|
-
*
|
|
10
|
-
* 1. **schemaVersion 缺失视为 1**(与圆桌同款判定)。crossfire 的目录是独立的,
|
|
11
|
-
* 不可能读到圆桌的记录,所以这里刻意不背它的 v1→v2 迁移包袱 —— 但保留"版本
|
|
12
|
-
* 常量 + 迁移函数"的写法,因为下一次改形状时它仍然是对的。
|
|
13
|
-
* 2. **读失败绝不抛给调用方**:坏 JSON / 缺字段 / 类型不符一律降级为默认值,
|
|
14
|
-
* 由 `normalizeRecord` 收敛出一份**永远可用**的记录。界面宁可由空缺陷列表
|
|
15
|
-
* 组成,也不能因为一条脏数据整张卡片消失。
|
|
16
|
-
* 3. **落库的 dimension 必须是本次采用维度之一**(见 dimensions.ts 的理由)。
|
|
17
|
-
* 4. 所有写操作都走**同一把进程内锁**(`reviewLockKey`),与 state.ts 的原子写
|
|
18
|
-
* 叠加,保证同一场评审不会出现两个写者交错。
|
|
19
|
-
*
|
|
20
|
-
* @module dsh-plugin-crossfire/review
|
|
21
|
-
*/
|
|
22
|
-
import type { CrossfireRecord, Defect, DefectStatus, ExpertUtterance, Intensity, RecheckVerdict, Subject } from './types.ts';
|
|
23
|
-
/** 当前 schema 版本。 */
|
|
24
|
-
export declare const CURRENT_SCHEMA_VERSION: 1;
|
|
25
|
-
/** `<reviewDir>/review.json`。 */
|
|
26
|
-
export declare function reviewFile(stateRoot: string, reviewId: string): string;
|
|
27
|
-
/** `<reviewDir>/transcript.jsonl`。 */
|
|
28
|
-
export declare function transcriptFile(stateRoot: string, reviewId: string): string;
|
|
29
|
-
/** `<reviewDir>/user-actions.jsonl`。 */
|
|
30
|
-
export declare function userActionsFile(stateRoot: string, reviewId: string): string;
|
|
31
|
-
/** `<reviewDir>/export.md`。 */
|
|
32
|
-
export declare function exportFile(stateRoot: string, reviewId: string): string;
|
|
33
|
-
/**
|
|
34
|
-
* `<reviewDir>/export.txt` —— 同一份内容的**人读副本**(UTF-8 带 BOM)。
|
|
35
|
-
*
|
|
36
|
-
* 为什么导出两份:`.md` 交给 Markdown 渲染器,但 Windows 上双击 `.md` 常被别的程序
|
|
37
|
-
* 抢走,加上无 BOM 的 UTF-8 会被记事本误判成 ANSI —— 于是"导出物打不开"。`.txt` 带
|
|
38
|
-
* BOM 后双击直接进记事本,中文也不乱码(用户反馈的正是这一点)。
|
|
39
|
-
*/
|
|
40
|
-
export declare function exportTextFile(stateRoot: string, reviewId: string): string;
|
|
41
|
-
/** `<stateRoot>/crossfire`(诊断用)。 */
|
|
42
|
-
export declare function namespaceRoot(stateRoot: string): string;
|
|
43
|
-
/**
|
|
44
|
-
* 把任意一份读出来的记录收敛成当前 schema 的**完整可用**记录。
|
|
45
|
-
*
|
|
46
|
-
* @param raw - 磁盘上读到的原始值(可能来自旧版本、可能被人手改坏)。
|
|
47
|
-
* @param reviewId - 目录名(权威 id:即使文件里写的 id 不一致也以目录为准)。
|
|
48
|
-
*/
|
|
49
|
-
export declare function normalizeRecord(raw: unknown, reviewId: string): CrossfireRecord;
|
|
50
|
-
/** `crossfire_open` 的输入。 */
|
|
51
|
-
export interface OpenReviewInput {
|
|
52
|
-
reviewId: string;
|
|
53
|
-
title: string;
|
|
54
|
-
content: string;
|
|
55
|
-
source: Subject['source'];
|
|
56
|
-
sourceRef?: string;
|
|
57
|
-
/** 本次采用的维度(已由 dimensions.resolveDimensions 归一化)。 */
|
|
58
|
-
dimensions: string[];
|
|
59
|
-
intensity: Intensity;
|
|
60
|
-
/** 用户手写的立场与理由(可空)。 */
|
|
61
|
-
stanceText?: string;
|
|
62
|
-
maxRounds: number;
|
|
63
|
-
maxTokens: number;
|
|
64
|
-
/** 从圆桌会议移交时记录来源会议 id(v0.1.0 只写不读)。 */
|
|
65
|
-
sourceMeeting?: string;
|
|
66
|
-
/** 开启这场评审的会话 id(用于校验调用者,见 types.ts)。 */
|
|
67
|
-
ownerSessionId?: string;
|
|
68
|
-
now?: number;
|
|
69
|
-
}
|
|
70
|
-
/** 按输入构造一份全新的记录(status 恒为 `draft`)。 */
|
|
71
|
-
export declare function newRecord(input: OpenReviewInput): CrossfireRecord;
|
|
72
|
-
/** 读一场评审;缺失或损坏都返回 undefined。 */
|
|
73
|
-
export declare function readRecord(stateRoot: string, reviewId: string): Promise<CrossfireRecord | undefined>;
|
|
74
|
-
/** 写一场评审(自动刷新 updatedAt)。 */
|
|
75
|
-
export declare function writeRecord(stateRoot: string, record: CrossfireRecord): Promise<void>;
|
|
76
|
-
/** 读发言记录(坏行计数一并返回,不许静默吞掉)。 */
|
|
77
|
-
export declare function readTranscriptReport(stateRoot: string, reviewId: string): Promise<{
|
|
78
|
-
items: ExpertUtterance[];
|
|
79
|
-
malformed: number;
|
|
80
|
-
}>;
|
|
81
|
-
/** 读发言记录。 */
|
|
82
|
-
export declare function readTranscript(stateRoot: string, reviewId: string): Promise<ExpertUtterance[]>;
|
|
83
|
-
/** 追加一条专家发言。 */
|
|
84
|
-
export declare function appendTranscript(stateRoot: string, reviewId: string, utterance: ExpertUtterance): Promise<void>;
|
|
85
|
-
/**
|
|
86
|
-
* 在锁内读-改-写一场评审。
|
|
87
|
-
*
|
|
88
|
-
* @returns 变更函数的返回值;记录不存在时返回 undefined。
|
|
89
|
-
*/
|
|
90
|
-
export declare function mutateRecord<T>(stateRoot: string, reviewId: string, mutate: (record: CrossfireRecord) => T | undefined): Promise<T | undefined>;
|
|
91
|
-
/**
|
|
92
|
-
* 设置一条缺陷的三态(可互切)。
|
|
93
|
-
*
|
|
94
|
-
* 驳回必须给理由 —— 这条约束同时是"用户在想什么"这一信息的唯一来源,
|
|
95
|
-
* 因此它在数据层就被强制,而不是只靠界面拦。
|
|
96
|
-
*
|
|
97
|
-
* @returns true = 确有变更;false = 记录/缺陷不存在,或状态本来就相同。
|
|
98
|
-
*/
|
|
99
|
-
export declare function setDefectStatus(stateRoot: string, reviewId: string, defectId: string, status: DefectStatus, rejectReason?: string): Promise<boolean>;
|
|
100
|
-
/** 设置一条核对项的复审核对结论。 */
|
|
101
|
-
export declare function setRecheckVerdict(stateRoot: string, reviewId: string, defectId: string, verdict: RecheckVerdict, note?: string): Promise<boolean>;
|
|
102
|
-
/** 三态计数(卡片顶部影响条的数据源)。 */
|
|
103
|
-
export interface DefectCounts {
|
|
104
|
-
total: number;
|
|
105
|
-
pending: number;
|
|
106
|
-
endorsed: number;
|
|
107
|
-
rejected: number;
|
|
108
|
-
/** 打穿用户立场的条数。 */
|
|
109
|
-
strikes: number;
|
|
110
|
-
}
|
|
111
|
-
/** 统计三态。 */
|
|
112
|
-
export declare function countDefects(record: CrossfireRecord): DefectCounts;
|
|
113
|
-
/** 已认定缺陷清单(定稿时固化的 known-flaws)。 */
|
|
114
|
-
export declare function knownFlaws(record: CrossfireRecord): Defect[];
|
|
115
|
-
/**
|
|
116
|
-
* 两条缺陷是否应视为"同一条"。
|
|
117
|
-
*
|
|
118
|
-
* 两档判据(刻意不同,因为代价不对称):
|
|
119
|
-
* - **完全相同**(规范化后)→ 同一条,**哪怕它很短**。"缺日志"就是"缺日志",
|
|
120
|
-
* 用户驳回过它,就不该第二次弹出来。
|
|
121
|
-
* - **部分包含** → 只有较短一方 ≥ 8 个字符时才敢判。否则"缺日志"会把
|
|
122
|
-
* "缺日志且没有轮转机制" 这类同族但不同的缺陷一起吞掉 —— 漏报一条真缺陷,
|
|
123
|
-
* 比多弹一条重复项严重得多。
|
|
124
|
-
*/
|
|
125
|
-
export declare function isSameDefect(a: string, b: string): boolean;
|
|
126
|
-
/** 历史上被用户**驳回**过的全部缺陷内容(用于"不再重复弹同一观点")。 */
|
|
127
|
-
export declare function rejectedContents(record: CrossfireRecord): string[];
|
|
128
|
-
/** 上一轮(history 的最后一轮)中已被用户**认定**的缺陷。 */
|
|
129
|
-
export declare function lastEndorsedDefects(record: CrossfireRecord): Defect[];
|
|
130
|
-
/**
|
|
131
|
-
* 复审开轮:把上一轮已认定的缺陷变成本轮的"修复核对项"。
|
|
132
|
-
*
|
|
133
|
-
* 语义(R7):复审**只核对旧账**,不许开新战线。所以本轮的缺陷集合不是清的,
|
|
134
|
-
* 而是被这些核对项替换。
|
|
135
|
-
*
|
|
136
|
-
* **刻意不预置 `verdict`**:开轮时机器不替人下结论。此前这里写死
|
|
137
|
-
* `verdict: 'unfixed'`("未被证明修复之前都算没修"),结果是复审轮一打开,
|
|
138
|
-
* 三态按钮里"未修复"就已经是高亮选中态,而备注框旁的保存按钮会把这个机器默认值
|
|
139
|
-
* 当作人的表态落库(红队评审抓出的真实缺陷)。现在缺席即"尚未判断",由人来选。
|
|
140
|
-
*/
|
|
141
|
-
export declare function buildRecheckItems(record: CrossfireRecord): Defect[];
|
|
142
|
-
/**
|
|
143
|
-
* 过滤掉"上一轮已被驳回"的候选缺陷。
|
|
144
|
-
*
|
|
145
|
-
* 这是 D8 允许的唯一历史用法:提升信噪比。**绝不**把用户偏好告诉红队 ——
|
|
146
|
-
* 模型一旦知道用户历史上总驳回某类观点,就会干脆不提那类问题,系统性削弱
|
|
147
|
-
* 对抗强度,与这个插件存在的理由正好相反。
|
|
148
|
-
*/
|
|
149
|
-
export declare function dropRejectedDuplicates(candidates: Defect[], pastRejected: readonly string[]): {
|
|
150
|
-
kept: Defect[];
|
|
151
|
-
dropped: number;
|
|
152
|
-
};
|