@y9931666/dsh-plugin-crossfire 0.1.3 → 0.1.5
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/index.js +34 -16
- package/lib/types/budget.d.ts +86 -0
- package/lib/types/client/ReviewCard.d.ts +41 -0
- package/lib/types/client/SettingsView.d.ts +34 -0
- package/lib/types/client/brand.d.ts +55 -0
- package/lib/types/client/defectTitle.d.ts +32 -0
- package/lib/types/client/icons.d.ts +23 -0
- package/lib/types/client/index.d.ts +21 -0
- package/lib/types/client/locales.d.ts +19 -0
- package/lib/types/client/merge.d.ts +27 -0
- package/lib/types/client/promptView.d.ts +63 -0
- package/lib/types/client/session-cache.d.ts +49 -0
- package/lib/types/client/slot-boundary.d.ts +46 -0
- package/lib/types/client/wire.d.ts +192 -0
- package/lib/types/dimensions.d.ts +47 -0
- package/lib/types/engine.d.ts +172 -0
- package/lib/types/etag.d.ts +40 -0
- package/lib/types/expert-events.d.ts +110 -0
- package/lib/types/export-md.d.ts +21 -0
- package/lib/types/harness-compat.d.ts +104 -0
- package/lib/types/index.d.ts +89 -0
- package/lib/types/members.d.ts +143 -0
- package/lib/types/noise.d.ts +29 -0
- package/lib/types/preferences.d.ts +134 -0
- package/lib/types/prompt.d.ts +16 -0
- package/lib/types/redteam.d.ts +115 -0
- package/lib/types/review.d.ts +152 -0
- package/lib/types/rpc.d.ts +176 -0
- package/lib/types/split.d.ts +156 -0
- package/lib/types/state.d.ts +84 -0
- package/lib/types/tools.d.ts +59 -0
- package/lib/types/types.d.ts +308 -0
- package/lib/types/version.d.ts +18 -0
- package/lib/types/web-guard.d.ts +55 -0
- package/package.json +36 -36
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 宿主兼容与能力边界(Host compatibility boundary)——「可选能力的懒探测层,绝不成为加载门禁」。
|
|
3
|
+
*
|
|
4
|
+
* **所有与 DSH 版本相关的形状都集中在这个文件里。**
|
|
5
|
+
* 理由与做法直接借鉴社区项目 dsh-agent-teams 的 `src/harness-compat.ts`:
|
|
6
|
+
*
|
|
7
|
+
* > Keep version-specific shapes here: **API presence alone is not a promise of
|
|
8
|
+
* > support for future versions.**
|
|
9
|
+
*
|
|
10
|
+
* 我们的宿主是 **rc 线**,API 尚未冻结,破坏性变更属于常态。所以在写任何
|
|
11
|
+
* `ctx.get(...)` / `ctx.on(...)` / 服务方法调用之前,先问一句:**这个形状在别的
|
|
12
|
+
* rc 上还成立吗?** 如果答案需要翻源码,那它就应该写进本文件,而不是散进业务代码。
|
|
13
|
+
*
|
|
14
|
+
* 两条铁律:
|
|
15
|
+
* 1. **探测绝不成为新的加载门禁**。这里没有 `inject`、没有静态导入宿主包,
|
|
16
|
+
* 缺失的能力一律降级(返回 `undefined`),进程照常起来 ——「整个界面消失」
|
|
17
|
+
* 这类事故的根因就是可选能力被当成必需能力。本文件只做**懒探测**:
|
|
18
|
+
* 调用方用到哪条路由、哪份工作区列表,才在那一步探测一次。
|
|
19
|
+
* 2. **降级由调用方留痕**。这里刻意**不打日志、不维护审计表**(探测点常落在
|
|
20
|
+
* tick / 轮询路径上,见文末说明):本层只负责把"能不能用"如实回答成
|
|
21
|
+
* `undefined` 或服务面,把"为什么没用上"交给拿到 `undefined` 的调用方
|
|
22
|
+
* 记一条告警,让"某个功能为什么没生效"有一个能查的答案。
|
|
23
|
+
*
|
|
24
|
+
* @module @y9931666/dsh-plugin-crossfire/harness-compat
|
|
25
|
+
*/
|
|
26
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
27
|
+
/**
|
|
28
|
+
* 读一个**可选**宿主服务。
|
|
29
|
+
*
|
|
30
|
+
* 统一走 `ctx.get`(cordis 核心、非严格:缺失返回 `undefined`)而不是属性访问
|
|
31
|
+
* —— 后者在没有 `inject` 时会**直接抛错**:
|
|
32
|
+
* `cannot get property "connection" without inject`。
|
|
33
|
+
*
|
|
34
|
+
* 三种失败一律折成 `undefined`,**本函数不产生任何副作用**:
|
|
35
|
+
* - 宿主 `Context` 上连 `get` 都没有(形状变化);
|
|
36
|
+
* - `ctx.get(name)` 自己抛错;
|
|
37
|
+
* - 服务未挂载(返回 `null` / `undefined`)。
|
|
38
|
+
* 调用方拿到 `undefined` 后自行决定降级行为与是否留下一条诊断。
|
|
39
|
+
*/
|
|
40
|
+
export declare function optionalService<T>(ctx: Context, name: string): T | undefined;
|
|
41
|
+
/** 可选服务键的结构式候选(宿主可能改名;按序取第一个可用的)。 */
|
|
42
|
+
export declare const WEB_SERVER_KEYS: readonly ["webServer", "httpServer"];
|
|
43
|
+
export declare const WORKSPACE_KEYS: readonly ["workspaceRegistry", "workspace"];
|
|
44
|
+
/** webServer 服务面(只声明我们真正调用的部分)。
|
|
45
|
+
*
|
|
46
|
+
* `register` 的返回值刻意只声明为 `() => void`:宿主不同 rc 上返回过
|
|
47
|
+
* disposer 与 `unknown`,而调用处一律以 `ctx.effect(() => webServer.register(…))`
|
|
48
|
+
* 的形式**忽略返回值**——effect 自带卸载。声明成 `unknown` 会让
|
|
49
|
+
* `ctx.effect` 的重载解析失败(它要求回调返回具体 Effect,不接受 unknown),
|
|
50
|
+
* 所以这里保留"当它是清理函数"的写法。 */
|
|
51
|
+
export interface WebRouteHost {
|
|
52
|
+
register(route: {
|
|
53
|
+
kind: 'exact';
|
|
54
|
+
path: string;
|
|
55
|
+
handler: (req: import('node:http').IncomingMessage, res: import('node:http').ServerResponse) => Promise<void> | void;
|
|
56
|
+
}): () => void;
|
|
57
|
+
}
|
|
58
|
+
/** workspace 注册表面。 */
|
|
59
|
+
export interface WorkspaceListHost {
|
|
60
|
+
list(): {
|
|
61
|
+
title: string;
|
|
62
|
+
path: string;
|
|
63
|
+
}[];
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* 解析 webServer 服务面。
|
|
67
|
+
*
|
|
68
|
+
* 服务键是**结构式候选**而非固定名:宿主可能把 `webServer` 换成别的名字,
|
|
69
|
+
* 所以按 `WEB_SERVER_KEYS` 顺序探测。headless profile 下取不到属预期,
|
|
70
|
+
* 只降级不报错(返回 `undefined`,本函数不抛错也不打日志)。
|
|
71
|
+
*/
|
|
72
|
+
export declare function webRouteHostOf(ctx: Context): WebRouteHost | undefined;
|
|
73
|
+
/** 解析 workspace 注册表面(同上:结构式候选 + 静默降级为 `undefined`)。 */
|
|
74
|
+
export declare function workspaceRegistryOf(ctx: Context): WorkspaceListHost | undefined;
|
|
75
|
+
/**
|
|
76
|
+
* 判定一个 `rpc.handle(...)` 返回值能不能当清理函数用。
|
|
77
|
+
*
|
|
78
|
+
* 宿主签名在 rc 线上变过:0.1.1 时代第三参 `{ authority }` 被移除,返回值在
|
|
79
|
+
* 不同版本里出现过 `disposer` / `undefined` / Promise。这里只做**形状判定**,
|
|
80
|
+
* 不 import 宿主类型:合法就交出去,不合法就交给调用方记一条降级。
|
|
81
|
+
*/
|
|
82
|
+
export declare function asDisposer(value: unknown): (() => void) | undefined;
|
|
83
|
+
/**
|
|
84
|
+
* 在可选的清理函数存在时注册一个 effect。
|
|
85
|
+
*
|
|
86
|
+
* `connection.rpc.handle` 的返回值形状随版本变化,直接 `ctx.effect(() => () =>
|
|
87
|
+
* release())` 会在返回值不是函数时静默留下一个永远不执行的清理器(通道泄漏)。
|
|
88
|
+
*
|
|
89
|
+
* @returns `true` 表示已注册 effect;`false` 表示返回值不是清理函数、**没有**注册
|
|
90
|
+
* (调用方据此决定是否报告一次降级)。
|
|
91
|
+
*/
|
|
92
|
+
export declare function effectWithOptionalDisposer(ctx: {
|
|
93
|
+
effect(callback: () => () => void, label?: string): unknown;
|
|
94
|
+
}, disposer: unknown, label: string): boolean;
|
|
95
|
+
/**
|
|
96
|
+
* 圆桌插件曾在本文件维护一张进程级能力审计表(`capabilityAudit` /
|
|
97
|
+
* `noteCapability` / `formatCapabilityAudit`),让 `status` 与 `doctor` 能打印
|
|
98
|
+
* "每个探测点第一次被看到时是什么状态"。
|
|
99
|
+
*
|
|
100
|
+
* crossfire 不搬这套:本文件现在的定位是**纯粹的可选能力懒探测层** ——
|
|
101
|
+
* 只回答"这个服务此刻能不能用",不留全局状态、不写日志、不参与加载决策。
|
|
102
|
+
* 诊断信息由**探测的调用方**在拿到 `undefined` 时按自己的上下文报告;
|
|
103
|
+
* 这样探测层不持有跨调用状态,也不会在轮询路径上积累内存。
|
|
104
|
+
*/
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Crossfire(针锋)—— host 插件入口。
|
|
3
|
+
*
|
|
4
|
+
* 它是一个**只做对抗式评审**的 host 插件:把一个已定稿的成果摆上台,让红队
|
|
5
|
+
* 专家从指定维度分头攻击,把攻击拆成逐条可裁决的缺陷,由人逐条认定或驳回。
|
|
6
|
+
*
|
|
7
|
+
* 与圆桌会议(`@y9931666/dsh-plugin-roundtable`)的关系:**零代码耦合**。针锋是从
|
|
8
|
+
* 圆桌里长出来的一件东西,但它不 import 圆桌的任何模块、不读圆桌的状态目录、
|
|
9
|
+
* 不改圆桌的任何文件。两者共存的唯一代价是各自维护一份内核。
|
|
10
|
+
*
|
|
11
|
+
* 批 1 交付:插件骨架(Config + 偏好存储 + 一条 RPC web 路由 + 系统提示段)。
|
|
12
|
+
* 批 2 加数据模型与 `crossfire_*` 工具;批 3 加轨迹内卡片;批 4 加立场区与
|
|
13
|
+
* 设置页全量。
|
|
14
|
+
*
|
|
15
|
+
* @module dsh-plugin-crossfire
|
|
16
|
+
*/
|
|
17
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
18
|
+
import z from '@deepseek-ai/schemastery';
|
|
19
|
+
/** 插件名(cordis entry id / 日志前缀 / bundle patch 里的 id)。 */
|
|
20
|
+
export declare const name = "crossfire";
|
|
21
|
+
/**
|
|
22
|
+
* REQUIRED services only —— 这份清单是**加载门禁**,所以它必须短。
|
|
23
|
+
*
|
|
24
|
+
* cordis 没有"可选注入"这种形式(`Inject` 只有 `string[] | Record`,而 record
|
|
25
|
+
* 形式是拦截配置),因此写在这里的每个名字都会让整个插件 fiber 保持 PENDING
|
|
26
|
+
* 直到该服务存在:没有工具、没有 `/plugins/dsh-plugin-crossfire/*` 路由、没有
|
|
27
|
+
* 卡片、也没有设置页。而缺一个**可选**能力却写在这里,在用户眼里就是"针锋整个
|
|
28
|
+
* 不见了",且任何地方都不报错。
|
|
29
|
+
*
|
|
30
|
+
* 所以 `llm` / `webServer` / `workspaceRegistry` / `connection` 一律**不列**,
|
|
31
|
+
* 全部经 `ctx.get(...)` 或 harness-compat 的探测层懒取:
|
|
32
|
+
* - `llm` 缺席时,观点拆分退到本地启发式兜底(零 token、确定性);
|
|
33
|
+
* - `webServer` 缺席时,只有设置页与卡片失联,host 侧与工具照常工作;
|
|
34
|
+
* - `connection` 缺席时,RPC 只走插件自有路由。
|
|
35
|
+
*/
|
|
36
|
+
export declare const inject: string[];
|
|
37
|
+
/** 插件配置。 */
|
|
38
|
+
export interface Config {
|
|
39
|
+
/** 评审状态目录名(相对会话工作区,默认 `.crossfire`)。 */
|
|
40
|
+
stateDir?: string;
|
|
41
|
+
/** `ctx.subagents` 里用于拉起红队专家的 provider(默认 `spawn`)。 */
|
|
42
|
+
memberProvider?: string;
|
|
43
|
+
/** 一场评审的专家数硬上限(默认 7,与固定维度池同宽)。 */
|
|
44
|
+
maxExperts?: number;
|
|
45
|
+
/** 默认攻击强度档(默认 `standard`)。 */
|
|
46
|
+
defaultIntensity?: 'quick' | 'standard' | 'deep';
|
|
47
|
+
/** 专家子代理的委派深度上限(默认 1:专家不得再招人)。 */
|
|
48
|
+
memberMaxDepth?: number;
|
|
49
|
+
/** 用法段在全局系统提示里的排序位(默认 117,避开圆桌的 116)。 */
|
|
50
|
+
promptSectionOrder?: number;
|
|
51
|
+
/**
|
|
52
|
+
* 专家**继承主持人**时实际生效的模型路由(provider / model)。
|
|
53
|
+
*
|
|
54
|
+
* 它只影响可观测性:开打时把这个生效值写回专家记录,卡片才能把"继承"画成真实
|
|
55
|
+
* 品牌(DeepSeek 的蓝色 DS 块),而不是一个中性灰 `?`。取不到时不写,界面会退化成
|
|
56
|
+
* `?` + `title="继承主持人"`,不会显示一个并非事实的路由。
|
|
57
|
+
*/
|
|
58
|
+
defaultProvider?: string;
|
|
59
|
+
defaultModel?: string;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* 配置 schema(schemastery)。
|
|
63
|
+
*
|
|
64
|
+
* 刻意**不**写 `: z<Config>` 注解:schemastery 的 `.default()` 会把字段推导成
|
|
65
|
+
* `T | Volatile<T>`(例如 `string | Volatile<string>`),而手写接口写的是
|
|
66
|
+
* `string | undefined`,二者不兼容会让 `tsc --emitDeclarationOnly` 以 TS2322
|
|
67
|
+
* 失败 —— 注意 `--noEmit` **不报**这个错,所以它只在生成声明时暴露。
|
|
68
|
+
* schema 自己就是配置形状的权威来源;下面的 `Config` 接口只用于 `apply` 的签名。
|
|
69
|
+
*/
|
|
70
|
+
export declare const Config: z<Schemastery.ObjectS<{
|
|
71
|
+
stateDir: z<string, string>;
|
|
72
|
+
memberProvider: z<string, string>;
|
|
73
|
+
maxExperts: z<number, number>;
|
|
74
|
+
defaultIntensity: z<"quick" | "standard" | "deep", "quick" | "standard" | "deep">;
|
|
75
|
+
memberMaxDepth: z<number, number>;
|
|
76
|
+
defaultProvider: z<string, string>;
|
|
77
|
+
defaultModel: z<string, string>;
|
|
78
|
+
promptSectionOrder: z<number, number>;
|
|
79
|
+
}>, Schemastery.ObjectT<{
|
|
80
|
+
stateDir: z<string, string>;
|
|
81
|
+
memberProvider: z<string, string>;
|
|
82
|
+
maxExperts: z<number, number>;
|
|
83
|
+
defaultIntensity: z<"quick" | "standard" | "deep", "quick" | "standard" | "deep">;
|
|
84
|
+
memberMaxDepth: z<number, number>;
|
|
85
|
+
defaultProvider: z<string, string>;
|
|
86
|
+
defaultModel: z<string, string>;
|
|
87
|
+
promptSectionOrder: z<number, number>;
|
|
88
|
+
}>>;
|
|
89
|
+
export declare function apply(ctx: Context, config: Config): void;
|
|
@@ -0,0 +1,143 @@
|
|
|
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;
|
|
@@ -0,0 +1,29 @@
|
|
|
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;
|
|
@@ -0,0 +1,134 @@
|
|
|
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;
|
|
@@ -0,0 +1,16 @@
|
|
|
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;
|
|
@@ -0,0 +1,115 @@
|
|
|
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;
|