chatccc 0.2.51 → 0.2.53
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 +103 -217
- package/bin/chatccc.mjs +23 -23
- package/config.sample.json +34 -30
- package/demo/ilink_echo_probe.ts +222 -222
- package/im-skills/feishu-skill/download-video.mjs +162 -162
- package/im-skills/feishu-skill/receive-send-file.md +66 -66
- package/im-skills/feishu-skill/receive-send-image.md +27 -27
- package/im-skills/feishu-skill/send-file.mjs +50 -50
- package/im-skills/feishu-skill/send-image.mjs +50 -50
- package/im-skills/feishu-skill/skill.md +10 -10
- package/package.json +59 -59
- package/src/__tests__/agent-image-rpc.test.ts +33 -33
- package/src/__tests__/agent-rpc-body.test.ts +41 -41
- package/src/__tests__/card-plain-text.test.ts +42 -0
- package/src/__tests__/claude-adapter.test.ts +54 -1
- package/src/__tests__/codex-adapter.test.ts +304 -304
- package/src/__tests__/config-reload.test.ts +58 -41
- package/src/__tests__/config-sample.test.ts +19 -0
- package/src/__tests__/crash-logging.test.ts +62 -62
- package/src/__tests__/fixtures/codex_simple_text.jsonl +4 -4
- package/src/__tests__/fixtures/codex_with_tool.jsonl +6 -6
- package/src/__tests__/im-skills.test.ts +46 -46
- package/src/__tests__/session.test.ts +95 -2
- package/src/__tests__/sim-agent.test.ts +173 -173
- package/src/__tests__/sim-platform.test.ts +76 -76
- package/src/__tests__/sim-store.test.ts +213 -213
- package/src/__tests__/stream-state.test.ts +134 -134
- package/src/__tests__/wechat-platform.test.ts +57 -0
- package/src/adapters/claude-adapter.ts +23 -2
- package/src/adapters/codex-adapter.ts +294 -294
- package/src/adapters/codex-session-meta-store.ts +130 -130
- package/src/agent-file-rpc.ts +156 -156
- package/src/agent-image-rpc.ts +152 -152
- package/src/agent-rpc-body.ts +48 -48
- package/src/card-plain-text.ts +108 -0
- package/src/cards.ts +4 -4
- package/src/config.ts +775 -742
- package/src/feishu-api.ts +6 -0
- package/src/im-skills.ts +109 -109
- package/src/index.ts +155 -788
- package/src/orchestrator.ts +1032 -0
- package/src/platform-adapter.ts +61 -0
- package/src/session-chat-binding.ts +7 -0
- package/src/session.ts +278 -113
- package/src/sim-agent.ts +167 -156
- package/src/trace.ts +50 -50
- package/src/web-ui.ts +1891 -1718
- package/src/wechat-platform.ts +517 -0
|
@@ -1,134 +1,134 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
|
|
2
|
-
import { mkdtemp, rm, readFile, readdir, writeFile } from "node:fs/promises";
|
|
3
|
-
import { tmpdir } from "node:os";
|
|
4
|
-
import { join } from "node:path";
|
|
5
|
-
|
|
6
|
-
// 在 import stream-state 之前先 mock config,让 USER_DATA_DIR 指向临时目录
|
|
7
|
-
const TEST_DATA_DIR = await mkdtemp(join(tmpdir(), "chatccc-stream-state-test-"));
|
|
8
|
-
vi.mock("../config.ts", async () => {
|
|
9
|
-
const actual = await vi.importActual<typeof import("../config.ts")>("../config.ts");
|
|
10
|
-
return {
|
|
11
|
-
...actual,
|
|
12
|
-
USER_DATA_DIR: TEST_DATA_DIR,
|
|
13
|
-
ts: () => "test-ts",
|
|
14
|
-
};
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
const {
|
|
18
|
-
writeStreamState,
|
|
19
|
-
readStreamState,
|
|
20
|
-
createEmptyStreamState,
|
|
21
|
-
STREAMS_DIR,
|
|
22
|
-
_setRenameForTest,
|
|
23
|
-
_resetRenameForTest,
|
|
24
|
-
} = await import("../stream-state.ts");
|
|
25
|
-
|
|
26
|
-
describe("writeStreamState — atomic rename", () => {
|
|
27
|
-
beforeEach(async () => {
|
|
28
|
-
// 清空测试目录
|
|
29
|
-
try {
|
|
30
|
-
const entries = await readdir(STREAMS_DIR);
|
|
31
|
-
for (const e of entries) await rm(join(STREAMS_DIR, e), { force: true });
|
|
32
|
-
} catch { /* dir not yet exist */ }
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
afterEach(async () => {
|
|
36
|
-
vi.restoreAllMocks();
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
it("writeStreamState 写完后 readStreamState 能读到完整内容", async () => {
|
|
40
|
-
const state = createEmptyStreamState("sid-1", "/tmp", "claude", 1);
|
|
41
|
-
state.accumulatedContent = "hello";
|
|
42
|
-
state.finalReply = "world";
|
|
43
|
-
|
|
44
|
-
await writeStreamState(state);
|
|
45
|
-
|
|
46
|
-
const got = await readStreamState("sid-1");
|
|
47
|
-
expect(got).not.toBeNull();
|
|
48
|
-
expect(got!.accumulatedContent).toBe("hello");
|
|
49
|
-
expect(got!.finalReply).toBe("world");
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
it("写入完成后,临时 .tmp 文件不应残留(rename 成功路径)", async () => {
|
|
53
|
-
const state = createEmptyStreamState("sid-2", "/tmp", "claude", 1);
|
|
54
|
-
await writeStreamState(state);
|
|
55
|
-
|
|
56
|
-
const entries = await readdir(STREAMS_DIR);
|
|
57
|
-
// 只应有 sid-2.json,没有 sid-2.json.tmp
|
|
58
|
-
expect(entries).toContain("sid-2.json");
|
|
59
|
-
expect(entries.some((e) => e.endsWith(".tmp"))).toBe(false);
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
it("覆盖写入时,reader 永远只读到完整 JSON(不读到半截)", async () => {
|
|
63
|
-
// 此测试验证:先写一个版本,再写第二个版本,reader 中间读取必然是某个完整版本
|
|
64
|
-
const state1 = createEmptyStreamState("sid-3", "/tmp", "claude", 1);
|
|
65
|
-
state1.accumulatedContent = "v1";
|
|
66
|
-
await writeStreamState(state1);
|
|
67
|
-
|
|
68
|
-
// 直接读原始字节,确认是完整 JSON
|
|
69
|
-
const raw1 = await readFile(join(STREAMS_DIR, "sid-3.json"), "utf-8");
|
|
70
|
-
expect(() => JSON.parse(raw1)).not.toThrow();
|
|
71
|
-
expect(JSON.parse(raw1).accumulatedContent).toBe("v1");
|
|
72
|
-
|
|
73
|
-
const state2 = createEmptyStreamState("sid-3", "/tmp", "claude", 2);
|
|
74
|
-
state2.accumulatedContent = "v2";
|
|
75
|
-
await writeStreamState(state2);
|
|
76
|
-
|
|
77
|
-
const raw2 = await readFile(join(STREAMS_DIR, "sid-3.json"), "utf-8");
|
|
78
|
-
expect(() => JSON.parse(raw2)).not.toThrow();
|
|
79
|
-
expect(JSON.parse(raw2).accumulatedContent).toBe("v2");
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
it("rename 失败时降级为直接覆盖写,不抛错 + 数据仍写入", async () => {
|
|
83
|
-
let renameCalls = 0;
|
|
84
|
-
_setRenameForTest(async () => {
|
|
85
|
-
renameCalls++;
|
|
86
|
-
throw Object.assign(new Error("EPERM mocked"), { code: "EPERM" });
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
try {
|
|
90
|
-
const state = createEmptyStreamState("sid-fallback", "/tmp", "claude", 1);
|
|
91
|
-
state.accumulatedContent = "fallback-content";
|
|
92
|
-
|
|
93
|
-
// 不应抛错
|
|
94
|
-
await expect(writeStreamState(state)).resolves.toBeUndefined();
|
|
95
|
-
|
|
96
|
-
// 数据仍应写入(降级路径走 writeFile 覆盖)
|
|
97
|
-
const got = await readStreamState("sid-fallback");
|
|
98
|
-
expect(got).not.toBeNull();
|
|
99
|
-
expect(got!.accumulatedContent).toBe("fallback-content");
|
|
100
|
-
|
|
101
|
-
expect(renameCalls).toBe(1);
|
|
102
|
-
} finally {
|
|
103
|
-
_resetRenameForTest();
|
|
104
|
-
}
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
it("readStreamState 对损坏的 JSON 返回 null(reader 兜底契约)", async () => {
|
|
108
|
-
// 直接写一段半截 JSON 到目标路径
|
|
109
|
-
const filePath = join(STREAMS_DIR, "sid-broken.json");
|
|
110
|
-
await writeFile(filePath, '{"sessionId":"sid-broken","accum', "utf-8");
|
|
111
|
-
|
|
112
|
-
const got = await readStreamState("sid-broken");
|
|
113
|
-
expect(got).toBeNull();
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
it("readStreamState 对不存在文件返回 null", async () => {
|
|
117
|
-
const got = await readStreamState("never-existed");
|
|
118
|
-
expect(got).toBeNull();
|
|
119
|
-
});
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
describe("createEmptyStreamState", () => {
|
|
123
|
-
it("生成的 state 字段正确且 status=running", () => {
|
|
124
|
-
const s = createEmptyStreamState("sid-X", "/cwd", "cursor", 5);
|
|
125
|
-
expect(s.sessionId).toBe("sid-X");
|
|
126
|
-
expect(s.cwd).toBe("/cwd");
|
|
127
|
-
expect(s.tool).toBe("cursor");
|
|
128
|
-
expect(s.turnCount).toBe(5);
|
|
129
|
-
expect(s.status).toBe("running");
|
|
130
|
-
expect(s.accumulatedContent).toBe("");
|
|
131
|
-
expect(s.finalReply).toBe("");
|
|
132
|
-
expect(s.chunkCount).toBe(0);
|
|
133
|
-
});
|
|
134
|
-
});
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
|
|
2
|
+
import { mkdtemp, rm, readFile, readdir, writeFile } from "node:fs/promises";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
|
|
6
|
+
// 在 import stream-state 之前先 mock config,让 USER_DATA_DIR 指向临时目录
|
|
7
|
+
const TEST_DATA_DIR = await mkdtemp(join(tmpdir(), "chatccc-stream-state-test-"));
|
|
8
|
+
vi.mock("../config.ts", async () => {
|
|
9
|
+
const actual = await vi.importActual<typeof import("../config.ts")>("../config.ts");
|
|
10
|
+
return {
|
|
11
|
+
...actual,
|
|
12
|
+
USER_DATA_DIR: TEST_DATA_DIR,
|
|
13
|
+
ts: () => "test-ts",
|
|
14
|
+
};
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
const {
|
|
18
|
+
writeStreamState,
|
|
19
|
+
readStreamState,
|
|
20
|
+
createEmptyStreamState,
|
|
21
|
+
STREAMS_DIR,
|
|
22
|
+
_setRenameForTest,
|
|
23
|
+
_resetRenameForTest,
|
|
24
|
+
} = await import("../stream-state.ts");
|
|
25
|
+
|
|
26
|
+
describe("writeStreamState — atomic rename", () => {
|
|
27
|
+
beforeEach(async () => {
|
|
28
|
+
// 清空测试目录
|
|
29
|
+
try {
|
|
30
|
+
const entries = await readdir(STREAMS_DIR);
|
|
31
|
+
for (const e of entries) await rm(join(STREAMS_DIR, e), { force: true });
|
|
32
|
+
} catch { /* dir not yet exist */ }
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
afterEach(async () => {
|
|
36
|
+
vi.restoreAllMocks();
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it("writeStreamState 写完后 readStreamState 能读到完整内容", async () => {
|
|
40
|
+
const state = createEmptyStreamState("sid-1", "/tmp", "claude", 1);
|
|
41
|
+
state.accumulatedContent = "hello";
|
|
42
|
+
state.finalReply = "world";
|
|
43
|
+
|
|
44
|
+
await writeStreamState(state);
|
|
45
|
+
|
|
46
|
+
const got = await readStreamState("sid-1");
|
|
47
|
+
expect(got).not.toBeNull();
|
|
48
|
+
expect(got!.accumulatedContent).toBe("hello");
|
|
49
|
+
expect(got!.finalReply).toBe("world");
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it("写入完成后,临时 .tmp 文件不应残留(rename 成功路径)", async () => {
|
|
53
|
+
const state = createEmptyStreamState("sid-2", "/tmp", "claude", 1);
|
|
54
|
+
await writeStreamState(state);
|
|
55
|
+
|
|
56
|
+
const entries = await readdir(STREAMS_DIR);
|
|
57
|
+
// 只应有 sid-2.json,没有 sid-2.json.tmp
|
|
58
|
+
expect(entries).toContain("sid-2.json");
|
|
59
|
+
expect(entries.some((e) => e.endsWith(".tmp"))).toBe(false);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it("覆盖写入时,reader 永远只读到完整 JSON(不读到半截)", async () => {
|
|
63
|
+
// 此测试验证:先写一个版本,再写第二个版本,reader 中间读取必然是某个完整版本
|
|
64
|
+
const state1 = createEmptyStreamState("sid-3", "/tmp", "claude", 1);
|
|
65
|
+
state1.accumulatedContent = "v1";
|
|
66
|
+
await writeStreamState(state1);
|
|
67
|
+
|
|
68
|
+
// 直接读原始字节,确认是完整 JSON
|
|
69
|
+
const raw1 = await readFile(join(STREAMS_DIR, "sid-3.json"), "utf-8");
|
|
70
|
+
expect(() => JSON.parse(raw1)).not.toThrow();
|
|
71
|
+
expect(JSON.parse(raw1).accumulatedContent).toBe("v1");
|
|
72
|
+
|
|
73
|
+
const state2 = createEmptyStreamState("sid-3", "/tmp", "claude", 2);
|
|
74
|
+
state2.accumulatedContent = "v2";
|
|
75
|
+
await writeStreamState(state2);
|
|
76
|
+
|
|
77
|
+
const raw2 = await readFile(join(STREAMS_DIR, "sid-3.json"), "utf-8");
|
|
78
|
+
expect(() => JSON.parse(raw2)).not.toThrow();
|
|
79
|
+
expect(JSON.parse(raw2).accumulatedContent).toBe("v2");
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it("rename 失败时降级为直接覆盖写,不抛错 + 数据仍写入", async () => {
|
|
83
|
+
let renameCalls = 0;
|
|
84
|
+
_setRenameForTest(async () => {
|
|
85
|
+
renameCalls++;
|
|
86
|
+
throw Object.assign(new Error("EPERM mocked"), { code: "EPERM" });
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
try {
|
|
90
|
+
const state = createEmptyStreamState("sid-fallback", "/tmp", "claude", 1);
|
|
91
|
+
state.accumulatedContent = "fallback-content";
|
|
92
|
+
|
|
93
|
+
// 不应抛错
|
|
94
|
+
await expect(writeStreamState(state)).resolves.toBeUndefined();
|
|
95
|
+
|
|
96
|
+
// 数据仍应写入(降级路径走 writeFile 覆盖)
|
|
97
|
+
const got = await readStreamState("sid-fallback");
|
|
98
|
+
expect(got).not.toBeNull();
|
|
99
|
+
expect(got!.accumulatedContent).toBe("fallback-content");
|
|
100
|
+
|
|
101
|
+
expect(renameCalls).toBe(1);
|
|
102
|
+
} finally {
|
|
103
|
+
_resetRenameForTest();
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it("readStreamState 对损坏的 JSON 返回 null(reader 兜底契约)", async () => {
|
|
108
|
+
// 直接写一段半截 JSON 到目标路径
|
|
109
|
+
const filePath = join(STREAMS_DIR, "sid-broken.json");
|
|
110
|
+
await writeFile(filePath, '{"sessionId":"sid-broken","accum', "utf-8");
|
|
111
|
+
|
|
112
|
+
const got = await readStreamState("sid-broken");
|
|
113
|
+
expect(got).toBeNull();
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it("readStreamState 对不存在文件返回 null", async () => {
|
|
117
|
+
const got = await readStreamState("never-existed");
|
|
118
|
+
expect(got).toBeNull();
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
describe("createEmptyStreamState", () => {
|
|
123
|
+
it("生成的 state 字段正确且 status=running", () => {
|
|
124
|
+
const s = createEmptyStreamState("sid-X", "/cwd", "cursor", 5);
|
|
125
|
+
expect(s.sessionId).toBe("sid-X");
|
|
126
|
+
expect(s.cwd).toBe("/cwd");
|
|
127
|
+
expect(s.tool).toBe("cursor");
|
|
128
|
+
expect(s.turnCount).toBe(5);
|
|
129
|
+
expect(s.status).toBe("running");
|
|
130
|
+
expect(s.accumulatedContent).toBe("");
|
|
131
|
+
expect(s.finalReply).toBe("");
|
|
132
|
+
expect(s.chunkCount).toBe(0);
|
|
133
|
+
});
|
|
134
|
+
});
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from "vitest";
|
|
2
|
+
|
|
3
|
+
import { buildHelpCard } from "../cards.ts";
|
|
4
|
+
import { createWechatAdapter } from "../wechat-platform.ts";
|
|
5
|
+
|
|
6
|
+
describe("createWechatAdapter", () => {
|
|
7
|
+
it("degrades raw cards to plain text messages", async () => {
|
|
8
|
+
const wire = {
|
|
9
|
+
push: vi.fn(async (_chatId: string, _text: string) => "msg-id"),
|
|
10
|
+
sendText: vi.fn(
|
|
11
|
+
async (_chatId: string, _text: string, _contextToken?: string) =>
|
|
12
|
+
"msg-id",
|
|
13
|
+
),
|
|
14
|
+
};
|
|
15
|
+
const log = vi.fn();
|
|
16
|
+
const platform = createWechatAdapter({
|
|
17
|
+
getWire: () => wire,
|
|
18
|
+
log,
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
const ok = await platform.sendRawCard("wx-chat-help", buildHelpCard("Hello"));
|
|
22
|
+
|
|
23
|
+
expect(ok).toBe(true);
|
|
24
|
+
expect(wire.push).toHaveBeenCalledTimes(1);
|
|
25
|
+
expect(wire.sendText).not.toHaveBeenCalled();
|
|
26
|
+
const [, text] = wire.push.mock.calls[0];
|
|
27
|
+
expect(text).toContain("# ChatCCC");
|
|
28
|
+
expect(text).toContain("Hello");
|
|
29
|
+
expect(text).toContain("/new");
|
|
30
|
+
expect(log).toHaveBeenCalledWith("[WECHAT] sendRawCard degraded to text");
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it("reports unsent non-final messages after the claw limit", async () => {
|
|
34
|
+
const wire = {
|
|
35
|
+
push: vi.fn(async (_chatId: string, _text: string) => "msg-id"),
|
|
36
|
+
sendText: vi.fn(
|
|
37
|
+
async (_chatId: string, _text: string, _contextToken?: string) =>
|
|
38
|
+
"msg-id",
|
|
39
|
+
),
|
|
40
|
+
};
|
|
41
|
+
const log = vi.fn();
|
|
42
|
+
const platform = createWechatAdapter({
|
|
43
|
+
getWire: () => wire,
|
|
44
|
+
log,
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
for (let i = 0; i < 10; i++) {
|
|
48
|
+
await expect(platform.sendText("wx-chat-limit", `chunk ${i}`)).resolves.toBe(true);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
await expect(platform.sendText("wx-chat-limit", "chunk 10")).resolves.toBe(false);
|
|
52
|
+
expect(wire.push).toHaveBeenCalledTimes(10);
|
|
53
|
+
expect(log).toHaveBeenCalledWith(
|
|
54
|
+
"[WECHAT] sendText skipped (claw limit): chatId=wx-chat-limit count=11",
|
|
55
|
+
);
|
|
56
|
+
});
|
|
57
|
+
});
|
|
@@ -89,11 +89,32 @@ function buildSdkEnv(
|
|
|
89
89
|
if (!apiKeyTrim && !baseUrlTrim) return undefined;
|
|
90
90
|
|
|
91
91
|
const env: Record<string, string | undefined> = { ...process.env };
|
|
92
|
+
// ChatCCC's Claude API config is authoritative when present. Remove Claude
|
|
93
|
+
// Code/user settings env that can silently override gateway/auth/model choice.
|
|
94
|
+
delete env.ANTHROPIC_AUTH_TOKEN;
|
|
95
|
+
delete env.CLAUDE_CODE_OAUTH_TOKEN;
|
|
96
|
+
delete env.ANTHROPIC_MODEL;
|
|
97
|
+
delete env.ANTHROPIC_DEFAULT_OPUS_MODEL;
|
|
98
|
+
delete env.ANTHROPIC_DEFAULT_SONNET_MODEL;
|
|
99
|
+
delete env.ANTHROPIC_DEFAULT_HAIKU_MODEL;
|
|
100
|
+
delete env.CLAUDE_CODE_SUBAGENT_MODEL;
|
|
101
|
+
delete env.CLAUDE_CODE_EFFORT_LEVEL;
|
|
102
|
+
|
|
92
103
|
if (apiKeyTrim) env.ANTHROPIC_API_KEY = apiKeyTrim;
|
|
93
104
|
if (baseUrlTrim) env.ANTHROPIC_BASE_URL = baseUrlTrim;
|
|
105
|
+
else delete env.ANTHROPIC_BASE_URL;
|
|
94
106
|
return env;
|
|
95
107
|
}
|
|
96
108
|
|
|
109
|
+
function resolveSettingSources(
|
|
110
|
+
_apiKey: string | undefined,
|
|
111
|
+
_baseUrl: string | undefined,
|
|
112
|
+
): Array<"project" | "local"> {
|
|
113
|
+
// CLAUDE.md / CLAUDE.local.md 是 Agent 指令文件,与 API 来源无关,
|
|
114
|
+
// 无论使用官方 Anthropic 还是第三方网关都应加载。
|
|
115
|
+
return ["project", "local"];
|
|
116
|
+
}
|
|
117
|
+
|
|
97
118
|
// ---------------------------------------------------------------------------
|
|
98
119
|
// buildSessionOptions — 还原 claudeSdkSessionOptions 的精确行为
|
|
99
120
|
// ---------------------------------------------------------------------------
|
|
@@ -111,7 +132,7 @@ function buildSessionOptions(
|
|
|
111
132
|
permissionMode: "bypassPermissions",
|
|
112
133
|
allowDangerouslySkipPermissions: true,
|
|
113
134
|
autoCompactEnabled: true,
|
|
114
|
-
settingSources:
|
|
135
|
+
settingSources: resolveSettingSources(apiKey, baseUrl),
|
|
115
136
|
};
|
|
116
137
|
if (!isEmpty(model)) o.model = model;
|
|
117
138
|
if (!isEmpty(effort)) o.effort = effort;
|
|
@@ -304,4 +325,4 @@ export function createClaudeAdapter(
|
|
|
304
325
|
options: ClaudeAdapterOptions,
|
|
305
326
|
): ToolAdapter {
|
|
306
327
|
return new ClaudeAdapter(options);
|
|
307
|
-
}
|
|
328
|
+
}
|