@p4code/cli 0.6.14 → 0.6.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bin.mjs CHANGED
@@ -268,7 +268,7 @@ const make$124 = () => {
268
268
  const layer$124 = Layer.sync(NetService, make$124);
269
269
  //#endregion
270
270
  //#region package.json
271
- var version$1 = "0.6.14";
271
+ var version$1 = "0.6.16";
272
272
  //#endregion
273
273
  //#region src/config.ts
274
274
  /**
@@ -9888,7 +9888,13 @@ const DeviceSession = Schema$1.Struct({
9888
9888
  platform: DevicePlatform,
9889
9889
  openedAt: Schema$1.String,
9890
9890
  /** Opaque lifetime identity; absent only on servers predating guarded close. */
9891
- leaseId: Schema$1.optional(DeviceId)
9891
+ leaseId: Schema$1.optional(DeviceId),
9892
+ /**
9893
+ * Server-resolved folder of the agent's thread. Apple-backed control opens the
9894
+ * Xcode project found here, because Xcode asks the user to approve an agent
9895
+ * only once it opens a project.
9896
+ */
9897
+ workspaceRoot: Schema$1.optional(Schema$1.String)
9892
9898
  });
9893
9899
  const DeviceServiceState = Schema$1.Struct({
9894
9900
  hosts: Schema$1.Array(DeviceHostSummary),
@@ -10165,7 +10171,8 @@ var DeviceOperationError = class extends Schema$1.TaggedErrorClass()("DeviceOper
10165
10171
  "request_failed",
10166
10172
  "invalid_payload",
10167
10173
  "settings_failed",
10168
- "hub_rejected"
10174
+ "hub_rejected",
10175
+ "agent_access_disabled"
10169
10176
  ]),
10170
10177
  exitCode: Schema$1.optional(Schema$1.Number),
10171
10178
  cause: Schema$1.Defect()
@@ -10176,7 +10183,8 @@ var DeviceOperationError = class extends Schema$1.TaggedErrorClass()("DeviceOper
10176
10183
  request_failed: "Could not communicate with device support. Try refreshing devices.",
10177
10184
  invalid_payload: "The device request could not be encoded.",
10178
10185
  settings_failed: "Could not read or save device settings.",
10179
- hub_rejected: "The device hub could not complete the request."
10186
+ hub_rejected: "The device hub could not complete the request.",
10187
+ agent_access_disabled: "Agent device access is off. Turn on Settings > Devices > Allow agents to control devices."
10180
10188
  }[this.reason];
10181
10189
  return `Device ${this.operation} failed: ${explanation}`;
10182
10190
  }
