chatccc 0.2.224 → 0.2.226

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 (68) 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/package.json +1 -1
  6. package/src/__tests__/agent-activity.test.ts +76 -76
  7. package/src/__tests__/builtin-chat-session.test.ts +350 -350
  8. package/src/__tests__/builtin-config.test.ts +26 -37
  9. package/src/__tests__/builtin-context.test.ts +163 -163
  10. package/src/__tests__/builtin-file-tools.test.ts +275 -275
  11. package/src/__tests__/builtin-permissions.test.ts +211 -0
  12. package/src/__tests__/builtin-session-select.test.ts +116 -116
  13. package/src/__tests__/builtin-skills.test.ts +141 -141
  14. package/src/__tests__/card-action-routing.test.ts +18 -18
  15. package/src/__tests__/ccc-adapter.test.ts +136 -136
  16. package/src/__tests__/claude-adapter.test.ts +614 -614
  17. package/src/__tests__/codex-adapter.test.ts +58 -58
  18. package/src/__tests__/codex-raw-stream-log.test.ts +170 -170
  19. package/src/__tests__/cursor-adapter.test.ts +268 -268
  20. package/src/__tests__/feishu-avatar.test.ts +164 -164
  21. package/src/__tests__/feishu-message-ingress.test.ts +138 -138
  22. package/src/__tests__/package-files.test.ts +24 -24
  23. package/src/__tests__/progress-reducer.test.ts +110 -110
  24. package/src/__tests__/response-stall.test.ts +49 -49
  25. package/src/__tests__/sim-platform.test.ts +16 -16
  26. package/src/__tests__/startup-lifecycle.test.ts +231 -231
  27. package/src/__tests__/stop-session.test.ts +34 -34
  28. package/src/__tests__/terminal-renderer.test.ts +247 -143
  29. package/src/__tests__/update-command-guard.test.ts +144 -144
  30. package/src/__tests__/web-ui.test.ts +326 -323
  31. package/src/adapters/adapter-interface.ts +18 -18
  32. package/src/adapters/ccc-adapter.ts +131 -128
  33. package/src/adapters/claude-adapter.ts +620 -620
  34. package/src/adapters/codex-adapter.ts +426 -426
  35. package/src/adapters/cursor-adapter.ts +681 -681
  36. package/src/agent-activity.ts +170 -170
  37. package/src/agent-delegate-task.ts +91 -91
  38. package/src/builtin/cli.ts +598 -514
  39. package/src/builtin/config.ts +84 -0
  40. package/src/builtin/context.ts +323 -323
  41. package/src/builtin/file-log.ts +38 -0
  42. package/src/builtin/file-tools.ts +1407 -1320
  43. package/src/builtin/index.ts +437 -426
  44. package/src/builtin/permissions.ts +226 -0
  45. package/src/builtin/privacy.ts +141 -0
  46. package/src/builtin/proc-tree-kill.ts +61 -0
  47. package/src/builtin/progress/cards-helpers.ts +76 -0
  48. package/src/builtin/progress/reducer.ts +108 -0
  49. package/src/builtin/progress/terminal-renderer.ts +294 -0
  50. package/src/builtin/progress/view.ts +77 -0
  51. package/src/builtin/raw-stream-log.ts +124 -0
  52. package/src/builtin/session-select.ts +48 -48
  53. package/src/builtin/skills.ts +108 -108
  54. package/src/card-action-routing.ts +14 -14
  55. package/src/feishu-api.ts +193 -193
  56. package/src/feishu-message-ingress.ts +195 -195
  57. package/src/index.ts +306 -306
  58. package/src/orchestrator.ts +2388 -2388
  59. package/src/platform-adapter.ts +6 -6
  60. package/src/progress/reducer.ts +108 -108
  61. package/src/progress/terminal-renderer.ts +294 -190
  62. package/src/progress/view.ts +77 -77
  63. package/src/response-stall.ts +28 -28
  64. package/src/session-chat-binding.ts +82 -82
  65. package/src/startup-lifecycle.ts +250 -250
  66. package/src/stream-state.ts +18 -18
  67. package/src/update-command-guard.ts +165 -165
  68. package/src/web-ui.ts +18 -2
@@ -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
+ }
@@ -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
+ }