chatccc 0.2.252 → 0.2.254

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 (296) hide show
  1. package/README.md +10 -1
  2. package/bin/cccagent.mjs +12 -3
  3. package/bin/chatccc.mjs +15 -6
  4. package/config.sample.json +2 -1
  5. package/deepccc-agent/README.md +40 -35
  6. package/deepccc-agent/os-prompts/darwin.md +8 -8
  7. package/deepccc-agent/os-prompts/linux.md +8 -8
  8. package/deepccc-agent/os-prompts/win32.md +11 -11
  9. package/deepccc-agent/package.json +65 -65
  10. package/dist/deepccc-agent/src/cli.js +634 -0
  11. package/dist/deepccc-agent/src/config.js +81 -0
  12. package/dist/deepccc-agent/src/context.js +348 -0
  13. package/dist/deepccc-agent/src/file-log.js +34 -0
  14. package/dist/deepccc-agent/src/file-tools.js +1208 -0
  15. package/dist/deepccc-agent/src/index.js +601 -0
  16. package/dist/deepccc-agent/src/permissions.js +170 -0
  17. package/dist/deepccc-agent/src/privacy.js +124 -0
  18. package/dist/deepccc-agent/src/proc-tree-kill.js +60 -0
  19. package/dist/deepccc-agent/src/progress/cards-helpers.js +70 -0
  20. package/dist/deepccc-agent/src/progress/reducer.js +102 -0
  21. package/dist/deepccc-agent/src/progress/terminal-renderer.js +264 -0
  22. package/dist/deepccc-agent/src/progress/view.js +30 -0
  23. package/dist/deepccc-agent/src/raw-stream-log.js +106 -0
  24. package/dist/deepccc-agent/src/session-search.js +276 -0
  25. package/dist/deepccc-agent/src/session-select.js +23 -0
  26. package/dist/deepccc-agent/src/sigint.js +26 -0
  27. package/dist/deepccc-agent/src/skills.js +178 -0
  28. package/dist/deepccc-agent/src/web-tools.js +246 -0
  29. package/dist/src/adapters/adapter-interface.js +19 -0
  30. package/dist/src/adapters/ccc-adapter.js +112 -0
  31. package/dist/src/adapters/claude-adapter.js +497 -0
  32. package/dist/src/adapters/claude-session-meta-store.js +92 -0
  33. package/dist/src/adapters/codex-adapter.js +279 -0
  34. package/dist/src/adapters/codex-session-meta-store.js +94 -0
  35. package/dist/src/adapters/cursor-adapter.js +491 -0
  36. package/dist/src/adapters/cursor-session-meta-store.js +116 -0
  37. package/dist/src/adapters/jsonl-stream.js +104 -0
  38. package/{src/adapters/proc-tree-kill.ts → dist/src/adapters/proc-tree-kill.js} +94 -97
  39. package/dist/src/adapters/raw-stream-log.js +106 -0
  40. package/dist/src/adapters/resource-monitor.js +113 -0
  41. package/dist/src/agent-activity.js +133 -0
  42. package/dist/src/agent-delegate-task-rpc.js +129 -0
  43. package/dist/src/agent-delegate-task.js +48 -0
  44. package/dist/src/agent-file-rpc.js +152 -0
  45. package/dist/src/agent-image-rpc.js +148 -0
  46. package/dist/src/agent-platform-routing.js +13 -0
  47. package/dist/src/agent-reload-config-rpc.js +23 -0
  48. package/dist/src/agent-rpc-body.js +87 -0
  49. package/dist/src/agent-stop-stuck.js +110 -0
  50. package/dist/src/agent-team/application/board-service.js +142 -0
  51. package/dist/src/agent-team/application/main-agent-service.js +151 -0
  52. package/dist/src/agent-team/domain/board.js +69 -0
  53. package/dist/src/agent-team/http/board-routes.js +213 -0
  54. package/dist/src/agent-team/infrastructure/filesystem-browser.js +112 -0
  55. package/dist/src/agent-team/infrastructure/main-agent-session-runtime.js +30 -0
  56. package/dist/src/agent-team/main-agent-bootstrap.js +15 -0
  57. package/dist/src/agent-team/repositories/board-repository.js +10 -0
  58. package/dist/src/agent-team/repositories/feishu-p2p-contact-store.js +64 -0
  59. package/dist/src/agent-team/repositories/json-board-repository.js +209 -0
  60. package/dist/src/agent-team/repositories/main-agent-binding-repository.js +84 -0
  61. package/dist/src/agent-team/sync/board-sync-adapter.js +1 -0
  62. package/dist/src/agent-team/web/agent-team-page.js +219 -0
  63. package/dist/src/agent-tool.js +10 -0
  64. package/dist/src/card-action-routing.js +7 -0
  65. package/dist/src/card-plain-text.js +101 -0
  66. package/dist/src/cardkit.js +158 -0
  67. package/dist/src/cards.js +573 -0
  68. package/dist/src/chatgpt-subscription-rpc.js +18 -0
  69. package/dist/src/chatgpt-subscription.js +199 -0
  70. package/dist/src/chrome-devtools-guard.js +238 -0
  71. package/dist/src/claude-sdk-installer.js +249 -0
  72. package/dist/src/codex-reset-actions.js +143 -0
  73. package/dist/src/config-utils.js +149 -0
  74. package/dist/src/config.js +808 -0
  75. package/dist/src/cursor-usage.js +77 -0
  76. package/dist/src/exit-banner.js +28 -0
  77. package/dist/src/feishu-api.js +1404 -0
  78. package/dist/src/feishu-message-ingress.js +137 -0
  79. package/dist/src/feishu-platform.js +97 -0
  80. package/dist/src/format-message.js +252 -0
  81. package/dist/src/git-command.js +155 -0
  82. package/dist/src/im-skills.js +121 -0
  83. package/dist/src/index.js +835 -0
  84. package/dist/src/litellm-proxy.js +300 -0
  85. package/dist/src/orchestrator.js +2096 -0
  86. package/dist/src/package-root.js +26 -0
  87. package/dist/src/platform-adapter.js +7 -0
  88. package/dist/src/platform-startup.js +6 -0
  89. package/dist/src/privacy.js +100 -0
  90. package/dist/src/progress/reducer.js +102 -0
  91. package/dist/src/progress/terminal-renderer.js +264 -0
  92. package/dist/src/progress/view.js +30 -0
  93. package/dist/src/response-stall.js +14 -0
  94. package/dist/src/runtime-entry.js +13 -0
  95. package/dist/src/runtime-reload.js +19 -0
  96. package/dist/src/session-chat-binding.js +183 -0
  97. package/dist/src/session-name.js +7 -0
  98. package/dist/src/session.js +2147 -0
  99. package/dist/src/shared-prefix.js +16 -0
  100. package/dist/src/shared.js +493 -0
  101. package/dist/src/sim-agent.js +105 -0
  102. package/dist/src/sim-platform.js +142 -0
  103. package/dist/src/sim-store.js +231 -0
  104. package/dist/src/simplify.js +99 -0
  105. package/dist/src/startup-lifecycle.js +209 -0
  106. package/dist/src/stream-state.js +141 -0
  107. package/dist/src/terminal-error.js +100 -0
  108. package/dist/src/trace.js +50 -0
  109. package/dist/src/turn-cards.js +92 -0
  110. package/dist/src/update-command-guard.js +114 -0
  111. package/{src/web-ui.ts → dist/src/web-ui.js} +753 -839
  112. package/dist/src/wechat-platform.js +545 -0
  113. package/package.json +76 -74
  114. package/deepccc-agent/LICENSE +0 -201
  115. package/deepccc-agent/bin/deepccc.mjs +0 -26
  116. package/deepccc-agent/docs/cache-hit-rate-1.jpg +0 -0
  117. package/deepccc-agent/docs/cache-hit-rate-2.jpg +0 -0
  118. package/deepccc-agent/package-lock.json +0 -2027
  119. package/deepccc-agent/src/__tests__/chat-session.test.ts +0 -877
  120. package/deepccc-agent/src/__tests__/cli-json.test.ts +0 -49
  121. package/deepccc-agent/src/__tests__/config.test.ts +0 -34
  122. package/deepccc-agent/src/__tests__/context.test.ts +0 -341
  123. package/deepccc-agent/src/__tests__/file-tools.test.ts +0 -240
  124. package/deepccc-agent/src/__tests__/permissions.test.ts +0 -199
  125. package/deepccc-agent/src/__tests__/privacy.test.ts +0 -318
  126. package/deepccc-agent/src/__tests__/progress-reducer.test.ts +0 -121
  127. package/deepccc-agent/src/__tests__/session-search.test.ts +0 -262
  128. package/deepccc-agent/src/__tests__/session-select.test.ts +0 -116
  129. package/deepccc-agent/src/__tests__/sigint.test.ts +0 -56
  130. package/deepccc-agent/src/__tests__/skills.test.ts +0 -284
  131. package/deepccc-agent/src/__tests__/terminal-renderer.test.ts +0 -247
  132. package/deepccc-agent/src/__tests__/web-tools.test.ts +0 -220
  133. package/deepccc-agent/src/cli.ts +0 -682
  134. package/deepccc-agent/src/config.ts +0 -101
  135. package/deepccc-agent/src/context.ts +0 -465
  136. package/deepccc-agent/src/file-log.ts +0 -38
  137. package/deepccc-agent/src/file-tools.ts +0 -1493
  138. package/deepccc-agent/src/index.ts +0 -710
  139. package/deepccc-agent/src/permissions.ts +0 -226
  140. package/deepccc-agent/src/privacy.ts +0 -141
  141. package/deepccc-agent/src/proc-tree-kill.ts +0 -61
  142. package/deepccc-agent/src/progress/cards-helpers.ts +0 -76
  143. package/deepccc-agent/src/progress/reducer.ts +0 -113
  144. package/deepccc-agent/src/progress/terminal-renderer.ts +0 -294
  145. package/deepccc-agent/src/progress/view.ts +0 -77
  146. package/deepccc-agent/src/raw-stream-log.ts +0 -124
  147. package/deepccc-agent/src/session-search.ts +0 -370
  148. package/deepccc-agent/src/session-select.ts +0 -48
  149. package/deepccc-agent/src/sigint.ts +0 -50
  150. package/deepccc-agent/src/skills.ts +0 -205
  151. package/deepccc-agent/src/web-tools.ts +0 -313
  152. package/deepccc-agent/tsconfig.build.json +0 -13
  153. package/deepccc-agent/tsconfig.json +0 -13
  154. package/deepccc-agent/vitest.config.ts +0 -7
  155. package/src/__tests__/adapter-interface.test.ts +0 -152
  156. package/src/__tests__/agent-activity.test.ts +0 -86
  157. package/src/__tests__/agent-delegate-task-rpc.test.ts +0 -165
  158. package/src/__tests__/agent-image-rpc.test.ts +0 -34
  159. package/src/__tests__/agent-platform-routing.test.ts +0 -26
  160. package/src/__tests__/agent-reload-config-rpc.test.ts +0 -99
  161. package/src/__tests__/agent-rpc-body.test.ts +0 -42
  162. package/src/__tests__/builtin-chat-session.test.ts +0 -532
  163. package/src/__tests__/builtin-cli-json.test.ts +0 -39
  164. package/src/__tests__/builtin-config.test.ts +0 -26
  165. package/src/__tests__/builtin-context.test.ts +0 -319
  166. package/src/__tests__/builtin-file-tools.test.ts +0 -240
  167. package/src/__tests__/builtin-permissions.test.ts +0 -219
  168. package/src/__tests__/builtin-session-search.test.ts +0 -262
  169. package/src/__tests__/builtin-session-select.test.ts +0 -116
  170. package/src/__tests__/builtin-sigint.test.ts +0 -56
  171. package/src/__tests__/builtin-skills.test.ts +0 -284
  172. package/src/__tests__/builtin-web-tools.test.ts +0 -220
  173. package/src/__tests__/card-action-routing.test.ts +0 -18
  174. package/src/__tests__/card-plain-text.test.ts +0 -45
  175. package/src/__tests__/cardkit.test.ts +0 -60
  176. package/src/__tests__/cards.test.ts +0 -607
  177. package/src/__tests__/ccc-adapter.test.ts +0 -194
  178. package/src/__tests__/chatgpt-subscription-rpc.test.ts +0 -89
  179. package/src/__tests__/chatgpt-subscription.test.ts +0 -135
  180. package/src/__tests__/chrome-devtools-guard.test.ts +0 -165
  181. package/src/__tests__/claude-adapter.test.ts +0 -614
  182. package/src/__tests__/claude-raw-stream-log.test.ts +0 -96
  183. package/src/__tests__/claude-sdk-installer.test.ts +0 -285
  184. package/src/__tests__/codex-adapter.test.ts +0 -331
  185. package/src/__tests__/codex-raw-stream-log.test.ts +0 -170
  186. package/src/__tests__/codex-reset-actions.test.ts +0 -146
  187. package/src/__tests__/config-reload.test.ts +0 -284
  188. package/src/__tests__/config-sample.test.ts +0 -97
  189. package/src/__tests__/config-utils.test.ts +0 -40
  190. package/src/__tests__/config.test.ts +0 -395
  191. package/src/__tests__/crash-logging.test.ts +0 -360
  192. package/src/__tests__/cursor-adapter.test.ts +0 -890
  193. package/src/__tests__/cursor-session-meta-store.test.ts +0 -212
  194. package/src/__tests__/feishu-api.test.ts +0 -60
  195. package/src/__tests__/feishu-avatar.test.ts +0 -504
  196. package/src/__tests__/feishu-message-ingress.test.ts +0 -138
  197. package/src/__tests__/feishu-platform.test.ts +0 -75
  198. package/src/__tests__/fixtures/codex_simple_text.jsonl +0 -4
  199. package/src/__tests__/fixtures/codex_with_tool.jsonl +0 -6
  200. package/src/__tests__/fixtures/cursor_partial_only.jsonl +0 -5
  201. package/src/__tests__/fixtures/cursor_partial_with_final.jsonl +0 -13
  202. package/src/__tests__/fixtures/cursor_with_tool_call.jsonl +0 -12
  203. package/src/__tests__/format-message.test.ts +0 -316
  204. package/src/__tests__/git-command.test.ts +0 -288
  205. package/src/__tests__/im-skills.test.ts +0 -125
  206. package/src/__tests__/jsonl-stream.test.ts +0 -79
  207. package/src/__tests__/orchestrator.test.ts +0 -1268
  208. package/src/__tests__/package-files.test.ts +0 -24
  209. package/src/__tests__/platform-startup.test.ts +0 -19
  210. package/src/__tests__/privacy.test.ts +0 -198
  211. package/src/__tests__/proc-tree-kill.test.ts +0 -108
  212. package/src/__tests__/progress-reducer.test.ts +0 -121
  213. package/src/__tests__/raw-stream-log.test.ts +0 -106
  214. package/src/__tests__/response-stall.test.ts +0 -49
  215. package/src/__tests__/restart.test.ts +0 -232
  216. package/src/__tests__/session-ccc-config.test.ts +0 -66
  217. package/src/__tests__/session.test.ts +0 -3004
  218. package/src/__tests__/shared-prefix.test.ts +0 -36
  219. package/src/__tests__/sim-agent.test.ts +0 -174
  220. package/src/__tests__/sim-platform.test.ts +0 -93
  221. package/src/__tests__/sim-store.test.ts +0 -214
  222. package/src/__tests__/simplify.test.ts +0 -283
  223. package/src/__tests__/startup-lifecycle.test.ts +0 -231
  224. package/src/__tests__/stop-session.test.ts +0 -162
  225. package/src/__tests__/stream-state.test.ts +0 -164
  226. package/src/__tests__/terminal-error.test.ts +0 -54
  227. package/src/__tests__/terminal-renderer.test.ts +0 -247
  228. package/src/__tests__/update-command-guard.test.ts +0 -144
  229. package/src/__tests__/web-ui.test.ts +0 -438
  230. package/src/__tests__/wechat-platform.test.ts +0 -111
  231. package/src/adapters/adapter-interface.ts +0 -217
  232. package/src/adapters/ccc-adapter.ts +0 -150
  233. package/src/adapters/claude-adapter.ts +0 -673
  234. package/src/adapters/claude-session-meta-store.ts +0 -120
  235. package/src/adapters/codex-adapter.ts +0 -426
  236. package/src/adapters/codex-session-meta-store.ts +0 -131
  237. package/src/adapters/cursor-adapter.ts +0 -681
  238. package/src/adapters/cursor-session-meta-store.ts +0 -154
  239. package/src/adapters/jsonl-stream.ts +0 -157
  240. package/src/adapters/raw-stream-log.ts +0 -124
  241. package/src/adapters/resource-monitor.ts +0 -141
  242. package/src/agent-activity.ts +0 -175
  243. package/src/agent-delegate-task-rpc.ts +0 -153
  244. package/src/agent-delegate-task.ts +0 -91
  245. package/src/agent-file-rpc.ts +0 -172
  246. package/src/agent-image-rpc.ts +0 -168
  247. package/src/agent-platform-routing.ts +0 -28
  248. package/src/agent-reload-config-rpc.ts +0 -34
  249. package/src/agent-rpc-body.ts +0 -92
  250. package/src/agent-stop-stuck.ts +0 -129
  251. package/src/card-action-routing.ts +0 -14
  252. package/src/card-plain-text.ts +0 -108
  253. package/src/cardkit.ts +0 -179
  254. package/src/cards.ts +0 -684
  255. package/src/chatgpt-subscription-rpc.ts +0 -27
  256. package/src/chatgpt-subscription.ts +0 -299
  257. package/src/chrome-devtools-guard.ts +0 -318
  258. package/src/claude-sdk-installer.ts +0 -324
  259. package/src/codex-reset-actions.ts +0 -184
  260. package/src/config-utils.ts +0 -211
  261. package/src/config.ts +0 -1063
  262. package/src/cursor-usage.ts +0 -128
  263. package/src/exit-banner.ts +0 -33
  264. package/src/feishu-api.ts +0 -1616
  265. package/src/feishu-message-ingress.ts +0 -195
  266. package/src/feishu-platform.ts +0 -159
  267. package/src/format-message.ts +0 -293
  268. package/src/git-command.ts +0 -202
  269. package/src/im-skills.ts +0 -149
  270. package/src/index.ts +0 -1089
  271. package/src/litellm-proxy.ts +0 -374
  272. package/src/orchestrator.ts +0 -2543
  273. package/src/platform-adapter.ts +0 -70
  274. package/src/platform-startup.ts +0 -16
  275. package/src/privacy.ts +0 -118
  276. package/src/progress/reducer.ts +0 -113
  277. package/src/progress/terminal-renderer.ts +0 -294
  278. package/src/progress/view.ts +0 -77
  279. package/src/response-stall.ts +0 -28
  280. package/src/runtime-reload.ts +0 -34
  281. package/src/session-chat-binding.ts +0 -292
  282. package/src/session-name.ts +0 -8
  283. package/src/session.ts +0 -2659
  284. package/src/shared-prefix.ts +0 -29
  285. package/src/shared.ts +0 -552
  286. package/src/sim-agent.ts +0 -167
  287. package/src/sim-platform.ts +0 -177
  288. package/src/sim-store.ts +0 -317
  289. package/src/simplify.ts +0 -120
  290. package/src/startup-lifecycle.ts +0 -250
  291. package/src/stream-state.ts +0 -177
  292. package/src/terminal-error.ts +0 -129
  293. package/src/trace.ts +0 -51
  294. package/src/turn-cards.ts +0 -118
  295. package/src/update-command-guard.ts +0 -165
  296. package/src/wechat-platform.ts +0 -680
