@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,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RPC handlers for the browser topology tab:
|
|
3
|
+
* - `roundtable/prefs.get` → { defaultMode, maxRounds, maxTokens }
|
|
4
|
+
* - `roundtable/prefs.set` → persist user preferences
|
|
5
|
+
* - `roundtable/edge.set` → flip one edge's direction (UI right-click menu)
|
|
6
|
+
* - `roundtable/edge.add` → drag-to-connect a new channel
|
|
7
|
+
* - `roundtable/edge.remove` → remove an edge
|
|
8
|
+
*
|
|
9
|
+
* Registered on the host through `ctx.inject(['connection'])` on the plugin's
|
|
10
|
+
* OWN RPC channel `/roundtable` (NOT the shared `/api` one). The `/api`
|
|
11
|
+
* channel is a single-interceptor shared channel owned by dsh-api-gateway, so
|
|
12
|
+
* registering a second `intercept('/api')` here throws
|
|
13
|
+
* "shared RPC channel /api already has an interceptor" and silently drops
|
|
14
|
+
* every roundtable RPC — which is exactly why drag-to-connect looked dead for
|
|
15
|
+
* both removed AND active nodes. Using `rpc.handle('/roundtable', ...)` gives
|
|
16
|
+
* this plugin its own prefix-routed channel, mirroring the ya-subagent plugin.
|
|
17
|
+
* @module dsh-plugin-roundtable/rpc
|
|
18
|
+
*/
|
|
19
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
20
|
+
import { ROLE_PRESET_MAX, ROUNDTABLE_PANELS, SQUAD_MAX, SQUAD_MEMBER_MAX, sanitizeHiddenPanels, sanitizeRolePresets, sanitizeSquads, type PreferenceStore, type RoundTablePreferences } from './preferences.ts';
|
|
21
|
+
/** RPC result envelope (mirrors the apiproxy wire shape). */
|
|
22
|
+
export type RpcResult<T> = {
|
|
23
|
+
ok: true;
|
|
24
|
+
value: T;
|
|
25
|
+
error?: never;
|
|
26
|
+
} | {
|
|
27
|
+
ok: false;
|
|
28
|
+
error: {
|
|
29
|
+
code: string;
|
|
30
|
+
message: string;
|
|
31
|
+
details?: unknown;
|
|
32
|
+
};
|
|
33
|
+
value?: never;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* 偏好形状与净化规则住在 `preferences.ts`(那里的模块注释说明了原因:宿主
|
|
37
|
+
* 0.2.0-rc.2 把 `settings` 服务重构成 `SettingsForms`,插件不再能注册自己的
|
|
38
|
+
* 命名空间)。这里 re-export 只为不打断既有引用路径。
|
|
39
|
+
*/
|
|
40
|
+
export type { RoundTablePreferences };
|
|
41
|
+
export { ROLE_PRESET_MAX, ROUNDTABLE_PANELS, SQUAD_MAX, SQUAD_MEMBER_MAX, sanitizeHiddenPanels, sanitizeRolePresets, sanitizeSquads, };
|
|
42
|
+
/** 自持久化偏好存储 + 会议 state 根,共享给工具与 RPC 两条执行路径。 */
|
|
43
|
+
export interface RoundTableRuntime {
|
|
44
|
+
/** 偏好读写(见 preferences.ts:宿主 settings 服务在 0.2.0-rc.2 已重构)。 */
|
|
45
|
+
prefs: PreferenceStore;
|
|
46
|
+
stateDir: string;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Browser-facing POST route for this plugin's RPC.
|
|
50
|
+
*
|
|
51
|
+
* It mirrors the snapshot route's transport (the plugin's own `webServer`
|
|
52
|
+
* registration) instead of relying on the host's generic connection channel,
|
|
53
|
+
* which is what the client actually reaches.
|
|
54
|
+
*/
|
|
55
|
+
export declare const RPC_ROUTE = "/plugins/dsh-plugin-roundtable/rpc";
|
|
56
|
+
/** One endpoint dispatch: `(endpoint, payload) => envelope`, never throws. */
|
|
57
|
+
export type RpcDispatch = (endpoint: string, payload: unknown) => Promise<RpcResult<unknown>>;
|
|
58
|
+
/**
|
|
59
|
+
* Register the RoundTable RPC handler.
|
|
60
|
+
*
|
|
61
|
+
* Two transports share ONE dispatch body, so the browser can reach the plugin
|
|
62
|
+
* even when the host's generic `connection` channel is not mounted (or its
|
|
63
|
+
* `/roundtable` prefix got dropped by a live plugin reload):
|
|
64
|
+
* 1. the host `connection` RPC channel `/roundtable` (kept for compatibility);
|
|
65
|
+
* 2. the plugin's own `webServer` POST route `<RPC_ROUTE>` — the same
|
|
66
|
+
* transport as the `/plugins/dsh-plugin-roundtable/state` snapshot route,
|
|
67
|
+
* which is what the browser actually uses.
|
|
68
|
+
*
|
|
69
|
+
* @returns the dispatch function, so the caller can mount it on a web route.
|
|
70
|
+
*/
|
|
71
|
+
export declare function registerRpc(ctx: Context, runtime: RoundTableRuntime): RpcDispatch;
|
|
72
|
+
/** One knowledge-base directory entry (阅览版: name/kind/format/size only). */
|
|
73
|
+
export interface KbEntry {
|
|
74
|
+
name: string;
|
|
75
|
+
kind: 'file' | 'dir';
|
|
76
|
+
/** Lowercased extension without the dot (empty for directories). */
|
|
77
|
+
ext: string;
|
|
78
|
+
size: number;
|
|
79
|
+
mtimeMs: number;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* List the first level of a knowledge-base directory (browse-only): skips
|
|
83
|
+
* dot-hidden entries, caps the result at 300 items so a huge folder cannot
|
|
84
|
+
* blow up the UI, and tolerates per-entry stat failures.
|
|
85
|
+
*/
|
|
86
|
+
export declare function listKbDirectory(dir: string): Promise<{
|
|
87
|
+
error: string;
|
|
88
|
+
files: KbEntry[];
|
|
89
|
+
}>;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DSH 原生 skill 能力的宿主侧读取(R2)。
|
|
3
|
+
*
|
|
4
|
+
* 插件**不**做 skill 导入:skill 的来源是官方目录
|
|
5
|
+
* (`<工作区>/.dsh/skills/`、`<工作区>/.agents/skills/`、`<DSH_HOME>/skills/`
|
|
6
|
+
* 以及 bundled 目录),由 `@deepseek-ai/dsh-skill-filesystem` 发现。本模块只
|
|
7
|
+
* 通过 `ctx.get('skills')` 可选读取清单与正文。
|
|
8
|
+
*
|
|
9
|
+
* 两个硬约束决定了这里的形状:
|
|
10
|
+
* 1. 插件可以在没有挂载 skill 服务的 profile 里加载 —— 因此一律走
|
|
11
|
+
* `ctx.get('skills')` 的**可选**读取,服务缺失只降级为空清单,绝不
|
|
12
|
+
* 让插件加载失败。
|
|
13
|
+
* 2. skill 工具的可用性由宿主决定,"放行 `skill` 工具"不等于"服务可用",
|
|
14
|
+
* 因此放行与否由调用方单独判断(见 members.ts)。
|
|
15
|
+
*
|
|
16
|
+
* @module dsh-plugin-roundtable/skills
|
|
17
|
+
*/
|
|
18
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
19
|
+
/** `ctx.skills.list()` 返回条目的结构性最小视图(只取插件要用的字段)。 */
|
|
20
|
+
export interface SkillSummaryLike {
|
|
21
|
+
readonly name: string;
|
|
22
|
+
readonly description: string;
|
|
23
|
+
readonly whenToUse?: string;
|
|
24
|
+
readonly source?: string;
|
|
25
|
+
readonly provider?: string;
|
|
26
|
+
readonly invocation?: {
|
|
27
|
+
readonly modelInvocable?: boolean;
|
|
28
|
+
readonly userInvocable?: boolean;
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
/** 完整 skill 定义(含正文)。 */
|
|
32
|
+
export interface SkillDefinitionLike extends SkillSummaryLike {
|
|
33
|
+
readonly content: string;
|
|
34
|
+
}
|
|
35
|
+
/** 只声明插件真正用到的两个方法,避免把宿主的服务类绑死在类型里。 */
|
|
36
|
+
export interface SkillRegistryLike {
|
|
37
|
+
list(options?: {
|
|
38
|
+
cwd?: string;
|
|
39
|
+
signal?: AbortSignal;
|
|
40
|
+
}): Promise<SkillSummaryLike[]>;
|
|
41
|
+
get(name: string, options?: {
|
|
42
|
+
cwd?: string;
|
|
43
|
+
signal?: AbortSignal;
|
|
44
|
+
}): Promise<SkillDefinitionLike | undefined>;
|
|
45
|
+
}
|
|
46
|
+
/** 读取 skill 服务;未挂载时返回 undefined(不抛错、不影响插件加载)。 */
|
|
47
|
+
export declare function skillRegistryOf(ctx: Context): SkillRegistryLike | undefined;
|
|
48
|
+
/**
|
|
49
|
+
* 列出某个工作区可见的、**可被模型调用**的 skill 清单。
|
|
50
|
+
*
|
|
51
|
+
* 服务缺失、发现失败或工作区不可读时都降级为空数组:设置卡片上的 skill
|
|
52
|
+
* 列表因此永远不会成为创建会议的阻塞点。
|
|
53
|
+
*/
|
|
54
|
+
export declare function listInvocableSkills(ctx: Context, cwd: string, signal?: AbortSignal): Promise<SkillSummaryLike[]>;
|
|
55
|
+
/**
|
|
56
|
+
* 读取一个 skill 的正文(relay 模式下主持人转交用)。
|
|
57
|
+
* 未挂载服务或 skill 不存在时返回 undefined。
|
|
58
|
+
*/
|
|
59
|
+
export declare function readSkillBody(ctx: Context, name: string, cwd: string, signal?: AbortSignal): Promise<SkillDefinitionLike | undefined>;
|
|
60
|
+
/** 把清单渲染成卡片/工具输出用的一行行文本。 */
|
|
61
|
+
export declare function formatSkillLine(summary: SkillSummaryLike): string;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Web snapshot collection: reads the disk truth under every workspace's
|
|
3
|
+
* state root and merges live node activity. The browser topology tab polls
|
|
4
|
+
* `/plugins/dsh-plugin-roundtable/state` for this.
|
|
5
|
+
* @module dsh-plugin-roundtable/snapshot
|
|
6
|
+
*/
|
|
7
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
8
|
+
/** One meeting snapshot for the Web UI. */
|
|
9
|
+
export interface MeetingSnapshot {
|
|
10
|
+
id: string;
|
|
11
|
+
name: string;
|
|
12
|
+
goal: string;
|
|
13
|
+
mode: string;
|
|
14
|
+
status: string;
|
|
15
|
+
round: number;
|
|
16
|
+
workspace: string;
|
|
17
|
+
/** 主持(创建)该会议的会话 id —— 用于"来源对话"前缀与互通过滤。 */
|
|
18
|
+
captainSessionId: string;
|
|
19
|
+
/** 知识库目录(阅览版);空 = 未设置。 */
|
|
20
|
+
kbPath: string;
|
|
21
|
+
/** R2:本次会议选中的 skill 名称清单;空 = 未选。 */
|
|
22
|
+
skills: string[];
|
|
23
|
+
/** R2.2/D5:skill 传递方式(relay / direct)。 */
|
|
24
|
+
skillDelivery: string;
|
|
25
|
+
budget: {
|
|
26
|
+
maxRounds: number;
|
|
27
|
+
maxTokens: number;
|
|
28
|
+
usedRounds: number;
|
|
29
|
+
usedTokens: number;
|
|
30
|
+
usedTokensReal?: number;
|
|
31
|
+
};
|
|
32
|
+
nodes: {
|
|
33
|
+
id: string;
|
|
34
|
+
key: string;
|
|
35
|
+
role: string;
|
|
36
|
+
provider: string;
|
|
37
|
+
model: string;
|
|
38
|
+
status: string;
|
|
39
|
+
activity: string;
|
|
40
|
+
}[];
|
|
41
|
+
edges: {
|
|
42
|
+
id: string;
|
|
43
|
+
from: string;
|
|
44
|
+
to: string;
|
|
45
|
+
direction: string;
|
|
46
|
+
}[];
|
|
47
|
+
pendingDecisions: {
|
|
48
|
+
id: string;
|
|
49
|
+
question: string;
|
|
50
|
+
options: string[];
|
|
51
|
+
}[];
|
|
52
|
+
/** Pending user actions recorded by the Web UI, awaiting the captain. */
|
|
53
|
+
pendingActions: {
|
|
54
|
+
id: string;
|
|
55
|
+
kind: string;
|
|
56
|
+
nodeKey: string;
|
|
57
|
+
role: string;
|
|
58
|
+
provider: string;
|
|
59
|
+
model: string;
|
|
60
|
+
text: string;
|
|
61
|
+
}[];
|
|
62
|
+
/** 针锋相对评审(无则 null)。 */
|
|
63
|
+
review: {
|
|
64
|
+
status: string;
|
|
65
|
+
reviewPass: number;
|
|
66
|
+
maxReviewPass: number;
|
|
67
|
+
question: string;
|
|
68
|
+
plan: string;
|
|
69
|
+
viewpoints: {
|
|
70
|
+
id: string;
|
|
71
|
+
nodeKey: string;
|
|
72
|
+
content: string;
|
|
73
|
+
endorsed: boolean;
|
|
74
|
+
}[];
|
|
75
|
+
} | null;
|
|
76
|
+
digest: string;
|
|
77
|
+
messages: {
|
|
78
|
+
id: string;
|
|
79
|
+
from: string;
|
|
80
|
+
to: string;
|
|
81
|
+
ts: number;
|
|
82
|
+
}[];
|
|
83
|
+
recent: {
|
|
84
|
+
id: string;
|
|
85
|
+
from: string;
|
|
86
|
+
to: string;
|
|
87
|
+
text: string;
|
|
88
|
+
ts: number;
|
|
89
|
+
round: number;
|
|
90
|
+
}[];
|
|
91
|
+
}
|
|
92
|
+
/** Collect snapshots across state roots, optionally filtered by captain session. */
|
|
93
|
+
export declare function collectMeetingSnapshots(ctx: Context, roots: readonly {
|
|
94
|
+
workspace: string;
|
|
95
|
+
stateRoot: string;
|
|
96
|
+
}[], sessionFilter?: string): Promise<MeetingSnapshot[]>;
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RoundTable durable state: atomic file persistence under
|
|
3
|
+
* `<workspace>/<stateDir>/<meetingId>/` with per-meeting process-local locks.
|
|
4
|
+
*
|
|
5
|
+
* - `meeting.json` — the Meeting record (nodes, edges, decisions, budget).
|
|
6
|
+
* - `transcript.jsonl` — append-only utterance log (torn-tail tolerant on read).
|
|
7
|
+
* - `review.json` — 针锋相对评审记录(议题/方案/观点/支持标记)。
|
|
8
|
+
* - `user-actions.jsonl` — UI 行为记录(主持人下轮执行)。
|
|
9
|
+
* - `kb-digest.json` — 知识库摘要缓存(C2:命中即免去重读)。
|
|
10
|
+
* @module dsh-plugin-roundtable/state
|
|
11
|
+
*/
|
|
12
|
+
import type { FeedbackEntry, KbDigestEntry, KbDigestFile, Meeting, MeetingUtterance, ReviewRecord, UserAction } from './types.ts';
|
|
13
|
+
/** Stable directory id from a display name (keeps CJK, lowercases latin). */
|
|
14
|
+
export declare function sanitizeKey(value: string): string;
|
|
15
|
+
/** Serialize async operations sharing one key inside this process. */
|
|
16
|
+
export declare function withMeetingLock<T>(key: string, operation: () => Promise<T>): Promise<T>;
|
|
17
|
+
/** Absolute state root under a workspace. */
|
|
18
|
+
export declare function stateRootOf(workspace: string, stateDir: string): string;
|
|
19
|
+
/** Absolute directory of one meeting. */
|
|
20
|
+
export declare function meetingDirOf(stateRoot: string, meetingId: string): string;
|
|
21
|
+
/**
|
|
22
|
+
* Drop every persisted record belonging to one meeting directory.
|
|
23
|
+
*
|
|
24
|
+
* `roundtable_create` reuses the id of a finished meeting (the id IS the
|
|
25
|
+
* sanitized name), and while `meeting.json` gets rewritten, `transcript.jsonl`
|
|
26
|
+
* is append-only and `review.json` / `user-actions.jsonl` / `export.md` /
|
|
27
|
+
* `kb-digest.json` simply stay behind. Without this reset a brand-new meeting
|
|
28
|
+
* inherits the previous one's utterances and review verbatim: the user is shown
|
|
29
|
+
* a review window for a plan that belongs to an entirely different meeting and
|
|
30
|
+
* is asked to endorse or reject flaws of a plan that is not the one on the
|
|
31
|
+
* table (P3).
|
|
32
|
+
*
|
|
33
|
+
* Idempotent — a missing directory is not an error.
|
|
34
|
+
*/
|
|
35
|
+
export declare function resetMeetingDirectory(stateRoot: string, meetingId: string): Promise<void>;
|
|
36
|
+
/**
|
|
37
|
+
* Atomically publish a UTF-8 text file.
|
|
38
|
+
*
|
|
39
|
+
* Prefers the host's `@deepseek-ai/dsh-atomic-write`: same-directory
|
|
40
|
+
* random-suffix temp + rename, plus the bounded retry for transient Windows
|
|
41
|
+
* `EACCES`/`EBUSY`/`EPERM` rename failures that our hand-rolled version lacked —
|
|
42
|
+
* it moved the temp aside as `.stale-*` and threw, so **that write was simply
|
|
43
|
+
* lost**. The host version removes its temp and rethrows instead.
|
|
44
|
+
*
|
|
45
|
+
* When the host does not provide that package, the local fallback keeps every
|
|
46
|
+
* state write working (same tmp + rename, minus the Windows retry) rather than
|
|
47
|
+
* failing the whole plugin.
|
|
48
|
+
*/
|
|
49
|
+
export declare function writeTextAtomic(file: string, text: string): Promise<void>;
|
|
50
|
+
/** Read one meeting record; undefined when absent.
|
|
51
|
+
*
|
|
52
|
+
* 与 {@link readReview} 对齐:读出来的记录一律先过 {@link normalizeMeeting}
|
|
53
|
+
* 升级到当前 schema 版本。v0.2.36 之前这里是全插件唯一的「裸
|
|
54
|
+
* `JSON.parse(...) as Meeting`」——形状一变老会议就静默读坏,而且没有任何
|
|
55
|
+
* 版本号可供分支。 */
|
|
56
|
+
export declare function readMeeting(stateRoot: string, meetingId: string): Promise<Meeting | undefined>;
|
|
57
|
+
/** Persist one meeting record (atomic). 写入前同样过一遍 normalize:这样
|
|
58
|
+
* 「读时升级 + 下次写入顺带落盘」形成一个闭环,老文件不会永远停在旧版本。 */
|
|
59
|
+
export declare function writeMeeting(stateRoot: string, meeting: Meeting): Promise<void>;
|
|
60
|
+
/** 当前 `meeting.json` 的形状版本。加字段/改语义时递增,并在
|
|
61
|
+
* {@link MEETING_MIGRATIONS} 补一条从「前一版」升上来的函数。 */
|
|
62
|
+
export declare const CURRENT_MEETING_SCHEMA_VERSION = 1;
|
|
63
|
+
/**
|
|
64
|
+
* Idempotently upgrade one meeting record to
|
|
65
|
+
* {@link CURRENT_MEETING_SCHEMA_VERSION}(**内存内**,不写回;下次
|
|
66
|
+
* {@link writeMeeting} 落盘时顺带升级)。
|
|
67
|
+
*
|
|
68
|
+
* 缺失 `schemaVersion` 视为 1(v0.2.36 及之前的文件都是这个形状),然后
|
|
69
|
+
* 依次跑迁移表。三条防御:
|
|
70
|
+
* - 迁移函数抛错 → 保留原记录并告警,**绝不让读会议失败**(读不出来比读得
|
|
71
|
+
* 不完美严重得多,那会让整场会议从界面上消失);
|
|
72
|
+
* - 版本高于当前(用户从新版本回退到老版本)→ 原样返回并告警,不做降级
|
|
73
|
+
* 猜测;
|
|
74
|
+
* - 版本非法(非整数 / < 1)→ 归一到 1 再走迁移。
|
|
75
|
+
*/
|
|
76
|
+
export declare function normalizeMeeting(meeting: Meeting): Meeting;
|
|
77
|
+
/** Append one utterance to the meeting transcript (JSONL). */
|
|
78
|
+
export declare function appendUtterance(stateRoot: string, meetingId: string, utterance: MeetingUtterance): Promise<void>;
|
|
79
|
+
/** Read the full transcript, skipping torn tails and malformed lines. */
|
|
80
|
+
export declare function readTranscript(stateRoot: string, meetingId: string): Promise<MeetingUtterance[]>;
|
|
81
|
+
/** List every meeting id under a state root (missing root = empty). */
|
|
82
|
+
export declare function listMeetings(stateRoot: string): Promise<string[]>;
|
|
83
|
+
/** Append one user action to the meeting's `user-actions.jsonl` (JSONL).
|
|
84
|
+
* Serialized against {@link clearUserActions} so an append can never race a
|
|
85
|
+
* clear (A2). A single short line stays atomic under `O_APPEND`. */
|
|
86
|
+
export declare function appendUserAction(stateRoot: string, meetingId: string, action: UserAction): Promise<void>;
|
|
87
|
+
/** Result of parsing `user-actions.jsonl`: the usable actions plus the number
|
|
88
|
+
* of lines that were NOT valid JSON. The count exists so a torn tail is
|
|
89
|
+
* *reported* instead of silently vanishing (A2). */
|
|
90
|
+
export interface UserActionsReport {
|
|
91
|
+
actions: UserAction[];
|
|
92
|
+
/** Unparseable lines (torn tail / half-written JSON) that were dropped. */
|
|
93
|
+
malformed: number;
|
|
94
|
+
}
|
|
95
|
+
/** Read every pending user action, counting rather than hiding bad lines. */
|
|
96
|
+
export declare function readUserActionsReport(stateRoot: string, meetingId: string): Promise<UserActionsReport>;
|
|
97
|
+
/** Read every pending user action, skipping torn tails and malformed lines. */
|
|
98
|
+
export declare function readUserActions(stateRoot: string, meetingId: string): Promise<UserAction[]>;
|
|
99
|
+
/**
|
|
100
|
+
* Clear every pending user action, atomically and under the append lock.
|
|
101
|
+
*
|
|
102
|
+
* Returns both how many actions were cleared and how many unparseable lines
|
|
103
|
+
* were dropped, so the captain can tell the user the truth about a loss
|
|
104
|
+
* instead of reporting a clean sweep that never happened (A2).
|
|
105
|
+
*/
|
|
106
|
+
export declare function clearUserActions(stateRoot: string, meetingId: string): Promise<{
|
|
107
|
+
cleared: number;
|
|
108
|
+
malformed: number;
|
|
109
|
+
}>;
|
|
110
|
+
/** Read the 针锋相对 review record; undefined when absent. Migrates old
|
|
111
|
+
* schemaVersion-1 records (endorsed boolean, whole-utterance viewpoints) to
|
|
112
|
+
* the current shape in memory — idempotent, no write-back (next writeReview
|
|
113
|
+
* persists the upgraded shape). */
|
|
114
|
+
export declare function readReview(stateRoot: string, meetingId: string): Promise<ReviewRecord | undefined>;
|
|
115
|
+
/** Idempotently upgrade a review record to schemaVersion 2 (in-memory only).
|
|
116
|
+
* v1: viewpoint = {id(utterance id), nodeKey, content, endorsed, ts} →
|
|
117
|
+
* v2: {id: `${utteranceId}#${seq}`, utteranceId, nodeKey, content,
|
|
118
|
+
* status, quote?, dimension, ts, seq}. v2 records created before the
|
|
119
|
+
* C3 复审轮次 fields existed are given reviewPass=1 / maxReviewPass=3. */
|
|
120
|
+
export declare function normalizeReview(review: ReviewRecord): ReviewRecord;
|
|
121
|
+
/** Persist the review record (atomic). */
|
|
122
|
+
export declare function writeReview(stateRoot: string, meetingId: string, review: ReviewRecord): Promise<void>;
|
|
123
|
+
/** Set one viewpoint's three-state status (pending/endorsed/rejected);
|
|
124
|
+
* when rejecting, the caller must supply a non-empty reason (C2).
|
|
125
|
+
* Returns true when the value changed. */
|
|
126
|
+
export declare function setViewpointStatus(stateRoot: string, meetingId: string, viewpointId: string, status: 'pending' | 'endorsed' | 'rejected', rejectReason?: string): Promise<boolean>;
|
|
127
|
+
/** Read the knowledge-base digest cache.
|
|
128
|
+
*
|
|
129
|
+
* A missing OR corrupt file reads as empty: a bad cache must never block a
|
|
130
|
+
* meeting — the worst case is that the captain re-reads a file, which is
|
|
131
|
+
* exactly the behaviour that existed before the cache. */
|
|
132
|
+
export declare function readKbDigest(stateRoot: string, meetingId: string): Promise<KbDigestFile>;
|
|
133
|
+
/** Persist the knowledge-base digest cache (atomic). */
|
|
134
|
+
export declare function writeKbDigest(stateRoot: string, meetingId: string, entries: readonly KbDigestEntry[]): Promise<void>;
|
|
135
|
+
/** Append one feedback entry to `<stateRoot>/feedback.jsonl`. */
|
|
136
|
+
export declare function appendFeedback(stateRoot: string, entry: FeedbackEntry): Promise<void>;
|
|
137
|
+
/** Read every feedback entry (newest last), skipping torn tails. */
|
|
138
|
+
export declare function readFeedback(stateRoot: string): Promise<FeedbackEntry[]>;
|
|
139
|
+
/** Remove every feedback entry for one meeting id (dedupe); returns count removed. */
|
|
140
|
+
export declare function clearFeedbackForMeeting(stateRoot: string, meetingId: string): Promise<number>;
|
|
141
|
+
/** Remove every feedback entry; returns how many were dropped. */
|
|
142
|
+
export declare function clearFeedback(stateRoot: string): Promise<number>;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 真实用量读取(第 3 批)。
|
|
3
|
+
*
|
|
4
|
+
* 背景:`meeting.budget.usedTokens` 只累加**发言文本**的粗估(≈0.6 token/字),
|
|
5
|
+
* 不含 system prompt、专家 persona、历史上下文与工具调用开销。真实会议数据
|
|
6
|
+
* 里,它 7/7 场与"逐条发言文本估算"完全相等,而主持人自己的发言就吃掉
|
|
7
|
+
* 38.9%(某一场达 73.4%)—— 用户设 200k,专家实际能用的远不到一半。v0.2.35
|
|
8
|
+
* 只补了 UI 标注("不是真实账单"),根因未修。
|
|
9
|
+
*
|
|
10
|
+
* 宿主其实有精确的 provider 上报值:
|
|
11
|
+
* - `ctx.sessionProjections.snapshot(session, ['tokenUsage'])` 的
|
|
12
|
+
* `tokenUsage` 由每个 assistant settlement 的 usage 采样累加而来
|
|
13
|
+
* (`uncachedInputTokens` / `outputTokens` / `cacheReadTokens` /
|
|
14
|
+
* `cacheWriteTokens`,四桶互不重叠);
|
|
15
|
+
* - `Agent.session` 是拿到 `Session` 的现成入口(`tools.ts` 早已在用
|
|
16
|
+
* `agent.session.header.cwd`)。
|
|
17
|
+
*
|
|
18
|
+
* **明确否定的一条**:`ctx.subagents.sendMessage` / `startContinuable` 的返回值
|
|
19
|
+
* 都不带 usage,`SubagentResult` / `SubagentRunEndInfo` 也没有 —— 想接真实用量
|
|
20
|
+
* 只能走"子会话的 session"这条路。
|
|
21
|
+
*
|
|
22
|
+
* 本模块只读、不写状态:把真实值作为 `roundtable_status` 的派生输出,而不是
|
|
23
|
+
* 新落一个半成品字段(否则又是"定义了却没人用"的账)。冷会话(进程重启后的
|
|
24
|
+
* 旧节点)取不到值 —— 宁可标注"不可测",也不拿估算冒充实测。
|
|
25
|
+
*
|
|
26
|
+
* @module dsh-plugin-roundtable/token-usage
|
|
27
|
+
*/
|
|
28
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
29
|
+
/** Provider-reported cumulative usage of one session. */
|
|
30
|
+
export interface SessionUsage {
|
|
31
|
+
inputTokens: number;
|
|
32
|
+
outputTokens: number;
|
|
33
|
+
cacheReadTokens: number;
|
|
34
|
+
cacheWriteTokens: number;
|
|
35
|
+
/** Sum of the four disjoint buckets. */
|
|
36
|
+
total: number;
|
|
37
|
+
}
|
|
38
|
+
/** Provider-anchored context occupancy of one session. */
|
|
39
|
+
export interface SessionPressure {
|
|
40
|
+
/** Prompt size of the most recent request (uncached input + cache read/write). */
|
|
41
|
+
pressureTokens: number;
|
|
42
|
+
/** What the NEXT request's prompt would cost. */
|
|
43
|
+
projectedTokens: number;
|
|
44
|
+
/** Newest known route capacity (0 = adapter advertised none). */
|
|
45
|
+
contextWindow: number;
|
|
46
|
+
}
|
|
47
|
+
/** How many expert sessions could be measured for one meeting. */
|
|
48
|
+
export interface MeetingRealUsage {
|
|
49
|
+
/** Sum of measured session totals. */
|
|
50
|
+
measuredTotal: number;
|
|
51
|
+
/** Sessions that produced a provider-reported figure. */
|
|
52
|
+
measuredSessions: number;
|
|
53
|
+
/** Child sessions that exist but could not be measured (cold / not live). */
|
|
54
|
+
unmeasuredSessions: number;
|
|
55
|
+
}
|
|
56
|
+
/** Coerce one projection value into a {@link SessionUsage}; undefined when empty. */
|
|
57
|
+
export declare function normalizeUsage(value: unknown): SessionUsage | undefined;
|
|
58
|
+
/** Coerce one projection value into a {@link SessionPressure}; undefined when empty. */
|
|
59
|
+
export declare function normalizePressure(value: unknown): SessionPressure | undefined;
|
|
60
|
+
/** Provider-reported usage of one live session (undefined when unavailable). */
|
|
61
|
+
export declare function liveSessionUsage(ctx: Context, sessionId: string): SessionUsage | undefined;
|
|
62
|
+
/** Provider-anchored pressure of one live session (undefined when unavailable). */
|
|
63
|
+
export declare function liveSessionPressure(ctx: Context, sessionId: string): SessionPressure | undefined;
|
|
64
|
+
/**
|
|
65
|
+
* Sum real usage across one meeting's expert child sessions.
|
|
66
|
+
*
|
|
67
|
+
* Each distinct session is measured once; ids that no longer resolve to a live
|
|
68
|
+
* agent are counted as `unmeasuredSessions` so the caller can say "3 of 5
|
|
69
|
+
* measured" instead of silently reporting a too-small total.
|
|
70
|
+
*/
|
|
71
|
+
export declare function meetingRealUsage(ctx: Context, sessionIds: readonly string[]): MeetingRealUsage;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `roundtable_*` model-facing tools.
|
|
3
|
+
*
|
|
4
|
+
* The captain (the session that created the meeting) orchestrates: expert
|
|
5
|
+
* nodes are continuable subagents it spawns and wakes. Nodes share the same
|
|
6
|
+
* tools, speak through `roundtable_speak`, and — in egalitarian mode — message
|
|
7
|
+
* each other directly. The aggregation gateway is a deterministic merge the
|
|
8
|
+
* captain pulls with `roundtable_summarize`; human decisions pause the turn
|
|
9
|
+
* through the `userQuestions` seam.
|
|
10
|
+
* @module dsh-plugin-roundtable/tools
|
|
11
|
+
*/
|
|
12
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
13
|
+
import type { Meeting, MeetingUtterance, ReviewRecord, SkillDelivery, UserAction } from './types.ts';
|
|
14
|
+
/** Resolved plugin config consumed by the tools. */
|
|
15
|
+
export interface ToolsConfig {
|
|
16
|
+
/** State directory name under the captain's workspace. */
|
|
17
|
+
stateDir: string;
|
|
18
|
+
/** Node subagent provider name. */
|
|
19
|
+
memberProvider: string;
|
|
20
|
+
/** Meeting size cap (nodes). */
|
|
21
|
+
maxNodes: number;
|
|
22
|
+
/** Default collaboration mode. */
|
|
23
|
+
defaultMode: 'orchestrated' | 'egalitarian' | 'redteam';
|
|
24
|
+
/** Node delegation depth cap. */
|
|
25
|
+
memberMaxDepth?: number;
|
|
26
|
+
/** Live expert answer limits from settings (read at every spawn). */
|
|
27
|
+
getExpertLimits?: () => {
|
|
28
|
+
maxTokens: number;
|
|
29
|
+
maxOpinions: number;
|
|
30
|
+
};
|
|
31
|
+
/** Live default skill-delivery mode from settings (R2.2/D5). */
|
|
32
|
+
getSkillDelivery?: () => SkillDelivery;
|
|
33
|
+
/** 设置卡片默认值(R1,设置页那一层;调用时实时读取)。 */
|
|
34
|
+
getPlannedDefaults?: () => PlannedDefaults;
|
|
35
|
+
/** 观点拆分 LLM 路由(V0.2.2):collect_review 时把发言拆成独立观点。 */
|
|
36
|
+
reviewSplit?: {
|
|
37
|
+
provider: string;
|
|
38
|
+
model: string;
|
|
39
|
+
maxOpinions: number;
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
/** Resolved defaults for one meeting's settings card (R1). */
|
|
43
|
+
interface PlannedDefaults {
|
|
44
|
+
mode: 'orchestrated' | 'egalitarian' | 'redteam';
|
|
45
|
+
maxRounds: number;
|
|
46
|
+
maxTokens: number;
|
|
47
|
+
skillDelivery: SkillDelivery;
|
|
48
|
+
}
|
|
49
|
+
/** Register every `roundtable_*` tool into the shared tools registry. */
|
|
50
|
+
export declare function registerRoundTableTools(ctx: Context, config: ToolsConfig): void;
|
|
51
|
+
/**
|
|
52
|
+
* 把一场会议整体渲染成 Markdown 交付物(B6)。
|
|
53
|
+
*
|
|
54
|
+
* 只读既有磁盘状态(`meeting.json` / `transcript.jsonl` / `review.json` /
|
|
55
|
+
* `user-actions.jsonl`),**不新增任何状态**;会议进行中也能导出,此时头部
|
|
56
|
+
* 会标注"进行中快照",避免把半场会议误当成结论。
|
|
57
|
+
*/
|
|
58
|
+
export declare function renderMeetingMarkdown(meeting: Meeting, utterances: readonly MeetingUtterance[], userActions: readonly UserAction[], review: ReviewRecord | undefined): string;
|
|
59
|
+
export {};
|