@cortexkit/aft-opencode 0.49.4 → 0.50.1
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 +6 -3
- package/dist/bg-notifications.d.ts.map +1 -1
- package/dist/config.d.ts +6 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +672 -108
- package/dist/normalize-schemas.d.ts +7 -4
- package/dist/normalize-schemas.d.ts.map +1 -1
- package/dist/tool-registration.d.ts +6 -0
- package/dist/tool-registration.d.ts.map +1 -1
- package/dist/tools/hoisted.d.ts.map +1 -1
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +8 -8
package/dist/index.js
CHANGED
|
@@ -8334,6 +8334,8 @@ var DEFAULT_BRIDGE_TIMEOUT_MS = 30000;
|
|
|
8334
8334
|
var BRIDGE_HANG_TIMEOUT_THRESHOLD = 2;
|
|
8335
8335
|
var MAX_STDOUT_BUFFER = 64 * 1024 * 1024;
|
|
8336
8336
|
var STDOUT_BUFFER_COMPACT_THRESHOLD = 64 * 1024;
|
|
8337
|
+
var HASHLINE_REGISTRATION_LOG_INTERVAL_MS = 60000;
|
|
8338
|
+
var HASHLINE_REGISTRATION_LOG_STATE_LIMIT = 256;
|
|
8337
8339
|
var TERMINAL_BASH_STATUSES = new Set([
|
|
8338
8340
|
"completed",
|
|
8339
8341
|
"failed",
|
|
@@ -8483,6 +8485,9 @@ class BinaryBridge {
|
|
|
8483
8485
|
configured = false;
|
|
8484
8486
|
_configurePromise = null;
|
|
8485
8487
|
configOverrides;
|
|
8488
|
+
editSlotSurvives;
|
|
8489
|
+
editSlotSurvivesCaptured = false;
|
|
8490
|
+
hashlineRegistrationLogState = new Map;
|
|
8486
8491
|
minVersion;
|
|
8487
8492
|
onVersionMismatch;
|
|
8488
8493
|
onConfigureWarnings;
|
|
@@ -8499,20 +8504,33 @@ class BinaryBridge {
|
|
|
8499
8504
|
errorPrefix;
|
|
8500
8505
|
logger;
|
|
8501
8506
|
childEnv;
|
|
8502
|
-
constructor(binaryPath, cwd, options, configOverrides) {
|
|
8507
|
+
constructor(binaryPath, cwd, options, configOverrides, editSlotSurvives) {
|
|
8503
8508
|
this.binaryPath = binaryPath;
|
|
8504
8509
|
this.cwd = cwd;
|
|
8505
8510
|
this.timeoutMs = options?.timeoutMs ?? DEFAULT_BRIDGE_TIMEOUT_MS;
|
|
8506
8511
|
this.hangThreshold = options?.hangThreshold ?? BRIDGE_HANG_TIMEOUT_THRESHOLD;
|
|
8507
8512
|
this.maxRestarts = options?.maxRestarts ?? 3;
|
|
8508
|
-
this.
|
|
8513
|
+
this.errorPrefix = options?.errorPrefix ?? "[aft-bridge]";
|
|
8514
|
+
this.configOverrides = { ...configOverrides ?? {} };
|
|
8515
|
+
const legacyEditSlotSurvives = this.configOverrides.edit_slot_survives;
|
|
8516
|
+
delete this.configOverrides.edit_slot_survives;
|
|
8517
|
+
if (legacyEditSlotSurvives !== undefined && typeof legacyEditSlotSurvives !== "boolean") {
|
|
8518
|
+
throw new Error(`${this.errorPrefix} edit_slot_survives must be a boolean`);
|
|
8519
|
+
}
|
|
8520
|
+
if (editSlotSurvives !== undefined && legacyEditSlotSurvives !== undefined && editSlotSurvives !== legacyEditSlotSurvives) {
|
|
8521
|
+
throw new Error(`${this.errorPrefix} conflicting edit_slot_survives construction values`);
|
|
8522
|
+
}
|
|
8523
|
+
const capturedEditSlotSurvives = editSlotSurvives ?? legacyEditSlotSurvives;
|
|
8524
|
+
if (typeof capturedEditSlotSurvives === "boolean") {
|
|
8525
|
+
this.editSlotSurvives = capturedEditSlotSurvives;
|
|
8526
|
+
this.editSlotSurvivesCaptured = true;
|
|
8527
|
+
}
|
|
8509
8528
|
this.minVersion = options?.minVersion;
|
|
8510
8529
|
this.onVersionMismatch = options?.onVersionMismatch;
|
|
8511
8530
|
this.onConfigureWarnings = options?.onConfigureWarnings;
|
|
8512
8531
|
this.onBashCompletion = options?.onBashCompletion;
|
|
8513
8532
|
this.onBashLongRunning = options?.onBashLongRunning;
|
|
8514
8533
|
this.onBashPatternMatch = options?.onBashPatternMatch;
|
|
8515
|
-
this.errorPrefix = options?.errorPrefix ?? "[aft-bridge]";
|
|
8516
8534
|
this.logger = options?.logger;
|
|
8517
8535
|
this.childEnv = options?.childEnv;
|
|
8518
8536
|
}
|
|
@@ -8623,13 +8641,54 @@ class BinaryBridge {
|
|
|
8623
8641
|
cacheStatusSnapshot(snapshot) {
|
|
8624
8642
|
this.cachedStatus = snapshot;
|
|
8625
8643
|
}
|
|
8644
|
+
setEditSlotSurvives(value) {
|
|
8645
|
+
if (this.editSlotSurvivesCaptured) {
|
|
8646
|
+
throw new Error(`${this.errorPrefix} edit_slot_survives is write-once and was already captured`);
|
|
8647
|
+
}
|
|
8648
|
+
this.editSlotSurvives = value;
|
|
8649
|
+
this.editSlotSurvivesCaptured = true;
|
|
8650
|
+
}
|
|
8651
|
+
logHashlineRegistrationCarrier(phase, sessionId, editSlotSurvives) {
|
|
8652
|
+
const session = sessionId && sessionId.length > 0 ? sessionId : "__default__";
|
|
8653
|
+
const key = `${phase}\x00${session}\x00${String(editSlotSurvives)}`;
|
|
8654
|
+
const now = Date.now();
|
|
8655
|
+
const state = this.hashlineRegistrationLogState.get(key);
|
|
8656
|
+
if (state && now - state.lastEmittedAt < HASHLINE_REGISTRATION_LOG_INTERVAL_MS) {
|
|
8657
|
+
state.suppressed += 1;
|
|
8658
|
+
return;
|
|
8659
|
+
}
|
|
8660
|
+
const repeated = state && state.suppressed > 0 ? ` repeated=${state.suppressed + 1}` : "";
|
|
8661
|
+
if (!state && this.hashlineRegistrationLogState.size >= HASHLINE_REGISTRATION_LOG_STATE_LIMIT) {
|
|
8662
|
+
const oldest = this.hashlineRegistrationLogState.keys().next().value;
|
|
8663
|
+
if (oldest !== undefined)
|
|
8664
|
+
this.hashlineRegistrationLogState.delete(oldest);
|
|
8665
|
+
}
|
|
8666
|
+
this.hashlineRegistrationLogState.set(key, { lastEmittedAt: now, suppressed: 0 });
|
|
8667
|
+
this.sessionLogVia(sessionId, `hashline registration carrier transport=ndjson phase=${phase} edit_slot_survives=${editSlotSurvives}${repeated}`);
|
|
8668
|
+
}
|
|
8626
8669
|
async send(command, params = {}, options) {
|
|
8627
|
-
|
|
8670
|
+
let dispatchParams = params;
|
|
8671
|
+
if (command === "configure") {
|
|
8672
|
+
dispatchParams = { ...params };
|
|
8673
|
+
delete dispatchParams.edit_slot_survives;
|
|
8674
|
+
const editSlotSurvives = this.editSlotSurvives;
|
|
8675
|
+
if (this.editSlotSurvivesCaptured && typeof editSlotSurvives === "boolean") {
|
|
8676
|
+
dispatchParams.edit_slot_survives = editSlotSurvives;
|
|
8677
|
+
const sessionId = typeof dispatchParams.session_id === "string" ? dispatchParams.session_id : undefined;
|
|
8678
|
+
this.logHashlineRegistrationCarrier("configure", sessionId, editSlotSurvives);
|
|
8679
|
+
}
|
|
8680
|
+
}
|
|
8681
|
+
return this.sendWithVersionMismatchRetry(command, dispatchParams, options, true);
|
|
8628
8682
|
}
|
|
8629
8683
|
async toolCall(sessionId, name, rawArgs = {}, options) {
|
|
8630
8684
|
const params = { name, arguments: rawArgs };
|
|
8631
8685
|
if (sessionId)
|
|
8632
8686
|
params.session_id = sessionId;
|
|
8687
|
+
const editSlotSurvives = this.editSlotSurvives;
|
|
8688
|
+
if (this.editSlotSurvivesCaptured && typeof editSlotSurvives === "boolean") {
|
|
8689
|
+
params.edit_slot_survives = editSlotSurvives;
|
|
8690
|
+
this.logHashlineRegistrationCarrier("tool_call", sessionId, editSlotSurvives);
|
|
8691
|
+
}
|
|
8633
8692
|
const { preview, ...sendOptions } = options ?? {};
|
|
8634
8693
|
if (preview === true)
|
|
8635
8694
|
params.preview = true;
|
|
@@ -9433,7 +9492,8 @@ function formatDroppedKeyWarnings(dropped) {
|
|
|
9433
9492
|
// ../aft-bridge/dist/downloader.js
|
|
9434
9493
|
import { spawnSync } from "node:child_process";
|
|
9435
9494
|
import { createHash as createHash2, randomUUID } from "node:crypto";
|
|
9436
|
-
import { chmodSync, closeSync, copyFileSync, createWriteStream, existsSync as existsSync2, mkdirSync, openSync, readFileSync as readFileSync3, renameSync, rmSync, statSync as statSync2, unlinkSync, writeSync } from "node:fs";
|
|
9495
|
+
import { chmodSync, closeSync, copyFileSync, createWriteStream, existsSync as existsSync2, mkdirSync, openSync, readdirSync, readFileSync as readFileSync3, renameSync, rmSync, statSync as statSync2, unlinkSync, writeSync } from "node:fs";
|
|
9496
|
+
import { hostname } from "node:os";
|
|
9437
9497
|
import { join as join4 } from "node:path";
|
|
9438
9498
|
import { Readable } from "node:stream";
|
|
9439
9499
|
import { pipeline } from "node:stream/promises";
|
|
@@ -9458,8 +9518,8 @@ var REPO = "cortexkit/aft";
|
|
|
9458
9518
|
var DOWNLOAD_TIMEOUT_MS = 300000;
|
|
9459
9519
|
var LATEST_TAG_TIMEOUT_MS = 30000;
|
|
9460
9520
|
var MAX_DOWNLOAD_BYTES = 200 * 1024 * 1024;
|
|
9461
|
-
var DOWNLOAD_LOCK_TIMEOUT_MS = 120000;
|
|
9462
9521
|
var DOWNLOAD_LOCK_STALE_MS = 10 * 60000;
|
|
9522
|
+
var DOWNLOAD_LOCK_TIMEOUT_MS = DOWNLOAD_LOCK_STALE_MS + 30000;
|
|
9463
9523
|
function readBinaryVersion(binaryPath) {
|
|
9464
9524
|
try {
|
|
9465
9525
|
const result = spawnSync(binaryPath, ["--version"], {
|
|
@@ -9527,11 +9587,37 @@ async function downloadBinary(version) {
|
|
|
9527
9587
|
let binaryTimeout = null;
|
|
9528
9588
|
let checksumTimeout = null;
|
|
9529
9589
|
const tmpPath = `${binaryPath}.${process.pid}.${Date.now()}.${Math.random().toString(16).slice(2)}.tmp`;
|
|
9590
|
+
const cleanUpPartialDownload = () => {
|
|
9591
|
+
try {
|
|
9592
|
+
if (existsSync2(tmpPath))
|
|
9593
|
+
unlinkSync(tmpPath);
|
|
9594
|
+
} catch {}
|
|
9595
|
+
};
|
|
9596
|
+
let interrupted = false;
|
|
9597
|
+
const cleanUpInterruptedDownload = () => {
|
|
9598
|
+
if (interrupted)
|
|
9599
|
+
return;
|
|
9600
|
+
interrupted = true;
|
|
9601
|
+
binaryController?.abort();
|
|
9602
|
+
checksumController?.abort();
|
|
9603
|
+
cleanUpPartialDownload();
|
|
9604
|
+
releaseLock?.();
|
|
9605
|
+
releaseLock = null;
|
|
9606
|
+
};
|
|
9607
|
+
const handleSigint = () => {
|
|
9608
|
+
cleanUpInterruptedDownload();
|
|
9609
|
+
process.off("SIGINT", handleSigint);
|
|
9610
|
+
process.kill(process.pid, "SIGINT");
|
|
9611
|
+
};
|
|
9612
|
+
const handleExit = () => cleanUpInterruptedDownload();
|
|
9613
|
+
process.once("SIGINT", handleSigint);
|
|
9614
|
+
process.once("exit", handleExit);
|
|
9530
9615
|
try {
|
|
9531
9616
|
if (!existsSync2(versionedCacheDir)) {
|
|
9532
9617
|
mkdirSync(versionedCacheDir, { recursive: true });
|
|
9533
9618
|
}
|
|
9534
9619
|
releaseLock = await acquireDownloadLock(lockPath);
|
|
9620
|
+
sweepStaleDownloadTemps(versionedCacheDir, binaryName);
|
|
9535
9621
|
if (existsSync2(binaryPath) && isExpectedCachedBinary(binaryPath, tag)) {
|
|
9536
9622
|
return binaryPath;
|
|
9537
9623
|
}
|
|
@@ -9607,13 +9693,11 @@ async function downloadBinary(version) {
|
|
|
9607
9693
|
} catch (err) {
|
|
9608
9694
|
const msg = err instanceof Error ? err.message : String(err);
|
|
9609
9695
|
error(`Failed to download AFT binary: ${msg}`);
|
|
9610
|
-
|
|
9611
|
-
try {
|
|
9612
|
-
unlinkSync(tmpPath);
|
|
9613
|
-
} catch {}
|
|
9614
|
-
}
|
|
9696
|
+
cleanUpPartialDownload();
|
|
9615
9697
|
return null;
|
|
9616
9698
|
} finally {
|
|
9699
|
+
process.off("SIGINT", handleSigint);
|
|
9700
|
+
process.off("exit", handleExit);
|
|
9617
9701
|
if (binaryTimeout) {
|
|
9618
9702
|
binaryController?.abort();
|
|
9619
9703
|
clearTimeout(binaryTimeout);
|
|
@@ -9639,17 +9723,85 @@ async function ensureBinary(version) {
|
|
|
9639
9723
|
log("No cached binary found, downloading latest...");
|
|
9640
9724
|
return downloadBinary();
|
|
9641
9725
|
}
|
|
9642
|
-
|
|
9726
|
+
function createDownloadLockOwner() {
|
|
9727
|
+
return JSON.stringify({
|
|
9728
|
+
pid: process.pid,
|
|
9729
|
+
hostname: hostname(),
|
|
9730
|
+
createdAt: Date.now(),
|
|
9731
|
+
token: randomUUID()
|
|
9732
|
+
});
|
|
9733
|
+
}
|
|
9734
|
+
function parseDownloadLockOwner(raw) {
|
|
9735
|
+
try {
|
|
9736
|
+
const parsed = JSON.parse(raw);
|
|
9737
|
+
if (typeof parsed === "object" && parsed !== null) {
|
|
9738
|
+
const { pid, hostname: ownerHostname } = parsed;
|
|
9739
|
+
return {
|
|
9740
|
+
pid: typeof pid === "number" && Number.isSafeInteger(pid) && pid > 0 ? pid : null,
|
|
9741
|
+
hostname: typeof ownerHostname === "string" && ownerHostname ? ownerHostname : null
|
|
9742
|
+
};
|
|
9743
|
+
}
|
|
9744
|
+
} catch {}
|
|
9745
|
+
const legacyPid = Number(raw.split(":", 1)[0]);
|
|
9746
|
+
return {
|
|
9747
|
+
pid: Number.isSafeInteger(legacyPid) && legacyPid > 0 ? legacyPid : null,
|
|
9748
|
+
hostname: null
|
|
9749
|
+
};
|
|
9750
|
+
}
|
|
9751
|
+
function isProcessAlive(pid) {
|
|
9752
|
+
try {
|
|
9753
|
+
process.kill(pid, 0);
|
|
9754
|
+
return true;
|
|
9755
|
+
} catch (err) {
|
|
9756
|
+
return err.code !== "ESRCH";
|
|
9757
|
+
}
|
|
9758
|
+
}
|
|
9759
|
+
function isReclaimableDownloadLock(owner, ageMs, staleMs) {
|
|
9760
|
+
if (Math.abs(ageMs) > staleMs)
|
|
9761
|
+
return true;
|
|
9762
|
+
return (owner.hostname === null || owner.hostname === hostname()) && owner.pid !== null && !isProcessAlive(owner.pid);
|
|
9763
|
+
}
|
|
9764
|
+
function reclaimDownloadLock(lockPath, expectedOwner) {
|
|
9765
|
+
try {
|
|
9766
|
+
if (readFileSync3(lockPath, "utf-8") !== expectedOwner)
|
|
9767
|
+
return false;
|
|
9768
|
+
rmSync(lockPath, { force: true });
|
|
9769
|
+
return true;
|
|
9770
|
+
} catch (err) {
|
|
9771
|
+
if (err.code === "ENOENT")
|
|
9772
|
+
return true;
|
|
9773
|
+
throw err;
|
|
9774
|
+
}
|
|
9775
|
+
}
|
|
9776
|
+
function sweepStaleDownloadTemps(versionedCacheDir, binaryName) {
|
|
9777
|
+
const tempPrefix = `${binaryName}.`;
|
|
9778
|
+
try {
|
|
9779
|
+
for (const entry of readdirSync(versionedCacheDir, { withFileTypes: true })) {
|
|
9780
|
+
if (!entry.isFile() || !entry.name.startsWith(tempPrefix) || !entry.name.endsWith(".tmp")) {
|
|
9781
|
+
continue;
|
|
9782
|
+
}
|
|
9783
|
+
const tempPath = join4(versionedCacheDir, entry.name);
|
|
9784
|
+
const ageMs = Date.now() - statSync2(tempPath).mtimeMs;
|
|
9785
|
+
if (Math.abs(ageMs) > DOWNLOAD_LOCK_STALE_MS)
|
|
9786
|
+
unlinkSync(tempPath);
|
|
9787
|
+
}
|
|
9788
|
+
} catch {}
|
|
9789
|
+
}
|
|
9790
|
+
async function acquireDownloadLock(lockPath, timing = {}) {
|
|
9791
|
+
const timeoutMs = timing.timeoutMs ?? DOWNLOAD_LOCK_TIMEOUT_MS;
|
|
9792
|
+
const staleMs = timing.staleMs ?? DOWNLOAD_LOCK_STALE_MS;
|
|
9793
|
+
const pollIntervalMs = timing.pollIntervalMs ?? 100;
|
|
9643
9794
|
const startedAt = Date.now();
|
|
9644
9795
|
while (true) {
|
|
9645
9796
|
try {
|
|
9646
|
-
const owner =
|
|
9797
|
+
const owner = createDownloadLockOwner();
|
|
9647
9798
|
const fd = openSync(lockPath, "wx");
|
|
9648
|
-
|
|
9799
|
+
try {
|
|
9800
|
+
writeSync(fd, owner);
|
|
9801
|
+
} finally {
|
|
9802
|
+
closeSync(fd);
|
|
9803
|
+
}
|
|
9649
9804
|
return () => {
|
|
9650
|
-
try {
|
|
9651
|
-
closeSync(fd);
|
|
9652
|
-
} catch {}
|
|
9653
9805
|
try {
|
|
9654
9806
|
if (readFileSync3(lockPath, "utf-8") === owner) {
|
|
9655
9807
|
rmSync(lockPath, { force: true });
|
|
@@ -9660,19 +9812,24 @@ async function acquireDownloadLock(lockPath) {
|
|
|
9660
9812
|
const code = err.code;
|
|
9661
9813
|
if (code !== "EEXIST")
|
|
9662
9814
|
throw err;
|
|
9815
|
+
let existingOwner;
|
|
9816
|
+
let ageMs;
|
|
9663
9817
|
try {
|
|
9664
|
-
|
|
9665
|
-
|
|
9666
|
-
|
|
9818
|
+
existingOwner = readFileSync3(lockPath, "utf-8");
|
|
9819
|
+
ageMs = Date.now() - statSync2(lockPath).mtimeMs;
|
|
9820
|
+
} catch (readErr) {
|
|
9821
|
+
if (readErr.code === "ENOENT")
|
|
9667
9822
|
continue;
|
|
9668
|
-
|
|
9669
|
-
} catch {
|
|
9670
|
-
continue;
|
|
9823
|
+
throw readErr;
|
|
9671
9824
|
}
|
|
9672
|
-
if (
|
|
9825
|
+
if (isReclaimableDownloadLock(parseDownloadLockOwner(existingOwner), ageMs, staleMs)) {
|
|
9826
|
+
if (reclaimDownloadLock(lockPath, existingOwner))
|
|
9827
|
+
continue;
|
|
9828
|
+
}
|
|
9829
|
+
if (Date.now() - startedAt > timeoutMs) {
|
|
9673
9830
|
throw new Error(`Timed out waiting for download lock: ${lockPath}`);
|
|
9674
9831
|
}
|
|
9675
|
-
await new Promise((resolve3) => setTimeout(resolve3,
|
|
9832
|
+
await new Promise((resolve3) => setTimeout(resolve3, pollIntervalMs));
|
|
9676
9833
|
}
|
|
9677
9834
|
}
|
|
9678
9835
|
}
|
|
@@ -12483,6 +12640,8 @@ function projectRootKeyHash(dir) {
|
|
|
12483
12640
|
var AFT_MODULE_ID = "aft";
|
|
12484
12641
|
var MAX_CONSECUTIVE_TRANSPORT_FAILURES = 3;
|
|
12485
12642
|
var BG_STABLE_MS = 5000;
|
|
12643
|
+
var BG_LIFECYCLE_LOG_INTERVAL_MS = 60000;
|
|
12644
|
+
var BG_DISPATCH_PROBE_INTERVAL_MS = 60000;
|
|
12486
12645
|
var DEFAULT_SESSION_ID = "__default__";
|
|
12487
12646
|
var LOCALLY_SATISFIED_COMMANDS = new Set(["configure"]);
|
|
12488
12647
|
|
|
@@ -12545,12 +12704,15 @@ class BgSubscription {
|
|
|
12545
12704
|
canAttach;
|
|
12546
12705
|
onRootAttachFailure;
|
|
12547
12706
|
onDormant;
|
|
12707
|
+
dispatchProbeIntervalMs;
|
|
12548
12708
|
nudgeRef;
|
|
12549
12709
|
isCurrent;
|
|
12550
12710
|
stopped = false;
|
|
12551
12711
|
current = null;
|
|
12552
12712
|
loop;
|
|
12553
|
-
|
|
12713
|
+
lifecycleLogState = new Map;
|
|
12714
|
+
nudgeReceiptLogState = null;
|
|
12715
|
+
constructor(identity, acquireClient, dropClient, consumerIdentity, onNudge, sleep2, canAttach, onRootAttachFailure, onDormant, dispatchProbeIntervalMs, nudgeRef, isCurrent = () => true) {
|
|
12554
12716
|
this.identity = identity;
|
|
12555
12717
|
this.acquireClient = acquireClient;
|
|
12556
12718
|
this.dropClient = dropClient;
|
|
@@ -12560,6 +12722,7 @@ class BgSubscription {
|
|
|
12560
12722
|
this.canAttach = canAttach;
|
|
12561
12723
|
this.onRootAttachFailure = onRootAttachFailure;
|
|
12562
12724
|
this.onDormant = onDormant;
|
|
12725
|
+
this.dispatchProbeIntervalMs = dispatchProbeIntervalMs;
|
|
12563
12726
|
this.nudgeRef = nudgeRef;
|
|
12564
12727
|
this.isCurrent = isCurrent;
|
|
12565
12728
|
this.loop = this.run();
|
|
@@ -12576,25 +12739,106 @@ class BgSubscription {
|
|
|
12576
12739
|
return;
|
|
12577
12740
|
});
|
|
12578
12741
|
}
|
|
12742
|
+
info(kind, message) {
|
|
12743
|
+
const now = Date.now();
|
|
12744
|
+
const state = this.lifecycleLogState.get(kind);
|
|
12745
|
+
if (state && now - state.lastEmittedAt < BG_LIFECYCLE_LOG_INTERVAL_MS) {
|
|
12746
|
+
state.suppressed += 1;
|
|
12747
|
+
return;
|
|
12748
|
+
}
|
|
12749
|
+
const suppressed = state?.suppressed ?? 0;
|
|
12750
|
+
this.lifecycleLogState.set(kind, { lastEmittedAt: now, suppressed: 0 });
|
|
12751
|
+
const suffix = suppressed > 0 ? ` suppressed=${suppressed}` : "";
|
|
12752
|
+
log(`subc bg_events: ${message}${suffix}`, { sessionId: this.identity.session });
|
|
12753
|
+
}
|
|
12754
|
+
routeId(route) {
|
|
12755
|
+
return `${route.channel}@${route.epoch}`;
|
|
12756
|
+
}
|
|
12757
|
+
recordNudgeReceipt(routeId) {
|
|
12758
|
+
const now = Date.now();
|
|
12759
|
+
const state = this.nudgeReceiptLogState;
|
|
12760
|
+
if (state && now - state.lastEmittedAt < BG_LIFECYCLE_LOG_INTERVAL_MS) {
|
|
12761
|
+
state.count += 1;
|
|
12762
|
+
return;
|
|
12763
|
+
}
|
|
12764
|
+
const count = (state?.count ?? 0) + 1;
|
|
12765
|
+
this.nudgeReceiptLogState = { lastEmittedAt: now, count: 0 };
|
|
12766
|
+
log(`subc bg_events: nudge received channel=${routeId} count=${count}`, {
|
|
12767
|
+
sessionId: this.identity.session
|
|
12768
|
+
});
|
|
12769
|
+
}
|
|
12770
|
+
errorText(error2) {
|
|
12771
|
+
return error2 instanceof Error ? `${error2.name}: ${error2.message}` : String(error2);
|
|
12772
|
+
}
|
|
12773
|
+
startDispatchProbe(client, routeId) {
|
|
12774
|
+
const initial = client.droppedIngressFrames;
|
|
12775
|
+
if (typeof initial !== "number")
|
|
12776
|
+
return () => {
|
|
12777
|
+
return;
|
|
12778
|
+
};
|
|
12779
|
+
let previous = initial;
|
|
12780
|
+
const timer = setInterval(() => {
|
|
12781
|
+
const total = client.droppedIngressFrames;
|
|
12782
|
+
if (typeof total !== "number" || total <= previous)
|
|
12783
|
+
return;
|
|
12784
|
+
const delta = total - previous;
|
|
12785
|
+
previous = total;
|
|
12786
|
+
this.info("dispatch-epoch-drop", `client ingress epoch drops scope=client observed_while_channel=${routeId} delta=${delta} total=${total}`);
|
|
12787
|
+
}, this.dispatchProbeIntervalMs);
|
|
12788
|
+
timer.unref?.();
|
|
12789
|
+
return () => clearInterval(timer);
|
|
12790
|
+
}
|
|
12579
12791
|
async run() {
|
|
12580
|
-
let
|
|
12792
|
+
let backoffAttempt = 0;
|
|
12793
|
+
let reconnectAttempt = 0;
|
|
12794
|
+
let reconnecting = false;
|
|
12795
|
+
const beginReconnect = () => {
|
|
12796
|
+
reconnecting = true;
|
|
12797
|
+
reconnectAttempt = reconnectAttempt === 0 ? 1 : reconnectAttempt + 1;
|
|
12798
|
+
};
|
|
12799
|
+
const giveUp = (reason) => {
|
|
12800
|
+
this.info("reconnect-gave-up", `reconnect gave-up attempt=${reconnectAttempt} reason=${reason}`);
|
|
12801
|
+
};
|
|
12581
12802
|
while (!this.stopped) {
|
|
12582
|
-
if (!this.isCurrent())
|
|
12803
|
+
if (!this.isCurrent()) {
|
|
12804
|
+
if (reconnecting)
|
|
12805
|
+
giveUp("stale-session");
|
|
12583
12806
|
return;
|
|
12807
|
+
}
|
|
12584
12808
|
if (!this.canAttach()) {
|
|
12809
|
+
if (reconnecting)
|
|
12810
|
+
giveUp("root-dormant");
|
|
12585
12811
|
this.onDormant();
|
|
12586
12812
|
return;
|
|
12587
12813
|
}
|
|
12814
|
+
if (reconnecting) {
|
|
12815
|
+
this.info("reconnect-attempt", `reconnect attempt=${reconnectAttempt}`);
|
|
12816
|
+
}
|
|
12588
12817
|
let client;
|
|
12589
12818
|
try {
|
|
12590
12819
|
client = await this.acquireClient();
|
|
12591
|
-
} catch {
|
|
12592
|
-
|
|
12820
|
+
} catch (err) {
|
|
12821
|
+
if (!reconnecting)
|
|
12822
|
+
beginReconnect();
|
|
12823
|
+
this.info("reconnect-error", `reconnect error attempt=${reconnectAttempt} error=${this.errorText(err)}`);
|
|
12824
|
+
await this.backoff(backoffAttempt++);
|
|
12825
|
+
if (reconnecting)
|
|
12826
|
+
reconnectAttempt += 1;
|
|
12593
12827
|
continue;
|
|
12594
12828
|
}
|
|
12595
|
-
if (this.stopped
|
|
12829
|
+
if (this.stopped) {
|
|
12830
|
+
if (reconnecting)
|
|
12831
|
+
giveUp("stopped");
|
|
12596
12832
|
return;
|
|
12833
|
+
}
|
|
12834
|
+
if (!this.isCurrent()) {
|
|
12835
|
+
if (reconnecting)
|
|
12836
|
+
giveUp("stale-session");
|
|
12837
|
+
return;
|
|
12838
|
+
}
|
|
12597
12839
|
if (!this.canAttach()) {
|
|
12840
|
+
if (reconnecting)
|
|
12841
|
+
giveUp("root-dormant");
|
|
12598
12842
|
this.onDormant();
|
|
12599
12843
|
return;
|
|
12600
12844
|
}
|
|
@@ -12603,44 +12847,84 @@ class BgSubscription {
|
|
|
12603
12847
|
route = await client.routeOpen({ kind: "tool_provider", module_id: AFT_MODULE_ID }, this.identity, { consumerIdentity: this.consumerIdentity });
|
|
12604
12848
|
} catch (err) {
|
|
12605
12849
|
if (this.isCurrent() && this.onRootAttachFailure(err)) {
|
|
12850
|
+
if (reconnecting)
|
|
12851
|
+
giveUp("root-dormant");
|
|
12606
12852
|
this.onDormant();
|
|
12607
12853
|
return;
|
|
12608
12854
|
}
|
|
12609
12855
|
if (isConsumerReconnectTransient(err))
|
|
12610
12856
|
this.dropClient(client);
|
|
12611
|
-
|
|
12857
|
+
if (!reconnecting)
|
|
12858
|
+
beginReconnect();
|
|
12859
|
+
this.info("reconnect-error", `reconnect error attempt=${reconnectAttempt} error=${this.errorText(err)}`);
|
|
12860
|
+
await this.backoff(backoffAttempt++);
|
|
12861
|
+
if (reconnecting)
|
|
12862
|
+
reconnectAttempt += 1;
|
|
12612
12863
|
continue;
|
|
12613
12864
|
}
|
|
12614
12865
|
if (this.stopped || !this.isCurrent()) {
|
|
12615
12866
|
safeCloseRoute(client, route);
|
|
12867
|
+
if (reconnecting)
|
|
12868
|
+
giveUp(this.stopped ? "stopped" : "stale-session");
|
|
12616
12869
|
return;
|
|
12617
12870
|
}
|
|
12618
12871
|
const subscribedAt = Date.now();
|
|
12872
|
+
const routeId = this.routeId(route);
|
|
12873
|
+
let stopDispatchProbe = () => {
|
|
12874
|
+
return;
|
|
12875
|
+
};
|
|
12619
12876
|
try {
|
|
12620
12877
|
const sub = client.subscribe(route, { op: "bg_events" }, () => {
|
|
12621
|
-
if (
|
|
12622
|
-
this.
|
|
12878
|
+
if (this.stopped) {
|
|
12879
|
+
this.info("nudge-drop-stopped", `nudge dropped cause=subscription-stopped channel=${routeId}`);
|
|
12880
|
+
return;
|
|
12881
|
+
}
|
|
12882
|
+
this.recordNudgeReceipt(routeId);
|
|
12883
|
+
if (!this.isCurrent()) {
|
|
12884
|
+
this.info("nudge-stale-carrier", `nudge carried by stale subscription; checking current session channel=${routeId}`);
|
|
12885
|
+
}
|
|
12886
|
+
this.onNudge();
|
|
12623
12887
|
});
|
|
12624
12888
|
this.current = sub;
|
|
12889
|
+
stopDispatchProbe = this.startDispatchProbe(client, routeId);
|
|
12890
|
+
this.info("subscription-open", `subscription open channel=${routeId}`);
|
|
12891
|
+
if (reconnecting) {
|
|
12892
|
+
this.info("reconnect-success", `reconnect success attempt=${reconnectAttempt} channel=${routeId}`);
|
|
12893
|
+
reconnecting = false;
|
|
12894
|
+
reconnectAttempt = 0;
|
|
12895
|
+
}
|
|
12625
12896
|
if (this.stopped)
|
|
12626
12897
|
sub.unsubscribe();
|
|
12627
12898
|
if (!this.stopped && this.isCurrent())
|
|
12628
12899
|
this.onNudge();
|
|
12629
12900
|
await sub.closed;
|
|
12630
|
-
|
|
12901
|
+
this.info("stream-end", `stream ended channel=${routeId}`);
|
|
12902
|
+
if (this.stopped) {
|
|
12903
|
+
giveUp("stopped");
|
|
12904
|
+
return;
|
|
12905
|
+
}
|
|
12906
|
+
beginReconnect();
|
|
12631
12907
|
} catch (err) {
|
|
12632
|
-
|
|
12908
|
+
const routeId2 = this.routeId(route);
|
|
12909
|
+
this.info("stream-error", `stream error channel=${routeId2} error=${this.errorText(err)}`);
|
|
12910
|
+
if (this.stopped) {
|
|
12911
|
+
giveUp("stopped");
|
|
12633
12912
|
return;
|
|
12913
|
+
}
|
|
12634
12914
|
if (isConsumerReconnectTransient(err))
|
|
12635
12915
|
this.dropClient(client);
|
|
12636
12916
|
if (Date.now() - subscribedAt >= BG_STABLE_MS)
|
|
12637
|
-
|
|
12917
|
+
backoffAttempt = 0;
|
|
12918
|
+
beginReconnect();
|
|
12638
12919
|
} finally {
|
|
12920
|
+
stopDispatchProbe();
|
|
12639
12921
|
this.current = null;
|
|
12640
12922
|
safeCloseRoute(client, route);
|
|
12641
12923
|
}
|
|
12642
|
-
await this.backoff(
|
|
12924
|
+
await this.backoff(backoffAttempt++);
|
|
12643
12925
|
}
|
|
12926
|
+
if (reconnecting)
|
|
12927
|
+
giveUp("stopped");
|
|
12644
12928
|
}
|
|
12645
12929
|
async backoff(attempt) {
|
|
12646
12930
|
const ms = Math.min(100 * 2 ** Math.min(attempt, 6), 2000);
|
|
@@ -12738,6 +13022,9 @@ class SubcTransport {
|
|
|
12738
13022
|
this.assertCurrent();
|
|
12739
13023
|
const { preview, timeoutMs, onProgress } = this.splitOptions(options);
|
|
12740
13024
|
const body = { name, arguments: rawArgs };
|
|
13025
|
+
const editSlotSurvives = this.pool.getEditSlotSurvives();
|
|
13026
|
+
if (editSlotSurvives !== undefined)
|
|
13027
|
+
body.edit_slot_survives = editSlotSurvives;
|
|
12741
13028
|
if (preview === true)
|
|
12742
13029
|
body.preview = true;
|
|
12743
13030
|
const reply = await this.pool.routeRequest(this.identityFor(sessionId), body, timeoutMs, onProgress, this.generation);
|
|
@@ -12752,7 +13039,11 @@ class SubcTransport {
|
|
|
12752
13039
|
}
|
|
12753
13040
|
const { timeoutMs, onProgress } = this.splitOptions(options);
|
|
12754
13041
|
const session = typeof params.session_id === "string" ? params.session_id : undefined;
|
|
12755
|
-
const
|
|
13042
|
+
const body = { name: command, arguments: params };
|
|
13043
|
+
const editSlotSurvives = this.pool.getEditSlotSurvives();
|
|
13044
|
+
if (editSlotSurvives !== undefined)
|
|
13045
|
+
body.edit_slot_survives = editSlotSurvives;
|
|
13046
|
+
const reply = await this.pool.routeRequest(this.identityFor(session), body, timeoutMs, onProgress, this.generation);
|
|
12756
13047
|
const response = reliftReply(reply);
|
|
12757
13048
|
this.captureStatusBar(response);
|
|
12758
13049
|
return response;
|
|
@@ -12776,6 +13067,7 @@ class SubcTransportPool {
|
|
|
12776
13067
|
onBgEventsNudge;
|
|
12777
13068
|
onBgEventsNudgeRef;
|
|
12778
13069
|
bgBackoffSleep;
|
|
13070
|
+
bgDispatchProbeIntervalMs;
|
|
12779
13071
|
lifecycleDemandCheck;
|
|
12780
13072
|
onLifecycleEvent;
|
|
12781
13073
|
onBgNudgeRejected;
|
|
@@ -12791,8 +13083,11 @@ class SubcTransportPool {
|
|
|
12791
13083
|
transportFailures = 0;
|
|
12792
13084
|
transports = new Map;
|
|
12793
13085
|
generationRejections = new Set;
|
|
13086
|
+
nudgeDeliveryLogState = new Map;
|
|
12794
13087
|
pendingRootCleanups = new Set;
|
|
12795
13088
|
shuttingDown = false;
|
|
13089
|
+
editSlotSurvives;
|
|
13090
|
+
editSlotSurvivesCaptured = false;
|
|
12796
13091
|
constructor(options) {
|
|
12797
13092
|
this.connectionFile = options.connectionFile;
|
|
12798
13093
|
this.harness = options.harness;
|
|
@@ -12802,6 +13097,7 @@ class SubcTransportPool {
|
|
|
12802
13097
|
this.onBgEventsNudge = options.onBgEventsNudge;
|
|
12803
13098
|
this.onBgEventsNudgeRef = options.onBgEventsNudgeRef;
|
|
12804
13099
|
this.bgBackoffSleep = options.bgBackoffSleep ?? ((ms) => new Promise((resolve4) => setTimeout(resolve4, ms)));
|
|
13100
|
+
this.bgDispatchProbeIntervalMs = options.bgDispatchProbeIntervalMs ?? BG_DISPATCH_PROBE_INTERVAL_MS;
|
|
12805
13101
|
const lifecycle = options.lifecycle;
|
|
12806
13102
|
const demandCheck = options.lifecycleDemandCheck ?? options.demandCheck ?? lifecycle?.demandCheck;
|
|
12807
13103
|
this.lifecycleDemandCheck = demandCheck;
|
|
@@ -13118,6 +13414,35 @@ class SubcTransportPool {
|
|
|
13118
13414
|
isCurrentSession(key, record) {
|
|
13119
13415
|
return this.sessions.get(key) === record && !record.closed;
|
|
13120
13416
|
}
|
|
13417
|
+
currentSessionForNudge(identity) {
|
|
13418
|
+
const current = this.sessions.get(identityKey(identity));
|
|
13419
|
+
return current && !current.closed ? current : null;
|
|
13420
|
+
}
|
|
13421
|
+
nudgeRefFor(record) {
|
|
13422
|
+
const poolId = this.currentPoolId();
|
|
13423
|
+
const generation = record.generation;
|
|
13424
|
+
if (poolId === undefined || generation === undefined)
|
|
13425
|
+
return;
|
|
13426
|
+
return {
|
|
13427
|
+
canonicalRoot: record.canonicalRoot,
|
|
13428
|
+
session: record.identity.session,
|
|
13429
|
+
concretePoolId: poolId,
|
|
13430
|
+
generation
|
|
13431
|
+
};
|
|
13432
|
+
}
|
|
13433
|
+
logNudgeDelivery(kind, record, message) {
|
|
13434
|
+
const key = `${kind}\x00${record.identityKey}`;
|
|
13435
|
+
const now = Date.now();
|
|
13436
|
+
const state = this.nudgeDeliveryLogState.get(key);
|
|
13437
|
+
if (state && now - state.lastEmittedAt < BG_LIFECYCLE_LOG_INTERVAL_MS) {
|
|
13438
|
+
state.suppressed += 1;
|
|
13439
|
+
return;
|
|
13440
|
+
}
|
|
13441
|
+
const suppressed = state?.suppressed ?? 0;
|
|
13442
|
+
this.nudgeDeliveryLogState.set(key, { lastEmittedAt: now, suppressed: 0 });
|
|
13443
|
+
const suffix = suppressed > 0 ? ` suppressed=${suppressed}` : "";
|
|
13444
|
+
log(`subc bg_events: ${message}${suffix}`, { sessionId: record.identity.session });
|
|
13445
|
+
}
|
|
13121
13446
|
removeIndexMembership(record) {
|
|
13122
13447
|
const keys = this.rootIndex.get(record.canonicalRoot);
|
|
13123
13448
|
if (!keys)
|
|
@@ -13371,20 +13696,32 @@ class SubcTransportPool {
|
|
|
13371
13696
|
return;
|
|
13372
13697
|
if (record.bgSub)
|
|
13373
13698
|
return;
|
|
13374
|
-
const
|
|
13375
|
-
const generation = record.generation;
|
|
13376
|
-
const nudgeRef = poolId !== undefined && generation !== undefined ? {
|
|
13377
|
-
canonicalRoot: record.canonicalRoot,
|
|
13378
|
-
session: identity.session,
|
|
13379
|
-
concretePoolId: poolId,
|
|
13380
|
-
generation
|
|
13381
|
-
} : undefined;
|
|
13699
|
+
const nudgeRef = this.nudgeRefFor(record);
|
|
13382
13700
|
const onNudge = () => {
|
|
13383
|
-
|
|
13701
|
+
const currentRecord = this.currentSessionForNudge(identity);
|
|
13702
|
+
if (!currentRecord) {
|
|
13703
|
+
this.logNudgeDelivery("drop-no-current-session", record, `nudge dropped cause=no-current-session root=${record.canonicalRoot}`);
|
|
13704
|
+
return;
|
|
13705
|
+
}
|
|
13706
|
+
if (currentRecord !== record) {
|
|
13707
|
+
this.logNudgeDelivery("forward-superseded-carrier", currentRecord, `nudge forwarding cause=superseded-carrying-record root=${currentRecord.canonicalRoot}`);
|
|
13708
|
+
}
|
|
13709
|
+
const currentRef = this.nudgeRefFor(currentRecord);
|
|
13710
|
+
let delivered = false;
|
|
13711
|
+
if (currentRef && this.onBgEventsNudgeRef) {
|
|
13712
|
+
this.onBgEventsNudgeRef(currentRef);
|
|
13713
|
+
delivered = true;
|
|
13714
|
+
}
|
|
13715
|
+
if (this.onBgEventsNudge) {
|
|
13716
|
+
if (!currentRef && this.onBgEventsNudgeRef) {
|
|
13717
|
+
this.logNudgeDelivery("fallback-missing-generation", currentRecord, `nudge dispatch fallback=root-session-handler cause=generation-provenance-unavailable root=${currentRecord.canonicalRoot}`);
|
|
13718
|
+
}
|
|
13719
|
+
this.onBgEventsNudge(currentRecord.identity.project_root, currentRecord.identity.session);
|
|
13720
|
+
delivered = true;
|
|
13721
|
+
}
|
|
13722
|
+
if (delivered)
|
|
13384
13723
|
return;
|
|
13385
|
-
this.
|
|
13386
|
-
if (nudgeRef)
|
|
13387
|
-
this.onBgEventsNudgeRef?.(nudgeRef);
|
|
13724
|
+
this.logNudgeDelivery("drop-no-compatible-handler", currentRecord, `nudge dropped cause=generation-provenance-unavailable-and-root-session-handler-unwired root=${currentRecord.canonicalRoot}`);
|
|
13388
13725
|
};
|
|
13389
13726
|
let sub = null;
|
|
13390
13727
|
const clearDormantSubscription = () => {
|
|
@@ -13396,7 +13733,7 @@ class SubcTransportPool {
|
|
|
13396
13733
|
return false;
|
|
13397
13734
|
this.markRootDormant(record.canonicalRoot, error2);
|
|
13398
13735
|
return true;
|
|
13399
|
-
}, clearDormantSubscription, nudgeRef, () => this.isCurrentSession(record.identityKey, record) && this.isCurrentLiveGeneration(record.canonicalRoot, record.generation));
|
|
13736
|
+
}, clearDormantSubscription, this.bgDispatchProbeIntervalMs, nudgeRef, () => this.isCurrentSession(record.identityKey, record) && this.isCurrentLiveGeneration(record.canonicalRoot, record.generation));
|
|
13400
13737
|
record.bgSub = sub;
|
|
13401
13738
|
if (!this.rootCanAttach(record.canonicalRoot)) {
|
|
13402
13739
|
record.bgSub = null;
|
|
@@ -13458,7 +13795,21 @@ class SubcTransportPool {
|
|
|
13458
13795
|
return Promise.resolve();
|
|
13459
13796
|
return registry.requestProjectRootClose(registration.concretePoolId, root, generation, cause);
|
|
13460
13797
|
}
|
|
13461
|
-
setConfigureOverride(
|
|
13798
|
+
setConfigureOverride(key, value) {
|
|
13799
|
+
if (key !== "edit_slot_survives")
|
|
13800
|
+
return;
|
|
13801
|
+
if (typeof value !== "boolean") {
|
|
13802
|
+
throw new Error("edit_slot_survives must be set once to a boolean");
|
|
13803
|
+
}
|
|
13804
|
+
if (this.editSlotSurvivesCaptured) {
|
|
13805
|
+
throw new Error("edit_slot_survives is write-once and was already captured");
|
|
13806
|
+
}
|
|
13807
|
+
this.editSlotSurvives = value;
|
|
13808
|
+
this.editSlotSurvivesCaptured = true;
|
|
13809
|
+
}
|
|
13810
|
+
getEditSlotSurvives() {
|
|
13811
|
+
return this.editSlotSurvives;
|
|
13812
|
+
}
|
|
13462
13813
|
async reconfigure(_projectRoot, _overrides) {}
|
|
13463
13814
|
async replaceBinary(path2) {
|
|
13464
13815
|
return path2;
|
|
@@ -13536,14 +13887,29 @@ function toolErrorFromResponse(command, response) {
|
|
|
13536
13887
|
return new AftToolError(message, code, response);
|
|
13537
13888
|
}
|
|
13538
13889
|
var BASH_TRANSPORT_DISPOSITION = "The transport to the AFT daemon was interrupted; no background task was created for this command and no task ID exists. Re-run the command. Do not poll bash_status for it.";
|
|
13890
|
+
var SUBC_MODULE_RESTART_DISPOSITION = "The AFT daemon module restarted while this call was in flight, so its outcome is UNKNOWN: it may or may not have executed. Verify actual state before re-running, and never blind-retry a mutation.";
|
|
13891
|
+
function isRouteGoodbyeError(error2) {
|
|
13892
|
+
if (!(error2 instanceof SubcError))
|
|
13893
|
+
return false;
|
|
13894
|
+
if (error2.code === undefined) {
|
|
13895
|
+
return error2.message.includes("route closed by subc");
|
|
13896
|
+
}
|
|
13897
|
+
return error2.code === "route_closed" && error2.message.includes("route closed by subc");
|
|
13898
|
+
}
|
|
13539
13899
|
function isTransportClassError(error2) {
|
|
13540
13900
|
return isBridgeTransportTimeout(error2) || isConsumerReconnectTransient(error2) || error2 instanceof StaleRouteHandleError || error2 instanceof SubcRootGenerationExpiredError || error2 instanceof SubcRootReapedError;
|
|
13541
13901
|
}
|
|
13542
13902
|
function adaptToolError(command, error2) {
|
|
13543
|
-
if (command !== "bash" || !isTransportClassError(error2))
|
|
13544
|
-
return error2;
|
|
13545
13903
|
if (!(error2 instanceof Error))
|
|
13546
13904
|
return error2;
|
|
13905
|
+
if (isRouteGoodbyeError(error2)) {
|
|
13906
|
+
if (error2.message.includes(SUBC_MODULE_RESTART_DISPOSITION))
|
|
13907
|
+
return error2;
|
|
13908
|
+
error2.message = error2.message ? `${error2.message} ${SUBC_MODULE_RESTART_DISPOSITION}` : SUBC_MODULE_RESTART_DISPOSITION;
|
|
13909
|
+
return error2;
|
|
13910
|
+
}
|
|
13911
|
+
if (command !== "bash" || !isTransportClassError(error2))
|
|
13912
|
+
return error2;
|
|
13547
13913
|
if (error2.message.includes(BASH_TRANSPORT_DISPOSITION))
|
|
13548
13914
|
return error2;
|
|
13549
13915
|
error2.message = error2.message ? `${error2.message} ${BASH_TRANSPORT_DISPOSITION}` : BASH_TRANSPORT_DISPOSITION;
|
|
@@ -14343,7 +14709,7 @@ async function ensureStorageMigrated(opts) {
|
|
|
14343
14709
|
throw new Error(`AFT storage migration failed (${detail}). ` + `Harness: ${opts.harness}. Legacy: ${legacyRoot}. Target: ${newRoot}. ` + `See log: ${logPath}. ` + `Plugin load aborted to prevent legacy/new state divergence.` + (stderrTail ? ` Stderr tail: ${stderrTail}` : "") + (stdoutTail ? ` Stdout tail: ${stdoutTail}` : ""));
|
|
14344
14710
|
}
|
|
14345
14711
|
// ../aft-bridge/dist/npm-resolver.js
|
|
14346
|
-
import { readdirSync, statSync as statSync5 } from "node:fs";
|
|
14712
|
+
import { readdirSync as readdirSync2, statSync as statSync5 } from "node:fs";
|
|
14347
14713
|
import { homedir as homedir8 } from "node:os";
|
|
14348
14714
|
import { delimiter, dirname as dirname4, isAbsolute as isAbsolute3, join as join9 } from "node:path";
|
|
14349
14715
|
function defaultDeps() {
|
|
@@ -14383,7 +14749,7 @@ function npmAdjacentToNode(deps) {
|
|
|
14383
14749
|
function highestVersionedNodeBin(installsDir, name) {
|
|
14384
14750
|
let entries;
|
|
14385
14751
|
try {
|
|
14386
|
-
entries =
|
|
14752
|
+
entries = readdirSync2(installsDir);
|
|
14387
14753
|
} catch {
|
|
14388
14754
|
return null;
|
|
14389
14755
|
}
|
|
@@ -14462,7 +14828,7 @@ function isNpmAvailable(deps = defaultDeps()) {
|
|
|
14462
14828
|
// ../aft-bridge/dist/onnx-runtime.js
|
|
14463
14829
|
import { execFileSync } from "node:child_process";
|
|
14464
14830
|
import { createHash as createHash4 } from "node:crypto";
|
|
14465
|
-
import { chmodSync as chmodSync3, closeSync as closeSync4, copyFileSync as copyFileSync3, createWriteStream as createWriteStream2, existsSync as existsSync7, lstatSync, mkdirSync as mkdirSync5, openSync as openSync4, readdirSync as
|
|
14831
|
+
import { chmodSync as chmodSync3, closeSync as closeSync4, copyFileSync as copyFileSync3, createWriteStream as createWriteStream2, existsSync as existsSync7, lstatSync, mkdirSync as mkdirSync5, openSync as openSync4, readdirSync as readdirSync3, readFileSync as readFileSync6, readlinkSync, realpathSync as realpathSync2, rmSync as rmSync3, statSync as statSync6, symlinkSync, unlinkSync as unlinkSync4, writeFileSync as writeFileSync3 } from "node:fs";
|
|
14466
14832
|
import { basename as basename2, dirname as dirname5, isAbsolute as isAbsolute4, join as join10, relative as relative2, resolve as resolve6, win32 } from "node:path";
|
|
14467
14833
|
import { Readable as Readable2 } from "node:stream";
|
|
14468
14834
|
import { pipeline as pipeline2 } from "node:stream/promises";
|
|
@@ -14579,7 +14945,7 @@ async function ensureOnnxRuntime(storageDir) {
|
|
|
14579
14945
|
}
|
|
14580
14946
|
function cleanupAbandonedStagingDirs(onnxBaseDir) {
|
|
14581
14947
|
try {
|
|
14582
|
-
const entries =
|
|
14948
|
+
const entries = readdirSync3(onnxBaseDir);
|
|
14583
14949
|
for (const entry of entries) {
|
|
14584
14950
|
if (!entry.startsWith(`${ORT_VERSION}.tmp.`))
|
|
14585
14951
|
continue;
|
|
@@ -14590,7 +14956,7 @@ function cleanupAbandonedStagingDirs(onnxBaseDir) {
|
|
|
14590
14956
|
let abandoned = false;
|
|
14591
14957
|
if (Number.isFinite(pid) && pid > 0) {
|
|
14592
14958
|
if (process.platform === "win32") {
|
|
14593
|
-
const ownerAlive =
|
|
14959
|
+
const ownerAlive = isProcessAlive2(pid);
|
|
14594
14960
|
if (!ownerAlive) {
|
|
14595
14961
|
abandoned = true;
|
|
14596
14962
|
} else {
|
|
@@ -14602,7 +14968,7 @@ function cleanupAbandonedStagingDirs(onnxBaseDir) {
|
|
|
14602
14968
|
}
|
|
14603
14969
|
}
|
|
14604
14970
|
} else {
|
|
14605
|
-
abandoned = !
|
|
14971
|
+
abandoned = !isProcessAlive2(pid);
|
|
14606
14972
|
}
|
|
14607
14973
|
} else {
|
|
14608
14974
|
abandoned = true;
|
|
@@ -14653,7 +15019,7 @@ function isPathInsideRoot(root, candidate) {
|
|
|
14653
15019
|
}
|
|
14654
15020
|
function detectOnnxVersion(libDir, libName) {
|
|
14655
15021
|
try {
|
|
14656
|
-
const entries =
|
|
15022
|
+
const entries = readdirSync3(libDir);
|
|
14657
15023
|
const barePrefix = libName.replace(/\.(so|dylib|dll)$/, "");
|
|
14658
15024
|
const expectedPrefix = process.platform === "win32" ? barePrefix.toLowerCase() : barePrefix;
|
|
14659
15025
|
for (const entry of entries) {
|
|
@@ -14716,7 +15082,7 @@ function isWindowsSystem32Directory(dir) {
|
|
|
14716
15082
|
}
|
|
14717
15083
|
function directoryContainsLibrary(dir, libName) {
|
|
14718
15084
|
try {
|
|
14719
|
-
const entries =
|
|
15085
|
+
const entries = readdirSync3(dir);
|
|
14720
15086
|
if (process.platform === "win32") {
|
|
14721
15087
|
const expected = libName.toLowerCase();
|
|
14722
15088
|
return entries.some((entry) => entry.toLowerCase() === expected);
|
|
@@ -14754,7 +15120,7 @@ function findSystemOnnxRuntime(libName) {
|
|
|
14754
15120
|
if (!existsSync7(nugetPackageDir))
|
|
14755
15121
|
return nugetPaths;
|
|
14756
15122
|
try {
|
|
14757
|
-
for (const entry of
|
|
15123
|
+
for (const entry of readdirSync3(nugetPackageDir, { withFileTypes: true })) {
|
|
14758
15124
|
if (!entry.isDirectory())
|
|
14759
15125
|
continue;
|
|
14760
15126
|
if (entry.name === "__globalPackagesFolder" || entry.name.startsWith("."))
|
|
@@ -14849,7 +15215,7 @@ function validateExtractedTree(stagingRoot) {
|
|
|
14849
15215
|
const realRoot = realpathSync2(stagingRoot);
|
|
14850
15216
|
let totalBytes = 0;
|
|
14851
15217
|
const walk = (dir) => {
|
|
14852
|
-
const entries =
|
|
15218
|
+
const entries = readdirSync3(dir);
|
|
14853
15219
|
for (const entry of entries) {
|
|
14854
15220
|
const fullPath = join10(dir, entry);
|
|
14855
15221
|
const lst = lstatSync(fullPath);
|
|
@@ -14907,7 +15273,7 @@ async function downloadOnnxRuntime(info, targetDir) {
|
|
|
14907
15273
|
throw new Error(`Expected directory not found: ${extractedDir}`);
|
|
14908
15274
|
}
|
|
14909
15275
|
mkdirSync5(targetDir, { recursive: true });
|
|
14910
|
-
const libFiles =
|
|
15276
|
+
const libFiles = readdirSync3(extractedDir).filter((f) => f.startsWith("libonnxruntime") || f.startsWith("onnxruntime"));
|
|
14911
15277
|
const realFiles = [];
|
|
14912
15278
|
const symlinks = [];
|
|
14913
15279
|
for (const libFile of libFiles) {
|
|
@@ -15085,7 +15451,7 @@ ${new Date().toISOString()}
|
|
|
15085
15451
|
}
|
|
15086
15452
|
const age = Date.now() - lockMtimeMs;
|
|
15087
15453
|
const ageWithinFresh = Math.abs(age) < STALE_LOCK_MS;
|
|
15088
|
-
const ownerAlive = owningPid !== null &&
|
|
15454
|
+
const ownerAlive = owningPid !== null && isProcessAlive2(owningPid);
|
|
15089
15455
|
if (ownerAlive && ageWithinFresh) {
|
|
15090
15456
|
return false;
|
|
15091
15457
|
}
|
|
@@ -15149,7 +15515,7 @@ function isWindowsProcessAlive(pid) {
|
|
|
15149
15515
|
return false;
|
|
15150
15516
|
}
|
|
15151
15517
|
}
|
|
15152
|
-
function
|
|
15518
|
+
function isProcessAlive2(pid) {
|
|
15153
15519
|
if (process.platform === "win32")
|
|
15154
15520
|
return isWindowsProcessAlive(pid);
|
|
15155
15521
|
try {
|
|
@@ -15524,6 +15890,19 @@ function parseEditArray(value) {
|
|
|
15524
15890
|
}
|
|
15525
15891
|
return value;
|
|
15526
15892
|
}
|
|
15893
|
+
function stripLineRangeSentinels(item) {
|
|
15894
|
+
const hasRangeField = ["startLine", "endLine", "content"].some((key) => hasOwn(item, key));
|
|
15895
|
+
if (!hasRangeField)
|
|
15896
|
+
return;
|
|
15897
|
+
if (item.oldString === "")
|
|
15898
|
+
delete item.oldString;
|
|
15899
|
+
if (item.newString === "")
|
|
15900
|
+
delete item.newString;
|
|
15901
|
+
if (item.replaceAll === false)
|
|
15902
|
+
delete item.replaceAll;
|
|
15903
|
+
if (item.occurrence === 1)
|
|
15904
|
+
delete item.occurrence;
|
|
15905
|
+
}
|
|
15527
15906
|
function normalizeEditItem(value, index) {
|
|
15528
15907
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
15529
15908
|
throw new InvalidRequestError(`edit: edits[${index}] must be an object`);
|
|
@@ -15532,6 +15911,7 @@ function normalizeEditItem(value, index) {
|
|
|
15532
15911
|
const item = copyOwnProperties(source);
|
|
15533
15912
|
normalizeItemAlias(item, "oldString", "oldText");
|
|
15534
15913
|
normalizeItemAlias(item, "newString", "newText");
|
|
15914
|
+
stripLineRangeSentinels(item);
|
|
15535
15915
|
const hasFindField = ["oldString", "newString", "replaceAll", "occurrence"].some((key) => hasOwn(item, key));
|
|
15536
15916
|
const hasRangeField = ["startLine", "endLine", "content"].some((key) => hasOwn(item, key));
|
|
15537
15917
|
if (hasFindField && hasRangeField) {
|
|
@@ -15656,6 +16036,8 @@ class BridgePool {
|
|
|
15656
16036
|
idleTimeoutMs;
|
|
15657
16037
|
bridgeOptions;
|
|
15658
16038
|
configOverrides;
|
|
16039
|
+
editSlotSurvives;
|
|
16040
|
+
editSlotSurvivesCaptured = false;
|
|
15659
16041
|
projectConfigLoader;
|
|
15660
16042
|
logger;
|
|
15661
16043
|
cleanupTimer = null;
|
|
@@ -15680,7 +16062,16 @@ class BridgePool {
|
|
|
15680
16062
|
logger: options.logger,
|
|
15681
16063
|
childEnv: options.childEnv
|
|
15682
16064
|
};
|
|
15683
|
-
this.configOverrides = configOverrides;
|
|
16065
|
+
this.configOverrides = { ...configOverrides };
|
|
16066
|
+
const initialEditSlotSurvives = this.configOverrides.edit_slot_survives;
|
|
16067
|
+
delete this.configOverrides.edit_slot_survives;
|
|
16068
|
+
if (initialEditSlotSurvives !== undefined) {
|
|
16069
|
+
if (typeof initialEditSlotSurvives !== "boolean") {
|
|
16070
|
+
throw new Error("edit_slot_survives must be a boolean");
|
|
16071
|
+
}
|
|
16072
|
+
this.editSlotSurvives = initialEditSlotSurvives;
|
|
16073
|
+
this.editSlotSurvivesCaptured = true;
|
|
16074
|
+
}
|
|
15684
16075
|
this.startCleanupTimer();
|
|
15685
16076
|
}
|
|
15686
16077
|
getActiveBridgeForRoot(projectRoot) {
|
|
@@ -15719,7 +16110,8 @@ class BridgePool {
|
|
|
15719
16110
|
}
|
|
15720
16111
|
const projectOverrides = this.loadProjectOverrides(key);
|
|
15721
16112
|
const mergedOverrides = { ...this.configOverrides, ...projectOverrides };
|
|
15722
|
-
|
|
16113
|
+
delete mergedOverrides.edit_slot_survives;
|
|
16114
|
+
const bridge = new BinaryBridge(this.binaryPath, key, this.bridgeOptions, mergedOverrides, this.editSlotSurvivesCaptured ? this.editSlotSurvives : undefined);
|
|
15723
16115
|
this.bridges.set(key, { bridge, lastUsed: Date.now() });
|
|
15724
16116
|
return bridge;
|
|
15725
16117
|
}
|
|
@@ -15823,6 +16215,23 @@ class BridgePool {
|
|
|
15823
16215
|
error(message, meta);
|
|
15824
16216
|
}
|
|
15825
16217
|
setConfigureOverride(key, value) {
|
|
16218
|
+
if (key === "edit_slot_survives") {
|
|
16219
|
+
if (typeof value !== "boolean") {
|
|
16220
|
+
throw new Error("edit_slot_survives must be set once to a boolean");
|
|
16221
|
+
}
|
|
16222
|
+
if (this.editSlotSurvivesCaptured) {
|
|
16223
|
+
throw new Error("edit_slot_survives is write-once and was already captured");
|
|
16224
|
+
}
|
|
16225
|
+
this.editSlotSurvives = value;
|
|
16226
|
+
this.editSlotSurvivesCaptured = true;
|
|
16227
|
+
for (const entry of this.bridges.values()) {
|
|
16228
|
+
entry.bridge.setEditSlotSurvives(value);
|
|
16229
|
+
}
|
|
16230
|
+
for (const bridge of this.staleBridges) {
|
|
16231
|
+
bridge.setEditSlotSurvives(value);
|
|
16232
|
+
}
|
|
16233
|
+
return;
|
|
16234
|
+
}
|
|
15826
16235
|
if (value === undefined) {
|
|
15827
16236
|
delete this.configOverrides[key];
|
|
15828
16237
|
} else {
|
|
@@ -15880,6 +16289,7 @@ class RevivableTransportPool {
|
|
|
15880
16289
|
revival = null;
|
|
15881
16290
|
transports = new Map;
|
|
15882
16291
|
configureOverrides = new Map;
|
|
16292
|
+
editSlotSurvivesCaptured = false;
|
|
15883
16293
|
constructor(initialPool, createPool, onBinaryReplaced) {
|
|
15884
16294
|
this.createPool = createPool;
|
|
15885
16295
|
this.onBinaryReplaced = onBinaryReplaced;
|
|
@@ -15910,6 +16320,18 @@ class RevivableTransportPool {
|
|
|
15910
16320
|
return this.getBridge(projectRoot).toolCall(runtime.sessionID, name, rawArgs, options);
|
|
15911
16321
|
}
|
|
15912
16322
|
setConfigureOverride(key, value) {
|
|
16323
|
+
if (key === "edit_slot_survives") {
|
|
16324
|
+
if (typeof value !== "boolean") {
|
|
16325
|
+
throw new Error("edit_slot_survives must be set once to a boolean");
|
|
16326
|
+
}
|
|
16327
|
+
if (this.editSlotSurvivesCaptured) {
|
|
16328
|
+
throw new Error("edit_slot_survives is write-once and was already captured");
|
|
16329
|
+
}
|
|
16330
|
+
this.activePool.setConfigureOverride(key, value);
|
|
16331
|
+
this.editSlotSurvivesCaptured = true;
|
|
16332
|
+
this.configureOverrides.set(key, value);
|
|
16333
|
+
return;
|
|
16334
|
+
}
|
|
15913
16335
|
if (value === undefined)
|
|
15914
16336
|
this.configureOverrides.delete(key);
|
|
15915
16337
|
else
|
|
@@ -15917,14 +16339,20 @@ class RevivableTransportPool {
|
|
|
15917
16339
|
this.activePool.setConfigureOverride(key, value);
|
|
15918
16340
|
}
|
|
15919
16341
|
async reconfigure(projectRoot, overrides) {
|
|
16342
|
+
const pool = await this.ensureActivePool();
|
|
16343
|
+
const runtimeOverrides = {};
|
|
15920
16344
|
for (const [key, value] of Object.entries(overrides)) {
|
|
16345
|
+
if (key === "edit_slot_survives") {
|
|
16346
|
+
this.setConfigureOverride(key, value);
|
|
16347
|
+
continue;
|
|
16348
|
+
}
|
|
15921
16349
|
if (value === undefined)
|
|
15922
16350
|
this.configureOverrides.delete(key);
|
|
15923
16351
|
else
|
|
15924
16352
|
this.configureOverrides.set(key, value);
|
|
16353
|
+
runtimeOverrides[key] = value;
|
|
15925
16354
|
}
|
|
15926
|
-
|
|
15927
|
-
await pool.reconfigure(projectRoot, overrides);
|
|
16355
|
+
await pool.reconfigure(projectRoot, runtimeOverrides);
|
|
15928
16356
|
}
|
|
15929
16357
|
async replaceBinary(path2) {
|
|
15930
16358
|
const replaced = await this.activePool.replaceBinary(path2);
|
|
@@ -16336,6 +16764,9 @@ var UNKNOWN_COMPLETION_TTL_MS = 5000;
|
|
|
16336
16764
|
var UNKNOWN_COMPLETION_CAP = 32;
|
|
16337
16765
|
var DEFAULT_SESSION_ID2 = "__default__";
|
|
16338
16766
|
var LOG_PREFIX = "[aft-plugin] bg-notifications:";
|
|
16767
|
+
var SUBC_NUDGE_LOG_INTERVAL_MS = 60000;
|
|
16768
|
+
var subcNudgesInFlight = new Map;
|
|
16769
|
+
var subcNudgeLogState = new Map;
|
|
16339
16770
|
function consumeBgCompletion(sessionID, taskId) {
|
|
16340
16771
|
const state = stateFor(sessionID);
|
|
16341
16772
|
state.pendingCompletions = state.pendingCompletions.filter((c) => c.task_id !== taskId);
|
|
@@ -16542,11 +16973,45 @@ async function handleIdleBgCompletions(drainContext) {
|
|
|
16542
16973
|
stateFor(drainContext.sessionID).wakeDeferredTaskIds.clear();
|
|
16543
16974
|
await triggerWakeIfPending(drainContext, false, true);
|
|
16544
16975
|
}
|
|
16545
|
-
|
|
16976
|
+
function handleSubcBgEventsNudge(drainContext) {
|
|
16977
|
+
const key = `${drainContext.directory}\x00${drainContext.sessionID}`;
|
|
16978
|
+
const inFlight = subcNudgesInFlight.get(key);
|
|
16979
|
+
if (inFlight) {
|
|
16980
|
+
logSubcNudgeLifecycle(drainContext, "coalesced-in-flight", "nudge coalesced cause=handler-already-in-flight");
|
|
16981
|
+
return inFlight;
|
|
16982
|
+
}
|
|
16983
|
+
logSubcNudgeLifecycle(drainContext, "handler-entry", "nudge handler entered");
|
|
16984
|
+
const handling = handleSubcBgEventsNudgeOnce(drainContext);
|
|
16985
|
+
subcNudgesInFlight.set(key, handling);
|
|
16986
|
+
const clear = () => {
|
|
16987
|
+
if (subcNudgesInFlight.get(key) === handling)
|
|
16988
|
+
subcNudgesInFlight.delete(key);
|
|
16989
|
+
};
|
|
16990
|
+
handling.then(clear, clear);
|
|
16991
|
+
return handling;
|
|
16992
|
+
}
|
|
16993
|
+
async function handleSubcBgEventsNudgeOnce(drainContext) {
|
|
16546
16994
|
bridgeForDrain(drainContext);
|
|
16547
16995
|
stateFor(drainContext.sessionID).wakeDeferredTaskIds.clear();
|
|
16548
16996
|
await triggerWakeIfPending(drainContext, false, true, true);
|
|
16549
16997
|
}
|
|
16998
|
+
function logSubcNudgeLifecycle(drainContext, kind, message) {
|
|
16999
|
+
const key = `${kind}\x00${drainContext.directory}\x00${drainContext.sessionID}`;
|
|
17000
|
+
const now = Date.now();
|
|
17001
|
+
const state = subcNudgeLogState.get(key);
|
|
17002
|
+
if (state && now - state.lastEmittedAt < SUBC_NUDGE_LOG_INTERVAL_MS) {
|
|
17003
|
+
state.suppressed += 1;
|
|
17004
|
+
return;
|
|
17005
|
+
}
|
|
17006
|
+
const suppressed = state?.suppressed ?? 0;
|
|
17007
|
+
subcNudgeLogState.set(key, { lastEmittedAt: now, suppressed: 0 });
|
|
17008
|
+
sessionLog(drainContext.sessionID, `${LOG_PREFIX} ${message}`, {
|
|
17009
|
+
event: "subc_bg_nudge_delivery",
|
|
17010
|
+
cause: kind,
|
|
17011
|
+
canonical_root: drainContext.directory,
|
|
17012
|
+
suppressed
|
|
17013
|
+
});
|
|
17014
|
+
}
|
|
16550
17015
|
async function triggerWakeIfPending(drainContext, skipDrain, includeDeferredCompletions = true, forceDrain = false) {
|
|
16551
17016
|
const state = stateFor(drainContext.sessionID);
|
|
16552
17017
|
if (!skipDrain && (forceDrain || state.outstandingTaskIds.size > 0 || !state.forcedDrainCompleted)) {
|
|
@@ -17412,6 +17877,7 @@ async function signalBashWaitDetachForProject(pool, projectRoot, sessionID) {
|
|
|
17412
17877
|
|
|
17413
17878
|
// src/config.ts
|
|
17414
17879
|
import { existsSync as existsSync10, readFileSync as readFileSync7, renameSync as renameSync5, unlinkSync as unlinkSync5, writeFileSync as writeFileSync4 } from "node:fs";
|
|
17880
|
+
import { parse as parsePath, resolve as resolvePath } from "node:path";
|
|
17415
17881
|
var import_comment_json = __toESM(require_src2(), 1);
|
|
17416
17882
|
|
|
17417
17883
|
// ../../node_modules/.bun/zod@4.4.3/node_modules/zod/v4/classic/external.js
|
|
@@ -17524,7 +17990,7 @@ __export(exports_external, {
|
|
|
17524
17990
|
instanceof: () => _instanceof,
|
|
17525
17991
|
includes: () => _includes,
|
|
17526
17992
|
httpUrl: () => httpUrl,
|
|
17527
|
-
hostname: () =>
|
|
17993
|
+
hostname: () => hostname3,
|
|
17528
17994
|
hex: () => hex2,
|
|
17529
17995
|
hash: () => hash,
|
|
17530
17996
|
guid: () => guid2,
|
|
@@ -18975,7 +19441,7 @@ __export(exports_regexes, {
|
|
|
18975
19441
|
idnEmail: () => idnEmail,
|
|
18976
19442
|
httpProtocol: () => httpProtocol,
|
|
18977
19443
|
html5Email: () => html5Email,
|
|
18978
|
-
hostname: () =>
|
|
19444
|
+
hostname: () => hostname2,
|
|
18979
19445
|
hex: () => hex,
|
|
18980
19446
|
guid: () => guid,
|
|
18981
19447
|
extendedDuration: () => extendedDuration,
|
|
@@ -19033,7 +19499,7 @@ var cidrv4 = /^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(25[0-5]
|
|
|
19033
19499
|
var cidrv6 = /^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|::|([0-9a-fA-F]{1,4})?::([0-9a-fA-F]{1,4}:?){0,6})\/(12[0-8]|1[01][0-9]|[1-9]?[0-9])$/;
|
|
19034
19500
|
var base64 = /^$|^(?:[0-9a-zA-Z+/]{4})*(?:(?:[0-9a-zA-Z+/]{2}==)|(?:[0-9a-zA-Z+/]{3}=))?$/;
|
|
19035
19501
|
var base64url = /^[A-Za-z0-9_-]*$/;
|
|
19036
|
-
var
|
|
19502
|
+
var hostname2 = /^(?=.{1,253}\.?$)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[-0-9a-zA-Z]{0,61}[0-9a-zA-Z])?)*\.?$/;
|
|
19037
19503
|
var domain = /^([a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$/;
|
|
19038
19504
|
var httpProtocol = /^https?$/;
|
|
19039
19505
|
var e164 = /^\+[1-9]\d{6,14}$/;
|
|
@@ -29657,7 +30123,7 @@ __export(exports_schemas2, {
|
|
|
29657
30123
|
int: () => int,
|
|
29658
30124
|
instanceof: () => _instanceof,
|
|
29659
30125
|
httpUrl: () => httpUrl,
|
|
29660
|
-
hostname: () =>
|
|
30126
|
+
hostname: () => hostname3,
|
|
29661
30127
|
hex: () => hex2,
|
|
29662
30128
|
hash: () => hash,
|
|
29663
30129
|
guid: () => guid2,
|
|
@@ -30309,7 +30775,7 @@ var ZodCustomStringFormat = /* @__PURE__ */ $constructor("ZodCustomStringFormat"
|
|
|
30309
30775
|
function stringFormat(format, fnOrRegex, _params = {}) {
|
|
30310
30776
|
return _stringFormat(ZodCustomStringFormat, format, fnOrRegex, _params);
|
|
30311
30777
|
}
|
|
30312
|
-
function
|
|
30778
|
+
function hostname3(_params) {
|
|
30313
30779
|
return _stringFormat(ZodCustomStringFormat, "hostname", exports_regexes.hostname, _params);
|
|
30314
30780
|
}
|
|
30315
30781
|
function hex2(_params) {
|
|
@@ -31798,6 +32264,7 @@ var BackupConfigSchema = exports_external.object({
|
|
|
31798
32264
|
var AftConfigSchema = exports_external.preprocess((value) => stripHarnessSpecificConfigKeys(value, PI_ONLY_KEYS), exports_external.object({
|
|
31799
32265
|
$schema: exports_external.string().optional(),
|
|
31800
32266
|
enabled: exports_external.boolean().optional(),
|
|
32267
|
+
edit_mode: exports_external.enum(["default", "hashline"]).optional(),
|
|
31801
32268
|
format_on_edit: exports_external.boolean().optional(),
|
|
31802
32269
|
formatter_timeout_secs: exports_external.number().int().min(1).max(600).optional(),
|
|
31803
32270
|
validate_on_edit: exports_external.enum(["syntax", "full"]).optional(),
|
|
@@ -32035,14 +32502,19 @@ ${serialized}` : serialized;
|
|
|
32035
32502
|
}
|
|
32036
32503
|
}
|
|
32037
32504
|
function parseConfigPartially(rawConfig) {
|
|
32038
|
-
|
|
32505
|
+
let configForParsing = rawConfig;
|
|
32506
|
+
if (Object.hasOwn(rawConfig, "edit_mode") && rawConfig.edit_mode !== "default" && rawConfig.edit_mode !== "hashline") {
|
|
32507
|
+
warn2(`Unknown edit_mode value ${JSON.stringify(rawConfig.edit_mode)}; falling back to "default"`);
|
|
32508
|
+
configForParsing = { ...rawConfig, edit_mode: "default" };
|
|
32509
|
+
}
|
|
32510
|
+
const fullResult = AftConfigSchema.safeParse(configForParsing);
|
|
32039
32511
|
if (fullResult.success) {
|
|
32040
32512
|
return fullResult.data;
|
|
32041
32513
|
}
|
|
32042
32514
|
const partialConfig = {};
|
|
32043
32515
|
const invalidSections = [];
|
|
32044
|
-
for (const key of Object.keys(
|
|
32045
|
-
const sectionResult = AftConfigSchema.safeParse({ [key]:
|
|
32516
|
+
for (const key of Object.keys(configForParsing)) {
|
|
32517
|
+
const sectionResult = AftConfigSchema.safeParse({ [key]: configForParsing[key] });
|
|
32046
32518
|
if (sectionResult.success) {
|
|
32047
32519
|
const parsed = sectionResult.data;
|
|
32048
32520
|
if (parsed[key] !== undefined) {
|
|
@@ -32211,6 +32683,7 @@ function getProjectLspStrippedKeys(lsp) {
|
|
|
32211
32683
|
}
|
|
32212
32684
|
var PROJECT_SAFE_TOP_LEVEL_FIELDS = new Set([
|
|
32213
32685
|
"enabled",
|
|
32686
|
+
"edit_mode",
|
|
32214
32687
|
"tool_surface",
|
|
32215
32688
|
"hoist_builtin_tools",
|
|
32216
32689
|
"format_on_edit",
|
|
@@ -32326,6 +32799,12 @@ function resolveAftConfigPaths(projectDirectory) {
|
|
|
32326
32799
|
migrateAftConfigFile2(paths.projectConfigPath);
|
|
32327
32800
|
return paths;
|
|
32328
32801
|
}
|
|
32802
|
+
function resolveOpenCodeRegistrationRoot(directory, worktree) {
|
|
32803
|
+
if (!worktree)
|
|
32804
|
+
return directory;
|
|
32805
|
+
const resolvedWorktree = resolvePath(worktree);
|
|
32806
|
+
return parsePath(resolvedWorktree).root === resolvedWorktree ? directory : resolvedWorktree;
|
|
32807
|
+
}
|
|
32329
32808
|
function buildConfigTierConfigureParams(projectDirectory, processState = {}) {
|
|
32330
32809
|
const paths = resolveAftConfigPaths(projectDirectory);
|
|
32331
32810
|
const initialPluginState = Object.keys(processState).length > 0;
|
|
@@ -33784,7 +34263,7 @@ ${new Date().toISOString()}
|
|
|
33784
34263
|
const age = Date.now() - lockMtimeMs;
|
|
33785
34264
|
const ageWithinFresh = Math.abs(age) < STALE_LOCK_MS2;
|
|
33786
34265
|
const skipLiveness = process.platform === "win32";
|
|
33787
|
-
const ownerAlive = !skipLiveness && owningPid !== null &&
|
|
34266
|
+
const ownerAlive = !skipLiveness && owningPid !== null && isProcessAlive3(owningPid);
|
|
33788
34267
|
if (skipLiveness ? ageWithinFresh : ownerAlive && ageWithinFresh) {
|
|
33789
34268
|
return false;
|
|
33790
34269
|
}
|
|
@@ -33794,7 +34273,7 @@ ${new Date().toISOString()}
|
|
|
33794
34273
|
} catch {}
|
|
33795
34274
|
return tryClaim();
|
|
33796
34275
|
}
|
|
33797
|
-
function
|
|
34276
|
+
function isProcessAlive3(pid) {
|
|
33798
34277
|
try {
|
|
33799
34278
|
process.kill(pid, 0);
|
|
33800
34279
|
return true;
|
|
@@ -34028,7 +34507,7 @@ var NPM_LSP_TABLE = [
|
|
|
34028
34507
|
];
|
|
34029
34508
|
|
|
34030
34509
|
// src/lsp-project-relevance.ts
|
|
34031
|
-
import { existsSync as existsSync15, readdirSync as
|
|
34510
|
+
import { existsSync as existsSync15, readdirSync as readdirSync4, readFileSync as readFileSync13 } from "node:fs";
|
|
34032
34511
|
import { join as join17 } from "node:path";
|
|
34033
34512
|
var MAX_WALK_DIRS = 200;
|
|
34034
34513
|
var MAX_WALK_DEPTH = 4;
|
|
@@ -34093,7 +34572,7 @@ function relevantExtensionsInProject(projectRoot, extToServer) {
|
|
|
34093
34572
|
visitedDirs += 1;
|
|
34094
34573
|
let entries;
|
|
34095
34574
|
try {
|
|
34096
|
-
entries =
|
|
34575
|
+
entries = readdirSync4(current.dir, { withFileTypes: true });
|
|
34097
34576
|
} catch {
|
|
34098
34577
|
continue;
|
|
34099
34578
|
}
|
|
@@ -34514,7 +34993,7 @@ import {
|
|
|
34514
34993
|
existsSync as existsSync17,
|
|
34515
34994
|
lstatSync as lstatSync2,
|
|
34516
34995
|
mkdirSync as mkdirSync9,
|
|
34517
|
-
readdirSync as
|
|
34996
|
+
readdirSync as readdirSync5,
|
|
34518
34997
|
readFileSync as readFileSync15,
|
|
34519
34998
|
readlinkSync as readlinkSync2,
|
|
34520
34999
|
realpathSync as realpathSync3,
|
|
@@ -34917,7 +35396,7 @@ function validateExtraction(stagingRoot) {
|
|
|
34917
35396
|
const walk = (dir) => {
|
|
34918
35397
|
let entries;
|
|
34919
35398
|
try {
|
|
34920
|
-
entries =
|
|
35399
|
+
entries = readdirSync5(dir);
|
|
34921
35400
|
} catch (err) {
|
|
34922
35401
|
throw new Error(`failed to read staging dir ${dir}: ${err}`);
|
|
34923
35402
|
}
|
|
@@ -35359,14 +35838,18 @@ function normalizeToolArgSchemas(toolDefinition) {
|
|
|
35359
35838
|
function bareToolName2(toolName) {
|
|
35360
35839
|
return toolName.startsWith("aft_") ? toolName.slice(4) : toolName;
|
|
35361
35840
|
}
|
|
35362
|
-
function prepareOpenCodeArguments(toolName, rawArguments) {
|
|
35841
|
+
function prepareOpenCodeArguments(toolName, rawArguments, preparation = {}) {
|
|
35363
35842
|
const bare = bareToolName2(toolName);
|
|
35843
|
+
if (bare === "edit" && preparation.hashlineEffective === true) {
|
|
35844
|
+
return rawArguments;
|
|
35845
|
+
}
|
|
35364
35846
|
if (bare === "edit") {
|
|
35365
35847
|
return prepareCanonicalEditArguments(toolName, rawArguments);
|
|
35366
35848
|
}
|
|
35367
35849
|
return prepareCanonicalPathArguments(toolName, rawArguments);
|
|
35368
35850
|
}
|
|
35369
35851
|
var DISPLAY_FILE_PATH_TOOLS = new Set(["read", "write", "edit"]);
|
|
35852
|
+
var PREPARED_EXECUTORS = new WeakSet;
|
|
35370
35853
|
function preserveDisplayFilePathAlias(toolName, rawArguments, prepared) {
|
|
35371
35854
|
if (!DISPLAY_FILE_PATH_TOOLS.has(toolName))
|
|
35372
35855
|
return;
|
|
@@ -35377,21 +35860,25 @@ function preserveDisplayFilePathAlias(toolName, rawArguments, prepared) {
|
|
|
35377
35860
|
raw.filePath = prepared.path;
|
|
35378
35861
|
}
|
|
35379
35862
|
}
|
|
35380
|
-
function prepareToolMap(tools) {
|
|
35863
|
+
function prepareToolMap(tools, preparation = {}) {
|
|
35381
35864
|
for (const [toolName, def] of Object.entries(tools)) {
|
|
35865
|
+
if (PREPARED_EXECUTORS.has(def.execute))
|
|
35866
|
+
continue;
|
|
35382
35867
|
const execute = def.execute;
|
|
35383
|
-
|
|
35384
|
-
const prepared = prepareOpenCodeArguments(toolName, args);
|
|
35868
|
+
const preparedExecute = async (args, context) => {
|
|
35869
|
+
const prepared = prepareOpenCodeArguments(toolName, args, preparation);
|
|
35385
35870
|
preserveDisplayFilePathAlias(toolName, args, prepared);
|
|
35386
35871
|
return execute(prepared, context);
|
|
35387
35872
|
};
|
|
35873
|
+
PREPARED_EXECUTORS.add(preparedExecute);
|
|
35874
|
+
def.execute = preparedExecute;
|
|
35388
35875
|
}
|
|
35389
35876
|
return tools;
|
|
35390
35877
|
}
|
|
35391
|
-
function normalizeToolMap(tools) {
|
|
35878
|
+
function normalizeToolMap(tools, preparation = {}) {
|
|
35392
35879
|
for (const def of Object.values(tools))
|
|
35393
35880
|
normalizeToolArgSchemas(def);
|
|
35394
|
-
return prepareToolMap(tools);
|
|
35881
|
+
return prepareToolMap(tools, preparation);
|
|
35395
35882
|
}
|
|
35396
35883
|
// src/shared/ignored-message.ts
|
|
35397
35884
|
async function sendIgnoredMessage2(client, sessionID, text) {
|
|
@@ -35522,7 +36009,7 @@ import { randomBytes as randomBytes3, timingSafeEqual as timingSafeEqual2 } from
|
|
|
35522
36009
|
import {
|
|
35523
36010
|
existsSync as existsSync18,
|
|
35524
36011
|
mkdirSync as mkdirSync10,
|
|
35525
|
-
readdirSync as
|
|
36012
|
+
readdirSync as readdirSync6,
|
|
35526
36013
|
readFileSync as readFileSync16,
|
|
35527
36014
|
renameSync as renameSync9,
|
|
35528
36015
|
unlinkSync as unlinkSync8,
|
|
@@ -35692,7 +36179,7 @@ class AftRpcServer {
|
|
|
35692
36179
|
sweepDeadPortFiles() {
|
|
35693
36180
|
let entries;
|
|
35694
36181
|
try {
|
|
35695
|
-
entries =
|
|
36182
|
+
entries = readdirSync6(this.portsDir);
|
|
35696
36183
|
} catch {
|
|
35697
36184
|
return;
|
|
35698
36185
|
}
|
|
@@ -37901,7 +38388,56 @@ ${backupBehavior}
|
|
|
37901
38388
|
- Symbol replace includes decorators, attributes, and doc comments in range
|
|
37902
38389
|
- Response is a compact server-rendered summary; before/after diff details are attached as UI metadata when available.`;
|
|
37903
38390
|
}
|
|
38391
|
+
function createHashlineEditTool(ctx) {
|
|
38392
|
+
return {
|
|
38393
|
+
description: "Apply a hashline patch. Arguments are exactly `{patch}` where `patch` is a non-empty string of one or more `[path#TAG]` sections with PUT/CUT/REM/MV operations. Paths and tags come from section headers; obtain tags from tagged reads. Server-owned preview control is outside this schema.",
|
|
38394
|
+
args: {
|
|
38395
|
+
patch: z8.string().min(1).describe("Hashline patch text with one or more [path#TAG] sections and PUT/CUT/REM/MV operations")
|
|
38396
|
+
},
|
|
38397
|
+
execute: async (args, context) => {
|
|
38398
|
+
const patch = args.patch;
|
|
38399
|
+
const projectRoot = await resolveProjectRoot(ctx, context);
|
|
38400
|
+
const rawArgs = { patch };
|
|
38401
|
+
const preflight = await callToolCall(ctx, context, "hashline_preflight", rawArgs);
|
|
38402
|
+
if (preflight.success === false)
|
|
38403
|
+
throw toolErrorFromResponse("edit", preflight);
|
|
38404
|
+
const permissionPatterns = [
|
|
38405
|
+
...coerceStringArray(preflight.affected_paths),
|
|
38406
|
+
...coerceStringArray(preflight.affected_rel_paths),
|
|
38407
|
+
...coerceStringArray(preflight.mv_destinations)
|
|
38408
|
+
].filter((value, index, all) => all.indexOf(value) === index);
|
|
38409
|
+
for (const target of [
|
|
38410
|
+
...coerceStringArray(preflight.affected_paths),
|
|
38411
|
+
...coerceStringArray(preflight.mv_destinations)
|
|
38412
|
+
]) {
|
|
38413
|
+
const absolute = resolvePathFromProjectRoot(projectRoot, target);
|
|
38414
|
+
const denial2 = await assertExternalDirectoryPermission(ctx, context, absolute);
|
|
38415
|
+
if (denial2)
|
|
38416
|
+
return permissionDeniedResponse(denial2);
|
|
38417
|
+
}
|
|
38418
|
+
const denial = await askEditPermission(context, permissionPatterns, {
|
|
38419
|
+
surface: "hashline"
|
|
38420
|
+
});
|
|
38421
|
+
if (denial)
|
|
38422
|
+
return permissionDeniedResponse(denial);
|
|
38423
|
+
const preview2 = await callToolCall(ctx, context, "edit", rawArgs, { preview: true });
|
|
38424
|
+
if (preview2.success === false)
|
|
38425
|
+
throw toolErrorFromResponse("edit", preview2);
|
|
38426
|
+
const data = await callToolCall(ctx, context, "edit", rawArgs);
|
|
38427
|
+
if (data.success === false)
|
|
38428
|
+
throw toolErrorFromResponse("edit", data);
|
|
38429
|
+
const firstPath = typeof data.filePath === "string" ? resolvePathFromProjectRoot(projectRoot, data.filePath) : "";
|
|
38430
|
+
return {
|
|
38431
|
+
output: data.text,
|
|
38432
|
+
title: firstPath ? relativeToWorktree(firstPath, projectRoot) : "edit",
|
|
38433
|
+
metadata: data.metadata && typeof data.metadata === "object" ? data.metadata : {}
|
|
38434
|
+
};
|
|
38435
|
+
}
|
|
38436
|
+
};
|
|
38437
|
+
}
|
|
37904
38438
|
function createEditTool(ctx, writeToolName = "write") {
|
|
38439
|
+
if (ctx.hashlineEffective === true)
|
|
38440
|
+
return createHashlineEditTool(ctx);
|
|
37905
38441
|
return {
|
|
37906
38442
|
description: getEditDescription(ctx, writeToolName),
|
|
37907
38443
|
args: {
|
|
@@ -38209,7 +38745,7 @@ function hoistedTools(ctx) {
|
|
|
38209
38745
|
tools.bash_kill = createBashKillTool(ctx);
|
|
38210
38746
|
}
|
|
38211
38747
|
}
|
|
38212
|
-
return prepareToolMap(tools);
|
|
38748
|
+
return prepareToolMap(tools, { hashlineEffective: ctx.hashlineEffective });
|
|
38213
38749
|
}
|
|
38214
38750
|
function aftPrefixedTools(ctx) {
|
|
38215
38751
|
const aftEditTool = createEditTool(ctx, "aft_write");
|
|
@@ -38231,7 +38767,7 @@ function aftPrefixedTools(ctx) {
|
|
|
38231
38767
|
tools.bash_kill = createBashKillTool(ctx);
|
|
38232
38768
|
}
|
|
38233
38769
|
}
|
|
38234
|
-
return prepareToolMap(tools);
|
|
38770
|
+
return prepareToolMap(tools, { hashlineEffective: ctx.hashlineEffective });
|
|
38235
38771
|
}
|
|
38236
38772
|
|
|
38237
38773
|
// src/tools/imports.ts
|
|
@@ -39240,6 +39776,15 @@ function semanticTools(ctx) {
|
|
|
39240
39776
|
|
|
39241
39777
|
// src/tool-registration.ts
|
|
39242
39778
|
var ALL_ONLY_TOOLS = ["aft_callgraph", "aft_delete", "aft_move", "aft_refactor"];
|
|
39779
|
+
function openCodeEditSlotSurvives(config2) {
|
|
39780
|
+
return (config2.tool_surface ?? "recommended") !== "minimal" && config2.hoist_builtin_tools !== false && !(config2.disabled_tools ?? []).includes("edit");
|
|
39781
|
+
}
|
|
39782
|
+
function openCodeHashlineEffective(config2) {
|
|
39783
|
+
return config2.edit_mode === "hashline" && openCodeEditSlotSurvives(config2);
|
|
39784
|
+
}
|
|
39785
|
+
function openCodeHashlineEditRegistered(config2, registeredTools) {
|
|
39786
|
+
return openCodeHashlineEffective(config2) && registeredTools.has("edit");
|
|
39787
|
+
}
|
|
39243
39788
|
function buildOpenCodeToolMap(ctx, config2, onUnknownDisabled) {
|
|
39244
39789
|
const surface = config2.tool_surface ?? "recommended";
|
|
39245
39790
|
const allTools = normalizeToolMap({
|
|
@@ -39254,7 +39799,7 @@ function buildOpenCodeToolMap(ctx, config2, onUnknownDisabled) {
|
|
|
39254
39799
|
...surface !== "minimal" && config2.search_index === true && searchTools(ctx),
|
|
39255
39800
|
...refactoringTools(ctx),
|
|
39256
39801
|
...surface !== "minimal" && conflictTools(ctx)
|
|
39257
|
-
});
|
|
39802
|
+
}, { hashlineEffective: ctx.hashlineEffective });
|
|
39258
39803
|
if (surface !== "all") {
|
|
39259
39804
|
for (const name of ALL_ONLY_TOOLS)
|
|
39260
39805
|
delete allTools[name];
|
|
@@ -39395,7 +39940,7 @@ var PLUGIN_VERSION = (() => {
|
|
|
39395
39940
|
return "0.0.0";
|
|
39396
39941
|
}
|
|
39397
39942
|
})();
|
|
39398
|
-
var ANNOUNCEMENT_VERSION = "0.
|
|
39943
|
+
var ANNOUNCEMENT_VERSION = "0.50.1";
|
|
39399
39944
|
var ANNOUNCEMENT_FEATURES = [
|
|
39400
39945
|
"Callgraph stays consistent under load: edits arriving mid-rebuild can no longer be silently lost from navigation results.",
|
|
39401
39946
|
"aft_zoom handles real-world .jsonc: files with comment banners above the opening brace now resolve, and not-found errors point at the segment that actually failed (thanks @iceteaSA).",
|
|
@@ -39411,22 +39956,23 @@ async function initializePluginForDirectory(input) {
|
|
|
39411
39956
|
});
|
|
39412
39957
|
}
|
|
39413
39958
|
};
|
|
39414
|
-
|
|
39959
|
+
const registrationRoot = resolveOpenCodeRegistrationRoot(input.directory, input.worktree);
|
|
39960
|
+
let aftConfig = loadAftConfig(registrationRoot);
|
|
39415
39961
|
if (aftConfig.enabled === false) {
|
|
39416
|
-
log2(`AFT disabled by config for ${
|
|
39962
|
+
log2(`AFT disabled by config for ${registrationRoot}`);
|
|
39417
39963
|
return { tool: {} };
|
|
39418
39964
|
}
|
|
39419
|
-
deliverConfigMigrationWarnings(
|
|
39420
|
-
aftConfig = loadAftConfig(
|
|
39421
|
-
enqueueConfigParseWarnings(
|
|
39965
|
+
deliverConfigMigrationWarnings(registrationRoot, migrateAftConfigLocations(registrationRoot, bridgeLogger).flatMap((result) => result.warnings));
|
|
39966
|
+
aftConfig = loadAftConfig(registrationRoot);
|
|
39967
|
+
enqueueConfigParseWarnings(registrationRoot, getConfigLoadErrors());
|
|
39422
39968
|
if (aftConfig.enabled === false) {
|
|
39423
|
-
log2(`AFT disabled by config for ${
|
|
39969
|
+
log2(`AFT disabled by config for ${registrationRoot}`);
|
|
39424
39970
|
return { tool: {} };
|
|
39425
39971
|
}
|
|
39426
39972
|
const binaryPath = await findBinary(PLUGIN_VERSION);
|
|
39427
39973
|
await ensureStorageMigrated({ harness: "opencode", binaryPath, logger: bridgeLogger });
|
|
39428
39974
|
const autoUpdateAbort = new AbortController;
|
|
39429
|
-
const projectEnabledCache = new Map([[
|
|
39975
|
+
const projectEnabledCache = new Map([[registrationRoot, true]]);
|
|
39430
39976
|
const loggedDisabledProjects = new Set;
|
|
39431
39977
|
const isProjectEnabled = (projectRoot) => {
|
|
39432
39978
|
const cached2 = projectEnabledCache.get(projectRoot);
|
|
@@ -39442,10 +39988,11 @@ async function initializePluginForDirectory(input) {
|
|
|
39442
39988
|
return enabled;
|
|
39443
39989
|
};
|
|
39444
39990
|
const storageDir = resolveCortexKitStorageRoot();
|
|
39445
|
-
const configOverrides = buildConfigTierConfigureParams(
|
|
39991
|
+
const configOverrides = buildConfigTierConfigureParams(registrationRoot, {
|
|
39446
39992
|
bash_permissions: true,
|
|
39447
39993
|
storage_dir: storageDir
|
|
39448
39994
|
});
|
|
39995
|
+
const hashlineEffective = openCodeHashlineEffective(aftConfig);
|
|
39449
39996
|
let lspInstallCompletion = null;
|
|
39450
39997
|
const isFastembedSemanticBackend = (aftConfig.semantic?.backend ?? "fastembed") === "fastembed";
|
|
39451
39998
|
let onnxRuntimePromise = null;
|
|
@@ -39635,6 +40182,16 @@ ${lines}
|
|
|
39635
40182
|
poolOptions,
|
|
39636
40183
|
configOverrides,
|
|
39637
40184
|
subcConnectionFile: aftConfig.subc?.connection_file,
|
|
40185
|
+
onBgEventsNudge: (directory, sessionID) => {
|
|
40186
|
+
handleSubcBgEventsNudge({
|
|
40187
|
+
ctx,
|
|
40188
|
+
directory,
|
|
40189
|
+
sessionID,
|
|
40190
|
+
client: input.client
|
|
40191
|
+
}).catch((err) => {
|
|
40192
|
+
warn2(`[aft-plugin] bg nudge rejected: ${err instanceof Error ? err.message : String(err)}`);
|
|
40193
|
+
});
|
|
40194
|
+
},
|
|
39638
40195
|
onBgEventsNudgeRef: (ref) => {
|
|
39639
40196
|
handleSubcBgEventsNudge({
|
|
39640
40197
|
ctx,
|
|
@@ -39664,6 +40221,7 @@ ${lines}
|
|
|
39664
40221
|
client: input.client,
|
|
39665
40222
|
plugin: input.plugin,
|
|
39666
40223
|
config: aftConfig,
|
|
40224
|
+
hashlineEffective,
|
|
39667
40225
|
storageDir: configOverrides.storage_dir,
|
|
39668
40226
|
isProjectEnabled
|
|
39669
40227
|
};
|
|
@@ -39865,6 +40423,10 @@ Install: ${getManualInstallHint()}`).catch(() => {});
|
|
|
39865
40423
|
"bash_status"
|
|
39866
40424
|
];
|
|
39867
40425
|
const registeredTools = new Set(Object.keys(allTools));
|
|
40426
|
+
const hashlineEditRegistered = openCodeHashlineEditRegistered(aftConfig, registeredTools);
|
|
40427
|
+
ctx.hashlineEffective = hashlineEditRegistered;
|
|
40428
|
+
pool.setConfigureOverride("edit_slot_survives", hashlineEditRegistered);
|
|
40429
|
+
log2(`hashline activation decision requested=${aftConfig.edit_mode === "hashline"} ` + `edit_slot_survives=${hashlineEditRegistered} effective=${ctx.hashlineEffective}`);
|
|
39868
40430
|
const aftSearchRegistered = registeredTools.has("aft_search");
|
|
39869
40431
|
pool.setConfigureOverride("aft_search_registered", aftSearchRegistered);
|
|
39870
40432
|
ctx.aftSearchRegistered = aftSearchRegistered;
|
|
@@ -39959,7 +40521,9 @@ Install: ${getManualInstallHint()}`).catch(() => {});
|
|
|
39959
40521
|
signalBashWaitDetachForProject(pool, sessionDir, sid);
|
|
39960
40522
|
},
|
|
39961
40523
|
"tool.execute.before": async (toolInput, output) => {
|
|
39962
|
-
output.args = prepareOpenCodeArguments(toolInput.tool, output.args
|
|
40524
|
+
output.args = prepareOpenCodeArguments(toolInput.tool, output.args, {
|
|
40525
|
+
hashlineEffective: ctx.hashlineEffective
|
|
40526
|
+
});
|
|
39963
40527
|
if (toolInput.sessionID)
|
|
39964
40528
|
inspectTier2Idle.clear(toolInput.sessionID);
|
|
39965
40529
|
},
|