@sideboard-ai/core 0.1.288 → 0.1.289

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 (34) hide show
  1. package/dist/{agents-GZLTMGSH.js → agents-4BBCKITL.js} +4 -4
  2. package/dist/{agents-UQMHFZSM.js → agents-XAKSP653.js} +4 -4
  3. package/dist/{chunk-HNB2HTCV.js → chunk-2IXKTAIT.js} +1 -1
  4. package/dist/{chunk-3W5SL5EW.js → chunk-5HPMKLJ7.js} +1 -1
  5. package/dist/{chunk-WW5XGEMY.js → chunk-C546EOEF.js} +12 -12
  6. package/dist/{chunk-442O6OXM.js → chunk-EBGJXOXT.js} +11 -11
  7. package/dist/{chunk-4NM72HJZ.js → chunk-G74TSBJ3.js} +4 -4
  8. package/dist/{chunk-WQSIUZEB.js → chunk-GBAACE3C.js} +4 -4
  9. package/dist/{chunk-2N77CLRQ.js → chunk-LUQ7MIVD.js} +3 -3
  10. package/dist/{chunk-IA4MPG7F.js → chunk-O4324MXE.js} +2 -2
  11. package/dist/{chunk-DBOFDEQ7.js → chunk-V3BSO2O6.js} +2 -2
  12. package/dist/{chunk-6NVQNTJO.js → chunk-VNCPRMIK.js} +3 -3
  13. package/dist/{conductor-MCU63Z5U.js → conductor-5MNGC25D.js} +4 -4
  14. package/dist/{conductor-ZLYZVIAV.js → conductor-UFJXJPTF.js} +4 -4
  15. package/dist/{coordinator-prompt-KHCXXDRO.js → coordinator-prompt-5Q66Y5KW.js} +3 -3
  16. package/dist/{coordinator-prompt-UV6THLMD.js → coordinator-prompt-BYPEW6DE.js} +3 -3
  17. package/dist/{global-workspace-GDHZSFP6.js → global-workspace-BTH2YGDZ.js} +3 -3
  18. package/dist/{global-workspace-AXTSYVAE.js → global-workspace-YKOYXSB2.js} +3 -3
  19. package/dist/index.cjs +149 -49
  20. package/dist/index.d.cts +18 -2
  21. package/dist/index.d.ts +18 -2
  22. package/dist/index.js +132 -32
  23. package/dist/mcp/run-stdio.js +6 -6
  24. package/dist/{orchestrator-SDH6W6ZL.js → orchestrator-IG4VU7MT.js} +6 -6
  25. package/dist/{orchestrator-5ZBHR6BJ.js → orchestrator-MKF3YBNK.js} +6 -6
  26. package/dist/{thread-store-7XBOUQ5K.js → thread-store-DPKESDFR.js} +1 -1
  27. package/dist/{thread-store-ZQZDREVH.js → thread-store-X6S2GIR5.js} +1 -1
  28. package/dist/{workspaces-Q2R2XGO3.js → workspaces-ELO2YVTU.js} +3 -3
  29. package/dist/{workspaces-L2UR4KC2.js → workspaces-RFD6I4FM.js} +3 -3
  30. package/dist/{worktree-2GOK4VYT.js → worktree-ADVQ6X6C.js} +2 -2
  31. package/dist/{worktree-YEUTJ7MZ.js → worktree-TN7DZDJZ.js} +2 -2
  32. package/package.json +1 -1
  33. /package/dist/{chunk-N5UGX3JL.js → chunk-QB5MDOHL.js} +0 -0
  34. /package/dist/{chunk-JPH7PYSK.js → chunk-YRXV3E7J.js} +0 -0