@@ -1,532 +0,0 @@
1
- import { mkdir, mkdtemp, readFile, writeFile } from "node:fs/promises";
2
- import { tmpdir } from "node:os";
3
- import { join } from "node:path";
4
-
5
- import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
6
-
7
- import { config } from "../../deepccc-agent/src/config.ts";
8
- import { estimateBuiltinContextTokens } from "../../deepccc-agent/src/context.ts";
9
-
10
- const streamTextMock = vi.fn();
11
- const generateTextMock = vi.fn();
12
- const createRawStreamLogMock = vi.fn();
13
- const rawLogWriteLineMock = vi.fn();
14
- const rawLogCloseMock = vi.fn();
15
- const originalRawStreamLogs = structuredClone(config.rawStreamLogs);
16
- const originalProvider = config.provider;
17
-
18
- vi.mock("@ai-sdk/openai-compatible", () => ({
19
- createOpenAICompatible: vi.fn(() => (modelId: string) => ({ modelId })),
20
- }));
21
-
22
- vi.mock("@ai-sdk/anthropic", () => ({
23
- createAnthropic: vi.fn(() => (modelId: string) => ({ modelId })),
24
- }));
25
-
26
- vi.mock("ai", () => ({
27
- streamText: streamTextMock,
28
- generateText: generateTextMock,
29
- isLoopFinished: vi.fn(() => ({ loopFinished: true })),
30
- stepCountIs: vi.fn((count: number) => ({ count })),
31
- jsonSchema: vi.fn((schema: unknown) => schema),
32
- tool: vi.fn((definition: unknown) => definition),
33
- }));
34
-
35
- vi.mock("../../deepccc-agent/src/raw-stream-log.js", () => ({
36
- createRawStreamLog: createRawStreamLogMock,
37
- }));
38
-
39
- async function collect(iterable: AsyncIterable<unknown>): Promise<unknown[]> {
40
- const events: unknown[] = [];
41
- for await (const event of iterable) events.push(event);
42
- return events;
43
- }
44
-
45
- async function* textStream(...chunks: string[]): AsyncIterable<string> {
46
- for (const chunk of chunks) yield chunk;
47
- }
48
-
49
- async function* fullStream(...parts: unknown[]): AsyncIterable<unknown> {
50
- for (const part of parts) yield part;
51
- }
52
-
53
- beforeEach(() => {
54
- config.provider = "openai";
55
- });
56
-
57
- afterEach(() => {
58
- streamTextMock.mockReset();
59
- generateTextMock.mockReset();
60
- createRawStreamLogMock.mockReset();
61
- rawLogWriteLineMock.mockReset();
62
- rawLogCloseMock.mockReset();
63
- config.rawStreamLogs = structuredClone(originalRawStreamLogs);
64
- config.provider = originalProvider;
65
- vi.useRealTimers();
66
- });
67
-
68
- describe("ChatSession context management", () => {
69
- it("keeps the generalized evidence gate in the stable system prompt prefix", async () => {
70
- const { ChatSession } = await import("../../deepccc-agent/src/index.ts");
71
- const dir = await mkdtemp(join(tmpdir(), "deepccc-session-evidence-gate-"));
72
- await writeFile(join(dir, "AGENTS.md"), "PROJECT GUIDANCE MARKER", "utf-8");
73
- streamTextMock.mockReturnValueOnce({ textStream: textStream("done") });
74
-
75
- const session = new ChatSession(
76
- { apiKey: "sk-test" },
77
- {
78
- cwd: dir,
79
- sessionId: "evidence-gate",
80
- systemPrompt: "CUSTOM PROMPT MARKER",
81
- },
82
- );
83
- await collect(session.chat("diagnose a consequential problem"));
84
-
85
- const system = streamTextMock.mock.calls.at(-1)?.[0].system as string;
86
- expect(system).toContain("## 证据门控结论");
87
- expect(system).toContain("权威事实来源");
88
- expect(system).toContain("区分直接观察与推断");
89
- expect(system).toContain("合理的替代解释");
90
- expect(system).toContain("运行时结论用运行时行为");
91
- expect(system).toContain("说明不确定性");
92
- expect(system).toContain("一旦已有决定性证据");
93
- expect(system).not.toContain("CodesForUnity");
94
- expect(system.indexOf("## 证据门控结论")).toBeLessThan(
95
- system.indexOf("PROJECT GUIDANCE MARKER"),
96
- );
97
- expect(system.indexOf("## 证据门控结论")).toBeLessThan(
98
- system.indexOf("当前工作目录"),
99
- );
100
- expect(system.indexOf("## 证据门控结论")).toBeLessThan(
101
- system.indexOf("CUSTOM PROMPT MARKER"),
102
- );
103
- });
104
-
105
- it("keeps execution discipline sections in the stable system prompt prefix", async () => {
106
- const { ChatSession } = await import("../../deepccc-agent/src/index.ts");
107
- const dir = await mkdtemp(join(tmpdir(), "deepccc-session-discipline-"));
108
- await writeFile(join(dir, "AGENTS.md"), "PROJECT GUIDANCE MARKER", "utf-8");
109
- streamTextMock.mockReturnValueOnce({ textStream: textStream("done") });
110
-
111
- const session = new ChatSession(
112
- { apiKey: "sk-test" },
113
- {
114
- cwd: dir,
115
- sessionId: "execution-discipline",
116
- systemPrompt: "CUSTOM PROMPT MARKER",
117
- },
118
- );
119
- await collect(session.chat("build a feature"));
120
-
121
- const system = streamTextMock.mock.calls.at(-1)?.[0].system as string;
122
- // 先盘点再动手:动手前低开销盘点 + 输出含验证策略的计划
123
- expect(system).toContain("## 行动前先调查");
124
- expect(system).toContain("以低成本盘点环境");
125
- expect(system).toContain("如何验证结果");
126
- // 授权自主:用户委托决策后只问真正阻塞项,不抛实现级选择题
127
- expect(system).toContain("## 授权范围");
128
- expect(system).toContain("不可逆操作");
129
- expect(system).toContain("不要把实现级选择题抛回给用户");
130
- // 交付自检:声明做了什么/如何验证/未验证项
131
- expect(system).toContain("## 交付前自检");
132
- expect(system).toContain("未验证或有风险");
133
- // 稳定前缀全部位于项目指令与 runtime 上下文之前
134
- for (const section of ["## 行动前先调查", "## 授权范围", "## 交付前自检"]) {
135
- expect(system.indexOf(section)).toBeGreaterThan(0);
136
- expect(system.indexOf(section)).toBeLessThan(system.indexOf("PROJECT GUIDANCE MARKER"));
137
- expect(system.indexOf(section)).toBeLessThan(system.indexOf("当前工作目录"));
138
- expect(system.indexOf(section)).toBeLessThan(system.indexOf("CUSTOM PROMPT MARKER"));
139
- }
140
- });
141
-
142
- it("injects cwd project instruction files before runtime workspace details", async () => {
143
- const { ChatSession } = await import("../../deepccc-agent/src/index.ts");
144
- const dir = await mkdtemp(join(tmpdir(), "deepccc-session-instructions-"));
145
- await writeFile(join(dir, "AGENTS.md"), "agents root guidance", "utf-8");
146
- await writeFile(join(dir, "AGENTS.local.md"), "agents local guidance", "utf-8");
147
- await writeFile(join(dir, "CLAUDE.md"), "claude root guidance", "utf-8");
148
- await writeFile(join(dir, "CLAUDE.local.md"), "claude local guidance", "utf-8");
149
- streamTextMock.mockReturnValueOnce({ textStream: textStream() });
150
-
151
- const session = new ChatSession(
152
- { apiKey: "sk-test" },
153
- {
154
- cwd: dir,
155
- sessionId: "project-instructions",
156
- },
157
- );
158
- await collect(session.chat("hi"));
159
-
160
- const system = streamTextMock.mock.calls.at(-1)?.[0].system as string;
161
- expect(system).toContain("## 项目指令");
162
- expect(system).toContain("### AGENTS.md");
163
- expect(system).toContain("agents root guidance");
164
- expect(system).toContain("### AGENTS.local.md");
165
- expect(system).toContain("agents local guidance");
166
- expect(system).toContain("### CLAUDE.md");
167
- expect(system).toContain("claude root guidance");
168
- expect(system).toContain("### CLAUDE.local.md");
169
- expect(system).toContain("claude local guidance");
170
-
171
- expect(system.indexOf("agents root guidance")).toBeLessThan(system.indexOf("agents local guidance"));
172
- expect(system.indexOf("agents local guidance")).toBeLessThan(system.indexOf("claude root guidance"));
173
- expect(system.indexOf("claude root guidance")).toBeLessThan(system.indexOf("claude local guidance"));
174
- expect(system.indexOf("claude local guidance")).toBeLessThan(system.indexOf(dir));
175
- });
176
-
177
- it("places the volatile skills index at the very end of system prompt", async () => {
178
- const { ChatSession } = await import("../../deepccc-agent/src/index.ts");
179
- const dir = await mkdtemp(join(tmpdir(), "deepccc-session-skill-order-"));
180
- const skillsDir = join(dir, "skills");
181
- await mkdir(join(skillsDir, "demo-skill"), { recursive: true });
182
- await writeFile(
183
- join(skillsDir, "demo-skill", "SKILL.md"),
184
- "---\nname: demo-skill\ndescription: demo description\n---\n\nbody\n",
185
- "utf-8",
186
- );
187
- streamTextMock.mockReturnValueOnce({ textStream: textStream("ok") });
188
-
189
- const session = new ChatSession(
190
- { apiKey: "sk-test" },
191
- {
192
- cwd: dir,
193
- sessionId: "skill-order",
194
- systemPrompt: "CUSTOM PROMPT MARKER",
195
- skillsDirs: [skillsDir],
196
- },
197
- );
198
- await collect(session.chat("hi"));
199
-
200
- const system = streamTextMock.mock.calls.at(-1)?.[0].system as string;
201
- expect(system).toContain("demo-skill");
202
- expect(system).toContain("CUSTOM PROMPT MARKER");
203
- expect(system).toContain("当前工作目录");
204
- // 稳定性排序:固定规则 → 项目指令 → runtime → custom → 技能索引(最后)
205
- expect(system.indexOf("CUSTOM PROMPT MARKER")).toBeGreaterThan(
206
- system.indexOf("当前工作目录"),
207
- );
208
- expect(system.indexOf("demo-skill")).toBeGreaterThan(system.indexOf("CUSTOM PROMPT MARKER"));
209
- });
210
-
211
- it("does not read project instruction files from parent directories", async () => {
212
- const { ChatSession } = await import("../../deepccc-agent/src/index.ts");
213
- const parent = await mkdtemp(join(tmpdir(), "deepccc-session-parent-instructions-"));
214
- const child = join(parent, "child");
215
- await mkdir(child);
216
- await writeFile(join(parent, "AGENTS.md"), "parent-only guidance", "utf-8");
217
- streamTextMock.mockReturnValueOnce({ textStream: textStream() });
218
-
219
- const session = new ChatSession(
220
- { apiKey: "sk-test" },
221
- {
222
- cwd: child,
223
- sessionId: "no-parent-instructions",
224
- },
225
- );
226
- await collect(session.chat("hi"));
227
-
228
- const system = streamTextMock.mock.calls.at(-1)?.[0].system as string;
229
- expect(system).not.toContain("parent-only guidance");
230
- });
231
-
232
- it("uses loop-finished stopping by default", async () => {
233
- const { ChatSession } = await import("../../deepccc-agent/src/index.ts");
234
- const dir = await mkdtemp(join(tmpdir(), "deepccc-session-unlimited-"));
235
- streamTextMock.mockReturnValueOnce({ textStream: textStream("done") });
236
-
237
- const session = new ChatSession(
238
- { apiKey: "sk-test" },
239
- {
240
- cwd: dir,
241
- sessionId: "unlimited-steps",
242
- },
243
- );
244
- await collect(session.chat("run a multi-stage workflow"));
245
-
246
- expect(streamTextMock).toHaveBeenLastCalledWith(expect.objectContaining({
247
- stopWhen: { loopFinished: true },
248
- }));
249
- });
250
-
251
- it("uses a configured tool step limit when provided", async () => {
252
- const { ChatSession } = await import("../../deepccc-agent/src/index.ts");
253
- const dir = await mkdtemp(join(tmpdir(), "deepccc-session-step-budget-"));
254
- streamTextMock.mockReturnValueOnce({ textStream: textStream("done") });
255
-
256
- const session = new ChatSession(
257
- { apiKey: "sk-test" },
258
- {
259
- cwd: dir,
260
- sessionId: "step-budget",
261
- maxSteps: 7,
262
- },
263
- );
264
- await collect(session.chat("run a bounded workflow"));
265
-
266
- expect(streamTextMock).toHaveBeenLastCalledWith(expect.objectContaining({
267
- stopWhen: { count: 7 },
268
- }));
269
- });
270
-
271
- it("loads persisted context, compacts older messages, and persists the new assistant reply", async () => {
272
- const { ChatSession } = await import("../../deepccc-agent/src/index.ts");
273
- const dir = await mkdtemp(join(tmpdir(), "deepccc-session-context-"));
274
-
275
- const seed = new ChatSession(
276
- { apiKey: "sk-test" },
277
- {
278
- persist: true,
279
- contextDir: dir,
280
- sessionId: "integration",
281
- compactAtTokens: 10_000,
282
- },
283
- );
284
- streamTextMock.mockReturnValueOnce({ textStream: textStream("old answer") });
285
- await collect(seed.chat("old question"));
286
-
287
- generateTextMock.mockResolvedValueOnce({ text: "## Current Task\n- old question summarized" });
288
- streamTextMock.mockReturnValueOnce({ textStream: textStream("new answer") });
289
-
290
- const restored = new ChatSession(
291
- { apiKey: "sk-test" },
292
- {
293
- persist: true,
294
- contextDir: dir,
295
- sessionId: "integration",
296
- compactAtTokens: 1,
297
- keepRecentMessages: 1,
298
- },
299
- );
300
- const events = await collect(restored.chat("new question"));
301
-
302
- expect(generateTextMock).toHaveBeenCalledOnce();
303
- expect(generateTextMock).toHaveBeenCalledWith(expect.objectContaining({ temperature: 0 }));
304
- expect(streamTextMock).toHaveBeenLastCalledWith(expect.objectContaining({
305
- messages: expect.arrayContaining([
306
- expect.objectContaining({ content: expect.stringContaining("old question summarized") }),
307
- expect.objectContaining({ role: "user", content: "new question" }),
308
- ]),
309
- }));
310
- expect(events.slice(0, 3)).toEqual([
311
- { type: "status", phase: "compacting" },
312
- { type: "compact", compactedMessages: 2 },
313
- { type: "status", phase: "generating" },
314
- ]);
315
- expect(restored.history.map((m) => m.content).join("\n")).toContain("new answer");
316
- });
317
-
318
- it("times out context compaction independently before reply generation", async () => {
319
- vi.useFakeTimers();
320
- const { ChatSession } = await import("../../deepccc-agent/src/index.ts");
321
- const dir = await mkdtemp(join(tmpdir(), "deepccc-session-compaction-timeout-"));
322
- const seed = new ChatSession(
323
- { apiKey: "sk-test" },
324
- { persist: true, contextDir: dir, sessionId: "timeout", compactAtTokens: 10_000 },
325
- );
326
- streamTextMock.mockReturnValueOnce({ textStream: textStream("old answer") });
327
- await collect(seed.chat("old question"));
328
-
329
- generateTextMock.mockImplementationOnce(({ abortSignal }: { abortSignal: AbortSignal }) =>
330
- new Promise((_resolve, reject) => {
331
- abortSignal.addEventListener("abort", () => reject(abortSignal.reason), { once: true });
332
- }));
333
- const restored = new ChatSession(
334
- { apiKey: "sk-test" },
335
- {
336
- persist: true,
337
- contextDir: dir,
338
- sessionId: "timeout",
339
- compactAtTokens: 1,
340
- keepRecentMessages: 1,
341
- compactionTimeoutMs: 100,
342
- },
343
- );
344
-
345
- const result = collect(restored.chat("new question"));
346
- const timeoutAssertion = expect(result).rejects.toThrow("Context compaction timed out");
347
- await vi.waitFor(() => expect(generateTextMock).toHaveBeenCalledOnce());
348
- await vi.advanceTimersByTimeAsync(101);
349
- await timeoutAssertion;
350
- expect(streamTextMock).toHaveBeenCalledTimes(1);
351
- });
352
-
353
- it("streams tool calls and tool results from fullStream", async () => {
354
- const { ChatSession } = await import("../../deepccc-agent/src/index.ts");
355
- const dir = await mkdtemp(join(tmpdir(), "deepccc-session-tools-"));
356
- const session = new ChatSession(
357
- { apiKey: "sk-test" },
358
- {
359
- persist: true,
360
- contextDir: dir,
361
- sessionId: "tools",
362
- },
363
- );
364
-
365
- streamTextMock.mockReturnValueOnce({
366
- fullStream: fullStream(
367
- { type: "tool-call", toolCallId: "call-1", toolName: "read_file", input: { path: "package.json" } },
368
- { type: "tool-result", toolCallId: "call-1", toolName: "read_file", output: { content: "{}" } },
369
- { type: "text-delta", text: "done" },
370
- ),
371
- });
372
-
373
- const events = await collect(session.chat("read package"));
374
-
375
- expect(events).toContainEqual({
376
- type: "tool_use",
377
- id: "call-1",
378
- name: "read_file",
379
- input: { path: "package.json" },
380
- });
381
- expect(events).toContainEqual({
382
- type: "tool_result",
383
- tool_use_id: "call-1",
384
- name: "read_file",
385
- content: { content: "{}" },
386
- is_error: false,
387
- });
388
- expect(events).toContainEqual({ type: "text", text: "done", accumulated: "done" });
389
- });
390
-
391
- it("caps the total persisted tool transcript for a turn", async () => {
392
- const { ChatSession } = await import("../../deepccc-agent/src/index.ts");
393
- const dir = await mkdtemp(join(tmpdir(), "deepccc-session-tool-cap-"));
394
- const session = new ChatSession(
395
- { apiKey: "sk-test" },
396
- { persist: true, contextDir: dir, sessionId: "tool-cap" },
397
- );
398
- const parts = Array.from({ length: 12 }, (_, index) => ({
399
- type: "tool-result",
400
- toolCallId: `call-${index}`,
401
- toolName: "read_file",
402
- output: { content: "x".repeat(8_000) },
403
- }));
404
- streamTextMock.mockReturnValueOnce({
405
- fullStream: fullStream(...parts, { type: "text-delta", text: "done" }),
406
- });
407
-
408
- await collect(session.chat("read many files"));
409
-
410
- const persisted = session.history.at(-1)?.content ?? "";
411
- expect(persisted.length).toBeLessThan(40_000);
412
- expect(persisted).toContain("工具记录已截断");
413
- });
414
-
415
- it("persists structured tool calls alongside the text transcript", async () => {
416
- const { ChatSession } = await import("../../deepccc-agent/src/index.ts");
417
- const dir = await mkdtemp(join(tmpdir(), "deepccc-session-structured-tools-"));
418
- const session = new ChatSession(
419
- { apiKey: "sk-test" },
420
- { persist: true, contextDir: dir, sessionId: "structured-tools" },
421
- );
422
-
423
- streamTextMock.mockReturnValueOnce({
424
- fullStream: fullStream(
425
- { type: "tool-call", toolCallId: "call-1", toolName: "read_file", input: { path: "package.json" } },
426
- { type: "tool-result", toolCallId: "call-1", toolName: "read_file", output: { content: "{}" } },
427
- { type: "text-delta", text: "done" },
428
- ),
429
- });
430
-
431
- await collect(session.chat("read package"));
432
-
433
- const raw = await readFile(join(dir, "structured-tools", "context.json"), "utf8");
434
- const state = JSON.parse(raw) as { messages: Array<{ content: string; toolCalls?: unknown }> };
435
- expect(state.messages).toHaveLength(2);
436
- expect(state.messages[1].toolCalls).toEqual([
437
- { name: "read_file", input: "{\"path\":\"package.json\"}", output: "{\"content\":\"{}\"}" },
438
- ]);
439
- expect(state.messages[1].content).toContain("[工具记录]");
440
- });
441
-
442
- it("records tool errors and preserves tool call order in structured tool calls", async () => {
443
- const { ChatSession } = await import("../../deepccc-agent/src/index.ts");
444
- const dir = await mkdtemp(join(tmpdir(), "deepccc-session-structured-tools-order-"));
445
- const session = new ChatSession(
446
- { apiKey: "sk-test" },
447
- { persist: true, contextDir: dir, sessionId: "structured-tools-order" },
448
- );
449
-
450
- // AI SDK 流中工具调用先全部到达(tool-call),结果后到达(tool-result/tool-error)
451
- streamTextMock.mockReturnValueOnce({
452
- fullStream: fullStream(
453
- { type: "tool-call", toolCallId: "c1", toolName: "run_command", input: { command: "npm test" } },
454
- { type: "tool-call", toolCallId: "c2", toolName: "read_file", input: { path: "a.ts" } },
455
- { type: "tool-result", toolCallId: "c1", toolName: "run_command", output: { exitCode: 0 } },
456
- { type: "tool-error", toolCallId: "c2", toolName: "read_file", error: new Error("boom") },
457
- { type: "text-delta", text: "failed" },
458
- ),
459
- });
460
-
461
- await collect(session.chat("run tests"));
462
-
463
- const raw = await readFile(join(dir, "structured-tools-order", "context.json"), "utf8");
464
- const state = JSON.parse(raw) as { messages: Array<{ toolCalls?: unknown[] }> };
465
- expect(state.messages[1].toolCalls).toEqual([
466
- { name: "run_command", input: "{\"command\":\"npm test\"}", output: "{\"exitCode\":0}" },
467
- { name: "read_file", input: "{\"path\":\"a.ts\"}", output: "boom", is_error: true },
468
- ]);
469
- });
470
-
471
- it("writes raw DeepCCC fullStream parts when raw stream logs are enabled", async () => {
472
- const { ChatSession } = await import("../../deepccc-agent/src/index.ts");
473
- config.rawStreamLogs = {
474
- enabled: true,
475
- maxBytesPerTurn: 4096,
476
- retentionDays: 3,
477
- keepCompleted: true,
478
- };
479
- createRawStreamLogMock.mockResolvedValueOnce({
480
- filePath: "raw.jsonl.gz",
481
- writeLine: rawLogWriteLineMock,
482
- close: rawLogCloseMock,
483
- });
484
- const session = new ChatSession(
485
- { apiKey: "sk-test" },
486
- {
487
- sessionId: "raw-log-session",
488
- },
489
- );
490
- const textPart = { type: "text-delta", text: "hello" };
491
- const toolPart = {
492
- type: "tool-call",
493
- toolCallId: "call-1",
494
- toolName: "read_file",
495
- input: { path: "package.json" },
496
- };
497
-
498
- streamTextMock.mockReturnValueOnce({
499
- fullStream: fullStream(textPart, toolPart),
500
- });
501
-
502
- await collect(session.chat("hi"));
503
-
504
- expect(createRawStreamLogMock).toHaveBeenCalledWith(expect.objectContaining({
505
- enabled: true,
506
- tool: "deepccc",
507
- sessionId: "raw-log-session",
508
- label: "prompt",
509
- maxBytesPerTurn: 4096,
510
- retentionDays: 3,
511
- }));
512
- expect(rawLogWriteLineMock).toHaveBeenNthCalledWith(1, JSON.stringify(textPart));
513
- expect(rawLogWriteLineMock).toHaveBeenNthCalledWith(2, JSON.stringify(toolPart));
514
- expect(rawLogCloseMock).toHaveBeenCalledWith({ keep: true });
515
- });
516
- });
517
-
518
- describe("estimateBuiltinContextTokens", () => {
519
- it("weights CJK characters closer to one token per character", () => {
520
- const messages = [{ role: "user" as const, content: "你好".repeat(100) }];
521
- const estimate = estimateBuiltinContextTokens("", messages);
522
- // 200 个汉字 ≈ 200 tokens(旧算法 chars/3 只估到 ~70,明显低估)
523
- expect(estimate).toBeGreaterThanOrEqual(180);
524
- });
525
-
526
- it("keeps latin text near chars/3.5", () => {
527
- const messages = [{ role: "user" as const, content: "a".repeat(1000) }];
528
- const estimate = estimateBuiltinContextTokens("", messages);
529
- expect(estimate).toBeGreaterThan(250);
530
- expect(estimate).toBeLessThan(340);
531
- });
532
- });
@@ -1,39 +0,0 @@
1
- import { execFile } from "node:child_process";
2
- import { promisify } from "node:util";
3
-
4
- import { describe, expect, it } from "vitest";
5
-
6
- const execFileAsync = promisify(execFile);
7
-
8
- describe("builtin cli --stream-json", () => {
9
- it("writes only JSON lines to stdout when startup fails", async () => {
10
- let caught: unknown;
11
- try {
12
- await execFileAsync(process.execPath, [
13
- "bin/cccagent.mjs",
14
- "--stream-json",
15
- "--prompt",
16
- "hello",
17
- "--api-key",
18
- "",
19
- ], {
20
- cwd: process.cwd(),
21
- timeout: 10_000,
22
- windowsHide: true,
23
- });
24
- } catch (err) {
25
- caught = err;
26
- }
27
-
28
- expect(caught).toMatchObject({ code: 1 });
29
- const stdout = (caught as { stdout?: string }).stdout ?? "";
30
- const lines = stdout.trim().split(/\r?\n/).filter(Boolean);
31
- expect(lines.length).toBeGreaterThan(0);
32
- for (const line of lines) {
33
- expect(() => JSON.parse(line)).not.toThrow();
34
- }
35
- expect(JSON.parse(lines.at(-1)!)).toEqual(expect.objectContaining({
36
- type: "error",
37
- }));
38
- });
39
- });
@@ -1,26 +0,0 @@
1
- import { afterEach, describe, expect, it } from "vitest";
2
-
3
- import { ChatSession } from "../../deepccc-agent/src/index.ts";
4
- import { config } from "../../deepccc-agent/src/config.ts";
5
-
6
- const originalDeepSeekApiKey = process.env.DEEPSEEK_API_KEY;
7
- const originalDeepCccApiKey = config.apiKey;
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.apiKey = originalDeepCccApiKey;
16
- });
17
-
18
- describe("builtin ChatSession config", () => {
19
- it("uses the builtin ~/.deepccc config when no apiKey is passed", () => {
20
- expect(() => new ChatSession()).not.toThrow();
21
- });
22
-
23
- it("allows constructor parameters to override config defaults", () => {
24
- expect(() => new ChatSession({ apiKey: "sk-test" })).not.toThrow();
25
- });
26
- });