chatccc 0.2.229 → 0.2.231

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.
Files changed (120) hide show
  1. package/.agents/skills/create-chatccc-feishu-app/SKILL.md +85 -85
  2. package/.claude/skills/create-chatccc-feishu-app/SKILL.md +85 -85
  3. package/.cursor/skills/create-chatccc-feishu-app/SKILL.md +85 -85
  4. package/README.md +90 -90
  5. package/agent-prompts/claude_specific.md +45 -45
  6. package/agent-prompts/codex_specific.md +2 -2
  7. package/agent-prompts/cursor_specific.md +13 -13
  8. package/bin/cccagent.mjs +17 -17
  9. package/im-skills/feishu-skill/receive-send-file.md +63 -63
  10. package/im-skills/feishu-skill/receive-send-image.md +24 -24
  11. package/im-skills/feishu-skill/skill.md +3 -3
  12. package/im-skills/wechat-file-skill/receive-send-file.md +38 -38
  13. package/im-skills/wechat-file-skill/send-file.mjs +83 -83
  14. package/im-skills/wechat-file-skill/skill.md +10 -10
  15. package/im-skills/wechat-image-skill/skill.md +10 -10
  16. package/im-skills/wechat-video-skill/receive-send-video.md +38 -38
  17. package/im-skills/wechat-video-skill/send-video.mjs +79 -79
  18. package/im-skills/wechat-video-skill/skill.md +10 -10
  19. package/package.json +1 -1
  20. package/scripts/postinstall-sharp-check.mjs +58 -58
  21. package/src/__tests__/agent-activity.test.ts +86 -76
  22. package/src/__tests__/agent-delegate-task-rpc.test.ts +165 -165
  23. package/src/__tests__/agent-reload-config-rpc.test.ts +99 -99
  24. package/src/__tests__/builtin-chat-session.test.ts +65 -1
  25. package/src/__tests__/builtin-cli-json.test.ts +39 -39
  26. package/src/__tests__/builtin-config.test.ts +26 -26
  27. package/src/__tests__/builtin-context.test.ts +202 -163
  28. package/src/__tests__/builtin-permissions.test.ts +211 -211
  29. package/src/__tests__/builtin-session-select.test.ts +116 -116
  30. package/src/__tests__/builtin-sigint.test.ts +56 -56
  31. package/src/__tests__/builtin-web-tools.test.ts +220 -220
  32. package/src/__tests__/card-action-routing.test.ts +18 -18
  33. package/src/__tests__/cardkit.test.ts +60 -60
  34. package/src/__tests__/ccc-adapter.test.ts +170 -136
  35. package/src/__tests__/chatgpt-subscription-rpc.test.ts +89 -89
  36. package/src/__tests__/chatgpt-subscription.test.ts +135 -135
  37. package/src/__tests__/chrome-devtools-guard.test.ts +165 -165
  38. package/src/__tests__/claude-adapter.test.ts +614 -614
  39. package/src/__tests__/claude-raw-stream-log.test.ts +87 -87
  40. package/src/__tests__/codex-adapter.test.ts +58 -58
  41. package/src/__tests__/codex-raw-stream-log.test.ts +170 -170
  42. package/src/__tests__/codex-reset-actions.test.ts +146 -146
  43. package/src/__tests__/cursor-adapter.test.ts +268 -268
  44. package/src/__tests__/feishu-api.test.ts +60 -60
  45. package/src/__tests__/feishu-avatar.test.ts +164 -164
  46. package/src/__tests__/feishu-message-ingress.test.ts +138 -138
  47. package/src/__tests__/feishu-platform.test.ts +22 -22
  48. package/src/__tests__/format-message.test.ts +47 -47
  49. package/src/__tests__/jsonl-stream.test.ts +79 -79
  50. package/src/__tests__/package-files.test.ts +24 -24
  51. package/src/__tests__/privacy.test.ts +198 -198
  52. package/src/__tests__/progress-reducer.test.ts +121 -110
  53. package/src/__tests__/raw-stream-log.test.ts +106 -106
  54. package/src/__tests__/response-stall.test.ts +49 -49
  55. package/src/__tests__/session.test.ts +174 -16
  56. package/src/__tests__/shared-prefix.test.ts +36 -36
  57. package/src/__tests__/sim-platform.test.ts +16 -16
  58. package/src/__tests__/startup-lifecycle.test.ts +231 -231
  59. package/src/__tests__/stop-session.test.ts +34 -34
  60. package/src/__tests__/stream-state.test.ts +42 -42
  61. package/src/__tests__/terminal-error.test.ts +54 -0
  62. package/src/__tests__/terminal-renderer.test.ts +247 -247
  63. package/src/__tests__/update-command-guard.test.ts +144 -144
  64. package/src/__tests__/web-ui.test.ts +326 -326
  65. package/src/adapters/adapter-interface.ts +24 -18
  66. package/src/adapters/ccc-adapter.ts +141 -131
  67. package/src/adapters/claude-adapter.ts +620 -620
  68. package/src/adapters/claude-session-meta-store.ts +120 -120
  69. package/src/adapters/codex-adapter.ts +426 -426
  70. package/src/adapters/cursor-adapter.ts +681 -681
  71. package/src/adapters/jsonl-stream.ts +157 -157
  72. package/src/adapters/raw-stream-log.ts +124 -124
  73. package/src/adapters/resource-monitor.ts +140 -140
  74. package/src/agent-activity.ts +175 -170
  75. package/src/agent-delegate-task-rpc.ts +153 -153
  76. package/src/agent-delegate-task.ts +91 -91
  77. package/src/agent-reload-config-rpc.ts +34 -34
  78. package/src/agent-stop-stuck.ts +129 -129
  79. package/src/builtin/cli.ts +9 -2
  80. package/src/builtin/config.ts +84 -84
  81. package/src/builtin/context.ts +62 -4
  82. package/src/builtin/file-log.ts +38 -38
  83. package/src/builtin/index.ts +83 -19
  84. package/src/builtin/proc-tree-kill.ts +61 -61
  85. package/src/builtin/progress/cards-helpers.ts +76 -76
  86. package/src/builtin/progress/reducer.ts +113 -108
  87. package/src/builtin/progress/terminal-renderer.ts +294 -294
  88. package/src/builtin/progress/view.ts +77 -77
  89. package/src/builtin/raw-stream-log.ts +124 -124
  90. package/src/builtin/session-select.ts +48 -48
  91. package/src/builtin/sigint.ts +50 -50
  92. package/src/builtin/web-tools.ts +313 -313
  93. package/src/card-action-routing.ts +14 -14
  94. package/src/chatgpt-subscription-rpc.ts +27 -27
  95. package/src/chatgpt-subscription.ts +299 -299
  96. package/src/chrome-devtools-guard.ts +318 -318
  97. package/src/codex-reset-actions.ts +184 -184
  98. package/src/feishu-api.ts +193 -193
  99. package/src/feishu-message-ingress.ts +195 -195
  100. package/src/feishu-platform.ts +20 -20
  101. package/src/format-message.ts +293 -293
  102. package/src/litellm-proxy.ts +374 -374
  103. package/src/orchestrator.ts +8 -3
  104. package/src/platform-adapter.ts +6 -6
  105. package/src/privacy.ts +118 -118
  106. package/src/progress/reducer.ts +113 -108
  107. package/src/progress/terminal-renderer.ts +294 -294
  108. package/src/progress/view.ts +77 -77
  109. package/src/response-stall.ts +28 -28
  110. package/src/runtime-reload.ts +34 -34
  111. package/src/session-chat-binding.ts +82 -82
  112. package/src/session-name.ts +8 -8
  113. package/src/session.ts +149 -26
  114. package/src/shared-prefix.ts +29 -29
  115. package/src/sim-platform.ts +20 -20
  116. package/src/startup-lifecycle.ts +250 -250
  117. package/src/stream-state.ts +21 -18
  118. package/src/terminal-error.ts +129 -0
  119. package/src/turn-cards.ts +117 -117
  120. package/src/update-command-guard.ts +165 -165
