@adhdev/daemon-standalone 1.0.28-rc.20 → 1.0.28-rc.22

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
@@ -33311,10 +33311,10 @@ var require_dist3 = __commonJS({
33311
33311
  }
33312
33312
  function getDaemonBuildInfo() {
33313
33313
  if (cached2) return cached2;
33314
- const commit = readInjected(true ? "284a88d08b7bb93b250385f4da9c523fb4d8fd68" : void 0) ?? "unknown";
33315
- const commitShort = readInjected(true ? "284a88d0" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
33316
- const version2 = readInjected(true ? "1.0.28-rc.20" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
33317
- const builtAt = readInjected(true ? "2026-07-28T17:20:58.778Z" : void 0);
33314
+ const commit = readInjected(true ? "dc10211420f1723bac639bd4dc2cfc2dd3fe0c81" : void 0) ?? "unknown";
33315
+ const commitShort = readInjected(true ? "dc102114" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
33316
+ const version2 = readInjected(true ? "1.0.28-rc.22" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
33317
+ const builtAt = readInjected(true ? "2026-07-29T00:52:17.964Z" : void 0);
33318
33318
  cached2 = builtAt ? { commit, commitShort, version: version2, builtAt } : { commit, commitShort, version: version2 };
33319
33319
  return cached2;
33320
33320
  }
@@ -37343,6 +37343,12 @@ ${error48.message || ""}`;
37343
37343
  function noteDroppedSuspension(reason) {
37344
37344
  metrics.suspensionsDropped[reason] = (metrics.suspensionsDropped[reason] ?? 0) + 1;
37345
37345
  }
37346
+ function noteRedriveBlocked(reason) {
37347
+ metrics.redriveBlockedByReason[reason] = (metrics.redriveBlockedByReason[reason] ?? 0) + 1;
37348
+ }
37349
+ function noteTargetPinCleared(reason) {
37350
+ metrics.targetPinClearedByReason[reason] = (metrics.targetPinClearedByReason[reason] ?? 0) + 1;
37351
+ }
37346
37352
  function logSuspensionOnce(level, key2, message) {
37347
37353
  if (suspensionLogKeys.has(key2)) return;
37348
37354
  if (suspensionLogKeys.size < MAX_SUSPENSION_LOG_KEYS) suspensionLogKeys.add(key2);
@@ -38070,7 +38076,9 @@ ${error48.message || ""}`;
38070
38076
  suspensionsDropped: {},
38071
38077
  reorderedGeneratingSuppressed: 0,
38072
38078
  redriveBlockedBySuspension: 0,
38073
- suspensionConsumedRecovered: 0
38079
+ suspensionConsumedRecovered: 0,
38080
+ redriveBlockedByReason: {},
38081
+ targetPinClearedByReason: {}
38074
38082
  };
38075
38083
  MAX_SUSPENSION_LOG_KEYS = 200;
38076
38084
  suspensionLogKeys = /* @__PURE__ */ new Set();
@@ -40204,6 +40212,7 @@ Next step: ${nextStep}`;
40204
40212
  deleteDirectDispatchesByTaskId: () => deleteDirectDispatchesByTaskId,
40205
40213
  describeTaskDependencyState: () => describeTaskDependencyState,
40206
40214
  enqueueTask: () => enqueueTask,
40215
+ expireTaskTargetPin: () => expireTaskTargetPin,
40207
40216
  getActiveDirectDispatches: () => getActiveDirectDispatches,
40208
40217
  getMeshQueueRevision: () => getMeshQueueRevision,
40209
40218
  getMeshQueueStats: () => getMeshQueueStats,
@@ -40906,6 +40915,24 @@ Next step: ${nextStep}`;
40906
40915
  if (result?.missionAffected) scheduleMissionCloseCandidateCheck(meshId, [result.entry, ...result.cascaded]);
40907
40916
  return result ? result.entry : null;
40908
40917
  }
40918
+ function expireTaskTargetPin(meshId, taskId, opts) {
40919
+ requireMeshHostQueueOwner(opts);
40920
+ return withQueueLock(meshId, () => {
40921
+ const entry = MeshRuntimeStore.getInstance().findQueueEntryById(meshId, taskId);
40922
+ if (!entry) return null;
40923
+ if (entry.status !== "pending") return null;
40924
+ if (!entry.targetSessionId && !entry.targetNodeId) return null;
40925
+ const clearedSession = entry.targetSessionId;
40926
+ const clearedNode = opts?.clearTargetNode ? entry.targetNodeId : void 0;
40927
+ delete entry.targetSessionId;
40928
+ if (opts?.clearTargetNode) delete entry.targetNodeId;
40929
+ entry.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
40930
+ if (opts?.reason) entry.requeueReason = opts.reason;
40931
+ MeshRuntimeStore.getInstance().updateQueueEntry(entry);
40932
+ LOG2.warn("MeshQueue", `Expired stale target pin on task ${taskId} (mesh ${meshId}): cleared${clearedSession ? ` targetSessionId=${clearedSession}` : ""}${clearedNode ? ` targetNodeId=${clearedNode}` : ""} (${opts?.reason ?? "target_pin_expired"}) \u2014 the task is now claimable by any compatible session`);
40933
+ return entry;
40934
+ });
40935
+ }
40909
40936
  function reclaimStrandedAssignedTask(meshId, taskId, opts) {
40910
40937
  requireMeshHostQueueOwner(opts);
40911
40938
  const result = withQueueLock(meshId, () => {
@@ -54072,6 +54099,10 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
54072
54099
  }
54073
54100
  return isWithinCloneBootstrapGrace(targetNodeId);
54074
54101
  }
54102
+ function targetPinAgeMs(task, nowMs = Date.now()) {
54103
+ const anchorMs = Date.parse(task.requeuedAt || task.createdAt || "");
54104
+ return Number.isFinite(anchorMs) ? nowMs - anchorMs : null;
54105
+ }
54075
54106
  function resolveDeadTargetVerdict(components, meshId, mesh, task) {
54076
54107
  const NOT_DEAD = { dead: false, nodeDead: false, reason: "" };
54077
54108
  const targetSessionId = readNonEmptyString(task.targetSessionId);
@@ -54643,11 +54674,29 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
54643
54674
  clearTargetNode: deadTarget.nodeDead
54644
54675
  });
54645
54676
  if (requeued) {
54677
+ noteTargetPinCleared(deadTarget.reason);
54646
54678
  LOG2.warn("MeshQueue", `DEAD-TARGET-SELFHEAL: task ${task.id} (mesh ${meshId}) was pinned to a dead target (${deadTarget.reason}); requeued${deadTarget.nodeDead ? " and unpinned node" : ""} (requeueCount=${requeued.requeueCount ?? "?"}, status=${requeued.status}).`);
54647
54679
  }
54648
54680
  markAutoLaunch(meshId, task.id, { status: "skipped", reason: "target_session_dead_requeued" });
54649
54681
  continue;
54650
54682
  }
54683
+ const pinAgeMs = targetPinAgeMs(task);
54684
+ if (pinAgeMs !== null && pinAgeMs >= TARGET_SESSION_PIN_TTL_MS) {
54685
+ const expired = expireTaskTargetPin(meshId, task.id, { reason: "target_session_pin_expired_unclaimed" });
54686
+ if (expired) {
54687
+ noteTargetPinCleared("target_session_pin_expired_unclaimed");
54688
+ traceMeshEventDrop("target_session_pin_expired", {
54689
+ taskId: task.id,
54690
+ sessionId: readNonEmptyString(task.targetSessionId),
54691
+ nodeId: readNonEmptyString(task.targetNodeId),
54692
+ meshId,
54693
+ event: "agent:ready"
54694
+ }, `unclaimed ${Math.round(pinAgeMs / 1e3)}s \u2265 ttl ${Math.round(TARGET_SESSION_PIN_TTL_MS / 1e3)}s \u2192 pin cleared, claimable`);
54695
+ LOG2.warn("MeshQueue", `TARGET-PIN-TTL: task ${task.id} (mesh ${meshId}) stayed pinned-but-unclaimed for ${Math.round(pinAgeMs / 1e3)}s (ttl ${Math.round(TARGET_SESSION_PIN_TTL_MS / 1e3)}s); expired the stale target pin so a compatible session can claim it.`);
54696
+ }
54697
+ markAutoLaunch(meshId, task.id, { status: "skipped", reason: "target_session_pin_expired" });
54698
+ continue;
54699
+ }
54651
54700
  markAutoLaunch(meshId, task.id, { status: "skipped", reason: "target_session_constraint" });
54652
54701
  continue;
54653
54702
  }
@@ -55278,6 +55327,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
55278
55327
  var TRANSIENT_TARGET_NODE_BOOTSTRAP_PENDING_REASON;
55279
55328
  var lastActionableSkipNotified;
55280
55329
  var DEAD_TARGET_GRACE_MS;
55330
+ var TARGET_SESSION_PIN_TTL_MS;
55281
55331
  var init_mesh_queue_assignment = __esm2({
55282
55332
  "src/mesh/mesh-queue-assignment.ts"() {
55283
55333
  "use strict";
@@ -55343,6 +55393,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
55343
55393
  TRANSIENT_TARGET_NODE_BOOTSTRAP_PENDING_REASON = "target_node_bootstrap_pending";
55344
55394
  lastActionableSkipNotified = /* @__PURE__ */ new Map();
55345
55395
  DEAD_TARGET_GRACE_MS = 6e4;
55396
+ TARGET_SESSION_PIN_TTL_MS = 15 * 6e4;
55346
55397
  }
55347
55398
  });
55348
55399
  function readSettings(state) {
@@ -59573,21 +59624,28 @@ ${cleanBody}`;
59573
59624
  }
59574
59625
  }
59575
59626
  async function pollAssignedTaskInTurnProgress(components, mesh, row) {
59627
+ return (await pollAssignedTaskActivity(components, mesh, row)).inTurnProgress;
59628
+ }
59629
+ async function pollAssignedTaskActivity(components, mesh, row) {
59630
+ const NONE = { inTurnProgress: false, lastAgentActivityMs: null };
59576
59631
  const sessionId = readNonEmptyString(row.assignedSessionId);
59577
59632
  const nodeId = readNonEmptyString(row.assignedNodeId);
59578
- if (!sessionId || !nodeId) return false;
59633
+ if (!sessionId || !nodeId) return NONE;
59579
59634
  const dispatchedAtMs = Date.parse(readNonEmptyString(row.dispatchTimestamp));
59580
- if (!Number.isFinite(dispatchedAtMs)) return false;
59635
+ if (!Number.isFinite(dispatchedAtMs)) return NONE;
59581
59636
  const payload = await runSessionEvidenceCollection(sessionId, () => fetchAssignedTaskChatTail(components, mesh, row));
59582
- if (!payload) return false;
59637
+ if (!payload) return NONE;
59583
59638
  const messages = Array.isArray(payload.messages) ? payload.messages : [];
59584
- return messages.some((msg) => {
59585
- if (!msg) return false;
59586
- if (msg.role === "user" || msg.role === "system") return false;
59639
+ let lastAgentActivityMs = null;
59640
+ for (const msg of messages) {
59641
+ if (!msg) continue;
59642
+ if (msg.role === "user" || msg.role === "system") continue;
59587
59643
  const ts2 = readChatMessageTimestampMs(msg);
59588
- if (typeof ts2 !== "number" || !Number.isFinite(ts2)) return false;
59589
- return ts2 >= dispatchedAtMs;
59590
- });
59644
+ if (typeof ts2 !== "number" || !Number.isFinite(ts2)) continue;
59645
+ if (ts2 < dispatchedAtMs) continue;
59646
+ if (lastAgentActivityMs === null || ts2 > lastAgentActivityMs) lastAgentActivityMs = ts2;
59647
+ }
59648
+ return { inTurnProgress: lastAgentActivityMs !== null, lastAgentActivityMs };
59591
59649
  }
59592
59650
  async function pollAssignedTaskTerminalEvidence(components, mesh, row, opts) {
59593
59651
  const sessionId = readNonEmptyString(row.assignedSessionId);
@@ -60053,6 +60111,7 @@ ${cleanBody}`;
60053
60111
  if (attempt.terminalOutcome) return false;
60054
60112
  const attemptSessionId = readNonEmptyString(attempt.sessionId);
60055
60113
  if (attemptSessionId && !sessionIdsEquivalent(attemptSessionId, sessionId)) return false;
60114
+ if (typeof row.dispatchNonce === "number" && typeof attempt.dispatchNonce === "number" && row.dispatchNonce !== attempt.dispatchNonce) return false;
60056
60115
  }
60057
60116
  const nodeId = readNonEmptyString(row.assignedNodeId);
60058
60117
  const coordinatorDaemonId = readNonEmptyString(attempt?.coordinatorDaemonId);
@@ -60205,6 +60264,26 @@ ${cleanBody}`;
60205
60264
  const redriveProfile = row.assignedSessionId ? resolveAssignedTranscriptProfile(components, row) : void 0;
60206
60265
  if (redriveProfile?.emitsPtyTurnEvents === false && await pollAssignedTaskInTurnProgress(components, { id: meshId, nodes: mesh.nodes }, row)) {
60207
60266
  deliveredUnconsumedUnknownStreak.delete(shortStreakKey);
60267
+ try {
60268
+ recordTurnAck({
60269
+ meshId,
60270
+ taskId: row.id,
60271
+ kind: "consumed",
60272
+ sessionId: row.assignedSessionId,
60273
+ legacy: {
60274
+ ...typeof row.dispatchNonce === "number" ? { dispatchNonce: row.dispatchNonce } : {},
60275
+ ...row.assignedNodeId ? { nodeId: row.assignedNodeId } : {},
60276
+ ...row.assignedProviderType ? { providerType: row.assignedProviderType } : {}
60277
+ },
60278
+ evidence: {
60279
+ source: "native_source_activity",
60280
+ profileClass: redriveProfile.class,
60281
+ profileTiming: redriveProfile.timing
60282
+ }
60283
+ });
60284
+ } catch {
60285
+ }
60286
+ noteRedriveBlocked("native_source_activity");
60208
60287
  traceMeshEventDrop("short_redrive_deferred_native_source_progress", {
60209
60288
  taskId: row.id,
60210
60289
  sessionId: row.assignedSessionId,
@@ -60440,6 +60519,70 @@ ${cleanBody}`;
60440
60519
  }, `${reclaimReason} \u2192 transcript ${terminalEvidence.outcome}${propagated ? " propagated" : ""}`);
