@sideboard-ai/core 0.1.294 → 0.1.296

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 (45) hide show
  1. package/dist/agents/claude-runner.cjs +55 -0
  2. package/dist/agents/claude-runner.js +57 -1
  3. package/dist/agents/cursor-runner.cjs +128 -50
  4. package/dist/agents/cursor-runner.js +63 -44
  5. package/dist/{agents-2PDBDZPS.js → agents-DZTPUKVF.js} +5 -5
  6. package/dist/{agents-73JXQXF2.js → agents-ICHYDSCD.js} +8 -8
  7. package/dist/{chunk-SVQTKVXH.js → chunk-2O3ICR77.js} +4 -4
  8. package/dist/{chunk-YKO6HG74.js → chunk-67BWWLEB.js} +6 -6
  9. package/dist/{chunk-JEYJGQ33.js → chunk-6GTABVNG.js} +7 -7
  10. package/dist/{chunk-YEIPPZ5W.js → chunk-BGEECLKH.js} +2 -2
  11. package/dist/{chunk-AFLNFGGB.js → chunk-ED4ZXQVO.js} +1 -0
  12. package/dist/{chunk-MIVX7QKI.js → chunk-HO5R3ZRH.js} +1 -0
  13. package/dist/{chunk-7UUILVMV.js → chunk-IOY2LF3Y.js} +1 -1
  14. package/dist/{chunk-K6IOJI3L.js → chunk-J54UPHXZ.js} +71 -2
  15. package/dist/{chunk-ZEP6JIRF.js → chunk-JWXJXEA3.js} +1 -1
  16. package/dist/{chunk-YSLCWCMJ.js → chunk-Q3DPZ4DE.js} +1 -1
  17. package/dist/{chunk-NYBFYFK7.js → chunk-QDA6NXRA.js} +1 -0
  18. package/dist/{chunk-CECZUHA5.js → chunk-QT36F4TD.js} +55 -26
  19. package/dist/{chunk-YRXV3E7J.js → chunk-TVN4TDFB.js} +9 -0
  20. package/dist/{chunk-6SGYGYH7.js → chunk-VXNZG4J6.js} +1 -1
  21. package/dist/{chunk-BYAFYLLZ.js → chunk-W2A63ZOK.js} +179 -42
  22. package/dist/{chunk-E7G7RY6T.js → chunk-WQREAX35.js} +2 -2
  23. package/dist/{chunk-QB5MDOHL.js → chunk-WZLV4WNS.js} +9 -0
  24. package/dist/{chunk-BJUHZSZQ.js → chunk-ZXWRNA4G.js} +5 -5
  25. package/dist/{conductor-7DEMFEIO.js → conductor-HJFDYIZD.js} +5 -5
  26. package/dist/{conductor-ZQZTYXSX.js → conductor-ZXDIA6OF.js} +6 -6
  27. package/dist/{coordinator-prompt-AROMFCP2.js → coordinator-prompt-ILHSVPOG.js} +3 -3
  28. package/dist/{coordinator-prompt-K6XHUG3N.js → coordinator-prompt-L3V2CMWX.js} +3 -3
  29. package/dist/{global-workspace-4WAU4PAL.js → global-workspace-47G453ZU.js} +3 -3
  30. package/dist/{global-workspace-PX4GUVLW.js → global-workspace-NMRUGGFF.js} +3 -3
  31. package/dist/index.cjs +1039 -700
  32. package/dist/index.d.cts +25 -4
  33. package/dist/index.d.ts +25 -4
  34. package/dist/index.js +299 -125
  35. package/dist/mcp/run-stdio.cjs +250 -144
  36. package/dist/mcp/run-stdio.js +70 -11
  37. package/dist/{orchestrator-LIEGSQ6M.js → orchestrator-MR7TCIPP.js} +7 -7
  38. package/dist/{orchestrator-YNEEEP6O.js → orchestrator-ZQU62CES.js} +10 -10
  39. package/dist/{thread-store-DPKESDFR.js → thread-store-H6D6MDUM.js} +1 -1
  40. package/dist/{thread-store-X6S2GIR5.js → thread-store-SADLLKRE.js} +1 -1
  41. package/dist/{workspaces-MFU3IGAP.js → workspaces-PGY4UWZ3.js} +3 -3
  42. package/dist/{workspaces-U7SSD27C.js → workspaces-WGZPRQJX.js} +3 -3
  43. package/dist/{worktree-TQKASYPZ.js → worktree-3MF5J5XZ.js} +2 -2
  44. package/dist/{worktree-2VSHRI72.js → worktree-JZXDAKQF.js} +2 -2
  45. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -19,17 +19,20 @@ import {
19
19
  agentBlockFields,
20
20
  agentGitPrompt,
21
21
  alignQueueAttachments,
22
+ answerToolPermission,
22
23
  appendQueuedItem,
23
24
  applyIssueListWindow,
24
25
  applyPromptCacheTtlEnv,
25
26
  applyThreadIntoMain,
26
27
  applyWorkspaceTags,
27
28
  armSchedules,
29
+ askToolPermission,
28
30
  assembleHomeBoard,
29
31
  boardPinIdentity,
30
32
  captureTurnBaseline,
31
33
  chatBlockedSuffix,
32
34
  claimDesktopHost,
35
+ clampBlockReason,
33
36
  clampMcpIssueLimit,
34
37
  classifyWorktreeColumn,
35
38
  classifyWorktreeListBadges,
@@ -160,6 +163,7 @@ import {
160
163
  reviewLabelForRepo,
161
164
  scheduleNeedsCaffeinate,
162
165
  schedulesPath,
166
+ setToolPermissionPublisher,
163
167
  shiftQueuedItem,
164
168
  shouldNotifyOrchestrator,
165
169
  shouldRemoveWorktreeOnTeardown,
@@ -185,8 +189,9 @@ import {
185
189
  worktreeAgentBlock,
186
190
  worktreeBoardStatus,
187
191
  worktreeCleanupSettings,
192
+ writeAgentBlock,
188
193
  writeWorktreeFile
189
- } from "./chunk-BYAFYLLZ.js";
194
+ } from "./chunk-W2A63ZOK.js";
190
195
  import {
191
196
  LEGACY_PLAN_FILE_REL,
192
197
  PLAN_FILE_NAME,
@@ -282,7 +287,7 @@ import {
282
287
  userMcpNamesToDisable,
283
288
  worktreeMcpToolNames,
284
289
  writeInjectedMcpConfig
285
- } from "./chunk-JEYJGQ33.js";
290
+ } from "./chunk-6GTABVNG.js";
286
291
  import {
287
292
  brightsyMcpServerName,
288
293
  connectBrightsyTeam,
@@ -410,7 +415,7 @@ import {
410
415
  workspaceAgentChatSummaries,
411
416
  worktreeRunPortsPath,
412
417
  wrapReviewSkillMarkdown
413
- } from "./chunk-YKO6HG74.js";
418
+ } from "./chunk-67BWWLEB.js";
414
419
  import {
415
420
  STDERR_EVENT_MAX_CHARS,
416
421
  STDOUT_EVENT_MAX_CHARS,
@@ -452,7 +457,7 @@ import {
452
457
  visibleToolRowDetail,
453
458
  withEventParentId,
454
459
  withEventsParentId
455
- } from "./chunk-YSLCWCMJ.js";
460
+ } from "./chunk-Q3DPZ4DE.js";
456
461
  import "./chunk-7H655NEW.js";
457
462
  import {
458
463
  ABLETIME_OAUTH_CALLBACK_PATH,
@@ -610,7 +615,7 @@ import {
610
615
  stageBuffersAsAttachments,
611
616
  syncWorkspacesFromThreads,
612
617
  takenTeamSlugsForOrchestration
613
- } from "./chunk-YEIPPZ5W.js";
618
+ } from "./chunk-BGEECLKH.js";
614
619
  import {
615
620
  GITHUB_PR_BODY_MAX_CHARS,
616
621
  GITHUB_PR_BODY_SAFE_CHARS,
@@ -698,7 +703,7 @@ import {
698
703
  suggestSlug,
699
704
  switchWorktreeBranch,
700
705
  warmGithubAgentAuth
701
- } from "./chunk-ZEP6JIRF.js";
706
+ } from "./chunk-JWXJXEA3.js";
702
707
  import {
703
708
  FAMOUS_SOCCER_TEAMS,
704
709
  allocateTeamName,
@@ -730,7 +735,7 @@ import {
730
735
  worktreeNameFromPath,
731
736
  worktreeSlugForTicket,
732
737
  writeThread
733
- } from "./chunk-QB5MDOHL.js";
738
+ } from "./chunk-WZLV4WNS.js";
734
739
  import "./chunk-2P4XKWV2.js";
735
740
  import {
736
741
  BRIGHTSY_OAUTH_CLIENT_ID,
@@ -752,8 +757,9 @@ import {
752
757
  setHttpFetchImpl
753
758
  } from "./chunk-JAT3JFOS.js";
754
759
  import {
755
- clipMcpToolResult
756
- } from "./chunk-K6IOJI3L.js";
760
+ clipMcpToolResult,
761
+ cursorToolErrorText
762
+ } from "./chunk-J54UPHXZ.js";
757
763
  import "./chunk-AXQ4ZWHK.js";
758
764
  import {
759
765
  AGENT_RUNNER_MAX_OLD_SPACE_MB,
@@ -769,8 +775,8 @@ import {
769
775
  trustSystemCertificates,
770
776
  turnCostUsdFromCursorUsage,
771
777
  withMaxOldSpaceSize
772
- } from "./chunk-7UUILVMV.js";
773
- import "./chunk-MIVX7QKI.js";
778
+ } from "./chunk-IOY2LF3Y.js";
779
+ import "./chunk-HO5R3ZRH.js";
774
780
  import {
775
781
  ATTACHMENTS_DIR,
776
782
  CLI_DIR,
@@ -818,7 +824,7 @@ import {
818
824
  rpcNotify,
819
825
  rpcResult,
820
826
  writeAgentRuntimeMetadata
821
- } from "./chunk-NYBFYFK7.js";
827
+ } from "./chunk-QDA6NXRA.js";
822
828
  import {
823
829
  AGENT_DONE_SOUNDS,
824
830
  CHAT_TEXT_SCALES,
@@ -3596,8 +3602,18 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
3596
3602
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3597
3603
  function wrapToolCallback(fn, cwd) {
3598
3604
  return async (...args) => {
3599
- const result = await fn(...args);
3600
- return clipMcpToolResult(result, cwd());
3605
+ try {
3606
+ const result = await fn(...args);
3607
+ return clipMcpToolResult(
3608
+ result,
3609
+ cwd()
3610
+ );
3611
+ } catch (err) {
3612
+ return {
3613
+ isError: true,
3614
+ content: [{ type: "text", text: cursorToolErrorText(err) }]
3615
+ };
3616
+ }
3601
3617
  };
3602
3618
  }
3603
3619
  function wrapRegistrar(orig, cwd) {
@@ -5038,6 +5054,124 @@ function registerScheduleTools(server) {
5038
5054
  // src/agent-rpc/handlers.ts
5039
5055
  import { randomUUID as randomUUID3 } from "crypto";
5040
5056
  import { isAbsolute } from "path";
5057
+
5058
+ // src/remote/phone-permission.ts
5059
+ var PHONE_CONTROL_PREFIX = "\0sb.phone\n";
5060
+ var DECISIONS = /* @__PURE__ */ new Set(["allow", "deny", "allow_session"]);
5061
+ var push = null;
5062
+ function setPhonePermissionPush(next) {
5063
+ push = next;
5064
+ }
5065
+ function notifyPhonePermission(chatId, prompt) {
5066
+ const id = chatId.trim();
5067
+ if (!id) return;
5068
+ push?.(id, prompt);
5069
+ }
5070
+ function permissionViewFromBlock(block) {
5071
+ if (!block || block.source !== "permission") return null;
5072
+ const prompt = block.permission;
5073
+ if (!prompt?.id || !prompt.title) return null;
5074
+ return {
5075
+ id: prompt.id,
5076
+ title: prompt.title,
5077
+ detail: typeof prompt.detail === "string" ? prompt.detail : "",
5078
+ ...prompt.path ? { path: prompt.path } : {},
5079
+ allowSession: prompt.allowSession !== false,
5080
+ ...prompt.defaultToNo ? { defaultToNo: true } : {}
5081
+ };
5082
+ }
5083
+ function phonePermissionView(chatId) {
5084
+ return permissionViewFromBlock(readThread(chatId)?.agentBlock);
5085
+ }
5086
+ function textField(value, key) {
5087
+ const raw = value[key];
5088
+ return typeof raw === "string" ? raw.trim() : "";
5089
+ }
5090
+ function takePhonePermission(text5) {
5091
+ if (!text5.startsWith(PHONE_CONTROL_PREFIX)) return null;
5092
+ let parsed;
5093
+ try {
5094
+ parsed = JSON.parse(text5.slice(PHONE_CONTROL_PREFIX.length));
5095
+ } catch {
5096
+ return null;
5097
+ }
5098
+ if (!parsed || typeof parsed !== "object") return null;
5099
+ if (parsed.op !== "permission") return null;
5100
+ const chatId = textField(parsed, "chatId");
5101
+ const id = textField(parsed, "id");
5102
+ const decision = textField(parsed, "decision");
5103
+ if (!chatId || !id || !DECISIONS.has(decision)) return "invalid";
5104
+ return { op: "permission", chatId, id, decision };
5105
+ }
5106
+ function dispatchPhonePermission(text5, emit) {
5107
+ const cmd = takePhonePermission(text5);
5108
+ if (cmd == null) return false;
5109
+ if (cmd === "invalid") {
5110
+ emit({ op: "error", message: "invalid phone command" });
5111
+ return true;
5112
+ }
5113
+ if (!answerToolPermission(cmd.id, cmd.decision)) {
5114
+ emit({ op: "error", chatId: cmd.chatId, message: "That request already finished." });
5115
+ emit({ op: "permission", chatId: cmd.chatId, prompt: phonePermissionView(cmd.chatId) });
5116
+ }
5117
+ return true;
5118
+ }
5119
+
5120
+ // src/agent-rpc/permission-handler.ts
5121
+ setToolPermissionPublisher({
5122
+ show(item) {
5123
+ const thread = readThread(item.threadId);
5124
+ if (!thread || thread.status === "archived") return false;
5125
+ writeAgentBlock(item.threadId, {
5126
+ source: "permission",
5127
+ reason: clampBlockReason(item.prompt.title),
5128
+ at: (/* @__PURE__ */ new Date()).toISOString(),
5129
+ permission: item.prompt
5130
+ });
5131
+ notifyPhonePermission(item.threadId, item.prompt);
5132
+ return true;
5133
+ },
5134
+ clear(threadId, id) {
5135
+ const thread = readThread(threadId);
5136
+ if (thread?.agentBlock?.source === "permission" && thread.agentBlock.permission?.id === id) {
5137
+ updateThread(threadId, { agentBlock: null });
5138
+ notifyPhonePermission(threadId, null);
5139
+ }
5140
+ }
5141
+ });
5142
+ function asRecord(value) {
5143
+ if (!value || typeof value !== "object" || Array.isArray(value)) return {};
5144
+ return value;
5145
+ }
5146
+ function str(value) {
5147
+ return typeof value === "string" ? value : "";
5148
+ }
5149
+ function invalidParams(message) {
5150
+ return Object.assign(new Error(message), { rpcCode: -32602 });
5151
+ }
5152
+ var handlePermissionAsk = async (params, _ctx) => {
5153
+ const rec = asRecord(params);
5154
+ const threadId = str(rec.threadId).trim();
5155
+ const toolName = str(rec.toolName).trim();
5156
+ if (!threadId) throw invalidParams("threadId is required");
5157
+ if (!toolName) throw invalidParams("toolName is required");
5158
+ const input = asRecord(rec.input);
5159
+ const ask = {
5160
+ threadId,
5161
+ toolName,
5162
+ input: Object.keys(input).length > 0 ? input : void 0,
5163
+ blockedPath: str(rec.blockedPath).trim() || void 0,
5164
+ title: str(rec.title).trim() || void 0,
5165
+ description: str(rec.description).trim() || void 0,
5166
+ displayName: str(rec.displayName).trim() || void 0,
5167
+ suggestions: rec.suggestions,
5168
+ suppressAlwaysAllow: rec.suppressAlwaysAllow === true,
5169
+ defaultToNo: rec.defaultToNo === true
5170
+ };
5171
+ return askToolPermission(ask);
5172
+ };
5173
+
5174
+ // src/agent-rpc/handlers.ts
5041
5175
  function presentArtifactResult(input) {
5042
5176
  const id = input.artifact_id?.trim() || `artifact_${Date.now().toString(36)}_${randomUUID3().slice(0, 8)}`;
5043
5177
  return {
@@ -5051,20 +5185,20 @@ function presentArtifactResult(input) {
5051
5185
  message: input.type === "log" ? "Log accepted. Same artifact_id appends; send only new lines next time." : "Artifact accepted. Sideboard desktop opens it in the side column beside chat."
5052
5186
  };
5053
5187
  }
5054
- function asRecord(value) {
5188
+ function asRecord2(value) {
5055
5189
  if (!value || typeof value !== "object" || Array.isArray(value)) return {};
5056
5190
  return value;
5057
5191
  }
5058
- function str(value) {
5192
+ function str2(value) {
5059
5193
  return typeof value === "string" ? value : "";
5060
5194
  }
5061
- function invalidParams(message) {
5195
+ function invalidParams2(message) {
5062
5196
  return Object.assign(new Error(message), { rpcCode: -32602 });
5063
5197
  }
5064
5198
  function jobCwd(rec) {
5065
- const raw = str(rec.cwd).trim();
5066
- if (!raw) throw invalidParams("cwd is required");
5067
- if (!isAbsolute(raw)) throw invalidParams("cwd must be an absolute path");
5199
+ const raw = str2(rec.cwd).trim();
5200
+ if (!raw) throw invalidParams2("cwd is required");
5201
+ if (!isAbsolute(raw)) throw invalidParams2("cwd must be an absolute path");
5068
5202
  return raw;
5069
5203
  }
5070
5204
  async function handleRuntimePing(_params, _ctx) {
@@ -5075,33 +5209,33 @@ async function handleRuntimePing(_params, _ctx) {
5075
5209
  };
5076
5210
  }
5077
5211
  async function handlePresentArtifact(params, _ctx) {
5078
- const rec = asRecord(params);
5079
- const title = str(rec.title).trim();
5080
- const type = str(rec.type).trim();
5212
+ const rec = asRecord2(params);
5213
+ const title = str2(rec.title).trim();
5214
+ const type = str2(rec.type).trim();
5081
5215
  const allowed = ["html", "svg", "markdown", "react", "log"];
5082
- if (!title) throw invalidParams("title is required");
5216
+ if (!title) throw invalidParams2("title is required");
5083
5217
  if (!allowed.includes(type)) {
5084
- throw invalidParams("type must be html, svg, markdown, react, or log");
5218
+ throw invalidParams2("type must be html, svg, markdown, react, or log");
5085
5219
  }
5086
- const statusRaw = str(rec.status).trim();
5220
+ const statusRaw = str2(rec.status).trim();
5087
5221
  const status = ["running", "ok", "failed", "idle"].includes(
5088
5222
  statusRaw
5089
5223
  ) ? statusRaw : void 0;
5090
- const modeRaw = str(rec.mode).trim();
5224
+ const modeRaw = str2(rec.mode).trim();
5091
5225
  const mode = modeRaw === "append" || modeRaw === "replace" ? modeRaw : void 0;
5092
5226
  return presentArtifactResult({
5093
5227
  title,
5094
5228
  type,
5095
- artifact_id: str(rec.artifact_id) || void 0,
5229
+ artifact_id: str2(rec.artifact_id) || void 0,
5096
5230
  status,
5097
- phase: str(rec.phase) || void 0,
5231
+ phase: str2(rec.phase) || void 0,
5098
5232
  mode
5099
5233
  });
5100
5234
  }
5101
5235
  async function handleJobWait(params, ctx, waitFn = waitForDetachedJob) {
5102
- const rec = asRecord(params);
5103
- const id = str(rec.id).trim();
5104
- if (!id) throw invalidParams("id is required");
5236
+ const rec = asRecord2(params);
5237
+ const id = str2(rec.id).trim();
5238
+ if (!id) throw invalidParams2("id is required");
5105
5239
  const cwd = jobCwd(rec);
5106
5240
  const deadline = Date.now() + AGENT_RPC_JOB_HOLD_MAX_MS;
5107
5241
  let last;
@@ -5123,14 +5257,14 @@ async function handleJobWait(params, ctx, waitFn = waitForDetachedJob) {
5123
5257
  };
5124
5258
  }
5125
5259
  async function handleJobStop(params, _ctx) {
5126
- const rec = asRecord(params);
5127
- const id = str(rec.id).trim();
5128
- if (!id) throw invalidParams("id is required");
5129
- const reason = str(rec.reason).trim() || void 0;
5260
+ const rec = asRecord2(params);
5261
+ const id = str2(rec.id).trim();
5262
+ if (!id) throw invalidParams2("id is required");
5263
+ const reason = str2(rec.reason).trim() || void 0;
5130
5264
  return stopDetachedJob(jobCwd(rec), id, reason ? { reason } : void 0);
5131
5265
  }
5132
5266
  async function defaultTurnWait(ref, timeoutMs) {
5133
- const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-YNEEEP6O.js");
5267
+ const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-ZQU62CES.js");
5134
5268
  const orch = getOrchestrator2();
5135
5269
  const thread = await orch.waitForTurn(ref, timeoutMs, {
5136
5270
  resolveIfStillRunning: true
@@ -5151,9 +5285,9 @@ async function defaultTurnWait(ref, timeoutMs) {
5151
5285
  });
5152
5286
  }
5153
5287
  async function handleTurnWait(params, ctx, waitFn = defaultTurnWait) {
5154
- const rec = asRecord(params);
5155
- const ref = str(rec.ref).trim();
5156
- if (!ref) throw invalidParams("ref is required");
5288
+ const rec = asRecord2(params);
5289
+ const ref = str2(rec.ref).trim();
5290
+ if (!ref) throw invalidParams2("ref is required");
5157
5291
  const requested = typeof rec.timeoutMs === "number" && Number.isFinite(rec.timeoutMs) ? rec.timeoutMs : void 0;
5158
5292
  const holdMax = requested !== void 0 ? Math.min(Math.max(1e3, Math.floor(requested)), AGENT_RPC_JOB_HOLD_MAX_MS) : AGENT_RPC_JOB_HOLD_MAX_MS;
5159
5293
  const deadline = Date.now() + holdMax;
@@ -5186,7 +5320,8 @@ function createAgentRpcDispatcher() {
5186
5320
  "ui.presentArtifact": handlePresentArtifact,
5187
5321
  "job.wait": handleJobWait,
5188
5322
  "job.stop": handleJobStop,
5189
- "turn.wait": handleTurnWait
5323
+ "turn.wait": handleTurnWait,
5324
+ "permission.ask": handlePermissionAsk
5190
5325
  };
5191
5326
  }
5192
5327
 
@@ -8852,7 +8987,7 @@ var BAKED_REMOTE_RELAY_URL = `wss://relay.sideboard.cloud${REMOTE_RELAY_PATH}`;
8852
8987
  function remoteRelayUrl() {
8853
8988
  return process.env.SIDEBOARD_REMOTE_RELAY_URL?.trim() || BAKED_REMOTE_RELAY_URL;
8854
8989
  }
8855
- function str2(value) {
8990
+ function str3(value) {
8856
8991
  return typeof value === "string" ? value.trim() : "";
8857
8992
  }
8858
8993
  function questions(value) {
@@ -8861,14 +8996,14 @@ function questions(value) {
8861
8996
  for (const item of value) {
8862
8997
  if (!item || typeof item !== "object") return null;
8863
8998
  const row2 = item;
8864
- const question = str2(row2.question);
8999
+ const question = str3(row2.question);
8865
9000
  if (!question || !Array.isArray(row2.options)) return null;
8866
9001
  const options = [];
8867
9002
  for (const opt of row2.options) {
8868
9003
  if (!opt || typeof opt !== "object") return null;
8869
- const label = str2(opt.label);
9004
+ const label = str3(opt.label);
8870
9005
  if (!label) return null;
8871
- const description = str2(opt.description);
9006
+ const description = str3(opt.description);
8872
9007
  options.push(description ? { label, description } : { label });
8873
9008
  }
8874
9009
  if (options.length === 0) return null;
@@ -8889,11 +9024,11 @@ function parseRemoteClientMessage(raw) {
8889
9024
  if (msg.type === "host_pair") return { type: "host_pair" };
8890
9025
  if (msg.type === "stop") return { type: "stop" };
8891
9026
  if (msg.type === "host_register") {
8892
- const deviceId = str2(parsed.deviceId);
8893
- const hostSecret = str2(parsed.hostSecret);
8894
- const deviceLabel = str2(parsed.deviceLabel);
9027
+ const deviceId = str3(parsed.deviceId);
9028
+ const hostSecret = str3(parsed.hostSecret);
9029
+ const deviceLabel = str3(parsed.deviceLabel);
8895
9030
  if (!deviceId || !hostSecret || !deviceLabel) return null;
8896
- const accountToken = str2(parsed.accountToken);
9031
+ const accountToken = str3(parsed.accountToken);
8897
9032
  return {
8898
9033
  type: "host_register",
8899
9034
  deviceId,
@@ -8903,12 +9038,12 @@ function parseRemoteClientMessage(raw) {
8903
9038
  };
8904
9039
  }
8905
9040
  if (msg.type === "pair") {
8906
- const code = str2(parsed.code).toUpperCase();
9041
+ const code = str3(parsed.code).toUpperCase();
8907
9042
  if (!code) return null;
8908
9043
  return { type: "pair", code };
8909
9044
  }
8910
9045
  if (msg.type === "resume") {
8911
- const sessionToken = str2(parsed.sessionToken);
9046
+ const sessionToken = str3(parsed.sessionToken);
8912
9047
  if (!sessionToken) return null;
8913
9048
  return { type: "resume", sessionToken };
8914
9049
  }
@@ -8919,19 +9054,19 @@ function parseRemoteClientMessage(raw) {
8919
9054
  return { type: "list_hosts", sessionTokens };
8920
9055
  }
8921
9056
  if (msg.type === "prompt") {
8922
- const text5 = str2(parsed.text);
9057
+ const text5 = str3(parsed.text);
8923
9058
  if (!text5) return null;
8924
9059
  return { type: "prompt", text: text5 };
8925
9060
  }
8926
9061
  if (msg.type === "assistant") {
8927
- return { type: "assistant", text: str2(parsed.text) };
9062
+ return { type: "assistant", text: str3(parsed.text) };
8928
9063
  }
8929
9064
  if (msg.type === "ask_user") {
8930
9065
  const parsedQuestions = questions(parsed.questions);
8931
9066
  if (!parsedQuestions?.length) return null;
8932
9067
  return {
8933
9068
  type: "ask_user",
8934
- text: str2(parsed.text),
9069
+ text: str3(parsed.text),
8935
9070
  questions: parsedQuestions
8936
9071
  };
8937
9072
  }
@@ -8957,7 +9092,7 @@ function parseRemoteServerMessage(raw) {
8957
9092
  for (const item of raw2) {
8958
9093
  if (!item || typeof item !== "object") return null;
8959
9094
  const row2 = item;
8960
- const sessionToken = str2(row2.sessionToken);
9095
+ const sessionToken = str3(row2.sessionToken);
8961
9096
  if (!sessionToken || typeof row2.deviceId !== "string" || typeof row2.deviceLabel !== "string") return null;
8962
9097
  if (typeof row2.online !== "boolean") return null;
8963
9098
  hosts.push({
@@ -8971,43 +9106,43 @@ function parseRemoteServerMessage(raw) {
8971
9106
  }
8972
9107
  if (type === "stop") return { type: "stop" };
8973
9108
  if (type === "registered") {
8974
- const deviceId = str2(parsed.deviceId);
8975
- const deviceLabel = str2(parsed.deviceLabel);
9109
+ const deviceId = str3(parsed.deviceId);
9110
+ const deviceLabel = str3(parsed.deviceLabel);
8976
9111
  if (!deviceId || !deviceLabel) return null;
8977
9112
  return { type: "registered", deviceId, deviceLabel };
8978
9113
  }
8979
9114
  if (type === "pairing_code") {
8980
- const code = str2(parsed.code);
9115
+ const code = str3(parsed.code);
8981
9116
  const expiresAt = parsed.expiresAt;
8982
9117
  if (!code || typeof expiresAt !== "number") return null;
8983
9118
  return { type: "pairing_code", code, expiresAt };
8984
9119
  }
8985
9120
  if (type === "inbound") {
8986
- const text5 = str2(parsed.text);
9121
+ const text5 = str3(parsed.text);
8987
9122
  if (!text5) return null;
8988
9123
  return { type: "inbound", text: text5 };
8989
9124
  }
8990
9125
  if (type === "paired") {
8991
- const deviceId = str2(parsed.deviceId);
8992
- const deviceLabel = str2(parsed.deviceLabel);
8993
- const sessionToken = str2(parsed.sessionToken);
9126
+ const deviceId = str3(parsed.deviceId);
9127
+ const deviceLabel = str3(parsed.deviceLabel);
9128
+ const sessionToken = str3(parsed.sessionToken);
8994
9129
  if (!deviceId || !deviceLabel || !sessionToken) return null;
8995
9130
  return { type: "paired", deviceId, deviceLabel, sessionToken };
8996
9131
  }
8997
9132
  if (type === "assistant") {
8998
- return { type: "assistant", text: str2(parsed.text) };
9133
+ return { type: "assistant", text: str3(parsed.text) };
8999
9134
  }
9000
9135
  if (type === "ask_user") {
9001
9136
  const parsedQuestions = questions(parsed.questions);
9002
9137
  if (!parsedQuestions?.length) return null;
9003
9138
  return {
9004
9139
  type: "ask_user",
9005
- text: str2(parsed.text),
9140
+ text: str3(parsed.text),
9006
9141
  questions: parsedQuestions
9007
9142
  };
9008
9143
  }
9009
9144
  if (type === "error") {
9010
- const message = str2(parsed.message) || "remote error";
9145
+ const message = str3(parsed.message) || "remote error";
9011
9146
  return { type: "error", message };
9012
9147
  }
9013
9148
  return null;
@@ -9435,10 +9570,10 @@ var MIN_MD_CHARS = 120;
9435
9570
  function oneLine(value, max) {
9436
9571
  return value.replace(/[`\r\n]/g, " ").replace(/\s+/g, " ").trim().slice(0, max);
9437
9572
  }
9438
- function asRecord2(value) {
9573
+ function asRecord3(value) {
9439
9574
  if (typeof value === "string") {
9440
9575
  try {
9441
- return asRecord2(JSON.parse(value));
9576
+ return asRecord3(JSON.parse(value));
9442
9577
  } catch {
9443
9578
  return void 0;
9444
9579
  }
@@ -9446,7 +9581,7 @@ function asRecord2(value) {
9446
9581
  if (value && typeof value === "object" && !Array.isArray(value)) return value;
9447
9582
  return void 0;
9448
9583
  }
9449
- function str3(value) {
9584
+ function str4(value) {
9450
9585
  return typeof value === "string" ? value : "";
9451
9586
  }
9452
9587
  function normalizedBody(content) {
@@ -9513,7 +9648,7 @@ function toolContent(input) {
9513
9648
  if (!input) return "";
9514
9649
  const direct = input.content;
9515
9650
  if (typeof direct === "string") return direct;
9516
- const nested = asRecord2(direct);
9651
+ const nested = asRecord3(direct);
9517
9652
  if (!nested) return "";
9518
9653
  for (const key of ["html", "markup", "text", "source", "content", "code"]) {
9519
9654
  if (typeof nested[key] === "string") return nested[key];
@@ -9536,13 +9671,13 @@ function phoneArtifactText(text5, parts, limit = Number.POSITIVE_INFINITY) {
9536
9671
  const extras = [];
9537
9672
  for (const part of parts ?? []) {
9538
9673
  if (part.type !== "tool" || part.parentId || !isArtifactTool(part.name)) continue;
9539
- const input = asRecord2(part.input);
9674
+ const input = asRecord3(part.input);
9540
9675
  const content = toolContent(input);
9541
9676
  const hint = artifactHint(content);
9542
9677
  if (hint && hints.has(hint)) continue;
9543
9678
  const kind = kindFor("", content);
9544
- const title = oneLine(str3(input?.title), 80) || (kind ? titleFor(kind, content) : "Artifact");
9545
- if (!str3(input?.title) && content.trim().length < MIN_FENCE_CHARS) continue;
9679
+ const title = oneLine(str4(input?.title), 80) || (kind ? titleFor(kind, content) : "Artifact");
9680
+ if (!str4(input?.title) && content.trim().length < MIN_FENCE_CHARS) continue;
9546
9681
  if (hint) hints.add(hint);
9547
9682
  extras.push(marker(title, hint));
9548
9683
  }
@@ -9555,11 +9690,11 @@ function phoneArtifactText(text5, parts, limit = Number.POSITIVE_INFINITY) {
9555
9690
  ${tail}` : tail;
9556
9691
  }
9557
9692
  function parseOpenArtifact(value, chatId) {
9558
- const record = asRecord2(value);
9693
+ const record = asRecord3(value);
9559
9694
  if (!record) return "invalid";
9560
- const title = oneLine(str3(record.title), 200);
9695
+ const title = oneLine(str4(record.title), 200);
9561
9696
  if (!title) return "invalid";
9562
- const hint = oneLine(str3(record.hint), 200);
9697
+ const hint = oneLine(str4(record.hint), 200);
9563
9698
  return { op: "open-artifact", chatId, title, ...hint ? { hint } : {} };
9564
9699
  }
9565
9700
  var PHONE_ARTIFACT_MAX_CHARS = 18e4;
@@ -9578,13 +9713,13 @@ function phoneArtifactDocuments(sources) {
9578
9713
  }
9579
9714
  for (const part of source.parts ?? []) {
9580
9715
  if (part.type !== "tool" || part.parentId || !isArtifactTool(part.name)) continue;
9581
- const input = asRecord2(part.input);
9716
+ const input = asRecord3(part.input);
9582
9717
  const content = toolContent(input);
9583
9718
  const hint = artifactHint(content);
9584
9719
  if (hint && hints.has(hint)) continue;
9585
- const typed = kindFor(str3(input?.type), content) ?? kindFor("", content);
9586
- const title = oneLine(str3(input?.title), 80) || (typed ? titleFor(typed, content) : "Artifact");
9587
- if (!str3(input?.title) && content.trim().length < MIN_FENCE_CHARS) continue;
9720
+ const typed = kindFor(str4(input?.type), content) ?? kindFor("", content);
9721
+ const title = oneLine(str4(input?.title), 80) || (typed ? titleFor(typed, content) : "Artifact");
9722
+ if (!str4(input?.title) && content.trim().length < MIN_FENCE_CHARS) continue;
9588
9723
  if (hint) hints.add(hint);
9589
9724
  docs.push({ title, content, kind: typed ?? "text" });
9590
9725
  }
@@ -9629,13 +9764,21 @@ function pickArtifactMatch(items, request) {
9629
9764
  }
9630
9765
 
9631
9766
  // src/remote/phone-panes.ts
9767
+ var PHONE_CHANGE_SCOPES = ["last_turn", "uncommitted", "staged", "unstaged", "commits"];
9768
+ var PHONE_CHANGE_SCOPE_DEFAULT = "commits";
9769
+ function phoneChangeScope(value) {
9770
+ return typeof value === "string" && PHONE_CHANGE_SCOPES.includes(value) ? value : void 0;
9771
+ }
9772
+ function phoneCommitSha(value) {
9773
+ return typeof value === "string" && /^[0-9a-f]{4,40}$/i.test(value) ? value : void 0;
9774
+ }
9632
9775
  var MAX_ROWS = 200;
9633
9776
  var MAX_QUERY = 200;
9634
9777
  function parsePhonePane(value, chatId) {
9635
9778
  const pane = value && typeof value === "object" ? value.pane : void 0;
9636
9779
  if (pane !== "files" && pane !== "changes" && pane !== "checks") return "invalid";
9637
- const scopeRaw = value.scope;
9638
- const scope = scopeRaw === "uncommitted" || scopeRaw === "commits" ? scopeRaw : void 0;
9780
+ const scope = phoneChangeScope(value.scope);
9781
+ const commitSha = phoneCommitSha(value.commitSha);
9639
9782
  const queryRaw = value.query;
9640
9783
  const query = typeof queryRaw === "string" ? queryRaw.trim().slice(0, MAX_QUERY) : "";
9641
9784
  return {
@@ -9643,6 +9786,7 @@ function parsePhonePane(value, chatId) {
9643
9786
  chatId,
9644
9787
  pane,
9645
9788
  ...scope && pane === "changes" ? { scope } : {},
9789
+ ...commitSha && pane === "changes" && scope === "commits" ? { commitSha } : {},
9646
9790
  ...query && pane === "files" ? { query } : {}
9647
9791
  };
9648
9792
  }
@@ -9651,10 +9795,10 @@ function filterPhoneFiles(paths, query) {
9651
9795
  if (!q) return paths;
9652
9796
  return paths.filter((path2) => path2.toLowerCase().includes(q));
9653
9797
  }
9654
- async function readPhonePane(thread, pane, scope, query) {
9798
+ async function readPhonePane(thread, pane, scope, query, commitSha) {
9655
9799
  try {
9656
9800
  if (pane === "files") return await readFiles(thread.id, query);
9657
- if (pane === "changes") return await readChanges(thread.id, scope ?? "commits");
9801
+ if (pane === "changes") return await readChanges(thread.id, scope ?? PHONE_CHANGE_SCOPE_DEFAULT, commitSha);
9658
9802
  return await readChecks(thread.id);
9659
9803
  } catch (err) {
9660
9804
  const message = err instanceof Error ? err.message : String(err);
@@ -9671,9 +9815,14 @@ async function readFiles(threadId, query) {
9671
9815
  ...note ? { note } : {}
9672
9816
  };
9673
9817
  }
9674
- async function readChanges(threadId, scope) {
9675
- const diff = await getOrchestrator().diff(threadId, { includePatches: false, scope });
9818
+ async function readChanges(threadId, scope, commitSha) {
9819
+ const diff = await getOrchestrator().diff(threadId, {
9820
+ includePatches: false,
9821
+ scope,
9822
+ commitSha: scope === "commits" ? commitSha ?? null : null
9823
+ });
9676
9824
  const shown = diff.files.slice(0, MAX_ROWS);
9825
+ const selected = diff.commits.find((commit) => commit.sha === commitSha || commit.shortSha === commitSha);
9677
9826
  return {
9678
9827
  rows: shown.map((file) => {
9679
9828
  const detail = changeDetail(file.additions, file.deletions);
@@ -9684,7 +9833,13 @@ async function readChanges(threadId, scope) {
9684
9833
  ...detail ? { detail } : {}
9685
9834
  };
9686
9835
  }),
9687
- ...diff.files.length > shown.length ? { note: `${diff.files.length - shown.length} more changed files` } : {}
9836
+ ...diff.files.length > shown.length ? { note: `${diff.files.length - shown.length} more changed files` } : {},
9837
+ scope: diff.scope,
9838
+ commitSha: selected?.sha ?? null,
9839
+ ...diff.base ? { base: diff.base } : {},
9840
+ hasLastTurnBase: diff.hasLastTurnBase,
9841
+ commits: diff.commits.slice(0, 40),
9842
+ scopeStats: diff.scopeStats
9688
9843
  };
9689
9844
  }
9690
9845
  function changeDetail(additions, deletions) {
@@ -9693,13 +9848,14 @@ function changeDetail(additions, deletions) {
9693
9848
  if (add === 0 && del === 0) return void 0;
9694
9849
  return `+${add} \u2212${del}`;
9695
9850
  }
9696
- async function readPhoneChangePatch(threadId, path2) {
9851
+ async function readPhoneChangePatch(threadId, path2, scope = PHONE_CHANGE_SCOPE_DEFAULT, commitSha) {
9697
9852
  const diff = await getOrchestrator().diff(threadId, {
9698
9853
  path: path2,
9699
- scope: "commits",
9854
+ scope,
9855
+ commitSha: scope === "commits" ? commitSha ?? null : null,
9700
9856
  includePatches: true,
9701
9857
  includeMeta: false,
9702
- includeUntracked: false
9858
+ includeUntracked: scope === "uncommitted" || scope === "unstaged" || scope === "last_turn" || scope === "commits" && !commitSha
9703
9859
  });
9704
9860
  const file = diff.files.find((row2) => row2.path === path2) ?? diff.files[0];
9705
9861
  if (!file) return null;
@@ -10044,7 +10200,7 @@ function listPhoneHistory(opts) {
10044
10200
  }
10045
10201
 
10046
10202
  // src/remote/phone-chats.ts
10047
- var PHONE_CONTROL_PREFIX = "\0sb.phone\n";
10203
+ var PHONE_CONTROL_PREFIX2 = "\0sb.phone\n";
10048
10204
  var PREVIEW_LIMIT2 = 90;
10049
10205
  var AGENT_KINDS = /* @__PURE__ */ new Set(["claude", "codex", "opencode", "brightsy", "cursor"]);
10050
10206
  var MAX_PHONE_FILES = 8;
@@ -10052,13 +10208,13 @@ var MAX_PHONE_FILE_CHARS = Math.ceil(8 * 1024 * 1024 * 4 / 3);
10052
10208
  var MAX_PHONE_FILES_CHARS = Math.ceil(20 * 1024 * 1024 * 4 / 3);
10053
10209
  var MAX_DICTATION_CHARS = Math.ceil(12 * 1024 * 1024 * 4 / 3);
10054
10210
  function encodePhoneControl(payload) {
10055
- return PHONE_CONTROL_PREFIX + JSON.stringify(payload);
10211
+ return PHONE_CONTROL_PREFIX2 + JSON.stringify(payload);
10056
10212
  }
10057
10213
  function takePhoneControl(text5) {
10058
- if (!text5.startsWith(PHONE_CONTROL_PREFIX)) return null;
10214
+ if (!text5.startsWith(PHONE_CONTROL_PREFIX2)) return null;
10059
10215
  let parsed;
10060
10216
  try {
10061
- parsed = JSON.parse(text5.slice(PHONE_CONTROL_PREFIX.length));
10217
+ parsed = JSON.parse(text5.slice(PHONE_CONTROL_PREFIX2.length));
10062
10218
  } catch {
10063
10219
  return "invalid";
10064
10220
  }
@@ -10069,14 +10225,14 @@ function parsePhoneControl(value) {
10069
10225
  const op = value.op;
10070
10226
  if (op === "list") return { op: "list" };
10071
10227
  if (op === "history") {
10072
- const query = textField(value, "query");
10073
- const after = textField(value, "after");
10228
+ const query = textField2(value, "query");
10229
+ const after = textField2(value, "after");
10074
10230
  return { op: "history", ...query ? { query } : {}, ...after ? { after } : {} };
10075
10231
  }
10076
10232
  if (op === "sources") {
10077
- const repoPath = textField(value, "repoPath");
10233
+ const repoPath = textField2(value, "repoPath");
10078
10234
  if (!repoPath || !isProjectPath2(repoPath)) return "invalid";
10079
- const query = textField(value, "query");
10235
+ const query = textField2(value, "query");
10080
10236
  return { op: "sources", repoPath, ...query ? { query } : {} };
10081
10237
  }
10082
10238
  if (op === "models") {
@@ -10085,7 +10241,7 @@ function parsePhoneControl(value) {
10085
10241
  return { op: "models", agent };
10086
10242
  }
10087
10243
  if (op === "dictate") {
10088
- const id = textField(value, "id");
10244
+ const id = textField2(value, "id");
10089
10245
  const audioBase64 = base64Field(value, "audioBase64");
10090
10246
  if (!id || !audioBase64 || audioBase64.length > MAX_DICTATION_CHARS) return "invalid";
10091
10247
  return { op: "dictate", id, audioBase64 };
@@ -10097,26 +10253,30 @@ function parsePhoneControl(value) {
10097
10253
  return { op, chatId };
10098
10254
  }
10099
10255
  if (op === "git") {
10100
- const action = textField(value, "action");
10256
+ const action = textField2(value, "action");
10101
10257
  if (!PHONE_HEADER_GIT_ACTIONS.includes(action)) return "invalid";
10102
10258
  return { op: "git", chatId, action };
10103
10259
  }
10104
10260
  if (op === "pane") return parsePhonePane(value, chatId);
10105
10261
  if (op === "open-artifact") return parseOpenArtifact(value, chatId);
10106
10262
  if (op === "open-file" || op === "media") {
10107
- const path2 = textField(value, "path");
10263
+ const path2 = textField2(value, "path");
10108
10264
  if (!path2 || path2.length > 2e3) return "invalid";
10109
10265
  if (op === "media") return { op, chatId, path: path2 };
10110
10266
  const startLine = lineField(value, "startLine");
10111
10267
  const endLine = lineField(value, "endLine");
10112
- const view = textField(value, "view") === "diff" ? "diff" : void 0;
10268
+ const view = textField2(value, "view") === "diff" ? "diff" : void 0;
10269
+ const scope = phoneChangeScope(value.scope);
10270
+ const commitSha = phoneCommitSha(value.commitSha);
10113
10271
  return {
10114
10272
  op,
10115
10273
  chatId,
10116
10274
  path: path2,
10117
10275
  ...startLine != null ? { startLine } : {},
10118
10276
  ...endLine != null ? { endLine } : {},
10119
- ...view ? { view } : {}
10277
+ ...view ? { view } : {},
10278
+ ...view && scope ? { scope } : {},
10279
+ ...view && scope === "commits" && commitSha ? { commitSha } : {}
10120
10280
  };
10121
10281
  }
10122
10282
  const draft = parsePhoneDraft(value);
@@ -10130,7 +10290,7 @@ function parsePhoneControl(value) {
10130
10290
  }
10131
10291
  return "invalid";
10132
10292
  }
10133
- function textField(value, key) {
10293
+ function textField2(value, key) {
10134
10294
  const raw = value[key];
10135
10295
  return typeof raw === "string" ? raw.trim() : "";
10136
10296
  }
@@ -10140,7 +10300,7 @@ function lineField(value, key) {
10140
10300
  return raw;
10141
10301
  }
10142
10302
  function agentField(value) {
10143
- const agent = textField(value, "agent");
10303
+ const agent = textField2(value, "agent");
10144
10304
  if (!agent) return void 0;
10145
10305
  return AGENT_KINDS.has(agent) ? agent : void 0;
10146
10306
  }
@@ -10151,7 +10311,7 @@ function base64Field(value, key) {
10151
10311
  }
10152
10312
  function parsePhoneDraft(value) {
10153
10313
  const draft = {};
10154
- if (textField(value, "agent")) {
10314
+ if (textField2(value, "agent")) {
10155
10315
  const agent = agentField(value);
10156
10316
  if (!agent) return "invalid";
10157
10317
  draft.agent = agent;
@@ -10191,7 +10351,7 @@ function parsePhoneDraft(value) {
10191
10351
  const refs = parsePhoneCodeRefs(value.refs);
10192
10352
  if (refs === "invalid") return "invalid";
10193
10353
  if (refs.length) draft.refs = refs;
10194
- const prompt = textField(value, "prompt");
10354
+ const prompt = textField2(value, "prompt");
10195
10355
  if (prompt) draft.prompt = prompt.slice(0, 2e4);
10196
10356
  return draft;
10197
10357
  }
@@ -10202,7 +10362,7 @@ function parsePhoneFiles(value) {
10202
10362
  let total = 0;
10203
10363
  for (const item of value) {
10204
10364
  if (!item || typeof item !== "object") return "invalid";
10205
- const name = (textField(item, "name").replace(/[/\\]/g, "_") || "file").slice(0, 180);
10365
+ const name = (textField2(item, "name").replace(/[/\\]/g, "_") || "file").slice(0, 180);
10206
10366
  const dataBase64 = base64Field(item, "dataBase64");
10207
10367
  if (!dataBase64 || !/^[A-Za-z0-9+/=]+$/.test(dataBase64)) return "invalid";
10208
10368
  if (dataBase64.length > MAX_PHONE_FILE_CHARS) return "invalid";
@@ -10218,10 +10378,10 @@ function parsePhoneLinks(value) {
10218
10378
  const links = [];
10219
10379
  for (const item of value) {
10220
10380
  if (!item || typeof item !== "object") return "invalid";
10221
- const ref = textField(item, "ref").slice(0, 80);
10381
+ const ref = textField2(item, "ref").slice(0, 80);
10222
10382
  if (!ref) return "invalid";
10223
- const title = (textField(item, "title") || ref).slice(0, 200);
10224
- const url = textField(item, "url").slice(0, 500);
10383
+ const title = (textField2(item, "title") || ref).slice(0, 200);
10384
+ const url = textField2(item, "url").slice(0, 500);
10225
10385
  links.push({ ref, title, ...url ? { url } : {} });
10226
10386
  }
10227
10387
  return links;
@@ -10229,7 +10389,7 @@ function parsePhoneLinks(value) {
10229
10389
  function parsePhoneCreate(value) {
10230
10390
  const draft = parsePhoneDraft(value);
10231
10391
  if (draft === "invalid") return "invalid";
10232
- const goal = textField(value, "goal") || draft.prompt || "";
10392
+ const goal = textField2(value, "goal") || draft.prompt || "";
10233
10393
  const where = value.where;
10234
10394
  if (where == null || where === "orchestration") {
10235
10395
  return {
@@ -10240,20 +10400,20 @@ function parsePhoneCreate(value) {
10240
10400
  };
10241
10401
  }
10242
10402
  if (where === "worktree") {
10243
- const chatId = textField(value, "chatId");
10403
+ const chatId = textField2(value, "chatId");
10244
10404
  if (!chatId) return "invalid";
10245
10405
  return { op: "create", where: "worktree", chatId, ...draft };
10246
10406
  }
10247
10407
  if (where === "project") {
10248
- const repoPath = textField(value, "repoPath");
10408
+ const repoPath = textField2(value, "repoPath");
10249
10409
  if (!repoPath || !isProjectPath2(repoPath)) return "invalid";
10250
- const sourceRaw = textField(value, "sourceType");
10410
+ const sourceRaw = textField2(value, "sourceType");
10251
10411
  let sourceType;
10252
10412
  if (sourceRaw === "branch" || sourceRaw === "pr" || sourceRaw === "ticket") sourceType = sourceRaw;
10253
10413
  else if (sourceRaw) return "invalid";
10254
- const sourceRef = textField(value, "sourceRef");
10414
+ const sourceRef = textField2(value, "sourceRef");
10255
10415
  if ((sourceType === "pr" || sourceType === "ticket") && !sourceRef) return "invalid";
10256
- const title = textField(value, "title").slice(0, 200);
10416
+ const title = textField2(value, "title").slice(0, 200);
10257
10417
  const cowboy = value.cowboy === true;
10258
10418
  return {
10259
10419
  op: "create",
@@ -10522,14 +10682,22 @@ function dispatchPhoneWorkspace(cmd, emit, publishOpened) {
10522
10682
  emit({ op: "error", chatId: cmd.chatId, message: "That agent is not on this Mac." });
10523
10683
  return;
10524
10684
  }
10525
- void readPhonePane(thread, cmd.pane, cmd.scope, cmd.query).then((body) => {
10685
+ void readPhonePane(thread, cmd.pane, cmd.scope, cmd.query, cmd.commitSha).then((body) => {
10526
10686
  emit({
10527
10687
  op: "pane",
10528
10688
  chatId: cmd.chatId,
10529
10689
  pane: cmd.pane,
10530
10690
  rows: body.rows,
10531
10691
  ...body.note ? { note: body.note } : {},
10532
- ...cmd.pane === "files" ? { query: cmd.query ?? "" } : {}
10692
+ ...cmd.pane === "files" ? { query: cmd.query ?? "" } : {},
10693
+ ...cmd.pane === "changes" ? {
10694
+ scope: body.scope,
10695
+ commitSha: body.commitSha ?? null,
10696
+ ...body.base ? { base: body.base } : {},
10697
+ hasLastTurnBase: body.hasLastTurnBase,
10698
+ commits: body.commits ?? [],
10699
+ ...body.scopeStats ? { scopeStats: body.scopeStats } : {}
10700
+ } : {}
10533
10701
  });
10534
10702
  });
10535
10703
  return;
@@ -10898,7 +11066,7 @@ function beginCoordinatorPrompt(body, opts) {
10898
11066
  // src/remote/listen.ts
10899
11067
  function emitOpened(opts, opened) {
10900
11068
  setPhoneOpenChat(opened.chat.id);
10901
- emitControl(opts, { op: "opened", ...opened });
11069
+ emitControl(opts, { op: "opened", ...opened, permission: phonePermissionView(opened.chat.id) });
10902
11070
  }
10903
11071
  function publishOpenedChat(opts, chatId) {
10904
11072
  const opened = openPhoneChat(chatId);
@@ -11126,7 +11294,7 @@ function handlePhoneControl(cmd, opts) {
11126
11294
  return;
11127
11295
  }
11128
11296
  if (cmd.view === "diff") {
11129
- void readPhoneChangePatch(thread.id, relative).then((body) => {
11297
+ void readPhoneChangePatch(thread.id, relative, cmd.scope, cmd.commitSha).then((body) => {
11130
11298
  if (!body) {
11131
11299
  emitControl(opts, { op: "error", chatId: cmd.chatId, message: "That file has no changes to show." });
11132
11300
  return;
@@ -11280,6 +11448,7 @@ function runPhonePrompt(opts, cmd, chatId, generation) {
11280
11448
  }
11281
11449
  function handleRemoteInbound(text5, opts) {
11282
11450
  const body = text5.trim();
11451
+ if (dispatchPhonePermission(body, (reply) => emitControl(opts, reply))) return;
11283
11452
  const control = takePhoneControl(body);
11284
11453
  if (control) {
11285
11454
  if (control === "invalid") {
@@ -11354,7 +11523,7 @@ function startPhoneBoardSync(opts) {
11354
11523
  let timer = null;
11355
11524
  let lastKey = "";
11356
11525
  let stopped = false;
11357
- const push = () => {
11526
+ const push2 = () => {
11358
11527
  timer = null;
11359
11528
  if (stopped) return;
11360
11529
  let sidebar;
@@ -11371,7 +11540,7 @@ function startPhoneBoardSync(opts) {
11371
11540
  const schedule = () => {
11372
11541
  if (stopped) return;
11373
11542
  if (timer) clearTimeout(timer);
11374
- timer = setTimeout(push, debounceMs);
11543
+ timer = setTimeout(push2, debounceMs);
11375
11544
  };
11376
11545
  const handle = (event) => {
11377
11546
  if (isPhoneBoardEvent(event)) schedule();
@@ -11381,13 +11550,13 @@ function startPhoneBoardSync(opts) {
11381
11550
  };
11382
11551
  const unsubscribe = (opts.subscribe ?? subscribePhoneBoard)(schedule);
11383
11552
  const offEvents = (opts.events ?? ((listener) => getOrchestrator().on(listener)))(handle);
11384
- push();
11553
+ push2();
11385
11554
  let loginTimer = null;
11386
11555
  const refreshViewerLogin = opts.refreshViewerLogin;
11387
11556
  if (refreshViewerLogin) {
11388
11557
  const afterLogin = () => {
11389
11558
  void refreshViewerLogin().then(() => {
11390
- if (!stopped) push();
11559
+ if (!stopped) push2();
11391
11560
  });
11392
11561
  };
11393
11562
  afterLogin();
@@ -11624,6 +11793,10 @@ function runRemoteHost(opts) {
11624
11793
  }
11625
11794
  send3({ type: "ask_user", text: outbound.text, questions: outbound.questions });
11626
11795
  };
11796
+ setPhonePermissionPush((chatId, prompt) => {
11797
+ if (getPhoneOpenChat() !== chatId) return;
11798
+ emitControl({ onOutbound: emit }, { op: "permission", chatId, prompt });
11799
+ });
11627
11800
  void loop();
11628
11801
  return {
11629
11802
  requestPairingCode() {
@@ -13243,7 +13416,7 @@ async function continueSourceThread(threadId, prompt) {
13243
13416
  await continueOnReply(threadId, prompt);
13244
13417
  return;
13245
13418
  }
13246
- const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-YNEEEP6O.js");
13419
+ const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-ZQU62CES.js");
13247
13420
  await getOrchestrator2().send(threadId, prompt);
13248
13421
  } catch {
13249
13422
  }
@@ -13685,6 +13858,7 @@ export {
13685
13858
  allocateTeamName,
13686
13859
  allocateTeamSlug,
13687
13860
  annotateDevAccessRuns,
13861
+ answerToolPermission,
13688
13862
  appDataDir,
13689
13863
  appSettingsPath,
13690
13864
  appendCompactedMessages,