hiwork-core 0.0.22 → 0.0.29
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/cordis.patch.yml +20 -4
- package/lib/ai-router-client.js +142 -1
- package/lib/client.js +961 -33
- package/lib/index.js +2185 -353
- package/lib/knowledge-config.js +273 -0
- package/lib/pms-client.js +216 -61
- package/lib/pms-todo-bridge.js +332 -313
- package/lib/types/ai-router-client.d.ts +61 -0
- package/lib/types/client/contracts.d.ts +10 -8
- package/lib/types/client/hero-greeting.d.ts +0 -11
- package/lib/types/client/pms-todo.d.ts +67 -49
- package/lib/types/client/styles.d.ts +28 -0
- package/lib/types/client/types.d.ts +13 -1
- package/lib/types/client/voice/MicButton.d.ts +24 -0
- package/lib/types/client/voice/VoiceOverlay.d.ts +37 -0
- package/lib/types/client/voice/composer.d.ts +98 -0
- package/lib/types/client/voice/entry.d.ts +46 -0
- package/lib/types/client/voice/recording.d.ts +57 -0
- package/lib/types/client/voice/voice-data.d.ts +101 -0
- package/lib/types/client/voice/voice-styles.d.ts +9 -0
- package/lib/types/index.d.ts +3 -1
- package/lib/types/knowledge-config.d.ts +115 -0
- package/lib/types/pms-client.d.ts +99 -34
- package/lib/types/pms-todo-bridge.d.ts +210 -184
- package/lib/types/voice-rpc.d.ts +133 -0
- package/lib/types/web-search.d.ts +123 -0
- package/lib/voice-rpc.js +211 -0
- package/lib/web-search.js +259 -0
- package/package.json +5 -3
|
@@ -286,6 +286,23 @@ export interface GatewayRelayContent {
|
|
|
286
286
|
revision: number;
|
|
287
287
|
bytes?: Uint8Array;
|
|
288
288
|
}
|
|
289
|
+
/**
|
|
290
|
+
* GET /api/me/voice-config 响应。
|
|
291
|
+
*
|
|
292
|
+
* 网关线上的字段是 snake_case `max_audio_seconds`,**在这一层就转成 camelCase**:
|
|
293
|
+
* Host 频道、浏览器半边与测试都只认 `maxDurationSeconds` 一种写法,免得同一个概念
|
|
294
|
+
* 在链路里换两次名字。
|
|
295
|
+
*/
|
|
296
|
+
export interface GatewayVoiceConfig {
|
|
297
|
+
/** 本部署 + 本账号是否可用语音识别(管理员开关)。 */
|
|
298
|
+
enabled: boolean;
|
|
299
|
+
/** 单次录音时长上限(秒):录音端据此自动停录,别让用户录到被网关 413 拒掉。 */
|
|
300
|
+
maxDurationSeconds: number;
|
|
301
|
+
}
|
|
302
|
+
/** POST /api/me/voice-transcribe 响应:识别出的文本(可能为空串,即「没听到人话」)。 */
|
|
303
|
+
export interface GatewayVoiceTranscription {
|
|
304
|
+
text: string;
|
|
305
|
+
}
|
|
289
306
|
/**
|
|
290
307
|
* POST /api/me/mcp-assertions 响应:一张 **120 秒**的身份断言(RS256 JWT)+
|
|
291
308
|
* 去哪换令牌。断言本身不是凭据(换不成令牌就是废纸),所以它可以出现在这里;
|
|
@@ -627,6 +644,50 @@ export declare class AiRouterClient implements GatewayClient {
|
|
|
627
644
|
* 那里才决定「这一份算不算成功拉到的清单」。
|
|
628
645
|
*/
|
|
629
646
|
getMyWorkbenchApps(sessionCookie: string): Promise<GatewayWorkbenchAppsView>;
|
|
647
|
+
/**
|
|
648
|
+
* GET /api/me/voice-config:当前账号的语音识别开关与录音时长上限。
|
|
649
|
+
*
|
|
650
|
+
* 与每日一句的 `getMyDailyQuote` 同款,**这两个方法不在 {@link GatewayClient} 接口上**:
|
|
651
|
+
* 那个接口是编排层的端口,「语音识别」与它无关,加进去只会逼着每处编排层假网关
|
|
652
|
+
* (tests/orchestration.spec.ts 里一堆)都补两个用不到的桩。频道侧改用结构子集
|
|
653
|
+
* `VoiceGatewayPort`(见 `src/voice-rpc.ts`),与 usage / daily 三处一致。
|
|
654
|
+
*
|
|
655
|
+
* 与 {@link getMyMcpAssignments} 同样的理由**不复用 `getMeJson`**:那条路径把 403 映射成
|
|
656
|
+
* `NO_KEY_ASSIGNED`(「尚未被分配虚拟 KEY」),而本端点的鉴权只有会话——一个还没被分配
|
|
657
|
+
* Key 的账号照样应该有语音识别(分能力与分 Key 是两件事)。沿用它的文案会把「没有 Key」
|
|
658
|
+
* 错报成原因。
|
|
659
|
+
*
|
|
660
|
+
* 本端点契约**恒 200**(开关开与关都是 200 + `enabled` 布尔),因此除 401 以外没有任何
|
|
661
|
+
* 需要单独解释的拒绝语义:403 / 404 / 5xx / 反代错误页一律收敛成 `GATEWAY_UNREACHABLE`。
|
|
662
|
+
*
|
|
663
|
+
* 形状收敛(**本层负责**,消费方拿到的永远是一个合法的 {@link GatewayVoiceConfig}):
|
|
664
|
+
* 网关线上是 snake_case `max_audio_seconds`,这里转 camelCase;字段缺失 / 类型不符
|
|
665
|
+
* 按「禁用 + 60 秒」处理——配置读不出来时话筒本就该消失,用一个 undefined 让消费方
|
|
666
|
+
* 去猜是把形状问题推给下游。真正的「读不到就回落」再往上一层(`src/voice-rpc.ts`)。
|
|
667
|
+
*/
|
|
668
|
+
getMyVoiceConfig(sessionCookie: string): Promise<GatewayVoiceConfig>;
|
|
669
|
+
/**
|
|
670
|
+
* POST /api/me/voice-transcribe:把一段录音(base64)交给网关转发上游做识别。
|
|
671
|
+
*
|
|
672
|
+
* 状态码映射与服务端 handler 一一对应,**不许合并**:
|
|
673
|
+
* - 401 = 会话过期(唯一一条能让人做点什么——重新登录——的拒绝);
|
|
674
|
+
* - 503 = 网关自己发的「功能不可用」,再按响应体 `code` 细分:
|
|
675
|
+
* `VOICE_NOT_CONFIGURED`(部署没配上游)/ `VOICE_DISABLED`(管理员把开关关了)。
|
|
676
|
+
* 两者给的提示不同(该找管理员配服务 vs 该找管理员开开关)。
|
|
677
|
+
* ⚠️ 响应体没有 `code`(非 JSON / 反代错误页)也归 `VOICE_DISABLED`——这条路由的
|
|
678
|
+
* 503 语义就是「没开」,而**上游故障走 502**(见下),不会伪装成 503;
|
|
679
|
+
* - 429 / 413 / 400 = 配额 / 超长 / 请求体不合法,三种不同的事,分开映射(合并后
|
|
680
|
+
* 客户端只会看到一句「识别失败」,413 的人不知道该录短一点);
|
|
681
|
+
* - 其余(含 502 = 上游出错)→ `VOICE_UPSTREAM`。
|
|
682
|
+
*
|
|
683
|
+
* message 一律取网关响应体里的 `message`(网关侧是冻结的中文文案,会直接展示给用户);
|
|
684
|
+
* 响应体不是合法 JSON / 缺 message 时退回本地兜底文案。**绝不把上游 / 网关原文回显**:
|
|
685
|
+
* 读取响应体走 {@link readVoiceError}(解析失败即空对象),与 `parseMeJson` 不回显原文同源。
|
|
686
|
+
*
|
|
687
|
+
* 超时**单独放宽到 60s**(见 {@link VOICE_TRANSCRIBE_TIMEOUT_MS}):这条要上传录音 +
|
|
688
|
+
* 等上游识别,构造时的 10s 默认值在这里是误报源。
|
|
689
|
+
*/
|
|
690
|
+
transcribeVoice(sessionCookie: string, audioBase64: string): Promise<GatewayVoiceTranscription>;
|
|
630
691
|
/**
|
|
631
692
|
* /api/me 自助端点的 GET 公共路径:网络异常 → GATEWAY_UNREACHABLE,
|
|
632
693
|
* 401/403/400 映射为稳定 code,响应体非 JSON 时不把原文带进异常。
|
|
@@ -104,23 +104,25 @@ export interface HiWorkCoreClientFace {
|
|
|
104
104
|
readonly managedWorkbench?: ManagedWorkbenchFace;
|
|
105
105
|
/**
|
|
106
106
|
* PMS 个人待办(工作台插件 hiwork-workbench 的右栏 tab 消费,见 `src/client/pms-todo.ts`;
|
|
107
|
-
* 契约见 hiwork-workbench 的 spec `docs/superpowers/specs/2026-09-
|
|
108
|
-
* §4.
|
|
107
|
+
* 契约见 hiwork-workbench 的 spec `docs/superpowers/specs/2026-09-21-pms-zero-input-login-design.md`
|
|
108
|
+
* §4.3 / §4.4 与 §3 D7)。
|
|
109
109
|
*
|
|
110
110
|
* **可选成员**(与 `managedMcp` / `managedSkills` / `managedWorkbench` 同款的两级可选语义):
|
|
111
111
|
* 新 core 遇到老 workbench 只是**没人来取**;老 core 遇到新 workbench 只是**没有这个面**
|
|
112
112
|
* (`face.pmsTodo` 为 `undefined` → 右栏 tab 显示「请升级 HiWork 客户端」、工作台入口卡片不显示,
|
|
113
113
|
* spec §6 的降级矩阵第一行,不报错、不空白)。两侧因此不必严格同发一个版本。
|
|
114
114
|
*
|
|
115
|
+
* ⚠️ **这一面没有登录动作**(spec §3 D3):零输入登录(网关身份断言 → PMS 换令牌)整段在 Host
|
|
116
|
+
* 半边发生,客户端连断言都看不到;`login` / `logout` 已随账号密码通道一起删除,本面**没有任何**
|
|
117
|
+
* 取 token / 取密码 / 收密码的成员(§3 D6 / §7:JWT 只在 Host 内存,前端 bundle 里搜不到 PMS 凭据)。
|
|
118
|
+
* 未就绪的两种态看 `status.reason`(`no-assignment` → 找管理员;`gateway-signed-out` → 重新登录
|
|
119
|
+
* HiWork 客户端)——面板据此选文案,**不许**给出任何可填表单。
|
|
120
|
+
*
|
|
115
121
|
* ⚠️ **这一面里还有第二级可选**:§3 D7 追加的 `myTasks` / `completeTask` **在面里也是可选的**
|
|
116
|
-
* (旧 core
|
|
122
|
+
* (旧 core 有这一面但只有前六项)。消费方按 `face.pmsTodo.myTasks !== undefined` 判断,
|
|
117
123
|
* 拿不到就只渲染待办那一半——**不许**把「没有这个能力」渲染成「我的任务为空」,那会让人
|
|
118
124
|
* 以为任务都完成了。同理,新的那一侧也不许把它们改成必填(详见 `PmsTodoFace` 的注释)。
|
|
119
|
-
*
|
|
120
|
-
* ⚠️ **账号密码不经过这一面**:`login()` 的入参里出现的密码只走这一次 RPC 调用,随即由 Host
|
|
121
|
-
* 侧交给 credentials 服务;本面**没有任何**取 token / 取密码的成员,返回的状态里也没有凭据
|
|
122
|
-
* (spec §3 D6 / §7:JWT 只在 Host 内存 + credentials,前端 bundle 里搜不到 PMS 凭据)。
|
|
123
|
-
* `myTasks` 也不收 `userId`——「谁的任务」由 Host 从凭据记录里取(同 §3 D6 的形状)。
|
|
125
|
+
* `myTasks` 也不收 `userId`——「谁的任务」由 Host 从 mint 响应里取(同 §3 D6 的形状)。
|
|
124
126
|
*/
|
|
125
127
|
readonly pmsTodo?: PmsTodoFace;
|
|
126
128
|
}
|
|
@@ -1,14 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* hero 问候语轮换(每日卡片的同页组件,自 hiwork-dsh-ui 静态版演化):
|
|
3
|
-
* 官方口号「探索未至之境 / Into the Unknown」替换为「HiWork,+ 候选池随机一条」,
|
|
4
|
-
* 前缀固定保留,候选每次进入 hero 页时随机切换(排除上一条,不重复);
|
|
5
|
-
* 同时隐藏鱼形品牌标记与「预览版」徽标。
|
|
6
|
-
*
|
|
7
|
-
* 实现:宿主标题节点用 CSS 归零隐藏,问候语是插件自有(data-hiwork-hero-greeting
|
|
8
|
-
* 标记)的相邻 span,由观察器幂等插入——不搬移/不改写宿主节点,宿主重建 hero 时自动
|
|
9
|
-
* 重挂并换下一条,非 hero 阶段自动清残留。样式 id 改用 hiwork 前缀:本模块已是
|
|
10
|
-
* dsh-ui 静态 ::after 版的超集,两版并存会双份文案。
|
|
11
|
-
*/
|
|
12
1
|
/** 问候候选池(前缀 HiWork,之后的部分),措辞逐字保留产品定稿。 */
|
|
13
2
|
export declare const HERO_GREETINGS: readonly string[];
|
|
14
3
|
/** 固定前缀:所有候选共用,轮换的只是后半句。 */
|
|
@@ -1,22 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* 「PMS 个人待办」客户端取数层:经 `/hiwork-pms-todo` loopback RPC
|
|
3
|
-
*
|
|
4
|
-
* 右栏 tab 渲染。
|
|
2
|
+
* 「PMS 个人待办」客户端取数层:经 `/hiwork-pms-todo` loopback RPC 拿到自己的待办、计数与项目任务,
|
|
3
|
+
* 交给工作台插件(hiwork-workbench)的右栏 tab 渲染。
|
|
5
4
|
*
|
|
6
|
-
* 契约见 spec `docs/superpowers/specs/2026-09-
|
|
7
|
-
* §4.
|
|
8
|
-
* 本文件与 `src/client/managed-workbench.ts`
|
|
9
|
-
* 那条链是纯只读,这一条还带「登录 / 登出」两个动作。
|
|
5
|
+
* 契约见 spec `docs/superpowers/specs/2026-09-21-pms-zero-input-login-design.md` §4.3(Host 频道)、
|
|
6
|
+
* §4.4(本面)与 §3 D7(**我的任务**:待办之外还要直接查未完成的项目任务)。
|
|
7
|
+
* 本文件与 `src/client/managed-workbench.ts` 同款,是同一副骨架的**第四条链**。
|
|
10
8
|
*
|
|
11
9
|
* 铁律:
|
|
12
|
-
* -
|
|
13
|
-
*
|
|
10
|
+
* - **本层不持有任何凭据,也没有登录动作**:零输入登录(网关身份断言 → PMS 换令牌)整段发生在
|
|
11
|
+
* Host 半边,客户端连断言长什么样都看不到;`login` / `logout` 两个端点已经随账号密码通道一起
|
|
12
|
+
* 删除(spec §3 D3)——本文件里**不许**再长出任何输入框式的成员;
|
|
14
13
|
* - **不 fetch、不直连 PMS**:一切经 Host 频道——这正是 §3 D6「凭据与数据流都在 Host」在
|
|
15
14
|
* 客户端一侧的落地形状;
|
|
16
15
|
* - **错误码原样透传**:Host 的六个稳定码(`NOT_CONFIGURED` / `UNAUTHORIZED` / `FORBIDDEN` /
|
|
17
|
-
* `UPSTREAM_UNAVAILABLE` / `BAD_REQUEST` / `INTERNAL
|
|
18
|
-
*
|
|
19
|
-
*
|
|
16
|
+
* `UPSTREAM_UNAVAILABLE` / `BAD_REQUEST` / `INTERNAL`)由消费方决定文案(`NOT_CONFIGURED` →
|
|
17
|
+
* 「找管理员」;`UNAUTHORIZED` → 「重新登录 HiWork 客户端」;`FORBIDDEN` → 「到 PMS 操作」,
|
|
18
|
+
* **不是**重新登录)。本层**不改写、不吞掉**它们。
|
|
20
19
|
*
|
|
21
20
|
* client bundle 纯度:本文件不 import 任何 `@deepseek-ai/*` 运行时值,也不 import Host 代码
|
|
22
21
|
* (类型按结构重声明,字段名与契约一致)。
|
|
@@ -25,10 +24,14 @@
|
|
|
25
24
|
export declare const PMS_TODO_CHANNEL = "/hiwork-pms-todo";
|
|
26
25
|
/**
|
|
27
26
|
* 端点白名单;必须与 Host `PMS_TODO_RPC_ENDPOINTS` 一致(由测试锁定)。
|
|
28
|
-
* 顺序即冻结契约里的顺序(`status` / `
|
|
29
|
-
* `
|
|
27
|
+
* 顺序即冻结契约里的顺序(`status` 是能力状态,`today` / `statistics` / `setStatus` 是待办数据,
|
|
28
|
+
* `myTasks` / `completeTask` 是项目任务 —— §3 D7)。
|
|
29
|
+
*
|
|
30
|
+
* ⚠️ 六项与 v1 的八项**刻意不同**(v1 还有 `login` / `logout`):那是版本闸口——老 client 拿八项
|
|
31
|
+
* 去比对新 Host 会对不上,于是走「请升级 HiWork 客户端」的降级,而不是带着一个已经删掉的
|
|
32
|
+
* 账号密码表单硬跑(spec §4.3)。
|
|
30
33
|
*/
|
|
31
|
-
export declare const PMS_TODO_ENDPOINTS: readonly ["status", "
|
|
34
|
+
export declare const PMS_TODO_ENDPOINTS: readonly ["status", "today", "statistics", "setStatus", "myTasks", "completeTask"];
|
|
32
35
|
export type PmsTodoEndpoint = (typeof PMS_TODO_ENDPOINTS)[number];
|
|
33
36
|
/** 勾选用的两个字面量(PMS 只认这两个)。 */
|
|
34
37
|
export type PmsTodoStatusValue = 'PENDING' | 'COMPLETED';
|
|
@@ -58,14 +61,38 @@ export interface PmsTodoItem {
|
|
|
58
61
|
isOverdue?: boolean;
|
|
59
62
|
isDueSoon?: boolean;
|
|
60
63
|
}
|
|
61
|
-
/**
|
|
64
|
+
/**
|
|
65
|
+
* `status` 的返回值(spec §4.4;字段名沿用 v1,语义重定义)。
|
|
66
|
+
*
|
|
67
|
+
* `configured` = **网关里存在 `slug='pms'` 的分配**(false → 「找管理员」,面板不显示任何可填表单);
|
|
68
|
+
* `loggedIn` = **手上有一张未过期的 PMS JWT**。`username`(mint 响应里的 PMS 工号)与 `expiresAt`
|
|
69
|
+
* (ISO 8601,**不是** token 本身)只给界面展示用;`userId` 是「我的任务」的查询键(§3 D7),
|
|
70
|
+
* 客户端**没有**任何按它查询的入口。
|
|
71
|
+
*/
|
|
62
72
|
export interface PmsTodoStatus {
|
|
63
73
|
configured: boolean;
|
|
64
74
|
loggedIn: boolean;
|
|
65
75
|
username?: string;
|
|
66
|
-
/** access token 的绝对过期时刻(ISO 8601)。**不是** token 本身。 */
|
|
67
76
|
expiresAt?: string;
|
|
77
|
+
userId?: number;
|
|
78
|
+
/**
|
|
79
|
+
* 为什么还没就绪(spec §4.4 新增的可选字段;三种取值对应三个不同的人 / 动作)。
|
|
80
|
+
*
|
|
81
|
+
* 不认识的取值**原样保留**语义上的开放:这里是联合类型,但 `toStatus` 遇到未知串会**丢掉**它
|
|
82
|
+
* (不是把整份状态判为无效)——老客户端遇到新 reason 只会少一条提示,不会崩页。
|
|
83
|
+
*/
|
|
84
|
+
reason?: PmsTodoStatusReason;
|
|
68
85
|
}
|
|
86
|
+
/**
|
|
87
|
+
* `reason` 的三个取值(spec §4.4)。
|
|
88
|
+
*
|
|
89
|
+
* `no-assignment` → 找管理员(客户端没被接进 PMS);`gateway-signed-out` → 用户自己在 HiWork
|
|
90
|
+
* 客户端重新登录;`unavailable` → 稍后重试(保留上次数据 + 陈旧标记)。合并成一个码会让用户
|
|
91
|
+
* 对着一个自己修不了的问题反复重试(§3 D8)。
|
|
92
|
+
*/
|
|
93
|
+
export type PmsTodoStatusReason = 'no-assignment' | 'gateway-signed-out' | 'unavailable';
|
|
94
|
+
/** `reason` 的白名单(`toStatus` 用它挡掉未知串;顺序即契约里的顺序)。 */
|
|
95
|
+
export declare const PMS_TODO_STATUS_REASONS: readonly ["no-assignment", "gateway-signed-out", "unavailable"];
|
|
69
96
|
/** `today` 的返回值。 */
|
|
70
97
|
export interface PmsTodoToday {
|
|
71
98
|
items: PmsTodoItem[];
|
|
@@ -139,20 +166,16 @@ export interface PmsTaskCompleteResult {
|
|
|
139
166
|
/** 任务状态数字(完成是 2)。 */
|
|
140
167
|
status: number;
|
|
141
168
|
}
|
|
142
|
-
/** 登录入参。 */
|
|
143
|
-
export interface PmsTodoLoginInput {
|
|
144
|
-
username: string;
|
|
145
|
-
password: string;
|
|
146
|
-
/** 是否把凭据交给 credentials 服务加密保存(Host 侧语义,见 spec §4.4)。 */
|
|
147
|
-
remember?: boolean;
|
|
148
|
-
}
|
|
149
169
|
/**
|
|
150
|
-
* 「PMS 待办」面(spec §4.
|
|
170
|
+
* 「PMS 待办」面(spec §4.4 冻结的六项 + §3 D7 追加的两项)。
|
|
151
171
|
*
|
|
152
172
|
* 它挂在 `HiWorkCoreClientFace` 上作为**可选成员** `pmsTodo?`:老 core 没有这个面,老
|
|
153
173
|
* workbench 也不会来取;两侧因此不必同发一个版本——老 core 遇新 workbench 就是
|
|
154
174
|
* 「请升级 HiWork 客户端」(spec §6 的降级矩阵第一行)。
|
|
155
175
|
*
|
|
176
|
+
* ⚠️ **没有 `login` / `logout`**(v1 有,spec §3 D3 随账号密码通道一起删除):零输入登录整段在
|
|
177
|
+
* Host 半边发生,客户端没有任何凭据入口——**不许**为了「兼容」把它们加回来。
|
|
178
|
+
*
|
|
156
179
|
* ⚠️ **`myTasks` / `completeTask` 是这一面里的两级可选**(版本闸口):§3 D7 是 2026-09-18 追加的,
|
|
157
180
|
* 装着的旧 core 有 `pmsTodo` 面但**没有**这两项。所以它们是**可选成员**,消费方必须按
|
|
158
181
|
* `face.pmsTodo.myTasks !== undefined` 判断(拿不到就只显示待办那一半,而不是崩页、也不是把
|
|
@@ -163,15 +186,15 @@ export interface PmsTodoFace {
|
|
|
163
186
|
/**
|
|
164
187
|
* 本地镜像(**同步读**,可能是 `undefined`)。
|
|
165
188
|
*
|
|
166
|
-
* ⚠️
|
|
167
|
-
*
|
|
168
|
-
*
|
|
189
|
+
* ⚠️ 它是镜像是**有意的**:界面渲染是同步的,而「网关里有没有 pms 分配 / 手上有没有令牌」是
|
|
190
|
+
* 异步来的。镜像由 `refreshStatus()` 的成功返回值、以及**首次调用本方法时的一次惰性探测**写入
|
|
191
|
+
* (结果到达后通知订阅者),所以调用方必须 `subscribe()` ——首次渲染很可能拿到 `undefined`,
|
|
192
|
+
* 等通知到了再重渲。
|
|
193
|
+
*
|
|
194
|
+
* ⚠️ `undefined` **不是**「未配置」:那是「还不知道」(探测还没回来)。未就绪的两种态要看
|
|
195
|
+
* `status.reason`(`no-assignment` → 找管理员;`gateway-signed-out` → 重新登录 HiWork 客户端)。
|
|
169
196
|
*/
|
|
170
197
|
readonly status: () => PmsTodoStatus | undefined;
|
|
171
|
-
/** 登录(凭据经 Host 换成 JWT;密码只在这一次调用里出现,本层不留存)。失败抛错。 */
|
|
172
|
-
readonly login: (input: PmsTodoLoginInput) => Promise<PmsTodoStatus>;
|
|
173
|
-
/** 登出(Host 清内存凭据 + 抹掉 credentials 记录)。 */
|
|
174
|
-
readonly logout: () => Promise<PmsTodoStatus>;
|
|
175
198
|
/** 今日待办(⚠️ 口径比名字宽:今天 + 明天 + 无截止日期,spec §D4)。失败抛错。 */
|
|
176
199
|
readonly today: (signal?: AbortSignal) => Promise<PmsTodoToday>;
|
|
177
200
|
/** total / pending / completed / overdue 计数。失败抛错。 */
|
|
@@ -189,7 +212,7 @@ export interface PmsTodoFace {
|
|
|
189
212
|
/**
|
|
190
213
|
* **我的未完成项目任务**(spec §3 D7)。**可选成员**——旧 core 上没有它(见本接口的抬头)。
|
|
191
214
|
*
|
|
192
|
-
* 「谁的任务」由 Host
|
|
215
|
+
* 「谁的任务」由 Host 从 mint 响应里取(客户端传不进来),所以这里没有 `userId` 入参。
|
|
193
216
|
* 失败抛错,错误码可判 `FORBIDDEN`(没有完成任务权限的角色会在**完成**时报它)。
|
|
194
217
|
*/
|
|
195
218
|
readonly myTasks?: (signal?: AbortSignal) => Promise<PmsTaskList>;
|
|
@@ -198,12 +221,12 @@ export interface PmsTodoFace {
|
|
|
198
221
|
*
|
|
199
222
|
* ⚠️ 与 `setStatus`(待办)是**两条不同的上游路径**:待办那条没有权限注解,这一条需要
|
|
200
223
|
* `task:list:edit`;缺权限时抛 `FORBIDDEN`,界面要按 §3 D7 说「在 HiWork 里没有完成任务的权限,
|
|
201
|
-
* 请到 PMS
|
|
224
|
+
* 请到 PMS 操作」,**不要**当成「登录过期」让用户重新登录。
|
|
202
225
|
*/
|
|
203
226
|
readonly completeTask?: (input: {
|
|
204
227
|
id: number;
|
|
205
228
|
}) => Promise<void>;
|
|
206
|
-
/**
|
|
229
|
+
/** 订阅镜像变化(换账号时会被清空)。返回退订函数。 */
|
|
207
230
|
readonly subscribe: (listener: () => void) => () => void;
|
|
208
231
|
}
|
|
209
232
|
/** 宿主 loopback RPC 调用面(结构兼容 `ctx.connection.rpc`)。 */
|
|
@@ -234,7 +257,8 @@ export declare function pmsTodoErrorOf(error: unknown): {
|
|
|
234
257
|
* PMS 待办的取数仓库。
|
|
235
258
|
*
|
|
236
259
|
* 本地镜像(`status()` 同步读)是有必要的:DSH 的 client 半边在不同插件之间只通过这个面同步取
|
|
237
|
-
*
|
|
260
|
+
* 状态,而「网关里有没有 pms 分配、手上有没有未过期的令牌」是异步来的。镜像**只由成功响应覆写**,
|
|
261
|
+
* 失败时不改动它。
|
|
238
262
|
*/
|
|
239
263
|
export declare class PmsTodoStore {
|
|
240
264
|
private readonly rpc;
|
|
@@ -243,7 +267,7 @@ export declare class PmsTodoStore {
|
|
|
243
267
|
/**
|
|
244
268
|
* 在飞请求**连同它所属的世代与端点**一起记账。
|
|
245
269
|
*
|
|
246
|
-
* 世代:`clear()
|
|
270
|
+
* 世代:`clear()`(换账号)之后,账上那条是**上一个人**发起的,它的待办属于上一个人
|
|
247
271
|
* ——复用它等于让新账号看到上一个人的待办。端点:`today` 与 `statistics` 的返回值形状不同,
|
|
248
272
|
* 只有同一个端点的并发调用才允许合并。
|
|
249
273
|
*/
|
|
@@ -257,8 +281,9 @@ export declare class PmsTodoStore {
|
|
|
257
281
|
* 本地镜像(同步)。从没同步过返回 `undefined`。
|
|
258
282
|
*
|
|
259
283
|
* ⚠️ 本方法有一次**惰性探测**:镜像还空着且从没探过时,它顺手发起一次 `status` 端点调用
|
|
260
|
-
* (结果到达后写镜像 +
|
|
261
|
-
*
|
|
284
|
+
* (结果到达后写镜像 + 通知订阅者)。理由——渲染是同步的,而「网关里有没有 pms 分配、手上
|
|
285
|
+
* 有没有令牌」只有主机知道;没有这一步,`status` 端点就永远没有调用方,界面也只能靠一次
|
|
286
|
+
* 失败的 `today()` 去反推(而且那反推出来的原因往往正好是错的)。
|
|
262
287
|
* 因此调用方看到 `undefined` **不是**「未配置」,而是「还不知道」:必须订阅。
|
|
263
288
|
*/
|
|
264
289
|
status(): PmsTodoStatus | undefined;
|
|
@@ -269,14 +294,6 @@ export declare class PmsTodoStore {
|
|
|
269
294
|
* 调用方(或下一次 `today()`)自己决定怎么展示。
|
|
270
295
|
*/
|
|
271
296
|
refreshStatus(signal?: AbortSignal): Promise<PmsTodoStatus>;
|
|
272
|
-
/**
|
|
273
|
-
* 登录(凭据交给 Host;**本层不留存密码、不写任何缓存**)。
|
|
274
|
-
*
|
|
275
|
-
* 成功时把返回的状态写进镜像——登录是「身份刚变过」,镜像必须跟着变。
|
|
276
|
-
*/
|
|
277
|
-
login(input: PmsTodoLoginInput): Promise<PmsTodoStatus>;
|
|
278
|
-
/** 登出(Host 侧清凭据);返回登出后的状态并写镜像。 */
|
|
279
|
-
logout(): Promise<PmsTodoStatus>;
|
|
280
297
|
/**
|
|
281
298
|
* 今日待办。
|
|
282
299
|
*
|
|
@@ -299,7 +316,7 @@ export declare class PmsTodoStore {
|
|
|
299
316
|
/**
|
|
300
317
|
* **我的未完成项目任务**(spec §3 D7):`myTasks` 端点的取数。
|
|
301
318
|
*
|
|
302
|
-
* 入参是**空对象**,没有 `userId`——「谁的任务」由 Host
|
|
319
|
+
* 入参是**空对象**,没有 `userId`——「谁的任务」由 Host 从 mint 响应里取(客户端连自己的
|
|
303
320
|
* PMS userId 都拿不到,这是 §3 D6 形状的一部分)。
|
|
304
321
|
*
|
|
305
322
|
* 与 `today` 同款:**不做本地缓存**(返回刚拉到的那一份),但**并发调用合并**(同一时刻问两遍
|
|
@@ -323,7 +340,8 @@ export declare class PmsTodoStore {
|
|
|
323
340
|
/** 订阅镜像变化(右栏 tab 据此重渲染)。返回退订函数。 */
|
|
324
341
|
subscribe(listener: () => void): () => void;
|
|
325
342
|
/**
|
|
326
|
-
*
|
|
343
|
+
* 清空镜像(换账号:状态是**按人**的——分配是按人下发的、令牌是按人换的,留着会串号),
|
|
344
|
+
* 并**作废在飞的请求**。
|
|
327
345
|
*
|
|
328
346
|
* 只清镜像是不够的:清空那一刻正有一次拉取在飞时,那次拉取是**上一个人**发起的。所以世代
|
|
329
347
|
* +1:那之后的调用者一律不复用旧世代的那条,自己重新发一次 RPC。用世代号而**不是**把
|
|
@@ -130,3 +130,31 @@ export declare const brandCss: string;
|
|
|
130
130
|
* 由 client 入口 apply() 注入一次(带 id 防重)。
|
|
131
131
|
*/
|
|
132
132
|
export declare const interactionCss: string;
|
|
133
|
+
/**
|
|
134
|
+
* 手写注入的 <style> 必须带 DSH 的归属标记(`data-plugin`)。
|
|
135
|
+
*
|
|
136
|
+
* DSH 的 client module system 用这个属性记账:每装载(materialize)一个插件 bundle,
|
|
137
|
+
* 就把文档里**所有没有** `data-plugin` 的 <style> 记到当时正在装载的那个插件名下
|
|
138
|
+
* (`claimStyles`,`dsh-client-modules/src/client/system.ts`);插件被替换、重建或停用
|
|
139
|
+
* 时按 `removeOwnedStyles(id)`(同包 `entry-lifecycle.ts`)删掉「记在它名下」的全部
|
|
140
|
+
* <style>。裸注入的样式会被随后第一个装载的插件顺手认领,再随它被替换/停用而**连带
|
|
141
|
+
* 删除**,而本插件此刻并没有重新 apply —— 表现就是这部分样式静默消失、只有刷新页面
|
|
142
|
+
* 才恢复(2026-09-23 实测:hiwork-knowledge 重建后,本插件两个样式表被一起删掉,实测
|
|
143
|
+
* 归属曾被记到 `@deepseek-ai/dsh-client-ui-deliverables` 名下)。
|
|
144
|
+
* 标记形态与官方 client bundle 预设一致(`tsdown.client.ts` 的 styleInjectionModule)。
|
|
145
|
+
*/
|
|
146
|
+
export declare const CORE_STYLE_OWNER = "hiwork-core";
|
|
147
|
+
/** 给本插件注入的 <style> 打归属标记;`cssTagId` 沿用官方预设的 `包名/文件名` 形态。 */
|
|
148
|
+
export declare function ownStyleTag(style: HTMLStyleElement, cssTagId: string): void;
|
|
149
|
+
/**
|
|
150
|
+
* 语音输入(话筒按钮 + 录音浮层)的样式,随本插件一起分发。
|
|
151
|
+
*
|
|
152
|
+
* 类名统一 `hiwv-` 前缀,**不引用宿主内部 CSS 变量**:这些浮层元素是 `document.body`
|
|
153
|
+
* 直挂的,宿主 DOM 结构/变量名跨版本不稳,故自带中性配色 + `prefers-color-scheme` 自适应。
|
|
154
|
+
* 设置分区(`.hiwv-settings*` / `.hiwv-field` / `.hiwv-help`)**不存在**:配置面在网关
|
|
155
|
+
* (管理端「语音识别设置」),桌面端没有任何语音相关的设置项。
|
|
156
|
+
*
|
|
157
|
+
* 注入走 `voice/voice-styles.ts` 的 `ensureVoiceStyle`,**必须**经 {@link ownStyleTag}
|
|
158
|
+
* 打归属标记(见上方事故记录):裸注入会被随后装载的插件认领并连带删除。
|
|
159
|
+
*/
|
|
160
|
+
export declare const voiceCss: string;
|
|
@@ -31,7 +31,19 @@ export type LoginErrorCode = 'AUTH_FAILED' | 'RATE_LIMITED' | 'GATEWAY_UNREACHAB
|
|
|
31
31
|
* 收敛成 `GATEWAY_UNREACHABLE`;工作台的 404 是一个明确的功能状态(spec §10 的降级矩阵),
|
|
32
32
|
* 消费方据此显示「本部署尚未启用工作台」而不是报错。别顺手把两边对齐。
|
|
33
33
|
*/
|
|
34
|
-
| 'WORKBENCH_DISABLED'
|
|
34
|
+
| 'WORKBENCH_DISABLED'
|
|
35
|
+
/** 本部署未启用语音识别(管理员把开关关了):识别时 503(或 503 无 body code)。 */
|
|
36
|
+
| 'VOICE_DISABLED'
|
|
37
|
+
/** 本部署未配置语音识别上游服务(网关侧没有可用 ASR 配置):识别时 503。 */
|
|
38
|
+
| 'VOICE_NOT_CONFIGURED'
|
|
39
|
+
/** 单用户识别频率超限:429。 */
|
|
40
|
+
| 'VOICE_LIMIT_REACHED'
|
|
41
|
+
/** 录音超过网关允许的大小 / 时长上限:413。 */
|
|
42
|
+
| 'VOICE_AUDIO_TOO_LARGE'
|
|
43
|
+
/** 录音数据不合法(base64 损坏 / 请求体越界):400。 */
|
|
44
|
+
| 'VOICE_BAD_REQUEST'
|
|
45
|
+
/** 上游识别服务出错或网关 502 之类的转发失败:其余非 2xx(含 502)。 */
|
|
46
|
+
| 'VOICE_UPSTREAM';
|
|
35
47
|
/** 一个稳定的、可安全展示的登录/刷新错误。 */
|
|
36
48
|
export interface LoginError {
|
|
37
49
|
code: LoginErrorCode;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 输入框工具栏的话筒按钮(`conversation.input.right` 槽)。
|
|
3
|
+
*
|
|
4
|
+
* 该槽渲染在发送区右组、**模型选择器之前**(宿主 InputBar 的顺序:right → model →
|
|
5
|
+
* stop/send),正是需求指定的落点。按钮只负责「打开浮层」,识别与插入都在浮层里。
|
|
6
|
+
*
|
|
7
|
+
* 一条约定:**不按「本机有没有配豆包密钥」改 tooltip**——D1 之后配置面整体废除,
|
|
8
|
+
* 能不能用由 `status` 端点说了算,而 `enabled !== true` 时本按钮根本不会被注册
|
|
9
|
+
* (见 `voice/entry.ts` 的门禁)。
|
|
10
|
+
*/
|
|
11
|
+
import { type ReactElement } from 'react';
|
|
12
|
+
import type { InjectFace, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';
|
|
13
|
+
/** 组件注入面:打开录音浮层(会话 id 由槽位在渲染时注入,见 entry)。 */
|
|
14
|
+
export interface MicButtonFace {
|
|
15
|
+
/** 打开浮层;`sessionId` 是槽位注入的会话 id(拿不到为 undefined,插入时退到当前会话)。 */
|
|
16
|
+
open: (sessionId?: string) => void;
|
|
17
|
+
}
|
|
18
|
+
/** 槽位组件 props:框架标准件 + 注入面(`conversation.input.right` 是 session 作用域 list 槽)。 */
|
|
19
|
+
export type MicButtonProps = PropsRuntime<'conversation.input.right'> & InjectFace<MicButtonFace>;
|
|
20
|
+
/**
|
|
21
|
+
* 话筒按钮;宿主通过 inject 合成 `open`。
|
|
22
|
+
* @param props - 槽位 props(只消费注入面的 `open`)。
|
|
23
|
+
*/
|
|
24
|
+
export declare function MicButton({ open }: MicButtonProps): ReactElement;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 录音浮层:点击话筒后弹出的「正在录音」交互。
|
|
3
|
+
*
|
|
4
|
+
* 需求约束:
|
|
5
|
+
* - 组件**不能太小**(面板最小 420px、居中、带遮罩);
|
|
6
|
+
* - 必须支持**取消**(ESC / 取消按钮 / 点遮罩),取消即丢弃录音、不写输入框;
|
|
7
|
+
* - 「完成」后上传识别,识别文本**填进输入框草稿,不自动发送**;
|
|
8
|
+
* - 没听清 / 失败时给出原因与出口(再录一次 / 重试识别 / 复制文本),绝不静默。
|
|
9
|
+
*
|
|
10
|
+
* 两条与网关中继相配的约定(设计稿 §5.4 冻结):
|
|
11
|
+
* 1. **时长上限来自 `status` RPC**(`{enabled, maxDurationSeconds}`,见 `voice-data.ts`),
|
|
12
|
+
* 不来自任何本机配置——D1 之后没有「本机密钥配置」这回事了;
|
|
13
|
+
* 2. **失败文案按网关 code 映射**({@link voiceErrorMessageOf}),不把 Host 的
|
|
14
|
+
* `message` 直出;「重试识别」只在可能自愈的 code 上出现(`status.enabled !== true`
|
|
15
|
+
* 与网关不可达走同一句「连接语音服务失败,请稍后重试」,本组件只是兜底,
|
|
16
|
+
* 正常情况下这种状态压根不会注册话筒按钮)。
|
|
17
|
+
*/
|
|
18
|
+
import { type ReactElement } from 'react';
|
|
19
|
+
import type { ComposerInsertResult } from './composer.ts';
|
|
20
|
+
import { type VoiceRuntime } from './voice-data.ts';
|
|
21
|
+
/** 浮层属性(由 `voice/entry.ts` 的浮层控制器组装)。 */
|
|
22
|
+
export interface VoiceOverlayProps {
|
|
23
|
+
/** 取数面:`status`(时长上限 + 能不能用)与 `transcribe`(WAV base64 → 文本)。 */
|
|
24
|
+
runtime: VoiceRuntime;
|
|
25
|
+
/** 关闭浮层(取消或完成后由组件内部调用;React root 的卸载由控制器负责)。 */
|
|
26
|
+
onClose: () => void;
|
|
27
|
+
/** 把识别文本写进会话输入框草稿(entry 用 `composer.ts` 的 `insertDraftText` 组装)。 */
|
|
28
|
+
insertDraft: (text: string) => ComposerInsertResult;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* RMS → 显示电平。语音的裸 RMS 通常只有 0.02~0.2,直接进 `8 + level*46` 的像素映射
|
|
32
|
+
* 几乎看不出跳动(0.1 的响度只比安静高 4px);放大 6 倍再钳到 1,正常说话就能打到
|
|
33
|
+
* 半高以上。导出供测试锁定映射。
|
|
34
|
+
*/
|
|
35
|
+
export declare function rmsToLevel(rms: number): number;
|
|
36
|
+
/** 录音浮层组件(由 `voice/entry.ts` 的 overlay 控制器挂载/卸载)。 */
|
|
37
|
+
export declare function VoiceOverlay(props: VoiceOverlayProps): ReactElement;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 把一段文本放进**某个会话的输入框**(卡片上的「追问」、页面上的「带进聊天」)。
|
|
3
|
+
*
|
|
4
|
+
* 为什么不是 `conversation.send(text)`:那会立刻排一轮对话发出去,用户没有反悔余地;
|
|
5
|
+
* 而且 `send()` 是**按调用方作用域寻址**的,本插件的 ctx 是 root 作用域,直接调会抛
|
|
6
|
+
* `conversation.send requires a session scope`。放进草稿既符合「带进聊天」的语义,
|
|
7
|
+
* 也把「发不发、要不要改」留给用户。
|
|
8
|
+
*
|
|
9
|
+
* 三个必须照抄的宿主细节(都来自 `hiwork-capabilities/src/client/composer.ts` 的实测结论):
|
|
10
|
+
* 1. **`actx.get('conversation')` 而不是 `actx.conversation`**:后者是属性访问,会撞上
|
|
11
|
+
* cordis 的注入门禁(`cannot get property "conversation" without inject`),而本插件
|
|
12
|
+
* 的顶层 `inject` 里没有 `conversation`(也不该有——那是可选能力);
|
|
13
|
+
* 2. **`sessions.scope(id)` 失败时退到 `sessions.binding(id).ctx`**:两条路在不同宿主
|
|
14
|
+
* 版本上互为兜底,且都要 try/catch——读属性本身就可能抛;
|
|
15
|
+
* 3. **读快照与插入之间不得有 await**:`draftRev` 是 CAS 票据,中间让出一次事件循环,
|
|
16
|
+
* 草稿被别人改过就会必然被拒。所以本模块整体是**同步**的。
|
|
17
|
+
*
|
|
18
|
+
* 本文件零依赖:bundle 必须自包含。
|
|
19
|
+
*/
|
|
20
|
+
/** 草稿里的引用 chip 占位(结构契约)。 */
|
|
21
|
+
export interface ComposerOccurrence {
|
|
22
|
+
readonly offset: number;
|
|
23
|
+
readonly length: number;
|
|
24
|
+
}
|
|
25
|
+
/** 输入机状态里本模块用到的部分(结构契约)。 */
|
|
26
|
+
export interface ComposerInputState {
|
|
27
|
+
/** 剪贴板投影:chip 展开成各自的 `clipboardText`。 */
|
|
28
|
+
readonly draft: string;
|
|
29
|
+
/** 单调递增的编辑器版本号,插入时用作 CAS 票据。 */
|
|
30
|
+
readonly draftRev: number;
|
|
31
|
+
/**
|
|
32
|
+
* 提交相位;旧宿主可能没有这个字段,缺省视为可写。
|
|
33
|
+
*
|
|
34
|
+
* 写成 `| undefined`(而不是裸 `?`)是本仓库 tsconfig 的 `exactOptionalPropertyTypes`
|
|
35
|
+
* 要求:裸可选成员不能显式传 `undefined`,而调用方/测试需要能这么写。
|
|
36
|
+
*/
|
|
37
|
+
readonly phase?: 'plain' | 'adjudicating' | 'claimed' | 'submitting' | undefined;
|
|
38
|
+
/** 草稿里的引用 chip,按 offset 升序。 */
|
|
39
|
+
readonly occurrences?: readonly ComposerOccurrence[] | undefined;
|
|
40
|
+
}
|
|
41
|
+
/** 会话作用域的输入机面(`conversation.input.for(actx)` 的返回值)。 */
|
|
42
|
+
export interface ComposerInsertionTarget {
|
|
43
|
+
readonly state: {
|
|
44
|
+
getSnapshot(): ComposerInputState;
|
|
45
|
+
};
|
|
46
|
+
/** 把普通文本替换进 pick-time span;旧宿主可能没有这个方法。 */
|
|
47
|
+
insertText?(text: string, span: {
|
|
48
|
+
start: number;
|
|
49
|
+
end: number;
|
|
50
|
+
draftRev: number;
|
|
51
|
+
}, keepCompleting?: boolean): boolean;
|
|
52
|
+
}
|
|
53
|
+
/** `actx.get('conversation')` 的最小结构面。 */
|
|
54
|
+
export interface ConversationAccess {
|
|
55
|
+
readonly input: {
|
|
56
|
+
for(actx: unknown): ComposerInsertionTarget | undefined;
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
/** `ctx.sessions` 的最小面(`scope` 与 `binding` 互为兜底)。 */
|
|
60
|
+
export interface SessionScopeAccess {
|
|
61
|
+
scope?(id: string): unknown;
|
|
62
|
+
binding?(id: string): {
|
|
63
|
+
ctx?: unknown;
|
|
64
|
+
} | undefined;
|
|
65
|
+
list?: {
|
|
66
|
+
getSnapshot(): {
|
|
67
|
+
readonly current?: string | undefined;
|
|
68
|
+
};
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
/** 插入结果。失败一律**返回原因**,不抛、不静默——静默会让按钮看起来是坏的。 */
|
|
72
|
+
export type ComposerInsertResult = {
|
|
73
|
+
readonly ok: true;
|
|
74
|
+
} | {
|
|
75
|
+
readonly ok: false;
|
|
76
|
+
readonly reason: 'no-target' | 'busy' | 'rejected';
|
|
77
|
+
};
|
|
78
|
+
/**
|
|
79
|
+
* 探测草稿在**坐标投影**里的末尾偏移。
|
|
80
|
+
*
|
|
81
|
+
* 用 `occurrences` 而不是 `draft.length`:chip 在坐标里只占 1 个位置,而 `draft` 里是
|
|
82
|
+
* 展开后的全文。拿错投影会被宿主直接拒掉。
|
|
83
|
+
* @param state - 输入机快照。
|
|
84
|
+
* @returns 探测坐标里的草稿末尾偏移。
|
|
85
|
+
*/
|
|
86
|
+
export declare function detectTextLength(state: ComposerInputState): number;
|
|
87
|
+
/**
|
|
88
|
+
* 把文本追加进某个会话输入框的草稿末尾。
|
|
89
|
+
*
|
|
90
|
+
* 落点选**末尾**:`/名字 ` 这类文本由宿主自己的触发器敲在光标处,我们追加在末尾不会
|
|
91
|
+
* 抢走用户已经写了一半的话;中间同样不得有 await(见文件头第 3 点)。
|
|
92
|
+
*
|
|
93
|
+
* @param sessions - `ctx.sessions`;缺失时直接判定没有目标。
|
|
94
|
+
* @param text - 要追加的文本。
|
|
95
|
+
* @param sessionId - 槽位注入的会话 id;缺省退到 `sessions.list.current`。
|
|
96
|
+
* @returns 成功或失败原因。
|
|
97
|
+
*/
|
|
98
|
+
export declare function insertDraftText(sessions: SessionScopeAccess | undefined, text: string, sessionId?: string): ComposerInsertResult;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { type SessionScopeAccess } from './composer.ts';
|
|
2
|
+
import { type VoiceRpcPort } from './voice-data.ts';
|
|
3
|
+
/** 话筒按钮落在输入框工具栏右组(渲染在模型选择器之前);必须与 package.json 的
|
|
4
|
+
* `dsh.client.inject` 里声明的 `@deepseek-ai/dsh-client-ui-conversation` 对齐。 */
|
|
5
|
+
export declare const VOICE_INPUT_SLOT = "conversation.input.right";
|
|
6
|
+
/** 槽位注册 id(稳定值,供宿主与测试定位)。 */
|
|
7
|
+
export declare const VOICE_INPUT_SLOT_ID = "hiwork-core-voice";
|
|
8
|
+
/** 槽位排序权重(list 槽按 order 升序渲染在右组里)。 */
|
|
9
|
+
export declare const VOICE_INPUT_SLOT_ORDER = 10;
|
|
10
|
+
/** 浮层控制器的容器类名(`document.body` 直挂的那层 div;无样式依赖,仅便于排查)。 */
|
|
11
|
+
export declare const VOICE_ROOT_CLASS = "hiwv-root";
|
|
12
|
+
/** 话筒按钮的注入面:`open` 已经绑好会话 id,组件只需调用它。 */
|
|
13
|
+
export interface VoiceMicInject {
|
|
14
|
+
open: (sessionId?: string) => void;
|
|
15
|
+
}
|
|
16
|
+
/** 安装依赖(由 `src/client/index.ts` 组装;测试用假实现注入)。 */
|
|
17
|
+
export interface VoiceEntryDeps {
|
|
18
|
+
/** 宿主 loopback RPC(`ctx.connection.rpc`)。 */
|
|
19
|
+
rpc: VoiceRpcPort;
|
|
20
|
+
/**
|
|
21
|
+
* **现取**会话服务(`() => ctx.get('sessions')`);缺失时草稿插入一律判定为「没有打开的会话」。
|
|
22
|
+
*
|
|
23
|
+
* 必须是取用函数而不是服务本身:`sessions` 不在插件的 `inject` 列表里(那是可选能力),
|
|
24
|
+
* 实测**装配期它还没上线**——`ctx.get('sessions')` 返回 `undefined`,而服务稍后才注册。
|
|
25
|
+
* 装配期快照会把 `undefined` 冻进浮层控制器,于是每次录音都在填草稿那步失败并报
|
|
26
|
+
* 「没有找到打开的会话输入框」(真机复现:装配期 `undefined`、点击时已就绪)。现取即可。
|
|
27
|
+
*/
|
|
28
|
+
resolveSessions: () => SessionScopeAccess | undefined;
|
|
29
|
+
/** 注册话筒按钮:index.ts 用 `ctx.slots.inject` + `ctx.slots.register` 实现,测试用 spy。 */
|
|
30
|
+
registerMic: (inject: VoiceMicInject) => void;
|
|
31
|
+
/** 归属文档;缺省取全局 `document`(Node 环境为 undefined 时不挂浮层)。 */
|
|
32
|
+
document?: Document | undefined;
|
|
33
|
+
/** 安装作废信号(插件卸载 / HMR 重建时 abort):作废后绝不注册话筒按钮。 */
|
|
34
|
+
signal?: AbortSignal | undefined;
|
|
35
|
+
}
|
|
36
|
+
/** 安装结果句柄:`enabled` 是 status 门禁的结论,`dispose` 关闭可能开着的浮层。 */
|
|
37
|
+
export interface VoiceEntryHandle {
|
|
38
|
+
readonly enabled: boolean;
|
|
39
|
+
dispose(): void;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* 安装语音输入:注入样式 → 问 status → 可用才注册话筒按钮。
|
|
43
|
+
* @param deps - 安装依赖。
|
|
44
|
+
* @returns 安装句柄(`enabled` 即 status 门禁结论)。
|
|
45
|
+
*/
|
|
46
|
+
export declare function installVoiceInput(deps: VoiceEntryDeps): Promise<VoiceEntryHandle>;
|