@cortexkit/aft 0.45.1 → 0.47.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/index.js CHANGED
@@ -1895,6 +1895,21 @@ function isEmptyParam(value) {
1895
1895
  return false;
1896
1896
  }
1897
1897
 
1898
+ // ../aft-bridge/dist/config-keys.js
1899
+ function stripHarnessSpecificConfigKeys(value, keys) {
1900
+ if (!value || typeof value !== "object" || Array.isArray(value))
1901
+ return value;
1902
+ const stripped = { ...value };
1903
+ for (const key of keys)
1904
+ delete stripped[key];
1905
+ return stripped;
1906
+ }
1907
+ var OPENCODE_ONLY_KEYS, PI_ONLY_KEYS;
1908
+ var init_config_keys = __esm(() => {
1909
+ OPENCODE_ONLY_KEYS = ["hoist_builtin_tools", "auto_update"];
1910
+ PI_ONLY_KEYS = [];
1911
+ });
1912
+
1898
1913
  // ../aft-bridge/dist/config-tiers.js
1899
1914
  import { existsSync, readFileSync as readFileSync2 } from "node:fs";
1900
1915
  import { resolve as resolve2 } from "node:path";
@@ -2223,6 +2238,120 @@ var init_downloader = __esm(() => {
2223
2238
  DOWNLOAD_LOCK_STALE_MS = 10 * 60000;
2224
2239
  });
2225
2240
 
2241
+ // ../aft-bridge/dist/durable-log.js
2242
+ import { appendFile, mkdir, rename, rm, stat } from "node:fs/promises";
2243
+ import { homedir as homedir5 } from "node:os";
2244
+ import { dirname, join as join4 } from "node:path";
2245
+ function homeDir() {
2246
+ if (process.platform === "win32")
2247
+ return process.env.USERPROFILE || process.env.HOME || homedir5();
2248
+ return process.env.HOME || homedir5();
2249
+ }
2250
+ function dataHome() {
2251
+ if (process.env.XDG_DATA_HOME)
2252
+ return process.env.XDG_DATA_HOME;
2253
+ if (process.platform === "win32") {
2254
+ return process.env.LOCALAPPDATA || process.env.APPDATA || join4(homeDir(), "AppData", "Local");
2255
+ }
2256
+ return join4(homeDir(), ".local", "share");
2257
+ }
2258
+ function resolveAftStorageRoot(configuredRoot) {
2259
+ if (configuredRoot)
2260
+ return configuredRoot;
2261
+ if (process.env.AFT_CACHE_DIR)
2262
+ return join4(process.env.AFT_CACHE_DIR, "aft");
2263
+ return join4(dataHome(), "cortexkit", "aft");
2264
+ }
2265
+ function resolveAftLogPath(filename, configuredRoot) {
2266
+ return join4(resolveAftStorageRoot(configuredRoot), "logs", filename);
2267
+ }
2268
+
2269
+ class RotatingLogSink {
2270
+ path;
2271
+ maxBytes;
2272
+ generations;
2273
+ estimatedBytes = null;
2274
+ queue = Promise.resolve();
2275
+ disabled = false;
2276
+ failureReported = false;
2277
+ constructor(path2, options = {}) {
2278
+ this.path = path2;
2279
+ this.maxBytes = options.maxBytes ?? DEFAULT_LOG_BYTES;
2280
+ this.generations = options.generations ?? DEFAULT_LOG_GENERATIONS;
2281
+ }
2282
+ append(data) {
2283
+ if (this.disabled || data.length === 0)
2284
+ return;
2285
+ this.queue = this.queue.then(() => this.write(data)).catch((error2) => {
2286
+ this.disabled = true;
2287
+ if (!this.failureReported) {
2288
+ this.failureReported = true;
2289
+ try {
2290
+ process.stderr.write(`[aft-plugin] durable log disabled for ${this.path}: ${error2 instanceof Error ? error2.message : String(error2)}
2291
+ `);
2292
+ } catch {}
2293
+ }
2294
+ });
2295
+ }
2296
+ async drain() {
2297
+ await this.queue;
2298
+ }
2299
+ async write(data) {
2300
+ await mkdir(dirname(this.path), { recursive: true });
2301
+ if (this.estimatedBytes === null) {
2302
+ try {
2303
+ this.estimatedBytes = (await stat(this.path)).size;
2304
+ } catch (error2) {
2305
+ if (!isMissing(error2))
2306
+ throw error2;
2307
+ this.estimatedBytes = 0;
2308
+ }
2309
+ }
2310
+ const bytes = Buffer.byteLength(data);
2311
+ if (this.estimatedBytes > 0 && this.estimatedBytes + bytes > this.maxBytes) {
2312
+ await this.rotate();
2313
+ }
2314
+ await appendFile(this.path, data, "utf8");
2315
+ this.estimatedBytes = (this.estimatedBytes ?? 0) + bytes;
2316
+ }
2317
+ async rotate() {
2318
+ if (this.generations <= 0) {
2319
+ await removeIfPresent(this.path);
2320
+ this.estimatedBytes = 0;
2321
+ return;
2322
+ }
2323
+ await removeIfPresent(`${this.path}.${this.generations}`);
2324
+ for (let generation = this.generations - 1;generation >= 1; generation -= 1) {
2325
+ await renameIfPresent(`${this.path}.${generation}`, `${this.path}.${generation + 1}`);
2326
+ }
2327
+ await renameIfPresent(this.path, `${this.path}.1`);
2328
+ this.estimatedBytes = 0;
2329
+ }
2330
+ }
2331
+ function isMissing(error2) {
2332
+ return typeof error2 === "object" && error2 !== null && "code" in error2 && error2.code === "ENOENT";
2333
+ }
2334
+ async function removeIfPresent(path2) {
2335
+ try {
2336
+ await rm(path2, { force: true });
2337
+ } catch (error2) {
2338
+ if (!isMissing(error2))
2339
+ throw error2;
2340
+ }
2341
+ }
2342
+ async function renameIfPresent(from, to) {
2343
+ try {
2344
+ await rename(from, to);
2345
+ } catch (error2) {
2346
+ if (!isMissing(error2))
2347
+ throw error2;
2348
+ }
2349
+ }
2350
+ var DEFAULT_LOG_BYTES, DEFAULT_LOG_GENERATIONS = 5;
2351
+ var init_durable_log = __esm(() => {
2352
+ DEFAULT_LOG_BYTES = 20 * 1024 * 1024;
2353
+ });
2354
+
2226
2355
  // ../aft-bridge/dist/edit-summary.js
