@cortexkit/aft-pi 0.58.0 → 0.58.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/config-error-state.d.ts +8 -0
- package/dist/config-error-state.d.ts.map +1 -1
- package/dist/config-live-reload.d.ts +28 -0
- package/dist/config-live-reload.d.ts.map +1 -0
- package/dist/config.d.ts +90 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/hashline-tag-sources.d.ts +18 -0
- package/dist/hashline-tag-sources.d.ts.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1252 -313
- package/dist/lsp-auto-install.d.ts +30 -0
- package/dist/lsp-auto-install.d.ts.map +1 -1
- package/dist/lsp-github-table.d.ts +4 -3
- package/dist/lsp-github-table.d.ts.map +1 -1
- package/dist/lsp-npm-table.d.ts +11 -1
- package/dist/lsp-npm-table.d.ts.map +1 -1
- package/dist/lsp-registry-probe.d.ts +8 -4
- package/dist/lsp-registry-probe.d.ts.map +1 -1
- package/dist/tool-registration.d.ts +12 -4
- package/dist/tool-registration.d.ts.map +1 -1
- package/dist/tools/_shared.d.ts.map +1 -1
- package/dist/tools/bash.d.ts +19 -4
- package/dist/tools/bash.d.ts.map +1 -1
- package/dist/tools/hoisted.d.ts.map +1 -1
- package/dist/tools/reading.d.ts.map +1 -1
- package/dist/workflow-hints.d.ts +16 -6
- package/dist/workflow-hints.d.ts.map +1 -1
- package/package.json +12 -10
package/dist/index.js
CHANGED
|
@@ -7935,25 +7935,79 @@ function error(message, meta) {
|
|
|
7935
7935
|
function isTerminalStatus(status) {
|
|
7936
7936
|
return status === "completed" || status === "failed" || status === "killed" || status === "timed_out" || status === "fate_unknown";
|
|
7937
7937
|
}
|
|
7938
|
-
function sleep(ms) {
|
|
7939
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
7940
|
-
}
|
|
7941
7938
|
// ../aft-bridge/dist/bash-hints.js
|
|
7942
7939
|
import * as fs from "node:fs";
|
|
7943
7940
|
import * as os from "node:os";
|
|
7944
7941
|
import * as path from "node:path";
|
|
7945
7942
|
var DEFAULT_PRIMARY_WATCH_TIMEOUT_MS = 30000;
|
|
7946
|
-
var
|
|
7943
|
+
var MAX_WATCH_TIMEOUT_MS = 1800000;
|
|
7944
|
+
var LONGEST_TIMER_DELAY_MS = 2147483647;
|
|
7945
|
+
var WATCH_SYNC_DEFAULTS_DESCRIPTION = "Sync waits default to 30s in a main session (max `bash.watch_sync_max_ms`, 120s by default); in a delegated session a sync wait without a timeout waits until the command finishes";
|
|
7946
|
+
var WATCH_TIMEOUT_PARAM_DESCRIPTION = "Sync-only timeout in milliseconds. In a main session: default 30000, max `bash.watch_sync_max_ms` (120000 by default). In a delegated session: omit it to wait until the command finishes; a value you pass is used as given.";
|
|
7947
7947
|
function resolveWatchTimeoutMs(requestedMs, role, capMs) {
|
|
7948
|
-
|
|
7949
|
-
|
|
7948
|
+
if (role === "worker")
|
|
7949
|
+
return requestedMs;
|
|
7950
|
+
return Math.min(requestedMs ?? DEFAULT_PRIMARY_WATCH_TIMEOUT_MS, capMs);
|
|
7951
|
+
}
|
|
7952
|
+
function maxWatchTimeoutMs(role, capMs) {
|
|
7953
|
+
return role === "worker" ? MAX_WATCH_TIMEOUT_MS : capMs;
|
|
7954
|
+
}
|
|
7955
|
+
function watchPollDelayMs(elapsedMs) {
|
|
7956
|
+
if (elapsedMs < 5000)
|
|
7957
|
+
return 100;
|
|
7958
|
+
if (elapsedMs < 30000)
|
|
7959
|
+
return 250;
|
|
7960
|
+
if (elapsedMs < 120000)
|
|
7961
|
+
return 500;
|
|
7962
|
+
return 1000;
|
|
7963
|
+
}
|
|
7964
|
+
var watchClock = {
|
|
7965
|
+
now: () => performance.now(),
|
|
7966
|
+
sleep: abortableSleep
|
|
7967
|
+
};
|
|
7968
|
+
function abortableSleep(ms, signal) {
|
|
7969
|
+
if (signal?.aborted)
|
|
7970
|
+
return Promise.resolve();
|
|
7971
|
+
return new Promise((resolve) => {
|
|
7972
|
+
const done = () => {
|
|
7973
|
+
clearTimeout(timer);
|
|
7974
|
+
signal?.removeEventListener("abort", done);
|
|
7975
|
+
resolve();
|
|
7976
|
+
};
|
|
7977
|
+
const timer = setTimeout(done, ms);
|
|
7978
|
+
signal?.addEventListener("abort", done, { once: true });
|
|
7979
|
+
});
|
|
7950
7980
|
}
|
|
7951
|
-
function
|
|
7981
|
+
function formatWatchWaited(elapsedMs, limitMs, capMs) {
|
|
7982
|
+
let limit;
|
|
7983
|
+
if (limitMs === undefined)
|
|
7984
|
+
limit = "no limit: waits until the command finishes";
|
|
7985
|
+
else if (capMs !== undefined && limitMs >= capMs)
|
|
7986
|
+
limit = `limit ${limitMs}ms, the bash.watch_sync_max_ms cap`;
|
|
7987
|
+
else
|
|
7988
|
+
limit = `limit ${limitMs}ms`;
|
|
7989
|
+
return `Waited ${Math.round(elapsedMs)}ms (${limit})`;
|
|
7990
|
+
}
|
|
7991
|
+
function watchTimeoutSteer(role, timeoutParam = "timeoutMs") {
|
|
7952
7992
|
if (role === "worker") {
|
|
7953
|
-
return `The command is still running; this is not a failure. Watch again
|
|
7993
|
+
return `The command is still running; this is not a failure. Watch again and don't report a result until it finishes; a bash_watch without ${timeoutParam} waits until the command finishes.`;
|
|
7954
7994
|
}
|
|
7955
7995
|
return "The command is still running; this is not a failure. Watch again, do other work, or end your turn: the completion reminder wakes you.";
|
|
7956
7996
|
}
|
|
7997
|
+
var WATCH_UNAVAILABLE_GIVE_UP_MS = 120000;
|
|
7998
|
+
var WORKER_KEEP_WAITING = "Call bash_watch again to keep waiting; don't report a result until the command finishes.";
|
|
7999
|
+
function interruptedWatchTail(role, primaryTail) {
|
|
8000
|
+
return role === "worker" ? `The task is still running. ${WORKER_KEEP_WAITING}` : primaryTail;
|
|
8001
|
+
}
|
|
8002
|
+
function runningTaskStatusHint(role) {
|
|
8003
|
+
return role === "worker" ? "To wait for it, call bash_watch; don't poll." : "A completion reminder will be delivered automatically; don't poll.";
|
|
8004
|
+
}
|
|
8005
|
+
function watchUnavailableSteer(role) {
|
|
8006
|
+
const unknown = "the bridge was busy, so task state is unknown.";
|
|
8007
|
+
if (role === "worker")
|
|
8008
|
+
return `${unknown} ${WORKER_KEEP_WAITING}`;
|
|
8009
|
+
return `${unknown} Do not poll; let the task's completion notification wake the session, or use one bash_status snapshot on the next normal tool call.`;
|
|
8010
|
+
}
|
|
7957
8011
|
var CONFLICT_HINT = `
|
|
7958
8012
|
|
|
7959
8013
|
[Hint] Use aft_conflicts to see all conflict regions across files in a single call.`;
|
|
@@ -8334,7 +8388,7 @@ import { existsSync } from "node:fs";
|
|
|
8334
8388
|
import { join as join3 } from "node:path";
|
|
8335
8389
|
|
|
8336
8390
|
// ../aft-bridge/dist/child-process.js
|
|
8337
|
-
import { execFileSync as nodeExecFileSync, execSync as nodeExecSync, spawn as nodeSpawn, spawnSync as nodeSpawnSync } from "node:child_process";
|
|
8391
|
+
import { execFile as nodeExecFile, execFileSync as nodeExecFileSync, execSync as nodeExecSync, spawn as nodeSpawn, spawnSync as nodeSpawnSync } from "node:child_process";
|
|
8338
8392
|
var HIDDEN = { windowsHide: true };
|
|
8339
8393
|
function withWindowsHidden(args, takesArgv) {
|
|
8340
8394
|
const out = [...args];
|
|
@@ -8356,6 +8410,7 @@ function hidden(resolve, takesArgv) {
|
|
|
8356
8410
|
}
|
|
8357
8411
|
var spawn = hidden(() => nodeSpawn, true);
|
|
8358
8412
|
var spawnSync = hidden(() => nodeSpawnSync, true);
|
|
8413
|
+
var execFile = hidden(() => nodeExecFile, true);
|
|
8359
8414
|
var execFileSync = hidden(() => nodeExecFileSync, true);
|
|
8360
8415
|
var execSync = hidden(() => nodeExecSync, false);
|
|
8361
8416
|
|
|
@@ -8811,6 +8866,9 @@ function isPassiveCommand(command) {
|
|
|
8811
8866
|
return PASSIVE_COMMANDS.has(command);
|
|
8812
8867
|
}
|
|
8813
8868
|
|
|
8869
|
+
// ../aft-bridge/dist/transport.js
|
|
8870
|
+
var WORKER_SESSION_FIELD = "worker_session";
|
|
8871
|
+
|
|
8814
8872
|
// ../aft-bridge/dist/bridge.js
|
|
8815
8873
|
var DEFAULT_BRIDGE_TIMEOUT_MS = 30000;
|
|
8816
8874
|
var BRIDGE_HANG_TIMEOUT_THRESHOLD = 2;
|
|
@@ -9229,9 +9287,11 @@ class BinaryBridge {
|
|
|
9229
9287
|
params.edit_slot_survives = editSlotSurvives;
|
|
9230
9288
|
this.logHashlineRegistrationCarrier("tool_call", sessionId, editSlotSurvives);
|
|
9231
9289
|
}
|
|
9232
|
-
const { preview, ...sendOptions } = options ?? {};
|
|
9290
|
+
const { preview, workerSession, ...sendOptions } = options ?? {};
|
|
9233
9291
|
if (preview === true)
|
|
9234
9292
|
params.preview = true;
|
|
9293
|
+
if (workerSession === true)
|
|
9294
|
+
params[WORKER_SESSION_FIELD] = true;
|
|
9235
9295
|
return await this.send("tool_call", params, Object.keys(sendOptions).length > 0 ? sendOptions : undefined);
|
|
9236
9296
|
}
|
|
9237
9297
|
async sendWithVersionMismatchRetry(command, params, options, canRetryAfterVersionSwap) {
|
|
@@ -9300,7 +9360,7 @@ class BinaryBridge {
|
|
|
9300
9360
|
if (command === "cancel_request" || Object.hasOwn(params, "command") || Object.hasOwn(params, "method")) {
|
|
9301
9361
|
const nested = { ...params };
|
|
9302
9362
|
const reserved = {};
|
|
9303
|
-
for (const key of ["session_id", "lsp_hints"]) {
|
|
9363
|
+
for (const key of ["session_id", "lsp_hints", WORKER_SESSION_FIELD]) {
|
|
9304
9364
|
if (Object.hasOwn(nested, key)) {
|
|
9305
9365
|
reserved[key] = nested[key];
|
|
9306
9366
|
delete nested[key];
|
|
@@ -9682,17 +9742,30 @@ class BinaryBridge {
|
|
|
9682
9742
|
}
|
|
9683
9743
|
}
|
|
9684
9744
|
onStderrData(data) {
|
|
9685
|
-
|
|
9686
|
-
let
|
|
9687
|
-
while (
|
|
9688
|
-
|
|
9689
|
-
|
|
9690
|
-
|
|
9691
|
-
|
|
9692
|
-
|
|
9693
|
-
|
|
9694
|
-
|
|
9695
|
-
|
|
9745
|
+
const partialLimit = 64 * 1024;
|
|
9746
|
+
let offset = 0;
|
|
9747
|
+
while (offset < data.length) {
|
|
9748
|
+
const newline = data.indexOf(`
|
|
9749
|
+
`, offset);
|
|
9750
|
+
const end = newline < 0 ? data.length : newline;
|
|
9751
|
+
while (offset < end) {
|
|
9752
|
+
const count = Math.min(partialLimit - this.stderrBuffer.length, end - offset);
|
|
9753
|
+
this.stderrBuffer += data.slice(offset, offset + count);
|
|
9754
|
+
offset += count;
|
|
9755
|
+
if (this.stderrBuffer.length === partialLimit) {
|
|
9756
|
+
const last = this.stderrBuffer.charCodeAt(this.stderrBuffer.length - 1);
|
|
9757
|
+
const carry = last >= 55296 && last <= 56319 ? this.stderrBuffer.slice(-1) : "";
|
|
9758
|
+
const fragment = carry ? this.stderrBuffer.slice(0, -1) : this.stderrBuffer;
|
|
9759
|
+
const tagged = tagStderrLine(`${fragment} [stderr record truncated into bounded fragments; ${Buffer.byteLength(fragment)} bytes shown, continuation follows]`);
|
|
9760
|
+
this.stderrBuffer = carry;
|
|
9761
|
+
this.logVia(tagged);
|
|
9762
|
+
this.pushStderrLine(tagged);
|
|
9763
|
+
}
|
|
9764
|
+
}
|
|
9765
|
+
if (newline < 0)
|
|
9766
|
+
break;
|
|
9767
|
+
this.flushStderrBuffer();
|
|
9768
|
+
offset = newline + 1;
|
|
9696
9769
|
}
|
|
9697
9770
|
}
|
|
9698
9771
|
flushStderrBuffer() {
|
|
@@ -10070,7 +10143,8 @@ function renderCallersGroupLines(group, theme) {
|
|
|
10070
10143
|
const bySymbolProvenance = new Map;
|
|
10071
10144
|
for (const caller of callers) {
|
|
10072
10145
|
const symbol = asString(caller.symbol) ?? "(unknown)";
|
|
10073
|
-
const
|
|
10146
|
+
const via = asString(caller.via) ?? "";
|
|
10147
|
+
const provenanceKey = asString(caller.resolved_by) === "name_match" ? `${symbol}\x00${via}\x00name_match` : `${symbol}\x00${via}\x00exact`;
|
|
10074
10148
|
const line = asNumber(caller.line);
|
|
10075
10149
|
const bucket = bySymbolProvenance.get(provenanceKey) ?? [];
|
|
10076
10150
|
if (line !== undefined)
|
|
@@ -10079,12 +10153,13 @@ function renderCallersGroupLines(group, theme) {
|
|
|
10079
10153
|
}
|
|
10080
10154
|
const keys = [...bySymbolProvenance.keys()].sort((a, b) => a.localeCompare(b));
|
|
10081
10155
|
for (const key of keys) {
|
|
10082
|
-
const symbol = key.split("\x00")
|
|
10156
|
+
const [symbol = "(unknown)", via = ""] = key.split("\x00");
|
|
10083
10157
|
const isNameMatch = key.endsWith("\x00name_match");
|
|
10084
10158
|
const lineNums = (bySymbolProvenance.get(key) ?? []).sort((a, b) => a - b);
|
|
10085
10159
|
const linePart = lineNums.length > 0 ? lineNums.map(String).join(", ") : "?";
|
|
10086
10160
|
const marker = isNameMatch ? ` ${theme.fg("warning", "~")}` : "";
|
|
10087
|
-
|
|
10161
|
+
const viaPart = via ? ` via ${via}` : "";
|
|
10162
|
+
lines.push(` ↳ ${symbol}:${linePart}${marker}${viaPart}`);
|
|
10088
10163
|
}
|
|
10089
10164
|
return lines;
|
|
10090
10165
|
}
|
|
@@ -10118,6 +10193,9 @@ function formatCallgraphSections(op, response, theme = PLAIN_CALLGRAPH_THEME, op
|
|
|
10118
10193
|
sections.push(renderCallersGroupLines(group, theme).join(`
|
|
10119
10194
|
`));
|
|
10120
10195
|
});
|
|
10196
|
+
const macroNote = asString(asRecord(record.macro_note)?.message);
|
|
10197
|
+
if (macroNote)
|
|
10198
|
+
sections.push(theme.fg("warning", macroNote));
|
|
10121
10199
|
return sections;
|
|
10122
10200
|
}
|
|
10123
10201
|
if (op === "trace_to_symbol") {
|
|
@@ -10331,6 +10409,35 @@ function coerceJsonCollectionParam(value, field) {
|
|
|
10331
10409
|
}
|
|
10332
10410
|
throw new Error(`${field} must be ${shape} (got an invalid type)`);
|
|
10333
10411
|
}
|
|
10412
|
+
function isBlankParam(value) {
|
|
10413
|
+
if (typeof value === "string")
|
|
10414
|
+
return value.trim().length === 0;
|
|
10415
|
+
return isEmptyParam(value);
|
|
10416
|
+
}
|
|
10417
|
+
function usableZoomTargets(targets) {
|
|
10418
|
+
if (isEmptyParam(targets))
|
|
10419
|
+
return [];
|
|
10420
|
+
const entries = Array.isArray(targets) ? targets : [targets];
|
|
10421
|
+
return entries.filter((entry) => {
|
|
10422
|
+
if (entry === null || entry === undefined)
|
|
10423
|
+
return false;
|
|
10424
|
+
if (typeof entry !== "object")
|
|
10425
|
+
return true;
|
|
10426
|
+
const record = entry;
|
|
10427
|
+
return !(isBlankParam(record.path) && isBlankParam(record.filePath) && isBlankParam(record.symbol));
|
|
10428
|
+
});
|
|
10429
|
+
}
|
|
10430
|
+
function isFindReplaceOnlyEdit(args) {
|
|
10431
|
+
const absent = (value) => value === undefined || value === null || value === "";
|
|
10432
|
+
const absentLine = (value) => absent(value) || value === 0;
|
|
10433
|
+
const edits = args.edits;
|
|
10434
|
+
return Array.isArray(edits) && edits.length > 0 && absent(args.appendContent) && absent(args.symbol) && absent(args.content) && edits.every((entry) => {
|
|
10435
|
+
if (entry === null || typeof entry !== "object")
|
|
10436
|
+
return false;
|
|
10437
|
+
const item = entry;
|
|
10438
|
+
return typeof item.oldString === "string" && (absent(item.newString) || typeof item.newString === "string") && absentLine(item.startLine) && absentLine(item.endLine) && absent(item.content);
|
|
10439
|
+
});
|
|
10440
|
+
}
|
|
10334
10441
|
// ../aft-bridge/dist/config-error.js
|
|
10335
10442
|
import { homedir as homedir6 } from "node:os";
|
|
10336
10443
|
import { isAbsolute as isAbsolute2, join as join6 } from "node:path";
|
|
@@ -10438,9 +10545,366 @@ function formatDroppedKeyWarnings(dropped) {
|
|
|
10438
10545
|
}
|
|
10439
10546
|
return dropped.map((item) => `Ignoring ${item.key} from ${item.tier} config (${item.reason})`);
|
|
10440
10547
|
}
|
|
10548
|
+
// ../aft-bridge/dist/config-watch.js
|
|
10549
|
+
import { readFileSync as readFileSync3, statSync as statSync3, watch } from "node:fs";
|
|
10550
|
+
import { basename, dirname } from "node:path";
|
|
10551
|
+
var CONFIG_WATCH_DEBOUNCE_MS = 150;
|
|
10552
|
+
var CONFIG_LIVE_KEEP_NOTE = "AFT keeps using the last valid configuration until the file is fixed.";
|
|
10553
|
+
function pathKey(name) {
|
|
10554
|
+
const segments = name.split(".");
|
|
10555
|
+
return {
|
|
10556
|
+
name,
|
|
10557
|
+
read(config) {
|
|
10558
|
+
let value = config;
|
|
10559
|
+
for (const segment of segments) {
|
|
10560
|
+
if (value === null || typeof value !== "object")
|
|
10561
|
+
return;
|
|
10562
|
+
value = value[segment];
|
|
10563
|
+
}
|
|
10564
|
+
return value;
|
|
10565
|
+
},
|
|
10566
|
+
write(config, value) {
|
|
10567
|
+
const set = (target, index) => {
|
|
10568
|
+
const copy = target !== null && typeof target === "object" && !Array.isArray(target) ? { ...target } : {};
|
|
10569
|
+
const segment = segments[index];
|
|
10570
|
+
if (index === segments.length - 1) {
|
|
10571
|
+
if (value === undefined)
|
|
10572
|
+
delete copy[segment];
|
|
10573
|
+
else
|
|
10574
|
+
copy[segment] = value;
|
|
10575
|
+
} else {
|
|
10576
|
+
copy[segment] = set(copy[segment], index + 1);
|
|
10577
|
+
}
|
|
10578
|
+
return copy;
|
|
10579
|
+
};
|
|
10580
|
+
return set(config, 0);
|
|
10581
|
+
}
|
|
10582
|
+
};
|
|
10583
|
+
}
|
|
10584
|
+
function bashKey(name, resolveBash) {
|
|
10585
|
+
return {
|
|
10586
|
+
name: `bash.${name}`,
|
|
10587
|
+
read: (config) => resolveBash(config)[name],
|
|
10588
|
+
write(config, value) {
|
|
10589
|
+
const resolved = { ...resolveBash(config), [name]: value };
|
|
10590
|
+
for (const key of Object.keys(resolved)) {
|
|
10591
|
+
if (resolved[key] === undefined)
|
|
10592
|
+
delete resolved[key];
|
|
10593
|
+
}
|
|
10594
|
+
return { ...config, bash: resolved };
|
|
10595
|
+
}
|
|
10596
|
+
};
|
|
10597
|
+
}
|
|
10598
|
+
function aftLiveConfigKeys(resolveBash) {
|
|
10599
|
+
return [
|
|
10600
|
+
pathKey("configure_warnings_delivery"),
|
|
10601
|
+
pathKey("restrict_to_project_root"),
|
|
10602
|
+
pathKey("inspect.diagnostics_timeout_ms"),
|
|
10603
|
+
pathKey("inspect.tier2_idle_minutes"),
|
|
10604
|
+
bashKey("foreground_wait_window_ms", resolveBash),
|
|
10605
|
+
bashKey("host_fallback", resolveBash),
|
|
10606
|
+
bashKey("subagent_background", resolveBash),
|
|
10607
|
+
bashKey("watch_sync_max_ms", resolveBash)
|
|
10608
|
+
];
|
|
10609
|
+
}
|
|
10610
|
+
function sameValue(a, b) {
|
|
10611
|
+
return JSON.stringify(a) === JSON.stringify(b);
|
|
10612
|
+
}
|
|
10613
|
+
function flattenLeaves(value, prefix, out) {
|
|
10614
|
+
if (value !== null && typeof value === "object" && !Array.isArray(value)) {
|
|
10615
|
+
const entries = Object.entries(value);
|
|
10616
|
+
if (entries.length === 0 && prefix)
|
|
10617
|
+
out.set(prefix, "{}");
|
|
10618
|
+
for (const [key, child] of entries) {
|
|
10619
|
+
flattenLeaves(child, prefix ? `${prefix}.${key}` : key, out);
|
|
10620
|
+
}
|
|
10621
|
+
return;
|
|
10622
|
+
}
|
|
10623
|
+
if (prefix)
|
|
10624
|
+
out.set(prefix, JSON.stringify(value));
|
|
10625
|
+
}
|
|
10626
|
+
function applyLiveConfigKeys(current, next, keys, baseline = current) {
|
|
10627
|
+
let config = current;
|
|
10628
|
+
const applied = [];
|
|
10629
|
+
for (const key of keys) {
|
|
10630
|
+
const value = key.read(next);
|
|
10631
|
+
if (sameValue(key.read(current), value))
|
|
10632
|
+
continue;
|
|
10633
|
+
config = key.write(config, value);
|
|
10634
|
+
applied.push(key.name);
|
|
10635
|
+
}
|
|
10636
|
+
const before = new Map;
|
|
10637
|
+
const after = new Map;
|
|
10638
|
+
flattenLeaves(baseline, "", before);
|
|
10639
|
+
flattenLeaves(next, "", after);
|
|
10640
|
+
const liveNames = keys.map((key) => key.name);
|
|
10641
|
+
const isLive = (leaf) => liveNames.some((name) => leaf === name || leaf.startsWith(`${name}.`));
|
|
10642
|
+
const deferred = new Set;
|
|
10643
|
+
for (const leaf of new Set([...before.keys(), ...after.keys()])) {
|
|
10644
|
+
if (before.get(leaf) === after.get(leaf) || isLive(leaf))
|
|
10645
|
+
continue;
|
|
10646
|
+
deferred.add(leaf);
|
|
10647
|
+
}
|
|
10648
|
+
return { config, applied, deferred: [...deferred].sort() };
|
|
10649
|
+
}
|
|
10650
|
+
function readTextOrNull(path) {
|
|
10651
|
+
try {
|
|
10652
|
+
return readFileSync3(path, "utf8");
|
|
10653
|
+
} catch {
|
|
10654
|
+
return null;
|
|
10655
|
+
}
|
|
10656
|
+
}
|
|
10657
|
+
var CONFIG_WATCH_REVALIDATE_MS = 2000;
|
|
10658
|
+
var CONFIG_WATCH_REJECTED_RETRIES = 3;
|
|
10659
|
+
var CONFIG_WATCH_MAX_DELAY_MS = 1000;
|
|
10660
|
+
function identityOf(dir) {
|
|
10661
|
+
try {
|
|
10662
|
+
const stat = statSync3(dir, { bigint: true });
|
|
10663
|
+
const base = `${stat.dev}:${stat.ino}`;
|
|
10664
|
+
return process.platform === "win32" ? base : `${base}:${stat.birthtimeNs}`;
|
|
10665
|
+
} catch {
|
|
10666
|
+
return null;
|
|
10667
|
+
}
|
|
10668
|
+
}
|
|
10669
|
+
function watchDirFor(file) {
|
|
10670
|
+
let dir = dirname(file);
|
|
10671
|
+
while (dir !== dirname(dir) && identityOf(dir) === null)
|
|
10672
|
+
dir = dirname(dir);
|
|
10673
|
+
return dir;
|
|
10674
|
+
}
|
|
10675
|
+
function watchAftConfigFiles(options) {
|
|
10676
|
+
const debounceMs = options.debounceMs ?? CONFIG_WATCH_DEBOUNCE_MS;
|
|
10677
|
+
const lastSeen = new Map;
|
|
10678
|
+
for (const path of options.paths) {
|
|
10679
|
+
if (options.initialTexts)
|
|
10680
|
+
lastSeen.set(path, options.initialTexts[path] ?? null);
|
|
10681
|
+
else
|
|
10682
|
+
lastSeen.set(path, options.checkAtStart ? undefined : readTextOrNull(path));
|
|
10683
|
+
}
|
|
10684
|
+
let timer = null;
|
|
10685
|
+
let firstPendingAt = null;
|
|
10686
|
+
let stopped = false;
|
|
10687
|
+
const watchers = new Map;
|
|
10688
|
+
const watchImpl = options.watchImpl ?? watch;
|
|
10689
|
+
let rejectedRetries = 0;
|
|
10690
|
+
const check = () => {
|
|
10691
|
+
timer = null;
|
|
10692
|
+
firstPendingAt = null;
|
|
10693
|
+
if (stopped)
|
|
10694
|
+
return;
|
|
10695
|
+
revalidate();
|
|
10696
|
+
const seen = new Map;
|
|
10697
|
+
for (const path of options.paths) {
|
|
10698
|
+
const text = readTextOrNull(path);
|
|
10699
|
+
if (text !== lastSeen.get(path))
|
|
10700
|
+
seen.set(path, text);
|
|
10701
|
+
}
|
|
10702
|
+
if (seen.size === 0)
|
|
10703
|
+
return;
|
|
10704
|
+
const outcome = options.onChange();
|
|
10705
|
+
if (outcome === false) {
|
|
10706
|
+
if (rejectedRetries < CONFIG_WATCH_REJECTED_RETRIES) {
|
|
10707
|
+
rejectedRetries += 1;
|
|
10708
|
+
retryTimer = setTimeout(check, CONFIG_WATCH_MAX_DELAY_MS);
|
|
10709
|
+
retryTimer.unref?.();
|
|
10710
|
+
}
|
|
10711
|
+
return;
|
|
10712
|
+
}
|
|
10713
|
+
rejectedRetries = 0;
|
|
10714
|
+
if (outcome instanceof Map) {
|
|
10715
|
+
for (const path of options.paths)
|
|
10716
|
+
lastSeen.set(path, outcome.get(path) ?? null);
|
|
10717
|
+
} else {
|
|
10718
|
+
for (const [path, text] of seen)
|
|
10719
|
+
lastSeen.set(path, text);
|
|
10720
|
+
}
|
|
10721
|
+
};
|
|
10722
|
+
let retryTimer = null;
|
|
10723
|
+
const schedule = () => {
|
|
10724
|
+
if (stopped)
|
|
10725
|
+
return;
|
|
10726
|
+
const now = Date.now();
|
|
10727
|
+
firstPendingAt ??= now;
|
|
10728
|
+
if (timer)
|
|
10729
|
+
clearTimeout(timer);
|
|
10730
|
+
const delay = Math.max(0, Math.min(debounceMs, firstPendingAt + CONFIG_WATCH_MAX_DELAY_MS - now));
|
|
10731
|
+
timer = setTimeout(check, delay);
|
|
10732
|
+
timer.unref?.();
|
|
10733
|
+
};
|
|
10734
|
+
function revalidate() {
|
|
10735
|
+
const wanted = new Set(options.paths.map(watchDirFor));
|
|
10736
|
+
let moved = false;
|
|
10737
|
+
for (const [dir, entry] of watchers) {
|
|
10738
|
+
if (!wanted.has(dir) || entry.stale || identityOf(dir) !== entry.identity) {
|
|
10739
|
+
entry.watcher.close();
|
|
10740
|
+
watchers.delete(dir);
|
|
10741
|
+
moved = true;
|
|
10742
|
+
}
|
|
10743
|
+
}
|
|
10744
|
+
for (const dir of wanted) {
|
|
10745
|
+
if (watchers.has(dir))
|
|
10746
|
+
continue;
|
|
10747
|
+
try {
|
|
10748
|
+
const identity = identityOf(dir);
|
|
10749
|
+
options.beforeWatchForTest?.(dir);
|
|
10750
|
+
const name = basename(dir);
|
|
10751
|
+
const watcher = watchImpl(dir, { persistent: false }, (event, filename) => {
|
|
10752
|
+
if (event === "rename" && filename === name) {
|
|
10753
|
+
const current = watchers.get(dir);
|
|
10754
|
+
if (current && current.watcher === watcher)
|
|
10755
|
+
current.stale = true;
|
|
10756
|
+
}
|
|
10757
|
+
schedule();
|
|
10758
|
+
});
|
|
10759
|
+
if (identityOf(dir) !== identity) {
|
|
10760
|
+
watcher.close();
|
|
10761
|
+
moved = true;
|
|
10762
|
+
continue;
|
|
10763
|
+
}
|
|
10764
|
+
const entry = { watcher, identity, stale: false };
|
|
10765
|
+
entry.watcher.on("error", () => {
|
|
10766
|
+
entry.watcher.close();
|
|
10767
|
+
if (watchers.get(dir) === entry)
|
|
10768
|
+
watchers.delete(dir);
|
|
10769
|
+
schedule();
|
|
10770
|
+
});
|
|
10771
|
+
watchers.set(dir, entry);
|
|
10772
|
+
moved = true;
|
|
10773
|
+
} catch {}
|
|
10774
|
+
}
|
|
10775
|
+
return moved;
|
|
10776
|
+
}
|
|
10777
|
+
revalidate();
|
|
10778
|
+
if (options.checkAtStart)
|
|
10779
|
+
check();
|
|
10780
|
+
const revalidateTimer = setInterval(() => {
|
|
10781
|
+
if (!stopped && revalidate())
|
|
10782
|
+
schedule();
|
|
10783
|
+
}, CONFIG_WATCH_REVALIDATE_MS);
|
|
10784
|
+
revalidateTimer.unref?.();
|
|
10785
|
+
return () => {
|
|
10786
|
+
stopped = true;
|
|
10787
|
+
if (timer)
|
|
10788
|
+
clearTimeout(timer);
|
|
10789
|
+
if (retryTimer)
|
|
10790
|
+
clearTimeout(retryTimer);
|
|
10791
|
+
clearInterval(revalidateTimer);
|
|
10792
|
+
for (const entry of watchers.values())
|
|
10793
|
+
entry.watcher.close();
|
|
10794
|
+
watchers.clear();
|
|
10795
|
+
};
|
|
10796
|
+
}
|
|
10797
|
+
function aftLiveSecurityKeys(keys) {
|
|
10798
|
+
const byName = new Map(keys.map((key) => [key.name, key]));
|
|
10799
|
+
const pick = (name) => {
|
|
10800
|
+
const key = byName.get(name);
|
|
10801
|
+
if (!key)
|
|
10802
|
+
throw new Error(`unknown live config key ${name}`);
|
|
10803
|
+
return key;
|
|
10804
|
+
};
|
|
10805
|
+
return [
|
|
10806
|
+
{
|
|
10807
|
+
key: pick("restrict_to_project_root"),
|
|
10808
|
+
loosens: (current, next) => current === true && next !== true
|
|
10809
|
+
},
|
|
10810
|
+
{
|
|
10811
|
+
key: pick("bash.host_fallback"),
|
|
10812
|
+
loosens: (current, next) => current !== true && next === true
|
|
10813
|
+
}
|
|
10814
|
+
];
|
|
10815
|
+
}
|
|
10816
|
+
function liveConfigReloadLogLine(applied, deferred) {
|
|
10817
|
+
let line = "config reload";
|
|
10818
|
+
if (applied.length > 0)
|
|
10819
|
+
line += ` applied=[${applied.join(",")}]`;
|
|
10820
|
+
if (deferred.length > 0) {
|
|
10821
|
+
line += ` deferred=[${deferred.join(",")}] (deferred keys apply on next connect/restart)`;
|
|
10822
|
+
}
|
|
10823
|
+
return line;
|
|
10824
|
+
}
|
|
10825
|
+
function startLiveConfigReload(options) {
|
|
10826
|
+
const baseline = options.getConfig();
|
|
10827
|
+
let lastError = null;
|
|
10828
|
+
let acceptedSources = new Set(options.initialSources);
|
|
10829
|
+
let acceptedProjectText = options.initialProjectText ?? null;
|
|
10830
|
+
let acceptedTexts;
|
|
10831
|
+
const reload = () => {
|
|
10832
|
+
let loaded = options.load();
|
|
10833
|
+
if (loaded.ok) {
|
|
10834
|
+
const read = new Set(loaded.sources);
|
|
10835
|
+
const deleted = [...acceptedSources].find((path) => !read.has(path));
|
|
10836
|
+
if (deleted !== undefined) {
|
|
10837
|
+
loaded = { ok: false, message: `AFT config at ${deleted} was deleted` };
|
|
10838
|
+
} else {
|
|
10839
|
+
acceptedSources = read;
|
|
10840
|
+
}
|
|
10841
|
+
}
|
|
10842
|
+
if (!loaded.ok) {
|
|
10843
|
+
const message = `${loaded.message.trim().replace(/[.!?]?$/, ".")} ${CONFIG_LIVE_KEEP_NOTE}`;
|
|
10844
|
+
if (message !== lastError) {
|
|
10845
|
+
lastError = message;
|
|
10846
|
+
options.reportError(message);
|
|
10847
|
+
}
|
|
10848
|
+
return null;
|
|
10849
|
+
}
|
|
10850
|
+
lastError = null;
|
|
10851
|
+
acceptedTexts = loaded.texts;
|
|
10852
|
+
const current = options.getConfig();
|
|
10853
|
+
let next = loaded.config;
|
|
10854
|
+
const held = [];
|
|
10855
|
+
const projectText = loaded.projectText ?? null;
|
|
10856
|
+
if (loaded.projectText !== undefined && projectText !== acceptedProjectText) {
|
|
10857
|
+
for (const security of options.securityKeys ?? []) {
|
|
10858
|
+
const now = security.key.read(current);
|
|
10859
|
+
if (security.loosens(now, security.key.read(next))) {
|
|
10860
|
+
next = security.key.write(next, now);
|
|
10861
|
+
held.push(security.key.name);
|
|
10862
|
+
}
|
|
10863
|
+
}
|
|
10864
|
+
if (held.length === 0)
|
|
10865
|
+
acceptedProjectText = projectText;
|
|
10866
|
+
}
|
|
10867
|
+
const result = applyLiveConfigKeys(current, next, options.keys, baseline);
|
|
10868
|
+
if (result.applied.length > 0)
|
|
10869
|
+
options.setConfig(result.config);
|
|
10870
|
+
if (result.applied.length > 0 || result.deferred.length > 0 || held.length > 0) {
|
|
10871
|
+
let line = liveConfigReloadLogLine(result.applied, result.deferred);
|
|
10872
|
+
if (held.length > 0) {
|
|
10873
|
+
line += ` held=[${held.join(",")}] (not loosened while the project file differs from the one the published values came from; the next restart applies them)`;
|
|
10874
|
+
}
|
|
10875
|
+
options.log(line);
|
|
10876
|
+
}
|
|
10877
|
+
return { ...result, held };
|
|
10878
|
+
};
|
|
10879
|
+
const safeReload = () => {
|
|
10880
|
+
try {
|
|
10881
|
+
acceptedTexts = undefined;
|
|
10882
|
+
if (reload() === null)
|
|
10883
|
+
return false;
|
|
10884
|
+
const texts = acceptedTexts;
|
|
10885
|
+
if (!texts)
|
|
10886
|
+
return true;
|
|
10887
|
+
return new Map(options.paths.map((path) => [path, texts[path] ?? null]));
|
|
10888
|
+
} catch (err) {
|
|
10889
|
+
options.reportError(`AFT config reload failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
10890
|
+
return false;
|
|
10891
|
+
}
|
|
10892
|
+
};
|
|
10893
|
+
if (options.watch === false) {
|
|
10894
|
+
return { reload, stop: () => {} };
|
|
10895
|
+
}
|
|
10896
|
+
const stop = watchAftConfigFiles({
|
|
10897
|
+
paths: options.paths,
|
|
10898
|
+
debounceMs: options.debounceMs,
|
|
10899
|
+
onChange: safeReload,
|
|
10900
|
+
initialTexts: options.initialTexts,
|
|
10901
|
+
checkAtStart: true
|
|
10902
|
+
});
|
|
10903
|
+
return { reload, stop };
|
|
10904
|
+
}
|
|
10441
10905
|
// ../aft-bridge/dist/downloader.js
|
|
10442
10906
|
import { createHash as createHash2, randomUUID } from "node:crypto";
|
|
10443
|
-
import { chmodSync, closeSync, copyFileSync, createWriteStream, existsSync as existsSync3, mkdirSync, openSync, readdirSync, readFileSync as
|
|
10907
|
+
import { chmodSync, closeSync, copyFileSync, createWriteStream, existsSync as existsSync3, mkdirSync, openSync, readdirSync, readFileSync as readFileSync4, renameSync as renameSync2, rmSync, statSync as statSync4, unlinkSync as unlinkSync2, writeSync } from "node:fs";
|
|
10444
10908
|
import { hostname } from "node:os";
|
|
10445
10909
|
import { join as join7 } from "node:path";
|
|
10446
10910
|
import { Readable } from "node:stream";
|
|
@@ -10792,7 +11256,7 @@ function isReclaimableDownloadLock(owner, ageMs, staleMs) {
|
|
|
10792
11256
|
}
|
|
10793
11257
|
function reclaimDownloadLock(lockPath, expectedOwner) {
|
|
10794
11258
|
try {
|
|
10795
|
-
if (
|
|
11259
|
+
if (readFileSync4(lockPath, "utf-8") !== expectedOwner)
|
|
10796
11260
|
return false;
|
|
10797
11261
|
rmSync(lockPath, { force: true });
|
|
10798
11262
|
return true;
|
|
@@ -10810,7 +11274,7 @@ function sweepStaleDownloadTemps(versionedCacheDir, binaryName) {
|
|
|
10810
11274
|
continue;
|
|
10811
11275
|
}
|
|
10812
11276
|
const tempPath = join7(versionedCacheDir, entry.name);
|
|
10813
|
-
const ageMs = Date.now() -
|
|
11277
|
+
const ageMs = Date.now() - statSync4(tempPath).mtimeMs;
|
|
10814
11278
|
if (Math.abs(ageMs) > DOWNLOAD_LOCK_STALE_MS)
|
|
10815
11279
|
unlinkSync2(tempPath);
|
|
10816
11280
|
}
|
|
@@ -10832,7 +11296,7 @@ async function acquireDownloadLock(lockPath, timing = {}) {
|
|
|
10832
11296
|
}
|
|
10833
11297
|
return () => {
|
|
10834
11298
|
try {
|
|
10835
|
-
if (
|
|
11299
|
+
if (readFileSync4(lockPath, "utf-8") === owner) {
|
|
10836
11300
|
rmSync(lockPath, { force: true });
|
|
10837
11301
|
}
|
|
10838
11302
|
} catch {}
|
|
@@ -10844,8 +11308,8 @@ async function acquireDownloadLock(lockPath, timing = {}) {
|
|
|
10844
11308
|
let existingOwner;
|
|
10845
11309
|
let ageMs;
|
|
10846
11310
|
try {
|
|
10847
|
-
existingOwner =
|
|
10848
|
-
ageMs = Date.now() -
|
|
11311
|
+
existingOwner = readFileSync4(lockPath, "utf-8");
|
|
11312
|
+
ageMs = Date.now() - statSync4(lockPath).mtimeMs;
|
|
10849
11313
|
} catch (readErr) {
|
|
10850
11314
|
if (readErr.code === "ENOENT")
|
|
10851
11315
|
continue;
|
|
@@ -10895,7 +11359,7 @@ async function fetchLatestTag() {
|
|
|
10895
11359
|
}
|
|
10896
11360
|
// ../aft-bridge/dist/durable-log.js
|
|
10897
11361
|
import { appendFile, chmod, mkdir, rename, rm, stat } from "node:fs/promises";
|
|
10898
|
-
import { dirname } from "node:path";
|
|
11362
|
+
import { dirname as dirname2 } from "node:path";
|
|
10899
11363
|
var DEFAULT_LOG_BYTES = 32 * 1024 * 1024;
|
|
10900
11364
|
var DEFAULT_LOG_GENERATIONS = 1;
|
|
10901
11365
|
var LOG_DIR_MODE = 448;
|
|
@@ -10908,6 +11372,10 @@ class RotatingLogSink {
|
|
|
10908
11372
|
queue = Promise.resolve();
|
|
10909
11373
|
disabled = false;
|
|
10910
11374
|
failureReported = false;
|
|
11375
|
+
queuedBytes = 0;
|
|
11376
|
+
queuedRecords = 0;
|
|
11377
|
+
droppedBytes = 0;
|
|
11378
|
+
overflowQueued = false;
|
|
10911
11379
|
constructor(path, options = {}) {
|
|
10912
11380
|
this.path = path;
|
|
10913
11381
|
this.maxBytes = options.maxBytes ?? DEFAULT_LOG_BYTES;
|
|
@@ -10916,7 +11384,34 @@ class RotatingLogSink {
|
|
|
10916
11384
|
append(data) {
|
|
10917
11385
|
if (this.disabled || data.length === 0)
|
|
10918
11386
|
return;
|
|
10919
|
-
|
|
11387
|
+
const bytes = Buffer.byteLength(data);
|
|
11388
|
+
const overflow = this.queuedBytes + bytes > 1024 * 1024 || this.queuedRecords >= 4096;
|
|
11389
|
+
if (overflow) {
|
|
11390
|
+
this.droppedBytes += bytes;
|
|
11391
|
+
if (this.overflowQueued)
|
|
11392
|
+
return;
|
|
11393
|
+
this.overflowQueued = true;
|
|
11394
|
+
data = "";
|
|
11395
|
+
} else {
|
|
11396
|
+
this.queuedBytes += bytes;
|
|
11397
|
+
this.queuedRecords += 1;
|
|
11398
|
+
}
|
|
11399
|
+
this.queue = this.queue.then(async () => {
|
|
11400
|
+
if (overflow) {
|
|
11401
|
+
const dropped = this.droppedBytes;
|
|
11402
|
+
this.droppedBytes = 0;
|
|
11403
|
+
this.overflowQueued = false;
|
|
11404
|
+
await this.write(`[aft-plugin] durable log queue overflow: dropped ${dropped} bytes (1MiB/4096-record pending limit); reduce diagnostic volume.
|
|
11405
|
+
`);
|
|
11406
|
+
} else {
|
|
11407
|
+
try {
|
|
11408
|
+
await this.write(data);
|
|
11409
|
+
} finally {
|
|
11410
|
+
this.queuedBytes -= bytes;
|
|
11411
|
+
this.queuedRecords -= 1;
|
|
11412
|
+
}
|
|
11413
|
+
}
|
|
11414
|
+
}).catch((error) => {
|
|
10920
11415
|
this.disabled = true;
|
|
10921
11416
|
if (!this.failureReported) {
|
|
10922
11417
|
this.failureReported = true;
|
|
@@ -10931,8 +11426,8 @@ class RotatingLogSink {
|
|
|
10931
11426
|
await this.queue;
|
|
10932
11427
|
}
|
|
10933
11428
|
async write(data) {
|
|
10934
|
-
const dir =
|
|
10935
|
-
await mkdir(
|
|
11429
|
+
const dir = dirname2(this.path);
|
|
11430
|
+
await mkdir(dirname2(dir), { recursive: true });
|
|
10936
11431
|
try {
|
|
10937
11432
|
await mkdir(dir, { mode: LOG_DIR_MODE });
|
|
10938
11433
|
} catch (error) {
|
|
@@ -11038,6 +11533,15 @@ function formatEditSummary(data) {
|
|
|
11038
11533
|
let s = `Edited (${detail}).`;
|
|
11039
11534
|
if (data.formatted)
|
|
11040
11535
|
s += formatAutoFormattedSuffix(data);
|
|
11536
|
+
const fuzzy = data.fuzzy_matches ?? (data.fuzzy_match ? [data.fuzzy_match] : []);
|
|
11537
|
+
for (const match of fuzzy) {
|
|
11538
|
+
s += `
|
|
11539
|
+
Fuzzy match (pass ${match.pass}).`;
|
|
11540
|
+
for (const [index, text] of match.replaced_text.entries()) {
|
|
11541
|
+
s += `
|
|
11542
|
+
Replaced text ${index + 1}: ${text}`;
|
|
11543
|
+
}
|
|
11544
|
+
}
|
|
11041
11545
|
return s;
|
|
11042
11546
|
}
|
|
11043
11547
|
function formatAutoFormattedSuffix(data) {
|
|
@@ -12766,7 +13270,7 @@ function reverseRequestMethod(body) {
|
|
|
12766
13270
|
}
|
|
12767
13271
|
}
|
|
12768
13272
|
// ../aft-bridge/dist/subc-transport.js
|
|
12769
|
-
import { existsSync as existsSync4, statSync as
|
|
13273
|
+
import { existsSync as existsSync4, statSync as statSync5 } from "node:fs";
|
|
12770
13274
|
|
|
12771
13275
|
// ../aft-bridge/dist/lifecycle-registry.js
|
|
12772
13276
|
import { lstat } from "node:fs/promises";
|
|
@@ -13238,6 +13742,13 @@ class SubcTransportShuttingDownError extends SubcCallError {
|
|
|
13238
13742
|
this.name = "SubcTransportShuttingDownError";
|
|
13239
13743
|
}
|
|
13240
13744
|
}
|
|
13745
|
+
|
|
13746
|
+
class ReconnectKeyChangedError extends Error {
|
|
13747
|
+
constructor() {
|
|
13748
|
+
super("the subc daemon restarted and its connection key changed; the reconnect did not complete within the deadline, retry");
|
|
13749
|
+
this.name = "ReconnectKeyChangedError";
|
|
13750
|
+
}
|
|
13751
|
+
}
|
|
13241
13752
|
function isSubcClientClosedError(error) {
|
|
13242
13753
|
return error instanceof SubcError && error.message === "client closed";
|
|
13243
13754
|
}
|
|
@@ -13289,6 +13800,7 @@ var RECONNECT_RETRY_FLOOR_MS = 100;
|
|
|
13289
13800
|
var RECONNECT_RETRY_CAP_MS = 2000;
|
|
13290
13801
|
var SUBC_DEFAULT_REQUEST_TIMEOUT_MS = 30000;
|
|
13291
13802
|
var ROUTE_OPEN_RELOAD_WAIT_CEILING_MS = 45000;
|
|
13803
|
+
var CALL_DEADLINE_EXPIRED = Symbol("call deadline expired");
|
|
13292
13804
|
function reloadWaitExhaustedSuffix(callDeadlineMs) {
|
|
13293
13805
|
if (callDeadlineMs > ROUTE_OPEN_RELOAD_WAIT_CEILING_MS) {
|
|
13294
13806
|
const ceilingSeconds = ROUTE_OPEN_RELOAD_WAIT_CEILING_MS / 1000;
|
|
@@ -13641,7 +14153,16 @@ function absentRootError(root) {
|
|
|
13641
14153
|
return new SubcError(`invalid route project root: project root does not exist: ${root}`, "config_divergence");
|
|
13642
14154
|
}
|
|
13643
14155
|
function reloadWindowExhaustedError(error, callDeadlineMs) {
|
|
13644
|
-
|
|
14156
|
+
return withDeadlineSuffix(error, reloadWaitExhaustedSuffix(callDeadlineMs));
|
|
14157
|
+
}
|
|
14158
|
+
function callDeadlinePassedError(lastError, callDeadlineMs) {
|
|
14159
|
+
const seconds = Math.round(callDeadlineMs / 100) / 10;
|
|
14160
|
+
const suffix = ` The AFT daemon module did not return within this call's ${seconds}s deadline.`;
|
|
14161
|
+
if (lastError !== undefined)
|
|
14162
|
+
return withDeadlineSuffix(lastError, suffix);
|
|
14163
|
+
return new SubcError(`route to module '${AFT_MODULE_ID}' was not ready before the call ran out of time.${suffix}`, REQUEST_DEADLINE_CODE);
|
|
14164
|
+
}
|
|
14165
|
+
function withDeadlineSuffix(error, suffix) {
|
|
13645
14166
|
if (error instanceof Error) {
|
|
13646
14167
|
try {
|
|
13647
14168
|
error.message += suffix;
|
|
@@ -13730,6 +14251,8 @@ class SubcTransport {
|
|
|
13730
14251
|
body.edit_slot_survives = editSlotSurvives;
|
|
13731
14252
|
if (preview === true)
|
|
13732
14253
|
body.preview = true;
|
|
14254
|
+
if (options?.workerSession === true)
|
|
14255
|
+
body[WORKER_SESSION_FIELD] = true;
|
|
13733
14256
|
const reply = await this.pool.routeRequest(this.identityFor(sessionId), body, timeoutMs, onProgress, this.generation, abortSignal);
|
|
13734
14257
|
return reliftReply(reply);
|
|
13735
14258
|
}
|
|
@@ -13740,7 +14263,10 @@ class SubcTransport {
|
|
|
13740
14263
|
}
|
|
13741
14264
|
const { timeoutMs, onProgress, abortSignal } = this.splitOptions(options);
|
|
13742
14265
|
const session = typeof params.session_id === "string" ? params.session_id : undefined;
|
|
13743
|
-
const
|
|
14266
|
+
const { [WORKER_SESSION_FIELD]: workerSession, ...args } = params;
|
|
14267
|
+
const body = { name: command, arguments: args };
|
|
14268
|
+
if (workerSession === true)
|
|
14269
|
+
body[WORKER_SESSION_FIELD] = true;
|
|
13744
14270
|
const editSlotSurvives = this.pool.getEditSlotSurvives();
|
|
13745
14271
|
if (editSlotSurvives !== undefined)
|
|
13746
14272
|
body.edit_slot_survives = editSlotSurvives;
|
|
@@ -13777,6 +14303,7 @@ class SubcTransportPool {
|
|
|
13777
14303
|
lifecycleRegistration = null;
|
|
13778
14304
|
outerFacadeEvictor;
|
|
13779
14305
|
client = null;
|
|
14306
|
+
hadWorkingClient = false;
|
|
13780
14307
|
connecting = null;
|
|
13781
14308
|
routeReopenRetryDelayMs = RECONNECT_RETRY_FLOOR_MS;
|
|
13782
14309
|
routeReopenRetry = null;
|
|
@@ -13880,7 +14407,7 @@ class SubcTransportPool {
|
|
|
13880
14407
|
rootCanAttach(root) {
|
|
13881
14408
|
let directoryExists = false;
|
|
13882
14409
|
try {
|
|
13883
|
-
directoryExists =
|
|
14410
|
+
directoryExists = statSync5(root).isDirectory();
|
|
13884
14411
|
} catch {}
|
|
13885
14412
|
if (directoryExists) {
|
|
13886
14413
|
this.dormantRoots.delete(root);
|
|
@@ -14220,6 +14747,11 @@ class SubcTransportPool {
|
|
|
14220
14747
|
return this.rootReapedError(record);
|
|
14221
14748
|
}
|
|
14222
14749
|
async routeRequest(identity, body, timeoutMs, onProgress, expectedGeneration, abortSignal) {
|
|
14750
|
+
const callStartedAt = performance.now();
|
|
14751
|
+
const callDeadlineMs = timeoutMs ?? SUBC_DEFAULT_REQUEST_TIMEOUT_MS;
|
|
14752
|
+
let scheduledDelayMs = 0;
|
|
14753
|
+
const spentMs = () => Math.max(performance.now() - callStartedAt, scheduledDelayMs);
|
|
14754
|
+
const remainingMs = () => callDeadlineMs - spentMs();
|
|
14223
14755
|
const root = asCanonicalRootPath(identity.project_root);
|
|
14224
14756
|
let generation = expectedGeneration;
|
|
14225
14757
|
if (this.lifecycleEnabled()) {
|
|
@@ -14235,7 +14767,49 @@ class SubcTransportPool {
|
|
|
14235
14767
|
try {
|
|
14236
14768
|
let client;
|
|
14237
14769
|
try {
|
|
14238
|
-
|
|
14770
|
+
let keyChanged;
|
|
14771
|
+
while (true) {
|
|
14772
|
+
const remaining = remainingMs();
|
|
14773
|
+
if (remaining <= 0)
|
|
14774
|
+
throw keyChanged ?? callDeadlinePassedError(undefined, callDeadlineMs);
|
|
14775
|
+
let timer;
|
|
14776
|
+
try {
|
|
14777
|
+
const acquired = await Promise.race([
|
|
14778
|
+
this.ensureClient(),
|
|
14779
|
+
new Promise((resolve) => {
|
|
14780
|
+
timer = setTimeout(() => resolve(CALL_DEADLINE_EXPIRED), remaining);
|
|
14781
|
+
})
|
|
14782
|
+
]);
|
|
14783
|
+
if (acquired === CALL_DEADLINE_EXPIRED) {
|
|
14784
|
+
throw keyChanged ?? callDeadlinePassedError(undefined, callDeadlineMs);
|
|
14785
|
+
}
|
|
14786
|
+
client = acquired;
|
|
14787
|
+
break;
|
|
14788
|
+
} catch (error) {
|
|
14789
|
+
if (!(error instanceof ReconnectKeyChangedError))
|
|
14790
|
+
throw error;
|
|
14791
|
+
keyChanged = error;
|
|
14792
|
+
if (spentMs() + this.nextRouteReopenDelayMs() >= callDeadlineMs)
|
|
14793
|
+
throw error;
|
|
14794
|
+
} finally {
|
|
14795
|
+
clearTimeout(timer);
|
|
14796
|
+
}
|
|
14797
|
+
const { delayMs, wait } = this.waitForRouteReopenBackoff();
|
|
14798
|
+
scheduledDelayMs = spentMs() + delayMs;
|
|
14799
|
+
let retryTimer;
|
|
14800
|
+
try {
|
|
14801
|
+
const waited = await Promise.race([
|
|
14802
|
+
wait,
|
|
14803
|
+
new Promise((resolve) => {
|
|
14804
|
+
retryTimer = setTimeout(() => resolve(CALL_DEADLINE_EXPIRED), callDeadlineMs - (performance.now() - callStartedAt));
|
|
14805
|
+
})
|
|
14806
|
+
]);
|
|
14807
|
+
if (waited === CALL_DEADLINE_EXPIRED)
|
|
14808
|
+
throw keyChanged;
|
|
14809
|
+
} finally {
|
|
14810
|
+
clearTimeout(retryTimer);
|
|
14811
|
+
}
|
|
14812
|
+
}
|
|
14239
14813
|
this.assertRecordLive(record);
|
|
14240
14814
|
} catch (error) {
|
|
14241
14815
|
throw this.annotateReapError(error, record);
|
|
@@ -14270,21 +14844,39 @@ class SubcTransportPool {
|
|
|
14270
14844
|
throw error;
|
|
14271
14845
|
}
|
|
14272
14846
|
};
|
|
14273
|
-
const callDeadlineMs = timeoutMs ?? SUBC_DEFAULT_REQUEST_TIMEOUT_MS;
|
|
14274
14847
|
const reloadWaitBudgetMs = Math.min(callDeadlineMs, ROUTE_OPEN_RELOAD_WAIT_CEILING_MS);
|
|
14275
|
-
let
|
|
14848
|
+
let lastRefusal;
|
|
14849
|
+
const openRouteWithinDeadline = async () => {
|
|
14850
|
+
const remaining = remainingMs();
|
|
14851
|
+
if (remaining <= 0)
|
|
14852
|
+
throw callDeadlinePassedError(lastRefusal, callDeadlineMs);
|
|
14853
|
+
let timer;
|
|
14854
|
+
const expired = new Promise((resolve) => {
|
|
14855
|
+
timer = setTimeout(() => resolve(CALL_DEADLINE_EXPIRED), remaining);
|
|
14856
|
+
});
|
|
14857
|
+
try {
|
|
14858
|
+
const opened = await Promise.race([openRoute(), expired]);
|
|
14859
|
+
if (opened === CALL_DEADLINE_EXPIRED) {
|
|
14860
|
+
throw callDeadlinePassedError(lastRefusal, callDeadlineMs);
|
|
14861
|
+
}
|
|
14862
|
+
return opened;
|
|
14863
|
+
} finally {
|
|
14864
|
+
clearTimeout(timer);
|
|
14865
|
+
}
|
|
14866
|
+
};
|
|
14276
14867
|
const openRouteAfterReloadWindow = async () => {
|
|
14277
14868
|
while (true) {
|
|
14278
14869
|
try {
|
|
14279
|
-
return await
|
|
14870
|
+
return await openRouteWithinDeadline();
|
|
14280
14871
|
} catch (error) {
|
|
14281
14872
|
if (!isRouteOpenReloadWindowError(error))
|
|
14282
14873
|
throw error;
|
|
14283
|
-
|
|
14874
|
+
lastRefusal = error;
|
|
14875
|
+
if (spentMs() + this.nextRouteReopenDelayMs() >= reloadWaitBudgetMs) {
|
|
14284
14876
|
throw reloadWindowExhaustedError(error, callDeadlineMs);
|
|
14285
14877
|
}
|
|
14286
14878
|
const { delayMs, wait } = this.waitForRouteReopenBackoff();
|
|
14287
|
-
|
|
14879
|
+
scheduledDelayMs = spentMs() + delayMs;
|
|
14288
14880
|
await wait;
|
|
14289
14881
|
}
|
|
14290
14882
|
}
|
|
@@ -14320,7 +14912,7 @@ class SubcTransportPool {
|
|
|
14320
14912
|
route: routeLabel(entry.handle)
|
|
14321
14913
|
});
|
|
14322
14914
|
};
|
|
14323
|
-
const requestOnRoute = async (route, entry) => {
|
|
14915
|
+
const requestOnRoute = async (route, entry, requestTimeoutMs) => {
|
|
14324
14916
|
this.assertRecordLive(record);
|
|
14325
14917
|
if (abortSignal?.aborted) {
|
|
14326
14918
|
clearRouteEntry(entry);
|
|
@@ -14336,8 +14928,10 @@ class SubcTransportPool {
|
|
|
14336
14928
|
};
|
|
14337
14929
|
abortSignal?.addEventListener("abort", onAbort, { once: true });
|
|
14338
14930
|
try {
|
|
14339
|
-
const
|
|
14340
|
-
|
|
14931
|
+
const request = client.request(route, body, {
|
|
14932
|
+
timeoutMs: Math.ceil(requestTimeoutMs),
|
|
14933
|
+
onProgress
|
|
14934
|
+
});
|
|
14341
14935
|
const reply = abortSignal ? await Promise.race([request, aborted]) : await request;
|
|
14342
14936
|
if (!this.isCurrentSession(key, record)) {
|
|
14343
14937
|
if (this.isReapInduced(record))
|
|
@@ -14358,8 +14952,11 @@ class SubcTransportPool {
|
|
|
14358
14952
|
let reopened = false;
|
|
14359
14953
|
let retriedAbsentRoute = false;
|
|
14360
14954
|
while (true) {
|
|
14955
|
+
const requestTimeoutMs = remainingMs();
|
|
14956
|
+
if (requestTimeoutMs <= 0)
|
|
14957
|
+
throw callDeadlinePassedError(lastRefusal, callDeadlineMs);
|
|
14361
14958
|
try {
|
|
14362
|
-
const reply = await requestOnRoute(routeAndEntry.route, routeAndEntry.entry);
|
|
14959
|
+
const reply = await requestOnRoute(routeAndEntry.route, routeAndEntry.entry, requestTimeoutMs);
|
|
14363
14960
|
if (reopened)
|
|
14364
14961
|
this.resetRouteReopenBackoff();
|
|
14365
14962
|
return reply;
|
|
@@ -14369,11 +14966,12 @@ class SubcTransportPool {
|
|
|
14369
14966
|
const ownsRoute = this.isCurrentSession(key, record) && this.client === client;
|
|
14370
14967
|
if (ownsRoute && isRouteRequestReloadRefusal(error)) {
|
|
14371
14968
|
clearRouteEntry(routeAndEntry.entry);
|
|
14372
|
-
|
|
14969
|
+
lastRefusal = error;
|
|
14970
|
+
if (spentMs() + this.nextRouteReopenDelayMs() >= reloadWaitBudgetMs) {
|
|
14373
14971
|
throw reloadWindowExhaustedError(error, callDeadlineMs);
|
|
14374
14972
|
}
|
|
14375
14973
|
const { delayMs, wait } = this.waitForRouteReopenBackoff();
|
|
14376
|
-
|
|
14974
|
+
scheduledDelayMs = spentMs() + delayMs;
|
|
14377
14975
|
await wait;
|
|
14378
14976
|
routeAndEntry = await openRouteAfterReloadWindow();
|
|
14379
14977
|
reopened = true;
|
|
@@ -14382,7 +14980,12 @@ class SubcTransportPool {
|
|
|
14382
14980
|
if (ownsRoute && !retriedAbsentRoute && isRouteProvenAbsentError(error)) {
|
|
14383
14981
|
retriedAbsentRoute = true;
|
|
14384
14982
|
clearRouteEntry(routeAndEntry.entry);
|
|
14385
|
-
|
|
14983
|
+
if (spentMs() + this.nextRouteReopenDelayMs() >= callDeadlineMs) {
|
|
14984
|
+
throw callDeadlinePassedError(error, callDeadlineMs);
|
|
14985
|
+
}
|
|
14986
|
+
const { delayMs, wait } = this.waitForRouteReopenBackoff();
|
|
14987
|
+
scheduledDelayMs = spentMs() + delayMs;
|
|
14988
|
+
await wait;
|
|
14386
14989
|
routeAndEntry = await openRouteAfterReloadWindow();
|
|
14387
14990
|
reopened = true;
|
|
14388
14991
|
continue;
|
|
@@ -14445,10 +15048,14 @@ class SubcTransportPool {
|
|
|
14445
15048
|
throw new SubcTransportShuttingDownError;
|
|
14446
15049
|
}
|
|
14447
15050
|
this.client = client;
|
|
15051
|
+
this.hadWorkingClient = true;
|
|
14448
15052
|
this.transportFailures = 0;
|
|
14449
15053
|
return client;
|
|
14450
15054
|
}).catch((error) => {
|
|
14451
15055
|
this.connecting = null;
|
|
15056
|
+
if (this.hadWorkingClient && error instanceof AuthError) {
|
|
15057
|
+
throw new ReconnectKeyChangedError;
|
|
15058
|
+
}
|
|
14452
15059
|
throw error;
|
|
14453
15060
|
});
|
|
14454
15061
|
return this.connecting;
|
|
@@ -16060,14 +16667,14 @@ function shouldInterruptWaitsForMessage(detachOnUserMessage, operatorText) {
|
|
|
16060
16667
|
return detachOnUserMessage || containsStandaloneDetachKeyword(operatorText);
|
|
16061
16668
|
}
|
|
16062
16669
|
// ../aft-bridge/dist/migration.js
|
|
16063
|
-
import { closeSync as closeSync3, existsSync as existsSync8, mkdirSync as mkdirSync3, openSync as openSync3, readFileSync as
|
|
16670
|
+
import { closeSync as closeSync3, existsSync as existsSync8, mkdirSync as mkdirSync3, openSync as openSync3, readFileSync as readFileSync7, renameSync as renameSync4, rmSync as rmSync2, statSync as statSync6, unlinkSync as unlinkSync3, writeFileSync as writeFileSync3 } from "node:fs";
|
|
16064
16671
|
import { homedir as homedir10, tmpdir as tmpdir2 } from "node:os";
|
|
16065
|
-
import { basename, dirname as
|
|
16672
|
+
import { basename as basename2, dirname as dirname5, join as join10, resolve as resolve6 } from "node:path";
|
|
16066
16673
|
|
|
16067
16674
|
// ../aft-bridge/dist/paths.js
|
|
16068
|
-
import { existsSync as existsSync6, mkdirSync as mkdirSync2, readFileSync as
|
|
16675
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync2, readFileSync as readFileSync5, renameSync as renameSync3, writeFileSync as writeFileSync2 } from "node:fs";
|
|
16069
16676
|
import { homedir as homedir8 } from "node:os";
|
|
16070
|
-
import { dirname as
|
|
16677
|
+
import { dirname as dirname3, isAbsolute as isAbsolute3, join as join8, resolve as resolve5 } from "node:path";
|
|
16071
16678
|
function homeDir3() {
|
|
16072
16679
|
if (process.platform === "win32")
|
|
16073
16680
|
return process.env.USERPROFILE || process.env.HOME || homedir8();
|
|
@@ -16127,7 +16734,7 @@ function repairRootScopedStorageFile(storageRoot, harness, fileName) {
|
|
|
16127
16734
|
if (existsSync6(harnessPath) || !existsSync6(rootPath))
|
|
16128
16735
|
return harnessPath;
|
|
16129
16736
|
try {
|
|
16130
|
-
mkdirSync2(
|
|
16737
|
+
mkdirSync2(dirname3(harnessPath), { recursive: true });
|
|
16131
16738
|
renameSync3(rootPath, harnessPath);
|
|
16132
16739
|
} catch {}
|
|
16133
16740
|
return harnessPath;
|
|
@@ -16139,7 +16746,7 @@ function shouldShowAnnouncement(storageRoot, harness, currentVersion) {
|
|
|
16139
16746
|
let lastVersion = "";
|
|
16140
16747
|
try {
|
|
16141
16748
|
if (existsSync6(versionFile)) {
|
|
16142
|
-
lastVersion =
|
|
16749
|
+
lastVersion = readFileSync5(versionFile, "utf-8").trim();
|
|
16143
16750
|
}
|
|
16144
16751
|
} catch {
|
|
16145
16752
|
return false;
|
|
@@ -16148,7 +16755,7 @@ function shouldShowAnnouncement(storageRoot, harness, currentVersion) {
|
|
|
16148
16755
|
return false;
|
|
16149
16756
|
if (!lastVersion) {
|
|
16150
16757
|
try {
|
|
16151
|
-
mkdirSync2(
|
|
16758
|
+
mkdirSync2(dirname3(versionFile), { recursive: true });
|
|
16152
16759
|
writeFileSync2(versionFile, currentVersion);
|
|
16153
16760
|
} catch {}
|
|
16154
16761
|
return false;
|
|
@@ -16160,7 +16767,7 @@ function markAnnouncementSeen(storageRoot, harness, currentVersion) {
|
|
|
16160
16767
|
return;
|
|
16161
16768
|
const versionFile = repairRootScopedStorageFile(storageRoot, harness, "last_announced_version");
|
|
16162
16769
|
try {
|
|
16163
|
-
mkdirSync2(
|
|
16770
|
+
mkdirSync2(dirname3(versionFile), { recursive: true });
|
|
16164
16771
|
writeFileSync2(versionFile, currentVersion);
|
|
16165
16772
|
} catch {}
|
|
16166
16773
|
}
|
|
@@ -16209,10 +16816,10 @@ function decodeFileUrl(target) {
|
|
|
16209
16816
|
}
|
|
16210
16817
|
|
|
16211
16818
|
// ../aft-bridge/dist/resolver.js
|
|
16212
|
-
import { closeSync as closeSync2, existsSync as existsSync7, promises as fsPromises, openSync as openSync2, readFileSync as
|
|
16819
|
+
import { closeSync as closeSync2, existsSync as existsSync7, promises as fsPromises, openSync as openSync2, readFileSync as readFileSync6, readSync } from "node:fs";
|
|
16213
16820
|
import { createRequire } from "node:module";
|
|
16214
16821
|
import { homedir as homedir9 } from "node:os";
|
|
16215
|
-
import { delimiter, dirname as
|
|
16822
|
+
import { delimiter, dirname as dirname4, isAbsolute as isAbsolute4, join as join9 } from "node:path";
|
|
16216
16823
|
var ensureBinaryForResolver = ensureBinary;
|
|
16217
16824
|
var npmPlatformPackageReader = null;
|
|
16218
16825
|
var cacheCopiesInFlight = new Map;
|
|
@@ -16365,8 +16972,8 @@ function npmPlatformPackage(ext) {
|
|
|
16365
16972
|
try {
|
|
16366
16973
|
const req = createRequire(import.meta.url);
|
|
16367
16974
|
const manifestPath = req.resolve(`@cortexkit/aft-${platformKey()}/package.json`);
|
|
16368
|
-
const manifest = JSON.parse(
|
|
16369
|
-
const binaryPath = join9(
|
|
16975
|
+
const manifest = JSON.parse(readFileSync6(manifestPath, "utf8"));
|
|
16976
|
+
const binaryPath = join9(dirname4(manifestPath), "bin", `aft${ext}`);
|
|
16370
16977
|
if (typeof manifest.version !== "string" || !existsSync7(binaryPath))
|
|
16371
16978
|
return null;
|
|
16372
16979
|
return { binaryPath, version: normalizeBareVersion(manifest.version) };
|
|
@@ -16620,7 +17227,7 @@ function acquireConfigMigrationLock(lockDir) {
|
|
|
16620
17227
|
if (code !== "EEXIST")
|
|
16621
17228
|
throw err;
|
|
16622
17229
|
try {
|
|
16623
|
-
const ageMs = Date.now() -
|
|
17230
|
+
const ageMs = Date.now() - statSync6(lockDir).mtimeMs;
|
|
16624
17231
|
if (ageMs > 60000) {
|
|
16625
17232
|
rmSync2(lockDir, { recursive: true, force: true });
|
|
16626
17233
|
continue;
|
|
@@ -16634,12 +17241,12 @@ function acquireConfigMigrationLock(lockDir) {
|
|
|
16634
17241
|
}
|
|
16635
17242
|
}
|
|
16636
17243
|
function atomicCopyConfigFile(sourcePath, targetPath) {
|
|
16637
|
-
mkdirSync3(
|
|
16638
|
-
const tmpPath = join10(
|
|
17244
|
+
mkdirSync3(dirname5(targetPath), { recursive: true });
|
|
17245
|
+
const tmpPath = join10(dirname5(targetPath), `.${basename2(targetPath)}.${process.pid}.${Date.now()}.${Math.random().toString(16).slice(2)}.tmp`);
|
|
16639
17246
|
let fd = null;
|
|
16640
17247
|
try {
|
|
16641
17248
|
fd = openSync3(tmpPath, "wx", 384);
|
|
16642
|
-
writeFileSync3(fd,
|
|
17249
|
+
writeFileSync3(fd, readFileSync7(sourcePath));
|
|
16643
17250
|
closeSync3(fd);
|
|
16644
17251
|
fd = null;
|
|
16645
17252
|
renameSync4(tmpPath, targetPath);
|
|
@@ -16656,8 +17263,8 @@ function atomicCopyConfigFile(sourcePath, targetPath) {
|
|
|
16656
17263
|
}
|
|
16657
17264
|
}
|
|
16658
17265
|
function atomicWriteConfigFile(targetPath, content) {
|
|
16659
|
-
mkdirSync3(
|
|
16660
|
-
const tmpPath = join10(
|
|
17266
|
+
mkdirSync3(dirname5(targetPath), { recursive: true });
|
|
17267
|
+
const tmpPath = join10(dirname5(targetPath), `.${basename2(targetPath)}.${process.pid}.${Date.now()}.${Math.random().toString(16).slice(2)}.tmp`);
|
|
16661
17268
|
let fd = null;
|
|
16662
17269
|
try {
|
|
16663
17270
|
fd = openSync3(tmpPath, "wx", 384);
|
|
@@ -16718,11 +17325,11 @@ function markLegacySourcesMovedAside(sources, targetPath, logger) {
|
|
|
16718
17325
|
const desiredMarkerPath = `${source.path}${MOVED_MARKER_SUFFIX}`;
|
|
16719
17326
|
const markerPath = nonClobberingSidecarPath(desiredMarkerPath);
|
|
16720
17327
|
try {
|
|
16721
|
-
const original =
|
|
17328
|
+
const original = readFileSync7(source.path, "utf-8");
|
|
16722
17329
|
if (markerPath !== desiredMarkerPath) {
|
|
16723
17330
|
logger?.warn?.(`Preserving existing legacy AFT marker ${desiredMarkerPath}; writing new marker to ${markerPath}`);
|
|
16724
17331
|
}
|
|
16725
|
-
atomicWriteConfigFile(markerPath, movedMarkerContent(targetPath,
|
|
17332
|
+
atomicWriteConfigFile(markerPath, movedMarkerContent(targetPath, basename2(source.path), original));
|
|
16726
17333
|
unlinkSync3(source.path);
|
|
16727
17334
|
info?.(`Moved legacy AFT config ${source.path} aside to ${markerPath}; AFT now reads ${targetPath}`);
|
|
16728
17335
|
} catch (err) {
|
|
@@ -16782,15 +17389,15 @@ function migrateAftConfigFile(opts) {
|
|
|
16782
17389
|
if (existingSources.length === 0) {
|
|
16783
17390
|
return { migrated: false, conflict: false, targetPath: opts.targetPath, warnings };
|
|
16784
17391
|
}
|
|
16785
|
-
mkdirSync3(
|
|
17392
|
+
mkdirSync3(dirname5(opts.targetPath), { recursive: true });
|
|
16786
17393
|
const release = acquireConfigMigrationLock(`${opts.targetPath}.lock`);
|
|
16787
17394
|
try {
|
|
16788
17395
|
const sources = existingSources.map((source) => ({
|
|
16789
17396
|
...source,
|
|
16790
|
-
content:
|
|
17397
|
+
content: readFileSync7(source.path, "utf-8")
|
|
16791
17398
|
}));
|
|
16792
17399
|
if (existsSync8(opts.targetPath)) {
|
|
16793
|
-
const targetContent =
|
|
17400
|
+
const targetContent = readFileSync7(opts.targetPath, "utf-8");
|
|
16794
17401
|
for (const source of sources) {
|
|
16795
17402
|
if (fileSemanticsMatch(source.content, targetContent))
|
|
16796
17403
|
continue;
|
|
@@ -16841,11 +17448,11 @@ function spawnErrorLabel(error) {
|
|
|
16841
17448
|
function migrationLogPath(newRoot, harness, logger) {
|
|
16842
17449
|
const desired = join10(newRoot, "logs", "migration", `${harness}-${Date.now()}.jsonl`);
|
|
16843
17450
|
try {
|
|
16844
|
-
mkdirSync3(
|
|
17451
|
+
mkdirSync3(dirname5(desired), { recursive: true });
|
|
16845
17452
|
return desired;
|
|
16846
17453
|
} catch (err) {
|
|
16847
17454
|
const fallback = join10(tmpdir2(), `aft-migration-${harness}-${Date.now()}.jsonl`);
|
|
16848
|
-
logger?.warn?.(`Failed to create AFT migration log directory ${
|
|
17455
|
+
logger?.warn?.(`Failed to create AFT migration log directory ${dirname5(desired)}: ${err instanceof Error ? err.message : String(err)}. ` + `Using fallback log path ${fallback}.`);
|
|
16849
17456
|
return fallback;
|
|
16850
17457
|
}
|
|
16851
17458
|
}
|
|
@@ -16906,9 +17513,9 @@ async function ensureStorageMigrated(opts) {
|
|
|
16906
17513
|
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}` : ""));
|
|
16907
17514
|
}
|
|
16908
17515
|
// ../aft-bridge/dist/migration-notices.js
|
|
16909
|
-
import { mkdirSync as mkdirSync4, readFileSync as
|
|
17516
|
+
import { mkdirSync as mkdirSync4, readFileSync as readFileSync8, renameSync as renameSync5, rmdirSync, statSync as statSync7, writeFileSync as writeFileSync4 } from "node:fs";
|
|
16910
17517
|
import { homedir as homedir11 } from "node:os";
|
|
16911
|
-
import { dirname as
|
|
17518
|
+
import { dirname as dirname6, join as join11, resolve as resolve7 } from "node:path";
|
|
16912
17519
|
function migrationNoticeStorePath(env = process.env) {
|
|
16913
17520
|
const stateHome = env.XDG_STATE_HOME && env.XDG_STATE_HOME.length > 0 ? env.XDG_STATE_HOME : join11(homedir11(), ".local", "state");
|
|
16914
17521
|
return join11(stateHome, "cortexkit", "aft", "migration-notices.json");
|
|
@@ -16918,7 +17525,7 @@ function noticeKey(configPath, digest) {
|
|
|
16918
17525
|
}
|
|
16919
17526
|
function readRecords(storePath) {
|
|
16920
17527
|
try {
|
|
16921
|
-
const parsed = JSON.parse(
|
|
17528
|
+
const parsed = JSON.parse(readFileSync8(storePath, "utf8"));
|
|
16922
17529
|
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
|
|
16923
17530
|
} catch {
|
|
16924
17531
|
return {};
|
|
@@ -16927,7 +17534,7 @@ function readRecords(storePath) {
|
|
|
16927
17534
|
var LOCK_STALE_MS = 1e4;
|
|
16928
17535
|
function withStoreLock(storePath, action) {
|
|
16929
17536
|
const lockPath = `${storePath}.lock`;
|
|
16930
|
-
mkdirSync4(
|
|
17537
|
+
mkdirSync4(dirname6(storePath), { recursive: true });
|
|
16931
17538
|
const deadline = Date.now() + 2000;
|
|
16932
17539
|
for (;; ) {
|
|
16933
17540
|
try {
|
|
@@ -16937,7 +17544,7 @@ function withStoreLock(storePath, action) {
|
|
|
16937
17544
|
if (error.code !== "EEXIST")
|
|
16938
17545
|
throw error;
|
|
16939
17546
|
try {
|
|
16940
|
-
if (Date.now() -
|
|
17547
|
+
if (Date.now() - statSync7(lockPath).mtimeMs > LOCK_STALE_MS)
|
|
16941
17548
|
rmdirSync(lockPath);
|
|
16942
17549
|
} catch {}
|
|
16943
17550
|
if (Date.now() > deadline)
|
|
@@ -16978,9 +17585,9 @@ function deliverMigrationNoticeOnce(options) {
|
|
|
16978
17585
|
}
|
|
16979
17586
|
}
|
|
16980
17587
|
// ../aft-bridge/dist/npm-resolver.js
|
|
16981
|
-
import { readdirSync as readdirSync2, statSync as
|
|
17588
|
+
import { readdirSync as readdirSync2, statSync as statSync8 } from "node:fs";
|
|
16982
17589
|
import { homedir as homedir12 } from "node:os";
|
|
16983
|
-
import { dirname as
|
|
17590
|
+
import { dirname as dirname7, isAbsolute as isAbsolute5, join as join12 } from "node:path";
|
|
16984
17591
|
function defaultDeps() {
|
|
16985
17592
|
return {
|
|
16986
17593
|
platform: process.platform,
|
|
@@ -16994,7 +17601,7 @@ function npmBinaryName(platform) {
|
|
|
16994
17601
|
}
|
|
16995
17602
|
function isFile(p) {
|
|
16996
17603
|
try {
|
|
16997
|
-
return
|
|
17604
|
+
return statSync8(p).isFile();
|
|
16998
17605
|
} catch {
|
|
16999
17606
|
return false;
|
|
17000
17607
|
}
|
|
@@ -17015,7 +17622,7 @@ function npmFromPath(deps) {
|
|
|
17015
17622
|
return null;
|
|
17016
17623
|
}
|
|
17017
17624
|
function npmAdjacentToNode(deps) {
|
|
17018
|
-
const dir =
|
|
17625
|
+
const dir = dirname7(deps.execPath);
|
|
17019
17626
|
return isFile(join12(dir, npmBinaryName(deps.platform))) ? dir : null;
|
|
17020
17627
|
}
|
|
17021
17628
|
function highestVersionedNodeBin(installsDir, name) {
|
|
@@ -17400,8 +18007,8 @@ function probeOnnxRuntimeLoadable(libPath, arch = process.arch) {
|
|
|
17400
18007
|
}
|
|
17401
18008
|
// ../aft-bridge/dist/onnx-runtime.js
|
|
17402
18009
|
import { createHash as createHash4, randomBytes as randomBytes2 } from "node:crypto";
|
|
17403
|
-
import { chmodSync as chmodSync2, closeSync as closeSync5, copyFileSync as copyFileSync2, createWriteStream as createWriteStream2, existsSync as existsSync11, lstatSync, mkdirSync as mkdirSync5, openSync as openSync5, readdirSync as readdirSync3, readFileSync as
|
|
17404
|
-
import { basename as
|
|
18010
|
+
import { chmodSync as chmodSync2, closeSync as closeSync5, copyFileSync as copyFileSync2, createWriteStream as createWriteStream2, existsSync as existsSync11, lstatSync, mkdirSync as mkdirSync5, openSync as openSync5, readdirSync as readdirSync3, readFileSync as readFileSync9, readlinkSync, realpathSync as realpathSync3, renameSync as renameSync6, rmSync as rmSync3, statSync as statSync9, symlinkSync, unlinkSync as unlinkSync4, writeFileSync as writeFileSync5 } from "node:fs";
|
|
18011
|
+
import { basename as basename3, dirname as dirname8, isAbsolute as isAbsolute6, join as join14, relative as relative2, resolve as resolve8, win32 } from "node:path";
|
|
17405
18012
|
import { Readable as Readable2 } from "node:stream";
|
|
17406
18013
|
import { pipeline as pipeline2 } from "node:stream/promises";
|
|
17407
18014
|
|
|
@@ -17441,31 +18048,36 @@ var ORT_PLATFORM_MAP = {
|
|
|
17441
18048
|
arm64: {
|
|
17442
18049
|
assetName: `onnxruntime-osx-arm64-${ORT_VERSION}`,
|
|
17443
18050
|
libName: "libonnxruntime.dylib",
|
|
17444
|
-
archiveType: "tgz"
|
|
18051
|
+
archiveType: "tgz",
|
|
18052
|
+
librarySha256: "872533f130f1839a5bc01788ddb4f75c83a189763441ba1178788ed965449289"
|
|
17445
18053
|
}
|
|
17446
18054
|
},
|
|
17447
18055
|
linux: {
|
|
17448
18056
|
x64: {
|
|
17449
18057
|
assetName: `onnxruntime-linux-x64-${ORT_VERSION}`,
|
|
17450
18058
|
libName: "libonnxruntime.so",
|
|
17451
|
-
archiveType: "tgz"
|
|
18059
|
+
archiveType: "tgz",
|
|
18060
|
+
librarySha256: "d132535d051344ff5c64c9c200004150559049a81ed330eb4422c1962fb6b7e4"
|
|
17452
18061
|
},
|
|
17453
18062
|
arm64: {
|
|
17454
18063
|
assetName: `onnxruntime-linux-aarch64-${ORT_VERSION}`,
|
|
17455
18064
|
libName: "libonnxruntime.so",
|
|
17456
|
-
archiveType: "tgz"
|
|
18065
|
+
archiveType: "tgz",
|
|
18066
|
+
librarySha256: "52b2a0e75e79468404284fec38ad5ee1a7a996232274f5e1b84f3e793fb07554"
|
|
17457
18067
|
}
|
|
17458
18068
|
},
|
|
17459
18069
|
win32: {
|
|
17460
18070
|
x64: {
|
|
17461
18071
|
assetName: `onnxruntime-win-x64-${ORT_VERSION}`,
|
|
17462
18072
|
libName: "onnxruntime.dll",
|
|
17463
|
-
archiveType: "zip"
|
|
18073
|
+
archiveType: "zip",
|
|
18074
|
+
librarySha256: "b95efb2113b603bbbf3f191061c5516a871ed546893c820e4f3b7b6c358dbf2a"
|
|
17464
18075
|
},
|
|
17465
18076
|
arm64: {
|
|
17466
18077
|
assetName: `onnxruntime-win-arm64-${ORT_VERSION}`,
|
|
17467
18078
|
libName: "onnxruntime.dll",
|
|
17468
|
-
archiveType: "zip"
|
|
18079
|
+
archiveType: "zip",
|
|
18080
|
+
librarySha256: "724d81ac50b11bfaa01ab3ce01b99fb6734a4b762259a8be4395ca662ce99fe6"
|
|
17469
18081
|
}
|
|
17470
18082
|
}
|
|
17471
18083
|
};
|
|
@@ -17605,7 +18217,7 @@ function cleanupAbandonedStagingDirs(onnxBaseDir) {
|
|
|
17605
18217
|
abandoned = true;
|
|
17606
18218
|
} else {
|
|
17607
18219
|
try {
|
|
17608
|
-
const ageMs = Date.now() -
|
|
18220
|
+
const ageMs = Date.now() - statSync9(stagingDir).mtimeMs;
|
|
17609
18221
|
abandoned = ageMs > STALE_LOCK_MS;
|
|
17610
18222
|
} catch {
|
|
17611
18223
|
abandoned = true;
|
|
@@ -17642,7 +18254,7 @@ var REQUIRED_ORT_MAJOR = 1;
|
|
|
17642
18254
|
var REQUIRED_ORT_MIN_MINOR = 20;
|
|
17643
18255
|
var INVALID_ORT_VERSION = "<invalid>";
|
|
17644
18256
|
function parseOnnxVersionFromPath(value) {
|
|
17645
|
-
const name =
|
|
18257
|
+
const name = basename3(value);
|
|
17646
18258
|
const semverish = name.match(/(?:^|[._-])(\d+\.\d+\.\d+(?:[-+][A-Za-z0-9.-]+)?)(?:\.(?:dylib|dll))?$/);
|
|
17647
18259
|
if (semverish)
|
|
17648
18260
|
return semverish[1].split(/[-+]/, 1)[0];
|
|
@@ -17847,7 +18459,7 @@ async function downloadFileWithCap(url, destPath) {
|
|
|
17847
18459
|
if (Number.isFinite(advertised) && advertised > MAX_DOWNLOAD_BYTES2) {
|
|
17848
18460
|
throw new Error(`Content-Length ${advertised} exceeds max ${MAX_DOWNLOAD_BYTES2}`);
|
|
17849
18461
|
}
|
|
17850
|
-
mkdirSync5(
|
|
18462
|
+
mkdirSync5(dirname8(destPath), { recursive: true });
|
|
17851
18463
|
let bytesWritten = 0;
|
|
17852
18464
|
const guard = new TransformStream({
|
|
17853
18465
|
transform(chunk, transformController) {
|
|
@@ -17881,7 +18493,7 @@ function validateExtractedTree(stagingRoot) {
|
|
|
17881
18493
|
const lst = lstatSync(fullPath);
|
|
17882
18494
|
if (lst.isSymbolicLink()) {
|
|
17883
18495
|
const linkTarget = readlinkSync(fullPath);
|
|
17884
|
-
const resolvedTarget = resolve8(
|
|
18496
|
+
const resolvedTarget = resolve8(dirname8(fullPath), linkTarget);
|
|
17885
18497
|
const rel = relative2(realRoot, resolvedTarget);
|
|
17886
18498
|
if (rel.startsWith("..") || isAbsolute6(rel) || win32.isAbsolute(rel)) {
|
|
17887
18499
|
throw new Error(`extracted symlink ${fullPath} points outside staging root: ${linkTarget}`);
|
|
@@ -17952,6 +18564,9 @@ async function downloadOnnxRuntime(info, targetDir) {
|
|
|
17952
18564
|
copyOnnxLibraries(info, extractedDir, stagedInstallDir, realFiles, symlinks);
|
|
17953
18565
|
const libPath = join14(stagedInstallDir, info.libName);
|
|
17954
18566
|
const libHash = sha256File2(libPath);
|
|
18567
|
+
if (libHash !== info.librarySha256) {
|
|
18568
|
+
throw new Error(`onnx runtime library hash mismatch at ${libPath}: sha256 ${libHash}, ` + `expected the pinned ${info.librarySha256} for ${info.assetName}`);
|
|
18569
|
+
}
|
|
17955
18570
|
writeOnnxInstalledMeta(stagedInstallDir, ORT_VERSION, libHash, archiveSha256);
|
|
17956
18571
|
publishOnnxRuntime(stagedInstallDir, targetDir);
|
|
17957
18572
|
rmSync3(tmpDir, { recursive: true, force: true });
|
|
@@ -18017,7 +18632,7 @@ function copyOnnxLibraries(info, extractedDir, targetDir, realFiles, symlinks, c
|
|
|
18017
18632
|
unlinkSync4(dst);
|
|
18018
18633
|
} catch {}
|
|
18019
18634
|
const dstForContainment = join14(targetRoot, link.name);
|
|
18020
|
-
const resolvedTarget = resolve8(
|
|
18635
|
+
const resolvedTarget = resolve8(dirname8(dstForContainment), link.target);
|
|
18021
18636
|
if (!isPathInsideRoot(targetRoot, resolvedTarget)) {
|
|
18022
18637
|
const message = `ONNX Runtime symlink ${link.name} points outside install dir: ${link.target}`;
|
|
18023
18638
|
if (requiredLibs.has(link.name)) {
|
|
@@ -18069,9 +18684,9 @@ function writeOnnxInstalledMeta(installDir, version, sha256, archiveSha256) {
|
|
|
18069
18684
|
function readOnnxInstalledMeta(installDir) {
|
|
18070
18685
|
const path = join14(installDir, ONNX_INSTALLED_META_FILE);
|
|
18071
18686
|
try {
|
|
18072
|
-
if (!
|
|
18687
|
+
if (!statSync9(path).isFile())
|
|
18073
18688
|
return null;
|
|
18074
|
-
const raw =
|
|
18689
|
+
const raw = readFileSync9(path, "utf8");
|
|
18075
18690
|
const parsed = JSON.parse(raw);
|
|
18076
18691
|
if (typeof parsed.version !== "string" || parsed.version.length === 0)
|
|
18077
18692
|
return null;
|
|
@@ -18087,7 +18702,7 @@ function readOnnxInstalledMeta(installDir) {
|
|
|
18087
18702
|
}
|
|
18088
18703
|
function sha256File2(path) {
|
|
18089
18704
|
const hash = createHash4("sha256");
|
|
18090
|
-
hash.update(
|
|
18705
|
+
hash.update(readFileSync9(path));
|
|
18091
18706
|
return hash.digest("hex");
|
|
18092
18707
|
}
|
|
18093
18708
|
function acquireLock(lockPath) {
|
|
@@ -18115,12 +18730,12 @@ ${new Date().toISOString()}
|
|
|
18115
18730
|
let owningPid = null;
|
|
18116
18731
|
let lockMtimeMs = 0;
|
|
18117
18732
|
try {
|
|
18118
|
-
const raw =
|
|
18733
|
+
const raw = readFileSync9(lockPath, "utf8");
|
|
18119
18734
|
const firstLine = raw.split(/\r?\n/, 1)[0]?.trim() ?? "";
|
|
18120
18735
|
const parsed = Number.parseInt(firstLine, 10);
|
|
18121
18736
|
if (Number.isFinite(parsed) && parsed > 0)
|
|
18122
18737
|
owningPid = parsed;
|
|
18123
|
-
lockMtimeMs =
|
|
18738
|
+
lockMtimeMs = statSync9(lockPath).mtimeMs;
|
|
18124
18739
|
} catch {
|
|
18125
18740
|
return tryClaim();
|
|
18126
18741
|
}
|
|
@@ -18140,7 +18755,7 @@ function releaseLock(lockPath) {
|
|
|
18140
18755
|
try {
|
|
18141
18756
|
let owningPid = null;
|
|
18142
18757
|
try {
|
|
18143
|
-
const raw =
|
|
18758
|
+
const raw = readFileSync9(lockPath, "utf8");
|
|
18144
18759
|
const firstLine = raw.split(/\r?\n/, 1)[0]?.trim() ?? "";
|
|
18145
18760
|
const parsed = Number.parseInt(firstLine, 10);
|
|
18146
18761
|
if (Number.isFinite(parsed) && parsed > 0)
|
|
@@ -18294,12 +18909,13 @@ function normalizeZoomTargets(record) {
|
|
|
18294
18909
|
throw new InvalidRequestError(`'targets[${index}].path' must be a non-empty string`);
|
|
18295
18910
|
}
|
|
18296
18911
|
const source = target;
|
|
18297
|
-
const
|
|
18912
|
+
const blank = (value) => value === undefined || value === null || typeof value === "string" && !value.trim();
|
|
18913
|
+
const emptyTarget = blank(source.symbol) && blank(source.path) && blank(source.filePath);
|
|
18298
18914
|
if (emptyTarget)
|
|
18299
18915
|
return { ...source };
|
|
18300
18916
|
const normalized = { ...source };
|
|
18301
18917
|
try {
|
|
18302
|
-
normalizeAliasPair(normalized, "path", "filePath",
|
|
18918
|
+
normalizeAliasPair(normalized, "path", "filePath", false);
|
|
18303
18919
|
} catch (error) {
|
|
18304
18920
|
if (error instanceof InvalidRequestError) {
|
|
18305
18921
|
throw new InvalidRequestError(error.message.replace("'filePath'", `'targets[${index}].filePath'`).replace("'path'", `'targets[${index}].path'`));
|
|
@@ -19880,7 +20496,7 @@ import {
|
|
|
19880
20496
|
// package.json
|
|
19881
20497
|
var package_default = {
|
|
19882
20498
|
name: "@cortexkit/aft-pi",
|
|
19883
|
-
version: "0.58.
|
|
20499
|
+
version: "0.58.2",
|
|
19884
20500
|
type: "module",
|
|
19885
20501
|
description: "Pi coding agent extension for Agent File Tools (AFT) — tree-sitter and LSP-powered code analysis",
|
|
19886
20502
|
main: "dist/index.js",
|
|
@@ -19903,30 +20519,32 @@ var package_default = {
|
|
|
19903
20519
|
"test:unit": "bun test src/__tests__ --path-ignore-patterns 'src/__tests__/e2e/**'"
|
|
19904
20520
|
},
|
|
19905
20521
|
dependencies: {
|
|
19906
|
-
"@cortexkit/aft-bridge": "0.58.
|
|
20522
|
+
"@cortexkit/aft-bridge": "0.58.2",
|
|
19907
20523
|
"comment-json": "^5.0.0",
|
|
19908
20524
|
diff: "^8.0.4",
|
|
19909
|
-
typebox: "1.1.38",
|
|
19910
20525
|
zod: "^4.4.3"
|
|
19911
20526
|
},
|
|
19912
20527
|
optionalDependencies: {
|
|
19913
|
-
"@cortexkit/aft-darwin-arm64": "0.58.
|
|
19914
|
-
"@cortexkit/aft-darwin-x64": "0.58.
|
|
19915
|
-
"@cortexkit/aft-linux-arm64": "0.58.
|
|
19916
|
-
"@cortexkit/aft-linux-x64": "0.58.
|
|
19917
|
-
"@cortexkit/aft-win32-arm64": "0.58.
|
|
19918
|
-
"@cortexkit/aft-win32-x64": "0.58.
|
|
20528
|
+
"@cortexkit/aft-darwin-arm64": "0.58.2",
|
|
20529
|
+
"@cortexkit/aft-darwin-x64": "0.58.2",
|
|
20530
|
+
"@cortexkit/aft-linux-arm64": "0.58.2",
|
|
20531
|
+
"@cortexkit/aft-linux-x64": "0.58.2",
|
|
20532
|
+
"@cortexkit/aft-win32-arm64": "0.58.2",
|
|
20533
|
+
"@cortexkit/aft-win32-x64": "0.58.2"
|
|
19919
20534
|
},
|
|
19920
20535
|
devDependencies: {
|
|
19921
20536
|
"@earendil-works/pi-coding-agent": "^0.80.2",
|
|
19922
20537
|
"@earendil-works/pi-ai": "^0.80.2",
|
|
19923
20538
|
"@earendil-works/pi-tui": "^0.80.2",
|
|
19924
20539
|
"@types/node": "^22.0.0",
|
|
20540
|
+
typebox: "1.1.38",
|
|
19925
20541
|
typescript: "^5.8.0"
|
|
19926
20542
|
},
|
|
19927
20543
|
peerDependencies: {
|
|
20544
|
+
"@earendil-works/pi-ai": "*",
|
|
19928
20545
|
"@earendil-works/pi-coding-agent": "*",
|
|
19929
|
-
"@earendil-works/pi-tui": "*"
|
|
20546
|
+
"@earendil-works/pi-tui": "*",
|
|
20547
|
+
typebox: "*"
|
|
19930
20548
|
},
|
|
19931
20549
|
exports: {
|
|
19932
20550
|
".": {
|
|
@@ -20192,6 +20810,24 @@ function appendDegradedStatus(lines, status, markdown) {
|
|
|
20192
20810
|
// src/tools/_shared.ts
|
|
20193
20811
|
import { existsSync as existsSync12 } from "node:fs";
|
|
20194
20812
|
import { Type } from "typebox";
|
|
20813
|
+
|
|
20814
|
+
// src/session-kind.ts
|
|
20815
|
+
var MAGIC_CONTEXT_SUBAGENT_ENV = "MAGIC_CONTEXT_PI_SUBAGENT";
|
|
20816
|
+
function piWorkerKind(extCtx, env = process.env) {
|
|
20817
|
+
if (env[MAGIC_CONTEXT_SUBAGENT_ENV] === "1")
|
|
20818
|
+
return "delegated";
|
|
20819
|
+
if (extCtx?.hasUI === false)
|
|
20820
|
+
return "headless";
|
|
20821
|
+
return;
|
|
20822
|
+
}
|
|
20823
|
+
function isPiWorkerSession(extCtx, env = process.env) {
|
|
20824
|
+
return piWorkerKind(extCtx, env) !== undefined;
|
|
20825
|
+
}
|
|
20826
|
+
function skipsEagerStartup(env = process.env) {
|
|
20827
|
+
return piWorkerKind(undefined, env) === "delegated";
|
|
20828
|
+
}
|
|
20829
|
+
|
|
20830
|
+
// src/tools/_shared.ts
|
|
20195
20831
|
var optionalInt = (min, max, description = "(integer)") => Type.Optional(Type.Union([Type.Integer({ minimum: min, maximum: max }), Type.String()], {
|
|
20196
20832
|
description
|
|
20197
20833
|
}));
|
|
@@ -20277,6 +20913,9 @@ async function callBridge(bridge, command, params = {}, extCtx, options) {
|
|
|
20277
20913
|
if (sessionId) {
|
|
20278
20914
|
merged.session_id = sessionId;
|
|
20279
20915
|
}
|
|
20916
|
+
if (isPiWorkerSession(extCtx)) {
|
|
20917
|
+
merged[WORKER_SESSION_FIELD] = true;
|
|
20918
|
+
}
|
|
20280
20919
|
const sendOptions = {
|
|
20281
20920
|
...timeoutMs !== undefined ? { timeoutMs } : {},
|
|
20282
20921
|
configureWarningClient: extCtx,
|
|
@@ -20304,6 +20943,7 @@ async function callToolCallForSession(bridge, name, rawArgs, sessionId, extCtx,
|
|
|
20304
20943
|
const sendOptions = {
|
|
20305
20944
|
...timeoutMs !== undefined ? { timeoutMs } : {},
|
|
20306
20945
|
configureWarningClient: extCtx,
|
|
20946
|
+
...isPiWorkerSession(extCtx) ? { workerSession: true } : {},
|
|
20307
20947
|
...options
|
|
20308
20948
|
};
|
|
20309
20949
|
let response;
|
|
@@ -20623,7 +21263,7 @@ function registerStatusCommand(pi, ctx) {
|
|
|
20623
21263
|
}
|
|
20624
21264
|
|
|
20625
21265
|
// src/config.ts
|
|
20626
|
-
import { existsSync as existsSync13, readFileSync as
|
|
21266
|
+
import { existsSync as existsSync13, readFileSync as readFileSync10, renameSync as renameSync7, unlinkSync as unlinkSync5, writeFileSync as writeFileSync6 } from "node:fs";
|
|
20627
21267
|
import { createRequire as createRequire2 } from "node:module";
|
|
20628
21268
|
import { homedir as homedir13 } from "node:os";
|
|
20629
21269
|
import { isAbsolute as isAbsolute7 } from "node:path";
|
|
@@ -25436,7 +26076,7 @@ var IndexConfigSchema = object({
|
|
|
25436
26076
|
roots: array(IndexRootSchema).optional()
|
|
25437
26077
|
});
|
|
25438
26078
|
var SemanticConfigSchema = object({
|
|
25439
|
-
backend: _enum(["fastembed", "openai_compatible", "ollama"]).optional(),
|
|
26079
|
+
backend: _enum(["fastembed", "openai_compatible", "ollama", "synapse"]).optional(),
|
|
25440
26080
|
model: string2().trim().min(1).optional(),
|
|
25441
26081
|
base_url: string2().trim().min(1).optional(),
|
|
25442
26082
|
api_key_env: string2().trim().min(1).optional(),
|
|
@@ -25447,6 +26087,17 @@ var SemanticConfigSchema = object({
|
|
|
25447
26087
|
max_input_tokens: number2().int().positive().optional(),
|
|
25448
26088
|
max_files: number2().int().positive().optional()
|
|
25449
26089
|
});
|
|
26090
|
+
var RerankConfigSchema = object({
|
|
26091
|
+
backend: _enum(["off", "onnx", "remote", "synapse"]).optional(),
|
|
26092
|
+
model: string2().trim().min(1).optional(),
|
|
26093
|
+
endpoint: string2().trim().min(1).optional(),
|
|
26094
|
+
api_key_env: string2().trim().min(1).optional(),
|
|
26095
|
+
top_n: number2().int().positive().optional(),
|
|
26096
|
+
timeout_ms: number2().int().positive().optional()
|
|
26097
|
+
});
|
|
26098
|
+
var SearchConfigSchema = object({
|
|
26099
|
+
rerank: RerankConfigSchema.optional()
|
|
26100
|
+
});
|
|
25450
26101
|
var LspExtensionSchema = string2().trim().min(1).refine((value) => value.replace(/^\.+/, "").length > 0, {
|
|
25451
26102
|
message: "Extension must include characters other than leading dots"
|
|
25452
26103
|
});
|
|
@@ -25510,6 +26161,10 @@ var SubcConfigSchema = object({
|
|
|
25510
26161
|
var GhShimConfigSchema = object({
|
|
25511
26162
|
binary_path: string2().trim().refine(isAbsolute7, "gh_shim.binary_path must be absolute").optional()
|
|
25512
26163
|
});
|
|
26164
|
+
var OpenCodeHostConfigSchema = object({
|
|
26165
|
+
server_url: string2().optional(),
|
|
26166
|
+
server_password_env: string2().optional()
|
|
26167
|
+
});
|
|
25513
26168
|
var GithubConfigSchema = object({
|
|
25514
26169
|
shim: boolean2().optional(),
|
|
25515
26170
|
read: boolean2().optional(),
|
|
@@ -25582,8 +26237,10 @@ var AftConfigFieldsSchema = object({
|
|
|
25582
26237
|
lsp: LspConfigSchema.optional(),
|
|
25583
26238
|
url_fetch_allow_private: boolean2().optional(),
|
|
25584
26239
|
semantic: SemanticConfigSchema.optional(),
|
|
26240
|
+
search: SearchConfigSchema.optional(),
|
|
25585
26241
|
bridge: BridgeConfigSchema.optional(),
|
|
25586
26242
|
subc: SubcConfigSchema.optional(),
|
|
26243
|
+
opencode: OpenCodeHostConfigSchema.optional(),
|
|
25587
26244
|
github: GithubConfigSchema.optional(),
|
|
25588
26245
|
gh_shim: GhShimConfigSchema.optional(),
|
|
25589
26246
|
git: GitConfigSchema.optional()
|
|
@@ -25729,7 +26386,7 @@ function migrateAftConfigFile2(configPath, logger = { log: log2, warn: warn2 })
|
|
|
25729
26386
|
let tmpPath = null;
|
|
25730
26387
|
let oldKeys = [];
|
|
25731
26388
|
try {
|
|
25732
|
-
const content =
|
|
26389
|
+
const content = readFileSync10(configPath, "utf-8");
|
|
25733
26390
|
const rawConfig = import_comment_json.parse(content);
|
|
25734
26391
|
if (!rawConfig || typeof rawConfig !== "object" || Array.isArray(rawConfig)) {
|
|
25735
26392
|
return { migrated: false, oldKeys: [] };
|
|
@@ -25764,6 +26421,18 @@ ${serialized}` : serialized;
|
|
|
25764
26421
|
}
|
|
25765
26422
|
}
|
|
25766
26423
|
var configLoadErrors = [];
|
|
26424
|
+
var configValidationErrors = [];
|
|
26425
|
+
var configLoadSources = [];
|
|
26426
|
+
var configLoadTexts = new Map;
|
|
26427
|
+
function getConfigLoadSources() {
|
|
26428
|
+
return configLoadSources;
|
|
26429
|
+
}
|
|
26430
|
+
function getConfigLoadTexts() {
|
|
26431
|
+
return configLoadTexts;
|
|
26432
|
+
}
|
|
26433
|
+
function getConfigValidationErrors() {
|
|
26434
|
+
return configValidationErrors;
|
|
26435
|
+
}
|
|
25767
26436
|
function getConfigLoadErrors() {
|
|
25768
26437
|
return configLoadErrors;
|
|
25769
26438
|
}
|
|
@@ -25808,7 +26477,9 @@ function loadConfigFromPath(configPath, tier) {
|
|
|
25808
26477
|
try {
|
|
25809
26478
|
if (!existsSync13(configPath))
|
|
25810
26479
|
return null;
|
|
25811
|
-
const content =
|
|
26480
|
+
const content = readFileSync10(configPath, "utf-8");
|
|
26481
|
+
configLoadSources.push(configPath);
|
|
26482
|
+
configLoadTexts.set(configPath, content);
|
|
25812
26483
|
const rawConfig = import_comment_json.parse(content);
|
|
25813
26484
|
if (!rawConfig || typeof rawConfig !== "object" || Array.isArray(rawConfig)) {
|
|
25814
26485
|
recordConfigParseFailure(configPath, "root must be an object");
|
|
@@ -25858,6 +26529,7 @@ function loadConfigFromPath(configPath, tier) {
|
|
|
25858
26529
|
} else {
|
|
25859
26530
|
const errorMsg = result.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join(", ");
|
|
25860
26531
|
warn2(`Config validation error in ${configPath}: ${errorMsg}`);
|
|
26532
|
+
configValidationErrors.push({ path: configPath, message: errorMsg });
|
|
25861
26533
|
parsed = parseConfigPartially(cleanConfig);
|
|
25862
26534
|
}
|
|
25863
26535
|
if (tier === "user" && parsed.disabled_tools === undefined) {
|
|
@@ -26061,6 +26733,8 @@ function getStrippedTopLevelKeys(override) {
|
|
|
26061
26733
|
stripped.push("sandbox.write_allow");
|
|
26062
26734
|
if (override.subc !== undefined)
|
|
26063
26735
|
stripped.push("subc");
|
|
26736
|
+
if (override.opencode !== undefined)
|
|
26737
|
+
stripped.push("opencode");
|
|
26064
26738
|
if (override.github !== undefined)
|
|
26065
26739
|
stripped.push("github");
|
|
26066
26740
|
if (override.gh_shim !== undefined)
|
|
@@ -26068,14 +26742,33 @@ function getStrippedTopLevelKeys(override) {
|
|
|
26068
26742
|
for (const tool of partitionProjectDisables(override.disabled_tools).ignored) {
|
|
26069
26743
|
stripped.push(`disabled_tools.${tool}`);
|
|
26070
26744
|
}
|
|
26745
|
+
stripped.push(...projectRerankStrippedKeys(override.search?.rerank));
|
|
26071
26746
|
return stripped;
|
|
26072
26747
|
}
|
|
26748
|
+
function projectRerankStrippedKeys(rerank) {
|
|
26749
|
+
if (rerank === undefined)
|
|
26750
|
+
return [];
|
|
26751
|
+
const stripped = [];
|
|
26752
|
+
if (rerank.backend !== undefined && rerank.backend !== "off")
|
|
26753
|
+
stripped.push("search.rerank.backend");
|
|
26754
|
+
for (const key of ["model", "endpoint", "api_key_env", "top_n", "timeout_ms"]) {
|
|
26755
|
+
if (rerank[key] !== undefined)
|
|
26756
|
+
stripped.push(`search.rerank.${key}`);
|
|
26757
|
+
}
|
|
26758
|
+
return stripped;
|
|
26759
|
+
}
|
|
26760
|
+
function mergeProjectSearchConfig(base, project) {
|
|
26761
|
+
if (project?.rerank?.backend !== "off")
|
|
26762
|
+
return base;
|
|
26763
|
+
return { ...base, rerank: { ...base?.rerank, backend: "off" } };
|
|
26764
|
+
}
|
|
26073
26765
|
function mergeConfigs(base, override) {
|
|
26074
26766
|
const disabledTools = unionDisabledTools(base.disabled_tools, partitionProjectDisables(override.disabled_tools).accepted);
|
|
26075
26767
|
const indexes = mergeIndexes(base.indexes, override.indexes, true);
|
|
26076
26768
|
const formatter = { ...base.formatter, ...override.formatter };
|
|
26077
26769
|
const checker = { ...base.checker, ...override.checker };
|
|
26078
26770
|
const semantic = mergeSemanticConfig(base.semantic, override.semantic);
|
|
26771
|
+
const search = mergeProjectSearchConfig(base.search, override.search);
|
|
26079
26772
|
const lsp = mergeLspConfig(base.lsp, override.lsp);
|
|
26080
26773
|
const experimental = mergeExperimentalConfig(base.experimental, override.experimental);
|
|
26081
26774
|
const bash = mergeBashConfig(base.bash, override.bash);
|
|
@@ -26105,6 +26798,7 @@ function mergeConfigs(base, override) {
|
|
|
26105
26798
|
...pi !== undefined ? { pi } : {},
|
|
26106
26799
|
experimental,
|
|
26107
26800
|
semantic,
|
|
26801
|
+
search,
|
|
26108
26802
|
...bridge !== undefined ? { bridge } : {},
|
|
26109
26803
|
...indexes !== undefined ? { indexes } : {},
|
|
26110
26804
|
...disabledTools !== undefined ? { disabled_tools: disabledTools } : {}
|
|
@@ -26163,6 +26857,9 @@ function buildConfigTierConfigureParams(projectDirectory, processState = {}) {
|
|
|
26163
26857
|
}
|
|
26164
26858
|
function loadAftConfig(projectDirectory) {
|
|
26165
26859
|
configLoadErrors = [];
|
|
26860
|
+
configValidationErrors = [];
|
|
26861
|
+
configLoadSources = [];
|
|
26862
|
+
configLoadTexts = new Map;
|
|
26166
26863
|
configLoadNotices = [];
|
|
26167
26864
|
semanticInputSupplied = false;
|
|
26168
26865
|
const { userConfigPath, projectConfigPath } = resolveAftConfigPaths(projectDirectory);
|
|
@@ -26213,10 +26910,10 @@ import {
|
|
|
26213
26910
|
createReadStream as createReadStream2,
|
|
26214
26911
|
existsSync as existsSync15,
|
|
26215
26912
|
mkdirSync as mkdirSync7,
|
|
26216
|
-
readFileSync as
|
|
26913
|
+
readFileSync as readFileSync13,
|
|
26217
26914
|
renameSync as renameSync9,
|
|
26218
26915
|
rmSync as rmSync5,
|
|
26219
|
-
statSync as
|
|
26916
|
+
statSync as statSync11,
|
|
26220
26917
|
writeFileSync as writeFileSync8
|
|
26221
26918
|
} from "node:fs";
|
|
26222
26919
|
import { join as join17 } from "node:path";
|
|
@@ -26227,10 +26924,10 @@ import {
|
|
|
26227
26924
|
closeSync as closeSync6,
|
|
26228
26925
|
mkdirSync as mkdirSync6,
|
|
26229
26926
|
openSync as openSync6,
|
|
26230
|
-
readFileSync as
|
|
26927
|
+
readFileSync as readFileSync11,
|
|
26231
26928
|
renameSync as renameSync8,
|
|
26232
26929
|
rmSync as rmSync4,
|
|
26233
|
-
statSync as
|
|
26930
|
+
statSync as statSync10,
|
|
26234
26931
|
unlinkSync as unlinkSync6,
|
|
26235
26932
|
writeFileSync as writeFileSync7
|
|
26236
26933
|
} from "node:fs";
|
|
@@ -26253,7 +26950,7 @@ function lspBinDir(npmPackage) {
|
|
|
26253
26950
|
function isInstalled(npmPackage, binary) {
|
|
26254
26951
|
for (const candidate of lspBinaryCandidates(binary)) {
|
|
26255
26952
|
try {
|
|
26256
|
-
if (
|
|
26953
|
+
if (statSync10(join15(lspBinDir(npmPackage), candidate)).isFile())
|
|
26257
26954
|
return true;
|
|
26258
26955
|
} catch {}
|
|
26259
26956
|
}
|
|
@@ -26281,9 +26978,9 @@ function writeInstalledMetaIn(installDir, version, sha256) {
|
|
|
26281
26978
|
function readInstalledMetaIn(installDir) {
|
|
26282
26979
|
const path = join15(installDir, INSTALLED_META_FILE);
|
|
26283
26980
|
try {
|
|
26284
|
-
if (!
|
|
26981
|
+
if (!statSync10(path).isFile())
|
|
26285
26982
|
return null;
|
|
26286
|
-
const raw =
|
|
26983
|
+
const raw = readFileSync11(path, "utf8");
|
|
26287
26984
|
const parsed = JSON.parse(raw);
|
|
26288
26985
|
if (typeof parsed.version !== "string" || parsed.version.length === 0)
|
|
26289
26986
|
return null;
|
|
@@ -26352,12 +27049,12 @@ ${token}
|
|
|
26352
27049
|
let owningPid = null;
|
|
26353
27050
|
let lockMtimeMs = 0;
|
|
26354
27051
|
try {
|
|
26355
|
-
const raw =
|
|
27052
|
+
const raw = readFileSync11(lock, "utf8");
|
|
26356
27053
|
const firstLine = raw.split(/\r?\n/, 1)[0]?.trim() ?? "";
|
|
26357
27054
|
const parsed = Number.parseInt(firstLine, 10);
|
|
26358
27055
|
if (Number.isFinite(parsed) && parsed > 0)
|
|
26359
27056
|
owningPid = parsed;
|
|
26360
|
-
lockMtimeMs =
|
|
27057
|
+
lockMtimeMs = statSync10(lock).mtimeMs;
|
|
26361
27058
|
} catch {
|
|
26362
27059
|
return tryClaim();
|
|
26363
27060
|
}
|
|
@@ -26391,7 +27088,7 @@ function releaseInstallLock(lockKey, acquisitionToken) {
|
|
|
26391
27088
|
let owningPid = null;
|
|
26392
27089
|
let owningToken = "";
|
|
26393
27090
|
try {
|
|
26394
|
-
const raw =
|
|
27091
|
+
const raw = readFileSync11(lock, "utf8");
|
|
26395
27092
|
const lines = raw.split(/\r?\n/);
|
|
26396
27093
|
const parsed = Number.parseInt(lines[0]?.trim() ?? "", 10);
|
|
26397
27094
|
if (Number.isFinite(parsed) && parsed > 0)
|
|
@@ -26438,7 +27135,7 @@ function autoInstallStampPath() {
|
|
|
26438
27135
|
}
|
|
26439
27136
|
function hasRecentAutoInstallStamp(path) {
|
|
26440
27137
|
try {
|
|
26441
|
-
const parsed = JSON.parse(
|
|
27138
|
+
const parsed = JSON.parse(readFileSync11(path, "utf8"));
|
|
26442
27139
|
return typeof parsed.lastRunMs === "number" && Number.isFinite(parsed.lastRunMs) && Math.abs(Date.now() - parsed.lastRunMs) < AUTO_INSTALL_WINDOW_MS;
|
|
26443
27140
|
} catch {
|
|
26444
27141
|
return false;
|
|
@@ -26446,7 +27143,7 @@ function hasRecentAutoInstallStamp(path) {
|
|
|
26446
27143
|
}
|
|
26447
27144
|
function readAutoInstallLockOwner(path) {
|
|
26448
27145
|
try {
|
|
26449
|
-
const parsed = JSON.parse(
|
|
27146
|
+
const parsed = JSON.parse(readFileSync11(path, "utf8"));
|
|
26450
27147
|
if (typeof parsed.pid !== "number" || typeof parsed.startedMs !== "number" || typeof parsed.token !== "string") {
|
|
26451
27148
|
return null;
|
|
26452
27149
|
}
|
|
@@ -26459,7 +27156,7 @@ function autoInstallLockIsStale(path) {
|
|
|
26459
27156
|
const owner = readAutoInstallLockOwner(path);
|
|
26460
27157
|
let age = Number.POSITIVE_INFINITY;
|
|
26461
27158
|
try {
|
|
26462
|
-
age = Math.abs(Date.now() -
|
|
27159
|
+
age = Math.abs(Date.now() - statSync10(path).mtimeMs);
|
|
26463
27160
|
} catch {
|
|
26464
27161
|
return true;
|
|
26465
27162
|
}
|
|
@@ -26537,7 +27234,7 @@ var VERSION_CHECK_FILE = ".aft-version-check";
|
|
|
26537
27234
|
function readVersionCheck(npmPackage) {
|
|
26538
27235
|
const file = join15(lspPackageDir(npmPackage), VERSION_CHECK_FILE);
|
|
26539
27236
|
try {
|
|
26540
|
-
const raw =
|
|
27237
|
+
const raw = readFileSync11(file, "utf8");
|
|
26541
27238
|
const parsed = JSON.parse(raw);
|
|
26542
27239
|
if (typeof parsed.last_checked === "string") {
|
|
26543
27240
|
return {
|
|
@@ -26640,6 +27337,14 @@ var NPM_LSP_TABLE = [
|
|
|
26640
27337
|
extensions: ["ts", "tsx", "js", "jsx", "mjs", "cjs", "mts", "cts"],
|
|
26641
27338
|
rootMarkers: ["tsconfig.json", "jsconfig.json", "package.json"]
|
|
26642
27339
|
},
|
|
27340
|
+
{
|
|
27341
|
+
id: "typescript-sdk",
|
|
27342
|
+
npm: "typescript",
|
|
27343
|
+
binary: "tsserver",
|
|
27344
|
+
extensions: ["ts", "tsx", "js", "jsx", "mjs", "cjs", "mts", "cts"],
|
|
27345
|
+
rootMarkers: ["tsconfig.json", "jsconfig.json", "package.json"],
|
|
27346
|
+
supportedMajor: 5
|
|
27347
|
+
},
|
|
26643
27348
|
{
|
|
26644
27349
|
id: "python",
|
|
26645
27350
|
npm: "pyright",
|
|
@@ -26712,9 +27417,18 @@ var NPM_LSP_TABLE = [
|
|
|
26712
27417
|
rootMarkers: ["biome.json", "biome.jsonc"]
|
|
26713
27418
|
}
|
|
26714
27419
|
];
|
|
27420
|
+
function majorVersionOf(version) {
|
|
27421
|
+
const match = /^v?(\d+)(?:\.|$)/.exec(version.trim());
|
|
27422
|
+
return match ? Number(match[1]) : null;
|
|
27423
|
+
}
|
|
27424
|
+
function isVersionSupported(spec, version) {
|
|
27425
|
+
if (spec.supportedMajor === undefined)
|
|
27426
|
+
return true;
|
|
27427
|
+
return majorVersionOf(version) === spec.supportedMajor;
|
|
27428
|
+
}
|
|
26715
27429
|
|
|
26716
27430
|
// src/lsp-project-relevance.ts
|
|
26717
|
-
import { existsSync as existsSync14, readdirSync as readdirSync4, readFileSync as
|
|
27431
|
+
import { existsSync as existsSync14, readdirSync as readdirSync4, readFileSync as readFileSync12 } from "node:fs";
|
|
26718
27432
|
import { join as join16 } from "node:path";
|
|
26719
27433
|
var MAX_WALK_DIRS = 200;
|
|
26720
27434
|
var MAX_WALK_DEPTH = 4;
|
|
@@ -26756,7 +27470,7 @@ function hasPackageJsonDep(projectRoot, depNames) {
|
|
|
26756
27470
|
}
|
|
26757
27471
|
function readPackageJson(projectRoot) {
|
|
26758
27472
|
try {
|
|
26759
|
-
const raw =
|
|
27473
|
+
const raw = readFileSync12(join16(projectRoot, "package.json"), "utf8");
|
|
26760
27474
|
const parsed = JSON.parse(raw);
|
|
26761
27475
|
if (typeof parsed !== "object" || parsed === null)
|
|
26762
27476
|
return null;
|
|
@@ -26808,10 +27522,11 @@ function extensionOf(fileName) {
|
|
|
26808
27522
|
|
|
26809
27523
|
// src/lsp-registry-probe.ts
|
|
26810
27524
|
var NPM_REGISTRY_BASE = "https://registry.npmjs.org";
|
|
26811
|
-
function pickEligibleVersion(response, graceDays, now = Date.now()) {
|
|
27525
|
+
function pickEligibleVersion(response, graceDays, now = Date.now(), accept = () => true) {
|
|
26812
27526
|
const times = response.time || {};
|
|
26813
27527
|
const cutoff = now - graceDays * 24 * 60 * 60 * 1000;
|
|
26814
27528
|
const candidates = [];
|
|
27529
|
+
let releaseCount = 0;
|
|
26815
27530
|
for (const [version, publishedAt] of Object.entries(times)) {
|
|
26816
27531
|
if (version === "created" || version === "modified")
|
|
26817
27532
|
continue;
|
|
@@ -26822,6 +27537,9 @@ function pickEligibleVersion(response, graceDays, now = Date.now()) {
|
|
|
26822
27537
|
const ts = Date.parse(publishedAt);
|
|
26823
27538
|
if (Number.isNaN(ts))
|
|
26824
27539
|
continue;
|
|
27540
|
+
releaseCount += 1;
|
|
27541
|
+
if (!accept(version))
|
|
27542
|
+
continue;
|
|
26825
27543
|
candidates.push({ version, publishedAt, ts });
|
|
26826
27544
|
}
|
|
26827
27545
|
candidates.sort((a, b) => b.ts - a.ts);
|
|
@@ -26830,10 +27548,11 @@ function pickEligibleVersion(response, graceDays, now = Date.now()) {
|
|
|
26830
27548
|
return {
|
|
26831
27549
|
version: eligible[0]?.version ?? null,
|
|
26832
27550
|
blockedByGrace,
|
|
27551
|
+
outsideSupportedRange: releaseCount > 0 && candidates.length === 0,
|
|
26833
27552
|
eligible: eligible.map(({ version, publishedAt }) => ({ version, publishedAt }))
|
|
26834
27553
|
};
|
|
26835
27554
|
}
|
|
26836
|
-
async function probeRegistry(npmPackage, graceDays, fetchImpl = fetch) {
|
|
27555
|
+
async function probeRegistry(npmPackage, graceDays, fetchImpl = fetch, accept) {
|
|
26837
27556
|
const encoded = encodeURIComponent(npmPackage).replace(/^%40/, "@");
|
|
26838
27557
|
const url = `${NPM_REGISTRY_BASE}/${encoded}`;
|
|
26839
27558
|
try {
|
|
@@ -26846,7 +27565,7 @@ async function probeRegistry(npmPackage, graceDays, fetchImpl = fetch) {
|
|
|
26846
27565
|
return null;
|
|
26847
27566
|
}
|
|
26848
27567
|
const json = await res.json();
|
|
26849
|
-
return pickEligibleVersion(json, graceDays);
|
|
27568
|
+
return pickEligibleVersion(json, graceDays, Date.now(), accept);
|
|
26850
27569
|
} catch (err) {
|
|
26851
27570
|
warn2(`[lsp] registry probe failed for ${npmPackage}: ${err}`);
|
|
26852
27571
|
return null;
|
|
@@ -26897,11 +27616,11 @@ async function resolveTargetVersion(spec, config, fetchImpl = fetch) {
|
|
|
26897
27616
|
}
|
|
26898
27617
|
const cached = readVersionCheck(spec.npm);
|
|
26899
27618
|
const weeklyMs = config.graceDays * 24 * 60 * 60 * 1000;
|
|
26900
|
-
const cachedSafe = isSafeVersion(cached?.latest_eligible ?? null);
|
|
27619
|
+
const cachedSafe = isSafeVersion(cached?.latest_eligible ?? null) && isVersionSupported(spec, cached?.latest_eligible);
|
|
26901
27620
|
if (cached && !shouldRecheckVersion(cached, weeklyMs) && cachedSafe) {
|
|
26902
27621
|
return { version: cached.latest_eligible, pinned: false, probe: null };
|
|
26903
27622
|
}
|
|
26904
|
-
const probe = await probeRegistry(spec.npm, config.graceDays, fetchImpl);
|
|
27623
|
+
const probe = await probeRegistry(spec.npm, config.graceDays, fetchImpl, (candidate) => isVersionSupported(spec, candidate));
|
|
26905
27624
|
if (!probe) {
|
|
26906
27625
|
return {
|
|
26907
27626
|
version: cachedSafe ? cached?.latest_eligible ?? null : null,
|
|
@@ -27036,11 +27755,16 @@ async function ensureServerInstalled(spec, config, fetchImpl, signal) {
|
|
|
27036
27755
|
const { version, probe } = await resolveTargetVersion(spec, config, fetchImpl);
|
|
27037
27756
|
if (!version) {
|
|
27038
27757
|
const installed = isInstalled(spec.npm, spec.binary);
|
|
27039
|
-
if (installed) {
|
|
27758
|
+
if (installed && installedVersionAllowed(spec, config)) {
|
|
27040
27759
|
warn2(`[lsp] no eligible version of ${spec.npm} (grace=${config.graceDays}d); keeping existing install`);
|
|
27041
27760
|
return { started: false, reason: "kept existing install" };
|
|
27042
27761
|
}
|
|
27043
|
-
const blocked = probe?.blockedByGrace ? `all versions are within ${config.graceDays}-day grace window` : "registry probe failed";
|
|
27762
|
+
const blocked = probe?.blockedByGrace ? `all versions are within ${config.graceDays}-day grace window` : probe?.outsideSupportedRange ? `no ${spec.supportedMajor}.x release on the registry` : "registry probe failed";
|
|
27763
|
+
if (installed) {
|
|
27764
|
+
const reason = `cached ${spec.npm} ${readInstalledMeta(spec.npm)?.version ?? "(unknown version)"} is outside the supported ${spec.supportedMajor}.x line and no replacement is available: ${blocked}`;
|
|
27765
|
+
warn2(`[lsp] ${reason}`);
|
|
27766
|
+
return { started: false, reason };
|
|
27767
|
+
}
|
|
27044
27768
|
warn2(`[lsp] skipping ${spec.npm}: ${blocked}`);
|
|
27045
27769
|
return { started: false, reason: blocked };
|
|
27046
27770
|
}
|
|
@@ -27104,7 +27828,7 @@ function hashInstalledBinary(spec) {
|
|
|
27104
27828
|
let pathToHash = null;
|
|
27105
27829
|
for (const p of candidates) {
|
|
27106
27830
|
try {
|
|
27107
|
-
if (
|
|
27831
|
+
if (statSync11(p).isFile()) {
|
|
27108
27832
|
pathToHash = p;
|
|
27109
27833
|
break;
|
|
27110
27834
|
}
|
|
@@ -27130,14 +27854,14 @@ function installedBinaryPath(spec) {
|
|
|
27130
27854
|
] : [lspBinaryPath(spec.npm, spec.binary)];
|
|
27131
27855
|
for (const candidate of candidates) {
|
|
27132
27856
|
try {
|
|
27133
|
-
if (
|
|
27857
|
+
if (statSync11(candidate).isFile())
|
|
27134
27858
|
return candidate;
|
|
27135
27859
|
} catch {}
|
|
27136
27860
|
}
|
|
27137
27861
|
return null;
|
|
27138
27862
|
}
|
|
27139
27863
|
function sha256OfFileSync(path) {
|
|
27140
|
-
return createHash5("sha256").update(
|
|
27864
|
+
return createHash5("sha256").update(readFileSync13(path)).digest("hex");
|
|
27141
27865
|
}
|
|
27142
27866
|
function quarantineCachedNpmInstall(spec, reason) {
|
|
27143
27867
|
const packageDir = lspPackageDir(spec.npm);
|
|
@@ -27165,6 +27889,15 @@ function validateCachedNpmInstall(spec) {
|
|
|
27165
27889
|
}
|
|
27166
27890
|
return true;
|
|
27167
27891
|
}
|
|
27892
|
+
function installedVersionAllowed(spec, config) {
|
|
27893
|
+
const installed = readInstalledMeta(spec.npm)?.version;
|
|
27894
|
+
if (!installed)
|
|
27895
|
+
return spec.supportedMajor === undefined;
|
|
27896
|
+
return isVersionSupported(spec, installed) || config.versions[spec.npm] === installed;
|
|
27897
|
+
}
|
|
27898
|
+
function cachedInstallUsable(spec, config) {
|
|
27899
|
+
return isInstalled(spec.npm, spec.binary) && validateCachedNpmInstall(spec) && installedVersionAllowed(spec, config);
|
|
27900
|
+
}
|
|
27168
27901
|
function runAutoInstall(projectRoot, config, fetchImpl = fetch) {
|
|
27169
27902
|
const cachedBinDirs = [];
|
|
27170
27903
|
const skipped = [];
|
|
@@ -27177,10 +27910,10 @@ function runAutoInstall(projectRoot, config, fetchImpl = fetch) {
|
|
|
27177
27910
|
return projectExtensions;
|
|
27178
27911
|
};
|
|
27179
27912
|
for (const spec of NPM_LSP_TABLE) {
|
|
27180
|
-
if (
|
|
27913
|
+
if (cachedInstallUsable(spec, config)) {
|
|
27181
27914
|
cachedBinDirs.push(lspBinDir(spec.npm));
|
|
27182
27915
|
}
|
|
27183
|
-
if (config.disabled.has(spec.id)) {
|
|
27916
|
+
if (config.disabled.has(spec.id) || spec.id === "typescript-sdk" && config.disabled.has("typescript")) {
|
|
27184
27917
|
skipped.push({ id: spec.id, reason: "disabled by config" });
|
|
27185
27918
|
continue;
|
|
27186
27919
|
}
|
|
@@ -27216,7 +27949,7 @@ function runAutoInstall(projectRoot, config, fetchImpl = fetch) {
|
|
|
27216
27949
|
},
|
|
27217
27950
|
skipped,
|
|
27218
27951
|
installsComplete: Promise.all(installPromises).then(() => {}),
|
|
27219
|
-
getCachedBinDirs: () => NPM_LSP_TABLE.filter((spec) =>
|
|
27952
|
+
getCachedBinDirs: () => NPM_LSP_TABLE.filter((spec) => cachedInstallUsable(spec, config)).map((spec) => lspBinDir(spec.npm))
|
|
27220
27953
|
};
|
|
27221
27954
|
}
|
|
27222
27955
|
async function pushLspPathsAfterAutoInstall(pool, projectRoot, cachedBinDirs) {
|
|
@@ -27235,16 +27968,16 @@ import {
|
|
|
27235
27968
|
lstatSync as lstatSync2,
|
|
27236
27969
|
mkdirSync as mkdirSync8,
|
|
27237
27970
|
readdirSync as readdirSync5,
|
|
27238
|
-
readFileSync as
|
|
27971
|
+
readFileSync as readFileSync14,
|
|
27239
27972
|
readlinkSync as readlinkSync2,
|
|
27240
27973
|
realpathSync as realpathSync4,
|
|
27241
27974
|
renameSync as renameSync10,
|
|
27242
27975
|
rmSync as rmSync6,
|
|
27243
|
-
statSync as
|
|
27976
|
+
statSync as statSync12,
|
|
27244
27977
|
unlinkSync as unlinkSync7,
|
|
27245
27978
|
writeFileSync as writeFileSync9
|
|
27246
27979
|
} from "node:fs";
|
|
27247
|
-
import { dirname as
|
|
27980
|
+
import { dirname as dirname9, join as join18, relative as relative3, resolve as resolve9 } from "node:path";
|
|
27248
27981
|
import { Readable as Readable3 } from "node:stream";
|
|
27249
27982
|
import { pipeline as pipeline3 } from "node:stream/promises";
|
|
27250
27983
|
|
|
@@ -27353,7 +28086,7 @@ function installFailureMarkerPath(spec) {
|
|
|
27353
28086
|
}
|
|
27354
28087
|
function readInstallFailureMarker(spec) {
|
|
27355
28088
|
try {
|
|
27356
|
-
const marker = JSON.parse(
|
|
28089
|
+
const marker = JSON.parse(readFileSync14(installFailureMarkerPath(spec), "utf8"));
|
|
27357
28090
|
if (typeof marker.release !== "string" || typeof marker.sha256 !== "string" || typeof marker.error !== "string" || typeof marker.at !== "string" || !Number.isFinite(Date.parse(marker.at))) {
|
|
27358
28091
|
return null;
|
|
27359
28092
|
}
|
|
@@ -27367,7 +28100,7 @@ function writeInstallFailureMarker(spec, release, sha256, installError) {
|
|
|
27367
28100
|
const markerError = installError.replace(/\s+/g, " ").trim();
|
|
27368
28101
|
const tempPath = `${path}.tmp-${process.pid}-${randomBytes3(6).toString("hex")}`;
|
|
27369
28102
|
try {
|
|
27370
|
-
mkdirSync8(
|
|
28103
|
+
mkdirSync8(dirname9(path), { recursive: true });
|
|
27371
28104
|
writeFileSync9(tempPath, `${JSON.stringify({ release, sha256, error: markerError, at: new Date().toISOString() })}
|
|
27372
28105
|
`, { encoding: "utf8", mode: 384 });
|
|
27373
28106
|
renameSync10(tempPath, path);
|
|
@@ -27396,7 +28129,7 @@ function ghBinaryPath(spec, platform) {
|
|
|
27396
28129
|
function isGithubInstalled(spec, platform) {
|
|
27397
28130
|
for (const candidate of ghBinaryCandidates(spec, platform)) {
|
|
27398
28131
|
try {
|
|
27399
|
-
if (
|
|
28132
|
+
if (statSync12(join18(ghBinDir(spec), candidate)).isFile())
|
|
27400
28133
|
return true;
|
|
27401
28134
|
} catch {}
|
|
27402
28135
|
}
|
|
@@ -27410,9 +28143,9 @@ function ghBinaryCandidates(spec, platform) {
|
|
|
27410
28143
|
function readGithubInstalledMetaIn(installDir) {
|
|
27411
28144
|
try {
|
|
27412
28145
|
const path = join18(installDir, INSTALLED_META_FILE2);
|
|
27413
|
-
if (!
|
|
28146
|
+
if (!statSync12(path).isFile())
|
|
27414
28147
|
return null;
|
|
27415
|
-
const parsed = JSON.parse(
|
|
28148
|
+
const parsed = JSON.parse(readFileSync14(path, "utf8"));
|
|
27416
28149
|
if (typeof parsed.version !== "string" || parsed.version.length === 0)
|
|
27417
28150
|
return null;
|
|
27418
28151
|
return {
|
|
@@ -27453,7 +28186,7 @@ function sha256OfFile(path) {
|
|
|
27453
28186
|
});
|
|
27454
28187
|
}
|
|
27455
28188
|
function sha256OfFileSync2(path) {
|
|
27456
|
-
return createHash6("sha256").update(
|
|
28189
|
+
return createHash6("sha256").update(readFileSync14(path)).digest("hex");
|
|
27457
28190
|
}
|
|
27458
28191
|
async function fetchReleaseByTag(githubRepo, tag, fetchImpl, signal) {
|
|
27459
28192
|
const candidates = [];
|
|
@@ -27629,7 +28362,7 @@ async function downloadFile(url, destPath, fetchImpl, assetSize, signal) {
|
|
|
27629
28362
|
if (Number.isFinite(advertised) && advertised > MAX_DOWNLOAD_BYTES3) {
|
|
27630
28363
|
throw new Error(`Content-Length ${advertised} exceeds max ${MAX_DOWNLOAD_BYTES3}`);
|
|
27631
28364
|
}
|
|
27632
|
-
mkdirSync8(
|
|
28365
|
+
mkdirSync8(dirname9(destPath), { recursive: true });
|
|
27633
28366
|
let bytesWritten = 0;
|
|
27634
28367
|
const guard = new TransformStream({
|
|
27635
28368
|
transform(chunk, controller) {
|
|
@@ -27799,7 +28532,7 @@ function quarantineCachedGithubInstall(spec, reason) {
|
|
|
27799
28532
|
const dest = join18(ghCacheRoot(), ".quarantine", encodeURIComponent(spec.id), `${Date.now()}`);
|
|
27800
28533
|
warn2(`[lsp] tofu_mismatch ${spec.id}: ${reason}; quarantining ${packageDir} -> ${dest}`);
|
|
27801
28534
|
try {
|
|
27802
|
-
mkdirSync8(
|
|
28535
|
+
mkdirSync8(dirname9(dest), { recursive: true });
|
|
27803
28536
|
rmSync6(dest, { recursive: true, force: true });
|
|
27804
28537
|
renameSync10(packageDir, dest);
|
|
27805
28538
|
} catch (err) {
|
|
@@ -27811,7 +28544,7 @@ function validateCachedGithubInstall(spec, platform) {
|
|
|
27811
28544
|
const meta = readGithubInstalledMetaIn(packageDir);
|
|
27812
28545
|
const binaryPath = ghBinaryCandidates(spec, platform).map((candidate) => join18(ghBinDir(spec), candidate)).find((candidate) => {
|
|
27813
28546
|
try {
|
|
27814
|
-
return
|
|
28547
|
+
return statSync12(candidate).isFile();
|
|
27815
28548
|
} catch {
|
|
27816
28549
|
return false;
|
|
27817
28550
|
}
|
|
@@ -27929,7 +28662,7 @@ async function downloadAndInstall(spec, tag, assets, platform, arch, fetchImpl,
|
|
|
27929
28662
|
return { ok: false, sha256: archiveSha256, error: message };
|
|
27930
28663
|
}
|
|
27931
28664
|
const targetBinary = ghBinaryPath(spec, platform);
|
|
27932
|
-
mkdirSync8(
|
|
28665
|
+
mkdirSync8(dirname9(targetBinary), { recursive: true });
|
|
27933
28666
|
try {
|
|
27934
28667
|
copyFileSync3(innerBinaryPath, targetBinary);
|
|
27935
28668
|
if (platform !== "win32") {
|
|
@@ -28347,6 +29080,23 @@ import {
|
|
|
28347
29080
|
import { Container as Container2, Spacer as Spacer2, Text as Text2 } from "@earendil-works/pi-tui";
|
|
28348
29081
|
import { Type as Type2 } from "typebox";
|
|
28349
29082
|
|
|
29083
|
+
// src/hashline-tag-sources.ts
|
|
29084
|
+
function hashlineTagSourceSentence(bashRewritesMintTags, isRegistered = () => true) {
|
|
29085
|
+
const rewrites = bashRewritesMintTags ? " (and accepted AFT `cat`/`head`/`tail` rewrites)" : "";
|
|
29086
|
+
const lookAlikes = [
|
|
29087
|
+
...["aft_zoom", "aft_outline"].filter(isRegistered),
|
|
29088
|
+
"grep",
|
|
29089
|
+
...isRegistered("aft_search") ? ["aft_search"] : []
|
|
29090
|
+
].map((name) => `\`${name}\``);
|
|
29091
|
+
lookAlikes.push("conflict snippets");
|
|
29092
|
+
return `Only \`read\`${rewrites} mint hashline tags. ${joinList(lookAlikes)} do not. After navigation, call \`read\` on every file and range the patch addresses.`;
|
|
29093
|
+
}
|
|
29094
|
+
function joinList(items) {
|
|
29095
|
+
if (items.length <= 2)
|
|
29096
|
+
return items.join(" and ");
|
|
29097
|
+
return `${items.slice(0, -1).join(", ")}, and ${items[items.length - 1]}`;
|
|
29098
|
+
}
|
|
29099
|
+
|
|
28350
29100
|
// src/tools/diff-format.ts
|
|
28351
29101
|
import { diffLines } from "diff";
|
|
28352
29102
|
|
|
@@ -28694,7 +29444,7 @@ var ISSUE_AND_PR_READ_DESCRIPTION = "GitHub issues and pull requests can be read
|
|
|
28694
29444
|
function whenGhReadEnabled(enabled, description) {
|
|
28695
29445
|
return enabled ? description : "";
|
|
28696
29446
|
}
|
|
28697
|
-
var READ_DESCRIPTION = "Read file contents with line numbers. Backed by AFT's indexed Rust reader — faster than the built-in `read` on large repos. Images are returned as attachments on vision-capable models; PDFs and non-vision models are not yet supported.";
|
|
29447
|
+
var READ_DESCRIPTION = "Read file contents with line numbers or sorted directory entries. Use startLine/endLine or 1-based offset/limit for either. Directory limit defaults to and is capped at 1000; enumeration stops at 10,000 entries and partial listings carry a shown/total trailer. Backed by AFT's indexed Rust reader — faster than the built-in `read` on large repos. Images are returned as attachments on vision-capable models; PDFs and non-vision models are not yet supported.";
|
|
28698
29448
|
function readDescription(ghReadEnabled) {
|
|
28699
29449
|
const githubDescription = whenGhReadEnabled(ghReadEnabled, ISSUE_AND_PR_READ_DESCRIPTION);
|
|
28700
29450
|
return githubDescription ? `${READ_DESCRIPTION} ${githubDescription}` : READ_DESCRIPTION;
|
|
@@ -28790,18 +29540,20 @@ async function assertExternalDirectoryPermission(extCtx, target, options = {}) {
|
|
|
28790
29540
|
}
|
|
28791
29541
|
var ReadParams = Type2.Object({
|
|
28792
29542
|
path: Type2.String({
|
|
28793
|
-
description: "Path to the file to read (absolute or relative to project root)"
|
|
29543
|
+
description: "Path to the file or directory to read (absolute or relative to project root)"
|
|
28794
29544
|
}),
|
|
28795
|
-
startLine: optionalInt(1, Number.MAX_SAFE_INTEGER, "1-based line to start reading from"),
|
|
28796
|
-
endLine: optionalInt(1, Number.MAX_SAFE_INTEGER, "1-based line to stop reading at (inclusive)"),
|
|
28797
|
-
limit: optionalInt(1, Number.MAX_SAFE_INTEGER, "
|
|
28798
|
-
offset: optionalInt(1, Number.MAX_SAFE_INTEGER, "1-based line
|
|
29545
|
+
startLine: optionalInt(1, Number.MAX_SAFE_INTEGER, "1-based line or directory entry to start reading from"),
|
|
29546
|
+
endLine: optionalInt(1, Number.MAX_SAFE_INTEGER, "1-based line or directory entry to stop reading at (inclusive)"),
|
|
29547
|
+
limit: optionalInt(1, Number.MAX_SAFE_INTEGER, "Max lines (default: 2000) or directory entries (default and cap: 1000) to return"),
|
|
29548
|
+
offset: optionalInt(1, Number.MAX_SAFE_INTEGER, "1-based line or directory entry to start reading from (use with limit). Ignored if startLine is provided")
|
|
28799
29549
|
});
|
|
28800
29550
|
var WriteParams = Type2.Object({
|
|
28801
29551
|
path: Type2.String({
|
|
28802
29552
|
description: "Path to the file to write (absolute or relative to project root)"
|
|
28803
29553
|
}),
|
|
28804
|
-
content: Type2.String({
|
|
29554
|
+
content: Type2.String({
|
|
29555
|
+
description: "Full file contents to write; for issue://N / pr://N, the comment body to post"
|
|
29556
|
+
})
|
|
28805
29557
|
});
|
|
28806
29558
|
var BatchEditParams = Type2.Object({
|
|
28807
29559
|
oldString: Type2.Optional(Type2.String({ description: "Text to find for a batch find/replace edit" })),
|
|
@@ -28825,14 +29577,14 @@ var EditParams = Type2.Object({
|
|
|
28825
29577
|
description: "Replacement content for symbol mode; only meaningful together with `symbol`. A call carrying one without the other is incomplete. For whole-file writes, use the `write` tool."
|
|
28826
29578
|
})),
|
|
28827
29579
|
appendContent: Type2.Optional(Type2.String({
|
|
28828
|
-
description: "Append text to the end of the file (creates the file if missing, parent dirs auto-created).
|
|
29580
|
+
description: "Append text to the end of the file (creates the file if missing, parent dirs auto-created). Mixing with other edit modes is rejected."
|
|
28829
29581
|
})),
|
|
28830
29582
|
edits: Type2.Optional(Type2.Array(BatchEditParams, {
|
|
28831
29583
|
minItems: 1,
|
|
28832
29584
|
description: "Batch edits — non-empty array of { oldString, newString }, { oldString, newString, replaceAll: true }, or { startLine, endLine, content } objects applied atomically."
|
|
28833
29585
|
}))
|
|
28834
29586
|
});
|
|
28835
|
-
var
|
|
29587
|
+
var HASHLINE_EDIT_GRAMMAR = [
|
|
28836
29588
|
"Apply a hashline patch. Arguments are exactly `{patch}` where `patch` is a non-empty string. Server-owned preview control is outside this schema.",
|
|
28837
29589
|
"",
|
|
28838
29590
|
"Quick reference:",
|
|
@@ -28840,10 +29592,15 @@ var HASHLINE_EDIT_DESCRIPTION = [
|
|
|
28840
29592
|
"- Same canonical path: multiple sections compose in patch order against pre-request coordinates.",
|
|
28841
29593
|
"- Addresses: `0` (BOF), `N` (one line), `N.=M` (range; `N..=M`/`N..M` also work), `<N`/`>N` (gap before/after), `N*`/`<N*`/`>N*` (block), and `$`/`$-K` (EOF-relative). A plain `N` PUT replaces; use `<N` or `>N` to insert.",
|
|
28842
29594
|
"- PUT text: `PUT <address>:` followed by one or more `+` body rows (`+` alone is blank). A final patch newline is allowed. PUT without `:` copies `@name` (or the anonymous register) and takes no body; names use `@` plus ASCII letters, digits, `_`, or `-`.",
|
|
28843
|
-
"- CUT: `CUT <address> [@name]`. REM: bare `REM` only, removing the whole file. MV: `MV <destination>` (one whitespace-free path, optional matching quotes), once and after any line operations. `*** Begin Patch`/`*** End Patch` is an optional envelope."
|
|
28844
|
-
"- Only `read` (and accepted AFT `cat`/`head`/`tail` rewrites) mint hashline tags. `aft_zoom`, `aft_outline`, `grep`, `aft_search`, and conflict snippets do not. After navigation, call `read` on every file and range the patch addresses."
|
|
29595
|
+
"- CUT: `CUT <address> [@name]`. REM: bare `REM` only, removing the whole file. MV: `MV <destination>` (one whitespace-free path, optional matching quotes), once and after any line operations. `*** Begin Patch`/`*** End Patch` is an optional envelope."
|
|
28845
29596
|
].join(`
|
|
28846
29597
|
`);
|
|
29598
|
+
function hashlineEditDescription(config) {
|
|
29599
|
+
const bashRewrites = toolEnabled(config, "bash") && resolveBashConfig(config).rewrite;
|
|
29600
|
+
const tagSources = hashlineTagSourceSentence(bashRewrites, (name) => toolEnabled(config, name));
|
|
29601
|
+
return `${HASHLINE_EDIT_GRAMMAR}
|
|
29602
|
+
- ${tagSources}`;
|
|
29603
|
+
}
|
|
28847
29604
|
var HashlineEditParams = Type2.Object({
|
|
28848
29605
|
patch: Type2.String({
|
|
28849
29606
|
minLength: 1,
|
|
@@ -28930,7 +29687,7 @@ function registerHoistedTools(pi, ctx, surface) {
|
|
|
28930
29687
|
name: readName,
|
|
28931
29688
|
label: readName,
|
|
28932
29689
|
description: readDescription(resolveGithubConfig(ctx.config).read),
|
|
28933
|
-
promptSnippet: "Read
|
|
29690
|
+
promptSnippet: "Read files or directories (supports offset/limit for either)",
|
|
28934
29691
|
promptGuidelines: [`Use ${readName} to examine files instead of cat or sed.`],
|
|
28935
29692
|
parameters: ReadParams,
|
|
28936
29693
|
async execute(_toolCallId, params, _signal, _onUpdate, extCtx) {
|
|
@@ -29057,7 +29814,7 @@ PDFs aren't supported on the Pi harness yet.`, response);
|
|
|
29057
29814
|
pi.registerTool({
|
|
29058
29815
|
name: editName,
|
|
29059
29816
|
label: editName,
|
|
29060
|
-
description:
|
|
29817
|
+
description: hashlineEditDescription(ctx.config),
|
|
29061
29818
|
promptSnippet: `Apply a tagged hashline patch through the ${editName} tool`,
|
|
29062
29819
|
promptGuidelines: [
|
|
29063
29820
|
"Read a file to obtain its current hashline tag before editing it.",
|
|
@@ -29115,8 +29872,7 @@ PDFs aren't supported on the Pi harness yet.`, response);
|
|
|
29115
29872
|
}
|
|
29116
29873
|
if (isGithubResourcePath(filePathArg)) {
|
|
29117
29874
|
const edits = params.edits;
|
|
29118
|
-
|
|
29119
|
-
if (!onlyFindReplace) {
|
|
29875
|
+
if (!isFindReplaceOnlyEdit(argsRecord)) {
|
|
29120
29876
|
throw new Error("edit: GitHub resources support only edits[] find/replace entries with oldString and optional newString");
|
|
29121
29877
|
}
|
|
29122
29878
|
const bridge = bridgeFor(ctx, extCtx.cwd);
|
|
@@ -29644,34 +30400,17 @@ function registerAstTools(pi, ctx, surface) {
|
|
|
29644
30400
|
// src/tools/bash.ts
|
|
29645
30401
|
import { Container as Container3, Spacer as Spacer3, Text as Text3 } from "@earendil-works/pi-tui";
|
|
29646
30402
|
import { Type as Type4 } from "typebox";
|
|
29647
|
-
|
|
29648
|
-
// src/session-kind.ts
|
|
29649
|
-
var MAGIC_CONTEXT_SUBAGENT_ENV = "MAGIC_CONTEXT_PI_SUBAGENT";
|
|
29650
|
-
function piWorkerKind(extCtx, env = process.env) {
|
|
29651
|
-
if (env[MAGIC_CONTEXT_SUBAGENT_ENV] === "1")
|
|
29652
|
-
return "delegated";
|
|
29653
|
-
if (extCtx?.hasUI === false)
|
|
29654
|
-
return "headless";
|
|
29655
|
-
return;
|
|
29656
|
-
}
|
|
29657
|
-
function isPiWorkerSession(extCtx, env = process.env) {
|
|
29658
|
-
return piWorkerKind(extCtx, env) !== undefined;
|
|
29659
|
-
}
|
|
29660
|
-
function skipsEagerStartup(env = process.env) {
|
|
29661
|
-
return piWorkerKind(undefined, env) === "delegated";
|
|
29662
|
-
}
|
|
29663
|
-
|
|
29664
|
-
// src/tools/bash.ts
|
|
29665
|
-
var BASH_WAIT_POLL_INTERVAL_MS = 100;
|
|
29666
30403
|
var REGEX_WAIT_SCAN_WINDOW_BYTES = 64 * 1024;
|
|
29667
30404
|
function watchCallerRole(extCtx, env = process.env) {
|
|
29668
30405
|
return isPiWorkerSession(extCtx, env) ? "worker" : "primary";
|
|
29669
30406
|
}
|
|
29670
|
-
function coerceConfiguredWatchTimeout(value, cap) {
|
|
30407
|
+
function coerceConfiguredWatchTimeout(value, role, cap) {
|
|
29671
30408
|
try {
|
|
29672
|
-
return coerceOptionalInt(value, "timeoutMs", 1, cap);
|
|
30409
|
+
return coerceOptionalInt(value, "timeoutMs", 1, maxWatchTimeoutMs(role, cap));
|
|
29673
30410
|
} catch (error) {
|
|
29674
30411
|
const message = error instanceof Error ? error.message : String(error);
|
|
30412
|
+
if (role === "worker")
|
|
30413
|
+
throw error;
|
|
29675
30414
|
throw new Error(`${message} (bash.watch_sync_max_ms)`);
|
|
29676
30415
|
}
|
|
29677
30416
|
}
|
|
@@ -29687,7 +30426,9 @@ function resolveForegroundWaitMs(configured) {
|
|
|
29687
30426
|
var BASH_TRANSPORT_TIMEOUT_MS2 = 30000;
|
|
29688
30427
|
var DEFAULT_HARD_TIMEOUT_MS = 30 * 60 * 1000;
|
|
29689
30428
|
var BASH_TRANSPORT_MARGIN_MS = 1e4;
|
|
29690
|
-
function orchestratedTransportTimeoutMs(blockToCompletion, wait, effectiveTimeout, foregroundWaitMs) {
|
|
30429
|
+
function orchestratedTransportTimeoutMs(blockToCompletion, wait, effectiveTimeout, foregroundWaitMs, noHardKill = false) {
|
|
30430
|
+
if (noHardKill)
|
|
30431
|
+
return LONGEST_TIMER_DELAY_MS;
|
|
29691
30432
|
const waitBudget = blockToCompletion || wait ? effectiveTimeout ?? DEFAULT_HARD_TIMEOUT_MS : foregroundWaitMs;
|
|
29692
30433
|
return waitBudget + BASH_TRANSPORT_MARGIN_MS;
|
|
29693
30434
|
}
|
|
@@ -29701,10 +30442,14 @@ var BashBaseParams = {
|
|
|
29701
30442
|
})),
|
|
29702
30443
|
description: Type4.Optional(Type4.String({
|
|
29703
30444
|
description: "Human-readable description shown in UI logs. Helps users understand what the command does without reading shell syntax."
|
|
29704
|
-
}))
|
|
30445
|
+
}))
|
|
30446
|
+
};
|
|
30447
|
+
var BashWaitParam = {
|
|
29705
30448
|
wait: Type4.Optional(Type4.Boolean({
|
|
29706
30449
|
description: "When true, run in the foreground without auto-promoting and wait until the command finishes or reaches its timeout; any new message detaches by default, while `bash.detach_on_user_message: false` keeps it blocking unless the message contains the literal `&detach`. The token is stripped before delivery; the rest of the message is preserved, and a token-only message becomes `(requested background detach)`. Use only when you know the result is required before doing anything else."
|
|
29707
|
-
}))
|
|
30450
|
+
}))
|
|
30451
|
+
};
|
|
30452
|
+
var BashSandboxParam = {
|
|
29708
30453
|
sandbox: Type4.Optional(Type4.Literal("host", {
|
|
29709
30454
|
description: "Request one-command approval to run unsandboxed on the host; use only when native sandboxing blocks required work, and note that it is a no-op when sandboxing is disabled."
|
|
29710
30455
|
}))
|
|
@@ -29728,16 +30473,65 @@ var BashPtyParams = {
|
|
|
29728
30473
|
};
|
|
29729
30474
|
var BashParams = Type4.Object({
|
|
29730
30475
|
...BashBaseParams,
|
|
30476
|
+
...BashWaitParam,
|
|
30477
|
+
...BashSandboxParam,
|
|
29731
30478
|
...BashBackgroundFlagParam,
|
|
29732
30479
|
...BashCompressionParam,
|
|
29733
30480
|
...BashPtyParams
|
|
29734
30481
|
});
|
|
29735
|
-
|
|
29736
|
-
|
|
29737
|
-
|
|
29738
|
-
}
|
|
29739
|
-
|
|
29740
|
-
|
|
30482
|
+
function bashParamsForConfig(features, companions) {
|
|
30483
|
+
return Type4.Object({
|
|
30484
|
+
...BashBaseParams,
|
|
30485
|
+
...features.background ? BashWaitParam : {},
|
|
30486
|
+
...features.sandbox ? BashSandboxParam : {},
|
|
30487
|
+
...features.background ? {
|
|
30488
|
+
background: Type4.Optional(Type4.Boolean({ description: backgroundParamDescription(companions) }))
|
|
30489
|
+
} : {},
|
|
30490
|
+
...features.compress ? BashCompressionParam : {},
|
|
30491
|
+
...features.background ? {
|
|
30492
|
+
...BashPtyParams,
|
|
30493
|
+
pty: Type4.Optional(Type4.Boolean({
|
|
30494
|
+
description: ptyParamDescription(companions, features.subagentBackground)
|
|
30495
|
+
}))
|
|
30496
|
+
} : {}
|
|
30497
|
+
});
|
|
30498
|
+
}
|
|
30499
|
+
function bashCompanionRegistered(config, companion) {
|
|
30500
|
+
return resolveBashConfig(config).background && toolEnabled(config, companion);
|
|
30501
|
+
}
|
|
30502
|
+
function registeredCompanions(config) {
|
|
30503
|
+
return {
|
|
30504
|
+
status: bashCompanionRegistered(config, "bash_status"),
|
|
30505
|
+
watch: bashCompanionRegistered(config, "bash_watch"),
|
|
30506
|
+
write: bashCompanionRegistered(config, "bash_write"),
|
|
30507
|
+
kill: bashCompanionRegistered(config, "bash_kill")
|
|
30508
|
+
};
|
|
30509
|
+
}
|
|
30510
|
+
function backgroundParamDescription(c) {
|
|
30511
|
+
let controls = "";
|
|
30512
|
+
if (c.watch) {
|
|
30513
|
+
controls += ` Use bash_watch to wait for completion or output patterns${c.status ? "; bash_status for a one-shot snapshot only." : "."}`;
|
|
30514
|
+
} else if (c.status) {
|
|
30515
|
+
controls += " Use bash_status for a one-shot snapshot.";
|
|
30516
|
+
}
|
|
30517
|
+
if (c.kill)
|
|
30518
|
+
controls += " Use bash_kill to terminate.";
|
|
30519
|
+
return `Spawn command in background and return immediately with a task_id.${controls} Ideal for long-running tasks like builds or dev servers.`;
|
|
30520
|
+
}
|
|
30521
|
+
function ptyParamDescription(c, subagentBackground) {
|
|
30522
|
+
let drive = "";
|
|
30523
|
+
if (c.status && c.write) {
|
|
30524
|
+
drive = ' Inspect with bash_status({ task_id, output_mode: "screen" }) and send input with bash_write.';
|
|
30525
|
+
} else if (c.status) {
|
|
30526
|
+
drive = ' Inspect with bash_status({ task_id, output_mode: "screen" }).';
|
|
30527
|
+
} else if (c.write) {
|
|
30528
|
+
drive = " Send input with bash_write.";
|
|
30529
|
+
}
|
|
30530
|
+
const workers = subagentBackground ? "" : " Unavailable in worker sessions because bash.subagent_background is false.";
|
|
30531
|
+
return `Spawn the command in a real PTY for interactive programs. Implies background: true automatically.${workers}${drive}`;
|
|
30532
|
+
}
|
|
30533
|
+
function nativeSandboxEnabled(config) {
|
|
30534
|
+
return config.sandbox?.enabled === true;
|
|
29741
30535
|
}
|
|
29742
30536
|
var BashTaskParams = Type4.Object({
|
|
29743
30537
|
task_id: Type4.String({
|
|
@@ -29869,18 +30663,47 @@ function getBashSpawnHook(pi) {
|
|
|
29869
30663
|
}
|
|
29870
30664
|
return api.hooks?.bashSpawn;
|
|
29871
30665
|
}
|
|
30666
|
+
function backgroundWaitSentence(c) {
|
|
30667
|
+
if (c.watch) {
|
|
30668
|
+
const noPolling = c.status ? ", and never loop `bash_status` to wait" : "";
|
|
30669
|
+
return `then \`bash_watch\` handles only a short remaining wait (in a main session a watch defaults to 30s, max bash.watch_sync_max_ms, 120s by default; in a delegated session a watch without a timeout waits until the command finishes); for anything longer end the turn and let the completion reminder wake you, or use bash({wait:true}) when the result is needed before anything else — never background a command and immediately \`bash_watch\` it (that wastes a turn for what foreground returns in one)${noPolling}.`;
|
|
30670
|
+
}
|
|
30671
|
+
return c.status ? "the task keeps running after the call returns, a completion reminder arrives when it exits, and `bash_status` reports its state and output. Use bash({wait:true}) instead when the result is needed before anything else." : "the task keeps running after the call returns and a completion reminder arrives when it exits. Use bash({wait:true}) instead when the result is needed before anything else.";
|
|
30672
|
+
}
|
|
30673
|
+
function ptyDriveClause(c) {
|
|
30674
|
+
if (c.status && c.write) {
|
|
30675
|
+
return ', and is driven with `bash_status({ output_mode: "screen" })` plus `bash_write`';
|
|
30676
|
+
}
|
|
30677
|
+
if (c.status)
|
|
30678
|
+
return ', and its screen is read with `bash_status({ output_mode: "screen" })`';
|
|
30679
|
+
if (c.write)
|
|
30680
|
+
return ", and is driven with `bash_write`";
|
|
30681
|
+
return "";
|
|
30682
|
+
}
|
|
29872
30683
|
function registerBashTool(pi, ctx, aftSearchRegistered = false, registeredName = "bash", registerCompanions = true, shell = "bash") {
|
|
29873
30684
|
const isPowerShell = shell === "powershell";
|
|
29874
30685
|
const spawnHook = isPowerShell ? undefined : getBashSpawnHook(pi);
|
|
29875
30686
|
const readToolName = "read";
|
|
29876
30687
|
const grepToolName = "grep";
|
|
29877
30688
|
const zoomSteer = toolEnabled(ctx.config, "aft_zoom") ? ", or `aft_zoom`" : "";
|
|
29878
|
-
const
|
|
30689
|
+
const steerTools = [
|
|
30690
|
+
aftSearchRegistered ? "`aft_search` (concepts, identifiers, regex, literals)" : `the \`${grepToolName}\` tool`,
|
|
30691
|
+
`\`${readToolName}\``,
|
|
30692
|
+
...toolEnabled(ctx.config, "aft_outline") ? ["`aft_outline`"] : []
|
|
30693
|
+
].join(", ");
|
|
30694
|
+
const searchSteer = `use ${steerTools}${zoomSteer} instead`;
|
|
30695
|
+
const companions = registeredCompanions(ctx.config);
|
|
29879
30696
|
const bashCfg = resolveBashConfig(ctx.config);
|
|
29880
30697
|
let hostFallbackActive = false;
|
|
29881
30698
|
const compressionSentence = bashCfg.compress ? " Output is compressed by default; pass `compressed: false` for raw output. Piped commands run verbatim and show the pipeline's output; for AFT's test/build summary, run the runner without `| head`, `| tail`, or `| grep`." : "";
|
|
30699
|
+
const supported = bashCfg.background ? [
|
|
30700
|
+
"workdir",
|
|
30701
|
+
"background tasks",
|
|
30702
|
+
...bashCfg.compress ? ["compressed output"] : [],
|
|
30703
|
+
"PTY mode"
|
|
30704
|
+
].join(", ") : bashCfg.compress ? "workdir and compressed output" : "workdir";
|
|
29882
30705
|
const detachSentence = bashCfg.detach_on_user_message ? "Any new message detaches this wait. Set `bash.detach_on_user_message: false` to keep it blocking; even then, a message containing the literal `&detach` forces detachment, and the token is stripped before delivery; the rest of the message is preserved, while a token-only message becomes `(requested background detach)`." : "Because `bash.detach_on_user_message` is false, a new message leaves this wait blocking; include the literal `&detach` anywhere to force detachment, and the token is stripped before delivery; the rest of the message is preserved, while a token-only message becomes `(requested background detach)`.";
|
|
29883
|
-
const tasksSentence = bashCfg.background ? ` Commands run in the foreground and return inline; \`wait: true\` blocks until a long command finishes instead of auto-promoting; ${detachSentence} Use it when you need the result before doing anything else; keep it off otherwise so auto-promote can remind you while you work. Use \`background: true\` yourself ONLY when you have other useful work to do while it runs;
|
|
30706
|
+
const tasksSentence = bashCfg.background ? ` Commands run in the foreground and return inline; \`wait: true\` blocks until a long command finishes instead of auto-promoting; ${detachSentence} Use it when you need the result before doing anything else; keep it off otherwise so auto-promote can remind you while you work. Use \`background: true\` yourself ONLY when you have other useful work to do while it runs; ${backgroundWaitSentence(companions)} A \`nohup … &\` launch still holds the call if the child keeps stdout/stderr; redirect both or use background:true. \`pty: true\` runs interactive programs (REPLs, TUIs), implies background${ptyDriveClause(companions)}.` : " Commands run in the foreground to completion; `timeout` is the hard kill cap (default 30 minutes).";
|
|
29884
30707
|
pi.registerTool({
|
|
29885
30708
|
name: registeredName,
|
|
29886
30709
|
label: registeredName,
|
|
@@ -29889,13 +30712,18 @@ function registerBashTool(pi, ctx, aftSearchRegistered = false, registeredName =
|
|
|
29889
30712
|
PowerShell syntax is not analyzed as POSIX shell. Each command requires explicit approval so syntax AFT cannot safely interpret is never auto-allowed.` : `Execute shell commands.${compressionSentence}${tasksSentence}
|
|
29890
30713
|
|
|
29891
30714
|
DO NOT use bash for code search or code exploration. If you are about to run grep, rg, sed, awk, find, or cat through bash to locate or read code: STOP — ${searchSteer}. When a list is cut, the reply ends with \`shown N of M <unit> (<reason>) · narrow: <knobs>\`; absence of that line means the list is complete.`,
|
|
29892
|
-
promptSnippet: isPowerShell ?
|
|
30715
|
+
promptSnippet: isPowerShell ? `Run PowerShell commands (timeout in milliseconds; supports ${supported})` : `Run shell commands (timeout in milliseconds; supports ${supported})`,
|
|
29893
30716
|
promptGuidelines: isPowerShell ? ["Use PowerShell syntax. Every command requires explicit approval."] : [
|
|
29894
30717
|
`DO NOT use bash for code search or exploration — ${searchSteer}.`,
|
|
29895
|
-
"Set compressed: false when you need ANSI color codes in the output.",
|
|
30718
|
+
...bashCfg.compress ? ["Set compressed: false when you need ANSI color codes in the output."] : [],
|
|
29896
30719
|
"Piped commands run verbatim and show the pipeline's output; run test/build tools without pipes when you need AFT's summary."
|
|
29897
30720
|
],
|
|
29898
|
-
parameters: bashParamsForConfig(
|
|
30721
|
+
parameters: bashParamsForConfig({
|
|
30722
|
+
background: bashCfg.background,
|
|
30723
|
+
compress: bashCfg.compress,
|
|
30724
|
+
sandbox: nativeSandboxEnabled(ctx.config),
|
|
30725
|
+
subagentBackground: bashCfg.subagent_background
|
|
30726
|
+
}, companions),
|
|
29899
30727
|
async execute(_toolCallId, params, signal, onUpdate, extCtx) {
|
|
29900
30728
|
const bridge = bridgeFor(ctx, extCtx.cwd);
|
|
29901
30729
|
const bashCfg = resolveBashConfig(ctx.config);
|
|
@@ -29905,7 +30733,7 @@ DO NOT use bash for code search or code exploration. If you are about to run gre
|
|
|
29905
30733
|
const ptyRows = backgroundDisabled ? undefined : coerceOptionalInt(params.ptyRows, "ptyRows", 1, 60);
|
|
29906
30734
|
const ptyCols = backgroundDisabled ? undefined : coerceOptionalInt(params.ptyCols, "ptyCols", 1, 140);
|
|
29907
30735
|
const compressed = coerceBoolean(params.compressed, true);
|
|
29908
|
-
const requestedWait = coerceBoolean(params.wait);
|
|
30736
|
+
const requestedWait = !backgroundDisabled && coerceBoolean(params.wait);
|
|
29909
30737
|
const rawRequestedPty = coerceBoolean(params.pty);
|
|
29910
30738
|
const rawRequestedBackground = coerceBoolean(params.background);
|
|
29911
30739
|
if (requestedWait && rawRequestedPty) {
|
|
@@ -29921,6 +30749,8 @@ DO NOT use bash for code search or code exploration. If you are about to run gre
|
|
|
29921
30749
|
}
|
|
29922
30750
|
const blockToCompletion = backgroundDisabled || requestedWait || workerForcedForeground;
|
|
29923
30751
|
const effectiveBackground = !blockToCompletion && (rawRequestedBackground || requestedPty);
|
|
30752
|
+
const isWorker = isPiWorkerSession(extCtx);
|
|
30753
|
+
const workerUnboundedWait = isWorker && requestedWait && timeout === undefined;
|
|
29924
30754
|
let spawnContext = {
|
|
29925
30755
|
command: params.command,
|
|
29926
30756
|
cwd: params.workdir
|
|
@@ -29955,7 +30785,7 @@ DO NOT use bash for code search or code exploration. If you are about to run gre
|
|
|
29955
30785
|
sandbox: params.sandbox,
|
|
29956
30786
|
...isPowerShell ? { shell: "powershell" } : {}
|
|
29957
30787
|
}, extCtx, {
|
|
29958
|
-
transportTimeoutMs: orchestratedTransportTimeoutMs(blockToCompletion, requestedWait, timeout, foregroundWaitMs),
|
|
30788
|
+
transportTimeoutMs: orchestratedTransportTimeoutMs(blockToCompletion, requestedWait, timeout, foregroundWaitMs, workerUnboundedWait),
|
|
29959
30789
|
onProgress: ({ text }) => {
|
|
29960
30790
|
streamed += text;
|
|
29961
30791
|
const displayText = truncateToVisualLines(streamed, 100);
|
|
@@ -29966,10 +30796,10 @@ DO NOT use bash for code search or code exploration. If you are about to run gre
|
|
|
29966
30796
|
const fallbackCause = classifyBashHostFallbackError(error);
|
|
29967
30797
|
if (isPowerShell || !bashCfg.host_fallback || fallbackCause === undefined)
|
|
29968
30798
|
throw error;
|
|
29969
|
-
if (rawRequestedBackground) {
|
|
30799
|
+
if (!backgroundDisabled && rawRequestedBackground) {
|
|
29970
30800
|
throw new Error(`${BASH_HOST_FALLBACK_REFUSAL}; background:true is unsupported.`);
|
|
29971
30801
|
}
|
|
29972
|
-
if (
|
|
30802
|
+
if (requestedPty) {
|
|
29973
30803
|
throw new Error(`${BASH_HOST_FALLBACK_REFUSAL}; pty:true is unsupported.`);
|
|
29974
30804
|
}
|
|
29975
30805
|
if (!extCtx.hasUI || typeof extCtx.ui?.confirm !== "function") {
|
|
@@ -30024,6 +30854,8 @@ DO NOT use bash for code search or code exploration. If you are about to run gre
|
|
|
30024
30854
|
registerBashCompanionTools(pi, ctx);
|
|
30025
30855
|
}
|
|
30026
30856
|
function registerBashCompanionTools(pi, ctx, enabled = { bashStatus: true, bashWatch: true, bashWrite: true, bashKill: true }) {
|
|
30857
|
+
if (!resolveBashConfig(ctx.config).background)
|
|
30858
|
+
return;
|
|
30027
30859
|
if (enabled.bashStatus) {
|
|
30028
30860
|
pi.registerTool(createBashStatusTool(ctx));
|
|
30029
30861
|
}
|
|
@@ -30044,7 +30876,7 @@ function createBashStatusTool(ctx) {
|
|
|
30044
30876
|
return {
|
|
30045
30877
|
name: "bash_status",
|
|
30046
30878
|
label: "bash_status",
|
|
30047
|
-
description:
|
|
30879
|
+
description: `Read-only snapshot of a background bash task. Returns immediately. Never waits. One look to check on a task is fine — never loop it to wait for completion.${bashCompanionRegistered(ctx.config, "bash_watch") ? " To wait, use bash_watch." : ""}`,
|
|
30048
30880
|
promptSnippet: "Inspect a background bash task by task_id",
|
|
30049
30881
|
parameters: BashStatusParams,
|
|
30050
30882
|
async execute(_toolCallId, params, _signal, _onUpdate, extCtx) {
|
|
@@ -30062,10 +30894,10 @@ function createBashWatchTool(ctx) {
|
|
|
30062
30894
|
return {
|
|
30063
30895
|
name: "bash_watch",
|
|
30064
30896
|
label: "bash_watch",
|
|
30065
|
-
description:
|
|
30897
|
+
description: `Watch a background bash task. ${WATCH_SYNC_DEFAULTS_DESCRIPTION}. In a main session sync waits are for a short remaining wait on a task; for anything longer end the turn on \`bash({background:true})\` and let the completion reminder wake you, or use \`bash({wait:true})\` when the result is needed before anything else. The user can interrupt anytime; the wait auto-converts to an async notification. Async (background:true, requires pattern) registers a non-blocking notification and returns immediately — use when you have parallel work or want to end your turn.${bashCompanionRegistered(ctx.config, "bash_status") ? " Never loop bash_status to wait." : ""}`,
|
|
30066
30898
|
promptSnippet: "Wait for or watch a background bash task",
|
|
30067
30899
|
parameters: BashWatchParams,
|
|
30068
|
-
async execute(_toolCallId, params,
|
|
30900
|
+
async execute(_toolCallId, params, signal, _onUpdate, extCtx) {
|
|
30069
30901
|
const bridge = bridgeFor(ctx, extCtx.cwd);
|
|
30070
30902
|
const waitFor = parseWaitPattern(params.pattern);
|
|
30071
30903
|
const bashCfg = resolveBashConfig(ctx.config);
|
|
@@ -30102,10 +30934,10 @@ A notification will fire when the pattern matches or the task exits.`, watchDeta
|
|
|
30102
30934
|
}
|
|
30103
30935
|
const syncWaitCap = bashCfg.watch_sync_max_ms;
|
|
30104
30936
|
const role = watchCallerRole(extCtx);
|
|
30105
|
-
const effectiveWaitMs = workerForcedSync ?
|
|
30106
|
-
const data = await waitForBashStatus(ctx, bridge, extCtx, params.task_id, undefined, waitFor, true, effectiveWaitMs);
|
|
30937
|
+
const effectiveWaitMs = workerForcedSync ? undefined : resolveWatchTimeoutMs(coerceConfiguredWatchTimeout(params.timeout_ms, role, syncWaitCap), role, syncWaitCap);
|
|
30938
|
+
const data = await waitForBashStatus(ctx, bridge, extCtx, params.task_id, undefined, waitFor, true, effectiveWaitMs, signal);
|
|
30107
30939
|
if (data.waited?.reason === "user_message") {
|
|
30108
|
-
const convertedText = await convertToAsyncWatchOnAbort(bridge, extCtx, params.task_id, waitFor, coerceBoolean(params.once, true));
|
|
30940
|
+
const convertedText = await convertToAsyncWatchOnAbort(bridge, extCtx, params.task_id, waitFor, coerceBoolean(params.once, true), data.waited.elapsed_ms, role);
|
|
30109
30941
|
return textResult(convertedText, { waited: data.waited });
|
|
30110
30942
|
}
|
|
30111
30943
|
const text = await formatBashStatus(extCtx, params.task_id, data, undefined, { role, capMs: syncWaitCap });
|
|
@@ -30113,9 +30945,11 @@ A notification will fire when the pattern matches or the task exits.`, watchDeta
|
|
|
30113
30945
|
}
|
|
30114
30946
|
};
|
|
30115
30947
|
}
|
|
30116
|
-
async function convertToAsyncWatchOnAbort(bridge, extCtx, taskId, waitFor, once) {
|
|
30948
|
+
async function convertToAsyncWatchOnAbort(bridge, extCtx, taskId, waitFor, once, elapsedMs, role) {
|
|
30949
|
+
const interrupted = `Sync watch for task ${taskId} was interrupted because you sent a message after ${elapsedMs}ms of waiting. `;
|
|
30950
|
+
const reminderFallback = interruptedWatchTail(role, `The task is still running in the background. A completion reminder will be ` + `delivered automatically when the task exits.`);
|
|
30117
30951
|
if (!waitFor) {
|
|
30118
|
-
return
|
|
30952
|
+
return interrupted + interruptedWatchTail(role, `The task is still running in the background. A completion reminder will be ` + `delivered automatically when the task exits; don't poll bash_status.`);
|
|
30119
30953
|
}
|
|
30120
30954
|
const notifyParams = {
|
|
30121
30955
|
task_id: taskId,
|
|
@@ -30131,19 +30965,19 @@ async function convertToAsyncWatchOnAbort(bridge, extCtx, taskId, waitFor, once)
|
|
|
30131
30965
|
const registered = await callBashBridge(bridge, "bash_notify", notifyParams, extCtx);
|
|
30132
30966
|
if (registered.success === false) {
|
|
30133
30967
|
unmarkExplicitControl(sessionId, taskId);
|
|
30134
|
-
return
|
|
30968
|
+
return interrupted + `Auto-registering an async watch failed (${String(registered.message ?? "unknown error")}). ` + reminderFallback;
|
|
30135
30969
|
}
|
|
30136
|
-
return
|
|
30970
|
+
return interrupted + `The wait has been converted to an async watch (${registered.watch_id}). ` + interruptedWatchTail(role, `A notification will fire when the pattern matches or the task exits.`);
|
|
30137
30971
|
} catch (err) {
|
|
30138
30972
|
unmarkExplicitControl(sessionId, taskId);
|
|
30139
|
-
return
|
|
30973
|
+
return interrupted + `Auto-registering an async watch failed (${err instanceof Error ? err.message : String(err)}). ` + reminderFallback;
|
|
30140
30974
|
}
|
|
30141
30975
|
}
|
|
30142
30976
|
function createBashWriteTool(ctx) {
|
|
30143
30977
|
return {
|
|
30144
30978
|
name: "bash_write",
|
|
30145
30979
|
label: "bash_write",
|
|
30146
|
-
description:
|
|
30980
|
+
description: `Write input bytes to a running PTY bash task. PTY-only${bashCompanionRegistered(ctx.config, "bash_status") ? '; check bash_status reports mode: "pty" first.' : "."} ` + 'Input is either a string (verbatim bytes) or an array mixing strings and { key: "esc" | "enter" | "up" | "ctrl-c" | ... } objects ' + 'for atomic text+key sequences such as [ "iHello", { key: "esc" }, ":wq", { key: "enter" } ]. ' + "Named keys cover enter/return/tab/space/backspace/esc/escape, arrows, home/end/page-up/page-down/delete/insert, f1..f12, and ctrl-a..ctrl-z. " + "Maximum 1 MiB per call (post-expansion).",
|
|
30147
30981
|
promptSnippet: "Write keystrokes/input to a PTY bash task",
|
|
30148
30982
|
parameters: BashWriteParams,
|
|
30149
30983
|
async execute(_toolCallId, params, _signal, _onUpdate, extCtx) {
|
|
@@ -30207,9 +31041,17 @@ async function bashStatusSnapshot(bridge, extCtx, taskId, outputMode, options, c
|
|
|
30207
31041
|
stderr_offset: cursor?.stderr
|
|
30208
31042
|
}, extCtx, options);
|
|
30209
31043
|
}
|
|
30210
|
-
async function waitForBashStatus(ctx, bridge, extCtx, taskId, outputMode, waitFor, waitForExit, effectiveWaitMs) {
|
|
30211
|
-
const startedAt =
|
|
30212
|
-
const deadline = startedAt + effectiveWaitMs;
|
|
31044
|
+
async function waitForBashStatus(ctx, bridge, extCtx, taskId, outputMode, waitFor, waitForExit, effectiveWaitMs, abortSignal) {
|
|
31045
|
+
const startedAt = watchClock.now();
|
|
31046
|
+
const deadline = effectiveWaitMs === undefined ? Number.POSITIVE_INFINITY : startedAt + effectiveWaitMs;
|
|
31047
|
+
const elapsedMs = () => Math.round(watchClock.now() - startedAt);
|
|
31048
|
+
const pause = () => watchClock.sleep(Math.min(watchPollDelayMs(elapsedMs()), Math.max(0, deadline - watchClock.now())), abortSignal);
|
|
31049
|
+
const waited = (reason, extra = {}) => ({
|
|
31050
|
+
reason,
|
|
31051
|
+
elapsed_ms: elapsedMs(),
|
|
31052
|
+
...effectiveWaitMs === undefined ? {} : { limit_ms: effectiveWaitMs },
|
|
31053
|
+
...extra
|
|
31054
|
+
});
|
|
30213
31055
|
let spillCursor = { output: 0, stderr: 0 };
|
|
30214
31056
|
const scanState = {
|
|
30215
31057
|
output: { text: "", baseOffset: 0 },
|
|
@@ -30228,29 +31070,29 @@ async function waitForBashStatus(ctx, bridge, extCtx, taskId, outputMode, waitFo
|
|
|
30228
31070
|
markTaskWaiting(sessionId, taskId);
|
|
30229
31071
|
let sawTerminal = false;
|
|
30230
31072
|
let lastData;
|
|
31073
|
+
let busySince;
|
|
30231
31074
|
try {
|
|
30232
31075
|
while (true) {
|
|
31076
|
+
if (abortSignal?.aborted) {
|
|
31077
|
+
return withWaited(lastData ?? unavailableSnapshot(), waited("aborted"));
|
|
31078
|
+
}
|
|
30233
31079
|
let data;
|
|
30234
31080
|
try {
|
|
30235
31081
|
data = await bashStatusSnapshot(bridge, extCtx, taskId, outputMode, bridgeOptions, waitFor ? spillCursor : undefined);
|
|
30236
31082
|
} catch (err) {
|
|
30237
31083
|
if (!isBridgeTransportTimeout(err))
|
|
30238
31084
|
throw err;
|
|
31085
|
+
busySince ??= watchClock.now();
|
|
30239
31086
|
if (isSyncWatchAborted(sessionId)) {
|
|
30240
|
-
return withWaited(lastData ?? unavailableSnapshot(),
|
|
30241
|
-
reason: "user_message",
|
|
30242
|
-
elapsed_ms: Date.now() - startedAt
|
|
30243
|
-
});
|
|
31087
|
+
return withWaited(lastData ?? unavailableSnapshot(), waited("user_message"));
|
|
30244
31088
|
}
|
|
30245
|
-
if (
|
|
30246
|
-
return withWaited(lastData ?? unavailableSnapshot(),
|
|
30247
|
-
reason: "unavailable",
|
|
30248
|
-
elapsed_ms: Date.now() - startedAt
|
|
30249
|
-
});
|
|
31089
|
+
if (watchClock.now() >= deadline || watchClock.now() - busySince >= WATCH_UNAVAILABLE_GIVE_UP_MS) {
|
|
31090
|
+
return withWaited(lastData ?? unavailableSnapshot(), waited("unavailable"));
|
|
30250
31091
|
}
|
|
30251
|
-
await
|
|
31092
|
+
await pause();
|
|
30252
31093
|
continue;
|
|
30253
31094
|
}
|
|
31095
|
+
busySince = undefined;
|
|
30254
31096
|
lastData = data;
|
|
30255
31097
|
const terminal = isTerminalStatus(data.status);
|
|
30256
31098
|
if (waitFor) {
|
|
@@ -30275,13 +31117,11 @@ async function waitForBashStatus(ctx, bridge, extCtx, taskId, outputMode, waitFo
|
|
|
30275
31117
|
await markBgCompletionDelivered({ ctx, directory: extCtx.cwd, sessionID: sessionId }, taskId);
|
|
30276
31118
|
}
|
|
30277
31119
|
const matchStream = data.mode === "pty" ? undefined : chunk.stream === "output" ? "stdout" : "stderr";
|
|
30278
|
-
return withWaited(data, {
|
|
30279
|
-
reason: "matched",
|
|
30280
|
-
elapsed_ms: Date.now() - startedAt,
|
|
31120
|
+
return withWaited(data, waited("matched", {
|
|
30281
31121
|
match: match.text,
|
|
30282
31122
|
match_offset: state.baseOffset + match.byteOffset,
|
|
30283
31123
|
match_stream: matchStream
|
|
30284
|
-
});
|
|
31124
|
+
}));
|
|
30285
31125
|
}
|
|
30286
31126
|
if (waitFor.kind === "substring") {
|
|
30287
31127
|
const trimmed = trimWaitScanBuffer(state.text, state.baseOffset, waitFor);
|
|
@@ -30297,15 +31137,15 @@ async function waitForBashStatus(ctx, bridge, extCtx, taskId, outputMode, waitFo
|
|
|
30297
31137
|
consumeBgCompletion(sessionId, taskId);
|
|
30298
31138
|
await markBgCompletionDelivered({ ctx, directory: extCtx.cwd, sessionID: sessionId }, taskId);
|
|
30299
31139
|
}
|
|
30300
|
-
return withWaited(data,
|
|
31140
|
+
return withWaited(data, waited("exited"));
|
|
30301
31141
|
}
|
|
30302
31142
|
if (isSyncWatchAborted(sessionId)) {
|
|
30303
|
-
return withWaited(data,
|
|
31143
|
+
return withWaited(data, waited("user_message"));
|
|
30304
31144
|
}
|
|
30305
|
-
if (
|
|
30306
|
-
return withWaited(data,
|
|
31145
|
+
if (watchClock.now() >= deadline) {
|
|
31146
|
+
return withWaited(data, waited("timeout"));
|
|
30307
31147
|
}
|
|
30308
|
-
await
|
|
31148
|
+
await pause();
|
|
30309
31149
|
}
|
|
30310
31150
|
} finally {
|
|
30311
31151
|
if (waitForExit && !sawTerminal)
|
|
@@ -30412,18 +31252,22 @@ function withWaited(data, waited) {
|
|
|
30412
31252
|
return { ...data, waited };
|
|
30413
31253
|
}
|
|
30414
31254
|
function formatWaitSummary(waited, details, watchRole) {
|
|
31255
|
+
const waitedText = formatWatchWaited(waited.elapsed_ms, waited.limit_ms, watchRole.role === "primary" ? watchRole.capMs : undefined);
|
|
30415
31256
|
if (waited.reason === "matched") {
|
|
30416
31257
|
const stream = waited.match_stream ? ` in ${waited.match_stream}` : "";
|
|
30417
|
-
return
|
|
31258
|
+
return `${waitedText}; matched ${JSON.stringify(waited.match ?? "")}${stream} at offset ${waited.match_offset ?? 0}.`;
|
|
30418
31259
|
}
|
|
30419
31260
|
if (waited.reason === "timeout") {
|
|
30420
|
-
return
|
|
31261
|
+
return `${waitedText}; timeout reached without match. ${watchTimeoutSteer(watchRole.role, "timeout_ms")}`;
|
|
30421
31262
|
}
|
|
30422
31263
|
if (waited.reason === "unavailable") {
|
|
30423
|
-
return
|
|
31264
|
+
return `${waitedText}; ${watchUnavailableSteer(watchRole.role)}`;
|
|
31265
|
+
}
|
|
31266
|
+
if (waited.reason === "aborted") {
|
|
31267
|
+
return `${waitedText}; the watch was cancelled. The task keeps running.`;
|
|
30424
31268
|
}
|
|
30425
31269
|
const exit = typeof details.exit_code === "number" ? `, exit ${details.exit_code}` : "";
|
|
30426
|
-
return
|
|
31270
|
+
return `${waitedText}; task exited (${details.status}${exit}).`;
|
|
30427
31271
|
}
|
|
30428
31272
|
async function formatBashStatus(extCtx, taskId, details, requestedOutputMode, watchRole) {
|
|
30429
31273
|
const exit = typeof details.exit_code === "number" ? ` (exit ${details.exit_code})` : "";
|
|
@@ -30442,10 +31286,10 @@ ${formatWaitSummary(details.waited, details, watchRole)}`;
|
|
|
30442
31286
|
text += `
|
|
30443
31287
|
${details.output_preview}`;
|
|
30444
31288
|
}
|
|
30445
|
-
const
|
|
30446
|
-
if (!isTerminalStatus(details.status) && !
|
|
31289
|
+
const workerWatchReturned = watchRole.role === "worker" && details.waited !== undefined;
|
|
31290
|
+
if (!isTerminalStatus(details.status) && !workerWatchReturned) {
|
|
30447
31291
|
text += `
|
|
30448
|
-
|
|
31292
|
+
${runningTaskStatusHint(watchRole.role)}`;
|
|
30449
31293
|
}
|
|
30450
31294
|
}
|
|
30451
31295
|
return text;
|
|
@@ -30690,7 +31534,7 @@ function registerFsTools(pi, ctx, surface) {
|
|
|
30690
31534
|
pi.registerTool(withPathAliasPreparation({
|
|
30691
31535
|
name: "aft_delete",
|
|
30692
31536
|
label: "delete",
|
|
30693
|
-
description: "Delete one or more files (or directories). " + (backupsDisabled ? "Backup capture is disabled by user config, so this tool does not create undo snapshots. " : "Each file is backed up before deletion — use `aft_safety undo` to recover any of them.
|
|
31537
|
+
description: "Delete one or more files (or directories). " + (backupsDisabled ? "Backup capture is disabled by user config, so this tool does not create undo snapshots. A directory tree containing a mount point of another filesystem is still refused. " : "Each file is backed up before deletion — use `aft_safety undo` to recover any of them. A recursive delete backs up the whole tree: directories (empty ones too, with their permissions), file contents, hard links (relinked by undo) and symlinks (the link itself, never its target; restored exactly, even when dangling). Sockets are deleted but undo does not restore them, and a file hard-linked to paths outside the tree comes back as an independent copy; both are reported as warnings. Refused before anything is deleted: mount points of another filesystem, named pipes, device nodes, and symlinks undo cannot recreate exactly (non-UTF-8 target, or on Windows). Paths under the system temp directory are never backed up, so only mount points are refused there. The deleted file's contents stay in the undo store until its retention expires. A recursive delete whose backup would record more than 2,000 entries (files, directories and links) or copy 100 MiB in one call is refused before anything is deleted; delete such a tree in smaller pieces, or use bash `rm -rf` when no undo is needed. ") + "Directory deletion requires recursive: true. " + "Returns { success, complete, deleted, skipped_files }: partial success is allowed; files that fail are reported in skipped_files.",
|
|
30694
31538
|
parameters: DeleteParams,
|
|
30695
31539
|
async execute(_toolCallId, params, _signal, _onUpdate, extCtx) {
|
|
30696
31540
|
const inputs = coerceStringArray(params.files);
|
|
@@ -30698,14 +31542,13 @@ function registerFsTools(pi, ctx, surface) {
|
|
|
30698
31542
|
throw new Error("delete: `files` must be a non-empty array of paths");
|
|
30699
31543
|
}
|
|
30700
31544
|
const files = await Promise.all(inputs.map((file) => resolvePathArg(extCtx.cwd, file)));
|
|
31545
|
+
const restrictToProjectRoot = ctx.config.restrict_to_project_root ?? false;
|
|
30701
31546
|
const checked = new Set;
|
|
30702
31547
|
for (const file of files) {
|
|
30703
31548
|
if (checked.has(file))
|
|
30704
31549
|
continue;
|
|
30705
31550
|
checked.add(file);
|
|
30706
|
-
await assertExternalDirectoryPermission(extCtx, file, {
|
|
30707
|
-
restrictToProjectRoot: ctx.config.restrict_to_project_root ?? false
|
|
30708
|
-
});
|
|
31551
|
+
await assertExternalDirectoryPermission(extCtx, file, { restrictToProjectRoot });
|
|
30709
31552
|
}
|
|
30710
31553
|
const bridge = bridgeFor(ctx, extCtx.cwd);
|
|
30711
31554
|
const response = await callToolCall(bridge, "delete", {
|
|
@@ -30743,10 +31586,9 @@ function registerFsTools(pi, ctx, surface) {
|
|
|
30743
31586
|
const filePath = await resolvePathArg(extCtx.cwd, params.path);
|
|
30744
31587
|
const destination = await resolvePathArg(extCtx.cwd, params.destination);
|
|
30745
31588
|
const checked = new Set([filePath, destination]);
|
|
31589
|
+
const restrictToProjectRoot = ctx.config.restrict_to_project_root ?? false;
|
|
30746
31590
|
for (const file of checked) {
|
|
30747
|
-
await assertExternalDirectoryPermission(extCtx, file, {
|
|
30748
|
-
restrictToProjectRoot: ctx.config.restrict_to_project_root ?? false
|
|
30749
|
-
});
|
|
31591
|
+
await assertExternalDirectoryPermission(extCtx, file, { restrictToProjectRoot });
|
|
30750
31592
|
}
|
|
30751
31593
|
const bridge = bridgeFor(ctx, extCtx.cwd);
|
|
30752
31594
|
const response = await callToolCall(bridge, "move", {
|
|
@@ -31391,6 +32233,9 @@ function registerNavigateTool(pi, ctx) {
|
|
|
31391
32233
|
|
|
31392
32234
|
// src/tools/reading.ts
|
|
31393
32235
|
import { Type as Type10 } from "typebox";
|
|
32236
|
+
function isGithubPathArg(value) {
|
|
32237
|
+
return typeof value === "string" && (value.startsWith("issue://") || value.startsWith("pr://"));
|
|
32238
|
+
}
|
|
31394
32239
|
var OutlineParams = Type10.Object({
|
|
31395
32240
|
target: Type10.Union([Type10.String(), Type10.Array(Type10.String())], {
|
|
31396
32241
|
description: "What to outline: a file path, directory path, URL (http:// or https://), or array of file paths. The mode is auto-detected: URLs by `http://`/`https://` prefix, directories by stat, arrays as multi-file."
|
|
@@ -31415,7 +32260,7 @@ var ZoomParams = Type10.Object({
|
|
|
31415
32260
|
description: "Symbol name for code, or heading text for Markdown/HTML. Pass a string or array for batched lookups in the same file/URL."
|
|
31416
32261
|
})),
|
|
31417
32262
|
targets: Type10.Optional(Type10.Union([ZoomTarget, Type10.Array(ZoomTarget)], {
|
|
31418
|
-
description: "Cross-file batch: `{ path, symbol }` or an array of them.
|
|
32263
|
+
description: "Cross-file batch: `{ path, symbol }` or an array of them. May be combined with path/url + symbols; all lookups are answered in one batch."
|
|
31419
32264
|
})),
|
|
31420
32265
|
contextLines: optionalInt(1, Number.MAX_SAFE_INTEGER, "Lines of context before/after (default: 3)"),
|
|
31421
32266
|
callgraph: Type10.Optional(Type10.Boolean({
|
|
@@ -31569,6 +32414,13 @@ function renderZoomResult(result, args, theme, context, options = { expanded: tr
|
|
|
31569
32414
|
context
|
|
31570
32415
|
});
|
|
31571
32416
|
}
|
|
32417
|
+
function outlineFocusSteer(config) {
|
|
32418
|
+
const zoomEnabled = toolEnabled(config, "aft_zoom");
|
|
32419
|
+
const reader = zoomEnabled ? "aft_zoom" : "read";
|
|
32420
|
+
const locate = toolEnabled(config, "aft_search") ? `aft_search + ${reader}` : reader;
|
|
32421
|
+
const callgraph = zoomEnabled ? toolEnabled(config, "aft_callgraph") ? " aft_zoom with `callgraph:true` gives one-level forward calls-out; use aft_callgraph only for reverse callers or multi-level traces." : " aft_zoom with `callgraph:true` gives one-level forward calls-out." : "";
|
|
32422
|
+
return `For understanding a specific feature, prefer ${locate} on named symbols; use aft_outline on a whole directory only for high-level structure mapping.${callgraph}`;
|
|
32423
|
+
}
|
|
31572
32424
|
function registerReadingTools(pi, ctx, surface) {
|
|
31573
32425
|
const zoomEnabled = toolEnabled(ctx.config, "aft_zoom");
|
|
31574
32426
|
const ghReadEnabled = resolveGithubConfig(ctx.config).read;
|
|
@@ -31580,7 +32432,7 @@ function registerReadingTools(pi, ctx, surface) {
|
|
|
31580
32432
|
label: "outline",
|
|
31581
32433
|
description: `Structural outline of source code, documentation files, or remote URLs. For code, returns symbols (functions, classes, types) with line ranges. For Markdown and HTML, returns heading hierarchy. Use this to explore structure before reading specific sections with ${zoomEnabled ? "aft_zoom" : "read"}. With \`files: true\`, the outline is breadth-first with directory rollups; drill in by outlining a subdirectory. Rows show language, symbol count, and line count.
|
|
31582
32434
|
|
|
31583
|
-
${
|
|
32435
|
+
${outlineFocusSteer(ctx.config)}
|
|
31584
32436
|
|
|
31585
32437
|
Pass a single \`target\`:
|
|
31586
32438
|
• file path → outline that file (with signatures)
|
|
@@ -31647,56 +32499,49 @@ When a list is cut, the reply ends with \`shown N of M <unit> (<reason>) · narr
|
|
|
31647
32499
|
pi.registerTool(withPathAliasPreparation({
|
|
31648
32500
|
name: "aft_zoom",
|
|
31649
32501
|
label: "zoom",
|
|
31650
|
-
description: "Inspect code symbols or documentation sections. For code, returns the full source of a symbol. Pass `callgraph: true` to also include call-graph annotations (calls-out / called-by within the same file). For Markdown and HTML, returns the section content under the given heading.\n\nUse
|
|
32502
|
+
description: "Inspect code symbols or documentation sections. For code, returns the full source of a symbol. Pass `callgraph: true` to also include call-graph annotations (calls-out / called-by within the same file). For Markdown and HTML, returns the section content under the given heading.\n\nUse `{ path, symbols }` or `{ url, symbols }` for one file/URL. `symbols` can be a string or array (one or many lookups in the same file/URL). Use `targets` for cross-file batches: `{ path, symbol }` or an array of them. Sending both merges them into one batch; a lookup that fails reports its own error line." + (githubZoomDescription ? `
|
|
31651
32503
|
|
|
31652
32504
|
${githubZoomDescription}` : ""),
|
|
31653
32505
|
parameters: ZoomParams,
|
|
31654
32506
|
async execute(_toolCallId, params, _signal, _onUpdate, extCtx) {
|
|
31655
32507
|
const bridge = bridgeFor(ctx, extCtx.cwd);
|
|
31656
|
-
const hasTargetsProvided = (t) => {
|
|
31657
|
-
if (isEmptyParam(t))
|
|
31658
|
-
return false;
|
|
31659
|
-
const entryEmpty = (entry) => {
|
|
31660
|
-
if (!entry || typeof entry !== "object")
|
|
31661
|
-
return true;
|
|
31662
|
-
const fp = entry.path;
|
|
31663
|
-
const sym = entry.symbol;
|
|
31664
|
-
const fpEmpty = typeof fp !== "string" || fp.length === 0;
|
|
31665
|
-
const symEmpty = typeof sym !== "string" || sym.length === 0;
|
|
31666
|
-
return fpEmpty && symEmpty;
|
|
31667
|
-
};
|
|
31668
|
-
if (Array.isArray(t))
|
|
31669
|
-
return !t.every(entryEmpty);
|
|
31670
|
-
return !entryEmpty(t);
|
|
31671
|
-
};
|
|
31672
32508
|
const targetsInput = coerceJsonCollectionParam(params.targets, "targets");
|
|
31673
|
-
const
|
|
31674
|
-
const
|
|
31675
|
-
const
|
|
31676
|
-
const
|
|
32509
|
+
const targetEntries = usableZoomTargets(targetsInput);
|
|
32510
|
+
const hasPath = !isBlankParam(params.path);
|
|
32511
|
+
const hasUrl = !isBlankParam(params.url);
|
|
32512
|
+
const hasTargets = targetEntries.length > 0;
|
|
32513
|
+
const hasSymbols = !isBlankParam(params.symbols);
|
|
31677
32514
|
const wantCallgraph = coerceBoolean(params.callgraph);
|
|
31678
32515
|
const contextLines = coerceOptionalInt(params.contextLines, "contextLines", 1, Number.MAX_SAFE_INTEGER);
|
|
31679
32516
|
if (hasTargets) {
|
|
31680
|
-
if (hasPath
|
|
31681
|
-
throw new Error("
|
|
32517
|
+
if (hasPath && hasUrl) {
|
|
32518
|
+
throw new Error("Provide exactly ONE of 'path' or 'url' — not both");
|
|
31682
32519
|
}
|
|
31683
|
-
const targets =
|
|
31684
|
-
|
|
31685
|
-
throw new Error("'targets' must be a non-empty object or array");
|
|
31686
|
-
}
|
|
31687
|
-
for (const [i, entry] of targets.entries()) {
|
|
31688
|
-
if (!entry || typeof entry.path !== "string" || entry.path.length === 0) {
|
|
32520
|
+
const targets = targetEntries.map((entry, i) => {
|
|
32521
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
|
|
31689
32522
|
throw new Error(`targets[${i}].path must be a non-empty string`);
|
|
31690
32523
|
}
|
|
31691
|
-
|
|
31692
|
-
|
|
31693
|
-
|
|
31694
|
-
|
|
31695
|
-
|
|
32524
|
+
const record = entry;
|
|
32525
|
+
const targetPath = isBlankParam(record.path) ? record.filePath : record.path;
|
|
32526
|
+
return {
|
|
32527
|
+
path: typeof targetPath === "string" ? targetPath : "",
|
|
32528
|
+
symbol: typeof record.symbol === "string" ? record.symbol : ""
|
|
32529
|
+
};
|
|
32530
|
+
});
|
|
32531
|
+
const localPaths = targets.map((target) => target.path).filter((path) => path.trim().length > 0 && !isGithubPathArg(path));
|
|
32532
|
+
if (hasPath && !isGithubPathArg(params.path))
|
|
32533
|
+
localPaths.push(params.path);
|
|
32534
|
+
const resolvedTargets = await Promise.all(localPaths.map((path) => resolvePathArg(extCtx.cwd, path)));
|
|
31696
32535
|
await assertReadPathPermissions(extCtx, ctx, resolvedTargets);
|
|
31697
32536
|
const rawArgs = {
|
|
31698
32537
|
targets: targets.map((target) => ({ filePath: target.path, symbol: target.symbol }))
|
|
31699
32538
|
};
|
|
32539
|
+
if (hasPath)
|
|
32540
|
+
rawArgs.filePath = params.path;
|
|
32541
|
+
else if (hasUrl)
|
|
32542
|
+
rawArgs.url = params.url;
|
|
32543
|
+
if (hasSymbols)
|
|
32544
|
+
rawArgs.symbols = params.symbols;
|
|
31700
32545
|
if (contextLines !== undefined)
|
|
31701
32546
|
rawArgs.contextLines = contextLines;
|
|
31702
32547
|
if (wantCallgraph)
|
|
@@ -31713,7 +32558,7 @@ ${githubZoomDescription}` : ""),
|
|
|
31713
32558
|
if (hasPath && hasUrl) {
|
|
31714
32559
|
throw new Error("Provide exactly ONE of 'path' or 'url' — not both");
|
|
31715
32560
|
}
|
|
31716
|
-
const githubPath = hasPath && (
|
|
32561
|
+
const githubPath = hasPath && isGithubPathArg(params.path) || hasUrl && isGithubPathArg(params.url);
|
|
31717
32562
|
if (!hasUrl && !githubPath) {
|
|
31718
32563
|
const file = await resolvePathArg(extCtx.cwd, params.path);
|
|
31719
32564
|
await assertReadPathPermissions(extCtx, ctx, file);
|
|
@@ -31752,7 +32597,7 @@ var SafetyParams = Type11.Object({
|
|
|
31752
32597
|
description: "Safety operation"
|
|
31753
32598
|
}),
|
|
31754
32599
|
path: Type11.Optional(Type11.String({
|
|
31755
|
-
description: "File path (required for history, optional for undo, optional for restore — restores only that file). Absolute or relative to project root."
|
|
32600
|
+
description: "File path (required for history, optional for undo, optional for checkpoint — snapshots only that file when files is omitted, optional for restore — restores only that file). Absolute or relative to project root."
|
|
31756
32601
|
})),
|
|
31757
32602
|
name: Type11.Optional(Type11.String({ description: "Checkpoint name (required for checkpoint, restore)" })),
|
|
31758
32603
|
files: Type11.Optional(Type11.Array(Type11.String(), {
|
|
@@ -32176,9 +33021,17 @@ function piHashlineDowngrade(config, surface) {
|
|
|
32176
33021
|
message: 'edit_mode "hashline" is not in effect because "read" is in disabled_tools (hashline edits need tagged reads); registered tools keep their ordinary behavior.'
|
|
32177
33022
|
};
|
|
32178
33023
|
}
|
|
33024
|
+
function withLiveRestriction(surface, ctx) {
|
|
33025
|
+
return Object.defineProperty({ ...surface }, "restrictToProjectRoot", {
|
|
33026
|
+
enumerable: true,
|
|
33027
|
+
get: () => ctx.config.restrict_to_project_root ?? false
|
|
33028
|
+
});
|
|
33029
|
+
}
|
|
32179
33030
|
function resolvePiToolSurface(config, pi) {
|
|
32180
33031
|
const disabled = new Set(resolvedDisabledTools(config));
|
|
32181
33032
|
const ok = (name) => !disabled.has(name);
|
|
33033
|
+
const background = resolveBashConfig(config).background;
|
|
33034
|
+
const companion = (name) => background && ok(name);
|
|
32182
33035
|
const powershellEnabled = (pi ? piPowerShellEnabledFromHost(pi) : undefined) ?? resolvePiPowerShellFallback(config);
|
|
32183
33036
|
return {
|
|
32184
33037
|
hoistBash: ok("bash"),
|
|
@@ -32200,10 +33053,10 @@ function resolvePiToolSurface(config, pi) {
|
|
|
32200
33053
|
move: ok("aft_move"),
|
|
32201
33054
|
astSearch: ok("ast_grep_search"),
|
|
32202
33055
|
astReplace: ok("ast_grep_replace"),
|
|
32203
|
-
bashStatus:
|
|
32204
|
-
bashWatch:
|
|
32205
|
-
bashWrite:
|
|
32206
|
-
bashKill:
|
|
33056
|
+
bashStatus: companion("bash_status"),
|
|
33057
|
+
bashWatch: companion("bash_watch"),
|
|
33058
|
+
bashWrite: companion("bash_write"),
|
|
33059
|
+
bashKill: companion("bash_kill")
|
|
32207
33060
|
};
|
|
32208
33061
|
}
|
|
32209
33062
|
var FUNNEL_BOUND = Symbol.for("aft.pi.registration_funnel_bound");
|
|
@@ -32228,7 +33081,8 @@ function bindToolRegistrationFunnel(pi, ctx, harness) {
|
|
|
32228
33081
|
}
|
|
32229
33082
|
});
|
|
32230
33083
|
}
|
|
32231
|
-
function registerPiToolSurface(pi, ctx,
|
|
33084
|
+
function registerPiToolSurface(pi, ctx, loadedSurface, harness) {
|
|
33085
|
+
const surface = withLiveRestriction(loadedSurface, ctx);
|
|
32232
33086
|
const boundPi = bindToolRegistrationFunnel(pi, ctx, harness);
|
|
32233
33087
|
if (surface.hoistBash)
|
|
32234
33088
|
registerBashTool(boundPi, ctx, surface.semantic, "bash", false);
|
|
@@ -32262,6 +33116,8 @@ var CONFIG_ERROR_STATUS_KEY = "aft";
|
|
|
32262
33116
|
var defaultDependencies = {
|
|
32263
33117
|
loadConfig: loadAftConfig,
|
|
32264
33118
|
configLoadErrors: getConfigLoadErrors,
|
|
33119
|
+
configLoadSources: getConfigLoadSources,
|
|
33120
|
+
configLoadTexts: getConfigLoadTexts,
|
|
32265
33121
|
migrateConfigLocations: (directory) => migrateAftConfigLocations(directory, bridgeLogger).flatMap((result) => result.warnings),
|
|
32266
33122
|
subcConnectionFileError
|
|
32267
33123
|
};
|
|
@@ -32280,6 +33136,8 @@ function parseFailure(dependencies) {
|
|
|
32280
33136
|
}
|
|
32281
33137
|
async function resolvePiBootstrapConfig(directory, notify, dependencies = defaultDependencies) {
|
|
32282
33138
|
let config;
|
|
33139
|
+
let sources = [];
|
|
33140
|
+
let sourceTexts = {};
|
|
32283
33141
|
try {
|
|
32284
33142
|
dependencies.loadConfig(directory);
|
|
32285
33143
|
const firstFailure = parseFailure(dependencies);
|
|
@@ -32291,11 +33149,13 @@ async function resolvePiBootstrapConfig(directory, notify, dependencies = defaul
|
|
|
32291
33149
|
const failure = parseFailure(dependencies);
|
|
32292
33150
|
if (failure)
|
|
32293
33151
|
return configErrorState(failure, notify);
|
|
33152
|
+
sources = [...dependencies.configLoadSources?.() ?? []];
|
|
33153
|
+
sourceTexts = Object.fromEntries(dependencies.configLoadTexts?.() ?? []);
|
|
32294
33154
|
} catch (err) {
|
|
32295
33155
|
return configErrorState(err instanceof Error ? err.message : String(err), notify);
|
|
32296
33156
|
}
|
|
32297
33157
|
const missing = await dependencies.subcConnectionFileError(config.subc?.connection_file);
|
|
32298
|
-
return missing === null ? { ok: true, config } : configErrorState(missing, notify, config);
|
|
33158
|
+
return missing === null ? { ok: true, config, sources, sourceTexts } : configErrorState(missing, notify, config);
|
|
32299
33159
|
}
|
|
32300
33160
|
function failingRegistrations(pi, message) {
|
|
32301
33161
|
const registerTool = (tool) => {
|
|
@@ -32331,6 +33191,58 @@ function registerPiConfigErrorState(pi, config, message) {
|
|
|
32331
33191
|
log2("AFT is in the config error state; every tool call will fail");
|
|
32332
33192
|
}
|
|
32333
33193
|
|
|
33194
|
+
// src/config-live-reload.ts
|
|
33195
|
+
var PI_LIVE_CONFIG_KEYS = aftLiveConfigKeys((config) => resolveBashConfig(config));
|
|
33196
|
+
function projectTextOf(directory) {
|
|
33197
|
+
const { projectConfigPath } = resolveCortexKitConfigPaths(directory);
|
|
33198
|
+
return getConfigLoadTexts().get(projectConfigPath) ?? null;
|
|
33199
|
+
}
|
|
33200
|
+
function loadPiConfigForLiveReload(directory) {
|
|
33201
|
+
try {
|
|
33202
|
+
const config = loadAftConfig(directory);
|
|
33203
|
+
const [failure] = getConfigLoadErrors();
|
|
33204
|
+
if (failure) {
|
|
33205
|
+
return { ok: false, message: formatConfigParseErrorMessage(failure.path, failure.message) };
|
|
33206
|
+
}
|
|
33207
|
+
const [invalid] = getConfigValidationErrors();
|
|
33208
|
+
if (invalid) {
|
|
33209
|
+
return {
|
|
33210
|
+
ok: false,
|
|
33211
|
+
message: `AFT config at ${invalid.path} has an invalid setting: ${invalid.message}`
|
|
33212
|
+
};
|
|
33213
|
+
}
|
|
33214
|
+
return {
|
|
33215
|
+
ok: true,
|
|
33216
|
+
config,
|
|
33217
|
+
sources: [...getConfigLoadSources()],
|
|
33218
|
+
projectText: projectTextOf(directory),
|
|
33219
|
+
texts: Object.fromEntries(getConfigLoadTexts())
|
|
33220
|
+
};
|
|
33221
|
+
} catch (err) {
|
|
33222
|
+
return { ok: false, message: err instanceof Error ? err.message : String(err) };
|
|
33223
|
+
}
|
|
33224
|
+
}
|
|
33225
|
+
function startPiLiveConfigReload(options) {
|
|
33226
|
+
const { userConfigPath, projectConfigPath } = resolveCortexKitConfigPaths(options.directory);
|
|
33227
|
+
return startLiveConfigReload({
|
|
33228
|
+
paths: [userConfigPath, projectConfigPath],
|
|
33229
|
+
load: () => loadPiConfigForLiveReload(options.directory),
|
|
33230
|
+
initialSources: options.initialSources,
|
|
33231
|
+
initialProjectText: options.initialSourceTexts?.[projectConfigPath] ?? null,
|
|
33232
|
+
initialTexts: options.initialSourceTexts,
|
|
33233
|
+
securityKeys: aftLiveSecurityKeys(PI_LIVE_CONFIG_KEYS),
|
|
33234
|
+
keys: PI_LIVE_CONFIG_KEYS,
|
|
33235
|
+
getConfig: options.getConfig,
|
|
33236
|
+
setConfig: options.setConfig,
|
|
33237
|
+
log: (message) => log2(`${message} (${options.directory})`),
|
|
33238
|
+
reportError: (message) => {
|
|
33239
|
+
error2(message);
|
|
33240
|
+
options.notify(message);
|
|
33241
|
+
},
|
|
33242
|
+
watch: options.watch
|
|
33243
|
+
});
|
|
33244
|
+
}
|
|
33245
|
+
|
|
32334
33246
|
// src/shutdown-hooks.ts
|
|
32335
33247
|
var GLOBAL_KEY = "__aftPiShutdownHooks__";
|
|
32336
33248
|
function getState() {
|
|
@@ -32414,7 +33326,10 @@ function registerShutdownCleanup(fn) {
|
|
|
32414
33326
|
|
|
32415
33327
|
// src/workflow-hints.ts
|
|
32416
33328
|
var HEADING = "## IMPORTANT NOTICE about your tools";
|
|
32417
|
-
|
|
33329
|
+
function hashlineTagSourceHint(bashRewritesMintTags, isRegistered = () => true) {
|
|
33330
|
+
return `**Hashline edit tags**: ${hashlineTagSourceSentence(bashRewritesMintTags, isRegistered)}`;
|
|
33331
|
+
}
|
|
33332
|
+
var HASHLINE_TAG_SOURCE_HINT = hashlineTagSourceHint(true);
|
|
32418
33333
|
function buildWorkflowHints(opts) {
|
|
32419
33334
|
const sections = [];
|
|
32420
33335
|
const grepName = "grep";
|
|
@@ -32429,6 +33344,8 @@ function buildWorkflowHints(opts) {
|
|
|
32429
33344
|
const hasInspect = !opts.absentTools.has("aft_inspect");
|
|
32430
33345
|
const hasBash = !opts.absentTools.has(bashName);
|
|
32431
33346
|
const hasBgBash = opts.bashBackgroundEnabled && hasBash && !opts.absentTools.has("bash_status");
|
|
33347
|
+
const hasWatch = !opts.absentTools.has("bash_watch");
|
|
33348
|
+
const hasWrite = !opts.absentTools.has("bash_write");
|
|
32432
33349
|
if (hasBash && opts.bashCompressionEnabled) {
|
|
32433
33350
|
sections.push([
|
|
32434
33351
|
"**Test/build output**: bash output is auto-compressed for non-piped commands. Piped commands run verbatim and show the pipeline's output. For AFT's test/build summary, run the runner without filters:",
|
|
@@ -32470,17 +33387,19 @@ function buildWorkflowHints(opts) {
|
|
|
32470
33387
|
].join(`
|
|
32471
33388
|
`));
|
|
32472
33389
|
}
|
|
32473
|
-
if (hasBgBash) {
|
|
33390
|
+
if (hasBgBash && hasWatch) {
|
|
32474
33391
|
sections.push([
|
|
32475
33392
|
`**Long-running commands** (builds, installs, full test suites): run them in the FOREGROUND — use \`${bashName}({ command, wait: true })\` when you know it is long and need the result before anything else; if you send a new message, the wait detaches to background; otherwise omit \`wait\` so auto-promote can hand you a reminder while you work.`,
|
|
32476
33393
|
"- `background: true` is ONLY for when you have OTHER useful work to do while it runs: start it, do the other work, and the completion reminder delivers the result (or spawn a subagent for the side work). Do NOT background a command and then immediately `bash_watch` it — that spends a whole extra turn waiting for something foreground returns in one.",
|
|
32477
33394
|
"- `bash_watch` is for blocking on an ALREADY-backgrounded task once you've run out of parallel work (sync — the user can interrupt), or reacting to a specific early output line (async: background:true + pattern). Never loop `bash_status` to wait — it's a one-shot inspector."
|
|
32478
33395
|
].join(`
|
|
32479
33396
|
`));
|
|
33397
|
+
}
|
|
33398
|
+
if (hasBgBash && hasWrite) {
|
|
32480
33399
|
sections.push(`**PTY / interactive commands**: PTY mode is for interactive REPLs and terminal apps (python, node, bash itself, vim). Start with \`${bashName}({ command: "python", pty: true, background: true })\`, read the screen with \`bash_status({ task_id, output_mode: "screen" })\`, and send input with \`bash_write({ task_id, input: "..." })\`.`);
|
|
32481
33400
|
}
|
|
32482
33401
|
if (opts.hashlineEffective === true) {
|
|
32483
|
-
sections.push(
|
|
33402
|
+
sections.push(hashlineTagSourceHint(hasBash && opts.bashRewriteEnabled !== false, (name) => !opts.absentTools.has(name)));
|
|
32484
33403
|
}
|
|
32485
33404
|
if (sections.length === 0) {
|
|
32486
33405
|
return null;
|
|
@@ -32495,14 +33414,18 @@ ${sections.join(`
|
|
|
32495
33414
|
`)}`;
|
|
32496
33415
|
}
|
|
32497
33416
|
function buildHintsFromConfig(config, absentTools, hashlineEffective = false) {
|
|
33417
|
+
const bashCfg = resolveBashConfig(config);
|
|
32498
33418
|
return buildWorkflowHints({
|
|
32499
|
-
bashBackgroundEnabled:
|
|
32500
|
-
bashCompressionEnabled:
|
|
33419
|
+
bashBackgroundEnabled: bashCfg.background,
|
|
33420
|
+
bashCompressionEnabled: bashCfg.compress,
|
|
33421
|
+
bashRewriteEnabled: bashCfg.rewrite,
|
|
32501
33422
|
absentTools,
|
|
32502
33423
|
hashlineEffective
|
|
32503
33424
|
});
|
|
32504
33425
|
}
|
|
32505
33426
|
function registerWorkflowHints(pi, config, surface) {
|
|
33427
|
+
if (skipsEagerStartup())
|
|
33428
|
+
return;
|
|
32506
33429
|
const absent = new Set;
|
|
32507
33430
|
if (!surface.outline)
|
|
32508
33431
|
absent.add("aft_outline");
|
|
@@ -32522,6 +33445,10 @@ function registerWorkflowHints(pi, config, surface) {
|
|
|
32522
33445
|
absent.add("bash");
|
|
32523
33446
|
if (!surface.bashStatus)
|
|
32524
33447
|
absent.add("bash_status");
|
|
33448
|
+
if (!surface.bashWatch)
|
|
33449
|
+
absent.add("bash_watch");
|
|
33450
|
+
if (!surface.bashWrite)
|
|
33451
|
+
absent.add("bash_write");
|
|
32525
33452
|
const hintsBlock = buildHintsFromConfig(config, absent, piHashlineEffective(config, surface));
|
|
32526
33453
|
if (!hintsBlock)
|
|
32527
33454
|
return;
|
|
@@ -32956,6 +33883,16 @@ ${lines}
|
|
|
32956
33883
|
hashlineEffective: hashlineEditRegistered,
|
|
32957
33884
|
storageDir
|
|
32958
33885
|
};
|
|
33886
|
+
const liveConfigReload = startPiLiveConfigReload({
|
|
33887
|
+
directory: projectRoot,
|
|
33888
|
+
initialSources: bootstrap.sources ?? [],
|
|
33889
|
+
initialSourceTexts: bootstrap.sourceTexts,
|
|
33890
|
+
getConfig: () => ctx.config,
|
|
33891
|
+
setConfig: (next) => {
|
|
33892
|
+
ctx.config = next;
|
|
33893
|
+
},
|
|
33894
|
+
notify: (message) => deliverConfigMigrationWarnings([message])
|
|
33895
|
+
});
|
|
32959
33896
|
if (onnxRuntimePromise) {
|
|
32960
33897
|
onnxRuntimePromise.then((ortDylibDir) => {
|
|
32961
33898
|
if (ortDylibDir) {
|
|
@@ -33062,6 +33999,7 @@ Retry with: npx @cortexkit/aft doctor --fix`
|
|
|
33062
33999
|
});
|
|
33063
34000
|
const unregisterShutdownCleanup = registerShutdownCleanup(async () => {
|
|
33064
34001
|
hostShutdownStarted = true;
|
|
34002
|
+
liveConfigReload.stop();
|
|
33065
34003
|
try {
|
|
33066
34004
|
await Promise.allSettled([abortInFlightAutoInstalls(), abortInFlightGithubInstalls()]);
|
|
33067
34005
|
await pool.shutdown();
|
|
@@ -33072,6 +34010,7 @@ Retry with: npx @cortexkit/aft doctor --fix`
|
|
|
33072
34010
|
});
|
|
33073
34011
|
pi.on("session_shutdown", async () => {
|
|
33074
34012
|
hostShutdownStarted = true;
|
|
34013
|
+
liveConfigReload.stop();
|
|
33075
34014
|
try {
|
|
33076
34015
|
await Promise.allSettled([abortInFlightAutoInstalls(), abortInFlightGithubInstalls()]);
|
|
33077
34016
|
await pool.shutdown();
|