chatccc 0.2.222 → 0.2.223

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/src/cards.ts CHANGED
@@ -1,10 +1,11 @@
1
- // ---------------------------------------------------------------------------
2
- // Button helpers
3
- // ---------------------------------------------------------------------------
4
-
5
- import { ABD_HELP_LINE } from "./shared-prefix.ts";
6
-
7
- export interface ButtonDef {
1
+ // ---------------------------------------------------------------------------
2
+ // Button helpers
3
+ // ---------------------------------------------------------------------------
4
+
5
+ import { ABD_HELP_LINE } from "./shared-prefix.ts";
6
+ import type { ProgressView } from "./progress/view.ts";
7
+
8
+ export interface ButtonDef {
8
9
  text: string;
9
10
  value: string;
10
11
  type?: "primary" | "default" | "danger";
@@ -100,13 +101,17 @@ export function normalizeToolName(name: string): string {
100
101
  // ---------------------------------------------------------------------------
101
102
 
102
103
  // CardKit schema 2.0 进度卡片(带停止按钮,支持流式更新)
103
- export function buildProgressCard(
104
- text: string,
105
- opts: { showStop?: boolean; headerTitle?: string; headerTemplate?: string } = {}
106
- ): string {
107
- const { showStop = true, headerTitle = "生成中...", headerTemplate = "blue" } = opts;
104
+ // 输入是平台无关的 ProgressView(与终端过程区块共享同一视图模型):
105
+ // - view.text main_content(正文)
106
+ // - view.showStop 是否展示状态/停止按钮
107
+ // - view.headerTitle → 卡片头部标题(生成中阶段的动态标题)
108
+ // - view.headerTemplate 卡片头部颜色模板
109
+ export function buildProgressCard(view: ProgressView): string {
110
+ const showStop = view.showStop;
111
+ const headerTitle = view.headerTitle;
112
+ const headerTemplate = view.headerTemplate;
108
113
  const elements: object[] = [
109
- { tag: "markdown", content: truncateContent(text), element_id: "main_content" },
114
+ { tag: "markdown", content: truncateContent(view.text), element_id: "main_content" },
110
115
  ];
111
116
  if (showStop) {
112
117
  elements.push({ tag: "hr" });
@@ -152,16 +157,16 @@ export function buildHelpCard(
152
157
  `发送 **/new** 创建新会话(使用 /cd 设置的目录,默认 ${defaultToolLabel})`,
153
158
  "发送 **/new claude** 创建新 Claude 会话",
154
159
  "发送 **/new cursor** 创建新 Cursor 会话",
155
- "发送 **/new codex** 创建新 Codex 会话",
156
- "发送 **/new ccc** 创建新 CCC Agent 会话",
160
+ "发送 **/new codex** 创建新 Codex 会话",
161
+ "发送 **/new ccc** 创建新 CCC Agent 会话",
157
162
  "发送 **/newh** 重置当前会话(沿用当前工作目录,不切换)",
158
163
  "发送 **/plan** 以规划模式提问(只读,不执行写操作)",
159
164
  "发送 **/ask** 以问答模式提问(只读,不执行写操作)",
160
- "发送 **/usage** 查看当前 Agent 的用量或余额",
161
- "发送 **/restart** 重启 ChatCCC 进程",
162
- "发送 **/update** 更新并重启(仅 npm 全局安装可用)",
163
- ABD_HELP_LINE,
164
- ].join("\n");
165
+ "发送 **/usage** 查看当前 Agent 的用量或余额",
166
+ "发送 **/restart** 重启 ChatCCC 进程",
167
+ "发送 **/update** 更新并重启(仅 npm 全局安装可用)",
168
+ ABD_HELP_LINE,
169
+ ].join("\n");
165
170
  return JSON.stringify({
166
171
  config: { wide_screen_mode: true },
167
172
  header: { template: "blue", title: { content: "ChatCCC", tag: "plain_text" } },
@@ -172,8 +177,8 @@ export function buildHelpCard(
172
177
  { text: `新建默认会话(/new,${defaultToolLabel})`, value: JSON.stringify({ cmd: "new" }), type: "primary" },
173
178
  { text: "新建 Claude 会话(/new claude)", value: JSON.stringify({ cmd: "new claude" }), type: "primary" },
174
179
  { text: "新建 Cursor 会话(/new cursor)", value: JSON.stringify({ cmd: "new cursor" }), type: "primary" },
175
- { text: "新建 Codex 会话(/new codex)", value: JSON.stringify({ cmd: "new codex" }), type: "primary" },
176
- { text: "新建 CCC Agent 会话(/new ccc)", value: JSON.stringify({ cmd: "new ccc" }), type: "primary" },
180
+ { text: "新建 Codex 会话(/new codex)", value: JSON.stringify({ cmd: "new codex" }), type: "primary" },
181
+ { text: "新建 CCC Agent 会话(/new ccc)", value: JSON.stringify({ cmd: "new ccc" }), type: "primary" },
177
182
  { text: "重启 ChatCCC(/restart)", value: JSON.stringify({ cmd: "restart" }), type: "danger" },
178
183
  { text: "更新并重启(/update)", value: JSON.stringify({ cmd: "update" }), type: "danger" },
179
184
  { text: "切换工作路径(/cd)", value: JSON.stringify({ cmd: "cd" }), type: "default" },
@@ -198,9 +203,9 @@ export function buildCdContent(
198
203
  ? `**当前会话工作路径:** \`${currentCwd}\``
199
204
  : "";
200
205
 
201
- const statusLine = isUpdate
202
- ? `**后续新建会话默认工作路径(已切换):** \`${dirPath}\``
203
- : `**后续新建会话默认工作路径:** \`${dirPath}\``;
206
+ const statusLine = isUpdate
207
+ ? `**后续新建会话默认工作路径(已切换):** \`${dirPath}\``
208
+ : `**后续新建会话默认工作路径:** \`${dirPath}\``;
204
209
 
205
210
  const lines: string[] = [];
206
211
  if (currentLine) lines.push(currentLine, "");
@@ -247,10 +252,10 @@ export function buildCdCard(
247
252
  text: { tag: "lark_md", content: `**当前会话工作路径:** \`${sessionCwd}\`` },
248
253
  });
249
254
  }
250
- elements.push({
251
- tag: "div",
252
- text: { tag: "lark_md", content: `**后续新建会话默认工作路径:** \`${dirPath}\`` },
253
- });
255
+ elements.push({
256
+ tag: "div",
257
+ text: { tag: "lark_md", content: `**后续新建会话默认工作路径:** \`${dirPath}\`` },
258
+ });
254
259
 
255
260
  if (recentDirs.length > 0) {
256
261
  elements.push({ tag: "hr" });
@@ -295,78 +300,78 @@ export function buildCdCard(
295
300
  });
296
301
  }
297
302
 
298
- function sessionToolLabel(tool: string): string {
299
- if (tool === "cursor") return "Cursor";
300
- if (tool === "codex") return "Codex";
301
- if (tool === "ccc") return "CCC Agent";
302
- return "Claude Code";
303
- }
304
-
305
- function pushSessionGroup(
306
- lines: string[],
307
- title: string,
308
- sessions: Array<{
309
- sessionId: string;
310
- chatName: string;
311
- chatId: string;
312
- active: boolean;
313
- turnCount: number;
314
- elapsedSeconds: number | null;
315
- model: string;
316
- tool: string;
317
- }>,
318
- formatSession: (session: {
319
- sessionId: string;
320
- chatName: string;
321
- chatId: string;
322
- active: boolean;
323
- turnCount: number;
324
- elapsedSeconds: number | null;
325
- model: string;
326
- tool: string;
327
- }, index: number) => string,
328
- index: { value: number },
329
- ): void {
330
- if (sessions.length === 0) return;
331
- if (index.value > 0) lines.push("", `**${title}:**`, "");
332
- else lines.push(`**${title}:**`, "");
333
- for (const session of sessions) {
334
- lines.push(formatSession(session, index.value++));
335
- }
336
- }
337
-
338
- // 所有会话列表卡片(Claude Code 优先,然后 Cursor)
339
- export function buildSessionsCard(sessions: Array<{
340
- sessionId: string;
341
- chatName: string;
342
- chatId: string;
343
- chatType?: string;
344
- active: boolean;
303
+ function sessionToolLabel(tool: string): string {
304
+ if (tool === "cursor") return "Cursor";
305
+ if (tool === "codex") return "Codex";
306
+ if (tool === "ccc") return "CCC Agent";
307
+ return "Claude Code";
308
+ }
309
+
310
+ function pushSessionGroup(
311
+ lines: string[],
312
+ title: string,
313
+ sessions: Array<{
314
+ sessionId: string;
315
+ chatName: string;
316
+ chatId: string;
317
+ active: boolean;
318
+ turnCount: number;
319
+ elapsedSeconds: number | null;
320
+ model: string;
321
+ tool: string;
322
+ }>,
323
+ formatSession: (session: {
324
+ sessionId: string;
325
+ chatName: string;
326
+ chatId: string;
327
+ active: boolean;
328
+ turnCount: number;
329
+ elapsedSeconds: number | null;
330
+ model: string;
331
+ tool: string;
332
+ }, index: number) => string,
333
+ index: { value: number },
334
+ ): void {
335
+ if (sessions.length === 0) return;
336
+ if (index.value > 0) lines.push("", `**${title}:**`, "");
337
+ else lines.push(`**${title}:**`, "");
338
+ for (const session of sessions) {
339
+ lines.push(formatSession(session, index.value++));
340
+ }
341
+ }
342
+
343
+ // 所有会话列表卡片(Claude Code 优先,然后 Cursor)
344
+ export function buildSessionsCard(sessions: Array<{
345
+ sessionId: string;
346
+ chatName: string;
347
+ chatId: string;
348
+ chatType?: string;
349
+ active: boolean;
345
350
  turnCount: number;
346
351
  elapsedSeconds: number | null;
347
352
  model: string;
348
353
  tool: string;
349
- }>, opts: { defaultToolLabel?: string; fixedPrivateSession?: boolean } = {}): string {
350
- const defaultToolLabel = opts.defaultToolLabel ?? "Claude Code";
351
- const fixedPrivateSession = opts.fixedPrivateSession ?? false;
352
- // 按 tool 分组排序:Claude Code 在前,Cursor 其次,Codex 最后
353
- const claudeCodeSessions = sessions.filter(s => s.tool !== "cursor" && s.tool !== "codex" && s.tool !== "ccc");
354
- const cursorSessions = sessions.filter(s => s.tool === "cursor");
355
- const codexSessions = sessions.filter(s => s.tool === "codex");
356
- const cccSessions = sessions.filter(s => s.tool === "ccc");
357
- const hasClaudeCode = claudeCodeSessions.length > 0;
358
- const hasCursor = cursorSessions.length > 0;
359
- const hasCodex = codexSessions.length > 0;
360
- const hasCcc = cccSessions.length > 0;
354
+ }>, opts: { defaultToolLabel?: string; fixedPrivateSession?: boolean } = {}): string {
355
+ const defaultToolLabel = opts.defaultToolLabel ?? "Claude Code";
356
+ const fixedPrivateSession = opts.fixedPrivateSession ?? false;
357
+ // 按 tool 分组排序:Claude Code 在前,Cursor 其次,Codex 最后
358
+ const claudeCodeSessions = sessions.filter(s => s.tool !== "cursor" && s.tool !== "codex" && s.tool !== "ccc");
359
+ const cursorSessions = sessions.filter(s => s.tool === "cursor");
360
+ const codexSessions = sessions.filter(s => s.tool === "codex");
361
+ const cccSessions = sessions.filter(s => s.tool === "ccc");
362
+ const hasClaudeCode = claudeCodeSessions.length > 0;
363
+ const hasCursor = cursorSessions.length > 0;
364
+ const hasCodex = codexSessions.length > 0;
365
+ const hasCcc = cccSessions.length > 0;
361
366
 
362
367
  if (sessions.length === 0) {
363
368
  return JSON.stringify({
364
369
  config: { wide_screen_mode: true },
365
370
  header: { template: "blue", title: { content: "所有会话", tag: "plain_text" } },
366
371
  elements: [
367
- { tag: "div", text: { tag: "lark_md", content: fixedPrivateSession
368
- ? `当前没有会话记录。\n\n直接发送普通消息即可创建飞书私聊专属 ${defaultToolLabel} 会话;默认 Agent 变化后,下一条普通消息会创建对应 Agent 的新空会话。发送 **/new**、**/new claude**、**/new cursor**、**/new codex** 或 **/new ccc** 会另外创建会话群。`
369
- : `当前没有会话记录。\n\n使用 **/new**(默认 ${defaultToolLabel})、**/new claude**、**/new cursor**、**/new codex** 或 **/new ccc** 创建新会话。\n创建后可在任意会话群内发送 **/sessions** 查看列表,用 **/session 数字** 切换会话。` } },
372
+ { tag: "div", text: { tag: "lark_md", content: fixedPrivateSession
373
+ ? `当前没有会话记录。\n\n直接发送普通消息即可创建飞书私聊专属 ${defaultToolLabel} 会话;默认 Agent 变化后,下一条普通消息会创建对应 Agent 的新空会话。发送 **/new**、**/new claude**、**/new cursor**、**/new codex** 或 **/new ccc** 会另外创建会话群。`
374
+ : `当前没有会话记录。\n\n使用 **/new**(默认 ${defaultToolLabel})、**/new claude**、**/new cursor**、**/new codex** 或 **/new ccc** 创建新会话。\n创建后可在任意会话群内发送 **/sessions** 查看列表,用 **/session 数字** 切换会话。` } },
370
375
  { tag: "hr" },
371
376
  { tag: "action", actions: [{ tag: "button", text: { tag: "plain_text", content: "收起" }, type: "default", value: { action: "close" } }] },
372
377
  ],
@@ -382,24 +387,24 @@ export function buildSessionsCard(sessions: Array<{
382
387
  const secs = s.elapsedSeconds % 60;
383
388
  extra = ` | 本轮: ${mins}分${secs}秒`;
384
389
  }
385
- const toolLabel = sessionToolLabel(s.tool);
390
+ const toolLabel = sessionToolLabel(s.tool);
386
391
  const namePart = s.chatName ? `**${s.chatName}** ` : "";
387
- const chatTag = !s.chatId
388
- ? " (chat id缺失)"
389
- : s.chatType === "p2p"
390
- ? " (私聊)"
391
- : s.chatType === "group" || (s.chatType == null && s.chatId.startsWith("oc_"))
392
- ? " (群聊)"
393
- : "";
392
+ const chatTag = !s.chatId
393
+ ? " (chat id缺失)"
394
+ : s.chatType === "p2p"
395
+ ? " (私聊)"
396
+ : s.chatType === "group" || (s.chatType == null && s.chatId.startsWith("oc_"))
397
+ ? " (群聊)"
398
+ : "";
394
399
  return `**${i + 1}.** ${namePart}${chatTag} \`${shortId}\` ${status} | 工具: ${toolLabel} | 轮数: ${s.turnCount} | ${s.model}${extra}`;
395
400
  };
396
401
 
397
402
  const lines: string[] = [`共 **${sessions.length}** 个会话:`, ""];
398
- const idx = { value: 0 };
399
- if (hasClaudeCode) pushSessionGroup(lines, "Claude Code 会话", claudeCodeSessions, formatSession, idx);
400
- if (hasCursor) pushSessionGroup(lines, "Cursor 会话", cursorSessions, formatSession, idx);
401
- if (hasCodex) pushSessionGroup(lines, "Codex 会话", codexSessions, formatSession, idx);
402
- if (hasCcc) pushSessionGroup(lines, "CCC Agent 会话", cccSessions, formatSession, idx);
403
+ const idx = { value: 0 };
404
+ if (hasClaudeCode) pushSessionGroup(lines, "Claude Code 会话", claudeCodeSessions, formatSession, idx);
405
+ if (hasCursor) pushSessionGroup(lines, "Cursor 会话", cursorSessions, formatSession, idx);
406
+ if (hasCodex) pushSessionGroup(lines, "Codex 会话", codexSessions, formatSession, idx);
407
+ if (hasCcc) pushSessionGroup(lines, "CCC Agent 会话", cccSessions, formatSession, idx);
403
408
 
404
409
  return JSON.stringify({
405
410
  config: { wide_screen_mode: true },
@@ -407,9 +412,9 @@ export function buildSessionsCard(sessions: Array<{
407
412
  elements: [
408
413
  { tag: "div", text: { tag: "lark_md", content: lines.join("\n") } },
409
414
  { tag: "hr" },
410
- { tag: "div", text: { tag: "lark_md", content: fixedPrivateSession
411
- ? "当前飞书私聊使用专属会话;默认 Agent 变化后,下一条普通消息会自动创建对应 Agent 的新空会话。发送 **/newh** 可在私聊中原地重置。群聊会话请回到对应群聊继续,私聊不支持 **/session** 切换。"
412
- : "在会话群内发送 **/newh** 可重置当前会话(创建新 Session,保留工作目录和群聊)。\n发送 **/session 数字**(如 `/session 1`)可将当前群聊切换到列表中对应编号的会话。" } },
415
+ { tag: "div", text: { tag: "lark_md", content: fixedPrivateSession
416
+ ? "当前飞书私聊使用专属会话;默认 Agent 变化后,下一条普通消息会自动创建对应 Agent 的新空会话。发送 **/newh** 可在私聊中原地重置。群聊会话请回到对应群聊继续,私聊不支持 **/session** 切换。"
417
+ : "在会话群内发送 **/newh** 可重置当前会话(创建新 Session,保留工作目录和群聊)。\n发送 **/session 数字**(如 `/session 1`)可将当前群聊切换到列表中对应编号的会话。" } },
413
418
  { tag: "hr" },
414
419
  {
415
420
  tag: "action",
@@ -466,11 +471,11 @@ export function buildQueueFullCard(): string {
466
471
  }
467
472
 
468
473
  // 状态卡片(带关闭按钮)
469
- export function buildStatusCard(statusText: string, template = "blue"): string {
470
- return JSON.stringify({
471
- config: { wide_screen_mode: true },
472
- header: { template, title: { content: "会话状态", tag: "plain_text" } },
473
- elements: [
474
+ export function buildStatusCard(statusText: string, template = "blue"): string {
475
+ return JSON.stringify({
476
+ config: { wide_screen_mode: true },
477
+ header: { template, title: { content: "会话状态", tag: "plain_text" } },
478
+ elements: [
474
479
  { tag: "div", text: { tag: "lark_md", content: statusText } },
475
480
  { tag: "hr" },
476
481
  {
@@ -482,82 +487,82 @@ export function buildStatusCard(statusText: string, template = "blue"): string {
482
487
  value: { action: "close" },
483
488
  }],
484
489
  },
485
- ],
486
- });
487
- }
488
-
489
- export function buildCodexUsageCard(content: string, resetCreditsAvailable: number | null): string {
490
- const elements: object[] = [
491
- { tag: "div", text: { tag: "lark_md", content } },
492
- ];
493
- if (resetCreditsAvailable !== null && resetCreditsAvailable > 0) {
494
- elements.push({ tag: "hr" });
495
- elements.push({
496
- tag: "action",
497
- actions: [{
498
- tag: "button",
499
- text: { tag: "plain_text", content: "发起重置" },
500
- type: "primary",
501
- value: { action: "codex_reset_request", availableCount: resetCreditsAvailable },
502
- }],
503
- });
504
- }
505
-
506
- return JSON.stringify({
507
- config: { wide_screen_mode: true },
508
- header: { template: "blue", title: { content: "Codex Usage", tag: "plain_text" } },
509
- elements,
510
- });
511
- }
512
-
513
- export function buildCodexResetConfirmCard(params: {
514
- availableCount: number;
515
- parentMessageId: string;
516
- requestId: string;
517
- }): string {
518
- const valueBase = {
519
- parentMessageId: params.parentMessageId,
520
- requestId: params.requestId,
521
- };
522
- return JSON.stringify({
523
- config: { wide_screen_mode: true },
524
- header: { template: "yellow", title: { content: "确认 Codex 主动重置", tag: "plain_text" } },
525
- elements: [
526
- {
527
- tag: "div",
528
- text: {
529
- tag: "lark_md",
530
- content: `当前可用主动重置次数:**${params.availableCount}**。\n\n确认后会消耗 1 次主动重置,并重置当前可重置的 Codex 用量窗口。`,
531
- },
532
- },
533
- { tag: "hr" },
534
- {
535
- tag: "action",
536
- actions: [
537
- {
538
- tag: "button",
539
- text: { tag: "plain_text", content: "是,发起重置" },
540
- type: "danger",
541
- value: { action: "codex_reset_confirm", decision: "yes", ...valueBase },
542
- },
543
- {
544
- tag: "button",
545
- text: { tag: "plain_text", content: "否" },
546
- type: "default",
547
- value: { action: "codex_reset_confirm", decision: "no", ...valueBase },
548
- },
549
- ],
550
- },
551
- ],
552
- });
553
- }
554
-
555
- /** 模型切换卡片(/model 命令,飞书专用,含切换按钮) */
556
- export function buildModelCard(
557
- currentModel: string,
558
- models: string[],
559
- tool?: string,
560
- ): string {
490
+ ],
491
+ });
492
+ }
493
+
494
+ export function buildCodexUsageCard(content: string, resetCreditsAvailable: number | null): string {
495
+ const elements: object[] = [
496
+ { tag: "div", text: { tag: "lark_md", content } },
497
+ ];
498
+ if (resetCreditsAvailable !== null && resetCreditsAvailable > 0) {
499
+ elements.push({ tag: "hr" });
500
+ elements.push({
501
+ tag: "action",
502
+ actions: [{
503
+ tag: "button",
504
+ text: { tag: "plain_text", content: "发起重置" },
505
+ type: "primary",
506
+ value: { action: "codex_reset_request", availableCount: resetCreditsAvailable },
507
+ }],
508
+ });
509
+ }
510
+
511
+ return JSON.stringify({
512
+ config: { wide_screen_mode: true },
513
+ header: { template: "blue", title: { content: "Codex Usage", tag: "plain_text" } },
514
+ elements,
515
+ });
516
+ }
517
+
518
+ export function buildCodexResetConfirmCard(params: {
519
+ availableCount: number;
520
+ parentMessageId: string;
521
+ requestId: string;
522
+ }): string {
523
+ const valueBase = {
524
+ parentMessageId: params.parentMessageId,
525
+ requestId: params.requestId,
526
+ };
527
+ return JSON.stringify({
528
+ config: { wide_screen_mode: true },
529
+ header: { template: "yellow", title: { content: "确认 Codex 主动重置", tag: "plain_text" } },
530
+ elements: [
531
+ {
532
+ tag: "div",
533
+ text: {
534
+ tag: "lark_md",
535
+ content: `当前可用主动重置次数:**${params.availableCount}**。\n\n确认后会消耗 1 次主动重置,并重置当前可重置的 Codex 用量窗口。`,
536
+ },
537
+ },
538
+ { tag: "hr" },
539
+ {
540
+ tag: "action",
541
+ actions: [
542
+ {
543
+ tag: "button",
544
+ text: { tag: "plain_text", content: "是,发起重置" },
545
+ type: "danger",
546
+ value: { action: "codex_reset_confirm", decision: "yes", ...valueBase },
547
+ },
548
+ {
549
+ tag: "button",
550
+ text: { tag: "plain_text", content: "否" },
551
+ type: "default",
552
+ value: { action: "codex_reset_confirm", decision: "no", ...valueBase },
553
+ },
554
+ ],
555
+ },
556
+ ],
557
+ });
558
+ }
559
+
560
+ /** 模型切换卡片(/model 命令,飞书专用,含切换按钮) */
561
+ export function buildModelCard(
562
+ currentModel: string,
563
+ models: string[],
564
+ tool?: string,
565
+ ): string {
561
566
  const toolLabel = tool ? ` (${tool})` : "";
562
567
  const currentLine = currentModel
563
568
  ? `**当前模型:** \`${currentModel}\``
@@ -574,12 +579,12 @@ export function buildModelCard(
574
579
  lines.push("", "没有可切换的模型。请在 config.json 中配置模型字段。");
575
580
  }
576
581
 
577
- if (tool === "codex") {
578
- lines.push("输入 `/fast` 查看或切换当前会话的 Fast 模式");
579
- }
580
-
581
- const buttons: ButtonDef[] = [];
582
- for (const m of models.slice(0, 20)) {
582
+ if (tool === "codex") {
583
+ lines.push("输入 `/fast` 查看或切换当前会话的 Fast 模式");
584
+ }
585
+
586
+ const buttons: ButtonDef[] = [];
587
+ for (const m of models.slice(0, 20)) {
583
588
  const shortName = m.includes("/") ? m.slice(m.lastIndexOf("/") + 1) : m;
584
589
  buttons.push({
585
590
  text: `/model ${shortName}`,
@@ -595,85 +600,85 @@ export function buildModelCard(
595
600
  { tag: "div", text: { tag: "lark_md", content: lines.join("\n") } },
596
601
  { tag: "hr" },
597
602
  buildButtons(buttons),
598
- ],
599
- });
600
- }
601
-
602
- export function buildFastModeCard(enabled: boolean): string {
603
- const mode = enabled ? "ON (Fast)" : "OFF (Standard)";
604
- return JSON.stringify({
605
- config: { wide_screen_mode: true },
606
- header: {
607
- template: enabled ? "green" : "blue",
608
- title: { content: "Codex Fast 模式", tag: "plain_text" },
609
- },
610
- elements: [
611
- {
612
- tag: "div",
613
- text: {
614
- tag: "lark_md",
615
- content: [
616
- `**当前模式:** ${mode}`,
617
- "",
618
- "切换将在下一条消息生效,当前生成不中断。",
619
- ].join("\n"),
620
- },
621
- },
622
- { tag: "hr" },
623
- buildButtons([
624
- {
625
- text: "ON",
626
- value: JSON.stringify({ cmd: "/fast on" }),
627
- type: enabled ? "primary" : "default",
628
- },
629
- {
630
- text: "OFF",
631
- value: JSON.stringify({ cmd: "/fast off" }),
632
- type: enabled ? "default" : "primary",
633
- },
634
- ]),
635
- ],
636
- });
637
- }
638
-
639
- export function buildEffortCard(
640
- currentEffort: string,
641
- efforts: string[],
642
- tool?: string,
643
- ): string {
644
- const toolLabel = tool ? ` (${tool})` : "";
645
- const currentLine = currentEffort
646
- ? `**当前 effort:** \`${currentEffort}\``
647
- : "**当前 effort:** 未指定";
648
-
649
- const lines: string[] = [currentLine];
650
- if (efforts.length > 0) {
651
- lines.push("", "**可切换 effort**");
652
- for (const effort of efforts) {
653
- lines.push(`- \`${effort}\``);
654
- }
655
- lines.push("", "点击按钮切换 effort,或输入 `/effort clear` 恢复默认");
656
- } else {
657
- lines.push("", "当前 agent 不支持 effort 切换。");
658
- }
659
-
660
- const buttons: ButtonDef[] = [];
661
- for (const effort of efforts.slice(0, 20)) {
662
- buttons.push({
663
- text: `/effort ${effort}`,
664
- value: JSON.stringify({ cmd: `/effort ${effort}` }),
665
- type: "primary",
666
- });
667
- }
668
-
669
- return JSON.stringify({
670
- config: { wide_screen_mode: true },
671
- header: { template: "blue", title: { content: `Effort 切换${toolLabel}`, tag: "plain_text" } },
672
- elements: [
673
- { tag: "div", text: { tag: "lark_md", content: lines.join("\n") } },
674
- { tag: "hr" },
675
- buildButtons(buttons),
676
- ],
677
- });
678
- }
603
+ ],
604
+ });
605
+ }
606
+
607
+ export function buildFastModeCard(enabled: boolean): string {
608
+ const mode = enabled ? "ON (Fast)" : "OFF (Standard)";
609
+ return JSON.stringify({
610
+ config: { wide_screen_mode: true },
611
+ header: {
612
+ template: enabled ? "green" : "blue",
613
+ title: { content: "Codex Fast 模式", tag: "plain_text" },
614
+ },
615
+ elements: [
616
+ {
617
+ tag: "div",
618
+ text: {
619
+ tag: "lark_md",
620
+ content: [
621
+ `**当前模式:** ${mode}`,
622
+ "",
623
+ "切换将在下一条消息生效,当前生成不中断。",
624
+ ].join("\n"),
625
+ },
626
+ },
627
+ { tag: "hr" },
628
+ buildButtons([
629
+ {
630
+ text: "ON",
631
+ value: JSON.stringify({ cmd: "/fast on" }),
632
+ type: enabled ? "primary" : "default",
633
+ },
634
+ {
635
+ text: "OFF",
636
+ value: JSON.stringify({ cmd: "/fast off" }),
637
+ type: enabled ? "default" : "primary",
638
+ },
639
+ ]),
640
+ ],
641
+ });
642
+ }
643
+
644
+ export function buildEffortCard(
645
+ currentEffort: string,
646
+ efforts: string[],
647
+ tool?: string,
648
+ ): string {
649
+ const toolLabel = tool ? ` (${tool})` : "";
650
+ const currentLine = currentEffort
651
+ ? `**当前 effort:** \`${currentEffort}\``
652
+ : "**当前 effort:** 未指定";
653
+
654
+ const lines: string[] = [currentLine];
655
+ if (efforts.length > 0) {
656
+ lines.push("", "**可切换 effort**");
657
+ for (const effort of efforts) {
658
+ lines.push(`- \`${effort}\``);
659
+ }
660
+ lines.push("", "点击按钮切换 effort,或输入 `/effort clear` 恢复默认");
661
+ } else {
662
+ lines.push("", "当前 agent 不支持 effort 切换。");
663
+ }
664
+
665
+ const buttons: ButtonDef[] = [];
666
+ for (const effort of efforts.slice(0, 20)) {
667
+ buttons.push({
668
+ text: `/effort ${effort}`,
669
+ value: JSON.stringify({ cmd: `/effort ${effort}` }),
670
+ type: "primary",
671
+ });
672
+ }
673
+
674
+ return JSON.stringify({
675
+ config: { wide_screen_mode: true },
676
+ header: { template: "blue", title: { content: `Effort 切换${toolLabel}`, tag: "plain_text" } },
677
+ elements: [
678
+ { tag: "div", text: { tag: "lark_md", content: lines.join("\n") } },
679
+ { tag: "hr" },
680
+ buildButtons(buttons),
681
+ ],
682
+ });
683
+ }
679
684