@cortexkit/aft-pi 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/README.md +2 -2
- package/dist/bg-notifications.d.ts +4 -2
- 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 +639 -99
- 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
|
@@ -8410,6 +8410,8 @@ var DEFAULT_BRIDGE_TIMEOUT_MS = 30000;
|
|
|
8410
8410
|
var BRIDGE_HANG_TIMEOUT_THRESHOLD = 2;
|
|
8411
8411
|
var MAX_STDOUT_BUFFER = 64 * 1024 * 1024;
|
|
8412
8412
|
var STDOUT_BUFFER_COMPACT_THRESHOLD = 64 * 1024;
|
|
8413
|
+
var HASHLINE_REGISTRATION_LOG_INTERVAL_MS = 60000;
|
|
8414
|
+
var HASHLINE_REGISTRATION_LOG_STATE_LIMIT = 256;
|
|
8413
8415
|
var TERMINAL_BASH_STATUSES = new Set([
|
|
8414
8416
|
"completed",
|
|
8415
8417
|
"failed",
|
|
@@ -8559,6 +8561,9 @@ class BinaryBridge {
|
|
|
8559
8561
|
configured = false;
|
|
8560
8562
|
_configurePromise = null;
|
|
8561
8563
|
configOverrides;
|
|
8564
|
+
editSlotSurvives;
|
|
8565
|
+
editSlotSurvivesCaptured = false;
|
|
8566
|
+
hashlineRegistrationLogState = new Map;
|
|
8562
8567
|
minVersion;
|
|
8563
8568
|
onVersionMismatch;
|
|
8564
8569
|
onConfigureWarnings;
|
|
@@ -8575,20 +8580,33 @@ class BinaryBridge {
|
|
|
8575
8580
|
errorPrefix;
|
|
8576
8581
|
logger;
|
|
8577
8582
|
childEnv;
|
|
8578
|
-
constructor(binaryPath, cwd, options, configOverrides) {
|
|
8583
|
+
constructor(binaryPath, cwd, options, configOverrides, editSlotSurvives) {
|
|
8579
8584
|
this.binaryPath = binaryPath;
|
|
8580
8585
|
this.cwd = cwd;
|
|
8581
8586
|
this.timeoutMs = options?.timeoutMs ?? DEFAULT_BRIDGE_TIMEOUT_MS;
|
|
8582
8587
|
this.hangThreshold = options?.hangThreshold ?? BRIDGE_HANG_TIMEOUT_THRESHOLD;
|
|
8583
8588
|
this.maxRestarts = options?.maxRestarts ?? 3;
|
|
8584
|
-
this.
|
|
8589
|
+
this.errorPrefix = options?.errorPrefix ?? "[aft-bridge]";
|
|
8590
|
+
this.configOverrides = { ...configOverrides ?? {} };
|
|
8591
|
+
const legacyEditSlotSurvives = this.configOverrides.edit_slot_survives;
|
|
8592
|
+
delete this.configOverrides.edit_slot_survives;
|
|
8593
|
+
if (legacyEditSlotSurvives !== undefined && typeof legacyEditSlotSurvives !== "boolean") {
|
|
8594
|
+
throw new Error(`${this.errorPrefix} edit_slot_survives must be a boolean`);
|
|
8595
|
+
}
|
|
8596
|
+
if (editSlotSurvives !== undefined && legacyEditSlotSurvives !== undefined && editSlotSurvives !== legacyEditSlotSurvives) {
|
|
8597
|
+
throw new Error(`${this.errorPrefix} conflicting edit_slot_survives construction values`);
|
|
8598
|
+
}
|
|
8599
|
+
const capturedEditSlotSurvives = editSlotSurvives ?? legacyEditSlotSurvives;
|
|
8600
|
+
if (typeof capturedEditSlotSurvives === "boolean") {
|
|
8601
|
+
this.editSlotSurvives = capturedEditSlotSurvives;
|
|
8602
|
+
this.editSlotSurvivesCaptured = true;
|
|
8603
|
+
}
|
|
8585
8604
|
this.minVersion = options?.minVersion;
|
|
8586
8605
|
this.onVersionMismatch = options?.onVersionMismatch;
|
|
8587
8606
|
this.onConfigureWarnings = options?.onConfigureWarnings;
|
|
8588
8607
|
this.onBashCompletion = options?.onBashCompletion;
|
|
8589
8608
|
this.onBashLongRunning = options?.onBashLongRunning;
|
|
8590
8609
|
this.onBashPatternMatch = options?.onBashPatternMatch;
|
|
8591
|
-
this.errorPrefix = options?.errorPrefix ?? "[aft-bridge]";
|
|
8592
8610
|
this.logger = options?.logger;
|
|
8593
8611
|
this.childEnv = options?.childEnv;
|
|
8594
8612
|
}
|
|
@@ -8699,13 +8717,54 @@ class BinaryBridge {
|
|
|
8699
8717
|
cacheStatusSnapshot(snapshot) {
|
|
8700
8718
|
this.cachedStatus = snapshot;
|
|
8701
8719
|
}
|
|
8720
|
+
setEditSlotSurvives(value) {
|
|
8721
|
+
if (this.editSlotSurvivesCaptured) {
|
|
8722
|
+
throw new Error(`${this.errorPrefix} edit_slot_survives is write-once and was already captured`);
|
|
8723
|
+
}
|
|
8724
|
+
this.editSlotSurvives = value;
|
|
8725
|
+
this.editSlotSurvivesCaptured = true;
|
|
8726
|
+
}
|
|
8727
|
+
logHashlineRegistrationCarrier(phase, sessionId, editSlotSurvives) {
|
|
8728
|
+
const session = sessionId && sessionId.length > 0 ? sessionId : "__default__";
|
|
8729
|
+
const key = `${phase}\x00${session}\x00${String(editSlotSurvives)}`;
|
|
8730
|
+
const now = Date.now();
|
|
8731
|
+
const state = this.hashlineRegistrationLogState.get(key);
|
|
8732
|
+
if (state && now - state.lastEmittedAt < HASHLINE_REGISTRATION_LOG_INTERVAL_MS) {
|
|
8733
|
+
state.suppressed += 1;
|
|
8734
|
+
return;
|
|
8735
|
+
}
|
|
8736
|
+
const repeated = state && state.suppressed > 0 ? ` repeated=${state.suppressed + 1}` : "";
|
|
8737
|
+
if (!state && this.hashlineRegistrationLogState.size >= HASHLINE_REGISTRATION_LOG_STATE_LIMIT) {
|
|
8738
|
+
const oldest = this.hashlineRegistrationLogState.keys().next().value;
|
|
8739
|
+
if (oldest !== undefined)
|
|
8740
|
+
this.hashlineRegistrationLogState.delete(oldest);
|
|
8741
|
+
}
|
|
8742
|
+
this.hashlineRegistrationLogState.set(key, { lastEmittedAt: now, suppressed: 0 });
|
|
8743
|
+
this.sessionLogVia(sessionId, `hashline registration carrier transport=ndjson phase=${phase} edit_slot_survives=${editSlotSurvives}${repeated}`);
|
|
8744
|
+
}
|
|
8702
8745
|
async send(command, params = {}, options) {
|
|
8703
|
-
|
|
8746
|
+
let dispatchParams = params;
|
|
8747
|
+
if (command === "configure") {
|
|
8748
|
+
dispatchParams = { ...params };
|
|
8749
|
+
delete dispatchParams.edit_slot_survives;
|
|
8750
|
+
const editSlotSurvives = this.editSlotSurvives;
|
|
8751
|
+
if (this.editSlotSurvivesCaptured && typeof editSlotSurvives === "boolean") {
|
|
8752
|
+
dispatchParams.edit_slot_survives = editSlotSurvives;
|
|
8753
|
+
const sessionId = typeof dispatchParams.session_id === "string" ? dispatchParams.session_id : undefined;
|
|
8754
|
+
this.logHashlineRegistrationCarrier("configure", sessionId, editSlotSurvives);
|
|
8755
|
+
}
|
|
8756
|
+
}
|
|
8757
|
+
return this.sendWithVersionMismatchRetry(command, dispatchParams, options, true);
|
|
8704
8758
|
}
|
|
8705
8759
|
async toolCall(sessionId, name, rawArgs = {}, options) {
|
|
8706
8760
|
const params = { name, arguments: rawArgs };
|
|
8707
8761
|
if (sessionId)
|
|
8708
8762
|
params.session_id = sessionId;
|
|
8763
|
+
const editSlotSurvives = this.editSlotSurvives;
|
|
8764
|
+
if (this.editSlotSurvivesCaptured && typeof editSlotSurvives === "boolean") {
|
|
8765
|
+
params.edit_slot_survives = editSlotSurvives;
|
|
8766
|
+
this.logHashlineRegistrationCarrier("tool_call", sessionId, editSlotSurvives);
|
|
8767
|
+
}
|
|
8709
8768
|
const { preview, ...sendOptions } = options ?? {};
|
|
8710
8769
|
if (preview === true)
|
|
8711
8770
|
params.preview = true;
|
|
@@ -9781,7 +9840,8 @@ function formatDroppedKeyWarnings(dropped) {
|
|
|
9781
9840
|
// ../aft-bridge/dist/downloader.js
|
|
9782
9841
|
import { spawnSync } from "node:child_process";
|
|
9783
9842
|
import { createHash as createHash2, randomUUID } from "node:crypto";
|
|
9784
|
-
import { chmodSync, closeSync, copyFileSync, createWriteStream, existsSync as existsSync2, mkdirSync, openSync, readFileSync as readFileSync3, renameSync, rmSync, statSync as statSync2, unlinkSync, writeSync } from "node:fs";
|
|
9843
|
+
import { chmodSync, closeSync, copyFileSync, createWriteStream, existsSync as existsSync2, mkdirSync, openSync, readdirSync, readFileSync as readFileSync3, renameSync, rmSync, statSync as statSync2, unlinkSync, writeSync } from "node:fs";
|
|
9844
|
+
import { hostname } from "node:os";
|
|
9785
9845
|
import { join as join4 } from "node:path";
|
|
9786
9846
|
import { Readable } from "node:stream";
|
|
9787
9847
|
import { pipeline } from "node:stream/promises";
|
|
@@ -9806,8 +9866,8 @@ var REPO = "cortexkit/aft";
|
|
|
9806
9866
|
var DOWNLOAD_TIMEOUT_MS = 300000;
|
|
9807
9867
|
var LATEST_TAG_TIMEOUT_MS = 30000;
|
|
9808
9868
|
var MAX_DOWNLOAD_BYTES = 200 * 1024 * 1024;
|
|
9809
|
-
var DOWNLOAD_LOCK_TIMEOUT_MS = 120000;
|
|
9810
9869
|
var DOWNLOAD_LOCK_STALE_MS = 10 * 60000;
|
|
9870
|
+
var DOWNLOAD_LOCK_TIMEOUT_MS = DOWNLOAD_LOCK_STALE_MS + 30000;
|
|
9811
9871
|
function readBinaryVersion(binaryPath) {
|
|
9812
9872
|
try {
|
|
9813
9873
|
const result = spawnSync(binaryPath, ["--version"], {
|
|
@@ -9875,11 +9935,37 @@ async function downloadBinary(version) {
|
|
|
9875
9935
|
let binaryTimeout = null;
|
|
9876
9936
|
let checksumTimeout = null;
|
|
9877
9937
|
const tmpPath = `${binaryPath}.${process.pid}.${Date.now()}.${Math.random().toString(16).slice(2)}.tmp`;
|
|
9938
|
+
const cleanUpPartialDownload = () => {
|
|
9939
|
+
try {
|
|
9940
|
+
if (existsSync2(tmpPath))
|
|
9941
|
+
unlinkSync(tmpPath);
|
|
9942
|
+
} catch {}
|
|
9943
|
+
};
|
|
9944
|
+
let interrupted = false;
|
|
9945
|
+
const cleanUpInterruptedDownload = () => {
|
|
9946
|
+
if (interrupted)
|
|
9947
|
+
return;
|
|
9948
|
+
interrupted = true;
|
|
9949
|
+
binaryController?.abort();
|
|
9950
|
+
checksumController?.abort();
|
|
9951
|
+
cleanUpPartialDownload();
|
|
9952
|
+
releaseLock?.();
|
|
9953
|
+
releaseLock = null;
|
|
9954
|
+
};
|
|
9955
|
+
const handleSigint = () => {
|
|
9956
|
+
cleanUpInterruptedDownload();
|
|
9957
|
+
process.off("SIGINT", handleSigint);
|
|
9958
|
+
process.kill(process.pid, "SIGINT");
|
|
9959
|
+
};
|
|
9960
|
+
const handleExit = () => cleanUpInterruptedDownload();
|
|
9961
|
+
process.once("SIGINT", handleSigint);
|
|
9962
|
+
process.once("exit", handleExit);
|
|
9878
9963
|
try {
|
|
9879
9964
|
if (!existsSync2(versionedCacheDir)) {
|
|
9880
9965
|
mkdirSync(versionedCacheDir, { recursive: true });
|
|
9881
9966
|
}
|
|
9882
9967
|
releaseLock = await acquireDownloadLock(lockPath);
|
|
9968
|
+
sweepStaleDownloadTemps(versionedCacheDir, binaryName);
|
|
9883
9969
|
if (existsSync2(binaryPath) && isExpectedCachedBinary(binaryPath, tag)) {
|
|
9884
9970
|
return binaryPath;
|
|
9885
9971
|
}
|
|
@@ -9955,13 +10041,11 @@ async function downloadBinary(version) {
|
|
|
9955
10041
|
} catch (err) {
|
|
9956
10042
|
const msg = err instanceof Error ? err.message : String(err);
|
|
9957
10043
|
error(`Failed to download AFT binary: ${msg}`);
|
|
9958
|
-
|
|
9959
|
-
try {
|
|
9960
|
-
unlinkSync(tmpPath);
|
|
9961
|
-
} catch {}
|
|
9962
|
-
}
|
|
10044
|
+
cleanUpPartialDownload();
|
|
9963
10045
|
return null;
|
|
9964
10046
|
} finally {
|
|
10047
|
+
process.off("SIGINT", handleSigint);
|
|
10048
|
+
process.off("exit", handleExit);
|
|
9965
10049
|
if (binaryTimeout) {
|
|
9966
10050
|
binaryController?.abort();
|
|
9967
10051
|
clearTimeout(binaryTimeout);
|
|
@@ -9987,17 +10071,85 @@ async function ensureBinary(version) {
|
|
|
9987
10071
|
log("No cached binary found, downloading latest...");
|
|
9988
10072
|
return downloadBinary();
|
|
9989
10073
|
}
|
|
9990
|
-
|
|
10074
|
+
function createDownloadLockOwner() {
|
|
10075
|
+
return JSON.stringify({
|
|
10076
|
+
pid: process.pid,
|
|
10077
|
+
hostname: hostname(),
|
|
10078
|
+
createdAt: Date.now(),
|
|
10079
|
+
token: randomUUID()
|
|
10080
|
+
});
|
|
10081
|
+
}
|
|
10082
|
+
function parseDownloadLockOwner(raw) {
|
|
10083
|
+
try {
|
|
10084
|
+
const parsed = JSON.parse(raw);
|
|
10085
|
+
if (typeof parsed === "object" && parsed !== null) {
|
|
10086
|
+
const { pid, hostname: ownerHostname } = parsed;
|
|
10087
|
+
return {
|
|
10088
|
+
pid: typeof pid === "number" && Number.isSafeInteger(pid) && pid > 0 ? pid : null,
|
|
10089
|
+
hostname: typeof ownerHostname === "string" && ownerHostname ? ownerHostname : null
|
|
10090
|
+
};
|
|
10091
|
+
}
|
|
10092
|
+
} catch {}
|
|
10093
|
+
const legacyPid = Number(raw.split(":", 1)[0]);
|
|
10094
|
+
return {
|
|
10095
|
+
pid: Number.isSafeInteger(legacyPid) && legacyPid > 0 ? legacyPid : null,
|
|
10096
|
+
hostname: null
|
|
10097
|
+
};
|
|
10098
|
+
}
|
|
10099
|
+
function isProcessAlive(pid) {
|
|
10100
|
+
try {
|
|
10101
|
+
process.kill(pid, 0);
|
|
10102
|
+
return true;
|
|
10103
|
+
} catch (err) {
|
|
10104
|
+
return err.code !== "ESRCH";
|
|
10105
|
+
}
|
|
10106
|
+
}
|
|
10107
|
+
function isReclaimableDownloadLock(owner, ageMs, staleMs) {
|
|
10108
|
+
if (Math.abs(ageMs) > staleMs)
|
|
10109
|
+
return true;
|
|
10110
|
+
return (owner.hostname === null || owner.hostname === hostname()) && owner.pid !== null && !isProcessAlive(owner.pid);
|
|
10111
|
+
}
|
|
10112
|
+
function reclaimDownloadLock(lockPath, expectedOwner) {
|
|
10113
|
+
try {
|
|
10114
|
+
if (readFileSync3(lockPath, "utf-8") !== expectedOwner)
|
|
10115
|
+
return false;
|
|
10116
|
+
rmSync(lockPath, { force: true });
|
|
10117
|
+
return true;
|
|
10118
|
+
} catch (err) {
|
|
10119
|
+
if (err.code === "ENOENT")
|
|
10120
|
+
return true;
|
|
10121
|
+
throw err;
|
|
10122
|
+
}
|
|
10123
|
+
}
|
|
10124
|
+
function sweepStaleDownloadTemps(versionedCacheDir, binaryName) {
|
|
10125
|
+
const tempPrefix = `${binaryName}.`;
|
|
10126
|
+
try {
|
|
10127
|
+
for (const entry of readdirSync(versionedCacheDir, { withFileTypes: true })) {
|
|
10128
|
+
if (!entry.isFile() || !entry.name.startsWith(tempPrefix) || !entry.name.endsWith(".tmp")) {
|
|
10129
|
+
continue;
|
|
10130
|
+
}
|
|
10131
|
+
const tempPath = join4(versionedCacheDir, entry.name);
|
|
10132
|
+
const ageMs = Date.now() - statSync2(tempPath).mtimeMs;
|
|
10133
|
+
if (Math.abs(ageMs) > DOWNLOAD_LOCK_STALE_MS)
|
|
10134
|
+
unlinkSync(tempPath);
|
|
10135
|
+
}
|
|
10136
|
+
} catch {}
|
|
10137
|
+
}
|
|
10138
|
+
async function acquireDownloadLock(lockPath, timing = {}) {
|
|
10139
|
+
const timeoutMs = timing.timeoutMs ?? DOWNLOAD_LOCK_TIMEOUT_MS;
|
|
10140
|
+
const staleMs = timing.staleMs ?? DOWNLOAD_LOCK_STALE_MS;
|
|
10141
|
+
const pollIntervalMs = timing.pollIntervalMs ?? 100;
|
|
9991
10142
|
const startedAt = Date.now();
|
|
9992
10143
|
while (true) {
|
|
9993
10144
|
try {
|
|
9994
|
-
const owner =
|
|
10145
|
+
const owner = createDownloadLockOwner();
|
|
9995
10146
|
const fd = openSync(lockPath, "wx");
|
|
9996
|
-
|
|
10147
|
+
try {
|
|
10148
|
+
writeSync(fd, owner);
|
|
10149
|
+
} finally {
|
|
10150
|
+
closeSync(fd);
|
|
10151
|
+
}
|
|
9997
10152
|
return () => {
|
|
9998
|
-
try {
|
|
9999
|
-
closeSync(fd);
|
|
10000
|
-
} catch {}
|
|
10001
10153
|
try {
|
|
10002
10154
|
if (readFileSync3(lockPath, "utf-8") === owner) {
|
|
10003
10155
|
rmSync(lockPath, { force: true });
|
|
@@ -10008,19 +10160,24 @@ async function acquireDownloadLock(lockPath) {
|
|
|
10008
10160
|
const code = err.code;
|
|
10009
10161
|
if (code !== "EEXIST")
|
|
10010
10162
|
throw err;
|
|
10163
|
+
let existingOwner;
|
|
10164
|
+
let ageMs;
|
|
10011
10165
|
try {
|
|
10012
|
-
|
|
10013
|
-
|
|
10014
|
-
|
|
10166
|
+
existingOwner = readFileSync3(lockPath, "utf-8");
|
|
10167
|
+
ageMs = Date.now() - statSync2(lockPath).mtimeMs;
|
|
10168
|
+
} catch (readErr) {
|
|
10169
|
+
if (readErr.code === "ENOENT")
|
|
10170
|
+
continue;
|
|
10171
|
+
throw readErr;
|
|
10172
|
+
}
|
|
10173
|
+
if (isReclaimableDownloadLock(parseDownloadLockOwner(existingOwner), ageMs, staleMs)) {
|
|
10174
|
+
if (reclaimDownloadLock(lockPath, existingOwner))
|
|
10015
10175
|
continue;
|
|
10016
|
-
}
|
|
10017
|
-
} catch {
|
|
10018
|
-
continue;
|
|
10019
10176
|
}
|
|
10020
|
-
if (Date.now() - startedAt >
|
|
10177
|
+
if (Date.now() - startedAt > timeoutMs) {
|
|
10021
10178
|
throw new Error(`Timed out waiting for download lock: ${lockPath}`);
|
|
10022
10179
|
}
|
|
10023
|
-
await new Promise((resolve3) => setTimeout(resolve3,
|
|
10180
|
+
await new Promise((resolve3) => setTimeout(resolve3, pollIntervalMs));
|
|
10024
10181
|
}
|
|
10025
10182
|
}
|
|
10026
10183
|
}
|
|
@@ -12873,6 +13030,8 @@ function normalizeWindowsRoot(p) {
|
|
|
12873
13030
|
var AFT_MODULE_ID = "aft";
|
|
12874
13031
|
var MAX_CONSECUTIVE_TRANSPORT_FAILURES = 3;
|
|
12875
13032
|
var BG_STABLE_MS = 5000;
|
|
13033
|
+
var BG_LIFECYCLE_LOG_INTERVAL_MS = 60000;
|
|
13034
|
+
var BG_DISPATCH_PROBE_INTERVAL_MS = 60000;
|
|
12876
13035
|
var DEFAULT_SESSION_ID = "__default__";
|
|
12877
13036
|
var LOCALLY_SATISFIED_COMMANDS = new Set(["configure"]);
|
|
12878
13037
|
|
|
@@ -12935,12 +13094,15 @@ class BgSubscription {
|
|
|
12935
13094
|
canAttach;
|
|
12936
13095
|
onRootAttachFailure;
|
|
12937
13096
|
onDormant;
|
|
13097
|
+
dispatchProbeIntervalMs;
|
|
12938
13098
|
nudgeRef;
|
|
12939
13099
|
isCurrent;
|
|
12940
13100
|
stopped = false;
|
|
12941
13101
|
current = null;
|
|
12942
13102
|
loop;
|
|
12943
|
-
|
|
13103
|
+
lifecycleLogState = new Map;
|
|
13104
|
+
nudgeReceiptLogState = null;
|
|
13105
|
+
constructor(identity, acquireClient, dropClient, consumerIdentity, onNudge, sleep2, canAttach, onRootAttachFailure, onDormant, dispatchProbeIntervalMs, nudgeRef, isCurrent = () => true) {
|
|
12944
13106
|
this.identity = identity;
|
|
12945
13107
|
this.acquireClient = acquireClient;
|
|
12946
13108
|
this.dropClient = dropClient;
|
|
@@ -12950,6 +13112,7 @@ class BgSubscription {
|
|
|
12950
13112
|
this.canAttach = canAttach;
|
|
12951
13113
|
this.onRootAttachFailure = onRootAttachFailure;
|
|
12952
13114
|
this.onDormant = onDormant;
|
|
13115
|
+
this.dispatchProbeIntervalMs = dispatchProbeIntervalMs;
|
|
12953
13116
|
this.nudgeRef = nudgeRef;
|
|
12954
13117
|
this.isCurrent = isCurrent;
|
|
12955
13118
|
this.loop = this.run();
|
|
@@ -12966,25 +13129,106 @@ class BgSubscription {
|
|
|
12966
13129
|
return;
|
|
12967
13130
|
});
|
|
12968
13131
|
}
|
|
13132
|
+
info(kind, message) {
|
|
13133
|
+
const now = Date.now();
|
|
13134
|
+
const state = this.lifecycleLogState.get(kind);
|
|
13135
|
+
if (state && now - state.lastEmittedAt < BG_LIFECYCLE_LOG_INTERVAL_MS) {
|
|
13136
|
+
state.suppressed += 1;
|
|
13137
|
+
return;
|
|
13138
|
+
}
|
|
13139
|
+
const suppressed = state?.suppressed ?? 0;
|
|
13140
|
+
this.lifecycleLogState.set(kind, { lastEmittedAt: now, suppressed: 0 });
|
|
13141
|
+
const suffix = suppressed > 0 ? ` suppressed=${suppressed}` : "";
|
|
13142
|
+
log(`subc bg_events: ${message}${suffix}`, { sessionId: this.identity.session });
|
|
13143
|
+
}
|
|
13144
|
+
routeId(route) {
|
|
13145
|
+
return `${route.channel}@${route.epoch}`;
|
|
13146
|
+
}
|
|
13147
|
+
recordNudgeReceipt(routeId) {
|
|
13148
|
+
const now = Date.now();
|
|
13149
|
+
const state = this.nudgeReceiptLogState;
|
|
13150
|
+
if (state && now - state.lastEmittedAt < BG_LIFECYCLE_LOG_INTERVAL_MS) {
|
|
13151
|
+
state.count += 1;
|
|
13152
|
+
return;
|
|
13153
|
+
}
|
|
13154
|
+
const count = (state?.count ?? 0) + 1;
|
|
13155
|
+
this.nudgeReceiptLogState = { lastEmittedAt: now, count: 0 };
|
|
13156
|
+
log(`subc bg_events: nudge received channel=${routeId} count=${count}`, {
|
|
13157
|
+
sessionId: this.identity.session
|
|
13158
|
+
});
|
|
13159
|
+
}
|
|
13160
|
+
errorText(error2) {
|
|
13161
|
+
return error2 instanceof Error ? `${error2.name}: ${error2.message}` : String(error2);
|
|
13162
|
+
}
|
|
13163
|
+
startDispatchProbe(client, routeId) {
|
|
13164
|
+
const initial = client.droppedIngressFrames;
|
|
13165
|
+
if (typeof initial !== "number")
|
|
13166
|
+
return () => {
|
|
13167
|
+
return;
|
|
13168
|
+
};
|
|
13169
|
+
let previous = initial;
|
|
13170
|
+
const timer = setInterval(() => {
|
|
13171
|
+
const total = client.droppedIngressFrames;
|
|
13172
|
+
if (typeof total !== "number" || total <= previous)
|
|
13173
|
+
return;
|
|
13174
|
+
const delta = total - previous;
|
|
13175
|
+
previous = total;
|
|
13176
|
+
this.info("dispatch-epoch-drop", `client ingress epoch drops scope=client observed_while_channel=${routeId} delta=${delta} total=${total}`);
|
|
13177
|
+
}, this.dispatchProbeIntervalMs);
|
|
13178
|
+
timer.unref?.();
|
|
13179
|
+
return () => clearInterval(timer);
|
|
13180
|
+
}
|
|
12969
13181
|
async run() {
|
|
12970
|
-
let
|
|
13182
|
+
let backoffAttempt = 0;
|
|
13183
|
+
let reconnectAttempt = 0;
|
|
13184
|
+
let reconnecting = false;
|
|
13185
|
+
const beginReconnect = () => {
|
|
13186
|
+
reconnecting = true;
|
|
13187
|
+
reconnectAttempt = reconnectAttempt === 0 ? 1 : reconnectAttempt + 1;
|
|
13188
|
+
};
|
|
13189
|
+
const giveUp = (reason) => {
|
|
13190
|
+
this.info("reconnect-gave-up", `reconnect gave-up attempt=${reconnectAttempt} reason=${reason}`);
|
|
13191
|
+
};
|
|
12971
13192
|
while (!this.stopped) {
|
|
12972
|
-
if (!this.isCurrent())
|
|
13193
|
+
if (!this.isCurrent()) {
|
|
13194
|
+
if (reconnecting)
|
|
13195
|
+
giveUp("stale-session");
|
|
12973
13196
|
return;
|
|
13197
|
+
}
|
|
12974
13198
|
if (!this.canAttach()) {
|
|
13199
|
+
if (reconnecting)
|
|
13200
|
+
giveUp("root-dormant");
|
|
12975
13201
|
this.onDormant();
|
|
12976
13202
|
return;
|
|
12977
13203
|
}
|
|
13204
|
+
if (reconnecting) {
|
|
13205
|
+
this.info("reconnect-attempt", `reconnect attempt=${reconnectAttempt}`);
|
|
13206
|
+
}
|
|
12978
13207
|
let client;
|
|
12979
13208
|
try {
|
|
12980
13209
|
client = await this.acquireClient();
|
|
12981
|
-
} catch {
|
|
12982
|
-
|
|
13210
|
+
} catch (err) {
|
|
13211
|
+
if (!reconnecting)
|
|
13212
|
+
beginReconnect();
|
|
13213
|
+
this.info("reconnect-error", `reconnect error attempt=${reconnectAttempt} error=${this.errorText(err)}`);
|
|
13214
|
+
await this.backoff(backoffAttempt++);
|
|
13215
|
+
if (reconnecting)
|
|
13216
|
+
reconnectAttempt += 1;
|
|
12983
13217
|
continue;
|
|
12984
13218
|
}
|
|
12985
|
-
if (this.stopped
|
|
13219
|
+
if (this.stopped) {
|
|
13220
|
+
if (reconnecting)
|
|
13221
|
+
giveUp("stopped");
|
|
12986
13222
|
return;
|
|
13223
|
+
}
|
|
13224
|
+
if (!this.isCurrent()) {
|
|
13225
|
+
if (reconnecting)
|
|
13226
|
+
giveUp("stale-session");
|
|
13227
|
+
return;
|
|
13228
|
+
}
|
|
12987
13229
|
if (!this.canAttach()) {
|
|
13230
|
+
if (reconnecting)
|
|
13231
|
+
giveUp("root-dormant");
|
|
12988
13232
|
this.onDormant();
|
|
12989
13233
|
return;
|
|
12990
13234
|
}
|
|
@@ -12993,44 +13237,84 @@ class BgSubscription {
|
|
|
12993
13237
|
route = await client.routeOpen({ kind: "tool_provider", module_id: AFT_MODULE_ID }, this.identity, { consumerIdentity: this.consumerIdentity });
|
|
12994
13238
|
} catch (err) {
|
|
12995
13239
|
if (this.isCurrent() && this.onRootAttachFailure(err)) {
|
|
13240
|
+
if (reconnecting)
|
|
13241
|
+
giveUp("root-dormant");
|
|
12996
13242
|
this.onDormant();
|
|
12997
13243
|
return;
|
|
12998
13244
|
}
|
|
12999
13245
|
if (isConsumerReconnectTransient(err))
|
|
13000
13246
|
this.dropClient(client);
|
|
13001
|
-
|
|
13247
|
+
if (!reconnecting)
|
|
13248
|
+
beginReconnect();
|
|
13249
|
+
this.info("reconnect-error", `reconnect error attempt=${reconnectAttempt} error=${this.errorText(err)}`);
|
|
13250
|
+
await this.backoff(backoffAttempt++);
|
|
13251
|
+
if (reconnecting)
|
|
13252
|
+
reconnectAttempt += 1;
|
|
13002
13253
|
continue;
|
|
13003
13254
|
}
|
|
13004
13255
|
if (this.stopped || !this.isCurrent()) {
|
|
13005
13256
|
safeCloseRoute(client, route);
|
|
13257
|
+
if (reconnecting)
|
|
13258
|
+
giveUp(this.stopped ? "stopped" : "stale-session");
|
|
13006
13259
|
return;
|
|
13007
13260
|
}
|
|
13008
13261
|
const subscribedAt = Date.now();
|
|
13262
|
+
const routeId = this.routeId(route);
|
|
13263
|
+
let stopDispatchProbe = () => {
|
|
13264
|
+
return;
|
|
13265
|
+
};
|
|
13009
13266
|
try {
|
|
13010
13267
|
const sub = client.subscribe(route, { op: "bg_events" }, () => {
|
|
13011
|
-
if (
|
|
13012
|
-
this.
|
|
13268
|
+
if (this.stopped) {
|
|
13269
|
+
this.info("nudge-drop-stopped", `nudge dropped cause=subscription-stopped channel=${routeId}`);
|
|
13270
|
+
return;
|
|
13271
|
+
}
|
|
13272
|
+
this.recordNudgeReceipt(routeId);
|
|
13273
|
+
if (!this.isCurrent()) {
|
|
13274
|
+
this.info("nudge-stale-carrier", `nudge carried by stale subscription; checking current session channel=${routeId}`);
|
|
13275
|
+
}
|
|
13276
|
+
this.onNudge();
|
|
13013
13277
|
});
|
|
13014
13278
|
this.current = sub;
|
|
13279
|
+
stopDispatchProbe = this.startDispatchProbe(client, routeId);
|
|
13280
|
+
this.info("subscription-open", `subscription open channel=${routeId}`);
|
|
13281
|
+
if (reconnecting) {
|
|
13282
|
+
this.info("reconnect-success", `reconnect success attempt=${reconnectAttempt} channel=${routeId}`);
|
|
13283
|
+
reconnecting = false;
|
|
13284
|
+
reconnectAttempt = 0;
|
|
13285
|
+
}
|
|
13015
13286
|
if (this.stopped)
|
|
13016
13287
|
sub.unsubscribe();
|
|
13017
13288
|
if (!this.stopped && this.isCurrent())
|
|
13018
13289
|
this.onNudge();
|
|
13019
13290
|
await sub.closed;
|
|
13020
|
-
|
|
13291
|
+
this.info("stream-end", `stream ended channel=${routeId}`);
|
|
13292
|
+
if (this.stopped) {
|
|
13293
|
+
giveUp("stopped");
|
|
13294
|
+
return;
|
|
13295
|
+
}
|
|
13296
|
+
beginReconnect();
|
|
13021
13297
|
} catch (err) {
|
|
13022
|
-
|
|
13298
|
+
const routeId2 = this.routeId(route);
|
|
13299
|
+
this.info("stream-error", `stream error channel=${routeId2} error=${this.errorText(err)}`);
|
|
13300
|
+
if (this.stopped) {
|
|
13301
|
+
giveUp("stopped");
|
|
13023
13302
|
return;
|
|
13303
|
+
}
|
|
13024
13304
|
if (isConsumerReconnectTransient(err))
|
|
13025
13305
|
this.dropClient(client);
|
|
13026
13306
|
if (Date.now() - subscribedAt >= BG_STABLE_MS)
|
|
13027
|
-
|
|
13307
|
+
backoffAttempt = 0;
|
|
13308
|
+
beginReconnect();
|
|
13028
13309
|
} finally {
|
|
13310
|
+
stopDispatchProbe();
|
|
13029
13311
|
this.current = null;
|
|
13030
13312
|
safeCloseRoute(client, route);
|
|
13031
13313
|
}
|
|
13032
|
-
await this.backoff(
|
|
13314
|
+
await this.backoff(backoffAttempt++);
|
|
13033
13315
|
}
|
|
13316
|
+
if (reconnecting)
|
|
13317
|
+
giveUp("stopped");
|
|
13034
13318
|
}
|
|
13035
13319
|
async backoff(attempt) {
|
|
13036
13320
|
const ms = Math.min(100 * 2 ** Math.min(attempt, 6), 2000);
|
|
@@ -13128,6 +13412,9 @@ class SubcTransport {
|
|
|
13128
13412
|
this.assertCurrent();
|
|
13129
13413
|
const { preview, timeoutMs, onProgress } = this.splitOptions(options);
|
|
13130
13414
|
const body = { name, arguments: rawArgs };
|
|
13415
|
+
const editSlotSurvives = this.pool.getEditSlotSurvives();
|
|
13416
|
+
if (editSlotSurvives !== undefined)
|
|
13417
|
+
body.edit_slot_survives = editSlotSurvives;
|
|
13131
13418
|
if (preview === true)
|
|
13132
13419
|
body.preview = true;
|
|
13133
13420
|
const reply = await this.pool.routeRequest(this.identityFor(sessionId), body, timeoutMs, onProgress, this.generation);
|
|
@@ -13142,7 +13429,11 @@ class SubcTransport {
|
|
|
13142
13429
|
}
|
|
13143
13430
|
const { timeoutMs, onProgress } = this.splitOptions(options);
|
|
13144
13431
|
const session = typeof params.session_id === "string" ? params.session_id : undefined;
|
|
13145
|
-
const
|
|
13432
|
+
const body = { name: command, arguments: params };
|
|
13433
|
+
const editSlotSurvives = this.pool.getEditSlotSurvives();
|
|
13434
|
+
if (editSlotSurvives !== undefined)
|
|
13435
|
+
body.edit_slot_survives = editSlotSurvives;
|
|
13436
|
+
const reply = await this.pool.routeRequest(this.identityFor(session), body, timeoutMs, onProgress, this.generation);
|
|
13146
13437
|
const response = reliftReply(reply);
|
|
13147
13438
|
this.captureStatusBar(response);
|
|
13148
13439
|
return response;
|
|
@@ -13166,6 +13457,7 @@ class SubcTransportPool {
|
|
|
13166
13457
|
onBgEventsNudge;
|
|
13167
13458
|
onBgEventsNudgeRef;
|
|
13168
13459
|
bgBackoffSleep;
|
|
13460
|
+
bgDispatchProbeIntervalMs;
|
|
13169
13461
|
lifecycleDemandCheck;
|
|
13170
13462
|
onLifecycleEvent;
|
|
13171
13463
|
onBgNudgeRejected;
|
|
@@ -13181,8 +13473,11 @@ class SubcTransportPool {
|
|
|
13181
13473
|
transportFailures = 0;
|
|
13182
13474
|
transports = new Map;
|
|
13183
13475
|
generationRejections = new Set;
|
|
13476
|
+
nudgeDeliveryLogState = new Map;
|
|
13184
13477
|
pendingRootCleanups = new Set;
|
|
13185
13478
|
shuttingDown = false;
|
|
13479
|
+
editSlotSurvives;
|
|
13480
|
+
editSlotSurvivesCaptured = false;
|
|
13186
13481
|
constructor(options) {
|
|
13187
13482
|
this.connectionFile = options.connectionFile;
|
|
13188
13483
|
this.harness = options.harness;
|
|
@@ -13192,6 +13487,7 @@ class SubcTransportPool {
|
|
|
13192
13487
|
this.onBgEventsNudge = options.onBgEventsNudge;
|
|
13193
13488
|
this.onBgEventsNudgeRef = options.onBgEventsNudgeRef;
|
|
13194
13489
|
this.bgBackoffSleep = options.bgBackoffSleep ?? ((ms) => new Promise((resolve4) => setTimeout(resolve4, ms)));
|
|
13490
|
+
this.bgDispatchProbeIntervalMs = options.bgDispatchProbeIntervalMs ?? BG_DISPATCH_PROBE_INTERVAL_MS;
|
|
13195
13491
|
const lifecycle = options.lifecycle;
|
|
13196
13492
|
const demandCheck = options.lifecycleDemandCheck ?? options.demandCheck ?? lifecycle?.demandCheck;
|
|
13197
13493
|
this.lifecycleDemandCheck = demandCheck;
|
|
@@ -13508,6 +13804,35 @@ class SubcTransportPool {
|
|
|
13508
13804
|
isCurrentSession(key, record) {
|
|
13509
13805
|
return this.sessions.get(key) === record && !record.closed;
|
|
13510
13806
|
}
|
|
13807
|
+
currentSessionForNudge(identity) {
|
|
13808
|
+
const current = this.sessions.get(identityKey(identity));
|
|
13809
|
+
return current && !current.closed ? current : null;
|
|
13810
|
+
}
|
|
13811
|
+
nudgeRefFor(record) {
|
|
13812
|
+
const poolId = this.currentPoolId();
|
|
13813
|
+
const generation = record.generation;
|
|
13814
|
+
if (poolId === undefined || generation === undefined)
|
|
13815
|
+
return;
|
|
13816
|
+
return {
|
|
13817
|
+
canonicalRoot: record.canonicalRoot,
|
|
13818
|
+
session: record.identity.session,
|
|
13819
|
+
concretePoolId: poolId,
|
|
13820
|
+
generation
|
|
13821
|
+
};
|
|
13822
|
+
}
|
|
13823
|
+
logNudgeDelivery(kind, record, message) {
|
|
13824
|
+
const key = `${kind}\x00${record.identityKey}`;
|
|
13825
|
+
const now = Date.now();
|
|
13826
|
+
const state = this.nudgeDeliveryLogState.get(key);
|
|
13827
|
+
if (state && now - state.lastEmittedAt < BG_LIFECYCLE_LOG_INTERVAL_MS) {
|
|
13828
|
+
state.suppressed += 1;
|
|
13829
|
+
return;
|
|
13830
|
+
}
|
|
13831
|
+
const suppressed = state?.suppressed ?? 0;
|
|
13832
|
+
this.nudgeDeliveryLogState.set(key, { lastEmittedAt: now, suppressed: 0 });
|
|
13833
|
+
const suffix = suppressed > 0 ? ` suppressed=${suppressed}` : "";
|
|
13834
|
+
log(`subc bg_events: ${message}${suffix}`, { sessionId: record.identity.session });
|
|
13835
|
+
}
|
|
13511
13836
|
removeIndexMembership(record) {
|
|
13512
13837
|
const keys = this.rootIndex.get(record.canonicalRoot);
|
|
13513
13838
|
if (!keys)
|
|
@@ -13761,20 +14086,32 @@ class SubcTransportPool {
|
|
|
13761
14086
|
return;
|
|
13762
14087
|
if (record.bgSub)
|
|
13763
14088
|
return;
|
|
13764
|
-
const
|
|
13765
|
-
const generation = record.generation;
|
|
13766
|
-
const nudgeRef = poolId !== undefined && generation !== undefined ? {
|
|
13767
|
-
canonicalRoot: record.canonicalRoot,
|
|
13768
|
-
session: identity.session,
|
|
13769
|
-
concretePoolId: poolId,
|
|
13770
|
-
generation
|
|
13771
|
-
} : undefined;
|
|
14089
|
+
const nudgeRef = this.nudgeRefFor(record);
|
|
13772
14090
|
const onNudge = () => {
|
|
13773
|
-
|
|
14091
|
+
const currentRecord = this.currentSessionForNudge(identity);
|
|
14092
|
+
if (!currentRecord) {
|
|
14093
|
+
this.logNudgeDelivery("drop-no-current-session", record, `nudge dropped cause=no-current-session root=${record.canonicalRoot}`);
|
|
13774
14094
|
return;
|
|
13775
|
-
|
|
13776
|
-
if (
|
|
13777
|
-
this.
|
|
14095
|
+
}
|
|
14096
|
+
if (currentRecord !== record) {
|
|
14097
|
+
this.logNudgeDelivery("forward-superseded-carrier", currentRecord, `nudge forwarding cause=superseded-carrying-record root=${currentRecord.canonicalRoot}`);
|
|
14098
|
+
}
|
|
14099
|
+
const currentRef = this.nudgeRefFor(currentRecord);
|
|
14100
|
+
let delivered = false;
|
|
14101
|
+
if (currentRef && this.onBgEventsNudgeRef) {
|
|
14102
|
+
this.onBgEventsNudgeRef(currentRef);
|
|
14103
|
+
delivered = true;
|
|
14104
|
+
}
|
|
14105
|
+
if (this.onBgEventsNudge) {
|
|
14106
|
+
if (!currentRef && this.onBgEventsNudgeRef) {
|
|
14107
|
+
this.logNudgeDelivery("fallback-missing-generation", currentRecord, `nudge dispatch fallback=root-session-handler cause=generation-provenance-unavailable root=${currentRecord.canonicalRoot}`);
|
|
14108
|
+
}
|
|
14109
|
+
this.onBgEventsNudge(currentRecord.identity.project_root, currentRecord.identity.session);
|
|
14110
|
+
delivered = true;
|
|
14111
|
+
}
|
|
14112
|
+
if (delivered)
|
|
14113
|
+
return;
|
|
14114
|
+
this.logNudgeDelivery("drop-no-compatible-handler", currentRecord, `nudge dropped cause=generation-provenance-unavailable-and-root-session-handler-unwired root=${currentRecord.canonicalRoot}`);
|
|
13778
14115
|
};
|
|
13779
14116
|
let sub = null;
|
|
13780
14117
|
const clearDormantSubscription = () => {
|
|
@@ -13786,7 +14123,7 @@ class SubcTransportPool {
|
|
|
13786
14123
|
return false;
|
|
13787
14124
|
this.markRootDormant(record.canonicalRoot, error2);
|
|
13788
14125
|
return true;
|
|
13789
|
-
}, clearDormantSubscription, nudgeRef, () => this.isCurrentSession(record.identityKey, record) && this.isCurrentLiveGeneration(record.canonicalRoot, record.generation));
|
|
14126
|
+
}, clearDormantSubscription, this.bgDispatchProbeIntervalMs, nudgeRef, () => this.isCurrentSession(record.identityKey, record) && this.isCurrentLiveGeneration(record.canonicalRoot, record.generation));
|
|
13790
14127
|
record.bgSub = sub;
|
|
13791
14128
|
if (!this.rootCanAttach(record.canonicalRoot)) {
|
|
13792
14129
|
record.bgSub = null;
|
|
@@ -13848,7 +14185,21 @@ class SubcTransportPool {
|
|
|
13848
14185
|
return Promise.resolve();
|
|
13849
14186
|
return registry.requestProjectRootClose(registration.concretePoolId, root, generation, cause);
|
|
13850
14187
|
}
|
|
13851
|
-
setConfigureOverride(
|
|
14188
|
+
setConfigureOverride(key, value) {
|
|
14189
|
+
if (key !== "edit_slot_survives")
|
|
14190
|
+
return;
|
|
14191
|
+
if (typeof value !== "boolean") {
|
|
14192
|
+
throw new Error("edit_slot_survives must be set once to a boolean");
|
|
14193
|
+
}
|
|
14194
|
+
if (this.editSlotSurvivesCaptured) {
|
|
14195
|
+
throw new Error("edit_slot_survives is write-once and was already captured");
|
|
14196
|
+
}
|
|
14197
|
+
this.editSlotSurvives = value;
|
|
14198
|
+
this.editSlotSurvivesCaptured = true;
|
|
14199
|
+
}
|
|
14200
|
+
getEditSlotSurvives() {
|
|
14201
|
+
return this.editSlotSurvives;
|
|
14202
|
+
}
|
|
13852
14203
|
async reconfigure(_projectRoot, _overrides) {}
|
|
13853
14204
|
async replaceBinary(path2) {
|
|
13854
14205
|
return path2;
|
|
@@ -13926,14 +14277,29 @@ function toolErrorFromResponse(command, response) {
|
|
|
13926
14277
|
return new AftToolError(message, code, response);
|
|
13927
14278
|
}
|
|
13928
14279
|
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.";
|
|
14280
|
+
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.";
|
|
14281
|
+
function isRouteGoodbyeError(error2) {
|
|
14282
|
+
if (!(error2 instanceof SubcError))
|
|
14283
|
+
return false;
|
|
14284
|
+
if (error2.code === undefined) {
|
|
14285
|
+
return error2.message.includes("route closed by subc");
|
|
14286
|
+
}
|
|
14287
|
+
return error2.code === "route_closed" && error2.message.includes("route closed by subc");
|
|
14288
|
+
}
|
|
13929
14289
|
function isTransportClassError(error2) {
|
|
13930
14290
|
return isBridgeTransportTimeout(error2) || isConsumerReconnectTransient(error2) || error2 instanceof StaleRouteHandleError || error2 instanceof SubcRootGenerationExpiredError || error2 instanceof SubcRootReapedError;
|
|
13931
14291
|
}
|
|
13932
14292
|
function adaptToolError(command, error2) {
|
|
13933
|
-
if (command !== "bash" || !isTransportClassError(error2))
|
|
13934
|
-
return error2;
|
|
13935
14293
|
if (!(error2 instanceof Error))
|
|
13936
14294
|
return error2;
|
|
14295
|
+
if (isRouteGoodbyeError(error2)) {
|
|
14296
|
+
if (error2.message.includes(SUBC_MODULE_RESTART_DISPOSITION))
|
|
14297
|
+
return error2;
|
|
14298
|
+
error2.message = error2.message ? `${error2.message} ${SUBC_MODULE_RESTART_DISPOSITION}` : SUBC_MODULE_RESTART_DISPOSITION;
|
|
14299
|
+
return error2;
|
|
14300
|
+
}
|
|
14301
|
+
if (command !== "bash" || !isTransportClassError(error2))
|
|
14302
|
+
return error2;
|
|
13937
14303
|
if (error2.message.includes(BASH_TRANSPORT_DISPOSITION))
|
|
13938
14304
|
return error2;
|
|
13939
14305
|
error2.message = error2.message ? `${error2.message} ${BASH_TRANSPORT_DISPOSITION}` : BASH_TRANSPORT_DISPOSITION;
|
|
@@ -14740,7 +15106,7 @@ async function ensureStorageMigrated(opts) {
|
|
|
14740
15106
|
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}` : ""));
|
|
14741
15107
|
}
|
|
14742
15108
|
// ../aft-bridge/dist/npm-resolver.js
|
|
14743
|
-
import { readdirSync, statSync as statSync5 } from "node:fs";
|
|
15109
|
+
import { readdirSync as readdirSync2, statSync as statSync5 } from "node:fs";
|
|
14744
15110
|
import { homedir as homedir9 } from "node:os";
|
|
14745
15111
|
import { delimiter, dirname as dirname4, isAbsolute as isAbsolute3, join as join9 } from "node:path";
|
|
14746
15112
|
function defaultDeps() {
|
|
@@ -14780,7 +15146,7 @@ function npmAdjacentToNode(deps) {
|
|
|
14780
15146
|
function highestVersionedNodeBin(installsDir, name) {
|
|
14781
15147
|
let entries;
|
|
14782
15148
|
try {
|
|
14783
|
-
entries =
|
|
15149
|
+
entries = readdirSync2(installsDir);
|
|
14784
15150
|
} catch {
|
|
14785
15151
|
return null;
|
|
14786
15152
|
}
|
|
@@ -14856,7 +15222,7 @@ function npmSpawnEnv(resolved, baseEnv = process.env) {
|
|
|
14856
15222
|
// ../aft-bridge/dist/onnx-runtime.js
|
|
14857
15223
|
import { execFileSync } from "node:child_process";
|
|
14858
15224
|
import { createHash as createHash3 } from "node:crypto";
|
|
14859
|
-
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
|
|
15225
|
+
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";
|
|
14860
15226
|
import { basename as basename2, dirname as dirname5, isAbsolute as isAbsolute4, join as join10, relative as relative2, resolve as resolve6, win32 } from "node:path";
|
|
14861
15227
|
import { Readable as Readable2 } from "node:stream";
|
|
14862
15228
|
import { pipeline as pipeline2 } from "node:stream/promises";
|
|
@@ -14970,7 +15336,7 @@ async function ensureOnnxRuntime(storageDir) {
|
|
|
14970
15336
|
}
|
|
14971
15337
|
function cleanupAbandonedStagingDirs(onnxBaseDir) {
|
|
14972
15338
|
try {
|
|
14973
|
-
const entries =
|
|
15339
|
+
const entries = readdirSync3(onnxBaseDir);
|
|
14974
15340
|
for (const entry of entries) {
|
|
14975
15341
|
if (!entry.startsWith(`${ORT_VERSION}.tmp.`))
|
|
14976
15342
|
continue;
|
|
@@ -14981,7 +15347,7 @@ function cleanupAbandonedStagingDirs(onnxBaseDir) {
|
|
|
14981
15347
|
let abandoned = false;
|
|
14982
15348
|
if (Number.isFinite(pid) && pid > 0) {
|
|
14983
15349
|
if (process.platform === "win32") {
|
|
14984
|
-
const ownerAlive =
|
|
15350
|
+
const ownerAlive = isProcessAlive2(pid);
|
|
14985
15351
|
if (!ownerAlive) {
|
|
14986
15352
|
abandoned = true;
|
|
14987
15353
|
} else {
|
|
@@ -14993,7 +15359,7 @@ function cleanupAbandonedStagingDirs(onnxBaseDir) {
|
|
|
14993
15359
|
}
|
|
14994
15360
|
}
|
|
14995
15361
|
} else {
|
|
14996
|
-
abandoned = !
|
|
15362
|
+
abandoned = !isProcessAlive2(pid);
|
|
14997
15363
|
}
|
|
14998
15364
|
} else {
|
|
14999
15365
|
abandoned = true;
|
|
@@ -15044,7 +15410,7 @@ function isPathInsideRoot(root, candidate) {
|
|
|
15044
15410
|
}
|
|
15045
15411
|
function detectOnnxVersion(libDir, libName) {
|
|
15046
15412
|
try {
|
|
15047
|
-
const entries =
|
|
15413
|
+
const entries = readdirSync3(libDir);
|
|
15048
15414
|
const barePrefix = libName.replace(/\.(so|dylib|dll)$/, "");
|
|
15049
15415
|
const expectedPrefix = process.platform === "win32" ? barePrefix.toLowerCase() : barePrefix;
|
|
15050
15416
|
for (const entry of entries) {
|
|
@@ -15107,7 +15473,7 @@ function isWindowsSystem32Directory(dir) {
|
|
|
15107
15473
|
}
|
|
15108
15474
|
function directoryContainsLibrary(dir, libName) {
|
|
15109
15475
|
try {
|
|
15110
|
-
const entries =
|
|
15476
|
+
const entries = readdirSync3(dir);
|
|
15111
15477
|
if (process.platform === "win32") {
|
|
15112
15478
|
const expected = libName.toLowerCase();
|
|
15113
15479
|
return entries.some((entry) => entry.toLowerCase() === expected);
|
|
@@ -15145,7 +15511,7 @@ function findSystemOnnxRuntime(libName) {
|
|
|
15145
15511
|
if (!existsSync7(nugetPackageDir))
|
|
15146
15512
|
return nugetPaths;
|
|
15147
15513
|
try {
|
|
15148
|
-
for (const entry of
|
|
15514
|
+
for (const entry of readdirSync3(nugetPackageDir, { withFileTypes: true })) {
|
|
15149
15515
|
if (!entry.isDirectory())
|
|
15150
15516
|
continue;
|
|
15151
15517
|
if (entry.name === "__globalPackagesFolder" || entry.name.startsWith("."))
|
|
@@ -15240,7 +15606,7 @@ function validateExtractedTree(stagingRoot) {
|
|
|
15240
15606
|
const realRoot = realpathSync2(stagingRoot);
|
|
15241
15607
|
let totalBytes = 0;
|
|
15242
15608
|
const walk = (dir) => {
|
|
15243
|
-
const entries =
|
|
15609
|
+
const entries = readdirSync3(dir);
|
|
15244
15610
|
for (const entry of entries) {
|
|
15245
15611
|
const fullPath = join10(dir, entry);
|
|
15246
15612
|
const lst = lstatSync(fullPath);
|
|
@@ -15298,7 +15664,7 @@ async function downloadOnnxRuntime(info, targetDir) {
|
|
|
15298
15664
|
throw new Error(`Expected directory not found: ${extractedDir}`);
|
|
15299
15665
|
}
|
|
15300
15666
|
mkdirSync5(targetDir, { recursive: true });
|
|
15301
|
-
const libFiles =
|
|
15667
|
+
const libFiles = readdirSync3(extractedDir).filter((f) => f.startsWith("libonnxruntime") || f.startsWith("onnxruntime"));
|
|
15302
15668
|
const realFiles = [];
|
|
15303
15669
|
const symlinks = [];
|
|
15304
15670
|
for (const libFile of libFiles) {
|
|
@@ -15476,7 +15842,7 @@ ${new Date().toISOString()}
|
|
|
15476
15842
|
}
|
|
15477
15843
|
const age = Date.now() - lockMtimeMs;
|
|
15478
15844
|
const ageWithinFresh = Math.abs(age) < STALE_LOCK_MS;
|
|
15479
|
-
const ownerAlive = owningPid !== null &&
|
|
15845
|
+
const ownerAlive = owningPid !== null && isProcessAlive2(owningPid);
|
|
15480
15846
|
if (ownerAlive && ageWithinFresh) {
|
|
15481
15847
|
return false;
|
|
15482
15848
|
}
|
|
@@ -15540,7 +15906,7 @@ function isWindowsProcessAlive(pid) {
|
|
|
15540
15906
|
return false;
|
|
15541
15907
|
}
|
|
15542
15908
|
}
|
|
15543
|
-
function
|
|
15909
|
+
function isProcessAlive2(pid) {
|
|
15544
15910
|
if (process.platform === "win32")
|
|
15545
15911
|
return isWindowsProcessAlive(pid);
|
|
15546
15912
|
try {
|
|
@@ -15915,6 +16281,19 @@ function parseEditArray(value) {
|
|
|
15915
16281
|
}
|
|
15916
16282
|
return value;
|
|
15917
16283
|
}
|
|
16284
|
+
function stripLineRangeSentinels(item) {
|
|
16285
|
+
const hasRangeField = ["startLine", "endLine", "content"].some((key) => hasOwn(item, key));
|
|
16286
|
+
if (!hasRangeField)
|
|
16287
|
+
return;
|
|
16288
|
+
if (item.oldString === "")
|
|
16289
|
+
delete item.oldString;
|
|
16290
|
+
if (item.newString === "")
|
|
16291
|
+
delete item.newString;
|
|
16292
|
+
if (item.replaceAll === false)
|
|
16293
|
+
delete item.replaceAll;
|
|
16294
|
+
if (item.occurrence === 1)
|
|
16295
|
+
delete item.occurrence;
|
|
16296
|
+
}
|
|
15918
16297
|
function normalizeEditItem(value, index) {
|
|
15919
16298
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
15920
16299
|
throw new InvalidRequestError(`edit: edits[${index}] must be an object`);
|
|
@@ -15923,6 +16302,7 @@ function normalizeEditItem(value, index) {
|
|
|
15923
16302
|
const item = copyOwnProperties(source);
|
|
15924
16303
|
normalizeItemAlias(item, "oldString", "oldText");
|
|
15925
16304
|
normalizeItemAlias(item, "newString", "newText");
|
|
16305
|
+
stripLineRangeSentinels(item);
|
|
15926
16306
|
const hasFindField = ["oldString", "newString", "replaceAll", "occurrence"].some((key) => hasOwn(item, key));
|
|
15927
16307
|
const hasRangeField = ["startLine", "endLine", "content"].some((key) => hasOwn(item, key));
|
|
15928
16308
|
if (hasFindField && hasRangeField) {
|
|
@@ -16065,6 +16445,8 @@ class BridgePool {
|
|
|
16065
16445
|
idleTimeoutMs;
|
|
16066
16446
|
bridgeOptions;
|
|
16067
16447
|
configOverrides;
|
|
16448
|
+
editSlotSurvives;
|
|
16449
|
+
editSlotSurvivesCaptured = false;
|
|
16068
16450
|
projectConfigLoader;
|
|
16069
16451
|
logger;
|
|
16070
16452
|
cleanupTimer = null;
|
|
@@ -16089,7 +16471,16 @@ class BridgePool {
|
|
|
16089
16471
|
logger: options.logger,
|
|
16090
16472
|
childEnv: options.childEnv
|
|
16091
16473
|
};
|
|
16092
|
-
this.configOverrides = configOverrides;
|
|
16474
|
+
this.configOverrides = { ...configOverrides };
|
|
16475
|
+
const initialEditSlotSurvives = this.configOverrides.edit_slot_survives;
|
|
16476
|
+
delete this.configOverrides.edit_slot_survives;
|
|
16477
|
+
if (initialEditSlotSurvives !== undefined) {
|
|
16478
|
+
if (typeof initialEditSlotSurvives !== "boolean") {
|
|
16479
|
+
throw new Error("edit_slot_survives must be a boolean");
|
|
16480
|
+
}
|
|
16481
|
+
this.editSlotSurvives = initialEditSlotSurvives;
|
|
16482
|
+
this.editSlotSurvivesCaptured = true;
|
|
16483
|
+
}
|
|
16093
16484
|
this.startCleanupTimer();
|
|
16094
16485
|
}
|
|
16095
16486
|
getActiveBridgeForRoot(projectRoot) {
|
|
@@ -16128,7 +16519,8 @@ class BridgePool {
|
|
|
16128
16519
|
}
|
|
16129
16520
|
const projectOverrides = this.loadProjectOverrides(key);
|
|
16130
16521
|
const mergedOverrides = { ...this.configOverrides, ...projectOverrides };
|
|
16131
|
-
|
|
16522
|
+
delete mergedOverrides.edit_slot_survives;
|
|
16523
|
+
const bridge = new BinaryBridge(this.binaryPath, key, this.bridgeOptions, mergedOverrides, this.editSlotSurvivesCaptured ? this.editSlotSurvives : undefined);
|
|
16132
16524
|
this.bridges.set(key, { bridge, lastUsed: Date.now() });
|
|
16133
16525
|
return bridge;
|
|
16134
16526
|
}
|
|
@@ -16232,6 +16624,23 @@ class BridgePool {
|
|
|
16232
16624
|
error(message, meta);
|
|
16233
16625
|
}
|
|
16234
16626
|
setConfigureOverride(key, value) {
|
|
16627
|
+
if (key === "edit_slot_survives") {
|
|
16628
|
+
if (typeof value !== "boolean") {
|
|
16629
|
+
throw new Error("edit_slot_survives must be set once to a boolean");
|
|
16630
|
+
}
|
|
16631
|
+
if (this.editSlotSurvivesCaptured) {
|
|
16632
|
+
throw new Error("edit_slot_survives is write-once and was already captured");
|
|
16633
|
+
}
|
|
16634
|
+
this.editSlotSurvives = value;
|
|
16635
|
+
this.editSlotSurvivesCaptured = true;
|
|
16636
|
+
for (const entry of this.bridges.values()) {
|
|
16637
|
+
entry.bridge.setEditSlotSurvives(value);
|
|
16638
|
+
}
|
|
16639
|
+
for (const bridge of this.staleBridges) {
|
|
16640
|
+
bridge.setEditSlotSurvives(value);
|
|
16641
|
+
}
|
|
16642
|
+
return;
|
|
16643
|
+
}
|
|
16235
16644
|
if (value === undefined) {
|
|
16236
16645
|
delete this.configOverrides[key];
|
|
16237
16646
|
} else {
|
|
@@ -16289,6 +16698,7 @@ class RevivableTransportPool {
|
|
|
16289
16698
|
revival = null;
|
|
16290
16699
|
transports = new Map;
|
|
16291
16700
|
configureOverrides = new Map;
|
|
16701
|
+
editSlotSurvivesCaptured = false;
|
|
16292
16702
|
constructor(initialPool, createPool, onBinaryReplaced) {
|
|
16293
16703
|
this.createPool = createPool;
|
|
16294
16704
|
this.onBinaryReplaced = onBinaryReplaced;
|
|
@@ -16319,6 +16729,18 @@ class RevivableTransportPool {
|
|
|
16319
16729
|
return this.getBridge(projectRoot).toolCall(runtime.sessionID, name, rawArgs, options);
|
|
16320
16730
|
}
|
|
16321
16731
|
setConfigureOverride(key, value) {
|
|
16732
|
+
if (key === "edit_slot_survives") {
|
|
16733
|
+
if (typeof value !== "boolean") {
|
|
16734
|
+
throw new Error("edit_slot_survives must be set once to a boolean");
|
|
16735
|
+
}
|
|
16736
|
+
if (this.editSlotSurvivesCaptured) {
|
|
16737
|
+
throw new Error("edit_slot_survives is write-once and was already captured");
|
|
16738
|
+
}
|
|
16739
|
+
this.activePool.setConfigureOverride(key, value);
|
|
16740
|
+
this.editSlotSurvivesCaptured = true;
|
|
16741
|
+
this.configureOverrides.set(key, value);
|
|
16742
|
+
return;
|
|
16743
|
+
}
|
|
16322
16744
|
if (value === undefined)
|
|
16323
16745
|
this.configureOverrides.delete(key);
|
|
16324
16746
|
else
|
|
@@ -16326,14 +16748,20 @@ class RevivableTransportPool {
|
|
|
16326
16748
|
this.activePool.setConfigureOverride(key, value);
|
|
16327
16749
|
}
|
|
16328
16750
|
async reconfigure(projectRoot, overrides) {
|
|
16751
|
+
const pool = await this.ensureActivePool();
|
|
16752
|
+
const runtimeOverrides = {};
|
|
16329
16753
|
for (const [key, value] of Object.entries(overrides)) {
|
|
16754
|
+
if (key === "edit_slot_survives") {
|
|
16755
|
+
this.setConfigureOverride(key, value);
|
|
16756
|
+
continue;
|
|
16757
|
+
}
|
|
16330
16758
|
if (value === undefined)
|
|
16331
16759
|
this.configureOverrides.delete(key);
|
|
16332
16760
|
else
|
|
16333
16761
|
this.configureOverrides.set(key, value);
|
|
16762
|
+
runtimeOverrides[key] = value;
|
|
16334
16763
|
}
|
|
16335
|
-
|
|
16336
|
-
await pool.reconfigure(projectRoot, overrides);
|
|
16764
|
+
await pool.reconfigure(projectRoot, runtimeOverrides);
|
|
16337
16765
|
}
|
|
16338
16766
|
async replaceBinary(path2) {
|
|
16339
16767
|
const replaced = await this.activePool.replaceBinary(path2);
|
|
@@ -16739,6 +17167,12 @@ var UNKNOWN_COMPLETION_TTL_MS = 5000;
|
|
|
16739
17167
|
var UNKNOWN_COMPLETION_CAP = 32;
|
|
16740
17168
|
var DEFAULT_SESSION_ID2 = "__default__";
|
|
16741
17169
|
var LOG_PREFIX = "[aft-pi] bg-notifications:";
|
|
17170
|
+
var SUBC_NUDGE_LOG_INTERVAL_MS = 60000;
|
|
17171
|
+
var subcNudgesInFlight = new Map;
|
|
17172
|
+
var subcNudgeLogState = new Map;
|
|
17173
|
+
function normalizeSessionID(sessionID) {
|
|
17174
|
+
return sessionID && sessionID.length > 0 ? sessionID : DEFAULT_SESSION_ID2;
|
|
17175
|
+
}
|
|
16742
17176
|
function consumeBgCompletion(sessionID, taskId) {
|
|
16743
17177
|
const state = stateFor(sessionID);
|
|
16744
17178
|
state.pendingCompletions = state.pendingCompletions.filter((c) => c.task_id !== taskId);
|
|
@@ -16934,11 +17368,49 @@ async function handleTurnEndBgCompletions(drainContext) {
|
|
|
16934
17368
|
stateFor(drainContext.sessionID).wakeDeferredTaskIds.clear();
|
|
16935
17369
|
await triggerWakeIfPending(drainContext, false, true);
|
|
16936
17370
|
}
|
|
16937
|
-
|
|
17371
|
+
function handleSubcBgEventsNudge(drainContext) {
|
|
17372
|
+
const resolvedDrainContext = {
|
|
17373
|
+
...drainContext,
|
|
17374
|
+
sessionID: normalizeSessionID(drainContext.sessionID)
|
|
17375
|
+
};
|
|
17376
|
+
const key = `${resolvedDrainContext.directory}\x00${resolvedDrainContext.sessionID}`;
|
|
17377
|
+
const inFlight = subcNudgesInFlight.get(key);
|
|
17378
|
+
if (inFlight) {
|
|
17379
|
+
logSubcNudgeLifecycle(resolvedDrainContext, "coalesced-in-flight", "nudge coalesced cause=handler-already-in-flight");
|
|
17380
|
+
return inFlight;
|
|
17381
|
+
}
|
|
17382
|
+
logSubcNudgeLifecycle(resolvedDrainContext, "handler-entry", "nudge handler entered");
|
|
17383
|
+
const handling = handleSubcBgEventsNudgeOnce(resolvedDrainContext);
|
|
17384
|
+
subcNudgesInFlight.set(key, handling);
|
|
17385
|
+
const clear = () => {
|
|
17386
|
+
if (subcNudgesInFlight.get(key) === handling)
|
|
17387
|
+
subcNudgesInFlight.delete(key);
|
|
17388
|
+
};
|
|
17389
|
+
handling.then(clear, clear);
|
|
17390
|
+
return handling;
|
|
17391
|
+
}
|
|
17392
|
+
async function handleSubcBgEventsNudgeOnce(drainContext) {
|
|
16938
17393
|
bridgeForDrain(drainContext);
|
|
16939
17394
|
stateFor(drainContext.sessionID).wakeDeferredTaskIds.clear();
|
|
16940
17395
|
await triggerWakeIfPending(drainContext, false, true, true);
|
|
16941
17396
|
}
|
|
17397
|
+
function logSubcNudgeLifecycle(drainContext, kind, message) {
|
|
17398
|
+
const key = `${kind}\x00${drainContext.directory}\x00${drainContext.sessionID}`;
|
|
17399
|
+
const now = Date.now();
|
|
17400
|
+
const state = subcNudgeLogState.get(key);
|
|
17401
|
+
if (state && now - state.lastEmittedAt < SUBC_NUDGE_LOG_INTERVAL_MS) {
|
|
17402
|
+
state.suppressed += 1;
|
|
17403
|
+
return;
|
|
17404
|
+
}
|
|
17405
|
+
const suppressed = state?.suppressed ?? 0;
|
|
17406
|
+
subcNudgeLogState.set(key, { lastEmittedAt: now, suppressed: 0 });
|
|
17407
|
+
sessionLog(drainContext.sessionID, `${LOG_PREFIX} ${message}`, {
|
|
17408
|
+
event: "subc_bg_nudge_delivery",
|
|
17409
|
+
cause: kind,
|
|
17410
|
+
canonical_root: drainContext.directory,
|
|
17411
|
+
suppressed
|
|
17412
|
+
});
|
|
17413
|
+
}
|
|
16942
17414
|
async function triggerWakeIfPending(drainContext, skipDrain, includeDeferredCompletions = true, forceDrain = false) {
|
|
16943
17415
|
const state = stateFor(drainContext.sessionID);
|
|
16944
17416
|
if (!skipDrain && (forceDrain || state.outstandingTaskIds.size > 0 || !state.forcedDrainCompleted)) {
|
|
@@ -17406,7 +17878,7 @@ import {
|
|
|
17406
17878
|
// package.json
|
|
17407
17879
|
var package_default = {
|
|
17408
17880
|
name: "@cortexkit/aft-pi",
|
|
17409
|
-
version: "0.
|
|
17881
|
+
version: "0.50.1",
|
|
17410
17882
|
type: "module",
|
|
17411
17883
|
description: "Pi coding agent extension for Agent File Tools (AFT) — tree-sitter and LSP-powered code analysis",
|
|
17412
17884
|
main: "dist/index.js",
|
|
@@ -17429,19 +17901,19 @@ var package_default = {
|
|
|
17429
17901
|
"test:unit": "bun test src/__tests__ --path-ignore-patterns 'src/__tests__/e2e/**'"
|
|
17430
17902
|
},
|
|
17431
17903
|
dependencies: {
|
|
17432
|
-
"@cortexkit/aft-bridge": "0.
|
|
17904
|
+
"@cortexkit/aft-bridge": "0.50.1",
|
|
17433
17905
|
"comment-json": "^5.0.0",
|
|
17434
17906
|
diff: "^8.0.4",
|
|
17435
17907
|
typebox: "1.1.38",
|
|
17436
17908
|
zod: "^4.4.3"
|
|
17437
17909
|
},
|
|
17438
17910
|
optionalDependencies: {
|
|
17439
|
-
"@cortexkit/aft-darwin-arm64": "0.
|
|
17440
|
-
"@cortexkit/aft-darwin-x64": "0.
|
|
17441
|
-
"@cortexkit/aft-linux-arm64": "0.
|
|
17442
|
-
"@cortexkit/aft-linux-x64": "0.
|
|
17443
|
-
"@cortexkit/aft-win32-arm64": "0.
|
|
17444
|
-
"@cortexkit/aft-win32-x64": "0.
|
|
17911
|
+
"@cortexkit/aft-darwin-arm64": "0.50.1",
|
|
17912
|
+
"@cortexkit/aft-darwin-x64": "0.50.1",
|
|
17913
|
+
"@cortexkit/aft-linux-arm64": "0.50.1",
|
|
17914
|
+
"@cortexkit/aft-linux-x64": "0.50.1",
|
|
17915
|
+
"@cortexkit/aft-win32-arm64": "0.50.1",
|
|
17916
|
+
"@cortexkit/aft-win32-x64": "0.50.1"
|
|
17445
17917
|
},
|
|
17446
17918
|
devDependencies: {
|
|
17447
17919
|
"@earendil-works/pi-coding-agent": "^0.80.2",
|
|
@@ -18181,7 +18653,7 @@ __export(exports_external, {
|
|
|
18181
18653
|
instanceof: () => _instanceof,
|
|
18182
18654
|
includes: () => _includes,
|
|
18183
18655
|
httpUrl: () => httpUrl,
|
|
18184
|
-
hostname: () =>
|
|
18656
|
+
hostname: () => hostname3,
|
|
18185
18657
|
hex: () => hex2,
|
|
18186
18658
|
hash: () => hash,
|
|
18187
18659
|
guid: () => guid2,
|
|
@@ -19632,7 +20104,7 @@ __export(exports_regexes, {
|
|
|
19632
20104
|
idnEmail: () => idnEmail,
|
|
19633
20105
|
httpProtocol: () => httpProtocol,
|
|
19634
20106
|
html5Email: () => html5Email,
|
|
19635
|
-
hostname: () =>
|
|
20107
|
+
hostname: () => hostname2,
|
|
19636
20108
|
hex: () => hex,
|
|
19637
20109
|
guid: () => guid,
|
|
19638
20110
|
extendedDuration: () => extendedDuration,
|
|
@@ -19690,7 +20162,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]
|
|
|
19690
20162
|
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])$/;
|
|
19691
20163
|
var base64 = /^$|^(?:[0-9a-zA-Z+/]{4})*(?:(?:[0-9a-zA-Z+/]{2}==)|(?:[0-9a-zA-Z+/]{3}=))?$/;
|
|
19692
20164
|
var base64url = /^[A-Za-z0-9_-]*$/;
|
|
19693
|
-
var
|
|
20165
|
+
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])?)*\.?$/;
|
|
19694
20166
|
var domain = /^([a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$/;
|
|
19695
20167
|
var httpProtocol = /^https?$/;
|
|
19696
20168
|
var e164 = /^\+[1-9]\d{6,14}$/;
|
|
@@ -30314,7 +30786,7 @@ __export(exports_schemas2, {
|
|
|
30314
30786
|
int: () => int,
|
|
30315
30787
|
instanceof: () => _instanceof,
|
|
30316
30788
|
httpUrl: () => httpUrl,
|
|
30317
|
-
hostname: () =>
|
|
30789
|
+
hostname: () => hostname3,
|
|
30318
30790
|
hex: () => hex2,
|
|
30319
30791
|
hash: () => hash,
|
|
30320
30792
|
guid: () => guid2,
|
|
@@ -30966,7 +31438,7 @@ var ZodCustomStringFormat = /* @__PURE__ */ $constructor("ZodCustomStringFormat"
|
|
|
30966
31438
|
function stringFormat(format, fnOrRegex, _params = {}) {
|
|
30967
31439
|
return _stringFormat(ZodCustomStringFormat, format, fnOrRegex, _params);
|
|
30968
31440
|
}
|
|
30969
|
-
function
|
|
31441
|
+
function hostname3(_params) {
|
|
30970
31442
|
return _stringFormat(ZodCustomStringFormat, "hostname", exports_regexes.hostname, _params);
|
|
30971
31443
|
}
|
|
30972
31444
|
function hex2(_params) {
|
|
@@ -32502,6 +32974,7 @@ var BackupConfigSchema = exports_external.object({
|
|
|
32502
32974
|
var AftConfigSchema = exports_external.preprocess((value) => stripHarnessSpecificConfigKeys(value, OPENCODE_ONLY_KEYS), exports_external.object({
|
|
32503
32975
|
$schema: exports_external.string().optional(),
|
|
32504
32976
|
enabled: exports_external.boolean().optional(),
|
|
32977
|
+
edit_mode: exports_external.enum(["default", "hashline"]).optional(),
|
|
32505
32978
|
format_on_edit: exports_external.boolean().optional(),
|
|
32506
32979
|
formatter_timeout_secs: exports_external.number().int().min(1).max(600).optional(),
|
|
32507
32980
|
validate_on_edit: exports_external.enum(["syntax", "full"]).optional(),
|
|
@@ -32715,10 +33188,15 @@ function loadConfigFromPath(configPath) {
|
|
|
32715
33188
|
}
|
|
32716
33189
|
}
|
|
32717
33190
|
function parseConfigPartially(rawConfig) {
|
|
33191
|
+
let configForParsing = rawConfig;
|
|
33192
|
+
if (Object.hasOwn(rawConfig, "edit_mode") && rawConfig.edit_mode !== "default" && rawConfig.edit_mode !== "hashline") {
|
|
33193
|
+
warn2(`Unknown edit_mode value ${JSON.stringify(rawConfig.edit_mode)}; falling back to "default"`);
|
|
33194
|
+
configForParsing = { ...rawConfig, edit_mode: "default" };
|
|
33195
|
+
}
|
|
32718
33196
|
const partialConfig = {};
|
|
32719
33197
|
const invalidSections = [];
|
|
32720
|
-
for (const key of Object.keys(
|
|
32721
|
-
const sectionResult = AftConfigSchema.safeParse({ [key]:
|
|
33198
|
+
for (const key of Object.keys(configForParsing)) {
|
|
33199
|
+
const sectionResult = AftConfigSchema.safeParse({ [key]: configForParsing[key] });
|
|
32722
33200
|
if (sectionResult.success) {
|
|
32723
33201
|
const parsed = sectionResult.data;
|
|
32724
33202
|
if (parsed[key] !== undefined) {
|
|
@@ -32843,6 +33321,7 @@ function getProjectLspStrippedKeys(lsp) {
|
|
|
32843
33321
|
}
|
|
32844
33322
|
var PROJECT_SAFE_TOP_LEVEL_FIELDS = new Set([
|
|
32845
33323
|
"enabled",
|
|
33324
|
+
"edit_mode",
|
|
32846
33325
|
"tool_surface",
|
|
32847
33326
|
"format_on_edit",
|
|
32848
33327
|
"validate_on_edit",
|
|
@@ -33122,7 +33601,7 @@ ${new Date().toISOString()}
|
|
|
33122
33601
|
const age = Date.now() - lockMtimeMs;
|
|
33123
33602
|
const ageWithinFresh = Math.abs(age) < STALE_LOCK_MS2;
|
|
33124
33603
|
const skipLiveness = process.platform === "win32";
|
|
33125
|
-
const ownerAlive = !skipLiveness && owningPid !== null &&
|
|
33604
|
+
const ownerAlive = !skipLiveness && owningPid !== null && isProcessAlive3(owningPid);
|
|
33126
33605
|
if (skipLiveness ? ageWithinFresh : ownerAlive && ageWithinFresh) {
|
|
33127
33606
|
return false;
|
|
33128
33607
|
}
|
|
@@ -33132,7 +33611,7 @@ ${new Date().toISOString()}
|
|
|
33132
33611
|
} catch {}
|
|
33133
33612
|
return tryClaim();
|
|
33134
33613
|
}
|
|
33135
|
-
function
|
|
33614
|
+
function isProcessAlive3(pid) {
|
|
33136
33615
|
try {
|
|
33137
33616
|
process.kill(pid, 0);
|
|
33138
33617
|
return true;
|
|
@@ -33366,7 +33845,7 @@ var NPM_LSP_TABLE = [
|
|
|
33366
33845
|
];
|
|
33367
33846
|
|
|
33368
33847
|
// src/lsp-project-relevance.ts
|
|
33369
|
-
import { existsSync as existsSync11, readdirSync as
|
|
33848
|
+
import { existsSync as existsSync11, readdirSync as readdirSync4, readFileSync as readFileSync9 } from "node:fs";
|
|
33370
33849
|
import { join as join13 } from "node:path";
|
|
33371
33850
|
var MAX_WALK_DIRS = 200;
|
|
33372
33851
|
var MAX_WALK_DEPTH = 4;
|
|
@@ -33431,7 +33910,7 @@ function relevantExtensionsInProject(projectRoot, extToServer) {
|
|
|
33431
33910
|
visitedDirs += 1;
|
|
33432
33911
|
let entries;
|
|
33433
33912
|
try {
|
|
33434
|
-
entries =
|
|
33913
|
+
entries = readdirSync4(current.dir, { withFileTypes: true });
|
|
33435
33914
|
} catch {
|
|
33436
33915
|
continue;
|
|
33437
33916
|
}
|
|
@@ -33851,7 +34330,7 @@ import {
|
|
|
33851
34330
|
existsSync as existsSync13,
|
|
33852
34331
|
lstatSync as lstatSync2,
|
|
33853
34332
|
mkdirSync as mkdirSync8,
|
|
33854
|
-
readdirSync as
|
|
34333
|
+
readdirSync as readdirSync5,
|
|
33855
34334
|
readFileSync as readFileSync11,
|
|
33856
34335
|
readlinkSync as readlinkSync2,
|
|
33857
34336
|
realpathSync as realpathSync3,
|
|
@@ -34254,7 +34733,7 @@ function validateExtraction(stagingRoot) {
|
|
|
34254
34733
|
const walk = (dir) => {
|
|
34255
34734
|
let entries;
|
|
34256
34735
|
try {
|
|
34257
|
-
entries =
|
|
34736
|
+
entries = readdirSync5(dir);
|
|
34258
34737
|
} catch (err) {
|
|
34259
34738
|
throw new Error(`failed to read staging dir ${dir}: ${err}`);
|
|
34260
34739
|
}
|
|
@@ -35190,6 +35669,15 @@ var EditParams = Type2.Object({
|
|
|
35190
35669
|
description: "Batch edits — non-empty array of { oldString, newString }, { oldString, newString, replaceAll: true }, or { startLine, endLine, content } objects applied atomically."
|
|
35191
35670
|
}))
|
|
35192
35671
|
});
|
|
35672
|
+
var HashlineEditParams = Type2.Object({
|
|
35673
|
+
patch: Type2.String({
|
|
35674
|
+
minLength: 1,
|
|
35675
|
+
description: "Hashline patch text with one or more [path#TAG] sections and PUT/CUT/REM/MV operations"
|
|
35676
|
+
})
|
|
35677
|
+
}, { additionalProperties: false });
|
|
35678
|
+
function stringList(value) {
|
|
35679
|
+
return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
35680
|
+
}
|
|
35193
35681
|
var GrepParams = Type2.Object({
|
|
35194
35682
|
pattern: Type2.String({ description: "Regex pattern to search for" }),
|
|
35195
35683
|
path: Type2.Optional(Type2.String({
|
|
@@ -35354,7 +35842,42 @@ PDFs aren't supported on the Pi harness yet.`, response);
|
|
|
35354
35842
|
}
|
|
35355
35843
|
}));
|
|
35356
35844
|
}
|
|
35357
|
-
if (surface.hoistEdit) {
|
|
35845
|
+
if (surface.hoistEdit && ctx.hashlineEffective === true) {
|
|
35846
|
+
pi.registerTool({
|
|
35847
|
+
name: "edit",
|
|
35848
|
+
label: "edit",
|
|
35849
|
+
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.",
|
|
35850
|
+
promptSnippet: "Apply a tagged hashline patch through the edit tool",
|
|
35851
|
+
promptGuidelines: [
|
|
35852
|
+
"Read a file to obtain its current hashline tag before editing it.",
|
|
35853
|
+
"Pass exactly one patch string; paths and tags belong in patch section headers."
|
|
35854
|
+
],
|
|
35855
|
+
parameters: HashlineEditParams,
|
|
35856
|
+
async execute(_toolCallId, params, _signal, _onUpdate, extCtx) {
|
|
35857
|
+
const bridge = bridgeFor(ctx, extCtx.cwd);
|
|
35858
|
+
const rawArgs = { patch: params.patch };
|
|
35859
|
+
const preflight = await callToolCall(bridge, "hashline_preflight", rawArgs, extCtx);
|
|
35860
|
+
if (preflight.success === false)
|
|
35861
|
+
throw toolErrorFromResponse("edit", preflight);
|
|
35862
|
+
for (const target of [
|
|
35863
|
+
...stringList(preflight.affected_paths),
|
|
35864
|
+
...stringList(preflight.mv_destinations)
|
|
35865
|
+
]) {
|
|
35866
|
+
await assertExternalDirectoryPermission(extCtx, await resolvePathArg(extCtx.cwd, target), {
|
|
35867
|
+
restrictToProjectRoot: surface.restrictToProjectRoot
|
|
35868
|
+
});
|
|
35869
|
+
}
|
|
35870
|
+
const preview = await callToolCall(bridge, "edit", rawArgs, extCtx, { preview: true });
|
|
35871
|
+
if (preview.success === false)
|
|
35872
|
+
throw toolErrorFromResponse("edit", preview);
|
|
35873
|
+
const response = await callToolCall(bridge, "edit", rawArgs, extCtx);
|
|
35874
|
+
if (response.success === false)
|
|
35875
|
+
throw toolErrorFromResponse("edit", response);
|
|
35876
|
+
return buildMutationResult(response);
|
|
35877
|
+
}
|
|
35878
|
+
});
|
|
35879
|
+
}
|
|
35880
|
+
if (surface.hoistEdit && ctx.hashlineEffective !== true) {
|
|
35358
35881
|
pi.registerTool(withPathAliasPreparation({
|
|
35359
35882
|
name: "edit",
|
|
35360
35883
|
label: "edit",
|
|
@@ -38574,7 +39097,7 @@ var PLUGIN_VERSION = (() => {
|
|
|
38574
39097
|
return "0.0.0";
|
|
38575
39098
|
}
|
|
38576
39099
|
})();
|
|
38577
|
-
var ANNOUNCEMENT_VERSION = "0.
|
|
39100
|
+
var ANNOUNCEMENT_VERSION = "0.50.1";
|
|
38578
39101
|
var ANNOUNCEMENT_FEATURES = [
|
|
38579
39102
|
"Callgraph stays consistent under load: edits arriving mid-rebuild can no longer be silently lost from navigation results.",
|
|
38580
39103
|
"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).",
|
|
@@ -38850,6 +39373,16 @@ ${lines}
|
|
|
38850
39373
|
poolOptions,
|
|
38851
39374
|
configOverrides,
|
|
38852
39375
|
subcConnectionFile: config2.subc?.connection_file,
|
|
39376
|
+
onBgEventsNudge: (directory, sessionID) => {
|
|
39377
|
+
handleSubcBgEventsNudge({
|
|
39378
|
+
ctx,
|
|
39379
|
+
directory,
|
|
39380
|
+
sessionID,
|
|
39381
|
+
runtime: pi
|
|
39382
|
+
}).catch((err) => {
|
|
39383
|
+
warn2(`[aft-pi] bg nudge rejected: ${err instanceof Error ? err.message : String(err)}`);
|
|
39384
|
+
});
|
|
39385
|
+
},
|
|
38853
39386
|
onBgEventsNudgeRef: (ref) => {
|
|
38854
39387
|
handleSubcBgEventsNudge({
|
|
38855
39388
|
ctx,
|
|
@@ -38874,8 +39407,16 @@ ${lines}
|
|
|
38874
39407
|
});
|
|
38875
39408
|
}
|
|
38876
39409
|
pool.setConfigureOverride("harness", "pi");
|
|
38877
|
-
|
|
38878
|
-
const
|
|
39410
|
+
const surface = resolvePiToolSurface(config2);
|
|
39411
|
+
const hashlineEditRegistered = config2.edit_mode === "hashline" && surface.hoistEdit;
|
|
39412
|
+
pool.setConfigureOverride("edit_slot_survives", hashlineEditRegistered);
|
|
39413
|
+
pool.setConfigureOverride("aft_search_registered", surface.semantic);
|
|
39414
|
+
const ctx = {
|
|
39415
|
+
pool,
|
|
39416
|
+
config: config2,
|
|
39417
|
+
hashlineEffective: hashlineEditRegistered,
|
|
39418
|
+
storageDir
|
|
39419
|
+
};
|
|
38879
39420
|
if (onnxRuntimePromise) {
|
|
38880
39421
|
onnxRuntimePromise.then((ortDylibDir) => {
|
|
38881
39422
|
if (ortDylibDir) {
|
|
@@ -38913,7 +39454,6 @@ ${lines}
|
|
|
38913
39454
|
if (ANNOUNCEMENT_VERSION && ANNOUNCEMENT_FEATURES.length > 0) {
|
|
38914
39455
|
sendFeatureAnnouncement(ANNOUNCEMENT_VERSION, ANNOUNCEMENT_FEATURES, ANNOUNCEMENT_FOOTER, storageDir);
|
|
38915
39456
|
}
|
|
38916
|
-
const surface = resolvePiToolSurface(config2);
|
|
38917
39457
|
registerPiToolSurface(pi, ctx, surface);
|
|
38918
39458
|
registerWorkflowHints(pi, config2, surface);
|
|
38919
39459
|
registerStatusCommand(pi, ctx);
|