@@ -1,77 +1,77 @@
1
- /**
2
- * progress/view.ts — 平台无关的"过程进度"视图模型
3
- *
4
- * 这是飞书过程卡片与终端过程区块共享的唯一数据模型:
5
- * - 飞书:buildProgressCard(view) 把 ProgressView 渲染为卡片 JSON
6
- * - 终端:TerminalProgressRenderer 把 ProgressView 渲染为 ANSI 区块
7
- * - 事件流:reduceProgress(prev, event) 把 ChatEvent 增量合并进 ProgressView
8
- *
9
- * 语义与飞书卡片对齐:
10
- * - headerTitle 是"生成中"阶段展示的标题(如"正在启动 Agent · 0秒")
11
- * - status 决定终态外观(完成 / 已停止 / 异常结束)
12
- * - text 是正文累积(对应卡片 main_content / 终端区块正文)
13
- * - tools 是本轮工具调用列表(飞书卡片暂不展示,终端折叠为单行)
14
- */
15
-
16
- export type ProgressStatus = "generating" | "done" | "stopped" | "error";
17
-
18
- export type ProgressToolStatus = "running" | "ok" | "error";
19
-
20
- export interface ProgressToolCall {
21
- /** 工具调用 ID(ChatEvent.tool_use.id,可能缺失) */
22
- id: string;
23
- /** 工具名,如 edit_file */
24
- name: string;
25
- status: ProgressToolStatus;
26
- /** 工具输入摘要(终端折叠行展示,截断保存) */
27
- detail?: string;
28
- /** 工具结果摘要(成功/失败,截断保存) */
29
- summary?: string;
30
- }
31
-
32
- export interface ProgressView {
33
- /** 当前阶段状态,决定终态外观 */
34
- status: ProgressStatus;
35
- /** 生成中阶段的头部标题(对应飞书卡片 header title) */
36
- headerTitle: string;
37
- /** 卡片头部颜色模板(飞书用),终端忽略 */
38
- headerTemplate: string;
39
- /** 正文累积内容 */
40
- text: string;
41
- /** 本轮工具调用列表 */
42
- tools: ProgressToolCall[];
43
- /** 是否展示停止按钮 / 停止提示 */
44
- showStop: boolean;
45
- /** 最近一次更新时间戳 */
46
- updatedAt: number;
47
- }
48
-
49
- export interface ProgressViewInit {
50
- status?: ProgressStatus;
51
- headerTitle?: string;
52
- headerTemplate?: string;
53
- text?: string;
54
- tools?: ProgressToolCall[];
55
- showStop?: boolean;
56
- }
57
-
58
- /** 创建 ProgressView,未提供的字段使用与飞书 buildProgressCard 一致的默认值 */
59
- export function progressView(init: ProgressViewInit = {}): ProgressView {
60
- return {
61
- status: init.status ?? "generating",
62
- headerTitle: init.headerTitle ?? "生成中...",
63
- headerTemplate: init.headerTemplate ?? "blue",
64
- text: init.text ?? "",
65
- tools: init.tools ?? [],
66
- showStop: init.showStop ?? true,
67
- updatedAt: Date.now(),
68
- };
69
- }
70
-
71
- /** 只读浅拷贝并覆盖部分字段,返回新视图(reducer 的不可变更新用) */
72
- export function withProgressView(
73
- prev: ProgressView,
74
- patch: Partial<ProgressView>,
75
- ): ProgressView {
76
- return { ...prev, ...patch, updatedAt: Date.now() };
77
- }
1
+ /**
2
+ * progress/view.ts — 平台无关的"过程进度"视图模型
3
+ *
4
+ * 这是飞书过程卡片与终端过程区块共享的唯一数据模型:
5
+ * - 飞书:buildProgressCard(view) 把 ProgressView 渲染为卡片 JSON
6
+ * - 终端:TerminalProgressRenderer 把 ProgressView 渲染为 ANSI 区块
7
+ * - 事件流:reduceProgress(prev, event) 把 ChatEvent 增量合并进 ProgressView
8
+ *
9
+ * 语义与飞书卡片对齐:
10
+ * - headerTitle 是"生成中"阶段展示的标题(如"正在启动 Agent · 0秒")
11
+ * - status 决定终态外观(完成 / 已停止 / 异常结束)
12
+ * - text 是正文累积(对应卡片 main_content / 终端区块正文)
13
+ * - tools 是本轮工具调用列表(飞书卡片暂不展示,终端折叠为单行)
14
+ */
15
+
16
+ export type ProgressStatus = "generating" | "done" | "stopped" | "error";
17
+
18
+ export type ProgressToolStatus = "running" | "ok" | "error";
19
+
20
+ export interface ProgressToolCall {
21
+ /** 工具调用 ID(ChatEvent.tool_use.id,可能缺失) */
22
+ id: string;
23
+ /** 工具名,如 edit_file */
24
+ name: string;
25
+ status: ProgressToolStatus;
26
+ /** 工具输入摘要(终端折叠行展示,截断保存) */
27
+ detail?: string;
28
+ /** 工具结果摘要(成功/失败,截断保存) */
29
+ summary?: string;
30
+ }
31
+
32
+ export interface ProgressView {
33
+ /** 当前阶段状态,决定终态外观 */
34
+ status: ProgressStatus;
35
+ /** 生成中阶段的头部标题(对应飞书卡片 header title) */
36
+ headerTitle: string;
37
+ /** 卡片头部颜色模板(飞书用),终端忽略 */
38
+ headerTemplate: string;
39
+ /** 正文累积内容 */
40
+ text: string;
41
+ /** 本轮工具调用列表 */
42
+ tools: ProgressToolCall[];
43
+ /** 是否展示停止按钮 / 停止提示 */
44
+ showStop: boolean;
45
+ /** 最近一次更新时间戳 */
46
+ updatedAt: number;
47
+ }
48
+
49
+ export interface ProgressViewInit {
50
+ status?: ProgressStatus;
51
+ headerTitle?: string;
52
+ headerTemplate?: string;
53
+ text?: string;
54
+ tools?: ProgressToolCall[];
55
+ showStop?: boolean;
56
+ }
57
+
58
+ /** 创建 ProgressView,未提供的字段使用与飞书 buildProgressCard 一致的默认值 */
59
+ export function progressView(init: ProgressViewInit = {}): ProgressView {
60
+ return {
61
+ status: init.status ?? "generating",
62
+ headerTitle: init.headerTitle ?? "生成中...",
63
+ headerTemplate: init.headerTemplate ?? "blue",
64
+ text: init.text ?? "",
65
+ tools: init.tools ?? [],
66
+ showStop: init.showStop ?? true,
67
+ updatedAt: Date.now(),
68
+ };
69
+ }
70
+
71
+ /** 只读浅拷贝并覆盖部分字段,返回新视图(reducer 的不可变更新用) */
72
+ export function withProgressView(
73
+ prev: ProgressView,
74
+ patch: Partial<ProgressView>,
75
+ ): ProgressView {
76
+ return { ...prev, ...patch, updatedAt: Date.now() };
77
+ }
@@ -1,28 +1,28 @@
1
- /** A snapshot of visible output progress while the Agent is starting or replying. */
2
- export interface ResponseProgressObservation {
3
- totalChars: number;
4
- unchangedSince: number;
5
- }
6
-
7
- /**
8
- * Tracks how long the displayed output character count has remained unchanged.
9
- * Leaving a monitored phase clears the window; returning starts a fresh one.
10
- */
11
- export function observeResponseProgress(
12
- previous: ResponseProgressObservation | undefined,
13
- isMonitoredPhase: boolean,
14
- totalChars: number,
15
- now = Date.now(),
16
- ): ResponseProgressObservation | undefined {
17
- if (!isMonitoredPhase) return undefined;
18
- if (previous?.totalChars === totalChars) return previous;
19
- return { totalChars, unchangedSince: now };
20
- }
21
-
22
- export function hasResponseStalled(
23
- observation: ResponseProgressObservation | undefined,
24
- now: number,
25
- timeoutMs: number,
26
- ): boolean {
27
- return observation !== undefined && now - observation.unchangedSince >= timeoutMs;
28
- }
1
+ /** A snapshot of visible output progress while the Agent is starting or replying. */
2
+ export interface ResponseProgressObservation {
3
+ totalChars: number;
4
+ unchangedSince: number;
5
+ }
6
+
7
+ /**
8
+ * Tracks how long the displayed output character count has remained unchanged.
9
+ * Leaving a monitored phase clears the window; returning starts a fresh one.
10
+ */
11
+ export function observeResponseProgress(
12
+ previous: ResponseProgressObservation | undefined,
13
+ isMonitoredPhase: boolean,
14
+ totalChars: number,
15
+ now = Date.now(),
16
+ ): ResponseProgressObservation | undefined {
17
+ if (!isMonitoredPhase) return undefined;
18
+ if (previous?.totalChars === totalChars) return previous;
19
+ return { totalChars, unchangedSince: now };
20
+ }
21
+
22
+ export function hasResponseStalled(
23
+ observation: ResponseProgressObservation | undefined,
24
+ now: number,
25
+ timeoutMs: number,
26
+ ): boolean {
27
+ return observation !== undefined && now - observation.unchangedSince >= timeoutMs;
28
+ }
@@ -1,34 +1,34 @@
1
- import {
2
- APP_ID,
3
- CONFIG_FILE,
4
- maskAppId,
5
- reloadConfigFromDisk,
6
- resolveDefaultAgentTool,
7
- type AgentTool,
8
- } from "./config.ts";
9
- import { appendStartupTrace } from "./shared.ts";
10
- import { clearAdapterCache } from "./session.ts";
11
- import { startChromeDevtoolsGuard } from "./chrome-devtools-guard.ts";
12
-
13
- export interface RuntimeReloadResult {
14
- configPath: string;
15
- defaultAgent: AgentTool;
16
- reloadedAt: string;
17
- }
18
-
19
- export function reloadRuntimeConfig(source: string): RuntimeReloadResult {
20
- reloadConfigFromDisk();
21
- clearAdapterCache();
22
- startChromeDevtoolsGuard();
23
-
24
- const result: RuntimeReloadResult = {
25
- configPath: CONFIG_FILE,
26
- defaultAgent: resolveDefaultAgentTool(),
27
- reloadedAt: new Date().toISOString(),
28
- };
29
- appendStartupTrace(`${source}: config reloaded`, {
30
- appIdMask: maskAppId(APP_ID),
31
- defaultAgent: result.defaultAgent,
32
- });
33
- return result;
34
- }
1
+ import {
2
+ APP_ID,
3
+ CONFIG_FILE,
4
+ maskAppId,
5
+ reloadConfigFromDisk,
6
+ resolveDefaultAgentTool,
7
+ type AgentTool,
8
+ } from "./config.ts";
9
+ import { appendStartupTrace } from "./shared.ts";
10
+ import { clearAdapterCache } from "./session.ts";
11
+ import { startChromeDevtoolsGuard } from "./chrome-devtools-guard.ts";
12
+
13
+ export interface RuntimeReloadResult {
14
+ configPath: string;
15
+ defaultAgent: AgentTool;
16
+ reloadedAt: string;
17
+ }
18
+
19
+ export function reloadRuntimeConfig(source: string): RuntimeReloadResult {
20
+ reloadConfigFromDisk();
21
+ clearAdapterCache();
22
+ startChromeDevtoolsGuard();
23
+
24
+ const result: RuntimeReloadResult = {
25
+ configPath: CONFIG_FILE,
26
+ defaultAgent: resolveDefaultAgentTool(),
27
+ reloadedAt: new Date().toISOString(),
28
+ };
29
+ appendStartupTrace(`${source}: config reloaded`, {
30
+ appIdMask: maskAppId(APP_ID),
31
+ defaultAgent: result.defaultAgent,
32
+ });
33
+ return result;
34
+ }
@@ -5,8 +5,8 @@
5
5
  // 由 session.ts 在初始化时调用 rebuildSessionChatsFromRegistry 重建
