chatccc 0.2.196 → 0.2.198

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 (53) hide show
  1. package/agent-prompts/cursor_specific.md +13 -13
  2. package/bin/cccagent.mjs +17 -17
  3. package/config.sample.json +27 -27
  4. package/package.json +2 -1
  5. package/src/__tests__/agent-reload-config-rpc.test.ts +99 -0
  6. package/src/__tests__/builtin-chat-session.test.ts +277 -181
  7. package/src/__tests__/builtin-cli-json.test.ts +39 -39
  8. package/src/__tests__/builtin-config.test.ts +33 -33
  9. package/src/__tests__/builtin-context.test.ts +163 -163
  10. package/src/__tests__/builtin-file-tools.test.ts +224 -196
  11. package/src/__tests__/builtin-session-select.test.ts +116 -116
  12. package/src/__tests__/builtin-sigint.test.ts +56 -56
  13. package/src/__tests__/cards.test.ts +109 -109
  14. package/src/__tests__/ccc-adapter.test.ts +114 -113
  15. package/src/__tests__/chatgpt-subscription-rpc.test.ts +89 -89
  16. package/src/__tests__/chatgpt-subscription.test.ts +135 -135
  17. package/src/__tests__/chrome-devtools-guard.test.ts +165 -165
  18. package/src/__tests__/claude-raw-stream-log.test.ts +87 -0
  19. package/src/__tests__/codex-raw-stream-log.test.ts +163 -0
  20. package/src/__tests__/config-reload.test.ts +10 -10
  21. package/src/__tests__/config-sample.test.ts +18 -18
  22. package/src/__tests__/cursor-adapter.test.ts +167 -113
  23. package/src/__tests__/feishu-avatar.test.ts +40 -40
  24. package/src/__tests__/jsonl-stream.test.ts +79 -0
  25. package/src/__tests__/orchestrator.test.ts +181 -154
  26. package/src/__tests__/raw-stream-log.test.ts +106 -106
  27. package/src/__tests__/session.test.ts +40 -40
  28. package/src/__tests__/sim-platform.test.ts +12 -12
  29. package/src/__tests__/web-ui.test.ts +209 -209
  30. package/src/adapters/ccc-adapter.ts +121 -119
  31. package/src/adapters/claude-adapter.ts +603 -566
  32. package/src/adapters/codex-adapter.ts +65 -52
  33. package/src/adapters/cursor-adapter.ts +269 -276
  34. package/src/adapters/jsonl-stream.ts +157 -0
  35. package/src/adapters/raw-stream-log.ts +124 -124
  36. package/src/agent-reload-config-rpc.ts +34 -0
  37. package/src/builtin/cli.ts +473 -461
  38. package/src/builtin/context.ts +323 -323
  39. package/src/builtin/file-tools.ts +1072 -915
  40. package/src/builtin/index.ts +404 -353
  41. package/src/builtin/session-select.ts +48 -48
  42. package/src/builtin/sigint.ts +50 -50
  43. package/src/cards.ts +195 -195
  44. package/src/chatgpt-subscription-rpc.ts +27 -27
  45. package/src/chatgpt-subscription.ts +299 -299
  46. package/src/chrome-devtools-guard.ts +318 -318
  47. package/src/config.ts +125 -125
  48. package/src/feishu-api.ts +49 -49
  49. package/src/index.ts +8 -13
  50. package/src/orchestrator.ts +166 -145
  51. package/src/runtime-reload.ts +34 -0
  52. package/src/session.ts +141 -141
  53. package/src/web-ui.ts +205 -205
@@ -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
+ });