chatccc 0.2.6 → 0.2.7
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.
- package/README.md +50 -11
- package/package.json +1 -1
- package/src/__tests__/adapter-interface.test.ts +151 -151
- package/src/__tests__/cards.test.ts +417 -413
- package/src/__tests__/claude-adapter.test.ts +528 -528
- package/src/__tests__/config.test.ts +123 -0
- package/src/__tests__/cursor-adapter.test.ts +662 -249
- package/src/__tests__/cursor-session-meta-store.test.ts +212 -0
- package/src/__tests__/fixtures/cursor_partial_only.jsonl +5 -0
- package/src/__tests__/fixtures/cursor_partial_with_final.jsonl +13 -0
- package/src/__tests__/fixtures/cursor_with_tool_call.jsonl +12 -0
- package/src/__tests__/git-command.test.ts +288 -0
- package/src/__tests__/session.test.ts +475 -296
- package/src/adapters/adapter-interface.ts +151 -126
- package/src/adapters/claude-adapter.ts +257 -257
- package/src/adapters/cursor-adapter.ts +401 -228
- package/src/adapters/cursor-session-meta-store.ts +154 -0
- package/src/cards.ts +33 -21
- package/src/config.ts +92 -2
- package/src/feishu-api.ts +1 -1
- package/src/git-command.ts +202 -0
- package/src/index.ts +58 -4
- package/src/session.ts +620 -513
|
@@ -1,258 +1,258 @@
|
|
|
1
|
-
// =============================================================================
|
|
2
|
-
// claude-adapter.ts — Claude Agent SDK 适配器
|
|
3
|
-
// =============================================================================
|
|
4
|
-
// 实现 ToolAdapter 接口,将 @anthropic-ai/claude-agent-sdk 调用封装在内。
|
|
5
|
-
// =============================================================================
|
|
6
|
-
|
|
7
|
-
import {
|
|
8
|
-
unstable_v2_createSession,
|
|
9
|
-
unstable_v2_resumeSession,
|
|
10
|
-
getSessionInfo as sdkGetSessionInfo,
|
|
11
|
-
} from "@anthropic-ai/claude-agent-sdk";
|
|
12
|
-
|
|
13
|
-
import type {
|
|
14
|
-
ToolAdapter,
|
|
15
|
-
UnifiedBlock,
|
|
16
|
-
UnifiedStreamMessage,
|
|
17
|
-
CreateSessionResult,
|
|
18
|
-
SessionInfo,
|
|
19
|
-
} from "./adapter-interface.ts";
|
|
20
|
-
|
|
21
|
-
// ---------------------------------------------------------------------------
|
|
22
|
-
// 类型别名:SDK 内部消息的形状(避免导入 sdk.d.ts 的大型联合类型)
|
|
23
|
-
// ---------------------------------------------------------------------------
|
|
24
|
-
|
|
25
|
-
interface SdkContentBlock {
|
|
26
|
-
type: string;
|
|
27
|
-
text?: string;
|
|
28
|
-
thinking?: string;
|
|
29
|
-
name?: string;
|
|
30
|
-
input?: unknown;
|
|
31
|
-
tool_use_id?: string;
|
|
32
|
-
content?: unknown;
|
|
33
|
-
is_error?: boolean;
|
|
34
|
-
query?: string;
|
|
35
|
-
[key: string]: unknown;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
interface SdkMessageLike {
|
|
39
|
-
type?: string;
|
|
40
|
-
subtype?: string;
|
|
41
|
-
message?: { content?: SdkContentBlock[] };
|
|
42
|
-
compact_metadata?: {
|
|
43
|
-
trigger?: "manual" | "auto";
|
|
44
|
-
pre_tokens?: number;
|
|
45
|
-
post_tokens?: number;
|
|
46
|
-
};
|
|
47
|
-
session_id?: string;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// ---------------------------------------------------------------------------
|
|
51
|
-
// ClaudeAdapterOptions
|
|
52
|
-
// ---------------------------------------------------------------------------
|
|
53
|
-
|
|
54
|
-
export interface ClaudeAdapterOptions {
|
|
55
|
-
model: string;
|
|
56
|
-
effort: string;
|
|
57
|
-
isDefault: (value: string) => boolean;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// ---------------------------------------------------------------------------
|
|
61
|
-
// buildSessionOptions — 还原 claudeSdkSessionOptions 的精确行为
|
|
62
|
-
// ---------------------------------------------------------------------------
|
|
63
|
-
|
|
64
|
-
function buildSessionOptions(
|
|
65
|
-
cwd: string,
|
|
66
|
-
model: string,
|
|
67
|
-
effort: string,
|
|
68
|
-
isDefault: (value: string) => boolean,
|
|
69
|
-
): Record<string, unknown> {
|
|
70
|
-
const o: Record<string, unknown> = {
|
|
71
|
-
cwd,
|
|
72
|
-
permissionMode: "bypassPermissions",
|
|
73
|
-
allowDangerouslySkipPermissions: true,
|
|
74
|
-
autoCompactEnabled: true,
|
|
75
|
-
settingSources: ["project", "local"],
|
|
76
|
-
};
|
|
77
|
-
if (!isDefault(model)) o.model = model;
|
|
78
|
-
if (!isDefault(effort)) o.effort = effort;
|
|
79
|
-
return o;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// ---------------------------------------------------------------------------
|
|
83
|
-
// normalizeSdkMessage — 关键映射:SDK 消息 → UnifiedStreamMessage | null
|
|
84
|
-
// ---------------------------------------------------------------------------
|
|
85
|
-
|
|
86
|
-
export function normalizeSdkMessage(msg: SdkMessageLike): UnifiedStreamMessage | null {
|
|
87
|
-
// 1) assistant / user 消息:遍历 content 块
|
|
88
|
-
if (
|
|
89
|
-
(msg.type === "assistant" || msg.type === "user") &&
|
|
90
|
-
msg.message?.content
|
|
91
|
-
) {
|
|
92
|
-
const blocks: UnifiedBlock[] = [];
|
|
93
|
-
for (const block of msg.message.content) {
|
|
94
|
-
if (block.type === "thinking" && block.thinking) {
|
|
95
|
-
blocks.push({ type: "thinking", thinking: block.thinking });
|
|
96
|
-
} else if (block.type === "tool_use") {
|
|
97
|
-
blocks.push({
|
|
98
|
-
type: "tool_use",
|
|
99
|
-
name: block.name ?? "unknown",
|
|
100
|
-
input: block.input,
|
|
101
|
-
});
|
|
102
|
-
} else if (block.type === "tool_result") {
|
|
103
|
-
blocks.push({
|
|
104
|
-
type: "tool_result",
|
|
105
|
-
tool_use_id: block.tool_use_id ?? "",
|
|
106
|
-
content: block.content,
|
|
107
|
-
is_error: block.is_error,
|
|
108
|
-
});
|
|
109
|
-
} else if (block.type === "redacted_thinking") {
|
|
110
|
-
blocks.push({ type: "redacted_thinking" });
|
|
111
|
-
} else if (block.type === "search_result") {
|
|
112
|
-
blocks.push({
|
|
113
|
-
type: "search_result",
|
|
114
|
-
query: block.query ?? "",
|
|
115
|
-
});
|
|
116
|
-
} else if (block.type === "text" && block.text) {
|
|
117
|
-
blocks.push({ type: "text", text: block.text });
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
return { type: msg.type, blocks };
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
// 2) system / compact_boundary 消息:上下文压缩事件
|
|
124
|
-
if (msg.type === "system" && msg.subtype === "compact_boundary") {
|
|
125
|
-
const meta = msg.compact_metadata;
|
|
126
|
-
if (!meta) return null;
|
|
127
|
-
return {
|
|
128
|
-
type: "system",
|
|
129
|
-
blocks: [
|
|
130
|
-
{
|
|
131
|
-
type: "compact_boundary",
|
|
132
|
-
trigger: meta.trigger ?? "auto",
|
|
133
|
-
pre_tokens: meta.pre_tokens ?? 0,
|
|
134
|
-
post_tokens: meta.post_tokens,
|
|
135
|
-
},
|
|
136
|
-
],
|
|
137
|
-
};
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
// 3) 其他消息类型:跳过
|
|
141
|
-
return null;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
// ---------------------------------------------------------------------------
|
|
145
|
-
// 适配器实现(私有类,仅通过工厂函数暴露)
|
|
146
|
-
// ---------------------------------------------------------------------------
|
|
147
|
-
|
|
148
|
-
class ClaudeAdapter implements ToolAdapter {
|
|
149
|
-
readonly displayName = "Claude Code";
|
|
150
|
-
readonly sessionDescPrefix = "Claude Code Session:";
|
|
151
|
-
private model: string;
|
|
152
|
-
private effort: string;
|
|
153
|
-
private isDefault: (value: string) => boolean;
|
|
154
|
-
|
|
155
|
-
constructor(options: ClaudeAdapterOptions) {
|
|
156
|
-
this.model = options.model;
|
|
157
|
-
this.effort = options.effort;
|
|
158
|
-
this.isDefault = options.isDefault;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
async createSession(cwd: string): Promise<CreateSessionResult> {
|
|
162
|
-
const sessionOpts = buildSessionOptions(
|
|
163
|
-
cwd,
|
|
164
|
-
this.model,
|
|
165
|
-
this.effort,
|
|
166
|
-
this.isDefault,
|
|
167
|
-
);
|
|
168
|
-
const session = unstable_v2_createSession(sessionOpts as any);
|
|
169
|
-
|
|
170
|
-
await session.send("ok");
|
|
171
|
-
|
|
172
|
-
const stream = session.stream();
|
|
173
|
-
const first = await stream.next();
|
|
174
|
-
|
|
175
|
-
if (first.done || !(first.value as SdkMessageLike)?.session_id) {
|
|
176
|
-
session.close();
|
|
177
|
-
throw new Error("No session ID in Claude init event");
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
const sessionId = (first.value as SdkMessageLike).session_id!;
|
|
181
|
-
|
|
182
|
-
// 后台消费剩余的 stream(必须:否则 SDK 内部缓冲可能阻塞)
|
|
183
|
-
(async () => {
|
|
184
|
-
try {
|
|
185
|
-
for await (const _msg of stream) {
|
|
186
|
-
// 静默消费
|
|
187
|
-
}
|
|
188
|
-
} catch {
|
|
189
|
-
// stream 异常不阻塞主流程
|
|
190
|
-
} finally {
|
|
191
|
-
session.close();
|
|
192
|
-
}
|
|
193
|
-
})();
|
|
194
|
-
|
|
195
|
-
return { sessionId };
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
async *prompt(
|
|
199
|
-
sessionId: string,
|
|
200
|
-
userText: string,
|
|
201
|
-
cwd: string,
|
|
202
|
-
signal?: AbortSignal,
|
|
203
|
-
): AsyncIterable<UnifiedStreamMessage> {
|
|
204
|
-
const sessionOpts = buildSessionOptions(
|
|
205
|
-
cwd,
|
|
206
|
-
this.model,
|
|
207
|
-
this.effort,
|
|
208
|
-
this.isDefault,
|
|
209
|
-
);
|
|
210
|
-
const session = unstable_v2_resumeSession(
|
|
211
|
-
sessionId,
|
|
212
|
-
sessionOpts as any,
|
|
213
|
-
);
|
|
214
|
-
|
|
215
|
-
await session.send(userText);
|
|
216
|
-
|
|
217
|
-
const stream = session.stream();
|
|
218
|
-
|
|
219
|
-
try {
|
|
220
|
-
for await (const msg of stream) {
|
|
221
|
-
if (signal?.aborted) break;
|
|
222
|
-
const normalized = normalizeSdkMessage(
|
|
223
|
-
msg as unknown as SdkMessageLike,
|
|
224
|
-
);
|
|
225
|
-
if (normalized) yield normalized;
|
|
226
|
-
}
|
|
227
|
-
} finally {
|
|
228
|
-
session.close();
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
async getSessionInfo(
|
|
233
|
-
sessionId: string,
|
|
234
|
-
): Promise<SessionInfo | undefined> {
|
|
235
|
-
const info = await sdkGetSessionInfo(sessionId);
|
|
236
|
-
if (!info) return undefined;
|
|
237
|
-
return {
|
|
238
|
-
sessionId: info.sessionId,
|
|
239
|
-
cwd: info.cwd,
|
|
240
|
-
summary: info.summary,
|
|
241
|
-
lastModified: info.lastModified,
|
|
242
|
-
};
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
async closeSession(_sessionId: string): Promise<void> {
|
|
246
|
-
// Claude SDK 在 stream 结束后自动关闭 session,此处为 no-op
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
// ---------------------------------------------------------------------------
|
|
251
|
-
// 工厂函数
|
|
252
|
-
// ---------------------------------------------------------------------------
|
|
253
|
-
|
|
254
|
-
export function createClaudeAdapter(
|
|
255
|
-
options: ClaudeAdapterOptions,
|
|
256
|
-
): ToolAdapter {
|
|
257
|
-
return new ClaudeAdapter(options);
|
|
1
|
+
// =============================================================================
|
|
2
|
+
// claude-adapter.ts — Claude Agent SDK 适配器
|
|
3
|
+
// =============================================================================
|
|
4
|
+
// 实现 ToolAdapter 接口,将 @anthropic-ai/claude-agent-sdk 调用封装在内。
|
|
5
|
+
// =============================================================================
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
unstable_v2_createSession,
|
|
9
|
+
unstable_v2_resumeSession,
|
|
10
|
+
getSessionInfo as sdkGetSessionInfo,
|
|
11
|
+
} from "@anthropic-ai/claude-agent-sdk";
|
|
12
|
+
|
|
13
|
+
import type {
|
|
14
|
+
ToolAdapter,
|
|
15
|
+
UnifiedBlock,
|
|
16
|
+
UnifiedStreamMessage,
|
|
17
|
+
CreateSessionResult,
|
|
18
|
+
SessionInfo,
|
|
19
|
+
} from "./adapter-interface.ts";
|
|
20
|
+
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
// 类型别名:SDK 内部消息的形状(避免导入 sdk.d.ts 的大型联合类型)
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
|
|
25
|
+
interface SdkContentBlock {
|
|
26
|
+
type: string;
|
|
27
|
+
text?: string;
|
|
28
|
+
thinking?: string;
|
|
29
|
+
name?: string;
|
|
30
|
+
input?: unknown;
|
|
31
|
+
tool_use_id?: string;
|
|
32
|
+
content?: unknown;
|
|
33
|
+
is_error?: boolean;
|
|
34
|
+
query?: string;
|
|
35
|
+
[key: string]: unknown;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
interface SdkMessageLike {
|
|
39
|
+
type?: string;
|
|
40
|
+
subtype?: string;
|
|
41
|
+
message?: { content?: SdkContentBlock[] };
|
|
42
|
+
compact_metadata?: {
|
|
43
|
+
trigger?: "manual" | "auto";
|
|
44
|
+
pre_tokens?: number;
|
|
45
|
+
post_tokens?: number;
|
|
46
|
+
};
|
|
47
|
+
session_id?: string;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// ---------------------------------------------------------------------------
|
|
51
|
+
// ClaudeAdapterOptions
|
|
52
|
+
// ---------------------------------------------------------------------------
|
|
53
|
+
|
|
54
|
+
export interface ClaudeAdapterOptions {
|
|
55
|
+
model: string;
|
|
56
|
+
effort: string;
|
|
57
|
+
isDefault: (value: string) => boolean;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// ---------------------------------------------------------------------------
|
|
61
|
+
// buildSessionOptions — 还原 claudeSdkSessionOptions 的精确行为
|
|
62
|
+
// ---------------------------------------------------------------------------
|
|
63
|
+
|
|
64
|
+
function buildSessionOptions(
|
|
65
|
+
cwd: string,
|
|
66
|
+
model: string,
|
|
67
|
+
effort: string,
|
|
68
|
+
isDefault: (value: string) => boolean,
|
|
69
|
+
): Record<string, unknown> {
|
|
70
|
+
const o: Record<string, unknown> = {
|
|
71
|
+
cwd,
|
|
72
|
+
permissionMode: "bypassPermissions",
|
|
73
|
+
allowDangerouslySkipPermissions: true,
|
|
74
|
+
autoCompactEnabled: true,
|
|
75
|
+
settingSources: ["project", "local"],
|
|
76
|
+
};
|
|
77
|
+
if (!isDefault(model)) o.model = model;
|
|
78
|
+
if (!isDefault(effort)) o.effort = effort;
|
|
79
|
+
return o;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// ---------------------------------------------------------------------------
|
|
83
|
+
// normalizeSdkMessage — 关键映射:SDK 消息 → UnifiedStreamMessage | null
|
|
84
|
+
// ---------------------------------------------------------------------------
|
|
85
|
+
|
|
86
|
+
export function normalizeSdkMessage(msg: SdkMessageLike): UnifiedStreamMessage | null {
|
|
87
|
+
// 1) assistant / user 消息:遍历 content 块
|
|
88
|
+
if (
|
|
89
|
+
(msg.type === "assistant" || msg.type === "user") &&
|
|
90
|
+
msg.message?.content
|
|
91
|
+
) {
|
|
92
|
+
const blocks: UnifiedBlock[] = [];
|
|
93
|
+
for (const block of msg.message.content) {
|
|
94
|
+
if (block.type === "thinking" && block.thinking) {
|
|
95
|
+
blocks.push({ type: "thinking", thinking: block.thinking });
|
|
96
|
+
} else if (block.type === "tool_use") {
|
|
97
|
+
blocks.push({
|
|
98
|
+
type: "tool_use",
|
|
99
|
+
name: block.name ?? "unknown",
|
|
100
|
+
input: block.input,
|
|
101
|
+
});
|
|
102
|
+
} else if (block.type === "tool_result") {
|
|
103
|
+
blocks.push({
|
|
104
|
+
type: "tool_result",
|
|
105
|
+
tool_use_id: block.tool_use_id ?? "",
|
|
106
|
+
content: block.content,
|
|
107
|
+
is_error: block.is_error,
|
|
108
|
+
});
|
|
109
|
+
} else if (block.type === "redacted_thinking") {
|
|
110
|
+
blocks.push({ type: "redacted_thinking" });
|
|
111
|
+
} else if (block.type === "search_result") {
|
|
112
|
+
blocks.push({
|
|
113
|
+
type: "search_result",
|
|
114
|
+
query: block.query ?? "",
|
|
115
|
+
});
|
|
116
|
+
} else if (block.type === "text" && block.text) {
|
|
117
|
+
blocks.push({ type: "text", text: block.text });
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return { type: msg.type, blocks };
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// 2) system / compact_boundary 消息:上下文压缩事件
|
|
124
|
+
if (msg.type === "system" && msg.subtype === "compact_boundary") {
|
|
125
|
+
const meta = msg.compact_metadata;
|
|
126
|
+
if (!meta) return null;
|
|
127
|
+
return {
|
|
128
|
+
type: "system",
|
|
129
|
+
blocks: [
|
|
130
|
+
{
|
|
131
|
+
type: "compact_boundary",
|
|
132
|
+
trigger: meta.trigger ?? "auto",
|
|
133
|
+
pre_tokens: meta.pre_tokens ?? 0,
|
|
134
|
+
post_tokens: meta.post_tokens,
|
|
135
|
+
},
|
|
136
|
+
],
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// 3) 其他消息类型:跳过
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// ---------------------------------------------------------------------------
|
|
145
|
+
// 适配器实现(私有类,仅通过工厂函数暴露)
|
|
146
|
+
// ---------------------------------------------------------------------------
|
|
147
|
+
|
|
148
|
+
class ClaudeAdapter implements ToolAdapter {
|
|
149
|
+
readonly displayName = "Claude Code";
|
|
150
|
+
readonly sessionDescPrefix = "Claude Code Session:";
|
|
151
|
+
private model: string;
|
|
152
|
+
private effort: string;
|
|
153
|
+
private isDefault: (value: string) => boolean;
|
|
154
|
+
|
|
155
|
+
constructor(options: ClaudeAdapterOptions) {
|
|
156
|
+
this.model = options.model;
|
|
157
|
+
this.effort = options.effort;
|
|
158
|
+
this.isDefault = options.isDefault;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
async createSession(cwd: string): Promise<CreateSessionResult> {
|
|
162
|
+
const sessionOpts = buildSessionOptions(
|
|
163
|
+
cwd,
|
|
164
|
+
this.model,
|
|
165
|
+
this.effort,
|
|
166
|
+
this.isDefault,
|
|
167
|
+
);
|
|
168
|
+
const session = unstable_v2_createSession(sessionOpts as any);
|
|
169
|
+
|
|
170
|
+
await session.send("ok");
|
|
171
|
+
|
|
172
|
+
const stream = session.stream();
|
|
173
|
+
const first = await stream.next();
|
|
174
|
+
|
|
175
|
+
if (first.done || !(first.value as SdkMessageLike)?.session_id) {
|
|
176
|
+
session.close();
|
|
177
|
+
throw new Error("No session ID in Claude init event");
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const sessionId = (first.value as SdkMessageLike).session_id!;
|
|
181
|
+
|
|
182
|
+
// 后台消费剩余的 stream(必须:否则 SDK 内部缓冲可能阻塞)
|
|
183
|
+
(async () => {
|
|
184
|
+
try {
|
|
185
|
+
for await (const _msg of stream) {
|
|
186
|
+
// 静默消费
|
|
187
|
+
}
|
|
188
|
+
} catch {
|
|
189
|
+
// stream 异常不阻塞主流程
|
|
190
|
+
} finally {
|
|
191
|
+
session.close();
|
|
192
|
+
}
|
|
193
|
+
})();
|
|
194
|
+
|
|
195
|
+
return { sessionId };
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
async *prompt(
|
|
199
|
+
sessionId: string,
|
|
200
|
+
userText: string,
|
|
201
|
+
cwd: string,
|
|
202
|
+
signal?: AbortSignal,
|
|
203
|
+
): AsyncIterable<UnifiedStreamMessage> {
|
|
204
|
+
const sessionOpts = buildSessionOptions(
|
|
205
|
+
cwd,
|
|
206
|
+
this.model,
|
|
207
|
+
this.effort,
|
|
208
|
+
this.isDefault,
|
|
209
|
+
);
|
|
210
|
+
const session = unstable_v2_resumeSession(
|
|
211
|
+
sessionId,
|
|
212
|
+
sessionOpts as any,
|
|
213
|
+
);
|
|
214
|
+
|
|
215
|
+
await session.send(userText);
|
|
216
|
+
|
|
217
|
+
const stream = session.stream();
|
|
218
|
+
|
|
219
|
+
try {
|
|
220
|
+
for await (const msg of stream) {
|
|
221
|
+
if (signal?.aborted) break;
|
|
222
|
+
const normalized = normalizeSdkMessage(
|
|
223
|
+
msg as unknown as SdkMessageLike,
|
|
224
|
+
);
|
|
225
|
+
if (normalized) yield normalized;
|
|
226
|
+
}
|
|
227
|
+
} finally {
|
|
228
|
+
session.close();
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
async getSessionInfo(
|
|
233
|
+
sessionId: string,
|
|
234
|
+
): Promise<SessionInfo | undefined> {
|
|
235
|
+
const info = await sdkGetSessionInfo(sessionId);
|
|
236
|
+
if (!info) return undefined;
|
|
237
|
+
return {
|
|
238
|
+
sessionId: info.sessionId,
|
|
239
|
+
cwd: info.cwd,
|
|
240
|
+
summary: info.summary,
|
|
241
|
+
lastModified: info.lastModified,
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
async closeSession(_sessionId: string): Promise<void> {
|
|
246
|
+
// Claude SDK 在 stream 结束后自动关闭 session,此处为 no-op
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// ---------------------------------------------------------------------------
|
|
251
|
+
// 工厂函数
|
|
252
|
+
// ---------------------------------------------------------------------------
|
|
253
|
+
|
|
254
|
+
export function createClaudeAdapter(
|
|
255
|
+
options: ClaudeAdapterOptions,
|
|
256
|
+
): ToolAdapter {
|
|
257
|
+
return new ClaudeAdapter(options);
|
|
258
258
|
}
|