pi-codebuddy-sdk 0.1.17 → 0.1.19
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 +12 -15
- package/package.json +4 -3
- package/src/agents-md.ts +39 -0
- package/src/askcodebuddy-ui.ts +90 -0
- package/src/config.ts +46 -0
- package/src/convert.ts +105 -0
- package/src/index.ts +1784 -92
- package/src/models.ts +19 -0
- package/src/query-state.ts +80 -0
- package/src/skills.ts +21 -0
- package/src/provider.ts +0 -289
package/README.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# pi-codebuddy-sdk
|
|
2
2
|
|
|
3
|
+
Forked from [pi-claude-bridge](https://github.com/elidickinson/pi-claude-bridge). Replaces Claude Agent SDK with CodeBuddy Agent SDK.
|
|
4
|
+
|
|
3
5
|
回退到 `@tencent-ai/agent-sdk` 的 Pi provider 扩展。无反向代理,直接使用本地 CodeBuddy CLI,合规安全。
|
|
4
6
|
|
|
5
7
|
## 安装
|
|
@@ -7,8 +9,8 @@
|
|
|
7
9
|
### 方式 1:全局安装(日常使用)
|
|
8
10
|
|
|
9
11
|
```bash
|
|
10
|
-
ln -s ~/Desktop/pi-codebuddy/src ~/.pi/agent/extensions/pi-codebuddy
|
|
11
|
-
cd ~/Desktop/pi-codebuddy && npm install
|
|
12
|
+
ln -s ~/Desktop/usaslahser/pi-codebuddy-sdk/src ~/.pi/agent/extensions/pi-codebuddy-sdk
|
|
13
|
+
cd ~/Desktop/usaslahser/pi-codebuddy-sdk && npm install
|
|
12
14
|
```
|
|
13
15
|
|
|
14
16
|
重启 Pi 即可自动发现。
|
|
@@ -16,7 +18,7 @@ cd ~/Desktop/pi-codebuddy && npm install
|
|
|
16
18
|
### 方式 2:临时加载(测试)
|
|
17
19
|
|
|
18
20
|
```bash
|
|
19
|
-
pi -e ~/Desktop/pi-codebuddy/src/index.ts --provider codebuddy --model <model>
|
|
21
|
+
pi -e ~/Desktop/usaslahser/pi-codebuddy-sdk/src/index.ts --provider codebuddy --model <model>
|
|
20
22
|
```
|
|
21
23
|
|
|
22
24
|
## 认证
|
|
@@ -71,32 +73,27 @@ pi --provider codebuddy --model claude-sonnet-4.6
|
|
|
71
73
|
| 模型 | 说明 |
|
|
72
74
|
|------|------|
|
|
73
75
|
| `claude-sonnet-4.6` | Claude Sonnet 4.6,支持 thinking |
|
|
74
|
-
| `claude-opus-4.8` | Claude Opus 4.8,支持 thinking + image |
|
|
75
|
-
| `gemini-3.1-pro` | Gemini 3.1 Pro,支持 thinking + image |
|
|
76
|
-
| `gpt-5.5` | GPT-5.5,支持 thinking + image |
|
|
77
76
|
| `hy3-preview-agent-ioa` | 混元 3 Preview,iOA 内免费无限额度 |
|
|
77
|
+
| `deepseek-v4-pro-ioa` | DeepSeek V4 Pro |
|
|
78
|
+
| `glm-5.2-ioa` | GLM 5.2 |
|
|
78
79
|
|
|
79
80
|
## 特性
|
|
80
81
|
|
|
81
82
|
| 特性 | 说明 |
|
|
82
83
|
|------|------|
|
|
83
|
-
| **动态模型发现** | 启动时从 CodeBuddy SDK
|
|
84
|
-
| **Session 池复用** | 多轮对话复用同一 CodeBuddy CLI 进程,首轮后每轮省 ~6s 启动 |
|
|
84
|
+
| **动态模型发现** | 启动时从 CodeBuddy SDK 拉取模型,自动标注 thinking/image 能力 |
|
|
85
85
|
| **Thinking 控制** | Pi 的 thinking level 映射到 CodeBuddy 的 thinking/effort 参数 |
|
|
86
86
|
| **Image 输入** | 原生支持 Claude/Gemini/GPT 的多模态图片输入 |
|
|
87
87
|
| **Abort 处理** | Esc 取消 → CodeBuddy `interrupt()`,干净终止 |
|
|
88
88
|
| **Token 统计** | 包含 cache_read / cache_write / total_cost_usd |
|
|
89
89
|
| **YOLO 模式** | `bypassPermissions`,所有工具自动批准(与 pi-cursor-sdk 一致) |
|
|
90
|
+
| **Stateless** | 每次调用独立 query(),无 session 复用,简单可靠 |
|
|
90
91
|
|
|
91
92
|
## FAQ
|
|
92
93
|
|
|
93
|
-
**Q: 和 pi-
|
|
94
|
-
|
|
95
|
-
pi-cursor-sdk 需要自建 MCP bridge 桥接 Cursor SDK 工具,pi-codebuddy SDK 原生处理工具执行,代码量减少 60%+。
|
|
96
|
-
|
|
97
|
-
**Q: 怎么查看当前用了多少 tokens?**
|
|
94
|
+
**Q: 和 pi-claude-bridge 有什么区别?**
|
|
98
95
|
|
|
99
|
-
|
|
96
|
+
pi-claude-bridge 使用 Anthropic Claude Agent SDK + MCP bridge,需要 session 管理和复杂的工具桥接。pi-codebuddy-sdk 使用腾讯 CodeBuddy Agent SDK,原生工具执行,代码量减少 60%+。
|
|
100
97
|
|
|
101
98
|
**Q: maxTurns 限制?**
|
|
102
99
|
|
|
@@ -104,4 +101,4 @@ pi-cursor-sdk 需要自建 MCP bridge 桥接 Cursor SDK 工具,pi-codebuddy SD
|
|
|
104
101
|
|
|
105
102
|
**Q: 支持哪些 CodeBuddy 版本?**
|
|
106
103
|
|
|
107
|
-
CodeBuddy CLI
|
|
104
|
+
CodeBuddy CLI >= 2.x(`@tencent-ai/agent-sdk` >= 0.3.0)。
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-codebuddy-sdk",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "CodeBuddy Agent SDK provider for Pi — native tool execution,
|
|
3
|
+
"version": "0.1.19",
|
|
4
|
+
"description": "CodeBuddy Agent SDK provider for Pi — native tool execution, thinking controls, stateless one-shot queries",
|
|
5
5
|
"author": "slahser",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"type": "module",
|
|
@@ -44,6 +44,7 @@
|
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
46
|
"@earendil-works/pi-ai": "^0.80.2",
|
|
47
|
-
"@earendil-works/pi-coding-agent": "^0.80.2"
|
|
47
|
+
"@earendil-works/pi-coding-agent": "^0.80.2",
|
|
48
|
+
"@earendil-works/pi-tui": "^0.80.2"
|
|
48
49
|
}
|
|
49
50
|
}
|
package/src/agents-md.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// AGENTS.md discovery for forwarding to CodeBuddy.
|
|
2
|
+
|
|
3
|
+
import { existsSync, readFileSync } from "fs";
|
|
4
|
+
import { homedir } from "os";
|
|
5
|
+
import { dirname, join, resolve } from "path";
|
|
6
|
+
|
|
7
|
+
const GLOBAL_AGENTS_PATH = join(homedir(), ".pi", "agent", "AGENTS.md");
|
|
8
|
+
|
|
9
|
+
export function resolveAgentsMdPath(): string | undefined {
|
|
10
|
+
const fromCwd = findAgentsMdInParents(process.cwd());
|
|
11
|
+
if (fromCwd) return fromCwd;
|
|
12
|
+
if (existsSync(GLOBAL_AGENTS_PATH)) return GLOBAL_AGENTS_PATH;
|
|
13
|
+
return undefined;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function findAgentsMdInParents(startDir: string): string | undefined {
|
|
17
|
+
let current = resolve(startDir);
|
|
18
|
+
while (true) {
|
|
19
|
+
const candidate = join(current, "AGENTS.md");
|
|
20
|
+
if (existsSync(candidate)) return candidate;
|
|
21
|
+
const parent = dirname(current);
|
|
22
|
+
if (parent === current) break;
|
|
23
|
+
current = parent;
|
|
24
|
+
}
|
|
25
|
+
return undefined;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function extractAgentsAppend(): string | undefined {
|
|
29
|
+
const agentsPath = resolveAgentsMdPath();
|
|
30
|
+
if (!agentsPath) return undefined;
|
|
31
|
+
try {
|
|
32
|
+
const content = readFileSync(agentsPath, "utf-8").trim();
|
|
33
|
+
if (!content) return undefined;
|
|
34
|
+
const sanitized = content; // keep as-is for CodeBuddy
|
|
35
|
+
return sanitized.length > 0 ? `# CODEBUDDY.md\n\n${sanitized}` : undefined;
|
|
36
|
+
} catch {
|
|
37
|
+
return undefined;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
// Status-line rendering helpers for the AskClaude tool.
|
|
2
|
+
//
|
|
3
|
+
// While Claude Code runs inside an AskClaude call, the pi TUI can't surface
|
|
4
|
+
// each tool_use individually — there's only one status row for the whole
|
|
5
|
+
// delegation. These helpers shape a tool_use record into a short, path-aware
|
|
6
|
+
// label (e.g. "Read(src/foo.ts)", "Bash(git log --oneline…)") and collapse
|
|
7
|
+
// runs of the same tool so the line doesn't flicker. Used only by
|
|
8
|
+
// promptAndWait; the provider path exposes tools directly through pi's TUI
|
|
9
|
+
// and doesn't need this.
|
|
10
|
+
|
|
11
|
+
export interface ToolCallState {
|
|
12
|
+
name: string;
|
|
13
|
+
status: string;
|
|
14
|
+
rawInput?: unknown;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function extractPath(rawInput: unknown): string | undefined {
|
|
18
|
+
if (!rawInput || typeof rawInput !== "object") return undefined;
|
|
19
|
+
const input = rawInput as Record<string, unknown>;
|
|
20
|
+
if (typeof input.file_path === "string") return input.file_path;
|
|
21
|
+
if (typeof input.path === "string") return input.path;
|
|
22
|
+
if (typeof input.command === "string") return input.command.substring(0, 80);
|
|
23
|
+
return undefined;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function shortPath(p: string): string {
|
|
27
|
+
const cwd = process.cwd();
|
|
28
|
+
if (p.startsWith(cwd + "/")) return p.slice(cwd.length + 1);
|
|
29
|
+
if (p.startsWith("/")) {
|
|
30
|
+
const parts = p.split("/");
|
|
31
|
+
if (parts.length > 3) return parts.slice(-2).join("/");
|
|
32
|
+
}
|
|
33
|
+
return p;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function formatToolAction(tc: ToolCallState): string | undefined {
|
|
37
|
+
const path = extractPath(tc.rawInput);
|
|
38
|
+
const verb = tc.name.toLowerCase().split(/\s/)[0];
|
|
39
|
+
if (verb === "read" || verb === "readfile") {
|
|
40
|
+
return path ? `Read(${shortPath(path)})` : "Read";
|
|
41
|
+
} else if (verb === "glob") {
|
|
42
|
+
const input = tc.rawInput as Record<string, unknown> | undefined;
|
|
43
|
+
const pat = typeof input?.pattern === "string" ? input.pattern.slice(0, 40) : "";
|
|
44
|
+
return pat ? `Glob(${pat})` : "Glob";
|
|
45
|
+
} else if (verb === "edit" || verb === "write" || verb === "writefile" || verb === "multiedit") {
|
|
46
|
+
return path ? `Edit(${shortPath(path)})` : "Edit";
|
|
47
|
+
} else if (verb === "bashoutput") {
|
|
48
|
+
return undefined; // redundant with preceding Bash call
|
|
49
|
+
} else if (verb === "bash" || verb === "terminal") {
|
|
50
|
+
return path ? `Bash(${path})` : "Bash";
|
|
51
|
+
} else if (verb === "agent") {
|
|
52
|
+
const input = tc.rawInput as Record<string, unknown> | undefined;
|
|
53
|
+
return `Agent(${String(input?.description ?? "").slice(0, 40)})`;
|
|
54
|
+
} else if (verb === "grep") {
|
|
55
|
+
const input = tc.rawInput as Record<string, unknown> | undefined;
|
|
56
|
+
const pat = typeof input?.pattern === "string" ? input.pattern.slice(0, 40) : "";
|
|
57
|
+
return pat ? `Grep(${pat})` : "Grep";
|
|
58
|
+
} else if (verb === "skill") {
|
|
59
|
+
const input = tc.rawInput as Record<string, unknown> | undefined;
|
|
60
|
+
const name = typeof input?.skill === "string" ? input.skill.slice(0, 40) : "";
|
|
61
|
+
return name ? `Skill(${name})` : "Skill";
|
|
62
|
+
} else if (verb === "todowrite" || verb === "taskcreate" || verb === "taskupdate") {
|
|
63
|
+
const todos = Array.isArray((tc.rawInput as any)?.todos) ? (tc.rawInput as any).todos : [];
|
|
64
|
+
const current = todos.find((t: any) => t.status === "in_progress") ?? todos.find((t: any) => t.status === "pending");
|
|
65
|
+
const label = current ? String(current.content ?? "").slice(0, 40) : "";
|
|
66
|
+
return label || undefined;
|
|
67
|
+
} else if (verb === "askclaude") {
|
|
68
|
+
// Recursive — don't show AskClaude in its own action summary
|
|
69
|
+
return undefined;
|
|
70
|
+
}
|
|
71
|
+
return tc.name;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function buildActionSummary(calls: Map<string, ToolCallState>): string {
|
|
75
|
+
const parts: string[] = [];
|
|
76
|
+
let prevVerb = "";
|
|
77
|
+
for (const [, tc] of calls) {
|
|
78
|
+
const action = formatToolAction(tc);
|
|
79
|
+
if (!action) continue;
|
|
80
|
+
const verb = tc.name.toLowerCase().split(/\s/)[0];
|
|
81
|
+
// Collapse consecutive calls to the same tool — keep only the latest
|
|
82
|
+
if (verb === prevVerb) {
|
|
83
|
+
parts[parts.length - 1] = action;
|
|
84
|
+
} else {
|
|
85
|
+
parts.push(action);
|
|
86
|
+
}
|
|
87
|
+
prevVerb = verb;
|
|
88
|
+
}
|
|
89
|
+
return parts.join("; ");
|
|
90
|
+
}
|
package/src/config.ts
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// User-facing extension config. Loaded once at extension registration from
|
|
2
|
+
// ~/.pi/agent/codebuddy-sdk.json and the project Pi config directory, project
|
|
3
|
+
// overriding global. Missing or unparseable files are ignored.
|
|
4
|
+
|
|
5
|
+
import { CONFIG_DIR_NAME } from "@earendil-works/pi-coding-agent";
|
|
6
|
+
import { existsSync, readFileSync } from "fs";
|
|
7
|
+
import { homedir } from "os";
|
|
8
|
+
import { join } from "path";
|
|
9
|
+
|
|
10
|
+
export interface Config {
|
|
11
|
+
askCodebuddy?: {
|
|
12
|
+
enabled?: boolean;
|
|
13
|
+
name?: string;
|
|
14
|
+
label?: string;
|
|
15
|
+
description?: string;
|
|
16
|
+
defaultMode?: "full" | "read" | "none";
|
|
17
|
+
defaultIsolated?: boolean;
|
|
18
|
+
allowFullMode?: boolean;
|
|
19
|
+
appendSkills?: boolean;
|
|
20
|
+
};
|
|
21
|
+
provider?: {
|
|
22
|
+
appendSystemPrompt?: boolean;
|
|
23
|
+
pathToCodebuddyExecutable?: string;
|
|
24
|
+
plan?: "pro" | "max";
|
|
25
|
+
longContextExtraUsage?: boolean;
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function tryParseJson(path: string): Partial<Config> {
|
|
30
|
+
if (!existsSync(path)) return {};
|
|
31
|
+
try {
|
|
32
|
+
return JSON.parse(readFileSync(path, "utf-8"));
|
|
33
|
+
} catch (e) {
|
|
34
|
+
console.error(`codebuddy-sdk: failed to parse ${path}: ${e}`);
|
|
35
|
+
return {};
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function loadConfig(cwd: string): Config {
|
|
40
|
+
const global = tryParseJson(join(homedir(), ".pi", "agent", "codebuddy-sdk.json"));
|
|
41
|
+
const project = tryParseJson(join(cwd, CONFIG_DIR_NAME, "codebuddy-sdk.json"));
|
|
42
|
+
return {
|
|
43
|
+
askCodebuddy: { ...global.askCodebuddy, ...project.askCodebuddy },
|
|
44
|
+
provider: { ...global.provider, ...project.provider },
|
|
45
|
+
};
|
|
46
|
+
}
|
package/src/convert.ts
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
// Pure pi -> SDK message conversion helpers.
|
|
2
|
+
|
|
3
|
+
import type { Message as PiMessage } from "@earendil-works/pi-ai";
|
|
4
|
+
|
|
5
|
+
export const PROVIDER_ID = "codebuddy";
|
|
6
|
+
|
|
7
|
+
export const PI_TO_SDK_TOOL_NAME: Record<string, string> = {
|
|
8
|
+
read: "Read", write: "Write", edit: "Edit", bash: "Bash",
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export function sanitizeToolId(id: string, cache: Map<string, string>): string {
|
|
12
|
+
const existing = cache.get(id);
|
|
13
|
+
if (existing) return existing;
|
|
14
|
+
const clean = id.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
15
|
+
cache.set(id, clean);
|
|
16
|
+
return clean;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function mapPiToolNameToSdk(name: string, customToolNameToSdk?: Map<string, string>): string {
|
|
20
|
+
if (!name) return "";
|
|
21
|
+
const normalized = name.toLowerCase();
|
|
22
|
+
if (customToolNameToSdk) {
|
|
23
|
+
const mapped = customToolNameToSdk.get(name) ?? customToolNameToSdk.get(normalized);
|
|
24
|
+
if (mapped) return mapped;
|
|
25
|
+
}
|
|
26
|
+
if (PI_TO_SDK_TOOL_NAME[normalized]) return PI_TO_SDK_TOOL_NAME[normalized];
|
|
27
|
+
// ponytail: simple PascalCase fallback instead of change-case dep
|
|
28
|
+
return name.charAt(0).toUpperCase() + name.slice(1);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function messageContentToText(
|
|
32
|
+
content: string | Array<{ type: string; text?: string; data?: string; mimeType?: string }>,
|
|
33
|
+
): string {
|
|
34
|
+
if (typeof content === "string") return content;
|
|
35
|
+
if (!Array.isArray(content)) return "";
|
|
36
|
+
const parts = [];
|
|
37
|
+
let hasText = false;
|
|
38
|
+
for (const block of content) {
|
|
39
|
+
if (block.type === "text" && block.text) { parts.push(block.text); hasText = true; }
|
|
40
|
+
else if (block.type !== "text" && block.type !== "image") { parts.push(`[${block.type}]`); }
|
|
41
|
+
}
|
|
42
|
+
return hasText ? parts.join("\n") : "";
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// SessionMessage is an internal type used by the provider's session persistence.
|
|
46
|
+
// It mirrors the Anthropic API message format.
|
|
47
|
+
export interface SessionMessage {
|
|
48
|
+
role: "user" | "assistant";
|
|
49
|
+
content: string | Array<Record<string, unknown>>;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Convert pi message array to SDK-compatible format. */
|
|
53
|
+
export function convertPiMessages(
|
|
54
|
+
messages: PiMessage[],
|
|
55
|
+
customToolNameToSdk?: Map<string, string>,
|
|
56
|
+
): { anthropicMessages: SessionMessage[]; sanitizedIds: Map<string, string> } {
|
|
57
|
+
const anthropicMessages: SessionMessage[] = [];
|
|
58
|
+
const sanitizedIds = new Map<string, string>();
|
|
59
|
+
|
|
60
|
+
for (const msg of messages) {
|
|
61
|
+
if (msg.role === "user") {
|
|
62
|
+
if (typeof msg.content === "string") {
|
|
63
|
+
anthropicMessages.push({ role: "user", content: msg.content || "[empty]" });
|
|
64
|
+
} else if (Array.isArray(msg.content)) {
|
|
65
|
+
const parts: Array<Record<string, unknown>> = [];
|
|
66
|
+
for (const block of msg.content) {
|
|
67
|
+
if (block.type === "text" && block.text) parts.push({ type: "text", text: block.text });
|
|
68
|
+
else if (block.type === "image" && (block as any).data && (block as any).mimeType) {
|
|
69
|
+
parts.push({ type: "image", source: { type: "base64", media_type: (block as any).mimeType, data: (block as any).data } });
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
anthropicMessages.push({ role: "user", content: parts.length ? parts : "[image]" });
|
|
73
|
+
} else {
|
|
74
|
+
anthropicMessages.push({ role: "user", content: "[empty]" });
|
|
75
|
+
}
|
|
76
|
+
} else if (msg.role === "assistant") {
|
|
77
|
+
const content = Array.isArray(msg.content) ? msg.content : [];
|
|
78
|
+
const blocks: Array<Record<string, unknown>> = [];
|
|
79
|
+
for (const block of content) {
|
|
80
|
+
if (block.type === "text" && block.text) {
|
|
81
|
+
blocks.push({ type: "text", text: block.text });
|
|
82
|
+
} else if (block.type === "thinking") {
|
|
83
|
+
const sig = (block as any).thinkingSignature;
|
|
84
|
+
const isCodebuddyProvider = msg.provider === PROVIDER_ID || msg.api === "codebuddy-sdk";
|
|
85
|
+
if (isCodebuddyProvider && sig) {
|
|
86
|
+
blocks.push({ type: "thinking", thinking: block.thinking ?? "", signature: sig });
|
|
87
|
+
}
|
|
88
|
+
} else if (block.type === "toolCall") {
|
|
89
|
+
const toolName = mapPiToolNameToSdk((block as any).name, customToolNameToSdk);
|
|
90
|
+
blocks.push({ type: "tool_use", id: sanitizeToolId((block as any).id, sanitizedIds), name: toolName, input: (block as any).arguments ?? {} });
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
if (!blocks.length) blocks.push({ type: "text", text: "[incompatible content omitted]" });
|
|
94
|
+
anthropicMessages.push({ role: "assistant", content: blocks });
|
|
95
|
+
} else if (msg.role === "toolResult") {
|
|
96
|
+
const text = typeof msg.content === "string" ? msg.content : messageContentToText(msg.content);
|
|
97
|
+
anthropicMessages.push({
|
|
98
|
+
role: "user",
|
|
99
|
+
content: [{ type: "tool_result", tool_use_id: sanitizeToolId((msg as any).toolCallId, sanitizedIds), content: text || "", is_error: (msg as any).isError }],
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return { anthropicMessages, sanitizedIds };
|
|
105
|
+
}
|