@p4code/cli 0.3.26 → 0.3.28

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
@@ -239,7 +239,7 @@ const make$92 = () => {
239
239
  const layer$82 = Layer.sync(NetService, make$92);
240
240
  //#endregion
241
241
  //#region package.json
242
- var version = "0.3.26";
242
+ var version = "0.3.28";
243
243
  //#endregion
244
244
  //#region src/config.ts
245
245
  /**
@@ -6620,6 +6620,8 @@ Schema$1.Struct({
6620
6620
  updatedAt: IsoDateTime,
6621
6621
  lastError: Schema$1.optional(TrimmedNonEmptyString)
6622
6622
  });
6623
+ /** Which half of a Fusion pair a session or turn belongs to. */
6624
+ const FusionRole = Schema$1.Literals(["implementer", "watcher"]);
6623
6625
  const ProviderSessionStartInput = Schema$1.Struct({
6624
6626
  threadId: ThreadId,
6625
6627
  provider: Schema$1.optional(ProviderDriverKind),
@@ -6631,7 +6633,13 @@ const ProviderSessionStartInput = Schema$1.Struct({
6631
6633
  sandboxMode: Schema$1.optional(ProviderSandboxMode),
6632
6634
  runtimeMode: RuntimeMode,
6633
6635
  compressMode: Schema$1.optional(CompressMode),
6634
- unpromptedSubagents: Schema$1.optional(Schema$1.Boolean)
6636
+ unpromptedSubagents: Schema$1.optional(Schema$1.Boolean),
6637
+ /**
6638
+ * Set when the thread is half of an active Fusion pair at session start, so
6639
+ * providers with a session-level instruction channel carry the role rules
6640
+ * there instead of on every message.
6641
+ */
6642
+ fusionRole: Schema$1.optional(FusionRole)
6635
6643
  });
6636
6644
  const ProviderSendTurnInput = Schema$1.Struct({
6637
6645
  threadId: ThreadId,
@@ -6641,7 +6649,8 @@ const ProviderSendTurnInput = Schema$1.Struct({
6641
6649
  modelSelection: Schema$1.optional(ModelSelection),
6642
6650
  interactionMode: Schema$1.optional(ProviderInteractionMode),
6643
6651
  compressMode: Schema$1.optional(CompressMode),
6644
- unpromptedSubagents: Schema$1.optional(Schema$1.Boolean)
6652
+ unpromptedSubagents: Schema$1.optional(Schema$1.Boolean),
6653
+ fusionRole: Schema$1.optional(FusionRole)
6645
6654
  });
6646
6655
  Schema$1.Struct({
6647
6656
  threadId: ThreadId,
@@ -8499,6 +8508,14 @@ const ServerSettings = Schema$1.Struct({
8499
8508
  */
8500
8509
  enableUnpromptedSubagents: Schema$1.Boolean.pipe(Schema$1.withDecodingDefault(Effect.succeed(true))),
8501
8510
  /**
8511
+ * Beta. Whether Fusion role rules travel once through the provider's
8512
+ * session channel (Claude's system prompt, Codex's developer instructions)
8513
+ * with a one-line reference on each message, or the legacy way: the full
8514
+ * block prepended to every applicable message. On keeps thread context
8515
+ * smaller; off is the rollback if a pair misbehaves.
8516
+ */
8517
+ enableOptimizedFusionPromptDelivery: Schema$1.Boolean.pipe(Schema$1.withDecodingDefault(Effect.succeed(true))),
8518
+ /**
8502
8519
  * Whether the board offers the scoping agent: the Scope action on a task and
8503
8520
  * the panel that drafts a new one by interview.
8504
8521
  *
@@ -8686,6 +8703,7 @@ const ServerSettingsPatch = Schema$1.Struct({
8686
8703
  enableVerificationBeforeCompletion: Schema$1.optionalKey(Schema$1.Boolean),
8687
8704
  enableRootCauseBeforeFix: Schema$1.optionalKey(Schema$1.Boolean),
8688
8705
  enableUnpromptedSubagents: Schema$1.optionalKey(Schema$1.Boolean),
8706
+ enableOptimizedFusionPromptDelivery: Schema$1.optionalKey(Schema$1.Boolean),
8689
8707
  enableScopingAgent: Schema$1.optionalKey(Schema$1.Boolean),
8690
8708
  enablePlanPhase: Schema$1.optionalKey(Schema$1.Boolean),
8691
8709
  writeGeneratedSkillsToRepo: Schema$1.optionalKey(Schema$1.Boolean),
@@ -69122,6 +69140,19 @@ const makeClaudeTextGeneration = Effect.fn("makeClaudeTextGeneration")(function*
69122
69140
  };
69123
69141
  });
69124
69142
  //#endregion
69143
+ //#region ../../packages/shared/src/String.ts
69144
+ /**
69145
+ * Drop a leaked tool-call tag from a plan step title. A mis-closed
69146
+ * `<parameter name="subject">` call swallows `</subject> <parameter
69147
+ * name="description">...` into the title; everything from the first closing
69148
+ * or parameter tag onward is noise the banner must not render.
69149
+ */
69150
+ function cleanPlanStepTitle(title) {
69151
+ const cut = title.search(/<\/[a-z_-]+>\s*(?=<parameter\b)|<parameter\b/i);
69152
+ const cleaned = (cut === -1 ? title : title.slice(0, cut)).trim();
69153
+ return cleaned.length > 0 ? cleaned : title.trim();
69154
+ }
69155
+ //#endregion
69125
69156
  //#region ../../packages/shared/src/cliArgs.ts
69126
69157
  function tokenizeCliArgs(args) {
69127
69158
  const input = args?.trim();
@@ -69261,6 +69292,40 @@ function formatAskUserQuestionAnswers(answers) {
69261
69292
  return formatted;
69262
69293
  }
69263
69294
  //#endregion
69295
+ //#region src/provider/FusionPrompts.ts
69296
+ /**
69297
+ * Role instructions for the two halves of a Fusion pair.
69298
+ *
69299
+ * They travel through the provider's session channel - Claude's system prompt
69300
+ * append, Codex's per-turn developer instructions - rather than as a prefix on
69301
+ * every message. The block is about 650 tokens; a message-level copy sits in
69302
+ * the conversation history for the rest of the thread, is re-read on every
69303
+ * later API call, and is lost at compaction anyway. The session channel is
69304
+ * cached, never accumulates, and survives compaction. Messages then carry
69305
+ * only a one-line reference and the mutable `[fusion-pair]` metadata.
69306
+ */
69307
+ 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.`;
69308
+ 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. To resume supervision, read builder events with thread_watch_events from lastReviewedImplementerSequence with limit 50, paging forward with the last returned sequence rather than requesting a whole range at once, 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.`;
69309
+ /**
69310
+ * The one-line stand-in for the full block on a message whose session already
69311
+ * carries the role instructions.
69312
+ */
69313
+ const FUSION_BUILDER_REFERENCE_LINE = "[fusion-builder] Fusion Builder rules in your session instructions still apply.";
69314
+ const FUSION_WATCHER_REFERENCE_LINE = "[fusion-watcher] Plain message outside a server wake; Fusion Supervisor rules in your session instructions still apply.";
69315
+ /**
69316
+ * Sent on every turn of a thread whose session still carries role
69317
+ * instructions after its pair was detached. Claude's system prompt is frozen
69318
+ * at session start, so the block cannot be removed; it is countered instead,
69319
+ * the same way a switched-off compress ruleset is.
69320
+ */
69321
+ const FUSION_DETACHED_REMINDER = "[fusion-detached] The Fusion pair ended. Fusion role rules in your session instructions no longer apply; work as a normal thread.";
69322
+ function fusionRoleInstructionsFor(role) {
69323
+ return role === "implementer" ? FUSION_BUILDER_INSTRUCTIONS : FUSION_WATCHER_INSTRUCTIONS;
69324
+ }
69325
+ function fusionRoleReferenceLineFor(role) {
69326
+ return role === "implementer" ? FUSION_BUILDER_REFERENCE_LINE : FUSION_WATCHER_REFERENCE_LINE;
69327
+ }
69328
+ //#endregion
69264
69329
  //#region src/provider/GuardrailPrompts.ts
69265
69330
  /** Fresh-evidence gate adapted from superpowers' verification skill. */
69266
69331
  const VERIFY_BEFORE_COMPLETION_PROMPT = "NO COMPLETION CLAIMS WITHOUT FRESH VERIFICATION EVIDENCE. Before claiming complete, fixed, or passing: 1) identify proving command; 2) run it fresh and fully; 3) read full output, exit code, failure count; 4) confirm evidence matches claim; 5) state claim with evidence. Missing or failed proof: report actual status.";
@@ -69804,7 +69869,7 @@ function extractPlanStepsFromTodoInput(input) {
69804
69869
  const todos = input.todos;
69805
69870
  if (!Array.isArray(todos) || todos.length === 0) return null;
69806
69871
  return todos.filter((t) => t !== null && typeof t === "object").map((todo) => ({
69807
- step: typeof todo.content === "string" && todo.content.trim().length > 0 ? todo.content.trim() : "Task",
69872
+ step: typeof todo.content === "string" && todo.content.trim().length > 0 ? cleanPlanStepTitle(todo.content) : "Task",
69808
69873
  status: todo.status === "completed" ? "completed" : todo.status === "in_progress" ? "inProgress" : "pending"
69809
69874
  }));
69810
69875
  }
@@ -69834,6 +69899,9 @@ function isClaudeTaskTool(toolName) {
69834
69899
  function normalizeClaudeTaskStatus(value) {
69835
69900
  return value === "completed" ? "completed" : value === "in_progress" ? "inProgress" : "pending";
69836
69901
  }
69902
+ function cleanStepSubject(value) {
69903
+ return value === void 0 ? void 0 : cleanPlanStepTitle(value);
69904
+ }
69837
69905
  function readString(value) {
69838
69906
  return typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
69839
69907
  }
@@ -69860,7 +69928,7 @@ function applyClaudeTaskToolResult(tasks, tool, result) {
69860
69928
  if (entry === null || typeof entry !== "object" || Array.isArray(entry)) continue;
69861
69929
  const task = entry;
69862
69930
  const id = readString(task.id);
69863
- const subject = readString(task.subject);
69931
+ const subject = cleanStepSubject(readString(task.subject));
69864
69932
  if (!id || !subject) continue;
69865
69933
  tasks.set(id, {
69866
69934
  id,
@@ -69874,7 +69942,7 @@ function applyClaudeTaskToolResult(tasks, tool, result) {
69874
69942
  if (tool.toolName === "TaskCreate") {
69875
69943
  const resultTask = readClaudeTaskFromResult(result);
69876
69944
  const id = readString(resultTask?.id);
69877
- const subject = readString(resultTask?.subject) ?? readString(tool.input.subject);
69945
+ const subject = cleanStepSubject(readString(resultTask?.subject) ?? readString(tool.input.subject));
69878
69946
  if (!id || !subject) return false;
69879
69947
  tasks.set(id, {
69880
69948
  id,
@@ -69888,7 +69956,7 @@ function applyClaudeTaskToolResult(tasks, tool, result) {
69888
69956
  if (!taskId) return false;
69889
69957
  const task = tasks.get(taskId);
69890
69958
  if (!task) return false;
69891
- const subject = readString(tool.input.subject);
69959
+ const subject = cleanStepSubject(readString(tool.input.subject));
69892
69960
  if (subject && task.subject !== subject) {
69893
69961
  task.subject = subject;
69894
69962
  changed = true;
@@ -71928,7 +71996,8 @@ const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (claudeSettin
71928
71996
  ...narrateBeforeTools ? [NARRATE_BEFORE_TOOLS_PROMPT] : [],
71929
71997
  ...guardrailPromptsFor(guardrailSettings),
71930
71998
  unpromptedSubagents ? SUBAGENTS_ALLOWED_PROMPT : SUBAGENTS_ON_REQUEST_PROMPT,
71931
- ...compressRuleset !== void 0 ? [compressRuleset] : []
71999
+ ...compressRuleset !== void 0 ? [compressRuleset] : [],
72000
+ ...input.fusionRole !== void 0 ? [fusionRoleInstructionsFor(input.fusionRole)] : []
71932
72001
  ].join("\n\n");
71933
72002
  const compressionSubagentHook = async (hookInput) => {
71934
72003
  if (hookInput.hook_event_name !== "SubagentStart") return {};
@@ -91582,13 +91651,14 @@ ${P4_CODE_BROWSER_TOOL_INSTRUCTIONS}
91582
91651
  function toSingleLine(value) {
91583
91652
  return value.replaceAll(/\s+/g, " ").trim();
91584
91653
  }
91585
- function buildCodexDeveloperInstructions(interactionMode, runtime, compressMode, guardrailSettings) {
91654
+ function buildCodexDeveloperInstructions(interactionMode, runtime, compressMode, guardrailSettings, fusionRole) {
91586
91655
  const base = interactionMode === "plan" ? CODEX_PLAN_MODE_DEVELOPER_INSTRUCTIONS : CODEX_DEFAULT_MODE_DEVELOPER_INSTRUCTIONS;
91587
91656
  const compressRuleset = compressRulesetFor(compressMode ?? "off");
91588
91657
  return [
91589
91658
  base,
91590
91659
  ...guardrailPromptsFor(guardrailSettings),
91591
91660
  ...compressRuleset === void 0 ? [] : [`<response_style>${compressRuleset}</response_style>`],
91661
+ ...fusionRole === void 0 ? [] : [`<fusion_role>${fusionRoleInstructionsFor(fusionRole)}</fusion_role>`],
91592
91662
  `<runtime_info>In case you're asked: you are running in P4Code through the Codex harness, as ${toSingleLine(runtime.model)} with ${toSingleLine(runtime.reasoningEffort)} reasoning effort. No need to mention this otherwise.</runtime_info>`
91593
91663
  ].join("\n\n");
91594
91664
  }
@@ -91720,7 +91790,7 @@ function buildCodexCollaborationMode(input) {
91720
91790
  developer_instructions: buildCodexDeveloperInstructions(input.interactionMode, {
91721
91791
  model,
91722
91792
  reasoningEffort
91723
- }, input.compressMode, input.guardrailPrompts)
91793
+ }, input.compressMode, input.guardrailPrompts, input.fusionRole)
91724
91794
  }
91725
91795
  };
91726
91796
  }
@@ -91740,7 +91810,8 @@ function buildTurnStartParams(input) {
91740
91810
  ...input.compressMode ? { compressMode: input.compressMode } : {},
91741
91811
  ...input.model ? { model: input.model } : {},
91742
91812
  ...input.effort ? { effort: input.effort } : {},
91743
- ...input.guardrailPrompts ? { guardrailPrompts: input.guardrailPrompts } : {}
91813
+ ...input.guardrailPrompts ? { guardrailPrompts: input.guardrailPrompts } : {},
91814
+ ...input.fusionRole ? { fusionRole: input.fusionRole } : {}
91744
91815
  });
91745
91816
  const compressRulesetUndeliverable = input.compressMode !== void 0 && input.compressMode !== "off" && collaborationMode === void 0;
91746
91817
  return decodeCodexTurnStartParamsWithCollaborationMode({
@@ -93486,6 +93557,7 @@ const makeCodexAdapter = Effect.fn("makeCodexAdapter")(function* (codexConfig, o
93486
93557
  ...serviceTier ? { serviceTier } : {},
93487
93558
  ...input.interactionMode !== void 0 ? { interactionMode: input.interactionMode } : {},
93488
93559
  ...input.compressMode !== void 0 ? { compressMode: input.compressMode } : {},
93560
+ ...input.fusionRole !== void 0 ? { fusionRole: input.fusionRole } : {},
93489
93561
  ...codexAttachments.length > 0 ? { attachments: codexAttachments } : {}
93490
93562
  }).pipe(Effect.mapError((cause) => mapCodexRuntimeError(input.threadId, "turn/start", cause)));
93491
93563
  });
@@ -108791,9 +108863,20 @@ const DEFAULT_RUNTIME_MODE = "full-access";
108791
108863
  const DEFAULT_THREAD_TITLE = "New thread";
108792
108864
  const NON_SYSTEM_PROVIDER_STRUCTURED_USER_QUESTIONS = structuredUserQuestionPrompt("your provider's structured user-input question tool");
108793
108865
  const FUSION_PROMOTION_INSTRUCTIONS = `Work independently in this normal thread. Fusion is a silent escalation path, not a startup procedure. Do not inspect Fusion tools/skill, mention Fusion status, or announce that Fusion was not invoked. First analyze the task normally. Only if that analysis reveals a concrete unresolved tradeoff, correctness risk, or design decision materially needing a second opinion, stop before implementation, propose Fusion, and ask the user for explicit approval. The user may approve with ordinary affirmative text such as "approved"; /fusion or $fusion also authorizes Fusion directly without a prior proposal. Do not activate, spawn, or promote until one of those authorizations arrives. UI work, complex logic, task size, unfamiliarity, or duration alone never qualifies.`;
108794
- 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. 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.`;
108795
- 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; this message arrived outside such a wake, so your conversational memory of the pair may be gone. The pair metadata below is authoritative: the builder thread exists and is the counterpart thread id. Never report that no builder thread exists. To resume supervision, read builder events with thread_watch_events from lastReviewedImplementerSequence with limit 50, paging forward with the last returned sequence rather than requesting a whole range at once, 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.`;
108796
108866
  const isFusionWatcherWakeMessageId = (messageId) => messageId.startsWith("fusion-review:") || messageId.startsWith("fusion-gate:");
108867
+ const findActiveFusionPair = (pairs, threadId) => (pairs ?? []).find((pair) => pair.detachedAt === null && (pair.implementerThreadId === threadId || pair.watcherThreadId === threadId));
108868
+ const fusionRoleForThread = (pairs, threadId) => {
108869
+ const pair = findActiveFusionPair(pairs, threadId);
108870
+ return pair === void 0 ? void 0 : pair.implementerThreadId === threadId ? "implementer" : "watcher";
108871
+ };
108872
+ /**
108873
+ * Providers whose session channel is filled once, at session start, and then
108874
+ * frozen: Claude's system prompt append. Only these need the reactor to
108875
+ * remember what the live session carries. Codex rebuilds its developer
108876
+ * instructions from every turn, and every other provider gets the block on
108877
+ * each message.
108878
+ */
108879
+ const providerFreezesFusionRoleAtSessionStart = (provider) => provider === "claudeAgent";
108797
108880
  const fusionPairContext = (pair, role) => {
108798
108881
  const counterpartThreadId = role === "implementer" ? pair.watcherThreadId : pair.implementerThreadId;
108799
108882
  return [
@@ -108911,6 +108994,21 @@ const make$4 = Effect.gen(function* () {
108911
108994
  * this is how the reactor knows there is something to counter.
108912
108995
  */
108913
108996
  const threadSessionRulesetModes = /* @__PURE__ */ new Map();
108997
+ /**
108998
+ * The Fusion role whose instructions the thread's live session was handed
108999
+ * at start, for the same reason as the ruleset above: Claude bakes them into
109000
+ * a frozen system prompt. A session that predates its pair carries none and
109001
+ * keeps getting the full block on each message; a session that outlives its
109002
+ * pair carries stale rules and gets a counter-line instead.
109003
+ */
109004
+ const threadSessionFusionRoles = /* @__PURE__ */ new Map();
109005
+ /**
109006
+ * The Beta switch between session-channel delivery and the legacy full
109007
+ * block on every message. Read per turn so flipping it affects the next
109008
+ * turn of every thread, running sessions included. A settings read failure
109009
+ * falls back to the default rather than failing the turn.
109010
+ */
109011
+ const readOptimizedFusionPromptDelivery = serverSettingsService.getSettings.pipe(Effect.map((settings) => settings.enableOptimizedFusionPromptDelivery), Effect.orElseSucceed(() => DEFAULT_SERVER_SETTINGS.enableOptimizedFusionPromptDelivery));
108914
109012
  const appendProviderFailureActivity = (input) => Effect.all({
108915
109013
  commandId: serverCommandId("provider-failure-activity"),
108916
109014
  eventId: serverEventId()
@@ -109175,7 +109273,8 @@ const make$4 = Effect.gen(function* () {
109175
109273
  if (!thread) return yield* Effect.die(/* @__PURE__ */ new Error(`Thread '${threadId}' was not found in read model.`));
109176
109274
  const desiredRuntimeMode = thread.runtimeMode;
109177
109275
  const requestedModelSelection = options?.modelSelection;
109178
- const watchThreadIds = ((yield* projectionSnapshotQuery.getCommandReadModel()).threadPairs ?? []).filter((pair) => pair.detachedAt === null && pair.watcherThreadId === threadId).map((pair) => pair.implementerThreadId);
109276
+ const commandReadModel = yield* projectionSnapshotQuery.getCommandReadModel();
109277
+ const watchThreadIds = (commandReadModel.threadPairs ?? []).filter((pair) => pair.detachedAt === null && pair.watcherThreadId === threadId).map((pair) => pair.implementerThreadId);
109179
109278
  yield* Effect.forEach(watchThreadIds, (watchedThreadId) => grantActiveMcpWatchThread({
109180
109279
  watcherThreadId: threadId,
109181
109280
  watchedThreadId
@@ -109261,8 +109360,12 @@ const make$4 = Effect.gen(function* () {
109261
109360
  thread,
109262
109361
  project
109263
109362
  });
109363
+ const optimizedFusionPromptDelivery = yield* readOptimizedFusionPromptDelivery;
109264
109364
  const startProviderSession = (input) => {
109265
109365
  threadSessionRulesetModes.set(threadId, thread.compressMode);
109366
+ const fusionRole = optimizedFusionPromptDelivery ? fusionRoleForThread(commandReadModel.threadPairs, threadId) : void 0;
109367
+ if (fusionRole === void 0 || !providerFreezesFusionRoleAtSessionStart(preferredProvider)) threadSessionFusionRoles.delete(threadId);
109368
+ else threadSessionFusionRoles.set(threadId, fusionRole);
109266
109369
  return providerService.startSession(threadId, {
109267
109370
  threadId,
109268
109371
  ...preferredProvider ? { provider: preferredProvider } : {},
@@ -109272,7 +109375,8 @@ const make$4 = Effect.gen(function* () {
109272
109375
  ...input?.resumeCursor !== void 0 ? { resumeCursor: input.resumeCursor } : {},
109273
109376
  runtimeMode: desiredRuntimeMode,
109274
109377
  compressMode: thread.compressMode,
109275
- unpromptedSubagents: thread.unpromptedSubagents
109378
+ unpromptedSubagents: thread.unpromptedSubagents,
109379
+ ...fusionRole !== void 0 ? { fusionRole } : {}
109276
109380
  }, watchThreadIds.length > 0 ? {
109277
109381
  watchThreadIds,
109278
109382
  adviseThreadIds: watchThreadIds
@@ -109375,10 +109479,23 @@ const make$4 = Effect.gen(function* () {
109375
109479
  "Attached PDF files are available at these local paths. Read them before answering:",
109376
109480
  ...documentReferenceLines
109377
109481
  ].filter((part) => part !== void 0).join("\n\n");
109378
- const activeFusionPair = ((yield* projectionSnapshotQuery.getCommandReadModel()).threadPairs ?? []).find((pair) => pair.detachedAt === null && (pair.implementerThreadId === input.threadId || pair.watcherThreadId === input.threadId));
109379
- const isFusionBuilder = activeFusionPair?.implementerThreadId === input.threadId;
109380
- const fusionInput = expandedInputWithDocuments === void 0 ? void 0 : activeFusionPair === void 0 ? `${FUSION_PROMOTION_INSTRUCTIONS}\n\n${expandedInputWithDocuments}` : isFusionBuilder ? `${FUSION_BUILDER_INSTRUCTIONS}\n\n${fusionPairContext(activeFusionPair, "implementer")}\n\n${expandedInputWithDocuments}` : isFusionWatcherWakeMessageId(input.messageId) ? expandedInputWithDocuments : `${FUSION_WATCHER_INSTRUCTIONS}\n\n${fusionPairContext(activeFusionPair, "watcher")}\n\n${expandedInputWithDocuments}`;
109482
+ const commandReadModel = yield* projectionSnapshotQuery.getCommandReadModel();
109483
+ const activeFusionPair = findActiveFusionPair(commandReadModel.threadPairs, input.threadId);
109484
+ const fusionRole = activeFusionPair === void 0 ? void 0 : activeFusionPair.implementerThreadId === input.threadId ? "implementer" : "watcher";
109381
109485
  const activeSession = yield* providerService.listSessions().pipe(Effect.map((sessions) => sessions.find((session) => session.threadId === input.threadId)));
109486
+ const optimizedFusionPromptDelivery = yield* readOptimizedFusionPromptDelivery;
109487
+ const rebuildsFusionRoleEachTurn = activeSession?.provider === "codex";
109488
+ const sessionCarriedFusionRole = threadSessionFusionRoles.get(input.threadId);
109489
+ const sessionFusionRole = !optimizedFusionPromptDelivery ? void 0 : rebuildsFusionRoleEachTurn ? fusionRole : sessionCarriedFusionRole;
109490
+ const fusionInput = expandedInputWithDocuments === void 0 ? void 0 : fusionRole === void 0 || activeFusionPair === void 0 ? [
109491
+ ...sessionCarriedFusionRole !== void 0 ? [FUSION_DETACHED_REMINDER] : [],
109492
+ FUSION_PROMOTION_INSTRUCTIONS,
109493
+ expandedInputWithDocuments
109494
+ ].join("\n\n") : fusionRole === "watcher" && isFusionWatcherWakeMessageId(input.messageId) ? expandedInputWithDocuments : [
109495
+ sessionFusionRole === fusionRole ? fusionRoleReferenceLineFor(fusionRole) : fusionRoleInstructionsFor(fusionRole),
109496
+ fusionPairContext(activeFusionPair, fusionRole),
109497
+ expandedInputWithDocuments
109498
+ ].join("\n\n");
109382
109499
  const providerHasStructuredQuestionSystemPrompt = activeSession?.provider === "claudeAgent" || activeSession?.provider === "codex";
109383
109500
  const inputWithStructuredQuestionPolicy = fusionInput !== void 0 && !providerHasStructuredQuestionSystemPrompt ? `${NON_SYSTEM_PROVIDER_STRUCTURED_USER_QUESTIONS}\n\n${fusionInput}` : fusionInput;
109384
109501
  const sessionModelSwitch = activeSession === void 0 ? "in-session" : activeSession.providerInstanceId === void 0 ? yield* new ProviderAdapterRequestError({
@@ -109411,7 +109528,8 @@ const make$4 = Effect.gen(function* () {
109411
109528
  ...modelForTurn !== void 0 ? { modelSelection: modelForTurn } : {},
109412
109529
  ...input.interactionMode !== void 0 ? { interactionMode: input.interactionMode } : {},
109413
109530
  compressMode,
109414
- unpromptedSubagents: thread.unpromptedSubagents
109531
+ unpromptedSubagents: thread.unpromptedSubagents,
109532
+ ...optimizedFusionPromptDelivery && fusionRole !== void 0 ? { fusionRole } : {}
109415
109533
  };
109416
109534
  });
109417
109535
  const maybeGenerateAndRenameWorktreeBranchForFirstTurn = Effect.fn("maybeGenerateAndRenameWorktreeBranchForFirstTurn")(function* (input) {
@@ -109637,6 +109755,7 @@ const make$4 = Effect.gen(function* () {
109637
109755
  const now = event.payload.createdAt;
109638
109756
  if (thread.session && thread.session.status !== "stopped") yield* providerService.stopSession({ threadId: thread.id });
109639
109757
  threadSessionRulesetModes.delete(thread.id);
109758
+ threadSessionFusionRoles.delete(thread.id);
109640
109759
  yield* setThreadSession({
109641
109760
  threadId: thread.id,
109642
109761
  session: {
@@ -109698,6 +109817,7 @@ const make$4 = Effect.gen(function* () {
109698
109817
  })));
109699
109818
  yield* Effect.all([flushQueuedSettle(thread.id), flushQueuedWorkspaceCleanup(thread.id)], { discard: true });
109700
109819
  threadSessionRulesetModes.delete(thread.id);
109820
+ threadSessionFusionRoles.delete(thread.id);
109701
109821
  threadBackgroundLiveness.clearThreadLiveness(thread.id);
109702
109822
  });
109703
109823
  const processDomainEvent = Effect.fn("processDomainEvent")(function* (event) {
@@ -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{$c as i,Al as a,Bl as o,Ca as s,Da as c,Et as l,Fa as ee,Fl as te,Fr as u,Ia as d,La as f,Na as ne,Oa as re,Pa as p,_n as m,b as h,ba as g,el as _,gt as v,h as y,ht as b,kr as ie,lu as x,nl as S,st as C,tl as w,vt as T,wl as E,wr as D,xn as O,yl as ae}from"./previewAssetResource-hBW3v1Su.js";import{a as k,i as oe,n as A,o as j,r as M,s as N,t as se}from"./toggle-group-tMSVv__W.js";import{F as ce,Fr as le,I as ue,J as de,L as fe,Lr as pe,Mr as me,Nr as he,R as P,Y as ge,_ as _e,_r as ve,at as ye,ci as be,cr as xe,ct as Se,dr as Ce,fr as we,h as Te,hr as Ee,it as De,jr as Oe,lr as ke,lt as Ae,oi as je,or as Me,ot as Ne,pr as Pe,rt as Fe,si as Ie,sr as Le,st as Re,ur as ze,yr as Be,zr as Ve}from"./index-BQ38fIEf.js";import{a as He,n as Ue}from"./fileCommentAnnotations-9363cOl3.js";var We=S(`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=Ee(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),d;t[0]===a?d=t[1]:(d=e=>e.getComposerDraft(a)?.reviewComments??qe,t[0]=a,t[1]=d);let f=D(d),[ne,re]=(0,F.useState)(null),[p,m]=(0,F.useState)(null),h;t[2]===n?h=t[3]:(h=new Map(n.map(Ze)),t[2]=n,t[3]=h);let g=h,_;if(t[4]!==p||t[5]!==n||t[6]!==f||t[7]!==r){let e;t[9]!==p||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=u(t,n);return r?R(e,r,{id:n.id,kind:`comment`,range:r,rangeLabel:n.rangeLabel,text:n.text}):e},[]),s=p?.fileKey===i?[...o,p.annotation]:o;return{id:i,type:`diff`,fileDiff:t,annotations:s,collapsed:a,version:De(`${a?`1`:`0`}:${s.flatMap(z).join(`:`)}`)}},t[9]=p,t[10]=f,t[11]=r,t[12]=e):e=t[12],_=n.map(e),t[4]=p,t[5]=n,t[6]=f,t[7]=r,t[8]=_}else _=t[8];let v=_,y;t[13]!==a||t[14]!==p?.annotation||t[15]!==te?(y=e=>{re(null),p?.annotation.metadata.entries.some(t=>t.id===e)?m(null):te(a,e)},t[13]=a,t[14]=p?.annotation,t[15]=te,t[16]=y):y=t[16];let b=y,x;t[17]!==ee||t[18]!==a||t[19]!==p||t[20]!==g||t[21]!==r||t[22]!==i?(x=(e,t)=>{let n=p?.annotation.metadata.entries.find(t=>t.id===e),o=p?g.get(p.fileKey):void 0;if(!n||!o)return;let s=ie({id:n.id,sectionId:r,sectionTitle:i,filePath:o.filePath,fileDiff:o.fileDiff,range:n.range,text:t});s&&ee(a,s),re(null),m(null)},t[17]=ee,t[18]=a,t[19]=p,t[20]=g,t[21]=r,t[22]=i,t[23]=x):x=t[23];let S=x,C;t[24]!==g||t[25]!==r||t[26]!==i?(C=(e,t)=>{if(!e)return;let n=t.item;if(n.type!==`diff`)return;let a=g.get(n.id);if(!a)return;let o=Ue(),s=ie({id:o,sectionId:r,sectionTitle:i,filePath:a.filePath,fileDiff:a.fileDiff,range:e,text:``});s&&m({fileKey:n.id,annotation:{side:Je(e),lineNumber:e.end,metadata:{entries:[{id:o,kind:`draft`,range:e,rangeLabel:s.rangeLabel,text:``}]}}})},t[24]=g,t[25]=r,t[26]=i,t[27]=C):C=t[27];let w=C,T=p!==null,E;t[28]===s?E=t[29]:(E=s?{ref:s}:{},t[28]=s,t[29]=E);let O;t[30]===c?O=t[31]:(O=c?{className:c}:{},t[30]=c,t[31]=O);let ae=!T,oe=!T,A;t[32]!==w||t[33]!==o||t[34]!==oe||t[35]!==ae?(A={...o,enableGutterUtility:ae,enableLineSelection:oe,onLineSelectionEnd:w},t[32]=w,t[33]=o,t[34]=oe,t[35]=ae,t[36]=A):A=t[36];let j;t[37]===l?j=t[38]:(j=e=>e.type===`diff`?l(e.fileDiff,e.id,e.collapsed===!0):null,t[37]=l,t[38]=j);let M;t[39]!==b||t[40]!==S?(M=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]=M):M=t[41];let N;return t[42]!==v||t[43]!==ne||t[44]!==A||t[45]!==j||t[46]!==M||t[47]!==E||t[48]!==O?(N=(0,L.jsx)(k,{...E,...O,items:v,selectedLines:ne,onSelectedLinesChange:re,options:A,renderHeaderPrefix:j,renderAnnotation:M}),t[42]=v,t[43]=ne,t[44]=A,t[45]=j,t[46]=M,t[47]=E,t[48]=O,t[49]=N):N=t[49],N}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:E(e,{label:`environment-data:review:diff-preview`,tag:x.reviewGetDiffPreview,staleTimeMs:5e3})}}var et=$e(O);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:u}=h(),y=le(),[ie]=(0,F.useState)(n),[x,S]=(0,F.useState)(`stacked`),[C,E]=(0,F.useState)(y.wordWrap),[D,O]=(0,F.useState)(y.diffIgnoreWhitespace),[k,_e]=(0,F.useState)(``),[Ee,De]=(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=ae(m.configValueAtom(I?.environmentId??null)),Xe=ve(I?.environmentId??null,z?.availableEditors??[]),Ze=l(I!=null&&R!=null?Oe.status({environmentId:I.environmentId,input:{cwd:R}}):null),B=P(e=>fe(e.byThreadKey,r,ie===`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`&&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=Ee.scopeKey===Y?Ee.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:D,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:D}}):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:D}}):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?Oe.listRefs({environmentId:I.environmentId,input:{cwd:X.data.cwd,includeMatchingRemoteRefs:!0,refKind:`local`,...k.trim().length>0?{query:k.trim()}:{},limit:100}}):null),vt=l(W===null&&G===`branch`&&I&&X.data?.cwd?Oe.listRefs({environmentId:I.environmentId,input:{cwd:X.data.cwd,includeMatchingRemoteRefs:!0,refKind:`remote`,...k.trim().length>0?{query:k.trim()}:{},limit:100}}):null),yt=tt(_t.data?.refs.filter(e=>e.name!==Z?.headRef)??[],vt.data?.refs??[]),bt=nt(yt,k),xt=e=>K&&K===e.remote?.name?K:e.local?.name??e.remote?.name??e.id,St=[H,...yt.map(xt)],Ct=[...k.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)(()=>Re(Tt,`diff-panel:${u}`,{compactPartialHunkOffsets:W===null}),[u,Tt,W]),At=(0,F.useMemo)(()=>!Q||Q.kind!==`files`?[]:Q.files.toSorted((e,t)=>Ae(e).localeCompare(Ae(t),void 0,{numeric:!0,sensitivity:`base`})),[Q]),$=(0,F.useMemo)(()=>At.map(e=>{let t=Fe(e);return{fileDiff:e,filePath:Ae(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)(()=>Ne(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=ce({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`&&!a(t)&&console.warn(`Failed to open diff file in editor.`,{operation:`open-diff-file`,environmentId:r.environmentId,threadId:r.threadId,...o(te(t))})})()}})},[R,Pt,Xe,r]),It=(0,F.useCallback)(e=>{De(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)(()=>{De(e=>{let t=e.scopeKey===Y?e.fileKeys:rt;return{scopeKey:Y,fileKeys:oe(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)(ge,{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)(d,{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)(re,{align:`start`,className:`w-60`,children:[(0,L.jsx)(c,{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)(c,{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)(c,{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)(ne,{children:[(0,L.jsx)(ee,{children:`Turn`}),(0,L.jsx)(p,{className:`w-64`,children:U.map(e=>{let t=e.checkpointTurnCount??V[e.turnId]??`?`;return(0,L.jsxs)(c,{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:Be(e.completedAt,y.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)(be,{className:`size-3.5 shrink-0 opacity-70`}),(0,L.jsxs)(Le,{items:St,filteredItems:Ct,value:K??H,onOpenChange:e=>{e||_e(``)},onValueChange:e=>{e&&Bt(e===H?null:e)},children:[(0,L.jsxs)(Pe,{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)(Ve,{"aria-hidden":`true`,className:`pointer-events-none absolute top-1.5 left-0 size-4 shrink-0 text-muted-foreground/55`}),(0,L.jsx)(ke,{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:k,onChange:e=>_e(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)(xe,{children:`No matching refs.`}),(0,L.jsxs)(Ce,{className:`max-h-64 min-w-0 overflow-x-hidden`,children:[(0,L.jsx)(ze,{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)(ze,{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)(Me,{"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)(w,{"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)(Te,{additions:Nt.additions,deletions:Nt.deletions,className:`mr-1 text-[11px]`,layout:`inline`}),$.length>0&&(0,L.jsxs)(b,{children:[(0,L.jsx)(T,{render:(0,L.jsx)(g,{type:`button`,size:`icon-xs`,variant:`outline`,"aria-label":Mt?`Expand all files`:`Collapse all files`,onClick:Lt}),children:Mt?(0,L.jsx)(je,{className:`size-3`}):(0,L.jsx)(Ie,{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:[x],onValueChange:e=>{let t=e[0];(t===`stacked`||t===`split`)&&S(t)},children:[(0,L.jsx)(se,{"aria-label":`Stacked diff view`,value:`stacked`,children:(0,L.jsx)(j,{className:`size-3`})}),(0,L.jsx)(se,{"aria-label":`Split diff view`,value:`split`,children:(0,L.jsx)(N,{className:`size-3`})})]}),(0,L.jsxs)(b,{children:[(0,L.jsx)(T,{render:(0,L.jsx)(se,{"aria-label":C?`Disable diff line wrapping`:`Enable diff line wrapping`,variant:`outline`,size:`xs`,pressed:C,onPressedChange:e=>{E(!!e)}}),children:(0,L.jsx)(pe,{className:`size-3`})}),(0,L.jsx)(v,{side:`top`,children:C?`Disable line wrapping`:`Enable line wrapping`})]}),(0,L.jsxs)(b,{children:[(0,L.jsx)(T,{render:(0,L.jsx)(se,{"aria-label":D?`Show whitespace changes`:`Hide whitespace changes`,variant:`outline`,size:`xs`,pressed:D,onPressedChange:e=>{O(!!e)}}),children:(0,L.jsx)(We,{className:`size-3`})}),(0,L.jsx)(v,{side:`top`,children:D?`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=Ae(e);return(0,L.jsxs)(b,{children:[(0,L.jsx)(T,{render:(0,L.jsx)(`button`,{type:`button`,className:f(`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`,ye(e)),"aria-label":n?`Expand ${r}`:`Collapse ${r}`,"aria-expanded":!n,onClick:e=>{e.stopPropagation(),It(t)}}),children:n?(0,L.jsx)(i,{className:`size-4`}):(0,L.jsx)(_,{className:`size-4`})}),(0,L.jsx)(v,{side:`top`,children:n?`Expand diff`:`Collapse diff`})]})},options:{diffStyle:x===`split`?`split`:`unified`,lineDiffType:`none`,overflow:C?`wrap`:`scroll`,theme:Se(u),themeType:u,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:f(`max-h-[72vh] rounded-md border border-border/70 bg-background/70 p-3 font-mono text-[11px] leading-relaxed text-muted-foreground/90`,C?`overflow-auto whitespace-pre-wrap wrap-break-word`:`overflow-auto`),children:Q.text})]})}):Dt?(0,L.jsx)(de,{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{_e as DiffWorkerPoolProvider,U as default};
98
+ //# sourceMappingURL=DiffPanel-C8FwdU8R.js.map