chatccc 0.2.189 → 0.2.191

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 (67) hide show
  1. package/agent-prompts/claude_specific.md +45 -45
  2. package/agent-prompts/codex_specific.md +2 -2
  3. package/agent-prompts/cursor_specific.md +13 -13
  4. package/config.sample.json +14 -14
  5. package/im-skills/feishu-skill/receive-send-file.md +63 -63
  6. package/im-skills/feishu-skill/receive-send-image.md +24 -24
  7. package/im-skills/feishu-skill/skill.md +3 -3
  8. package/im-skills/wechat-file-skill/receive-send-file.md +38 -38
  9. package/im-skills/wechat-file-skill/send-file.mjs +83 -83
  10. package/im-skills/wechat-file-skill/skill.md +10 -10
  11. package/im-skills/wechat-image-skill/skill.md +10 -10
  12. package/im-skills/wechat-video-skill/receive-send-video.md +38 -38
  13. package/im-skills/wechat-video-skill/send-video.mjs +79 -79
  14. package/im-skills/wechat-video-skill/skill.md +10 -10
  15. package/package.json +1 -1
  16. package/scripts/postinstall-sharp-check.mjs +58 -58
  17. package/src/__tests__/agent-delegate-task-rpc.test.ts +165 -165
  18. package/src/__tests__/builtin-config.test.ts +33 -33
  19. package/src/__tests__/card-plain-text.test.ts +5 -5
  20. package/src/__tests__/cardkit.test.ts +60 -60
  21. package/src/__tests__/cards.test.ts +77 -77
  22. package/src/__tests__/chatgpt-subscription-rpc.test.ts +89 -89
  23. package/src/__tests__/chatgpt-subscription.test.ts +135 -135
  24. package/src/__tests__/chrome-devtools-guard.test.ts +165 -165
  25. package/src/__tests__/claude-adapter.test.ts +592 -592
  26. package/src/__tests__/codex-reset-actions.test.ts +146 -146
  27. package/src/__tests__/config-reload.test.ts +4 -4
  28. package/src/__tests__/config-sample.test.ts +17 -17
  29. package/src/__tests__/feishu-api.test.ts +60 -60
  30. package/src/__tests__/feishu-platform.test.ts +22 -22
  31. package/src/__tests__/format-message.test.ts +47 -47
  32. package/src/__tests__/orchestrator.test.ts +120 -116
  33. package/src/__tests__/privacy.test.ts +198 -198
  34. package/src/__tests__/raw-stream-log.test.ts +106 -106
  35. package/src/__tests__/session.test.ts +17 -17
  36. package/src/__tests__/shared-prefix.test.ts +36 -36
  37. package/src/__tests__/stream-state.test.ts +42 -42
  38. package/src/__tests__/web-ui.test.ts +209 -209
  39. package/src/adapters/claude-adapter.ts +566 -566
  40. package/src/adapters/claude-session-meta-store.ts +120 -120
  41. package/src/adapters/codex-adapter.ts +30 -30
  42. package/src/adapters/cursor-adapter.ts +46 -46
  43. package/src/adapters/raw-stream-log.ts +124 -124
  44. package/src/adapters/resource-monitor.ts +140 -140
  45. package/src/agent-delegate-task-rpc.ts +153 -153
  46. package/src/agent-delegate-task.ts +81 -81
  47. package/src/agent-stop-stuck.ts +129 -129
  48. package/src/builtin/cli.ts +204 -189
  49. package/src/builtin/index.ts +170 -168
  50. package/src/cards.ts +137 -137
  51. package/src/chatgpt-subscription-rpc.ts +27 -27
  52. package/src/chatgpt-subscription.ts +299 -299
  53. package/src/chrome-devtools-guard.ts +318 -318
  54. package/src/codex-reset-actions.ts +184 -184
  55. package/src/config.ts +86 -86
  56. package/src/feishu-platform.ts +20 -20
  57. package/src/format-message.ts +293 -293
  58. package/src/litellm-proxy.ts +374 -374
  59. package/src/orchestrator.ts +143 -143
  60. package/src/privacy.ts +118 -118
  61. package/src/session-chat-binding.ts +6 -6
  62. package/src/session-name.ts +8 -8
  63. package/src/session.ts +98 -98
  64. package/src/shared-prefix.ts +29 -29
  65. package/src/sim-platform.ts +20 -20
  66. package/src/turn-cards.ts +117 -117
  67. package/src/web-ui.ts +205 -205