package/dist/index.cjs CHANGED
@@ -1160,9 +1160,9 @@ function sumOptional(a, b) {
1160
1160
  function fromInclusiveInputUsage(opts) {
1161
1161
  const totalInput = Number(opts.inputTokens) || 0;
1162
1162
  const outputTokens = Number(opts.outputTokens) || 0;
1163
- const cached = Number(opts.cachedInputTokens) || 0;
1163
+ const cached2 = Number(opts.cachedInputTokens) || 0;
1164
1164
  if (!totalInput && !outputTokens) return null;
1165
- const cacheReadTokens = cached > 0 ? Math.min(cached, totalInput) : 0;
1165
+ const cacheReadTokens = cached2 > 0 ? Math.min(cached2, totalInput) : 0;
1166
1166
  return {
1167
1167
  inputTokens: Math.max(0, totalInput - cacheReadTokens),
1168
1168
  outputTokens,
@@ -4702,8 +4702,8 @@ function readThread(id) {
4702
4702
  return null;
4703
4703
  }
4704
4704
  const cache2 = cacheForDir();
4705
- const cached = cache2.byId.get(id);
4706
- if (cached && cache2.mtimeMs.get(id) === mtimeMs) return cached;
4705
+ const cached2 = cache2.byId.get(id);
4706
+ if (cached2 && cache2.mtimeMs.get(id) === mtimeMs) return cached2;
4707
4707
  const raw = (0, import_node_fs12.readFileSync)(path2, "utf8");
4708
4708
  const thread = normalizeThread(JSON.parse(raw));
4709
4709
  rememberThread(thread, mtimeMs);
@@ -6922,15 +6922,15 @@ function loadFromDisk(threadId) {
6922
6922
  }
6923
6923
  }
6924
6924
  function memFor(threadId) {
6925
- const cached = memory.get(threadId);
6926
- if (cached) return cached;
6925
+ const cached2 = memory.get(threadId);
6926
+ if (cached2) return cached2;
6927
6927
  const mem = fromSnapshot(loadFromDisk(threadId) ?? emptySetupLog());
6928
6928
  memory.set(threadId, mem);
6929
6929
  return mem;
6930
6930
  }
6931
6931
  function readSetupLog(threadId) {
6932
- const cached = memory.get(threadId);
6933
- if (cached) return materialize(cached);
6932
+ const cached2 = memory.get(threadId);
6933
+ if (cached2) return materialize(cached2);
6934
6934
  const disk = loadFromDisk(threadId);
6935
6935
  if (disk) {
6936
6936
  memory.set(threadId, fromSnapshot(disk));
@@ -7076,15 +7076,15 @@ function loadFromDisk2(key) {
7076
7076
  }
7077
7077
  }
7078
7078
  function memFor2(key) {
7079
- const cached = memory2.get(key);
7080
- if (cached) return cached;
7079
+ const cached2 = memory2.get(key);
7080
+ if (cached2) return cached2;
7081
7081
  const mem = fromSnapshot2(loadFromDisk2(key) ?? emptyRunLog());
7082
7082
  memory2.set(key, mem);
7083
7083
  return mem;
7084
7084
  }
7085
7085
  function readRunLog(key) {
7086
- const cached = memory2.get(key);
7087
- if (cached) return materialize2(cached);
7086
+ const cached2 = memory2.get(key);
7087
+ if (cached2) return materialize2(cached2);
7088
7088
  const disk = loadFromDisk2(key);
7089
7089
  if (disk) {
7090
7090
  memory2.set(key, fromSnapshot2(disk));
@@ -15128,8 +15128,8 @@ var init_claude = __esm({
15128
15128
  return { type: "stdout", data: line };
15129
15129
  }
15130
15130
  },
15131
- async resolveSessionId(_worktreePath, cached) {
15132
- return cached;
15131
+ async resolveSessionId(_worktreePath, cached2) {
15132
+ return cached2;
15133
15133
  },
15134
15134
  async buildAttach(thread) {
15135
15135
  const claude = resolveClaudeExecutable();
@@ -15513,8 +15513,8 @@ var init_codex = __esm({
15513
15513
  return { type: "stdout", data: line };
15514
15514
  }
15515
15515
  },
15516
- async resolveSessionId(_worktreePath, cached) {
15517
- return cached;
15516
+ async resolveSessionId(_worktreePath, cached2) {
15517
+ return cached2;
15518
15518
  },
15519
15519
  async buildAttach(thread) {
15520
15520
  const codex = resolveAgentExecutable("codex");
@@ -16150,8 +16150,8 @@ var init_cursor = __esm({
16150
16150
  parseEvent(line) {
16151
16151
  return parseCursorRunnerLine(line);
16152
16152
  },
16153
- async resolveSessionId(_worktreePath, cached) {
16154
- return cached;
16153
+ async resolveSessionId(_worktreePath, cached2) {
16154
+ return cached2;
16155
16155
  },
16156
16156
  async buildAttach(thread) {
16157
16157
  const which = await run("which", ["cursor"], { reject: false });
@@ -16633,8 +16633,8 @@ var init_opencode = __esm({
16633
16633
  return { type: "stdout", data: line };
16634
16634
  }
16635
16635
  },
16636
- async resolveSessionId(worktreePath, cached) {
16637
- const cachedId = cached?.trim() || null;
16636
+ async resolveSessionId(worktreePath, cached2) {
16637
+ const cachedId = cached2?.trim() || null;
16638
16638
  const listed = await run(
16639
16639
  resolveAgentExecutable("opencode"),
16640
16640
  ["session", "list", "--format", "json"],
@@ -16961,7 +16961,7 @@ function numberOrUndef(value) {
16961
16961
  if (typeof value !== "number" || !Number.isFinite(value)) return void 0;
16962
16962
  return value;
16963
16963
  }
16964
- function parseClaudeUsagePayload(raw, fetchedAt = (/* @__PURE__ */ new Date()).toISOString()) {
16964
+ function parseClaudeUsagePayload(raw, fetchedAt2 = (/* @__PURE__ */ new Date()).toISOString()) {
16965
16965
  if (!raw || typeof raw !== "object") return null;
16966
16966
  const obj = raw;
16967
16967
  const source = obj.rate_limits && typeof obj.rate_limits === "object" ? obj.rate_limits : obj;
@@ -16975,7 +16975,7 @@ function parseClaudeUsagePayload(raw, fetchedAt = (/* @__PURE__ */ new Date()).t
16975
16975
  return {
16976
16976
  windows,
16977
16977
  ...extra ? { extraUsage: extra } : {},
16978
- fetchedAt
16978
+ fetchedAt: fetchedAt2
16979
16979
  };
16980
16980
  }
16981
16981
  function hottestClaudeWindow(usage) {
@@ -17018,15 +17018,15 @@ function formatClaudeUsageResetClock(resetsAt) {
17018
17018
  return at.toISOString();
17019
17019
  }
17020
17020
  }
17021
- function formatClaudeUsageFetchedAt(fetchedAt, now = /* @__PURE__ */ new Date()) {
17022
- const at = new Date(fetchedAt);
17021
+ function formatClaudeUsageFetchedAt(fetchedAt2, now = /* @__PURE__ */ new Date()) {
17022
+ const at = new Date(fetchedAt2);
17023
17023
  if (Number.isNaN(at.getTime())) return "";
17024
17024
  const minutes = Math.max(0, Math.round((now.getTime() - at.getTime()) / 6e4));
17025
17025
  if (minutes < 1) return "Updated just now";
17026
17026
  if (minutes < 60) return `Updated ${minutes}m ago`;
17027
17027
  const hours = Math.round(minutes / 60);
17028
17028
  if (hours < 36) return `Updated ${hours}h ago`;
17029
- return `Updated ${formatClaudeUsageResetClock(fetchedAt)}`;
17029
+ return `Updated ${formatClaudeUsageResetClock(fetchedAt2)}`;
17030
17030
  }
17031
17031
  function formatClaudeExtraUsageDetail(extra) {
17032
17032
  if (!extra.enabled) return "Extra usage off";
@@ -36106,15 +36106,15 @@ async function getHomeBoardInputs(workspaces, opts) {
36106
36106
  }
36107
36107
  const promise = (async () => {
36108
36108
  const inputs = await loadHomeBoardInputs(workspaces);
36109
- const fetchedAt = opts?.now ?? Date.now();
36109
+ const fetchedAt2 = opts?.now ?? Date.now();
36110
36110
  const pins = syncBoardPins(listBoardPins(), inputs.issues, inputs.prs);
36111
36111
  replaceBoardPins(pins);
36112
- const loaded = { ...inputs, fetchedAt, fromCache: false, pins };
36112
+ const loaded = { ...inputs, fetchedAt: fetchedAt2, fromCache: false, pins };
36113
36113
  if (shouldCacheHomeBoardInputs(inputs)) {
36114
36114
  const entry = {
36115
36115
  version: CACHE_VERSION,
36116
36116
  workspaceKey: key,
36117
- fetchedAt,
36117
+ fetchedAt: fetchedAt2,
36118
36118
  inputs
36119
36119
  };
36120
36120
  memory3 = entry;
@@ -39883,14 +39883,46 @@ var RemoteHub = class {
39883
39883
  this.codes.delete(code);
39884
39884
  this.attachPhone(socket, host);
39885
39885
  }
39886
+ /** In-memory session, with the live Mac's label when that socket is up. */
39887
+ memorySession(sessionToken) {
39888
+ const cached2 = this.phonesByToken.get(sessionToken);
39889
+ if (!cached2) return null;
39890
+ const live = this.hosts.get(cached2.deviceId);
39891
+ const remembered = this.remembered.get(cached2.deviceId);
39892
+ const current = live?.sessionToken ?? remembered?.sessionToken ?? null;
39893
+ if (current && current !== sessionToken) {
39894
+ this.phonesByToken.delete(sessionToken);
39895
+ return null;
39896
+ }
39897
+ return {
39898
+ deviceId: cached2.deviceId,
39899
+ deviceLabel: live?.deviceLabel || remembered?.deviceLabel || cached2.deviceLabel
39900
+ };
39901
+ }
39902
+ knownSession(sessionToken) {
39903
+ const cached2 = this.memorySession(sessionToken);
39904
+ if (cached2) return cached2;
39905
+ const stored = this.opts.phoneSessions?.find(sessionToken) ?? null;
39906
+ if (!stored) return null;
39907
+ const live = this.hosts.get(stored.deviceId);
39908
+ const remembered = this.remembered.get(stored.deviceId);
39909
+ const current = live?.sessionToken ?? remembered?.sessionToken ?? null;
39910
+ if (current && current !== sessionToken) return null;
39911
+ this.phonesByToken.set(sessionToken, stored);
39912
+ if (remembered && !remembered.sessionToken) remembered.sessionToken = sessionToken;
39913
+ if (live && !live.sessionToken) live.sessionToken = sessionToken;
39914
+ return {
39915
+ deviceId: stored.deviceId,
39916
+ deviceLabel: live?.deviceLabel || remembered?.deviceLabel || stored.deviceLabel
39917
+ };
39918
+ }
39886
39919
  resumePhone(socket, sessionToken) {
39887
- const deviceId = this.phonesByToken.get(sessionToken);
39888
- const remembered = deviceId ? this.remembered.get(deviceId) : void 0;
39889
- if (!deviceId || !remembered || remembered.sessionToken !== sessionToken) {
39920
+ const known = this.knownSession(sessionToken);
39921
+ if (!known) {
39890
39922
  send2(socket, { type: "error", message: "phone session expired \u2014 pair again" });
39891
39923
  return;
39892
39924
  }
39893
- const host = this.hosts.get(deviceId);
39925
+ const host = this.hosts.get(known.deviceId);
39894
39926
  if (!host) {
39895
39927
  send2(socket, { type: "host_offline" });
39896
39928
  return;
@@ -39899,17 +39931,13 @@ var RemoteHub = class {
39899
39931
  }
39900
39932
  listHosts(socket, sessionTokens) {
39901
39933
  const hosts = sessionTokens.map((sessionToken) => {
39902
- const deviceId = this.phonesByToken.get(sessionToken);
39903
- const remembered = deviceId ? this.remembered.get(deviceId) : void 0;
39904
- const valid = Boolean(deviceId && remembered && remembered.sessionToken === sessionToken);
39905
- if (!valid || !deviceId || !remembered) {
39906
- return { sessionToken, deviceId: "", deviceLabel: "", online: false };
39907
- }
39908
- const host = this.hosts.get(deviceId);
39934
+ const known = this.knownSession(sessionToken);
39935
+ if (!known) return { sessionToken, deviceId: "", deviceLabel: "", online: false };
39936
+ const host = this.hosts.get(known.deviceId);
39909
39937
  return {
39910
39938
  sessionToken,
39911
- deviceId,
39912
- deviceLabel: host?.deviceLabel || remembered.deviceLabel,
39939
+ deviceId: known.deviceId,
39940
+ deviceLabel: host?.deviceLabel || known.deviceLabel,
39913
39941
  online: Boolean(host)
39914
39942
  };
39915
39943
  });
@@ -39940,18 +39968,20 @@ var RemoteHub = class {
39940
39968
  }
39941
39969
  host.phone = null;
39942
39970
  }
39971
+ const created = sessionToken == null;
39943
39972
  const token = sessionToken ?? (0, import_node_crypto25.randomBytes)(24).toString("hex");
39944
39973
  if (host.sessionToken && host.sessionToken !== token) {
39945
39974
  this.phonesByToken.delete(host.sessionToken);
39946
39975
  }
39947
39976
  host.sessionToken = token;
39948
39977
  host.phone = socket;
39949
- this.phonesByToken.set(token, host.deviceId);
39978
+ this.phonesByToken.set(token, { deviceId: host.deviceId, deviceLabel: host.deviceLabel });
39950
39979
  this.remembered.set(host.deviceId, {
39951
39980
  hostSecret: host.hostSecret,
39952
39981
  sessionToken: token,
39953
39982
  deviceLabel: host.deviceLabel
39954
39983
  });
39984
+ if (created) this.opts.phoneSessions?.save(host.deviceId, token);
39955
39985
  this.bySocket.set(socket, { role: "phone", deviceId: host.deviceId });
39956
39986
  this.opts.onAuthorized?.(socket);
39957
39987
  send2(socket, {
@@ -40215,6 +40245,29 @@ function phoneAccountDefaults() {
40215
40245
  };
40216
40246
  }
40217
40247
 
40248
+ // src/remote/phone-viewer-login.ts
40249
+ var TTL_MS = 5 * 6e4;
40250
+ var cached = "";
40251
+ var fetchedAt = 0;
40252
+ var inflight3 = null;
40253
+ function phoneViewerLogin() {
40254
+ return cached;
40255
+ }
40256
+ function refreshPhoneViewerLogin(force = false) {
40257
+ const now = Date.now();
40258
+ if (!force && fetchedAt && now - fetchedAt < TTL_MS) return Promise.resolve(cached);
40259
+ if (inflight3) return inflight3;
40260
+ const run2 = getGitHubStatus().then((status) => {
40261
+ cached = status.login?.trim() ?? "";
40262
+ fetchedAt = Date.now();
40263
+ return cached;
40264
+ }).catch(() => cached).finally(() => {
40265
+ if (inflight3 === run2) inflight3 = null;
40266
+ });
40267
+ inflight3 = run2;
40268
+ return run2;
40269
+ }
40270
+
40218
40271
  // src/remote/phone-transcript.ts
40219
40272
  init_turn_live();
40220
40273
 
@@ -40519,7 +40572,8 @@ function isProjectPath(path2) {
40519
40572
  function listPhoneChats() {
40520
40573
  return listGlobalThreads().slice().sort((a, b) => a.updatedAt < b.updatedAt ? 1 : a.updatedAt > b.updatedAt ? -1 : 0).map(summarize);
40521
40574
  }
40522
- function listPhoneSidebar() {
40575
+ function listPhoneSidebar(viewerLogin = phoneViewerLogin()) {
40576
+ const settings = loadAppSettings();
40523
40577
  const threads = listThreads();
40524
40578
  const byRepo = /* @__PURE__ */ new Map();
40525
40579
  for (const thread of threads) {
@@ -40537,10 +40591,16 @@ function listPhoneSidebar() {
40537
40591
  path: path2,
40538
40592
  worktrees: groupHomeBoardWorktrees(repoThreads).map((group) => {
40539
40593
  const tags = workspaceTagsFromGroup(group);
40594
+ const badge = classifyWorktreeListBadges(
40595
+ group,
40596
+ viewerLogin,
40597
+ resolveProjectReviewLabel(settings, path2)
40598
+ )[0];
40540
40599
  return {
40541
40600
  label: worktreeDisplayLabelForGroup(group),
40542
40601
  chats: group.map(summarize),
40543
- ...tags.length > 0 ? { tags } : {}
40602
+ ...tags.length > 0 ? { tags } : {},
40603
+ ...badge ? { badge: { id: badge.id, label: badge.label, title: badge.title } } : {}
40544
40604
  };
40545
40605
  })
40546
40606
  }));
@@ -41453,7 +41513,9 @@ function chatKey(row2) {
41453
41513
  function phoneBoardKey(sidebar) {
41454
41514
  const orchestration = sidebar.orchestration.map(chatKey).join("\n");
41455
41515
  const projects = sidebar.projects.map((project) => {
41456
- const trees = project.worktrees.map((tree) => `${tree.label} ${(tree.tags ?? []).join(",")} ${tree.chats.map(chatKey).join("\n")}`).join("\n");
41516
+ const trees = project.worktrees.map(
41517
+ (tree) => `${tree.label} ${tree.badge?.id ?? ""} ${(tree.tags ?? []).join(",")} ${tree.chats.map(chatKey).join("\n")}`
41518
+ ).join("\n");
41457
41519
  return `${project.path} ${project.name}
41458
41520
  ${trees}`;
41459
41521
  }).join("\n---\n");
@@ -41531,10 +41593,23 @@ function startPhoneBoardSync(opts) {
41531
41593
  const unsubscribe = (opts.subscribe ?? subscribePhoneBoard)(schedule);
41532
41594
  const offEvents = (opts.events ?? ((listener) => getOrchestrator().on(listener)))(handle);
41533
41595
  push();
41596
+ let loginTimer = null;
41597
+ const refreshViewerLogin = opts.refreshViewerLogin;
41598
+ if (refreshViewerLogin) {
41599
+ const afterLogin = () => {
41600
+ void refreshViewerLogin().then(() => {
41601
+ if (!stopped) push();
41602
+ });
41603
+ };
41604
+ afterLogin();
41605
+ loginTimer = setInterval(afterLogin, opts.viewerLoginIntervalMs ?? 5 * 6e4);
41606
+ }
41534
41607
  return () => {
41535
41608
  stopped = true;
41536
41609
  if (timer) clearTimeout(timer);
41537
41610
  timer = null;
41611
+ if (loginTimer) clearInterval(loginTimer);
41612
+ loginTimer = null;
41538
41613
  unsubscribe();
41539
41614
  offEvents();
41540
41615
  };
@@ -41601,7 +41676,8 @@ function runRemoteHost(opts) {
41601
41676
  },
41602
41677
  onRunningChat: (chatId) => {
41603
41678
  deliverPhoneTurn({ onOutbound: emit }, chatId);
41604
- }
41679
+ },
41680
+ refreshViewerLogin: () => refreshPhoneViewerLogin(true)
41605
41681
  });
41606
41682
  };
41607
41683
  const send3 = (msg) => {
@@ -41864,9 +41940,15 @@ var RelayAccountStore = class {
41864
41940
  account_id TEXT NOT NULL,
41865
41941
  host_secret_hash TEXT NOT NULL,
41866
41942
  device_label TEXT NOT NULL,
41867
- created_at INTEGER NOT NULL
41943
+ created_at INTEGER NOT NULL,
41944
+ session_token_hash TEXT
41868
41945
  );
41869
41946
  `);
41947
+ const cols = this.db.prepare("PRAGMA table_info(devices)").all();
41948
+ if (!cols.some((col) => col.name === "session_token_hash")) {
41949
+ this.db.exec("ALTER TABLE devices ADD COLUMN session_token_hash TEXT");
41950
+ }
41951
+ this.db.exec("CREATE INDEX IF NOT EXISTS devices_session ON devices(session_token_hash)");
41870
41952
  }
41871
41953
  close() {
41872
41954
  this.db.close();
@@ -41995,6 +42077,20 @@ var RelayAccountStore = class {
41995
42077
  throw err;
41996
42078
  }
41997
42079
  }
42080
+ /** Store a phone pairing when it is created. A later process looks it up on a memory miss. */
42081
+ savePhoneSession(deviceId, sessionToken) {
42082
+ const id = deviceId.trim();
42083
+ const token = sessionToken.trim();
42084
+ if (!id || !token) return;
42085
+ this.db.prepare("UPDATE devices SET session_token_hash = ? WHERE device_id = ?").run(sha256(token), id);
42086
+ }
42087
+ findPhoneSession(sessionToken) {
42088
+ const token = sessionToken.trim();
42089
+ if (!token) return null;
42090
+ const row2 = this.db.prepare("SELECT device_id, device_label FROM devices WHERE session_token_hash = ?").get(sha256(token));
42091
+ if (!row2) return null;
42092
+ return { deviceId: row2.device_id, deviceLabel: row2.device_label };
42093
+ }
41998
42094
  requireAccount(accountId) {
41999
42095
  const row2 = this.db.prepare("SELECT id FROM accounts WHERE id = ?").get(accountId);
42000
42096
  if (!row2) throw new Error("That Sideboard account is no longer on the relay.");
@@ -42985,6 +43081,10 @@ async function startRelayServer(opts) {
42985
43081
  const decision = accountStore.authorizeHost(input);
42986
43082
  return decision.ok ? { ok: true } : { ok: false, message: decision.message };
42987
43083
  } : void 0,
43084
+ phoneSessions: accountStore ? {
43085
+ find: (sessionToken) => accountStore.findPhoneSession(sessionToken),
43086
+ save: (deviceId, sessionToken) => accountStore.savePhoneSession(deviceId, sessionToken)
43087
+ } : void 0,
42988
43088
  onAuthorized: (socket) => authMarks.get(socket)?.()
42989
43089
  });
42990
43090
  const handleCallback = async (reqUrl, res) => {
@@ -43470,8 +43570,8 @@ function displayName(user) {
43470
43570
  return fromProfile || user.real_name?.trim() || user.name?.trim() || "";
43471
43571
  }
43472
43572
  async function resolveUserName(token, userId, fallback, fetchImpl) {
43473
- const cached = nameCache.get(userId);
43474
- if (cached) return cached;
43573
+ const cached2 = nameCache.get(userId);
43574
+ if (cached2) return cached2;
43475
43575
  try {
43476
43576
  const data = await slackApi(token, "users.info", { user: userId }, fetchImpl);
43477
43577
  const name = displayName(data.user ?? {}) || fallback;
package/dist/index.d.cts CHANGED
@@ -7891,12 +7891,24 @@ type RemoteHostAuthorizer = (input: {
7891
7891
  ok: false;
7892
7892
  message: string;
7893
7893
  };
7894
+ type RemotePhoneSession = {
7895
+ deviceId: string;
7896
+ deviceLabel: string;
7897
+ };
7898
+ /**
7899
+ * Phone pairings on disk. `save` runs when a pairing is created.
7900
+ * `find` runs only when that token is not already in memory.
7901
+ */
7902
+ type RemotePhoneSessions = {
7903
+ find(sessionToken: string): RemotePhoneSession | null;
7904
+ save(deviceId: string, sessionToken: string): void;
7905
+ };
7894
7906
  /**
7895
7907
  * In-memory phone ↔ Mac registry. One active phone socket per Mac.
7896
7908
  * A phone may hold session tokens for several Macs and attaches to one at a time.
7897
7909
  * Host secrets and session tokens survive that Mac reconnecting in this process.
7898
- * A relay restart drops the live sockets — the phone pairs again. When an
7899
- * authorizer is set, the account and host secret live in durable storage.
7910
+ * A new pairing is written to the store. A relay restart drops memory; the next
7911
+ * resume reads that store, then keeps the session in memory.
7900
7912
  */
7901
7913
  declare class RemoteHub {
7902
7914
  private readonly opts;
@@ -7907,6 +7919,7 @@ declare class RemoteHub {
7907
7919
  private readonly phonesByToken;
7908
7920
  constructor(opts?: {
7909
7921
  authorizeHost?: RemoteHostAuthorizer;
7922
+ phoneSessions?: RemotePhoneSessions;
7910
7923
  onAuthorized?: (socket: RemoteSocket) => void;
7911
7924
  });
7912
7925
  handleClientMessage(socket: RemoteSocket, raw: string): void;
@@ -7915,6 +7928,9 @@ declare class RemoteHub {
7915
7928
  private registerHost;
7916
7929
  private issueCode;
7917
7930
  private pairPhone;
7931
+ /** In-memory session, with the live Mac's label when that socket is up. */
7932
+ private memorySession;
7933
+ private knownSession;
7918
7934
  private resumePhone;
7919
7935
  private listHosts;
7920
7936
  private releasePhoneSocket;
package/dist/index.d.ts CHANGED
@@ -7891,12 +7891,24 @@ type RemoteHostAuthorizer = (input: {
7891
7891
  ok: false;
7892
7892
  message: string;
7893
7893
  };
7894
+ type RemotePhoneSession = {
7895
+ deviceId: string;
7896
+ deviceLabel: string;
7897
+ };
7898
+ /**
7899
+ * Phone pairings on disk. `save` runs when a pairing is created.
7900
+ * `find` runs only when that token is not already in memory.
7901
+ */
7902
+ type RemotePhoneSessions = {
7903
+ find(sessionToken: string): RemotePhoneSession | null;
7904
+ save(deviceId: string, sessionToken: string): void;
7905
+ };
7894
7906
  /**
7895
7907
  * In-memory phone ↔ Mac registry. One active phone socket per Mac.
7896
7908
  * A phone may hold session tokens for several Macs and attaches to one at a time.
7897
7909
  * Host secrets and session tokens survive that Mac reconnecting in this process.
7898
- * A relay restart drops the live sockets — the phone pairs again. When an
7899
- * authorizer is set, the account and host secret live in durable storage.
7910
+ * A new pairing is written to the store. A relay restart drops memory; the next
7911
+ * resume reads that store, then keeps the session in memory.
7900
7912
  */
7901
7913
  declare class RemoteHub {
7902
7914
  private readonly opts;
@@ -7907,6 +7919,7 @@ declare class RemoteHub {
7907
7919
  private readonly phonesByToken;
7908
7920
  constructor(opts?: {
7909
7921
  authorizeHost?: RemoteHostAuthorizer;
7922
+ phoneSessions?: RemotePhoneSessions;
7910
7923
  onAuthorized?: (socket: RemoteSocket) => void;
7911
7924
  });
7912
7925
  handleClientMessage(socket: RemoteSocket, raw: string): void;
@@ -7915,6 +7928,9 @@ declare class RemoteHub {
7915
7928
  private registerHost;
7916
7929
  private issueCode;
7917
7930
  private pairPhone;
7931
+ /** In-memory session, with the live Mac's label when that socket is up. */
7932
+ private memorySession;
7933
+ private knownSession;
7918
7934
  private resumePhone;
7919
7935
  private listHosts;
7920
7936
  private releasePhoneSocket;