@cortexkit/aft-opencode 0.58.0 → 0.58.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -7878,6 +7878,9 @@ function isTerminalStatus(status) {
7878
7878
  function sleep(ms) {
7879
7879
  return new Promise((resolve) => setTimeout(resolve, ms));
7880
7880
  }
7881
+ function monotonicNowMs() {
7882
+ return performance.now();
7883
+ }
7881
7884
  // ../aft-bridge/dist/bash-hints.js
7882
7885
  import * as fs from "node:fs";
7883
7886
  import * as os from "node:os";
@@ -7888,6 +7891,10 @@ function resolveWatchTimeoutMs(requestedMs, role, capMs) {
7888
7891
  const fallback = role === "worker" ? capMs : DEFAULT_PRIMARY_WATCH_TIMEOUT_MS;
7889
7892
  return Math.min(requestedMs ?? fallback, capMs);
7890
7893
  }
7894
+ function formatWatchWaited(elapsedMs, limitMs, capMs) {
7895
+ const limit = limitMs >= capMs ? `limit ${limitMs}ms, the bash.watch_sync_max_ms cap` : `limit ${limitMs}ms`;
7896
+ return `Waited ${Math.round(elapsedMs)}ms (${limit})`;
7897
+ }
7891
7898
  function watchTimeoutSteer(role, capMs, timeoutParam = "timeoutMs") {
7892
7899
  if (role === "worker") {
7893
7900
  return `The command is still running; this is not a failure. Watch again (${timeoutParam} up to ${capMs}) and don't report a result until it finishes.`;
@@ -8274,7 +8281,7 @@ import { existsSync } from "node:fs";
8274
8281
  import { join as join3 } from "node:path";
8275
8282
 
8276
8283
  // ../aft-bridge/dist/child-process.js
8277
- import { execFileSync as nodeExecFileSync, execSync as nodeExecSync, spawn as nodeSpawn, spawnSync as nodeSpawnSync } from "node:child_process";
8284
+ import { execFile as nodeExecFile, execFileSync as nodeExecFileSync, execSync as nodeExecSync, spawn as nodeSpawn, spawnSync as nodeSpawnSync } from "node:child_process";
8278
8285
  var HIDDEN = { windowsHide: true };
8279
8286
  function withWindowsHidden(args, takesArgv) {
8280
8287
  const out = [...args];
@@ -8296,6 +8303,7 @@ function hidden(resolve, takesArgv) {
8296
8303
  }
8297
8304
  var spawn = hidden(() => nodeSpawn, true);
8298
8305
  var spawnSync = hidden(() => nodeSpawnSync, true);
8306
+ var execFile = hidden(() => nodeExecFile, true);
8299
8307
  var execFileSync = hidden(() => nodeExecFileSync, true);
8300
8308
  var execSync = hidden(() => nodeExecSync, false);
8301
8309
 
@@ -9622,17 +9630,30 @@ class BinaryBridge {
9622
9630
  }
9623
9631
  }
9624
9632
  onStderrData(data) {
9625
- this.stderrBuffer += data;
9626
- let newlineIdx;
9627
- while ((newlineIdx = this.stderrBuffer.indexOf(`
9628
- `)) !== -1) {
9629
- const line = this.stderrBuffer.slice(0, newlineIdx).replace(/\r$/, "");
9630
- this.stderrBuffer = this.stderrBuffer.slice(newlineIdx + 1);
9631
- if (!line || !shouldSurfaceStderrLine(line))
9632
- continue;
9633
- const tagged = tagStderrLine(line);
9634
- this.logVia(tagged);
9635
- this.pushStderrLine(tagged);
9633
+ const partialLimit = 64 * 1024;
9634
+ let offset = 0;
9635
+ while (offset < data.length) {
9636
+ const newline = data.indexOf(`
9637
+ `, offset);
9638
+ const end = newline < 0 ? data.length : newline;
9639
+ while (offset < end) {
9640
+ const count = Math.min(partialLimit - this.stderrBuffer.length, end - offset);
9641
+ this.stderrBuffer += data.slice(offset, offset + count);
9642
+ offset += count;
9643
+ if (this.stderrBuffer.length === partialLimit) {
9644
+ const last = this.stderrBuffer.charCodeAt(this.stderrBuffer.length - 1);
9645
+ const carry = last >= 55296 && last <= 56319 ? this.stderrBuffer.slice(-1) : "";
9646
+ const fragment = carry ? this.stderrBuffer.slice(0, -1) : this.stderrBuffer;
9647
+ const tagged = tagStderrLine(`${fragment} [stderr record truncated into bounded fragments; ${Buffer.byteLength(fragment)} bytes shown, continuation follows]`);
9648
+ this.stderrBuffer = carry;
9649
+ this.logVia(tagged);
9650
+ this.pushStderrLine(tagged);
9651
+ }
9652
+ }
9653
+ if (newline < 0)
9654
+ break;
9655
+ this.flushStderrBuffer();
9656
+ offset = newline + 1;
9636
9657
  }
9637
9658
  }
9638
9659
  flushStderrBuffer() {
@@ -10121,9 +10142,366 @@ function formatDroppedKeyWarnings(dropped) {
10121
10142
  }
10122
10143
  return dropped.map((item) => `Ignoring ${item.key} from ${item.tier} config (${item.reason})`);
10123
10144
  }
10145
+ // ../aft-bridge/dist/config-watch.js
10146
+ import { readFileSync as readFileSync3, statSync as statSync3, watch } from "node:fs";
10147
+ import { basename, dirname } from "node:path";
10148
+ var CONFIG_WATCH_DEBOUNCE_MS = 150;
10149
+ var CONFIG_LIVE_KEEP_NOTE = "AFT keeps using the last valid configuration until the file is fixed.";
10150
+ function pathKey(name) {
10151
+ const segments = name.split(".");
10152
+ return {
10153
+ name,
10154
+ read(config) {
10155
+ let value = config;
10156
+ for (const segment of segments) {
10157
+ if (value === null || typeof value !== "object")
10158
+ return;
10159
+ value = value[segment];
10160
+ }
10161
+ return value;
10162
+ },
10163
+ write(config, value) {
10164
+ const set = (target, index) => {
10165
+ const copy = target !== null && typeof target === "object" && !Array.isArray(target) ? { ...target } : {};
10166
+ const segment = segments[index];
10167
+ if (index === segments.length - 1) {
10168
+ if (value === undefined)
10169
+ delete copy[segment];
10170
+ else
10171
+ copy[segment] = value;
10172
+ } else {
10173
+ copy[segment] = set(copy[segment], index + 1);
10174
+ }
10175
+ return copy;
10176
+ };
10177
+ return set(config, 0);
10178
+ }
10179
+ };
10180
+ }
10181
+ function bashKey(name, resolveBash) {
10182
+ return {
10183
+ name: `bash.${name}`,
10184
+ read: (config) => resolveBash(config)[name],
10185
+ write(config, value) {
10186
+ const resolved = { ...resolveBash(config), [name]: value };
10187
+ for (const key of Object.keys(resolved)) {
10188
+ if (resolved[key] === undefined)
10189
+ delete resolved[key];
10190
+ }
10191
+ return { ...config, bash: resolved };
10192
+ }
10193
+ };
10194
+ }
10195
+ function aftLiveConfigKeys(resolveBash) {
10196
+ return [
10197
+ pathKey("configure_warnings_delivery"),
10198
+ pathKey("restrict_to_project_root"),
10199
+ pathKey("inspect.diagnostics_timeout_ms"),
10200
+ pathKey("inspect.tier2_idle_minutes"),
10201
+ bashKey("foreground_wait_window_ms", resolveBash),
10202
+ bashKey("host_fallback", resolveBash),
10203
+ bashKey("subagent_background", resolveBash),
10204
+ bashKey("watch_sync_max_ms", resolveBash)
10205
+ ];
10206
+ }
10207
+ function sameValue(a, b) {
10208
+ return JSON.stringify(a) === JSON.stringify(b);
10209
+ }
10210
+ function flattenLeaves(value, prefix, out) {
10211
+ if (value !== null && typeof value === "object" && !Array.isArray(value)) {
10212
+ const entries = Object.entries(value);
10213
+ if (entries.length === 0 && prefix)
10214
+ out.set(prefix, "{}");
10215
+ for (const [key, child] of entries) {
10216
+ flattenLeaves(child, prefix ? `${prefix}.${key}` : key, out);
10217
+ }
10218
+ return;
10219
+ }
10220
+ if (prefix)
10221
+ out.set(prefix, JSON.stringify(value));
10222
+ }
10223
+ function applyLiveConfigKeys(current, next, keys, baseline = current) {
10224
+ let config = current;
10225
+ const applied = [];
10226
+ for (const key of keys) {
10227
+ const value = key.read(next);
10228
+ if (sameValue(key.read(current), value))
10229
+ continue;
10230
+ config = key.write(config, value);
10231
+ applied.push(key.name);
10232
+ }
10233
+ const before = new Map;
10234
+ const after = new Map;
10235
+ flattenLeaves(baseline, "", before);
10236
+ flattenLeaves(next, "", after);
10237
+ const liveNames = keys.map((key) => key.name);
10238
+ const isLive = (leaf) => liveNames.some((name) => leaf === name || leaf.startsWith(`${name}.`));
10239
+ const deferred = new Set;
10240
+ for (const leaf of new Set([...before.keys(), ...after.keys()])) {
10241
+ if (before.get(leaf) === after.get(leaf) || isLive(leaf))
10242
+ continue;
10243
+ deferred.add(leaf);
10244
+ }
10245
+ return { config, applied, deferred: [...deferred].sort() };
10246
+ }
10247
+ function readTextOrNull(path) {
10248
+ try {
10249
+ return readFileSync3(path, "utf8");
10250
+ } catch {
10251
+ return null;
10252
+ }
10253
+ }
10254
+ var CONFIG_WATCH_REVALIDATE_MS = 2000;
10255
+ var CONFIG_WATCH_REJECTED_RETRIES = 3;
10256
+ var CONFIG_WATCH_MAX_DELAY_MS = 1000;
10257
+ function identityOf(dir) {
10258
+ try {
10259
+ const stat = statSync3(dir, { bigint: true });
10260
+ const base = `${stat.dev}:${stat.ino}`;
10261
+ return process.platform === "win32" ? base : `${base}:${stat.birthtimeNs}`;
10262
+ } catch {
10263
+ return null;
10264
+ }
10265
+ }
10266
+ function watchDirFor(file) {
10267
+ let dir = dirname(file);
10268
+ while (dir !== dirname(dir) && identityOf(dir) === null)
10269
+ dir = dirname(dir);
10270
+ return dir;
10271
+ }
10272
+ function watchAftConfigFiles(options) {
10273
+ const debounceMs = options.debounceMs ?? CONFIG_WATCH_DEBOUNCE_MS;
10274
+ const lastSeen = new Map;
10275
+ for (const path of options.paths) {
10276
+ if (options.initialTexts)
10277
+ lastSeen.set(path, options.initialTexts[path] ?? null);
10278
+ else
10279
+ lastSeen.set(path, options.checkAtStart ? undefined : readTextOrNull(path));
10280
+ }
10281
+ let timer = null;
10282
+ let firstPendingAt = null;
10283
+ let stopped = false;
10284
+ const watchers = new Map;
10285
+ const watchImpl = options.watchImpl ?? watch;
10286
+ let rejectedRetries = 0;
10287
+ const check = () => {
10288
+ timer = null;
10289
+ firstPendingAt = null;
10290
+ if (stopped)
10291
+ return;
10292
+ revalidate();
10293
+ const seen = new Map;
10294
+ for (const path of options.paths) {
10295
+ const text = readTextOrNull(path);
10296
+ if (text !== lastSeen.get(path))
10297
+ seen.set(path, text);
10298
+ }
10299
+ if (seen.size === 0)
10300
+ return;
10301
+ const outcome = options.onChange();
10302
+ if (outcome === false) {
10303
+ if (rejectedRetries < CONFIG_WATCH_REJECTED_RETRIES) {
10304
+ rejectedRetries += 1;
10305
+ retryTimer = setTimeout(check, CONFIG_WATCH_MAX_DELAY_MS);
10306
+ retryTimer.unref?.();
10307
+ }
10308
+ return;
10309
+ }
10310
+ rejectedRetries = 0;
10311
+ if (outcome instanceof Map) {
10312
+ for (const path of options.paths)
10313
+ lastSeen.set(path, outcome.get(path) ?? null);
10314
+ } else {
10315
+ for (const [path, text] of seen)
10316
+ lastSeen.set(path, text);
10317
+ }
10318
+ };
10319
+ let retryTimer = null;
10320
+ const schedule = () => {
10321
+ if (stopped)
10322
+ return;
10323
+ const now = Date.now();
10324
+ firstPendingAt ??= now;
10325
+ if (timer)
10326
+ clearTimeout(timer);
10327
+ const delay = Math.max(0, Math.min(debounceMs, firstPendingAt + CONFIG_WATCH_MAX_DELAY_MS - now));
10328
+ timer = setTimeout(check, delay);
10329
+ timer.unref?.();
10330
+ };
10331
+ function revalidate() {
10332
+ const wanted = new Set(options.paths.map(watchDirFor));
10333
+ let moved = false;
10334
+ for (const [dir, entry] of watchers) {
10335
+ if (!wanted.has(dir) || entry.stale || identityOf(dir) !== entry.identity) {
10336
+ entry.watcher.close();
10337
+ watchers.delete(dir);
10338
+ moved = true;
10339
+ }
10340
+ }
10341
+ for (const dir of wanted) {
10342
+ if (watchers.has(dir))
10343
+ continue;
10344
+ try {
10345
+ const identity = identityOf(dir);
10346
+ options.beforeWatchForTest?.(dir);
10347
+ const name = basename(dir);
10348
+ const watcher = watchImpl(dir, { persistent: false }, (event, filename) => {
10349
+ if (event === "rename" && filename === name) {
10350
+ const current = watchers.get(dir);
10351
+ if (current && current.watcher === watcher)
10352
+ current.stale = true;
10353
+ }
10354
+ schedule();
10355
+ });
10356
+ if (identityOf(dir) !== identity) {
10357
+ watcher.close();
10358
+ moved = true;
10359
+ continue;
10360
+ }
10361
+ const entry = { watcher, identity, stale: false };
10362
+ entry.watcher.on("error", () => {
10363
+ entry.watcher.close();
10364
+ if (watchers.get(dir) === entry)
10365
+ watchers.delete(dir);
10366
+ schedule();
10367
+ });
10368
+ watchers.set(dir, entry);
10369
+ moved = true;
10370
+ } catch {}
10371
+ }
10372
+ return moved;
10373
+ }
10374
+ revalidate();
10375
+ if (options.checkAtStart)
10376
+ check();
10377
+ const revalidateTimer = setInterval(() => {
10378
+ if (!stopped && revalidate())
10379
+ schedule();
10380
+ }, CONFIG_WATCH_REVALIDATE_MS);
10381
+ revalidateTimer.unref?.();
10382
+ return () => {
10383
+ stopped = true;
10384
+ if (timer)
10385
+ clearTimeout(timer);
10386
+ if (retryTimer)
10387
+ clearTimeout(retryTimer);
10388
+ clearInterval(revalidateTimer);
10389
+ for (const entry of watchers.values())
10390
+ entry.watcher.close();
10391
+ watchers.clear();
10392
+ };
10393
+ }
10394
+ function aftLiveSecurityKeys(keys) {
10395
+ const byName = new Map(keys.map((key) => [key.name, key]));
10396
+ const pick = (name) => {
10397
+ const key = byName.get(name);
10398
+ if (!key)
10399
+ throw new Error(`unknown live config key ${name}`);
10400
+ return key;
10401
+ };
10402
+ return [
10403
+ {
10404
+ key: pick("restrict_to_project_root"),
10405
+ loosens: (current, next) => current === true && next !== true
10406
+ },
10407
+ {
10408
+ key: pick("bash.host_fallback"),
10409
+ loosens: (current, next) => current !== true && next === true
10410
+ }
10411
+ ];
10412
+ }
10413
+ function liveConfigReloadLogLine(applied, deferred) {
10414
+ let line = "config reload";
10415
+ if (applied.length > 0)
10416
+ line += ` applied=[${applied.join(",")}]`;
10417
+ if (deferred.length > 0) {
10418
+ line += ` deferred=[${deferred.join(",")}] (deferred keys apply on next connect/restart)`;
10419
+ }
10420
+ return line;
10421
+ }
10422
+ function startLiveConfigReload(options) {
10423
+ const baseline = options.getConfig();
10424
+ let lastError = null;
10425
+ let acceptedSources = new Set(options.initialSources);
10426
+ let acceptedProjectText = options.initialProjectText ?? null;
10427
+ let acceptedTexts;
10428
+ const reload = () => {
10429
+ let loaded = options.load();
10430
+ if (loaded.ok) {
10431
+ const read = new Set(loaded.sources);
10432
+ const deleted = [...acceptedSources].find((path) => !read.has(path));
10433
+ if (deleted !== undefined) {
10434
+ loaded = { ok: false, message: `AFT config at ${deleted} was deleted` };
10435
+ } else {
10436
+ acceptedSources = read;
10437
+ }
10438
+ }
10439
+ if (!loaded.ok) {
10440
+ const message = `${loaded.message.trim().replace(/[.!?]?$/, ".")} ${CONFIG_LIVE_KEEP_NOTE}`;
10441
+ if (message !== lastError) {
10442
+ lastError = message;
10443
+ options.reportError(message);
10444
+ }
10445
+ return null;
10446
+ }
10447
+ lastError = null;
10448
+ acceptedTexts = loaded.texts;
10449
+ const current = options.getConfig();
10450
+ let next = loaded.config;
10451
+ const held = [];
10452
+ const projectText = loaded.projectText ?? null;
10453
+ if (loaded.projectText !== undefined && projectText !== acceptedProjectText) {
10454
+ for (const security of options.securityKeys ?? []) {
10455
+ const now = security.key.read(current);
10456
+ if (security.loosens(now, security.key.read(next))) {
10457
+ next = security.key.write(next, now);
10458
+ held.push(security.key.name);
10459
+ }
10460
+ }
10461
+ if (held.length === 0)
10462
+ acceptedProjectText = projectText;
10463
+ }
10464
+ const result = applyLiveConfigKeys(current, next, options.keys, baseline);
10465
+ if (result.applied.length > 0)
10466
+ options.setConfig(result.config);
10467
+ if (result.applied.length > 0 || result.deferred.length > 0 || held.length > 0) {
10468
+ let line = liveConfigReloadLogLine(result.applied, result.deferred);
10469
+ if (held.length > 0) {
10470
+ line += ` held=[${held.join(",")}] (not loosened while the project file differs from the one the published values came from; the next restart applies them)`;
10471
+ }
10472
+ options.log(line);
10473
+ }
10474
+ return { ...result, held };
10475
+ };
10476
+ const safeReload = () => {
10477
+ try {
10478
+ acceptedTexts = undefined;
10479
+ if (reload() === null)
10480
+ return false;
10481
+ const texts = acceptedTexts;
10482
+ if (!texts)
10483
+ return true;
10484
+ return new Map(options.paths.map((path) => [path, texts[path] ?? null]));
10485
+ } catch (err) {
10486
+ options.reportError(`AFT config reload failed: ${err instanceof Error ? err.message : String(err)}`);
10487
+ return false;
10488
+ }
10489
+ };
10490
+ if (options.watch === false) {
10491
+ return { reload, stop: () => {} };
10492
+ }
10493
+ const stop = watchAftConfigFiles({
10494
+ paths: options.paths,
10495
+ debounceMs: options.debounceMs,
10496
+ onChange: safeReload,
10497
+ initialTexts: options.initialTexts,
10498
+ checkAtStart: true
10499
+ });
10500
+ return { reload, stop };
10501
+ }
10124
10502
  // ../aft-bridge/dist/downloader.js
10125
10503
  import { createHash as createHash2, randomUUID } from "node:crypto";
10126
- import { chmodSync, closeSync, copyFileSync, createWriteStream, existsSync as existsSync3, mkdirSync, openSync, readdirSync, readFileSync as readFileSync3, renameSync as renameSync2, rmSync, statSync as statSync3, unlinkSync as unlinkSync2, writeSync } from "node:fs";
10504
+ import { chmodSync, closeSync, copyFileSync, createWriteStream, existsSync as existsSync3, mkdirSync, openSync, readdirSync, readFileSync as readFileSync4, renameSync as renameSync2, rmSync, statSync as statSync4, unlinkSync as unlinkSync2, writeSync } from "node:fs";
10127
10505
  import { hostname } from "node:os";
10128
10506
  import { join as join7 } from "node:path";
10129
10507
  import { Readable } from "node:stream";
@@ -10475,7 +10853,7 @@ function isReclaimableDownloadLock(owner, ageMs, staleMs) {
10475
10853
  }
10476
10854
  function reclaimDownloadLock(lockPath, expectedOwner) {
10477
10855
  try {
10478
- if (readFileSync3(lockPath, "utf-8") !== expectedOwner)
10856
+ if (readFileSync4(lockPath, "utf-8") !== expectedOwner)
10479
10857
  return false;
10480
10858
  rmSync(lockPath, { force: true });
10481
10859
  return true;
@@ -10493,7 +10871,7 @@ function sweepStaleDownloadTemps(versionedCacheDir, binaryName) {
10493
10871
  continue;
10494
10872
  }
10495
10873
  const tempPath = join7(versionedCacheDir, entry.name);
10496
- const ageMs = Date.now() - statSync3(tempPath).mtimeMs;
10874
+ const ageMs = Date.now() - statSync4(tempPath).mtimeMs;
10497
10875
  if (Math.abs(ageMs) > DOWNLOAD_LOCK_STALE_MS)
10498
10876
  unlinkSync2(tempPath);
10499
10877
  }
@@ -10515,7 +10893,7 @@ async function acquireDownloadLock(lockPath, timing = {}) {
10515
10893
  }
10516
10894
  return () => {
10517
10895
  try {
10518
- if (readFileSync3(lockPath, "utf-8") === owner) {
10896
+ if (readFileSync4(lockPath, "utf-8") === owner) {
10519
10897
  rmSync(lockPath, { force: true });
10520
10898
  }
10521
10899
  } catch {}
@@ -10527,8 +10905,8 @@ async function acquireDownloadLock(lockPath, timing = {}) {
10527
10905
  let existingOwner;
10528
10906
  let ageMs;
10529
10907
  try {
10530
- existingOwner = readFileSync3(lockPath, "utf-8");
10531
- ageMs = Date.now() - statSync3(lockPath).mtimeMs;
10908
+ existingOwner = readFileSync4(lockPath, "utf-8");
10909
+ ageMs = Date.now() - statSync4(lockPath).mtimeMs;
10532
10910
  } catch (readErr) {
10533
10911
  if (readErr.code === "ENOENT")
10534
10912
  continue;
@@ -10578,7 +10956,7 @@ async function fetchLatestTag() {
10578
10956
  }
10579
10957
  // ../aft-bridge/dist/durable-log.js
10580
10958
  import { appendFile, chmod, mkdir, rename, rm, stat } from "node:fs/promises";
10581
- import { dirname } from "node:path";
10959
+ import { dirname as dirname2 } from "node:path";
10582
10960
  var DEFAULT_LOG_BYTES = 32 * 1024 * 1024;
10583
10961
  var DEFAULT_LOG_GENERATIONS = 1;
10584
10962
  var LOG_DIR_MODE = 448;
@@ -10591,6 +10969,10 @@ class RotatingLogSink {
10591
10969
  queue = Promise.resolve();
10592
10970
  disabled = false;
10593
10971
  failureReported = false;
10972
+ queuedBytes = 0;
10973
+ queuedRecords = 0;
10974
+ droppedBytes = 0;
10975
+ overflowQueued = false;
10594
10976
  constructor(path, options = {}) {
10595
10977
  this.path = path;
10596
10978
  this.maxBytes = options.maxBytes ?? DEFAULT_LOG_BYTES;
@@ -10599,7 +10981,34 @@ class RotatingLogSink {
10599
10981
  append(data) {
10600
10982
  if (this.disabled || data.length === 0)
10601
10983
  return;
10602
- this.queue = this.queue.then(() => this.write(data)).catch((error) => {
10984
+ const bytes = Buffer.byteLength(data);
10985
+ const overflow = this.queuedBytes + bytes > 1024 * 1024 || this.queuedRecords >= 4096;
10986
+ if (overflow) {
10987
+ this.droppedBytes += bytes;
10988
+ if (this.overflowQueued)
10989
+ return;
10990
+ this.overflowQueued = true;
10991
+ data = "";
10992
+ } else {
10993
+ this.queuedBytes += bytes;
10994
+ this.queuedRecords += 1;
10995
+ }
10996
+ this.queue = this.queue.then(async () => {
10997
+ if (overflow) {
10998
+ const dropped = this.droppedBytes;
10999
+ this.droppedBytes = 0;
11000
+ this.overflowQueued = false;
11001
+ await this.write(`[aft-plugin] durable log queue overflow: dropped ${dropped} bytes (1MiB/4096-record pending limit); reduce diagnostic volume.
11002
+ `);
11003
+ } else {
11004
+ try {
11005
+ await this.write(data);
11006
+ } finally {
11007
+ this.queuedBytes -= bytes;
11008
+ this.queuedRecords -= 1;
11009
+ }
11010
+ }
11011
+ }).catch((error) => {
10603
11012
  this.disabled = true;
10604
11013
  if (!this.failureReported) {
10605
11014
  this.failureReported = true;
@@ -10614,8 +11023,8 @@ class RotatingLogSink {
10614
11023
  await this.queue;
10615
11024
  }
10616
11025
  async write(data) {
10617
- const dir = dirname(this.path);
10618
- await mkdir(dirname(dir), { recursive: true });
11026
+ const dir = dirname2(this.path);
11027
+ await mkdir(dirname2(dir), { recursive: true });
10619
11028
  try {
10620
11029
  await mkdir(dir, { mode: LOG_DIR_MODE });
10621
11030
  } catch (error) {
@@ -12403,7 +12812,7 @@ function reverseRequestMethod(body) {
12403
12812
  }
12404
12813
  }
12405
12814
  // ../aft-bridge/dist/subc-transport.js
12406
- import { existsSync as existsSync4, statSync as statSync4 } from "node:fs";
12815
+ import { existsSync as existsSync4, statSync as statSync5 } from "node:fs";
12407
12816
 
12408
12817
  // ../aft-bridge/dist/lifecycle-registry.js
12409
12818
  import { lstat } from "node:fs/promises";
@@ -12930,6 +13339,7 @@ var RECONNECT_RETRY_FLOOR_MS = 100;
12930
13339
  var RECONNECT_RETRY_CAP_MS = 2000;
12931
13340
  var SUBC_DEFAULT_REQUEST_TIMEOUT_MS = 30000;
12932
13341
  var ROUTE_OPEN_RELOAD_WAIT_CEILING_MS = 45000;
13342
+ var CALL_DEADLINE_EXPIRED = Symbol("call deadline expired");
12933
13343
  function reloadWaitExhaustedSuffix(callDeadlineMs) {
12934
13344
  if (callDeadlineMs > ROUTE_OPEN_RELOAD_WAIT_CEILING_MS) {
12935
13345
  const ceilingSeconds = ROUTE_OPEN_RELOAD_WAIT_CEILING_MS / 1000;
@@ -13282,7 +13692,16 @@ function absentRootError(root) {
13282
13692
  return new SubcError(`invalid route project root: project root does not exist: ${root}`, "config_divergence");
13283
13693
  }
13284
13694
  function reloadWindowExhaustedError(error, callDeadlineMs) {
13285
- const suffix = reloadWaitExhaustedSuffix(callDeadlineMs);
13695
+ return withDeadlineSuffix(error, reloadWaitExhaustedSuffix(callDeadlineMs));
13696
+ }
13697
+ function callDeadlinePassedError(lastError, callDeadlineMs) {
13698
+ const seconds = Math.round(callDeadlineMs / 100) / 10;
13699
+ const suffix = ` The AFT daemon module did not return within this call's ${seconds}s deadline.`;
13700
+ if (lastError !== undefined)
13701
+ return withDeadlineSuffix(lastError, suffix);
13702
+ return new SubcError(`route to module '${AFT_MODULE_ID}' was not ready before the call ran out of time.${suffix}`, REQUEST_DEADLINE_CODE);
13703
+ }
13704
+ function withDeadlineSuffix(error, suffix) {
13286
13705
  if (error instanceof Error) {
13287
13706
  try {
13288
13707
  error.message += suffix;
@@ -13521,7 +13940,7 @@ class SubcTransportPool {
13521
13940
  rootCanAttach(root) {
13522
13941
  let directoryExists = false;
13523
13942
  try {
13524
- directoryExists = statSync4(root).isDirectory();
13943
+ directoryExists = statSync5(root).isDirectory();
13525
13944
  } catch {}
13526
13945
  if (directoryExists) {
13527
13946
  this.dormantRoots.delete(root);
@@ -13861,6 +14280,7 @@ class SubcTransportPool {
13861
14280
  return this.rootReapedError(record);
13862
14281
  }
13863
14282
  async routeRequest(identity, body, timeoutMs, onProgress, expectedGeneration, abortSignal) {
14283
+ const callStartedAt = performance.now();
13864
14284
  const root = asCanonicalRootPath(identity.project_root);
13865
14285
  let generation = expectedGeneration;
13866
14286
  if (this.lifecycleEnabled()) {
@@ -13913,19 +14333,41 @@ class SubcTransportPool {
13913
14333
  };
13914
14334
  const callDeadlineMs = timeoutMs ?? SUBC_DEFAULT_REQUEST_TIMEOUT_MS;
13915
14335
  const reloadWaitBudgetMs = Math.min(callDeadlineMs, ROUTE_OPEN_RELOAD_WAIT_CEILING_MS);
13916
- let reloadWaitedMs = 0;
14336
+ let scheduledDelayMs = 0;
14337
+ let lastRefusal;
14338
+ const spentMs = () => Math.max(performance.now() - callStartedAt, scheduledDelayMs);
14339
+ const remainingMs = () => callDeadlineMs - spentMs();
14340
+ const openRouteWithinDeadline = async () => {
14341
+ const remaining = remainingMs();
14342
+ if (remaining <= 0)
14343
+ throw callDeadlinePassedError(lastRefusal, callDeadlineMs);
14344
+ let timer;
14345
+ const expired = new Promise((resolve) => {
14346
+ timer = setTimeout(() => resolve(CALL_DEADLINE_EXPIRED), remaining);
14347
+ });
14348
+ try {
14349
+ const opened = await Promise.race([openRoute(), expired]);
14350
+ if (opened === CALL_DEADLINE_EXPIRED) {
14351
+ throw callDeadlinePassedError(lastRefusal, callDeadlineMs);
14352
+ }
14353
+ return opened;
14354
+ } finally {
14355
+ clearTimeout(timer);
14356
+ }
14357
+ };
13917
14358
  const openRouteAfterReloadWindow = async () => {
13918
14359
  while (true) {
13919
14360
  try {
13920
- return await openRoute();
14361
+ return await openRouteWithinDeadline();
13921
14362
  } catch (error) {
13922
14363
  if (!isRouteOpenReloadWindowError(error))
13923
14364
  throw error;
13924
- if (reloadWaitedMs + this.nextRouteReopenDelayMs() >= reloadWaitBudgetMs) {
14365
+ lastRefusal = error;
14366
+ if (spentMs() + this.nextRouteReopenDelayMs() >= reloadWaitBudgetMs) {
13925
14367
  throw reloadWindowExhaustedError(error, callDeadlineMs);
13926
14368
  }
13927
14369
  const { delayMs, wait } = this.waitForRouteReopenBackoff();
13928
- reloadWaitedMs += delayMs;
14370
+ scheduledDelayMs = spentMs() + delayMs;
13929
14371
  await wait;
13930
14372
  }
13931
14373
  }
@@ -13961,7 +14403,7 @@ class SubcTransportPool {
13961
14403
  route: routeLabel(entry.handle)
13962
14404
  });
13963
14405
  };
13964
- const requestOnRoute = async (route, entry) => {
14406
+ const requestOnRoute = async (route, entry, requestTimeoutMs) => {
13965
14407
  this.assertRecordLive(record);
13966
14408
  if (abortSignal?.aborted) {
13967
14409
  clearRouteEntry(entry);
@@ -13977,8 +14419,10 @@ class SubcTransportPool {
13977
14419
  };
13978
14420
  abortSignal?.addEventListener("abort", onAbort, { once: true });
13979
14421
  try {
13980
- const requestTimeoutMs = reloadWaitedMs > 0 ? callDeadlineMs - reloadWaitedMs : timeoutMs;
13981
- const request = client.request(route, body, { timeoutMs: requestTimeoutMs, onProgress });
14422
+ const request = client.request(route, body, {
14423
+ timeoutMs: Math.ceil(requestTimeoutMs),
14424
+ onProgress
14425
+ });
13982
14426
  const reply = abortSignal ? await Promise.race([request, aborted]) : await request;
13983
14427
  if (!this.isCurrentSession(key, record)) {
13984
14428
  if (this.isReapInduced(record))
@@ -13999,8 +14443,11 @@ class SubcTransportPool {
13999
14443
  let reopened = false;
14000
14444
  let retriedAbsentRoute = false;
14001
14445
  while (true) {
14446
+ const requestTimeoutMs = remainingMs();
14447
+ if (requestTimeoutMs <= 0)
14448
+ throw callDeadlinePassedError(lastRefusal, callDeadlineMs);
14002
14449
  try {
14003
- const reply = await requestOnRoute(routeAndEntry.route, routeAndEntry.entry);
14450
+ const reply = await requestOnRoute(routeAndEntry.route, routeAndEntry.entry, requestTimeoutMs);
14004
14451
  if (reopened)
14005
14452
  this.resetRouteReopenBackoff();
14006
14453
  return reply;
@@ -14010,11 +14457,12 @@ class SubcTransportPool {
14010
14457
  const ownsRoute = this.isCurrentSession(key, record) && this.client === client;
14011
14458
  if (ownsRoute && isRouteRequestReloadRefusal(error)) {
14012
14459
  clearRouteEntry(routeAndEntry.entry);
14013
- if (reloadWaitedMs + this.nextRouteReopenDelayMs() >= reloadWaitBudgetMs) {
14460
+ lastRefusal = error;
14461
+ if (spentMs() + this.nextRouteReopenDelayMs() >= reloadWaitBudgetMs) {
14014
14462
  throw reloadWindowExhaustedError(error, callDeadlineMs);
14015
14463
  }
14016
14464
  const { delayMs, wait } = this.waitForRouteReopenBackoff();
14017
- reloadWaitedMs += delayMs;
14465
+ scheduledDelayMs = spentMs() + delayMs;
14018
14466
  await wait;
14019
14467
  routeAndEntry = await openRouteAfterReloadWindow();
14020
14468
  reopened = true;
@@ -14023,7 +14471,12 @@ class SubcTransportPool {
14023
14471
  if (ownsRoute && !retriedAbsentRoute && isRouteProvenAbsentError(error)) {
14024
14472
  retriedAbsentRoute = true;
14025
14473
  clearRouteEntry(routeAndEntry.entry);
14026
- await this.waitForRouteReopenBackoff().wait;
14474
+ if (spentMs() + this.nextRouteReopenDelayMs() >= callDeadlineMs) {
14475
+ throw callDeadlinePassedError(error, callDeadlineMs);
14476
+ }
14477
+ const { delayMs, wait } = this.waitForRouteReopenBackoff();
14478
+ scheduledDelayMs = spentMs() + delayMs;
14479
+ await wait;
14027
14480
  routeAndEntry = await openRouteAfterReloadWindow();
14028
14481
  reopened = true;
14029
14482
  continue;
@@ -15846,14 +16299,14 @@ function shouldInterruptWaitsForMessage(detachOnUserMessage, operatorText) {
15846
16299
  return detachOnUserMessage || containsStandaloneDetachKeyword(operatorText);
15847
16300
  }
15848
16301
  // ../aft-bridge/dist/migration.js
15849
- import { closeSync as closeSync3, existsSync as existsSync8, mkdirSync as mkdirSync3, openSync as openSync3, readFileSync as readFileSync6, renameSync as renameSync4, rmSync as rmSync2, statSync as statSync5, unlinkSync as unlinkSync3, writeFileSync as writeFileSync3 } from "node:fs";
16302
+ import { closeSync as closeSync3, existsSync as existsSync8, mkdirSync as mkdirSync3, openSync as openSync3, readFileSync as readFileSync7, renameSync as renameSync4, rmSync as rmSync2, statSync as statSync6, unlinkSync as unlinkSync3, writeFileSync as writeFileSync3 } from "node:fs";
15850
16303
  import { homedir as homedir8, tmpdir as tmpdir2 } from "node:os";
15851
- import { basename, dirname as dirname4, join as join10, resolve as resolve6 } from "node:path";
16304
+ import { basename as basename2, dirname as dirname5, join as join10, resolve as resolve6 } from "node:path";
15852
16305
 
15853
16306
  // ../aft-bridge/dist/paths.js
15854
- import { existsSync as existsSync6, mkdirSync as mkdirSync2, readFileSync as readFileSync4, renameSync as renameSync3, writeFileSync as writeFileSync2 } from "node:fs";
16307
+ import { existsSync as existsSync6, mkdirSync as mkdirSync2, readFileSync as readFileSync5, renameSync as renameSync3, writeFileSync as writeFileSync2 } from "node:fs";
15855
16308
  import { homedir as homedir6 } from "node:os";
15856
- import { dirname as dirname2, isAbsolute as isAbsolute3, join as join8, resolve as resolve5 } from "node:path";
16309
+ import { dirname as dirname3, isAbsolute as isAbsolute3, join as join8, resolve as resolve5 } from "node:path";
15857
16310
  function homeDir3() {
15858
16311
  if (process.platform === "win32")
15859
16312
  return process.env.USERPROFILE || process.env.HOME || homedir6();
@@ -15913,7 +16366,7 @@ function repairRootScopedStorageFile(storageRoot, harness, fileName) {
15913
16366
  if (existsSync6(harnessPath) || !existsSync6(rootPath))
15914
16367
  return harnessPath;
15915
16368
  try {
15916
- mkdirSync2(dirname2(harnessPath), { recursive: true });
16369
+ mkdirSync2(dirname3(harnessPath), { recursive: true });
15917
16370
  renameSync3(rootPath, harnessPath);
15918
16371
  } catch {}
15919
16372
  return harnessPath;
@@ -15925,7 +16378,7 @@ function shouldShowAnnouncement(storageRoot, harness, currentVersion) {
15925
16378
  let lastVersion = "";
15926
16379
  try {
15927
16380
  if (existsSync6(versionFile)) {
15928
- lastVersion = readFileSync4(versionFile, "utf-8").trim();
16381
+ lastVersion = readFileSync5(versionFile, "utf-8").trim();
15929
16382
  }
15930
16383
  } catch {
15931
16384
  return false;
@@ -15934,7 +16387,7 @@ function shouldShowAnnouncement(storageRoot, harness, currentVersion) {
15934
16387
  return false;
15935
16388
  if (!lastVersion) {
15936
16389
  try {
15937
- mkdirSync2(dirname2(versionFile), { recursive: true });
16390
+ mkdirSync2(dirname3(versionFile), { recursive: true });
15938
16391
  writeFileSync2(versionFile, currentVersion);
15939
16392
  } catch {}
15940
16393
  return false;
@@ -15946,7 +16399,7 @@ function markAnnouncementSeen(storageRoot, harness, currentVersion) {
15946
16399
  return;
15947
16400
  const versionFile = repairRootScopedStorageFile(storageRoot, harness, "last_announced_version");
15948
16401
  try {
15949
- mkdirSync2(dirname2(versionFile), { recursive: true });
16402
+ mkdirSync2(dirname3(versionFile), { recursive: true });
15950
16403
  writeFileSync2(versionFile, currentVersion);
15951
16404
  } catch {}
15952
16405
  }
@@ -15995,10 +16448,10 @@ function decodeFileUrl(target) {
15995
16448
  }
15996
16449
 
15997
16450
  // ../aft-bridge/dist/resolver.js
15998
- import { closeSync as closeSync2, existsSync as existsSync7, promises as fsPromises, openSync as openSync2, readFileSync as readFileSync5, readSync } from "node:fs";
16451
+ import { closeSync as closeSync2, existsSync as existsSync7, promises as fsPromises, openSync as openSync2, readFileSync as readFileSync6, readSync } from "node:fs";
15999
16452
  import { createRequire } from "node:module";
16000
16453
  import { homedir as homedir7 } from "node:os";
16001
- import { delimiter, dirname as dirname3, isAbsolute as isAbsolute4, join as join9 } from "node:path";
16454
+ import { delimiter, dirname as dirname4, isAbsolute as isAbsolute4, join as join9 } from "node:path";
16002
16455
  var ensureBinaryForResolver = ensureBinary;
16003
16456
  var npmPlatformPackageReader = null;
16004
16457
  var cacheCopiesInFlight = new Map;
@@ -16151,8 +16604,8 @@ function npmPlatformPackage(ext) {
16151
16604
  try {
16152
16605
  const req = createRequire(import.meta.url);
16153
16606
  const manifestPath = req.resolve(`@cortexkit/aft-${platformKey()}/package.json`);
16154
- const manifest = JSON.parse(readFileSync5(manifestPath, "utf8"));
16155
- const binaryPath = join9(dirname3(manifestPath), "bin", `aft${ext}`);
16607
+ const manifest = JSON.parse(readFileSync6(manifestPath, "utf8"));
16608
+ const binaryPath = join9(dirname4(manifestPath), "bin", `aft${ext}`);
16156
16609
  if (typeof manifest.version !== "string" || !existsSync7(binaryPath))
16157
16610
  return null;
16158
16611
  return { binaryPath, version: normalizeBareVersion(manifest.version) };
@@ -16406,7 +16859,7 @@ function acquireConfigMigrationLock(lockDir) {
16406
16859
  if (code !== "EEXIST")
16407
16860
  throw err;
16408
16861
  try {
16409
- const ageMs = Date.now() - statSync5(lockDir).mtimeMs;
16862
+ const ageMs = Date.now() - statSync6(lockDir).mtimeMs;
16410
16863
  if (ageMs > 60000) {
16411
16864
  rmSync2(lockDir, { recursive: true, force: true });
16412
16865
  continue;
@@ -16420,12 +16873,12 @@ function acquireConfigMigrationLock(lockDir) {
16420
16873
  }
16421
16874
  }
16422
16875
  function atomicCopyConfigFile(sourcePath, targetPath) {
16423
- mkdirSync3(dirname4(targetPath), { recursive: true });
16424
- const tmpPath = join10(dirname4(targetPath), `.${basename(targetPath)}.${process.pid}.${Date.now()}.${Math.random().toString(16).slice(2)}.tmp`);
16876
+ mkdirSync3(dirname5(targetPath), { recursive: true });
16877
+ const tmpPath = join10(dirname5(targetPath), `.${basename2(targetPath)}.${process.pid}.${Date.now()}.${Math.random().toString(16).slice(2)}.tmp`);
16425
16878
  let fd = null;
16426
16879
  try {
16427
16880
  fd = openSync3(tmpPath, "wx", 384);
16428
- writeFileSync3(fd, readFileSync6(sourcePath));
16881
+ writeFileSync3(fd, readFileSync7(sourcePath));
16429
16882
  closeSync3(fd);
16430
16883
  fd = null;
16431
16884
  renameSync4(tmpPath, targetPath);
@@ -16442,8 +16895,8 @@ function atomicCopyConfigFile(sourcePath, targetPath) {
16442
16895
  }
16443
16896
  }
16444
16897
  function atomicWriteConfigFile(targetPath, content) {
16445
- mkdirSync3(dirname4(targetPath), { recursive: true });
16446
- const tmpPath = join10(dirname4(targetPath), `.${basename(targetPath)}.${process.pid}.${Date.now()}.${Math.random().toString(16).slice(2)}.tmp`);
16898
+ mkdirSync3(dirname5(targetPath), { recursive: true });
16899
+ const tmpPath = join10(dirname5(targetPath), `.${basename2(targetPath)}.${process.pid}.${Date.now()}.${Math.random().toString(16).slice(2)}.tmp`);
16447
16900
  let fd = null;
16448
16901
  try {
16449
16902
  fd = openSync3(tmpPath, "wx", 384);
@@ -16504,11 +16957,11 @@ function markLegacySourcesMovedAside(sources, targetPath, logger) {
16504
16957
  const desiredMarkerPath = `${source.path}${MOVED_MARKER_SUFFIX}`;
16505
16958
  const markerPath = nonClobberingSidecarPath(desiredMarkerPath);
16506
16959
  try {
16507
- const original = readFileSync6(source.path, "utf-8");
16960
+ const original = readFileSync7(source.path, "utf-8");
16508
16961
  if (markerPath !== desiredMarkerPath) {
16509
16962
  logger?.warn?.(`Preserving existing legacy AFT marker ${desiredMarkerPath}; writing new marker to ${markerPath}`);
16510
16963
  }
16511
- atomicWriteConfigFile(markerPath, movedMarkerContent(targetPath, basename(source.path), original));
16964
+ atomicWriteConfigFile(markerPath, movedMarkerContent(targetPath, basename2(source.path), original));
16512
16965
  unlinkSync3(source.path);
16513
16966
  info?.(`Moved legacy AFT config ${source.path} aside to ${markerPath}; AFT now reads ${targetPath}`);
16514
16967
  } catch (err) {
@@ -16568,15 +17021,15 @@ function migrateAftConfigFile(opts) {
16568
17021
  if (existingSources.length === 0) {
16569
17022
  return { migrated: false, conflict: false, targetPath: opts.targetPath, warnings };
16570
17023
  }
16571
- mkdirSync3(dirname4(opts.targetPath), { recursive: true });
17024
+ mkdirSync3(dirname5(opts.targetPath), { recursive: true });
16572
17025
  const release = acquireConfigMigrationLock(`${opts.targetPath}.lock`);
16573
17026
  try {
16574
17027
  const sources = existingSources.map((source) => ({
16575
17028
  ...source,
16576
- content: readFileSync6(source.path, "utf-8")
17029
+ content: readFileSync7(source.path, "utf-8")
16577
17030
  }));
16578
17031
  if (existsSync8(opts.targetPath)) {
16579
- const targetContent = readFileSync6(opts.targetPath, "utf-8");
17032
+ const targetContent = readFileSync7(opts.targetPath, "utf-8");
16580
17033
  for (const source of sources) {
16581
17034
  if (fileSemanticsMatch(source.content, targetContent))
16582
17035
  continue;
@@ -16627,11 +17080,11 @@ function spawnErrorLabel(error) {
16627
17080
  function migrationLogPath(newRoot, harness, logger) {
16628
17081
  const desired = join10(newRoot, "logs", "migration", `${harness}-${Date.now()}.jsonl`);
16629
17082
  try {
16630
- mkdirSync3(dirname4(desired), { recursive: true });
17083
+ mkdirSync3(dirname5(desired), { recursive: true });
16631
17084
  return desired;
16632
17085
  } catch (err) {
16633
17086
  const fallback = join10(tmpdir2(), `aft-migration-${harness}-${Date.now()}.jsonl`);
16634
- logger?.warn?.(`Failed to create AFT migration log directory ${dirname4(desired)}: ${err instanceof Error ? err.message : String(err)}. ` + `Using fallback log path ${fallback}.`);
17087
+ logger?.warn?.(`Failed to create AFT migration log directory ${dirname5(desired)}: ${err instanceof Error ? err.message : String(err)}. ` + `Using fallback log path ${fallback}.`);
16635
17088
  return fallback;
16636
17089
  }
16637
17090
  }
@@ -16692,9 +17145,9 @@ async function ensureStorageMigrated(opts) {
16692
17145
  throw new Error(`AFT storage migration failed (${detail}). ` + `Harness: ${opts.harness}. Legacy: ${legacyRoot}. Target: ${newRoot}. ` + `See log: ${logPath}. ` + `Plugin load aborted to prevent legacy/new state divergence.` + (stderrTail ? ` Stderr tail: ${stderrTail}` : "") + (stdoutTail ? ` Stdout tail: ${stdoutTail}` : ""));
16693
17146
  }
16694
17147
  // ../aft-bridge/dist/migration-notices.js
16695
- import { mkdirSync as mkdirSync4, readFileSync as readFileSync7, renameSync as renameSync5, rmdirSync, statSync as statSync6, writeFileSync as writeFileSync4 } from "node:fs";
17148
+ import { mkdirSync as mkdirSync4, readFileSync as readFileSync8, renameSync as renameSync5, rmdirSync, statSync as statSync7, writeFileSync as writeFileSync4 } from "node:fs";
16696
17149
  import { homedir as homedir9 } from "node:os";
16697
- import { dirname as dirname5, join as join11, resolve as resolve7 } from "node:path";
17150
+ import { dirname as dirname6, join as join11, resolve as resolve7 } from "node:path";
16698
17151
  function migrationNoticeStorePath(env = process.env) {
16699
17152
  const stateHome = env.XDG_STATE_HOME && env.XDG_STATE_HOME.length > 0 ? env.XDG_STATE_HOME : join11(homedir9(), ".local", "state");
16700
17153
  return join11(stateHome, "cortexkit", "aft", "migration-notices.json");
@@ -16704,7 +17157,7 @@ function noticeKey(configPath, digest) {
16704
17157
  }
16705
17158
  function readRecords(storePath) {
16706
17159
  try {
16707
- const parsed = JSON.parse(readFileSync7(storePath, "utf8"));
17160
+ const parsed = JSON.parse(readFileSync8(storePath, "utf8"));
16708
17161
  return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
16709
17162
  } catch {
16710
17163
  return {};
@@ -16713,7 +17166,7 @@ function readRecords(storePath) {
16713
17166
  var LOCK_STALE_MS = 1e4;
16714
17167
  function withStoreLock(storePath, action) {
16715
17168
  const lockPath = `${storePath}.lock`;
16716
- mkdirSync4(dirname5(storePath), { recursive: true });
17169
+ mkdirSync4(dirname6(storePath), { recursive: true });
16717
17170
  const deadline = Date.now() + 2000;
16718
17171
  for (;; ) {
16719
17172
  try {
@@ -16723,7 +17176,7 @@ function withStoreLock(storePath, action) {
16723
17176
  if (error.code !== "EEXIST")
16724
17177
  throw error;
16725
17178
  try {
16726
- if (Date.now() - statSync6(lockPath).mtimeMs > LOCK_STALE_MS)
17179
+ if (Date.now() - statSync7(lockPath).mtimeMs > LOCK_STALE_MS)
16727
17180
  rmdirSync(lockPath);
16728
17181
  } catch {}
16729
17182
  if (Date.now() > deadline)
@@ -16764,9 +17217,9 @@ function deliverMigrationNoticeOnce(options) {
16764
17217
  }
16765
17218
  }
16766
17219
  // ../aft-bridge/dist/npm-resolver.js
16767
- import { readdirSync as readdirSync2, statSync as statSync7 } from "node:fs";
17220
+ import { readdirSync as readdirSync2, statSync as statSync8 } from "node:fs";
16768
17221
  import { homedir as homedir10 } from "node:os";
16769
- import { dirname as dirname6, isAbsolute as isAbsolute5, join as join12 } from "node:path";
17222
+ import { dirname as dirname7, isAbsolute as isAbsolute5, join as join12 } from "node:path";
16770
17223
  function defaultDeps() {
16771
17224
  return {
16772
17225
  platform: process.platform,
@@ -16780,7 +17233,7 @@ function npmBinaryName(platform) {
16780
17233
  }
16781
17234
  function isFile(p) {
16782
17235
  try {
16783
- return statSync7(p).isFile();
17236
+ return statSync8(p).isFile();
16784
17237
  } catch {
16785
17238
  return false;
16786
17239
  }
@@ -16801,7 +17254,7 @@ function npmFromPath(deps) {
16801
17254
  return null;
16802
17255
  }
16803
17256
  function npmAdjacentToNode(deps) {
16804
- const dir = dirname6(deps.execPath);
17257
+ const dir = dirname7(deps.execPath);
16805
17258
  return isFile(join12(dir, npmBinaryName(deps.platform))) ? dir : null;
16806
17259
  }
16807
17260
  function highestVersionedNodeBin(installsDir, name) {
@@ -17189,8 +17642,8 @@ function probeOnnxRuntimeLoadable(libPath, arch = process.arch) {
17189
17642
  }
17190
17643
  // ../aft-bridge/dist/onnx-runtime.js
17191
17644
  import { createHash as createHash5, randomBytes as randomBytes2 } from "node:crypto";
17192
- import { chmodSync as chmodSync2, closeSync as closeSync5, copyFileSync as copyFileSync2, createWriteStream as createWriteStream2, existsSync as existsSync11, lstatSync, mkdirSync as mkdirSync5, openSync as openSync5, readdirSync as readdirSync3, readFileSync as readFileSync8, readlinkSync, realpathSync as realpathSync3, renameSync as renameSync6, rmSync as rmSync3, statSync as statSync8, symlinkSync, unlinkSync as unlinkSync4, writeFileSync as writeFileSync5 } from "node:fs";
17193
- import { basename as basename2, dirname as dirname7, isAbsolute as isAbsolute6, join as join14, relative as relative2, resolve as resolve8, win32 } from "node:path";
17645
+ import { chmodSync as chmodSync2, closeSync as closeSync5, copyFileSync as copyFileSync2, createWriteStream as createWriteStream2, existsSync as existsSync11, lstatSync, mkdirSync as mkdirSync5, openSync as openSync5, readdirSync as readdirSync3, readFileSync as readFileSync9, readlinkSync, realpathSync as realpathSync3, renameSync as renameSync6, rmSync as rmSync3, statSync as statSync9, symlinkSync, unlinkSync as unlinkSync4, writeFileSync as writeFileSync5 } from "node:fs";
17646
+ import { basename as basename3, dirname as dirname8, isAbsolute as isAbsolute6, join as join14, relative as relative2, resolve as resolve8, win32 } from "node:path";
17194
17647
  import { Readable as Readable2 } from "node:stream";
17195
17648
  import { pipeline as pipeline2 } from "node:stream/promises";
17196
17649
 
@@ -17397,7 +17850,7 @@ function cleanupAbandonedStagingDirs(onnxBaseDir) {
17397
17850
  abandoned = true;
17398
17851
  } else {
17399
17852
  try {
17400
- const ageMs = Date.now() - statSync8(stagingDir).mtimeMs;
17853
+ const ageMs = Date.now() - statSync9(stagingDir).mtimeMs;
17401
17854
  abandoned = ageMs > STALE_LOCK_MS;
17402
17855
  } catch {
17403
17856
  abandoned = true;
@@ -17434,7 +17887,7 @@ var REQUIRED_ORT_MAJOR = 1;
17434
17887
  var REQUIRED_ORT_MIN_MINOR = 20;
17435
17888
  var INVALID_ORT_VERSION = "<invalid>";
17436
17889
  function parseOnnxVersionFromPath(value) {
17437
- const name = basename2(value);
17890
+ const name = basename3(value);
17438
17891
  const semverish = name.match(/(?:^|[._-])(\d+\.\d+\.\d+(?:[-+][A-Za-z0-9.-]+)?)(?:\.(?:dylib|dll))?$/);
17439
17892
  if (semverish)
17440
17893
  return semverish[1].split(/[-+]/, 1)[0];
@@ -17639,7 +18092,7 @@ async function downloadFileWithCap(url, destPath) {
17639
18092
  if (Number.isFinite(advertised) && advertised > MAX_DOWNLOAD_BYTES2) {
17640
18093
  throw new Error(`Content-Length ${advertised} exceeds max ${MAX_DOWNLOAD_BYTES2}`);
17641
18094
  }
17642
- mkdirSync5(dirname7(destPath), { recursive: true });
18095
+ mkdirSync5(dirname8(destPath), { recursive: true });
17643
18096
  let bytesWritten = 0;
17644
18097
  const guard = new TransformStream({
17645
18098
  transform(chunk, transformController) {
@@ -17673,7 +18126,7 @@ function validateExtractedTree(stagingRoot) {
17673
18126
  const lst = lstatSync(fullPath);
17674
18127
  if (lst.isSymbolicLink()) {
17675
18128
  const linkTarget = readlinkSync(fullPath);
17676
- const resolvedTarget = resolve8(dirname7(fullPath), linkTarget);
18129
+ const resolvedTarget = resolve8(dirname8(fullPath), linkTarget);
17677
18130
  const rel = relative2(realRoot, resolvedTarget);
17678
18131
  if (rel.startsWith("..") || isAbsolute6(rel) || win32.isAbsolute(rel)) {
17679
18132
  throw new Error(`extracted symlink ${fullPath} points outside staging root: ${linkTarget}`);
@@ -17809,7 +18262,7 @@ function copyOnnxLibraries(info, extractedDir, targetDir, realFiles, symlinks, c
17809
18262
  unlinkSync4(dst);
17810
18263
  } catch {}
17811
18264
  const dstForContainment = join14(targetRoot, link.name);
17812
- const resolvedTarget = resolve8(dirname7(dstForContainment), link.target);
18265
+ const resolvedTarget = resolve8(dirname8(dstForContainment), link.target);
17813
18266
  if (!isPathInsideRoot(targetRoot, resolvedTarget)) {
17814
18267
  const message = `ONNX Runtime symlink ${link.name} points outside install dir: ${link.target}`;
17815
18268
  if (requiredLibs.has(link.name)) {
@@ -17861,9 +18314,9 @@ function writeOnnxInstalledMeta(installDir, version, sha256, archiveSha256) {
17861
18314
  function readOnnxInstalledMeta(installDir) {
17862
18315
  const path = join14(installDir, ONNX_INSTALLED_META_FILE);
17863
18316
  try {
17864
- if (!statSync8(path).isFile())
18317
+ if (!statSync9(path).isFile())
17865
18318
  return null;
17866
- const raw = readFileSync8(path, "utf8");
18319
+ const raw = readFileSync9(path, "utf8");
17867
18320
  const parsed = JSON.parse(raw);
17868
18321
  if (typeof parsed.version !== "string" || parsed.version.length === 0)
17869
18322
  return null;
@@ -17879,7 +18332,7 @@ function readOnnxInstalledMeta(installDir) {
17879
18332
  }
17880
18333
  function sha256File2(path) {
17881
18334
  const hash = createHash5("sha256");
17882
- hash.update(readFileSync8(path));
18335
+ hash.update(readFileSync9(path));
17883
18336
  return hash.digest("hex");
17884
18337
  }
17885
18338
  function acquireLock(lockPath) {
@@ -17907,12 +18360,12 @@ ${new Date().toISOString()}
17907
18360
  let owningPid = null;
17908
18361
  let lockMtimeMs = 0;
17909
18362
  try {
17910
- const raw = readFileSync8(lockPath, "utf8");
18363
+ const raw = readFileSync9(lockPath, "utf8");
17911
18364
  const firstLine = raw.split(/\r?\n/, 1)[0]?.trim() ?? "";
17912
18365
  const parsed = Number.parseInt(firstLine, 10);
17913
18366
  if (Number.isFinite(parsed) && parsed > 0)
17914
18367
  owningPid = parsed;
17915
- lockMtimeMs = statSync8(lockPath).mtimeMs;
18368
+ lockMtimeMs = statSync9(lockPath).mtimeMs;
17916
18369
  } catch {
17917
18370
  return tryClaim();
17918
18371
  }
@@ -17932,7 +18385,7 @@ function releaseLock(lockPath) {
17932
18385
  try {
17933
18386
  let owningPid = null;
17934
18387
  try {
17935
- const raw = readFileSync8(lockPath, "utf8");
18388
+ const raw = readFileSync9(lockPath, "utf8");
17936
18389
  const firstLine = raw.split(/\r?\n/, 1)[0]?.trim() ?? "";
17937
18390
  const parsed = Number.parseInt(firstLine, 10);
17938
18391
  if (Number.isFinite(parsed) && parsed > 0)
@@ -18623,7 +19076,7 @@ function formatSemanticIndexStatus(status, stage, error, backend) {
18623
19076
  return status;
18624
19077
  }
18625
19078
  // src/config.ts
18626
- import { existsSync as existsSync12, readFileSync as readFileSync9, renameSync as renameSync7, unlinkSync as unlinkSync5, writeFileSync as writeFileSync6 } from "node:fs";
19079
+ import { existsSync as existsSync12, readFileSync as readFileSync10, renameSync as renameSync7, unlinkSync as unlinkSync5, writeFileSync as writeFileSync6 } from "node:fs";
18627
19080
  import { homedir as homedir11 } from "node:os";
18628
19081
  import { isAbsolute as isAbsolute7, parse as parsePath, resolve as resolvePath } from "node:path";
18629
19082
  var import_comment_json = __toESM(require_src2(), 1);
@@ -23466,6 +23919,17 @@ var SemanticConfigSchema = object({
23466
23919
  max_input_tokens: number2().int().positive().optional(),
23467
23920
  max_files: number2().int().positive().optional()
23468
23921
  });
23922
+ var RerankConfigSchema = object({
23923
+ backend: _enum(["off", "onnx", "remote", "synapse"]).optional(),
23924
+ model: string2().trim().min(1).optional(),
23925
+ endpoint: string2().trim().min(1).optional(),
23926
+ api_key_env: string2().trim().min(1).optional(),
23927
+ top_n: number2().int().positive().optional(),
23928
+ timeout_ms: number2().int().positive().optional()
23929
+ });
23930
+ var SearchConfigSchema = object({
23931
+ rerank: RerankConfigSchema.optional()
23932
+ });
23469
23933
  var LspExtensionSchema = string2().trim().min(1).refine((value) => value.replace(/^\.+/, "").length > 0, {
23470
23934
  message: "Extension must include characters other than leading dots"
23471
23935
  });
@@ -23611,6 +24075,7 @@ var AftConfigFieldsSchema = object({
23611
24075
  lsp: LspConfigSchema.optional(),
23612
24076
  url_fetch_allow_private: boolean2().optional(),
23613
24077
  semantic: SemanticConfigSchema.optional(),
24078
+ search: SearchConfigSchema.optional(),
23614
24079
  auto_update: boolean2().optional(),
23615
24080
  bridge: BridgeConfigSchema.optional(),
23616
24081
  subc: SubcConfigSchema.optional(),
@@ -23855,7 +24320,7 @@ function migrateAftConfigFile2(configPath, logger = { log: log2, warn: warn2 })
23855
24320
  let tmpPath = null;
23856
24321
  let oldKeys = [];
23857
24322
  try {
23858
- const content = readFileSync9(configPath, "utf-8");
24323
+ const content = readFileSync10(configPath, "utf-8");
23859
24324
  const rawConfig = import_comment_json.parse(content);
23860
24325
  if (!rawConfig || typeof rawConfig !== "object" || Array.isArray(rawConfig)) {
23861
24326
  return { migrated: false, oldKeys: [] };
@@ -23922,6 +24387,18 @@ function parseConfigPartially(rawConfig) {
23922
24387
  return partialConfig;
23923
24388
  }
23924
24389
  var configLoadErrors = [];
24390
+ var configValidationErrors = [];
24391
+ var configLoadSources = [];
24392
+ var configLoadTexts = new Map;
24393
+ function getConfigLoadSources() {
24394
+ return configLoadSources;
24395
+ }
24396
+ function getConfigLoadTexts() {
24397
+ return configLoadTexts;
24398
+ }
24399
+ function getConfigValidationErrors() {
24400
+ return configValidationErrors;
24401
+ }
23925
24402
  function getConfigLoadErrors() {
23926
24403
  return configLoadErrors;
23927
24404
  }
@@ -23954,7 +24431,9 @@ function loadConfigFromPath(configPath, tier) {
23954
24431
  if (!existsSync12(configPath)) {
23955
24432
  return null;
23956
24433
  }
23957
- const content = readFileSync9(configPath, "utf-8");
24434
+ const content = readFileSync10(configPath, "utf-8");
24435
+ configLoadSources.push(configPath);
24436
+ configLoadTexts.set(configPath, content);
23958
24437
  const rawConfig = import_comment_json.parse(content);
23959
24438
  migrateRawConfig(rawConfig, configPath, { log: log2, warn: warn2 });
23960
24439
  cleanConfig = stripJsoncSymbols(rawConfig);
@@ -23999,6 +24478,7 @@ function loadConfigFromPath(configPath, tier) {
23999
24478
  } else {
24000
24479
  const errorMsg = result.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join(", ");
24001
24480
  warn2(`Config validation error in ${configPath}: ${errorMsg}`);
24481
+ configValidationErrors.push({ path: configPath, message: errorMsg });
24002
24482
  parsed = parseConfigPartially(cleanConfig);
24003
24483
  }
24004
24484
  if (!parsed)
@@ -24195,14 +24675,33 @@ function getStrippedTopLevelKeys(override) {
24195
24675
  for (const tool of partitionProjectDisables(override.disabled_tools).ignored) {
24196
24676
  stripped.push(`disabled_tools.${tool}`);
24197
24677
  }
24678
+ stripped.push(...projectRerankStrippedKeys(override.search?.rerank));
24198
24679
  return stripped;
24199
24680
  }
24681
+ function projectRerankStrippedKeys(rerank) {
24682
+ if (rerank === undefined)
24683
+ return [];
24684
+ const stripped = [];
24685
+ if (rerank.backend !== undefined && rerank.backend !== "off")
24686
+ stripped.push("search.rerank.backend");
24687
+ for (const key of ["model", "endpoint", "api_key_env", "top_n", "timeout_ms"]) {
24688
+ if (rerank[key] !== undefined)
24689
+ stripped.push(`search.rerank.${key}`);
24690
+ }
24691
+ return stripped;
24692
+ }
24693
+ function mergeProjectSearchConfig(base, project) {
24694
+ if (project?.rerank?.backend !== "off")
24695
+ return base;
24696
+ return { ...base, rerank: { ...base?.rerank, backend: "off" } };
24697
+ }
24200
24698
  function mergeConfigs(base, override) {
24201
24699
  const disabledTools = unionDisabledTools(base.disabled_tools, partitionProjectDisables(override.disabled_tools).accepted);
24202
24700
  const indexes = mergeIndexes(base.indexes, override.indexes, true);
24203
24701
  const formatter = { ...base.formatter, ...override.formatter };
24204
24702
  const checker = { ...base.checker, ...override.checker };
24205
24703
  const semantic = mergeSemanticConfig(base.semantic, override.semantic);
24704
+ const search = mergeProjectSearchConfig(base.search, override.search);
24206
24705
  const lsp = mergeLspConfig(base.lsp, override.lsp);
24207
24706
  const experimental = mergeExperimentalConfig(base.experimental, override.experimental);
24208
24707
  const bash = mergeBashConfig(base.bash, override.bash);
@@ -24232,6 +24731,7 @@ function mergeConfigs(base, override) {
24232
24731
  ...pi !== undefined ? { pi } : {},
24233
24732
  experimental,
24234
24733
  semantic,
24734
+ search,
24235
24735
  ...bridge !== undefined ? { bridge } : {},
24236
24736
  ...indexes !== undefined ? { indexes } : {},
24237
24737
  ...disabledTools !== undefined ? { disabled_tools: disabledTools } : {}
@@ -24296,6 +24796,9 @@ function buildConfigTierConfigureParams(projectDirectory, processState = {}) {
24296
24796
  }
24297
24797
  function loadAftConfig(projectDirectory) {
24298
24798
  configLoadErrors = [];
24799
+ configValidationErrors = [];
24800
+ configLoadSources = [];
24801
+ configLoadTexts = new Map;
24299
24802
  configLoadNotices = [];
24300
24803
  semanticInputSupplied = false;
24301
24804
  const { userConfigPath, projectConfigPath } = resolveAftConfigPaths(projectDirectory);
@@ -38128,16 +38631,29 @@ function markBridgeEnd() {
38128
38631
  slot.bridgeEnd = performance.now();
38129
38632
  }
38130
38633
  }
38634
+ async function measurePreStage(stage, operation) {
38635
+ const start = performance.now();
38636
+ try {
38637
+ return await operation();
38638
+ } finally {
38639
+ const slot = perfStore.getStore();
38640
+ if (slot) {
38641
+ slot.stages ??= {};
38642
+ slot.stages[stage] = (slot.stages[stage] ?? 0) + performance.now() - start;
38643
+ }
38644
+ }
38645
+ }
38131
38646
  function emit(slot) {
38647
+ const stages = slot.stages ? ` directory=${Math.round(slot.stages.directory ?? 0)}ms permission=${Math.round(slot.stages.permission ?? 0)}ms capability=${Math.round(slot.stages.capability ?? 0)}ms` : "";
38132
38648
  const t3 = performance.now();
38133
38649
  const total = Math.round(t3 - slot.t0);
38134
38650
  if (slot.bridgeStart !== undefined && slot.bridgeEnd !== undefined) {
38135
38651
  const pre = Math.round(slot.bridgeStart - slot.t0);
38136
38652
  const bridge = Math.round(slot.bridgeEnd - slot.bridgeStart);
38137
38653
  const post = Math.round(t3 - slot.bridgeEnd);
38138
- sessionLog(slot.sessionID, `perf tool=${slot.tool} total=${total}ms pre=${pre}ms bridge=${bridge}ms post=${post}ms`);
38654
+ sessionLog(slot.sessionID, `perf tool=${slot.tool} total=${total}ms pre=${pre}ms bridge=${bridge}ms post=${post}ms${stages}`);
38139
38655
  } else {
38140
- sessionLog(slot.sessionID, `perf tool=${slot.tool} total=${total}ms (no bridge call)`);
38656
+ sessionLog(slot.sessionID, `perf tool=${slot.tool} total=${total}ms (no bridge call)${stages}`);
38141
38657
  }
38142
38658
  }
38143
38659
  function instrumentToolMap(tools) {
@@ -38367,10 +38883,10 @@ import {
38367
38883
  createReadStream as createReadStream2,
38368
38884
  existsSync as existsSync15,
38369
38885
  mkdirSync as mkdirSync7,
38370
- readFileSync as readFileSync12,
38886
+ readFileSync as readFileSync13,
38371
38887
  renameSync as renameSync9,
38372
38888
  rmSync as rmSync5,
38373
- statSync as statSync10,
38889
+ statSync as statSync11,
38374
38890
  writeFileSync as writeFileSync8
38375
38891
  } from "node:fs";
38376
38892
  import { join as join17 } from "node:path";
@@ -38381,10 +38897,10 @@ import {
38381
38897
  closeSync as closeSync6,
38382
38898
  mkdirSync as mkdirSync6,
38383
38899
  openSync as openSync6,
38384
- readFileSync as readFileSync10,
38900
+ readFileSync as readFileSync11,
38385
38901
  renameSync as renameSync8,
38386
38902
  rmSync as rmSync4,
38387
- statSync as statSync9,
38903
+ statSync as statSync10,
38388
38904
  unlinkSync as unlinkSync6,
38389
38905
  writeFileSync as writeFileSync7
38390
38906
  } from "node:fs";
@@ -38407,7 +38923,7 @@ function lspBinDir(npmPackage) {
38407
38923
  function isInstalled(npmPackage, binary) {
38408
38924
  for (const candidate of lspBinaryCandidates(binary)) {
38409
38925
  try {
38410
- if (statSync9(join15(lspBinDir(npmPackage), candidate)).isFile())
38926
+ if (statSync10(join15(lspBinDir(npmPackage), candidate)).isFile())
38411
38927
  return true;
38412
38928
  } catch {}
38413
38929
  }
@@ -38435,9 +38951,9 @@ function writeInstalledMetaIn(installDir, version, sha256) {
38435
38951
  function readInstalledMetaIn(installDir) {
38436
38952
  const path = join15(installDir, INSTALLED_META_FILE);
38437
38953
  try {
38438
- if (!statSync9(path).isFile())
38954
+ if (!statSync10(path).isFile())
38439
38955
  return null;
38440
- const raw = readFileSync10(path, "utf8");
38956
+ const raw = readFileSync11(path, "utf8");
38441
38957
  const parsed = JSON.parse(raw);
38442
38958
  if (typeof parsed.version !== "string" || parsed.version.length === 0)
38443
38959
  return null;
@@ -38506,12 +39022,12 @@ ${token}
38506
39022
  let owningPid = null;
38507
39023
  let lockMtimeMs = 0;
38508
39024
  try {
38509
- const raw = readFileSync10(lock, "utf8");
39025
+ const raw = readFileSync11(lock, "utf8");
38510
39026
  const firstLine = raw.split(/\r?\n/, 1)[0]?.trim() ?? "";
38511
39027
  const parsed = Number.parseInt(firstLine, 10);
38512
39028
  if (Number.isFinite(parsed) && parsed > 0)
38513
39029
  owningPid = parsed;
38514
- lockMtimeMs = statSync9(lock).mtimeMs;
39030
+ lockMtimeMs = statSync10(lock).mtimeMs;
38515
39031
  } catch {
38516
39032
  return tryClaim();
38517
39033
  }
@@ -38545,7 +39061,7 @@ function releaseInstallLock(lockKey, acquisitionToken) {
38545
39061
  let owningPid = null;
38546
39062
  let owningToken = "";
38547
39063
  try {
38548
- const raw = readFileSync10(lock, "utf8");
39064
+ const raw = readFileSync11(lock, "utf8");
38549
39065
  const lines = raw.split(/\r?\n/);
38550
39066
  const parsed = Number.parseInt(lines[0]?.trim() ?? "", 10);
38551
39067
  if (Number.isFinite(parsed) && parsed > 0)
@@ -38592,7 +39108,7 @@ function autoInstallStampPath() {
38592
39108
  }
38593
39109
  function hasRecentAutoInstallStamp(path) {
38594
39110
  try {
38595
- const parsed = JSON.parse(readFileSync10(path, "utf8"));
39111
+ const parsed = JSON.parse(readFileSync11(path, "utf8"));
38596
39112
  return typeof parsed.lastRunMs === "number" && Number.isFinite(parsed.lastRunMs) && Math.abs(Date.now() - parsed.lastRunMs) < AUTO_INSTALL_WINDOW_MS;
38597
39113
  } catch {
38598
39114
  return false;
@@ -38600,7 +39116,7 @@ function hasRecentAutoInstallStamp(path) {
38600
39116
  }
38601
39117
  function readAutoInstallLockOwner(path) {
38602
39118
  try {
38603
- const parsed = JSON.parse(readFileSync10(path, "utf8"));
39119
+ const parsed = JSON.parse(readFileSync11(path, "utf8"));
38604
39120
  if (typeof parsed.pid !== "number" || typeof parsed.startedMs !== "number" || typeof parsed.token !== "string") {
38605
39121
  return null;
38606
39122
  }
@@ -38613,7 +39129,7 @@ function autoInstallLockIsStale(path) {
38613
39129
  const owner = readAutoInstallLockOwner(path);
38614
39130
  let age = Number.POSITIVE_INFINITY;
38615
39131
  try {
38616
- age = Math.abs(Date.now() - statSync9(path).mtimeMs);
39132
+ age = Math.abs(Date.now() - statSync10(path).mtimeMs);
38617
39133
  } catch {
38618
39134
  return true;
38619
39135
  }
@@ -38691,7 +39207,7 @@ var VERSION_CHECK_FILE = ".aft-version-check";
38691
39207
  function readVersionCheck(npmPackage) {
38692
39208
  const file = join15(lspPackageDir(npmPackage), VERSION_CHECK_FILE);
38693
39209
  try {
38694
- const raw = readFileSync10(file, "utf8");
39210
+ const raw = readFileSync11(file, "utf8");
38695
39211
  const parsed = JSON.parse(raw);
38696
39212
  if (typeof parsed.last_checked === "string") {
38697
39213
  return {
@@ -38794,6 +39310,13 @@ var NPM_LSP_TABLE = [
38794
39310
  extensions: ["ts", "tsx", "js", "jsx", "mjs", "cjs", "mts", "cts"],
38795
39311
  rootMarkers: ["tsconfig.json", "jsconfig.json", "package.json"]
38796
39312
  },
39313
+ {
39314
+ id: "typescript-sdk",
39315
+ npm: "typescript",
39316
+ binary: "tsserver",
39317
+ extensions: ["ts", "tsx", "js", "jsx", "mjs", "cjs", "mts", "cts"],
39318
+ rootMarkers: ["tsconfig.json", "jsconfig.json", "package.json"]
39319
+ },
38797
39320
  {
38798
39321
  id: "python",
38799
39322
  npm: "pyright",
@@ -38868,7 +39391,7 @@ var NPM_LSP_TABLE = [
38868
39391
  ];
38869
39392
 
38870
39393
  // src/lsp-project-relevance.ts
38871
- import { existsSync as existsSync14, readdirSync as readdirSync4, readFileSync as readFileSync11 } from "node:fs";
39394
+ import { existsSync as existsSync14, readdirSync as readdirSync4, readFileSync as readFileSync12 } from "node:fs";
38872
39395
  import { join as join16 } from "node:path";
38873
39396
  var MAX_WALK_DIRS = 200;
38874
39397
  var MAX_WALK_DEPTH = 4;
@@ -38910,7 +39433,7 @@ function hasPackageJsonDep(projectRoot, depNames) {
38910
39433
  }
38911
39434
  function readPackageJson(projectRoot) {
38912
39435
  try {
38913
- const raw = readFileSync11(join16(projectRoot, "package.json"), "utf8");
39436
+ const raw = readFileSync12(join16(projectRoot, "package.json"), "utf8");
38914
39437
  const parsed = JSON.parse(raw);
38915
39438
  if (typeof parsed !== "object" || parsed === null)
38916
39439
  return null;
@@ -39258,7 +39781,7 @@ function hashInstalledBinary(spec) {
39258
39781
  let pathToHash = null;
39259
39782
  for (const p of candidates) {
39260
39783
  try {
39261
- if (statSync10(p).isFile()) {
39784
+ if (statSync11(p).isFile()) {
39262
39785
  pathToHash = p;
39263
39786
  break;
39264
39787
  }
@@ -39284,14 +39807,14 @@ function installedBinaryPath(spec) {
39284
39807
  ] : [lspBinaryPath(spec.npm, spec.binary)];
39285
39808
  for (const candidate of candidates) {
39286
39809
  try {
39287
- if (statSync10(candidate).isFile())
39810
+ if (statSync11(candidate).isFile())
39288
39811
  return candidate;
39289
39812
  } catch {}
39290
39813
  }
39291
39814
  return null;
39292
39815
  }
39293
39816
  function sha256OfFileSync(path) {
39294
- return createHash7("sha256").update(readFileSync12(path)).digest("hex");
39817
+ return createHash7("sha256").update(readFileSync13(path)).digest("hex");
39295
39818
  }
39296
39819
  function quarantineCachedNpmInstall(spec, reason) {
39297
39820
  const packageDir = lspPackageDir(spec.npm);
@@ -39334,7 +39857,7 @@ function runAutoInstall(projectRoot, config, fetchImpl = fetch) {
39334
39857
  if (isInstalled(spec.npm, spec.binary) && validateCachedNpmInstall(spec)) {
39335
39858
  cachedBinDirs.push(lspBinDir(spec.npm));
39336
39859
  }
39337
- if (config.disabled.has(spec.id)) {
39860
+ if (config.disabled.has(spec.id) || spec.id === "typescript-sdk" && config.disabled.has("typescript")) {
39338
39861
  skipped.push({ id: spec.id, reason: "disabled by config" });
39339
39862
  continue;
39340
39863
  }
@@ -39390,16 +39913,16 @@ import {
39390
39913
  lstatSync as lstatSync2,
39391
39914
  mkdirSync as mkdirSync8,
39392
39915
  readdirSync as readdirSync5,
39393
- readFileSync as readFileSync13,
39916
+ readFileSync as readFileSync14,
39394
39917
  readlinkSync as readlinkSync2,
39395
39918
  realpathSync as realpathSync4,
39396
39919
  renameSync as renameSync10,
39397
39920
  rmSync as rmSync6,
39398
- statSync as statSync11,
39921
+ statSync as statSync12,
39399
39922
  unlinkSync as unlinkSync7,
39400
39923
  writeFileSync as writeFileSync9
39401
39924
  } from "node:fs";
39402
- import { dirname as dirname8, join as join18, relative as relative3, resolve as resolve10 } from "node:path";
39925
+ import { dirname as dirname9, join as join18, relative as relative3, resolve as resolve10 } from "node:path";
39403
39926
  import { Readable as Readable3 } from "node:stream";
39404
39927
  import { pipeline as pipeline3 } from "node:stream/promises";
39405
39928
 
@@ -39508,7 +40031,7 @@ function installFailureMarkerPath(spec) {
39508
40031
  }
39509
40032
  function readInstallFailureMarker(spec) {
39510
40033
  try {
39511
- const marker = JSON.parse(readFileSync13(installFailureMarkerPath(spec), "utf8"));
40034
+ const marker = JSON.parse(readFileSync14(installFailureMarkerPath(spec), "utf8"));
39512
40035
  if (typeof marker.release !== "string" || typeof marker.sha256 !== "string" || typeof marker.error !== "string" || typeof marker.at !== "string" || !Number.isFinite(Date.parse(marker.at))) {
39513
40036
  return null;
39514
40037
  }
@@ -39522,7 +40045,7 @@ function writeInstallFailureMarker(spec, release, sha256, installError) {
39522
40045
  const markerError = installError.replace(/\s+/g, " ").trim();
39523
40046
  const tempPath = `${path}.tmp-${process.pid}-${randomBytes4(6).toString("hex")}`;
39524
40047
  try {
39525
- mkdirSync8(dirname8(path), { recursive: true });
40048
+ mkdirSync8(dirname9(path), { recursive: true });
39526
40049
  writeFileSync9(tempPath, `${JSON.stringify({ release, sha256, error: markerError, at: new Date().toISOString() })}
39527
40050
  `, { encoding: "utf8", mode: 384 });
39528
40051
  renameSync10(tempPath, path);
@@ -39551,7 +40074,7 @@ function ghBinaryPath(spec, platform) {
39551
40074
  function isGithubInstalled(spec, platform) {
39552
40075
  for (const candidate of ghBinaryCandidates(spec, platform)) {
39553
40076
  try {
39554
- if (statSync11(join18(ghBinDir(spec), candidate)).isFile())
40077
+ if (statSync12(join18(ghBinDir(spec), candidate)).isFile())
39555
40078
  return true;
39556
40079
  } catch {}
39557
40080
  }
@@ -39565,9 +40088,9 @@ function ghBinaryCandidates(spec, platform) {
39565
40088
  function readGithubInstalledMetaIn(installDir) {
39566
40089
  try {
39567
40090
  const path = join18(installDir, INSTALLED_META_FILE2);
39568
- if (!statSync11(path).isFile())
40091
+ if (!statSync12(path).isFile())
39569
40092
  return null;
39570
- const parsed = JSON.parse(readFileSync13(path, "utf8"));
40093
+ const parsed = JSON.parse(readFileSync14(path, "utf8"));
39571
40094
  if (typeof parsed.version !== "string" || parsed.version.length === 0)
39572
40095
  return null;
39573
40096
  return {
@@ -39608,7 +40131,7 @@ function sha256OfFile(path) {
39608
40131
  });
39609
40132
  }
39610
40133
  function sha256OfFileSync2(path) {
39611
- return createHash8("sha256").update(readFileSync13(path)).digest("hex");
40134
+ return createHash8("sha256").update(readFileSync14(path)).digest("hex");
39612
40135
  }
39613
40136
  async function fetchReleaseByTag(githubRepo, tag, fetchImpl, signal) {
39614
40137
  const candidates = [];
@@ -39784,7 +40307,7 @@ async function downloadFile(url, destPath, fetchImpl, assetSize, signal) {
39784
40307
  if (Number.isFinite(advertised) && advertised > MAX_DOWNLOAD_BYTES3) {
39785
40308
  throw new Error(`Content-Length ${advertised} exceeds max ${MAX_DOWNLOAD_BYTES3}`);
39786
40309
  }
39787
- mkdirSync8(dirname8(destPath), { recursive: true });
40310
+ mkdirSync8(dirname9(destPath), { recursive: true });
39788
40311
  let bytesWritten = 0;
39789
40312
  const guard = new TransformStream({
39790
40313
  transform(chunk, controller) {
@@ -39954,7 +40477,7 @@ function quarantineCachedGithubInstall(spec, reason) {
39954
40477
  const dest = join18(ghCacheRoot(), ".quarantine", encodeURIComponent(spec.id), `${Date.now()}`);
39955
40478
  warn2(`[lsp] tofu_mismatch ${spec.id}: ${reason}; quarantining ${packageDir} -> ${dest}`);
39956
40479
  try {
39957
- mkdirSync8(dirname8(dest), { recursive: true });
40480
+ mkdirSync8(dirname9(dest), { recursive: true });
39958
40481
  rmSync6(dest, { recursive: true, force: true });
39959
40482
  renameSync10(packageDir, dest);
39960
40483
  } catch (err) {
@@ -39966,7 +40489,7 @@ function validateCachedGithubInstall(spec, platform) {
39966
40489
  const meta = readGithubInstalledMetaIn(packageDir);
39967
40490
  const binaryPath = ghBinaryCandidates(spec, platform).map((candidate) => join18(ghBinDir(spec), candidate)).find((candidate) => {
39968
40491
  try {
39969
- return statSync11(candidate).isFile();
40492
+ return statSync12(candidate).isFile();
39970
40493
  } catch {
39971
40494
  return false;
39972
40495
  }
@@ -40084,7 +40607,7 @@ async function downloadAndInstall(spec, tag, assets, platform, arch, fetchImpl,
40084
40607
  return { ok: false, sha256: archiveSha256, error: message };
40085
40608
  }
40086
40609
  const targetBinary = ghBinaryPath(spec, platform);
40087
- mkdirSync8(dirname8(targetBinary), { recursive: true });
40610
+ mkdirSync8(dirname9(targetBinary), { recursive: true });
40088
40611
  try {
40089
40612
  copyFileSync3(innerBinaryPath, targetBinary);
40090
40613
  if (platform !== "win32") {
@@ -40613,14 +41136,16 @@ async function assertExternalDirectoryPermission(ctx, context, target, options)
40613
41136
  const directory = root ? normalizePath(root) : root;
40614
41137
  const rawWorktree = context.worktree;
40615
41138
  const worktree = rawWorktree && rawWorktree !== "/" ? normalizePath(rawWorktree) : rawWorktree;
41139
+ const restrictToProjectRoot = ctx.config.restrict_to_project_root === true;
40616
41140
  if (directory && containsPath(directory, absoluteTarget))
40617
41141
  return;
40618
- if (worktree && worktree !== "/" && worktree !== directory && containsPath(worktree, absoluteTarget)) {
41142
+ if (!restrictToProjectRoot && worktree && worktree !== "/" && worktree !== directory && containsPath(worktree, absoluteTarget)) {
40619
41143
  return;
40620
41144
  }
40621
- if (ctx.config.restrict_to_project_root === true) {
40622
- if (options?.serverValidatedRead === true)
41145
+ if (restrictToProjectRoot) {
41146
+ if (options?.serverValidatedRead === true && await isSessionOwnedBashArtifact(ctx, context, absoluteTarget)) {
40623
41147
  return;
41148
+ }
40624
41149
  notifyRestrictBlocked(ctx, context, absoluteTarget);
40625
41150
  return restrictDenialMessage(absoluteTarget);
40626
41151
  }
@@ -41150,8 +41675,87 @@ function projectV2Tool(name, definition, location, consumers = {}) {
41150
41675
  }
41151
41676
 
41152
41677
  // src/tools/hoisted.ts
41678
+ import { stat as stat2 } from "node:fs/promises";
41153
41679
  import * as path4 from "node:path";
41154
41680
 
41681
+ // src/shared/read-vision.ts
41682
+ var VISION_HOST_TIMEOUT_MS = 1500;
41683
+ var states = new WeakMap;
41684
+ function stateFor2(client) {
41685
+ let state = states.get(client);
41686
+ if (!state) {
41687
+ state = { models: new Map, history: new Map, capabilities: new Map };
41688
+ states.set(client, state);
41689
+ }
41690
+ return state;
41691
+ }
41692
+ function rememberReadModel(client, sessionID, model) {
41693
+ stateFor2(client).models.set(sessionID, model);
41694
+ }
41695
+ async function bounded(call) {
41696
+ let timer;
41697
+ try {
41698
+ return await Promise.race([
41699
+ Promise.resolve().then(call),
41700
+ new Promise((resolve) => {
41701
+ timer = setTimeout(() => resolve(undefined), VISION_HOST_TIMEOUT_MS);
41702
+ })
41703
+ ]);
41704
+ } catch {
41705
+ return;
41706
+ } finally {
41707
+ clearTimeout(timer);
41708
+ }
41709
+ }
41710
+ function record3(value) {
41711
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : undefined;
41712
+ }
41713
+ async function currentSessionVisionCapability(client, sessionID) {
41714
+ if (!sessionID)
41715
+ return;
41716
+ const state = stateFor2(client);
41717
+ let model = state.models.get(sessionID);
41718
+ if (!model) {
41719
+ let pending = state.history.get(sessionID);
41720
+ if (!pending) {
41721
+ pending = bounded(() => resolvePromptContext(client, sessionID)).then((context) => context?.model);
41722
+ state.history.set(sessionID, pending);
41723
+ pending.finally(() => state.history.delete(sessionID));
41724
+ }
41725
+ const historical = await pending;
41726
+ model = state.models.get(sessionID) ?? historical;
41727
+ }
41728
+ if (!model)
41729
+ return;
41730
+ const currentModel = model;
41731
+ const key = JSON.stringify([model.providerID, model.modelID]);
41732
+ let capability = state.capabilities.get(key);
41733
+ if (!capability) {
41734
+ capability = (async () => {
41735
+ const api = client.provider;
41736
+ if (!api?.list)
41737
+ return;
41738
+ const list = api.list.bind(api);
41739
+ const listed = record3(await bounded(list));
41740
+ const catalog = record3(listed?.data) ?? listed;
41741
+ const providers = catalog?.all ?? catalog?.providers;
41742
+ if (!Array.isArray(providers))
41743
+ return;
41744
+ const provider = providers.map(record3).find((entry) => entry?.id === currentModel.providerID);
41745
+ const models = provider?.models;
41746
+ const entry = record3(Array.isArray(models) ? models.find((entry) => record3(entry)?.id === currentModel.modelID) : record3(models)?.[currentModel.modelID]);
41747
+ const inputs = record3(entry?.modalities)?.input;
41748
+ return Array.isArray(inputs) ? inputs.includes("image") : typeof entry?.attachment === "boolean" ? entry.attachment : undefined;
41749
+ })();
41750
+ state.capabilities.set(key, capability);
41751
+ capability.then((value) => {
41752
+ if (value === undefined)
41753
+ state.capabilities.delete(key);
41754
+ });
41755
+ }
41756
+ return capability;
41757
+ }
41758
+
41155
41759
  // src/shared/subagent-detect.ts
41156
41760
  import { Effect as Effect2 } from "effect";
41157
41761
  var CACHE_MAX_ENTRIES2 = 200;
@@ -41738,7 +42342,7 @@ A notification will fire when the pattern matches or the task exits.`;
41738
42342
  const data = await waitForBashStatus(ctx, context, taskId, undefined, waitFor, effectiveWaitMs);
41739
42343
  const waited = data.waited;
41740
42344
  if (waited?.reason === "user_message") {
41741
- const convertedText = await convertToAsyncWatchOnAbort(ctx, context, taskId, waitFor, coerceBoolean(args.once, true));
42345
+ const convertedText = await convertToAsyncWatchOnAbort(ctx, context, taskId, waitFor, coerceBoolean(args.once, true), waited.elapsed_ms);
41742
42346
  const metadata = context.metadata;
41743
42347
  metadata?.({ taskId, status: data.status, waited, convertedToAsync: true });
41744
42348
  return convertedText;
@@ -41750,9 +42354,10 @@ A notification will fire when the pattern matches or the task exits.`;
41750
42354
  }
41751
42355
  };
41752
42356
  }
41753
- async function convertToAsyncWatchOnAbort(ctx, context, taskId, waitFor, once) {
42357
+ async function convertToAsyncWatchOnAbort(ctx, context, taskId, waitFor, once, elapsedMs) {
42358
+ const interrupted = `Sync watch for task ${taskId} was interrupted because you sent a message after ${elapsedMs}ms of waiting. `;
41754
42359
  if (!waitFor) {
41755
- return `Sync watch for task ${taskId} was interrupted because you sent a message. ` + `The task is still running in the background. A completion reminder will be ` + `delivered automatically when the task exits; don't poll bash_status.`;
42360
+ return interrupted + `The task is still running in the background. A completion reminder will be ` + `delivered automatically when the task exits; don't poll bash_status.`;
41756
42361
  }
41757
42362
  const notifyParams = {
41758
42363
  task_id: taskId,
@@ -41767,12 +42372,12 @@ async function convertToAsyncWatchOnAbort(ctx, context, taskId, waitFor, once) {
41767
42372
  const registered = await callBashBridge(ctx, context, "bash_notify", notifyParams);
41768
42373
  if (registered.success === false) {
41769
42374
  unmarkExplicitControl(context.sessionID, taskId);
41770
- return `Sync watch for task ${taskId} was interrupted because you sent a message. ` + `Auto-registering an async watch failed (${String(registered.message ?? "unknown error")}). ` + `The task is still running in the background. A completion reminder will be ` + `delivered automatically when the task exits.`;
42375
+ return interrupted + `Auto-registering an async watch failed (${String(registered.message ?? "unknown error")}). ` + `The task is still running in the background. A completion reminder will be ` + `delivered automatically when the task exits.`;
41771
42376
  }
41772
- return `Sync watch for task ${taskId} was interrupted because you sent a message. ` + `The wait has been converted to an async watch (${registered.watch_id}). ` + `A notification will fire when the pattern matches or the task exits.`;
42377
+ return interrupted + `The wait has been converted to an async watch (${registered.watch_id}). ` + `A notification will fire when the pattern matches or the task exits.`;
41773
42378
  } catch (err) {
41774
42379
  unmarkExplicitControl(context.sessionID, taskId);
41775
- return `Sync watch for task ${taskId} was interrupted because you sent a message. ` + `Auto-registering an async watch failed (${err instanceof Error ? err.message : String(err)}). ` + `The task is still running in the background. A completion reminder will be ` + `delivered automatically when the task exits.`;
42380
+ return interrupted + `Auto-registering an async watch failed (${err instanceof Error ? err.message : String(err)}). ` + `The task is still running in the background. A completion reminder will be ` + `delivered automatically when the task exits.`;
41776
42381
  }
41777
42382
  }
41778
42383
  function formatWatchResultText(taskId, data, waited, role, syncWaitCap) {
@@ -41781,21 +42386,22 @@ function formatWatchResultText(taskId, data, waited, role, syncWaitCap) {
41781
42386
  const dur = typeof data.duration_ms === "number" ? ` ${Math.round(data.duration_ms / 1000)}s` : "";
41782
42387
  let text = `Task ${taskId}: ${status}${exit}${dur}`;
41783
42388
  if (waited) {
42389
+ const waitedText = formatWatchWaited(waited.elapsed_ms, waited.limit_ms, syncWaitCap);
41784
42390
  if (waited.reason === "matched") {
41785
42391
  const stream = waited.match_stream ? ` in ${waited.match_stream}` : "";
41786
42392
  text += `
41787
- Waited ${waited.elapsed_ms}ms; matched ${JSON.stringify(waited.match ?? "")}${stream} at offset ${waited.match_offset ?? 0}.`;
42393
+ ${waitedText}; matched ${JSON.stringify(waited.match ?? "")}${stream} at offset ${waited.match_offset ?? 0}.`;
41788
42394
  } else if (waited.reason === "timeout") {
41789
42395
  text += `
41790
- Waited ${waited.elapsed_ms}ms; timeout reached without match. ${watchTimeoutSteer(role, syncWaitCap)}`;
42396
+ ${waitedText}; timeout reached without match. ${watchTimeoutSteer(role, syncWaitCap)}`;
41791
42397
  } else if (waited.reason === "unavailable") {
41792
42398
  text += `
41793
- Waited ${waited.elapsed_ms}ms; the bridge was busy, so task state is unknown. Do not poll; let the task's completion notification wake the session, or use one bash_status snapshot on the next normal tool call.`;
42399
+ ${waitedText}; the bridge was busy, so task state is unknown. Do not poll; let the task's completion notification wake the session, or use one bash_status snapshot on the next normal tool call.`;
41794
42400
  } else {
41795
42401
  const stat = String(data.status ?? "unknown");
41796
42402
  const e = typeof data.exit_code === "number" ? `, exit ${data.exit_code}` : "";
41797
42403
  text += `
41798
- Waited ${waited.elapsed_ms}ms; task exited (${stat}${e}).`;
42404
+ ${waitedText}; task exited (${stat}${e}).`;
41799
42405
  }
41800
42406
  }
41801
42407
  const preview = data.output_preview;
@@ -41817,8 +42423,9 @@ async function bashStatusSnapshot2(ctx, runtime, taskId, outputMode, cursor, opt
41817
42423
  return data;
41818
42424
  }
41819
42425
  async function waitForBashStatus(ctx, runtime, taskId, outputMode, waitFor, effectiveWaitMs) {
41820
- const startedAt = Date.now();
42426
+ const startedAt = monotonicNowMs();
41821
42427
  const deadline = startedAt + effectiveWaitMs;
42428
+ const elapsedMs = () => Math.round(monotonicNowMs() - startedAt);
41822
42429
  let spillCursor = { output: 0, stderr: 0 };
41823
42430
  const scanState = {
41824
42431
  output: { text: "", baseOffset: 0 },
@@ -41843,16 +42450,18 @@ async function waitForBashStatus(ctx, runtime, taskId, outputMode, waitFor, effe
41843
42450
  if (isSyncWatchAborted(runtime.sessionID)) {
41844
42451
  return withWaited(lastData ?? unavailableSnapshot(), {
41845
42452
  reason: "user_message",
41846
- elapsed_ms: Date.now() - startedAt
42453
+ elapsed_ms: elapsedMs(),
42454
+ limit_ms: effectiveWaitMs
41847
42455
  });
41848
42456
  }
41849
- if (Date.now() >= deadline) {
42457
+ if (monotonicNowMs() >= deadline) {
41850
42458
  return withWaited(lastData ?? unavailableSnapshot(), {
41851
42459
  reason: "unavailable",
41852
- elapsed_ms: Date.now() - startedAt
42460
+ elapsed_ms: elapsedMs(),
42461
+ limit_ms: effectiveWaitMs
41853
42462
  });
41854
42463
  }
41855
- await sleep(Math.min(BASH_WAIT_POLL_INTERVAL_MS, Math.max(0, deadline - Date.now())));
42464
+ await sleep(Math.min(BASH_WAIT_POLL_INTERVAL_MS, Math.max(0, deadline - monotonicNowMs())));
41856
42465
  continue;
41857
42466
  }
41858
42467
  lastData = data;
@@ -41881,7 +42490,8 @@ async function waitForBashStatus(ctx, runtime, taskId, outputMode, waitFor, effe
41881
42490
  const matchStream = data.mode === "pty" ? undefined : chunk.stream === "output" ? "stdout" : "stderr";
41882
42491
  return withWaited(data, {
41883
42492
  reason: "matched",
41884
- elapsed_ms: Date.now() - startedAt,
42493
+ elapsed_ms: elapsedMs(),
42494
+ limit_ms: effectiveWaitMs,
41885
42495
  match: match.text,
41886
42496
  match_offset: state.baseOffset + match.byteOffset,
41887
42497
  match_stream: matchStream
@@ -41899,15 +42509,27 @@ async function waitForBashStatus(ctx, runtime, taskId, outputMode, waitFor, effe
41899
42509
  sawTerminal = true;
41900
42510
  consumeBgCompletion(runtime.sessionID, taskId);
41901
42511
  await markBgCompletionDelivered({ ctx, directory: projectRootFor(runtime), sessionID: runtime.sessionID }, taskId);
41902
- return withWaited(data, { reason: "exited", elapsed_ms: Date.now() - startedAt });
42512
+ return withWaited(data, {
42513
+ reason: "exited",
42514
+ elapsed_ms: elapsedMs(),
42515
+ limit_ms: effectiveWaitMs
42516
+ });
41903
42517
  }
41904
42518
  if (isSyncWatchAborted(runtime.sessionID)) {
41905
- return withWaited(data, { reason: "user_message", elapsed_ms: Date.now() - startedAt });
42519
+ return withWaited(data, {
42520
+ reason: "user_message",
42521
+ elapsed_ms: elapsedMs(),
42522
+ limit_ms: effectiveWaitMs
42523
+ });
41906
42524
  }
41907
- if (Date.now() >= deadline) {
41908
- return withWaited(data, { reason: "timeout", elapsed_ms: Date.now() - startedAt });
42525
+ if (monotonicNowMs() >= deadline) {
42526
+ return withWaited(data, {
42527
+ reason: "timeout",
42528
+ elapsed_ms: elapsedMs(),
42529
+ limit_ms: effectiveWaitMs
42530
+ });
41909
42531
  }
41910
- await sleep(Math.min(BASH_WAIT_POLL_INTERVAL_MS, Math.max(0, deadline - Date.now())));
42532
+ await sleep(Math.min(BASH_WAIT_POLL_INTERVAL_MS, Math.max(0, deadline - monotonicNowMs())));
41911
42533
  }
41912
42534
  } finally {
41913
42535
  if (!sawTerminal)
@@ -42055,47 +42677,6 @@ var ISSUE_AND_PR_READ_DESCRIPTION = "GitHub issues and pull requests can be read
42055
42677
  function whenGhReadEnabled(enabled, description) {
42056
42678
  return enabled ? description : "";
42057
42679
  }
42058
- function asRecord(value) {
42059
- return typeof value === "object" && value !== null && !Array.isArray(value) ? value : undefined;
42060
- }
42061
- function visionCapabilityForOpenCodeModel(model) {
42062
- const entry = model;
42063
- if (Array.isArray(entry?.modalities?.input)) {
42064
- return entry.modalities.input.includes("image");
42065
- }
42066
- return typeof entry?.attachment === "boolean" ? entry.attachment : undefined;
42067
- }
42068
- function modelFromOpenCodeProvider(provider, modelID) {
42069
- const models = provider.models;
42070
- if (Array.isArray(models)) {
42071
- return models.find((model) => asRecord(model)?.id === modelID);
42072
- }
42073
- return asRecord(models)?.[modelID];
42074
- }
42075
- async function currentSessionVisionCapability(client, sessionID) {
42076
- if (!sessionID)
42077
- return;
42078
- const promptContext = await resolvePromptContext(client, sessionID);
42079
- const currentModel = promptContext?.model;
42080
- if (!currentModel)
42081
- return;
42082
- const providerApi = client.provider;
42083
- if (typeof providerApi?.list !== "function")
42084
- return;
42085
- let listed;
42086
- try {
42087
- listed = await providerApi.list();
42088
- } catch {
42089
- return;
42090
- }
42091
- const result = asRecord(listed);
42092
- const catalog = asRecord(result?.data) ?? result;
42093
- const providers = Array.isArray(catalog?.all) ? catalog.all : Array.isArray(catalog?.providers) ? catalog.providers : [];
42094
- const provider = providers.map(asRecord).find((entry) => entry?.id === currentModel.providerID);
42095
- if (!provider)
42096
- return;
42097
- return visionCapabilityForOpenCodeModel(modelFromOpenCodeProvider(provider, currentModel.modelID));
42098
- }
42099
42680
  function persistFilePathAlias(args, context) {
42100
42681
  if (typeof args.path === "string" && !Object.hasOwn(args, "filePath")) {
42101
42682
  args.filePath = args.path;
@@ -42267,7 +42848,7 @@ function inferBeforeStart(ops, from, beforeLen) {
42267
42848
  var z7 = tool.schema;
42268
42849
  var READ_DESCRIPTION = `Read file contents or list directory entries.
42269
42850
 
42270
- Use either startLine/endLine OR offset/limit to read a section of a file.
42851
+ Use either startLine/endLine OR offset/limit to read a section of a file or sorted directory listing.
42271
42852
 
42272
42853
  Behavior:
42273
42854
  - Returns line-numbered content (e.g., "1: const x = 1")
@@ -42275,7 +42856,7 @@ Behavior:
42275
42856
  - Output capped at 50KB
42276
42857
  - Binary files are auto-detected and return a size-only message
42277
42858
  - Supported images (PNG, JPEG, GIF, WebP) and PDFs are returned as tool attachments; range arguments are ignored for media
42278
- - Directories return sorted entries with trailing / for subdirectories
42859
+ - Directories return sorted entries with trailing / for subdirectories; offset is 1-based, limit defaults to and is capped at 1000 entries. Enumeration stops at 10,000 entries; partial listings carry a shown/total trailer.
42279
42860
 
42280
42861
  Examples:
42281
42862
  Read full file: { "path": "src/app.ts" }
@@ -42299,30 +42880,30 @@ function createReadTool(ctx) {
42299
42880
  description: readDescription(resolveGithubConfig(ctx.config).read),
42300
42881
  args: {
42301
42882
  filePath: z7.string().describe("Path to file or directory (absolute or relative to project root)"),
42302
- startLine: optionalInt(1, Number.MAX_SAFE_INTEGER).describe("1-based line to start reading from"),
42303
- endLine: optionalInt(1, Number.MAX_SAFE_INTEGER).describe("1-based line to stop reading at (inclusive)"),
42304
- limit: optionalInt(1, Number.MAX_SAFE_INTEGER).describe("Max lines to return (default: 2000)"),
42305
- offset: optionalInt(1, Number.MAX_SAFE_INTEGER).describe("1-based line number to start reading from (use with limit). Ignored if startLine is provided")
42883
+ startLine: optionalInt(1, Number.MAX_SAFE_INTEGER).describe("1-based line or directory entry to start reading from"),
42884
+ endLine: optionalInt(1, Number.MAX_SAFE_INTEGER).describe("1-based line or directory entry to stop reading at (inclusive)"),
42885
+ limit: optionalInt(1, Number.MAX_SAFE_INTEGER).describe("Max lines (default: 2000) or directory entries (default and cap: 1000) to return"),
42886
+ offset: optionalInt(1, Number.MAX_SAFE_INTEGER).describe("1-based line or directory entry to start reading from (use with limit). Ignored if startLine is provided")
42306
42887
  },
42307
42888
  execute: async (args, context) => {
42308
42889
  const file = args.path;
42309
- const projectRoot = await resolveProjectRoot(ctx, context);
42890
+ const projectRoot = await measurePreStage("directory", () => resolveProjectRoot(ctx, context));
42310
42891
  const filePath = resolvePathFromProjectRoot(projectRoot, file);
42311
42892
  persistFilePathAlias(args, context);
42312
42893
  {
42313
- const denial = await assertExternalDirectoryPermission(ctx, context, filePath, {
42894
+ const denial = await measurePreStage("permission", () => assertExternalDirectoryPermission(ctx, context, filePath, {
42314
42895
  serverValidatedRead: true
42315
- });
42896
+ }));
42316
42897
  if (denial)
42317
42898
  return permissionDeniedResponse(denial);
42318
42899
  }
42319
42900
  try {
42320
- await runAsk(context.ask({
42901
+ await measurePreStage("permission", () => runAsk(context.ask({
42321
42902
  permission: "read",
42322
42903
  patterns: [permissionPath(context, filePath)],
42323
42904
  always: ["*"],
42324
42905
  metadata: {}
42325
- }));
42906
+ })));
42326
42907
  } catch (error) {
42327
42908
  const failure = classifyPermissionError(error);
42328
42909
  return permissionDeniedResponse(failure.kind === "rule_denied" ? permissionRuleDenial("read") : failure.message);
@@ -42346,7 +42927,8 @@ function createReadTool(ctx) {
42346
42927
  rawArgs.endLine = endLine;
42347
42928
  if (rawLimit !== undefined && rawOffset === undefined)
42348
42929
  rawArgs.limit = rawLimit;
42349
- const visionCapability = await currentSessionVisionCapability(ctx.client, context.sessionID);
42930
+ const mediaCandidate = isGithubResourcePath(file) || /\.(png|jpe?g|gif|webp|pdf)$/i.test(filePath) && await stat2(filePath).then((info) => info.isFile(), () => false);
42931
+ const visionCapability = mediaCandidate ? await measurePreStage("capability", () => currentSessionVisionCapability(ctx.client, context.sessionID)) : undefined;
42350
42932
  if (visionCapability !== undefined)
42351
42933
  rawArgs.vision_capability = visionCapability;
42352
42934
  const response = await callToolCall(ctx, context, "read", rawArgs);
@@ -42790,7 +43372,7 @@ function createApplyPatchTool(ctx) {
42790
43372
  };
42791
43373
  }
42792
43374
  function deleteDescription(ctx) {
42793
- const backupText = ctx.config.backup?.enabled === false ? "Backup capture is disabled by user config, so this tool does not create undo snapshots. " : "Each file is backed up before deletion — use aft_safety undo to recover any of them. For directories, every file inside is individually backed up before the tree is removed. The deleted file's contents stay in the undo store until its retention expires. A recursive delete whose backup would copy more than 2,000 files or 100 MiB in one call is refused before anything is deleted; delete such a tree in smaller pieces, or use bash `rm -rf` when no undo is needed. ";
43375
+ const backupText = ctx.config.backup?.enabled === false ? "Backup capture is disabled by user config, so this tool does not create undo snapshots. A directory tree containing a mount point of another filesystem is still refused. " : "Each file is backed up before deletion — use aft_safety undo to recover any of them. A recursive delete backs up the whole tree: directories (empty ones too, with their permissions), file contents, hard links (relinked by undo) and symlinks (the link itself, never its target; restored exactly, even when dangling). Sockets are deleted but undo does not restore them, and a file hard-linked to paths outside the tree comes back as an independent copy; both are reported as warnings. Refused before anything is deleted: mount points of another filesystem, named pipes, device nodes, and symlinks undo cannot recreate exactly (non-UTF-8 target, or on Windows). Paths under the system temp directory are never backed up, so only mount points are refused there. The deleted file's contents stay in the undo store until its retention expires. A recursive delete whose backup would record more than 2,000 entries (files, directories and links) or copy 100 MiB in one call is refused before anything is deleted; delete such a tree in smaller pieces, or use bash `rm -rf` when no undo is needed. ";
42794
43376
  return `Delete one or more files (or directories).
42795
43377
 
42796
43378
  ` + backupText + `Directory deletion requires recursive: true. Without it, passing a directory returns an error.
@@ -43006,7 +43588,7 @@ async function resolveAndGateScope(ctx, context, scope) {
43006
43588
  }
43007
43589
  return { scope: Array.isArray(scope) ? resolved : resolved[0] };
43008
43590
  }
43009
- function asRecord2(value) {
43591
+ function asRecord(value) {
43010
43592
  return value !== null && typeof value === "object" && !Array.isArray(value) ? value : undefined;
43011
43593
  }
43012
43594
  function asString(value) {
@@ -43014,7 +43596,7 @@ function asString(value) {
43014
43596
  }
43015
43597
  function firstRecord(...values) {
43016
43598
  for (const value of values) {
43017
- const record = asRecord2(value);
43599
+ const record = asRecord(value);
43018
43600
  if (record)
43019
43601
  return record;
43020
43602
  }
@@ -43049,7 +43631,7 @@ function parseInspectPhaseEntries(value) {
43049
43631
  if (!Array.isArray(value))
43050
43632
  return [];
43051
43633
  return value.flatMap((candidate) => {
43052
- const entry = asRecord2(candidate);
43634
+ const entry = asRecord(candidate);
43053
43635
  const id = asString(entry?.id);
43054
43636
  if (!id)
43055
43637
  return [];
@@ -43065,7 +43647,7 @@ function parseInspectPhaseEntries(value) {
43065
43647
  });
43066
43648
  }
43067
43649
  function parseInspectTerminal(payload) {
43068
- const response = asRecord2(payload);
43650
+ const response = asRecord(payload);
43069
43651
  if (!response)
43070
43652
  return;
43071
43653
  const kind = terminalKind(response);
@@ -43080,7 +43662,7 @@ function parseInspectTerminal(payload) {
43080
43662
  waitStampText: kind === "FRESH" ? firstString(waitStamp?.text, waitStamp?.human_text) : undefined
43081
43663
  };
43082
43664
  }
43083
- const failedPhaseRecord = asRecord2(response.failed_phase);
43665
+ const failedPhaseRecord = asRecord(response.failed_phase);
43084
43666
  const failedPhaseId = firstString(failedPhaseRecord?.id, response.failed_phase);
43085
43667
  const failedPhase = failedPhaseId ? parseInspectPhaseEntries([
43086
43668
  {
@@ -43853,7 +44435,7 @@ function hasMeaningfulProjectSearchFallback(query, pathArg, externalRoot) {
43853
44435
  return trimmedQuery.length > 0 && trimmedQuery !== pathArg && trimmedQuery !== externalRoot;
43854
44436
  }
43855
44437
  var GENERATION_CHANGED_DISCLOSURE = "index changed - order re-derived";
43856
- function asRecord3(value) {
44438
+ function asRecord2(value) {
43857
44439
  return typeof value === "object" && value !== null && !Array.isArray(value) ? value : undefined;
43858
44440
  }
43859
44441
  function normalizeContinuityQuery(query) {
@@ -43868,14 +44450,14 @@ function continuityKey(projectRoot, query, includeTests, topK) {
43868
44450
  ]);
43869
44451
  }
43870
44452
  function responseGeneration(response) {
43871
- const envelope = asRecord3(response);
43872
- const structured = asRecord3(envelope?.structuredContent) ?? asRecord3(envelope?.structured_content);
43873
- const plan = asRecord3(envelope?.plan) ?? asRecord3(structured?.plan);
44453
+ const envelope = asRecord2(response);
44454
+ const structured = asRecord2(envelope?.structuredContent) ?? asRecord2(envelope?.structured_content);
44455
+ const plan = asRecord2(envelope?.plan) ?? asRecord2(structured?.plan);
43874
44456
  const generation = plan?.snapshot_generation ?? envelope?.snapshot_generation;
43875
44457
  return typeof generation === "string" ? generation : undefined;
43876
44458
  }
43877
44459
  function surfaceSearchText(response, continuity, sessionId, key) {
43878
- const envelope = asRecord3(response);
44460
+ const envelope = asRecord2(response);
43879
44461
  const text = typeof envelope?.text === "string" ? envelope.text : "";
43880
44462
  const generation = responseGeneration(response);
43881
44463
  if (generation === undefined)
@@ -44153,6 +44735,8 @@ var defaultBridgeBootstrapDependencies = {
44153
44735
  loadConfig: loadAftConfig,
44154
44736
  deliverLoadNotices: (notify) => deliverConfigLoadNotices(notify, getConfigLoadNotices()),
44155
44737
  configLoadErrors: getConfigLoadErrors,
44738
+ configLoadSources: getConfigLoadSources,
44739
+ configLoadTexts: getConfigLoadTexts,
44156
44740
  subcConnectionFileError,
44157
44741
  migrateConfigLocations: (directory) => migrateAftConfigLocations(directory, bridgeLogger).flatMap((result) => result.warnings),
44158
44742
  resolveBinary: resolveBinaryWithWarmup,
@@ -44194,8 +44778,12 @@ function loadBootstrapConfig(directory, notify, dependencies = defaultBridgeBoot
44194
44778
  const failure = parseFailure(dependencies);
44195
44779
  if (failure)
44196
44780
  return configErrorState(failure, notify);
44781
+ const provenance = dependencies.configLoadSources ? {
44782
+ sources: [...dependencies.configLoadSources()],
44783
+ sourceTexts: Object.fromEntries(dependencies.configLoadTexts?.() ?? [])
44784
+ } : {};
44197
44785
  dependencies.deliverLoadNotices?.(notify);
44198
- return { ok: true, config };
44786
+ return { ok: true, config, ...provenance };
44199
44787
  } catch (err) {
44200
44788
  return configErrorState(err instanceof Error ? err.message : String(err), notify);
44201
44789
  }
@@ -44387,8 +44975,60 @@ function buildConfigErrorToolMap(config, message, client, buildToolMap = buildAf
44387
44975
  return failEveryToolCall(buildToolMap(ctx, config), message);
44388
44976
  }
44389
44977
 
44978
+ // src/config-live-reload.ts
44979
+ var OPENCODE_LIVE_CONFIG_KEYS = aftLiveConfigKeys((config) => resolveBashConfig(config));
44980
+ function projectTextOf(directory) {
44981
+ const { projectConfigPath } = resolveCortexKitConfigPaths(directory);
44982
+ return getConfigLoadTexts().get(projectConfigPath) ?? null;
44983
+ }
44984
+ function loadOpenCodeConfigForLiveReload(directory) {
44985
+ try {
44986
+ const config = loadAftConfig(directory);
44987
+ const [failure] = getConfigLoadErrors();
44988
+ if (failure) {
44989
+ return { ok: false, message: formatConfigParseErrorMessage(failure.path, failure.message) };
44990
+ }
44991
+ const [invalid] = getConfigValidationErrors();
44992
+ if (invalid) {
44993
+ return {
44994
+ ok: false,
44995
+ message: `AFT config at ${invalid.path} has an invalid setting: ${invalid.message}`
44996
+ };
44997
+ }
44998
+ return {
44999
+ ok: true,
45000
+ config,
45001
+ sources: [...getConfigLoadSources()],
45002
+ projectText: projectTextOf(directory),
45003
+ texts: Object.fromEntries(getConfigLoadTexts())
45004
+ };
45005
+ } catch (err) {
45006
+ return { ok: false, message: err instanceof Error ? err.message : String(err) };
45007
+ }
45008
+ }
45009
+ function startOpenCodeLiveConfigReload(options) {
45010
+ const { userConfigPath, projectConfigPath } = resolveCortexKitConfigPaths(options.directory);
45011
+ return startLiveConfigReload({
45012
+ paths: [userConfigPath, projectConfigPath],
45013
+ load: () => loadOpenCodeConfigForLiveReload(options.directory),
45014
+ initialSources: options.initialSources,
45015
+ initialProjectText: options.initialSourceTexts?.[projectConfigPath] ?? null,
45016
+ initialTexts: options.initialSourceTexts,
45017
+ securityKeys: aftLiveSecurityKeys(OPENCODE_LIVE_CONFIG_KEYS),
45018
+ keys: OPENCODE_LIVE_CONFIG_KEYS,
45019
+ getConfig: options.getConfig,
45020
+ setConfig: options.setConfig,
45021
+ log: (message) => log2(`${message} (${options.directory})`),
45022
+ reportError: (message) => {
45023
+ error2(message);
45024
+ options.notify(message);
45025
+ },
45026
+ watch: options.watch
45027
+ });
45028
+ }
45029
+
44390
45030
  // src/notifications.ts
44391
- import { existsSync as existsSync18, readFileSync as readFileSync14 } from "node:fs";
45031
+ import { existsSync as existsSync18, readFileSync as readFileSync15 } from "node:fs";
44392
45032
  import { homedir as homedir13, platform } from "node:os";
44393
45033
  import { join as join20 } from "node:path";
44394
45034
  function isTuiMode() {
@@ -44430,7 +45070,7 @@ function readDesktopState() {
44430
45070
  return { serverUrl: null };
44431
45071
  }
44432
45072
  try {
44433
- const raw = readFileSync14(statePath, "utf-8");
45073
+ const raw = readFileSync15(statePath, "utf-8");
44434
45074
  const state = JSON.parse(raw);
44435
45075
  let serverUrl = null;
44436
45076
  const serverStr = state.server;
@@ -44929,23 +45569,25 @@ import {
44929
45569
  existsSync as existsSync21,
44930
45570
  mkdirSync as mkdirSync9,
44931
45571
  openSync as openSync7,
44932
- readFileSync as readFileSync17,
45572
+ readFileSync as readFileSync18,
44933
45573
  renameSync as renameSync11,
44934
45574
  rmSync as rmSync8,
44935
- statSync as statSync14,
45575
+ statSync as statSync15,
44936
45576
  writeFileSync as writeFileSync12
44937
45577
  } from "node:fs";
44938
- import { dirname as dirname14 } from "node:path";
45578
+ import { dirname as dirname15 } from "node:path";
44939
45579
 
44940
45580
  // src/hooks/auto-update-checker/cache.ts
44941
- import { cpSync, existsSync as existsSync20, mkdtempSync, readFileSync as readFileSync16, rmSync as rmSync7, writeFileSync as writeFileSync11 } from "node:fs";
45581
+ import { cpSync, existsSync as existsSync20, mkdtempSync, readFileSync as readFileSync17, rmSync as rmSync7, writeFileSync as writeFileSync11 } from "node:fs";
44942
45582
  import { tmpdir as tmpdir4 } from "node:os";
44943
- import { basename as basename5, dirname as dirname13, join as join23 } from "node:path";
45583
+ import { basename as basename6, dirname as dirname14, join as join23 } from "node:path";
44944
45584
  var import_comment_json3 = __toESM(require_src2(), 1);
44945
45585
 
44946
45586
  // src/hooks/auto-update-checker/checker.ts
44947
- import { existsSync as existsSync19, readFileSync as readFileSync15, statSync as statSync13, writeFileSync as writeFileSync10 } from "node:fs";
44948
- import { dirname as dirname12, isAbsolute as isAbsolute11, join as join22, resolve as resolve13 } from "node:path";
45587
+ import { existsSync as existsSync19, readFileSync as readFileSync16, statSync as statSync14, writeFileSync as writeFileSync10 } from "node:fs";
45588
+ import { readFile } from "node:fs/promises";
45589
+ import { homedir as homedir14 } from "node:os";
45590
+ import { dirname as dirname13, isAbsolute as isAbsolute11, join as join22, resolve as resolve13 } from "node:path";
44949
45591
  import { fileURLToPath } from "node:url";
44950
45592
  var import_comment_json2 = __toESM(require_src2(), 1);
44951
45593
 
@@ -45036,14 +45678,14 @@ function resolvePathPluginSpec(spec, configPath) {
45036
45678
  }
45037
45679
  if (isAbsolute11(spec) || /^[A-Za-z]:[\\/]/.test(spec))
45038
45680
  return spec;
45039
- return resolve13(dirname12(configPath), spec);
45681
+ return resolve13(dirname13(configPath), spec);
45040
45682
  }
45041
45683
  function getLocalDevPath(directory) {
45042
45684
  for (const configPath of getConfigPaths(directory)) {
45043
45685
  try {
45044
45686
  if (!existsSync19(configPath))
45045
45687
  continue;
45046
- const rawConfig = parseJsonConfig(readFileSync15(configPath, "utf-8"));
45688
+ const rawConfig = parseJsonConfig(readFileSync16(configPath, "utf-8"));
45047
45689
  const plugins = getPluginEntries(rawConfig);
45048
45690
  for (const entry of plugins) {
45049
45691
  if (entry === PACKAGE_NAME || entry.startsWith(`${PACKAGE_NAME}@`))
@@ -45053,7 +45695,7 @@ function getLocalDevPath(directory) {
45053
45695
  const pkgPath = findPackageJsonUp(localPath);
45054
45696
  if (!pkgPath)
45055
45697
  continue;
45056
- const pkg = PackageJsonSchema.safeParse(JSON.parse(readFileSync15(pkgPath, "utf-8")));
45698
+ const pkg = PackageJsonSchema.safeParse(JSON.parse(readFileSync16(pkgPath, "utf-8")));
45057
45699
  if (pkg.success && pkg.data.name === PACKAGE_NAME)
45058
45700
  return localPath;
45059
45701
  }
@@ -45064,18 +45706,18 @@ function getLocalDevPath(directory) {
45064
45706
  }
45065
45707
  function findPackageJsonUp(startPath) {
45066
45708
  try {
45067
- const stat = statSync13(startPath);
45068
- let dir = stat.isDirectory() ? startPath : dirname12(startPath);
45709
+ const stat = statSync14(startPath);
45710
+ let dir = stat.isDirectory() ? startPath : dirname13(startPath);
45069
45711
  for (let i = 0;i < 10; i++) {
45070
45712
  const pkgPath = join22(dir, "package.json");
45071
45713
  if (existsSync19(pkgPath)) {
45072
45714
  try {
45073
- const pkg = PackageJsonSchema.safeParse(JSON.parse(readFileSync15(pkgPath, "utf-8")));
45715
+ const pkg = PackageJsonSchema.safeParse(JSON.parse(readFileSync16(pkgPath, "utf-8")));
45074
45716
  if (pkg.success && pkg.data.name === PACKAGE_NAME)
45075
45717
  return pkgPath;
45076
45718
  } catch {}
45077
45719
  }
45078
- const parent = dirname12(dir);
45720
+ const parent = dirname13(dir);
45079
45721
  if (parent === dir)
45080
45722
  break;
45081
45723
  dir = parent;
@@ -45091,7 +45733,7 @@ function getLocalDevVersion(directory) {
45091
45733
  const pkgPath = findPackageJsonUp(localPath);
45092
45734
  if (!pkgPath)
45093
45735
  return null;
45094
- const pkg = PackageJsonSchema.safeParse(JSON.parse(readFileSync15(pkgPath, "utf-8")));
45736
+ const pkg = PackageJsonSchema.safeParse(JSON.parse(readFileSync16(pkgPath, "utf-8")));
45095
45737
  return pkg.success ? pkg.data.version ?? null : null;
45096
45738
  } catch {
45097
45739
  return null;
@@ -45099,7 +45741,7 @@ function getLocalDevVersion(directory) {
45099
45741
  }
45100
45742
  function getCurrentRuntimePackageJsonPath(currentModuleUrl = import.meta.url) {
45101
45743
  try {
45102
- return findPackageJsonUp(dirname12(fileURLToPath(currentModuleUrl)));
45744
+ return findPackageJsonUp(dirname13(fileURLToPath(currentModuleUrl)));
45103
45745
  } catch (err) {
45104
45746
  warn2(`[auto-update-checker] Failed to resolve runtime package path: ${String(err)}`);
45105
45747
  return null;
@@ -45110,7 +45752,7 @@ function findPluginEntry(directory) {
45110
45752
  try {
45111
45753
  if (!existsSync19(configPath))
45112
45754
  continue;
45113
- const rawConfig = parseJsonConfig(readFileSync15(configPath, "utf-8"));
45755
+ const rawConfig = parseJsonConfig(readFileSync16(configPath, "utf-8"));
45114
45756
  const plugins = getPluginEntries(rawConfig);
45115
45757
  for (const entry of plugins) {
45116
45758
  if (entry === PACKAGE_NAME) {
@@ -45143,7 +45785,7 @@ function getCachedVersion(spec) {
45143
45785
  try {
45144
45786
  if (!existsSync19(packageJsonPath))
45145
45787
  continue;
45146
- const pkg = PackageJsonSchema.safeParse(JSON.parse(readFileSync15(packageJsonPath, "utf-8")));
45788
+ const pkg = PackageJsonSchema.safeParse(JSON.parse(readFileSync16(packageJsonPath, "utf-8")));
45147
45789
  if (pkg.success && pkg.data.version) {
45148
45790
  if (!spec)
45149
45791
  cachedPackageVersion = pkg.data.version;
@@ -45153,6 +45795,117 @@ function getCachedVersion(spec) {
45153
45795
  }
45154
45796
  return null;
45155
45797
  }
45798
+ var registryCache = new Map;
45799
+ function npmConfigGet(key, directory) {
45800
+ return new Promise((resolve, reject) => {
45801
+ execFile(process.platform === "win32" ? "npm.cmd" : "npm", ["config", "get", key], {
45802
+ cwd: directory,
45803
+ timeout: NPM_FETCH_TIMEOUT,
45804
+ windowsHide: true,
45805
+ shell: process.platform === "win32"
45806
+ }, (error, stdout) => {
45807
+ if (error)
45808
+ reject(error);
45809
+ else
45810
+ resolve(stdout.trim());
45811
+ });
45812
+ });
45813
+ }
45814
+ function configuredRegistry(value) {
45815
+ return value && value !== "undefined" && value !== "null" ? value : undefined;
45816
+ }
45817
+ async function readRegistrySettings(path, required = false) {
45818
+ let text;
45819
+ try {
45820
+ text = await readFile(path, "utf8");
45821
+ } catch (error) {
45822
+ if (!required && error.code === "ENOENT")
45823
+ return {};
45824
+ throw error;
45825
+ }
45826
+ const settings = {};
45827
+ for (const line of text.split(/\r?\n/)) {
45828
+ const trimmed = line.trim();
45829
+ if (!trimmed || trimmed.startsWith("#") || trimmed.startsWith(";"))
45830
+ continue;
45831
+ const separator = trimmed.indexOf("=");
45832
+ if (separator < 0)
45833
+ continue;
45834
+ const key = trimmed.slice(0, separator).trim();
45835
+ if (key === "registry" || key === "@cortexkit:registry") {
45836
+ settings[key] = configuredRegistry(trimmed.slice(separator + 1).trim());
45837
+ }
45838
+ }
45839
+ return settings;
45840
+ }
45841
+ function registryEnvironment(key) {
45842
+ const variable = `npm_config_${key}`;
45843
+ const value = process.env[variable] ?? Object.entries(process.env).find(([name]) => name.toLowerCase() === variable)?.[1];
45844
+ return configuredRegistry(value ?? "");
45845
+ }
45846
+ async function registryWithoutNpm(directory) {
45847
+ let scoped = registryEnvironment("@cortexkit:registry");
45848
+ let registry = registryEnvironment("registry");
45849
+ const visited = new Set;
45850
+ for (let current = directory;; current = dirname13(current)) {
45851
+ if (scoped)
45852
+ break;
45853
+ const path = join22(current, ".npmrc");
45854
+ visited.add(path);
45855
+ const settings = await readRegistrySettings(path);
45856
+ scoped ??= settings["@cortexkit:registry"];
45857
+ registry ??= settings.registry;
45858
+ if (dirname13(current) === current)
45859
+ break;
45860
+ }
45861
+ const explicitUserConfig = process.env.npm_config_userconfig ?? process.env.NPM_CONFIG_USERCONFIG;
45862
+ const home = (process.platform === "win32" ? process.env.USERPROFILE : process.env.HOME) ?? homedir14();
45863
+ const userConfig = explicitUserConfig ?? join22(home, ".npmrc");
45864
+ if (!scoped && !visited.has(userConfig)) {
45865
+ const settings = await readRegistrySettings(userConfig, explicitUserConfig !== undefined);
45866
+ scoped = settings["@cortexkit:registry"];
45867
+ registry ??= settings.registry;
45868
+ }
45869
+ const selected = scoped ?? registry ?? NPM_REGISTRY_URL;
45870
+ return selected.replace(/\$\{([^}]+)\}/g, (_match, name) => {
45871
+ const value = process.env[name];
45872
+ if (value === undefined)
45873
+ throw new Error("Unresolved registry environment variable");
45874
+ return value;
45875
+ });
45876
+ }
45877
+ function validateRegistry(registry) {
45878
+ const url = new URL(registry);
45879
+ if (url.protocol !== "https:" && url.protocol !== "http:")
45880
+ throw new Error("Unsupported registry protocol");
45881
+ return registry;
45882
+ }
45883
+ var reportedRegistryFailures = new Set;
45884
+ function reportRegistryFailure(directory) {
45885
+ const key = resolve13(directory);
45886
+ if (reportedRegistryFailures.has(key))
45887
+ return;
45888
+ reportedRegistryFailures.add(key);
45889
+ debug2("[auto-update-checker] Configured npm registry could not be resolved or used; skipping update check.");
45890
+ }
45891
+ function resolveRegistry(directory) {
45892
+ const key = resolve13(directory);
45893
+ let pending = registryCache.get(key);
45894
+ if (!pending) {
45895
+ pending = (async () => {
45896
+ let registry;
45897
+ try {
45898
+ const scoped = configuredRegistry(await npmConfigGet("@cortexkit:registry", key));
45899
+ registry = scoped ?? configuredRegistry(await npmConfigGet("registry", key)) ?? NPM_REGISTRY_URL;
45900
+ } catch {
45901
+ registry = await registryWithoutNpm(key);
45902
+ }
45903
+ return validateRegistry(registry);
45904
+ })();
45905
+ registryCache.set(key, pending);
45906
+ }
45907
+ return pending;
45908
+ }
45156
45909
  function buildRegistryUrl(registryUrl) {
45157
45910
  return `${registryUrl.replace(/\/+$/, "")}/${encodeURIComponent(PACKAGE_NAME).replace("%2F", "/")}`;
45158
45911
  }
@@ -45164,17 +45917,24 @@ async function getLatestVersion(channel = "latest", options = {}) {
45164
45917
  try {
45165
45918
  if (options.signal?.aborted)
45166
45919
  return null;
45167
- const response = await fetch(buildRegistryUrl(options.registryUrl ?? NPM_REGISTRY_URL), {
45920
+ const registry = validateRegistry(options.registryUrl ?? await resolveRegistry(options.directory ?? process.cwd()));
45921
+ if (controller.signal.aborted)
45922
+ return null;
45923
+ const response = await fetch(buildRegistryUrl(registry), {
45168
45924
  signal: controller.signal,
45169
45925
  headers: { Accept: "application/json" }
45170
45926
  });
45171
- if (!response.ok)
45927
+ if (!response.ok) {
45928
+ reportRegistryFailure(options.directory ?? process.cwd());
45172
45929
  return null;
45930
+ }
45173
45931
  const data = NpmPackageEnvelopeSchema.safeParse(await response.json());
45174
45932
  if (!data.success)
45175
45933
  return null;
45176
45934
  return data.data["dist-tags"][channel] ?? data.data["dist-tags"].latest ?? null;
45177
45935
  } catch {
45936
+ if (!controller.signal.aborted)
45937
+ reportRegistryFailure(options.directory ?? process.cwd());
45178
45938
  return null;
45179
45939
  } finally {
45180
45940
  options.signal?.removeEventListener("abort", abortHandler);
@@ -45194,9 +45954,9 @@ function createAutoUpdateSnapshot(installDir, packageJsonPath, packageName) {
45194
45954
  cpSync(packageDir, stagedPackageDir, { recursive: true });
45195
45955
  return {
45196
45956
  packageJsonPath,
45197
- packageJson: existsSync20(packageJsonPath) ? readFileSync16(packageJsonPath, "utf-8") : null,
45957
+ packageJson: existsSync20(packageJsonPath) ? readFileSync17(packageJsonPath, "utf-8") : null,
45198
45958
  lockfilePath,
45199
- lockfile: existsSync20(lockfilePath) ? readFileSync16(lockfilePath, "utf-8") : null,
45959
+ lockfile: existsSync20(lockfilePath) ? readFileSync17(lockfilePath, "utf-8") : null,
45200
45960
  packageDir,
45201
45961
  stagedPackageDir,
45202
45962
  tempDir
@@ -45223,9 +45983,9 @@ function restoreAutoUpdateSnapshot(snapshot) {
45223
45983
  function stripPackageNameFromPath(pathValue, packageName) {
45224
45984
  let current = pathValue;
45225
45985
  for (const segment of [...packageName.split("/")].reverse()) {
45226
- if (basename5(current) !== segment)
45986
+ if (basename6(current) !== segment)
45227
45987
  return null;
45228
- current = dirname13(current);
45988
+ current = dirname14(current);
45229
45989
  }
45230
45990
  return current;
45231
45991
  }
@@ -45234,7 +45994,7 @@ function removeFromPackageLock(installDir, packageName) {
45234
45994
  if (!existsSync20(lockPath))
45235
45995
  return false;
45236
45996
  try {
45237
- const lock = import_comment_json3.parse(readFileSync16(lockPath, "utf-8"));
45997
+ const lock = import_comment_json3.parse(readFileSync17(lockPath, "utf-8"));
45238
45998
  let modified = false;
45239
45999
  if (lock.packages) {
45240
46000
  const key = `node_modules/${packageName}`;
@@ -45260,7 +46020,7 @@ function ensureDependencyVersion(packageJsonPath, packageName, version) {
45260
46020
  if (!existsSync20(packageJsonPath))
45261
46021
  return false;
45262
46022
  try {
45263
- const raw = import_comment_json3.parse(readFileSync16(packageJsonPath, "utf-8"));
46023
+ const raw = import_comment_json3.parse(readFileSync17(packageJsonPath, "utf-8"));
45264
46024
  const pkgJson = PackageJsonSchema.safeParse(raw);
45265
46025
  if (!pkgJson.success)
45266
46026
  return false;
@@ -45295,17 +46055,17 @@ function removeInstalledPackage(installDir, packageName) {
45295
46055
  }
45296
46056
  function resolveInstallContext(runtimePackageJsonPath = getCurrentRuntimePackageJsonPath()) {
45297
46057
  if (runtimePackageJsonPath) {
45298
- const packageDir = dirname13(runtimePackageJsonPath);
46058
+ const packageDir = dirname14(runtimePackageJsonPath);
45299
46059
  const nodeModulesDir = stripPackageNameFromPath(packageDir, PACKAGE_NAME);
45300
- if (nodeModulesDir && basename5(nodeModulesDir) === "node_modules") {
45301
- const installDir = dirname13(nodeModulesDir);
46060
+ if (nodeModulesDir && basename6(nodeModulesDir) === "node_modules") {
46061
+ const installDir = dirname14(nodeModulesDir);
45302
46062
  const packageJsonPath = join23(installDir, "package.json");
45303
46063
  if (existsSync20(packageJsonPath))
45304
46064
  return { installDir, packageJsonPath };
45305
46065
  }
45306
46066
  return null;
45307
46067
  }
45308
- const cacheRoot = dirname13(cacheDir());
46068
+ const cacheRoot = dirname14(cacheDir());
45309
46069
  const legacyPackageJsonPath = join23(cacheRoot, "package.json");
45310
46070
  if (existsSync20(legacyPackageJsonPath)) {
45311
46071
  return { installDir: cacheRoot, packageJsonPath: legacyPackageJsonPath };
@@ -45508,7 +46268,7 @@ function claimCheckSlot(storageDir, intervalMs) {
45508
46268
  try {
45509
46269
  if (hasRecentCheckTimestamp(file, intervalMs))
45510
46270
  return null;
45511
- mkdirSync9(dirname14(file), { recursive: true });
46271
+ mkdirSync9(dirname15(file), { recursive: true });
45512
46272
  const lockPath = `${file}.lock`;
45513
46273
  const owner = { pid: process.pid, startedMs: Date.now() };
45514
46274
  let lockFd = null;
@@ -45579,9 +46339,9 @@ function isOrphanedCheckLock(lockPath) {
45579
46339
  }
45580
46340
  }
45581
46341
  function readCheckLockState(lockPath) {
45582
- const fallbackStartedMs = statSync14(lockPath).mtimeMs;
46342
+ const fallbackStartedMs = statSync15(lockPath).mtimeMs;
45583
46343
  try {
45584
- const parsed = JSON.parse(readFileSync17(lockPath, "utf-8"));
46344
+ const parsed = JSON.parse(readFileSync18(lockPath, "utf-8"));
45585
46345
  const pid = Number.isSafeInteger(parsed.pid) && (parsed.pid ?? 0) > 0 ? parsed.pid ?? null : null;
45586
46346
  const startedMs = typeof parsed.startedMs === "number" && Number.isFinite(parsed.startedMs) ? parsed.startedMs : fallbackStartedMs;
45587
46347
  return { pid, startedMs };
@@ -45591,7 +46351,7 @@ function readCheckLockState(lockPath) {
45591
46351
  }
45592
46352
  function checkLockIsOwnedBy(lockPath, owner) {
45593
46353
  try {
45594
- const parsed = JSON.parse(readFileSync17(lockPath, "utf-8"));
46354
+ const parsed = JSON.parse(readFileSync18(lockPath, "utf-8"));
45595
46355
  return parsed.pid === owner.pid && parsed.startedMs === owner.startedMs;
45596
46356
  } catch {
45597
46357
  return false;
@@ -45601,7 +46361,7 @@ function hasRecentCheckTimestamp(file, intervalMs) {
45601
46361
  if (!existsSync21(file))
45602
46362
  return false;
45603
46363
  try {
45604
- const raw = JSON.parse(readFileSync17(file, "utf-8"));
46364
+ const raw = JSON.parse(readFileSync18(file, "utf-8"));
45605
46365
  const last = typeof raw.lastCheckedMs === "number" ? raw.lastCheckedMs : 0;
45606
46366
  return Number.isFinite(last) && Date.now() - last < intervalMs;
45607
46367
  } catch {
@@ -45657,6 +46417,7 @@ async function runBackgroundUpdateCheck(ctx, options) {
45657
46417
  const channel = extractChannel(pluginInfo.pinnedVersion ?? currentVersion);
45658
46418
  const latestVersion = await getLatestVersion(channel, {
45659
46419
  registryUrl: options.npmRegistryUrl,
46420
+ directory: ctx.directory,
45660
46421
  timeoutMs: options.fetchTimeoutMs,
45661
46422
  signal: options.signal
45662
46423
  });
@@ -45797,13 +46558,13 @@ import {
45797
46558
  existsSync as existsSync22,
45798
46559
  mkdirSync as mkdirSync10,
45799
46560
  readdirSync as readdirSync6,
45800
- readFileSync as readFileSync18,
46561
+ readFileSync as readFileSync19,
45801
46562
  renameSync as renameSync12,
45802
46563
  unlinkSync as unlinkSync8,
45803
46564
  writeFileSync as writeFileSync13
45804
46565
  } from "node:fs";
45805
46566
  import { createServer } from "node:http";
45806
- import { dirname as dirname15, join as join25 } from "node:path";
46567
+ import { dirname as dirname16, join as join25 } from "node:path";
45807
46568
 
45808
46569
  // src/shared/rpc-utils.ts
45809
46570
  import { join as join24 } from "node:path";
@@ -45953,7 +46714,7 @@ class AftRpcServer {
45953
46714
  }
45954
46715
  }
45955
46716
  writePortFile() {
45956
- const dir = dirname15(this.portFilePath);
46717
+ const dir = dirname16(this.portFilePath);
45957
46718
  mkdirSync10(dir, { recursive: true, mode: 448 });
45958
46719
  const tmpPath = `${this.portFilePath}.tmp`;
45959
46720
  writeFileSync13(tmpPath, JSON.stringify({
@@ -45978,7 +46739,7 @@ class AftRpcServer {
45978
46739
  if (filePath === this.portFilePath)
45979
46740
  continue;
45980
46741
  try {
45981
- const record = parseRpcPortRecord(readFileSync18(filePath, "utf-8"));
46742
+ const record = parseRpcPortRecord(readFileSync19(filePath, "utf-8"));
45982
46743
  if (record === null) {
45983
46744
  unlinkSync8(filePath);
45984
46745
  continue;
@@ -46193,7 +46954,7 @@ function formatSemanticIndexLabel(semantic) {
46193
46954
  backendUrl: semantic.backend_url
46194
46955
  });
46195
46956
  }
46196
- function asRecord4(value) {
46957
+ function asRecord3(value) {
46197
46958
  return typeof value === "object" && value !== null ? value : {};
46198
46959
  }
46199
46960
  function readString(value, fallback = "") {
@@ -46212,7 +46973,7 @@ function readOptionalNumber(value) {
46212
46973
  return typeof value === "number" && Number.isFinite(value) ? value : null;
46213
46974
  }
46214
46975
  function readCompressionAggregate(value) {
46215
- const aggregate = asRecord4(value);
46976
+ const aggregate = asRecord3(value);
46216
46977
  return {
46217
46978
  events: readNumber(aggregate.events),
46218
46979
  original_tokens: readNumber(aggregate.original_tokens),
@@ -46223,7 +46984,7 @@ function readCompressionAggregate(value) {
46223
46984
  function readCompression(value) {
46224
46985
  if (typeof value !== "object" || value === null)
46225
46986
  return;
46226
- const compression = asRecord4(value);
46987
+ const compression = asRecord3(value);
46227
46988
  return {
46228
46989
  project: readCompressionAggregate(compression.project),
46229
46990
  session: readCompressionAggregate(compression.session)
@@ -46232,7 +46993,7 @@ function readCompression(value) {
46232
46993
  function readStatusBar(value) {
46233
46994
  if (typeof value !== "object" || value === null)
46234
46995
  return;
46235
- const bar = asRecord4(value);
46996
+ const bar = asRecord3(value);
46236
46997
  const errors = readOptionalNumber(bar.errors);
46237
46998
  const warnings = readOptionalNumber(bar.warnings);
46238
46999
  const deadCode = readOptionalNumber(bar.dead_code);
@@ -46290,17 +47051,17 @@ function formatBytes(bytes) {
46290
47051
  return `${value.toFixed(decimals)} ${units[unitIndex]}`;
46291
47052
  }
46292
47053
  function coerceAftStatus(response) {
46293
- const features = asRecord4(response.features);
46294
- const searchIndex = asRecord4(response.search_index);
46295
- const semanticIndex = asRecord4(response.semantic_index);
47054
+ const features = asRecord3(response.features);
47055
+ const searchIndex = asRecord3(response.search_index);
47056
+ const semanticIndex = asRecord3(response.semantic_index);
46296
47057
  const semanticConfig = {
46297
- ...asRecord4(response.semantic),
46298
- ...asRecord4(response.semantic_config)
47058
+ ...asRecord3(response.semantic),
47059
+ ...asRecord3(response.semantic_config)
46299
47060
  };
46300
- const disk = asRecord4(response.disk);
46301
- const symbolCache = asRecord4(response.symbol_cache);
46302
- const runtime = asRecord4(response.runtime);
46303
- const session = asRecord4(response.session);
47061
+ const disk = asRecord3(response.disk);
47062
+ const symbolCache = asRecord3(response.symbol_cache);
47063
+ const runtime = asRecord3(response.runtime);
47064
+ const session = asRecord3(response.session);
46304
47065
  return {
46305
47066
  version: readString(response.version, "unknown"),
46306
47067
  project_root: readNullableString(response.project_root),
@@ -46841,6 +47602,16 @@ async function initializePluginForDirectory(input) {
46841
47602
  storageDir: configOverrides.storage_dir,
46842
47603
  isProjectEnabled
46843
47604
  };
47605
+ const liveConfigReload = startOpenCodeLiveConfigReload({
47606
+ directory: registrationRoot,
47607
+ initialSources: bootstrap.sources ?? [],
47608
+ initialSourceTexts: bootstrap.sourceTexts,
47609
+ getConfig: () => ctx.config,
47610
+ setConfig: (config) => {
47611
+ ctx.config = config;
47612
+ },
47613
+ notify: (message) => deliverConfigMigrationWarnings(registrationRoot, [message])
47614
+ });
46844
47615
  const statusSubscribedBridges = new WeakSet;
46845
47616
  const statusUnsubscribes = new Set;
46846
47617
  const subscribeBridgeStatus = (bridge) => {
@@ -46878,6 +47649,7 @@ async function initializePluginForDirectory(input) {
46878
47649
  let clearInspectTier2Idle = () => {};
46879
47650
  const shutdownCleanup = registerShutdownCleanup(async (reason) => {
46880
47651
  autoUpdateAbort.abort();
47652
+ liveConfigReload.stop();
46881
47653
  clearInspectTier2Idle();
46882
47654
  for (const unsubscribe of statusUnsubscribes) {
46883
47655
  try {
@@ -47036,7 +47808,7 @@ Install: ${getManualInstallHint()}`);
47036
47808
  }
47037
47809
  const inspectTier2Idle = createInspectTier2IdleScheduler({
47038
47810
  isEnabled: () => registeredTools.has("aft_inspect"),
47039
- idleMinutes: () => aftConfig.inspect?.tier2_idle_minutes,
47811
+ idleMinutes: () => ctx.config.inspect?.tier2_idle_minutes,
47040
47812
  warn: warn2,
47041
47813
  run: async (sessionID) => {
47042
47814
  const sessionDir = await getSessionDirectory(input.client, sessionID, input.directory) ?? input.directory;
@@ -47094,11 +47866,17 @@ Install: ${getManualInstallHint()}`);
47094
47866
  storageDir: configOverrides.storage_dir,
47095
47867
  pluginVersion: PLUGIN_VERSION,
47096
47868
  serverUrl: input.serverUrl?.toString(),
47097
- delivery: aftConfig.configure_warnings_delivery ?? "toast"
47869
+ delivery: ctx.config.configure_warnings_delivery ?? "toast"
47098
47870
  });
47099
47871
  }
47100
47872
  await flushConfigureWarningsOnIdle(sessionID);
47101
47873
  },
47874
+ "chat.params": async (params) => {
47875
+ rememberReadModel(input.client, params.sessionID, {
47876
+ providerID: params.model.providerID,
47877
+ modelID: params.model.id
47878
+ });
47879
+ },
47102
47880
  "chat.message": async (messageInput, messageOutput) => {
47103
47881
  const sid = messageInput.sessionID ?? messageInput.sessionId ?? messageInput.id;
47104
47882
  warmSessionDirectory(input.client, sid, input.directory);