instar 1.3.1659 → 1.3.1661

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.
Files changed (261) hide show
  1. package/dashboard/console/services-model.js +2 -0
  2. package/dashboard/desktop.css +2 -0
  3. package/dashboard/desktop.html +1 -0
  4. package/dashboard/desktop.js +39 -3
  5. package/dist/commands/server.d.ts.map +1 -1
  6. package/dist/commands/server.js +165 -70
  7. package/dist/commands/server.js.map +1 -1
  8. package/dist/commands/setClaudeKey.d.ts.map +1 -1
  9. package/dist/commands/setClaudeKey.js +5 -3
  10. package/dist/commands/setClaudeKey.js.map +1 -1
  11. package/dist/commands/setup.d.ts.map +1 -1
  12. package/dist/commands/setup.js +19 -2
  13. package/dist/commands/setup.js.map +1 -1
  14. package/dist/config/ConfigDefaults.d.ts.map +1 -1
  15. package/dist/config/ConfigDefaults.js +9 -0
  16. package/dist/config/ConfigDefaults.js.map +1 -1
  17. package/dist/core/BackupManager.d.ts.map +1 -1
  18. package/dist/core/BackupManager.js +1 -0
  19. package/dist/core/BackupManager.js.map +1 -1
  20. package/dist/core/CodexLifecycleProductionComposition.d.ts +1 -1
  21. package/dist/core/CodexLifecycleProductionComposition.d.ts.map +1 -1
  22. package/dist/core/DesktopAccessAudit.d.ts +5 -1
  23. package/dist/core/DesktopAccessAudit.d.ts.map +1 -1
  24. package/dist/core/DesktopAccessAudit.js +5 -0
  25. package/dist/core/DesktopAccessAudit.js.map +1 -1
  26. package/dist/core/DesktopLockedController.d.ts +21 -1
  27. package/dist/core/DesktopLockedController.d.ts.map +1 -1
  28. package/dist/core/DesktopLockedController.js +29 -7
  29. package/dist/core/DesktopLockedController.js.map +1 -1
  30. package/dist/core/DirectDesktopAccess.d.ts +26 -1
  31. package/dist/core/DirectDesktopAccess.d.ts.map +1 -1
  32. package/dist/core/DirectDesktopAccess.js +77 -8
  33. package/dist/core/DirectDesktopAccess.js.map +1 -1
  34. package/dist/core/MacDesktopHost.d.ts +1 -0
  35. package/dist/core/MacDesktopHost.d.ts.map +1 -1
  36. package/dist/core/MacDesktopHost.js +29 -3
  37. package/dist/core/MacDesktopHost.js.map +1 -1
  38. package/dist/core/ModelTierEscalation.d.ts +1 -1
  39. package/dist/core/ModelTierEscalation.d.ts.map +1 -1
  40. package/dist/core/ModelTierEscalation.js +3 -0
  41. package/dist/core/ModelTierEscalation.js.map +1 -1
  42. package/dist/core/PostUpdateMigrator.d.ts +21 -0
  43. package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
  44. package/dist/core/PostUpdateMigrator.js +92 -10
  45. package/dist/core/PostUpdateMigrator.js.map +1 -1
  46. package/dist/core/SessionManager.d.ts +6 -0
  47. package/dist/core/SessionManager.d.ts.map +1 -1
  48. package/dist/core/SessionManager.js +27 -3
  49. package/dist/core/SessionManager.js.map +1 -1
  50. package/dist/core/TopicProfileResolver.d.ts +9 -0
  51. package/dist/core/TopicProfileResolver.d.ts.map +1 -1
  52. package/dist/core/TopicProfileResolver.js +33 -0
  53. package/dist/core/TopicProfileResolver.js.map +1 -1
  54. package/dist/core/UpdateChecker.d.ts +5 -0
  55. package/dist/core/UpdateChecker.d.ts.map +1 -1
  56. package/dist/core/UpdateChecker.js +142 -69
  57. package/dist/core/UpdateChecker.js.map +1 -1
  58. package/dist/core/UsageWallRescue.d.ts +259 -0
  59. package/dist/core/UsageWallRescue.d.ts.map +1 -0
  60. package/dist/core/UsageWallRescue.js +581 -0
  61. package/dist/core/UsageWallRescue.js.map +1 -0
  62. package/dist/core/WriteDomainRegistry.d.ts.map +1 -1
  63. package/dist/core/WriteDomainRegistry.js +8 -0
  64. package/dist/core/WriteDomainRegistry.js.map +1 -1
  65. package/dist/core/channelRegistry.d.ts +6 -0
  66. package/dist/core/channelRegistry.d.ts.map +1 -1
  67. package/dist/core/channelRegistry.js +19 -0
  68. package/dist/core/channelRegistry.js.map +1 -1
  69. package/dist/core/codexCapabilities.d.ts +2 -0
  70. package/dist/core/codexCapabilities.d.ts.map +1 -1
  71. package/dist/core/codexCapabilities.js +26 -0
  72. package/dist/core/codexCapabilities.js.map +1 -1
  73. package/dist/core/dashboardPin.d.ts +22 -0
  74. package/dist/core/dashboardPin.d.ts.map +1 -1
  75. package/dist/core/dashboardPin.js +41 -0
  76. package/dist/core/dashboardPin.js.map +1 -1
  77. package/dist/core/devGatedFeatures.d.ts.map +1 -1
  78. package/dist/core/devGatedFeatures.js +6 -0
  79. package/dist/core/devGatedFeatures.js.map +1 -1
  80. package/dist/core/instarChannels.d.ts +2 -0
  81. package/dist/core/instarChannels.d.ts.map +1 -1
  82. package/dist/core/instarChannels.js +13 -0
  83. package/dist/core/instarChannels.js.map +1 -1
  84. package/dist/core/memorySandboxProbe.d.ts +8 -0
  85. package/dist/core/memorySandboxProbe.d.ts.map +1 -1
  86. package/dist/core/memorySandboxProbe.js +25 -0
  87. package/dist/core/memorySandboxProbe.js.map +1 -1
  88. package/dist/core/types.d.ts +35 -0
  89. package/dist/core/types.d.ts.map +1 -1
  90. package/dist/core/types.js.map +1 -1
  91. package/dist/data/standards-guard-index.json +1 -1
  92. package/dist/data/standards-guard-index.meta.json +2 -2
  93. package/dist/data/standards-registry.meta.json +1 -1
  94. package/dist/lifeline/ServerSupervisor.d.ts.map +1 -1
  95. package/dist/lifeline/ServerSupervisor.js +14 -0
  96. package/dist/lifeline/ServerSupervisor.js.map +1 -1
  97. package/dist/lifeline/TelegramLifeline.d.ts +10 -0
  98. package/dist/lifeline/TelegramLifeline.d.ts.map +1 -1
  99. package/dist/lifeline/TelegramLifeline.js +92 -16
  100. package/dist/lifeline/TelegramLifeline.js.map +1 -1
  101. package/dist/lifeline/pollDecision.d.ts +27 -0
  102. package/dist/lifeline/pollDecision.d.ts.map +1 -1
  103. package/dist/lifeline/pollDecision.js +28 -0
  104. package/dist/lifeline/pollDecision.js.map +1 -1
  105. package/dist/messaging/TelegramAdapter.d.ts +3 -0
  106. package/dist/messaging/TelegramAdapter.d.ts.map +1 -1
  107. package/dist/messaging/TelegramAdapter.js +12 -0
  108. package/dist/messaging/TelegramAdapter.js.map +1 -1
  109. package/dist/messaging/telegram-origin/OriginDeliveryResolution.d.ts.map +1 -1
  110. package/dist/messaging/telegram-origin/OriginDeliveryResolution.js +2 -1
  111. package/dist/messaging/telegram-origin/OriginDeliveryResolution.js.map +1 -1
  112. package/dist/messaging/telegram-origin/OriginForwardToHolder.d.ts +9 -1
  113. package/dist/messaging/telegram-origin/OriginForwardToHolder.d.ts.map +1 -1
  114. package/dist/messaging/telegram-origin/OriginForwardToHolder.js +48 -5
  115. package/dist/messaging/telegram-origin/OriginForwardToHolder.js.map +1 -1
  116. package/dist/messaging/telegram-origin/OriginMesh.d.ts.map +1 -1
  117. package/dist/messaging/telegram-origin/OriginMesh.js +4 -2
  118. package/dist/messaging/telegram-origin/OriginMesh.js.map +1 -1
  119. package/dist/messaging/telegram-origin/OriginOperationOutcome.d.ts +1 -0
  120. package/dist/messaging/telegram-origin/OriginOperationOutcome.d.ts.map +1 -1
  121. package/dist/messaging/telegram-origin/OriginOperationOutcome.js +21 -15
  122. package/dist/messaging/telegram-origin/OriginOperationOutcome.js.map +1 -1
  123. package/dist/messaging/telegram-origin/OriginStoreBackend.d.ts.map +1 -1
  124. package/dist/messaging/telegram-origin/OriginStoreBackend.js +7 -1
  125. package/dist/messaging/telegram-origin/OriginStoreBackend.js.map +1 -1
  126. package/dist/messaging/telegram-origin/TelegramOriginRuntime.js +1 -1
  127. package/dist/messaging/telegram-origin/TelegramOriginRuntime.js.map +1 -1
  128. package/dist/monitoring/PresenceProxy.d.ts +17 -0
  129. package/dist/monitoring/PresenceProxy.d.ts.map +1 -1
  130. package/dist/monitoring/PresenceProxy.js +61 -4
  131. package/dist/monitoring/PresenceProxy.js.map +1 -1
  132. package/dist/monitoring/SessionWatchdog.d.ts +22 -0
  133. package/dist/monitoring/SessionWatchdog.d.ts.map +1 -1
  134. package/dist/monitoring/SessionWatchdog.js +48 -1
  135. package/dist/monitoring/SessionWatchdog.js.map +1 -1
  136. package/dist/monitoring/rateLimitDetection.d.ts +37 -0
  137. package/dist/monitoring/rateLimitDetection.d.ts.map +1 -1
  138. package/dist/monitoring/rateLimitDetection.js +117 -0
  139. package/dist/monitoring/rateLimitDetection.js.map +1 -1
  140. package/dist/native/InstarWedgeCanary.app/Contents/MacOS/InstarWedgeCanary +0 -0
  141. package/dist/native/instar-desktop-host +0 -0
  142. package/dist/native/instar-desktop-host.json +1 -1
  143. package/dist/recommended/recipes.d.ts +37 -0
  144. package/dist/recommended/recipes.d.ts.map +1 -0
  145. package/dist/recommended/recipes.js +67 -0
  146. package/dist/recommended/recipes.js.map +1 -0
  147. package/dist/recommended/setupRestart.d.ts +18 -0
  148. package/dist/recommended/setupRestart.d.ts.map +1 -0
  149. package/dist/recommended/setupRestart.js +23 -0
  150. package/dist/recommended/setupRestart.js.map +1 -0
  151. package/dist/recommended/setupState.d.ts +25 -0
  152. package/dist/recommended/setupState.d.ts.map +1 -0
  153. package/dist/recommended/setupState.js +30 -0
  154. package/dist/recommended/setupState.js.map +1 -0
  155. package/dist/scaffold/templates.d.ts.map +1 -1
  156. package/dist/scaffold/templates.js +7 -6
  157. package/dist/scaffold/templates.js.map +1 -1
  158. package/dist/server/AgentServer.d.ts +3 -0
  159. package/dist/server/AgentServer.d.ts.map +1 -1
  160. package/dist/server/AgentServer.js +29 -1
  161. package/dist/server/AgentServer.js.map +1 -1
  162. package/dist/server/CapabilityIndex.d.ts.map +1 -1
  163. package/dist/server/CapabilityIndex.js +16 -6
  164. package/dist/server/CapabilityIndex.js.map +1 -1
  165. package/dist/server/middleware.d.ts.map +1 -1
  166. package/dist/server/middleware.js +6 -0
  167. package/dist/server/middleware.js.map +1 -1
  168. package/dist/server/mobilePairingRoutes.d.ts.map +1 -1
  169. package/dist/server/mobilePairingRoutes.js +10 -0
  170. package/dist/server/mobilePairingRoutes.js.map +1 -1
  171. package/dist/server/mobilePermissionsRoutes.d.ts +260 -0
  172. package/dist/server/mobilePermissionsRoutes.d.ts.map +1 -0
  173. package/dist/server/mobilePermissionsRoutes.js +684 -0
  174. package/dist/server/mobilePermissionsRoutes.js.map +1 -0
  175. package/dist/server/recommendedServiceRoutes.d.ts +27 -1
  176. package/dist/server/recommendedServiceRoutes.d.ts.map +1 -1
  177. package/dist/server/recommendedServiceRoutes.js +220 -2
  178. package/dist/server/recommendedServiceRoutes.js.map +1 -1
  179. package/dist/server/routes.d.ts +5 -0
  180. package/dist/server/routes.d.ts.map +1 -1
  181. package/dist/server/routes.js +374 -71
  182. package/dist/server/routes.js.map +1 -1
  183. package/dist/testing/selfActionRegistry.d.ts.map +1 -1
  184. package/dist/testing/selfActionRegistry.js +89 -0
  185. package/dist/testing/selfActionRegistry.js.map +1 -1
  186. package/dist/threadline/ThreadlineEndpoints.d.ts +3 -0
  187. package/dist/threadline/ThreadlineEndpoints.d.ts.map +1 -1
  188. package/dist/threadline/ThreadlineEndpoints.js +16 -1
  189. package/dist/threadline/ThreadlineEndpoints.js.map +1 -1
  190. package/dist/threadline/ThreadlineMCPServer.d.ts +2 -0
  191. package/dist/threadline/ThreadlineMCPServer.d.ts.map +1 -1
  192. package/dist/threadline/ThreadlineMCPServer.js +20 -11
  193. package/dist/threadline/ThreadlineMCPServer.js.map +1 -1
  194. package/dist/threadline/ThreadlineMcpHttpService.d.ts.map +1 -1
  195. package/dist/threadline/ThreadlineMcpHttpService.js +8 -0
  196. package/dist/threadline/ThreadlineMcpHttpService.js.map +1 -1
  197. package/dist/threadline/agentRelayStatus.d.ts +28 -0
  198. package/dist/threadline/agentRelayStatus.d.ts.map +1 -0
  199. package/dist/threadline/agentRelayStatus.js +66 -0
  200. package/dist/threadline/agentRelayStatus.js.map +1 -0
  201. package/dist/threadline/mcp-http-client.d.ts.map +1 -1
  202. package/dist/threadline/mcp-http-client.js +16 -19
  203. package/dist/threadline/mcp-http-client.js.map +1 -1
  204. package/dist/threadline/mcp-stdio-entry.js +8 -0
  205. package/dist/threadline/mcp-stdio-entry.js.map +1 -1
  206. package/dist/threadline/relayForward.d.ts.map +1 -1
  207. package/dist/threadline/relayForward.js +3 -1
  208. package/dist/threadline/relayForward.js.map +1 -1
  209. package/dist/tunnel/TunnelManager.d.ts.map +1 -1
  210. package/dist/tunnel/TunnelManager.js +3 -0
  211. package/dist/tunnel/TunnelManager.js.map +1 -1
  212. package/dist/users/MemoryAudience.d.ts +2 -0
  213. package/dist/users/MemoryAudience.d.ts.map +1 -1
  214. package/dist/users/MemoryAudience.js +5 -0
  215. package/dist/users/MemoryAudience.js.map +1 -1
  216. package/dist/users/MemoryInboundTracker.d.ts +16 -0
  217. package/dist/users/MemoryInboundTracker.d.ts.map +1 -1
  218. package/dist/users/MemoryInboundTracker.js +36 -0
  219. package/dist/users/MemoryInboundTracker.js.map +1 -1
  220. package/dist/users/MemoryPrivacy.d.ts +7 -0
  221. package/dist/users/MemoryPrivacy.d.ts.map +1 -1
  222. package/dist/users/MemoryPrivacy.js +15 -0
  223. package/dist/users/MemoryPrivacy.js.map +1 -1
  224. package/dist/users/MemoryPrivacyRecovery.d.ts +65 -0
  225. package/dist/users/MemoryPrivacyRecovery.d.ts.map +1 -0
  226. package/dist/users/MemoryPrivacyRecovery.js +124 -0
  227. package/dist/users/MemoryPrivacyRecovery.js.map +1 -0
  228. package/dist/users/wireMemoryPrivacy.d.ts +6 -0
  229. package/dist/users/wireMemoryPrivacy.d.ts.map +1 -1
  230. package/dist/users/wireMemoryPrivacy.js +74 -22
  231. package/dist/users/wireMemoryPrivacy.js.map +1 -1
  232. package/package.json +1 -1
  233. package/scripts/lint-machine-local-justification.js +12 -0
  234. package/scripts/locked-proof/configure-proof-agent.mjs +18 -5
  235. package/scripts/locked-proof/driver.mjs +45 -6
  236. package/scripts/locked-proof/stage-proof-agents.sh +31 -9
  237. package/skills/instar-dev/templates/side-effects-artifact.md +6 -0
  238. package/src/data/builtin-manifest.json +67 -67
  239. package/src/data/feature-ladders.json +2 -1
  240. package/src/data/standards-guard-index.json +1 -1
  241. package/src/data/standards-guard-index.meta.json +2 -2
  242. package/src/data/standards-registry.meta.json +1 -1
  243. package/src/data/state-coherence-registry.json +15 -0
  244. package/src/scaffold/templates.ts +7 -6
  245. package/upgrades/1.3.1660.md +82 -0
  246. package/upgrades/1.3.1661.md +77 -0
  247. package/upgrades/native-update-preflight.eli16.md +23 -0
  248. package/upgrades/side-effects/desktop-locked-proof-fixes.md +110 -0
  249. package/upgrades/side-effects/echo-bugs-codex-default.md +75 -0
  250. package/upgrades/side-effects/feedback-delivery-route-docs.md +70 -0
  251. package/upgrades/side-effects/lease-poll-boot-hold.md +48 -0
  252. package/upgrades/side-effects/locked-proof-harness-viewer.md +66 -0
  253. package/upgrades/side-effects/memory-privacy-owner-recovery.md +86 -0
  254. package/upgrades/side-effects/mobile-native-permissions.md +136 -0
  255. package/upgrades/side-effects/native-update-preflight.md +74 -0
  256. package/upgrades/side-effects/recommended-services-proof-fixes.md +65 -0
  257. package/upgrades/side-effects/recommended-services-runner.md +88 -0
  258. package/upgrades/side-effects/set-claude-key-no-cli.md +12 -0
  259. package/upgrades/side-effects/standby-telegram-replies.md +53 -0
  260. package/upgrades/side-effects/usage-wall-rescue.md +101 -0
  261. package/upgrades/side-effects/whole-agent-threadline-status.md +49 -0
