chatccc 0.2.201 → 0.2.203

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 (95) hide show
  1. package/README.md +23 -21
  2. package/agent-prompts/claude_specific.md +45 -45
  3. package/agent-prompts/codex_specific.md +2 -2
  4. package/agent-prompts/cursor_specific.md +13 -13
  5. package/bin/cccagent.mjs +17 -17
  6. package/config.sample.json +27 -27
  7. package/im-skills/feishu-skill/receive-send-file.md +63 -63
  8. package/im-skills/feishu-skill/receive-send-image.md +24 -24
  9. package/im-skills/feishu-skill/skill.md +3 -3
  10. package/im-skills/wechat-file-skill/receive-send-file.md +38 -38
  11. package/im-skills/wechat-file-skill/send-file.mjs +83 -83
  12. package/im-skills/wechat-file-skill/skill.md +10 -10
  13. package/im-skills/wechat-image-skill/skill.md +10 -10
  14. package/im-skills/wechat-video-skill/receive-send-video.md +38 -38
  15. package/im-skills/wechat-video-skill/send-video.mjs +79 -79
  16. package/im-skills/wechat-video-skill/skill.md +10 -10
  17. package/package.json +1 -1
  18. package/scripts/postinstall-sharp-check.mjs +58 -58
  19. package/src/__tests__/agent-delegate-task-rpc.test.ts +165 -165
  20. package/src/__tests__/agent-reload-config-rpc.test.ts +99 -99
  21. package/src/__tests__/builtin-chat-session.test.ts +277 -277
  22. package/src/__tests__/builtin-cli-json.test.ts +39 -39
  23. package/src/__tests__/builtin-config.test.ts +33 -33
  24. package/src/__tests__/builtin-context.test.ts +163 -163
  25. package/src/__tests__/builtin-file-tools.test.ts +224 -224
  26. package/src/__tests__/builtin-session-select.test.ts +116 -116
  27. package/src/__tests__/builtin-sigint.test.ts +56 -56
  28. package/src/__tests__/card-plain-text.test.ts +5 -5
  29. package/src/__tests__/cardkit.test.ts +60 -60
  30. package/src/__tests__/cards.test.ts +133 -122
  31. package/src/__tests__/ccc-adapter.test.ts +114 -114
  32. package/src/__tests__/chatgpt-subscription-rpc.test.ts +89 -89
  33. package/src/__tests__/chatgpt-subscription.test.ts +135 -135
  34. package/src/__tests__/chrome-devtools-guard.test.ts +165 -165
  35. package/src/__tests__/claude-adapter.test.ts +592 -592
  36. package/src/__tests__/claude-raw-stream-log.test.ts +87 -87
  37. package/src/__tests__/codex-raw-stream-log.test.ts +163 -163
  38. package/src/__tests__/codex-reset-actions.test.ts +146 -146
  39. package/src/__tests__/config-reload.test.ts +10 -10
  40. package/src/__tests__/config-sample.test.ts +18 -18
  41. package/src/__tests__/cursor-adapter.test.ts +226 -226
  42. package/src/__tests__/feishu-api.test.ts +60 -60
  43. package/src/__tests__/feishu-avatar.test.ts +90 -90
  44. package/src/__tests__/feishu-platform.test.ts +22 -22
  45. package/src/__tests__/format-message.test.ts +47 -47
  46. package/src/__tests__/jsonl-stream.test.ts +79 -79
  47. package/src/__tests__/orchestrator.test.ts +464 -301
  48. package/src/__tests__/privacy.test.ts +198 -198
  49. package/src/__tests__/raw-stream-log.test.ts +106 -106
  50. package/src/__tests__/session.test.ts +46 -44
  51. package/src/__tests__/shared-prefix.test.ts +36 -36
  52. package/src/__tests__/sim-platform.test.ts +12 -12
  53. package/src/__tests__/stream-state.test.ts +42 -42
  54. package/src/__tests__/update-command-guard.test.ts +144 -0
  55. package/src/__tests__/web-ui.test.ts +209 -209
  56. package/src/adapters/ccc-adapter.ts +121 -121
  57. package/src/adapters/claude-adapter.ts +603 -603
  58. package/src/adapters/claude-session-meta-store.ts +120 -120
  59. package/src/adapters/codex-adapter.ts +380 -380
  60. package/src/adapters/cursor-adapter.ts +673 -673
  61. package/src/adapters/jsonl-stream.ts +157 -157
  62. package/src/adapters/raw-stream-log.ts +124 -124
  63. package/src/adapters/resource-monitor.ts +140 -140
  64. package/src/agent-delegate-task-rpc.ts +153 -153
  65. package/src/agent-delegate-task.ts +81 -81
  66. package/src/agent-reload-config-rpc.ts +34 -34
  67. package/src/agent-stop-stuck.ts +129 -129
  68. package/src/builtin/cli.ts +473 -473
  69. package/src/builtin/context.ts +323 -323
  70. package/src/builtin/file-tools.ts +1072 -1072
  71. package/src/builtin/index.ts +404 -404
  72. package/src/builtin/session-select.ts +48 -48
  73. package/src/builtin/sigint.ts +50 -50
  74. package/src/cards.ts +222 -210
  75. package/src/chatgpt-subscription-rpc.ts +27 -27
  76. package/src/chatgpt-subscription.ts +299 -299
  77. package/src/chrome-devtools-guard.ts +318 -318
  78. package/src/codex-reset-actions.ts +184 -184
  79. package/src/config.ts +125 -125
  80. package/src/feishu-api.ts +118 -118
  81. package/src/feishu-platform.ts +20 -20
  82. package/src/format-message.ts +293 -293
  83. package/src/index.ts +48 -9
  84. package/src/litellm-proxy.ts +374 -374
  85. package/src/orchestrator.ts +391 -346
  86. package/src/privacy.ts +118 -118
  87. package/src/runtime-reload.ts +34 -34
  88. package/src/session-chat-binding.ts +6 -6
  89. package/src/session-name.ts +8 -8
  90. package/src/session.ts +177 -169
  91. package/src/shared-prefix.ts +29 -29
  92. package/src/sim-platform.ts +20 -20
  93. package/src/turn-cards.ts +117 -117
  94. package/src/update-command-guard.ts +165 -0
  95. package/src/web-ui.ts +205 -205
