@cortexkit/aft-opencode 0.39.1 → 0.39.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/bg-notifications.d.ts +5 -20
- package/dist/bg-notifications.d.ts.map +1 -1
- package/dist/config.d.ts +23 -10
- package/dist/config.d.ts.map +1 -1
- package/dist/configure-warnings.d.ts +4 -1
- package/dist/configure-warnings.d.ts.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +535 -284
- package/dist/notifications.d.ts +1 -1
- package/dist/notifications.d.ts.map +1 -1
- package/dist/tools/bash.d.ts +3 -5
- package/dist/tools/bash.d.ts.map +1 -1
- package/dist/tools/hoisted.d.ts +5 -12
- package/dist/tools/hoisted.d.ts.map +1 -1
- package/dist/tools/navigation.d.ts.map +1 -1
- package/dist/tools/search.d.ts.map +1 -1
- package/dist/tui.js +31 -24
- package/dist/workflow-hints.d.ts +3 -4
- package/dist/workflow-hints.d.ts.map +1 -1
- package/package.json +11 -11
- package/src/tui/index.tsx +17 -14
- package/src/tui/sidebar.tsx +19 -14
- package/src/tui/types/opencode-plugin-tui.d.ts +7 -0
- package/dist/shared/live-server-client.d.ts +0 -81
- package/dist/shared/live-server-client.d.ts.map +0 -1
- package/src/shared/live-server-client.ts +0 -206
package/dist/index.js
CHANGED
|
@@ -11939,6 +11939,11 @@ var LONG_RUNNING_COMMAND_TIMEOUT_MS = {
|
|
|
11939
11939
|
function timeoutForCommand(command) {
|
|
11940
11940
|
return LONG_RUNNING_COMMAND_TIMEOUT_MS[command];
|
|
11941
11941
|
}
|
|
11942
|
+
var PASSIVE_COMMANDS = new Set(["status"]);
|
|
11943
|
+
var PASSIVE_COMMAND_TIMEOUT_MS = 5000;
|
|
11944
|
+
function isPassiveCommand(command) {
|
|
11945
|
+
return PASSIVE_COMMANDS.has(command);
|
|
11946
|
+
}
|
|
11942
11947
|
|
|
11943
11948
|
// ../aft-bridge/dist/status-bar.js
|
|
11944
11949
|
var STATUS_BAR_HEARTBEAT_CALLS = 15;
|
|
@@ -12016,6 +12021,11 @@ function bashTaskIdFrom(response) {
|
|
|
12016
12021
|
function tagStderrLine(line) {
|
|
12017
12022
|
return /^\[aft(-\w+)?\] /.test(line) ? line : `[aft] ${line}`;
|
|
12018
12023
|
}
|
|
12024
|
+
var BENIGN_CPUINFO_PROC_CPUINFO_PARSE_FAILURE = "failed to parse processor information from /proc/cpuinfo";
|
|
12025
|
+
function shouldSurfaceStderrLine(line) {
|
|
12026
|
+
const normalized = line.trim();
|
|
12027
|
+
return !(normalized === `Error in cpuinfo: ${BENIGN_CPUINFO_PROC_CPUINFO_PARSE_FAILURE}` || normalized === BENIGN_CPUINFO_PROC_CPUINFO_PARSE_FAILURE);
|
|
12028
|
+
}
|
|
12019
12029
|
function compareSemver(a, b) {
|
|
12020
12030
|
const [aMain, aPre] = a.split("-", 2);
|
|
12021
12031
|
const [bMain, bPre] = b.split("-", 2);
|
|
@@ -12107,6 +12117,7 @@ class BinaryBridge {
|
|
|
12107
12117
|
_restartCount = 0;
|
|
12108
12118
|
_shuttingDown = false;
|
|
12109
12119
|
timeoutMs;
|
|
12120
|
+
hangThreshold;
|
|
12110
12121
|
maxRestarts;
|
|
12111
12122
|
configured = false;
|
|
12112
12123
|
_configurePromise = null;
|
|
@@ -12131,6 +12142,7 @@ class BinaryBridge {
|
|
|
12131
12142
|
this.binaryPath = binaryPath;
|
|
12132
12143
|
this.cwd = cwd;
|
|
12133
12144
|
this.timeoutMs = options?.timeoutMs ?? DEFAULT_BRIDGE_TIMEOUT_MS;
|
|
12145
|
+
this.hangThreshold = options?.hangThreshold ?? BRIDGE_HANG_TIMEOUT_THRESHOLD;
|
|
12134
12146
|
this.maxRestarts = options?.maxRestarts ?? 3;
|
|
12135
12147
|
this.configOverrides = clampSemanticTimeout(configOverrides ?? {}, this.timeoutMs);
|
|
12136
12148
|
this.minVersion = options?.minVersion;
|
|
@@ -12248,7 +12260,9 @@ class BinaryBridge {
|
|
|
12248
12260
|
if (requestSessionId && options?.configureWarningClient !== undefined) {
|
|
12249
12261
|
this.configureWarningClients.set(requestSessionId, options.configureWarningClient);
|
|
12250
12262
|
}
|
|
12251
|
-
const
|
|
12263
|
+
const passive = isPassiveCommand(command);
|
|
12264
|
+
const resolvedTimeoutMs = options?.transportTimeoutMs ?? options?.timeoutMs ?? this.timeoutMs;
|
|
12265
|
+
const effectiveTimeoutMs = passive ? Math.min(resolvedTimeoutMs, PASSIVE_COMMAND_TIMEOUT_MS) : resolvedTimeoutMs;
|
|
12252
12266
|
const implicitTransportOptions = {
|
|
12253
12267
|
...options?.transportTimeoutMs !== undefined || options?.timeoutMs !== undefined ? { transportTimeoutMs: effectiveTimeoutMs } : {},
|
|
12254
12268
|
markConfiguredOnSuccess: false
|
|
@@ -12298,7 +12312,7 @@ class BinaryBridge {
|
|
|
12298
12312
|
}
|
|
12299
12313
|
const line = `${JSON.stringify(request)}
|
|
12300
12314
|
`;
|
|
12301
|
-
const keepBridgeOnTimeout = options?.keepBridgeOnTimeout === true;
|
|
12315
|
+
const keepBridgeOnTimeout = passive || options?.keepBridgeOnTimeout === true;
|
|
12302
12316
|
let requestSentAt = Date.now();
|
|
12303
12317
|
const response = await new Promise((resolve2, reject) => {
|
|
12304
12318
|
const timer = setTimeout(() => {
|
|
@@ -12320,7 +12334,7 @@ class BinaryBridge {
|
|
|
12320
12334
|
const childActiveSinceRequest = this.lastChildActivityAt > requestSentAt;
|
|
12321
12335
|
const consecutiveTimeouts = this.consecutiveRequestTimeouts + 1;
|
|
12322
12336
|
this.consecutiveRequestTimeouts = consecutiveTimeouts;
|
|
12323
|
-
const keepWarm = childActiveSinceRequest || consecutiveTimeouts <
|
|
12337
|
+
const keepWarm = childActiveSinceRequest || consecutiveTimeouts < this.hangThreshold;
|
|
12324
12338
|
const restartSuffix = keepWarm ? " — bridge kept warm" : " — restarting bridge";
|
|
12325
12339
|
const timeoutMsg = `Request "${command}" (id=${id}) timed out after ${effectiveTimeoutMs}ms${restartSuffix}`;
|
|
12326
12340
|
if (requestSessionId) {
|
|
@@ -12615,7 +12629,7 @@ class BinaryBridge {
|
|
|
12615
12629
|
`)) !== -1) {
|
|
12616
12630
|
const line = this.stderrBuffer.slice(0, newlineIdx).replace(/\r$/, "");
|
|
12617
12631
|
this.stderrBuffer = this.stderrBuffer.slice(newlineIdx + 1);
|
|
12618
|
-
if (!line)
|
|
12632
|
+
if (!line || !shouldSurfaceStderrLine(line))
|
|
12619
12633
|
continue;
|
|
12620
12634
|
const tagged = tagStderrLine(line);
|
|
12621
12635
|
this.logVia(tagged);
|
|
@@ -12625,7 +12639,7 @@ class BinaryBridge {
|
|
|
12625
12639
|
flushStderrBuffer() {
|
|
12626
12640
|
const line = this.stderrBuffer.replace(/\r$/, "");
|
|
12627
12641
|
this.stderrBuffer = "";
|
|
12628
|
-
if (!line)
|
|
12642
|
+
if (!line || !shouldSurfaceStderrLine(line))
|
|
12629
12643
|
return;
|
|
12630
12644
|
const tagged = tagStderrLine(line);
|
|
12631
12645
|
this.logVia(tagged);
|
|
@@ -12850,6 +12864,208 @@ class BinaryBridge {
|
|
|
12850
12864
|
}
|
|
12851
12865
|
}
|
|
12852
12866
|
}
|
|
12867
|
+
// ../aft-bridge/dist/callgraph-format.js
|
|
12868
|
+
import { homedir as homedir3 } from "node:os";
|
|
12869
|
+
var PLAIN_CALLGRAPH_THEME = {
|
|
12870
|
+
fg: (_role, text) => text
|
|
12871
|
+
};
|
|
12872
|
+
function asRecord(value) {
|
|
12873
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
12874
|
+
return;
|
|
12875
|
+
return value;
|
|
12876
|
+
}
|
|
12877
|
+
function asRecords(value) {
|
|
12878
|
+
return Array.isArray(value) ? value.map(asRecord).filter(Boolean) : [];
|
|
12879
|
+
}
|
|
12880
|
+
function asString(value) {
|
|
12881
|
+
return typeof value === "string" ? value : undefined;
|
|
12882
|
+
}
|
|
12883
|
+
function asNumber(value) {
|
|
12884
|
+
return typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
12885
|
+
}
|
|
12886
|
+
function asBoolean(value) {
|
|
12887
|
+
return typeof value === "boolean" ? value : undefined;
|
|
12888
|
+
}
|
|
12889
|
+
function shortenPath(path2) {
|
|
12890
|
+
const home = homedir3();
|
|
12891
|
+
if (path2.startsWith(home))
|
|
12892
|
+
return `~${path2.slice(home.length)}`;
|
|
12893
|
+
return path2;
|
|
12894
|
+
}
|
|
12895
|
+
function joinNonEmpty(parts, separator = " · ") {
|
|
12896
|
+
return parts.filter((part) => Boolean(part && part.length > 0)).join(separator);
|
|
12897
|
+
}
|
|
12898
|
+
function treeLine(depth, text) {
|
|
12899
|
+
return `${" ".repeat(depth)}${depth === 0 ? "" : "↳ "}${text}`;
|
|
12900
|
+
}
|
|
12901
|
+
function renderCallTreeNode(node, depth, lines, theme) {
|
|
12902
|
+
const name = asString(node.name) ?? "(unknown)";
|
|
12903
|
+
const file = shortenPath(asString(node.file) ?? "(unknown file)");
|
|
12904
|
+
const line = asNumber(node.line);
|
|
12905
|
+
const unresolved = node.resolved === false ? ` ${theme.fg("warning", "[unresolved]")}` : "";
|
|
12906
|
+
const location = line !== undefined ? `[${file}:${line}]` : `[${file}]`;
|
|
12907
|
+
lines.push(treeLine(depth, `${name} ${location}${unresolved}`));
|
|
12908
|
+
asRecords(node.children).forEach((child) => {
|
|
12909
|
+
renderCallTreeNode(child, depth + 1, lines, theme);
|
|
12910
|
+
});
|
|
12911
|
+
}
|
|
12912
|
+
function depthWarning(response, theme, depthField = "depth_limited", truncatedField = "truncated") {
|
|
12913
|
+
const limited = asBoolean(response[depthField]);
|
|
12914
|
+
const truncated = asNumber(response[truncatedField]) ?? 0;
|
|
12915
|
+
if (!limited && truncated === 0)
|
|
12916
|
+
return "";
|
|
12917
|
+
const detail = truncated > 0 ? `, ${truncated} truncated` : "";
|
|
12918
|
+
return theme.fg("warning", `(depth limited${detail})`);
|
|
12919
|
+
}
|
|
12920
|
+
function renderTracePath(path2, index, lines) {
|
|
12921
|
+
lines.push(`Path ${index + 1}`);
|
|
12922
|
+
asRecords(path2.hops).forEach((hop, hopIndex) => {
|
|
12923
|
+
const symbol = asString(hop.symbol) ?? "(unknown)";
|
|
12924
|
+
const file = shortenPath(asString(hop.file) ?? "(unknown file)");
|
|
12925
|
+
const line = asNumber(hop.line);
|
|
12926
|
+
const entry = hop.is_entry_point === true ? " [entry]" : "";
|
|
12927
|
+
lines.push(treeLine(hopIndex + 1, `${symbol}${entry} ${line !== undefined ? `[${file}:${line}]` : `[${file}]`}`));
|
|
12928
|
+
});
|
|
12929
|
+
}
|
|
12930
|
+
function renderCallersGroupLines(group, theme) {
|
|
12931
|
+
const file = shortenPath(asString(group.file) ?? "(unknown file)");
|
|
12932
|
+
const lines = [theme.fg("accent", file)];
|
|
12933
|
+
const callers = asRecords(group.callers);
|
|
12934
|
+
const bySymbol = new Map;
|
|
12935
|
+
for (const caller of callers) {
|
|
12936
|
+
const symbol = asString(caller.symbol) ?? "(unknown)";
|
|
12937
|
+
const line = asNumber(caller.line);
|
|
12938
|
+
const bucket = bySymbol.get(symbol) ?? [];
|
|
12939
|
+
if (line !== undefined)
|
|
12940
|
+
bucket.push(line);
|
|
12941
|
+
bySymbol.set(symbol, bucket);
|
|
12942
|
+
}
|
|
12943
|
+
const symbols = [...bySymbol.keys()].sort((a, b) => a.localeCompare(b));
|
|
12944
|
+
for (const symbol of symbols) {
|
|
12945
|
+
const lineNums = (bySymbol.get(symbol) ?? []).sort((a, b) => a - b);
|
|
12946
|
+
const linePart = lineNums.length > 0 ? lineNums.map(String).join(", ") : "?";
|
|
12947
|
+
lines.push(` ↳ ${symbol}:${linePart}`);
|
|
12948
|
+
}
|
|
12949
|
+
return lines;
|
|
12950
|
+
}
|
|
12951
|
+
function formatCallgraphSections(op, response, theme = PLAIN_CALLGRAPH_THEME) {
|
|
12952
|
+
const record = asRecord(response);
|
|
12953
|
+
if (!record)
|
|
12954
|
+
return [theme.fg("muted", "No navigation result.")];
|
|
12955
|
+
if (op === "call_tree") {
|
|
12956
|
+
const lines = [];
|
|
12957
|
+
renderCallTreeNode(record, 0, lines, theme);
|
|
12958
|
+
const warning = depthWarning(record, theme);
|
|
12959
|
+
if (warning)
|
|
12960
|
+
lines.push(warning);
|
|
12961
|
+
return lines.length > 0 ? lines : [theme.fg("muted", "No call tree available.")];
|
|
12962
|
+
}
|
|
12963
|
+
if (op === "callers") {
|
|
12964
|
+
const groups = asRecords(record.callers);
|
|
12965
|
+
const warning = depthWarning(record, theme);
|
|
12966
|
+
const total = asNumber(record.total_callers) ?? 0;
|
|
12967
|
+
const sections2 = [
|
|
12968
|
+
joinNonEmpty([
|
|
12969
|
+
theme.fg("success", `${total} caller${total === 1 ? "" : "s"}`),
|
|
12970
|
+
theme.fg("muted", `${groups.length} file group${groups.length === 1 ? "" : "s"}`),
|
|
12971
|
+
warning
|
|
12972
|
+
])
|
|
12973
|
+
];
|
|
12974
|
+
groups.forEach((group) => {
|
|
12975
|
+
sections2.push(renderCallersGroupLines(group, theme).join(`
|
|
12976
|
+
`));
|
|
12977
|
+
});
|
|
12978
|
+
return sections2;
|
|
12979
|
+
}
|
|
12980
|
+
if (op === "trace_to_symbol") {
|
|
12981
|
+
const path2 = asRecords(record.path);
|
|
12982
|
+
const complete = asBoolean(record.complete);
|
|
12983
|
+
const reason = asString(record.reason);
|
|
12984
|
+
if (path2.length === 0) {
|
|
12985
|
+
const prefix = complete === false ? theme.fg("warning", "No complete path") : theme.fg("muted", "No path");
|
|
12986
|
+
return [`${prefix}${reason ? ` (${reason})` : ""}`];
|
|
12987
|
+
}
|
|
12988
|
+
const lines = [theme.fg("success", `${path2.length} hop${path2.length === 1 ? "" : "s"}`)];
|
|
12989
|
+
path2.forEach((hop, index) => {
|
|
12990
|
+
const symbol = asString(hop.symbol) ?? "(unknown)";
|
|
12991
|
+
const file = shortenPath(asString(hop.file) ?? "(unknown file)");
|
|
12992
|
+
const line = asNumber(hop.line);
|
|
12993
|
+
lines.push(treeLine(index + 1, `${symbol} ${line !== undefined ? `[${file}:${line}]` : `[${file}]`}`));
|
|
12994
|
+
});
|
|
12995
|
+
return lines;
|
|
12996
|
+
}
|
|
12997
|
+
if (op === "trace_to") {
|
|
12998
|
+
const paths = asRecords(record.paths);
|
|
12999
|
+
const warning = depthWarning(record, theme, "max_depth_reached", "truncated_paths");
|
|
13000
|
+
const totalPaths = asNumber(record.total_paths) ?? paths.length;
|
|
13001
|
+
const entryPoints = asNumber(record.entry_points_found) ?? 0;
|
|
13002
|
+
const sections2 = [
|
|
13003
|
+
joinNonEmpty([
|
|
13004
|
+
theme.fg("success", `${totalPaths} path${totalPaths === 1 ? "" : "s"}`),
|
|
13005
|
+
theme.fg("muted", `${entryPoints} entry point${entryPoints === 1 ? "" : "s"}`),
|
|
13006
|
+
warning
|
|
13007
|
+
])
|
|
13008
|
+
];
|
|
13009
|
+
if (paths.length === 0)
|
|
13010
|
+
sections2.push(theme.fg("muted", "No entry paths found."));
|
|
13011
|
+
paths.forEach((path2, index) => {
|
|
13012
|
+
const lines = [];
|
|
13013
|
+
renderTracePath(path2, index, lines);
|
|
13014
|
+
sections2.push(lines.join(`
|
|
13015
|
+
`));
|
|
13016
|
+
});
|
|
13017
|
+
return sections2;
|
|
13018
|
+
}
|
|
13019
|
+
if (op === "impact") {
|
|
13020
|
+
const callers = asRecords(record.callers);
|
|
13021
|
+
const warning = depthWarning(record, theme);
|
|
13022
|
+
const totalAffected = asNumber(record.total_affected) ?? callers.length;
|
|
13023
|
+
const affectedFiles = asNumber(record.affected_files) ?? 0;
|
|
13024
|
+
const sections2 = [
|
|
13025
|
+
joinNonEmpty([
|
|
13026
|
+
theme.fg("warning", `${totalAffected} affected call site${totalAffected === 1 ? "" : "s"}`),
|
|
13027
|
+
theme.fg("muted", `${affectedFiles} file${affectedFiles === 1 ? "" : "s"}`),
|
|
13028
|
+
warning
|
|
13029
|
+
])
|
|
13030
|
+
];
|
|
13031
|
+
if (callers.length === 0)
|
|
13032
|
+
sections2.push(theme.fg("muted", "No impacted callers found."));
|
|
13033
|
+
callers.forEach((caller) => {
|
|
13034
|
+
const file = shortenPath(asString(caller.caller_file) ?? "(unknown file)");
|
|
13035
|
+
const symbol = asString(caller.caller_symbol) ?? "(unknown)";
|
|
13036
|
+
const line = asNumber(caller.line) ?? 0;
|
|
13037
|
+
const entry = caller.is_entry_point === true ? ` ${theme.fg("warning", "[entry]")}` : "";
|
|
13038
|
+
const expression = asString(caller.call_expression);
|
|
13039
|
+
const params = Array.isArray(caller.parameters) ? caller.parameters.map(String).join(", ") : "";
|
|
13040
|
+
sections2.push([
|
|
13041
|
+
`${theme.fg("accent", file)}:${line}`,
|
|
13042
|
+
` ↳ ${symbol}${entry}`,
|
|
13043
|
+
expression ? ` ${theme.fg("muted", expression)}` : undefined,
|
|
13044
|
+
params ? ` ${theme.fg("muted", `params: ${params}`)}` : undefined
|
|
13045
|
+
].filter(Boolean).join(`
|
|
13046
|
+
`));
|
|
13047
|
+
});
|
|
13048
|
+
return sections2;
|
|
13049
|
+
}
|
|
13050
|
+
const hops = asRecords(record.hops);
|
|
13051
|
+
const sections = [
|
|
13052
|
+
joinNonEmpty([
|
|
13053
|
+
theme.fg("success", `${hops.length} hop${hops.length === 1 ? "" : "s"}`),
|
|
13054
|
+
asBoolean(record.depth_limited) ? theme.fg("warning", "(depth limited)") : undefined
|
|
13055
|
+
])
|
|
13056
|
+
];
|
|
13057
|
+
if (hops.length === 0)
|
|
13058
|
+
sections.push(theme.fg("muted", "No data-flow hops found."));
|
|
13059
|
+
hops.forEach((hop, index) => {
|
|
13060
|
+
const file = shortenPath(asString(hop.file) ?? "(unknown file)");
|
|
13061
|
+
const symbol = asString(hop.symbol) ?? "(unknown)";
|
|
13062
|
+
const variable = asString(hop.variable) ?? "(unknown)";
|
|
13063
|
+
const line = asNumber(hop.line) ?? 0;
|
|
13064
|
+
const approximate = hop.approximate === true ? ` ${theme.fg("warning", "[approx]")}` : "";
|
|
13065
|
+
sections.push(treeLine(index, `${variable} ${theme.fg("muted", `${asString(hop.flow_type) ?? "flow"}`)} ${symbol} [${file}:${line}]${approximate}`));
|
|
13066
|
+
});
|
|
13067
|
+
return sections;
|
|
13068
|
+
}
|
|
12853
13069
|
// ../aft-bridge/dist/coerce.js
|
|
12854
13070
|
function coerceStringArray(value) {
|
|
12855
13071
|
if (Array.isArray(value)) {
|
|
@@ -12886,7 +13102,7 @@ function isEmptyParam(value) {
|
|
|
12886
13102
|
import { spawnSync } from "node:child_process";
|
|
12887
13103
|
import { createHash, randomUUID } from "node:crypto";
|
|
12888
13104
|
import { chmodSync, closeSync, copyFileSync, createWriteStream, existsSync, mkdirSync, openSync, readFileSync, renameSync, rmSync, statSync, unlinkSync, writeSync } from "node:fs";
|
|
12889
|
-
import { homedir as
|
|
13105
|
+
import { homedir as homedir4 } from "node:os";
|
|
12890
13106
|
import { join as join3 } from "node:path";
|
|
12891
13107
|
import { Readable } from "node:stream";
|
|
12892
13108
|
import { pipeline } from "node:stream/promises";
|
|
@@ -12944,10 +13160,10 @@ function isExpectedCachedBinary(binaryPath, tag) {
|
|
|
12944
13160
|
function getCacheDir() {
|
|
12945
13161
|
if (process.platform === "win32") {
|
|
12946
13162
|
const localAppData = process.env.LOCALAPPDATA || process.env.APPDATA;
|
|
12947
|
-
const base2 = localAppData || join3(
|
|
13163
|
+
const base2 = localAppData || join3(homedir4(), "AppData", "Local");
|
|
12948
13164
|
return join3(base2, "aft", "bin");
|
|
12949
13165
|
}
|
|
12950
|
-
const base = process.env.XDG_CACHE_HOME || join3(
|
|
13166
|
+
const base = process.env.XDG_CACHE_HOME || join3(homedir4(), ".cache");
|
|
12951
13167
|
return join3(base, "aft", "bin");
|
|
12952
13168
|
}
|
|
12953
13169
|
function getBinaryName() {
|
|
@@ -13220,7 +13436,7 @@ function stripJsoncSymbols(value) {
|
|
|
13220
13436
|
// ../aft-bridge/dist/migration.js
|
|
13221
13437
|
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
13222
13438
|
import { existsSync as existsSync4, mkdirSync as mkdirSync4 } from "node:fs";
|
|
13223
|
-
import { homedir as
|
|
13439
|
+
import { homedir as homedir6, tmpdir } from "node:os";
|
|
13224
13440
|
import { dirname as dirname2, join as join6 } from "node:path";
|
|
13225
13441
|
|
|
13226
13442
|
// ../aft-bridge/dist/paths.js
|
|
@@ -13277,7 +13493,7 @@ function markAnnouncementSeen(storageRoot, harness, currentVersion) {
|
|
|
13277
13493
|
import { execSync } from "node:child_process";
|
|
13278
13494
|
import { chmodSync as chmodSync2, closeSync as closeSync2, copyFileSync as copyFileSync2, existsSync as existsSync3, mkdirSync as mkdirSync3, openSync as openSync2, readSync, renameSync as renameSync3, unlinkSync as unlinkSync2 } from "node:fs";
|
|
13279
13495
|
import { createRequire as createRequire2 } from "node:module";
|
|
13280
|
-
import { homedir as
|
|
13496
|
+
import { homedir as homedir5 } from "node:os";
|
|
13281
13497
|
import { join as join5 } from "node:path";
|
|
13282
13498
|
var ensureBinaryForResolver = ensureBinary;
|
|
13283
13499
|
function copyToVersionedCache(npmBinaryPath, knownVersion) {
|
|
@@ -13319,7 +13535,7 @@ function normalizeBareVersion(version) {
|
|
|
13319
13535
|
return version.startsWith("v") ? version.slice(1) : version;
|
|
13320
13536
|
}
|
|
13321
13537
|
function homeDirFromEnv(env) {
|
|
13322
|
-
return (process.platform === "win32" ? env.USERPROFILE || env.HOME : env.HOME) ||
|
|
13538
|
+
return (process.platform === "win32" ? env.USERPROFILE || env.HOME : env.HOME) || homedir5();
|
|
13323
13539
|
}
|
|
13324
13540
|
function cacheDirFromEnv(env) {
|
|
13325
13541
|
if (process.platform === "win32") {
|
|
@@ -13501,8 +13717,8 @@ function dataHome() {
|
|
|
13501
13717
|
}
|
|
13502
13718
|
function homeDir() {
|
|
13503
13719
|
if (process.platform === "win32")
|
|
13504
|
-
return process.env.USERPROFILE || process.env.HOME ||
|
|
13505
|
-
return process.env.HOME ||
|
|
13720
|
+
return process.env.USERPROFILE || process.env.HOME || homedir6();
|
|
13721
|
+
return process.env.HOME || homedir6();
|
|
13506
13722
|
}
|
|
13507
13723
|
function resolveLegacyStorageRoot(harness) {
|
|
13508
13724
|
if (harness === "pi")
|
|
@@ -13592,13 +13808,13 @@ async function ensureStorageMigrated(opts) {
|
|
|
13592
13808
|
}
|
|
13593
13809
|
// ../aft-bridge/dist/npm-resolver.js
|
|
13594
13810
|
import { readdirSync, statSync as statSync2 } from "node:fs";
|
|
13595
|
-
import { homedir as
|
|
13811
|
+
import { homedir as homedir7 } from "node:os";
|
|
13596
13812
|
import { delimiter, dirname as dirname3, isAbsolute as isAbsolute2, join as join7 } from "node:path";
|
|
13597
13813
|
function defaultDeps() {
|
|
13598
13814
|
return {
|
|
13599
13815
|
platform: process.platform,
|
|
13600
13816
|
env: process.env,
|
|
13601
|
-
home:
|
|
13817
|
+
home: homedir7(),
|
|
13602
13818
|
execPath: process.execPath
|
|
13603
13819
|
};
|
|
13604
13820
|
}
|
|
@@ -14961,6 +15177,7 @@ class BridgePool {
|
|
|
14961
15177
|
this.projectConfigLoader = options.projectConfigLoader;
|
|
14962
15178
|
this.bridgeOptions = {
|
|
14963
15179
|
timeoutMs: options.timeoutMs,
|
|
15180
|
+
hangThreshold: options.hangThreshold,
|
|
14964
15181
|
maxRestarts: options.maxRestarts,
|
|
14965
15182
|
minVersion: options.minVersion,
|
|
14966
15183
|
onVersionMismatch: options.onVersionMismatch,
|
|
@@ -15364,9 +15581,6 @@ function write(level, message, data, sessionId) {
|
|
|
15364
15581
|
function log2(message, data) {
|
|
15365
15582
|
write("INFO", message, data);
|
|
15366
15583
|
}
|
|
15367
|
-
function debug(message, data) {
|
|
15368
|
-
write("DEBUG", message, data);
|
|
15369
|
-
}
|
|
15370
15584
|
function warn2(message, data) {
|
|
15371
15585
|
write("WARN", message, data);
|
|
15372
15586
|
}
|
|
@@ -15376,9 +15590,6 @@ function error2(message, data) {
|
|
|
15376
15590
|
function sessionLog(sessionId, message, data) {
|
|
15377
15591
|
write("INFO", message, data, sessionId);
|
|
15378
15592
|
}
|
|
15379
|
-
function sessionDebug(sessionId, message, data) {
|
|
15380
|
-
write("DEBUG", message, data, sessionId);
|
|
15381
|
-
}
|
|
15382
15593
|
function sessionWarn(sessionId, message, data) {
|
|
15383
15594
|
write("WARN", message, data, sessionId);
|
|
15384
15595
|
}
|
|
@@ -15472,84 +15683,6 @@ async function resolvePromptContext(client, sessionId) {
|
|
|
15472
15683
|
return result;
|
|
15473
15684
|
}
|
|
15474
15685
|
|
|
15475
|
-
// src/shared/live-server-client.ts
|
|
15476
|
-
import { createOpencodeClient } from "@opencode-ai/sdk";
|
|
15477
|
-
var clientCache = new Map;
|
|
15478
|
-
function cacheKey(serverUrl, directory) {
|
|
15479
|
-
return `${serverUrl}|${directory}`;
|
|
15480
|
-
}
|
|
15481
|
-
function normalizeServerUrl(serverUrl) {
|
|
15482
|
-
try {
|
|
15483
|
-
return new URL(serverUrl).toString();
|
|
15484
|
-
} catch {
|
|
15485
|
-
return serverUrl;
|
|
15486
|
-
}
|
|
15487
|
-
}
|
|
15488
|
-
function serverAuthHeaders() {
|
|
15489
|
-
const password = process.env.OPENCODE_SERVER_PASSWORD;
|
|
15490
|
-
if (!password)
|
|
15491
|
-
return;
|
|
15492
|
-
const username = process.env.OPENCODE_SERVER_USERNAME ?? "opencode";
|
|
15493
|
-
return {
|
|
15494
|
-
Authorization: `Basic ${Buffer.from(`${username}:${password}`).toString("base64")}`
|
|
15495
|
-
};
|
|
15496
|
-
}
|
|
15497
|
-
function getLiveServerClient(serverUrl, directory) {
|
|
15498
|
-
const key = cacheKey(serverUrl, directory);
|
|
15499
|
-
const cached = clientCache.get(key);
|
|
15500
|
-
if (cached)
|
|
15501
|
-
return cached;
|
|
15502
|
-
const client = createOpencodeClient({
|
|
15503
|
-
baseUrl: serverUrl,
|
|
15504
|
-
directory,
|
|
15505
|
-
headers: serverAuthHeaders(),
|
|
15506
|
-
fetch: globalThis.fetch
|
|
15507
|
-
});
|
|
15508
|
-
clientCache.set(key, client);
|
|
15509
|
-
return client;
|
|
15510
|
-
}
|
|
15511
|
-
var liveServerWakeAvailableByServerUrl = new Map;
|
|
15512
|
-
var legacyLiveServerWakeAvailable = false;
|
|
15513
|
-
async function probeServerReachable(serverUrl, timeoutMs = 1500) {
|
|
15514
|
-
if (!serverUrl)
|
|
15515
|
-
return false;
|
|
15516
|
-
const normalizedServerUrl = normalizeServerUrl(serverUrl);
|
|
15517
|
-
const controller = new AbortController;
|
|
15518
|
-
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
15519
|
-
let reachable = false;
|
|
15520
|
-
try {
|
|
15521
|
-
const probeUrl = new URL("/session", serverUrl).toString();
|
|
15522
|
-
const res = await globalThis.fetch(probeUrl, {
|
|
15523
|
-
method: "GET",
|
|
15524
|
-
headers: serverAuthHeaders(),
|
|
15525
|
-
signal: controller.signal
|
|
15526
|
-
});
|
|
15527
|
-
reachable = res.ok || res.status === 401 || res.status === 403;
|
|
15528
|
-
} catch {
|
|
15529
|
-
reachable = false;
|
|
15530
|
-
} finally {
|
|
15531
|
-
clearTimeout(timer);
|
|
15532
|
-
liveServerWakeAvailableByServerUrl.set(normalizedServerUrl, reachable);
|
|
15533
|
-
}
|
|
15534
|
-
return reachable;
|
|
15535
|
-
}
|
|
15536
|
-
function setLiveServerWakeAvailable(serverUrlOrAvailable, available) {
|
|
15537
|
-
if (typeof serverUrlOrAvailable === "boolean") {
|
|
15538
|
-
legacyLiveServerWakeAvailable = serverUrlOrAvailable;
|
|
15539
|
-
return;
|
|
15540
|
-
}
|
|
15541
|
-
if (!serverUrlOrAvailable) {
|
|
15542
|
-
legacyLiveServerWakeAvailable = available ?? false;
|
|
15543
|
-
return;
|
|
15544
|
-
}
|
|
15545
|
-
liveServerWakeAvailableByServerUrl.set(normalizeServerUrl(serverUrlOrAvailable), available ?? false);
|
|
15546
|
-
}
|
|
15547
|
-
function useLiveServerWake(serverUrl) {
|
|
15548
|
-
if (!serverUrl)
|
|
15549
|
-
return legacyLiveServerWakeAvailable;
|
|
15550
|
-
return liveServerWakeAvailableByServerUrl.get(normalizeServerUrl(serverUrl)) ?? false;
|
|
15551
|
-
}
|
|
15552
|
-
|
|
15553
15686
|
// src/bg-notifications.ts
|
|
15554
15687
|
function hashReminder(text) {
|
|
15555
15688
|
return createHash3("sha256").update(text).digest("hex").slice(0, 16);
|
|
@@ -15776,18 +15909,18 @@ async function triggerWakeIfPending(drainContext, skipDrain, includeDeferredComp
|
|
|
15776
15909
|
directory: drainContext.directory,
|
|
15777
15910
|
attempt: state.wakeRetryAttempts + 1
|
|
15778
15911
|
});
|
|
15779
|
-
throw new Error("
|
|
15912
|
+
throw new Error("in-process wake client unavailable: input.client absent");
|
|
15780
15913
|
}
|
|
15781
15914
|
return drainContext.client;
|
|
15782
15915
|
};
|
|
15783
|
-
const sendPrompt = async (
|
|
15784
|
-
if (typeof
|
|
15785
|
-
throw new Error(
|
|
15916
|
+
const sendPrompt = async (client2) => {
|
|
15917
|
+
if (typeof client2.session?.promptAsync !== "function") {
|
|
15918
|
+
throw new Error("wake client.session.promptAsync is unavailable");
|
|
15786
15919
|
}
|
|
15787
|
-
const promptContext = await resolvePromptContext(
|
|
15920
|
+
const promptContext = await resolvePromptContext(client2, drainContext.sessionID);
|
|
15788
15921
|
const body = {
|
|
15789
15922
|
noReply: false,
|
|
15790
|
-
parts: [{ type: "text", text: reminder }]
|
|
15923
|
+
parts: [{ type: "text", text: reminder, synthetic: true }]
|
|
15791
15924
|
};
|
|
15792
15925
|
if (promptContext?.agent)
|
|
15793
15926
|
body.agent = promptContext.agent;
|
|
@@ -15807,7 +15940,6 @@ async function triggerWakeIfPending(drainContext, skipDrain, includeDeferredComp
|
|
|
15807
15940
|
directory: drainContext.directory,
|
|
15808
15941
|
reminder_sha256: hashReminder(reminder),
|
|
15809
15942
|
reminder_chars: reminder.length,
|
|
15810
|
-
wake_client_path: clientPath,
|
|
15811
15943
|
prompt_context: promptContext ? {
|
|
15812
15944
|
agent: promptContext.agent,
|
|
15813
15945
|
model: promptContext.model ? {
|
|
@@ -15822,18 +15954,16 @@ async function triggerWakeIfPending(drainContext, skipDrain, includeDeferredComp
|
|
|
15822
15954
|
...wakeMeta
|
|
15823
15955
|
});
|
|
15824
15956
|
try {
|
|
15825
|
-
await
|
|
15957
|
+
await client2.session.promptAsync({
|
|
15826
15958
|
path: { id: drainContext.sessionID },
|
|
15827
15959
|
body
|
|
15828
15960
|
});
|
|
15829
15961
|
} catch (err) {
|
|
15830
|
-
|
|
15831
|
-
logPromptError(drainContext.sessionID, `${LOG_PREFIX} wake promptAsync error`, {
|
|
15962
|
+
sessionWarn(drainContext.sessionID, `${LOG_PREFIX} wake promptAsync error`, {
|
|
15832
15963
|
event: "bash_completion_wake_prompt_async_error",
|
|
15833
15964
|
delivery_id: deliveryID2,
|
|
15834
15965
|
attempt: state.wakeRetryAttempts + 1,
|
|
15835
15966
|
task_ids: taskIDs,
|
|
15836
|
-
wake_client_path: clientPath,
|
|
15837
15967
|
error: err instanceof Error ? err.message : String(err)
|
|
15838
15968
|
});
|
|
15839
15969
|
throw err;
|
|
@@ -15842,38 +15972,12 @@ async function triggerWakeIfPending(drainContext, skipDrain, includeDeferredComp
|
|
|
15842
15972
|
event: "bash_completion_wake_prompt_async_ok",
|
|
15843
15973
|
delivery_id: deliveryID2,
|
|
15844
15974
|
attempt: state.wakeRetryAttempts + 1,
|
|
15845
|
-
task_ids: taskIDs
|
|
15846
|
-
wake_client_path: clientPath
|
|
15975
|
+
task_ids: taskIDs
|
|
15847
15976
|
});
|
|
15848
15977
|
return deliveryID2;
|
|
15849
15978
|
};
|
|
15850
|
-
|
|
15851
|
-
|
|
15852
|
-
const liveClient = getLiveServerClient(drainContext.serverUrl, drainContext.directory);
|
|
15853
|
-
const deliveryID2 = await sendPrompt(liveClient, "live-server");
|
|
15854
|
-
await ackCompletions(drainContext, deliveredCompletions, deliveryID2);
|
|
15855
|
-
return;
|
|
15856
|
-
} catch (err) {
|
|
15857
|
-
setLiveServerWakeAvailable(drainContext.serverUrl, false);
|
|
15858
|
-
sessionDebug(drainContext.sessionID, `${LOG_PREFIX} live-server wake failed; falling back`, {
|
|
15859
|
-
event: "bash_completion_wake_live_server_fallback",
|
|
15860
|
-
task_ids: taskIDs,
|
|
15861
|
-
directory: drainContext.directory,
|
|
15862
|
-
server_url: drainContext.serverUrl,
|
|
15863
|
-
attempt: state.wakeRetryAttempts + 1,
|
|
15864
|
-
error: err instanceof Error ? err.message : String(err)
|
|
15865
|
-
});
|
|
15866
|
-
const fallbackClient2 = getInProcessClient();
|
|
15867
|
-
const deliveryID2 = await sendPrompt(fallbackClient2, "in-process-fallback");
|
|
15868
|
-
state.retryDelayMs = null;
|
|
15869
|
-
state.wakeRetryAttempts = 0;
|
|
15870
|
-
state.wakeHardStopped = false;
|
|
15871
|
-
await ackCompletions(drainContext, deliveredCompletions, deliveryID2);
|
|
15872
|
-
return;
|
|
15873
|
-
}
|
|
15874
|
-
}
|
|
15875
|
-
const fallbackClient = getInProcessClient();
|
|
15876
|
-
const deliveryID = await sendPrompt(fallbackClient, "in-process-fallback");
|
|
15979
|
+
const client = getInProcessClient();
|
|
15980
|
+
const deliveryID = await sendPrompt(client);
|
|
15877
15981
|
await ackCompletions(drainContext, deliveredCompletions, deliveryID);
|
|
15878
15982
|
}, (err, hardStopped) => {
|
|
15879
15983
|
sessionWarn(drainContext.sessionID, hardStopped ? `${LOG_PREFIX} wake send failed ${MAX_WAKE_SEND_ATTEMPTS} times; stopping retries: ${err instanceof Error ? err.message : String(err)}` : `${LOG_PREFIX} wake send failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -16256,7 +16360,7 @@ function formatDuration(completion) {
|
|
|
16256
16360
|
|
|
16257
16361
|
// src/config.ts
|
|
16258
16362
|
import { existsSync as existsSync6, readFileSync as readFileSync4, renameSync as renameSync4, unlinkSync as unlinkSync4, writeFileSync as writeFileSync3 } from "node:fs";
|
|
16259
|
-
import { homedir as
|
|
16363
|
+
import { homedir as homedir8 } from "node:os";
|
|
16260
16364
|
import { join as join10 } from "node:path";
|
|
16261
16365
|
var import_comment_json = __toESM(require_src2(), 1);
|
|
16262
16366
|
|
|
@@ -29871,6 +29975,10 @@ var BashFeaturesSchema = exports_external.object({
|
|
|
29871
29975
|
foreground_wait_window_ms: exports_external.number().int().positive().optional()
|
|
29872
29976
|
});
|
|
29873
29977
|
var BashConfigSchema = exports_external.union([exports_external.boolean(), BashFeaturesSchema]);
|
|
29978
|
+
var BridgeConfigSchema = exports_external.object({
|
|
29979
|
+
request_timeout_ms: exports_external.number().int().min(1000, { message: "bridge.request_timeout_ms must be at least 1000" }).optional(),
|
|
29980
|
+
hang_threshold: exports_external.number().int().min(1, { message: "bridge.hang_threshold must be at least 1" }).optional()
|
|
29981
|
+
});
|
|
29874
29982
|
var InspectConfigSchema = exports_external.object({
|
|
29875
29983
|
enabled: exports_external.boolean().optional(),
|
|
29876
29984
|
tier2_idle_minutes: exports_external.number().min(0).optional(),
|
|
@@ -29903,6 +30011,8 @@ var AftConfigSchema = exports_external.object({
|
|
|
29903
30011
|
restrict_to_project_root: exports_external.boolean().optional(),
|
|
29904
30012
|
search_index: exports_external.boolean().optional(),
|
|
29905
30013
|
semantic_search: exports_external.boolean().optional(),
|
|
30014
|
+
callgraph_store: exports_external.boolean().optional(),
|
|
30015
|
+
callgraph_chunk_size: exports_external.number().optional(),
|
|
29906
30016
|
inspect: InspectConfigSchema.optional(),
|
|
29907
30017
|
bash: BashConfigSchema.optional(),
|
|
29908
30018
|
experimental: ExperimentalConfigSchema.optional(),
|
|
@@ -29910,7 +30020,8 @@ var AftConfigSchema = exports_external.object({
|
|
|
29910
30020
|
url_fetch_allow_private: exports_external.boolean().optional(),
|
|
29911
30021
|
semantic: SemanticConfigSchema.optional(),
|
|
29912
30022
|
max_callgraph_files: exports_external.number().int().positive().optional(),
|
|
29913
|
-
auto_update: exports_external.boolean().optional()
|
|
30023
|
+
auto_update: exports_external.boolean().optional(),
|
|
30024
|
+
bridge: BridgeConfigSchema.optional()
|
|
29914
30025
|
}).strict();
|
|
29915
30026
|
function normalizeLspExtension(extension) {
|
|
29916
30027
|
return extension.trim().replace(/^\.+/, "");
|
|
@@ -29980,6 +30091,10 @@ function resolveProjectOverridesForConfigure(config2) {
|
|
|
29980
30091
|
overrides.search_index = config2.search_index;
|
|
29981
30092
|
if (config2.semantic_search !== undefined)
|
|
29982
30093
|
overrides.semantic_search = config2.semantic_search;
|
|
30094
|
+
if (config2.callgraph_store !== undefined)
|
|
30095
|
+
overrides.callgraph_store = config2.callgraph_store;
|
|
30096
|
+
if (config2.callgraph_chunk_size !== undefined)
|
|
30097
|
+
overrides.callgraph_chunk_size = config2.callgraph_chunk_size;
|
|
29983
30098
|
Object.assign(overrides, resolveExperimentalConfigForConfigure(config2));
|
|
29984
30099
|
Object.assign(overrides, resolveLspConfigForConfigure(config2));
|
|
29985
30100
|
if (config2.semantic !== undefined)
|
|
@@ -30254,6 +30369,17 @@ function parseConfigPartially(rawConfig) {
|
|
|
30254
30369
|
}
|
|
30255
30370
|
return partialConfig;
|
|
30256
30371
|
}
|
|
30372
|
+
var configLoadErrors = [];
|
|
30373
|
+
function getConfigLoadErrors() {
|
|
30374
|
+
return configLoadErrors;
|
|
30375
|
+
}
|
|
30376
|
+
function formatConfigParseFailureMessage(configPath, errorMessage) {
|
|
30377
|
+
return `AFT config at ${configPath} failed to parse and was ignored (running on defaults): ${errorMessage}. ` + "Fix the syntax or run `npx @cortexkit/aft doctor`.";
|
|
30378
|
+
}
|
|
30379
|
+
function recordConfigParseFailure(configPath, errorMessage) {
|
|
30380
|
+
configLoadErrors.push({ path: configPath, message: errorMessage });
|
|
30381
|
+
warn2(formatConfigParseFailureMessage(configPath, errorMessage));
|
|
30382
|
+
}
|
|
30257
30383
|
function loadConfigFromPath(configPath) {
|
|
30258
30384
|
try {
|
|
30259
30385
|
if (!existsSync6(configPath)) {
|
|
@@ -30274,6 +30400,7 @@ function loadConfigFromPath(configPath) {
|
|
|
30274
30400
|
} catch (err) {
|
|
30275
30401
|
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
30276
30402
|
error2(`Error loading config from ${configPath}: ${errorMsg}`);
|
|
30403
|
+
recordConfigParseFailure(configPath, errorMsg);
|
|
30277
30404
|
return null;
|
|
30278
30405
|
}
|
|
30279
30406
|
}
|
|
@@ -30397,6 +30524,8 @@ var PROJECT_SAFE_TOP_LEVEL_FIELDS = new Set([
|
|
|
30397
30524
|
"configure_warnings_delivery",
|
|
30398
30525
|
"search_index",
|
|
30399
30526
|
"semantic_search",
|
|
30527
|
+
"callgraph_store",
|
|
30528
|
+
"callgraph_chunk_size",
|
|
30400
30529
|
"inspect",
|
|
30401
30530
|
"experimental",
|
|
30402
30531
|
"bash"
|
|
@@ -30420,6 +30549,8 @@ function getStrippedTopLevelKeys(override) {
|
|
|
30420
30549
|
stripped.push("max_callgraph_files");
|
|
30421
30550
|
if (override.auto_update !== undefined)
|
|
30422
30551
|
stripped.push("auto_update");
|
|
30552
|
+
if (override.bridge !== undefined)
|
|
30553
|
+
stripped.push("bridge");
|
|
30423
30554
|
return stripped;
|
|
30424
30555
|
}
|
|
30425
30556
|
function mergeConfigs(base, override) {
|
|
@@ -30431,6 +30562,7 @@ function mergeConfigs(base, override) {
|
|
|
30431
30562
|
const experimental = mergeExperimentalConfig(base.experimental, override.experimental);
|
|
30432
30563
|
const bash = mergeBashConfig(base.bash, override.bash);
|
|
30433
30564
|
const inspect = mergeInspectConfig(base.inspect, override.inspect);
|
|
30565
|
+
const bridge = base.bridge;
|
|
30434
30566
|
const safeOverride = pickProjectSafeFields(override);
|
|
30435
30567
|
delete safeOverride.bash;
|
|
30436
30568
|
delete safeOverride.inspect;
|
|
@@ -30444,18 +30576,28 @@ function mergeConfigs(base, override) {
|
|
|
30444
30576
|
...inspect !== undefined ? { inspect } : {},
|
|
30445
30577
|
experimental,
|
|
30446
30578
|
semantic,
|
|
30579
|
+
...bridge !== undefined ? { bridge } : {},
|
|
30447
30580
|
...disabledTools.length > 0 ? { disabled_tools: [...new Set(disabledTools)] } : {}
|
|
30448
30581
|
};
|
|
30449
30582
|
}
|
|
30583
|
+
var DEFAULT_BRIDGE_REQUEST_TIMEOUT_MS = 30000;
|
|
30584
|
+
var DEFAULT_BRIDGE_HANG_THRESHOLD = 2;
|
|
30585
|
+
function resolveBridgePoolTransportOptions(config2) {
|
|
30586
|
+
return {
|
|
30587
|
+
timeoutMs: config2.bridge?.request_timeout_ms ?? DEFAULT_BRIDGE_REQUEST_TIMEOUT_MS,
|
|
30588
|
+
hangThreshold: config2.bridge?.hang_threshold ?? DEFAULT_BRIDGE_HANG_THRESHOLD
|
|
30589
|
+
};
|
|
30590
|
+
}
|
|
30450
30591
|
function getOpenCodeConfigDir() {
|
|
30451
30592
|
const envDir = process.env.OPENCODE_CONFIG_DIR?.trim();
|
|
30452
30593
|
if (envDir) {
|
|
30453
30594
|
return envDir;
|
|
30454
30595
|
}
|
|
30455
|
-
const xdgConfig = process.env.XDG_CONFIG_HOME || join10(
|
|
30596
|
+
const xdgConfig = process.env.XDG_CONFIG_HOME || join10(homedir8(), ".config");
|
|
30456
30597
|
return join10(xdgConfig, "opencode");
|
|
30457
30598
|
}
|
|
30458
30599
|
function loadAftConfig(projectDirectory) {
|
|
30600
|
+
configLoadErrors = [];
|
|
30459
30601
|
const configDir = getOpenCodeConfigDir();
|
|
30460
30602
|
const userBasePath = join10(configDir, "aft");
|
|
30461
30603
|
migrateAftConfigFile(`${userBasePath}.jsonc`);
|
|
@@ -30488,7 +30630,7 @@ function loadAftConfig(projectDirectory) {
|
|
|
30488
30630
|
|
|
30489
30631
|
// src/notifications.ts
|
|
30490
30632
|
import { existsSync as existsSync7, readFileSync as readFileSync5 } from "node:fs";
|
|
30491
|
-
import { homedir as
|
|
30633
|
+
import { homedir as homedir9, platform } from "node:os";
|
|
30492
30634
|
import { join as join11 } from "node:path";
|
|
30493
30635
|
function isTuiMode() {
|
|
30494
30636
|
return process.env.OPENCODE_CLIENT === "cli";
|
|
@@ -30510,7 +30652,7 @@ var WARNING_MARKER = `${AFT_MARKER} ⚠️`;
|
|
|
30510
30652
|
var STATUS_MARKER = `${AFT_MARKER} ✅`;
|
|
30511
30653
|
function getDesktopStatePath() {
|
|
30512
30654
|
const os3 = platform();
|
|
30513
|
-
const home =
|
|
30655
|
+
const home = homedir9();
|
|
30514
30656
|
if (os3 === "darwin") {
|
|
30515
30657
|
return join11(home, "Library", "Application Support", "ai.opencode.desktop", "opencode.global.dat");
|
|
30516
30658
|
}
|
|
@@ -30789,6 +30931,8 @@ function warningTitle(warning) {
|
|
|
30789
30931
|
return "Checker is not installed";
|
|
30790
30932
|
case "lsp_binary_missing":
|
|
30791
30933
|
return "LSP binary is missing";
|
|
30934
|
+
case "config_parse_failed":
|
|
30935
|
+
return "Config failed to parse";
|
|
30792
30936
|
}
|
|
30793
30937
|
}
|
|
30794
30938
|
function formatConfigureWarningLine(warning) {
|
|
@@ -30910,12 +31054,36 @@ async function cleanupWarnings(opts) {
|
|
|
30910
31054
|
|
|
30911
31055
|
// src/configure-warnings.ts
|
|
30912
31056
|
var pendingEagerWarnings = new Map;
|
|
31057
|
+
var pendingConfigParseWarnings = new Map;
|
|
30913
31058
|
var pendingBySession = new Map;
|
|
30914
31059
|
function isConfigureWarning(value) {
|
|
30915
31060
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
30916
31061
|
return false;
|
|
30917
31062
|
const warning = value;
|
|
30918
|
-
return (warning.kind === "formatter_not_installed" || warning.kind === "checker_not_installed" || warning.kind === "lsp_binary_missing") && typeof warning.hint === "string";
|
|
31063
|
+
return (warning.kind === "formatter_not_installed" || warning.kind === "checker_not_installed" || warning.kind === "lsp_binary_missing" || warning.kind === "config_parse_failed") && typeof warning.hint === "string";
|
|
31064
|
+
}
|
|
31065
|
+
function configParseWarningsFromErrors(errors3) {
|
|
31066
|
+
return errors3.map((entry) => ({
|
|
31067
|
+
kind: "config_parse_failed",
|
|
31068
|
+
hint: formatConfigParseFailureMessage(entry.path, entry.message)
|
|
31069
|
+
}));
|
|
31070
|
+
}
|
|
31071
|
+
function enqueueConfigParseWarnings(projectRoot, errors3) {
|
|
31072
|
+
if (!projectRoot || errors3.length === 0)
|
|
31073
|
+
return;
|
|
31074
|
+
const incoming = configParseWarningsFromErrors(errors3);
|
|
31075
|
+
const existing = pendingConfigParseWarnings.get(projectRoot) ?? [];
|
|
31076
|
+
for (const warning of incoming) {
|
|
31077
|
+
if (!existing.some((item) => item.hint === warning.hint)) {
|
|
31078
|
+
existing.push(warning);
|
|
31079
|
+
}
|
|
31080
|
+
}
|
|
31081
|
+
pendingConfigParseWarnings.set(projectRoot, existing);
|
|
31082
|
+
}
|
|
31083
|
+
function drainPendingConfigParseWarnings(projectRoot) {
|
|
31084
|
+
const pending = pendingConfigParseWarnings.get(projectRoot) ?? [];
|
|
31085
|
+
pendingConfigParseWarnings.delete(projectRoot);
|
|
31086
|
+
return pending;
|
|
30919
31087
|
}
|
|
30920
31088
|
function coerceConfigureWarnings(warnings) {
|
|
30921
31089
|
return warnings.filter(isConfigureWarning);
|
|
@@ -30926,7 +31094,10 @@ function drainPendingEagerWarnings(projectRoot) {
|
|
|
30926
31094
|
return pending;
|
|
30927
31095
|
}
|
|
30928
31096
|
function enqueueConfigureWarningsForSession(context) {
|
|
30929
|
-
const validWarnings =
|
|
31097
|
+
const validWarnings = [
|
|
31098
|
+
...drainPendingConfigParseWarnings(context.projectRoot),
|
|
31099
|
+
...coerceConfigureWarnings(context.warnings)
|
|
31100
|
+
];
|
|
30930
31101
|
if (!context.sessionId) {
|
|
30931
31102
|
if (validWarnings.length === 0)
|
|
30932
31103
|
return;
|
|
@@ -30996,27 +31167,27 @@ var import_comment_json3 = __toESM(require_src2(), 1);
|
|
|
30996
31167
|
// src/hooks/auto-update-checker/checker.ts
|
|
30997
31168
|
var import_comment_json2 = __toESM(require_src2(), 1);
|
|
30998
31169
|
import { existsSync as existsSync8, readFileSync as readFileSync6, statSync as statSync4, writeFileSync as writeFileSync4 } from "node:fs";
|
|
30999
|
-
import { homedir as
|
|
31170
|
+
import { homedir as homedir11 } from "node:os";
|
|
31000
31171
|
import { dirname as dirname5, isAbsolute as isAbsolute4, join as join13, resolve as resolve3 } from "node:path";
|
|
31001
31172
|
import { fileURLToPath } from "node:url";
|
|
31002
31173
|
|
|
31003
31174
|
// src/hooks/auto-update-checker/constants.ts
|
|
31004
|
-
import { homedir as
|
|
31175
|
+
import { homedir as homedir10, platform as platform2 } from "node:os";
|
|
31005
31176
|
import { join as join12 } from "node:path";
|
|
31006
31177
|
var PACKAGE_NAME = "@cortexkit/aft-opencode";
|
|
31007
31178
|
var NPM_REGISTRY_URL = "https://registry.npmjs.org";
|
|
31008
31179
|
var NPM_FETCH_TIMEOUT = 1e4;
|
|
31009
31180
|
function getOpenCodeCacheRoot() {
|
|
31010
31181
|
if (platform2() === "win32") {
|
|
31011
|
-
return join12(process.env.LOCALAPPDATA ??
|
|
31182
|
+
return join12(process.env.LOCALAPPDATA ?? homedir10(), "opencode");
|
|
31012
31183
|
}
|
|
31013
|
-
return join12(
|
|
31184
|
+
return join12(homedir10(), ".cache", "opencode");
|
|
31014
31185
|
}
|
|
31015
31186
|
function getOpenCodeConfigRoot() {
|
|
31016
31187
|
if (platform2() === "win32") {
|
|
31017
|
-
return join12(process.env.APPDATA ?? join12(
|
|
31188
|
+
return join12(process.env.APPDATA ?? join12(homedir10(), "AppData", "Roaming"), "opencode");
|
|
31018
31189
|
}
|
|
31019
|
-
return join12(process.env.XDG_CONFIG_HOME ?? join12(
|
|
31190
|
+
return join12(process.env.XDG_CONFIG_HOME ?? join12(homedir10(), ".config"), "opencode");
|
|
31020
31191
|
}
|
|
31021
31192
|
var CACHE_DIR = join12(getOpenCodeCacheRoot(), "packages");
|
|
31022
31193
|
var USER_OPENCODE_CONFIG = join12(getOpenCodeConfigRoot(), "opencode.json");
|
|
@@ -31195,7 +31366,7 @@ function getCachedVersion(spec) {
|
|
|
31195
31366
|
getCurrentRuntimePackageJsonPath(),
|
|
31196
31367
|
spec ? getSpecCachePackageJsonPath(spec) : null,
|
|
31197
31368
|
getSpecCachePackageJsonPath(`${PACKAGE_NAME}@latest`),
|
|
31198
|
-
join13(
|
|
31369
|
+
join13(homedir11(), ".cache", "opencode", "node_modules", PACKAGE_NAME, "package.json")
|
|
31199
31370
|
].filter(isString);
|
|
31200
31371
|
for (const packageJsonPath of candidates) {
|
|
31201
31372
|
try {
|
|
@@ -31698,7 +31869,7 @@ import {
|
|
|
31698
31869
|
unlinkSync as unlinkSync5,
|
|
31699
31870
|
writeFileSync as writeFileSync7
|
|
31700
31871
|
} from "node:fs";
|
|
31701
|
-
import { homedir as
|
|
31872
|
+
import { homedir as homedir12 } from "node:os";
|
|
31702
31873
|
import { join as join15 } from "node:path";
|
|
31703
31874
|
function aftCacheBase() {
|
|
31704
31875
|
const override = process.env.AFT_CACHE_DIR;
|
|
@@ -31706,10 +31877,10 @@ function aftCacheBase() {
|
|
|
31706
31877
|
return override;
|
|
31707
31878
|
if (process.platform === "win32") {
|
|
31708
31879
|
const localAppData = process.env.LOCALAPPDATA || process.env.APPDATA;
|
|
31709
|
-
const base2 = localAppData || join15(
|
|
31880
|
+
const base2 = localAppData || join15(homedir12(), "AppData", "Local");
|
|
31710
31881
|
return join15(base2, "aft");
|
|
31711
31882
|
}
|
|
31712
|
-
const base = process.env.XDG_CACHE_HOME || join15(
|
|
31883
|
+
const base = process.env.XDG_CACHE_HOME || join15(homedir12(), ".cache");
|
|
31713
31884
|
return join15(base, "aft");
|
|
31714
31885
|
}
|
|
31715
31886
|
function lspCacheRoot() {
|
|
@@ -33792,7 +33963,7 @@ async function verifySessionDirectory(client, sessionId) {
|
|
|
33792
33963
|
}
|
|
33793
33964
|
|
|
33794
33965
|
// src/shared/status.ts
|
|
33795
|
-
function
|
|
33966
|
+
function asRecord2(value) {
|
|
33796
33967
|
return typeof value === "object" && value !== null ? value : {};
|
|
33797
33968
|
}
|
|
33798
33969
|
function readString(value, fallback = "") {
|
|
@@ -33811,7 +33982,7 @@ function readOptionalNumber(value) {
|
|
|
33811
33982
|
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
33812
33983
|
}
|
|
33813
33984
|
function readCompressionAggregate(value) {
|
|
33814
|
-
const aggregate =
|
|
33985
|
+
const aggregate = asRecord2(value);
|
|
33815
33986
|
return {
|
|
33816
33987
|
events: readNumber(aggregate.events),
|
|
33817
33988
|
original_tokens: readNumber(aggregate.original_tokens),
|
|
@@ -33822,7 +33993,7 @@ function readCompressionAggregate(value) {
|
|
|
33822
33993
|
function readCompression(value) {
|
|
33823
33994
|
if (typeof value !== "object" || value === null)
|
|
33824
33995
|
return;
|
|
33825
|
-
const compression =
|
|
33996
|
+
const compression = asRecord2(value);
|
|
33826
33997
|
return {
|
|
33827
33998
|
project: readCompressionAggregate(compression.project),
|
|
33828
33999
|
session: readCompressionAggregate(compression.session)
|
|
@@ -33831,7 +34002,7 @@ function readCompression(value) {
|
|
|
33831
34002
|
function readStatusBar(value) {
|
|
33832
34003
|
if (typeof value !== "object" || value === null)
|
|
33833
34004
|
return;
|
|
33834
|
-
const bar =
|
|
34005
|
+
const bar = asRecord2(value);
|
|
33835
34006
|
return {
|
|
33836
34007
|
errors: readNumber(bar.errors),
|
|
33837
34008
|
warnings: readNumber(bar.warnings),
|
|
@@ -33875,16 +34046,16 @@ function formatBytes(bytes) {
|
|
|
33875
34046
|
return `${value.toFixed(decimals)} ${units[unitIndex]}`;
|
|
33876
34047
|
}
|
|
33877
34048
|
function coerceAftStatus(response) {
|
|
33878
|
-
const features =
|
|
33879
|
-
const searchIndex =
|
|
33880
|
-
const semanticIndex =
|
|
34049
|
+
const features = asRecord2(response.features);
|
|
34050
|
+
const searchIndex = asRecord2(response.search_index);
|
|
34051
|
+
const semanticIndex = asRecord2(response.semantic_index);
|
|
33881
34052
|
const semanticConfig = {
|
|
33882
|
-
...
|
|
33883
|
-
...
|
|
34053
|
+
...asRecord2(response.semantic),
|
|
34054
|
+
...asRecord2(response.semantic_config)
|
|
33884
34055
|
};
|
|
33885
|
-
const disk =
|
|
33886
|
-
const symbolCache =
|
|
33887
|
-
const session =
|
|
34056
|
+
const disk = asRecord2(response.disk);
|
|
34057
|
+
const symbolCache = asRecord2(response.symbol_cache);
|
|
34058
|
+
const session = asRecord2(response.session);
|
|
33888
34059
|
return {
|
|
33889
34060
|
version: readString(response.version, "unknown"),
|
|
33890
34061
|
project_root: readNullableString(response.project_root),
|
|
@@ -34006,7 +34177,7 @@ import { existsSync as existsSync15, mkdirSync as mkdirSync11, readFileSync as r
|
|
|
34006
34177
|
import { dirname as dirname10, join as join22 } from "node:path";
|
|
34007
34178
|
|
|
34008
34179
|
// src/shared/opencode-config-dir.ts
|
|
34009
|
-
import { homedir as
|
|
34180
|
+
import { homedir as homedir13 } from "node:os";
|
|
34010
34181
|
import { join as join21, resolve as resolve5 } from "node:path";
|
|
34011
34182
|
function getCliConfigDir() {
|
|
34012
34183
|
const envConfigDir = process.env.OPENCODE_CONFIG_DIR?.trim();
|
|
@@ -34014,9 +34185,9 @@ function getCliConfigDir() {
|
|
|
34014
34185
|
return resolve5(envConfigDir);
|
|
34015
34186
|
}
|
|
34016
34187
|
if (process.platform === "win32") {
|
|
34017
|
-
return join21(
|
|
34188
|
+
return join21(homedir13(), ".config", "opencode");
|
|
34018
34189
|
}
|
|
34019
|
-
return join21(process.env.XDG_CONFIG_HOME || join21(
|
|
34190
|
+
return join21(process.env.XDG_CONFIG_HOME || join21(homedir13(), ".config"), "opencode");
|
|
34020
34191
|
}
|
|
34021
34192
|
function getOpenCodeConfigDir2(_options) {
|
|
34022
34193
|
return getCliConfigDir();
|
|
@@ -34856,18 +35027,43 @@ function resolveForegroundWaitMs(configured) {
|
|
|
34856
35027
|
function bashToolDescription(aftSearchRegistered, compressionOn, backgroundOn) {
|
|
34857
35028
|
const searchSteer = aftSearchRegistered ? "use aft_search (concepts, identifiers, regex, literals), read, aft_outline, or aft_zoom instead" : "use the grep tool, read, aft_outline, or aft_zoom instead";
|
|
34858
35029
|
const compression = compressionOn ? " Output is compressed by default; pass compressed: false for raw output." : "";
|
|
34859
|
-
const tasks = backgroundOn ? ' Pass background: true to run in the background and get a taskId for bash_status/bash_kill. Pass pty: true for interactive programs (REPLs, TUIs) and drive them with bash_status({ outputMode: "screen" }) plus bash_write (pty implies background automatically).' : " Commands
|
|
34860
|
-
return `Execute shell commands.${compression}${tasks}
|
|
35030
|
+
const tasks = backgroundOn ? ' Pass background: true to run in the background and get a taskId for bash_status/bash_watch/bash_kill. Pass pty: true for interactive programs (REPLs, TUIs) and drive them with bash_status({ outputMode: "screen" }) plus bash_write (pty implies background automatically). Use bash_watch to wait for exit or output patterns (sync blocks, async notifies). Do not loop bash_status to wait.' : " Commands run in the foreground to completion; timeout is the hard kill cap (default 30 minutes).";
|
|
35031
|
+
return `Execute shell commands.${compression}${tasks}
|
|
34861
35032
|
|
|
34862
35033
|
DO NOT use bash for code search or code exploration. If you are about to run grep, rg, sed, awk, find, or cat through bash to locate or read code: STOP — ${searchSteer}.`;
|
|
34863
35034
|
}
|
|
35035
|
+
function pushUnique(target, values) {
|
|
35036
|
+
for (const value of values) {
|
|
35037
|
+
if (!target.includes(value))
|
|
35038
|
+
target.push(value);
|
|
35039
|
+
}
|
|
35040
|
+
}
|
|
35041
|
+
function groupBashPermissionAsks(asks) {
|
|
35042
|
+
const grouped = [];
|
|
35043
|
+
let bashAsk;
|
|
35044
|
+
for (const ask of asks) {
|
|
35045
|
+
if (ask.kind === "bash") {
|
|
35046
|
+
if (!bashAsk) {
|
|
35047
|
+
bashAsk = { kind: "bash", patterns: [], always: [] };
|
|
35048
|
+
grouped.push(bashAsk);
|
|
35049
|
+
}
|
|
35050
|
+
pushUnique(bashAsk.patterns, ask.patterns);
|
|
35051
|
+
pushUnique(bashAsk.always, ask.always);
|
|
35052
|
+
continue;
|
|
35053
|
+
}
|
|
35054
|
+
grouped.push(ask);
|
|
35055
|
+
}
|
|
35056
|
+
return grouped;
|
|
35057
|
+
}
|
|
35058
|
+
function permissionsGrantedForRetry(asks) {
|
|
35059
|
+
return asks.flatMap((ask) => ask.always.length > 0 ? ask.always : ask.patterns);
|
|
35060
|
+
}
|
|
34864
35061
|
async function withPermissionLoop(ctx, runtime, params, bridgeCall, options) {
|
|
34865
35062
|
const first = await bridgeCall(ctx, runtime, "bash", params, options);
|
|
34866
35063
|
if (first.success !== false || first.code !== "permission_required")
|
|
34867
35064
|
return first;
|
|
34868
35065
|
const asks = Array.isArray(first.asks) ? first.asks : [];
|
|
34869
|
-
const
|
|
34870
|
-
for (const ask of asks) {
|
|
35066
|
+
for (const ask of groupBashPermissionAsks(asks)) {
|
|
34871
35067
|
const permission = ask.kind === "external_directory" ? "external_directory" : "bash";
|
|
34872
35068
|
await runAsk(runtime.ask({
|
|
34873
35069
|
permission,
|
|
@@ -34875,54 +35071,60 @@ async function withPermissionLoop(ctx, runtime, params, bridgeCall, options) {
|
|
|
34875
35071
|
always: ask.always,
|
|
34876
35072
|
metadata: {}
|
|
34877
35073
|
}));
|
|
34878
|
-
permissionsGranted.push(...ask.always.length > 0 ? ask.always : ask.patterns);
|
|
34879
35074
|
}
|
|
34880
|
-
const second = await bridgeCall(ctx, runtime, "bash", { ...params, permissions_granted:
|
|
35075
|
+
const second = await bridgeCall(ctx, runtime, "bash", { ...params, permissions_granted: permissionsGrantedForRetry(asks) }, options);
|
|
34881
35076
|
if (second.success === false && second.code === "permission_required") {
|
|
34882
35077
|
throw new Error("bash permission retry failed");
|
|
34883
35078
|
}
|
|
34884
35079
|
return second;
|
|
34885
35080
|
}
|
|
34886
35081
|
function createBashTool(ctx, aftSearchRegisteredOverride) {
|
|
35082
|
+
const initialBashCfg = resolveBashConfig(ctx.config);
|
|
35083
|
+
const backgroundFlagArg = initialBashCfg.background ? {
|
|
35084
|
+
background: z4.boolean().optional().describe("When true, spawn the command in the background and return a taskId for bash_status/bash_kill instead of waiting for completion. Defaults to false.")
|
|
35085
|
+
} : {};
|
|
35086
|
+
const ptyArgs = initialBashCfg.background ? {
|
|
35087
|
+
pty: z4.boolean().optional().describe('When true, spawn the command in a real PTY for interactive programs (python/node/bash REPLs, vim). Implies background: true automatically. Unavailable in subagent sessions. Inspect with bash_status({ taskId, outputMode: "screen" }) and drive interactively with bash_write — its input accepts either a string OR an array like [ "iHello", { key: "esc" }, ":wq", { key: "enter" } ] for atomic text+key sequences.'),
|
|
35088
|
+
ptyRows: optionalInt(1, 60).describe("PTY terminal height in rows — ignored when pty is false. Defaults to 24 when pty: true. Minimum 1, maximum 60."),
|
|
35089
|
+
ptyCols: optionalInt(1, 140).describe("PTY terminal width in columns — ignored when pty is false. Defaults to 80 when pty: true. Minimum 1, maximum 140.")
|
|
35090
|
+
} : {};
|
|
35091
|
+
const args = {
|
|
35092
|
+
command: z4.string().describe("Shell command to execute. Supports pipes, redirection, and normal shell syntax."),
|
|
35093
|
+
timeout: optionalInt(1, Number.MAX_SAFE_INTEGER).describe(initialBashCfg.background ? "Hard kill cap in milliseconds (positive integer). When omitted, the task can run up to 30 minutes. Foreground bash returns inline if the command finishes within ~8s (configurable via bash.foreground_wait_window_ms); otherwise it's automatically promoted to background and a completion reminder is delivered when the task actually finishes." : "Hard kill cap in milliseconds (positive integer). When omitted, the foreground command can run up to 30 minutes and returns inline when it finishes."),
|
|
35094
|
+
workdir: z4.string().optional().describe("Working directory for command execution. Relative paths resolve through the bridge; defaults to the current tool context/project root when omitted."),
|
|
35095
|
+
description: z4.string().optional().describe("Short 5-10 word human-readable summary shown in OpenCode UI metadata instead of raw shell syntax."),
|
|
35096
|
+
...backgroundFlagArg,
|
|
35097
|
+
compressed: z4.boolean().optional().describe("When true or omitted, return compressed output with noisy terminal control sequences reduced. Set to false for raw output."),
|
|
35098
|
+
...ptyArgs
|
|
35099
|
+
};
|
|
34887
35100
|
return {
|
|
34888
|
-
description: (
|
|
34889
|
-
|
|
34890
|
-
|
|
34891
|
-
})(),
|
|
34892
|
-
args: {
|
|
34893
|
-
command: z4.string().describe("Shell command to execute. Supports pipes, redirection, and normal shell syntax."),
|
|
34894
|
-
timeout: optionalInt(1, Number.MAX_SAFE_INTEGER).describe("Hard kill cap in milliseconds (positive integer). When omitted, the task can run up to 30 minutes. Foreground bash returns inline if the command finishes within ~8s (configurable via bash.foreground_wait_window_ms); otherwise it's automatically promoted to background and a completion reminder is delivered when the task actually finishes."),
|
|
34895
|
-
workdir: z4.string().optional().describe("Working directory for command execution. Relative paths resolve through the bridge; defaults to the current tool context/project root when omitted."),
|
|
34896
|
-
description: z4.string().optional().describe("Short 5-10 word human-readable summary shown in OpenCode UI metadata instead of raw shell syntax."),
|
|
34897
|
-
background: z4.boolean().optional().describe("When true, spawn the command in the background and return a taskId for bash_status/bash_kill instead of waiting for completion. Defaults to false."),
|
|
34898
|
-
compressed: z4.boolean().optional().describe("When true or omitted, return compressed output with noisy terminal control sequences reduced. Set to false for raw output."),
|
|
34899
|
-
pty: z4.boolean().optional().describe('When true, spawn the command in a real PTY for interactive programs (python/node/bash REPLs, vim). Implies background: true automatically. Unavailable in subagent sessions. Inspect with bash_status({ taskId, outputMode: "screen" }) and drive interactively with bash_write — its input accepts either a string OR an array like [ "iHello", { key: "esc" }, ":wq", { key: "enter" } ] for atomic text+key sequences.'),
|
|
34900
|
-
ptyRows: optionalInt(1, 60).describe("PTY terminal height in rows — ignored when pty is false. Defaults to 24 when pty: true. Minimum 1, maximum 60."),
|
|
34901
|
-
ptyCols: optionalInt(1, 140).describe("PTY terminal width in columns — ignored when pty is false. Defaults to 80 when pty: true. Minimum 1, maximum 140.")
|
|
34902
|
-
},
|
|
34903
|
-
execute: async (args, context) => {
|
|
35101
|
+
description: bashToolDescription(false, initialBashCfg.compress, initialBashCfg.background),
|
|
35102
|
+
args,
|
|
35103
|
+
execute: async (args2, context) => {
|
|
34904
35104
|
const bashCfg = resolveBashConfig(ctx.config);
|
|
34905
35105
|
const ctxAftSearchRegistered = ctx.aftSearchRegistered === true;
|
|
34906
35106
|
const aftSearchRegistered = aftSearchRegisteredOverride ?? ctxAftSearchRegistered;
|
|
34907
35107
|
let accumulatedOutput = "";
|
|
34908
|
-
const description =
|
|
35108
|
+
const description = args2.description;
|
|
34909
35109
|
const metadata = context.metadata;
|
|
34910
|
-
const rawCommand =
|
|
34911
|
-
const compressionEnabled = bashCfg.compress &&
|
|
35110
|
+
const rawCommand = args2.command;
|
|
35111
|
+
const compressionEnabled = bashCfg.compress && args2.compressed !== false;
|
|
34912
35112
|
const pipeStrip = maybeStripCompressorPipe(rawCommand, compressionEnabled);
|
|
34913
35113
|
const command = pipeStrip.command;
|
|
34914
|
-
const cwd =
|
|
35114
|
+
const cwd = args2.workdir ?? context.directory;
|
|
34915
35115
|
const isSubagent = await resolveIsSubagent(ctx.client, context.sessionID, context.directory);
|
|
34916
|
-
const
|
|
34917
|
-
const
|
|
35116
|
+
const backgroundDisabled = !bashCfg.background;
|
|
35117
|
+
const requestedPty = !backgroundDisabled && args2.pty === true;
|
|
35118
|
+
const requestedBackground = !backgroundDisabled && (args2.background === true || requestedPty);
|
|
34918
35119
|
if (requestedPty && isSubagent) {
|
|
34919
35120
|
throw new Error("PTY mode is not available in subagent sessions; subagents cannot drive interactive terminals.");
|
|
34920
35121
|
}
|
|
34921
35122
|
const allowSubagentBg = bashCfg.subagent_background;
|
|
34922
35123
|
const subagentForcedForeground = isSubagent && !allowSubagentBg;
|
|
34923
|
-
const
|
|
34924
|
-
const
|
|
34925
|
-
const
|
|
35124
|
+
const blockToCompletion = subagentForcedForeground || backgroundDisabled;
|
|
35125
|
+
const effectiveBackground = blockToCompletion ? false : requestedBackground;
|
|
35126
|
+
const rawTimeout = args2.timeout;
|
|
35127
|
+
const effectiveTimeout = effectiveBackground || backgroundDisabled ? rawTimeout : resolveBashKillTimeout(rawTimeout, bashCfg.foreground_wait_window_ms);
|
|
34926
35128
|
if (subagentForcedForeground && requestedBackground) {
|
|
34927
35129
|
sessionLog(context.sessionID, "[bash] subagent + background:true → converting to foreground (subagent would lose task_id)");
|
|
34928
35130
|
}
|
|
@@ -34930,15 +35132,15 @@ function createBashTool(ctx, aftSearchRegisteredOverride) {
|
|
|
34930
35132
|
const data = await withPermissionLoop(ctx, context, {
|
|
34931
35133
|
command,
|
|
34932
35134
|
timeout: effectiveTimeout,
|
|
34933
|
-
workdir:
|
|
35135
|
+
workdir: args2.workdir,
|
|
34934
35136
|
env: shellEnv?.env ?? {},
|
|
34935
35137
|
description,
|
|
34936
35138
|
background: effectiveBackground,
|
|
34937
35139
|
notify_on_completion: effectiveBackground,
|
|
34938
|
-
compressed:
|
|
35140
|
+
compressed: args2.compressed,
|
|
34939
35141
|
pty: requestedPty,
|
|
34940
|
-
pty_rows:
|
|
34941
|
-
pty_cols:
|
|
35142
|
+
pty_rows: args2.ptyRows,
|
|
35143
|
+
pty_cols: args2.ptyCols,
|
|
34942
35144
|
permissions_requested: true
|
|
34943
35145
|
}, callBashBridge, {
|
|
34944
35146
|
onProgress: ({ text }) => {
|
|
@@ -34963,7 +35165,7 @@ function createBashTool(ctx, aftSearchRegisteredOverride) {
|
|
|
34963
35165
|
return { output: startedLine, title: uiTitle, metadata: metadataPayload2 };
|
|
34964
35166
|
}
|
|
34965
35167
|
const foregroundWaitMs = resolveForegroundWaitMs(bashCfg.foreground_wait_window_ms);
|
|
34966
|
-
const waitTimeoutMs =
|
|
35168
|
+
const waitTimeoutMs = blockToCompletion ? effectiveTimeout ?? DEFAULT_HARD_TIMEOUT_MS : effectiveTimeout !== undefined ? Math.min(effectiveTimeout, foregroundWaitMs) : foregroundWaitMs;
|
|
34967
35169
|
const startedAt = Date.now();
|
|
34968
35170
|
while (true) {
|
|
34969
35171
|
const status = await callBashBridge(ctx, context, "bash_status", { task_id: taskId });
|
|
@@ -34977,7 +35179,7 @@ function createBashTool(ctx, aftSearchRegisteredOverride) {
|
|
|
34977
35179
|
return { output: rendered2, title: uiTitle, metadata: metadataPayload2 };
|
|
34978
35180
|
}
|
|
34979
35181
|
if (Date.now() - startedAt >= waitTimeoutMs) {
|
|
34980
|
-
if (
|
|
35182
|
+
if (blockToCompletion) {
|
|
34981
35183
|
await sleep(FOREGROUND_POLL_INTERVAL_MS);
|
|
34982
35184
|
continue;
|
|
34983
35185
|
}
|
|
@@ -35037,7 +35239,7 @@ function createBashTool(ctx, aftSearchRegisteredOverride) {
|
|
|
35037
35239
|
}
|
|
35038
35240
|
function createBashStatusTool(ctx) {
|
|
35039
35241
|
return {
|
|
35040
|
-
description: "Read-only snapshot of a background or PTY bash task's current state and output. Returns immediately. Never waits.
|
|
35242
|
+
description: "Read-only snapshot of a background or PTY bash task's current state and output. Returns immediately. Never waits. One look to check on a task is fine — never loop it to wait for completion. To wait, use bash_watch.",
|
|
35041
35243
|
args: {
|
|
35042
35244
|
taskId: z4.string().describe("Background task ID returned by bash({ background: true }), e.g. bash-6b454047."),
|
|
35043
35245
|
outputMode: z4.enum(["screen", "raw", "both"]).optional().describe("PTY output rendering mode. Defaults to screen for PTY tasks and preserves existing behavior for piped tasks when omitted.")
|
|
@@ -35515,16 +35717,16 @@ import { tool as tool6 } from "@opencode-ai/plugin";
|
|
|
35515
35717
|
var z6 = tool6.schema;
|
|
35516
35718
|
var BASH_WAIT_POLL_INTERVAL_MS = 100;
|
|
35517
35719
|
var DEFAULT_BASH_STATUS_WAIT_TIMEOUT_MS = 30000;
|
|
35518
|
-
var MAX_BASH_STATUS_WAIT_TIMEOUT_MS =
|
|
35720
|
+
var MAX_BASH_STATUS_WAIT_TIMEOUT_MS = 30 * 60 * 1000;
|
|
35519
35721
|
var REGEX_WAIT_SCAN_WINDOW_BYTES = 64 * 1024;
|
|
35520
35722
|
function createBashWatchTool(ctx) {
|
|
35521
35723
|
return {
|
|
35522
|
-
description: "Watch a background bash task.
|
|
35724
|
+
description: "Watch a background bash task. Sync (default) blocks until a pattern matches, the task exits, or timeout — use it when the result is the next thing you need, even for long builds/tests/installs (pass timeoutMs up to 30 min for those). The user can interrupt anytime; the wait auto-converts to an async notification. Async (background:true, requires pattern) registers a non-blocking notification and returns immediately — use when you have parallel work or want to end your turn. Never loop bash_status to wait.",
|
|
35523
35725
|
args: {
|
|
35524
35726
|
taskId: z6.string().describe("Background task ID returned by bash({ background: true })."),
|
|
35525
35727
|
pattern: z6.union([z6.string(), z6.object({ regex: z6.string() })]).optional().describe("Substring or regex pattern. Optional in sync mode; required with background:true. Sync substring watches keep only the overlap tail needed for boundary matches; sync regex watches use a 64 KB rolling output window."),
|
|
35526
35728
|
background: z6.boolean().optional().describe("When true, register an async watch and return immediately. Defaults to false (sync wait)."),
|
|
35527
|
-
timeoutMs: optionalInt(1, MAX_BASH_STATUS_WAIT_TIMEOUT_MS).describe("Sync-only timeout in milliseconds. Default 30000, max
|
|
35729
|
+
timeoutMs: optionalInt(1, MAX_BASH_STATUS_WAIT_TIMEOUT_MS).describe("Sync-only timeout in milliseconds. Default 30000, max 1800000 (30 min)."),
|
|
35528
35730
|
once: z6.boolean().optional().describe("Async-only. Defaults true; false keeps the watch sticky until task exit.")
|
|
35529
35731
|
},
|
|
35530
35732
|
execute: async (args, context) => {
|
|
@@ -37084,12 +37286,15 @@ function hoistedTools(ctx) {
|
|
|
37084
37286
|
aft_delete: createDeleteTool(ctx),
|
|
37085
37287
|
aft_move: createMoveTool(ctx)
|
|
37086
37288
|
};
|
|
37087
|
-
|
|
37289
|
+
const bashCfg = resolveBashConfig(ctx.config);
|
|
37290
|
+
if (bashCfg.enabled) {
|
|
37088
37291
|
tools.bash = createBashTool(ctx);
|
|
37089
|
-
|
|
37090
|
-
|
|
37091
|
-
|
|
37092
|
-
|
|
37292
|
+
if (bashCfg.background) {
|
|
37293
|
+
tools.bash_status = createBashStatusTool(ctx);
|
|
37294
|
+
tools.bash_write = createBashWriteTool(ctx);
|
|
37295
|
+
tools.bash_watch = createBashWatchTool(ctx);
|
|
37296
|
+
tools.bash_kill = createBashKillTool(ctx);
|
|
37297
|
+
}
|
|
37093
37298
|
}
|
|
37094
37299
|
return tools;
|
|
37095
37300
|
}
|
|
@@ -37143,12 +37348,15 @@ function aftPrefixedTools(ctx) {
|
|
|
37143
37348
|
aft_delete: createDeleteTool(ctx),
|
|
37144
37349
|
aft_move: createMoveTool(ctx)
|
|
37145
37350
|
};
|
|
37146
|
-
|
|
37351
|
+
const bashCfg = resolveBashConfig(ctx.config);
|
|
37352
|
+
if (bashCfg.enabled) {
|
|
37147
37353
|
tools.aft_bash = createBashTool(ctx);
|
|
37148
|
-
|
|
37149
|
-
|
|
37150
|
-
|
|
37151
|
-
|
|
37354
|
+
if (bashCfg.background) {
|
|
37355
|
+
tools.bash_status = createBashStatusTool(ctx);
|
|
37356
|
+
tools.bash_write = createBashWriteTool(ctx);
|
|
37357
|
+
tools.bash_watch = createBashWatchTool(ctx);
|
|
37358
|
+
tools.bash_kill = createBashKillTool(ctx);
|
|
37359
|
+
}
|
|
37152
37360
|
}
|
|
37153
37361
|
return tools;
|
|
37154
37362
|
}
|
|
@@ -37232,15 +37440,15 @@ function importTools(ctx) {
|
|
|
37232
37440
|
// src/tools/inspect.ts
|
|
37233
37441
|
import { tool as tool10 } from "@opencode-ai/plugin";
|
|
37234
37442
|
var z10 = tool10.schema;
|
|
37235
|
-
function
|
|
37443
|
+
function asRecord3(value) {
|
|
37236
37444
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
37237
37445
|
return;
|
|
37238
37446
|
return value;
|
|
37239
37447
|
}
|
|
37240
|
-
function
|
|
37448
|
+
function asNumber2(value) {
|
|
37241
37449
|
return typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
37242
37450
|
}
|
|
37243
|
-
function
|
|
37451
|
+
function asString2(value) {
|
|
37244
37452
|
return typeof value === "string" ? value : undefined;
|
|
37245
37453
|
}
|
|
37246
37454
|
function asStringArray(value) {
|
|
@@ -37257,16 +37465,16 @@ function diagnosticsServerSummary(section) {
|
|
|
37257
37465
|
return parts.length > 0 ? parts.join("; ") : "none reported";
|
|
37258
37466
|
}
|
|
37259
37467
|
function formatDiagnosticsSummary(summary) {
|
|
37260
|
-
const section =
|
|
37468
|
+
const section = asRecord3(summary?.diagnostics);
|
|
37261
37469
|
if (!section)
|
|
37262
37470
|
return;
|
|
37263
|
-
const errors3 =
|
|
37264
|
-
const warnings =
|
|
37265
|
-
const info =
|
|
37266
|
-
const hints =
|
|
37471
|
+
const errors3 = asNumber2(section.errors);
|
|
37472
|
+
const warnings = asNumber2(section.warnings);
|
|
37473
|
+
const info = asNumber2(section.info);
|
|
37474
|
+
const hints = asNumber2(section.hints);
|
|
37267
37475
|
const hasCounts = [errors3, warnings, info, hints].some((value) => value !== undefined);
|
|
37268
37476
|
const counts = `${errors3 ?? 0} errors, ${warnings ?? 0} warnings, ${info ?? 0} info, ${hints ?? 0} hints`;
|
|
37269
|
-
const status =
|
|
37477
|
+
const status = asString2(section.status);
|
|
37270
37478
|
if (status === "pending") {
|
|
37271
37479
|
return hasCounts ? `diagnostics: ${counts} so far — still pending (servers: ${diagnosticsServerSummary(section)})` : `diagnostics: pending (servers: ${diagnosticsServerSummary(section)})`;
|
|
37272
37480
|
}
|
|
@@ -37279,9 +37487,9 @@ function formatDiagnosticsSummary(summary) {
|
|
|
37279
37487
|
return;
|
|
37280
37488
|
}
|
|
37281
37489
|
function formatDiagnosticLocation(diagnostic) {
|
|
37282
|
-
const file2 =
|
|
37283
|
-
const line =
|
|
37284
|
-
const column =
|
|
37490
|
+
const file2 = asString2(diagnostic.file) ?? "(unknown file)";
|
|
37491
|
+
const line = asNumber2(diagnostic.line);
|
|
37492
|
+
const column = asNumber2(diagnostic.column);
|
|
37285
37493
|
if (line === undefined)
|
|
37286
37494
|
return file2;
|
|
37287
37495
|
if (column === undefined)
|
|
@@ -37289,21 +37497,21 @@ function formatDiagnosticLocation(diagnostic) {
|
|
|
37289
37497
|
return `${file2}:${line}:${column}`;
|
|
37290
37498
|
}
|
|
37291
37499
|
function formatDiagnosticsDetails(details) {
|
|
37292
|
-
const diagnostics = Array.isArray(details?.diagnostics) ? details.diagnostics.map(
|
|
37500
|
+
const diagnostics = Array.isArray(details?.diagnostics) ? details.diagnostics.map(asRecord3).filter(Boolean) : [];
|
|
37293
37501
|
return diagnostics.map((diagnostic) => {
|
|
37294
|
-
const severity =
|
|
37295
|
-
const message =
|
|
37296
|
-
const source =
|
|
37502
|
+
const severity = asString2(diagnostic.severity) ?? "information";
|
|
37503
|
+
const message = asString2(diagnostic.message) ?? "(no message)";
|
|
37504
|
+
const source = asString2(diagnostic.source);
|
|
37297
37505
|
const suffix = source ? ` [${source}]` : "";
|
|
37298
37506
|
return `${formatDiagnosticLocation(diagnostic)} ${severity} ${message}${suffix}`;
|
|
37299
37507
|
});
|
|
37300
37508
|
}
|
|
37301
37509
|
function renderInspectDiagnostics(response) {
|
|
37302
37510
|
const lines = [];
|
|
37303
|
-
const summaryLine = formatDiagnosticsSummary(
|
|
37511
|
+
const summaryLine = formatDiagnosticsSummary(asRecord3(response.summary));
|
|
37304
37512
|
if (summaryLine)
|
|
37305
37513
|
lines.push(summaryLine);
|
|
37306
|
-
const detailLines = formatDiagnosticsDetails(
|
|
37514
|
+
const detailLines = formatDiagnosticsDetails(asRecord3(response.details));
|
|
37307
37515
|
if (detailLines.length > 0) {
|
|
37308
37516
|
lines.push("diagnostics details:", ...detailLines.map((line) => `- ${line}`));
|
|
37309
37517
|
}
|
|
@@ -37488,7 +37696,8 @@ function navigationTools(ctx) {
|
|
|
37488
37696
|
}
|
|
37489
37697
|
throw new Error(message);
|
|
37490
37698
|
}
|
|
37491
|
-
return
|
|
37699
|
+
return formatCallgraphSections(args.op, response).join(`
|
|
37700
|
+
`);
|
|
37492
37701
|
}
|
|
37493
37702
|
}
|
|
37494
37703
|
};
|
|
@@ -38207,13 +38416,42 @@ function searchPathExists(projectRoot, target) {
|
|
|
38207
38416
|
}
|
|
38208
38417
|
function splitSearchPathArg(projectRoot, raw) {
|
|
38209
38418
|
if (searchPathExists(projectRoot, raw) || !/\s/.test(raw)) {
|
|
38210
|
-
return [raw];
|
|
38419
|
+
return { paths: [raw], missing: [] };
|
|
38211
38420
|
}
|
|
38212
38421
|
const fragments = raw.trim().split(/\s+/).filter(Boolean);
|
|
38213
|
-
if (fragments.length < 2
|
|
38214
|
-
return [raw];
|
|
38422
|
+
if (fragments.length < 2) {
|
|
38423
|
+
return { paths: [raw], missing: [] };
|
|
38424
|
+
}
|
|
38425
|
+
const existing = [];
|
|
38426
|
+
const missing = [];
|
|
38427
|
+
for (const fragment of fragments) {
|
|
38428
|
+
if (searchPathExists(projectRoot, fragment)) {
|
|
38429
|
+
existing.push(fragment);
|
|
38430
|
+
} else {
|
|
38431
|
+
missing.push(fragment);
|
|
38432
|
+
}
|
|
38433
|
+
}
|
|
38434
|
+
if (existing.length === 0) {
|
|
38435
|
+
return { paths: [raw], missing: [] };
|
|
38215
38436
|
}
|
|
38216
|
-
return
|
|
38437
|
+
return { paths: existing, missing };
|
|
38438
|
+
}
|
|
38439
|
+
function bridgeSearchPathArg(projectRoot, split) {
|
|
38440
|
+
return split.paths.map((target) => absoluteSearchPath(projectRoot, target)).join(" ");
|
|
38441
|
+
}
|
|
38442
|
+
function formatSkippedSearchPaths(missing) {
|
|
38443
|
+
if (missing.length === 0)
|
|
38444
|
+
return;
|
|
38445
|
+
const noun = missing.length === 1 ? "path" : "paths";
|
|
38446
|
+
return `Skipped ${missing.length} ${noun} not found: ${missing.join(", ")}`;
|
|
38447
|
+
}
|
|
38448
|
+
function appendSkippedSearchPaths(text, missing) {
|
|
38449
|
+
const note = formatSkippedSearchPaths(missing);
|
|
38450
|
+
if (!note)
|
|
38451
|
+
return text;
|
|
38452
|
+
return text.length > 0 ? `${text}
|
|
38453
|
+
|
|
38454
|
+
${note}` : note;
|
|
38217
38455
|
}
|
|
38218
38456
|
function searchPathKind(projectRoot, target, defaultKind) {
|
|
38219
38457
|
try {
|
|
@@ -38226,8 +38464,8 @@ function searchPathKind(projectRoot, target, defaultKind) {
|
|
|
38226
38464
|
return defaultKind;
|
|
38227
38465
|
}
|
|
38228
38466
|
}
|
|
38229
|
-
function searchPathTargets(projectRoot,
|
|
38230
|
-
return
|
|
38467
|
+
function searchPathTargets(projectRoot, split, defaultKind) {
|
|
38468
|
+
return split.paths.map((target) => {
|
|
38231
38469
|
const absoluteTarget = absoluteSearchPath(projectRoot, target);
|
|
38232
38470
|
return {
|
|
38233
38471
|
target: absoluteTarget,
|
|
@@ -38277,16 +38515,17 @@ function searchTools(ctx) {
|
|
|
38277
38515
|
const projectRoot = await resolveProjectRoot(ctx, context);
|
|
38278
38516
|
const pattern = String(args.pattern);
|
|
38279
38517
|
const includeArg = args.include ? String(args.include) : undefined;
|
|
38280
|
-
const pathArg = args.path ?
|
|
38281
|
-
const
|
|
38518
|
+
const pathArg = args.path ? String(args.path) : undefined;
|
|
38519
|
+
const pathSplit = pathArg ? splitSearchPathArg(projectRoot, pathArg) : undefined;
|
|
38520
|
+
const bridgePath = pathSplit ? bridgeSearchPathArg(projectRoot, pathSplit) : undefined;
|
|
38282
38521
|
const grepDenied = await askGrepPermission(context, pattern, {
|
|
38283
38522
|
path: bridgePath,
|
|
38284
38523
|
include: includeArg
|
|
38285
38524
|
});
|
|
38286
38525
|
if (grepDenied)
|
|
38287
38526
|
return permissionDeniedResponse(grepDenied);
|
|
38288
|
-
if (
|
|
38289
|
-
for (const target of searchPathTargets(projectRoot,
|
|
38527
|
+
if (pathSplit) {
|
|
38528
|
+
for (const target of searchPathTargets(projectRoot, pathSplit, "file")) {
|
|
38290
38529
|
const externalDenied = await assertExternalDirectoryPermission(context, target.target, {
|
|
38291
38530
|
kind: target.kind
|
|
38292
38531
|
});
|
|
@@ -38304,7 +38543,10 @@ function searchTools(ctx) {
|
|
|
38304
38543
|
if (response.success === false) {
|
|
38305
38544
|
throw new Error(response.message || "grep failed");
|
|
38306
38545
|
}
|
|
38307
|
-
|
|
38546
|
+
if (pathSplit && pathSplit.missing.length > 0) {
|
|
38547
|
+
response.complete = false;
|
|
38548
|
+
}
|
|
38549
|
+
return appendSkippedSearchPaths(formatGrepOutput(response), pathSplit?.missing ?? []);
|
|
38308
38550
|
}
|
|
38309
38551
|
};
|
|
38310
38552
|
const globTool = {
|
|
@@ -38316,7 +38558,7 @@ function searchTools(ctx) {
|
|
|
38316
38558
|
execute: async (args, context) => {
|
|
38317
38559
|
const projectRoot = await resolveProjectRoot(ctx, context);
|
|
38318
38560
|
let globPattern = expandTilde2(String(args.pattern));
|
|
38319
|
-
let globPath = args.path ?
|
|
38561
|
+
let globPath = args.path ? String(args.path) : undefined;
|
|
38320
38562
|
if (!globPath && globPattern.startsWith("/")) {
|
|
38321
38563
|
const metaIdx = globPattern.search(/[*?{}[\]]/);
|
|
38322
38564
|
if (metaIdx > 0) {
|
|
@@ -38330,12 +38572,13 @@ function searchTools(ctx) {
|
|
|
38330
38572
|
globPattern = path7.basename(globPattern);
|
|
38331
38573
|
}
|
|
38332
38574
|
}
|
|
38333
|
-
const
|
|
38575
|
+
const pathSplit = globPath ? splitSearchPathArg(projectRoot, globPath) : undefined;
|
|
38576
|
+
const bridgePath = pathSplit ? bridgeSearchPathArg(projectRoot, pathSplit) : undefined;
|
|
38577
|
+
const globDenied = await askGlobPermission(context, globPattern, { path: bridgePath });
|
|
38334
38578
|
if (globDenied)
|
|
38335
38579
|
return permissionDeniedResponse(globDenied);
|
|
38336
|
-
|
|
38337
|
-
|
|
38338
|
-
for (const target of searchPathTargets(projectRoot, globPath, "directory")) {
|
|
38580
|
+
if (pathSplit) {
|
|
38581
|
+
for (const target of searchPathTargets(projectRoot, pathSplit, "directory")) {
|
|
38339
38582
|
const externalDenied = await assertExternalDirectoryPermission(context, target.target, {
|
|
38340
38583
|
kind: target.kind
|
|
38341
38584
|
});
|
|
@@ -38350,14 +38593,17 @@ function searchTools(ctx) {
|
|
|
38350
38593
|
if (response.success === false) {
|
|
38351
38594
|
throw new Error(response.message || "glob failed");
|
|
38352
38595
|
}
|
|
38596
|
+
if (pathSplit && pathSplit.missing.length > 0) {
|
|
38597
|
+
response.complete = false;
|
|
38598
|
+
}
|
|
38353
38599
|
if (typeof response.text === "string") {
|
|
38354
|
-
return response.text;
|
|
38600
|
+
return appendSkippedSearchPaths(response.text, pathSplit?.missing ?? []);
|
|
38355
38601
|
}
|
|
38356
38602
|
if (Array.isArray(response.files)) {
|
|
38357
|
-
return response.files.join(`
|
|
38358
|
-
`);
|
|
38603
|
+
return appendSkippedSearchPaths(response.files.join(`
|
|
38604
|
+
`), pathSplit?.missing ?? []);
|
|
38359
38605
|
}
|
|
38360
|
-
return response.text || JSON.stringify(response);
|
|
38606
|
+
return appendSkippedSearchPaths(response.text || JSON.stringify(response), pathSplit?.missing ?? []);
|
|
38361
38607
|
}
|
|
38362
38608
|
};
|
|
38363
38609
|
const hoisting = ctx.config.hoist_builtin_tools !== false;
|
|
@@ -38499,11 +38745,11 @@ function buildWorkflowHints(opts) {
|
|
|
38499
38745
|
}
|
|
38500
38746
|
if (hasBash && hasBgBash) {
|
|
38501
38747
|
sections.push([
|
|
38502
|
-
`**Long-running commands** (builds, installs, full test suites): \`${bashName}({ background: true })\` returns a \`taskId\` immediately
|
|
38503
|
-
"1.
|
|
38504
|
-
"2.
|
|
38505
|
-
|
|
38506
|
-
|
|
38748
|
+
`**Long-running commands** (builds, installs, full test suites): \`${bashName}({ background: true })\` returns a \`taskId\` immediately.`,
|
|
38749
|
+
"1. Nothing else useful to do (a build/test/validation whose result is the next thing you need) → sync `bash_watch` to block until it exits (pass a longer timeoutMs for long commands; the user can interrupt).",
|
|
38750
|
+
"2. Useful parallel work available → end your turn; the completion reminder delivers the result. (Or spawn a subagent for the side work.)",
|
|
38751
|
+
"3. Want to react to a specific early output line → async `bash_watch` (background:true + pattern).",
|
|
38752
|
+
"Never loop `bash_status` to wait — it's a one-shot inspector, not a wait primitive."
|
|
38507
38753
|
].join(`
|
|
38508
38754
|
`));
|
|
38509
38755
|
sections.push(`**PTY / interactive commands**: PTY mode is for interactive REPLs and terminal apps (python, node, bash itself, vim). Start with \`${bashName}({ command: "python", pty: true, background: true })\`, read the screen with \`${bashStatusName}({ taskId, outputMode: "screen" })\`, and send input with \`${bashWriteName}({ taskId, input: "..." })\`.`);
|
|
@@ -38588,6 +38834,7 @@ async function initializePluginForDirectory(input) {
|
|
|
38588
38834
|
const binaryPath = await findBinary(PLUGIN_VERSION);
|
|
38589
38835
|
await ensureStorageMigrated({ harness: "opencode", binaryPath, logger: bridgeLogger });
|
|
38590
38836
|
const aftConfig = loadAftConfig(input.directory);
|
|
38837
|
+
enqueueConfigParseWarnings(input.directory, getConfigLoadErrors());
|
|
38591
38838
|
const autoUpdateAbort = new AbortController;
|
|
38592
38839
|
const configOverrides = {
|
|
38593
38840
|
...resolveProjectOverridesForConfigure(aftConfig),
|
|
@@ -38672,11 +38919,13 @@ ${lines}
|
|
|
38672
38919
|
}
|
|
38673
38920
|
const versionUpgradePromises = new Map;
|
|
38674
38921
|
const poolOptions = {
|
|
38922
|
+
...resolveBridgePoolTransportOptions(aftConfig),
|
|
38675
38923
|
errorPrefix: "[aft-plugin]",
|
|
38676
38924
|
minVersion: PLUGIN_VERSION,
|
|
38677
38925
|
projectConfigLoader: (projectRoot) => {
|
|
38678
38926
|
try {
|
|
38679
38927
|
const projectConfig = loadAftConfig(projectRoot);
|
|
38928
|
+
enqueueConfigParseWarnings(projectRoot, getConfigLoadErrors());
|
|
38680
38929
|
return resolveProjectOverridesForConfigure(projectConfig);
|
|
38681
38930
|
} catch (err) {
|
|
38682
38931
|
warn2(`loadAftConfig(${projectRoot}) failed; falling back to plugin-init config: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -38735,8 +38984,7 @@ ${lines}
|
|
|
38735
38984
|
ctx,
|
|
38736
38985
|
directory: sessionDir,
|
|
38737
38986
|
sessionID: completion.session_id,
|
|
38738
|
-
client: input.client
|
|
38739
|
-
serverUrl: input.serverUrl?.toString()
|
|
38987
|
+
client: input.client
|
|
38740
38988
|
}, completion);
|
|
38741
38989
|
},
|
|
38742
38990
|
onBashLongRunning: (reminder, bridge) => {
|
|
@@ -38745,8 +38993,7 @@ ${lines}
|
|
|
38745
38993
|
ctx,
|
|
38746
38994
|
directory: sessionDir,
|
|
38747
38995
|
sessionID: reminder.session_id,
|
|
38748
|
-
client: input.client
|
|
38749
|
-
serverUrl: input.serverUrl?.toString()
|
|
38996
|
+
client: input.client
|
|
38750
38997
|
}, reminder);
|
|
38751
38998
|
},
|
|
38752
38999
|
onBashPatternMatch: (frame, bridge) => {
|
|
@@ -38755,8 +39002,7 @@ ${lines}
|
|
|
38755
39002
|
ctx,
|
|
38756
39003
|
directory: sessionDir,
|
|
38757
39004
|
sessionID: frame.session_id,
|
|
38758
|
-
client: input.client
|
|
38759
|
-
serverUrl: input.serverUrl?.toString()
|
|
39005
|
+
client: input.client
|
|
38760
39006
|
}, frame);
|
|
38761
39007
|
}
|
|
38762
39008
|
};
|
|
@@ -38769,16 +39015,6 @@ ${lines}
|
|
|
38769
39015
|
config: aftConfig,
|
|
38770
39016
|
storageDir: configOverrides.storage_dir
|
|
38771
39017
|
};
|
|
38772
|
-
probeServerReachable(input.serverUrl?.toString()).then((reachable) => {
|
|
38773
|
-
setLiveServerWakeAvailable(reachable);
|
|
38774
|
-
if (reachable) {
|
|
38775
|
-
log2("Live OpenCode HTTP listener reachable; bg-notifications wake path = live-server (anomalyco/opencode#28202 workaround active).");
|
|
38776
|
-
} else {
|
|
38777
|
-
debug("Live OpenCode HTTP listener unreachable; bg-notifications wake path = in-process-fallback. Wakes will still arrive but the upstream duplicate-runner bug (anomalyco/opencode#28202) is not worked around. Launch with `opencode --port 0` in TUI mode to activate the workaround.");
|
|
38778
|
-
}
|
|
38779
|
-
}).catch(() => {
|
|
38780
|
-
setLiveServerWakeAvailable(false);
|
|
38781
|
-
});
|
|
38782
39018
|
if (onnxRuntimePromise) {
|
|
38783
39019
|
onnxRuntimePromise.then((ortDylibDir) => {
|
|
38784
39020
|
if (ortDylibDir) {
|
|
@@ -39013,9 +39249,24 @@ Install: ${getManualInstallHint()}`).catch(() => {});
|
|
|
39013
39249
|
ctx,
|
|
39014
39250
|
directory: sessionDir,
|
|
39015
39251
|
sessionID,
|
|
39016
|
-
client: input.client
|
|
39017
|
-
serverUrl: input.serverUrl?.toString()
|
|
39252
|
+
client: input.client
|
|
39018
39253
|
});
|
|
39254
|
+
const configParseWarnings = drainPendingConfigParseWarnings(sessionDir);
|
|
39255
|
+
if (configParseWarnings.length > 0) {
|
|
39256
|
+
const bridge = pool.getActiveBridgeForRoot(sessionDir) ?? pool.getBridge(sessionDir);
|
|
39257
|
+
enqueueConfigureWarningsForSession({
|
|
39258
|
+
projectRoot: sessionDir,
|
|
39259
|
+
sessionId: sessionID,
|
|
39260
|
+
client: input.client,
|
|
39261
|
+
bridge,
|
|
39262
|
+
warnings: configParseWarnings,
|
|
39263
|
+
fallbackClient: input.client,
|
|
39264
|
+
storageDir: configOverrides.storage_dir,
|
|
39265
|
+
pluginVersion: PLUGIN_VERSION,
|
|
39266
|
+
serverUrl: input.serverUrl?.toString(),
|
|
39267
|
+
delivery: aftConfig.configure_warnings_delivery ?? "toast"
|
|
39268
|
+
});
|
|
39269
|
+
}
|
|
39019
39270
|
await flushConfigureWarningsOnIdle(sessionID);
|
|
39020
39271
|
},
|
|
39021
39272
|
"chat.message": async (messageInput) => {
|