@cortexkit/aft-opencode 0.32.0 → 0.33.0
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/config.d.ts +19 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/index.d.ts +1 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +556 -184
- package/dist/notifications.d.ts.map +1 -1
- package/dist/patch-parser.d.ts.map +1 -1
- package/dist/shared/status.d.ts +1 -1
- package/dist/shutdown-hooks.d.ts +1 -0
- package/dist/shutdown-hooks.d.ts.map +1 -1
- package/dist/tools/hoisted.d.ts.map +1 -1
- package/dist/tools/inspect.d.ts +30 -0
- package/dist/tools/inspect.d.ts.map +1 -0
- package/dist/tools/navigation.d.ts +1 -1
- package/dist/tools/reading.d.ts.map +1 -1
- package/dist/tools/semantic.d.ts.map +1 -1
- package/dist/tui.js +1 -1
- package/dist/workflow-hints.d.ts.map +1 -1
- package/package.json +12 -12
- package/src/shared/status.ts +1 -1
- package/src/tui/sidebar.tsx +1 -1
- package/dist/tools/lsp.d.ts +0 -7
- package/dist/tools/lsp.d.ts.map +0 -1
package/dist/index.js
CHANGED
|
@@ -11599,6 +11599,7 @@ import { homedir } from "node:os";
|
|
|
11599
11599
|
import { join } from "node:path";
|
|
11600
11600
|
import { StringDecoder } from "node:string_decoder";
|
|
11601
11601
|
var DEFAULT_BRIDGE_TIMEOUT_MS = 30000;
|
|
11602
|
+
var BRIDGE_HANG_TIMEOUT_THRESHOLD = 2;
|
|
11602
11603
|
var SEMANTIC_TIMEOUT_SAFETY_MARGIN_MS = 5000;
|
|
11603
11604
|
var MAX_STDOUT_BUFFER = 64 * 1024 * 1024;
|
|
11604
11605
|
function tagStderrLine(line) {
|
|
@@ -11706,8 +11707,11 @@ class BinaryBridge {
|
|
|
11706
11707
|
statusListeners = new Set;
|
|
11707
11708
|
configureWarningClients = new Map;
|
|
11708
11709
|
restartResetTimer = null;
|
|
11710
|
+
lastChildActivityAt = 0;
|
|
11711
|
+
consecutiveRequestTimeouts = 0;
|
|
11709
11712
|
errorPrefix;
|
|
11710
11713
|
logger;
|
|
11714
|
+
childEnv;
|
|
11711
11715
|
constructor(binaryPath, cwd, options, configOverrides) {
|
|
11712
11716
|
this.binaryPath = binaryPath;
|
|
11713
11717
|
this.cwd = cwd;
|
|
@@ -11722,6 +11726,7 @@ class BinaryBridge {
|
|
|
11722
11726
|
this.onBashPatternMatch = options?.onBashPatternMatch;
|
|
11723
11727
|
this.errorPrefix = options?.errorPrefix ?? "[aft-bridge]";
|
|
11724
11728
|
this.logger = options?.logger;
|
|
11729
|
+
this.childEnv = options?.childEnv;
|
|
11725
11730
|
}
|
|
11726
11731
|
logVia(message, meta) {
|
|
11727
11732
|
const logger = this.logger ?? getActiveLogger();
|
|
@@ -11873,20 +11878,41 @@ class BinaryBridge {
|
|
|
11873
11878
|
const line = `${JSON.stringify(request)}
|
|
11874
11879
|
`;
|
|
11875
11880
|
const keepBridgeOnTimeout = options?.keepBridgeOnTimeout === true;
|
|
11881
|
+
let requestSentAt = Date.now();
|
|
11876
11882
|
const response = await new Promise((resolve, reject) => {
|
|
11877
11883
|
const timer = setTimeout(() => {
|
|
11884
|
+
const entry = this.pending.get(id);
|
|
11885
|
+
if (!entry)
|
|
11886
|
+
return;
|
|
11878
11887
|
this.pending.delete(id);
|
|
11879
|
-
|
|
11888
|
+
clearTimeout(entry.timer);
|
|
11889
|
+
if (keepBridgeOnTimeout) {
|
|
11890
|
+
const timeoutMsg2 = `Request "${command}" (id=${id}) timed out after ${effectiveTimeoutMs}ms`;
|
|
11891
|
+
if (requestSessionId) {
|
|
11892
|
+
this.sessionWarnVia(requestSessionId, timeoutMsg2);
|
|
11893
|
+
} else {
|
|
11894
|
+
this.warnVia(timeoutMsg2);
|
|
11895
|
+
}
|
|
11896
|
+
entry.reject(new Error(`${this.errorPrefix} Request "${command}" (id=${id}) timed out after ${effectiveTimeoutMs}ms`));
|
|
11897
|
+
return;
|
|
11898
|
+
}
|
|
11899
|
+
const childActiveSinceRequest = this.lastChildActivityAt > requestSentAt;
|
|
11900
|
+
const consecutiveTimeouts = this.consecutiveRequestTimeouts + 1;
|
|
11901
|
+
this.consecutiveRequestTimeouts = consecutiveTimeouts;
|
|
11902
|
+
const keepWarm = childActiveSinceRequest || consecutiveTimeouts < BRIDGE_HANG_TIMEOUT_THRESHOLD;
|
|
11903
|
+
const restartSuffix = keepWarm ? " — bridge kept warm" : " — restarting bridge";
|
|
11880
11904
|
const timeoutMsg = `Request "${command}" (id=${id}) timed out after ${effectiveTimeoutMs}ms${restartSuffix}`;
|
|
11881
11905
|
if (requestSessionId) {
|
|
11882
11906
|
this.sessionWarnVia(requestSessionId, timeoutMsg);
|
|
11883
11907
|
} else {
|
|
11884
11908
|
this.warnVia(timeoutMsg);
|
|
11885
11909
|
}
|
|
11886
|
-
|
|
11887
|
-
|
|
11888
|
-
|
|
11910
|
+
if (keepWarm) {
|
|
11911
|
+
entry.reject(new Error(`${this.errorPrefix} request "${command}" timed out after ${effectiveTimeoutMs}ms (bridge busy/under load); bridge kept warm — retry`));
|
|
11912
|
+
return;
|
|
11889
11913
|
}
|
|
11914
|
+
entry.reject(new Error(`${this.errorPrefix} Request "${command}" (id=${id}) timed out after ${effectiveTimeoutMs}ms`));
|
|
11915
|
+
this.handleTimeout(requestSessionId);
|
|
11890
11916
|
}, effectiveTimeoutMs);
|
|
11891
11917
|
this.pending.set(id, { resolve, reject, timer, onProgress: options?.onProgress });
|
|
11892
11918
|
if (!this.process?.stdin?.writable) {
|
|
@@ -11895,6 +11921,7 @@ class BinaryBridge {
|
|
|
11895
11921
|
reject(new Error(`${this.errorPrefix} stdin not writable for command "${command}"`));
|
|
11896
11922
|
return;
|
|
11897
11923
|
}
|
|
11924
|
+
requestSentAt = Date.now();
|
|
11898
11925
|
this.process.stdin.write(line, (err) => {
|
|
11899
11926
|
if (err) {
|
|
11900
11927
|
const entry = this.pending.get(id);
|
|
@@ -12089,6 +12116,15 @@ class BinaryBridge {
|
|
|
12089
12116
|
this.logVia(`ORT_DYLIB_PATH set from managed ONNX Runtime: ${ortLibraryPath}`);
|
|
12090
12117
|
}
|
|
12091
12118
|
}
|
|
12119
|
+
if (this.childEnv) {
|
|
12120
|
+
for (const [key, value] of Object.entries(this.childEnv)) {
|
|
12121
|
+
if (value === undefined) {
|
|
12122
|
+
delete env[key];
|
|
12123
|
+
} else {
|
|
12124
|
+
env[key] = value;
|
|
12125
|
+
}
|
|
12126
|
+
}
|
|
12127
|
+
}
|
|
12092
12128
|
const child = spawn(this.binaryPath, [], {
|
|
12093
12129
|
cwd: this.cwd,
|
|
12094
12130
|
stdio: ["pipe", "pipe", "pipe"],
|
|
@@ -12138,6 +12174,8 @@ class BinaryBridge {
|
|
|
12138
12174
|
this.process = child;
|
|
12139
12175
|
this.stdoutBuffer = "";
|
|
12140
12176
|
this.stderrBuffer = "";
|
|
12177
|
+
this.lastChildActivityAt = 0;
|
|
12178
|
+
this.consecutiveRequestTimeouts = 0;
|
|
12141
12179
|
this.stderrTail = [];
|
|
12142
12180
|
}
|
|
12143
12181
|
pushStderrLine(line) {
|
|
@@ -12193,6 +12231,7 @@ class BinaryBridge {
|
|
|
12193
12231
|
continue;
|
|
12194
12232
|
try {
|
|
12195
12233
|
const response = JSON.parse(line);
|
|
12234
|
+
this.lastChildActivityAt = Date.now();
|
|
12196
12235
|
if (response.type === "progress") {
|
|
12197
12236
|
const requestId = response.request_id;
|
|
12198
12237
|
const entry = requestId ? this.pending.get(requestId) : undefined;
|
|
@@ -12245,6 +12284,7 @@ class BinaryBridge {
|
|
|
12245
12284
|
continue;
|
|
12246
12285
|
this.pending.delete(id);
|
|
12247
12286
|
clearTimeout(entry.timer);
|
|
12287
|
+
this.consecutiveRequestTimeouts = 0;
|
|
12248
12288
|
this.scheduleRestartCountReset();
|
|
12249
12289
|
entry.resolve(response);
|
|
12250
12290
|
} else if (typeof response.type === "string") {
|
|
@@ -12256,6 +12296,7 @@ class BinaryBridge {
|
|
|
12256
12296
|
}
|
|
12257
12297
|
}
|
|
12258
12298
|
handleTimeout(triggeringSessionId) {
|
|
12299
|
+
this.consecutiveRequestTimeouts = 0;
|
|
12259
12300
|
this.rejectAllPending(new Error(`${this.errorPrefix} bridge killed during sibling timeout — request aborted`));
|
|
12260
12301
|
if (this.process) {
|
|
12261
12302
|
this.process.kill("SIGKILL");
|
|
@@ -12334,7 +12375,7 @@ class BinaryBridge {
|
|
|
12334
12375
|
// ../aft-bridge/dist/downloader.js
|
|
12335
12376
|
import { spawnSync } from "node:child_process";
|
|
12336
12377
|
import { createHash, randomUUID } from "node:crypto";
|
|
12337
|
-
import { chmodSync, closeSync, createWriteStream, existsSync, mkdirSync, openSync, readFileSync, renameSync, rmSync, statSync, unlinkSync, writeSync } from "node:fs";
|
|
12378
|
+
import { chmodSync, closeSync, copyFileSync, createWriteStream, existsSync, mkdirSync, openSync, readFileSync, renameSync, rmSync, statSync, unlinkSync, writeSync } from "node:fs";
|
|
12338
12379
|
import { homedir as homedir2 } from "node:os";
|
|
12339
12380
|
import { join as join2 } from "node:path";
|
|
12340
12381
|
import { Readable } from "node:stream";
|
|
@@ -12501,10 +12542,18 @@ async function downloadBinary(version) {
|
|
|
12501
12542
|
throw new Error(`Checksum mismatch for ${assetName}: expected ${expectedHash}, got ${actualHash}. ` + "The binary may have been tampered with.");
|
|
12502
12543
|
}
|
|
12503
12544
|
log(`Checksum verified (SHA-256: ${actualHash.slice(0, 16)}...)`);
|
|
12504
|
-
if (process.platform
|
|
12545
|
+
if (process.platform === "win32") {
|
|
12546
|
+
copyFileSync(tmpPath, binaryPath);
|
|
12547
|
+
} else {
|
|
12505
12548
|
chmodSync(tmpPath, 493);
|
|
12549
|
+
renameSync(tmpPath, binaryPath);
|
|
12550
|
+
}
|
|
12551
|
+
try {
|
|
12552
|
+
if (existsSync(tmpPath))
|
|
12553
|
+
unlinkSync(tmpPath);
|
|
12554
|
+
} catch {
|
|
12555
|
+
warn(`Could not clean up temporary download file ${tmpPath} — it can be removed manually.`);
|
|
12506
12556
|
}
|
|
12507
|
-
renameSync(tmpPath, binaryPath);
|
|
12508
12557
|
log(`AFT binary ready at ${binaryPath}`);
|
|
12509
12558
|
return binaryPath;
|
|
12510
12559
|
} catch (err) {
|
|
@@ -12668,7 +12717,7 @@ function markAnnouncementSeen(storageRoot, harness, currentVersion) {
|
|
|
12668
12717
|
|
|
12669
12718
|
// ../aft-bridge/dist/resolver.js
|
|
12670
12719
|
import { execSync } from "node:child_process";
|
|
12671
|
-
import { chmodSync as chmodSync2, copyFileSync, existsSync as existsSync3, mkdirSync as mkdirSync3, renameSync as renameSync3 } from "node:fs";
|
|
12720
|
+
import { chmodSync as chmodSync2, copyFileSync as copyFileSync2, existsSync as existsSync3, mkdirSync as mkdirSync3, renameSync as renameSync3, unlinkSync as unlinkSync2 } from "node:fs";
|
|
12672
12721
|
import { createRequire as createRequire2 } from "node:module";
|
|
12673
12722
|
import { homedir as homedir3 } from "node:os";
|
|
12674
12723
|
import { join as join4 } from "node:path";
|
|
@@ -12691,10 +12740,15 @@ function copyToVersionedCache(npmBinaryPath, knownVersion) {
|
|
|
12691
12740
|
}
|
|
12692
12741
|
mkdirSync3(versionedDir, { recursive: true });
|
|
12693
12742
|
const tmpPath = `${cachedPath}.${process.pid}.${Date.now()}.tmp`;
|
|
12694
|
-
|
|
12743
|
+
copyFileSync2(npmBinaryPath, tmpPath);
|
|
12695
12744
|
if (process.platform !== "win32") {
|
|
12696
12745
|
chmodSync2(tmpPath, 493);
|
|
12697
12746
|
}
|
|
12747
|
+
if (process.platform === "win32" && existsSync3(cachedPath)) {
|
|
12748
|
+
try {
|
|
12749
|
+
unlinkSync2(cachedPath);
|
|
12750
|
+
} catch {}
|
|
12751
|
+
}
|
|
12698
12752
|
renameSync3(tmpPath, cachedPath);
|
|
12699
12753
|
log(`Copied npm binary to versioned cache: ${cachedPath}`);
|
|
12700
12754
|
return cachedPath;
|
|
@@ -12946,7 +13000,7 @@ async function ensureStorageMigrated(opts) {
|
|
|
12946
13000
|
// ../aft-bridge/dist/onnx-runtime.js
|
|
12947
13001
|
import { execFileSync } from "node:child_process";
|
|
12948
13002
|
import { createHash as createHash2 } from "node:crypto";
|
|
12949
|
-
import { chmodSync as chmodSync3, closeSync as closeSync2, copyFileSync as
|
|
13003
|
+
import { chmodSync as chmodSync3, closeSync as closeSync2, copyFileSync as copyFileSync3, createWriteStream as createWriteStream2, existsSync as existsSync5, lstatSync, mkdirSync as mkdirSync5, openSync as openSync2, readdirSync, readFileSync as readFileSync3, readlinkSync, realpathSync, rmSync as rmSync2, statSync as statSync2, symlinkSync, unlinkSync as unlinkSync3, writeFileSync as writeFileSync2 } from "node:fs";
|
|
12950
13004
|
import { basename, dirname as dirname3, isAbsolute, join as join6, relative, resolve, win32 } from "node:path";
|
|
12951
13005
|
import { Readable as Readable2 } from "node:stream";
|
|
12952
13006
|
import { pipeline as pipeline2 } from "node:stream/promises";
|
|
@@ -13185,20 +13239,64 @@ function findSystemOnnxRuntime(libName) {
|
|
|
13185
13239
|
} else if (process.platform === "linux") {
|
|
13186
13240
|
searchPaths.push("/usr/lib", "/usr/lib/x86_64-linux-gnu", "/usr/lib/aarch64-linux-gnu", "/usr/local/lib");
|
|
13187
13241
|
} else if (process.platform === "win32") {
|
|
13242
|
+
const programFiles = process.env.ProgramFiles ?? "C:\\Program Files";
|
|
13243
|
+
const programFilesX86 = process.env["ProgramFiles(x86)"] ?? "C:\\Program Files (x86)";
|
|
13244
|
+
searchPaths.push(join6(programFiles, "onnxruntime", "lib"), join6(programFiles, "Microsoft ONNX Runtime", "lib"), join6(programFiles, "Microsoft Machine Learning", "lib"), join6(programFilesX86, "onnxruntime", "lib"), ...(() => {
|
|
13245
|
+
const nugetPaths = [];
|
|
13246
|
+
const userProfile = process.env.USERPROFILE ?? "";
|
|
13247
|
+
if (!userProfile)
|
|
13248
|
+
return nugetPaths;
|
|
13249
|
+
const nugetPackageDir = join6(userProfile, ".nuget", "packages", "microsoft.ml.onnxruntime");
|
|
13250
|
+
if (!existsSync5(nugetPackageDir))
|
|
13251
|
+
return nugetPaths;
|
|
13252
|
+
try {
|
|
13253
|
+
for (const entry of readdirSync(nugetPackageDir, { withFileTypes: true })) {
|
|
13254
|
+
if (!entry.isDirectory())
|
|
13255
|
+
continue;
|
|
13256
|
+
if (entry.name === "__globalPackagesFolder" || entry.name.startsWith("."))
|
|
13257
|
+
continue;
|
|
13258
|
+
nugetPaths.push(join6(nugetPackageDir, entry.name, "runtimes", "win-x64", "native"), join6(nugetPackageDir, entry.name, "runtimes", "win-arm64", "native"));
|
|
13259
|
+
}
|
|
13260
|
+
} catch (err) {
|
|
13261
|
+
warn(`Failed to scan NuGet ONNX Runtime cache ${nugetPackageDir}: ${err instanceof Error ? err.message : String(err)}`);
|
|
13262
|
+
}
|
|
13263
|
+
return nugetPaths;
|
|
13264
|
+
})());
|
|
13188
13265
|
searchPaths.push(...pathEntriesForPlatform());
|
|
13189
13266
|
}
|
|
13267
|
+
const normalizeCase = process.platform === "win32" || process.platform === "darwin";
|
|
13190
13268
|
const seen = new Set;
|
|
13191
|
-
|
|
13192
|
-
|
|
13193
|
-
|
|
13194
|
-
|
|
13195
|
-
if (
|
|
13196
|
-
|
|
13197
|
-
|
|
13198
|
-
|
|
13199
|
-
|
|
13269
|
+
const uniquePaths = searchPaths.filter((p) => {
|
|
13270
|
+
let key = resolve(p).replace(/[/\\]+$/, "");
|
|
13271
|
+
if (normalizeCase)
|
|
13272
|
+
key = key.toLowerCase();
|
|
13273
|
+
if (seen.has(key))
|
|
13274
|
+
return false;
|
|
13275
|
+
seen.add(key);
|
|
13276
|
+
return true;
|
|
13277
|
+
});
|
|
13278
|
+
for (const dir of uniquePaths) {
|
|
13279
|
+
const libPath = join6(dir, libName);
|
|
13280
|
+
if (process.platform === "win32") {
|
|
13281
|
+
if (!directoryContainsLibrary(dir, libName))
|
|
13282
|
+
continue;
|
|
13283
|
+
} else if (!existsSync5(libPath)) {
|
|
13200
13284
|
continue;
|
|
13201
13285
|
}
|
|
13286
|
+
let skipVersionCheck = false;
|
|
13287
|
+
if (process.platform === "win32") {
|
|
13288
|
+
const dirLower = dir.toLowerCase();
|
|
13289
|
+
const isCommonPath = dirLower.includes("program files") || dirLower.includes("onnxruntime");
|
|
13290
|
+
if (!isCommonPath)
|
|
13291
|
+
skipVersionCheck = true;
|
|
13292
|
+
}
|
|
13293
|
+
if (!skipVersionCheck) {
|
|
13294
|
+
const version = detectOnnxVersion(dir, libName);
|
|
13295
|
+
if (version && !isOnnxVersionCompatible(version)) {
|
|
13296
|
+
warn(`Skipping system ONNX Runtime at ${dir} (v${version}); AFT requires ` + `v${REQUIRED_ORT_MAJOR}.${REQUIRED_ORT_MIN_MINOR}+. Falling through to AFT-managed download.`);
|
|
13297
|
+
continue;
|
|
13298
|
+
}
|
|
13299
|
+
}
|
|
13202
13300
|
return dir;
|
|
13203
13301
|
}
|
|
13204
13302
|
return null;
|
|
@@ -13236,7 +13334,7 @@ async function downloadFileWithCap(url, destPath) {
|
|
|
13236
13334
|
await pipeline2(nodeStream, createWriteStream2(destPath), { signal: controller.signal });
|
|
13237
13335
|
} catch (err) {
|
|
13238
13336
|
try {
|
|
13239
|
-
|
|
13337
|
+
unlinkSync3(destPath);
|
|
13240
13338
|
} catch {}
|
|
13241
13339
|
throw err;
|
|
13242
13340
|
} finally {
|
|
@@ -13297,7 +13395,7 @@ async function downloadOnnxRuntime(info, targetDir) {
|
|
|
13297
13395
|
await extractZipArchive(archivePath, tmpDir);
|
|
13298
13396
|
}
|
|
13299
13397
|
try {
|
|
13300
|
-
|
|
13398
|
+
unlinkSync3(archivePath);
|
|
13301
13399
|
} catch {}
|
|
13302
13400
|
validateExtractedTree(tmpDir);
|
|
13303
13401
|
const extractedDir = join6(tmpDir, info.assetName, "lib");
|
|
@@ -13346,7 +13444,7 @@ async function downloadOnnxRuntime(info, targetDir) {
|
|
|
13346
13444
|
return null;
|
|
13347
13445
|
}
|
|
13348
13446
|
}
|
|
13349
|
-
function copyOnnxLibraries(info, extractedDir, targetDir, realFiles, symlinks, copyFile =
|
|
13447
|
+
function copyOnnxLibraries(info, extractedDir, targetDir, realFiles, symlinks, copyFile = copyFileSync3) {
|
|
13350
13448
|
const requiredLibs = new Set([info.libName]);
|
|
13351
13449
|
for (const libFile of realFiles) {
|
|
13352
13450
|
const src = join6(extractedDir, libFile);
|
|
@@ -13367,7 +13465,7 @@ function copyOnnxLibraries(info, extractedDir, targetDir, realFiles, symlinks, c
|
|
|
13367
13465
|
for (const link of symlinks) {
|
|
13368
13466
|
const dst = join6(targetDir, link.name);
|
|
13369
13467
|
try {
|
|
13370
|
-
|
|
13468
|
+
unlinkSync3(dst);
|
|
13371
13469
|
} catch {}
|
|
13372
13470
|
try {
|
|
13373
13471
|
symlinkSync(link.target, dst);
|
|
@@ -13478,7 +13576,7 @@ ${new Date().toISOString()}
|
|
|
13478
13576
|
}
|
|
13479
13577
|
log(`[onnx] reclaiming install lock (owner_pid=${owningPid ?? "unknown"}, alive=${ownerAlive}, age_ms=${age})`);
|
|
13480
13578
|
try {
|
|
13481
|
-
|
|
13579
|
+
unlinkSync3(lockPath);
|
|
13482
13580
|
} catch {}
|
|
13483
13581
|
return tryClaim();
|
|
13484
13582
|
}
|
|
@@ -13503,7 +13601,7 @@ function releaseLock(lockPath) {
|
|
|
13503
13601
|
return;
|
|
13504
13602
|
}
|
|
13505
13603
|
try {
|
|
13506
|
-
|
|
13604
|
+
unlinkSync3(lockPath);
|
|
13507
13605
|
} catch (unlinkErr) {
|
|
13508
13606
|
const code = unlinkErr.code;
|
|
13509
13607
|
if (code !== "ENOENT") {
|
|
@@ -14738,7 +14836,7 @@ function formatDuration(completion) {
|
|
|
14738
14836
|
|
|
14739
14837
|
// src/config.ts
|
|
14740
14838
|
var import_comment_json = __toESM(require_src2(), 1);
|
|
14741
|
-
import { existsSync as existsSync6, readFileSync as readFileSync4, renameSync as renameSync4, unlinkSync as
|
|
14839
|
+
import { existsSync as existsSync6, readFileSync as readFileSync4, renameSync as renameSync4, unlinkSync as unlinkSync4, writeFileSync as writeFileSync3 } from "node:fs";
|
|
14742
14840
|
import { homedir as homedir5 } from "node:os";
|
|
14743
14841
|
import { join as join8 } from "node:path";
|
|
14744
14842
|
|
|
@@ -28326,6 +28424,7 @@ var LspConfigSchema = exports_external.object({
|
|
|
28326
28424
|
servers: exports_external.record(exports_external.string().trim().min(1), LspServerEntrySchema).optional(),
|
|
28327
28425
|
disabled: exports_external.array(exports_external.string().trim().min(1)).optional(),
|
|
28328
28426
|
python: exports_external.enum(["pyright", "ty", "auto"]).optional(),
|
|
28427
|
+
diagnostics_on_edit: exports_external.boolean().optional(),
|
|
28329
28428
|
auto_install: exports_external.boolean().optional(),
|
|
28330
28429
|
grace_days: exports_external.number().int().positive().optional(),
|
|
28331
28430
|
versions: exports_external.record(exports_external.string().trim().min(1), exports_external.string().trim().min(1)).optional()
|
|
@@ -28349,6 +28448,24 @@ var BashFeaturesSchema = exports_external.object({
|
|
|
28349
28448
|
long_running_reminder_interval_ms: exports_external.number().int().positive().optional()
|
|
28350
28449
|
});
|
|
28351
28450
|
var BashConfigSchema = exports_external.union([exports_external.boolean(), BashFeaturesSchema]);
|
|
28451
|
+
var InspectConfigSchema = exports_external.object({
|
|
28452
|
+
enabled: exports_external.boolean().optional(),
|
|
28453
|
+
tier2_idle_minutes: exports_external.number().min(0).optional(),
|
|
28454
|
+
categories: exports_external.record(exports_external.string(), exports_external.boolean()).optional(),
|
|
28455
|
+
tier2_soft_deadline_ms: exports_external.number().int().positive().optional(),
|
|
28456
|
+
max_drill_down_items: exports_external.number().int().positive().max(100).optional(),
|
|
28457
|
+
duplicates: exports_external.object({
|
|
28458
|
+
lower_bound: exports_external.number().int().positive().optional(),
|
|
28459
|
+
discard_cost: exports_external.number().int().min(0).optional(),
|
|
28460
|
+
anonymize: exports_external.object({
|
|
28461
|
+
variables: exports_external.boolean().optional(),
|
|
28462
|
+
fields: exports_external.boolean().optional(),
|
|
28463
|
+
methods: exports_external.boolean().optional(),
|
|
28464
|
+
types: exports_external.boolean().optional(),
|
|
28465
|
+
literals: exports_external.boolean().optional()
|
|
28466
|
+
}).optional()
|
|
28467
|
+
}).optional()
|
|
28468
|
+
});
|
|
28352
28469
|
var AftConfigSchema = exports_external.object({
|
|
28353
28470
|
$schema: exports_external.string().optional(),
|
|
28354
28471
|
format_on_edit: exports_external.boolean().optional(),
|
|
@@ -28362,6 +28479,7 @@ var AftConfigSchema = exports_external.object({
|
|
|
28362
28479
|
restrict_to_project_root: exports_external.boolean().optional(),
|
|
28363
28480
|
search_index: exports_external.boolean().optional(),
|
|
28364
28481
|
semantic_search: exports_external.boolean().optional(),
|
|
28482
|
+
inspect: InspectConfigSchema.optional(),
|
|
28365
28483
|
bash: BashConfigSchema.optional(),
|
|
28366
28484
|
experimental: ExperimentalConfigSchema.optional(),
|
|
28367
28485
|
lsp: LspConfigSchema.optional(),
|
|
@@ -28438,6 +28556,8 @@ function resolveProjectOverridesForConfigure(config2) {
|
|
|
28438
28556
|
Object.assign(overrides, resolveLspConfigForConfigure(config2));
|
|
28439
28557
|
if (config2.semantic !== undefined)
|
|
28440
28558
|
overrides.semantic = config2.semantic;
|
|
28559
|
+
if (config2.inspect !== undefined)
|
|
28560
|
+
overrides.inspect = config2.inspect;
|
|
28441
28561
|
if (config2.max_callgraph_files !== undefined)
|
|
28442
28562
|
overrides.max_callgraph_files = config2.max_callgraph_files;
|
|
28443
28563
|
return overrides;
|
|
@@ -28653,7 +28773,7 @@ ${serialized}` : serialized;
|
|
|
28653
28773
|
} catch (err) {
|
|
28654
28774
|
if (tmpPath) {
|
|
28655
28775
|
try {
|
|
28656
|
-
|
|
28776
|
+
unlinkSync4(tmpPath);
|
|
28657
28777
|
} catch {}
|
|
28658
28778
|
}
|
|
28659
28779
|
if (isWritableMigrationError(err)) {
|
|
@@ -28746,6 +28866,9 @@ function mergeLspConfig(baseLsp, overrideLsp) {
|
|
|
28746
28866
|
const projectLsp = {};
|
|
28747
28867
|
if (overrideLsp?.python !== undefined)
|
|
28748
28868
|
projectLsp.python = overrideLsp.python;
|
|
28869
|
+
if (overrideLsp?.diagnostics_on_edit !== undefined) {
|
|
28870
|
+
projectLsp.diagnostics_on_edit = overrideLsp.diagnostics_on_edit;
|
|
28871
|
+
}
|
|
28749
28872
|
const userDisabled = baseLsp?.disabled ?? [];
|
|
28750
28873
|
const lsp = {
|
|
28751
28874
|
...baseLsp,
|
|
@@ -28757,6 +28880,27 @@ function mergeLspConfig(baseLsp, overrideLsp) {
|
|
|
28757
28880
|
}
|
|
28758
28881
|
return Object.fromEntries(Object.entries(lsp).filter(([, value]) => value !== undefined));
|
|
28759
28882
|
}
|
|
28883
|
+
function mergeInspectConfig(baseInspect, overrideInspect) {
|
|
28884
|
+
const inspect = {
|
|
28885
|
+
...baseInspect,
|
|
28886
|
+
...overrideInspect,
|
|
28887
|
+
duplicates: baseInspect?.duplicates || overrideInspect?.duplicates ? {
|
|
28888
|
+
...baseInspect?.duplicates,
|
|
28889
|
+
...overrideInspect?.duplicates,
|
|
28890
|
+
anonymize: baseInspect?.duplicates?.anonymize || overrideInspect?.duplicates?.anonymize ? {
|
|
28891
|
+
...baseInspect?.duplicates?.anonymize,
|
|
28892
|
+
...overrideInspect?.duplicates?.anonymize
|
|
28893
|
+
} : undefined
|
|
28894
|
+
} : undefined
|
|
28895
|
+
};
|
|
28896
|
+
if (inspect.duplicates && inspect.duplicates.anonymize === undefined) {
|
|
28897
|
+
delete inspect.duplicates.anonymize;
|
|
28898
|
+
}
|
|
28899
|
+
if (Object.values(inspect).every((value) => value === undefined)) {
|
|
28900
|
+
return;
|
|
28901
|
+
}
|
|
28902
|
+
return Object.fromEntries(Object.entries(inspect).filter(([, value]) => value !== undefined));
|
|
28903
|
+
}
|
|
28760
28904
|
function mergeBashConfig(baseBash, overrideBash) {
|
|
28761
28905
|
if (baseBash === undefined && overrideBash === undefined)
|
|
28762
28906
|
return;
|
|
@@ -28816,6 +28960,7 @@ var PROJECT_SAFE_TOP_LEVEL_FIELDS = new Set([
|
|
|
28816
28960
|
"validate_on_edit",
|
|
28817
28961
|
"search_index",
|
|
28818
28962
|
"semantic_search",
|
|
28963
|
+
"inspect",
|
|
28819
28964
|
"experimental",
|
|
28820
28965
|
"bash"
|
|
28821
28966
|
]);
|
|
@@ -28848,8 +28993,10 @@ function mergeConfigs(base, override) {
|
|
|
28848
28993
|
const lsp = mergeLspConfig(base.lsp, override.lsp);
|
|
28849
28994
|
const experimental = mergeExperimentalConfig(base.experimental, override.experimental);
|
|
28850
28995
|
const bash = mergeBashConfig(base.bash, override.bash);
|
|
28996
|
+
const inspect = mergeInspectConfig(base.inspect, override.inspect);
|
|
28851
28997
|
const safeOverride = pickProjectSafeFields(override);
|
|
28852
28998
|
delete safeOverride.bash;
|
|
28999
|
+
delete safeOverride.inspect;
|
|
28853
29000
|
return {
|
|
28854
29001
|
...base,
|
|
28855
29002
|
...safeOverride,
|
|
@@ -28857,6 +29004,7 @@ function mergeConfigs(base, override) {
|
|
|
28857
29004
|
...Object.keys(checker).length > 0 ? { checker } : {},
|
|
28858
29005
|
...lsp ? { lsp } : {},
|
|
28859
29006
|
...bash !== undefined ? { bash } : {},
|
|
29007
|
+
...inspect !== undefined ? { inspect } : {},
|
|
28860
29008
|
experimental,
|
|
28861
29009
|
semantic,
|
|
28862
29010
|
...disabledTools.length > 0 ? { disabled_tools: [...new Set(disabledTools)] } : {}
|
|
@@ -29603,7 +29751,7 @@ import {
|
|
|
29603
29751
|
openSync as openSync4,
|
|
29604
29752
|
readFileSync as readFileSync8,
|
|
29605
29753
|
statSync as statSync4,
|
|
29606
|
-
unlinkSync as
|
|
29754
|
+
unlinkSync as unlinkSync5,
|
|
29607
29755
|
writeFileSync as writeFileSync7
|
|
29608
29756
|
} from "node:fs";
|
|
29609
29757
|
import { homedir as homedir8 } from "node:os";
|
|
@@ -29733,7 +29881,7 @@ ${new Date().toISOString()}
|
|
|
29733
29881
|
}
|
|
29734
29882
|
log2(`[lsp] reclaiming install lock for ${lockKey} (owner_pid=${owningPid ?? "unknown"}, alive=${ownerAlive}, age_ms=${age})`);
|
|
29735
29883
|
try {
|
|
29736
|
-
|
|
29884
|
+
unlinkSync5(lock);
|
|
29737
29885
|
} catch {}
|
|
29738
29886
|
return tryClaim();
|
|
29739
29887
|
}
|
|
@@ -29770,7 +29918,7 @@ function releaseInstallLock(lockKey) {
|
|
|
29770
29918
|
return;
|
|
29771
29919
|
}
|
|
29772
29920
|
try {
|
|
29773
|
-
|
|
29921
|
+
unlinkSync5(lock);
|
|
29774
29922
|
} catch (unlinkErr) {
|
|
29775
29923
|
const code = unlinkErr.code;
|
|
29776
29924
|
if (code !== "ENOENT") {
|
|
@@ -30427,7 +30575,7 @@ function runAutoInstall(projectRoot, config2, fetchImpl = fetch) {
|
|
|
30427
30575
|
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
30428
30576
|
import { createHash as createHash5, randomBytes } from "node:crypto";
|
|
30429
30577
|
import {
|
|
30430
|
-
copyFileSync as
|
|
30578
|
+
copyFileSync as copyFileSync4,
|
|
30431
30579
|
createReadStream as createReadStream2,
|
|
30432
30580
|
createWriteStream as createWriteStream3,
|
|
30433
30581
|
existsSync as existsSync11,
|
|
@@ -30440,7 +30588,7 @@ import {
|
|
|
30440
30588
|
renameSync as renameSync7,
|
|
30441
30589
|
rmSync as rmSync6,
|
|
30442
30590
|
statSync as statSync6,
|
|
30443
|
-
unlinkSync as
|
|
30591
|
+
unlinkSync as unlinkSync6,
|
|
30444
30592
|
writeFileSync as writeFileSync8
|
|
30445
30593
|
} from "node:fs";
|
|
30446
30594
|
import { dirname as dirname7, join as join15, relative as relative2, resolve as resolve3 } from "node:path";
|
|
@@ -30802,7 +30950,7 @@ async function downloadFile(url2, destPath, fetchImpl, assetSize, signal) {
|
|
|
30802
30950
|
await pipeline3(nodeStream, createWriteStream3(destPath), { signal: timeout.signal });
|
|
30803
30951
|
} catch (err) {
|
|
30804
30952
|
try {
|
|
30805
|
-
|
|
30953
|
+
unlinkSync6(destPath);
|
|
30806
30954
|
} catch {}
|
|
30807
30955
|
throw err;
|
|
30808
30956
|
} finally {
|
|
@@ -31034,7 +31182,7 @@ async function downloadAndInstall(spec, tag, assets, platform2, arch, fetchImpl,
|
|
|
31034
31182
|
} catch (err) {
|
|
31035
31183
|
error2(`[lsp] hash ${spec.id} failed: ${err}`);
|
|
31036
31184
|
try {
|
|
31037
|
-
|
|
31185
|
+
unlinkSync6(archivePath);
|
|
31038
31186
|
} catch {}
|
|
31039
31187
|
return null;
|
|
31040
31188
|
}
|
|
@@ -31045,7 +31193,7 @@ async function downloadAndInstall(spec, tag, assets, platform2, arch, fetchImpl,
|
|
|
31045
31193
|
if (previousArchiveSha256 !== archiveSha256) {
|
|
31046
31194
|
error2(`[lsp] ${spec.id} ${tag}: TOFU sha256 mismatch — refusing install. ` + `Previously installed archive sha256=${previousArchiveSha256}, downloaded sha256=${archiveSha256}. ` + `This means the published release for tag ${tag} changed. Investigate before proceeding. ` + `Run \`aft doctor --clear\` to wipe the cache and force a fresh install if you've verified the change.`);
|
|
31047
31195
|
try {
|
|
31048
|
-
|
|
31196
|
+
unlinkSync6(archivePath);
|
|
31049
31197
|
} catch {}
|
|
31050
31198
|
return null;
|
|
31051
31199
|
}
|
|
@@ -31057,7 +31205,7 @@ async function downloadAndInstall(spec, tag, assets, platform2, arch, fetchImpl,
|
|
|
31057
31205
|
return null;
|
|
31058
31206
|
} finally {
|
|
31059
31207
|
try {
|
|
31060
|
-
|
|
31208
|
+
unlinkSync6(archivePath);
|
|
31061
31209
|
} catch {}
|
|
31062
31210
|
}
|
|
31063
31211
|
const innerBinaryPath = join15(extractDir, spec.binaryPathInArchive(platform2, arch, version2));
|
|
@@ -31068,7 +31216,7 @@ async function downloadAndInstall(spec, tag, assets, platform2, arch, fetchImpl,
|
|
|
31068
31216
|
const targetBinary = ghBinaryPath(spec, platform2);
|
|
31069
31217
|
mkdirSync9(dirname7(targetBinary), { recursive: true });
|
|
31070
31218
|
try {
|
|
31071
|
-
|
|
31219
|
+
copyFileSync4(innerBinaryPath, targetBinary);
|
|
31072
31220
|
if (platform2 !== "win32") {
|
|
31073
31221
|
const { chmodSync: chmodSync4 } = await import("node:fs");
|
|
31074
31222
|
chmodSync4(targetBinary, 493);
|
|
@@ -31426,13 +31574,21 @@ async function getSessionMessages(client, sessionId) {
|
|
|
31426
31574
|
async function sendIgnoredMessage(client, sessionId, text) {
|
|
31427
31575
|
try {
|
|
31428
31576
|
const c = client;
|
|
31429
|
-
const
|
|
31577
|
+
const promptContext = await resolvePromptContext(c, sessionId);
|
|
31430
31578
|
const body = {
|
|
31431
31579
|
noReply: true,
|
|
31432
31580
|
parts: [{ type: "text", text, ignored: true }]
|
|
31433
31581
|
};
|
|
31434
|
-
if (agent)
|
|
31435
|
-
body.agent = agent;
|
|
31582
|
+
if (promptContext?.agent)
|
|
31583
|
+
body.agent = promptContext.agent;
|
|
31584
|
+
if (promptContext?.model) {
|
|
31585
|
+
body.model = {
|
|
31586
|
+
providerID: promptContext.model.providerID,
|
|
31587
|
+
modelID: promptContext.model.modelID
|
|
31588
|
+
};
|
|
31589
|
+
}
|
|
31590
|
+
if (promptContext?.variant)
|
|
31591
|
+
body.variant = promptContext.variant;
|
|
31436
31592
|
const promptInput = {
|
|
31437
31593
|
path: { id: sessionId },
|
|
31438
31594
|
body
|
|
@@ -31450,14 +31606,6 @@ async function sendIgnoredMessage(client, sessionId, text) {
|
|
|
31450
31606
|
}
|
|
31451
31607
|
return false;
|
|
31452
31608
|
}
|
|
31453
|
-
async function resolveCurrentAgent(client, sessionId) {
|
|
31454
|
-
try {
|
|
31455
|
-
const ctx = await resolvePromptContext(client, sessionId);
|
|
31456
|
-
return ctx?.agent;
|
|
31457
|
-
} catch {
|
|
31458
|
-
return;
|
|
31459
|
-
}
|
|
31460
|
-
}
|
|
31461
31609
|
async function deleteMessage(serverUrl, sessionId, messageId) {
|
|
31462
31610
|
const auth = getServerAuth();
|
|
31463
31611
|
const url2 = `${serverUrl}/session/${encodeURIComponent(sessionId)}/message/${encodeURIComponent(messageId)}`;
|
|
@@ -31534,13 +31682,18 @@ function persistAnnouncedVersion(storageDir, version2) {
|
|
|
31534
31682
|
markAnnouncementSeen(storageDir, "opencode", version2);
|
|
31535
31683
|
}
|
|
31536
31684
|
async function readWarnedTools(bridge) {
|
|
31685
|
+
let resp;
|
|
31686
|
+
try {
|
|
31687
|
+
resp = await bridge.send("db_get_state", { key: "warned_tools" });
|
|
31688
|
+
} catch {
|
|
31689
|
+
return null;
|
|
31690
|
+
}
|
|
31691
|
+
if (resp.success === false)
|
|
31692
|
+
return null;
|
|
31693
|
+
const value = resp.data?.value;
|
|
31694
|
+
if (typeof value !== "string")
|
|
31695
|
+
return {};
|
|
31537
31696
|
try {
|
|
31538
|
-
const resp = await bridge.send("db_get_state", { key: "warned_tools" });
|
|
31539
|
-
if (resp.success === false)
|
|
31540
|
-
return {};
|
|
31541
|
-
const value = resp.data?.value;
|
|
31542
|
-
if (typeof value !== "string")
|
|
31543
|
-
return {};
|
|
31544
31697
|
const parsed = JSON.parse(value);
|
|
31545
31698
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
31546
31699
|
return {};
|
|
@@ -31549,12 +31702,16 @@ async function readWarnedTools(bridge) {
|
|
|
31549
31702
|
return {};
|
|
31550
31703
|
}
|
|
31551
31704
|
}
|
|
31552
|
-
async function
|
|
31705
|
+
async function warnedStatus(bridge, key) {
|
|
31553
31706
|
const warned = await readWarnedTools(bridge);
|
|
31554
|
-
|
|
31707
|
+
if (warned === null)
|
|
31708
|
+
return "unknown";
|
|
31709
|
+
return warned[key] === true || typeof warned[key] === "string" ? "warned" : "fresh";
|
|
31555
31710
|
}
|
|
31556
31711
|
async function recordWarning(bridge, key) {
|
|
31557
31712
|
const warned = await readWarnedTools(bridge);
|
|
31713
|
+
if (warned === null)
|
|
31714
|
+
return;
|
|
31558
31715
|
warned[key] = true;
|
|
31559
31716
|
try {
|
|
31560
31717
|
await bridge.send("db_set_state", {
|
|
@@ -31606,7 +31763,8 @@ async function deliverConfigureWarnings(opts, warnings) {
|
|
|
31606
31763
|
return;
|
|
31607
31764
|
for (const warning of warnings) {
|
|
31608
31765
|
const key = warningKey(warning, opts.projectRoot);
|
|
31609
|
-
|
|
31766
|
+
const status = await warnedStatus(opts.bridge, key);
|
|
31767
|
+
if (status !== "fresh")
|
|
31610
31768
|
continue;
|
|
31611
31769
|
const delivered = await sendIgnoredMessage(opts.client, opts.sessionId, formatConfigureWarning(warning));
|
|
31612
31770
|
if (!delivered)
|
|
@@ -31755,7 +31913,7 @@ function writeTerminal(terminal, data) {
|
|
|
31755
31913
|
|
|
31756
31914
|
// src/shared/rpc-server.ts
|
|
31757
31915
|
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
31758
|
-
import { mkdirSync as mkdirSync10, renameSync as renameSync8, unlinkSync as
|
|
31916
|
+
import { mkdirSync as mkdirSync10, renameSync as renameSync8, unlinkSync as unlinkSync7, writeFileSync as writeFileSync9 } from "node:fs";
|
|
31759
31917
|
import { createServer } from "node:http";
|
|
31760
31918
|
import { dirname as dirname8, join as join18 } from "node:path";
|
|
31761
31919
|
|
|
@@ -31829,7 +31987,7 @@ class AftRpcServer {
|
|
|
31829
31987
|
}
|
|
31830
31988
|
this.token = null;
|
|
31831
31989
|
try {
|
|
31832
|
-
|
|
31990
|
+
unlinkSync7(this.portFilePath);
|
|
31833
31991
|
} catch {}
|
|
31834
31992
|
}
|
|
31835
31993
|
dispatch(req, res) {
|
|
@@ -32215,24 +32373,28 @@ function getState() {
|
|
|
32215
32373
|
}
|
|
32216
32374
|
return g[GLOBAL_KEY];
|
|
32217
32375
|
}
|
|
32218
|
-
var
|
|
32376
|
+
var runningCleanups = false;
|
|
32219
32377
|
async function runCleanups(reason) {
|
|
32220
|
-
if (
|
|
32221
|
-
return;
|
|
32222
|
-
shuttingDown = true;
|
|
32223
|
-
const state = getState();
|
|
32224
|
-
if (state.cleanups.size === 0)
|
|
32378
|
+
if (runningCleanups)
|
|
32225
32379
|
return;
|
|
32226
|
-
|
|
32227
|
-
|
|
32228
|
-
|
|
32229
|
-
|
|
32230
|
-
|
|
32231
|
-
|
|
32232
|
-
|
|
32233
|
-
|
|
32234
|
-
|
|
32235
|
-
|
|
32380
|
+
runningCleanups = true;
|
|
32381
|
+
try {
|
|
32382
|
+
const state = getState();
|
|
32383
|
+
if (state.cleanups.size === 0)
|
|
32384
|
+
return;
|
|
32385
|
+
log2(`Shutdown triggered by ${reason} — running ${state.cleanups.size} cleanup(s)`);
|
|
32386
|
+
const cleanups = Array.from(state.cleanups);
|
|
32387
|
+
state.cleanups.clear();
|
|
32388
|
+
await Promise.allSettled(cleanups.map(async (fn) => {
|
|
32389
|
+
try {
|
|
32390
|
+
await fn();
|
|
32391
|
+
} catch (err) {
|
|
32392
|
+
log2(`Cleanup error: ${err.message}`);
|
|
32393
|
+
}
|
|
32394
|
+
}));
|
|
32395
|
+
} finally {
|
|
32396
|
+
runningCleanups = false;
|
|
32397
|
+
}
|
|
32236
32398
|
}
|
|
32237
32399
|
function installProcessHandlers() {
|
|
32238
32400
|
const state = getState();
|
|
@@ -33082,7 +33244,14 @@ function applyUpdateChunks(originalContent, filePath, chunks) {
|
|
|
33082
33244
|
lineIndex = contextIdx + 1;
|
|
33083
33245
|
}
|
|
33084
33246
|
if (chunk.old_lines.length === 0) {
|
|
33085
|
-
|
|
33247
|
+
let insertionIdx;
|
|
33248
|
+
if (chunk.change_context) {
|
|
33249
|
+
insertionIdx = lineIndex;
|
|
33250
|
+
} else if (originalLines.length > 0 && originalLines[originalLines.length - 1] === "") {
|
|
33251
|
+
insertionIdx = originalLines.length - 1;
|
|
33252
|
+
} else {
|
|
33253
|
+
insertionIdx = originalLines.length;
|
|
33254
|
+
}
|
|
33086
33255
|
replacements.push([insertionIdx, 0, chunk.new_lines]);
|
|
33087
33256
|
continue;
|
|
33088
33257
|
}
|
|
@@ -34067,6 +34236,10 @@ function inferBeforeStart(ops, from, beforeLen) {
|
|
|
34067
34236
|
return beforeLen;
|
|
34068
34237
|
}
|
|
34069
34238
|
var z7 = tool7.schema;
|
|
34239
|
+
var DIAGNOSTICS_PARAM_DESCRIPTION = "When true, wait up to 3 seconds for fresh LSP diagnostics on the edited file and include them in the result. Defaults to the configured `lsp.diagnostics_on_edit` value (false unless configured); per-call true/false overrides. Use aft_inspect to check diagnostics across a batch of edits or before tests/commits.";
|
|
34240
|
+
function diagnosticsOnEditDefault(ctx) {
|
|
34241
|
+
return ctx.config.lsp?.diagnostics_on_edit ?? false;
|
|
34242
|
+
}
|
|
34070
34243
|
var READ_DESCRIPTION = `Read file contents or list directory entries.
|
|
34071
34244
|
|
|
34072
34245
|
Use either startLine/endLine OR offset/limit to read a section of a file.
|
|
@@ -34084,8 +34257,7 @@ Examples:
|
|
|
34084
34257
|
Read lines 50-100: { "filePath": "src/app.ts", "startLine": 50, "endLine": 100 }
|
|
34085
34258
|
Read 30 lines from line 200: { "filePath": "src/app.ts", "offset": 200, "limit": 30 }
|
|
34086
34259
|
List directory: { "filePath": "src/" }
|
|
34087
|
-
|
|
34088
|
-
Returns: Line-numbered file content string. For directories: newline-joined sorted entries. For binary files: size/message string.`;
|
|
34260
|
+
`;
|
|
34089
34261
|
function createReadTool(ctx) {
|
|
34090
34262
|
return {
|
|
34091
34263
|
description: READ_DESCRIPTION,
|
|
@@ -34210,24 +34382,26 @@ function getWriteDescription(editToolName) {
|
|
|
34210
34382
|
|
|
34211
34383
|
Automatically creates parent directories. Backs up existing files before overwriting.
|
|
34212
34384
|
If the project has a formatter configured (biome, prettier, rustfmt, etc.), the file
|
|
34213
|
-
is auto-formatted after writing.
|
|
34385
|
+
is auto-formatted after writing. Edits return as soon as the write completes unless
|
|
34386
|
+
the configured \`lsp.diagnostics_on_edit\` default or a per-call \`diagnostics: true\`
|
|
34387
|
+
asks for legacy sync-wait behavior. Call \`aft_inspect\` afterward to check
|
|
34388
|
+
diagnostics across a batch of edits.
|
|
34214
34389
|
|
|
34215
34390
|
**Behavior:**
|
|
34216
34391
|
- Creates parent directories automatically (no need to mkdir first)
|
|
34217
34392
|
- Existing files are backed up before overwriting (recoverable via aft_safety undo)
|
|
34218
34393
|
- Auto-formats using project formatter if configured (biome.json, .prettierrc, etc.)
|
|
34219
|
-
-
|
|
34394
|
+
- LSP diagnostics follow \`lsp.diagnostics_on_edit\` by default; pass \`diagnostics\` to override per call
|
|
34220
34395
|
- Use this for creating new files or completely replacing file contents
|
|
34221
|
-
- For partial edits (find/replace), use the \`${editToolName}\` tool instead
|
|
34222
|
-
|
|
34223
|
-
Returns: Status message string (for example: "Created new file. Auto-formatted.") with optional inline LSP error lines.`;
|
|
34396
|
+
- For partial edits (find/replace), use the \`${editToolName}\` tool instead`;
|
|
34224
34397
|
}
|
|
34225
34398
|
function createWriteTool(ctx, editToolName = "edit") {
|
|
34226
34399
|
return {
|
|
34227
34400
|
description: getWriteDescription(editToolName),
|
|
34228
34401
|
args: {
|
|
34229
34402
|
filePath: z7.string().describe("Path to the file to write (absolute or relative to project root)"),
|
|
34230
|
-
content: z7.string().describe("The full content to write to the file")
|
|
34403
|
+
content: z7.string().describe("The full content to write to the file"),
|
|
34404
|
+
diagnostics: z7.boolean().optional().describe(DIAGNOSTICS_PARAM_DESCRIPTION)
|
|
34231
34405
|
},
|
|
34232
34406
|
execute: async (args, context) => {
|
|
34233
34407
|
const file2 = args.filePath;
|
|
@@ -34249,8 +34423,8 @@ function createWriteTool(ctx, editToolName = "edit") {
|
|
|
34249
34423
|
file: filePath,
|
|
34250
34424
|
content,
|
|
34251
34425
|
create_dirs: true,
|
|
34252
|
-
diagnostics:
|
|
34253
|
-
|
|
34426
|
+
diagnostics: args.diagnostics ?? diagnosticsOnEditDefault(ctx),
|
|
34427
|
+
include_diff_content: true
|
|
34254
34428
|
});
|
|
34255
34429
|
if (data.success === false) {
|
|
34256
34430
|
throw new Error(data.message || "write failed");
|
|
@@ -34280,7 +34454,7 @@ LSP errors detected, please fix:
|
|
|
34280
34454
|
if (pendingServers && pendingServers.length > 0) {
|
|
34281
34455
|
output += `
|
|
34282
34456
|
|
|
34283
|
-
Note: LSP server(s) did not respond in time: ${pendingServers.join(", ")}. Diagnostics may be incomplete;
|
|
34457
|
+
Note: LSP server(s) did not respond in time: ${pendingServers.join(", ")}. Diagnostics may be incomplete; call aft_inspect for a checkpoint diagnostics snapshot.`;
|
|
34284
34458
|
}
|
|
34285
34459
|
if (exitedServers && exitedServers.length > 0) {
|
|
34286
34460
|
output += `
|
|
@@ -34362,11 +34536,8 @@ Note: Modes 6 and 7 are options on mode 5 (find/replace) — they require \`oldS
|
|
|
34362
34536
|
- Auto-formats using project formatter if configured
|
|
34363
34537
|
- Tree-sitter syntax validation on all edits
|
|
34364
34538
|
- Symbol replace includes decorators, attributes, and doc comments in range
|
|
34365
|
-
-
|
|
34366
|
-
|
|
34367
|
-
Returns: JSON string for the selected edit mode. Edits may append inline LSP error lines.
|
|
34368
|
-
|
|
34369
|
-
Common response fields: success (boolean), diff (object with before/after), backup_id (string), syntax_valid (boolean). Exact fields vary by mode.`;
|
|
34539
|
+
- Edits return as soon as the write completes unless \`lsp.diagnostics_on_edit\` or a per-call \`diagnostics: true\` requests legacy sync-wait behavior. Call \`aft_inspect\` afterward to check diagnostics across a batch of edits.
|
|
34540
|
+
- Response is a JSON string for the selected edit mode; key fields include success, diff, backup_id, syntax_valid, and mode-specific fields.`;
|
|
34370
34541
|
}
|
|
34371
34542
|
function createEditTool(ctx, writeToolName = "write") {
|
|
34372
34543
|
return {
|
|
@@ -34381,7 +34552,8 @@ function createEditTool(ctx, writeToolName = "write") {
|
|
|
34381
34552
|
content: z7.string().optional().describe("Replacement content for symbol mode or operations[].command='write'. For whole-file writes, use the `write` tool."),
|
|
34382
34553
|
appendContent: z7.string().optional().describe("Text to append to the end of filePath; creates the file if needed"),
|
|
34383
34554
|
edits: z7.array(z7.record(z7.string(), z7.unknown())).optional().describe("Batch edits — array of { oldString: string, newString: string } or { startLine: number (1-based), endLine: number (1-based, inclusive), content: string }"),
|
|
34384
|
-
operations: z7.array(z7.record(z7.string(), z7.unknown())).optional().describe("Transaction — array of { file: string, command: 'edit_match' | 'write', match?: string, replacement?: string, content?: string } for multi-file edits with rollback. Note: uses 'file'/'match'/'replacement' (not filePath/oldString/newString)")
|
|
34555
|
+
operations: z7.array(z7.record(z7.string(), z7.unknown())).optional().describe("Transaction — array of { file: string, command: 'edit_match' | 'write', match?: string, replacement?: string, content?: string } for multi-file edits with rollback. Note: uses 'file'/'match'/'replacement' (not filePath/oldString/newString)"),
|
|
34556
|
+
diagnostics: z7.boolean().optional().describe(DIAGNOSTICS_PARAM_DESCRIPTION)
|
|
34385
34557
|
},
|
|
34386
34558
|
execute: async (args, context) => {
|
|
34387
34559
|
const argsRecord = args;
|
|
@@ -34477,8 +34649,8 @@ function createEditTool(ctx, writeToolName = "write") {
|
|
|
34477
34649
|
const hint = typeof args.content === "string" ? ` To write the whole file, use the '${writeToolName}' tool. To edit existing content, provide 'oldString' (and optionally 'newString'), 'symbol' + 'content', or an 'edits' array.` : " Provide 'oldString' (+ optional 'newString'), 'symbol' + 'content', 'edits' array, or 'operations' array.";
|
|
34478
34650
|
throw new Error(`edit: no edit mode resolved from arguments.${hint}`);
|
|
34479
34651
|
}
|
|
34480
|
-
params.diagnostics =
|
|
34481
|
-
params.
|
|
34652
|
+
params.diagnostics = args.diagnostics ?? diagnosticsOnEditDefault(ctx);
|
|
34653
|
+
params.include_diff_content = true;
|
|
34482
34654
|
const data = await callBridge(ctx, context, command, params);
|
|
34483
34655
|
if (data.success && data.diff) {
|
|
34484
34656
|
const diff = data.diff;
|
|
@@ -34503,7 +34675,12 @@ function createEditTool(ctx, writeToolName = "write") {
|
|
|
34503
34675
|
});
|
|
34504
34676
|
}
|
|
34505
34677
|
}
|
|
34506
|
-
|
|
34678
|
+
const agentData = { ...data };
|
|
34679
|
+
if (agentData.diff && typeof agentData.diff === "object") {
|
|
34680
|
+
const d = agentData.diff;
|
|
34681
|
+
agentData.diff = { additions: d.additions ?? 0, deletions: d.deletions ?? 0 };
|
|
34682
|
+
}
|
|
34683
|
+
let result = JSON.stringify(agentData);
|
|
34507
34684
|
const globSkipNote = formatGlobSkipReasonsNote(data.format_skip_reasons);
|
|
34508
34685
|
if (globSkipNote)
|
|
34509
34686
|
result += `
|
|
@@ -34531,7 +34708,7 @@ ${diagLines}`;
|
|
|
34531
34708
|
if (pendingServers && pendingServers.length > 0) {
|
|
34532
34709
|
result += `
|
|
34533
34710
|
|
|
34534
|
-
Note: LSP server(s) did not respond in time: ${pendingServers.join(", ")}. Diagnostics may be incomplete;
|
|
34711
|
+
Note: LSP server(s) did not respond in time: ${pendingServers.join(", ")}. Diagnostics may be incomplete; call aft_inspect for a checkpoint diagnostics snapshot.`;
|
|
34535
34712
|
}
|
|
34536
34713
|
if (exitedServers && exitedServers.length > 0) {
|
|
34537
34714
|
result += `
|
|
@@ -34592,15 +34769,17 @@ Example patch:
|
|
|
34592
34769
|
- You must include a header with your intended action (Add/Delete/Update)
|
|
34593
34770
|
- You must prefix new lines with \`+\` even when creating a new file
|
|
34594
34771
|
|
|
34595
|
-
|
|
34772
|
+
Edits return as soon as the write completes unless \`lsp.diagnostics_on_edit\` or a per-call \`diagnostics: true\` requests legacy sync-wait behavior. Call \`aft_inspect\` afterward to check diagnostics across a batch of edits.`;
|
|
34596
34773
|
function createApplyPatchTool(ctx) {
|
|
34597
34774
|
return {
|
|
34598
34775
|
description: APPLY_PATCH_DESCRIPTION,
|
|
34599
34776
|
args: {
|
|
34600
|
-
patchText: z7.string().describe("The full patch text including Begin/End markers")
|
|
34777
|
+
patchText: z7.string().describe("The full patch text including Begin/End markers"),
|
|
34778
|
+
diagnostics: z7.boolean().optional().describe(DIAGNOSTICS_PARAM_DESCRIPTION)
|
|
34601
34779
|
},
|
|
34602
34780
|
execute: async (args, context) => {
|
|
34603
34781
|
const patchText = args.patchText;
|
|
34782
|
+
const diagnostics = args.diagnostics ?? diagnosticsOnEditDefault(ctx);
|
|
34604
34783
|
if (!patchText)
|
|
34605
34784
|
throw new Error("'patchText' is required");
|
|
34606
34785
|
let hunks;
|
|
@@ -34680,8 +34859,8 @@ function createApplyPatchTool(ctx) {
|
|
|
34680
34859
|
file: filePath,
|
|
34681
34860
|
content,
|
|
34682
34861
|
create_dirs: true,
|
|
34683
|
-
diagnostics
|
|
34684
|
-
|
|
34862
|
+
diagnostics,
|
|
34863
|
+
include_diff_content: true,
|
|
34685
34864
|
multi_file_write_paths: multiFileWritePaths
|
|
34686
34865
|
});
|
|
34687
34866
|
const wrDiff = writeResult.diff;
|
|
@@ -34700,7 +34879,7 @@ function createApplyPatchTool(ctx) {
|
|
|
34700
34879
|
const filePath2 = path4.resolve(context.directory, hunk.path);
|
|
34701
34880
|
if (fs6.existsSync(filePath2)) {
|
|
34702
34881
|
try {
|
|
34703
|
-
|
|
34882
|
+
fs6.rmSync(filePath2, { force: true });
|
|
34704
34883
|
} catch {}
|
|
34705
34884
|
}
|
|
34706
34885
|
}
|
|
@@ -34733,8 +34912,8 @@ function createApplyPatchTool(ctx) {
|
|
|
34733
34912
|
file: targetPath,
|
|
34734
34913
|
content: newContent,
|
|
34735
34914
|
create_dirs: true,
|
|
34736
|
-
diagnostics
|
|
34737
|
-
|
|
34915
|
+
diagnostics,
|
|
34916
|
+
include_diff_content: true,
|
|
34738
34917
|
multi_file_write_paths: multiFileWritePaths
|
|
34739
34918
|
});
|
|
34740
34919
|
const diags = writeResult.lsp_diagnostics;
|
|
@@ -35007,7 +35186,7 @@ function aftPrefixedTools(ctx) {
|
|
|
35007
35186
|
file: filePath,
|
|
35008
35187
|
content: normalizedArgs.content,
|
|
35009
35188
|
create_dirs: normalizedArgs.create_dirs !== false,
|
|
35010
|
-
diagnostics:
|
|
35189
|
+
diagnostics: normalizedArgs.diagnostics ?? diagnosticsOnEditDefault(ctx)
|
|
35011
35190
|
};
|
|
35012
35191
|
const response = await callBridge(ctx, context, "write", writeParams);
|
|
35013
35192
|
if (response.success === false) {
|
|
@@ -35096,44 +35275,169 @@ function importTools(ctx) {
|
|
|
35096
35275
|
};
|
|
35097
35276
|
}
|
|
35098
35277
|
|
|
35099
|
-
// src/tools/
|
|
35278
|
+
// src/tools/inspect.ts
|
|
35100
35279
|
import { tool as tool9 } from "@opencode-ai/plugin";
|
|
35101
35280
|
var z9 = tool9.schema;
|
|
35102
|
-
function
|
|
35103
|
-
|
|
35104
|
-
|
|
35105
|
-
|
|
35281
|
+
function asRecord2(value) {
|
|
35282
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
35283
|
+
return;
|
|
35284
|
+
return value;
|
|
35285
|
+
}
|
|
35286
|
+
function asNumber(value) {
|
|
35287
|
+
return typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
35288
|
+
}
|
|
35289
|
+
function asString(value) {
|
|
35290
|
+
return typeof value === "string" ? value : undefined;
|
|
35291
|
+
}
|
|
35292
|
+
function asStringArray(value) {
|
|
35293
|
+
return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
35294
|
+
}
|
|
35295
|
+
function diagnosticsServerSummary(section) {
|
|
35296
|
+
const pending = asStringArray(section.servers_pending);
|
|
35297
|
+
const notInstalled = asStringArray(section.servers_not_installed);
|
|
35298
|
+
const parts = [];
|
|
35299
|
+
if (pending.length > 0)
|
|
35300
|
+
parts.push(`pending: ${pending.join(", ")}`);
|
|
35301
|
+
if (notInstalled.length > 0)
|
|
35302
|
+
parts.push(`not installed: ${notInstalled.join(", ")}`);
|
|
35303
|
+
return parts.length > 0 ? parts.join("; ") : "none reported";
|
|
35304
|
+
}
|
|
35305
|
+
function formatDiagnosticsSummary(summary) {
|
|
35306
|
+
const section = asRecord2(summary?.diagnostics);
|
|
35307
|
+
if (!section)
|
|
35308
|
+
return;
|
|
35309
|
+
const errors3 = asNumber(section.errors);
|
|
35310
|
+
const warnings = asNumber(section.warnings);
|
|
35311
|
+
const info = asNumber(section.info);
|
|
35312
|
+
const hints = asNumber(section.hints);
|
|
35313
|
+
const hasCounts = [errors3, warnings, info, hints].some((value) => value !== undefined);
|
|
35314
|
+
const counts = `${errors3 ?? 0} errors, ${warnings ?? 0} warnings, ${info ?? 0} info, ${hints ?? 0} hints`;
|
|
35315
|
+
const status = asString(section.status);
|
|
35316
|
+
if (status === "pending") {
|
|
35317
|
+
return hasCounts ? `diagnostics: ${counts} so far — still pending (servers: ${diagnosticsServerSummary(section)})` : `diagnostics: pending (servers: ${diagnosticsServerSummary(section)})`;
|
|
35318
|
+
}
|
|
35319
|
+
if (status === "incomplete") {
|
|
35320
|
+
return hasCounts ? `diagnostics: ${counts} (incomplete — servers: ${diagnosticsServerSummary(section)})` : `diagnostics: unavailable (status incomplete; servers: ${diagnosticsServerSummary(section)})`;
|
|
35321
|
+
}
|
|
35322
|
+
if (hasCounts) {
|
|
35323
|
+
return `diagnostics: ${counts}`;
|
|
35324
|
+
}
|
|
35325
|
+
return;
|
|
35326
|
+
}
|
|
35327
|
+
function formatDiagnosticLocation(diagnostic) {
|
|
35328
|
+
const file2 = asString(diagnostic.file) ?? "(unknown file)";
|
|
35329
|
+
const line = asNumber(diagnostic.line);
|
|
35330
|
+
const column = asNumber(diagnostic.column);
|
|
35331
|
+
if (line === undefined)
|
|
35332
|
+
return file2;
|
|
35333
|
+
if (column === undefined)
|
|
35334
|
+
return `${file2}:${line}`;
|
|
35335
|
+
return `${file2}:${line}:${column}`;
|
|
35336
|
+
}
|
|
35337
|
+
function formatDiagnosticsDetails(details) {
|
|
35338
|
+
const diagnostics = Array.isArray(details?.diagnostics) ? details.diagnostics.map(asRecord2).filter(Boolean) : [];
|
|
35339
|
+
return diagnostics.map((diagnostic) => {
|
|
35340
|
+
const severity = asString(diagnostic.severity) ?? "information";
|
|
35341
|
+
const message = asString(diagnostic.message) ?? "(no message)";
|
|
35342
|
+
const source = asString(diagnostic.source);
|
|
35343
|
+
const suffix = source ? ` [${source}]` : "";
|
|
35344
|
+
return `${formatDiagnosticLocation(diagnostic)} ${severity} ${message}${suffix}`;
|
|
35345
|
+
});
|
|
35346
|
+
}
|
|
35347
|
+
function renderInspectDiagnostics(response) {
|
|
35348
|
+
const lines = [];
|
|
35349
|
+
const summaryLine = formatDiagnosticsSummary(asRecord2(response.summary));
|
|
35350
|
+
if (summaryLine)
|
|
35351
|
+
lines.push(summaryLine);
|
|
35352
|
+
const detailLines = formatDiagnosticsDetails(asRecord2(response.details));
|
|
35353
|
+
if (detailLines.length > 0) {
|
|
35354
|
+
lines.push("diagnostics details:", ...detailLines.map((line) => `- ${line}`));
|
|
35355
|
+
}
|
|
35356
|
+
return lines.join(`
|
|
35357
|
+
`);
|
|
35358
|
+
}
|
|
35359
|
+
function appendRenderedDiagnostics(text, response) {
|
|
35360
|
+
if (/^diagnostics[: ]/im.test(text))
|
|
35361
|
+
return text;
|
|
35362
|
+
const diagnostics = renderInspectDiagnostics(response);
|
|
35363
|
+
if (!diagnostics)
|
|
35364
|
+
return text;
|
|
35365
|
+
return text ? `${text}
|
|
35366
|
+
|
|
35367
|
+
${diagnostics}` : diagnostics;
|
|
35368
|
+
}
|
|
35369
|
+
function arg(schema) {
|
|
35370
|
+
return schema;
|
|
35371
|
+
}
|
|
35372
|
+
function normalizeStringOrArray(value) {
|
|
35373
|
+
return isEmptyParam(value) ? undefined : value;
|
|
35374
|
+
}
|
|
35375
|
+
function inspectToolSurfaceEnabled(config2) {
|
|
35376
|
+
return (config2.tool_surface ?? "recommended") !== "minimal" && config2.inspect?.enabled !== false;
|
|
35377
|
+
}
|
|
35378
|
+
function createInspectTier2IdleScheduler(options) {
|
|
35379
|
+
const timers = new Map;
|
|
35380
|
+
const setTimer = options.setTimer ?? ((callback, delayMs) => setTimeout(callback, delayMs));
|
|
35381
|
+
const clearTimer = options.clearTimer ?? ((timer) => clearTimeout(timer));
|
|
35382
|
+
const clear = (sessionID) => {
|
|
35383
|
+
const timer = timers.get(sessionID);
|
|
35384
|
+
if (!timer)
|
|
35385
|
+
return;
|
|
35386
|
+
clearTimer(timer);
|
|
35387
|
+
timers.delete(sessionID);
|
|
35388
|
+
};
|
|
35389
|
+
const clearAll = () => {
|
|
35390
|
+
for (const timer of timers.values()) {
|
|
35391
|
+
clearTimer(timer);
|
|
35392
|
+
}
|
|
35393
|
+
timers.clear();
|
|
35394
|
+
};
|
|
35395
|
+
const schedule = (sessionID) => {
|
|
35396
|
+
if (!options.isEnabled())
|
|
35397
|
+
return;
|
|
35398
|
+
clear(sessionID);
|
|
35399
|
+
const idleMinutes = options.idleMinutes() ?? 4;
|
|
35400
|
+
const delayMs = Math.max(0, idleMinutes * 60 * 1000);
|
|
35401
|
+
const timer = setTimer(() => {
|
|
35402
|
+
timers.delete(sessionID);
|
|
35403
|
+
options.run(sessionID).catch((err) => {
|
|
35404
|
+
options.warn?.(`inspect_tier2_run failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
35405
|
+
});
|
|
35406
|
+
}, delayMs);
|
|
35407
|
+
timers.set(sessionID, timer);
|
|
35408
|
+
};
|
|
35409
|
+
return { schedule, clear, clearAll };
|
|
35410
|
+
}
|
|
35411
|
+
function inspectTools(ctx) {
|
|
35412
|
+
const inspectTool = {
|
|
35413
|
+
description: "Codebase health snapshot. One call returns summary stats for: TODOs, diagnostics, file/symbol metrics, dead code, unused exports, code duplicates. Pass `sections` for per-category drill-down details.\n\n" + "Categories run in tiers — Tier 1 (todos, metrics) return synchronously from cache. Tier 2 (dead_code, unused_exports, duplicates) run as background scans triggered on session idle; calls may return cached `stale_categories: [...]` results or `pending_categories: [...]` while a refresh is in progress (waits up to 1s for fresh data before falling back to cached).\n\n" + `Use when: starting work on unfamiliar code, after multi-edit batches to check diagnostics, before a refactor, before review, or to verify cleanup completeness.
|
|
35414
|
+
|
|
35415
|
+
` + "Treat `dead_code` as a hint, not proof: reachability is call-based, so symbols reached only via method dispatch or referenced only in type position may be false positives — verify before deleting.",
|
|
35106
35416
|
args: {
|
|
35107
|
-
|
|
35108
|
-
|
|
35109
|
-
|
|
35110
|
-
waitMs: optionalInt(1, 1e4).describe("Wait up to N ms (max 10000) for push diagnostics. Push-only servers like bash-language-server and yaml-language-server — use after an edit.")
|
|
35417
|
+
sections: arg(z9.union([z9.string(), z9.array(z9.string())]).optional().describe("Categories to include in detailed drill-down (e.g. 'todos' or ['todos', 'dead_code']). Use 'all' for every active category. Omit for summary-only mode.")),
|
|
35418
|
+
scope: arg(z9.union([z9.string(), z9.array(z9.string())]).optional().describe("Restrict scan/results to paths under this scope (file or directory, absolute or relative to project root). Tier 1 scopes the scan; Tier 2 scans project-wide and applies scope as a result filter.")),
|
|
35419
|
+
topK: arg(z9.number().int().positive().max(100).optional().describe("Max drill-down items per category. Default 20, max 100."))
|
|
35111
35420
|
},
|
|
35112
35421
|
execute: async (args, context) => {
|
|
35113
|
-
const
|
|
35114
|
-
const
|
|
35115
|
-
|
|
35116
|
-
|
|
35117
|
-
|
|
35118
|
-
|
|
35119
|
-
if (filePath !== undefined)
|
|
35120
|
-
params.file = filePath;
|
|
35121
|
-
if (directory !== undefined)
|
|
35122
|
-
params.directory = directory;
|
|
35123
|
-
if (args.severity !== undefined)
|
|
35124
|
-
params.severity = args.severity;
|
|
35125
|
-
if (args.waitMs !== undefined)
|
|
35126
|
-
params.wait_ms = args.waitMs;
|
|
35127
|
-
const result = await callBridge(ctx, context, "lsp_diagnostics", params);
|
|
35128
|
-
if (result.success === false) {
|
|
35129
|
-
throw new Error(result.message || "lsp_diagnostics failed");
|
|
35422
|
+
const sections = normalizeStringOrArray(args.sections);
|
|
35423
|
+
const scope = normalizeStringOrArray(args.scope);
|
|
35424
|
+
const topK = args.topK === undefined || args.topK === null ? undefined : args.topK;
|
|
35425
|
+
const response = await callBridge(ctx, context, "inspect", { sections, scope, topK });
|
|
35426
|
+
if (response.success === false) {
|
|
35427
|
+
throw new Error(response.message || "inspect failed");
|
|
35130
35428
|
}
|
|
35131
|
-
|
|
35429
|
+
if (typeof response.text === "string") {
|
|
35430
|
+
return appendRenderedDiagnostics(response.text, response);
|
|
35431
|
+
}
|
|
35432
|
+
const diagnostics = renderInspectDiagnostics(response);
|
|
35433
|
+
const json2 = JSON.stringify(response, null, 2);
|
|
35434
|
+
return diagnostics ? `${json2}
|
|
35435
|
+
|
|
35436
|
+
${diagnostics}` : json2;
|
|
35132
35437
|
}
|
|
35133
35438
|
};
|
|
35134
|
-
const hoisting = ctx.config.hoist_builtin_tools !== false;
|
|
35135
35439
|
return {
|
|
35136
|
-
|
|
35440
|
+
aft_inspect: inspectTool
|
|
35137
35441
|
};
|
|
35138
35442
|
}
|
|
35139
35443
|
|
|
@@ -35142,18 +35446,18 @@ import { tool as tool10 } from "@opencode-ai/plugin";
|
|
|
35142
35446
|
var z10 = tool10.schema;
|
|
35143
35447
|
function navigationTools(ctx) {
|
|
35144
35448
|
return {
|
|
35145
|
-
|
|
35146
|
-
description: `
|
|
35449
|
+
aft_callgraph: {
|
|
35450
|
+
description: `Answer code-relationship questions from a real call graph — instead of grep + read chains. Reach for this whenever the question is about how symbols connect: who calls X, what X calls, what breaks if X changes, how execution reaches X, or how a value flows.
|
|
35147
35451
|
|
|
35148
35452
|
` + `Ops:
|
|
35149
|
-
` + `- 'call_tree': See what a function calls (forward traversal). Use to understand dependencies before modifying a function.
|
|
35150
35453
|
` + `- 'callers': Find all call sites of a symbol. Use before renaming or changing a function's signature.
|
|
35151
|
-
` + `- '
|
|
35152
|
-
` + `- '
|
|
35153
|
-
` + `- '
|
|
35454
|
+
` + `- 'impact': What breaks if a symbol changes — affected callers with signatures and entry-point status (blast radius). Use before a risky edit.
|
|
35455
|
+
` + `- 'call_tree': What a function calls (forward traversal). Use to understand a function's dependencies before modifying it.
|
|
35456
|
+
` + `- 'trace_to': How execution reaches a function from entry points (routes, exports, main). Use to understand context around deeply-nested code.
|
|
35457
|
+
` + `- 'trace_to_symbol': Shortest call path from one symbol to another. Requires 'toSymbol'. If multiple targets match, the error returns candidate files; retry with 'toFile' to disambiguate.
|
|
35154
35458
|
` + `- 'trace_data': Follow a value through variable assignments and function parameters across files. Requires 'symbol' (scope to trace from) and 'expression'.
|
|
35155
35459
|
|
|
35156
|
-
` + `All ops require both 'filePath' and 'symbol'.
|
|
35460
|
+
` + `All ops require both 'filePath' and 'symbol'. 'expression' is additionally required for trace_data; 'toSymbol' for trace_to_symbol.
|
|
35157
35461
|
|
|
35158
35462
|
`,
|
|
35159
35463
|
args: {
|
|
@@ -35332,9 +35636,25 @@ function readingTools(ctx) {
|
|
|
35332
35636
|
contextLines: optionalInt(1, Number.MAX_SAFE_INTEGER).describe("Lines of context before/after the symbol (default: 3)")
|
|
35333
35637
|
},
|
|
35334
35638
|
execute: async (args, context) => {
|
|
35639
|
+
const hasTargetsProvided = (t) => {
|
|
35640
|
+
if (isEmptyParam(t))
|
|
35641
|
+
return false;
|
|
35642
|
+
const entryEmpty = (entry) => {
|
|
35643
|
+
if (!entry || typeof entry !== "object")
|
|
35644
|
+
return true;
|
|
35645
|
+
const fp = entry.filePath;
|
|
35646
|
+
const sym = entry.symbol;
|
|
35647
|
+
const fpEmpty = typeof fp !== "string" || fp.length === 0;
|
|
35648
|
+
const symEmpty = typeof sym !== "string" || sym.length === 0;
|
|
35649
|
+
return fpEmpty && symEmpty;
|
|
35650
|
+
};
|
|
35651
|
+
if (Array.isArray(t))
|
|
35652
|
+
return !t.every(entryEmpty);
|
|
35653
|
+
return !entryEmpty(t);
|
|
35654
|
+
};
|
|
35335
35655
|
const hasFilePath = !isEmptyParam(args.filePath);
|
|
35336
35656
|
const hasUrl = !isEmptyParam(args.url);
|
|
35337
|
-
const hasTargets =
|
|
35657
|
+
const hasTargets = hasTargetsProvided(args.targets);
|
|
35338
35658
|
const hasSymbols = !isEmptyParam(args.symbols);
|
|
35339
35659
|
const zoomCallID = getCallID3(context);
|
|
35340
35660
|
if (zoomCallID) {
|
|
@@ -35790,7 +36110,7 @@ function expandTilde(input) {
|
|
|
35790
36110
|
}
|
|
35791
36111
|
return input;
|
|
35792
36112
|
}
|
|
35793
|
-
function
|
|
36113
|
+
function arg2(schema) {
|
|
35794
36114
|
return schema;
|
|
35795
36115
|
}
|
|
35796
36116
|
function formatGrepOutput(response) {
|
|
@@ -35888,9 +36208,9 @@ function searchTools(ctx) {
|
|
|
35888
36208
|
const grepTool = {
|
|
35889
36209
|
description: "Search file contents using regular expressions. Returns matching lines with file paths and line numbers (no surrounding context lines — use `read` for that). Always case-sensitive. Capped at 100 matches; if you hit the cap, narrow with `path` or `include` and re-run.",
|
|
35890
36210
|
args: {
|
|
35891
|
-
pattern:
|
|
35892
|
-
include:
|
|
35893
|
-
path:
|
|
36211
|
+
pattern: arg2(exports_external.string().describe("Regular expression pattern to search for")),
|
|
36212
|
+
include: arg2(exports_external.string().optional().describe("File pattern to include (e.g. '*.ts', '*.{ts,tsx}')")),
|
|
36213
|
+
path: arg2(exports_external.string().optional().describe("Directory to search (absolute or relative to project root)"))
|
|
35894
36214
|
},
|
|
35895
36215
|
execute: async (args, context) => {
|
|
35896
36216
|
const pattern = String(args.pattern);
|
|
@@ -35927,8 +36247,8 @@ function searchTools(ctx) {
|
|
|
35927
36247
|
const globTool = {
|
|
35928
36248
|
description: "Find files matching a glob pattern. Returns matching file paths sorted by modification time.",
|
|
35929
36249
|
args: {
|
|
35930
|
-
pattern:
|
|
35931
|
-
path:
|
|
36250
|
+
pattern: arg2(exports_external.string().describe("Glob pattern to match (e.g. '**/*.ts', 'src/**/*.test.*')")),
|
|
36251
|
+
path: arg2(exports_external.string().optional().describe("Directory to search (absolute or relative to project root)"))
|
|
35932
36252
|
},
|
|
35933
36253
|
execute: async (args, context) => {
|
|
35934
36254
|
let globPattern = expandTilde(String(args.pattern));
|
|
@@ -35985,7 +36305,19 @@ function searchTools(ctx) {
|
|
|
35985
36305
|
// src/tools/semantic.ts
|
|
35986
36306
|
import { tool as tool14 } from "@opencode-ai/plugin";
|
|
35987
36307
|
var z14 = tool14.schema;
|
|
35988
|
-
function
|
|
36308
|
+
function semanticHonestyNote(response) {
|
|
36309
|
+
const notes = [];
|
|
36310
|
+
if (response.more_available === true)
|
|
36311
|
+
notes.push("more results available");
|
|
36312
|
+
if (response.engine_capped === true)
|
|
36313
|
+
notes.push("enumeration capped");
|
|
36314
|
+
if (response.fully_degraded === true)
|
|
36315
|
+
notes.push("fully degraded");
|
|
36316
|
+
if (response.complete === false)
|
|
36317
|
+
notes.push("partial/incomplete");
|
|
36318
|
+
return notes.length > 0 ? `Search status: ${notes.join("; ")}.` : undefined;
|
|
36319
|
+
}
|
|
36320
|
+
function arg3(schema) {
|
|
35989
36321
|
return schema;
|
|
35990
36322
|
}
|
|
35991
36323
|
function semanticTools(ctx) {
|
|
@@ -36003,19 +36335,19 @@ function semanticTools(ctx) {
|
|
|
36003
36335
|
"When NOT to use:",
|
|
36004
36336
|
"- You need exhaustive literal enumeration → use grep directly",
|
|
36005
36337
|
"- You want the file/module structure → use aft_outline",
|
|
36006
|
-
"- You're following a call chain → use
|
|
36338
|
+
"- You're following a call chain → use aft_callgraph",
|
|
36007
36339
|
"",
|
|
36008
36340
|
"Set hint to 'regex', 'literal', 'semantic', or 'auto' to override or document routing intent."
|
|
36009
36341
|
].join(`
|
|
36010
36342
|
`),
|
|
36011
36343
|
args: {
|
|
36012
|
-
query:
|
|
36013
|
-
topK:
|
|
36014
|
-
hint:
|
|
36344
|
+
query: arg3(z14.string().describe("Concept, regex, literal text, filename, or capability to find. Examples: 'fuzzy match with whitespace tolerance', '^export', 'Cargo.lock'.")),
|
|
36345
|
+
topK: arg3(optionalInt(1, 100).describe("Number of results (default: 10, max: 100)")),
|
|
36346
|
+
hint: arg3(z14.enum(["regex", "literal", "semantic", "auto"]).optional().describe("Optional routing hint. Defaults to 'auto'."))
|
|
36015
36347
|
},
|
|
36016
36348
|
execute: async (args, context) => {
|
|
36017
|
-
if (typeof args.query !== "string" || args.query.length === 0) {
|
|
36018
|
-
throw new Error("semantic_search: invalid params:
|
|
36349
|
+
if (isEmptyParam(args.query) || typeof args.query !== "string" || args.query.trim().length === 0) {
|
|
36350
|
+
throw new Error("semantic_search: invalid params: `query` must be a non-empty string");
|
|
36019
36351
|
}
|
|
36020
36352
|
const query = args.query;
|
|
36021
36353
|
const hint = typeof args.hint === "string" ? args.hint : undefined;
|
|
@@ -36032,15 +36364,28 @@ function semanticTools(ctx) {
|
|
|
36032
36364
|
bridgeParams.hint = hint;
|
|
36033
36365
|
const response = await callBridge(ctx, context, "semantic_search", bridgeParams);
|
|
36034
36366
|
if (response.success === false) {
|
|
36035
|
-
|
|
36036
|
-
|
|
36037
|
-
}
|
|
36038
|
-
|
|
36039
|
-
|
|
36040
|
-
if (typeof response.text === "string") {
|
|
36041
|
-
return response.text
|
|
36367
|
+
const message = typeof response.message === "string" && response.message.length > 0 ? response.message : "semantic_search failed";
|
|
36368
|
+
const code = typeof response.code === "string" && response.code.length > 0 ? response.code : undefined;
|
|
36369
|
+
throw new Error(code ? `semantic_search: ${code} — ${message}` : message);
|
|
36370
|
+
}
|
|
36371
|
+
const honestyNote = semanticHonestyNote(response);
|
|
36372
|
+
if (typeof response.text === "string" && response.status === "disabled" && Array.isArray(response.results) && response.results.length === 0) {
|
|
36373
|
+
return honestyNote ? `${response.text}
|
|
36374
|
+
${honestyNote}` : response.text;
|
|
36375
|
+
}
|
|
36376
|
+
const structured = JSON.stringify(response, null, 2);
|
|
36377
|
+
if (typeof response.text === "string" && response.text.length > 0) {
|
|
36378
|
+
const display = honestyNote ? `${response.text}
|
|
36379
|
+
${honestyNote}` : response.text;
|
|
36380
|
+
return `${display}
|
|
36381
|
+
|
|
36382
|
+
Structured response:
|
|
36383
|
+
${structured}`;
|
|
36042
36384
|
}
|
|
36043
|
-
return
|
|
36385
|
+
return honestyNote ? `${honestyNote}
|
|
36386
|
+
|
|
36387
|
+
Structured response:
|
|
36388
|
+
${structured}` : structured;
|
|
36044
36389
|
}
|
|
36045
36390
|
};
|
|
36046
36391
|
return {
|
|
@@ -36170,7 +36515,8 @@ function buildWorkflowHints(opts) {
|
|
|
36170
36515
|
const hasZoom = !opts.disabledTools.has("aft_zoom");
|
|
36171
36516
|
const hasGrep = opts.toolSurface !== "minimal" && !opts.disabledTools.has(grepName);
|
|
36172
36517
|
const hasSearch = opts.toolSurface !== "minimal" && opts.semanticEnabled && !opts.disabledTools.has("aft_search");
|
|
36173
|
-
const hasNavigate = opts.toolSurface === "all" && !opts.disabledTools.has("
|
|
36518
|
+
const hasNavigate = opts.toolSurface === "all" && !opts.disabledTools.has("aft_callgraph");
|
|
36519
|
+
const hasInspect = opts.toolSurface !== "minimal" && !opts.disabledTools.has("aft_inspect");
|
|
36174
36520
|
const hasBash = !opts.disabledTools.has(bashName);
|
|
36175
36521
|
const hasBgBash = hasBash && opts.bashBackgroundEnabled && !opts.disabledTools.has(bashStatusName);
|
|
36176
36522
|
if (hasOutline && hasZoom) {
|
|
@@ -36179,14 +36525,17 @@ function buildWorkflowHints(opts) {
|
|
|
36179
36525
|
if (hasOutline && hasZoom && (hasGrep || hasSearch)) {
|
|
36180
36526
|
if (hasSearch) {
|
|
36181
36527
|
const grepFallback = hasGrep ? ` Use \`${grepName}\` directly only when you need exhaustive enumeration of literal text (every TODO, every import of X) without ranking.` : "";
|
|
36182
|
-
sections.push(`**Code exploration**: \`aft_search\` is the primary code-search tool. It auto-routes by query shape — exact identifiers, regex, error messages, natural language all use the same call.
|
|
36528
|
+
sections.push(`**Code exploration**: \`aft_search\` is the primary code-search tool. It auto-routes by query shape — exact identifiers, regex, error messages, natural language all use the same call. Very short queries fall back to literal scans; pass \`hint: "regex"\` / \`hint: "literal"\` / \`hint: "semantic"\` to override routing if needed. Then \`aft_outline\` for structure → \`aft_zoom\` for symbol(s).${grepFallback}`);
|
|
36183
36529
|
} else {
|
|
36184
36530
|
sections.push(`**Code exploration**: \`${grepName}\` to locate → \`aft_outline\` for structure → \`aft_zoom\` for symbol(s).`);
|
|
36185
36531
|
}
|
|
36186
36532
|
}
|
|
36533
|
+
if (hasInspect) {
|
|
36534
|
+
sections.push("**Codebase health & diagnostics**: AFT does not surface compile/type errors automatically after edits — pull them with `aft_inspect`. Run it after a batch of edits and before you run tests or commit, when starting in unfamiliar code, or before a refactor/review. One call summarizes diagnostics (compile/type errors), TODOs, metrics, dead code, unused exports, and duplicates; pass `sections` for focused drill-down and `scope` to actively pull diagnostics for a specific file or directory. Its diagnostics are a fast checkpoint, not the authority — a clean `tsc` / `cargo check` / `pyright` run is the real gate. Treat `stale_categories` as a genuine stale-cache signal while an async Tier 2 refresh catches up.");
|
|
36535
|
+
}
|
|
36187
36536
|
if (hasNavigate) {
|
|
36188
36537
|
sections.push([
|
|
36189
|
-
"Use `
|
|
36538
|
+
"Use `aft_callgraph` for code-relationship questions instead of grep + read chains:",
|
|
36190
36539
|
"- `callers` — find all call sites before changing a function signature",
|
|
36191
36540
|
"- `impact` — blast radius (which functions/files will need updates)",
|
|
36192
36541
|
"- `trace_to` — how execution reaches this code from entry points (routes, exports, main)",
|
|
@@ -36303,13 +36652,13 @@ var PLUGIN_VERSION = (() => {
|
|
|
36303
36652
|
return "0.0.0";
|
|
36304
36653
|
}
|
|
36305
36654
|
})();
|
|
36306
|
-
var ANNOUNCEMENT_VERSION = "0.
|
|
36655
|
+
var ANNOUNCEMENT_VERSION = "0.33.0";
|
|
36307
36656
|
var ANNOUNCEMENT_FEATURES = [
|
|
36308
|
-
"`
|
|
36309
|
-
|
|
36310
|
-
"
|
|
36311
|
-
"
|
|
36312
|
-
|
|
36657
|
+
"New `aft_inspect` — one call for codebase health: diagnostics, metrics, TODOs, dead code, unused exports, and duplicates.",
|
|
36658
|
+
"Diagnostics now flow through `aft_inspect` (run it after a batch of edits) instead of arriving automatically on every edit.",
|
|
36659
|
+
"`aft_navigate` is renamed to `aft_callgraph`; the Rust call graph now resolves cross-file callers.",
|
|
36660
|
+
"Edits no longer echo the whole file back to the agent — much lower token cost per edit.",
|
|
36661
|
+
"Batch of `aft_search` correctness fixes and undo-history/SSRF/Windows hardening."
|
|
36313
36662
|
];
|
|
36314
36663
|
var ANNOUNCEMENT_FOOTER = "Join us on Discord: https://discord.gg/F2uWxjGnU";
|
|
36315
36664
|
var plugin = async (input) => initializePluginForDirectory(input);
|
|
@@ -36521,7 +36870,10 @@ ${lines}
|
|
|
36521
36870
|
});
|
|
36522
36871
|
}
|
|
36523
36872
|
const rpcServer = new AftRpcServer(configOverrides.storage_dir, input.directory);
|
|
36524
|
-
|
|
36873
|
+
let clearInspectTier2Idle = () => {};
|
|
36874
|
+
registerShutdownCleanup(async () => {
|
|
36875
|
+
autoUpdateAbort.abort();
|
|
36876
|
+
clearInspectTier2Idle();
|
|
36525
36877
|
await Promise.allSettled([abortInFlightAutoInstalls(), abortInFlightGithubInstalls()]);
|
|
36526
36878
|
try {
|
|
36527
36879
|
rpcServer.stop();
|
|
@@ -36621,7 +36973,7 @@ Install: ${getManualInstallHint()}`).catch(() => {});
|
|
|
36621
36973
|
}
|
|
36622
36974
|
const surface = aftConfig.tool_surface ?? "recommended";
|
|
36623
36975
|
const ALL_ONLY_TOOLS = new Set([
|
|
36624
|
-
"
|
|
36976
|
+
"aft_callgraph",
|
|
36625
36977
|
"aft_delete",
|
|
36626
36978
|
"aft_move",
|
|
36627
36979
|
"aft_transform",
|
|
@@ -36636,9 +36988,9 @@ Install: ${getManualInstallHint()}`).catch(() => {});
|
|
|
36636
36988
|
...navigationTools(ctx),
|
|
36637
36989
|
...surface !== "minimal" && astTools(ctx),
|
|
36638
36990
|
...surface !== "minimal" && aftConfig.semantic_search === true && semanticTools(ctx),
|
|
36991
|
+
...inspectToolSurfaceEnabled(aftConfig) && inspectTools(ctx),
|
|
36639
36992
|
...surface !== "minimal" && aftConfig.search_index === true && searchTools(ctx),
|
|
36640
36993
|
...refactoringTools(ctx),
|
|
36641
|
-
...surface !== "minimal" && lspTools(ctx),
|
|
36642
36994
|
...surface !== "minimal" && conflictTools(ctx)
|
|
36643
36995
|
});
|
|
36644
36996
|
if (surface !== "all") {
|
|
@@ -36669,7 +37021,8 @@ Install: ${getManualInstallHint()}`).catch(() => {});
|
|
|
36669
37021
|
"aft_outline",
|
|
36670
37022
|
"aft_zoom",
|
|
36671
37023
|
"aft_search",
|
|
36672
|
-
"
|
|
37024
|
+
"aft_callgraph",
|
|
37025
|
+
"aft_inspect",
|
|
36673
37026
|
"grep",
|
|
36674
37027
|
"aft_grep",
|
|
36675
37028
|
"bash",
|
|
@@ -36686,6 +37039,20 @@ Install: ${getManualInstallHint()}`).catch(() => {});
|
|
|
36686
37039
|
if (hintsBlock) {
|
|
36687
37040
|
log2(`Workflow hints injected (${hintsBlock.length} chars)`);
|
|
36688
37041
|
}
|
|
37042
|
+
const inspectTier2Idle = createInspectTier2IdleScheduler({
|
|
37043
|
+
isEnabled: () => registeredTools.has("aft_inspect"),
|
|
37044
|
+
idleMinutes: () => aftConfig.inspect?.tier2_idle_minutes,
|
|
37045
|
+
warn: warn2,
|
|
37046
|
+
run: async (sessionID) => {
|
|
37047
|
+
const sessionDir = await getSessionDirectory(input.client, sessionID, input.directory) ?? input.directory;
|
|
37048
|
+
const bridge = ctx.pool.getActiveBridgeForRoot(sessionDir) ?? ctx.pool.getBridge(sessionDir);
|
|
37049
|
+
const response = await bridge.send("inspect_tier2_run", { session_id: sessionID });
|
|
37050
|
+
if (response.success === false) {
|
|
37051
|
+
warn2(response.message || "inspect_tier2_run failed");
|
|
37052
|
+
}
|
|
37053
|
+
}
|
|
37054
|
+
});
|
|
37055
|
+
clearInspectTier2Idle = () => inspectTier2Idle.clearAll();
|
|
36689
37056
|
return {
|
|
36690
37057
|
tool: allTools,
|
|
36691
37058
|
"experimental.chat.system.transform": async (_input, output) => {
|
|
@@ -36695,11 +37062,17 @@ Install: ${getManualInstallHint()}`).catch(() => {});
|
|
|
36695
37062
|
},
|
|
36696
37063
|
event: async (eventInput) => {
|
|
36697
37064
|
await autoUpdateEventHook(eventInput);
|
|
36698
|
-
|
|
36699
|
-
return;
|
|
37065
|
+
const eventType = eventInput.event.type;
|
|
36700
37066
|
const sessionID = extractSessionID(eventInput.event.properties);
|
|
37067
|
+
if ((eventType === "session.deleted" || eventType === "session.shutdown") && sessionID) {
|
|
37068
|
+
inspectTier2Idle.clear(sessionID);
|
|
37069
|
+
return;
|
|
37070
|
+
}
|
|
37071
|
+
if (eventType !== "session.idle")
|
|
37072
|
+
return;
|
|
36701
37073
|
if (!sessionID)
|
|
36702
37074
|
return;
|
|
37075
|
+
inspectTier2Idle.schedule(sessionID);
|
|
36703
37076
|
const sessionDir = await getSessionDirectory(input.client, sessionID, input.directory) ?? input.directory;
|
|
36704
37077
|
await handleIdleBgCompletions({
|
|
36705
37078
|
ctx,
|
|
@@ -36713,6 +37086,10 @@ Install: ${getManualInstallHint()}`).catch(() => {});
|
|
|
36713
37086
|
const sid = messageInput.sessionID ?? messageInput.sessionId ?? messageInput.id;
|
|
36714
37087
|
warmSessionDirectory(input.client, sid, input.directory);
|
|
36715
37088
|
},
|
|
37089
|
+
"tool.execute.before": async (toolInput) => {
|
|
37090
|
+
if (toolInput.sessionID)
|
|
37091
|
+
inspectTier2Idle.clear(toolInput.sessionID);
|
|
37092
|
+
},
|
|
36716
37093
|
"command.execute.before": async (commandInput, _output) => {
|
|
36717
37094
|
if (isTuiMode2() || commandInput.command !== STATUS_COMMAND) {
|
|
36718
37095
|
return;
|
|
@@ -36763,12 +37140,7 @@ Install: ${getManualInstallHint()}`).catch(() => {});
|
|
|
36763
37140
|
};
|
|
36764
37141
|
},
|
|
36765
37142
|
dispose: async () => {
|
|
36766
|
-
|
|
36767
|
-
unregisterShutdown();
|
|
36768
|
-
await Promise.allSettled([abortInFlightAutoInstalls(), abortInFlightGithubInstalls()]);
|
|
36769
|
-
rpcServer.stop();
|
|
36770
|
-
await disposeAllPtyTerminals();
|
|
36771
|
-
await pool.shutdown();
|
|
37143
|
+
await runCleanups("dispose");
|
|
36772
37144
|
}
|
|
36773
37145
|
};
|
|
36774
37146
|
}
|