@@ -9,6 +9,8 @@ const BACKUP_WORDS = {
9
9
  };
10
10
 
11
11
  function choiceWord(s) {
12
+ if (s.stalled) return 'Setup stopped';
13
+ if (s.verified && s.verified.result === 'pass') return 'Working';
12
14
  if (s.choice === 'yes') return 'You said yes';
13
15
  if (s.choice === 'no' || s.legacyDeclined) return 'You said no';
14
16
  return s.available ? 'Not decided' : 'Not available yet';
@@ -46,6 +46,8 @@ body.desktop-live #localCursor{display:block}
46
46
  #lockedPassword[hidden],#leaveUnlockedButton[hidden]{display:none}
47
47
  #lockedPassword input{width:100%;font-size:16px;padding:.55rem}
48
48
  #lockedPassword p{margin:0;font-size:.8rem}
49
+ #lockedPasswordReason{position:absolute;z-index:8;left:max(.5rem,env(safe-area-inset-left));right:max(.5rem,env(safe-area-inset-right));top:max(.5rem,env(safe-area-inset-top));margin:0;padding:.6rem;background:#111827f2;border:1px solid #64748b;border-radius:10px;color:#f1f5f9;font-size:.85rem;pointer-events:none}
50
+ #lockedPasswordReason[hidden]{display:none}
49
51
  #leaveUnlockedButton{position:absolute;z-index:8;right:max(.5rem,env(safe-area-inset-right));bottom:4rem;padding:.55rem .8rem}
50
52
  #lockedCard{margin-top:.8rem}
51
53
  #lockedCard[hidden]{display:none}
@@ -95,6 +95,7 @@
95
95
  <button type="submit">Unlock</button>
96
96
  <p>The machine serving this page and the tunnel provider can read what you type here. The Instar app is safer.</p>
97
97
  </form>
98
+ <p id="lockedPasswordReason" role="status" hidden></p>
98
99
  <button id="leaveUnlockedButton" type="button" hidden>Leave unlocked</button>
99
100
  <div id="keyboardBar" hidden>
100
101
  <label class="type-label" for="typeText">Type on remote desktop</label>
@@ -415,11 +415,19 @@ let controlSequence = 0n;
415
415
  let lockedSealKey = '';
416
416
  let lockedScreenState = '';
417
417
  let lockedBoxAllowed = false;
418
+ let lockedBoxRefusal = '';
418
419
  let secretSequence = 0;
419
420
  function syncLockedPassword() {
420
- const show = lockedBoxAllowed && lockedScreenState === 'locked-in-session' && !!lockedSealKey;
421
+ const locked = lockedScreenState === 'locked-in-session';
422
+ const show = lockedBoxAllowed && !lockedBoxRefusal && locked && !!lockedSealKey;
421
423
  $('lockedPassword').hidden = !show;
422
424
  if (!show) $('lockedPasswordInput').value = '';
425
+ // A locked Mac whose box cannot work says why, rather than showing nothing.
426
+ const reason = $('lockedPasswordReason');
427
+ if (reason) {
428
+ reason.hidden = !(locked && lockedBoxRefusal);
429
+ reason.textContent = locked && lockedBoxRefusal ? lockedMessage(lockedBoxRefusal) : '';
430
+ }
423
431
  }
424
432
  function lockedMessage(reason) {
425
433
  return { 'relock-pending': 'This Mac is finishing a relock. Try again in a couple of minutes.',
@@ -428,7 +436,30 @@ function lockedMessage(reason) {
428
436
  aborted: 'Stopped before the password finished. Type it again.',
429
437
  'field-dirty': 'Clearing the password field. Try again in a moment.',
430
438
  'browser-box-off': 'Password entry from a browser is off for this Mac.',
431
- 'tunnel-not-first-tier': 'Password entry is off while the backup connection is in use.' }[reason]
439
+ 'tunnel-not-first-tier': 'Password entry is off while the backup connection is in use.',
440
+ 'tunnel-not-active': 'Password entry from a browser is off until this Mac\'s secure tunnel is up. This page updates when it is.',
441
+ 'retained-helper-required': 'Password entry needs Instar\'s kept desktop helper on this Mac, and this install does not have one.',
442
+ 'own-input-grant-required': 'Password entry needs macOS Accessibility permission for Instar\'s own desktop helper on this Mac.',
443
+ 'unsupported-macos': 'Password entry is not available on this Mac\'s macOS version.',
444
+ 'helper-unprobed': 'This Mac\'s desktop helper has not answered yet, so password entry is not ready.',
445
+ 'shield-not-confirmed': 'This Mac\'s lock screen is not ready for a password right now. Wake the screen and try again.',
446
+ 'not-at-the-lock-screen': 'This Mac is not at its lock screen, so the password was not typed.',
447
+ 'not-locked': 'This Mac is not locked, so there is nothing to unlock.',
448
+ 'not-available': 'Password entry is not available in this session.',
449
+ 'grant-ended': 'This remote session has ended. Start a new one to try again.',
450
+ 'repeat': 'That password was already sent. Type it again to retry.',
451
+ 'too-long': 'That password is too long to type here.',
452
+ 'character-not-typable': 'That password has a character this Mac\'s keyboard layout cannot type from here.',
453
+ 'state-unavailable': 'This Mac could not read its relock record. Try again in a moment.',
454
+ 'relock-not-owed': 'This Mac is not ready for a password in this session. Start a new session.',
455
+ relocking: 'This Mac is relocking now. Try again in a couple of minutes.',
456
+ 'relock-pending-unconfirmed': 'This Mac is finishing a relock. Try again in a couple of minutes.',
457
+ 'relock-failed': 'This Mac could not confirm its last relock. Check it before trying again.',
458
+ 'relock-failed-producer-alive': 'This Mac could not confirm its last relock. Check it before trying again.',
459
+ 'guard-not-alive': 'This Mac\'s relock guard is not running, so password entry is paused. Try again shortly.',
460
+ disabled: 'Locked-screen access is turned off on this Mac.',
461
+ busy: 'This Mac is busy with another password. Try again in a moment.',
462
+ 'state-unknown': 'This Mac\'s lock state is unclear right now. Try again in a moment.' }[reason]
432
463
  ?? 'This Mac did not accept that. Try again.';
433
464
  }
434
465
  async function receiveControl(record) {
@@ -440,7 +471,11 @@ async function receiveControl(record) {
440
471
  controlSequence = sequence;
441
472
  const value = JSON.parse(new TextDecoder().decode(plain));
442
473
  if (value.type === 'seal') lockedSealKey = value.key;
443
- if (value.type === 'state') { lockedScreenState = value.state; lockedBoxAllowed = value.box === true; }
474
+ if (value.type === 'state') {
475
+ lockedScreenState = value.state;
476
+ lockedBoxAllowed = value.box === true;
477
+ lockedBoxRefusal = typeof value.boxRefusal === 'string' ? value.boxRefusal : '';
478
+ }
444
479
  if (value.type === 'secret-result' && value.refused) status(lockedMessage(value.refused));
445
480
  if (value.type === 'secret-result' && value.sent) { $('leaveUnlockedButton').hidden = false; }
446
481
  if (value.type === 'relock') status(value.phase === 'left-unlocked' ? 'This Mac will stay unlocked.' : 'Relocking has begun.');
@@ -934,6 +969,7 @@ async function openStream(result, resume = null) {
934
969
  socket.binaryType = 'arraybuffer';
935
970
  controlSequence = 0n;
936
971
  lockedSealKey = '';
972
+ lockedBoxRefusal = '';
937
973
  secretSequence = 0;
938
974
  syncLockedPassword();
939
975
  return new Promise((resolve, reject) => {
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/commands/server.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/commands/server.ts"],"names":[],"mappings":"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"}
@@ -5,8 +5,7 @@
5
5
  */
6
6
  import { configureClaudeChildAuth, sweepClaudeAuthEnvFiles } from '../core/claudeChildAuth.js';
7
7
  import { SlackPrincipalResolver } from '../permissions/SlackPrincipalResolver.js';
8
- import { MEMORY_PRIVACY_UNAVAILABLE_REPLY } from '../users/MemoryInboundTracker.js';
9
- import { whatsappPersonKey } from '../users/MemoryAudience.js';
8
+ import { whatsappPersonKey, whatsappSpeakerJid } from '../users/MemoryAudience.js';
10
9
  import { slackHistoryLabel } from '../messaging/slack/threadHistoryLabel.js';
11
10
  import { readPairingOwnerName } from '../threadline/pairingOwnerName.js';
12
11
  import { buildThrowawayJobGuard, checkThrowawayExpiry, describeExpiry, takeLastAutoStartRefusal, throwawayStartupDecision } from '../core/ThrowawayAgentGuard.js';
@@ -58,6 +57,7 @@ import { setAspClassifierRef, getAspClassifierRef } from '../core/aspClassifierR
58
57
  import { resolveInboundTelegramMessageId, resolveSignedByAgent, operatorAutoBindPermitted } from '../messaging/shared/signedInbound.js';
59
58
  import { verifiedEntityName } from '../messaging/shared/channelIdentity.js';
60
59
  import { StandDownRegistry } from '../core/StandDownRegistry.js';
60
+ import { ensureDashboardPin, persistDashboardPin, pickDashboardPin } from '../core/dashboardPin.js';
61
61
  import { getActiveOverloadShedPlanner, getMoveAftercare } from '../core/OverloadShedPlanner.js';
62
62
  import { QuietTopicScanner } from '../core/quietTopicScanner.js';
63
63
  import { RebalanceProofRunner, getActiveRebalanceProof, waitForSettledIdle, proofSpawnBrief, setActiveRebalanceProof, setActiveRebalanceProofStore } from '../core/rebalanceProof.js';
@@ -426,9 +426,11 @@ async function handleFixCommand(topicId, text, deps) {
426
426
  return true;
427
427
  }
428
428
  if (cmd === 'fix dashboard') {
429
- const existing = deps.liveConfig.get('dashboardPin', '');
430
- if (existing) {
431
- await send(`Your dashboard already has a PIN: ${existing}`);
429
+ // A vault-backed config holds {secret:true}; resolve it rather than print an object.
430
+ const stored = deps.liveConfig.get('dashboardPin', '');
431
+ if (stored) {
432
+ const existing = pickDashboardPin(stored, deps.config.stateDir);
433
+ await send(existing ? `Your dashboard already has a PIN: ${existing}` : 'Your dashboard already has a PIN, kept in its secret store.');
432
434
  return true;
433
435
  }
434
436
  const pin = String(randomInt(100000, 1000000));
@@ -642,6 +644,8 @@ let _topicProfileCtxRef = null;
642
644
  let _inboundQueue = null;
643
645
  /** Memory privacy (docs/specs/memory-privacy.md): set in startServer, read at every inbound. */
644
646
  let _memoryInbound = null;
647
+ /** The tunnel's address for a path, once a tunnel exists. The tunnel starts after memory privacy. */
648
+ let _memoryLinkFor = null;
645
649
  function telegramConfiguredForMemory(config) {
646
650
  return (config.messaging ?? []).some((m) => m.type === 'telegram');
647
651
  }
@@ -1136,6 +1140,10 @@ let _quotaAwareScheduler = null;
1136
1140
  // Subscription & Auth Standard P1.3 — pre-limit proactive swap monitor. Null
1137
1141
  // until wired (requires the scheduler; gated behind subscriptionPool.proactiveSwap).
1138
1142
  let _proactiveSwapMonitor = null;
1143
+ // usage-wall-rescue — moves a session off a walled account (tagged or not) and
1144
+ // tags spawns. Null until wired next to the scheduler; read late by the
1145
+ // PresenceProxy wording hook.
1146
+ let _usageWallRescue = null;
1139
1147
  // swap-continuity-antithrash §3/§4 — the anti-thrash spine. The ledger + engine
1140
1148
  // load and hydrate UNCONDITIONALLY at boot (R4-L3: reactive rows append
1141
1149
  // regardless of antiThrash.enabled, so the index is warm at a mid-run flag
@@ -1411,7 +1419,9 @@ spawnOpts) {
1411
1419
  _topicResumeMap?.remove(topicId);
1412
1420
  console.log(`[spawnSessionForTopic] Context-exhaustion latch active for topic ${topicId} — forcing fresh spawn`);
1413
1421
  }
1414
- let resumeSessionId = _topicResumeMap?.get(topicId) ?? undefined;
1422
+ // A group default can change the resolved framework without a per-topic pin
1423
+ // event. Never pass a saved Claude UUID to Codex (or vice versa) at restart.
1424
+ let resumeSessionId = _topicResumeMap?.getForFramework(topicId, framework) ?? undefined;
1415
1425
  if (resumeSessionId) {
1416
1426
  console.log(`[spawnSessionForTopic] Found resume UUID for topic ${topicId}: ${resumeSessionId} (source: TopicResumeMap — trusted)`);
1417
1427
  }
@@ -2356,16 +2366,22 @@ getHubClassifierDeps) {
2356
2366
  : null;
2357
2367
  // Memory privacy: record who spoke and re-check the group's readers BEFORE routing.
2358
2368
  if (_memoryInbound) {
2359
- const verdict = await _memoryInbound.onInbound({
2369
+ const memoryFacts = {
2360
2370
  placeKey: telegram.memoryPlaceKey,
2361
2371
  placeKind: 'shared',
2362
2372
  senderKey: resolvedUser ? `user:${resolvedUser.id}` : telegramUserId ? `telegram:${telegramUserId}` : null,
2363
2373
  personId: resolvedUser?.id ?? null,
2364
2374
  readerCount: () => telegram.groupReaderCount(),
2365
2375
  liveSession: () => telegram.getSessionForTopic(topicId),
2366
- });
2376
+ };
2377
+ const verdict = await _memoryInbound.onInbound(memoryFacts);
2367
2378
  if (verdict === 'unavailable') {
2368
- await telegram.sendToTopic(topicId, MEMORY_PRIVACY_UNAVAILABLE_REPLY);
2379
+ await telegram.sendToTopic(topicId, await _memoryInbound.refuse(memoryFacts, async (text) => {
2380
+ // This DM has one fixed recipient, so it goes only when that is who asked.
2381
+ if (!telegramUserId || telegram.getOwnerUserId() !== telegramUserId)
2382
+ return false;
2383
+ return (await telegram.sendToOwnerDM(text)) !== null;
2384
+ }));
2369
2385
  return;
2370
2386
  }
2371
2387
  }
@@ -3262,17 +3278,23 @@ getHubClassifierDeps) {
3262
3278
  async function whatsappMemoryCheck(whatsapp, jid, metadata) {
3263
3279
  if (!_memoryInbound)
3264
3280
  return true;
3265
- const profile = _memoryUserResolver?.channel('whatsapp', jid) ?? null;
3266
- const verdict = await _memoryInbound.onInbound({
3281
+ const senderJid = whatsappSpeakerJid(metadata, jid);
3282
+ const profile = _memoryUserResolver?.channel('whatsapp', senderJid) ?? null;
3283
+ const memoryFacts = {
3267
3284
  placeKey: `whatsapp:${jid}`,
3268
3285
  placeKind: jid.endsWith('@g.us') ? 'shared' : 'private',
3269
- senderKey: profile ? `user:${profile.id}` : whatsappPersonKey(metadata?.senderJid ?? jid),
3286
+ senderKey: profile ? `user:${profile.id}` : whatsappPersonKey(senderJid),
3270
3287
  personId: profile?.id ?? null,
3271
3288
  liveSession: () => whatsapp.getSessionForChannel(jid),
3272
- });
3289
+ };
3290
+ const verdict = await _memoryInbound.onInbound(memoryFacts);
3273
3291
  if (verdict === 'deliver')
3274
3292
  return true;
3275
- await whatsapp.send({ userId: jid, content: MEMORY_PRIVACY_UNAVAILABLE_REPLY, channel: { type: 'whatsapp', identifier: jid } });
3293
+ const content = await _memoryInbound.refuse(memoryFacts, async (text) => {
3294
+ await whatsapp.send({ userId: senderJid, content: text, channel: { type: 'whatsapp', identifier: senderJid } });
3295
+ return true;
3296
+ });
3297
+ await whatsapp.send({ userId: jid, content, channel: { type: 'whatsapp', identifier: jid } });
3276
3298
  return false;
3277
3299
  }
3278
3300
  /**
@@ -3440,16 +3462,17 @@ function wireIMessageRouting(imessage, sessionManager) {
3440
3462
  // with imsg, which the sandbox cannot give, so a scoped session is refused here.
3441
3463
  if (_memoryInbound) {
3442
3464
  const imProfile = _memoryUserResolver?.channel('imessage', sender) ?? null;
3443
- const verdict = await _memoryInbound.onInbound({
3465
+ const memoryFacts = {
3444
3466
  placeKey: `imessage:${senderNorm}`,
3445
3467
  placeKind: 'private',
3446
3468
  senderKey: imProfile ? `user:${imProfile.id}` : `imessage:${sender}`,
3447
3469
  personId: imProfile?.id ?? null,
3448
3470
  scopedUnsupported: true,
3449
3471
  liveSession: () => imessage.getSessionForSender(sender),
3450
- });
3472
+ };
3473
+ const verdict = await _memoryInbound.onInbound(memoryFacts);
3451
3474
  if (verdict === 'unavailable') {
3452
- await imessage.send({ userId: sender, content: MEMORY_PRIVACY_UNAVAILABLE_REPLY, channel: { type: 'imessage', identifier: sender } });
3475
+ await imessage.send({ userId: sender, content: await _memoryInbound.refuse(memoryFacts), channel: { type: 'imessage', identifier: sender } });
3453
3476
  return;
3454
3477
  }
3455
3478
  }
@@ -6505,6 +6528,7 @@ export async function startServer(options) {
6505
6528
  projectName: config.projectName,
6506
6529
  framework: () => sessionManagerConfig.framework ?? 'claude-code',
6507
6530
  claudePath: sessionManagerConfig.frameworkBinaryPaths?.['claude-code'] ?? sessionManagerConfig.claudePath,
6531
+ linkFor: (urlPath) => _memoryLinkFor?.(urlPath) ?? null,
6508
6532
  memoryPrivacy: () => config.memoryPrivacy,
6509
6533
  messaging: () => (config.messaging ?? []),
6510
6534
  initialUsers: config.users,
@@ -6865,6 +6889,7 @@ export async function startServer(options) {
6865
6889
  defaultFramework: () => _defaultFramework,
6866
6890
  configTopicFrameworks: () => _topicFrameworks,
6867
6891
  configProfileDefaults: () => topicProfilesCfg?.defaults ?? {},
6892
+ configGroupDefaults: () => topicProfilesCfg?.groupDefaults ?? {},
6868
6893
  frameworkDefaultModels: () => config.sessions?.frameworkDefaultModels ?? {},
6869
6894
  tierEscalationConfig: () => normalizeTierEscalationConfig(config.models?.tierEscalation),
6870
6895
  localModelBinding: (topicKey) => _topicLocalModelStore?.get(Number(topicKey)) ?? null,
@@ -9578,16 +9603,21 @@ export async function startServer(options) {
9578
9603
  // a channel or thread is shared, and a thread's audience is its channel's.
9579
9604
  if (_memoryInbound) {
9580
9605
  const slackProfile = slackUserId ? _memoryUserResolver?.slack(slackUserId) ?? null : null;
9581
- const verdict = await _memoryInbound.onInbound({
9606
+ const memoryFacts = {
9582
9607
  placeKey: `slack:${channelId}`,
9583
9608
  placeKind: channelId.startsWith('D') ? 'private' : 'shared',
9584
9609
  senderKey: slackProfile ? `user:${slackProfile.id}` : slackUserId ? `slack:${slackUserId}` : null,
9585
9610
  personId: slackProfile?.id ?? null,
9586
9611
  readerCount: () => slackAdapter.channelReaderCount(channelId),
9587
9612
  liveSession: () => slackAdapter.getSessionForChannel(routingKey),
9588
- });
9613
+ };
9614
+ const verdict = await _memoryInbound.onInbound(memoryFacts);
9589
9615
  if (verdict === 'unavailable') {
9590
- await slackAdapter.sendToChannel(channelId, MEMORY_PRIVACY_UNAVAILABLE_REPLY, replyThreadTs ? { thread_ts: replyThreadTs } : undefined);
9616
+ // Posting to a user id opens the app's direct message with them.
9617
+ const reply = await _memoryInbound.refuse(memoryFacts, slackUserId
9618
+ ? async (text) => { await slackAdapter.sendToChannel(slackUserId, text); return true; }
9619
+ : undefined);
9620
+ await slackAdapter.sendToChannel(channelId, reply, replyThreadTs ? { thread_ts: replyThreadTs } : undefined);
9591
9621
  return 'refused';
9592
9622
  }
9593
9623
  }
@@ -13125,6 +13155,7 @@ export async function startServer(options) {
13125
13155
  if (!config.tunnel) {
13126
13156
  liveConfig.set('tunnel', { enabled: true, type: 'quick' });
13127
13157
  }
13158
+ _memoryLinkFor = (urlPath) => tunnel?.getExternalUrl(urlPath) ?? null;
13128
13159
  tunnel = new TunnelManager({
13129
13160
  enabled: true,
13130
13161
  type: config.tunnel?.type || 'quick',
@@ -16101,6 +16132,9 @@ export async function startServer(options) {
16101
16132
  hasContextExhaustionLatch: sessionRecovery
16102
16133
  ? (topicId) => sessionRecovery.hasContextWedgedSeen(topicId)
16103
16134
  : undefined,
16135
+ // usage-wall-rescue: when another account has room, the standby says
16136
+ // the session is being moved — never "paused until reset".
16137
+ describeQuotaWall: (sessionName) => _usageWallRescue?.describeWall(sessionName) ?? null,
16104
16138
  // Shared per-topic mutex — coordinates with PromiseBeacon.
16105
16139
  acquireProxyMutex: (topicId, holder) => proxyCoordinator.tryAcquire(topicId, holder),
16106
16140
  releaseProxyMutex: (topicId, holder) => proxyCoordinator.release(topicId, holder),
@@ -21184,8 +21218,8 @@ export async function startServer(options) {
21184
21218
  onNoAlternate: telegramRef ? (sessionName, exhaustedAccountId) => {
21185
21219
  void telegramRef.createAttentionItem({
21186
21220
  id: `subpool-no-alternate-${exhaustedAccountId}`,
21187
- title: 'Subscription pool — no alternate account to swap to',
21188
- summary: `Session "${sessionName}" hit account "${exhaustedAccountId}"'s quota and there's no other eligible account in the pool. The session falls back to the existing rate-limit back-off; consider enrolling another account.`,
21221
+ title: 'Every subscription account is at its usage limit',
21222
+ summary: `Session "${sessionName}" reached the usage limit on account "${exhaustedAccountId}", and no other connected account has room. It will carry on by itself when the limit resets. Connecting another account would let it carry on sooner.`,
21189
21223
  category: 'subscription-pool',
21190
21224
  priority: 'HIGH',
21191
21225
  sourceContext: 'subscription-pool:no-alternate',
@@ -21193,36 +21227,84 @@ export async function startServer(options) {
21193
21227
  } : undefined,
21194
21228
  logger: { log: (m) => console.log(m), warn: (m) => console.warn(m) },
21195
21229
  });
21196
- // DARK auto-trigger: only when explicitly enabled does a rate-limit
21197
- // escalation drive an account swap. Default off — opt-in per Justin's
21198
- // tier-2 sign-off (auto-swapping live sessions is real authority).
21199
- if (config.subscriptionPool?.autoSwapOnRateLimit) {
21200
- rateLimitSentinel.on('rate-limit:escalated', (rlState) => {
21201
- const sessionName = rlState?.sessionName;
21202
- if (!sessionName)
21203
- return;
21204
- // Resolve which account this session is running under; only pool-managed
21205
- // sessions are swap-eligible (others fall back to the existing back-off).
21206
- const session = state.listSessions({ status: 'running' })
21207
- .find(s => s.tmuxSession === sessionName);
21208
- const accountId = session?.subscriptionAccountId;
21209
- if (!accountId)
21210
- return;
21211
- void _quotaAwareScheduler?.onQuotaPressure({
21212
- sessionName,
21213
- exhaustedAccountId: accountId,
21214
- nowMs: Date.now(),
21215
- framework: session?.framework ?? 'claude-code',
21216
- });
21230
+ // usage-wall-rescue (docs/specs/usage-wall-rescue.md). ONE switch,
21231
+ // `subscriptionPool.wallRescue.enabled` (default true, operator-approved
21232
+ // 2026-11-04), read LIVE; effective only while the pool has 2+ usable
21233
+ // accounts of the session's framework. Covers untagged sessions by
21234
+ // resolving the account the session is actually on.
21235
+ const { UsageWallRescue } = await import('../core/UsageWallRescue.js');
21236
+ const readWallRescueConfig = () => liveConfig.get('subscriptionPool', config.subscriptionPool);
21237
+ _usageWallRescue = new UsageWallRescue({
21238
+ readConfig: readWallRescueConfig,
21239
+ listAccounts: () => subscriptionPool.list(),
21240
+ getSession: (name) => state.listSessions({ status: 'running' }).find((s) => s.tmuxSession === name),
21241
+ configHomeForSession: (name) => sessionManager.configHomeForSession(name),
21242
+ resolveDefaultAccountId: async () => (await inUseAccountResolver.resolve(subscriptionPool.list())).activeAccountId,
21243
+ swap: (a) => _quotaAwareScheduler
21244
+ ? _quotaAwareScheduler.onQuotaPressure(a)
21245
+ : Promise.resolve({ swapped: false, toAccountId: null, reason: 'scheduler-unavailable' }),
21246
+ // Plain notice to the conversation, only when the session cannot be
21247
+ // moved (no account has room, or moves kept failing).
21248
+ // Coalesced: one in-flight poll at a time and at most one per minute, so
21249
+ // many sessions holding on a stale reading cannot fan out pool polls.
21250
+ requestQuotaRefresh: (() => {
21251
+ let inFlight = false;
21252
+ let lastAt = 0;
21253
+ return () => {
21254
+ const now = Date.now();
21255
+ if (inFlight || now - lastAt < 60_000)
21256
+ return;
21257
+ inFlight = true;
21258
+ lastAt = now;
21259
+ void Promise.resolve(quotaPoller.pollAll())
21260
+ .catch(() => { })
21261
+ .finally(() => { inFlight = false; });
21262
+ };
21263
+ })(),
21264
+ notifyUser: (sessionName, text) => {
21265
+ const raw = telegram?.getTopicForSession(sessionName);
21266
+ const topicId = typeof raw === 'number' ? raw : Number(raw);
21267
+ if (raw == null || !Number.isFinite(topicId))
21268
+ return false;
21269
+ // Resolves true only on a confirmed send; anything else leaves the
21270
+ // standby to report the wall.
21271
+ return sendAutomationReply('usage-wall-rescue', topicId, { text, metadata: { provenance: 'automation' } }, deterministicAutomationAuthor())
21272
+ .then((resp) => resp.ok === true)
21273
+ .catch(() => false);
21274
+ },
21275
+ logger: { log: (m) => console.log(m), warn: (m) => console.warn(m) },
21276
+ });
21277
+ const wallRescue = _usageWallRescue;
21278
+ void wallRescue.refreshDefaultAccount();
21279
+ const wallRescueDefaultTimer = setInterval(() => { void wallRescue.refreshDefaultAccount(); }, 60_000);
21280
+ wallRescueDefaultTimer.unref?.();
21281
+ // The trigger: the watchdog's settled usage-wall signal.
21282
+ if (watchdog) {
21283
+ watchdog.on('usage-wall', (sessionName) => {
21284
+ void wallRescue.onUsageWall(sessionName);
21285
+ });
21286
+ watchdog.on('usage-wall-cleared', (sessionName) => {
21287
+ wallRescue.onWallCleared(sessionName);
21217
21288
  });
21218
21289
  }
21219
- // Subscription-pool session PINNING (Subscription & Auth Standard): when
21220
- // enabled, new claude-code spawns launch on the scheduler-picked optimal
21221
- // account (reset-date / headroom score) and carry its id. This is the
21222
- // prerequisite that makes auto-swap functional — without a session→account
21223
- // tag, the swap engine has nothing to move. Default off → spawns use the
21224
- // default config exactly as before (the resolver stays unwired).
21225
- if (config.subscriptionPool?.pinSessionsToPool) {
21290
+ // Legacy opt-in: an explicit autoSwapOnRateLimit:true also moves a session
21291
+ // on a server-throttle escalation (a throttle is shared capacity, so the
21292
+ // default-on rescue does not move sessions for it). It goes through the
21293
+ // SAME funnel and brakes as the wall; untagged sessions are resolved
21294
+ // instead of skipped. The live config is checked per event.
21295
+ rateLimitSentinel.on('rate-limit:escalated', (rlState) => {
21296
+ const sessionName = rlState?.sessionName;
21297
+ if (!sessionName)
21298
+ return;
21299
+ void wallRescue.onThrottleEscalated(sessionName);
21300
+ });
21301
+ // Session PINNING / tagging. An explicit pinSessionsToPool:true pins
21302
+ // every claude-code spawn (as before). Otherwise the rescue's resolver
21303
+ // tags spawns when the pool has 2+ usable accounts (so no session is
21304
+ // untagged) and, with pinning explicitly off, still refuses to launch on
21305
+ // a default login the pool knows is walled. 0/1 usable accounts → null →
21306
+ // the default login, exactly as before.
21307
+ if (config.subscriptionPool?.pinSessionsToPool === true) {
21226
21308
  const { selectAccount } = await import('../core/QuotaAwareScheduler.js');
21227
21309
  sessionManager.setSpawnAccountResolver(() => {
21228
21310
  const acct = selectAccount(subscriptionPool.list(), { nowMs: Date.now(), framework: 'claude-code' });
@@ -21230,12 +21312,16 @@ export async function startServer(options) {
21230
21312
  });
21231
21313
  console.log(pc.green(' Subscription-pool session pinning enabled'));
21232
21314
  }
21315
+ else {
21316
+ sessionManager.setSpawnAccountResolver(() => wallRescue.spawnAccount());
21317
+ }
21233
21318
  // ProactiveSwapMonitor (Subscription & Auth Standard P1.3 — the PRE-LIMIT
21234
21319
  // half). Moves a session OFF an account BEFORE it walls, at a lag-aware
21235
- // measured threshold. Complements the reactive autoSwapOnRateLimit (which
21236
- // only fires AFTER the wall) and covers UNTAGGED sessions by resolving the
21237
- // default-config login — so the primary interactive session is swap-visible
21238
- // instead of wedging (the 2026-06-09 failure). DARK by default: only runs
21320
+ // measured threshold. Complements the reactive usage-wall rescue (which
21321
+ // only fires AFTER the wall). It does NOT move untagged sessions (Q3):
21322
+ // those are reached by the default-slot repoint arm, and on a pool with
21323
+ // 2+ usable accounts the wall rescue tags new spawns so few stay
21324
+ // untagged. DARK by default: only runs
21239
21325
  // when subscriptionPool.proactiveSwap.enabled is true (moving live sessions
21240
21326
  // is real authority).
21241
21327
  const proactiveCfg = config.subscriptionPool?.proactiveSwap;
@@ -28973,8 +29059,8 @@ export async function startServer(options) {
28973
29059
  const result = reply.result;
28974
29060
  if (reply.ok && result?.ok && result.operationId === operationId) {
28975
29061
  const state = String(result.state);
28976
- if (state === 'accepted' && result.originReceiptConfirmed === true)
28977
- return { state: 'accepted', receiptJson: JSON.stringify({ confirmedVia: 'receipt', deliveryMachineId: machineId, operationId }) };
29062
+ if (state === 'accepted' && result.originReceiptConfirmed === true && result.messageIds?.length === 1 && /^[1-9]\d*$/.test(result.messageIds[0]))
29063
+ return { state: 'accepted', receiptJson: JSON.stringify({ messageId: result.messageIds[0], confirmedVia: 'receipt', deliveryMachineId: machineId, operationId }) };
28978
29064
  // Definite non-admission: never seen, evidence only, or terminal without delivery.
28979
29065
  if (['no-record', 'not-admitted', 'known-failed', 'expired', 'suppressed', 'superseded'].includes(state))
28980
29066
  return { state: 'not-accepted' };
@@ -28995,7 +29081,7 @@ export async function startServer(options) {
28995
29081
  },
28996
29082
  resolve: (operationId, deliveryMachineId, receiptJson) => runtime.store.recordForwardedAcceptance({ operationId, deliveryMachineId, receiptJson }),
28997
29083
  settleUnknown: (operationId, deliveryMachineId) => runtime.store.recordForwardedOutcomeUnknown({ operationId, deliveryMachineId }),
28998
- rehold: async (operationId, detail) => { await runtime.store.recordOperationState({ operationId, state: 'held', holdReason: originForwardMod.HOLD_REASON_LEASE_NOT_HELD, holdDetail: detail }); },
29084
+ rehold: async (operationId, detail) => runtime.store.recordOperationState({ operationId, state: 'held', holdReason: originForwardMod.HOLD_REASON_LEASE_NOT_HELD, holdDetail: detail }),
28999
29085
  supersede: (operationId, next, detail) => runtime.store.supersedeWithAdmission({ operationId, admission: next.admission,
29000
29086
  holdReason: originForwardMod.HOLD_REASON_LEASE_NOT_HELD, holdDetail: detail }),
29001
29087
  sendLocal: async (detail) => {
@@ -29004,6 +29090,14 @@ export async function startServer(options) {
29004
29090
  },
29005
29091
  }, row, operation);
29006
29092
  // §3.3 — a delivered/resolved forward may have emptied the topic's held set.
29093
+ if (outcome === 'sent' || outcome === 'resolved') {
29094
+ // The original adapter call returned HELD, so it never wrote its
29095
+ // outbound topic row. Only a durable receipt with a concrete
29096
+ // Telegram id may fill that gap; this never sends a second copy.
29097
+ const recovered = await originForwardMod.confirmedRecoveredForward(row, id => runtime.store.getOperation(id)).catch(() => null);
29098
+ if (recovered)
29099
+ telegram.recordRecoveredOutbound(recovered.topicId, recovered.text, recovered.messageId, row.producerKind === 'session' ? 'agent' : 'automation');
29100
+ }
29007
29101
  if (outcome === 'sent' || outcome === 'resolved' || outcome === 'settled')
29008
29102
  await clearHeldItemIfResolved(row.destination.topicId);
29009
29103
  return outcome;
@@ -30413,7 +30507,7 @@ export async function startServer(options) {
30413
30507
  const server = new AgentServer({ config, ecosystemLedger: _ecosystemLedger ?? undefined, recommendedServicesLedger: _recommendedServicesLedger ?? undefined, singleInstanceLock, agentIdentity: _agentIdentityDeps, terminateSessionAuthority: terminateWithAuthority, subscriptionEmailBinding: credentialLocationLedger, subscriptionEmailBarrier, subscriptionIdentityOracle, sessionManager, llmQueue: sharedLlmQueue, state, scheduler, telegram, telegramOrigin: telegramOriginBoot?.runtime, relationships, feedback, feedbackAnomalyDetector, dispatches, updateChecker, autoUpdater, autoDispatcher, quotaTracker, quotaManager, publisher, viewer, jobAnomalySendAutomationReply: sendAutomationReply, tunnel, evolution, watchdog, topicMemory, triageNurse, projectMapper, cartographerRoots: cartographerRoots ?? undefined, coherenceGate: scopeVerifier, contextHierarchy, canonicalState, operationGate, sentinel, adaptiveTrust, memoryMonitor, orphanReaper, coherenceMonitor, commitmentTracker, subscriptionPool, accountFollowMePeerViews: async () => { const nickById = new Map((_listPoolMachines?.() ?? []).map((m) => [m.machineId, verifiedEntityName(m.machineId, m.nickname) ?? ''])); let peers = (_resolvePeerUrls?.() ?? []).map((p) => ({ machineId: p.machineId, nickname: nickById.get(p.machineId) ?? '', url: p.url })); if (peers.length === 0) {
30414
30508
  peers = (_listPoolMachines?.() ?? []).filter((m) => m.machineId !== _meshSelfId && !!m.lastKnownUrl).map((m) => ({ machineId: m.machineId, nickname: verifiedEntityName(m.machineId, m.nickname) ?? '', url: m.lastKnownUrl }));
30415
30509
  } if (peers.length === 0)
30416
- return []; const { fetchPeerSubscriptionViews } = await import('../core/fetchPeerSubscriptionViews.js'); return fetchPeerSubscriptionViews({ peers: () => peers, fetchImpl: fetch, authToken: config.authToken ?? '' }); }, quotaPoller, quotaAwareScheduler: _quotaAwareScheduler ?? undefined, proactiveSwapMonitor: _proactiveSwapMonitor ?? undefined, inUseAccountResolver, enrollmentWizard, accountFollowMeRevocation, credentialRepointing, semanticMemory, activitySentinel, rateLimitSentinel, releaseReadinessSentinel: releaseReadinessSentinel ?? undefined, greenPrAutoMerger: greenPrAutoMerger ?? undefined, localVerify: localVerifyService ?? undefined, localVerifyStatus: localVerifyStatus ?? undefined, guardLatchStore: guardLatchStore ?? undefined, agentStopSurfacer: agentStopSurfacer ?? undefined, messageRouter, summarySentinel, spawnManager, systemReviewer, capabilityMapper, selfKnowledgeTree, coverageAuditor, topicResumeMap: _topicResumeMap ?? undefined, topicProfile: _topicProfileCtx ?? undefined, sessionRefresh: _sessionRefresh ?? undefined, autonomyManager, trustElevationTracker, autonomousEvolution, coordinator: coordinator.enabled ? coordinator : undefined, meshBindActive: coordinator.managers.identityManager.hasIdentity() && config.multiMachine?.meshTransport?.enabled !== false, localSigningKeyPem, leaseTransport, peerEndpointRecorder, getSelfMeshEndpoints, onLeasePullRequest: () => leaseCoordinatorRef?.currentLease() ?? null, liveTailReceiver, handoffWireTransport, onHandoffBegin, onHandoffInitiate: handoffInitiate, handoffInProgress: handoffSentinelInProgress, messageLedger, currentInboundByTopic, replyMarkerTransport, onReplyMarker: messageLedger ? (marker) => { const m = marker; messageLedger.applyRemoteReplyMarker(m.dedupeKey, { platform: m.platform, replyIdempotencyKey: m.replyIdempotencyKey, epoch: m.epoch, topic: m.topic ?? null }); } : undefined, whatsapp: whatsappAdapter, slack: slackAdapter, imessage: imessageAdapter, conversationRegistry, conversationBindAuth, appConversations: _appConversationService ?? undefined, slackInboundDispatch: () => _slackInboundDispatch, conversationFollowThrough, whatsappBusinessBackend, messageBridge, hookEventReceiver, worktreeMonitor, subagentTracker, instructionsVerifier, handshakeManager: threadlineHandshake, threadlineRouter, conversationStore, threadLog, threadMessageRecorder, warrantsReplyGate, collaborationSurfacer, peerHomebaseRelay, threadResumeMap, topicLinkageHandler: topicLinkageHandler ?? undefined, threadlineRelayClient, pairingInvites: threadlinePairingInvites, getLastRelayEvent: threadlineGetLastRelayEvent, a2aRelayForward: _a2aRelayForwardCtx ?? undefined, threadlineReplyWaiters, threadlineMcpHttp, memoryPrivacy: _memoryPrivacyWired ?? undefined, listenerManager: listenerManager ?? undefined, a2aDeliveryTracker: a2aDeliveryTracker ?? undefined, relayVerdictCounters: threadlineRelayClient ? (() => ({ ...threadlineRelayClient.relayVerdictCounters, expiredMismatchIgnored: a2aDeliveryTracker?.expiredMismatchIgnored ?? 0 })) : undefined, inboundIdLedger: _inboundIdLedger ?? undefined, inboundIdLedgerDaemonDeferred: () => _inboundIdLedgerDaemonDeferred, responseReviewGate, reviewCanaryBattery, messagingToneGate, outboundDedupGate, telemetryHeartbeat, pasteManager, featureRegistry, discoveryEvaluator, completionEvaluator, unifiedTrust, liveConfig, sharedStateLedger, ledgerSessionRegistry, worktreeManager, oidcEnrolledRepos: parallelDevConfig?.oidcEnrolledRepos, initiativeTracker, projectRoundRunner, projectDriftChecker, machineHeartbeat, machinePoolRegistry, ropeHealthMonitor, writeAdmission: writeAdmission ?? undefined, getInboundQueue: () => _inboundQueue, getMachineCoherence: () => _machineCoherenceSentinel, getSingleMachineFailoverGap: () => _singleMachineFailoverGap, getMissingLoginSession: () => _missingLoginSession, getSessionPoolFailoverRunner: () => _sessionPoolFailoverRunnerDriver?.status() ?? null, sessionPoolPromotionActivation: _sessionPoolPromotionActivation, meshRpcDispatcher, deliverA2aToMachine: _deliverA2aToMachine ?? undefined, workingSetPullCoordinator, workingSetArtifactManager, orchestratorPoller, commitmentReplicaStore, preferenceReplicaStore, replicatedRecordEmitter, replicatedStoreReconciler, conflictStore, rollbackUnmerge, droppedOriginRegistry, preferencesUnionReader, jevMemoryPicker, forwardCommitmentMutate, sessionOwnershipRegistry, sendDrain: _sendDrain ?? undefined, claimTopicOwnershipOnSpawn: (topicId) => _claimOwnershipForAutonomousSpawn ? _claimOwnershipForAutonomousSpawn(topicId) : 'unwired', topicPinStore: _topicPinStore ?? undefined, topicPinSkewQuarantine: _topicPinSkewQuarantine ?? undefined, topicPinFoldView: _topicPinFoldView ?? undefined, ownershipReconciler: _ownershipReconciler ?? undefined, staleOwnerEngine: _staleOwnerEngine ?? undefined, duplicateReconciler: _duplicateReconciler ?? undefined, ownerDarkLadder: _ownerDarkLadder ?? undefined, spawnAdmission: _spawnAdmission ?? undefined, judgmentProvenance: _judgmentProvenance ?? undefined, leaseHandback: _leaseHandbackCtx ?? undefined, streamTicketStore: _streamTicketStore ?? undefined, poolStreamAllowRemoteInput: config.dashboard?.poolStream?.allowRemoteInput ?? false, poolStreamConnector: _poolStreamConnector ?? undefined, secretSync: _secretSyncHandle ?? undefined, meshSelfId: _meshSelfId ?? undefined, resolveRouterUrl: _resolveRouterUrl ?? undefined, resolvePeerUrls: _resolvePeerUrls ?? undefined, recordRopeResult: _recordRopeResult ?? undefined, guardRegistry, listPoolMachines: _listPoolMachines ?? undefined, deliverMandateToMachine: _deliverMandateToMachine ?? undefined, passkeyRevokeOutbox: _passkeyRevokeOutbox ? () => _passkeyRevokeOutbox : undefined, passkeyPeerOnline: _passkeyPeerOnline, passkeyPoolReader: _passkeyPoolReader ? () => _passkeyPoolReader : undefined, passkeyProofBrowser: async (profileDir) => { const { ChromeCdpReloginBrowser } = await import('../core/ChromeCdpReloginBrowser.js'); return new ChromeCdpReloginBrowser({ userDataDir: profileDir, passkeyMode: true, headless: true, launchTimeoutMs: 30_000, operationTimeoutMs: 20_000 }); }, poolLink: _poolLink ?? undefined, poolPollCache: _poolPollCache ?? undefined, sessionPoolE2EResultStore, proxyCoordinator, topicIntentStore, topicIntentArcCheck, usherSignalStore, intelligence: sharedIntelligence ?? undefined, telegramBridgeConfig, telegramBridge: telegramBridge ?? undefined, threadlineObservability, briefDeps, workingMemory, taskFlowRegistry, threadlineFlowBridge, sessionReaper, agentWorktreeReaper, externalHogSentinel, orphanedWorkSentinel, mcpProcessReaper, codexArmSessionSweeper, geminiLoopRunner, sleepController, agentActivityState, reapLog, resumeQueue, resumeDrainer, autonomousLivenessReconciler, enforcedTerminationStatus: () => enforcedTerminationWatchdog?.guardStatus() ?? null, prHandLease: prHandLease ?? undefined, standDownRegistry: _standDownRegistry ?? undefined, standDownAudit: _standDownAudit ?? undefined, operatorStopRecorder: recordOperatorStop, sleepWakeDetector, unjustifiedStopGate, stopGateDb, stopNotifier, liveTestGate, liveTestGateMode, liveTestRunnerCtx });
30510
+ return []; const { fetchPeerSubscriptionViews } = await import('../core/fetchPeerSubscriptionViews.js'); return fetchPeerSubscriptionViews({ peers: () => peers, fetchImpl: fetch, authToken: config.authToken ?? '' }); }, quotaPoller, quotaAwareScheduler: _quotaAwareScheduler ?? undefined, proactiveSwapMonitor: _proactiveSwapMonitor ?? undefined, usageWallRescue: _usageWallRescue ?? undefined, inUseAccountResolver, enrollmentWizard, accountFollowMeRevocation, credentialRepointing, semanticMemory, activitySentinel, rateLimitSentinel, releaseReadinessSentinel: releaseReadinessSentinel ?? undefined, greenPrAutoMerger: greenPrAutoMerger ?? undefined, localVerify: localVerifyService ?? undefined, localVerifyStatus: localVerifyStatus ?? undefined, guardLatchStore: guardLatchStore ?? undefined, agentStopSurfacer: agentStopSurfacer ?? undefined, messageRouter, summarySentinel, spawnManager, systemReviewer, capabilityMapper, selfKnowledgeTree, coverageAuditor, topicResumeMap: _topicResumeMap ?? undefined, topicProfile: _topicProfileCtx ?? undefined, sessionRefresh: _sessionRefresh ?? undefined, autonomyManager, trustElevationTracker, autonomousEvolution, coordinator: coordinator.enabled ? coordinator : undefined, meshBindActive: coordinator.managers.identityManager.hasIdentity() && config.multiMachine?.meshTransport?.enabled !== false, localSigningKeyPem, leaseTransport, peerEndpointRecorder, getSelfMeshEndpoints, onLeasePullRequest: () => leaseCoordinatorRef?.currentLease() ?? null, liveTailReceiver, handoffWireTransport, onHandoffBegin, onHandoffInitiate: handoffInitiate, handoffInProgress: handoffSentinelInProgress, messageLedger, currentInboundByTopic, replyMarkerTransport, onReplyMarker: messageLedger ? (marker) => { const m = marker; messageLedger.applyRemoteReplyMarker(m.dedupeKey, { platform: m.platform, replyIdempotencyKey: m.replyIdempotencyKey, epoch: m.epoch, topic: m.topic ?? null }); } : undefined, whatsapp: whatsappAdapter, slack: slackAdapter, imessage: imessageAdapter, conversationRegistry, conversationBindAuth, appConversations: _appConversationService ?? undefined, slackInboundDispatch: () => _slackInboundDispatch, conversationFollowThrough, whatsappBusinessBackend, messageBridge, hookEventReceiver, worktreeMonitor, subagentTracker, instructionsVerifier, handshakeManager: threadlineHandshake, threadlineRouter, conversationStore, threadLog, threadMessageRecorder, warrantsReplyGate, collaborationSurfacer, peerHomebaseRelay, threadResumeMap, topicLinkageHandler: topicLinkageHandler ?? undefined, threadlineRelayClient, pairingInvites: threadlinePairingInvites, getLastRelayEvent: threadlineGetLastRelayEvent, a2aRelayForward: _a2aRelayForwardCtx ?? undefined, threadlineReplyWaiters, threadlineMcpHttp, memoryPrivacy: _memoryPrivacyWired ?? undefined, listenerManager: listenerManager ?? undefined, a2aDeliveryTracker: a2aDeliveryTracker ?? undefined, relayVerdictCounters: threadlineRelayClient ? (() => ({ ...threadlineRelayClient.relayVerdictCounters, expiredMismatchIgnored: a2aDeliveryTracker?.expiredMismatchIgnored ?? 0 })) : undefined, inboundIdLedger: _inboundIdLedger ?? undefined, inboundIdLedgerDaemonDeferred: () => _inboundIdLedgerDaemonDeferred, responseReviewGate, reviewCanaryBattery, messagingToneGate, outboundDedupGate, telemetryHeartbeat, pasteManager, featureRegistry, discoveryEvaluator, completionEvaluator, unifiedTrust, liveConfig, sharedStateLedger, ledgerSessionRegistry, worktreeManager, oidcEnrolledRepos: parallelDevConfig?.oidcEnrolledRepos, initiativeTracker, projectRoundRunner, projectDriftChecker, machineHeartbeat, machinePoolRegistry, ropeHealthMonitor, writeAdmission: writeAdmission ?? undefined, getInboundQueue: () => _inboundQueue, getMachineCoherence: () => _machineCoherenceSentinel, getSingleMachineFailoverGap: () => _singleMachineFailoverGap, getMissingLoginSession: () => _missingLoginSession, getSessionPoolFailoverRunner: () => _sessionPoolFailoverRunnerDriver?.status() ?? null, sessionPoolPromotionActivation: _sessionPoolPromotionActivation, meshRpcDispatcher, deliverA2aToMachine: _deliverA2aToMachine ?? undefined, workingSetPullCoordinator, workingSetArtifactManager, orchestratorPoller, commitmentReplicaStore, preferenceReplicaStore, replicatedRecordEmitter, replicatedStoreReconciler, conflictStore, rollbackUnmerge, droppedOriginRegistry, preferencesUnionReader, jevMemoryPicker, forwardCommitmentMutate, sessionOwnershipRegistry, sendDrain: _sendDrain ?? undefined, claimTopicOwnershipOnSpawn: (topicId) => _claimOwnershipForAutonomousSpawn ? _claimOwnershipForAutonomousSpawn(topicId) : 'unwired', topicPinStore: _topicPinStore ?? undefined, topicPinSkewQuarantine: _topicPinSkewQuarantine ?? undefined, topicPinFoldView: _topicPinFoldView ?? undefined, ownershipReconciler: _ownershipReconciler ?? undefined, staleOwnerEngine: _staleOwnerEngine ?? undefined, duplicateReconciler: _duplicateReconciler ?? undefined, ownerDarkLadder: _ownerDarkLadder ?? undefined, spawnAdmission: _spawnAdmission ?? undefined, judgmentProvenance: _judgmentProvenance ?? undefined, leaseHandback: _leaseHandbackCtx ?? undefined, streamTicketStore: _streamTicketStore ?? undefined, poolStreamAllowRemoteInput: config.dashboard?.poolStream?.allowRemoteInput ?? false, poolStreamConnector: _poolStreamConnector ?? undefined, secretSync: _secretSyncHandle ?? undefined, meshSelfId: _meshSelfId ?? undefined, resolveRouterUrl: _resolveRouterUrl ?? undefined, resolvePeerUrls: _resolvePeerUrls ?? undefined, recordRopeResult: _recordRopeResult ?? undefined, guardRegistry, listPoolMachines: _listPoolMachines ?? undefined, deliverMandateToMachine: _deliverMandateToMachine ?? undefined, passkeyRevokeOutbox: _passkeyRevokeOutbox ? () => _passkeyRevokeOutbox : undefined, passkeyPeerOnline: _passkeyPeerOnline, passkeyPoolReader: _passkeyPoolReader ? () => _passkeyPoolReader : undefined, passkeyProofBrowser: async (profileDir) => { const { ChromeCdpReloginBrowser } = await import('../core/ChromeCdpReloginBrowser.js'); return new ChromeCdpReloginBrowser({ userDataDir: profileDir, passkeyMode: true, headless: true, launchTimeoutMs: 30_000, operationTimeoutMs: 20_000 }); }, poolLink: _poolLink ?? undefined, poolPollCache: _poolPollCache ?? undefined, sessionPoolE2EResultStore, proxyCoordinator, topicIntentStore, topicIntentArcCheck, usherSignalStore, intelligence: sharedIntelligence ?? undefined, telegramBridgeConfig, telegramBridge: telegramBridge ?? undefined, threadlineObservability, briefDeps, workingMemory, taskFlowRegistry, threadlineFlowBridge, sessionReaper, agentWorktreeReaper, externalHogSentinel, orphanedWorkSentinel, mcpProcessReaper, codexArmSessionSweeper, geminiLoopRunner, sleepController, agentActivityState, reapLog, resumeQueue, resumeDrainer, autonomousLivenessReconciler, enforcedTerminationStatus: () => enforcedTerminationWatchdog?.guardStatus() ?? null, prHandLease: prHandLease ?? undefined, standDownRegistry: _standDownRegistry ?? undefined, standDownAudit: _standDownAudit ?? undefined, operatorStopRecorder: recordOperatorStop, sleepWakeDetector, unjustifiedStopGate, stopGateDb, stopNotifier, liveTestGate, liveTestGateMode, liveTestRunnerCtx });
30417
30511
  if (_pendingCompactionEvidence)
30418
30512
  featureEvidence.record('multiMachine.coherenceJournal.replication.compaction', _pendingCompactionEvidence); // Resolve the late-bound topic-operator getter (increment 2e): routing was
30419
30513
  const readIdentityProjectionPeerRows = async () => {
@@ -31622,6 +31716,16 @@ export async function startServer(options) {
31622
31716
  advertiseSelfMeshUrl(coordinator.managers.identityManager, coordinator.identity.machineId, cfUrl, (m) => console.log(pc.dim(m)));
31623
31717
  await advertiseSelfMeshEndpointsNow(coordinator.managers.identityManager, coordinator.identity.machineId, config, cfUrl);
31624
31718
  }
31719
+ // Every agent gets a dashboard PIN, with or without Telegram: the PIN
31720
+ // gates every dashboard operator route, and an agent must work without
31721
+ // Telegram. An existing PIN (or an unresolved vault reference) is kept.
31722
+ // Runs before the Telegram dashboard broadcast below, which reads config.dashboardPin.
31723
+ const pinStore = { at: 'config' };
31724
+ if (ensureDashboardPin(config, pin => {
31725
+ pinStore.at = persistDashboardPin(config.stateDir, pin, value => liveConfig.set('dashboardPin', value));
31726
+ }).generated) {
31727
+ console.log(pc.green(` Auto-generated a dashboard PIN (stored in this agent's ${pinStore.at === 'vault' ? 'secret store' : 'config'})`));
31728
+ }
31625
31729
  // ── Dashboard Topic: always-available link ──────────────────────────
31626
31730
  // Retroactive: creates the topic on first run for existing agents.
31627
31731
  // Posts tunnel URL + PIN and pins the message for instant access.
@@ -31630,16 +31734,6 @@ export async function startServer(options) {
31630
31734
  const dashTopicId = await telegram.ensureDashboardTopic();
31631
31735
  if (dashTopicId) {
31632
31736
  console.log(pc.green(` Dashboard topic: ${dashTopicId}`));
31633
- // Auto-generate dashboardPin if missing — do this on every startup,
31634
- // not just during upgrades. The PIN should always exist.
31635
- if (!config.dashboardPin) {
31636
- const { randomInt } = await import('node:crypto');
31637
- const pin = String(randomInt(100000, 1000000)); // 6-digit
31638
- config.dashboardPin = pin;
31639
- // Persist via LiveConfig so it survives restart
31640
- liveConfig.set('dashboardPin', pin);
31641
- console.log(pc.green(` Auto-generated dashboard PIN: ${pin}`));
31642
- }
31643
31737
  // Wire the tunnel's two-channel notifier to telegram now that
31644
31738
  // both telegram + Dashboard topic exist. Per
31645
31739
  // specs/dev-infrastructure/tunnel-failure-resilience.md Part 3:
@@ -31649,8 +31743,9 @@ export async function startServer(options) {
31649
31743
  // at compose time so the notifier never holds stale credentials.
31650
31744
  if (tunnel) {
31651
31745
  tunnel.attachTelegram(telegram, () => {
31652
- const live = liveConfig.get('dashboardPin', '');
31653
- return live || config.dashboardPin;
31746
+ // A vault-backed config holds {secret:true}; resolve it to the real PIN.
31747
+ return pickDashboardPin(liveConfig.get('dashboardPin', ''), config.stateDir)
31748
+ ?? pickDashboardPin(config.dashboardPin, config.stateDir) ?? '';
31654
31749
  });
31655
31750
  }
31656
31751
  // Only broadcast if we have a tunnel URL — posting localhost to Telegram