feat: add AgentRuntime abstraction for Claude Agent SDK Support
* added AgentRuntime interface and implementation for PiAgent and ClaudeCode SDK (ccsdk) * support for Claude Code Max and z.AI Coding Plan subscriptions (which are limited to Claude Code) * tool bridge to ensure that the Claude Code SDK agent loop is capable of invoking the same tools as pi-agent I used Claude Opus 4.5 to help craft this PR.
This commit is contained in:
parent
7800194f33
commit
0a2e444b21
@ -155,6 +155,7 @@
|
||||
"packageManager": "pnpm@10.23.0",
|
||||
"dependencies": {
|
||||
"@agentclientprotocol/sdk": "0.13.1",
|
||||
"@anthropic-ai/claude-agent-sdk": "^0.2.22",
|
||||
"@aws-sdk/client-bedrock": "^3.975.0",
|
||||
"@buape/carbon": "0.14.0",
|
||||
"@clack/prompts": "^0.11.0",
|
||||
@ -250,7 +251,11 @@
|
||||
"@sinclair/typebox": "0.34.47",
|
||||
"hono": "4.11.4",
|
||||
"tar": "7.5.4"
|
||||
}
|
||||
},
|
||||
"ignoredBuiltDependencies": [
|
||||
"core-js",
|
||||
"node-llama-cpp"
|
||||
]
|
||||
},
|
||||
"vitest": {
|
||||
"coverage": {
|
||||
|
||||
253
pnpm-lock.yaml
generated
253
pnpm-lock.yaml
generated
@ -16,6 +16,9 @@ importers:
|
||||
'@agentclientprotocol/sdk':
|
||||
specifier: 0.13.1
|
||||
version: 0.13.1(zod@4.3.6)
|
||||
'@anthropic-ai/claude-agent-sdk':
|
||||
specifier: ^0.2.22
|
||||
version: 0.2.22(zod@4.3.6)
|
||||
'@aws-sdk/client-bedrock':
|
||||
specifier: ^3.975.0
|
||||
version: 3.975.0
|
||||
@ -383,12 +386,12 @@ importers:
|
||||
'@microsoft/agents-hosting-extensions-teams':
|
||||
specifier: ^1.2.2
|
||||
version: 1.2.2
|
||||
moltbot:
|
||||
specifier: workspace:*
|
||||
version: link:../..
|
||||
express:
|
||||
specifier: ^5.2.1
|
||||
version: 5.2.1
|
||||
moltbot:
|
||||
specifier: workspace:*
|
||||
version: link:../..
|
||||
proper-lockfile:
|
||||
specifier: ^4.1.2
|
||||
version: 4.1.2
|
||||
@ -519,6 +522,12 @@ packages:
|
||||
peerDependencies:
|
||||
zod: ^3.25.0 || ^4.0.0
|
||||
|
||||
'@anthropic-ai/claude-agent-sdk@0.2.22':
|
||||
resolution: {integrity: sha512-AO39PYe8nAfqx748UmQQ26BZAX91sOQomYFdtf5AwMwgOIH0BumrNHsHtrmgBZalsseWn84LAFfKtG5ylGR5Nw==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
peerDependencies:
|
||||
zod: ^4.0.0
|
||||
|
||||
'@anthropic-ai/sdk@0.71.2':
|
||||
resolution: {integrity: sha512-TGNDEUuEstk/DKu0/TflXAEt+p+p/WhTlFzEnoosvbaDU2LTjm42igSdlL0VijrKpWejtOKxX0b8A7uc+XiSAQ==}
|
||||
hasBin: true
|
||||
@ -1100,33 +1109,65 @@ packages:
|
||||
resolution: {integrity: sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
'@img/sharp-darwin-arm64@0.33.5':
|
||||
resolution: {integrity: sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
|
||||
'@img/sharp-darwin-arm64@0.34.5':
|
||||
resolution: {integrity: sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
|
||||
'@img/sharp-darwin-x64@0.33.5':
|
||||
resolution: {integrity: sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
|
||||
'@img/sharp-darwin-x64@0.34.5':
|
||||
resolution: {integrity: sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
|
||||
'@img/sharp-libvips-darwin-arm64@1.0.4':
|
||||
resolution: {integrity: sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
|
||||
'@img/sharp-libvips-darwin-arm64@1.2.4':
|
||||
resolution: {integrity: sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
|
||||
'@img/sharp-libvips-darwin-x64@1.0.4':
|
||||
resolution: {integrity: sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
|
||||
'@img/sharp-libvips-darwin-x64@1.2.4':
|
||||
resolution: {integrity: sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
|
||||
'@img/sharp-libvips-linux-arm64@1.0.4':
|
||||
resolution: {integrity: sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
|
||||
'@img/sharp-libvips-linux-arm64@1.2.4':
|
||||
resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
|
||||
'@img/sharp-libvips-linux-arm@1.0.5':
|
||||
resolution: {integrity: sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
|
||||
'@img/sharp-libvips-linux-arm@1.2.4':
|
||||
resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==}
|
||||
cpu: [arm]
|
||||
@ -1147,27 +1188,54 @@ packages:
|
||||
cpu: [s390x]
|
||||
os: [linux]
|
||||
|
||||
'@img/sharp-libvips-linux-x64@1.0.4':
|
||||
resolution: {integrity: sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
|
||||
'@img/sharp-libvips-linux-x64@1.2.4':
|
||||
resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
|
||||
'@img/sharp-libvips-linuxmusl-arm64@1.0.4':
|
||||
resolution: {integrity: sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
|
||||
'@img/sharp-libvips-linuxmusl-arm64@1.2.4':
|
||||
resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
|
||||
'@img/sharp-libvips-linuxmusl-x64@1.0.4':
|
||||
resolution: {integrity: sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
|
||||
'@img/sharp-libvips-linuxmusl-x64@1.2.4':
|
||||
resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
|
||||
'@img/sharp-linux-arm64@0.33.5':
|
||||
resolution: {integrity: sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
|
||||
'@img/sharp-linux-arm64@0.34.5':
|
||||
resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
|
||||
'@img/sharp-linux-arm@0.33.5':
|
||||
resolution: {integrity: sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
|
||||
'@img/sharp-linux-arm@0.34.5':
|
||||
resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
@ -1192,18 +1260,36 @@ packages:
|
||||
cpu: [s390x]
|
||||
os: [linux]
|
||||
|
||||
'@img/sharp-linux-x64@0.33.5':
|
||||
resolution: {integrity: sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
|
||||
'@img/sharp-linux-x64@0.34.5':
|
||||
resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
|
||||
'@img/sharp-linuxmusl-arm64@0.33.5':
|
||||
resolution: {integrity: sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
|
||||
'@img/sharp-linuxmusl-arm64@0.34.5':
|
||||
resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
|
||||
'@img/sharp-linuxmusl-x64@0.33.5':
|
||||
resolution: {integrity: sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
|
||||
'@img/sharp-linuxmusl-x64@0.34.5':
|
||||
resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
@ -1227,6 +1313,12 @@ packages:
|
||||
cpu: [ia32]
|
||||
os: [win32]
|
||||
|
||||
'@img/sharp-win32-x64@0.33.5':
|
||||
resolution: {integrity: sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
'@img/sharp-win32-x64@0.34.5':
|
||||
resolution: {integrity: sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
@ -3214,11 +3306,6 @@ packages:
|
||||
class-variance-authority@0.7.1:
|
||||
resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==}
|
||||
|
||||
clawdbot@2026.1.24-3:
|
||||
resolution: {integrity: sha512-zt9BzhWXduq8ZZR4rfzQDurQWAgmijTTyPZCQGrn5ew6wCEwhxxEr2/NHG7IlCwcfRsKymsY4se9KMhoNz0JtQ==}
|
||||
engines: {node: '>=22.12.0'}
|
||||
hasBin: true
|
||||
|
||||
cli-cursor@5.0.0:
|
||||
resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==}
|
||||
engines: {node: '>=18'}
|
||||
@ -5582,6 +5669,19 @@ snapshots:
|
||||
dependencies:
|
||||
zod: 4.3.6
|
||||
|
||||
'@anthropic-ai/claude-agent-sdk@0.2.22(zod@4.3.6)':
|
||||
dependencies:
|
||||
zod: 4.3.6
|
||||
optionalDependencies:
|
||||
'@img/sharp-darwin-arm64': 0.33.5
|
||||
'@img/sharp-darwin-x64': 0.33.5
|
||||
'@img/sharp-linux-arm': 0.33.5
|
||||
'@img/sharp-linux-arm64': 0.33.5
|
||||
'@img/sharp-linux-x64': 0.33.5
|
||||
'@img/sharp-linuxmusl-arm64': 0.33.5
|
||||
'@img/sharp-linuxmusl-x64': 0.33.5
|
||||
'@img/sharp-win32-x64': 0.33.5
|
||||
|
||||
'@anthropic-ai/sdk@0.71.2(zod@4.3.6)':
|
||||
dependencies:
|
||||
json-schema-to-ts: 3.1.1
|
||||
@ -6706,25 +6806,47 @@ snapshots:
|
||||
|
||||
'@img/colour@1.0.0': {}
|
||||
|
||||
'@img/sharp-darwin-arm64@0.33.5':
|
||||
optionalDependencies:
|
||||
'@img/sharp-libvips-darwin-arm64': 1.0.4
|
||||
optional: true
|
||||
|
||||
'@img/sharp-darwin-arm64@0.34.5':
|
||||
optionalDependencies:
|
||||
'@img/sharp-libvips-darwin-arm64': 1.2.4
|
||||
optional: true
|
||||
|
||||
'@img/sharp-darwin-x64@0.33.5':
|
||||
optionalDependencies:
|
||||
'@img/sharp-libvips-darwin-x64': 1.0.4
|
||||
optional: true
|
||||
|
||||
'@img/sharp-darwin-x64@0.34.5':
|
||||
optionalDependencies:
|
||||
'@img/sharp-libvips-darwin-x64': 1.2.4
|
||||
optional: true
|
||||
|
||||
'@img/sharp-libvips-darwin-arm64@1.0.4':
|
||||
optional: true
|
||||
|
||||
'@img/sharp-libvips-darwin-arm64@1.2.4':
|
||||
optional: true
|
||||
|
||||
'@img/sharp-libvips-darwin-x64@1.0.4':
|
||||
optional: true
|
||||
|
||||
'@img/sharp-libvips-darwin-x64@1.2.4':
|
||||
optional: true
|
||||
|
||||
'@img/sharp-libvips-linux-arm64@1.0.4':
|
||||
optional: true
|
||||
|
||||
'@img/sharp-libvips-linux-arm64@1.2.4':
|
||||
optional: true
|
||||
|
||||
'@img/sharp-libvips-linux-arm@1.0.5':
|
||||
optional: true
|
||||
|
||||
'@img/sharp-libvips-linux-arm@1.2.4':
|
||||
optional: true
|
||||
|
||||
@ -6737,20 +6859,39 @@ snapshots:
|
||||
'@img/sharp-libvips-linux-s390x@1.2.4':
|
||||
optional: true
|
||||
|
||||
'@img/sharp-libvips-linux-x64@1.0.4':
|
||||
optional: true
|
||||
|
||||
'@img/sharp-libvips-linux-x64@1.2.4':
|
||||
optional: true
|
||||
|
||||
'@img/sharp-libvips-linuxmusl-arm64@1.0.4':
|
||||
optional: true
|
||||
|
||||
'@img/sharp-libvips-linuxmusl-arm64@1.2.4':
|
||||
optional: true
|
||||
|
||||
'@img/sharp-libvips-linuxmusl-x64@1.0.4':
|
||||
optional: true
|
||||
|
||||
'@img/sharp-libvips-linuxmusl-x64@1.2.4':
|
||||
optional: true
|
||||
|
||||
'@img/sharp-linux-arm64@0.33.5':
|
||||
optionalDependencies:
|
||||
'@img/sharp-libvips-linux-arm64': 1.0.4
|
||||
optional: true
|
||||
|
||||
'@img/sharp-linux-arm64@0.34.5':
|
||||
optionalDependencies:
|
||||
'@img/sharp-libvips-linux-arm64': 1.2.4
|
||||
optional: true
|
||||
|
||||
'@img/sharp-linux-arm@0.33.5':
|
||||
optionalDependencies:
|
||||
'@img/sharp-libvips-linux-arm': 1.0.5
|
||||
optional: true
|
||||
|
||||
'@img/sharp-linux-arm@0.34.5':
|
||||
optionalDependencies:
|
||||
'@img/sharp-libvips-linux-arm': 1.2.4
|
||||
@ -6771,16 +6912,31 @@ snapshots:
|
||||
'@img/sharp-libvips-linux-s390x': 1.2.4
|
||||
optional: true
|
||||
|
||||
'@img/sharp-linux-x64@0.33.5':
|
||||
optionalDependencies:
|
||||
'@img/sharp-libvips-linux-x64': 1.0.4
|
||||
optional: true
|
||||
|
||||
'@img/sharp-linux-x64@0.34.5':
|
||||
optionalDependencies:
|
||||
'@img/sharp-libvips-linux-x64': 1.2.4
|
||||
optional: true
|
||||
|
||||
'@img/sharp-linuxmusl-arm64@0.33.5':
|
||||
optionalDependencies:
|
||||
'@img/sharp-libvips-linuxmusl-arm64': 1.0.4
|
||||
optional: true
|
||||
|
||||
'@img/sharp-linuxmusl-arm64@0.34.5':
|
||||
optionalDependencies:
|
||||
'@img/sharp-libvips-linuxmusl-arm64': 1.2.4
|
||||
optional: true
|
||||
|
||||
'@img/sharp-linuxmusl-x64@0.33.5':
|
||||
optionalDependencies:
|
||||
'@img/sharp-libvips-linuxmusl-x64': 1.0.4
|
||||
optional: true
|
||||
|
||||
'@img/sharp-linuxmusl-x64@0.34.5':
|
||||
optionalDependencies:
|
||||
'@img/sharp-libvips-linuxmusl-x64': 1.2.4
|
||||
@ -6797,6 +6953,9 @@ snapshots:
|
||||
'@img/sharp-win32-ia32@0.34.5':
|
||||
optional: true
|
||||
|
||||
'@img/sharp-win32-x64@0.33.5':
|
||||
optional: true
|
||||
|
||||
'@img/sharp-win32-x64@0.34.5':
|
||||
optional: true
|
||||
|
||||
@ -9098,84 +9257,6 @@ snapshots:
|
||||
dependencies:
|
||||
clsx: 2.1.1
|
||||
|
||||
clawdbot@2026.1.24-3(@types/express@5.0.6)(audio-decode@2.2.3)(devtools-protocol@0.0.1561482)(typescript@5.9.3):
|
||||
dependencies:
|
||||
'@agentclientprotocol/sdk': 0.13.1(zod@4.3.6)
|
||||
'@aws-sdk/client-bedrock': 3.975.0
|
||||
'@buape/carbon': 0.14.0(hono@4.11.4)
|
||||
'@clack/prompts': 0.11.0
|
||||
'@grammyjs/runner': 2.0.3(grammy@1.39.3)
|
||||
'@grammyjs/transformer-throttler': 1.2.1(grammy@1.39.3)
|
||||
'@homebridge/ciao': 1.3.4
|
||||
'@line/bot-sdk': 10.6.0
|
||||
'@lydell/node-pty': 1.2.0-beta.3
|
||||
'@mariozechner/pi-agent-core': 0.49.3(ws@8.19.0)(zod@4.3.6)
|
||||
'@mariozechner/pi-ai': 0.49.3(ws@8.19.0)(zod@4.3.6)
|
||||
'@mariozechner/pi-coding-agent': 0.49.3(ws@8.19.0)(zod@4.3.6)
|
||||
'@mariozechner/pi-tui': 0.49.3
|
||||
'@mozilla/readability': 0.6.0
|
||||
'@sinclair/typebox': 0.34.47
|
||||
'@slack/bolt': 4.6.0(@types/express@5.0.6)
|
||||
'@slack/web-api': 7.13.0
|
||||
'@whiskeysockets/baileys': 7.0.0-rc.9(audio-decode@2.2.3)(sharp@0.34.5)
|
||||
ajv: 8.17.1
|
||||
body-parser: 2.2.2
|
||||
chalk: 5.6.2
|
||||
chokidar: 5.0.0
|
||||
chromium-bidi: 13.0.1(devtools-protocol@0.0.1561482)
|
||||
cli-highlight: 2.1.11
|
||||
commander: 14.0.2
|
||||
croner: 9.1.0
|
||||
detect-libc: 2.1.2
|
||||
discord-api-types: 0.38.37
|
||||
dotenv: 17.2.3
|
||||
express: 5.2.1
|
||||
file-type: 21.3.0
|
||||
grammy: 1.39.3
|
||||
hono: 4.11.4
|
||||
jiti: 2.6.1
|
||||
json5: 2.2.3
|
||||
jszip: 3.10.1
|
||||
linkedom: 0.18.12
|
||||
long: 5.3.2
|
||||
markdown-it: 14.1.0
|
||||
node-edge-tts: 1.2.9
|
||||
osc-progress: 0.3.0
|
||||
pdfjs-dist: 5.4.530
|
||||
playwright-core: 1.58.0
|
||||
proper-lockfile: 4.1.2
|
||||
qrcode-terminal: 0.12.0
|
||||
sharp: 0.34.5
|
||||
sqlite-vec: 0.1.7-alpha.2
|
||||
tar: 7.5.4
|
||||
tslog: 4.10.2
|
||||
undici: 7.19.0
|
||||
ws: 8.19.0
|
||||
yaml: 2.8.2
|
||||
zod: 4.3.6
|
||||
optionalDependencies:
|
||||
'@napi-rs/canvas': 0.1.88
|
||||
node-llama-cpp: 3.15.0(typescript@5.9.3)
|
||||
transitivePeerDependencies:
|
||||
- '@discordjs/opus'
|
||||
- '@modelcontextprotocol/sdk'
|
||||
- '@types/express'
|
||||
- audio-decode
|
||||
- aws-crt
|
||||
- bufferutil
|
||||
- canvas
|
||||
- debug
|
||||
- devtools-protocol
|
||||
- encoding
|
||||
- ffmpeg-static
|
||||
- jimp
|
||||
- link-preview-js
|
||||
- node-opus
|
||||
- opusscript
|
||||
- supports-color
|
||||
- typescript
|
||||
- utf-8-validate
|
||||
|
||||
cli-cursor@5.0.0:
|
||||
dependencies:
|
||||
restore-cursor: 5.1.0
|
||||
|
||||
95
src/agents/agent-runtime.ts
Normal file
95
src/agents/agent-runtime.ts
Normal file
@ -0,0 +1,95 @@
|
||||
/**
|
||||
* Agent runtime abstraction layer.
|
||||
*
|
||||
* Defines the common interface for agent execution backends (Pi Agent, Claude Agent SDK, etc.).
|
||||
*/
|
||||
|
||||
import type { ImageContent } from "@mariozechner/pi-ai";
|
||||
import type { MoltbotConfig } from "../config/config.js";
|
||||
import type { AgentStreamParams } from "../commands/agent/types.js";
|
||||
import type { ThinkLevel, VerboseLevel } from "../auto-reply/thinking.js";
|
||||
import type { SkillSnapshot } from "./skills.js";
|
||||
import type { EmbeddedPiRunResult } from "./pi-embedded-runner/types.js";
|
||||
|
||||
/** Agent runtime backend discriminant. */
|
||||
export type AgentRuntimeKind = "pi" | "ccsdk";
|
||||
|
||||
/** Result type shared by all agent runtimes. */
|
||||
export type AgentRuntimeResult = EmbeddedPiRunResult;
|
||||
|
||||
/** Streaming and event callbacks for agent runs. */
|
||||
export type AgentRuntimeCallbacks = {
|
||||
/** Called when a partial reply chunk is available. */
|
||||
onPartialReply?: (payload: { text?: string; mediaUrls?: string[] }) => void | Promise<void>;
|
||||
/** Called when the assistant message starts. */
|
||||
onAssistantMessageStart?: () => void | Promise<void>;
|
||||
/** Called for block-level reply delivery. */
|
||||
onBlockReply?: (payload: {
|
||||
text?: string;
|
||||
mediaUrls?: string[];
|
||||
audioAsVoice?: boolean;
|
||||
replyToId?: string;
|
||||
replyToTag?: boolean;
|
||||
replyToCurrent?: boolean;
|
||||
}) => void | Promise<void>;
|
||||
/** Called when block replies should be flushed. */
|
||||
onBlockReplyFlush?: () => void | Promise<void>;
|
||||
/** Called for reasoning/thinking stream events. */
|
||||
onReasoningStream?: (payload: { text?: string; mediaUrls?: string[] }) => void | Promise<void>;
|
||||
/** Called when a tool result is available. */
|
||||
onToolResult?: (payload: { text?: string; mediaUrls?: string[] }) => void | Promise<void>;
|
||||
/** Called for agent lifecycle and internal events. */
|
||||
onAgentEvent?: (evt: { stream: string; data: Record<string, unknown> }) => void;
|
||||
};
|
||||
|
||||
/** Common parameters for agent run invocations. */
|
||||
export type AgentRuntimeRunParams = {
|
||||
sessionId: string;
|
||||
sessionKey?: string;
|
||||
sessionFile: string;
|
||||
workspaceDir: string;
|
||||
agentDir?: string;
|
||||
config?: MoltbotConfig;
|
||||
skillsSnapshot?: SkillSnapshot;
|
||||
prompt: string;
|
||||
images?: ImageContent[];
|
||||
provider?: string;
|
||||
model?: string;
|
||||
authProfileId?: string;
|
||||
authProfileIdSource?: "auto" | "user";
|
||||
thinkLevel?: ThinkLevel;
|
||||
verboseLevel?: VerboseLevel;
|
||||
timeoutMs: number;
|
||||
runId: string;
|
||||
lane?: string;
|
||||
abortSignal?: AbortSignal;
|
||||
extraSystemPrompt?: string;
|
||||
streamParams?: AgentStreamParams;
|
||||
/** Messaging context for delivery routing. */
|
||||
messageChannel?: string;
|
||||
agentAccountId?: string;
|
||||
messageTo?: string;
|
||||
messageThreadId?: string | number;
|
||||
groupId?: string | null;
|
||||
groupChannel?: string | null;
|
||||
groupSpace?: string | null;
|
||||
spawnedBy?: string | null;
|
||||
currentChannelId?: string;
|
||||
currentThreadTs?: string;
|
||||
replyToMode?: "off" | "first" | "all";
|
||||
hasRepliedRef?: { value: boolean };
|
||||
} & AgentRuntimeCallbacks;
|
||||
|
||||
/**
|
||||
* Agent runtime interface.
|
||||
*
|
||||
* Defines the contract for executing agent turns across different backends.
|
||||
*/
|
||||
export interface AgentRuntime {
|
||||
/** Runtime backend discriminant. */
|
||||
readonly kind: AgentRuntimeKind;
|
||||
/** Human-readable display name for the runtime. */
|
||||
readonly displayName: string;
|
||||
/** Execute an agent turn with the given parameters. */
|
||||
run(params: AgentRuntimeRunParams): Promise<AgentRuntimeResult>;
|
||||
}
|
||||
@ -17,6 +17,8 @@ type AgentEntry = NonNullable<NonNullable<MoltbotConfig["agents"]>["list"]>[numb
|
||||
|
||||
type ResolvedAgentConfig = {
|
||||
name?: string;
|
||||
runtime?: AgentEntry["runtime"];
|
||||
ccsdk?: AgentEntry["ccsdk"];
|
||||
workspace?: string;
|
||||
agentDir?: string;
|
||||
model?: AgentEntry["model"];
|
||||
@ -97,6 +99,8 @@ export function resolveAgentConfig(
|
||||
if (!entry) return undefined;
|
||||
return {
|
||||
name: typeof entry.name === "string" ? entry.name : undefined,
|
||||
runtime: entry.runtime,
|
||||
ccsdk: entry.ccsdk,
|
||||
workspace: typeof entry.workspace === "string" ? entry.workspace : undefined,
|
||||
agentDir: typeof entry.agentDir === "string" ? entry.agentDir : undefined,
|
||||
model:
|
||||
|
||||
54
src/agents/claude-agent-sdk/index.ts
Normal file
54
src/agents/claude-agent-sdk/index.ts
Normal file
@ -0,0 +1,54 @@
|
||||
/**
|
||||
* Claude Agent SDK integration module.
|
||||
*
|
||||
* Provides an alternative agent runtime using the Claude Agent SDK.
|
||||
*
|
||||
* @module agents/claude-agent-sdk
|
||||
*/
|
||||
|
||||
export { isSdkAvailable, loadClaudeAgentSdk, resetSdkLoaderForTest } from "./sdk-loader.js";
|
||||
export {
|
||||
buildAnthropicSdkProvider,
|
||||
buildBedrockSdkProvider,
|
||||
buildClaudeCliSdkProvider,
|
||||
buildOpenRouterSdkProvider,
|
||||
buildVertexSdkProvider,
|
||||
buildZaiSdkProvider,
|
||||
resolveProviderConfig,
|
||||
} from "./provider-config.js";
|
||||
export { runSdkAgent } from "./sdk-runner.js";
|
||||
export { createCcSdkAgentRuntime, type CcSdkAgentRuntimeContext } from "./sdk-agent-runtime.js";
|
||||
export {
|
||||
bridgeMoltbotToolsToMcpServer,
|
||||
bridgeMoltbotToolsSync,
|
||||
buildMcpAllowedTools,
|
||||
convertToolResult,
|
||||
extractJsonSchema,
|
||||
mcpToolName,
|
||||
resetMcpServerCache,
|
||||
wrapToolHandler,
|
||||
type BridgeOptions,
|
||||
type BridgeResult,
|
||||
} from "./tool-bridge.js";
|
||||
export type {
|
||||
McpCallToolResult,
|
||||
McpContentBlock,
|
||||
McpImageContent,
|
||||
McpSdkServerConfig,
|
||||
McpServerConstructor,
|
||||
McpServerLike,
|
||||
McpTextContent,
|
||||
SdkRunnerQueryOptions,
|
||||
} from "./tool-bridge.types.js";
|
||||
export type {
|
||||
SdkDoneEvent,
|
||||
SdkErrorEvent,
|
||||
SdkEvent,
|
||||
SdkEventType,
|
||||
SdkProviderConfig,
|
||||
SdkProviderEnv,
|
||||
SdkRunnerParams,
|
||||
SdkTextEvent,
|
||||
SdkToolResultEvent,
|
||||
SdkToolUseEvent,
|
||||
} from "./types.js";
|
||||
220
src/agents/claude-agent-sdk/provider-config.ts
Normal file
220
src/agents/claude-agent-sdk/provider-config.ts
Normal file
@ -0,0 +1,220 @@
|
||||
/**
|
||||
* Provider configuration builders for the Claude Agent SDK.
|
||||
*
|
||||
* Supports multiple authentication methods:
|
||||
* - Anthropic API key (ANTHROPIC_API_KEY)
|
||||
* - Claude Code CLI OAuth (reuses ~/.claude credentials)
|
||||
* - z.AI subscription (via ANTHROPIC_AUTH_TOKEN or CLI auth)
|
||||
* - OpenRouter (Anthropic-compatible API)
|
||||
* - AWS Bedrock
|
||||
* - Google Vertex AI
|
||||
*/
|
||||
|
||||
import type { SdkProviderConfig, SdkProviderEnv } from "./types.js";
|
||||
import { readClaudeCliCredentialsCached } from "../cli-credentials.js";
|
||||
import { createSubsystemLogger } from "../../logging/subsystem.js";
|
||||
|
||||
const log = createSubsystemLogger("agents/claude-agent-sdk");
|
||||
|
||||
/**
|
||||
* Build provider config for direct Anthropic API access.
|
||||
*/
|
||||
export function buildAnthropicSdkProvider(apiKey: string): SdkProviderConfig {
|
||||
return {
|
||||
name: "Anthropic",
|
||||
env: {
|
||||
ANTHROPIC_API_KEY: apiKey,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Build provider config for Claude Code CLI/subscription auth.
|
||||
*
|
||||
* This method reads credentials from the Claude Code CLI's keychain/file storage,
|
||||
* enabling subscription-based access (Claude Max, z.AI, etc.) without an API key.
|
||||
*
|
||||
* The SDK will automatically use these credentials when they're available in the
|
||||
* environment or via the CLI's native auth mechanism.
|
||||
*/
|
||||
export function buildClaudeCliSdkProvider(): SdkProviderConfig | null {
|
||||
// Read cached credentials from Claude Code CLI
|
||||
const credentials = readClaudeCliCredentialsCached({
|
||||
allowKeychainPrompt: false,
|
||||
ttlMs: 60_000, // 1 minute cache
|
||||
});
|
||||
|
||||
if (!credentials) {
|
||||
log.debug("No Claude CLI credentials found");
|
||||
return null;
|
||||
}
|
||||
|
||||
// For OAuth credentials, use the access token as ANTHROPIC_AUTH_TOKEN
|
||||
if (credentials.type === "oauth") {
|
||||
// Check if token is expired
|
||||
if (credentials.expires < Date.now()) {
|
||||
log.warn("Claude CLI credentials expired", {
|
||||
expiresAt: new Date(credentials.expires).toISOString(),
|
||||
});
|
||||
// Still return the config - the SDK may handle refresh internally
|
||||
}
|
||||
|
||||
return {
|
||||
name: "Claude CLI (subscription)",
|
||||
env: {
|
||||
ANTHROPIC_AUTH_TOKEN: credentials.access,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// For token-type credentials (less common)
|
||||
if (credentials.type === "token") {
|
||||
return {
|
||||
name: "Claude CLI (token)",
|
||||
env: {
|
||||
ANTHROPIC_AUTH_TOKEN: credentials.token,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build provider config for z.AI subscription access.
|
||||
*
|
||||
* z.AI uses Anthropic-compatible API with a different base URL.
|
||||
*/
|
||||
export function buildZaiSdkProvider(authToken: string): SdkProviderConfig {
|
||||
return {
|
||||
name: "z.AI",
|
||||
env: {
|
||||
ANTHROPIC_AUTH_TOKEN: authToken,
|
||||
// z.AI may use a custom base URL - leave unset to use default
|
||||
// The SDK or z.AI gateway handles routing
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Build provider config for OpenRouter (Anthropic-compatible).
|
||||
*/
|
||||
export function buildOpenRouterSdkProvider(apiKey: string): SdkProviderConfig {
|
||||
return {
|
||||
name: "OpenRouter (Anthropic-compatible)",
|
||||
env: {
|
||||
ANTHROPIC_BASE_URL: "https://openrouter.ai/api/v1",
|
||||
ANTHROPIC_API_KEY: apiKey,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Build provider config for AWS Bedrock.
|
||||
*
|
||||
* AWS credentials should be configured via standard AWS mechanisms
|
||||
* (environment variables, shared credentials file, IAM role, etc.).
|
||||
*/
|
||||
export function buildBedrockSdkProvider(): SdkProviderConfig {
|
||||
return {
|
||||
name: "AWS Bedrock",
|
||||
env: {
|
||||
CLAUDE_CODE_USE_BEDROCK: "1",
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Build provider config for Google Vertex AI.
|
||||
*
|
||||
* Google Cloud credentials should be configured via standard GCP mechanisms
|
||||
* (GOOGLE_APPLICATION_CREDENTIALS, default credentials, etc.).
|
||||
*/
|
||||
export function buildVertexSdkProvider(): SdkProviderConfig {
|
||||
return {
|
||||
name: "Google Vertex AI",
|
||||
env: {
|
||||
CLAUDE_CODE_USE_VERTEX: "1",
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve provider configuration based on available credentials.
|
||||
*
|
||||
* Priority order:
|
||||
* 1. Explicit API key from config/env
|
||||
* 2. Claude Code CLI credentials (subscription auth)
|
||||
* 3. Environment variables (fallback)
|
||||
*/
|
||||
export function resolveProviderConfig(options?: {
|
||||
apiKey?: string;
|
||||
authToken?: string;
|
||||
baseUrl?: string;
|
||||
useCliCredentials?: boolean;
|
||||
}): SdkProviderConfig {
|
||||
// 1. Explicit API key takes precedence
|
||||
if (options?.apiKey) {
|
||||
const config = buildAnthropicSdkProvider(options.apiKey);
|
||||
if (options.baseUrl) {
|
||||
config.env.ANTHROPIC_BASE_URL = options.baseUrl;
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
// 2. Explicit auth token (OAuth/subscription)
|
||||
if (options?.authToken) {
|
||||
const env: SdkProviderEnv = {
|
||||
ANTHROPIC_AUTH_TOKEN: options.authToken,
|
||||
};
|
||||
if (options.baseUrl) {
|
||||
env.ANTHROPIC_BASE_URL = options.baseUrl;
|
||||
}
|
||||
return {
|
||||
name: "Anthropic (auth token)",
|
||||
env,
|
||||
};
|
||||
}
|
||||
|
||||
// 3. Try Claude CLI credentials if enabled (default: true)
|
||||
if (options?.useCliCredentials !== false) {
|
||||
const cliConfig = buildClaudeCliSdkProvider();
|
||||
if (cliConfig) {
|
||||
if (options?.baseUrl) {
|
||||
cliConfig.env.ANTHROPIC_BASE_URL = options.baseUrl;
|
||||
}
|
||||
return cliConfig;
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Check environment variables
|
||||
const envApiKey = process.env.ANTHROPIC_API_KEY;
|
||||
const envAuthToken = process.env.ANTHROPIC_AUTH_TOKEN;
|
||||
|
||||
if (envApiKey) {
|
||||
return {
|
||||
name: "Anthropic (env)",
|
||||
env: {
|
||||
ANTHROPIC_API_KEY: envApiKey,
|
||||
ANTHROPIC_BASE_URL: options?.baseUrl ?? process.env.ANTHROPIC_BASE_URL,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (envAuthToken) {
|
||||
return {
|
||||
name: "Anthropic (env auth token)",
|
||||
env: {
|
||||
ANTHROPIC_AUTH_TOKEN: envAuthToken,
|
||||
ANTHROPIC_BASE_URL: options?.baseUrl ?? process.env.ANTHROPIC_BASE_URL,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// 5. Return empty config - SDK will use its own credential resolution
|
||||
log.debug("No explicit credentials provided, SDK will use native auth");
|
||||
return {
|
||||
name: "Anthropic (SDK native)",
|
||||
env: {},
|
||||
};
|
||||
}
|
||||
85
src/agents/claude-agent-sdk/sdk-agent-runtime.ts
Normal file
85
src/agents/claude-agent-sdk/sdk-agent-runtime.ts
Normal file
@ -0,0 +1,85 @@
|
||||
/**
|
||||
* Claude Agent SDK runtime implementation.
|
||||
*
|
||||
* Implements the AgentRuntime interface using the Claude Agent SDK for execution.
|
||||
*/
|
||||
|
||||
import type { MoltbotConfig } from "../../config/config.js";
|
||||
import type { AgentRuntime, AgentRuntimeRunParams, AgentRuntimeResult } from "../agent-runtime.js";
|
||||
import type { AgentCcSdkConfig } from "../../config/types.agents.js";
|
||||
import { runSdkAgent } from "./sdk-runner.js";
|
||||
import { resolveProviderConfig } from "./provider-config.js";
|
||||
import { isSdkAvailable } from "./sdk-loader.js";
|
||||
import { createSubsystemLogger } from "../../logging/subsystem.js";
|
||||
|
||||
const log = createSubsystemLogger("agents/claude-agent-sdk");
|
||||
|
||||
export type CcSdkAgentRuntimeContext = {
|
||||
/** Moltbot configuration. */
|
||||
config?: MoltbotConfig;
|
||||
/** Claude Code SDK configuration. */
|
||||
ccsdkConfig?: AgentCcSdkConfig;
|
||||
/** Explicit API key override. */
|
||||
apiKey?: string;
|
||||
/** Explicit auth token override (for subscription auth). */
|
||||
authToken?: string;
|
||||
/** Custom base URL for API requests. */
|
||||
baseUrl?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a Claude Code SDK runtime instance.
|
||||
*
|
||||
* The CCSDK runtime uses the Claude Agent SDK for model execution,
|
||||
* which supports:
|
||||
* - Claude Code CLI authentication (subscription-based)
|
||||
* - Anthropic API key authentication
|
||||
* - AWS Bedrock and Google Vertex AI backends
|
||||
*/
|
||||
export function createCcSdkAgentRuntime(context?: CcSdkAgentRuntimeContext): AgentRuntime {
|
||||
// Pre-check SDK availability
|
||||
if (!isSdkAvailable()) {
|
||||
log.warn("Claude Agent SDK not available - runtime will fail on first run");
|
||||
}
|
||||
|
||||
// Resolve provider configuration from context
|
||||
const providerConfig = resolveProviderConfig({
|
||||
apiKey: context?.apiKey,
|
||||
authToken: context?.authToken,
|
||||
baseUrl: context?.baseUrl,
|
||||
useCliCredentials: true, // Enable Claude CLI credential resolution
|
||||
});
|
||||
|
||||
return {
|
||||
kind: "ccsdk",
|
||||
displayName: `Claude Code SDK (${providerConfig.name})`,
|
||||
|
||||
async run(params: AgentRuntimeRunParams): Promise<AgentRuntimeResult> {
|
||||
log.debug("CCSDK runtime run", {
|
||||
sessionId: params.sessionId,
|
||||
runId: params.runId,
|
||||
provider: providerConfig.name,
|
||||
});
|
||||
|
||||
return runSdkAgent({
|
||||
sessionId: params.sessionId,
|
||||
sessionKey: params.sessionKey,
|
||||
sessionFile: params.sessionFile,
|
||||
workspaceDir: params.workspaceDir,
|
||||
agentDir: params.agentDir,
|
||||
config: params.config ?? context?.config,
|
||||
prompt: params.prompt,
|
||||
model: params.model ? `${params.provider}/${params.model}` : undefined,
|
||||
providerConfig,
|
||||
timeoutMs: params.timeoutMs,
|
||||
runId: params.runId,
|
||||
abortSignal: params.abortSignal,
|
||||
extraSystemPrompt: params.extraSystemPrompt,
|
||||
hooksEnabled: context?.ccsdkConfig?.hooksEnabled,
|
||||
sdkOptions: context?.ccsdkConfig?.options,
|
||||
modelTiers: context?.ccsdkConfig?.models,
|
||||
onAgentEvent: params.onAgentEvent,
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
84
src/agents/claude-agent-sdk/sdk-loader.ts
Normal file
84
src/agents/claude-agent-sdk/sdk-loader.ts
Normal file
@ -0,0 +1,84 @@
|
||||
/**
|
||||
* Dynamic loader for the Claude Agent SDK.
|
||||
*
|
||||
* The SDK is an optional dependency, so we load it dynamically to avoid
|
||||
* breaking installations that don't have it installed.
|
||||
*/
|
||||
|
||||
import { createRequire } from "node:module";
|
||||
import { createSubsystemLogger } from "../../logging/subsystem.js";
|
||||
|
||||
const log = createSubsystemLogger("agents/claude-agent-sdk");
|
||||
|
||||
/**
|
||||
* SDK module type (generic since the actual types may not be available).
|
||||
*
|
||||
* The Claude Agent SDK exports various classes and functions for agent execution.
|
||||
* Since it's an optional dependency, we use a generic type here.
|
||||
*/
|
||||
export type ClaudeAgentSdkModule = {
|
||||
// The actual SDK API - shape will vary by version
|
||||
[key: string]: unknown;
|
||||
};
|
||||
|
||||
/** Cached SDK module reference. */
|
||||
let sdkModule: ClaudeAgentSdkModule | null = null;
|
||||
let loadAttempted = false;
|
||||
let loadError: Error | null = null;
|
||||
|
||||
/**
|
||||
* Check if the Claude Agent SDK is available.
|
||||
*/
|
||||
export function isSdkAvailable(): boolean {
|
||||
if (loadAttempted) return sdkModule !== null;
|
||||
try {
|
||||
// Probe for the module without loading it fully
|
||||
const require = createRequire(import.meta.url);
|
||||
require.resolve("@anthropic-ai/claude-agent-sdk");
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the Claude Agent SDK dynamically.
|
||||
*
|
||||
* @throws Error if the SDK is not installed
|
||||
*/
|
||||
export async function loadClaudeAgentSdk(): Promise<ClaudeAgentSdkModule> {
|
||||
if (sdkModule) {
|
||||
return sdkModule;
|
||||
}
|
||||
|
||||
if (loadAttempted && loadError) {
|
||||
throw loadError;
|
||||
}
|
||||
|
||||
loadAttempted = true;
|
||||
|
||||
try {
|
||||
// Dynamic import to avoid bundling issues
|
||||
// The SDK package name is resolved at runtime
|
||||
const moduleName = "@anthropic-ai/claude-agent-sdk";
|
||||
sdkModule = (await import(/* @vite-ignore */ moduleName)) as ClaudeAgentSdkModule;
|
||||
log.info("Claude Agent SDK loaded successfully");
|
||||
return sdkModule;
|
||||
} catch (err) {
|
||||
loadError = new Error(
|
||||
"Claude Agent SDK not installed. Install with: npm install @anthropic-ai/claude-agent-sdk",
|
||||
);
|
||||
loadError.cause = err;
|
||||
log.warn("Failed to load Claude Agent SDK", { error: String(err) });
|
||||
throw loadError;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the SDK loader state (for testing).
|
||||
*/
|
||||
export function resetSdkLoaderForTest(): void {
|
||||
sdkModule = null;
|
||||
loadAttempted = false;
|
||||
loadError = null;
|
||||
}
|
||||
276
src/agents/claude-agent-sdk/sdk-runner.ts
Normal file
276
src/agents/claude-agent-sdk/sdk-runner.ts
Normal file
@ -0,0 +1,276 @@
|
||||
/**
|
||||
* Claude Agent SDK runner.
|
||||
*
|
||||
* Executes agent turns using the Claude Agent SDK, handling authentication,
|
||||
* event streaming, and result adaptation.
|
||||
*/
|
||||
|
||||
import type { SdkRunnerParams, SdkProviderEnv } from "./types.js";
|
||||
import type { CcSdkModelTiers } from "../../config/types.agents.js";
|
||||
import type { AgentRuntimeResult } from "../agent-runtime.js";
|
||||
import { loadClaudeAgentSdk, type ClaudeAgentSdkModule } from "./sdk-loader.js";
|
||||
import { resolveProviderConfig } from "./provider-config.js";
|
||||
import { createSubsystemLogger } from "../../logging/subsystem.js";
|
||||
|
||||
const log = createSubsystemLogger("agents/claude-agent-sdk");
|
||||
|
||||
/**
|
||||
* Build environment variables for model tier configuration.
|
||||
*
|
||||
* The Claude Code SDK uses these environment variables to select models
|
||||
* for different task complexity tiers.
|
||||
*/
|
||||
function buildModelTierEnv(modelTiers?: CcSdkModelTiers): SdkProviderEnv {
|
||||
const env: SdkProviderEnv = {};
|
||||
|
||||
if (modelTiers?.haiku) {
|
||||
env.ANTHROPIC_DEFAULT_HAIKU_MODEL = modelTiers.haiku;
|
||||
}
|
||||
if (modelTiers?.sonnet) {
|
||||
env.ANTHROPIC_DEFAULT_SONNET_MODEL = modelTiers.sonnet;
|
||||
}
|
||||
if (modelTiers?.opus) {
|
||||
env.ANTHROPIC_DEFAULT_OPUS_MODEL = modelTiers.opus;
|
||||
}
|
||||
|
||||
return env;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run an agent turn using the Claude Agent SDK.
|
||||
*
|
||||
* This function:
|
||||
* 1. Loads the SDK dynamically
|
||||
* 2. Configures authentication based on available credentials
|
||||
* 3. Executes the agent turn with the given prompt
|
||||
* 4. Streams events back to the caller
|
||||
* 5. Returns results in the standard AgentRuntimeResult format
|
||||
*/
|
||||
export async function runSdkAgent(params: SdkRunnerParams): Promise<AgentRuntimeResult> {
|
||||
const started = Date.now();
|
||||
|
||||
// Emit lifecycle start event
|
||||
params.onAgentEvent?.({
|
||||
stream: "lifecycle",
|
||||
data: {
|
||||
phase: "start",
|
||||
runtime: "sdk",
|
||||
sessionId: params.sessionId,
|
||||
runId: params.runId,
|
||||
},
|
||||
});
|
||||
|
||||
try {
|
||||
// Load the SDK
|
||||
const sdk = await loadClaudeAgentSdk();
|
||||
|
||||
// Resolve provider configuration
|
||||
const providerConfig = params.providerConfig ?? resolveProviderConfig();
|
||||
|
||||
log.info("Starting SDK agent run", {
|
||||
provider: providerConfig.name,
|
||||
model: params.model,
|
||||
sessionId: params.sessionId,
|
||||
runId: params.runId,
|
||||
});
|
||||
|
||||
// Set up environment for the SDK
|
||||
const originalEnv = { ...process.env };
|
||||
try {
|
||||
// Apply provider environment variables
|
||||
for (const [key, value] of Object.entries(providerConfig.env)) {
|
||||
if (value !== undefined) {
|
||||
process.env[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
// Apply model tier environment variables
|
||||
const modelTierEnv = buildModelTierEnv(params.modelTiers);
|
||||
for (const [key, value] of Object.entries(modelTierEnv)) {
|
||||
if (value !== undefined) {
|
||||
process.env[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
// Build SDK options
|
||||
const sdkOptions = {
|
||||
model: params.model,
|
||||
workingDirectory: params.workspaceDir,
|
||||
systemPrompt: params.extraSystemPrompt,
|
||||
timeout: params.timeoutMs,
|
||||
signal: params.abortSignal,
|
||||
...params.sdkOptions,
|
||||
};
|
||||
|
||||
// Run the agent
|
||||
// Note: The actual SDK API may differ - this is a placeholder implementation
|
||||
// that will need to be updated based on the actual SDK interface
|
||||
const result = await runSdkAgentInternal(sdk, params.prompt, sdkOptions, {
|
||||
onEvent: (event) => {
|
||||
// Forward events to the caller
|
||||
params.onAgentEvent?.({
|
||||
stream: "sdk",
|
||||
data: event as Record<string, unknown>,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
// Build result
|
||||
const agentResult: AgentRuntimeResult = {
|
||||
payloads: result.texts.map((text) => ({ text })),
|
||||
meta: {
|
||||
durationMs: Date.now() - started,
|
||||
agentMeta: {
|
||||
sessionId: params.sessionId,
|
||||
provider: "anthropic",
|
||||
model: params.model ?? "claude-sonnet-4-20250514",
|
||||
usage: result.usage,
|
||||
},
|
||||
aborted: result.aborted,
|
||||
},
|
||||
};
|
||||
|
||||
// Emit lifecycle end event
|
||||
params.onAgentEvent?.({
|
||||
stream: "lifecycle",
|
||||
data: {
|
||||
phase: "end",
|
||||
runtime: "sdk",
|
||||
startedAt: started,
|
||||
endedAt: Date.now(),
|
||||
aborted: result.aborted ?? false,
|
||||
},
|
||||
});
|
||||
|
||||
return agentResult;
|
||||
} finally {
|
||||
// Restore original environment
|
||||
const keysToRestore = [
|
||||
...Object.keys(providerConfig.env),
|
||||
...Object.keys(buildModelTierEnv(params.modelTiers)),
|
||||
];
|
||||
for (const key of keysToRestore) {
|
||||
if (originalEnv[key] !== undefined) {
|
||||
process.env[key] = originalEnv[key];
|
||||
} else {
|
||||
delete process.env[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||
|
||||
log.error("SDK agent run failed", {
|
||||
error: errorMessage,
|
||||
sessionId: params.sessionId,
|
||||
runId: params.runId,
|
||||
});
|
||||
|
||||
// Emit lifecycle error event
|
||||
params.onAgentEvent?.({
|
||||
stream: "lifecycle",
|
||||
data: {
|
||||
phase: "error",
|
||||
runtime: "sdk",
|
||||
startedAt: started,
|
||||
endedAt: Date.now(),
|
||||
error: errorMessage,
|
||||
},
|
||||
});
|
||||
|
||||
// Return error result
|
||||
return {
|
||||
payloads: [
|
||||
{
|
||||
text: `SDK agent error: ${errorMessage}`,
|
||||
isError: true,
|
||||
},
|
||||
],
|
||||
meta: {
|
||||
durationMs: Date.now() - started,
|
||||
agentMeta: {
|
||||
sessionId: params.sessionId,
|
||||
provider: "anthropic",
|
||||
model: params.model ?? "unknown",
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal SDK execution function.
|
||||
*
|
||||
* This is a placeholder that needs to be implemented based on the actual
|
||||
* Claude Agent SDK API. The SDK provides different interfaces for:
|
||||
* - Simple completions
|
||||
* - Agentic tool use
|
||||
* - Streaming responses
|
||||
*/
|
||||
async function runSdkAgentInternal(
|
||||
sdk: ClaudeAgentSdkModule,
|
||||
_prompt: string,
|
||||
options: {
|
||||
model?: string;
|
||||
workingDirectory?: string;
|
||||
systemPrompt?: string;
|
||||
timeout?: number;
|
||||
signal?: AbortSignal;
|
||||
},
|
||||
_callbacks: {
|
||||
onEvent?: (event: unknown) => void;
|
||||
},
|
||||
): Promise<{
|
||||
texts: string[];
|
||||
usage?: {
|
||||
input?: number;
|
||||
output?: number;
|
||||
cacheRead?: number;
|
||||
cacheWrite?: number;
|
||||
total?: number;
|
||||
};
|
||||
aborted?: boolean;
|
||||
}> {
|
||||
// Placeholder implementation - needs to be updated based on actual SDK API
|
||||
// The SDK likely provides something like:
|
||||
// - sdk.createAgent() or sdk.Agent class
|
||||
// - agent.run(prompt) or agent.chat(prompt)
|
||||
// - Event streaming via async iterators or callbacks
|
||||
|
||||
const texts: string[] = [];
|
||||
const usage:
|
||||
| {
|
||||
input?: number;
|
||||
output?: number;
|
||||
cacheRead?: number;
|
||||
cacheWrite?: number;
|
||||
total?: number;
|
||||
}
|
||||
| undefined = undefined;
|
||||
let aborted = false;
|
||||
|
||||
try {
|
||||
// Check if SDK has the expected interface
|
||||
// This needs to be updated based on actual SDK API
|
||||
if ("Claude" in sdk || "createAgent" in sdk || "Agent" in sdk) {
|
||||
// TODO: Implement actual SDK call based on SDK's public API
|
||||
// For now, throw to indicate SDK integration is not yet complete
|
||||
throw new Error(
|
||||
"Claude Agent SDK integration not yet implemented. " +
|
||||
"Please check SDK documentation for the correct API.",
|
||||
);
|
||||
}
|
||||
|
||||
// Fallback: check for simple completion API
|
||||
throw new Error("Unable to find compatible API in Claude Agent SDK");
|
||||
} catch (error) {
|
||||
if (options.signal?.aborted) {
|
||||
aborted = true;
|
||||
texts.push("Agent run was aborted.");
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
return { texts, usage, aborted };
|
||||
}
|
||||
368
src/agents/claude-agent-sdk/tool-bridge.ts
Normal file
368
src/agents/claude-agent-sdk/tool-bridge.ts
Normal file
@ -0,0 +1,368 @@
|
||||
/**
|
||||
* Bridge between Moltbot tools (AnyAgentTool) and the Claude Agent SDK's
|
||||
* MCP-based custom tool system.
|
||||
*
|
||||
* This module is pure facade/adapter code — no business logic is duplicated.
|
||||
* Each Moltbot tool's `execute()` function is called as-is; the bridge only
|
||||
* converts schemas, argument shapes, and result formats.
|
||||
*
|
||||
* Usage:
|
||||
* ```ts
|
||||
* const mcpConfig = await bridgeMoltbotToolsToMcpServer({
|
||||
* name: "moltbot",
|
||||
* tools: myMoltbotTools,
|
||||
* });
|
||||
* // Pass to SDK query():
|
||||
* query({ prompt, options: { mcpServers: { moltbot: mcpConfig } } })
|
||||
* ```
|
||||
*/
|
||||
|
||||
import type { AgentToolResult } from "@mariozechner/pi-agent-core";
|
||||
|
||||
import { createSubsystemLogger } from "../../logging/subsystem.js";
|
||||
import { normalizeToolName } from "../tool-policy.js";
|
||||
import type { AnyAgentTool } from "../tools/common.js";
|
||||
import type {
|
||||
McpCallToolResult,
|
||||
McpContentBlock,
|
||||
McpSdkServerConfig,
|
||||
McpServerConstructor,
|
||||
McpServerLike,
|
||||
} from "./tool-bridge.types.js";
|
||||
|
||||
const log = createSubsystemLogger("agents/claude-agent-sdk/tool-bridge");
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Schema conversion: TypeBox → JSON Schema (passthrough)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Extract a clean JSON Schema object from a TypeBox schema.
|
||||
*
|
||||
* TypeBox schemas *are* JSON Schema objects, but they carry internal Symbol
|
||||
* metadata and potentially circular references. A JSON round-trip strips those
|
||||
* cleanly. We also ensure a sensible fallback for tools with no schema.
|
||||
*/
|
||||
export function extractJsonSchema(tool: AnyAgentTool): Record<string, unknown> {
|
||||
const schema = tool.parameters;
|
||||
if (!schema || typeof schema !== "object") {
|
||||
return { type: "object", properties: {} };
|
||||
}
|
||||
try {
|
||||
return JSON.parse(JSON.stringify(schema)) as Record<string, unknown>;
|
||||
} catch {
|
||||
// Schema is not serializable (shouldn't happen with TypeBox, but be safe).
|
||||
log.debug(`Schema for "${tool.name}" is not JSON-serializable, using empty schema`);
|
||||
return { type: "object", properties: {} };
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Result conversion: AgentToolResult → McpCallToolResult
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Convert a Moltbot `AgentToolResult` to an MCP `CallToolResult`.
|
||||
*
|
||||
* Content block shapes are nearly identical between the two systems.
|
||||
* The main differences handled:
|
||||
* - `tool_error` blocks → text + `isError: true`
|
||||
* - `details` metadata → serialized as a `<tool-details>` text block
|
||||
* - Empty results → "(no output)" fallback
|
||||
*/
|
||||
export function convertToolResult(result: AgentToolResult<unknown>): McpCallToolResult {
|
||||
const content: McpContentBlock[] = [];
|
||||
let isError = false;
|
||||
|
||||
for (const block of result.content) {
|
||||
switch (block.type) {
|
||||
case "text":
|
||||
content.push({ type: "text", text: (block as { text: string }).text });
|
||||
break;
|
||||
case "image":
|
||||
content.push({
|
||||
type: "image",
|
||||
data: (block as { data: string }).data,
|
||||
mimeType: (block as { mimeType: string }).mimeType,
|
||||
});
|
||||
break;
|
||||
default: {
|
||||
// Handle tool_error or any other block type with an 'error' field.
|
||||
const maybeError = block as { type: string; error?: string; text?: string };
|
||||
if (maybeError.type === "tool_error" || maybeError.error) {
|
||||
const errorText = maybeError.error ?? maybeError.text ?? "Unknown tool error";
|
||||
content.push({ type: "text", text: errorText });
|
||||
isError = true;
|
||||
} else if (maybeError.text) {
|
||||
// Fallback: treat any block with a text field as text content.
|
||||
content.push({ type: "text", text: maybeError.text });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Serialize `details` as an additional text block when present.
|
||||
// The model benefits from structured metadata; we use an XML tag
|
||||
// wrapper so it's easy to parse and doesn't pollute the main output.
|
||||
if (result.details !== undefined && result.details !== null) {
|
||||
try {
|
||||
const serialized = JSON.stringify(result.details, null, 2);
|
||||
content.push({
|
||||
type: "text",
|
||||
text: `<tool-details>\n${serialized}\n</tool-details>`,
|
||||
});
|
||||
} catch {
|
||||
// details not serializable — skip
|
||||
}
|
||||
}
|
||||
|
||||
if (content.length === 0) {
|
||||
content.push({ type: "text", text: "(no output)" });
|
||||
}
|
||||
|
||||
return isError ? { content, isError: true } : { content };
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Handler wrapping: AnyAgentTool.execute → MCP handler
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Wrap a Moltbot tool's `execute()` as an MCP tool handler.
|
||||
*
|
||||
* Differences bridged:
|
||||
* - Moltbot: `execute(toolCallId, params, signal?, onUpdate?)`
|
||||
* - MCP: `handler(args) → Promise<CallToolResult>`
|
||||
*
|
||||
* Notable: MCP handlers have no abort signal or streaming update callback.
|
||||
* We pass the shared `abortSignal` (if provided) and skip `onUpdate`.
|
||||
*/
|
||||
export function wrapToolHandler(
|
||||
tool: AnyAgentTool,
|
||||
abortSignal?: AbortSignal,
|
||||
): (args: Record<string, unknown>) => Promise<McpCallToolResult> {
|
||||
const normalizedName = normalizeToolName(tool.name);
|
||||
|
||||
return async (args: Record<string, unknown>): Promise<McpCallToolResult> => {
|
||||
// Generate a synthetic toolCallId. The Claude Agent SDK doesn't expose its
|
||||
// internal tool call ID to MCP handlers, so we create a unique one for
|
||||
// internal tracking/logging. This is safe because Moltbot tools only use
|
||||
// toolCallId for logging and scoping, not for cross-referencing with the
|
||||
// model's response.
|
||||
const toolCallId = `mcp-bridge-${normalizedName}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
||||
|
||||
try {
|
||||
const result = await tool.execute(
|
||||
toolCallId,
|
||||
args,
|
||||
abortSignal, // 3rd arg: AbortSignal (undefined if not provided)
|
||||
undefined, // 4th arg: onUpdate — not supported in MCP tool protocol
|
||||
);
|
||||
return convertToolResult(result);
|
||||
} catch (err) {
|
||||
// Propagate AbortError so the SDK runner can handle cancellation.
|
||||
if (err instanceof Error && err.name === "AbortError") {
|
||||
return {
|
||||
content: [{ type: "text", text: `Tool "${normalizedName}" was aborted.` }],
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
|
||||
const message = err instanceof Error ? err.message : String(err);
|
||||
log.error(`Tool ${normalizedName} failed: ${message}`);
|
||||
if (err instanceof Error && err.stack) {
|
||||
log.debug(`Tool ${normalizedName} stack:\n${err.stack}`);
|
||||
}
|
||||
|
||||
return {
|
||||
content: [{ type: "text", text: `Tool error (${normalizedName}): ${message}` }],
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// MCP Server loading (dynamic import — SDK is optional)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
let cachedMcpServerConstructor: McpServerConstructor | undefined;
|
||||
|
||||
/**
|
||||
* Dynamically load the McpServer class from the MCP SDK.
|
||||
*
|
||||
* The MCP SDK (`@modelcontextprotocol/sdk`) is a transitive dependency of
|
||||
* `@anthropic-ai/claude-agent-sdk`. When the Claude Agent SDK is installed,
|
||||
* the MCP SDK is available. We import it lazily to avoid build-time deps.
|
||||
*/
|
||||
async function loadMcpServerClass(): Promise<McpServerConstructor> {
|
||||
if (cachedMcpServerConstructor) return cachedMcpServerConstructor;
|
||||
|
||||
// The MCP SDK exports McpServer from this path.
|
||||
const moduleName: string = "@modelcontextprotocol/sdk/server/mcp.js";
|
||||
try {
|
||||
const mod = (await import(moduleName)) as { McpServer?: McpServerConstructor };
|
||||
if (!mod.McpServer || typeof mod.McpServer !== "function") {
|
||||
throw new Error("McpServer class not found in @modelcontextprotocol/sdk");
|
||||
}
|
||||
cachedMcpServerConstructor = mod.McpServer;
|
||||
return mod.McpServer;
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : String(err);
|
||||
throw new Error(
|
||||
`Failed to load MCP SDK. Ensure @anthropic-ai/claude-agent-sdk is installed ` +
|
||||
`(it includes @modelcontextprotocol/sdk as a dependency).\n\nError: ${message}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Tool name utilities
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/** MCP tool naming convention: mcp__{server}__{tool} */
|
||||
export function mcpToolName(serverName: string, toolName: string): string {
|
||||
return `mcp__${serverName}__${toolName}`;
|
||||
}
|
||||
|
||||
/** Build the allowedTools list for the Claude Agent SDK from bridged tools. */
|
||||
export function buildMcpAllowedTools(serverName: string, tools: AnyAgentTool[]): string[] {
|
||||
return tools.map((t) => mcpToolName(serverName, t.name));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Main bridge: Moltbot tools → MCP server config
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export type BridgeOptions = {
|
||||
/** MCP server name (used in mcp__{name}__{tool} naming). */
|
||||
name: string;
|
||||
/** Moltbot tools to bridge. These should already be policy-filtered. */
|
||||
tools: AnyAgentTool[];
|
||||
/** Optional shared abort signal for all tool executions. */
|
||||
abortSignal?: AbortSignal;
|
||||
};
|
||||
|
||||
export type BridgeResult = {
|
||||
/** MCP server config ready for `query({ options: { mcpServers } })`. */
|
||||
serverConfig: McpSdkServerConfig;
|
||||
/** Pre-built `allowedTools` list for the SDK options. */
|
||||
allowedTools: string[];
|
||||
/** Number of tools registered. */
|
||||
toolCount: number;
|
||||
/** Tool names that were registered. */
|
||||
registeredTools: string[];
|
||||
/** Tool names that were skipped (e.g., schema extraction failed). */
|
||||
skippedTools: string[];
|
||||
};
|
||||
|
||||
/**
|
||||
* Bridge Moltbot tools into an in-process MCP server config for the Claude Agent SDK.
|
||||
*
|
||||
* This is the main entry point. It:
|
||||
* 1. Dynamically loads the MCP SDK's McpServer class.
|
||||
* 2. For each Moltbot tool, extracts JSON Schema, wraps the handler, and registers.
|
||||
* 3. Returns a config object ready for `query({ options: { mcpServers } })`.
|
||||
*
|
||||
* No business logic is duplicated — each tool's `execute()` is called directly.
|
||||
*/
|
||||
export async function bridgeMoltbotToolsToMcpServer(options: BridgeOptions): Promise<BridgeResult> {
|
||||
const McpServer = await loadMcpServerClass();
|
||||
const server: McpServerLike = new McpServer({
|
||||
name: options.name,
|
||||
version: "1.0.0",
|
||||
});
|
||||
|
||||
const registered: string[] = [];
|
||||
const skipped: string[] = [];
|
||||
|
||||
for (const tool of options.tools) {
|
||||
const toolName = tool.name;
|
||||
if (!toolName?.trim()) {
|
||||
log.debug("Skipping tool with empty name");
|
||||
skipped.push("(unnamed)");
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
const jsonSchema = extractJsonSchema(tool);
|
||||
const handler = wrapToolHandler(tool, options.abortSignal);
|
||||
|
||||
server.tool(toolName, tool.description ?? `Moltbot tool: ${toolName}`, jsonSchema, handler);
|
||||
|
||||
registered.push(toolName);
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : String(err);
|
||||
log.error(`Failed to register tool "${toolName}": ${message}`);
|
||||
skipped.push(toolName);
|
||||
}
|
||||
}
|
||||
|
||||
const serverConfig: McpSdkServerConfig = {
|
||||
type: "sdk",
|
||||
name: options.name,
|
||||
instance: server,
|
||||
};
|
||||
|
||||
return {
|
||||
serverConfig,
|
||||
allowedTools: buildMcpAllowedTools(options.name, options.tools),
|
||||
toolCount: registered.length,
|
||||
registeredTools: registered,
|
||||
skippedTools: skipped,
|
||||
};
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Convenience: bridge without async (for tests / when McpServer is provided)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Synchronous variant for when the McpServer class is already available.
|
||||
* Useful in tests where you can provide a mock McpServer constructor.
|
||||
*/
|
||||
export function bridgeMoltbotToolsSync(
|
||||
options: BridgeOptions & { McpServer: McpServerConstructor },
|
||||
): BridgeResult {
|
||||
const server: McpServerLike = new options.McpServer({
|
||||
name: options.name,
|
||||
version: "1.0.0",
|
||||
});
|
||||
|
||||
const registered: string[] = [];
|
||||
const skipped: string[] = [];
|
||||
|
||||
for (const tool of options.tools) {
|
||||
const toolName = tool.name;
|
||||
if (!toolName?.trim()) {
|
||||
skipped.push("(unnamed)");
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
const jsonSchema = extractJsonSchema(tool);
|
||||
const handler = wrapToolHandler(tool, options.abortSignal);
|
||||
server.tool(toolName, tool.description ?? `Moltbot tool: ${toolName}`, jsonSchema, handler);
|
||||
registered.push(toolName);
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : String(err);
|
||||
log.error(`Failed to register tool "${toolName}": ${message}`);
|
||||
skipped.push(toolName);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
serverConfig: { type: "sdk", name: options.name, instance: server },
|
||||
allowedTools: buildMcpAllowedTools(options.name, options.tools),
|
||||
toolCount: registered.length,
|
||||
registeredTools: registered,
|
||||
skippedTools: skipped,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the cached MCP server constructor (for testing).
|
||||
*/
|
||||
export function resetMcpServerCache(): void {
|
||||
cachedMcpServerConstructor = undefined;
|
||||
}
|
||||
117
src/agents/claude-agent-sdk/tool-bridge.types.ts
Normal file
117
src/agents/claude-agent-sdk/tool-bridge.types.ts
Normal file
@ -0,0 +1,117 @@
|
||||
/**
|
||||
* Local type definitions for interop with the Claude Agent SDK and MCP SDK.
|
||||
*
|
||||
* These are compatible with the runtime shapes but do not require the packages
|
||||
* to be installed at build time (both are optional, lazy-loaded integrations).
|
||||
*/
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// MCP CallToolResult (from @modelcontextprotocol/sdk/types.js)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export type McpTextContent = {
|
||||
type: "text";
|
||||
text: string;
|
||||
};
|
||||
|
||||
export type McpImageContent = {
|
||||
type: "image";
|
||||
data: string;
|
||||
mimeType: string;
|
||||
};
|
||||
|
||||
export type McpResourceContent = {
|
||||
type: "resource";
|
||||
uri: string;
|
||||
text?: string;
|
||||
blob?: string;
|
||||
};
|
||||
|
||||
export type McpContentBlock = McpTextContent | McpImageContent | McpResourceContent;
|
||||
|
||||
export type McpCallToolResult = {
|
||||
content: McpContentBlock[];
|
||||
isError?: boolean;
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// MCP Server config shapes (passed to SDK query() → options.mcpServers)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/** In-process MCP server created via createSdkMcpServer() or new McpServer(). */
|
||||
export type McpSdkServerConfig = {
|
||||
type: "sdk";
|
||||
name: string;
|
||||
/** The McpServer instance — typed as `unknown` to avoid importing the class. */
|
||||
instance: unknown;
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Claude Agent SDK query() options (subset we use)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export type SdkRunnerQueryOptions = {
|
||||
/** MCP servers to expose to the agent. */
|
||||
mcpServers?: Record<string, McpSdkServerConfig>;
|
||||
/** Tool allow list (pattern: "mcp__{server}__{tool}"). */
|
||||
allowedTools?: string[];
|
||||
/** Tool deny list. */
|
||||
disallowedTools?: string[];
|
||||
/** Built-in Claude Code tools to enable (or a preset). */
|
||||
tools?: string[] | { type: "preset"; preset: string };
|
||||
/** Environment variables for the SDK runtime (auth, base URL, timeout). */
|
||||
env?: Record<string, string>;
|
||||
/** Permission mode. */
|
||||
permissionMode?: string;
|
||||
/** Working directory. */
|
||||
cwd?: string;
|
||||
/** System prompt configuration. */
|
||||
systemPrompt?: string | { type: "preset"; preset: string };
|
||||
/** Max agent turns before stopping. */
|
||||
maxTurns?: number;
|
||||
/** Model to use. */
|
||||
model?: string;
|
||||
/** Additional directories the agent can access. */
|
||||
additionalDirectories?: string[];
|
||||
/** Where to load Claude Code settings from ("project", etc.). */
|
||||
settingSources?: string[];
|
||||
/** Include partial message events in the SDK stream. */
|
||||
includePartialMessages?: boolean;
|
||||
/** Claude Code hook callbacks. */
|
||||
hooks?: Record<string, unknown>;
|
||||
/** Path to the Claude Code executable to run. */
|
||||
pathToClaudeCodeExecutable?: string;
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Claude Agent SDK event shapes (defensive — events are untyped at runtime)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export type SdkResultEvent = {
|
||||
type: "result";
|
||||
subtype?: "success" | "error";
|
||||
result?: string;
|
||||
error?: string;
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// MCP Server tool registration (shape of McpServer.tool() from the SDK)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Minimal interface for the McpServer class from `@modelcontextprotocol/sdk`.
|
||||
* We only use the `tool()` registration method and the constructor.
|
||||
*/
|
||||
export interface McpServerLike {
|
||||
tool(
|
||||
name: string,
|
||||
description: string,
|
||||
inputSchema: Record<string, unknown>,
|
||||
handler: (args: Record<string, unknown>) => Promise<McpCallToolResult>,
|
||||
): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor shape for new McpServer({ name, version }).
|
||||
*/
|
||||
export type McpServerConstructor = new (opts: { name: string; version: string }) => McpServerLike;
|
||||
141
src/agents/claude-agent-sdk/types.ts
Normal file
141
src/agents/claude-agent-sdk/types.ts
Normal file
@ -0,0 +1,141 @@
|
||||
/**
|
||||
* Type definitions for the Claude Agent SDK integration.
|
||||
*/
|
||||
|
||||
import type { MoltbotConfig } from "../../config/config.js";
|
||||
import type { CcSdkModelTiers } from "../../config/types.agents.js";
|
||||
|
||||
/** Provider environment variables for SDK authentication and model config. */
|
||||
export type SdkProviderEnv = {
|
||||
/** Anthropic API key or OAuth token. */
|
||||
ANTHROPIC_API_KEY?: string;
|
||||
/** OAuth access token (alternative to API key). */
|
||||
ANTHROPIC_AUTH_TOKEN?: string;
|
||||
/** Custom base URL for API requests. */
|
||||
ANTHROPIC_BASE_URL?: string;
|
||||
/** Use Bedrock backend. */
|
||||
CLAUDE_CODE_USE_BEDROCK?: string;
|
||||
/** Use Vertex AI backend. */
|
||||
CLAUDE_CODE_USE_VERTEX?: string;
|
||||
/** Model for fast/simple tasks (Haiku tier). */
|
||||
ANTHROPIC_DEFAULT_HAIKU_MODEL?: string;
|
||||
/** Model for balanced tasks (Sonnet tier). */
|
||||
ANTHROPIC_DEFAULT_SONNET_MODEL?: string;
|
||||
/** Model for complex reasoning (Opus tier). */
|
||||
ANTHROPIC_DEFAULT_OPUS_MODEL?: string;
|
||||
};
|
||||
|
||||
/** Provider configuration for SDK runner. */
|
||||
export type SdkProviderConfig = {
|
||||
/** Human-readable provider name. */
|
||||
name: string;
|
||||
/** Environment variables to set for authentication. */
|
||||
env: SdkProviderEnv;
|
||||
/** Model override (if different from config). */
|
||||
model?: string;
|
||||
};
|
||||
|
||||
/** Parameters for SDK runner execution. */
|
||||
export type SdkRunnerParams = {
|
||||
/** Session identifier. */
|
||||
sessionId: string;
|
||||
/** Session key for routing. */
|
||||
sessionKey?: string;
|
||||
/** Path to session file for conversation history. */
|
||||
sessionFile: string;
|
||||
/** Agent workspace directory. */
|
||||
workspaceDir: string;
|
||||
/** Agent data directory. */
|
||||
agentDir?: string;
|
||||
/** Moltbot configuration. */
|
||||
config?: MoltbotConfig;
|
||||
/** User prompt/message. */
|
||||
prompt: string;
|
||||
/** Model to use (provider/model format). */
|
||||
model?: string;
|
||||
/** Provider configuration. */
|
||||
providerConfig?: SdkProviderConfig;
|
||||
/** Timeout in milliseconds. */
|
||||
timeoutMs: number;
|
||||
/** Run identifier for event correlation. */
|
||||
runId: string;
|
||||
/** Abort signal for cancellation. */
|
||||
abortSignal?: AbortSignal;
|
||||
/** Extra system prompt to append. */
|
||||
extraSystemPrompt?: string;
|
||||
/** Enable Claude Code hooks. */
|
||||
hooksEnabled?: boolean;
|
||||
/** Additional SDK options. */
|
||||
sdkOptions?: Record<string, unknown>;
|
||||
/** 3-tier model configuration (haiku/sonnet/opus). */
|
||||
modelTiers?: CcSdkModelTiers;
|
||||
/** Called for agent lifecycle events. */
|
||||
onAgentEvent?: (evt: { stream: string; data: Record<string, unknown> }) => void;
|
||||
};
|
||||
|
||||
/** SDK event types from the Claude Agent SDK. */
|
||||
export type SdkEventType =
|
||||
| "assistant_message"
|
||||
| "tool_use"
|
||||
| "tool_result"
|
||||
| "error"
|
||||
| "done"
|
||||
| "thinking"
|
||||
| "text";
|
||||
|
||||
/** Base SDK event structure. */
|
||||
export type SdkEvent = {
|
||||
type: SdkEventType;
|
||||
data?: unknown;
|
||||
};
|
||||
|
||||
/** SDK text/content event. */
|
||||
export type SdkTextEvent = SdkEvent & {
|
||||
type: "text" | "assistant_message";
|
||||
data: {
|
||||
text?: string;
|
||||
content?: string;
|
||||
};
|
||||
};
|
||||
|
||||
/** SDK tool use event. */
|
||||
export type SdkToolUseEvent = SdkEvent & {
|
||||
type: "tool_use";
|
||||
data: {
|
||||
name: string;
|
||||
input: Record<string, unknown>;
|
||||
id?: string;
|
||||
};
|
||||
};
|
||||
|
||||
/** SDK tool result event. */
|
||||
export type SdkToolResultEvent = SdkEvent & {
|
||||
type: "tool_result";
|
||||
data: {
|
||||
tool_use_id: string;
|
||||
content: string;
|
||||
is_error?: boolean;
|
||||
};
|
||||
};
|
||||
|
||||
/** SDK error event. */
|
||||
export type SdkErrorEvent = SdkEvent & {
|
||||
type: "error";
|
||||
data: {
|
||||
message: string;
|
||||
code?: string;
|
||||
};
|
||||
};
|
||||
|
||||
/** SDK done event. */
|
||||
export type SdkDoneEvent = SdkEvent & {
|
||||
type: "done";
|
||||
data?: {
|
||||
usage?: {
|
||||
input_tokens?: number;
|
||||
output_tokens?: number;
|
||||
cache_read_input_tokens?: number;
|
||||
cache_creation_input_tokens?: number;
|
||||
};
|
||||
};
|
||||
};
|
||||
94
src/agents/main-agent-runtime-factory.ts
Normal file
94
src/agents/main-agent-runtime-factory.ts
Normal file
@ -0,0 +1,94 @@
|
||||
/**
|
||||
* Main agent runtime factory.
|
||||
*
|
||||
* Resolves and creates the appropriate agent runtime based on configuration.
|
||||
*/
|
||||
|
||||
import type { MoltbotConfig } from "../config/config.js";
|
||||
import type { AgentRuntimeKind } from "../config/types.agent-defaults.js";
|
||||
import type { AgentRuntime } from "./agent-runtime.js";
|
||||
import { resolveAgentConfig } from "./agent-scope.js";
|
||||
import { createPiAgentRuntime } from "./pi-agent-runtime.js";
|
||||
import { createSubsystemLogger } from "../logging/subsystem.js";
|
||||
|
||||
const log = createSubsystemLogger("agents/runtime-factory");
|
||||
|
||||
/** Default runtime when none is configured. */
|
||||
const DEFAULT_RUNTIME: AgentRuntimeKind = "pi";
|
||||
|
||||
/**
|
||||
* Resolve the runtime kind for an agent.
|
||||
*
|
||||
* Resolution order:
|
||||
* 1. Per-agent runtime (agents.list[].runtime)
|
||||
* 2. Default runtime (agents.defaults.runtime)
|
||||
* 3. Fallback to "pi"
|
||||
*/
|
||||
export function resolveAgentRuntimeKind(config: MoltbotConfig, agentId: string): AgentRuntimeKind {
|
||||
// Check per-agent config first
|
||||
const agentConfig = resolveAgentConfig(config, agentId);
|
||||
if (agentConfig?.runtime === "pi" || agentConfig?.runtime === "ccsdk") {
|
||||
return agentConfig.runtime;
|
||||
}
|
||||
|
||||
// Fall back to defaults
|
||||
const defaultRuntime = config.agents?.defaults?.runtime;
|
||||
if (defaultRuntime === "pi" || defaultRuntime === "ccsdk") {
|
||||
return defaultRuntime;
|
||||
}
|
||||
|
||||
return DEFAULT_RUNTIME;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an agent runtime for the specified agent.
|
||||
*
|
||||
* @param config - Moltbot configuration
|
||||
* @param agentId - Agent identifier
|
||||
* @returns The appropriate AgentRuntime instance
|
||||
*/
|
||||
export async function createAgentRuntime(
|
||||
config: MoltbotConfig,
|
||||
agentId: string,
|
||||
): Promise<AgentRuntime> {
|
||||
const runtimeKind = resolveAgentRuntimeKind(config, agentId);
|
||||
|
||||
log.debug("Creating agent runtime", { agentId, runtime: runtimeKind });
|
||||
|
||||
if (runtimeKind === "ccsdk") {
|
||||
// Dynamically import to avoid loading SDK when not needed
|
||||
const { createCcSdkAgentRuntime, isSdkAvailable } = await import("./claude-agent-sdk/index.js");
|
||||
|
||||
if (!isSdkAvailable()) {
|
||||
log.warn("CCSDK runtime requested but SDK not available, falling back to Pi runtime", {
|
||||
agentId,
|
||||
});
|
||||
return createPiAgentRuntime();
|
||||
}
|
||||
|
||||
// Get CCSDK-specific config from the agent entry
|
||||
const agentConfig = resolveAgentConfig(config, agentId);
|
||||
|
||||
return createCcSdkAgentRuntime({
|
||||
config,
|
||||
ccsdkConfig: agentConfig?.ccsdk,
|
||||
});
|
||||
}
|
||||
|
||||
// Default to Pi runtime
|
||||
return createPiAgentRuntime();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the Claude Code SDK runtime is available.
|
||||
*
|
||||
* This can be used to conditionally show CCSDK-related options in the UI.
|
||||
*/
|
||||
export async function isCcSdkRuntimeAvailable(): Promise<boolean> {
|
||||
try {
|
||||
const { isSdkAvailable } = await import("./claude-agent-sdk/index.js");
|
||||
return isSdkAvailable();
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
68
src/agents/pi-agent-runtime.ts
Normal file
68
src/agents/pi-agent-runtime.ts
Normal file
@ -0,0 +1,68 @@
|
||||
/**
|
||||
* Pi Agent runtime implementation.
|
||||
*
|
||||
* Wraps the existing runEmbeddedPiAgent function to conform to the AgentRuntime interface.
|
||||
*/
|
||||
|
||||
import type { AgentRuntime, AgentRuntimeRunParams, AgentRuntimeResult } from "./agent-runtime.js";
|
||||
import { runEmbeddedPiAgent } from "./pi-embedded.js";
|
||||
|
||||
/**
|
||||
* Create a Pi Agent runtime instance.
|
||||
*
|
||||
* The Pi Agent runtime is the default backend that uses the Pi AI SDK
|
||||
* for model execution with moltbot's tool system.
|
||||
*/
|
||||
export function createPiAgentRuntime(): AgentRuntime {
|
||||
return {
|
||||
kind: "pi",
|
||||
displayName: "Pi Agent",
|
||||
|
||||
async run(params: AgentRuntimeRunParams): Promise<AgentRuntimeResult> {
|
||||
return runEmbeddedPiAgent({
|
||||
sessionId: params.sessionId,
|
||||
sessionKey: params.sessionKey,
|
||||
sessionFile: params.sessionFile,
|
||||
workspaceDir: params.workspaceDir,
|
||||
agentDir: params.agentDir,
|
||||
config: params.config,
|
||||
skillsSnapshot: params.skillsSnapshot,
|
||||
prompt: params.prompt,
|
||||
images: params.images,
|
||||
provider: params.provider,
|
||||
model: params.model,
|
||||
authProfileId: params.authProfileId,
|
||||
authProfileIdSource: params.authProfileIdSource,
|
||||
thinkLevel: params.thinkLevel,
|
||||
verboseLevel: params.verboseLevel,
|
||||
timeoutMs: params.timeoutMs,
|
||||
runId: params.runId,
|
||||
lane: params.lane,
|
||||
abortSignal: params.abortSignal,
|
||||
extraSystemPrompt: params.extraSystemPrompt,
|
||||
streamParams: params.streamParams,
|
||||
// Messaging context
|
||||
messageChannel: params.messageChannel,
|
||||
agentAccountId: params.agentAccountId,
|
||||
messageTo: params.messageTo,
|
||||
messageThreadId: params.messageThreadId,
|
||||
groupId: params.groupId,
|
||||
groupChannel: params.groupChannel,
|
||||
groupSpace: params.groupSpace,
|
||||
spawnedBy: params.spawnedBy,
|
||||
currentChannelId: params.currentChannelId,
|
||||
currentThreadTs: params.currentThreadTs,
|
||||
replyToMode: params.replyToMode,
|
||||
hasRepliedRef: params.hasRepliedRef,
|
||||
// Callbacks
|
||||
onPartialReply: params.onPartialReply,
|
||||
onAssistantMessageStart: params.onAssistantMessageStart,
|
||||
onBlockReply: params.onBlockReply,
|
||||
onBlockReplyFlush: params.onBlockReplyFlush,
|
||||
onReasoningStream: params.onReasoningStream,
|
||||
onToolResult: params.onToolResult,
|
||||
onAgentEvent: params.onAgentEvent,
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
@ -12,6 +12,9 @@ import type {
|
||||
} from "./types.sandbox.js";
|
||||
import type { MemorySearchConfig } from "./types.tools.js";
|
||||
|
||||
/** Agent runtime backend discriminant. */
|
||||
export type AgentRuntimeKind = "pi" | "ccsdk";
|
||||
|
||||
export type AgentModelEntryConfig = {
|
||||
alias?: string;
|
||||
/** Provider-specific API parameters (e.g., GLM-4.7 thinking mode). */
|
||||
@ -92,6 +95,8 @@ export type CliBackendConfig = {
|
||||
};
|
||||
|
||||
export type AgentDefaultsConfig = {
|
||||
/** Agent runtime backend (pi or sdk). Default: "pi". */
|
||||
runtime?: AgentRuntimeKind;
|
||||
/** Primary model and fallbacks (provider/model). */
|
||||
model?: AgentModelListConfig;
|
||||
/** Optional image-capable model and fallbacks (provider/model). */
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import type { AgentDefaultsConfig } from "./types.agent-defaults.js";
|
||||
import type { AgentDefaultsConfig, AgentRuntimeKind } from "./types.agent-defaults.js";
|
||||
import type { HumanDelayConfig, IdentityConfig } from "./types.base.js";
|
||||
import type { GroupChatConfig } from "./types.messages.js";
|
||||
import type {
|
||||
@ -21,6 +21,10 @@ export type AgentConfig = {
|
||||
id: string;
|
||||
default?: boolean;
|
||||
name?: string;
|
||||
/** Agent runtime backend (pi or ccsdk). Overrides agents.defaults.runtime. */
|
||||
runtime?: AgentRuntimeKind;
|
||||
/** Claude Code SDK configuration when runtime is "ccsdk". */
|
||||
ccsdk?: AgentCcSdkConfig;
|
||||
workspace?: string;
|
||||
agentDir?: string;
|
||||
model?: AgentModelConfig;
|
||||
@ -62,6 +66,26 @@ export type AgentConfig = {
|
||||
tools?: AgentToolsConfig;
|
||||
};
|
||||
|
||||
/** Model tier configuration for Claude Code SDK. */
|
||||
export type CcSdkModelTiers = {
|
||||
/** Model for fast/simple tasks (maps to ANTHROPIC_DEFAULT_HAIKU_MODEL). */
|
||||
haiku?: string;
|
||||
/** Model for balanced tasks (maps to ANTHROPIC_DEFAULT_SONNET_MODEL). */
|
||||
sonnet?: string;
|
||||
/** Model for complex reasoning (maps to ANTHROPIC_DEFAULT_OPUS_MODEL). */
|
||||
opus?: string;
|
||||
};
|
||||
|
||||
/** Claude Code SDK runtime configuration. */
|
||||
export type AgentCcSdkConfig = {
|
||||
/** Enable Claude Code lifecycle hooks (pre-tool, post-tool, etc.). */
|
||||
hooksEnabled?: boolean;
|
||||
/** Additional SDK options passed to the runner. */
|
||||
options?: Record<string, unknown>;
|
||||
/** 3-tier model configuration for Claude Code SDK. */
|
||||
models?: CcSdkModelTiers;
|
||||
};
|
||||
|
||||
export type AgentsConfig = {
|
||||
defaults?: AgentDefaultsConfig;
|
||||
list?: AgentConfig[];
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { z } from "zod";
|
||||
import {
|
||||
AgentRuntimeKindSchema,
|
||||
HeartbeatSchema,
|
||||
MemorySearchSchema,
|
||||
SandboxBrowserSchema,
|
||||
@ -13,8 +14,12 @@ import {
|
||||
HumanDelaySchema,
|
||||
} from "./zod-schema.core.js";
|
||||
|
||||
// Re-export for consumers
|
||||
export { AgentRuntimeKindSchema };
|
||||
|
||||
export const AgentDefaultsSchema = z
|
||||
.object({
|
||||
runtime: AgentRuntimeKindSchema.optional(),
|
||||
model: z
|
||||
.object({
|
||||
primary: z.string().optional(),
|
||||
|
||||
@ -413,11 +413,36 @@ export const AgentModelSchema = z.union([
|
||||
})
|
||||
.strict(),
|
||||
]);
|
||||
/** Agent runtime backend discriminant. */
|
||||
export const AgentRuntimeKindSchema = z.union([z.literal("pi"), z.literal("ccsdk")]);
|
||||
|
||||
/** Model tier configuration for Claude Code SDK. */
|
||||
export const CcSdkModelTiersSchema = z
|
||||
.object({
|
||||
haiku: z.string().optional(),
|
||||
sonnet: z.string().optional(),
|
||||
opus: z.string().optional(),
|
||||
})
|
||||
.strict()
|
||||
.optional();
|
||||
|
||||
/** Claude Code SDK runtime configuration schema. */
|
||||
export const AgentCcSdkConfigSchema = z
|
||||
.object({
|
||||
hooksEnabled: z.boolean().optional(),
|
||||
options: z.record(z.string(), z.unknown()).optional(),
|
||||
models: CcSdkModelTiersSchema,
|
||||
})
|
||||
.strict()
|
||||
.optional();
|
||||
|
||||
export const AgentEntrySchema = z
|
||||
.object({
|
||||
id: z.string(),
|
||||
default: z.boolean().optional(),
|
||||
name: z.string().optional(),
|
||||
runtime: AgentRuntimeKindSchema.optional(),
|
||||
ccsdk: AgentCcSdkConfigSchema,
|
||||
workspace: z.string().optional(),
|
||||
agentDir: z.string().optional(),
|
||||
model: AgentModelSchema.optional(),
|
||||
|
||||
Loading…
Reference in New Issue
Block a user