2227
2356
  function formatEditSummary(data) {
2228
2357
  if (data.rolled_back === true) {
@@ -2308,27 +2437,27 @@ function stripJsoncSymbols(value) {
2308
2437
 
2309
2438
  // ../aft-bridge/dist/paths.js
2310
2439
  import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync4, renameSync as renameSync2, writeFileSync } from "node:fs";
2311
- import { homedir as homedir5 } from "node:os";
2312
- import { dirname, isAbsolute as isAbsolute2, join as join4, resolve as resolve3 } from "node:path";
2313
- function homeDir() {
2440
+ import { homedir as homedir6 } from "node:os";
2441
+ import { dirname as dirname2, isAbsolute as isAbsolute2, join as join5, resolve as resolve3 } from "node:path";
2442
+ function homeDir2() {
2314
2443
  if (process.platform === "win32")
2315
- return process.env.USERPROFILE || process.env.HOME || homedir5();
2316
- return process.env.HOME || homedir5();
2444
+ return process.env.USERPROFILE || process.env.HOME || homedir6();
2445
+ return process.env.HOME || homedir6();
2317
2446
  }
2318
2447
  function configHome() {
2319
2448
  const xdg = process.env.XDG_CONFIG_HOME;
2320
2449
  if (xdg && isAbsolute2(xdg))
2321
2450
  return xdg;
2322
- return join4(homeDir(), ".config");
2451
+ return join5(homeDir2(), ".config");
2323
2452
  }
2324
2453
  function legacyOpenCodeConfigDir() {
2325
2454
  const envDir = process.env.OPENCODE_CONFIG_DIR?.trim();
2326
2455
  if (envDir)
2327
2456
  return resolve3(envDir);
2328
- return join4(configHome(), "opencode");
2457
+ return join5(configHome(), "opencode");
2329
2458
  }
2330
2459
  function legacyPiAgentDir() {
2331
- return join4(homeDir(), ".pi", "agent");
2460
+ return join5(homeDir2(), ".pi", "agent");
2332
2461
  }
2333
2462
  function legacySources(basePath, label, harness) {
2334
2463
  return [
@@ -2337,10 +2466,10 @@ function legacySources(basePath, label, harness) {
2337
2466
  ];
2338
2467
  }
2339
2468
  function resolveCortexKitUserConfigPath() {
2340
- return join4(configHome(), "cortexkit", "aft.jsonc");
2469
+ return join5(configHome(), "cortexkit", "aft.jsonc");
2341
2470
  }
2342
2471
  function resolveCortexKitProjectConfigPath(projectDirectory) {
2343
- return join4(projectDirectory, ".cortexkit", "aft.jsonc");
2472
+ return join5(projectDirectory, ".cortexkit", "aft.jsonc");
2344
2473
  }
2345
2474
  function resolveCortexKitConfigPaths(projectDirectory) {
2346
2475
  return {
@@ -2351,25 +2480,25 @@ function resolveCortexKitConfigPaths(projectDirectory) {
2351
2480
  function resolveLegacyAftConfigSources(projectDirectory) {
2352
2481
  return {
2353
2482
  user: [
2354
- ...legacySources(join4(legacyOpenCodeConfigDir(), "aft"), "OpenCode user", "opencode"),
2355
- ...legacySources(join4(legacyPiAgentDir(), "aft"), "Pi user", "pi")
2483
+ ...legacySources(join5(legacyOpenCodeConfigDir(), "aft"), "OpenCode user", "opencode"),
2484
+ ...legacySources(join5(legacyPiAgentDir(), "aft"), "Pi user", "pi")
2356
2485
  ],
2357
2486
  project: [
2358
- ...legacySources(join4(projectDirectory, ".opencode", "aft"), "OpenCode project", "opencode"),
2359
- ...legacySources(join4(projectDirectory, ".pi", "aft"), "Pi project", "pi")
2487
+ ...legacySources(join5(projectDirectory, ".opencode", "aft"), "OpenCode project", "opencode"),
2488
+ ...legacySources(join5(projectDirectory, ".pi", "aft"), "Pi project", "pi")
2360
2489
  ]
2361
2490
  };
2362
2491
  }
2363
2492
  function resolveHarnessStoragePath(storageRoot, harness, ...segments) {
2364
- return join4(storageRoot, harness, ...segments);
2493
+ return join5(storageRoot, harness, ...segments);
2365
2494
  }
2366
2495
  function repairRootScopedStorageFile(storageRoot, harness, fileName) {
2367
2496
  const harnessPath = resolveHarnessStoragePath(storageRoot, harness, fileName);
2368
- const rootPath = join4(storageRoot, fileName);
2497
+ const rootPath = join5(storageRoot, fileName);
2369
2498
  if (existsSync3(harnessPath) || !existsSync3(rootPath))
2370
2499
  return harnessPath;
2371
2500
  try {
2372
- mkdirSync2(dirname(harnessPath), { recursive: true });
2501
+ mkdirSync2(dirname2(harnessPath), { recursive: true });
2373
2502
  renameSync2(rootPath, harnessPath);
2374
2503
  } catch {}
2375
2504
  return harnessPath;
@@ -2390,7 +2519,7 @@ function shouldShowAnnouncement(storageRoot, harness, currentVersion) {
2390
2519
  return false;
2391
2520
  if (!lastVersion) {
2392
2521
  try {
2393
- mkdirSync2(dirname(versionFile), { recursive: true });
2522
+ mkdirSync2(dirname2(versionFile), { recursive: true });
2394
2523
  writeFileSync(versionFile, currentVersion);
2395
2524
  } catch {}
2396
2525
  return false;
@@ -2402,7 +2531,7 @@ function markAnnouncementSeen(storageRoot, harness, currentVersion) {
2402
2531
  return;
2403
2532
  const versionFile = repairRootScopedStorageFile(storageRoot, harness, "last_announced_version");
2404
2533
  try {
2405
- mkdirSync2(dirname(versionFile), { recursive: true });
2534
+ mkdirSync2(dirname2(versionFile), { recursive: true });
2406
2535
  writeFileSync(versionFile, currentVersion);
2407
2536
  } catch {}
2408
2537
  }
@@ -2412,8 +2541,8 @@ var init_paths = () => {};
2412
2541
  import { execSync } from "node:child_process";
2413
2542
  import { chmodSync as chmodSync2, closeSync as closeSync2, copyFileSync as copyFileSync2, existsSync as existsSync4, mkdirSync as mkdirSync3, openSync as openSync2, readSync, renameSync as renameSync3, unlinkSync as unlinkSync2 } from "node:fs";
2414
2543
  import { createRequire } from "node:module";
2415
- import { homedir as homedir6 } from "node:os";
2416
- import { join as join5 } from "node:path";
2544
+ import { homedir as homedir7 } from "node:os";
2545
+ import { join as join6 } from "node:path";
2417
2546
  function copyToVersionedCache(npmBinaryPath, knownVersion) {
2418
2547
  try {
2419
2548
  const version = knownVersion ?? readBinaryVersion(npmBinaryPath);
@@ -2421,9 +2550,9 @@ function copyToVersionedCache(npmBinaryPath, knownVersion) {
2421
2550
  return null;
2422
2551
  const tag = version.startsWith("v") ? version : `v${version}`;
2423
2552
  const cacheDir = getCacheDir();
2424
- const versionedDir = join5(cacheDir, tag);
2553
+ const versionedDir = join6(cacheDir, tag);
2425
2554
  const ext = process.platform === "win32" ? ".exe" : "";
2426
- const cachedPath = join5(versionedDir, `aft${ext}`);
2555
+ const cachedPath = join6(versionedDir, `aft${ext}`);
2427
2556
  if (existsSync4(cachedPath)) {
2428
2557
  const cachedVersion = readBinaryVersion(cachedPath);
2429
2558
  if (cachedVersion === version)
@@ -2453,18 +2582,18 @@ function normalizeBareVersion(version) {
2453
2582
  return version.startsWith("v") ? version.slice(1) : version;
2454
2583
  }
2455
2584
  function homeDirFromEnv(env) {
2456
- return (process.platform === "win32" ? env.USERPROFILE || env.HOME : env.HOME) || homedir6();
2585
+ return (process.platform === "win32" ? env.USERPROFILE || env.HOME : env.HOME) || homedir7();
2457
2586
  }
2458
2587
  function cacheDirFromEnv(env) {
2459
2588
  if (process.platform === "win32") {
2460
- const base2 = env.LOCALAPPDATA || env.APPDATA || join5(homeDirFromEnv(env), "AppData", "Local");
2461
- return join5(base2, "aft", "bin");
2589
+ const base2 = env.LOCALAPPDATA || env.APPDATA || join6(homeDirFromEnv(env), "AppData", "Local");
2590
+ return join6(base2, "aft", "bin");
2462
2591
  }
2463
- const base = env.XDG_CACHE_HOME || join5(homeDirFromEnv(env), ".cache");
2464
- return join5(base, "aft", "bin");
2592
+ const base = env.XDG_CACHE_HOME || join6(homeDirFromEnv(env), ".cache");
2593
+ return join6(base, "aft", "bin");
2465
2594
  }
2466
2595
  function cachedBinaryPathFromEnv(version, env, ext) {
2467
- const binaryPath = join5(cacheDirFromEnv(env), version, `aft${ext}`);
2596
+ const binaryPath = join6(cacheDirFromEnv(env), version, `aft${ext}`);
2468
2597
  return existsSync4(binaryPath) ? binaryPath : null;
2469
2598
  }
2470
2599
  function isExpectedCachedBinary2(binaryPath, expectedVersion) {
@@ -2583,7 +2712,7 @@ function findBinarySync(expectedVersion) {
2583
2712
  return usable;
2584
2713
  }
2585
2714
  } catch {}
2586
- const cargoPath = join5(homeDirFromEnv(env), ".cargo", "bin", `aft${ext}`);
2715
+ const cargoPath = join6(homeDirFromEnv(env), ".cargo", "bin", `aft${ext}`);
2587
2716
  if (existsSync4(cargoPath)) {
2588
2717
  const usable = probeBinaryCandidate(cargoPath, "cargo", expectedVersion);
2589
2718
  if (usable)
@@ -2631,28 +2760,28 @@ var init_resolver = __esm(() => {
2631
2760
  // ../aft-bridge/dist/migration.js
2632
2761
  import { spawnSync as spawnSync2 } from "node:child_process";
2633
2762
  import { closeSync as closeSync3, existsSync as existsSync5, mkdirSync as mkdirSync4, openSync as openSync3, readFileSync as readFileSync5, renameSync as renameSync4, rmSync as rmSync2, statSync as statSync2, unlinkSync as unlinkSync3, writeFileSync as writeFileSync2 } from "node:fs";
2634
- import { homedir as homedir7, tmpdir } from "node:os";
2635
- import { basename, dirname as dirname2, join as join6, resolve as resolve4 } from "node:path";
2636
- function dataHome() {
2763
+ import { homedir as homedir8, tmpdir } from "node:os";
2764
+ import { basename, dirname as dirname3, join as join7, resolve as resolve4 } from "node:path";
2765
+ function dataHome2() {
2637
2766
  if (process.env.XDG_DATA_HOME)
2638
2767
  return process.env.XDG_DATA_HOME;
2639
2768
  if (process.platform === "win32") {
2640
- return process.env.LOCALAPPDATA || process.env.APPDATA || join6(homeDir2(), "AppData", "Local");
2769
+ return process.env.LOCALAPPDATA || process.env.APPDATA || join7(homeDir3(), "AppData", "Local");
2641
2770
  }
2642
- return join6(homeDir2(), ".local", "share");
2771
+ return join7(homeDir3(), ".local", "share");
2643
2772
  }
2644
- function homeDir2() {
2773
+ function homeDir3() {
2645
2774
  if (process.platform === "win32")
2646
- return process.env.USERPROFILE || process.env.HOME || homedir7();
2647
- return process.env.HOME || homedir7();
2775
+ return process.env.USERPROFILE || process.env.HOME || homedir8();
2776
+ return process.env.HOME || homedir8();
2648
2777
  }
2649
2778
  function resolveLegacyStorageRoot(harness) {
2650
2779
  if (harness === "pi")
2651
- return join6(homeDir2(), ".pi", "agent", "aft");
2652
- return join6(dataHome(), "opencode", "storage", "plugin", "aft");
2780
+ return join7(homeDir3(), ".pi", "agent", "aft");
2781
+ return join7(dataHome2(), "opencode", "storage", "plugin", "aft");
2653
2782
  }
2654
2783
  function resolveCortexKitStorageRoot() {
2655
- return join6(dataHome(), "cortexkit", "aft");
2784
+ return join7(dataHome2(), "cortexkit", "aft");
2656
2785
  }
2657
2786
  function stripJsoncForParse(input) {
2658
2787
  let out = "";
@@ -2781,8 +2910,8 @@ function acquireConfigMigrationLock(lockDir) {
2781
2910
  }
2782
2911
  }
2783
2912
  function atomicCopyConfigFile(sourcePath, targetPath) {
2784
- mkdirSync4(dirname2(targetPath), { recursive: true });
2785
- const tmpPath = join6(dirname2(targetPath), `.${basename(targetPath)}.${process.pid}.${Date.now()}.${Math.random().toString(16).slice(2)}.tmp`);
2913
+ mkdirSync4(dirname3(targetPath), { recursive: true });
2914
+ const tmpPath = join7(dirname3(targetPath), `.${basename(targetPath)}.${process.pid}.${Date.now()}.${Math.random().toString(16).slice(2)}.tmp`);
2786
2915
  let fd = null;
2787
2916
  try {
2788
2917
  fd = openSync3(tmpPath, "wx", 384);
@@ -2803,8 +2932,8 @@ function atomicCopyConfigFile(sourcePath, targetPath) {
2803
2932
  }
2804
2933
  }
2805
2934
  function atomicWriteConfigFile(targetPath, content) {
2806
- mkdirSync4(dirname2(targetPath), { recursive: true });
2807
- const tmpPath = join6(dirname2(targetPath), `.${basename(targetPath)}.${process.pid}.${Date.now()}.${Math.random().toString(16).slice(2)}.tmp`);
2935
+ mkdirSync4(dirname3(targetPath), { recursive: true });
2936
+ const tmpPath = join7(dirname3(targetPath), `.${basename(targetPath)}.${process.pid}.${Date.now()}.${Math.random().toString(16).slice(2)}.tmp`);
2808
2937
  let fd = null;
2809
2938
  try {
2810
2939
  fd = openSync3(tmpPath, "wx", 384);
@@ -2927,7 +3056,7 @@ function migrateAftConfigFile(opts) {
2927
3056
  if (existingSources.length === 0) {
2928
3057
  return { migrated: false, conflict: false, targetPath: opts.targetPath, warnings };
2929
3058
  }
2930
- mkdirSync4(dirname2(opts.targetPath), { recursive: true });
3059
+ mkdirSync4(dirname3(opts.targetPath), { recursive: true });
2931
3060
  const release = acquireConfigMigrationLock(`${opts.targetPath}.lock`);
2932
3061
  try {
2933
3062
  const sources = existingSources.map((source) => ({
@@ -2984,13 +3113,13 @@ function spawnErrorLabel(error2) {
2984
3113
  return [code, error2.message].filter(Boolean).join(": ");
2985
3114
  }
2986
3115
  function migrationLogPath(newRoot, harness, logger) {
2987
- const desired = join6(newRoot, "logs", "migration", `${harness}-${Date.now()}.jsonl`);
3116
+ const desired = join7(newRoot, "logs", "migration", `${harness}-${Date.now()}.jsonl`);
2988
3117
  try {
2989
- mkdirSync4(dirname2(desired), { recursive: true });
3118
+ mkdirSync4(dirname3(desired), { recursive: true });
2990
3119
  return desired;
2991
3120
  } catch (err) {
2992
- const fallback = join6(tmpdir(), `aft-migration-${harness}-${Date.now()}.jsonl`);
2993
- logger?.warn?.(`Failed to create AFT migration log directory ${dirname2(desired)}: ${err instanceof Error ? err.message : String(err)}. ` + `Using fallback log path ${fallback}.`);
3121
+ const fallback = join7(tmpdir(), `aft-migration-${harness}-${Date.now()}.jsonl`);
3122
+ logger?.warn?.(`Failed to create AFT migration log directory ${dirname3(desired)}: ${err instanceof Error ? err.message : String(err)}. ` + `Using fallback log path ${fallback}.`);
2994
3123
  return fallback;
2995
3124
  }
2996
3125
  }
@@ -3091,13 +3220,13 @@ var init_migration = __esm(() => {
3091
3220
  // ../aft-bridge/dist/npm-resolver.js
3092
3221
  import { execFileSync } from "node:child_process";
3093
3222
  import { readdirSync, statSync as statSync3 } from "node:fs";
3094
- import { homedir as homedir8 } from "node:os";
3095
- import { delimiter, dirname as dirname3, isAbsolute as isAbsolute3, join as join7 } from "node:path";
3223
+ import { homedir as homedir9 } from "node:os";
3224
+ import { delimiter, dirname as dirname4, isAbsolute as isAbsolute3, join as join8 } from "node:path";
3096
3225
  function defaultDeps() {
3097
3226
  return {
3098
3227
  platform: process.platform,
3099
3228
  env: process.env,
3100
- home: homedir8(),
3229
+ home: homedir9(),
3101
3230
  execPath: process.execPath
3102
3231
  };
3103
3232
  }
@@ -3118,14 +3247,14 @@ function npmFromPath(deps) {
3118
3247
  const dir = entry.trim().replace(/^"|"$/g, "");
3119
3248
  if (!dir || !isAbsolute3(dir))
3120
3249
  continue;
3121
- if (isFile(join7(dir, name)))
3250
+ if (isFile(join8(dir, name)))
3122
3251
  return dir;
3123
3252
  }
3124
3253
  return null;
3125
3254
  }
3126
3255
  function npmAdjacentToNode(deps) {
3127
- const dir = dirname3(deps.execPath);
3128
- return isFile(join7(dir, npmBinaryName(deps.platform))) ? dir : null;
3256
+ const dir = dirname4(deps.execPath);
3257
+ return isFile(join8(dir, npmBinaryName(deps.platform))) ? dir : null;
3129
3258
  }
3130
3259
  function highestVersionedNodeBin(installsDir, name) {
3131
3260
  let entries;
@@ -3134,8 +3263,8 @@ function highestVersionedNodeBin(installsDir, name) {
3134
3263
  } catch {
3135
3264
  return null;
3136
3265
  }
3137
- const candidates = entries.filter((v) => isFile(join7(installsDir, v, "bin", name))).sort((a, b) => compareVersionsDesc(a, b));
3138
- return candidates.length > 0 ? join7(installsDir, candidates[0], "bin") : null;
3266
+ const candidates = entries.filter((v) => isFile(join8(installsDir, v, "bin", name))).sort((a, b) => compareVersionsDesc(a, b));
3267
+ return candidates.length > 0 ? join8(installsDir, candidates[0], "bin") : null;
3139
3268
  }
3140
3269
  function compareVersionsDesc(a, b) {
3141
3270
  const pa = a.replace(/^v/, "").split(".").map((n) => Number.parseInt(n, 10));
@@ -3160,22 +3289,22 @@ function wellKnownNpmDirs(deps) {
3160
3289
  const programFiles = env.ProgramFiles || "C:\\Program Files";
3161
3290
  const appData = env.APPDATA;
3162
3291
  const localAppData = env.LOCALAPPDATA;
3163
- push(join7(programFiles, "nodejs"));
3292
+ push(join8(programFiles, "nodejs"));
3164
3293
  if (appData)
3165
- push(join7(appData, "npm"));
3294
+ push(join8(appData, "npm"));
3166
3295
  if (localAppData)
3167
- push(join7(localAppData, "Volta", "bin"));
3296
+ push(join8(localAppData, "Volta", "bin"));
3168
3297
  if (env.NVM_SYMLINK)
3169
3298
  push(env.NVM_SYMLINK);
3170
3299
  } else {
3171
3300
  if (env.NVM_BIN)
3172
3301
  push(env.NVM_BIN);
3173
- push(highestVersionedNodeBin(join7(home, ".nvm", "versions", "node"), name));
3174
- push(highestVersionedNodeBin(join7(home, ".local", "share", "mise", "installs", "node"), name));
3175
- push(highestVersionedNodeBin(join7(home, ".asdf", "installs", "nodejs"), name));
3176
- push(join7(home, ".volta", "bin"));
3177
- push(join7(home, ".asdf", "shims"));
3178
- const systemDirs = deps.systemNpmDirs ?? (platform === "darwin" ? ["/opt/homebrew/bin", "/usr/local/bin"] : ["/usr/local/bin", "/usr/bin", join7(home, ".local", "bin")]);
3302
+ push(highestVersionedNodeBin(join8(home, ".nvm", "versions", "node"), name));
3303
+ push(highestVersionedNodeBin(join8(home, ".local", "share", "mise", "installs", "node"), name));
3304
+ push(highestVersionedNodeBin(join8(home, ".asdf", "installs", "nodejs"), name));
3305
+ push(join8(home, ".volta", "bin"));
3306
+ push(join8(home, ".asdf", "shims"));
3307
+ const systemDirs = deps.systemNpmDirs ?? (platform === "darwin" ? ["/opt/homebrew/bin", "/usr/local/bin"] : ["/usr/local/bin", "/usr/bin", join8(home, ".local", "bin")]);
3179
3308
  for (const dir of systemDirs)
3180
3309
  push(dir);
3181
3310
  }
@@ -3185,12 +3314,12 @@ function resolveNpm(deps = defaultDeps()) {
3185
3314
  const name = npmBinaryName(deps.platform);
3186
3315
  const onPath = npmFromPath(deps);
3187
3316
  if (onPath)
3188
- return { command: join7(onPath, name), binDir: onPath };
3317
+ return { command: join8(onPath, name), binDir: onPath };
3189
3318
  const adjacent = npmAdjacentToNode(deps);
3190
3319
  if (adjacent)
3191
- return { command: join7(adjacent, name), binDir: adjacent };
3320
+ return { command: join8(adjacent, name), binDir: adjacent };
3192
3321
  for (const dir of wellKnownNpmDirs(deps)) {
3193
- const candidate = join7(dir, name);
3322
+ const candidate = join8(dir, name);
3194
3323
  if (isFile(candidate))
3195
3324
  return { command: candidate, binDir: dir };
3196
3325
  }
@@ -3226,7 +3355,7 @@ var init_npm_resolver = () => {};
3226
3355
  import { execFileSync as execFileSync2 } from "node:child_process";
3227
3356
  import { createHash as createHash3 } from "node:crypto";
3228
3357
  import { chmodSync as chmodSync3, closeSync as closeSync4, copyFileSync as copyFileSync3, createWriteStream as createWriteStream2, existsSync as existsSync6, lstatSync, mkdirSync as mkdirSync5, openSync as openSync4, readdirSync as readdirSync2, readFileSync as readFileSync6, readlinkSync, realpathSync, rmSync as rmSync3, statSync as statSync4, symlinkSync, unlinkSync as unlinkSync4, writeFileSync as writeFileSync3 } from "node:fs";
3229
- import { basename as basename2, dirname as dirname4, isAbsolute as isAbsolute4, join as join8, relative as relative2, resolve as resolve5, win32 } from "node:path";
3358
+ import { basename as basename2, dirname as dirname5, isAbsolute as isAbsolute4, join as join9, relative as relative2, resolve as resolve5, win32 } from "node:path";
3230
3359
  import { Readable as Readable2 } from "node:stream";
3231
3360
  import { pipeline as pipeline2 } from "node:stream/promises";
3232
3361
  function getPlatformInfo() {
@@ -3252,10 +3381,10 @@ function getManualInstallHint() {
3252
3381
  }
3253
3382
  async function ensureOnnxRuntime(storageDir) {
3254
3383
  const info = getPlatformInfo();
3255
- const ortVersionDir = join8(storageDir, "onnxruntime", ORT_VERSION);
3384
+ const ortVersionDir = join9(storageDir, "onnxruntime", ORT_VERSION);
3256
3385
  const libName = info?.libName ?? "libonnxruntime.dylib";
3257
3386
  const resolvedOrtDir = resolveCachedOnnxRuntimeDir(ortVersionDir, libName);
3258
- const libPath = join8(resolvedOrtDir, libName);
3387
+ const libPath = join9(resolvedOrtDir, libName);
3259
3388
  if (existsSync6(libPath)) {
3260
3389
  const meta = readOnnxInstalledMeta(ortVersionDir);
3261
3390
  if (meta?.sha256) {
@@ -3285,9 +3414,9 @@ async function ensureOnnxRuntime(storageDir) {
3285
3414
  warn(`ONNX Runtime auto-download not available for ${process.platform}/${process.arch}. Install manually: ${getManualInstallHint()}`);
3286
3415
  return null;
3287
3416
  }
3288
- const onnxBaseDir = join8(storageDir, "onnxruntime");
3417
+ const onnxBaseDir = join9(storageDir, "onnxruntime");
3289
3418
  mkdirSync5(onnxBaseDir, { recursive: true });
3290
- const lockPath = join8(onnxBaseDir, ONNX_LOCK_FILE);
3419
+ const lockPath = join9(onnxBaseDir, ONNX_LOCK_FILE);
3291
3420
  cleanupAbandonedStagingDirs(onnxBaseDir);
3292
3421
  if (!acquireLock(lockPath)) {
3293
3422
  warn(`ONNX Runtime install already in progress in another process (lock: ${lockPath}). Skipping.`);
@@ -3306,7 +3435,7 @@ function cleanupAbandonedStagingDirs(onnxBaseDir) {
3306
3435
  for (const entry of entries) {
3307
3436
  if (!entry.startsWith(`${ORT_VERSION}.tmp.`))
3308
3437
  continue;
3309
- const stagingDir = join8(onnxBaseDir, entry);
3438
+ const stagingDir = join9(onnxBaseDir, entry);
3310
3439
  const parts = entry.split(".");
3311
3440
  const pidStr = parts[parts.length - 2];
3312
3441
  const pid = pidStr ? Number.parseInt(pidStr, 10) : NaN;
@@ -3343,7 +3472,7 @@ function cleanupAbandonedStagingDirs(onnxBaseDir) {
3343
3472
  }
3344
3473
  function cleanupIncompleteTargetIfUnowned(ortDir) {
3345
3474
  try {
3346
- if (existsSync6(ortDir) && !existsSync6(join8(ortDir, ONNX_INSTALLED_META_FILE))) {
3475
+ if (existsSync6(ortDir) && !existsSync6(join9(ortDir, ONNX_INSTALLED_META_FILE))) {
3347
3476
  log(`[onnx] removing half-populated install dir ${ortDir} (no meta file)`);
3348
3477
  rmSync3(ortDir, { recursive: true, force: true });
3349
3478
  }
@@ -3388,7 +3517,7 @@ function detectOnnxVersion(libDir, libName) {
3388
3517
  if (version)
3389
3518
  return version;
3390
3519
  }
3391
- const base = join8(libDir, libName);
3520
+ const base = join9(libDir, libName);
3392
3521
  if (existsSync6(base)) {
3393
3522
  try {
3394
3523
  const real = realpathSync(base);
@@ -3427,6 +3556,17 @@ function pathEntriesForPlatform() {
3427
3556
  return isAbsolute4(entry) || win32.isAbsolute(entry);
3428
3557
  });
3429
3558
  }
3559
+ function isWindowsSystem32Directory(dir) {
3560
+ if (process.platform !== "win32")
3561
+ return false;
3562
+ const normalizedDir = win32.resolve(dir).replace(/[\\/]+$/, "").toLowerCase();
3563
+ const windowsRoots = [process.env.SystemRoot, process.env.windir, "C:\\Windows"];
3564
+ return windowsRoots.some((root) => {
3565
+ if (!root)
3566
+ return false;
3567
+ return normalizedDir === win32.resolve(root, "System32").replace(/[\\/]+$/, "").toLowerCase();
3568
+ });
3569
+ }
3430
3570
  function directoryContainsLibrary(dir, libName) {
3431
3571
  try {
3432
3572
  const entries = readdirSync2(dir);
@@ -3440,10 +3580,10 @@ function directoryContainsLibrary(dir, libName) {
3440
3580
  }
3441
3581
  }
3442
3582
  function resolveCachedOnnxRuntimeDir(ortVersionDir, libName) {
3443
- if (existsSync6(join8(ortVersionDir, libName)))
3583
+ if (existsSync6(join9(ortVersionDir, libName)))
3444
3584
  return ortVersionDir;
3445
- const libSubdir = join8(ortVersionDir, "lib");
3446
- if (existsSync6(join8(libSubdir, libName)))
3585
+ const libSubdir = join9(ortVersionDir, "lib");
3586
+ if (existsSync6(join9(libSubdir, libName)))
3447
3587
  return libSubdir;
3448
3588
  return ortVersionDir;
3449
3589
  }
@@ -3458,12 +3598,12 @@ function findSystemOnnxRuntime(libName) {
3458
3598
  } else if (process.platform === "win32") {
3459
3599
  const programFiles = process.env.ProgramFiles ?? "C:\\Program Files";
3460
3600
  const programFilesX86 = process.env["ProgramFiles(x86)"] ?? "C:\\Program Files (x86)";
3461
- searchPaths.push(join8(programFiles, "onnxruntime", "lib"), join8(programFiles, "Microsoft ONNX Runtime", "lib"), join8(programFiles, "Microsoft Machine Learning", "lib"), join8(programFilesX86, "onnxruntime", "lib"), ...(() => {
3601
+ searchPaths.push(join9(programFiles, "onnxruntime", "lib"), join9(programFiles, "Microsoft ONNX Runtime", "lib"), join9(programFiles, "Microsoft Machine Learning", "lib"), join9(programFilesX86, "onnxruntime", "lib"), ...(() => {
3462
3602
  const nugetPaths = [];
3463
3603
  const userProfile = process.env.USERPROFILE ?? "";
3464
3604
  if (!userProfile)
3465
3605
  return nugetPaths;
3466
- const nugetPackageDir = join8(userProfile, ".nuget", "packages", "microsoft.ml.onnxruntime");
3606
+ const nugetPackageDir = join9(userProfile, ".nuget", "packages", "microsoft.ml.onnxruntime");
3467
3607
  if (!existsSync6(nugetPackageDir))
3468
3608
  return nugetPaths;
3469
3609
  try {
@@ -3472,7 +3612,7 @@ function findSystemOnnxRuntime(libName) {
3472
3612
  continue;
3473
3613
  if (entry.name === "__globalPackagesFolder" || entry.name.startsWith("."))
3474
3614
  continue;
3475
- nugetPaths.push(join8(nugetPackageDir, entry.name, "runtimes", "win-x64", "native"), join8(nugetPackageDir, entry.name, "runtimes", "win-arm64", "native"));
3615
+ nugetPaths.push(join9(nugetPackageDir, entry.name, "runtimes", "win-x64", "native"), join9(nugetPackageDir, entry.name, "runtimes", "win-arm64", "native"));
3476
3616
  }
3477
3617
  } catch (err) {
3478
3618
  warn(`Failed to scan NuGet ONNX Runtime cache ${nugetPackageDir}: ${err instanceof Error ? err.message : String(err)}`);
@@ -3494,7 +3634,7 @@ function findSystemOnnxRuntime(libName) {
3494
3634
  });
3495
3635
  const unknownVersionPaths = [];
3496
3636
  for (const dir of uniquePaths) {
3497
- const libPath = join8(dir, libName);
3637
+ const libPath = join9(dir, libName);
3498
3638
  if (process.platform === "win32") {
3499
3639
  if (!directoryContainsLibrary(dir, libName))
3500
3640
  continue;
@@ -3503,6 +3643,10 @@ function findSystemOnnxRuntime(libName) {
3503
3643
  }
3504
3644
  const version = detectOnnxVersion(dir, libName);
3505
3645
  if (!version) {
3646
+ if (isWindowsSystem32Directory(dir)) {
3647
+ warn(`Skipping unversioned Windows system ONNX Runtime at ${dir}; falling through to AFT-managed download.`);
3648
+ continue;
3649
+ }
3506
3650
  unknownVersionPaths.push(dir);
3507
3651
  continue;
3508
3652
  }
@@ -3530,7 +3674,7 @@ async function downloadFileWithCap(url, destPath) {
3530
3674
  if (Number.isFinite(advertised) && advertised > MAX_DOWNLOAD_BYTES2) {
3531
3675
  throw new Error(`Content-Length ${advertised} exceeds max ${MAX_DOWNLOAD_BYTES2}`);
3532
3676
  }
3533
- mkdirSync5(dirname4(destPath), { recursive: true });
3677
+ mkdirSync5(dirname5(destPath), { recursive: true });
3534
3678
  let bytesWritten = 0;
3535
3679
  const guard = new TransformStream({
3536
3680
  transform(chunk, transformController) {
@@ -3560,11 +3704,11 @@ function validateExtractedTree(stagingRoot) {
3560
3704
  const walk = (dir) => {
3561
3705
  const entries = readdirSync2(dir);
3562
3706
  for (const entry of entries) {
3563
- const fullPath = join8(dir, entry);
3707
+ const fullPath = join9(dir, entry);
3564
3708
  const lst = lstatSync(fullPath);
3565
3709
  if (lst.isSymbolicLink()) {
3566
3710
  const linkTarget = readlinkSync(fullPath);
3567
- const resolvedTarget = resolve5(dirname4(fullPath), linkTarget);
3711
+ const resolvedTarget = resolve5(dirname5(fullPath), linkTarget);
3568
3712
  const rel2 = relative2(realRoot, resolvedTarget);
3569
3713
  if (rel2.startsWith("..") || isAbsolute4(rel2) || win32.isAbsolute(rel2)) {
3570
3714
  throw new Error(`extracted symlink ${fullPath} points outside staging root: ${linkTarget}`);
@@ -3595,7 +3739,7 @@ async function downloadOnnxRuntime(info, targetDir) {
3595
3739
  const tmpDir = `${targetDir}.tmp.${process.pid}.${Date.now().toString(36)}`;
3596
3740
  try {
3597
3741
  mkdirSync5(tmpDir, { recursive: true });
3598
- const archivePath = join8(tmpDir, `onnxruntime.${info.archiveType}`);
3742
+ const archivePath = join9(tmpDir, `onnxruntime.${info.archiveType}`);
3599
3743
  await downloadFileWithCap(url, archivePath);
3600
3744
  const archiveSha256 = sha256File(archivePath);
3601
3745
  log(`ONNX Runtime archive sha256=${archiveSha256}`);
@@ -3611,7 +3755,7 @@ async function downloadOnnxRuntime(info, targetDir) {
3611
3755
  unlinkSync4(archivePath);
3612
3756
  } catch {}
3613
3757
  validateExtractedTree(tmpDir);
3614
- const extractedDir = join8(tmpDir, info.assetName, "lib");
3758
+ const extractedDir = join9(tmpDir, info.assetName, "lib");
3615
3759
  if (!existsSync6(extractedDir)) {
3616
3760
  throw new Error(`Expected directory not found: ${extractedDir}`);
3617
3761
  }
@@ -3620,11 +3764,11 @@ async function downloadOnnxRuntime(info, targetDir) {
3620
3764
  const realFiles = [];
3621
3765
  const symlinks = [];
3622
3766
  for (const libFile of libFiles) {
3623
- const src = join8(extractedDir, libFile);
3767
+ const src = join9(extractedDir, libFile);
3624
3768
  try {
3625
- const stat = lstatSync(src);
3626
- log(`ORT extract: ${libFile} — isSymlink=${stat.isSymbolicLink()}, isFile=${stat.isFile()}, size=${stat.size}`);
3627
- if (stat.isSymbolicLink()) {
3769
+ const stat2 = lstatSync(src);
3770
+ log(`ORT extract: ${libFile} — isSymlink=${stat2.isSymbolicLink()}, isFile=${stat2.isFile()}, size=${stat2.size}`);
3771
+ if (stat2.isSymbolicLink()) {
3628
3772
  symlinks.push({ name: libFile, target: readlinkSync(src) });
3629
3773
  } else {
3630
3774
  realFiles.push(libFile);
@@ -3635,7 +3779,7 @@ async function downloadOnnxRuntime(info, targetDir) {
3635
3779
  }
3636
3780
  }
3637
3781
  copyOnnxLibraries(info, extractedDir, targetDir, realFiles, symlinks);
3638
- const libPath = join8(targetDir, info.libName);
3782
+ const libPath = join9(targetDir, info.libName);
3639
3783
  let libHash = null;
3640
3784
  try {
3641
3785
  libHash = sha256File(libPath);
@@ -3660,8 +3804,8 @@ async function downloadOnnxRuntime(info, targetDir) {
3660
3804
  function copyOnnxLibraries(info, extractedDir, targetDir, realFiles, symlinks, copyFile = copyFileSync3) {
3661
3805
  const requiredLibs = new Set([info.libName]);
3662
3806
  for (const libFile of realFiles) {
3663
- const src = join8(extractedDir, libFile);
3664
- const dst = join8(targetDir, libFile);
3807
+ const src = join9(extractedDir, libFile);
3808
+ const dst = join9(targetDir, libFile);
3665
3809
  try {
3666
3810
  copyFile(src, dst);
3667
3811
  if (process.platform !== "win32") {
@@ -3677,12 +3821,12 @@ function copyOnnxLibraries(info, extractedDir, targetDir, realFiles, symlinks, c
3677
3821
  }
3678
3822
  const targetRoot = realpathSync(targetDir);
3679
3823
  for (const link of symlinks) {
3680
- const dst = join8(targetDir, link.name);
3824
+ const dst = join9(targetDir, link.name);
3681
3825
  try {
3682
3826
  unlinkSync4(dst);
3683
3827
  } catch {}
3684
- const dstForContainment = join8(targetRoot, link.name);
3685
- const resolvedTarget = resolve5(dirname4(dstForContainment), link.target);
3828
+ const dstForContainment = join9(targetRoot, link.name);
3829
+ const resolvedTarget = resolve5(dirname5(dstForContainment), link.target);
3686
3830
  if (!isPathInsideRoot(targetRoot, resolvedTarget)) {
3687
3831
  const message = `ONNX Runtime symlink ${link.name} points outside install dir: ${link.target}`;
3688
3832
  if (requiredLibs.has(link.name)) {
@@ -3702,7 +3846,7 @@ function copyOnnxLibraries(info, extractedDir, targetDir, realFiles, symlinks, c
3702
3846
  log(`ORT extract: failed to symlink optional ${link.name}: ${symlinkErr}`);
3703
3847
  }
3704
3848
  }
3705
- const requiredPath = join8(targetDir, info.libName);
3849
+ const requiredPath = join9(targetDir, info.libName);
3706
3850
  if (!existsSync6(requiredPath)) {
3707
3851
  rmSync3(targetDir, { recursive: true, force: true });
3708
3852
  throw new Error(`Required ONNX Runtime library missing after install: ${requiredPath}`);
@@ -3729,13 +3873,13 @@ function writeOnnxInstalledMeta(installDir, version, sha256, archiveSha256) {
3729
3873
  ...sha256 ? { sha256 } : {},
3730
3874
  archiveSha256
3731
3875
  };
3732
- writeFileSync3(join8(installDir, ONNX_INSTALLED_META_FILE), JSON.stringify(meta), "utf8");
3876
+ writeFileSync3(join9(installDir, ONNX_INSTALLED_META_FILE), JSON.stringify(meta), "utf8");
3733
3877
  } catch (err) {
3734
3878
  log(`[onnx] failed to write installed-meta in ${installDir}: ${err}`);
3735
3879
  }
3736
3880
  }
3737
3881
  function readOnnxInstalledMeta(installDir) {
3738
- const path2 = join8(installDir, ONNX_INSTALLED_META_FILE);
3882
+ const path2 = join9(installDir, ONNX_INSTALLED_META_FILE);
3739
3883
  try {
3740
3884
  if (!statSync4(path2).isFile())
3741
3885
  return null;
@@ -3873,7 +4017,7 @@ function isProcessAlive(pid) {
3873
4017
  }
3874
4018
  function cleanupOnnxRuntime(storageDir) {
3875
4019
  try {
3876
- const ortBase = join8(storageDir, "onnxruntime");
4020
+ const ortBase = join9(storageDir, "onnxruntime");
3877
4021
  if (existsSync6(ortBase)) {
3878
4022
  rmSync3(ortBase, { recursive: true, force: true });
3879
4023
  }
@@ -3975,10 +4119,10 @@ function projectRootKeyHash(dir) {
3975
4119
  var init_project_identity = () => {};
3976
4120
 
3977
4121
  // ../aft-bridge/dist/pool.js
3978
- import { homedir as homedir9 } from "node:os";
4122
+ import { homedir as homedir10 } from "node:os";
3979
4123
  function canonicalHomeDir() {
3980
4124
  try {
3981
- const home = homedir9();
4125
+ const home = homedir10();
3982
4126
  if (!home)
3983
4127
  return null;
3984
4128
  return canonicalizeProjectRoot(home);
@@ -4004,6 +4148,7 @@ class BridgePool {
4004
4148
  projectConfigLoader;
4005
4149
  logger;
4006
4150
  cleanupTimer = null;
4151
+ shutdownCalled = false;
4007
4152
  constructor(binaryPath, options = {}, configOverrides = {}) {
4008
4153
  this.binaryPath = binaryPath;
4009
4154
  this.maxPoolSize = options.maxPoolSize ?? DEFAULT_MAX_POOL_SIZE;
@@ -4025,12 +4170,11 @@ class BridgePool {
4025
4170
  childEnv: options.childEnv
4026
4171
  };
4027
4172
  this.configOverrides = configOverrides;
4028
- if (Number.isFinite(this.idleTimeoutMs)) {
4029
- this.cleanupTimer = setInterval(() => this.cleanup(), CLEANUP_INTERVAL_MS);
4030
- this.cleanupTimer.unref();
4031
- }
4173
+ this.startCleanupTimer();
4032
4174
  }
4033
4175
  getActiveBridgeForRoot(projectRoot) {
4176
+ if (this.shutdownCalled)
4177
+ return null;
4034
4178
  const key = normalizeKey(projectRoot);
4035
4179
  const entry = this.bridges.get(key);
4036
4180
  if (!entry?.bridge.isAlive())
@@ -4038,7 +4182,21 @@ class BridgePool {
4038
4182
  entry.lastUsed = Date.now();
4039
4183
  return entry.bridge;
4040
4184
  }
4185
+ activeBridges() {
4186
+ if (this.shutdownCalled)
4187
+ return [];
4188
+ const alive = [];
4189
+ for (const entry of this.bridges.values()) {
4190
+ if (entry.bridge.isAlive())
4191
+ alive.push(entry.bridge);
4192
+ }
4193
+ return alive;
4194
+ }
4041
4195
  getBridge(projectRoot) {
4196
+ if (this.shutdownCalled) {
4197
+ this.shutdownCalled = false;
4198
+ this.startCleanupTimer();
4199
+ }
4042
4200
  const key = normalizeKey(projectRoot);
4043
4201
  const existing = this.bridges.get(key);
4044
4202
  if (existing) {
@@ -4048,15 +4206,7 @@ class BridgePool {
4048
4206
  if (this.bridges.size >= this.maxPoolSize) {
4049
4207
  this.evictLRU();
4050
4208
  }
4051
- let projectOverrides = {};
4052
- if (this.projectConfigLoader) {
4053
- try {
4054
- projectOverrides = this.projectConfigLoader(key) ?? {};
4055
- } catch (err) {
4056
- const message = err instanceof Error ? err.message : String(err);
4057
- this.error(`projectConfigLoader failed; using global overrides only: ${message}`);
4058
- }
4059
- }
4209
+ const projectOverrides = this.loadProjectOverrides(key);
4060
4210
  const mergedOverrides = { ...this.configOverrides, ...projectOverrides };
4061
4211
  const bridge = new BinaryBridge(this.binaryPath, key, this.bridgeOptions, mergedOverrides);
4062
4212
  this.bridges.set(key, { bridge, lastUsed: Date.now() });
@@ -4106,6 +4256,7 @@ class BridgePool {
4106
4256
  }
4107
4257
  async closeSession(_projectRoot, _session) {}
4108
4258
  async shutdown() {
4259
+ this.shutdownCalled = true;
4109
4260
  if (this.cleanupTimer) {
4110
4261
  clearInterval(this.cleanupTimer);
4111
4262
  this.cleanupTimer = null;
@@ -4118,6 +4269,9 @@ class BridgePool {
4118
4269
  this.staleBridges.clear();
4119
4270
  await Promise.allSettled(shutdowns);
4120
4271
  }
4272
+ isShutdown() {
4273
+ return this.shutdownCalled;
4274
+ }
4121
4275
  async replaceBinary(newPath) {
4122
4276
  this.binaryPath = newPath;
4123
4277
  for (const entry of this.bridges.values()) {
@@ -4127,6 +4281,12 @@ class BridgePool {
4127
4281
  this.log(`Binary path updated to ${newPath}. Active bridges marked stale — next calls will use the new binary.`);
4128
4282
  return newPath;
4129
4283
  }
4284
+ startCleanupTimer() {
4285
+ if (!Number.isFinite(this.idleTimeoutMs) || this.cleanupTimer)
4286
+ return;
4287
+ this.cleanupTimer = setInterval(() => this.cleanup(), CLEANUP_INTERVAL_MS);
4288
+ this.cleanupTimer.unref();
4289
+ }
4130
4290
  log(message, meta) {
4131
4291
  const logger = this.logger ?? getActiveLogger();
4132
4292
  if (logger) {
@@ -4158,6 +4318,36 @@ class BridgePool {
4158
4318
  this.configOverrides[key] = value;
4159
4319
  }
4160
4320
  }
4321
+ async reconfigure(projectRoot, overrides) {
4322
+ const key = normalizeKey(projectRoot);
4323
+ for (const [name, value] of Object.entries(overrides)) {
4324
+ this.setConfigureOverride(name, value);
4325
+ }
4326
+ const bridge = this.getActiveBridgeForRoot(key);
4327
+ if (!bridge)
4328
+ return;
4329
+ const projectOverrides = this.loadProjectOverrides(key);
4330
+ const response = await bridge.send("configure", {
4331
+ project_root: key,
4332
+ ...this.configOverrides,
4333
+ ...projectOverrides,
4334
+ ...overrides
4335
+ });
4336
+ if (response.success === false) {
4337
+ throw new Error(`configure refresh failed for ${key}: ${String(response.message ?? "unknown error")}`);
4338
+ }
4339
+ }
4340
+ loadProjectOverrides(key) {
4341
+ if (!this.projectConfigLoader)
4342
+ return {};
4343
+ try {
4344
+ return this.projectConfigLoader(key) ?? {};
4345
+ } catch (err) {
4346
+ const message = err instanceof Error ? err.message : String(err);
4347
+ this.error(`projectConfigLoader failed; using global overrides only: ${message}`);
4348
+ return {};
4349
+ }
4350
+ }
4161
4351
  get size() {
4162
4352
  return this.bridges.size;
4163
4353
  }
@@ -4188,7 +4378,186 @@ var init_pool = __esm(() => {
4188
4378
  };
4189
4379
  });
4190
4380
 
4191
- // ../../node_modules/.bun/@cortexkit+subc-client@0.3.0/node_modules/@cortexkit/subc-client/src/auth.ts
4381
+ // ../aft-bridge/dist/revivable-transport.js
4382
+ class RevivableTransportPool {
4383
+ createPool;
4384
+ onBinaryReplaced;
4385
+ activePool;
4386
+ revival = null;
4387
+ transports = new Map;
4388
+ configureOverrides = new Map;
4389
+ constructor(initialPool, createPool, onBinaryReplaced) {
4390
+ this.createPool = createPool;
4391
+ this.onBinaryReplaced = onBinaryReplaced;
4392
+ this.activePool = initialPool;
4393
+ }
4394
+ getBridge(projectRoot) {
4395
+ const key = canonicalizeProjectRoot(projectRoot);
4396
+ let transport = this.transports.get(key);
4397
+ if (!transport) {
4398
+ transport = new RevivableProjectTransport(this, key);
4399
+ this.transports.set(key, transport);
4400
+ }
4401
+ return transport;
4402
+ }
4403
+ activeBridges() {
4404
+ return this.activePool.activeBridges();
4405
+ }
4406
+ getActiveBridgeForRoot(projectRoot) {
4407
+ const key = canonicalizeProjectRoot(projectRoot);
4408
+ const bridge = this.currentBridge(key);
4409
+ if (!bridge)
4410
+ return null;
4411
+ const transport = this.getBridge(key);
4412
+ transport.refreshStatusSubscription(bridge);
4413
+ return transport;
4414
+ }
4415
+ async toolCall(projectRoot, runtime, name, rawArgs = {}, options) {
4416
+ return this.getBridge(projectRoot).toolCall(runtime.sessionID, name, rawArgs, options);
4417
+ }
4418
+ setConfigureOverride(key, value) {
4419
+ if (value === undefined)
4420
+ this.configureOverrides.delete(key);
4421
+ else
4422
+ this.configureOverrides.set(key, value);
4423
+ this.activePool.setConfigureOverride(key, value);
4424
+ }
4425
+ async reconfigure(projectRoot, overrides) {
4426
+ for (const [key, value] of Object.entries(overrides)) {
4427
+ if (value === undefined)
4428
+ this.configureOverrides.delete(key);
4429
+ else
4430
+ this.configureOverrides.set(key, value);
4431
+ }
4432
+ const pool = await this.ensureActivePool();
4433
+ await pool.reconfigure(projectRoot, overrides);
4434
+ }
4435
+ async replaceBinary(path2) {
4436
+ const replaced = await this.activePool.replaceBinary(path2);
4437
+ this.onBinaryReplaced?.(replaced);
4438
+ return replaced;
4439
+ }
4440
+ closeSession(projectRoot, session) {
4441
+ return this.activePool.closeSession(projectRoot, session);
4442
+ }
4443
+ async shutdown() {
4444
+ const revival = this.revival;
4445
+ if (revival) {
4446
+ await Promise.allSettled([revival]);
4447
+ }
4448
+ await this.activePool.shutdown();
4449
+ for (const transport of this.transports.values()) {
4450
+ transport.refreshStatusSubscription(null);
4451
+ }
4452
+ }
4453
+ isShutdown() {
4454
+ return this.activePool.isShutdown();
4455
+ }
4456
+ async ensureActivePool() {
4457
+ if (!this.activePool.isShutdown())
4458
+ return this.activePool;
4459
+ if (this.revival)
4460
+ return this.revival;
4461
+ warn("transport was shut down but new demand arrived — reviving (host quit hook fired without process exit?)");
4462
+ const revival = this.createPool().then((pool) => {
4463
+ for (const [key, value] of this.configureOverrides) {
4464
+ pool.setConfigureOverride(key, value);
4465
+ }
4466
+ this.activePool = pool;
4467
+ for (const [root, transport] of this.transports) {
4468
+ transport.refreshStatusSubscription(pool.getActiveBridgeForRoot(root));
4469
+ }
4470
+ return pool;
4471
+ });
4472
+ this.revival = revival;
4473
+ revival.then(() => {
4474
+ if (this.revival === revival)
4475
+ this.revival = null;
4476
+ }, () => {
4477
+ if (this.revival === revival)
4478
+ this.revival = null;
4479
+ });
4480
+ return revival;
4481
+ }
4482
+ currentBridge(projectRoot) {
4483
+ return this.activePool.getActiveBridgeForRoot(projectRoot);
4484
+ }
4485
+ async send(projectRoot, command, params, options) {
4486
+ const pool = await this.ensureActivePool();
4487
+ const bridge = pool.getBridge(projectRoot);
4488
+ this.getBridge(projectRoot).refreshStatusSubscription(bridge);
4489
+ return bridge.send(command, params, options);
4490
+ }
4491
+ async toolCallOnProject(projectRoot, sessionId, name, rawArgs, options) {
4492
+ const pool = await this.ensureActivePool();
4493
+ const bridge = pool.getBridge(projectRoot);
4494
+ this.getBridge(projectRoot).refreshStatusSubscription(bridge);
4495
+ return bridge.toolCall(sessionId, name, rawArgs, options);
4496
+ }
4497
+ }
4498
+
4499
+ class RevivableProjectTransport {
4500
+ owner;
4501
+ projectRoot;
4502
+ statusListeners = new Map;
4503
+ statusBridges = new Map;
4504
+ constructor(owner, projectRoot) {
4505
+ this.owner = owner;
4506
+ this.projectRoot = projectRoot;
4507
+ }
4508
+ getCwd() {
4509
+ return this.projectRoot;
4510
+ }
4511
+ getStatusBar() {
4512
+ return this.owner.currentBridge(this.projectRoot)?.getStatusBar();
4513
+ }
4514
+ getCachedStatus() {
4515
+ return this.owner.currentBridge(this.projectRoot)?.getCachedStatus() ?? null;
4516
+ }
4517
+ cacheStatusSnapshot(snapshot) {
4518
+ this.owner.currentBridge(this.projectRoot)?.cacheStatusSnapshot(snapshot);
4519
+ }
4520
+ send(command, params, options) {
4521
+ return this.owner.send(this.projectRoot, command, params, options);
4522
+ }
4523
+ toolCall(sessionId, name, rawArgs, options) {
4524
+ return this.owner.toolCallOnProject(this.projectRoot, sessionId, name, rawArgs, options);
4525
+ }
4526
+ subscribeStatus(listener) {
4527
+ if (this.statusListeners.has(listener))
4528
+ return () => this.removeStatusListener(listener);
4529
+ this.statusListeners.set(listener, () => {});
4530
+ this.bindStatusListener(listener, this.owner.currentBridge(this.projectRoot));
4531
+ return () => this.removeStatusListener(listener);
4532
+ }
4533
+ refreshStatusSubscription(bridge) {
4534
+ for (const listener of this.statusListeners.keys()) {
4535
+ this.bindStatusListener(listener, bridge);
4536
+ }
4537
+ }
4538
+ bindStatusListener(listener, bridge) {
4539
+ if (this.statusBridges.get(listener) === bridge)
4540
+ return;
4541
+ const previousUnsubscribe = this.statusListeners.get(listener);
4542
+ previousUnsubscribe?.();
4543
+ const maybe = bridge;
4544
+ const unsubscribe = maybe && typeof maybe.subscribeStatus === "function" ? maybe.subscribeStatus(listener) : () => {};
4545
+ this.statusListeners.set(listener, unsubscribe);
4546
+ this.statusBridges.set(listener, bridge);
4547
+ }
4548
+ removeStatusListener(listener) {
4549
+ const unsubscribe = this.statusListeners.get(listener);
4550
+ this.statusListeners.delete(listener);
4551
+ this.statusBridges.delete(listener);
4552
+ unsubscribe?.();
4553
+ }
4554
+ }
4555
+ var init_revivable_transport = __esm(() => {
4556
+ init_active_logger();
4557
+ init_project_identity();
4558
+ });
4559
+
4560
+ // ../../node_modules/.bun/@cortexkit+subc-client@0.5.0/node_modules/@cortexkit/subc-client/dist/auth.js
4192
4561
  import { createHmac, randomBytes, timingSafeEqual } from "node:crypto";
4193
4562
  function computeProof(key, domain, clientNonce, serverNonce, daemonId) {
4194
4563
  const mac = createHmac("sha256", Buffer.from(key));
@@ -4249,7 +4618,132 @@ var init_auth = __esm(() => {
4249
4618
  };
4250
4619
  });
4251
4620
 
4252
- // ../../node_modules/.bun/@cortexkit+subc-client@0.3.0/node_modules/@cortexkit/subc-client/src/connection-file.ts
4621
+ // ../../node_modules/.bun/@cortexkit+subc-client@0.5.0/node_modules/@cortexkit/subc-client/dist/envelope.js
4622
+ function isPureHeader(ty) {
4623
+ return ty === FrameType.Cancel || ty === FrameType.Ping || ty === FrameType.Pong || ty === FrameType.Goodbye;
4624
+ }
4625
+ function buildFlags(binary, priority, last, admissionClass = AdmissionClass.Normal) {
4626
+ let flags = 0;
4627
+ if (binary)
4628
+ flags |= FLAG_BINARY;
4629
+ flags |= priority << FLAG_PRIORITY_SHIFT;
4630
+ if (last)
4631
+ flags |= FLAG_LAST;
4632
+ flags |= admissionClass << FLAG_ADMISSION_SHIFT;
4633
+ return flags;
4634
+ }
4635
+ function encodeHeader(header) {
4636
+ const buffer = new Uint8Array(HEADER_LEN);
4637
+ const view = new DataView(buffer.buffer);
4638
+ view.setUint32(0, header.len, true);
4639
+ buffer[4] = header.ver;
4640
+ buffer[5] = header.ty;
4641
+ buffer[6] = header.flags;
4642
+ view.setUint16(7, header.channel, true);
4643
+ view.setUint32(9, header.epoch, true);
4644
+ view.setBigUint64(13, header.corr, true);
4645
+ return buffer;
4646
+ }
4647
+ function decodeHeader(bytes) {
4648
+ if (bytes.length < FROZEN_PREFIX_LEN) {
4649
+ throw new DecodeError(`header shorter than frozen prefix: have ${bytes.length} bytes`, "too_short_for_prefix");
4650
+ }
4651
+ const ver = bytes[4];
4652
+ if (ver !== PROTOCOL_VERSION)
4653
+ throw new DecodeError(`unsupported envelope version ${ver}`, "unsupported_version");
4654
+ if (bytes.length < HEADER_LEN) {
4655
+ throw new DecodeError(`header too short for version: have ${bytes.length} bytes, need ${HEADER_LEN}`, "too_short_for_header");
4656
+ }
4657
+ const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
4658
+ const len = view.getUint32(0, true);
4659
+ const typeByte = bytes[5];
4660
+ if (typeByte > FRAME_TYPE_MAX)
4661
+ throw new DecodeError(`unknown frame type byte ${typeByte}`, "unknown_frame_type");
4662
+ const ty = typeByte;
4663
+ const flags = bytes[6];
4664
+ if ((flags & FLAG_RESERVED_MASK) !== 0) {
4665
+ throw new DecodeError(`reserved flag bits set in flags 0b${flags.toString(2).padStart(8, "0")}`, "reserved_flag_bits");
4666
+ }
4667
+ if ((flags & FLAG_PRIORITY_MASK) >> FLAG_PRIORITY_SHIFT === 3) {
4668
+ throw new DecodeError(`reserved priority bits set in flags 0b${flags.toString(2).padStart(8, "0")}`, "reserved_priority_bits");
4669
+ }
4670
+ const admission = (flags & FLAG_ADMISSION_MASK) >> FLAG_ADMISSION_SHIFT;
4671
+ if (admission === 3) {
4672
+ throw new DecodeError(`reserved admission class set in flags 0b${flags.toString(2).padStart(8, "0")}`, "reserved_admission_class");
4673
+ }
4674
+ if (admission === AdmissionClass.Sheddable && ty !== FrameType.Push && ty !== FrameType.StreamData) {
4675
+ throw new DecodeError(`SHEDDABLE admission class is illegal on ${FrameType[ty]} in flags 0b${flags.toString(2).padStart(8, "0")}`, "sheddable_illegal_frame_type");
4676
+ }
4677
+ const channel = view.getUint16(7, true);
4678
+ const epoch = view.getUint32(9, true);
4679
+ if (channel === 0 && epoch !== 0) {
4680
+ throw new DecodeError(`control channel carried nonzero epoch ${epoch}`, "nonzero_epoch_on_control_channel");
4681
+ }
4682
+ if (isPureHeader(ty) && len !== 0) {
4683
+ throw new DecodeError(`pure-header frame ${FrameType[ty]} declared non-zero body length ${len}`, "pure_header_frame_with_body");
4684
+ }
4685
+ return { len, ver, ty, flags, channel, epoch, corr: view.getBigUint64(13, true) };
4686
+ }
4687
+ function buildFrame(ty, flags, channel, epoch, corr, body) {
4688
+ return buildFrameWithVersion(PROTOCOL_VERSION, ty, flags, channel, epoch, corr, body);
4689
+ }
4690
+ function buildFrameWithVersion(ver, ty, flags, channel, epoch, corr, body) {
4691
+ if (body.length > MAX_FRAME_BODY_LEN) {
4692
+ throw new DecodeError(`frame body ${body.length} exceeds max ${MAX_FRAME_BODY_LEN}`, "frame_body_too_large");
4693
+ }
4694
+ const header = { len: body.length, ver, ty, flags, channel, epoch, corr };
4695
+ decodeHeader(encodeHeader(header));
4696
+ return { header, body };
4697
+ }
4698
+ function encodeFrame(frame) {
4699
+ if (frame.header.len !== frame.body.length) {
4700
+ throw new DecodeError(`frame header length ${frame.header.len} does not match body length ${frame.body.length}`, "frame_length_mismatch");
4701
+ }
4702
+ const header = encodeHeader(frame.header);
4703
+ const output = new Uint8Array(header.length + frame.body.length);
4704
+ output.set(header, 0);
4705
+ output.set(frame.body, header.length);
4706
+ return output;
4707
+ }
4708
+ var PROTOCOL_VERSION = 2, HEADER_LEN = 21, FROZEN_PREFIX_LEN = 5, MAX_FRAME_BODY_LEN, FrameType, FRAME_TYPE_MAX, Priority, AdmissionClass, FLAG_BINARY = 1, FLAG_PRIORITY_MASK = 6, FLAG_PRIORITY_SHIFT = 1, FLAG_LAST = 8, FLAG_ADMISSION_MASK = 48, FLAG_ADMISSION_SHIFT = 4, FLAG_RESERVED_MASK = 192, DecodeError;
4709
+ var init_envelope = __esm(() => {
4710
+ MAX_FRAME_BODY_LEN = 64 * 1024 * 1024;
4711
+ (function(FrameType2) {
4712
+ FrameType2[FrameType2["Request"] = 0] = "Request";
4713
+ FrameType2[FrameType2["Response"] = 1] = "Response";
4714
+ FrameType2[FrameType2["Push"] = 2] = "Push";
4715
+ FrameType2[FrameType2["StreamData"] = 3] = "StreamData";
4716
+ FrameType2[FrameType2["StreamEnd"] = 4] = "StreamEnd";
4717
+ FrameType2[FrameType2["Error"] = 5] = "Error";
4718
+ FrameType2[FrameType2["Cancel"] = 6] = "Cancel";
4719
+ FrameType2[FrameType2["Ping"] = 7] = "Ping";
4720
+ FrameType2[FrameType2["Pong"] = 8] = "Pong";
4721
+ FrameType2[FrameType2["Hello"] = 9] = "Hello";
4722
+ FrameType2[FrameType2["HelloAck"] = 10] = "HelloAck";
4723
+ FrameType2[FrameType2["Goodbye"] = 11] = "Goodbye";
4724
+ })(FrameType || (FrameType = {}));
4725
+ FRAME_TYPE_MAX = FrameType.Goodbye;
4726
+ (function(Priority2) {
4727
+ Priority2[Priority2["Passive"] = 0] = "Passive";
4728
+ Priority2[Priority2["Interactive"] = 1] = "Interactive";
4729
+ Priority2[Priority2["Background"] = 2] = "Background";
4730
+ })(Priority || (Priority = {}));
4731
+ (function(AdmissionClass2) {
4732
+ AdmissionClass2[AdmissionClass2["Normal"] = 0] = "Normal";
4733
+ AdmissionClass2[AdmissionClass2["Expedite"] = 1] = "Expedite";
4734
+ AdmissionClass2[AdmissionClass2["Sheddable"] = 2] = "Sheddable";
4735
+ })(AdmissionClass || (AdmissionClass = {}));
4736
+ DecodeError = class DecodeError extends Error {
4737
+ code;
4738
+ constructor(message, code) {
4739
+ super(message);
4740
+ this.code = code;
4741
+ this.name = "DecodeError";
4742
+ }
4743
+ };
4744
+ });
4745
+
4746
+ // ../../node_modules/.bun/@cortexkit+subc-client@0.5.0/node_modules/@cortexkit/subc-client/dist/connection-file.js
4253
4747
  import { promises as fs } from "node:fs";
4254
4748
  function toBytes(value, field) {
4255
4749
  if (!Array.isArray(value) || value.some((n) => typeof n !== "number")) {
@@ -4274,8 +4768,8 @@ function validate(info) {
4274
4768
  async function verifyOwnerOnly(path2) {
4275
4769
  if (process.platform === "win32")
4276
4770
  return;
4277
- const stat = await fs.stat(path2);
4278
- const mode = stat.mode & 511;
4771
+ const stat2 = await fs.stat(path2);
4772
+ const mode = stat2.mode & 511;
4279
4773
  if ((mode & 63) !== 0) {
4280
4774
  throw new ConnectionFileError(`connection file ${path2} has insecure permissions 0o${mode.toString(8)}; expected owner-only 0600`);
4281
4775
  }
@@ -4289,6 +4783,10 @@ async function readConnectionFile(path2) {
4289
4783
  } catch (err) {
4290
4784
  throw new ConnectionFileError(`connection file JSON read failed for ${path2}: ${String(err)}`);
4291
4785
  }
4786
+ const wireVersion = parsed.wire_version;
4787
+ if (wireVersion !== undefined && wireVersion !== PROTOCOL_VERSION) {
4788
+ throw new ConnectionFileError(`connection file wire_version ${String(wireVersion)} but this client speaks ${PROTOCOL_VERSION}; the client library must be upgraded`);
4789
+ }
4292
4790
  const endpointsRaw = parsed.endpoints;
4293
4791
  if (!Array.isArray(endpointsRaw)) {
4294
4792
  throw new ConnectionFileError("connection file 'endpoints' must be an array");
@@ -4313,110 +4811,59 @@ async function readConnectionFile(path2) {
4313
4811
  }
4314
4812
  var SCHEMA_VERSION = 1, MIN_KEY_LEN = 32, DAEMON_ID_LEN = 16, ConnectionFileError;
4315
4813
  var init_connection_file = __esm(() => {
4814
+ init_envelope();
4316
4815
  ConnectionFileError = class ConnectionFileError extends Error {
4317
4816
  };
4318
4817
  });
4319
4818
 
4320
- // ../../node_modules/.bun/@cortexkit+subc-client@0.3.0/node_modules/@cortexkit/subc-client/src/envelope.ts
4321
- function isPureHeader(ty) {
4322
- return ty === 6 /* Cancel */ || ty === 7 /* Ping */ || ty === 8 /* Pong */ || ty === 11 /* Goodbye */;
4323
- }
4324
- function buildFlags(binary, priority, last) {
4325
- let b = 0;
4326
- if (binary)
4327
- b |= FLAG_BINARY;
4328
- b |= priority << FLAG_PRIORITY_SHIFT;
4329
- if (last)
4330
- b |= FLAG_LAST;
4331
- return b;
4332
- }
4333
- function encodeHeader(h) {
4334
- const buf = new Uint8Array(HEADER_LEN);
4335
- const view = new DataView(buf.buffer);
4336
- view.setUint32(0, h.len, true);
4337
- buf[4] = h.ver;
4338
- buf[5] = h.ty;
4339
- buf[6] = h.flags;
4340
- view.setUint16(7, h.channel, true);
4341
- view.setBigUint64(9, h.corr, true);
4342
- return buf;
4343
- }
4344
- function decodeHeader(bytes) {
4345
- if (bytes.length < FROZEN_PREFIX_LEN) {
4346
- throw new DecodeError(`header shorter than frozen prefix: have ${bytes.length} bytes`);
4347
- }
4348
- const ver = bytes[4];
4349
- if (ver !== PROTOCOL_VERSION) {
4350
- throw new DecodeError(`unsupported envelope version ${ver}`);
4351
- }
4352
- if (bytes.length < HEADER_LEN) {
4353
- throw new DecodeError(`header too short for version: have ${bytes.length} bytes, need ${HEADER_LEN}`);
4354
- }
4355
- const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
4356
- const len = view.getUint32(0, true);
4357
- const tyByte = bytes[5];
4358
- if (tyByte > FRAME_TYPE_MAX) {
4359
- throw new DecodeError(`unknown frame type byte ${tyByte}`);
4360
- }
4361
- const ty = tyByte;
4362
- const flags = bytes[6];
4363
- if ((flags & FLAG_RESERVED_MASK) !== 0) {
4364
- throw new DecodeError(`reserved flag bits set in flags 0b${flags.toString(2)}`);
4365
- }
4366
- if ((flags & FLAG_PRIORITY_MASK) >> FLAG_PRIORITY_SHIFT === 3) {
4367
- throw new DecodeError(`reserved priority bits set in flags 0b${flags.toString(2)}`);
4368
- }
4369
- if (isPureHeader(ty) && len !== 0) {
4370
- throw new DecodeError(`pure-header frame ${FrameType[ty]} declared non-zero body length ${len}`);
4371
- }
4372
- const channel = view.getUint16(7, true);
4373
- const corr = view.getBigUint64(9, true);
4374
- return { len, ver, ty, flags, channel, corr };
4375
- }
4376
- function buildFrame(ty, flags, channel, corr, body) {
4377
- return buildFrameWithVersion(PROTOCOL_VERSION, ty, flags, channel, corr, body);
4378
- }
4379
- function buildFrameWithVersion(ver, ty, flags, channel, corr, body) {
4380
- if (body.length > MAX_FRAME_BODY_LEN) {
4381
- throw new DecodeError(`frame body ${body.length} exceeds max ${MAX_FRAME_BODY_LEN}`);
4382
- }
4383
- if (isPureHeader(ty) && body.length !== 0) {
4384
- throw new DecodeError(`pure-header frame ${FrameType[ty]} cannot carry a body`);
4385
- }
4386
- return {
4387
- header: { len: body.length, ver, ty, flags, channel, corr },
4388
- body
4389
- };
4390
- }
4391
- function encodeFrame(frame) {
4392
- const header = encodeHeader(frame.header);
4393
- const out = new Uint8Array(header.length + frame.body.length);
4394
- out.set(header, 0);
4395
- out.set(frame.body, header.length);
4396
- return out;
4397
- }
4398
- var PROTOCOL_VERSION = 1, HEADER_LEN = 17, FROZEN_PREFIX_LEN = 5, MAX_FRAME_BODY_LEN, FrameType, FRAME_TYPE_MAX = 11 /* Goodbye */, FLAG_BINARY = 1, FLAG_PRIORITY_MASK = 6, FLAG_PRIORITY_SHIFT = 1, FLAG_LAST = 8, FLAG_RESERVED_MASK = 240, DecodeError;
4399
- var init_envelope = __esm(() => {
4400
- MAX_FRAME_BODY_LEN = 64 * 1024 * 1024;
4401
- ((FrameType2) => {
4402
- FrameType2[FrameType2["Request"] = 0] = "Request";
4403
- FrameType2[FrameType2["Response"] = 1] = "Response";
4404
- FrameType2[FrameType2["Push"] = 2] = "Push";
4405
- FrameType2[FrameType2["StreamData"] = 3] = "StreamData";
4406
- FrameType2[FrameType2["StreamEnd"] = 4] = "StreamEnd";
4407
- FrameType2[FrameType2["Error"] = 5] = "Error";
4408
- FrameType2[FrameType2["Cancel"] = 6] = "Cancel";
4409
- FrameType2[FrameType2["Ping"] = 7] = "Ping";
4410
- FrameType2[FrameType2["Pong"] = 8] = "Pong";
4411
- FrameType2[FrameType2["Hello"] = 9] = "Hello";
4412
- FrameType2[FrameType2["HelloAck"] = 10] = "HelloAck";
4413
- FrameType2[FrameType2["Goodbye"] = 11] = "Goodbye";
4414
- })(FrameType ||= {});
4415
- DecodeError = class DecodeError extends Error {
4819
+ // ../../node_modules/.bun/@cortexkit+subc-client@0.5.0/node_modules/@cortexkit/subc-client/dist/route-handle.js
4820
+ class RouteHandle {
4821
+ channel;
4822
+ epoch;
4823
+ constructor(channel, epoch, token) {
4824
+ if (!Number.isInteger(channel) || channel <= 0 || channel > 65535) {
4825
+ throw new RangeError(`route channel must be an integer in 1..65535, got ${channel}`);
4826
+ }
4827
+ if (!Number.isInteger(epoch) || epoch <= 0 || epoch > 4294967295) {
4828
+ throw new RangeError(`route epoch must be an integer in 1..4294967295, got ${epoch}`);
4829
+ }
4830
+ this.channel = channel;
4831
+ this.epoch = epoch;
4832
+ connectionToken.set(this, token);
4833
+ Object.freeze(this);
4834
+ }
4835
+ static create(channel, epoch, token) {
4836
+ return new RouteHandle(channel, epoch, token);
4837
+ }
4838
+ }
4839
+ function createRouteHandle(channel, epoch, token) {
4840
+ const factory = RouteHandle;
4841
+ return factory.create(channel, epoch, token);
4842
+ }
4843
+ function newConnectionToken() {
4844
+ return Object.freeze({});
4845
+ }
4846
+ function belongsToConnection(handle, token) {
4847
+ return connectionToken.get(handle) === token;
4848
+ }
4849
+ function sameRouteHandle(left, right) {
4850
+ return left === right;
4851
+ }
4852
+ var connectionToken, StaleRouteHandleError;
4853
+ var init_route_handle = __esm(() => {
4854
+ connectionToken = new WeakMap;
4855
+ StaleRouteHandleError = class StaleRouteHandleError extends Error {
4856
+ handle;
4857
+ code = "stale_route_handle";
4858
+ constructor(handle) {
4859
+ super(`route handle (${handle.channel}, ${handle.epoch}) is not live on the current connection`);
4860
+ this.handle = handle;
4861
+ this.name = "StaleRouteHandleError";
4862
+ }
4416
4863
  };
4417
4864
  });
4418
4865
 
4419
- // ../../node_modules/.bun/@cortexkit+subc-client@0.3.0/node_modules/@cortexkit/subc-client/src/socket.ts
4866
+ // ../../node_modules/.bun/@cortexkit+subc-client@0.5.0/node_modules/@cortexkit/subc-client/dist/socket.js
4420
4867
  import net from "node:net";
4421
4868
 
4422
4869
  class SubcSocket {
@@ -4465,6 +4912,24 @@ class SubcSocket {
4465
4912
  });
4466
4913
  });
4467
4914
  }
4915
+ async readFrame(headerDeadlineMs, bodyDeadline, onHeader) {
4916
+ const prefix = await this.readExact(FROZEN_PREFIX_LEN, headerDeadlineMs);
4917
+ const version = prefix[4];
4918
+ if (version !== PROTOCOL_VERSION)
4919
+ throw new DecodeError(`unsupported envelope version ${version}`, "unsupported_version");
4920
+ const remainder = await this.readExact(HEADER_LEN - FROZEN_PREFIX_LEN, headerDeadlineMs);
4921
+ const headerBytes = new Uint8Array(HEADER_LEN);
4922
+ headerBytes.set(prefix);
4923
+ headerBytes.set(remainder, FROZEN_PREFIX_LEN);
4924
+ const header = decodeHeader(headerBytes);
4925
+ if (header.len > MAX_FRAME_BODY_LEN) {
4926
+ throw new DecodeError(`frame body ${header.len} exceeds max ${MAX_FRAME_BODY_LEN}`, "frame_body_too_large");
4927
+ }
4928
+ onHeader?.();
4929
+ const bodyDeadlineMs = typeof bodyDeadline === "number" ? bodyDeadline : Date.now() + bodyDeadline.afterHeaderMs;
4930
+ const body = header.len === 0 ? new Uint8Array(0) : await this.readExact(header.len, bodyDeadlineMs);
4931
+ return { header, body };
4932
+ }
4468
4933
  readExact(n, deadlineMs) {
4469
4934
  if (this.waiter) {
4470
4935
  return Promise.reject(new Error("concurrent readExact is not supported"));
@@ -4587,6 +5052,7 @@ class SubcSocket {
4587
5052
  }
4588
5053
  var SocketClosedError, SocketTimeoutError, SocketWriteNotQueuedError, SocketWriteQueuedError;
4589
5054
  var init_socket = __esm(() => {
5055
+ init_envelope();
4590
5056
  SocketClosedError = class SocketClosedError extends Error {
4591
5057
  };
4592
5058
  SocketTimeoutError = class SocketTimeoutError extends Error {
@@ -4607,7 +5073,7 @@ var init_socket = __esm(() => {
4607
5073
  };
4608
5074
  });
4609
5075
 
4610
- // ../../node_modules/.bun/@cortexkit+subc-client@0.3.0/node_modules/@cortexkit/subc-client/src/client.ts
5076
+ // ../../node_modules/.bun/@cortexkit+subc-client@0.5.0/node_modules/@cortexkit/subc-client/dist/client.js
4611
5077
  import { promises as fs2 } from "node:fs";
4612
5078
  import { debuglog } from "node:util";
4613
5079
 
@@ -4617,7 +5083,11 @@ class SubcClient {
4617
5083
  opts;
4618
5084
  nextCorr = 1n;
4619
5085
  pending = new Map;
5086
+ lateResponses = new Map;
4620
5087
  routes = new Map;
5088
+ liveRoutes = new Map;
5089
+ connectionToken = newConnectionToken();
5090
+ ingressEpochDropCount = 0;
4621
5091
  closedErr = null;
4622
5092
  closeStarted = false;
4623
5093
  reconnecting = null;
@@ -4645,34 +5115,71 @@ class SubcClient {
4645
5115
  }
4646
5116
  async routeOpen(target, identity, opts = {}) {
4647
5117
  const consumerIdentity = routeOpenConsumerIdentity(opts);
5118
+ const consumerCapabilities = opts.consumerCapabilities;
4648
5119
  const body = this.encode({
4649
5120
  op: "route.open",
4650
5121
  target,
4651
5122
  identity,
4652
- ...consumerIdentity ? { consumer_identity: consumerIdentity } : {}
5123
+ ...consumerIdentity ? { consumer_identity: consumerIdentity } : {},
5124
+ ...consumerCapabilities !== undefined ? { consumer_capabilities: consumerCapabilities } : {}
4653
5125
  });
4654
- const reply = await this.controlRpc(body);
4655
- const parsed = this.parseJson(reply);
4656
- if (typeof parsed.route_channel !== "number") {
4657
- throw new SubcError(`route.open returned no route_channel: ${JSON.stringify(parsed)}`);
4658
- }
4659
- return parsed.route_channel;
5126
+ let installed = null;
5127
+ const install = (frame) => {
5128
+ if (frame.header.ty !== FrameType.Response)
5129
+ return true;
5130
+ const parsed = this.parseJson(frame);
5131
+ if (typeof parsed.route_channel !== "number" || typeof parsed.route_epoch !== "number") {
5132
+ throw new SubcError(`route.open returned no route handle: ${JSON.stringify(parsed)}`);
5133
+ }
5134
+ installed = this.installRoute(parsed.route_channel, parsed.route_epoch);
5135
+ return true;
5136
+ };
5137
+ const closeLateRoute = (frame) => {
5138
+ if (frame.header.ty !== FrameType.Response)
5139
+ return;
5140
+ try {
5141
+ const parsed = this.parseJson(frame);
5142
+ if (typeof parsed.route_channel !== "number" || typeof parsed.route_epoch !== "number")
5143
+ return;
5144
+ const lateHandle = this.installRoute(parsed.route_channel, parsed.route_epoch);
5145
+ this.failHandle(lateHandle, new SubcError("late route.open was closed", "route_closed"));
5146
+ this.liveRoutes.delete(lateHandle.channel);
5147
+ this.sendRouteGoodbye(lateHandle, true);
5148
+ } catch {
5149
+ this.closeConnectionAfterCleanupFailure();
5150
+ }
5151
+ };
5152
+ await this.controlRpc(body, install, closeLateRoute);
5153
+ if (!installed)
5154
+ throw new SubcError("route.open response was not installed");
5155
+ return installed;
4660
5156
  }
4661
- async request(routeChannel, body, opts = {}) {
5157
+ async request(handle, body, opts = {}) {
5158
+ this.assertLiveHandle(handle);
4662
5159
  const bytes = body instanceof Uint8Array ? body : this.encode(body);
4663
- const priority = opts.priority ?? 1 /* Interactive */;
4664
- const reply = await this.send(routeChannel, bytes, priority, opts.timeoutMs, opts.onProgress);
5160
+ const priority = opts.priority ?? Priority.Interactive;
5161
+ const admission = opts.admissionClass ?? AdmissionClass.Normal;
5162
+ const reply = await this.send(handle, bytes, priority, admission, opts.timeoutMs, opts.onProgress);
4665
5163
  return this.parseJson(reply);
4666
5164
  }
4667
5165
  async call(moduleId, method, params, opts = {}) {
4668
5166
  const body = params === undefined ? { method } : { method, params };
5167
+ let retriedUnknownChannel = false;
4669
5168
  for (;; ) {
4670
- const routeChannel = await this.cachedRouteChannel(moduleId, opts);
5169
+ const routeHandle = await this.cachedRouteHandle(moduleId, opts);
4671
5170
  try {
4672
- return await this.managedRequest(routeChannel, body, opts);
5171
+ return await this.managedRequest(routeHandle, body, opts);
4673
5172
  } catch (err) {
4674
5173
  if (!(err instanceof SubcCallError))
4675
5174
  throw this.terminalCallError("managed call failed", err);
5175
+ if (err.code === "unknown_channel" && !retriedUnknownChannel && !this.closeStarted) {
5176
+ retriedUnknownChannel = true;
5177
+ this.evictRouteHandle(routeHandle);
5178
+ continue;
5179
+ }
5180
+ if (err.code === "unknown_channel" && retriedUnknownChannel) {
5181
+ this.evictRouteHandle(routeHandle);
5182
+ }
4676
5183
  if (err.kind === "not_sent") {
4677
5184
  try {
4678
5185
  await this.reconnectAfterDrop(err);
@@ -4688,29 +5195,31 @@ class SubcClient {
4688
5195
  }
4689
5196
  }
4690
5197
  }
4691
- subscribe(routeChannel, body, onEvent, opts = {}) {
5198
+ subscribe(handle, body, onEvent, opts = {}) {
5199
+ this.assertLiveHandle(handle);
4692
5200
  const bytes = body instanceof Uint8Array ? body : this.encode(body);
4693
- const priority = opts.priority ?? 1 /* Interactive */;
4694
- const corr = this.nextCorr++;
4695
- const key = `${routeChannel}:${corr}`;
5201
+ const priority = opts.priority ?? Priority.Interactive;
5202
+ const admission = opts.admissionClass ?? AdmissionClass.Normal;
5203
+ const corr = this.allocateCorr();
5204
+ const key = pendingKey(handle, corr);
4696
5205
  const closed = new Promise((resolve7, reject) => {
4697
5206
  if (this.closedErr) {
4698
5207
  reject(this.closedErr);
4699
5208
  return;
4700
5209
  }
4701
5210
  this.pending.set(key, {
4702
- channel: routeChannel,
5211
+ handle,
4703
5212
  resolve: () => resolve7(),
4704
5213
  reject,
4705
5214
  onProgress: onEvent,
4706
5215
  timer: null,
4707
5216
  subscription: true
4708
5217
  });
4709
- const frame = buildFrame(0 /* Request */, buildFlags(false, priority, false), routeChannel, corr, bytes);
5218
+ const frame = buildFrame(FrameType.Request, buildFlags(false, priority, false, admission), handle.channel, handle.epoch, corr, bytes);
4710
5219
  this.sock.write(encodeFrame(frame), Date.now() + DEFAULT_REQUEST_TIMEOUT_MS).catch((err) => {
4711
- const p = this.pending.get(key);
4712
- if (p)
4713
- this.rejectPending(key, p, err instanceof Error ? err : new SubcError(String(err)));
5220
+ const pending = this.pending.get(key);
5221
+ if (pending)
5222
+ this.rejectPending(key, pending, err instanceof Error ? err : new SubcError(String(err)));
4714
5223
  });
4715
5224
  });
4716
5225
  let cancelled = false;
@@ -4718,45 +5227,77 @@ class SubcClient {
4718
5227
  if (cancelled)
4719
5228
  return;
4720
5229
  cancelled = true;
4721
- const cancel = buildFrame(6 /* Cancel */, buildFlags(false, priority, false), routeChannel, corr, EMPTY_BODY);
4722
- this.sock.write(encodeFrame(cancel), Date.now() + DEFAULT_REQUEST_TIMEOUT_MS).catch(() => {});
5230
+ this.cancel(handle, corr, priority);
4723
5231
  };
4724
5232
  return { unsubscribe, closed };
4725
5233
  }
4726
- async closeRoute(target, identity, opts = {}) {
5234
+ cancel(handle, corr, priority = Priority.Interactive) {
5235
+ this.assertLiveHandle(handle);
5236
+ const cancel = buildFrame(FrameType.Cancel, buildFlags(false, priority, false), handle.channel, handle.epoch, corr, EMPTY_BODY);
5237
+ this.sock.write(encodeFrame(cancel), Date.now() + DEFAULT_REQUEST_TIMEOUT_MS).catch(() => {
5238
+ return;
5239
+ });
5240
+ }
5241
+ async routePoll(handle, kind) {
5242
+ this.assertLiveHandle(handle);
5243
+ const body = this.encode({
5244
+ op: "route.poll",
5245
+ route_channel: handle.channel,
5246
+ route_epoch: handle.epoch,
5247
+ kind
5248
+ });
5249
+ const reply = await this.controlRpc(body, (frame) => {
5250
+ if (frame.header.ty !== FrameType.Response)
5251
+ return true;
5252
+ const parsed = this.parseJson(frame);
5253
+ return parsed.route_channel === handle.channel && parsed.route_epoch === handle.epoch;
5254
+ });
5255
+ return this.parseJson(reply);
5256
+ }
5257
+ async closeRoute(handle, opts = {}) {
5258
+ this.assertLiveHandle(handle);
5259
+ for (const [key, cached] of this.routes) {
5260
+ if (cached.handle && sameRouteHandle(cached.handle, handle)) {
5261
+ cached.closed = true;
5262
+ cached.handle = null;
5263
+ this.routes.delete(key);
5264
+ }
5265
+ }
5266
+ if (opts.drain)
5267
+ await this.drainUnaryOnHandle(handle);
5268
+ this.failHandle(handle, new SubcError("route closed by closeRoute", "route_closed"));
5269
+ if (this.liveRoutes.get(handle.channel) === handle)
5270
+ this.liveRoutes.delete(handle.channel);
5271
+ this.sendRouteGoodbye(handle);
5272
+ }
5273
+ async closeManagedRoute(target, identity, opts = {}) {
4727
5274
  const key = routeCacheKey(target, identity, routeOpenConsumerIdentity(opts));
4728
5275
  const cached = this.routes.get(key);
4729
5276
  if (!cached)
4730
5277
  return;
4731
5278
  cached.closed = true;
4732
5279
  this.routes.delete(key);
4733
- const channel = cached.channel;
4734
- cached.channel = null;
4735
- if (channel !== null)
4736
- await this.closeRouteChannel(channel, opts);
5280
+ const handle = cached.handle;
5281
+ cached.handle = null;
5282
+ if (handle)
5283
+ await this.closeRoute(handle, opts);
4737
5284
  }
4738
- async closeRouteChannel(channel, opts = {}) {
4739
- if (channel === 0)
4740
- return;
4741
- if (opts.drain) {
4742
- await this.drainUnaryOnChannel(channel);
4743
- }
4744
- this.failChannel(channel, new SubcError("route closed by closeRoute", "route_closed"));
4745
- this.sendRouteGoodbye(channel);
5285
+ async closeRouteChannel(handle, opts = {}) {
5286
+ await this.closeRoute(handle, opts);
4746
5287
  }
4747
5288
  close() {
4748
5289
  this.closeStarted = true;
4749
5290
  this.fail(new SubcError("client closed"));
4750
5291
  this.sock.close();
4751
5292
  }
4752
- drainUnaryOnChannel(channel) {
5293
+ drainUnaryOnHandle(handle) {
4753
5294
  const waiters = [];
4754
5295
  for (const pending of this.pending.values()) {
4755
- if (pending.channel === channel && !pending.subscription) {
5296
+ if (pending.handle === handle && !pending.subscription) {
4756
5297
  waiters.push(new Promise((resolve7) => {
4757
- const prev = pending.onSettle;
5298
+ const previous = pending.onSettle;
4758
5299
  pending.onSettle = () => {
4759
- prev?.();
5300
+ previous?.();
4760
5301
  resolve7();
4761
5302
  };
4762
5303
  }));
@@ -4766,11 +5307,21 @@ class SubcClient {
4766
5307
  return;
4767
5308
  });
4768
5309
  }
4769
- sendRouteGoodbye(channel) {
4770
- if (this.closedErr)
5310
+ sendRouteGoodbye(handle, closeOnQueueFailure = false) {
5311
+ this.assertLiveConnection(handle);
5312
+ if (this.closedErr) {
5313
+ if (closeOnQueueFailure)
5314
+ this.closeConnectionAfterCleanupFailure();
4771
5315
  return;
4772
- const goodbye = buildFrame(11 /* Goodbye */, buildFlags(false, 1 /* Interactive */, false), channel, 0n, EMPTY_BODY);
4773
- this.sock.write(encodeFrame(goodbye), Date.now() + DEFAULT_REQUEST_TIMEOUT_MS).catch(() => {});
5316
+ }
5317
+ const goodbye = buildFrame(FrameType.Goodbye, buildFlags(false, Priority.Interactive, false), handle.channel, handle.epoch, 0n, EMPTY_BODY);
5318
+ const write = this.sock.writeTracked(encodeFrame(goodbye), Date.now() + DEFAULT_REQUEST_TIMEOUT_MS);
5319
+ if (!write.queued && closeOnQueueFailure)
5320
+ this.closeConnectionAfterCleanupFailure();
5321
+ write.completed.catch(() => {
5322
+ if (closeOnQueueFailure && !write.queued)
5323
+ this.closeConnectionAfterCleanupFailure();
5324
+ });
4774
5325
  }
4775
5326
  static async openConnection(opts) {
4776
5327
  const conn = await readConnectionFile(opts.connectionFile);
@@ -4785,37 +5336,48 @@ class SubcClient {
4785
5336
  }
4786
5337
  return { sock, conn };
4787
5338
  }
4788
- async controlRpc(body) {
4789
- return this.send(0, body, 1 /* Interactive */, undefined, undefined);
5339
+ async controlRpc(body, acceptFrame, onLateResponse) {
5340
+ return this.send(null, body, Priority.Interactive, AdmissionClass.Normal, undefined, undefined, acceptFrame, onLateResponse);
4790
5341
  }
4791
- send(channel, body, priority, timeoutMs, onProgress) {
5342
+ send(handle, body, priority, admission, timeoutMs, onProgress, acceptFrame, onLateResponse) {
5343
+ if (handle)
5344
+ this.assertLiveHandle(handle);
4792
5345
  if (this.closedErr)
4793
5346
  return Promise.reject(this.closedErr);
4794
- const corr = this.nextCorr++;
4795
- const key = `${channel}:${corr}`;
4796
- const frame = buildFrame(0 /* Request */, buildFlags(false, priority, false), channel, corr, body);
5347
+ let corr;
5348
+ try {
5349
+ corr = this.allocateCorr();
5350
+ } catch (error2) {
5351
+ return Promise.reject(error2);
5352
+ }
5353
+ const key = pendingKey(handle, corr);
5354
+ const channel = handle?.channel ?? 0;
5355
+ const epoch = handle?.epoch ?? 0;
5356
+ const frame = buildFrame(FrameType.Request, buildFlags(false, priority, false, admission), channel, epoch, corr, body);
4797
5357
  return new Promise((resolve7, reject) => {
4798
5358
  const ms = timeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
4799
5359
  const pending = {
4800
- channel,
5360
+ handle,
4801
5361
  resolve: resolve7,
4802
5362
  reject,
4803
5363
  onProgress,
4804
- timer: null
5364
+ timer: null,
5365
+ acceptFrame,
5366
+ onLateResponse
4805
5367
  };
4806
- pending.timer = setTimeout(() => {
4807
- this.arbitrateTimeout(key, pending, channel, corr, ms);
4808
- }, ms);
5368
+ pending.timer = setTimeout(() => this.arbitrateTimeout(key, pending, channel, corr, ms), ms);
4809
5369
  this.pending.set(key, pending);
4810
- this.sock.write(encodeFrame(frame), Date.now() + ms).catch((err) => {
4811
- const p = this.pending.get(key);
4812
- if (p)
4813
- this.rejectPending(key, p, err instanceof Error ? err : new SubcError(String(err)));
5370
+ this.sock.write(encodeFrame(frame), Date.now() + ms).catch((error2) => {
5371
+ const current = this.pending.get(key);
5372
+ if (current)
5373
+ this.rejectPending(key, current, error2 instanceof Error ? error2 : new SubcError(String(error2)));
4814
5374
  });
4815
5375
  });
4816
5376
  }
4817
5377
  arbitrateTimeout(key, pending, channel, corr, ms) {
4818
5378
  const settleAsTimeout = () => {
5379
+ if (pending.onLateResponse)
5380
+ this.lateResponses.set(key, pending.onLateResponse);
4819
5381
  this.rejectPending(key, pending, new SubcError(this.timeoutMessage(channel, corr, ms), REQUEST_DEADLINE_MARKER));
4820
5382
  };
4821
5383
  const graceDeadline = Date.now() + this.opts.timeoutArbitrationGraceMs;
@@ -4831,59 +5393,67 @@ class SubcClient {
4831
5393
  };
4832
5394
  setImmediate(arbitrate);
4833
5395
  }
4834
- async managedRequest(routeChannel, body, opts) {
5396
+ async managedRequest(handle, body, opts) {
4835
5397
  const bytes = body instanceof Uint8Array ? body : this.encode(body);
4836
- const priority = opts.priority ?? 1 /* Interactive */;
5398
+ const priority = opts.priority ?? Priority.Interactive;
5399
+ const admission = opts.admissionClass ?? AdmissionClass.Normal;
4837
5400
  try {
4838
- const reply = await this.sendManaged(routeChannel, bytes, priority, opts.timeoutMs, opts.onProgress);
5401
+ const reply = await this.sendManaged(handle, bytes, priority, admission, opts.timeoutMs, opts.onProgress);
4839
5402
  return this.parseJson(reply);
4840
- } catch (err) {
4841
- if (err instanceof SubcCallError)
4842
- throw err;
4843
- throw this.terminalCallError("managed call failed", err);
5403
+ } catch (error2) {
5404
+ if (error2 instanceof SubcCallError)
5405
+ throw error2;
5406
+ throw this.terminalCallError("managed call failed", error2);
4844
5407
  }
4845
5408
  }
4846
- sendManaged(channel, body, priority, timeoutMs, onProgress) {
5409
+ sendManaged(handle, body, priority, admission, timeoutMs, onProgress) {
5410
+ try {
5411
+ this.assertLiveHandle(handle);
5412
+ } catch (error2) {
5413
+ return Promise.reject(this.notSentCallError("request used a stale route handle", error2));
5414
+ }
4847
5415
  if (this.closedErr) {
4848
5416
  return Promise.reject(this.notSentCallError("request was not sent because the subc connection was already closed", this.closedErr));
4849
5417
  }
4850
- const corr = this.nextCorr++;
4851
- const key = `${channel}:${corr}`;
4852
- const frame = buildFrame(0 /* Request */, buildFlags(false, priority, false), channel, corr, body);
5418
+ let corr;
5419
+ try {
5420
+ corr = this.allocateCorr();
5421
+ } catch (error2) {
5422
+ return Promise.reject(this.notSentCallError("request correlation allocator was exhausted", error2));
5423
+ }
5424
+ const key = pendingKey(handle, corr);
5425
+ const frame = buildFrame(FrameType.Request, buildFlags(false, priority, false, admission), handle.channel, handle.epoch, corr, body);
4853
5426
  let handedToSocket = false;
4854
- const classifyFailure = (err) => {
4855
- if (!handedToSocket) {
4856
- return this.notSentCallError("request bytes were not queued to the subc socket", err);
5427
+ const classifyFailure = (error2) => {
5428
+ if (!handedToSocket)
5429
+ return this.notSentCallError("request bytes were not queued to the subc socket", error2);
5430
+ if (error2 instanceof SubcError && error2.code === REQUEST_DEADLINE_MARKER) {
5431
+ return new SubcCallError("outcome_unknown", `managed call deadline exceeded after request bytes were queued to the local socket; no terminal response was observed; outcome unknown${causeMessage(error2)}`, DEADLINE_NO_DROP_CODE, error2);
4857
5432
  }
4858
- if (err instanceof SubcError && err.code === REQUEST_DEADLINE_MARKER) {
4859
- return new SubcCallError("outcome_unknown", `managed call deadline exceeded after request bytes were queued to the local socket; no terminal response was observed; outcome unknown${causeMessage(err)}`, DEADLINE_NO_DROP_CODE, err);
4860
- }
4861
- return this.outcomeUnknownCallError("connection dropped before the managed call returned a response", err);
5433
+ return this.outcomeUnknownCallError("connection dropped before the managed call returned a response", error2);
4862
5434
  };
4863
5435
  return new Promise((resolve7, reject) => {
4864
5436
  const ms = timeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
4865
5437
  const pending = {
4866
- channel,
5438
+ handle,
4867
5439
  resolve: resolve7,
4868
5440
  reject,
4869
5441
  onProgress,
4870
5442
  timer: null,
4871
5443
  classifyFailure
4872
5444
  };
4873
- pending.timer = setTimeout(() => {
4874
- this.arbitrateTimeout(key, pending, channel, corr, ms);
4875
- }, ms);
5445
+ pending.timer = setTimeout(() => this.arbitrateTimeout(key, pending, handle.channel, corr, ms), ms);
4876
5446
  this.pending.set(key, pending);
4877
5447
  const write = this.sock.writeTracked(encodeFrame(frame), Date.now() + ms);
4878
5448
  handedToSocket = write.queued;
4879
- write.completed.catch((err) => {
4880
- const p = this.pending.get(key);
4881
- if (p)
4882
- this.rejectPending(key, p, err instanceof Error ? err : new SubcError(String(err)));
5449
+ write.completed.catch((error2) => {
5450
+ const current = this.pending.get(key);
5451
+ if (current)
5452
+ this.rejectPending(key, current, error2 instanceof Error ? error2 : new SubcError(String(error2)));
4883
5453
  });
4884
5454
  });
4885
5455
  }
4886
- async cachedRouteChannel(moduleId, opts) {
5456
+ async cachedRouteHandle(moduleId, opts) {
4887
5457
  const identity = opts.identity ?? this.opts.identity;
4888
5458
  if (!identity) {
4889
5459
  throw new SubcCallError("terminal", "managed call requires a BindIdentity in SubcClient.connect({ identity }) or call(..., { identity })", "missing_identity");
@@ -4899,15 +5469,13 @@ class SubcClient {
4899
5469
  target,
4900
5470
  identity,
4901
5471
  consumerIdentity,
4902
- channel: null,
4903
- generation: 0,
5472
+ handle: null,
4904
5473
  opening: null
4905
5474
  };
4906
5475
  this.routes.set(key, cached);
4907
5476
  }
4908
- if (cached.channel !== null && cached.generation === this.generation && !this.closedErr) {
4909
- return cached.channel;
4910
- }
5477
+ if (cached.handle && this.isLiveHandle(cached.handle))
5478
+ return cached.handle;
4911
5479
  if (!cached.opening) {
4912
5480
  cached.opening = this.openCachedRoute(cached).finally(() => {
4913
5481
  cached.opening = null;
@@ -4924,44 +5492,43 @@ class SubcClient {
4924
5492
  throw this.routeClosedDuringOpen();
4925
5493
  try {
4926
5494
  await this.ensureConnectedForManaged();
4927
- } catch (err) {
4928
- throw this.notSentRecoveryError("route.open could not run because reconnect failed", err);
4929
- }
4930
- if (cached.channel !== null && cached.generation === this.generation && !this.closedErr) {
4931
- return cached.channel;
5495
+ } catch (error2) {
5496
+ throw this.notSentRecoveryError("route.open could not run because reconnect failed", error2);
4932
5497
  }
5498
+ if (cached.handle && this.isLiveHandle(cached.handle))
5499
+ return cached.handle;
4933
5500
  try {
4934
- const channel = await this.routeOpen(cached.target, cached.identity, {
5501
+ const handle = await this.routeOpen(cached.target, cached.identity, {
4935
5502
  consumerIdentity: cached.consumerIdentity ?? null
4936
5503
  });
4937
5504
  if (cached.closed) {
4938
- this.sendRouteGoodbye(channel);
5505
+ this.liveRoutes.delete(handle.channel);
5506
+ this.sendRouteGoodbye(handle);
4939
5507
  throw this.routeClosedDuringOpen();
4940
5508
  }
4941
- cached.channel = channel;
4942
- cached.generation = this.generation;
4943
- return channel;
4944
- } catch (err) {
4945
- if (err instanceof SubcCallError && err.code === "route_closed")
4946
- throw err;
4947
- if (!this.closeStarted && isConsumerReconnectTransient(err)) {
5509
+ cached.handle = handle;
5510
+ return handle;
5511
+ } catch (error2) {
5512
+ if (error2 instanceof SubcCallError && error2.code === "route_closed")
5513
+ throw error2;
5514
+ if (!this.closeStarted && isConsumerReconnectTransient(error2)) {
4948
5515
  try {
4949
- await this.reconnectAfterDrop(err);
4950
- } catch (reconnectErr) {
4951
- throw this.notSentRecoveryError("route.open was not sent and reconnect failed", reconnectErr);
5516
+ await this.reconnectAfterDrop(error2);
5517
+ } catch (reconnectError) {
5518
+ throw this.notSentRecoveryError("route.open was not sent and reconnect failed", reconnectError);
4952
5519
  }
4953
5520
  continue;
4954
5521
  }
4955
- if (!this.closeStarted && err instanceof SubcError && isRetryableRouteOpenCode(err.code)) {
5522
+ if (!this.closeStarted && error2 instanceof SubcError && isRetryableRouteOpenCode(error2.code)) {
4956
5523
  routeRetryAttempt += 1;
4957
5524
  if (routeRetryAttempt < this.opts.reconnectBackoff.maxAttempts && Date.now() < routeRetryDeadline) {
4958
5525
  await this.opts.sleep(routeRetryDelay);
4959
5526
  routeRetryDelay = Math.min(routeRetryDelay * 2, this.opts.reconnectBackoff.capMs);
4960
5527
  continue;
4961
5528
  }
4962
- throw this.notSentCallError(`route.open failed for module ${cached.moduleId}: ${err.code} (retry budget exhausted)`, err);
5529
+ throw this.notSentCallError(`route.open failed for module ${cached.moduleId}: ${error2.code} (retry budget exhausted)`, error2);
4963
5530
  }
4964
- throw this.terminalCallError(`route.open failed for module ${cached.moduleId}`, err);
5531
+ throw this.terminalCallError(`route.open failed for module ${cached.moduleId}`, error2);
4965
5532
  }
4966
5533
  }
4967
5534
  }
@@ -5008,6 +5575,9 @@ class SubcClient {
5008
5575
  return;
5009
5576
  } catch (err) {
5010
5577
  if (!isConsumerReconnectTransient(err) || attempt >= this.opts.reconnectBackoff.maxAttempts) {
5578
+ if (err instanceof AuthError && attempt > 1) {
5579
+ throw new AuthError(`reconnect gave up after ${attempt} attempts: ${err.message} — the connection file and the daemon's key disagree persistently ` + `(daemon restarting in a loop, split connection-file paths, or a genuinely foreign daemon on this port); ` + `check the daemon, then restart this host app`);
5580
+ }
5011
5581
  throw err;
5012
5582
  }
5013
5583
  await this.opts.sleep(delay);
@@ -5021,25 +5591,27 @@ class SubcClient {
5021
5591
  this.currentConn = opened.conn;
5022
5592
  this.closedErr = null;
5023
5593
  this.generation += 1;
5594
+ this.connectionToken = newConnectionToken();
5595
+ this.liveRoutes.clear();
5596
+ this.lateResponses.clear();
5597
+ this.nextCorr = 1n;
5024
5598
  this.readLoop(opened.sock, this.generation);
5025
5599
  }
5026
5600
  async reopenCachedRoutes() {
5027
- for (const cached of this.routes.values()) {
5028
- cached.channel = null;
5029
- cached.generation = 0;
5030
- }
5601
+ for (const cached of this.routes.values())
5602
+ cached.handle = null;
5031
5603
  for (const cached of this.routes.values()) {
5032
5604
  if (cached.closed)
5033
5605
  continue;
5034
- const channel = await this.routeOpen(cached.target, cached.identity, {
5606
+ const handle = await this.routeOpen(cached.target, cached.identity, {
5035
5607
  consumerIdentity: cached.consumerIdentity ?? null
5036
5608
  });
5037
5609
  if (cached.closed) {
5038
- this.sendRouteGoodbye(channel);
5610
+ this.liveRoutes.delete(handle.channel);
5611
+ this.sendRouteGoodbye(handle);
5039
5612
  continue;
5040
5613
  }
5041
- cached.channel = channel;
5042
- cached.generation = this.generation;
5614
+ cached.handle = handle;
5043
5615
  }
5044
5616
  }
5045
5617
  timeoutMessage(channel, corr, ms) {
@@ -5053,52 +5625,69 @@ class SubcClient {
5053
5625
  async readLoop(sock, generation) {
5054
5626
  try {
5055
5627
  for (;; ) {
5056
- const headerBytes = await sock.readExact(HEADER_LEN, Number.POSITIVE_INFINITY);
5057
- this.readerActive = true;
5628
+ this.readerActive = false;
5629
+ const frame = await sock.readFrame(Number.POSITIVE_INFINITY, { afterHeaderMs: BODY_READ_TIMEOUT_MS }, () => {
5630
+ this.readerActive = true;
5631
+ });
5058
5632
  try {
5059
- const header = decodeHeader(headerBytes);
5060
- const body = header.len === 0 ? new Uint8Array(0) : await sock.readExact(header.len, Date.now() + BODY_READ_TIMEOUT_MS);
5061
- if (this.sock === sock && this.generation === generation) {
5062
- this.dispatch({ header, body });
5063
- }
5633
+ if (this.sock === sock && this.generation === generation)
5634
+ this.dispatch(frame);
5064
5635
  } finally {
5065
5636
  this.readerActive = false;
5066
5637
  }
5067
5638
  }
5068
- } catch (err) {
5639
+ } catch (error2) {
5069
5640
  if (this.sock === sock && this.generation === generation) {
5070
- this.fail(err instanceof Error ? err : new SubcError(String(err)));
5641
+ this.fail(error2 instanceof Error ? error2 : new SubcError(String(error2)));
5071
5642
  }
5072
5643
  }
5073
5644
  }
5074
5645
  dispatch(frame) {
5075
- const key = `${frame.header.channel}:${frame.header.corr}`;
5646
+ let handle = null;
5647
+ if (frame.header.channel !== 0) {
5648
+ handle = this.liveRoutes.get(frame.header.channel) ?? null;
5649
+ if (!handle || handle.epoch !== frame.header.epoch) {
5650
+ this.ingressEpochDropCount += 1;
5651
+ return;
5652
+ }
5653
+ }
5654
+ const key = pendingKey(handle, frame.header.corr);
5076
5655
  const pending = this.pending.get(key);
5077
5656
  if (pending) {
5657
+ if (pending.acceptFrame && !pending.acceptFrame(frame))
5658
+ return;
5078
5659
  switch (frame.header.ty) {
5079
- case 2 /* Push */:
5080
- case 3 /* StreamData */:
5660
+ case FrameType.Push:
5661
+ case FrameType.StreamData:
5081
5662
  pending.onProgress?.(frame.body);
5082
5663
  return;
5083
- case 1 /* Response */:
5084
- case 4 /* StreamEnd */:
5664
+ case FrameType.Response:
5665
+ case FrameType.StreamEnd:
5085
5666
  this.settle(key, pending, () => pending.resolve(frame));
5086
5667
  return;
5087
- case 5 /* Error */:
5668
+ case FrameType.Error:
5088
5669
  this.settle(key, pending, () => pending.reject(this.errorFromFrame(frame)));
5089
5670
  return;
5090
5671
  default:
5091
5672
  return;
5092
5673
  }
5093
5674
  }
5094
- if (frame.header.ty === 11 /* Goodbye */) {
5095
- this.failChannel(frame.header.channel, new SubcError("route closed by subc (GOODBYE)"));
5675
+ const late = this.lateResponses.get(key);
5676
+ if (late && (frame.header.ty === FrameType.Response || frame.header.ty === FrameType.Error)) {
5677
+ this.lateResponses.delete(key);
5678
+ late(frame);
5096
5679
  return;
5097
5680
  }
5098
- if (frame.header.ty === 1 /* Response */ || frame.header.ty === 5 /* Error */ || frame.header.ty === 4 /* StreamEnd */) {
5099
- debug("dropped terminal frame with no waiter: type=%d channel=%d corr=%s port=%s", frame.header.ty, frame.header.channel, frame.header.corr, this.sock.localPort() ?? "?");
5681
+ if (frame.header.ty === FrameType.Goodbye && handle) {
5682
+ this.failHandle(handle, new SubcError("route closed by subc (GOODBYE)"));
5683
+ if (this.liveRoutes.get(handle.channel) === handle)
5684
+ this.liveRoutes.delete(handle.channel);
5685
+ this.evictRouteHandle(handle);
5100
5686
  return;
5101
5687
  }
5688
+ if (frame.header.ty === FrameType.Response || frame.header.ty === FrameType.Error || frame.header.ty === FrameType.StreamEnd) {
5689
+ debug("dropped terminal frame with no waiter: type=%d channel=%d epoch=%d corr=%s port=%s", frame.header.ty, frame.header.channel, frame.header.epoch, frame.header.corr, this.sock.localPort() ?? "?");
5690
+ }
5102
5691
  }
5103
5692
  settle(key, pending, run) {
5104
5693
  if (this.pending.get(key) !== pending)
@@ -5121,11 +5710,16 @@ class SubcClient {
5121
5710
  return new SubcError(Buffer.from(frame.body).toString("utf8") || "subc error");
5122
5711
  }
5123
5712
  }
5124
- failChannel(channel, err) {
5713
+ evictRouteHandle(handle) {
5714
+ for (const cached of this.routes.values()) {
5715
+ if (cached.handle && sameRouteHandle(cached.handle, handle))
5716
+ cached.handle = null;
5717
+ }
5718
+ }
5719
+ failHandle(handle, error2) {
5125
5720
  for (const [key, pending] of this.pending) {
5126
- if (pending.channel === channel) {
5127
- this.rejectPending(key, pending, err);
5128
- }
5721
+ if (pending.handle && sameRouteHandle(pending.handle, handle))
5722
+ this.rejectPending(key, pending, error2);
5129
5723
  }
5130
5724
  }
5131
5725
  fail(err) {
@@ -5153,6 +5747,44 @@ class SubcClient {
5153
5747
  return this.notSentCallError(message, cause);
5154
5748
  return this.terminalCallError(message, cause);
5155
5749
  }
5750
+ get droppedIngressFrames() {
5751
+ return this.ingressEpochDropCount;
5752
+ }
5753
+ installRoute(channel, epoch) {
5754
+ const handle = createRouteHandle(channel, epoch, this.connectionToken);
5755
+ this.liveRoutes.set(channel, handle);
5756
+ return handle;
5757
+ }
5758
+ isLiveHandle(handle) {
5759
+ return belongsToConnection(handle, this.connectionToken) && this.liveRoutes.get(handle.channel) === handle;
5760
+ }
5761
+ assertLiveConnection(handle) {
5762
+ if (!belongsToConnection(handle, this.connectionToken))
5763
+ throw new StaleRouteHandleError(handle);
5764
+ }
5765
+ assertLiveHandle(handle) {
5766
+ if (!this.isLiveHandle(handle))
5767
+ throw new StaleRouteHandleError(handle);
5768
+ }
5769
+ allocateCorr() {
5770
+ const maximum = 0xffffffffffffffffn;
5771
+ if (this.nextCorr > maximum) {
5772
+ const error2 = new SubcError("channel-0 correlation id allocator exhausted", "corr_exhausted");
5773
+ this.fail(error2);
5774
+ this.sock.close();
5775
+ this.scheduleReconnectAfterDrop(error2);
5776
+ throw error2;
5777
+ }
5778
+ const corr = this.nextCorr;
5779
+ this.nextCorr += 1n;
5780
+ return corr;
5781
+ }
5782
+ closeConnectionAfterCleanupFailure() {
5783
+ const error2 = new SubcError("late route cleanup could not be queued", "late_route_cleanup_failed");
5784
+ this.fail(error2);
5785
+ this.sock.close();
5786
+ this.scheduleReconnectAfterDrop(error2);
5787
+ }
5156
5788
  encode(value) {
5157
5789
  return new Uint8Array(Buffer.from(JSON.stringify(value), "utf8"));
5158
5790
  }
@@ -5167,7 +5799,9 @@ function isConsumerReconnectTransient(err) {
5167
5799
  return true;
5168
5800
  if (err instanceof SubcCallError)
5169
5801
  return err.kind === "not_sent" || err.kind === "outcome_unknown";
5170
- if (err instanceof SubcError || err instanceof ConnectionFileError || err instanceof AuthError)
5802
+ if (err instanceof AuthError)
5803
+ return true;
5804
+ if (err instanceof SubcError || err instanceof ConnectionFileError)
5171
5805
  return false;
5172
5806
  const code = errorCode(err);
5173
5807
  return code === "ECONNREFUSED" || code === "ECONNRESET" || code === "EPIPE" || code === "ETIMEDOUT" || code === "ENOENT";
@@ -5220,11 +5854,15 @@ function causeMessage(cause) {
5220
5854
  return "";
5221
5855
  return `: ${cause instanceof Error ? cause.message : String(cause)}`;
5222
5856
  }
5223
- var debug, DEFAULT_HANDSHAKE_TIMEOUT_MS = 1e4, DEFAULT_REQUEST_TIMEOUT_MS = 30000, TIMEOUT_ARBITRATION_GRACE_MS = 50, REQUEST_DEADLINE_MARKER = "request_deadline", DEADLINE_NO_DROP_CODE = "deadline_exceeded_no_drop_observed", ROUTE_OPEN_RETRY_DEADLINE_MS = 1e4, BODY_READ_TIMEOUT_MS = 30000, EMPTY_BODY, DEFAULT_MANAGED_TARGET_KIND = "management_surface", SUBC_MODULE_ID_ENV = "SUBC_MODULE_ID", SUBC_LAUNCH_NONCE_ENV = "SUBC_LAUNCH_NONCE", DEFAULT_RECONNECT_BACKOFF, SubcCallError, SubcError;
5857
+ function pendingKey(handle, corr) {
5858
+ return handle ? `${handle.channel}:${handle.epoch}:${corr}` : `0:0:${corr}`;
5859
+ }
5860
+ var debug, DEFAULT_HANDSHAKE_TIMEOUT_MS = 1e4, DEFAULT_REQUEST_TIMEOUT_MS = 30000, TIMEOUT_ARBITRATION_GRACE_MS = 50, REQUEST_DEADLINE_MARKER = "request_deadline", DEADLINE_NO_DROP_CODE = "deadline_exceeded_no_drop_observed", ROUTE_OPEN_RETRY_DEADLINE_MS = 30000, BODY_READ_TIMEOUT_MS = 30000, EMPTY_BODY, DEFAULT_MANAGED_TARGET_KIND = "management_surface", SUBC_MODULE_ID_ENV = "SUBC_MODULE_ID", SUBC_LAUNCH_NONCE_ENV = "SUBC_LAUNCH_NONCE", DEFAULT_RECONNECT_BACKOFF, SubcCallError, SubcError;
5224
5861
  var init_client = __esm(() => {
5225
5862
  init_auth();
5226
5863
  init_connection_file();
5227
5864
  init_envelope();
5865
+ init_route_handle();
5228
5866
  init_socket();
5229
5867
  debug = debuglog("subc-client");
5230
5868
  EMPTY_BODY = new Uint8Array(0);
@@ -5254,9 +5892,44 @@ var init_client = __esm(() => {
5254
5892
  };
5255
5893
  });
5256
5894
 
5257
- // ../../node_modules/.bun/@cortexkit+subc-client@0.3.0/node_modules/@cortexkit/subc-client/src/provider.ts
5895
+ // ../../node_modules/.bun/@cortexkit+subc-client@0.5.0/node_modules/@cortexkit/subc-client/dist/provider.js
5258
5896
  import { Buffer as Buffer2 } from "node:buffer";
5259
5897
 
5898
+ class AsyncPermitPool {
5899
+ available;
5900
+ waiters = [];
5901
+ constructor(capacity) {
5902
+ if (!Number.isInteger(capacity) || capacity <= 0) {
5903
+ throw new SubcProviderError("provider handler capacity must be a positive integer", "invalid_handler_capacity");
5904
+ }
5905
+ this.available = capacity;
5906
+ }
5907
+ async acquire() {
5908
+ if (this.available > 0) {
5909
+ this.available -= 1;
5910
+ return this.releaseOnce();
5911
+ }
5912
+ await new Promise((resolve7) => {
5913
+ this.waiters.push(resolve7);
5914
+ });
5915
+ return this.releaseOnce();
5916
+ }
5917
+ releaseOnce() {
5918
+ let released = false;
5919
+ return () => {
5920
+ if (released)
5921
+ return;
5922
+ released = true;
5923
+ const next = this.waiters.shift();
5924
+ if (next) {
5925
+ next();
5926
+ } else {
5927
+ this.available += 1;
5928
+ }
5929
+ };
5930
+ }
5931
+ }
5932
+
5260
5933
  class SubcProvider {
5261
5934
  sock;
5262
5935
  currentConn;
@@ -5268,6 +5941,12 @@ class SubcProvider {
5268
5941
  closeStarted = false;
5269
5942
  closedErr = null;
5270
5943
  inflight = new Map;
5944
+ pending = new Map;
5945
+ liveRoutes = new Map;
5946
+ connectionToken = newConnectionToken();
5947
+ nextCorr = 1n;
5948
+ ingressEpochDropCount = 0;
5949
+ requestGate = new AsyncPermitPool(DEFAULT_PROVIDER_HANDLER_CAPACITY);
5271
5950
  reconnecting = null;
5272
5951
  generation = 1;
5273
5952
  connectionEpoch = 1;
@@ -5286,12 +5965,55 @@ class SubcProvider {
5286
5965
  this.readLoop(sock, this.generation);
5287
5966
  this.enqueueConnectionState({ state: "connected", epoch: this.connectionEpoch });
5288
5967
  }
5968
+ get droppedIngressFrames() {
5969
+ return this.ingressEpochDropCount;
5970
+ }
5289
5971
  get conn() {
5290
5972
  return this.currentConn;
5291
5973
  }
5292
5974
  currentEpoch() {
5293
5975
  return this.connectionEpoch;
5294
5976
  }
5977
+ async request(handle, body, opts = {}) {
5978
+ this.assertLiveHandle(handle);
5979
+ const corr = this.allocateCorr();
5980
+ const key = routeKey(handle, corr);
5981
+ const timeoutMs = opts.timeoutMs ?? WRITE_TIMEOUT_MS;
5982
+ const frame = buildFrame(FrameType.Request, buildFlags(false, opts.priority ?? Priority.Interactive, false, opts.admissionClass ?? AdmissionClass.Normal), handle.channel, handle.epoch, corr, body);
5983
+ return await new Promise((resolve7, reject) => {
5984
+ const timer = setTimeout(() => {
5985
+ if (this.pending.delete(key))
5986
+ reject(new SubcProviderError("reverse request timed out", "request_timeout"));
5987
+ }, timeoutMs);
5988
+ this.pending.set(key, {
5989
+ resolve: (response) => resolve7(response.body),
5990
+ reject,
5991
+ timer
5992
+ });
5993
+ this.sendOn(this.sock, this.generation, frame).catch((error2) => {
5994
+ const pending = this.pending.get(key);
5995
+ if (!pending)
5996
+ return;
5997
+ this.pending.delete(key);
5998
+ clearTimeout(pending.timer);
5999
+ reject(error2 instanceof Error ? error2 : new SubcProviderError(String(error2)));
6000
+ });
6001
+ });
6002
+ }
6003
+ async push(handle, body, opts = {}) {
6004
+ this.assertLiveHandle(handle);
6005
+ await this.sendOn(this.sock, this.generation, buildFrame(FrameType.Push, buildFlags(false, opts.priority ?? Priority.Interactive, false, opts.admissionClass ?? AdmissionClass.Normal), handle.channel, handle.epoch, 0n, body));
6006
+ }
6007
+ cancel(handle, corr) {
6008
+ this.assertLiveHandle(handle);
6009
+ this.sendOn(this.sock, this.generation, buildFrame(FrameType.Cancel, controlFlags(), handle.channel, handle.epoch, corr, new Uint8Array(0)));
6010
+ }
6011
+ closeRoute(handle) {
6012
+ this.assertLiveHandle(handle);
6013
+ this.liveRoutes.delete(handle.channel);
6014
+ this.abortHandle(handle);
6015
+ this.sendOn(this.sock, this.generation, buildFrame(FrameType.Goodbye, controlFlags(), handle.channel, handle.epoch, 0n, new Uint8Array(0)));
6016
+ }
5295
6017
  static async connect(opts) {
5296
6018
  if (opts.manifest.protocol_ver !== PROTOCOL_VERSION) {
5297
6019
  throw new SubcProviderError(`manifest protocol_ver ${opts.manifest.protocol_ver} does not match client protocol ${PROTOCOL_VERSION}`, "invalid_manifest");
@@ -5306,7 +6028,7 @@ class SubcProvider {
5306
6028
  this.cancelRestoredDebounce();
5307
6029
  const sock = this.sock;
5308
6030
  try {
5309
- await sendFrame(sock, buildFrame(11 /* Goodbye */, controlFlags(), 0, 0n, new Uint8Array(0)));
6031
+ await sendFrame(sock, buildFrame(FrameType.Goodbye, controlFlags(), 0, 0, 0n, new Uint8Array(0)));
5310
6032
  } catch {} finally {
5311
6033
  sock.close();
5312
6034
  this.finishClosed();
@@ -5314,12 +6036,13 @@ class SubcProvider {
5314
6036
  }
5315
6037
  await this.closed;
5316
6038
  }
5317
- static async openConnection(opts) {
6039
+ static async openConnection(opts, onSocket) {
5318
6040
  const conn = await readConnectionFile(opts.connectionFile);
5319
6041
  const deadline = Date.now() + (opts.handshakeTimeoutMs ?? DEFAULT_HANDSHAKE_TIMEOUT_MS2);
5320
6042
  const endpoint = conn.endpoints[0];
5321
6043
  const sock = await SubcSocket.connect(endpoint.host, endpoint.port, deadline);
5322
6044
  try {
6045
+ onSocket?.(sock);
5323
6046
  await authenticateClient(sock, conn, deadline);
5324
6047
  await sendFrame(sock, buildHelloFrame(opts));
5325
6048
  const ack = await expectHelloAck(sock, deadline);
@@ -5332,19 +6055,17 @@ class SubcProvider {
5332
6055
  async readLoop(sock, generation) {
5333
6056
  try {
5334
6057
  for (;; ) {
5335
- const headerBytes = await sock.readExact(HEADER_LEN, Number.POSITIVE_INFINITY);
5336
- const header = decodeHeader(headerBytes);
5337
- const body = header.len === 0 ? new Uint8Array(0) : await sock.readExact(header.len, Date.now() + BODY_READ_TIMEOUT_MS2);
5338
- const keepGoing = await this.dispatch({ header, body }, sock, generation);
6058
+ const frame = await sock.readFrame(Number.POSITIVE_INFINITY, { afterHeaderMs: BODY_READ_TIMEOUT_MS2 });
6059
+ const keepGoing = await this.dispatch(frame, sock, generation);
5339
6060
  if (!keepGoing) {
5340
6061
  if (this.sock === sock && this.generation === generation)
5341
6062
  this.closeStarted = true;
5342
6063
  break;
5343
6064
  }
5344
6065
  }
5345
- } catch (err) {
6066
+ } catch (error2) {
5346
6067
  if (this.sock === sock && this.generation === generation && !this.closeStarted) {
5347
- this.handleUnexpectedDrop(sock, generation, err instanceof Error ? err : new SubcProviderError(String(err)));
6068
+ this.handleUnexpectedDrop(sock, generation, error2 instanceof Error ? error2 : new SubcProviderError(String(error2)));
5348
6069
  return;
5349
6070
  }
5350
6071
  } finally {
@@ -5356,28 +6077,58 @@ class SubcProvider {
5356
6077
  }
5357
6078
  }
5358
6079
  async dispatch(frame, sock, generation) {
6080
+ let handle = null;
6081
+ if (frame.header.channel !== 0) {
6082
+ handle = this.liveRoutes.get(frame.header.channel) ?? null;
6083
+ if (!handle || handle.epoch !== frame.header.epoch) {
6084
+ this.ingressEpochDropCount += 1;
6085
+ return true;
6086
+ }
6087
+ }
6088
+ if (handle) {
6089
+ const pendingKey2 = routeKey(handle, frame.header.corr);
6090
+ const pending = this.pending.get(pendingKey2);
6091
+ if (pending) {
6092
+ if (frame.header.ty === FrameType.Push || frame.header.ty === FrameType.StreamData)
6093
+ return true;
6094
+ if (frame.header.ty === FrameType.Response || frame.header.ty === FrameType.StreamEnd) {
6095
+ this.pending.delete(pendingKey2);
6096
+ clearTimeout(pending.timer);
6097
+ pending.resolve(frame);
6098
+ return true;
6099
+ }
6100
+ if (frame.header.ty === FrameType.Error) {
6101
+ this.pending.delete(pendingKey2);
6102
+ clearTimeout(pending.timer);
6103
+ pending.reject(providerErrorFromFrame(frame));
6104
+ return true;
6105
+ }
6106
+ }
6107
+ }
5359
6108
  switch (frame.header.ty) {
5360
- case 7 /* Ping */:
6109
+ case FrameType.Ping:
5361
6110
  if (frame.header.channel === 0) {
5362
- await this.sendOn(sock, generation, buildFrameWithVersion(frame.header.ver, 8 /* Pong */, frame.header.flags, 0, frame.header.corr, new Uint8Array(0)));
6111
+ await this.sendOn(sock, generation, buildFrameWithVersion(frame.header.ver, FrameType.Pong, frame.header.flags, 0, 0, frame.header.corr, new Uint8Array(0)));
5363
6112
  }
5364
6113
  return true;
5365
- case 11 /* Goodbye */:
5366
- if (frame.header.channel === 0)
6114
+ case FrameType.Goodbye:
6115
+ if (!handle)
5367
6116
  return false;
5368
- this.abortChannel(generation, frame.header.channel);
5369
- await this.opts.onRouteGone?.(frame.header.channel);
6117
+ this.liveRoutes.delete(handle.channel);
6118
+ this.abortHandle(handle);
6119
+ await this.opts.onRouteGone?.(handle);
5370
6120
  return true;
5371
- case 6 /* Cancel */:
5372
- this.inflight.get(routeKey(generation, frame.header.channel, frame.header.corr))?.abort();
6121
+ case FrameType.Cancel:
6122
+ if (handle)
6123
+ this.inflight.get(routeKey(handle, frame.header.corr))?.abort();
5373
6124
  return true;
5374
- case 0 /* Request */:
6125
+ case FrameType.Request:
5375
6126
  if (frame.header.channel === 0) {
5376
6127
  await this.handleControlRequest(frame, sock, generation);
5377
- } else {
5378
- this.handleDataRequest(frame, sock, generation).catch((err) => {
6128
+ } else if (handle) {
6129
+ this.handleDataRequest(frame, handle, sock, generation).catch((error2) => {
5379
6130
  if (!this.closeStarted && this.sock === sock && this.generation === generation) {
5380
- console.warn("SubcProvider handler failed after its request was dispatched", err);
6131
+ console.warn("SubcProvider handler failed after its request was dispatched", error2);
5381
6132
  }
5382
6133
  });
5383
6134
  }
@@ -5386,19 +6137,28 @@ class SubcProvider {
5386
6137
  return true;
5387
6138
  }
5388
6139
  }
5389
- abortChannel(generation, channel) {
5390
- const prefix = `${generation}:${channel}:`;
6140
+ abortHandle(handle) {
6141
+ const prefix = `${handle.channel}:${handle.epoch}:`;
5391
6142
  for (const [key, controller] of this.inflight) {
5392
6143
  if (key.startsWith(prefix))
5393
6144
  controller.abort();
5394
6145
  }
6146
+ for (const [key, pending] of this.pending) {
6147
+ if (!key.startsWith(prefix))
6148
+ continue;
6149
+ this.pending.delete(key);
6150
+ clearTimeout(pending.timer);
6151
+ pending.reject(new StaleRouteHandleError(handle));
6152
+ }
5395
6153
  }
5396
- abortGeneration(generation) {
5397
- const prefix = `${generation}:`;
5398
- for (const [key, controller] of this.inflight) {
5399
- if (key.startsWith(prefix))
5400
- controller.abort();
6154
+ abortGeneration(_generation) {
6155
+ this.abortAllInflight();
6156
+ for (const [key, pending] of this.pending) {
6157
+ this.pending.delete(key);
6158
+ clearTimeout(pending.timer);
6159
+ pending.reject(new SubcProviderError("provider connection dropped", "connection_dropped"));
5401
6160
  }
6161
+ this.liveRoutes.clear();
5402
6162
  }
5403
6163
  abortAllInflight() {
5404
6164
  for (const controller of this.inflight.values())
@@ -5406,56 +6166,136 @@ class SubcProvider {
5406
6166
  }
5407
6167
  async handleControlRequest(frame, sock, generation) {
5408
6168
  const request = parseJson(frame.body);
6169
+ if (request.op === HEALTH_CHECK_OP) {
6170
+ this.handleHealthRequest(frame, sock, generation).catch((error2) => {
6171
+ if (!this.closeStarted && this.sock === sock && this.generation === generation) {
6172
+ console.warn("SubcProvider health handler failed after its request was dispatched", error2);
6173
+ }
6174
+ });
6175
+ return;
6176
+ }
5409
6177
  if (request.op !== "route.bind") {
5410
6178
  throw new SubcProviderError(`unsupported module control request ${request.op ?? "<missing op>"}`);
5411
6179
  }
6180
+ const boundChannel = numberField(request.route_channel, "route_channel");
6181
+ const boundEpoch = numberField(request.epoch, "epoch");
6182
+ const stale = this.liveRoutes.get(boundChannel);
6183
+ if (stale) {
6184
+ if (boundEpoch <= stale.epoch) {
6185
+ await this.sendError(frame, "route_rejected", `route.bind epoch ${boundEpoch} does not supersede installed epoch ${stale.epoch} on channel ${boundChannel}`, controlFlags(), sock, generation);
6186
+ return;
6187
+ }
6188
+ this.liveRoutes.delete(stale.channel);
6189
+ this.abortHandle(stale);
6190
+ await this.opts.onRouteGone?.(stale);
6191
+ }
6192
+ const tentative = createRouteHandle(boundChannel, boundEpoch, this.connectionToken);
5412
6193
  const bindRequest = {
5413
- route_channel: numberField(request.route_channel, "route_channel"),
6194
+ handle: tentative,
5414
6195
  target: request.target,
5415
6196
  identity: request.identity,
5416
- principal: request.principal
6197
+ principal: request.principal,
6198
+ consumer_capabilities: request.consumer_capabilities
5417
6199
  };
5418
- const decision = await this.opts.onBind?.(bindRequest);
6200
+ let decision;
6201
+ try {
6202
+ decision = await this.opts.onBind?.(bindRequest);
6203
+ } catch (error2) {
6204
+ try {
6205
+ await this.sendError(frame, "route_rejected", error2 instanceof Error ? error2.message : String(error2), controlFlags(), sock, generation);
6206
+ } finally {
6207
+ await this.opts.onRouteGone?.(tentative);
6208
+ }
6209
+ return;
6210
+ }
5419
6211
  const rejection = bindRejection(decision);
5420
6212
  if (rejection) {
5421
- await this.sendError(frame, rejection.code, rejection.message, controlFlags(), sock, generation);
6213
+ try {
6214
+ await this.sendError(frame, rejection.code, rejection.message, controlFlags(), sock, generation);
6215
+ } finally {
6216
+ await this.opts.onRouteGone?.(tentative);
6217
+ }
5422
6218
  return;
5423
6219
  }
5424
- await this.sendOn(sock, generation, buildFrameWithVersion(frame.header.ver, 1 /* Response */, controlFlags(), 0, frame.header.corr, encodeJson({ op: "route.bind" })));
6220
+ try {
6221
+ await this.sendOn(sock, generation, buildFrameWithVersion(frame.header.ver, FrameType.Response, controlFlags(), 0, 0, frame.header.corr, encodeJson({ op: "route.bind" })));
6222
+ } catch (error2) {
6223
+ await this.opts.onRouteGone?.(tentative);
6224
+ throw error2;
6225
+ }
6226
+ if (this.sock !== sock || this.generation !== generation || this.closeStarted || this.closedErr) {
6227
+ await this.opts.onRouteGone?.(tentative);
6228
+ return;
6229
+ }
6230
+ this.liveRoutes.set(tentative.channel, tentative);
6231
+ await this.opts.onBound?.(tentative);
5425
6232
  }
5426
- async handleDataRequest(frame, sock, generation) {
5427
- const { channel, corr, ver } = frame.header;
5428
- const key = routeKey(generation, channel, corr);
6233
+ async handleDataRequest(frame, handle, sock, generation) {
6234
+ const { corr, ver } = frame.header;
6235
+ const key = routeKey(handle, corr);
5429
6236
  const controller = new AbortController;
5430
6237
  this.inflight.set(key, controller);
5431
- const dataFlags = buildFlags(false, 1 /* Interactive */, false);
5432
- const ctx = {
6238
+ const context = {
6239
+ handle,
5433
6240
  signal: controller.signal,
5434
6241
  currentEpoch: () => this.connectionEpoch,
5435
- emit: async (eventBody) => {
6242
+ emit: async (eventBody, options = {}) => {
6243
+ this.assertLiveHandle(handle);
5436
6244
  if (controller.signal.aborted)
5437
6245
  return;
5438
- await this.sendOn(sock, generation, buildFrameWithVersion(ver, 3 /* StreamData */, dataFlags, channel, corr, eventBody));
6246
+ await this.sendOn(sock, generation, buildFrameWithVersion(ver, FrameType.StreamData, buildFlags(false, options.priority ?? Priority.Interactive, false, options.admissionClass ?? AdmissionClass.Normal), handle.channel, handle.epoch, corr, eventBody));
5439
6247
  }
5440
6248
  };
6249
+ const releasePermit = await (this.requestGate ?? new AsyncPermitPool(DEFAULT_PROVIDER_HANDLER_CAPACITY)).acquire();
6250
+ const dataFlags = buildFlags(false, Priority.Interactive, false);
5441
6251
  try {
5442
- const body = await this.opts.handler(channel, frame.body, ctx);
6252
+ const body = await this.opts.handler(handle, frame.body, context);
6253
+ if (controller.signal.aborted)
6254
+ return;
6255
+ this.assertLiveHandle(handle);
5443
6256
  if (body === undefined) {
5444
- await this.sendOn(sock, generation, buildFrameWithVersion(ver, 4 /* StreamEnd */, dataFlags, channel, corr, new Uint8Array(0)));
6257
+ await this.sendOn(sock, generation, buildFrameWithVersion(ver, FrameType.StreamEnd, dataFlags, handle.channel, handle.epoch, corr, new Uint8Array(0)));
5445
6258
  } else if (body instanceof Uint8Array) {
5446
- await this.sendOn(sock, generation, buildFrameWithVersion(ver, 1 /* Response */, dataFlags, channel, corr, body));
6259
+ await this.sendOn(sock, generation, buildFrameWithVersion(ver, FrameType.Response, dataFlags, handle.channel, handle.epoch, corr, body));
5447
6260
  } else {
5448
6261
  throw new SubcProviderError("provider handler must return a Uint8Array or void", "invalid_handler_response");
5449
6262
  }
5450
- } catch (err) {
5451
- await this.sendError(frame, err instanceof SubcProviderError && err.code ? err.code : "handler_error", err instanceof Error ? err.message : String(err), dataFlags, sock, generation);
6263
+ } catch (error2) {
6264
+ if (error2 instanceof StaleRouteHandleError || controller.signal.aborted)
6265
+ return;
6266
+ await this.sendError(frame, error2 instanceof SubcProviderError && error2.code ? error2.code : "handler_error", error2 instanceof Error ? error2.message : String(error2), dataFlags, sock, generation);
6267
+ } finally {
6268
+ releasePermit();
6269
+ if (this.inflight.get(key) === controller)
6270
+ this.inflight.delete(key);
6271
+ }
6272
+ }
6273
+ async handleHealthRequest(frame, sock, generation) {
6274
+ const { corr, ver } = frame.header;
6275
+ const key = `control:${generation}:${corr}`;
6276
+ const controller = new AbortController;
6277
+ this.inflight.set(key, controller);
6278
+ const releasePermit = await (this.requestGate ?? new AsyncPermitPool(DEFAULT_PROVIDER_HANDLER_CAPACITY)).acquire();
6279
+ try {
6280
+ if (controller.signal.aborted)
6281
+ return;
6282
+ const report = await this.opts.health();
6283
+ await this.sendOn(sock, generation, buildFrameWithVersion(ver, FrameType.Response, controlFlags(), 0, 0, corr, encodeJson({
6284
+ op: HEALTH_CHECK_OP,
6285
+ status: report.status,
6286
+ ...report.detail === undefined ? {} : { detail: report.detail },
6287
+ ...report.metrics === undefined ? {} : { metrics: report.metrics }
6288
+ })));
6289
+ } catch (error2) {
6290
+ await this.sendError(frame, error2 instanceof SubcProviderError && error2.code ? error2.code : "health_error", error2 instanceof Error ? error2.message : String(error2), controlFlags(), sock, generation);
5452
6291
  } finally {
6292
+ releasePermit();
5453
6293
  if (this.inflight.get(key) === controller)
5454
6294
  this.inflight.delete(key);
5455
6295
  }
5456
6296
  }
5457
6297
  async sendError(frame, code, message, flags, sock, generation) {
5458
- await this.sendOn(sock, generation, buildFrameWithVersion(frame.header.ver, 5 /* Error */, flags, frame.header.channel, frame.header.corr, encodeJson({ code, message })));
6298
+ await this.sendOn(sock, generation, buildFrameWithVersion(frame.header.ver, FrameType.Error, flags, frame.header.channel, frame.header.epoch, frame.header.corr, encodeJson({ code, message })));
5459
6299
  }
5460
6300
  async sendOn(sock, generation, frame) {
5461
6301
  if (this.sock !== sock || this.generation !== generation || this.closeStarted || this.closedErr)
@@ -5463,42 +6303,79 @@ class SubcProvider {
5463
6303
  await sendFrame(sock, frame);
5464
6304
  }
5465
6305
  handleUnexpectedDrop(sock, generation, cause) {
6306
+ if (this.closeStarted || this.sock !== sock || this.generation !== generation)
6307
+ return;
5466
6308
  this.cancelRestoredDebounce();
5467
6309
  this.abortGeneration(generation);
5468
6310
  this.generation += 1;
5469
6311
  sock.close();
5470
- this.enqueueConnectionState({ state: "down", cause });
5471
- this.scheduleReconnectAfterDrop(cause);
6312
+ this.scheduleReconnectAfterDrop(cause, this.generation, sock);
5472
6313
  }
5473
- scheduleReconnectAfterDrop(trigger) {
5474
- if (this.closeStarted || this.reconnecting)
6314
+ scheduleReconnectAfterDrop(cause, generation, droppedSocket) {
6315
+ if (this.closeStarted)
5475
6316
  return;
5476
- const promise = this.reconnectWithRetry(trigger).catch((err) => {
5477
- if (!this.closeStarted)
6317
+ const previous = this.reconnecting;
6318
+ if (previous) {
6319
+ if (!this.shouldSupersedeReconnect(previous, generation, droppedSocket))
6320
+ return;
6321
+ previous.superseded = true;
6322
+ previous.socket?.close();
6323
+ }
6324
+ const cycle = {
6325
+ generation,
6326
+ socket: null,
6327
+ socketDied: false,
6328
+ superseded: false
6329
+ };
6330
+ this.reconnecting = cycle;
6331
+ this.enqueueConnectionState({ state: "down", cause });
6332
+ this.reconnectWithRetry(cycle).catch((err) => {
6333
+ if (this.isCurrentReconnect(cycle) && !this.closeStarted) {
5478
6334
  this.failFatal(err instanceof Error ? err : new SubcProviderError(String(err)));
6335
+ }
5479
6336
  }).finally(() => {
5480
- if (this.reconnecting === promise)
6337
+ if (this.isCurrentReconnect(cycle))
5481
6338
  this.reconnecting = null;
5482
6339
  });
5483
- this.reconnecting = promise;
5484
6340
  }
5485
- async reconnectWithRetry(_trigger) {
6341
+ async reconnectWithRetry(cycle) {
5486
6342
  let attempt = 0;
5487
6343
  let delay = this.opts.reconnectBackoff.baseMs;
5488
6344
  for (;; ) {
6345
+ if (!this.isCurrentReconnect(cycle))
6346
+ return;
5489
6347
  if (this.closeStarted)
5490
6348
  throw new SubcProviderError("provider closed");
6349
+ cycle.socket = null;
6350
+ cycle.socketDied = false;
5491
6351
  attempt += 1;
5492
- this.enqueueConnectionState({ state: "reconnecting", attempt });
6352
+ this.enqueueReconnectState(cycle, attempt);
5493
6353
  try {
5494
- const opened = await SubcProvider.openConnection(this.opts);
5495
- if (this.closeStarted) {
6354
+ const opened = await SubcProvider.openConnection(this.opts, (sock) => {
6355
+ if (!this.isCurrentReconnect(cycle)) {
6356
+ sock.close();
6357
+ return;
6358
+ }
6359
+ cycle.socket = sock;
6360
+ });
6361
+ if (!this.isCurrentReconnect(cycle) || this.closeStarted) {
5496
6362
  opened.sock.close();
5497
- throw new SubcProviderError("provider closed");
6363
+ if (this.closeStarted)
6364
+ throw new SubcProviderError("provider closed");
6365
+ return;
5498
6366
  }
5499
- this.replaceConnection(opened);
6367
+ const epoch = this.replaceConnection(opened, cycle.generation);
6368
+ this.reconnecting = null;
6369
+ if (this.reconnecting !== null) {
6370
+ throw new SubcProviderError("reconnect state must clear before restored", "reconnect_state");
6371
+ }
6372
+ this.scheduleRestored(cycle.generation, epoch);
5500
6373
  return;
5501
6374
  } catch (err) {
6375
+ if (!this.isCurrentReconnect(cycle))
6376
+ return;
6377
+ if (cycle.socket)
6378
+ cycle.socketDied = true;
5502
6379
  if (this.closeStarted)
5503
6380
  throw err;
5504
6381
  if (!isProviderReconnectTransient(err))
@@ -5508,16 +6385,29 @@ class SubcProvider {
5508
6385
  }
5509
6386
  }
5510
6387
  }
5511
- replaceConnection(opened) {
6388
+ isCurrentReconnect(cycle) {
6389
+ return !cycle.superseded && this.reconnecting === cycle && this.generation === cycle.generation;
6390
+ }
6391
+ shouldSupersedeReconnect(cycle, generation, droppedSocket) {
6392
+ return cycle.generation < generation || cycle.socketDied || cycle.socket === droppedSocket;
6393
+ }
6394
+ enqueueReconnectState(cycle, attempt) {
6395
+ if (!this.isCurrentReconnect(cycle))
6396
+ return;
6397
+ this.enqueueConnectionState({ state: "reconnecting", attempt }, cycle.generation, cycle);
6398
+ }
6399
+ replaceConnection(opened, generation) {
5512
6400
  this.sock.close();
5513
6401
  this.sock = opened.sock;
5514
6402
  this.currentConn = opened.conn;
5515
6403
  this.storage = opened.ack.storage;
5516
6404
  this.closedErr = null;
5517
6405
  this.connectionEpoch += 1;
5518
- const generation = this.generation;
6406
+ this.connectionToken = newConnectionToken();
6407
+ this.liveRoutes.clear();
6408
+ this.nextCorr = 1n;
5519
6409
  this.readLoop(opened.sock, generation);
5520
- this.scheduleRestored(generation, this.connectionEpoch);
6410
+ return this.connectionEpoch;
5521
6411
  }
5522
6412
  scheduleRestored(generation, epoch) {
5523
6413
  if (!this.opts.onConnectionState)
@@ -5525,7 +6415,7 @@ class SubcProvider {
5525
6415
  const token = ++this.restoredDebounceToken;
5526
6416
  this.opts.sleep(this.opts.restoredDebounceMs).then(() => {
5527
6417
  if (token === this.restoredDebounceToken && !this.closeStarted && this.sock && this.generation === generation && this.connectionEpoch === epoch) {
5528
- this.enqueueConnectionState({ state: "restored", epoch });
6418
+ this.enqueueConnectionState({ state: "restored", epoch }, generation);
5529
6419
  }
5530
6420
  }).catch((err) => {
5531
6421
  if (token === this.restoredDebounceToken && !this.closeStarted) {
@@ -5536,10 +6426,10 @@ class SubcProvider {
5536
6426
  cancelRestoredDebounce() {
5537
6427
  this.restoredDebounceToken += 1;
5538
6428
  }
5539
- enqueueConnectionState(event) {
6429
+ enqueueConnectionState(event, generation, reconnect) {
5540
6430
  if (!this.opts.onConnectionState)
5541
6431
  return;
5542
- this.stateQueue.push(event);
6432
+ this.stateQueue.push({ event, generation, reconnect });
5543
6433
  if (!this.drainingStateQueue)
5544
6434
  this.drainConnectionStateQueue();
5545
6435
  }
@@ -5549,7 +6439,12 @@ class SubcProvider {
5549
6439
  this.drainingStateQueue = true;
5550
6440
  try {
5551
6441
  while (this.stateQueue.length > 0) {
5552
- const event = this.stateQueue[0];
6442
+ const queued = this.stateQueue[0];
6443
+ if (this.closeStarted || queued.generation !== undefined && queued.generation !== this.generation || queued.reconnect?.superseded) {
6444
+ this.stateQueue.shift();
6445
+ continue;
6446
+ }
6447
+ const { event } = queued;
5553
6448
  try {
5554
6449
  await this.opts.onConnectionState?.(event);
5555
6450
  this.stateQueue.shift();
@@ -5569,6 +6464,24 @@ class SubcProvider {
5569
6464
  this.drainConnectionStateQueue();
5570
6465
  }
5571
6466
  }
6467
+ isLiveHandle(handle) {
6468
+ return belongsToConnection(handle, this.connectionToken) && this.liveRoutes.get(handle.channel) === handle;
6469
+ }
6470
+ assertLiveHandle(handle) {
6471
+ if (!this.isLiveHandle(handle))
6472
+ throw new StaleRouteHandleError(handle);
6473
+ }
6474
+ allocateCorr() {
6475
+ const maximum = 0xffffffffffffffffn;
6476
+ if (this.nextCorr > maximum) {
6477
+ const error2 = new SubcProviderError("channel-0 correlation id allocator exhausted", "corr_exhausted");
6478
+ this.handleUnexpectedDrop(this.sock, this.generation, error2);
6479
+ throw error2;
6480
+ }
6481
+ const corr = this.nextCorr;
6482
+ this.nextCorr += 1n;
6483
+ return corr;
6484
+ }
5572
6485
  failFatal(err) {
5573
6486
  if (!this.closedErr)
5574
6487
  this.closedErr = err;
@@ -5582,8 +6495,16 @@ class SubcProvider {
5582
6495
  this.resolveClosed();
5583
6496
  }
5584
6497
  }
5585
- function routeKey(generation, channel, corr) {
5586
- return `${generation}:${channel}:${corr}`;
6498
+ function routeKey(handle, corr) {
6499
+ return `${handle.channel}:${handle.epoch}:${corr}`;
6500
+ }
6501
+ function providerErrorFromFrame(frame) {
6502
+ try {
6503
+ const body = parseJson(frame.body);
6504
+ return new SubcProviderError(body.message ?? "subc error", body.code);
6505
+ } catch {
6506
+ return new SubcProviderError(Buffer2.from(frame.body).toString("utf8") || "subc error");
6507
+ }
5587
6508
  }
5588
6509
  function launchNonce(opts) {
5589
6510
  const nonce = opts.launchNonce ?? process.env[SUBC_LAUNCH_NONCE_ENV2];
@@ -5594,9 +6515,11 @@ function normalizeProviderConnectOptions(opts) {
5594
6515
  connectionFile: opts.connectionFile,
5595
6516
  manifest: opts.manifest,
5596
6517
  handler: opts.handler,
6518
+ health: opts.health ?? (() => ({ status: "ok" })),
5597
6519
  handshakeTimeoutMs: opts.handshakeTimeoutMs,
5598
6520
  controlOps: opts.controlOps,
5599
6521
  onBind: opts.onBind,
6522
+ onBound: opts.onBound,
5600
6523
  onRouteGone: opts.onRouteGone,
5601
6524
  reconnectBackoff: opts.reconnectBackoff ?? DEFAULT_RECONNECT_BACKOFF,
5602
6525
  sleep: opts.sleep ?? ((ms) => new Promise((resolve7) => setTimeout(resolve7, ms))),
@@ -5605,12 +6528,19 @@ function normalizeProviderConnectOptions(opts) {
5605
6528
  launchNonce: opts.launchNonce
5606
6529
  };
5607
6530
  }
6531
+ function normalizedControlOps(controlOps) {
6532
+ if (controlOps === null)
6533
+ return null;
6534
+ const merged = new Set(controlOps ?? []);
6535
+ merged.add(HEALTH_CHECK_OP);
6536
+ return [...merged];
6537
+ }
5608
6538
  function buildHelloFrame(opts) {
5609
6539
  const nonce = launchNonce(opts);
5610
- return buildFrame(9 /* Hello */, controlFlags(), 0, HELLO_CORR, encodeJson({
6540
+ return buildFrame(FrameType.Hello, controlFlags(), 0, 0, HELLO_CORR, encodeJson({
5611
6541
  manifest: normalizeManifest(opts.manifest),
5612
6542
  protocol_ver: PROTOCOL_VERSION,
5613
- control_ops: opts.controlOps === undefined ? null : opts.controlOps,
6543
+ control_ops: normalizedControlOps(opts.controlOps),
5614
6544
  ...nonce ? { launch_nonce: nonce } : {}
5615
6545
  }));
5616
6546
  }
@@ -5621,7 +6551,9 @@ function isProviderReconnectTransient(err) {
5621
6551
  return true;
5622
6552
  if (err instanceof SocketWriteNotQueuedError || err instanceof SocketWriteQueuedError)
5623
6553
  return true;
5624
- if (err instanceof ConnectionFileError || err instanceof AuthError)
6554
+ if (err instanceof AuthError)
6555
+ return true;
6556
+ if (err instanceof ConnectionFileError)
5625
6557
  return false;
5626
6558
  const code = errorCode2(err);
5627
6559
  return code === "ECONNREFUSED" || code === "ECONNRESET" || code === "EPIPE" || code === "ETIMEDOUT" || code === "ENOENT";
@@ -5638,20 +6570,23 @@ async function pauseBeforeStateRetry() {
5638
6570
  await new Promise((resolve7) => setTimeout(resolve7, 0));
5639
6571
  }
5640
6572
  function controlFlags() {
5641
- return buildFlags(false, 0 /* Passive */, false);
6573
+ return buildFlags(false, Priority.Passive, false);
5642
6574
  }
5643
6575
  async function sendFrame(sock, frame) {
5644
6576
  await sock.write(encodeFrame(frame), Date.now() + WRITE_TIMEOUT_MS);
5645
6577
  }
5646
6578
  async function expectHelloAck(sock, deadline) {
5647
- const header = decodeHeader(await sock.readExact(HEADER_LEN, deadline));
5648
- const body = header.len === 0 ? new Uint8Array(0) : await sock.readExact(header.len, deadline);
5649
- const frame = { header, body };
5650
- switch (header.ty) {
5651
- case 10 /* HelloAck */:
5652
- return parseJson(body);
5653
- case 5 /* Error */: {
5654
- const error2 = parseJson(body);
6579
+ const frame = await sock.readFrame(deadline, deadline);
6580
+ switch (frame.header.ty) {
6581
+ case FrameType.HelloAck: {
6582
+ const ack = parseJson(frame.body);
6583
+ if (ack.negotiated_ver !== PROTOCOL_VERSION) {
6584
+ throw new SubcProviderError(`subc negotiated protocol ${ack.negotiated_ver}; expected exactly ${PROTOCOL_VERSION}`, "unsupported_version");
6585
+ }
6586
+ return ack;
6587
+ }
6588
+ case FrameType.Error: {
6589
+ const error2 = parseJson(frame.body);
5655
6590
  throw new SubcProviderError(`subc rejected HELLO: ${error2.code ?? "unknown"} — ${error2.message ?? "subc error"}`, error2.code);
5656
6591
  }
5657
6592
  default:
@@ -5716,6 +6651,7 @@ function normalizeProviderRole(role) {
5716
6651
  role: "tool_provider",
5717
6652
  tools: role.tools.map((tool) => ({
5718
6653
  name: tool.name,
6654
+ ...tool.description === undefined ? {} : { description: tool.description },
5719
6655
  execution_mode: tool.execution_mode,
5720
6656
  schema: tool.schema
5721
6657
  })),
@@ -5791,12 +6727,13 @@ function normalizeScheduledTask(task) {
5791
6727
  }
5792
6728
  };
5793
6729
  }
5794
- var DEFAULT_HANDSHAKE_TIMEOUT_MS2 = 1e4, BODY_READ_TIMEOUT_MS2 = 30000, WRITE_TIMEOUT_MS = 30000, DEFAULT_RESTORED_DEBOUNCE_MS = 250, HELLO_CORR = 1n, SubcProviderError, SUBC_LAUNCH_NONCE_ENV2 = "SUBC_LAUNCH_NONCE";
6730
+ var DEFAULT_HANDSHAKE_TIMEOUT_MS2 = 1e4, BODY_READ_TIMEOUT_MS2 = 30000, WRITE_TIMEOUT_MS = 30000, DEFAULT_RESTORED_DEBOUNCE_MS = 250, DEFAULT_PROVIDER_HANDLER_CAPACITY = 64, HEALTH_CHECK_OP = "health.check", HELLO_CORR = 1n, SubcProviderError, SUBC_LAUNCH_NONCE_ENV2 = "SUBC_LAUNCH_NONCE";
5795
6731
  var init_provider = __esm(() => {
5796
6732
  init_auth();
5797
6733
  init_client();
5798
6734
  init_connection_file();
5799
6735
  init_envelope();
6736
+ init_route_handle();
5800
6737
  init_socket();
5801
6738
  SubcProviderError = class SubcProviderError extends Error {
5802
6739
  code;
@@ -5807,9 +6744,10 @@ var init_provider = __esm(() => {
5807
6744
  };
5808
6745
  });
5809
6746
 
5810
- // ../../node_modules/.bun/@cortexkit+subc-client@0.3.0/node_modules/@cortexkit/subc-client/src/index.ts
5811
- var init_src = __esm(() => {
6747
+ // ../../node_modules/.bun/@cortexkit+subc-client@0.5.0/node_modules/@cortexkit/subc-client/dist/index.js
6748
+ var init_dist = __esm(() => {
5812
6749
  init_client();
6750
+ init_route_handle();
5813
6751
  init_connection_file();
5814
6752
  init_envelope();
5815
6753
  init_auth();
@@ -5826,15 +6764,17 @@ class BgSubscription {
5826
6764
  identity;
5827
6765
  acquireClient;
5828
6766
  dropClient;
6767
+ consumerIdentity;
5829
6768
  onNudge;
5830
6769
  sleep;
5831
6770
  stopped = false;
5832
6771
  current = null;
5833
6772
  loop;
5834
- constructor(identity, acquireClient, dropClient, onNudge, sleep2) {
6773
+ constructor(identity, acquireClient, dropClient, consumerIdentity, onNudge, sleep2) {
5835
6774
  this.identity = identity;
5836
6775
  this.acquireClient = acquireClient;
5837
6776
  this.dropClient = dropClient;
6777
+ this.consumerIdentity = consumerIdentity;
5838
6778
  this.onNudge = onNudge;
5839
6779
  this.sleep = sleep2;
5840
6780
  this.loop = this.run();
@@ -5863,9 +6803,9 @@ class BgSubscription {
5863
6803
  }
5864
6804
  if (this.stopped)
5865
6805
  return;
5866
- let channel;
6806
+ let route;
5867
6807
  try {
5868
- channel = await client.routeOpen({ kind: "tool_provider", module_id: AFT_MODULE_ID }, this.identity);
6808
+ route = await client.routeOpen({ kind: "tool_provider", module_id: AFT_MODULE_ID }, this.identity, { consumerIdentity: this.consumerIdentity });
5869
6809
  } catch (err) {
5870
6810
  if (isConsumerReconnectTransient(err))
5871
6811
  this.dropClient(client);
@@ -5873,12 +6813,12 @@ class BgSubscription {
5873
6813
  continue;
5874
6814
  }
5875
6815
  if (this.stopped) {
5876
- safeCloseRoute(client, channel);
6816
+ safeCloseRoute(client, route);
5877
6817
  return;
5878
6818
  }
5879
6819
  const subscribedAt = Date.now();
5880
6820
  try {
5881
- const sub = client.subscribe(channel, { op: "bg_events" }, () => {
6821
+ const sub = client.subscribe(route, { op: "bg_events" }, () => {
5882
6822
  if (!this.stopped)
5883
6823
  this.onNudge();
5884
6824
  });
@@ -5898,7 +6838,7 @@ class BgSubscription {
5898
6838
  attempt = 0;
5899
6839
  } finally {
5900
6840
  this.current = null;
5901
- safeCloseRoute(client, channel);
6841
+ safeCloseRoute(client, route);
5902
6842
  }
5903
6843
  await this.backoff(attempt++);
5904
6844
  }
@@ -5911,9 +6851,12 @@ class BgSubscription {
5911
6851
  function isRecord(value) {
5912
6852
  return typeof value === "object" && value !== null && !Array.isArray(value);
5913
6853
  }
5914
- function safeCloseRoute(client, channel) {
6854
+ function isRouteProvenAbsentError(err) {
6855
+ return err instanceof SubcError && err.code === "unknown_channel" || err instanceof StaleRouteHandleError;
6856
+ }
6857
+ function safeCloseRoute(client, route) {
5915
6858
  try {
5916
- client.closeRouteChannel(channel).catch(() => {
6859
+ client.closeRouteChannel(route).catch(() => {
5917
6860
  return;
5918
6861
  });
5919
6862
  } catch {}
@@ -5987,7 +6930,7 @@ class SubcTransport {
5987
6930
  if (!options)
5988
6931
  return {};
5989
6932
  const preview = options.preview;
5990
- const timeoutMs = options.timeoutMs;
6933
+ const timeoutMs = options.transportTimeoutMs ?? options.timeoutMs;
5991
6934
  const onProgress = options.onProgress;
5992
6935
  return { preview, timeoutMs, onProgress };
5993
6936
  }
@@ -5997,6 +6940,7 @@ class SubcTransportPool {
5997
6940
  harness;
5998
6941
  connectionFile;
5999
6942
  handshakeTimeoutMs;
6943
+ consumerIdentity;
6000
6944
  connectFn;
6001
6945
  onBgEventsNudge;
6002
6946
  bgBackoffSleep;
@@ -6010,6 +6954,7 @@ class SubcTransportPool {
6010
6954
  this.connectionFile = options.connectionFile;
6011
6955
  this.harness = options.harness;
6012
6956
  this.handshakeTimeoutMs = options.handshakeTimeoutMs;
6957
+ this.consumerIdentity = options.consumerIdentity;
6013
6958
  this.connectFn = options.connect ?? ((opts) => SubcClient.connect(opts));
6014
6959
  this.onBgEventsNudge = options.onBgEventsNudge;
6015
6960
  this.bgBackoffSleep = options.bgBackoffSleep ?? ((ms) => new Promise((resolve7) => setTimeout(resolve7, ms)));
@@ -6032,6 +6977,11 @@ class SubcTransportPool {
6032
6977
  return null;
6033
6978
  return this.transports.get(key) ?? null;
6034
6979
  }
6980
+ activeBridges() {
6981
+ if (!this.client || this.shuttingDown)
6982
+ return [];
6983
+ return [...this.transports.values()];
6984
+ }
6035
6985
  async toolCall(projectRoot, runtime, name, rawArgs = {}, options) {
6036
6986
  return this.getBridge(projectRoot).toolCall(runtime.sessionID, name, rawArgs, options);
6037
6987
  }
@@ -6057,45 +7007,66 @@ class SubcTransportPool {
6057
7007
  record.inflight += 1;
6058
7008
  try {
6059
7009
  const client = await this.ensureClient();
6060
- if (!this.isCurrentSession(key, record)) {
6061
- throw new RouteTornDownError("subc session closed");
6062
- }
6063
- let channel;
6064
- let entry;
6065
- try {
6066
- ({ channel, entry } = await this.routeChannel(client, identity, record));
7010
+ const openRoute = async () => {
6067
7011
  if (!this.isCurrentSession(key, record)) {
6068
7012
  throw new RouteTornDownError("subc session closed");
6069
7013
  }
6070
- } catch (err) {
6071
- if (err instanceof RouteTornDownError)
7014
+ try {
7015
+ const opened = await this.routeHandle(client, identity, record);
7016
+ if (!this.isCurrentSession(key, record)) {
7017
+ throw new RouteTornDownError("subc session closed");
7018
+ }
7019
+ return opened;
7020
+ } catch (err) {
7021
+ if (err instanceof RouteTornDownError)
7022
+ throw err;
7023
+ if (isConsumerReconnectTransient(err) && this.isCurrentSession(key, record) && this.client === client) {
7024
+ this.dropClient(client);
7025
+ }
6072
7026
  throw err;
6073
- if (isConsumerReconnectTransient(err) && this.isCurrentSession(key, record) && this.client === client) {
6074
- this.dropClient(client);
6075
- }
6076
- throw err;
6077
- }
6078
- try {
6079
- const reply = await client.request(channel, body, { timeoutMs, onProgress });
6080
- if (this.isCurrentSession(key, record) && this.client === client) {
6081
- this.transportFailures = 0;
6082
7027
  }
6083
- this.ensureBgSubscription(identity, record);
6084
- return reply;
6085
- } catch (err) {
6086
- if (record.routeEntry === entry) {
6087
- entry.closed = true;
7028
+ };
7029
+ const clearRouteEntry = (entry2) => {
7030
+ if (record.routeEntry === entry2) {
7031
+ entry2.closed = true;
6088
7032
  record.routeEntry = null;
6089
7033
  }
7034
+ };
7035
+ const handleRequestFailure = (err, entry2) => {
7036
+ clearRouteEntry(entry2);
6090
7037
  if (this.isCurrentSession(key, record) && this.client === client) {
6091
7038
  if (isConsumerReconnectTransient(err)) {
6092
7039
  this.transportFailures = 0;
6093
7040
  this.dropClient(client);
6094
- } else if (++this.transportFailures >= MAX_CONSECUTIVE_TRANSPORT_FAILURES) {
7041
+ } else if (!isRouteProvenAbsentError(err) && ++this.transportFailures >= MAX_CONSECUTIVE_TRANSPORT_FAILURES) {
6095
7042
  this.transportFailures = 0;
6096
7043
  this.dropClient(client);
6097
7044
  }
6098
7045
  }
7046
+ };
7047
+ const requestOnRoute = async (route2) => {
7048
+ const reply = await client.request(route2, body, { timeoutMs, onProgress });
7049
+ if (this.isCurrentSession(key, record) && this.client === client) {
7050
+ this.transportFailures = 0;
7051
+ }
7052
+ this.ensureBgSubscription(identity, record);
7053
+ return reply;
7054
+ };
7055
+ let { route, entry } = await openRoute();
7056
+ try {
7057
+ return await requestOnRoute(route);
7058
+ } catch (err) {
7059
+ if (isRouteProvenAbsentError(err) && this.isCurrentSession(key, record) && this.client === client) {
7060
+ clearRouteEntry(entry);
7061
+ ({ route, entry } = await openRoute());
7062
+ try {
7063
+ return await requestOnRoute(route);
7064
+ } catch (retryErr) {
7065
+ handleRequestFailure(retryErr, entry);
7066
+ throw retryErr;
7067
+ }
7068
+ }
7069
+ handleRequestFailure(err, entry);
6099
7070
  throw err;
6100
7071
  }
6101
7072
  } finally {
@@ -6131,24 +7102,26 @@ class SubcTransportPool {
6131
7102
  });
6132
7103
  return this.connecting;
6133
7104
  }
6134
- async routeChannel(client, identity, record) {
7105
+ async routeHandle(client, identity, record) {
6135
7106
  const key = identityKey(identity);
6136
7107
  const existing = record.routeEntry;
6137
- if (existing?.channel != null)
6138
- return { channel: existing.channel, entry: existing };
7108
+ if (existing?.handle != null)
7109
+ return { route: existing.handle, entry: existing };
6139
7110
  if (existing?.opening)
6140
- return { channel: await existing.opening, entry: existing };
6141
- const entry = { client, opening: null, channel: null, closed: false };
6142
- const opening = client.routeOpen({ kind: "tool_provider", module_id: AFT_MODULE_ID }, identity).then((channel) => {
7111
+ return { route: await existing.opening, entry: existing };
7112
+ const entry = { client, opening: null, handle: null, closed: false };
7113
+ const opening = client.routeOpen({ kind: "tool_provider", module_id: AFT_MODULE_ID }, identity, {
7114
+ consumerIdentity: this.consumerIdentity
7115
+ }).then((route) => {
6143
7116
  if (!this.isCurrentSession(key, record) || record.routeEntry !== entry || entry.closed || this.client !== client) {
6144
- safeCloseRoute(client, channel);
7117
+ safeCloseRoute(client, route);
6145
7118
  if (record.routeEntry === entry)
6146
7119
  record.routeEntry = null;
6147
7120
  throw new RouteTornDownError("subc route opened after teardown");
6148
7121
  }
6149
- entry.channel = channel;
7122
+ entry.handle = route;
6150
7123
  entry.opening = null;
6151
- return channel;
7124
+ return route;
6152
7125
  }).catch((err) => {
6153
7126
  const current = this.isCurrentSession(key, record);
6154
7127
  if (record.routeEntry === entry) {
@@ -6162,7 +7135,7 @@ class SubcTransportPool {
6162
7135
  });
6163
7136
  entry.opening = opening;
6164
7137
  record.routeEntry = entry;
6165
- return { channel: await opening, entry };
7138
+ return { route: await opening, entry };
6166
7139
  }
6167
7140
  ensureBgSubscription(identity, record) {
6168
7141
  if (this.shuttingDown || !this.onBgEventsNudge)
@@ -6173,7 +7146,7 @@ class SubcTransportPool {
6173
7146
  if (record.bgSub)
6174
7147
  return;
6175
7148
  const onNudge = () => this.onBgEventsNudge?.(identity.project_root, identity.session);
6176
- const sub = new BgSubscription(identity, () => this.ensureClient(), (client) => this.dropClient(client), onNudge, this.bgBackoffSleep);
7149
+ const sub = new BgSubscription(identity, () => this.ensureClient(), (client) => this.dropClient(client), this.consumerIdentity, onNudge, this.bgBackoffSleep);
6177
7150
  record.bgSub = sub;
6178
7151
  }
6179
7152
  dropClient(client) {
@@ -6194,9 +7167,13 @@ class SubcTransportPool {
6194
7167
  }
6195
7168
  }
6196
7169
  setConfigureOverride(_key, _value) {}
7170
+ async reconfigure(_projectRoot, _overrides) {}
6197
7171
  async replaceBinary(path2) {
6198
7172
  return path2;
6199
7173
  }
7174
+ isShutdown() {
7175
+ return this.shuttingDown;
7176
+ }
6200
7177
  async shutdown() {
6201
7178
  this.shuttingDown = true;
6202
7179
  const subs = [];
@@ -6220,10 +7197,10 @@ class SubcTransportPool {
6220
7197
  this.transports.clear();
6221
7198
  await Promise.allSettled(subs.map((sub) => sub.stop()));
6222
7199
  await Promise.allSettled(entries.map(async (entry) => {
6223
- if (entry.channel == null)
7200
+ if (entry.handle == null)
6224
7201
  return;
6225
7202
  try {
6226
- await entry.client.closeRouteChannel(entry.channel);
7203
+ await entry.client.closeRouteChannel(entry.handle);
6227
7204
  } catch {}
6228
7205
  }));
6229
7206
  if (client) {
@@ -6252,16 +7229,16 @@ class SubcTransportPool {
6252
7229
  entry.closed = true;
6253
7230
  if (sub)
6254
7231
  await sub.stop();
6255
- if (entry?.channel != null) {
7232
+ if (entry?.handle != null) {
6256
7233
  try {
6257
- await entry.client.closeRouteChannel(entry.channel);
7234
+ await entry.client.closeRouteChannel(entry.handle);
6258
7235
  } catch {}
6259
7236
  }
6260
7237
  }
6261
7238
  }
6262
7239
  var AFT_MODULE_ID = "aft", MAX_CONSECUTIVE_TRANSPORT_FAILURES = 3, BG_STABLE_MS = 5000, DEFAULT_SESSION_ID = "__default__", LOCALLY_SATISFIED_COMMANDS, RouteTornDownError;
6263
7240
  var init_subc_transport = __esm(() => {
6264
- init_src();
7241
+ init_dist();
6265
7242
  init_project_identity();
6266
7243
  LOCALLY_SATISFIED_COMMANDS = new Set(["configure"]);
6267
7244
  RouteTornDownError = class RouteTornDownError extends Error {
@@ -6355,18 +7332,25 @@ function formatReadFooter(agentSpecifiedRange, data, options) {
6355
7332
  }
6356
7333
 
6357
7334
  // ../aft-bridge/dist/transport-factory.js
6358
- import { homedir as homedir10 } from "node:os";
6359
- import { isAbsolute as isAbsolute5, join as join9 } from "node:path";
7335
+ import { homedir as homedir11 } from "node:os";
7336
+ import { isAbsolute as isAbsolute5, join as join10 } from "node:path";
6360
7337
  function resolveConnectionFilePath(raw) {
6361
7338
  const trimmed = raw.trim();
6362
7339
  if (trimmed.startsWith("~")) {
6363
- return join9(homedir10(), trimmed.slice(1).replace(/^[/\\]/, ""));
7340
+ return join10(homedir11(), trimmed.slice(1).replace(/^[/\\]/, ""));
6364
7341
  }
6365
7342
  if (isAbsolute5(trimmed))
6366
7343
  return trimmed;
6367
- return join9(homedir10(), trimmed);
7344
+ return join10(homedir11(), trimmed);
6368
7345
  }
6369
7346
  async function createAftTransportPool(opts) {
7347
+ let binaryPath = opts.binaryPath;
7348
+ const createPool = () => createConcreteAftTransportPool({ ...opts, binaryPath });
7349
+ return new RevivableTransportPool(await createPool(), createPool, (path2) => {
7350
+ binaryPath = path2;
7351
+ });
7352
+ }
7353
+ async function createConcreteAftTransportPool(opts) {
6370
7354
  const raw = opts.subcConnectionFile?.trim();
6371
7355
  if (raw && raw.length > 0) {
6372
7356
  const connectionFile = resolveConnectionFilePath(raw);
@@ -6377,6 +7361,7 @@ async function createAftTransportPool(opts) {
6377
7361
  return new SubcTransportPool({
6378
7362
  connectionFile,
6379
7363
  harness: opts.harness,
7364
+ consumerIdentity: opts.subcConsumerIdentity,
6380
7365
  onBgEventsNudge: opts.onBgEventsNudge
6381
7366
  });
6382
7367
  }
@@ -6384,6 +7369,7 @@ async function createAftTransportPool(opts) {
6384
7369
  }
6385
7370
  var init_transport_factory = __esm(() => {
6386
7371
  init_pool();
7372
+ init_revivable_transport();
6387
7373
  init_subc_transport();
6388
7374
  });
6389
7375
 
@@ -6501,6 +7487,7 @@ __export(exports_dist, {
6501
7487
  timeoutForCommand: () => timeoutForCommand,
6502
7488
  tagStderrLine: () => tagStderrLine,
6503
7489
  stripJsoncSymbols: () => stripJsoncSymbols,
7490
+ stripHarnessSpecificConfigKeys: () => stripHarnessSpecificConfigKeys,
6504
7491
  statusBarLine: () => statusBarLine,
6505
7492
  sleep: () => sleep,
6506
7493
  shouldShowAnnouncement: () => shouldShowAnnouncement,
@@ -6515,6 +7502,8 @@ __export(exports_dist, {
6515
7502
  resolveCortexKitProjectConfigPath: () => resolveCortexKitProjectConfigPath,
6516
7503
  resolveCortexKitConfigPaths: () => resolveCortexKitConfigPaths,
6517
7504
  resolveBashKillTimeout: () => resolveBashKillTimeout,
7505
+ resolveAftStorageRoot: () => resolveAftStorageRoot,
7506
+ resolveAftLogPath: () => resolveAftLogPath,
6518
7507
  repairRootScopedStorageFile: () => repairRootScopedStorageFile,
6519
7508
  readConfigTiers: () => readConfigTiers,
6520
7509
  projectRootKeyHash: () => projectRootKeyHash,
@@ -6572,23 +7561,31 @@ __export(exports_dist, {
6572
7561
  __onnxTest__: () => __test__,
6573
7562
  SubcTransportPool: () => SubcTransportPool,
6574
7563
  STATUS_BAR_HEARTBEAT_CALLS: () => STATUS_BAR_HEARTBEAT_CALLS,
7564
+ RotatingLogSink: () => RotatingLogSink,
7565
+ RevivableTransportPool: () => RevivableTransportPool,
6575
7566
  PLATFORM_ASSET_MAP: () => PLATFORM_ASSET_MAP,
6576
7567
  PLATFORM_ARCH_MAP: () => PLATFORM_ARCH_MAP,
6577
7568
  PLAIN_CALLGRAPH_THEME: () => PLAIN_CALLGRAPH_THEME,
7569
+ PI_ONLY_KEYS: () => PI_ONLY_KEYS,
7570
+ OPENCODE_ONLY_KEYS: () => OPENCODE_ONLY_KEYS,
6578
7571
  LONG_RUNNING_COMMAND_TIMEOUT_MS: () => LONG_RUNNING_COMMAND_TIMEOUT_MS,
6579
7572
  HomeProjectRootError: () => HomeProjectRootError,
7573
+ DEFAULT_LOG_GENERATIONS: () => DEFAULT_LOG_GENERATIONS,
7574
+ DEFAULT_LOG_BYTES: () => DEFAULT_LOG_BYTES,
6580
7575
  BridgeTransportTimeoutError: () => BridgeTransportTimeoutError,
6581
7576
  BridgePool: () => BridgePool,
6582
7577
  BinaryBridge: () => BinaryBridge
6583
7578
  });
6584
- var init_dist = __esm(() => {
7579
+ var init_dist2 = __esm(() => {
6585
7580
  init_active_logger();
6586
7581
  init_bash_hints();
6587
7582
  init_bridge();
6588
7583
  init_callgraph_format();
6589
7584
  init_command_timeouts();
7585
+ init_config_keys();
6590
7586
  init_config_tiers();
6591
7587
  init_downloader();
7588
+ init_durable_log();
6592
7589
  init_migration();
6593
7590
  init_npm_resolver();
6594
7591
  init_onnx_runtime();
@@ -6597,70 +7594,70 @@ var init_dist = __esm(() => {
6597
7594
  init_pool();
6598
7595
  init_project_identity();
6599
7596
  init_resolver();
7597
+ init_revivable_transport();
6600
7598
  init_subc_transport();
6601
7599
  init_transport_factory();
6602
7600
  });
6603
7601
 
6604
7602
  // src/lib/paths.ts
6605
- import { homedir as homedir11, tmpdir as tmpdir2 } from "node:os";
6606
- import { join as join10 } from "node:path";
7603
+ import { homedir as homedir12 } from "node:os";
7604
+ import { join as join11 } from "node:path";
6607
7605
  function getAftBinaryCacheDir() {
6608
7606
  if (process.env.AFT_CACHE_DIR) {
6609
- return join10(process.env.AFT_CACHE_DIR, "bin");
7607
+ return join11(process.env.AFT_CACHE_DIR, "bin");
6610
7608
  }
6611
7609
  if (process.platform === "win32") {
6612
7610
  const localAppData = process.env.LOCALAPPDATA || process.env.APPDATA;
6613
- const base2 = localAppData || join10(homedir11(), "AppData", "Local");
6614
- return join10(base2, "aft", "bin");
7611
+ const base2 = localAppData || join11(homedir12(), "AppData", "Local");
7612
+ return join11(base2, "aft", "bin");
6615
7613
  }
6616
- const base = process.env.XDG_CACHE_HOME || join10(homedir11(), ".cache");
6617
- return join10(base, "aft", "bin");
7614
+ const base = process.env.XDG_CACHE_HOME || join11(homedir12(), ".cache");
7615
+ return join11(base, "aft", "bin");
6618
7616
  }
6619
7617
  function getAftBinaryName() {
6620
7618
  return process.platform === "win32" ? "aft.exe" : "aft";
6621
7619
  }
6622
7620
  function getAftLspPackagesDir() {
6623
7621
  if (process.env.AFT_CACHE_DIR) {
6624
- return join10(process.env.AFT_CACHE_DIR, "lsp-packages");
7622
+ return join11(process.env.AFT_CACHE_DIR, "lsp-packages");
6625
7623
  }
6626
7624
  if (process.platform === "win32") {
6627
7625
  const localAppData = process.env.LOCALAPPDATA || process.env.APPDATA;
6628
- const base2 = localAppData || join10(homedir11(), "AppData", "Local");
6629
- return join10(base2, "aft", "lsp-packages");
7626
+ const base2 = localAppData || join11(homedir12(), "AppData", "Local");
7627
+ return join11(base2, "aft", "lsp-packages");
6630
7628
  }
6631
- const base = process.env.XDG_CACHE_HOME || join10(homedir11(), ".cache");
6632
- return join10(base, "aft", "lsp-packages");
7629
+ const base = process.env.XDG_CACHE_HOME || join11(homedir12(), ".cache");
7630
+ return join11(base, "aft", "lsp-packages");
6633
7631
  }
6634
7632
  function getAftLspBinariesDir() {
6635
7633
  if (process.env.AFT_CACHE_DIR) {
6636
- return join10(process.env.AFT_CACHE_DIR, "lsp-binaries");
7634
+ return join11(process.env.AFT_CACHE_DIR, "lsp-binaries");
6637
7635
  }
6638
7636
  if (process.platform === "win32") {
6639
7637
  const localAppData = process.env.LOCALAPPDATA || process.env.APPDATA;
6640
- const base2 = localAppData || join10(homedir11(), "AppData", "Local");
6641
- return join10(base2, "aft", "lsp-binaries");
7638
+ const base2 = localAppData || join11(homedir12(), "AppData", "Local");
7639
+ return join11(base2, "aft", "lsp-binaries");
6642
7640
  }
6643
- const base = process.env.XDG_CACHE_HOME || join10(homedir11(), ".cache");
6644
- return join10(base, "aft", "lsp-binaries");
7641
+ const base = process.env.XDG_CACHE_HOME || join11(homedir12(), ".cache");
7642
+ return join11(base, "aft", "lsp-binaries");
6645
7643
  }
6646
- function homeDir3() {
7644
+ function homeDir4() {
6647
7645
  if (process.platform === "win32")
6648
- return process.env.USERPROFILE || process.env.HOME || homedir11();
6649
- return process.env.HOME || homedir11();
7646
+ return process.env.USERPROFILE || process.env.HOME || homedir12();
7647
+ return process.env.HOME || homedir12();
6650
7648
  }
6651
- function dataHome2() {
7649
+ function dataHome3() {
6652
7650
  if (process.env.XDG_DATA_HOME)
6653
7651
  return process.env.XDG_DATA_HOME;
6654
7652
  if (process.platform === "win32") {
6655
- return process.env.LOCALAPPDATA || process.env.APPDATA || join10(homeDir3(), "AppData", "Local");
7653
+ return process.env.LOCALAPPDATA || process.env.APPDATA || join11(homeDir4(), "AppData", "Local");
6656
7654
  }
6657
- return join10(homeDir3(), ".local", "share");
7655
+ return join11(homeDir4(), ".local", "share");
6658
7656
  }
6659
7657
  function getCortexKitStorageRoot() {
6660
- return join10(dataHome2(), "cortexkit", "aft");
6661
- }
6662
- function getTmpLogPath(filename) {
6663
- return join10(tmpdir2(), filename);
7658
+ if (process.env.AFT_CACHE_DIR)
7659
+ return join11(process.env.AFT_CACHE_DIR, "aft");
7660
+ return join11(dataHome3(), "cortexkit", "aft");
6664
7661
  }
6665
7662
  var init_paths2 = () => {};
6666
7663
 
@@ -6668,11 +7665,11 @@ var init_paths2 = () => {};
6668
7665
  import { execSync as execSync2, spawnSync as spawnSync3 } from "node:child_process";
6669
7666
  import { existsSync as existsSync7 } from "node:fs";
6670
7667
  import { createRequire as createRequire2 } from "node:module";
6671
- import { homedir as homedir12 } from "node:os";
6672
- import { join as join11 } from "node:path";
7668
+ import { homedir as homedir13 } from "node:os";
7669
+ import { join as join12 } from "node:path";
6673
7670
  async function loadPluginVersion() {
6674
7671
  try {
6675
- const bridge = await Promise.resolve().then(() => (init_dist(), exports_dist));
7672
+ const bridge = await Promise.resolve().then(() => (init_dist2(), exports_dist));
6676
7673
  if (typeof bridge.PLUGIN_VERSION === "string" && bridge.PLUGIN_VERSION.length > 0) {
6677
7674
  return bridge.PLUGIN_VERSION;
6678
7675
  }
@@ -6791,7 +7788,7 @@ function aftBinaryCandidates(preferredVersion) {
6791
7788
  const candidates = [];
6792
7789
  if (preferredVersion) {
6793
7790
  const tag = preferredVersion.startsWith("v") ? preferredVersion : `v${preferredVersion}`;
6794
- pushCandidate(candidates, join11(getAftBinaryCacheDir(), tag, getAftBinaryName()));
7791
+ pushCandidate(candidates, join12(getAftBinaryCacheDir(), tag, getAftBinaryName()));
6795
7792
  }
6796
7793
  const key = platformKey2();
6797
7794
  if (key) {
@@ -6814,7 +7811,7 @@ function aftBinaryCandidates(preferredVersion) {
6814
7811
  }
6815
7812
  }
6816
7813
  } catch {}
6817
- pushCandidate(candidates, join11(homedir12(), ".cargo", "bin", getAftBinaryName()));
7814
+ pushCandidate(candidates, join12(homedir13(), ".cargo", "bin", getAftBinaryName()));
6818
7815
  return candidates;
6819
7816
  }
6820
7817
  function findAftBinary(preferredVersion) {
@@ -6822,7 +7819,7 @@ function findAftBinary(preferredVersion) {
6822
7819
  }
6823
7820
  var PLUGIN_VERSION, VERSION_LINE;
6824
7821
  var init_binary_probe = __esm(async () => {
6825
- init_dist();
7822
+ init_dist2();
6826
7823
  init_paths2();
6827
7824
  PLUGIN_VERSION = await loadPluginVersion();
6828
7825
  VERSION_LINE = /^(?:aft\s+)?v?(\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?)$/i;
@@ -6830,21 +7827,21 @@ var init_binary_probe = __esm(async () => {
6830
7827
 
6831
7828
  // src/lib/fs-util.ts
6832
7829
  import { existsSync as existsSync8, readdirSync as readdirSync3, statSync as statSync5 } from "node:fs";
6833
- import { join as join12 } from "node:path";
7830
+ import { join as join13 } from "node:path";
6834
7831
  function dirSize(path2) {
6835
7832
  if (!existsSync8(path2)) {
6836
7833
  return 0;
6837
7834
  }
6838
- const stat = statSync5(path2);
6839
- if (stat.isFile()) {
6840
- return stat.size;
7835
+ const stat2 = statSync5(path2);
7836
+ if (stat2.isFile()) {
7837
+ return stat2.size;
6841
7838
  }
6842
- if (!stat.isDirectory()) {
7839
+ if (!stat2.isDirectory()) {
6843
7840
  return 0;
6844
7841
  }
6845
7842
  let total = 0;
6846
7843
  for (const entry of readdirSync3(path2)) {
6847
- total += dirSize(join12(path2, entry));
7844
+ total += dirSize(join13(path2, entry));
6848
7845
  }
6849
7846
  return total;
6850
7847
  }
@@ -14467,10 +15464,10 @@ var require_stringify = __commonJS((exports, module) => {
14467
15464
  replacer = null;
14468
15465
  indent = EMPTY;
14469
15466
  };
14470
- var join13 = (one, two, gap) => one ? two ? one + two.trim() + LF + gap : one.trimRight() + repeat_line_breaks(Math.max(1, count_trailing_line_breaks(one, gap)), gap) : two ? two.trimRight() + repeat_line_breaks(Math.max(1, count_trailing_line_breaks(two, gap)), gap) : EMPTY;
15467
+ var join14 = (one, two, gap) => one ? two ? one + two.trim() + LF + gap : one.trimRight() + repeat_line_breaks(Math.max(1, count_trailing_line_breaks(one, gap)), gap) : two ? two.trimRight() + repeat_line_breaks(Math.max(1, count_trailing_line_breaks(two, gap)), gap) : EMPTY;
14471
15468
  var join_content = (inside, value, gap) => {
14472
15469
  const comment = process_comments(value, PREFIX_BEFORE, gap + indent, true);
14473
- return join13(comment, inside, gap);
15470
+ return join14(comment, inside, gap);
14474
15471
  };
14475
15472
  var stringify_string = (holder, key, value) => {
14476
15473
  const raw = get_raw_string_literal(holder, key);
@@ -14492,13 +15489,13 @@ var require_stringify = __commonJS((exports, module) => {
14492
15489
  if (i !== 0) {
14493
15490
  inside += COMMA;
14494
15491
  }
14495
- const before = join13(after_comma, process_comments(value, BEFORE(i), deeper_gap), deeper_gap);
15492
+ const before = join14(after_comma, process_comments(value, BEFORE(i), deeper_gap), deeper_gap);
14496
15493
  inside += before || LF + deeper_gap;
14497
15494
  inside += stringify(i, value, deeper_gap) || STR_NULL;
14498
15495
  inside += process_comments(value, AFTER_VALUE(i), deeper_gap);
14499
15496
  after_comma = process_comments(value, AFTER(i), deeper_gap);
14500
15497
  }
14501
- inside += join13(after_comma, process_comments(value, PREFIX_AFTER, deeper_gap), deeper_gap);
15498
+ inside += join14(after_comma, process_comments(value, PREFIX_AFTER, deeper_gap), deeper_gap);
14502
15499
  return BRACKET_OPEN + join_content(inside, value, gap) + BRACKET_CLOSE;
14503
15500
  };
14504
15501
  var object_stringify = (value, gap) => {
@@ -14519,13 +15516,13 @@ var require_stringify = __commonJS((exports, module) => {
14519
15516
  inside += COMMA;
14520
15517
  }
14521
15518
  first = false;
14522
- const before = join13(after_comma, process_comments(value, BEFORE(key), deeper_gap), deeper_gap);
15519
+ const before = join14(after_comma, process_comments(value, BEFORE(key), deeper_gap), deeper_gap);
14523
15520
  inside += before || LF + deeper_gap;
14524
15521
  inside += quote(key) + process_comments(value, AFTER_PROP(key), deeper_gap) + COLON + process_comments(value, AFTER_COLON(key), deeper_gap) + SPACE + sv + process_comments(value, AFTER_VALUE(key), deeper_gap);
14525
15522
  after_comma = process_comments(value, AFTER(key), deeper_gap);
14526
15523
  };
14527
15524
  keys.forEach(iteratee);
14528
- inside += join13(after_comma, process_comments(value, PREFIX_AFTER, deeper_gap), deeper_gap);
15525
+ inside += join14(after_comma, process_comments(value, PREFIX_AFTER, deeper_gap), deeper_gap);
14529
15526
  return CURLY_BRACKET_OPEN + join_content(inside, value, gap) + CURLY_BRACKET_CLOSE;
14530
15527
  };
14531
15528
  function stringify(key, holder, gap) {
@@ -14619,7 +15616,7 @@ var require_src2 = __commonJS((exports, module) => {
14619
15616
 
14620
15617
  // src/lib/jsonc.ts
14621
15618
  import { existsSync as existsSync9, mkdirSync as mkdirSync6, readFileSync as readFileSync7, writeFileSync as writeFileSync4 } from "node:fs";
14622
- import { dirname as dirname5 } from "node:path";
15619
+ import { dirname as dirname6 } from "node:path";
14623
15620
  function detectJsoncFile(configDir, baseName) {
14624
15621
  const jsoncPath = `${configDir}/${baseName}.jsonc`;
14625
15622
  const jsonPath = `${configDir}/${baseName}.json`;
@@ -14647,7 +15644,7 @@ function readJsoncFile(path2) {
14647
15644
  }
14648
15645
  }
14649
15646
  function writeJsoncFile(path2, value, format = "json") {
14650
- mkdirSync6(dirname5(path2), { recursive: true });
15647
+ mkdirSync6(dirname6(path2), { recursive: true });
14651
15648
  const serialized = format === "jsonc" ? import_comment_json.stringify(value, null, 2) : JSON.stringify(value, null, 2);
14652
15649
  writeFileSync4(path2, `${serialized}
14653
15650
  `);
@@ -14707,25 +15704,25 @@ var init_self_version = () => {};
14707
15704
  // src/adapters/opencode.ts
14708
15705
  import { execSync as execSync3 } from "node:child_process";
14709
15706
  import { existsSync as existsSync10, readFileSync as readFileSync8, rmSync as rmSync4, statSync as statSync6 } from "node:fs";
14710
- import { homedir as homedir13 } from "node:os";
14711
- import { dirname as dirname6, join as join13, parse, resolve as resolve7 } from "node:path";
15707
+ import { homedir as homedir14 } from "node:os";
15708
+ import { dirname as dirname7, join as join14, parse, resolve as resolve7 } from "node:path";
14712
15709
  import { fileURLToPath } from "node:url";
14713
15710
  function getOpenCodeConfigDir() {
14714
15711
  const envDir = process.env.OPENCODE_CONFIG_DIR?.trim();
14715
15712
  if (envDir)
14716
15713
  return resolve7(envDir);
14717
- const xdg = process.env.XDG_CONFIG_HOME || join13(homedir13(), ".config");
14718
- return join13(xdg, "opencode");
15714
+ const xdg = process.env.XDG_CONFIG_HOME || join14(homedir14(), ".config");
15715
+ return join14(xdg, "opencode");
14719
15716
  }
14720
15717
  function getOpenCodeCacheDir() {
14721
15718
  const xdg = process.env.XDG_CACHE_HOME;
14722
15719
  if (xdg)
14723
- return join13(xdg, "opencode");
15720
+ return join14(xdg, "opencode");
14724
15721
  if (process.platform === "win32") {
14725
- const localAppData = process.env.LOCALAPPDATA ?? join13(homedir13(), "AppData", "Local");
14726
- return join13(localAppData, "opencode");
15722
+ const localAppData = process.env.LOCALAPPDATA ?? join14(homedir14(), "AppData", "Local");
15723
+ return join14(localAppData, "opencode");
14727
15724
  }
14728
- return join13(homedir13(), ".cache", "opencode");
15725
+ return join14(homedir14(), ".cache", "opencode");
14729
15726
  }
14730
15727
  function hasOpenCodeCli() {
14731
15728
  try {
@@ -14738,12 +15735,12 @@ function hasOpenCodeCli() {
14738
15735
  function openCodeDesktopAppExists() {
14739
15736
  const candidates = [];
14740
15737
  if (process.platform === "darwin") {
14741
- candidates.push("/Applications/OpenCode.app", "/Applications/OpenCode Beta.app", join13(homedir13(), "Applications", "OpenCode.app"), join13(homedir13(), "Applications", "OpenCode Beta.app"));
15738
+ candidates.push("/Applications/OpenCode.app", "/Applications/OpenCode Beta.app", join14(homedir14(), "Applications", "OpenCode.app"), join14(homedir14(), "Applications", "OpenCode Beta.app"));
14742
15739
  } else if (process.platform === "win32") {
14743
- const localAppData = process.env.LOCALAPPDATA ?? join13(homedir13(), "AppData", "Local");
14744
- candidates.push(join13(localAppData, "Programs", "opencode"), join13(localAppData, "opencode"));
15740
+ const localAppData = process.env.LOCALAPPDATA ?? join14(homedir14(), "AppData", "Local");
15741
+ candidates.push(join14(localAppData, "Programs", "opencode"), join14(localAppData, "opencode"));
14745
15742
  } else {
14746
- candidates.push("/opt/OpenCode", "/usr/lib/opencode", join13(homedir13(), ".local", "share", "applications", "opencode.desktop"));
15743
+ candidates.push("/opt/OpenCode", "/usr/lib/opencode", join14(homedir14(), ".local", "share", "applications", "opencode.desktop"));
14747
15744
  }
14748
15745
  return candidates.some((p) => {
14749
15746
  try {
@@ -14772,15 +15769,15 @@ function pathPointsToOurPlugin(entry) {
14772
15769
  try {
14773
15770
  if (!existsSync10(fsPath))
14774
15771
  return false;
14775
- let searchDir = statSync6(fsPath).isDirectory() ? fsPath : dirname6(fsPath);
15772
+ let searchDir = statSync6(fsPath).isDirectory() ? fsPath : dirname7(fsPath);
14776
15773
  let pkgJsonPath = null;
14777
15774
  while (true) {
14778
- const candidate = join13(searchDir, "package.json");
15775
+ const candidate = join14(searchDir, "package.json");
14779
15776
  if (existsSync10(candidate)) {
14780
15777
  pkgJsonPath = candidate;
14781
15778
  break;
14782
15779
  }
14783
- const parent = dirname6(searchDir);
15780
+ const parent = dirname7(searchDir);
14784
15781
  if (parent === searchDir || searchDir === parse(searchDir).root)
14785
15782
  break;
14786
15783
  searchDir = parent;
@@ -14946,10 +15943,10 @@ class OpenCodeAdapter {
14946
15943
  };
14947
15944
  }
14948
15945
  getPluginCacheInfo() {
14949
- const path2 = join13(getOpenCodeCacheDir(), "packages", PLUGIN_ENTRY);
15946
+ const path2 = join14(getOpenCodeCacheDir(), "packages", PLUGIN_ENTRY);
14950
15947
  let cached;
14951
15948
  try {
14952
- const installedPkgPath = join13(path2, "node_modules", "@cortexkit", "aft-opencode", "package.json");
15949
+ const installedPkgPath = join14(path2, "node_modules", "@cortexkit", "aft-opencode", "package.json");
14953
15950
  if (existsSync10(installedPkgPath)) {
14954
15951
  const pkg = JSON.parse(readFileSync8(installedPkgPath, "utf-8"));
14955
15952
  cached = typeof pkg.version === "string" ? pkg.version : undefined;
@@ -14968,7 +15965,7 @@ class OpenCodeAdapter {
14968
15965
  return getCortexKitStorageRoot();
14969
15966
  }
14970
15967
  getLogFile() {
14971
- return getTmpLogPath("aft-plugin.log");
15968
+ return resolveAftLogPath("aft-plugin.log");
14972
15969
  }
14973
15970
  getInstallHint() {
14974
15971
  return "Install OpenCode: https://opencode.ai/docs/install";
@@ -15010,17 +16007,17 @@ class OpenCodeAdapter {
15010
16007
  describeStorageSubtrees() {
15011
16008
  const storage = this.getStorageDir();
15012
16009
  return {
15013
- index: dirSize(join13(storage, "index")),
15014
- semantic: dirSize(join13(storage, "semantic")),
15015
- backups: dirSize(join13(storage, "backups")),
15016
- url_cache: dirSize(join13(storage, "url_cache")),
15017
- onnxruntime: dirSize(join13(storage, "onnxruntime"))
16010
+ index: dirSize(join14(storage, "index")),
16011
+ semantic: dirSize(join14(storage, "semantic")),
16012
+ backups: dirSize(join14(storage, "backups")),
16013
+ url_cache: dirSize(join14(storage, "url_cache")),
16014
+ onnxruntime: dirSize(join14(storage, "onnxruntime"))
15018
16015
  };
15019
16016
  }
15020
16017
  }
15021
16018
  var PLUGIN_NAME = "@cortexkit/aft-opencode", PLUGIN_ENTRY;
15022
16019
  var init_opencode = __esm(() => {
15023
- init_dist();
16020
+ init_dist2();
15024
16021
  init_fs_util();
15025
16022
  init_jsonc();
15026
16023
  init_paths2();
@@ -15031,15 +16028,15 @@ var init_opencode = __esm(() => {
15031
16028
  // src/adapters/pi.ts
15032
16029
  import { execSync as execSync4, spawnSync as spawnSync4 } from "node:child_process";
15033
16030
  import { existsSync as existsSync11, readFileSync as readFileSync9 } from "node:fs";
15034
- import { homedir as homedir14 } from "node:os";
15035
- import { join as join14 } from "node:path";
16031
+ import { homedir as homedir15 } from "node:os";
16032
+ import { join as join15 } from "node:path";
15036
16033
  function getPiAgentDir() {
15037
16034
  const envHome = process.platform === "win32" ? process.env.USERPROFILE : process.env.HOME;
15038
- const home = envHome && envHome.length > 0 ? envHome : homedir14();
15039
- return join14(home, ".pi", "agent");
16035
+ const home = envHome && envHome.length > 0 ? envHome : homedir15();
16036
+ return join15(home, ".pi", "agent");
15040
16037
  }
15041
16038
  function readPiExtensionIndex() {
15042
- const settingsPath = join14(getPiAgentDir(), "settings.json");
16039
+ const settingsPath = join15(getPiAgentDir(), "settings.json");
15043
16040
  if (existsSync11(settingsPath)) {
15044
16041
  try {
15045
16042
  const raw = readFileSync9(settingsPath, "utf-8");
@@ -15053,10 +16050,10 @@ function readPiExtensionIndex() {
15053
16050
  } catch {}
15054
16051
  }
15055
16052
  const candidates = [
15056
- join14(getPiAgentDir(), "extensions.json"),
15057
- join14(getPiAgentDir(), "extensions.jsonc"),
15058
- join14(getPiAgentDir(), "config.json"),
15059
- join14(getPiAgentDir(), "config.jsonc")
16053
+ join15(getPiAgentDir(), "extensions.json"),
16054
+ join15(getPiAgentDir(), "extensions.jsonc"),
16055
+ join15(getPiAgentDir(), "config.json"),
16056
+ join15(getPiAgentDir(), "config.jsonc")
15060
16057
  ];
15061
16058
  for (const path2 of candidates) {
15062
16059
  if (!existsSync11(path2))
@@ -15092,14 +16089,14 @@ function piEntryMatchesAft(entry) {
15092
16089
  } else if (entry.startsWith("/")) {
15093
16090
  resolved = entry;
15094
16091
  } else if (entry.length > 0) {
15095
- resolved = join14(getPiAgentDir(), entry);
16092
+ resolved = join15(getPiAgentDir(), entry);
15096
16093
  }
15097
16094
  if (!resolved)
15098
16095
  return false;
15099
16096
  try {
15100
16097
  if (!existsSync11(resolved))
15101
16098
  return false;
15102
- const pkgPath = join14(resolved, "package.json");
16099
+ const pkgPath = join15(resolved, "package.json");
15103
16100
  if (!existsSync11(pkgPath))
15104
16101
  return false;
15105
16102
  const pkg = JSON.parse(readFileSync9(pkgPath, "utf-8"));
@@ -15151,7 +16148,7 @@ class PiAdapter {
15151
16148
  const aftConfigExists = existsSync11(aftConfigPath);
15152
16149
  return {
15153
16150
  configDir,
15154
- harnessConfig: index.path ?? join14(configDir, "extensions.json"),
16151
+ harnessConfig: index.path ?? join15(configDir, "extensions.json"),
15155
16152
  harnessConfigFormat: index.path ? "json" : "none",
15156
16153
  aftConfig: aftConfigPath,
15157
16154
  aftConfigFormat: aftConfigExists ? "jsonc" : "none"
@@ -15196,8 +16193,8 @@ class PiAdapter {
15196
16193
  }
15197
16194
  getPluginCacheInfo() {
15198
16195
  const candidates = [
15199
- join14(getPiAgentDir(), "node_modules", "@cortexkit", "aft-pi", "package.json"),
15200
- join14(getPiAgentDir(), "extensions", "node_modules", "@cortexkit", "aft-pi", "package.json")
16196
+ join15(getPiAgentDir(), "node_modules", "@cortexkit", "aft-pi", "package.json"),
16197
+ join15(getPiAgentDir(), "extensions", "node_modules", "@cortexkit", "aft-pi", "package.json")
15201
16198
  ];
15202
16199
  for (const candidate of candidates) {
15203
16200
  if (!existsSync11(candidate))
@@ -15214,7 +16211,7 @@ class PiAdapter {
15214
16211
  } catch {}
15215
16212
  }
15216
16213
  return {
15217
- path: join14(getPiAgentDir(), "extensions"),
16214
+ path: join15(getPiAgentDir(), "extensions"),
15218
16215
  exists: false
15219
16216
  };
15220
16217
  }
@@ -15222,7 +16219,7 @@ class PiAdapter {
15222
16219
  return getCortexKitStorageRoot();
15223
16220
  }
15224
16221
  getLogFile() {
15225
- return getTmpLogPath("aft-pi.log");
16222
+ return resolveAftLogPath("aft-plugin.log");
15226
16223
  }
15227
16224
  getInstallHint() {
15228
16225
  return "Install Pi: https://github.com/badlogic/pi-mono";
@@ -15236,17 +16233,17 @@ class PiAdapter {
15236
16233
  describeStorageSubtrees() {
15237
16234
  const storage = this.getStorageDir();
15238
16235
  return {
15239
- index: dirSize(join14(storage, "index")),
15240
- semantic: dirSize(join14(storage, "semantic")),
15241
- backups: dirSize(join14(storage, "backups")),
15242
- url_cache: dirSize(join14(storage, "url_cache")),
15243
- onnxruntime: dirSize(join14(storage, "onnxruntime"))
16236
+ index: dirSize(join15(storage, "index")),
16237
+ semantic: dirSize(join15(storage, "semantic")),
16238
+ backups: dirSize(join15(storage, "backups")),
16239
+ url_cache: dirSize(join15(storage, "url_cache")),
16240
+ onnxruntime: dirSize(join15(storage, "onnxruntime"))
15244
16241
  };
15245
16242
  }
15246
16243
  }
15247
16244
  var PLUGIN_NAME2 = "@cortexkit/aft-pi", PLUGIN_ENTRY2;
15248
16245
  var init_pi = __esm(() => {
15249
- init_dist();
16246
+ init_dist2();
15250
16247
  init_fs_util();
15251
16248
  init_jsonc();
15252
16249
  init_paths2();
@@ -15382,7 +16379,7 @@ var ANSI_RE, CONTROL_RE, CJKT_WIDE_RE, TAB_RE, EMOJI_RE, LATIN_RE, MODIFIER_RE,
15382
16379
  ellipsed: truncationEnabled && LIMIT >= ELLIPSIS_WIDTH
15383
16380
  };
15384
16381
  }, dist_default;
15385
- var init_dist2 = __esm(() => {
16382
+ var init_dist3 = __esm(() => {
15386
16383
  init_utils();
15387
16384
  ANSI_RE = /[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]|\u001b\]8;[^;]*;.*?(?:\u0007|\u001b\u005c)/y;
15388
16385
  CONTROL_RE = /[\x00-\x08\x0A-\x1F\x7F-\x9F]{1,1000}/y;
@@ -15399,8 +16396,8 @@ var init_dist2 = __esm(() => {
15399
16396
  var NO_TRUNCATION2, fastStringWidth = (input, options = {}) => {
15400
16397
  return dist_default(input, NO_TRUNCATION2, options).width;
15401
16398
  }, dist_default2;
15402
- var init_dist3 = __esm(() => {
15403
- init_dist2();
16399
+ var init_dist4 = __esm(() => {
16400
+ init_dist3();
15404
16401
  NO_TRUNCATION2 = {
15405
16402
  limit: Infinity,
15406
16403
  ellipsis: "",
@@ -15606,7 +16603,7 @@ var ESC = "\x1B", CSI = "›", END_CODE = 39, ANSI_ESCAPE_BELL = "\x07", ANSI_CSI
15606
16603
  return returnValue;
15607
16604
  }, CRLF_OR_LF;
15608
16605
  var init_main = __esm(() => {
15609
- init_dist3();
16606
+ init_dist4();
15610
16607
  ANSI_ESCAPE_LINK = `${ANSI_OSC}8;;`;
15611
16608
  GROUP_REGEX = new RegExp(`(?:\\${ANSI_CSI}(?<code>\\d+)m|\\${ANSI_ESCAPE_LINK}(?<uri>.*)${ANSI_ESCAPE_BELL})`, "y");
15612
16609
  CRLF_OR_LF = /\r?\n/;
@@ -15961,7 +16958,7 @@ function T2(r2, t2, i, s) {
15961
16958
  };
15962
16959
  }
15963
16960
  var import_sisteransi, a$2, t, settings, R, CANCEL_SYMBOL, getColumns = (e) => ("columns" in e) && typeof e.columns == "number" ? e.columns : 80, getRows = (e) => ("rows" in e) && typeof e.rows == "number" ? e.rows : 20, T$1, r, _, U, u$1, o$1, h, a$1, a2, n;
15964
- var init_dist4 = __esm(() => {
16961
+ var init_dist5 = __esm(() => {
15965
16962
  init_main();
15966
16963
  import_sisteransi = __toESM(require_src3(), 1);
15967
16964
  a$2 = ["up", "down", "left", "right", "space", "enter", "cancel"];
@@ -16813,11 +17810,11 @@ ${r2}
16813
17810
  }
16814
17811
  }
16815
17812
  }).prompt();
16816
- var init_dist5 = __esm(() => {
16817
- init_dist4();
16818
- init_dist4();
17813
+ var init_dist6 = __esm(() => {
17814
+ init_dist5();
17815
+ init_dist5();
16819
17816
  init_main();
16820
- init_dist3();
17817
+ init_dist4();
16821
17818
  import_sisteransi2 = __toESM(require_src3(), 1);
16822
17819
  unicode = isUnicodeSupported();
16823
17820
  S_STEP_ACTIVE = unicodeOr("◆", "*");
@@ -16954,7 +17951,7 @@ async function text2(message, options = {}) {
16954
17951
  return result;
16955
17952
  }
16956
17953
  var init_prompts = __esm(() => {
16957
- init_dist5();
17954
+ init_dist6();
16958
17955
  });
16959
17956
 
16960
17957
  // src/lib/harness-select.ts
@@ -17264,28 +18261,30 @@ var init_aft_bridge = () => {};
17264
18261
  // src/commands/lsp.ts
17265
18262
  var exports_lsp = {};
17266
18263
  __export(exports_lsp, {
18264
+ typescriptPackageWarning: () => typescriptPackageWarning,
17267
18265
  runLspDoctor: () => runLspDoctor,
17268
18266
  renderLspInspection: () => renderLspInspection,
17269
18267
  printLspDoctorHelp: () => printLspDoctorHelp,
17270
18268
  findProjectRootForFile: () => findProjectRootForFile
17271
18269
  });
17272
18270
  import { existsSync as existsSync12, readdirSync as readdirSync4, statSync as statSync7 } from "node:fs";
17273
- import { dirname as dirname7, join as join15, resolve as resolve8 } from "node:path";
18271
+ import { createRequire as createRequire4 } from "node:module";
18272
+ import { dirname as dirname8, join as join16, resolve as resolve8 } from "node:path";
17274
18273
  function findProjectRootForFile(filePath, fallbackCwd = process.cwd()) {
17275
18274
  const resolvedFile = resolve8(fallbackCwd, filePath);
17276
- let dir = dirname7(resolvedFile);
18275
+ let dir = dirname8(resolvedFile);
17277
18276
  try {
17278
18277
  if (existsSync12(resolvedFile) && statSync7(resolvedFile).isDirectory()) {
17279
18278
  dir = resolvedFile;
17280
18279
  }
17281
18280
  } catch {
17282
- dir = dirname7(resolvedFile);
18281
+ dir = dirname8(resolvedFile);
17283
18282
  }
17284
18283
  while (true) {
17285
- if (PROJECT_ROOT_MARKERS.some((marker) => existsSync12(join15(dir, marker)))) {
18284
+ if (PROJECT_ROOT_MARKERS.some((marker) => existsSync12(join16(dir, marker)))) {
17286
18285
  return dir;
17287
18286
  }
17288
- const parent = dirname7(dir);
18287
+ const parent = dirname8(dir);
17289
18288
  if (parent === dir)
17290
18289
  return resolve8(fallbackCwd);
17291
18290
  dir = parent;
@@ -17341,7 +18340,12 @@ async function runLspDoctor(options) {
17341
18340
  log2.error(inspect.message ?? inspect.code ?? "lsp_inspect failed");
17342
18341
  return 1;
17343
18342
  }
17344
- console.log(renderLspInspection(file, inspect));
18343
+ const inspection = inspect;
18344
+ const typescriptWarning = typescriptPackageWarning(inspection);
18345
+ console.log(renderLspInspection(file, {
18346
+ ...inspection,
18347
+ ...typescriptWarning ? { typescript_package_warning: typescriptWarning } : {}
18348
+ }));
17345
18349
  return 0;
17346
18350
  }
17347
18351
  function renderLspInspection(inputFile, response) {
@@ -17377,6 +18381,10 @@ function renderLspInspection(inputFile, response) {
17377
18381
  lines.push(` Action: ${installHint(server.binary_name)}`);
17378
18382
  }
17379
18383
  }
18384
+ if (response.typescript_package_warning) {
18385
+ lines.push("");
18386
+ lines.push(`Warning: ${response.typescript_package_warning}`);
18387
+ }
17380
18388
  const diagnostics = response.diagnostics ?? [];
17381
18389
  lines.push("");
17382
18390
  lines.push(`Diagnostics (${response.diagnostics_count ?? diagnostics.length} found):`);
@@ -17405,8 +18413,8 @@ function parseFileArg(argv) {
17405
18413
  function buildConfigureParams(adapter, projectRoot) {
17406
18414
  const userConfigPath = adapter.detectConfigPaths().aftConfig;
17407
18415
  const dir = adapter.kind === "pi" ? ".pi" : ".opencode";
17408
- const projectJsonc = join15(projectRoot, dir, "aft.jsonc");
17409
- const projectJson = join15(projectRoot, dir, "aft.json");
18416
+ const projectJsonc = join16(projectRoot, dir, "aft.jsonc");
18417
+ const projectJson = join16(projectRoot, dir, "aft.json");
17410
18418
  const projectConfigPath = existsSync12(projectJsonc) ? projectJsonc : projectJson;
17411
18419
  return {
17412
18420
  id: "doctor-lsp-configure",
@@ -17420,10 +18428,10 @@ function buildConfigureParams(adapter, projectRoot) {
17420
18428
  function inferLspPathsExtra(_lsp) {
17421
18429
  const paths = new Set;
17422
18430
  for (const entry of childDirs(getAftLspPackagesDir())) {
17423
- paths.add(join15(entry, "node_modules", ".bin"));
18431
+ paths.add(join16(entry, "node_modules", ".bin"));
17424
18432
  }
17425
18433
  for (const entry of childDirs(getAftLspBinariesDir())) {
17426
- paths.add(join15(entry, "bin"));
18434
+ paths.add(join16(entry, "bin"));
17427
18435
  }
17428
18436
  return [...paths];
17429
18437
  }
@@ -17431,7 +18439,7 @@ function childDirs(path2) {
17431
18439
  if (!existsSync12(path2))
17432
18440
  return [];
17433
18441
  try {
17434
- return readdirSync4(path2).map((entry) => join15(path2, entry)).filter((entry) => {
18442
+ return readdirSync4(path2).map((entry) => join16(path2, entry)).filter((entry) => {
17435
18443
  try {
17436
18444
  return statSync7(entry).isDirectory();
17437
18445
  } catch {
@@ -17466,6 +18474,18 @@ function formatSpawnStatus(server) {
17466
18474
  function formatList(values) {
17467
18475
  return values.length === 0 ? "(none)" : values.join(", ");
17468
18476
  }
18477
+ function typescriptPackageWarning(response) {
18478
+ const typescriptServerSpawned = response.matching_servers?.some((server) => server.id === "typescript" && server.spawn_status === "ok");
18479
+ const diagnosticsCount = response.diagnostics_count ?? response.diagnostics?.length ?? 0;
18480
+ if (!typescriptServerSpawned || !response.project_root || diagnosticsCount > 0)
18481
+ return null;
18482
+ try {
18483
+ createRequire4(join16(response.project_root, "package.json")).resolve("typescript");
18484
+ return null;
18485
+ } catch {
18486
+ return "typescript package not resolvable from project — server will produce no diagnostics";
18487
+ }
18488
+ }
17469
18489
  function installHint(binaryName) {
17470
18490
  if (binaryName === "ty")
17471
18491
  return "Install with `uv tool install ty` or `pip install ty`.";
@@ -17475,7 +18495,7 @@ function installHint(binaryName) {
17475
18495
  }
17476
18496
  var PROJECT_ROOT_MARKERS;
17477
18497
  var init_lsp = __esm(async () => {
17478
- init_dist();
18498
+ init_dist2();
17479
18499
  init_aft_bridge();
17480
18500
  init_harness_select();
17481
18501
  init_paths2();
@@ -17509,7 +18529,7 @@ __export(exports_doctor_filters, {
17509
18529
  printDoctorFiltersHelp: () => printDoctorFiltersHelp
17510
18530
  });
17511
18531
  import { existsSync as existsSync13 } from "node:fs";
17512
- import { homedir as homedir15 } from "node:os";
18532
+ import { homedir as homedir16 } from "node:os";
17513
18533
  import { relative as relative3, resolve as resolve9 } from "node:path";
17514
18534
  function printDoctorFiltersHelp() {
17515
18535
  console.log("Usage: aft doctor filters [--show <name>] [trust|untrust]");
@@ -17727,7 +18747,7 @@ function truncate(value) {
17727
18747
  return value.length <= 80 ? value : `${value.slice(0, 77)}…`;
17728
18748
  }
17729
18749
  function formatHome(path2) {
17730
- const home = homedir15();
18750
+ const home = homedir16();
17731
18751
  return path2.startsWith(home) ? `~${path2.slice(home.length)}` : path2;
17732
18752
  }
17733
18753
  function formatProjectPath(path2, projectRoot) {
@@ -17751,7 +18771,7 @@ var init_doctor_filters = __esm(async () => {
17751
18771
 
17752
18772
  // src/lib/binary-cache.ts
17753
18773
  import { existsSync as existsSync14, readdirSync as readdirSync5, statSync as statSync8 } from "node:fs";
17754
- import { join as join16 } from "node:path";
18774
+ import { join as join17 } from "node:path";
17755
18775
  function getBinaryCacheInfo(activeVersion) {
17756
18776
  const path2 = getAftBinaryCacheDir();
17757
18777
  if (!existsSync14(path2)) {
@@ -17764,7 +18784,7 @@ function getBinaryCacheInfo(activeVersion) {
17764
18784
  }
17765
18785
  const versions = readdirSync5(path2).filter((entry) => {
17766
18786
  try {
17767
- return statSync8(join16(path2, entry)).isDirectory();
18787
+ return statSync8(join17(path2, entry)).isDirectory();
17768
18788
  } catch {
17769
18789
  return false;
17770
18790
  }
@@ -17785,7 +18805,7 @@ var init_binary_cache = __esm(() => {
17785
18805
 
17786
18806
  // src/lib/sanitize.ts
17787
18807
  import { realpathSync as realpathSync3 } from "node:fs";
17788
- import { homedir as homedir16, userInfo } from "node:os";
18808
+ import { homedir as homedir17, userInfo } from "node:os";
17789
18809
  function escapeRegex(value) {
17790
18810
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
17791
18811
  }
@@ -17816,7 +18836,7 @@ function redactSecrets(content) {
17816
18836
  }
17817
18837
  function sanitizeContent(content) {
17818
18838
  const username = userInfo().username;
17819
- const home = homedir16();
18839
+ const home = homedir17();
17820
18840
  let sanitized = redactSecrets(content);
17821
18841
  const cwd = process.cwd();
17822
18842
  for (const candidate of new Set([cwd, safeRealpath(cwd)])) {
@@ -17863,11 +18883,9 @@ var init_sanitize = __esm(() => {
17863
18883
 
17864
18884
  // src/lib/bridge-tool-failures.ts
17865
18885
  import { closeSync as closeSync5, existsSync as existsSync15, openSync as openSync5, readSync as readSync2, statSync as statSync9 } from "node:fs";
17866
- import { tmpdir as tmpdir3 } from "node:os";
17867
- import { join as join17 } from "node:path";
17868
18886
  function resolveBridgePluginLogPath() {
17869
18887
  const isTestEnv = process.env.BUN_TEST === "1" || false;
17870
- return join17(tmpdir3(), isTestEnv ? "aft-plugin-test.log" : "aft-plugin.log");
18888
+ return resolveAftLogPath(isTestEnv ? "aft-plugin-test.log" : "aft-plugin.log");
17871
18889
  }
17872
18890
  function tailLogFileBytes(path2, maxBytes) {
17873
18891
  if (!existsSync15(path2) || maxBytes <= 0)
@@ -17998,31 +19016,156 @@ function buildRecentAftToolFailuresSectionFromLog(logPath = resolveBridgePluginL
17998
19016
  }
17999
19017
  var BRIDGE_LOG_TAIL_BYTES, MAX_TOOL_FAILURE_CLASSES = 30, SESSION_TAG_PATTERN, STRUCTURED_CODE_PATTERN;
18000
19018
  var init_bridge_tool_failures = __esm(() => {
19019
+ init_dist2();
18001
19020
  init_sanitize();
18002
19021
  BRIDGE_LOG_TAIL_BYTES = 2 * 1024 * 1024;
18003
19022
  SESSION_TAG_PATTERN = /\[ses_[^\]\s]+\]|\[[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}\]/g;
18004
19023
  STRUCTURED_CODE_PATTERN = /"code"\s*:\s*"([^"]+)"/;
18005
19024
  });
18006
19025
 
18007
- // src/lib/lsp-cache.ts
18008
- import { existsSync as existsSync16, readdirSync as readdirSync6, rmSync as rmSync5, statSync as statSync10 } from "node:fs";
19026
+ // src/lib/legacy-storage.ts
19027
+ import { existsSync as existsSync16, readdirSync as readdirSync6, statSync as statSync10 } from "node:fs";
18009
19028
  import { join as join18 } from "node:path";
19029
+ function summarizeLegacyPartitionDuplication(storageRoot) {
19030
+ if (!existsSync16(storageRoot)) {
19031
+ return { totalPartitions: 0, totalBytes: 0, byHarness: [] };
19032
+ }
19033
+ const byHarness = [];
19034
+ for (const harness of safeReadDir(storageRoot)) {
19035
+ const harnessPath = join18(storageRoot, harness);
19036
+ if (!isDirectory(harnessPath))
19037
+ continue;
19038
+ const partitions = new Map;
19039
+ collectCallgraphPartitions(join18(harnessPath, "callgraph"), partitions);
19040
+ collectInspectPartitions(join18(harnessPath, "inspect"), partitions);
19041
+ if (partitions.size === 0)
19042
+ continue;
19043
+ let bytes = 0;
19044
+ for (const size of partitions.values())
19045
+ bytes += size;
19046
+ byHarness.push({ harness, partitions: partitions.size, bytes });
19047
+ }
19048
+ byHarness.sort((left, right) => left.harness.localeCompare(right.harness));
19049
+ return {
19050
+ totalPartitions: byHarness.reduce((sum, item) => sum + item.partitions, 0),
19051
+ totalBytes: byHarness.reduce((sum, item) => sum + item.bytes, 0),
19052
+ byHarness
19053
+ };
19054
+ }
19055
+ function collectCallgraphPartitions(domainPath, partitions) {
19056
+ if (!isDirectory(domainPath))
19057
+ return;
19058
+ for (const name of safeReadDir(domainPath)) {
19059
+ const path2 = join18(domainPath, name);
19060
+ if (isDirectory(path2)) {
19061
+ if (!looksLikePartitionKey(name))
19062
+ continue;
19063
+ addPartitionBytes(partitions, `callgraph:${name}`, dirSize(path2));
19064
+ continue;
19065
+ }
19066
+ const key = callgraphPartitionKeyFromName(name);
19067
+ if (!key)
19068
+ continue;
19069
+ addPartitionBytes(partitions, `callgraph:${key}`, safeFileSize(path2));
19070
+ }
19071
+ }
19072
+ function collectInspectPartitions(domainPath, partitions) {
19073
+ if (!isDirectory(domainPath))
19074
+ return;
19075
+ for (const name of safeReadDir(domainPath)) {
19076
+ const path2 = join18(domainPath, name);
19077
+ if (isDirectory(path2)) {
19078
+ if (!looksLikePartitionKey(name))
19079
+ continue;
19080
+ addPartitionBytes(partitions, `inspect:${name}`, dirSize(path2));
19081
+ continue;
19082
+ }
19083
+ const key = inspectPartitionKeyFromName(name);
19084
+ if (!key)
19085
+ continue;
19086
+ addPartitionBytes(partitions, `inspect:${key}`, safeFileSize(path2));
19087
+ }
19088
+ }
19089
+ function addPartitionBytes(partitions, key, bytes) {
19090
+ partitions.set(key, (partitions.get(key) ?? 0) + bytes);
19091
+ }
19092
+ function callgraphPartitionKeyFromName(name) {
19093
+ if (name.includes(".tmp."))
19094
+ return null;
19095
+ if (name.endsWith(".current")) {
19096
+ const key2 = name.slice(0, -".current".length);
19097
+ return looksLikePartitionKey(key2) ? key2 : null;
19098
+ }
19099
+ const base = sqliteishBaseName(name);
19100
+ if (!base)
19101
+ return null;
19102
+ const split = base.indexOf(".g");
19103
+ const key = split === -1 ? base : base.slice(0, split);
19104
+ return looksLikePartitionKey(key) ? key : null;
19105
+ }
19106
+ function inspectPartitionKeyFromName(name) {
19107
+ if (name.includes(".tmp."))
19108
+ return null;
19109
+ const base = sqliteishBaseName(name);
19110
+ return base && looksLikePartitionKey(base) ? base : null;
19111
+ }
19112
+ function sqliteishBaseName(name) {
19113
+ for (const suffix of SQLITE_SUFFIXES) {
19114
+ if (name.endsWith(suffix)) {
19115
+ return name.slice(0, -suffix.length);
19116
+ }
19117
+ }
19118
+ return null;
19119
+ }
19120
+ function looksLikePartitionKey(value) {
19121
+ return /^[0-9a-fA-F]{16}$/.test(value);
19122
+ }
19123
+ function safeReadDir(path2) {
19124
+ try {
19125
+ return readdirSync6(path2).sort((left, right) => left.localeCompare(right));
19126
+ } catch {
19127
+ return [];
19128
+ }
19129
+ }
19130
+ function isDirectory(path2) {
19131
+ try {
19132
+ return statSync10(path2).isDirectory();
19133
+ } catch {
19134
+ return false;
19135
+ }
19136
+ }
19137
+ function safeFileSize(path2) {
19138
+ try {
19139
+ return statSync10(path2).isFile() ? statSync10(path2).size : 0;
19140
+ } catch {
19141
+ return 0;
19142
+ }
19143
+ }
19144
+ var SQLITE_SUFFIXES;
19145
+ var init_legacy_storage = __esm(() => {
19146
+ init_fs_util();
19147
+ SQLITE_SUFFIXES = [".sqlite-wal", ".sqlite-shm", ".sqlite-journal", ".sqlite"];
19148
+ });
19149
+
19150
+ // src/lib/lsp-cache.ts
19151
+ import { existsSync as existsSync17, readdirSync as readdirSync7, rmSync as rmSync5, statSync as statSync11 } from "node:fs";
19152
+ import { join as join19 } from "node:path";
18010
19153
  function inspectDir(path2) {
18011
- if (!existsSync16(path2)) {
19154
+ if (!existsSync17(path2)) {
18012
19155
  return { entries: [], totalSize: 0 };
18013
19156
  }
18014
19157
  const entries = [];
18015
19158
  let totalSize = 0;
18016
19159
  let names;
18017
19160
  try {
18018
- names = readdirSync6(path2);
19161
+ names = readdirSync7(path2);
18019
19162
  } catch {
18020
19163
  return { entries: [], totalSize: 0 };
18021
19164
  }
18022
19165
  for (const name of names) {
18023
- const full = join18(path2, name);
19166
+ const full = join19(path2, name);
18024
19167
  try {
18025
- if (!statSync10(full).isDirectory())
19168
+ if (!statSync11(full).isDirectory())
18026
19169
  continue;
18027
19170
  const size = dirSize(full);
18028
19171
  entries.push({
@@ -18078,8 +19221,8 @@ var init_lsp_cache = __esm(() => {
18078
19221
  });
18079
19222
 
18080
19223
  // src/lib/onnx.ts
18081
- import { existsSync as existsSync17, readdirSync as readdirSync7, readlinkSync as readlinkSync2, realpathSync as realpathSync4 } from "node:fs";
18082
- import { basename as basename3, isAbsolute as isAbsolute6, join as join19, resolve as resolve10, win32 as win322 } from "node:path";
19224
+ import { existsSync as existsSync18, readdirSync as readdirSync8, readlinkSync as readlinkSync2, realpathSync as realpathSync4 } from "node:fs";
19225
+ import { basename as basename3, isAbsolute as isAbsolute6, join as join20, resolve as resolve10, win32 as win322 } from "node:path";
18083
19226
  function getOnnxLibraryName() {
18084
19227
  if (process.platform === "darwin")
18085
19228
  return "libonnxruntime.dylib";
@@ -18119,9 +19262,20 @@ function pathEntriesForPlatform2() {
18119
19262
  return isAbsolute6(entry) || win322.isAbsolute(entry);
18120
19263
  });
18121
19264
  }
19265
+ function isWindowsSystem32Directory2(dir) {
19266
+ if (process.platform !== "win32")
19267
+ return false;
19268
+ const normalizedDir = win322.resolve(dir).replace(/[\\/]+$/, "").toLowerCase();
19269
+ const windowsRoots = [process.env.SystemRoot, process.env.windir, "C:\\Windows"];
19270
+ return windowsRoots.some((root) => {
19271
+ if (!root)
19272
+ return false;
19273
+ return normalizedDir === win322.resolve(root, "System32").replace(/[\\/]+$/, "").toLowerCase();
19274
+ });
19275
+ }
18122
19276
  function directoryContainsLibrary2(dir, libName) {
18123
19277
  try {
18124
- const entries = readdirSync7(dir);
19278
+ const entries = readdirSync8(dir);
18125
19279
  if (process.platform === "win32") {
18126
19280
  const expected = libName.toLowerCase();
18127
19281
  return entries.some((entry) => entry.toLowerCase() === expected);
@@ -18131,6 +19285,24 @@ function directoryContainsLibrary2(dir, libName) {
18131
19285
  return false;
18132
19286
  }
18133
19287
  }
19288
+ function findIgnoredWindowsSystemOnnxRuntime() {
19289
+ if (process.platform !== "win32")
19290
+ return null;
19291
+ const windowsRoots = [process.env.SystemRoot, process.env.windir, "C:\\Windows"];
19292
+ const seen = new Set;
19293
+ for (const root of windowsRoots) {
19294
+ if (!root)
19295
+ continue;
19296
+ const systemDir = join20(root, "System32");
19297
+ const key = win322.resolve(systemDir).toLowerCase();
19298
+ if (seen.has(key))
19299
+ continue;
19300
+ seen.add(key);
19301
+ if (directoryContainsLibrary2(systemDir, getOnnxLibraryName()))
19302
+ return systemDir;
19303
+ }
19304
+ return null;
19305
+ }
18134
19306
  function findSystemOnnxRuntime2() {
18135
19307
  const libName = getOnnxLibraryName();
18136
19308
  const searchPaths = [];
@@ -18142,21 +19314,21 @@ function findSystemOnnxRuntime2() {
18142
19314
  searchPaths.push(...pathEntriesForPlatform2());
18143
19315
  const programFiles = process.env.ProgramFiles ?? "C:\\Program Files";
18144
19316
  const programFilesX86 = process.env["ProgramFiles(x86)"] ?? "C:\\Program Files (x86)";
18145
- searchPaths.push(join19(programFiles, "onnxruntime", "lib"), join19(programFiles, "Microsoft ONNX Runtime", "lib"), join19(programFiles, "Microsoft Machine Learning", "lib"), join19(programFilesX86, "onnxruntime", "lib"), ...(() => {
19317
+ searchPaths.push(join20(programFiles, "onnxruntime", "lib"), join20(programFiles, "Microsoft ONNX Runtime", "lib"), join20(programFiles, "Microsoft Machine Learning", "lib"), join20(programFilesX86, "onnxruntime", "lib"), ...(() => {
18146
19318
  const nugetPaths = [];
18147
19319
  const userProfile = process.env.USERPROFILE ?? "";
18148
19320
  if (!userProfile)
18149
19321
  return nugetPaths;
18150
- const nugetPackageDir = join19(userProfile, ".nuget", "packages", "microsoft.ml.onnxruntime");
18151
- if (!existsSync17(nugetPackageDir))
19322
+ const nugetPackageDir = join20(userProfile, ".nuget", "packages", "microsoft.ml.onnxruntime");
19323
+ if (!existsSync18(nugetPackageDir))
18152
19324
  return nugetPaths;
18153
19325
  try {
18154
- for (const entry of readdirSync7(nugetPackageDir, { withFileTypes: true })) {
19326
+ for (const entry of readdirSync8(nugetPackageDir, { withFileTypes: true })) {
18155
19327
  if (!entry.isDirectory())
18156
19328
  continue;
18157
19329
  if (entry.name === "__globalPackagesFolder" || entry.name.startsWith("."))
18158
19330
  continue;
18159
- nugetPaths.push(join19(nugetPackageDir, entry.name, "runtimes", "win-x64", "native"), join19(nugetPackageDir, entry.name, "runtimes", "win-arm64", "native"));
19331
+ nugetPaths.push(join20(nugetPackageDir, entry.name, "runtimes", "win-x64", "native"), join20(nugetPackageDir, entry.name, "runtimes", "win-arm64", "native"));
18160
19332
  }
18161
19333
  } catch {}
18162
19334
  return nugetPaths;
@@ -18176,6 +19348,8 @@ function findSystemOnnxRuntime2() {
18176
19348
  continue;
18177
19349
  const version = detectOrtVersion(dir);
18178
19350
  if (!version) {
19351
+ if (isWindowsSystem32Directory2(dir))
19352
+ continue;
18179
19353
  unknownVersionPaths.push(dir);
18180
19354
  continue;
18181
19355
  }
@@ -18186,12 +19360,12 @@ function findSystemOnnxRuntime2() {
18186
19360
  return unknownVersionPaths[0] ?? null;
18187
19361
  }
18188
19362
  function findCachedOnnxRuntime(storageDir) {
18189
- const ortDir = join19(storageDir, "onnxruntime", ONNX_RUNTIME_VERSION);
19363
+ const ortDir = join20(storageDir, "onnxruntime", ONNX_RUNTIME_VERSION);
18190
19364
  const libName = getOnnxLibraryName();
18191
- if (existsSync17(join19(ortDir, libName)))
19365
+ if (existsSync18(join20(ortDir, libName)))
18192
19366
  return ortDir;
18193
- const libSubdir = join19(ortDir, "lib");
18194
- if (existsSync17(join19(libSubdir, libName)))
19367
+ const libSubdir = join20(ortDir, "lib");
19368
+ if (existsSync18(join20(libSubdir, libName)))
18195
19369
  return libSubdir;
18196
19370
  return null;
18197
19371
  }
@@ -18212,11 +19386,11 @@ function parseOrtVersionFromDirectoryPath(value) {
18212
19386
  return null;
18213
19387
  }
18214
19388
  function detectOrtVersion(libDir) {
18215
- if (!existsSync17(libDir))
19389
+ if (!existsSync18(libDir))
18216
19390
  return null;
18217
19391
  const libName = getOnnxLibraryName();
18218
19392
  try {
18219
- const entries = readdirSync7(libDir);
19393
+ const entries = readdirSync8(libDir);
18220
19394
  const barePrefix = libName.replace(/\.(so|dylib|dll)$/, "");
18221
19395
  const expectedPrefix = process.platform === "win32" ? barePrefix.toLowerCase() : barePrefix;
18222
19396
  for (const entry of entries) {
@@ -18227,8 +19401,8 @@ function detectOrtVersion(libDir) {
18227
19401
  if (version)
18228
19402
  return version;
18229
19403
  }
18230
- const base = join19(libDir, libName);
18231
- if (existsSync17(base)) {
19404
+ const base = join20(libDir, libName);
19405
+ if (existsSync18(base)) {
18232
19406
  try {
18233
19407
  const real = realpathSync4(base);
18234
19408
  const version = parseOrtVersionFromPath(real) ?? parseOrtVersionFromDirectoryPath(real);
@@ -18263,10 +19437,10 @@ import {
18263
19437
  accessSync,
18264
19438
  closeSync as closeSync6,
18265
19439
  constants,
18266
- existsSync as existsSync18,
19440
+ existsSync as existsSync19,
18267
19441
  openSync as openSync6,
18268
19442
  readSync as readSync3,
18269
- statSync as statSync11
19443
+ statSync as statSync12
18270
19444
  } from "node:fs";
18271
19445
  async function collectDiagnostics(adapters) {
18272
19446
  const cliVersion = getSelfVersion();
@@ -18301,7 +19475,7 @@ async function diagnoseHarness(adapter) {
18301
19475
  const logPath = adapter.getLogFile();
18302
19476
  const pluginCache = adapter.getPluginCacheInfo();
18303
19477
  const storageAccessible = (() => {
18304
- if (!existsSync18(storage))
19478
+ if (!existsSync19(storage))
18305
19479
  return false;
18306
19480
  try {
18307
19481
  accessSync(storage, constants.R_OK | constants.W_OK);
@@ -18311,8 +19485,10 @@ async function diagnoseHarness(adapter) {
18311
19485
  }
18312
19486
  })();
18313
19487
  const describeStorage = "describeStorageSubtrees" in adapter && typeof adapter.describeStorageSubtrees === "function" ? adapter.describeStorageSubtrees() : {};
19488
+ const legacyDuplication = summarizeLegacyPartitionDuplication(storage);
18314
19489
  const semanticEnabled = aftEnabled && (aftConfigRead.value?.semantic_search === true || aftConfigRead.value?.experimental_semantic_search === true);
18315
19490
  const systemOrtDir = findSystemOnnxRuntime2();
19491
+ const ignoredSystemOrtDir = systemOrtDir ? null : findIgnoredWindowsSystemOnnxRuntime();
18316
19492
  const cachedOrtDir = findCachedOnnxRuntime(storage);
18317
19493
  const systemVersion = systemOrtDir ? detectOrtVersion(systemOrtDir) : null;
18318
19494
  const cachedVersion = cachedOrtDir ? detectOrtVersion(cachedOrtDir) : null;
@@ -18324,7 +19500,7 @@ async function diagnoseHarness(adapter) {
18324
19500
  pluginRegistered: adapter.hasPluginEntry(),
18325
19501
  configPaths,
18326
19502
  aftConfig: {
18327
- exists: existsSync18(configPaths.aftConfig),
19503
+ exists: existsSync19(configPaths.aftConfig),
18328
19504
  ...aftConfigRead.error ? { parseError: aftConfigRead.error } : {},
18329
19505
  enabled: aftEnabled,
18330
19506
  ...aftEnabledSource ? { enabledSource: aftEnabledSource } : {},
@@ -18333,15 +19509,18 @@ async function diagnoseHarness(adapter) {
18333
19509
  pluginCache,
18334
19510
  storageDir: {
18335
19511
  path: storage,
18336
- exists: existsSync18(storage),
19512
+ exists: existsSync19(storage),
18337
19513
  accessible: storageAccessible,
18338
- sizesByKey: describeStorage
19514
+ sizesByKey: describeStorage,
19515
+ ...legacyDuplication.totalPartitions > 0 ? { legacyDuplication } : {}
18339
19516
  },
18340
19517
  onnxRuntime: {
18341
19518
  required: semanticEnabled,
18342
19519
  systemPath: systemOrtDir,
18343
19520
  systemVersion,
18344
19521
  systemCompatible: systemVersion ? isOrtVersionCompatible(systemVersion) : null,
19522
+ ignoredSystemPath: ignoredSystemOrtDir,
19523
+ ignoredSystemReason: ignoredSystemOrtDir ? "version unreadable (Windows system copy) — ignored" : null,
18345
19524
  cachedPath: cachedOrtDir,
18346
19525
  cachedVersion,
18347
19526
  cachedCompatible: cachedVersion ? isOrtVersionCompatible(cachedVersion) : null,
@@ -18351,8 +19530,8 @@ async function diagnoseHarness(adapter) {
18351
19530
  },
18352
19531
  logFile: {
18353
19532
  path: logPath,
18354
- exists: existsSync18(logPath),
18355
- sizeKb: existsSync18(logPath) ? Math.round(statSync11(logPath).size / 1024) : 0
19533
+ exists: existsSync19(logPath),
19534
+ sizeKb: existsSync19(logPath) ? Math.round(statSync12(logPath).size / 1024) : 0
18356
19535
  }
18357
19536
  };
18358
19537
  }
@@ -18507,7 +19686,7 @@ function collectDiagnosticIssues(report) {
18507
19686
  code: "onnx_missing",
18508
19687
  severity: "medium",
18509
19688
  scope: h2.displayName,
18510
- message: "ONNX Runtime is required for semantic search but was not detected.",
19689
+ message: h2.onnxRuntime.ignoredSystemPath ? `ONNX Runtime at ${h2.onnxRuntime.ignoredSystemPath} has a ${h2.onnxRuntime.ignoredSystemReason}; no compatible runtime was detected.` : "ONNX Runtime is required for semantic search but was not detected.",
18511
19690
  remediation: `Run \`aft doctor --fix\` or install ONNX Runtime manually (${h2.onnxRuntime.installHint}).`
18512
19691
  });
18513
19692
  }
@@ -18548,14 +19727,14 @@ function formatDiagnosticIssuesSection(report) {
18548
19727
  return lines;
18549
19728
  }
18550
19729
  function tailLogFile(path2, lines) {
18551
- if (!existsSync18(path2))
19730
+ if (!existsSync19(path2))
18552
19731
  return "";
18553
19732
  if (lines <= 0)
18554
19733
  return "";
18555
19734
  const chunkSize = 64 * 1024;
18556
19735
  let fd = null;
18557
19736
  try {
18558
- const size = statSync11(path2).size;
19737
+ const size = statSync12(path2).size;
18559
19738
  fd = openSync6(path2, "r");
18560
19739
  const chunks = [];
18561
19740
  let position = size;
@@ -18585,9 +19764,10 @@ function tailLogFile(path2, lines) {
18585
19764
  }
18586
19765
  }
18587
19766
  var init_diagnostics = __esm(async () => {
18588
- init_dist();
19767
+ init_dist2();
18589
19768
  init_binary_cache();
18590
19769
  init_jsonc();
19770
+ init_legacy_storage();
18591
19771
  init_lsp_cache();
18592
19772
  init_onnx();
18593
19773
  init_sanitize();
@@ -18735,34 +19915,42 @@ var init_issue_body = __esm(() => {
18735
19915
  });
18736
19916
 
18737
19917
  // src/lib/onnx-fix.ts
18738
- import { existsSync as existsSync19, rmSync as rmSync6 } from "node:fs";
18739
- import { join as join20 } from "node:path";
19918
+ import { existsSync as existsSync20, rmSync as rmSync6 } from "node:fs";
19919
+ import { join as join21 } from "node:path";
18740
19920
  function findOnnxFixCandidates(report) {
18741
19921
  const candidates = [];
18742
19922
  for (const harness of report.harnesses) {
18743
19923
  if (!harness.onnxRuntime.required)
18744
19924
  continue;
18745
- if (!harness.storageDir.exists)
18746
- continue;
18747
- const storageOnnxDir = join20(harness.storageDir.path, "onnxruntime");
19925
+ const storageOnnxDir = join21(harness.storageDir.path, "onnxruntime");
18748
19926
  const systemTooOld = harness.onnxRuntime.systemPath !== null && harness.onnxRuntime.systemCompatible === false;
18749
19927
  const cachedTooOld = harness.onnxRuntime.cachedPath !== null && harness.onnxRuntime.cachedCompatible === false;
18750
19928
  const hasCompatibleCached = harness.onnxRuntime.cachedCompatible === true;
18751
19929
  if (cachedTooOld) {
18752
19930
  candidates.push({
18753
19931
  harness,
18754
- reason: `cached ONNX Runtime at ${harness.onnxRuntime.cachedPath} is v${harness.onnxRuntime.cachedVersion}, but AFT requires ${harness.onnxRuntime.requirement}. Clearing forces a fresh download on next start.`,
19932
+ reason: `cached ONNX Runtime at ${harness.onnxRuntime.cachedPath} is v${harness.onnxRuntime.cachedVersion}, but AFT requires ${harness.onnxRuntime.requirement}. Clearing it allows an immediate managed download.`,
18755
19933
  storageOnnxDir,
18756
- storageOnnxBytes: existsSync19(storageOnnxDir) ? dirSize(storageOnnxDir) : 0
19934
+ storageOnnxBytes: existsSync20(storageOnnxDir) ? dirSize(storageOnnxDir) : 0
18757
19935
  });
18758
19936
  continue;
18759
19937
  }
18760
19938
  if (systemTooOld && !hasCompatibleCached) {
18761
19939
  candidates.push({
18762
19940
  harness,
18763
- reason: `system ONNX Runtime at ${harness.onnxRuntime.systemPath} is v${harness.onnxRuntime.systemVersion}, but AFT requires ${harness.onnxRuntime.requirement}, and no AFT-managed install is present. AFT v0.19.5+ skips incompatible system installs and auto-downloads v1.24 on next start; clearing any stale state here ensures a clean slate.`,
19941
+ reason: `system ONNX Runtime at ${harness.onnxRuntime.systemPath} is v${harness.onnxRuntime.systemVersion}, but AFT requires ${harness.onnxRuntime.requirement}, and no AFT-managed install is present. AFT will leave the system copy untouched and download v1.24 into managed storage.`,
19942
+ storageOnnxDir,
19943
+ storageOnnxBytes: existsSync20(storageOnnxDir) ? dirSize(storageOnnxDir) : 0
19944
+ });
19945
+ continue;
19946
+ }
19947
+ if (!harness.onnxRuntime.systemPath && !harness.onnxRuntime.cachedPath) {
19948
+ const ignoredCopy = harness.onnxRuntime.ignoredSystemPath ? ` The Windows system copy at ${harness.onnxRuntime.ignoredSystemPath} was ignored because its version is unreadable.` : "";
19949
+ candidates.push({
19950
+ harness,
19951
+ reason: `no compatible ONNX Runtime is installed.${ignoredCopy} AFT will download v1.24 into managed storage.`,
18764
19952
  storageOnnxDir,
18765
- storageOnnxBytes: existsSync19(storageOnnxDir) ? dirSize(storageOnnxDir) : 0
19953
+ storageOnnxBytes: existsSync20(storageOnnxDir) ? dirSize(storageOnnxDir) : 0
18766
19954
  });
18767
19955
  }
18768
19956
  }
@@ -18770,58 +19958,74 @@ function findOnnxFixCandidates(report) {
18770
19958
  }
18771
19959
  async function runOnnxFix(adapters, report, options = {}) {
18772
19960
  const candidates = findOnnxFixCandidates(report);
18773
- if (candidates.length === 0) {
19961
+ if (candidates.length === 0)
18774
19962
  return null;
18775
- }
18776
- log2.warn(`Found ${candidates.length} ONNX Runtime issue(s) that --fix can address by clearing AFT-managed cache:`);
18777
- for (const c3 of candidates) {
18778
- log2.info(` • ${c3.harness.displayName}: ${c3.reason}`);
18779
- if (c3.storageOnnxBytes > 0) {
18780
- log2.info(` will delete: ${c3.storageOnnxDir} (${formatBytes(c3.storageOnnxBytes)})`);
19963
+ log2.warn(`Found ${candidates.length} ONNX Runtime issue(s) that --fix can repair with an AFT-managed runtime:`);
19964
+ for (const candidate of candidates) {
19965
+ log2.info(` ${candidate.harness.displayName}: ${candidate.reason}`);
19966
+ if (candidate.storageOnnxBytes > 0) {
19967
+ log2.info(` will delete: ${candidate.storageOnnxDir} (${formatBytes(candidate.storageOnnxBytes)})`);
18781
19968
  } else {
18782
- log2.info(` no AFT-managed ONNX cache to delete; nothing to reclaim`);
19969
+ log2.info(" no AFT-managed ONNX cache to delete");
18783
19970
  }
18784
19971
  }
18785
- note("This NEVER touches system paths like /usr/lib. It only deletes AFT's own ONNX download cache. " + "On next bridge start, AFT will re-download ONNX Runtime v1.24 and use that instead of the " + "incompatible system library.", "Safe operation");
19972
+ note("This NEVER touches system paths like /usr/lib or C:\\Windows\\System32. It only replaces AFT's own ONNX cache, then downloads the compatible managed runtime.", "Safe operation");
18786
19973
  const confirmFn = options.confirmFn ?? confirm2;
18787
19974
  const proceed = options.yes ? true : await confirmFn("Proceed with the fixes above?", true);
18788
19975
  if (!proceed) {
18789
19976
  log2.info("Skipped — no changes made.");
18790
19977
  return null;
18791
19978
  }
18792
- const result = { cleared: 0, bytesReclaimed: 0, errors: [] };
19979
+ const result = { cleared: 0, installed: 0, bytesReclaimed: 0, errors: [] };
18793
19980
  const rmFn = options.rmFn ?? rmSync6;
18794
- for (const c3 of candidates) {
18795
- if (!existsSync19(c3.storageOnnxDir)) {
18796
- log2.success(`${c3.harness.displayName}: no cached state to clear; restart your harness to trigger a fresh ONNX download`);
18797
- continue;
19981
+ const ensureFn = options.ensureFn ?? ensureOnnxRuntime;
19982
+ for (const candidate of candidates) {
19983
+ if (existsSync20(candidate.storageOnnxDir)) {
19984
+ try {
19985
+ rmFn(candidate.storageOnnxDir, { recursive: true, force: true });
19986
+ result.cleared += 1;
19987
+ result.bytesReclaimed += candidate.storageOnnxBytes;
19988
+ log2.success(`${candidate.harness.displayName}: cleared ${candidate.storageOnnxDir} (reclaimed ${formatBytes(candidate.storageOnnxBytes)})`);
19989
+ } catch (err) {
19990
+ const message = err instanceof Error ? err.message : String(err);
19991
+ log2.error(`${candidate.harness.displayName}: failed to clear ${candidate.storageOnnxDir}: ${message}`);
19992
+ result.errors.push({ path: candidate.storageOnnxDir, error: message });
19993
+ continue;
19994
+ }
18798
19995
  }
18799
19996
  try {
18800
- rmFn(c3.storageOnnxDir, { recursive: true, force: true });
18801
- result.cleared += 1;
18802
- result.bytesReclaimed += c3.storageOnnxBytes;
18803
- log2.success(`${c3.harness.displayName}: cleared ${c3.storageOnnxDir} (reclaimed ${formatBytes(c3.storageOnnxBytes)})`);
19997
+ log2.info(`${candidate.harness.displayName}: downloading managed ONNX Runtime…`);
19998
+ const installedPath = await ensureFn(candidate.harness.storageDir.path);
19999
+ if (!installedPath) {
20000
+ const message = "managed ONNX Runtime download was unavailable";
20001
+ log2.error(`${candidate.harness.displayName}: ${message}`);
20002
+ result.errors.push({ path: candidate.storageOnnxDir, error: message });
20003
+ continue;
20004
+ }
20005
+ result.installed += 1;
20006
+ log2.success(`${candidate.harness.displayName}: ONNX Runtime installed at ${installedPath}`);
18804
20007
  } catch (err) {
18805
- const message = err.message ?? "unknown error";
18806
- log2.error(`${c3.harness.displayName}: failed to clear ${c3.storageOnnxDir}: ${message}`);
18807
- result.errors.push({ path: c3.storageOnnxDir, error: message });
20008
+ const message = err instanceof Error ? err.message : String(err);
20009
+ log2.error(`${candidate.harness.displayName}: ONNX Runtime download failed: ${message}`);
20010
+ result.errors.push({ path: candidate.storageOnnxDir, error: message });
18808
20011
  }
18809
20012
  }
18810
- if (result.cleared > 0 || candidates.some((c3) => c3.storageOnnxBytes === 0)) {
18811
- note("Restart your AFT-using harness (OpenCode / Pi) to trigger a fresh ONNX Runtime download. " + "Watch the TUI sidebar the Semantic Index status should move from 'failed' → 'building' → 'ready'.", "Next step");
20013
+ if (result.installed > 0) {
20014
+ note("Restart your AFT-using harness (OpenCode / Pi) so semantic indexing uses the newly installed managed runtime.", "Next step");
18812
20015
  }
18813
20016
  return result;
18814
20017
  }
18815
20018
  var init_onnx_fix = __esm(() => {
20019
+ init_dist2();
18816
20020
  init_fs_util();
18817
20021
  init_prompts();
18818
20022
  });
18819
20023
 
18820
20024
  // src/lib/sessions.ts
18821
- import { existsSync as existsSync20, readdirSync as readdirSync8, readFileSync as readFileSync10, statSync as statSync12 } from "node:fs";
18822
- import { createRequire as createRequire4 } from "node:module";
18823
- import { homedir as homedir17 } from "node:os";
18824
- import { basename as basename4, join as join21 } from "node:path";
20025
+ import { existsSync as existsSync21, readdirSync as readdirSync9, readFileSync as readFileSync10, statSync as statSync13 } from "node:fs";
20026
+ import { createRequire as createRequire5 } from "node:module";
20027
+ import { homedir as homedir18 } from "node:os";
20028
+ import { basename as basename4, join as join22 } from "node:path";
18825
20029
  function listRecentSessions(adapter) {
18826
20030
  try {
18827
20031
  if (adapter.kind === "opencode")
@@ -18850,12 +20054,12 @@ function mapOpenCodeSessionRows(rows) {
18850
20054
  }).filter((session) => session !== null).sort((a3, b2) => b2.lastActivity - a3.lastActivity).slice(0, MAX_RECENT_SESSIONS);
18851
20055
  }
18852
20056
  function listRecentOpenCodeSessions() {
18853
- const dbPath = join21(getXdgDataHome(), "opencode", "opencode.db");
18854
- if (!existsSync20(dbPath))
20057
+ const dbPath = join22(getXdgDataHome(), "opencode", "opencode.db");
20058
+ if (!existsSync21(dbPath))
18855
20059
  return [];
18856
20060
  let db = null;
18857
20061
  try {
18858
- const require2 = createRequire4(import.meta.url);
20062
+ const require2 = createRequire5(import.meta.url);
18859
20063
  const sqlite = require2("node:sqlite");
18860
20064
  db = new sqlite.DatabaseSync(dbPath, { readOnly: true });
18861
20065
  const rows = db.prepare("SELECT id, title, time_updated FROM session ORDER BY time_updated DESC LIMIT 5").all();
@@ -18870,22 +20074,22 @@ function listRecentOpenCodeSessions() {
18870
20074
  }
18871
20075
  function getXdgDataHome() {
18872
20076
  const xdgDataHome = process.env.XDG_DATA_HOME;
18873
- return xdgDataHome && xdgDataHome.length > 0 ? xdgDataHome : join21(homedir17(), ".local", "share");
20077
+ return xdgDataHome && xdgDataHome.length > 0 ? xdgDataHome : join22(homedir18(), ".local", "share");
18874
20078
  }
18875
20079
  function listRecentPiSessions() {
18876
- return listPiSessionsFromDir(join21(getHomeDir(), ".pi", "agent", "sessions"));
20080
+ return listPiSessionsFromDir(join22(getHomeDir(), ".pi", "agent", "sessions"));
18877
20081
  }
18878
20082
  function getHomeDir() {
18879
20083
  const envHome = process.platform === "win32" ? process.env.USERPROFILE : process.env.HOME;
18880
- return envHome && envHome.length > 0 ? envHome : homedir17();
20084
+ return envHome && envHome.length > 0 ? envHome : homedir18();
18881
20085
  }
18882
20086
  function listPiSessionsFromDir(sessionsDir) {
18883
20087
  try {
18884
- if (!existsSync20(sessionsDir))
20088
+ if (!existsSync21(sessionsDir))
18885
20089
  return [];
18886
20090
  const files = collectJsonlFiles(sessionsDir).map((filePath) => {
18887
20091
  try {
18888
- const stats = statSync12(filePath);
20092
+ const stats = statSync13(filePath);
18889
20093
  return { filePath, mtimeMs: stats.mtimeMs };
18890
20094
  } catch {
18891
20095
  return null;
@@ -18914,12 +20118,12 @@ function collectJsonlFiles(root) {
18914
20118
  continue;
18915
20119
  let entries;
18916
20120
  try {
18917
- entries = readdirSync8(dir, { withFileTypes: true });
20121
+ entries = readdirSync9(dir, { withFileTypes: true });
18918
20122
  } catch {
18919
20123
  continue;
18920
20124
  }
18921
20125
  for (const entry of entries) {
18922
- const path2 = join21(dir, entry.name);
20126
+ const path2 = join22(dir, entry.name);
18923
20127
  if (entry.isDirectory()) {
18924
20128
  stack.push(path2);
18925
20129
  } else if (entry.isFile() && entry.name.endsWith(".jsonl")) {
@@ -19019,17 +20223,17 @@ __export(exports_doctor, {
19019
20223
  import { execFileSync as execFileSync3 } from "node:child_process";
19020
20224
  import {
19021
20225
  chmodSync as chmodSync4,
19022
- existsSync as existsSync21,
20226
+ existsSync as existsSync22,
19023
20227
  mkdirSync as mkdirSync7,
19024
20228
  mkdtempSync,
19025
20229
  readFileSync as readFileSync11,
19026
20230
  realpathSync as realpathSync5,
19027
20231
  rmSync as rmSync7,
19028
- statSync as statSync13,
20232
+ statSync as statSync14,
19029
20233
  writeFileSync as writeFileSync5
19030
20234
  } from "node:fs";
19031
- import { tmpdir as tmpdir4 } from "node:os";
19032
- import { join as join22 } from "node:path";
20235
+ import { tmpdir as tmpdir2 } from "node:os";
20236
+ import { join as join23 } from "node:path";
19033
20237
  async function runDoctor(options) {
19034
20238
  if (options.issue) {
19035
20239
  return runIssueFlow(options.argv);
@@ -19098,6 +20302,8 @@ async function runDoctor(options) {
19098
20302
  }
19099
20303
  if (h2.onnxRuntime.systemPath) {
19100
20304
  parts.push(`system: ${h2.onnxRuntime.systemVersion ?? "unknown"}${h2.onnxRuntime.systemCompatible === false ? " (incompatible)" : ""}`);
20305
+ } else if (h2.onnxRuntime.ignoredSystemPath) {
20306
+ parts.push(`system: ${h2.onnxRuntime.ignoredSystemReason} at ${h2.onnxRuntime.ignoredSystemPath}`);
19101
20307
  }
19102
20308
  if (!h2.onnxRuntime.cachedPath && !h2.onnxRuntime.systemPath) {
19103
20309
  parts.push(`not installed — ${h2.onnxRuntime.installHint}`);
@@ -19194,7 +20400,7 @@ function clearOldBinaries() {
19194
20400
  errors: [],
19195
20401
  keptVersion: keepTag
19196
20402
  };
19197
- if (!existsSync21(info.path)) {
20403
+ if (!existsSync22(info.path)) {
19198
20404
  log2.info(`Binary cache: nothing to clear at ${info.path}`);
19199
20405
  return result;
19200
20406
  }
@@ -19204,10 +20410,10 @@ function clearOldBinaries() {
19204
20410
  return result;
19205
20411
  }
19206
20412
  for (const version of stale) {
19207
- const dir = join22(info.path, version);
20413
+ const dir = join23(info.path, version);
19208
20414
  let bytes = 0;
19209
20415
  try {
19210
- bytes = statSync13(dir).isDirectory() ? dirSize(dir) : 0;
20416
+ bytes = statSync14(dir).isDirectory() ? dirSize(dir) : 0;
19211
20417
  } catch {
19212
20418
  bytes = 0;
19213
20419
  }
@@ -19381,12 +20587,12 @@ function buildDoctorFixPlan(adapters, report) {
19381
20587
  if (candidate.storageOnnxBytes > 0) {
19382
20588
  items.push({
19383
20589
  kind: "onnx",
19384
- message: `Will delete AFT-managed ONNX cache at ${candidate.storageOnnxDir} (${formatBytes(candidate.storageOnnxBytes)})`
20590
+ message: `Will replace AFT-managed ONNX cache at ${candidate.storageOnnxDir} (${formatBytes(candidate.storageOnnxBytes)}) and download a compatible runtime`
19385
20591
  });
19386
20592
  } else {
19387
20593
  items.push({
19388
20594
  kind: "onnx",
19389
- message: `Will leave system ONNX untouched and refresh AFT-managed ONNX state for ${candidate.harness.displayName} on next start`
20595
+ message: `Will leave system ONNX untouched and download a compatible AFT-managed runtime for ${candidate.harness.displayName}`
19390
20596
  });
19391
20597
  }
19392
20598
  }
@@ -19468,7 +20674,7 @@ async function runFixFlow(argv) {
19468
20674
  } else {
19469
20675
  log2.info("AFT binary not found. Downloading…");
19470
20676
  try {
19471
- const { ensureBinary: ensureBinary2 } = await Promise.resolve().then(() => (init_dist(), exports_dist));
20677
+ const { ensureBinary: ensureBinary2 } = await Promise.resolve().then(() => (init_dist2(), exports_dist));
19472
20678
  const path2 = await ensureBinary2(`v${report.cliVersion}`);
19473
20679
  if (path2) {
19474
20680
  log2.success(`AFT binary installed at ${path2}`);
@@ -19518,7 +20724,8 @@ function logDoctorIssues(report) {
19518
20724
  }
19519
20725
  function formatDoctorStorageStatus(h2) {
19520
20726
  const state = h2.storageDir.exists ? h2.storageDir.path : `${h2.storageDir.path} (${h2.pluginRegistered ? "not yet created (lazy — created on first tool call)" : "not created"})`;
19521
- return `${state} (${formatStorageSizes(h2.storageDir.sizesByKey)})`;
20727
+ const legacyDuplication = formatLegacyDuplication(h2.storageDir.legacyDuplication);
20728
+ return `${state} (${[formatStorageSizes(h2.storageDir.sizesByKey), legacyDuplication].filter((part) => Boolean(part)).join("; ")})`;
19522
20729
  }
19523
20730
  async function confirmBinaryDownloadDespitePluginSkew(report, argv) {
19524
20731
  const skews = findPluginCliVersionSkews(report);
@@ -19548,7 +20755,7 @@ function ensureStorageDirsForRegisteredPlugins(adapters) {
19548
20755
  if (!adapter.isInstalled() || !adapter.hasPluginEntry())
19549
20756
  continue;
19550
20757
  const storageDir = adapter.getStorageDir();
19551
- if (existsSync21(storageDir))
20758
+ if (existsSync22(storageDir))
19552
20759
  continue;
19553
20760
  mkdirSync7(storageDir, { recursive: true });
19554
20761
  summary.created += 1;
@@ -19630,6 +20837,12 @@ function formatStorageSizes(sizes) {
19630
20837
  const parts = Object.entries(sizes).filter(([, size]) => size > 0).map(([key, size]) => `${key}: ${formatBytes(size)}`);
19631
20838
  return parts.length > 0 ? parts.join(", ") : "empty";
19632
20839
  }
20840
+ function formatLegacyDuplication(summary) {
20841
+ if (!summary || summary.totalPartitions === 0)
20842
+ return null;
20843
+ const byHarness = summary.byHarness.map((entry) => `${entry.harness}: ${entry.partitions} partition(s) / ${formatBytes(entry.bytes)}`).join("; ");
20844
+ return `legacy duplication: ${summary.totalPartitions} partition(s), ${formatBytes(summary.totalBytes)} total [${byHarness}]`;
20845
+ }
19633
20846
  function isInteractiveTerminal() {
19634
20847
  return process.stdin.isTTY === true && process.stdout.isTTY === true;
19635
20848
  }
@@ -19661,11 +20874,11 @@ function deriveIssueTitleFromBody(body) {
19661
20874
  function writeIssueReviewFile(body) {
19662
20875
  let reviewDir = null;
19663
20876
  try {
19664
- reviewDir = mkdtempSync(join22(tmpdir4(), "aft-issue-"));
20877
+ reviewDir = mkdtempSync(join23(tmpdir2(), "aft-issue-"));
19665
20878
  if (process.platform !== "win32") {
19666
20879
  chmodSync4(reviewDir, 448);
19667
20880
  }
19668
- const outPath = join22(reviewDir, "issue.md");
20881
+ const outPath = join23(reviewDir, "issue.md");
19669
20882
  writeFileSync5(outPath, `${body}
19670
20883
  `, { encoding: "utf8", mode: 384, flag: "wx" });
19671
20884
  return { path: outPath, realPath: realpathSync5(outPath) };
@@ -19826,7 +21039,7 @@ https://github.com/cortexkit/aft/issues/new and paste the contents of ${outPath}
19826
21039
  }
19827
21040
  var DOCTOR_CLEAR_TARGET_OPTIONS, DOCTOR_FORCE_CLEAR_TARGETS;
19828
21041
  var init_doctor = __esm(async () => {
19829
- init_dist();
21042
+ init_dist2();
19830
21043
  init_binary_cache();
19831
21044
  init_bridge_tool_failures();
19832
21045
  init_fs_util();