@p4code/cli 0.4.19 → 0.5.1

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
@@ -260,7 +260,7 @@ const make$115 = () => {
260
260
  const layer$118 = Layer.sync(NetService, make$115);
261
261
  //#endregion
262
262
  //#region package.json
263
- var version$1 = "0.4.19";
263
+ var version$1 = "0.5.1";
264
264
  //#endregion
265
265
  //#region src/config.ts
266
266
  /**
@@ -4138,6 +4138,7 @@ const ThreadTurnStartBootstrapCreateThread = Schema$1.Struct({
4138
4138
  createdAt: IsoDateTime
4139
4139
  });
4140
4140
  const ThreadTurnStartBootstrapPrepareWorktree = Schema$1.Struct({
4141
+ mode: Schema$1.optional(Schema$1.Literals(["auto", "worktree"])),
4141
4142
  projectCwd: TrimmedNonEmptyString,
4142
4143
  baseBranch: TrimmedNonEmptyString,
4143
4144
  branch: Schema$1.optional(TrimmedNonEmptyString),
@@ -9800,7 +9801,14 @@ const ComputerUsePermissionHosts = Schema$1.Record(TrimmedNonEmptyString, Schema
9800
9801
  hostId: TrimmedNonEmptyString,
9801
9802
  label: Schema$1.optionalKey(TrimmedNonEmptyString)
9802
9803
  }));
9804
+ const FusionModelPair = Schema$1.Struct({
9805
+ builder: ModelSelection,
9806
+ supervisor: ModelSelection
9807
+ });
9803
9808
  const ClientSettingsSchema = Schema$1.Struct({
9809
+ fusionPairOrder: Schema$1.Record(Schema$1.String, Schema$1.Array(Schema$1.String)).pipe(Schema$1.withDecodingDefault(Effect.succeed({}))),
9810
+ fusionRecentPairs: Schema$1.Array(FusionModelPair).pipe(Schema$1.withDecodingDefault(Effect.succeed([]))),
9811
+ fusionFavoritePairs: Schema$1.Array(FusionModelPair).pipe(Schema$1.withDecodingDefault(Effect.succeed([]))),
9804
9812
  loadBalancingEnabled: Schema$1.Boolean.pipe(Schema$1.withDecodingDefault(Effect.succeed(false))),
9805
9813
  loadBalancingWeights: LoadBalancingWeights.pipe(Schema$1.withDecodingDefault(Effect.succeed({}))),
9806
9814
  appFontId: AppFontId.pipe(Schema$1.withDecodingDefault(Effect.succeed(DEFAULT_APP_FONT_ID))),
@@ -9838,7 +9846,11 @@ const ClientSettingsSchema = Schema$1.Struct({
9838
9846
  wordWrap: Schema$1.Boolean.pipe(Schema$1.withDecodingDefault(Effect.succeed(true)))
9839
9847
  });
9840
9848
  const DEFAULT_CLIENT_SETTINGS = Schema$1.decodeSync(ClientSettingsSchema)({});
9841
- const ThreadEnvMode = Schema$1.Literals(["local", "worktree"]);
9849
+ const ThreadEnvMode = Schema$1.Literals([
9850
+ "auto",
9851
+ "local",
9852
+ "worktree"
9853
+ ]);
9842
9854
  const makeBinaryPathSetting = (fallback) => TrimmedString.pipe(Schema$1.decodeTo(Schema$1.String, SchemaTransformation.transformOrFail({
9843
9855
  decode: (value) => Effect.succeed(value || fallback),
9844
9856
  encode: (value) => Effect.succeed(value)
@@ -10180,7 +10192,7 @@ const ServerSettings = Schema$1.Struct({
10180
10192
  */
10181
10193
  writeGeneratedSkillsToRepo: Schema$1.Boolean.pipe(Schema$1.withDecodingDefault(Effect.succeed(true))),
10182
10194
  automaticGitFetchInterval: Schema$1.DurationFromMillis.pipe(Schema$1.withDecodingDefault(Effect.succeed(Duration.toMillis(DEFAULT_AUTOMATIC_GIT_FETCH_INTERVAL)))),
10183
- defaultThreadEnvMode: ThreadEnvMode.pipe(Schema$1.withDecodingDefault(Effect.succeed("local"))),
10195
+ defaultThreadEnvMode: ThreadEnvMode.pipe(Schema$1.withDecodingDefault(Effect.succeed("auto"))),
10184
10196
  newWorktreesStartFromOrigin: Schema$1.Boolean.pipe(Schema$1.withDecodingDefault(Effect.succeed(true))),
10185
10197
  enableDeviceSupport: Schema$1.Boolean.pipe(Schema$1.withDecodingDefault(Effect.succeed(false))),
10186
10198
  enableAgentDeviceAccess: Schema$1.Boolean.pipe(Schema$1.withDecodingDefault(Effect.succeed(false))),
@@ -10387,6 +10399,9 @@ const ServerSettingsPatch = Schema$1.Struct({
10387
10399
  providerInstances: Schema$1.optionalKey(Schema$1.Record(ProviderInstanceId, ProviderInstanceConfig))
10388
10400
  });
10389
10401
  Schema$1.Struct({
10402
+ fusionPairOrder: Schema$1.optionalKey(Schema$1.Record(Schema$1.String, Schema$1.Array(Schema$1.String))),
10403
+ fusionRecentPairs: Schema$1.optionalKey(Schema$1.Array(FusionModelPair)),
10404
+ fusionFavoritePairs: Schema$1.optionalKey(Schema$1.Array(FusionModelPair)),
10390
10405
  loadBalancingEnabled: Schema$1.optionalKey(Schema$1.Boolean),
10391
10406
  loadBalancingWeights: Schema$1.optionalKey(LoadBalancingWeights),
10392
10407
  appFontId: Schema$1.optionalKey(AppFontId),
@@ -31981,8 +31996,7 @@ Example - "Why does my React component re-render?"
31981
31996
  * Concrete numeric constraints, not adjectives: the compress-mode measurements
31982
31997
  * found vague wording ("aim for fewer lines") recovers to baseline while hard
31983
31998
  * numbers hold across turns. The numbers step down by level so the modes stay
31984
- * visibly distinct. Reminder position stays before the user's message - the
31985
- * after-message placement measured 20% worse on Opus.
31999
+ * visibly distinct. Reminders follow the user's message so the task leads.
31986
32000
  */
31987
32001
  const COMPRESS_TURN_REMINDERS = {
31988
32002
  lite: "[response style: compressed lite - no filler/hedging, full sentences, at most 20 lines unless the answer is code; keep negations and numbers exact; code/commits/security text normal]",
@@ -32004,16 +32018,16 @@ function compressTurnReminderFor(mode) {
32004
32018
  return mode === "off" ? void 0 : COMPRESS_TURN_REMINDERS[mode];
32005
32019
  }
32006
32020
  /**
32007
- * What to prepend to a turn's message so the running session ends up in the
32021
+ * What to append to a turn's message so the running session ends up in the
32008
32022
  * requested compress mode.
32009
32023
  *
32010
32024
  * `staleRulesetMode` is the mode whose ruleset the session is still carrying in
32011
32025
  * a channel that cannot be rebuilt for this turn - Claude's system prompt is
32012
- * frozen at session start, and a first-turn message prefix lives in the
32026
+ * frozen at session start, and a first-turn message suffix lives in the
32013
32027
  * conversation history forever. Codex rebuilds its developer instructions every
32014
32028
  * turn, so callers pass `undefined` for it and no revert line is spent.
32015
32029
  */
32016
- function compressTurnPrefixFor(input) {
32030
+ function compressTurnSuffixFor(input) {
32017
32031
  if (input.injectRuleset) return compressRulesetFor(input.mode);
32018
32032
  const reminder = compressTurnReminderFor(input.mode);
32019
32033
  if (reminder !== void 0) return reminder;
@@ -56065,6 +56079,67 @@ const staticAndDevRouteLayer = HttpRouter.add("GET", "*", Effect.gen(function* (
56065
56079
  });
56066
56080
  }));
56067
56081
  //#endregion
56082
+ //#region src/orchestration/AutoWorkspace.ts
56083
+ const AUTO_WORKSPACE_DECISION_TIMEOUT = Duration.seconds(30);
56084
+ const checkoutAdmissions = PartitionedSemaphore.makeUnsafe({ permits: 1 });
56085
+ /** Shared across connections; independent checkouts can start concurrently. */
56086
+ function withAutoWorkspaceAdmission(cwd, effect) {
56087
+ return checkoutAdmissions.withPermit(NodePath.resolve(cwd))(effect);
56088
+ }
56089
+ function isAutoCheckoutBusy(input) {
56090
+ const pair = input.snapshot.threadPairs?.find((candidate) => candidate.detachedAt === null && (candidate.implementerThreadId === input.threadId || candidate.watcherThreadId === input.threadId));
56091
+ const cwd = NodePath.resolve(input.cwd);
56092
+ const projectRoots = new Map(input.snapshot.projects.map((project) => [project.id, project.workspaceRoot]));
56093
+ return input.snapshot.threads.some((thread) => {
56094
+ if (thread.deletedAt !== null || thread.id === input.threadId || thread.id === pair?.implementerThreadId || thread.id === pair?.watcherThreadId) return false;
56095
+ const root = thread.worktreePath ?? projectRoots.get(thread.projectId);
56096
+ if (!root || NodePath.resolve(root) !== cwd) return false;
56097
+ return thread.latestTurn?.state === "running" || thread.session?.status === "starting" || thread.session?.status === "running";
56098
+ });
56099
+ }
56100
+ const BUSY_CHECKOUT_DECISION = {
56101
+ mode: "worktree",
56102
+ reason: "Another agent is working in the current checkout."
56103
+ };
56104
+ const UNAVAILABLE_DECISION = {
56105
+ mode: "worktree",
56106
+ reason: "The agent could not choose a workspace; using an isolated worktree."
56107
+ };
56108
+ const chooseAutoWorkspace = Effect.fn("chooseAutoWorkspace")(function* (input, textGeneration) {
56109
+ if (!input.isRepo) return {
56110
+ mode: "local",
56111
+ reason: "This project has no Git repository."
56112
+ };
56113
+ if (input.busy) return BUSY_CHECKOUT_DECISION;
56114
+ if (!textGeneration.generateBtwAnswer || !input.modelSelection) return UNAVAILABLE_DECISION;
56115
+ return yield* textGeneration.generateBtwAnswer({
56116
+ cwd: input.cwd,
56117
+ modelSelection: input.modelSelection,
56118
+ context: input.message,
56119
+ question: [
56120
+ "Choose the workspace for this task before its coding agent starts.",
56121
+ "Return exactly local or worktree in the answer field. Do not perform the task.",
56122
+ "No other P4 agent is currently working in this checkout.",
56123
+ `Uncommitted changes are ${input.hasWorkingTreeChanges ? "present" : "absent"}.`,
56124
+ "Prefer local for questions, investigation, small focused edits, and work that needs current uncommitted changes.",
56125
+ "Prefer worktree for independent implementation, broad changes, risky experiments, or possible interference with someone else's edits.",
56126
+ "A new worktree starts from the selected base branch; uncommitted files are not copied.",
56127
+ "Honor an explicit workspace preference in the task. Preserve unrelated changes. If uncertain, choose worktree."
56128
+ ].join("\n")
56129
+ }).pipe(Effect.timeout(AUTO_WORKSPACE_DECISION_TIMEOUT), Effect.map(({ answer }) => {
56130
+ const mode = answer.trim().toLowerCase();
56131
+ if (mode === "local") return {
56132
+ mode,
56133
+ reason: "The agent chose the current checkout for this task."
56134
+ };
56135
+ if (mode === "worktree") return {
56136
+ mode,
56137
+ reason: "The agent chose an isolated worktree for this task."
56138
+ };
56139
+ return UNAVAILABLE_DECISION;
56140
+ }), Effect.catch(() => Effect.succeed(UNAVAILABLE_DECISION)));
56141
+ });
56142
+ //#endregion
56068
56143
  //#region src/provider/ProviderInstanceEnvironment.ts
56069
56144
  function mergeProviderInstanceEnvironment(environment, baseEnv = process.env) {
56070
56145
  if (!environment || environment.length === 0) return baseEnv;
@@ -63500,7 +63575,13 @@ function formatAskUserQuestionAnswers(answers) {
63500
63575
  * only a one-line reference and the mutable `[fusion-pair]` metadata.
63501
63576
  */
63502
63577
  const FUSION_BUILDER_INSTRUCTIONS = `You are Fusion Builder in an already-created native server pair. Server owns pairing and coordination. Do not inspect or invoke the Fusion skill, create/pair/rename threads, or announce/setup Fusion. Start the user's task directly. Before editing, create and maintain the phase list with your provider's step-tracking tool (Claude Code: TaskCreate for each phase, then TaskUpdate for status, or TodoWrite when that is the tool offered; Codex: update_plan), never the MCP task board tools - one entry per phase in order, exactly one in progress at a time, marked completed at each phase end - so phases render in the task banner. That list holds phase entries only for the whole task; keep step-level or per-file todos out of it. Name each phase in 3-6 words by its outcome, never by a command, file path, or flag, because the banner shows the title verbatim. Prose alone leaves the banner empty. Split it into the fewest substantial phases the task genuinely needs plus a final integration/whole-task phase; most tasks need one to three work phases. Each phase is a complete reviewable slice of behavior. Never split per file, per function, or per trivial step: over-splitting spends review turns instead of finishing the job. Add a phase only when a real review boundary, risky decision, or independent behavior separates the work. Complete exactly one phase per turn, and finish the whole phase in that turn rather than stopping early. Do not run tests, typecheck, lint, or builds per phase; write the tests the change needs, then run verification once in the final phase over the whole task. Exception: a phase whose own correctness is unclear may run the single narrowest check that resolves it. End every phase turn with phase completed, todo status, changed behavior/files, and remaining phases; do not start the next phase in the same turn. Server then wakes the paired Supervisor, which resumes you through ${FUSION_ADVICE_PROMPT_PREFIX}; a user message may also revise or resume the work. Final phase verifies the entire task against the original request and labels it ready for whole-task review. Supervisor is unreachable during your turn. Never spawn/use another Supervisor thread/subagent or attribute Supervisor decisions without ${FUSION_ADVICE_PROMPT_PREFIX}. Within the current phase, continue when straightforward or evidence is clear. For a concrete unresolved tradeoff, correctness risk, or design decision materially needing judgment, stop safely before the risky choice; final response states the exact question and why review is needed. Evaluate/follow Supervisor advice unless conflicting with user request or verified repo state.`;
63503
- const FUSION_WATCHER_TOOL_INSTRUCTIONS = `Supervisor tools belong to the p4-code MCP server: thread_watch_review, thread_watch_events, thread_advise, and thread_gate_respond. If absent from the visible tool list, first use the available tool search/discovery facility to load those exact tools, including provider-prefixed names; absence from the initial list does not establish unavailability. If discovery or a call fails, report the exact missing capability or error and the need to restore the p4-code MCP connection. Do not offer manual relay, draft or implement the task yourself, or abandon the supervisor role as a workaround. For an actionable user requirement, read the relevant builder evidence and relay it through thread_advise; when a gate is open, answer through thread_gate_respond instead.`;
63578
+ const FUSION_WATCHER_TOOL_INSTRUCTIONS = `Supervisor tools belong to the p4-code MCP server: thread_watch_review, thread_watch_events, thread_advise, and thread_gate_respond. If absent from the visible tool list, first use the available tool search/discovery facility to load those exact tools, including provider-prefixed names; absence from the initial list does not establish unavailability. If discovery or a call fails, report the exact missing capability or error and the need to restore the p4-code MCP connection. Do not offer manual relay, draft or implement the task yourself, or abandon the supervisor role as a workaround. For an actionable user requirement, read the relevant builder evidence and relay it through thread_advise; when a gate is open, answer through thread_gate_respond instead. Structure every actionable supervisor advice message with these Markdown headings, in order:
63579
+ ## Recommendation
63580
+ State the decision or correction in one sentence. For a phase review, say whether the phase is approved or needs changes.
63581
+ ## Reason
63582
+ Give concise evidence, relevant file/line references, and any unresolved risk. Distinguish observed facts from assumptions; do not claim checks you did not run.
63583
+ ## Next step
63584
+ Give ordered, concrete actions for the builder, including the next phase and its completion condition. Preserve user requirements, constraints, and delivery authorization. Keep each section short; omit empty bullets and repeated status. Formatting does not authorize an otherwise unnecessary advice turn or replace an exact protocol response.`;
63504
63585
  const FUSION_WATCHER_INSTRUCTIONS = `You are Fusion Supervisor (watcher) in an already-created native server pair. Server owns pairing and coordination and wakes you with ${FUSION_REVIEW_PROMPT_PREFIX} or ${FUSION_GATE_PROMPT_PREFIX} prompts at builder turn boundaries. A plain message outside such a wake may arrive after your conversational memory of the pair is gone; its [fusion-pair] metadata block is authoritative: the builder thread exists and is the counterpart thread id. Never report that no builder thread exists. For a new pair, the initial user request is yours: inspect the task and available project context, plan the work, then direct the first actionable phase to the idle builder through thread_advise. Include the original requirements, constraints, and authorized delivery scope in that direction. The builder has not received the initial prompt and must not start before your direction. Missing builder turn evidence is expected before that first direction; do not wait for it or implement the work yourself. Ask the user only when needed to resolve a blocking requirement. ${FUSION_WATCHER_TOOL_INSTRUCTIONS} To resume supervision, read builder evidence with thread_watch_review from lastReviewedImplementerSequence, capture its throughSequence on the first page and reuse that fixed bound while paging with nextAfterSequence until hasMore is false (including empty pages). Apply message append/replace operations by identity; recover truncated evidence through targeted raw thread_watch_events source ranges. Retain reviewed requirements and evidence for final review; recover missing context with targeted history reads rather than mandatory raw replay from zero, derive phase from artifacts (git log/status, PR, builder events, including its turn.plan.updated phase list), steer with thread_advise, and answer an open gate with thread_gate_respond. When a review or gate wake prompt specifies an explicit event range, that range wins over this metadata. Never poll or wait for the builder; deliver review or advice, then end the turn. Every thread_advise starts a builder turn whose completion wakes you again, so never advise a builder that is idle on an external wait or has nothing actionable; report the state in your own thread and end without advising.`;
63505
63586
  /**
63506
63587
  * The one-line stand-in for the full block on a message whose session already
@@ -125225,24 +125306,75 @@ const makeWsRpcLayer = (currentSession, previewAutomationBroker, computerUseBrok
125225
125306
  });
125226
125307
  createdThread = true;
125227
125308
  }
125228
- if (bootstrap?.prepareWorktree) {
125229
- let worktreeBaseRef = bootstrap.prepareWorktree.baseBranch;
125230
- if (bootstrap.prepareWorktree.startFromOrigin) {
125309
+ let prepareWorktree = bootstrap?.prepareWorktree;
125310
+ if (prepareWorktree?.mode === "auto") {
125311
+ const snapshot = yield* projectionSnapshotQuery.getCommandReadModel();
125312
+ const currentThread = snapshot.threads.find((thread) => thread.id === command.threadId);
125313
+ const localStatus = yield* gitWorkflow.localStatus({ cwd: prepareWorktree.projectCwd });
125314
+ let decision = yield* chooseAutoWorkspace({
125315
+ cwd: prepareWorktree.projectCwd,
125316
+ message: command.message.text,
125317
+ isRepo: localStatus.isRepo,
125318
+ busy: isAutoCheckoutBusy({
125319
+ snapshot,
125320
+ threadId: command.threadId,
125321
+ cwd: prepareWorktree.projectCwd
125322
+ }),
125323
+ hasWorkingTreeChanges: localStatus.hasWorkingTreeChanges,
125324
+ modelSelection: command.modelSelection ?? currentThread?.modelSelection
125325
+ }, textGeneration);
125326
+ if (decision.mode === "local" && localStatus.isRepo && isAutoCheckoutBusy({
125327
+ snapshot: yield* projectionSnapshotQuery.getCommandReadModel(),
125328
+ threadId: command.threadId,
125329
+ cwd: prepareWorktree.projectCwd
125330
+ })) decision = BUSY_CHECKOUT_DECISION;
125331
+ yield* orchestrationEngine.dispatch({
125332
+ type: "thread.activity.append",
125333
+ commandId: yield* serverCommandId("auto-workspace-decision"),
125334
+ threadId: command.threadId,
125335
+ activity: {
125336
+ id: yield* serverEventId,
125337
+ tone: "info",
125338
+ kind: "workspace.auto.resolved",
125339
+ turnId: null,
125340
+ summary: decision.mode === "worktree" ? "Auto selected a new worktree" : "Auto selected the current checkout",
125341
+ payload: {
125342
+ mode: decision.mode,
125343
+ reason: decision.reason
125344
+ },
125345
+ createdAt: command.createdAt
125346
+ },
125347
+ createdAt: command.createdAt
125348
+ });
125349
+ if (decision.mode === "local") {
125350
+ yield* orchestrationEngine.dispatch({
125351
+ type: "thread.meta.update",
125352
+ commandId: yield* serverCommandId("auto-workspace-local"),
125353
+ threadId: command.threadId,
125354
+ branch: localStatus.refName,
125355
+ worktreePath: null
125356
+ });
125357
+ prepareWorktree = void 0;
125358
+ }
125359
+ }
125360
+ if (prepareWorktree) {
125361
+ let worktreeBaseRef = prepareWorktree.baseBranch;
125362
+ if (prepareWorktree.startFromOrigin) {
125231
125363
  yield* gitWorkflow.fetchRemote({
125232
- cwd: bootstrap.prepareWorktree.projectCwd,
125364
+ cwd: prepareWorktree.projectCwd,
125233
125365
  remoteName: "origin"
125234
125366
  });
125235
125367
  worktreeBaseRef = (yield* gitWorkflow.resolveRemoteTrackingCommit({
125236
- cwd: bootstrap.prepareWorktree.projectCwd,
125237
- refName: bootstrap.prepareWorktree.baseBranch,
125368
+ cwd: prepareWorktree.projectCwd,
125369
+ refName: prepareWorktree.baseBranch,
125238
125370
  fallbackRemoteName: "origin"
125239
125371
  })).commitSha;
125240
125372
  }
125241
125373
  const worktree = yield* gitWorkflow.createWorktree({
125242
- cwd: bootstrap.prepareWorktree.projectCwd,
125374
+ cwd: prepareWorktree.projectCwd,
125243
125375
  refName: worktreeBaseRef,
125244
- newRefName: bootstrap.prepareWorktree.branch,
125245
- baseRefName: bootstrap.prepareWorktree.baseBranch,
125376
+ newRefName: prepareWorktree.branch,
125377
+ baseRefName: prepareWorktree.baseBranch,
125246
125378
  path: null
125247
125379
  });
125248
125380
  targetWorktreePath = worktree.worktree.path;
@@ -125253,7 +125385,7 @@ const makeWsRpcLayer = (currentSession, previewAutomationBroker, computerUseBrok
125253
125385
  branch: worktree.worktree.refName,
125254
125386
  worktreePath: targetWorktreePath
125255
125387
  });
125256
- if (!bootstrap.createThread) {
125388
+ if (!bootstrap?.createThread) {
125257
125389
  const snapshot = yield* projectionSnapshotQuery.getCommandReadModel();
125258
125390
  const pair = snapshot.threadPairs?.find((candidate) => candidate.detachedAt === null && candidate.watcherThreadId === command.threadId);
125259
125391
  const builder = pair && snapshot.threads.find((thread) => thread.id === pair.implementerThreadId && thread.deletedAt === null);
@@ -125274,7 +125406,7 @@ const makeWsRpcLayer = (currentSession, previewAutomationBroker, computerUseBrok
125274
125406
  if (Cause.hasInterruptsOnly(cause)) return Effect.fail(dispatchError);
125275
125407
  return cleanupCreatedThread().pipe(Effect.flatMap(() => Effect.fail(dispatchError)));
125276
125408
  }));
125277
- });
125409
+ }).pipe((effect) => command.bootstrap?.prepareWorktree?.mode === "auto" ? withAutoWorkspaceAdmission(command.bootstrap.prepareWorktree.projectCwd, effect) : effect);
125278
125410
  const dispatchNormalizedCommand = (normalizedCommand) => {
125279
125411
  const dispatchEffect = normalizedCommand.type === "thread.turn.start" && normalizedCommand.bootstrap ? dispatchBootstrapTurnStart(normalizedCommand) : orchestrationEngine.dispatch(normalizedCommand).pipe(Effect.mapError((cause) => toDispatchCommandError(cause, "Failed to dispatch orchestration command")));
125280
125412
  return startup.enqueueCommand(dispatchEffect).pipe(Effect.mapError((cause) => toDispatchCommandError(cause, "Failed to dispatch orchestration command")));
@@ -133209,18 +133341,18 @@ const make$5 = Effect.gen(function* () {
133209
133341
  const rebuildsRulesetEachTurn = activeSession?.provider === "codex";
133210
133342
  const sessionRulesetMode = hadActiveSession && !rebuildsRulesetEachTurn ? threadSessionRulesetModes.get(input.threadId) : void 0;
133211
133343
  const levelChangedMidSession = compressMode !== "off" && sessionRulesetMode !== void 0 && sessionRulesetMode !== compressMode;
133212
- const compressPrefix = compressTurnPrefixFor({
133344
+ const compressSuffix = compressTurnSuffixFor({
133213
133345
  mode: compressMode,
133214
133346
  injectRuleset: !hadActiveSession && !hasSessionLevelRuleset || levelChangedMidSession,
133215
133347
  staleRulesetMode: sessionRulesetMode
133216
133348
  });
133217
133349
  if (levelChangedMidSession) threadSessionRulesetModes.set(input.threadId, compressMode);
133218
133350
  const chatInput = isChatProject(thread.projectId) && activeSession?.provider !== "opencode" && activeSession?.provider !== "claudeAgent" && thread.messages.filter((message) => message.role === "user").length === 1 ? [P4_CHAT_SYSTEM_PROMPT, inputWithStructuredQuestionPolicy].filter(Boolean).join("\n\n") : inputWithStructuredQuestionPolicy;
133219
- const inputWithCompressPrefix = chatInput !== void 0 && compressPrefix !== void 0 ? `${compressPrefix}\n\n${chatInput}` : chatInput;
133351
+ const inputWithCompressSuffix = chatInput !== void 0 && compressSuffix !== void 0 ? `${chatInput}\n\n${compressSuffix}` : chatInput;
133220
133352
  return {
133221
133353
  threadId: input.threadId,
133222
133354
  ...isChatProject(thread.projectId) && activeSession?.provider === "opencode" ? { systemPrompt: P4_CHAT_SYSTEM_PROMPT } : {},
133223
- ...inputWithCompressPrefix ? { input: inputWithCompressPrefix } : {},
133355
+ ...inputWithCompressSuffix ? { input: inputWithCompressSuffix } : {},
133224
133356
  ...providerAttachments.length > 0 ? { attachments: providerAttachments } : {},
133225
133357
  ...modelForTurn !== void 0 ? { modelSelection: modelForTurn } : {},
133226
133358
  ...input.interactionMode !== void 0 ? { interactionMode: input.interactionMode } : {},
@@ -133288,6 +133420,14 @@ const make$5 = Effect.gen(function* () {
133288
133420
  threadId: input.threadId,
133289
133421
  title: generated.title
133290
133422
  });
133423
+ if (input.watcherThreadId !== void 0) {
133424
+ if ((yield* resolveThread(input.watcherThreadId))?.title === `Review: ${input.titleSeed ?? DEFAULT_THREAD_TITLE}`) yield* orchestrationEngine.dispatch({
133425
+ type: "thread.meta.update",
133426
+ commandId: yield* serverCommandId("thread-title-rename"),
133427
+ threadId: input.watcherThreadId,
133428
+ title: `Review: ${generated.title}`
133429
+ });
133430
+ }
133291
133431
  }).pipe(Effect.catchCause((cause) => Effect.logWarning("provider command reactor failed to generate or rename thread title", {
133292
133432
  threadId: input.threadId,
133293
133433
  cwd: input.cwd,
@@ -133332,8 +133472,13 @@ const make$5 = Effect.gen(function* () {
133332
133472
  worktreePath: thread.worktreePath,
133333
133473
  ...generationInput
133334
133474
  }).pipe(Effect.forkScoped);
133335
- if (canReplaceThreadTitle(thread.title, event.payload.titleSeed)) yield* maybeGenerateThreadTitleForFirstTurn({
133336
- threadId: event.payload.threadId,
133475
+ const commandReadModel = yield* projectionSnapshotQuery.getCommandReadModel();
133476
+ const pair = findActiveFusionPair(commandReadModel.threadPairs, thread.id);
133477
+ const builder = pair?.watcherThreadId === thread.id ? yield* resolveThread(pair.implementerThreadId) : void 0;
133478
+ const titleThread = builder && !builder.messages.some((entry) => entry.role === "user") ? builder : thread;
133479
+ if (canReplaceThreadTitle(titleThread.title, event.payload.titleSeed)) yield* maybeGenerateThreadTitleForFirstTurn({
133480
+ threadId: titleThread.id,
133481
+ ...titleThread.id !== thread.id ? { watcherThreadId: thread.id } : {},
133337
133482
  cwd: generationCwd,
133338
133483
  ...generationInput
133339
133484
  }).pipe(Effect.forkScoped);
Binary file
@@ -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{$l as i,Ba as a,Et as o,Fa as s,Ga as c,Il as l,Ja as ee,Ka as te,Ma as u,Rr as d,Ur as ne,Uu as f,Ya as p,Yr as re,_n as m,au as h,b as ie,dl as g,fl as _,gt as v,gu as ae,h as y,ht as b,ml as x,pl as oe,qa as S,st as C,uu as se,vt as w,xn as T,za as E}from"./previewAssetResource-DZZapyaR.js";import{a as D,c as O,i as k,n as A,o as j,r as M,s as ce,t as N}from"./toggle-group-B5Gkl6L5.js";import{F as le,Fr as ue,I as de,J as fe,L as pe,Lr as me,Mr as he,Nr as ge,R as P,Rr as _e,Y as ve,_ as ye,_r as be,at as xe,cr as Se,ct as Ce,dr as we,fr as Te,h as Ee,hr as De,it as Oe,jr as ke,lr as Ae,lt as je,oi as Me,or as Ne,ot as Pe,pr as Fe,rt as Ie,si as Le,sr as Re,st as ze,ur as Be,yr as Ve}from"./index-BopiBtUj.js";import{a as He,n as Ue}from"./fileCommentAnnotations-BfBnLApA.js";var We=x(`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}C.getState().openFile(e,t);return}r(n?y(t,n):t)}var I=r();function Ke(e,t){let n=(0,I.c)(4),r=De(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=d(Qe),te=d(B),u;t[0]===a?u=t[1]:(u=e=>e.getComposerDraft(a)?.reviewComments??qe,t[0]=a,t[1]=u);let f=d(u),[p,m]=(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]!==f||t[7]!==r){let e;t[9]!==h||t[10]!==f||t[11]!==r?(e=e=>{let{fileDiff:t,filePath:n,fileKey:i,collapsed:a}=e,o=f.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:Oe(`${a?`1`:`0`}:${s.flatMap(z).join(`:`)}`)}},t[9]=h,t[10]=f,t[11]=r,t[12]=e):e=t[12],v=n.map(e),t[4]=h,t[5]=n,t[6]=f,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=>{m(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),m(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 oe=x,S;t[24]!==_||t[25]!==r||t[26]!==i?(S=(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]=S):S=t[27];let C=S,se=h!==null,w;t[28]===s?w=t[29]:(w=s?{ref:s}:{},t[28]=s,t[29]=w);let T;t[30]===c?T=t[31]:(T=c?{className:c}:{},t[30]=c,t[31]=T);let E=!se,O=!se,k;t[32]!==C||t[33]!==o||t[34]!==O||t[35]!==E?(k={...o,enableGutterUtility:E,enableLineSelection:O,onLineSelectionEnd:C},t[32]=C,t[33]=o,t[34]=O,t[35]=E,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]!==oe?(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=>oe(e.id,t),onDelete:()=>b(e.id)},e.id))}),t[39]=b,t[40]=oe,t[41]=j):j=t[41];let M;return t[42]!==ae||t[43]!==p||t[44]!==k||t[45]!==A||t[46]!==j||t[47]!==w||t[48]!==T?(M=(0,L.jsx)(D,{...w,...T,items:ae,selectedLines:p,onSelectedLinesChange:m,options:k,renderHeaderPrefix:A,renderAnnotation:j}),t[42]=ae,t[43]=p,t[44]=k,t[45]=A,t[46]=j,t[47]=w,t[48]=T,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:i(e,{label:`environment-data:review:diff-preview`,tag:f.reviewGetDiffPreview,staleTimeMs:5e3})}}var et=$e(T);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:i}=ie(),d=ue(),[ne]=(0,F.useState)(n),[f,re]=(0,F.useState)(`stacked`),[y,x]=(0,F.useState)(d.wordWrap),[C,T]=(0,F.useState)(d.diffIgnoreWhitespace),[D,ye]=(0,F.useState)(``),[De,Oe]=(0,F.useState)(()=>({scopeKey:null,fileKeys:rt})),He=(0,F.useRef)(null),Ue=r.threadId,I=ge(r),qe=I?.projectId??null,Je=he(I&&qe?{environmentId:I.environmentId,projectId:qe}:null),R=I?.worktreePath??Je?.workspaceRoot,z=l(m.configValueAtom(I?.environmentId??null)),Xe=be(I?.environmentId??null,z?.availableEditors??[]),Ze=o(I!=null&&R!=null?ke.status({environmentId:I.environmentId,input:{cwd:R}}):null),B=P(e=>pe(e.byThreadKey,r,ne===`unstaged`)),Qe=Ze.data?.isRepo??!0,{turnDiffSummaries:$e,inferredCheckpointTurnCountByTurnId:V}=de(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`&&P.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=De.scopeKey===Y?De.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:C,cacheScope:q?`turn:${q.turnId}`:null},{enabled:Qe&&q!==void 0}),mt=o(W===null&&I&&R?et.diffPreview({environmentId:I.environmentId,input:{cwd:R,...K?{baseRef:K}:{},ignoreWhitespace:C}}):null),ht=W===null&&mt.error?.includes(`configured workspace root`)===!0&&z?.cwd!==void 0&&z.cwd!==R,gt=o(ht&&I&&z?et.diffPreview({environmentId:I.environmentId,input:{cwd:z.cwd,...K?{baseRef:K}:{},ignoreWhitespace:C}}):null),X=ht?gt:mt,Z=X.data?.sources.find(e=>e.kind===(G===`unstaged`?`working-tree`:`branch-range`)),_t=o(W===null&&G===`branch`&&I&&X.data?.cwd?ke.listRefs({environmentId:I.environmentId,input:{cwd:X.data.cwd,includeMatchingRemoteRefs:!0,refKind:`local`,...D.trim().length>0?{query:D.trim()}:{},limit:100}}):null),vt=o(W===null&&G===`branch`&&I&&X.data?.cwd?ke.listRefs({environmentId:I.environmentId,input:{cwd:X.data.cwd,includeMatchingRemoteRefs:!0,refKind:`remote`,...D.trim().length>0?{query:D.trim()}:{},limit:100}}):null),yt=tt(_t.data?.refs.filter(e=>e.name!==Z?.headRef)??[],vt.data?.refs??[]),bt=nt(yt,D),xt=e=>K&&K===e.remote?.name?K:e.local?.name??e.remote?.name??e.id,St=[H,...yt.map(xt)],Ct=[...D.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)(()=>ze(Tt,`diff-panel:${i}`,{compactPartialHunkOffsets:W===null}),[i,Tt,W]),At=(0,F.useMemo)(()=>!Q||Q.kind!==`files`?[]:Q.files.toSorted((e,t)=>je(e).localeCompare(je(t),void 0,{numeric:!0,sensitivity:`base`})),[Q]),$=(0,F.useMemo)(()=>At.map(e=>{let t=Ie(e);return{fileDiff:e,filePath:je(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)(()=>Pe(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=le({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`&&!h(t)&&console.warn(`Failed to open diff file in editor.`,{operation:`open-diff-file`,environmentId:r.environmentId,threadId:r.threadId,...ae(se(t))})})()}})},[R,Pt,Xe,r]),It=(0,F.useCallback)(e=>{Oe(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)(()=>{Oe(e=>{let t=e.scopeKey===Y?e.fileKeys:rt;return{scopeKey:Y,fileKeys:k(jt,t)}})},[Y,jt]),Rt=e=>{P.getState().selectTurn(r,e)},zt=e=>{P.getState().selectGitScope(r,e)},Bt=e=>{P.getState().selectBranchBaseRef(r,e)};return(0,L.jsx)(ve,{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)(s,{children:[(0,L.jsxs)(ee,{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)(_,{className:`size-3.5 shrink-0 text-muted-foreground`})]}),(0,L.jsxs)(a,{align:`start`,className:`w-60`,children:[(0,L.jsx)(E,{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)(E,{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)(E,{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)(c,{children:[(0,L.jsx)(S,{children:`Turn`}),(0,L.jsx)(te,{className:`w-64`,children:U.map(e=>{let t=e.checkpointTurnCount??V[e.turnId]??`?`;return(0,L.jsxs)(E,{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,d.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)(Ne,{items:St,filteredItems:Ct,value:K??H,onOpenChange:e=>{e||ye(``)},onValueChange:e=>{e&&Bt(e===H?null:e)},children:[(0,L.jsxs)(Te,{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)(_,{className:`size-3.5 shrink-0 opacity-70`})]}),(0,L.jsxs)(we,{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)(_e,{"aria-hidden":`true`,className:`pointer-events-none absolute top-1.5 left-0 size-4 shrink-0 text-muted-foreground/55`}),(0,L.jsx)(Se,{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:D,onChange:e=>ye(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)(Re,{children:`No matching refs.`}),(0,L.jsxs)(Be,{className:`max-h-64 min-w-0 overflow-x-hidden`,children:[(0,L.jsx)(Ae,{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)(Ae,{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)(Fe,{"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)(oe,{"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)(Ee,{additions:Nt.additions,deletions:Nt.deletions,className:`mr-1 text-[11px]`,layout:`inline`}),$.length>0&&(0,L.jsxs)(b,{children:[(0,L.jsx)(w,{render:(0,L.jsx)(u,{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)(O,{className:`size-3`})}),(0,L.jsx)(v,{side:`top`,children:Mt?`Expand all files`:`Collapse all files`})]}),(0,L.jsxs)(A,{className:`shrink-0`,variant:`outline`,size:`xs`,value:[f],onValueChange:e=>{let t=e[0];(t===`stacked`||t===`split`)&&re(t)},children:[(0,L.jsx)(N,{"aria-label":`Stacked diff view`,value:`stacked`,children:(0,L.jsx)(j,{className:`size-3`})}),(0,L.jsx)(N,{"aria-label":`Split diff view`,value:`split`,children:(0,L.jsx)(ce,{className:`size-3`})})]}),(0,L.jsxs)(b,{children:[(0,L.jsx)(w,{render:(0,L.jsx)(N,{"aria-label":y?`Disable diff line wrapping`:`Enable diff line wrapping`,variant:`outline`,size:`xs`,pressed:y,onPressedChange:e=>{x(!!e)}}),children:(0,L.jsx)(me,{className:`size-3`})}),(0,L.jsx)(v,{side:`top`,children:y?`Disable line wrapping`:`Enable line wrapping`})]}),(0,L.jsxs)(b,{children:[(0,L.jsx)(w,{render:(0,L.jsx)(N,{"aria-label":C?`Show whitespace changes`:`Hide whitespace changes`,variant:`outline`,size:`xs`,pressed:C,onPressedChange:e=>{T(!!e)}}),children:(0,L.jsx)(We,{className:`size-3`})}),(0,L.jsx)(v,{side:`top`,children:C?`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=je(e);return(0,L.jsxs)(b,{children:[(0,L.jsx)(w,{render:(0,L.jsx)(`button`,{type:`button`,className:p(`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)(g,{className:`size-4`}):(0,L.jsx)(_,{className:`size-4`})}),(0,L.jsx)(v,{side:`top`,children:n?`Expand diff`:`Collapse diff`})]})},options:{diffStyle:f===`split`?`split`:`unified`,lineDiffType:`none`,overflow:y?`wrap`:`scroll`,theme:Ce(i),themeType:i,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:p(`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{ye as DiffWorkerPoolProvider,U as default};
98
+ //# sourceMappingURL=DiffPanel-ByJrw-zk.js.map