@@ -1,120 +1,120 @@
1
- // =============================================================================
2
- // claude-session-meta-store.ts — Claude 会话 sessionId → meta 持久化映射
3
- // =============================================================================
4
- // 背景:Claude Agent SDK 的 getSessionInfo 可能因 cwd / 本地记录缺失而查不到。
5
- // ChatCCC 额外维护 sessionId → { cwd, model } 映射作为展示与恢复兜底。
6
- //
7
- // 存储:
8
- // 文件 state/claude-session-meta.json,结构:
9
- // { "<sessionId>": { "cwd": "...", "model": "..." } }
10
- //
11
- // API 设计:
12
- // set(sid, partial) → 部分合并写入;只更新非空字段,不会清空其他字段
13
- // =============================================================================
14
-
15
- import { mkdir, readFile, writeFile } from "node:fs/promises";
16
- import { dirname, join } from "node:path";
17
-
18
- import { USER_DATA_DIR } from "../config.ts";
19
-
20
- export const CLAUDE_SESSION_META_FILE = join(
21
- USER_DATA_DIR,
22
- "state",
23
- "claude-session-meta.json",
24
- );
25
-
26
- export interface ClaudeSessionMeta {
27
- cwd: string;
28
- model?: string;
29
- }
30
-
31
- export interface ClaudeSessionMetaStore {
32
- get(sessionId: string): Promise<ClaudeSessionMeta | undefined>;
33
- set(sessionId: string, partial: Partial<ClaudeSessionMeta>): Promise<void>;
34
- }
35
-
36
- interface RawEntry {
37
- cwd?: string;
38
- model?: string;
39
- }
40
-
41
- function isNonEmptyString(v: unknown): v is string {
42
- return typeof v === "string" && v.length > 0;
43
- }
44
-
45
- function parseEntry(raw: unknown): RawEntry | null {
46
- if (typeof raw === "string" && raw.length > 0) {
47
- return { cwd: raw };
48
- }
49
- if (raw && typeof raw === "object" && !Array.isArray(raw)) {
50
- const obj = raw as Record<string, unknown>;
51
- const out: RawEntry = {};
52
- if (isNonEmptyString(obj.cwd)) out.cwd = obj.cwd;
53
- if (isNonEmptyString(obj.model)) out.model = obj.model;
54
- return out;
55
- }
56
- return null;
57
- }
58
-
59
- export function createClaudeSessionMetaStore(
60
- filePath: string = CLAUDE_SESSION_META_FILE,
61
- ): ClaudeSessionMetaStore {
62
- let cache: Record<string, RawEntry> | null = null;
63
-
64
- async function load(): Promise<Record<string, RawEntry>> {
65
- if (cache) return cache;
66
- try {
67
- const raw = await readFile(filePath, "utf-8");
68
- const parsed = JSON.parse(raw);
69
- if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
70
- const out: Record<string, RawEntry> = {};
71
- for (const [k, v] of Object.entries(parsed as Record<string, unknown>)) {
72
- const entry = parseEntry(v);
73
- if (entry) out[k] = entry;
74
- }
75
- cache = out;
76
- return out;
77
- }
78
- } catch {
79
- // 文件不存在 / JSON 损坏 → 视为空映射
80
- }
81
- cache = {};
82
- return cache;
83
- }
84
-
85
- return {
86
- async get(sessionId: string): Promise<ClaudeSessionMeta | undefined> {
87
- const map = await load();
88
- const entry = map[sessionId];
89
- if (!entry || !isNonEmptyString(entry.cwd)) return undefined;
90
- return entry.model
91
- ? { cwd: entry.cwd, model: entry.model }
92
- : { cwd: entry.cwd };
93
- },
94
-
95
- async set(
96
- sessionId: string,
97
- partial: Partial<ClaudeSessionMeta>,
98
- ): Promise<void> {
99
- const map = await load();
100
- const existing = map[sessionId] ?? {};
101
- const merged: RawEntry = { ...existing };
102
- if (isNonEmptyString(partial.cwd)) merged.cwd = partial.cwd;
103
- if (isNonEmptyString(partial.model)) merged.model = partial.model;
104
-
105
- if (existing.cwd === merged.cwd && existing.model === merged.model) return;
106
-
107
- map[sessionId] = merged;
108
- try {
109
- await mkdir(dirname(filePath), { recursive: true });
110
- await writeFile(filePath, JSON.stringify(map, null, 2), "utf-8");
111
- } catch (err) {
112
- console.error(
113
- `[claude-session-meta] failed to persist ${filePath}: ${(err as Error).message}`,
114
- );
115
- }
116
- },
117
- };
118
- }
119
-
120
- export const defaultClaudeSessionMetaStore = createClaudeSessionMetaStore();
1
+ // =============================================================================
2
+ // claude-session-meta-store.ts — Claude 会话 sessionId → meta 持久化映射
3
+ // =============================================================================
4
+ // 背景:Claude Agent SDK 的 getSessionInfo 可能因 cwd / 本地记录缺失而查不到。
5
+ // ChatCCC 额外维护 sessionId → { cwd, model } 映射作为展示与恢复兜底。
6
+ //
7
+ // 存储:
8
+ // 文件 state/claude-session-meta.json,结构:
9
+ // { "<sessionId>": { "cwd": "...", "model": "..." } }
10
+ //
11
+ // API 设计:
12
+ // set(sid, partial) → 部分合并写入;只更新非空字段,不会清空其他字段
13
+ // =============================================================================
14
+
15
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
16
+ import { dirname, join } from "node:path";
17
+
18
+ import { USER_DATA_DIR } from "../config.ts";
19
+
20
+ export const CLAUDE_SESSION_META_FILE = join(
21
+ USER_DATA_DIR,
22
+ "state",
23
+ "claude-session-meta.json",
24
+ );
25
+
26
+ export interface ClaudeSessionMeta {
27
+ cwd: string;
28
+ model?: string;
29
+ }
30
+
31
+ export interface ClaudeSessionMetaStore {
32
+ get(sessionId: string): Promise<ClaudeSessionMeta | undefined>;
33
+ set(sessionId: string, partial: Partial<ClaudeSessionMeta>): Promise<void>;
34
+ }
35
+
36
+ interface RawEntry {
37
+ cwd?: string;
38
+ model?: string;
39
+ }
40
+
41
+ function isNonEmptyString(v: unknown): v is string {
42
+ return typeof v === "string" && v.length > 0;
43
+ }
44
+
45
+ function parseEntry(raw: unknown): RawEntry | null {
46
+ if (typeof raw === "string" && raw.length > 0) {
47
+ return { cwd: raw };
48
+ }
49
+ if (raw && typeof raw === "object" && !Array.isArray(raw)) {
50
+ const obj = raw as Record<string, unknown>;
51
+ const out: RawEntry = {};
52
+ if (isNonEmptyString(obj.cwd)) out.cwd = obj.cwd;
53
+ if (isNonEmptyString(obj.model)) out.model = obj.model;
54
+ return out;
55
+ }
56
+ return null;
57
+ }
58
+
59
+ export function createClaudeSessionMetaStore(
60
+ filePath: string = CLAUDE_SESSION_META_FILE,
61
+ ): ClaudeSessionMetaStore {
62
+ let cache: Record<string, RawEntry> | null = null;
63
+
64
+ async function load(): Promise<Record<string, RawEntry>> {
65
+ if (cache) return cache;
66
+ try {
67
+ const raw = await readFile(filePath, "utf-8");
68
+ const parsed = JSON.parse(raw);
69
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
70
+ const out: Record<string, RawEntry> = {};
71
+ for (const [k, v] of Object.entries(parsed as Record<string, unknown>)) {
72
+ const entry = parseEntry(v);
73
+ if (entry) out[k] = entry;
74
+ }
75
+ cache = out;
76
+ return out;
77
+ }
78
+ } catch {
79
+ // 文件不存在 / JSON 损坏 → 视为空映射
80
+ }
81
+ cache = {};
82
+ return cache;
83
+ }
84
+
85
+ return {
86
+ async get(sessionId: string): Promise<ClaudeSessionMeta | undefined> {
87
+ const map = await load();
88
+ const entry = map[sessionId];
89
+ if (!entry || !isNonEmptyString(entry.cwd)) return undefined;
90
+ return entry.model
91
+ ? { cwd: entry.cwd, model: entry.model }
92
+ : { cwd: entry.cwd };
93
+ },
94
+
95
+ async set(
96
+ sessionId: string,
97
+ partial: Partial<ClaudeSessionMeta>,
98
+ ): Promise<void> {
99
+ const map = await load();
100
+ const existing = map[sessionId] ?? {};
101
+ const merged: RawEntry = { ...existing };
102
+ if (isNonEmptyString(partial.cwd)) merged.cwd = partial.cwd;
103
+ if (isNonEmptyString(partial.model)) merged.model = partial.model;
104
+
105
+ if (existing.cwd === merged.cwd && existing.model === merged.model) return;
106
+
107
+ map[sessionId] = merged;
108
+ try {
109
+ await mkdir(dirname(filePath), { recursive: true });
110
+ await writeFile(filePath, JSON.stringify(map, null, 2), "utf-8");
111
+ } catch (err) {
112
+ console.error(
113
+ `[claude-session-meta] failed to persist ${filePath}: ${(err as Error).message}`,
114
+ );
115
+ }
116
+ },
117
+ };
118
+ }
119
+
120
+ export const defaultClaudeSessionMetaStore = createClaudeSessionMetaStore();
@@ -69,9 +69,9 @@ function buildCodexPromptText(userText: string): string {
69
69
  // ---------------------------------------------------------------------------
70
70
 
71
71
  /** 可通过 config.json codex.path 自定义 Codex 可执行文件路径 */
72
- function detectCodexCommand(): string {
73
- return config.codex.path || "codex";
74
- }
72
+ function detectCodexCommand(): string {
73
+ return config.codex.path || "codex";
74
+ }
75
75
 
76
76
  /** exec 模式共用参数:JSONL 输出、绕过沙盒和确认、跳过 git 仓库检查 */
77
77
  const CODEX_BASE_ARGS = [
@@ -183,13 +183,13 @@ export function normalizeCodexMessage(
183
183
  // 子进程辅助函数
184
184
  // ---------------------------------------------------------------------------
185
185
 
186
- function spawnCodex(
187
- args: string[],
188
- cwd?: string,
189
- stdinText?: string,
190
- modelOverride?: string,
191
- effortOverride?: string,
192
- ): ChildProcess {
186
+ function spawnCodex(
187
+ args: string[],
188
+ cwd?: string,
189
+ stdinText?: string,
190
+ modelOverride?: string,
191
+ effortOverride?: string,
192
+ ): ChildProcess {
193
193
  const allArgs = [...args];
194
194
  const model = modelOverride ?? resolveCodexModel();
195
195
  if (model) {
@@ -197,12 +197,12 @@ function spawnCodex(
197
197
  const execIdx = allArgs.indexOf("exec");
198
198
  allArgs.splice(execIdx + 1, 0, "-m", model);
199
199
  }
200
- const effort = effortOverride ?? resolveCodexEffort();
200
+ const effort = effortOverride ?? resolveCodexEffort();
201
201
  if (effort) {
202
202
  allArgs.push("-c", `model_reasoning_effort="${effort}"`);
203
203
  }
204
204
 
205
- const proc = spawn(detectCodexCommand(), allArgs, {
205
+ const proc = spawn(detectCodexCommand(), allArgs, {
206
206
  cwd,
207
207
  stdio: [stdinText !== undefined ? "pipe" : "ignore", "pipe", "pipe"],
208
208
  windowsHide: true,
@@ -258,17 +258,17 @@ async function* readJsonLines(
258
258
  // ---------------------------------------------------------------------------
259
259
 
260
260
  class CodexAdapter implements ToolAdapter {
261
- readonly displayName = "Codex";
262
- readonly sessionDescPrefix = "Codex Session:";
263
- private metaStore: CodexSessionMetaStore;
264
- private modelOverride: string | undefined;
265
- private effortOverride: string | undefined;
266
-
267
- constructor(metaStore: CodexSessionMetaStore, modelOverride?: string, effortOverride?: string) {
268
- this.metaStore = metaStore;
269
- this.modelOverride = modelOverride;
270
- this.effortOverride = effortOverride;
271
- }
261
+ readonly displayName = "Codex";
262
+ readonly sessionDescPrefix = "Codex Session:";
263
+ private metaStore: CodexSessionMetaStore;
264
+ private modelOverride: string | undefined;
265
+ private effortOverride: string | undefined;
266
+
267
+ constructor(metaStore: CodexSessionMetaStore, modelOverride?: string, effortOverride?: string) {
268
+ this.metaStore = metaStore;
269
+ this.modelOverride = modelOverride;
270
+ this.effortOverride = effortOverride;
271
+ }
272
272
 
273
273
  // createSession: 生成 sessionId,记录 cwd,不创建 Codex 线程(延迟到首次 prompt)
274
274
  async createSession(cwd: string): Promise<CreateSessionResult> {
@@ -298,7 +298,7 @@ class CodexAdapter implements ToolAdapter {
298
298
  ? [...baseArgs, "-C", cwd, "-"]
299
299
  : [...baseArgs, "resume", threadId, "-"];
300
300
 
301
- const proc = spawnCodex(args, cwd, buildCodexPromptText(userText), this.modelOverride, this.effortOverride);
301
+ const proc = spawnCodex(args, cwd, buildCodexPromptText(userText), this.modelOverride, this.effortOverride);
302
302
  if (proc.pid !== undefined) options?.onProcessStart?.({ pid: proc.pid });
303
303
 
304
304
  // 关键:spawn 用了 shell:true,proc.pid 指向的是壳进程(cmd.exe / sh)。
@@ -352,8 +352,8 @@ class CodexAdapter implements ToolAdapter {
352
352
  export interface CreateCodexAdapterOptions {
353
353
  metaStore?: CodexSessionMetaStore;
354
354
  /** per-session 模型覆盖(/model 命令);传了就用,不传走全局 codex.model */
355
- model?: string;
356
- effort?: string;
355
+ model?: string;
356
+ effort?: string;
357
357
  }
358
358
 
359
359
  export function createCodexAdapter(
@@ -361,7 +361,7 @@ export function createCodexAdapter(
361
361
  ): ToolAdapter {
362
362
  return new CodexAdapter(
363
363
  options.metaStore ?? defaultCodexSessionMetaStore,
364
- options.model,
365
- options.effort,
366
- );
367
- }
364
+ options.model,
365
+ options.effort,
366
+ );
367
+ }
@@ -20,16 +20,16 @@ import type {
20
20
  SessionInfo,
21
21
  } from "./adapter-interface.ts";
22
22
  import { parseUserCommand } from "./adapter-interface.ts";
23
- import { config, CURSOR_AGENT_COMMAND, CURSOR_AGENT_ARGS, RAW_STREAM_LOGS_DIR } from "../config.ts";
23
+ import { config, CURSOR_AGENT_COMMAND, CURSOR_AGENT_ARGS, RAW_STREAM_LOGS_DIR } from "../config.ts";
24
24
  import {
25
25
  defaultCursorSessionMetaStore,
26
26
  type CursorSessionMetaStore,
27
27
  } from "./cursor-session-meta-store.ts";
28
- import { killProcessTree } from "./proc-tree-kill.ts";
29
- import {
30
- createRawStreamLog,
31
- type RawStreamLogHandle,
32
- } from "./raw-stream-log.ts";
28
+ import { killProcessTree } from "./proc-tree-kill.ts";
29
+ import {
30
+ createRawStreamLog,
31
+ type RawStreamLogHandle,
32
+ } from "./raw-stream-log.ts";
33
33
 
34
34
  // ---------------------------------------------------------------------------
35
35
  // 特殊注入提示
@@ -348,12 +348,12 @@ function spawnAgent(
348
348
  return proc;
349
349
  }
350
350
 
351
- async function* readJsonLines(
352
- proc: ChildProcess,
353
- signal?: AbortSignal,
354
- debugTag?: string,
355
- rawLog?: RawStreamLogHandle | null,
356
- ): AsyncGenerator<CursorMessageLine> {
351
+ async function* readJsonLines(
352
+ proc: ChildProcess,
353
+ signal?: AbortSignal,
354
+ debugTag?: string,
355
+ rawLog?: RawStreamLogHandle | null,
356
+ ): AsyncGenerator<CursorMessageLine> {
357
357
  const tag = debugTag ?? "cursor";
358
358
  const rl = createInterface({ input: proc.stdout!, crlfDelay: Infinity });
359
359
  // abort 时主动 close readline,避免等待 Windows 管道自然关闭(可能延迟数分钟)
@@ -364,10 +364,10 @@ async function* readJsonLines(
364
364
  for await (const line of rl) {
365
365
  if (signal?.aborted) break;
366
366
  lineCount++;
367
- const trimmed = line.trim();
368
- if (!trimmed) continue;
369
- rawLog?.writeLine(trimmed);
370
- try {
367
+ const trimmed = line.trim();
368
+ if (!trimmed) continue;
369
+ rawLog?.writeLine(trimmed);
370
+ try {
371
371
  yield JSON.parse(trimmed) as CursorMessageLine;
372
372
  } catch { /* 非 JSON 行静默跳过 */ }
373
373
  }
@@ -432,32 +432,32 @@ class CursorAdapter implements ToolAdapter {
432
432
  cmd.mode ?? undefined,
433
433
  );
434
434
  this.activeProcs.add(proc);
435
- if (proc.pid !== undefined) options?.onProcessStart?.({ pid: proc.pid });
436
-
437
- const rawLogConfig = config.rawStreamLogs.cursor;
438
- let rawLog: RawStreamLogHandle | null = null;
439
- try {
440
- rawLog = await createRawStreamLog({
441
- enabled: rawLogConfig.enabled,
442
- rootDir: RAW_STREAM_LOGS_DIR,
443
- tool: "cursor",
444
- sessionId,
445
- label: "prompt",
446
- maxBytesPerTurn: rawLogConfig.maxBytesPerTurn,
447
- retentionDays: rawLogConfig.retentionDays,
448
- });
449
- } catch (err) {
450
- console.error(`[Cursor raw stream log] create failed: ${(err as Error).message}`);
451
- }
435
+ if (proc.pid !== undefined) options?.onProcessStart?.({ pid: proc.pid });
436
+
437
+ const rawLogConfig = config.rawStreamLogs.cursor;
438
+ let rawLog: RawStreamLogHandle | null = null;
439
+ try {
440
+ rawLog = await createRawStreamLog({
441
+ enabled: rawLogConfig.enabled,
442
+ rootDir: RAW_STREAM_LOGS_DIR,
443
+ tool: "cursor",
444
+ sessionId,
445
+ label: "prompt",
446
+ maxBytesPerTurn: rawLogConfig.maxBytesPerTurn,
447
+ retentionDays: rawLogConfig.retentionDays,
448
+ });
449
+ } catch (err) {
450
+ console.error(`[Cursor raw stream log] create failed: ${(err as Error).message}`);
451
+ }
452
452
 
453
453
  // 见 codex-adapter.ts 同位置注释:spawn 用了 shell:true,必须杀整棵树,
454
454
  // 否则 abort 后真正在跑的孙进程 cursor-agent 还会继续输出 & 占用资源。
455
- const onAbort = () => { void killProcessTree(proc.pid); };
456
- signal?.addEventListener("abort", onAbort, { once: true });
457
- let sawResult = false;
458
-
459
- try {
460
- for await (const raw of readJsonLines(proc, signal, sessionId, rawLog)) {
455
+ const onAbort = () => { void killProcessTree(proc.pid); };
456
+ signal?.addEventListener("abort", onAbort, { once: true });
457
+ let sawResult = false;
458
+
459
+ try {
460
+ for await (const raw of readJsonLines(proc, signal, sessionId, rawLog)) {
461
461
  if (signal?.aborted) break;
462
462
  if (
463
463
  raw.type === "system" &&
@@ -473,17 +473,17 @@ class CursorAdapter implements ToolAdapter {
473
473
  if (normalized) yield normalized;
474
474
 
475
475
  // result 是流末事件,收到后立即结束进程,防止 CLI 僵死导致 readline 挂起。
476
- if (raw.type === "result") {
477
- sawResult = true;
478
- void killProcessTree(proc.pid);
476
+ if (raw.type === "result") {
477
+ sawResult = true;
478
+ void killProcessTree(proc.pid);
479
479
  break;
480
480
  }
481
481
  }
482
482
  } finally {
483
483
  signal?.removeEventListener("abort", onAbort);
484
- await killProcessTree(proc.pid);
485
- await rawLog?.close({ keep: rawLogConfig.keepCompleted || signal?.aborted === true || !sawResult });
486
- this.activeProcs.delete(proc);
484
+ await killProcessTree(proc.pid);
485
+ await rawLog?.close({ keep: rawLogConfig.keepCompleted || signal?.aborted === true || !sawResult });
486
+ this.activeProcs.delete(proc);
487
487
  if (proc.pid !== undefined) options?.onProcessExit?.({ pid: proc.pid });
488
488
  console.log(`[Cursor debug] prompt end: sessionId=${sessionId}, signalAborted=${signal?.aborted ?? false}`);
489
489
  }
@@ -519,4 +519,4 @@ export function createCursorAdapter(
519
519
  options: CreateCursorAdapterOptions = {},
520
520
  ): ToolAdapter {
521
521
  return new CursorAdapter(options.metaStore ?? defaultCursorSessionMetaStore, options.model);
522
- }
522
+ }