60441
60520
  continue;
60442
60521
  }
60522
+ const currentAttempt = (() => {
60523
+ try {
60524
+ return store.getCurrentTurnAttempt(meshId, row.id);
60525
+ } catch {
60526
+ return null;
60527
+ }
60528
+ })();
60529
+ const attemptStage = readNonEmptyString(currentAttempt?.stage);
60530
+ if (currentAttempt && !currentAttempt.terminalOutcome && (attemptStage === "generating" || attemptStage === "waiting_approval" || attemptStage === "waiting_choice" || attemptStage === "finalizing")) {
60531
+ deliveredNoTurnUnknownStreak.delete(streakKey);
60532
+ noteRedriveBlocked("active_attempt_stage");
60533
+ traceMeshEventDrop("redrive_blocked_active_attempt", {
60534
+ taskId: row.id,
60535
+ sessionId: row.assignedSessionId,
60536
+ nodeId: row.assignedNodeId,
60537
+ meshId,
60538
+ event: "agent:generating_completed"
60539
+ }, `attempt stage ${attemptStage} \u2014 live turn, ${reclaimReason} suppressed`);
60540
+ continue;
60541
+ }
60542
+ const noTurnProfile = row.assignedSessionId ? resolveAssignedTranscriptProfile(components, row) : void 0;
60543
+ if (noTurnProfile?.emitsPtyTurnEvents === false) {
60544
+ const activity = await pollAssignedTaskActivity(components, { id: meshId, nodes: mesh.nodes }, row);
60545
+ if (activity.inTurnProgress && activity.lastAgentActivityMs !== null && nowMs - activity.lastAgentActivityMs <= NATIVE_SOURCE_ACTIVITY_STALE_MS) {
60546
+ deliveredNoTurnUnknownStreak.delete(streakKey);
60547
+ try {
60548
+ recordTurnAck({
60549
+ meshId,
60550
+ taskId: row.id,
60551
+ kind: "consumed",
60552
+ sessionId: row.assignedSessionId,
60553
+ legacy: {
60554
+ ...typeof row.dispatchNonce === "number" ? { dispatchNonce: row.dispatchNonce } : {},
60555
+ ...row.assignedNodeId ? { nodeId: row.assignedNodeId } : {},
60556
+ ...row.assignedProviderType ? { providerType: row.assignedProviderType } : {}
60557
+ },
60558
+ evidence: {
60559
+ source: "native_source_activity",
60560
+ profileClass: noTurnProfile.class,
60561
+ profileTiming: noTurnProfile.timing
60562
+ }
60563
+ });
60564
+ } catch {
60565
+ }
60566
+ noteRedriveBlocked("native_source_activity");
60567
+ traceMeshEventDrop("redrive_blocked_native_source_activity", {
60568
+ taskId: row.id,
60569
+ sessionId: row.assignedSessionId,
60570
+ nodeId: row.assignedNodeId,
60571
+ meshId,
60572
+ event: "agent:generating_completed"
60573
+ }, `${noTurnProfile.class}_fresh_activity \u2014 consumed promoted, ${reclaimReason} suppressed`);
60574
+ continue;
60575
+ }
60576
+ if (activity.inTurnProgress) {
60577
+ traceMeshEventStage("native_source_activity_stale", {
60578
+ taskId: row.id,
60579
+ sessionId: row.assignedSessionId,
60580
+ nodeId: row.assignedNodeId,
60581
+ meshId,
60582
+ event: "agent:generating_completed"
60583
+ }, `${noTurnProfile.class} quiet >${Math.round(NATIVE_SOURCE_ACTIVITY_STALE_MS / 1e3)}s \u2192 ${reclaimReason} proceeds`);
60584
+ }
60585
+ }
60443
60586
  const reclaimedLost = reclaimStrandedAssignedTask(meshId, row.id, {
60444
60587
  reason: reclaimReason,
60445
60588
  ageMs: nowMs - dispatchedAtMs
@@ -61005,6 +61148,7 @@ ${cleanBody}`;
61005
61148
  var heldEventLedgerRecorded;
61006
61149
  var ASSIGNED_STRANDED_DEADLINE_MS;
61007
61150
  var DELIVERED_NO_TURN_DEADLINE_MS;
61151
+ var NATIVE_SOURCE_ACTIVITY_STALE_MS;
61008
61152
  var ASSIGNED_DELIVERED_UNCONSUMED_REDRIVE_MS;
61009
61153
  var RECLAIM_UNKNOWN_GRACE_TICKS;
61010
61154
  var deliveredNoTurnUnknownStreak;
@@ -61055,6 +61199,7 @@ ${cleanBody}`;
61055
61199
  heldEventLedgerRecorded = /* @__PURE__ */ new Set();
61056
61200
  ASSIGNED_STRANDED_DEADLINE_MS = 5 * 6e4;
61057
61201
  DELIVERED_NO_TURN_DEADLINE_MS = 15 * 6e4;
61202
+ NATIVE_SOURCE_ACTIVITY_STALE_MS = 10 * 6e4;
61058
61203
  ASSIGNED_DELIVERED_UNCONSUMED_REDRIVE_MS = 25e3;
61059
61204
  RECLAIM_UNKNOWN_GRACE_TICKS = 3;
61060
61205
  deliveredNoTurnUnknownStreak = /* @__PURE__ */ new Map();
@@ -67899,6 +68044,7 @@ ${lastSnapshot}`;
67899
68044
  isOperatingNoteTombstoned: () => isOperatingNoteTombstoned,
67900
68045
  isP2pRelayTransportFailure: () => isP2pRelayTransportFailure,
67901
68046
  isPathInside: () => isPathInside,
68047
+ isPreviewReleaseChannel: () => isPreviewReleaseChannel,
67902
68048
  isRestartBlockingPresentation: () => isRestartBlockingPresentation,
67903
68049
  isSessionHostLiveRuntime: () => import_session_host_core4.isSessionHostLiveRuntime,
67904
68050
  isSessionHostRecoverySnapshot: () => import_session_host_core4.isSessionHostRecoverySnapshot,
@@ -68209,6 +68355,28 @@ ${lastSnapshot}`;
68209
68355
  });