6
6
  // ---------------------------------------------------------------------------
7
7
 
8
- import type { PlatformAdapter } from "./platform-adapter.ts";
9
- import type { ResponseProgressObservation } from "./response-stall.ts";
8
+ import type { PlatformAdapter } from "./platform-adapter.ts";
9
+ import type { ResponseProgressObservation } from "./response-stall.ts";
10
10
 
11
11
  const sessionChatsMap = new Map<string, Set<string>>();
12
12
 
@@ -53,51 +53,51 @@ export function hasChatsForSession(sessionId: string): boolean {
53
53
  return (sessionChatsMap.get(sessionId)?.size ?? 0) > 0;
54
54
  }
55
55
 
56
- // Agent generator 退出后,最终卡片、registry、头像等仍可能在异步收尾。该阶段
57
- // 也必须阻止下一轮提前进入,否则旧会话的落盘可能覆盖新会话绑定。
58
- const finalizingSessions = new Set<string>();
59
-
60
- // response-stall 自动恢复预约只存在于当前进程内:用户已明确 ChatCCC 重启后
61
- // 不需要补发恢复 prompt。预约从检测到第一次停滞起一直持有到恢复轮同步进入
62
- // runAgentSession,填补旧轮收尾和新轮 activePrompts.set 之间的空窗,保证此时
63
- // 到达的普通用户消息只能进入缓存队列,不能抢在恢复 prompt 前执行。
64
- const autoRecoveryReservations = new Set<string>();
65
-
66
- /** 检查 sessionId 是否有活跃 prompt、正在收尾或已预约自动恢复。 */
67
- export function isSessionRunning(sessionId: string): boolean {
68
- return activePrompts.has(sessionId)
69
- || finalizingSessions.has(sessionId)
70
- || autoRecoveryReservations.has(sessionId);
71
- }
72
-
73
- export function markSessionFinalizing(sessionId: string): void {
74
- finalizingSessions.add(sessionId);
75
- }
76
-
77
- export function clearSessionFinalizing(sessionId: string): void {
78
- finalizingSessions.delete(sessionId);
79
- }
80
-
81
- /** 为 response-stall 后的内部恢复轮预留下一次执行权。 */
82
- export function reserveAutoRecovery(sessionId: string): boolean {
83
- if (autoRecoveryReservations.has(sessionId)) return false;
84
- autoRecoveryReservations.add(sessionId);
85
- return true;
86
- }
87
-
88
- /** 恢复轮启动前原子消费预约;返回 false 表示已被 /stop 取消。 */
89
- export function consumeAutoRecoveryReservation(sessionId: string): boolean {
90
- return autoRecoveryReservations.delete(sessionId);
91
- }
92
-
93
- /** 取消尚未启动的恢复轮。 */
94
- export function cancelAutoRecoveryReservation(sessionId: string): boolean {
95
- return autoRecoveryReservations.delete(sessionId);
96
- }
97
-
98
- export function hasAutoRecoveryReservation(sessionId: string): boolean {
99
- return autoRecoveryReservations.has(sessionId);
100
- }
56
+ // Agent generator 退出后,最终卡片、registry、头像等仍可能在异步收尾。该阶段
57
+ // 也必须阻止下一轮提前进入,否则旧会话的落盘可能覆盖新会话绑定。
58
+ const finalizingSessions = new Set<string>();
59
+
60
+ // response-stall 自动恢复预约只存在于当前进程内:用户已明确 ChatCCC 重启后
61
+ // 不需要补发恢复 prompt。预约从检测到第一次停滞起一直持有到恢复轮同步进入
62
+ // runAgentSession,填补旧轮收尾和新轮 activePrompts.set 之间的空窗,保证此时
63
+ // 到达的普通用户消息只能进入缓存队列,不能抢在恢复 prompt 前执行。
64
+ const autoRecoveryReservations = new Set<string>();
65
+
66
+ /** 检查 sessionId 是否有活跃 prompt、正在收尾或已预约自动恢复。 */
67
+ export function isSessionRunning(sessionId: string): boolean {
68
+ return activePrompts.has(sessionId)
69
+ || finalizingSessions.has(sessionId)
70
+ || autoRecoveryReservations.has(sessionId);
71
+ }
72
+
73
+ export function markSessionFinalizing(sessionId: string): void {
74
+ finalizingSessions.add(sessionId);
75
+ }
76
+
77
+ export function clearSessionFinalizing(sessionId: string): void {
78
+ finalizingSessions.delete(sessionId);
79
+ }
80
+
81
+ /** 为 response-stall 后的内部恢复轮预留下一次执行权。 */
82
+ export function reserveAutoRecovery(sessionId: string): boolean {
83
+ if (autoRecoveryReservations.has(sessionId)) return false;
84
+ autoRecoveryReservations.add(sessionId);
85
+ return true;
86
+ }
87
+
88
+ /** 恢复轮启动前原子消费预约;返回 false 表示已被 /stop 取消。 */
89
+ export function consumeAutoRecoveryReservation(sessionId: string): boolean {
90
+ return autoRecoveryReservations.delete(sessionId);
91
+ }
92
+
93
+ /** 取消尚未启动的恢复轮。 */
94
+ export function cancelAutoRecoveryReservation(sessionId: string): boolean {
95
+ return autoRecoveryReservations.delete(sessionId);
96
+ }
97
+
98
+ export function hasAutoRecoveryReservation(sessionId: string): boolean {
99
+ return autoRecoveryReservations.has(sessionId);
100
+ }
101
101
 
