@cortexkit/aft-opencode 0.39.2 → 0.39.4
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 +11 -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 +597 -321
- package/dist/notifications.d.ts +1 -1
- package/dist/notifications.d.ts.map +1 -1
- package/dist/shared/ignored-message.d.ts +19 -0
- package/dist/shared/ignored-message.d.ts.map +1 -0
- package/dist/tools/ast.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/bash_watch.d.ts +1 -1
- package/dist/tools/bash_watch.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/permissions.d.ts +2 -1
- package/dist/tools/permissions.d.ts.map +1 -1
- package/dist/tools/reading.d.ts.map +1 -1
- package/dist/tools/search.d.ts.map +1 -1
- package/dist/tui.js +18 -14
- package/dist/workflow-hints.d.ts +3 -4
- package/dist/workflow-hints.d.ts.map +1 -1
- package/package.json +11 -11
- package/src/shared/ignored-message.ts +61 -0
- package/src/tui/index.tsx +17 -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
|
@@ -12101,6 +12101,21 @@ class BridgeReplacedDuringVersionCheck extends Error {
|
|
|
12101
12101
|
}
|
|
12102
12102
|
}
|
|
12103
12103
|
|
|
12104
|
+
class BridgeTransportTimeoutError extends Error {
|
|
12105
|
+
command;
|
|
12106
|
+
timeoutMs;
|
|
12107
|
+
code = "transport_timeout";
|
|
12108
|
+
constructor(command, timeoutMs, message) {
|
|
12109
|
+
super(message);
|
|
12110
|
+
this.command = command;
|
|
12111
|
+
this.timeoutMs = timeoutMs;
|
|
12112
|
+
this.name = "BridgeTransportTimeoutError";
|
|
12113
|
+
}
|
|
12114
|
+
}
|
|
12115
|
+
function isBridgeTransportTimeout(err) {
|
|
12116
|
+
return err instanceof Error && err.code === "transport_timeout";
|
|
12117
|
+
}
|
|
12118
|
+
|
|
12104
12119
|
class BinaryBridge {
|
|
12105
12120
|
static RESTART_RESET_MS = 5 * 60 * 1000;
|
|
12106
12121
|
static STDERR_TAIL_MAX = 20;
|
|
@@ -12328,7 +12343,7 @@ class BinaryBridge {
|
|
|
12328
12343
|
} else {
|
|
12329
12344
|
this.warnVia(timeoutMsg2);
|
|
12330
12345
|
}
|
|
12331
|
-
entry.reject(new
|
|
12346
|
+
entry.reject(new BridgeTransportTimeoutError(command, effectiveTimeoutMs, `${this.errorPrefix} Request "${command}" (id=${id}) timed out after ${effectiveTimeoutMs}ms`));
|
|
12332
12347
|
return;
|
|
12333
12348
|
}
|
|
12334
12349
|
const childActiveSinceRequest = this.lastChildActivityAt > requestSentAt;
|
|
@@ -12864,6 +12879,220 @@ class BinaryBridge {
|
|
|
12864
12879
|
}
|
|
12865
12880
|
}
|
|
12866
12881
|
}
|
|
12882
|
+
// ../aft-bridge/dist/callgraph-format.js
|
|
12883
|
+
import { homedir as homedir3 } from "node:os";
|
|
12884
|
+
var PLAIN_CALLGRAPH_THEME = {
|
|
12885
|
+
fg: (_role, text) => text
|
|
12886
|
+
};
|
|
12887
|
+
function asRecord(value) {
|
|
12888
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
12889
|
+
return;
|
|
12890
|
+
return value;
|
|
12891
|
+
}
|
|
12892
|
+
function asRecords(value) {
|
|
12893
|
+
return Array.isArray(value) ? value.map(asRecord).filter(Boolean) : [];
|
|
12894
|
+
}
|
|
12895
|
+
function asString(value) {
|
|
12896
|
+
return typeof value === "string" ? value : undefined;
|
|
12897
|
+
}
|
|
12898
|
+
function asNumber(value) {
|
|
12899
|
+
return typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
12900
|
+
}
|
|
12901
|
+
function asBoolean(value) {
|
|
12902
|
+
return typeof value === "boolean" ? value : undefined;
|
|
12903
|
+
}
|
|
12904
|
+
function shortenPath(path2) {
|
|
12905
|
+
const home = homedir3();
|
|
12906
|
+
if (path2.startsWith(home))
|
|
12907
|
+
return `~${path2.slice(home.length)}`;
|
|
12908
|
+
return path2;
|
|
12909
|
+
}
|
|
12910
|
+
function joinNonEmpty(parts, separator = " · ") {
|
|
12911
|
+
return parts.filter((part) => Boolean(part && part.length > 0)).join(separator);
|
|
12912
|
+
}
|
|
12913
|
+
function treeLine(depth, text) {
|
|
12914
|
+
return `${" ".repeat(depth)}${depth === 0 ? "" : "↳ "}${text}`;
|
|
12915
|
+
}
|
|
12916
|
+
function nameMatchEdgeMarker(record, theme) {
|
|
12917
|
+
return asString(record.resolved_by) === "name_match" ? ` ${theme.fg("warning", "~")}` : "";
|
|
12918
|
+
}
|
|
12919
|
+
function renderCallTreeNode(node, depth, lines, theme) {
|
|
12920
|
+
const name = asString(node.name) ?? "(unknown)";
|
|
12921
|
+
const file = shortenPath(asString(node.file) ?? "(unknown file)");
|
|
12922
|
+
const line = asNumber(node.line);
|
|
12923
|
+
const unresolved = node.resolved === false ? ` ${theme.fg("warning", "[unresolved]")}` : "";
|
|
12924
|
+
const nameMatch = nameMatchEdgeMarker(node, theme);
|
|
12925
|
+
const location = line !== undefined ? `[${file}:${line}]` : `[${file}]`;
|
|
12926
|
+
lines.push(treeLine(depth, `${name} ${location}${unresolved}${nameMatch}`));
|
|
12927
|
+
asRecords(node.children).forEach((child) => {
|
|
12928
|
+
renderCallTreeNode(child, depth + 1, lines, theme);
|
|
12929
|
+
});
|
|
12930
|
+
}
|
|
12931
|
+
function depthWarning(response, theme, depthField = "depth_limited", truncatedField = "truncated") {
|
|
12932
|
+
const limited = asBoolean(response[depthField]);
|
|
12933
|
+
const truncated = asNumber(response[truncatedField]) ?? 0;
|
|
12934
|
+
if (!limited && truncated === 0)
|
|
12935
|
+
return "";
|
|
12936
|
+
const detail = truncated > 0 ? `, ${truncated} truncated` : "";
|
|
12937
|
+
return theme.fg("warning", `(depth limited${detail})`);
|
|
12938
|
+
}
|
|
12939
|
+
function renderTracePath(path2, index, lines, theme) {
|
|
12940
|
+
lines.push(`Path ${index + 1}`);
|
|
12941
|
+
asRecords(path2.hops).forEach((hop, hopIndex) => {
|
|
12942
|
+
const symbol = asString(hop.symbol) ?? "(unknown)";
|
|
12943
|
+
const file = shortenPath(asString(hop.file) ?? "(unknown file)");
|
|
12944
|
+
const line = asNumber(hop.line);
|
|
12945
|
+
const entry = hop.is_entry_point === true ? " [entry]" : "";
|
|
12946
|
+
const nameMatch = nameMatchEdgeMarker(hop, theme);
|
|
12947
|
+
lines.push(treeLine(hopIndex + 1, `${symbol}${entry} ${line !== undefined ? `[${file}:${line}]` : `[${file}]`}${nameMatch}`));
|
|
12948
|
+
});
|
|
12949
|
+
}
|
|
12950
|
+
function renderCallersGroupLines(group, theme) {
|
|
12951
|
+
const file = shortenPath(asString(group.file) ?? "(unknown file)");
|
|
12952
|
+
const lines = [theme.fg("accent", file)];
|
|
12953
|
+
const callers = asRecords(group.callers);
|
|
12954
|
+
const bySymbolProvenance = new Map;
|
|
12955
|
+
for (const caller of callers) {
|
|
12956
|
+
const symbol = asString(caller.symbol) ?? "(unknown)";
|
|
12957
|
+
const provenanceKey = asString(caller.resolved_by) === "name_match" ? `${symbol}\x00name_match` : `${symbol}\x00exact`;
|
|
12958
|
+
const line = asNumber(caller.line);
|
|
12959
|
+
const bucket = bySymbolProvenance.get(provenanceKey) ?? [];
|
|
12960
|
+
if (line !== undefined)
|
|
12961
|
+
bucket.push(line);
|
|
12962
|
+
bySymbolProvenance.set(provenanceKey, bucket);
|
|
12963
|
+
}
|
|
12964
|
+
const keys = [...bySymbolProvenance.keys()].sort((a, b) => a.localeCompare(b));
|
|
12965
|
+
for (const key of keys) {
|
|
12966
|
+
const symbol = key.split("\x00")[0] ?? "(unknown)";
|
|
12967
|
+
const isNameMatch = key.endsWith("\x00name_match");
|
|
12968
|
+
const lineNums = (bySymbolProvenance.get(key) ?? []).sort((a, b) => a - b);
|
|
12969
|
+
const linePart = lineNums.length > 0 ? lineNums.map(String).join(", ") : "?";
|
|
12970
|
+
const marker = isNameMatch ? ` ${theme.fg("warning", "~")}` : "";
|
|
12971
|
+
lines.push(` ↳ ${symbol}:${linePart}${marker}`);
|
|
12972
|
+
}
|
|
12973
|
+
return lines;
|
|
12974
|
+
}
|
|
12975
|
+
function formatCallgraphSections(op, response, theme = PLAIN_CALLGRAPH_THEME) {
|
|
12976
|
+
const record = asRecord(response);
|
|
12977
|
+
if (!record)
|
|
12978
|
+
return [theme.fg("muted", "No navigation result.")];
|
|
12979
|
+
if (op === "call_tree") {
|
|
12980
|
+
const lines = [];
|
|
12981
|
+
renderCallTreeNode(record, 0, lines, theme);
|
|
12982
|
+
const warning = depthWarning(record, theme);
|
|
12983
|
+
if (warning)
|
|
12984
|
+
lines.push(warning);
|
|
12985
|
+
return lines.length > 0 ? lines : [theme.fg("muted", "No call tree available.")];
|
|
12986
|
+
}
|
|
12987
|
+
if (op === "callers") {
|
|
12988
|
+
const groups = asRecords(record.callers);
|
|
12989
|
+
const warning = depthWarning(record, theme);
|
|
12990
|
+
const total = asNumber(record.total_callers) ?? 0;
|
|
12991
|
+
const sections2 = [
|
|
12992
|
+
joinNonEmpty([
|
|
12993
|
+
theme.fg("success", `${total} caller${total === 1 ? "" : "s"}`),
|
|
12994
|
+
theme.fg("muted", `${groups.length} file group${groups.length === 1 ? "" : "s"}`),
|
|
12995
|
+
warning
|
|
12996
|
+
])
|
|
12997
|
+
];
|
|
12998
|
+
groups.forEach((group) => {
|
|
12999
|
+
sections2.push(renderCallersGroupLines(group, theme).join(`
|
|
13000
|
+
`));
|
|
13001
|
+
});
|
|
13002
|
+
return sections2;
|
|
13003
|
+
}
|
|
13004
|
+
if (op === "trace_to_symbol") {
|
|
13005
|
+
const path2 = asRecords(record.path);
|
|
13006
|
+
const complete = asBoolean(record.complete);
|
|
13007
|
+
const reason = asString(record.reason);
|
|
13008
|
+
if (path2.length === 0) {
|
|
13009
|
+
const prefix = complete === false ? theme.fg("warning", "No complete path") : theme.fg("muted", "No path");
|
|
13010
|
+
return [`${prefix}${reason ? ` (${reason})` : ""}`];
|
|
13011
|
+
}
|
|
13012
|
+
const lines = [theme.fg("success", `${path2.length} hop${path2.length === 1 ? "" : "s"}`)];
|
|
13013
|
+
path2.forEach((hop, index) => {
|
|
13014
|
+
const symbol = asString(hop.symbol) ?? "(unknown)";
|
|
13015
|
+
const file = shortenPath(asString(hop.file) ?? "(unknown file)");
|
|
13016
|
+
const line = asNumber(hop.line);
|
|
13017
|
+
const nameMatch = nameMatchEdgeMarker(hop, theme);
|
|
13018
|
+
lines.push(treeLine(index + 1, `${symbol} ${line !== undefined ? `[${file}:${line}]` : `[${file}]`}${nameMatch}`));
|
|
13019
|
+
});
|
|
13020
|
+
return lines;
|
|
13021
|
+
}
|
|
13022
|
+
if (op === "trace_to") {
|
|
13023
|
+
const paths = asRecords(record.paths);
|
|
13024
|
+
const warning = depthWarning(record, theme, "max_depth_reached", "truncated_paths");
|
|
13025
|
+
const totalPaths = asNumber(record.total_paths) ?? paths.length;
|
|
13026
|
+
const entryPoints = asNumber(record.entry_points_found) ?? 0;
|
|
13027
|
+
const sections2 = [
|
|
13028
|
+
joinNonEmpty([
|
|
13029
|
+
theme.fg("success", `${totalPaths} path${totalPaths === 1 ? "" : "s"}`),
|
|
13030
|
+
theme.fg("muted", `${entryPoints} entry point${entryPoints === 1 ? "" : "s"}`),
|
|
13031
|
+
warning
|
|
13032
|
+
])
|
|
13033
|
+
];
|
|
13034
|
+
if (paths.length === 0)
|
|
13035
|
+
sections2.push(theme.fg("muted", "No entry paths found."));
|
|
13036
|
+
paths.forEach((path2, index) => {
|
|
13037
|
+
const lines = [];
|
|
13038
|
+
renderTracePath(path2, index, lines, theme);
|
|
13039
|
+
sections2.push(lines.join(`
|
|
13040
|
+
`));
|
|
13041
|
+
});
|
|
13042
|
+
return sections2;
|
|
13043
|
+
}
|
|
13044
|
+
if (op === "impact") {
|
|
13045
|
+
const callers = asRecords(record.callers);
|
|
13046
|
+
const warning = depthWarning(record, theme);
|
|
13047
|
+
const totalAffected = asNumber(record.total_affected) ?? callers.length;
|
|
13048
|
+
const affectedFiles = asNumber(record.affected_files) ?? 0;
|
|
13049
|
+
const sections2 = [
|
|
13050
|
+
joinNonEmpty([
|
|
13051
|
+
theme.fg("warning", `${totalAffected} affected call site${totalAffected === 1 ? "" : "s"}`),
|
|
13052
|
+
theme.fg("muted", `${affectedFiles} file${affectedFiles === 1 ? "" : "s"}`),
|
|
13053
|
+
warning
|
|
13054
|
+
])
|
|
13055
|
+
];
|
|
13056
|
+
if (callers.length === 0)
|
|
13057
|
+
sections2.push(theme.fg("muted", "No impacted callers found."));
|
|
13058
|
+
callers.forEach((caller) => {
|
|
13059
|
+
const file = shortenPath(asString(caller.caller_file) ?? "(unknown file)");
|
|
13060
|
+
const symbol = asString(caller.caller_symbol) ?? "(unknown)";
|
|
13061
|
+
const line = asNumber(caller.line) ?? 0;
|
|
13062
|
+
const entry = caller.is_entry_point === true ? ` ${theme.fg("warning", "[entry]")}` : "";
|
|
13063
|
+
const nameMatch = nameMatchEdgeMarker(caller, theme);
|
|
13064
|
+
const expression = asString(caller.call_expression);
|
|
13065
|
+
const params = Array.isArray(caller.parameters) ? caller.parameters.map(String).join(", ") : "";
|
|
13066
|
+
sections2.push([
|
|
13067
|
+
`${theme.fg("accent", file)}:${line}`,
|
|
13068
|
+
` ↳ ${symbol}${entry}${nameMatch}`,
|
|
13069
|
+
expression ? ` ${theme.fg("muted", expression)}` : undefined,
|
|
13070
|
+
params ? ` ${theme.fg("muted", `params: ${params}`)}` : undefined
|
|
13071
|
+
].filter(Boolean).join(`
|
|
13072
|
+
`));
|
|
13073
|
+
});
|
|
13074
|
+
return sections2;
|
|
13075
|
+
}
|
|
13076
|
+
const hops = asRecords(record.hops);
|
|
13077
|
+
const sections = [
|
|
13078
|
+
joinNonEmpty([
|
|
13079
|
+
theme.fg("success", `${hops.length} hop${hops.length === 1 ? "" : "s"}`),
|
|
13080
|
+
asBoolean(record.depth_limited) ? theme.fg("warning", "(depth limited)") : undefined
|
|
13081
|
+
])
|
|
13082
|
+
];
|
|
13083
|
+
if (hops.length === 0)
|
|
13084
|
+
sections.push(theme.fg("muted", "No data-flow hops found."));
|
|
13085
|
+
hops.forEach((hop, index) => {
|
|
13086
|
+
const file = shortenPath(asString(hop.file) ?? "(unknown file)");
|
|
13087
|
+
const symbol = asString(hop.symbol) ?? "(unknown)";
|
|
13088
|
+
const variable = asString(hop.variable) ?? "(unknown)";
|
|
13089
|
+
const line = asNumber(hop.line) ?? 0;
|
|
13090
|
+
const approximate = hop.approximate === true ? ` ${theme.fg("warning", "[approx]")}` : "";
|
|
13091
|
+
const nameMatch = nameMatchEdgeMarker(hop, theme);
|
|
13092
|
+
sections.push(treeLine(index, `${variable} ${theme.fg("muted", `${asString(hop.flow_type) ?? "flow"}`)} ${symbol} [${file}:${line}]${approximate}${nameMatch}`));
|
|
13093
|
+
});
|
|
13094
|
+
return sections;
|
|
13095
|
+
}
|
|
12867
13096
|
// ../aft-bridge/dist/coerce.js
|
|
12868
13097
|
function coerceStringArray(value) {
|
|
12869
13098
|
if (Array.isArray(value)) {
|
|
@@ -12900,7 +13129,7 @@ function isEmptyParam(value) {
|
|
|
12900
13129
|
import { spawnSync } from "node:child_process";
|
|
12901
13130
|
import { createHash, randomUUID } from "node:crypto";
|
|
12902
13131
|
import { chmodSync, closeSync, copyFileSync, createWriteStream, existsSync, mkdirSync, openSync, readFileSync, renameSync, rmSync, statSync, unlinkSync, writeSync } from "node:fs";
|
|
12903
|
-
import { homedir as
|
|
13132
|
+
import { homedir as homedir4 } from "node:os";
|
|
12904
13133
|
import { join as join3 } from "node:path";
|
|
12905
13134
|
import { Readable } from "node:stream";
|
|
12906
13135
|
import { pipeline } from "node:stream/promises";
|
|
@@ -12958,10 +13187,10 @@ function isExpectedCachedBinary(binaryPath, tag) {
|
|
|
12958
13187
|
function getCacheDir() {
|
|
12959
13188
|
if (process.platform === "win32") {
|
|
12960
13189
|
const localAppData = process.env.LOCALAPPDATA || process.env.APPDATA;
|
|
12961
|
-
const base2 = localAppData || join3(
|
|
13190
|
+
const base2 = localAppData || join3(homedir4(), "AppData", "Local");
|
|
12962
13191
|
return join3(base2, "aft", "bin");
|
|
12963
13192
|
}
|
|
12964
|
-
const base = process.env.XDG_CACHE_HOME || join3(
|
|
13193
|
+
const base = process.env.XDG_CACHE_HOME || join3(homedir4(), ".cache");
|
|
12965
13194
|
return join3(base, "aft", "bin");
|
|
12966
13195
|
}
|
|
12967
13196
|
function getBinaryName() {
|
|
@@ -13203,7 +13432,7 @@ function formatEditSummary(data) {
|
|
|
13203
13432
|
if (data.created === true) {
|
|
13204
13433
|
let s2 = `Created file (${counts}).`;
|
|
13205
13434
|
if (data.formatted)
|
|
13206
|
-
s2 +=
|
|
13435
|
+
s2 += formatAutoFormattedSuffix(data);
|
|
13207
13436
|
return s2;
|
|
13208
13437
|
}
|
|
13209
13438
|
let detail = counts;
|
|
@@ -13214,9 +13443,21 @@ function formatEditSummary(data) {
|
|
|
13214
13443
|
}
|
|
13215
13444
|
let s = `Edited (${detail}).`;
|
|
13216
13445
|
if (data.formatted)
|
|
13217
|
-
s +=
|
|
13446
|
+
s += formatAutoFormattedSuffix(data);
|
|
13218
13447
|
return s;
|
|
13219
13448
|
}
|
|
13449
|
+
function formatAutoFormattedSuffix(data) {
|
|
13450
|
+
const reflowText = data.reformatted?.text;
|
|
13451
|
+
if (typeof reflowText === "string" && reflowText.length > 0) {
|
|
13452
|
+
return `
|
|
13453
|
+
Auto-formatted — the formatter reflowed your edit. On disk now:
|
|
13454
|
+
${reflowText}`;
|
|
13455
|
+
}
|
|
13456
|
+
if (data.reformatted?.extensive === true) {
|
|
13457
|
+
return " Auto-formatted — extensive reflow; re-read the file before your next anchored edit.";
|
|
13458
|
+
}
|
|
13459
|
+
return " Auto-formatted.";
|
|
13460
|
+
}
|
|
13220
13461
|
// ../aft-bridge/dist/jsonc.js
|
|
13221
13462
|
function stripJsoncSymbols(value) {
|
|
13222
13463
|
if (Array.isArray(value)) {
|
|
@@ -13234,7 +13475,7 @@ function stripJsoncSymbols(value) {
|
|
|
13234
13475
|
// ../aft-bridge/dist/migration.js
|
|
13235
13476
|
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
13236
13477
|
import { existsSync as existsSync4, mkdirSync as mkdirSync4 } from "node:fs";
|
|
13237
|
-
import { homedir as
|
|
13478
|
+
import { homedir as homedir6, tmpdir } from "node:os";
|
|
13238
13479
|
import { dirname as dirname2, join as join6 } from "node:path";
|
|
13239
13480
|
|
|
13240
13481
|
// ../aft-bridge/dist/paths.js
|
|
@@ -13291,7 +13532,7 @@ function markAnnouncementSeen(storageRoot, harness, currentVersion) {
|
|
|
13291
13532
|
import { execSync } from "node:child_process";
|
|
13292
13533
|
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";
|
|
13293
13534
|
import { createRequire as createRequire2 } from "node:module";
|
|
13294
|
-
import { homedir as
|
|
13535
|
+
import { homedir as homedir5 } from "node:os";
|
|
13295
13536
|
import { join as join5 } from "node:path";
|
|
13296
13537
|
var ensureBinaryForResolver = ensureBinary;
|
|
13297
13538
|
function copyToVersionedCache(npmBinaryPath, knownVersion) {
|
|
@@ -13333,7 +13574,7 @@ function normalizeBareVersion(version) {
|
|
|
13333
13574
|
return version.startsWith("v") ? version.slice(1) : version;
|
|
13334
13575
|
}
|
|
13335
13576
|
function homeDirFromEnv(env) {
|
|
13336
|
-
return (process.platform === "win32" ? env.USERPROFILE || env.HOME : env.HOME) ||
|
|
13577
|
+
return (process.platform === "win32" ? env.USERPROFILE || env.HOME : env.HOME) || homedir5();
|
|
13337
13578
|
}
|
|
13338
13579
|
function cacheDirFromEnv(env) {
|
|
13339
13580
|
if (process.platform === "win32") {
|
|
@@ -13515,8 +13756,8 @@ function dataHome() {
|
|
|
13515
13756
|
}
|
|
13516
13757
|
function homeDir() {
|
|
13517
13758
|
if (process.platform === "win32")
|
|
13518
|
-
return process.env.USERPROFILE || process.env.HOME ||
|
|
13519
|
-
return process.env.HOME ||
|
|
13759
|
+
return process.env.USERPROFILE || process.env.HOME || homedir6();
|
|
13760
|
+
return process.env.HOME || homedir6();
|
|
13520
13761
|
}
|
|
13521
13762
|
function resolveLegacyStorageRoot(harness) {
|
|
13522
13763
|
if (harness === "pi")
|
|
@@ -13606,13 +13847,13 @@ async function ensureStorageMigrated(opts) {
|
|
|
13606
13847
|
}
|
|
13607
13848
|
// ../aft-bridge/dist/npm-resolver.js
|
|
13608
13849
|
import { readdirSync, statSync as statSync2 } from "node:fs";
|
|
13609
|
-
import { homedir as
|
|
13850
|
+
import { homedir as homedir7 } from "node:os";
|
|
13610
13851
|
import { delimiter, dirname as dirname3, isAbsolute as isAbsolute2, join as join7 } from "node:path";
|
|
13611
13852
|
function defaultDeps() {
|
|
13612
13853
|
return {
|
|
13613
13854
|
platform: process.platform,
|
|
13614
13855
|
env: process.env,
|
|
13615
|
-
home:
|
|
13856
|
+
home: homedir7(),
|
|
13616
13857
|
execPath: process.execPath
|
|
13617
13858
|
};
|
|
13618
13859
|
}
|
|
@@ -15379,9 +15620,6 @@ function write(level, message, data, sessionId) {
|
|
|
15379
15620
|
function log2(message, data) {
|
|
15380
15621
|
write("INFO", message, data);
|
|
15381
15622
|
}
|
|
15382
|
-
function debug(message, data) {
|
|
15383
|
-
write("DEBUG", message, data);
|
|
15384
|
-
}
|
|
15385
15623
|
function warn2(message, data) {
|
|
15386
15624
|
write("WARN", message, data);
|
|
15387
15625
|
}
|
|
@@ -15391,9 +15629,6 @@ function error2(message, data) {
|
|
|
15391
15629
|
function sessionLog(sessionId, message, data) {
|
|
15392
15630
|
write("INFO", message, data, sessionId);
|
|
15393
15631
|
}
|
|
15394
|
-
function sessionDebug(sessionId, message, data) {
|
|
15395
|
-
write("DEBUG", message, data, sessionId);
|
|
15396
|
-
}
|
|
15397
15632
|
function sessionWarn(sessionId, message, data) {
|
|
15398
15633
|
write("WARN", message, data, sessionId);
|
|
15399
15634
|
}
|
|
@@ -15487,84 +15722,6 @@ async function resolvePromptContext(client, sessionId) {
|
|
|
15487
15722
|
return result;
|
|
15488
15723
|
}
|
|
15489
15724
|
|
|
15490
|
-
// src/shared/live-server-client.ts
|
|
15491
|
-
import { createOpencodeClient } from "@opencode-ai/sdk";
|
|
15492
|
-
var clientCache = new Map;
|
|
15493
|
-
function cacheKey(serverUrl, directory) {
|
|
15494
|
-
return `${serverUrl}|${directory}`;
|
|
15495
|
-
}
|
|
15496
|
-
function normalizeServerUrl(serverUrl) {
|
|
15497
|
-
try {
|
|
15498
|
-
return new URL(serverUrl).toString();
|
|
15499
|
-
} catch {
|
|
15500
|
-
return serverUrl;
|
|
15501
|
-
}
|
|
15502
|
-
}
|
|
15503
|
-
function serverAuthHeaders() {
|
|
15504
|
-
const password = process.env.OPENCODE_SERVER_PASSWORD;
|
|
15505
|
-
if (!password)
|
|
15506
|
-
return;
|
|
15507
|
-
const username = process.env.OPENCODE_SERVER_USERNAME ?? "opencode";
|
|
15508
|
-
return {
|
|
15509
|
-
Authorization: `Basic ${Buffer.from(`${username}:${password}`).toString("base64")}`
|
|
15510
|
-
};
|
|
15511
|
-
}
|
|
15512
|
-
function getLiveServerClient(serverUrl, directory) {
|
|
15513
|
-
const key = cacheKey(serverUrl, directory);
|
|
15514
|
-
const cached = clientCache.get(key);
|
|
15515
|
-
if (cached)
|
|
15516
|
-
return cached;
|
|
15517
|
-
const client = createOpencodeClient({
|
|
15518
|
-
baseUrl: serverUrl,
|
|
15519
|
-
directory,
|
|
15520
|
-
headers: serverAuthHeaders(),
|
|
15521
|
-
fetch: globalThis.fetch
|
|
15522
|
-
});
|
|
15523
|
-
clientCache.set(key, client);
|
|
15524
|
-
return client;
|
|
15525
|
-
}
|
|
15526
|
-
var liveServerWakeAvailableByServerUrl = new Map;
|
|
15527
|
-
var legacyLiveServerWakeAvailable = false;
|
|
15528
|
-
async function probeServerReachable(serverUrl, timeoutMs = 1500) {
|
|
15529
|
-
if (!serverUrl)
|
|
15530
|
-
return false;
|
|
15531
|
-
const normalizedServerUrl = normalizeServerUrl(serverUrl);
|
|
15532
|
-
const controller = new AbortController;
|
|
15533
|
-
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
15534
|
-
let reachable = false;
|
|
15535
|
-
try {
|
|
15536
|
-
const probeUrl = new URL("/session", serverUrl).toString();
|
|
15537
|
-
const res = await globalThis.fetch(probeUrl, {
|
|
15538
|
-
method: "GET",
|
|
15539
|
-
headers: serverAuthHeaders(),
|
|
15540
|
-
signal: controller.signal
|
|
15541
|
-
});
|
|
15542
|
-
reachable = res.ok || res.status === 401 || res.status === 403;
|
|
15543
|
-
} catch {
|
|
15544
|
-
reachable = false;
|
|
15545
|
-
} finally {
|
|
15546
|
-
clearTimeout(timer);
|
|
15547
|
-
liveServerWakeAvailableByServerUrl.set(normalizedServerUrl, reachable);
|
|
15548
|
-
}
|
|
15549
|
-
return reachable;
|
|
15550
|
-
}
|
|
15551
|
-
function setLiveServerWakeAvailable(serverUrlOrAvailable, available) {
|
|
15552
|
-
if (typeof serverUrlOrAvailable === "boolean") {
|
|
15553
|
-
legacyLiveServerWakeAvailable = serverUrlOrAvailable;
|
|
15554
|
-
return;
|
|
15555
|
-
}
|
|
15556
|
-
if (!serverUrlOrAvailable) {
|
|
15557
|
-
legacyLiveServerWakeAvailable = available ?? false;
|
|
15558
|
-
return;
|
|
15559
|
-
}
|
|
15560
|
-
liveServerWakeAvailableByServerUrl.set(normalizeServerUrl(serverUrlOrAvailable), available ?? false);
|
|
15561
|
-
}
|
|
15562
|
-
function useLiveServerWake(serverUrl) {
|
|
15563
|
-
if (!serverUrl)
|
|
15564
|
-
return legacyLiveServerWakeAvailable;
|
|
15565
|
-
return liveServerWakeAvailableByServerUrl.get(normalizeServerUrl(serverUrl)) ?? false;
|
|
15566
|
-
}
|
|
15567
|
-
|
|
15568
15725
|
// src/bg-notifications.ts
|
|
15569
15726
|
function hashReminder(text) {
|
|
15570
15727
|
return createHash3("sha256").update(text).digest("hex").slice(0, 16);
|
|
@@ -15791,15 +15948,15 @@ async function triggerWakeIfPending(drainContext, skipDrain, includeDeferredComp
|
|
|
15791
15948
|
directory: drainContext.directory,
|
|
15792
15949
|
attempt: state.wakeRetryAttempts + 1
|
|
15793
15950
|
});
|
|
15794
|
-
throw new Error("
|
|
15951
|
+
throw new Error("in-process wake client unavailable: input.client absent");
|
|
15795
15952
|
}
|
|
15796
15953
|
return drainContext.client;
|
|
15797
15954
|
};
|
|
15798
|
-
const sendPrompt = async (
|
|
15799
|
-
if (typeof
|
|
15800
|
-
throw new Error(
|
|
15955
|
+
const sendPrompt = async (client2) => {
|
|
15956
|
+
if (typeof client2.session?.promptAsync !== "function") {
|
|
15957
|
+
throw new Error("wake client.session.promptAsync is unavailable");
|
|
15801
15958
|
}
|
|
15802
|
-
const promptContext = await resolvePromptContext(
|
|
15959
|
+
const promptContext = await resolvePromptContext(client2, drainContext.sessionID);
|
|
15803
15960
|
const body = {
|
|
15804
15961
|
noReply: false,
|
|
15805
15962
|
parts: [{ type: "text", text: reminder, synthetic: true }]
|
|
@@ -15822,7 +15979,6 @@ async function triggerWakeIfPending(drainContext, skipDrain, includeDeferredComp
|
|
|
15822
15979
|
directory: drainContext.directory,
|
|
15823
15980
|
reminder_sha256: hashReminder(reminder),
|
|
15824
15981
|
reminder_chars: reminder.length,
|
|
15825
|
-
wake_client_path: clientPath,
|
|
15826
15982
|
prompt_context: promptContext ? {
|
|
15827
15983
|
agent: promptContext.agent,
|
|
15828
15984
|
model: promptContext.model ? {
|
|
@@ -15837,18 +15993,16 @@ async function triggerWakeIfPending(drainContext, skipDrain, includeDeferredComp
|
|
|
15837
15993
|
...wakeMeta
|
|
15838
15994
|
});
|
|
15839
15995
|
try {
|
|
15840
|
-
await
|
|
15996
|
+
await client2.session.promptAsync({
|
|
15841
15997
|
path: { id: drainContext.sessionID },
|
|
15842
15998
|
body
|
|
15843
15999
|
});
|
|
15844
16000
|
} catch (err) {
|
|
15845
|
-
|
|
15846
|
-
logPromptError(drainContext.sessionID, `${LOG_PREFIX} wake promptAsync error`, {
|
|
16001
|
+
sessionWarn(drainContext.sessionID, `${LOG_PREFIX} wake promptAsync error`, {
|
|
15847
16002
|
event: "bash_completion_wake_prompt_async_error",
|
|
15848
16003
|
delivery_id: deliveryID2,
|
|
15849
16004
|
attempt: state.wakeRetryAttempts + 1,
|
|
15850
16005
|
task_ids: taskIDs,
|
|
15851
|
-
wake_client_path: clientPath,
|
|
15852
16006
|
error: err instanceof Error ? err.message : String(err)
|
|
15853
16007
|
});
|
|
15854
16008
|
throw err;
|
|
@@ -15857,38 +16011,12 @@ async function triggerWakeIfPending(drainContext, skipDrain, includeDeferredComp
|
|
|
15857
16011
|
event: "bash_completion_wake_prompt_async_ok",
|
|
15858
16012
|
delivery_id: deliveryID2,
|
|
15859
16013
|
attempt: state.wakeRetryAttempts + 1,
|
|
15860
|
-
task_ids: taskIDs
|
|
15861
|
-
wake_client_path: clientPath
|
|
16014
|
+
task_ids: taskIDs
|
|
15862
16015
|
});
|
|
15863
16016
|
return deliveryID2;
|
|
15864
16017
|
};
|
|
15865
|
-
|
|
15866
|
-
|
|
15867
|
-
const liveClient = getLiveServerClient(drainContext.serverUrl, drainContext.directory);
|
|
15868
|
-
const deliveryID2 = await sendPrompt(liveClient, "live-server");
|
|
15869
|
-
await ackCompletions(drainContext, deliveredCompletions, deliveryID2);
|
|
15870
|
-
return;
|
|
15871
|
-
} catch (err) {
|
|
15872
|
-
setLiveServerWakeAvailable(drainContext.serverUrl, false);
|
|
15873
|
-
sessionDebug(drainContext.sessionID, `${LOG_PREFIX} live-server wake failed; falling back`, {
|
|
15874
|
-
event: "bash_completion_wake_live_server_fallback",
|
|
15875
|
-
task_ids: taskIDs,
|
|
15876
|
-
directory: drainContext.directory,
|
|
15877
|
-
server_url: drainContext.serverUrl,
|
|
15878
|
-
attempt: state.wakeRetryAttempts + 1,
|
|
15879
|
-
error: err instanceof Error ? err.message : String(err)
|
|
15880
|
-
});
|
|
15881
|
-
const fallbackClient2 = getInProcessClient();
|
|
15882
|
-
const deliveryID2 = await sendPrompt(fallbackClient2, "in-process-fallback");
|
|
15883
|
-
state.retryDelayMs = null;
|
|
15884
|
-
state.wakeRetryAttempts = 0;
|
|
15885
|
-
state.wakeHardStopped = false;
|
|
15886
|
-
await ackCompletions(drainContext, deliveredCompletions, deliveryID2);
|
|
15887
|
-
return;
|
|
15888
|
-
}
|
|
15889
|
-
}
|
|
15890
|
-
const fallbackClient = getInProcessClient();
|
|
15891
|
-
const deliveryID = await sendPrompt(fallbackClient, "in-process-fallback");
|
|
16018
|
+
const client = getInProcessClient();
|
|
16019
|
+
const deliveryID = await sendPrompt(client);
|
|
15892
16020
|
await ackCompletions(drainContext, deliveredCompletions, deliveryID);
|
|
15893
16021
|
}, (err, hardStopped) => {
|
|
15894
16022
|
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)}`);
|
|
@@ -16271,7 +16399,7 @@ function formatDuration(completion) {
|
|
|
16271
16399
|
|
|
16272
16400
|
// src/config.ts
|
|
16273
16401
|
import { existsSync as existsSync6, readFileSync as readFileSync4, renameSync as renameSync4, unlinkSync as unlinkSync4, writeFileSync as writeFileSync3 } from "node:fs";
|
|
16274
|
-
import { homedir as
|
|
16402
|
+
import { homedir as homedir8 } from "node:os";
|
|
16275
16403
|
import { join as join10 } from "node:path";
|
|
16276
16404
|
var import_comment_json = __toESM(require_src2(), 1);
|
|
16277
16405
|
|
|
@@ -29922,6 +30050,8 @@ var AftConfigSchema = exports_external.object({
|
|
|
29922
30050
|
restrict_to_project_root: exports_external.boolean().optional(),
|
|
29923
30051
|
search_index: exports_external.boolean().optional(),
|
|
29924
30052
|
semantic_search: exports_external.boolean().optional(),
|
|
30053
|
+
callgraph_store: exports_external.boolean().optional(),
|
|
30054
|
+
callgraph_chunk_size: exports_external.number().optional(),
|
|
29925
30055
|
inspect: InspectConfigSchema.optional(),
|
|
29926
30056
|
bash: BashConfigSchema.optional(),
|
|
29927
30057
|
experimental: ExperimentalConfigSchema.optional(),
|
|
@@ -30000,6 +30130,10 @@ function resolveProjectOverridesForConfigure(config2) {
|
|
|
30000
30130
|
overrides.search_index = config2.search_index;
|
|
30001
30131
|
if (config2.semantic_search !== undefined)
|
|
30002
30132
|
overrides.semantic_search = config2.semantic_search;
|
|
30133
|
+
if (config2.callgraph_store !== undefined)
|
|
30134
|
+
overrides.callgraph_store = config2.callgraph_store;
|
|
30135
|
+
if (config2.callgraph_chunk_size !== undefined)
|
|
30136
|
+
overrides.callgraph_chunk_size = config2.callgraph_chunk_size;
|
|
30003
30137
|
Object.assign(overrides, resolveExperimentalConfigForConfigure(config2));
|
|
30004
30138
|
Object.assign(overrides, resolveLspConfigForConfigure(config2));
|
|
30005
30139
|
if (config2.semantic !== undefined)
|
|
@@ -30274,6 +30408,17 @@ function parseConfigPartially(rawConfig) {
|
|
|
30274
30408
|
}
|
|
30275
30409
|
return partialConfig;
|
|
30276
30410
|
}
|
|
30411
|
+
var configLoadErrors = [];
|
|
30412
|
+
function getConfigLoadErrors() {
|
|
30413
|
+
return configLoadErrors;
|
|
30414
|
+
}
|
|
30415
|
+
function formatConfigParseFailureMessage(configPath, errorMessage) {
|
|
30416
|
+
return `AFT config at ${configPath} failed to parse and was ignored (running on defaults): ${errorMessage}. ` + "Fix the syntax or run `npx @cortexkit/aft doctor`.";
|
|
30417
|
+
}
|
|
30418
|
+
function recordConfigParseFailure(configPath, errorMessage) {
|
|
30419
|
+
configLoadErrors.push({ path: configPath, message: errorMessage });
|
|
30420
|
+
warn2(formatConfigParseFailureMessage(configPath, errorMessage));
|
|
30421
|
+
}
|
|
30277
30422
|
function loadConfigFromPath(configPath) {
|
|
30278
30423
|
try {
|
|
30279
30424
|
if (!existsSync6(configPath)) {
|
|
@@ -30294,6 +30439,7 @@ function loadConfigFromPath(configPath) {
|
|
|
30294
30439
|
} catch (err) {
|
|
30295
30440
|
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
30296
30441
|
error2(`Error loading config from ${configPath}: ${errorMsg}`);
|
|
30442
|
+
recordConfigParseFailure(configPath, errorMsg);
|
|
30297
30443
|
return null;
|
|
30298
30444
|
}
|
|
30299
30445
|
}
|
|
@@ -30417,6 +30563,8 @@ var PROJECT_SAFE_TOP_LEVEL_FIELDS = new Set([
|
|
|
30417
30563
|
"configure_warnings_delivery",
|
|
30418
30564
|
"search_index",
|
|
30419
30565
|
"semantic_search",
|
|
30566
|
+
"callgraph_store",
|
|
30567
|
+
"callgraph_chunk_size",
|
|
30420
30568
|
"inspect",
|
|
30421
30569
|
"experimental",
|
|
30422
30570
|
"bash"
|
|
@@ -30484,10 +30632,11 @@ function getOpenCodeConfigDir() {
|
|
|
30484
30632
|
if (envDir) {
|
|
30485
30633
|
return envDir;
|
|
30486
30634
|
}
|
|
30487
|
-
const xdgConfig = process.env.XDG_CONFIG_HOME || join10(
|
|
30635
|
+
const xdgConfig = process.env.XDG_CONFIG_HOME || join10(homedir8(), ".config");
|
|
30488
30636
|
return join10(xdgConfig, "opencode");
|
|
30489
30637
|
}
|
|
30490
30638
|
function loadAftConfig(projectDirectory) {
|
|
30639
|
+
configLoadErrors = [];
|
|
30491
30640
|
const configDir = getOpenCodeConfigDir();
|
|
30492
30641
|
const userBasePath = join10(configDir, "aft");
|
|
30493
30642
|
migrateAftConfigFile(`${userBasePath}.jsonc`);
|
|
@@ -30520,7 +30669,7 @@ function loadAftConfig(projectDirectory) {
|
|
|
30520
30669
|
|
|
30521
30670
|
// src/notifications.ts
|
|
30522
30671
|
import { existsSync as existsSync7, readFileSync as readFileSync5 } from "node:fs";
|
|
30523
|
-
import { homedir as
|
|
30672
|
+
import { homedir as homedir9, platform } from "node:os";
|
|
30524
30673
|
import { join as join11 } from "node:path";
|
|
30525
30674
|
function isTuiMode() {
|
|
30526
30675
|
return process.env.OPENCODE_CLIENT === "cli";
|
|
@@ -30542,7 +30691,7 @@ var WARNING_MARKER = `${AFT_MARKER} ⚠️`;
|
|
|
30542
30691
|
var STATUS_MARKER = `${AFT_MARKER} ✅`;
|
|
30543
30692
|
function getDesktopStatePath() {
|
|
30544
30693
|
const os3 = platform();
|
|
30545
|
-
const home =
|
|
30694
|
+
const home = homedir9();
|
|
30546
30695
|
if (os3 === "darwin") {
|
|
30547
30696
|
return join11(home, "Library", "Application Support", "ai.opencode.desktop", "opencode.global.dat");
|
|
30548
30697
|
}
|
|
@@ -30821,6 +30970,8 @@ function warningTitle(warning) {
|
|
|
30821
30970
|
return "Checker is not installed";
|
|
30822
30971
|
case "lsp_binary_missing":
|
|
30823
30972
|
return "LSP binary is missing";
|
|
30973
|
+
case "config_parse_failed":
|
|
30974
|
+
return "Config failed to parse";
|
|
30824
30975
|
}
|
|
30825
30976
|
}
|
|
30826
30977
|
function formatConfigureWarningLine(warning) {
|
|
@@ -30942,12 +31093,36 @@ async function cleanupWarnings(opts) {
|
|
|
30942
31093
|
|
|
30943
31094
|
// src/configure-warnings.ts
|
|
30944
31095
|
var pendingEagerWarnings = new Map;
|
|
31096
|
+
var pendingConfigParseWarnings = new Map;
|
|
30945
31097
|
var pendingBySession = new Map;
|
|
30946
31098
|
function isConfigureWarning(value) {
|
|
30947
31099
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
30948
31100
|
return false;
|
|
30949
31101
|
const warning = value;
|
|
30950
|
-
return (warning.kind === "formatter_not_installed" || warning.kind === "checker_not_installed" || warning.kind === "lsp_binary_missing") && typeof warning.hint === "string";
|
|
31102
|
+
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";
|
|
31103
|
+
}
|
|
31104
|
+
function configParseWarningsFromErrors(errors3) {
|
|
31105
|
+
return errors3.map((entry) => ({
|
|
31106
|
+
kind: "config_parse_failed",
|
|
31107
|
+
hint: formatConfigParseFailureMessage(entry.path, entry.message)
|
|
31108
|
+
}));
|
|
31109
|
+
}
|
|
31110
|
+
function enqueueConfigParseWarnings(projectRoot, errors3) {
|
|
31111
|
+
if (!projectRoot || errors3.length === 0)
|
|
31112
|
+
return;
|
|
31113
|
+
const incoming = configParseWarningsFromErrors(errors3);
|
|
31114
|
+
const existing = pendingConfigParseWarnings.get(projectRoot) ?? [];
|
|
31115
|
+
for (const warning of incoming) {
|
|
31116
|
+
if (!existing.some((item) => item.hint === warning.hint)) {
|
|
31117
|
+
existing.push(warning);
|
|
31118
|
+
}
|
|
31119
|
+
}
|
|
31120
|
+
pendingConfigParseWarnings.set(projectRoot, existing);
|
|
31121
|
+
}
|
|
31122
|
+
function drainPendingConfigParseWarnings(projectRoot) {
|
|
31123
|
+
const pending = pendingConfigParseWarnings.get(projectRoot) ?? [];
|
|
31124
|
+
pendingConfigParseWarnings.delete(projectRoot);
|
|
31125
|
+
return pending;
|
|
30951
31126
|
}
|
|
30952
31127
|
function coerceConfigureWarnings(warnings) {
|
|
30953
31128
|
return warnings.filter(isConfigureWarning);
|
|
@@ -30958,7 +31133,10 @@ function drainPendingEagerWarnings(projectRoot) {
|
|
|
30958
31133
|
return pending;
|
|
30959
31134
|
}
|
|
30960
31135
|
function enqueueConfigureWarningsForSession(context) {
|
|
30961
|
-
const validWarnings =
|
|
31136
|
+
const validWarnings = [
|
|
31137
|
+
...drainPendingConfigParseWarnings(context.projectRoot),
|
|
31138
|
+
...coerceConfigureWarnings(context.warnings)
|
|
31139
|
+
];
|
|
30962
31140
|
if (!context.sessionId) {
|
|
30963
31141
|
if (validWarnings.length === 0)
|
|
30964
31142
|
return;
|
|
@@ -31028,27 +31206,27 @@ var import_comment_json3 = __toESM(require_src2(), 1);
|
|
|
31028
31206
|
// src/hooks/auto-update-checker/checker.ts
|
|
31029
31207
|
var import_comment_json2 = __toESM(require_src2(), 1);
|
|
31030
31208
|
import { existsSync as existsSync8, readFileSync as readFileSync6, statSync as statSync4, writeFileSync as writeFileSync4 } from "node:fs";
|
|
31031
|
-
import { homedir as
|
|
31209
|
+
import { homedir as homedir11 } from "node:os";
|
|
31032
31210
|
import { dirname as dirname5, isAbsolute as isAbsolute4, join as join13, resolve as resolve3 } from "node:path";
|
|
31033
31211
|
import { fileURLToPath } from "node:url";
|
|
31034
31212
|
|
|
31035
31213
|
// src/hooks/auto-update-checker/constants.ts
|
|
31036
|
-
import { homedir as
|
|
31214
|
+
import { homedir as homedir10, platform as platform2 } from "node:os";
|
|
31037
31215
|
import { join as join12 } from "node:path";
|
|
31038
31216
|
var PACKAGE_NAME = "@cortexkit/aft-opencode";
|
|
31039
31217
|
var NPM_REGISTRY_URL = "https://registry.npmjs.org";
|
|
31040
31218
|
var NPM_FETCH_TIMEOUT = 1e4;
|
|
31041
31219
|
function getOpenCodeCacheRoot() {
|
|
31042
31220
|
if (platform2() === "win32") {
|
|
31043
|
-
return join12(process.env.LOCALAPPDATA ??
|
|
31221
|
+
return join12(process.env.LOCALAPPDATA ?? homedir10(), "opencode");
|
|
31044
31222
|
}
|
|
31045
|
-
return join12(
|
|
31223
|
+
return join12(homedir10(), ".cache", "opencode");
|
|
31046
31224
|
}
|
|
31047
31225
|
function getOpenCodeConfigRoot() {
|
|
31048
31226
|
if (platform2() === "win32") {
|
|
31049
|
-
return join12(process.env.APPDATA ?? join12(
|
|
31227
|
+
return join12(process.env.APPDATA ?? join12(homedir10(), "AppData", "Roaming"), "opencode");
|
|
31050
31228
|
}
|
|
31051
|
-
return join12(process.env.XDG_CONFIG_HOME ?? join12(
|
|
31229
|
+
return join12(process.env.XDG_CONFIG_HOME ?? join12(homedir10(), ".config"), "opencode");
|
|
31052
31230
|
}
|
|
31053
31231
|
var CACHE_DIR = join12(getOpenCodeCacheRoot(), "packages");
|
|
31054
31232
|
var USER_OPENCODE_CONFIG = join12(getOpenCodeConfigRoot(), "opencode.json");
|
|
@@ -31227,7 +31405,7 @@ function getCachedVersion(spec) {
|
|
|
31227
31405
|
getCurrentRuntimePackageJsonPath(),
|
|
31228
31406
|
spec ? getSpecCachePackageJsonPath(spec) : null,
|
|
31229
31407
|
getSpecCachePackageJsonPath(`${PACKAGE_NAME}@latest`),
|
|
31230
|
-
join13(
|
|
31408
|
+
join13(homedir11(), ".cache", "opencode", "node_modules", PACKAGE_NAME, "package.json")
|
|
31231
31409
|
].filter(isString);
|
|
31232
31410
|
for (const packageJsonPath of candidates) {
|
|
31233
31411
|
try {
|
|
@@ -31730,7 +31908,7 @@ import {
|
|
|
31730
31908
|
unlinkSync as unlinkSync5,
|
|
31731
31909
|
writeFileSync as writeFileSync7
|
|
31732
31910
|
} from "node:fs";
|
|
31733
|
-
import { homedir as
|
|
31911
|
+
import { homedir as homedir12 } from "node:os";
|
|
31734
31912
|
import { join as join15 } from "node:path";
|
|
31735
31913
|
function aftCacheBase() {
|
|
31736
31914
|
const override = process.env.AFT_CACHE_DIR;
|
|
@@ -31738,10 +31916,10 @@ function aftCacheBase() {
|
|
|
31738
31916
|
return override;
|
|
31739
31917
|
if (process.platform === "win32") {
|
|
31740
31918
|
const localAppData = process.env.LOCALAPPDATA || process.env.APPDATA;
|
|
31741
|
-
const base2 = localAppData || join15(
|
|
31919
|
+
const base2 = localAppData || join15(homedir12(), "AppData", "Local");
|
|
31742
31920
|
return join15(base2, "aft");
|
|
31743
31921
|
}
|
|
31744
|
-
const base = process.env.XDG_CACHE_HOME || join15(
|
|
31922
|
+
const base = process.env.XDG_CACHE_HOME || join15(homedir12(), ".cache");
|
|
31745
31923
|
return join15(base, "aft");
|
|
31746
31924
|
}
|
|
31747
31925
|
function lspCacheRoot() {
|
|
@@ -33416,6 +33594,34 @@ function normalizeToolMap(tools) {
|
|
|
33416
33594
|
}
|
|
33417
33595
|
return tools;
|
|
33418
33596
|
}
|
|
33597
|
+
// src/shared/ignored-message.ts
|
|
33598
|
+
async function sendIgnoredMessage2(client, sessionID, text) {
|
|
33599
|
+
const typedClient = client;
|
|
33600
|
+
let agent;
|
|
33601
|
+
try {
|
|
33602
|
+
const ctx = await resolvePromptContext(client, sessionID);
|
|
33603
|
+
agent = ctx?.agent;
|
|
33604
|
+
} catch {
|
|
33605
|
+
agent = undefined;
|
|
33606
|
+
}
|
|
33607
|
+
const body = {
|
|
33608
|
+
noReply: true,
|
|
33609
|
+
parts: [{ type: "text", text, ignored: true }]
|
|
33610
|
+
};
|
|
33611
|
+
if (agent)
|
|
33612
|
+
body.agent = agent;
|
|
33613
|
+
const promptInput = { path: { id: sessionID }, body };
|
|
33614
|
+
if (typeof typedClient.session?.prompt === "function") {
|
|
33615
|
+
await Promise.resolve(typedClient.session.prompt(promptInput));
|
|
33616
|
+
return;
|
|
33617
|
+
}
|
|
33618
|
+
if (typeof typedClient.session?.promptAsync === "function") {
|
|
33619
|
+
await typedClient.session.promptAsync(promptInput);
|
|
33620
|
+
return;
|
|
33621
|
+
}
|
|
33622
|
+
throw new Error("[aft-plugin] client.session.prompt is unavailable");
|
|
33623
|
+
}
|
|
33624
|
+
|
|
33419
33625
|
// src/shared/pty-cache.ts
|
|
33420
33626
|
var import_headless = __toESM(require_xterm_headless(), 1);
|
|
33421
33627
|
import * as fs2 from "node:fs/promises";
|
|
@@ -33824,7 +34030,7 @@ async function verifySessionDirectory(client, sessionId) {
|
|
|
33824
34030
|
}
|
|
33825
34031
|
|
|
33826
34032
|
// src/shared/status.ts
|
|
33827
|
-
function
|
|
34033
|
+
function asRecord2(value) {
|
|
33828
34034
|
return typeof value === "object" && value !== null ? value : {};
|
|
33829
34035
|
}
|
|
33830
34036
|
function readString(value, fallback = "") {
|
|
@@ -33843,7 +34049,7 @@ function readOptionalNumber(value) {
|
|
|
33843
34049
|
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
33844
34050
|
}
|
|
33845
34051
|
function readCompressionAggregate(value) {
|
|
33846
|
-
const aggregate =
|
|
34052
|
+
const aggregate = asRecord2(value);
|
|
33847
34053
|
return {
|
|
33848
34054
|
events: readNumber(aggregate.events),
|
|
33849
34055
|
original_tokens: readNumber(aggregate.original_tokens),
|
|
@@ -33854,7 +34060,7 @@ function readCompressionAggregate(value) {
|
|
|
33854
34060
|
function readCompression(value) {
|
|
33855
34061
|
if (typeof value !== "object" || value === null)
|
|
33856
34062
|
return;
|
|
33857
|
-
const compression =
|
|
34063
|
+
const compression = asRecord2(value);
|
|
33858
34064
|
return {
|
|
33859
34065
|
project: readCompressionAggregate(compression.project),
|
|
33860
34066
|
session: readCompressionAggregate(compression.session)
|
|
@@ -33863,7 +34069,7 @@ function readCompression(value) {
|
|
|
33863
34069
|
function readStatusBar(value) {
|
|
33864
34070
|
if (typeof value !== "object" || value === null)
|
|
33865
34071
|
return;
|
|
33866
|
-
const bar =
|
|
34072
|
+
const bar = asRecord2(value);
|
|
33867
34073
|
return {
|
|
33868
34074
|
errors: readNumber(bar.errors),
|
|
33869
34075
|
warnings: readNumber(bar.warnings),
|
|
@@ -33907,16 +34113,16 @@ function formatBytes(bytes) {
|
|
|
33907
34113
|
return `${value.toFixed(decimals)} ${units[unitIndex]}`;
|
|
33908
34114
|
}
|
|
33909
34115
|
function coerceAftStatus(response) {
|
|
33910
|
-
const features =
|
|
33911
|
-
const searchIndex =
|
|
33912
|
-
const semanticIndex =
|
|
34116
|
+
const features = asRecord2(response.features);
|
|
34117
|
+
const searchIndex = asRecord2(response.search_index);
|
|
34118
|
+
const semanticIndex = asRecord2(response.semantic_index);
|
|
33913
34119
|
const semanticConfig = {
|
|
33914
|
-
...
|
|
33915
|
-
...
|
|
34120
|
+
...asRecord2(response.semantic),
|
|
34121
|
+
...asRecord2(response.semantic_config)
|
|
33916
34122
|
};
|
|
33917
|
-
const disk =
|
|
33918
|
-
const symbolCache =
|
|
33919
|
-
const session =
|
|
34123
|
+
const disk = asRecord2(response.disk);
|
|
34124
|
+
const symbolCache = asRecord2(response.symbol_cache);
|
|
34125
|
+
const session = asRecord2(response.session);
|
|
33920
34126
|
return {
|
|
33921
34127
|
version: readString(response.version, "unknown"),
|
|
33922
34128
|
project_root: readNullableString(response.project_root),
|
|
@@ -34038,7 +34244,7 @@ import { existsSync as existsSync15, mkdirSync as mkdirSync11, readFileSync as r
|
|
|
34038
34244
|
import { dirname as dirname10, join as join22 } from "node:path";
|
|
34039
34245
|
|
|
34040
34246
|
// src/shared/opencode-config-dir.ts
|
|
34041
|
-
import { homedir as
|
|
34247
|
+
import { homedir as homedir13 } from "node:os";
|
|
34042
34248
|
import { join as join21, resolve as resolve5 } from "node:path";
|
|
34043
34249
|
function getCliConfigDir() {
|
|
34044
34250
|
const envConfigDir = process.env.OPENCODE_CONFIG_DIR?.trim();
|
|
@@ -34046,9 +34252,9 @@ function getCliConfigDir() {
|
|
|
34046
34252
|
return resolve5(envConfigDir);
|
|
34047
34253
|
}
|
|
34048
34254
|
if (process.platform === "win32") {
|
|
34049
|
-
return join21(
|
|
34255
|
+
return join21(homedir13(), ".config", "opencode");
|
|
34050
34256
|
}
|
|
34051
|
-
return join21(process.env.XDG_CONFIG_HOME || join21(
|
|
34257
|
+
return join21(process.env.XDG_CONFIG_HOME || join21(homedir13(), ".config"), "opencode");
|
|
34052
34258
|
}
|
|
34053
34259
|
function getOpenCodeConfigDir2(_options) {
|
|
34054
34260
|
return getCliConfigDir();
|
|
@@ -34356,6 +34562,27 @@ async function callBashBridge(ctx, runtime, command, params = {}, options) {
|
|
|
34356
34562
|
import * as fs4 from "node:fs";
|
|
34357
34563
|
import * as path4 from "node:path";
|
|
34358
34564
|
var UNSUPPORTED_ASK_HOST = "AFT requires OpenCode 1.15.5 or newer for permission asks; please upgrade OpenCode";
|
|
34565
|
+
var RESTRICT_NOTICE_THROTTLE_MS = 5 * 60 * 1000;
|
|
34566
|
+
var restrictNoticeLastSentAt = new Map;
|
|
34567
|
+
function restrictNoticeWording(target) {
|
|
34568
|
+
return `AFT blocked access to a path outside the project:
|
|
34569
|
+
${target}
|
|
34570
|
+
` + "`restrict_to_project_root` is enabled (full isolation), so AFT does not access paths " + "outside the project root. To allow external paths, set `restrict_to_project_root: false` " + "in your aft.jsonc.";
|
|
34571
|
+
}
|
|
34572
|
+
function notifyRestrictBlocked(ctx, context, target) {
|
|
34573
|
+
const sessionID = context.sessionID;
|
|
34574
|
+
if (!sessionID)
|
|
34575
|
+
return;
|
|
34576
|
+
const now = Date.now();
|
|
34577
|
+
const last = restrictNoticeLastSentAt.get(sessionID);
|
|
34578
|
+
if (last !== undefined && now - last < RESTRICT_NOTICE_THROTTLE_MS)
|
|
34579
|
+
return;
|
|
34580
|
+
restrictNoticeLastSentAt.set(sessionID, now);
|
|
34581
|
+
sendIgnoredMessage2(ctx.client, sessionID, restrictNoticeWording(target)).catch(() => {});
|
|
34582
|
+
}
|
|
34583
|
+
function restrictDenialMessage(target) {
|
|
34584
|
+
return `Blocked: '${target}' is outside the project root and restrict_to_project_root is enabled ` + "(AFT full isolation). Not overridable per-call; set restrict_to_project_root: false in " + "aft.jsonc to allow external paths.";
|
|
34585
|
+
}
|
|
34359
34586
|
async function runAsk(maybe) {
|
|
34360
34587
|
await maybe;
|
|
34361
34588
|
}
|
|
@@ -34435,11 +34662,9 @@ function normalizePathPattern(p) {
|
|
|
34435
34662
|
const dir = /^[A-Za-z]:$/.test(match[1]) ? `${match[1]}\\` : match[1];
|
|
34436
34663
|
return path4.join(normalizePath(dir), match[2]);
|
|
34437
34664
|
}
|
|
34438
|
-
async function assertExternalDirectoryPermission(context, target, options) {
|
|
34665
|
+
async function assertExternalDirectoryPermission(ctx, context, target, options) {
|
|
34439
34666
|
if (!target)
|
|
34440
34667
|
return;
|
|
34441
|
-
if (typeof context.ask !== "function")
|
|
34442
|
-
return UNSUPPORTED_ASK_HOST;
|
|
34443
34668
|
const resolved = resolveAbsolutePath(context, target);
|
|
34444
34669
|
const absoluteTarget = normalizePath(resolved);
|
|
34445
34670
|
const root = projectRootFor(context);
|
|
@@ -34451,6 +34676,12 @@ async function assertExternalDirectoryPermission(context, target, options) {
|
|
|
34451
34676
|
if (worktree && worktree !== "/" && worktree !== directory && containsPath(worktree, absoluteTarget)) {
|
|
34452
34677
|
return;
|
|
34453
34678
|
}
|
|
34679
|
+
if (ctx.config.restrict_to_project_root === true) {
|
|
34680
|
+
notifyRestrictBlocked(ctx, context, absoluteTarget);
|
|
34681
|
+
return restrictDenialMessage(absoluteTarget);
|
|
34682
|
+
}
|
|
34683
|
+
if (typeof context.ask !== "function")
|
|
34684
|
+
return UNSUPPORTED_ASK_HOST;
|
|
34454
34685
|
const kind = options?.kind ?? "file";
|
|
34455
34686
|
const parentDir = kind === "directory" ? absoluteTarget : path4.dirname(absoluteTarget);
|
|
34456
34687
|
const rawGlob = process.platform === "win32" ? normalizePathPattern(path4.join(parentDir, "*")) : path4.join(parentDir, "*").replaceAll("\\", "/");
|
|
@@ -34547,12 +34778,12 @@ async function resolveAstPaths(ctx, context, paths) {
|
|
|
34547
34778
|
const resolved = await Promise.all(paths.filter((p) => typeof p === "string" && p.length > 0).map((p) => resolvePathArg(ctx, context, p)));
|
|
34548
34779
|
return resolved.length > 0 ? resolved : undefined;
|
|
34549
34780
|
}
|
|
34550
|
-
async function checkAstPathsPermission(context, paths) {
|
|
34781
|
+
async function checkAstPathsPermission(ctx, context, paths) {
|
|
34551
34782
|
if (paths === undefined)
|
|
34552
34783
|
return;
|
|
34553
34784
|
const uniquePaths = Array.from(new Set(paths));
|
|
34554
34785
|
for (const p of uniquePaths) {
|
|
34555
|
-
const denial = await assertExternalDirectoryPermission(context, p, { kind: "directory" });
|
|
34786
|
+
const denial = await assertExternalDirectoryPermission(ctx, context, p, { kind: "directory" });
|
|
34556
34787
|
if (denial)
|
|
34557
34788
|
return denial;
|
|
34558
34789
|
}
|
|
@@ -34586,7 +34817,7 @@ function astTools(ctx) {
|
|
|
34586
34817
|
},
|
|
34587
34818
|
execute: async (args, context) => {
|
|
34588
34819
|
const paths = await resolveAstPaths(ctx, context, args.paths);
|
|
34589
|
-
const externalDenied = await checkAstPathsPermission(context, paths);
|
|
34820
|
+
const externalDenied = await checkAstPathsPermission(ctx, context, paths);
|
|
34590
34821
|
if (externalDenied)
|
|
34591
34822
|
return permissionDeniedResponse(externalDenied);
|
|
34592
34823
|
const params = {
|
|
@@ -34684,13 +34915,13 @@ ${hint}`;
|
|
|
34684
34915
|
execute: async (args, context) => {
|
|
34685
34916
|
const isDryRun = args.dryRun === true;
|
|
34686
34917
|
const paths = await resolveAstPaths(ctx, context, args.paths);
|
|
34687
|
-
const externalDenied = await checkAstPathsPermission(context, paths);
|
|
34918
|
+
const externalDenied = await checkAstPathsPermission(ctx, context, paths);
|
|
34688
34919
|
if (externalDenied)
|
|
34689
34920
|
return permissionDeniedResponse(externalDenied);
|
|
34690
34921
|
if (!isDryRun) {
|
|
34691
34922
|
if (!Array.isArray(args.paths)) {
|
|
34692
34923
|
const targetPath = await resolvePathArg(ctx, context, ".");
|
|
34693
|
-
const denial = await assertExternalDirectoryPermission(context, targetPath, {
|
|
34924
|
+
const denial = await assertExternalDirectoryPermission(ctx, context, targetPath, {
|
|
34694
34925
|
kind: "directory"
|
|
34695
34926
|
});
|
|
34696
34927
|
if (denial)
|
|
@@ -34888,18 +35119,43 @@ function resolveForegroundWaitMs(configured) {
|
|
|
34888
35119
|
function bashToolDescription(aftSearchRegistered, compressionOn, backgroundOn) {
|
|
34889
35120
|
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";
|
|
34890
35121
|
const compression = compressionOn ? " Output is compressed by default; pass compressed: false for raw output." : "";
|
|
34891
|
-
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
|
|
34892
|
-
return `Execute shell commands.${compression}${tasks}
|
|
35122
|
+
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).";
|
|
35123
|
+
return `Execute shell commands.${compression}${tasks}
|
|
34893
35124
|
|
|
34894
35125
|
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}.`;
|
|
34895
35126
|
}
|
|
35127
|
+
function pushUnique(target, values) {
|
|
35128
|
+
for (const value of values) {
|
|
35129
|
+
if (!target.includes(value))
|
|
35130
|
+
target.push(value);
|
|
35131
|
+
}
|
|
35132
|
+
}
|
|
35133
|
+
function groupBashPermissionAsks(asks) {
|
|
35134
|
+
const grouped = [];
|
|
35135
|
+
let bashAsk;
|
|
35136
|
+
for (const ask of asks) {
|
|
35137
|
+
if (ask.kind === "bash") {
|
|
35138
|
+
if (!bashAsk) {
|
|
35139
|
+
bashAsk = { kind: "bash", patterns: [], always: [] };
|
|
35140
|
+
grouped.push(bashAsk);
|
|
35141
|
+
}
|
|
35142
|
+
pushUnique(bashAsk.patterns, ask.patterns);
|
|
35143
|
+
pushUnique(bashAsk.always, ask.always);
|
|
35144
|
+
continue;
|
|
35145
|
+
}
|
|
35146
|
+
grouped.push(ask);
|
|
35147
|
+
}
|
|
35148
|
+
return grouped;
|
|
35149
|
+
}
|
|
35150
|
+
function permissionsGrantedForRetry(asks) {
|
|
35151
|
+
return asks.flatMap((ask) => ask.always.length > 0 ? ask.always : ask.patterns);
|
|
35152
|
+
}
|
|
34896
35153
|
async function withPermissionLoop(ctx, runtime, params, bridgeCall, options) {
|
|
34897
35154
|
const first = await bridgeCall(ctx, runtime, "bash", params, options);
|
|
34898
35155
|
if (first.success !== false || first.code !== "permission_required")
|
|
34899
35156
|
return first;
|
|
34900
35157
|
const asks = Array.isArray(first.asks) ? first.asks : [];
|
|
34901
|
-
const
|
|
34902
|
-
for (const ask of asks) {
|
|
35158
|
+
for (const ask of groupBashPermissionAsks(asks)) {
|
|
34903
35159
|
const permission = ask.kind === "external_directory" ? "external_directory" : "bash";
|
|
34904
35160
|
await runAsk(runtime.ask({
|
|
34905
35161
|
permission,
|
|
@@ -34907,54 +35163,60 @@ async function withPermissionLoop(ctx, runtime, params, bridgeCall, options) {
|
|
|
34907
35163
|
always: ask.always,
|
|
34908
35164
|
metadata: {}
|
|
34909
35165
|
}));
|
|
34910
|
-
permissionsGranted.push(...ask.always.length > 0 ? ask.always : ask.patterns);
|
|
34911
35166
|
}
|
|
34912
|
-
const second = await bridgeCall(ctx, runtime, "bash", { ...params, permissions_granted:
|
|
35167
|
+
const second = await bridgeCall(ctx, runtime, "bash", { ...params, permissions_granted: permissionsGrantedForRetry(asks) }, options);
|
|
34913
35168
|
if (second.success === false && second.code === "permission_required") {
|
|
34914
35169
|
throw new Error("bash permission retry failed");
|
|
34915
35170
|
}
|
|
34916
35171
|
return second;
|
|
34917
35172
|
}
|
|
34918
35173
|
function createBashTool(ctx, aftSearchRegisteredOverride) {
|
|
35174
|
+
const initialBashCfg = resolveBashConfig(ctx.config);
|
|
35175
|
+
const backgroundFlagArg = initialBashCfg.background ? {
|
|
35176
|
+
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.")
|
|
35177
|
+
} : {};
|
|
35178
|
+
const ptyArgs = initialBashCfg.background ? {
|
|
35179
|
+
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.'),
|
|
35180
|
+
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."),
|
|
35181
|
+
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.")
|
|
35182
|
+
} : {};
|
|
35183
|
+
const args = {
|
|
35184
|
+
command: z4.string().describe("Shell command to execute. Supports pipes, redirection, and normal shell syntax."),
|
|
35185
|
+
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."),
|
|
35186
|
+
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."),
|
|
35187
|
+
description: z4.string().optional().describe("Short 5-10 word human-readable summary shown in OpenCode UI metadata instead of raw shell syntax."),
|
|
35188
|
+
...backgroundFlagArg,
|
|
35189
|
+
compressed: z4.boolean().optional().describe("When true or omitted, return compressed output with noisy terminal control sequences reduced. Set to false for raw output."),
|
|
35190
|
+
...ptyArgs
|
|
35191
|
+
};
|
|
34919
35192
|
return {
|
|
34920
|
-
description: (
|
|
34921
|
-
|
|
34922
|
-
|
|
34923
|
-
})(),
|
|
34924
|
-
args: {
|
|
34925
|
-
command: z4.string().describe("Shell command to execute. Supports pipes, redirection, and normal shell syntax."),
|
|
34926
|
-
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."),
|
|
34927
|
-
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."),
|
|
34928
|
-
description: z4.string().optional().describe("Short 5-10 word human-readable summary shown in OpenCode UI metadata instead of raw shell syntax."),
|
|
34929
|
-
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."),
|
|
34930
|
-
compressed: z4.boolean().optional().describe("When true or omitted, return compressed output with noisy terminal control sequences reduced. Set to false for raw output."),
|
|
34931
|
-
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.'),
|
|
34932
|
-
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."),
|
|
34933
|
-
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.")
|
|
34934
|
-
},
|
|
34935
|
-
execute: async (args, context) => {
|
|
35193
|
+
description: bashToolDescription(false, initialBashCfg.compress, initialBashCfg.background),
|
|
35194
|
+
args,
|
|
35195
|
+
execute: async (args2, context) => {
|
|
34936
35196
|
const bashCfg = resolveBashConfig(ctx.config);
|
|
34937
35197
|
const ctxAftSearchRegistered = ctx.aftSearchRegistered === true;
|
|
34938
35198
|
const aftSearchRegistered = aftSearchRegisteredOverride ?? ctxAftSearchRegistered;
|
|
34939
35199
|
let accumulatedOutput = "";
|
|
34940
|
-
const description =
|
|
35200
|
+
const description = args2.description;
|
|
34941
35201
|
const metadata = context.metadata;
|
|
34942
|
-
const rawCommand =
|
|
34943
|
-
const compressionEnabled = bashCfg.compress &&
|
|
35202
|
+
const rawCommand = args2.command;
|
|
35203
|
+
const compressionEnabled = bashCfg.compress && args2.compressed !== false;
|
|
34944
35204
|
const pipeStrip = maybeStripCompressorPipe(rawCommand, compressionEnabled);
|
|
34945
35205
|
const command = pipeStrip.command;
|
|
34946
|
-
const cwd =
|
|
35206
|
+
const cwd = args2.workdir ?? context.directory;
|
|
34947
35207
|
const isSubagent = await resolveIsSubagent(ctx.client, context.sessionID, context.directory);
|
|
34948
|
-
const
|
|
34949
|
-
const
|
|
35208
|
+
const backgroundDisabled = !bashCfg.background;
|
|
35209
|
+
const requestedPty = !backgroundDisabled && args2.pty === true;
|
|
35210
|
+
const requestedBackground = !backgroundDisabled && (args2.background === true || requestedPty);
|
|
34950
35211
|
if (requestedPty && isSubagent) {
|
|
34951
35212
|
throw new Error("PTY mode is not available in subagent sessions; subagents cannot drive interactive terminals.");
|
|
34952
35213
|
}
|
|
34953
35214
|
const allowSubagentBg = bashCfg.subagent_background;
|
|
34954
35215
|
const subagentForcedForeground = isSubagent && !allowSubagentBg;
|
|
34955
|
-
const
|
|
34956
|
-
const
|
|
34957
|
-
const
|
|
35216
|
+
const blockToCompletion = subagentForcedForeground || backgroundDisabled;
|
|
35217
|
+
const effectiveBackground = blockToCompletion ? false : requestedBackground;
|
|
35218
|
+
const rawTimeout = args2.timeout;
|
|
35219
|
+
const effectiveTimeout = effectiveBackground || backgroundDisabled ? rawTimeout : resolveBashKillTimeout(rawTimeout, bashCfg.foreground_wait_window_ms);
|
|
34958
35220
|
if (subagentForcedForeground && requestedBackground) {
|
|
34959
35221
|
sessionLog(context.sessionID, "[bash] subagent + background:true → converting to foreground (subagent would lose task_id)");
|
|
34960
35222
|
}
|
|
@@ -34962,15 +35224,15 @@ function createBashTool(ctx, aftSearchRegisteredOverride) {
|
|
|
34962
35224
|
const data = await withPermissionLoop(ctx, context, {
|
|
34963
35225
|
command,
|
|
34964
35226
|
timeout: effectiveTimeout,
|
|
34965
|
-
workdir:
|
|
35227
|
+
workdir: args2.workdir,
|
|
34966
35228
|
env: shellEnv?.env ?? {},
|
|
34967
35229
|
description,
|
|
34968
35230
|
background: effectiveBackground,
|
|
34969
35231
|
notify_on_completion: effectiveBackground,
|
|
34970
|
-
compressed:
|
|
35232
|
+
compressed: args2.compressed,
|
|
34971
35233
|
pty: requestedPty,
|
|
34972
|
-
pty_rows:
|
|
34973
|
-
pty_cols:
|
|
35234
|
+
pty_rows: args2.ptyRows,
|
|
35235
|
+
pty_cols: args2.ptyCols,
|
|
34974
35236
|
permissions_requested: true
|
|
34975
35237
|
}, callBashBridge, {
|
|
34976
35238
|
onProgress: ({ text }) => {
|
|
@@ -34995,7 +35257,7 @@ function createBashTool(ctx, aftSearchRegisteredOverride) {
|
|
|
34995
35257
|
return { output: startedLine, title: uiTitle, metadata: metadataPayload2 };
|
|
34996
35258
|
}
|
|
34997
35259
|
const foregroundWaitMs = resolveForegroundWaitMs(bashCfg.foreground_wait_window_ms);
|
|
34998
|
-
const waitTimeoutMs =
|
|
35260
|
+
const waitTimeoutMs = blockToCompletion ? effectiveTimeout ?? DEFAULT_HARD_TIMEOUT_MS : effectiveTimeout !== undefined ? Math.min(effectiveTimeout, foregroundWaitMs) : foregroundWaitMs;
|
|
34999
35261
|
const startedAt = Date.now();
|
|
35000
35262
|
while (true) {
|
|
35001
35263
|
const status = await callBashBridge(ctx, context, "bash_status", { task_id: taskId });
|
|
@@ -35009,7 +35271,7 @@ function createBashTool(ctx, aftSearchRegisteredOverride) {
|
|
|
35009
35271
|
return { output: rendered2, title: uiTitle, metadata: metadataPayload2 };
|
|
35010
35272
|
}
|
|
35011
35273
|
if (Date.now() - startedAt >= waitTimeoutMs) {
|
|
35012
|
-
if (
|
|
35274
|
+
if (blockToCompletion) {
|
|
35013
35275
|
await sleep(FOREGROUND_POLL_INTERVAL_MS);
|
|
35014
35276
|
continue;
|
|
35015
35277
|
}
|
|
@@ -35069,7 +35331,7 @@ function createBashTool(ctx, aftSearchRegisteredOverride) {
|
|
|
35069
35331
|
}
|
|
35070
35332
|
function createBashStatusTool(ctx) {
|
|
35071
35333
|
return {
|
|
35072
|
-
description: "Read-only snapshot of a background or PTY bash task's current state and output. Returns immediately. Never waits.
|
|
35334
|
+
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.",
|
|
35073
35335
|
args: {
|
|
35074
35336
|
taskId: z4.string().describe("Background task ID returned by bash({ background: true }), e.g. bash-6b454047."),
|
|
35075
35337
|
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.")
|
|
@@ -35221,7 +35483,7 @@ function conflictTools(ctx) {
|
|
|
35221
35483
|
const expanded = expandTilde2(String(args.path));
|
|
35222
35484
|
const projectRoot = await resolveProjectRoot(ctx, context);
|
|
35223
35485
|
const resolved = resolvePathFromProjectRoot(projectRoot, expanded);
|
|
35224
|
-
const denied = await assertExternalDirectoryPermission(context, resolved, {
|
|
35486
|
+
const denied = await assertExternalDirectoryPermission(ctx, context, resolved, {
|
|
35225
35487
|
kind: "directory"
|
|
35226
35488
|
});
|
|
35227
35489
|
if (denied)
|
|
@@ -35547,16 +35809,19 @@ import { tool as tool6 } from "@opencode-ai/plugin";
|
|
|
35547
35809
|
var z6 = tool6.schema;
|
|
35548
35810
|
var BASH_WAIT_POLL_INTERVAL_MS = 100;
|
|
35549
35811
|
var DEFAULT_BASH_STATUS_WAIT_TIMEOUT_MS = 30000;
|
|
35550
|
-
var MAX_BASH_STATUS_WAIT_TIMEOUT_MS =
|
|
35812
|
+
var MAX_BASH_STATUS_WAIT_TIMEOUT_MS = 30 * 60 * 1000;
|
|
35551
35813
|
var REGEX_WAIT_SCAN_WINDOW_BYTES = 64 * 1024;
|
|
35814
|
+
function unavailableSnapshot() {
|
|
35815
|
+
return { status: "unknown" };
|
|
35816
|
+
}
|
|
35552
35817
|
function createBashWatchTool(ctx) {
|
|
35553
35818
|
return {
|
|
35554
|
-
description: "Watch a background bash task.
|
|
35819
|
+
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.",
|
|
35555
35820
|
args: {
|
|
35556
35821
|
taskId: z6.string().describe("Background task ID returned by bash({ background: true })."),
|
|
35557
35822
|
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."),
|
|
35558
35823
|
background: z6.boolean().optional().describe("When true, register an async watch and return immediately. Defaults to false (sync wait)."),
|
|
35559
|
-
timeoutMs: optionalInt(1, MAX_BASH_STATUS_WAIT_TIMEOUT_MS).describe("Sync-only timeout in milliseconds. Default 30000, max
|
|
35824
|
+
timeoutMs: optionalInt(1, MAX_BASH_STATUS_WAIT_TIMEOUT_MS).describe("Sync-only timeout in milliseconds. Default 30000, max 1800000 (30 min)."),
|
|
35560
35825
|
once: z6.boolean().optional().describe("Async-only. Defaults true; false keeps the watch sticky until task exit.")
|
|
35561
35826
|
},
|
|
35562
35827
|
execute: async (args, context) => {
|
|
@@ -35653,6 +35918,9 @@ Waited ${waited.elapsed_ms}ms; matched ${JSON.stringify(waited.match ?? "")} at
|
|
|
35653
35918
|
} else if (waited.reason === "timeout") {
|
|
35654
35919
|
text += `
|
|
35655
35920
|
Waited ${waited.elapsed_ms}ms; timeout reached without match.`;
|
|
35921
|
+
} else if (waited.reason === "unavailable") {
|
|
35922
|
+
text += `
|
|
35923
|
+
Waited ${waited.elapsed_ms}ms; the bridge stayed busy and status couldn't be read. The task may still be running — check with bash_status({ taskId }).`;
|
|
35656
35924
|
} else {
|
|
35657
35925
|
const stat = String(data.status ?? "unknown");
|
|
35658
35926
|
const e = typeof data.exit_code === "number" ? `, exit ${data.exit_code}` : "";
|
|
@@ -35686,9 +35954,31 @@ async function waitForBashStatus(ctx, runtime, taskId, outputMode, waitFor, effe
|
|
|
35686
35954
|
clearSyncWatchAbort(runtime.sessionID);
|
|
35687
35955
|
markTaskWaiting(runtime.sessionID, taskId);
|
|
35688
35956
|
let sawTerminal = false;
|
|
35957
|
+
let lastData;
|
|
35689
35958
|
try {
|
|
35690
35959
|
while (true) {
|
|
35691
|
-
|
|
35960
|
+
let data;
|
|
35961
|
+
try {
|
|
35962
|
+
data = await bashStatusSnapshot2(ctx, runtime, taskId, outputMode, bridgeOptions);
|
|
35963
|
+
} catch (err) {
|
|
35964
|
+
if (!isBridgeTransportTimeout(err))
|
|
35965
|
+
throw err;
|
|
35966
|
+
if (isSyncWatchAborted(runtime.sessionID)) {
|
|
35967
|
+
return withWaited(lastData ?? unavailableSnapshot(), {
|
|
35968
|
+
reason: "user_message",
|
|
35969
|
+
elapsed_ms: Date.now() - startedAt
|
|
35970
|
+
});
|
|
35971
|
+
}
|
|
35972
|
+
if (Date.now() >= deadline) {
|
|
35973
|
+
return withWaited(lastData ?? unavailableSnapshot(), {
|
|
35974
|
+
reason: "unavailable",
|
|
35975
|
+
elapsed_ms: Date.now() - startedAt
|
|
35976
|
+
});
|
|
35977
|
+
}
|
|
35978
|
+
await sleep(Math.min(BASH_WAIT_POLL_INTERVAL_MS, Math.max(0, deadline - Date.now())));
|
|
35979
|
+
continue;
|
|
35980
|
+
}
|
|
35981
|
+
lastData = data;
|
|
35692
35982
|
const terminal = isTerminalStatus(data.status);
|
|
35693
35983
|
if (waitFor) {
|
|
35694
35984
|
const scan = await readNewTaskOutput(runtime, taskId, data, spillCursor);
|
|
@@ -36222,7 +36512,7 @@ function createReadTool(ctx) {
|
|
|
36222
36512
|
const projectRoot = await resolveProjectRoot(ctx, context);
|
|
36223
36513
|
const filePath = resolvePathFromProjectRoot(projectRoot, file2);
|
|
36224
36514
|
{
|
|
36225
|
-
const denial = await assertExternalDirectoryPermission(context, filePath);
|
|
36515
|
+
const denial = await assertExternalDirectoryPermission(ctx, context, filePath);
|
|
36226
36516
|
if (denial)
|
|
36227
36517
|
return permissionDeniedResponse(denial);
|
|
36228
36518
|
}
|
|
@@ -36333,7 +36623,7 @@ function createWriteTool(ctx, editToolName = "edit") {
|
|
|
36333
36623
|
const filePath = resolvePathFromProjectRoot(projectRoot, file2);
|
|
36334
36624
|
const relPath = path5.relative(projectRoot, filePath);
|
|
36335
36625
|
{
|
|
36336
|
-
const denial2 = await assertExternalDirectoryPermission(context, filePath);
|
|
36626
|
+
const denial2 = await assertExternalDirectoryPermission(ctx, context, filePath);
|
|
36337
36627
|
if (denial2)
|
|
36338
36628
|
return permissionDeniedResponse(denial2);
|
|
36339
36629
|
}
|
|
@@ -36484,7 +36774,7 @@ function createEditTool(ctx, writeToolName = "write") {
|
|
|
36484
36774
|
const filePath = resolvePathFromProjectRoot(projectRoot, file2);
|
|
36485
36775
|
const relPath = path5.relative(projectRoot, filePath);
|
|
36486
36776
|
{
|
|
36487
|
-
const denial2 = await assertExternalDirectoryPermission(context, filePath);
|
|
36777
|
+
const denial2 = await assertExternalDirectoryPermission(ctx, context, filePath);
|
|
36488
36778
|
if (denial2)
|
|
36489
36779
|
return permissionDeniedResponse(denial2);
|
|
36490
36780
|
}
|
|
@@ -36720,7 +37010,7 @@ function createApplyPatchTool(ctx) {
|
|
|
36720
37010
|
if (asked.has(filePath))
|
|
36721
37011
|
continue;
|
|
36722
37012
|
asked.add(filePath);
|
|
36723
|
-
const denial2 = await assertExternalDirectoryPermission(context, filePath);
|
|
37013
|
+
const denial2 = await assertExternalDirectoryPermission(ctx, context, filePath);
|
|
36724
37014
|
if (denial2)
|
|
36725
37015
|
return permissionDeniedResponse(denial2);
|
|
36726
37016
|
}
|
|
@@ -37031,7 +37321,7 @@ function createDeleteTool(ctx) {
|
|
|
37031
37321
|
if (asked.has(filePath))
|
|
37032
37322
|
continue;
|
|
37033
37323
|
asked.add(filePath);
|
|
37034
|
-
const denial = await assertExternalDirectoryPermission(context, filePath);
|
|
37324
|
+
const denial = await assertExternalDirectoryPermission(ctx, context, filePath);
|
|
37035
37325
|
if (denial)
|
|
37036
37326
|
return permissionDeniedResponse(denial);
|
|
37037
37327
|
}
|
|
@@ -37079,13 +37369,13 @@ function createMoveTool(ctx) {
|
|
|
37079
37369
|
const filePath = resolvePathFromProjectRoot(projectRoot, args.filePath);
|
|
37080
37370
|
const destPath = resolvePathFromProjectRoot(projectRoot, args.destination);
|
|
37081
37371
|
{
|
|
37082
|
-
const sourceDenial = await assertExternalDirectoryPermission(context, filePath, {
|
|
37372
|
+
const sourceDenial = await assertExternalDirectoryPermission(ctx, context, filePath, {
|
|
37083
37373
|
kind: "file"
|
|
37084
37374
|
});
|
|
37085
37375
|
if (sourceDenial)
|
|
37086
37376
|
return permissionDeniedResponse(sourceDenial);
|
|
37087
37377
|
if (destPath !== filePath) {
|
|
37088
|
-
const destDenial = await assertExternalDirectoryPermission(context, destPath);
|
|
37378
|
+
const destDenial = await assertExternalDirectoryPermission(ctx, context, destPath);
|
|
37089
37379
|
if (destDenial)
|
|
37090
37380
|
return permissionDeniedResponse(destDenial);
|
|
37091
37381
|
}
|
|
@@ -37116,12 +37406,15 @@ function hoistedTools(ctx) {
|
|
|
37116
37406
|
aft_delete: createDeleteTool(ctx),
|
|
37117
37407
|
aft_move: createMoveTool(ctx)
|
|
37118
37408
|
};
|
|
37119
|
-
|
|
37409
|
+
const bashCfg = resolveBashConfig(ctx.config);
|
|
37410
|
+
if (bashCfg.enabled) {
|
|
37120
37411
|
tools.bash = createBashTool(ctx);
|
|
37121
|
-
|
|
37122
|
-
|
|
37123
|
-
|
|
37124
|
-
|
|
37412
|
+
if (bashCfg.background) {
|
|
37413
|
+
tools.bash_status = createBashStatusTool(ctx);
|
|
37414
|
+
tools.bash_write = createBashWriteTool(ctx);
|
|
37415
|
+
tools.bash_watch = createBashWatchTool(ctx);
|
|
37416
|
+
tools.bash_kill = createBashKillTool(ctx);
|
|
37417
|
+
}
|
|
37125
37418
|
}
|
|
37126
37419
|
return tools;
|
|
37127
37420
|
}
|
|
@@ -37144,7 +37437,7 @@ function aftPrefixedTools(ctx) {
|
|
|
37144
37437
|
const filePath = resolvePathFromProjectRoot(projectRoot, file2);
|
|
37145
37438
|
const relPath = path5.relative(projectRoot, filePath);
|
|
37146
37439
|
{
|
|
37147
|
-
const denial2 = await assertExternalDirectoryPermission(context, filePath);
|
|
37440
|
+
const denial2 = await assertExternalDirectoryPermission(ctx, context, filePath);
|
|
37148
37441
|
if (denial2)
|
|
37149
37442
|
return permissionDeniedResponse(denial2);
|
|
37150
37443
|
}
|
|
@@ -37175,12 +37468,15 @@ function aftPrefixedTools(ctx) {
|
|
|
37175
37468
|
aft_delete: createDeleteTool(ctx),
|
|
37176
37469
|
aft_move: createMoveTool(ctx)
|
|
37177
37470
|
};
|
|
37178
|
-
|
|
37471
|
+
const bashCfg = resolveBashConfig(ctx.config);
|
|
37472
|
+
if (bashCfg.enabled) {
|
|
37179
37473
|
tools.aft_bash = createBashTool(ctx);
|
|
37180
|
-
|
|
37181
|
-
|
|
37182
|
-
|
|
37183
|
-
|
|
37474
|
+
if (bashCfg.background) {
|
|
37475
|
+
tools.bash_status = createBashStatusTool(ctx);
|
|
37476
|
+
tools.bash_write = createBashWriteTool(ctx);
|
|
37477
|
+
tools.bash_watch = createBashWatchTool(ctx);
|
|
37478
|
+
tools.bash_kill = createBashKillTool(ctx);
|
|
37479
|
+
}
|
|
37184
37480
|
}
|
|
37185
37481
|
return tools;
|
|
37186
37482
|
}
|
|
@@ -37218,7 +37514,7 @@ function importTools(ctx) {
|
|
|
37218
37514
|
}
|
|
37219
37515
|
const filePath = await resolvePathArg(ctx, context, args.filePath);
|
|
37220
37516
|
{
|
|
37221
|
-
const denial = await assertExternalDirectoryPermission(context, filePath);
|
|
37517
|
+
const denial = await assertExternalDirectoryPermission(ctx, context, filePath);
|
|
37222
37518
|
if (denial)
|
|
37223
37519
|
return permissionDeniedResponse(denial);
|
|
37224
37520
|
}
|
|
@@ -37264,15 +37560,15 @@ function importTools(ctx) {
|
|
|
37264
37560
|
// src/tools/inspect.ts
|
|
37265
37561
|
import { tool as tool10 } from "@opencode-ai/plugin";
|
|
37266
37562
|
var z10 = tool10.schema;
|
|
37267
|
-
function
|
|
37563
|
+
function asRecord3(value) {
|
|
37268
37564
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
37269
37565
|
return;
|
|
37270
37566
|
return value;
|
|
37271
37567
|
}
|
|
37272
|
-
function
|
|
37568
|
+
function asNumber2(value) {
|
|
37273
37569
|
return typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
37274
37570
|
}
|
|
37275
|
-
function
|
|
37571
|
+
function asString2(value) {
|
|
37276
37572
|
return typeof value === "string" ? value : undefined;
|
|
37277
37573
|
}
|
|
37278
37574
|
function asStringArray(value) {
|
|
@@ -37289,16 +37585,16 @@ function diagnosticsServerSummary(section) {
|
|
|
37289
37585
|
return parts.length > 0 ? parts.join("; ") : "none reported";
|
|
37290
37586
|
}
|
|
37291
37587
|
function formatDiagnosticsSummary(summary) {
|
|
37292
|
-
const section =
|
|
37588
|
+
const section = asRecord3(summary?.diagnostics);
|
|
37293
37589
|
if (!section)
|
|
37294
37590
|
return;
|
|
37295
|
-
const errors3 =
|
|
37296
|
-
const warnings =
|
|
37297
|
-
const info =
|
|
37298
|
-
const hints =
|
|
37591
|
+
const errors3 = asNumber2(section.errors);
|
|
37592
|
+
const warnings = asNumber2(section.warnings);
|
|
37593
|
+
const info = asNumber2(section.info);
|
|
37594
|
+
const hints = asNumber2(section.hints);
|
|
37299
37595
|
const hasCounts = [errors3, warnings, info, hints].some((value) => value !== undefined);
|
|
37300
37596
|
const counts = `${errors3 ?? 0} errors, ${warnings ?? 0} warnings, ${info ?? 0} info, ${hints ?? 0} hints`;
|
|
37301
|
-
const status =
|
|
37597
|
+
const status = asString2(section.status);
|
|
37302
37598
|
if (status === "pending") {
|
|
37303
37599
|
return hasCounts ? `diagnostics: ${counts} so far — still pending (servers: ${diagnosticsServerSummary(section)})` : `diagnostics: pending (servers: ${diagnosticsServerSummary(section)})`;
|
|
37304
37600
|
}
|
|
@@ -37311,9 +37607,9 @@ function formatDiagnosticsSummary(summary) {
|
|
|
37311
37607
|
return;
|
|
37312
37608
|
}
|
|
37313
37609
|
function formatDiagnosticLocation(diagnostic) {
|
|
37314
|
-
const file2 =
|
|
37315
|
-
const line =
|
|
37316
|
-
const column =
|
|
37610
|
+
const file2 = asString2(diagnostic.file) ?? "(unknown file)";
|
|
37611
|
+
const line = asNumber2(diagnostic.line);
|
|
37612
|
+
const column = asNumber2(diagnostic.column);
|
|
37317
37613
|
if (line === undefined)
|
|
37318
37614
|
return file2;
|
|
37319
37615
|
if (column === undefined)
|
|
@@ -37321,21 +37617,21 @@ function formatDiagnosticLocation(diagnostic) {
|
|
|
37321
37617
|
return `${file2}:${line}:${column}`;
|
|
37322
37618
|
}
|
|
37323
37619
|
function formatDiagnosticsDetails(details) {
|
|
37324
|
-
const diagnostics = Array.isArray(details?.diagnostics) ? details.diagnostics.map(
|
|
37620
|
+
const diagnostics = Array.isArray(details?.diagnostics) ? details.diagnostics.map(asRecord3).filter(Boolean) : [];
|
|
37325
37621
|
return diagnostics.map((diagnostic) => {
|
|
37326
|
-
const severity =
|
|
37327
|
-
const message =
|
|
37328
|
-
const source =
|
|
37622
|
+
const severity = asString2(diagnostic.severity) ?? "information";
|
|
37623
|
+
const message = asString2(diagnostic.message) ?? "(no message)";
|
|
37624
|
+
const source = asString2(diagnostic.source);
|
|
37329
37625
|
const suffix = source ? ` [${source}]` : "";
|
|
37330
37626
|
return `${formatDiagnosticLocation(diagnostic)} ${severity} ${message}${suffix}`;
|
|
37331
37627
|
});
|
|
37332
37628
|
}
|
|
37333
37629
|
function renderInspectDiagnostics(response) {
|
|
37334
37630
|
const lines = [];
|
|
37335
|
-
const summaryLine = formatDiagnosticsSummary(
|
|
37631
|
+
const summaryLine = formatDiagnosticsSummary(asRecord3(response.summary));
|
|
37336
37632
|
if (summaryLine)
|
|
37337
37633
|
lines.push(summaryLine);
|
|
37338
|
-
const detailLines = formatDiagnosticsDetails(
|
|
37634
|
+
const detailLines = formatDiagnosticsDetails(asRecord3(response.details));
|
|
37339
37635
|
if (detailLines.length > 0) {
|
|
37340
37636
|
lines.push("diagnostics details:", ...detailLines.map((line) => `- ${line}`));
|
|
37341
37637
|
}
|
|
@@ -37368,7 +37664,7 @@ async function resolveAndGateScope(ctx, context, scope) {
|
|
|
37368
37664
|
if (checked.has(target))
|
|
37369
37665
|
continue;
|
|
37370
37666
|
checked.add(target);
|
|
37371
|
-
const denial = await assertExternalDirectoryPermission(context, target);
|
|
37667
|
+
const denial = await assertExternalDirectoryPermission(ctx, context, target);
|
|
37372
37668
|
if (denial)
|
|
37373
37669
|
return { scope: undefined, denial };
|
|
37374
37670
|
}
|
|
@@ -37465,6 +37761,7 @@ function navigationTools(ctx) {
|
|
|
37465
37761
|
|
|
37466
37762
|
` + `All ops require both 'filePath' and 'symbol'. 'expression' is additionally required for trace_data; 'toSymbol' for trace_to_symbol.
|
|
37467
37763
|
|
|
37764
|
+
` + `Markers: ~ = edge resolved by name only (may point at the wrong same-named symbol); [unresolved] = callee not resolved to a definition, so the location shown is the call site. Unmarked edges are resolved exactly.
|
|
37468
37765
|
`,
|
|
37469
37766
|
args: {
|
|
37470
37767
|
op: z11.enum(["call_tree", "callers", "trace_to", "trace_to_symbol", "impact", "trace_data"]).describe("Navigation operation"),
|
|
@@ -37495,7 +37792,7 @@ function navigationTools(ctx) {
|
|
|
37495
37792
|
if (checked.has(target))
|
|
37496
37793
|
continue;
|
|
37497
37794
|
checked.add(target);
|
|
37498
|
-
const denial = await assertExternalDirectoryPermission(context, target);
|
|
37795
|
+
const denial = await assertExternalDirectoryPermission(ctx, context, target);
|
|
37499
37796
|
if (denial)
|
|
37500
37797
|
return permissionDeniedResponse(denial);
|
|
37501
37798
|
}
|
|
@@ -37520,7 +37817,8 @@ function navigationTools(ctx) {
|
|
|
37520
37817
|
}
|
|
37521
37818
|
throw new Error(message);
|
|
37522
37819
|
}
|
|
37523
|
-
return
|
|
37820
|
+
return formatCallgraphSections(args.op, response).join(`
|
|
37821
|
+
`);
|
|
37524
37822
|
}
|
|
37525
37823
|
}
|
|
37526
37824
|
};
|
|
@@ -37571,7 +37869,7 @@ function readingTools(ctx) {
|
|
|
37571
37869
|
throw new Error("'target' must be a non-empty string or array of strings");
|
|
37572
37870
|
}
|
|
37573
37871
|
const resolvedTargets = await Promise.all(target.map((entry) => resolvePathArg(ctx, context, entry)));
|
|
37574
|
-
const permissionDenied3 = await assertPathExternalPermissions(context, resolvedTargets, "directory");
|
|
37872
|
+
const permissionDenied3 = await assertPathExternalPermissions(ctx, context, resolvedTargets, "directory");
|
|
37575
37873
|
if (permissionDenied3)
|
|
37576
37874
|
return permissionDeniedResponse(permissionDenied3);
|
|
37577
37875
|
const response3 = await callBridge(ctx, context, "outline", {
|
|
@@ -37593,7 +37891,7 @@ function readingTools(ctx) {
|
|
|
37593
37891
|
const st = await stat(resolvedPath);
|
|
37594
37892
|
isDirectory2 = st.isDirectory();
|
|
37595
37893
|
} catch {}
|
|
37596
|
-
const permissionDenied2 = await assertPathExternalPermissions(context, resolvedPath, isDirectory2 ? "directory" : "file");
|
|
37894
|
+
const permissionDenied2 = await assertPathExternalPermissions(ctx, context, resolvedPath, isDirectory2 ? "directory" : "file");
|
|
37597
37895
|
if (permissionDenied2)
|
|
37598
37896
|
return permissionDeniedResponse(permissionDenied2);
|
|
37599
37897
|
const params = isDirectory2 ? { directory: resolvedPath, files: true } : { file: resolvedPath, files: true };
|
|
@@ -37612,7 +37910,7 @@ function readingTools(ctx) {
|
|
|
37612
37910
|
}
|
|
37613
37911
|
if (isArray) {
|
|
37614
37912
|
const resolvedTargets = await Promise.all(target.map((entry) => resolvePathArg(ctx, context, entry)));
|
|
37615
|
-
const permissionDenied2 = await assertPathExternalPermissions(context, resolvedTargets);
|
|
37913
|
+
const permissionDenied2 = await assertPathExternalPermissions(ctx, context, resolvedTargets);
|
|
37616
37914
|
if (permissionDenied2)
|
|
37617
37915
|
return permissionDeniedResponse(permissionDenied2);
|
|
37618
37916
|
const response2 = await callBridge(ctx, context, "outline", {
|
|
@@ -37633,7 +37931,7 @@ function readingTools(ctx) {
|
|
|
37633
37931
|
const st = await stat(resolvedTarget);
|
|
37634
37932
|
isDirectory = st.isDirectory();
|
|
37635
37933
|
} catch {}
|
|
37636
|
-
const permissionDenied = await assertPathExternalPermissions(context, resolvedTarget, isDirectory ? "directory" : "file");
|
|
37934
|
+
const permissionDenied = await assertPathExternalPermissions(ctx, context, resolvedTarget, isDirectory ? "directory" : "file");
|
|
37637
37935
|
if (permissionDenied)
|
|
37638
37936
|
return permissionDeniedResponse(permissionDenied);
|
|
37639
37937
|
if (isDirectory) {
|
|
@@ -37728,7 +38026,7 @@ function readingTools(ctx) {
|
|
|
37728
38026
|
}
|
|
37729
38027
|
}
|
|
37730
38028
|
const resolvedTargets = await Promise.all(targets.map((t) => resolvePathArg(ctx, context, t.filePath)));
|
|
37731
|
-
const permissionDenied = await assertPathExternalPermissions(context, resolvedTargets);
|
|
38029
|
+
const permissionDenied = await assertPathExternalPermissions(ctx, context, resolvedTargets);
|
|
37732
38030
|
if (permissionDenied)
|
|
37733
38031
|
return permissionDeniedResponse(permissionDenied);
|
|
37734
38032
|
const responses = await Promise.all(targets.map((t, index) => {
|
|
@@ -37760,7 +38058,7 @@ function readingTools(ctx) {
|
|
|
37760
38058
|
}
|
|
37761
38059
|
const file2 = hasUrl ? args.url : await resolvePathArg(ctx, context, args.filePath);
|
|
37762
38060
|
if (!hasUrl) {
|
|
37763
|
-
const permissionDenied = await assertPathExternalPermissions(context, file2);
|
|
38061
|
+
const permissionDenied = await assertPathExternalPermissions(ctx, context, file2);
|
|
37764
38062
|
if (permissionDenied)
|
|
37765
38063
|
return permissionDeniedResponse(permissionDenied);
|
|
37766
38064
|
}
|
|
@@ -37832,7 +38130,7 @@ function formatZoomBatchResult(targetLabel, symbols, responses) {
|
|
|
37832
38130
|
`) };
|
|
37833
38131
|
}
|
|
37834
38132
|
var MAX_UNCHECKED_FILES_IN_FOOTER = 10;
|
|
37835
|
-
async function assertPathExternalPermissions(context, target, kind = "file") {
|
|
38133
|
+
async function assertPathExternalPermissions(ctx, context, target, kind = "file") {
|
|
37836
38134
|
const targets = Array.isArray(target) ? target : [target];
|
|
37837
38135
|
const checked = new Set;
|
|
37838
38136
|
for (const resolvedPath of targets) {
|
|
@@ -37842,7 +38140,7 @@ async function assertPathExternalPermissions(context, target, kind = "file") {
|
|
|
37842
38140
|
if (checked.has(key))
|
|
37843
38141
|
continue;
|
|
37844
38142
|
checked.add(key);
|
|
37845
|
-
const denial = await assertExternalDirectoryPermission(context, resolvedPath, { kind });
|
|
38143
|
+
const denial = await assertExternalDirectoryPermission(ctx, context, resolvedPath, { kind });
|
|
37846
38144
|
if (denial)
|
|
37847
38145
|
return denial;
|
|
37848
38146
|
}
|
|
@@ -38008,7 +38306,7 @@ function refactoringTools(ctx) {
|
|
|
38008
38306
|
if (asked.has(affectedPath))
|
|
38009
38307
|
continue;
|
|
38010
38308
|
asked.add(affectedPath);
|
|
38011
|
-
const denial = await assertExternalDirectoryPermission(context, affectedPath);
|
|
38309
|
+
const denial = await assertExternalDirectoryPermission(ctx, context, affectedPath);
|
|
38012
38310
|
if (denial)
|
|
38013
38311
|
return permissionDeniedResponse(denial);
|
|
38014
38312
|
}
|
|
@@ -38116,7 +38414,7 @@ function safetyTools(ctx) {
|
|
|
38116
38414
|
}
|
|
38117
38415
|
const previewPaths = Array.from(new Set(responsePaths(preview2)));
|
|
38118
38416
|
for (const filePath2 of previewPaths) {
|
|
38119
|
-
const denial = await assertExternalDirectoryPermission(context, filePath2);
|
|
38417
|
+
const denial = await assertExternalDirectoryPermission(ctx, context, filePath2);
|
|
38120
38418
|
if (denial)
|
|
38121
38419
|
return permissionDeniedResponse(denial);
|
|
38122
38420
|
}
|
|
@@ -38140,7 +38438,7 @@ function safetyTools(ctx) {
|
|
|
38140
38438
|
if (uniqueParents.has(parent))
|
|
38141
38439
|
continue;
|
|
38142
38440
|
uniqueParents.add(parent);
|
|
38143
|
-
const denial = await assertExternalDirectoryPermission(context, abs, {
|
|
38441
|
+
const denial = await assertExternalDirectoryPermission(ctx, context, abs, {
|
|
38144
38442
|
kind: "file"
|
|
38145
38443
|
});
|
|
38146
38444
|
if (denial)
|
|
@@ -38154,7 +38452,7 @@ function safetyTools(ctx) {
|
|
|
38154
38452
|
throw new Error(bridgeErrorMessage(preview2, "checkpoint path preview failed"));
|
|
38155
38453
|
}
|
|
38156
38454
|
for (const filePath of new Set(responsePaths(preview2))) {
|
|
38157
|
-
const denial = await assertExternalDirectoryPermission(context, filePath);
|
|
38455
|
+
const denial = await assertExternalDirectoryPermission(ctx, context, filePath);
|
|
38158
38456
|
if (denial)
|
|
38159
38457
|
return permissionDeniedResponse(denial);
|
|
38160
38458
|
}
|
|
@@ -38349,7 +38647,7 @@ function searchTools(ctx) {
|
|
|
38349
38647
|
return permissionDeniedResponse(grepDenied);
|
|
38350
38648
|
if (pathSplit) {
|
|
38351
38649
|
for (const target of searchPathTargets(projectRoot, pathSplit, "file")) {
|
|
38352
|
-
const externalDenied = await assertExternalDirectoryPermission(context, target.target, {
|
|
38650
|
+
const externalDenied = await assertExternalDirectoryPermission(ctx, context, target.target, {
|
|
38353
38651
|
kind: target.kind
|
|
38354
38652
|
});
|
|
38355
38653
|
if (externalDenied)
|
|
@@ -38402,7 +38700,7 @@ function searchTools(ctx) {
|
|
|
38402
38700
|
return permissionDeniedResponse(globDenied);
|
|
38403
38701
|
if (pathSplit) {
|
|
38404
38702
|
for (const target of searchPathTargets(projectRoot, pathSplit, "directory")) {
|
|
38405
|
-
const externalDenied = await assertExternalDirectoryPermission(context, target.target, {
|
|
38703
|
+
const externalDenied = await assertExternalDirectoryPermission(ctx, context, target.target, {
|
|
38406
38704
|
kind: target.kind
|
|
38407
38705
|
});
|
|
38408
38706
|
if (externalDenied)
|
|
@@ -38568,11 +38866,11 @@ function buildWorkflowHints(opts) {
|
|
|
38568
38866
|
}
|
|
38569
38867
|
if (hasBash && hasBgBash) {
|
|
38570
38868
|
sections.push([
|
|
38571
|
-
`**Long-running commands** (builds, installs, full test suites): \`${bashName}({ background: true })\` returns a \`taskId\` immediately
|
|
38572
|
-
"1.
|
|
38573
|
-
"2.
|
|
38574
|
-
|
|
38575
|
-
|
|
38869
|
+
`**Long-running commands** (builds, installs, full test suites): \`${bashName}({ background: true })\` returns a \`taskId\` immediately.`,
|
|
38870
|
+
"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).",
|
|
38871
|
+
"2. Useful parallel work available → end your turn; the completion reminder delivers the result. (Or spawn a subagent for the side work.)",
|
|
38872
|
+
"3. Want to react to a specific early output line → async `bash_watch` (background:true + pattern).",
|
|
38873
|
+
"Never loop `bash_status` to wait — it's a one-shot inspector, not a wait primitive."
|
|
38576
38874
|
].join(`
|
|
38577
38875
|
`));
|
|
38578
38876
|
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: "..." })\`.`);
|
|
@@ -38610,32 +38908,6 @@ function isTuiMode2() {
|
|
|
38610
38908
|
function throwSentinel(command) {
|
|
38611
38909
|
throw new Error(`${SENTINEL_PREFIX}${command.toUpperCase().replace(/-/g, "_")}_HANDLED__`);
|
|
38612
38910
|
}
|
|
38613
|
-
async function sendIgnoredMessage2(client, sessionID, text) {
|
|
38614
|
-
const typedClient = client;
|
|
38615
|
-
let agent;
|
|
38616
|
-
try {
|
|
38617
|
-
const ctx = await resolvePromptContext(client, sessionID);
|
|
38618
|
-
agent = ctx?.agent;
|
|
38619
|
-
} catch {
|
|
38620
|
-
agent = undefined;
|
|
38621
|
-
}
|
|
38622
|
-
const body = {
|
|
38623
|
-
noReply: true,
|
|
38624
|
-
parts: [{ type: "text", text, ignored: true }]
|
|
38625
|
-
};
|
|
38626
|
-
if (agent)
|
|
38627
|
-
body.agent = agent;
|
|
38628
|
-
const promptInput = { path: { id: sessionID }, body };
|
|
38629
|
-
if (typeof typedClient.session?.prompt === "function") {
|
|
38630
|
-
await Promise.resolve(typedClient.session.prompt(promptInput));
|
|
38631
|
-
return;
|
|
38632
|
-
}
|
|
38633
|
-
if (typeof typedClient.session?.promptAsync === "function") {
|
|
38634
|
-
await typedClient.session.promptAsync(promptInput);
|
|
38635
|
-
return;
|
|
38636
|
-
}
|
|
38637
|
-
throw new Error("[aft-plugin] client.session.prompt is unavailable");
|
|
38638
|
-
}
|
|
38639
38911
|
var PLUGIN_VERSION = (() => {
|
|
38640
38912
|
try {
|
|
38641
38913
|
const req = createRequire3(import.meta.url);
|
|
@@ -38657,6 +38929,7 @@ async function initializePluginForDirectory(input) {
|
|
|
38657
38929
|
const binaryPath = await findBinary(PLUGIN_VERSION);
|
|
38658
38930
|
await ensureStorageMigrated({ harness: "opencode", binaryPath, logger: bridgeLogger });
|
|
38659
38931
|
const aftConfig = loadAftConfig(input.directory);
|
|
38932
|
+
enqueueConfigParseWarnings(input.directory, getConfigLoadErrors());
|
|
38660
38933
|
const autoUpdateAbort = new AbortController;
|
|
38661
38934
|
const configOverrides = {
|
|
38662
38935
|
...resolveProjectOverridesForConfigure(aftConfig),
|
|
@@ -38747,6 +39020,7 @@ ${lines}
|
|
|
38747
39020
|
projectConfigLoader: (projectRoot) => {
|
|
38748
39021
|
try {
|
|
38749
39022
|
const projectConfig = loadAftConfig(projectRoot);
|
|
39023
|
+
enqueueConfigParseWarnings(projectRoot, getConfigLoadErrors());
|
|
38750
39024
|
return resolveProjectOverridesForConfigure(projectConfig);
|
|
38751
39025
|
} catch (err) {
|
|
38752
39026
|
warn2(`loadAftConfig(${projectRoot}) failed; falling back to plugin-init config: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -38805,8 +39079,7 @@ ${lines}
|
|
|
38805
39079
|
ctx,
|
|
38806
39080
|
directory: sessionDir,
|
|
38807
39081
|
sessionID: completion.session_id,
|
|
38808
|
-
client: input.client
|
|
38809
|
-
serverUrl: input.serverUrl?.toString()
|
|
39082
|
+
client: input.client
|
|
38810
39083
|
}, completion);
|
|
38811
39084
|
},
|
|
38812
39085
|
onBashLongRunning: (reminder, bridge) => {
|
|
@@ -38815,8 +39088,7 @@ ${lines}
|
|
|
38815
39088
|
ctx,
|
|
38816
39089
|
directory: sessionDir,
|
|
38817
39090
|
sessionID: reminder.session_id,
|
|
38818
|
-
client: input.client
|
|
38819
|
-
serverUrl: input.serverUrl?.toString()
|
|
39091
|
+
client: input.client
|
|
38820
39092
|
}, reminder);
|
|
38821
39093
|
},
|
|
38822
39094
|
onBashPatternMatch: (frame, bridge) => {
|
|
@@ -38825,8 +39097,7 @@ ${lines}
|
|
|
38825
39097
|
ctx,
|
|
38826
39098
|
directory: sessionDir,
|
|
38827
39099
|
sessionID: frame.session_id,
|
|
38828
|
-
client: input.client
|
|
38829
|
-
serverUrl: input.serverUrl?.toString()
|
|
39100
|
+
client: input.client
|
|
38830
39101
|
}, frame);
|
|
38831
39102
|
}
|
|
38832
39103
|
};
|
|
@@ -38839,16 +39110,6 @@ ${lines}
|
|
|
38839
39110
|
config: aftConfig,
|
|
38840
39111
|
storageDir: configOverrides.storage_dir
|
|
38841
39112
|
};
|
|
38842
|
-
probeServerReachable(input.serverUrl?.toString()).then((reachable) => {
|
|
38843
|
-
setLiveServerWakeAvailable(reachable);
|
|
38844
|
-
if (reachable) {
|
|
38845
|
-
log2("Live OpenCode HTTP listener reachable; bg-notifications wake path = live-server (anomalyco/opencode#28202 workaround active).");
|
|
38846
|
-
} else {
|
|
38847
|
-
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.");
|
|
38848
|
-
}
|
|
38849
|
-
}).catch(() => {
|
|
38850
|
-
setLiveServerWakeAvailable(false);
|
|
38851
|
-
});
|
|
38852
39113
|
if (onnxRuntimePromise) {
|
|
38853
39114
|
onnxRuntimePromise.then((ortDylibDir) => {
|
|
38854
39115
|
if (ortDylibDir) {
|
|
@@ -39083,9 +39344,24 @@ Install: ${getManualInstallHint()}`).catch(() => {});
|
|
|
39083
39344
|
ctx,
|
|
39084
39345
|
directory: sessionDir,
|
|
39085
39346
|
sessionID,
|
|
39086
|
-
client: input.client
|
|
39087
|
-
serverUrl: input.serverUrl?.toString()
|
|
39347
|
+
client: input.client
|
|
39088
39348
|
});
|
|
39349
|
+
const configParseWarnings = drainPendingConfigParseWarnings(sessionDir);
|
|
39350
|
+
if (configParseWarnings.length > 0) {
|
|
39351
|
+
const bridge = pool.getActiveBridgeForRoot(sessionDir) ?? pool.getBridge(sessionDir);
|
|
39352
|
+
enqueueConfigureWarningsForSession({
|
|
39353
|
+
projectRoot: sessionDir,
|
|
39354
|
+
sessionId: sessionID,
|
|
39355
|
+
client: input.client,
|
|
39356
|
+
bridge,
|
|
39357
|
+
warnings: configParseWarnings,
|
|
39358
|
+
fallbackClient: input.client,
|
|
39359
|
+
storageDir: configOverrides.storage_dir,
|
|
39360
|
+
pluginVersion: PLUGIN_VERSION,
|
|
39361
|
+
serverUrl: input.serverUrl?.toString(),
|
|
39362
|
+
delivery: aftConfig.configure_warnings_delivery ?? "toast"
|
|
39363
|
+
});
|
|
39364
|
+
}
|
|
39089
39365
|
await flushConfigureWarningsOnIdle(sessionID);
|
|
39090
39366
|
},
|
|
39091
39367
|
"chat.message": async (messageInput) => {
|