@y9931666/dsh-plugin-roundtable 1.0.0-rc.2
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/LICENSE +21 -0
- package/README.md +328 -0
- package/cordis.patch.yml +22 -0
- package/lib/client.js +4276 -0
- package/lib/edge-helper-iqXOG5o_.js +12 -0
- package/lib/index.js +4535 -0
- package/lib/state-BzBQig2q.js +479 -0
- package/lib/state-pJShWoUr.js +3 -0
- package/lib/types/aggregator.d.ts +15 -0
- package/lib/types/budget.d.ts +46 -0
- package/lib/types/charter.d.ts +13 -0
- package/lib/types/client/RoundTableSettings.d.ts +14 -0
- package/lib/types/client/RoundTableView.d.ts +27 -0
- package/lib/types/client/brand-logos.generated.d.ts +5 -0
- package/lib/types/client/index.d.ts +23 -0
- package/lib/types/client/locales.d.ts +14 -0
- package/lib/types/client/slot-boundary.d.ts +49 -0
- package/lib/types/client/tool-views-model.d.ts +65 -0
- package/lib/types/client/tool-views.d.ts +31 -0
- package/lib/types/client/wire.d.ts +238 -0
- package/lib/types/edge-helper.d.ts +7 -0
- package/lib/types/harness-compat.d.ts +204 -0
- package/lib/types/index.d.ts +53 -0
- package/lib/types/kb-digest.d.ts +47 -0
- package/lib/types/members.d.ts +83 -0
- package/lib/types/node-events.d.ts +90 -0
- package/lib/types/plan.d.ts +86 -0
- package/lib/types/preferences.d.ts +143 -0
- package/lib/types/prompt.d.ts +52 -0
- package/lib/types/proxy-thinking.d.ts +12 -0
- package/lib/types/review-split.d.ts +61 -0
- package/lib/types/rpc.d.ts +89 -0
- package/lib/types/skills.d.ts +61 -0
- package/lib/types/snapshot.d.ts +96 -0
- package/lib/types/state.d.ts +142 -0
- package/lib/types/token-usage.d.ts +71 -0
- package/lib/types/tools.d.ts +59 -0
- package/lib/types/types.d.ts +330 -0
- package/lib/types/version.d.ts +21 -0
- package/lib/types/web-guard.d.ts +37 -0
- package/lib/types/workspace-candidates.d.ts +17 -0
- package/lib/workspace-candidates-B7isleLt.js +3 -0
- package/lib/workspace-candidates-DNmGPpSc.js +20 -0
- package/package.json +161 -0
- package/release-notes/v0.2.0.md +39 -0
- package/release-notes/v0.2.1.md +39 -0
- package/release-notes/v0.2.2.md +54 -0
- package/release-notes/v0.2.21.md +42 -0
- package/release-notes/v0.2.31.md +70 -0
- package/release-notes/v0.2.32.md +52 -0
- package/release-notes/v0.2.35.md +116 -0
- package/release-notes/v0.2.36.md +84 -0
- package/release-notes/v1.0.0-rc.1.md +165 -0
- package/release-notes/v1.0.0-rc.2.md +180 -0
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 知识库摘要缓存(C2):让主持人不必为同一个文件重复读两遍。
|
|
3
|
+
*
|
|
4
|
+
* 分工刻意划清——本模块**不调用任何 LLM**,只做三件事:
|
|
5
|
+
* 1. 失效判定:`path + size + mtimeMs` 任一变化即失效(宿主
|
|
6
|
+
* `listKbDirectory()` 本来就在 stat 每个条目,比对不额外产生 IO);
|
|
7
|
+
* 2. 合并与淘汰:同 path 覆盖;超条数 / 超长度按最旧淘汰;
|
|
8
|
+
* 3. 把缓存条目与磁盘现状比对,供 `roundtable_status` 直接展示
|
|
9
|
+
* "哪条还能用、哪条得重读"。
|
|
10
|
+
*
|
|
11
|
+
* digest 正文由主持人读过文件后写下(`roundtable_kb_digest` 工具)。
|
|
12
|
+
* 缓存**不参与预算计数**:它不消耗模型 token。
|
|
13
|
+
*
|
|
14
|
+
* @module dsh-plugin-roundtable/kb-digest
|
|
15
|
+
*/
|
|
16
|
+
import type { KbDigestEntry } from './types.ts';
|
|
17
|
+
/** 缓存条目数上限;超出按 `ts` 最旧淘汰。 */
|
|
18
|
+
export declare const KB_DIGEST_MAX_ENTRIES = 50;
|
|
19
|
+
/** 单条摘要字符上限;超出截断(缓存是"要点",不是全文副本)。 */
|
|
20
|
+
export declare const KB_DIGEST_MAX_CHARS = 2000;
|
|
21
|
+
/** 一条缓存条目,外加它相对磁盘现状是否仍然有效。 */
|
|
22
|
+
export interface KbDigestStatusEntry extends KbDigestEntry {
|
|
23
|
+
/** true = 失效键未变,摘要可直接使用;false = 文件变了/读不到,必须重读。 */
|
|
24
|
+
valid: boolean;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* 失效判定(纯函数):失效键完全一致才算命中。
|
|
28
|
+
*
|
|
29
|
+
* `current === undefined`(文件已不存在 / 读不到)一律视为失效——宁可让
|
|
30
|
+
* 主持人多读一次,也不要拿一份可能过期的摘要去回答专家。
|
|
31
|
+
*/
|
|
32
|
+
export declare function digestIsFresh(entry: Pick<KbDigestEntry, 'size' | 'mtimeMs'>, current: {
|
|
33
|
+
size: number;
|
|
34
|
+
mtimeMs: number;
|
|
35
|
+
} | undefined): boolean;
|
|
36
|
+
/**
|
|
37
|
+
* 合并一条摘要(纯函数):同 `path` 覆盖,其余按 `ts` 从新到旧保留,
|
|
38
|
+
* 超出 `maxEntries` 的部分淘汰最旧。返回新数组,不改动入参。
|
|
39
|
+
*/
|
|
40
|
+
export declare function mergeKbDigestEntry(entries: readonly KbDigestEntry[], next: KbDigestEntry, maxEntries?: number): KbDigestEntry[];
|
|
41
|
+
/**
|
|
42
|
+
* 用磁盘现状标注每条缓存的 `valid`。
|
|
43
|
+
*
|
|
44
|
+
* 每条一次 `stat`:条目上限是 {@link KB_DIGEST_MAX_ENTRIES}(50),
|
|
45
|
+
* 而且只在 `roundtable_status` 里调用,成本可忽略。
|
|
46
|
+
*/
|
|
47
|
+
export declare function evaluateKbDigests(entries: readonly KbDigestEntry[]): Promise<KbDigestStatusEntry[]>;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Expert-node subagent lifecycle: spawn one continuable child per node,
|
|
3
|
+
* deliver messages into its next FIFO turn, interrupt it, and observe its
|
|
4
|
+
* live activity. Mirrors the AgentTeams member pattern against the
|
|
5
|
+
* 0.1.2-rc.1 subagent seam.
|
|
6
|
+
*
|
|
7
|
+
* Node personas are the《全局协作总纲》plus node-specific rules; the charter
|
|
8
|
+
* is injected so every node carries the four-section protocol.
|
|
9
|
+
* @module dsh-plugin-roundtable/members
|
|
10
|
+
*/
|
|
11
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
12
|
+
import type { Agent } from '@deepseek-ai/dsh-agent';
|
|
13
|
+
import type { ToolRestriction } from '@deepseek-ai/dsh-tools';
|
|
14
|
+
import type { Meeting, MeetingNode, SkillDelivery } from './types.ts';
|
|
15
|
+
import { CAPTAIN_KEY } from './types.ts';
|
|
16
|
+
import { nodePersona, nodeWelcome } from './prompt.ts';
|
|
17
|
+
import type { ExpertLimits, NodeSkillContext } from './prompt.ts';
|
|
18
|
+
/**
|
|
19
|
+
* 本插件自己声明的 `MessageSourceMap` 条目。
|
|
20
|
+
*
|
|
21
|
+
* 0.1.5-rc.3 的宿主有一个 catch-all 的 `plugin` kind
|
|
22
|
+
* (`{ kind: 'plugin', plugin: string }`),本插件曾用它标注注入给主持人的
|
|
23
|
+
* steering 内容。**0.2.0-rc.2 移除了它**:`@deepseek-ai/dsh-llm` 的
|
|
24
|
+
* `MessageSourceMap` 只保留 `user` / `model` / `tool` / `system-prompt`,
|
|
25
|
+
* 其余一律由**生产者在自己的模块里声明**——宿主的 `tool-registry`、
|
|
26
|
+
* `ptc-mode`、`agent-message`、`subagent-settled` 都是这么做的。原文注释写得很
|
|
27
|
+
* 直白:"there is no shared catch-all `plugin` kind"。
|
|
28
|
+
*
|
|
29
|
+
* 所以这里按同一模式补上本插件的 kind,而不是退化成匿名的 `user` 消息:
|
|
30
|
+
* 转录消费者据此把"插件注入的提示"与"用户自己说的话"分开显示。
|
|
31
|
+
* 运行时无影响——`kind` 是归属标签,未知值一律 fall through。
|
|
32
|
+
*/
|
|
33
|
+
declare module '@deepseek-ai/dsh-llm' {
|
|
34
|
+
interface MessageSourceMap {
|
|
35
|
+
/** 圆桌会议插件注入的 steering / 系统侧提示。 */
|
|
36
|
+
roundtable: {
|
|
37
|
+
kind: 'roundtable';
|
|
38
|
+
/** 生产者标识,便于在转录里追溯来源。 */
|
|
39
|
+
plugin: string;
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/** Runtime knobs for node spawning, resolved from plugin config. */
|
|
44
|
+
export interface MemberRuntimeConfig {
|
|
45
|
+
/** Registered `ctx.subagents` provider name (must support continuable + persona). */
|
|
46
|
+
provider: string;
|
|
47
|
+
/** Node delegation depth cap (1 by default: experts may not spawn teams of their own). */
|
|
48
|
+
maxDepth?: number;
|
|
49
|
+
}
|
|
50
|
+
/** The node's tool restriction: deny captain-only tools, and — in direct
|
|
51
|
+
* skill-delivery mode — narrow the surface to the explicit expert allowlist
|
|
52
|
+
* so the host's `skill` loader is reachable from a node. */
|
|
53
|
+
export declare function nodeToolRestriction(skillDelivery?: SkillDelivery): ToolRestriction;
|
|
54
|
+
export { nodePersona, nodeWelcome };
|
|
55
|
+
export type { ExpertLimits, NodeSkillContext };
|
|
56
|
+
/**
|
|
57
|
+
* Spawn one node as a durable continuable subagent of the captain and fill
|
|
58
|
+
* `node.id` with its child session id. On failure nothing is persisted.
|
|
59
|
+
*/
|
|
60
|
+
export declare function spawnNode(ctx: Context, config: MemberRuntimeConfig, meeting: Meeting, node: MeetingNode, captain: Agent, stateDir: string, signal: AbortSignal, limits?: ExpertLimits, skill?: NodeSkillContext): Promise<void>;
|
|
61
|
+
/**
|
|
62
|
+
* Deliver one message to a node as its next FIFO turn. Best effort: a failure
|
|
63
|
+
* is logged and reported as `false` so the caller can decide.
|
|
64
|
+
*/
|
|
65
|
+
export declare function deliverToNode(ctx: Context, captain: Agent, childId: string, text: string, signal: AbortSignal): Promise<boolean>;
|
|
66
|
+
/** Request cancellation of one live node's current turn (fire and return). */
|
|
67
|
+
export declare function interruptNode(ctx: Context, captain: Agent, childId: string): void;
|
|
68
|
+
/**
|
|
69
|
+
* Interrupt one node **without** needing a live captain Agent.
|
|
70
|
+
*
|
|
71
|
+
* `ctx.subagents.interrupt` needs an ancestor Agent as its authority, so it is
|
|
72
|
+
* useless on paths that only hold session ids (the RPC surface, or a captain
|
|
73
|
+
* whose Agent is offline). The host's remote face is explicitly documented as
|
|
74
|
+
* "what keeps a live child interruptible while its parent Agent is offline".
|
|
75
|
+
*
|
|
76
|
+
* @returns true when the host accepted the request.
|
|
77
|
+
*/
|
|
78
|
+
export declare function interruptNodeByParent(ctx: Context, childId: string, parentSessionId: string): boolean;
|
|
79
|
+
/** Steer a live message into the captain at its nearest model boundary (best effort). */
|
|
80
|
+
export declare function steerCaptain(captain: Agent, text: string): boolean;
|
|
81
|
+
/** Resolve the real driver activity for durable node ids. */
|
|
82
|
+
export declare function nodeActivity(ctx: Context, nodes: readonly MeetingNode[]): Map<string, 'running' | 'idle' | 'ready'>;
|
|
83
|
+
export { CAPTAIN_KEY };
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 专家子代理的生命周期接入(第 1 批)。
|
|
3
|
+
*
|
|
4
|
+
* 背景(来自真实会议数据):专家的产出**只在它主动调用 `roundtable_speak`
|
|
5
|
+
* 的那一刻**才落盘。v0-2-36 那场评审里,一位红队专家完成了大量取证,却在
|
|
6
|
+
* speak 之前进程中断 —— 观点全丢,`review.json` 永久停在 `reviewing`、
|
|
7
|
+
* `viewpoints` 为空。同时 9 条被拉起的路由里 4 条 0 产出,而插件只留下一个
|
|
8
|
+
* `removed` 墓碑,没有任何失败原因,只能靠主持人用自然语言反复催办。
|
|
9
|
+
*
|
|
10
|
+
* 宿主其实早就把两件事递到手上:
|
|
11
|
+
* - `subagent/end` 带 `lastAssistantMessage`,且官方说明 **可续聊子代理的
|
|
12
|
+
* 每一个 activation epoch 都会发这个事件** —— 于是产出能在 `speak` 之外被
|
|
13
|
+
* 自动捕获,中途中断也不会全丢;
|
|
14
|
+
* - 同一事件带 `stopReason`(`error` / `max-tokens` / `aborted` …),基础设施
|
|
15
|
+
* 级失败时 `lastAssistantMessage` 为空;而 `agent/request-error` 进一步带
|
|
16
|
+
* `provider` 与 `failure: LlmFailure`(含 HTTP `status`),于是"为什么拉不
|
|
17
|
+
* 起来"(额度、鉴权、模型名写错)可以被写成 `node.lastError`。
|
|
18
|
+
*
|
|
19
|
+
* 索引是**进程内**的:childId → (stateRoot, meetingId)。冷启动后的旧节点无法
|
|
20
|
+
* 归属,这是刻意的 —— 宁可漏捕获,也不要把别的会话的文本写进会议记录。
|
|
21
|
+
*
|
|
22
|
+
* @module dsh-plugin-roundtable/node-events
|
|
23
|
+
*/
|
|
24
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
25
|
+
import type { ContentBlock } from '@deepseek-ai/dsh-llm';
|
|
26
|
+
import type { MeetingNode } from './types.ts';
|
|
27
|
+
/** Where one node's meeting lives on disk. */
|
|
28
|
+
export interface NodeLocation {
|
|
29
|
+
stateRoot: string;
|
|
30
|
+
meetingId: string;
|
|
31
|
+
}
|
|
32
|
+
/** Remember which meeting a freshly spawned node belongs to. */
|
|
33
|
+
export declare function registerNodeChild(childId: string, location: NodeLocation): void;
|
|
34
|
+
/** Drop one node's index entry (node removed, or failed spawn rolled back). */
|
|
35
|
+
export declare function forgetNodeChild(childId: string): void;
|
|
36
|
+
/** Look up a node's meeting by its child session id. */
|
|
37
|
+
export declare function locateNodeChild(childId: string): NodeLocation | undefined;
|
|
38
|
+
/** Test-only: clear the process-local index. */
|
|
39
|
+
export declare function __resetNodeIndexForTests(): void;
|
|
40
|
+
/** Flatten the text blocks of one assistant output (empty when there is none). */
|
|
41
|
+
export declare function blocksToText(blocks: readonly ContentBlock[] | undefined): string;
|
|
42
|
+
/** What one settled child run means for its node. */
|
|
43
|
+
export interface NodeEndOutcome {
|
|
44
|
+
/** Captured output; empty string means the child produced nothing usable. */
|
|
45
|
+
text: string;
|
|
46
|
+
/** Readable reason, written to `node.lastError` when `text` is empty. */
|
|
47
|
+
note: string;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Decide what a settled child run contributes to its meeting.
|
|
51
|
+
*
|
|
52
|
+
* A run that ends `completed` but carries no text is *not* a hard failure (the
|
|
53
|
+
* expert may have only acknowledged), so it gets its own wording — the point is
|
|
54
|
+
* to never leave the captain guessing between "still working" and "died".
|
|
55
|
+
*/
|
|
56
|
+
export declare function describeNodeEnd(info: {
|
|
57
|
+
readonly stopReason?: string;
|
|
58
|
+
readonly lastAssistantMessage?: readonly ContentBlock[];
|
|
59
|
+
}): NodeEndOutcome;
|
|
60
|
+
/** One-line readable description of a provider request failure. */
|
|
61
|
+
export declare function describeRequestFailure(provider: string, failure: {
|
|
62
|
+
message?: string;
|
|
63
|
+
code?: string;
|
|
64
|
+
status?: number;
|
|
65
|
+
} | undefined): string;
|
|
66
|
+
/**
|
|
67
|
+
* Subscribe the two host events that make expert output and expert failure
|
|
68
|
+
* visible.
|
|
69
|
+
*
|
|
70
|
+
* Deliberately non-invasive: `subagent/end` is a plain emit, and the
|
|
71
|
+
* `agent/request-error` waterfall listener always delegates through `next()` so
|
|
72
|
+
* the host's own retry/recovery policy is untouched — this plugin only observes.
|
|
73
|
+
*/
|
|
74
|
+
export declare function attachNodeEvents(ctx: Context): void;
|
|
75
|
+
/** Node liveness, refined by asking the host's own subagent tree. */
|
|
76
|
+
export type NodeLiveness = 'running' | 'idle' | 'ready' | 'missing';
|
|
77
|
+
/**
|
|
78
|
+
* Reconcile node liveness against the host's real subagent tree.
|
|
79
|
+
*
|
|
80
|
+
* `ctx.agents.get(id)` only knows **live** drivers, so a node whose process is
|
|
81
|
+
* gone looks exactly like one that is merely between turns — both report
|
|
82
|
+
* `ready`, and the captain cannot tell "待唤醒" from "已经没了". Asking the
|
|
83
|
+
* durable catalog separates them: an id the host still lists is a cold-but-real
|
|
84
|
+
* child, an id it no longer knows is `missing` — which is precisely the old
|
|
85
|
+
* "把消息投进死节点、静默丢一整轮" failure seen in the meeting records.
|
|
86
|
+
*
|
|
87
|
+
* Runs one listing per meeting (not per node) and degrades to the previous
|
|
88
|
+
* semantics when the catalog is unavailable.
|
|
89
|
+
*/
|
|
90
|
+
export declare function reconcileNodeLiveness(ctx: Context, captainSessionId: string, nodes: readonly MeetingNode[]): Promise<Map<string, NodeLiveness>>;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 会议设置确认卡片(R1):草案数据结构 + 卡片 Markdown 渲染 + 答案解析。
|
|
3
|
+
*
|
|
4
|
+
* 原生 `ctx.userQuestions` 没有表单能力(只有选项列表 + 一个自由文本
|
|
5
|
+
* `custom`),所以"可改全部"是通过 `custom` 自由文本 + 回流重建实现的,
|
|
6
|
+
* 而不是富表单。本模块只负责把一份草案渲染成**可读**的 Markdown,并把
|
|
7
|
+
* 用户的回答解析成三态:approved / revise / unavailable。
|
|
8
|
+
*
|
|
9
|
+
* @module dsh-plugin-roundtable/plan
|
|
10
|
+
*/
|
|
11
|
+
import type { SkillDelivery } from './types.ts';
|
|
12
|
+
/** skill 的两种传递方式(D5)。单一来源:types.ts。 */
|
|
13
|
+
export type { SkillDelivery };
|
|
14
|
+
/** 卡片上"按此创建"选项的固定标签;`intent.approve` 必须与它逐字一致。 */
|
|
15
|
+
export declare const PLAN_APPROVE_LABEL = "\u6309\u6B64\u521B\u5EFA";
|
|
16
|
+
/** 卡片上的次选项:用户想改点什么。 */
|
|
17
|
+
export declare const PLAN_REVISE_LABEL = "\u6211\u8981\u4FEE\u6539";
|
|
18
|
+
/**
|
|
19
|
+
* 一张设置卡片所描述的会议草案(尚未创建)。
|
|
20
|
+
*
|
|
21
|
+
* 字段与 `roundtable_create` 的入参一一对应,外加 `skills` / `skillDelivery`,
|
|
22
|
+
* 这样用户确认后主持人可以**原样**按草案创建会议,不再自行拍参数。
|
|
23
|
+
*/
|
|
24
|
+
export interface MeetingDraft {
|
|
25
|
+
/** 会议名称(同时作为会议 id 的稳定来源)。 */
|
|
26
|
+
name: string;
|
|
27
|
+
/** 会议背景与核心目标(总纲第一节)。 */
|
|
28
|
+
goal: string;
|
|
29
|
+
/** 协作模式。 */
|
|
30
|
+
mode: 'orchestrated' | 'egalitarian' | 'redteam';
|
|
31
|
+
/** 轮数上限,超限闭麦。 */
|
|
32
|
+
maxRounds: number;
|
|
33
|
+
/** Token 预算上限,超限闭麦。 */
|
|
34
|
+
maxTokens: number;
|
|
35
|
+
/** 知识库目录(可空):主持人按需读取其中文件转交专家。 */
|
|
36
|
+
kbPath: string;
|
|
37
|
+
/** 本次会议选中的 skill 名称清单(可为空)。 */
|
|
38
|
+
skills: string[];
|
|
39
|
+
/** skill 传递方式:relay=主持人中转;direct=专家自行调用。 */
|
|
40
|
+
skillDelivery: SkillDelivery;
|
|
41
|
+
}
|
|
42
|
+
/** 卡片选项的两态解析结果。 */
|
|
43
|
+
export type PlanConfirmation = {
|
|
44
|
+
kind: 'approved';
|
|
45
|
+
} | {
|
|
46
|
+
kind: 'revise';
|
|
47
|
+
note: string;
|
|
48
|
+
}
|
|
49
|
+
/** 服务缺失或用户没给任何选择:主持人应把它当作"再确认一次"。 */
|
|
50
|
+
| {
|
|
51
|
+
kind: 'unavailable';
|
|
52
|
+
note: string;
|
|
53
|
+
};
|
|
54
|
+
/** 清单里的一条 skill:只要名字与描述这两个字段。 */
|
|
55
|
+
export interface SkillListEntry {
|
|
56
|
+
readonly name: string;
|
|
57
|
+
readonly description?: string;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* 把一份草案渲染成卡片正文(Markdown)。
|
|
61
|
+
*
|
|
62
|
+
* 这里是"让用户看懂将要创建什么"的唯一位置:每一项参数都显示当前值,
|
|
63
|
+
* 专家逐个列出 key / 角色 / 路由,并把**当前可选**的 skill 清单也列出来
|
|
64
|
+
* (用户要在「我要修改」的自由文本里增删 skill,就得先看得到名字)。
|
|
65
|
+
* 禁止在其中塞伪交互说明。
|
|
66
|
+
*/
|
|
67
|
+
export declare function formatMeetingDraft(draft: MeetingDraft, experts: readonly {
|
|
68
|
+
key: string;
|
|
69
|
+
role?: string;
|
|
70
|
+
provider?: string;
|
|
71
|
+
model?: string;
|
|
72
|
+
}[], options?: {
|
|
73
|
+
revised?: boolean;
|
|
74
|
+
availableSkills?: readonly SkillListEntry[];
|
|
75
|
+
}): string;
|
|
76
|
+
/**
|
|
77
|
+
* 解析用户的卡片回答。
|
|
78
|
+
*
|
|
79
|
+
* 协议与 `intent.kind: 'plan-review'` 的语义一致:`selected` 含
|
|
80
|
+
* `PLAN_APPROVE_LABEL` 即视为批准,其余选项(或只有 custom 文本)视为
|
|
81
|
+
* 要求修改。不识别 intent 的 UI 会退回普通选项列表,答复编码完全相同。
|
|
82
|
+
*/
|
|
83
|
+
export declare function resolvePlanConfirmation(answer: {
|
|
84
|
+
selected: string[];
|
|
85
|
+
custom?: string;
|
|
86
|
+
} | undefined, approveLabel?: string): PlanConfirmation;
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 运行时偏好的持久化与净化(宿主设置模型重构后的自持久化实现)。
|
|
3
|
+
*
|
|
4
|
+
* ## 为什么不再是宿主的 settings 命名空间
|
|
5
|
+
*
|
|
6
|
+
* 0.1.5-rc.3 时代,宿主 `settings` 服务提供
|
|
7
|
+
* `settings.register(ns, schema, { base })`,返回一个 `SettingsScope`,自带
|
|
8
|
+
* `get()` / `update()` / `watch()` —— 插件因此可以把「运行时可改、改完立即
|
|
9
|
+
* 生效」的偏好寄存在宿主的 `settings.yaml` 里。
|
|
10
|
+
*
|
|
11
|
+
* 0.2.0-rc.2 把该服务重构为 `SettingsForms`:它把 **profile 配置投影成表单**
|
|
12
|
+
* (`configure` / `describe` / `update` / `replace` / `mutate`),插件不再有
|
|
13
|
+
* 「注册自己的命名空间」这个动作,`SettingsScope` 类型也随之消失。新模型里
|
|
14
|
+
* 插件的可配置面是它自己的 Cordis `Config`,而写 Config 会触发 Loader 重载
|
|
15
|
+
* 该 entry —— 对「改一个偏好就重启整场会议」来说太重了。
|
|
16
|
+
*
|
|
17
|
+
* 因此本插件改为自持久化:偏好落在 DSH home 下的
|
|
18
|
+
* `roundtable/preferences.json`,读写都在插件内部完成,不再依赖宿主设置服务,
|
|
19
|
+
* 也就不会因为该服务的下一次重构而再次失效。
|
|
20
|
+
*
|
|
21
|
+
* ## 三条铁律(与 state.ts 同源)
|
|
22
|
+
*
|
|
23
|
+
* 1. **读失败绝不抛给调用方**:偏好文件损坏或被手改成非法 JSON,一律退回默认
|
|
24
|
+
* 值并留一条日志。偏好是观察性配置,它不能让整场会议从界面上消失。
|
|
25
|
+
* 2. **写入必须原子**:复用 {@link writeTextAtomic}(宿主 `writeFileAtomic`
|
|
26
|
+
* 优先、本地 tmp+rename 兜底),避免半截 JSON 落盘。
|
|
27
|
+
* 3. **非法字段一律净化**:schemastery 不校验数组条目的 required 字段,
|
|
28
|
+
* 条目级校验只能手写 —— 与既有净化契约一致。
|
|
29
|
+
*
|
|
30
|
+
* @module dsh-plugin-roundtable/preferences
|
|
31
|
+
*/
|
|
32
|
+
import type { RolePreset, RoleSquad } from './types.ts';
|
|
33
|
+
/**
|
|
34
|
+
* 解析 DeepSeek Harness home。
|
|
35
|
+
*
|
|
36
|
+
* 与宿主 `@deepseek-ai/dsh-home-paths` 的 `resolveDshHome` 优先级对齐:
|
|
37
|
+
* `configured` > `$DSH_HOME` > `~/.dsh`。只有第一级做不到 —— 它是宿主进程内部
|
|
38
|
+
* 传入的显式覆盖,插件拿不到 —— 但环境变量与默认值这两级完全一致,因此在
|
|
39
|
+
* 任何正常启动的宿主里都会落到同一个目录。
|
|
40
|
+
*
|
|
41
|
+
* 刻意**不**静态 import `@deepseek-ai/dsh-home-paths`:那会把它变成加载门禁,
|
|
42
|
+
* 而本模块只是要一个路径(见 harness-compat 的铁律 #1)。
|
|
43
|
+
*/
|
|
44
|
+
export declare function dshHomeDir(): string;
|
|
45
|
+
/** 偏好文件的绝对路径(DSH home 下的 `roundtable/preferences.json`)。 */
|
|
46
|
+
export declare function preferencesFilePath(): string;
|
|
47
|
+
/** Wire shape of the runtime preferences. */
|
|
48
|
+
export interface RoundTablePreferences {
|
|
49
|
+
readonly defaultMode: 'orchestrated' | 'egalitarian' | 'redteam';
|
|
50
|
+
readonly maxRounds: number;
|
|
51
|
+
readonly maxTokens: number;
|
|
52
|
+
/** 互通开关:true = 显示所有圆桌会议;false = 仅显示当前对话开启的会议。 */
|
|
53
|
+
readonly showAllMeetings: boolean;
|
|
54
|
+
/** 专家每轮输出 token 上限(模型请求 max_tokens),0 = 不限制。 */
|
|
55
|
+
readonly expertMaxTokens: number;
|
|
56
|
+
/** 专家每轮最多提几条意见,0 = 不限制。 */
|
|
57
|
+
readonly expertMaxOpinions: number;
|
|
58
|
+
/** E1/E4 反馈:会议结束后是否询问轻量反馈;false = 永久关闭(设置页可改)。 */
|
|
59
|
+
readonly feedbackEnabled: boolean;
|
|
60
|
+
/** R2.2/D5:skill 传递方式(relay=主持人中转;direct=专家自行调用)。 */
|
|
61
|
+
readonly skillDelivery: 'relay' | 'direct';
|
|
62
|
+
/** 右栏面板可见性(R3):被列出的面板在拓扑页隐藏;空 = 全部显示。 */
|
|
63
|
+
readonly hiddenPanels: string[];
|
|
64
|
+
/** A1:画布图例是否已被用户关闭(默认 false = 显示)。 */
|
|
65
|
+
readonly legendHidden: boolean;
|
|
66
|
+
/** B3:用户自建角色预设(全局偏好;不预置任何内置角色)。 */
|
|
67
|
+
readonly rolePresets: RolePreset[];
|
|
68
|
+
/** B3+:用户自建阵容预设(一次套用多位专家;同样不预置)。 */
|
|
69
|
+
readonly squads: RoleSquad[];
|
|
70
|
+
}
|
|
71
|
+
/** 没有任何持久化数据时的偏好。`defaultMode` 由调用方按插件 Config 覆盖。 */
|
|
72
|
+
export declare function defaultPreferences(defaultMode: RoundTablePreferences['defaultMode']): RoundTablePreferences;
|
|
73
|
+
/** 右栏可隐藏的面板 id(客户端与服务端共用的稳定标识)。 */
|
|
74
|
+
export declare const ROUNDTABLE_PANELS: readonly string[];
|
|
75
|
+
/** 把任意输入收敛成合法的隐藏面板清单(未知 id 丢弃,去重)。 */
|
|
76
|
+
export declare function sanitizeHiddenPanels(value: unknown): string[];
|
|
77
|
+
/** 预设条目的硬上限(客户端与服务端共用;服务端截断,客户端提前拦截)。 */
|
|
78
|
+
export declare const ROLE_PRESET_MAX = 50;
|
|
79
|
+
/**
|
|
80
|
+
* 把任意输入收敛成合法的角色预设清单(B3)。
|
|
81
|
+
*
|
|
82
|
+
* schemastery 的 `z.object` 在 resolve 阶段**不校验缺失的 required 字段**
|
|
83
|
+
* (`s({ rolePresets: [{ id: 'a' }] })` 直接通过),所以条目级校验不能依赖
|
|
84
|
+
* schema,必须在这里手写 —— 与 {@link sanitizeHiddenPanels} 同一套路:
|
|
85
|
+
* - `name` 与 `role` 非空是硬要求,二者缺一即丢弃该条;
|
|
86
|
+
* - `id` 为空或与前面的条目重复时**重新分配**(保数据,不静默丢条目);
|
|
87
|
+
* - `provider`/`model` 必须成对出现,否则整体视为"继承主持人";
|
|
88
|
+
* - 单字段超长截断,总条数超 {@link ROLE_PRESET_MAX} 截断。
|
|
89
|
+
*/
|
|
90
|
+
export declare function sanitizeRolePresets(value: unknown): RolePreset[];
|
|
91
|
+
/** 阵容预设的硬上限(客户端与服务端共用;服务端截断,客户端提前拦截)。 */
|
|
92
|
+
export declare const SQUAD_MAX = 20;
|
|
93
|
+
export declare const SQUAD_MEMBER_MAX = 8;
|
|
94
|
+
/**
|
|
95
|
+
* 把任意输入收敛成合法的阵容预设清单(B3+)。
|
|
96
|
+
*
|
|
97
|
+
* 与 {@link sanitizeRolePresets} 同一套路(schemastery 不校验 required,
|
|
98
|
+
* 条目级校验只能手写),差别只有两条:
|
|
99
|
+
* - 成员的**去重键是 key**(同一阵容里不能有两位同名专家);
|
|
100
|
+
* - **没有任何成员的阵容被丢弃** —— 否则设置页会出现一条点了没反应的条目。
|
|
101
|
+
*
|
|
102
|
+
* 兼容性(这一条就是"结构变更必须自带迁移"的落地方式):`squads` 是
|
|
103
|
+
* **可选新增字段**,旧偏好对象里没有它时读到的是 `[]`,读侧归一化、
|
|
104
|
+
* 只有用户真的保存时才写回,因此不需要版本号迁移,也不可能损坏已有数据。
|
|
105
|
+
*/
|
|
106
|
+
export declare function sanitizeSquads(value: unknown): RoleSquad[];
|
|
107
|
+
/**
|
|
108
|
+
* 把 `layer` 叠在 `base` 之上并逐字段净化。
|
|
109
|
+
*
|
|
110
|
+
* 语义与宿主 `SettingsScope.update(patch)` + `get()` 的组合一致:**只覆盖
|
|
111
|
+
* `layer` 中出现的字段**,其余沿用 `base`。因此同一个函数同时服务于两条路径:
|
|
112
|
+
* - 读盘:`normalizePreferences(fileContent, defaults)` —— 旧文件缺新字段时补默认;
|
|
113
|
+
* - 写入:`normalizePreferences(patch, current)` —— 设置页的部分更新。
|
|
114
|
+
*/
|
|
115
|
+
export declare function normalizePreferences(layer: unknown, base: RoundTablePreferences): RoundTablePreferences;
|
|
116
|
+
/** 偏好读取面:`get()` 永远返回一个完整、合法的对象。 */
|
|
117
|
+
export interface PreferenceStore {
|
|
118
|
+
/** 当前生效的偏好(同步读,永远完整)。 */
|
|
119
|
+
get(): RoundTablePreferences;
|
|
120
|
+
/** 叠加一次部分更新,落盘后返回新值;写失败只记日志,不抛给调用方。 */
|
|
121
|
+
update(patch: unknown): Promise<RoundTablePreferences>;
|
|
122
|
+
/** 订阅变更(返回退订函数);回调抛错不影响其它订阅者。 */
|
|
123
|
+
watch(listener: (value: RoundTablePreferences) => void): () => void;
|
|
124
|
+
/** 等待首次读盘完成(测试与诊断用)。 */
|
|
125
|
+
ready(): Promise<void>;
|
|
126
|
+
}
|
|
127
|
+
/** 建一个偏好存储所需的最小依赖。 */
|
|
128
|
+
export interface PreferenceStoreOptions {
|
|
129
|
+
/** 基准偏好(通常来自插件 Config 的 `defaultMode` 与内置默认值)。 */
|
|
130
|
+
base: RoundTablePreferences;
|
|
131
|
+
/** 覆盖偏好文件路径(测试用)。 */
|
|
132
|
+
file?: string;
|
|
133
|
+
/** 诊断日志;缺省静默 —— 偏好问题不该刷屏。 */
|
|
134
|
+
log?: (message: string, error?: unknown) => void;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* 创建偏好存储。
|
|
138
|
+
*
|
|
139
|
+
* 读盘是**惰性且异步**的(构造后立刻开始),因此 `get()` 在首次读盘完成前
|
|
140
|
+
* 返回基准值而不是阻塞宿主启动 —— 这与宿主设置服务的可用性无关,也是它比
|
|
141
|
+
* `ctx.inject(['settings'])` 更稳的地方:没有可选能力可缺。
|
|
142
|
+
*/
|
|
143
|
+
export declare function createPreferenceStore(options: PreferenceStoreOptions): PreferenceStore;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 提示词资产(第 4 步:各 AI 之间的提示词与限定)。
|
|
3
|
+
*
|
|
4
|
+
* 本模块**只生成纯文本**,零运行时依赖(仅 type-only import)。这样
|
|
5
|
+
* `test/prompt-budget.test.mjs` 可以直接 import 它做体积护栏 —— 提示词
|
|
6
|
+
* 会随时间自然膨胀,护栏是唯一能阻止它涨回去的手段。
|
|
7
|
+
*
|
|
8
|
+
* 三条设计原则(压缩而不丢信息):
|
|
9
|
+
* 1. **常驻的极简**:usage 段只讲"什么时候用哪件工具",操作规程留在各
|
|
10
|
+
* 工具自己的 description 里(两处都常驻,所以同一规则绝不写两遍);
|
|
11
|
+
* 2. **按需的详尽**:细节在真正用到它的那一刻才进上下文;
|
|
12
|
+
* 3. **跨边界的结构化**:转发用引用(`R{n}[{speaker}]`)而不是复述全文。
|
|
13
|
+
*
|
|
14
|
+
* @module dsh-plugin-roundtable/prompt
|
|
15
|
+
*/
|
|
16
|
+
import type { Meeting, MeetingNode, SkillDelivery } from './types.ts';
|
|
17
|
+
/** 任务信封(主持人 → 专家):固定四段,可机械转发,不粘贴全文。 */
|
|
18
|
+
export declare const TASK_ENVELOPE = "[TASK R{\u8F6E} \u2192 {key}] \u76EE\u6807\uFF5C\u8F93\u5165\uFF08\u5F15\u7528 R{n}[{\u53D1\u8A00\u4EBA}] \u6216\u6587\u4EF6\u8DEF\u5F84\uFF09\uFF5C\u7EA6\u675F\uFF5C\u671F\u671B\u4EA7\u51FA";
|
|
19
|
+
/** 报告信封(专家 → 网关/主持人):`[核心产出]` 就是网关抓取的段落。 */
|
|
20
|
+
export declare const REPORT_ENVELOPE = "[REPORT R{\u8F6E} {key}] \u7ED3\u8BBA\uFF08\u22643 \u6761\uFF09\uFF5C\u8BC1\u636E\uFF5C\u5F85\u51B3\u7B56";
|
|
21
|
+
/**
|
|
22
|
+
* 主持人(DeepSeek 本体)的常驻 usage 段。
|
|
23
|
+
*
|
|
24
|
+
* **它每一次请求都要付费**,包括完全不开会的日常对话。因此这里只保留
|
|
25
|
+
* "何时触发、按什么顺序、红线在哪",把流程细节交给各工具的 description。
|
|
26
|
+
* 也**不再逐个列出工具名**:工具 schema 本来就在请求里,列一遍是纯冗余。
|
|
27
|
+
*/
|
|
28
|
+
export declare function usageSectionText(): string;
|
|
29
|
+
/** Per-expert answer limits resolved from settings at spawn time. */
|
|
30
|
+
export interface ExpertLimits {
|
|
31
|
+
/** Per-request output token cap (model max_tokens); 0 = unlimited. */
|
|
32
|
+
maxTokens?: number;
|
|
33
|
+
/** Max opinions per round (prompt-level constraint); 0 = unlimited. */
|
|
34
|
+
maxOpinions?: number;
|
|
35
|
+
}
|
|
36
|
+
/** 节点 persona 需要的 skill 上下文(R2:会议选中的 skill 与传递方式)。 */
|
|
37
|
+
export interface NodeSkillContext {
|
|
38
|
+
/** 选择随 persona 注入的 skill 名称(缺省取 meeting.skills)。 */
|
|
39
|
+
names?: readonly string[];
|
|
40
|
+
/** 缺省取 meeting.skillDelivery。 */
|
|
41
|
+
delivery?: SkillDelivery;
|
|
42
|
+
/** host 是否真的注册了 `skill` 工具(未注册时不得让专家去调它)。 */
|
|
43
|
+
skillToolAvailable?: boolean;
|
|
44
|
+
}
|
|
45
|
+
/** The node's system prompt (persona): the charter plus node working rules.
|
|
46
|
+
*
|
|
47
|
+
* 与 charter 严格分工:**总纲写共同红线(格式、编造、拍板),这里只写
|
|
48
|
+
* "你这个节点具体怎么做"**。同一约束绝不在这两处各写一遍 —— persona 会
|
|
49
|
+
* 跟着专家的每一次请求付费。 */
|
|
50
|
+
export declare function nodePersona(meeting: Meeting, node: MeetingNode, stateDir: string, limits?: ExpertLimits, skill?: NodeSkillContext): string;
|
|
51
|
+
/** The initial user message delivered when the node is created. */
|
|
52
|
+
export declare function nodeWelcome(meeting: Meeting, node: MeetingNode): string;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Proxy Thinking (代理思考): when a black-box worker model (no visible
|
|
3
|
+
* reasoning) must execute a task, the director model (DeepSeek) first writes
|
|
4
|
+
* its own reasoning as `[DeepSeek 代理思考]`, then translates the goal into
|
|
5
|
+
* exact worker parameters. The UI prefixes stages with
|
|
6
|
+
* `[DeepSeek 代理思考]` / `[<worker> 渲染中]`.
|
|
7
|
+
* @module dsh-plugin-roundtable/proxy-thinking
|
|
8
|
+
*/
|
|
9
|
+
/** The director-side prompt template (spec §6.2). */
|
|
10
|
+
export declare const PROXY_THINKING_TEMPLATE = "\u3010\u4EE3\u7406\u601D\u8003\uFF08Proxy Thinking\uFF09\u2014\u2014 \u5BFC\u6F14\u6A21\u578B\u4FA7\u3011\n\n\u4F60\u5F53\u524D\u626E\u6F14\"\u5BFC\u6F14\u6A21\u578B\"\uFF0C\u8D1F\u8D23\u628A\u4E00\u4E2A\u6A21\u7CCA\u76EE\u6807\u4EA4\u7ED9\u4E00\u4E2A\"\u65E0\u601D\u8003\u8FC7\u7A0B\u7684\u5E72\u6D3B\u6A21\u578B\" <worker_model> \u6267\u884C\u3002\n<worker_model> \u4E0D\u4F1A\u5C55\u793A\u601D\u8003\u8FC7\u7A0B\u3001\u4E0D\u4F1A\u81EA\u6211\u7EA0\u9519\u3001\u4E0D\u4F1A\u8FFD\u95EE\u6F84\u6E05\u3002\n\u56E0\u6B64\u4F60\u7684\u804C\u8D23\u662F\u66FF\u5B83\u5B8C\u6210\u601D\u8003\uFF0C\u5E76\u628A\u4EFB\u52A1\u7FFB\u8BD1\u6210\u5B83\u80FD\u76F4\u63A5\u6267\u884C\u7684\u6307\u4EE4\u3002\n\n\u8BF7\u4E25\u683C\u6309\u4EE5\u4E0B\u987A\u5E8F\u8F93\u51FA\uFF0C\u4E0D\u8981\u9057\u6F0F\u4EFB\u4F55\u4E00\u8282\uFF1A\n\n\u4E00\u3001[DeepSeek \u4EE3\u7406\u601D\u8003]\n\u7528\u81EA\u7136\u8BED\u8A00\u5199\u51FA\u4F60\u7684\u5B8C\u6574\u63A8\u7406\u8FC7\u7A0B\uFF0C\u81F3\u5C11\u8986\u76D6\uFF1A\n- \u4F60\u5982\u4F55\u7406\u89E3\u8FD9\u4E2A\u76EE\u6807\uFF08\u62C6\u89E3\u6210\u54EA\u4E9B\u5B50\u610F\u56FE\uFF09\n- \u4F60\u9009\u62E9\u4EC0\u4E48\u7B56\u7565\uFF0C\u4E3A\u4EC0\u4E48\n- \u5173\u952E\u53C2\u6570\u4E3A\u4EC0\u4E48\u8FD9\u6837\u5B9A\uFF08\u7ED9\u51FA\u53D6\u820D\u7406\u7531\uFF09\n- \u9884\u671F\u7ED3\u679C\u662F\u4EC0\u4E48\n- \u53EF\u80FD\u7684\u5931\u8D25\u70B9\u4E0E\u515C\u5E95\u65B9\u6848\n\uFF08\u8FD9\u4E00\u8282\u662F\"\u4EE3\u7406\u601D\u8003\"\uFF0C\u76F4\u63A5\u5C55\u793A\u7ED9\u7528\u6237\uFF0C\u8865\u9F50 <worker_model> \u7F3A\u5931\u7684\u900F\u660E\u6027\u3002\uFF09\n\n\u4E8C\u3001[\u53C2\u6570\u7FFB\u8BD1 \u2192 <worker_model>]\n\u628A\u76EE\u6807\u7FFB\u8BD1\u6210 <worker_model> \u80FD\u76F4\u63A5\u6267\u884C\u7684\u7CBE\u786E\u6307\u4EE4/\u53C2\u6570\uFF1A\n- \u5B57\u6BB5\u5FC5\u987B\u7B26\u5408 <worker_model> \u7684\u8F93\u5165\u89C4\u8303\uFF08schema\uFF09\n- \u7981\u6B62\u542B\u7CCA\u63CF\u8FF0\uFF1A\u4E0D\u7528\"\u597D\u770B\u4E00\u70B9/\u9AD8\u7EA7\u4E00\u70B9\"\uFF0C\u5FC5\u987B\u7ED9\u51FA\u5177\u4F53\u98CE\u683C\u3001\u6784\u56FE\u3001\u65F6\u957F\u3001\u5C3A\u5BF8\u3001\u8BED\u6C14\u7B49\n- \u8F93\u51FA\u4E3A\u7ED3\u6784\u5316 JSON \u6216\u7CBE\u786E\u6307\u4EE4\u6587\u672C\uFF08\u4EE5 <worker_model> \u5B9E\u9645\u63A5\u53D7\u7684\u683C\u5F0F\u4E3A\u51C6\uFF09\n\n\u4E09\u3001[\u9884\u671F\u4E0E\u515C\u5E95]\n- \u9884\u671F\u7ED3\u679C\uFF1A\u6267\u884C\u6210\u529F\u65F6\u5E94\u5F97\u5230\u4EC0\u4E48\n- \u5931\u8D25\u515C\u5E95\uFF1A\u82E5\u7ED3\u679C\u4E0D\u7B26\u5408\u9884\u671F\uFF0C\u4F60\u63A5\u4E0B\u6765\u5982\u4F55\u4FEE\u6B63\u6216\u964D\u7EA7";
|
|
11
|
+
/** Compose the director-side prompt for one worker task. */
|
|
12
|
+
export declare function proxyThinkingPrompt(workerModel: string, task: string): string;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 观点拆分(V0.2.2 批次 1):collect_review 时把红队专家的一条发言用 LLM
|
|
3
|
+
* 拆成 1~3 条独立观点,用户即可对单条观点「支持/驳回」。
|
|
4
|
+
*
|
|
5
|
+
* 三道防线(V0.2.2 方案定稿):
|
|
6
|
+
* ① JSON schema 严格约束输出(tools 参数 + temperature 0)
|
|
7
|
+
* ② 任何解析失败 → 返回 null,调用方整条兜底(seq=0)
|
|
8
|
+
* ③ quote 必须是发言原文的子串(规范化后 indexOf),否则丢弃该 quote
|
|
9
|
+
* @module dsh-plugin-roundtable/review-split
|
|
10
|
+
*/
|
|
11
|
+
/** 一条拆分结果。 */
|
|
12
|
+
export interface SplitLine {
|
|
13
|
+
content: string;
|
|
14
|
+
quote?: string;
|
|
15
|
+
dimension: string;
|
|
16
|
+
/** 证据分级(C1):代码/bug 类 = repro(可复现步骤);设计类 = argument(论证链)。 */
|
|
17
|
+
evidence?: {
|
|
18
|
+
kind: 'repro' | 'argument';
|
|
19
|
+
text: string;
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
/** 拆分调用的模型路由配置(默认 deepseek-official/deepseek-v4-flash,实测可用)。 */
|
|
23
|
+
export interface ReviewSplitConfig {
|
|
24
|
+
provider: string;
|
|
25
|
+
model: string;
|
|
26
|
+
/** 每条发言最多拆几条观点(默认 3)。 */
|
|
27
|
+
maxOpinions: number;
|
|
28
|
+
}
|
|
29
|
+
/** 最小化 LLM 服务形状(与 ctx.llm.stream 结构兼容)。 */
|
|
30
|
+
export interface SplitLlmLike {
|
|
31
|
+
stream(options: {
|
|
32
|
+
provider: string;
|
|
33
|
+
model: string;
|
|
34
|
+
messages: {
|
|
35
|
+
id: string;
|
|
36
|
+
role: 'user';
|
|
37
|
+
content: {
|
|
38
|
+
type: 'text';
|
|
39
|
+
text: string;
|
|
40
|
+
}[];
|
|
41
|
+
source: {
|
|
42
|
+
kind: 'user';
|
|
43
|
+
};
|
|
44
|
+
}[];
|
|
45
|
+
system?: string;
|
|
46
|
+
tools?: {
|
|
47
|
+
name: string;
|
|
48
|
+
description: string;
|
|
49
|
+
parameters: Record<string, unknown>;
|
|
50
|
+
}[];
|
|
51
|
+
temperature?: number;
|
|
52
|
+
maxTokens?: number;
|
|
53
|
+
signal?: AbortSignal;
|
|
54
|
+
}): AsyncIterable<unknown>;
|
|
55
|
+
}
|
|
56
|
+
export declare function splitByMarkers(content: string): SplitLine[] | null;
|
|
57
|
+
/**
|
|
58
|
+
* 拆分一条发言为若干独立观点;任何失败返回 null(调用方整条兜底)。
|
|
59
|
+
* quote 与发言原文做规范化子串校验(防线③),不匹配即丢弃。
|
|
60
|
+
*/
|
|
61
|
+
export declare function splitUtterance(llm: SplitLlmLike, config: ReviewSplitConfig, nodeKey: string, content: string): Promise<SplitLine[] | null>;
|