68210
68356
  return { promptId, answers };
68211
68357
  }
68358
+ function interactivePromptContentFingerprint(questions) {
68359
+ let hash2 = 2166136261;
68360
+ const mix = (text) => {
68361
+ for (let i = 0; i < text.length; i += 1) {
68362
+ hash2 ^= text.charCodeAt(i);
68363
+ hash2 = Math.imul(hash2, 16777619) >>> 0;
68364
+ }
68365
+ };
68366
+ for (const question of questions) {
68367
+ mix(question.question);
68368
+ mix(question.multiSelect ? "multi" : "single");
68369
+ for (const option of question.options) {
68370
+ mix("");
68371
+ mix(option.label);
68372
+ }
68373
+ mix("");
68374
+ }
68375
+ return hash2.toString(16).padStart(8, "0");
68376
+ }
68377
+ function stableClaudeTuiPromptId(questions) {
68378
+ return `ask-user-tui-${interactivePromptContentFingerprint(questions)}`;
68379
+ }
68212
68380
  function buildClaudeInteractiveToolResult(response) {
68213
68381
  return JSON.stringify({
68214
68382
  type: "user",
@@ -72936,8 +73104,23 @@ ${effect.notification.body || ""}`.trim();
72936
73104
  function stripTrailingSlashes(url2) {
72937
73105
  return url2.replace(/\/+$/, "");
72938
73106
  }
72939
- function resolveRegistryBaseUrl(configuredUrl, env2 = process.env) {
72940
- const resolved = cleanString(configuredUrl) ?? cleanString(env2[REGISTRY_URL_ENV_VAR]) ?? DEFAULT_REGISTRY_BASE_URL;
73107
+ var REGISTRY_API_PATH = "/api/v1/registry";
73108
+ function deriveRegistryBaseUrlFromServerUrl(serverUrl) {
73109
+ const cleaned = cleanString(serverUrl);
73110
+ if (!cleaned) return void 0;
73111
+ const stripped = stripTrailingSlashes(cleaned);
73112
+ let parsed;
73113
+ try {
73114
+ parsed = new URL(stripped);
73115
+ } catch {
73116
+ return void 0;
73117
+ }
73118
+ if (parsed.protocol !== "https:" && parsed.protocol !== "http:") return void 0;
73119
+ if (stripped.endsWith(REGISTRY_API_PATH)) return stripped;
73120
+ return `${stripped}${REGISTRY_API_PATH}`;
73121
+ }
73122
+ function resolveRegistryBaseUrl(configuredUrl, env2 = process.env, serverUrl) {
73123
+ const resolved = cleanString(configuredUrl) ?? cleanString(env2[REGISTRY_URL_ENV_VAR]) ?? deriveRegistryBaseUrlFromServerUrl(serverUrl) ?? DEFAULT_REGISTRY_BASE_URL;
72941
73124
  return stripTrailingSlashes(resolved);
72942
73125
  }
72943
73126
  function resolveProviderTarballUrl(configuredUrl, env2 = process.env) {
@@ -78004,7 +78187,8 @@ ${effect.notification.body || ""}`.trim();
78004
78187
  const https = require("https");
78005
78188
  const fs47 = require("fs");
78006
78189
  const path50 = require("path");
78007
- const REGISTRY = resolveRegistryBaseUrl(loadConfig2().registryUrl);
78190
+ const cfg = loadConfig2();
78191
+ const REGISTRY = resolveRegistryBaseUrl(cfg.registryUrl, process.env, cfg.serverUrl);
78008
78192
  function fetchText(url2, timeoutMs) {
78009
78193
  return new Promise((resolve28, reject) => {
78010
78194
  const req = https.get(url2, { headers: { "User-Agent": "adhdev-daemon", "Accept": "application/json" }, timeout: timeoutMs }, (res) => {
@@ -78374,7 +78558,8 @@ ${formatManifestValidationIssues2(validation.issues)}`,
78374
78558
  const installed = this.handleListInstalledProviders({});
78375
78559
  if (!installed.success) return installed;
78376
78560
  const https = require("https");
78377
- const REGISTRY = resolveRegistryBaseUrl(loadConfig2().registryUrl);
78561
+ const cfg = loadConfig2();
78562
+ const REGISTRY = resolveRegistryBaseUrl(cfg.registryUrl, process.env, cfg.serverUrl);
78378
78563
  function fetchJson(url2) {
78379
78564
  return new Promise((resolve28, reject) => {
78380
78565
  const req = https.get(url2, { headers: { "User-Agent": "adhdev-daemon", "Accept": "application/json" }, timeout: 1e4 }, (res) => {
@@ -85059,10 +85244,15 @@ ${body}
85059
85244
  if (!screenText.includes("Enter to select")) return;
85060
85245
  if (headers.length === 0) {
85061
85246
  const prompt = detectClaudeAskUserQuestionPromptFromTuiPages([{ screenText }], {
85062
- promptId: `ask-user-${this.providerSessionId || "claude"}-${Date.now()}`,
85247
+ // REBIND OPTION FIDELITY (rc.20): provisional id — replaced with the
85248
+ // content-addressed stable id below, so the SAME picker re-captured
85249
+ // after a daemon restart keeps the SAME promptId and pre-restart
85250
+ // answers still bind to the options they were issued against.
85251
+ promptId: "ask-user-tui-pending",
85063
85252
  providerType: this.cliType
85064
85253
  });
85065
85254
  if (!prompt) return;
85255
+ prompt.promptId = stableClaudeTuiPromptId(prompt.questions);
85066
85256
  this.activeInteractivePrompt = prompt;
85067
85257
  this.interactivePromptTransport = "tui";
85068
85258
  this.interactivePromptLostAt = null;
@@ -85170,10 +85360,14 @@ ${body}
85170
85360
  }
85171
85361
  }
85172
85362
  const prompt = detectClaudeAskUserQuestionPromptFromTuiPages(pages, {
85173
- promptId: `ask-user-${this.providerSessionId || "claude"}-${Date.now()}`,
85363
+ // REBIND OPTION FIDELITY (rc.20): provisional id — replaced with the
85364
+ // content-addressed stable id below (same rationale as the
85365
+ // headerless capture in maybeCaptureClaudeTuiPrompt).
85366
+ promptId: "ask-user-tui-pending",
85174
85367
  providerType: this.cliType
85175
85368
  });
85176
85369
  if (!prompt) return;
85370
+ prompt.promptId = stableClaudeTuiPromptId(prompt.questions);
85177
85371
  this.activeInteractivePrompt = prompt;
85178
85372
  this.interactivePromptTransport = "tui";
85179
85373
  this.interactivePromptLostAt = null;
@@ -86662,6 +86856,12 @@ ${body}
86662
86856
  }
86663
86857
  } else if (event === "interactive_prompt_response" && data) {
86664
86858
  try {
86859
+ const heldPromptId = typeof this.activeInteractivePrompt?.promptId === "string" && this.activeInteractivePrompt.promptId ? this.activeInteractivePrompt.promptId : "";
86860
+ const incomingPromptId = typeof data?.promptId === "string" ? data.promptId.trim() : "";
86861
+ if (heldPromptId && incomingPromptId && incomingPromptId !== heldPromptId) {
86862
+ LOG2.warn("CLI", `[${this.type}] interactive_prompt_response REJECTED: stale promptId "${incomingPromptId}" does not match active prompt "${heldPromptId}" \u2014 answer not applied (no index/default fallback); re-answer against the active promptId`);
86863
+ return;
86864
+ }
86665
86865
  const response = this.activeInteractivePrompt && this.activeInteractivePrompt.promptId === data?.promptId && Array.isArray(data?.answers) ? resolveInteractivePromptResponse(this.activeInteractivePrompt, data) : normalizeInteractivePromptResponse2(data);
86666
86866
  if (this.activeInteractivePrompt?.promptId === response.promptId) {
86667
86867
  this.activeInteractivePrompt = null;
@@ -91615,6 +91815,14 @@ Run 'adhdev doctor' for detailed diagnostics.`
91615
91815
  if (coordinatorEntry?.meshId) {
91616
91816
  restoredSettings.meshCoordinatorFor = coordinatorEntry.meshId;
91617
91817
  }
91818
+ const recordMeshNodeFor = typeof record2.meshNodeFor === "string" && record2.meshNodeFor.trim() ? record2.meshNodeFor.trim() : "";
91819
+ const recordMeshNodeId = typeof record2.meshNodeId === "string" && record2.meshNodeId.trim() ? record2.meshNodeId.trim() : "";
91820
+ if (recordMeshNodeFor) restoredSettings.meshNodeFor = recordMeshNodeFor;
91821
+ if (recordMeshNodeId) {
91822
+ restoredSettings.meshNodeId = recordMeshNodeId;
91823
+ restoredSettings.meshLastNodeId = recordMeshNodeId;
91824
+ }
91825
+ if (record2.launchedByCoordinator === true) restoredSettings.launchedByCoordinator = true;
91618
91826
  try {
91619
91827
  await this.registerCliInstance(
91620
91828
  record2.runtimeId,
@@ -94291,9 +94499,14 @@ Run 'adhdev doctor' for detailed diagnostics.`
94291
94499
  this.providerType = providerType;
94292
94500
  }
94293
94501
  };
94294
- function resolveProviderChannel(configured, env2 = process.env) {
94502
+ function isPreviewReleaseChannel(releaseChannel) {
94503
+ const normalized = typeof releaseChannel === "string" ? releaseChannel.trim().toLowerCase() : "";
94504
+ return normalized === "preview" || normalized === "next";
94505
+ }
94506
+ function resolveProviderChannel(configured, env2 = process.env, releaseChannel) {
94295
94507
  const raw = configured && configured.trim() || (env2[PROVIDER_CHANNEL_ENV_VAR] ?? "").trim();
94296
- return raw === "preview" ? "preview" : "stable";
94508
+ if (raw) return raw === "preview" ? "preview" : "stable";
94509
+ return isPreviewReleaseChannel(releaseChannel) ? "preview" : DEFAULT_PROVIDER_CHANNEL;
94297
94510
  }
94298
94511
  function partitionChannelEntries(entries) {
94299
94512
  const activatable = [];
@@ -94718,6 +94931,15 @@ Run 'adhdev doctor' for detailed diagnostics.`
94718
94931
  `channel metadata for "${channel}" has an unexpected shape (missing providers array)`
94719
94932
  );
94720
94933
  }
94934
+ if (channel === "preview") {
94935
+ const echo = typeof body.channel === "string" ? body.channel.trim().toLowerCase() : "";
94936
+ if (echo !== channel) {
94937
+ throw new ProviderChannelError(
94938
+ "CHANNEL_METADATA_MISMATCH",
94939
+ `registry response for channel "preview" ${typeof body.channel === "string" ? `echoes channel "${body.channel}"` : "omits the top-level channel echo"} \u2014 the registry does not honor the channel contract (legacy/stable payload); refusing to treat its rows as preview, last-known-good preserved`
94940
+ );
94941
+ }
94942
+ }
94721
94943
  const entries = [];
94722
94944
  for (const raw of body.providers) {
94723
94945
  if (!raw || typeof raw.type !== "string" || typeof raw.version !== "string" || typeof raw.category !== "string") {
@@ -95086,10 +95308,11 @@ Run 'adhdev doctor' for detailed diagnostics.`
95086
95308
  * the resolved provider channel is 'stable' (production mode).
95087
95309
  */
95088
95310
  static UNVERIFIED_TARBALL_ENV_VAR = "ADHDEV_PROVIDER_ALLOW_UNVERIFIED_TARBALL";
95089
- /** Resolved explicit provider channel (absent/ambiguous → 'stable'). */
95311
+ /** Resolved provider channel (explicit config/env wins; otherwise derived from the daemon release channel; absent/ambiguous → 'stable'). */
95090
95312
  channel;
95091
95313
  allowUnverifiedTarball;
95092
95314
  channelStore;
95315
+ channelSyncIO;
95093
95316
  probeStarts = [];
95094
95317
  siblingLogged = false;
95095
95318
  siblingRefusalLogged = false;
@@ -95169,11 +95392,12 @@ Run 'adhdev doctor' for detailed diagnostics.`
95169
95392
  constructor(options) {
95170
95393
  this.logFn = options?.logFn || LOG2.forComponent("Provider").asLogFn();
95171
95394
  this.probeStarts = options?.probeStarts ?? [process.cwd(), __dirname];
95172
- this.registryBaseUrl = resolveRegistryBaseUrl(options?.registryUrl);
95395
+ this.registryBaseUrl = resolveRegistryBaseUrl(options?.registryUrl, process.env, options?.serverUrl);
95173
95396
  this.providerTarballUrl = resolveProviderTarballUrl(options?.providerTarballUrl);
95174
- this.channel = resolveProviderChannel(options?.channel);
95397
+ this.channel = resolveProviderChannel(options?.channel, process.env, options?.updateChannel);
95175
95398
  this.allowUnverifiedTarball = options?.allowUnverifiedTarball === true || process.env[_ProviderLoader.UNVERIFIED_TARBALL_ENV_VAR] === "1";
95176
95399
  this.channelStore = options?.channelStore === null ? null : options?.channelStore ?? new ProviderChannelStore(ProviderChannelStore.defaultRoot(), this.logFn);
95400
+ this.channelSyncIO = options?.channelSyncIO;
95177
95401
  this.defaultProvidersDir = path41.join(getConfigDir2(), "providers");
95178
95402
  const detected = this.detectDefaultUserDir();
95179
95403
  this.userDir = detected.path;
@@ -95437,7 +95661,8 @@ Run 'adhdev doctor' for detailed diagnostics.`
95437
95661
  store: this.channelStore,
95438
95662
  registryBaseUrl: this.registryBaseUrl,
95439
95663
  providerTarballUrl: this.providerTarballUrl,
95440
- logFn: this.logFn
95664
+ logFn: this.logFn,
95665
+ ...this.channelSyncIO
95441
95666
  });
95442
95667
  const targetTypes = collectSyncTargetTypes(this.upstreamDir, this.channelStore, this.channel);
95443
95668
  const report = await runtime.sync({ channel: this.channel, targetTypes });
@@ -95452,6 +95677,39 @@ Run 'adhdev doctor' for detailed diagnostics.`
95452
95677
  }
95453
95678
  return report;
95454
95679
  }
95680
+ /**
95681
+ * Number of valid active pointers on the resolved channel (0 = empty or
95682
+ * disabled store). Corrupt pointer files are excluded by the store.
95683
+ */
95684
+ countVerifiedChannelPointers() {
95685
+ if (!this.channelStore) return 0;
95686
+ try {
95687
+ return this.channelStore.listPointers(this.channel).pointers.size;
95688
+ } catch {
95689
+ return 0;
95690
+ }
95691
+ }
95692
+ /**
95693
+ * Bounded one-shot first sync for an empty verified channel store.
95694
+ *
95695
+ * Closes the rc.20 preview activation gap: a daemon whose provider channel
95696
+ * newly derives to a channel with an EMPTY store (e.g. updateChannel=preview
95697
+ * while providerChannel defaulted to stable) would otherwise sit at 0 active
95698
+ * providers until a manual check_provider_updates. Runs at most one
95699
+ * verified sync per call, only when the resolved channel has no pointers
95700
+ * AND there are installed (.upstream) providers to sync. Fail-closed: any
95701
+ * registry/transport failure activates nothing (last-known-good preserved)
95702
+ * and is retried on the next boot or via check_provider_updates. Never
95703
+ * invoked from any status path.
95704
+ *
95705
+ * Returns the sync report, or null when the first-sync gate did not apply.
95706
+ */
95707
+ async maybeFirstSyncVerifiedChannel() {
95708
+ if (!this.channelStore) return null;
95709
+ if (this.countVerifiedChannelPointers() > 0) return null;
95710
+ if (!this.hasUpstream()) return null;
95711
+ return this.syncVerifiedChannel();
95712
+ }
95455
95713
  /**
95456
95714
  * Roll a provider back to its previously activated verified object. Pure
95457
95715
  * local pointer flip — no network. Returns the new active digest, or null
@@ -97061,6 +97319,7 @@ ${formatManifestValidationIssues2(validation2.issues)}`);
97061
97319
  return 0;
97062
97320
  }
97063
97321
  };
97322
+ init_config();
97064
97323
  function normalizeMacAppPath(appPath) {
97065
97324
  const trimmed = String(appPath || "").trim();
97066
97325
  if (!trimmed) return null;
@@ -97102,8 +97361,16 @@ ${formatManifestValidationIssues2(validation2.issues)}`);
97102
97361
  var _providerLoader = null;
97103
97362
  function getProviderLoader() {
97104
97363
  if (!_providerLoader) {
97105
- _providerLoader = new ProviderLoader({ logFn: () => {
97106
- } });
97364
+ const appConfig = loadConfig2();
97365
+ _providerLoader = new ProviderLoader({
97366
+ logFn: () => {
97367
+ },
97368
+ // Suppress logs during launch
97369
+ registryUrl: appConfig.registryUrl,
97370
+ serverUrl: appConfig.serverUrl,
97371
+ channel: appConfig.providerChannel,
97372
+ updateChannel: appConfig.updateChannel
97373
+ });
97107
97374
  _providerLoader.loadAll();
97108
97375
  _providerLoader.registerToDetector();
97109
97376
  }
@@ -99667,6 +99934,25 @@ ${formatManifestValidationIssues2(validation2.issues)}`);
99667
99934
  if (!instance) return { success: false, error: `No running instance for session ${sessionId}` };
99668
99935
  const isFriendlyArrayForm = rawResponse && typeof rawResponse === "object" && Array.isArray(rawResponse.answers);
99669
99936
  const payload = isFriendlyArrayForm ? rawResponse : normalizeInteractivePromptResponse2(rawResponse);
99937
+ const heldPrompt = (() => {
99938
+ try {
99939
+ const state = instance.getState?.();
99940
+ return state?.activeChat?.activeInteractivePrompt ?? state?.activeInteractivePrompt ?? null;
99941
+ } catch {
99942
+ return null;
99943
+ }
99944
+ })();
99945
+ const heldPromptId = typeof heldPrompt?.promptId === "string" && heldPrompt.promptId.trim() ? heldPrompt.promptId.trim() : "";
99946
+ const incomingPromptId = typeof payload?.promptId === "string" ? payload.promptId.trim() : "";
99947
+ if (heldPromptId && incomingPromptId && incomingPromptId !== heldPromptId) {
99948
+ return {
99949
+ success: false,
99950
+ error: `Stale promptId "${incomingPromptId}" \u2014 the session's active question is "${heldPromptId}". The answer was NOT applied; re-answer with mesh_answer_question against the active promptId.`,
99951
+ waitingChoice: true,
99952
+ promptId: heldPromptId,
99953
+ stalePromptId: incomingPromptId
99954
+ };
99955
+ }
99670
99956
  ctx.deps.instanceManager.sendEvent(sessionId, "interactive_prompt_response", payload);
99671
99957
  return { success: true };
99672
99958
  }
@@ -113259,7 +113545,13 @@ data: ${JSON.stringify(msg.data)}
113259
113545
  workspace: record2.workspace,
113260
113546
  cliArgs: Array.isArray(record2.meta?.cliArgs) ? record2.meta.cliArgs : [],
113261
113547
  providerSessionId: typeof record2.meta?.providerSessionId === "string" ? String(record2.meta.providerSessionId) : void 0,
113262
- managedBy: typeof record2.meta?.managedBy === "string" ? String(record2.meta.managedBy) : void 0
113548
+ managedBy: typeof record2.meta?.managedBy === "string" ? String(record2.meta.managedBy) : void 0,
113549
+ // Session-level mesh membership (rc.20 rebound relay envelope) —
113550
+ // surfaced so restoreHostedSessions can re-apply it to the rebuilt
113551
+ // instance settings. Task-level markers stay out (see the descriptor).
113552
+ meshNodeFor: typeof record2.meta?.meshNodeFor === "string" && record2.meta.meshNodeFor.trim() ? String(record2.meta.meshNodeFor).trim() : void 0,
113553
+ meshNodeId: typeof record2.meta?.meshNodeId === "string" && record2.meta.meshNodeId.trim() ? String(record2.meta.meshNodeId).trim() : void 0,
113554
+ launchedByCoordinator: record2.meta?.launchedByCoordinator === true ? true : void 0
113263
113555
  }));
113264
113556
  } finally {
113265
113557
  await client.close().catch(() => {
@@ -113778,12 +114070,22 @@ data: ${JSON.stringify(msg.data)}
113778
114070
  sourceMode: providerSourceMode,
113779
114071
  userDir: appConfig.providerDir,
113780
114072
  registryUrl: appConfig.registryUrl,
114073
+ serverUrl: appConfig.serverUrl,
113781
114074
  providerTarballUrl: appConfig.providerTarballUrl,
113782
114075
  channel: appConfig.providerChannel,
114076
+ updateChannel: appConfig.updateChannel,
113783
114077
  allowUnverifiedTarball: appConfig.providerAllowUnverifiedTarball
113784
114078
  });
113785
114079
  providerLoader.loadAll();
113786
114080
  providerLoader.registerToDetector();
114081
+ void providerLoader.maybeFirstSyncVerifiedChannel().then((report) => {
114082
+ if (!report) return;
114083
+ if (report.status === "error") {
114084
+ LOG2.warn("Init", `Verified channel first-sync failed (last-known-good preserved): ${report.errors.map((e) => e.code).join(", ") || "unknown"}`);
114085
+ } else if (report.activated.length > 0) {
114086
+ LOG2.info("Init", `Verified channel first-sync activated ${report.activated.length} providers (${providerLoader.channel})`);
114087
+ }
114088
+ }).catch((e) => LOG2.warn("Init", `Verified channel first-sync error: ${e?.message || e}`));
113787
114089
  setDefaultProviderLoader(providerLoader);
113788
114090
  const versionArchive = new VersionArchive();
113789
114091
  providerLoader.setVersionArchive(versionArchive);