nodal-agents 0.4.0 → 0.4.2
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 +38 -14
- package/cli.js +16 -1
- package/migrations/0030_agent_fallback_llm_keys.sql +6 -0
- package/migrations/0031_llm_key_capabilities.sql +7 -0
- package/migrations/0032_drop_llm_key_model_caps.sql +9 -0
- package/migrations/0033_agent_fallback_chain.sql +21 -0
- package/migrations/meta/_journal.json +28 -0
- package/package.json +1 -1
- package/runner.js +512 -79
- package/web/.next/BUILD_ID +1 -1
- package/web/.next/build-manifest.json +2 -2
- package/web/.next/prerender-manifest.json +3 -3
- package/web/.next/server/app/(dashboard)/agents/[id]/edit/page.js +3 -3
- package/web/.next/server/app/(dashboard)/agents/[id]/edit/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/agents/[id]/edit/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/agents/[id]/telegram/page.js +1 -1
- package/web/.next/server/app/(dashboard)/agents/[id]/telegram/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/agents/[id]/telegram/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/agents/page.js +2 -2
- package/web/.next/server/app/(dashboard)/agents/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/agents/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/approvals/page.js +2 -2
- package/web/.next/server/app/(dashboard)/approvals/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/approvals/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/automations/page.js +1 -1
- package/web/.next/server/app/(dashboard)/automations/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/automations/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/billing/page.js +1 -1
- package/web/.next/server/app/(dashboard)/billing/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/billing/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/chat/page.js +2 -2
- package/web/.next/server/app/(dashboard)/chat/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/chat/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/connectors/page.js +1 -1
- package/web/.next/server/app/(dashboard)/connectors/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/connectors/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/credentials/page.js +1 -1
- package/web/.next/server/app/(dashboard)/credentials/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/credentials/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/jobs/[id]/page.js +2 -2
- package/web/.next/server/app/(dashboard)/jobs/[id]/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/jobs/[id]/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/jobs/page.js +2 -2
- package/web/.next/server/app/(dashboard)/jobs/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/jobs/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/llm-providers/page.js +2 -2
- package/web/.next/server/app/(dashboard)/llm-providers/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/llm-providers/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/logs/page.js +1 -1
- package/web/.next/server/app/(dashboard)/logs/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/logs/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/mcp/page.js +2 -2
- package/web/.next/server/app/(dashboard)/mcp/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/mcp/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/memories/page.js +2 -2
- package/web/.next/server/app/(dashboard)/memories/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/memories/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/page.js +3 -3
- package/web/.next/server/app/(dashboard)/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/settings/page.js +2 -2
- package/web/.next/server/app/(dashboard)/settings/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/settings/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/skills/[id]/edit/page.js +1 -1
- package/web/.next/server/app/(dashboard)/skills/[id]/edit/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/skills/[id]/edit/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/skills/new/page.js +2 -2
- package/web/.next/server/app/(dashboard)/skills/new/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/skills/new/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/skills/page.js +2 -2
- package/web/.next/server/app/(dashboard)/skills/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/skills/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/_global-error.html +1 -1
- package/web/.next/server/app/_global-error.rsc +2 -2
- package/web/.next/server/app/_global-error.segments/_full.segment.rsc +2 -2
- package/web/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/web/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/web/.next/server/app/_global-error.segments/_index.segment.rsc +2 -2
- package/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/_not-found.html +1 -1
- package/web/.next/server/app/_not-found.rsc +3 -3
- package/web/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
- package/web/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/web/.next/server/app/_not-found.segments/_index.segment.rsc +3 -3
- package/web/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
- package/web/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/web/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/web/.next/server/app/api/oauth/[provider]/callback/route.js +1 -1
- package/web/.next/server/app/api/oauth/[provider]/start/route.js +1 -1
- package/web/.next/server/app/login/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/onboarding/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/onboarding.html +1 -1
- package/web/.next/server/app/onboarding.rsc +3 -3
- package/web/.next/server/app/onboarding.segments/_full.segment.rsc +3 -3
- package/web/.next/server/app/onboarding.segments/_head.segment.rsc +1 -1
- package/web/.next/server/app/onboarding.segments/_index.segment.rsc +3 -3
- package/web/.next/server/app/onboarding.segments/_tree.segment.rsc +2 -2
- package/web/.next/server/app/onboarding.segments/onboarding/__PAGE__.segment.rsc +1 -1
- package/web/.next/server/app/onboarding.segments/onboarding.segment.rsc +1 -1
- package/web/.next/server/chunks/1945.js +1 -0
- package/web/.next/server/chunks/3233.js +1 -0
- package/web/.next/server/chunks/4574.js +1 -1
- package/web/.next/server/chunks/4808.js +1 -0
- package/web/.next/server/chunks/4839.js +1 -0
- package/web/.next/server/chunks/{3057.js → 7466.js} +1 -1
- package/web/.next/server/chunks/7741.js +3 -3
- package/web/.next/server/chunks/8206.js +1 -0
- package/web/.next/server/chunks/8398.js +1 -0
- package/web/.next/server/chunks/{7557.js → 9606.js} +2 -2
- package/web/.next/server/middleware-build-manifest.js +1 -1
- package/web/.next/server/pages/404.html +1 -1
- package/web/.next/server/pages/500.html +1 -1
- package/web/.next/server/server-reference-manifest.js +1 -1
- package/web/.next/server/server-reference-manifest.json +1 -1
- package/web/.next/static/chunks/{9060-df7c0c4c6fa27737.js → 2575-e660568bd1a9bcb6.js} +2 -2
- package/web/.next/static/chunks/3233-e6efb7fb1fa24591.js +1 -0
- package/web/.next/static/chunks/5436-c1006a40e59853ed.js +1 -0
- package/web/.next/static/chunks/7025-7afa82fda10bddc4.js +62 -0
- package/web/.next/static/chunks/{5801-e411029984b17b8b.js → 8396-f3502b9af3172006.js} +1 -1
- package/web/.next/static/chunks/{8503-ced632da5c3fce79.js → 9098-2bfef80a73c706b3.js} +1 -1
- package/web/.next/static/chunks/9123-20653d928e33410a.js +1 -0
- package/web/.next/static/chunks/{6679-7c76034b83edeb06.js → 9582-fbf7c8d9b2a39101.js} +1 -1
- package/web/.next/static/chunks/app/(dashboard)/agents/[id]/edit/page-a8e293c54c818084.js +2 -0
- package/web/.next/static/chunks/app/(dashboard)/agents/[id]/telegram/{page-e6b35d5f361044a9.js → page-7a94ae67b2c3c9c3.js} +1 -1
- package/web/.next/static/chunks/app/(dashboard)/agents/page-b258b8975ac6450b.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/approvals/page-79dea6e91956eeba.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/automations/page-3b863b7af8e2c1a3.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/chat/page-4d965bb7ee3732db.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/connectors/page-4a437ba82f4086da.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/jobs/[id]/page-be20dcbf25c8f3ce.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/jobs/page-94a311f688a255d8.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/layout-e1b0d4fad2926646.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/llm-providers/page-e5e2c4e2b783d37f.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/mcp/page-c071c54f76273ac4.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/memories/page-8ca0b34ad35eb1fa.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/{page-fc49d7ed8e472118.js → page-fb50e1576a3ab2e4.js} +1 -1
- package/web/.next/static/chunks/app/(dashboard)/settings/page-7b256e9c462e97f8.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/skills/[id]/edit/page-12930816795e8b20.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/skills/new/page-e3a19abaf7468db9.js +1 -0
- package/web/.next/static/chunks/app/(dashboard)/skills/page-43f1475a0bc9c45f.js +1 -0
- package/web/.next/static/css/78ead23854ab041e.css +3 -0
- package/web/.next/server/chunks/1511.js +0 -1
- package/web/.next/server/chunks/2103.js +0 -1
- package/web/.next/server/chunks/211.js +0 -1
- package/web/.next/server/chunks/8178.js +0 -1
- package/web/.next/server/chunks/9201.js +0 -1
- package/web/.next/server/chunks/9824.js +0 -1
- package/web/.next/static/chunks/1165-ec573be2aa63710b.js +0 -1
- package/web/.next/static/chunks/2569-6b5e0af9c1f584a4.js +0 -1
- package/web/.next/static/chunks/6522-3f865de55adb618d.js +0 -1
- package/web/.next/static/chunks/921-f437093debcddbb3.js +0 -1
- package/web/.next/static/chunks/9421-d522a48618c4fe37.js +0 -62
- package/web/.next/static/chunks/app/(dashboard)/agents/[id]/edit/page-d3724fbf38b71806.js +0 -2
- package/web/.next/static/chunks/app/(dashboard)/agents/page-b58294bf588f4581.js +0 -1
- package/web/.next/static/chunks/app/(dashboard)/approvals/page-b9e504918d043b6d.js +0 -1
- package/web/.next/static/chunks/app/(dashboard)/automations/page-4807e81e2af3030e.js +0 -1
- package/web/.next/static/chunks/app/(dashboard)/chat/page-2c8f9571a443f250.js +0 -1
- package/web/.next/static/chunks/app/(dashboard)/connectors/page-72ccb0e3a5ed6f2d.js +0 -1
- package/web/.next/static/chunks/app/(dashboard)/jobs/[id]/page-40172a14d0b1368f.js +0 -1
- package/web/.next/static/chunks/app/(dashboard)/jobs/page-d4a3a16745e02fd1.js +0 -1
- package/web/.next/static/chunks/app/(dashboard)/layout-4d5634ba460464d7.js +0 -1
- package/web/.next/static/chunks/app/(dashboard)/llm-providers/page-90fb785e2ab32759.js +0 -1
- package/web/.next/static/chunks/app/(dashboard)/mcp/page-426478332dfe8313.js +0 -1
- package/web/.next/static/chunks/app/(dashboard)/memories/page-aa46f5f7efbfa262.js +0 -1
- package/web/.next/static/chunks/app/(dashboard)/settings/page-1cc10beb46234c7d.js +0 -1
- package/web/.next/static/chunks/app/(dashboard)/skills/[id]/edit/page-0b61f21847f4c7a0.js +0 -1
- package/web/.next/static/chunks/app/(dashboard)/skills/new/page-9de96e643c361732.js +0 -1
- package/web/.next/static/chunks/app/(dashboard)/skills/page-4566512d74e54bfe.js +0 -1
- package/web/.next/static/css/0a81480f93d3ab37.css +0 -3
- /package/web/.next/static/{9FXcaPSw8KYgjKzjKLpT2 → ZuUX-HBTQOhLf0tFI6JQI}/_buildManifest.js +0 -0
- /package/web/.next/static/{9FXcaPSw8KYgjKzjKLpT2 → ZuUX-HBTQOhLf0tFI6JQI}/_ssgManifest.js +0 -0
package/runner.js
CHANGED
|
@@ -1102,6 +1102,143 @@ var init_connector_catalog = __esm({
|
|
|
1102
1102
|
}
|
|
1103
1103
|
});
|
|
1104
1104
|
|
|
1105
|
+
// ../../packages/shared/src/model-catalog.ts
|
|
1106
|
+
function findModelCatalogEntry(provider, modelId) {
|
|
1107
|
+
return MODEL_CATALOG[provider]?.find((e) => e.modelId === modelId);
|
|
1108
|
+
}
|
|
1109
|
+
var MODEL_CATALOG;
|
|
1110
|
+
var init_model_catalog = __esm({
|
|
1111
|
+
"../../packages/shared/src/model-catalog.ts"() {
|
|
1112
|
+
"use strict";
|
|
1113
|
+
MODEL_CATALOG = {
|
|
1114
|
+
anthropic: [
|
|
1115
|
+
{
|
|
1116
|
+
modelId: "claude-opus-4-8",
|
|
1117
|
+
label: "Claude Opus 4.8",
|
|
1118
|
+
capabilities: { tools: true, forcedToolChoice: true }
|
|
1119
|
+
},
|
|
1120
|
+
{
|
|
1121
|
+
modelId: "claude-sonnet-4-6",
|
|
1122
|
+
label: "Claude Sonnet 4.6",
|
|
1123
|
+
capabilities: { tools: true, forcedToolChoice: true }
|
|
1124
|
+
},
|
|
1125
|
+
{
|
|
1126
|
+
modelId: "claude-haiku-4-5-20251001",
|
|
1127
|
+
label: "Claude Haiku 4.5",
|
|
1128
|
+
capabilities: { tools: true, forcedToolChoice: true }
|
|
1129
|
+
}
|
|
1130
|
+
],
|
|
1131
|
+
openai: [
|
|
1132
|
+
{ modelId: "gpt-5", label: "GPT-5", capabilities: { tools: true, forcedToolChoice: true } },
|
|
1133
|
+
{
|
|
1134
|
+
modelId: "gpt-5-mini",
|
|
1135
|
+
label: "GPT-5 mini",
|
|
1136
|
+
capabilities: { tools: true, forcedToolChoice: true }
|
|
1137
|
+
}
|
|
1138
|
+
],
|
|
1139
|
+
google: [
|
|
1140
|
+
{
|
|
1141
|
+
modelId: "gemini-2.0-flash",
|
|
1142
|
+
label: "Gemini 2.0 Flash",
|
|
1143
|
+
capabilities: { tools: true, forcedToolChoice: true }
|
|
1144
|
+
},
|
|
1145
|
+
{
|
|
1146
|
+
modelId: "gemini-2.5-pro",
|
|
1147
|
+
label: "Gemini 2.5 Pro",
|
|
1148
|
+
capabilities: { tools: true, forcedToolChoice: true }
|
|
1149
|
+
}
|
|
1150
|
+
],
|
|
1151
|
+
groq: [
|
|
1152
|
+
{
|
|
1153
|
+
modelId: "llama-3.3-70b-versatile",
|
|
1154
|
+
label: "Llama 3.3 70B",
|
|
1155
|
+
capabilities: { tools: true, forcedToolChoice: true }
|
|
1156
|
+
}
|
|
1157
|
+
],
|
|
1158
|
+
mistral: [
|
|
1159
|
+
{
|
|
1160
|
+
modelId: "mistral-large-latest",
|
|
1161
|
+
label: "Mistral Large",
|
|
1162
|
+
capabilities: { tools: true, forcedToolChoice: true }
|
|
1163
|
+
}
|
|
1164
|
+
],
|
|
1165
|
+
// OpenRouter models are namespaced by sub-vendor (anthropic/, deepseek/, …).
|
|
1166
|
+
// The UI groups them by that vendor (see modelGroupLabel). Tested + working
|
|
1167
|
+
// routes; all accept a forced tool_choice.
|
|
1168
|
+
openrouter: [
|
|
1169
|
+
// Anthropic
|
|
1170
|
+
{
|
|
1171
|
+
modelId: "anthropic/claude-haiku-4.5",
|
|
1172
|
+
label: "Claude Haiku 4.5",
|
|
1173
|
+
capabilities: { tools: true, forcedToolChoice: true }
|
|
1174
|
+
},
|
|
1175
|
+
{
|
|
1176
|
+
modelId: "anthropic/claude-opus-4.7",
|
|
1177
|
+
label: "Claude Opus 4.7",
|
|
1178
|
+
capabilities: { tools: true, forcedToolChoice: true }
|
|
1179
|
+
},
|
|
1180
|
+
{
|
|
1181
|
+
modelId: "anthropic/claude-opus-4.7-fast",
|
|
1182
|
+
label: "Claude Opus 4.7 (fast)",
|
|
1183
|
+
capabilities: { tools: true, forcedToolChoice: true }
|
|
1184
|
+
},
|
|
1185
|
+
{
|
|
1186
|
+
modelId: "anthropic/claude-opus-4.8",
|
|
1187
|
+
label: "Claude Opus 4.8",
|
|
1188
|
+
capabilities: { tools: true, forcedToolChoice: true }
|
|
1189
|
+
},
|
|
1190
|
+
{
|
|
1191
|
+
modelId: "anthropic/claude-opus-4.8-fast",
|
|
1192
|
+
label: "Claude Opus 4.8 (fast)",
|
|
1193
|
+
capabilities: { tools: true, forcedToolChoice: true }
|
|
1194
|
+
},
|
|
1195
|
+
{
|
|
1196
|
+
modelId: "anthropic/claude-sonnet-4.6",
|
|
1197
|
+
label: "Claude Sonnet 4.6",
|
|
1198
|
+
capabilities: { tools: true, forcedToolChoice: true }
|
|
1199
|
+
},
|
|
1200
|
+
// DeepSeek
|
|
1201
|
+
{
|
|
1202
|
+
modelId: "deepseek/deepseek-v3.2",
|
|
1203
|
+
label: "DeepSeek V3.2",
|
|
1204
|
+
capabilities: { tools: true, forcedToolChoice: true }
|
|
1205
|
+
},
|
|
1206
|
+
{
|
|
1207
|
+
modelId: "deepseek/deepseek-v4-flash",
|
|
1208
|
+
label: "DeepSeek V4 Flash",
|
|
1209
|
+
capabilities: { tools: true, forcedToolChoice: true }
|
|
1210
|
+
},
|
|
1211
|
+
{
|
|
1212
|
+
modelId: "deepseek/deepseek-v4-pro",
|
|
1213
|
+
label: "DeepSeek V4 Pro",
|
|
1214
|
+
capabilities: { tools: true, forcedToolChoice: true }
|
|
1215
|
+
},
|
|
1216
|
+
// Google
|
|
1217
|
+
{
|
|
1218
|
+
modelId: "google/gemini-3.1-flash-lite-preview",
|
|
1219
|
+
label: "Gemini 3.1 Flash Lite (preview)",
|
|
1220
|
+
capabilities: { tools: true, forcedToolChoice: true }
|
|
1221
|
+
},
|
|
1222
|
+
{
|
|
1223
|
+
modelId: "google/gemini-3.1-pro-preview",
|
|
1224
|
+
label: "Gemini 3.1 Pro (preview)",
|
|
1225
|
+
capabilities: { tools: true, forcedToolChoice: true }
|
|
1226
|
+
},
|
|
1227
|
+
{
|
|
1228
|
+
modelId: "google/gemini-3.5-flash",
|
|
1229
|
+
label: "Gemini 3.5 Flash",
|
|
1230
|
+
capabilities: { tools: true, forcedToolChoice: true }
|
|
1231
|
+
},
|
|
1232
|
+
{
|
|
1233
|
+
modelId: "google/gemma-4-31b-it",
|
|
1234
|
+
label: "Gemma 4 31B-IT",
|
|
1235
|
+
capabilities: { tools: true, forcedToolChoice: true }
|
|
1236
|
+
}
|
|
1237
|
+
]
|
|
1238
|
+
};
|
|
1239
|
+
}
|
|
1240
|
+
});
|
|
1241
|
+
|
|
1105
1242
|
// ../../packages/shared/src/index.ts
|
|
1106
1243
|
var init_src = __esm({
|
|
1107
1244
|
"../../packages/shared/src/index.ts"() {
|
|
@@ -1127,6 +1264,7 @@ var init_src = __esm({
|
|
|
1127
1264
|
init_providers();
|
|
1128
1265
|
init_root_agent();
|
|
1129
1266
|
init_connector_catalog();
|
|
1267
|
+
init_model_catalog();
|
|
1130
1268
|
}
|
|
1131
1269
|
});
|
|
1132
1270
|
|
|
@@ -1262,7 +1400,6 @@ var init_llm_keys = __esm({
|
|
|
1262
1400
|
apiKeyLast4: text3("api_key_last4").notNull().default(""),
|
|
1263
1401
|
baseUrl: text3("base_url"),
|
|
1264
1402
|
nickname: text3("nickname"),
|
|
1265
|
-
defaultModel: text3("default_model"),
|
|
1266
1403
|
isActive: boolean2("is_active").notNull().default(true),
|
|
1267
1404
|
createdAt: timestamp3("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
1268
1405
|
updatedAt: timestamp3("updated_at", { withTimezone: true }).notNull().defaultNow()
|
|
@@ -1281,6 +1418,7 @@ import {
|
|
|
1281
1418
|
integer,
|
|
1282
1419
|
bigint,
|
|
1283
1420
|
timestamp as timestamp4,
|
|
1421
|
+
jsonb as jsonb2,
|
|
1284
1422
|
index as index3,
|
|
1285
1423
|
check as check2
|
|
1286
1424
|
} from "drizzle-orm/pg-core";
|
|
@@ -1301,6 +1439,14 @@ var init_agents = __esm({
|
|
|
1301
1439
|
personality: text4("personality").notNull(),
|
|
1302
1440
|
model: text4("model").default("claude-sonnet-4-6-20260217"),
|
|
1303
1441
|
llmKeyId: uuid4("llm_key_id").references(() => entityLlmKeys.id, { onDelete: "set null" }),
|
|
1442
|
+
// Ordered LLM-key fallback chain (Guard 2). When the primary key
|
|
1443
|
+
// (llmKeyId) exhausts retries / times out / hits quota mid-job, the runner
|
|
1444
|
+
// fails over to these in order; all-down fails loud (`all_providers_failed`).
|
|
1445
|
+
// Empty = no failover (default). Each link is a (keyId, model) pair so a
|
|
1446
|
+
// fallback runs on a CHOSEN model (empty model ⇒ that provider's catalog
|
|
1447
|
+
// default). FK integrity is enforced in the app layer; a deleted key is
|
|
1448
|
+
// skipped at resolution time.
|
|
1449
|
+
fallbackChain: jsonb2("fallback_chain").$type().default(sql2`'[]'::jsonb`),
|
|
1304
1450
|
active: boolean3("active").default(true),
|
|
1305
1451
|
isDefault: boolean3("is_default").default(false),
|
|
1306
1452
|
role: text4("role").default("agent"),
|
|
@@ -1380,7 +1526,7 @@ var init_agents = __esm({
|
|
|
1380
1526
|
});
|
|
1381
1527
|
|
|
1382
1528
|
// ../../packages/db/src/schema/jobs.ts
|
|
1383
|
-
import { pgTable as pgTable5, text as text5, uuid as uuid5, integer as integer2, jsonb as
|
|
1529
|
+
import { pgTable as pgTable5, text as text5, uuid as uuid5, integer as integer2, jsonb as jsonb3, timestamp as timestamp5, index as index4, check as check3 } from "drizzle-orm/pg-core";
|
|
1384
1530
|
import { sql as sql3 } from "drizzle-orm";
|
|
1385
1531
|
var agentJobs;
|
|
1386
1532
|
var init_jobs = __esm({
|
|
@@ -1400,7 +1546,7 @@ var init_jobs = __esm({
|
|
|
1400
1546
|
originalTask: text5("original_task"),
|
|
1401
1547
|
chatId: text5("chat_id"),
|
|
1402
1548
|
systemPrompt: text5("system_prompt"),
|
|
1403
|
-
messages:
|
|
1549
|
+
messages: jsonb3("messages").default(sql3`'[]'::jsonb`),
|
|
1404
1550
|
toolsUsed: text5("tools_used").array().default(sql3`'{}'::text[]`),
|
|
1405
1551
|
turn: integer2("turn").default(0),
|
|
1406
1552
|
result: text5("result"),
|
|
@@ -1428,7 +1574,7 @@ var init_jobs = __esm({
|
|
|
1428
1574
|
* different specialist (live regression: job `7767a3c1`, 2026-05-19).
|
|
1429
1575
|
*/
|
|
1430
1576
|
lastFailedDelegationSlug: text5("last_failed_delegation_slug"),
|
|
1431
|
-
pendingDelegation:
|
|
1577
|
+
pendingDelegation: jsonb3("pending_delegation"),
|
|
1432
1578
|
completedAt: timestamp5("completed_at", { withTimezone: true }),
|
|
1433
1579
|
createdAt: timestamp5("created_at", { withTimezone: true }).defaultNow(),
|
|
1434
1580
|
updatedAt: timestamp5("updated_at", { withTimezone: true }).defaultNow()
|
|
@@ -1463,7 +1609,7 @@ import {
|
|
|
1463
1609
|
text as text6,
|
|
1464
1610
|
uuid as uuid6,
|
|
1465
1611
|
integer as integer3,
|
|
1466
|
-
jsonb as
|
|
1612
|
+
jsonb as jsonb4,
|
|
1467
1613
|
timestamp as timestamp6,
|
|
1468
1614
|
numeric,
|
|
1469
1615
|
index as index5,
|
|
@@ -1499,7 +1645,7 @@ var init_tasks = __esm({
|
|
|
1499
1645
|
outputTokens: integer3("output_tokens").default(0),
|
|
1500
1646
|
costUsd: numeric("cost_usd", { precision: 10, scale: 6 }).default("0"),
|
|
1501
1647
|
dependsOn: uuid6("depends_on").array().default(sql4`'{}'::uuid[]`),
|
|
1502
|
-
context:
|
|
1648
|
+
context: jsonb4("context").default(sql4`'{}'::jsonb`),
|
|
1503
1649
|
rootJobId: uuid6("root_job_id"),
|
|
1504
1650
|
lockedAt: timestamp6("locked_at", { withTimezone: true }),
|
|
1505
1651
|
lockedBy: text6("locked_by"),
|
|
@@ -1607,7 +1753,7 @@ var init_connectors = __esm({
|
|
|
1607
1753
|
});
|
|
1608
1754
|
|
|
1609
1755
|
// ../../packages/db/src/schema/tool_calls.ts
|
|
1610
|
-
import { pgTable as pgTable9, text as text9, uuid as uuid9, integer as integer4, jsonb as
|
|
1756
|
+
import { pgTable as pgTable9, text as text9, uuid as uuid9, integer as integer4, jsonb as jsonb5, timestamp as timestamp9, index as index8 } from "drizzle-orm/pg-core";
|
|
1611
1757
|
import { sql as sql7 } from "drizzle-orm";
|
|
1612
1758
|
var toolCalls;
|
|
1613
1759
|
var init_tool_calls = __esm({
|
|
@@ -1622,7 +1768,7 @@ var init_tool_calls = __esm({
|
|
|
1622
1768
|
entityId: uuid9("entity_id").references(() => entities.id, { onDelete: "cascade" }),
|
|
1623
1769
|
jobId: uuid9("job_id").references(() => agentJobs.id, { onDelete: "cascade" }),
|
|
1624
1770
|
toolName: text9("tool_name").notNull(),
|
|
1625
|
-
toolInput:
|
|
1771
|
+
toolInput: jsonb5("tool_input"),
|
|
1626
1772
|
toolOutput: text9("tool_output"),
|
|
1627
1773
|
durationMs: integer4("duration_ms"),
|
|
1628
1774
|
turn: integer4("turn"),
|
|
@@ -1639,7 +1785,7 @@ var init_tool_calls = __esm({
|
|
|
1639
1785
|
});
|
|
1640
1786
|
|
|
1641
1787
|
// ../../packages/db/src/schema/approvals.ts
|
|
1642
|
-
import { pgTable as pgTable10, text as text10, uuid as uuid10, jsonb as
|
|
1788
|
+
import { pgTable as pgTable10, text as text10, uuid as uuid10, jsonb as jsonb6, timestamp as timestamp10, index as index9, check as check7 } from "drizzle-orm/pg-core";
|
|
1643
1789
|
import { sql as sql8 } from "drizzle-orm";
|
|
1644
1790
|
var approvalRequests, approvalRules;
|
|
1645
1791
|
var init_approvals = __esm({
|
|
@@ -1656,7 +1802,7 @@ var init_approvals = __esm({
|
|
|
1656
1802
|
jobId: uuid10("job_id").notNull().references(() => agentJobs.id, { onDelete: "cascade" }),
|
|
1657
1803
|
agentId: uuid10("agent_id").references(() => agents.id, { onDelete: "cascade" }),
|
|
1658
1804
|
toolName: text10("tool_name").notNull(),
|
|
1659
|
-
toolInput:
|
|
1805
|
+
toolInput: jsonb6("tool_input").notNull(),
|
|
1660
1806
|
status: text10("status").default("pending"),
|
|
1661
1807
|
requestedAt: timestamp10("requested_at", { withTimezone: true }).defaultNow(),
|
|
1662
1808
|
resolvedAt: timestamp10("resolved_at", { withTimezone: true }),
|
|
@@ -1691,7 +1837,7 @@ var init_approvals = __esm({
|
|
|
1691
1837
|
agentId: uuid10("agent_id").references(() => agents.id, { onDelete: "cascade" }),
|
|
1692
1838
|
toolName: text10("tool_name").notNull(),
|
|
1693
1839
|
action: text10("action").notNull(),
|
|
1694
|
-
conditionJson:
|
|
1840
|
+
conditionJson: jsonb6("condition_json").default(sql8`'{}'::jsonb`),
|
|
1695
1841
|
createdAt: timestamp10("created_at", { withTimezone: true }).defaultNow(),
|
|
1696
1842
|
updatedAt: timestamp10("updated_at", { withTimezone: true }).defaultNow()
|
|
1697
1843
|
},
|
|
@@ -1819,7 +1965,7 @@ import {
|
|
|
1819
1965
|
uuid as uuid13,
|
|
1820
1966
|
boolean as boolean7,
|
|
1821
1967
|
integer as integer7,
|
|
1822
|
-
jsonb as
|
|
1968
|
+
jsonb as jsonb7,
|
|
1823
1969
|
timestamp as timestamp13,
|
|
1824
1970
|
index as index12
|
|
1825
1971
|
} from "drizzle-orm/pg-core";
|
|
@@ -1843,8 +1989,8 @@ var init_skills = __esm({
|
|
|
1843
1989
|
description: text13("description"),
|
|
1844
1990
|
defaultContent: text13("default_content"),
|
|
1845
1991
|
contentOverridden: boolean7("content_overridden").default(false),
|
|
1846
|
-
requiredConfig:
|
|
1847
|
-
operations:
|
|
1992
|
+
requiredConfig: jsonb7("required_config").default(sql10`'[]'::jsonb`),
|
|
1993
|
+
operations: jsonb7("operations").default(sql10`'[]'::jsonb`),
|
|
1848
1994
|
requiredBuiltins: text13("required_builtins").array().notNull().default(sql10`'{}'::text[]`),
|
|
1849
1995
|
createdAt: timestamp13("created_at", { withTimezone: true }).defaultNow(),
|
|
1850
1996
|
updatedAt: timestamp13("updated_at", { withTimezone: true }).defaultNow()
|
|
@@ -1889,7 +2035,7 @@ var init_skills = __esm({
|
|
|
1889
2035
|
entityId: uuid13("entity_id").notNull().references(() => entities.id, { onDelete: "cascade" }),
|
|
1890
2036
|
agentId: uuid13("agent_id").notNull().references(() => agents.id, { onDelete: "cascade" }),
|
|
1891
2037
|
skillId: uuid13("skill_id").notNull().references(() => agentSkills.id, { onDelete: "cascade" }),
|
|
1892
|
-
approvalOverrides:
|
|
2038
|
+
approvalOverrides: jsonb7("approval_overrides").default(sql10`'{}'::jsonb`),
|
|
1893
2039
|
useCustomInstructions: boolean7("use_custom_instructions").notNull().default(false),
|
|
1894
2040
|
enabledOperations: text13("enabled_operations").array(),
|
|
1895
2041
|
createdAt: timestamp13("created_at", { withTimezone: true }).notNull().defaultNow()
|
|
@@ -1999,7 +2145,7 @@ import {
|
|
|
1999
2145
|
text as text16,
|
|
2000
2146
|
uuid as uuid16,
|
|
2001
2147
|
boolean as boolean10,
|
|
2002
|
-
jsonb as
|
|
2148
|
+
jsonb as jsonb8,
|
|
2003
2149
|
timestamp as timestamp16,
|
|
2004
2150
|
index as index15,
|
|
2005
2151
|
uniqueIndex as uniqueIndex2,
|
|
@@ -2023,7 +2169,7 @@ var init_mcp = __esm({
|
|
|
2023
2169
|
url: text16("url"),
|
|
2024
2170
|
command: text16("command"),
|
|
2025
2171
|
args: text16("args").array().default(sql13`'{}'::text[]`),
|
|
2026
|
-
envVars:
|
|
2172
|
+
envVars: jsonb8("env_vars").default(sql13`'{}'::jsonb`),
|
|
2027
2173
|
// Encrypted (enc:v1: blob) credential for HTTP MCP servers — same pattern
|
|
2028
2174
|
// as connectors.api_key. NULL for servers that need no auth.
|
|
2029
2175
|
apiKey: text16("api_key"),
|
|
@@ -2036,7 +2182,7 @@ var init_mcp = __esm({
|
|
|
2036
2182
|
// The literal header name or query param name (e.g. 'x-api-key', 'api_key').
|
|
2037
2183
|
authParamName: text16("auth_param_name"),
|
|
2038
2184
|
active: boolean10("active").default(true),
|
|
2039
|
-
availableTools:
|
|
2185
|
+
availableTools: jsonb8("available_tools"),
|
|
2040
2186
|
createdAt: timestamp16("created_at", { withTimezone: true }).defaultNow(),
|
|
2041
2187
|
updatedAt: timestamp16("updated_at", { withTimezone: true }).defaultNow()
|
|
2042
2188
|
},
|
|
@@ -2058,7 +2204,7 @@ var init_mcp = __esm({
|
|
|
2058
2204
|
entityId: uuid16("entity_id").notNull().references(() => entities.id, { onDelete: "cascade" }),
|
|
2059
2205
|
agentId: uuid16("agent_id").notNull().references(() => agents.id, { onDelete: "cascade" }),
|
|
2060
2206
|
mcpServerId: uuid16("mcp_server_id").notNull().references(() => mcpServers.id, { onDelete: "cascade" }),
|
|
2061
|
-
enabledTools:
|
|
2207
|
+
enabledTools: jsonb8("enabled_tools"),
|
|
2062
2208
|
createdAt: timestamp16("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
2063
2209
|
updatedAt: timestamp16("updated_at", { withTimezone: true }).notNull().defaultNow()
|
|
2064
2210
|
},
|
|
@@ -2077,7 +2223,7 @@ var init_mcp = __esm({
|
|
|
2077
2223
|
entityId: uuid16("entity_id").notNull().references(() => entities.id, { onDelete: "cascade" }),
|
|
2078
2224
|
slug: text16("slug").notNull(),
|
|
2079
2225
|
active: boolean10("active").notNull().default(true),
|
|
2080
|
-
toolConfig:
|
|
2226
|
+
toolConfig: jsonb8("tool_config").notNull().default(sql13`'{}'::jsonb`),
|
|
2081
2227
|
createdAt: timestamp16("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
2082
2228
|
updatedAt: timestamp16("updated_at", { withTimezone: true }).notNull().defaultNow()
|
|
2083
2229
|
},
|
|
@@ -2093,7 +2239,7 @@ import {
|
|
|
2093
2239
|
uuid as uuid17,
|
|
2094
2240
|
boolean as boolean11,
|
|
2095
2241
|
integer as integer9,
|
|
2096
|
-
jsonb as
|
|
2242
|
+
jsonb as jsonb9,
|
|
2097
2243
|
timestamp as timestamp17,
|
|
2098
2244
|
index as index16,
|
|
2099
2245
|
check as check12
|
|
@@ -2111,7 +2257,7 @@ var init_misc = __esm({
|
|
|
2111
2257
|
id: uuid17("id").primaryKey().defaultRandom(),
|
|
2112
2258
|
entityId: uuid17("entity_id").notNull().references(() => entities.id, { onDelete: "cascade" }),
|
|
2113
2259
|
agentId: uuid17("agent_id").references(() => agents.id, { onDelete: "cascade" }),
|
|
2114
|
-
messages:
|
|
2260
|
+
messages: jsonb9("messages").notNull().default(sql14`'[]'::jsonb`),
|
|
2115
2261
|
status: text17("status").notNull().default("active"),
|
|
2116
2262
|
turnCount: integer9("turn_count").notNull().default(0),
|
|
2117
2263
|
createdAt: timestamp17("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
@@ -2131,7 +2277,7 @@ var init_misc = __esm({
|
|
|
2131
2277
|
slug: text17("slug").notNull(),
|
|
2132
2278
|
description: text17("description"),
|
|
2133
2279
|
pluginType: text17("plugin_type").notNull(),
|
|
2134
|
-
config:
|
|
2280
|
+
config: jsonb9("config").default(sql14`'{}'::jsonb`),
|
|
2135
2281
|
active: boolean11("active").default(true),
|
|
2136
2282
|
hook: text17("hook").notNull(),
|
|
2137
2283
|
webhookUrl: text17("webhook_url"),
|
|
@@ -10307,6 +10453,21 @@ Caused by: ${underlyingCause.stack}`;
|
|
|
10307
10453
|
}
|
|
10308
10454
|
}
|
|
10309
10455
|
};
|
|
10456
|
+
var AllProvidersFailedError = class extends Error {
|
|
10457
|
+
code = "all_providers_failed";
|
|
10458
|
+
underlyingCause;
|
|
10459
|
+
constructor(providerCount, underlyingCause) {
|
|
10460
|
+
super(
|
|
10461
|
+
`All ${providerCount} LLM providers failed; last: ${formatCauseSummary(underlyingCause)}`
|
|
10462
|
+
);
|
|
10463
|
+
this.name = "AllProvidersFailedError";
|
|
10464
|
+
this.underlyingCause = underlyingCause;
|
|
10465
|
+
if (underlyingCause instanceof Error) {
|
|
10466
|
+
this.stack = `${this.stack}
|
|
10467
|
+
Caused by: ${underlyingCause.stack}`;
|
|
10468
|
+
}
|
|
10469
|
+
}
|
|
10470
|
+
};
|
|
10310
10471
|
function formatCauseSummary(cause) {
|
|
10311
10472
|
if (cause instanceof Error) {
|
|
10312
10473
|
const name = cause.name || "Error";
|
|
@@ -10587,6 +10748,48 @@ function sleep(ms) {
|
|
|
10587
10748
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
10588
10749
|
}
|
|
10589
10750
|
|
|
10751
|
+
// ../../packages/llm/src/tool-choice-floor.ts
|
|
10752
|
+
function isUnsupportedToolChoiceError(err) {
|
|
10753
|
+
let cur = err;
|
|
10754
|
+
for (let depth = 0; depth < 5 && cur; depth++) {
|
|
10755
|
+
if (!(cur instanceof Error)) return false;
|
|
10756
|
+
const parts = [cur.message ?? ""];
|
|
10757
|
+
const body = cur.responseBody;
|
|
10758
|
+
if (typeof body === "string") parts.push(body);
|
|
10759
|
+
const data = cur.data;
|
|
10760
|
+
if (data !== void 0 && data !== null) {
|
|
10761
|
+
try {
|
|
10762
|
+
parts.push(JSON.stringify(data));
|
|
10763
|
+
} catch {
|
|
10764
|
+
}
|
|
10765
|
+
}
|
|
10766
|
+
const text22 = parts.join(" ").toLowerCase();
|
|
10767
|
+
if (text22.includes("tool_choice") && (text22.includes("no endpoints") || text22.includes("not supported") || text22.includes("does not support") || text22.includes("unsupported") || text22.includes("invalid value"))) {
|
|
10768
|
+
return true;
|
|
10769
|
+
}
|
|
10770
|
+
const inner = cur.cause;
|
|
10771
|
+
if (inner === cur) return false;
|
|
10772
|
+
cur = inner;
|
|
10773
|
+
}
|
|
10774
|
+
return false;
|
|
10775
|
+
}
|
|
10776
|
+
async function generateWithToolChoiceFloor(run, toolChoice, label) {
|
|
10777
|
+
try {
|
|
10778
|
+
return await run();
|
|
10779
|
+
} catch (err) {
|
|
10780
|
+
const wasForced = toolChoice !== void 0 && toolChoice !== "auto";
|
|
10781
|
+
if (wasForced && isUnsupportedToolChoiceError(err)) {
|
|
10782
|
+
console.warn(
|
|
10783
|
+
`[tool_choice_relaxed] ${label}: provider rejected tool_choice=${JSON.stringify(
|
|
10784
|
+
toolChoice
|
|
10785
|
+
)} \u2014 retrying with 'auto'`
|
|
10786
|
+
);
|
|
10787
|
+
return run("auto");
|
|
10788
|
+
}
|
|
10789
|
+
throw err;
|
|
10790
|
+
}
|
|
10791
|
+
}
|
|
10792
|
+
|
|
10590
10793
|
// ../../packages/llm/src/providers/anthropic.ts
|
|
10591
10794
|
import { createAnthropic } from "@ai-sdk/anthropic";
|
|
10592
10795
|
function buildAnthropicModel(config) {
|
|
@@ -11068,21 +11271,27 @@ function createLlmClient(config) {
|
|
|
11068
11271
|
};
|
|
11069
11272
|
const clientGenerateText = async (args) => {
|
|
11070
11273
|
validateIfMessages(args);
|
|
11071
|
-
|
|
11072
|
-
|
|
11073
|
-
|
|
11074
|
-
|
|
11075
|
-
|
|
11076
|
-
|
|
11077
|
-
|
|
11078
|
-
|
|
11079
|
-
|
|
11080
|
-
|
|
11081
|
-
|
|
11082
|
-
|
|
11083
|
-
|
|
11274
|
+
const toolChoice = args.toolChoice;
|
|
11275
|
+
return generateWithToolChoiceFloor(
|
|
11276
|
+
(override) => withRetry(
|
|
11277
|
+
() => callWithTimeout(
|
|
11278
|
+
() => generateText({
|
|
11279
|
+
...args,
|
|
11280
|
+
model,
|
|
11281
|
+
...override ? { toolChoice: override } : {},
|
|
11282
|
+
// AI SDK native timeout via AbortSignal.timeout(). Survives
|
|
11283
|
+
// middleware wrapping unlike a passed-in abortSignal which their
|
|
11284
|
+
// internal retry can swallow.
|
|
11285
|
+
timeout: LLM_TIMEOUT_MS,
|
|
11286
|
+
// Disable AI SDK internal retry — we own retries via withRetry to
|
|
11287
|
+
// preserve typed error handling (Quota/MessageStructure/LLMTimeout).
|
|
11288
|
+
maxRetries: 0
|
|
11289
|
+
})
|
|
11290
|
+
),
|
|
11291
|
+
retryOpts
|
|
11084
11292
|
),
|
|
11085
|
-
|
|
11293
|
+
toolChoice,
|
|
11294
|
+
`${config.provider}/${config.model}`
|
|
11086
11295
|
);
|
|
11087
11296
|
};
|
|
11088
11297
|
const clientStreamText = (args) => {
|
|
@@ -11112,6 +11321,66 @@ function createLlmClient(config) {
|
|
|
11112
11321
|
};
|
|
11113
11322
|
}
|
|
11114
11323
|
|
|
11324
|
+
// ../../packages/llm/src/failover.ts
|
|
11325
|
+
function isFailoverWorthy(err) {
|
|
11326
|
+
return err instanceof RetryExhaustedError || err instanceof LLMTimeoutError || err instanceof QuotaExhaustedError;
|
|
11327
|
+
}
|
|
11328
|
+
function errLabel(err) {
|
|
11329
|
+
return err instanceof Error ? err.name : String(err);
|
|
11330
|
+
}
|
|
11331
|
+
function createFailoverFromClients(clients) {
|
|
11332
|
+
if (clients.length === 0) {
|
|
11333
|
+
throw new ProviderConfigError("failover: at least one client is required");
|
|
11334
|
+
}
|
|
11335
|
+
if (clients.length === 1) return clients[0];
|
|
11336
|
+
let activeIndex = 0;
|
|
11337
|
+
async function runWithFailover(op, label) {
|
|
11338
|
+
let lastErr;
|
|
11339
|
+
for (let i = activeIndex; i < clients.length; i++) {
|
|
11340
|
+
try {
|
|
11341
|
+
const result = await op(clients[i]);
|
|
11342
|
+
activeIndex = i;
|
|
11343
|
+
return result;
|
|
11344
|
+
} catch (err) {
|
|
11345
|
+
lastErr = err;
|
|
11346
|
+
if (!isFailoverWorthy(err)) throw err;
|
|
11347
|
+
const next = i + 1;
|
|
11348
|
+
if (next < clients.length) {
|
|
11349
|
+
console.warn(
|
|
11350
|
+
`[llm-failover] ${label}: ${clients[i].config.provider}/${clients[i].config.model} failed (${errLabel(err)}) \u2014 failing over to ${clients[next].config.provider}/${clients[next].config.model}`
|
|
11351
|
+
);
|
|
11352
|
+
}
|
|
11353
|
+
}
|
|
11354
|
+
}
|
|
11355
|
+
throw new AllProvidersFailedError(clients.length, lastErr);
|
|
11356
|
+
}
|
|
11357
|
+
const primary = clients[0];
|
|
11358
|
+
return {
|
|
11359
|
+
// Surface the primary's identity/capabilities; the chain is homogeneous in
|
|
11360
|
+
// the capability that matters here (tool use). Failover is for outages, not
|
|
11361
|
+
// capability switching.
|
|
11362
|
+
config: primary.config,
|
|
11363
|
+
capabilities: primary.capabilities,
|
|
11364
|
+
generateText: ((args) => runWithFailover(
|
|
11365
|
+
(c) => c.generateText(args),
|
|
11366
|
+
"generateText"
|
|
11367
|
+
)),
|
|
11368
|
+
// Streaming keeps single-provider semantics (the runner loop uses
|
|
11369
|
+
// generateText). Delegate to the currently-active provider.
|
|
11370
|
+
streamText: ((args) => clients[activeIndex].streamText(args)),
|
|
11371
|
+
generateObject: ((args) => runWithFailover(
|
|
11372
|
+
(c) => c.generateObject(args),
|
|
11373
|
+
"generateObject"
|
|
11374
|
+
))
|
|
11375
|
+
};
|
|
11376
|
+
}
|
|
11377
|
+
function createFailoverLlmClient(configs) {
|
|
11378
|
+
if (configs.length === 0) {
|
|
11379
|
+
throw new ProviderConfigError("failover: at least one provider config is required");
|
|
11380
|
+
}
|
|
11381
|
+
return createFailoverFromClients(configs.map((c) => createLlmClient(c)));
|
|
11382
|
+
}
|
|
11383
|
+
|
|
11115
11384
|
// ../../packages/llm/src/embeddings.ts
|
|
11116
11385
|
import { embed } from "ai";
|
|
11117
11386
|
import { createOllama as createOllama2 } from "ollama-ai-provider-v2";
|
|
@@ -11344,7 +11613,10 @@ async function _writeToolCall(ctx, toolName, input, output, durationMs) {
|
|
|
11344
11613
|
|
|
11345
11614
|
// ../../packages/tools/src/tool-choice.ts
|
|
11346
11615
|
function computeToolChoice(cfg) {
|
|
11347
|
-
const { isOrchestrator, turn, hasAdapterTools } = cfg;
|
|
11616
|
+
const { isOrchestrator, turn, hasAdapterTools, modelSupportsForcedToolChoice = true } = cfg;
|
|
11617
|
+
if (!modelSupportsForcedToolChoice) {
|
|
11618
|
+
return "auto";
|
|
11619
|
+
}
|
|
11348
11620
|
if (hasAdapterTools && !isOrchestrator) {
|
|
11349
11621
|
return "required";
|
|
11350
11622
|
}
|
|
@@ -26212,6 +26484,67 @@ async function createMcpTools(opts) {
|
|
|
26212
26484
|
return { tools, close: conn.close };
|
|
26213
26485
|
}
|
|
26214
26486
|
|
|
26487
|
+
// src/job/resolve-llm.ts
|
|
26488
|
+
init_src();
|
|
26489
|
+
async function resolveAgentLlmClient(db, agent, onSkip) {
|
|
26490
|
+
if (!agent.llmKeyId) return { ok: false, reason: "agent_no_llm_configured" };
|
|
26491
|
+
const seen = /* @__PURE__ */ new Set();
|
|
26492
|
+
const requested = [];
|
|
26493
|
+
for (const link of [
|
|
26494
|
+
{ keyId: agent.llmKeyId, model: agent.model },
|
|
26495
|
+
...agent.fallbackChain ?? []
|
|
26496
|
+
]) {
|
|
26497
|
+
if (typeof link.keyId === "string" && link.keyId.length > 0 && !seen.has(link.keyId)) {
|
|
26498
|
+
seen.add(link.keyId);
|
|
26499
|
+
requested.push({ keyId: link.keyId, model: link.model ?? "" });
|
|
26500
|
+
}
|
|
26501
|
+
}
|
|
26502
|
+
const ids = requested.map((r) => r.keyId);
|
|
26503
|
+
const rows = await db.select().from(entityLlmKeys).where(inArray3(entityLlmKeys.id, ids));
|
|
26504
|
+
const byId = new Map(rows.map((r) => [r.id, r]));
|
|
26505
|
+
try {
|
|
26506
|
+
const configs = [];
|
|
26507
|
+
for (const { keyId, model: requestedModel } of requested) {
|
|
26508
|
+
const row = byId.get(keyId);
|
|
26509
|
+
if (!row || !row.isActive) {
|
|
26510
|
+
onSkip?.({ keyId, reason: "missing_or_inactive" });
|
|
26511
|
+
continue;
|
|
26512
|
+
}
|
|
26513
|
+
const model = requestedModel.length > 0 ? requestedModel : MODEL_CATALOG[row.provider]?.[0]?.modelId ?? "";
|
|
26514
|
+
if (!model) {
|
|
26515
|
+
onSkip?.({ keyId, reason: "no_catalog_model" });
|
|
26516
|
+
continue;
|
|
26517
|
+
}
|
|
26518
|
+
const plaintextKey = row.apiKey ? decrypt(row.apiKey) : "";
|
|
26519
|
+
configs.push({
|
|
26520
|
+
provider: row.provider,
|
|
26521
|
+
model,
|
|
26522
|
+
apiKey: plaintextKey || void 0,
|
|
26523
|
+
baseURL: row.baseUrl ?? void 0
|
|
26524
|
+
});
|
|
26525
|
+
}
|
|
26526
|
+
if (configs.length === 0) return { ok: false, reason: "agent_no_llm_configured" };
|
|
26527
|
+
const effectivePrimary = configs[0];
|
|
26528
|
+
const client = configs.length > 1 ? createFailoverLlmClient(configs) : createLlmClient(effectivePrimary);
|
|
26529
|
+
return {
|
|
26530
|
+
ok: true,
|
|
26531
|
+
client,
|
|
26532
|
+
primaryProvider: effectivePrimary.provider,
|
|
26533
|
+
chainLength: configs.length,
|
|
26534
|
+
// Capability comes from the model CATALOG (provider, model of the
|
|
26535
|
+
// effective primary), not a stored column. Unknown/custom models default
|
|
26536
|
+
// to true; the runtime tool_choice floor backstops a wrong guess.
|
|
26537
|
+
primarySupportsForcedToolChoice: findModelCatalogEntry(effectivePrimary.provider, effectivePrimary.model)?.capabilities.forcedToolChoice ?? true
|
|
26538
|
+
};
|
|
26539
|
+
} catch (err) {
|
|
26540
|
+
return {
|
|
26541
|
+
ok: false,
|
|
26542
|
+
reason: "llm_key_invalid",
|
|
26543
|
+
detail: err instanceof Error ? err.message.slice(0, 200) : "llm_key_invalid"
|
|
26544
|
+
};
|
|
26545
|
+
}
|
|
26546
|
+
}
|
|
26547
|
+
|
|
26215
26548
|
// ../../packages/orchestration/src/errors.ts
|
|
26216
26549
|
var DelegationPendingError = class extends Error {
|
|
26217
26550
|
constructor(childJobId, childSlug) {
|
|
@@ -26273,7 +26606,16 @@ var DEFAULT_LIMITS = {
|
|
|
26273
26606
|
maxDelegationDepth: 3,
|
|
26274
26607
|
maxTurns: 50,
|
|
26275
26608
|
// matches Hermes Agent's per-subagent iteration budget; cumulative cap across resumes
|
|
26276
|
-
maxConsecutiveDeliveryTurns: 3
|
|
26609
|
+
maxConsecutiveDeliveryTurns: 3,
|
|
26610
|
+
// 1.5M total tokens: a loud backstop well above any legitimate single job
|
|
26611
|
+
// (typical jobs sit in the tens of thousands) yet below the ~2.4M-token
|
|
26612
|
+
// runaway that motivated it. Override per-deployment via MAX_TOTAL_TOKENS_PER_JOB.
|
|
26613
|
+
maxTotalTokensPerJob: 15e5,
|
|
26614
|
+
// 12 identical (toolName+input+output) turns in a row before declaring the job
|
|
26615
|
+
// stuck. Deliberately conservative: a real poll completes (output changes) long
|
|
26616
|
+
// before 12 identical reads, so this only catches genuinely degenerate loops —
|
|
26617
|
+
// and maxTurns (50) is the ultimate backstop above it.
|
|
26618
|
+
maxNoProgressRepeats: 12
|
|
26277
26619
|
};
|
|
26278
26620
|
var ChainCounters = class _ChainCounters {
|
|
26279
26621
|
constructor(limits = DEFAULT_LIMITS) {
|
|
@@ -26971,7 +27313,7 @@ function buildJobContextBlock(ctx) {
|
|
|
26971
27313
|
if (ctx.telegramChatId) lines.push(`- telegram_chat_id: ${ctx.telegramChatId}`);
|
|
26972
27314
|
if (ctx.surface === "chat") {
|
|
26973
27315
|
lines.push(
|
|
26974
|
-
'- surface: in-app dashboard chat \u2014 you are talking directly with the user; reply in plain text. For conversation or recalling facts, just reply (your durable facts are loaded below). For ANY action \u2014 using a connector or skill, delegating to your team, sending/fetching/creating/publishing, or (as the workspace ROOT) creating agents, skills, MCP servers, connectors or automations \u2014 call `run_task` with a clear, self-contained instruction:
|
|
27316
|
+
'- surface: in-app dashboard chat \u2014 you are talking directly with the user; reply in plain text. For conversation or recalling facts, just reply (your durable facts are loaded below). For ANY action \u2014 using a connector or skill, delegating to your team, sending/fetching/creating/publishing, or (as the workspace ROOT) creating agents, skills, MCP servers, connectors or automations \u2014 you MUST call the `run_task` tool with a clear, self-contained instruction. CRITICAL: writing in text that you will do something (e.g. "Je lance X\u2026") does NOT start anything \u2014 ONLY an actual `run_task` tool call performs the action. If you intend to act, the `run_task` tool call is mandatory; a text-only reply about an action accomplishes nothing. It runs as a tracked job with your FULL toolset. `run_task` is your gateway to everything you can do \u2014 NEVER tell the user you cannot do something that an action could accomplish; escalate it via `run_task` instead. You may add a one-line acknowledgment in your own voice alongside the call, but the `run_task` call is what actually does the work. Do not call any other named tool on this surface.'
|
|
26975
27317
|
);
|
|
26976
27318
|
}
|
|
26977
27319
|
if (ctx.notifyOnSuccess) {
|
|
@@ -27312,9 +27654,21 @@ function truncateForContext(value) {
|
|
|
27312
27654
|
|
|
27313
27655
|
[... truncated: ${dropped} chars dropped (total ${value.length}) ...]`;
|
|
27314
27656
|
}
|
|
27657
|
+
function stableStringify(value) {
|
|
27658
|
+
return JSON.stringify(value, (_key, val) => {
|
|
27659
|
+
if (val && typeof val === "object" && !Array.isArray(val)) {
|
|
27660
|
+
return Object.keys(val).sort().reduce((acc, k) => {
|
|
27661
|
+
acc[k] = val[k];
|
|
27662
|
+
return acc;
|
|
27663
|
+
}, {});
|
|
27664
|
+
}
|
|
27665
|
+
return val;
|
|
27666
|
+
});
|
|
27667
|
+
}
|
|
27315
27668
|
async function executeJob(jobId, deps, _runnerEnv) {
|
|
27316
27669
|
const { db, registry } = deps;
|
|
27317
27670
|
let llmClient;
|
|
27671
|
+
let modelSupportsForcedToolChoice = true;
|
|
27318
27672
|
const startedAt = Date.now();
|
|
27319
27673
|
const trace = (event, data) => {
|
|
27320
27674
|
console.error(`[exec ${jobId}] ${event}`, data ? JSON.stringify(data) : "");
|
|
@@ -27378,28 +27732,27 @@ async function executeJob(jobId, deps, _runnerEnv) {
|
|
|
27378
27732
|
return { status: "failed", error: "agent_no_llm_configured" };
|
|
27379
27733
|
}
|
|
27380
27734
|
{
|
|
27381
|
-
const
|
|
27382
|
-
|
|
27383
|
-
|
|
27384
|
-
|
|
27385
|
-
|
|
27386
|
-
|
|
27387
|
-
|
|
27388
|
-
|
|
27389
|
-
|
|
27390
|
-
|
|
27391
|
-
|
|
27392
|
-
|
|
27393
|
-
}
|
|
27394
|
-
|
|
27395
|
-
|
|
27396
|
-
|
|
27397
|
-
|
|
27398
|
-
|
|
27399
|
-
|
|
27400
|
-
|
|
27401
|
-
|
|
27402
|
-
}
|
|
27735
|
+
const resolved = await resolveAgentLlmClient(
|
|
27736
|
+
db,
|
|
27737
|
+
{
|
|
27738
|
+
llmKeyId: agentRow.llmKeyId,
|
|
27739
|
+
fallbackChain: agentRow.fallbackChain ?? null,
|
|
27740
|
+
model: agent.model
|
|
27741
|
+
},
|
|
27742
|
+
(info) => trace("fallback_key_skipped", info)
|
|
27743
|
+
);
|
|
27744
|
+
if (!resolved.ok) {
|
|
27745
|
+
const code = resolved.reason === "agent_no_llm_configured" ? "agent_no_llm_configured" : `llm_key_invalid:${resolved.detail}`;
|
|
27746
|
+
await failJob(db, jobId, code, runStats());
|
|
27747
|
+
return { status: "failed", error: code };
|
|
27748
|
+
}
|
|
27749
|
+
llmClient = resolved.client;
|
|
27750
|
+
modelSupportsForcedToolChoice = resolved.primarySupportsForcedToolChoice;
|
|
27751
|
+
trace("llm_client_from_key", {
|
|
27752
|
+
provider: resolved.primaryProvider,
|
|
27753
|
+
chainLength: resolved.chainLength,
|
|
27754
|
+
forcedToolChoice: modelSupportsForcedToolChoice
|
|
27755
|
+
});
|
|
27403
27756
|
}
|
|
27404
27757
|
const childRows = await db.select({ id: agents.id, slug: agents.slug, role: agents.role }).from(agentAssignments).innerJoin(agents, eq4(agentAssignments.subAgentId, agents.id)).where(and3(eq4(agentAssignments.orchestratorId, agentRow.id), eq4(agents.active, true)));
|
|
27405
27758
|
const children = childRows.map((r) => ({
|
|
@@ -27734,6 +28087,20 @@ async function executeJob(jobId, deps, _runnerEnv) {
|
|
|
27734
28087
|
await setJobStatus(db, jobId, "awaiting_approval");
|
|
27735
28088
|
return { status: "awaiting_approval" };
|
|
27736
28089
|
};
|
|
28090
|
+
const maxTotalTokensPerJob = (() => {
|
|
28091
|
+
const raw = process.env["MAX_TOTAL_TOKENS_PER_JOB"];
|
|
28092
|
+
const n = raw ? Number(raw) : NaN;
|
|
28093
|
+
return Number.isFinite(n) && n > 0 ? n : DEFAULT_LIMITS.maxTotalTokensPerJob;
|
|
28094
|
+
})();
|
|
28095
|
+
const recentTurnSignatures = [];
|
|
28096
|
+
const maxNoProgressRepeats = (() => {
|
|
28097
|
+
const raw = process.env["MAX_NO_PROGRESS_REPEATS"];
|
|
28098
|
+
const n = raw ? Number(raw) : NaN;
|
|
28099
|
+
return Number.isFinite(n) && n >= 2 ? Math.floor(n) : DEFAULT_LIMITS.maxNoProgressRepeats;
|
|
28100
|
+
})();
|
|
28101
|
+
const unresolvedToolFailures = /* @__PURE__ */ new Set();
|
|
28102
|
+
const MAX_UNRESOLVED_FAILURE_NUDGES = 2;
|
|
28103
|
+
let unresolvedFailureNudges = 0;
|
|
27737
28104
|
try {
|
|
27738
28105
|
while (true) {
|
|
27739
28106
|
turn += 1;
|
|
@@ -27749,7 +28116,12 @@ async function executeJob(jobId, deps, _runnerEnv) {
|
|
|
27749
28116
|
return { status: "failed", error: "turn_limit_exceeded" };
|
|
27750
28117
|
}
|
|
27751
28118
|
validateMessageStructure(messages);
|
|
27752
|
-
const toolChoice = computeToolChoice({
|
|
28119
|
+
const toolChoice = computeToolChoice({
|
|
28120
|
+
isOrchestrator,
|
|
28121
|
+
turn,
|
|
28122
|
+
hasAdapterTools,
|
|
28123
|
+
modelSupportsForcedToolChoice
|
|
28124
|
+
});
|
|
27753
28125
|
const aiSdkTools = {};
|
|
27754
28126
|
for (const [name, toolDef] of toolMap) {
|
|
27755
28127
|
const description = authoringToolsSuffix && (name === "create_skill" || name === "update_skill") ? toolDef.description + authoringToolsSuffix : toolDef.description;
|
|
@@ -27767,6 +28139,11 @@ async function executeJob(jobId, deps, _runnerEnv) {
|
|
|
27767
28139
|
const completionT = Number(usage?.outputTokens ?? 0);
|
|
27768
28140
|
inputTokens += Number.isFinite(promptT) ? promptT : 0;
|
|
27769
28141
|
outputTokens += Number.isFinite(completionT) ? completionT : 0;
|
|
28142
|
+
if (inputTokens + outputTokens > maxTotalTokensPerJob) {
|
|
28143
|
+
trace("token_budget_exceeded", { turn, inputTokens, outputTokens, maxTotalTokensPerJob });
|
|
28144
|
+
await failJob(db, jobId, "token_budget_exceeded", runStats());
|
|
28145
|
+
return { status: "failed", error: "token_budget_exceeded" };
|
|
28146
|
+
}
|
|
27770
28147
|
const rawToolCalls = response.toolCalls ?? [];
|
|
27771
28148
|
trace("llm_call_done", {
|
|
27772
28149
|
turn,
|
|
@@ -28011,6 +28388,11 @@ async function executeJob(jobId, deps, _runnerEnv) {
|
|
|
28011
28388
|
if (toolResult.outcome === "success" && DELIVERY_TOOL_NAMES.has(call.name)) {
|
|
28012
28389
|
telegramDelivered = true;
|
|
28013
28390
|
}
|
|
28391
|
+
if (toolResult.outcome === "success") {
|
|
28392
|
+
unresolvedToolFailures.delete(call.name);
|
|
28393
|
+
} else {
|
|
28394
|
+
unresolvedToolFailures.add(call.name);
|
|
28395
|
+
}
|
|
28014
28396
|
toolResultBlocks.push({
|
|
28015
28397
|
type: "tool-result",
|
|
28016
28398
|
toolCallId: call.id,
|
|
@@ -28059,6 +28441,38 @@ async function executeJob(jobId, deps, _runnerEnv) {
|
|
|
28059
28441
|
}
|
|
28060
28442
|
if (returnResultCall) {
|
|
28061
28443
|
trace("return_result_branch", { turn });
|
|
28444
|
+
const rrStatus = returnResultCall.input?.status;
|
|
28445
|
+
if (rrStatus === "success" && unresolvedToolFailures.size > 0) {
|
|
28446
|
+
const stuck = [...unresolvedToolFailures];
|
|
28447
|
+
if (unresolvedFailureNudges < MAX_UNRESOLVED_FAILURE_NUDGES) {
|
|
28448
|
+
unresolvedFailureNudges += 1;
|
|
28449
|
+
trace("unresolved_tool_failure_nudge", {
|
|
28450
|
+
turn,
|
|
28451
|
+
attempt: unresolvedFailureNudges,
|
|
28452
|
+
stuck
|
|
28453
|
+
});
|
|
28454
|
+
toolResultBlocks.push({
|
|
28455
|
+
type: "tool-result",
|
|
28456
|
+
toolCallId: returnResultCall.toolCallId,
|
|
28457
|
+
toolName: "return_result",
|
|
28458
|
+
output: toResultOutput({
|
|
28459
|
+
error: "deferred: tu signales success mais ces actions ont \xE9chou\xE9 sans \xEAtre corrig\xE9es (" + stuck.join(", ") + "). R\xE9essaie l'action jusqu'\xE0 r\xE9ussite, ou appelle return_result avec status='blocked'."
|
|
28460
|
+
})
|
|
28461
|
+
});
|
|
28462
|
+
messages = [...messages, { role: "tool", content: toolResultBlocks }];
|
|
28463
|
+
messages = [
|
|
28464
|
+
...messages,
|
|
28465
|
+
{
|
|
28466
|
+
role: "user",
|
|
28467
|
+
content: "[syst\xE8me] Ne d\xE9clare pas un succ\xE8s qui n'a pas eu lieu. Une action a \xE9chou\xE9 et n'a pas \xE9t\xE9 corrig\xE9e. Corrige-la, ou termine honn\xEAtement avec status='blocked'."
|
|
28468
|
+
}
|
|
28469
|
+
];
|
|
28470
|
+
continue;
|
|
28471
|
+
}
|
|
28472
|
+
trace("unresolved_tool_failure", { turn, stuck });
|
|
28473
|
+
await failJob(db, jobId, "unresolved_tool_failure", runStats());
|
|
28474
|
+
return { status: "failed", error: "unresolved_tool_failure" };
|
|
28475
|
+
}
|
|
28062
28476
|
const taskRows = await db.select({ id: agentTasks.id }).from(agentTasks).where(eq4(agentTasks.rootJobId, jobId));
|
|
28063
28477
|
if (requiresToolDelivery && !telegramDelivered && taskRows.length === 0) {
|
|
28064
28478
|
if (telegramRedeliveryNudges < MAX_TELEGRAM_REDELIVERY_NUDGES) {
|
|
@@ -28118,6 +28532,23 @@ async function executeJob(jobId, deps, _runnerEnv) {
|
|
|
28118
28532
|
if (toolResultBlocks.length > 0) {
|
|
28119
28533
|
messages = [...messages, { role: "tool", content: toolResultBlocks }];
|
|
28120
28534
|
}
|
|
28535
|
+
const turnSignature = toolResultBlocks.map((b) => {
|
|
28536
|
+
const call = callsToProcess.find((c) => c.id === b.toolCallId);
|
|
28537
|
+
const input = call ? stableStringify(call.input) : "";
|
|
28538
|
+
const output = b.output.type === "text" ? b.output.value : stableStringify(b.output.value ?? null);
|
|
28539
|
+
return `${b.toolName}\0${input}\0${output}`;
|
|
28540
|
+
}).sort().join("\n");
|
|
28541
|
+
if (turnSignature !== "") {
|
|
28542
|
+
recentTurnSignatures.push(turnSignature);
|
|
28543
|
+
if (recentTurnSignatures.length > maxNoProgressRepeats) {
|
|
28544
|
+
recentTurnSignatures.shift();
|
|
28545
|
+
}
|
|
28546
|
+
if (recentTurnSignatures.length === maxNoProgressRepeats && recentTurnSignatures.every((s) => s === turnSignature)) {
|
|
28547
|
+
trace("no_progress_detected", { turn, repeats: recentTurnSignatures.length });
|
|
28548
|
+
await failJob(db, jobId, "no_progress_detected", runStats());
|
|
28549
|
+
return { status: "failed", error: "no_progress_detected" };
|
|
28550
|
+
}
|
|
28551
|
+
}
|
|
28121
28552
|
await saveCheckpoint(db, jobId, {
|
|
28122
28553
|
messages,
|
|
28123
28554
|
turn,
|
|
@@ -28148,6 +28579,10 @@ async function executeJob(jobId, deps, _runnerEnv) {
|
|
|
28148
28579
|
await failJob(db, jobId, "quota_exhausted", runStats());
|
|
28149
28580
|
return { status: "failed", error: "quota_exhausted" };
|
|
28150
28581
|
}
|
|
28582
|
+
if (err instanceof AllProvidersFailedError) {
|
|
28583
|
+
await failJob(db, jobId, err.code, runStats());
|
|
28584
|
+
return { status: "failed", error: err.code };
|
|
28585
|
+
}
|
|
28151
28586
|
if (err instanceof MessageStructureError) {
|
|
28152
28587
|
await failJob(db, jobId, `message_structure_invalid:${err.code}`, runStats());
|
|
28153
28588
|
return { status: "failed", error: `message_structure_invalid:${err.code}` };
|
|
@@ -28786,20 +29221,18 @@ async function runChatTurn(opts) {
|
|
|
28786
29221
|
const title = message.trim().slice(0, TITLE_MAX) + (message.trim().length > TITLE_MAX ? "\u2026" : "");
|
|
28787
29222
|
await db.update(conversations).set({ title }).where(eq4(conversations.id, conversationId));
|
|
28788
29223
|
}
|
|
28789
|
-
const
|
|
28790
|
-
|
|
28791
|
-
|
|
28792
|
-
|
|
28793
|
-
|
|
28794
|
-
|
|
28795
|
-
|
|
28796
|
-
|
|
28797
|
-
|
|
28798
|
-
|
|
28799
|
-
});
|
|
28800
|
-
} catch {
|
|
28801
|
-
return { ok: false, error: "llm_key_invalid" };
|
|
29224
|
+
const resolved = await resolveAgentLlmClient(db, {
|
|
29225
|
+
llmKeyId: agentRow.llmKeyId,
|
|
29226
|
+
fallbackChain: agentRow.fallbackChain ?? null,
|
|
29227
|
+
model: agentRow.model ?? DEFAULT_MODEL
|
|
29228
|
+
});
|
|
29229
|
+
if (!resolved.ok) {
|
|
29230
|
+
return {
|
|
29231
|
+
ok: false,
|
|
29232
|
+
error: resolved.reason === "agent_no_llm_configured" ? "agent_no_llm_configured" : "llm_key_invalid"
|
|
29233
|
+
};
|
|
28802
29234
|
}
|
|
29235
|
+
const llmClient = resolved.client;
|
|
28803
29236
|
const agent = {
|
|
28804
29237
|
id: agentRow.id,
|
|
28805
29238
|
name: agentRow.name,
|
|
@@ -28915,7 +29348,6 @@ async function seedDefaultLlmKey(db, env2) {
|
|
|
28915
29348
|
apiKeyLast4: last4(plaintextKey),
|
|
28916
29349
|
baseUrl: env2.LLM_BASE_URL ?? null,
|
|
28917
29350
|
nickname: "Default (env)",
|
|
28918
|
-
defaultModel: env2.LLM_MODEL,
|
|
28919
29351
|
isActive: true
|
|
28920
29352
|
}).returning({ id: entityLlmKeys.id });
|
|
28921
29353
|
if (!newKey) return;
|
|
@@ -30710,7 +31142,7 @@ function createApp(deps, runnerEnv) {
|
|
|
30710
31142
|
throw err;
|
|
30711
31143
|
}
|
|
30712
31144
|
});
|
|
30713
|
-
|
|
31145
|
+
const bearerOrSession = async (c, next) => {
|
|
30714
31146
|
const auth2 = c.req.header("authorization") ?? "";
|
|
30715
31147
|
const bearer = auth2.startsWith("Bearer ") ? auth2.slice(7) : null;
|
|
30716
31148
|
if (bearer && runnerEnv.WORKER_SECRET && bearer === runnerEnv.WORKER_SECRET) {
|
|
@@ -30729,7 +31161,8 @@ function createApp(deps, runnerEnv) {
|
|
|
30729
31161
|
}
|
|
30730
31162
|
throw err;
|
|
30731
31163
|
}
|
|
30732
|
-
}
|
|
31164
|
+
};
|
|
31165
|
+
app.use("/api/approve", bearerOrSession);
|
|
30733
31166
|
app.get("/api/health", (c) => healthRoute(c, deps));
|
|
30734
31167
|
app.post("/api/agent", (c) => agentRoute(c, deps, runnerEnv));
|
|
30735
31168
|
app.post("/api/worker", (c) => workerRoute(c, deps, runnerEnv));
|