@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.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 ? "29441f596e5efe1972b874e0fb5b3d380c9d9bda" : void 0) ?? "unknown";
408
+ const commitShort = readInjected(true ? "29441f59" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
409
+ const version = readInjected(true ? "0.9.82-rc.457" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
410
+ const builtAt = readInjected(true ? "2026-07-04T08:51:27.287Z" : 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
  };
@@ -8925,6 +8927,7 @@ __export(worktree_bootstrap_config_exports, {
8925
8927
  WORKTREE_BOOTSTRAP_STALE_RUNNING_MS: () => WORKTREE_BOOTSTRAP_STALE_RUNNING_MS,
8926
8928
  computeStaleInputsDigest: () => computeStaleInputsDigest,
8927
8929
  evaluateWorktreeBootstrapState: () => evaluateWorktreeBootstrapState,
8930
+ getRegisteredSubmodulePaths: () => getRegisteredSubmodulePaths,
8928
8931
  isWorktreeBootstrapStaleRunning: () => isWorktreeBootstrapStaleRunning,
8929
8932
  loadMeshWorktreeBootstrapConfig: () => loadMeshWorktreeBootstrapConfig,
8930
8933
  runMeshWorktreeBootstrap: () => runMeshWorktreeBootstrap,
@@ -30282,6 +30285,35 @@ var DaemonCdpInitializer = class {
30282
30285
 
30283
30286
  // src/commands/handler.ts
30284
30287
  init_builders();
30288
+ init_config();
30289
+
30290
+ // src/config/registry-resolver.ts
30291
+ var DEFAULT_REGISTRY_BASE_URL = "https://api.adhf.dev/api/v1/registry";
30292
+ var DEFAULT_PROVIDER_TARBALL_URL = "https://github.com/vilmire/adhdev-providers/archive/refs/heads/main.tar.gz";
30293
+ var REGISTRY_URL_ENV_VAR = "ADHDEV_REGISTRY_URL";
30294
+ var PROVIDER_TARBALL_URL_ENV_VAR = "ADHDEV_PROVIDER_TARBALL_URL";
30295
+ function cleanString(value) {
30296
+ return typeof value === "string" && value.trim() ? value.trim() : void 0;
30297
+ }
30298
+ function stripTrailingSlashes(url) {
30299
+ return url.replace(/\/+$/, "");
30300
+ }
30301
+ function resolveRegistryBaseUrl(configuredUrl, env = process.env) {
30302
+ const resolved = cleanString(configuredUrl) ?? cleanString(env[REGISTRY_URL_ENV_VAR]) ?? DEFAULT_REGISTRY_BASE_URL;
30303
+ return stripTrailingSlashes(resolved);
30304
+ }
30305
+ function resolveProviderTarballUrl(configuredUrl, env = process.env) {
30306
+ return cleanString(configuredUrl) ?? cleanString(env[PROVIDER_TARBALL_URL_ENV_VAR]) ?? DEFAULT_PROVIDER_TARBALL_URL;
30307
+ }
30308
+ function resolveProviderTarballTarget(configuredUrl, env = process.env) {
30309
+ const url = resolveProviderTarballUrl(configuredUrl, env);
30310
+ const parsed = new URL(url);
30311
+ return {
30312
+ url,
30313
+ hostname: parsed.hostname,
30314
+ path: parsed.pathname + (parsed.search || "")
30315
+ };
30316
+ }
30285
30317
 
30286
30318
  // src/sessions/reconcile.ts
30287
30319
  function upsertSessionTarget(sessionRegistry, target) {
@@ -35179,7 +35211,7 @@ var DaemonCommandHandler = class {
35179
35211
  const https = __require("https");
35180
35212
  const fs40 = __require("fs");
35181
35213
  const path44 = __require("path");
35182
- const REGISTRY = "https://api.adhf.dev/api/v1/registry";
35214
+ const REGISTRY = resolveRegistryBaseUrl(loadConfig().registryUrl);
35183
35215
  function fetchText(url, timeoutMs) {
35184
35216
  return new Promise((resolve25, reject) => {
35185
35217
  const req = https.get(url, { headers: { "User-Agent": "adhdev-daemon", "Accept": "application/json" }, timeout: timeoutMs }, (res) => {
@@ -35541,7 +35573,7 @@ ${formatManifestValidationIssues2(validation.issues)}`,
35541
35573
  const installed = this.handleListInstalledProviders({});
35542
35574
  if (!installed.success) return installed;
35543
35575
  const https = __require("https");
35544
- const REGISTRY = "https://api.adhf.dev/api/v1/registry";
35576
+ const REGISTRY = resolveRegistryBaseUrl(loadConfig().registryUrl);
35545
35577
  function fetchJson(url) {
35546
35578
  return new Promise((resolve25, reject) => {
35547
35579
  const req = https.get(url, { headers: { "User-Agent": "adhdev-daemon", "Accept": "application/json" }, timeout: 1e4 }, (res) => {
@@ -37144,6 +37176,7 @@ var CHANNEL_SERVER_URL = {
37144
37176
  stable: "https://api.adhf.dev",
37145
37177
  preview: "https://api-preview.adhf.dev"
37146
37178
  };
37179
+ var VENDOR_SERVER_URLS = new Set(Object.values(CHANNEL_SERVER_URL));
37147
37180
  function normalizeReleaseChannel(value) {
37148
37181
  if (typeof value !== "string") return null;
37149
37182
  const normalized = value.trim().toLowerCase();
@@ -37165,7 +37198,11 @@ var daemonLifecycleHandlers = {
37165
37198
  const npmTag = CHANNEL_NPM_TAG[channel];
37166
37199
  const latest = String(execNpmCommandSync(["view", `${pkgName}@${npmTag}`, "version"], { encoding: "utf-8", timeout: 1e4 }, npmSurface)).trim();
37167
37200
  LOG.info("Upgrade", `Latest ${pkgName}@${npmTag}: v${latest}`);
37168
- updateConfig({ updateChannel: channel, serverUrl: CHANNEL_SERVER_URL[channel] });
37201
+ const currentServerUrl = typeof loadConfig().serverUrl === "string" ? loadConfig().serverUrl.trim() : "";
37202
+ const useVendorServerUrl = currentServerUrl === "" || VENDOR_SERVER_URLS.has(currentServerUrl);
37203
+ updateConfig(
37204
+ useVendorServerUrl ? { updateChannel: channel, serverUrl: CHANNEL_SERVER_URL[channel] } : { updateChannel: channel }
37205
+ );
37169
37206
  let currentInstalled = null;
37170
37207
  try {
37171
37208
  const currentJson = String(execNpmCommandSync(["ls", "-g", pkgName, "--depth=0", "--json"], {
@@ -37873,6 +37910,7 @@ function applyPreLaunchTrust(trust, workingDir) {
37873
37910
 
37874
37911
  // src/providers/spec/fsm-driver.ts
37875
37912
  init_logger();
37913
+ init_debug_config();
37876
37914
  init_pty_write_chunking();
37877
37915
  function countNewlines(s2) {
37878
37916
  let n = 0;
@@ -37941,6 +37979,11 @@ var FsmDriver = class {
37941
37979
  * (−1 = whole screen), or a `section:<id>` / `<region>#ignore:<pat>` string
37942
37980
  * when the clause scopes to a section or declares an ignore_lines filter. */
37943
37981
  regionLastChangedAt = /* @__PURE__ */ new Map();
37982
+ /** COMPLETION-EARLYNOTIFY stable-eval trace: last stable/not-stable verdict
37983
+ * recorded per stable region, so the trace fires only when the verdict FLIPS
37984
+ * (not every quiet frame). Cleared on every transition alongside
37985
+ * regionLastChangedAt. Diagnostic-only — never consulted by the FSM. */
37986
+ stableVerdictCache = /* @__PURE__ */ new Map();
37944
37987
  /** Timer that re-runs evaluate() when a time-condition would flip true
37945
37988
  * with no PTY frame to trigger it. */
37946
37989
  wakeTimer = null;
@@ -38276,6 +38319,7 @@ var FsmDriver = class {
38276
38319
  this.currentStateId = fired.to;
38277
38320
  this.stateEnteredAt = now;
38278
38321
  this.regionLastChangedAt.clear();
38322
+ this.stableVerdictCache.clear();
38279
38323
  this.pushHistory(fired.to, stateById(this.spec, fired.to)?.label ?? fired.to, {
38280
38324
  reason: "transition",
38281
38325
  via: `${from}\u2192${fired.to}`,
@@ -38380,6 +38424,7 @@ var FsmDriver = class {
38380
38424
  trackRegionChanges(currentLines, cursor, now) {
38381
38425
  if (this.prevScreenLines.length === 0) return;
38382
38426
  const descs = this.stableRegionDescriptors();
38427
+ const stableTraceOn = shouldCollectTraceCategory("fsm-transition");
38383
38428
  const needsSections = descs.some((d) => !!d.section);
38384
38429
  const curSections = needsSections ? resolveSections(this.spec.sections ?? {}, currentLines) : [];
38385
38430
  const prevSections = needsSections ? resolveSections(this.spec.sections ?? {}, this.prevScreenLines) : [];
@@ -38400,6 +38445,28 @@ var FsmDriver = class {
38400
38445
  const cur = filterIgnoredLines(curLines, d.ignoreRe).join("\n");
38401
38446
  const prev = filterIgnoredLines(prevLines, d.ignoreRe).join("\n");
38402
38447
  if (cur !== prev) this.regionLastChangedAt.set(d.key, now);
38448
+ if (stableTraceOn && typeof d.holdMs === "number") {
38449
+ const lastChanged = this.regionLastChangedAt.get(d.key) ?? this.stateEnteredAt;
38450
+ const ageMs = now - lastChanged;
38451
+ const verdict = ageMs >= d.holdMs;
38452
+ if (this.stableVerdictCache.get(d.key) !== verdict) {
38453
+ this.stableVerdictCache.set(d.key, verdict);
38454
+ recordDebugTrace({
38455
+ category: "fsm-transition",
38456
+ stage: "stable-eval",
38457
+ level: "debug",
38458
+ payload: {
38459
+ state: this.currentStateId,
38460
+ regionKey: String(d.key),
38461
+ ignorePattern: d.ignoreRe?.source ?? null,
38462
+ fingerprintLen: cur.length,
38463
+ ageMs,
38464
+ holdMs: d.holdMs,
38465
+ verdict
38466
+ }
38467
+ });
38468
+ }
38469
+ }
38403
38470
  }
38404
38471
  }
38405
38472
  /** Every distinct stable-region descriptor referenced by stable_ms
@@ -38915,7 +38982,8 @@ function collectStableDescriptors(when, byKey) {
38915
38982
  const w = when;
38916
38983
  if ("stable_ms" in w) {
38917
38984
  const key2 = stableRegionKey(w);
38918
- if (!byKey.has(key2)) {
38985
+ const existing = byKey.get(key2);
38986
+ if (!existing) {
38919
38987
  let ignoreRe;
38920
38988
  if (w.ignore_lines) {
38921
38989
  try {
@@ -38923,7 +38991,9 @@ function collectStableDescriptors(when, byKey) {
38923
38991
  } catch {
38924
38992
  }
38925
38993
  }
38926
- byKey.set(key2, { key: key2, section: w.section, cursor_above: w.cursor_above, ignoreRe });
38994
+ 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 });
38995
+ } else if (existing.holdMs === void 0 && typeof w.stable_ms === "number") {
38996
+ existing.holdMs = w.stable_ms;
38927
38997
  }
38928
38998
  return;
38929
38999
  }
@@ -40695,6 +40765,7 @@ function createCliAdapter(provider, workingDir, cliArgs, extraEnv, transportFact
40695
40765
 
40696
40766
  // src/providers/cli-provider-instance.ts
40697
40767
  init_logger();
40768
+ init_debug_config();
40698
40769
  init_mesh_event_trace();
40699
40770
  init_control_effects();
40700
40771
  init_approval_utils();
@@ -41768,9 +41839,10 @@ var CliProviderInstance = class _CliProviderInstance {
41768
41839
  return restoredHistory.messages;
41769
41840
  }
41770
41841
  completionFinalAssistantEvidence(parsedMessages, turnStartedAt) {
41842
+ const turnClosed = !this.hasAdapterPendingResponse();
41771
41843
  if (this.completionHasFinalAssistantMessage(parsedMessages, turnStartedAt)) {
41772
41844
  return {
41773
- present: true,
41845
+ present: turnClosed,
41774
41846
  messages: Array.isArray(parsedMessages) ? parsedMessages : [],
41775
41847
  source: "parsed"
41776
41848
  };
@@ -41778,7 +41850,7 @@ var CliProviderInstance = class _CliProviderInstance {
41778
41850
  const externalMessages = this.readExternalCompletionMessages();
41779
41851
  if (externalMessages) {
41780
41852
  return {
41781
- present: this.completionHasFinalAssistantMessage(externalMessages, turnStartedAt),
41853
+ present: turnClosed && this.completionHasFinalAssistantMessage(externalMessages, turnStartedAt),
41782
41854
  messages: externalMessages,
41783
41855
  source: "external-native"
41784
41856
  };
@@ -41877,11 +41949,9 @@ var CliProviderInstance = class _CliProviderInstance {
41877
41949
  if (latestVisibleStatus !== "idle") return { reason: `status:${latestVisibleStatus}`, terminal: true };
41878
41950
  const adapterAny = this.adapter;
41879
41951
  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
- }
41952
+ if (adapterAny?.isWaitingForResponse === true) return { reason: "adapter_waiting_for_response", terminal: !approvalResolvedIdle };
41953
+ if (adapterAny?.currentTurnScope) return { reason: "adapter_turn_scope_active", terminal: !approvalResolvedIdle };
41954
+ if (this.hasAdapterPendingResponse()) return { reason: "adapter_pending_response", terminal: !approvalResolvedIdle };
41885
41955
  const partial = typeof this.adapter.getPartialResponse === "function" ? this.adapter.getPartialResponse() : "";
41886
41956
  if (typeof partial === "string" && partial.trim()) return { reason: "partial_response_pending", terminal: true };
41887
41957
  let parsed;
@@ -42036,6 +42106,39 @@ var CliProviderInstance = class _CliProviderInstance {
42036
42106
  event
42037
42107
  };
42038
42108
  }
42109
+ // COMPLETION-EARLYNOTIFY instrumentation. A session-keyed FSM-transition +
42110
+ // completion-gate snapshot recorded into the shared debug-trace ring buffer
42111
+ // (secret-safe, length/role/pattern-name only — never screen or bubble text).
42112
+ // Retrieved via getRecentDebugTrace (chat_debug_bundle). Both categories are a
42113
+ // no-op unless collectDebugTrace is on AND the category is selected, so the
42114
+ // hot-path guards below (completionTraceOn / fsmTraceOn) keep production cost
42115
+ // at a single boolean check.
42116
+ completionTraceOn() {
42117
+ return shouldCollectTraceCategory("completion-gate");
42118
+ }
42119
+ fsmTraceOn() {
42120
+ return shouldCollectTraceCategory("fsm-transition");
42121
+ }
42122
+ recordCompletionGateTrace(stage, payload) {
42123
+ recordDebugTrace({
42124
+ category: "completion-gate",
42125
+ stage,
42126
+ level: "debug",
42127
+ sessionId: this.instanceId,
42128
+ providerType: this.type,
42129
+ payload
42130
+ });
42131
+ }
42132
+ recordFsmTransitionTrace(payload) {
42133
+ recordDebugTrace({
42134
+ category: "fsm-transition",
42135
+ stage: "transition",
42136
+ level: "debug",
42137
+ sessionId: this.instanceId,
42138
+ providerType: this.type,
42139
+ payload
42140
+ });
42141
+ }
42039
42142
  flushCompletedDebounceIfFinalized() {
42040
42143
  const pending = this.completedDebouncePending;
42041
42144
  if (!pending) {
@@ -42048,12 +42151,27 @@ var CliProviderInstance = class _CliProviderInstance {
42048
42151
  LOG.debug("CLI", `[${this.type}] flush attempt: adapterStatus=${latestStatus.status} latestVisible=${latestVisibleStatus} generatingStartedAt=${this.generatingStartedAt} isWaitingForResponse=${!!this.adapter?.isWaitingForResponse} hasPartial=${!!this.adapter.getPartialResponse?.()}`);
42049
42152
  if (latestVisibleStatus !== "idle") {
42050
42153
  LOG.info("CLI", `[${this.type}] cancelled pending completed (resumed ${latestVisibleStatus})`);
42154
+ if (this.completionTraceOn()) this.recordCompletionGateTrace("cancel", {
42155
+ blockReason: "resumed_status",
42156
+ latestVisibleStatus,
42157
+ previousStatus: pending.previousStatus,
42158
+ busyEpochAtArm: pending.busyEpochAtArm,
42159
+ busyEpoch: this.busyEpoch
42160
+ });
42051
42161
  this.completedDebouncePending = null;
42052
42162
  this.completedDebounceTimer = null;
42053
42163
  return;
42054
42164
  }
42055
42165
  if (typeof pending.busyEpochAtArm === "number" && this.busyEpoch !== pending.busyEpochAtArm) {
42056
42166
  LOG.info("CLI", `[${this.type}] cancelled pending completed (busy re-entry during settle: epoch ${pending.busyEpochAtArm}\u2192${this.busyEpoch})`);
42167
+ if (this.completionTraceOn()) this.recordCompletionGateTrace("cancel", {
42168
+ blockReason: "busy_reentry",
42169
+ latestVisibleStatus,
42170
+ previousStatus: pending.previousStatus,
42171
+ busyEpochAtArm: pending.busyEpochAtArm,
42172
+ busyEpoch: this.busyEpoch,
42173
+ busyEpochDelta: this.busyEpoch - pending.busyEpochAtArm
42174
+ });
42057
42175
  this.completedDebouncePending = null;
42058
42176
  this.completedDebounceTimer = null;
42059
42177
  return;
@@ -42061,6 +42179,14 @@ var CliProviderInstance = class _CliProviderInstance {
42061
42179
  const latestOutputAt = typeof latestStatus?.lastOutputAt === "number" ? latestStatus.lastOutputAt : void 0;
42062
42180
  if (typeof pending.lastOutputAtArm === "number" && typeof latestOutputAt === "number" && latestOutputAt > pending.lastOutputAtArm) {
42063
42181
  LOG.info("CLI", `[${this.type}] cancelled pending completed (new PTY output during settle: ${pending.lastOutputAtArm}\u2192${latestOutputAt})`);
42182
+ if (this.completionTraceOn()) this.recordCompletionGateTrace("cancel", {
42183
+ blockReason: "new_pty_output",
42184
+ latestVisibleStatus,
42185
+ previousStatus: pending.previousStatus,
42186
+ lastOutputAtArm: pending.lastOutputAtArm,
42187
+ lastOutputAt: latestOutputAt,
42188
+ lastOutputAtDelta: latestOutputAt - pending.lastOutputAtArm
42189
+ });
42064
42190
  this.completedDebouncePending = null;
42065
42191
  this.completedDebounceTimer = null;
42066
42192
  return;
@@ -42077,6 +42203,14 @@ var CliProviderInstance = class _CliProviderInstance {
42077
42203
  if (this.isMeshWorkerSession()) {
42078
42204
  traceMeshEventDrop("completion_gate_hold", this.meshTraceCtx(), `${blockReason} waited=${waitedMs}ms`);
42079
42205
  }
42206
+ if (this.completionTraceOn()) this.recordCompletionGateTrace("hold", {
42207
+ blockReason,
42208
+ latestVisibleStatus,
42209
+ terminal: block2.terminal === true,
42210
+ holdForTranscript: block2.holdForTranscript === true,
42211
+ approvalResolvedIdle: pending.previousStatus === "waiting_approval",
42212
+ waitedMs
42213
+ });
42080
42214
  pending.loggedBlockReason = blockReason;
42081
42215
  }
42082
42216
  this.scheduleCompletedDebounceFlush(COMPLETED_FINALIZATION_RETRY_MS);
@@ -42096,6 +42230,19 @@ var CliProviderInstance = class _CliProviderInstance {
42096
42230
  if (this.isMeshWorkerSession()) {
42097
42231
  traceMeshEventStage("fired", this.meshTraceCtx(), `forced after ${waitedMs}ms (${blockReason})`);
42098
42232
  }
42233
+ if (this.completionTraceOn()) this.recordCompletionGateTrace("fire", {
42234
+ path: isTranscriptEvidenceGate && !emittedAfterFinalizationTimeout ? "canon_c_decoupled" : "forced_timeout",
42235
+ blockReason,
42236
+ latestVisibleStatus,
42237
+ approvalResolvedIdle: pending.previousStatus === "waiting_approval",
42238
+ finalAssistantPresent: completionDiagnostic.finalAssistantPresent === true,
42239
+ evidenceSource: completionDiagnostic.finalAssistantEvidenceSource ?? null,
42240
+ lastVisibleRole: completionDiagnostic.lastVisibleRole ?? null,
42241
+ lastVisibleContentLen: completionDiagnostic.lastVisibleContentLength ?? null,
42242
+ emittedAfterFinalizationTimeout,
42243
+ waitedMs,
42244
+ busyEpoch: this.busyEpoch
42245
+ });
42099
42246
  this.pushEvent({
42100
42247
  event: "agent:generating_completed",
42101
42248
  chatTitle: pending.chatTitle,
@@ -42123,6 +42270,14 @@ var CliProviderInstance = class _CliProviderInstance {
42123
42270
  if (this.isMeshWorkerSession()) {
42124
42271
  traceMeshEventStage("fired", this.meshTraceCtx(), `duration=${pending.duration}s`);
42125
42272
  }
42273
+ if (this.completionTraceOn()) this.recordCompletionGateTrace("fire", {
42274
+ path: "clean",
42275
+ latestVisibleStatus,
42276
+ approvalResolvedIdle: pending.previousStatus === "waiting_approval",
42277
+ finalAssistantPresent: true,
42278
+ duration: pending.duration,
42279
+ busyEpoch: this.busyEpoch
42280
+ });
42126
42281
  this.pushEvent({
42127
42282
  event: "agent:generating_completed",
42128
42283
  chatTitle: pending.chatTitle,
@@ -42356,6 +42511,18 @@ var CliProviderInstance = class _CliProviderInstance {
42356
42511
  const previousStatus = this.lastStatus;
42357
42512
  if (newStatus !== this.lastStatus) {
42358
42513
  LOG.info("CLI", `[${this.type}] status: ${this.lastStatus} \u2192 ${newStatus}`);
42514
+ if (this.fsmTraceOn()) this.recordFsmTransitionTrace({
42515
+ from: this.lastStatus,
42516
+ to: newStatus,
42517
+ rawStatus,
42518
+ autoApproveActive,
42519
+ autoApproveHoldIdle,
42520
+ autoApproveBusy: this.autoApproveBusy,
42521
+ hasPending: this.hasAdapterPendingResponse(),
42522
+ busyEpoch: this.busyEpoch,
42523
+ lastOutputAt: typeof adapterStatus?.lastOutputAt === "number" ? adapterStatus.lastOutputAt : null,
42524
+ lastScreenChangeAt: typeof adapterStatus?.lastScreenChangeAt === "number" ? adapterStatus.lastScreenChangeAt : null
42525
+ });
42359
42526
  const startingToGeneratingWithActiveTurn = this.lastStatus === "starting" && newStatus === "generating" && this.hasAdapterPendingResponse();
42360
42527
  if (this.lastStatus === "idle" && newStatus === "generating" || startingToGeneratingWithActiveTurn) {
42361
42528
  if (this.completedDebouncePending && this.generatingStartedAt === 0) {
@@ -42482,6 +42649,17 @@ var CliProviderInstance = class _CliProviderInstance {
42482
42649
  if (this.isMeshWorkerSession()) {
42483
42650
  traceMeshEventStage("arm", this.meshTraceCtx(), `short-generating settle-arm (source=${shortEvidenceSource}, missingEvidence=${missingEvidence})`);
42484
42651
  }
42652
+ if (this.completionTraceOn()) this.recordCompletionGateTrace("arm", {
42653
+ branch: "short_generating",
42654
+ previousStatus: this.lastStatus,
42655
+ turnStartedAt: shortTurnStartedAt || null,
42656
+ busyEpochAtArm: this.busyEpoch,
42657
+ lastOutputAtArm: typeof adapterStatus?.lastOutputAt === "number" ? adapterStatus.lastOutputAt : null,
42658
+ flushDelay: NATIVE_HISTORY_MESH_IDLE_SETTLE_MS,
42659
+ evidenceSource: shortEvidenceSource,
42660
+ missingEvidence,
42661
+ hasFinalSummary: !!shortFinalSummary
42662
+ });
42485
42663
  this.scheduleCompletedDebounceFlush(NATIVE_HISTORY_MESH_IDLE_SETTLE_MS);
42486
42664
  } else if (missingEvidence) {
42487
42665
  LOG.info("CLI", `[${this.type}] short completion suppressed: missing final assistant evidence, non-mesh session (source=${shortEvidenceSource})`);
@@ -42530,6 +42708,16 @@ var CliProviderInstance = class _CliProviderInstance {
42530
42708
  const meshSettleSession = this.isAutonomousMeshSession();
42531
42709
  const flushDelay = ownsExternalHistory ? meshSettleSession ? NATIVE_HISTORY_MESH_IDLE_SETTLE_MS : 0 : 3e3;
42532
42710
  LOG.debug("CLI", `[${this.type}] set completedDebouncePending duration=${duration}s ownsExternalHistory=${ownsExternalHistory} meshSettle=${meshSettleSession} flushDelay=${flushDelay}ms generatingStartedAt=${this.generatingStartedAt}`);
42711
+ if (this.completionTraceOn()) this.recordCompletionGateTrace("arm", {
42712
+ branch: "normal",
42713
+ previousStatus: this.completedDebouncePending.previousStatus,
42714
+ turnStartedAt: this.completedDebouncePending.turnStartedAt ?? null,
42715
+ busyEpochAtArm: this.completedDebouncePending.busyEpochAtArm ?? null,
42716
+ lastOutputAtArm: this.completedDebouncePending.lastOutputAtArm ?? null,
42717
+ flushDelay,
42718
+ ownsExternalHistory,
42719
+ meshSettle: meshSettleSession
42720
+ });
42533
42721
  this.scheduleCompletedDebounceFlush(flushDelay);
42534
42722
  }
42535
42723
  } else if (newStatus === "idle" && this.lastStatus === "starting") {
@@ -47534,12 +47722,17 @@ var ProviderLoader = class _ProviderLoader {
47534
47722
  logFn;
47535
47723
  versionArchive = null;
47536
47724
  scriptsCache = /* @__PURE__ */ new Map();
47725
+ /**
47726
+ * Resolved registry base URL and provider tarball URL. Resolution order:
47727
+ * explicit config field (constructor option) → env var → vendor default.
47728
+ * See `config/registry-resolver.ts`.
47729
+ */
47730
+ registryBaseUrl;
47731
+ providerTarballUrl;
47537
47732
  /** Inject VersionArchive so resolve() can auto-detect installed versions */
47538
47733
  setVersionArchive(archive) {
47539
47734
  this.versionArchive = archive;
47540
47735
  }
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
47736
  static META_FILE = ".meta.json";
47544
47737
  static REGISTRY_META_FILE = ".registry-meta.json";
47545
47738
  static REPO_PROVIDER_DIRNAME = "adhdev-providers";
@@ -47607,6 +47800,8 @@ var ProviderLoader = class _ProviderLoader {
47607
47800
  constructor(options) {
47608
47801
  this.logFn = options?.logFn || LOG.forComponent("Provider").asLogFn();
47609
47802
  this.probeStarts = options?.probeStarts ?? [process.cwd(), __dirname];
47803
+ this.registryBaseUrl = resolveRegistryBaseUrl(options?.registryUrl);
47804
+ this.providerTarballUrl = resolveProviderTarballUrl(options?.providerTarballUrl);
47610
47805
  this.defaultProvidersDir = path35.join(os25.homedir(), ".adhdev", "providers");
47611
47806
  const detected = this.detectDefaultUserDir();
47612
47807
  this.userDir = detected.path;
@@ -48639,7 +48834,7 @@ var ProviderLoader = class _ProviderLoader {
48639
48834
  this.log("Registry sync skipped (sourceMode=no-upstream)");
48640
48835
  return { updated: false };
48641
48836
  }
48642
- this.log(`Registry sync starting (${_ProviderLoader.REGISTRY_BASE_URL})...`);
48837
+ this.log(`Registry sync starting (${this.registryBaseUrl})...`);
48643
48838
  const https = __require("https");
48644
48839
  const regMetaPath = path35.join(this.upstreamDir, _ProviderLoader.REGISTRY_META_FILE);
48645
48840
  let cachedChecksums = {};
@@ -48650,7 +48845,7 @@ var ProviderLoader = class _ProviderLoader {
48650
48845
  } catch {
48651
48846
  }
48652
48847
  try {
48653
- const listUrl = `${_ProviderLoader.REGISTRY_BASE_URL}/providers`;
48848
+ const listUrl = `${this.registryBaseUrl}/providers`;
48654
48849
  const listBody = await new Promise((resolve25, reject) => {
48655
48850
  const req = https.get(listUrl, { headers: { "User-Agent": "adhdev-daemon", "Accept": "application/json" }, timeout: 1e4 }, (res) => {
48656
48851
  if (res.statusCode !== 200) {
@@ -48674,7 +48869,7 @@ var ProviderLoader = class _ProviderLoader {
48674
48869
  const { type, category, checksum, version } = entry;
48675
48870
  const cacheKey = `${category}/${type}`;
48676
48871
  if (cachedChecksums[cacheKey] === checksum) continue;
48677
- const dlUrl = `${_ProviderLoader.REGISTRY_BASE_URL}/providers/${type}/${version}/download`;
48872
+ const dlUrl = `${this.registryBaseUrl}/providers/${type}/${version}/download`;
48678
48873
  const manifestBody = await new Promise((resolve25, reject) => {
48679
48874
  const req = https.get(dlUrl, { headers: { "User-Agent": "adhdev-daemon", "Accept": "application/json" }, timeout: 3e4 }, (res) => {
48680
48875
  if (res.statusCode !== 200) {
@@ -48741,12 +48936,13 @@ var ProviderLoader = class _ProviderLoader {
48741
48936
  this.log("Upstream check skipped (last check < 30min ago)");
48742
48937
  return { updated: false };
48743
48938
  }
48939
+ const tarballTarget = resolveProviderTarballTarget(this.providerTarballUrl);
48744
48940
  try {
48745
48941
  const etag = await new Promise((resolve25, reject) => {
48746
48942
  const options = {
48747
48943
  method: "HEAD",
48748
- hostname: "github.com",
48749
- path: "/vilmire/adhdev-providers/archive/refs/heads/main.tar.gz",
48944
+ hostname: tarballTarget.hostname,
48945
+ path: tarballTarget.path,
48750
48946
  headers: { "User-Agent": "adhdev-launcher" },
48751
48947
  timeout: 1e4
48752
48948
  };
@@ -48787,7 +48983,7 @@ var ProviderLoader = class _ProviderLoader {
48787
48983
  this.log("Downloading latest providers from GitHub...");
48788
48984
  const tmpTar = path35.join(os25.tmpdir(), `adhdev-providers-${Date.now()}.tar.gz`);
48789
48985
  const tmpExtract = path35.join(os25.tmpdir(), `adhdev-providers-extract-${Date.now()}`);
48790
- await this.downloadFile(_ProviderLoader.GITHUB_TARBALL_URL, tmpTar);
48986
+ await this.downloadFile(tarballTarget.url, tmpTar);
48791
48987
  fs25.mkdirSync(tmpExtract, { recursive: true });
48792
48988
  await execAsync5(`tar -xzf "${tmpTar}" -C "${tmpExtract}"`, { timeout: 3e4 });
48793
48989
  const extracted = fs25.readdirSync(tmpExtract);
@@ -48887,7 +49083,7 @@ var ProviderLoader = class _ProviderLoader {
48887
49083
  etag,
48888
49084
  timestamp,
48889
49085
  lastCheck: new Date(timestamp).toISOString(),
48890
- source: _ProviderLoader.GITHUB_TARBALL_URL
49086
+ source: this.providerTarballUrl
48891
49087
  }, null, 2));
48892
49088
  } catch {
48893
49089
  }
@@ -49999,6 +50195,47 @@ async function decideOssCloneSync(ossCtx, worktreeOssSha, sourceSha, rg) {
49999
50195
  if (await isAncestor(worktreeOssSha, sourceSha)) return "advance";
50000
50196
  return "skip_diverged";
50001
50197
  }
50198
+ async function syncClonedWorktreeSubmodules(worktreePath, sourceWorkspace, rg) {
50199
+ const submodulePaths = getRegisteredSubmodulePaths(worktreePath);
50200
+ if (submodulePaths.size === 0) return;
50201
+ const sourceCtx = { workspace: sourceWorkspace, repoRoot: sourceWorkspace, isGitRepo: true };
50202
+ const worktreeCtx = { workspace: worktreePath, repoRoot: worktreePath, isGitRepo: true };
50203
+ const readStdout = (out) => (typeof out === "string" ? out : out?.stdout ?? "").trim();
50204
+ for (const submodulePath of submodulePaths) {
50205
+ try {
50206
+ const sourceStatusOut = await rg(sourceCtx, ["submodule", "status", submodulePath], { timeoutMs: 1e4 });
50207
+ const sourceSha = readStdout(sourceStatusOut).match(/^[+\- ]?([0-9a-f]{40})/)?.[1];
50208
+ if (!sourceSha) continue;
50209
+ const subCtx = {
50210
+ workspace: `${worktreePath}/${submodulePath}`,
50211
+ repoRoot: `${worktreePath}/${submodulePath}`,
50212
+ isGitRepo: true
50213
+ };
50214
+ const worktreeSubSha = readStdout(await rg(subCtx, ["rev-parse", "HEAD"], { timeoutMs: 1e4 }));
50215
+ if (!worktreeSubSha || worktreeSubSha === sourceSha) continue;
50216
+ await rg(subCtx, ["fetch", `${sourceWorkspace}/${submodulePath}`, "HEAD"], { timeoutMs: 6e4 });
50217
+ let action;
50218
+ try {
50219
+ action = await decideOssCloneSync(subCtx, worktreeSubSha, sourceSha, rg);
50220
+ } catch (decideErr) {
50221
+ action = "skip_diverged";
50222
+ console.warn(`[mesh] ${submodulePath} submodule sync guard could not resolve ancestry (kept fresh worktree HEAD): ${decideErr?.message ?? decideErr}`);
50223
+ }
50224
+ if (action === "advance") {
50225
+ await rg(subCtx, ["checkout", sourceSha], { timeoutMs: 1e4 });
50226
+ await rg(worktreeCtx, ["add", submodulePath], { timeoutMs: 1e4 });
50227
+ await rg(worktreeCtx, ["commit", "-m", `chore: sync ${submodulePath} to source node HEAD on clone`], { timeoutMs: 1e4 });
50228
+ console.log(`[mesh] Advanced ${submodulePath} submodule to newer source HEAD ${sourceSha.slice(0, 8)} in worktree`);
50229
+ } else if (action === "skip_rewind") {
50230
+ 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`);
50231
+ } else if (action === "skip_diverged") {
50232
+ 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)`);
50233
+ }
50234
+ } catch (subErr) {
50235
+ console.warn(`[mesh] ${submodulePath} submodule sync to source HEAD failed (best-effort):`, subErr?.message ?? subErr);
50236
+ }
50237
+ }
50238
+ }
50002
50239
  var meshCrudHandlers = {
50003
50240
  list_meshes: async (ctx, _args) => {
50004
50241
  try {
@@ -50703,42 +50940,8 @@ var meshCrudHandlers = {
50703
50940
  submodulesInitialized2 = true;
50704
50941
  const sourceWorkspace = sourceNode.repoRoot || sourceNode.workspace;
50705
50942
  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
- }
50943
+ const { runGit: rg } = await Promise.resolve().then(() => (init_git_executor(), git_executor_exports));
50944
+ await syncClonedWorktreeSubmodules(result.worktreePath, sourceWorkspace, rg);
50742
50945
  }
50743
50946
  } catch (subErr) {
50744
50947
  console.warn("[mesh] Submodule init failed for worktree:", subErr.message);
@@ -52172,6 +52375,26 @@ import { execFileSync as execFileSync6 } from "child_process";
52172
52375
  import { existsSync as existsSync40, readFileSync as readFileSync31 } from "fs";
52173
52376
  import { resolve as resolve19 } from "path";
52174
52377
  var PREVIEW_DEPLOY_RECORD = ".adhdev/preview-deploy.json";
52378
+ var PREVIEW_PIPELINE_SCRIPTS = [
52379
+ "scripts/preview-freshness.mjs",
52380
+ "scripts/smoke-preview-web.mjs",
52381
+ "scripts/deploy-preview-local.mjs"
52382
+ ];
52383
+ function hasDeployPreviewNpmScript(repoRoot) {
52384
+ const pkgPath = resolve19(repoRoot, "package.json");
52385
+ if (!existsSync40(pkgPath)) return false;
52386
+ try {
52387
+ const pkg = JSON.parse(readFileSync31(pkgPath, "utf8"));
52388
+ return typeof pkg?.scripts?.["deploy:preview"] === "string";
52389
+ } catch {
52390
+ return false;
52391
+ }
52392
+ }
52393
+ function isPreviewPipelineConfigured(repoRoot) {
52394
+ if (existsSync40(resolve19(repoRoot, PREVIEW_DEPLOY_RECORD))) return true;
52395
+ if (PREVIEW_PIPELINE_SCRIPTS.some((rel) => existsSync40(resolve19(repoRoot, rel)))) return true;
52396
+ return hasDeployPreviewNpmScript(repoRoot);
52397
+ }
52175
52398
  function runGit2(repoRoot, args) {
52176
52399
  try {
52177
52400
  return execFileSync6("git", args, {
@@ -52223,6 +52446,7 @@ function readCurrentMainCommit(repoRoot) {
52223
52446
  return { currentMainCommit: null, currentMainCommitSource: "unknown" };
52224
52447
  }
52225
52448
  function buildPreviewFreshness(repoRoot) {
52449
+ if (!isPreviewPipelineConfigured(repoRoot)) return null;
52226
52450
  const current = readCurrentMainCommit(repoRoot);
52227
52451
  const record = readRecord6(repoRoot);
52228
52452
  const lastPreviewCommit = normalizeCommit(record?.lastPreviewCommit);
@@ -65943,7 +66167,9 @@ async function initDaemonComponents(config) {
65943
66167
  const providerLoader = new ProviderLoader({
65944
66168
  logFn: config.providerLogFn,
65945
66169
  sourceMode: providerSourceMode,
65946
- userDir: appConfig.providerDir
66170
+ userDir: appConfig.providerDir,
66171
+ registryUrl: appConfig.registryUrl,
66172
+ providerTarballUrl: appConfig.providerTarballUrl
65947
66173
  });
65948
66174
  providerLoader.loadAll();
65949
66175
  providerLoader.registerToDetector();