@skj1724/oh-my-opencode 3.19.7 → 3.19.9
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/cli/index.js +22 -2
- package/dist/config/index.d.ts +2 -2
- package/dist/config/schema.d.ts +251 -0
- package/dist/features/background-agent/manager.d.ts +6 -2
- package/dist/features/background-agent/types.d.ts +2 -0
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/runtime-fallback/index.d.ts +4 -0
- package/dist/index.js +709 -345
- package/dist/shared/provider-error-classifier.d.ts +1 -1
- package/dist/shared/runtime-fallback.d.ts +15 -1
- package/dist/tools/delegate-task/tools.d.ts +3 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -5140,7 +5140,7 @@ function getChain(agent, category) {
|
|
|
5140
5140
|
if (category && CATEGORY_MODEL_REQUIREMENTS[category]) {
|
|
5141
5141
|
return CATEGORY_MODEL_REQUIREMENTS[category].fallbackChain;
|
|
5142
5142
|
}
|
|
5143
|
-
|
|
5143
|
+
return;
|
|
5144
5144
|
}
|
|
5145
5145
|
function resolveNextFallbackModel(input) {
|
|
5146
5146
|
const {
|
|
@@ -5149,40 +5149,58 @@ function resolveNextFallbackModel(input) {
|
|
|
5149
5149
|
currentModel,
|
|
5150
5150
|
attempts,
|
|
5151
5151
|
availableModels,
|
|
5152
|
-
lastErrorClassification
|
|
5152
|
+
lastErrorClassification,
|
|
5153
|
+
configuredFallbackModels,
|
|
5154
|
+
maxAttempts
|
|
5153
5155
|
} = input;
|
|
5156
|
+
if (maxAttempts !== undefined && attempts.length >= maxAttempts) {
|
|
5157
|
+
return {
|
|
5158
|
+
kind: "exhausted",
|
|
5159
|
+
attempts,
|
|
5160
|
+
reason: "max fallback attempts reached",
|
|
5161
|
+
lastErrorClassification
|
|
5162
|
+
};
|
|
5163
|
+
}
|
|
5154
5164
|
const chain = getChain(agent, category);
|
|
5155
|
-
const candidates = expandChain(chain);
|
|
5165
|
+
const candidates = configuredFallbackModels && configuredFallbackModels.length > 0 ? configuredFallbackModels : chain ? expandChain(chain) : undefined;
|
|
5166
|
+
if (!candidates) {
|
|
5167
|
+
return {
|
|
5168
|
+
kind: "unconfigured",
|
|
5169
|
+
reason: `No fallback chain found for agent="${agent ?? ""}" category="${category ?? ""}"`
|
|
5170
|
+
};
|
|
5171
|
+
}
|
|
5156
5172
|
const skipKeys = new Set;
|
|
5157
5173
|
skipKeys.add(modelKey(currentModel));
|
|
5158
5174
|
for (const a of attempts) {
|
|
5159
5175
|
skipKeys.add(modelKey(a.model));
|
|
5160
5176
|
}
|
|
5161
|
-
const
|
|
5162
|
-
const currentKey = modelKey(currentModel);
|
|
5163
|
-
const isInAttempts = attempts.some((a) => modelKey(a.model) === currentKey);
|
|
5164
|
-
if (!isInAttempts) {
|
|
5165
|
-
resultAttempts.push({ model: currentModel });
|
|
5166
|
-
}
|
|
5177
|
+
const skipped = [];
|
|
5167
5178
|
const hasAvailabilityFilter = availableModels != null && availableModels.size > 0;
|
|
5168
5179
|
for (const candidate of candidates) {
|
|
5169
5180
|
const key = modelKey(candidate);
|
|
5170
|
-
if (skipKeys.has(key))
|
|
5181
|
+
if (skipKeys.has(key)) {
|
|
5182
|
+
skipped.push({ model: candidate, reason: "already attempted or current model" });
|
|
5171
5183
|
continue;
|
|
5184
|
+
}
|
|
5172
5185
|
if (hasAvailabilityFilter) {
|
|
5173
5186
|
const match = fuzzyMatchModel(key, availableModels, [candidate.providerID]);
|
|
5174
|
-
if (!match)
|
|
5187
|
+
if (!match) {
|
|
5188
|
+
skipped.push({ model: candidate, reason: "model unavailable" });
|
|
5175
5189
|
continue;
|
|
5190
|
+
}
|
|
5176
5191
|
}
|
|
5177
5192
|
return {
|
|
5178
5193
|
kind: "next",
|
|
5179
5194
|
model: candidate,
|
|
5180
|
-
attempts
|
|
5195
|
+
attempts,
|
|
5196
|
+
skipped
|
|
5181
5197
|
};
|
|
5182
5198
|
}
|
|
5183
5199
|
return {
|
|
5184
5200
|
kind: "exhausted",
|
|
5185
|
-
attempts
|
|
5201
|
+
attempts,
|
|
5202
|
+
reason: "No fallback candidates available",
|
|
5203
|
+
skipped,
|
|
5186
5204
|
lastErrorClassification
|
|
5187
5205
|
};
|
|
5188
5206
|
}
|
|
@@ -24629,6 +24647,464 @@ function createPerfProfilerHook(options) {
|
|
|
24629
24647
|
"chat.message": async () => {}
|
|
24630
24648
|
};
|
|
24631
24649
|
}
|
|
24650
|
+
// src/shared/provider-error-classifier.ts
|
|
24651
|
+
function extractErrorInfo(error) {
|
|
24652
|
+
if (typeof error === "string") {
|
|
24653
|
+
return { message: error };
|
|
24654
|
+
}
|
|
24655
|
+
if (error instanceof Error) {
|
|
24656
|
+
const anyErr = error;
|
|
24657
|
+
return {
|
|
24658
|
+
statusCode: anyErr.status ?? anyErr.statusCode ?? anyErr.httpStatus,
|
|
24659
|
+
code: anyErr.code ?? anyErr.error?.code,
|
|
24660
|
+
type: anyErr.type ?? anyErr.error?.type,
|
|
24661
|
+
message: error.message,
|
|
24662
|
+
status: anyErr.status ?? anyErr.error?.status,
|
|
24663
|
+
headers: anyErr.headers
|
|
24664
|
+
};
|
|
24665
|
+
}
|
|
24666
|
+
if (typeof error === "object" && error !== null) {
|
|
24667
|
+
const obj = error;
|
|
24668
|
+
const inner = obj.error ?? {};
|
|
24669
|
+
return {
|
|
24670
|
+
statusCode: obj.status ?? obj.statusCode ?? inner.status,
|
|
24671
|
+
code: inner.code ?? obj.code,
|
|
24672
|
+
type: inner.type ?? obj.type,
|
|
24673
|
+
message: inner.message ?? obj.message ?? String(error),
|
|
24674
|
+
status: inner.status ?? obj.status,
|
|
24675
|
+
headers: obj.headers
|
|
24676
|
+
};
|
|
24677
|
+
}
|
|
24678
|
+
return { message: String(error) };
|
|
24679
|
+
}
|
|
24680
|
+
function parseRetryAfterMs(headers) {
|
|
24681
|
+
if (!headers)
|
|
24682
|
+
return;
|
|
24683
|
+
const retryAfter = headers["retry-after"] ?? headers["Retry-After"];
|
|
24684
|
+
if (retryAfter) {
|
|
24685
|
+
const seconds = Number(retryAfter);
|
|
24686
|
+
if (!isNaN(seconds) && seconds > 0) {
|
|
24687
|
+
return seconds * 1000;
|
|
24688
|
+
}
|
|
24689
|
+
}
|
|
24690
|
+
const reset = headers["x-ratelimit-reset"] ?? headers["X-Ratelimit-Reset"];
|
|
24691
|
+
if (reset) {
|
|
24692
|
+
const resetTimestamp = Number(reset);
|
|
24693
|
+
if (!isNaN(resetTimestamp)) {
|
|
24694
|
+
const resetMs = resetTimestamp > 1000000000000 ? resetTimestamp : resetTimestamp * 1000;
|
|
24695
|
+
const delayMs = resetMs - Date.now();
|
|
24696
|
+
return delayMs > 0 ? delayMs : 0;
|
|
24697
|
+
}
|
|
24698
|
+
}
|
|
24699
|
+
return;
|
|
24700
|
+
}
|
|
24701
|
+
function isContextOverflow(message, code) {
|
|
24702
|
+
const lowerMessage = message.toLowerCase();
|
|
24703
|
+
return lowerMessage.includes("context_length_exceeded") || lowerMessage.includes("prompt is too long") || lowerMessage.includes("maximum context length") || code === "context_length_exceeded";
|
|
24704
|
+
}
|
|
24705
|
+
function isZhipuQuotaCode(code) {
|
|
24706
|
+
if (typeof code !== "number")
|
|
24707
|
+
return false;
|
|
24708
|
+
return [1113, 1304, 1308, 1309].includes(code);
|
|
24709
|
+
}
|
|
24710
|
+
function isGenericQuotaMessage(message) {
|
|
24711
|
+
const m = message.toLowerCase();
|
|
24712
|
+
return m.includes("usage quota") || m.includes("quota exceeded") || m.includes("exceeded your quota") || m.includes("quota") && m.includes("exceeded") || m.includes("upgrade your plan");
|
|
24713
|
+
}
|
|
24714
|
+
function isZhipuRateLimitCode(code) {
|
|
24715
|
+
if (typeof code !== "number")
|
|
24716
|
+
return false;
|
|
24717
|
+
return [1302, 1303].includes(code);
|
|
24718
|
+
}
|
|
24719
|
+
function isZhipuOverloadedCode(code) {
|
|
24720
|
+
if (typeof code !== "number")
|
|
24721
|
+
return false;
|
|
24722
|
+
return code === 1312;
|
|
24723
|
+
}
|
|
24724
|
+
function isGeminiQuotaDetails(details) {
|
|
24725
|
+
if (!Array.isArray(details))
|
|
24726
|
+
return false;
|
|
24727
|
+
return details.some((d) => d?.["@type"]?.includes("QuotaFailure") || d?.["@type"]?.includes("google.rpc.QuotaFailure"));
|
|
24728
|
+
}
|
|
24729
|
+
function isGeminiPerMinuteLimit(message, details) {
|
|
24730
|
+
const lowerMessage = message.toLowerCase();
|
|
24731
|
+
if (lowerMessage.includes("per_minute") || lowerMessage.includes("per minute")) {
|
|
24732
|
+
return true;
|
|
24733
|
+
}
|
|
24734
|
+
if (Array.isArray(details)) {
|
|
24735
|
+
return details.some((d) => d?.["@type"]?.includes("RetryInfo"));
|
|
24736
|
+
}
|
|
24737
|
+
return false;
|
|
24738
|
+
}
|
|
24739
|
+
function classifyProviderError(error) {
|
|
24740
|
+
const info = extractErrorInfo(error);
|
|
24741
|
+
const { statusCode, code, type: type2, message, status, headers } = info;
|
|
24742
|
+
if (isContextOverflow(message, code)) {
|
|
24743
|
+
return {
|
|
24744
|
+
category: "context_overflow",
|
|
24745
|
+
retryable: false,
|
|
24746
|
+
shouldFallback: false,
|
|
24747
|
+
statusCode,
|
|
24748
|
+
reason: "Context length exceeded, prompt too long for model"
|
|
24749
|
+
};
|
|
24750
|
+
}
|
|
24751
|
+
if (statusCode === 401 || statusCode === 403) {
|
|
24752
|
+
return {
|
|
24753
|
+
category: "auth",
|
|
24754
|
+
retryable: false,
|
|
24755
|
+
shouldFallback: false,
|
|
24756
|
+
statusCode,
|
|
24757
|
+
reason: statusCode === 401 ? "Invalid API key or authentication" : "Permission denied"
|
|
24758
|
+
};
|
|
24759
|
+
}
|
|
24760
|
+
const lowerMessage = message.toLowerCase();
|
|
24761
|
+
const isModelUnavailableMessage = lowerMessage.includes("model not found") || lowerMessage.includes("model unavailable") || lowerMessage.includes("unsupported model") || lowerMessage.includes("invalid model") || lowerMessage.includes("unknown model");
|
|
24762
|
+
const isProviderUnavailableMessage = lowerMessage.includes("provider not found") || lowerMessage.includes("unknown provider") || lowerMessage.includes("invalid provider");
|
|
24763
|
+
if (isProviderUnavailableMessage && (statusCode === 400 || statusCode === 404 || statusCode === 422)) {
|
|
24764
|
+
return {
|
|
24765
|
+
category: "provider_unavailable",
|
|
24766
|
+
retryable: false,
|
|
24767
|
+
shouldFallback: true,
|
|
24768
|
+
statusCode,
|
|
24769
|
+
reason: `Provider unavailable: ${message.substring(0, 100)}`
|
|
24770
|
+
};
|
|
24771
|
+
}
|
|
24772
|
+
if (isModelUnavailableMessage && (statusCode === 400 || statusCode === 404 || statusCode === 422)) {
|
|
24773
|
+
return {
|
|
24774
|
+
category: "model_unavailable",
|
|
24775
|
+
retryable: false,
|
|
24776
|
+
shouldFallback: true,
|
|
24777
|
+
statusCode,
|
|
24778
|
+
reason: `Model unavailable: ${message.substring(0, 100)}`
|
|
24779
|
+
};
|
|
24780
|
+
}
|
|
24781
|
+
if (statusCode === 404) {
|
|
24782
|
+
return {
|
|
24783
|
+
category: "model_unavailable",
|
|
24784
|
+
retryable: false,
|
|
24785
|
+
shouldFallback: true,
|
|
24786
|
+
statusCode,
|
|
24787
|
+
reason: `Model not found (404): ${message.substring(0, 100)}`
|
|
24788
|
+
};
|
|
24789
|
+
}
|
|
24790
|
+
if (statusCode === 400) {
|
|
24791
|
+
return {
|
|
24792
|
+
category: "bad_request",
|
|
24793
|
+
retryable: false,
|
|
24794
|
+
shouldFallback: false,
|
|
24795
|
+
statusCode,
|
|
24796
|
+
reason: "Invalid request parameters"
|
|
24797
|
+
};
|
|
24798
|
+
}
|
|
24799
|
+
if (statusCode === 429 && (code === "insufficient_quota" || type2 === "insufficient_quota")) {
|
|
24800
|
+
return {
|
|
24801
|
+
category: "quota",
|
|
24802
|
+
retryable: false,
|
|
24803
|
+
shouldFallback: true,
|
|
24804
|
+
statusCode,
|
|
24805
|
+
providerGuess: "openai",
|
|
24806
|
+
reason: "OpenAI quota exceeded, billing issue"
|
|
24807
|
+
};
|
|
24808
|
+
}
|
|
24809
|
+
if (statusCode === 402 && type2 === "billing_error") {
|
|
24810
|
+
return {
|
|
24811
|
+
category: "quota",
|
|
24812
|
+
retryable: false,
|
|
24813
|
+
shouldFallback: true,
|
|
24814
|
+
statusCode,
|
|
24815
|
+
providerGuess: "anthropic",
|
|
24816
|
+
reason: "Anthropic billing error, payment required"
|
|
24817
|
+
};
|
|
24818
|
+
}
|
|
24819
|
+
if (statusCode === 429 && status === "RESOURCE_EXHAUSTED" && isGeminiQuotaDetails(info.headers ? undefined : error?.error?.details)) {
|
|
24820
|
+
return {
|
|
24821
|
+
category: "quota",
|
|
24822
|
+
retryable: false,
|
|
24823
|
+
shouldFallback: true,
|
|
24824
|
+
statusCode,
|
|
24825
|
+
providerGuess: "gemini",
|
|
24826
|
+
reason: "Gemini daily quota exceeded"
|
|
24827
|
+
};
|
|
24828
|
+
}
|
|
24829
|
+
if (statusCode === 429 && isZhipuQuotaCode(code)) {
|
|
24830
|
+
const quotaReasons = {
|
|
24831
|
+
1113: "\u8D26\u6237\u6B20\u8D39",
|
|
24832
|
+
1304: "\u8C03\u7528\u6B21\u6570\u8D85\u8FC7\u9650\u989D",
|
|
24833
|
+
1308: "\u4F7F\u7528\u91CF\u8D85\u8FC7\u4E0A\u9650",
|
|
24834
|
+
1309: "\u5957\u9910\u5DF2\u5230\u671F"
|
|
24835
|
+
};
|
|
24836
|
+
return {
|
|
24837
|
+
category: "quota",
|
|
24838
|
+
retryable: false,
|
|
24839
|
+
shouldFallback: true,
|
|
24840
|
+
statusCode,
|
|
24841
|
+
providerGuess: "zhipu",
|
|
24842
|
+
reason: `Zhipu/GLM: ${quotaReasons[code] ?? "quota exceeded"}`
|
|
24843
|
+
};
|
|
24844
|
+
}
|
|
24845
|
+
if (statusCode === 529 && type2 === "overloaded_error") {
|
|
24846
|
+
return {
|
|
24847
|
+
category: "overloaded",
|
|
24848
|
+
retryable: true,
|
|
24849
|
+
shouldFallback: false,
|
|
24850
|
+
statusCode,
|
|
24851
|
+
providerGuess: "anthropic",
|
|
24852
|
+
reason: "Anthropic API overloaded"
|
|
24853
|
+
};
|
|
24854
|
+
}
|
|
24855
|
+
if (statusCode === 429 && isZhipuOverloadedCode(code)) {
|
|
24856
|
+
return {
|
|
24857
|
+
category: "overloaded",
|
|
24858
|
+
retryable: true,
|
|
24859
|
+
shouldFallback: false,
|
|
24860
|
+
statusCode,
|
|
24861
|
+
providerGuess: "zhipu",
|
|
24862
|
+
reason: "Zhipu/GLM: \u5F53\u524D\u8D1F\u8F7D\u8FC7\u9AD8"
|
|
24863
|
+
};
|
|
24864
|
+
}
|
|
24865
|
+
if (statusCode === 429 && type2 === "rate_limit_error") {
|
|
24866
|
+
return {
|
|
24867
|
+
category: "rate_limit",
|
|
24868
|
+
retryable: true,
|
|
24869
|
+
shouldFallback: false,
|
|
24870
|
+
statusCode,
|
|
24871
|
+
providerGuess: "anthropic",
|
|
24872
|
+
retryAfterMs: parseRetryAfterMs(headers),
|
|
24873
|
+
reason: "Anthropic rate limit exceeded"
|
|
24874
|
+
};
|
|
24875
|
+
}
|
|
24876
|
+
if (statusCode === 429 && code === "rate_limit_exceeded") {
|
|
24877
|
+
return {
|
|
24878
|
+
category: "rate_limit",
|
|
24879
|
+
retryable: true,
|
|
24880
|
+
shouldFallback: false,
|
|
24881
|
+
statusCode,
|
|
24882
|
+
providerGuess: "openai",
|
|
24883
|
+
retryAfterMs: parseRetryAfterMs(headers),
|
|
24884
|
+
reason: "OpenAI rate limit exceeded"
|
|
24885
|
+
};
|
|
24886
|
+
}
|
|
24887
|
+
if (statusCode === 429 && status === "RESOURCE_EXHAUSTED" && isGeminiPerMinuteLimit(message, error?.error?.details)) {
|
|
24888
|
+
return {
|
|
24889
|
+
category: "rate_limit",
|
|
24890
|
+
retryable: true,
|
|
24891
|
+
shouldFallback: false,
|
|
24892
|
+
statusCode,
|
|
24893
|
+
providerGuess: "gemini",
|
|
24894
|
+
retryAfterMs: parseRetryAfterMs(headers),
|
|
24895
|
+
reason: "Gemini per-minute rate limit exceeded"
|
|
24896
|
+
};
|
|
24897
|
+
}
|
|
24898
|
+
if (statusCode === 429 && isZhipuRateLimitCode(code)) {
|
|
24899
|
+
const rateLimitReasons = {
|
|
24900
|
+
1302: "\u5E76\u53D1\u8BF7\u6C42\u8D85\u8FC7\u9650\u5236",
|
|
24901
|
+
1303: "\u8BF7\u6C42\u9891\u7387\u8D85\u8FC7\u9650\u5236"
|
|
24902
|
+
};
|
|
24903
|
+
return {
|
|
24904
|
+
category: "rate_limit",
|
|
24905
|
+
retryable: true,
|
|
24906
|
+
shouldFallback: false,
|
|
24907
|
+
statusCode,
|
|
24908
|
+
providerGuess: "zhipu",
|
|
24909
|
+
retryAfterMs: parseRetryAfterMs(headers),
|
|
24910
|
+
reason: `Zhipu/GLM: ${rateLimitReasons[code] ?? "rate limit exceeded"}`
|
|
24911
|
+
};
|
|
24912
|
+
}
|
|
24913
|
+
if (statusCode === 429) {
|
|
24914
|
+
return {
|
|
24915
|
+
category: "rate_limit",
|
|
24916
|
+
retryable: true,
|
|
24917
|
+
shouldFallback: false,
|
|
24918
|
+
statusCode,
|
|
24919
|
+
retryAfterMs: parseRetryAfterMs(headers),
|
|
24920
|
+
reason: "Rate limit exceeded (generic 429)"
|
|
24921
|
+
};
|
|
24922
|
+
}
|
|
24923
|
+
if (isGenericQuotaMessage(message)) {
|
|
24924
|
+
return {
|
|
24925
|
+
category: "quota",
|
|
24926
|
+
retryable: false,
|
|
24927
|
+
shouldFallback: true,
|
|
24928
|
+
statusCode,
|
|
24929
|
+
reason: `Quota exceeded: ${message.substring(0, 100)}`
|
|
24930
|
+
};
|
|
24931
|
+
}
|
|
24932
|
+
return {
|
|
24933
|
+
category: "unknown",
|
|
24934
|
+
retryable: false,
|
|
24935
|
+
shouldFallback: false,
|
|
24936
|
+
statusCode,
|
|
24937
|
+
reason: `Unknown error: ${message.substring(0, 100)}`
|
|
24938
|
+
};
|
|
24939
|
+
}
|
|
24940
|
+
|
|
24941
|
+
// src/shared/retry-strategy.ts
|
|
24942
|
+
var DEFAULT_RETRY_CONFIG = {
|
|
24943
|
+
max_attempts: 3,
|
|
24944
|
+
initial_delay_ms: 1000,
|
|
24945
|
+
backoff_factor: 2,
|
|
24946
|
+
max_delay_ms: 30000,
|
|
24947
|
+
jitter: true,
|
|
24948
|
+
respect_retry_after: true
|
|
24949
|
+
};
|
|
24950
|
+
function calculateRetryDelay(attempt, config, retryAfterMs) {
|
|
24951
|
+
if (attempt >= config.max_attempts) {
|
|
24952
|
+
return {
|
|
24953
|
+
retryable: false,
|
|
24954
|
+
delay_ms: 0,
|
|
24955
|
+
attempt,
|
|
24956
|
+
reason: `max attempts (${config.max_attempts}) reached`
|
|
24957
|
+
};
|
|
24958
|
+
}
|
|
24959
|
+
const exponentialDelay = config.initial_delay_ms * Math.pow(config.backoff_factor, attempt);
|
|
24960
|
+
let baseDelay;
|
|
24961
|
+
let reason;
|
|
24962
|
+
if (config.respect_retry_after && retryAfterMs !== undefined && retryAfterMs > 0) {
|
|
24963
|
+
baseDelay = retryAfterMs;
|
|
24964
|
+
reason = "Retry-After";
|
|
24965
|
+
} else {
|
|
24966
|
+
baseDelay = exponentialDelay;
|
|
24967
|
+
reason = "exponential backoff";
|
|
24968
|
+
}
|
|
24969
|
+
baseDelay = Math.min(baseDelay, config.max_delay_ms);
|
|
24970
|
+
let finalDelay;
|
|
24971
|
+
if (config.jitter) {
|
|
24972
|
+
const jitterRange = baseDelay * 0.5;
|
|
24973
|
+
finalDelay = baseDelay + (Math.random() * 2 - 1) * jitterRange;
|
|
24974
|
+
finalDelay = Math.max(0, finalDelay);
|
|
24975
|
+
} else {
|
|
24976
|
+
finalDelay = baseDelay;
|
|
24977
|
+
}
|
|
24978
|
+
return {
|
|
24979
|
+
retryable: true,
|
|
24980
|
+
delay_ms: Math.round(finalDelay),
|
|
24981
|
+
attempt,
|
|
24982
|
+
reason
|
|
24983
|
+
};
|
|
24984
|
+
}
|
|
24985
|
+
|
|
24986
|
+
// src/hooks/runtime-fallback/index.ts
|
|
24987
|
+
init_logger();
|
|
24988
|
+
init_runtime_fallback();
|
|
24989
|
+
function createRuntimeFallbackHook(ctx, options) {
|
|
24990
|
+
const retryStates = new Map;
|
|
24991
|
+
const fallbackAttempts = new Map;
|
|
24992
|
+
const config = options?.config ?? {
|
|
24993
|
+
enabled: true,
|
|
24994
|
+
max_attempts: 3,
|
|
24995
|
+
max_retries_before_fallback: 2,
|
|
24996
|
+
initial_delay_ms: DEFAULT_RETRY_CONFIG.initial_delay_ms,
|
|
24997
|
+
backoff_factor: DEFAULT_RETRY_CONFIG.backoff_factor,
|
|
24998
|
+
max_delay_ms: DEFAULT_RETRY_CONFIG.max_delay_ms,
|
|
24999
|
+
respect_retry_after: DEFAULT_RETRY_CONFIG.respect_retry_after,
|
|
25000
|
+
jitter: DEFAULT_RETRY_CONFIG.jitter
|
|
25001
|
+
};
|
|
25002
|
+
const handler = async ({
|
|
25003
|
+
event
|
|
25004
|
+
}) => {
|
|
25005
|
+
if (!config.enabled)
|
|
25006
|
+
return false;
|
|
25007
|
+
if (event.type === "session.deleted") {
|
|
25008
|
+
const props2 = event.properties;
|
|
25009
|
+
const info = props2?.info;
|
|
25010
|
+
const sessionID2 = info?.id ?? props2?.sessionID;
|
|
25011
|
+
if (sessionID2) {
|
|
25012
|
+
retryStates.delete(sessionID2);
|
|
25013
|
+
fallbackAttempts.delete(sessionID2);
|
|
25014
|
+
}
|
|
25015
|
+
return false;
|
|
25016
|
+
}
|
|
25017
|
+
if (event.type !== "session.error")
|
|
25018
|
+
return false;
|
|
25019
|
+
const props = event.properties;
|
|
25020
|
+
const sessionID = props?.sessionID;
|
|
25021
|
+
const error = props?.error;
|
|
25022
|
+
if (!sessionID || error === undefined || error === null)
|
|
25023
|
+
return false;
|
|
25024
|
+
if (options?.sessionRecovery?.isRecoverableError(error)) {
|
|
25025
|
+
return false;
|
|
25026
|
+
}
|
|
25027
|
+
const classification = classifyProviderError(error);
|
|
25028
|
+
if (classification.category === "context_overflow") {
|
|
25029
|
+
return false;
|
|
25030
|
+
}
|
|
25031
|
+
if (classification.category === "auth" || classification.category === "bad_request") {
|
|
25032
|
+
return false;
|
|
25033
|
+
}
|
|
25034
|
+
if (classification.category === "rate_limit") {
|
|
25035
|
+
const state2 = retryStates.get(sessionID) ?? { attempt: 0, lastAttemptTime: Date.now() };
|
|
25036
|
+
const decision = calculateRetryDelay(state2.attempt, config, classification.retryAfterMs);
|
|
25037
|
+
if (decision.retryable && state2.attempt < config.max_retries_before_fallback) {
|
|
25038
|
+
retryStates.set(sessionID, {
|
|
25039
|
+
attempt: state2.attempt + 1,
|
|
25040
|
+
lastAttemptTime: Date.now()
|
|
25041
|
+
});
|
|
25042
|
+
await new Promise((resolve8) => setTimeout(resolve8, decision.delay_ms));
|
|
25043
|
+
return false;
|
|
25044
|
+
}
|
|
25045
|
+
retryStates.delete(sessionID);
|
|
25046
|
+
}
|
|
25047
|
+
if (classification.category !== "quota" && classification.category !== "rate_limit") {
|
|
25048
|
+
return false;
|
|
25049
|
+
}
|
|
25050
|
+
let currentModel = { providerID: "", modelID: "" };
|
|
25051
|
+
let agent = props?.agent;
|
|
25052
|
+
const category = props?.category;
|
|
25053
|
+
try {
|
|
25054
|
+
const messagesResp = await ctx.client.session.messages?.({ path: { id: sessionID } });
|
|
25055
|
+
const messages = messagesResp?.data ?? [];
|
|
25056
|
+
for (let i2 = messages.length - 1;i2 >= 0; i2--) {
|
|
25057
|
+
const info = messages[i2].info;
|
|
25058
|
+
if (!agent && info?.agent)
|
|
25059
|
+
agent = info.agent;
|
|
25060
|
+
const msgModel = info?.model;
|
|
25061
|
+
if (msgModel?.providerID && msgModel?.modelID) {
|
|
25062
|
+
currentModel = { providerID: msgModel.providerID, modelID: msgModel.modelID };
|
|
25063
|
+
break;
|
|
25064
|
+
}
|
|
25065
|
+
if (info?.providerID && info?.modelID) {
|
|
25066
|
+
currentModel = { providerID: info.providerID, modelID: info.modelID };
|
|
25067
|
+
break;
|
|
25068
|
+
}
|
|
25069
|
+
}
|
|
25070
|
+
} catch (messageReadError) {
|
|
25071
|
+
log("[runtime-fallback] failed to read session messages", { sessionID, error: String(messageReadError) });
|
|
25072
|
+
}
|
|
25073
|
+
agent ??= "sisyphus";
|
|
25074
|
+
const attempts = fallbackAttempts.get(sessionID) ?? [];
|
|
25075
|
+
const fallbackResult = resolveNextFallbackModel({
|
|
25076
|
+
agent,
|
|
25077
|
+
category,
|
|
25078
|
+
currentModel,
|
|
25079
|
+
attempts,
|
|
25080
|
+
configuredFallbackModels: options?.getConfiguredFallbackModels?.(agent, category),
|
|
25081
|
+
maxAttempts: config.max_attempts,
|
|
25082
|
+
lastErrorClassification: classification
|
|
25083
|
+
});
|
|
25084
|
+
if (fallbackResult.kind !== "next") {
|
|
25085
|
+
return false;
|
|
25086
|
+
}
|
|
25087
|
+
try {
|
|
25088
|
+
await ctx.client.session.prompt({
|
|
25089
|
+
path: { id: sessionID },
|
|
25090
|
+
body: {
|
|
25091
|
+
model: fallbackResult.model,
|
|
25092
|
+
parts: [{ type: "text", text: "continue" }]
|
|
25093
|
+
},
|
|
25094
|
+
query: { directory: ctx.directory }
|
|
25095
|
+
});
|
|
25096
|
+
fallbackAttempts.delete(sessionID);
|
|
25097
|
+
return true;
|
|
25098
|
+
} catch (fallbackError) {
|
|
25099
|
+
fallbackAttempts.set(sessionID, [
|
|
25100
|
+
...attempts,
|
|
25101
|
+
{ model: fallbackResult.model, error: classifyProviderError(fallbackError) }
|
|
25102
|
+
]);
|
|
25103
|
+
return false;
|
|
25104
|
+
}
|
|
25105
|
+
};
|
|
25106
|
+
return { handler };
|
|
25107
|
+
}
|
|
24632
25108
|
// src/features/context-injector/collector.ts
|
|
24633
25109
|
var PRIORITY_ORDER = {
|
|
24634
25110
|
critical: 0,
|
|
@@ -43385,256 +43861,7 @@ function initTaskToastManager(client2, concurrencyManager) {
|
|
|
43385
43861
|
}
|
|
43386
43862
|
// src/tools/delegate-task/tools.ts
|
|
43387
43863
|
init_shared();
|
|
43388
|
-
|
|
43389
|
-
// src/shared/provider-error-classifier.ts
|
|
43390
|
-
function extractErrorInfo(error45) {
|
|
43391
|
-
if (typeof error45 === "string") {
|
|
43392
|
-
return { message: error45 };
|
|
43393
|
-
}
|
|
43394
|
-
if (error45 instanceof Error) {
|
|
43395
|
-
const anyErr = error45;
|
|
43396
|
-
return {
|
|
43397
|
-
statusCode: anyErr.status ?? anyErr.statusCode ?? anyErr.httpStatus,
|
|
43398
|
-
code: anyErr.code ?? anyErr.error?.code,
|
|
43399
|
-
type: anyErr.type ?? anyErr.error?.type,
|
|
43400
|
-
message: error45.message,
|
|
43401
|
-
status: anyErr.status ?? anyErr.error?.status,
|
|
43402
|
-
headers: anyErr.headers
|
|
43403
|
-
};
|
|
43404
|
-
}
|
|
43405
|
-
if (typeof error45 === "object" && error45 !== null) {
|
|
43406
|
-
const obj = error45;
|
|
43407
|
-
const inner = obj.error ?? {};
|
|
43408
|
-
return {
|
|
43409
|
-
statusCode: obj.status ?? obj.statusCode ?? inner.status,
|
|
43410
|
-
code: inner.code ?? obj.code,
|
|
43411
|
-
type: inner.type ?? obj.type,
|
|
43412
|
-
message: inner.message ?? obj.message ?? String(error45),
|
|
43413
|
-
status: inner.status ?? obj.status,
|
|
43414
|
-
headers: obj.headers
|
|
43415
|
-
};
|
|
43416
|
-
}
|
|
43417
|
-
return { message: String(error45) };
|
|
43418
|
-
}
|
|
43419
|
-
function parseRetryAfterMs(headers) {
|
|
43420
|
-
if (!headers)
|
|
43421
|
-
return;
|
|
43422
|
-
const retryAfter = headers["retry-after"] ?? headers["Retry-After"];
|
|
43423
|
-
if (retryAfter) {
|
|
43424
|
-
const seconds = Number(retryAfter);
|
|
43425
|
-
if (!isNaN(seconds) && seconds > 0) {
|
|
43426
|
-
return seconds * 1000;
|
|
43427
|
-
}
|
|
43428
|
-
}
|
|
43429
|
-
const reset = headers["x-ratelimit-reset"] ?? headers["X-Ratelimit-Reset"];
|
|
43430
|
-
if (reset) {
|
|
43431
|
-
const resetTimestamp = Number(reset);
|
|
43432
|
-
if (!isNaN(resetTimestamp)) {
|
|
43433
|
-
const resetMs = resetTimestamp > 1000000000000 ? resetTimestamp : resetTimestamp * 1000;
|
|
43434
|
-
const delayMs = resetMs - Date.now();
|
|
43435
|
-
return delayMs > 0 ? delayMs : 0;
|
|
43436
|
-
}
|
|
43437
|
-
}
|
|
43438
|
-
return;
|
|
43439
|
-
}
|
|
43440
|
-
function isContextOverflow(message, code) {
|
|
43441
|
-
const lowerMessage = message.toLowerCase();
|
|
43442
|
-
return lowerMessage.includes("context_length_exceeded") || lowerMessage.includes("prompt is too long") || lowerMessage.includes("maximum context length") || code === "context_length_exceeded";
|
|
43443
|
-
}
|
|
43444
|
-
function isZhipuQuotaCode(code) {
|
|
43445
|
-
if (typeof code !== "number")
|
|
43446
|
-
return false;
|
|
43447
|
-
return [1113, 1304, 1308, 1309].includes(code);
|
|
43448
|
-
}
|
|
43449
|
-
function isZhipuRateLimitCode(code) {
|
|
43450
|
-
if (typeof code !== "number")
|
|
43451
|
-
return false;
|
|
43452
|
-
return [1302, 1303].includes(code);
|
|
43453
|
-
}
|
|
43454
|
-
function isZhipuOverloadedCode(code) {
|
|
43455
|
-
if (typeof code !== "number")
|
|
43456
|
-
return false;
|
|
43457
|
-
return code === 1312;
|
|
43458
|
-
}
|
|
43459
|
-
function isGeminiQuotaDetails(details) {
|
|
43460
|
-
if (!Array.isArray(details))
|
|
43461
|
-
return false;
|
|
43462
|
-
return details.some((d) => d?.["@type"]?.includes("QuotaFailure") || d?.["@type"]?.includes("google.rpc.QuotaFailure"));
|
|
43463
|
-
}
|
|
43464
|
-
function isGeminiPerMinuteLimit(message, details) {
|
|
43465
|
-
const lowerMessage = message.toLowerCase();
|
|
43466
|
-
if (lowerMessage.includes("per_minute") || lowerMessage.includes("per minute")) {
|
|
43467
|
-
return true;
|
|
43468
|
-
}
|
|
43469
|
-
if (Array.isArray(details)) {
|
|
43470
|
-
return details.some((d) => d?.["@type"]?.includes("RetryInfo"));
|
|
43471
|
-
}
|
|
43472
|
-
return false;
|
|
43473
|
-
}
|
|
43474
|
-
function classifyProviderError(error45) {
|
|
43475
|
-
const info = extractErrorInfo(error45);
|
|
43476
|
-
const { statusCode, code, type: type2, message, status, headers } = info;
|
|
43477
|
-
if (isContextOverflow(message, code)) {
|
|
43478
|
-
return {
|
|
43479
|
-
category: "context_overflow",
|
|
43480
|
-
retryable: false,
|
|
43481
|
-
shouldFallback: false,
|
|
43482
|
-
statusCode,
|
|
43483
|
-
reason: "Context length exceeded, prompt too long for model"
|
|
43484
|
-
};
|
|
43485
|
-
}
|
|
43486
|
-
if (statusCode === 401 || statusCode === 403) {
|
|
43487
|
-
return {
|
|
43488
|
-
category: "auth",
|
|
43489
|
-
retryable: false,
|
|
43490
|
-
shouldFallback: false,
|
|
43491
|
-
statusCode,
|
|
43492
|
-
reason: statusCode === 401 ? "Invalid API key or authentication" : "Permission denied"
|
|
43493
|
-
};
|
|
43494
|
-
}
|
|
43495
|
-
if (statusCode === 400) {
|
|
43496
|
-
return {
|
|
43497
|
-
category: "bad_request",
|
|
43498
|
-
retryable: false,
|
|
43499
|
-
shouldFallback: false,
|
|
43500
|
-
statusCode,
|
|
43501
|
-
reason: "Invalid request parameters"
|
|
43502
|
-
};
|
|
43503
|
-
}
|
|
43504
|
-
if (statusCode === 429 && (code === "insufficient_quota" || type2 === "insufficient_quota")) {
|
|
43505
|
-
return {
|
|
43506
|
-
category: "quota",
|
|
43507
|
-
retryable: false,
|
|
43508
|
-
shouldFallback: true,
|
|
43509
|
-
statusCode,
|
|
43510
|
-
providerGuess: "openai",
|
|
43511
|
-
reason: "OpenAI quota exceeded, billing issue"
|
|
43512
|
-
};
|
|
43513
|
-
}
|
|
43514
|
-
if (statusCode === 402 && type2 === "billing_error") {
|
|
43515
|
-
return {
|
|
43516
|
-
category: "quota",
|
|
43517
|
-
retryable: false,
|
|
43518
|
-
shouldFallback: true,
|
|
43519
|
-
statusCode,
|
|
43520
|
-
providerGuess: "anthropic",
|
|
43521
|
-
reason: "Anthropic billing error, payment required"
|
|
43522
|
-
};
|
|
43523
|
-
}
|
|
43524
|
-
if (statusCode === 429 && status === "RESOURCE_EXHAUSTED" && isGeminiQuotaDetails(info.headers ? undefined : error45?.error?.details)) {
|
|
43525
|
-
return {
|
|
43526
|
-
category: "quota",
|
|
43527
|
-
retryable: false,
|
|
43528
|
-
shouldFallback: true,
|
|
43529
|
-
statusCode,
|
|
43530
|
-
providerGuess: "gemini",
|
|
43531
|
-
reason: "Gemini daily quota exceeded"
|
|
43532
|
-
};
|
|
43533
|
-
}
|
|
43534
|
-
if (statusCode === 429 && isZhipuQuotaCode(code)) {
|
|
43535
|
-
const quotaReasons = {
|
|
43536
|
-
1113: "\u8D26\u6237\u6B20\u8D39",
|
|
43537
|
-
1304: "\u8C03\u7528\u6B21\u6570\u8D85\u8FC7\u9650\u989D",
|
|
43538
|
-
1308: "\u4F7F\u7528\u91CF\u8D85\u8FC7\u4E0A\u9650",
|
|
43539
|
-
1309: "\u5957\u9910\u5DF2\u5230\u671F"
|
|
43540
|
-
};
|
|
43541
|
-
return {
|
|
43542
|
-
category: "quota",
|
|
43543
|
-
retryable: false,
|
|
43544
|
-
shouldFallback: true,
|
|
43545
|
-
statusCode,
|
|
43546
|
-
providerGuess: "zhipu",
|
|
43547
|
-
reason: `Zhipu/GLM: ${quotaReasons[code] ?? "quota exceeded"}`
|
|
43548
|
-
};
|
|
43549
|
-
}
|
|
43550
|
-
if (statusCode === 529 && type2 === "overloaded_error") {
|
|
43551
|
-
return {
|
|
43552
|
-
category: "overloaded",
|
|
43553
|
-
retryable: true,
|
|
43554
|
-
shouldFallback: false,
|
|
43555
|
-
statusCode,
|
|
43556
|
-
providerGuess: "anthropic",
|
|
43557
|
-
reason: "Anthropic API overloaded"
|
|
43558
|
-
};
|
|
43559
|
-
}
|
|
43560
|
-
if (statusCode === 429 && isZhipuOverloadedCode(code)) {
|
|
43561
|
-
return {
|
|
43562
|
-
category: "overloaded",
|
|
43563
|
-
retryable: true,
|
|
43564
|
-
shouldFallback: false,
|
|
43565
|
-
statusCode,
|
|
43566
|
-
providerGuess: "zhipu",
|
|
43567
|
-
reason: "Zhipu/GLM: \u5F53\u524D\u8D1F\u8F7D\u8FC7\u9AD8"
|
|
43568
|
-
};
|
|
43569
|
-
}
|
|
43570
|
-
if (statusCode === 429 && type2 === "rate_limit_error") {
|
|
43571
|
-
return {
|
|
43572
|
-
category: "rate_limit",
|
|
43573
|
-
retryable: true,
|
|
43574
|
-
shouldFallback: false,
|
|
43575
|
-
statusCode,
|
|
43576
|
-
providerGuess: "anthropic",
|
|
43577
|
-
retryAfterMs: parseRetryAfterMs(headers),
|
|
43578
|
-
reason: "Anthropic rate limit exceeded"
|
|
43579
|
-
};
|
|
43580
|
-
}
|
|
43581
|
-
if (statusCode === 429 && code === "rate_limit_exceeded") {
|
|
43582
|
-
return {
|
|
43583
|
-
category: "rate_limit",
|
|
43584
|
-
retryable: true,
|
|
43585
|
-
shouldFallback: false,
|
|
43586
|
-
statusCode,
|
|
43587
|
-
providerGuess: "openai",
|
|
43588
|
-
retryAfterMs: parseRetryAfterMs(headers),
|
|
43589
|
-
reason: "OpenAI rate limit exceeded"
|
|
43590
|
-
};
|
|
43591
|
-
}
|
|
43592
|
-
if (statusCode === 429 && status === "RESOURCE_EXHAUSTED" && isGeminiPerMinuteLimit(message, error45?.error?.details)) {
|
|
43593
|
-
return {
|
|
43594
|
-
category: "rate_limit",
|
|
43595
|
-
retryable: true,
|
|
43596
|
-
shouldFallback: false,
|
|
43597
|
-
statusCode,
|
|
43598
|
-
providerGuess: "gemini",
|
|
43599
|
-
retryAfterMs: parseRetryAfterMs(headers),
|
|
43600
|
-
reason: "Gemini per-minute rate limit exceeded"
|
|
43601
|
-
};
|
|
43602
|
-
}
|
|
43603
|
-
if (statusCode === 429 && isZhipuRateLimitCode(code)) {
|
|
43604
|
-
const rateLimitReasons = {
|
|
43605
|
-
1302: "\u5E76\u53D1\u8BF7\u6C42\u8D85\u8FC7\u9650\u5236",
|
|
43606
|
-
1303: "\u8BF7\u6C42\u9891\u7387\u8D85\u8FC7\u9650\u5236"
|
|
43607
|
-
};
|
|
43608
|
-
return {
|
|
43609
|
-
category: "rate_limit",
|
|
43610
|
-
retryable: true,
|
|
43611
|
-
shouldFallback: false,
|
|
43612
|
-
statusCode,
|
|
43613
|
-
providerGuess: "zhipu",
|
|
43614
|
-
retryAfterMs: parseRetryAfterMs(headers),
|
|
43615
|
-
reason: `Zhipu/GLM: ${rateLimitReasons[code] ?? "rate limit exceeded"}`
|
|
43616
|
-
};
|
|
43617
|
-
}
|
|
43618
|
-
if (statusCode === 429) {
|
|
43619
|
-
return {
|
|
43620
|
-
category: "rate_limit",
|
|
43621
|
-
retryable: true,
|
|
43622
|
-
shouldFallback: false,
|
|
43623
|
-
statusCode,
|
|
43624
|
-
retryAfterMs: parseRetryAfterMs(headers),
|
|
43625
|
-
reason: "Rate limit exceeded (generic 429)"
|
|
43626
|
-
};
|
|
43627
|
-
}
|
|
43628
|
-
return {
|
|
43629
|
-
category: "unknown",
|
|
43630
|
-
retryable: false,
|
|
43631
|
-
shouldFallback: false,
|
|
43632
|
-
statusCode,
|
|
43633
|
-
reason: `Unknown error: ${message.substring(0, 100)}`
|
|
43634
|
-
};
|
|
43635
|
-
}
|
|
43636
|
-
|
|
43637
|
-
// src/tools/delegate-task/tools.ts
|
|
43864
|
+
init_runtime_fallback();
|
|
43638
43865
|
var SISYPHUS_JUNIOR_AGENT = "sisyphus-junior";
|
|
43639
43866
|
function parseModelString(model) {
|
|
43640
43867
|
const parts = model.split("/");
|
|
@@ -43643,6 +43870,19 @@ function parseModelString(model) {
|
|
|
43643
43870
|
}
|
|
43644
43871
|
return;
|
|
43645
43872
|
}
|
|
43873
|
+
function parseFallbackModelEntries(entries) {
|
|
43874
|
+
return entries?.map((entry) => {
|
|
43875
|
+
if ("model" in entry) {
|
|
43876
|
+
const parsed = parseModelString(entry.model);
|
|
43877
|
+
return {
|
|
43878
|
+
providerID: parsed?.providerID ?? "",
|
|
43879
|
+
modelID: parsed?.modelID ?? entry.model,
|
|
43880
|
+
variant: entry.variant
|
|
43881
|
+
};
|
|
43882
|
+
}
|
|
43883
|
+
return entry;
|
|
43884
|
+
});
|
|
43885
|
+
}
|
|
43646
43886
|
function getMessageDir9(sessionID) {
|
|
43647
43887
|
if (!existsSync48(MESSAGE_STORAGE))
|
|
43648
43888
|
return null;
|
|
@@ -43732,8 +43972,15 @@ ${categoryPromptAppend}`;
|
|
|
43732
43972
|
return skillContent || categoryPromptAppend;
|
|
43733
43973
|
}
|
|
43734
43974
|
function createDelegateTask(options) {
|
|
43735
|
-
const { manager, client: client2, directory, userCategories, gitMasterConfig, sisyphusJuniorModel } = options;
|
|
43975
|
+
const { manager, client: client2, directory, userCategories, gitMasterConfig, sisyphusJuniorModel, runtimeFallbackConfig, agentFallbackModels } = options;
|
|
43736
43976
|
const allCategories = { ...DEFAULT_CATEGORIES, ...userCategories };
|
|
43977
|
+
const getConfiguredFallbackModels = (agent, category) => {
|
|
43978
|
+
const agentModels = agent ? agentFallbackModels?.[agent] : undefined;
|
|
43979
|
+
if (agentModels)
|
|
43980
|
+
return parseFallbackModelEntries(agentModels);
|
|
43981
|
+
const categoryModels = category ? userCategories?.[category]?.fallback_models : undefined;
|
|
43982
|
+
return parseFallbackModelEntries(categoryModels);
|
|
43983
|
+
};
|
|
43737
43984
|
const categoryNames = Object.keys(allCategories);
|
|
43738
43985
|
const categoryExamples = categoryNames.map((k) => `'${k}'`).join(", ");
|
|
43739
43986
|
const categoryList = categoryNames.map((name) => {
|
|
@@ -44062,6 +44309,7 @@ ${textContent || "(\u65E0\u6587\u672C\u8F93\u51FA)"}
|
|
|
44062
44309
|
parentMessageID: ctx.messageID,
|
|
44063
44310
|
parentModel,
|
|
44064
44311
|
parentAgent,
|
|
44312
|
+
category: args.category,
|
|
44065
44313
|
model: categoryModel,
|
|
44066
44314
|
skills: args.load_skills.length > 0 ? args.load_skills : undefined,
|
|
44067
44315
|
skillContent: systemContent
|
|
@@ -44162,26 +44410,85 @@ Status: ${task.status}
|
|
|
44162
44410
|
}
|
|
44163
44411
|
});
|
|
44164
44412
|
} catch (promptError) {
|
|
44165
|
-
|
|
44166
|
-
|
|
44167
|
-
|
|
44168
|
-
|
|
44169
|
-
|
|
44170
|
-
|
|
44171
|
-
|
|
44413
|
+
const classification = classifyProviderError(promptError);
|
|
44414
|
+
const canFallback = runtimeFallbackConfig?.enabled !== false && (classification.retryable || classification.shouldFallback);
|
|
44415
|
+
if (canFallback) {
|
|
44416
|
+
const attempts = [];
|
|
44417
|
+
let fallbackSucceeded = false;
|
|
44418
|
+
let currentModel = categoryModel ?? { providerID: "", modelID: "" };
|
|
44419
|
+
let currentClassification = classification;
|
|
44420
|
+
while (true) {
|
|
44421
|
+
const fallbackResult = resolveNextFallbackModel({
|
|
44422
|
+
agent: agentToUse,
|
|
44423
|
+
category: args.category,
|
|
44424
|
+
currentModel,
|
|
44425
|
+
attempts,
|
|
44426
|
+
configuredFallbackModels: getConfiguredFallbackModels(agentToUse, args.category),
|
|
44427
|
+
maxAttempts: runtimeFallbackConfig?.max_attempts,
|
|
44428
|
+
lastErrorClassification: currentClassification
|
|
44429
|
+
});
|
|
44430
|
+
if (fallbackResult.kind !== "next")
|
|
44431
|
+
break;
|
|
44432
|
+
try {
|
|
44433
|
+
await client2.session.prompt({
|
|
44434
|
+
path: { id: sessionID },
|
|
44435
|
+
body: {
|
|
44436
|
+
agent: agentToUse,
|
|
44437
|
+
system: systemContent,
|
|
44438
|
+
tools: {
|
|
44439
|
+
task: false,
|
|
44440
|
+
delegate_task: false,
|
|
44441
|
+
call_omo_agent: true
|
|
44442
|
+
},
|
|
44443
|
+
parts: [{ type: "text", text: args.prompt }],
|
|
44444
|
+
model: fallbackResult.model
|
|
44445
|
+
}
|
|
44446
|
+
});
|
|
44447
|
+
fallbackSucceeded = true;
|
|
44448
|
+
break;
|
|
44449
|
+
} catch (fallbackError) {
|
|
44450
|
+
currentClassification = classifyProviderError(fallbackError);
|
|
44451
|
+
attempts.push({ model: fallbackResult.model, error: currentClassification });
|
|
44452
|
+
currentModel = fallbackResult.model;
|
|
44453
|
+
if (!currentClassification.retryable && !currentClassification.shouldFallback) {
|
|
44454
|
+
throw fallbackError;
|
|
44455
|
+
}
|
|
44456
|
+
}
|
|
44457
|
+
}
|
|
44458
|
+
if (!fallbackSucceeded) {
|
|
44459
|
+
if (toastManager && taskId !== undefined) {
|
|
44460
|
+
toastManager.removeTask(taskId);
|
|
44461
|
+
}
|
|
44462
|
+
return formatDetailedError(promptError, {
|
|
44463
|
+
operation: "\u53D1\u9001 prompt",
|
|
44464
|
+
args,
|
|
44465
|
+
sessionID,
|
|
44466
|
+
agent: agentToUse,
|
|
44467
|
+
category: args.category
|
|
44468
|
+
});
|
|
44469
|
+
}
|
|
44470
|
+
} else {
|
|
44471
|
+
if (toastManager && taskId !== undefined) {
|
|
44472
|
+
toastManager.removeTask(taskId);
|
|
44473
|
+
}
|
|
44474
|
+
const errorMessage = promptError instanceof Error ? promptError.message : String(promptError);
|
|
44475
|
+
if (errorMessage.includes("agent.name") || errorMessage.includes("undefined")) {
|
|
44476
|
+
return formatDetailedError(new Error(`Agent "${agentToUse}" \u672A\u627E\u5230\u3002\u8BF7\u786E\u8BA4\u8BE5 agent \u5DF2\u5728 opencode.json \u4E2D\u6CE8\u518C\u6216\u7531\u63D2\u4EF6\u63D0\u4F9B\u3002`), {
|
|
44477
|
+
operation: "\u53D1\u9001 prompt \u7ED9 agent",
|
|
44478
|
+
args,
|
|
44479
|
+
sessionID,
|
|
44480
|
+
agent: agentToUse,
|
|
44481
|
+
category: args.category
|
|
44482
|
+
});
|
|
44483
|
+
}
|
|
44484
|
+
return formatDetailedError(promptError, {
|
|
44485
|
+
operation: "\u53D1\u9001 prompt",
|
|
44172
44486
|
args,
|
|
44173
44487
|
sessionID,
|
|
44174
44488
|
agent: agentToUse,
|
|
44175
44489
|
category: args.category
|
|
44176
44490
|
});
|
|
44177
44491
|
}
|
|
44178
|
-
return formatDetailedError(promptError, {
|
|
44179
|
-
operation: "\u53D1\u9001 prompt",
|
|
44180
|
-
args,
|
|
44181
|
-
sessionID,
|
|
44182
|
-
agent: agentToUse,
|
|
44183
|
-
category: args.category
|
|
44184
|
-
});
|
|
44185
44492
|
}
|
|
44186
44493
|
const POLL_INTERVAL_MS = 500;
|
|
44187
44494
|
const MAX_POLL_TIME_MS = 10 * 60 * 1000;
|
|
@@ -44250,7 +44557,7 @@ Session ID: ${sessionID}`;
|
|
|
44250
44557
|
const diagnosis = classification.category !== "unknown" ? `
|
|
44251
44558
|
|
|
44252
44559
|
\uD83D\uDD0D **\u9519\u8BEF\u5206\u7C7B**: ${classification.reason}
|
|
44253
|
-
${classification.shouldFallback ? "\uD83D\uDCA1 \u6B64\u9519\u8BEF\
|
|
44560
|
+
${classification.shouldFallback ? "\uD83D\uDCA1 \u6B64\u9519\u8BEF\u7B26\u5408 runtime fallback \u6761\u4EF6\u3002" : classification.retryable ? "\u23F3 \u6B64\u9519\u8BEF\u53EF\u91CD\u8BD5\u3002" : ""}` : "";
|
|
44254
44561
|
return `Error fetching result: ${messagesResult.error}${diagnosis}
|
|
44255
44562
|
|
|
44256
44563
|
Session ID: ${sessionID}`;
|
|
@@ -44293,7 +44600,7 @@ ${textContent || "(\u65E0\u6587\u672C\u8F93\u51FA)"}
|
|
|
44293
44600
|
const diagnosis = classification.category !== "unknown" ? `
|
|
44294
44601
|
|
|
44295
44602
|
\uD83D\uDD0D **\u9519\u8BEF\u5206\u7C7B**: ${classification.reason}
|
|
44296
|
-
${classification.shouldFallback ? "\uD83D\uDCA1 \u6B64\u9519\u8BEF\
|
|
44603
|
+
${classification.shouldFallback ? "\uD83D\uDCA1 \u6B64\u9519\u8BEF\u7B26\u5408 runtime fallback \u6761\u4EF6\u3002" : classification.retryable ? "\u23F3 \u6B64\u9519\u8BEF\u53EF\u91CD\u8BD5\u3002" : ""}` : "";
|
|
44297
44604
|
return `\u4EFB\u52A1\u6267\u884C\u5931\u8D25: ${error45 instanceof Error ? error45.message : String(error45)}${diagnosis}
|
|
44298
44605
|
|
|
44299
44606
|
Session ID: ${syncSessionID ?? "unknown"}`;
|
|
@@ -44548,6 +44855,7 @@ class BackgroundManager {
|
|
|
44548
44855
|
parentMessageID: input.parentMessageID,
|
|
44549
44856
|
parentModel: input.parentModel,
|
|
44550
44857
|
parentAgent: input.parentAgent,
|
|
44858
|
+
category: input.category,
|
|
44551
44859
|
model: input.model,
|
|
44552
44860
|
maxSteps: this.config?.maxSteps,
|
|
44553
44861
|
maxRuntimeMs: this.config?.maxRuntimeMs,
|
|
@@ -44676,72 +44984,79 @@ class BackgroundManager {
|
|
|
44676
44984
|
},
|
|
44677
44985
|
parts: [{ type: "text", text: input.prompt }]
|
|
44678
44986
|
}
|
|
44679
|
-
}).catch((error45) => {
|
|
44680
|
-
|
|
44681
|
-
|
|
44682
|
-
|
|
44683
|
-
|
|
44684
|
-
|
|
44685
|
-
|
|
44686
|
-
|
|
44687
|
-
|
|
44688
|
-
|
|
44689
|
-
|
|
44690
|
-
|
|
44691
|
-
|
|
44692
|
-
|
|
44693
|
-
|
|
44694
|
-
|
|
44987
|
+
}).catch(async (error45) => {
|
|
44988
|
+
await this.handlePromptFailure(sessionID, input, error45);
|
|
44989
|
+
});
|
|
44990
|
+
}
|
|
44991
|
+
async handlePromptFailure(sessionID, input, error45) {
|
|
44992
|
+
log("[background-agent] promptAsync error:", error45);
|
|
44993
|
+
const existingTask = this.findBySession(sessionID);
|
|
44994
|
+
if (!existingTask)
|
|
44995
|
+
return;
|
|
44996
|
+
const runtimeFallback = this.config?.runtimeFallback;
|
|
44997
|
+
const classification = classifyProviderError(error45);
|
|
44998
|
+
const canFallback = runtimeFallback?.enabled !== false && (classification.retryable || classification.shouldFallback);
|
|
44999
|
+
if (canFallback) {
|
|
45000
|
+
let currentError = error45;
|
|
45001
|
+
let currentClassification = classification;
|
|
45002
|
+
let currentModel = input.model ?? { providerID: "", modelID: "" };
|
|
45003
|
+
while (true) {
|
|
45004
|
+
const attempts = existingTask.attempts ?? [];
|
|
45005
|
+
const fallbackResult = resolveNextFallbackModel({
|
|
45006
|
+
agent: input.agent,
|
|
45007
|
+
category: input.category,
|
|
45008
|
+
currentModel,
|
|
45009
|
+
attempts,
|
|
45010
|
+
maxAttempts: runtimeFallback?.max_attempts,
|
|
45011
|
+
lastErrorClassification: currentClassification
|
|
45012
|
+
});
|
|
45013
|
+
if (fallbackResult.kind !== "next") {
|
|
45014
|
+
existingTask.error = fallbackResult.kind === "exhausted" ? `All fallback models exhausted. Last error: ${currentClassification.reason}` : fallbackResult.reason;
|
|
45015
|
+
break;
|
|
45016
|
+
}
|
|
45017
|
+
log("[background-agent] Fallback to model:", fallbackResult.model);
|
|
45018
|
+
try {
|
|
45019
|
+
await this.client.session.prompt({
|
|
45020
|
+
path: { id: sessionID },
|
|
45021
|
+
body: {
|
|
45022
|
+
agent: input.agent,
|
|
44695
45023
|
model: fallbackResult.model,
|
|
44696
|
-
|
|
44697
|
-
}
|
|
44698
|
-
|
|
44699
|
-
|
|
44700
|
-
|
|
44701
|
-
|
|
44702
|
-
|
|
44703
|
-
|
|
44704
|
-
|
|
44705
|
-
|
|
44706
|
-
|
|
44707
|
-
|
|
44708
|
-
|
|
44709
|
-
|
|
44710
|
-
|
|
44711
|
-
|
|
44712
|
-
task2.completedAt = new Date;
|
|
44713
|
-
if (task2.concurrencyKey) {
|
|
44714
|
-
this.concurrencyManager.release(task2.concurrencyKey);
|
|
44715
|
-
task2.concurrencyKey = undefined;
|
|
44716
|
-
}
|
|
44717
|
-
this.markForNotification(task2);
|
|
44718
|
-
this.notifyParentSession(task2).catch((err) => {
|
|
44719
|
-
log("[background-agent] Failed to notify on fallback error:", err);
|
|
44720
|
-
});
|
|
44721
|
-
}
|
|
44722
|
-
});
|
|
44723
|
-
return;
|
|
44724
|
-
}
|
|
44725
|
-
existingTask.error = `All fallback models exhausted. Last error: ${classification.reason}`;
|
|
44726
|
-
} else {
|
|
44727
|
-
const errorMessage = error45 instanceof Error ? error45.message : String(error45);
|
|
44728
|
-
if (errorMessage.includes("agent.name") || errorMessage.includes("undefined")) {
|
|
44729
|
-
existingTask.error = `Agent "${input.agent}" not found. Make sure the agent is registered in your opencode.json or provided by a plugin.`;
|
|
44730
|
-
} else {
|
|
44731
|
-
existingTask.error = errorMessage;
|
|
45024
|
+
parts: [{ type: "text", text: input.prompt }]
|
|
45025
|
+
}
|
|
45026
|
+
});
|
|
45027
|
+
existingTask.attempts = attempts;
|
|
45028
|
+
return;
|
|
45029
|
+
} catch (retryError) {
|
|
45030
|
+
currentError = retryError;
|
|
45031
|
+
currentClassification = classifyProviderError(currentError);
|
|
45032
|
+
existingTask.attempts = [...attempts, {
|
|
45033
|
+
model: fallbackResult.model,
|
|
45034
|
+
error: currentClassification
|
|
45035
|
+
}];
|
|
45036
|
+
currentModel = fallbackResult.model;
|
|
45037
|
+
if (!currentClassification.retryable && !currentClassification.shouldFallback) {
|
|
45038
|
+
existingTask.error = `Fallback failed: ${retryError instanceof Error ? retryError.message : String(retryError)}`;
|
|
45039
|
+
break;
|
|
44732
45040
|
}
|
|
44733
45041
|
}
|
|
44734
|
-
existingTask.status = "error";
|
|
44735
|
-
existingTask.completedAt = new Date;
|
|
44736
|
-
if (existingTask.concurrencyKey) {
|
|
44737
|
-
this.concurrencyManager.release(existingTask.concurrencyKey);
|
|
44738
|
-
existingTask.concurrencyKey = undefined;
|
|
44739
|
-
}
|
|
44740
|
-
this.markForNotification(existingTask);
|
|
44741
|
-
this.notifyParentSession(existingTask).catch((err) => {
|
|
44742
|
-
log("[background-agent] Failed to notify on error:", err);
|
|
44743
|
-
});
|
|
44744
45042
|
}
|
|
45043
|
+
} else {
|
|
45044
|
+
const errorMessage = error45 instanceof Error ? error45.message : String(error45);
|
|
45045
|
+
if (errorMessage.includes("agent.name") || errorMessage.includes("undefined")) {
|
|
45046
|
+
existingTask.error = `Agent "${input.agent}" not found. Make sure the agent is registered in your opencode.json or provided by a plugin.`;
|
|
45047
|
+
} else {
|
|
45048
|
+
existingTask.error = errorMessage;
|
|
45049
|
+
}
|
|
45050
|
+
}
|
|
45051
|
+
existingTask.status = "error";
|
|
45052
|
+
existingTask.completedAt = new Date;
|
|
45053
|
+
if (existingTask.concurrencyKey) {
|
|
45054
|
+
this.concurrencyManager.release(existingTask.concurrencyKey);
|
|
45055
|
+
existingTask.concurrencyKey = undefined;
|
|
45056
|
+
}
|
|
45057
|
+
this.markForNotification(existingTask);
|
|
45058
|
+
this.notifyParentSession(existingTask).catch((err) => {
|
|
45059
|
+
log("[background-agent] Failed to notify on error:", err);
|
|
44745
45060
|
});
|
|
44746
45061
|
}
|
|
44747
45062
|
getTask(id) {
|
|
@@ -63568,6 +63883,23 @@ var BuiltinCommandNameSchema = exports_external2.enum([
|
|
|
63568
63883
|
"init-deep",
|
|
63569
63884
|
"start-work"
|
|
63570
63885
|
]);
|
|
63886
|
+
var ProviderModelStringSchema = exports_external2.string().refine((value) => {
|
|
63887
|
+
const separatorIndex = value.indexOf("/");
|
|
63888
|
+
return separatorIndex > 0 && separatorIndex < value.length - 1;
|
|
63889
|
+
}, "Expected provider/model format");
|
|
63890
|
+
var NonEmptyStringSchema = exports_external2.string().min(1);
|
|
63891
|
+
var FallbackModelEntrySchema = exports_external2.union([
|
|
63892
|
+
exports_external2.object({
|
|
63893
|
+
model: ProviderModelStringSchema,
|
|
63894
|
+
variant: exports_external2.string().optional()
|
|
63895
|
+
}).strict(),
|
|
63896
|
+
exports_external2.object({
|
|
63897
|
+
providerID: NonEmptyStringSchema,
|
|
63898
|
+
modelID: NonEmptyStringSchema,
|
|
63899
|
+
variant: exports_external2.string().optional()
|
|
63900
|
+
}).strict()
|
|
63901
|
+
]);
|
|
63902
|
+
var FallbackModelsSchema = exports_external2.array(FallbackModelEntrySchema);
|
|
63571
63903
|
var AgentOverrideConfigSchema = exports_external2.object({
|
|
63572
63904
|
model: exports_external2.string().optional(),
|
|
63573
63905
|
variant: exports_external2.string().optional(),
|
|
@@ -63582,7 +63914,8 @@ var AgentOverrideConfigSchema = exports_external2.object({
|
|
|
63582
63914
|
description: exports_external2.string().optional(),
|
|
63583
63915
|
mode: exports_external2.enum(["subagent", "primary", "all"]).optional(),
|
|
63584
63916
|
color: exports_external2.string().regex(/^#[0-9A-Fa-f]{6}$/).optional(),
|
|
63585
|
-
permission: AgentPermissionSchema.optional()
|
|
63917
|
+
permission: AgentPermissionSchema.optional(),
|
|
63918
|
+
fallback_models: FallbackModelsSchema.optional()
|
|
63586
63919
|
});
|
|
63587
63920
|
var AgentOverridesSchema = exports_external2.object({
|
|
63588
63921
|
build: AgentOverrideConfigSchema.optional(),
|
|
@@ -63618,6 +63951,7 @@ var CategoryConfigSchema = exports_external2.object({
|
|
|
63618
63951
|
description: exports_external2.string().optional(),
|
|
63619
63952
|
model: exports_external2.string().optional(),
|
|
63620
63953
|
variant: exports_external2.string().optional(),
|
|
63954
|
+
fallback_models: FallbackModelsSchema.optional(),
|
|
63621
63955
|
temperature: exports_external2.number().min(0).max(2).optional(),
|
|
63622
63956
|
top_p: exports_external2.number().min(0).max(1).optional(),
|
|
63623
63957
|
maxTokens: exports_external2.number().optional(),
|
|
@@ -63748,6 +64082,7 @@ var GitMasterConfigSchema = exports_external2.object({
|
|
|
63748
64082
|
var RuntimeFallbackConfigSchema = exports_external2.object({
|
|
63749
64083
|
enabled: exports_external2.boolean().default(true),
|
|
63750
64084
|
max_attempts: exports_external2.number().min(0).default(3),
|
|
64085
|
+
max_retries_before_fallback: exports_external2.number().min(0).default(2),
|
|
63751
64086
|
initial_delay_ms: exports_external2.number().min(0).default(2000),
|
|
63752
64087
|
backoff_factor: exports_external2.number().min(1).default(2),
|
|
63753
64088
|
max_delay_ms: exports_external2.number().min(0).default(30000),
|
|
@@ -68829,9 +69164,31 @@ var OhMyOpenCodePlugin = async (ctx) => {
|
|
|
68829
69164
|
const disabledHooks = new Set(pluginConfig.disabled_hooks ?? []);
|
|
68830
69165
|
const firstMessageVariantGate = createFirstMessageVariantGate();
|
|
68831
69166
|
const isHookEnabled = (hookName) => !disabledHooks.has(hookName);
|
|
69167
|
+
const parseConfiguredFallbackModels = (entries) => entries?.map((entry) => {
|
|
69168
|
+
if ("model" in entry) {
|
|
69169
|
+
const separatorIndex = entry.model.indexOf("/");
|
|
69170
|
+
return {
|
|
69171
|
+
providerID: entry.model.slice(0, separatorIndex),
|
|
69172
|
+
modelID: entry.model.slice(separatorIndex + 1),
|
|
69173
|
+
variant: entry.variant
|
|
69174
|
+
};
|
|
69175
|
+
}
|
|
69176
|
+
return entry;
|
|
69177
|
+
});
|
|
68832
69178
|
const modelCacheState = createModelCacheState();
|
|
68833
69179
|
const contextWindowMonitor = isHookEnabled("context-window-monitor") ? createContextWindowMonitorHook(ctx) : null;
|
|
68834
69180
|
const sessionRecovery = isHookEnabled("session-recovery") ? createSessionRecoveryHook(ctx, { experimental: pluginConfig.experimental }) : null;
|
|
69181
|
+
const runtimeFallback = isHookEnabled("runtime-fallback") && pluginConfig.runtime_fallback?.enabled !== false ? createRuntimeFallbackHook(ctx, {
|
|
69182
|
+
config: pluginConfig.runtime_fallback,
|
|
69183
|
+
sessionRecovery: sessionRecovery ?? undefined,
|
|
69184
|
+
getConfiguredFallbackModels: (agent, category) => {
|
|
69185
|
+
const agentModels = agent && pluginConfig.agents?.[agent]?.fallback_models;
|
|
69186
|
+
if (agentModels)
|
|
69187
|
+
return parseConfiguredFallbackModels(agentModels);
|
|
69188
|
+
const categoryModels = category ? pluginConfig.categories?.[category]?.fallback_models : undefined;
|
|
69189
|
+
return parseConfiguredFallbackModels(categoryModels);
|
|
69190
|
+
}
|
|
69191
|
+
}) : null;
|
|
68835
69192
|
let sessionNotification = null;
|
|
68836
69193
|
if (isHookEnabled("session-notification")) {
|
|
68837
69194
|
const forceEnable = pluginConfig.notification?.force_enable ?? false;
|
|
@@ -68884,7 +69241,10 @@ var OhMyOpenCodePlugin = async (ctx) => {
|
|
|
68884
69241
|
const prometheusMdOnly = isHookEnabled("prometheus-md-only") ? createPrometheusMdOnlyHook(ctx) : null;
|
|
68885
69242
|
const questionLabelTruncator = createQuestionLabelTruncatorHook();
|
|
68886
69243
|
const taskResumeInfo = createTaskResumeInfoHook();
|
|
68887
|
-
const backgroundManager = new BackgroundManager(ctx,
|
|
69244
|
+
const backgroundManager = new BackgroundManager(ctx, {
|
|
69245
|
+
...pluginConfig.background_task,
|
|
69246
|
+
runtimeFallback: pluginConfig.runtime_fallback
|
|
69247
|
+
});
|
|
68888
69248
|
const atlasHook = isHookEnabled("atlas") ? createAtlasHook(ctx, { directory: ctx.directory, backgroundManager }) : null;
|
|
68889
69249
|
const perfTracer = pluginConfig.experimental?.profiling?.enabled ? new PerfTracer({
|
|
68890
69250
|
enabled: true,
|
|
@@ -68931,7 +69291,9 @@ var OhMyOpenCodePlugin = async (ctx) => {
|
|
|
68931
69291
|
directory: ctx.directory,
|
|
68932
69292
|
userCategories: pluginConfig.categories,
|
|
68933
69293
|
gitMasterConfig: pluginConfig.git_master,
|
|
68934
|
-
sisyphusJuniorModel: pluginConfig.agents?.["sisyphus-junior"]?.model
|
|
69294
|
+
sisyphusJuniorModel: pluginConfig.agents?.["sisyphus-junior"]?.model,
|
|
69295
|
+
runtimeFallbackConfig: pluginConfig.runtime_fallback,
|
|
69296
|
+
agentFallbackModels: Object.fromEntries(Object.entries(pluginConfig.agents ?? {}).map(([agent, config4]) => [agent, config4?.fallback_models]))
|
|
68935
69297
|
});
|
|
68936
69298
|
const disabledSkills = new Set(pluginConfig.disabled_skills ?? []);
|
|
68937
69299
|
const systemMcpNames = getSystemMcpServerNames();
|
|
@@ -69100,6 +69462,8 @@ var OhMyOpenCodePlugin = async (ctx) => {
|
|
|
69100
69462
|
hookCount++;
|
|
69101
69463
|
await wrapWithTiming(perfTracer, "event", "anthropicContextWindowLimitRecovery", () => anthropicContextWindowLimitRecovery?.event(input), evtSessionID);
|
|
69102
69464
|
hookCount++;
|
|
69465
|
+
await wrapWithTiming(perfTracer, "event", "runtimeFallback", () => runtimeFallback?.handler(input), evtSessionID);
|
|
69466
|
+
hookCount++;
|
|
69103
69467
|
await wrapWithTiming(perfTracer, "event", "agentUsageReminder", () => agentUsageReminder?.event(input), evtSessionID);
|
|
69104
69468
|
hookCount++;
|
|
69105
69469
|
await wrapWithTiming(perfTracer, "event", "interactiveBashSession", () => interactiveBashSession?.event(input), evtSessionID);
|