oh-my-opencode 3.15.1 → 3.15.3
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/agents/hephaestus/gpt-5-4.d.ts +22 -1
- package/dist/cli/index.js +477 -206
- package/dist/cli/run/continuation-state.d.ts +2 -1
- package/dist/create-runtime-tmux-config.d.ts +1 -0
- package/dist/features/background-agent/spawner.d.ts +3 -0
- package/dist/features/boulder-state/storage.d.ts +1 -1
- package/dist/features/boulder-state/types.d.ts +1 -0
- package/dist/features/claude-code-plugin-loader/discovery.d.ts +2 -1
- package/dist/features/claude-code-plugin-loader/loader.d.ts +1 -0
- package/dist/features/claude-code-plugin-loader/types.d.ts +10 -0
- package/dist/hooks/atlas/background-launch-session-tracking.d.ts +11 -0
- package/dist/hooks/atlas/boulder-continuation-injector.d.ts +2 -1
- package/dist/hooks/atlas/task-context.d.ts +7 -0
- package/dist/hooks/atlas/types.d.ts +1 -0
- package/dist/hooks/auto-update-checker/constants.d.ts +3 -3
- package/dist/index.js +1026 -681
- package/dist/plugin/tool-registry.d.ts +1 -0
- package/dist/shared/agent-display-names.d.ts +1 -0
- package/dist/tools/delegate-task/resolve-call-id.d.ts +2 -0
- package/package.json +12 -12
package/dist/index.js
CHANGED
|
@@ -17496,7 +17496,7 @@ function getOpenCodeConfigPaths(options) {
|
|
|
17496
17496
|
configJson: join6(configDir, "opencode.json"),
|
|
17497
17497
|
configJsonc: join6(configDir, "opencode.jsonc"),
|
|
17498
17498
|
packageJson: join6(configDir, "package.json"),
|
|
17499
|
-
omoConfig: join6(configDir,
|
|
17499
|
+
omoConfig: join6(configDir, `${CONFIG_BASENAME}.json`)
|
|
17500
17500
|
};
|
|
17501
17501
|
}
|
|
17502
17502
|
// src/shared/opencode-version.ts
|
|
@@ -60789,15 +60789,19 @@ function convertSDKMessageToStoredMessage(msg) {
|
|
|
60789
60789
|
async function findNearestMessageWithFieldsFromSDK(client, sessionID) {
|
|
60790
60790
|
try {
|
|
60791
60791
|
const response = await client.session.messages({ path: { id: sessionID } });
|
|
60792
|
-
const messages = normalizeSDKResponse(response, [], { preferResponseOnMissingData: true })
|
|
60793
|
-
|
|
60794
|
-
|
|
60792
|
+
const messages = normalizeSDKResponse(response, [], { preferResponseOnMissingData: true }).map((message) => ({
|
|
60793
|
+
stored: convertSDKMessageToStoredMessage(message),
|
|
60794
|
+
createdAt: message.info?.time?.created ?? Number.NEGATIVE_INFINITY,
|
|
60795
|
+
id: typeof message.id === "string" ? message.id : ""
|
|
60796
|
+
})).sort((left, right) => right.createdAt - left.createdAt || right.id.localeCompare(left.id));
|
|
60797
|
+
for (const message of messages) {
|
|
60798
|
+
const stored = message.stored;
|
|
60795
60799
|
if (stored?.agent && stored.model?.providerID && stored.model?.modelID) {
|
|
60796
60800
|
return stored;
|
|
60797
60801
|
}
|
|
60798
60802
|
}
|
|
60799
|
-
for (
|
|
60800
|
-
const stored =
|
|
60803
|
+
for (const message of messages) {
|
|
60804
|
+
const stored = message.stored;
|
|
60801
60805
|
if (stored?.agent || stored?.model?.providerID && stored?.model?.modelID) {
|
|
60802
60806
|
return stored;
|
|
60803
60807
|
}
|
|
@@ -60813,7 +60817,15 @@ async function findNearestMessageWithFieldsFromSDK(client, sessionID) {
|
|
|
60813
60817
|
async function findFirstMessageWithAgentFromSDK(client, sessionID) {
|
|
60814
60818
|
try {
|
|
60815
60819
|
const response = await client.session.messages({ path: { id: sessionID } });
|
|
60816
|
-
const messages = normalizeSDKResponse(response, [], { preferResponseOnMissingData: true })
|
|
60820
|
+
const messages = normalizeSDKResponse(response, [], { preferResponseOnMissingData: true }).sort((left, right) => {
|
|
60821
|
+
const leftTime = left.info?.time?.created ?? Number.POSITIVE_INFINITY;
|
|
60822
|
+
const rightTime = right.info?.time?.created ?? Number.POSITIVE_INFINITY;
|
|
60823
|
+
if (leftTime !== rightTime)
|
|
60824
|
+
return leftTime - rightTime;
|
|
60825
|
+
const leftId = typeof left.id === "string" ? left.id : "";
|
|
60826
|
+
const rightId = typeof right.id === "string" ? right.id : "";
|
|
60827
|
+
return leftId.localeCompare(rightId);
|
|
60828
|
+
});
|
|
60817
60829
|
for (const msg of messages) {
|
|
60818
60830
|
const stored = convertSDKMessageToStoredMessage(msg);
|
|
60819
60831
|
if (stored?.agent) {
|
|
@@ -60833,27 +60845,27 @@ function findNearestMessageWithFields(messageDir) {
|
|
|
60833
60845
|
return null;
|
|
60834
60846
|
}
|
|
60835
60847
|
try {
|
|
60836
|
-
const
|
|
60837
|
-
for (const file of files) {
|
|
60848
|
+
const messages = readdirSync(messageDir).filter((f) => f.endsWith(".json")).map((fileName) => {
|
|
60838
60849
|
try {
|
|
60839
|
-
const content = readFileSync6(join12(messageDir,
|
|
60850
|
+
const content = readFileSync6(join12(messageDir, fileName), "utf-8");
|
|
60840
60851
|
const msg = JSON.parse(content);
|
|
60841
|
-
|
|
60842
|
-
|
|
60843
|
-
|
|
60852
|
+
return {
|
|
60853
|
+
fileName,
|
|
60854
|
+
msg,
|
|
60855
|
+
createdAt: typeof msg.time?.created === "number" ? msg.time.created : Number.NEGATIVE_INFINITY
|
|
60856
|
+
};
|
|
60844
60857
|
} catch {
|
|
60845
|
-
|
|
60858
|
+
return null;
|
|
60859
|
+
}
|
|
60860
|
+
}).filter((entry) => entry !== null).sort((left, right) => right.createdAt - left.createdAt || right.fileName.localeCompare(left.fileName));
|
|
60861
|
+
for (const entry of messages) {
|
|
60862
|
+
if (entry.msg.agent && entry.msg.model?.providerID && entry.msg.model?.modelID) {
|
|
60863
|
+
return entry.msg;
|
|
60846
60864
|
}
|
|
60847
60865
|
}
|
|
60848
|
-
for (const
|
|
60849
|
-
|
|
60850
|
-
|
|
60851
|
-
const msg = JSON.parse(content);
|
|
60852
|
-
if (msg.agent || msg.model?.providerID && msg.model?.modelID) {
|
|
60853
|
-
return msg;
|
|
60854
|
-
}
|
|
60855
|
-
} catch {
|
|
60856
|
-
continue;
|
|
60866
|
+
for (const entry of messages) {
|
|
60867
|
+
if (entry.msg.agent || entry.msg.model?.providerID && entry.msg.model?.modelID) {
|
|
60868
|
+
return entry.msg;
|
|
60857
60869
|
}
|
|
60858
60870
|
}
|
|
60859
60871
|
} catch {
|
|
@@ -60866,16 +60878,22 @@ function findFirstMessageWithAgent(messageDir) {
|
|
|
60866
60878
|
return null;
|
|
60867
60879
|
}
|
|
60868
60880
|
try {
|
|
60869
|
-
const
|
|
60870
|
-
for (const file of files) {
|
|
60881
|
+
const messages = readdirSync(messageDir).filter((f) => f.endsWith(".json")).map((fileName) => {
|
|
60871
60882
|
try {
|
|
60872
|
-
const content = readFileSync6(join12(messageDir,
|
|
60883
|
+
const content = readFileSync6(join12(messageDir, fileName), "utf-8");
|
|
60873
60884
|
const msg = JSON.parse(content);
|
|
60874
|
-
|
|
60875
|
-
|
|
60876
|
-
|
|
60885
|
+
return {
|
|
60886
|
+
fileName,
|
|
60887
|
+
msg,
|
|
60888
|
+
createdAt: typeof msg.time?.created === "number" ? msg.time.created : Number.POSITIVE_INFINITY
|
|
60889
|
+
};
|
|
60877
60890
|
} catch {
|
|
60878
|
-
|
|
60891
|
+
return null;
|
|
60892
|
+
}
|
|
60893
|
+
}).filter((entry) => entry !== null).sort((left, right) => left.createdAt - right.createdAt || left.fileName.localeCompare(right.fileName));
|
|
60894
|
+
for (const entry of messages) {
|
|
60895
|
+
if (entry.msg.agent) {
|
|
60896
|
+
return entry.msg.agent;
|
|
60879
60897
|
}
|
|
60880
60898
|
}
|
|
60881
60899
|
} catch {
|
|
@@ -60983,7 +61001,7 @@ function getAgentConfigKey(agentName) {
|
|
|
60983
61001
|
return lower;
|
|
60984
61002
|
return lower;
|
|
60985
61003
|
}
|
|
60986
|
-
function
|
|
61004
|
+
function normalizeAgentForPromptKey(agentName) {
|
|
60987
61005
|
if (typeof agentName !== "string") {
|
|
60988
61006
|
return;
|
|
60989
61007
|
}
|
|
@@ -60994,10 +61012,10 @@ function normalizeAgentForPrompt(agentName) {
|
|
|
60994
61012
|
const lower = trimmed.toLowerCase();
|
|
60995
61013
|
const reversed = REVERSE_DISPLAY_NAMES[lower];
|
|
60996
61014
|
if (reversed !== undefined) {
|
|
60997
|
-
return
|
|
61015
|
+
return reversed;
|
|
60998
61016
|
}
|
|
60999
61017
|
if (AGENT_DISPLAY_NAMES[lower] !== undefined) {
|
|
61000
|
-
return
|
|
61018
|
+
return lower;
|
|
61001
61019
|
}
|
|
61002
61020
|
return trimmed;
|
|
61003
61021
|
}
|
|
@@ -62294,11 +62312,11 @@ function getPluginsBaseDir() {
|
|
|
62294
62312
|
}
|
|
62295
62313
|
return join18(homedir6(), ".claude", "plugins");
|
|
62296
62314
|
}
|
|
62297
|
-
function getInstalledPluginsPath() {
|
|
62298
|
-
return join18(getPluginsBaseDir(), "installed_plugins.json");
|
|
62315
|
+
function getInstalledPluginsPath(pluginsBaseDir) {
|
|
62316
|
+
return join18(pluginsBaseDir ?? getPluginsBaseDir(), "installed_plugins.json");
|
|
62299
62317
|
}
|
|
62300
|
-
function loadInstalledPlugins() {
|
|
62301
|
-
const dbPath = getInstalledPluginsPath();
|
|
62318
|
+
function loadInstalledPlugins(pluginsBaseDir) {
|
|
62319
|
+
const dbPath = getInstalledPluginsPath(pluginsBaseDir);
|
|
62302
62320
|
if (!existsSync14(dbPath)) {
|
|
62303
62321
|
return null;
|
|
62304
62322
|
}
|
|
@@ -62402,7 +62420,8 @@ function extractPluginEntries(db) {
|
|
|
62402
62420
|
return Object.entries(db.plugins).map(([key, installations]) => [key, installations[0]]);
|
|
62403
62421
|
}
|
|
62404
62422
|
function discoverInstalledPlugins(options) {
|
|
62405
|
-
const
|
|
62423
|
+
const pluginsBaseDir = options?.pluginsHomeOverride ?? getPluginsBaseDir();
|
|
62424
|
+
const db = loadInstalledPlugins(pluginsBaseDir);
|
|
62406
62425
|
const settings = loadClaudeSettings();
|
|
62407
62426
|
const plugins = [];
|
|
62408
62427
|
const errors = [];
|
|
@@ -62411,6 +62430,7 @@ function discoverInstalledPlugins(options) {
|
|
|
62411
62430
|
}
|
|
62412
62431
|
const settingsEnabledPlugins = settings?.enabledPlugins;
|
|
62413
62432
|
const overrideEnabledPlugins = options?.enabledPluginsOverride;
|
|
62433
|
+
const pluginManifestLoader = options?.loadPluginManifestOverride ?? loadPluginManifest;
|
|
62414
62434
|
for (const [pluginKey, installation] of extractPluginEntries(db)) {
|
|
62415
62435
|
if (!installation)
|
|
62416
62436
|
continue;
|
|
@@ -62427,7 +62447,7 @@ function discoverInstalledPlugins(options) {
|
|
|
62427
62447
|
});
|
|
62428
62448
|
continue;
|
|
62429
62449
|
}
|
|
62430
|
-
const manifest =
|
|
62450
|
+
const manifest = pluginManifestLoader(installPath);
|
|
62431
62451
|
const pluginName = manifest?.name || derivePluginNameFromKey(pluginKey);
|
|
62432
62452
|
const loadedPlugin = {
|
|
62433
62453
|
name: pluginName,
|
|
@@ -62928,11 +62948,21 @@ function loadPluginHooksConfigs(plugins) {
|
|
|
62928
62948
|
}
|
|
62929
62949
|
|
|
62930
62950
|
// src/features/claude-code-plugin-loader/loader.ts
|
|
62951
|
+
var cachedPluginComponentsByKey = new Map;
|
|
62952
|
+
function clonePluginComponentsResult(result) {
|
|
62953
|
+
return structuredClone(result);
|
|
62954
|
+
}
|
|
62931
62955
|
function isClaudeCodePluginsDisabled() {
|
|
62932
62956
|
const disableFlag = process.env.OPENCODE_DISABLE_CLAUDE_CODE;
|
|
62933
62957
|
const disablePluginsFlag = process.env.OPENCODE_DISABLE_CLAUDE_CODE_PLUGINS;
|
|
62934
62958
|
return disableFlag === "true" || disableFlag === "1" || disablePluginsFlag === "true" || disablePluginsFlag === "1";
|
|
62935
62959
|
}
|
|
62960
|
+
function getPluginComponentsCacheKey(options) {
|
|
62961
|
+
const overrideEntries = Object.entries(options?.enabledPluginsOverride ?? {}).sort(([leftKey], [rightKey]) => leftKey.localeCompare(rightKey));
|
|
62962
|
+
return JSON.stringify({
|
|
62963
|
+
enabledPluginsOverride: overrideEntries
|
|
62964
|
+
});
|
|
62965
|
+
}
|
|
62936
62966
|
async function loadAllPluginComponents(options) {
|
|
62937
62967
|
if (isClaudeCodePluginsDisabled()) {
|
|
62938
62968
|
log("Claude Code plugin loading disabled via OPENCODE_DISABLE_CLAUDE_CODE env var");
|
|
@@ -62946,6 +62976,11 @@ async function loadAllPluginComponents(options) {
|
|
|
62946
62976
|
errors: []
|
|
62947
62977
|
};
|
|
62948
62978
|
}
|
|
62979
|
+
const cacheKey = getPluginComponentsCacheKey(options);
|
|
62980
|
+
const cachedPluginComponents = cachedPluginComponentsByKey.get(cacheKey);
|
|
62981
|
+
if (cachedPluginComponents) {
|
|
62982
|
+
return clonePluginComponentsResult(cachedPluginComponents);
|
|
62983
|
+
}
|
|
62949
62984
|
const { plugins, errors } = discoverInstalledPlugins(options);
|
|
62950
62985
|
const [commands, skills, agents, mcpServers, hooksConfigs] = await Promise.all([
|
|
62951
62986
|
Promise.resolve(loadPluginCommands(plugins)),
|
|
@@ -62955,7 +62990,7 @@ async function loadAllPluginComponents(options) {
|
|
|
62955
62990
|
Promise.resolve(loadPluginHooksConfigs(plugins))
|
|
62956
62991
|
]);
|
|
62957
62992
|
log(`Loaded ${plugins.length} plugins with ${Object.keys(commands).length} commands, ${Object.keys(skills).length} skills, ${Object.keys(agents).length} agents, ${Object.keys(mcpServers).length} MCP servers`);
|
|
62958
|
-
|
|
62993
|
+
const result = {
|
|
62959
62994
|
commands,
|
|
62960
62995
|
skills,
|
|
62961
62996
|
agents,
|
|
@@ -62964,6 +62999,8 @@ async function loadAllPluginComponents(options) {
|
|
|
62964
62999
|
plugins,
|
|
62965
63000
|
errors
|
|
62966
63001
|
};
|
|
63002
|
+
cachedPluginComponentsByKey.set(cacheKey, clonePluginComponentsResult(result));
|
|
63003
|
+
return clonePluginComponentsResult(result);
|
|
62967
63004
|
}
|
|
62968
63005
|
// src/shared/plugin-command-discovery.ts
|
|
62969
63006
|
function discoverPluginCommandDefinitions(options) {
|
|
@@ -63478,11 +63515,12 @@ async function injectContinuation(args) {
|
|
|
63478
63515
|
} : undefined);
|
|
63479
63516
|
tools = tools ?? previousMessage?.tools;
|
|
63480
63517
|
}
|
|
63481
|
-
|
|
63518
|
+
const promptAgent = normalizeAgentForPromptKey(agentName);
|
|
63519
|
+
if (promptAgent && skipAgents.some((s) => getAgentConfigKey(s) === getAgentConfigKey(promptAgent))) {
|
|
63482
63520
|
log(`[${HOOK_NAME}] Skipped: agent in skipAgents list`, { sessionID, agent: agentName });
|
|
63483
63521
|
return;
|
|
63484
63522
|
}
|
|
63485
|
-
if (!
|
|
63523
|
+
if (!promptAgent) {
|
|
63486
63524
|
const compactionState = sessionStateStore.getExistingState(sessionID);
|
|
63487
63525
|
if (compactionState && isCompactionGuardActive(compactionState, Date.now())) {
|
|
63488
63526
|
log(`[${HOOK_NAME}] Skipped: agent unknown after compaction`, { sessionID });
|
|
@@ -63513,7 +63551,7 @@ ${todoList}`;
|
|
|
63513
63551
|
try {
|
|
63514
63552
|
log(`[${HOOK_NAME}] Injecting continuation`, {
|
|
63515
63553
|
sessionID,
|
|
63516
|
-
agent:
|
|
63554
|
+
agent: promptAgent,
|
|
63517
63555
|
model,
|
|
63518
63556
|
incompleteCount: freshIncompleteCount
|
|
63519
63557
|
});
|
|
@@ -63521,7 +63559,7 @@ ${todoList}`;
|
|
|
63521
63559
|
await ctx.client.session.promptAsync({
|
|
63522
63560
|
path: { id: sessionID },
|
|
63523
63561
|
body: {
|
|
63524
|
-
agent:
|
|
63562
|
+
agent: promptAgent,
|
|
63525
63563
|
...model !== undefined ? { model } : {},
|
|
63526
63564
|
...inheritedTools ? { tools: inheritedTools } : {},
|
|
63527
63565
|
parts: [createInternalAgentTextPart(prompt)]
|
|
@@ -82203,6 +82241,7 @@ var RETRYABLE_MESSAGE_PATTERNS = [
|
|
|
82203
82241
|
"over limit",
|
|
82204
82242
|
"overloaded",
|
|
82205
82243
|
"bad gateway",
|
|
82244
|
+
"bad request",
|
|
82206
82245
|
"unknown provider",
|
|
82207
82246
|
"provider not found",
|
|
82208
82247
|
"model_not_supported",
|
|
@@ -82529,13 +82568,15 @@ init_logger();
|
|
|
82529
82568
|
import { existsSync as existsSync39 } from "fs";
|
|
82530
82569
|
import { join as join42 } from "path";
|
|
82531
82570
|
var CONFIG_CACHE_TTL_MS2 = 30000;
|
|
82532
|
-
var USER_CONFIG_PATH = join42(getOpenCodeConfigDir({ binary: "opencode" }), "opencode-cc-plugin.json");
|
|
82533
82571
|
var configCache2 = new Map;
|
|
82572
|
+
function getUserConfigPath() {
|
|
82573
|
+
return join42(getOpenCodeConfigDir({ binary: "opencode" }), "opencode-cc-plugin.json");
|
|
82574
|
+
}
|
|
82534
82575
|
function getProjectConfigPath() {
|
|
82535
82576
|
return join42(process.cwd(), ".opencode", "opencode-cc-plugin.json");
|
|
82536
82577
|
}
|
|
82537
82578
|
function getCacheKey2() {
|
|
82538
|
-
return process.cwd()
|
|
82579
|
+
return `${process.cwd()}::${getUserConfigPath()}`;
|
|
82539
82580
|
}
|
|
82540
82581
|
function getCachedConfig2(cacheKey) {
|
|
82541
82582
|
const cachedEntry = configCache2.get(cacheKey);
|
|
@@ -82582,7 +82623,7 @@ async function loadPluginExtendedConfig() {
|
|
|
82582
82623
|
if (cachedConfig) {
|
|
82583
82624
|
return cachedConfig;
|
|
82584
82625
|
}
|
|
82585
|
-
const userConfig = await loadConfigFromPath(
|
|
82626
|
+
const userConfig = await loadConfigFromPath(getUserConfigPath());
|
|
82586
82627
|
const projectConfig = await loadConfigFromPath(getProjectConfigPath());
|
|
82587
82628
|
const merged = {
|
|
82588
82629
|
disabledHooks: mergeDisabledHooks(userConfig?.disabledHooks, projectConfig?.disabledHooks)
|
|
@@ -84427,26 +84468,33 @@ function getWindowsAppdataDir2() {
|
|
|
84427
84468
|
return null;
|
|
84428
84469
|
return process.env.APPDATA ?? path5.join(os4.homedir(), "AppData", "Roaming");
|
|
84429
84470
|
}
|
|
84430
|
-
|
|
84431
|
-
|
|
84432
|
-
|
|
84471
|
+
function getUserConfigDir() {
|
|
84472
|
+
return getOpenCodeConfigDir({ binary: "opencode" });
|
|
84473
|
+
}
|
|
84474
|
+
function getUserOpencodeConfig() {
|
|
84475
|
+
return path5.join(getUserConfigDir(), "opencode.json");
|
|
84476
|
+
}
|
|
84477
|
+
function getUserOpencodeConfigJsonc() {
|
|
84478
|
+
return path5.join(getUserConfigDir(), "opencode.jsonc");
|
|
84479
|
+
}
|
|
84433
84480
|
var INSTALLED_PACKAGE_JSON = path5.join(CACHE_DIR, "node_modules", PACKAGE_NAME, "package.json");
|
|
84434
84481
|
|
|
84435
84482
|
// src/hooks/auto-update-checker/checker/config-paths.ts
|
|
84436
84483
|
import * as os5 from "os";
|
|
84437
84484
|
import * as path6 from "path";
|
|
84438
84485
|
function getConfigPaths2(directory) {
|
|
84486
|
+
const userConfigDir = getUserConfigDir();
|
|
84439
84487
|
const paths = [
|
|
84440
84488
|
path6.join(directory, ".opencode", "opencode.json"),
|
|
84441
84489
|
path6.join(directory, ".opencode", "opencode.jsonc"),
|
|
84442
|
-
|
|
84443
|
-
|
|
84490
|
+
getUserOpencodeConfig(),
|
|
84491
|
+
getUserOpencodeConfigJsonc()
|
|
84444
84492
|
];
|
|
84445
84493
|
if (process.platform === "win32") {
|
|
84446
84494
|
const crossPlatformDir = path6.join(os5.homedir(), ".config");
|
|
84447
84495
|
const appdataDir = getWindowsAppdataDir2();
|
|
84448
84496
|
if (appdataDir) {
|
|
84449
|
-
const alternateDir =
|
|
84497
|
+
const alternateDir = userConfigDir === crossPlatformDir ? appdataDir : crossPlatformDir;
|
|
84450
84498
|
const alternateConfig = path6.join(alternateDir, "opencode", "opencode.json");
|
|
84451
84499
|
const alternateConfigJsonc = path6.join(alternateDir, "opencode", "opencode.jsonc");
|
|
84452
84500
|
if (!paths.includes(alternateConfig)) {
|
|
@@ -84937,8 +84985,9 @@ function removeFromBunLock(packageName) {
|
|
|
84937
84985
|
}
|
|
84938
84986
|
function invalidatePackage(packageName = PACKAGE_NAME) {
|
|
84939
84987
|
try {
|
|
84988
|
+
const userConfigDir = getUserConfigDir();
|
|
84940
84989
|
const pkgDirs = [
|
|
84941
|
-
path10.join(
|
|
84990
|
+
path10.join(userConfigDir, "node_modules", packageName),
|
|
84942
84991
|
path10.join(CACHE_DIR, "node_modules", packageName)
|
|
84943
84992
|
];
|
|
84944
84993
|
let packageRemoved = false;
|
|
@@ -94744,6 +94793,15 @@ function readBoulderState(directory) {
|
|
|
94744
94793
|
if (!Array.isArray(parsed.session_ids)) {
|
|
94745
94794
|
parsed.session_ids = [];
|
|
94746
94795
|
}
|
|
94796
|
+
if (!parsed.session_origins || typeof parsed.session_origins !== "object" || Array.isArray(parsed.session_origins)) {
|
|
94797
|
+
parsed.session_origins = {};
|
|
94798
|
+
}
|
|
94799
|
+
if (parsed.session_ids.length === 1) {
|
|
94800
|
+
const soleSessionId = parsed.session_ids[0];
|
|
94801
|
+
if (typeof soleSessionId === "string" && parsed.session_origins[soleSessionId] !== "appended" && parsed.session_origins[soleSessionId] !== "direct") {
|
|
94802
|
+
parsed.session_origins[soleSessionId] = "direct";
|
|
94803
|
+
}
|
|
94804
|
+
}
|
|
94747
94805
|
if (!parsed.task_sessions || typeof parsed.task_sessions !== "object" || Array.isArray(parsed.task_sessions)) {
|
|
94748
94806
|
parsed.task_sessions = {};
|
|
94749
94807
|
}
|
|
@@ -94765,22 +94823,34 @@ function writeBoulderState(directory, state3) {
|
|
|
94765
94823
|
return false;
|
|
94766
94824
|
}
|
|
94767
94825
|
}
|
|
94768
|
-
function appendSessionId(directory, sessionId) {
|
|
94826
|
+
function appendSessionId(directory, sessionId, origin = "direct") {
|
|
94769
94827
|
const state3 = readBoulderState(directory);
|
|
94770
94828
|
if (!state3)
|
|
94771
94829
|
return null;
|
|
94830
|
+
if (!state3.session_origins || typeof state3.session_origins !== "object" || Array.isArray(state3.session_origins)) {
|
|
94831
|
+
state3.session_origins = {};
|
|
94832
|
+
}
|
|
94772
94833
|
if (!state3.session_ids?.includes(sessionId)) {
|
|
94773
94834
|
if (!Array.isArray(state3.session_ids)) {
|
|
94774
94835
|
state3.session_ids = [];
|
|
94775
94836
|
}
|
|
94776
94837
|
const originalSessionIds = [...state3.session_ids];
|
|
94838
|
+
const originalSessionOrigins = { ...state3.session_origins };
|
|
94777
94839
|
state3.session_ids.push(sessionId);
|
|
94840
|
+
state3.session_origins[sessionId] = origin;
|
|
94778
94841
|
if (writeBoulderState(directory, state3)) {
|
|
94779
94842
|
return state3;
|
|
94780
94843
|
}
|
|
94781
94844
|
state3.session_ids = originalSessionIds;
|
|
94845
|
+
state3.session_origins = originalSessionOrigins;
|
|
94782
94846
|
return null;
|
|
94783
94847
|
}
|
|
94848
|
+
if (!state3.session_origins[sessionId]) {
|
|
94849
|
+
state3.session_origins[sessionId] = origin;
|
|
94850
|
+
if (!writeBoulderState(directory, state3)) {
|
|
94851
|
+
return null;
|
|
94852
|
+
}
|
|
94853
|
+
}
|
|
94784
94854
|
return state3;
|
|
94785
94855
|
}
|
|
94786
94856
|
function clearBoulderState(directory) {
|
|
@@ -94869,6 +94939,9 @@ function createBoulderState(planPath, sessionId, agent, worktreePath) {
|
|
|
94869
94939
|
active_plan: planPath,
|
|
94870
94940
|
started_at: new Date().toISOString(),
|
|
94871
94941
|
session_ids: [sessionId],
|
|
94942
|
+
session_origins: {
|
|
94943
|
+
[sessionId]: "direct"
|
|
94944
|
+
},
|
|
94872
94945
|
plan_name: getPlanName(planPath),
|
|
94873
94946
|
...agent !== undefined ? { agent } : {},
|
|
94874
94947
|
...worktreePath !== undefined ? { worktree_path: worktreePath } : {}
|
|
@@ -95500,6 +95573,100 @@ function isAbortError(error48) {
|
|
|
95500
95573
|
return false;
|
|
95501
95574
|
}
|
|
95502
95575
|
|
|
95576
|
+
// src/hooks/atlas/session-last-agent.ts
|
|
95577
|
+
import { readFileSync as readFileSync44, readdirSync as readdirSync17 } from "fs";
|
|
95578
|
+
import { join as join68 } from "path";
|
|
95579
|
+
function isCompactionAgent2(agent) {
|
|
95580
|
+
return typeof agent === "string" && agent.toLowerCase() === "compaction";
|
|
95581
|
+
}
|
|
95582
|
+
function getLastAgentFromMessageDir(messageDir) {
|
|
95583
|
+
try {
|
|
95584
|
+
const messages = readdirSync17(messageDir).filter((fileName) => fileName.endsWith(".json")).map((fileName) => {
|
|
95585
|
+
try {
|
|
95586
|
+
const content = readFileSync44(join68(messageDir, fileName), "utf-8");
|
|
95587
|
+
const parsed = JSON.parse(content);
|
|
95588
|
+
return {
|
|
95589
|
+
fileName,
|
|
95590
|
+
agent: parsed.agent,
|
|
95591
|
+
createdAt: typeof parsed.time?.created === "number" ? parsed.time.created : Number.NEGATIVE_INFINITY
|
|
95592
|
+
};
|
|
95593
|
+
} catch {
|
|
95594
|
+
return null;
|
|
95595
|
+
}
|
|
95596
|
+
}).filter((message) => message !== null).sort((left, right) => right.createdAt - left.createdAt || right.fileName.localeCompare(left.fileName));
|
|
95597
|
+
for (const message of messages) {
|
|
95598
|
+
if (typeof message.agent === "string" && !isCompactionAgent2(message.agent)) {
|
|
95599
|
+
return message.agent.toLowerCase();
|
|
95600
|
+
}
|
|
95601
|
+
}
|
|
95602
|
+
} catch {
|
|
95603
|
+
return null;
|
|
95604
|
+
}
|
|
95605
|
+
return null;
|
|
95606
|
+
}
|
|
95607
|
+
async function getLastAgentFromSession(sessionID, client) {
|
|
95608
|
+
if (isSqliteBackend() && client) {
|
|
95609
|
+
try {
|
|
95610
|
+
const response = await client.session.messages({ path: { id: sessionID } });
|
|
95611
|
+
const messages = normalizeSDKResponse(response, [], {
|
|
95612
|
+
preferResponseOnMissingData: true
|
|
95613
|
+
}).sort((left, right) => {
|
|
95614
|
+
const leftTime = left.info?.time?.created ?? Number.NEGATIVE_INFINITY;
|
|
95615
|
+
const rightTime = right.info?.time?.created ?? Number.NEGATIVE_INFINITY;
|
|
95616
|
+
if (leftTime !== rightTime) {
|
|
95617
|
+
return rightTime - leftTime;
|
|
95618
|
+
}
|
|
95619
|
+
const leftId = typeof left.id === "string" ? left.id : "";
|
|
95620
|
+
const rightId = typeof right.id === "string" ? right.id : "";
|
|
95621
|
+
return rightId.localeCompare(leftId);
|
|
95622
|
+
});
|
|
95623
|
+
for (const message of messages) {
|
|
95624
|
+
const agent = message.info?.agent;
|
|
95625
|
+
if (typeof agent === "string" && !isCompactionAgent2(agent)) {
|
|
95626
|
+
return agent.toLowerCase();
|
|
95627
|
+
}
|
|
95628
|
+
}
|
|
95629
|
+
} catch {
|
|
95630
|
+
return null;
|
|
95631
|
+
}
|
|
95632
|
+
return null;
|
|
95633
|
+
}
|
|
95634
|
+
const messageDir = getMessageDir(sessionID);
|
|
95635
|
+
if (!messageDir)
|
|
95636
|
+
return null;
|
|
95637
|
+
return getLastAgentFromMessageDir(messageDir);
|
|
95638
|
+
}
|
|
95639
|
+
|
|
95640
|
+
// src/hooks/atlas/boulder-session-lineage.ts
|
|
95641
|
+
init_logger();
|
|
95642
|
+
async function isSessionInBoulderLineage(input) {
|
|
95643
|
+
const visitedSessionIDs = new Set;
|
|
95644
|
+
let currentSessionID = input.sessionID;
|
|
95645
|
+
while (!visitedSessionIDs.has(currentSessionID)) {
|
|
95646
|
+
visitedSessionIDs.add(currentSessionID);
|
|
95647
|
+
const sessionResult = await input.client.session.get({ path: { id: currentSessionID } }).catch((error48) => {
|
|
95648
|
+
log(`[${HOOK_NAME7}] Failed to resolve session lineage`, {
|
|
95649
|
+
sessionID: input.sessionID,
|
|
95650
|
+
currentSessionID,
|
|
95651
|
+
error: error48
|
|
95652
|
+
});
|
|
95653
|
+
return null;
|
|
95654
|
+
});
|
|
95655
|
+
if (!sessionResult || sessionResult.error) {
|
|
95656
|
+
return false;
|
|
95657
|
+
}
|
|
95658
|
+
const parentSessionID = sessionResult.data?.parentID;
|
|
95659
|
+
if (!parentSessionID) {
|
|
95660
|
+
return false;
|
|
95661
|
+
}
|
|
95662
|
+
if (input.boulderSessionIDs.includes(parentSessionID)) {
|
|
95663
|
+
return true;
|
|
95664
|
+
}
|
|
95665
|
+
currentSessionID = parentSessionID;
|
|
95666
|
+
}
|
|
95667
|
+
return false;
|
|
95668
|
+
}
|
|
95669
|
+
|
|
95503
95670
|
// src/hooks/atlas/idle-event.ts
|
|
95504
95671
|
init_logger();
|
|
95505
95672
|
|
|
@@ -95696,9 +95863,17 @@ If you were given **multiple genuinely independent goals** (unrelated tasks, par
|
|
|
95696
95863
|
async function resolveRecentPromptContextForSession(ctx, sessionID) {
|
|
95697
95864
|
try {
|
|
95698
95865
|
const messagesResp = await ctx.client.session.messages({ path: { id: sessionID } });
|
|
95699
|
-
const messages = normalizeSDKResponse(messagesResp, [])
|
|
95700
|
-
|
|
95701
|
-
const
|
|
95866
|
+
const messages = normalizeSDKResponse(messagesResp, []).sort((left, right) => {
|
|
95867
|
+
const leftTime = left.info?.time?.created ?? Number.NEGATIVE_INFINITY;
|
|
95868
|
+
const rightTime = right.info?.time?.created ?? Number.NEGATIVE_INFINITY;
|
|
95869
|
+
if (leftTime !== rightTime)
|
|
95870
|
+
return rightTime - leftTime;
|
|
95871
|
+
const leftId = typeof left.id === "string" ? left.id : "";
|
|
95872
|
+
const rightId = typeof right.id === "string" ? right.id : "";
|
|
95873
|
+
return rightId.localeCompare(leftId);
|
|
95874
|
+
});
|
|
95875
|
+
for (const message of messages) {
|
|
95876
|
+
const info = message.info;
|
|
95702
95877
|
const model2 = info?.model;
|
|
95703
95878
|
const tools2 = normalizePromptTools(info?.tools);
|
|
95704
95879
|
if (model2?.providerID && model2?.modelID) {
|
|
@@ -95742,7 +95917,7 @@ async function injectBoulderContinuation(input) {
|
|
|
95742
95917
|
const hasRunningBgTasks = backgroundManager ? backgroundManager.getTasksByParentSession(sessionID).some((t) => t.status === "running") : false;
|
|
95743
95918
|
if (hasRunningBgTasks) {
|
|
95744
95919
|
log(`[${HOOK_NAME7}] Skipped injection: background tasks running`, { sessionID });
|
|
95745
|
-
return;
|
|
95920
|
+
return "skipped_background_tasks";
|
|
95746
95921
|
}
|
|
95747
95922
|
const worktreeContext = worktreePath ? `
|
|
95748
95923
|
|
|
@@ -95759,7 +95934,7 @@ async function injectBoulderContinuation(input) {
|
|
|
95759
95934
|
sessionID,
|
|
95760
95935
|
agent: continuationAgent ?? agent ?? "unknown"
|
|
95761
95936
|
});
|
|
95762
|
-
return;
|
|
95937
|
+
return "skipped_agent_unavailable";
|
|
95763
95938
|
}
|
|
95764
95939
|
try {
|
|
95765
95940
|
log(`[${HOOK_NAME7}] Injecting boulder continuation`, { sessionID, planName, remaining });
|
|
@@ -95768,7 +95943,7 @@ async function injectBoulderContinuation(input) {
|
|
|
95768
95943
|
await ctx.client.session.promptAsync({
|
|
95769
95944
|
path: { id: sessionID },
|
|
95770
95945
|
body: {
|
|
95771
|
-
agent:
|
|
95946
|
+
agent: continuationAgent,
|
|
95772
95947
|
...promptContext.model !== undefined ? { model: promptContext.model } : {},
|
|
95773
95948
|
...inheritedTools ? { tools: inheritedTools } : {},
|
|
95774
95949
|
parts: [createInternalAgentTextPart(prompt)]
|
|
@@ -95777,6 +95952,7 @@ async function injectBoulderContinuation(input) {
|
|
|
95777
95952
|
});
|
|
95778
95953
|
sessionState.promptFailureCount = 0;
|
|
95779
95954
|
log(`[${HOOK_NAME7}] Boulder continuation injected`, { sessionID });
|
|
95955
|
+
return "injected";
|
|
95780
95956
|
} catch (err) {
|
|
95781
95957
|
sessionState.promptFailureCount += 1;
|
|
95782
95958
|
sessionState.lastFailureAt = Date.now();
|
|
@@ -95785,72 +95961,24 @@ async function injectBoulderContinuation(input) {
|
|
|
95785
95961
|
error: String(err),
|
|
95786
95962
|
promptFailureCount: sessionState.promptFailureCount
|
|
95787
95963
|
});
|
|
95964
|
+
return "failed";
|
|
95788
95965
|
}
|
|
95789
95966
|
}
|
|
95790
95967
|
|
|
95791
|
-
// src/hooks/atlas/boulder-session-lineage.ts
|
|
95792
|
-
init_logger();
|
|
95793
|
-
async function isSessionInBoulderLineage(input) {
|
|
95794
|
-
const visitedSessionIDs = new Set;
|
|
95795
|
-
let currentSessionID = input.sessionID;
|
|
95796
|
-
while (!visitedSessionIDs.has(currentSessionID)) {
|
|
95797
|
-
visitedSessionIDs.add(currentSessionID);
|
|
95798
|
-
const sessionResult = await input.client.session.get({ path: { id: currentSessionID } }).catch((error48) => {
|
|
95799
|
-
log(`[${HOOK_NAME7}] Failed to resolve session lineage`, {
|
|
95800
|
-
sessionID: input.sessionID,
|
|
95801
|
-
currentSessionID,
|
|
95802
|
-
error: error48
|
|
95803
|
-
});
|
|
95804
|
-
return null;
|
|
95805
|
-
});
|
|
95806
|
-
if (!sessionResult || sessionResult.error) {
|
|
95807
|
-
return false;
|
|
95808
|
-
}
|
|
95809
|
-
const parentSessionID = sessionResult.data?.parentID;
|
|
95810
|
-
if (!parentSessionID) {
|
|
95811
|
-
return false;
|
|
95812
|
-
}
|
|
95813
|
-
if (input.boulderSessionIDs.includes(parentSessionID)) {
|
|
95814
|
-
return true;
|
|
95815
|
-
}
|
|
95816
|
-
currentSessionID = parentSessionID;
|
|
95817
|
-
}
|
|
95818
|
-
return false;
|
|
95819
|
-
}
|
|
95820
|
-
|
|
95821
95968
|
// src/hooks/atlas/resolve-active-boulder-session.ts
|
|
95822
95969
|
async function resolveActiveBoulderSession(input) {
|
|
95823
95970
|
const boulderState = readBoulderState(input.directory);
|
|
95824
95971
|
if (!boulderState) {
|
|
95825
95972
|
return null;
|
|
95826
95973
|
}
|
|
95974
|
+
if (!boulderState.session_ids.includes(input.sessionID)) {
|
|
95975
|
+
return null;
|
|
95976
|
+
}
|
|
95827
95977
|
const progress = getPlanProgress(boulderState.active_plan);
|
|
95828
95978
|
if (progress.isComplete) {
|
|
95829
95979
|
return { boulderState, progress, appendedSession: false };
|
|
95830
95980
|
}
|
|
95831
|
-
|
|
95832
|
-
return { boulderState, progress, appendedSession: false };
|
|
95833
|
-
}
|
|
95834
|
-
if (!subagentSessions.has(input.sessionID)) {
|
|
95835
|
-
return null;
|
|
95836
|
-
}
|
|
95837
|
-
const belongsToActiveBoulder = await isSessionInBoulderLineage({
|
|
95838
|
-
client: input.client,
|
|
95839
|
-
sessionID: input.sessionID,
|
|
95840
|
-
boulderSessionIDs: boulderState.session_ids
|
|
95841
|
-
});
|
|
95842
|
-
if (!belongsToActiveBoulder) {
|
|
95843
|
-
return null;
|
|
95844
|
-
}
|
|
95845
|
-
const updatedBoulderState = appendSessionId(input.directory, input.sessionID);
|
|
95846
|
-
if (!updatedBoulderState?.session_ids.includes(input.sessionID)) {
|
|
95847
|
-
return null;
|
|
95848
|
-
}
|
|
95849
|
-
return {
|
|
95850
|
-
boulderState: updatedBoulderState,
|
|
95851
|
-
progress,
|
|
95852
|
-
appendedSession: true
|
|
95853
|
-
};
|
|
95981
|
+
return { boulderState, progress, appendedSession: false };
|
|
95854
95982
|
}
|
|
95855
95983
|
|
|
95856
95984
|
// src/hooks/atlas/idle-event.ts
|
|
@@ -95864,12 +95992,38 @@ function hasRunningBackgroundTasks(sessionID, options) {
|
|
|
95864
95992
|
}
|
|
95865
95993
|
async function injectContinuation2(input) {
|
|
95866
95994
|
const remaining = input.progress.total - input.progress.completed;
|
|
95867
|
-
input.sessionState.
|
|
95995
|
+
if (input.sessionState.isInjectingContinuation) {
|
|
95996
|
+
scheduleRetry({
|
|
95997
|
+
ctx: input.ctx,
|
|
95998
|
+
sessionID: input.sessionID,
|
|
95999
|
+
sessionState: input.sessionState,
|
|
96000
|
+
options: input.options
|
|
96001
|
+
});
|
|
96002
|
+
return;
|
|
96003
|
+
}
|
|
96004
|
+
input.sessionState.isInjectingContinuation = true;
|
|
95868
96005
|
try {
|
|
95869
96006
|
const currentBoulder = readBoulderState(input.ctx.directory);
|
|
95870
96007
|
const currentTask = currentBoulder ? readCurrentTopLevelTask(currentBoulder.active_plan) : null;
|
|
95871
96008
|
const preferredTaskSession = currentTask ? getTaskSessionState(input.ctx.directory, currentTask.key) : null;
|
|
95872
|
-
|
|
96009
|
+
if (!currentBoulder) {
|
|
96010
|
+
return;
|
|
96011
|
+
}
|
|
96012
|
+
const canContinueSession = await canContinueTrackedBoulderSession({
|
|
96013
|
+
client: input.ctx.client,
|
|
96014
|
+
sessionID: input.sessionID,
|
|
96015
|
+
sessionOrigin: currentBoulder.session_origins?.[input.sessionID],
|
|
96016
|
+
boulderSessionIDs: currentBoulder.session_ids,
|
|
96017
|
+
requiredAgent: currentBoulder.agent
|
|
96018
|
+
});
|
|
96019
|
+
if (!canContinueSession) {
|
|
96020
|
+
log(`[${HOOK_NAME7}] Skipped: tracked descendant agent does not match boulder agent`, {
|
|
96021
|
+
sessionID: input.sessionID,
|
|
96022
|
+
requiredAgent: currentBoulder.agent ?? "atlas"
|
|
96023
|
+
});
|
|
96024
|
+
return;
|
|
96025
|
+
}
|
|
96026
|
+
const result = await injectBoulderContinuation({
|
|
95873
96027
|
ctx: input.ctx,
|
|
95874
96028
|
sessionID: input.sessionID,
|
|
95875
96029
|
planName: input.planName,
|
|
@@ -95882,9 +96036,43 @@ async function injectContinuation2(input) {
|
|
|
95882
96036
|
backgroundManager: input.options?.backgroundManager,
|
|
95883
96037
|
sessionState: input.sessionState
|
|
95884
96038
|
});
|
|
96039
|
+
if (result === "injected") {
|
|
96040
|
+
if (input.sessionState.pendingRetryTimer) {
|
|
96041
|
+
clearTimeout(input.sessionState.pendingRetryTimer);
|
|
96042
|
+
input.sessionState.pendingRetryTimer = undefined;
|
|
96043
|
+
}
|
|
96044
|
+
input.sessionState.lastContinuationInjectedAt = Date.now();
|
|
96045
|
+
return;
|
|
96046
|
+
}
|
|
96047
|
+
if (result === "skipped_background_tasks") {
|
|
96048
|
+
scheduleRetry({
|
|
96049
|
+
ctx: input.ctx,
|
|
96050
|
+
sessionID: input.sessionID,
|
|
96051
|
+
sessionState: input.sessionState,
|
|
96052
|
+
options: input.options
|
|
96053
|
+
});
|
|
96054
|
+
return;
|
|
96055
|
+
}
|
|
96056
|
+
if (result === "failed") {
|
|
96057
|
+
scheduleRetry({
|
|
96058
|
+
ctx: input.ctx,
|
|
96059
|
+
sessionID: input.sessionID,
|
|
96060
|
+
sessionState: input.sessionState,
|
|
96061
|
+
options: input.options
|
|
96062
|
+
});
|
|
96063
|
+
}
|
|
95885
96064
|
} catch (error48) {
|
|
95886
96065
|
log(`[${HOOK_NAME7}] Failed to inject boulder continuation`, { sessionID: input.sessionID, error: error48 });
|
|
95887
96066
|
input.sessionState.promptFailureCount += 1;
|
|
96067
|
+
input.sessionState.lastFailureAt = Date.now();
|
|
96068
|
+
scheduleRetry({
|
|
96069
|
+
ctx: input.ctx,
|
|
96070
|
+
sessionID: input.sessionID,
|
|
96071
|
+
sessionState: input.sessionState,
|
|
96072
|
+
options: input.options
|
|
96073
|
+
});
|
|
96074
|
+
} finally {
|
|
96075
|
+
input.sessionState.isInjectingContinuation = false;
|
|
95888
96076
|
}
|
|
95889
96077
|
}
|
|
95890
96078
|
function scheduleRetry(input) {
|
|
@@ -95898,6 +96086,10 @@ function scheduleRetry(input) {
|
|
|
95898
96086
|
return;
|
|
95899
96087
|
if (sessionState.waitingForFinalWaveApproval)
|
|
95900
96088
|
return;
|
|
96089
|
+
const now = Date.now();
|
|
96090
|
+
if (sessionState.lastContinuationInjectedAt && now - sessionState.lastContinuationInjectedAt < CONTINUATION_COOLDOWN_MS2) {
|
|
96091
|
+
return;
|
|
96092
|
+
}
|
|
95901
96093
|
const currentBoulder = readBoulderState(ctx.directory);
|
|
95902
96094
|
if (!currentBoulder)
|
|
95903
96095
|
return;
|
|
@@ -95908,8 +96100,19 @@ function scheduleRetry(input) {
|
|
|
95908
96100
|
return;
|
|
95909
96101
|
if (options?.isContinuationStopped?.(sessionID))
|
|
95910
96102
|
return;
|
|
95911
|
-
|
|
96103
|
+
const canContinueSession = await canContinueTrackedBoulderSession({
|
|
96104
|
+
client: ctx.client,
|
|
96105
|
+
sessionID,
|
|
96106
|
+
sessionOrigin: currentBoulder.session_origins?.[sessionID],
|
|
96107
|
+
boulderSessionIDs: currentBoulder.session_ids,
|
|
96108
|
+
requiredAgent: currentBoulder.agent
|
|
96109
|
+
});
|
|
96110
|
+
if (!canContinueSession)
|
|
95912
96111
|
return;
|
|
96112
|
+
if (hasRunningBackgroundTasks(sessionID, options)) {
|
|
96113
|
+
scheduleRetry({ ctx, sessionID, sessionState, options });
|
|
96114
|
+
return;
|
|
96115
|
+
}
|
|
95913
96116
|
await injectContinuation2({
|
|
95914
96117
|
ctx,
|
|
95915
96118
|
sessionID,
|
|
@@ -95945,27 +96148,19 @@ async function handleAtlasSessionIdle(input) {
|
|
|
95945
96148
|
plan: boulderState.plan_name
|
|
95946
96149
|
});
|
|
95947
96150
|
}
|
|
95948
|
-
|
|
95949
|
-
|
|
95950
|
-
|
|
95951
|
-
|
|
95952
|
-
|
|
95953
|
-
|
|
95954
|
-
|
|
95955
|
-
|
|
95956
|
-
|
|
95957
|
-
|
|
95958
|
-
|
|
95959
|
-
|
|
95960
|
-
|
|
95961
|
-
if (!agentMatches) {
|
|
95962
|
-
log(`[${HOOK_NAME7}] Skipped: subagent agent does not match boulder agent`, {
|
|
95963
|
-
sessionID,
|
|
95964
|
-
agent: sessionAgent ?? "unknown",
|
|
95965
|
-
requiredAgent: requiredAgentName
|
|
95966
|
-
});
|
|
95967
|
-
return;
|
|
95968
|
-
}
|
|
96151
|
+
const canContinueSession = await canContinueTrackedBoulderSession({
|
|
96152
|
+
client: ctx.client,
|
|
96153
|
+
sessionID,
|
|
96154
|
+
sessionOrigin: boulderState.session_origins?.[sessionID],
|
|
96155
|
+
boulderSessionIDs: boulderState.session_ids,
|
|
96156
|
+
requiredAgent: boulderState.agent
|
|
96157
|
+
});
|
|
96158
|
+
if (!canContinueSession) {
|
|
96159
|
+
log(`[${HOOK_NAME7}] Skipped: tracked descendant agent does not match boulder agent`, {
|
|
96160
|
+
sessionID,
|
|
96161
|
+
requiredAgent: boulderState.agent ?? "atlas"
|
|
96162
|
+
});
|
|
96163
|
+
return;
|
|
95969
96164
|
}
|
|
95970
96165
|
const sessionState = getState(sessionID);
|
|
95971
96166
|
const now = Date.now();
|
|
@@ -95992,6 +96187,7 @@ async function handleAtlasSessionIdle(input) {
|
|
|
95992
96187
|
sessionState.lastFailureAt = undefined;
|
|
95993
96188
|
}
|
|
95994
96189
|
if (hasRunningBackgroundTasks(sessionID, options)) {
|
|
96190
|
+
scheduleRetry({ ctx, sessionID, sessionState, options });
|
|
95995
96191
|
log(`[${HOOK_NAME7}] Skipped: background tasks running`, { sessionID });
|
|
95996
96192
|
return;
|
|
95997
96193
|
}
|
|
@@ -96019,6 +96215,30 @@ async function handleAtlasSessionIdle(input) {
|
|
|
96019
96215
|
worktreePath: boulderState.worktree_path
|
|
96020
96216
|
});
|
|
96021
96217
|
}
|
|
96218
|
+
async function canContinueTrackedBoulderSession(input) {
|
|
96219
|
+
const ancestorSessionIDs = input.boulderSessionIDs.filter((trackedSessionID) => trackedSessionID !== input.sessionID);
|
|
96220
|
+
if (ancestorSessionIDs.length === 0) {
|
|
96221
|
+
return true;
|
|
96222
|
+
}
|
|
96223
|
+
const isTrackedDescendant = await isSessionInBoulderLineage({
|
|
96224
|
+
client: input.client,
|
|
96225
|
+
sessionID: input.sessionID,
|
|
96226
|
+
boulderSessionIDs: ancestorSessionIDs
|
|
96227
|
+
});
|
|
96228
|
+
if (input.sessionOrigin === "direct") {
|
|
96229
|
+
return true;
|
|
96230
|
+
}
|
|
96231
|
+
if (!isTrackedDescendant) {
|
|
96232
|
+
return false;
|
|
96233
|
+
}
|
|
96234
|
+
const sessionAgent = await getLastAgentFromSession(input.sessionID, input.client) ?? getSessionAgent(input.sessionID);
|
|
96235
|
+
if (!sessionAgent) {
|
|
96236
|
+
return false;
|
|
96237
|
+
}
|
|
96238
|
+
const requiredAgentKey = getAgentConfigKey(input.requiredAgent ?? "atlas");
|
|
96239
|
+
const sessionAgentKey = getAgentConfigKey(sessionAgent);
|
|
96240
|
+
return sessionAgentKey === requiredAgentKey || requiredAgentKey === getAgentConfigKey("atlas") && sessionAgentKey === getAgentConfigKey("sisyphus");
|
|
96241
|
+
}
|
|
96022
96242
|
|
|
96023
96243
|
// src/hooks/atlas/event-handler.ts
|
|
96024
96244
|
function createAtlasEventHandler(input) {
|
|
@@ -96108,8 +96328,141 @@ function createAtlasEventHandler(input) {
|
|
|
96108
96328
|
// src/hooks/atlas/tool-execute-after.ts
|
|
96109
96329
|
init_logger();
|
|
96110
96330
|
|
|
96331
|
+
// src/hooks/atlas/background-launch-session-tracking.ts
|
|
96332
|
+
init_logger();
|
|
96333
|
+
|
|
96334
|
+
// src/hooks/atlas/subagent-session-id.ts
|
|
96335
|
+
init_logger();
|
|
96336
|
+
function extractSessionIdFromMetadata(metadata) {
|
|
96337
|
+
if (metadata && typeof metadata === "object" && "sessionId" in metadata) {
|
|
96338
|
+
const value = metadata.sessionId;
|
|
96339
|
+
if (typeof value === "string" && value.startsWith("ses_")) {
|
|
96340
|
+
return value;
|
|
96341
|
+
}
|
|
96342
|
+
}
|
|
96343
|
+
return;
|
|
96344
|
+
}
|
|
96345
|
+
function extractSessionIdFromOutput(output) {
|
|
96346
|
+
const taskMetadataBlocks = [...output.matchAll(/<task_metadata>([\s\S]*?)<\/task_metadata>/gi)];
|
|
96347
|
+
const lastTaskMetadataBlock = taskMetadataBlocks.at(-1)?.[1];
|
|
96348
|
+
if (lastTaskMetadataBlock) {
|
|
96349
|
+
const taskMetadataSessionMatch = lastTaskMetadataBlock.match(/session_id:\s*(ses_[a-zA-Z0-9_-]+)/i);
|
|
96350
|
+
if (taskMetadataSessionMatch) {
|
|
96351
|
+
return taskMetadataSessionMatch[1];
|
|
96352
|
+
}
|
|
96353
|
+
}
|
|
96354
|
+
const explicitSessionMatches = [...output.matchAll(/Session ID:\s*(ses_[a-zA-Z0-9_-]+)/g)];
|
|
96355
|
+
return explicitSessionMatches.at(-1)?.[1];
|
|
96356
|
+
}
|
|
96357
|
+
async function validateSubagentSessionId(input) {
|
|
96358
|
+
if (!input.sessionID || input.lineageSessionIDs.length === 0) {
|
|
96359
|
+
return;
|
|
96360
|
+
}
|
|
96361
|
+
const belongsToLineage = await isSessionInBoulderLineage({
|
|
96362
|
+
client: input.client,
|
|
96363
|
+
sessionID: input.sessionID,
|
|
96364
|
+
boulderSessionIDs: input.lineageSessionIDs
|
|
96365
|
+
});
|
|
96366
|
+
if (!belongsToLineage) {
|
|
96367
|
+
log(`[${HOOK_NAME7}] Ignoring extracted session id outside active lineage`, {
|
|
96368
|
+
sessionID: input.sessionID,
|
|
96369
|
+
lineageSessionIDs: input.lineageSessionIDs
|
|
96370
|
+
});
|
|
96371
|
+
return;
|
|
96372
|
+
}
|
|
96373
|
+
return input.sessionID;
|
|
96374
|
+
}
|
|
96375
|
+
|
|
96376
|
+
// src/hooks/atlas/task-context.ts
|
|
96377
|
+
function resolvePreferredSessionId(currentSessionId, trackedSessionId) {
|
|
96378
|
+
return currentSessionId ?? trackedSessionId ?? "<session_id>";
|
|
96379
|
+
}
|
|
96380
|
+
function resolveTaskContext(pendingTaskRef, planPath) {
|
|
96381
|
+
if (!pendingTaskRef) {
|
|
96382
|
+
return {
|
|
96383
|
+
currentTask: readCurrentTopLevelTask(planPath),
|
|
96384
|
+
shouldSkipTaskSessionUpdate: false,
|
|
96385
|
+
shouldIgnoreCurrentSessionId: false
|
|
96386
|
+
};
|
|
96387
|
+
}
|
|
96388
|
+
if (pendingTaskRef.kind === "track") {
|
|
96389
|
+
return {
|
|
96390
|
+
currentTask: pendingTaskRef.task,
|
|
96391
|
+
shouldSkipTaskSessionUpdate: false,
|
|
96392
|
+
shouldIgnoreCurrentSessionId: false
|
|
96393
|
+
};
|
|
96394
|
+
}
|
|
96395
|
+
if (pendingTaskRef.reason === "explicit_resume") {
|
|
96396
|
+
return {
|
|
96397
|
+
currentTask: readCurrentTopLevelTask(planPath),
|
|
96398
|
+
shouldSkipTaskSessionUpdate: true,
|
|
96399
|
+
shouldIgnoreCurrentSessionId: true
|
|
96400
|
+
};
|
|
96401
|
+
}
|
|
96402
|
+
return {
|
|
96403
|
+
currentTask: pendingTaskRef.task,
|
|
96404
|
+
shouldSkipTaskSessionUpdate: true,
|
|
96405
|
+
shouldIgnoreCurrentSessionId: true
|
|
96406
|
+
};
|
|
96407
|
+
}
|
|
96408
|
+
|
|
96409
|
+
// src/hooks/atlas/background-launch-session-tracking.ts
|
|
96410
|
+
async function syncBackgroundLaunchSessionTracking(input) {
|
|
96411
|
+
const { ctx, boulderState, toolInput, toolOutput, pendingTaskRef, metadataSessionId } = input;
|
|
96412
|
+
if (!boulderState) {
|
|
96413
|
+
return;
|
|
96414
|
+
}
|
|
96415
|
+
const extractedSessionId = metadataSessionId ?? extractSessionIdFromOutput(toolOutput.output);
|
|
96416
|
+
const lineageSessionIDs = boulderState.session_ids;
|
|
96417
|
+
const subagentSessionId = await validateSubagentSessionId({
|
|
96418
|
+
client: ctx.client,
|
|
96419
|
+
sessionID: extractedSessionId,
|
|
96420
|
+
lineageSessionIDs
|
|
96421
|
+
});
|
|
96422
|
+
const trackedSessionId = subagentSessionId ?? await resolveFallbackTrackedSessionId({
|
|
96423
|
+
ctx,
|
|
96424
|
+
extractedSessionId,
|
|
96425
|
+
lineageSessionIDs
|
|
96426
|
+
});
|
|
96427
|
+
if (!trackedSessionId) {
|
|
96428
|
+
return;
|
|
96429
|
+
}
|
|
96430
|
+
appendSessionId(ctx.directory, trackedSessionId, "appended");
|
|
96431
|
+
const { currentTask, shouldSkipTaskSessionUpdate } = resolveTaskContext(pendingTaskRef, boulderState.active_plan);
|
|
96432
|
+
if (currentTask && !shouldSkipTaskSessionUpdate) {
|
|
96433
|
+
upsertTaskSessionState(ctx.directory, {
|
|
96434
|
+
taskKey: currentTask.key,
|
|
96435
|
+
taskLabel: currentTask.label,
|
|
96436
|
+
taskTitle: currentTask.title,
|
|
96437
|
+
sessionId: trackedSessionId,
|
|
96438
|
+
agent: typeof toolOutput.metadata?.agent === "string" ? toolOutput.metadata.agent : undefined,
|
|
96439
|
+
category: typeof toolOutput.metadata?.category === "string" ? toolOutput.metadata.category : undefined
|
|
96440
|
+
});
|
|
96441
|
+
}
|
|
96442
|
+
log(`[${HOOK_NAME7}] Background launch session tracked`, {
|
|
96443
|
+
sessionID: toolInput.sessionID,
|
|
96444
|
+
subagentSessionId: trackedSessionId,
|
|
96445
|
+
taskKey: currentTask?.key
|
|
96446
|
+
});
|
|
96447
|
+
}
|
|
96448
|
+
async function resolveFallbackTrackedSessionId(input) {
|
|
96449
|
+
if (!input.extractedSessionId) {
|
|
96450
|
+
return;
|
|
96451
|
+
}
|
|
96452
|
+
try {
|
|
96453
|
+
const session = await input.ctx.client.session.get({ path: { id: input.extractedSessionId } });
|
|
96454
|
+
const parentSessionId = session.data?.parentID;
|
|
96455
|
+
if (typeof parentSessionId === "string" && input.lineageSessionIDs.includes(parentSessionId)) {
|
|
96456
|
+
return input.extractedSessionId;
|
|
96457
|
+
}
|
|
96458
|
+
return;
|
|
96459
|
+
} catch {
|
|
96460
|
+
return;
|
|
96461
|
+
}
|
|
96462
|
+
}
|
|
96463
|
+
|
|
96111
96464
|
// src/hooks/atlas/final-wave-plan-state.ts
|
|
96112
|
-
import { existsSync as existsSync61, readFileSync as
|
|
96465
|
+
import { existsSync as existsSync61, readFileSync as readFileSync45 } from "fs";
|
|
96113
96466
|
var TODO_HEADING_PATTERN2 = /^##\s+TODOs\b/i;
|
|
96114
96467
|
var FINAL_VERIFICATION_HEADING_PATTERN2 = /^##\s+Final Verification Wave\b/i;
|
|
96115
96468
|
var SECOND_LEVEL_HEADING_PATTERN2 = /^##\s+/;
|
|
@@ -96121,7 +96474,7 @@ function readFinalWavePlanState(planPath) {
|
|
|
96121
96474
|
return null;
|
|
96122
96475
|
}
|
|
96123
96476
|
try {
|
|
96124
|
-
const content =
|
|
96477
|
+
const content = readFileSync45(planPath, "utf-8");
|
|
96125
96478
|
const lines = content.split(/\r?\n/);
|
|
96126
96479
|
let section = "other";
|
|
96127
96480
|
let pendingImplementationTaskCount = 0;
|
|
@@ -96190,48 +96543,6 @@ function isSisyphusPath(filePath) {
|
|
|
96190
96543
|
return /\.sisyphus[/\\]/.test(filePath);
|
|
96191
96544
|
}
|
|
96192
96545
|
|
|
96193
|
-
// src/hooks/atlas/subagent-session-id.ts
|
|
96194
|
-
init_logger();
|
|
96195
|
-
function extractSessionIdFromMetadata(metadata) {
|
|
96196
|
-
if (metadata && typeof metadata === "object" && "sessionId" in metadata) {
|
|
96197
|
-
const value = metadata.sessionId;
|
|
96198
|
-
if (typeof value === "string" && value.startsWith("ses_")) {
|
|
96199
|
-
return value;
|
|
96200
|
-
}
|
|
96201
|
-
}
|
|
96202
|
-
return;
|
|
96203
|
-
}
|
|
96204
|
-
function extractSessionIdFromOutput(output) {
|
|
96205
|
-
const taskMetadataBlocks = [...output.matchAll(/<task_metadata>([\s\S]*?)<\/task_metadata>/gi)];
|
|
96206
|
-
const lastTaskMetadataBlock = taskMetadataBlocks.at(-1)?.[1];
|
|
96207
|
-
if (lastTaskMetadataBlock) {
|
|
96208
|
-
const taskMetadataSessionMatch = lastTaskMetadataBlock.match(/session_id:\s*(ses_[a-zA-Z0-9_-]+)/i);
|
|
96209
|
-
if (taskMetadataSessionMatch) {
|
|
96210
|
-
return taskMetadataSessionMatch[1];
|
|
96211
|
-
}
|
|
96212
|
-
}
|
|
96213
|
-
const explicitSessionMatches = [...output.matchAll(/Session ID:\s*(ses_[a-zA-Z0-9_-]+)/g)];
|
|
96214
|
-
return explicitSessionMatches.at(-1)?.[1];
|
|
96215
|
-
}
|
|
96216
|
-
async function validateSubagentSessionId(input) {
|
|
96217
|
-
if (!input.sessionID || input.lineageSessionIDs.length === 0) {
|
|
96218
|
-
return;
|
|
96219
|
-
}
|
|
96220
|
-
const belongsToLineage = await isSessionInBoulderLineage({
|
|
96221
|
-
client: input.client,
|
|
96222
|
-
sessionID: input.sessionID,
|
|
96223
|
-
boulderSessionIDs: input.lineageSessionIDs
|
|
96224
|
-
});
|
|
96225
|
-
if (!belongsToLineage) {
|
|
96226
|
-
log(`[${HOOK_NAME7}] Ignoring extracted session id outside active lineage`, {
|
|
96227
|
-
sessionID: input.sessionID,
|
|
96228
|
-
lineageSessionIDs: input.lineageSessionIDs
|
|
96229
|
-
});
|
|
96230
|
-
return;
|
|
96231
|
-
}
|
|
96232
|
-
return input.sessionID;
|
|
96233
|
-
}
|
|
96234
|
-
|
|
96235
96546
|
// src/hooks/atlas/verification-reminders.ts
|
|
96236
96547
|
function buildReuseHint(sessionId) {
|
|
96237
96548
|
return `
|
|
@@ -96412,37 +96723,6 @@ function isWriteOrEditToolName(toolName) {
|
|
|
96412
96723
|
}
|
|
96413
96724
|
|
|
96414
96725
|
// src/hooks/atlas/tool-execute-after.ts
|
|
96415
|
-
function resolvePreferredSessionId(currentSessionId, trackedSessionId) {
|
|
96416
|
-
return currentSessionId ?? trackedSessionId ?? "<session_id>";
|
|
96417
|
-
}
|
|
96418
|
-
function resolveTaskContext(pendingTaskRef, planPath) {
|
|
96419
|
-
if (!pendingTaskRef) {
|
|
96420
|
-
return {
|
|
96421
|
-
currentTask: readCurrentTopLevelTask(planPath),
|
|
96422
|
-
shouldSkipTaskSessionUpdate: false,
|
|
96423
|
-
shouldIgnoreCurrentSessionId: false
|
|
96424
|
-
};
|
|
96425
|
-
}
|
|
96426
|
-
if (pendingTaskRef.kind === "track") {
|
|
96427
|
-
return {
|
|
96428
|
-
currentTask: pendingTaskRef.task,
|
|
96429
|
-
shouldSkipTaskSessionUpdate: false,
|
|
96430
|
-
shouldIgnoreCurrentSessionId: false
|
|
96431
|
-
};
|
|
96432
|
-
}
|
|
96433
|
-
if (pendingTaskRef.reason === "explicit_resume") {
|
|
96434
|
-
return {
|
|
96435
|
-
currentTask: readCurrentTopLevelTask(planPath),
|
|
96436
|
-
shouldSkipTaskSessionUpdate: true,
|
|
96437
|
-
shouldIgnoreCurrentSessionId: true
|
|
96438
|
-
};
|
|
96439
|
-
}
|
|
96440
|
-
return {
|
|
96441
|
-
currentTask: pendingTaskRef.task,
|
|
96442
|
-
shouldSkipTaskSessionUpdate: true,
|
|
96443
|
-
shouldIgnoreCurrentSessionId: true
|
|
96444
|
-
};
|
|
96445
|
-
}
|
|
96446
96726
|
function createToolExecuteAfterHandler2(input) {
|
|
96447
96727
|
const { ctx, pendingFilePaths, pendingTaskRefs, autoCommit, getState } = input;
|
|
96448
96728
|
return async (toolInput, toolOutput) => {
|
|
@@ -96480,12 +96760,20 @@ function createToolExecuteAfterHandler2(input) {
|
|
|
96480
96760
|
if (toolInput.callID) {
|
|
96481
96761
|
pendingTaskRefs.delete(toolInput.callID);
|
|
96482
96762
|
}
|
|
96763
|
+
const boulderState = readBoulderState(ctx.directory);
|
|
96483
96764
|
const isBackgroundLaunch = outputStr.includes("Background task launched") || outputStr.includes("Background task continued") || outputStr.includes("Background delegate launched") || outputStr.includes("Background agent task launched");
|
|
96484
96765
|
if (isBackgroundLaunch) {
|
|
96766
|
+
await syncBackgroundLaunchSessionTracking({
|
|
96767
|
+
ctx,
|
|
96768
|
+
boulderState,
|
|
96769
|
+
toolInput,
|
|
96770
|
+
toolOutput,
|
|
96771
|
+
pendingTaskRef,
|
|
96772
|
+
metadataSessionId
|
|
96773
|
+
});
|
|
96485
96774
|
return;
|
|
96486
96775
|
}
|
|
96487
96776
|
if (toolOutput.output && typeof toolOutput.output === "string") {
|
|
96488
|
-
const boulderState = readBoulderState(ctx.directory);
|
|
96489
96777
|
const worktreePath = boulderState?.worktree_path?.trim();
|
|
96490
96778
|
const verificationDirectory = worktreePath ? worktreePath : ctx.directory;
|
|
96491
96779
|
const gitStats = collectGitDiffStats(verificationDirectory);
|
|
@@ -96500,14 +96788,7 @@ function createToolExecuteAfterHandler2(input) {
|
|
|
96500
96788
|
} = resolveTaskContext(pendingTaskRef, boulderState.active_plan);
|
|
96501
96789
|
const trackedTaskSession = currentTask ? getTaskSessionState(ctx.directory, currentTask.key) : null;
|
|
96502
96790
|
const sessionState = toolInput.sessionID ? getState(toolInput.sessionID) : undefined;
|
|
96503
|
-
|
|
96504
|
-
appendSessionId(ctx.directory, toolInput.sessionID);
|
|
96505
|
-
log(`[${HOOK_NAME7}] Appended session to boulder`, {
|
|
96506
|
-
sessionID: toolInput.sessionID,
|
|
96507
|
-
plan: boulderState.plan_name
|
|
96508
|
-
});
|
|
96509
|
-
}
|
|
96510
|
-
const lineageSessionIDs = toolInput.sessionID && !boulderState.session_ids.includes(toolInput.sessionID) ? [...boulderState.session_ids, toolInput.sessionID] : boulderState.session_ids;
|
|
96791
|
+
const lineageSessionIDs = boulderState.session_ids;
|
|
96511
96792
|
const subagentSessionId = await validateSubagentSessionId({
|
|
96512
96793
|
client: ctx.client,
|
|
96513
96794
|
sessionID: extractedSessionId,
|
|
@@ -97000,7 +97281,7 @@ function clearSessionModel(sessionID) {
|
|
|
97000
97281
|
}
|
|
97001
97282
|
|
|
97002
97283
|
// src/hooks/compaction-context-injector/session-id.ts
|
|
97003
|
-
function
|
|
97284
|
+
function isCompactionAgent3(agent) {
|
|
97004
97285
|
return agent?.trim().toLowerCase() === "compaction";
|
|
97005
97286
|
}
|
|
97006
97287
|
function resolveSessionID(props) {
|
|
@@ -97009,7 +97290,7 @@ function resolveSessionID(props) {
|
|
|
97009
97290
|
|
|
97010
97291
|
// src/hooks/compaction-context-injector/validated-model.ts
|
|
97011
97292
|
function resolveValidatedModel(info) {
|
|
97012
|
-
if (
|
|
97293
|
+
if (isCompactionAgent3(info?.agent)) {
|
|
97013
97294
|
return;
|
|
97014
97295
|
}
|
|
97015
97296
|
const providerID = info?.model?.providerID ?? info?.providerID;
|
|
@@ -97043,7 +97324,7 @@ async function resolveSessionPromptConfig(ctx, sessionID) {
|
|
|
97043
97324
|
});
|
|
97044
97325
|
for (let index = messages.length - 1;index >= 0; index--) {
|
|
97045
97326
|
const info = messages[index].info;
|
|
97046
|
-
if (!promptConfig.agent && info?.agent && !
|
|
97327
|
+
if (!promptConfig.agent && info?.agent && !isCompactionAgent3(info.agent)) {
|
|
97047
97328
|
promptConfig.agent = info.agent;
|
|
97048
97329
|
}
|
|
97049
97330
|
if (!promptConfig.model) {
|
|
@@ -97134,7 +97415,7 @@ function trackAssistantOutput(state3, messageID) {
|
|
|
97134
97415
|
init_logger();
|
|
97135
97416
|
|
|
97136
97417
|
// src/hooks/compaction-context-injector/recovery-prompt-config.ts
|
|
97137
|
-
function
|
|
97418
|
+
function isCompactionAgent4(agent) {
|
|
97138
97419
|
return agent?.trim().toLowerCase() === "compaction";
|
|
97139
97420
|
}
|
|
97140
97421
|
function matchesExpectedModel(actualModel, expectedModel) {
|
|
@@ -97167,7 +97448,7 @@ function createExpectedRecoveryPromptConfig(checkpoint, currentPromptConfig) {
|
|
|
97167
97448
|
}
|
|
97168
97449
|
function isPromptConfigRecovered(actualPromptConfig, expectedPromptConfig) {
|
|
97169
97450
|
const actualAgent = actualPromptConfig.agent;
|
|
97170
|
-
const agentMatches = typeof actualAgent === "string" && !
|
|
97451
|
+
const agentMatches = typeof actualAgent === "string" && !isCompactionAgent4(actualAgent) && actualAgent.toLowerCase() === expectedPromptConfig.agent.toLowerCase();
|
|
97171
97452
|
return agentMatches && matchesExpectedModel(actualPromptConfig.model, expectedPromptConfig.model) && matchesExpectedTools(actualPromptConfig.tools, expectedPromptConfig.tools);
|
|
97172
97453
|
}
|
|
97173
97454
|
|
|
@@ -98121,12 +98402,12 @@ import * as fs18 from "fs";
|
|
|
98121
98402
|
import * as path11 from "path";
|
|
98122
98403
|
// src/shared/migrate-legacy-config-file.ts
|
|
98123
98404
|
init_logger();
|
|
98124
|
-
import { existsSync as existsSync62, readFileSync as
|
|
98125
|
-
import { join as
|
|
98405
|
+
import { existsSync as existsSync62, readFileSync as readFileSync46, renameSync as renameSync4, rmSync as rmSync3 } from "fs";
|
|
98406
|
+
import { join as join69, dirname as dirname20, basename as basename9 } from "path";
|
|
98126
98407
|
function buildCanonicalPath(legacyPath) {
|
|
98127
98408
|
const dir = dirname20(legacyPath);
|
|
98128
98409
|
const ext = basename9(legacyPath).includes(".jsonc") ? ".jsonc" : ".json";
|
|
98129
|
-
return
|
|
98410
|
+
return join69(dir, `${CONFIG_BASENAME}${ext}`);
|
|
98130
98411
|
}
|
|
98131
98412
|
function archiveLegacyConfigFile(legacyPath) {
|
|
98132
98413
|
const backupPath = `${legacyPath}.bak`;
|
|
@@ -98166,7 +98447,7 @@ function migrateLegacyConfigFile(legacyPath) {
|
|
|
98166
98447
|
if (existsSync62(canonicalPath))
|
|
98167
98448
|
return false;
|
|
98168
98449
|
try {
|
|
98169
|
-
const content =
|
|
98450
|
+
const content = readFileSync46(legacyPath, "utf-8");
|
|
98170
98451
|
writeFileAtomically(canonicalPath, content);
|
|
98171
98452
|
const archivedLegacyConfig = archiveLegacyConfigFile(legacyPath);
|
|
98172
98453
|
log("[migrateLegacyConfigFile] Migrated legacy config to canonical path", {
|
|
@@ -98309,7 +98590,7 @@ function mergeConfigs(base, override) {
|
|
|
98309
98590
|
function loadPluginConfig(directory, ctx) {
|
|
98310
98591
|
const configDir = getOpenCodeConfigDir({ binary: "opencode" });
|
|
98311
98592
|
const userDetected = detectPluginConfigFile(configDir);
|
|
98312
|
-
let userConfigPath = userDetected.format !== "none" ? userDetected.path : path11.join(configDir,
|
|
98593
|
+
let userConfigPath = userDetected.format !== "none" ? userDetected.path : path11.join(configDir, `${CONFIG_BASENAME}.json`);
|
|
98313
98594
|
if (userDetected.legacyPath) {
|
|
98314
98595
|
log("Canonical plugin config detected alongside legacy config. Remove the legacy file to avoid confusion.", {
|
|
98315
98596
|
canonicalPath: userDetected.path,
|
|
@@ -98317,12 +98598,15 @@ function loadPluginConfig(directory, ctx) {
|
|
|
98317
98598
|
});
|
|
98318
98599
|
}
|
|
98319
98600
|
if (userDetected.format !== "none" && path11.basename(userDetected.path).startsWith(LEGACY_CONFIG_BASENAME)) {
|
|
98320
|
-
migrateLegacyConfigFile(userDetected.path);
|
|
98321
|
-
|
|
98601
|
+
const migrated = migrateLegacyConfigFile(userDetected.path);
|
|
98602
|
+
const canonicalPath = path11.join(path11.dirname(userDetected.path), `${CONFIG_BASENAME}${path11.extname(userDetected.path)}`);
|
|
98603
|
+
if (migrated || fs18.existsSync(canonicalPath)) {
|
|
98604
|
+
userConfigPath = canonicalPath;
|
|
98605
|
+
}
|
|
98322
98606
|
}
|
|
98323
98607
|
const projectBasePath = path11.join(directory, ".opencode");
|
|
98324
98608
|
const projectDetected = detectPluginConfigFile(projectBasePath);
|
|
98325
|
-
let projectConfigPath = projectDetected.format !== "none" ? projectDetected.path : path11.join(projectBasePath,
|
|
98609
|
+
let projectConfigPath = projectDetected.format !== "none" ? projectDetected.path : path11.join(projectBasePath, `${CONFIG_BASENAME}.json`);
|
|
98326
98610
|
if (projectDetected.legacyPath) {
|
|
98327
98611
|
log("Canonical plugin config detected alongside legacy config. Remove the legacy file to avoid confusion.", {
|
|
98328
98612
|
canonicalPath: projectDetected.path,
|
|
@@ -98330,8 +98614,11 @@ function loadPluginConfig(directory, ctx) {
|
|
|
98330
98614
|
});
|
|
98331
98615
|
}
|
|
98332
98616
|
if (projectDetected.format !== "none" && path11.basename(projectDetected.path).startsWith(LEGACY_CONFIG_BASENAME)) {
|
|
98333
|
-
migrateLegacyConfigFile(projectDetected.path);
|
|
98334
|
-
|
|
98617
|
+
const projectMigrated = migrateLegacyConfigFile(projectDetected.path);
|
|
98618
|
+
const canonicalProjectPath = path11.join(path11.dirname(projectDetected.path), `${CONFIG_BASENAME}${path11.extname(projectDetected.path)}`);
|
|
98619
|
+
if (projectMigrated || fs18.existsSync(canonicalProjectPath)) {
|
|
98620
|
+
projectConfigPath = canonicalProjectPath;
|
|
98621
|
+
}
|
|
98335
98622
|
}
|
|
98336
98623
|
const userConfig = loadConfigFromPath2(userConfigPath, ctx);
|
|
98337
98624
|
let config2 = userConfig ?? OhMyOpenCodeConfigSchema.parse({});
|
|
@@ -99643,7 +99930,7 @@ function createRuntimeFallbackHook(ctx, options) {
|
|
|
99643
99930
|
}
|
|
99644
99931
|
// src/hooks/write-existing-file-guard/hook.ts
|
|
99645
99932
|
import { existsSync as existsSync65, realpathSync as realpathSync7 } from "fs";
|
|
99646
|
-
import { basename as basename11, dirname as dirname22, isAbsolute as isAbsolute10, join as
|
|
99933
|
+
import { basename as basename11, dirname as dirname22, isAbsolute as isAbsolute10, join as join71, normalize as normalize2, relative as relative8, resolve as resolve12 } from "path";
|
|
99647
99934
|
|
|
99648
99935
|
// src/hooks/write-existing-file-guard/tool-execute-before-handler.ts
|
|
99649
99936
|
import { existsSync as existsSync64 } from "fs";
|
|
@@ -99818,7 +100105,7 @@ function toCanonicalPath2(absolutePath) {
|
|
|
99818
100105
|
} else {
|
|
99819
100106
|
const absoluteDir = dirname22(absolutePath);
|
|
99820
100107
|
const resolvedDir = existsSync65(absoluteDir) ? realpathSync7.native(absoluteDir) : absoluteDir;
|
|
99821
|
-
canonicalPath =
|
|
100108
|
+
canonicalPath = join71(resolvedDir, basename11(absolutePath));
|
|
99822
100109
|
}
|
|
99823
100110
|
return normalize2(canonicalPath);
|
|
99824
100111
|
}
|
|
@@ -100833,12 +101120,12 @@ function createWebFetchRedirectGuardHook(_ctx) {
|
|
|
100833
101120
|
init_logger();
|
|
100834
101121
|
|
|
100835
101122
|
// src/hooks/legacy-plugin-toast/auto-migrate.ts
|
|
100836
|
-
import { existsSync as existsSync66, readFileSync as
|
|
100837
|
-
import { join as
|
|
101123
|
+
import { existsSync as existsSync66, readFileSync as readFileSync48 } from "fs";
|
|
101124
|
+
import { join as join72 } from "path";
|
|
100838
101125
|
function detectOpenCodeConfigPath(overrideConfigDir) {
|
|
100839
101126
|
if (overrideConfigDir) {
|
|
100840
|
-
const jsoncPath =
|
|
100841
|
-
const jsonPath =
|
|
101127
|
+
const jsoncPath = join72(overrideConfigDir, "opencode.jsonc");
|
|
101128
|
+
const jsonPath = join72(overrideConfigDir, "opencode.json");
|
|
100842
101129
|
if (existsSync66(jsoncPath))
|
|
100843
101130
|
return jsoncPath;
|
|
100844
101131
|
if (existsSync66(jsonPath))
|
|
@@ -100857,7 +101144,7 @@ function autoMigrateLegacyPluginEntry(overrideConfigDir) {
|
|
|
100857
101144
|
if (!configPath)
|
|
100858
101145
|
return { migrated: false, from: null, to: null, configPath: null };
|
|
100859
101146
|
try {
|
|
100860
|
-
const content =
|
|
101147
|
+
const content = readFileSync48(configPath, "utf-8");
|
|
100861
101148
|
const parseResult = parseJsoncSafe(content);
|
|
100862
101149
|
if (!parseResult.data?.plugin)
|
|
100863
101150
|
return { migrated: false, from: null, to: null, configPath };
|
|
@@ -101251,13 +101538,13 @@ var DEFAULT_MAX_SYMBOLS = 200;
|
|
|
101251
101538
|
var DEFAULT_MAX_DIAGNOSTICS = 200;
|
|
101252
101539
|
var DEFAULT_MAX_DIRECTORY_FILES = 50;
|
|
101253
101540
|
// src/tools/lsp/server-config-loader.ts
|
|
101254
|
-
import { existsSync as existsSync67, readFileSync as
|
|
101255
|
-
import { join as
|
|
101541
|
+
import { existsSync as existsSync67, readFileSync as readFileSync49 } from "fs";
|
|
101542
|
+
import { join as join73 } from "path";
|
|
101256
101543
|
function loadJsonFile(path12) {
|
|
101257
101544
|
if (!existsSync67(path12))
|
|
101258
101545
|
return null;
|
|
101259
101546
|
try {
|
|
101260
|
-
return parseJsonc(
|
|
101547
|
+
return parseJsonc(readFileSync49(path12, "utf-8"));
|
|
101261
101548
|
} catch {
|
|
101262
101549
|
return null;
|
|
101263
101550
|
}
|
|
@@ -101266,9 +101553,9 @@ function getConfigPaths3() {
|
|
|
101266
101553
|
const cwd = process.cwd();
|
|
101267
101554
|
const configDir = getOpenCodeConfigDir({ binary: "opencode" });
|
|
101268
101555
|
return {
|
|
101269
|
-
project: detectPluginConfigFile(
|
|
101556
|
+
project: detectPluginConfigFile(join73(cwd, ".opencode")).path,
|
|
101270
101557
|
user: detectPluginConfigFile(configDir).path,
|
|
101271
|
-
opencode: detectConfigFile(
|
|
101558
|
+
opencode: detectConfigFile(join73(configDir, "opencode")).path
|
|
101272
101559
|
};
|
|
101273
101560
|
}
|
|
101274
101561
|
function loadAllConfigs() {
|
|
@@ -101338,19 +101625,19 @@ function getMergedServers() {
|
|
|
101338
101625
|
|
|
101339
101626
|
// src/tools/lsp/server-installation.ts
|
|
101340
101627
|
import { existsSync as existsSync68 } from "fs";
|
|
101341
|
-
import { delimiter, join as
|
|
101628
|
+
import { delimiter, join as join75 } from "path";
|
|
101342
101629
|
|
|
101343
101630
|
// src/tools/lsp/server-path-bases.ts
|
|
101344
|
-
import { join as
|
|
101631
|
+
import { join as join74 } from "path";
|
|
101345
101632
|
function getLspServerAdditionalPathBases(workingDirectory) {
|
|
101346
101633
|
const configDir = getOpenCodeConfigDir({ binary: "opencode" });
|
|
101347
|
-
const dataDir =
|
|
101634
|
+
const dataDir = join74(getDataDir(), "opencode");
|
|
101348
101635
|
return [
|
|
101349
|
-
|
|
101350
|
-
|
|
101351
|
-
|
|
101352
|
-
|
|
101353
|
-
|
|
101636
|
+
join74(workingDirectory, "node_modules", ".bin"),
|
|
101637
|
+
join74(configDir, "bin"),
|
|
101638
|
+
join74(configDir, "node_modules", ".bin"),
|
|
101639
|
+
join74(dataDir, "bin"),
|
|
101640
|
+
join74(dataDir, "bin", "node_modules", ".bin")
|
|
101354
101641
|
];
|
|
101355
101642
|
}
|
|
101356
101643
|
|
|
@@ -101381,14 +101668,14 @@ function isServerInstalled(command) {
|
|
|
101381
101668
|
const paths = pathEnv.split(delimiter);
|
|
101382
101669
|
for (const p of paths) {
|
|
101383
101670
|
for (const suffix of exts) {
|
|
101384
|
-
if (existsSync68(
|
|
101671
|
+
if (existsSync68(join75(p, cmd + suffix))) {
|
|
101385
101672
|
return true;
|
|
101386
101673
|
}
|
|
101387
101674
|
}
|
|
101388
101675
|
}
|
|
101389
101676
|
for (const base of getLspServerAdditionalPathBases(process.cwd())) {
|
|
101390
101677
|
for (const suffix of exts) {
|
|
101391
|
-
if (existsSync68(
|
|
101678
|
+
if (existsSync68(join75(base, cmd + suffix))) {
|
|
101392
101679
|
return true;
|
|
101393
101680
|
}
|
|
101394
101681
|
}
|
|
@@ -101584,7 +101871,7 @@ function spawnProcess(command, options) {
|
|
|
101584
101871
|
return proc;
|
|
101585
101872
|
}
|
|
101586
101873
|
// src/tools/lsp/lsp-client.ts
|
|
101587
|
-
import { readFileSync as
|
|
101874
|
+
import { readFileSync as readFileSync50 } from "fs";
|
|
101588
101875
|
import { extname as extname4, resolve as resolve13 } from "path";
|
|
101589
101876
|
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
101590
101877
|
|
|
@@ -101856,7 +102143,7 @@ class LSPClient extends LSPClientConnection {
|
|
|
101856
102143
|
async openFile(filePath) {
|
|
101857
102144
|
const absPath = resolve13(filePath);
|
|
101858
102145
|
const uri = pathToFileURL2(absPath).href;
|
|
101859
|
-
const text =
|
|
102146
|
+
const text = readFileSync50(absPath, "utf-8");
|
|
101860
102147
|
if (!this.openedFiles.has(absPath)) {
|
|
101861
102148
|
const ext = extname4(absPath);
|
|
101862
102149
|
const languageId = getLanguageId(ext);
|
|
@@ -102408,10 +102695,10 @@ function formatApplyResult(result) {
|
|
|
102408
102695
|
`);
|
|
102409
102696
|
}
|
|
102410
102697
|
// src/tools/lsp/workspace-edit.ts
|
|
102411
|
-
import { readFileSync as
|
|
102698
|
+
import { readFileSync as readFileSync51, writeFileSync as writeFileSync19 } from "fs";
|
|
102412
102699
|
function applyTextEditsToFile(filePath, edits) {
|
|
102413
102700
|
try {
|
|
102414
|
-
let content =
|
|
102701
|
+
let content = readFileSync51(filePath, "utf-8");
|
|
102415
102702
|
const lines = content.split(`
|
|
102416
102703
|
`);
|
|
102417
102704
|
const sortedEdits = [...edits].sort((a, b) => {
|
|
@@ -102477,7 +102764,7 @@ function applyWorkspaceEdit(edit) {
|
|
|
102477
102764
|
try {
|
|
102478
102765
|
const oldPath = uriToPath(change.oldUri);
|
|
102479
102766
|
const newPath = uriToPath(change.newUri);
|
|
102480
|
-
const content =
|
|
102767
|
+
const content = readFileSync51(oldPath, "utf-8");
|
|
102481
102768
|
writeFileSync19(newPath, content, "utf-8");
|
|
102482
102769
|
__require("fs").unlinkSync(oldPath);
|
|
102483
102770
|
result.filesModified.push(newPath);
|
|
@@ -114960,8 +115247,8 @@ var lsp_symbols = tool({
|
|
|
114960
115247
|
import { resolve as resolve16 } from "path";
|
|
114961
115248
|
|
|
114962
115249
|
// src/tools/lsp/directory-diagnostics.ts
|
|
114963
|
-
import { existsSync as existsSync71, lstatSync as lstatSync2, readdirSync as
|
|
114964
|
-
import { extname as extname6, join as
|
|
115250
|
+
import { existsSync as existsSync71, lstatSync as lstatSync2, readdirSync as readdirSync18 } from "fs";
|
|
115251
|
+
import { extname as extname6, join as join76, resolve as resolve15 } from "path";
|
|
114965
115252
|
var SKIP_DIRECTORIES = new Set(["node_modules", ".git", "dist", "build", ".next", "out"]);
|
|
114966
115253
|
function collectFilesWithExtension(dir, extension, maxFiles) {
|
|
114967
115254
|
const files = [];
|
|
@@ -114970,14 +115257,14 @@ function collectFilesWithExtension(dir, extension, maxFiles) {
|
|
|
114970
115257
|
return;
|
|
114971
115258
|
let entries = [];
|
|
114972
115259
|
try {
|
|
114973
|
-
entries =
|
|
115260
|
+
entries = readdirSync18(currentDir);
|
|
114974
115261
|
} catch {
|
|
114975
115262
|
return;
|
|
114976
115263
|
}
|
|
114977
115264
|
for (const entry of entries) {
|
|
114978
115265
|
if (files.length >= maxFiles)
|
|
114979
115266
|
return;
|
|
114980
|
-
const fullPath =
|
|
115267
|
+
const fullPath = join76(currentDir, entry);
|
|
114981
115268
|
let stat;
|
|
114982
115269
|
try {
|
|
114983
115270
|
stat = lstatSync2(fullPath);
|
|
@@ -115079,8 +115366,8 @@ async function aggregateDiagnosticsForDirectory(directory, extension, severity,
|
|
|
115079
115366
|
}
|
|
115080
115367
|
|
|
115081
115368
|
// src/tools/lsp/infer-extension.ts
|
|
115082
|
-
import { readdirSync as
|
|
115083
|
-
import { extname as extname7, join as
|
|
115369
|
+
import { readdirSync as readdirSync19, lstatSync as lstatSync3 } from "fs";
|
|
115370
|
+
import { extname as extname7, join as join77 } from "path";
|
|
115084
115371
|
var SKIP_DIRECTORIES2 = new Set(["node_modules", ".git", "dist", "build", ".next", "out"]);
|
|
115085
115372
|
var MAX_SCAN_ENTRIES = 500;
|
|
115086
115373
|
function inferExtensionFromDirectory(directory) {
|
|
@@ -115091,14 +115378,14 @@ function inferExtensionFromDirectory(directory) {
|
|
|
115091
115378
|
return;
|
|
115092
115379
|
let entries;
|
|
115093
115380
|
try {
|
|
115094
|
-
entries =
|
|
115381
|
+
entries = readdirSync19(dir);
|
|
115095
115382
|
} catch {
|
|
115096
115383
|
return;
|
|
115097
115384
|
}
|
|
115098
115385
|
for (const entry of entries) {
|
|
115099
115386
|
if (scanned >= MAX_SCAN_ENTRIES)
|
|
115100
115387
|
return;
|
|
115101
|
-
const fullPath =
|
|
115388
|
+
const fullPath = join77(dir, entry);
|
|
115102
115389
|
let stat;
|
|
115103
115390
|
try {
|
|
115104
115391
|
stat = lstatSync3(fullPath);
|
|
@@ -115263,12 +115550,12 @@ var DEFAULT_MAX_MATCHES = 500;
|
|
|
115263
115550
|
|
|
115264
115551
|
// src/tools/ast-grep/sg-cli-path.ts
|
|
115265
115552
|
import { createRequire as createRequire4 } from "module";
|
|
115266
|
-
import { dirname as dirname23, join as
|
|
115553
|
+
import { dirname as dirname23, join as join79 } from "path";
|
|
115267
115554
|
import { existsSync as existsSync73, statSync as statSync10 } from "fs";
|
|
115268
115555
|
|
|
115269
115556
|
// src/tools/ast-grep/downloader.ts
|
|
115270
115557
|
import { existsSync as existsSync72 } from "fs";
|
|
115271
|
-
import { join as
|
|
115558
|
+
import { join as join78 } from "path";
|
|
115272
115559
|
import { homedir as homedir14 } from "os";
|
|
115273
115560
|
import { createRequire as createRequire3 } from "module";
|
|
115274
115561
|
init_logger();
|
|
@@ -115295,12 +115582,12 @@ var PLATFORM_MAP2 = {
|
|
|
115295
115582
|
function getCacheDir3() {
|
|
115296
115583
|
if (process.platform === "win32") {
|
|
115297
115584
|
const localAppData = process.env.LOCALAPPDATA || process.env.APPDATA;
|
|
115298
|
-
const base2 = localAppData ||
|
|
115299
|
-
return
|
|
115585
|
+
const base2 = localAppData || join78(homedir14(), "AppData", "Local");
|
|
115586
|
+
return join78(base2, "oh-my-opencode", "bin");
|
|
115300
115587
|
}
|
|
115301
115588
|
const xdgCache = process.env.XDG_CACHE_HOME;
|
|
115302
|
-
const base = xdgCache ||
|
|
115303
|
-
return
|
|
115589
|
+
const base = xdgCache || join78(homedir14(), ".cache");
|
|
115590
|
+
return join78(base, "oh-my-opencode", "bin");
|
|
115304
115591
|
}
|
|
115305
115592
|
function getBinaryName3() {
|
|
115306
115593
|
return process.platform === "win32" ? "sg.exe" : "sg";
|
|
@@ -115317,7 +115604,7 @@ async function downloadAstGrep(version3 = DEFAULT_VERSION) {
|
|
|
115317
115604
|
}
|
|
115318
115605
|
const cacheDir = getCacheDir3();
|
|
115319
115606
|
const binaryName = getBinaryName3();
|
|
115320
|
-
const binaryPath =
|
|
115607
|
+
const binaryPath = join78(cacheDir, binaryName);
|
|
115321
115608
|
if (existsSync72(binaryPath)) {
|
|
115322
115609
|
return binaryPath;
|
|
115323
115610
|
}
|
|
@@ -115326,7 +115613,7 @@ async function downloadAstGrep(version3 = DEFAULT_VERSION) {
|
|
|
115326
115613
|
const downloadUrl = `https://github.com/${REPO2}/releases/download/${version3}/${assetName}`;
|
|
115327
115614
|
log(`[oh-my-opencode] Downloading ast-grep binary...`);
|
|
115328
115615
|
try {
|
|
115329
|
-
const archivePath =
|
|
115616
|
+
const archivePath = join78(cacheDir, assetName);
|
|
115330
115617
|
ensureCacheDir(cacheDir);
|
|
115331
115618
|
await downloadArchive(downloadUrl, archivePath);
|
|
115332
115619
|
await extractZipArchive(archivePath, cacheDir);
|
|
@@ -115380,7 +115667,7 @@ function findSgCliPathSync() {
|
|
|
115380
115667
|
const require2 = createRequire4(import.meta.url);
|
|
115381
115668
|
const cliPackageJsonPath = require2.resolve("@ast-grep/cli/package.json");
|
|
115382
115669
|
const cliDirectory = dirname23(cliPackageJsonPath);
|
|
115383
|
-
const sgPath =
|
|
115670
|
+
const sgPath = join79(cliDirectory, binaryName);
|
|
115384
115671
|
if (existsSync73(sgPath) && isValidBinary(sgPath)) {
|
|
115385
115672
|
return sgPath;
|
|
115386
115673
|
}
|
|
@@ -115392,7 +115679,7 @@ function findSgCliPathSync() {
|
|
|
115392
115679
|
const packageJsonPath = require2.resolve(`${platformPackage}/package.json`);
|
|
115393
115680
|
const packageDirectory = dirname23(packageJsonPath);
|
|
115394
115681
|
const astGrepBinaryName = process.platform === "win32" ? "ast-grep.exe" : "ast-grep";
|
|
115395
|
-
const binaryPath =
|
|
115682
|
+
const binaryPath = join79(packageDirectory, astGrepBinaryName);
|
|
115396
115683
|
if (existsSync73(binaryPath) && isValidBinary(binaryPath)) {
|
|
115397
115684
|
return binaryPath;
|
|
115398
115685
|
}
|
|
@@ -115794,18 +116081,18 @@ var {spawn: spawn18 } = globalThis.Bun;
|
|
|
115794
116081
|
|
|
115795
116082
|
// src/tools/grep/constants.ts
|
|
115796
116083
|
import { existsSync as existsSync77 } from "fs";
|
|
115797
|
-
import { join as
|
|
116084
|
+
import { join as join81, dirname as dirname24 } from "path";
|
|
115798
116085
|
import { spawnSync as spawnSync4 } from "child_process";
|
|
115799
116086
|
|
|
115800
116087
|
// src/tools/grep/downloader.ts
|
|
115801
|
-
import { existsSync as existsSync76, readdirSync as
|
|
115802
|
-
import { join as
|
|
116088
|
+
import { existsSync as existsSync76, readdirSync as readdirSync20 } from "fs";
|
|
116089
|
+
import { join as join80 } from "path";
|
|
115803
116090
|
function findFileRecursive(dir, filename) {
|
|
115804
116091
|
try {
|
|
115805
|
-
const entries =
|
|
116092
|
+
const entries = readdirSync20(dir, { withFileTypes: true, recursive: true });
|
|
115806
116093
|
for (const entry of entries) {
|
|
115807
116094
|
if (entry.isFile() && entry.name === filename) {
|
|
115808
|
-
return
|
|
116095
|
+
return join80(entry.parentPath ?? dir, entry.name);
|
|
115809
116096
|
}
|
|
115810
116097
|
}
|
|
115811
116098
|
} catch {
|
|
@@ -115826,11 +116113,11 @@ function getPlatformKey() {
|
|
|
115826
116113
|
}
|
|
115827
116114
|
function getInstallDir() {
|
|
115828
116115
|
const homeDir = process.env.HOME || process.env.USERPROFILE || ".";
|
|
115829
|
-
return
|
|
116116
|
+
return join80(homeDir, ".cache", "oh-my-opencode", "bin");
|
|
115830
116117
|
}
|
|
115831
116118
|
function getRgPath() {
|
|
115832
116119
|
const isWindows2 = process.platform === "win32";
|
|
115833
|
-
return
|
|
116120
|
+
return join80(getInstallDir(), isWindows2 ? "rg.exe" : "rg");
|
|
115834
116121
|
}
|
|
115835
116122
|
async function extractTarGz2(archivePath, destDir) {
|
|
115836
116123
|
const platformKey = getPlatformKey();
|
|
@@ -115847,7 +116134,7 @@ async function extractZip2(archivePath, destDir) {
|
|
|
115847
116134
|
const binaryName = process.platform === "win32" ? "rg.exe" : "rg";
|
|
115848
116135
|
const foundPath = findFileRecursive(destDir, binaryName);
|
|
115849
116136
|
if (foundPath) {
|
|
115850
|
-
const destPath =
|
|
116137
|
+
const destPath = join80(destDir, binaryName);
|
|
115851
116138
|
if (foundPath !== destPath) {
|
|
115852
116139
|
const { renameSync: renameSync5 } = await import("fs");
|
|
115853
116140
|
renameSync5(foundPath, destPath);
|
|
@@ -115868,7 +116155,7 @@ async function downloadAndInstallRipgrep() {
|
|
|
115868
116155
|
ensureCacheDir(installDir);
|
|
115869
116156
|
const filename = `ripgrep-${RG_VERSION}-${config4.platform}.${config4.extension}`;
|
|
115870
116157
|
const url3 = `https://github.com/BurntSushi/ripgrep/releases/download/${RG_VERSION}/${filename}`;
|
|
115871
|
-
const archivePath =
|
|
116158
|
+
const archivePath = join80(installDir, filename);
|
|
115872
116159
|
try {
|
|
115873
116160
|
await downloadArchive(url3, archivePath);
|
|
115874
116161
|
if (config4.extension === "tar.gz") {
|
|
@@ -115914,11 +116201,11 @@ function getOpenCodeBundledRg() {
|
|
|
115914
116201
|
const isWindows2 = process.platform === "win32";
|
|
115915
116202
|
const rgName = isWindows2 ? "rg.exe" : "rg";
|
|
115916
116203
|
const candidates = [
|
|
115917
|
-
|
|
115918
|
-
|
|
115919
|
-
|
|
115920
|
-
|
|
115921
|
-
|
|
116204
|
+
join81(getDataDir(), "opencode", "bin", rgName),
|
|
116205
|
+
join81(execDir, rgName),
|
|
116206
|
+
join81(execDir, "bin", rgName),
|
|
116207
|
+
join81(execDir, "..", "bin", rgName),
|
|
116208
|
+
join81(execDir, "..", "libexec", rgName)
|
|
115922
116209
|
];
|
|
115923
116210
|
for (const candidate of candidates) {
|
|
115924
116211
|
if (existsSync77(candidate)) {
|
|
@@ -117030,9 +117317,9 @@ function createSkillTool(options = {}) {
|
|
|
117030
117317
|
}
|
|
117031
117318
|
var skill = createSkillTool();
|
|
117032
117319
|
// src/tools/session-manager/constants.ts
|
|
117033
|
-
import { join as
|
|
117034
|
-
var TODO_DIR2 =
|
|
117035
|
-
var TRANSCRIPT_DIR2 =
|
|
117320
|
+
import { join as join82 } from "path";
|
|
117321
|
+
var TODO_DIR2 = join82(getClaudeConfigDir(), "todos");
|
|
117322
|
+
var TRANSCRIPT_DIR2 = join82(getClaudeConfigDir(), "transcripts");
|
|
117036
117323
|
var SESSION_LIST_DESCRIPTION = `List all OpenCode sessions with optional filtering.
|
|
117037
117324
|
|
|
117038
117325
|
Returns a list of available session IDs with metadata including message count, date range, and agents used.
|
|
@@ -117107,7 +117394,7 @@ Has Transcript: Yes (234 entries)`;
|
|
|
117107
117394
|
// src/tools/session-manager/file-storage.ts
|
|
117108
117395
|
import { existsSync as existsSync78 } from "fs";
|
|
117109
117396
|
import { readdir, readFile } from "fs/promises";
|
|
117110
|
-
import { join as
|
|
117397
|
+
import { join as join83 } from "path";
|
|
117111
117398
|
async function getFileMainSessions(directory) {
|
|
117112
117399
|
if (!existsSync78(SESSION_STORAGE))
|
|
117113
117400
|
return [];
|
|
@@ -117117,13 +117404,13 @@ async function getFileMainSessions(directory) {
|
|
|
117117
117404
|
for (const projectDir of projectDirs) {
|
|
117118
117405
|
if (!projectDir.isDirectory())
|
|
117119
117406
|
continue;
|
|
117120
|
-
const projectPath =
|
|
117407
|
+
const projectPath = join83(SESSION_STORAGE, projectDir.name);
|
|
117121
117408
|
const sessionFiles = await readdir(projectPath);
|
|
117122
117409
|
for (const file3 of sessionFiles) {
|
|
117123
117410
|
if (!file3.endsWith(".json"))
|
|
117124
117411
|
continue;
|
|
117125
117412
|
try {
|
|
117126
|
-
const content = await readFile(
|
|
117413
|
+
const content = await readFile(join83(projectPath, file3), "utf-8");
|
|
117127
117414
|
const meta3 = JSON.parse(content);
|
|
117128
117415
|
if (meta3.parentID)
|
|
117129
117416
|
continue;
|
|
@@ -117150,7 +117437,7 @@ async function getFileAllSessions() {
|
|
|
117150
117437
|
for (const entry of entries) {
|
|
117151
117438
|
if (!entry.isDirectory())
|
|
117152
117439
|
continue;
|
|
117153
|
-
const sessionPath =
|
|
117440
|
+
const sessionPath = join83(dir, entry.name);
|
|
117154
117441
|
const files = await readdir(sessionPath);
|
|
117155
117442
|
if (files.some((file3) => file3.endsWith(".json"))) {
|
|
117156
117443
|
sessions.push(entry.name);
|
|
@@ -117179,7 +117466,7 @@ async function getFileSessionMessages(sessionID) {
|
|
|
117179
117466
|
if (!file3.endsWith(".json"))
|
|
117180
117467
|
continue;
|
|
117181
117468
|
try {
|
|
117182
|
-
const content = await readFile(
|
|
117469
|
+
const content = await readFile(join83(messageDir, file3), "utf-8");
|
|
117183
117470
|
const meta3 = JSON.parse(content);
|
|
117184
117471
|
const parts = await readParts2(meta3.id);
|
|
117185
117472
|
messages.push({
|
|
@@ -117205,7 +117492,7 @@ async function getFileSessionMessages(sessionID) {
|
|
|
117205
117492
|
});
|
|
117206
117493
|
}
|
|
117207
117494
|
async function readParts2(messageID) {
|
|
117208
|
-
const partDir =
|
|
117495
|
+
const partDir = join83(PART_STORAGE, messageID);
|
|
117209
117496
|
if (!existsSync78(partDir))
|
|
117210
117497
|
return [];
|
|
117211
117498
|
const parts = [];
|
|
@@ -117215,7 +117502,7 @@ async function readParts2(messageID) {
|
|
|
117215
117502
|
if (!file3.endsWith(".json"))
|
|
117216
117503
|
continue;
|
|
117217
117504
|
try {
|
|
117218
|
-
const content = await readFile(
|
|
117505
|
+
const content = await readFile(join83(partDir, file3), "utf-8");
|
|
117219
117506
|
parts.push(JSON.parse(content));
|
|
117220
117507
|
} catch {
|
|
117221
117508
|
continue;
|
|
@@ -117234,7 +117521,7 @@ async function getFileSessionTodos(sessionID) {
|
|
|
117234
117521
|
const todoFiles = allFiles.filter((file3) => file3 === `${sessionID}.json`);
|
|
117235
117522
|
for (const file3 of todoFiles) {
|
|
117236
117523
|
try {
|
|
117237
|
-
const content = await readFile(
|
|
117524
|
+
const content = await readFile(join83(TODO_DIR2, file3), "utf-8");
|
|
117238
117525
|
const data = JSON.parse(content);
|
|
117239
117526
|
if (!Array.isArray(data))
|
|
117240
117527
|
continue;
|
|
@@ -117256,7 +117543,7 @@ async function getFileSessionTodos(sessionID) {
|
|
|
117256
117543
|
async function getFileSessionTranscript(sessionID) {
|
|
117257
117544
|
if (!existsSync78(TRANSCRIPT_DIR2))
|
|
117258
117545
|
return 0;
|
|
117259
|
-
const transcriptFile =
|
|
117546
|
+
const transcriptFile = join83(TRANSCRIPT_DIR2, `${sessionID}.jsonl`);
|
|
117260
117547
|
if (!existsSync78(transcriptFile))
|
|
117261
117548
|
return 0;
|
|
117262
117549
|
try {
|
|
@@ -117509,7 +117796,48 @@ async function readSessionTodos(sessionID) {
|
|
|
117509
117796
|
}
|
|
117510
117797
|
return getFileSessionTodos(sessionID);
|
|
117511
117798
|
}
|
|
117799
|
+
async function readSessionTranscript(sessionID) {
|
|
117800
|
+
return getFileSessionTranscript(sessionID);
|
|
117801
|
+
}
|
|
117512
117802
|
async function getSessionInfo(sessionID) {
|
|
117803
|
+
if (isSqliteBackend() && sdkClient) {
|
|
117804
|
+
try {
|
|
117805
|
+
const sdkMessages = await getSdkSessionMessages(sdkClient, sessionID);
|
|
117806
|
+
if (sdkMessages.length > 0) {
|
|
117807
|
+
const agentsUsed = new Set;
|
|
117808
|
+
let firstMessage;
|
|
117809
|
+
let lastMessage;
|
|
117810
|
+
for (const msg of sdkMessages) {
|
|
117811
|
+
if (msg.agent)
|
|
117812
|
+
agentsUsed.add(msg.agent);
|
|
117813
|
+
if (msg.time?.created) {
|
|
117814
|
+
const date9 = new Date(msg.time.created);
|
|
117815
|
+
if (!firstMessage || date9 < firstMessage)
|
|
117816
|
+
firstMessage = date9;
|
|
117817
|
+
if (!lastMessage || date9 > lastMessage)
|
|
117818
|
+
lastMessage = date9;
|
|
117819
|
+
}
|
|
117820
|
+
}
|
|
117821
|
+
const todos = await readSessionTodos(sessionID);
|
|
117822
|
+
const transcriptEntries = await readSessionTranscript(sessionID);
|
|
117823
|
+
return {
|
|
117824
|
+
id: sessionID,
|
|
117825
|
+
message_count: sdkMessages.length,
|
|
117826
|
+
first_message: firstMessage,
|
|
117827
|
+
last_message: lastMessage,
|
|
117828
|
+
agents_used: Array.from(agentsUsed),
|
|
117829
|
+
has_todos: todos.length > 0,
|
|
117830
|
+
has_transcript: transcriptEntries > 0,
|
|
117831
|
+
todos,
|
|
117832
|
+
transcript_entries: transcriptEntries
|
|
117833
|
+
};
|
|
117834
|
+
}
|
|
117835
|
+
} catch (error92) {
|
|
117836
|
+
if (!shouldFallbackFromSdkError(error92))
|
|
117837
|
+
throw error92;
|
|
117838
|
+
log("[session-manager] falling back to file session info after SDK unavailable error", { error: String(error92), sessionID });
|
|
117839
|
+
}
|
|
117840
|
+
}
|
|
117513
117841
|
return getFileSessionInfo(sessionID);
|
|
117514
117842
|
}
|
|
117515
117843
|
|
|
@@ -119490,9 +119818,9 @@ async function resolveMultimodalLookerAgentMetadata(ctx) {
|
|
|
119490
119818
|
|
|
119491
119819
|
// src/tools/look-at/image-converter.ts
|
|
119492
119820
|
import * as childProcess from "child_process";
|
|
119493
|
-
import { existsSync as existsSync79, mkdtempSync, readFileSync as
|
|
119821
|
+
import { existsSync as existsSync79, mkdtempSync, readFileSync as readFileSync52, rmSync as rmSync4, unlinkSync as unlinkSync11, writeFileSync as writeFileSync20 } from "fs";
|
|
119494
119822
|
import { tmpdir as tmpdir7 } from "os";
|
|
119495
|
-
import { dirname as dirname27, join as
|
|
119823
|
+
import { dirname as dirname27, join as join84 } from "path";
|
|
119496
119824
|
var SUPPORTED_FORMATS = new Set([
|
|
119497
119825
|
"image/jpeg",
|
|
119498
119826
|
"image/png",
|
|
@@ -119533,8 +119861,8 @@ function convertImageToJpeg(inputPath, mimeType) {
|
|
|
119533
119861
|
if (!existsSync79(inputPath)) {
|
|
119534
119862
|
throw new Error(`File not found: ${inputPath}`);
|
|
119535
119863
|
}
|
|
119536
|
-
const tempDir = mkdtempSync(
|
|
119537
|
-
const outputPath =
|
|
119864
|
+
const tempDir = mkdtempSync(join84(tmpdir7(), "opencode-img-"));
|
|
119865
|
+
const outputPath = join84(tempDir, "converted.jpg");
|
|
119538
119866
|
log(`[image-converter] Converting ${mimeType} to JPEG: ${inputPath}`);
|
|
119539
119867
|
try {
|
|
119540
119868
|
if (process.platform === "darwin") {
|
|
@@ -119599,9 +119927,9 @@ function cleanupConvertedImage(filePath) {
|
|
|
119599
119927
|
}
|
|
119600
119928
|
}
|
|
119601
119929
|
function convertBase64ImageToJpeg(base64Data, mimeType) {
|
|
119602
|
-
const tempDir = mkdtempSync(
|
|
119930
|
+
const tempDir = mkdtempSync(join84(tmpdir7(), "opencode-b64-"));
|
|
119603
119931
|
const inputExt = mimeType.split("/")[1] || "bin";
|
|
119604
|
-
const inputPath =
|
|
119932
|
+
const inputPath = join84(tempDir, `input.${inputExt}`);
|
|
119605
119933
|
const tempFiles = [inputPath];
|
|
119606
119934
|
try {
|
|
119607
119935
|
const cleanBase64 = base64Data.replace(/^data:[^;]+;base64,/, "");
|
|
@@ -119610,7 +119938,7 @@ function convertBase64ImageToJpeg(base64Data, mimeType) {
|
|
|
119610
119938
|
log(`[image-converter] Converting Base64 ${mimeType} to JPEG`);
|
|
119611
119939
|
const outputPath = convertImageToJpeg(inputPath, mimeType);
|
|
119612
119940
|
tempFiles.push(outputPath);
|
|
119613
|
-
const convertedBuffer =
|
|
119941
|
+
const convertedBuffer = readFileSync52(outputPath);
|
|
119614
119942
|
const convertedBase64 = convertedBuffer.toString("base64");
|
|
119615
119943
|
log(`[image-converter] Base64 conversion successful`);
|
|
119616
119944
|
return { base64: convertedBase64, tempFiles };
|
|
@@ -120062,6 +120390,11 @@ function formatDetailedError(error92, ctx) {
|
|
|
120062
120390
|
`);
|
|
120063
120391
|
}
|
|
120064
120392
|
|
|
120393
|
+
// src/tools/delegate-task/resolve-call-id.ts
|
|
120394
|
+
function resolveCallID(ctx) {
|
|
120395
|
+
return ctx.callID ?? ctx.callId ?? ctx.call_id;
|
|
120396
|
+
}
|
|
120397
|
+
|
|
120065
120398
|
// src/tools/delegate-task/background-continuation.ts
|
|
120066
120399
|
async function executeBackgroundContinuation(args, ctx, executorCtx, parentContext) {
|
|
120067
120400
|
const { manager } = executorCtx;
|
|
@@ -120089,8 +120422,9 @@ async function executeBackgroundContinuation(args, ctx, executorCtx, parentConte
|
|
|
120089
120422
|
}
|
|
120090
120423
|
};
|
|
120091
120424
|
await ctx.metadata?.(bgContMeta);
|
|
120092
|
-
|
|
120093
|
-
|
|
120425
|
+
const callID = resolveCallID(ctx);
|
|
120426
|
+
if (callID) {
|
|
120427
|
+
storeToolMetadata(ctx.sessionID, callID, bgContMeta);
|
|
120094
120428
|
}
|
|
120095
120429
|
return `Background task continued.
|
|
120096
120430
|
|
|
@@ -120409,8 +120743,9 @@ async function executeSyncContinuation(args, ctx, executorCtx, deps = syncContin
|
|
|
120409
120743
|
}
|
|
120410
120744
|
};
|
|
120411
120745
|
await ctx.metadata?.(syncContMeta);
|
|
120412
|
-
|
|
120413
|
-
|
|
120746
|
+
const callID = resolveCallID(ctx);
|
|
120747
|
+
if (callID) {
|
|
120748
|
+
storeToolMetadata(ctx.sessionID, callID, syncContMeta);
|
|
120414
120749
|
}
|
|
120415
120750
|
const allowTask = isPlanFamily(resumeAgent);
|
|
120416
120751
|
const tddEnabled = sisyphusAgentConfig?.tdd;
|
|
@@ -120554,8 +120889,9 @@ Task ID: ${task.id}`;
|
|
|
120554
120889
|
}
|
|
120555
120890
|
};
|
|
120556
120891
|
await ctx.metadata?.(bgTaskMeta);
|
|
120557
|
-
|
|
120558
|
-
|
|
120892
|
+
const callID = resolveCallID(ctx);
|
|
120893
|
+
if (callID) {
|
|
120894
|
+
storeToolMetadata(ctx.sessionID, callID, bgTaskMeta);
|
|
120559
120895
|
}
|
|
120560
120896
|
const startTime = new Date;
|
|
120561
120897
|
const timingCfg = getTimingConfig();
|
|
@@ -120797,8 +121133,9 @@ Task ID: ${task.id}`;
|
|
|
120797
121133
|
metadata
|
|
120798
121134
|
};
|
|
120799
121135
|
await ctx.metadata?.(unstableMeta);
|
|
120800
|
-
|
|
120801
|
-
|
|
121136
|
+
const callID = resolveCallID(ctx);
|
|
121137
|
+
if (callID) {
|
|
121138
|
+
storeToolMetadata(ctx.sessionID, callID, unstableMeta);
|
|
120802
121139
|
}
|
|
120803
121140
|
const taskMetadataBlock = sessionId ? `
|
|
120804
121141
|
|
|
@@ -121026,8 +121363,9 @@ async function executeSyncTask(args, ctx, executorCtx, parentContext, agentToUse
|
|
|
121026
121363
|
}
|
|
121027
121364
|
};
|
|
121028
121365
|
await ctx.metadata?.(syncTaskMeta);
|
|
121029
|
-
|
|
121030
|
-
|
|
121366
|
+
const callID = resolveCallID(ctx);
|
|
121367
|
+
if (callID) {
|
|
121368
|
+
storeToolMetadata(ctx.sessionID, callID, syncTaskMeta);
|
|
121031
121369
|
}
|
|
121032
121370
|
const promptError = await deps.sendSyncPrompt(client2, {
|
|
121033
121371
|
sessionID,
|
|
@@ -121505,7 +121843,7 @@ Available categories: ${categoryNames.join(", ")}`
|
|
|
121505
121843
|
};
|
|
121506
121844
|
}
|
|
121507
121845
|
const resolvedModel = actualModel?.toLowerCase();
|
|
121508
|
-
const isUnstableAgent = resolved.config.is_unstable_agent ?? (resolvedModel ? resolvedModel.includes("gemini") || resolvedModel.includes("minimax")
|
|
121846
|
+
const isUnstableAgent = resolved.config.is_unstable_agent ?? (resolvedModel ? resolvedModel.includes("gemini") || resolvedModel.includes("minimax") : false);
|
|
121509
121847
|
const defaultProviderID = categoryModel?.providerID ?? parseModelString(actualModel ?? "")?.providerID ?? "opencode";
|
|
121510
121848
|
const configuredFallbackChain = buildFallbackChainFromModels(normalizedConfiguredFallbackModels, defaultProviderID);
|
|
121511
121849
|
const effectiveEntry = matchedFallback && categoryModel ? fallbackEntry ?? (configuredFallbackChain ? findMostSpecificFallbackEntry(categoryModel.providerID, categoryModel.modelID, configuredFallbackChain) : undefined) : undefined;
|
|
@@ -121769,6 +122107,9 @@ function createDelegateTask(options) {
|
|
|
121769
122107
|
await ctx.metadata?.({
|
|
121770
122108
|
title: args.description
|
|
121771
122109
|
});
|
|
122110
|
+
if (!args.description || typeof args.description !== "string") {
|
|
122111
|
+
throw new Error(`Invalid arguments: 'description' parameter is REQUIRED. Provide a short (3-5 words) task description.`);
|
|
122112
|
+
}
|
|
121772
122113
|
if (args.run_in_background === undefined) {
|
|
121773
122114
|
throw new Error(`Invalid arguments: 'run_in_background' parameter is REQUIRED. Specify run_in_background=false for task delegation, or run_in_background=true for parallel exploration.`);
|
|
121774
122115
|
}
|
|
@@ -121888,7 +122229,7 @@ function createDelegateTask(options) {
|
|
|
121888
122229
|
// src/tools/delegate-task/index.ts
|
|
121889
122230
|
init_constants();
|
|
121890
122231
|
// src/tools/task/task-create.ts
|
|
121891
|
-
import { join as
|
|
122232
|
+
import { join as join86 } from "path";
|
|
121892
122233
|
|
|
121893
122234
|
// src/tools/task/types.ts
|
|
121894
122235
|
var TaskStatusSchema = exports_external.enum(["pending", "in_progress", "completed", "deleted"]);
|
|
@@ -121942,18 +122283,18 @@ var TaskDeleteInputSchema = exports_external.object({
|
|
|
121942
122283
|
});
|
|
121943
122284
|
|
|
121944
122285
|
// src/features/claude-tasks/storage.ts
|
|
121945
|
-
import { join as
|
|
121946
|
-
import { existsSync as existsSync80, mkdirSync as mkdirSync16, readFileSync as
|
|
122286
|
+
import { join as join85, dirname as dirname28, basename as basename13, isAbsolute as isAbsolute11 } from "path";
|
|
122287
|
+
import { existsSync as existsSync80, mkdirSync as mkdirSync16, readFileSync as readFileSync53, writeFileSync as writeFileSync21, renameSync as renameSync5, unlinkSync as unlinkSync12, readdirSync as readdirSync21 } from "fs";
|
|
121947
122288
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
121948
122289
|
function getTaskDir(config4 = {}) {
|
|
121949
122290
|
const tasksConfig = config4.sisyphus?.tasks;
|
|
121950
122291
|
const storagePath = tasksConfig?.storage_path;
|
|
121951
122292
|
if (storagePath) {
|
|
121952
|
-
return isAbsolute11(storagePath) ? storagePath :
|
|
122293
|
+
return isAbsolute11(storagePath) ? storagePath : join85(process.cwd(), storagePath);
|
|
121953
122294
|
}
|
|
121954
122295
|
const configDir = getOpenCodeConfigDir({ binary: "opencode" });
|
|
121955
122296
|
const listId = resolveTaskListId(config4);
|
|
121956
|
-
return
|
|
122297
|
+
return join85(configDir, "tasks", listId);
|
|
121957
122298
|
}
|
|
121958
122299
|
function sanitizePathSegment(value) {
|
|
121959
122300
|
return value.replace(/[^a-zA-Z0-9_-]/g, "-") || "default";
|
|
@@ -121980,7 +122321,7 @@ function readJsonSafe(filePath, schema2) {
|
|
|
121980
122321
|
if (!existsSync80(filePath)) {
|
|
121981
122322
|
return null;
|
|
121982
122323
|
}
|
|
121983
|
-
const content =
|
|
122324
|
+
const content = readFileSync53(filePath, "utf-8");
|
|
121984
122325
|
const parsed = JSON.parse(content);
|
|
121985
122326
|
const result = schema2.safeParse(parsed);
|
|
121986
122327
|
if (!result.success) {
|
|
@@ -122012,7 +122353,7 @@ function generateTaskId() {
|
|
|
122012
122353
|
return `T-${randomUUID3()}`;
|
|
122013
122354
|
}
|
|
122014
122355
|
function acquireLock(dirPath) {
|
|
122015
|
-
const lockPath =
|
|
122356
|
+
const lockPath = join85(dirPath, ".lock");
|
|
122016
122357
|
const lockId = randomUUID3();
|
|
122017
122358
|
const createLock = (timestamp2) => {
|
|
122018
122359
|
writeFileSync21(lockPath, JSON.stringify({ id: lockId, timestamp: timestamp2 }), {
|
|
@@ -122022,7 +122363,7 @@ function acquireLock(dirPath) {
|
|
|
122022
122363
|
};
|
|
122023
122364
|
const isStale = () => {
|
|
122024
122365
|
try {
|
|
122025
|
-
const lockContent =
|
|
122366
|
+
const lockContent = readFileSync53(lockPath, "utf-8");
|
|
122026
122367
|
const lockData = JSON.parse(lockContent);
|
|
122027
122368
|
const lockAge = Date.now() - lockData.timestamp;
|
|
122028
122369
|
return lockAge > STALE_LOCK_THRESHOLD_MS;
|
|
@@ -122062,7 +122403,7 @@ function acquireLock(dirPath) {
|
|
|
122062
122403
|
try {
|
|
122063
122404
|
if (!existsSync80(lockPath))
|
|
122064
122405
|
return;
|
|
122065
|
-
const lockContent =
|
|
122406
|
+
const lockContent = readFileSync53(lockPath, "utf-8");
|
|
122066
122407
|
const lockData = JSON.parse(lockContent);
|
|
122067
122408
|
if (lockData.id !== lockId)
|
|
122068
122409
|
return;
|
|
@@ -122225,7 +122566,7 @@ async function handleCreate(args, config4, ctx, context) {
|
|
|
122225
122566
|
threadID: context.sessionID
|
|
122226
122567
|
};
|
|
122227
122568
|
const validatedTask = TaskObjectSchema.parse(task);
|
|
122228
|
-
writeJsonAtomic(
|
|
122569
|
+
writeJsonAtomic(join86(taskDir, `${taskId}.json`), validatedTask);
|
|
122229
122570
|
await syncTaskTodoUpdate(ctx, validatedTask, context.sessionID);
|
|
122230
122571
|
return JSON.stringify({
|
|
122231
122572
|
task: {
|
|
@@ -122247,7 +122588,7 @@ async function handleCreate(args, config4, ctx, context) {
|
|
|
122247
122588
|
}
|
|
122248
122589
|
}
|
|
122249
122590
|
// src/tools/task/task-get.ts
|
|
122250
|
-
import { join as
|
|
122591
|
+
import { join as join87 } from "path";
|
|
122251
122592
|
var TASK_ID_PATTERN = /^T-[A-Za-z0-9-]+$/;
|
|
122252
122593
|
function parseTaskId(id) {
|
|
122253
122594
|
if (!TASK_ID_PATTERN.test(id))
|
|
@@ -122272,7 +122613,7 @@ Returns null if the task does not exist or the file is invalid.`,
|
|
|
122272
122613
|
return JSON.stringify({ error: "invalid_task_id" });
|
|
122273
122614
|
}
|
|
122274
122615
|
const taskDir = getTaskDir(config4);
|
|
122275
|
-
const taskPath =
|
|
122616
|
+
const taskPath = join87(taskDir, `${taskId}.json`);
|
|
122276
122617
|
const task = readJsonSafe(taskPath, TaskObjectSchema);
|
|
122277
122618
|
return JSON.stringify({ task: task ?? null });
|
|
122278
122619
|
} catch (error92) {
|
|
@@ -122285,8 +122626,8 @@ Returns null if the task does not exist or the file is invalid.`,
|
|
|
122285
122626
|
});
|
|
122286
122627
|
}
|
|
122287
122628
|
// src/tools/task/task-list.ts
|
|
122288
|
-
import { join as
|
|
122289
|
-
import { existsSync as existsSync81, readdirSync as
|
|
122629
|
+
import { join as join88 } from "path";
|
|
122630
|
+
import { existsSync as existsSync81, readdirSync as readdirSync22 } from "fs";
|
|
122290
122631
|
function createTaskList(config4) {
|
|
122291
122632
|
return tool({
|
|
122292
122633
|
description: `List all active tasks with summary information.
|
|
@@ -122300,13 +122641,13 @@ Returns summary format: id, subject, status, owner, blockedBy (not full descript
|
|
|
122300
122641
|
if (!existsSync81(taskDir)) {
|
|
122301
122642
|
return JSON.stringify({ tasks: [] });
|
|
122302
122643
|
}
|
|
122303
|
-
const files =
|
|
122644
|
+
const files = readdirSync22(taskDir).filter((f) => f.endsWith(".json") && f.startsWith("T-")).map((f) => f.replace(".json", ""));
|
|
122304
122645
|
if (files.length === 0) {
|
|
122305
122646
|
return JSON.stringify({ tasks: [] });
|
|
122306
122647
|
}
|
|
122307
122648
|
const allTasks = [];
|
|
122308
122649
|
for (const fileId of files) {
|
|
122309
|
-
const task = readJsonSafe(
|
|
122650
|
+
const task = readJsonSafe(join88(taskDir, `${fileId}.json`), TaskObjectSchema);
|
|
122310
122651
|
if (task) {
|
|
122311
122652
|
allTasks.push(task);
|
|
122312
122653
|
}
|
|
@@ -122334,7 +122675,7 @@ Returns summary format: id, subject, status, owner, blockedBy (not full descript
|
|
|
122334
122675
|
});
|
|
122335
122676
|
}
|
|
122336
122677
|
// src/tools/task/task-update.ts
|
|
122337
|
-
import { join as
|
|
122678
|
+
import { join as join89 } from "path";
|
|
122338
122679
|
var TASK_ID_PATTERN2 = /^T-[A-Za-z0-9-]+$/;
|
|
122339
122680
|
function parseTaskId2(id) {
|
|
122340
122681
|
if (!TASK_ID_PATTERN2.test(id))
|
|
@@ -122382,7 +122723,7 @@ async function handleUpdate(args, config4, ctx, context) {
|
|
|
122382
122723
|
return JSON.stringify({ error: "task_lock_unavailable" });
|
|
122383
122724
|
}
|
|
122384
122725
|
try {
|
|
122385
|
-
const taskPath =
|
|
122726
|
+
const taskPath = join89(taskDir, `${taskId}.json`);
|
|
122386
122727
|
const task = readJsonSafe(taskPath, TaskObjectSchema);
|
|
122387
122728
|
if (!task) {
|
|
122388
122729
|
return JSON.stringify({ error: "task_not_found" });
|
|
@@ -124046,6 +124387,9 @@ var builtinTools = {
|
|
|
124046
124387
|
function isTmuxIntegrationEnabled(pluginConfig) {
|
|
124047
124388
|
return pluginConfig.tmux?.enabled ?? false;
|
|
124048
124389
|
}
|
|
124390
|
+
function isInteractiveBashEnabled(which = Bun.which) {
|
|
124391
|
+
return which("tmux") !== null;
|
|
124392
|
+
}
|
|
124049
124393
|
function createRuntimeTmuxConfig(pluginConfig) {
|
|
124050
124394
|
return TmuxConfigSchema.parse(pluginConfig.tmux ?? {});
|
|
124051
124395
|
}
|
|
@@ -124594,6 +124938,40 @@ function createHooks(args) {
|
|
|
124594
124938
|
}
|
|
124595
124939
|
};
|
|
124596
124940
|
}
|
|
124941
|
+
// src/features/background-agent/constants.ts
|
|
124942
|
+
var TASK_TTL_MS = 30 * 60 * 1000;
|
|
124943
|
+
var TERMINAL_TASK_TTL_MS = 30 * 60 * 1000;
|
|
124944
|
+
var MIN_STABILITY_TIME_MS2 = 10 * 1000;
|
|
124945
|
+
var DEFAULT_STALE_TIMEOUT_MS = 2700000;
|
|
124946
|
+
var DEFAULT_MESSAGE_STALENESS_TIMEOUT_MS = 3600000;
|
|
124947
|
+
var DEFAULT_MAX_TOOL_CALLS = 4000;
|
|
124948
|
+
var DEFAULT_CIRCUIT_BREAKER_CONSECUTIVE_THRESHOLD = 20;
|
|
124949
|
+
var DEFAULT_CIRCUIT_BREAKER_ENABLED = true;
|
|
124950
|
+
var MIN_RUNTIME_BEFORE_STALE_MS = 30000;
|
|
124951
|
+
var DEFAULT_SESSION_GONE_TIMEOUT_MS = 60000;
|
|
124952
|
+
var MIN_IDLE_TIME_MS = 5000;
|
|
124953
|
+
var POLLING_INTERVAL_MS = 3000;
|
|
124954
|
+
var TASK_CLEANUP_DELAY_MS = 10 * 60 * 1000;
|
|
124955
|
+
|
|
124956
|
+
// src/features/background-agent/spawner.ts
|
|
124957
|
+
var FALLBACK_AGENT = "general";
|
|
124958
|
+
function isAgentNotFoundError(error92) {
|
|
124959
|
+
const message = typeof error92 === "string" ? error92 : error92 instanceof Error ? error92.message : typeof error92 === "object" && error92 !== null && typeof error92.message === "string" ? error92.message : String(error92);
|
|
124960
|
+
return message.includes("Agent not found") || message.includes("agent.name");
|
|
124961
|
+
}
|
|
124962
|
+
function buildFallbackBody(originalBody, fallbackAgent) {
|
|
124963
|
+
return {
|
|
124964
|
+
...originalBody,
|
|
124965
|
+
agent: fallbackAgent,
|
|
124966
|
+
tools: {
|
|
124967
|
+
task: false,
|
|
124968
|
+
call_omo_agent: true,
|
|
124969
|
+
question: false,
|
|
124970
|
+
...getAgentToolRestrictions(fallbackAgent)
|
|
124971
|
+
}
|
|
124972
|
+
};
|
|
124973
|
+
}
|
|
124974
|
+
|
|
124597
124975
|
// src/features/background-agent/task-history.ts
|
|
124598
124976
|
var MAX_ENTRIES_PER_PARENT = 100;
|
|
124599
124977
|
|
|
@@ -124751,21 +125129,6 @@ class ConcurrencyManager {
|
|
|
124751
125129
|
}
|
|
124752
125130
|
}
|
|
124753
125131
|
|
|
124754
|
-
// src/features/background-agent/constants.ts
|
|
124755
|
-
var TASK_TTL_MS = 30 * 60 * 1000;
|
|
124756
|
-
var TERMINAL_TASK_TTL_MS = 30 * 60 * 1000;
|
|
124757
|
-
var MIN_STABILITY_TIME_MS2 = 10 * 1000;
|
|
124758
|
-
var DEFAULT_STALE_TIMEOUT_MS = 2700000;
|
|
124759
|
-
var DEFAULT_MESSAGE_STALENESS_TIMEOUT_MS = 3600000;
|
|
124760
|
-
var DEFAULT_MAX_TOOL_CALLS = 4000;
|
|
124761
|
-
var DEFAULT_CIRCUIT_BREAKER_CONSECUTIVE_THRESHOLD = 20;
|
|
124762
|
-
var DEFAULT_CIRCUIT_BREAKER_ENABLED = true;
|
|
124763
|
-
var MIN_RUNTIME_BEFORE_STALE_MS = 30000;
|
|
124764
|
-
var DEFAULT_SESSION_GONE_TIMEOUT_MS = 60000;
|
|
124765
|
-
var MIN_IDLE_TIME_MS = 5000;
|
|
124766
|
-
var POLLING_INTERVAL_MS = 3000;
|
|
124767
|
-
var TASK_CLEANUP_DELAY_MS = 10 * 60 * 1000;
|
|
124768
|
-
|
|
124769
125132
|
// src/features/background-agent/duration-formatter.ts
|
|
124770
125133
|
function formatDuration3(start, end) {
|
|
124771
125134
|
const duration5 = (end ?? new Date).getTime() - start.getTime();
|
|
@@ -124784,14 +125147,15 @@ function formatDuration3(start, end) {
|
|
|
124784
125147
|
// src/features/background-agent/background-task-notification-template.ts
|
|
124785
125148
|
function buildBackgroundTaskNotificationText(input) {
|
|
124786
125149
|
const { task, duration: duration5, statusText, allComplete, remainingCount, completedTasks } = input;
|
|
125150
|
+
const safeDescription = (t) => t.description || t.id;
|
|
124787
125151
|
const errorInfo = task.error ? `
|
|
124788
125152
|
**Error:** ${task.error}` : "";
|
|
124789
125153
|
if (allComplete) {
|
|
124790
125154
|
const succeededTasks = completedTasks.filter((t) => t.status === "completed");
|
|
124791
125155
|
const failedTasks = completedTasks.filter((t) => t.status !== "completed");
|
|
124792
|
-
const succeededText = succeededTasks.length > 0 ? succeededTasks.map((t) => `- \`${t.id}\`: ${t
|
|
125156
|
+
const succeededText = succeededTasks.length > 0 ? succeededTasks.map((t) => `- \`${t.id}\`: ${safeDescription(t)}`).join(`
|
|
124793
125157
|
`) : "";
|
|
124794
|
-
const failedText = failedTasks.length > 0 ? failedTasks.map((t) => `- \`${t.id}\`: ${t
|
|
125158
|
+
const failedText = failedTasks.length > 0 ? failedTasks.map((t) => `- \`${t.id}\`: ${safeDescription(t)} [${t.status.toUpperCase()}]${t.error ? ` - ${t.error}` : ""}`).join(`
|
|
124795
125159
|
`) : "";
|
|
124796
125160
|
const hasFailures = failedTasks.length > 0;
|
|
124797
125161
|
const header = hasFailures ? `[ALL BACKGROUND TASKS FINISHED - ${failedTasks.length} FAILED]` : "[ALL BACKGROUND TASKS COMPLETE]";
|
|
@@ -124808,7 +125172,7 @@ ${failedText}
|
|
|
124808
125172
|
`;
|
|
124809
125173
|
}
|
|
124810
125174
|
if (!body) {
|
|
124811
|
-
body = `- \`${task.id}\`: ${task
|
|
125175
|
+
body = `- \`${task.id}\`: ${safeDescription(task)} [${task.status.toUpperCase()}]${task.error ? ` - ${task.error}` : ""}
|
|
124812
125176
|
`;
|
|
124813
125177
|
}
|
|
124814
125178
|
return `<system-reminder>
|
|
@@ -124825,7 +125189,7 @@ Use \`background_output(task_id="<id>")\` to retrieve each result.${hasFailures
|
|
|
124825
125189
|
return `<system-reminder>
|
|
124826
125190
|
[BACKGROUND TASK ${statusText}]
|
|
124827
125191
|
**ID:** \`${task.id}\`
|
|
124828
|
-
**Description:** ${task
|
|
125192
|
+
**Description:** ${safeDescription(task)}
|
|
124829
125193
|
**Duration:** ${duration5}${errorInfo}
|
|
124830
125194
|
|
|
124831
125195
|
**${remainingCount} task${remainingCount === 1 ? "" : "s"} still in progress.** You WILL be notified when ALL complete.
|
|
@@ -125103,16 +125467,16 @@ function unregisterManagerForCleanup(manager) {
|
|
|
125103
125467
|
}
|
|
125104
125468
|
|
|
125105
125469
|
// src/features/background-agent/compaction-aware-message-resolver.ts
|
|
125106
|
-
import { readdirSync as
|
|
125107
|
-
import { join as
|
|
125108
|
-
function
|
|
125470
|
+
import { readdirSync as readdirSync23, readFileSync as readFileSync54 } from "fs";
|
|
125471
|
+
import { join as join90 } from "path";
|
|
125472
|
+
function isCompactionAgent5(agent) {
|
|
125109
125473
|
return agent?.trim().toLowerCase() === "compaction";
|
|
125110
125474
|
}
|
|
125111
125475
|
function hasFullAgentAndModel(message) {
|
|
125112
|
-
return !!message.agent && !
|
|
125476
|
+
return !!message.agent && !isCompactionAgent5(message.agent) && !!message.model?.providerID && !!message.model?.modelID;
|
|
125113
125477
|
}
|
|
125114
125478
|
function hasPartialAgentOrModel(message) {
|
|
125115
|
-
const hasAgent = !!message.agent && !
|
|
125479
|
+
const hasAgent = !!message.agent && !isCompactionAgent5(message.agent);
|
|
125116
125480
|
const hasModel = !!message.model?.providerID && !!message.model?.modelID;
|
|
125117
125481
|
return hasAgent || hasModel || !!message.tools;
|
|
125118
125482
|
}
|
|
@@ -125138,7 +125502,7 @@ function convertSessionMessageToStoredMessage(message) {
|
|
|
125138
125502
|
function mergeStoredMessages(messages, sessionID) {
|
|
125139
125503
|
const merged = {};
|
|
125140
125504
|
for (const message of messages) {
|
|
125141
|
-
if (!message ||
|
|
125505
|
+
if (!message || isCompactionAgent5(message.agent)) {
|
|
125142
125506
|
continue;
|
|
125143
125507
|
}
|
|
125144
125508
|
if (!merged.agent && message.agent) {
|
|
@@ -125179,11 +125543,11 @@ function resolvePromptContextFromSessionMessages(messages, sessionID) {
|
|
|
125179
125543
|
}
|
|
125180
125544
|
function findNearestMessageExcludingCompaction(messageDir, sessionID) {
|
|
125181
125545
|
try {
|
|
125182
|
-
const files =
|
|
125546
|
+
const files = readdirSync23(messageDir).filter((name) => name.endsWith(".json")).sort().reverse();
|
|
125183
125547
|
const messages = [];
|
|
125184
125548
|
for (const file3 of files) {
|
|
125185
125549
|
try {
|
|
125186
|
-
const content =
|
|
125550
|
+
const content = readFileSync54(join90(messageDir, file3), "utf-8");
|
|
125187
125551
|
messages.push(JSON.parse(content));
|
|
125188
125552
|
} catch {
|
|
125189
125553
|
continue;
|
|
@@ -125269,7 +125633,7 @@ function handleSessionIdleBackgroundEvent(args) {
|
|
|
125269
125633
|
}
|
|
125270
125634
|
|
|
125271
125635
|
// src/features/background-agent/manager.ts
|
|
125272
|
-
import { join as
|
|
125636
|
+
import { join as join91 } from "path";
|
|
125273
125637
|
|
|
125274
125638
|
// src/features/background-agent/remove-task-toast-tracking.ts
|
|
125275
125639
|
function removeTaskToastTracking(taskId) {
|
|
@@ -125957,32 +126321,52 @@ class BackgroundManager {
|
|
|
125957
126321
|
if (input.model) {
|
|
125958
126322
|
applySessionPromptParams(sessionID, input.model);
|
|
125959
126323
|
}
|
|
126324
|
+
const promptBody = {
|
|
126325
|
+
agent: input.agent,
|
|
126326
|
+
...launchModel ? { model: launchModel } : {},
|
|
126327
|
+
...launchVariant ? { variant: launchVariant } : {},
|
|
126328
|
+
system: input.skillContent,
|
|
126329
|
+
tools: (() => {
|
|
126330
|
+
const tools = {
|
|
126331
|
+
task: false,
|
|
126332
|
+
call_omo_agent: true,
|
|
126333
|
+
question: false,
|
|
126334
|
+
...getAgentToolRestrictions(input.agent)
|
|
126335
|
+
};
|
|
126336
|
+
setSessionTools(sessionID, tools);
|
|
126337
|
+
return tools;
|
|
126338
|
+
})(),
|
|
126339
|
+
parts: [createInternalAgentTextPart(input.prompt)]
|
|
126340
|
+
};
|
|
125960
126341
|
promptWithModelSuggestionRetry(this.client, {
|
|
125961
126342
|
path: { id: sessionID },
|
|
125962
|
-
body:
|
|
125963
|
-
agent: input.agent,
|
|
125964
|
-
...launchModel ? { model: launchModel } : {},
|
|
125965
|
-
...launchVariant ? { variant: launchVariant } : {},
|
|
125966
|
-
system: input.skillContent,
|
|
125967
|
-
tools: (() => {
|
|
125968
|
-
const tools = {
|
|
125969
|
-
task: false,
|
|
125970
|
-
call_omo_agent: true,
|
|
125971
|
-
question: false,
|
|
125972
|
-
...getAgentToolRestrictions(input.agent)
|
|
125973
|
-
};
|
|
125974
|
-
setSessionTools(sessionID, tools);
|
|
125975
|
-
return tools;
|
|
125976
|
-
})(),
|
|
125977
|
-
parts: [createInternalAgentTextPart(input.prompt)]
|
|
125978
|
-
}
|
|
126343
|
+
body: promptBody
|
|
125979
126344
|
}).catch(async (error92) => {
|
|
126345
|
+
if (isAgentNotFoundError(error92) && input.agent !== FALLBACK_AGENT) {
|
|
126346
|
+
log("[background-agent] Agent not found, retrying with fallback agent", {
|
|
126347
|
+
original: input.agent,
|
|
126348
|
+
fallback: FALLBACK_AGENT,
|
|
126349
|
+
taskId: task.id
|
|
126350
|
+
});
|
|
126351
|
+
try {
|
|
126352
|
+
const fallbackBody = buildFallbackBody(promptBody, FALLBACK_AGENT);
|
|
126353
|
+
setSessionTools(sessionID, fallbackBody.tools);
|
|
126354
|
+
await promptWithModelSuggestionRetry(this.client, {
|
|
126355
|
+
path: { id: sessionID },
|
|
126356
|
+
body: fallbackBody
|
|
126357
|
+
});
|
|
126358
|
+
task.agent = FALLBACK_AGENT;
|
|
126359
|
+
return;
|
|
126360
|
+
} catch (retryError) {
|
|
126361
|
+
log("[background-agent] Fallback agent also failed:", retryError);
|
|
126362
|
+
}
|
|
126363
|
+
}
|
|
125980
126364
|
log("[background-agent] promptAsync error:", error92);
|
|
125981
126365
|
const existingTask = this.findBySession(sessionID);
|
|
125982
126366
|
if (existingTask) {
|
|
125983
126367
|
existingTask.status = "interrupt";
|
|
125984
126368
|
const errorMessage = error92 instanceof Error ? error92.message : String(error92);
|
|
125985
|
-
if (errorMessage.includes("agent.name") || errorMessage.includes("undefined")) {
|
|
126369
|
+
if (errorMessage.includes("agent.name") || errorMessage.includes("undefined") || isAgentNotFoundError(error92)) {
|
|
125986
126370
|
existingTask.error = `Agent "${input.agent}" not found. Make sure the agent is registered in your opencode.json or provided by a plugin.`;
|
|
125987
126371
|
} else {
|
|
125988
126372
|
existingTask.error = errorMessage;
|
|
@@ -126493,6 +126877,13 @@ class BackgroundManager {
|
|
|
126493
126877
|
}
|
|
126494
126878
|
async handleSessionErrorEvent(args) {
|
|
126495
126879
|
const { task, errorInfo, errorMessage, errorName } = args;
|
|
126880
|
+
if (isAgentNotFoundError({ message: errorInfo.message })) {
|
|
126881
|
+
log("[background-agent] Skipping session.error fallback for agent-not-found (handled by prompt catch)", {
|
|
126882
|
+
taskId: task.id,
|
|
126883
|
+
errorMessage: errorInfo.message?.slice(0, 100)
|
|
126884
|
+
});
|
|
126885
|
+
return;
|
|
126886
|
+
}
|
|
126496
126887
|
if (await this.tryFallbackRetry(task, errorInfo, "session.error")) {
|
|
126497
126888
|
return;
|
|
126498
126889
|
}
|
|
@@ -126896,7 +127287,7 @@ ${originalText}`;
|
|
|
126896
127287
|
parentSessionID: task.parentSessionID
|
|
126897
127288
|
});
|
|
126898
127289
|
}
|
|
126899
|
-
const messageDir =
|
|
127290
|
+
const messageDir = join91(MESSAGE_STORAGE, task.parentSessionID);
|
|
126900
127291
|
const currentMessage = messageDir ? findNearestMessageExcludingCompaction(messageDir, task.parentSessionID) : null;
|
|
126901
127292
|
agent = currentMessage?.agent ?? task.parentAgent;
|
|
126902
127293
|
model = currentMessage?.model?.providerID && currentMessage?.model?.modelID ? { providerID: currentMessage.model.providerID, modelID: currentMessage.model.modelID } : undefined;
|
|
@@ -127224,11 +127615,11 @@ ${originalText}`;
|
|
|
127224
127615
|
}
|
|
127225
127616
|
}
|
|
127226
127617
|
// src/features/mcp-oauth/storage.ts
|
|
127227
|
-
import { chmodSync as chmodSync2, existsSync as existsSync82, mkdirSync as mkdirSync17, readFileSync as
|
|
127228
|
-
import { dirname as dirname29, join as
|
|
127618
|
+
import { chmodSync as chmodSync2, existsSync as existsSync82, mkdirSync as mkdirSync17, readFileSync as readFileSync55, unlinkSync as unlinkSync13, writeFileSync as writeFileSync22 } from "fs";
|
|
127619
|
+
import { dirname as dirname29, join as join92 } from "path";
|
|
127229
127620
|
var STORAGE_FILE_NAME = "mcp-oauth.json";
|
|
127230
127621
|
function getMcpOauthStoragePath() {
|
|
127231
|
-
return
|
|
127622
|
+
return join92(getOpenCodeConfigDir({ binary: "opencode" }), STORAGE_FILE_NAME);
|
|
127232
127623
|
}
|
|
127233
127624
|
function normalizeHost(serverHost) {
|
|
127234
127625
|
let host = serverHost.trim();
|
|
@@ -127269,7 +127660,7 @@ function readStore() {
|
|
|
127269
127660
|
return null;
|
|
127270
127661
|
}
|
|
127271
127662
|
try {
|
|
127272
|
-
const content =
|
|
127663
|
+
const content = readFileSync55(filePath, "utf-8");
|
|
127273
127664
|
return JSON.parse(content);
|
|
127274
127665
|
} catch {
|
|
127275
127666
|
return null;
|
|
@@ -133774,18 +134165,18 @@ class TmuxSessionManager {
|
|
|
133774
134165
|
var SESSION_TIMEOUT_MS3 = 10 * 60 * 1000;
|
|
133775
134166
|
var MIN_STABILITY_TIME_MS4 = 10 * 1000;
|
|
133776
134167
|
// src/features/claude-code-mcp-loader/loader.ts
|
|
133777
|
-
import { existsSync as existsSync83, readFileSync as
|
|
133778
|
-
import { join as
|
|
134168
|
+
import { existsSync as existsSync83, readFileSync as readFileSync56 } from "fs";
|
|
134169
|
+
import { join as join93 } from "path";
|
|
133779
134170
|
import { homedir as homedir15 } from "os";
|
|
133780
134171
|
init_logger();
|
|
133781
134172
|
function getMcpConfigPaths() {
|
|
133782
134173
|
const claudeConfigDir = getClaudeConfigDir();
|
|
133783
134174
|
const cwd = process.cwd();
|
|
133784
134175
|
return [
|
|
133785
|
-
{ path:
|
|
133786
|
-
{ path:
|
|
133787
|
-
{ path:
|
|
133788
|
-
{ path:
|
|
134176
|
+
{ path: join93(homedir15(), ".claude.json"), scope: "user" },
|
|
134177
|
+
{ path: join93(claudeConfigDir, ".mcp.json"), scope: "user" },
|
|
134178
|
+
{ path: join93(cwd, ".mcp.json"), scope: "project" },
|
|
134179
|
+
{ path: join93(cwd, ".claude", ".mcp.json"), scope: "local" }
|
|
133789
134180
|
];
|
|
133790
134181
|
}
|
|
133791
134182
|
async function loadMcpConfigFile(filePath) {
|
|
@@ -133808,7 +134199,7 @@ function getSystemMcpServerNames() {
|
|
|
133808
134199
|
if (!existsSync83(path13))
|
|
133809
134200
|
continue;
|
|
133810
134201
|
try {
|
|
133811
|
-
const content =
|
|
134202
|
+
const content = readFileSync56(path13, "utf-8");
|
|
133812
134203
|
const config4 = JSON.parse(content);
|
|
133813
134204
|
if (!config4?.mcpServers)
|
|
133814
134205
|
continue;
|
|
@@ -138815,43 +139206,39 @@ function buildTodoDisciplineSection3(useTaskSystem) {
|
|
|
138815
139206
|
if (useTaskSystem) {
|
|
138816
139207
|
return `## Task Discipline (NON-NEGOTIABLE)
|
|
138817
139208
|
|
|
138818
|
-
Track ALL multi-step work with tasks. This is your execution backbone
|
|
139209
|
+
**Track ALL multi-step work with tasks. This is your execution backbone.**
|
|
138819
139210
|
|
|
138820
139211
|
### When to Create Tasks (MANDATORY)
|
|
138821
139212
|
|
|
138822
|
-
- 2+ step task - \`task_create\` FIRST, atomic breakdown
|
|
138823
|
-
- Uncertain scope - \`task_create\` to clarify thinking
|
|
138824
|
-
- Complex single task -
|
|
139213
|
+
- **2+ step task** - \`task_create\` FIRST, atomic breakdown
|
|
139214
|
+
- **Uncertain scope** - \`task_create\` to clarify thinking
|
|
139215
|
+
- **Complex single task** - Break down into trackable steps
|
|
138825
139216
|
|
|
138826
139217
|
### Workflow (STRICT)
|
|
138827
139218
|
|
|
138828
|
-
1. On task start
|
|
138829
|
-
2. Before each step
|
|
138830
|
-
3. After each step
|
|
138831
|
-
4. Scope changes
|
|
138832
|
-
|
|
138833
|
-
Tasks prevent drift, enable recovery if interrupted, and make each commitment explicit. Skipping tasks on multi-step work, batch-completing, or proceeding without \`in_progress\` are blocking violations.
|
|
139219
|
+
1. **On task start**: \`task_create\` with atomic steps-no announcements, just create
|
|
139220
|
+
2. **Before each step**: \`task_update(status="in_progress")\` (ONE at a time)
|
|
139221
|
+
3. **After each step**: \`task_update(status="completed")\` IMMEDIATELY (NEVER batch)
|
|
139222
|
+
4. **Scope changes**: Update tasks BEFORE proceeding
|
|
138834
139223
|
|
|
138835
139224
|
**NO TASKS ON MULTI-STEP WORK = INCOMPLETE WORK.**`;
|
|
138836
139225
|
}
|
|
138837
139226
|
return `## Todo Discipline (NON-NEGOTIABLE)
|
|
138838
139227
|
|
|
138839
|
-
Track ALL multi-step work with todos. This is your execution backbone
|
|
139228
|
+
**Track ALL multi-step work with todos. This is your execution backbone.**
|
|
138840
139229
|
|
|
138841
139230
|
### When to Create Todos (MANDATORY)
|
|
138842
139231
|
|
|
138843
|
-
- 2+ step task - \`todowrite\` FIRST, atomic breakdown
|
|
138844
|
-
- Uncertain scope - \`todowrite\` to clarify thinking
|
|
138845
|
-
- Complex single task -
|
|
139232
|
+
- **2+ step task** - \`todowrite\` FIRST, atomic breakdown
|
|
139233
|
+
- **Uncertain scope** - \`todowrite\` to clarify thinking
|
|
139234
|
+
- **Complex single task** - Break down into trackable steps
|
|
138846
139235
|
|
|
138847
139236
|
### Workflow (STRICT)
|
|
138848
139237
|
|
|
138849
|
-
1. On task start
|
|
138850
|
-
2. Before each step
|
|
138851
|
-
3. After each step
|
|
138852
|
-
4. Scope changes
|
|
138853
|
-
|
|
138854
|
-
Todos prevent drift, enable recovery if interrupted, and make each commitment explicit. Skipping todos on multi-step work, batch-completing, or proceeding without \`in_progress\` are blocking violations.
|
|
139238
|
+
1. **On task start**: \`todowrite\` with atomic steps-no announcements, just create
|
|
139239
|
+
2. **Before each step**: Mark \`in_progress\` (ONE at a time)
|
|
139240
|
+
3. **After each step**: Mark \`completed\` IMMEDIATELY (NEVER batch)
|
|
139241
|
+
4. **Scope changes**: Update todos BEFORE proceeding
|
|
138855
139242
|
|
|
138856
139243
|
**NO TODOS ON MULTI-STEP WORK = INCOMPLETE WORK.**`;
|
|
138857
139244
|
}
|
|
@@ -138862,316 +139249,261 @@ function buildHephaestusPrompt3(availableAgents = [], availableTools = [], avail
|
|
|
138862
139249
|
const librarianSection = buildLibrarianSection(availableAgents);
|
|
138863
139250
|
const categorySkillsGuide = buildCategorySkillsDelegationGuide(availableCategories, availableSkills);
|
|
138864
139251
|
const delegationTable = buildDelegationTable(availableAgents);
|
|
138865
|
-
const
|
|
139252
|
+
const hasOracle = availableAgents.some((agent) => agent.name === "oracle");
|
|
138866
139253
|
const hardBlocks = buildHardBlocksSection();
|
|
138867
139254
|
const antiPatterns = buildAntiPatternsSection();
|
|
139255
|
+
const antiDuplication = buildAntiDuplicationSection();
|
|
138868
139256
|
const todoDiscipline = buildTodoDisciplineSection3(useTaskSystem);
|
|
138869
|
-
|
|
138870
|
-
|
|
138871
|
-
## Identity
|
|
138872
|
-
|
|
138873
|
-
You build context by examining the codebase first without making assumptions. You think through the nuances of the code you encounter. You do not stop early. You complete.
|
|
138874
|
-
|
|
138875
|
-
Persist until the task is fully handled end-to-end within the current turn. Persevere even when tool calls fail. Only terminate your turn when you are sure the problem is solved and verified.
|
|
138876
|
-
|
|
138877
|
-
When blocked: try a different approach \u2192 decompose the problem \u2192 challenge assumptions \u2192 explore how others solved it. Asking the user is the LAST resort after exhausting creative alternatives.
|
|
138878
|
-
|
|
138879
|
-
### Do NOT Ask - Just Do
|
|
138880
|
-
|
|
138881
|
-
**FORBIDDEN:**
|
|
138882
|
-
- Asking permission in any form ("Should I proceed?", "Would you like me to...?", "I can do X if you want") \u2192 JUST DO IT.
|
|
138883
|
-
- "Do you want me to run tests?" \u2192 RUN THEM.
|
|
138884
|
-
- "I noticed Y, should I fix it?" \u2192 FIX IT OR NOTE IN FINAL MESSAGE.
|
|
138885
|
-
- Stopping after partial implementation \u2192 100% OR NOTHING.
|
|
138886
|
-
- Answering a question then stopping \u2192 The question implies action. DO THE ACTION.
|
|
138887
|
-
- "I'll do X" / "I recommend X" then ending turn \u2192 You COMMITTED to X. DO X NOW before ending.
|
|
138888
|
-
- Explaining findings without acting on them \u2192 ACT on your findings immediately.
|
|
138889
|
-
|
|
138890
|
-
**CORRECT:**
|
|
138891
|
-
- Keep going until COMPLETELY done
|
|
138892
|
-
- Run verification (lint, tests, build) WITHOUT asking
|
|
138893
|
-
- Make decisions. Course-correct only on CONCRETE failure
|
|
138894
|
-
- Note assumptions in final message, not as questions mid-work
|
|
138895
|
-
- Need context? Fire explore/librarian in background IMMEDIATELY - continue only with non-overlapping work while they search
|
|
138896
|
-
- User asks "did you do X?" and you didn't \u2192 Acknowledge briefly, DO X immediately
|
|
138897
|
-
- User asks a question implying work \u2192 Answer briefly, DO the implied work in the same turn
|
|
138898
|
-
- You wrote a plan in your response \u2192 EXECUTE the plan before ending turn - plans are starting lines, not finish lines
|
|
138899
|
-
|
|
138900
|
-
### Task Scope Clarification
|
|
138901
|
-
|
|
138902
|
-
You handle multi-step sub-tasks of a SINGLE GOAL. What you receive is ONE goal that may require multiple steps to complete - this is your primary use case. Only reject when given MULTIPLE INDEPENDENT goals in one request.
|
|
139257
|
+
const identityBlock = `<identity>
|
|
139258
|
+
You are Hephaestus, an autonomous deep worker for software engineering.
|
|
138903
139259
|
|
|
138904
|
-
|
|
139260
|
+
You communicate warmly and directly, like a senior colleague walking through a problem together. You explain the why behind decisions, not just the what. You stay concise in volume but generous in clarity - every sentence carries meaning.
|
|
138905
139261
|
|
|
138906
|
-
|
|
139262
|
+
You build context by examining the codebase first without assumptions. You think through the nuances of the code you encounter. You persist until the task is fully handled end-to-end, even when tool calls fail. You only end your turn when the problem is solved and verified.
|
|
138907
139263
|
|
|
138908
|
-
|
|
139264
|
+
You are autonomous. When you see work to do, do it - run tests, fix issues, make decisions. Course-correct only on concrete failure. State assumptions in your final message, not as questions along the way. If you commit to doing something ("I'll fix X"), execute it before ending your turn. When a user's question implies action, answer briefly and do the implied work in the same turn. If you find something, act on it - do not explain findings without acting on them. Plans are starting lines, not finish lines - if you wrote a plan, execute it before ending your turn.
|
|
138909
139265
|
|
|
138910
|
-
|
|
139266
|
+
When blocked: try a different approach, decompose the problem, challenge your assumptions, explore how others solved it. Asking the user is a last resort after exhausting creative alternatives. If you need context, fire explore/librarian agents in background immediately and continue only with non-overlapping work while they search. Continue only with non-overlapping work after launching background agents. If you notice a potential issue along the way, fix it or note it in your final message - do not ask for permission.
|
|
138911
139267
|
|
|
139268
|
+
You handle multi-step sub-tasks of a single goal. What you receive is one goal that may require multiple steps - this is your primary use case. Only flag when given genuinely independent goals in one request.
|
|
139269
|
+
</identity>`;
|
|
139270
|
+
const intentBlock = `<intent>
|
|
138912
139271
|
${keyTriggers}
|
|
138913
139272
|
|
|
138914
|
-
|
|
138915
|
-
### Step 0: Extract True Intent (BEFORE Classification)
|
|
139273
|
+
You are an autonomous deep worker. Users chose you for ACTION, not analysis. Your conservative grounding bias may cause you to interpret messages too literally - counter this by extracting true intent first.
|
|
138916
139274
|
|
|
138917
|
-
|
|
139275
|
+
Every message has a surface form and a true intent. Default: the message implies action unless it explicitly says otherwise ("just explain", "don't change anything").
|
|
138918
139276
|
|
|
138919
|
-
|
|
138920
|
-
|
|
138921
|
-
**Intent Mapping (act on TRUE intent, not surface form):**
|
|
138922
|
-
|
|
138923
|
-
| Surface Form | True Intent | Your Response |
|
|
139277
|
+
<intent_mapping>
|
|
139278
|
+
| Surface Form | True Intent | Your Move |
|
|
138924
139279
|
|---|---|---|
|
|
138925
|
-
| "Did you do X?" (and you didn't) |
|
|
138926
|
-
| "How does X work?" | Understand
|
|
138927
|
-
| "Can you look into Y?" | Investigate
|
|
138928
|
-
| "What's the best way to do Z?" |
|
|
138929
|
-
| "Why is A broken?" / "I'm seeing error B" | Fix A / Fix B | Diagnose
|
|
138930
|
-
| "What do you think about C?" | Evaluate
|
|
139280
|
+
| "Did you do X?" (and you didn't) | Do X now | Acknowledge briefly, do X |
|
|
139281
|
+
| "How does X work?" | Understand to fix/improve | Explore, then implement/fix |
|
|
139282
|
+
| "Can you look into Y?" | Investigate and resolve | Investigate, then resolve |
|
|
139283
|
+
| "What's the best way to do Z?" | Do Z the best way | Decide, then implement |
|
|
139284
|
+
| "Why is A broken?" / "I'm seeing error B" | Fix A / Fix B | Diagnose, then fix |
|
|
139285
|
+
| "What do you think about C?" | Evaluate and implement | Evaluate, then implement best option |
|
|
139286
|
+
</intent_mapping>
|
|
138931
139287
|
|
|
138932
|
-
Pure question (
|
|
139288
|
+
Pure question (no action) only when ALL of these are true: user explicitly says "just explain" / "don't change anything", no actionable codebase context, and no problem or improvement is mentioned.
|
|
138933
139289
|
|
|
138934
|
-
|
|
139290
|
+
State your read before acting: "I detect [intent type] - [reason]. [What I'm doing now]." This commits you to follow through in the same turn.
|
|
138935
139291
|
|
|
138936
|
-
|
|
138937
|
-
|
|
138938
|
-
|
|
138939
|
-
|
|
138940
|
-
|
|
138941
|
-
|
|
138942
|
-
|
|
138943
|
-
|
|
138944
|
-
|
|
138945
|
-
|
|
138946
|
-
- **Explicit**: Specific file/line, clear command - Execute directly
|
|
138947
|
-
- **Exploratory**: "How does X work?", "Find Y" - Fire explore (1-3) + tools in parallel \u2192 then ACT on findings (see Step 0 true intent)
|
|
138948
|
-
- **Open-ended**: "Improve", "Refactor", "Add feature" - Full Execution Loop required
|
|
138949
|
-
- **Ambiguous**: Unclear scope, multiple interpretations - Ask ONE clarifying question
|
|
138950
|
-
|
|
138951
|
-
### Step 2: Ambiguity Protocol (EXPLORE FIRST - NEVER ask before exploring)
|
|
138952
|
-
|
|
138953
|
-
- Single valid interpretation - proceed immediately
|
|
138954
|
-
- Missing info that MIGHT exist - EXPLORE FIRST with tools (\`gh\`, \`git\`, \`grep\`, explore agents)
|
|
138955
|
-
- Multiple plausible interpretations - cover ALL likely intents comprehensively, don't ask
|
|
138956
|
-
- Truly impossible to proceed - ask ONE precise question (LAST RESORT)
|
|
138957
|
-
|
|
138958
|
-
Exploration hierarchy (MANDATORY before any question):
|
|
138959
|
-
1. Direct tools: \`gh pr list\`, \`git log\`, \`grep\`, \`rg\`, file reads
|
|
139292
|
+
Complexity:
|
|
139293
|
+
- Trivial (single file, <10 lines) - direct tools, unless a key trigger fires
|
|
139294
|
+
- Explicit (specific file/line) - execute directly
|
|
139295
|
+
- Exploratory ("how does X work?") - fire explore agents + tools in parallel, then act on findings
|
|
139296
|
+
- Open-ended ("improve", "refactor") - full execution loop
|
|
139297
|
+
- Ambiguous - explore first, cover all likely intents comprehensively rather than asking
|
|
139298
|
+
- Uncertain scope - create todos to clarify thinking, then proceed
|
|
139299
|
+
|
|
139300
|
+
Before asking the user anything, exhaust this hierarchy:
|
|
139301
|
+
1. Direct tools: \`grep\`, \`rg\`, file reads, \`gh\`, \`git log\`
|
|
138960
139302
|
2. Explore agents: fire 2-3 parallel background searches
|
|
138961
139303
|
3. Librarian agents: check docs, GitHub, external sources
|
|
138962
139304
|
4. Context inference: educated guess from surrounding context
|
|
138963
|
-
5.
|
|
139305
|
+
5. Only when 1-4 all fail: ask one precise question
|
|
138964
139306
|
|
|
138965
|
-
|
|
138966
|
-
|
|
138967
|
-
|
|
138968
|
-
|
|
138969
|
-
|
|
139307
|
+
Before acting, check:
|
|
139308
|
+
- Do I have implicit assumptions? Is the search scope clear?
|
|
139309
|
+
- Is there a skill whose domain overlaps? Load it immediately.
|
|
139310
|
+
- Is there a specialized agent that matches this? What category + skills to equip?
|
|
139311
|
+
- Can I do it myself for the best result? Default to delegation for complex tasks.
|
|
138970
139312
|
|
|
138971
|
-
|
|
138972
|
-
|
|
138973
|
-
|
|
138974
|
-
|
|
138975
|
-
3. Can I do it myself for the best result, FOR SURE?
|
|
139313
|
+
If the user's approach seems problematic, explain your concern and the alternative, then proceed with the better approach. Flag major risks before implementing.
|
|
139314
|
+
</intent>`;
|
|
139315
|
+
const exploreBlock = `<explore>
|
|
139316
|
+
${toolSelection}
|
|
138976
139317
|
|
|
138977
|
-
|
|
139318
|
+
${exploreSection}
|
|
138978
139319
|
|
|
138979
|
-
|
|
139320
|
+
${librarianSection}
|
|
138980
139321
|
|
|
138981
|
-
|
|
139322
|
+
<tool_usage_rules>
|
|
139323
|
+
- Parallelize independent tool calls: multiple file reads, grep searches, agent fires - all at once
|
|
139324
|
+
- Explore/Librarian = background grep. ALWAYS \`run_in_background=true\`, ALWAYS parallel
|
|
139325
|
+
- After any file edit: restate what changed, where, and what validation follows
|
|
139326
|
+
- Prefer tools over guessing whenever you need specific data (files, configs, patterns)
|
|
139327
|
+
</tool_usage_rules>
|
|
138982
139328
|
|
|
138983
|
-
|
|
139329
|
+
<tool_call_philosophy>
|
|
139330
|
+
More tool calls = more accuracy. Ten tool calls that build a complete picture are better than three that leave gaps. Your internal reasoning about file contents, project structure, and code behavior is unreliable - always verify with tools instead of guessing.
|
|
138984
139331
|
|
|
138985
|
-
|
|
139332
|
+
Treat every tool call as an investment in correctness, not a cost to minimize. When you are unsure whether to make a tool call, make it. When you think you have enough context, make one more call to verify. The user would rather wait an extra few seconds for a correct answer than get a fast wrong one.
|
|
139333
|
+
</tool_call_philosophy>
|
|
138986
139334
|
|
|
138987
|
-
|
|
139335
|
+
<tool_persistence>
|
|
139336
|
+
Do not stop calling tools just to save calls. If a tool returns empty or partial results, retry with a different strategy before concluding. Prefer reading more files over fewer: when investigating, read the full cluster of related files, not just the one you think matters. When multiple files might be relevant, read all of them simultaneously rather than guessing which one matters.
|
|
139337
|
+
</tool_persistence>
|
|
138988
139338
|
|
|
138989
|
-
|
|
139339
|
+
<dig_deeper>
|
|
139340
|
+
Do not stop at the first plausible answer. Look for second-order issues, edge cases, and missing constraints. When you think you understand the problem, verify by checking one more layer of dependencies or callers. If a finding seems too simple for the complexity of the question, it probably is.
|
|
139341
|
+
</dig_deeper>
|
|
138990
139342
|
|
|
138991
|
-
|
|
139343
|
+
<dependency_checks>
|
|
139344
|
+
Before taking an action, check whether prerequisite discovery or lookup is required. Do not skip prerequisite steps just because the intended final action seems obvious. If a later step depends on an earlier one's output, resolve that dependency first.
|
|
139345
|
+
</dependency_checks>
|
|
138992
139346
|
|
|
138993
|
-
|
|
139347
|
+
Prefer tools over guessing whenever you need specific data (files, configs, patterns). Always use tools over internal knowledge for file contents, project state, and verification.
|
|
138994
139348
|
|
|
138995
|
-
|
|
139349
|
+
<parallel_execution>
|
|
139350
|
+
Parallelize aggressively - this is where you gain the most speed and accuracy. Every independent operation should run simultaneously, not sequentially:
|
|
139351
|
+
- Multiple file reads: read 5 files at once, not one by one
|
|
139352
|
+
- Grep + file reads: search and read in the same turn
|
|
139353
|
+
- Multiple explore/librarian agents: fire 3-5 agents in parallel for different angles on the same question
|
|
139354
|
+
- Agent fires + direct tool calls: launch background agents AND do direct reads simultaneously
|
|
138996
139355
|
|
|
138997
|
-
|
|
138998
|
-
|
|
138999
|
-
- Explore/Librarian = background grep. ALWAYS \`run_in_background=true\`, ALWAYS parallel.
|
|
139000
|
-
- Never chain together bash commands with separators like \`&&\`, \`;\`, or \`|\` in a single call. Run each command as a separate tool invocation.
|
|
139001
|
-
- After any file edit: restate what changed, where, and what validation follows.
|
|
139002
|
-
- Prefer tools over guessing whenever you need specific data (files, configs, patterns).
|
|
139003
|
-
</tool_usage_rules>
|
|
139356
|
+
Fire 2-5 explore agents in parallel for any non-trivial codebase question. Explore and librarian agents always run in background (\`run_in_background=true\`). Never use \`run_in_background=false\` for explore/librarian. After launching, continue only with non-overlapping work. Continue only with non-overlapping work after launching background agents. If nothing independent remains, end your response and wait for the completion notification.
|
|
139357
|
+
</parallel_execution>
|
|
139004
139358
|
|
|
139005
|
-
|
|
139359
|
+
How to call explore/librarian:
|
|
139006
139360
|
\`\`\`
|
|
139007
|
-
// Codebase search
|
|
139361
|
+
// Codebase search
|
|
139008
139362
|
task(subagent_type="explore", run_in_background=true, load_skills=[], description="Find [what]", prompt="[CONTEXT]: ... [GOAL]: ... [REQUEST]: ...")
|
|
139009
139363
|
|
|
139010
|
-
// External docs/OSS search
|
|
139364
|
+
// External docs/OSS search
|
|
139011
139365
|
task(subagent_type="librarian", run_in_background=true, load_skills=[], description="Find [what]", prompt="[CONTEXT]: ... [GOAL]: ... [REQUEST]: ...")
|
|
139012
|
-
|
|
139013
139366
|
\`\`\`
|
|
139014
139367
|
|
|
139015
|
-
|
|
139016
|
-
- [CONTEXT]: Task, files/modules involved, approach
|
|
139017
|
-
- [GOAL]: Specific outcome needed - what decision this unblocks
|
|
139018
|
-
- [DOWNSTREAM]: How results will be used
|
|
139019
|
-
- [REQUEST]: What to find, format to return, what to SKIP
|
|
139368
|
+
Never chain together bash commands with separators like \`&&\`, \`;\`, or \`|\` in a single call. Run each command as a separate tool invocation.
|
|
139020
139369
|
|
|
139021
|
-
|
|
139022
|
-
- Fire 2-5 explore agents in parallel for any non-trivial codebase question
|
|
139023
|
-
- Parallelize independent file reads - don't read files one at a time
|
|
139024
|
-
- NEVER use \`run_in_background=false\` for explore/librarian
|
|
139025
|
-
- Continue only with non-overlapping work after launching background agents
|
|
139026
|
-
- Collect results with \`background_output(task_id="...")\` when needed
|
|
139027
|
-
- BEFORE final answer, cancel DISPOSABLE tasks individually: \`background_cancel(taskId="bg_explore_xxx")\`, \`background_cancel(taskId="bg_librarian_xxx")\`
|
|
139028
|
-
- **NEVER use \`background_cancel(all=true)\`** - it kills tasks whose results you haven't collected yet
|
|
139370
|
+
After any file edit, briefly restate what changed, where, and what validation follows.
|
|
139029
139371
|
|
|
139030
|
-
|
|
139372
|
+
Once you delegate exploration to background agents, do not repeat the same search yourself. Continue only with non-overlapping work only. Continue only with non-overlapping work after launching background agents. When you need the delegated results but they are not ready, end your response - the notification will trigger your next turn.
|
|
139031
139373
|
|
|
139032
|
-
|
|
139374
|
+
Agent prompt structure:
|
|
139375
|
+
- [CONTEXT]: Task, files/modules involved, approach
|
|
139376
|
+
- [GOAL]: Specific outcome needed - what decision this unblocks
|
|
139377
|
+
- [DOWNSTREAM]: How results will be used
|
|
139378
|
+
- [REQUEST]: What to find, format to return, what to skip
|
|
139033
139379
|
|
|
139034
|
-
|
|
139380
|
+
Background task management:
|
|
139381
|
+
- Collect results with \`background_output(task_id="...")\` when completed
|
|
139382
|
+
- Before final answer, cancel disposable tasks individually: \`background_cancel(taskId="...")\`
|
|
139383
|
+
- Never use \`background_cancel(all=true)\` - it kills tasks whose results you have not collected yet
|
|
139035
139384
|
|
|
139036
|
-
|
|
139385
|
+
${antiDuplication}
|
|
139037
139386
|
|
|
139038
|
-
|
|
139387
|
+
Stop searching when you have enough context, the same info repeats, or two iterations found nothing new.
|
|
139388
|
+
</explore>`;
|
|
139389
|
+
const constraintsBlock = `<constraints>
|
|
139390
|
+
${hardBlocks}
|
|
139039
139391
|
|
|
139040
|
-
|
|
139041
|
-
|
|
139042
|
-
|
|
139043
|
-
|
|
139044
|
-
|
|
139392
|
+
${antiPatterns}
|
|
139393
|
+
</constraints>`;
|
|
139394
|
+
const executionBlock = `<execution>
|
|
139395
|
+
1. **Explore**: Fire 2-5 explore/librarian agents in parallel + direct tool reads. Goal: complete understanding, not just enough context.
|
|
139396
|
+
2. **Plan**: List files to modify, specific changes, dependencies, complexity estimate.
|
|
139397
|
+
3. **Decide**: Trivial (<10 lines, single file) -> self. Complex (multi-file, >100 lines) -> delegate.
|
|
139398
|
+
4. **Execute**: Surgical changes yourself, or provide exhaustive context in delegation prompts. Match existing patterns. Minimal diff. Search the codebase for similar patterns before writing code. Default to ASCII. Add comments only for non-obvious blocks.
|
|
139399
|
+
5. **Verify**: \`lsp_diagnostics\` on all modified files (zero errors) -> run related tests (\`foo.ts\` -> \`foo.test.ts\`) -> typecheck -> build if applicable (exit 0). Fix only issues your changes caused.
|
|
139045
139400
|
|
|
139046
|
-
If verification fails
|
|
139401
|
+
If verification fails, return to step 1 with a materially different approach. After three attempts: stop, revert to last working state, document what you tried, consult Oracle. If Oracle cannot resolve, ask the user.
|
|
139047
139402
|
|
|
139048
|
-
|
|
139403
|
+
While working, you may notice unexpected changes you did not make - likely from the user or autogeneration. If they directly conflict with your task, ask. Otherwise, focus on your task.
|
|
139049
139404
|
|
|
139050
|
-
|
|
139405
|
+
<completion_check>
|
|
139406
|
+
When you think you are done: re-read the original request. Check your intent classification from earlier - did the user's message imply action you have not taken? Verify every item is fully implemented - not partially, not "extend later." Run verification once more. Then report what you did, what you verified, and the results.
|
|
139407
|
+
</completion_check>
|
|
139051
139408
|
|
|
139052
|
-
|
|
139409
|
+
<failure_recovery>
|
|
139410
|
+
Fix root causes, not symptoms. Re-verify after every attempt. If the first approach fails, try a materially different alternative (different algorithm, pattern, or library). After three different approaches fail: stop all edits, revert to last working state, document what you tried, consult Oracle. If Oracle cannot resolve, ask the user with a clear explanation.
|
|
139053
139411
|
|
|
139412
|
+
Never leave code broken, delete failing tests, or make random changes hoping something works.
|
|
139413
|
+
</failure_recovery>
|
|
139414
|
+
</execution>`;
|
|
139415
|
+
const trackingBlock = `<tracking>
|
|
139054
139416
|
${todoDiscipline}
|
|
139417
|
+
</tracking>`;
|
|
139418
|
+
const progressBlock = `<progress>
|
|
139419
|
+
Report progress at meaningful phase transitions. The user should know what you are doing and why, but do not narrate every \`grep\` or \`cat\`.
|
|
139055
139420
|
|
|
139056
|
-
|
|
139057
|
-
|
|
139058
|
-
## Progress Updates
|
|
139059
|
-
|
|
139060
|
-
Report progress proactively every ~30 seconds. The user should always know what you're doing and why.
|
|
139061
|
-
|
|
139062
|
-
When to update (MANDATORY):
|
|
139421
|
+
When to update:
|
|
139063
139422
|
- Before exploration: "Checking the repo structure for auth patterns..."
|
|
139064
139423
|
- After discovery: "Found the config in \`src/config/\`. The pattern uses factory functions."
|
|
139065
139424
|
- Before large edits: "About to refactor the handler - touching 3 files."
|
|
139066
139425
|
- On phase transitions: "Exploration done. Moving to implementation."
|
|
139067
139426
|
- On blockers: "Hit a snag with the types - trying generics instead."
|
|
139068
139427
|
|
|
139069
|
-
Style:
|
|
139070
|
-
|
|
139071
|
-
|
|
139072
|
-
|
|
139073
|
-
## Implementation
|
|
139074
|
-
|
|
139428
|
+
Style: one sentence, concrete, with at least one specific detail (file path, pattern found, decision made). Explain the why behind technical decisions. Keep updates varied in structure.
|
|
139429
|
+
</progress>`;
|
|
139430
|
+
const delegationBlock = `<delegation>
|
|
139075
139431
|
${categorySkillsGuide}
|
|
139076
139432
|
|
|
139077
|
-
|
|
139078
|
-
|
|
139079
|
-
|
|
139080
|
-
|
|
139081
|
-
-
|
|
139082
|
-
- **Browser testing**: \`playwright\` - Browser automation, screenshots, verification
|
|
139083
|
-
- **Git operations**: \`git-master\` - Atomic commits, rebase/squash, blame/bisect
|
|
139084
|
-
- **Tauri desktop app**: \`tauri-macos-craft\` - macOS-native UI, vibrancy, traffic lights
|
|
139085
|
-
|
|
139086
|
-
User-installed skills get PRIORITY. Always evaluate ALL available skills before delegating.
|
|
139433
|
+
When delegating, check all available skills. User-installed skills get priority. Always evaluate all available skills before delegating. Example domain-skill mappings:
|
|
139434
|
+
- Frontend/UI work: \`frontend-ui-ux\` - Anti-slop design: bold typography, intentional color, meaningful motion
|
|
139435
|
+
- Browser testing: \`playwright\` - Browser automation, screenshots, verification
|
|
139436
|
+
- Git operations: \`git-master\` - Atomic commits, rebase/squash, blame/bisect
|
|
139437
|
+
- Tauri desktop app: \`tauri-macos-craft\` - macOS-native UI, vibrancy, traffic lights
|
|
139087
139438
|
|
|
139088
139439
|
${delegationTable}
|
|
139089
139440
|
|
|
139090
|
-
|
|
139091
|
-
|
|
139092
|
-
|
|
139093
|
-
|
|
139094
|
-
|
|
139095
|
-
|
|
139096
|
-
|
|
139097
|
-
|
|
139098
|
-
|
|
139099
|
-
\`\`\`
|
|
139100
|
-
|
|
139101
|
-
Vague prompts = rejected. Be exhaustive.
|
|
139102
|
-
|
|
139103
|
-
After delegation, ALWAYS verify: works as expected? follows codebase pattern? MUST DO / MUST NOT DO respected? NEVER trust subagent self-reports. ALWAYS verify with your own tools.
|
|
139104
|
-
|
|
139105
|
-
### Session Continuity
|
|
139106
|
-
|
|
139107
|
-
Every \`task()\` output includes a session_id. USE IT for follow-ups.
|
|
139108
|
-
|
|
139109
|
-
- Task failed/incomplete - \`session_id="{id}", prompt="Fix: {error}"\`
|
|
139110
|
-
- Follow-up on result - \`session_id="{id}", prompt="Also: {question}"\`
|
|
139111
|
-
- Verification failed - \`session_id="{id}", prompt="Failed: {error}. Fix."\`
|
|
139112
|
-
|
|
139113
|
-
${oracleSection ? `
|
|
139114
|
-
${oracleSection}
|
|
139115
|
-
` : ""}
|
|
139116
|
-
|
|
139117
|
-
## Output Contract
|
|
139118
|
-
|
|
139119
|
-
<output_contract>
|
|
139120
|
-
Always favor conciseness. Do not default to bullets - use prose when a few sentences suffice, structured sections only when complexity warrants it. Group findings by outcome rather than enumerating every detail.
|
|
139441
|
+
<delegation_prompt>
|
|
139442
|
+
Every delegation prompt needs these 6 sections:
|
|
139443
|
+
1. TASK: atomic goal
|
|
139444
|
+
2. EXPECTED OUTCOME: deliverables + success criteria
|
|
139445
|
+
3. REQUIRED TOOLS: explicit whitelist
|
|
139446
|
+
4. MUST DO: exhaustive requirements - leave nothing implicit
|
|
139447
|
+
5. MUST NOT DO: forbidden actions - anticipate rogue behavior
|
|
139448
|
+
6. CONTEXT: file paths, existing patterns, constraints
|
|
139449
|
+
</delegation_prompt>
|
|
139121
139450
|
|
|
139122
|
-
|
|
139123
|
-
|
|
139124
|
-
Do not begin responses with conversational interjections or meta commentary. NEVER open with: "Done -", "Got it", "Great question!", "That's a great idea!", "You're right to call that out".
|
|
139125
|
-
|
|
139126
|
-
DO send clear context before significant actions - explain what you're doing and why in plain language so anyone can follow. When explaining technical decisions, explain the WHY, not just the WHAT.
|
|
139127
|
-
|
|
139128
|
-
Updates at meaningful milestones must include a concrete outcome ("Found X", "Updated Y"). Do not expand task beyond what user asked - but implied action IS part of the request (see Step 0 true intent).
|
|
139129
|
-
</output_contract>
|
|
139130
|
-
|
|
139131
|
-
## Code Quality & Verification
|
|
139132
|
-
|
|
139133
|
-
### Before Writing Code (MANDATORY)
|
|
139134
|
-
|
|
139135
|
-
1. SEARCH existing codebase for similar patterns/styles
|
|
139136
|
-
2. Match naming, indentation, import styles, error handling conventions
|
|
139137
|
-
3. Default to ASCII. Add comments only for non-obvious blocks
|
|
139451
|
+
After delegation, verify by reading every file the subagent touched. Check: works as expected? follows codebase pattern? Do not trust self-reports.
|
|
139138
139452
|
|
|
139139
|
-
|
|
139453
|
+
<session_continuity>
|
|
139454
|
+
Every \`task()\` returns a session_id. Use it for all follow-ups:
|
|
139455
|
+
- Task failed/incomplete: \`session_id="{id}", prompt="Fix: {error}"\`
|
|
139456
|
+
- Follow-up on result: \`session_id="{id}", prompt="Also: {question}"\`
|
|
139457
|
+
- Verification failed: \`session_id="{id}", prompt="Failed: {error}. Fix."\`
|
|
139140
139458
|
|
|
139141
|
-
|
|
139142
|
-
|
|
139143
|
-
|
|
139144
|
-
|
|
139145
|
-
|
|
139459
|
+
This preserves full context, avoids repeated exploration, saves 70%+ tokens.
|
|
139460
|
+
</session_continuity>
|
|
139461
|
+
${hasOracle ? `
|
|
139462
|
+
<oracle>
|
|
139463
|
+
Oracle is a read-only reasoning model, available as a last-resort escalation path when you are genuinely stuck.
|
|
139464
|
+
|
|
139465
|
+
Consult Oracle only when:
|
|
139466
|
+
- You have tried 2+ materially different approaches and all failed
|
|
139467
|
+
- You have documented what you tried and why each approach failed
|
|
139468
|
+
- The problem requires architectural insight beyond what codebase exploration provides
|
|
139469
|
+
|
|
139470
|
+
Do not consult Oracle:
|
|
139471
|
+
- Before attempting the fix yourself (try first, escalate later)
|
|
139472
|
+
- For questions answerable from code you have already read
|
|
139473
|
+
- For routine decisions, even complex ones you can reason through
|
|
139474
|
+
- On your first or second attempt at any task
|
|
139475
|
+
|
|
139476
|
+
If you do consult Oracle, announce "Consulting Oracle for [reason]" before invocation. Collect Oracle results before your final answer. Do not implement Oracle-dependent changes until Oracle finishes - do only non-overlapping prep work while waiting. Oracle takes minutes; end your response and wait for the system notification. Never poll, never cancel Oracle.
|
|
139477
|
+
</oracle>` : ""}
|
|
139478
|
+
</delegation>`;
|
|
139479
|
+
const communicationBlock = `<communication>
|
|
139480
|
+
Your output is the one part the user actually sees. Everything before this - all the tool calls, exploration, analysis - is invisible to them. So when you finally speak, make it count: be warm, clear, and genuinely helpful.
|
|
139146
139481
|
|
|
139147
|
-
|
|
139482
|
+
Write in complete, natural sentences that anyone can follow. Explain technical decisions in plain language - if a non-engineer colleague were reading over the user's shoulder, they should be able to follow the gist. Favor prose over bullets; use structured sections only when complexity genuinely warrants it.
|
|
139148
139483
|
|
|
139149
|
-
|
|
139484
|
+
For simple tasks, 1-2 short paragraphs. For larger tasks, at most 2-4 sections grouped by outcome, not by file. Group findings by outcome rather than enumerating every detail.
|
|
139150
139485
|
|
|
139151
|
-
|
|
139486
|
+
When explaining what you did: lead with the result ("Fixed the auth bug - the token was expiring before the refresh check"), then add supporting detail only if it helps understanding. Include concrete details: file paths, patterns found, decisions made. Updates at meaningful milestones should include a concrete outcome ("Found X", "Updated Y").
|
|
139152
139487
|
|
|
139153
|
-
|
|
139154
|
-
|
|
139488
|
+
Do not pad responses with conversational openers ("Done -", "Got it", "Great question!"), meta commentary, or acknowledgements. Do not repeat the user's request back. Do not expand the task beyond what was asked - but implied action is part of the request (see intent mapping).
|
|
139489
|
+
</communication>`;
|
|
139490
|
+
return `${identityBlock}
|
|
139155
139491
|
|
|
139156
|
-
|
|
139157
|
-
2. Did you write "I'll do X" or "I recommend X"? \u2192 Did you then DO X?
|
|
139158
|
-
3. Did you offer to do something ("Would you like me to...?") \u2192 VIOLATION. Go back and do it.
|
|
139159
|
-
4. Did you answer a question and stop? \u2192 Was there implied work? If yes, do it now.
|
|
139492
|
+
${intentBlock}
|
|
139160
139493
|
|
|
139161
|
-
|
|
139162
|
-
</turn_end_self_check>
|
|
139494
|
+
${exploreBlock}
|
|
139163
139495
|
|
|
139164
|
-
|
|
139496
|
+
${constraintsBlock}
|
|
139165
139497
|
|
|
139166
|
-
|
|
139498
|
+
${executionBlock}
|
|
139167
139499
|
|
|
139168
|
-
|
|
139500
|
+
${trackingBlock}
|
|
139169
139501
|
|
|
139170
|
-
|
|
139502
|
+
${progressBlock}
|
|
139171
139503
|
|
|
139172
|
-
|
|
139504
|
+
${delegationBlock}
|
|
139173
139505
|
|
|
139174
|
-
|
|
139506
|
+
${communicationBlock}`;
|
|
139175
139507
|
}
|
|
139176
139508
|
|
|
139177
139509
|
// src/agents/hephaestus/agent.ts
|
|
@@ -139234,7 +139566,7 @@ function createHephaestusAgent2(model, availableAgents, availableToolNames, avai
|
|
|
139234
139566
|
}
|
|
139235
139567
|
createHephaestusAgent2.mode = MODE10;
|
|
139236
139568
|
// src/agents/builtin-agents/resolve-file-uri.ts
|
|
139237
|
-
import { existsSync as existsSync84, readFileSync as
|
|
139569
|
+
import { existsSync as existsSync84, readFileSync as readFileSync57 } from "fs";
|
|
139238
139570
|
import { homedir as homedir16 } from "os";
|
|
139239
139571
|
import { isAbsolute as isAbsolute12, resolve as resolve20 } from "path";
|
|
139240
139572
|
init_logger();
|
|
@@ -139263,7 +139595,7 @@ function resolvePromptAppend(promptAppend, configDir) {
|
|
|
139263
139595
|
return `[WARNING: Could not resolve file URI: ${promptAppend}]`;
|
|
139264
139596
|
}
|
|
139265
139597
|
try {
|
|
139266
|
-
return
|
|
139598
|
+
return readFileSync57(filePath, "utf8");
|
|
139267
139599
|
} catch {
|
|
139268
139600
|
return `[WARNING: Could not read file: ${promptAppend}]`;
|
|
139269
139601
|
}
|
|
@@ -140486,8 +140818,8 @@ async function createBuiltinAgents(disabledAgents = [], agentOverrides = {}, dir
|
|
|
140486
140818
|
return result;
|
|
140487
140819
|
}
|
|
140488
140820
|
// src/features/claude-code-agent-loader/loader.ts
|
|
140489
|
-
import { existsSync as existsSync85, readdirSync as
|
|
140490
|
-
import { join as
|
|
140821
|
+
import { existsSync as existsSync85, readdirSync as readdirSync24, readFileSync as readFileSync58 } from "fs";
|
|
140822
|
+
import { join as join94, basename as basename14 } from "path";
|
|
140491
140823
|
function parseToolsConfig2(toolsStr) {
|
|
140492
140824
|
if (!toolsStr)
|
|
140493
140825
|
return;
|
|
@@ -140504,15 +140836,15 @@ function loadAgentsFromDir(agentsDir, scope) {
|
|
|
140504
140836
|
if (!existsSync85(agentsDir)) {
|
|
140505
140837
|
return [];
|
|
140506
140838
|
}
|
|
140507
|
-
const entries =
|
|
140839
|
+
const entries = readdirSync24(agentsDir, { withFileTypes: true });
|
|
140508
140840
|
const agents = [];
|
|
140509
140841
|
for (const entry of entries) {
|
|
140510
140842
|
if (!isMarkdownFile(entry))
|
|
140511
140843
|
continue;
|
|
140512
|
-
const agentPath =
|
|
140844
|
+
const agentPath = join94(agentsDir, entry.name);
|
|
140513
140845
|
const agentName = basename14(entry.name, ".md");
|
|
140514
140846
|
try {
|
|
140515
|
-
const content =
|
|
140847
|
+
const content = readFileSync58(agentPath, "utf-8");
|
|
140516
140848
|
const { data, body } = parseFrontmatter(content);
|
|
140517
140849
|
const name = data.name || agentName;
|
|
140518
140850
|
const originalDescription = data.description || "";
|
|
@@ -140542,7 +140874,7 @@ function loadAgentsFromDir(agentsDir, scope) {
|
|
|
140542
140874
|
return agents;
|
|
140543
140875
|
}
|
|
140544
140876
|
function loadUserAgents() {
|
|
140545
|
-
const userAgentsDir =
|
|
140877
|
+
const userAgentsDir = join94(getClaudeConfigDir(), "agents");
|
|
140546
140878
|
const agents = loadAgentsFromDir(userAgentsDir, "user");
|
|
140547
140879
|
const result = {};
|
|
140548
140880
|
for (const agent of agents) {
|
|
@@ -140551,7 +140883,7 @@ function loadUserAgents() {
|
|
|
140551
140883
|
return result;
|
|
140552
140884
|
}
|
|
140553
140885
|
function loadProjectAgents(directory) {
|
|
140554
|
-
const projectAgentsDir =
|
|
140886
|
+
const projectAgentsDir = join94(directory ?? process.cwd(), ".claude", "agents");
|
|
140555
140887
|
const agents = loadAgentsFromDir(projectAgentsDir, "project");
|
|
140556
140888
|
const result = {};
|
|
140557
140889
|
for (const agent of agents) {
|
|
@@ -143041,7 +143373,7 @@ async function applyAgentConfig(params) {
|
|
|
143041
143373
|
}
|
|
143042
143374
|
// src/features/claude-code-command-loader/loader.ts
|
|
143043
143375
|
import { promises as fs19 } from "fs";
|
|
143044
|
-
import { join as
|
|
143376
|
+
import { join as join95, basename as basename15 } from "path";
|
|
143045
143377
|
init_logger();
|
|
143046
143378
|
async function loadCommandsFromDir(commandsDir, scope, visited = new Set, prefix = "") {
|
|
143047
143379
|
try {
|
|
@@ -143072,7 +143404,7 @@ async function loadCommandsFromDir(commandsDir, scope, visited = new Set, prefix
|
|
|
143072
143404
|
if (entry.isDirectory()) {
|
|
143073
143405
|
if (entry.name.startsWith("."))
|
|
143074
143406
|
continue;
|
|
143075
|
-
const subDirPath =
|
|
143407
|
+
const subDirPath = join95(commandsDir, entry.name);
|
|
143076
143408
|
const subPrefix = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
143077
143409
|
const subCommands = await loadCommandsFromDir(subDirPath, scope, visited, subPrefix);
|
|
143078
143410
|
commands3.push(...subCommands);
|
|
@@ -143080,7 +143412,7 @@ async function loadCommandsFromDir(commandsDir, scope, visited = new Set, prefix
|
|
|
143080
143412
|
}
|
|
143081
143413
|
if (!isMarkdownFile(entry))
|
|
143082
143414
|
continue;
|
|
143083
|
-
const commandPath =
|
|
143415
|
+
const commandPath = join95(commandsDir, entry.name);
|
|
143084
143416
|
const baseCommandName = basename15(entry.name, ".md");
|
|
143085
143417
|
const commandName = prefix ? `${prefix}/${baseCommandName}` : baseCommandName;
|
|
143086
143418
|
try {
|
|
@@ -143139,12 +143471,12 @@ function commandsToRecord(commands3) {
|
|
|
143139
143471
|
return result;
|
|
143140
143472
|
}
|
|
143141
143473
|
async function loadUserCommands() {
|
|
143142
|
-
const userCommandsDir =
|
|
143474
|
+
const userCommandsDir = join95(getClaudeConfigDir(), "commands");
|
|
143143
143475
|
const commands3 = await loadCommandsFromDir(userCommandsDir, "user");
|
|
143144
143476
|
return commandsToRecord(commands3);
|
|
143145
143477
|
}
|
|
143146
143478
|
async function loadProjectCommands(directory) {
|
|
143147
|
-
const projectCommandsDir =
|
|
143479
|
+
const projectCommandsDir = join95(directory ?? process.cwd(), ".claude", "commands");
|
|
143148
143480
|
const commands3 = await loadCommandsFromDir(projectCommandsDir, "project");
|
|
143149
143481
|
return commandsToRecord(commands3);
|
|
143150
143482
|
}
|
|
@@ -143551,7 +143883,9 @@ function createConfigHandler(deps) {
|
|
|
143551
143883
|
// src/create-managers.ts
|
|
143552
143884
|
function createManagers(args) {
|
|
143553
143885
|
const { ctx, pluginConfig, tmuxConfig, modelCacheState, backgroundNotificationHookEnabled } = args;
|
|
143554
|
-
|
|
143886
|
+
if (tmuxConfig.enabled) {
|
|
143887
|
+
markServerRunningInProcess();
|
|
143888
|
+
}
|
|
143555
143889
|
const tmuxSessionManager = new TmuxSessionManager(ctx, tmuxConfig);
|
|
143556
143890
|
registerManagerForCleanup({
|
|
143557
143891
|
shutdown: async () => {
|
|
@@ -143751,8 +144085,14 @@ function trimToolsToCap(filteredTools, maxTools) {
|
|
|
143751
144085
|
log(`[tool-registry] Trimmed ${removed} tools to satisfy max_tools=${maxTools}. Final plugin tool count=${currentCount}.`);
|
|
143752
144086
|
}
|
|
143753
144087
|
function createToolRegistry(args) {
|
|
143754
|
-
const {
|
|
143755
|
-
|
|
144088
|
+
const {
|
|
144089
|
+
ctx,
|
|
144090
|
+
pluginConfig,
|
|
144091
|
+
managers,
|
|
144092
|
+
skillContext,
|
|
144093
|
+
availableCategories,
|
|
144094
|
+
interactiveBashEnabled = isInteractiveBashEnabled()
|
|
144095
|
+
} = args;
|
|
143756
144096
|
const backgroundTools = createBackgroundTools(managers.backgroundManager, ctx.client);
|
|
143757
144097
|
const callOmoAgent = createCallOmoAgent(ctx, managers.backgroundManager, pluginConfig.disabled_agents ?? [], pluginConfig.agents, pluginConfig.categories);
|
|
143758
144098
|
const isMultimodalLookerEnabled = !(pluginConfig.disabled_agents ?? []).some((agent) => agent.toLowerCase() === "multimodal-looker");
|
|
@@ -143829,7 +144169,7 @@ function createToolRegistry(args) {
|
|
|
143829
144169
|
task: delegateTask,
|
|
143830
144170
|
skill_mcp: skillMcpTool,
|
|
143831
144171
|
skill: skillTool,
|
|
143832
|
-
...
|
|
144172
|
+
...interactiveBashEnabled ? { interactive_bash } : {},
|
|
143833
144173
|
...taskToolsRecord,
|
|
143834
144174
|
...hashlineToolsRecord
|
|
143835
144175
|
};
|
|
@@ -144122,10 +144462,10 @@ function createChatHeadersHandler(args) {
|
|
|
144122
144462
|
|
|
144123
144463
|
// src/plugin/ultrawork-db-model-override.ts
|
|
144124
144464
|
import { Database } from "bun:sqlite";
|
|
144125
|
-
import { join as
|
|
144465
|
+
import { join as join96 } from "path";
|
|
144126
144466
|
import { existsSync as existsSync86 } from "fs";
|
|
144127
144467
|
function getDbPath() {
|
|
144128
|
-
return
|
|
144468
|
+
return join96(getDataDir(), "opencode", "opencode.db");
|
|
144129
144469
|
}
|
|
144130
144470
|
var MAX_MICROTASK_RETRIES = 10;
|
|
144131
144471
|
function tryUpdateMessageModel(db, messageId, targetModel, variant) {
|
|
@@ -144676,11 +145016,12 @@ function applyUserConfiguredFallbackChain(sessionID, agentName, currentProviderI
|
|
|
144676
145016
|
setSessionFallbackChain(sessionID, fallbackChain);
|
|
144677
145017
|
}
|
|
144678
145018
|
}
|
|
144679
|
-
function
|
|
145019
|
+
function isCompactionAgent6(agent) {
|
|
144680
145020
|
return agent.toLowerCase() === "compaction";
|
|
144681
145021
|
}
|
|
144682
145022
|
function createEventHandler2(args) {
|
|
144683
|
-
const { ctx, firstMessageVariantGate, managers, hooks: hooks2 } = args;
|
|
145023
|
+
const { ctx, pluginConfig, firstMessageVariantGate, managers, hooks: hooks2 } = args;
|
|
145024
|
+
const tmuxIntegrationEnabled = isTmuxIntegrationEnabled(pluginConfig);
|
|
144684
145025
|
const pluginContext = ctx;
|
|
144685
145026
|
const isRuntimeFallbackEnabled = hooks2.runtimeFallback !== null && hooks2.runtimeFallback !== undefined && (typeof args.pluginConfig.runtime_fallback === "boolean" ? args.pluginConfig.runtime_fallback : args.pluginConfig.runtime_fallback?.enabled ?? false);
|
|
144686
145027
|
const isModelFallbackEnabled = hooks2.modelFallback !== null && hooks2.modelFallback !== undefined;
|
|
@@ -144823,7 +145164,7 @@ function createEventHandler2(args) {
|
|
|
144823
145164
|
}
|
|
144824
145165
|
const { event } = input;
|
|
144825
145166
|
const props = event.properties;
|
|
144826
|
-
if (TMUX_ACTIVITY_EVENT_TYPES.has(event.type)) {
|
|
145167
|
+
if (tmuxIntegrationEnabled && TMUX_ACTIVITY_EVENT_TYPES.has(event.type)) {
|
|
144827
145168
|
managers.tmuxSessionManager.onEvent?.(event);
|
|
144828
145169
|
}
|
|
144829
145170
|
if (event.type === "session.created") {
|
|
@@ -144832,7 +145173,9 @@ function createEventHandler2(args) {
|
|
|
144832
145173
|
setMainSession(sessionInfo?.id);
|
|
144833
145174
|
}
|
|
144834
145175
|
firstMessageVariantGate.markSessionCreated(sessionInfo);
|
|
144835
|
-
|
|
145176
|
+
if (tmuxIntegrationEnabled) {
|
|
145177
|
+
await managers.tmuxSessionManager.onSessionCreated(event);
|
|
145178
|
+
}
|
|
144836
145179
|
}
|
|
144837
145180
|
if (event.type === "session.deleted") {
|
|
144838
145181
|
const sessionInfo = props?.info;
|
|
@@ -144860,9 +145203,11 @@ function createEventHandler2(args) {
|
|
|
144860
145203
|
deleteSessionTools(sessionInfo.id);
|
|
144861
145204
|
await managers.skillMcpManager.disconnectSession(sessionInfo.id);
|
|
144862
145205
|
await lspManager.cleanupTempDirectoryClients();
|
|
144863
|
-
|
|
144864
|
-
|
|
144865
|
-
|
|
145206
|
+
if (tmuxIntegrationEnabled) {
|
|
145207
|
+
await managers.tmuxSessionManager.onSessionDeleted({
|
|
145208
|
+
sessionID: sessionInfo.id
|
|
145209
|
+
});
|
|
145210
|
+
}
|
|
144866
145211
|
}
|
|
144867
145212
|
}
|
|
144868
145213
|
if (event.type === "message.removed") {
|
|
@@ -144876,7 +145221,7 @@ function createEventHandler2(args) {
|
|
|
144876
145221
|
const agent = info?.agent;
|
|
144877
145222
|
const role = info?.role;
|
|
144878
145223
|
if (sessionID && role === "user") {
|
|
144879
|
-
const isCompactionMessage = agent ?
|
|
145224
|
+
const isCompactionMessage = agent ? isCompactionAgent6(agent) : false;
|
|
144880
145225
|
if (agent && !isCompactionMessage) {
|
|
144881
145226
|
updateSessionAgent(sessionID, agent);
|
|
144882
145227
|
}
|