oh-my-opencode-slim 1.1.1 → 1.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ja-JP.md +638 -0
- package/README.ko-KR.md +634 -0
- package/README.md +44 -13
- package/README.zh-CN.md +627 -0
- package/dist/cli/background-subagents.d.ts +13 -0
- package/dist/cli/index.js +65 -92
- package/dist/cli/skills.d.ts +2 -30
- package/dist/cli/types.d.ts +0 -1
- package/dist/config/fallback-chains.d.ts +1 -0
- package/dist/config/schema.d.ts +7 -0
- package/dist/hooks/auto-update-checker/checker.d.ts +7 -1
- package/dist/hooks/auto-update-checker/constants.d.ts +1 -0
- package/dist/hooks/auto-update-checker/types.d.ts +10 -0
- package/dist/hooks/deepwork/index.d.ts +13 -0
- package/dist/index.js +300 -121
- package/dist/tools/ast-grep/tools.d.ts +1 -1
- package/dist/tools/cancel-task.d.ts +16 -0
- package/dist/tools/preset-manager.d.ts +6 -7
- package/dist/tools/subtask/tools.d.ts +6 -1
- package/dist/tui.js +17 -62
- package/dist/utils/background-job-board.d.ts +90 -0
- package/oh-my-opencode-slim.schema.json +11 -0
- package/package.json +6 -3
- package/dist/agents/council-master.d.ts +0 -2
- package/dist/background/background-manager.d.ts +0 -203
- package/dist/background/index.d.ts +0 -3
- package/dist/background/multiplexer-session-manager.d.ts +0 -70
- package/dist/background/subagent-depth.d.ts +0 -35
- package/dist/cli/divoom.d.ts +0 -23
- package/dist/goal/index.d.ts +0 -3
- package/dist/goal/manager.d.ts +0 -41
- package/dist/goal/prompts.d.ts +0 -4
- package/dist/goal/store.d.ts +0 -15
- package/dist/goal/types.d.ts +0 -28
- package/dist/integrations/divoom/index.d.ts +0 -3
- package/dist/integrations/divoom/status-manager.d.ts +0 -31
- package/dist/integrations/divoom/swift-helper-source.d.ts +0 -1
- package/dist/integrations/divoom/swift-transport.d.ts +0 -26
- package/dist/integrations/divoom/types.d.ts +0 -41
- package/dist/tools/background.d.ts +0 -13
- package/dist/tools/fork/command.d.ts +0 -28
- package/dist/tools/fork/files.d.ts +0 -33
- package/dist/tools/fork/index.d.ts +0 -10
- package/dist/tools/fork/state.d.ts +0 -7
- package/dist/tools/fork/tools.d.ts +0 -23
- package/dist/tools/fork/vendor.d.ts +0 -28
- package/dist/tools/handoff/command.d.ts +0 -29
- package/dist/tools/handoff/files.d.ts +0 -33
- package/dist/tools/handoff/index.d.ts +0 -10
- package/dist/tools/handoff/state.d.ts +0 -7
- package/dist/tools/handoff/tools.d.ts +0 -23
- package/dist/tools/handoff/vendor.d.ts +0 -28
- package/dist/tools/lsp/client.d.ts +0 -81
- package/dist/tools/lsp/config-store.d.ts +0 -29
- package/dist/tools/lsp/config.d.ts +0 -5
- package/dist/tools/lsp/constants.d.ts +0 -24
- package/dist/tools/lsp/index.d.ts +0 -4
- package/dist/tools/lsp/tools.d.ts +0 -5
- package/dist/tools/lsp/types.d.ts +0 -45
- package/dist/tools/lsp/utils.d.ts +0 -34
- package/dist/utils/tmux-debug-log.d.ts +0 -2
package/dist/index.js
CHANGED
|
@@ -18196,6 +18196,13 @@ function getCustomOpenCodeConfigDir() {
|
|
|
18196
18196
|
const configDir = process.env.OPENCODE_CONFIG_DIR?.trim();
|
|
18197
18197
|
return configDir || undefined;
|
|
18198
18198
|
}
|
|
18199
|
+
function getConfigDir() {
|
|
18200
|
+
const customConfigDir = getCustomOpenCodeConfigDir();
|
|
18201
|
+
if (customConfigDir) {
|
|
18202
|
+
return customConfigDir;
|
|
18203
|
+
}
|
|
18204
|
+
return getDefaultOpenCodeConfigDir();
|
|
18205
|
+
}
|
|
18199
18206
|
function getConfigSearchDirs() {
|
|
18200
18207
|
const dirs = [getCustomOpenCodeConfigDir(), getDefaultOpenCodeConfigDir()];
|
|
18201
18208
|
return dirs.filter((dir, index) => {
|
|
@@ -18203,7 +18210,7 @@ function getConfigSearchDirs() {
|
|
|
18203
18210
|
});
|
|
18204
18211
|
}
|
|
18205
18212
|
function getOpenCodeConfigPaths() {
|
|
18206
|
-
const configDir =
|
|
18213
|
+
const configDir = getConfigDir();
|
|
18207
18214
|
return [join(configDir, "opencode.json"), join(configDir, "opencode.jsonc")];
|
|
18208
18215
|
}
|
|
18209
18216
|
|
|
@@ -18230,19 +18237,6 @@ var CUSTOM_SKILLS = [
|
|
|
18230
18237
|
];
|
|
18231
18238
|
|
|
18232
18239
|
// src/cli/skills.ts
|
|
18233
|
-
var RECOMMENDED_SKILLS = [
|
|
18234
|
-
{
|
|
18235
|
-
name: "agent-browser",
|
|
18236
|
-
repo: "https://github.com/vercel-labs/agent-browser",
|
|
18237
|
-
skillName: "agent-browser",
|
|
18238
|
-
allowedAgents: ["designer"],
|
|
18239
|
-
description: "High-performance browser automation",
|
|
18240
|
-
postInstallCommands: [
|
|
18241
|
-
"npm install -g agent-browser",
|
|
18242
|
-
"agent-browser install"
|
|
18243
|
-
]
|
|
18244
|
-
}
|
|
18245
|
-
];
|
|
18246
18240
|
var PERMISSION_ONLY_SKILLS = [
|
|
18247
18241
|
{
|
|
18248
18242
|
name: "requesting-code-review",
|
|
@@ -18267,12 +18261,6 @@ function getSkillPermissionsForAgent(agentName, skillList) {
|
|
|
18267
18261
|
}
|
|
18268
18262
|
return permissions;
|
|
18269
18263
|
}
|
|
18270
|
-
for (const skill of RECOMMENDED_SKILLS) {
|
|
18271
|
-
const isAllowed = skill.allowedAgents.includes("*") || skill.allowedAgents.includes(agentName);
|
|
18272
|
-
if (isAllowed) {
|
|
18273
|
-
permissions[skill.skillName] = "allow";
|
|
18274
|
-
}
|
|
18275
|
-
}
|
|
18276
18264
|
for (const skill of CUSTOM_SKILLS) {
|
|
18277
18265
|
const isAllowed = skill.allowedAgents.includes("*") || skill.allowedAgents.includes(agentName);
|
|
18278
18266
|
if (isAllowed) {
|
|
@@ -18603,6 +18591,9 @@ var TodoContinuationConfigSchema = z2.object({
|
|
|
18603
18591
|
autoEnable: z2.boolean().default(false).describe("Automatically enable auto-continue when the orchestrator session has enough todos"),
|
|
18604
18592
|
autoEnableThreshold: z2.number().int().min(1).max(50).default(4).describe("Number of todos that triggers auto-enable (only used when autoEnable is true)")
|
|
18605
18593
|
});
|
|
18594
|
+
var SubtaskConfigSchema = z2.object({
|
|
18595
|
+
timeoutMs: z2.number().int().min(0).max(24 * 60 * 60 * 1000).optional().describe("Subtask worker timeout in ms. 0 disables the timeout. Defaults to 300000 (5 minutes).")
|
|
18596
|
+
});
|
|
18606
18597
|
var FailoverConfigSchema = z2.object({
|
|
18607
18598
|
enabled: z2.boolean().default(true),
|
|
18608
18599
|
timeoutMs: z2.number().min(0).default(15000),
|
|
@@ -18650,6 +18641,7 @@ var PluginConfigSchema = z2.object({
|
|
|
18650
18641
|
sessionManager: SessionManagerConfigSchema.optional(),
|
|
18651
18642
|
divoom: DivoomConfigSchema.optional(),
|
|
18652
18643
|
todoContinuation: TodoContinuationConfigSchema.optional(),
|
|
18644
|
+
subtask: SubtaskConfigSchema.optional(),
|
|
18653
18645
|
fallback: FailoverConfigSchema.optional(),
|
|
18654
18646
|
council: CouncilConfigSchema.optional()
|
|
18655
18647
|
}).superRefine((value, ctx) => {
|
|
@@ -18670,7 +18662,9 @@ function loadConfigFromPath(configPath, options) {
|
|
|
18670
18662
|
const content = fs.readFileSync(configPath, "utf-8");
|
|
18671
18663
|
let rawConfig;
|
|
18672
18664
|
try {
|
|
18673
|
-
|
|
18665
|
+
const stripped = stripJsonComments(content);
|
|
18666
|
+
const interpolated = stripped.replace(/\{env:([^}]+)\}/g, (_, varName) => process.env[varName] ?? "");
|
|
18667
|
+
rawConfig = JSON.parse(interpolated);
|
|
18674
18668
|
} catch (error) {
|
|
18675
18669
|
const message = error instanceof Error ? error.message : String(error);
|
|
18676
18670
|
options?.onWarning?.({
|
|
@@ -18749,7 +18743,8 @@ function mergePluginConfigs(base, override) {
|
|
|
18749
18743
|
sessionManager: deepMerge(base.sessionManager, override.sessionManager),
|
|
18750
18744
|
divoom: deepMerge(base.divoom, override.divoom),
|
|
18751
18745
|
fallback: deepMerge(base.fallback, override.fallback),
|
|
18752
|
-
council: deepMerge(base.council, override.council)
|
|
18746
|
+
council: deepMerge(base.council, override.council),
|
|
18747
|
+
subtask: deepMerge(base.subtask, override.subtask)
|
|
18753
18748
|
};
|
|
18754
18749
|
}
|
|
18755
18750
|
function deepMerge(base, override) {
|
|
@@ -18901,34 +18896,32 @@ function parseModelReference(model) {
|
|
|
18901
18896
|
async function promptWithTimeout(client, args, timeoutMs, signal) {
|
|
18902
18897
|
if (signal?.aborted)
|
|
18903
18898
|
throw new Error("Prompt cancelled");
|
|
18904
|
-
if (timeoutMs <= 0) {
|
|
18905
|
-
await client.session.prompt(args);
|
|
18906
|
-
return;
|
|
18907
|
-
}
|
|
18908
18899
|
const sessionId = args.path.id;
|
|
18900
|
+
const hasTimeout = timeoutMs > 0;
|
|
18909
18901
|
let timer;
|
|
18910
18902
|
let onAbort;
|
|
18911
18903
|
try {
|
|
18912
18904
|
const promptPromise = client.session.prompt(args);
|
|
18913
18905
|
promptPromise.catch(() => {});
|
|
18914
|
-
|
|
18915
|
-
|
|
18916
|
-
new Promise((_, reject) => {
|
|
18906
|
+
const racers = [promptPromise];
|
|
18907
|
+
if (hasTimeout) {
|
|
18908
|
+
racers.push(new Promise((_, reject) => {
|
|
18917
18909
|
timer = setTimeout(() => {
|
|
18918
18910
|
reject(new OperationTimeoutError(`Prompt timed out after ${timeoutMs}ms`));
|
|
18919
18911
|
}, timeoutMs);
|
|
18920
|
-
})
|
|
18921
|
-
|
|
18922
|
-
|
|
18923
|
-
|
|
18912
|
+
}));
|
|
18913
|
+
}
|
|
18914
|
+
if (signal) {
|
|
18915
|
+
racers.push(new Promise((_, reject) => {
|
|
18924
18916
|
if (signal.aborted) {
|
|
18925
18917
|
reject(new Error("Prompt cancelled"));
|
|
18926
18918
|
return;
|
|
18927
18919
|
}
|
|
18928
18920
|
onAbort = () => reject(new Error("Prompt cancelled"));
|
|
18929
18921
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
18930
|
-
})
|
|
18931
|
-
|
|
18922
|
+
}));
|
|
18923
|
+
}
|
|
18924
|
+
await Promise.race(racers);
|
|
18932
18925
|
} catch (error) {
|
|
18933
18926
|
if (error instanceof OperationTimeoutError) {
|
|
18934
18927
|
try {
|
|
@@ -19012,7 +19005,7 @@ var AGENT_DESCRIPTIONS = {
|
|
|
19012
19005
|
- Permissions: Read/write files
|
|
19013
19006
|
- Stats: 2x faster code edits, 1/2 cost of orchestrator, 0.8x quality of orchestrator
|
|
19014
19007
|
- Tools/Constraints: Execution-focused—no research, no architectural decisions
|
|
19015
|
-
- **Delegate when:** For implementation work, think and triage first. If the change is non-trivial or multi-file, hand bounded execution to @fixer • Writing or updating tests • Tasks that touch test files, fixtures, mocks, or test helpers. Parallelization benefits: Task involves multiple folders and multiple files
|
|
19008
|
+
- **Delegate when:** For implementation work, think and triage first. If the change is non-trivial or multi-file, hand bounded execution to @fixer • Writing or updating tests • Tasks that touch test files, fixtures, mocks, or test helpers. Parallelization benefits: Task involves multiple folders and multiple files modification, scoping work per folder and spawning parallel @fixers for each folder.
|
|
19016
19009
|
- **Don't delegate when:** Needs discovery/research/decisions • Single small change (<20 lines, one file) • Unclear requirements needing iteration • Explaining to fixer > doing • Tight integration with your current work • Sequential dependencies
|
|
19017
19010
|
- **Rule of thumb:** Explaining > doing? → yourself. Test file modifications and bounded implementation work usually go to @fixer. Bigger or lots of edits, splitting makes sense, parallelized by spawning @fixers per certain scope.`,
|
|
19018
19011
|
council: `@council
|
|
@@ -19135,7 +19128,7 @@ relevant files. Wait for the summary, then integrate and verify it.
|
|
|
19135
19128
|
5. Adjust if needed
|
|
19136
19129
|
|
|
19137
19130
|
### Session Reuse
|
|
19138
|
-
- Smartly reuse an available specialist session -
|
|
19131
|
+
- Smartly reuse an available specialist session - context reuse saves time and tokens
|
|
19139
19132
|
- When too much unrelated, and really needed, start a fresh session with the specialist
|
|
19140
19133
|
- If multiple remembered sessions fit, prefer the most recently used matching session.
|
|
19141
19134
|
- Prefer re-uses over creating new sessions all the time
|
|
@@ -19982,6 +19975,35 @@ function getDisabledAgents(config) {
|
|
|
19982
19975
|
return disabled;
|
|
19983
19976
|
}
|
|
19984
19977
|
|
|
19978
|
+
// src/config/fallback-chains.ts
|
|
19979
|
+
function normalizeFallbackChainsForPreset(chains, presetName) {
|
|
19980
|
+
const normalized = {};
|
|
19981
|
+
for (const [rawKey, chainModels] of Object.entries(chains)) {
|
|
19982
|
+
if (!chainModels?.length)
|
|
19983
|
+
continue;
|
|
19984
|
+
const separatorIndex = rawKey.indexOf(":");
|
|
19985
|
+
const hasPresetScope = separatorIndex !== -1;
|
|
19986
|
+
const scopedPreset = hasPresetScope ? rawKey.slice(0, separatorIndex) : "";
|
|
19987
|
+
const agentName = hasPresetScope ? rawKey.slice(separatorIndex + 1) : rawKey;
|
|
19988
|
+
if (!agentName)
|
|
19989
|
+
continue;
|
|
19990
|
+
if (hasPresetScope && scopedPreset !== presetName)
|
|
19991
|
+
continue;
|
|
19992
|
+
const existing = normalized[agentName] ?? [];
|
|
19993
|
+
const seen = new Set(existing);
|
|
19994
|
+
for (const chainModel of chainModels) {
|
|
19995
|
+
if (seen.has(chainModel))
|
|
19996
|
+
continue;
|
|
19997
|
+
seen.add(chainModel);
|
|
19998
|
+
existing.push(chainModel);
|
|
19999
|
+
}
|
|
20000
|
+
if (existing.length > 0) {
|
|
20001
|
+
normalized[agentName] = existing;
|
|
20002
|
+
}
|
|
20003
|
+
}
|
|
20004
|
+
return normalized;
|
|
20005
|
+
}
|
|
20006
|
+
|
|
19985
20007
|
// src/config/runtime-preset.ts
|
|
19986
20008
|
var activeRuntimePreset = null;
|
|
19987
20009
|
function setActiveRuntimePreset(name) {
|
|
@@ -19998,10 +20020,6 @@ function setActiveRuntimePresetWithPrevious(name) {
|
|
|
19998
20020
|
previousRuntimePreset = activeRuntimePreset;
|
|
19999
20021
|
activeRuntimePreset = name;
|
|
20000
20022
|
}
|
|
20001
|
-
function rollbackRuntimePreset(previous) {
|
|
20002
|
-
activeRuntimePreset = previous;
|
|
20003
|
-
previousRuntimePreset = null;
|
|
20004
|
-
}
|
|
20005
20023
|
|
|
20006
20024
|
// src/utils/logger.ts
|
|
20007
20025
|
import * as fs2 from "node:fs";
|
|
@@ -20014,7 +20032,7 @@ var RETENTION_MS = 7 * 24 * 60 * 60 * 1000;
|
|
|
20014
20032
|
var logFile = null;
|
|
20015
20033
|
var writeChain = Promise.resolve();
|
|
20016
20034
|
function getLogDir() {
|
|
20017
|
-
return process.env.OPENCODE_LOG_DIR ?? path2.join(os.homedir(), ".local/share/opencode");
|
|
20035
|
+
return process.env.OPENCODE_LOG_DIR ?? path2.join(os.homedir(), ".local/share/opencode/log");
|
|
20018
20036
|
}
|
|
20019
20037
|
function cleanupOldLogs(logDir) {
|
|
20020
20038
|
try {
|
|
@@ -22139,6 +22157,7 @@ import * as os3 from "node:os";
|
|
|
22139
22157
|
import * as path6 from "node:path";
|
|
22140
22158
|
var PACKAGE_NAME = "oh-my-opencode-slim";
|
|
22141
22159
|
var NPM_REGISTRY_URL = `https://registry.npmjs.org/-/package/${PACKAGE_NAME}/dist-tags`;
|
|
22160
|
+
var NPM_PACKAGE_URL = `https://registry.npmjs.org/${PACKAGE_NAME}`;
|
|
22142
22161
|
var NPM_FETCH_TIMEOUT = 5000;
|
|
22143
22162
|
function getCacheDir() {
|
|
22144
22163
|
if (process.platform === "win32") {
|
|
@@ -22165,6 +22184,83 @@ function isPrereleaseVersion(version) {
|
|
|
22165
22184
|
function isDistTag(version) {
|
|
22166
22185
|
return !/^\d/.test(version);
|
|
22167
22186
|
}
|
|
22187
|
+
function parseVersion(version) {
|
|
22188
|
+
const normalized = version.trim().replace(/^[~^=<>\s]+/, "");
|
|
22189
|
+
const match = normalized.match(/^(\d+)\.(\d+)\.(\d+)(?:-([\w.-]+))?/);
|
|
22190
|
+
if (!match)
|
|
22191
|
+
return null;
|
|
22192
|
+
return {
|
|
22193
|
+
major: Number(match[1]),
|
|
22194
|
+
minor: Number(match[2]),
|
|
22195
|
+
patch: Number(match[3]),
|
|
22196
|
+
prerelease: match[4] ?? null
|
|
22197
|
+
};
|
|
22198
|
+
}
|
|
22199
|
+
function compareVersions(a, b) {
|
|
22200
|
+
const parsedA = parseVersion(a);
|
|
22201
|
+
const parsedB = parseVersion(b);
|
|
22202
|
+
if (!parsedA || !parsedB)
|
|
22203
|
+
return a.localeCompare(b);
|
|
22204
|
+
const parts = [
|
|
22205
|
+
"major",
|
|
22206
|
+
"minor",
|
|
22207
|
+
"patch"
|
|
22208
|
+
];
|
|
22209
|
+
for (const part of parts) {
|
|
22210
|
+
if (parsedA[part] !== parsedB[part]) {
|
|
22211
|
+
return parsedA[part] - parsedB[part];
|
|
22212
|
+
}
|
|
22213
|
+
}
|
|
22214
|
+
if (parsedA.prerelease === parsedB.prerelease)
|
|
22215
|
+
return 0;
|
|
22216
|
+
if (!parsedA.prerelease)
|
|
22217
|
+
return 1;
|
|
22218
|
+
if (!parsedB.prerelease)
|
|
22219
|
+
return -1;
|
|
22220
|
+
return comparePrerelease(parsedA.prerelease, parsedB.prerelease);
|
|
22221
|
+
}
|
|
22222
|
+
function comparePrerelease(a, b) {
|
|
22223
|
+
const segmentsA = a.split(".");
|
|
22224
|
+
const segmentsB = b.split(".");
|
|
22225
|
+
const length = Math.max(segmentsA.length, segmentsB.length);
|
|
22226
|
+
for (let i = 0;i < length; i++) {
|
|
22227
|
+
const segmentA = segmentsA[i];
|
|
22228
|
+
const segmentB = segmentsB[i];
|
|
22229
|
+
if (segmentA === segmentB)
|
|
22230
|
+
continue;
|
|
22231
|
+
if (segmentA === undefined)
|
|
22232
|
+
return -1;
|
|
22233
|
+
if (segmentB === undefined)
|
|
22234
|
+
return 1;
|
|
22235
|
+
const numberA = Number(segmentA);
|
|
22236
|
+
const numberB = Number(segmentB);
|
|
22237
|
+
const numericA = Number.isInteger(numberA);
|
|
22238
|
+
const numericB = Number.isInteger(numberB);
|
|
22239
|
+
if (numericA && numericB)
|
|
22240
|
+
return numberA - numberB;
|
|
22241
|
+
if (numericA)
|
|
22242
|
+
return -1;
|
|
22243
|
+
if (numericB)
|
|
22244
|
+
return 1;
|
|
22245
|
+
const comparison = segmentA.localeCompare(segmentB);
|
|
22246
|
+
if (comparison !== 0)
|
|
22247
|
+
return comparison;
|
|
22248
|
+
}
|
|
22249
|
+
return 0;
|
|
22250
|
+
}
|
|
22251
|
+
function getPrereleaseChannel(version) {
|
|
22252
|
+
if (!version.prerelease)
|
|
22253
|
+
return null;
|
|
22254
|
+
return version.prerelease.split(".")[0] ?? null;
|
|
22255
|
+
}
|
|
22256
|
+
function isVersionInChannel(version, channel) {
|
|
22257
|
+
const parsed = parseVersion(version);
|
|
22258
|
+
if (!parsed)
|
|
22259
|
+
return false;
|
|
22260
|
+
if (channel === "latest")
|
|
22261
|
+
return parsed.prerelease === null;
|
|
22262
|
+
return getPrereleaseChannel(parsed) === channel;
|
|
22263
|
+
}
|
|
22168
22264
|
function extractChannel(version) {
|
|
22169
22265
|
if (!version)
|
|
22170
22266
|
return "latest";
|
|
@@ -22312,6 +22408,10 @@ function getCachedVersion() {
|
|
|
22312
22408
|
return null;
|
|
22313
22409
|
}
|
|
22314
22410
|
async function getLatestVersion(channel = "latest") {
|
|
22411
|
+
const distTags = await fetchDistTags();
|
|
22412
|
+
return distTags?.[channel] ?? distTags?.latest ?? null;
|
|
22413
|
+
}
|
|
22414
|
+
async function fetchDistTags() {
|
|
22315
22415
|
const controller = new AbortController;
|
|
22316
22416
|
const timeoutId = setTimeout(() => controller.abort(), NPM_FETCH_TIMEOUT);
|
|
22317
22417
|
try {
|
|
@@ -22322,13 +22422,84 @@ async function getLatestVersion(channel = "latest") {
|
|
|
22322
22422
|
if (!response.ok)
|
|
22323
22423
|
return null;
|
|
22324
22424
|
const data = await response.json();
|
|
22325
|
-
return data
|
|
22425
|
+
return data;
|
|
22326
22426
|
} catch {
|
|
22327
22427
|
return null;
|
|
22328
22428
|
} finally {
|
|
22329
22429
|
clearTimeout(timeoutId);
|
|
22330
22430
|
}
|
|
22331
22431
|
}
|
|
22432
|
+
async function getLatestCompatibleVersion(currentVersion, channel = "latest") {
|
|
22433
|
+
const current = parseVersion(currentVersion);
|
|
22434
|
+
if (!current) {
|
|
22435
|
+
const latestVersion = await getLatestVersion(channel);
|
|
22436
|
+
return {
|
|
22437
|
+
latestVersion: null,
|
|
22438
|
+
latestMajorVersion: latestVersion,
|
|
22439
|
+
blockedByMajor: latestVersion !== null,
|
|
22440
|
+
unsafeReason: latestVersion ? "unparseable-current-version" : undefined
|
|
22441
|
+
};
|
|
22442
|
+
}
|
|
22443
|
+
const controller = new AbortController;
|
|
22444
|
+
const timeoutId = setTimeout(() => controller.abort(), NPM_FETCH_TIMEOUT);
|
|
22445
|
+
try {
|
|
22446
|
+
const response = await fetch(NPM_PACKAGE_URL, {
|
|
22447
|
+
signal: controller.signal,
|
|
22448
|
+
headers: { Accept: "application/json" }
|
|
22449
|
+
});
|
|
22450
|
+
if (!response.ok)
|
|
22451
|
+
return await getCompatibleFromDistTags(current, channel);
|
|
22452
|
+
const data = await response.json();
|
|
22453
|
+
const distTags = data["dist-tags"] ?? { latest: "" };
|
|
22454
|
+
const taggedVersion = distTags[channel] ?? distTags.latest ?? null;
|
|
22455
|
+
const latestMajorVersion = getBlockingMajorVersion(current, [
|
|
22456
|
+
taggedVersion,
|
|
22457
|
+
distTags.latest
|
|
22458
|
+
]);
|
|
22459
|
+
const blockedByMajor = latestMajorVersion !== null;
|
|
22460
|
+
const versions = Object.keys(data.versions ?? {}).filter((version) => {
|
|
22461
|
+
const parsed = parseVersion(version);
|
|
22462
|
+
return parsed?.major === current.major && isVersionInChannel(version, channel);
|
|
22463
|
+
}).sort(compareVersions);
|
|
22464
|
+
const latestVersion = versions.at(-1) ?? null;
|
|
22465
|
+
return { latestVersion, latestMajorVersion, blockedByMajor };
|
|
22466
|
+
} catch {
|
|
22467
|
+
return await getCompatibleFromDistTags(current, channel);
|
|
22468
|
+
} finally {
|
|
22469
|
+
clearTimeout(timeoutId);
|
|
22470
|
+
}
|
|
22471
|
+
}
|
|
22472
|
+
async function getCompatibleFromDistTags(current, channel) {
|
|
22473
|
+
const distTags = await fetchDistTags();
|
|
22474
|
+
if (!distTags) {
|
|
22475
|
+
return {
|
|
22476
|
+
latestVersion: null,
|
|
22477
|
+
latestMajorVersion: null,
|
|
22478
|
+
blockedByMajor: false
|
|
22479
|
+
};
|
|
22480
|
+
}
|
|
22481
|
+
const latestVersion = distTags[channel] ?? distTags.latest ?? null;
|
|
22482
|
+
const latestMajorVersion = getBlockingMajorVersion(current, [
|
|
22483
|
+
latestVersion,
|
|
22484
|
+
distTags.latest
|
|
22485
|
+
]);
|
|
22486
|
+
const blockedByMajor = latestMajorVersion !== null;
|
|
22487
|
+
const parsedLatest = latestVersion ? parseVersion(latestVersion) : null;
|
|
22488
|
+
const compatibleLatestVersion = parsedLatest?.major === current.major && latestVersion && isVersionInChannel(latestVersion, channel) ? latestVersion : null;
|
|
22489
|
+
return {
|
|
22490
|
+
latestVersion: compatibleLatestVersion,
|
|
22491
|
+
latestMajorVersion,
|
|
22492
|
+
blockedByMajor
|
|
22493
|
+
};
|
|
22494
|
+
}
|
|
22495
|
+
function getBlockingMajorVersion(current, candidates) {
|
|
22496
|
+
for (const candidate of candidates) {
|
|
22497
|
+
const parsed = candidate ? parseVersion(candidate) : null;
|
|
22498
|
+
if (parsed && parsed.major > current.major)
|
|
22499
|
+
return candidate ?? null;
|
|
22500
|
+
}
|
|
22501
|
+
return null;
|
|
22502
|
+
}
|
|
22332
22503
|
|
|
22333
22504
|
// src/hooks/auto-update-checker/cache.ts
|
|
22334
22505
|
function removeFromBunLock(installDir, packageName) {
|
|
@@ -22472,7 +22643,20 @@ async function runBackgroundUpdateCheck(ctx, autoUpdate) {
|
|
|
22472
22643
|
return;
|
|
22473
22644
|
}
|
|
22474
22645
|
const channel = extractChannel(pluginInfo.pinnedVersion ?? currentVersion);
|
|
22475
|
-
const
|
|
22646
|
+
const latestInfo = await getLatestCompatibleVersion(currentVersion, channel);
|
|
22647
|
+
if (latestInfo.unsafeReason === "unparseable-current-version") {
|
|
22648
|
+
log(`[auto-update-checker] Current version is not semver; skipping auto-update: ${currentVersion}`);
|
|
22649
|
+
if (latestInfo.latestMajorVersion) {
|
|
22650
|
+
showToast(ctx, `OMO-Slim ${latestInfo.latestMajorVersion}`, `v${latestInfo.latestMajorVersion} available. Auto-update skipped because the current version could not be compared safely.`, "info", 8000);
|
|
22651
|
+
}
|
|
22652
|
+
return;
|
|
22653
|
+
}
|
|
22654
|
+
if (latestInfo.blockedByMajor && latestInfo.latestMajorVersion) {
|
|
22655
|
+
showMajorUpgradeToast(ctx, latestInfo.latestMajorVersion);
|
|
22656
|
+
log(`[auto-update-checker] Major update available; skipping auto-update: ${latestInfo.latestMajorVersion}`);
|
|
22657
|
+
return;
|
|
22658
|
+
}
|
|
22659
|
+
const latestVersion = latestInfo.latestVersion;
|
|
22476
22660
|
if (!latestVersion) {
|
|
22477
22661
|
log("[auto-update-checker] Failed to fetch latest version for channel:", channel);
|
|
22478
22662
|
return;
|
|
@@ -22509,6 +22693,10 @@ Restart OpenCode to apply.`, "success", 8000);
|
|
|
22509
22693
|
log("[auto-update-checker] bun install failed; update not installed");
|
|
22510
22694
|
}
|
|
22511
22695
|
}
|
|
22696
|
+
function showMajorUpgradeToast(ctx, version) {
|
|
22697
|
+
showToast(ctx, `oh-my-opencode-slim v${version} is available.`, `It requires OpenCode background subagents.
|
|
22698
|
+
Run: bunx oh-my-opencode-slim@latest install --background-subagents=yes`, "info", 12000);
|
|
22699
|
+
}
|
|
22512
22700
|
async function runBunInstallSafe(installDir) {
|
|
22513
22701
|
try {
|
|
22514
22702
|
const proc = crossSpawn(["bun", "install"], {
|
|
@@ -23194,7 +23382,7 @@ var RATE_LIMIT_PATTERNS = [
|
|
|
23194
23382
|
/usage limit/i,
|
|
23195
23383
|
/overloaded/i,
|
|
23196
23384
|
/resource.?exhausted/i,
|
|
23197
|
-
/insufficient.?quota/i,
|
|
23385
|
+
/insufficient.?(quota|balance)/i,
|
|
23198
23386
|
/high concurrency/i,
|
|
23199
23387
|
/reduce concurrency/i
|
|
23200
23388
|
];
|
|
@@ -23270,7 +23458,7 @@ class ForegroundFallbackManager {
|
|
|
23270
23458
|
if (!props?.sessionID || props.status?.type !== "retry")
|
|
23271
23459
|
break;
|
|
23272
23460
|
const msg = props.status.message?.toLowerCase() ?? "";
|
|
23273
|
-
if (msg.includes("rate limit") || msg.includes("usage limit") || msg.includes("usage exceeded") || msg.includes("quota exceeded") || msg.includes("exceededbudget") || msg.includes("over budget") || msg.includes("high concurrency") || msg.includes("reduce concurrency")) {
|
|
23461
|
+
if (msg.includes("rate limit") || msg.includes("usage limit") || msg.includes("usage exceeded") || msg.includes("quota exceeded") || msg.includes("exceededbudget") || msg.includes("over budget") || msg.includes("insufficient") || msg.includes("high concurrency") || msg.includes("reduce concurrency")) {
|
|
23274
23462
|
await this.tryFallback(props.sessionID);
|
|
23275
23463
|
}
|
|
23276
23464
|
break;
|
|
@@ -23677,14 +23865,13 @@ function createPhaseReminderHook() {
|
|
|
23677
23865
|
if (originalText.includes(SLIM_INTERNAL_INITIATOR_MARKER)) {
|
|
23678
23866
|
return;
|
|
23679
23867
|
}
|
|
23680
|
-
if (
|
|
23868
|
+
if (lastUserMessage.parts.some((p) => p.text?.includes(PHASE_REMINDER))) {
|
|
23681
23869
|
return;
|
|
23682
23870
|
}
|
|
23683
|
-
lastUserMessage.parts
|
|
23684
|
-
|
|
23685
|
-
|
|
23686
|
-
|
|
23687
|
-
${PHASE_REMINDER}`;
|
|
23871
|
+
lastUserMessage.parts.push({
|
|
23872
|
+
type: "text",
|
|
23873
|
+
text: PHASE_REMINDER
|
|
23874
|
+
});
|
|
23688
23875
|
}
|
|
23689
23876
|
};
|
|
23690
23877
|
}
|
|
@@ -24329,7 +24516,7 @@ function createTaskSessionManagerHook(_ctx, options) {
|
|
|
24329
24516
|
};
|
|
24330
24517
|
}
|
|
24331
24518
|
// src/hooks/todo-continuation/index.ts
|
|
24332
|
-
import { tool } from "@opencode-ai/plugin
|
|
24519
|
+
import { tool } from "@opencode-ai/plugin";
|
|
24333
24520
|
|
|
24334
24521
|
// src/hooks/todo-continuation/todo-hygiene.ts
|
|
24335
24522
|
var TODO_HYGIENE_REMINDER = "If the active task changed or finished, update the todo list to match the current work state.";
|
|
@@ -29439,7 +29626,8 @@ class MultiplexerSessionManager {
|
|
|
29439
29626
|
title,
|
|
29440
29627
|
directory,
|
|
29441
29628
|
createdAt: now,
|
|
29442
|
-
lastSeenAt: now
|
|
29629
|
+
lastSeenAt: now,
|
|
29630
|
+
seenInStatus: false
|
|
29443
29631
|
});
|
|
29444
29632
|
log("[multiplexer-session-manager] pane spawned", {
|
|
29445
29633
|
instanceId: this.instanceId,
|
|
@@ -29531,11 +29719,12 @@ class MultiplexerSessionManager {
|
|
|
29531
29719
|
const isIdle = status?.type === "idle";
|
|
29532
29720
|
if (status) {
|
|
29533
29721
|
tracked.lastSeenAt = now;
|
|
29722
|
+
tracked.seenInStatus = true;
|
|
29534
29723
|
tracked.missingSince = undefined;
|
|
29535
29724
|
} else if (!tracked.missingSince) {
|
|
29536
29725
|
tracked.missingSince = now;
|
|
29537
29726
|
}
|
|
29538
|
-
const missingTooLong = !!tracked.missingSince && now - tracked.missingSince >= SESSION_MISSING_GRACE_MS;
|
|
29727
|
+
const missingTooLong = tracked.seenInStatus && !!tracked.missingSince && now - tracked.missingSince >= SESSION_MISSING_GRACE_MS;
|
|
29539
29728
|
const isTimedOut = now - tracked.createdAt > SESSION_TIMEOUT_MS;
|
|
29540
29729
|
if (isIdle || missingTooLong || isTimedOut) {
|
|
29541
29730
|
sessionsToClose.push({
|
|
@@ -29657,7 +29846,8 @@ class MultiplexerSessionManager {
|
|
|
29657
29846
|
title: known.title,
|
|
29658
29847
|
directory: known.directory,
|
|
29659
29848
|
createdAt: now,
|
|
29660
|
-
lastSeenAt: now
|
|
29849
|
+
lastSeenAt: now,
|
|
29850
|
+
seenInStatus: false
|
|
29661
29851
|
});
|
|
29662
29852
|
log("[multiplexer-session-manager] pane respawned on busy", {
|
|
29663
29853
|
instanceId: this.instanceId,
|
|
@@ -29727,7 +29917,7 @@ async function isServerRunning(serverUrl, timeoutMs = 3000, maxAttempts = 2) {
|
|
|
29727
29917
|
return false;
|
|
29728
29918
|
}
|
|
29729
29919
|
// src/tools/ast-grep/tools.ts
|
|
29730
|
-
import { tool as tool2 } from "@opencode-ai/plugin
|
|
29920
|
+
import { tool as tool2 } from "@opencode-ai/plugin";
|
|
29731
29921
|
|
|
29732
29922
|
// src/tools/ast-grep/cli.ts
|
|
29733
29923
|
import { existsSync as existsSync9 } from "node:fs";
|
|
@@ -30338,6 +30528,9 @@ Returns the councillor responses with a summary footer.`,
|
|
|
30338
30528
|
});
|
|
30339
30529
|
return { council_session };
|
|
30340
30530
|
}
|
|
30531
|
+
// src/tools/preset-manager.ts
|
|
30532
|
+
import * as fs11 from "node:fs";
|
|
30533
|
+
|
|
30341
30534
|
// src/tui-state.ts
|
|
30342
30535
|
import * as fs10 from "node:fs";
|
|
30343
30536
|
import * as os5 from "node:os";
|
|
@@ -30456,64 +30649,47 @@ function createPresetManager(ctx, config) {
|
|
|
30456
30649
|
agentUpdates[resolvedName] = agentConfig;
|
|
30457
30650
|
}
|
|
30458
30651
|
}
|
|
30459
|
-
const currentRuntimePreset = getActiveRuntimePreset();
|
|
30460
|
-
const resetUpdates = {};
|
|
30461
|
-
if (currentRuntimePreset && config.presets?.[currentRuntimePreset]) {
|
|
30462
|
-
const oldPreset = config.presets[currentRuntimePreset];
|
|
30463
|
-
for (const rawName of Object.keys(oldPreset)) {
|
|
30464
|
-
const resolvedOld = AGENT_ALIASES[rawName] ?? rawName;
|
|
30465
|
-
if (resolvedOld in agentUpdates)
|
|
30466
|
-
continue;
|
|
30467
|
-
const baseline = config.agents?.[resolvedOld];
|
|
30468
|
-
if (baseline) {
|
|
30469
|
-
resetUpdates[resolvedOld] = mapOverrideToAgentConfig(baseline);
|
|
30470
|
-
}
|
|
30471
|
-
}
|
|
30472
|
-
}
|
|
30473
30652
|
const hasAgentUpdates = Object.keys(agentUpdates).length > 0;
|
|
30474
|
-
const allUpdates = { ...resetUpdates, ...agentUpdates };
|
|
30475
30653
|
if (!hasAgentUpdates) {
|
|
30476
30654
|
output.parts.push(createInternalAgentTextPart(`Preset "${presetName}" is empty (no agent overrides defined).`));
|
|
30477
30655
|
return;
|
|
30478
30656
|
}
|
|
30479
|
-
const previousPreset = activePreset;
|
|
30480
30657
|
setActiveRuntimePresetWithPrevious(presetName);
|
|
30481
30658
|
try {
|
|
30482
|
-
|
|
30483
|
-
|
|
30484
|
-
|
|
30485
|
-
|
|
30486
|
-
|
|
30487
|
-
|
|
30488
|
-
|
|
30489
|
-
|
|
30490
|
-
|
|
30491
|
-
|
|
30492
|
-
|
|
30493
|
-
|
|
30494
|
-
|
|
30495
|
-
|
|
30496
|
-
|
|
30497
|
-
|
|
30498
|
-
|
|
30499
|
-
|
|
30500
|
-
|
|
30501
|
-
|
|
30502
|
-
|
|
30503
|
-
|
|
30504
|
-
|
|
30505
|
-
|
|
30506
|
-
|
|
30507
|
-
if (
|
|
30508
|
-
|
|
30509
|
-
|
|
30510
|
-
|
|
30659
|
+
const { userConfigPath } = findPluginConfigPaths(ctx.directory);
|
|
30660
|
+
if (userConfigPath) {
|
|
30661
|
+
const raw = fs11.readFileSync(userConfigPath, "utf-8");
|
|
30662
|
+
const persisted = JSON.parse(stripJsonComments(raw));
|
|
30663
|
+
persisted.preset = presetName;
|
|
30664
|
+
fs11.writeFileSync(userConfigPath, `${JSON.stringify(persisted, null, 2)}
|
|
30665
|
+
`);
|
|
30666
|
+
}
|
|
30667
|
+
} catch {}
|
|
30668
|
+
const snapshot = readTuiSnapshot();
|
|
30669
|
+
const agentModels = { ...snapshot.agentModels };
|
|
30670
|
+
for (const [agentName, agentConfig] of Object.entries(agentUpdates)) {
|
|
30671
|
+
if (typeof agentConfig.model === "string") {
|
|
30672
|
+
agentModels[agentName] = agentConfig.model;
|
|
30673
|
+
}
|
|
30674
|
+
}
|
|
30675
|
+
recordTuiAgentModels({ agentModels });
|
|
30676
|
+
activePreset = presetName;
|
|
30677
|
+
const summaryParts = [];
|
|
30678
|
+
for (const [name, cfg] of Object.entries(agentUpdates)) {
|
|
30679
|
+
const parts = [name];
|
|
30680
|
+
if (cfg.model)
|
|
30681
|
+
parts.push(`model: ${cfg.model}`);
|
|
30682
|
+
if (cfg.variant)
|
|
30683
|
+
parts.push(`variant: ${cfg.variant}`);
|
|
30684
|
+
if (cfg.temperature !== undefined)
|
|
30685
|
+
parts.push(`temp: ${cfg.temperature}`);
|
|
30686
|
+
if (cfg.options)
|
|
30687
|
+
parts.push("options: yes");
|
|
30688
|
+
summaryParts.push(parts.join(" → "));
|
|
30689
|
+
}
|
|
30690
|
+
output.parts.push(createInternalAgentTextPart(`Saved preset "${presetName}". Restart or reload OpenCode to apply it to agent configuration. The current session was not reloaded to avoid interrupting the active conversation.
|
|
30511
30691
|
${summaryParts.join(`
|
|
30512
30692
|
`)}`));
|
|
30513
|
-
} catch (err) {
|
|
30514
|
-
rollbackRuntimePreset(previousPreset);
|
|
30515
|
-
output.parts.push(createInternalAgentTextPart(`Failed to switch preset "${presetName}": ${String(err)}`));
|
|
30516
|
-
}
|
|
30517
30693
|
}
|
|
30518
30694
|
function mapOverrideToAgentConfig(override) {
|
|
30519
30695
|
const agentConfig = {};
|
|
@@ -32921,11 +33097,11 @@ function createSubtaskCommandManager(_ctx, state) {
|
|
|
32921
33097
|
};
|
|
32922
33098
|
}
|
|
32923
33099
|
// src/tools/subtask/files.ts
|
|
32924
|
-
import * as
|
|
33100
|
+
import * as fs13 from "node:fs/promises";
|
|
32925
33101
|
import * as path20 from "node:path";
|
|
32926
33102
|
|
|
32927
33103
|
// src/tools/subtask/vendor.ts
|
|
32928
|
-
import * as
|
|
33104
|
+
import * as fs12 from "node:fs/promises";
|
|
32929
33105
|
import * as path19 from "node:path";
|
|
32930
33106
|
var DEFAULT_READ_LIMIT = 2000;
|
|
32931
33107
|
var MAX_LINE_LENGTH = 2000;
|
|
@@ -32969,7 +33145,7 @@ async function isBinaryFile(filepath) {
|
|
|
32969
33145
|
return true;
|
|
32970
33146
|
}
|
|
32971
33147
|
try {
|
|
32972
|
-
const file = await
|
|
33148
|
+
const file = await fs12.open(filepath, "r");
|
|
32973
33149
|
try {
|
|
32974
33150
|
const buffer = Buffer.alloc(SAMPLE_BYTES);
|
|
32975
33151
|
const result = await file.read(buffer, 0, SAMPLE_BYTES, 0);
|
|
@@ -33055,24 +33231,24 @@ function parseFileReferences(text) {
|
|
|
33055
33231
|
}
|
|
33056
33232
|
async function buildSyntheticFileParts(directory, refs) {
|
|
33057
33233
|
const parts = [];
|
|
33058
|
-
const realDirectory = await
|
|
33234
|
+
const realDirectory = await fs13.realpath(directory);
|
|
33059
33235
|
for (const ref of refs) {
|
|
33060
33236
|
const filepath = path20.resolve(directory, ref);
|
|
33061
33237
|
const relative3 = path20.relative(directory, filepath);
|
|
33062
33238
|
if (relative3.startsWith("..") || path20.isAbsolute(relative3))
|
|
33063
33239
|
continue;
|
|
33064
33240
|
try {
|
|
33065
|
-
const realFilepath = await
|
|
33241
|
+
const realFilepath = await fs13.realpath(filepath);
|
|
33066
33242
|
const realRelative = path20.relative(realDirectory, realFilepath);
|
|
33067
33243
|
if (realRelative.startsWith("..") || path20.isAbsolute(realRelative)) {
|
|
33068
33244
|
continue;
|
|
33069
33245
|
}
|
|
33070
|
-
const stats = await
|
|
33246
|
+
const stats = await fs13.stat(realFilepath);
|
|
33071
33247
|
if (!stats.isFile())
|
|
33072
33248
|
continue;
|
|
33073
33249
|
if (await isBinaryFile(realFilepath))
|
|
33074
33250
|
continue;
|
|
33075
|
-
const content = await
|
|
33251
|
+
const content = await fs13.readFile(realFilepath, "utf-8");
|
|
33076
33252
|
parts.push({
|
|
33077
33253
|
type: "text",
|
|
33078
33254
|
synthetic: true,
|
|
@@ -33107,7 +33283,7 @@ function createSubtaskState() {
|
|
|
33107
33283
|
}
|
|
33108
33284
|
// src/tools/subtask/tools.ts
|
|
33109
33285
|
import { tool as tool5 } from "@opencode-ai/plugin";
|
|
33110
|
-
var
|
|
33286
|
+
var DEFAULT_SUBTASK_TIMEOUT_MS = 5 * 60 * 1000;
|
|
33111
33287
|
var SUBTASK_SUMMARY_TAG_REGEX = /<\/?subtask_summary>/g;
|
|
33112
33288
|
function normalizeSubtaskSummary(text) {
|
|
33113
33289
|
return text.replace(SUBTASK_SUMMARY_TAG_REGEX, "").trim();
|
|
@@ -33119,8 +33295,9 @@ function getAbortSignal(context) {
|
|
|
33119
33295
|
const signal = context.abort;
|
|
33120
33296
|
return signal && typeof signal === "object" && "addEventListener" in signal && "removeEventListener" in signal && "aborted" in signal ? signal : undefined;
|
|
33121
33297
|
}
|
|
33122
|
-
function createSubtaskTool(ctx, state, depthTracker) {
|
|
33298
|
+
function createSubtaskTool(ctx, state, depthTracker, options = {}) {
|
|
33123
33299
|
const client = ctx.client;
|
|
33300
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_SUBTASK_TIMEOUT_MS;
|
|
33124
33301
|
return tool5({
|
|
33125
33302
|
description: "Run a child worker session and return its completion summary to the caller",
|
|
33126
33303
|
args: {
|
|
@@ -33218,7 +33395,7 @@ Risks / follow-up:
|
|
|
33218
33395
|
...await buildSyntheticFileParts(directory, files)
|
|
33219
33396
|
]
|
|
33220
33397
|
}
|
|
33221
|
-
},
|
|
33398
|
+
}, timeoutMs, abortSignal);
|
|
33222
33399
|
const extraction = await extractSessionResult(client, childSessionID, {
|
|
33223
33400
|
directory,
|
|
33224
33401
|
includeReasoning: false
|
|
@@ -33471,11 +33648,10 @@ var OhMyOpenCodeLite = async (ctx) => {
|
|
|
33471
33648
|
runtimeChains[agentDef.name] = agentDef._modelArray.map((m) => m.id);
|
|
33472
33649
|
}
|
|
33473
33650
|
}
|
|
33651
|
+
const activePresetForFallback = getActiveRuntimePreset() ?? config.preset ?? null;
|
|
33474
33652
|
if (config.fallback?.enabled !== false) {
|
|
33475
|
-
const chains = config.fallback?.chains ?? {};
|
|
33653
|
+
const chains = normalizeFallbackChainsForPreset(config.fallback?.chains ?? {}, activePresetForFallback);
|
|
33476
33654
|
for (const [agentName, chainModels] of Object.entries(chains)) {
|
|
33477
|
-
if (!chainModels?.length)
|
|
33478
|
-
continue;
|
|
33479
33655
|
const existing = runtimeChains[agentName] ?? [];
|
|
33480
33656
|
const seen = new Set(existing);
|
|
33481
33657
|
for (const m of chainModels) {
|
|
@@ -33587,7 +33763,9 @@ var OhMyOpenCodeLite = async (ctx) => {
|
|
|
33587
33763
|
...todoContinuationHook.tool,
|
|
33588
33764
|
ast_grep_search,
|
|
33589
33765
|
ast_grep_replace,
|
|
33590
|
-
subtask: createSubtaskTool(ctx, subtaskState, depthTracker
|
|
33766
|
+
subtask: createSubtaskTool(ctx, subtaskState, depthTracker, {
|
|
33767
|
+
timeoutMs: config.subtask?.timeoutMs
|
|
33768
|
+
}),
|
|
33591
33769
|
read_session: createReadSessionTool(ctx.client, subtaskState)
|
|
33592
33770
|
},
|
|
33593
33771
|
mcp: mcps,
|
|
@@ -33613,8 +33791,9 @@ var OhMyOpenCodeLite = async (ctx) => {
|
|
|
33613
33791
|
}
|
|
33614
33792
|
}
|
|
33615
33793
|
const configAgent = opencodeConfig.agent;
|
|
33794
|
+
const activePresetForFallback = getActiveRuntimePreset() ?? config.preset ?? null;
|
|
33616
33795
|
const fallbackChainsEnabled = config.fallback?.enabled !== false;
|
|
33617
|
-
const fallbackChains = fallbackChainsEnabled ? config.fallback?.chains ?? {} : {};
|
|
33796
|
+
const fallbackChains = fallbackChainsEnabled ? normalizeFallbackChainsForPreset(config.fallback?.chains ?? {}, activePresetForFallback) : {};
|
|
33618
33797
|
const effectiveArrays = {};
|
|
33619
33798
|
for (const [agentName, models] of Object.entries(modelArrayMap)) {
|
|
33620
33799
|
effectiveArrays[agentName] = [...models];
|