@ai-setting/roy-agent-core 1.7.62 → 1.7.63
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/dist/env/agent/agent-component.d.ts +33 -0
- package/dist/env/agent/agent-component.js +4 -3
- package/dist/env/agent/index.js +4 -3
- package/dist/env/agent/types.d.ts +17 -0
- package/dist/env/error-reply.d.ts +53 -1
- package/dist/env/error-reply.js +8 -1
- package/dist/env/event-source/event-source-handlers.js +3 -2
- package/dist/env/event-source/index.js +4 -3
- package/dist/env/index.js +14 -13
- package/dist/env/llm/adapter-pi-ai.d.ts +47 -0
- package/dist/env/llm/index.js +3 -2
- package/dist/env/llm/model-fallback.d.ts +54 -0
- package/dist/env/llm/redact.d.ts +39 -0
- package/dist/env/llm/types.d.ts +13 -0
- package/dist/env/session/index.js +5 -4
- package/dist/env/session/storage/index.js +4 -3
- package/dist/env/task/delegate/delegate-tool.d.ts +27 -1
- package/dist/env/task/delegate/delegate-tool.js +1 -1
- package/dist/env/task/delegate/index.js +1 -1
- package/dist/env/task/index.js +2 -2
- package/dist/env/tool/types.d.ts +13 -0
- package/dist/env/workflow/index.js +7 -6
- package/dist/env/workflow/tools/index.js +6 -5
- package/dist/index.js +15 -14
- package/dist/shared/@ai-setting/{roy-agent-core-9qgrc9dy.js → roy-agent-core-03k5drfp.js} +54 -14
- package/dist/shared/@ai-setting/{roy-agent-core-2veqhrxa.js → roy-agent-core-1dnsk6ht.js} +2 -2
- package/dist/shared/@ai-setting/{roy-agent-core-vvb4cyyy.js → roy-agent-core-2cw72f16.js} +7 -2
- package/dist/shared/@ai-setting/{roy-agent-core-cqpd41gf.js → roy-agent-core-5hq4v41q.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-fr296a53.js → roy-agent-core-60f7h284.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-gz2jnaan.js → roy-agent-core-b4zdm2n4.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-48h4zh60.js → roy-agent-core-bm3gav5d.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-h3fwjbhp.js → roy-agent-core-ebws32tv.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-5bj0xfjk.js → roy-agent-core-g23yp3t1.js} +298 -34
- package/dist/shared/@ai-setting/{roy-agent-core-w2h6xt6k.js → roy-agent-core-g6pw4esq.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-fqandv0a.js → roy-agent-core-gnt23yx0.js} +21 -11
- package/dist/shared/@ai-setting/roy-agent-core-ntn32q7j.js +72 -0
- package/dist/shared/@ai-setting/{roy-agent-core-x1d6edkh.js → roy-agent-core-rfhxj8v2.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-nmn853z0.js → roy-agent-core-t6nx0rsz.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-9nvyffxg.js → roy-agent-core-va4m04e8.js} +117 -7
- package/package.json +1 -1
|
@@ -10,12 +10,12 @@ import {
|
|
|
10
10
|
} from "./roy-agent-core-p8rf4z89.js";
|
|
11
11
|
import {
|
|
12
12
|
SessionMessageConverter
|
|
13
|
-
} from "./roy-agent-core-
|
|
13
|
+
} from "./roy-agent-core-rfhxj8v2.js";
|
|
14
14
|
import {
|
|
15
15
|
parseToolArgsStrict,
|
|
16
16
|
safeParseToolArgs,
|
|
17
17
|
withTimeout
|
|
18
|
-
} from "./roy-agent-core-
|
|
18
|
+
} from "./roy-agent-core-03k5drfp.js";
|
|
19
19
|
import {
|
|
20
20
|
getCurrentBgTaskId
|
|
21
21
|
} from "./roy-agent-core-c1v263jn.js";
|
|
@@ -197,6 +197,184 @@ var AGENT_CONFIG_REGISTRATION = {
|
|
|
197
197
|
// src/env/agent/agent-component.ts
|
|
198
198
|
init_decorator();
|
|
199
199
|
init_trace_filters();
|
|
200
|
+
|
|
201
|
+
// src/env/llm/model-fallback.ts
|
|
202
|
+
var NON_FALLBACKABLE_STATUS = new Set([400, 401, 403]);
|
|
203
|
+
var MODEL_UNAVAILABLE_PATTERNS = [
|
|
204
|
+
/model(?:\s+id|\s+name)?\s+(?:is\s+)?(?:unavailable|not\s+available)/i,
|
|
205
|
+
/model(?:\s+id|\s+name)?\s+(?:is\s+)?(?:not\s+found|does\s+not\s+exist|not\s+supported|deprecated|disabled|invalid|unknown)/i,
|
|
206
|
+
/\b(?:unknown|unsupported|nonexistent)\s+model\b/i,
|
|
207
|
+
/model[_\s-]?not[_\s-]?found/i,
|
|
208
|
+
/no\s+such\s+model/i,
|
|
209
|
+
/模型(?:不存在|不可用|未找到|不支持|已下线)/
|
|
210
|
+
];
|
|
211
|
+
var CREDIT_PATTERNS = [
|
|
212
|
+
/credit balance/i,
|
|
213
|
+
/balance (?:is )?too? ?low/i,
|
|
214
|
+
/insufficient (?:balance|credits?|funds?)/i,
|
|
215
|
+
/payment required/i,
|
|
216
|
+
/\b402\b/,
|
|
217
|
+
/充值|欠费/
|
|
218
|
+
];
|
|
219
|
+
var INTRINSIC_BLOCKERS = [
|
|
220
|
+
/abort(?:ed|error)?/i,
|
|
221
|
+
/operation was (?:aborted|cancelled)/i,
|
|
222
|
+
/context threshold exceeded/i,
|
|
223
|
+
/context window (?:exceeded|overflow)/i
|
|
224
|
+
];
|
|
225
|
+
var QUOTA_PATTERNS = [
|
|
226
|
+
/insufficient[_ ]quota/i,
|
|
227
|
+
/exceeded your current quota/i,
|
|
228
|
+
/quota (?:exceeded|exhausted)/i,
|
|
229
|
+
/rate[ _-]?limit/i,
|
|
230
|
+
/\b429\b/,
|
|
231
|
+
/too many requests/i,
|
|
232
|
+
/billing/i,
|
|
233
|
+
/insufficient balance/i,
|
|
234
|
+
/余额不足/i,
|
|
235
|
+
/额度(?:不足|耗尽|用尽|超限)/,
|
|
236
|
+
/配额(?:不足|耗尽|用尽)/
|
|
237
|
+
];
|
|
238
|
+
var SERVER_ERROR_PATTERNS = [
|
|
239
|
+
/(?:http|status|code|error)\D{0,12}5\d{2}\b/i,
|
|
240
|
+
/\b5\d{2}\s+(?:internal|bad\s+gateway|service\s+unavailable|gateway\s+timeout|server\s+error)/i,
|
|
241
|
+
/internal server error/i,
|
|
242
|
+
/bad gateway/i,
|
|
243
|
+
/service unavailable/i,
|
|
244
|
+
/gateway timeout/i,
|
|
245
|
+
/overloaded/i
|
|
246
|
+
];
|
|
247
|
+
var TIMEOUT_PATTERNS = [
|
|
248
|
+
/timed?[\s_-]?out/i,
|
|
249
|
+
/timeout/i,
|
|
250
|
+
/etimedout/i,
|
|
251
|
+
/econnreset/i,
|
|
252
|
+
/econnrefused/i,
|
|
253
|
+
/socket hang up/i,
|
|
254
|
+
/network (?:error|failure)/i,
|
|
255
|
+
/fetch failed/i
|
|
256
|
+
];
|
|
257
|
+
var RETRY_EXHAUSTED_PATTERNS = [
|
|
258
|
+
/retry(?:ing)? (?:failed|exhausted)/i,
|
|
259
|
+
/retries exhausted/i,
|
|
260
|
+
/max(?:imum)? ?retries/i,
|
|
261
|
+
/too many retries/i,
|
|
262
|
+
/\bretryerror\b/i,
|
|
263
|
+
/all retries? failed/i
|
|
264
|
+
];
|
|
265
|
+
var HARD_BLOCKERS = [
|
|
266
|
+
/\b401\b/,
|
|
267
|
+
/unauthorized/i,
|
|
268
|
+
/\b403\b/,
|
|
269
|
+
/forbidden/i,
|
|
270
|
+
/invalid[ _-]?api[ _-]?key/i,
|
|
271
|
+
/authentication (?:failed|error)/i,
|
|
272
|
+
/\b400\b/,
|
|
273
|
+
/bad request/i,
|
|
274
|
+
/abort(?:ed|error)?/i,
|
|
275
|
+
/operation was (?:aborted|cancelled)/i,
|
|
276
|
+
/context threshold exceeded/i,
|
|
277
|
+
/context window (?:exceeded|overflow)/i
|
|
278
|
+
];
|
|
279
|
+
function extractStatusCode(error) {
|
|
280
|
+
if (!error || typeof error !== "object")
|
|
281
|
+
return;
|
|
282
|
+
const candidate = error;
|
|
283
|
+
const cause = candidate.cause ?? undefined;
|
|
284
|
+
const raw = candidate.statusCode ?? candidate.status ?? candidate.response?.status ?? cause?.statusCode ?? cause?.status;
|
|
285
|
+
const num = typeof raw === "number" ? raw : Number(raw);
|
|
286
|
+
return Number.isFinite(num) && num > 0 ? num : undefined;
|
|
287
|
+
}
|
|
288
|
+
var TEXT_KEYS = [
|
|
289
|
+
"message",
|
|
290
|
+
"errorMessage",
|
|
291
|
+
"reason",
|
|
292
|
+
"detail",
|
|
293
|
+
"body",
|
|
294
|
+
"responseBody",
|
|
295
|
+
"error",
|
|
296
|
+
"data",
|
|
297
|
+
"lastError"
|
|
298
|
+
];
|
|
299
|
+
function collectErrorText(error) {
|
|
300
|
+
const parts = [];
|
|
301
|
+
const pushFromObject = (obj) => {
|
|
302
|
+
for (const key of TEXT_KEYS) {
|
|
303
|
+
const value = obj[key];
|
|
304
|
+
if (typeof value === "string")
|
|
305
|
+
parts.push(value);
|
|
306
|
+
}
|
|
307
|
+
};
|
|
308
|
+
if (typeof error === "string") {
|
|
309
|
+
parts.push(error);
|
|
310
|
+
} else if (error instanceof Error) {
|
|
311
|
+
parts.push(error.message);
|
|
312
|
+
parts.push(error.name);
|
|
313
|
+
const cause = error.cause;
|
|
314
|
+
if (cause instanceof Error) {
|
|
315
|
+
parts.push(cause.message);
|
|
316
|
+
parts.push(cause.name);
|
|
317
|
+
} else if (typeof cause === "string") {
|
|
318
|
+
parts.push(cause);
|
|
319
|
+
} else if (cause && typeof cause === "object") {
|
|
320
|
+
pushFromObject(cause);
|
|
321
|
+
}
|
|
322
|
+
} else if (error && typeof error === "object") {
|
|
323
|
+
pushFromObject(error);
|
|
324
|
+
}
|
|
325
|
+
return parts.filter(Boolean).join(" | ");
|
|
326
|
+
}
|
|
327
|
+
function isFallbackEligibleError(error, hasFallback = true) {
|
|
328
|
+
if (!hasFallback)
|
|
329
|
+
return false;
|
|
330
|
+
if (error === undefined || error === null)
|
|
331
|
+
return false;
|
|
332
|
+
const text = collectErrorText(error);
|
|
333
|
+
const status = extractStatusCode(error);
|
|
334
|
+
const timedOut = TIMEOUT_PATTERNS.some((re) => re.test(text));
|
|
335
|
+
if (timedOut)
|
|
336
|
+
return true;
|
|
337
|
+
if (INTRINSIC_BLOCKERS.some((re) => re.test(text)))
|
|
338
|
+
return false;
|
|
339
|
+
if (MODEL_UNAVAILABLE_PATTERNS.some((re) => re.test(text)))
|
|
340
|
+
return true;
|
|
341
|
+
if (CREDIT_PATTERNS.some((re) => re.test(text)))
|
|
342
|
+
return true;
|
|
343
|
+
if (HARD_BLOCKERS.some((re) => re.test(text)))
|
|
344
|
+
return false;
|
|
345
|
+
if (status !== undefined && NON_FALLBACKABLE_STATUS.has(status))
|
|
346
|
+
return false;
|
|
347
|
+
if (status !== undefined) {
|
|
348
|
+
if (status === 404 || status === 408 || status === 429 || status >= 500)
|
|
349
|
+
return true;
|
|
350
|
+
}
|
|
351
|
+
if (/(?:^|[^/\w])404(?!\d)/.test(text))
|
|
352
|
+
return true;
|
|
353
|
+
if (QUOTA_PATTERNS.some((re) => re.test(text)))
|
|
354
|
+
return true;
|
|
355
|
+
if (SERVER_ERROR_PATTERNS.some((re) => re.test(text)))
|
|
356
|
+
return true;
|
|
357
|
+
if (RETRY_EXHAUSTED_PATTERNS.some((re) => re.test(text)))
|
|
358
|
+
return true;
|
|
359
|
+
return false;
|
|
360
|
+
}
|
|
361
|
+
function parseFallbackModel(fallbackModel) {
|
|
362
|
+
if (!fallbackModel)
|
|
363
|
+
return;
|
|
364
|
+
const trimmed = fallbackModel.trim();
|
|
365
|
+
if (!trimmed)
|
|
366
|
+
return;
|
|
367
|
+
const slashIndex = trimmed.indexOf("/");
|
|
368
|
+
if (slashIndex > 0) {
|
|
369
|
+
return {
|
|
370
|
+
providerId: trimmed.slice(0, slashIndex),
|
|
371
|
+
modelId: trimmed.slice(slashIndex + 1)
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
return { modelId: trimmed };
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// src/env/agent/agent-component.ts
|
|
200
378
|
init_global_hook_manager();
|
|
201
379
|
init_context();
|
|
202
380
|
init_workflow_hil();
|
|
@@ -403,6 +581,7 @@ class AgentComponent extends BaseComponent {
|
|
|
403
581
|
abortControllers = new Map;
|
|
404
582
|
defaultTools = [];
|
|
405
583
|
doomLoopCaches = new Map;
|
|
584
|
+
fallbackState = new Map;
|
|
406
585
|
configComponent;
|
|
407
586
|
configWatcher;
|
|
408
587
|
messageConverter = new SessionMessageConverter;
|
|
@@ -882,6 +1061,7 @@ class AgentComponent extends BaseComponent {
|
|
|
882
1061
|
tools: this.getAvailableTools(agent, effectiveContext),
|
|
883
1062
|
systemPrompt: resolvedSystemPrompt ?? agent.config.systemPrompt ?? "You are a helpful assistant.",
|
|
884
1063
|
context: effectiveContext,
|
|
1064
|
+
_runId: runId,
|
|
885
1065
|
...cacheSnapshot ? { _cacheSnapshot: cacheSnapshot } : {}
|
|
886
1066
|
};
|
|
887
1067
|
let historyMessageCount = 0;
|
|
@@ -1295,6 +1475,7 @@ class AgentComponent extends BaseComponent {
|
|
|
1295
1475
|
this.aborted.delete(runId);
|
|
1296
1476
|
this.abortControllers.delete(runId);
|
|
1297
1477
|
this.doomLoopCaches.delete(runId);
|
|
1478
|
+
this.fallbackState.delete(runId);
|
|
1298
1479
|
if (result.error) {
|
|
1299
1480
|
this.pushEnvEvent({
|
|
1300
1481
|
type: "agent.error",
|
|
@@ -1484,45 +1665,78 @@ class AgentComponent extends BaseComponent {
|
|
|
1484
1665
|
async invokeLLM(ctx) {
|
|
1485
1666
|
logger2.debug("[invokeLLM] Starting LLM invocation");
|
|
1486
1667
|
if (this.llmComponent?.invoke) {
|
|
1668
|
+
const llm = this.llmComponent;
|
|
1487
1669
|
const messages = await this.buildMessages(ctx);
|
|
1488
1670
|
const convertedMessages = messages.map(toLLMMessage);
|
|
1489
1671
|
logger2.debug(`[invokeLLM] Calling LLMComponent.invoke with ${convertedMessages.length} messages`);
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1672
|
+
const runKey = ctx._runId ?? ctx.context.sessionId ?? "default-run";
|
|
1673
|
+
const sticky = this.fallbackState.get(runKey);
|
|
1674
|
+
const configuredFallback = this.resolveFallbackModel(ctx.context.fallbackModel);
|
|
1675
|
+
const activeModel = sticky?.activeModel ?? ctx.context.model;
|
|
1676
|
+
const invokeWithModel = (model, signal) => llm.invoke({
|
|
1677
|
+
messages: convertedMessages,
|
|
1678
|
+
tools: ctx.tools.map((t) => ({
|
|
1679
|
+
name: t.name,
|
|
1680
|
+
description: t.description || "",
|
|
1681
|
+
parameters: t.parameters ?? {}
|
|
1682
|
+
})),
|
|
1683
|
+
model,
|
|
1684
|
+
context: {
|
|
1685
|
+
sessionId: ctx.context.sessionId,
|
|
1686
|
+
messageId: ctx.context.messageId
|
|
1687
|
+
},
|
|
1688
|
+
abortSignal: signal,
|
|
1689
|
+
skipThresholdCheck: ctx.context.agentType === "sub",
|
|
1690
|
+
...ctx.context.toolChoice ? { toolChoice: ctx.context.toolChoice } : {}
|
|
1691
|
+
});
|
|
1692
|
+
const startAttempt = (model, label) => {
|
|
1693
|
+
const attemptController = new AbortController;
|
|
1694
|
+
const signal = ctx.context.abort ? AbortSignal.any([ctx.context.abort, attemptController.signal]) : attemptController.signal;
|
|
1509
1695
|
return {
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
arguments: typeof tc.function.arguments === "string" ? tc.function.arguments : JSON.stringify(tc.function.arguments)
|
|
1517
|
-
} : undefined,
|
|
1518
|
-
name: tc.name,
|
|
1519
|
-
arguments: tc.arguments
|
|
1520
|
-
})),
|
|
1521
|
-
finishReason: result.output?.finishReason
|
|
1696
|
+
promise: withTimeout(invokeWithModel(model, signal), 300000, label),
|
|
1697
|
+
cancel: () => {
|
|
1698
|
+
try {
|
|
1699
|
+
attemptController.abort();
|
|
1700
|
+
} catch {}
|
|
1701
|
+
}
|
|
1522
1702
|
};
|
|
1703
|
+
};
|
|
1704
|
+
let primaryAttempt;
|
|
1705
|
+
try {
|
|
1706
|
+
primaryAttempt = startAttempt(activeModel, "LLM invocation timeout after 300s");
|
|
1707
|
+
const result = await primaryAttempt.promise;
|
|
1708
|
+
logger2.debug("[invokeLLM] LLMComponent.invoke returned successfully");
|
|
1709
|
+
return this.normalizeLLMOutput(result);
|
|
1523
1710
|
} catch (error) {
|
|
1524
1711
|
logger2.error("[invokeLLM] LLMComponent.invoke threw an error:", error);
|
|
1525
|
-
|
|
1712
|
+
primaryAttempt?.cancel();
|
|
1713
|
+
if (ctx.context.abort?.aborted) {
|
|
1714
|
+
throw error;
|
|
1715
|
+
}
|
|
1716
|
+
const alreadyDowngraded = sticky?.fallbackModel !== undefined;
|
|
1717
|
+
const canFallback = !alreadyDowngraded && !!configuredFallback && !AgentComponent.sameModel(configuredFallback, activeModel) && isFallbackEligibleError(error, true);
|
|
1718
|
+
if (!canFallback) {
|
|
1719
|
+
throw error;
|
|
1720
|
+
}
|
|
1721
|
+
logger2.warn(`[invokeLLM] primary model "${activeModel ?? "(default)"}" failed with a fallback-eligible error; downgrading to fallback model "${configuredFallback}" for run ${runKey}`);
|
|
1722
|
+
this.fallbackState.set(runKey, {
|
|
1723
|
+
activeModel: configuredFallback,
|
|
1724
|
+
fallbackModel: configuredFallback
|
|
1725
|
+
});
|
|
1726
|
+
const fallbackAttempt = startAttempt(configuredFallback, "LLM invocation timeout after 300s (fallback)");
|
|
1727
|
+
try {
|
|
1728
|
+
const result = await fallbackAttempt.promise;
|
|
1729
|
+
logger2.info(`[invokeLLM] fallback model "${configuredFallback}" succeeded for run ${runKey}`);
|
|
1730
|
+
return this.normalizeLLMOutput(result);
|
|
1731
|
+
} catch (fallbackError) {
|
|
1732
|
+
fallbackAttempt.cancel();
|
|
1733
|
+
if (fallbackError instanceof ContextError) {
|
|
1734
|
+
logger2.warn(`[invokeLLM] fallback model "${configuredFallback}" hit a context limit; re-throwing ContextError so the compaction pipeline can handle it`);
|
|
1735
|
+
throw fallbackError;
|
|
1736
|
+
}
|
|
1737
|
+
logger2.error(`[invokeLLM] fallback model "${configuredFallback}" also failed; re-throwing the ORIGINAL primary error`, fallbackError);
|
|
1738
|
+
throw error;
|
|
1739
|
+
}
|
|
1526
1740
|
}
|
|
1527
1741
|
}
|
|
1528
1742
|
logger2.warn("[invokeLLM] No LLMComponent available, using mock");
|
|
@@ -1531,6 +1745,53 @@ class AgentComponent extends BaseComponent {
|
|
|
1531
1745
|
finishReason: "stop"
|
|
1532
1746
|
};
|
|
1533
1747
|
}
|
|
1748
|
+
resolveFallbackModel(explicit) {
|
|
1749
|
+
const readConfig = (key) => {
|
|
1750
|
+
const raw2 = this.configComponent?.get?.(key);
|
|
1751
|
+
if (typeof raw2 !== "string")
|
|
1752
|
+
return;
|
|
1753
|
+
const trimmed = raw2.trim();
|
|
1754
|
+
return trimmed ? trimmed : undefined;
|
|
1755
|
+
};
|
|
1756
|
+
const raw = explicit?.trim() || readConfig("llm.fallbackModel");
|
|
1757
|
+
if (!raw)
|
|
1758
|
+
return;
|
|
1759
|
+
const parsed = parseFallbackModel(raw);
|
|
1760
|
+
if (!parsed?.modelId)
|
|
1761
|
+
return;
|
|
1762
|
+
if (parsed.providerId)
|
|
1763
|
+
return `${parsed.providerId}/${parsed.modelId}`;
|
|
1764
|
+
const provider = readConfig("llm.fallbackProvider");
|
|
1765
|
+
return provider ? `${provider}/${parsed.modelId}` : parsed.modelId;
|
|
1766
|
+
}
|
|
1767
|
+
static sameModel(a, b) {
|
|
1768
|
+
const norm = (value) => {
|
|
1769
|
+
if (!value)
|
|
1770
|
+
return "";
|
|
1771
|
+
const trimmed = value.trim();
|
|
1772
|
+
const slash = trimmed.indexOf("/");
|
|
1773
|
+
return slash >= 0 ? trimmed.slice(slash + 1) : trimmed;
|
|
1774
|
+
};
|
|
1775
|
+
const left = norm(a);
|
|
1776
|
+
const right = norm(b);
|
|
1777
|
+
return !!left && left === right;
|
|
1778
|
+
}
|
|
1779
|
+
normalizeLLMOutput(result) {
|
|
1780
|
+
return {
|
|
1781
|
+
content: result.output?.content || "",
|
|
1782
|
+
reasoning: result.output?.reasoning,
|
|
1783
|
+
toolCalls: result.output?.toolCalls?.map((tc) => ({
|
|
1784
|
+
id: tc.id,
|
|
1785
|
+
function: tc.function ? {
|
|
1786
|
+
name: tc.function.name,
|
|
1787
|
+
arguments: typeof tc.function.arguments === "string" ? tc.function.arguments : JSON.stringify(tc.function.arguments)
|
|
1788
|
+
} : undefined,
|
|
1789
|
+
name: tc.name,
|
|
1790
|
+
arguments: tc.arguments
|
|
1791
|
+
})),
|
|
1792
|
+
finishReason: result.output?.finishReason
|
|
1793
|
+
};
|
|
1794
|
+
}
|
|
1534
1795
|
parseToolCallArguments(toolCall) {
|
|
1535
1796
|
const rawArgs = toolCall.function?.arguments ?? toolCall.arguments;
|
|
1536
1797
|
return safeParseToolArgs(rawArgs);
|
|
@@ -1579,10 +1840,13 @@ class AgentComponent extends BaseComponent {
|
|
|
1579
1840
|
},
|
|
1580
1841
|
sessionId: toolEventSessionId
|
|
1581
1842
|
});
|
|
1843
|
+
const effectiveRunModel = this.fallbackState.get(ctx._runId ?? ctx.context.sessionId ?? "default-run")?.activeModel ?? ctx.context.model;
|
|
1582
1844
|
const toolContext = {
|
|
1583
1845
|
session_id: ctx.context.sessionId,
|
|
1584
1846
|
message_id: ctx.context.messageId,
|
|
1585
1847
|
abort: ctx.context.abort,
|
|
1848
|
+
model: effectiveRunModel,
|
|
1849
|
+
fallbackModel: ctx.context.fallbackModel,
|
|
1586
1850
|
workdir: ctx.context.metadata?.workdir,
|
|
1587
1851
|
metadata: ctx.context.metadata
|
|
1588
1852
|
};
|
|
@@ -1113,7 +1113,7 @@ class BackgroundTaskManager {
|
|
|
1113
1113
|
return null;
|
|
1114
1114
|
}
|
|
1115
1115
|
async createTask(options) {
|
|
1116
|
-
const { parentSessionId, description, prompt, subagentType, timeout, similarTaskIds, existingSubSessionId, triggerEventId } = options;
|
|
1116
|
+
const { parentSessionId, description, prompt, subagentType, timeout, similarTaskIds, existingSubSessionId, triggerEventId, model, fallbackModel } = options;
|
|
1117
1117
|
const taskIdGen = `process_${Date.now()}_${Math.random().toString(36).slice(2, 11)}`;
|
|
1118
1118
|
const sessionComponent = this.getSessionComponent();
|
|
1119
1119
|
if (!sessionComponent) {
|
|
@@ -1211,6 +1211,8 @@ class BackgroundTaskManager {
|
|
|
1211
1211
|
abortController,
|
|
1212
1212
|
similarTaskIds,
|
|
1213
1213
|
triggerEventId,
|
|
1214
|
+
model,
|
|
1215
|
+
fallbackModel,
|
|
1214
1216
|
replyChannel: replyChannelSnapshot
|
|
1215
1217
|
};
|
|
1216
1218
|
this.tasks.set(taskIdGen, task);
|
|
@@ -1622,13 +1624,19 @@ ${reuseSection.markdown}`;
|
|
|
1622
1624
|
if (signal) {
|
|
1623
1625
|
signal.addEventListener("abort", onAbort);
|
|
1624
1626
|
}
|
|
1625
|
-
const invokeHandleQuery = () =>
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1627
|
+
const invokeHandleQuery = () => {
|
|
1628
|
+
const inheritedModel = task?.model;
|
|
1629
|
+
const inheritedFallbackModel = task?.fallbackModel;
|
|
1630
|
+
return this.env.handle_query?.(delegateQuery, {
|
|
1631
|
+
sessionId,
|
|
1632
|
+
deniedTools: deniedTools.length > 0 ? deniedTools : undefined,
|
|
1633
|
+
agentType: subagentType,
|
|
1634
|
+
abort: signal,
|
|
1635
|
+
runId: capturedRunId,
|
|
1636
|
+
...inheritedModel ? { model: inheritedModel } : {},
|
|
1637
|
+
...inheritedFallbackModel ? { fallbackModel: inheritedFallbackModel } : {}
|
|
1638
|
+
});
|
|
1639
|
+
};
|
|
1632
1640
|
const wrappedPromise = processRegistry ? runWithProcessRegistryAsync(processRegistry, () => runWithBgTaskIdAsync(bgTaskId, () => invokeHandleQuery())) : runWithBgTaskIdAsync(bgTaskId, () => invokeHandleQuery());
|
|
1633
1641
|
wrappedPromise?.then((result) => {
|
|
1634
1642
|
if (settled)
|
|
@@ -2004,7 +2012,7 @@ No need to poll — just wait for the notification and use \`task_get\` with the
|
|
|
2004
2012
|
};
|
|
2005
2013
|
await globalHookManager.execute(TaskHookPoints.DELEGATE_BEFORE, delegateCtx, { sessionId: parentSessionId });
|
|
2006
2014
|
promptWithTaskInfo = delegateCtx.prompt;
|
|
2007
|
-
return await handleBackgroundTask(backgroundTaskManager, parentSessionId, description, promptWithTaskInfo, delegateCtx.subagentType, delegateCtx.timeout, similar_task_ids, params.sub_session_id, resolvedTriggerEventId);
|
|
2015
|
+
return await handleBackgroundTask(backgroundTaskManager, parentSessionId, description, promptWithTaskInfo, delegateCtx.subagentType, delegateCtx.timeout, similar_task_ids, params.sub_session_id, resolvedTriggerEventId, ctx.model, ctx.fallbackModel);
|
|
2008
2016
|
}
|
|
2009
2017
|
};
|
|
2010
2018
|
tool.execute = wrapFunction(tool.execute, "delegate.execute", {
|
|
@@ -2048,7 +2056,7 @@ function _captureReplyChannelSnapshot() {
|
|
|
2048
2056
|
};
|
|
2049
2057
|
}
|
|
2050
2058
|
var captureReplyChannelSnapshot = wrapFunction(_captureReplyChannelSnapshot, "delegate.captureReplyChannel", { recordParams: false, recordResult: true, recordError: true });
|
|
2051
|
-
async function handleBackgroundTask(backgroundTaskManager, parentSessionId, description, prompt, subagentType, timeout, similarTaskIds, existingSubSessionId, triggerEventId) {
|
|
2059
|
+
async function handleBackgroundTask(backgroundTaskManager, parentSessionId, description, prompt, subagentType, timeout, similarTaskIds, existingSubSessionId, triggerEventId, model, fallbackModel) {
|
|
2052
2060
|
const startTime = Date.now();
|
|
2053
2061
|
try {
|
|
2054
2062
|
let actualSubagentType = subagentType;
|
|
@@ -2074,7 +2082,9 @@ async function handleBackgroundTask(backgroundTaskManager, parentSessionId, desc
|
|
|
2074
2082
|
similarTaskIds,
|
|
2075
2083
|
originalWorkflowName,
|
|
2076
2084
|
existingSubSessionId,
|
|
2077
|
-
triggerEventId
|
|
2085
|
+
triggerEventId,
|
|
2086
|
+
model,
|
|
2087
|
+
fallbackModel
|
|
2078
2088
|
});
|
|
2079
2089
|
const output = [
|
|
2080
2090
|
`✅ Background task accepted`,
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// src/env/llm/redact.ts
|
|
2
|
+
function redactSecrets(text, knownSecrets = []) {
|
|
3
|
+
let out = text;
|
|
4
|
+
for (const secret of knownSecrets) {
|
|
5
|
+
if (secret && secret.length >= 8) {
|
|
6
|
+
out = out.split(secret).join("<redacted>");
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
out = out.replace(/\boc_sk_[A-Za-z0-9_-]{8,}\b/g, "<redacted>");
|
|
10
|
+
out = out.replace(/\bsk-(?:proj-)?[A-Za-z0-9_-]{16,}\b/g, "<redacted>");
|
|
11
|
+
out = out.replace(/\bBearer\s+[A-Za-z0-9._\-+/=]{12,}\b/g, "<redacted>");
|
|
12
|
+
return out;
|
|
13
|
+
}
|
|
14
|
+
var CREDENTIAL_BEARING_KEYS = new Set([
|
|
15
|
+
"url",
|
|
16
|
+
"requestBodyValues",
|
|
17
|
+
"requestBody",
|
|
18
|
+
"responseBody",
|
|
19
|
+
"responseHeaders",
|
|
20
|
+
"headers"
|
|
21
|
+
]);
|
|
22
|
+
function redactDeep(value, knownSecrets, depth = 0) {
|
|
23
|
+
if (depth > 4)
|
|
24
|
+
return value;
|
|
25
|
+
if (typeof value === "string")
|
|
26
|
+
return redactSecrets(value, knownSecrets);
|
|
27
|
+
if (Array.isArray(value))
|
|
28
|
+
return value.map((v) => redactDeep(v, knownSecrets, depth + 1));
|
|
29
|
+
if (value && typeof value === "object") {
|
|
30
|
+
const out = {};
|
|
31
|
+
for (const [k, v] of Object.entries(value)) {
|
|
32
|
+
out[k] = redactDeep(v, knownSecrets, depth + 1);
|
|
33
|
+
}
|
|
34
|
+
return out;
|
|
35
|
+
}
|
|
36
|
+
return value;
|
|
37
|
+
}
|
|
38
|
+
function redactErrorForTransport(error, knownSecrets = [], depth = 0) {
|
|
39
|
+
if (depth > 6)
|
|
40
|
+
return;
|
|
41
|
+
if (!error || typeof error !== "object") {
|
|
42
|
+
return typeof error === "string" ? redactSecrets(error, knownSecrets) : error;
|
|
43
|
+
}
|
|
44
|
+
const src = error;
|
|
45
|
+
const out = {};
|
|
46
|
+
for (const key of Object.getOwnPropertyNames(src)) {
|
|
47
|
+
const desc = Object.getOwnPropertyDescriptor(src, key);
|
|
48
|
+
if (!desc || !("value" in desc))
|
|
49
|
+
continue;
|
|
50
|
+
const value = src[key];
|
|
51
|
+
if (typeof value === "string") {
|
|
52
|
+
out[key] = redactSecrets(value, knownSecrets);
|
|
53
|
+
} else if (key === "cause" || key === "lastError") {
|
|
54
|
+
out[key] = redactErrorForTransport(value, knownSecrets, depth + 1);
|
|
55
|
+
} else if (key === "errors" && Array.isArray(value)) {
|
|
56
|
+
out[key] = value.map((inner) => redactErrorForTransport(inner, knownSecrets, depth + 1));
|
|
57
|
+
} else if (CREDENTIAL_BEARING_KEYS.has(key)) {
|
|
58
|
+
out[key] = redactDeep(value, knownSecrets);
|
|
59
|
+
} else {
|
|
60
|
+
out[key] = value;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
for (const key of ["name", "message", "stack"]) {
|
|
64
|
+
const value = src[key];
|
|
65
|
+
if (typeof value === "string" && !(key in out)) {
|
|
66
|
+
out[key] = redactSecrets(value, knownSecrets);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return out;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export { redactSecrets, redactErrorForTransport };
|