@sideboard-ai/core 0.1.316 → 0.1.318

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 (48) hide show
  1. package/dist/{abletime-NZ2ABWAL.js → abletime-KWOBUL46.js} +4 -4
  2. package/dist/{abletime-oauth-CBFFBINA.js → abletime-oauth-IUIAGLFJ.js} +3 -3
  3. package/dist/{abletime-rest-NSOP72AI.js → abletime-rest-RWN4EPY3.js} +3 -3
  4. package/dist/{agents-Y3M3EKLY.js → agents-2AR7I6B6.js} +6 -6
  5. package/dist/{agents-5XJ7EHGU.js → agents-KABJUDPI.js} +4 -4
  6. package/dist/{app-settings-RQAFJZCD.js → app-settings-V6YPYY57.js} +1 -1
  7. package/dist/{chunk-4ISF5XUA.js → chunk-23ZUL2EX.js} +25 -25
  8. package/dist/{chunk-VVV7EI6T.js → chunk-2HOZMSKQ.js} +2 -2
  9. package/dist/{chunk-ELBK34TY.js → chunk-7ADEYEMH.js} +5 -5
  10. package/dist/{chunk-5CTG5W7S.js → chunk-A46CIARU.js} +1 -1
  11. package/dist/{chunk-PN32Z4JL.js → chunk-A6C67DY5.js} +2 -2
  12. package/dist/{chunk-NXNNYY3Q.js → chunk-AESO2KJS.js} +2 -2
  13. package/dist/{chunk-S3U4HR5F.js → chunk-AZ4GEEGL.js} +2 -2
  14. package/dist/{chunk-7BLIY2DD.js → chunk-BXTISNKW.js} +15 -15
  15. package/dist/{chunk-U7PNCYNX.js → chunk-E52SQENB.js} +1 -0
  16. package/dist/{chunk-OLYNM52B.js → chunk-FDXHNROD.js} +2 -2
  17. package/dist/{chunk-FVTR54LY.js → chunk-GH7UMULC.js} +4 -4
  18. package/dist/{chunk-U35AHE2P.js → chunk-HOLB6ZFZ.js} +33 -2
  19. package/dist/{chunk-NKVNHBDB.js → chunk-IAPO7ZFG.js} +4 -4
  20. package/dist/{chunk-DNWO6PQJ.js → chunk-NRSC4N5X.js} +5 -34
  21. package/dist/{chunk-ATSZXRFQ.js → chunk-ST2UQBJT.js} +6 -35
  22. package/dist/{chunk-BPBDGJ43.js → chunk-TUJFCJJU.js} +2 -2
  23. package/dist/{chunk-GXSV3X3L.js → chunk-W5QZWAUB.js} +3 -3
  24. package/dist/{chunk-CCUGIVYN.js → chunk-XOZNEAWF.js} +3 -3
  25. package/dist/{chunk-GFM63DTU.js → chunk-YALFZKSC.js} +3 -3
  26. package/dist/{chunk-CHUPIJP7.js → chunk-YEIHCSZS.js} +63 -2
  27. package/dist/{conductor-CFLBKVHJ.js → conductor-MG2RAQNT.js} +5 -5
  28. package/dist/{conductor-NZ4V5CJX.js → conductor-NIUBJLTS.js} +4 -4
  29. package/dist/{coordinator-prompt-HUZC4DZ2.js → coordinator-prompt-3CT3D5UM.js} +3 -3
  30. package/dist/{coordinator-prompt-SPOYUZG5.js → coordinator-prompt-X74F3JIC.js} +4 -4
  31. package/dist/{global-workspace-SK4JQBPN.js → global-workspace-BUWMRNW5.js} +3 -3
  32. package/dist/{global-workspace-3NNDDTFD.js → global-workspace-FTNJEML3.js} +4 -4
  33. package/dist/index.cjs +96 -42
  34. package/dist/index.d.cts +2 -0
  35. package/dist/index.d.ts +2 -0
  36. package/dist/index.js +50 -27
  37. package/dist/mcp/run-stdio.cjs +36 -34
  38. package/dist/mcp/run-stdio.js +6 -6
  39. package/dist/{optional-cli-LMSRZNH4.js → optional-cli-6GGESWGE.js} +4 -4
  40. package/dist/{orchestrator-KXEPWTG5.js → orchestrator-2SXK4U65.js} +9 -9
  41. package/dist/{orchestrator-7342RGRU.js → orchestrator-N54V3B76.js} +6 -6
  42. package/dist/{thread-store-5OF6M6VZ.js → thread-store-7BBA63KV.js} +1 -1
  43. package/dist/{thread-store-GOVVXQV2.js → thread-store-PLNIWFZ4.js} +1 -1
  44. package/dist/{workspaces-EYNZDL45.js → workspaces-2V5UYPYC.js} +3 -3
  45. package/dist/{workspaces-52L6NZ3P.js → workspaces-MGKEAYNM.js} +4 -4
  46. package/dist/{worktree-BP5ICYQ2.js → worktree-THAXW5U7.js} +3 -3
  47. package/dist/{worktree-UCOSAI7J.js → worktree-XJKLDYOL.js} +2 -2
  48. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -1272,6 +1272,7 @@ function parseRemoteClientMessage(raw) {
1272
1272
  const msg = parsed;
1273
1273
  if (msg.type === "ping") return { type: "ping" };
1274
1274
  if (msg.type === "host_pair") return { type: "host_pair" };
1275
+ if (msg.type === "pairing_done") return { type: "pairing_done" };
1275
1276
  if (msg.type === "e2ee_fail") return { type: "e2ee_fail" };
1276
1277
  if (msg.type === "stop") return { type: "stop" };
1277
1278
  if (msg.type === "host_register") {
@@ -4112,6 +4113,38 @@ var init_caffeinate_hold = __esm({
4112
4113
  }
4113
4114
  });
4114
4115
 
4116
+ // src/review/review-write-gate.ts
4117
+ function hasLiveReviewSibling(thread, siblings = []) {
4118
+ const selfId = thread.id;
4119
+ return siblings.some(
4120
+ (t) => t.id !== selfId && t.status !== "archived" && t.title.trim() === REVIEW_TAB_TITLE
4121
+ );
4122
+ }
4123
+ function isReviewWriteGatedThread(thread, siblings) {
4124
+ if (thread.sourceType === "pr" || thread.title.trim() === REVIEW_TAB_TITLE) {
4125
+ return true;
4126
+ }
4127
+ return hasLiveReviewSibling(thread, siblings);
4128
+ }
4129
+ function formatReviewWriteGateDirective() {
4130
+ return [
4131
+ "Review writes are public. The PR or ticket author sees GitHub/Linear/AbleTime comments, ticket updates, and submitted reviews immediately \u2014 a draft will confuse them.",
4132
+ "Write the recommendation in chat so the user can work through the feedback first. Do not call github_comment, linear_comment, abletime_comment, github_update_issue, linear_update_issue, abletime_update_task, or run `gh pr review` / `gh pr comment`, until they type a next step that asks you to post.",
4133
+ "After the review is in chat, stop. Do not call ask_user. The user needs time to read and will type the next steps. If they already asked you to post a specific comment or update in this turn, that is confirmation.",
4134
+ "Do not request reviewers, change labels, or assign yourself unless they asked \u2014 those also notify the author."
4135
+ ].join("\n");
4136
+ }
4137
+ function formatReviewWriteGateReminder() {
4138
+ return "Review writes: keep the review in chat. Do not ask_user after it \u2014 they read and type next steps. Do not github_comment / linear_comment / *_update_* / `gh pr review` on the PR or ticket until they ask. Authors see those immediately.";
4139
+ }
4140
+ var REVIEW_TAB_TITLE;
4141
+ var init_review_write_gate = __esm({
4142
+ "src/review/review-write-gate.ts"() {
4143
+ "use strict";
4144
+ REVIEW_TAB_TITLE = "Review";
4145
+ }
4146
+ });
4147
+
4115
4148
  // src/git/team-meta.ts
4116
4149
  var SOCCER_TEAM_META;
4117
4150
  var init_team_meta = __esm({
@@ -4983,11 +5016,12 @@ function branchDisplayLabel(branchName, worktreePath) {
4983
5016
  }
4984
5017
  function taskPurposeTitle(title) {
4985
5018
  const trimmed = title?.trim() ?? "";
4986
- if (!trimmed || teamSlugFromName(trimmed)) return null;
5019
+ if (!trimmed || teamSlugFromName(trimmed) || trimmed === REVIEW_TAB_TITLE) return null;
4987
5020
  return trimmed;
4988
5021
  }
4989
5022
  function threadDisplayLabel(thread) {
4990
- if (thread.userSetTitle && thread.title?.trim()) return thread.title.trim();
5023
+ const titled = thread.title?.trim() ?? "";
5024
+ if (thread.userSetTitle && titled && titled !== REVIEW_TAB_TITLE) return titled;
4991
5025
  if (thread.prTitle?.trim()) return thread.prTitle.trim();
4992
5026
  if (isPlaceholderBranch(thread.branchName, thread.worktreePath)) {
4993
5027
  const purpose = taskPurposeTitle(thread.title);
@@ -5003,6 +5037,35 @@ function worktreeDisplayLabelForGroup(threads) {
5003
5037
  const canonical = [...threads].sort((a, b) => a.createdAt.localeCompare(b.createdAt))[0];
5004
5038
  return threadDisplayLabel(canonical);
5005
5039
  }
5040
+ function worktreeAnchorLabel(threads) {
5041
+ if (threads.length === 0) return { title: "Worktree", subtitle: null };
5042
+ const canonical = [...threads].sort((a, b) => a.createdAt.localeCompare(b.createdAt))[0];
5043
+ const pr = threads.map((t) => t.prTitle?.trim()).find((v) => Boolean(v)) ?? "";
5044
+ const branch = (canonical.branchName ?? "").trim();
5045
+ const placeholder = isPlaceholderBranch(branch, canonical.worktreePath);
5046
+ const display = branchDisplayLabel(canonical.branchName, canonical.worktreePath);
5047
+ const realBranch = !placeholder && branch && branch !== "HEAD" ? branch : null;
5048
+ if (pr) {
5049
+ return {
5050
+ title: pr,
5051
+ subtitle: realBranch && realBranch !== pr ? realBranch : null
5052
+ };
5053
+ }
5054
+ if (placeholder) {
5055
+ const purpose = taskPurposeTitle(canonical.title);
5056
+ if (purpose) return { title: purpose, subtitle: null };
5057
+ }
5058
+ if (display) {
5059
+ return {
5060
+ title: display,
5061
+ subtitle: realBranch && realBranch !== display ? realBranch : null
5062
+ };
5063
+ }
5064
+ return {
5065
+ title: worktreeNameFromPath(canonical.worktreePath),
5066
+ subtitle: realBranch
5067
+ };
5068
+ }
5006
5069
  function agentNicknameFromWorktreePath(worktreePath) {
5007
5070
  const dir = worktreeNameFromPath(worktreePath);
5008
5071
  if (!soccerTokenFromTakenSlug(dir)) return null;
@@ -5053,6 +5116,7 @@ var WORKSPACE_TAG_LIMIT, WORKSPACE_TAG_MAX_LENGTH;
5053
5116
  var init_worktree_labels = __esm({
5054
5117
  "src/git/worktree-labels.ts"() {
5055
5118
  "use strict";
5119
+ init_review_write_gate();
5056
5120
  init_teams();
5057
5121
  WORKSPACE_TAG_LIMIT = 8;
5058
5122
  WORKSPACE_TAG_MAX_LENGTH = 32;
@@ -20887,38 +20951,6 @@ var init_chat_tabs = __esm({
20887
20951
  }
20888
20952
  });
20889
20953
 
20890
- // src/review/review-write-gate.ts
20891
- function hasLiveReviewSibling(thread, siblings = []) {
20892
- const selfId = thread.id;
20893
- return siblings.some(
20894
- (t) => t.id !== selfId && t.status !== "archived" && t.title.trim() === REVIEW_TAB_TITLE
20895
- );
20896
- }
20897
- function isReviewWriteGatedThread(thread, siblings) {
20898
- if (thread.sourceType === "pr" || thread.title.trim() === REVIEW_TAB_TITLE) {
20899
- return true;
20900
- }
20901
- return hasLiveReviewSibling(thread, siblings);
20902
- }
20903
- function formatReviewWriteGateDirective() {
20904
- return [
20905
- "Review writes are public. The PR or ticket author sees GitHub/Linear/AbleTime comments, ticket updates, and submitted reviews immediately \u2014 a draft will confuse them.",
20906
- "Write the recommendation in chat so the user can work through the feedback first. Do not call github_comment, linear_comment, abletime_comment, github_update_issue, linear_update_issue, abletime_update_task, or run `gh pr review` / `gh pr comment`, until they type a next step that asks you to post.",
20907
- "After the review is in chat, stop. Do not call ask_user. The user needs time to read and will type the next steps. If they already asked you to post a specific comment or update in this turn, that is confirmation.",
20908
- "Do not request reviewers, change labels, or assign yourself unless they asked \u2014 those also notify the author."
20909
- ].join("\n");
20910
- }
20911
- function formatReviewWriteGateReminder() {
20912
- return "Review writes: keep the review in chat. Do not ask_user after it \u2014 they read and type next steps. Do not github_comment / linear_comment / *_update_* / `gh pr review` on the PR or ticket until they ask. Authors see those immediately.";
20913
- }
20914
- var REVIEW_TAB_TITLE;
20915
- var init_review_write_gate = __esm({
20916
- "src/review/review-write-gate.ts"() {
20917
- "use strict";
20918
- REVIEW_TAB_TITLE = "Review";
20919
- }
20920
- });
20921
-
20922
20954
  // src/review/review-request-template.ts
20923
20955
  function wrapReviewSkillMarkdown(body) {
20924
20956
  const trimmed = body.replace(/^\uFEFF/, "").trim();
@@ -33931,8 +33963,11 @@ var DirectionalSeal = class {
33931
33963
  recvCounter = 0;
33932
33964
  transcript = "";
33933
33965
  sendDirection;
33966
+ matchesClientNonce(clientNonce) {
33967
+ return clientNonce === this.hello.clientNonce;
33968
+ }
33934
33969
  acceptReady(ready, desktopNonceBytes) {
33935
- if (ready.v !== 1 || ready.clientNonce !== this.hello.clientNonce) return false;
33970
+ if (ready.v !== 1 || !this.matchesClientNonce(ready.clientNonce)) return false;
33936
33971
  const desktopNonce = desktopNonceBytes ?? b64urlToBytes(ready.desktopNonce);
33937
33972
  if (desktopNonce.length !== NONCE_BYTES) return false;
33938
33973
  let shared;
@@ -35497,7 +35532,7 @@ function listPhoneSidebar(viewerLogin = phoneViewerLogin()) {
35497
35532
  const badge = classifyWorktreeListBadges(group, viewerLogin, resolveProjectReviewLabel(settings, path2))[0];
35498
35533
  const blockedBy = issueBlockedByIds(group);
35499
35534
  return {
35500
- label: worktreeDisplayLabelForGroup(group),
35535
+ label: worktreeAnchorLabel(group).title,
35501
35536
  chats: group.map(summarize),
35502
35537
  ...tags.length > 0 ? { tags } : {},
35503
35538
  ...blockedBy.length > 0 ? { blockedBy } : {},
@@ -35577,7 +35612,7 @@ function projectPlace(thread) {
35577
35612
  return {
35578
35613
  kind: "project",
35579
35614
  repoPath: thread.repoPath,
35580
- worktree: worktreeDisplayLabelForGroup(group.length > 0 ? group : [thread])
35615
+ worktree: worktreeAnchorLabel(group.length > 0 ? group : [thread]).title
35581
35616
  };
35582
35617
  }
35583
35618
  function assertKnownProject(repoPath) {
@@ -39239,6 +39274,7 @@ function runRemoteHost(opts) {
39239
39274
  clientNonce: msg.clientNonce
39240
39275
  });
39241
39276
  if (ready) send3({ type: "e2ee_ready", ...ready });
39277
+ else send3({ type: "e2ee_fail" });
39242
39278
  return;
39243
39279
  }
39244
39280
  if (msg.type === "e2ee") {
@@ -39251,8 +39287,13 @@ function runRemoteHost(opts) {
39251
39287
  }
39252
39288
  if (opened.okFrame) {
39253
39289
  send3({ type: "e2ee", frame: opened.okFrame });
39254
- startBoardSync();
39290
+ try {
39291
+ startBoardSync();
39292
+ } catch (err) {
39293
+ log(`remote error: ${err instanceof Error ? err.message : err}`);
39294
+ }
39255
39295
  opts.onPairingCode?.(null);
39296
+ send3({ type: "pairing_done" });
39256
39297
  opts.onStatus?.({ connected: true, phoneConnected: true });
39257
39298
  log("phone joined");
39258
39299
  return;
@@ -40338,6 +40379,10 @@ var RemoteHub = class {
40338
40379
  this.issueCode(socket);
40339
40380
  return;
40340
40381
  }
40382
+ if (msg.type === "pairing_done") {
40383
+ this.finishPairing(socket);
40384
+ return;
40385
+ }
40341
40386
  if (msg.type === "pair") {
40342
40387
  this.pairPhone(socket, msg.code);
40343
40388
  return;
@@ -40417,7 +40462,8 @@ var RemoteHub = class {
40417
40462
  hostSecret,
40418
40463
  socket,
40419
40464
  sessionToken,
40420
- phone: existing?.socket === socket ? existing.phone : null
40465
+ phone: existing?.socket === socket ? existing.phone : null,
40466
+ pairingCode: existing && existing.socket === socket ? existing.pairingCode : null
40421
40467
  };
40422
40468
  this.hosts.set(deviceId, host);
40423
40469
  this.remembered.set(deviceId, { hostSecret, sessionToken, deviceLabel });
@@ -40453,8 +40499,15 @@ var RemoteHub = class {
40453
40499
  send(socket, { type: "error", message: "Mac is offline" });
40454
40500
  return;
40455
40501
  }
40456
- this.codes.delete(code);
40457
- this.attachPhone(socket, host);
40502
+ this.attachPhone(socket, host, void 0, code);
40503
+ }
40504
+ /** The Mac accepted the phone. That QR must not replace the saved session. */
40505
+ finishPairing(socket) {
40506
+ const bound = this.bySocket.get(socket);
40507
+ const host = bound?.role === "host" ? this.hosts.get(bound.deviceId) : null;
40508
+ if (!host || host.socket !== socket) return;
40509
+ if (host.pairingCode) this.codes.delete(host.pairingCode);
40510
+ host.pairingCode = null;
40458
40511
  }
40459
40512
  /** In-memory session, with the live Mac's label when that socket is up. */
40460
40513
  memorySession(sessionToken) {
@@ -40527,7 +40580,7 @@ var RemoteHub = class {
40527
40580
  send(prev.socket, { type: "phone_left" });
40528
40581
  }
40529
40582
  }
40530
- attachPhone(socket, host, sessionToken) {
40583
+ attachPhone(socket, host, sessionToken, pairingCode) {
40531
40584
  const bound = this.bySocket.get(socket);
40532
40585
  if (bound?.role === "phone" && bound.deviceId !== host.deviceId) {
40533
40586
  this.releasePhoneSocket(socket);
@@ -40548,6 +40601,7 @@ var RemoteHub = class {
40548
40601
  }
40549
40602
  host.sessionToken = token;
40550
40603
  host.phone = socket;
40604
+ if (pairingCode) host.pairingCode = pairingCode;
40551
40605
  this.phonesByToken.set(token, { deviceId: host.deviceId, deviceLabel: host.deviceLabel });
40552
40606
  this.remembered.set(host.deviceId, {
40553
40607
  hostSecret: host.hostSecret,
package/dist/index.d.cts CHANGED
@@ -8328,6 +8328,8 @@ declare class RemoteHub {
8328
8328
  private registerHost;
8329
8329
  private issueCode;
8330
8330
  private pairPhone;
8331
+ /** The Mac accepted the phone. That QR must not replace the saved session. */
8332
+ private finishPairing;
8331
8333
  /** In-memory session, with the live Mac's label when that socket is up. */
8332
8334
  private memorySession;
8333
8335
  private knownSession;
package/dist/index.d.ts CHANGED
@@ -8328,6 +8328,8 @@ declare class RemoteHub {
8328
8328
  private registerHost;
8329
8329
  private issueCode;
8330
8330
  private pairPhone;
8331
+ /** The Mac accepted the phone. That QR must not replace the saved session. */
8332
+ private finishPairing;
8331
8333
  /** In-memory session, with the live Mac's label when that socket is up. */
8332
8334
  private memorySession;
8333
8335
  private knownSession;
package/dist/index.js CHANGED
@@ -203,7 +203,7 @@ import {
203
203
  worktreeCleanupSettings,
204
204
  writeAgentBlock,
205
205
  writeWorktreeFile
206
- } from "./chunk-4ISF5XUA.js";
206
+ } from "./chunk-23ZUL2EX.js";
207
207
  import {
208
208
  LEGACY_PLAN_FILE_REL,
209
209
  PLAN_FILE_NAME,
@@ -293,7 +293,7 @@ import {
293
293
  userMcpNamesToDisable,
294
294
  worktreeMcpToolNames,
295
295
  writeInjectedMcpConfig
296
- } from "./chunk-NKVNHBDB.js";
296
+ } from "./chunk-IAPO7ZFG.js";
297
297
  import {
298
298
  brightsyMcpServerName,
299
299
  connectBrightsyTeam,
@@ -424,7 +424,7 @@ import {
424
424
  workspaceAgentChatSummaries,
425
425
  worktreeRunPortsPath,
426
426
  wrapReviewSkillMarkdown
427
- } from "./chunk-ATSZXRFQ.js";
427
+ } from "./chunk-ST2UQBJT.js";
428
428
  import {
429
429
  STDERR_EVENT_MAX_CHARS,
430
430
  STDOUT_EVENT_MAX_CHARS,
@@ -490,7 +490,7 @@ import {
490
490
  parseAbleTimeOAuthCallbackUrl,
491
491
  resolveAbleTimeOAuthHost,
492
492
  startAbleTimeOAuth
493
- } from "./chunk-CCUGIVYN.js";
493
+ } from "./chunk-XOZNEAWF.js";
494
494
  import {
495
495
  ableTimeDependencyTarget,
496
496
  commentAbleTimeTask,
@@ -515,10 +515,10 @@ import {
515
515
  toAbleTimeIssueInfo,
516
516
  updateAbleTimeTask,
517
517
  verifyAbleTimeConnection
518
- } from "./chunk-GXSV3X3L.js";
518
+ } from "./chunk-W5QZWAUB.js";
519
519
  import {
520
520
  looksLikeAbleTimeUlid
521
- } from "./chunk-VVV7EI6T.js";
521
+ } from "./chunk-2HOZMSKQ.js";
522
522
  import {
523
523
  ABLETIME_MCP_PATH,
524
524
  ABLETIME_MCP_PM_PATH,
@@ -539,13 +539,13 @@ import {
539
539
  normalizeAbleTimeCredential,
540
540
  normalizeAbleTimeHost,
541
541
  rewriteAbleTimeError
542
- } from "./chunk-ELBK34TY.js";
542
+ } from "./chunk-7ADEYEMH.js";
543
543
  import {
544
544
  clearOptionalCliPresenceCache,
545
545
  detectOptionalServiceClis,
546
546
  installOptionalServiceCli,
547
547
  optionalCliPresence
548
- } from "./chunk-S3U4HR5F.js";
548
+ } from "./chunk-AZ4GEEGL.js";
549
549
  import {
550
550
  OPTIONAL_SERVICES,
551
551
  OPTIONAL_SERVICE_IDS,
@@ -561,7 +561,7 @@ import {
561
561
  optionalServiceConnected,
562
562
  optionalServiceSpec,
563
563
  verifyOptionalService
564
- } from "./chunk-OLYNM52B.js";
564
+ } from "./chunk-FDXHNROD.js";
565
565
  import {
566
566
  getAgentSetupInfo,
567
567
  installAgent,
@@ -573,7 +573,7 @@ import {
573
573
  prepareTerminalCommand,
574
574
  resolveLoginCommand,
575
575
  whichOnPath
576
- } from "./chunk-NXNNYY3Q.js";
576
+ } from "./chunk-AESO2KJS.js";
577
577
  import {
578
578
  CLOUD_COORDINATOR_BUSY_REPLY,
579
579
  CLOUD_COORDINATOR_CANCELED_REPLY,
@@ -626,7 +626,7 @@ import {
626
626
  stageBuffersAsAttachments,
627
627
  syncWorkspacesFromThreads,
628
628
  takenTeamSlugsForOrchestration
629
- } from "./chunk-GFM63DTU.js";
629
+ } from "./chunk-YALFZKSC.js";
630
630
  import {
631
631
  GITHUB_PR_BODY_MAX_CHARS,
632
632
  GITHUB_PR_BODY_SAFE_CHARS,
@@ -714,7 +714,7 @@ import {
714
714
  suggestSlug,
715
715
  switchWorktreeBranch,
716
716
  warmGithubAgentAuth
717
- } from "./chunk-BPBDGJ43.js";
717
+ } from "./chunk-TUJFCJJU.js";
718
718
  import {
719
719
  FAMOUS_SOCCER_TEAMS,
720
720
  allocateTeamName,
@@ -742,12 +742,13 @@ import {
742
742
  withThreadLock,
743
743
  workspaceNameLabel,
744
744
  workspaceTagsFromGroup,
745
+ worktreeAnchorLabel,
745
746
  worktreeDisplayLabel,
746
747
  worktreeDisplayLabelForGroup,
747
748
  worktreeNameFromPath,
748
749
  worktreeSlugForTicket,
749
750
  writeThread
750
- } from "./chunk-CHUPIJP7.js";
751
+ } from "./chunk-YEIHCSZS.js";
751
752
  import "./chunk-2P4XKWV2.js";
752
753
  import {
753
754
  BRIGHTSY_OAUTH_CLIENT_ID,
@@ -1012,7 +1013,7 @@ import {
1012
1013
  withTurnScopedCost,
1013
1014
  writeSecureJson,
1014
1015
  writeViewerContext
1015
- } from "./chunk-U7PNCYNX.js";
1016
+ } from "./chunk-E52SQENB.js";
1016
1017
  import {
1017
1018
  THINKING_EFFORTS,
1018
1019
  isThinkingEffort,
@@ -1204,8 +1205,11 @@ var DirectionalSeal = class {
1204
1205
  recvCounter = 0;
1205
1206
  transcript = "";
1206
1207
  sendDirection;
1208
+ matchesClientNonce(clientNonce) {
1209
+ return clientNonce === this.hello.clientNonce;
1210
+ }
1207
1211
  acceptReady(ready, desktopNonceBytes) {
1208
- if (ready.v !== 1 || ready.clientNonce !== this.hello.clientNonce) return false;
1212
+ if (ready.v !== 1 || !this.matchesClientNonce(ready.clientNonce)) return false;
1209
1213
  const desktopNonce = desktopNonceBytes ?? b64urlToBytes(ready.desktopNonce);
1210
1214
  if (desktopNonce.length !== NONCE_BYTES) return false;
1211
1215
  let shared;
@@ -2708,7 +2712,7 @@ function listPhoneSidebar(viewerLogin = phoneViewerLogin()) {
2708
2712
  const badge = classifyWorktreeListBadges(group, viewerLogin, resolveProjectReviewLabel(settings, path2))[0];
2709
2713
  const blockedBy = issueBlockedByIds(group);
2710
2714
  return {
2711
- label: worktreeDisplayLabelForGroup(group),
2715
+ label: worktreeAnchorLabel(group).title,
2712
2716
  chats: group.map(summarize),
2713
2717
  ...tags.length > 0 ? { tags } : {},
2714
2718
  ...blockedBy.length > 0 ? { blockedBy } : {},
@@ -2788,7 +2792,7 @@ function projectPlace(thread) {
2788
2792
  return {
2789
2793
  kind: "project",
2790
2794
  repoPath: thread.repoPath,
2791
- worktree: worktreeDisplayLabelForGroup(group.length > 0 ? group : [thread])
2795
+ worktree: worktreeAnchorLabel(group.length > 0 ? group : [thread]).title
2792
2796
  };
2793
2797
  }
2794
2798
  function assertKnownProject(repoPath) {
@@ -6391,6 +6395,7 @@ function runRemoteHost(opts) {
6391
6395
  clientNonce: msg.clientNonce
6392
6396
  });
6393
6397
  if (ready) send3({ type: "e2ee_ready", ...ready });
6398
+ else send3({ type: "e2ee_fail" });
6394
6399
  return;
6395
6400
  }
6396
6401
  if (msg.type === "e2ee") {
@@ -6403,8 +6408,13 @@ function runRemoteHost(opts) {
6403
6408
  }
6404
6409
  if (opened.okFrame) {
6405
6410
  send3({ type: "e2ee", frame: opened.okFrame });
6406
- startBoardSync();
6411
+ try {
6412
+ startBoardSync();
6413
+ } catch (err) {
6414
+ log(`remote error: ${err instanceof Error ? err.message : err}`);
6415
+ }
6407
6416
  opts.onPairingCode?.(null);
6417
+ send3({ type: "pairing_done" });
6408
6418
  opts.onStatus?.({ connected: true, phoneConnected: true });
6409
6419
  log("phone joined");
6410
6420
  return;
@@ -7486,6 +7496,10 @@ var RemoteHub = class {
7486
7496
  this.issueCode(socket);
7487
7497
  return;
7488
7498
  }
7499
+ if (msg.type === "pairing_done") {
7500
+ this.finishPairing(socket);
7501
+ return;
7502
+ }
7489
7503
  if (msg.type === "pair") {
7490
7504
  this.pairPhone(socket, msg.code);
7491
7505
  return;
@@ -7565,7 +7579,8 @@ var RemoteHub = class {
7565
7579
  hostSecret,
7566
7580
  socket,
7567
7581
  sessionToken,
7568
- phone: existing?.socket === socket ? existing.phone : null
7582
+ phone: existing?.socket === socket ? existing.phone : null,
7583
+ pairingCode: existing && existing.socket === socket ? existing.pairingCode : null
7569
7584
  };
7570
7585
  this.hosts.set(deviceId, host);
7571
7586
  this.remembered.set(deviceId, { hostSecret, sessionToken, deviceLabel });
@@ -7601,8 +7616,15 @@ var RemoteHub = class {
7601
7616
  send(socket, { type: "error", message: "Mac is offline" });
7602
7617
  return;
7603
7618
  }
7604
- this.codes.delete(code);
7605
- this.attachPhone(socket, host);
7619
+ this.attachPhone(socket, host, void 0, code);
7620
+ }
7621
+ /** The Mac accepted the phone. That QR must not replace the saved session. */
7622
+ finishPairing(socket) {
7623
+ const bound = this.bySocket.get(socket);
7624
+ const host = bound?.role === "host" ? this.hosts.get(bound.deviceId) : null;
7625
+ if (!host || host.socket !== socket) return;
7626
+ if (host.pairingCode) this.codes.delete(host.pairingCode);
7627
+ host.pairingCode = null;
7606
7628
  }
7607
7629
  /** In-memory session, with the live Mac's label when that socket is up. */
7608
7630
  memorySession(sessionToken) {
@@ -7675,7 +7697,7 @@ var RemoteHub = class {
7675
7697
  send(prev.socket, { type: "phone_left" });
7676
7698
  }
7677
7699
  }
7678
- attachPhone(socket, host, sessionToken) {
7700
+ attachPhone(socket, host, sessionToken, pairingCode) {
7679
7701
  const bound = this.bySocket.get(socket);
7680
7702
  if (bound?.role === "phone" && bound.deviceId !== host.deviceId) {
7681
7703
  this.releasePhoneSocket(socket);
@@ -7696,6 +7718,7 @@ var RemoteHub = class {
7696
7718
  }
7697
7719
  host.sessionToken = token;
7698
7720
  host.phone = socket;
7721
+ if (pairingCode) host.pairingCode = pairingCode;
7699
7722
  this.phonesByToken.set(token, { deviceId: host.deviceId, deviceLabel: host.deviceLabel });
7700
7723
  this.remembered.set(host.deviceId, {
7701
7724
  hostSecret: host.hostSecret,
@@ -8730,7 +8753,7 @@ async function startHeadlessHost(opts) {
8730
8753
  };
8731
8754
  const refreshRunning = async () => {
8732
8755
  try {
8733
- const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-KXEPWTG5.js");
8756
+ const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-2SXK4U65.js");
8734
8757
  agentsRunning = getOrchestrator2().getRuntime().running;
8735
8758
  } catch {
8736
8759
  agentsRunning = 0;
@@ -10663,7 +10686,7 @@ async function handleJobStop(params, _ctx) {
10663
10686
  return stopDetachedJob(jobCwd(rec), id, reason ? { reason } : void 0);
10664
10687
  }
10665
10688
  async function defaultTurnWait(ref, timeoutMs) {
10666
- const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-KXEPWTG5.js");
10689
+ const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-2SXK4U65.js");
10667
10690
  const orch = getOrchestrator2();
10668
10691
  const thread = await orch.waitForTurn(ref, timeoutMs, {
10669
10692
  resolveIfStillRunning: true
@@ -11016,7 +11039,7 @@ async function startMcpServer() {
11016
11039
  );
11017
11040
  }
11018
11041
  try {
11019
- const { maxConcurrentAgents: maxConcurrentAgents2 } = await import("./app-settings-RQAFJZCD.js");
11042
+ const { maxConcurrentAgents: maxConcurrentAgents2 } = await import("./app-settings-V6YPYY57.js");
11020
11043
  orch.setMaxConcurrent(maxConcurrentAgents2());
11021
11044
  } catch {
11022
11045
  }
@@ -11564,7 +11587,7 @@ async function startMcpServer() {
11564
11587
  if (!worktreeProfile) {
11565
11588
  registerConnectedIssueVendorTools(server);
11566
11589
  const { getCaffeinateHold: getCaffeinateHold2, setCaffeinateHold: setCaffeinateHold2 } = await import("./caffeinate-hold-ZNUEZGHB.js");
11567
- const { resolveNewThreadOptions: resolveNewThreadOptions2, resolveThreadDefaults: resolveThreadDefaults2 } = await import("./app-settings-RQAFJZCD.js");
11590
+ const { resolveNewThreadOptions: resolveNewThreadOptions2, resolveThreadDefaults: resolveThreadDefaults2 } = await import("./app-settings-V6YPYY57.js");
11568
11591
  const accountDefaults = resolveThreadDefaults2();
11569
11592
  const accountDefaultsHint = `Account defaults: agent=${accountDefaults.agent}, model=${accountDefaults.model?.trim() || "Auto"}, effort=${accountDefaults.effort}`;
11570
11593
  server.tool(
@@ -14316,7 +14339,7 @@ async function continueSourceThread(threadId, prompt) {
14316
14339
  await continueOnReply(threadId, prompt);
14317
14340
  return;
14318
14341
  }
14319
- const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-KXEPWTG5.js");
14342
+ const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-2SXK4U65.js");
14320
14343
  await getOrchestrator2().send(threadId, prompt);
14321
14344
  } catch {
14322
14345
  }
@@ -2653,6 +2653,38 @@ var init_run = __esm({
2653
2653
  }
2654
2654
  });
2655
2655
 
2656
+ // src/review/review-write-gate.ts
2657
+ function hasLiveReviewSibling(thread, siblings = []) {
2658
+ const selfId = thread.id;
2659
+ return siblings.some(
2660
+ (t) => t.id !== selfId && t.status !== "archived" && t.title.trim() === REVIEW_TAB_TITLE
2661
+ );
2662
+ }
2663
+ function isReviewWriteGatedThread(thread, siblings) {
2664
+ if (thread.sourceType === "pr" || thread.title.trim() === REVIEW_TAB_TITLE) {
2665
+ return true;
2666
+ }
2667
+ return hasLiveReviewSibling(thread, siblings);
2668
+ }
2669
+ function formatReviewWriteGateDirective() {
2670
+ return [
2671
+ "Review writes are public. The PR or ticket author sees GitHub/Linear/AbleTime comments, ticket updates, and submitted reviews immediately \u2014 a draft will confuse them.",
2672
+ "Write the recommendation in chat so the user can work through the feedback first. Do not call github_comment, linear_comment, abletime_comment, github_update_issue, linear_update_issue, abletime_update_task, or run `gh pr review` / `gh pr comment`, until they type a next step that asks you to post.",
2673
+ "After the review is in chat, stop. Do not call ask_user. The user needs time to read and will type the next steps. If they already asked you to post a specific comment or update in this turn, that is confirmation.",
2674
+ "Do not request reviewers, change labels, or assign yourself unless they asked \u2014 those also notify the author."
2675
+ ].join("\n");
2676
+ }
2677
+ function formatReviewWriteGateReminder() {
2678
+ return "Review writes: keep the review in chat. Do not ask_user after it \u2014 they read and type next steps. Do not github_comment / linear_comment / *_update_* / `gh pr review` on the PR or ticket until they ask. Authors see those immediately.";
2679
+ }
2680
+ var REVIEW_TAB_TITLE;
2681
+ var init_review_write_gate = __esm({
2682
+ "src/review/review-write-gate.ts"() {
2683
+ "use strict";
2684
+ REVIEW_TAB_TITLE = "Review";
2685
+ }
2686
+ });
2687
+
2656
2688
  // src/git/team-meta.ts
2657
2689
  var SOCCER_TEAM_META;
2658
2690
  var init_team_meta = __esm({
@@ -3524,11 +3556,12 @@ function branchDisplayLabel(branchName, worktreePath) {
3524
3556
  }
3525
3557
  function taskPurposeTitle(title) {
3526
3558
  const trimmed = title?.trim() ?? "";
3527
- if (!trimmed || teamSlugFromName(trimmed)) return null;
3559
+ if (!trimmed || teamSlugFromName(trimmed) || trimmed === REVIEW_TAB_TITLE) return null;
3528
3560
  return trimmed;
3529
3561
  }
3530
3562
  function threadDisplayLabel(thread) {
3531
- if (thread.userSetTitle && thread.title?.trim()) return thread.title.trim();
3563
+ const titled = thread.title?.trim() ?? "";
3564
+ if (thread.userSetTitle && titled && titled !== REVIEW_TAB_TITLE) return titled;
3532
3565
  if (thread.prTitle?.trim()) return thread.prTitle.trim();
3533
3566
  if (isPlaceholderBranch(thread.branchName, thread.worktreePath)) {
3534
3567
  const purpose = taskPurposeTitle(thread.title);
@@ -3584,6 +3617,7 @@ var WORKSPACE_TAG_LIMIT, WORKSPACE_TAG_MAX_LENGTH;
3584
3617
  var init_worktree_labels = __esm({
3585
3618
  "src/git/worktree-labels.ts"() {
3586
3619
  "use strict";
3620
+ init_review_write_gate();
3587
3621
  init_teams();
3588
3622
  WORKSPACE_TAG_LIMIT = 8;
3589
3623
  WORKSPACE_TAG_MAX_LENGTH = 32;
@@ -17248,38 +17282,6 @@ var init_chat_tabs = __esm({
17248
17282
  }
17249
17283
  });
17250
17284
 
17251
- // src/review/review-write-gate.ts
17252
- function hasLiveReviewSibling(thread, siblings = []) {
17253
- const selfId = thread.id;
17254
- return siblings.some(
17255
- (t) => t.id !== selfId && t.status !== "archived" && t.title.trim() === REVIEW_TAB_TITLE
17256
- );
17257
- }
17258
- function isReviewWriteGatedThread(thread, siblings) {
17259
- if (thread.sourceType === "pr" || thread.title.trim() === REVIEW_TAB_TITLE) {
17260
- return true;
17261
- }
17262
- return hasLiveReviewSibling(thread, siblings);
17263
- }
17264
- function formatReviewWriteGateDirective() {
17265
- return [
17266
- "Review writes are public. The PR or ticket author sees GitHub/Linear/AbleTime comments, ticket updates, and submitted reviews immediately \u2014 a draft will confuse them.",
17267
- "Write the recommendation in chat so the user can work through the feedback first. Do not call github_comment, linear_comment, abletime_comment, github_update_issue, linear_update_issue, abletime_update_task, or run `gh pr review` / `gh pr comment`, until they type a next step that asks you to post.",
17268
- "After the review is in chat, stop. Do not call ask_user. The user needs time to read and will type the next steps. If they already asked you to post a specific comment or update in this turn, that is confirmation.",
17269
- "Do not request reviewers, change labels, or assign yourself unless they asked \u2014 those also notify the author."
17270
- ].join("\n");
17271
- }
17272
- function formatReviewWriteGateReminder() {
17273
- return "Review writes: keep the review in chat. Do not ask_user after it \u2014 they read and type next steps. Do not github_comment / linear_comment / *_update_* / `gh pr review` on the PR or ticket until they ask. Authors see those immediately.";
17274
- }
17275
- var REVIEW_TAB_TITLE;
17276
- var init_review_write_gate = __esm({
17277
- "src/review/review-write-gate.ts"() {
17278
- "use strict";
17279
- REVIEW_TAB_TITLE = "Review";
17280
- }
17281
- });
17282
-
17283
17285
  // src/review/review-request-template.ts
17284
17286
  var REVIEW_REQUEST_TEMPLATE, REVIEW_SKILL_PATH, REVIEW_SKILL_NAME;
17285
17287
  var init_review_request_template = __esm({