102
102
  // ---------------------------------------------------------------------------
103
103
  // activePrompts: sessionId → 活跃 prompt 控制
@@ -108,28 +108,28 @@ export interface ActivePrompt {
108
108
  stopped: boolean;
109
109
  startTime: number;
110
110
  /** Root PID for the CLI process currently serving this prompt, if the adapter exposes one. */
111
- processPid?: number;
112
- processMonitor?: ReturnType<typeof setInterval>;
113
- responseStallMonitor?: ReturnType<typeof setInterval>;
114
- /** Grace timer that force-closes a stream which stays open after its authoritative final event. */
115
- finalResponseCloseTimer?: ReturnType<typeof setTimeout>;
116
- /** Character-count progress observed only while the activity is "responding". */
117
- responseProgress?: ResponseProgressObservation;
118
- /** Set before a response-stall auto-end begins so competing monitors cannot win the race. */
119
- autoEnded?: boolean;
120
- autoEndedAt?: number;
111
+ processPid?: number;
112
+ processMonitor?: ReturnType<typeof setInterval>;
113
+ responseStallMonitor?: ReturnType<typeof setInterval>;
114
+ /** Grace timer that force-closes a stream which stays open after its authoritative final event. */
115
+ finalResponseCloseTimer?: ReturnType<typeof setTimeout>;
116
+ /** Character-count progress observed only while the activity is "responding". */
117
+ responseProgress?: ResponseProgressObservation;
118
+ /** Set before a response-stall auto-end begins so competing monitors cannot win the race. */
119
+ autoEnded?: boolean;
120
+ autoEndedAt?: number;
121
121
  /** Set when the watchdog detects that the CLI process disappeared before stream finalization. */
122
122
  abnormalExit?: boolean;
123
123
  abnormalExitNotified?: boolean;
124
- /** Set when the resource monitor detects CPU + memory unchanged for 3 minutes. */
125
- resourceStuck?: boolean;
126
- /** True only for the single internal continuation turn after a response stall. */
127
- autoRecovery?: boolean;
128
- /** Adapter observed an authoritative completed final-response event. */
129
- finalResponseObserved?: boolean;
130
- /** Adapter-provided callback to close the underlying SDK session / subprocess.
131
- * Called by stop-stuck-loop before controller.abort() to terminate the CLI
132
- * process immediately, rather than waiting for the async generator to unblock. */
124
+ /** Set when the resource monitor detects CPU + memory unchanged for 3 minutes. */
125
+ resourceStuck?: boolean;
126
+ /** True only for the single internal continuation turn after a response stall. */
127
+ autoRecovery?: boolean;
128
+ /** Adapter observed an authoritative completed final-response event. */
129
+ finalResponseObserved?: boolean;
130
+ /** Adapter-provided callback to close the underlying SDK session / subprocess.
131
+ * Called by stop-stuck-loop before controller.abort() to terminate the CLI
132
+ * process immediately, rather than waiting for the async generator to unblock. */
133
133
  closeSession?: () => void;
134
134
  }