@@ -1,33 +1,33 @@
1
- import { afterEach, describe, expect, it } from "vitest";
2
-
3
- import { ChatSession } from "../builtin/index.ts";
4
- import { config } from "../config.ts";
5
-
6
- const originalDeepSeekApiKey = process.env.DEEPSEEK_API_KEY;
7
- const originalCccConfig = structuredClone(config.ccc);
8
-
9
- afterEach(() => {
10
- if (originalDeepSeekApiKey === undefined) {
11
- delete process.env.DEEPSEEK_API_KEY;
12
- } else {
13
- process.env.DEEPSEEK_API_KEY = originalDeepSeekApiKey;
14
- }
15
- config.ccc = structuredClone(originalCccConfig);
16
- });
17
-
18
- describe("builtin ChatSession config", () => {
19
- it("does not fall back to DEEPSEEK_API_KEY environment variable", () => {
20
- config.ccc = {
21
- DEEPSEEK_API_KEY: "",
22
- DEEPSEEK_BASE_URL: "https://api.deepseek.com/v1",
23
- model: "deepseek-v4-pro",
24
- };
25
- process.env.DEEPSEEK_API_KEY = "sk-env-should-not-be-used";
26
-
27
- expect(() => new ChatSession()).toThrow("ccc.DEEPSEEK_API_KEY 未设置");
28
- });
29
-
30
- it("allows constructor parameters to override config defaults", () => {
31
- expect(() => new ChatSession({ apiKey: "sk-test" })).not.toThrow();
32
- });
33
- });
1
+ import { afterEach, describe, expect, it } from "vitest";
2
+
3
+ import { ChatSession } from "../builtin/index.ts";
4
+ import { config } from "../config.ts";
5
+
6
+ const originalDeepSeekApiKey = process.env.DEEPSEEK_API_KEY;
7
+ const originalCccConfig = structuredClone(config.ccc);
8
+
9
+ afterEach(() => {
10
+ if (originalDeepSeekApiKey === undefined) {
11
+ delete process.env.DEEPSEEK_API_KEY;
12
+ } else {
13
+ process.env.DEEPSEEK_API_KEY = originalDeepSeekApiKey;
14
+ }
15
+ config.ccc = structuredClone(originalCccConfig);
16
+ });
17
+
18
+ describe("builtin ChatSession config", () => {
19
+ it("does not fall back to DEEPSEEK_API_KEY environment variable", () => {
20
+ config.ccc = {
21
+ DEEPSEEK_API_KEY: "",
22
+ DEEPSEEK_BASE_URL: "https://api.deepseek.com/v1",
23
+ model: "deepseek-v4-pro",
24
+ };
25
+ process.env.DEEPSEEK_API_KEY = "sk-env-should-not-be-used";
26
+
27
+ expect(() => new ChatSession()).toThrow("ccc.DEEPSEEK_API_KEY 未设置");
28
+ });
29
+
30
+ it("allows constructor parameters to override config defaults", () => {
31
+ expect(() => new ChatSession({ apiKey: "sk-test" })).not.toThrow();
32
+ });
33
+ });
@@ -1,163 +1,163 @@
1
- import { mkdtemp, readFile } from "node:fs/promises";
2
- import { tmpdir } from "node:os";
3
- import { join } from "node:path";
4
-
5
- import { describe, expect, it } from "vitest";
6
-
7
- import {
8
- BuiltinContextManager,
9
- estimateBuiltinContextTokens,
10
- listBuiltinContextSessions,
11
- newBuiltinSessionId,
12
- serializeMessagesForSummary,
13
- } from "../builtin/context.ts";
14
-
15
- describe("BuiltinContextManager", () => {
16
- it("persists and restores summary, messages, and total message count", async () => {
17
- const dir = await mkdtemp(join(tmpdir(), "chatccc-builtin-context-"));
18
-
19
- const first = new BuiltinContextManager({
20
- persist: true,
21
- contextDir: dir,
22
- sessionId: "persisted",
23
- });
24
- first.setSummary("## 用户目标\n- 保留这个摘要");
25
- first.appendMessage({ role: "user", content: "第一轮" });
26
- first.appendMessage({ role: "assistant", content: "第一轮回复" });
27
- first.save();
28
-
29
- const restored = new BuiltinContextManager({
30
- persist: true,
31
- contextDir: dir,
32
- sessionId: "persisted",
33
- });
34
-
35
- expect(restored.summary).toContain("保留这个摘要");
36
- expect(restored.messages).toEqual([
37
- { role: "user", content: "第一轮" },
38
- { role: "assistant", content: "第一轮回复" },
39
- ]);
40
- expect(restored.totalMessages).toBe(2);
41
- });
42
-
43
- it("selects only older messages for compaction and keeps recent messages raw", () => {
44
- const context = new BuiltinContextManager({
45
- compactAtTokens: 1,
46
- keepRecentMessages: 2,
47
- persist: false,
48
- });
49
- context.appendMessage({ role: "user", content: "旧用户消息" });
50
- context.appendMessage({ role: "assistant", content: "旧助手回复" });
51
- context.appendMessage({ role: "user", content: "近期用户消息" });
52
- context.appendMessage({ role: "assistant", content: "近期助手回复" });
53
-
54
- const plan = context.planCompaction();
55
-
56
- expect(plan).not.toBeNull();
57
- expect(plan?.oldMessages).toEqual([
58
- { role: "user", content: "旧用户消息" },
59
- { role: "assistant", content: "旧助手回复" },
60
- ]);
61
- expect(plan?.recentMessages).toEqual([
62
- { role: "user", content: "近期用户消息" },
63
- { role: "assistant", content: "近期助手回复" },
64
- ]);
65
- });
66
-
67
- it("applies a compacted summary and builds model messages with summary plus recent raw turns", () => {
68
- const context = new BuiltinContextManager({
69
- compactAtTokens: 1,
70
- keepRecentMessages: 1,
71
- persist: false,
72
- });
73
- context.appendMessage({ role: "user", content: "old" });
74
- context.appendMessage({ role: "assistant", content: "recent" });
75
-
76
- const plan = context.planCompaction();
77
- expect(plan).not.toBeNull();
78
- context.applyCompaction("## 当前任务状态\n- 已压缩旧上下文", plan!);
79
-
80
- expect(context.summary).toContain("已压缩旧上下文");
81
- expect(context.messages).toEqual([{ role: "assistant", content: "recent" }]);
82
- expect(context.buildModelMessages()).toEqual([
83
- {
84
- role: "user",
85
- content: expect.stringContaining("较早对话摘要"),
86
- },
87
- { role: "assistant", content: "recent" },
88
- ]);
89
- });
90
-
91
- it("reset clears memory and the persisted context file", async () => {
92
- const dir = await mkdtemp(join(tmpdir(), "chatccc-builtin-context-reset-"));
93
- const context = new BuiltinContextManager({
94
- persist: true,
95
- contextDir: dir,
96
- sessionId: "reset",
97
- });
98
- context.setSummary("summary");
99
- context.appendMessage({ role: "user", content: "hello" });
100
- context.save();
101
-
102
- context.reset();
103
-
104
- expect(context.summary).toBe("");
105
- expect(context.messages).toEqual([]);
106
- expect(context.totalMessages).toBe(0);
107
-
108
- const raw = await readFile(join(dir, "reset", "context.json"), "utf8");
109
- const persisted = JSON.parse(raw) as { summary: string; messages: unknown[]; totalMessages: number };
110
- expect(persisted.summary).toBe("");
111
- expect(persisted.messages).toEqual([]);
112
- expect(persisted.totalMessages).toBe(0);
113
- });
114
-
115
- it("persists cwd metadata and lists saved sessions newest first", async () => {
116
- const dir = await mkdtemp(join(tmpdir(), "chatccc-builtin-context-list-"));
117
-
118
- const first = new BuiltinContextManager({
119
- persist: true,
120
- contextDir: dir,
121
- sessionId: "older",
122
- cwd: "C:\\repo-a",
123
- });
124
- first.appendMessage({ role: "user", content: "old" });
125
-
126
- const second = new BuiltinContextManager({
127
- persist: true,
128
- contextDir: dir,
129
- sessionId: "newer",
130
- cwd: "C:\\repo-b",
131
- });
132
- second.appendMessage({ role: "user", content: "new" });
133
-
134
- const sessions = listBuiltinContextSessions(dir);
135
-
136
- expect(sessions.map((s) => s.sessionId)).toEqual(["newer", "older"]);
137
- expect(sessions[0]).toEqual(expect.objectContaining({
138
- cwd: "C:\\repo-b",
139
- totalMessages: 1,
140
- hasSummary: false,
141
- }));
142
- });
143
- });
144
-
145
- describe("builtin context helpers", () => {
146
- it("creates readable timestamp-based session ids", () => {
147
- const id = newBuiltinSessionId(new Date(2026, 6, 2, 12, 15, 30), "a1b2c3");
148
-
149
- expect(id).toBe("session-20260702-121530-a1b2c3");
150
- });
151
-
152
- it("estimates tokens from summary and messages", () => {
153
- expect(estimateBuiltinContextTokens("abc", [{ role: "user", content: "abcdef" }]))
154
- .toBeGreaterThanOrEqual(3);
155
- });
156
-
157
- it("serializes messages for summarization with stable role labels", () => {
158
- expect(serializeMessagesForSummary([
159
- { role: "user", content: "你好" },
160
- { role: "assistant", content: "收到" },
161
- ])).toContain("user");
162
- });
163
- });
1
+ import { mkdtemp, readFile } from "node:fs/promises";
2
+ import { tmpdir } from "node:os";
3
+ import { join } from "node:path";
4
+
5
+ import { describe, expect, it } from "vitest";
6
+
7
+ import {
8
+ BuiltinContextManager,
9
+ estimateBuiltinContextTokens,
10
+ listBuiltinContextSessions,
11
+ newBuiltinSessionId,
12
+ serializeMessagesForSummary,
13
+ } from "../builtin/context.ts";
14
+
15
+ describe("BuiltinContextManager", () => {
16
+ it("persists and restores summary, messages, and total message count", async () => {
17
+ const dir = await mkdtemp(join(tmpdir(), "chatccc-builtin-context-"));
18
+
19
+ const first = new BuiltinContextManager({
20
+ persist: true,
21
+ contextDir: dir,
22
+ sessionId: "persisted",
23
+ });
24
+ first.setSummary("## 用户目标\n- 保留这个摘要");
25
+ first.appendMessage({ role: "user", content: "第一轮" });
26
+ first.appendMessage({ role: "assistant", content: "第一轮回复" });
27
+ first.save();
28
+
29
+ const restored = new BuiltinContextManager({
30
+ persist: true,
31
+ contextDir: dir,
32
+ sessionId: "persisted",
33
+ });
34
+
35
+ expect(restored.summary).toContain("保留这个摘要");
36
+ expect(restored.messages).toEqual([
37
+ { role: "user", content: "第一轮" },
38
+ { role: "assistant", content: "第一轮回复" },
39
+ ]);
40
+ expect(restored.totalMessages).toBe(2);
41
+ });
42
+
43
+ it("selects only older messages for compaction and keeps recent messages raw", () => {
44
+ const context = new BuiltinContextManager({
45
+ compactAtTokens: 1,
46
+ keepRecentMessages: 2,
47
+ persist: false,
48
+ });
49
+ context.appendMessage({ role: "user", content: "旧用户消息" });
50
+ context.appendMessage({ role: "assistant", content: "旧助手回复" });
51
+ context.appendMessage({ role: "user", content: "近期用户消息" });
52
+ context.appendMessage({ role: "assistant", content: "近期助手回复" });
53
+
54
+ const plan = context.planCompaction();
55
+
56
+ expect(plan).not.toBeNull();
57
+ expect(plan?.oldMessages).toEqual([
58
+ { role: "user", content: "旧用户消息" },
59
+ { role: "assistant", content: "旧助手回复" },
60
+ ]);
61
+ expect(plan?.recentMessages).toEqual([
62
+ { role: "user", content: "近期用户消息" },
63
+ { role: "assistant", content: "近期助手回复" },
64
+ ]);
65
+ });
66
+
67
+ it("applies a compacted summary and builds model messages with summary plus recent raw turns", () => {
68
+ const context = new BuiltinContextManager({
69
+ compactAtTokens: 1,
70
+ keepRecentMessages: 1,
71
+ persist: false,
72
+ });
73
+ context.appendMessage({ role: "user", content: "old" });
74
+ context.appendMessage({ role: "assistant", content: "recent" });
75
+
76
+ const plan = context.planCompaction();
77
+ expect(plan).not.toBeNull();
78
+ context.applyCompaction("## 当前任务状态\n- 已压缩旧上下文", plan!);
79
+
80
+ expect(context.summary).toContain("已压缩旧上下文");
81
+ expect(context.messages).toEqual([{ role: "assistant", content: "recent" }]);
82
+ expect(context.buildModelMessages()).toEqual([
83
+ {
84
+ role: "user",
85
+ content: expect.stringContaining("较早对话摘要"),
86
+ },
87
+ { role: "assistant", content: "recent" },
88
+ ]);
89
+ });
90
+
91
+ it("reset clears memory and the persisted context file", async () => {
92
+ const dir = await mkdtemp(join(tmpdir(), "chatccc-builtin-context-reset-"));
93
+ const context = new BuiltinContextManager({
94
+ persist: true,
95
+ contextDir: dir,
96
+ sessionId: "reset",
97
+ });
98
+ context.setSummary("summary");
99
+ context.appendMessage({ role: "user", content: "hello" });
100
+ context.save();
101
+
102
+ context.reset();
103
+
104
+ expect(context.summary).toBe("");
105
+ expect(context.messages).toEqual([]);
106
+ expect(context.totalMessages).toBe(0);
107
+
108
+ const raw = await readFile(join(dir, "reset", "context.json"), "utf8");
109
+ const persisted = JSON.parse(raw) as { summary: string; messages: unknown[]; totalMessages: number };
110
+ expect(persisted.summary).toBe("");
111
+ expect(persisted.messages).toEqual([]);
112
+ expect(persisted.totalMessages).toBe(0);
113
+ });
114
+
115
+ it("persists cwd metadata and lists saved sessions newest first", async () => {
116
+ const dir = await mkdtemp(join(tmpdir(), "chatccc-builtin-context-list-"));
117
+
118
+ const first = new BuiltinContextManager({
119
+ persist: true,
120
+ contextDir: dir,
121
+ sessionId: "older",
122
+ cwd: "C:\\repo-a",
123
+ });
124
+ first.appendMessage({ role: "user", content: "old" });
125
+
126
+ const second = new BuiltinContextManager({
127
+ persist: true,
128
+ contextDir: dir,
129
+ sessionId: "newer",
130
+ cwd: "C:\\repo-b",
131
+ });
132
+ second.appendMessage({ role: "user", content: "new" });
133
+
134
+ const sessions = listBuiltinContextSessions(dir);
135
+
136
+ expect(sessions.map((s) => s.sessionId)).toEqual(["newer", "older"]);
137
+ expect(sessions[0]).toEqual(expect.objectContaining({
138
+ cwd: "C:\\repo-b",
139
+ totalMessages: 1,
140
+ hasSummary: false,
141
+ }));
142
+ });
143
+ });
144
+
145
+ describe("builtin context helpers", () => {
146
+ it("creates readable timestamp-based session ids", () => {
147
+ const id = newBuiltinSessionId(new Date(2026, 6, 2, 12, 15, 30), "a1b2c3");
148
+
149
+ expect(id).toBe("session-20260702-121530-a1b2c3");
150
+ });
151
+
152
+ it("estimates tokens from summary and messages", () => {
153
+ expect(estimateBuiltinContextTokens("abc", [{ role: "user", content: "abcdef" }]))
154
+ .toBeGreaterThanOrEqual(3);
155
+ });
156
+
157
+ it("serializes messages for summarization with stable role labels", () => {
158
+ expect(serializeMessagesForSummary([
159
+ { role: "user", content: "你好" },
160
+ { role: "assistant", content: "收到" },
161
+ ])).toContain("user");
162
+ });
163
+ });