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

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
@@ -409,10 +409,10 @@ function readInjected(value) {
409
409
  }
410
410
  function getDaemonBuildInfo() {
411
411
  if (cached) return cached;
412
- const commit = readInjected(true ? "49032ec6c48a0cbe6f122e2615088b38407712b6" : void 0) ?? "unknown";
413
- const commitShort = readInjected(true ? "49032ec6" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
414
- const version = readInjected(true ? "0.9.82-rc.456" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
415
- const builtAt = readInjected(true ? "2026-07-04T07:05:38.214Z" : void 0);
412
+ const commit = readInjected(true ? "29441f596e5efe1972b874e0fb5b3d380c9d9bda" : void 0) ?? "unknown";
413
+ const commitShort = readInjected(true ? "29441f59" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
414
+ const version = readInjected(true ? "0.9.82-rc.457" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
415
+ const builtAt = readInjected(true ? "2026-07-04T08:51:27.287Z" : void 0);
416
416
  cached = builtAt ? { commit, commitShort, version, builtAt } : { commit, commitShort, version };
417
417
  return cached;
418
418
  }
@@ -1586,6 +1586,8 @@ function normalizeConfig(raw) {
1586
1586
  ideSettings: isPlainObject(parsed.ideSettings) ? parsed.ideSettings : {},
1587
1587
  providerSourceMode: resolveProviderSourceMode(parsed.providerSourceMode, parsed.disableUpstream),
1588
1588
  providerDir: asOptionalString(parsed.providerDir),
1589
+ registryUrl: asOptionalString(parsed.registryUrl),
1590
+ providerTarballUrl: asOptionalString(parsed.providerTarballUrl),
1589
1591
  updateChannel: parsed.updateChannel === "preview" ? "preview" : "stable",
1590
1592
  terminalSizingMode: parsed.terminalSizingMode === "fit" ? "fit" : "measured"
1591
1593
  };
@@ -8932,6 +8934,7 @@ __export(worktree_bootstrap_config_exports, {
8932
8934
  WORKTREE_BOOTSTRAP_STALE_RUNNING_MS: () => WORKTREE_BOOTSTRAP_STALE_RUNNING_MS,
8933
8935
  computeStaleInputsDigest: () => computeStaleInputsDigest,
8934
8936
  evaluateWorktreeBootstrapState: () => evaluateWorktreeBootstrapState,
8937
+ getRegisteredSubmodulePaths: () => getRegisteredSubmodulePaths,
8935
8938
  isWorktreeBootstrapStaleRunning: () => isWorktreeBootstrapStaleRunning,
8936
8939
  loadMeshWorktreeBootstrapConfig: () => loadMeshWorktreeBootstrapConfig,
8937
8940
  runMeshWorktreeBootstrap: () => runMeshWorktreeBootstrap,
@@ -30689,6 +30692,35 @@ var DaemonCdpInitializer = class {
30689
30692
 
30690
30693
  // src/commands/handler.ts
30691
30694
  init_builders();
30695
+ init_config();
30696
+
30697
+ // src/config/registry-resolver.ts
30698
+ var DEFAULT_REGISTRY_BASE_URL = "https://api.adhf.dev/api/v1/registry";
30699
+ var DEFAULT_PROVIDER_TARBALL_URL = "https://github.com/vilmire/adhdev-providers/archive/refs/heads/main.tar.gz";
30700
+ var REGISTRY_URL_ENV_VAR = "ADHDEV_REGISTRY_URL";
30701
+ var PROVIDER_TARBALL_URL_ENV_VAR = "ADHDEV_PROVIDER_TARBALL_URL";
30702
+ function cleanString(value) {
30703
+ return typeof value === "string" && value.trim() ? value.trim() : void 0;
30704
+ }
30705
+ function stripTrailingSlashes(url) {
30706
+ return url.replace(/\/+$/, "");
30707
+ }
30708
+ function resolveRegistryBaseUrl(configuredUrl, env = process.env) {
30709
+ const resolved = cleanString(configuredUrl) ?? cleanString(env[REGISTRY_URL_ENV_VAR]) ?? DEFAULT_REGISTRY_BASE_URL;
30710
+ return stripTrailingSlashes(resolved);
30711
+ }
30712
+ function resolveProviderTarballUrl(configuredUrl, env = process.env) {
30713
+ return cleanString(configuredUrl) ?? cleanString(env[PROVIDER_TARBALL_URL_ENV_VAR]) ?? DEFAULT_PROVIDER_TARBALL_URL;
30714
+ }
30715
+ function resolveProviderTarballTarget(configuredUrl, env = process.env) {
30716
+ const url = resolveProviderTarballUrl(configuredUrl, env);
30717
+ const parsed = new URL(url);
30718
+ return {
30719
+ url,
30720
+ hostname: parsed.hostname,
30721
+ path: parsed.pathname + (parsed.search || "")
30722
+ };
30723
+ }
30692
30724
 
30693
30725
  // src/sessions/reconcile.ts
30694
30726
  function upsertSessionTarget(sessionRegistry, target) {
@@ -35586,7 +35618,7 @@ var DaemonCommandHandler = class {
35586
35618
  const https = require("https");
35587
35619
  const fs40 = require("fs");
35588
35620
  const path44 = require("path");
35589
- const REGISTRY = "https://api.adhf.dev/api/v1/registry";
35621
+ const REGISTRY = resolveRegistryBaseUrl(loadConfig().registryUrl);
35590
35622
  function fetchText(url, timeoutMs) {
35591
35623
  return new Promise((resolve25, reject) => {
35592
35624
  const req = https.get(url, { headers: { "User-Agent": "adhdev-daemon", "Accept": "application/json" }, timeout: timeoutMs }, (res) => {
@@ -35948,7 +35980,7 @@ ${formatManifestValidationIssues2(validation.issues)}`,
35948
35980
  const installed = this.handleListInstalledProviders({});
35949
35981
  if (!installed.success) return installed;
35950
35982
  const https = require("https");
35951
- const REGISTRY = "https://api.adhf.dev/api/v1/registry";
35983
+ const REGISTRY = resolveRegistryBaseUrl(loadConfig().registryUrl);
35952
35984
  function fetchJson(url) {
35953
35985
  return new Promise((resolve25, reject) => {
35954
35986
  const req = https.get(url, { headers: { "User-Agent": "adhdev-daemon", "Accept": "application/json" }, timeout: 1e4 }, (res) => {
@@ -37551,6 +37583,7 @@ var CHANNEL_SERVER_URL = {
37551
37583
  stable: "https://api.adhf.dev",
37552
37584
  preview: "https://api-preview.adhf.dev"
37553
37585
  };
37586
+ var VENDOR_SERVER_URLS = new Set(Object.values(CHANNEL_SERVER_URL));
37554
37587
  function normalizeReleaseChannel(value) {
37555
37588
  if (typeof value !== "string") return null;
37556
37589
  const normalized = value.trim().toLowerCase();
@@ -37572,7 +37605,11 @@ var daemonLifecycleHandlers = {
37572
37605
  const npmTag = CHANNEL_NPM_TAG[channel];
37573
37606
  const latest = String(execNpmCommandSync(["view", `${pkgName}@${npmTag}`, "version"], { encoding: "utf-8", timeout: 1e4 }, npmSurface)).trim();
37574
37607
  LOG.info("Upgrade", `Latest ${pkgName}@${npmTag}: v${latest}`);
37575
- updateConfig({ updateChannel: channel, serverUrl: CHANNEL_SERVER_URL[channel] });
37608
+ const currentServerUrl = typeof loadConfig().serverUrl === "string" ? loadConfig().serverUrl.trim() : "";
37609
+ const useVendorServerUrl = currentServerUrl === "" || VENDOR_SERVER_URLS.has(currentServerUrl);
37610
+ updateConfig(
37611
+ useVendorServerUrl ? { updateChannel: channel, serverUrl: CHANNEL_SERVER_URL[channel] } : { updateChannel: channel }
37612
+ );
37576
37613
  let currentInstalled = null;
37577
37614
  try {
37578
37615
  const currentJson = String(execNpmCommandSync(["ls", "-g", pkgName, "--depth=0", "--json"], {
@@ -38280,6 +38317,7 @@ function applyPreLaunchTrust(trust, workingDir) {
38280
38317
 
38281
38318
  // src/providers/spec/fsm-driver.ts
38282
38319
  init_logger();
38320
+ init_debug_config();
38283
38321
  init_pty_write_chunking();
38284
38322
  function countNewlines(s2) {
38285
38323
  let n = 0;
@@ -38348,6 +38386,11 @@ var FsmDriver = class {
38348
38386
  * (−1 = whole screen), or a `section:<id>` / `<region>#ignore:<pat>` string
38349
38387
  * when the clause scopes to a section or declares an ignore_lines filter. */
38350
38388
  regionLastChangedAt = /* @__PURE__ */ new Map();
38389
+ /** COMPLETION-EARLYNOTIFY stable-eval trace: last stable/not-stable verdict
38390
+ * recorded per stable region, so the trace fires only when the verdict FLIPS
38391
+ * (not every quiet frame). Cleared on every transition alongside
38392
+ * regionLastChangedAt. Diagnostic-only — never consulted by the FSM. */
38393
+ stableVerdictCache = /* @__PURE__ */ new Map();
38351
38394
  /** Timer that re-runs evaluate() when a time-condition would flip true
38352
38395
  * with no PTY frame to trigger it. */
38353
38396
  wakeTimer = null;
@@ -38683,6 +38726,7 @@ var FsmDriver = class {
38683
38726
  this.currentStateId = fired.to;
38684
38727
  this.stateEnteredAt = now;
38685
38728
  this.regionLastChangedAt.clear();
38729
+ this.stableVerdictCache.clear();
38686
38730
  this.pushHistory(fired.to, stateById(this.spec, fired.to)?.label ?? fired.to, {
38687
38731
  reason: "transition",
38688
38732
  via: `${from}\u2192${fired.to}`,
@@ -38787,6 +38831,7 @@ var FsmDriver = class {
38787
38831
  trackRegionChanges(currentLines, cursor, now) {
38788
38832
  if (this.prevScreenLines.length === 0) return;
38789
38833
  const descs = this.stableRegionDescriptors();
38834
+ const stableTraceOn = shouldCollectTraceCategory("fsm-transition");
38790
38835
  const needsSections = descs.some((d) => !!d.section);
38791
38836
  const curSections = needsSections ? resolveSections(this.spec.sections ?? {}, currentLines) : [];
38792
38837
  const prevSections = needsSections ? resolveSections(this.spec.sections ?? {}, this.prevScreenLines) : [];
@@ -38807,6 +38852,28 @@ var FsmDriver = class {
38807
38852
  const cur = filterIgnoredLines(curLines, d.ignoreRe).join("\n");
38808
38853
  const prev = filterIgnoredLines(prevLines, d.ignoreRe).join("\n");
38809
38854
  if (cur !== prev) this.regionLastChangedAt.set(d.key, now);
38855
+ if (stableTraceOn && typeof d.holdMs === "number") {
38856
+ const lastChanged = this.regionLastChangedAt.get(d.key) ?? this.stateEnteredAt;
38857
+ const ageMs = now - lastChanged;
38858
+ const verdict = ageMs >= d.holdMs;
38859
+ if (this.stableVerdictCache.get(d.key) !== verdict) {
38860
+ this.stableVerdictCache.set(d.key, verdict);
38861
+ recordDebugTrace({
38862
+ category: "fsm-transition",
38863
+ stage: "stable-eval",
38864
+ level: "debug",
38865
+ payload: {
38866
+ state: this.currentStateId,
38867
+ regionKey: String(d.key),
38868
+ ignorePattern: d.ignoreRe?.source ?? null,
38869
+ fingerprintLen: cur.length,
38870
+ ageMs,
38871
+ holdMs: d.holdMs,
38872
+ verdict
38873
+ }
38874
+ });
38875
+ }
38876
+ }
38810
38877
  }
38811
38878
  }
38812
38879
  /** Every distinct stable-region descriptor referenced by stable_ms
@@ -39322,7 +39389,8 @@ function collectStableDescriptors(when, byKey) {
39322
39389
  const w = when;
39323
39390
  if ("stable_ms" in w) {
39324
39391
  const key2 = stableRegionKey(w);
39325
- if (!byKey.has(key2)) {
39392
+ const existing = byKey.get(key2);
39393
+ if (!existing) {
39326
39394
  let ignoreRe;
39327
39395
  if (w.ignore_lines) {
39328
39396
  try {
@@ -39330,7 +39398,9 @@ function collectStableDescriptors(when, byKey) {
39330
39398
  } catch {
39331
39399
  }
39332
39400
  }
39333
- byKey.set(key2, { key: key2, section: w.section, cursor_above: w.cursor_above, ignoreRe });
39401
+ 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 });
39402
+ } else if (existing.holdMs === void 0 && typeof w.stable_ms === "number") {
39403
+ existing.holdMs = w.stable_ms;
39334
39404
  }
39335
39405
  return;
39336
39406
  }
@@ -41102,6 +41172,7 @@ function createCliAdapter(provider, workingDir, cliArgs, extraEnv, transportFact
41102
41172
 
41103
41173
  // src/providers/cli-provider-instance.ts
41104
41174
  init_logger();
41175
+ init_debug_config();
41105
41176
  init_mesh_event_trace();
41106
41177
  init_control_effects();
41107
41178
  init_approval_utils();
@@ -42175,9 +42246,10 @@ var CliProviderInstance = class _CliProviderInstance {
42175
42246
  return restoredHistory.messages;
42176
42247
  }
42177
42248
  completionFinalAssistantEvidence(parsedMessages, turnStartedAt) {
42249
+ const turnClosed = !this.hasAdapterPendingResponse();
42178
42250
  if (this.completionHasFinalAssistantMessage(parsedMessages, turnStartedAt)) {
42179
42251
  return {
42180
- present: true,
42252
+ present: turnClosed,
42181
42253
  messages: Array.isArray(parsedMessages) ? parsedMessages : [],
42182
42254
  source: "parsed"
42183
42255
  };
@@ -42185,7 +42257,7 @@ var CliProviderInstance = class _CliProviderInstance {
42185
42257
  const externalMessages = this.readExternalCompletionMessages();
42186
42258
  if (externalMessages) {
42187
42259
  return {
42188
- present: this.completionHasFinalAssistantMessage(externalMessages, turnStartedAt),
42260
+ present: turnClosed && this.completionHasFinalAssistantMessage(externalMessages, turnStartedAt),
42189
42261
  messages: externalMessages,
42190
42262
  source: "external-native"
42191
42263
  };
@@ -42284,11 +42356,9 @@ var CliProviderInstance = class _CliProviderInstance {
42284
42356
  if (latestVisibleStatus !== "idle") return { reason: `status:${latestVisibleStatus}`, terminal: true };
42285
42357
  const adapterAny = this.adapter;
42286
42358
  const approvalResolvedIdle = pending.previousStatus === "waiting_approval";
42287
- if (!approvalResolvedIdle) {
42288
- if (adapterAny?.isWaitingForResponse === true) return { reason: "adapter_waiting_for_response", terminal: true };
42289
- if (adapterAny?.currentTurnScope) return { reason: "adapter_turn_scope_active", terminal: true };
42290
- if (this.hasAdapterPendingResponse()) return { reason: "adapter_pending_response", terminal: true };
42291
- }
42359
+ if (adapterAny?.isWaitingForResponse === true) return { reason: "adapter_waiting_for_response", terminal: !approvalResolvedIdle };
42360
+ if (adapterAny?.currentTurnScope) return { reason: "adapter_turn_scope_active", terminal: !approvalResolvedIdle };
42361
+ if (this.hasAdapterPendingResponse()) return { reason: "adapter_pending_response", terminal: !approvalResolvedIdle };
42292
42362
  const partial = typeof this.adapter.getPartialResponse === "function" ? this.adapter.getPartialResponse() : "";
42293
42363
  if (typeof partial === "string" && partial.trim()) return { reason: "partial_response_pending", terminal: true };
42294
42364
  let parsed;
@@ -42443,6 +42513,39 @@ var CliProviderInstance = class _CliProviderInstance {
42443
42513
  event
42444
42514
  };
42445
42515
  }
42516
+ // COMPLETION-EARLYNOTIFY instrumentation. A session-keyed FSM-transition +
42517
+ // completion-gate snapshot recorded into the shared debug-trace ring buffer
42518
+ // (secret-safe, length/role/pattern-name only — never screen or bubble text).
42519
+ // Retrieved via getRecentDebugTrace (chat_debug_bundle). Both categories are a
42520
+ // no-op unless collectDebugTrace is on AND the category is selected, so the
42521
+ // hot-path guards below (completionTraceOn / fsmTraceOn) keep production cost
42522
+ // at a single boolean check.
42523
+ completionTraceOn() {
42524
+ return shouldCollectTraceCategory("completion-gate");
42525
+ }
42526
+ fsmTraceOn() {
42527
+ return shouldCollectTraceCategory("fsm-transition");
42528
+ }
42529
+ recordCompletionGateTrace(stage, payload) {
42530
+ recordDebugTrace({
42531
+ category: "completion-gate",
42532
+ stage,
42533
+ level: "debug",
42534
+ sessionId: this.instanceId,
42535
+ providerType: this.type,
42536
+ payload
42537
+ });
42538
+ }
42539
+ recordFsmTransitionTrace(payload) {
42540
+ recordDebugTrace({
42541
+ category: "fsm-transition",
42542
+ stage: "transition",
42543
+ level: "debug",
42544
+ sessionId: this.instanceId,
42545
+ providerType: this.type,
42546
+ payload
42547
+ });
42548
+ }
42446
42549
  flushCompletedDebounceIfFinalized() {
42447
42550
  const pending = this.completedDebouncePending;
42448
42551
  if (!pending) {
@@ -42455,12 +42558,27 @@ var CliProviderInstance = class _CliProviderInstance {
42455
42558
  LOG.debug("CLI", `[${this.type}] flush attempt: adapterStatus=${latestStatus.status} latestVisible=${latestVisibleStatus} generatingStartedAt=${this.generatingStartedAt} isWaitingForResponse=${!!this.adapter?.isWaitingForResponse} hasPartial=${!!this.adapter.getPartialResponse?.()}`);
42456
42559
  if (latestVisibleStatus !== "idle") {
42457
42560
  LOG.info("CLI", `[${this.type}] cancelled pending completed (resumed ${latestVisibleStatus})`);
42561
+ if (this.completionTraceOn()) this.recordCompletionGateTrace("cancel", {
42562
+ blockReason: "resumed_status",
42563
+ latestVisibleStatus,
42564
+ previousStatus: pending.previousStatus,
42565
+ busyEpochAtArm: pending.busyEpochAtArm,
42566
+ busyEpoch: this.busyEpoch
42567
+ });
42458
42568
  this.completedDebouncePending = null;
42459
42569
  this.completedDebounceTimer = null;
42460
42570
  return;
42461
42571
  }
42462
42572
  if (typeof pending.busyEpochAtArm === "number" && this.busyEpoch !== pending.busyEpochAtArm) {
42463
42573
  LOG.info("CLI", `[${this.type}] cancelled pending completed (busy re-entry during settle: epoch ${pending.busyEpochAtArm}\u2192${this.busyEpoch})`);
42574
+ if (this.completionTraceOn()) this.recordCompletionGateTrace("cancel", {
42575
+ blockReason: "busy_reentry",
42576
+ latestVisibleStatus,
42577
+ previousStatus: pending.previousStatus,
42578
+ busyEpochAtArm: pending.busyEpochAtArm,
42579
+ busyEpoch: this.busyEpoch,
42580
+ busyEpochDelta: this.busyEpoch - pending.busyEpochAtArm
42581
+ });
42464
42582
  this.completedDebouncePending = null;
42465
42583
  this.completedDebounceTimer = null;
42466
42584
  return;
@@ -42468,6 +42586,14 @@ var CliProviderInstance = class _CliProviderInstance {
42468
42586
  const latestOutputAt = typeof latestStatus?.lastOutputAt === "number" ? latestStatus.lastOutputAt : void 0;
42469
42587
  if (typeof pending.lastOutputAtArm === "number" && typeof latestOutputAt === "number" && latestOutputAt > pending.lastOutputAtArm) {
42470
42588
  LOG.info("CLI", `[${this.type}] cancelled pending completed (new PTY output during settle: ${pending.lastOutputAtArm}\u2192${latestOutputAt})`);
42589
+ if (this.completionTraceOn()) this.recordCompletionGateTrace("cancel", {
42590
+ blockReason: "new_pty_output",
42591
+ latestVisibleStatus,
42592
+ previousStatus: pending.previousStatus,
42593
+ lastOutputAtArm: pending.lastOutputAtArm,
42594
+ lastOutputAt: latestOutputAt,
42595
+ lastOutputAtDelta: latestOutputAt - pending.lastOutputAtArm
42596
+ });
42471
42597
  this.completedDebouncePending = null;
42472
42598
  this.completedDebounceTimer = null;
42473
42599
  return;
@@ -42484,6 +42610,14 @@ var CliProviderInstance = class _CliProviderInstance {
42484
42610
  if (this.isMeshWorkerSession()) {
42485
42611
  traceMeshEventDrop("completion_gate_hold", this.meshTraceCtx(), `${blockReason} waited=${waitedMs}ms`);
42486
42612
  }
42613
+ if (this.completionTraceOn()) this.recordCompletionGateTrace("hold", {
42614
+ blockReason,
42615
+ latestVisibleStatus,
42616
+ terminal: block2.terminal === true,
42617
+ holdForTranscript: block2.holdForTranscript === true,
42618
+ approvalResolvedIdle: pending.previousStatus === "waiting_approval",
42619
+ waitedMs
42620
+ });
42487
42621
  pending.loggedBlockReason = blockReason;
42488
42622
  }
42489
42623
  this.scheduleCompletedDebounceFlush(COMPLETED_FINALIZATION_RETRY_MS);
@@ -42503,6 +42637,19 @@ var CliProviderInstance = class _CliProviderInstance {
42503
42637
  if (this.isMeshWorkerSession()) {
42504
42638
  traceMeshEventStage("fired", this.meshTraceCtx(), `forced after ${waitedMs}ms (${blockReason})`);
42505
42639
  }
42640
+ if (this.completionTraceOn()) this.recordCompletionGateTrace("fire", {
42641
+ path: isTranscriptEvidenceGate && !emittedAfterFinalizationTimeout ? "canon_c_decoupled" : "forced_timeout",
42642
+ blockReason,
42643
+ latestVisibleStatus,
42644
+ approvalResolvedIdle: pending.previousStatus === "waiting_approval",
42645
+ finalAssistantPresent: completionDiagnostic.finalAssistantPresent === true,
42646
+ evidenceSource: completionDiagnostic.finalAssistantEvidenceSource ?? null,
42647
+ lastVisibleRole: completionDiagnostic.lastVisibleRole ?? null,
42648
+ lastVisibleContentLen: completionDiagnostic.lastVisibleContentLength ?? null,
42649
+ emittedAfterFinalizationTimeout,
42650
+ waitedMs,
42651
+ busyEpoch: this.busyEpoch
42652
+ });
42506
42653
  this.pushEvent({
42507
42654
  event: "agent:generating_completed",
42508
42655
  chatTitle: pending.chatTitle,
@@ -42530,6 +42677,14 @@ var CliProviderInstance = class _CliProviderInstance {
42530
42677
  if (this.isMeshWorkerSession()) {
42531
42678
  traceMeshEventStage("fired", this.meshTraceCtx(), `duration=${pending.duration}s`);
42532
42679
  }
42680
+ if (this.completionTraceOn()) this.recordCompletionGateTrace("fire", {
42681
+ path: "clean",
42682
+ latestVisibleStatus,
42683
+ approvalResolvedIdle: pending.previousStatus === "waiting_approval",
42684
+ finalAssistantPresent: true,
42685
+ duration: pending.duration,
42686
+ busyEpoch: this.busyEpoch
42687
+ });
42533
42688
  this.pushEvent({
42534
42689
  event: "agent:generating_completed",
42535
42690
  chatTitle: pending.chatTitle,
@@ -42763,6 +42918,18 @@ var CliProviderInstance = class _CliProviderInstance {
42763
42918
  const previousStatus = this.lastStatus;
42764
42919
  if (newStatus !== this.lastStatus) {
42765
42920
  LOG.info("CLI", `[${this.type}] status: ${this.lastStatus} \u2192 ${newStatus}`);
42921
+ if (this.fsmTraceOn()) this.recordFsmTransitionTrace({
42922
+ from: this.lastStatus,
42923
+ to: newStatus,
42924
+ rawStatus,
42925
+ autoApproveActive,
42926
+ autoApproveHoldIdle,
42927
+ autoApproveBusy: this.autoApproveBusy,
42928
+ hasPending: this.hasAdapterPendingResponse(),
42929
+ busyEpoch: this.busyEpoch,
42930
+ lastOutputAt: typeof adapterStatus?.lastOutputAt === "number" ? adapterStatus.lastOutputAt : null,
42931
+ lastScreenChangeAt: typeof adapterStatus?.lastScreenChangeAt === "number" ? adapterStatus.lastScreenChangeAt : null
42932
+ });
42766
42933
  const startingToGeneratingWithActiveTurn = this.lastStatus === "starting" && newStatus === "generating" && this.hasAdapterPendingResponse();
42767
42934
  if (this.lastStatus === "idle" && newStatus === "generating" || startingToGeneratingWithActiveTurn) {
42768
42935
  if (this.completedDebouncePending && this.generatingStartedAt === 0) {
@@ -42889,6 +43056,17 @@ var CliProviderInstance = class _CliProviderInstance {
42889
43056
  if (this.isMeshWorkerSession()) {
42890
43057
  traceMeshEventStage("arm", this.meshTraceCtx(), `short-generating settle-arm (source=${shortEvidenceSource}, missingEvidence=${missingEvidence})`);
42891
43058
  }
43059
+ if (this.completionTraceOn()) this.recordCompletionGateTrace("arm", {
43060
+ branch: "short_generating",
43061
+ previousStatus: this.lastStatus,
43062
+ turnStartedAt: shortTurnStartedAt || null,
43063
+ busyEpochAtArm: this.busyEpoch,
43064
+ lastOutputAtArm: typeof adapterStatus?.lastOutputAt === "number" ? adapterStatus.lastOutputAt : null,
43065
+ flushDelay: NATIVE_HISTORY_MESH_IDLE_SETTLE_MS,
43066
+ evidenceSource: shortEvidenceSource,
43067
+ missingEvidence,
43068
+ hasFinalSummary: !!shortFinalSummary
43069
+ });
42892
43070
  this.scheduleCompletedDebounceFlush(NATIVE_HISTORY_MESH_IDLE_SETTLE_MS);
42893
43071
  } else if (missingEvidence) {
42894
43072
  LOG.info("CLI", `[${this.type}] short completion suppressed: missing final assistant evidence, non-mesh session (source=${shortEvidenceSource})`);
@@ -42937,6 +43115,16 @@ var CliProviderInstance = class _CliProviderInstance {
42937
43115
  const meshSettleSession = this.isAutonomousMeshSession();
42938
43116
  const flushDelay = ownsExternalHistory ? meshSettleSession ? NATIVE_HISTORY_MESH_IDLE_SETTLE_MS : 0 : 3e3;
42939
43117
  LOG.debug("CLI", `[${this.type}] set completedDebouncePending duration=${duration}s ownsExternalHistory=${ownsExternalHistory} meshSettle=${meshSettleSession} flushDelay=${flushDelay}ms generatingStartedAt=${this.generatingStartedAt}`);
43118
+ if (this.completionTraceOn()) this.recordCompletionGateTrace("arm", {
43119
+ branch: "normal",
43120
+ previousStatus: this.completedDebouncePending.previousStatus,
43121
+ turnStartedAt: this.completedDebouncePending.turnStartedAt ?? null,
43122
+ busyEpochAtArm: this.completedDebouncePending.busyEpochAtArm ?? null,
43123
+ lastOutputAtArm: this.completedDebouncePending.lastOutputAtArm ?? null,
43124
+ flushDelay,
43125
+ ownsExternalHistory,
43126
+ meshSettle: meshSettleSession
43127
+ });
42940
43128
  this.scheduleCompletedDebounceFlush(flushDelay);
42941
43129
  }
42942
43130
  } else if (newStatus === "idle" && this.lastStatus === "starting") {
@@ -47936,12 +48124,17 @@ var ProviderLoader = class _ProviderLoader {
47936
48124
  logFn;
47937
48125
  versionArchive = null;
47938
48126
  scriptsCache = /* @__PURE__ */ new Map();
48127
+ /**
48128
+ * Resolved registry base URL and provider tarball URL. Resolution order:
48129
+ * explicit config field (constructor option) → env var → vendor default.
48130
+ * See `config/registry-resolver.ts`.
48131
+ */
48132
+ registryBaseUrl;
48133
+ providerTarballUrl;
47939
48134
  /** Inject VersionArchive so resolve() can auto-detect installed versions */
47940
48135
  setVersionArchive(archive) {
47941
48136
  this.versionArchive = archive;
47942
48137
  }
47943
- static GITHUB_TARBALL_URL = "https://github.com/vilmire/adhdev-providers/archive/refs/heads/main.tar.gz";
47944
- static REGISTRY_BASE_URL = "https://api.adhf.dev/api/v1/registry";
47945
48138
  static META_FILE = ".meta.json";
47946
48139
  static REGISTRY_META_FILE = ".registry-meta.json";
47947
48140
  static REPO_PROVIDER_DIRNAME = "adhdev-providers";
@@ -48009,6 +48202,8 @@ var ProviderLoader = class _ProviderLoader {
48009
48202
  constructor(options) {
48010
48203
  this.logFn = options?.logFn || LOG.forComponent("Provider").asLogFn();
48011
48204
  this.probeStarts = options?.probeStarts ?? [process.cwd(), __dirname];
48205
+ this.registryBaseUrl = resolveRegistryBaseUrl(options?.registryUrl);
48206
+ this.providerTarballUrl = resolveProviderTarballUrl(options?.providerTarballUrl);
48012
48207
  this.defaultProvidersDir = path35.join(os25.homedir(), ".adhdev", "providers");
48013
48208
  const detected = this.detectDefaultUserDir();
48014
48209
  this.userDir = detected.path;
@@ -49041,7 +49236,7 @@ var ProviderLoader = class _ProviderLoader {
49041
49236
  this.log("Registry sync skipped (sourceMode=no-upstream)");
49042
49237
  return { updated: false };
49043
49238
  }
49044
- this.log(`Registry sync starting (${_ProviderLoader.REGISTRY_BASE_URL})...`);
49239
+ this.log(`Registry sync starting (${this.registryBaseUrl})...`);
49045
49240
  const https = require("https");
49046
49241
  const regMetaPath = path35.join(this.upstreamDir, _ProviderLoader.REGISTRY_META_FILE);
49047
49242
  let cachedChecksums = {};
@@ -49052,7 +49247,7 @@ var ProviderLoader = class _ProviderLoader {
49052
49247
  } catch {
49053
49248
  }
49054
49249
  try {
49055
- const listUrl = `${_ProviderLoader.REGISTRY_BASE_URL}/providers`;
49250
+ const listUrl = `${this.registryBaseUrl}/providers`;
49056
49251
  const listBody = await new Promise((resolve25, reject) => {
49057
49252
  const req = https.get(listUrl, { headers: { "User-Agent": "adhdev-daemon", "Accept": "application/json" }, timeout: 1e4 }, (res) => {
49058
49253
  if (res.statusCode !== 200) {
@@ -49076,7 +49271,7 @@ var ProviderLoader = class _ProviderLoader {
49076
49271
  const { type, category, checksum, version } = entry;
49077
49272
  const cacheKey = `${category}/${type}`;
49078
49273
  if (cachedChecksums[cacheKey] === checksum) continue;
49079
- const dlUrl = `${_ProviderLoader.REGISTRY_BASE_URL}/providers/${type}/${version}/download`;
49274
+ const dlUrl = `${this.registryBaseUrl}/providers/${type}/${version}/download`;
49080
49275
  const manifestBody = await new Promise((resolve25, reject) => {
49081
49276
  const req = https.get(dlUrl, { headers: { "User-Agent": "adhdev-daemon", "Accept": "application/json" }, timeout: 3e4 }, (res) => {
49082
49277
  if (res.statusCode !== 200) {
@@ -49143,12 +49338,13 @@ var ProviderLoader = class _ProviderLoader {
49143
49338
  this.log("Upstream check skipped (last check < 30min ago)");
49144
49339
  return { updated: false };
49145
49340
  }
49341
+ const tarballTarget = resolveProviderTarballTarget(this.providerTarballUrl);
49146
49342
  try {
49147
49343
  const etag = await new Promise((resolve25, reject) => {
49148
49344
  const options = {
49149
49345
  method: "HEAD",
49150
- hostname: "github.com",
49151
- path: "/vilmire/adhdev-providers/archive/refs/heads/main.tar.gz",
49346
+ hostname: tarballTarget.hostname,
49347
+ path: tarballTarget.path,
49152
49348
  headers: { "User-Agent": "adhdev-launcher" },
49153
49349
  timeout: 1e4
49154
49350
  };
@@ -49189,7 +49385,7 @@ var ProviderLoader = class _ProviderLoader {
49189
49385
  this.log("Downloading latest providers from GitHub...");
49190
49386
  const tmpTar = path35.join(os25.tmpdir(), `adhdev-providers-${Date.now()}.tar.gz`);
49191
49387
  const tmpExtract = path35.join(os25.tmpdir(), `adhdev-providers-extract-${Date.now()}`);
49192
- await this.downloadFile(_ProviderLoader.GITHUB_TARBALL_URL, tmpTar);
49388
+ await this.downloadFile(tarballTarget.url, tmpTar);
49193
49389
  fs25.mkdirSync(tmpExtract, { recursive: true });
49194
49390
  await execAsync5(`tar -xzf "${tmpTar}" -C "${tmpExtract}"`, { timeout: 3e4 });
49195
49391
  const extracted = fs25.readdirSync(tmpExtract);
@@ -49289,7 +49485,7 @@ var ProviderLoader = class _ProviderLoader {
49289
49485
  etag,
49290
49486
  timestamp,
49291
49487
  lastCheck: new Date(timestamp).toISOString(),
49292
- source: _ProviderLoader.GITHUB_TARBALL_URL
49488
+ source: this.providerTarballUrl
49293
49489
  }, null, 2));
49294
49490
  } catch {
49295
49491
  }
@@ -50401,6 +50597,47 @@ async function decideOssCloneSync(ossCtx, worktreeOssSha, sourceSha, rg) {
50401
50597
  if (await isAncestor(worktreeOssSha, sourceSha)) return "advance";
50402
50598
  return "skip_diverged";
50403
50599
  }
50600
+ async function syncClonedWorktreeSubmodules(worktreePath, sourceWorkspace, rg) {
50601
+ const submodulePaths = getRegisteredSubmodulePaths(worktreePath);
50602
+ if (submodulePaths.size === 0) return;
50603
+ const sourceCtx = { workspace: sourceWorkspace, repoRoot: sourceWorkspace, isGitRepo: true };
50604
+ const worktreeCtx = { workspace: worktreePath, repoRoot: worktreePath, isGitRepo: true };
50605
+ const readStdout = (out) => (typeof out === "string" ? out : out?.stdout ?? "").trim();
50606
+ for (const submodulePath of submodulePaths) {
50607
+ try {
50608
+ const sourceStatusOut = await rg(sourceCtx, ["submodule", "status", submodulePath], { timeoutMs: 1e4 });
50609
+ const sourceSha = readStdout(sourceStatusOut).match(/^[+\- ]?([0-9a-f]{40})/)?.[1];
50610
+ if (!sourceSha) continue;
50611
+ const subCtx = {
50612
+ workspace: `${worktreePath}/${submodulePath}`,
50613
+ repoRoot: `${worktreePath}/${submodulePath}`,
50614
+ isGitRepo: true
50615
+ };
50616
+ const worktreeSubSha = readStdout(await rg(subCtx, ["rev-parse", "HEAD"], { timeoutMs: 1e4 }));
50617
+ if (!worktreeSubSha || worktreeSubSha === sourceSha) continue;
50618
+ await rg(subCtx, ["fetch", `${sourceWorkspace}/${submodulePath}`, "HEAD"], { timeoutMs: 6e4 });
50619
+ let action;
50620
+ try {
50621
+ action = await decideOssCloneSync(subCtx, worktreeSubSha, sourceSha, rg);
50622
+ } catch (decideErr) {
50623
+ action = "skip_diverged";
50624
+ console.warn(`[mesh] ${submodulePath} submodule sync guard could not resolve ancestry (kept fresh worktree HEAD): ${decideErr?.message ?? decideErr}`);
50625
+ }
50626
+ if (action === "advance") {
50627
+ await rg(subCtx, ["checkout", sourceSha], { timeoutMs: 1e4 });
50628
+ await rg(worktreeCtx, ["add", submodulePath], { timeoutMs: 1e4 });
50629
+ await rg(worktreeCtx, ["commit", "-m", `chore: sync ${submodulePath} to source node HEAD on clone`], { timeoutMs: 1e4 });
50630
+ console.log(`[mesh] Advanced ${submodulePath} submodule to newer source HEAD ${sourceSha.slice(0, 8)} in worktree`);
50631
+ } else if (action === "skip_rewind") {
50632
+ 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`);
50633
+ } else if (action === "skip_diverged") {
50634
+ 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)`);
50635
+ }
50636
+ } catch (subErr) {
50637
+ console.warn(`[mesh] ${submodulePath} submodule sync to source HEAD failed (best-effort):`, subErr?.message ?? subErr);
50638
+ }
50639
+ }
50640
+ }
50404
50641
  var meshCrudHandlers = {
50405
50642
  list_meshes: async (ctx, _args) => {
50406
50643
  try {
@@ -51105,42 +51342,8 @@ var meshCrudHandlers = {
51105
51342
  submodulesInitialized2 = true;
51106
51343
  const sourceWorkspace = sourceNode.repoRoot || sourceNode.workspace;
51107
51344
  if (sourceWorkspace) {
51108
- try {
51109
- const { runGit: rg } = await Promise.resolve().then(() => (init_git_executor(), git_executor_exports));
51110
- const sourceCtx = { workspace: sourceWorkspace, repoRoot: sourceWorkspace, isGitRepo: true };
51111
- const worktreeCtx = { workspace: result.worktreePath, repoRoot: result.worktreePath, isGitRepo: true };
51112
- const sourceStatusOut = await rg(sourceCtx, ["submodule", "status", "oss"], { timeoutMs: 1e4 });
51113
- const sourceStatusLine = (typeof sourceStatusOut === "string" ? sourceStatusOut : sourceStatusOut?.stdout ?? "").trim();
51114
- const sourceShaMatch = sourceStatusLine.match(/^[+\- ]?([0-9a-f]{40})/);
51115
- const sourceSha = sourceShaMatch?.[1];
51116
- if (sourceSha) {
51117
- const ossCtx = { workspace: `${result.worktreePath}/oss`, repoRoot: `${result.worktreePath}/oss`, isGitRepo: true };
51118
- const worktreeOssHeadOut = await rg(ossCtx, ["rev-parse", "HEAD"], { timeoutMs: 1e4 });
51119
- const worktreeOssSha = (typeof worktreeOssHeadOut === "string" ? worktreeOssHeadOut : worktreeOssHeadOut?.stdout ?? "").trim();
51120
- if (worktreeOssSha && worktreeOssSha !== sourceSha) {
51121
- await rg(ossCtx, ["fetch", `${sourceWorkspace}/oss`, "HEAD"], { timeoutMs: 6e4 });
51122
- let ossAction;
51123
- try {
51124
- ossAction = await decideOssCloneSync(ossCtx, worktreeOssSha, sourceSha, rg);
51125
- } catch (decideErr) {
51126
- ossAction = "skip_diverged";
51127
- console.warn(`[mesh] oss submodule sync guard could not resolve ancestry (kept fresh worktree HEAD): ${decideErr?.message ?? decideErr}`);
51128
- }
51129
- if (ossAction === "advance") {
51130
- await rg(ossCtx, ["checkout", sourceSha], { timeoutMs: 1e4 });
51131
- await rg(worktreeCtx, ["add", "oss"], { timeoutMs: 1e4 });
51132
- await rg(worktreeCtx, ["commit", "-m", "chore: sync oss to source node HEAD on clone"], { timeoutMs: 1e4 });
51133
- console.log(`[mesh] Advanced oss submodule to newer source HEAD ${sourceSha.slice(0, 8)} in worktree`);
51134
- } else if (ossAction === "skip_rewind") {
51135
- 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`);
51136
- } else if (ossAction === "skip_diverged") {
51137
- 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)`);
51138
- }
51139
- }
51140
- }
51141
- } catch (ossErr) {
51142
- console.warn("[mesh] oss submodule sync to source HEAD failed (best-effort):", ossErr.message);
51143
- }
51345
+ const { runGit: rg } = await Promise.resolve().then(() => (init_git_executor(), git_executor_exports));
51346
+ await syncClonedWorktreeSubmodules(result.worktreePath, sourceWorkspace, rg);
51144
51347
  }
51145
51348
  } catch (subErr) {
51146
51349
  console.warn("[mesh] Submodule init failed for worktree:", subErr.message);
@@ -52574,6 +52777,26 @@ var import_node_child_process4 = require("child_process");
52574
52777
  var import_node_fs4 = require("fs");
52575
52778
  var import_node_path2 = require("path");
52576
52779
  var PREVIEW_DEPLOY_RECORD = ".adhdev/preview-deploy.json";
52780
+ var PREVIEW_PIPELINE_SCRIPTS = [
52781
+ "scripts/preview-freshness.mjs",
52782
+ "scripts/smoke-preview-web.mjs",
52783
+ "scripts/deploy-preview-local.mjs"
52784
+ ];
52785
+ function hasDeployPreviewNpmScript(repoRoot) {
52786
+ const pkgPath = (0, import_node_path2.resolve)(repoRoot, "package.json");
52787
+ if (!(0, import_node_fs4.existsSync)(pkgPath)) return false;
52788
+ try {
52789
+ const pkg = JSON.parse((0, import_node_fs4.readFileSync)(pkgPath, "utf8"));
52790
+ return typeof pkg?.scripts?.["deploy:preview"] === "string";
52791
+ } catch {
52792
+ return false;
52793
+ }
52794
+ }
52795
+ function isPreviewPipelineConfigured(repoRoot) {
52796
+ if ((0, import_node_fs4.existsSync)((0, import_node_path2.resolve)(repoRoot, PREVIEW_DEPLOY_RECORD))) return true;
52797
+ if (PREVIEW_PIPELINE_SCRIPTS.some((rel) => (0, import_node_fs4.existsSync)((0, import_node_path2.resolve)(repoRoot, rel)))) return true;
52798
+ return hasDeployPreviewNpmScript(repoRoot);
52799
+ }
52577
52800
  function runGit2(repoRoot, args) {
52578
52801
  try {
52579
52802
  return (0, import_node_child_process4.execFileSync)("git", args, {
@@ -52625,6 +52848,7 @@ function readCurrentMainCommit(repoRoot) {
52625
52848
  return { currentMainCommit: null, currentMainCommitSource: "unknown" };
52626
52849
  }
52627
52850
  function buildPreviewFreshness(repoRoot) {
52851
+ if (!isPreviewPipelineConfigured(repoRoot)) return null;
52628
52852
  const current = readCurrentMainCommit(repoRoot);
52629
52853
  const record = readRecord6(repoRoot);
52630
52854
  const lastPreviewCommit = normalizeCommit(record?.lastPreviewCommit);
@@ -66335,7 +66559,9 @@ async function initDaemonComponents(config) {
66335
66559
  const providerLoader = new ProviderLoader({
66336
66560
  logFn: config.providerLogFn,
66337
66561
  sourceMode: providerSourceMode,
66338
- userDir: appConfig.providerDir
66562
+ userDir: appConfig.providerDir,
66563
+ registryUrl: appConfig.registryUrl,
66564
+ providerTarballUrl: appConfig.providerTarballUrl
66339
66565
  });
66340
66566
  providerLoader.loadAll();
66341
66567
  providerLoader.registerToDetector();