@adhdev/daemon-core 0.9.82-rc.456 → 0.9.82-rc.458

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.mjs CHANGED
@@ -404,10 +404,10 @@ function readInjected(value) {
404
404
  }
405
405
  function getDaemonBuildInfo() {
406
406
  if (cached) return cached;
407
- const commit = readInjected(true ? "49032ec6c48a0cbe6f122e2615088b38407712b6" : void 0) ?? "unknown";
408
- const commitShort = readInjected(true ? "49032ec6" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
409
- const version = readInjected(true ? "0.9.82-rc.456" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
410
- const builtAt = readInjected(true ? "2026-07-04T07:05:38.214Z" : void 0);
407
+ const commit = readInjected(true ? "690e14cc1bdb099a0c52d7fffef41329cc5b15b5" : void 0) ?? "unknown";
408
+ const commitShort = readInjected(true ? "690e14cc" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
409
+ const version = readInjected(true ? "0.9.82-rc.458" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
410
+ const builtAt = readInjected(true ? "2026-07-04T09:53:14.279Z" : void 0);
411
411
  cached = builtAt ? { commit, commitShort, version, builtAt } : { commit, commitShort, version };
412
412
  return cached;
413
413
  }
@@ -1585,6 +1585,8 @@ function normalizeConfig(raw) {
1585
1585
  ideSettings: isPlainObject(parsed.ideSettings) ? parsed.ideSettings : {},
1586
1586
  providerSourceMode: resolveProviderSourceMode(parsed.providerSourceMode, parsed.disableUpstream),
1587
1587
  providerDir: asOptionalString(parsed.providerDir),
1588
+ registryUrl: asOptionalString(parsed.registryUrl),
1589
+ providerTarballUrl: asOptionalString(parsed.providerTarballUrl),
1588
1590
  updateChannel: parsed.updateChannel === "preview" ? "preview" : "stable",
1589
1591
  terminalSizingMode: parsed.terminalSizingMode === "fit" ? "fit" : "measured"
1590
1592
  };
@@ -3699,7 +3701,7 @@ function buildRulesSection(coordinatorCliType) {
3699
3701
  - **Verify via git, not source.** Use \`mesh_git_status\` to confirm side effects. Treat agent summaries as self-reports, not verification.
3700
3702
  - **Limit parallelism.** Start with 1\u20132 tasks; scale only on success. Never duplicate a session because \`mesh_read_chat\` shows no final message while tool/terminal activity is ongoing. This caps *concurrent* load \u2014 it does not mean serialize independent work: when a new, independent request arrives and there is headroom under \`maxParallelTasks\`, dispatch it right away rather than waiting for an in-flight task or a user nudge (read-only diagnosis especially, since it has no merge cost).
3701
3703
  - **Check history first.** Call \`mesh_task_history\` at session start to avoid duplicate work and inform recovery. On failure, read task history before retrying.
3702
- - **Sequence shared-base-moving merges.** Parallel dispatch is encouraged, but merging one worktree can advance another in-flight worktree's base \u2014 especially the oss submodule pointer \u2014 turning a clean fast-forward into a diverged rebase (patch-equivalence correctly blocks this). Before merging an in-flight worktree while siblings are also in flight, land in an intentional order, re-clone long-running worktrees from the advanced base, or expect to manually rebase + ff-only the laggards; merging an independent fix mid-flight can strand siblings into a rebase.
3704
+ - **Sequence shared-base-moving merges.** Parallel dispatch is encouraged, but merging one worktree can advance another in-flight worktree's base \u2014 especially a shared submodule pointer \u2014 turning a clean fast-forward into a diverged rebase (patch-equivalence correctly blocks this). Before merging an in-flight worktree while siblings are also in flight, land in an intentional order, re-clone long-running worktrees from the advanced base, or expect to manually rebase + ff-only the laggards; merging an independent fix mid-flight can strand siblings into a rebase.
3703
3705
  - **Converge branches.** After worktree tasks: refine/fast-forward, or classify as \`pushed_feature_branch_needs_merge\` / \`blocked_review\` / \`cleanup_candidate\` / \`not_mergeable\`. Clean up with \`mesh_remove_node\`.
3704
3706
  - **Refinery is config-driven.** \`mesh_refine_node\` must run validation from \`.adhdev/refine.{json,yaml,yml}\` or \`repo-mesh.refine.*\`. Heuristics are scaffolding only.
3705
3707
  - **Submodule reachability = publish-needed.** \`submodule_reachability_failed\` \u2192 classify as \`blocked_review\`, request user approval to push to submodule main, then rerun \`mesh_refine_node\`.
@@ -8922,11 +8924,15 @@ var worktree_bootstrap_config_exports = {};
8922
8924
  __export(worktree_bootstrap_config_exports, {
8923
8925
  MESH_WORKTREE_BOOTSTRAP_CONFIG_LOCATIONS: () => MESH_WORKTREE_BOOTSTRAP_CONFIG_LOCATIONS,
8924
8926
  MESH_WORKTREE_BOOTSTRAP_CONFIG_SCHEMA: () => MESH_WORKTREE_BOOTSTRAP_CONFIG_SCHEMA,
8927
+ SUBMODULE_DEFAULT_BRANCH_FALLBACK: () => SUBMODULE_DEFAULT_BRANCH_FALLBACK,
8925
8928
  WORKTREE_BOOTSTRAP_STALE_RUNNING_MS: () => WORKTREE_BOOTSTRAP_STALE_RUNNING_MS,
8926
8929
  computeStaleInputsDigest: () => computeStaleInputsDigest,
8927
8930
  evaluateWorktreeBootstrapState: () => evaluateWorktreeBootstrapState,
8931
+ getRegisteredSubmodulePaths: () => getRegisteredSubmodulePaths,
8932
+ getSubmoduleConfiguredBranches: () => getSubmoduleConfiguredBranches,
8928
8933
  isWorktreeBootstrapStaleRunning: () => isWorktreeBootstrapStaleRunning,
8929
8934
  loadMeshWorktreeBootstrapConfig: () => loadMeshWorktreeBootstrapConfig,
8935
+ resolveSubmoduleDefaultBranch: () => resolveSubmoduleDefaultBranch,
8930
8936
  runMeshWorktreeBootstrap: () => runMeshWorktreeBootstrap,
8931
8937
  shouldDeferDispatchForBootstrap: () => shouldDeferDispatchForBootstrap,
8932
8938
  validateMeshWorktreeBootstrapConfig: () => validateMeshWorktreeBootstrapConfig
@@ -8957,6 +8963,81 @@ function getRegisteredSubmodulePaths(workspace) {
8957
8963
  }
8958
8964
  return paths;
8959
8965
  }
8966
+ function getSubmoduleConfiguredBranches(workspace) {
8967
+ const branchesByPath = /* @__PURE__ */ new Map();
8968
+ try {
8969
+ const out = execFileSync2(
8970
+ resolveWin32Executable("git"),
8971
+ ["config", "--file", ".gitmodules", "--list"],
8972
+ { cwd: workspace, encoding: "utf8", timeout: 1e4, windowsHide: true }
8973
+ );
8974
+ const pathByName = /* @__PURE__ */ new Map();
8975
+ const branchByName = /* @__PURE__ */ new Map();
8976
+ for (const line of String(out).split(/\r?\n/)) {
8977
+ const trimmed = line.trim();
8978
+ if (!trimmed) continue;
8979
+ const eq = trimmed.indexOf("=");
8980
+ if (eq < 0) continue;
8981
+ const key2 = trimmed.slice(0, eq);
8982
+ const value = trimmed.slice(eq + 1).trim();
8983
+ const match = /^submodule\.(.+)\.(path|branch)$/.exec(key2);
8984
+ if (!match) continue;
8985
+ const name = match[1];
8986
+ if (match[2] === "path") {
8987
+ const norm = value.replace(/\\/g, "/").replace(/\/+$/, "");
8988
+ if (norm) pathByName.set(name, norm);
8989
+ } else if (value) {
8990
+ branchByName.set(name, value);
8991
+ }
8992
+ }
8993
+ for (const [name, submodulePath] of pathByName) {
8994
+ const branch = branchByName.get(name);
8995
+ if (branch && branch !== ".") branchesByPath.set(submodulePath, branch);
8996
+ }
8997
+ } catch {
8998
+ }
8999
+ return branchesByPath;
9000
+ }
9001
+ function isPlausibleBranchName(name) {
9002
+ return typeof name === "string" && name.length > 0 && !/\s/.test(name) && name !== "HEAD";
9003
+ }
9004
+ async function resolveSubmoduleDefaultBranch(opts) {
9005
+ const remote = opts.remote?.trim() || "origin";
9006
+ const localTimeout = opts.timeoutMs ?? 1e4;
9007
+ const git = resolveWin32Executable("git");
9008
+ const execFileAsync4 = promisify3(execFile3);
9009
+ if (opts.superprojectWorkspace && opts.submodulePath) {
9010
+ try {
9011
+ const normalized = opts.submodulePath.replace(/\\/g, "/").replace(/\/+$/, "");
9012
+ const configured = getSubmoduleConfiguredBranches(opts.superprojectWorkspace).get(normalized);
9013
+ if (isPlausibleBranchName(configured)) return configured;
9014
+ } catch {
9015
+ }
9016
+ }
9017
+ try {
9018
+ const { stdout } = await execFileAsync4(
9019
+ git,
9020
+ ["symbolic-ref", "--short", `refs/remotes/${remote}/HEAD`],
9021
+ { cwd: opts.submoduleRepoPath, encoding: "utf8", timeout: localTimeout, windowsHide: true }
9022
+ );
9023
+ const short = String(stdout || "").trim();
9024
+ const prefix = `${remote}/`;
9025
+ const branch = short.startsWith(prefix) ? short.slice(prefix.length) : short;
9026
+ if (isPlausibleBranchName(branch)) return branch;
9027
+ } catch {
9028
+ }
9029
+ try {
9030
+ const { stdout } = await execFileAsync4(
9031
+ git,
9032
+ ["ls-remote", "--symref", remote, "HEAD"],
9033
+ { cwd: opts.submoduleRepoPath, encoding: "utf8", timeout: Math.max(localTimeout, 3e4), windowsHide: true }
9034
+ );
9035
+ const match = /^ref:\s+refs\/heads\/(\S+)\s+HEAD/m.exec(String(stdout || ""));
9036
+ if (match && isPlausibleBranchName(match[1])) return match[1];
9037
+ } catch {
9038
+ }
9039
+ return SUBMODULE_DEFAULT_BRANCH_FALLBACK;
9040
+ }
8960
9041
  function isCleanIgnoringSubmoduleGitlinks(porcelain, submodulePaths) {
8961
9042
  const lines = porcelain.split(/\r?\n/).filter((line) => line.length > 0);
8962
9043
  for (const line of lines) {
@@ -9199,13 +9280,14 @@ async function runMeshWorktreeBootstrap(mesh, workspace) {
9199
9280
  }
9200
9281
  return state;
9201
9282
  }
9202
- var WORKTREE_BOOTSTRAP_STALE_RUNNING_MS, MESH_WORKTREE_BOOTSTRAP_CONFIG_LOCATIONS, MESH_WORKTREE_BOOTSTRAP_CONFIG_SCHEMA, DEFAULT_TIMEOUT_MS2, DEFAULT_OUTPUT_LIMIT_BYTES, OUTPUT_SUMMARY_CHARS;
9283
+ var WORKTREE_BOOTSTRAP_STALE_RUNNING_MS, SUBMODULE_DEFAULT_BRANCH_FALLBACK, MESH_WORKTREE_BOOTSTRAP_CONFIG_LOCATIONS, MESH_WORKTREE_BOOTSTRAP_CONFIG_SCHEMA, DEFAULT_TIMEOUT_MS2, DEFAULT_OUTPUT_LIMIT_BYTES, OUTPUT_SUMMARY_CHARS;
9203
9284
  var init_worktree_bootstrap_config = __esm({
9204
9285
  "src/mesh/worktree-bootstrap-config.ts"() {
9205
9286
  "use strict";
9206
9287
  init_resolve_executable();
9207
9288
  init_refine_config();
9208
9289
  WORKTREE_BOOTSTRAP_STALE_RUNNING_MS = 10 * 60 * 1e3;
9290
+ SUBMODULE_DEFAULT_BRANCH_FALLBACK = "main";
9209
9291
  MESH_WORKTREE_BOOTSTRAP_CONFIG_LOCATIONS = [
9210
9292
  ".adhdev/worktree_bootstrap.json",
9211
9293
  ".adhdev/worktree_bootstrap.yaml",
@@ -9822,15 +9904,24 @@ async function resolveSubmodulePushes(status, options, execute, timeoutMs) {
9822
9904
  results.push({ ...base, code: "submodule_status_incomplete" });
9823
9905
  continue;
9824
9906
  }
9907
+ const remoteBranch = await resolveSubmoduleDefaultBranch({
9908
+ submoduleRepoPath: repoPath,
9909
+ superprojectWorkspace: status.repoRoot ?? status.workspace,
9910
+ submodulePath: submodule.path,
9911
+ timeoutMs
9912
+ });
9913
+ base.remoteBranch = remoteBranch;
9914
+ const remoteRef = `refs/remotes/origin/${remoteBranch}`;
9915
+ const fetchRefspec = `refs/heads/${remoteBranch}:${remoteRef}`;
9825
9916
  try {
9826
- await runGit(repoPath, ["-c", "protocol.file.allow=always", "fetch", "origin", "refs/heads/main:refs/remotes/origin/main"], { timeoutMs: timeoutMs ?? 3e4 });
9917
+ await runGit(repoPath, ["-c", "protocol.file.allow=always", "fetch", "origin", fetchRefspec], { timeoutMs: timeoutMs ?? 3e4 });
9827
9918
  } catch (error) {
9828
9919
  results.push({ ...base, code: "submodule_fetch_failed", error: formatGitError2(error) });
9829
9920
  continue;
9830
9921
  }
9831
9922
  let alreadyReachable = false;
9832
9923
  try {
9833
- await runGit(repoPath, ["merge-base", "--is-ancestor", submodule.commit, "refs/remotes/origin/main"], { timeoutMs: timeoutMs ?? 15e3 });
9924
+ await runGit(repoPath, ["merge-base", "--is-ancestor", submodule.commit, remoteRef], { timeoutMs: timeoutMs ?? 15e3 });
9834
9925
  alreadyReachable = true;
9835
9926
  } catch {
9836
9927
  }
@@ -9839,20 +9930,20 @@ async function resolveSubmodulePushes(status, options, execute, timeoutMs) {
9839
9930
  continue;
9840
9931
  }
9841
9932
  try {
9842
- await runGit(repoPath, ["merge-base", "--is-ancestor", "refs/remotes/origin/main", submodule.commit], { timeoutMs: timeoutMs ?? 15e3 });
9933
+ await runGit(repoPath, ["merge-base", "--is-ancestor", remoteRef, submodule.commit], { timeoutMs: timeoutMs ?? 15e3 });
9843
9934
  } catch (error) {
9844
9935
  results.push({ ...base, pushed: false, skipped: false, code: "submodule_non_fast_forward", error: formatGitError2(error) });
9845
9936
  continue;
9846
9937
  }
9847
- const refspec = `${submodule.commit}:refs/heads/main`;
9938
+ const refspec = `${submodule.commit}:refs/heads/${remoteBranch}`;
9848
9939
  if (!execute) {
9849
9940
  results.push({ ...base, pushed: false, skipped: false, code: "submodule_push_available", refspec });
9850
9941
  continue;
9851
9942
  }
9852
9943
  try {
9853
9944
  await runGit(repoPath, ["push", "origin", refspec], { timeoutMs: timeoutMs ?? 3e4 });
9854
- await runGit(repoPath, ["-c", "protocol.file.allow=always", "fetch", "origin", "refs/heads/main:refs/remotes/origin/main"], { timeoutMs: timeoutMs ?? 3e4 });
9855
- await runGit(repoPath, ["merge-base", "--is-ancestor", submodule.commit, "refs/remotes/origin/main"], { timeoutMs: timeoutMs ?? 15e3 });
9945
+ await runGit(repoPath, ["-c", "protocol.file.allow=always", "fetch", "origin", fetchRefspec], { timeoutMs: timeoutMs ?? 3e4 });
9946
+ await runGit(repoPath, ["merge-base", "--is-ancestor", submodule.commit, remoteRef], { timeoutMs: timeoutMs ?? 15e3 });
9856
9947
  results.push({ ...base, pushed: true, skipped: false, code: "submodule_pushed", refspec });
9857
9948
  } catch (error) {
9858
9949
  results.push({ ...base, pushed: false, skipped: false, code: "submodule_push_failed", refspec, error: formatGitError2(error) });
@@ -10120,6 +10211,7 @@ var init_mesh_fast_forward = __esm({
10120
10211
  "use strict";
10121
10212
  init_git_status();
10122
10213
  init_git_executor();
10214
+ init_worktree_bootstrap_config();
10123
10215
  STATUS_OPTIONS = { refreshUpstream: true, includeSubmodules: true, timeoutMs: 15e3, forceFresh: true };
10124
10216
  }
10125
10217
  });
@@ -18495,6 +18587,9 @@ var init_approval_utils = __esm({
18495
18587
  });
18496
18588
 
18497
18589
  // src/logging/debug-config.ts
18590
+ function isAlwaysOnTraceCategory(category) {
18591
+ return !!category && ALWAYS_ON_TRACE_CATEGORIES.includes(category);
18592
+ }
18498
18593
  function normalizeCategories(categories) {
18499
18594
  if (!Array.isArray(categories)) return [];
18500
18595
  return categories.map((category) => String(category || "").trim()).filter(Boolean);
@@ -18524,17 +18619,19 @@ function resetDebugRuntimeConfig() {
18524
18619
  }
18525
18620
  function shouldCollectTraceCategory(category) {
18526
18621
  const config = currentConfig;
18622
+ if (isAlwaysOnTraceCategory(category)) return true;
18527
18623
  if (!config.collectDebugTrace) return false;
18528
18624
  if (!category) return true;
18529
18625
  if (config.traceCategories.length === 0) return true;
18530
18626
  return config.traceCategories.includes(category);
18531
18627
  }
18532
- var NORMAL_TRACE_BUFFER_SIZE, DEV_TRACE_BUFFER_SIZE, DEFAULT_CONFIG2, currentConfig;
18628
+ var NORMAL_TRACE_BUFFER_SIZE, DEV_TRACE_BUFFER_SIZE, ALWAYS_ON_TRACE_CATEGORIES, DEFAULT_CONFIG2, currentConfig;
18533
18629
  var init_debug_config = __esm({
18534
18630
  "src/logging/debug-config.ts"() {
18535
18631
  "use strict";
18536
18632
  NORMAL_TRACE_BUFFER_SIZE = 200;
18537
18633
  DEV_TRACE_BUFFER_SIZE = 1e3;
18634
+ ALWAYS_ON_TRACE_CATEGORIES = ["completion-gate", "fsm-transition"];
18538
18635
  DEFAULT_CONFIG2 = {
18539
18636
  logLevel: "info",
18540
18637
  collectDebugTrace: false,
@@ -30282,6 +30379,35 @@ var DaemonCdpInitializer = class {
30282
30379
 
30283
30380
  // src/commands/handler.ts
30284
30381
  init_builders();
30382
+ init_config();
30383
+
30384
+ // src/config/registry-resolver.ts
30385
+ var DEFAULT_REGISTRY_BASE_URL = "https://api.adhf.dev/api/v1/registry";
30386
+ var DEFAULT_PROVIDER_TARBALL_URL = "https://github.com/vilmire/adhdev-providers/archive/refs/heads/main.tar.gz";
30387
+ var REGISTRY_URL_ENV_VAR = "ADHDEV_REGISTRY_URL";
30388
+ var PROVIDER_TARBALL_URL_ENV_VAR = "ADHDEV_PROVIDER_TARBALL_URL";
30389
+ function cleanString(value) {
30390
+ return typeof value === "string" && value.trim() ? value.trim() : void 0;
30391
+ }
30392
+ function stripTrailingSlashes(url) {
30393
+ return url.replace(/\/+$/, "");
30394
+ }
30395
+ function resolveRegistryBaseUrl(configuredUrl, env = process.env) {
30396
+ const resolved = cleanString(configuredUrl) ?? cleanString(env[REGISTRY_URL_ENV_VAR]) ?? DEFAULT_REGISTRY_BASE_URL;
30397
+ return stripTrailingSlashes(resolved);
30398
+ }
30399
+ function resolveProviderTarballUrl(configuredUrl, env = process.env) {
30400
+ return cleanString(configuredUrl) ?? cleanString(env[PROVIDER_TARBALL_URL_ENV_VAR]) ?? DEFAULT_PROVIDER_TARBALL_URL;
30401
+ }
30402
+ function resolveProviderTarballTarget(configuredUrl, env = process.env) {
30403
+ const url = resolveProviderTarballUrl(configuredUrl, env);
30404
+ const parsed = new URL(url);
30405
+ return {
30406
+ url,
30407
+ hostname: parsed.hostname,
30408
+ path: parsed.pathname + (parsed.search || "")
30409
+ };
30410
+ }
30285
30411
 
30286
30412
  // src/sessions/reconcile.ts
30287
30413
  function upsertSessionTarget(sessionRegistry, target) {
@@ -30597,7 +30723,7 @@ function createDebugTraceStore(options) {
30597
30723
  const capacity = Math.max(1, Math.floor(options.capacity || 100));
30598
30724
  return {
30599
30725
  record(event) {
30600
- if (!options.enabled) return null;
30726
+ if (!options.enabled && !isAlwaysOnTraceCategory(event.category)) return null;
30601
30727
  const entry = createEntry(event);
30602
30728
  entries.push(entry);
30603
30729
  if (entries.length > capacity) {
@@ -35179,7 +35305,7 @@ var DaemonCommandHandler = class {
35179
35305
  const https = __require("https");
35180
35306
  const fs40 = __require("fs");
35181
35307
  const path44 = __require("path");
35182
- const REGISTRY = "https://api.adhf.dev/api/v1/registry";
35308
+ const REGISTRY = resolveRegistryBaseUrl(loadConfig().registryUrl);
35183
35309
  function fetchText(url, timeoutMs) {
35184
35310
  return new Promise((resolve25, reject) => {
35185
35311
  const req = https.get(url, { headers: { "User-Agent": "adhdev-daemon", "Accept": "application/json" }, timeout: timeoutMs }, (res) => {
@@ -35541,7 +35667,7 @@ ${formatManifestValidationIssues2(validation.issues)}`,
35541
35667
  const installed = this.handleListInstalledProviders({});
35542
35668
  if (!installed.success) return installed;
35543
35669
  const https = __require("https");
35544
- const REGISTRY = "https://api.adhf.dev/api/v1/registry";
35670
+ const REGISTRY = resolveRegistryBaseUrl(loadConfig().registryUrl);
35545
35671
  function fetchJson(url) {
35546
35672
  return new Promise((resolve25, reject) => {
35547
35673
  const req = https.get(url, { headers: { "User-Agent": "adhdev-daemon", "Accept": "application/json" }, timeout: 1e4 }, (res) => {
@@ -37144,6 +37270,7 @@ var CHANNEL_SERVER_URL = {
37144
37270
  stable: "https://api.adhf.dev",
37145
37271
  preview: "https://api-preview.adhf.dev"
37146
37272
  };
37273
+ var VENDOR_SERVER_URLS = new Set(Object.values(CHANNEL_SERVER_URL));
37147
37274
  function normalizeReleaseChannel(value) {
37148
37275
  if (typeof value !== "string") return null;
37149
37276
  const normalized = value.trim().toLowerCase();
@@ -37165,7 +37292,11 @@ var daemonLifecycleHandlers = {
37165
37292
  const npmTag = CHANNEL_NPM_TAG[channel];
37166
37293
  const latest = String(execNpmCommandSync(["view", `${pkgName}@${npmTag}`, "version"], { encoding: "utf-8", timeout: 1e4 }, npmSurface)).trim();
37167
37294
  LOG.info("Upgrade", `Latest ${pkgName}@${npmTag}: v${latest}`);
37168
- updateConfig({ updateChannel: channel, serverUrl: CHANNEL_SERVER_URL[channel] });
37295
+ const currentServerUrl = typeof loadConfig().serverUrl === "string" ? loadConfig().serverUrl.trim() : "";
37296
+ const useVendorServerUrl = currentServerUrl === "" || VENDOR_SERVER_URLS.has(currentServerUrl);
37297
+ updateConfig(
37298
+ useVendorServerUrl ? { updateChannel: channel, serverUrl: CHANNEL_SERVER_URL[channel] } : { updateChannel: channel }
37299
+ );
37169
37300
  let currentInstalled = null;
37170
37301
  try {
37171
37302
  const currentJson = String(execNpmCommandSync(["ls", "-g", pkgName, "--depth=0", "--json"], {
@@ -37873,6 +38004,7 @@ function applyPreLaunchTrust(trust, workingDir) {
37873
38004
 
37874
38005
  // src/providers/spec/fsm-driver.ts
37875
38006
  init_logger();
38007
+ init_debug_config();
37876
38008
  init_pty_write_chunking();
37877
38009
  function countNewlines(s2) {
37878
38010
  let n = 0;
@@ -37941,6 +38073,11 @@ var FsmDriver = class {
37941
38073
  * (−1 = whole screen), or a `section:<id>` / `<region>#ignore:<pat>` string
37942
38074
  * when the clause scopes to a section or declares an ignore_lines filter. */
37943
38075
  regionLastChangedAt = /* @__PURE__ */ new Map();
38076
+ /** COMPLETION-EARLYNOTIFY stable-eval trace: last stable/not-stable verdict
38077
+ * recorded per stable region, so the trace fires only when the verdict FLIPS
38078
+ * (not every quiet frame). Cleared on every transition alongside
38079
+ * regionLastChangedAt. Diagnostic-only — never consulted by the FSM. */
38080
+ stableVerdictCache = /* @__PURE__ */ new Map();
37944
38081
  /** Timer that re-runs evaluate() when a time-condition would flip true
37945
38082
  * with no PTY frame to trigger it. */
37946
38083
  wakeTimer = null;
@@ -38276,6 +38413,7 @@ var FsmDriver = class {
38276
38413
  this.currentStateId = fired.to;
38277
38414
  this.stateEnteredAt = now;
38278
38415
  this.regionLastChangedAt.clear();
38416
+ this.stableVerdictCache.clear();
38279
38417
  this.pushHistory(fired.to, stateById(this.spec, fired.to)?.label ?? fired.to, {
38280
38418
  reason: "transition",
38281
38419
  via: `${from}\u2192${fired.to}`,
@@ -38380,6 +38518,7 @@ var FsmDriver = class {
38380
38518
  trackRegionChanges(currentLines, cursor, now) {
38381
38519
  if (this.prevScreenLines.length === 0) return;
38382
38520
  const descs = this.stableRegionDescriptors();
38521
+ const stableTraceOn = shouldCollectTraceCategory("fsm-transition");
38383
38522
  const needsSections = descs.some((d) => !!d.section);
38384
38523
  const curSections = needsSections ? resolveSections(this.spec.sections ?? {}, currentLines) : [];
38385
38524
  const prevSections = needsSections ? resolveSections(this.spec.sections ?? {}, this.prevScreenLines) : [];
@@ -38400,6 +38539,28 @@ var FsmDriver = class {
38400
38539
  const cur = filterIgnoredLines(curLines, d.ignoreRe).join("\n");
38401
38540
  const prev = filterIgnoredLines(prevLines, d.ignoreRe).join("\n");
38402
38541
  if (cur !== prev) this.regionLastChangedAt.set(d.key, now);
38542
+ if (stableTraceOn && typeof d.holdMs === "number") {
38543
+ const lastChanged = this.regionLastChangedAt.get(d.key) ?? this.stateEnteredAt;
38544
+ const ageMs = now - lastChanged;
38545
+ const verdict = ageMs >= d.holdMs;
38546
+ if (this.stableVerdictCache.get(d.key) !== verdict) {
38547
+ this.stableVerdictCache.set(d.key, verdict);
38548
+ recordDebugTrace({
38549
+ category: "fsm-transition",
38550
+ stage: "stable-eval",
38551
+ level: "debug",
38552
+ payload: {
38553
+ state: this.currentStateId,
38554
+ regionKey: String(d.key),
38555
+ ignorePattern: d.ignoreRe?.source ?? null,
38556
+ fingerprintLen: cur.length,
38557
+ ageMs,
38558
+ holdMs: d.holdMs,
38559
+ verdict
38560
+ }
38561
+ });
38562
+ }
38563
+ }
38403
38564
  }
38404
38565
  }
38405
38566
  /** Every distinct stable-region descriptor referenced by stable_ms
@@ -38915,7 +39076,8 @@ function collectStableDescriptors(when, byKey) {
38915
39076
  const w = when;
38916
39077
  if ("stable_ms" in w) {
38917
39078
  const key2 = stableRegionKey(w);
38918
- if (!byKey.has(key2)) {
39079
+ const existing = byKey.get(key2);
39080
+ if (!existing) {
38919
39081
  let ignoreRe;
38920
39082
  if (w.ignore_lines) {
38921
39083
  try {
@@ -38923,7 +39085,9 @@ function collectStableDescriptors(when, byKey) {
38923
39085
  } catch {
38924
39086
  }
38925
39087
  }
38926
- byKey.set(key2, { key: key2, section: w.section, cursor_above: w.cursor_above, ignoreRe });
39088
+ byKey.set(key2, { key: key2, section: w.section, cursor_above: w.cursor_above, ignoreRe, holdMs: typeof w.stable_ms === "number" ? w.stable_ms : void 0 });
39089
+ } else if (existing.holdMs === void 0 && typeof w.stable_ms === "number") {
39090
+ existing.holdMs = w.stable_ms;
38927
39091
  }
38928
39092
  return;
38929
39093
  }
@@ -40695,6 +40859,7 @@ function createCliAdapter(provider, workingDir, cliArgs, extraEnv, transportFact
40695
40859
 
40696
40860
  // src/providers/cli-provider-instance.ts
40697
40861
  init_logger();
40862
+ init_debug_config();
40698
40863
  init_mesh_event_trace();
40699
40864
  init_control_effects();
40700
40865
  init_approval_utils();
@@ -41768,9 +41933,10 @@ var CliProviderInstance = class _CliProviderInstance {
41768
41933
  return restoredHistory.messages;
41769
41934
  }
41770
41935
  completionFinalAssistantEvidence(parsedMessages, turnStartedAt) {
41936
+ const turnClosed = !this.hasAdapterPendingResponse();
41771
41937
  if (this.completionHasFinalAssistantMessage(parsedMessages, turnStartedAt)) {
41772
41938
  return {
41773
- present: true,
41939
+ present: turnClosed,
41774
41940
  messages: Array.isArray(parsedMessages) ? parsedMessages : [],
41775
41941
  source: "parsed"
41776
41942
  };
@@ -41778,7 +41944,7 @@ var CliProviderInstance = class _CliProviderInstance {
41778
41944
  const externalMessages = this.readExternalCompletionMessages();
41779
41945
  if (externalMessages) {
41780
41946
  return {
41781
- present: this.completionHasFinalAssistantMessage(externalMessages, turnStartedAt),
41947
+ present: turnClosed && this.completionHasFinalAssistantMessage(externalMessages, turnStartedAt),
41782
41948
  messages: externalMessages,
41783
41949
  source: "external-native"
41784
41950
  };
@@ -41877,11 +42043,9 @@ var CliProviderInstance = class _CliProviderInstance {
41877
42043
  if (latestVisibleStatus !== "idle") return { reason: `status:${latestVisibleStatus}`, terminal: true };
41878
42044
  const adapterAny = this.adapter;
41879
42045
  const approvalResolvedIdle = pending.previousStatus === "waiting_approval";
41880
- if (!approvalResolvedIdle) {
41881
- if (adapterAny?.isWaitingForResponse === true) return { reason: "adapter_waiting_for_response", terminal: true };
41882
- if (adapterAny?.currentTurnScope) return { reason: "adapter_turn_scope_active", terminal: true };
41883
- if (this.hasAdapterPendingResponse()) return { reason: "adapter_pending_response", terminal: true };
41884
- }
42046
+ if (adapterAny?.isWaitingForResponse === true) return { reason: "adapter_waiting_for_response", terminal: !approvalResolvedIdle };
42047
+ if (adapterAny?.currentTurnScope) return { reason: "adapter_turn_scope_active", terminal: !approvalResolvedIdle };
42048
+ if (this.hasAdapterPendingResponse()) return { reason: "adapter_pending_response", terminal: !approvalResolvedIdle };
41885
42049
  const partial = typeof this.adapter.getPartialResponse === "function" ? this.adapter.getPartialResponse() : "";
41886
42050
  if (typeof partial === "string" && partial.trim()) return { reason: "partial_response_pending", terminal: true };
41887
42051
  let parsed;
@@ -42036,6 +42200,39 @@ var CliProviderInstance = class _CliProviderInstance {
42036
42200
  event
42037
42201
  };
42038
42202
  }
42203
+ // COMPLETION-EARLYNOTIFY instrumentation. A session-keyed FSM-transition +
42204
+ // completion-gate snapshot recorded into the shared debug-trace ring buffer
42205
+ // (secret-safe, length/role/pattern-name only — never screen or bubble text).
42206
+ // Retrieved via getRecentDebugTrace (chat_debug_bundle). Both categories are a
42207
+ // no-op unless collectDebugTrace is on AND the category is selected, so the
42208
+ // hot-path guards below (completionTraceOn / fsmTraceOn) keep production cost
42209
+ // at a single boolean check.
42210
+ completionTraceOn() {
42211
+ return shouldCollectTraceCategory("completion-gate");
42212
+ }
42213
+ fsmTraceOn() {
42214
+ return shouldCollectTraceCategory("fsm-transition");
42215
+ }
42216
+ recordCompletionGateTrace(stage, payload) {
42217
+ recordDebugTrace({
42218
+ category: "completion-gate",
42219
+ stage,
42220
+ level: "debug",
42221
+ sessionId: this.instanceId,
42222
+ providerType: this.type,
42223
+ payload
42224
+ });
42225
+ }
42226
+ recordFsmTransitionTrace(payload) {
42227
+ recordDebugTrace({
42228
+ category: "fsm-transition",
42229
+ stage: "transition",
42230
+ level: "debug",
42231
+ sessionId: this.instanceId,
42232
+ providerType: this.type,
42233
+ payload
42234
+ });
42235
+ }
42039
42236
  flushCompletedDebounceIfFinalized() {
42040
42237
  const pending = this.completedDebouncePending;
42041
42238
  if (!pending) {
@@ -42048,12 +42245,27 @@ var CliProviderInstance = class _CliProviderInstance {
42048
42245
  LOG.debug("CLI", `[${this.type}] flush attempt: adapterStatus=${latestStatus.status} latestVisible=${latestVisibleStatus} generatingStartedAt=${this.generatingStartedAt} isWaitingForResponse=${!!this.adapter?.isWaitingForResponse} hasPartial=${!!this.adapter.getPartialResponse?.()}`);
42049
42246
  if (latestVisibleStatus !== "idle") {
42050
42247
  LOG.info("CLI", `[${this.type}] cancelled pending completed (resumed ${latestVisibleStatus})`);
42248
+ if (this.completionTraceOn()) this.recordCompletionGateTrace("cancel", {
42249
+ blockReason: "resumed_status",
42250
+ latestVisibleStatus,
42251
+ previousStatus: pending.previousStatus,
42252
+ busyEpochAtArm: pending.busyEpochAtArm,
42253
+ busyEpoch: this.busyEpoch
42254
+ });
42051
42255
  this.completedDebouncePending = null;
42052
42256
  this.completedDebounceTimer = null;
42053
42257
  return;
42054
42258
  }
42055
42259
  if (typeof pending.busyEpochAtArm === "number" && this.busyEpoch !== pending.busyEpochAtArm) {
42056
42260
  LOG.info("CLI", `[${this.type}] cancelled pending completed (busy re-entry during settle: epoch ${pending.busyEpochAtArm}\u2192${this.busyEpoch})`);
42261
+ if (this.completionTraceOn()) this.recordCompletionGateTrace("cancel", {
42262
+ blockReason: "busy_reentry",
42263
+ latestVisibleStatus,
42264
+ previousStatus: pending.previousStatus,
42265
+ busyEpochAtArm: pending.busyEpochAtArm,
42266
+ busyEpoch: this.busyEpoch,
42267
+ busyEpochDelta: this.busyEpoch - pending.busyEpochAtArm
42268
+ });
42057
42269
  this.completedDebouncePending = null;
42058
42270
  this.completedDebounceTimer = null;
42059
42271
  return;
@@ -42061,6 +42273,14 @@ var CliProviderInstance = class _CliProviderInstance {
42061
42273
  const latestOutputAt = typeof latestStatus?.lastOutputAt === "number" ? latestStatus.lastOutputAt : void 0;
42062
42274
  if (typeof pending.lastOutputAtArm === "number" && typeof latestOutputAt === "number" && latestOutputAt > pending.lastOutputAtArm) {
42063
42275
  LOG.info("CLI", `[${this.type}] cancelled pending completed (new PTY output during settle: ${pending.lastOutputAtArm}\u2192${latestOutputAt})`);
42276
+ if (this.completionTraceOn()) this.recordCompletionGateTrace("cancel", {
42277
+ blockReason: "new_pty_output",
42278
+ latestVisibleStatus,
42279
+ previousStatus: pending.previousStatus,
42280
+ lastOutputAtArm: pending.lastOutputAtArm,
42281
+ lastOutputAt: latestOutputAt,
42282
+ lastOutputAtDelta: latestOutputAt - pending.lastOutputAtArm
42283
+ });
42064
42284
  this.completedDebouncePending = null;
42065
42285
  this.completedDebounceTimer = null;
42066
42286
  return;
@@ -42077,6 +42297,14 @@ var CliProviderInstance = class _CliProviderInstance {
42077
42297
  if (this.isMeshWorkerSession()) {
42078
42298
  traceMeshEventDrop("completion_gate_hold", this.meshTraceCtx(), `${blockReason} waited=${waitedMs}ms`);
42079
42299
  }
42300
+ if (this.completionTraceOn()) this.recordCompletionGateTrace("hold", {
42301
+ blockReason,
42302
+ latestVisibleStatus,
42303
+ terminal: block2.terminal === true,
42304
+ holdForTranscript: block2.holdForTranscript === true,
42305
+ approvalResolvedIdle: pending.previousStatus === "waiting_approval",
42306
+ waitedMs
42307
+ });
42080
42308
  pending.loggedBlockReason = blockReason;
42081
42309
  }
42082
42310
  this.scheduleCompletedDebounceFlush(COMPLETED_FINALIZATION_RETRY_MS);
@@ -42096,6 +42324,19 @@ var CliProviderInstance = class _CliProviderInstance {
42096
42324
  if (this.isMeshWorkerSession()) {
42097
42325
  traceMeshEventStage("fired", this.meshTraceCtx(), `forced after ${waitedMs}ms (${blockReason})`);
42098
42326
  }
42327
+ if (this.completionTraceOn()) this.recordCompletionGateTrace("fire", {
42328
+ path: isTranscriptEvidenceGate && !emittedAfterFinalizationTimeout ? "canon_c_decoupled" : "forced_timeout",
42329
+ blockReason,
42330
+ latestVisibleStatus,
42331
+ approvalResolvedIdle: pending.previousStatus === "waiting_approval",
42332
+ finalAssistantPresent: completionDiagnostic.finalAssistantPresent === true,
42333
+ evidenceSource: completionDiagnostic.finalAssistantEvidenceSource ?? null,
42334
+ lastVisibleRole: completionDiagnostic.lastVisibleRole ?? null,
42335
+ lastVisibleContentLen: completionDiagnostic.lastVisibleContentLength ?? null,
42336
+ emittedAfterFinalizationTimeout,
42337
+ waitedMs,
42338
+ busyEpoch: this.busyEpoch
42339
+ });
42099
42340
  this.pushEvent({
42100
42341
  event: "agent:generating_completed",
42101
42342
  chatTitle: pending.chatTitle,
@@ -42123,6 +42364,14 @@ var CliProviderInstance = class _CliProviderInstance {
42123
42364
  if (this.isMeshWorkerSession()) {
42124
42365
  traceMeshEventStage("fired", this.meshTraceCtx(), `duration=${pending.duration}s`);
42125
42366
  }
42367
+ if (this.completionTraceOn()) this.recordCompletionGateTrace("fire", {
42368
+ path: "clean",
42369
+ latestVisibleStatus,
42370
+ approvalResolvedIdle: pending.previousStatus === "waiting_approval",
42371
+ finalAssistantPresent: true,
42372
+ duration: pending.duration,
42373
+ busyEpoch: this.busyEpoch
42374
+ });
42126
42375
  this.pushEvent({
42127
42376
  event: "agent:generating_completed",
42128
42377
  chatTitle: pending.chatTitle,
@@ -42356,6 +42605,18 @@ var CliProviderInstance = class _CliProviderInstance {
42356
42605
  const previousStatus = this.lastStatus;
42357
42606
  if (newStatus !== this.lastStatus) {
42358
42607
  LOG.info("CLI", `[${this.type}] status: ${this.lastStatus} \u2192 ${newStatus}`);
42608
+ if (this.fsmTraceOn()) this.recordFsmTransitionTrace({
42609
+ from: this.lastStatus,
42610
+ to: newStatus,
42611
+ rawStatus,
42612
+ autoApproveActive,
42613
+ autoApproveHoldIdle,
42614
+ autoApproveBusy: this.autoApproveBusy,
42615
+ hasPending: this.hasAdapterPendingResponse(),
42616
+ busyEpoch: this.busyEpoch,
42617
+ lastOutputAt: typeof adapterStatus?.lastOutputAt === "number" ? adapterStatus.lastOutputAt : null,
42618
+ lastScreenChangeAt: typeof adapterStatus?.lastScreenChangeAt === "number" ? adapterStatus.lastScreenChangeAt : null
42619
+ });
42359
42620
  const startingToGeneratingWithActiveTurn = this.lastStatus === "starting" && newStatus === "generating" && this.hasAdapterPendingResponse();
42360
42621
  if (this.lastStatus === "idle" && newStatus === "generating" || startingToGeneratingWithActiveTurn) {
42361
42622
  if (this.completedDebouncePending && this.generatingStartedAt === 0) {
@@ -42482,6 +42743,17 @@ var CliProviderInstance = class _CliProviderInstance {
42482
42743
  if (this.isMeshWorkerSession()) {
42483
42744
  traceMeshEventStage("arm", this.meshTraceCtx(), `short-generating settle-arm (source=${shortEvidenceSource}, missingEvidence=${missingEvidence})`);
42484
42745
  }
42746
+ if (this.completionTraceOn()) this.recordCompletionGateTrace("arm", {
42747
+ branch: "short_generating",
42748
+ previousStatus: this.lastStatus,
42749
+ turnStartedAt: shortTurnStartedAt || null,
42750
+ busyEpochAtArm: this.busyEpoch,
42751
+ lastOutputAtArm: typeof adapterStatus?.lastOutputAt === "number" ? adapterStatus.lastOutputAt : null,
42752
+ flushDelay: NATIVE_HISTORY_MESH_IDLE_SETTLE_MS,
42753
+ evidenceSource: shortEvidenceSource,
42754
+ missingEvidence,
42755
+ hasFinalSummary: !!shortFinalSummary
42756
+ });
42485
42757
  this.scheduleCompletedDebounceFlush(NATIVE_HISTORY_MESH_IDLE_SETTLE_MS);
42486
42758
  } else if (missingEvidence) {
42487
42759
  LOG.info("CLI", `[${this.type}] short completion suppressed: missing final assistant evidence, non-mesh session (source=${shortEvidenceSource})`);
@@ -42530,6 +42802,16 @@ var CliProviderInstance = class _CliProviderInstance {
42530
42802
  const meshSettleSession = this.isAutonomousMeshSession();
42531
42803
  const flushDelay = ownsExternalHistory ? meshSettleSession ? NATIVE_HISTORY_MESH_IDLE_SETTLE_MS : 0 : 3e3;
42532
42804
  LOG.debug("CLI", `[${this.type}] set completedDebouncePending duration=${duration}s ownsExternalHistory=${ownsExternalHistory} meshSettle=${meshSettleSession} flushDelay=${flushDelay}ms generatingStartedAt=${this.generatingStartedAt}`);
42805
+ if (this.completionTraceOn()) this.recordCompletionGateTrace("arm", {
42806
+ branch: "normal",
42807
+ previousStatus: this.completedDebouncePending.previousStatus,
42808
+ turnStartedAt: this.completedDebouncePending.turnStartedAt ?? null,
42809
+ busyEpochAtArm: this.completedDebouncePending.busyEpochAtArm ?? null,
42810
+ lastOutputAtArm: this.completedDebouncePending.lastOutputAtArm ?? null,
42811
+ flushDelay,
42812
+ ownsExternalHistory,
42813
+ meshSettle: meshSettleSession
42814
+ });
42533
42815
  this.scheduleCompletedDebounceFlush(flushDelay);
42534
42816
  }
42535
42817
  } else if (newStatus === "idle" && this.lastStatus === "starting") {
@@ -47534,12 +47816,17 @@ var ProviderLoader = class _ProviderLoader {
47534
47816
  logFn;
47535
47817
  versionArchive = null;
47536
47818
  scriptsCache = /* @__PURE__ */ new Map();
47819
+ /**
47820
+ * Resolved registry base URL and provider tarball URL. Resolution order:
47821
+ * explicit config field (constructor option) → env var → vendor default.
47822
+ * See `config/registry-resolver.ts`.
47823
+ */
47824
+ registryBaseUrl;
47825
+ providerTarballUrl;
47537
47826
  /** Inject VersionArchive so resolve() can auto-detect installed versions */
47538
47827
  setVersionArchive(archive) {
47539
47828
  this.versionArchive = archive;
47540
47829
  }
47541
- static GITHUB_TARBALL_URL = "https://github.com/vilmire/adhdev-providers/archive/refs/heads/main.tar.gz";
47542
- static REGISTRY_BASE_URL = "https://api.adhf.dev/api/v1/registry";
47543
47830
  static META_FILE = ".meta.json";
47544
47831
  static REGISTRY_META_FILE = ".registry-meta.json";
47545
47832
  static REPO_PROVIDER_DIRNAME = "adhdev-providers";
@@ -47607,6 +47894,8 @@ var ProviderLoader = class _ProviderLoader {
47607
47894
  constructor(options) {
47608
47895
  this.logFn = options?.logFn || LOG.forComponent("Provider").asLogFn();
47609
47896
  this.probeStarts = options?.probeStarts ?? [process.cwd(), __dirname];
47897
+ this.registryBaseUrl = resolveRegistryBaseUrl(options?.registryUrl);
47898
+ this.providerTarballUrl = resolveProviderTarballUrl(options?.providerTarballUrl);
47610
47899
  this.defaultProvidersDir = path35.join(os25.homedir(), ".adhdev", "providers");
47611
47900
  const detected = this.detectDefaultUserDir();
47612
47901
  this.userDir = detected.path;
@@ -48639,7 +48928,7 @@ var ProviderLoader = class _ProviderLoader {
48639
48928
  this.log("Registry sync skipped (sourceMode=no-upstream)");
48640
48929
  return { updated: false };
48641
48930
  }
48642
- this.log(`Registry sync starting (${_ProviderLoader.REGISTRY_BASE_URL})...`);
48931
+ this.log(`Registry sync starting (${this.registryBaseUrl})...`);
48643
48932
  const https = __require("https");
48644
48933
  const regMetaPath = path35.join(this.upstreamDir, _ProviderLoader.REGISTRY_META_FILE);
48645
48934
  let cachedChecksums = {};
@@ -48650,7 +48939,7 @@ var ProviderLoader = class _ProviderLoader {
48650
48939
  } catch {
48651
48940
  }
48652
48941
  try {
48653
- const listUrl = `${_ProviderLoader.REGISTRY_BASE_URL}/providers`;
48942
+ const listUrl = `${this.registryBaseUrl}/providers`;
48654
48943
  const listBody = await new Promise((resolve25, reject) => {
48655
48944
  const req = https.get(listUrl, { headers: { "User-Agent": "adhdev-daemon", "Accept": "application/json" }, timeout: 1e4 }, (res) => {
48656
48945
  if (res.statusCode !== 200) {
@@ -48674,7 +48963,7 @@ var ProviderLoader = class _ProviderLoader {
48674
48963
  const { type, category, checksum, version } = entry;
48675
48964
  const cacheKey = `${category}/${type}`;
48676
48965
  if (cachedChecksums[cacheKey] === checksum) continue;
48677
- const dlUrl = `${_ProviderLoader.REGISTRY_BASE_URL}/providers/${type}/${version}/download`;
48966
+ const dlUrl = `${this.registryBaseUrl}/providers/${type}/${version}/download`;
48678
48967
  const manifestBody = await new Promise((resolve25, reject) => {
48679
48968
  const req = https.get(dlUrl, { headers: { "User-Agent": "adhdev-daemon", "Accept": "application/json" }, timeout: 3e4 }, (res) => {
48680
48969
  if (res.statusCode !== 200) {
@@ -48741,12 +49030,13 @@ var ProviderLoader = class _ProviderLoader {
48741
49030
  this.log("Upstream check skipped (last check < 30min ago)");
48742
49031
  return { updated: false };
48743
49032
  }
49033
+ const tarballTarget = resolveProviderTarballTarget(this.providerTarballUrl);
48744
49034
  try {
48745
49035
  const etag = await new Promise((resolve25, reject) => {
48746
49036
  const options = {
48747
49037
  method: "HEAD",
48748
- hostname: "github.com",
48749
- path: "/vilmire/adhdev-providers/archive/refs/heads/main.tar.gz",
49038
+ hostname: tarballTarget.hostname,
49039
+ path: tarballTarget.path,
48750
49040
  headers: { "User-Agent": "adhdev-launcher" },
48751
49041
  timeout: 1e4
48752
49042
  };
@@ -48787,7 +49077,7 @@ var ProviderLoader = class _ProviderLoader {
48787
49077
  this.log("Downloading latest providers from GitHub...");
48788
49078
  const tmpTar = path35.join(os25.tmpdir(), `adhdev-providers-${Date.now()}.tar.gz`);
48789
49079
  const tmpExtract = path35.join(os25.tmpdir(), `adhdev-providers-extract-${Date.now()}`);
48790
- await this.downloadFile(_ProviderLoader.GITHUB_TARBALL_URL, tmpTar);
49080
+ await this.downloadFile(tarballTarget.url, tmpTar);
48791
49081
  fs25.mkdirSync(tmpExtract, { recursive: true });
48792
49082
  await execAsync5(`tar -xzf "${tmpTar}" -C "${tmpExtract}"`, { timeout: 3e4 });
48793
49083
  const extracted = fs25.readdirSync(tmpExtract);
@@ -48887,7 +49177,7 @@ var ProviderLoader = class _ProviderLoader {
48887
49177
  etag,
48888
49178
  timestamp,
48889
49179
  lastCheck: new Date(timestamp).toISOString(),
48890
- source: _ProviderLoader.GITHUB_TARBALL_URL
49180
+ source: this.providerTarballUrl
48891
49181
  }, null, 2));
48892
49182
  } catch {
48893
49183
  }
@@ -49999,6 +50289,47 @@ async function decideOssCloneSync(ossCtx, worktreeOssSha, sourceSha, rg) {
49999
50289
  if (await isAncestor(worktreeOssSha, sourceSha)) return "advance";
50000
50290
  return "skip_diverged";
50001
50291
  }
50292
+ async function syncClonedWorktreeSubmodules(worktreePath, sourceWorkspace, rg) {
50293
+ const submodulePaths = getRegisteredSubmodulePaths(worktreePath);
50294
+ if (submodulePaths.size === 0) return;
50295
+ const sourceCtx = { workspace: sourceWorkspace, repoRoot: sourceWorkspace, isGitRepo: true };
50296
+ const worktreeCtx = { workspace: worktreePath, repoRoot: worktreePath, isGitRepo: true };
50297
+ const readStdout = (out) => (typeof out === "string" ? out : out?.stdout ?? "").trim();
50298
+ for (const submodulePath of submodulePaths) {
50299
+ try {
50300
+ const sourceStatusOut = await rg(sourceCtx, ["submodule", "status", submodulePath], { timeoutMs: 1e4 });
50301
+ const sourceSha = readStdout(sourceStatusOut).match(/^[+\- ]?([0-9a-f]{40})/)?.[1];
50302
+ if (!sourceSha) continue;
50303
+ const subCtx = {
50304
+ workspace: `${worktreePath}/${submodulePath}`,
50305
+ repoRoot: `${worktreePath}/${submodulePath}`,
50306
+ isGitRepo: true
50307
+ };
50308
+ const worktreeSubSha = readStdout(await rg(subCtx, ["rev-parse", "HEAD"], { timeoutMs: 1e4 }));
50309
+ if (!worktreeSubSha || worktreeSubSha === sourceSha) continue;
50310
+ await rg(subCtx, ["fetch", `${sourceWorkspace}/${submodulePath}`, "HEAD"], { timeoutMs: 6e4 });
50311
+ let action;
50312
+ try {
50313
+ action = await decideOssCloneSync(subCtx, worktreeSubSha, sourceSha, rg);
50314
+ } catch (decideErr) {
50315
+ action = "skip_diverged";
50316
+ console.warn(`[mesh] ${submodulePath} submodule sync guard could not resolve ancestry (kept fresh worktree HEAD): ${decideErr?.message ?? decideErr}`);
50317
+ }
50318
+ if (action === "advance") {
50319
+ await rg(subCtx, ["checkout", sourceSha], { timeoutMs: 1e4 });
50320
+ await rg(worktreeCtx, ["add", submodulePath], { timeoutMs: 1e4 });
50321
+ await rg(worktreeCtx, ["commit", "-m", `chore: sync ${submodulePath} to source node HEAD on clone`], { timeoutMs: 1e4 });
50322
+ console.log(`[mesh] Advanced ${submodulePath} submodule to newer source HEAD ${sourceSha.slice(0, 8)} in worktree`);
50323
+ } else if (action === "skip_rewind") {
50324
+ console.warn(`[mesh] Skipped ${submodulePath} submodule rewind on clone: source node ${submodulePath} ${sourceSha.slice(0, 8)} is an ancestor of the fresh worktree ${submodulePath} ${worktreeSubSha.slice(0, 8)} \u2014 kept fresher worktree HEAD`);
50325
+ } else if (action === "skip_diverged") {
50326
+ console.warn(`[mesh] Skipped ${submodulePath} submodule sync on clone: source node ${submodulePath} ${sourceSha.slice(0, 8)} diverged from the fresh worktree ${submodulePath} ${worktreeSubSha.slice(0, 8)} \u2014 kept worktree HEAD (coordinator reconciles)`);
50327
+ }
50328
+ } catch (subErr) {
50329
+ console.warn(`[mesh] ${submodulePath} submodule sync to source HEAD failed (best-effort):`, subErr?.message ?? subErr);
50330
+ }
50331
+ }
50332
+ }
50002
50333
  var meshCrudHandlers = {
50003
50334
  list_meshes: async (ctx, _args) => {
50004
50335
  try {
@@ -50703,42 +51034,8 @@ var meshCrudHandlers = {
50703
51034
  submodulesInitialized2 = true;
50704
51035
  const sourceWorkspace = sourceNode.repoRoot || sourceNode.workspace;
50705
51036
  if (sourceWorkspace) {
50706
- try {
50707
- const { runGit: rg } = await Promise.resolve().then(() => (init_git_executor(), git_executor_exports));
50708
- const sourceCtx = { workspace: sourceWorkspace, repoRoot: sourceWorkspace, isGitRepo: true };
50709
- const worktreeCtx = { workspace: result.worktreePath, repoRoot: result.worktreePath, isGitRepo: true };
50710
- const sourceStatusOut = await rg(sourceCtx, ["submodule", "status", "oss"], { timeoutMs: 1e4 });
50711
- const sourceStatusLine = (typeof sourceStatusOut === "string" ? sourceStatusOut : sourceStatusOut?.stdout ?? "").trim();
50712
- const sourceShaMatch = sourceStatusLine.match(/^[+\- ]?([0-9a-f]{40})/);
50713
- const sourceSha = sourceShaMatch?.[1];
50714
- if (sourceSha) {
50715
- const ossCtx = { workspace: `${result.worktreePath}/oss`, repoRoot: `${result.worktreePath}/oss`, isGitRepo: true };
50716
- const worktreeOssHeadOut = await rg(ossCtx, ["rev-parse", "HEAD"], { timeoutMs: 1e4 });
50717
- const worktreeOssSha = (typeof worktreeOssHeadOut === "string" ? worktreeOssHeadOut : worktreeOssHeadOut?.stdout ?? "").trim();
50718
- if (worktreeOssSha && worktreeOssSha !== sourceSha) {
50719
- await rg(ossCtx, ["fetch", `${sourceWorkspace}/oss`, "HEAD"], { timeoutMs: 6e4 });
50720
- let ossAction;
50721
- try {
50722
- ossAction = await decideOssCloneSync(ossCtx, worktreeOssSha, sourceSha, rg);
50723
- } catch (decideErr) {
50724
- ossAction = "skip_diverged";
50725
- console.warn(`[mesh] oss submodule sync guard could not resolve ancestry (kept fresh worktree HEAD): ${decideErr?.message ?? decideErr}`);
50726
- }
50727
- if (ossAction === "advance") {
50728
- await rg(ossCtx, ["checkout", sourceSha], { timeoutMs: 1e4 });
50729
- await rg(worktreeCtx, ["add", "oss"], { timeoutMs: 1e4 });
50730
- await rg(worktreeCtx, ["commit", "-m", "chore: sync oss to source node HEAD on clone"], { timeoutMs: 1e4 });
50731
- console.log(`[mesh] Advanced oss submodule to newer source HEAD ${sourceSha.slice(0, 8)} in worktree`);
50732
- } else if (ossAction === "skip_rewind") {
50733
- console.warn(`[mesh] Skipped oss submodule rewind on clone: source node oss ${sourceSha.slice(0, 8)} is an ancestor of the fresh worktree oss ${worktreeOssSha.slice(0, 8)} \u2014 kept fresher worktree HEAD`);
50734
- } else if (ossAction === "skip_diverged") {
50735
- console.warn(`[mesh] Skipped oss submodule sync on clone: source node oss ${sourceSha.slice(0, 8)} diverged from the fresh worktree oss ${worktreeOssSha.slice(0, 8)} \u2014 kept worktree HEAD (coordinator reconciles)`);
50736
- }
50737
- }
50738
- }
50739
- } catch (ossErr) {
50740
- console.warn("[mesh] oss submodule sync to source HEAD failed (best-effort):", ossErr.message);
50741
- }
51037
+ const { runGit: rg } = await Promise.resolve().then(() => (init_git_executor(), git_executor_exports));
51038
+ await syncClonedWorktreeSubmodules(result.worktreePath, sourceWorkspace, rg);
50742
51039
  }
50743
51040
  } catch (subErr) {
50744
51041
  console.warn("[mesh] Submodule init failed for worktree:", subErr.message);
@@ -52172,6 +52469,26 @@ import { execFileSync as execFileSync6 } from "child_process";
52172
52469
  import { existsSync as existsSync40, readFileSync as readFileSync31 } from "fs";
52173
52470
  import { resolve as resolve19 } from "path";
52174
52471
  var PREVIEW_DEPLOY_RECORD = ".adhdev/preview-deploy.json";
52472
+ var PREVIEW_PIPELINE_SCRIPTS = [
52473
+ "scripts/preview-freshness.mjs",
52474
+ "scripts/smoke-preview-web.mjs",
52475
+ "scripts/deploy-preview-local.mjs"
52476
+ ];
52477
+ function hasDeployPreviewNpmScript(repoRoot) {
52478
+ const pkgPath = resolve19(repoRoot, "package.json");
52479
+ if (!existsSync40(pkgPath)) return false;
52480
+ try {
52481
+ const pkg = JSON.parse(readFileSync31(pkgPath, "utf8"));
52482
+ return typeof pkg?.scripts?.["deploy:preview"] === "string";
52483
+ } catch {
52484
+ return false;
52485
+ }
52486
+ }
52487
+ function isPreviewPipelineConfigured(repoRoot) {
52488
+ if (existsSync40(resolve19(repoRoot, PREVIEW_DEPLOY_RECORD))) return true;
52489
+ if (PREVIEW_PIPELINE_SCRIPTS.some((rel) => existsSync40(resolve19(repoRoot, rel)))) return true;
52490
+ return hasDeployPreviewNpmScript(repoRoot);
52491
+ }
52175
52492
  function runGit2(repoRoot, args) {
52176
52493
  try {
52177
52494
  return execFileSync6("git", args, {
@@ -52223,6 +52540,7 @@ function readCurrentMainCommit(repoRoot) {
52223
52540
  return { currentMainCommit: null, currentMainCommitSource: "unknown" };
52224
52541
  }
52225
52542
  function buildPreviewFreshness(repoRoot) {
52543
+ if (!isPreviewPipelineConfigured(repoRoot)) return null;
52226
52544
  const current = readCurrentMainCommit(repoRoot);
52227
52545
  const record = readRecord6(repoRoot);
52228
52546
  const lastPreviewCommit = normalizeCommit(record?.lastPreviewCommit);
@@ -55038,6 +55356,7 @@ async function runMeshRefineSubmoduleReachabilityGate(repoRoot, mergedTree, opti
55038
55356
  commit: gitlink.commit,
55039
55357
  reachable: false
55040
55358
  };
55359
+ let submoduleDefaultBranch = "main";
55041
55360
  try {
55042
55361
  if (!fs30.existsSync(submodulePath)) {
55043
55362
  entry.error = `Submodule checkout missing at ${gitlink.path}`;
@@ -55090,9 +55409,14 @@ async function runMeshRefineSubmoduleReachabilityGate(repoRoot, mergedTree, opti
55090
55409
  entries.push(entry);
55091
55410
  continue;
55092
55411
  }
55093
- entry.remoteMainBranch = "main";
55412
+ submoduleDefaultBranch = await resolveSubmoduleDefaultBranch({
55413
+ submoduleRepoPath: submodulePath,
55414
+ superprojectWorkspace: repoRoot,
55415
+ submodulePath: gitlink.path
55416
+ });
55417
+ entry.remoteMainBranch = submoduleDefaultBranch;
55094
55418
  try {
55095
- await verifyRemoteMainContainsCommit(submodulePath, gitlink.commit, "main");
55419
+ await verifyRemoteMainContainsCommit(submodulePath, gitlink.commit, submoduleDefaultBranch);
55096
55420
  entry.fetchedFromOrigin = true;
55097
55421
  entry.remoteReachable = true;
55098
55422
  entry.remoteMainReachable = true;
@@ -55102,17 +55426,17 @@ async function runMeshRefineSubmoduleReachabilityGate(repoRoot, mergedTree, opti
55102
55426
  entry.remoteMainReachable = false;
55103
55427
  entry.publishRequired = true;
55104
55428
  const details = truncateValidationOutput(e?.stderr || e?.message || String(e));
55105
- entry.error = `Submodule remote main reachability check failed for origin/main: ${details}`;
55429
+ entry.error = `Submodule remote main reachability check failed for origin/${submoduleDefaultBranch}: ${details}`;
55106
55430
  if (options.allowAutoPublishSubmoduleMainCommits === true && entry.localReachable === true) {
55107
55431
  entry.autoPublishAllowed = true;
55108
55432
  entry.autoPublishAttempted = true;
55109
55433
  try {
55110
- const publish = await publishCommitToRemoteMain(submodulePath, gitlink.commit, "main");
55434
+ const publish = await publishCommitToRemoteMain(submodulePath, gitlink.commit, submoduleDefaultBranch);
55111
55435
  entry.autoPublishRefspec = publish.refspec;
55112
55436
  entry.publishStdout = truncateValidationOutput(publish.stdout);
55113
55437
  entry.publishStderr = truncateValidationOutput(publish.stderr);
55114
55438
  entry.autoPublishSucceeded = true;
55115
- await verifyRemoteMainContainsCommit(submodulePath, gitlink.commit, "main");
55439
+ await verifyRemoteMainContainsCommit(submodulePath, gitlink.commit, submoduleDefaultBranch);
55116
55440
  entry.fetchedFromOrigin = true;
55117
55441
  entry.remoteReachable = true;
55118
55442
  entry.remoteMainReachable = true;
@@ -55124,12 +55448,12 @@ async function runMeshRefineSubmoduleReachabilityGate(repoRoot, mergedTree, opti
55124
55448
  entry.autoPublishSucceeded = false;
55125
55449
  entry.autoPublishVerified = false;
55126
55450
  const publishDetails = truncateValidationOutput(publishError?.stderr || publishError?.message || String(publishError));
55127
- entry.error = `Submodule auto-publish to origin/main failed or could not be verified: ${publishDetails}`;
55451
+ entry.error = `Submodule auto-publish to origin/${submoduleDefaultBranch} failed or could not be verified: ${publishDetails}`;
55128
55452
  }
55129
55453
  } else if (options.allowAutoPublishSubmoduleMainCommits === true) {
55130
55454
  entry.autoPublishAllowed = true;
55131
55455
  entry.autoPublishAttempted = false;
55132
- entry.autoPublishSkippedReason = entry.autoPublishSkippedReason || "candidate commit is not reachable in the source checkout or worktree submodule, so Refinery cannot push it to origin/main";
55456
+ entry.autoPublishSkippedReason = entry.autoPublishSkippedReason || `candidate commit is not reachable in the source checkout or worktree submodule, so Refinery cannot push it to origin/${submoduleDefaultBranch}`;
55133
55457
  }
55134
55458
  }
55135
55459
  } catch (e) {
@@ -55137,7 +55461,7 @@ async function runMeshRefineSubmoduleReachabilityGate(repoRoot, mergedTree, opti
55137
55461
  entry.remoteMainReachable = false;
55138
55462
  entry.publishRequired = true;
55139
55463
  const details = truncateValidationOutput(e?.stderr || e?.message || String(e));
55140
- entry.error = `Submodule remote main reachability check failed for origin/main: ${details}`;
55464
+ entry.error = `Submodule remote main reachability check failed for origin/${submoduleDefaultBranch}: ${details}`;
55141
55465
  }
55142
55466
  } catch (e) {
55143
55467
  entry.error = truncateValidationOutput(e?.message || String(e));
@@ -65943,7 +66267,9 @@ async function initDaemonComponents(config) {
65943
66267
  const providerLoader = new ProviderLoader({
65944
66268
  logFn: config.providerLogFn,
65945
66269
  sourceMode: providerSourceMode,
65946
- userDir: appConfig.providerDir
66270
+ userDir: appConfig.providerDir,
66271
+ registryUrl: appConfig.registryUrl,
66272
+ providerTarballUrl: appConfig.providerTarballUrl
65947
66273
  });
65948
66274
  providerLoader.loadAll();
65949
66275
  providerLoader.registerToDetector();
@@ -66434,6 +66760,7 @@ var V1_ALL_PRIMITIVES = Object.freeze(
66434
66760
  );
66435
66761
  var V1_CONTRACT_VERSION = "1.0.0";
66436
66762
  export {
66763
+ ALWAYS_ON_TRACE_CATEGORIES,
66437
66764
  AcpProviderInstance,
66438
66765
  AgentStreamPoller,
66439
66766
  BUILTIN_CHAT_MESSAGE_KINDS,
@@ -66665,6 +66992,7 @@ export {
66665
66992
  installGlobalInterceptor,
66666
66993
  interactivePromptFromClaudeAskUserQuestion,
66667
66994
  isActivityChatMessage,
66995
+ isAlwaysOnTraceCategory,
66668
66996
  isBuiltinChatMessageKind,
66669
66997
  isCdpConnected,
66670
66998
  isExtensionInstalled,