@@ -11332,6 +11340,12 @@ const ServerSettings = Schema$1.Struct({
11332
11340
  */
11333
11341
  enableUnpromptedSubagents: Schema$1.Boolean.pipe(Schema$1.withDecodingDefault(Effect.succeed(true))),
11334
11342
  /**
11343
+ * Compression mode new threads start with. The composer writes the user's
11344
+ * last pick here, so the next new thread follows it; each thread keeps its
11345
+ * own mode once created.
11346
+ */
11347
+ defaultCompressMode: CompressMode.pipe(Schema$1.withDecodingDefault(Effect.succeed(DEFAULT_COMPRESS_MODE))),
11348
+ /**
11335
11349
  * Beta. Whether Fusion role rules travel once through the provider's
11336
11350
  * session channel (Claude's system prompt, Codex's developer instructions)
11337
11351
  * with a one-line reference on each message, or the legacy way: the full
@@ -11635,6 +11649,7 @@ const ServerSettingsPatch = Schema$1.Struct({
11635
11649
  enableVerificationBeforeCompletion: Schema$1.optionalKey(Schema$1.Boolean),
11636
11650
  enableRootCauseBeforeFix: Schema$1.optionalKey(Schema$1.Boolean),
11637
11651
  enableUnpromptedSubagents: Schema$1.optionalKey(Schema$1.Boolean),
11652
+ defaultCompressMode: Schema$1.optionalKey(CompressMode),
11638
11653
  enableOptimizedFusionPromptDelivery: Schema$1.optionalKey(Schema$1.Boolean),
11639
11654
  enableFusionObservation: Schema$1.optionalKey(Schema$1.Boolean),
11640
11655
  storageCleanup: Schema$1.optionalKey(Schema$1.Struct({
@@ -49538,13 +49553,21 @@ var XcodeMcpClient = class XcodeMcpClient {
49538
49553
  throw error;
49539
49554
  }
49540
49555
  }
49541
- request(method, params, signal) {
49556
+ /**
49557
+ * `abandonAfterMs` gives up on a slow reply without closing the bridge; the
49558
+ * default timeout closes it, since a stuck bridge cannot be trusted.
49559
+ */
49560
+ request(method, params, signal, abandonAfterMs) {
49542
49561
  signal.throwIfAborted();
49543
49562
  if (this.closed) return Promise.reject(/* @__PURE__ */ new Error("Device MCP session is closed. Select the device again."));
49544
49563
  const id = ++this.sequence;
49545
49564
  return new Promise((resolve, reject) => {
49546
49565
  const abort = () => this.close(/* @__PURE__ */ new Error("Device MCP request cancelled. Select the device again."));
49547
- const timer = setTimeout(() => this.close(/* @__PURE__ */ new Error("Device MCP timed out. Select the device again.")), REQUEST_TIMEOUT_MS$5);
49566
+ const timer = abandonAfterMs === void 0 ? setTimeout(() => this.close(/* @__PURE__ */ new Error("Device MCP timed out. Select the device again.")), REQUEST_TIMEOUT_MS$5) : setTimeout(() => {
49567
+ this.pending.delete(id);
49568
+ finish();
49569
+ reject(/* @__PURE__ */ new Error(`Device MCP ${method} did not answer in time.`));
49570
+ }, abandonAfterMs);
49548
49571
  const finish = () => {
49549
49572
  clearTimeout(timer);
49550
49573
  signal.removeEventListener("abort", abort);
@@ -49568,7 +49591,7 @@ var XcodeMcpClient = class XcodeMcpClient {
49568
49591
  }) + "\n");
49569
49592
  });
49570
49593
  }
49571
- async call(name, args, signal) {
49594
+ async call(name, args, signal, options) {
49572
49595
  if (!this.tools.has(name)) throw new Error(`Selected device MCP service does not expose ${name}. Check adapter capabilities and Xcode setup.`);
49573
49596
  const redact = (error) => {
49574
49597
  let message = error instanceof Error ? error.message : String(error);
@@ -49579,7 +49602,7 @@ var XcodeMcpClient = class XcodeMcpClient {
49579
49602
  const result = objectValue(await this.request("tools/call", {
49580
49603
  name,
49581
49604
  arguments: args
49582
- }, signal));
49605
+ }, signal, options?.abandonAfterMs));
49583
49606
  const structured = objectValue(result.structuredContent);
49584
49607
  if (result.isError === true || structured.didError === true) {
49585
49608
  const detail = (Array.isArray(result.content) ? result.content : []).map((part) => objectValue(part).text).filter((part) => typeof part === "string").join("\n");
@@ -49781,6 +49804,15 @@ const REQUIRED_DEVICE_TOOLS = [
49781
49804
  "DeviceInteractionSynthesize",
49782
49805
  "DeviceInteractionEndSession"
49783
49806
  ];
49807
+ const XCODE_PROJECT_SEARCH_DEPTH = 3;
49808
+ const DISCOVERED_PROJECT_OPEN_WAIT_MS = 15e3;
49809
+ const XCODE_PROJECT_SKIPPED_DIRECTORIES = /* @__PURE__ */ new Set([
49810
+ "node_modules",
49811
+ "Pods",
49812
+ "DerivedData",
49813
+ "build",
49814
+ "Carthage"
49815
+ ]);
49784
49816
  const FRAME_PATTERN = /\{\{(-?[\d.]+),\s*(-?[\d.]+)\},\s*\{([\d.]+),\s*([\d.]+)\}\}/;
49785
49817
  const HIT_PATTERN = /hitPoint:\s*\{(-?[\d.]+),\s*(-?[\d.]+)\}/;
49786
49818
  const Capture = Schema$1.Struct({
@@ -49848,6 +49880,33 @@ function appleHierarchySnapshot(text, sequence) {
49848
49880
  elements
49849
49881
  };
49850
49882
  }
49883
+ /**
49884
+ * Shallowest `.xcworkspace` under a thread's folder, else the shallowest
49885
+ * `.xcodeproj`. Bundles, dot folders and dependency trees are not searched.
49886
+ */
49887
+ const findXcodeProject = (fs, root) => Effect.gen(function* () {
49888
+ let level = [root];
49889
+ for (let depth = 0; depth <= XCODE_PROJECT_SEARCH_DEPTH && level.length > 0; depth++) {
49890
+ const next = [];
49891
+ let project;
49892
+ for (const directory of level) {
49893
+ const names = yield* fs.readDirectory(directory).pipe(Effect.orElseSucceed(() => []));
49894
+ for (const name of names.toSorted()) {
49895
+ const path = `${directory}/${name}`;
49896
+ if (name.endsWith(".xcworkspace")) return path;
49897
+ if (name.endsWith(".xcodeproj")) {
49898
+ project ??= path;
49899
+ continue;
49900
+ }
49901
+ if (name.startsWith(".") || XCODE_PROJECT_SKIPPED_DIRECTORIES.has(name)) continue;
49902
+ const info = yield* fs.stat(path).pipe(Effect.option);
49903
+ if (Option.isSome(info) && info.value.type === "Directory") next.push(path);
49904
+ }
49905
+ }
49906
+ if (project) return project;
49907
+ level = next;
49908
+ }
49909
+ });
49851
49910
  function createAppleDeviceAdapter(input) {
49852
49911
  const sessions = /* @__PURE__ */ new Map();
49853
49912
  const fail = (operation) => (cause) => new DeviceOperationError({
@@ -49870,7 +49929,9 @@ function createAppleDeviceAdapter(input) {
49870
49929
  }
49871
49930
  }).catch(() => {});
49872
49931
  };
49873
- const get = (session) => Effect.tryPromise({
49932
+ const workspaceFor = (session) => input.workspacePath !== void 0 || !session.workspaceRoot || session.leaseId !== void 0 && sessions.has(session.leaseId) ? Effect.succeed(input.workspacePath) : findXcodeProject(input.fs, session.workspaceRoot);
49933
+ const get = (session) => Effect.flatMap(workspaceFor(session), (workspace) => open(session, workspace));
49934
+ const open = (session, workspace) => Effect.tryPromise({
49874
49935
  try: async (signal) => {
49875
49936
  if (session.hostId !== "local" || session.platform !== "ios" || !session.leaseId) throw new Error("Apple MCP requires an exact local iOS device lease.");
49876
49937
  let pending = sessions.get(session.leaseId);
@@ -49885,6 +49946,7 @@ function createAppleDeviceAdapter(input) {
49885
49946
  const missing = REQUIRED_DEVICE_TOOLS.filter((tool) => !client.tools.has(tool));
49886
49947
  if (missing.length > 0) throw new Error(`Selected Xcode lacks required device tools: ${missing.join(", ")}. No device session was opened.`);
49887
49948
  if (input.workspacePath) await client.call("XcodeOpenWorkspace", { path: input.workspacePath }, signal);
49949
+ else if (workspace) await client.call("XcodeOpenWorkspace", { path: workspace }, signal, { abandonAfterMs: DISCOVERED_PROJECT_OPEN_WAIT_MS }).catch(() => void 0);
49888
49950
  const opened = Schema$1.decodeUnknownSync(Session)(appleMcpData(await client.call("DeviceInteractionStartSession", {
49889
49951
  deviceIdentifier: session.deviceId,
49890
49952
  sessionIdentifier: `P4 Device ${session.threadId.slice(0, 8)}`
@@ -49956,7 +50018,7 @@ function createAppleDeviceAdapter(input) {
49956
50018
  const permission = objectValue(status.permission);
49957
50019
  if ((Array.isArray(permission.pendingAgentApprovals) ? permission.pendingAgentApprovals : []).some((item) => objectValue(objectValue(objectValue(objectValue(item).subject).agent)._0).name === "p4code-device-adapter")) return "Apple Xcode is waiting for approval of p4code-device-adapter and its project folder. Approve the pending request in Xcode, then select the device again. P4 cannot grant this approval automatically.";
49958
50020
  if (status.running === false) return "Apple MCP service is not running. Open the selected Xcode project or configure its headless service, then select the device again.";
49959
- return "Apple MCP did not respond in time. Check Xcode agent/folder permissions and session logs before selecting the device again.";
50021
+ return "Apple MCP refused or did not respond. Xcode asks to approve p4code-device-adapter only when P4 opens an Xcode project: use a thread whose folder contains an .xcworkspace or .xcodeproj (or set P4CODE_APPLE_MCP_WORKSPACE), approve the prompt in Xcode, then select the device again.";
49960
50022
  }).pipe(Effect.orElseSucceed(() => "Apple MCP did not respond. Check Xcode agent/folder permissions and service status.")),
49961
50023
  discover: Effect.gen(function* () {
49962
50024
  const result = yield* input.run("xcrun", [
@@ -51583,7 +51645,7 @@ const makeWithHosts = Effect.fn("DeviceService.makeWithHosts")(function* (input)
51583
51645
  yield* (yield* initialize).refresh;
51584
51646
  if (access === "agent" && !state.agentAccessEnabled) return yield* new DeviceOperationError({
51585
51647
  operation: "list",
51586
- reason: "settings_failed",
51648
+ reason: "agent_access_disabled",
51587
51649
  cause: "Agent device access is disabled."
51588
51650
  });
51589
51651
  const ids = enabled ? state.hosts.map((host) => host.id) : [];
@@ -51689,7 +51751,8 @@ const makeWithHosts = Effect.fn("DeviceService.makeWithHosts")(function* (input)
51689
51751
  deviceId: found.device.id,
51690
51752
  platform: found.device.platform,
51691
51753
  openedAt,
51692
- leaseId: NodeCrypto.randomUUID()
51754
+ leaseId: NodeCrypto.randomUUID(),
51755
+ ...value.workspaceRoot ? { workspaceRoot: value.workspaceRoot } : {}
51693
51756
  };
51694
51757
  yield* closeCommands(sessionsToClose(state.sessions, session));
51695
51758
  yield* update((current) => ({
@@ -57972,6 +58035,7 @@ const requireThreadSpawn = Effect.fn("mcp.requireThreadSpawn")(function* (fusion
57972
58035
  //#region src/mcp/DeviceInvocation.ts
57973
58036
  const isDeviceToolUnavailableError = Schema$1.is(DeviceToolUnavailableError);
57974
58037
  const isDeviceActionUnavailableError = Schema$1.is(DeviceActionUnavailableError);
58038
+ const isDeviceOperationError = Schema$1.is(DeviceOperationError);
57975
58039
  /** Internal bridge; tool payloads never choose thread identity or daemon credentials. */
57976
58040
  const withDeviceInvocation = (execute, capability = "devices") => Effect.gen(function* () {
57977
58041
  const invocation = yield* McpInvocationContext;
@@ -57979,9 +58043,21 @@ const withDeviceInvocation = (execute, capability = "devices") => Effect.gen(fun
57979
58043
  const lifetime = invocation.authorizationLifetime;
57980
58044
  const denied = () => new DeviceToolUnavailableError({ reason: "Device command unavailable or authorization changed." });
57981
58045
  if (!invocation.capabilities.has(capability) || !lifetime?.revocation || lifetime.revoked) return yield* denied();
57982
- const work = execute(service, invocation.threadId).pipe(Effect.tap(() => lifetime.revoked ? Effect.fail(denied()) : Effect.void), Effect.mapError((error) => isDeviceToolUnavailableError(error) || isDeviceActionUnavailableError(error) ? error : denied()));
58046
+ const work = execute(service, invocation.threadId).pipe(Effect.tap(() => lifetime.revoked ? Effect.fail(denied()) : Effect.void), Effect.mapError((error) => isDeviceToolUnavailableError(error) || isDeviceActionUnavailableError(error) ? error : isDeviceOperationError(error) && error.reason === "agent_access_disabled" ? new DeviceToolUnavailableError({ reason: error.message }) : denied()));
57983
58047
  return yield* Effect.raceFirst(work, Deferred.await(lifetime.revocation).pipe(Effect.andThen(Effect.fail(denied()))));
57984
58048
  });
58049
+ /**
58050
+ * The thread's worktree, else its project root, for opening a device session.
58051
+ * Unreadable projections yield nothing: the session still opens without it.
58052
+ */
58053
+ const threadWorkspaceRoot = (threadId) => Effect.gen(function* () {
58054
+ const query = yield* ProjectionSnapshotQuery;
58055
+ const thread = yield* query.getThreadShellById(threadId);
58056
+ if (Option.isNone(thread)) return void 0;
58057
+ if (thread.value.worktreePath) return thread.value.worktreePath;
58058
+ const project = yield* query.getProjectShellById(thread.value.projectId);
58059
+ return Option.isSome(project) ? project.value.workspaceRoot : void 0;
58060
+ }).pipe(Effect.orElseSucceed(() => void 0));
57985
58061
  const withDeviceSession = (target, execute) => withDeviceInvocation((service, threadId) => service.withAgentCommand({
57986
58062
  hostId: target.hostId,
57987
58063
  deviceId: target.deviceId,
@@ -58014,6 +58090,7 @@ const makeComputerUseDevices = Effect.fn("ComputerUseDevices.make")(function* ()
58014
58090
  const screenshots = yield* DeviceScreenshot;
58015
58091
  const commands = yield* DeviceCommandProxy;
58016
58092
  const http = yield* HttpClient.HttpClient;
58093
+ const snapshots = yield* ProjectionSnapshotQuery;
58017
58094
  const stale = () => new DeviceOperationError({
58018
58095
  operation: "command",
58019
58096
  reason: "request_failed",
@@ -58034,11 +58111,13 @@ const makeComputerUseDevices = Effect.fn("ComputerUseDevices.make")(function* ()
58034
58111
  const device = state.devices.find((device) => device.hostId === input.hostId && device.id === input.deviceId && device.platform === input.platform);
58035
58112
  if (!device) return yield* stale();
58036
58113
  if (device.physical) return yield* new DeviceToolUnavailableError({ reason: "Physical-device UI control is not supported by this adapter. Pairing, trust, Developer Mode and signing must be configured separately; use supported device-management workflows." });
58114
+ const workspaceRoot = yield* threadWorkspaceRoot(threadId).pipe(Effect.provideService(ProjectionSnapshotQuery, snapshots));
58037
58115
  const session = yield* service.agentOpen({
58038
58116
  threadId,
58039
58117
  hostId: input.hostId,
58040
58118
  deviceId: input.deviceId,
58041
- platform: input.platform
58119
+ platform: input.platform,
58120
+ ...workspaceRoot ? { workspaceRoot } : {}
58042
58121
  });
58043
58122
  if (!session.leaseId) return yield* stale();
58044
58123
  return {
@@ -69723,6 +69802,13 @@ const NARRATE_BEFORE_TOOLS_PROMPT = "Before each turn's first tool call, state n
69723
69802
  */
69724
69803
  const SUBAGENTS_ALLOWED_PROMPT = "You may spawn Task subagents without asking; this overrides earlier request-only rules. Use for bounded search/review whose result is far smaller than required reading. Single-file answer/edit stays inline.";
69725
69804
  const SUBAGENTS_ON_REQUEST_PROMPT = "Never spawn Task subagents unless user asks; this overrides earlier proactive permission. Work inline; say when delegation would have been better.";
69805
+ /**
69806
+ * Appended to ordinary (non-Fusion) sessions. Fusion role prompts already tell
69807
+ * both roles to follow applicable instructions and load memories and skills;
69808
+ * without the same line an ordinary thread treats the user's memory store as
69809
+ * optional background it may skip.
69810
+ */
69811
+ const INSTRUCTIONS_AND_MEMORY_PROMPT = "Follow applicable global and project instructions, and load relevant memories and skills on demand. When ~/.agent-memory exists, read its INDEX.md and the entries it points you to before acting.";
69726
69812
  //#endregion
69727
69813
  //#region src/provider/Layers/ClaudeAdapter.ts
69728
69814
  /**
@@ -70398,6 +70484,22 @@ const CLAUDE_SETTING_SOURCES = [
70398
70484
  "project",
70399
70485
  "local"
70400
70486
  ];
70487
+ /**
70488
+ * Claude user-scope MCP servers this session already receives under their
70489
+ * `p4-user-` alias, as `deniedMcpServers` entries.
70490
+ *
70491
+ * The `user` setting source still loads the originals from `~/.claude.json`,
70492
+ * where Claude has no credential for a server p4code authorized. An OAuth
70493
+ * server then appears twice, once working and once as "needs authentication",
70494
+ * and the agent believes the service is unavailable. Denying each original by
70495
+ * name leaves the working alias as the only copy. The deny matches by name in
70496
+ * every Claude scope, so a same-named project server is dropped too.
70497
+ */
70498
+ function claudeShadowedUserScopeMcpServers(sessionServerNames) {
70499
+ const prefix = mcpUserScopeSessionName("");
70500
+ const names = new Set(sessionServerNames);
70501
+ return sessionServerNames.filter((name) => name.startsWith(prefix)).map((name) => name.slice(prefix.length)).filter((original) => original.length > 0 && !names.has(original)).map((serverName) => ({ serverName }));
70502
+ }
70401
70503
  function buildPromptText(input, boundInstanceId, customModels = [], manifest) {
70402
70504
  const rawEffort = input.modelSelection?.instanceId === boundInstanceId ? getModelSelectionStringOptionValue(input.modelSelection, "effort") : null;
70403
70505
  const caps = getClaudeModelCapabilities(input.modelSelection?.instanceId === boundInstanceId ? input.modelSelection.model : void 0, customModels, manifest);
@@ -72433,14 +72535,16 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
72433
72535
  }[input.runtimeMode];
72434
72536
  const disabledSkills = options?.resolveDisabledSkills === void 0 ? [] : yield* options.resolveDisabledSkills;
72435
72537
  const skillOverrides = disabledSkills.length === 0 ? void 0 : buildSkillOverrides(disabledSkills, yield* discoverClaudeSkills(claudeSettings, input.cwd, claudeEnvironment).pipe(Effect.provideService(FileSystem.FileSystem, fileSystem), Effect.provideService(Path$1.Path, path)));
72538
+ const mcpSession = readMcpProviderSession(input.threadId);
72539
+ const registeredMcpServers = Object.fromEntries(Object.entries(options?.resolveMcpServers === void 0 ? {} : yield* options.resolveMcpServers(input.cwd)).filter(([name]) => !providerMcpReservedNames().has(name)));
72540
+ const deniedMcpServers = claudeShadowedUserScopeMcpServers([...Object.keys(registeredMcpServers), ...providerMcpReservedNames()]);
72436
72541
  const settings = {
72437
72542
  ...typeof thinking === "boolean" ? { alwaysThinkingEnabled: thinking } : {},
72438
72543
  ...fastMode ? { fastMode: true } : {},
72439
72544
  ...ultracode ? { ultracode: true } : {},
72440
- ...skillOverrides ? { skillOverrides } : {}
72545
+ ...skillOverrides ? { skillOverrides } : {},
72546
+ ...deniedMcpServers.length > 0 ? { deniedMcpServers } : {}
72441
72547
  };
72442
- const mcpSession = readMcpProviderSession(input.threadId);
72443
- const registeredMcpServers = Object.fromEntries(Object.entries(options?.resolveMcpServers === void 0 ? {} : yield* options.resolveMcpServers(input.cwd)).filter(([name]) => !providerMcpReservedNames().has(name)));
72444
72548
  const narrateBeforeTools = options?.resolveToolCallNarration === void 0 ? DEFAULT_SERVER_SETTINGS.enableToolCallNarration : yield* options.resolveToolCallNarration;
72445
72549
  const guardrailSettings = options?.resolveGuardrailPrompts === void 0 ? {
72446
72550
  enableVerificationBeforeCompletion: DEFAULT_SERVER_SETTINGS.enableVerificationBeforeCompletion,
@@ -72461,7 +72565,7 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
72461
72565
  ...narrateBeforeTools ? [NARRATE_BEFORE_TOOLS_PROMPT] : [],
72462
72566
  ...guardrailPromptsFor(guardrailSettings),
72463
72567
  unpromptedSubagents ? SUBAGENTS_ALLOWED_PROMPT : SUBAGENTS_ON_REQUEST_PROMPT,
72464
- ...input.fusionRole !== void 0 ? [fusionRoleInstructionsWith(input.fusionRole, "claudeAgent", fusionPromptSettings)] : [],
72568
+ ...input.fusionRole !== void 0 ? [fusionRoleInstructionsWith(input.fusionRole, "claudeAgent", fusionPromptSettings)] : [INSTRUCTIONS_AND_MEMORY_PROMPT],
72465
72569
  ...fusionLeanContextApplies(fusionContextPolicy) ? [FUSION_SAFETY_KERNEL] : [],
72466
72570
  ...compressRuleset !== void 0 ? [compressRuleset] : []
72467
72571
  ].join("\n\n");
@@ -133150,6 +133254,7 @@ const makeWsRpcLayer = (currentSession, previewAutomationBroker, computerUseBrok
133150
133254
  reason: plan.reason
133151
133255
  });
133152
133256
  const modelSelection = plan.model;
133257
+ const compressMode = yield* serverSettings.getSettings.pipe(Effect.map((settings) => settings.defaultCompressMode), Effect.orElseSucceed(() => DEFAULT_COMPRESS_MODE));
133153
133258
  const threadId = ThreadId.make(yield* crypto.randomUUIDv4.pipe(Effect.orDie));
133154
133259
  const createdAt = yield* nowIso$2;
133155
133260
  const threadTitle = buildTaskThreadTitle(task.title);
@@ -133174,7 +133279,7 @@ const makeWsRpcLayer = (currentSession, previewAutomationBroker, computerUseBrok
133174
133279
  titleSeed: threadTitle,
133175
133280
  runtimeMode: DEFAULT_RUNTIME_MODE$2,
133176
133281
  interactionMode: DEFAULT_PROVIDER_INTERACTION_MODE,
133177
- compressMode: DEFAULT_COMPRESS_MODE,
133282
+ compressMode,
133178
133283
  unpromptedSubagents: true,
133179
133284
  bootstrap: { createThread: {
133180
133285
  projectId: plan.projectId,
@@ -133182,7 +133287,7 @@ const makeWsRpcLayer = (currentSession, previewAutomationBroker, computerUseBrok
133182
133287
  modelSelection,
133183
133288
  runtimeMode: DEFAULT_RUNTIME_MODE$2,
133184
133289
  interactionMode: DEFAULT_PROVIDER_INTERACTION_MODE,
133185
- compressMode: DEFAULT_COMPRESS_MODE,
133290
+ compressMode,
133186
133291
  unpromptedSubagents: true,
133187
133292
  branch: null,
133188
133293
  worktreePath: null,
@@ -133875,7 +133980,7 @@ const ProviderAdapterRegistryLive = Layer.effect(ProviderAdapterRegistry, makePr
133875
133980
  //#endregion
133876
133981
  //#region src/provider/ComputerUsePrompts.ts
133877
133982
  /** Routing guidance is injected only when the session has P4's Computer Use endpoint. */
133878
- const COMPUTER_USE_ROUTING_PROMPT = "P4 Computer Use is available for this session through the cua_repl MCP server. When you use it for a native desktop app, do not substitute AppleScript or shell automation unless the user explicitly requests that path. For a named app, start directly with const app = await cua.getApp(exactNameOrBundleId); read the automatically returned state/image and stop if it answers the request. Reuse the handle and batch safe actions; do not repeat selection or unchanged state. For Device Hub, simulator, emulator, or connected-device requests, use cua.listDevices() once, then cua.getDevice({hostId,deviceId,platform}) with exact discovered IDs, unless the user explicitly requested a different route such as simctl. Selection already emits initial device state. Reuse its handle; do not print the handle or repeat that state before acting. Never substitute macOS getApp('Simulator'). Device hosts are scoped to this provider environment and can differ from the desktop host. If device discovery, opening, inspection, or interaction reports missing permission or unavailable control, stop P4 device interaction and report the exact limitation. Do not silently retry through screenshots, shell simctl, Xcode tools, or another MCP server; an explicit user request for one of those routes is separate authorization to use it. Do not call getState/getApp, control the P4 desktop app, or open device settings to work around a device API failure. Missing consent, unavailable devices, or unsupported operations are P4 limitations to report, never permission to switch P4 hosts or bypass the tool.";
133983
+ const COMPUTER_USE_ROUTING_PROMPT = "P4 Computer Use is available for this session through the cua_repl MCP server. When you use it for a native desktop app, do not substitute AppleScript or shell automation unless the user explicitly requests that path. For a named app, start directly with const app = await cua.getApp(exactNameOrBundleId); read the automatically returned state/image and stop if it answers the request. Reuse the handle and batch safe actions; do not repeat selection or unchanged state. For Device Hub, simulator, emulator, or connected-device requests, use cua.listDevices() once, then cua.getDevice({hostId,deviceId,platform}) with exact discovered IDs, unless the user explicitly requested a different route such as simctl. Selection already emits initial device state. Reuse its handle; do not print the handle or repeat that state before acting. Never substitute macOS getApp('Simulator'). Device hosts are scoped to this provider environment and can differ from the desktop host. If device discovery, opening, inspection, or interaction reports missing permission or unavailable control, stop P4 device interaction and report the exact limitation. Do not silently retry through screenshots, shell simctl, Xcode tools, or another MCP server; an explicit user request for one of those routes, including a standing fallback authorization in the user's global or project instructions, is separate authorization to use it; report the P4 error and the fallback you used. Do not call getState/getApp, control the P4 desktop app, or open device settings to work around a device API failure. Missing consent, unavailable devices, or unsupported operations are P4 limitations to report, never on their own permission to switch P4 hosts or bypass the tool.";
133879
133984
  /** The routing note, naming the server the way this session's provider sees it. */
133880
133985
  function withComputerUseRouting(input, enabled, serverName = "cua_repl") {
133881
133986
  const prompt = serverName === "cua_repl" ? COMPUTER_USE_ROUTING_PROMPT : COMPUTER_USE_ROUTING_PROMPT.replace("the cua_repl MCP server", `the ${serverName} MCP server`);
@@ -135370,7 +135475,11 @@ const DeviceOpenTool = Tool.make("device_open", {
135370
135475
  parameters: DeviceToolOpenInput,
135371
135476
  success: DeviceToolOpenResult,
135372
135477
  failure: DeviceToolError,
135373
- dependencies: [McpInvocationContext, DeviceService]
135478
+ dependencies: [
135479
+ McpInvocationContext,
135480
+ DeviceService,
135481
+ ProjectionSnapshotQuery
135482
+ ]
135374
135483
  }).annotate(Tool.Title, "Open device").annotate(Tool.Readonly, false);
135375
135484
  const DeviceCloseTool = Tool.make("device_close", {
135376
135485
  description: "Close this thread's device sessions on the named host. Omitted device closes all sessions on that host only. Optional shutdown powers off the selected devices and closes their other viewing sessions.",
@@ -135482,11 +135591,13 @@ const DeviceToolkitHandlersLive = DeviceToolkit.toLayer({
135482
135591
  try: () => selectDevice(state, threadId, value),
135483
135592
  catch: () => new DeviceToolUnavailableError({ reason: "Choose an explicit platform/device from device_list on the requested host." })
135484
135593
  });
135594
+ const workspaceRoot = yield* threadWorkspaceRoot(threadId);
135485
135595
  const session = yield* service.agentOpen({
135486
135596
  threadId,
135487
135597
  hostId: value.hostId,
135488
135598
  deviceId: selected.id,
135489
- platform: selected.platform
135599
+ platform: selected.platform,
135600
+ ...workspaceRoot ? { workspaceRoot } : {}
135490
135601
  });
135491
135602
  const current = yield* service.state;
135492
135603
  const device = current.devices.find((device) => device.hostId === session.hostId && device.id === session.deviceId && device.platform === session.platform);
@@ -0,0 +1,98 @@
1
+ import{r as e}from"./rolldown-runtime-QTnfLwEv.js";import{n as t,r as n,t as r}from"./compiler-runtime-CLAvuQ-D.js";import{$a as i,$u as a,Ba as o,Cl as s,Cn as c,Dt as l,Eu as ee,Ga as te,Kl as u,Kr as ne,La as d,Qa as f,Qr as re,Sl as p,Vr as m,Wa as h,Xa as ie,Za as g,_t as _,b as v,bu as ae,ct as y,eo as b,gt as x,h as S,hu as C,lu as w,vn as oe,wl as T,xl as E,yt as D}from"./previewAssetResource-CdRrwvdJ.js";import{a as O,c as se,i as k,n as A,o as j,r as M,s as ce,t as le}from"./toggle-group-BPwLpAJP.js";import{A as ue,Fr as de,J as fe,Lr as pe,M as N,Mr as me,Nr as he,Rr as ge,Y as _e,_ as ve,ar as ye,cr as be,ct as xe,dr as Se,dt as Ce,ft as P,h as we,ir as Te,j as Ee,jr as De,k as Oe,lr as ke,lt as Ae,nr as je,oi as Me,or as Ne,ot as Pe,pr as Fe,rr as Ie,si as Le,sr as Re,st as ze,ut as Be,yr as Ve}from"./index-sXPBcrsQ.js";import{a as He,n as Ue}from"./fileCommentAnnotations-CgO_0-Tr.js";var We=T(`pilcrow`,[[`path`,{d:`M13 4v16`,key:`8vvj80`}],[`path`,{d:`M17 4v16`,key:`7dpous`}],[`path`,{d:`M19 4H9.5a4.5 4.5 0 0 0 0 9H13`,key:`sh4n9v`}]]),F=e(n(),1);function Ge({threadRef:e,filePath:t,activeCwd:n,openInEditor:r,openFileSurface:i}){if(e){if(i){i(t);return}y.getState().openFile(e,t);return}r(n?S(t,n):t)}var I=r();function Ke(e,t){let n=(0,I.c)(4),r=Se(e,t),i;return n[0]!==r.data||n[1]!==r.error||n[2]!==r.isPending?(i={data:r.data,error:r.error,isPending:r.isPending},n[0]=r.data,n[1]=r.error,n[2]=r.isPending,n[3]=i):i=n[3],i}var L=t(),qe=[];function Je(e){return(e.endSide??e.side)===`deletions`?`deletions`:`additions`}function R(e,t,n){let r=Je(t),i=e.findIndex(e=>e.side===r&&e.lineNumber===t.end);return i<0?[...e,{side:r,lineNumber:t.end,metadata:{entries:[n]}}]:e.map((e,t)=>t===i?{...e,metadata:{entries:[...e.metadata.entries,n]}}:e)}function Ye(e){let t=(0,I.c)(50),{files:n,sectionId:r,sectionTitle:i,composerDraftTarget:a,options:o,viewerRef:s,className:c,renderHeaderPrefix:l}=e,ee=m(Qe),te=m(B),u;t[0]===a?u=t[1]:(u=e=>e.getComposerDraft(a)?.reviewComments??qe,t[0]=a,t[1]=u);let d=m(u),[f,p]=(0,F.useState)(null),[h,ie]=(0,F.useState)(null),g;t[2]===n?g=t[3]:(g=new Map(n.map(Ze)),t[2]=n,t[3]=g);let _=g,v;if(t[4]!==h||t[5]!==n||t[6]!==d||t[7]!==r){let e;t[9]!==h||t[10]!==d||t[11]!==r?(e=e=>{let{fileDiff:t,filePath:n,fileKey:i,collapsed:a}=e,o=d.filter(e=>e.sectionId===r&&e.filePath===n&&(e.fenceLanguage??`diff`)===`diff`).reduce((e,n)=>{let r=re(t,n);return r?R(e,r,{id:n.id,kind:`comment`,range:r,rangeLabel:n.rangeLabel,text:n.text}):e},[]),s=h?.fileKey===i?[...o,h.annotation]:o;return{id:i,type:`diff`,fileDiff:t,annotations:s,collapsed:a,version:ze(`${a?`1`:`0`}:${s.flatMap(z).join(`:`)}`)}},t[9]=h,t[10]=d,t[11]=r,t[12]=e):e=t[12],v=n.map(e),t[4]=h,t[5]=n,t[6]=d,t[7]=r,t[8]=v}else v=t[8];let ae=v,y;t[13]!==a||t[14]!==h?.annotation||t[15]!==te?(y=e=>{p(null),h?.annotation.metadata.entries.some(t=>t.id===e)?ie(null):te(a,e)},t[13]=a,t[14]=h?.annotation,t[15]=te,t[16]=y):y=t[16];let b=y,x;t[17]!==ee||t[18]!==a||t[19]!==h||t[20]!==_||t[21]!==r||t[22]!==i?(x=(e,t)=>{let n=h?.annotation.metadata.entries.find(t=>t.id===e),o=h?_.get(h.fileKey):void 0;if(!n||!o)return;let s=ne({id:n.id,sectionId:r,sectionTitle:i,filePath:o.filePath,fileDiff:o.fileDiff,range:n.range,text:t});s&&ee(a,s),p(null),ie(null)},t[17]=ee,t[18]=a,t[19]=h,t[20]=_,t[21]=r,t[22]=i,t[23]=x):x=t[23];let S=x,C;t[24]!==_||t[25]!==r||t[26]!==i?(C=(e,t)=>{if(!e)return;let n=t.item;if(n.type!==`diff`)return;let a=_.get(n.id);if(!a)return;let o=Ue(),s=ne({id:o,sectionId:r,sectionTitle:i,filePath:a.filePath,fileDiff:a.fileDiff,range:e,text:``});s&&ie({fileKey:n.id,annotation:{side:Je(e),lineNumber:e.end,metadata:{entries:[{id:o,kind:`draft`,range:e,rangeLabel:s.rangeLabel,text:``}]}}})},t[24]=_,t[25]=r,t[26]=i,t[27]=C):C=t[27];let w=C,oe=h!==null,T;t[28]===s?T=t[29]:(T=s?{ref:s}:{},t[28]=s,t[29]=T);let E;t[30]===c?E=t[31]:(E=c?{className:c}:{},t[30]=c,t[31]=E);let D=!oe,se=!oe,k;t[32]!==w||t[33]!==o||t[34]!==se||t[35]!==D?(k={...o,enableGutterUtility:D,enableLineSelection:se,onLineSelectionEnd:w},t[32]=w,t[33]=o,t[34]=se,t[35]=D,t[36]=k):k=t[36];let A;t[37]===l?A=t[38]:(A=e=>e.type===`diff`?l(e.fileDiff,e.id,e.collapsed===!0):null,t[37]=l,t[38]=A);let j;t[39]!==b||t[40]!==S?(j=e=>(0,L.jsx)(`div`,{className:`py-1`,children:e.metadata.entries.map(e=>(0,L.jsx)(He,{kind:e.kind,rangeLabel:e.rangeLabel,text:e.text,onCancel:()=>b(e.id),onComment:t=>S(e.id,t),onDelete:()=>b(e.id)},e.id))}),t[39]=b,t[40]=S,t[41]=j):j=t[41];let M;return t[42]!==ae||t[43]!==f||t[44]!==k||t[45]!==A||t[46]!==j||t[47]!==T||t[48]!==E?(M=(0,L.jsx)(O,{...T,...E,items:ae,selectedLines:f,onSelectedLinesChange:p,options:k,renderHeaderPrefix:A,renderAnnotation:j}),t[42]=ae,t[43]=f,t[44]=k,t[45]=A,t[46]=j,t[47]=T,t[48]=E,t[49]=M):M=t[49],M}function z(e){return e.metadata.entries.map(Xe)}function Xe(e){return`${e.id}:${e.rangeLabel}:${e.text}`}function Ze(e){return[e.fileKey,e]}function B(e){return e.removeReviewComment}function Qe(e){return e.addReviewComment}function $e(e){return{diffPreview:w(e,{label:`environment-data:review:diff-preview`,tag:a.reviewGetDiffPreview,staleTimeMs:5e3})}}var et=$e(c);function V(e){return e.remoteName&&e.name.startsWith(`${e.remoteName}/`)?e.name.slice(e.remoteName.length+1):e.name}function tt(e,t){let n=new Set(t),r=e.map(e=>{let r=t.filter(t=>n.has(t)&&V(t)===e.name),i=r.find(e=>e.remoteName===`origin`)??r[0]??null;return i&&n.delete(i),{id:`local:${e.name}`,label:e.name,local:e,remote:i}}),i=t.filter(e=>n.has(e)).map(e=>({id:`remote:${e.name}`,label:e.name,local:null,remote:e}));return[...r,...i]}function nt(e,t){let n=t.trim().toLocaleLowerCase();return n.length===0?e:e.filter(e=>e.label.toLocaleLowerCase().includes(n)||e.local?.name.toLocaleLowerCase().includes(n)===!0||e.remote?.name.toLocaleLowerCase().includes(n)===!0)}var H=`__automatic_base_ref__`,rt=new Set,it=`
2
+ [data-diffs-header],
3
+ [data-diff],
4
+ [data-file],
5
+ [data-error-wrapper],
6
+ [data-virtualizer-buffer] {
7
+ --diffs-header-font-family: var(--font-sans) !important;
8
+ --diffs-font-family: var(--font-mono) !important;
9
+ --diffs-bg: color-mix(in srgb, var(--card) 90%, var(--background)) !important;
10
+ --diffs-light-bg: color-mix(in srgb, var(--card) 90%, var(--background)) !important;
11
+ --diffs-dark-bg: color-mix(in srgb, var(--card) 90%, var(--background)) !important;
12
+ --diffs-token-light-bg: transparent;
13
+ --diffs-token-dark-bg: transparent;
14
+
15
+ --diffs-bg-context-override: color-mix(in srgb, var(--background) 97%, var(--foreground));
16
+ --diffs-bg-hover-override: color-mix(in srgb, var(--background) 94%, var(--foreground));
17
+ --diffs-bg-separator-override: color-mix(in srgb, var(--background) 95%, var(--foreground));
18
+ --diffs-bg-buffer-override: color-mix(in srgb, var(--background) 90%, var(--foreground));
19
+
20
+ --diffs-bg-addition-override: color-mix(in srgb, var(--background) 92%, var(--success));
21
+ --diffs-bg-addition-number-override: color-mix(in srgb, var(--background) 88%, var(--success));
22
+ --diffs-bg-addition-hover-override: color-mix(in srgb, var(--background) 85%, var(--success));
23
+ --diffs-bg-addition-emphasis-override: color-mix(in srgb, var(--background) 80%, var(--success));
24
+
25
+ --diffs-bg-deletion-override: color-mix(in srgb, var(--background) 92%, var(--destructive));
26
+ --diffs-bg-deletion-number-override: color-mix(in srgb, var(--background) 88%, var(--destructive));
27
+ --diffs-bg-deletion-hover-override: color-mix(in srgb, var(--background) 85%, var(--destructive));
28
+ --diffs-bg-deletion-emphasis-override: color-mix(
29
+ in srgb,
30
+ var(--background) 80%,
31
+ var(--destructive)
32
+ );
33
+
34
+ background-color: var(--diffs-bg) !important;
35
+ }
36
+
37
+ [data-file-info] {
38
+ background-color: color-mix(in srgb, var(--card) 94%, var(--foreground)) !important;
39
+ border-block-color: var(--border) !important;
40
+ color: var(--foreground) !important;
41
+ }
42
+
43
+ [data-diffs-header] {
44
+ position: sticky !important;
45
+ top: 0;
46
+ z-index: 4;
47
+ background-color: color-mix(in srgb, var(--card) 94%, var(--foreground)) !important;
48
+ border-bottom: 1px solid var(--border) !important;
49
+ align-items: center !important;
50
+ font-family: var(--font-sans) !important;
51
+ font-size: 12px !important;
52
+ line-height: 1 !important;
53
+ min-height: 32px !important;
54
+ padding-block: 6px !important;
55
+ }
56
+
57
+ [data-diffs-header] [data-header-content] {
58
+ align-items: center !important;
59
+ line-height: 1 !important;
60
+ }
61
+
62
+ [data-diffs-header] [data-metadata] {
63
+ align-items: center !important;
64
+ line-height: 1 !important;
65
+ font-variant-numeric: tabular-nums;
66
+ }
67
+
68
+ [data-diffs-header] [data-additions-count],
69
+ [data-diffs-header] [data-deletions-count] {
70
+ font-family: var(--font-mono) !important;
71
+ font-size: 11px !important;
72
+ font-variant-numeric: tabular-nums;
73
+ line-height: 1 !important;
74
+ }
75
+
76
+ [data-diffs-header] [data-change-icon],
77
+ [data-diffs-header] [data-rename-icon] {
78
+ display: block;
79
+ flex-shrink: 0;
80
+ }
81
+
82
+ [data-title] {
83
+ cursor: pointer;
84
+ transition:
85
+ color 120ms ease,
86
+ text-decoration-color 120ms ease;
87
+ text-decoration: underline;
88
+ text-decoration-color: transparent;
89
+ text-underline-offset: 2px;
90
+ font-family: var(--font-sans) !important;
91
+ }
92
+
93
+ [data-title]:hover {
94
+ color: color-mix(in srgb, var(--foreground) 84%, var(--primary)) !important;
95
+ text-decoration-color: currentColor;
96
+ }
97
+ `;function U({mode:e=`inline`,composerDraftTarget:t,initialGitScope:n,threadRef:r}){let{resolvedTheme:a}=v(),c=de(),[ne]=(0,F.useState)(n),[re,m]=(0,F.useState)(`stacked`),[y,S]=(0,F.useState)(c.wordWrap),[w,T]=(0,F.useState)(c.diffIgnoreWhitespace),[O,ve]=(0,F.useState)(``),[Se,ze]=(0,F.useState)(()=>({scopeKey:null,fileKeys:rt})),He=(0,F.useRef)(null),Ue=r.threadId,I=he(r),qe=I?.projectId??null,Je=me(I&&qe?{environmentId:I.environmentId,projectId:qe}:null),R=I?.worktreePath??Je?.workspaceRoot,z=u(oe.configValueAtom(I?.environmentId??null)),Xe=Fe(I?.environmentId??null,z?.availableEditors??[]),Ze=l(I!=null&&R!=null?De.status({environmentId:I.environmentId,input:{cwd:R}}):null),B=N(e=>Ee(e.byThreadKey,r,ne===`unstaged`)),Qe=Ze.data?.isRepo??!0,{turnDiffSummaries:$e,inferredCheckpointTurnCountByTurnId:V}=ue(I),U=(0,F.useMemo)(()=>[...$e].toSorted((e,t)=>{let n=e.checkpointTurnCount??V[e.turnId]??0,r=t.checkpointTurnCount??V[t.turnId]??0;return n===r?t.completedAt.localeCompare(e.completedAt):r-n}),[V,$e]);(0,F.useEffect)(()=>{B.kind===`turn`&&N.getState().reconcileTurnSelection(r,U.map(e=>e.turnId))},[B,U,r]);let W=B.kind===`turn`?B.turnId:null,G=B.kind===`unstaged`?`unstaged`:`branch`,K=B.kind===`branch`?B.baseRef:null,at=B.kind===`turn`?B.filePath:null,ot=B.kind===`turn`?B.revealRequestId:0,q=W===null?void 0:U.find(e=>e.turnId===W)??U[0],J=q&&(q.checkpointTurnCount??V[q.turnId]),st=U[0],ct=W===null?G===`unstaged`?`Working tree`:`Branch changes`:q?.turnId===st?.turnId?`Latest turn`:`Turn ${J??`?`}`,lt=q?`turn:${q.turnId}`:G,Y=`${r.environmentId}:${r.threadId}:${lt}`,ut=Se.scopeKey===Y?Se.fileKeys:rt,dt=q?`Turn ${J??`?`}`:G===`unstaged`?`Working tree`:`Branch changes`,ft=(0,F.useMemo)(()=>typeof J==`number`?{fromTurnCount:Math.max(0,J-1),toTurnCount:J}:null,[J]),pt=Ke({environmentId:I?.environmentId??null,threadId:Ue,fromTurnCount:ft?.fromTurnCount??null,toTurnCount:ft?.toTurnCount??null,ignoreWhitespace:w,cacheScope:q?`turn:${q.turnId}`:null},{enabled:Qe&&q!==void 0}),mt=l(W===null&&I&&R?et.diffPreview({environmentId:I.environmentId,input:{cwd:R,...K?{baseRef:K}:{},ignoreWhitespace:w}}):null),ht=W===null&&mt.error?.includes(`configured workspace root`)===!0&&z?.cwd!==void 0&&z.cwd!==R,gt=l(ht&&I&&z?et.diffPreview({environmentId:I.environmentId,input:{cwd:z.cwd,...K?{baseRef:K}:{},ignoreWhitespace:w}}):null),X=ht?gt:mt,Z=X.data?.sources.find(e=>e.kind===(G===`unstaged`?`working-tree`:`branch-range`)),_t=l(W===null&&G===`branch`&&I&&X.data?.cwd?De.listRefs({environmentId:I.environmentId,input:{cwd:X.data.cwd,includeMatchingRemoteRefs:!0,refKind:`local`,...O.trim().length>0?{query:O.trim()}:{},limit:100}}):null),vt=l(W===null&&G===`branch`&&I&&X.data?.cwd?De.listRefs({environmentId:I.environmentId,input:{cwd:X.data.cwd,includeMatchingRemoteRefs:!0,refKind:`remote`,...O.trim().length>0?{query:O.trim()}:{},limit:100}}):null),yt=tt(_t.data?.refs.filter(e=>e.name!==Z?.headRef)??[],vt.data?.refs??[]),bt=nt(yt,O),xt=e=>K&&K===e.remote?.name?K:e.local?.name??e.remote?.name??e.id,St=[H,...yt.map(xt)],Ct=[...O.trim().length===0?[H]:[],...bt.map(xt)],wt=Z?.diff,Tt=q?pt.data?.diff:wt,Et=!q&&Z?.truncated===!0,Dt=q?pt.isPending:X.isPending,Ot=q?pt.error:X.error,kt=typeof Tt==`string`&&Tt.trim().length===0,Q=(0,F.useMemo)(()=>Be(Tt,`diff-panel:${a}`,{compactPartialHunkOffsets:W===null}),[a,Tt,W]),At=(0,F.useMemo)(()=>!Q||Q.kind!==`files`?[]:Q.files.toSorted((e,t)=>P(e).localeCompare(P(t),void 0,{numeric:!0,sensitivity:`base`})),[Q]),$=(0,F.useMemo)(()=>At.map(e=>{let t=Pe(e);return{fileDiff:e,filePath:P(e),fileKey:t,collapsed:ut.has(t)}}),[ut,At]),jt=(0,F.useMemo)(()=>$.map(e=>e.fileKey),[$]),Mt=M(jt,ut),Nt=(0,F.useMemo)(()=>Ae(At),[At]);(0,F.useEffect)(()=>{if(!at)return;let e=$.find(e=>e.filePath===at);e&&He.current?.scrollTo({type:`item`,id:e.fileKey,align:`start`})},[$,at,ot]);let Pt=Oe({threadRef:r,workspaceRoot:R??null}),Ft=(0,F.useCallback)(e=>{Ge({threadRef:r,filePath:e,activeCwd:R,openFileSurface:Pt,openInEditor:e=>{(async()=>{let t=await Xe(e);t._tag===`Failure`&&!C(t)&&console.warn(`Failed to open diff file in editor.`,{operation:`open-diff-file`,environmentId:r.environmentId,threadId:r.threadId,...ee(ae(t))})})()}})},[R,Pt,Xe,r]),It=(0,F.useCallback)(e=>{ze(t=>{let n=new Set(t.scopeKey===Y?t.fileKeys:[]);return n.has(e)?n.delete(e):n.add(e),{scopeKey:Y,fileKeys:n}})},[Y]),Lt=(0,F.useCallback)(()=>{ze(e=>{let t=e.scopeKey===Y?e.fileKeys:rt;return{scopeKey:Y,fileKeys:k(jt,t)}})},[Y,jt]),Rt=e=>{N.getState().selectTurn(r,e)},zt=e=>{N.getState().selectGitScope(r,e)},Bt=e=>{N.getState().selectBranchBaseRef(r,e)};return(0,L.jsx)(_e,{mode:e,header:(0,L.jsxs)(L.Fragment,{children:[(0,L.jsxs)(`div`,{className:`flex min-w-0 flex-1 items-center gap-3 [-webkit-app-region:no-drag]`,children:[(0,L.jsxs)(o,{children:[(0,L.jsxs)(i,{className:`inline-flex h-6 max-w-full items-center gap-1 rounded-md bg-muted/70 px-2 text-xs font-medium text-foreground outline-none transition-colors hover:bg-muted focus-visible:ring-2 focus-visible:ring-ring`,"aria-label":`Diff scope: ${ct}`,children:[(0,L.jsx)(`span`,{className:`truncate`,children:ct}),(0,L.jsx)(p,{className:`size-3.5 shrink-0 text-muted-foreground`})]}),(0,L.jsxs)(te,{align:`start`,className:`w-60`,children:[(0,L.jsx)(h,{className:W===null&&G===`unstaged`?`bg-foreground/[0.08]`:void 0,onClick:()=>zt(`unstaged`),children:(0,L.jsx)(`span`,{children:`Working tree`})}),(0,L.jsx)(h,{className:W===null&&G===`branch`?`bg-foreground/[0.08]`:void 0,onClick:()=>zt(`branch`),children:(0,L.jsx)(`span`,{children:`Branch changes`})}),(0,L.jsx)(h,{className:W!==null&&q?.turnId===st?.turnId?`bg-foreground/[0.08]`:void 0,onClick:()=>{st&&Rt(st.turnId)},children:(0,L.jsx)(`span`,{children:`Latest turn`})}),(0,L.jsxs)(ie,{children:[(0,L.jsx)(f,{children:`Turn`}),(0,L.jsx)(g,{className:`w-64`,children:U.map(e=>{let t=e.checkpointTurnCount??V[e.turnId]??`?`;return(0,L.jsxs)(h,{className:e.turnId===q?.turnId?`bg-foreground/[0.08]`:void 0,onClick:()=>Rt(e.turnId),children:[(0,L.jsxs)(`span`,{children:[`Turn `,t]}),(0,L.jsx)(`span`,{className:`ml-auto text-xs tabular-nums text-muted-foreground`,children:Ve(e.completedAt,c.timestampFormat)})]},e.turnId)})})]})]})]}),W===null&&G===`branch`&&Z?.baseRef&&(0,L.jsxs)(`div`,{className:`flex min-w-0 max-w-full items-center gap-2 overflow-hidden text-xs text-muted-foreground`,title:`${Z.headRef??`HEAD`} → ${Z.baseRef}`,"aria-label":`Comparing ${Z.headRef??`HEAD`} against ${Z.baseRef}`,children:[(0,L.jsx)(`span`,{className:`min-w-0 max-w-48 truncate`,children:Z.headRef??`HEAD`}),(0,L.jsx)(Le,{className:`size-3.5 shrink-0 opacity-70`}),(0,L.jsxs)(je,{items:St,filteredItems:Ct,value:K??H,onOpenChange:e=>{e||ve(``)},onValueChange:e=>{e&&Bt(e===H?null:e)},children:[(0,L.jsxs)(be,{className:`inline-flex min-w-0 max-w-48 items-center gap-1 overflow-hidden rounded-md px-1.5 py-1 outline-none transition-colors hover:bg-muted hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring`,"aria-label":`Change comparison target. Currently ${Z.baseRef}`,children:[(0,L.jsx)(`span`,{className:`min-w-0 truncate`,children:Z.baseRef}),(0,L.jsx)(p,{className:`size-3.5 shrink-0 opacity-70`})]}),(0,L.jsxs)(Re,{align:`start`,className:`w-72 min-w-0 max-w-[calc(100vw-1rem)] overflow-hidden [&>[data-slot=combobox-popup]]:min-w-0 [&>[data-slot=combobox-popup]]:overflow-hidden`,children:[(0,L.jsx)(`div`,{className:`min-w-0 shrink-0 px-3 pt-2.5`,children:(0,L.jsxs)(`div`,{className:`relative -translate-y-px border-b border-border/70 pb-1.5 transition-colors focus-within:border-ring`,children:[(0,L.jsx)(ge,{"aria-hidden":`true`,className:`pointer-events-none absolute top-1.5 left-0 size-4 shrink-0 text-muted-foreground/55`}),(0,L.jsx)(Te,{className:`[&_input]:h-6.5 [&_input]:ps-5 [&_input]:font-sans [&_input]:leading-6.5`,inputClassName:`rounded-none bg-transparent text-sm`,placeholder:`Search refs...`,showTrigger:!1,size:`sm`,unstyled:!0,value:O,onChange:e=>ve(e.target.value)})]})}),(0,L.jsxs)(`div`,{className:`grid shrink-0 grid-cols-[1rem_minmax(0,1fr)] items-center gap-2 border-b border-border/70 ps-3 pe-6.5 pt-2 pb-1.5 font-medium text-[10px] text-muted-foreground uppercase tracking-wide`,children:[(0,L.jsx)(`span`,{"aria-hidden":`true`}),(0,L.jsxs)(`div`,{className:`grid min-w-0 grid-cols-[minmax(0,1fr)_2rem] items-center`,children:[(0,L.jsx)(`span`,{children:`Branch`}),(0,L.jsx)(`span`,{className:`text-right`,children:`Remote`})]})]}),(0,L.jsx)(Ie,{children:`No matching refs.`}),(0,L.jsxs)(Ne,{className:`max-h-64 min-w-0 overflow-x-hidden`,children:[(0,L.jsx)(ye,{className:`h-8 w-full min-w-0 grid-cols-[1rem_minmax(0,1fr)] py-0`,contentClassName:`w-full min-w-0 overflow-hidden`,value:H,children:(0,L.jsx)(`span`,{className:`block min-w-0 truncate`,children:`Automatic`})}),yt.map(e=>{let t=xt(e),n=e.local!==null&&e.remote!==null,r=e.remote?.name===t;return(0,L.jsx)(ye,{className:`h-8 w-full min-w-0 grid-cols-[1rem_minmax(0,1fr)] py-0`,contentClassName:`w-full min-w-0 overflow-hidden`,value:t,children:(0,L.jsxs)(`div`,{className:`grid w-full min-w-0 grid-cols-[minmax(0,1fr)_2rem] items-center overflow-hidden`,children:[(0,L.jsx)(`span`,{className:`block min-w-0 truncate pe-2`,children:e.label}),n?(0,L.jsx)(`div`,{className:`flex justify-end`,onClick:e=>e.stopPropagation(),onPointerDown:e=>e.stopPropagation(),children:(0,L.jsx)(ke,{"aria-label":`Use remote version of ${e.label}`,checked:r,className:`[--thumb-size:--spacing(3)]`,onCheckedChange:t=>{let n=t?e.remote?.name:e.local?.name;n&&Bt(n)}})}):e.remote?(0,L.jsx)(`span`,{className:`flex justify-end text-muted-foreground`,title:`Remote only`,children:(0,L.jsx)(s,{"aria-hidden":`true`,className:`size-3`})}):null]})},e.id)})]})]})]})]})]}),(0,L.jsxs)(`div`,{className:`flex shrink-0 items-center gap-1 [-webkit-app-region:no-drag]`,children:[$.length>0&&(0,L.jsx)(we,{additions:Nt.additions,deletions:Nt.deletions,className:`mr-1 text-[11px]`,layout:`inline`}),$.length>0&&(0,L.jsxs)(x,{children:[(0,L.jsx)(D,{render:(0,L.jsx)(d,{type:`button`,size:`icon-xs`,variant:`outline`,"aria-label":Mt?`Expand all files`:`Collapse all files`,onClick:Lt}),children:Mt?(0,L.jsx)(Me,{className:`size-3`}):(0,L.jsx)(se,{className:`size-3`})}),(0,L.jsx)(_,{side:`top`,children:Mt?`Expand all files`:`Collapse all files`})]}),(0,L.jsxs)(A,{className:`shrink-0`,variant:`outline`,size:`xs`,value:[re],onValueChange:e=>{let t=e[0];(t===`stacked`||t===`split`)&&m(t)},children:[(0,L.jsx)(le,{"aria-label":`Stacked diff view`,value:`stacked`,children:(0,L.jsx)(j,{className:`size-3`})}),(0,L.jsx)(le,{"aria-label":`Split diff view`,value:`split`,children:(0,L.jsx)(ce,{className:`size-3`})})]}),(0,L.jsxs)(x,{children:[(0,L.jsx)(D,{render:(0,L.jsx)(le,{"aria-label":y?`Disable diff line wrapping`:`Enable diff line wrapping`,variant:`outline`,size:`xs`,pressed:y,onPressedChange:e=>{S(!!e)}}),children:(0,L.jsx)(pe,{className:`size-3`})}),(0,L.jsx)(_,{side:`top`,children:y?`Disable line wrapping`:`Enable line wrapping`})]}),(0,L.jsxs)(x,{children:[(0,L.jsx)(D,{render:(0,L.jsx)(le,{"aria-label":w?`Show whitespace changes`:`Hide whitespace changes`,variant:`outline`,size:`xs`,pressed:w,onPressedChange:e=>{T(!!e)}}),children:(0,L.jsx)(We,{className:`size-3`})}),(0,L.jsx)(_,{side:`top`,children:w?`Show whitespace changes`:`Hide whitespace changes`})]})]})]}),children:I?Qe?W!==null&&U.length===0?(0,L.jsx)(`div`,{className:`flex flex-1 items-center justify-center px-5 text-center text-xs text-muted-foreground/70`,children:`No completed turns yet.`}):(0,L.jsx)(L.Fragment,{children:(0,L.jsxs)(`div`,{className:`diff-panel-viewport flex min-h-0 min-w-0 flex-1 flex-col overflow-hidden`,children:[Et&&(0,L.jsx)(`p`,{className:`shrink-0 border-b border-border/70 bg-muted/40 px-3 py-1.5 text-[11px] text-muted-foreground`,children:`This diff was truncated because it exceeded the preview limit. The changes shown are incomplete.`}),Ot&&!Q&&(0,L.jsx)(`div`,{className:`px-3`,children:(0,L.jsx)(`p`,{className:`mb-2 text-[11px] text-red-500/80`,children:Ot})}),Q?Q.kind===`files`?(0,L.jsx)(`div`,{className:`min-h-0 flex-1`,onClickCapture:e=>{let t=(e.nativeEvent.composedPath?.()??[]).find(e=>e instanceof HTMLElement&&e.hasAttribute(`data-title`))?.textContent?.trim();t&&Ft(t)},children:(0,L.jsx)(Ye,{viewerRef:He,className:`diff-render-surface h-full min-h-0 overflow-auto`,files:$,sectionId:lt,sectionTitle:dt,composerDraftTarget:t,renderHeaderPrefix:(e,t,n)=>{let r=P(e);return(0,L.jsxs)(x,{children:[(0,L.jsx)(D,{render:(0,L.jsx)(`button`,{type:`button`,className:b(`inline-flex size-5 shrink-0 cursor-pointer items-center justify-center rounded-sm border-0 bg-transparent p-0 transition-colors hover:bg-foreground/10 focus-visible:outline-hidden`,xe(e)),"aria-label":n?`Expand ${r}`:`Collapse ${r}`,"aria-expanded":!n,onClick:e=>{e.stopPropagation(),It(t)}}),children:n?(0,L.jsx)(E,{className:`size-4`}):(0,L.jsx)(p,{className:`size-4`})}),(0,L.jsx)(_,{side:`top`,children:n?`Expand diff`:`Collapse diff`})]})},options:{diffStyle:re===`split`?`split`:`unified`,lineDiffType:`none`,overflow:y?`wrap`:`scroll`,theme:Ce(a),themeType:a,unsafeCSS:it,stickyHeaders:!0,itemMetrics:{diffHeaderHeight:33},layout:{paddingTop:0,paddingBottom:8,gap:8}}},Y??lt)}):(0,L.jsx)(`div`,{className:`min-h-0 flex-1 overflow-auto p-2`,children:(0,L.jsxs)(`div`,{className:`space-y-2`,children:[(0,L.jsx)(`p`,{className:`text-[11px] text-muted-foreground/75`,children:Q.reason}),(0,L.jsx)(`pre`,{className:b(`max-h-[72vh] rounded-md border border-border/70 bg-background/70 p-3 font-mono text-[11px] leading-relaxed text-muted-foreground/90`,y?`overflow-auto whitespace-pre-wrap wrap-break-word`:`overflow-auto`),children:Q.text})]})}):Dt?(0,L.jsx)(fe,{label:q?`Loading checkpoint diff...`:G===`unstaged`?`Loading working tree diff...`:`Loading branch diff...`}):(0,L.jsx)(`div`,{className:`flex h-full items-center justify-center px-3 py-2 text-xs text-muted-foreground/70`,children:(0,L.jsx)(`p`,{children:kt?`No net changes in this selection.`:`No patch available for this selection.`})})]})}):(0,L.jsx)(`div`,{className:`flex flex-1 items-center justify-center px-5 text-center text-xs text-muted-foreground/70`,children:`Turn diffs are unavailable because this project is not a git repository.`}):(0,L.jsx)(`div`,{className:`flex flex-1 items-center justify-center px-5 text-center text-xs text-muted-foreground/70`,children:`Select a thread to inspect turn diffs.`})})}export{ve as DiffWorkerPoolProvider,U as default};
98
+ //# sourceMappingURL=DiffPanel-D-49iCgp.js.map