@cortexkit/aft-pi 0.59.0 → 0.60.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bg-notifications.d.ts.map +1 -1
- package/dist/config-error-state.d.ts +2 -2
- package/dist/config-error-state.d.ts.map +1 -1
- package/dist/config-live-reload.d.ts.map +1 -1
- package/dist/config.d.ts +0 -4
- package/dist/config.d.ts.map +1 -1
- package/dist/index.js +971 -325
- package/dist/lsp-github-install.d.ts +7 -1
- package/dist/lsp-github-install.d.ts.map +1 -1
- package/dist/tools/bash.d.ts +2 -0
- package/dist/tools/bash.d.ts.map +1 -1
- package/dist/tools/hoisted.d.ts.map +1 -1
- package/dist/tools/inspect.d.ts.map +1 -1
- package/package.json +8 -8
package/dist/index.js
CHANGED
|
@@ -7964,11 +7964,11 @@ var DEFAULT_WORKER_WAIT_MAX_MS = 1800000;
|
|
|
7964
7964
|
var MIN_WORKER_WAIT_MAX_MS = 60000;
|
|
7965
7965
|
var LONGEST_TIMER_DELAY_MS = 2147483647;
|
|
7966
7966
|
var WORKER_WAIT_LIMIT_PHRASE = "the worker wait limit (`bash.worker_wait_max_ms`, 30 minutes by default)";
|
|
7967
|
-
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 up to the worker wait limit (`bash.worker_wait_max_ms`, 30 minutes by default), then reports it is still running; watch again to keep waiting";
|
|
7968
|
-
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 up to the worker wait limit (`bash.worker_wait_max_ms`, 30 minutes by default), after which the watch reports the command is still running; a value you pass is
|
|
7967
|
+
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 up to the worker wait limit (`bash.worker_wait_max_ms`, 30 minutes by default), then reports it is still running; watch again to keep waiting. An explicit timeout never exceeds that worker limit";
|
|
7968
|
+
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 up to the worker wait limit (`bash.worker_wait_max_ms`, 30 minutes by default), after which the watch reports the command is still running; a value you pass is clamped to that worker limit.";
|
|
7969
7969
|
function resolveWatchTimeoutMs(requestedMs, role, capMs, workerWaitMaxMs) {
|
|
7970
7970
|
if (role === "worker")
|
|
7971
|
-
return requestedMs ?? workerWaitMaxMs;
|
|
7971
|
+
return Math.min(requestedMs ?? workerWaitMaxMs, workerWaitMaxMs);
|
|
7972
7972
|
return Math.min(requestedMs ?? DEFAULT_PRIMARY_WATCH_TIMEOUT_MS, capMs);
|
|
7973
7973
|
}
|
|
7974
7974
|
function maxWatchTimeoutMs(role, capMs) {
|
|
@@ -8031,6 +8031,10 @@ function taskKillDeadlineText(data, role, nowMs = Date.now()) {
|
|
|
8031
8031
|
return "";
|
|
8032
8032
|
if (data.status === "unknown")
|
|
8033
8033
|
return "";
|
|
8034
|
+
if (typeof data.remote_phase_note === "string" && data.remote_phase_note !== "") {
|
|
8035
|
+
const note = data.remote_phase_note;
|
|
8036
|
+
return `${note.charAt(0).toUpperCase()}${note.slice(1)}.`;
|
|
8037
|
+
}
|
|
8034
8038
|
const hardKill = data.hard_kill;
|
|
8035
8039
|
if (!hardKill || typeof hardKill.limit_ms !== "number") {
|
|
8036
8040
|
return "This task has no kill deadline.";
|
|
@@ -8765,8 +8769,8 @@ var BASH_HOST_FALLBACK_BANNER = "[AFT host fallback - module transport down; no
|
|
|
8765
8769
|
var BASH_HOST_FALLBACK_MAX_OUTPUT_BYTES = 100 * 1024;
|
|
8766
8770
|
var BASH_HOST_FALLBACK_MAX_TIMEOUT_MS = 10 * 60 * 1000;
|
|
8767
8771
|
var BASH_HOST_FALLBACK_REFUSAL = "AFT transport is down; only foreground execution is available in host fallback";
|
|
8768
|
-
var BASH_RUNON_DESCRIPTION = 'Run
|
|
8769
|
-
var BASH_RUNON_GUIDANCE = 'When remote runs are available, put `runon: "linux"` on build and test lines (cargo, bun test), including chains and pipes. Keep git, gh, interactive and file-editing commands local, and keep a line local if it needs macOS (Seatbelt, codesign, launchd, TCC, AppKit) or runs binaries built on this machine: a remote build leaves no binaries or target/ output
|
|
8772
|
+
var BASH_RUNON_DESCRIPTION = 'Run on a remote build server: `linux` (2 vCPUs), or `linux,Nc` for N vCPUs such as `linux,8c`; add ",net" for outbound internet (offline by default). `windows` runs the command in Git Bash on a Windows build machine (no vCPU or network options).';
|
|
8773
|
+
var BASH_RUNON_GUIDANCE = 'When remote runs are available, put `runon: "linux"` on build and test lines (cargo, bun test), including chains and pipes. Keep git, gh, interactive and file-editing commands local, and keep a line local if it needs macOS (Seatbelt, codesign, launchd, TCC, AppKit) or runs binaries built on this machine: a remote build leaves no binaries or target/ output in the local worktree. Add `,Nc` to request N vCPUs (`linux,4c`); without it the job gets 2 vCPUs. The job sees exactly the CPUs it gets, so ask for what the command uses: 2c (the default) for a single test binary or script, 4c for `cargo check`/clippy on one crate, 8c for a workspace test suite, and 16c for a large release build.';
|
|
8770
8774
|
function pathKeyForPlatform(env, platform) {
|
|
8771
8775
|
return platform === "win32" ? Object.keys(env).find((key) => key.toLowerCase() === "path") : "PATH";
|
|
8772
8776
|
}
|
|
@@ -9210,6 +9214,7 @@ function isPassiveCommand(command) {
|
|
|
9210
9214
|
|
|
9211
9215
|
// ../aft-bridge/dist/transport.js
|
|
9212
9216
|
var WORKER_SESSION_FIELD = "worker_session";
|
|
9217
|
+
var HOST_TOOL_NAMES_FIELD = "host_tool_names";
|
|
9213
9218
|
var PRESET_FIELD = "preset";
|
|
9214
9219
|
function callPresetFor(workerSession) {
|
|
9215
9220
|
return workerSession ? "worker" : "head";
|
|
@@ -9374,6 +9379,8 @@ class BinaryBridge {
|
|
|
9374
9379
|
lastBinaryFingerprintCheckAt = 0;
|
|
9375
9380
|
_retiringDueToBinaryChange = false;
|
|
9376
9381
|
pending = new Map;
|
|
9382
|
+
activeSendCalls = 0;
|
|
9383
|
+
processLoopReferenced = true;
|
|
9377
9384
|
outstandingBackgroundTaskIds = new Set;
|
|
9378
9385
|
nextId = 1;
|
|
9379
9386
|
processGeneration = 0;
|
|
@@ -9605,6 +9612,32 @@ class BinaryBridge {
|
|
|
9605
9612
|
this.sessionLogVia(sessionId, `hashline registration carrier transport=ndjson phase=${phase} edit_slot_survives=${editSlotSurvives}${repeated}`);
|
|
9606
9613
|
}
|
|
9607
9614
|
async send(command, params = {}, options) {
|
|
9615
|
+
this.activeSendCalls += 1;
|
|
9616
|
+
this.syncEventLoopHold();
|
|
9617
|
+
try {
|
|
9618
|
+
return await this.dispatchSend(command, params, options);
|
|
9619
|
+
} finally {
|
|
9620
|
+
this.activeSendCalls -= 1;
|
|
9621
|
+
this.syncEventLoopHold();
|
|
9622
|
+
}
|
|
9623
|
+
}
|
|
9624
|
+
syncEventLoopHold() {
|
|
9625
|
+
const child = this.process;
|
|
9626
|
+
if (!child)
|
|
9627
|
+
return;
|
|
9628
|
+
const hold = this.activeSendCalls > 0 || this.outstandingBackgroundTaskIds.size > 0;
|
|
9629
|
+
if (hold === this.processLoopReferenced)
|
|
9630
|
+
return;
|
|
9631
|
+
this.processLoopReferenced = hold;
|
|
9632
|
+
const handles = [child, child.stdin, child.stdout, child.stderr];
|
|
9633
|
+
for (const handle of handles) {
|
|
9634
|
+
if (hold)
|
|
9635
|
+
handle?.ref?.();
|
|
9636
|
+
else
|
|
9637
|
+
handle?.unref?.();
|
|
9638
|
+
}
|
|
9639
|
+
}
|
|
9640
|
+
async dispatchSend(command, params, options) {
|
|
9608
9641
|
let dispatchParams = params;
|
|
9609
9642
|
if (command === "configure") {
|
|
9610
9643
|
dispatchParams = { ...params };
|
|
@@ -9642,11 +9675,13 @@ class BinaryBridge {
|
|
|
9642
9675
|
params.edit_slot_survives = editSlotSurvives;
|
|
9643
9676
|
this.logHashlineRegistrationCarrier("tool_call", sessionId, editSlotSurvives);
|
|
9644
9677
|
}
|
|
9645
|
-
const { preview, workerSession, ...sendOptions } = options ?? {};
|
|
9678
|
+
const { preview, workerSession, hostToolNames, ...sendOptions } = options ?? {};
|
|
9646
9679
|
if (preview === true)
|
|
9647
9680
|
params.preview = true;
|
|
9648
9681
|
if (workerSession === true)
|
|
9649
9682
|
params[WORKER_SESSION_FIELD] = true;
|
|
9683
|
+
if (hostToolNames)
|
|
9684
|
+
params[HOST_TOOL_NAMES_FIELD] = hostToolNames;
|
|
9650
9685
|
return await this.send("tool_call", params, Object.keys(sendOptions).length > 0 ? sendOptions : undefined);
|
|
9651
9686
|
}
|
|
9652
9687
|
async sendWithVersionMismatchRetry(command, params, options, canRetryAfterVersionSwap) {
|
|
@@ -9715,7 +9750,12 @@ class BinaryBridge {
|
|
|
9715
9750
|
if (command === "cancel_request" || Object.hasOwn(params, "command") || Object.hasOwn(params, "method")) {
|
|
9716
9751
|
const nested = { ...params };
|
|
9717
9752
|
const reserved = {};
|
|
9718
|
-
for (const key of [
|
|
9753
|
+
for (const key of [
|
|
9754
|
+
"session_id",
|
|
9755
|
+
"lsp_hints",
|
|
9756
|
+
WORKER_SESSION_FIELD,
|
|
9757
|
+
HOST_TOOL_NAMES_FIELD
|
|
9758
|
+
]) {
|
|
9719
9759
|
if (Object.hasOwn(nested, key)) {
|
|
9720
9760
|
reserved[key] = nested[key];
|
|
9721
9761
|
delete nested[key];
|
|
@@ -10090,6 +10130,8 @@ class BinaryBridge {
|
|
|
10090
10130
|
this.lastChildActivityAt = 0;
|
|
10091
10131
|
this.consecutiveRequestTimeouts = 0;
|
|
10092
10132
|
this.stderrTail = [];
|
|
10133
|
+
this.processLoopReferenced = true;
|
|
10134
|
+
this.syncEventLoopHold();
|
|
10093
10135
|
}
|
|
10094
10136
|
pushStderrLine(line) {
|
|
10095
10137
|
this.stderrTail.push(line);
|
|
@@ -10216,6 +10258,7 @@ class BinaryBridge {
|
|
|
10216
10258
|
const taskId = bashTaskIdFrom(response);
|
|
10217
10259
|
if (taskId)
|
|
10218
10260
|
this.outstandingBackgroundTaskIds.delete(taskId);
|
|
10261
|
+
this.syncEventLoopHold();
|
|
10219
10262
|
this.onBashCompletion?.(response, this);
|
|
10220
10263
|
return;
|
|
10221
10264
|
}
|
|
@@ -10329,7 +10372,7 @@ class BinaryBridge {
|
|
|
10329
10372
|
const delay = 100 * 2 ** this._restartCount;
|
|
10330
10373
|
this._restartCount++;
|
|
10331
10374
|
this.logVia(`Auto-restart #${this._restartCount} in ${delay}ms`);
|
|
10332
|
-
setTimeout(() => {
|
|
10375
|
+
const restartTimer = setTimeout(() => {
|
|
10333
10376
|
if (!this._shuttingDown && !this.isAlive()) {
|
|
10334
10377
|
try {
|
|
10335
10378
|
this.spawnProcess();
|
|
@@ -10338,6 +10381,7 @@ class BinaryBridge {
|
|
|
10338
10381
|
}
|
|
10339
10382
|
}
|
|
10340
10383
|
}, delay);
|
|
10384
|
+
restartTimer.unref?.();
|
|
10341
10385
|
this.scheduleRestartCountReset();
|
|
10342
10386
|
} else {
|
|
10343
10387
|
this.errorVia(`Max restarts (${this.maxRestarts}) reached, giving up. Logs: ${this.getLogFilePathVia()}${tail}`);
|
|
@@ -10358,6 +10402,7 @@ class BinaryBridge {
|
|
|
10358
10402
|
this._restartCount = 0;
|
|
10359
10403
|
this.restartResetTimer = null;
|
|
10360
10404
|
}, BinaryBridge.RESTART_RESET_MS);
|
|
10405
|
+
this.restartResetTimer.unref?.();
|
|
10361
10406
|
}
|
|
10362
10407
|
clearRestartResetTimer() {
|
|
10363
10408
|
if (this.restartResetTimer) {
|
|
@@ -11235,10 +11280,12 @@ function startLiveConfigReload(options) {
|
|
|
11235
11280
|
let acceptedTexts;
|
|
11236
11281
|
const reload = () => {
|
|
11237
11282
|
let loaded = options.load();
|
|
11283
|
+
let deletedProjectConfig = false;
|
|
11238
11284
|
if (loaded.ok) {
|
|
11239
11285
|
const read = new Set(loaded.sources);
|
|
11240
11286
|
const deleted = [...acceptedSources].find((path) => !read.has(path));
|
|
11241
|
-
|
|
11287
|
+
deletedProjectConfig = deleted === options.projectPath && loaded.projectText === null;
|
|
11288
|
+
if (deleted !== undefined && !deletedProjectConfig) {
|
|
11242
11289
|
loaded = { ok: false, message: `AFT config at ${deleted} was deleted` };
|
|
11243
11290
|
} else {
|
|
11244
11291
|
acceptedSources = read;
|
|
@@ -12065,17 +12112,17 @@ ${reflowText}`;
|
|
|
12065
12112
|
}
|
|
12066
12113
|
return " Auto-formatted.";
|
|
12067
12114
|
}
|
|
12068
|
-
// ../../node_modules/.bun/@cortexkit+subc-client@0.
|
|
12115
|
+
// ../../node_modules/.bun/@cortexkit+subc-client@0.21.2/node_modules/@cortexkit/subc-client/dist/client.js
|
|
12069
12116
|
import { promises as fs3 } from "node:fs";
|
|
12070
12117
|
import { debuglog } from "node:util";
|
|
12071
12118
|
|
|
12072
|
-
// ../../node_modules/.bun/@cortexkit+subc-client@0.
|
|
12119
|
+
// ../../node_modules/.bun/@cortexkit+subc-client@0.21.2/node_modules/@cortexkit/subc-client/dist/auth.js
|
|
12073
12120
|
import { createHmac, randomBytes, timingSafeEqual } from "node:crypto";
|
|
12074
12121
|
|
|
12075
|
-
// ../../node_modules/.bun/@cortexkit+subc-client@0.
|
|
12122
|
+
// ../../node_modules/.bun/@cortexkit+subc-client@0.21.2/node_modules/@cortexkit/subc-client/dist/socket.js
|
|
12076
12123
|
import net from "node:net";
|
|
12077
12124
|
|
|
12078
|
-
// ../../node_modules/.bun/@cortexkit+subc-client@0.
|
|
12125
|
+
// ../../node_modules/.bun/@cortexkit+subc-client@0.21.2/node_modules/@cortexkit/subc-client/dist/envelope.js
|
|
12079
12126
|
var PROTOCOL_VERSION = 2;
|
|
12080
12127
|
var HEADER_LEN = 21;
|
|
12081
12128
|
var FROZEN_PREFIX_LEN = 5;
|
|
@@ -12178,12 +12225,12 @@ function validateHeaderFields(header) {
|
|
|
12178
12225
|
if (admission === AdmissionClass.Sheddable && ty !== FrameType.Push && ty !== FrameType.StreamData) {
|
|
12179
12226
|
throw new DecodeError(`SHEDDABLE admission class is illegal on ${FrameType[ty]} in flags 0b${flags.toString(2).padStart(8, "0")}`, "sheddable_illegal_frame_type");
|
|
12180
12227
|
}
|
|
12181
|
-
if (channel === 0 && epoch !== 0) {
|
|
12182
|
-
throw new DecodeError(`control channel carried nonzero epoch ${epoch}`, "nonzero_epoch_on_control_channel");
|
|
12183
|
-
}
|
|
12184
12228
|
if (isPureHeader(ty) && len !== 0) {
|
|
12185
12229
|
throw new DecodeError(`pure-header frame ${FrameType[ty]} declared non-zero body length ${len}`, "pure_header_frame_with_body");
|
|
12186
12230
|
}
|
|
12231
|
+
if (channel === 0 && epoch !== 0) {
|
|
12232
|
+
throw new DecodeError(`control channel carried nonzero epoch ${epoch}`, "nonzero_epoch_on_control_channel");
|
|
12233
|
+
}
|
|
12187
12234
|
}
|
|
12188
12235
|
function decodeHeader(bytes) {
|
|
12189
12236
|
if (bytes.length < FROZEN_PREFIX_LEN) {
|
|
@@ -12217,12 +12264,12 @@ function decodeHeader(bytes) {
|
|
|
12217
12264
|
}
|
|
12218
12265
|
const channel = view.getUint16(7, true);
|
|
12219
12266
|
const epoch = view.getUint32(9, true);
|
|
12220
|
-
if (channel === 0 && epoch !== 0) {
|
|
12221
|
-
throw new DecodeError(`control channel carried nonzero epoch ${epoch}`, "nonzero_epoch_on_control_channel");
|
|
12222
|
-
}
|
|
12223
12267
|
if (isPureHeader(ty) && len !== 0) {
|
|
12224
12268
|
throw new DecodeError(`pure-header frame ${FrameType[ty]} declared non-zero body length ${len}`, "pure_header_frame_with_body");
|
|
12225
12269
|
}
|
|
12270
|
+
if (channel === 0 && epoch !== 0) {
|
|
12271
|
+
throw new DecodeError(`control channel carried nonzero epoch ${epoch}`, "nonzero_epoch_on_control_channel");
|
|
12272
|
+
}
|
|
12226
12273
|
return { len, ver, ty, flags, channel, epoch, corr: view.getBigUint64(13, true) };
|
|
12227
12274
|
}
|
|
12228
12275
|
function buildFrame(ty, flags, channel, epoch, corr, body) {
|
|
@@ -12240,14 +12287,18 @@ function encodeFrame(frame) {
|
|
|
12240
12287
|
if (frame.header.len !== frame.body.length) {
|
|
12241
12288
|
throw new DecodeError(`frame header length ${frame.header.len} does not match body length ${frame.body.length}`, "frame_length_mismatch");
|
|
12242
12289
|
}
|
|
12290
|
+
if (frame.body.length > MAX_FRAME_BODY_LEN) {
|
|
12291
|
+
throw new DecodeError(`frame body ${frame.body.length} exceeds max ${MAX_FRAME_BODY_LEN}`, "frame_body_too_large");
|
|
12292
|
+
}
|
|
12243
12293
|
const header = encodeHeader(frame.header);
|
|
12294
|
+
decodeHeader(header);
|
|
12244
12295
|
const output = new Uint8Array(header.length + frame.body.length);
|
|
12245
12296
|
output.set(header, 0);
|
|
12246
12297
|
output.set(frame.body, header.length);
|
|
12247
12298
|
return output;
|
|
12248
12299
|
}
|
|
12249
12300
|
|
|
12250
|
-
// ../../node_modules/.bun/@cortexkit+subc-client@0.
|
|
12301
|
+
// ../../node_modules/.bun/@cortexkit+subc-client@0.21.2/node_modules/@cortexkit/subc-client/dist/socket.js
|
|
12251
12302
|
class SocketClosedError extends Error {
|
|
12252
12303
|
}
|
|
12253
12304
|
|
|
@@ -12297,6 +12348,15 @@ class SubcSocket {
|
|
|
12297
12348
|
sock.on("data", (chunk) => {
|
|
12298
12349
|
this.chunks.push(chunk);
|
|
12299
12350
|
this.buffered += chunk.length;
|
|
12351
|
+
if (this.waiter) {
|
|
12352
|
+
if (this.waiter.immediate) {
|
|
12353
|
+
clearImmediate(this.waiter.immediate);
|
|
12354
|
+
this.waiter.immediate = null;
|
|
12355
|
+
}
|
|
12356
|
+
if (this.waiter.extendOnProgress && this.buffered < this.waiter.need) {
|
|
12357
|
+
this.armWaiterTimer(this.waiter);
|
|
12358
|
+
}
|
|
12359
|
+
}
|
|
12300
12360
|
this.tryServe();
|
|
12301
12361
|
});
|
|
12302
12362
|
const fail = (err) => {
|
|
@@ -12343,30 +12403,80 @@ class SubcSocket {
|
|
|
12343
12403
|
throw new DecodeError(`frame body ${header.len} exceeds max ${MAX_FRAME_BODY_LEN}`, "frame_body_too_large");
|
|
12344
12404
|
}
|
|
12345
12405
|
onHeader?.();
|
|
12346
|
-
const
|
|
12347
|
-
const body = header.len === 0 ? new Uint8Array(0) : await this.readExact(header.len,
|
|
12406
|
+
const bodyDeadlineArg = typeof bodyDeadline === "number" ? bodyDeadline : { idleTimeoutMs: bodyDeadline.afterHeaderMs };
|
|
12407
|
+
const body = header.len === 0 ? new Uint8Array(0) : await this.readExact(header.len, bodyDeadlineArg);
|
|
12348
12408
|
return { header, body };
|
|
12349
12409
|
}
|
|
12350
|
-
|
|
12410
|
+
armWaiterTimer(w) {
|
|
12411
|
+
if (!Number.isFinite(w.budget))
|
|
12412
|
+
return;
|
|
12413
|
+
if (w.timer) {
|
|
12414
|
+
clearTimeout(w.timer);
|
|
12415
|
+
w.timer = null;
|
|
12416
|
+
}
|
|
12417
|
+
w.timer = setTimeout(() => {
|
|
12418
|
+
w.timer = null;
|
|
12419
|
+
const bufferedAtTimeout = this.buffered;
|
|
12420
|
+
const verifyTimeout = () => {
|
|
12421
|
+
if (this.waiter !== w)
|
|
12422
|
+
return;
|
|
12423
|
+
if (w.extendOnProgress && this.buffered > bufferedAtTimeout) {
|
|
12424
|
+
this.armWaiterTimer(w);
|
|
12425
|
+
return;
|
|
12426
|
+
}
|
|
12427
|
+
this.waiter = null;
|
|
12428
|
+
w.reject(new SocketTimeoutError(`timed out waiting for ${w.need} bytes`));
|
|
12429
|
+
};
|
|
12430
|
+
w.immediate = setImmediate(() => {
|
|
12431
|
+
if (this.waiter !== w)
|
|
12432
|
+
return;
|
|
12433
|
+
if (this.buffered > bufferedAtTimeout) {
|
|
12434
|
+
verifyTimeout();
|
|
12435
|
+
} else {
|
|
12436
|
+
w.immediate = setImmediate(() => {
|
|
12437
|
+
w.immediate = null;
|
|
12438
|
+
verifyTimeout();
|
|
12439
|
+
});
|
|
12440
|
+
}
|
|
12441
|
+
});
|
|
12442
|
+
}, w.budget);
|
|
12443
|
+
}
|
|
12444
|
+
readExact(n, deadline) {
|
|
12351
12445
|
if (this.waiter) {
|
|
12352
12446
|
return Promise.reject(new Error("concurrent readExact is not supported"));
|
|
12353
12447
|
}
|
|
12354
12448
|
if (n === 0)
|
|
12355
12449
|
return Promise.resolve(new Uint8Array(0));
|
|
12356
12450
|
return new Promise((resolve, reject) => {
|
|
12357
|
-
let
|
|
12358
|
-
|
|
12359
|
-
|
|
12360
|
-
|
|
12451
|
+
let budget;
|
|
12452
|
+
const extendOnProgress = typeof deadline === "object";
|
|
12453
|
+
if (typeof deadline === "object") {
|
|
12454
|
+
budget = deadline.idleTimeoutMs;
|
|
12455
|
+
if (budget <= 0) {
|
|
12361
12456
|
reject(new SocketTimeoutError(`timed out waiting for ${n} bytes`));
|
|
12362
12457
|
return;
|
|
12363
12458
|
}
|
|
12364
|
-
|
|
12365
|
-
|
|
12459
|
+
} else if (Number.isFinite(deadline)) {
|
|
12460
|
+
const remaining = deadline - Date.now();
|
|
12461
|
+
if (remaining <= 0) {
|
|
12366
12462
|
reject(new SocketTimeoutError(`timed out waiting for ${n} bytes`));
|
|
12367
|
-
|
|
12463
|
+
return;
|
|
12464
|
+
}
|
|
12465
|
+
budget = remaining;
|
|
12466
|
+
} else {
|
|
12467
|
+
budget = Number.POSITIVE_INFINITY;
|
|
12368
12468
|
}
|
|
12369
|
-
|
|
12469
|
+
const waiter = {
|
|
12470
|
+
need: n,
|
|
12471
|
+
resolve,
|
|
12472
|
+
reject,
|
|
12473
|
+
timer: null,
|
|
12474
|
+
immediate: null,
|
|
12475
|
+
budget,
|
|
12476
|
+
extendOnProgress
|
|
12477
|
+
};
|
|
12478
|
+
this.waiter = waiter;
|
|
12479
|
+
this.armWaiterTimer(waiter);
|
|
12370
12480
|
this.tryServe();
|
|
12371
12481
|
});
|
|
12372
12482
|
}
|
|
@@ -12449,6 +12559,8 @@ class SubcSocket {
|
|
|
12449
12559
|
this.waiter = null;
|
|
12450
12560
|
if (w.timer)
|
|
12451
12561
|
clearTimeout(w.timer);
|
|
12562
|
+
if (w.immediate)
|
|
12563
|
+
clearImmediate(w.immediate);
|
|
12452
12564
|
w.resolve(out);
|
|
12453
12565
|
return;
|
|
12454
12566
|
}
|
|
@@ -12456,6 +12568,8 @@ class SubcSocket {
|
|
|
12456
12568
|
this.waiter = null;
|
|
12457
12569
|
if (w.timer)
|
|
12458
12570
|
clearTimeout(w.timer);
|
|
12571
|
+
if (w.immediate)
|
|
12572
|
+
clearImmediate(w.immediate);
|
|
12459
12573
|
w.reject(this.closedErr);
|
|
12460
12574
|
}
|
|
12461
12575
|
}
|
|
@@ -12480,7 +12594,7 @@ class SubcSocket {
|
|
|
12480
12594
|
}
|
|
12481
12595
|
}
|
|
12482
12596
|
|
|
12483
|
-
// ../../node_modules/.bun/@cortexkit+subc-client@0.
|
|
12597
|
+
// ../../node_modules/.bun/@cortexkit+subc-client@0.21.2/node_modules/@cortexkit/subc-client/dist/auth.js
|
|
12484
12598
|
var NONCE_LEN = 32;
|
|
12485
12599
|
var MAX_AUTH_MESSAGE_LEN = 4096;
|
|
12486
12600
|
var SERVER_PROOF_DOMAIN = "subc-server-v1";
|
|
@@ -12554,7 +12668,7 @@ async function authenticateClient(sock, conn, deadlineMs) {
|
|
|
12554
12668
|
await writeMessage(sock, { client_auth: Array.from(clientAuth) }, deadlineMs);
|
|
12555
12669
|
}
|
|
12556
12670
|
|
|
12557
|
-
// ../../node_modules/.bun/@cortexkit+subc-client@0.
|
|
12671
|
+
// ../../node_modules/.bun/@cortexkit+subc-client@0.21.2/node_modules/@cortexkit/subc-client/dist/connection-file.js
|
|
12558
12672
|
import { promises as fs2 } from "node:fs";
|
|
12559
12673
|
var SCHEMA_VERSION = 1;
|
|
12560
12674
|
var MIN_KEY_LEN = 32;
|
|
@@ -12587,18 +12701,29 @@ function validate(info) {
|
|
|
12587
12701
|
throw new ConnectionFileError(`connection file daemon_id must be ${DAEMON_ID_LEN} bytes, got ${info.daemonId.length}`);
|
|
12588
12702
|
}
|
|
12589
12703
|
}
|
|
12590
|
-
|
|
12704
|
+
function verifyOwnerOnly(path, stat, expectedUid) {
|
|
12591
12705
|
if (process.platform === "win32")
|
|
12592
12706
|
return;
|
|
12593
|
-
|
|
12707
|
+
if (expectedUid !== undefined && stat.uid !== expectedUid) {
|
|
12708
|
+
throw new ConnectionFileError(`connection file ${path} is owned by uid ${stat.uid}, expected effective uid ${expectedUid}`);
|
|
12709
|
+
}
|
|
12594
12710
|
const mode = stat.mode & 511;
|
|
12595
12711
|
if ((mode & 63) !== 0) {
|
|
12596
12712
|
throw new ConnectionFileError(`connection file ${path} has insecure permissions 0o${mode.toString(8)}; expected owner-only 0600`);
|
|
12597
12713
|
}
|
|
12598
12714
|
}
|
|
12599
12715
|
async function readConnectionFile(path) {
|
|
12600
|
-
|
|
12601
|
-
|
|
12716
|
+
return readConnectionFileForUid(path, process.geteuid?.());
|
|
12717
|
+
}
|
|
12718
|
+
async function readConnectionFileForUid(path, expectedUid) {
|
|
12719
|
+
const handle = await fs2.open(path, "r");
|
|
12720
|
+
let raw;
|
|
12721
|
+
try {
|
|
12722
|
+
verifyOwnerOnly(path, await handle.stat(), expectedUid);
|
|
12723
|
+
raw = await handle.readFile({ encoding: "utf8" });
|
|
12724
|
+
} finally {
|
|
12725
|
+
await handle.close();
|
|
12726
|
+
}
|
|
12602
12727
|
let parsed;
|
|
12603
12728
|
try {
|
|
12604
12729
|
parsed = JSON.parse(raw);
|
|
@@ -12632,7 +12757,7 @@ async function readConnectionFile(path) {
|
|
|
12632
12757
|
return info;
|
|
12633
12758
|
}
|
|
12634
12759
|
|
|
12635
|
-
// ../../node_modules/.bun/@cortexkit+subc-client@0.
|
|
12760
|
+
// ../../node_modules/.bun/@cortexkit+subc-client@0.21.2/node_modules/@cortexkit/subc-client/dist/launch-nonce.js
|
|
12636
12761
|
import { closeSync as closeSync4, fstatSync as fstatSync2, readSync as readSync3 } from "node:fs";
|
|
12637
12762
|
var SUBC_LAUNCH_NONCE_ENV = "SUBC_LAUNCH_NONCE";
|
|
12638
12763
|
var SUBC_LAUNCH_NONCE_FD_ENV = "SUBC_LAUNCH_NONCE_FD";
|
|
@@ -12821,7 +12946,7 @@ function closeQuietly(fd) {
|
|
|
12821
12946
|
} catch {}
|
|
12822
12947
|
}
|
|
12823
12948
|
|
|
12824
|
-
// ../../node_modules/.bun/@cortexkit+subc-client@0.
|
|
12949
|
+
// ../../node_modules/.bun/@cortexkit+subc-client@0.21.2/node_modules/@cortexkit/subc-client/dist/route-handle.js
|
|
12825
12950
|
var connectionToken = new WeakMap;
|
|
12826
12951
|
var reverseRequests = new WeakMap;
|
|
12827
12952
|
|
|
@@ -12914,7 +13039,7 @@ function sameRouteHandle(left, right) {
|
|
|
12914
13039
|
return left === right;
|
|
12915
13040
|
}
|
|
12916
13041
|
|
|
12917
|
-
// ../../node_modules/.bun/@cortexkit+subc-client@0.
|
|
13042
|
+
// ../../node_modules/.bun/@cortexkit+subc-client@0.21.2/node_modules/@cortexkit/subc-client/dist/client.js
|
|
12918
13043
|
var debug = debuglog("subc-client");
|
|
12919
13044
|
var DEFAULT_HANDSHAKE_TIMEOUT_MS = 1e4;
|
|
12920
13045
|
var DEFAULT_REQUEST_TIMEOUT_MS = 30000;
|
|
@@ -12971,6 +13096,12 @@ function requireBinaryBody(body) {
|
|
|
12971
13096
|
const type = body === null ? "null" : Array.isArray(body) ? "array" : typeof body;
|
|
12972
13097
|
throw new SubcError(`binary request body must be a Uint8Array; got ${type}`, "binary_body_required");
|
|
12973
13098
|
}
|
|
13099
|
+
var SCOPE_ROUTE_CLOSE_REASONS = new Set([
|
|
13100
|
+
"scope_ended",
|
|
13101
|
+
"scope_carrier_removed",
|
|
13102
|
+
"scope_delegation_changed",
|
|
13103
|
+
"scope_parent_ended"
|
|
13104
|
+
]);
|
|
12974
13105
|
function parseRouteCloseReason(reason) {
|
|
12975
13106
|
switch (reason) {
|
|
12976
13107
|
case "reload":
|
|
@@ -13113,14 +13244,16 @@ class SubcClient {
|
|
|
13113
13244
|
return modules.filter((module) => module.capabilities?.provides.includes(capability) ?? false).map((module) => module.module_id).sort();
|
|
13114
13245
|
}
|
|
13115
13246
|
async routeOpen(target, identity, opts = {}) {
|
|
13247
|
+
const scope = validatedRouteScope(opts.scope);
|
|
13116
13248
|
const release = await this.routeOpenGate.acquire();
|
|
13117
13249
|
try {
|
|
13118
|
-
return await this.sendRouteOpen(target, identity, opts);
|
|
13250
|
+
return await this.sendRouteOpen(target, identity, { ...opts, scope });
|
|
13119
13251
|
} finally {
|
|
13120
13252
|
release();
|
|
13121
13253
|
}
|
|
13122
13254
|
}
|
|
13123
13255
|
async sendRouteOpen(target, identity, opts = {}) {
|
|
13256
|
+
const scope = validatedRouteScope(opts.scope);
|
|
13124
13257
|
const consumerIdentity = routeOpenConsumerIdentity(opts);
|
|
13125
13258
|
const reverseRequests = opts.reverseRequests ?? new ReverseRequestRegistry;
|
|
13126
13259
|
reverseRequests.seal();
|
|
@@ -13130,7 +13263,8 @@ class SubcClient {
|
|
|
13130
13263
|
target,
|
|
13131
13264
|
identity,
|
|
13132
13265
|
...consumerIdentity ? { consumer_identity: consumerIdentity } : {},
|
|
13133
|
-
...consumerCapabilities.length > 0 ? { consumer_capabilities: consumerCapabilities } : {}
|
|
13266
|
+
...consumerCapabilities.length > 0 ? { consumer_capabilities: consumerCapabilities } : {},
|
|
13267
|
+
...scope ? { scope: { owner: scope.owner, ref: scope.ref, scope_epoch: scope.scopeEpoch } } : {}
|
|
13134
13268
|
});
|
|
13135
13269
|
let installed = null;
|
|
13136
13270
|
const install = (frame) => {
|
|
@@ -13333,7 +13467,7 @@ class SubcClient {
|
|
|
13333
13467
|
this.sendRouteGoodbye(handle);
|
|
13334
13468
|
}
|
|
13335
13469
|
async closeManagedRoute(target, identity, opts = {}) {
|
|
13336
|
-
const key = routeCacheKey(target, identity, routeOpenConsumerIdentity(opts), (opts.reverseRequests ?? new ReverseRequestRegistry).capabilities());
|
|
13470
|
+
const key = routeCacheKey(target, identity, routeOpenConsumerIdentity(opts), (opts.reverseRequests ?? new ReverseRequestRegistry).capabilities(), validatedRouteScope(opts.scope));
|
|
13337
13471
|
const cached = this.routes.get(key);
|
|
13338
13472
|
if (!cached)
|
|
13339
13473
|
return;
|
|
@@ -13429,13 +13563,13 @@ class SubcClient {
|
|
|
13429
13563
|
};
|
|
13430
13564
|
pending.timer = setTimeout(() => this.arbitrateTimeout(key, pending, channel, corr, ms), ms);
|
|
13431
13565
|
this.pending.set(key, pending);
|
|
13432
|
-
if (signal)
|
|
13433
|
-
this.attachCancelSignal(signal, key, handle, corr, priority);
|
|
13434
13566
|
writeBorrowed(this.sock, encodeFrame(frame), Date.now() + ms).catch((error) => {
|
|
13435
13567
|
const current = this.pending.get(key);
|
|
13436
13568
|
if (current)
|
|
13437
13569
|
this.rejectPending(key, current, error instanceof Error ? error : new SubcError(String(error)));
|
|
13438
13570
|
});
|
|
13571
|
+
if (signal)
|
|
13572
|
+
this.attachCancelSignal(signal, key, handle, corr, priority);
|
|
13439
13573
|
});
|
|
13440
13574
|
}
|
|
13441
13575
|
arbitrateTimeout(key, pending, channel, corr, ms) {
|
|
@@ -13509,8 +13643,6 @@ class SubcClient {
|
|
|
13509
13643
|
};
|
|
13510
13644
|
pending.timer = setTimeout(() => this.arbitrateTimeout(key, pending, handle.channel, corr, ms), ms);
|
|
13511
13645
|
this.pending.set(key, pending);
|
|
13512
|
-
if (signal)
|
|
13513
|
-
this.attachCancelSignal(signal, key, handle, corr, priority);
|
|
13514
13646
|
const write = writeTrackedBorrowed(this.sock, encodeFrame(frame), Date.now() + ms);
|
|
13515
13647
|
handedToSocket = write.queued;
|
|
13516
13648
|
write.completed.catch((error) => {
|
|
@@ -13518,9 +13650,12 @@ class SubcClient {
|
|
|
13518
13650
|
if (current)
|
|
13519
13651
|
this.rejectPending(key, current, error instanceof Error ? error : new SubcError(String(error)));
|
|
13520
13652
|
});
|
|
13653
|
+
if (signal && write.queued)
|
|
13654
|
+
this.attachCancelSignal(signal, key, handle, corr, priority);
|
|
13521
13655
|
});
|
|
13522
13656
|
}
|
|
13523
13657
|
async cachedRouteHandle(moduleId, opts) {
|
|
13658
|
+
const scope = validatedRouteScope(opts.scope);
|
|
13524
13659
|
const identity = opts.identity ?? this.opts.identity;
|
|
13525
13660
|
if (!identity) {
|
|
13526
13661
|
throw new SubcCallError("terminal", "managed call requires a BindIdentity in SubcClient.connect({ identity }) or call(..., { identity })", "missing_identity");
|
|
@@ -13529,7 +13664,7 @@ class SubcClient {
|
|
|
13529
13664
|
const consumerIdentity = routeOpenConsumerIdentity(opts);
|
|
13530
13665
|
const reverseRequests = opts.reverseRequests ?? new ReverseRequestRegistry;
|
|
13531
13666
|
reverseRequests.seal();
|
|
13532
|
-
const key = routeCacheKey(target, identity, consumerIdentity, reverseRequests.capabilities());
|
|
13667
|
+
const key = routeCacheKey(target, identity, consumerIdentity, reverseRequests.capabilities(), scope);
|
|
13533
13668
|
let cached = this.routes.get(key);
|
|
13534
13669
|
if (!cached) {
|
|
13535
13670
|
cached = {
|
|
@@ -13537,6 +13672,7 @@ class SubcClient {
|
|
|
13537
13672
|
moduleId,
|
|
13538
13673
|
target,
|
|
13539
13674
|
identity,
|
|
13675
|
+
scope,
|
|
13540
13676
|
consumerIdentity,
|
|
13541
13677
|
reverseRequests,
|
|
13542
13678
|
handle: null,
|
|
@@ -13544,6 +13680,8 @@ class SubcClient {
|
|
|
13544
13680
|
};
|
|
13545
13681
|
this.routes.set(key, cached);
|
|
13546
13682
|
}
|
|
13683
|
+
if (cached.endReason !== undefined)
|
|
13684
|
+
throw this.endedCachedRoute(cached.endReason);
|
|
13547
13685
|
if (cached.handle && this.isLiveHandle(cached.handle))
|
|
13548
13686
|
return cached.handle;
|
|
13549
13687
|
const startedAt = this.opts.now();
|
|
@@ -13579,6 +13717,8 @@ class SubcClient {
|
|
|
13579
13717
|
for (;; ) {
|
|
13580
13718
|
if (cached.closed)
|
|
13581
13719
|
throw this.routeClosedDuringOpen();
|
|
13720
|
+
if (cached.endReason !== undefined)
|
|
13721
|
+
throw this.endedCachedRoute(cached.endReason);
|
|
13582
13722
|
try {
|
|
13583
13723
|
await this.ensureConnectedForManaged();
|
|
13584
13724
|
} catch (error) {
|
|
@@ -13593,6 +13733,7 @@ class SubcClient {
|
|
|
13593
13733
|
try {
|
|
13594
13734
|
outcome = {
|
|
13595
13735
|
handle: await this.sendRouteOpen(cached.target, cached.identity, {
|
|
13736
|
+
scope: cached.scope,
|
|
13596
13737
|
consumerIdentity: cached.consumerIdentity ?? null,
|
|
13597
13738
|
reverseRequests: cached.reverseRequests
|
|
13598
13739
|
})
|
|
@@ -13760,10 +13901,11 @@ class SubcClient {
|
|
|
13760
13901
|
}
|
|
13761
13902
|
for (const key of routeKeys) {
|
|
13762
13903
|
const cached = this.routes.get(key);
|
|
13763
|
-
if (!cached || cached.closed)
|
|
13904
|
+
if (!cached || cached.closed || cached.endReason !== undefined)
|
|
13764
13905
|
continue;
|
|
13765
13906
|
try {
|
|
13766
13907
|
const handle = await this.routeOpen(cached.target, cached.identity, {
|
|
13908
|
+
scope: cached.scope,
|
|
13767
13909
|
consumerIdentity: cached.consumerIdentity ?? null,
|
|
13768
13910
|
reverseRequests: cached.reverseRequests
|
|
13769
13911
|
});
|
|
@@ -13791,11 +13933,15 @@ class SubcClient {
|
|
|
13791
13933
|
const cause = new SubcError("route was closed before route.open completed", "route_closed", undefined, "closed_by_caller");
|
|
13792
13934
|
return new SubcCallError("not_sent", cause.message, cause.code, cause);
|
|
13793
13935
|
}
|
|
13936
|
+
endedCachedRoute(reason) {
|
|
13937
|
+
const cause = new SubcError(`cached route closed by subc (${reason}); must not reopen`, "route_closed", undefined, reason);
|
|
13938
|
+
return this.terminalCallError(cause.message, cause);
|
|
13939
|
+
}
|
|
13794
13940
|
async readLoop(sock, generation) {
|
|
13795
13941
|
try {
|
|
13796
13942
|
for (;; ) {
|
|
13797
13943
|
this.readerActive = false;
|
|
13798
|
-
const frame = await sock.readFrame(Number.POSITIVE_INFINITY, { afterHeaderMs: BODY_READ_TIMEOUT_MS }, () => {
|
|
13944
|
+
const frame = await sock.readFrame(Number.POSITIVE_INFINITY, { afterHeaderMs: this.opts.bodyReadTimeoutMs ?? BODY_READ_TIMEOUT_MS }, () => {
|
|
13799
13945
|
this.readerActive = true;
|
|
13800
13946
|
});
|
|
13801
13947
|
try {
|
|
@@ -13874,6 +14020,7 @@ class SubcClient {
|
|
|
13874
14020
|
}
|
|
13875
14021
|
if (frame.header.ty === FrameType.Goodbye && handle) {
|
|
13876
14022
|
this.failHandle(handle, new SubcError("route closed by subc (GOODBYE)", "route_closed", undefined, this.routeEndReason(handle.channel)));
|
|
14023
|
+
this.handleCachedRouteClose(handle.channel, this.routeEndReason(handle.channel));
|
|
13877
14024
|
if (this.liveRoutes.get(handle.channel) === handle)
|
|
13878
14025
|
this.liveRoutes.delete(handle.channel);
|
|
13879
14026
|
this.evictRouteHandle(handle);
|
|
@@ -13963,13 +14110,29 @@ class SubcClient {
|
|
|
13963
14110
|
const final = push.op === "route.closed";
|
|
13964
14111
|
if (final || !this.routeEndReasons.get(channel)?.final) {
|
|
13965
14112
|
this.routeEndReasons.set(channel, { reason, final });
|
|
14113
|
+
if (final)
|
|
14114
|
+
this.handleCachedRouteClose(channel, reason);
|
|
13966
14115
|
}
|
|
13967
14116
|
}
|
|
13968
14117
|
}
|
|
13969
14118
|
} else if (typeof push.body.module_id === "string") {
|
|
13970
14119
|
for (const [channel, module] of this.routeModules) {
|
|
13971
|
-
if (module === push.body.module_id)
|
|
14120
|
+
if (module === push.body.module_id) {
|
|
13972
14121
|
this.legacyChannelReasons.set(channel, reason);
|
|
14122
|
+
if (push.op === "route.closed")
|
|
14123
|
+
this.handleCachedRouteClose(channel, reason);
|
|
14124
|
+
}
|
|
14125
|
+
}
|
|
14126
|
+
}
|
|
14127
|
+
}
|
|
14128
|
+
handleCachedRouteClose(channel, reason) {
|
|
14129
|
+
for (const [key, cached] of this.routes) {
|
|
14130
|
+
if (cached.handle?.channel !== channel)
|
|
14131
|
+
continue;
|
|
14132
|
+
if (SCOPE_ROUTE_CLOSE_REASONS.has(reason)) {
|
|
14133
|
+
cached.endReason = reason;
|
|
14134
|
+
} else {
|
|
14135
|
+
this.routes.delete(key);
|
|
13973
14136
|
}
|
|
13974
14137
|
}
|
|
13975
14138
|
}
|
|
@@ -13992,6 +14155,10 @@ class SubcClient {
|
|
|
13992
14155
|
this.routeEndReasons.clear();
|
|
13993
14156
|
this.legacyChannelReasons.clear();
|
|
13994
14157
|
this.routeModules.clear();
|
|
14158
|
+
for (const [key, cached] of this.routes) {
|
|
14159
|
+
if (cached.endReason === undefined)
|
|
14160
|
+
this.routes.delete(key);
|
|
14161
|
+
}
|
|
13995
14162
|
for (const [key, pending] of this.pending) {
|
|
13996
14163
|
this.rejectPending(key, pending, pending.handle ? routeError : err);
|
|
13997
14164
|
}
|
|
@@ -14086,6 +14253,10 @@ function isRouteOpenRefusal(err) {
|
|
|
14086
14253
|
function isRetryableRouteOpenCode(code) {
|
|
14087
14254
|
if (code === "capability_forbidden")
|
|
14088
14255
|
return false;
|
|
14256
|
+
if (code === "target_flow_unsupported")
|
|
14257
|
+
return false;
|
|
14258
|
+
if (code === "target_agent_run_unsupported")
|
|
14259
|
+
return false;
|
|
14089
14260
|
return code === "module_reloading" || code === "module_warming" || code === "target_unavailable" || code === "module_timeout" || code === "scope_not_synced" || code === "scope_changed";
|
|
14090
14261
|
}
|
|
14091
14262
|
function equalJitter(delayMs, random) {
|
|
@@ -14143,6 +14314,7 @@ function normalizeConnectOptions(opts) {
|
|
|
14143
14314
|
random: opts.random ?? Math.random,
|
|
14144
14315
|
routeOpenRetryDeadlineMs: opts.routeOpenRetryDeadlineMs ?? ROUTE_OPEN_RETRY_DEADLINE_MS,
|
|
14145
14316
|
timeoutArbitrationGraceMs: opts.timeoutArbitrationGraceMs ?? TIMEOUT_ARBITRATION_GRACE_MS,
|
|
14317
|
+
bodyReadTimeoutMs: opts.bodyReadTimeoutMs,
|
|
14146
14318
|
livenessProbeWindowMs: opts.livenessProbeWindowMs ?? LIVENESS_PROBE_WINDOW_MS,
|
|
14147
14319
|
onControlPush: opts.onControlPush
|
|
14148
14320
|
};
|
|
@@ -14166,10 +14338,28 @@ function isValidCapabilityIdentifier(identifier) {
|
|
|
14166
14338
|
const numericVersion = Number(version);
|
|
14167
14339
|
return Number.isSafeInteger(numericVersion) && numericVersion >= 1 && numericVersion <= 4294967295;
|
|
14168
14340
|
}
|
|
14169
|
-
function routeCacheKey(target, identity, consumerIdentity, consumerCapabilities) {
|
|
14341
|
+
function routeCacheKey(target, identity, consumerIdentity, consumerCapabilities, scope) {
|
|
14170
14342
|
const consumerPart = consumerIdentity ? `${consumerIdentity.module_id}\x00${consumerIdentity.launch_nonce}` : "";
|
|
14171
14343
|
const capabilitiesPart = [...consumerCapabilities].sort().join("\x00");
|
|
14172
|
-
|
|
14344
|
+
const scopePart = scope ? JSON.stringify([scope.owner.kind, scope.owner.kind === "reserved" ? scope.owner.module_id : null, scope.ref, scope.scopeEpoch]) : "";
|
|
14345
|
+
return `${target.kind}\x00${target.module_id}\x00${identity.project_root}\x00${identity.harness}\x00${identity.session}\x00${consumerPart}\x00${capabilitiesPart}\x00${scopePart}`;
|
|
14346
|
+
}
|
|
14347
|
+
function validatedRouteScope(scope) {
|
|
14348
|
+
if (scope === undefined)
|
|
14349
|
+
return;
|
|
14350
|
+
if (!scope || typeof scope.owner !== "object" || !scope.owner || !["reserved", "direct", "unverified"].includes(scope.owner.kind) || scope.owner.kind === "reserved" && (typeof scope.owner.module_id !== "string" || scope.owner.module_id.length === 0)) {
|
|
14351
|
+
throw new SubcError("scope.owner must be a Principal with a non-empty reserved module_id", "invalid_scope");
|
|
14352
|
+
}
|
|
14353
|
+
if (typeof scope.ref !== "string" || scope.ref.length === 0) {
|
|
14354
|
+
throw new SubcError("scope.ref must be a non-empty string", "invalid_scope");
|
|
14355
|
+
}
|
|
14356
|
+
if (scope.scopeEpoch === undefined) {
|
|
14357
|
+
throw new SubcError("scope.scopeEpoch is required", "scope_epoch_required");
|
|
14358
|
+
}
|
|
14359
|
+
if (!Number.isSafeInteger(scope.scopeEpoch) || scope.scopeEpoch < 0) {
|
|
14360
|
+
throw new SubcError("scope.scopeEpoch must be a safe non-negative integer", "invalid_scope");
|
|
14361
|
+
}
|
|
14362
|
+
return { owner: { ...scope.owner }, ref: scope.ref, scopeEpoch: scope.scopeEpoch };
|
|
14173
14363
|
}
|
|
14174
14364
|
function routeOpenConsumerIdentity(opts = {}) {
|
|
14175
14365
|
if (opts.consumerIdentity !== undefined)
|
|
@@ -14686,6 +14876,41 @@ class SubcRouteClosedMidCallError extends SubcCallError {
|
|
|
14686
14876
|
this.reason = reason;
|
|
14687
14877
|
}
|
|
14688
14878
|
}
|
|
14879
|
+
|
|
14880
|
+
class SubcFrameReadTimeoutError extends SubcCallError {
|
|
14881
|
+
constructor(cause) {
|
|
14882
|
+
super("outcome_unknown", "the command may have run; timed out waiting for frame body bytes from the AFT daemon", "frame_read_timeout", cause);
|
|
14883
|
+
this.name = "SubcFrameReadTimeoutError";
|
|
14884
|
+
}
|
|
14885
|
+
}
|
|
14886
|
+
function isFrameBodyReadTimeoutMessage(message) {
|
|
14887
|
+
return typeof message === "string" && (/timed out waiting for \d+ bytes/i.test(message) || message.includes("timed out waiting for frame body bytes"));
|
|
14888
|
+
}
|
|
14889
|
+
function isSubcFrameReadTimeoutError(error) {
|
|
14890
|
+
if (error instanceof SubcFrameReadTimeoutError)
|
|
14891
|
+
return true;
|
|
14892
|
+
if (error instanceof SocketTimeoutError && isFrameBodyReadTimeoutMessage(error.message)) {
|
|
14893
|
+
return true;
|
|
14894
|
+
}
|
|
14895
|
+
if (error instanceof SubcError && isFrameBodyReadTimeoutMessage(error.message)) {
|
|
14896
|
+
return true;
|
|
14897
|
+
}
|
|
14898
|
+
if (error instanceof SubcCallError) {
|
|
14899
|
+
if (error.code === "frame_read_timeout")
|
|
14900
|
+
return true;
|
|
14901
|
+
if (error.cause !== undefined && error.cause !== error && isSubcFrameReadTimeoutError(error.cause)) {
|
|
14902
|
+
return true;
|
|
14903
|
+
}
|
|
14904
|
+
if (isFrameBodyReadTimeoutMessage(error.message))
|
|
14905
|
+
return true;
|
|
14906
|
+
}
|
|
14907
|
+
return false;
|
|
14908
|
+
}
|
|
14909
|
+
function frameReadTimeoutError(error) {
|
|
14910
|
+
if (!isSubcFrameReadTimeoutError(error))
|
|
14911
|
+
return error;
|
|
14912
|
+
return error instanceof SubcFrameReadTimeoutError ? error : new SubcFrameReadTimeoutError(error);
|
|
14913
|
+
}
|
|
14689
14914
|
function isLocalRouteCloseRejection(error) {
|
|
14690
14915
|
return error instanceof SubcError && error.closeReason === "closed_by_caller";
|
|
14691
14916
|
}
|
|
@@ -14712,6 +14937,8 @@ function isSubcClientClosedError(error) {
|
|
|
14712
14937
|
return error instanceof SubcError && error.message === "client closed";
|
|
14713
14938
|
}
|
|
14714
14939
|
function isSocketDeathError(error, depth = 0) {
|
|
14940
|
+
if (isSubcFrameReadTimeoutError(error))
|
|
14941
|
+
return true;
|
|
14715
14942
|
if (error instanceof SubcError && error.closeReason === "connection_lost")
|
|
14716
14943
|
return true;
|
|
14717
14944
|
if (error instanceof SubcCallError) {
|
|
@@ -14754,6 +14981,12 @@ class SubcLivenessProbeTimeoutError extends Error {
|
|
|
14754
14981
|
}
|
|
14755
14982
|
}
|
|
14756
14983
|
var AFT_MODULE_ID = "aft";
|
|
14984
|
+
var HOST_SCOPE_LOOKUP_TIMEOUT_MS = 2000;
|
|
14985
|
+
var HOST_SCOPE_RETRY_FLOOR_MS = 5000;
|
|
14986
|
+
var HOST_SCOPE_RETRY_CAP_MS = 300000;
|
|
14987
|
+
function isHostScopeSyncRefusal(code) {
|
|
14988
|
+
return code === "scope_not_live" || code === "scope_not_synced" || code === "scope_changed";
|
|
14989
|
+
}
|
|
14757
14990
|
var MAX_CONSECUTIVE_TRANSPORT_FAILURES = 3;
|
|
14758
14991
|
var LIVENESS_PROBE_TIMEOUT_MS = 15000;
|
|
14759
14992
|
var REQUEST_DEADLINE_CODE = "request_deadline";
|
|
@@ -14832,7 +15065,7 @@ class BgSubscription {
|
|
|
14832
15065
|
identity;
|
|
14833
15066
|
acquireClient;
|
|
14834
15067
|
dropClient;
|
|
14835
|
-
|
|
15068
|
+
openRoute;
|
|
14836
15069
|
onNudge;
|
|
14837
15070
|
sleep;
|
|
14838
15071
|
canAttach;
|
|
@@ -14843,21 +15076,25 @@ class BgSubscription {
|
|
|
14843
15076
|
isCurrent;
|
|
14844
15077
|
noteClientAlive;
|
|
14845
15078
|
noteUnansweredCall;
|
|
15079
|
+
onRouteAttached;
|
|
14846
15080
|
stopped = false;
|
|
14847
15081
|
current = null;
|
|
14848
15082
|
attached = null;
|
|
15083
|
+
route = null;
|
|
14849
15084
|
loop;
|
|
14850
15085
|
lifecycleLogState = new Map;
|
|
14851
15086
|
nudgeReceiptLogState = null;
|
|
14852
|
-
constructor(identity, acquireClient, dropClient,
|
|
15087
|
+
constructor(identity, acquireClient, dropClient, openRoute, onNudge, sleep, canAttach, onRootAttachFailure, onDormant, dispatchProbeIntervalMs, nudgeRef, isCurrent = () => true, noteClientAlive = () => {
|
|
14853
15088
|
return;
|
|
14854
15089
|
}, noteUnansweredCall = () => {
|
|
14855
15090
|
return;
|
|
15091
|
+
}, onRouteAttached = () => {
|
|
15092
|
+
return;
|
|
14856
15093
|
}) {
|
|
14857
15094
|
this.identity = identity;
|
|
14858
15095
|
this.acquireClient = acquireClient;
|
|
14859
15096
|
this.dropClient = dropClient;
|
|
14860
|
-
this.
|
|
15097
|
+
this.openRoute = openRoute;
|
|
14861
15098
|
this.onNudge = onNudge;
|
|
14862
15099
|
this.sleep = sleep;
|
|
14863
15100
|
this.canAttach = canAttach;
|
|
@@ -14868,11 +15105,15 @@ class BgSubscription {
|
|
|
14868
15105
|
this.isCurrent = isCurrent;
|
|
14869
15106
|
this.noteClientAlive = noteClientAlive;
|
|
14870
15107
|
this.noteUnansweredCall = noteUnansweredCall;
|
|
15108
|
+
this.onRouteAttached = onRouteAttached;
|
|
14871
15109
|
this.loop = this.run();
|
|
14872
15110
|
}
|
|
14873
15111
|
get attachedClient() {
|
|
14874
15112
|
return this.attached;
|
|
14875
15113
|
}
|
|
15114
|
+
get attachedRoute() {
|
|
15115
|
+
return this.route;
|
|
15116
|
+
}
|
|
14876
15117
|
async stop() {
|
|
14877
15118
|
this.stopped = true;
|
|
14878
15119
|
const sub = this.current;
|
|
@@ -14997,7 +15238,7 @@ class BgSubscription {
|
|
|
14997
15238
|
}
|
|
14998
15239
|
let route;
|
|
14999
15240
|
try {
|
|
15000
|
-
route = await
|
|
15241
|
+
route = await this.openRoute(client);
|
|
15001
15242
|
} catch (err) {
|
|
15002
15243
|
if (this.isCurrent() && this.onRootAttachFailure(err)) {
|
|
15003
15244
|
if (reconnecting)
|
|
@@ -15055,6 +15296,8 @@ class BgSubscription {
|
|
|
15055
15296
|
});
|
|
15056
15297
|
this.current = sub;
|
|
15057
15298
|
this.attached = client;
|
|
15299
|
+
this.route = route;
|
|
15300
|
+
this.onRouteAttached();
|
|
15058
15301
|
stopDispatchProbe = this.startDispatchProbe(client, routeId);
|
|
15059
15302
|
this.info("subscription-open", `subscription open channel=${routeId}`);
|
|
15060
15303
|
if (reconnecting) {
|
|
@@ -15097,6 +15340,7 @@ class BgSubscription {
|
|
|
15097
15340
|
stopDispatchProbe();
|
|
15098
15341
|
this.current = null;
|
|
15099
15342
|
this.attached = null;
|
|
15343
|
+
this.route = null;
|
|
15100
15344
|
safeCloseRoute(client, route);
|
|
15101
15345
|
}
|
|
15102
15346
|
await this.backoff(backoffAttempt++);
|
|
@@ -15112,7 +15356,7 @@ function isRecord(value) {
|
|
|
15112
15356
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
15113
15357
|
}
|
|
15114
15358
|
function isRouteProvenAbsentError(err) {
|
|
15115
|
-
return err instanceof SubcError && err.code === "unknown_channel" || err instanceof StaleRouteHandleError;
|
|
15359
|
+
return err instanceof SubcError && (err.code === "unknown_channel" || err.code === "stale_route_epoch") || err instanceof StaleRouteHandleError;
|
|
15116
15360
|
}
|
|
15117
15361
|
function isAbsentRootRouteError(err) {
|
|
15118
15362
|
return isRecord(err) && err.code === "config_divergence" && typeof err.message === "string" && err.message.includes("project root does not exist");
|
|
@@ -15239,6 +15483,8 @@ class SubcTransport {
|
|
|
15239
15483
|
body.preview = true;
|
|
15240
15484
|
if (options?.workerSession === true)
|
|
15241
15485
|
body[WORKER_SESSION_FIELD] = true;
|
|
15486
|
+
if (options?.hostToolNames)
|
|
15487
|
+
body[HOST_TOOL_NAMES_FIELD] = options.hostToolNames;
|
|
15242
15488
|
body[PRESET_FIELD] = callPresetFor(options?.workerSession === true);
|
|
15243
15489
|
const reply = await this.pool.routeRequest(this.identityFor(sessionId), body, timeoutMs, onProgress, this.generation, abortSignal);
|
|
15244
15490
|
return reliftReply(reply);
|
|
@@ -15250,10 +15496,12 @@ class SubcTransport {
|
|
|
15250
15496
|
}
|
|
15251
15497
|
const { timeoutMs, onProgress, abortSignal } = this.splitOptions(options);
|
|
15252
15498
|
const session = typeof params.session_id === "string" ? params.session_id : undefined;
|
|
15253
|
-
const { [WORKER_SESSION_FIELD]: workerSession, ...args } = params;
|
|
15499
|
+
const { [WORKER_SESSION_FIELD]: workerSession, [HOST_TOOL_NAMES_FIELD]: hostToolNames, ...args } = params;
|
|
15254
15500
|
const body = { name: command, arguments: args };
|
|
15255
15501
|
if (workerSession === true)
|
|
15256
15502
|
body[WORKER_SESSION_FIELD] = true;
|
|
15503
|
+
if (hostToolNames !== undefined)
|
|
15504
|
+
body[HOST_TOOL_NAMES_FIELD] = hostToolNames;
|
|
15257
15505
|
body[PRESET_FIELD] = callPresetFor(workerSession === true);
|
|
15258
15506
|
const editSlotSurvives = this.pool.getEditSlotSurvives();
|
|
15259
15507
|
if (editSlotSurvives !== undefined)
|
|
@@ -15276,6 +15524,10 @@ class SubcTransportPool {
|
|
|
15276
15524
|
connectionFile;
|
|
15277
15525
|
handshakeTimeoutMs;
|
|
15278
15526
|
consumerIdentity;
|
|
15527
|
+
hostScopes;
|
|
15528
|
+
hostScopeCache = new Map;
|
|
15529
|
+
hostScopeRefusals = new Set;
|
|
15530
|
+
hostScopeOutcomes = new Map;
|
|
15279
15531
|
connectFn;
|
|
15280
15532
|
onBgEventsNudge;
|
|
15281
15533
|
onBgEventsNudgeRef;
|
|
@@ -15297,6 +15549,7 @@ class SubcTransportPool {
|
|
|
15297
15549
|
routeReopenRetry = null;
|
|
15298
15550
|
routeReopenRetryMs = null;
|
|
15299
15551
|
sessions = new Map;
|
|
15552
|
+
sessionIndex = new Map;
|
|
15300
15553
|
observedStarts = new Set;
|
|
15301
15554
|
sessionSweep = null;
|
|
15302
15555
|
rootIndex = new Map;
|
|
@@ -15316,6 +15569,7 @@ class SubcTransportPool {
|
|
|
15316
15569
|
this.harness = options.harness;
|
|
15317
15570
|
this.handshakeTimeoutMs = options.handshakeTimeoutMs;
|
|
15318
15571
|
this.consumerIdentity = options.consumerIdentity;
|
|
15572
|
+
this.hostScopes = options.hostScopes ?? false;
|
|
15319
15573
|
this.connectFn = options.connect ?? ((opts) => SubcClient.connect(opts));
|
|
15320
15574
|
this.onBgEventsNudge = options.onBgEventsNudge;
|
|
15321
15575
|
this.onBgEventsNudgeRef = options.onBgEventsNudgeRef;
|
|
@@ -15535,7 +15789,9 @@ class SubcTransportPool {
|
|
|
15535
15789
|
return transport;
|
|
15536
15790
|
}
|
|
15537
15791
|
getBridge(projectRoot) {
|
|
15538
|
-
|
|
15792
|
+
return this.getBridgeForCanonicalRoot(this.canonicalRoot(projectRoot));
|
|
15793
|
+
}
|
|
15794
|
+
getBridgeForCanonicalRoot(root) {
|
|
15539
15795
|
if (this.shuttingDown && this.lifecycleEnabled()) {
|
|
15540
15796
|
throw new SubcTransportShuttingDownError;
|
|
15541
15797
|
}
|
|
@@ -15632,6 +15888,12 @@ class SubcTransportPool {
|
|
|
15632
15888
|
pendingDetach: false
|
|
15633
15889
|
};
|
|
15634
15890
|
this.sessions.set(key, record);
|
|
15891
|
+
let records = this.sessionIndex.get(identity.session);
|
|
15892
|
+
if (!records) {
|
|
15893
|
+
records = new Set;
|
|
15894
|
+
this.sessionIndex.set(identity.session, records);
|
|
15895
|
+
}
|
|
15896
|
+
records.add(record);
|
|
15635
15897
|
let keys = this.rootIndex.get(root);
|
|
15636
15898
|
if (!keys) {
|
|
15637
15899
|
keys = new Set;
|
|
@@ -15809,6 +16071,10 @@ class SubcTransportPool {
|
|
|
15809
16071
|
log(`subc bg_events: ${message}${suffix}`, { sessionId: record.identity.session });
|
|
15810
16072
|
}
|
|
15811
16073
|
removeIndexMembership(record) {
|
|
16074
|
+
const records = this.sessionIndex.get(record.identity.session);
|
|
16075
|
+
records?.delete(record);
|
|
16076
|
+
if (records?.size === 0)
|
|
16077
|
+
this.sessionIndex.delete(record.identity.session);
|
|
15812
16078
|
const keys = this.rootIndex.get(record.canonicalRoot);
|
|
15813
16079
|
if (!keys)
|
|
15814
16080
|
return;
|
|
@@ -15821,6 +16087,7 @@ class SubcTransportPool {
|
|
|
15821
16087
|
this.sessions.delete(record.identityKey);
|
|
15822
16088
|
this.removeIndexMembership(record);
|
|
15823
16089
|
}
|
|
16090
|
+
this.stopUnusedHostScopeRetry(record.identity.session);
|
|
15824
16091
|
}
|
|
15825
16092
|
detachSession(key, reason) {
|
|
15826
16093
|
const record = this.sessions.get(key);
|
|
@@ -15840,6 +16107,7 @@ class SubcTransportPool {
|
|
|
15840
16107
|
record.routeEntry = null;
|
|
15841
16108
|
if (routeEntry)
|
|
15842
16109
|
routeEntry.closed = true;
|
|
16110
|
+
this.stopUnusedHostScopeRetry(record.identity.session);
|
|
15843
16111
|
return { record, bgSub, routeEntry };
|
|
15844
16112
|
}
|
|
15845
16113
|
async cleanupDetached(detached) {
|
|
@@ -15896,6 +16164,17 @@ class SubcTransportPool {
|
|
|
15896
16164
|
entry.inflight -= 1;
|
|
15897
16165
|
this.closeRetiredRoute(record, entry);
|
|
15898
16166
|
}
|
|
16167
|
+
retireHostToolRoute(record) {
|
|
16168
|
+
const entry = record.routeEntry;
|
|
16169
|
+
if (!entry)
|
|
16170
|
+
return;
|
|
16171
|
+
entry.closed = true;
|
|
16172
|
+
entry.closedBy ??= "route replaced";
|
|
16173
|
+
record.routeEntry = null;
|
|
16174
|
+
record.retiredRoutes.add(entry);
|
|
16175
|
+
this.closeRetiredRoute(record, entry);
|
|
16176
|
+
this.stopUnusedHostScopeRetry(record.identity.session);
|
|
16177
|
+
}
|
|
15899
16178
|
async routeRequest(identity, body, timeoutMs, onProgress, expectedGeneration, abortSignal) {
|
|
15900
16179
|
const callStartedAt = performance.now();
|
|
15901
16180
|
const callDeadlineMs = timeoutMs ?? SUBC_DEFAULT_REQUEST_TIMEOUT_MS;
|
|
@@ -16034,15 +16313,14 @@ class SubcTransportPool {
|
|
|
16034
16313
|
const clearRouteEntry = (entry) => {
|
|
16035
16314
|
if (record.routeEntry !== entry)
|
|
16036
16315
|
return;
|
|
16037
|
-
|
|
16038
|
-
entry.closedBy ??= "route replaced";
|
|
16039
|
-
record.routeEntry = null;
|
|
16040
|
-
record.retiredRoutes.add(entry);
|
|
16041
|
-
this.closeRetiredRoute(record, entry);
|
|
16316
|
+
this.retireHostToolRoute(record);
|
|
16042
16317
|
};
|
|
16043
16318
|
const handleRequestFailure = (error, entry) => {
|
|
16044
16319
|
const aborted = error instanceof Error && error.name === "AbortError";
|
|
16045
16320
|
const ownsClient = this.isCurrentSession(key, record) && this.client === client && !this.isReapInduced(record);
|
|
16321
|
+
if (ownsClient && error instanceof SubcError && error.closeReason?.startsWith("scope_")) {
|
|
16322
|
+
this.invalidateHostScope(identity.session);
|
|
16323
|
+
}
|
|
16046
16324
|
if (!aborted && ownsClient && isSocketDeathError(error)) {
|
|
16047
16325
|
this.dropClient(client, {
|
|
16048
16326
|
error,
|
|
@@ -16051,7 +16329,7 @@ class SubcTransportPool {
|
|
|
16051
16329
|
route: routeLabel(entry.handle)
|
|
16052
16330
|
});
|
|
16053
16331
|
}
|
|
16054
|
-
if (aborted || isRouteProvenAbsentError(error) || error instanceof SubcError && error.closeReason !== undefined) {
|
|
16332
|
+
if (aborted || isRouteProvenAbsentError(error) || error instanceof SubcError && error.closeReason !== undefined || isSubcFrameReadTimeoutError(error)) {
|
|
16055
16333
|
clearRouteEntry(entry);
|
|
16056
16334
|
}
|
|
16057
16335
|
if (aborted || !ownsClient || this.client !== client)
|
|
@@ -16130,10 +16408,13 @@ class SubcTransportPool {
|
|
|
16130
16408
|
this.resetRouteReopenBackoff();
|
|
16131
16409
|
return reply;
|
|
16132
16410
|
} catch (requestError) {
|
|
16133
|
-
const error = routeClosedMidCallError(requestError, routeAndEntry.entry, record);
|
|
16411
|
+
const error = frameReadTimeoutError(routeClosedMidCallError(requestError, routeAndEntry.entry, record));
|
|
16134
16412
|
if (this.isReapInduced(record))
|
|
16135
16413
|
throw this.annotateReapError(error, record);
|
|
16136
16414
|
const ownsRoute = this.isCurrentSession(key, record) && this.client === client;
|
|
16415
|
+
if (ownsRoute && (error instanceof SubcError && error.code === "stale_route_epoch" || error instanceof StaleRouteHandleError)) {
|
|
16416
|
+
this.invalidateHostScope(identity.session);
|
|
16417
|
+
}
|
|
16137
16418
|
if (ownsRoute && isRouteRequestReloadRefusal(error)) {
|
|
16138
16419
|
clearRouteEntry(routeAndEntry.entry);
|
|
16139
16420
|
lastRefusal = error;
|
|
@@ -16210,9 +16491,14 @@ class SubcTransportPool {
|
|
|
16210
16491
|
return this.client;
|
|
16211
16492
|
if (this.connecting)
|
|
16212
16493
|
return this.connecting;
|
|
16494
|
+
let owner;
|
|
16213
16495
|
this.connecting = this.connectFn({
|
|
16214
16496
|
connectionFile: this.connectionFile,
|
|
16215
|
-
handshakeTimeoutMs: this.handshakeTimeoutMs
|
|
16497
|
+
handshakeTimeoutMs: this.handshakeTimeoutMs,
|
|
16498
|
+
onControlPush: (push) => {
|
|
16499
|
+
if (owner && this.client === owner)
|
|
16500
|
+
this.onScopeRouteClose(push);
|
|
16501
|
+
}
|
|
16216
16502
|
}).then((client) => {
|
|
16217
16503
|
this.connecting = null;
|
|
16218
16504
|
if (this.shuttingDown) {
|
|
@@ -16222,6 +16508,7 @@ class SubcTransportPool {
|
|
|
16222
16508
|
throw new SubcTransportShuttingDownError;
|
|
16223
16509
|
}
|
|
16224
16510
|
this.client = client;
|
|
16511
|
+
owner = client;
|
|
16225
16512
|
this.hadWorkingClient = true;
|
|
16226
16513
|
this.transportFailures = 0;
|
|
16227
16514
|
return client;
|
|
@@ -16251,9 +16538,7 @@ class SubcTransportPool {
|
|
|
16251
16538
|
closedBy: null
|
|
16252
16539
|
};
|
|
16253
16540
|
this.assertRootCanAttach(record.canonicalRoot);
|
|
16254
|
-
const opening =
|
|
16255
|
-
consumerIdentity: this.consumerIdentity
|
|
16256
|
-
}).then((route) => {
|
|
16541
|
+
const opening = this.openHostToolRoute(client, identity, record, entry).then((route) => {
|
|
16257
16542
|
if (!this.isCurrentSession(record.identityKey, record) || record.routeEntry !== entry || entry.closed || this.client !== client || this.lifecycleEnabled() && !this.isCurrentLiveGeneration(record.canonicalRoot, record.generation)) {
|
|
16258
16543
|
safeCloseRoute(client, route);
|
|
16259
16544
|
if (record.routeEntry === entry)
|
|
@@ -16263,6 +16548,12 @@ class SubcTransportPool {
|
|
|
16263
16548
|
entry.handle = route;
|
|
16264
16549
|
entry.opening = null;
|
|
16265
16550
|
this.noteClientAlive(client);
|
|
16551
|
+
const state = this.hostScopeCache.get(identity.session);
|
|
16552
|
+
if (state)
|
|
16553
|
+
this.scheduleHostScopeRetry(identity.session, state);
|
|
16554
|
+
if (state?.answer?.result === "scope" && !state.scopeBindPending && !entry.hostScope && !this.hostScopeRefusals.has(identity.session)) {
|
|
16555
|
+
this.promoteHostScope(identity.session);
|
|
16556
|
+
}
|
|
16266
16557
|
return route;
|
|
16267
16558
|
}).catch((error) => {
|
|
16268
16559
|
const currentEntry = this.isCurrentSession(record.identityKey, record) && record.routeEntry === entry;
|
|
@@ -16270,6 +16561,7 @@ class SubcTransportPool {
|
|
|
16270
16561
|
entry.closed = true;
|
|
16271
16562
|
record.routeEntry = null;
|
|
16272
16563
|
}
|
|
16564
|
+
this.stopUnusedHostScopeRetry(identity.session);
|
|
16273
16565
|
if (!this.isCurrentSession(record.identityKey, record) && !(error instanceof RouteTornDownError)) {
|
|
16274
16566
|
throw new RouteTornDownError("subc route opened after session closed");
|
|
16275
16567
|
}
|
|
@@ -16282,6 +16574,327 @@ class SubcTransportPool {
|
|
|
16282
16574
|
record.routeEntry = entry;
|
|
16283
16575
|
return { route: await opening, entry };
|
|
16284
16576
|
}
|
|
16577
|
+
hostScope(client, identity) {
|
|
16578
|
+
if (this.hostScopeRefusals.has(identity.session))
|
|
16579
|
+
return Promise.resolve(undefined);
|
|
16580
|
+
let state = this.hostScopeCache.get(identity.session);
|
|
16581
|
+
if (!state) {
|
|
16582
|
+
state = { retryAt: 0, retryMs: HOST_SCOPE_RETRY_FLOOR_MS };
|
|
16583
|
+
this.hostScopeCache.set(identity.session, state);
|
|
16584
|
+
}
|
|
16585
|
+
if (state.answer) {
|
|
16586
|
+
return Promise.resolve(state.answer.result === "scope" && !state.scopeBindPending ? state.answer.scope : undefined);
|
|
16587
|
+
}
|
|
16588
|
+
if (state.pending)
|
|
16589
|
+
return state.pending;
|
|
16590
|
+
if (Date.now() < state.retryAt)
|
|
16591
|
+
return Promise.resolve(undefined);
|
|
16592
|
+
const current = state;
|
|
16593
|
+
clearTimeout(current.retryTimer);
|
|
16594
|
+
current.retryTimer = undefined;
|
|
16595
|
+
const abort = new AbortController;
|
|
16596
|
+
current.lookupAbort = abort;
|
|
16597
|
+
const lookup = this.readHostScope(client, identity, abort.signal).then((outcome) => {
|
|
16598
|
+
if (this.hostScopeCache.get(identity.session) !== current)
|
|
16599
|
+
return;
|
|
16600
|
+
current.pending = undefined;
|
|
16601
|
+
current.lookupAbort = undefined;
|
|
16602
|
+
if (abort.signal.aborted)
|
|
16603
|
+
return;
|
|
16604
|
+
this.logHostScopeOutcome(identity.session, outcome);
|
|
16605
|
+
if (outcome.result === "scope" || outcome.result === "no_scope") {
|
|
16606
|
+
current.answer = outcome;
|
|
16607
|
+
current.retryAt = 0;
|
|
16608
|
+
current.retryMs = HOST_SCOPE_RETRY_FLOOR_MS;
|
|
16609
|
+
if (outcome.result === "scope")
|
|
16610
|
+
this.promoteHostScope(identity.session);
|
|
16611
|
+
} else {
|
|
16612
|
+
current.retryAt = Date.now() + current.retryMs;
|
|
16613
|
+
current.retryMs = Math.min(current.retryMs * 2, HOST_SCOPE_RETRY_CAP_MS);
|
|
16614
|
+
this.scheduleHostScopeRetry(identity.session, current);
|
|
16615
|
+
}
|
|
16616
|
+
return outcome.result === "scope" ? outcome.scope : undefined;
|
|
16617
|
+
});
|
|
16618
|
+
current.pending = lookup;
|
|
16619
|
+
return lookup;
|
|
16620
|
+
}
|
|
16621
|
+
unscopedHostRecord(session) {
|
|
16622
|
+
for (const record of this.sessionIndex.get(session) ?? []) {
|
|
16623
|
+
const entry = record.routeEntry;
|
|
16624
|
+
if (record.identity.session === session && !record.closed && (entry && !entry.closed && !entry.hostScope && entry.client === this.client && (entry.handle || entry.opening) || record.bgSub?.attachedClient === this.client && record.bgSub?.attachedRoute))
|
|
16625
|
+
return record;
|
|
16626
|
+
}
|
|
16627
|
+
return;
|
|
16628
|
+
}
|
|
16629
|
+
cancelHostScopeRetry(state) {
|
|
16630
|
+
clearTimeout(state.retryTimer);
|
|
16631
|
+
state.retryTimer = undefined;
|
|
16632
|
+
state.lookupAbort?.abort();
|
|
16633
|
+
}
|
|
16634
|
+
stopUnusedHostScopeRetry(session) {
|
|
16635
|
+
const state = this.hostScopeCache.get(session);
|
|
16636
|
+
if (state && !this.unscopedHostRecord(session)) {
|
|
16637
|
+
if (state.retryTimer || state.scopeBindPending && state.bindPending)
|
|
16638
|
+
log(`subc head scope: session=${session} retry stopped reason=route_closed`);
|
|
16639
|
+
this.cancelHostScopeRetry(state);
|
|
16640
|
+
state.scopeBindPending = false;
|
|
16641
|
+
}
|
|
16642
|
+
}
|
|
16643
|
+
scheduleHostScopeRetry(session, state) {
|
|
16644
|
+
if (this.shuttingDown || this.hostScopeCache.get(session) !== state || state.answer && !state.scopeBindPending || state.pending || state.bindPending || state.retryTimer || this.hostScopeRefusals.has(session) || !this.unscopedHostRecord(session))
|
|
16645
|
+
return;
|
|
16646
|
+
state.retryTimer = setTimeout(() => {
|
|
16647
|
+
state.retryTimer = undefined;
|
|
16648
|
+
const record = this.unscopedHostRecord(session);
|
|
16649
|
+
const client = record?.routeEntry?.client ?? record?.bgSub?.attachedClient;
|
|
16650
|
+
if (this.shuttingDown || this.hostScopeCache.get(session) !== state || !record || !client)
|
|
16651
|
+
return;
|
|
16652
|
+
let retry;
|
|
16653
|
+
if (state.scopeBindPending && state.answer?.result === "scope") {
|
|
16654
|
+
state.bindPending = this.retryHostScopeBind(client, record.identity, state, state.answer.scope).finally(() => {
|
|
16655
|
+
state.bindPending = undefined;
|
|
16656
|
+
this.scheduleHostScopeRetry(session, state);
|
|
16657
|
+
});
|
|
16658
|
+
retry = state.bindPending;
|
|
16659
|
+
} else {
|
|
16660
|
+
retry = this.hostScope(client, record.identity);
|
|
16661
|
+
}
|
|
16662
|
+
retry.catch((error) => {
|
|
16663
|
+
warn(`subc head scope: background retry failed code=${error instanceof Error ? error.name : "Error"}`);
|
|
16664
|
+
});
|
|
16665
|
+
}, Math.max(0, state.retryAt - Date.now()));
|
|
16666
|
+
state.retryTimer.unref();
|
|
16667
|
+
}
|
|
16668
|
+
deferHostScopeBind(session, state, code) {
|
|
16669
|
+
state.scopeBindPending = true;
|
|
16670
|
+
if (Date.now() >= state.retryAt) {
|
|
16671
|
+
state.retryAt = Date.now() + state.retryMs;
|
|
16672
|
+
state.retryMs = Math.min(state.retryMs * 2, HOST_SCOPE_RETRY_CAP_MS);
|
|
16673
|
+
}
|
|
16674
|
+
warn(`subc head scope: session=${session} scoped bind refused code=${code}; binding unscoped; retry in=${Math.max(0, state.retryAt - Date.now())}ms`);
|
|
16675
|
+
this.scheduleHostScopeRetry(session, state);
|
|
16676
|
+
}
|
|
16677
|
+
refuseHostScopeBind(session, state, code) {
|
|
16678
|
+
if (!this.hostScopeRefusals.has(session)) {
|
|
16679
|
+
this.hostScopeRefusals.add(session);
|
|
16680
|
+
warn(`subc head scope: session=${session} scoped bind refused code=${code}; binding unscoped; retry stopped`);
|
|
16681
|
+
}
|
|
16682
|
+
if (state) {
|
|
16683
|
+
state.scopeBindPending = false;
|
|
16684
|
+
this.cancelHostScopeRetry(state);
|
|
16685
|
+
}
|
|
16686
|
+
}
|
|
16687
|
+
async retryHostScopeBind(client, identity, state, scope) {
|
|
16688
|
+
try {
|
|
16689
|
+
const route = await client.routeOpen({ kind: "tool_provider", module_id: AFT_MODULE_ID }, identity, { consumerIdentity: this.consumerIdentity, scope });
|
|
16690
|
+
safeCloseRoute(client, route);
|
|
16691
|
+
if (this.hostScopeCache.get(identity.session) !== state || !state.scopeBindPending || this.shuttingDown || client !== this.client || !this.unscopedHostRecord(identity.session))
|
|
16692
|
+
return;
|
|
16693
|
+
state.scopeBindPending = false;
|
|
16694
|
+
state.retryAt = 0;
|
|
16695
|
+
state.retryMs = HOST_SCOPE_RETRY_FLOOR_MS;
|
|
16696
|
+
log(`subc head scope: session=${identity.session} scoped bind recovered ref=${scope.ref.slice(0, 8)} epoch=${scope.scopeEpoch}`);
|
|
16697
|
+
this.promoteHostScope(identity.session);
|
|
16698
|
+
} catch (error) {
|
|
16699
|
+
if (this.hostScopeCache.get(identity.session) !== state || !state.scopeBindPending || this.shuttingDown || !this.unscopedHostRecord(identity.session))
|
|
16700
|
+
return;
|
|
16701
|
+
if (error instanceof SubcError && error.code && !isSocketDeathError(error) && !isRouteOpenReloadWindowError(error) && !isHostScopeSyncRefusal(error.code)) {
|
|
16702
|
+
this.refuseHostScopeBind(identity.session, state, error.code);
|
|
16703
|
+
} else {
|
|
16704
|
+
this.deferHostScopeBind(identity.session, state, error instanceof SubcError ? error.code ?? error.name : "Error");
|
|
16705
|
+
}
|
|
16706
|
+
}
|
|
16707
|
+
}
|
|
16708
|
+
promoteHostScope(session) {
|
|
16709
|
+
for (const record of this.sessions.values()) {
|
|
16710
|
+
const entry = record.routeEntry;
|
|
16711
|
+
if (record.identity.session !== session)
|
|
16712
|
+
continue;
|
|
16713
|
+
if (entry?.handle && !entry.hostScope)
|
|
16714
|
+
this.retireHostToolRoute(record);
|
|
16715
|
+
const sub = record.bgSub;
|
|
16716
|
+
if (!sub?.attachedRoute)
|
|
16717
|
+
continue;
|
|
16718
|
+
sub.stop().then(() => {
|
|
16719
|
+
if (record.bgSub !== sub)
|
|
16720
|
+
return;
|
|
16721
|
+
record.bgSub = null;
|
|
16722
|
+
this.ensureBgSubscription(record.identity, record);
|
|
16723
|
+
});
|
|
16724
|
+
}
|
|
16725
|
+
}
|
|
16726
|
+
logHostScopeOutcome(session, outcome) {
|
|
16727
|
+
let key;
|
|
16728
|
+
let detail;
|
|
16729
|
+
switch (outcome.result) {
|
|
16730
|
+
case "scope":
|
|
16731
|
+
key = JSON.stringify([outcome.result, outcome.scope.ref, outcome.scope.scopeEpoch]);
|
|
16732
|
+
detail = `scope ref=${outcome.scope.ref.slice(0, 8)} epoch=${outcome.scope.scopeEpoch}`;
|
|
16733
|
+
break;
|
|
16734
|
+
case "error":
|
|
16735
|
+
key = JSON.stringify([outcome.result, outcome.code]);
|
|
16736
|
+
detail = `error code=${outcome.code} after=${outcome.afterMs}`;
|
|
16737
|
+
break;
|
|
16738
|
+
case "timeout":
|
|
16739
|
+
key = outcome.result;
|
|
16740
|
+
detail = `timeout after=${outcome.afterMs}`;
|
|
16741
|
+
break;
|
|
16742
|
+
default:
|
|
16743
|
+
key = outcome.result;
|
|
16744
|
+
detail = outcome.result;
|
|
16745
|
+
}
|
|
16746
|
+
if (this.hostScopeOutcomes.get(session) === key)
|
|
16747
|
+
return;
|
|
16748
|
+
this.hostScopeOutcomes.set(session, key);
|
|
16749
|
+
log(`subc head scope: session=${session} result=${detail}`);
|
|
16750
|
+
}
|
|
16751
|
+
async readHostScope(client, identity, signal) {
|
|
16752
|
+
const startedAt = Date.now();
|
|
16753
|
+
const timeout = () => ({ result: "timeout", afterMs: Date.now() - startedAt });
|
|
16754
|
+
let expired = false;
|
|
16755
|
+
let timer;
|
|
16756
|
+
const abort = new AbortController;
|
|
16757
|
+
let cancel;
|
|
16758
|
+
const cancelled = new Promise((resolve) => {
|
|
16759
|
+
cancel = () => {
|
|
16760
|
+
expired = true;
|
|
16761
|
+
abort.abort();
|
|
16762
|
+
resolve({ result: "error", code: "AbortError", afterMs: Date.now() - startedAt });
|
|
16763
|
+
};
|
|
16764
|
+
signal.addEventListener("abort", cancel, { once: true });
|
|
16765
|
+
});
|
|
16766
|
+
const read = async () => {
|
|
16767
|
+
let route;
|
|
16768
|
+
try {
|
|
16769
|
+
route = await client.routeOpen({ kind: "management_surface", module_id: "prefrontal-core" }, identity, { consumerIdentity: this.consumerIdentity });
|
|
16770
|
+
if (expired)
|
|
16771
|
+
return timeout();
|
|
16772
|
+
const envelope = await client.request(route, {
|
|
16773
|
+
method: "session.scope_identity",
|
|
16774
|
+
params: { harness: identity.harness, session: identity.session }
|
|
16775
|
+
}, { timeoutMs: HOST_SCOPE_LOOKUP_TIMEOUT_MS, signal: abort.signal });
|
|
16776
|
+
const reply = isRecord(envelope) ? envelope.result : undefined;
|
|
16777
|
+
if (isRecord(reply) && reply.answer === "no_scope")
|
|
16778
|
+
return { result: "no_scope" };
|
|
16779
|
+
if (!isRecord(reply) || !isRecord(reply.owner) || reply.owner.kind !== "reserved" || reply.owner.module_id !== "prefrontal-core" || typeof reply.ref !== "string" || reply.ref.length === 0 || !Number.isSafeInteger(reply.scope_epoch) || reply.scope_epoch < 0)
|
|
16780
|
+
return { result: "malformed" };
|
|
16781
|
+
return {
|
|
16782
|
+
result: "scope",
|
|
16783
|
+
scope: {
|
|
16784
|
+
owner: { kind: "reserved", module_id: "prefrontal-core" },
|
|
16785
|
+
ref: reply.ref,
|
|
16786
|
+
scopeEpoch: reply.scope_epoch
|
|
16787
|
+
}
|
|
16788
|
+
};
|
|
16789
|
+
} catch (error) {
|
|
16790
|
+
if (expired)
|
|
16791
|
+
return timeout();
|
|
16792
|
+
return {
|
|
16793
|
+
result: "error",
|
|
16794
|
+
code: error instanceof SubcError && error.code ? error.code : error instanceof Error ? error.name : "Error",
|
|
16795
|
+
afterMs: Date.now() - startedAt
|
|
16796
|
+
};
|
|
16797
|
+
} finally {
|
|
16798
|
+
if (route)
|
|
16799
|
+
safeCloseRoute(client, route);
|
|
16800
|
+
}
|
|
16801
|
+
};
|
|
16802
|
+
try {
|
|
16803
|
+
return await Promise.race([
|
|
16804
|
+
read(),
|
|
16805
|
+
cancelled,
|
|
16806
|
+
new Promise((resolve) => {
|
|
16807
|
+
timer = setTimeout(() => {
|
|
16808
|
+
expired = true;
|
|
16809
|
+
abort.abort();
|
|
16810
|
+
resolve(timeout());
|
|
16811
|
+
}, HOST_SCOPE_LOOKUP_TIMEOUT_MS);
|
|
16812
|
+
})
|
|
16813
|
+
]);
|
|
16814
|
+
} finally {
|
|
16815
|
+
clearTimeout(timer);
|
|
16816
|
+
signal.removeEventListener("abort", cancel);
|
|
16817
|
+
}
|
|
16818
|
+
}
|
|
16819
|
+
openHostToolRoute(client, identity, record, entry) {
|
|
16820
|
+
if (!this.hostScopes || !["opencode", "pi"].includes(identity.harness) || identity.session === DEFAULT_SESSION_ID) {
|
|
16821
|
+
return client.routeOpen({ kind: "tool_provider", module_id: AFT_MODULE_ID }, identity, {
|
|
16822
|
+
consumerIdentity: this.consumerIdentity
|
|
16823
|
+
});
|
|
16824
|
+
}
|
|
16825
|
+
return this.openScopedHostToolRoute(client, identity, record, entry);
|
|
16826
|
+
}
|
|
16827
|
+
async openScopedHostToolRoute(client, identity, record, entry) {
|
|
16828
|
+
const scope = await this.hostScope(client, identity);
|
|
16829
|
+
this.assertRecordLive(record);
|
|
16830
|
+
if (scope) {
|
|
16831
|
+
try {
|
|
16832
|
+
const route = await client.routeOpen({ kind: "tool_provider", module_id: AFT_MODULE_ID }, identity, { consumerIdentity: this.consumerIdentity, scope });
|
|
16833
|
+
if (entry)
|
|
16834
|
+
entry.hostScope = scope;
|
|
16835
|
+
return route;
|
|
16836
|
+
} catch (error) {
|
|
16837
|
+
if (!(error instanceof SubcError) || !error.code || isSocketDeathError(error))
|
|
16838
|
+
throw error;
|
|
16839
|
+
if (isRouteOpenReloadWindowError(error))
|
|
16840
|
+
throw error;
|
|
16841
|
+
this.assertRecordLive(record);
|
|
16842
|
+
const state = this.hostScopeCache.get(identity.session);
|
|
16843
|
+
if (state && isHostScopeSyncRefusal(error.code)) {
|
|
16844
|
+
this.deferHostScopeBind(identity.session, state, error.code);
|
|
16845
|
+
} else {
|
|
16846
|
+
this.refuseHostScopeBind(identity.session, state, error.code);
|
|
16847
|
+
}
|
|
16848
|
+
}
|
|
16849
|
+
}
|
|
16850
|
+
this.assertRecordLive(record);
|
|
16851
|
+
const route = await client.routeOpen({ kind: "tool_provider", module_id: AFT_MODULE_ID }, identity, {
|
|
16852
|
+
consumerIdentity: this.consumerIdentity
|
|
16853
|
+
});
|
|
16854
|
+
if (this.hostScopeCache.get(identity.session)?.answer?.result === "scope" && !this.hostScopeCache.get(identity.session)?.scopeBindPending && !this.hostScopeRefusals.has(identity.session)) {
|
|
16855
|
+
safeCloseRoute(client, route);
|
|
16856
|
+
return this.openScopedHostToolRoute(client, identity, record, entry);
|
|
16857
|
+
}
|
|
16858
|
+
return route;
|
|
16859
|
+
}
|
|
16860
|
+
invalidateHostScope(session) {
|
|
16861
|
+
const state = this.hostScopeCache.get(session);
|
|
16862
|
+
if (state) {
|
|
16863
|
+
if (state.scopeBindPending)
|
|
16864
|
+
log(`subc head scope: session=${session} retry stopped reason=scope_route_closed`);
|
|
16865
|
+
this.cancelHostScopeRetry(state);
|
|
16866
|
+
}
|
|
16867
|
+
this.hostScopeCache.delete(session);
|
|
16868
|
+
this.hostScopeRefusals.delete(session);
|
|
16869
|
+
for (const record of this.sessions.values()) {
|
|
16870
|
+
if (record.identity.session !== session || !record.routeEntry)
|
|
16871
|
+
continue;
|
|
16872
|
+
this.retireHostToolRoute(record);
|
|
16873
|
+
}
|
|
16874
|
+
}
|
|
16875
|
+
onScopeRouteClose(push) {
|
|
16876
|
+
if (push.op !== "route.closed")
|
|
16877
|
+
return;
|
|
16878
|
+
const reason = routeCloseReason(push);
|
|
16879
|
+
if (!reason?.startsWith("scope_")) {
|
|
16880
|
+
for (const record of this.sessions.values()) {
|
|
16881
|
+
const route = record.routeEntry?.handle;
|
|
16882
|
+
if (!route)
|
|
16883
|
+
continue;
|
|
16884
|
+
if (Array.isArray(push.body.channels) && push.body.channels.includes(route.channel) || !Array.isArray(push.body.channels) && push.body.module_id === AFT_MODULE_ID)
|
|
16885
|
+
this.retireHostToolRoute(record);
|
|
16886
|
+
}
|
|
16887
|
+
return;
|
|
16888
|
+
}
|
|
16889
|
+
const sessions = new Set;
|
|
16890
|
+
for (const record of this.sessions.values()) {
|
|
16891
|
+
const routes = [record.routeEntry?.handle, record.bgSub?.attachedRoute].filter((route) => route != null);
|
|
16892
|
+
if (Array.isArray(push.body.channels) && routes.some((route) => push.body.channels.includes(route.channel)) || !Array.isArray(push.body.channels) && push.body.module_id === AFT_MODULE_ID)
|
|
16893
|
+
sessions.add(record.identity.session);
|
|
16894
|
+
}
|
|
16895
|
+
for (const session of sessions)
|
|
16896
|
+
this.invalidateHostScope(session);
|
|
16897
|
+
}
|
|
16285
16898
|
ensureBgSubscription(identity, record) {
|
|
16286
16899
|
if (this.shuttingDown || !this.onBgEventsNudge && !this.onBgEventsNudgeRef)
|
|
16287
16900
|
return;
|
|
@@ -16321,17 +16934,28 @@ class SubcTransportPool {
|
|
|
16321
16934
|
return;
|
|
16322
16935
|
this.logNudgeDelivery("drop-no-compatible-handler", currentRecord, `nudge dropped cause=generation-provenance-unavailable-and-root-session-handler-unwired root=${currentRecord.canonicalRoot}`);
|
|
16323
16936
|
};
|
|
16937
|
+
const binding = {};
|
|
16324
16938
|
let sub = null;
|
|
16325
16939
|
const clearDormantSubscription = () => {
|
|
16326
16940
|
if (sub && record.bgSub === sub)
|
|
16327
16941
|
record.bgSub = null;
|
|
16328
16942
|
};
|
|
16329
|
-
sub = new BgSubscription(identity, () => this.ensureClient(), (client, cause) => this.dropClient(client, cause),
|
|
16943
|
+
sub = new BgSubscription(identity, () => this.ensureClient(), (client, cause) => this.dropClient(client, cause), (client) => {
|
|
16944
|
+
binding.hostScope = undefined;
|
|
16945
|
+
return this.openHostToolRoute(client, identity, record, binding);
|
|
16946
|
+
}, onNudge, this.bgBackoffSleep, () => this.rootCanAttach(record.canonicalRoot), (error) => {
|
|
16330
16947
|
if (!isAbsentRootRouteError(error))
|
|
16331
16948
|
return false;
|
|
16332
16949
|
this.markRootDormant(record.canonicalRoot, error);
|
|
16333
16950
|
return true;
|
|
16334
|
-
}, clearDormantSubscription, this.bgDispatchProbeIntervalMs, nudgeRef, () => this.isCurrentSession(record.identityKey, record) && this.isCurrentLiveGeneration(record.canonicalRoot, record.generation), (client) => this.noteClientAlive(client), (client, cause) => this.noteUnansweredCall(client, cause))
|
|
16951
|
+
}, clearDormantSubscription, this.bgDispatchProbeIntervalMs, nudgeRef, () => this.isCurrentSession(record.identityKey, record) && this.isCurrentLiveGeneration(record.canonicalRoot, record.generation), (client) => this.noteClientAlive(client), (client, cause) => this.noteUnansweredCall(client, cause), () => {
|
|
16952
|
+
const state = this.hostScopeCache.get(identity.session);
|
|
16953
|
+
if (state)
|
|
16954
|
+
this.scheduleHostScopeRetry(identity.session, state);
|
|
16955
|
+
if (state?.answer?.result === "scope" && !state.scopeBindPending && !binding.hostScope && !this.hostScopeRefusals.has(identity.session)) {
|
|
16956
|
+
this.promoteHostScope(identity.session);
|
|
16957
|
+
}
|
|
16958
|
+
});
|
|
16335
16959
|
record.bgSub = sub;
|
|
16336
16960
|
if (!this.rootCanAttach(record.canonicalRoot)) {
|
|
16337
16961
|
record.bgSub = null;
|
|
@@ -16367,6 +16991,8 @@ class SubcTransportPool {
|
|
|
16367
16991
|
record.routeEntry = null;
|
|
16368
16992
|
this.deleteSessionIfEmpty(record.identityKey, record);
|
|
16369
16993
|
}
|
|
16994
|
+
for (const session of this.hostScopeCache.keys())
|
|
16995
|
+
this.stopUnusedHostScopeRetry(session);
|
|
16370
16996
|
this.transportFailures = 0;
|
|
16371
16997
|
try {
|
|
16372
16998
|
client.close();
|
|
@@ -16504,6 +17130,13 @@ class SubcTransportPool {
|
|
|
16504
17130
|
this.generationRejections.clear();
|
|
16505
17131
|
this.observedStarts.clear();
|
|
16506
17132
|
this.dormantRoots.clear();
|
|
17133
|
+
const scopeLookups = [...this.hostScopeCache.values()].flatMap((state) => {
|
|
17134
|
+
this.cancelHostScopeRetry(state);
|
|
17135
|
+
return state.pending ? [state.pending] : [];
|
|
17136
|
+
});
|
|
17137
|
+
this.hostScopeCache.clear();
|
|
17138
|
+
this.hostScopeRefusals.clear();
|
|
17139
|
+
this.hostScopeOutcomes.clear();
|
|
16507
17140
|
const registration = this.lifecycleRegistration;
|
|
16508
17141
|
registration?.deregister();
|
|
16509
17142
|
this.lifecycleRegistration = null;
|
|
@@ -16514,11 +17147,16 @@ class SubcTransportPool {
|
|
|
16514
17147
|
detached.push(owner);
|
|
16515
17148
|
}
|
|
16516
17149
|
this.rootIndex.clear();
|
|
17150
|
+
this.sessionIndex.clear();
|
|
16517
17151
|
this.transports.clear();
|
|
16518
17152
|
const client = this.client;
|
|
16519
17153
|
this.client = null;
|
|
16520
17154
|
const pending = [...this.pendingRootCleanups];
|
|
16521
|
-
await Promise.allSettled([
|
|
17155
|
+
await Promise.allSettled([
|
|
17156
|
+
...detached.map((owner) => this.cleanupDetached(owner)),
|
|
17157
|
+
...pending,
|
|
17158
|
+
...scopeLookups
|
|
17159
|
+
]);
|
|
16522
17160
|
if (client) {
|
|
16523
17161
|
try {
|
|
16524
17162
|
client.close();
|
|
@@ -16532,6 +17170,13 @@ class SubcTransportPool {
|
|
|
16532
17170
|
session: session && session.length > 0 ? session : DEFAULT_SESSION_ID
|
|
16533
17171
|
};
|
|
16534
17172
|
const owner = this.detachSession(identityKey(identity), "session_closed");
|
|
17173
|
+
if (!this.sessionIndex.has(identity.session)) {
|
|
17174
|
+
const state = this.hostScopeCache.get(identity.session);
|
|
17175
|
+
if (state)
|
|
17176
|
+
this.cancelHostScopeRetry(state);
|
|
17177
|
+
this.hostScopeCache.delete(identity.session);
|
|
17178
|
+
this.hostScopeRefusals.delete(identity.session);
|
|
17179
|
+
}
|
|
16535
17180
|
if (owner)
|
|
16536
17181
|
await this.cleanupDetached(owner);
|
|
16537
17182
|
}
|
|
@@ -16572,6 +17217,8 @@ var BASH_TRANSPORT_DISPOSITION = "The transport to the AFT daemon was interrupte
|
|
|
16572
17217
|
var SUBC_MODULE_RESTART_DISPOSITION = "The AFT daemon module restarted while this call was in flight, so its outcome is UNKNOWN: it may or may not have executed. Verify actual state before re-running, and never blind-retry a mutation.";
|
|
16573
17218
|
var BRIDGE_TRANSPORT_UNKNOWN_OUTCOME_DISPOSITION = "The standalone AFT transport failed after this call may have been sent, so its outcome is UNKNOWN: it may or may not have executed. Verify actual state before re-running, and never blind-retry a mutation.";
|
|
16574
17219
|
var SUBC_ROUTE_CLOSED_MID_CALL_DISPOSITION = "The plugin closed this call's route while the call was in flight, so its outcome is UNKNOWN: it may or may not have executed. Verify actual state before re-running, and never blind-retry a mutation.";
|
|
17220
|
+
var BASH_FRAME_READ_TIMEOUT_DISPOSITION = "The command's state is UNKNOWN: the transport timed out while reading the response frame, so it may or may not have executed. Check bash_status if a task ID could have been assigned, and do not re-run side-effecting commands blindly.";
|
|
17221
|
+
var SUBC_FRAME_READ_TIMEOUT_DISPOSITION = "The transport timed out while reading the response frame for this call, so its outcome is UNKNOWN: it may or may not have executed. Verify actual state before re-running, and never blind-retry a mutation.";
|
|
16575
17222
|
function isRouteClosedMidCallError(error) {
|
|
16576
17223
|
return error instanceof SubcRouteClosedMidCallError || isLocalRouteCloseRejection(error);
|
|
16577
17224
|
}
|
|
@@ -16633,6 +17280,8 @@ function classifyBashHostFallbackError(error) {
|
|
|
16633
17280
|
return;
|
|
16634
17281
|
if (isRouteClosedMidCallError(error))
|
|
16635
17282
|
return;
|
|
17283
|
+
if (isSubcFrameReadTimeoutError(error))
|
|
17284
|
+
return;
|
|
16636
17285
|
if (error instanceof SubcTransportShuttingDownError)
|
|
16637
17286
|
return "transport down";
|
|
16638
17287
|
if (isSubcRouteTornDownError(error))
|
|
@@ -16656,6 +17305,14 @@ function isTransportClassError(error) {
|
|
|
16656
17305
|
function adaptToolError(command, error) {
|
|
16657
17306
|
if (!(error instanceof Error))
|
|
16658
17307
|
return error;
|
|
17308
|
+
if (isSubcFrameReadTimeoutError(error)) {
|
|
17309
|
+
const typedError = error instanceof SubcFrameReadTimeoutError ? error : new SubcFrameReadTimeoutError(error);
|
|
17310
|
+
const disposition = command === "bash" ? BASH_FRAME_READ_TIMEOUT_DISPOSITION : SUBC_FRAME_READ_TIMEOUT_DISPOSITION;
|
|
17311
|
+
if (typedError.message.includes(disposition))
|
|
17312
|
+
return typedError;
|
|
17313
|
+
typedError.message = typedError.message ? `${typedError.message} ${disposition}` : disposition;
|
|
17314
|
+
return typedError;
|
|
17315
|
+
}
|
|
16659
17316
|
if (error instanceof BridgeTransportUnknownOutcomeError) {
|
|
16660
17317
|
if (error.message.includes(BRIDGE_TRANSPORT_UNKNOWN_OUTCOME_DISPOSITION))
|
|
16661
17318
|
return error;
|
|
@@ -16737,7 +17394,12 @@ var RETIRED_PATHS = [
|
|
|
16737
17394
|
["semantic_search", "indexes.semantic"],
|
|
16738
17395
|
["experimental_semantic_search", "indexes.semantic"],
|
|
16739
17396
|
["callgraph_store", "indexes.callgraph"],
|
|
16740
|
-
["github.enabled", "github.read,github.write,github.shim"]
|
|
17397
|
+
["github.enabled", "github.read,github.write,github.shim"],
|
|
17398
|
+
["experimental_lsp_ty", "experimental.lsp_ty"],
|
|
17399
|
+
["experimental_bash_rewrite", "bash.rewrite"],
|
|
17400
|
+
["experimental_bash_compress", "bash.compress"],
|
|
17401
|
+
["experimental_bash_background", "bash.background"],
|
|
17402
|
+
["experimental.bash", "bash"]
|
|
16741
17403
|
];
|
|
16742
17404
|
var INDEX_INPUTS = [
|
|
16743
17405
|
["trigram", "search_index", "experimental_search_index"],
|
|
@@ -16875,10 +17537,68 @@ function translateInspectLspPaths(map, prefix, out) {
|
|
|
16875
17537
|
recordRetired(out, prefix, `inspect.${leaf}`);
|
|
16876
17538
|
}
|
|
16877
17539
|
}
|
|
17540
|
+
function translateExperimentalPaths(map, prefix, out) {
|
|
17541
|
+
for (const [oldKey, leaf] of [
|
|
17542
|
+
["experimental_lsp_ty", "lsp_ty"],
|
|
17543
|
+
["experimental_bash_rewrite", "rewrite"],
|
|
17544
|
+
["experimental_bash_compress", "compress"],
|
|
17545
|
+
["experimental_bash_background", "background"]
|
|
17546
|
+
]) {
|
|
17547
|
+
if (!Object.hasOwn(map, oldKey))
|
|
17548
|
+
continue;
|
|
17549
|
+
recordRetired(out, prefix, oldKey);
|
|
17550
|
+
const value = map[oldKey];
|
|
17551
|
+
delete map[oldKey];
|
|
17552
|
+
if (!isRecord2(map.experimental))
|
|
17553
|
+
map.experimental = {};
|
|
17554
|
+
const experimental = map.experimental;
|
|
17555
|
+
let destination = experimental;
|
|
17556
|
+
if (leaf !== "lsp_ty") {
|
|
17557
|
+
if (!isRecord2(experimental.bash))
|
|
17558
|
+
experimental.bash = {};
|
|
17559
|
+
destination = experimental.bash;
|
|
17560
|
+
}
|
|
17561
|
+
if (Object.hasOwn(destination, leaf)) {
|
|
17562
|
+
superseded(out, oldKey, `${oldKey} is ignored because experimental.${leaf === "lsp_ty" ? leaf : `bash.${leaf}`} is already set`);
|
|
17563
|
+
} else {
|
|
17564
|
+
destination[leaf] = value;
|
|
17565
|
+
}
|
|
17566
|
+
}
|
|
17567
|
+
if (!isRecord2(map.experimental) || !Object.hasOwn(map.experimental, "bash"))
|
|
17568
|
+
return;
|
|
17569
|
+
const experimental = map.experimental;
|
|
17570
|
+
const legacy = experimental.bash;
|
|
17571
|
+
if (isRecord2(legacy)) {
|
|
17572
|
+
if (!["rewrite", "compress", "background"].some((leaf) => Object.hasOwn(legacy, leaf)))
|
|
17573
|
+
return;
|
|
17574
|
+
for (const leaf of Object.keys(legacy))
|
|
17575
|
+
recordRetired(out, prefix, `experimental.bash.${leaf}`);
|
|
17576
|
+
if (Object.hasOwn(map, "bash")) {
|
|
17577
|
+
superseded(out, "experimental.bash", 'experimental.bash is ignored because top-level "bash" is already set');
|
|
17578
|
+
} else {
|
|
17579
|
+
const bash = {
|
|
17580
|
+
rewrite: legacy.rewrite === true,
|
|
17581
|
+
compress: legacy.compress === true,
|
|
17582
|
+
background: legacy.background === true
|
|
17583
|
+
};
|
|
17584
|
+
for (const leaf of ["long_running_reminder_enabled", "long_running_reminder_interval_ms"]) {
|
|
17585
|
+
if (Object.hasOwn(legacy, leaf))
|
|
17586
|
+
bash[leaf] = legacy[leaf];
|
|
17587
|
+
}
|
|
17588
|
+
map.bash = bash;
|
|
17589
|
+
}
|
|
17590
|
+
} else {
|
|
17591
|
+
recordRetired(out, prefix, "experimental.bash");
|
|
17592
|
+
}
|
|
17593
|
+
delete experimental.bash;
|
|
17594
|
+
if (Object.keys(experimental).length === 0)
|
|
17595
|
+
delete map.experimental;
|
|
17596
|
+
}
|
|
16878
17597
|
function translateBlock(map, userBase, blockLabel, out) {
|
|
16879
17598
|
const prefix = blockLabel === "base" ? "" : `${blockLabel}.`;
|
|
16880
17599
|
translateGithubAliases(map, prefix, out);
|
|
16881
17600
|
translateInspectLspPaths(map, prefix, out);
|
|
17601
|
+
translateExperimentalPaths(map, prefix, out);
|
|
16882
17602
|
let explicitList;
|
|
16883
17603
|
const rawList = map.disabled_tools;
|
|
16884
17604
|
if (Array.isArray(rawList) && rawList.every((entry) => typeof entry === "string")) {
|
|
@@ -16891,6 +17611,8 @@ function translateBlock(map, userBase, blockLabel, out) {
|
|
|
16891
17611
|
});
|
|
16892
17612
|
}
|
|
16893
17613
|
for (const [path] of RETIRED_PATHS) {
|
|
17614
|
+
if (path === "experimental.bash")
|
|
17615
|
+
continue;
|
|
16894
17616
|
if (hasPath(map, path))
|
|
16895
17617
|
recordRetired(out, prefix, path);
|
|
16896
17618
|
}
|
|
@@ -17349,11 +18071,13 @@ class BridgePool {
|
|
|
17349
18071
|
return alive;
|
|
17350
18072
|
}
|
|
17351
18073
|
getBridge(projectRoot) {
|
|
18074
|
+
return this.getBridgeForCanonicalRoot(asCanonicalRootPath(normalizeKey(projectRoot)));
|
|
18075
|
+
}
|
|
18076
|
+
getBridgeForCanonicalRoot(key) {
|
|
17352
18077
|
if (this.shutdownCalled) {
|
|
17353
18078
|
this.shutdownCalled = false;
|
|
17354
18079
|
this.startCleanupTimer();
|
|
17355
18080
|
}
|
|
17356
|
-
const key = normalizeKey(projectRoot);
|
|
17357
18081
|
const existing = this.bridges.get(key);
|
|
17358
18082
|
if (existing) {
|
|
17359
18083
|
existing.lastUsed = Date.now();
|
|
@@ -17559,7 +18283,9 @@ class RevivableTransportPool {
|
|
|
17559
18283
|
this.activePool = initialPool;
|
|
17560
18284
|
}
|
|
17561
18285
|
getBridge(projectRoot) {
|
|
17562
|
-
|
|
18286
|
+
return this.getBridgeForCanonicalRoot(asCanonicalRootPath(canonicalizeProjectRoot(projectRoot)));
|
|
18287
|
+
}
|
|
18288
|
+
getBridgeForCanonicalRoot(key) {
|
|
17563
18289
|
let transport = this.transports.get(key);
|
|
17564
18290
|
if (!transport) {
|
|
17565
18291
|
transport = new RevivableProjectTransport(this, key);
|
|
@@ -17594,7 +18320,7 @@ class RevivableTransportPool {
|
|
|
17594
18320
|
return transport;
|
|
17595
18321
|
}
|
|
17596
18322
|
async toolCall(projectRoot, runtime, name, rawArgs = {}, options) {
|
|
17597
|
-
return this.
|
|
18323
|
+
return this.toolCallForCanonicalRoot(asCanonicalRootPath(canonicalizeProjectRoot(projectRoot)), runtime.sessionID, name, rawArgs, options);
|
|
17598
18324
|
}
|
|
17599
18325
|
setConfigureOverride(key, value) {
|
|
17600
18326
|
if (key === "edit_slot_survives") {
|
|
@@ -17734,14 +18460,22 @@ class RevivableTransportPool {
|
|
|
17734
18460
|
return this.activePool?.getActiveBridgeForRoot(projectRoot) ?? null;
|
|
17735
18461
|
}
|
|
17736
18462
|
async send(projectRoot, command, params, options) {
|
|
18463
|
+
return this.sendForCanonicalRoot(asCanonicalRootPath(canonicalizeProjectRoot(projectRoot)), command, params, options);
|
|
18464
|
+
}
|
|
18465
|
+
async sendForCanonicalRoot(projectRoot, command, params, options) {
|
|
18466
|
+
this.getBridgeForCanonicalRoot(projectRoot);
|
|
17737
18467
|
const pool = await this.ensureActivePool();
|
|
17738
|
-
const bridge = pool.getBridge(projectRoot);
|
|
18468
|
+
const bridge = pool.getBridgeForCanonicalRoot?.(projectRoot) ?? pool.getBridge(projectRoot);
|
|
17739
18469
|
this.transports.get(projectRoot)?.refreshStatusSubscription(bridge);
|
|
17740
18470
|
return bridge.send(command, params, options);
|
|
17741
18471
|
}
|
|
17742
18472
|
async toolCallOnProject(projectRoot, sessionId, name, rawArgs, options) {
|
|
18473
|
+
return this.toolCallForCanonicalRoot(asCanonicalRootPath(canonicalizeProjectRoot(projectRoot)), sessionId, name, rawArgs, options);
|
|
18474
|
+
}
|
|
18475
|
+
async toolCallForCanonicalRoot(projectRoot, sessionId, name, rawArgs, options) {
|
|
18476
|
+
this.getBridgeForCanonicalRoot(projectRoot);
|
|
17743
18477
|
const pool = await this.ensureActivePool();
|
|
17744
|
-
const bridge = pool.getBridge(projectRoot);
|
|
18478
|
+
const bridge = pool.getBridgeForCanonicalRoot?.(projectRoot) ?? pool.getBridge(projectRoot);
|
|
17745
18479
|
this.transports.get(projectRoot)?.refreshStatusSubscription(bridge);
|
|
17746
18480
|
return bridge.toolCall(sessionId, name, rawArgs, options);
|
|
17747
18481
|
}
|
|
@@ -17819,7 +18553,7 @@ function satisfyLifecycleDemand(wrapper, projectRoot) {
|
|
|
17819
18553
|
const activePool = wrapper.activePool;
|
|
17820
18554
|
if (activePool.isShutdown() || !(activePool instanceof SubcTransportPool))
|
|
17821
18555
|
return;
|
|
17822
|
-
activePool.
|
|
18556
|
+
activePool.getBridgeForCanonicalRoot(asCanonicalRootPath(projectRoot));
|
|
17823
18557
|
}
|
|
17824
18558
|
async function createAftTransportPool(opts) {
|
|
17825
18559
|
let binaryPath = opts.binaryPath;
|
|
@@ -17854,8 +18588,8 @@ async function createAftTransportPool(opts) {
|
|
|
17854
18588
|
});
|
|
17855
18589
|
if (reapingEnabled) {
|
|
17856
18590
|
const owner = wrapper;
|
|
17857
|
-
const getBridge = owner.
|
|
17858
|
-
owner.
|
|
18591
|
+
const getBridge = owner.getBridgeForCanonicalRoot.bind(owner);
|
|
18592
|
+
owner.getBridgeForCanonicalRoot = (projectRoot) => {
|
|
17859
18593
|
satisfyLifecycleDemand(owner, projectRoot);
|
|
17860
18594
|
return getBridge(projectRoot);
|
|
17861
18595
|
};
|
|
@@ -17881,6 +18615,7 @@ async function createConcreteAftTransportPool(opts) {
|
|
|
17881
18615
|
throw new Error(missing);
|
|
17882
18616
|
}
|
|
17883
18617
|
return new SubcTransportPool({
|
|
18618
|
+
hostScopes: opts.harness === "opencode" || opts.harness === "pi",
|
|
17884
18619
|
connectionFile,
|
|
17885
18620
|
harness: opts.harness,
|
|
17886
18621
|
consumerIdentity: opts.subcConsumerIdentity,
|
|
@@ -17952,7 +18687,7 @@ import { homedir as homedir11, tmpdir as tmpdir3 } from "node:os";
|
|
|
17952
18687
|
import { basename as basename2, dirname as dirname6, join as join12, resolve as resolve8 } from "node:path";
|
|
17953
18688
|
|
|
17954
18689
|
// ../aft-bridge/dist/paths.js
|
|
17955
|
-
import { existsSync as existsSync5, readFileSync as readFileSync5, renameSync as renameSync3, writeFileSync as writeFileSync2 } from "node:fs";
|
|
18690
|
+
import { existsSync as existsSync5, readFileSync as readFileSync5, realpathSync as realpathSync3, renameSync as renameSync3, writeFileSync as writeFileSync2 } from "node:fs";
|
|
17956
18691
|
import { homedir as homedir9 } from "node:os";
|
|
17957
18692
|
import { dirname as dirname4, isAbsolute as isAbsolute5, join as join10, resolve as resolve7 } from "node:path";
|
|
17958
18693
|
function homeDir3() {
|
|
@@ -17987,6 +18722,23 @@ function resolveCortexKitUserConfigPath() {
|
|
|
17987
18722
|
function resolveCortexKitProjectConfigPath(projectDirectory) {
|
|
17988
18723
|
return join10(projectDirectory, ".cortexkit", "aft.jsonc");
|
|
17989
18724
|
}
|
|
18725
|
+
function resolveProjectConfigReadPath(projectDirectory, operatingHarness) {
|
|
18726
|
+
const canonical = resolveCortexKitProjectConfigPath(projectDirectory);
|
|
18727
|
+
if (existsSync5(canonical))
|
|
18728
|
+
return canonical;
|
|
18729
|
+
const sources = resolveLegacyAftConfigSources(projectDirectory).project;
|
|
18730
|
+
const ordered = operatingHarness ? [
|
|
18731
|
+
...sources.filter((source) => source.harness === operatingHarness),
|
|
18732
|
+
...sources.filter((source) => source.harness !== operatingHarness)
|
|
18733
|
+
] : sources;
|
|
18734
|
+
return ordered.find((source) => existsSync5(source.path))?.path ?? canonical;
|
|
18735
|
+
}
|
|
18736
|
+
function legacyProjectConfigLocationNotice(projectDirectory, path) {
|
|
18737
|
+
const canonical = resolveCortexKitProjectConfigPath(projectDirectory);
|
|
18738
|
+
if (path === canonical)
|
|
18739
|
+
return null;
|
|
18740
|
+
return `Legacy project config ${path} is read in place; move it to ${canonical} yourself, and use aft doctor --fix to update retired keys.`;
|
|
18741
|
+
}
|
|
17990
18742
|
function resolveCortexKitConfigPaths(projectDirectory) {
|
|
17991
18743
|
return {
|
|
17992
18744
|
userConfigPath: resolveCortexKitUserConfigPath(),
|
|
@@ -17994,15 +18746,27 @@ function resolveCortexKitConfigPaths(projectDirectory) {
|
|
|
17994
18746
|
};
|
|
17995
18747
|
}
|
|
17996
18748
|
function resolveLegacyAftConfigSources(projectDirectory) {
|
|
18749
|
+
const project = [
|
|
18750
|
+
...legacySources(join10(projectDirectory, ".opencode", "aft"), "OpenCode project", "opencode"),
|
|
18751
|
+
...legacySources(join10(projectDirectory, ".pi", "aft"), "Pi project", "pi")
|
|
18752
|
+
];
|
|
18753
|
+
const identity = (path) => {
|
|
18754
|
+
try {
|
|
18755
|
+
return realpathSync3(path);
|
|
18756
|
+
} catch {
|
|
18757
|
+
return resolve7(path);
|
|
18758
|
+
}
|
|
18759
|
+
};
|
|
18760
|
+
const protectedPaths = new Set([
|
|
18761
|
+
resolveCortexKitProjectConfigPath(projectDirectory),
|
|
18762
|
+
...project.map((source) => source.path)
|
|
18763
|
+
].map(identity));
|
|
17997
18764
|
return {
|
|
17998
18765
|
user: [
|
|
17999
18766
|
...legacySources(join10(legacyOpenCodeConfigDir(), "aft"), "OpenCode user", "opencode"),
|
|
18000
18767
|
...legacySources(join10(legacyPiAgentDir(), "aft"), "Pi user", "pi")
|
|
18001
|
-
],
|
|
18002
|
-
project
|
|
18003
|
-
...legacySources(join10(projectDirectory, ".opencode", "aft"), "OpenCode project", "opencode"),
|
|
18004
|
-
...legacySources(join10(projectDirectory, ".pi", "aft"), "Pi project", "pi")
|
|
18005
|
-
]
|
|
18768
|
+
].filter((source) => !protectedPaths.has(identity(source.path))),
|
|
18769
|
+
project
|
|
18006
18770
|
};
|
|
18007
18771
|
}
|
|
18008
18772
|
function resolveHarnessStoragePath(storageRoot, harness, ...segments) {
|
|
@@ -19187,7 +19951,7 @@ function npmSpawnEnv(resolved, baseEnv = process.env, platform = process.platfor
|
|
|
19187
19951
|
return withPathPrepended(baseEnv, resolved.binDir, platform);
|
|
19188
19952
|
}
|
|
19189
19953
|
// ../aft-bridge/dist/onnx-probe.js
|
|
19190
|
-
import { closeSync as closeSync7, existsSync as existsSync8, openSync as openSync6, readSync as readSync5, realpathSync as
|
|
19954
|
+
import { closeSync as closeSync7, existsSync as existsSync8, openSync as openSync6, readSync as readSync5, realpathSync as realpathSync4 } from "node:fs";
|
|
19191
19955
|
var MH_MAGIC = 4277009102;
|
|
19192
19956
|
var MH_MAGIC_64 = 4277009103;
|
|
19193
19957
|
var FAT_MAGIC = 3405691582;
|
|
@@ -19274,7 +20038,7 @@ function isUncheckableDependency(path) {
|
|
|
19274
20038
|
function probeOnnxRuntimeLoadable(libPath, arch = process.arch) {
|
|
19275
20039
|
let fd;
|
|
19276
20040
|
try {
|
|
19277
|
-
fd = openSync6(
|
|
20041
|
+
fd = openSync6(realpathSync4(libPath), "r");
|
|
19278
20042
|
} catch (err) {
|
|
19279
20043
|
return {
|
|
19280
20044
|
loadable: false,
|
|
@@ -19313,7 +20077,7 @@ function probeOnnxRuntimeLoadable(libPath, arch = process.arch) {
|
|
|
19313
20077
|
}
|
|
19314
20078
|
// ../aft-bridge/dist/onnx-runtime.js
|
|
19315
20079
|
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
19316
|
-
import { chmodSync as chmodSync2, closeSync as closeSync8, copyFileSync as copyFileSync3, createWriteStream as createWriteStream2, existsSync as existsSync10, lstatSync as lstatSync2, openSync as openSync7, readdirSync as readdirSync3, readFileSync as readFileSync9, readlinkSync, realpathSync as
|
|
20080
|
+
import { chmodSync as chmodSync2, closeSync as closeSync8, copyFileSync as copyFileSync3, createWriteStream as createWriteStream2, existsSync as existsSync10, lstatSync as lstatSync2, openSync as openSync7, readdirSync as readdirSync3, readFileSync as readFileSync9, readlinkSync, realpathSync as realpathSync5, renameSync as renameSync6, rmSync as rmSync3, statSync as statSync9, symlinkSync, unlinkSync as unlinkSync4, writeFileSync as writeFileSync5 } from "node:fs";
|
|
19317
20081
|
import { basename as basename3, dirname as dirname9, isAbsolute as isAbsolute8, join as join16, relative as relative4, resolve as resolve10, win32 as win322 } from "node:path";
|
|
19318
20082
|
import { Readable as Readable2 } from "node:stream";
|
|
19319
20083
|
import { pipeline as pipeline2 } from "node:stream/promises";
|
|
@@ -19594,7 +20358,7 @@ function detectOnnxVersion(libDir, libName) {
|
|
|
19594
20358
|
try {
|
|
19595
20359
|
const entries = readdirSync3(libDir);
|
|
19596
20360
|
try {
|
|
19597
|
-
const resolved = parseOnnxVersionFromPath(
|
|
20361
|
+
const resolved = parseOnnxVersionFromPath(realpathSync5(join16(libDir, libName)));
|
|
19598
20362
|
if (resolved && resolved !== INVALID_ORT_VERSION)
|
|
19599
20363
|
return resolved;
|
|
19600
20364
|
} catch {}
|
|
@@ -19611,7 +20375,7 @@ function detectOnnxVersion(libDir, libName) {
|
|
|
19611
20375
|
const base = join16(libDir, libName);
|
|
19612
20376
|
if (existsSync10(base)) {
|
|
19613
20377
|
try {
|
|
19614
|
-
const real =
|
|
20378
|
+
const real = realpathSync5(base);
|
|
19615
20379
|
const version = parseOnnxVersionFromPath(real) ?? parseOnnxVersionFromDirectoryPath(real);
|
|
19616
20380
|
if (version)
|
|
19617
20381
|
return version;
|
|
@@ -19803,7 +20567,7 @@ async function downloadFileWithCap(url, destPath) {
|
|
|
19803
20567
|
}
|
|
19804
20568
|
}
|
|
19805
20569
|
function validateExtractedTree(stagingRoot) {
|
|
19806
|
-
const realRoot =
|
|
20570
|
+
const realRoot = realpathSync5(stagingRoot);
|
|
19807
20571
|
let totalBytes = 0;
|
|
19808
20572
|
const walk = (dir) => {
|
|
19809
20573
|
const entries = readdirSync3(dir);
|
|
@@ -19952,7 +20716,7 @@ function copyOnnxLibraries(info, extractedDir, targetDir, realFiles, symlinks, c
|
|
|
19952
20716
|
log(`ORT extract: failed to copy optional ${libFile}: ${copyErr}`);
|
|
19953
20717
|
}
|
|
19954
20718
|
}
|
|
19955
|
-
const targetRoot =
|
|
20719
|
+
const targetRoot = realpathSync5(targetDir);
|
|
19956
20720
|
for (const link of symlinks) {
|
|
19957
20721
|
const dst = join16(targetDir, link.name);
|
|
19958
20722
|
try {
|
|
@@ -21156,7 +21920,7 @@ async function handlePushedBgLongRunning(drainContext, reminder) {
|
|
|
21156
21920
|
async function appendToolResultBgCompletions(drainContext, content) {
|
|
21157
21921
|
const state = stateFor(drainContext.sessionID);
|
|
21158
21922
|
if (state.outstandingTaskIds.size === 0 && state.pendingCompletions.length === 0 && state.pendingLongRunning.length === 0 && state.pendingPatternMatches.length === 0) {
|
|
21159
|
-
if (!idleDrainMayFindWork(state))
|
|
21923
|
+
if (!idleDrainMayFindWork(state, drainContext))
|
|
21160
21924
|
return;
|
|
21161
21925
|
await drainCompletions(drainContext);
|
|
21162
21926
|
}
|
|
@@ -21288,7 +22052,8 @@ async function withBgHopTimeout(operation, label) {
|
|
|
21288
22052
|
}
|
|
21289
22053
|
async function triggerWakeIfPending(drainContext, skipDrain, includeDeferredCompletions = true, forceDrain = false) {
|
|
21290
22054
|
const state = stateFor(drainContext.sessionID);
|
|
21291
|
-
|
|
22055
|
+
const hasTrackedOrPendingWork = state.outstandingTaskIds.size > 0 || state.pendingCompletions.length > 0 || state.pendingLongRunning.length > 0 || state.pendingPatternMatches.length > 0;
|
|
22056
|
+
if (!skipDrain && (forceDrain || state.outstandingTaskIds.size > 0 || !state.forcedDrainCompleted) && (forceDrain || hasTrackedOrPendingWork || idleDrainMayFindWork(state, drainContext))) {
|
|
21292
22057
|
await drainCompletions(drainContext);
|
|
21293
22058
|
}
|
|
21294
22059
|
routeExplicitControlCompletions(state);
|
|
@@ -21409,8 +22174,13 @@ function bridgeForDrain(drainContext) {
|
|
|
21409
22174
|
function isTerminalNudgeError(error) {
|
|
21410
22175
|
return error instanceof Error && error.code === "root_generation_expired";
|
|
21411
22176
|
}
|
|
21412
|
-
function idleDrainMayFindWork(state) {
|
|
21413
|
-
|
|
22177
|
+
function idleDrainMayFindWork(state, drainContext) {
|
|
22178
|
+
if (state.untrackedCompletionSinceDrain || state.deliveredAwaitingAckTaskIds.size > 0) {
|
|
22179
|
+
return true;
|
|
22180
|
+
}
|
|
22181
|
+
if (state.forcedDrainCompleted)
|
|
22182
|
+
return false;
|
|
22183
|
+
return Boolean(drainContext.resolvedBridge ?? drainContext.ctx.pool.getActiveBridgeForRoot(drainContext.directory));
|
|
21414
22184
|
}
|
|
21415
22185
|
async function drainCompletions(drainContext) {
|
|
21416
22186
|
const state = stateFor(drainContext.sessionID);
|
|
@@ -21840,7 +22610,7 @@ import {
|
|
|
21840
22610
|
// package.json
|
|
21841
22611
|
var package_default = {
|
|
21842
22612
|
name: "@cortexkit/aft-pi",
|
|
21843
|
-
version: "0.
|
|
22613
|
+
version: "0.60.0",
|
|
21844
22614
|
type: "module",
|
|
21845
22615
|
description: "Pi coding agent extension for Agent File Tools (AFT) — tree-sitter and LSP-powered code analysis",
|
|
21846
22616
|
main: "dist/index.js",
|
|
@@ -21864,18 +22634,18 @@ var package_default = {
|
|
|
21864
22634
|
"test:e2e": "bun test src/__tests__/e2e/"
|
|
21865
22635
|
},
|
|
21866
22636
|
dependencies: {
|
|
21867
|
-
"@cortexkit/aft-bridge": "0.
|
|
22637
|
+
"@cortexkit/aft-bridge": "0.60.0",
|
|
21868
22638
|
"comment-json": "^5.0.0",
|
|
21869
22639
|
diff: "^8.0.4",
|
|
21870
22640
|
zod: "^4.4.3"
|
|
21871
22641
|
},
|
|
21872
22642
|
optionalDependencies: {
|
|
21873
|
-
"@cortexkit/aft-darwin-arm64": "0.
|
|
21874
|
-
"@cortexkit/aft-darwin-x64": "0.
|
|
21875
|
-
"@cortexkit/aft-linux-arm64": "0.
|
|
21876
|
-
"@cortexkit/aft-linux-x64": "0.
|
|
21877
|
-
"@cortexkit/aft-win32-arm64": "0.
|
|
21878
|
-
"@cortexkit/aft-win32-x64": "0.
|
|
22643
|
+
"@cortexkit/aft-darwin-arm64": "0.60.0",
|
|
22644
|
+
"@cortexkit/aft-darwin-x64": "0.60.0",
|
|
22645
|
+
"@cortexkit/aft-linux-arm64": "0.60.0",
|
|
22646
|
+
"@cortexkit/aft-linux-x64": "0.60.0",
|
|
22647
|
+
"@cortexkit/aft-win32-arm64": "0.60.0",
|
|
22648
|
+
"@cortexkit/aft-win32-x64": "0.60.0"
|
|
21879
22649
|
},
|
|
21880
22650
|
devDependencies: {
|
|
21881
22651
|
"@earendil-works/pi-coding-agent": "^0.80.2",
|
|
@@ -22613,7 +23383,7 @@ function registerStatusCommand(pi, ctx) {
|
|
|
22613
23383
|
}
|
|
22614
23384
|
|
|
22615
23385
|
// src/config.ts
|
|
22616
|
-
import { existsSync as existsSync12, readFileSync as readFileSync10
|
|
23386
|
+
import { existsSync as existsSync12, readFileSync as readFileSync10 } from "node:fs";
|
|
22617
23387
|
import { homedir as homedir14 } from "node:os";
|
|
22618
23388
|
import { isAbsolute as isAbsolute9 } from "node:path";
|
|
22619
23389
|
var import_comment_json = __toESM(require_src2(), 1);
|
|
@@ -27723,154 +28493,6 @@ function applyActiveHarnessOverride(config, trusted) {
|
|
|
27723
28493
|
merged.indexes = indexes;
|
|
27724
28494
|
return merged;
|
|
27725
28495
|
}
|
|
27726
|
-
var CONFIG_MIGRATIONS = [
|
|
27727
|
-
{ oldKey: "experimental_lsp_ty", newPath: ["experimental", "lsp_ty"] },
|
|
27728
|
-
{ oldKey: "experimental_bash_rewrite", newPath: ["experimental", "bash", "rewrite"] },
|
|
27729
|
-
{ oldKey: "experimental_bash_compress", newPath: ["experimental", "bash", "compress"] },
|
|
27730
|
-
{ oldKey: "experimental_bash_background", newPath: ["experimental", "bash", "background"] }
|
|
27731
|
-
];
|
|
27732
|
-
function isWritableMigrationError(errorValue) {
|
|
27733
|
-
const code = errorValue?.code;
|
|
27734
|
-
return code === "EROFS" || code === "EACCES" || code === "EPERM";
|
|
27735
|
-
}
|
|
27736
|
-
function extractCommentsForPreservation(content) {
|
|
27737
|
-
const comments = [];
|
|
27738
|
-
const linePattern = /\/\/[^\n]*/g;
|
|
27739
|
-
for (const match of content.match(linePattern) ?? []) {
|
|
27740
|
-
comments.push(match.trim());
|
|
27741
|
-
}
|
|
27742
|
-
const blockPattern = /\/\*[\s\S]*?\*\//g;
|
|
27743
|
-
for (const match of content.match(blockPattern) ?? []) {
|
|
27744
|
-
comments.push(match.replace(/\s+/g, " ").trim());
|
|
27745
|
-
}
|
|
27746
|
-
return comments;
|
|
27747
|
-
}
|
|
27748
|
-
function ensureRecordAtPath(root, path) {
|
|
27749
|
-
let current = root;
|
|
27750
|
-
for (const segment of path) {
|
|
27751
|
-
const existing = current[segment];
|
|
27752
|
-
if (!existing || typeof existing !== "object" || Array.isArray(existing)) {
|
|
27753
|
-
current[segment] = {};
|
|
27754
|
-
}
|
|
27755
|
-
current = current[segment];
|
|
27756
|
-
}
|
|
27757
|
-
return current;
|
|
27758
|
-
}
|
|
27759
|
-
function hasPath2(root, path) {
|
|
27760
|
-
let current = root;
|
|
27761
|
-
for (const segment of path) {
|
|
27762
|
-
if (!current || typeof current !== "object" || Array.isArray(current))
|
|
27763
|
-
return false;
|
|
27764
|
-
const record2 = current;
|
|
27765
|
-
if (!Object.hasOwn(record2, segment))
|
|
27766
|
-
return false;
|
|
27767
|
-
current = record2[segment];
|
|
27768
|
-
}
|
|
27769
|
-
return true;
|
|
27770
|
-
}
|
|
27771
|
-
function setPath(root, path, value) {
|
|
27772
|
-
const parent = ensureRecordAtPath(root, path.slice(0, -1));
|
|
27773
|
-
parent[path[path.length - 1]] = value;
|
|
27774
|
-
}
|
|
27775
|
-
function migrateRawConfig(rawConfig, configPath, logger) {
|
|
27776
|
-
const oldKeys = [];
|
|
27777
|
-
for (const migration of CONFIG_MIGRATIONS) {
|
|
27778
|
-
if (!Object.hasOwn(rawConfig, migration.oldKey))
|
|
27779
|
-
continue;
|
|
27780
|
-
if (hasPath2(rawConfig, migration.newPath)) {
|
|
27781
|
-
logger?.warn(`Config migration conflict at ${configPath}: ${migration.oldKey} ignored because ${migration.newPath.join(".")} is already set`);
|
|
27782
|
-
} else {
|
|
27783
|
-
setPath(rawConfig, migration.newPath, rawConfig[migration.oldKey]);
|
|
27784
|
-
}
|
|
27785
|
-
delete rawConfig[migration.oldKey];
|
|
27786
|
-
oldKeys.push(migration.oldKey);
|
|
27787
|
-
}
|
|
27788
|
-
oldKeys.push(...migrateExperimentalBashBlock(rawConfig, configPath, logger));
|
|
27789
|
-
return oldKeys;
|
|
27790
|
-
}
|
|
27791
|
-
function migrateExperimentalBashBlock(rawConfig, configPath, logger) {
|
|
27792
|
-
const experimental = rawConfig.experimental;
|
|
27793
|
-
if (typeof experimental !== "object" || experimental === null || Array.isArray(experimental)) {
|
|
27794
|
-
return [];
|
|
27795
|
-
}
|
|
27796
|
-
const expRecord = experimental;
|
|
27797
|
-
if (!Object.hasOwn(expRecord, "bash"))
|
|
27798
|
-
return [];
|
|
27799
|
-
const legacyBash = expRecord.bash;
|
|
27800
|
-
if (typeof legacyBash !== "object" || legacyBash === null || Array.isArray(legacyBash)) {
|
|
27801
|
-
delete expRecord.bash;
|
|
27802
|
-
if (Object.keys(expRecord).length === 0)
|
|
27803
|
-
delete rawConfig.experimental;
|
|
27804
|
-
return ["experimental.bash"];
|
|
27805
|
-
}
|
|
27806
|
-
const bashRecord = legacyBash;
|
|
27807
|
-
const hasFeatureFlag = "rewrite" in bashRecord || "compress" in bashRecord || "background" in bashRecord;
|
|
27808
|
-
if (!hasFeatureFlag)
|
|
27809
|
-
return [];
|
|
27810
|
-
const movedKeys = Object.keys(bashRecord).map((k) => `experimental.bash.${k}`);
|
|
27811
|
-
if (Object.hasOwn(rawConfig, "bash")) {
|
|
27812
|
-
logger?.warn(`Config migration conflict at ${configPath}: experimental.bash dropped because top-level "bash" is already set`);
|
|
27813
|
-
} else {
|
|
27814
|
-
const migrated = {
|
|
27815
|
-
rewrite: bashRecord.rewrite === true,
|
|
27816
|
-
compress: bashRecord.compress === true,
|
|
27817
|
-
background: bashRecord.background === true
|
|
27818
|
-
};
|
|
27819
|
-
if (bashRecord.long_running_reminder_enabled !== undefined) {
|
|
27820
|
-
migrated.long_running_reminder_enabled = bashRecord.long_running_reminder_enabled;
|
|
27821
|
-
}
|
|
27822
|
-
if (bashRecord.long_running_reminder_interval_ms !== undefined) {
|
|
27823
|
-
migrated.long_running_reminder_interval_ms = bashRecord.long_running_reminder_interval_ms;
|
|
27824
|
-
}
|
|
27825
|
-
rawConfig.bash = migrated;
|
|
27826
|
-
}
|
|
27827
|
-
delete expRecord.bash;
|
|
27828
|
-
if (Object.keys(expRecord).length === 0) {
|
|
27829
|
-
delete rawConfig.experimental;
|
|
27830
|
-
}
|
|
27831
|
-
return movedKeys;
|
|
27832
|
-
}
|
|
27833
|
-
function migrateAftConfigFile2(configPath, logger = { log: log2, warn: warn2 }) {
|
|
27834
|
-
if (!existsSync12(configPath)) {
|
|
27835
|
-
return { migrated: false, oldKeys: [] };
|
|
27836
|
-
}
|
|
27837
|
-
let tmpPath = null;
|
|
27838
|
-
let oldKeys = [];
|
|
27839
|
-
try {
|
|
27840
|
-
const content = readFileSync10(configPath, "utf-8");
|
|
27841
|
-
const rawConfig = import_comment_json.parse(content);
|
|
27842
|
-
if (!rawConfig || typeof rawConfig !== "object" || Array.isArray(rawConfig)) {
|
|
27843
|
-
return { migrated: false, oldKeys: [] };
|
|
27844
|
-
}
|
|
27845
|
-
oldKeys = migrateRawConfig(rawConfig, configPath, logger);
|
|
27846
|
-
if (oldKeys.length === 0) {
|
|
27847
|
-
return { migrated: false, oldKeys: [] };
|
|
27848
|
-
}
|
|
27849
|
-
const serialized = `${import_comment_json.stringify(rawConfig, null, 2)}
|
|
27850
|
-
`;
|
|
27851
|
-
const preservedComments = extractCommentsForPreservation(content).filter((comment) => !serialized.includes(comment.trim()));
|
|
27852
|
-
const nextContent = preservedComments.length > 0 ? `${preservedComments.join(`
|
|
27853
|
-
`)}
|
|
27854
|
-
${serialized}` : serialized;
|
|
27855
|
-
tmpPath = `${configPath}.tmp.${process.pid}`;
|
|
27856
|
-
writeFileSync6(tmpPath, nextContent, "utf-8");
|
|
27857
|
-
renameSync7(tmpPath, configPath);
|
|
27858
|
-
logger.log(`Migrated config at ${configPath}: removed ${oldKeys.join(", ")}`);
|
|
27859
|
-
return { migrated: true, oldKeys };
|
|
27860
|
-
} catch (err) {
|
|
27861
|
-
if (tmpPath) {
|
|
27862
|
-
try {
|
|
27863
|
-
unlinkSync5(tmpPath);
|
|
27864
|
-
} catch {}
|
|
27865
|
-
}
|
|
27866
|
-
if (isWritableMigrationError(err)) {
|
|
27867
|
-
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
27868
|
-
logger.warn(`Config migration could not write ${configPath} (${errorMsg}); using migrated config in memory`);
|
|
27869
|
-
return { migrated: oldKeys.length > 0, oldKeys };
|
|
27870
|
-
}
|
|
27871
|
-
return { migrated: false, oldKeys: [] };
|
|
27872
|
-
}
|
|
27873
|
-
}
|
|
27874
28496
|
var configLoadErrors = [];
|
|
27875
28497
|
var configValidationErrors = [];
|
|
27876
28498
|
var configLoadSources = [];
|
|
@@ -27919,7 +28541,6 @@ function loadConfigFromPath(configPath, tier) {
|
|
|
27919
28541
|
recordConfigParseFailure(configPath, "root must be an object");
|
|
27920
28542
|
return null;
|
|
27921
28543
|
}
|
|
27922
|
-
migrateRawConfig(rawConfig, configPath, { log: log2, warn: warn2 });
|
|
27923
28544
|
cleanConfig = stripJsoncSymbols(rawConfig);
|
|
27924
28545
|
} catch (err) {
|
|
27925
28546
|
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
@@ -28293,21 +28914,14 @@ function migrateAftConfigLocations(projectDirectory, logger = { log: log2, warn:
|
|
|
28293
28914
|
legacySources: legacy.user,
|
|
28294
28915
|
operatingHarness: "pi",
|
|
28295
28916
|
logger
|
|
28296
|
-
}),
|
|
28297
|
-
migrateAftConfigFile({
|
|
28298
|
-
scope: "project",
|
|
28299
|
-
targetPath: paths.projectConfigPath,
|
|
28300
|
-
legacySources: legacy.project,
|
|
28301
|
-
operatingHarness: "pi",
|
|
28302
|
-
logger
|
|
28303
28917
|
})
|
|
28304
28918
|
];
|
|
28305
28919
|
}
|
|
28306
28920
|
function resolveAftConfigPaths(projectDirectory) {
|
|
28307
|
-
|
|
28308
|
-
|
|
28309
|
-
|
|
28310
|
-
|
|
28921
|
+
return {
|
|
28922
|
+
...resolveCortexKitConfigPaths(projectDirectory),
|
|
28923
|
+
projectConfigPath: resolveProjectConfigReadPath(projectDirectory, ACTIVE_HARNESS)
|
|
28924
|
+
};
|
|
28311
28925
|
}
|
|
28312
28926
|
function buildConfigTierConfigureParams(projectDirectory, processState = {}) {
|
|
28313
28927
|
const paths = resolveAftConfigPaths(projectDirectory);
|
|
@@ -28329,6 +28943,15 @@ function loadAftConfig(projectDirectory) {
|
|
|
28329
28943
|
configLoadNotices = [];
|
|
28330
28944
|
semanticInputSupplied = false;
|
|
28331
28945
|
const { userConfigPath, projectConfigPath } = resolveAftConfigPaths(projectDirectory);
|
|
28946
|
+
const locationNotice = legacyProjectConfigLocationNotice(projectDirectory, projectConfigPath);
|
|
28947
|
+
if (locationNotice) {
|
|
28948
|
+
log2(locationNotice);
|
|
28949
|
+
configLoadNotices.push({
|
|
28950
|
+
configPath: projectConfigPath,
|
|
28951
|
+
digest: "legacy-project-config-location",
|
|
28952
|
+
message: locationNotice
|
|
28953
|
+
});
|
|
28954
|
+
}
|
|
28332
28955
|
let config = loadConfigFromPath(userConfigPath, "user") ?? {
|
|
28333
28956
|
disabled_tools: [...DEFAULT_DISABLED_TOOLS]
|
|
28334
28957
|
};
|
|
@@ -28371,7 +28994,7 @@ function loadAftConfig(projectDirectory) {
|
|
|
28371
28994
|
}
|
|
28372
28995
|
|
|
28373
28996
|
// src/lsp-auto-install.ts
|
|
28374
|
-
import { existsSync as existsSync14, mkdirSync as mkdirSync6, renameSync as
|
|
28997
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync6, renameSync as renameSync8, rmSync as rmSync5, statSync as statSync11, writeFileSync as writeFileSync7 } from "node:fs";
|
|
28375
28998
|
import { join as join19 } from "node:path";
|
|
28376
28999
|
|
|
28377
29000
|
// src/lsp-cache.ts
|
|
@@ -28381,11 +29004,11 @@ import {
|
|
|
28381
29004
|
mkdirSync as mkdirSync5,
|
|
28382
29005
|
openSync as openSync8,
|
|
28383
29006
|
readFileSync as readFileSync11,
|
|
28384
|
-
renameSync as
|
|
29007
|
+
renameSync as renameSync7,
|
|
28385
29008
|
rmSync as rmSync4,
|
|
28386
29009
|
statSync as statSync10,
|
|
28387
|
-
unlinkSync as
|
|
28388
|
-
writeFileSync as
|
|
29010
|
+
unlinkSync as unlinkSync5,
|
|
29011
|
+
writeFileSync as writeFileSync6
|
|
28389
29012
|
} from "node:fs";
|
|
28390
29013
|
import { join as join17 } from "node:path";
|
|
28391
29014
|
function aftCacheBase() {
|
|
@@ -28426,7 +29049,7 @@ function writeInstalledMetaIn(installDir, version, sha256) {
|
|
|
28426
29049
|
installedAt: new Date().toISOString(),
|
|
28427
29050
|
...sha256 ? { sha256 } : {}
|
|
28428
29051
|
};
|
|
28429
|
-
|
|
29052
|
+
writeFileSync6(join17(installDir, INSTALLED_META_FILE), JSON.stringify(meta), "utf8");
|
|
28430
29053
|
} catch (err) {
|
|
28431
29054
|
log2(`[lsp-cache] failed to write installed-meta in ${installDir}: ${err}`);
|
|
28432
29055
|
}
|
|
@@ -28467,7 +29090,7 @@ function acquireInstallLock(lockKey) {
|
|
|
28467
29090
|
try {
|
|
28468
29091
|
const fd = openSync8(lock, "wx");
|
|
28469
29092
|
try {
|
|
28470
|
-
|
|
29093
|
+
writeFileSync6(fd, `${process.pid}
|
|
28471
29094
|
${new Date().toISOString()}
|
|
28472
29095
|
${token}
|
|
28473
29096
|
`);
|
|
@@ -28523,7 +29146,7 @@ ${token}
|
|
|
28523
29146
|
}
|
|
28524
29147
|
log2(`[lsp] reclaiming install lock for ${lockKey} (owner_pid=${owningPid ?? "unknown"}, alive=${ownerAlive}, age_ms=${age})`);
|
|
28525
29148
|
try {
|
|
28526
|
-
|
|
29149
|
+
unlinkSync5(lock);
|
|
28527
29150
|
} catch {}
|
|
28528
29151
|
return tryClaim();
|
|
28529
29152
|
}
|
|
@@ -28562,7 +29185,7 @@ function releaseInstallLock(lockKey, acquisitionToken) {
|
|
|
28562
29185
|
return;
|
|
28563
29186
|
}
|
|
28564
29187
|
try {
|
|
28565
|
-
|
|
29188
|
+
unlinkSync5(lock);
|
|
28566
29189
|
} catch (unlinkErr) {
|
|
28567
29190
|
const code = unlinkErr.code;
|
|
28568
29191
|
if (code !== "ENOENT") {
|
|
@@ -28637,7 +29260,7 @@ function claimLspAutoInstallPass() {
|
|
|
28637
29260
|
try {
|
|
28638
29261
|
const fd = openSync8(lock, "wx");
|
|
28639
29262
|
try {
|
|
28640
|
-
|
|
29263
|
+
writeFileSync6(fd, JSON.stringify(owner));
|
|
28641
29264
|
} finally {
|
|
28642
29265
|
closeSync9(fd);
|
|
28643
29266
|
}
|
|
@@ -28668,12 +29291,12 @@ function claimLspAutoInstallPass() {
|
|
|
28668
29291
|
return null;
|
|
28669
29292
|
}
|
|
28670
29293
|
const tempPath = `${stampPath}.tmp-${process.pid}-${owner.token}`;
|
|
28671
|
-
|
|
29294
|
+
writeFileSync6(tempPath, `${JSON.stringify({ lastRunMs: Date.now() })}
|
|
28672
29295
|
`, {
|
|
28673
29296
|
encoding: "utf8",
|
|
28674
29297
|
mode: 384
|
|
28675
29298
|
});
|
|
28676
|
-
|
|
29299
|
+
renameSync7(tempPath, stampPath);
|
|
28677
29300
|
return { release };
|
|
28678
29301
|
} catch (err) {
|
|
28679
29302
|
if (claimed) {
|
|
@@ -28710,7 +29333,7 @@ function writeVersionCheck(npmPackage, latest) {
|
|
|
28710
29333
|
last_checked: new Date().toISOString(),
|
|
28711
29334
|
latest_eligible: latest
|
|
28712
29335
|
};
|
|
28713
|
-
|
|
29336
|
+
writeFileSync6(file, JSON.stringify(record, null, 2));
|
|
28714
29337
|
}
|
|
28715
29338
|
function shouldRecheckVersion(record, weeklyCheckIntervalMs = 7 * 24 * 60 * 60 * 1000) {
|
|
28716
29339
|
if (!record)
|
|
@@ -29091,7 +29714,7 @@ function ensureInstallAnchor(cwd) {
|
|
|
29091
29714
|
try {
|
|
29092
29715
|
const stub = join19(cwd, "package.json");
|
|
29093
29716
|
if (!existsSync14(stub)) {
|
|
29094
|
-
|
|
29717
|
+
writeFileSync7(stub, `${JSON.stringify({ name: "aft-lsp-cache", version: "0.0.0", private: true })}
|
|
29095
29718
|
`);
|
|
29096
29719
|
}
|
|
29097
29720
|
} catch (err) {
|
|
@@ -29304,7 +29927,7 @@ function quarantineCachedNpmInstall(spec, reason) {
|
|
|
29304
29927
|
try {
|
|
29305
29928
|
mkdirSync6(join19(dest, ".."), { recursive: true });
|
|
29306
29929
|
rmSync5(dest, { recursive: true, force: true });
|
|
29307
|
-
|
|
29930
|
+
renameSync8(packageDir, dest);
|
|
29308
29931
|
} catch (err) {
|
|
29309
29932
|
warn2(`[lsp] tofu_mismatch ${spec.npm}: failed to quarantine cache entry: ${err}`);
|
|
29310
29933
|
}
|
|
@@ -29407,12 +30030,12 @@ import {
|
|
|
29407
30030
|
readFileSync as readFileSync13,
|
|
29408
30031
|
readlinkSync as readlinkSync2,
|
|
29409
30032
|
readSync as readSync6,
|
|
29410
|
-
realpathSync as
|
|
29411
|
-
renameSync as
|
|
30033
|
+
realpathSync as realpathSync6,
|
|
30034
|
+
renameSync as renameSync9,
|
|
29412
30035
|
rmSync as rmSync6,
|
|
29413
30036
|
statSync as statSync12,
|
|
29414
|
-
unlinkSync as
|
|
29415
|
-
writeFileSync as
|
|
30037
|
+
unlinkSync as unlinkSync6,
|
|
30038
|
+
writeFileSync as writeFileSync8
|
|
29416
30039
|
} from "node:fs";
|
|
29417
30040
|
import { dirname as dirname10, join as join20, relative as relative5, resolve as resolve11 } from "node:path";
|
|
29418
30041
|
import { Readable as Readable3 } from "node:stream";
|
|
@@ -29538,9 +30161,9 @@ function writeInstallFailureMarker(spec, release, sha256, installError) {
|
|
|
29538
30161
|
const tempPath = `${path}.tmp-${process.pid}-${randomBytes3(6).toString("hex")}`;
|
|
29539
30162
|
try {
|
|
29540
30163
|
mkdirSync7(dirname10(path), { recursive: true });
|
|
29541
|
-
|
|
30164
|
+
writeFileSync8(tempPath, `${JSON.stringify({ release, sha256, error: markerError, at: new Date().toISOString() })}
|
|
29542
30165
|
`, { encoding: "utf8", mode: 384 });
|
|
29543
|
-
|
|
30166
|
+
renameSync9(tempPath, path);
|
|
29544
30167
|
} catch (err) {
|
|
29545
30168
|
try {
|
|
29546
30169
|
rmSync6(tempPath, { force: true });
|
|
@@ -29606,7 +30229,7 @@ function writeGithubInstalledMetaIn(installDir, version, binarySha256, archiveSh
|
|
|
29606
30229
|
binarySha256,
|
|
29607
30230
|
...archiveSha256 ? { archiveSha256 } : {}
|
|
29608
30231
|
};
|
|
29609
|
-
|
|
30232
|
+
writeFileSync8(join20(installDir, INSTALLED_META_FILE2), JSON.stringify(meta), "utf8");
|
|
29610
30233
|
} catch (err) {
|
|
29611
30234
|
warn2(`[lsp] failed to write github installed metadata in ${installDir}: ${err}`);
|
|
29612
30235
|
}
|
|
@@ -29846,7 +30469,7 @@ async function downloadFile(url, destPath, fetchImpl, assetSize, signal) {
|
|
|
29846
30469
|
await pipeline3(nodeStream, createWriteStream3(destPath), { signal: timeout.signal });
|
|
29847
30470
|
} catch (err) {
|
|
29848
30471
|
try {
|
|
29849
|
-
|
|
30472
|
+
unlinkSync6(destPath);
|
|
29850
30473
|
} catch {}
|
|
29851
30474
|
throw err;
|
|
29852
30475
|
} finally {
|
|
@@ -29875,7 +30498,7 @@ async function fetchFollowingRedirects(url, fetchImpl, signal) {
|
|
|
29875
30498
|
throw new Error(`too many redirects (>${maxRedirects})`);
|
|
29876
30499
|
}
|
|
29877
30500
|
function validateExtraction(stagingRoot) {
|
|
29878
|
-
const realStagingRoot =
|
|
30501
|
+
const realStagingRoot = realpathSync6(stagingRoot);
|
|
29879
30502
|
let totalBytes = 0;
|
|
29880
30503
|
const walk = (dir) => {
|
|
29881
30504
|
let entries;
|
|
@@ -29901,7 +30524,7 @@ function validateExtraction(stagingRoot) {
|
|
|
29901
30524
|
}
|
|
29902
30525
|
let realFull;
|
|
29903
30526
|
try {
|
|
29904
|
-
realFull =
|
|
30527
|
+
realFull = realpathSync6(full);
|
|
29905
30528
|
} catch (err) {
|
|
29906
30529
|
throw new Error(`failed to realpath ${full}: ${err}`);
|
|
29907
30530
|
}
|
|
@@ -29929,14 +30552,30 @@ function assertArchiveEntryPath(entry) {
|
|
|
29929
30552
|
throw new Error(`archive entry escapes archive root: ${entry}`);
|
|
29930
30553
|
}
|
|
29931
30554
|
}
|
|
30555
|
+
var ARCHIVE_LIST_TIMEOUT_MS = 30000;
|
|
29932
30556
|
function runArchiveTool(command, args, timeout) {
|
|
29933
30557
|
return new Promise((resolvePromise, reject) => {
|
|
29934
|
-
|
|
29935
|
-
|
|
29936
|
-
|
|
29937
|
-
|
|
29938
|
-
|
|
29939
|
-
|
|
30558
|
+
let child;
|
|
30559
|
+
const deadline = setTimeout(() => {
|
|
30560
|
+
reject(new Error(`${command} ${args[0]} timed out after ${timeout} ms`));
|
|
30561
|
+
child?.kill("SIGKILL");
|
|
30562
|
+
child?.stdin?.destroy();
|
|
30563
|
+
child?.stdout?.destroy();
|
|
30564
|
+
child?.stderr?.destroy();
|
|
30565
|
+
}, timeout);
|
|
30566
|
+
try {
|
|
30567
|
+
child = execFile(command, args, { encoding: "utf8", timeout, killSignal: "SIGKILL" }, (err, stdout) => {
|
|
30568
|
+
clearTimeout(deadline);
|
|
30569
|
+
if (err)
|
|
30570
|
+
reject(err);
|
|
30571
|
+
else
|
|
30572
|
+
resolvePromise(stdout);
|
|
30573
|
+
});
|
|
30574
|
+
child.stdin?.end();
|
|
30575
|
+
} catch (error) {
|
|
30576
|
+
clearTimeout(deadline);
|
|
30577
|
+
reject(error);
|
|
30578
|
+
}
|
|
29940
30579
|
});
|
|
29941
30580
|
}
|
|
29942
30581
|
async function runTarExtraction(args, timeout) {
|
|
@@ -29950,7 +30589,7 @@ async function runTarExtraction(args, timeout) {
|
|
|
29950
30589
|
}
|
|
29951
30590
|
async function precheckWithTar(archivePath, command) {
|
|
29952
30591
|
let totalBytes = 0;
|
|
29953
|
-
const verbose = await runArchiveTool(command, ["-tvf", archivePath]);
|
|
30592
|
+
const verbose = await runArchiveTool(command, ["-tvf", archivePath], ARCHIVE_LIST_TIMEOUT_MS);
|
|
29954
30593
|
for (const line of verbose.split(`
|
|
29955
30594
|
`)) {
|
|
29956
30595
|
if (line.startsWith("h")) {
|
|
@@ -29963,7 +30602,7 @@ async function precheckWithTar(archivePath, command) {
|
|
|
29963
30602
|
totalBytes += Math.max(...numeric);
|
|
29964
30603
|
}
|
|
29965
30604
|
}
|
|
29966
|
-
const names = await runArchiveTool(command, ["-tf", archivePath]);
|
|
30605
|
+
const names = await runArchiveTool(command, ["-tf", archivePath], ARCHIVE_LIST_TIMEOUT_MS);
|
|
29967
30606
|
for (const entry of names.split(`
|
|
29968
30607
|
`)) {
|
|
29969
30608
|
if (entry.length > 0)
|
|
@@ -29974,11 +30613,11 @@ async function precheckWithTar(archivePath, command) {
|
|
|
29974
30613
|
async function precheckArchiveContents(archivePath, archiveType, zipListingMode = "platform") {
|
|
29975
30614
|
let totalBytes = 0;
|
|
29976
30615
|
if (archiveType === "zip" && zipListingMode === "platform" && process.platform !== "win32") {
|
|
29977
|
-
const out = await runArchiveTool("unzip", ["-l", archivePath]);
|
|
30616
|
+
const out = await runArchiveTool("unzip", ["-l", archivePath], ARCHIVE_LIST_TIMEOUT_MS);
|
|
29978
30617
|
const match = out.match(/^\s*(\d+)\s+\d+\s+files?\s*$/m);
|
|
29979
30618
|
if (match)
|
|
29980
30619
|
totalBytes = Number.parseInt(match[1] ?? "0", 10);
|
|
29981
|
-
const names = await runArchiveTool("unzip", ["-Z1", archivePath]);
|
|
30620
|
+
const names = await runArchiveTool("unzip", ["-Z1", archivePath], ARCHIVE_LIST_TIMEOUT_MS);
|
|
29982
30621
|
for (const entry of names.split(`
|
|
29983
30622
|
`)) {
|
|
29984
30623
|
if (entry.length > 0)
|
|
@@ -30005,7 +30644,7 @@ async function extractArchiveSafely(archivePath, destDir, archiveType) {
|
|
|
30005
30644
|
try {
|
|
30006
30645
|
rmSync6(destDir, { recursive: true, force: true });
|
|
30007
30646
|
} catch {}
|
|
30008
|
-
|
|
30647
|
+
renameSync9(stagingDir, destDir);
|
|
30009
30648
|
} catch (err) {
|
|
30010
30649
|
try {
|
|
30011
30650
|
rmSync6(stagingDir, { recursive: true, force: true });
|
|
@@ -30020,7 +30659,7 @@ function quarantineCachedGithubInstall(spec, reason) {
|
|
|
30020
30659
|
try {
|
|
30021
30660
|
mkdirSync7(dirname10(dest), { recursive: true });
|
|
30022
30661
|
rmSync6(dest, { recursive: true, force: true });
|
|
30023
|
-
|
|
30662
|
+
renameSync9(packageDir, dest);
|
|
30024
30663
|
} catch (err) {
|
|
30025
30664
|
warn2(`[lsp] tofu_mismatch ${spec.id}: failed to quarantine cache entry: ${err}`);
|
|
30026
30665
|
}
|
|
@@ -30112,7 +30751,7 @@ async function downloadAndInstall(spec, tag, assets, platform, arch, fetchImpl,
|
|
|
30112
30751
|
const message = `hash failed: ${err instanceof Error ? err.message : String(err)}`;
|
|
30113
30752
|
error2(`[lsp] hash ${spec.id} failed: ${err}`);
|
|
30114
30753
|
try {
|
|
30115
|
-
|
|
30754
|
+
unlinkSync6(archivePath);
|
|
30116
30755
|
} catch {}
|
|
30117
30756
|
return { ok: false, sha256: "", error: message };
|
|
30118
30757
|
}
|
|
@@ -30123,7 +30762,7 @@ async function downloadAndInstall(spec, tag, assets, platform, arch, fetchImpl,
|
|
|
30123
30762
|
const message = `TOFU sha256 mismatch: previously installed archive sha256=${previousArchiveSha256}, ` + `downloaded sha256=${archiveSha256}`;
|
|
30124
30763
|
error2(`[lsp] ${spec.id} ${tag}: ${message}. ` + `This means the published release for tag ${tag} changed. Investigate before proceeding.`);
|
|
30125
30764
|
try {
|
|
30126
|
-
|
|
30765
|
+
unlinkSync6(archivePath);
|
|
30127
30766
|
} catch {}
|
|
30128
30767
|
return { ok: false, sha256: archiveSha256, error: message };
|
|
30129
30768
|
}
|
|
@@ -30135,7 +30774,7 @@ async function downloadAndInstall(spec, tag, assets, platform, arch, fetchImpl,
|
|
|
30135
30774
|
return { ok: false, sha256: archiveSha256, error: message };
|
|
30136
30775
|
} finally {
|
|
30137
30776
|
try {
|
|
30138
|
-
|
|
30777
|
+
unlinkSync6(archivePath);
|
|
30139
30778
|
} catch {}
|
|
30140
30779
|
}
|
|
30141
30780
|
const innerBinaryPath = join20(extractDir, spec.binaryPathInArchive(platform, arch, version));
|
|
@@ -31571,11 +32210,6 @@ PDFs aren't supported on the Pi harness yet.`, response);
|
|
|
31571
32210
|
restrictToProjectRoot: surface.restrictToProjectRoot
|
|
31572
32211
|
});
|
|
31573
32212
|
}
|
|
31574
|
-
const preview = await callToolCallForSession(bridge, "edit", rawArgs, sessionId, extCtx, {
|
|
31575
|
-
preview: true
|
|
31576
|
-
});
|
|
31577
|
-
if (preview.success === false)
|
|
31578
|
-
throw toolErrorFromResponse("edit", preview);
|
|
31579
32213
|
const response = await callToolCallForSession(bridge, "edit", rawArgs, sessionId, extCtx);
|
|
31580
32214
|
if (response.success === false)
|
|
31581
32215
|
throw toolErrorFromResponse("edit", response);
|
|
@@ -31718,7 +32352,7 @@ function buildMutationResult(response) {
|
|
|
31718
32352
|
let text = response.text;
|
|
31719
32353
|
if (typeof text !== "string") {
|
|
31720
32354
|
text = formatEditSummary(response);
|
|
31721
|
-
if (noOp) {
|
|
32355
|
+
if (noOp && response.rolled_back !== true) {
|
|
31722
32356
|
text += `
|
|
31723
32357
|
|
|
31724
32358
|
Note: no net file change — the match was found and applied, but the file content is byte-identical to before. Likely causes: oldString and newString are identical, or a formatter normalized the change away.`;
|
|
@@ -32190,7 +32824,7 @@ var BashBackgroundTimeoutParam = {
|
|
|
32190
32824
|
};
|
|
32191
32825
|
var BashWaitParam = {
|
|
32192
32826
|
wait: Type5.Optional(Type5.Boolean({
|
|
32193
|
-
description: "When true, run in the foreground without auto-promoting and wait until the command finishes or reaches its timeout (
|
|
32827
|
+
description: "When true, run in the foreground without auto-promoting and wait until the command finishes or reaches its timeout (at most bash.worker_wait_max_ms, 30 minutes by default; the command then keeps running in the background and the reply says how to keep waiting); 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."
|
|
32194
32828
|
}))
|
|
32195
32829
|
};
|
|
32196
32830
|
var BashSandboxParam = {
|
|
@@ -32453,7 +33087,7 @@ function registerBashTool(pi, ctx, aftSearchRegistered = false, registeredName =
|
|
|
32453
33087
|
"PTY mode"
|
|
32454
33088
|
].join(", ") : bashCfg.compress ? "workdir and compressed output" : "workdir";
|
|
32455
33089
|
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)`.";
|
|
32456
|
-
const tasksSentence = bashCfg.background ? ` Commands run in the foreground and return inline; \`wait: true\` blocks until a long command finishes instead of auto-promoting (
|
|
33090
|
+
const tasksSentence = bashCfg.background ? ` Commands run in the foreground and return inline; \`wait: true\` blocks until a long command finishes instead of auto-promoting (it blocks up to \`bash.worker_wait_max_ms\`, 30 minutes by default, then reports the command is still running; watch again to keep waiting); ${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).";
|
|
32457
33091
|
const remoteRuns = () => !isPowerShell && remoteRunsOffered(ctx.config);
|
|
32458
33092
|
pi.registerTool({
|
|
32459
33093
|
name: registeredName,
|
|
@@ -32509,7 +33143,7 @@ DO NOT use bash for code search or code exploration. If you are about to run gre
|
|
|
32509
33143
|
const blockToCompletion = backgroundDisabled || requestedWait || workerForcedForeground;
|
|
32510
33144
|
const effectiveBackground = !blockToCompletion && (rawRequestedBackground || requestedPty);
|
|
32511
33145
|
const isWorker = isPiWorkerSession(extCtx);
|
|
32512
|
-
const workerWaitMaxMs =
|
|
33146
|
+
const workerWaitMaxMs = bashCfg.worker_wait_max_ms;
|
|
32513
33147
|
let spawnContext = {
|
|
32514
33148
|
command: params.command,
|
|
32515
33149
|
cwd: params.workdir
|
|
@@ -33064,7 +33698,10 @@ async function formatBashStatus(extCtx, taskId, details, requestedOutputMode, wa
|
|
|
33064
33698
|
if (details.live_descendants_summary) {
|
|
33065
33699
|
text += ` · ${details.live_descendants_summary}`;
|
|
33066
33700
|
}
|
|
33067
|
-
if (details.
|
|
33701
|
+
if (details.remote_phase_note && !details.waited) {
|
|
33702
|
+
text += ` · ${details.remote_phase_note}`;
|
|
33703
|
+
}
|
|
33704
|
+
if (details.output_incomplete || details.status === "failed" && details.status_reason) {
|
|
33068
33705
|
text += `
|
|
33069
33706
|
[${details.status_reason || "PTY output may be incomplete"}]`;
|
|
33070
33707
|
}
|
|
@@ -33570,10 +34207,16 @@ import { Type as Type9 } from "typebox";
|
|
|
33570
34207
|
var INSPECT_TRANSPORT_HEADROOM_MS = 30000;
|
|
33571
34208
|
var InspectParams = Type9.Object({
|
|
33572
34209
|
sections: Type9.Optional(Type9.Union([Type9.String(), Type9.Array(Type9.String())], {
|
|
33573
|
-
description: "Categories to include in detailed drill-down (e.g. 'todos' or ['todos', 'dead_code', 'cycles']). Use 'all' for every active category. Omit for summary-only mode.
|
|
34210
|
+
description: "Categories to include in detailed drill-down (e.g. 'todos' or ['todos', 'dead_code', 'cycles']). Use 'all' for every active category. Omit for summary-only mode. With scope, listing sections without 'diagnostics' (or 'all') skips diagnostics work; omitting sections keeps diagnostics. Other categories are verified regardless of sections."
|
|
33574
34211
|
})),
|
|
33575
34212
|
scope: Type9.Optional(Type9.Union([Type9.String(), Type9.Array(Type9.String())], {
|
|
33576
|
-
description: "Restrict returned results to paths under this scope (one path, or an array of paths — not a space-separated list; file or directory; absolute or relative to project root). `scope=` narrows results
|
|
34213
|
+
description: "Restrict returned results to paths under this scope (one path, or an array of paths — not a space-separated list; file or directory; absolute or relative to project root). `scope=` narrows results. Scoped requests collect diagnostics for the scoped files and report coverage gaps, unless sections lists other categories and leaves out 'diagnostics'."
|
|
34214
|
+
})),
|
|
34215
|
+
offset: Type9.Optional(Type9.Integer({
|
|
34216
|
+
minimum: 0,
|
|
34217
|
+
maximum: Number.MAX_SAFE_INTEGER,
|
|
34218
|
+
default: 0,
|
|
34219
|
+
description: "Rows to skip independently in each drill-down list. Default 0; follow next_offset in each list envelope."
|
|
33577
34220
|
})),
|
|
33578
34221
|
topK: Type9.Optional(Type9.Integer({
|
|
33579
34222
|
minimum: 1,
|
|
@@ -33885,7 +34528,7 @@ function registerInspectTool(pi, ctx) {
|
|
|
33885
34528
|
pi.registerTool({
|
|
33886
34529
|
name: "aft_inspect",
|
|
33887
34530
|
label: "inspect",
|
|
33888
|
-
description: "Codebase health inspection that waits for current analysis. FRESH means the reported analysis is current. PARTIAL means some diagnostics are unknown; the header names the analyzer, reason, and retry guidance. INTERRUPTED means the request stopped without a fresh snapshot; retry aft_inspect. PHASE-FAILED means inspection could not finish; address the reported reason and retry, or narrow the scope. `sections` selects drill-down detail
|
|
34531
|
+
description: "Codebase health inspection that waits for current analysis. FRESH means the reported analysis is current. PARTIAL means some diagnostics are unknown; the header names the analyzer, reason, and retry guidance. INTERRUPTED means the request stopped without a fresh snapshot; retry aft_inspect. PHASE-FAILED means inspection could not finish; address the reported reason and retry, or narrow the scope. `sections` selects drill-down detail. Scoped requests collect diagnostics when sections is omitted or includes 'diagnostics' or 'all'; sections that leave diagnostics out skip diagnostics work. Unscoped requests keep warm diagnostics. Categories switched off in `inspect.categories` are not computed or refreshed, render as off, and never make the header PARTIAL.\n\n" + "Use `scope=` to narrow all findings, counts, and examples to those paths. Cross-boundary duplicates are labeled as groups touching the scope. Scope also limits Rust analyzer startup to the Cargo workspaces owning those paths when diagnostics are requested. Files without an authoritative diagnostic report remain named gaps (complete: false), not a clean result.\n\n" + `Use when: starting work on unfamiliar code, after multi-edit batches to check diagnostics, before a refactor, before review, or to verify cleanup completeness.
|
|
33889
34532
|
|
|
33890
34533
|
` + "Treat `dead_code` as a hint, not proof: reachability is call-based, so symbols reached only via method dispatch or referenced only in type position may be false positives — verify before deleting.\n\n" + "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.",
|
|
33891
34534
|
parameters: InspectParams,
|
|
@@ -33901,6 +34544,8 @@ function registerInspectTool(pi, ctx) {
|
|
|
33901
34544
|
rawArgs.scope = scope;
|
|
33902
34545
|
if (topK !== undefined)
|
|
33903
34546
|
rawArgs.topK = topK;
|
|
34547
|
+
if (params.offset !== undefined)
|
|
34548
|
+
rawArgs.offset = params.offset;
|
|
33904
34549
|
const response = await callToolCall(bridge, "inspect", rawArgs, extCtx, {
|
|
33905
34550
|
transportTimeoutMs: resolveInspectDiagnosticsTimeoutMs(ctx.config) + INSPECT_TRANSPORT_HEADROOM_MS
|
|
33906
34551
|
});
|
|
@@ -35115,6 +35760,7 @@ function startPiLiveConfigReload(options) {
|
|
|
35115
35760
|
const { userConfigPath, projectConfigPath } = resolveCortexKitConfigPaths(options.directory);
|
|
35116
35761
|
return startLiveConfigReload({
|
|
35117
35762
|
paths: [userConfigPath, projectConfigPath],
|
|
35763
|
+
projectPath: projectConfigPath,
|
|
35118
35764
|
load: () => loadPiConfigForLiveReload(options.directory),
|
|
35119
35765
|
initialSources: options.initialSources,
|
|
35120
35766
|
initialProjectText: options.initialSourceTexts?.[projectConfigPath] ?? null,
|
|
@@ -35411,11 +36057,11 @@ var PLUGIN_VERSION = (() => {
|
|
|
35411
36057
|
return "0.0.0";
|
|
35412
36058
|
}
|
|
35413
36059
|
})();
|
|
35414
|
-
var ANNOUNCEMENT_VERSION = "0.
|
|
36060
|
+
var ANNOUNCEMENT_VERSION = "0.60.0";
|
|
35415
36061
|
var ANNOUNCEMENT_FEATURES = [
|
|
35416
|
-
"
|
|
35417
|
-
"
|
|
35418
|
-
"
|
|
36062
|
+
"Call graphs stay current cheaply: a one-file edit updates a project's graph in seconds instead of minutes, and linked worktrees reuse their main checkout's graph until they change.",
|
|
36063
|
+
"`aft_inspect` dead-code and unused-export results are more accurate on Rust, Go and TypeScript, and inspect uses far less memory on large projects.",
|
|
36064
|
+
"OpenCode 2 gets native `shell` and `patch` tools, and stopped subagents are no longer woken by their own background commands."
|
|
35419
36065
|
];
|
|
35420
36066
|
var ANNOUNCEMENT_FOOTER = "Join us on Discord: https://discord.gg/DSa65w8wuf";
|
|
35421
36067
|
var pendingEagerWarnings = new Map;
|