oh-my-opencode 3.17.6 → 3.17.7
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 +21 -19
- package/dist/features/background-agent/attempt-lifecycle.d.ts +12 -0
- package/dist/features/background-agent/background-task-notification-template.d.ts +2 -1
- package/dist/features/background-agent/constants.d.ts +1 -0
- package/dist/features/background-agent/fallback-retry-handler.d.ts +8 -0
- package/dist/features/background-agent/manager.d.ts +9 -0
- package/dist/features/background-agent/types.d.ts +24 -0
- package/dist/hooks/model-fallback/controller-accessor.d.ts +1 -0
- package/dist/hooks/model-fallback/fallback-state-controller.d.ts +1 -0
- package/dist/hooks/model-fallback/hook.d.ts +2 -1
- package/dist/hooks/preemptive-compaction-degradation-monitor.d.ts +1 -0
- package/dist/hooks/preemptive-compaction-no-text-tail.d.ts +1 -0
- package/dist/index.js +988 -326
- package/dist/plugin/event.d.ts +1 -0
- package/dist/shared/dynamic-truncator.d.ts +9 -10
- package/dist/shared/model-error-classifier.d.ts +2 -2
- package/dist/tools/background-task/clients.d.ts +1 -0
- package/dist/tools/delegate-task/builtin-categories.d.ts +1 -0
- package/dist/tools/delegate-task/builtin-category-definition.d.ts +1 -0
- package/dist/tools/delegate-task/constants.d.ts +1 -1
- package/dist/tools/delegate-task/executor-types.d.ts +1 -0
- package/dist/tools/delegate-task/openai-categories.d.ts +3 -0
- package/dist/tools/delegate-task/sync-task-fallback.d.ts +3 -0
- package/package.json +12 -12
package/dist/index.js
CHANGED
|
@@ -4446,6 +4446,56 @@ var require_picomatch2 = __commonJS((exports, module) => {
|
|
|
4446
4446
|
module.exports = picomatch;
|
|
4447
4447
|
});
|
|
4448
4448
|
|
|
4449
|
+
// src/agents/types.ts
|
|
4450
|
+
function extractModelName(model) {
|
|
4451
|
+
return model.includes("/") ? model.split("/").pop() ?? model : model;
|
|
4452
|
+
}
|
|
4453
|
+
function isGptModel(model) {
|
|
4454
|
+
const modelName = extractModelName(model).toLowerCase();
|
|
4455
|
+
return modelName.includes("gpt");
|
|
4456
|
+
}
|
|
4457
|
+
function isGptNativeSisyphusModel(model) {
|
|
4458
|
+
const modelName = extractModelName(model).toLowerCase();
|
|
4459
|
+
return GPT_NATIVE_SISYPHUS_RE.test(modelName);
|
|
4460
|
+
}
|
|
4461
|
+
function isGpt5_5Model(model) {
|
|
4462
|
+
const modelName = extractModelName(model).toLowerCase();
|
|
4463
|
+
return modelName.includes("gpt-5.5") || modelName.includes("gpt-5-5");
|
|
4464
|
+
}
|
|
4465
|
+
function isGpt5_3CodexModel(model) {
|
|
4466
|
+
const modelName = extractModelName(model).toLowerCase();
|
|
4467
|
+
return modelName.includes("gpt-5.3-codex") || modelName.includes("gpt-5-3-codex");
|
|
4468
|
+
}
|
|
4469
|
+
function isClaudeOpus47Model(model) {
|
|
4470
|
+
const modelName = extractModelName(model).toLowerCase().replaceAll(".", "-");
|
|
4471
|
+
return modelName.includes("claude-opus-4-7");
|
|
4472
|
+
}
|
|
4473
|
+
function isKimiK2Model(model) {
|
|
4474
|
+
const modelName = extractModelName(model).toLowerCase();
|
|
4475
|
+
if (modelName.includes("kimi"))
|
|
4476
|
+
return true;
|
|
4477
|
+
if (/k2[-.]?p[56]/.test(modelName))
|
|
4478
|
+
return true;
|
|
4479
|
+
return false;
|
|
4480
|
+
}
|
|
4481
|
+
function isGlmModel(model) {
|
|
4482
|
+
const modelName = extractModelName(model).toLowerCase();
|
|
4483
|
+
return modelName.includes("glm");
|
|
4484
|
+
}
|
|
4485
|
+
function isGeminiModel(model) {
|
|
4486
|
+
if (GEMINI_PROVIDERS.some((prefix) => model.startsWith(prefix)))
|
|
4487
|
+
return true;
|
|
4488
|
+
if (model.startsWith("github-copilot/") && extractModelName(model).toLowerCase().startsWith("gemini"))
|
|
4489
|
+
return true;
|
|
4490
|
+
const modelName = extractModelName(model).toLowerCase();
|
|
4491
|
+
return modelName.startsWith("gemini-");
|
|
4492
|
+
}
|
|
4493
|
+
var GPT_NATIVE_SISYPHUS_RE, GEMINI_PROVIDERS;
|
|
4494
|
+
var init_types = __esm(() => {
|
|
4495
|
+
GPT_NATIVE_SISYPHUS_RE = /gpt-5[.-](?:[4-9]|\d{2,})/i;
|
|
4496
|
+
GEMINI_PROVIDERS = ["google/", "google-vertex/"];
|
|
4497
|
+
});
|
|
4498
|
+
|
|
4449
4499
|
// src/hooks/ralph-loop/constants.ts
|
|
4450
4500
|
var HOOK_NAME3 = "ralph-loop", DEFAULT_STATE_FILE = ".sisyphus/ralph-loop.local.md", DEFAULT_MAX_ITERATIONS = 100, ULTRAWORK_MAX_ITERATIONS = 500, DEFAULT_COMPLETION_PROMISE = "DONE", ULTRAWORK_VERIFICATION_PROMISE = "VERIFIED";
|
|
4451
4501
|
var init_constants = () => {};
|
|
@@ -8035,6 +8085,12 @@ var init_kimi_categories = __esm(() => {
|
|
|
8035
8085
|
});
|
|
8036
8086
|
|
|
8037
8087
|
// src/tools/delegate-task/openai-categories.ts
|
|
8088
|
+
function resolveDeepCategoryPromptAppend(model) {
|
|
8089
|
+
if (model && isGpt5_5Model(model)) {
|
|
8090
|
+
return DEEP_CATEGORY_PROMPT_APPEND_GPT_5_5;
|
|
8091
|
+
}
|
|
8092
|
+
return DEEP_CATEGORY_PROMPT_APPEND;
|
|
8093
|
+
}
|
|
8038
8094
|
var ULTRABRAIN_CATEGORY_PROMPT_APPEND = `<Category_Context>
|
|
8039
8095
|
You are working on DEEP LOGICAL REASONING / COMPLEX ARCHITECTURE tasks.
|
|
8040
8096
|
|
|
@@ -8074,6 +8130,26 @@ Genuinely independent tasks = flag and refuse, require separate delegations.
|
|
|
8074
8130
|
Approach: explore extensively, understand deeply, then act decisively. Prefer comprehensive solutions over quick patches. If the goal is unclear, make reasonable assumptions and proceed.
|
|
8075
8131
|
|
|
8076
8132
|
Minimal status updates. Focus on results, not play-by-play. Report completion with summary of changes.
|
|
8133
|
+
</Category_Context>`, DEEP_CATEGORY_PROMPT_APPEND_GPT_5_5 = `<Category_Context name="deep">
|
|
8134
|
+
You are operating in DEEP mode. This is the category reserved for goal-oriented autonomous work on hairy problems that reward thorough exploration and comprehensive solutions.
|
|
8135
|
+
|
|
8136
|
+
The orchestrator chose this category because the task benefits from depth over speed. You should feel empowered to spend the time needed: five to fifteen minutes of silent exploration before the first edit is normal and correct. Rushing to implementation on a deep task is a failure mode, not a feature.
|
|
8137
|
+
|
|
8138
|
+
# How deep mode adjusts the base behavior
|
|
8139
|
+
|
|
8140
|
+
**Exploration budget: generous.** Read the files you need, trace dependencies both directions, fire 2-5 explore/librarian sub-agents in parallel for broader questions. Build a complete mental model before the first \`apply_patch\`. Exploration here is an investment, not overhead.
|
|
8141
|
+
|
|
8142
|
+
**Goal, not plan.** You receive a GOAL describing the desired outcome. You figure out HOW to achieve it. The orchestrator deliberately did not hand you a step-by-step plan; producing one and asking for approval is not what was asked. Execute.
|
|
8143
|
+
|
|
8144
|
+
**Atomic task treatment.** When the goal contains numbered steps or phases, treat them as sub-steps of ONE task and execute them all in this turn. Splitting them across turns is wrong unless they reveal an architectural blocker that requires the user's input. If the "steps" turn out to be genuinely independent tasks that should have been separate delegations, flag that in your final message and refuse the ones beyond scope.
|
|
8145
|
+
|
|
8146
|
+
**Root cause bias.** Prefer root-cause fixes over symptom fixes. A null check around \`foo()\` is a symptom fix; fixing whatever causes \`foo()\` to return unexpected values is the root fix. Trace at least two levels up before settling on an answer. In deep mode, you have permission (and the expectation) to do the deeper fix.
|
|
8147
|
+
|
|
8148
|
+
**Ambition scaled to context.** For brand-new greenfield work, be ambitious. Choose strong defaults, avoid AI-slop aesthetics, produce something you would be proud to hand to another senior engineer. For changes in an existing codebase, be surgical and respect the existing patterns; depth does not mean invasiveness.
|
|
8149
|
+
|
|
8150
|
+
**Completion bar: full delivery.** "Simplified version", "proof of concept", and "you can extend this later" are not acceptable deliveries for a deep task. The orchestrator routed here specifically for a complete solution. If you hit a genuine blocker (missing secret, design decision only the user can make, three materially different attempts all failed), document it and return; otherwise, finish the task.
|
|
8151
|
+
|
|
8152
|
+
**Status cadence: sparse.** The user is not on the other side of this conversation; the orchestrator is, and they will synthesize your progress. Send commentary only at meaningful phase transitions (starting exploration, starting implementation, starting verification, hitting a genuine blocker). Do not narrate every tool call; silence during focused work is expected.
|
|
8077
8153
|
</Category_Context>`, QUICK_CATEGORY_PROMPT_APPEND = `<Category_Context>
|
|
8078
8154
|
You are working on SMALL / QUICK tasks.
|
|
8079
8155
|
|
|
@@ -8125,6 +8201,7 @@ EXPECTED OUTPUT:
|
|
|
8125
8201
|
If your prompt lacks this structure, REWRITE IT before delegating.
|
|
8126
8202
|
</Caller_Warning>`, OPENAI_CATEGORIES;
|
|
8127
8203
|
var init_openai_categories = __esm(() => {
|
|
8204
|
+
init_types();
|
|
8128
8205
|
OPENAI_CATEGORIES = [
|
|
8129
8206
|
{
|
|
8130
8207
|
name: "ultrabrain",
|
|
@@ -8136,7 +8213,8 @@ var init_openai_categories = __esm(() => {
|
|
|
8136
8213
|
name: "deep",
|
|
8137
8214
|
config: { model: "openai/gpt-5.5", variant: "medium" },
|
|
8138
8215
|
description: "Goal-oriented autonomous problem-solving. Thorough research before action. For hairy problems requiring deep understanding.",
|
|
8139
|
-
promptAppend: DEEP_CATEGORY_PROMPT_APPEND
|
|
8216
|
+
promptAppend: DEEP_CATEGORY_PROMPT_APPEND,
|
|
8217
|
+
resolvePromptAppend: resolveDeepCategoryPromptAppend
|
|
8140
8218
|
},
|
|
8141
8219
|
{
|
|
8142
8220
|
name: "quick",
|
|
@@ -8151,7 +8229,7 @@ var init_openai_categories = __esm(() => {
|
|
|
8151
8229
|
function buildCategoryRecord(selector) {
|
|
8152
8230
|
return Object.fromEntries(BUILTIN_CATEGORIES.map((definition) => [definition.name, selector(definition)]));
|
|
8153
8231
|
}
|
|
8154
|
-
var BUILTIN_CATEGORIES, DEFAULT_CATEGORIES, CATEGORY_PROMPT_APPENDS, CATEGORY_DESCRIPTIONS;
|
|
8232
|
+
var BUILTIN_CATEGORIES, DEFAULT_CATEGORIES, CATEGORY_PROMPT_APPENDS, CATEGORY_DESCRIPTIONS, CATEGORY_PROMPT_APPEND_RESOLVERS;
|
|
8155
8233
|
var init_builtin_categories = __esm(() => {
|
|
8156
8234
|
init_anthropic_categories();
|
|
8157
8235
|
init_google_categories();
|
|
@@ -8166,6 +8244,7 @@ var init_builtin_categories = __esm(() => {
|
|
|
8166
8244
|
DEFAULT_CATEGORIES = buildCategoryRecord((definition) => definition.config);
|
|
8167
8245
|
CATEGORY_PROMPT_APPENDS = buildCategoryRecord((definition) => definition.promptAppend);
|
|
8168
8246
|
CATEGORY_DESCRIPTIONS = buildCategoryRecord((definition) => definition.description);
|
|
8247
|
+
CATEGORY_PROMPT_APPEND_RESOLVERS = Object.fromEntries(BUILTIN_CATEGORIES.filter((definition) => definition.resolvePromptAppend !== undefined).map((definition) => [definition.name, definition.resolvePromptAppend]));
|
|
8169
8248
|
});
|
|
8170
8249
|
|
|
8171
8250
|
// src/tools/delegate-task/constants.ts
|
|
@@ -15921,6 +16000,41 @@ function normalizeSDKResponse(response, fallback, options) {
|
|
|
15921
16000
|
// src/shared/dynamic-truncator.ts
|
|
15922
16001
|
var CHARS_PER_TOKEN_ESTIMATE = 4;
|
|
15923
16002
|
var DEFAULT_TARGET_MAX_TOKENS = 50000;
|
|
16003
|
+
var usageCacheByClient = new WeakMap;
|
|
16004
|
+
function createModelCacheKey(modelCacheState) {
|
|
16005
|
+
if (!modelCacheState) {
|
|
16006
|
+
return "default";
|
|
16007
|
+
}
|
|
16008
|
+
const cachedLimits = modelCacheState.modelContextLimitsCache ? [...modelCacheState.modelContextLimitsCache.entries()].sort(([leftKey], [rightKey]) => leftKey.localeCompare(rightKey)).map(([modelKey, limit]) => `${modelKey}:${limit}`).join(",") : "";
|
|
16009
|
+
return `${modelCacheState.anthropicContext1MEnabled ? "1m" : "200k"}|${cachedLimits}`;
|
|
16010
|
+
}
|
|
16011
|
+
function getUsageCache(client, modelCacheState) {
|
|
16012
|
+
let cacheByModelState = usageCacheByClient.get(client);
|
|
16013
|
+
if (!cacheByModelState) {
|
|
16014
|
+
cacheByModelState = new Map;
|
|
16015
|
+
usageCacheByClient.set(client, cacheByModelState);
|
|
16016
|
+
}
|
|
16017
|
+
const modelCacheKey = createModelCacheKey(modelCacheState);
|
|
16018
|
+
let cache = cacheByModelState.get(modelCacheKey);
|
|
16019
|
+
if (!cache) {
|
|
16020
|
+
cache = new Map;
|
|
16021
|
+
cacheByModelState.set(modelCacheKey, cache);
|
|
16022
|
+
}
|
|
16023
|
+
return cache;
|
|
16024
|
+
}
|
|
16025
|
+
function invalidateContextWindowUsageCache(ctx, sessionID) {
|
|
16026
|
+
const cacheByModelState = usageCacheByClient.get(ctx.client);
|
|
16027
|
+
if (!cacheByModelState) {
|
|
16028
|
+
return;
|
|
16029
|
+
}
|
|
16030
|
+
for (const cache of cacheByModelState.values()) {
|
|
16031
|
+
if (sessionID) {
|
|
16032
|
+
cache.delete(sessionID);
|
|
16033
|
+
} else {
|
|
16034
|
+
cache.clear();
|
|
16035
|
+
}
|
|
16036
|
+
}
|
|
16037
|
+
}
|
|
15924
16038
|
function estimateTokens(text) {
|
|
15925
16039
|
return Math.ceil(text.length / CHARS_PER_TOKEN_ESTIMATE);
|
|
15926
16040
|
}
|
|
@@ -15982,6 +16096,16 @@ function truncateToTokenLimit(output, maxTokens, preserveHeaderLines = 3) {
|
|
|
15982
16096
|
};
|
|
15983
16097
|
}
|
|
15984
16098
|
async function getContextWindowUsage(ctx, sessionID, modelCacheState) {
|
|
16099
|
+
const cache = getUsageCache(ctx.client, modelCacheState);
|
|
16100
|
+
const cached = cache.get(sessionID);
|
|
16101
|
+
if (cached) {
|
|
16102
|
+
return cached;
|
|
16103
|
+
}
|
|
16104
|
+
const usagePromise = fetchContextWindowUsage(ctx, sessionID, modelCacheState);
|
|
16105
|
+
cache.set(sessionID, usagePromise);
|
|
16106
|
+
return usagePromise;
|
|
16107
|
+
}
|
|
16108
|
+
async function fetchContextWindowUsage(ctx, sessionID, modelCacheState) {
|
|
15985
16109
|
try {
|
|
15986
16110
|
const response = await ctx.client.session.messages({
|
|
15987
16111
|
path: { id: sessionID }
|
|
@@ -66161,7 +66285,9 @@ var RETRYABLE_MESSAGE_PATTERNS = [
|
|
|
66161
66285
|
"502",
|
|
66162
66286
|
"504",
|
|
66163
66287
|
"429",
|
|
66164
|
-
"529"
|
|
66288
|
+
"529",
|
|
66289
|
+
"403",
|
|
66290
|
+
"forbidden"
|
|
66165
66291
|
];
|
|
66166
66292
|
var STOP_MESSAGE_PATTERNS = [
|
|
66167
66293
|
"quota will reset after",
|
|
@@ -66541,14 +66667,12 @@ async function handleSessionIdle(args) {
|
|
|
66541
66667
|
return;
|
|
66542
66668
|
}
|
|
66543
66669
|
if (!todos || todos.length === 0) {
|
|
66544
|
-
sessionStateStore.resetContinuationProgress(sessionID);
|
|
66545
66670
|
sessionStateStore.resetContinuationProgress(sessionID);
|
|
66546
66671
|
log(`[${HOOK_NAME}] No todos`, { sessionID });
|
|
66547
66672
|
return;
|
|
66548
66673
|
}
|
|
66549
66674
|
const incompleteCount = getIncompleteCount(todos);
|
|
66550
66675
|
if (incompleteCount === 0) {
|
|
66551
|
-
sessionStateStore.resetContinuationProgress(sessionID);
|
|
66552
66676
|
sessionStateStore.resetContinuationProgress(sessionID);
|
|
66553
66677
|
log(`[${HOOK_NAME}] All todos complete`, { sessionID, total: todos.length });
|
|
66554
66678
|
return;
|
|
@@ -71652,17 +71776,21 @@ function createModelFallbackStateController(input) {
|
|
|
71652
71776
|
function setSessionFallbackChain(sessionID, fallbackChain) {
|
|
71653
71777
|
if (!sessionID)
|
|
71654
71778
|
return;
|
|
71655
|
-
sessionFallbackChains.set(sessionID, fallbackChain?.length ? fallbackChain : []);
|
|
71779
|
+
sessionFallbackChains.set(sessionID, fallbackChain?.length ? [...fallbackChain] : []);
|
|
71656
71780
|
}
|
|
71657
71781
|
function clearSessionFallbackChain(sessionID) {
|
|
71658
71782
|
sessionFallbackChains.delete(sessionID);
|
|
71659
71783
|
}
|
|
71784
|
+
function getSessionFallbackChain(sessionID) {
|
|
71785
|
+
const fallbackChain = sessionFallbackChains.get(sessionID);
|
|
71786
|
+
return fallbackChain ? [...fallbackChain] : undefined;
|
|
71787
|
+
}
|
|
71660
71788
|
function setPendingModelFallback(sessionID, agentName, currentProviderID, currentModelID) {
|
|
71661
71789
|
const agentKey = getAgentConfigKey(agentName);
|
|
71662
71790
|
const requirements = AGENT_MODEL_REQUIREMENTS[agentKey];
|
|
71663
71791
|
const fallbackChain = sessionFallbackChains.get(sessionID) ?? requirements?.fallbackChain;
|
|
71664
71792
|
if (!fallbackChain?.length) {
|
|
71665
|
-
log(
|
|
71793
|
+
log(`[model-fallback] No fallback chain for agent: ${agentName} (key: ${agentKey})`);
|
|
71666
71794
|
return false;
|
|
71667
71795
|
}
|
|
71668
71796
|
const existing = pendingModelFallbacks.get(sessionID);
|
|
@@ -71674,21 +71802,21 @@ function createModelFallbackStateController(input) {
|
|
|
71674
71802
|
attemptCount: 0,
|
|
71675
71803
|
pending: true
|
|
71676
71804
|
});
|
|
71677
|
-
log(
|
|
71805
|
+
log(`[model-fallback] Set pending fallback for session: ${sessionID}, agent: ${agentName}`);
|
|
71678
71806
|
return true;
|
|
71679
71807
|
}
|
|
71680
71808
|
if (existing.pending) {
|
|
71681
|
-
log(
|
|
71809
|
+
log(`[model-fallback] Pending fallback already armed for session: ${sessionID}`);
|
|
71682
71810
|
return false;
|
|
71683
71811
|
}
|
|
71684
71812
|
existing.providerID = currentProviderID;
|
|
71685
71813
|
existing.modelID = currentModelID;
|
|
71686
71814
|
existing.pending = true;
|
|
71687
71815
|
if (existing.attemptCount >= existing.fallbackChain.length) {
|
|
71688
|
-
log(
|
|
71816
|
+
log(`[model-fallback] Fallback chain exhausted for session: ${sessionID}`);
|
|
71689
71817
|
return false;
|
|
71690
71818
|
}
|
|
71691
|
-
log(
|
|
71819
|
+
log(`[model-fallback] Re-armed pending fallback for session: ${sessionID}`);
|
|
71692
71820
|
return true;
|
|
71693
71821
|
}
|
|
71694
71822
|
function getNextFallback2(sessionID) {
|
|
@@ -71698,7 +71826,7 @@ function createModelFallbackStateController(input) {
|
|
|
71698
71826
|
const fallback = getNextReachableFallback(sessionID, state3);
|
|
71699
71827
|
if (fallback)
|
|
71700
71828
|
return fallback;
|
|
71701
|
-
log(
|
|
71829
|
+
log(`[model-fallback] No more fallbacks for session: ${sessionID}`);
|
|
71702
71830
|
pendingModelFallbacks.delete(sessionID);
|
|
71703
71831
|
return null;
|
|
71704
71832
|
}
|
|
@@ -71720,6 +71848,7 @@ function createModelFallbackStateController(input) {
|
|
|
71720
71848
|
return {
|
|
71721
71849
|
lastToastKey,
|
|
71722
71850
|
setSessionFallbackChain,
|
|
71851
|
+
getSessionFallbackChain,
|
|
71723
71852
|
clearSessionFallbackChain,
|
|
71724
71853
|
setPendingModelFallback,
|
|
71725
71854
|
getNextFallback: getNextFallback2,
|
|
@@ -71761,6 +71890,7 @@ function createModelFallbackHook(args) {
|
|
|
71761
71890
|
return {
|
|
71762
71891
|
lastToastKey: controller.lastToastKey,
|
|
71763
71892
|
setSessionFallbackChain: controller.setSessionFallbackChain,
|
|
71893
|
+
getSessionFallbackChain: controller.getSessionFallbackChain,
|
|
71764
71894
|
clearSessionFallbackChain: controller.clearSessionFallbackChain,
|
|
71765
71895
|
setPendingModelFallback: controller.setPendingModelFallback,
|
|
71766
71896
|
getNextFallback: controller.getNextFallback,
|
|
@@ -74072,13 +74202,6 @@ function readPackageVersion(packageJsonPath) {
|
|
|
74072
74202
|
return pkg.version ?? null;
|
|
74073
74203
|
}
|
|
74074
74204
|
function getCachedVersion() {
|
|
74075
|
-
for (const candidate of INSTALLED_PACKAGE_JSON_CANDIDATES) {
|
|
74076
|
-
try {
|
|
74077
|
-
if (fs12.existsSync(candidate)) {
|
|
74078
|
-
return readPackageVersion(candidate);
|
|
74079
|
-
}
|
|
74080
|
-
} catch {}
|
|
74081
|
-
}
|
|
74082
74205
|
try {
|
|
74083
74206
|
const currentDir = path10.dirname(fileURLToPath3(import.meta.url));
|
|
74084
74207
|
const pkgPath = findPackageJsonUp(currentDir);
|
|
@@ -74088,6 +74211,13 @@ function getCachedVersion() {
|
|
|
74088
74211
|
} catch (err) {
|
|
74089
74212
|
log("[auto-update-checker] Failed to resolve version from current directory:", err);
|
|
74090
74213
|
}
|
|
74214
|
+
for (const candidate of INSTALLED_PACKAGE_JSON_CANDIDATES) {
|
|
74215
|
+
try {
|
|
74216
|
+
if (fs12.existsSync(candidate)) {
|
|
74217
|
+
return readPackageVersion(candidate);
|
|
74218
|
+
}
|
|
74219
|
+
} catch {}
|
|
74220
|
+
}
|
|
74091
74221
|
try {
|
|
74092
74222
|
const execDir = path10.dirname(fs12.realpathSync(process.execPath));
|
|
74093
74223
|
const pkgPath = findPackageJsonUp(execDir);
|
|
@@ -75101,54 +75231,8 @@ function createAgentUsageReminderHook(_ctx) {
|
|
|
75101
75231
|
event: eventHandler
|
|
75102
75232
|
};
|
|
75103
75233
|
}
|
|
75104
|
-
// src/agents/types.ts
|
|
75105
|
-
function extractModelName(model) {
|
|
75106
|
-
return model.includes("/") ? model.split("/").pop() ?? model : model;
|
|
75107
|
-
}
|
|
75108
|
-
function isGptModel(model) {
|
|
75109
|
-
const modelName = extractModelName(model).toLowerCase();
|
|
75110
|
-
return modelName.includes("gpt");
|
|
75111
|
-
}
|
|
75112
|
-
var GPT_NATIVE_SISYPHUS_RE = /gpt-5[.-](?:[4-9]|\d{2,})/i;
|
|
75113
|
-
function isGptNativeSisyphusModel(model) {
|
|
75114
|
-
const modelName = extractModelName(model).toLowerCase();
|
|
75115
|
-
return GPT_NATIVE_SISYPHUS_RE.test(modelName);
|
|
75116
|
-
}
|
|
75117
|
-
function isGpt5_5Model(model) {
|
|
75118
|
-
const modelName = extractModelName(model).toLowerCase();
|
|
75119
|
-
return modelName.includes("gpt-5.5") || modelName.includes("gpt-5-5");
|
|
75120
|
-
}
|
|
75121
|
-
function isGpt5_3CodexModel(model) {
|
|
75122
|
-
const modelName = extractModelName(model).toLowerCase();
|
|
75123
|
-
return modelName.includes("gpt-5.3-codex") || modelName.includes("gpt-5-3-codex");
|
|
75124
|
-
}
|
|
75125
|
-
function isClaudeOpus47Model(model) {
|
|
75126
|
-
const modelName = extractModelName(model).toLowerCase().replaceAll(".", "-");
|
|
75127
|
-
return modelName.includes("claude-opus-4-7");
|
|
75128
|
-
}
|
|
75129
|
-
function isKimiK2Model(model) {
|
|
75130
|
-
const modelName = extractModelName(model).toLowerCase();
|
|
75131
|
-
if (modelName.includes("kimi"))
|
|
75132
|
-
return true;
|
|
75133
|
-
if (/k2[-.]?p[56]/.test(modelName))
|
|
75134
|
-
return true;
|
|
75135
|
-
return false;
|
|
75136
|
-
}
|
|
75137
|
-
var GEMINI_PROVIDERS = ["google/", "google-vertex/"];
|
|
75138
|
-
function isGlmModel(model) {
|
|
75139
|
-
const modelName = extractModelName(model).toLowerCase();
|
|
75140
|
-
return modelName.includes("glm");
|
|
75141
|
-
}
|
|
75142
|
-
function isGeminiModel(model) {
|
|
75143
|
-
if (GEMINI_PROVIDERS.some((prefix) => model.startsWith(prefix)))
|
|
75144
|
-
return true;
|
|
75145
|
-
if (model.startsWith("github-copilot/") && extractModelName(model).toLowerCase().startsWith("gemini"))
|
|
75146
|
-
return true;
|
|
75147
|
-
const modelName = extractModelName(model).toLowerCase();
|
|
75148
|
-
return modelName.startsWith("gemini-");
|
|
75149
|
-
}
|
|
75150
|
-
|
|
75151
75234
|
// src/hooks/keyword-detector/ultrawork/source-detector.ts
|
|
75235
|
+
init_types();
|
|
75152
75236
|
function isPlannerAgent(agentName) {
|
|
75153
75237
|
if (!agentName)
|
|
75154
75238
|
return false;
|
|
@@ -78005,6 +78089,7 @@ function createRalphLoopHook(ctx, options) {
|
|
|
78005
78089
|
};
|
|
78006
78090
|
}
|
|
78007
78091
|
// src/hooks/no-sisyphus-gpt/hook.ts
|
|
78092
|
+
init_types();
|
|
78008
78093
|
init_agent_display_names();
|
|
78009
78094
|
var TOAST_TITLE = "NEVER Use Sisyphus with GPT";
|
|
78010
78095
|
var TOAST_MESSAGE = [
|
|
@@ -78060,6 +78145,7 @@ function createNoSisyphusGptHook(ctx) {
|
|
|
78060
78145
|
};
|
|
78061
78146
|
}
|
|
78062
78147
|
// src/hooks/no-hephaestus-non-gpt/hook.ts
|
|
78148
|
+
init_types();
|
|
78063
78149
|
init_agent_display_names();
|
|
78064
78150
|
var TOAST_TITLE2 = "NEVER Use Hephaestus with Non-GPT";
|
|
78065
78151
|
var TOAST_MESSAGE2 = [
|
|
@@ -87912,7 +87998,10 @@ function findMessageByID(messages, messageID) {
|
|
|
87912
87998
|
return messages.find((message) => message.info?.id === messageID);
|
|
87913
87999
|
}
|
|
87914
88000
|
async function resolveNoTextTailFromSession(args) {
|
|
87915
|
-
const { client, sessionID, messageID, directory } = args;
|
|
88001
|
+
const { client, sessionID, messageID, directory, parts } = args;
|
|
88002
|
+
if (Array.isArray(parts)) {
|
|
88003
|
+
return isStepOnlyNoTextParts(parts);
|
|
88004
|
+
}
|
|
87916
88005
|
try {
|
|
87917
88006
|
const response = await client.session.messages({
|
|
87918
88007
|
path: { id: sessionID },
|
|
@@ -88039,7 +88128,8 @@ function createPostCompactionDegradationMonitor(args) {
|
|
|
88039
88128
|
client,
|
|
88040
88129
|
sessionID: info.sessionID,
|
|
88041
88130
|
messageID: info.id,
|
|
88042
|
-
directory
|
|
88131
|
+
directory,
|
|
88132
|
+
parts: info.parts
|
|
88043
88133
|
});
|
|
88044
88134
|
if (!isNoTextTail) {
|
|
88045
88135
|
postCompactionNoTextStreak.set(info.sessionID, 0);
|
|
@@ -88203,7 +88293,8 @@ function createPreemptiveCompactionHook(ctx, pluginConfig, modelCacheState) {
|
|
|
88203
88293
|
compactedSessions.delete(info.sessionID);
|
|
88204
88294
|
await postCompactionMonitor.onAssistantMessageUpdated({
|
|
88205
88295
|
sessionID: info.sessionID,
|
|
88206
|
-
id: info.id
|
|
88296
|
+
id: info.id,
|
|
88297
|
+
parts: info.parts
|
|
88207
88298
|
});
|
|
88208
88299
|
}
|
|
88209
88300
|
};
|
|
@@ -88800,7 +88891,7 @@ function classifyErrorType(error) {
|
|
|
88800
88891
|
if (errorName?.includes("providermodelnotfounderror") || errorName?.includes("modelnotfounderror") || errorName?.includes("unknownerror") && /model\s+not\s+found/i.test(message)) {
|
|
88801
88892
|
return "model_not_found";
|
|
88802
88893
|
}
|
|
88803
|
-
if (errorName?.includes("quotaexceeded") || errorName?.includes("insufficientquota") || errorName?.includes("billingerror") || /quota.?exceeded/i.test(message) || /subscription.*quota/i.test(message) || /insufficient.?quota/i.test(message) || /billing.?(?:hard.?)?limit/i.test(message) || /exhausted\s+your\s+capacity/i.test(message) || /out\s+of\s+credits?/i.test(message) || /payment.?required/i.test(message) || /usage\s+limit/i.test(message)) {
|
|
88894
|
+
if (errorName?.includes("quotaexceeded") || errorName?.includes("insufficientquota") || errorName?.includes("billingerror") || /quota.?exceeded/i.test(message) || /subscription.*quota/i.test(message) || /insufficient.?(?:quota|balance|funds?)/i.test(message) || /billing.?(?:hard.?)?limit/i.test(message) || /exhausted\s+your\s+capacity/i.test(message) || /out\s+of\s+credits?/i.test(message) || /payment.?required/i.test(message) || /usage\s+limit/i.test(message)) {
|
|
88804
88895
|
return "quota_exceeded";
|
|
88805
88896
|
}
|
|
88806
88897
|
return;
|
|
@@ -88828,8 +88919,7 @@ function isRetryableError(error, retryOnErrors) {
|
|
|
88828
88919
|
return true;
|
|
88829
88920
|
}
|
|
88830
88921
|
if (errorType === "quota_exceeded") {
|
|
88831
|
-
|
|
88832
|
-
return hasAutoRetrySignal;
|
|
88922
|
+
return true;
|
|
88833
88923
|
}
|
|
88834
88924
|
if (statusCode && retryOnErrors.includes(statusCode)) {
|
|
88835
88925
|
return true;
|
|
@@ -89918,6 +90008,19 @@ function extractFilePath(metadata) {
|
|
|
89918
90008
|
}
|
|
89919
90009
|
return;
|
|
89920
90010
|
}
|
|
90011
|
+
function extractLineCount(metadata) {
|
|
90012
|
+
if (!metadata || typeof metadata !== "object") {
|
|
90013
|
+
return;
|
|
90014
|
+
}
|
|
90015
|
+
const objectMeta = metadata;
|
|
90016
|
+
const candidates = [objectMeta.lineCount, objectMeta.linesWritten, objectMeta.lines];
|
|
90017
|
+
for (const candidate of candidates) {
|
|
90018
|
+
if (typeof candidate === "number" && Number.isInteger(candidate) && candidate >= 0) {
|
|
90019
|
+
return candidate;
|
|
90020
|
+
}
|
|
90021
|
+
}
|
|
90022
|
+
return;
|
|
90023
|
+
}
|
|
89921
90024
|
async function appendWriteHashlineOutput(output) {
|
|
89922
90025
|
if (output.output.startsWith(WRITE_SUCCESS_MARKER)) {
|
|
89923
90026
|
return;
|
|
@@ -89926,6 +90029,11 @@ async function appendWriteHashlineOutput(output) {
|
|
|
89926
90029
|
if (outputLower.startsWith("error") || outputLower.includes("failed")) {
|
|
89927
90030
|
return;
|
|
89928
90031
|
}
|
|
90032
|
+
const metadataLineCount = extractLineCount(output.metadata);
|
|
90033
|
+
if (metadataLineCount !== undefined) {
|
|
90034
|
+
output.output = `${WRITE_SUCCESS_MARKER} ${metadataLineCount} lines written.`;
|
|
90035
|
+
return;
|
|
90036
|
+
}
|
|
89929
90037
|
const filePath = extractFilePath(output.metadata);
|
|
89930
90038
|
if (!filePath) {
|
|
89931
90039
|
return;
|
|
@@ -96281,6 +96389,83 @@ async function formatFullSession(task, client2, options) {
|
|
|
96281
96389
|
`);
|
|
96282
96390
|
}
|
|
96283
96391
|
|
|
96392
|
+
// src/features/background-agent/error-classifier.ts
|
|
96393
|
+
function isRecord15(value) {
|
|
96394
|
+
return typeof value === "object" && value !== null;
|
|
96395
|
+
}
|
|
96396
|
+
function isAbortedSessionError(error) {
|
|
96397
|
+
const message = getErrorText(error);
|
|
96398
|
+
return message.toLowerCase().includes("aborted");
|
|
96399
|
+
}
|
|
96400
|
+
function getErrorText(error) {
|
|
96401
|
+
if (!error)
|
|
96402
|
+
return "";
|
|
96403
|
+
if (typeof error === "string")
|
|
96404
|
+
return error;
|
|
96405
|
+
if (error instanceof Error) {
|
|
96406
|
+
return `${error.name}: ${error.message}`;
|
|
96407
|
+
}
|
|
96408
|
+
if (typeof error === "object" && error !== null) {
|
|
96409
|
+
if ("message" in error && typeof error.message === "string") {
|
|
96410
|
+
return error.message;
|
|
96411
|
+
}
|
|
96412
|
+
if ("name" in error && typeof error.name === "string") {
|
|
96413
|
+
return error.name;
|
|
96414
|
+
}
|
|
96415
|
+
}
|
|
96416
|
+
return "";
|
|
96417
|
+
}
|
|
96418
|
+
function extractErrorName2(error) {
|
|
96419
|
+
if (isRecord15(error) && typeof error["name"] === "string")
|
|
96420
|
+
return error["name"];
|
|
96421
|
+
if (error instanceof Error)
|
|
96422
|
+
return error.name;
|
|
96423
|
+
return;
|
|
96424
|
+
}
|
|
96425
|
+
function extractErrorMessage(error) {
|
|
96426
|
+
if (!error)
|
|
96427
|
+
return;
|
|
96428
|
+
if (typeof error === "string")
|
|
96429
|
+
return error;
|
|
96430
|
+
if (isRecord15(error)) {
|
|
96431
|
+
const dataRaw = error["data"];
|
|
96432
|
+
const candidates = [
|
|
96433
|
+
dataRaw,
|
|
96434
|
+
isRecord15(dataRaw) ? dataRaw["error"] : undefined,
|
|
96435
|
+
error["error"],
|
|
96436
|
+
error["cause"],
|
|
96437
|
+
error
|
|
96438
|
+
];
|
|
96439
|
+
for (const candidate of candidates) {
|
|
96440
|
+
if (typeof candidate === "string" && candidate.length > 0)
|
|
96441
|
+
return candidate;
|
|
96442
|
+
if (isRecord15(candidate) && typeof candidate["message"] === "string" && candidate["message"].length > 0) {
|
|
96443
|
+
return candidate["message"];
|
|
96444
|
+
}
|
|
96445
|
+
}
|
|
96446
|
+
}
|
|
96447
|
+
if (error instanceof Error)
|
|
96448
|
+
return error.message;
|
|
96449
|
+
try {
|
|
96450
|
+
return JSON.stringify(error);
|
|
96451
|
+
} catch {
|
|
96452
|
+
return String(error);
|
|
96453
|
+
}
|
|
96454
|
+
}
|
|
96455
|
+
function getSessionErrorMessage(properties) {
|
|
96456
|
+
const errorRaw = properties["error"];
|
|
96457
|
+
if (!isRecord15(errorRaw))
|
|
96458
|
+
return;
|
|
96459
|
+
const dataRaw = errorRaw["data"];
|
|
96460
|
+
if (isRecord15(dataRaw)) {
|
|
96461
|
+
const message2 = dataRaw["message"];
|
|
96462
|
+
if (typeof message2 === "string")
|
|
96463
|
+
return message2;
|
|
96464
|
+
}
|
|
96465
|
+
const message = errorRaw["message"];
|
|
96466
|
+
return typeof message === "string" ? message : undefined;
|
|
96467
|
+
}
|
|
96468
|
+
|
|
96284
96469
|
// src/tools/background-task/task-result-format.ts
|
|
96285
96470
|
function getTimeString(value) {
|
|
96286
96471
|
return typeof value === "string" ? value : "";
|
|
@@ -96327,6 +96512,19 @@ Session ID: ${task.sessionID}
|
|
|
96327
96512
|
const timeB = getTimeString(b.info?.time);
|
|
96328
96513
|
return timeA.localeCompare(timeB);
|
|
96329
96514
|
});
|
|
96515
|
+
const sessionError = sortedMessages.filter((message) => message.info?.role === "assistant" && message.info?.error).map((message) => extractErrorMessage(message.info?.error)).find((message) => typeof message === "string" && message.length > 0);
|
|
96516
|
+
if (sessionError) {
|
|
96517
|
+
return `Task Result
|
|
96518
|
+
|
|
96519
|
+
Task ID: ${task.id}
|
|
96520
|
+
Description: ${task.description}
|
|
96521
|
+
Duration: ${formatDuration(task.startedAt ?? new Date, task.completedAt)}
|
|
96522
|
+
Session ID: ${task.sessionID}
|
|
96523
|
+
|
|
96524
|
+
---
|
|
96525
|
+
|
|
96526
|
+
Session error: ${sessionError}`;
|
|
96527
|
+
}
|
|
96330
96528
|
const newMessages = consumeNewMessages(task.sessionID, sortedMessages);
|
|
96331
96529
|
if (newMessages.length === 0) {
|
|
96332
96530
|
const duration2 = formatDuration(task.startedAt ?? new Date, task.completedAt);
|
|
@@ -98254,6 +98452,18 @@ async function fetchSessionMessages(client2, sessionID) {
|
|
|
98254
98452
|
const rawData = messagesResult?.data ?? messagesResult;
|
|
98255
98453
|
return Array.isArray(rawData) ? rawData : [];
|
|
98256
98454
|
}
|
|
98455
|
+
function getTerminalSessionError(messages) {
|
|
98456
|
+
const lastAssistant = [...messages].reverse().find((msg) => msg.info?.role === "assistant");
|
|
98457
|
+
const lastUser = [...messages].reverse().find((msg) => msg.info?.role === "user");
|
|
98458
|
+
if (lastUser?.info?.id && lastAssistant?.info?.id && lastAssistant.info.id <= lastUser.info.id) {
|
|
98459
|
+
return null;
|
|
98460
|
+
}
|
|
98461
|
+
if (!lastAssistant?.info || !("error" in lastAssistant.info)) {
|
|
98462
|
+
return null;
|
|
98463
|
+
}
|
|
98464
|
+
const errorMessage = extractErrorMessage(lastAssistant.info.error);
|
|
98465
|
+
return errorMessage && errorMessage.length > 0 ? errorMessage : "Session error";
|
|
98466
|
+
}
|
|
98257
98467
|
function isSessionComplete(messages) {
|
|
98258
98468
|
let lastUser;
|
|
98259
98469
|
let lastAssistant;
|
|
@@ -98342,6 +98552,11 @@ Session ID: ${input.sessionID}`;
|
|
|
98342
98552
|
if (input.anchorMessageCount !== undefined && messages.length <= input.anchorMessageCount) {
|
|
98343
98553
|
continue;
|
|
98344
98554
|
}
|
|
98555
|
+
const sessionError = getTerminalSessionError(messages);
|
|
98556
|
+
if (sessionError) {
|
|
98557
|
+
log("[task] Poll detected terminal session error", { sessionID: input.sessionID, sessionError });
|
|
98558
|
+
return sessionError;
|
|
98559
|
+
}
|
|
98345
98560
|
if (isSessionComplete(messages)) {
|
|
98346
98561
|
log("[task] Poll complete - terminal finish detected", { sessionID: input.sessionID, pollCount });
|
|
98347
98562
|
break;
|
|
@@ -99110,7 +99325,8 @@ async function retrySyncPromptWithFallbacks(input) {
|
|
|
99110
99325
|
if (!categoryModel || !fallbackChain || fallbackChain.length === 0) {
|
|
99111
99326
|
return {
|
|
99112
99327
|
promptError: initialError,
|
|
99113
|
-
categoryModel
|
|
99328
|
+
categoryModel,
|
|
99329
|
+
fallbackState: undefined
|
|
99114
99330
|
};
|
|
99115
99331
|
}
|
|
99116
99332
|
const fallbackState = {
|
|
@@ -99126,7 +99342,8 @@ async function retrySyncPromptWithFallbacks(input) {
|
|
|
99126
99342
|
if (!nextFallback) {
|
|
99127
99343
|
return {
|
|
99128
99344
|
promptError: finalError,
|
|
99129
|
-
categoryModel
|
|
99345
|
+
categoryModel,
|
|
99346
|
+
fallbackState
|
|
99130
99347
|
};
|
|
99131
99348
|
}
|
|
99132
99349
|
const fallbackModel = toDelegatedModelConfig(nextFallback);
|
|
@@ -99134,7 +99351,8 @@ async function retrySyncPromptWithFallbacks(input) {
|
|
|
99134
99351
|
if (!promptError) {
|
|
99135
99352
|
return {
|
|
99136
99353
|
promptError: null,
|
|
99137
|
-
categoryModel: fallbackModel
|
|
99354
|
+
categoryModel: fallbackModel,
|
|
99355
|
+
fallbackState
|
|
99138
99356
|
};
|
|
99139
99357
|
}
|
|
99140
99358
|
finalError = promptError;
|
|
@@ -99143,6 +99361,12 @@ async function retrySyncPromptWithFallbacks(input) {
|
|
|
99143
99361
|
fallbackState.pending = true;
|
|
99144
99362
|
}
|
|
99145
99363
|
}
|
|
99364
|
+
function getNextSyncFallbackModel(sessionID, fallbackState) {
|
|
99365
|
+
if (!fallbackState)
|
|
99366
|
+
return null;
|
|
99367
|
+
const nextFallback = getNextReachableFallback(sessionID, fallbackState);
|
|
99368
|
+
return nextFallback ? toDelegatedModelConfig(nextFallback) : null;
|
|
99369
|
+
}
|
|
99146
99370
|
|
|
99147
99371
|
// src/tools/delegate-task/sync-task.ts
|
|
99148
99372
|
async function executeSyncTask(args, ctx, executorCtx, parentContext, agentToUse, categoryModel, systemContent, modelInfo, fallbackChain, deps = syncTaskDeps) {
|
|
@@ -99181,26 +99405,50 @@ async function executeSyncTask(args, ctx, executorCtx, parentContext, agentToUse
|
|
|
99181
99405
|
const sessionID = createSessionResult.sessionID;
|
|
99182
99406
|
spawnReservation?.commit();
|
|
99183
99407
|
syncSessionID = sessionID;
|
|
99184
|
-
|
|
99185
|
-
|
|
99186
|
-
|
|
99187
|
-
|
|
99188
|
-
|
|
99189
|
-
|
|
99190
|
-
|
|
99191
|
-
|
|
99192
|
-
|
|
99193
|
-
|
|
99194
|
-
|
|
99195
|
-
|
|
99196
|
-
|
|
99197
|
-
|
|
99198
|
-
|
|
99199
|
-
|
|
99200
|
-
|
|
99408
|
+
const registerSyncSession = async (newSessionID) => {
|
|
99409
|
+
syncSessionID = newSessionID;
|
|
99410
|
+
subagentSessions.add(newSessionID);
|
|
99411
|
+
syncSubagentSessions.add(newSessionID);
|
|
99412
|
+
setSessionAgent(newSessionID, agentToUse);
|
|
99413
|
+
executorCtx.modelFallbackControllerAccessor?.setSessionFallbackChain(newSessionID, fallbackChain);
|
|
99414
|
+
if (args.category) {
|
|
99415
|
+
SessionCategoryRegistry.register(newSessionID, args.category);
|
|
99416
|
+
}
|
|
99417
|
+
if (onSyncSessionCreated) {
|
|
99418
|
+
log("[task] Invoking onSyncSessionCreated callback", { sessionID: newSessionID, parentID: parentContext.sessionID });
|
|
99419
|
+
try {
|
|
99420
|
+
await onSyncSessionCreated({
|
|
99421
|
+
sessionID: newSessionID,
|
|
99422
|
+
parentID: parentContext.sessionID,
|
|
99423
|
+
title: args.description
|
|
99424
|
+
});
|
|
99425
|
+
} catch (error) {
|
|
99426
|
+
log("[task] onSyncSessionCreated callback failed", { error: String(error) });
|
|
99427
|
+
}
|
|
99428
|
+
await new Promise((r) => setTimeout(r, 200));
|
|
99201
99429
|
}
|
|
99202
|
-
|
|
99203
|
-
|
|
99430
|
+
};
|
|
99431
|
+
const publishSyncMetadata = async (currentSessionID, currentModel, currentTaskId, spawnDepth) => {
|
|
99432
|
+
await publishToolMetadata(ctx, {
|
|
99433
|
+
title: args.description,
|
|
99434
|
+
metadata: {
|
|
99435
|
+
prompt: args.prompt,
|
|
99436
|
+
agent: agentToUse,
|
|
99437
|
+
category: args.category,
|
|
99438
|
+
...args.requested_subagent_type !== undefined ? { requested_subagent_type: args.requested_subagent_type } : {},
|
|
99439
|
+
load_skills: args.load_skills,
|
|
99440
|
+
description: args.description,
|
|
99441
|
+
run_in_background: args.run_in_background,
|
|
99442
|
+
taskId: currentSessionID,
|
|
99443
|
+
sessionId: currentSessionID,
|
|
99444
|
+
sync: true,
|
|
99445
|
+
spawnDepth,
|
|
99446
|
+
command: args.command,
|
|
99447
|
+
model: resolveMetadataModel(currentModel, parentContext.model)
|
|
99448
|
+
}
|
|
99449
|
+
});
|
|
99450
|
+
};
|
|
99451
|
+
await registerSyncSession(sessionID);
|
|
99204
99452
|
taskId = `sync_${sessionID.slice(0, 8)}`;
|
|
99205
99453
|
const startTime = new Date;
|
|
99206
99454
|
if (toastManager) {
|
|
@@ -99215,25 +99463,7 @@ async function executeSyncTask(args, ctx, executorCtx, parentContext, agentToUse
|
|
|
99215
99463
|
modelInfo
|
|
99216
99464
|
});
|
|
99217
99465
|
}
|
|
99218
|
-
|
|
99219
|
-
title: args.description,
|
|
99220
|
-
metadata: {
|
|
99221
|
-
prompt: args.prompt,
|
|
99222
|
-
agent: agentToUse,
|
|
99223
|
-
category: args.category,
|
|
99224
|
-
...args.requested_subagent_type !== undefined ? { requested_subagent_type: args.requested_subagent_type } : {},
|
|
99225
|
-
load_skills: args.load_skills,
|
|
99226
|
-
description: args.description,
|
|
99227
|
-
run_in_background: args.run_in_background,
|
|
99228
|
-
taskId: sessionID,
|
|
99229
|
-
sessionId: sessionID,
|
|
99230
|
-
sync: true,
|
|
99231
|
-
spawnDepth: spawnContext.childDepth,
|
|
99232
|
-
command: args.command,
|
|
99233
|
-
model: resolveMetadataModel(categoryModel, parentContext.model)
|
|
99234
|
-
}
|
|
99235
|
-
};
|
|
99236
|
-
await publishToolMetadata(ctx, syncTaskMeta);
|
|
99466
|
+
await publishSyncMetadata(sessionID, categoryModel, taskId, spawnContext.childDepth);
|
|
99237
99467
|
const syncPromptInput = {
|
|
99238
99468
|
sessionID,
|
|
99239
99469
|
agentToUse,
|
|
@@ -99244,55 +99474,106 @@ async function executeSyncTask(args, ctx, executorCtx, parentContext, agentToUse
|
|
|
99244
99474
|
sisyphusAgentConfig: executorCtx.sisyphusAgentConfig
|
|
99245
99475
|
};
|
|
99246
99476
|
let effectiveCategoryModel = categoryModel;
|
|
99247
|
-
let
|
|
99248
|
-
|
|
99249
|
-
|
|
99250
|
-
|
|
99251
|
-
|
|
99252
|
-
|
|
99253
|
-
|
|
99254
|
-
|
|
99255
|
-
|
|
99256
|
-
|
|
99257
|
-
|
|
99258
|
-
|
|
99259
|
-
|
|
99260
|
-
|
|
99477
|
+
let fallbackState = effectiveCategoryModel && fallbackChain?.length ? {
|
|
99478
|
+
providerID: effectiveCategoryModel.providerID,
|
|
99479
|
+
modelID: effectiveCategoryModel.modelID,
|
|
99480
|
+
fallbackChain,
|
|
99481
|
+
attemptCount: 0,
|
|
99482
|
+
pending: true
|
|
99483
|
+
} : undefined;
|
|
99484
|
+
let activeSessionID = sessionID;
|
|
99485
|
+
const cleanupRetrySession = (currentSessionID) => {
|
|
99486
|
+
subagentSessions.delete(currentSessionID);
|
|
99487
|
+
syncSubagentSessions.delete(currentSessionID);
|
|
99488
|
+
executorCtx.modelFallbackControllerAccessor?.clearSessionFallbackChain(currentSessionID);
|
|
99489
|
+
SessionCategoryRegistry.remove(currentSessionID);
|
|
99490
|
+
};
|
|
99491
|
+
try {
|
|
99492
|
+
while (true) {
|
|
99493
|
+
let promptError = await deps.sendSyncPrompt(client2, {
|
|
99494
|
+
...syncPromptInput,
|
|
99495
|
+
sessionID: activeSessionID,
|
|
99496
|
+
categoryModel: effectiveCategoryModel
|
|
99497
|
+
});
|
|
99498
|
+
if (promptError) {
|
|
99499
|
+
const promptResult = await retrySyncPromptWithFallbacks({
|
|
99500
|
+
sessionID: activeSessionID,
|
|
99501
|
+
initialError: promptError,
|
|
99502
|
+
categoryModel: effectiveCategoryModel,
|
|
99503
|
+
fallbackChain,
|
|
99504
|
+
sendPrompt: async (fallbackModel) => {
|
|
99505
|
+
return deps.sendSyncPrompt(client2, {
|
|
99506
|
+
...syncPromptInput,
|
|
99507
|
+
sessionID: activeSessionID,
|
|
99508
|
+
categoryModel: fallbackModel
|
|
99509
|
+
});
|
|
99510
|
+
}
|
|
99261
99511
|
});
|
|
99512
|
+
promptError = promptResult.promptError;
|
|
99513
|
+
effectiveCategoryModel = promptResult.categoryModel;
|
|
99514
|
+
fallbackState = promptResult.fallbackState ?? fallbackState;
|
|
99515
|
+
if (promptError) {
|
|
99516
|
+
return promptError;
|
|
99517
|
+
}
|
|
99262
99518
|
}
|
|
99263
|
-
|
|
99264
|
-
|
|
99265
|
-
|
|
99266
|
-
|
|
99267
|
-
|
|
99268
|
-
|
|
99269
|
-
|
|
99270
|
-
|
|
99271
|
-
|
|
99272
|
-
|
|
99273
|
-
|
|
99274
|
-
|
|
99275
|
-
|
|
99276
|
-
|
|
99277
|
-
|
|
99278
|
-
|
|
99279
|
-
|
|
99280
|
-
|
|
99281
|
-
|
|
99282
|
-
|
|
99283
|
-
|
|
99284
|
-
|
|
99285
|
-
|
|
99286
|
-
|
|
99287
|
-
|
|
99288
|
-
|
|
99289
|
-
|
|
99519
|
+
const pollError = await deps.pollSyncSession(ctx, client2, {
|
|
99520
|
+
sessionID: activeSessionID,
|
|
99521
|
+
agentToUse,
|
|
99522
|
+
toastManager,
|
|
99523
|
+
taskId
|
|
99524
|
+
}, syncPollTimeoutMs);
|
|
99525
|
+
if (pollError) {
|
|
99526
|
+
const nextFallbackModel = shouldRetryError({ message: pollError }) ? getNextSyncFallbackModel(activeSessionID, fallbackState) : null;
|
|
99527
|
+
if (!nextFallbackModel) {
|
|
99528
|
+
return pollError;
|
|
99529
|
+
}
|
|
99530
|
+
cleanupRetrySession(activeSessionID);
|
|
99531
|
+
const retrySessionResult = await deps.createSyncSession(client2, {
|
|
99532
|
+
parentSessionID: parentContext.sessionID,
|
|
99533
|
+
agentToUse,
|
|
99534
|
+
description: args.description,
|
|
99535
|
+
defaultDirectory: directory
|
|
99536
|
+
});
|
|
99537
|
+
if (!retrySessionResult.ok) {
|
|
99538
|
+
return retrySessionResult.error;
|
|
99539
|
+
}
|
|
99540
|
+
activeSessionID = retrySessionResult.sessionID;
|
|
99541
|
+
effectiveCategoryModel = nextFallbackModel;
|
|
99542
|
+
await registerSyncSession(activeSessionID);
|
|
99543
|
+
if (toastManager && taskId) {
|
|
99544
|
+
toastManager.addTask({
|
|
99545
|
+
id: taskId,
|
|
99546
|
+
sessionID: activeSessionID,
|
|
99547
|
+
description: args.description,
|
|
99548
|
+
agent: agentToUse,
|
|
99549
|
+
isBackground: false,
|
|
99550
|
+
category: args.category,
|
|
99551
|
+
skills: args.load_skills,
|
|
99552
|
+
modelInfo
|
|
99553
|
+
});
|
|
99554
|
+
}
|
|
99555
|
+
if (taskId) {
|
|
99556
|
+
await publishSyncMetadata(activeSessionID, effectiveCategoryModel, taskId, spawnContext.childDepth);
|
|
99557
|
+
}
|
|
99558
|
+
continue;
|
|
99559
|
+
}
|
|
99560
|
+
const result = await deps.fetchSyncResult(client2, activeSessionID);
|
|
99561
|
+
if (!result.ok) {
|
|
99562
|
+
return result.error;
|
|
99563
|
+
}
|
|
99564
|
+
const duration = formatDuration2(startTime);
|
|
99565
|
+
const actualModelStr = effectiveCategoryModel ? `${effectiveCategoryModel.providerID}/${effectiveCategoryModel.modelID}` : undefined;
|
|
99566
|
+
const parentModelStr = parentContext.model ? `${parentContext.model.providerID}/${parentContext.model.modelID}` : undefined;
|
|
99567
|
+
let modelRoutingNote = "";
|
|
99568
|
+
if (actualModelStr && parentModelStr && actualModelStr !== parentModelStr) {
|
|
99569
|
+
modelRoutingNote = `
|
|
99290
99570
|
\u26A0\uFE0F Model routing: parent used ${parentModelStr}, this subagent used ${actualModelStr} (via category: ${args.category ?? "unknown"})`;
|
|
99291
|
-
|
|
99292
|
-
|
|
99571
|
+
} else if (actualModelStr) {
|
|
99572
|
+
modelRoutingNote = `
|
|
99293
99573
|
Model: ${actualModelStr}${args.category ? ` (category: ${args.category})` : ""}`;
|
|
99294
|
-
|
|
99295
|
-
|
|
99574
|
+
}
|
|
99575
|
+
await publishSyncMetadata(activeSessionID, effectiveCategoryModel, taskId, spawnContext.childDepth);
|
|
99576
|
+
return `Task completed in ${duration}.
|
|
99296
99577
|
|
|
99297
99578
|
Agent: ${agentToUse}${args.category ? ` (category: ${args.category})` : ""}${modelRoutingNote}
|
|
99298
99579
|
|
|
@@ -99301,11 +99582,12 @@ Agent: ${agentToUse}${args.category ? ` (category: ${args.category})` : ""}${mod
|
|
|
99301
99582
|
${result.textContent || "(No text output)"}
|
|
99302
99583
|
|
|
99303
99584
|
${buildTaskMetadataBlock({
|
|
99304
|
-
|
|
99305
|
-
|
|
99306
|
-
|
|
99307
|
-
|
|
99308
|
-
|
|
99585
|
+
sessionId: activeSessionID,
|
|
99586
|
+
taskId: activeSessionID,
|
|
99587
|
+
agent: agentToUse,
|
|
99588
|
+
category: args.category
|
|
99589
|
+
})}`;
|
|
99590
|
+
}
|
|
99309
99591
|
} finally {
|
|
99310
99592
|
if (toastManager && taskId !== undefined) {
|
|
99311
99593
|
toastManager.removeTask(taskId);
|
|
@@ -99384,6 +99666,7 @@ function resolveCategoryConfig(categoryName, options) {
|
|
|
99384
99666
|
}
|
|
99385
99667
|
|
|
99386
99668
|
// src/tools/delegate-task/category-resolver.ts
|
|
99669
|
+
init_constants2();
|
|
99387
99670
|
init_plugin_identity();
|
|
99388
99671
|
|
|
99389
99672
|
// src/tools/delegate-task/available-models.ts
|
|
@@ -99608,6 +99891,19 @@ function applyCategoryParams(base, config2) {
|
|
|
99608
99891
|
result.thinking = config2.thinking;
|
|
99609
99892
|
return result;
|
|
99610
99893
|
}
|
|
99894
|
+
function resolveCategoryPromptAppendForModel(categoryName, actualModel, staticPromptAppend, userPromptAppend) {
|
|
99895
|
+
const dynamicResolver = CATEGORY_PROMPT_APPEND_RESOLVERS[categoryName];
|
|
99896
|
+
if (!dynamicResolver) {
|
|
99897
|
+
return staticPromptAppend || undefined;
|
|
99898
|
+
}
|
|
99899
|
+
const dynamicBase = dynamicResolver(actualModel);
|
|
99900
|
+
if (!userPromptAppend) {
|
|
99901
|
+
return dynamicBase || undefined;
|
|
99902
|
+
}
|
|
99903
|
+
return dynamicBase ? `${dynamicBase}
|
|
99904
|
+
|
|
99905
|
+
${userPromptAppend}` : userPromptAppend;
|
|
99906
|
+
}
|
|
99611
99907
|
async function resolveCategoryExecution(args, executorCtx, inheritedModel, systemDefaultModel) {
|
|
99612
99908
|
const { client: client2, userCategories, sisyphusJuniorModel } = executorCtx;
|
|
99613
99909
|
const categoryName = args.category;
|
|
@@ -99737,7 +100033,7 @@ Available categories: ${allCategoryNames}`
|
|
|
99737
100033
|
const parsedModel = parseModelString(actualModel);
|
|
99738
100034
|
categoryModel = parsedModel ?? undefined;
|
|
99739
100035
|
}
|
|
99740
|
-
const categoryPromptAppend = resolved.promptAppend
|
|
100036
|
+
const categoryPromptAppend = resolveCategoryPromptAppendForModel(args.category, actualModel, resolved.promptAppend, userCategories?.[args.category]?.prompt_append);
|
|
99741
100037
|
if (!categoryModel && !actualModel && !isModelResolutionSkipped) {
|
|
99742
100038
|
const categoryNames = Object.keys(enabledCategories);
|
|
99743
100039
|
return {
|
|
@@ -103445,6 +103741,43 @@ function formatDuration3(start, end) {
|
|
|
103445
103741
|
}
|
|
103446
103742
|
|
|
103447
103743
|
// src/features/background-agent/background-task-notification-template.ts
|
|
103744
|
+
function formatAttemptModel(attempt) {
|
|
103745
|
+
if (attempt.providerID && attempt.modelID) {
|
|
103746
|
+
return `${attempt.providerID}/${attempt.modelID}`;
|
|
103747
|
+
}
|
|
103748
|
+
if (attempt.modelID) {
|
|
103749
|
+
return attempt.modelID;
|
|
103750
|
+
}
|
|
103751
|
+
if (attempt.providerID) {
|
|
103752
|
+
return attempt.providerID;
|
|
103753
|
+
}
|
|
103754
|
+
return "unknown-model";
|
|
103755
|
+
}
|
|
103756
|
+
function formatAttemptTimeline(task) {
|
|
103757
|
+
if (!task.attempts || task.attempts.length <= 1) {
|
|
103758
|
+
return "";
|
|
103759
|
+
}
|
|
103760
|
+
const lines = task.attempts.map((attempt) => {
|
|
103761
|
+
const attemptLines = [
|
|
103762
|
+
` - Attempt ${attempt.attemptNumber} \u2014 ${attempt.status.toUpperCase()} \u2014 ${formatAttemptModel(attempt)} \u2014 ${attempt.sessionID ?? "unknown"}`
|
|
103763
|
+
];
|
|
103764
|
+
if (attempt.status !== "completed" && attempt.error) {
|
|
103765
|
+
attemptLines.push(` Error: ${attempt.error}`);
|
|
103766
|
+
}
|
|
103767
|
+
return attemptLines.join(`
|
|
103768
|
+
`);
|
|
103769
|
+
}).join(`
|
|
103770
|
+
`);
|
|
103771
|
+
return `Background task attempts:
|
|
103772
|
+
${lines}`;
|
|
103773
|
+
}
|
|
103774
|
+
function formatTaskSummaryLine(task) {
|
|
103775
|
+
const baseLine = `- \`${task.id}\`: ${task.description || task.id}`;
|
|
103776
|
+
const statusSuffix = task.status === "completed" ? "" : ` [${task.status.toUpperCase()}]${task.error ? ` - ${task.error}` : ""}`;
|
|
103777
|
+
const timeline = formatAttemptTimeline(task);
|
|
103778
|
+
return `${baseLine}${statusSuffix}${timeline ? `
|
|
103779
|
+
${timeline}` : ""}`;
|
|
103780
|
+
}
|
|
103448
103781
|
function buildBackgroundTaskNotificationText(input) {
|
|
103449
103782
|
const { task, duration, statusText, allComplete, remainingCount, completedTasks } = input;
|
|
103450
103783
|
const safeDescription = (t) => t.description || t.id;
|
|
@@ -103453,9 +103786,9 @@ function buildBackgroundTaskNotificationText(input) {
|
|
|
103453
103786
|
if (allComplete) {
|
|
103454
103787
|
const succeededTasks = completedTasks.filter((t) => t.status === "completed");
|
|
103455
103788
|
const failedTasks = completedTasks.filter((t) => t.status !== "completed");
|
|
103456
|
-
const succeededText = succeededTasks.length > 0 ? succeededTasks.map((t) =>
|
|
103789
|
+
const succeededText = succeededTasks.length > 0 ? succeededTasks.map((t) => formatTaskSummaryLine(t)).join(`
|
|
103457
103790
|
`) : "";
|
|
103458
|
-
const failedText = failedTasks.length > 0 ? failedTasks.map((t) =>
|
|
103791
|
+
const failedText = failedTasks.length > 0 ? failedTasks.map((t) => formatTaskSummaryLine(t)).join(`
|
|
103459
103792
|
`) : "";
|
|
103460
103793
|
const hasFailures = failedTasks.length > 0;
|
|
103461
103794
|
const header = hasFailures ? `[ALL BACKGROUND TASKS FINISHED - ${failedTasks.length} FAILED]` : "[ALL BACKGROUND TASKS COMPLETE]";
|
|
@@ -103472,7 +103805,7 @@ ${failedText}
|
|
|
103472
103805
|
`;
|
|
103473
103806
|
}
|
|
103474
103807
|
if (!body) {
|
|
103475
|
-
body =
|
|
103808
|
+
body = `${formatTaskSummaryLine(task)}
|
|
103476
103809
|
`;
|
|
103477
103810
|
}
|
|
103478
103811
|
return `<system-reminder>
|
|
@@ -103499,83 +103832,6 @@ Use \`background_output(task_id="${task.id}")\` to retrieve this result when rea
|
|
|
103499
103832
|
</system-reminder>`;
|
|
103500
103833
|
}
|
|
103501
103834
|
|
|
103502
|
-
// src/features/background-agent/error-classifier.ts
|
|
103503
|
-
function isRecord15(value) {
|
|
103504
|
-
return typeof value === "object" && value !== null;
|
|
103505
|
-
}
|
|
103506
|
-
function isAbortedSessionError(error) {
|
|
103507
|
-
const message = getErrorText(error);
|
|
103508
|
-
return message.toLowerCase().includes("aborted");
|
|
103509
|
-
}
|
|
103510
|
-
function getErrorText(error) {
|
|
103511
|
-
if (!error)
|
|
103512
|
-
return "";
|
|
103513
|
-
if (typeof error === "string")
|
|
103514
|
-
return error;
|
|
103515
|
-
if (error instanceof Error) {
|
|
103516
|
-
return `${error.name}: ${error.message}`;
|
|
103517
|
-
}
|
|
103518
|
-
if (typeof error === "object" && error !== null) {
|
|
103519
|
-
if ("message" in error && typeof error.message === "string") {
|
|
103520
|
-
return error.message;
|
|
103521
|
-
}
|
|
103522
|
-
if ("name" in error && typeof error.name === "string") {
|
|
103523
|
-
return error.name;
|
|
103524
|
-
}
|
|
103525
|
-
}
|
|
103526
|
-
return "";
|
|
103527
|
-
}
|
|
103528
|
-
function extractErrorName2(error) {
|
|
103529
|
-
if (isRecord15(error) && typeof error["name"] === "string")
|
|
103530
|
-
return error["name"];
|
|
103531
|
-
if (error instanceof Error)
|
|
103532
|
-
return error.name;
|
|
103533
|
-
return;
|
|
103534
|
-
}
|
|
103535
|
-
function extractErrorMessage(error) {
|
|
103536
|
-
if (!error)
|
|
103537
|
-
return;
|
|
103538
|
-
if (typeof error === "string")
|
|
103539
|
-
return error;
|
|
103540
|
-
if (error instanceof Error)
|
|
103541
|
-
return error.message;
|
|
103542
|
-
if (isRecord15(error)) {
|
|
103543
|
-
const dataRaw = error["data"];
|
|
103544
|
-
const candidates = [
|
|
103545
|
-
error,
|
|
103546
|
-
dataRaw,
|
|
103547
|
-
error["error"],
|
|
103548
|
-
isRecord15(dataRaw) ? dataRaw["error"] : undefined,
|
|
103549
|
-
error["cause"]
|
|
103550
|
-
];
|
|
103551
|
-
for (const candidate of candidates) {
|
|
103552
|
-
if (typeof candidate === "string" && candidate.length > 0)
|
|
103553
|
-
return candidate;
|
|
103554
|
-
if (isRecord15(candidate) && typeof candidate["message"] === "string" && candidate["message"].length > 0) {
|
|
103555
|
-
return candidate["message"];
|
|
103556
|
-
}
|
|
103557
|
-
}
|
|
103558
|
-
}
|
|
103559
|
-
try {
|
|
103560
|
-
return JSON.stringify(error);
|
|
103561
|
-
} catch {
|
|
103562
|
-
return String(error);
|
|
103563
|
-
}
|
|
103564
|
-
}
|
|
103565
|
-
function getSessionErrorMessage(properties) {
|
|
103566
|
-
const errorRaw = properties["error"];
|
|
103567
|
-
if (!isRecord15(errorRaw))
|
|
103568
|
-
return;
|
|
103569
|
-
const dataRaw = errorRaw["data"];
|
|
103570
|
-
if (isRecord15(dataRaw)) {
|
|
103571
|
-
const message2 = dataRaw["message"];
|
|
103572
|
-
if (typeof message2 === "string")
|
|
103573
|
-
return message2;
|
|
103574
|
-
}
|
|
103575
|
-
const message = errorRaw["message"];
|
|
103576
|
-
return typeof message === "string" ? message : undefined;
|
|
103577
|
-
}
|
|
103578
|
-
|
|
103579
103835
|
// src/features/background-agent/abort-with-timeout.ts
|
|
103580
103836
|
async function abortWithTimeout(client2, sessionID, timeoutMs = 1e4) {
|
|
103581
103837
|
let timeoutHandle;
|
|
@@ -103603,9 +103859,138 @@ async function abortWithTimeout(client2, sessionID, timeoutMs = 1e4) {
|
|
|
103603
103859
|
}
|
|
103604
103860
|
}
|
|
103605
103861
|
|
|
103862
|
+
// src/features/background-agent/attempt-lifecycle.ts
|
|
103863
|
+
function toAttemptModel(model) {
|
|
103864
|
+
return {
|
|
103865
|
+
providerID: model?.providerID,
|
|
103866
|
+
modelID: model?.modelID,
|
|
103867
|
+
variant: model?.variant
|
|
103868
|
+
};
|
|
103869
|
+
}
|
|
103870
|
+
function toTaskModel(attempt) {
|
|
103871
|
+
if (!attempt.providerID || !attempt.modelID) {
|
|
103872
|
+
return;
|
|
103873
|
+
}
|
|
103874
|
+
return {
|
|
103875
|
+
providerID: attempt.providerID,
|
|
103876
|
+
modelID: attempt.modelID,
|
|
103877
|
+
...attempt.variant ? { variant: attempt.variant } : {}
|
|
103878
|
+
};
|
|
103879
|
+
}
|
|
103880
|
+
function getAttemptIndex(task, attemptID) {
|
|
103881
|
+
return task.attempts?.findIndex((attempt) => attempt.attemptID === attemptID) ?? -1;
|
|
103882
|
+
}
|
|
103883
|
+
function getAttempt(task, attemptID) {
|
|
103884
|
+
const index = getAttemptIndex(task, attemptID);
|
|
103885
|
+
return index === -1 ? undefined : task.attempts?.[index];
|
|
103886
|
+
}
|
|
103887
|
+
function isTerminalStatus(status) {
|
|
103888
|
+
return status === "completed" || status === "error" || status === "cancelled" || status === "interrupt";
|
|
103889
|
+
}
|
|
103890
|
+
function getCurrentAttempt(task) {
|
|
103891
|
+
if (!task.currentAttemptID) {
|
|
103892
|
+
return;
|
|
103893
|
+
}
|
|
103894
|
+
return getAttempt(task, task.currentAttemptID);
|
|
103895
|
+
}
|
|
103896
|
+
function ensureCurrentAttempt(task, model = task.model) {
|
|
103897
|
+
const existingAttempt = getCurrentAttempt(task);
|
|
103898
|
+
if (existingAttempt) {
|
|
103899
|
+
return existingAttempt;
|
|
103900
|
+
}
|
|
103901
|
+
const attempt = {
|
|
103902
|
+
attemptID: `att_${crypto.randomUUID().slice(0, 8)}`,
|
|
103903
|
+
attemptNumber: (task.attempts?.length ?? 0) + 1,
|
|
103904
|
+
sessionID: task.sessionID,
|
|
103905
|
+
...toAttemptModel(model),
|
|
103906
|
+
status: task.status,
|
|
103907
|
+
error: task.error,
|
|
103908
|
+
startedAt: task.startedAt,
|
|
103909
|
+
completedAt: task.completedAt
|
|
103910
|
+
};
|
|
103911
|
+
task.attempts = [...task.attempts ?? [], attempt];
|
|
103912
|
+
task.currentAttemptID = attempt.attemptID;
|
|
103913
|
+
return attempt;
|
|
103914
|
+
}
|
|
103915
|
+
function projectTaskFromCurrentAttempt(task) {
|
|
103916
|
+
const currentAttempt = getCurrentAttempt(task);
|
|
103917
|
+
if (!currentAttempt) {
|
|
103918
|
+
return task;
|
|
103919
|
+
}
|
|
103920
|
+
task.status = currentAttempt.status;
|
|
103921
|
+
task.sessionID = currentAttempt.sessionID;
|
|
103922
|
+
task.startedAt = currentAttempt.startedAt;
|
|
103923
|
+
task.completedAt = currentAttempt.completedAt;
|
|
103924
|
+
task.error = currentAttempt.error;
|
|
103925
|
+
task.model = toTaskModel(currentAttempt);
|
|
103926
|
+
return task;
|
|
103927
|
+
}
|
|
103928
|
+
function startAttempt(task, model) {
|
|
103929
|
+
const attempt = {
|
|
103930
|
+
attemptID: `att_${crypto.randomUUID().slice(0, 8)}`,
|
|
103931
|
+
attemptNumber: (task.attempts?.length ?? 0) + 1,
|
|
103932
|
+
...toAttemptModel(model),
|
|
103933
|
+
status: "pending"
|
|
103934
|
+
};
|
|
103935
|
+
task.attempts = [...task.attempts ?? [], attempt];
|
|
103936
|
+
task.currentAttemptID = attempt.attemptID;
|
|
103937
|
+
task.status = "pending";
|
|
103938
|
+
task.sessionID = undefined;
|
|
103939
|
+
task.startedAt = undefined;
|
|
103940
|
+
task.completedAt = undefined;
|
|
103941
|
+
task.error = undefined;
|
|
103942
|
+
task.model = model;
|
|
103943
|
+
return attempt;
|
|
103944
|
+
}
|
|
103945
|
+
function bindAttemptSession(task, attemptID, sessionID, model) {
|
|
103946
|
+
ensureCurrentAttempt(task, model);
|
|
103947
|
+
if (task.currentAttemptID !== attemptID) {
|
|
103948
|
+
return;
|
|
103949
|
+
}
|
|
103950
|
+
const attempt = getAttempt(task, attemptID);
|
|
103951
|
+
if (!attempt || isTerminalStatus(attempt.status)) {
|
|
103952
|
+
return;
|
|
103953
|
+
}
|
|
103954
|
+
attempt.sessionID = sessionID;
|
|
103955
|
+
attempt.status = "running";
|
|
103956
|
+
attempt.startedAt = new Date;
|
|
103957
|
+
attempt.completedAt = undefined;
|
|
103958
|
+
attempt.error = undefined;
|
|
103959
|
+
attempt.providerID = model?.providerID ?? attempt.providerID;
|
|
103960
|
+
attempt.modelID = model?.modelID ?? attempt.modelID;
|
|
103961
|
+
attempt.variant = model?.variant ?? attempt.variant;
|
|
103962
|
+
return getCurrentAttempt(projectTaskFromCurrentAttempt(task));
|
|
103963
|
+
}
|
|
103964
|
+
function finalizeAttempt(task, attemptID, status, error) {
|
|
103965
|
+
const attempt = getAttempt(task, attemptID);
|
|
103966
|
+
if (!attempt) {
|
|
103967
|
+
return;
|
|
103968
|
+
}
|
|
103969
|
+
attempt.status = status;
|
|
103970
|
+
attempt.completedAt = new Date;
|
|
103971
|
+
attempt.error = error;
|
|
103972
|
+
if (task.currentAttemptID === attemptID) {
|
|
103973
|
+
projectTaskFromCurrentAttempt(task);
|
|
103974
|
+
}
|
|
103975
|
+
return attempt;
|
|
103976
|
+
}
|
|
103977
|
+
function scheduleRetryAttempt(task, failedAttemptID, nextModel, error) {
|
|
103978
|
+
const failedAttempt = finalizeAttempt(task, failedAttemptID, "error", error);
|
|
103979
|
+
if (!failedAttempt || task.currentAttemptID !== failedAttemptID) {
|
|
103980
|
+
return;
|
|
103981
|
+
}
|
|
103982
|
+
return startAttempt(task, nextModel);
|
|
103983
|
+
}
|
|
103984
|
+
function findAttemptBySession(task, sessionID) {
|
|
103985
|
+
return task.attempts?.find((attempt) => attempt.sessionID === sessionID);
|
|
103986
|
+
}
|
|
103987
|
+
|
|
103606
103988
|
// src/features/background-agent/fallback-retry-handler.ts
|
|
103989
|
+
function canonicalizeModelID2(modelID) {
|
|
103990
|
+
return modelID.toLowerCase().replace(/\./g, "-");
|
|
103991
|
+
}
|
|
103607
103992
|
async function tryFallbackRetry(args) {
|
|
103608
|
-
const { task, errorInfo, source, concurrencyManager, client: client2, idleDeferralTimers, queuesByKey, processKey } = args;
|
|
103993
|
+
const { task, errorInfo, source, concurrencyManager, client: client2, idleDeferralTimers, queuesByKey, processKey, onRetrying } = args;
|
|
103609
103994
|
const fallbackChain = task.fallbackChain;
|
|
103610
103995
|
const canRetry = shouldRetryError(errorInfo) && fallbackChain && fallbackChain.length > 0 && hasMoreFallbacks(fallbackChain, task.attemptCount ?? 0);
|
|
103611
103996
|
if (!canRetry)
|
|
@@ -103625,6 +104010,7 @@ async function tryFallbackRetry(args) {
|
|
|
103625
104010
|
};
|
|
103626
104011
|
let selectedAttemptCount = attemptCount;
|
|
103627
104012
|
let nextFallback;
|
|
104013
|
+
let nextProviderID;
|
|
103628
104014
|
while (fallbackChain && selectedAttemptCount < fallbackChain.length) {
|
|
103629
104015
|
const candidate = getNextFallback(fallbackChain, selectedAttemptCount);
|
|
103630
104016
|
if (!candidate)
|
|
@@ -103639,12 +104025,25 @@ async function tryFallbackRetry(args) {
|
|
|
103639
104025
|
});
|
|
103640
104026
|
continue;
|
|
103641
104027
|
}
|
|
104028
|
+
const candidateProviderID = selectFallbackProvider(candidate.providers, task.model?.providerID);
|
|
104029
|
+
const candidateModelID = transformModelForProvider(candidateProviderID, candidate.model);
|
|
104030
|
+
const isNoOpFallback = !!task.model && candidateProviderID.toLowerCase() === task.model.providerID.toLowerCase() && canonicalizeModelID2(candidateModelID) === canonicalizeModelID2(task.model.modelID);
|
|
104031
|
+
if (isNoOpFallback) {
|
|
104032
|
+
log("[background-agent] Skipping no-op fallback:", {
|
|
104033
|
+
taskId: task.id,
|
|
104034
|
+
source,
|
|
104035
|
+
model: candidate.model,
|
|
104036
|
+
providers: candidate.providers
|
|
104037
|
+
});
|
|
104038
|
+
continue;
|
|
104039
|
+
}
|
|
103642
104040
|
nextFallback = candidate;
|
|
104041
|
+
nextProviderID = candidateProviderID;
|
|
103643
104042
|
break;
|
|
103644
104043
|
}
|
|
103645
104044
|
if (!nextFallback)
|
|
103646
104045
|
return false;
|
|
103647
|
-
const providerID = selectFallbackProvider(nextFallback.providers, task.model?.providerID);
|
|
104046
|
+
const providerID = nextProviderID ?? selectFallbackProvider(nextFallback.providers, task.model?.providerID);
|
|
103648
104047
|
log("[background-agent] Retryable error, attempting fallback:", {
|
|
103649
104048
|
taskId: task.id,
|
|
103650
104049
|
source,
|
|
@@ -103663,18 +104062,34 @@ async function tryFallbackRetry(args) {
|
|
|
103663
104062
|
idleDeferralTimers.delete(task.id);
|
|
103664
104063
|
}
|
|
103665
104064
|
const previousSessionID = task.sessionID;
|
|
103666
|
-
|
|
104065
|
+
const previousModel = task.model;
|
|
103667
104066
|
const transformedModelId = transformModelForProvider(providerID, nextFallback.model);
|
|
103668
|
-
|
|
104067
|
+
const nextModel = {
|
|
103669
104068
|
providerID,
|
|
103670
104069
|
modelID: transformedModelId,
|
|
103671
104070
|
variant: nextFallback.variant
|
|
103672
104071
|
};
|
|
103673
|
-
task.
|
|
103674
|
-
|
|
103675
|
-
task.
|
|
104072
|
+
task.attemptCount = selectedAttemptCount;
|
|
104073
|
+
const failedAttemptID = ensureCurrentAttempt(task, previousModel).attemptID;
|
|
104074
|
+
const nextAttempt = failedAttemptID ? scheduleRetryAttempt(task, failedAttemptID, nextModel, errorInfo.message) : undefined;
|
|
104075
|
+
if (!nextAttempt) {
|
|
104076
|
+
return false;
|
|
104077
|
+
}
|
|
103676
104078
|
task.queuedAt = new Date;
|
|
103677
|
-
task.
|
|
104079
|
+
task.retryNotification = {
|
|
104080
|
+
previousSessionID,
|
|
104081
|
+
failedModel: previousModel ? `${previousModel.providerID}/${previousModel.modelID}` : undefined,
|
|
104082
|
+
failedError: errorInfo.message,
|
|
104083
|
+
nextModel: `${providerID}/${transformedModelId}`
|
|
104084
|
+
};
|
|
104085
|
+
onRetrying?.({
|
|
104086
|
+
task,
|
|
104087
|
+
source,
|
|
104088
|
+
previousSessionID,
|
|
104089
|
+
failedModel: task.retryNotification.failedModel,
|
|
104090
|
+
failedError: errorInfo.message,
|
|
104091
|
+
nextModel: `${providerID}/${transformedModelId}`
|
|
104092
|
+
});
|
|
103678
104093
|
const key = task.model ? `${task.model.providerID}/${task.model.modelID}` : task.agent;
|
|
103679
104094
|
const queue = queuesByKey.get(key) ?? [];
|
|
103680
104095
|
const retryInput = {
|
|
@@ -103686,7 +104101,7 @@ async function tryFallbackRetry(args) {
|
|
|
103686
104101
|
parentModel: task.parentModel,
|
|
103687
104102
|
parentAgent: task.parentAgent,
|
|
103688
104103
|
parentTools: task.parentTools,
|
|
103689
|
-
model:
|
|
104104
|
+
model: nextModel,
|
|
103690
104105
|
fallbackChain: task.fallbackChain,
|
|
103691
104106
|
category: task.category,
|
|
103692
104107
|
isUnstableAgent: task.isUnstableAgent
|
|
@@ -103694,7 +104109,7 @@ async function tryFallbackRetry(args) {
|
|
|
103694
104109
|
if (previousSessionID) {
|
|
103695
104110
|
await abortWithTimeout(client2, previousSessionID).catch(() => {});
|
|
103696
104111
|
}
|
|
103697
|
-
queue.push({ task, input: retryInput });
|
|
104112
|
+
queue.push({ task, input: retryInput, attemptID: nextAttempt.attemptID });
|
|
103698
104113
|
queuesByKey.set(key, queue);
|
|
103699
104114
|
processKey(key);
|
|
103700
104115
|
return true;
|
|
@@ -104312,10 +104727,37 @@ function resolveMessagePartInfo(properties) {
|
|
|
104312
104727
|
}
|
|
104313
104728
|
return properties;
|
|
104314
104729
|
}
|
|
104730
|
+
function formatAttemptModelSummary(attempt) {
|
|
104731
|
+
if (!attempt?.providerID || !attempt.modelID) {
|
|
104732
|
+
return;
|
|
104733
|
+
}
|
|
104734
|
+
return `${attempt.providerID}/${attempt.modelID}`;
|
|
104735
|
+
}
|
|
104736
|
+
function getPreviousAttempt(task, attemptID) {
|
|
104737
|
+
if (!attemptID || !task.attempts || task.attempts.length === 0) {
|
|
104738
|
+
return;
|
|
104739
|
+
}
|
|
104740
|
+
const attemptIndex = task.attempts.findIndex((attempt) => attempt.attemptID === attemptID);
|
|
104741
|
+
if (attemptIndex <= 0) {
|
|
104742
|
+
return;
|
|
104743
|
+
}
|
|
104744
|
+
return task.attempts[attemptIndex - 1];
|
|
104745
|
+
}
|
|
104746
|
+
function cloneAttempts(task) {
|
|
104747
|
+
if (!task.attempts) {
|
|
104748
|
+
return;
|
|
104749
|
+
}
|
|
104750
|
+
return task.attempts.map((attempt) => ({ ...attempt }));
|
|
104751
|
+
}
|
|
104752
|
+
function buildLocalSessionUrl(directory, sessionID) {
|
|
104753
|
+
const encodedDirectory = Buffer.from(directory).toString("base64url");
|
|
104754
|
+
return `http://127.0.0.1:4096/${encodedDirectory}/session/${sessionID}`;
|
|
104755
|
+
}
|
|
104315
104756
|
var MAX_TASK_REMOVAL_RESCHEDULES = 6;
|
|
104316
104757
|
|
|
104317
104758
|
class BackgroundManager {
|
|
104318
104759
|
tasks;
|
|
104760
|
+
tasksByParentSession;
|
|
104319
104761
|
notifications;
|
|
104320
104762
|
pendingNotifications;
|
|
104321
104763
|
pendingByParent;
|
|
@@ -104340,10 +104782,12 @@ class BackgroundManager {
|
|
|
104340
104782
|
rootDescendantCounts;
|
|
104341
104783
|
preStartDescendantReservations;
|
|
104342
104784
|
enableParentSessionNotifications;
|
|
104785
|
+
modelFallbackControllerAccessor;
|
|
104343
104786
|
taskHistory = new TaskHistory;
|
|
104344
104787
|
cachedCircuitBreakerSettings;
|
|
104345
104788
|
constructor(ctx, config2, options) {
|
|
104346
104789
|
this.tasks = new Map;
|
|
104790
|
+
this.tasksByParentSession = new Map;
|
|
104347
104791
|
this.notifications = new Map;
|
|
104348
104792
|
this.pendingNotifications = new Map;
|
|
104349
104793
|
this.pendingByParent = new Map;
|
|
@@ -104357,6 +104801,7 @@ class BackgroundManager {
|
|
|
104357
104801
|
this.rootDescendantCounts = new Map;
|
|
104358
104802
|
this.preStartDescendantReservations = new Set;
|
|
104359
104803
|
this.enableParentSessionNotifications = options?.enableParentSessionNotifications ?? true;
|
|
104804
|
+
this.modelFallbackControllerAccessor = options?.modelFallbackControllerAccessor;
|
|
104360
104805
|
this.registerProcessCleanup();
|
|
104361
104806
|
}
|
|
104362
104807
|
async abortSessionWithLogging(sessionID, reason) {
|
|
@@ -104429,6 +104874,42 @@ class BackgroundManager {
|
|
|
104429
104874
|
}
|
|
104430
104875
|
this.unregisterRootDescendant(task.rootSessionID);
|
|
104431
104876
|
}
|
|
104877
|
+
addTask(task) {
|
|
104878
|
+
this.tasks.set(task.id, task);
|
|
104879
|
+
if (!task.parentSessionID) {
|
|
104880
|
+
return;
|
|
104881
|
+
}
|
|
104882
|
+
const taskIDs = this.tasksByParentSession.get(task.parentSessionID) ?? new Set;
|
|
104883
|
+
taskIDs.add(task.id);
|
|
104884
|
+
this.tasksByParentSession.set(task.parentSessionID, taskIDs);
|
|
104885
|
+
}
|
|
104886
|
+
removeTask(task) {
|
|
104887
|
+
this.tasks.delete(task.id);
|
|
104888
|
+
this.removeTaskFromParentIndex(task.id, task.parentSessionID);
|
|
104889
|
+
}
|
|
104890
|
+
updateTaskParent(task, parentSessionID) {
|
|
104891
|
+
if (task.parentSessionID === parentSessionID) {
|
|
104892
|
+
return;
|
|
104893
|
+
}
|
|
104894
|
+
this.removeTaskFromParentIndex(task.id, task.parentSessionID);
|
|
104895
|
+
task.parentSessionID = parentSessionID;
|
|
104896
|
+
const taskIDs = this.tasksByParentSession.get(parentSessionID) ?? new Set;
|
|
104897
|
+
taskIDs.add(task.id);
|
|
104898
|
+
this.tasksByParentSession.set(parentSessionID, taskIDs);
|
|
104899
|
+
}
|
|
104900
|
+
removeTaskFromParentIndex(taskID, parentSessionID) {
|
|
104901
|
+
if (!parentSessionID) {
|
|
104902
|
+
return;
|
|
104903
|
+
}
|
|
104904
|
+
const taskIDs = this.tasksByParentSession.get(parentSessionID);
|
|
104905
|
+
if (!taskIDs) {
|
|
104906
|
+
return;
|
|
104907
|
+
}
|
|
104908
|
+
taskIDs.delete(taskID);
|
|
104909
|
+
if (taskIDs.size === 0) {
|
|
104910
|
+
this.tasksByParentSession.delete(parentSessionID);
|
|
104911
|
+
}
|
|
104912
|
+
}
|
|
104432
104913
|
async launch(input) {
|
|
104433
104914
|
log("[background-agent] launch() called with:", {
|
|
104434
104915
|
agent: input.agent,
|
|
@@ -104466,7 +104947,8 @@ class BackgroundManager {
|
|
|
104466
104947
|
attemptCount: 0,
|
|
104467
104948
|
category: input.category
|
|
104468
104949
|
};
|
|
104469
|
-
|
|
104950
|
+
const firstAttempt = startAttempt(task, input.model);
|
|
104951
|
+
this.addTask(task);
|
|
104470
104952
|
this.taskHistory.record(input.parentSessionID, { id: task.id, agent: input.agent, description: input.description, status: "pending", category: input.category });
|
|
104471
104953
|
if (input.parentSessionID) {
|
|
104472
104954
|
const pending = this.pendingByParent.get(input.parentSessionID) ?? new Set;
|
|
@@ -104475,7 +104957,7 @@ class BackgroundManager {
|
|
|
104475
104957
|
}
|
|
104476
104958
|
const key = this.getConcurrencyKeyFromInput(input);
|
|
104477
104959
|
const queue = this.queuesByKey.get(key) ?? [];
|
|
104478
|
-
queue.push({ task, input });
|
|
104960
|
+
queue.push({ task, input, attemptID: firstAttempt.attemptID });
|
|
104479
104961
|
this.queuesByKey.set(key, queue);
|
|
104480
104962
|
log("[background-agent] Task queued:", { taskId: task.id, key, queueLength: queue.length });
|
|
104481
104963
|
const toastManager = getTaskToastManager();
|
|
@@ -104521,9 +105003,13 @@ class BackgroundManager {
|
|
|
104521
105003
|
} catch (error) {
|
|
104522
105004
|
log("[background-agent] Error starting task:", error);
|
|
104523
105005
|
this.rollbackPreStartDescendantReservation(item.task);
|
|
104524
|
-
item.task.
|
|
104525
|
-
|
|
104526
|
-
|
|
105006
|
+
if (item.task.currentAttemptID) {
|
|
105007
|
+
finalizeAttempt(item.task, item.task.currentAttemptID, "error", error instanceof Error ? error.message : String(error));
|
|
105008
|
+
} else {
|
|
105009
|
+
item.task.status = "error";
|
|
105010
|
+
item.task.error = error instanceof Error ? error.message : String(error);
|
|
105011
|
+
item.task.completedAt = new Date;
|
|
105012
|
+
}
|
|
104527
105013
|
if (item.task.concurrencyKey) {
|
|
104528
105014
|
this.concurrencyManager.release(item.task.concurrencyKey);
|
|
104529
105015
|
item.task.concurrencyKey = undefined;
|
|
@@ -104546,6 +105032,7 @@ class BackgroundManager {
|
|
|
104546
105032
|
}
|
|
104547
105033
|
async startTask(item) {
|
|
104548
105034
|
const { task, input } = item;
|
|
105035
|
+
const attemptID = item.attemptID ?? ensureCurrentAttempt(task, input.model).attemptID;
|
|
104549
105036
|
log("[background-agent] Starting task:", {
|
|
104550
105037
|
taskId: task.id,
|
|
104551
105038
|
agent: input.agent,
|
|
@@ -104615,15 +105102,49 @@ class BackgroundManager {
|
|
|
104615
105102
|
this.concurrencyManager.release(concurrencyKey);
|
|
104616
105103
|
return;
|
|
104617
105104
|
}
|
|
104618
|
-
|
|
104619
|
-
|
|
104620
|
-
|
|
105105
|
+
const boundAttempt = bindAttemptSession(task, attemptID, sessionID, input.model);
|
|
105106
|
+
if (!boundAttempt) {
|
|
105107
|
+
await this.abortSessionWithLogging(sessionID, "stale attempt binding cleanup");
|
|
105108
|
+
subagentSessions.delete(sessionID);
|
|
105109
|
+
if (task.rootSessionID) {
|
|
105110
|
+
this.unregisterRootDescendant(task.rootSessionID);
|
|
105111
|
+
}
|
|
105112
|
+
this.concurrencyManager.release(concurrencyKey);
|
|
105113
|
+
return;
|
|
105114
|
+
}
|
|
104621
105115
|
task.progress = {
|
|
104622
105116
|
toolCalls: 0,
|
|
104623
105117
|
lastUpdate: new Date
|
|
104624
105118
|
};
|
|
104625
105119
|
task.concurrencyKey = concurrencyKey;
|
|
104626
105120
|
task.concurrencyGroup = concurrencyKey;
|
|
105121
|
+
if (task.retryNotification) {
|
|
105122
|
+
const attemptNumber = boundAttempt.attemptNumber;
|
|
105123
|
+
const retrySessionUrl = buildLocalSessionUrl(parentDirectory, sessionID);
|
|
105124
|
+
const previousAttempt = getPreviousAttempt(task, boundAttempt.attemptID);
|
|
105125
|
+
const failedSessionID = previousAttempt?.sessionID ?? task.retryNotification.previousSessionID;
|
|
105126
|
+
const failedSessionLine = failedSessionID ? `
|
|
105127
|
+
- Failed session: \`${failedSessionID}\`` : "";
|
|
105128
|
+
const failedModel = formatAttemptModelSummary(previousAttempt) ?? task.retryNotification.failedModel;
|
|
105129
|
+
const failedModelLine = failedModel ? `
|
|
105130
|
+
- Failed model: \`${failedModel}\`` : "";
|
|
105131
|
+
const failedError = previousAttempt?.error ?? task.retryNotification.failedError;
|
|
105132
|
+
const failedErrorLine = failedError ? `
|
|
105133
|
+
- Error: ${failedError}` : "";
|
|
105134
|
+
const retryModel = formatAttemptModelSummary(boundAttempt) ?? task.retryNotification.nextModel;
|
|
105135
|
+
this.queuePendingNotification(task.parentSessionID, `<system-reminder>
|
|
105136
|
+
[BACKGROUND TASK RETRY SESSION READY]
|
|
105137
|
+
**ID:** \`${task.id}\`
|
|
105138
|
+
**Description:** ${task.description}
|
|
105139
|
+
**Retry attempt:** ${attemptNumber}
|
|
105140
|
+
**Retry session:** \`${sessionID}\`
|
|
105141
|
+
**Retry link:** ${retrySessionUrl}${failedSessionLine}${failedModelLine}${failedErrorLine}${retryModel ? `
|
|
105142
|
+
- Model: \`${retryModel}\`` : ""}
|
|
105143
|
+
|
|
105144
|
+
The fallback retry session is now created and can be inspected directly.
|
|
105145
|
+
</system-reminder>`);
|
|
105146
|
+
task.retryNotification = undefined;
|
|
105147
|
+
}
|
|
104627
105148
|
this.taskHistory.record(input.parentSessionID, { id: task.id, sessionID, agent: input.agent, description: input.description, status: "running", category: input.category, startedAt: task.startedAt });
|
|
104628
105149
|
this.startPolling();
|
|
104629
105150
|
log("[background-agent] Launching task:", { taskId: task.id, sessionID, agent: input.agent });
|
|
@@ -104687,16 +105208,33 @@ class BackgroundManager {
|
|
|
104687
105208
|
}
|
|
104688
105209
|
}
|
|
104689
105210
|
log("[background-agent] promptAsync error:", error);
|
|
104690
|
-
const
|
|
105211
|
+
const resolvedTask = this.resolveTaskAttemptBySession(sessionID);
|
|
105212
|
+
const existingTask = resolvedTask?.task;
|
|
105213
|
+
if (resolvedTask && !resolvedTask.isCurrent) {
|
|
105214
|
+
log("[background-agent] Ignoring prompt error from stale attempt session", {
|
|
105215
|
+
sessionID,
|
|
105216
|
+
currentAttemptID: resolvedTask.task.currentAttemptID,
|
|
105217
|
+
attemptID: resolvedTask.attemptID
|
|
105218
|
+
});
|
|
105219
|
+
return;
|
|
105220
|
+
}
|
|
104691
105221
|
if (existingTask) {
|
|
104692
|
-
|
|
104693
|
-
|
|
104694
|
-
|
|
104695
|
-
|
|
105222
|
+
const errorInfo = {
|
|
105223
|
+
name: extractErrorName2(error),
|
|
105224
|
+
message: extractErrorMessage(error)
|
|
105225
|
+
};
|
|
105226
|
+
if (await this.tryFallbackRetry(existingTask, errorInfo, "promptAsync.launch")) {
|
|
105227
|
+
return;
|
|
105228
|
+
}
|
|
105229
|
+
const errorMessage = errorInfo.message ?? (error instanceof Error ? error.message : String(error));
|
|
105230
|
+
const terminalError = errorMessage.includes("agent.name") || errorMessage.includes("undefined") || isAgentNotFoundError(error) ? `Agent "${input.agent}" not found. Make sure the agent is registered in your opencode.json or provided by a plugin.` : errorMessage;
|
|
105231
|
+
if (existingTask.currentAttemptID) {
|
|
105232
|
+
finalizeAttempt(existingTask, existingTask.currentAttemptID, "interrupt", terminalError);
|
|
104696
105233
|
} else {
|
|
104697
|
-
existingTask.
|
|
105234
|
+
existingTask.status = "interrupt";
|
|
105235
|
+
existingTask.error = terminalError;
|
|
105236
|
+
existingTask.completedAt = new Date;
|
|
104698
105237
|
}
|
|
104699
|
-
existingTask.completedAt = new Date;
|
|
104700
105238
|
if (existingTask.rootSessionID) {
|
|
104701
105239
|
this.unregisterRootDescendant(existingTask.rootSessionID);
|
|
104702
105240
|
}
|
|
@@ -104717,13 +105255,24 @@ class BackgroundManager {
|
|
|
104717
105255
|
return this.tasks.get(id);
|
|
104718
105256
|
}
|
|
104719
105257
|
getTasksByParentSession(sessionID) {
|
|
104720
|
-
const
|
|
104721
|
-
|
|
104722
|
-
|
|
104723
|
-
|
|
105258
|
+
const taskIDs = this.tasksByParentSession.get(sessionID);
|
|
105259
|
+
if (!taskIDs) {
|
|
105260
|
+
const result = [];
|
|
105261
|
+
for (const task of this.tasks.values()) {
|
|
105262
|
+
if (task.parentSessionID === sessionID) {
|
|
105263
|
+
result.push(task);
|
|
105264
|
+
}
|
|
105265
|
+
}
|
|
105266
|
+
return result;
|
|
105267
|
+
}
|
|
105268
|
+
const tasks = [];
|
|
105269
|
+
for (const taskID of taskIDs) {
|
|
105270
|
+
const task = this.tasks.get(taskID);
|
|
105271
|
+
if (task) {
|
|
105272
|
+
tasks.push(task);
|
|
104724
105273
|
}
|
|
104725
105274
|
}
|
|
104726
|
-
return
|
|
105275
|
+
return tasks;
|
|
104727
105276
|
}
|
|
104728
105277
|
getAllDescendantTasks(sessionID) {
|
|
104729
105278
|
const result = [];
|
|
@@ -104742,9 +105291,31 @@ class BackgroundManager {
|
|
|
104742
105291
|
if (task.sessionID === sessionID) {
|
|
104743
105292
|
return task;
|
|
104744
105293
|
}
|
|
105294
|
+
if (findAttemptBySession(task, sessionID)) {
|
|
105295
|
+
return task;
|
|
105296
|
+
}
|
|
104745
105297
|
}
|
|
104746
105298
|
return;
|
|
104747
105299
|
}
|
|
105300
|
+
resolveTaskAttemptBySession(sessionID) {
|
|
105301
|
+
const task = this.findBySession(sessionID);
|
|
105302
|
+
if (!task) {
|
|
105303
|
+
return;
|
|
105304
|
+
}
|
|
105305
|
+
const attempt = findAttemptBySession(task, sessionID);
|
|
105306
|
+
if (!attempt) {
|
|
105307
|
+
return {
|
|
105308
|
+
task,
|
|
105309
|
+
attemptID: undefined,
|
|
105310
|
+
isCurrent: task.sessionID === sessionID
|
|
105311
|
+
};
|
|
105312
|
+
}
|
|
105313
|
+
return {
|
|
105314
|
+
task,
|
|
105315
|
+
attemptID: attempt.attemptID,
|
|
105316
|
+
isCurrent: task.currentAttemptID === attempt.attemptID
|
|
105317
|
+
};
|
|
105318
|
+
}
|
|
104748
105319
|
getConcurrencyKeyFromInput(input) {
|
|
104749
105320
|
if (input.model) {
|
|
104750
105321
|
return `${input.model.providerID}/${input.model.modelID}`;
|
|
@@ -104757,7 +105328,7 @@ class BackgroundManager {
|
|
|
104757
105328
|
const parentChanged = input.parentSessionID !== existingTask.parentSessionID;
|
|
104758
105329
|
if (parentChanged) {
|
|
104759
105330
|
this.cleanupPendingByParent(existingTask);
|
|
104760
|
-
existingTask
|
|
105331
|
+
this.updateTaskParent(existingTask, input.parentSessionID);
|
|
104761
105332
|
}
|
|
104762
105333
|
if (input.parentAgent !== undefined) {
|
|
104763
105334
|
existingTask.parentAgent = input.parentAgent;
|
|
@@ -104801,7 +105372,7 @@ class BackgroundManager {
|
|
|
104801
105372
|
concurrencyKey: input.concurrencyKey,
|
|
104802
105373
|
concurrencyGroup
|
|
104803
105374
|
};
|
|
104804
|
-
this.
|
|
105375
|
+
this.addTask(task);
|
|
104805
105376
|
subagentSessions.add(input.sessionID);
|
|
104806
105377
|
this.startPolling();
|
|
104807
105378
|
this.taskHistory.record(input.parentSessionID, { id: task.id, sessionID: input.sessionID, agent: input.agent || "task", description: input.description, status: "running", startedAt: task.startedAt });
|
|
@@ -104840,7 +105411,7 @@ class BackgroundManager {
|
|
|
104840
105411
|
existingTask.status = "running";
|
|
104841
105412
|
existingTask.completedAt = undefined;
|
|
104842
105413
|
existingTask.error = undefined;
|
|
104843
|
-
existingTask
|
|
105414
|
+
this.updateTaskParent(existingTask, input.parentSessionID);
|
|
104844
105415
|
existingTask.parentMessageID = input.parentMessageID;
|
|
104845
105416
|
existingTask.parentModel = input.parentModel;
|
|
104846
105417
|
existingTask.parentAgent = input.parentAgent;
|
|
@@ -104907,8 +105478,15 @@ class BackgroundManager {
|
|
|
104907
105478
|
}
|
|
104908
105479
|
}).catch(async (error) => {
|
|
104909
105480
|
log("[background-agent] resume prompt error:", error);
|
|
105481
|
+
const errorInfo = {
|
|
105482
|
+
name: extractErrorName2(error),
|
|
105483
|
+
message: extractErrorMessage(error)
|
|
105484
|
+
};
|
|
105485
|
+
if (await this.tryFallbackRetry(existingTask, errorInfo, "promptAsync.resume")) {
|
|
105486
|
+
return;
|
|
105487
|
+
}
|
|
104910
105488
|
existingTask.status = "interrupt";
|
|
104911
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
105489
|
+
const errorMessage = errorInfo.message ?? (error instanceof Error ? error.message : String(error));
|
|
104912
105490
|
existingTask.error = errorMessage;
|
|
104913
105491
|
existingTask.completedAt = new Date;
|
|
104914
105492
|
if (existingTask.rootSessionID) {
|
|
@@ -104991,8 +105569,11 @@ class BackgroundManager {
|
|
|
104991
105569
|
}
|
|
104992
105570
|
if (role !== "assistant")
|
|
104993
105571
|
return;
|
|
104994
|
-
const
|
|
104995
|
-
if (!
|
|
105572
|
+
const resolved = this.resolveTaskAttemptBySession(sessionID);
|
|
105573
|
+
if (!resolved?.isCurrent)
|
|
105574
|
+
return;
|
|
105575
|
+
const { task } = resolved;
|
|
105576
|
+
if (task.status !== "running")
|
|
104996
105577
|
return;
|
|
104997
105578
|
const assistantError = info["error"];
|
|
104998
105579
|
if (!assistantError)
|
|
@@ -105013,9 +105594,10 @@ class BackgroundManager {
|
|
|
105013
105594
|
const sessionID = partInfo?.sessionID;
|
|
105014
105595
|
if (!sessionID)
|
|
105015
105596
|
return;
|
|
105016
|
-
const
|
|
105017
|
-
if (!
|
|
105597
|
+
const resolved = this.resolveTaskAttemptBySession(sessionID);
|
|
105598
|
+
if (!resolved?.isCurrent)
|
|
105018
105599
|
return;
|
|
105600
|
+
const { task } = resolved;
|
|
105019
105601
|
if (this.hasOutputSignalFromPart(partInfo)) {
|
|
105020
105602
|
this.markSessionOutputObserved(sessionID);
|
|
105021
105603
|
}
|
|
@@ -105100,7 +105682,10 @@ class BackgroundManager {
|
|
|
105100
105682
|
return;
|
|
105101
105683
|
handleSessionIdleBackgroundEvent({
|
|
105102
105684
|
properties: props,
|
|
105103
|
-
findBySession: (id) =>
|
|
105685
|
+
findBySession: (id) => {
|
|
105686
|
+
const resolved = this.resolveTaskAttemptBySession(id);
|
|
105687
|
+
return resolved?.isCurrent ? resolved.task : undefined;
|
|
105688
|
+
},
|
|
105104
105689
|
idleDeferralTimers: this.idleDeferralTimers,
|
|
105105
105690
|
validateSessionHasOutput: (id) => this.validateSessionHasOutput(id),
|
|
105106
105691
|
checkSessionTodos: (id) => this.checkSessionTodos(id),
|
|
@@ -105112,8 +105697,11 @@ class BackgroundManager {
|
|
|
105112
105697
|
const sessionID = typeof props?.sessionID === "string" ? props.sessionID : undefined;
|
|
105113
105698
|
if (!sessionID)
|
|
105114
105699
|
return;
|
|
105115
|
-
const
|
|
105116
|
-
if (!
|
|
105700
|
+
const resolved = this.resolveTaskAttemptBySession(sessionID);
|
|
105701
|
+
if (!resolved?.isCurrent)
|
|
105702
|
+
return;
|
|
105703
|
+
const { task } = resolved;
|
|
105704
|
+
if (task.status !== "running")
|
|
105117
105705
|
return;
|
|
105118
105706
|
const errorObj = props?.error;
|
|
105119
105707
|
const errorName = errorObj?.name;
|
|
@@ -105140,9 +105728,9 @@ class BackgroundManager {
|
|
|
105140
105728
|
this.clearSessionOutputObserved(sessionID);
|
|
105141
105729
|
this.clearSessionTodoObservation(sessionID);
|
|
105142
105730
|
const tasksToCancel = new Map;
|
|
105143
|
-
const directTask = this.
|
|
105144
|
-
if (directTask) {
|
|
105145
|
-
tasksToCancel.set(directTask.id, directTask);
|
|
105731
|
+
const directTask = this.resolveTaskAttemptBySession(sessionID);
|
|
105732
|
+
if (directTask?.isCurrent) {
|
|
105733
|
+
tasksToCancel.set(directTask.task.id, directTask.task);
|
|
105146
105734
|
}
|
|
105147
105735
|
for (const descendant of this.getAllDescendantTasks(sessionID)) {
|
|
105148
105736
|
tasksToCancel.set(descendant.id, descendant);
|
|
@@ -105188,8 +105776,11 @@ class BackgroundManager {
|
|
|
105188
105776
|
const status = props?.status;
|
|
105189
105777
|
if (!sessionID || status?.type !== "retry")
|
|
105190
105778
|
return;
|
|
105191
|
-
const
|
|
105192
|
-
if (!
|
|
105779
|
+
const resolved = this.resolveTaskAttemptBySession(sessionID);
|
|
105780
|
+
if (!resolved?.isCurrent)
|
|
105781
|
+
return;
|
|
105782
|
+
const { task } = resolved;
|
|
105783
|
+
if (task.status !== "running")
|
|
105193
105784
|
return;
|
|
105194
105785
|
const errorMessage = typeof status.message === "string" ? status.message : undefined;
|
|
105195
105786
|
const errorInfo = { name: "SessionRetry", message: errorMessage };
|
|
@@ -105203,6 +105794,12 @@ class BackgroundManager {
|
|
|
105203
105794
|
}
|
|
105204
105795
|
async handleSessionErrorEvent(args) {
|
|
105205
105796
|
const { task, errorInfo, errorMessage, errorName } = args;
|
|
105797
|
+
if (!task.fallbackChain && task.sessionID) {
|
|
105798
|
+
const sessionFallbackChain = this.modelFallbackControllerAccessor?.getSessionFallbackChain(task.sessionID);
|
|
105799
|
+
if (sessionFallbackChain?.length) {
|
|
105800
|
+
task.fallbackChain = sessionFallbackChain;
|
|
105801
|
+
}
|
|
105802
|
+
}
|
|
105206
105803
|
if (isAgentNotFoundError({ message: errorInfo.message })) {
|
|
105207
105804
|
log("[background-agent] Skipping session.error fallback for agent-not-found (handled by prompt catch)", {
|
|
105208
105805
|
taskId: task.id,
|
|
@@ -105222,9 +105819,13 @@ class BackgroundManager {
|
|
|
105222
105819
|
hasFallbackChain: !!task.fallbackChain,
|
|
105223
105820
|
canRetry
|
|
105224
105821
|
});
|
|
105225
|
-
task.
|
|
105226
|
-
|
|
105227
|
-
|
|
105822
|
+
if (task.currentAttemptID) {
|
|
105823
|
+
finalizeAttempt(task, task.currentAttemptID, "error", errorMsg);
|
|
105824
|
+
} else {
|
|
105825
|
+
task.status = "error";
|
|
105826
|
+
task.error = errorMsg;
|
|
105827
|
+
task.completedAt = new Date;
|
|
105828
|
+
}
|
|
105228
105829
|
if (task.rootSessionID) {
|
|
105229
105830
|
this.unregisterRootDescendant(task.rootSessionID);
|
|
105230
105831
|
}
|
|
@@ -105268,7 +105869,28 @@ class BackgroundManager {
|
|
|
105268
105869
|
client: this.client,
|
|
105269
105870
|
idleDeferralTimers: this.idleDeferralTimers,
|
|
105270
105871
|
queuesByKey: this.queuesByKey,
|
|
105271
|
-
processKey: (key) => this.processKey(key)
|
|
105872
|
+
processKey: (key) => this.processKey(key),
|
|
105873
|
+
onRetrying: ({ task: task2, source: source2 }) => {
|
|
105874
|
+
const currentAttempt = getCurrentAttempt(task2);
|
|
105875
|
+
const previousAttempt = getPreviousAttempt(task2, currentAttempt?.attemptID);
|
|
105876
|
+
const sourceText = source2 ? ` via ${source2}` : "";
|
|
105877
|
+
const failedSessionLine = previousAttempt?.sessionID ? `
|
|
105878
|
+
- Failed session: \`${previousAttempt.sessionID}\`` : "";
|
|
105879
|
+
const failedModel = formatAttemptModelSummary(previousAttempt);
|
|
105880
|
+
const failedModelLine = failedModel ? `
|
|
105881
|
+
- Failed model: \`${failedModel}\`` : "";
|
|
105882
|
+
const failedErrorLine = previousAttempt?.error ? `
|
|
105883
|
+
- Error: ${previousAttempt.error}` : "";
|
|
105884
|
+
const nextModel = formatAttemptModelSummary(currentAttempt);
|
|
105885
|
+
this.queuePendingNotification(task2.parentSessionID, `<system-reminder>
|
|
105886
|
+
[BACKGROUND TASK RETRYING]
|
|
105887
|
+
**ID:** \`${task2.id}\`
|
|
105888
|
+
**Description:** ${task2.description}${sourceText}${failedSessionLine}${failedModelLine}${failedErrorLine}${nextModel ? `
|
|
105889
|
+
- Next model: \`${nextModel}\`` : ""}
|
|
105890
|
+
|
|
105891
|
+
The task was re-queued on a fallback model after a retryable failure.
|
|
105892
|
+
</system-reminder>`);
|
|
105893
|
+
}
|
|
105272
105894
|
});
|
|
105273
105895
|
return result.then((retried) => {
|
|
105274
105896
|
if (retried && previousSessionID) {
|
|
@@ -105400,7 +106022,7 @@ ${originalText}`;
|
|
|
105400
106022
|
}
|
|
105401
106023
|
}
|
|
105402
106024
|
this.clearNotificationsForTask(taskId);
|
|
105403
|
-
this.
|
|
106025
|
+
this.removeTask(task);
|
|
105404
106026
|
this.clearTaskHistoryWhenParentTasksGone(task.parentSessionID);
|
|
105405
106027
|
if (task.sessionID) {
|
|
105406
106028
|
subagentSessions.delete(task.sessionID);
|
|
@@ -105434,14 +106056,18 @@ ${originalText}`;
|
|
|
105434
106056
|
log("[background-agent] Cancelled pending task:", { taskId, key });
|
|
105435
106057
|
}
|
|
105436
106058
|
const wasRunning = task.status === "running";
|
|
105437
|
-
task.
|
|
105438
|
-
|
|
106059
|
+
if (task.currentAttemptID) {
|
|
106060
|
+
finalizeAttempt(task, task.currentAttemptID, "cancelled", reason);
|
|
106061
|
+
} else {
|
|
106062
|
+
task.status = "cancelled";
|
|
106063
|
+
task.completedAt = new Date;
|
|
106064
|
+
if (reason) {
|
|
106065
|
+
task.error = reason;
|
|
106066
|
+
}
|
|
106067
|
+
}
|
|
105439
106068
|
if (wasRunning && task.rootSessionID) {
|
|
105440
106069
|
this.unregisterRootDescendant(task.rootSessionID);
|
|
105441
106070
|
}
|
|
105442
|
-
if (reason) {
|
|
105443
|
-
task.error = reason;
|
|
105444
|
-
}
|
|
105445
106071
|
this.taskHistory.record(task.parentSessionID, { id: task.id, sessionID: task.sessionID, agent: task.agent, description: task.description, status: "cancelled", category: task.category, startedAt: task.startedAt, completedAt: task.completedAt });
|
|
105446
106072
|
if (task.concurrencyKey) {
|
|
105447
106073
|
this.concurrencyManager.release(task.concurrencyKey);
|
|
@@ -105516,8 +106142,12 @@ ${originalText}`;
|
|
|
105516
106142
|
log("[background-agent] Task already completed, skipping:", { taskId: task.id, status: task.status, source });
|
|
105517
106143
|
return false;
|
|
105518
106144
|
}
|
|
105519
|
-
task.
|
|
105520
|
-
|
|
106145
|
+
if (task.currentAttemptID) {
|
|
106146
|
+
finalizeAttempt(task, task.currentAttemptID, "completed");
|
|
106147
|
+
} else {
|
|
106148
|
+
task.status = "completed";
|
|
106149
|
+
task.completedAt = new Date;
|
|
106150
|
+
}
|
|
105521
106151
|
this.taskHistory.record(task.parentSessionID, { id: task.id, sessionID: task.sessionID, agent: task.agent, description: task.description, status: "completed", category: task.category, startedAt: task.startedAt, completedAt: task.completedAt });
|
|
105522
106152
|
if (task.rootSessionID) {
|
|
105523
106153
|
this.unregisterRootDescendant(task.rootSessionID);
|
|
@@ -105563,7 +106193,8 @@ ${originalText}`;
|
|
|
105563
106193
|
id: task.id,
|
|
105564
106194
|
description: task.description,
|
|
105565
106195
|
status: task.status,
|
|
105566
|
-
error: task.error
|
|
106196
|
+
error: task.error,
|
|
106197
|
+
attempts: cloneAttempts(task)
|
|
105567
106198
|
});
|
|
105568
106199
|
const pendingSet = this.pendingByParent.get(task.parentSessionID);
|
|
105569
106200
|
let allComplete = false;
|
|
@@ -105579,7 +106210,7 @@ ${originalText}`;
|
|
|
105579
106210
|
remainingCount = Array.from(this.tasks.values()).filter((t) => t.parentSessionID === task.parentSessionID && t.id !== task.id && (t.status === "running" || t.status === "pending")).length;
|
|
105580
106211
|
allComplete = remainingCount === 0;
|
|
105581
106212
|
}
|
|
105582
|
-
const completedTasks = allComplete ? this.completedTaskSummaries.get(task.parentSessionID) ?? [{ id: task.id, description: task.description, status: task.status, error: task.error }] : [];
|
|
106213
|
+
const completedTasks = allComplete ? this.completedTaskSummaries.get(task.parentSessionID) ?? [{ id: task.id, description: task.description, status: task.status, error: task.error, attempts: cloneAttempts(task) }] : [];
|
|
105583
106214
|
if (allComplete) {
|
|
105584
106215
|
this.completedTaskSummaries.delete(task.parentSessionID);
|
|
105585
106216
|
}
|
|
@@ -105741,9 +106372,13 @@ ${originalText}`;
|
|
|
105741
106372
|
return verifySessionExists(this.client, sessionID, this.directory);
|
|
105742
106373
|
}
|
|
105743
106374
|
async failCrashedTask(task, errorMessage) {
|
|
105744
|
-
task.
|
|
105745
|
-
|
|
105746
|
-
|
|
106375
|
+
if (task.currentAttemptID) {
|
|
106376
|
+
finalizeAttempt(task, task.currentAttemptID, "error", errorMessage);
|
|
106377
|
+
} else {
|
|
106378
|
+
task.status = "error";
|
|
106379
|
+
task.error = errorMessage;
|
|
106380
|
+
task.completedAt = new Date;
|
|
106381
|
+
}
|
|
105747
106382
|
if (task.rootSessionID) {
|
|
105748
106383
|
this.unregisterRootDescendant(task.rootSessionID);
|
|
105749
106384
|
}
|
|
@@ -105911,6 +106546,7 @@ ${originalText}`;
|
|
|
105911
106546
|
}
|
|
105912
106547
|
this.concurrencyManager.clear();
|
|
105913
106548
|
this.tasks.clear();
|
|
106549
|
+
this.tasksByParentSession.clear();
|
|
105914
106550
|
this.notifications.clear();
|
|
105915
106551
|
this.pendingNotifications.clear();
|
|
105916
106552
|
this.pendingByParent.clear();
|
|
@@ -111164,12 +111800,16 @@ function createModelFallbackControllerAccessor() {
|
|
|
111164
111800
|
function setSessionFallbackChain2(sessionID, fallbackChain) {
|
|
111165
111801
|
controller?.setSessionFallbackChain(sessionID, fallbackChain);
|
|
111166
111802
|
}
|
|
111803
|
+
function getSessionFallbackChain(sessionID) {
|
|
111804
|
+
return controller?.getSessionFallbackChain(sessionID);
|
|
111805
|
+
}
|
|
111167
111806
|
function clearSessionFallbackChain2(sessionID) {
|
|
111168
111807
|
controller?.clearSessionFallbackChain(sessionID);
|
|
111169
111808
|
}
|
|
111170
111809
|
return {
|
|
111171
111810
|
register,
|
|
111172
111811
|
setSessionFallbackChain: setSessionFallbackChain2,
|
|
111812
|
+
getSessionFallbackChain,
|
|
111173
111813
|
clearSessionFallbackChain: clearSessionFallbackChain2
|
|
111174
111814
|
};
|
|
111175
111815
|
}
|
|
@@ -114065,6 +114705,12 @@ async function loadMcpConfigs(disabledMcps = []) {
|
|
|
114065
114705
|
}
|
|
114066
114706
|
return { servers, loadedServers };
|
|
114067
114707
|
}
|
|
114708
|
+
// src/agents/index.ts
|
|
114709
|
+
init_types();
|
|
114710
|
+
|
|
114711
|
+
// src/agents/sisyphus.ts
|
|
114712
|
+
init_types();
|
|
114713
|
+
|
|
114068
114714
|
// src/agents/sisyphus/gemini.ts
|
|
114069
114715
|
function buildGeminiToolMandate() {
|
|
114070
114716
|
return `<TOOL_CALL_MANDATE>
|
|
@@ -115208,6 +115854,7 @@ ${antiPatterns}
|
|
|
115208
115854
|
}
|
|
115209
115855
|
|
|
115210
115856
|
// src/agents/gpt-apply-patch-guard.ts
|
|
115857
|
+
init_types();
|
|
115211
115858
|
var GPT_APPLY_PATCH_GUIDANCE = "Use the `edit` and `write` tools for file changes. Do not use `apply_patch` on GPT models - it is unreliable here and can hang during verification.";
|
|
115212
115859
|
function getGptApplyPatchPermission(model) {
|
|
115213
115860
|
return isGptModel(model) ? { apply_patch: "deny" } : {};
|
|
@@ -116313,6 +116960,7 @@ ${styleBlock}`;
|
|
|
116313
116960
|
}
|
|
116314
116961
|
|
|
116315
116962
|
// src/agents/frontier-tool-schema-guard.ts
|
|
116963
|
+
init_types();
|
|
116316
116964
|
var FRONTIER_TOOL_SCHEMA_NAMES = ["grep", "glob"];
|
|
116317
116965
|
function isOpus47Model(model) {
|
|
116318
116966
|
const modelName = model.includes("/") ? model.split("/").pop() ?? model : model;
|
|
@@ -116864,6 +117512,7 @@ ${buildGeminiVerificationOverride()}
|
|
|
116864
117512
|
createSisyphusAgent.mode = MODE;
|
|
116865
117513
|
|
|
116866
117514
|
// src/agents/oracle.ts
|
|
117515
|
+
init_types();
|
|
116867
117516
|
var MODE2 = "subagent";
|
|
116868
117517
|
var ORACLE_PROMPT_METADATA = {
|
|
116869
117518
|
category: "advisor",
|
|
@@ -118077,6 +118726,9 @@ var metisPromptMetadata = {
|
|
|
118077
118726
|
keyTrigger: "Ambiguous or complex request \u2192 consult Metis before Prometheus"
|
|
118078
118727
|
};
|
|
118079
118728
|
|
|
118729
|
+
// src/agents/atlas/agent.ts
|
|
118730
|
+
init_types();
|
|
118731
|
+
|
|
118080
118732
|
// src/agents/atlas/shared-prompt.ts
|
|
118081
118733
|
var ATLAS_DELEGATION_SYSTEM = `<delegation_system>
|
|
118082
118734
|
## How to Delegate
|
|
@@ -119303,6 +119955,7 @@ var atlasPromptMetadata = {
|
|
|
119303
119955
|
keyTrigger: "Todo list path provided OR multiple tasks requiring multi-agent orchestration"
|
|
119304
119956
|
};
|
|
119305
119957
|
// src/agents/momus.ts
|
|
119958
|
+
init_types();
|
|
119306
119959
|
var MODE8 = "subagent";
|
|
119307
119960
|
var MOMUS_DEFAULT_PROMPT = `You are a **practical** work plan reviewer. Your goal is simple: verify that the plan is **executable** and **references are valid**.
|
|
119308
119961
|
|
|
@@ -119607,6 +120260,9 @@ var momusPromptMetadata = {
|
|
|
119607
120260
|
keyTrigger: 'Work plan saved to `.sisyphus/plans/*.md` \u2192 invoke Momus with the file path as the sole prompt (e.g. `prompt=".sisyphus/plans/my-plan.md"`). Do NOT invoke Momus for inline plans or todo lists.'
|
|
119608
120261
|
};
|
|
119609
120262
|
|
|
120263
|
+
// src/agents/hephaestus/agent.ts
|
|
120264
|
+
init_types();
|
|
120265
|
+
|
|
119610
120266
|
// src/agents/hephaestus/gpt.ts
|
|
119611
120267
|
function buildTodoDisciplineSection(useTaskSystem) {
|
|
119612
120268
|
if (useTaskSystem) {
|
|
@@ -122104,6 +122760,7 @@ No tasks on multi-step work = INCOMPLETE WORK. The user tracks your progress thr
|
|
|
122104
122760
|
No todos on multi-step work = INCOMPLETE WORK. The user tracks your progress through todos.`;
|
|
122105
122761
|
}
|
|
122106
122762
|
// src/agents/sisyphus-junior/agent.ts
|
|
122763
|
+
init_types();
|
|
122107
122764
|
var MODE11 = "subagent";
|
|
122108
122765
|
var BLOCKED_TOOLS3 = ["task"];
|
|
122109
122766
|
var GPT_BLOCKED_TOOLS = ["task", "apply_patch"];
|
|
@@ -124906,6 +125563,7 @@ function getGeminiPrometheusPrompt() {
|
|
|
124906
125563
|
}
|
|
124907
125564
|
|
|
124908
125565
|
// src/agents/prometheus/system-prompt.ts
|
|
125566
|
+
init_types();
|
|
124909
125567
|
var PROMETHEUS_SYSTEM_PROMPT = `${PROMETHEUS_IDENTITY_CONSTRAINTS}
|
|
124910
125568
|
${PROMETHEUS_INTERVIEW_MODE}
|
|
124911
125569
|
${PROMETHEUS_PLAN_GENERATION}
|
|
@@ -125801,6 +126459,7 @@ function createManagers(args) {
|
|
|
125801
126459
|
deps.markServerRunningInProcessFn();
|
|
125802
126460
|
}
|
|
125803
126461
|
const tmuxSessionManager = new deps.TmuxSessionManagerClass(ctx, tmuxConfig);
|
|
126462
|
+
const modelFallbackControllerAccessor = createModelFallbackControllerAccessor();
|
|
125804
126463
|
deps.registerManagerForCleanupFn({
|
|
125805
126464
|
shutdown: async () => {
|
|
125806
126465
|
await tmuxSessionManager.cleanup().catch((error) => {
|
|
@@ -125844,7 +126503,8 @@ function createManagers(args) {
|
|
|
125844
126503
|
log("[create-managers] tmux cleanup error during shutdown:", error);
|
|
125845
126504
|
});
|
|
125846
126505
|
},
|
|
125847
|
-
enableParentSessionNotifications: backgroundNotificationHookEnabled
|
|
126506
|
+
enableParentSessionNotifications: backgroundNotificationHookEnabled,
|
|
126507
|
+
modelFallbackControllerAccessor
|
|
125848
126508
|
});
|
|
125849
126509
|
deps.initTaskToastManagerFn(ctx.client);
|
|
125850
126510
|
const skillMcpManager = new deps.SkillMcpManagerClass;
|
|
@@ -125853,7 +126513,6 @@ function createManagers(args) {
|
|
|
125853
126513
|
pluginConfig,
|
|
125854
126514
|
modelCacheState
|
|
125855
126515
|
});
|
|
125856
|
-
const modelFallbackControllerAccessor = createModelFallbackControllerAccessor();
|
|
125857
126516
|
return {
|
|
125858
126517
|
tmuxSessionManager,
|
|
125859
126518
|
backgroundManager,
|
|
@@ -126994,15 +127653,13 @@ function extractErrorMessage3(error) {
|
|
|
126994
127653
|
return "";
|
|
126995
127654
|
if (typeof error === "string")
|
|
126996
127655
|
return error;
|
|
126997
|
-
if (error instanceof Error)
|
|
126998
|
-
return error.message;
|
|
126999
127656
|
if (isRecord19(error)) {
|
|
127000
127657
|
const candidates = [
|
|
127001
|
-
error,
|
|
127002
127658
|
error.data,
|
|
127003
|
-
error.error,
|
|
127004
127659
|
isRecord19(error.data) ? error.data.error : undefined,
|
|
127005
|
-
error.
|
|
127660
|
+
error.error,
|
|
127661
|
+
error.cause,
|
|
127662
|
+
error
|
|
127006
127663
|
];
|
|
127007
127664
|
for (const candidate of candidates) {
|
|
127008
127665
|
if (isRecord19(candidate) && typeof candidate.message === "string" && candidate.message.length > 0) {
|
|
@@ -127010,6 +127667,8 @@ function extractErrorMessage3(error) {
|
|
|
127010
127667
|
}
|
|
127011
127668
|
}
|
|
127012
127669
|
}
|
|
127670
|
+
if (error instanceof Error)
|
|
127671
|
+
return error.message;
|
|
127013
127672
|
try {
|
|
127014
127673
|
return JSON.stringify(error);
|
|
127015
127674
|
} catch {
|
|
@@ -127299,6 +127958,9 @@ function createEventHandler2(args) {
|
|
|
127299
127958
|
const sessionID = info?.sessionID;
|
|
127300
127959
|
const agent = info?.agent;
|
|
127301
127960
|
const role = info?.role;
|
|
127961
|
+
if (sessionID && info?.finish === true) {
|
|
127962
|
+
invalidateContextWindowUsageCache(pluginContext, sessionID);
|
|
127963
|
+
}
|
|
127302
127964
|
if (sessionID && role === "user") {
|
|
127303
127965
|
const isCompactionMessage2 = agent ? isCompactionAgent5(agent) : false;
|
|
127304
127966
|
if (agent && !isCompactionMessage2) {
|
|
@@ -132595,7 +133257,7 @@ class PostHog extends PostHogBackendClient {
|
|
|
132595
133257
|
// package.json
|
|
132596
133258
|
var package_default = {
|
|
132597
133259
|
name: "oh-my-opencode",
|
|
132598
|
-
version: "3.17.
|
|
133260
|
+
version: "3.17.7",
|
|
132599
133261
|
description: "The Best AI Agent Harness - Batteries-Included OpenCode Plugin with Multi-Model Orchestration, Parallel Background Agents, and Crafted LSP/AST Tools",
|
|
132600
133262
|
main: "./dist/index.js",
|
|
132601
133263
|
types: "dist/index.d.ts",
|
|
@@ -132675,17 +133337,17 @@ var package_default = {
|
|
|
132675
133337
|
zod: "^4.3.0"
|
|
132676
133338
|
},
|
|
132677
133339
|
optionalDependencies: {
|
|
132678
|
-
"oh-my-opencode-darwin-arm64": "3.17.
|
|
132679
|
-
"oh-my-opencode-darwin-x64": "3.17.
|
|
132680
|
-
"oh-my-opencode-darwin-x64-baseline": "3.17.
|
|
132681
|
-
"oh-my-opencode-linux-arm64": "3.17.
|
|
132682
|
-
"oh-my-opencode-linux-arm64-musl": "3.17.
|
|
132683
|
-
"oh-my-opencode-linux-x64": "3.17.
|
|
132684
|
-
"oh-my-opencode-linux-x64-baseline": "3.17.
|
|
132685
|
-
"oh-my-opencode-linux-x64-musl": "3.17.
|
|
132686
|
-
"oh-my-opencode-linux-x64-musl-baseline": "3.17.
|
|
132687
|
-
"oh-my-opencode-windows-x64": "3.17.
|
|
132688
|
-
"oh-my-opencode-windows-x64-baseline": "3.17.
|
|
133340
|
+
"oh-my-opencode-darwin-arm64": "3.17.7",
|
|
133341
|
+
"oh-my-opencode-darwin-x64": "3.17.7",
|
|
133342
|
+
"oh-my-opencode-darwin-x64-baseline": "3.17.7",
|
|
133343
|
+
"oh-my-opencode-linux-arm64": "3.17.7",
|
|
133344
|
+
"oh-my-opencode-linux-arm64-musl": "3.17.7",
|
|
133345
|
+
"oh-my-opencode-linux-x64": "3.17.7",
|
|
133346
|
+
"oh-my-opencode-linux-x64-baseline": "3.17.7",
|
|
133347
|
+
"oh-my-opencode-linux-x64-musl": "3.17.7",
|
|
133348
|
+
"oh-my-opencode-linux-x64-musl-baseline": "3.17.7",
|
|
133349
|
+
"oh-my-opencode-windows-x64": "3.17.7",
|
|
133350
|
+
"oh-my-opencode-windows-x64-baseline": "3.17.7"
|
|
132689
133351
|
},
|
|
132690
133352
|
overrides: {},
|
|
132691
133353
|
trustedDependencies: [
|