135
135
 
@@ -193,10 +193,10 @@ export interface DisplayCardState {
193
193
  cardId: string;
194
194
  sequence: number;
195
195
  cardBusy: boolean;
196
- cardCreatedAt: number;
197
- lastSentContent: string;
198
- /** Last rendered activity header; elapsed time can change without body output. */
199
- lastSentHeaderTitle?: string;
196
+ cardCreatedAt: number;
197
+ lastSentContent: string;
198
+ /** Last rendered activity header; elapsed time can change without body output. */
199
+ lastSentHeaderTitle?: string;
200
200
  streamErrorNotified: boolean;
201
201
  /** 所属 session */
202
202
  sessionId: string;
@@ -209,8 +209,8 @@ export interface DisplayCardState {
209
209
  lastSentAccLen?: number;
210
210
  /** WeChat delta: 上次发送时的 finalReply */
211
211
  lastSentFinalReply?: string;
212
- /** Liveness animation counter; the explicit activity header conveys Agent state. */
213
- dotCount: number;
212
+ /** Liveness animation counter; the explicit activity header conveys Agent state. */
213
+ dotCount: number;
214
214
  }
215
215
 
216
216
  export const displayCards = new Map<string, DisplayCardState>();
@@ -274,18 +274,18 @@ export function consumeQueuedMessage(platform: PlatformAdapter, msg: QueuedMessa
274
274
  export function resetBindingState(): void {
275
275
  sessionChatsMap.clear();
276
276
  lastActiveChatMap.clear();
277
- for (const prompt of activePrompts.values()) {
278
- if (prompt.processMonitor) clearInterval(prompt.processMonitor);
279
- if (prompt.responseStallMonitor) clearInterval(prompt.responseStallMonitor);
280
- if (prompt.finalResponseCloseTimer) clearTimeout(prompt.finalResponseCloseTimer);
281
- }
282
- activePrompts.clear();
283
- finalizingSessions.clear();
284
- autoRecoveryReservations.clear();
285
- queuedMessages.clear();
277
+ for (const prompt of activePrompts.values()) {
278
+ if (prompt.processMonitor) clearInterval(prompt.processMonitor);
279
+ if (prompt.responseStallMonitor) clearInterval(prompt.responseStallMonitor);
280
+ if (prompt.finalResponseCloseTimer) clearTimeout(prompt.finalResponseCloseTimer);
281
+ }
282
+ activePrompts.clear();
283
+ finalizingSessions.clear();
284
+ autoRecoveryReservations.clear();
285
+ queuedMessages.clear();
286
286
  displayCards.clear();
287
287
  if (unifiedDisplayLoopHandle !== null) {
288
288
  clearInterval(unifiedDisplayLoopHandle);
289
289
  unifiedDisplayLoopHandle = null;
290
290
  }
291
- }
291
+ }
@@ -1,8 +1,8 @@
1
- export function cwdDisplayName(cwd: string): string {
2
- const trimmed = cwd.trim().replace(/[\\/]+$/, "");
3
- return trimmed.split(/[\\/]/).filter(Boolean).pop() || trimmed || "cwd";
4
- }
5
-
6
- export function sessionChatName(left: string, cwd: string): string {
7
- return `${left}-${cwdDisplayName(cwd)}`;
8
- }
1
+ export function cwdDisplayName(cwd: string): string {
2
+ const trimmed = cwd.trim().replace(/[\\/]+$/, "");
3
+ return trimmed.split(/[\\/]/).filter(Boolean).pop() || trimmed || "cwd";
4
+ }
5
+
6
+ export function sessionChatName(left: string, cwd: string): string {
7
+ return `${left}-${cwdDisplayName(cwd)}`;
8
+ }