@p4code/cli 0.6.16 → 0.6.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bin.mjs CHANGED
@@ -268,7 +268,7 @@ const make$124 = () => {
268
268
  const layer$124 = Layer.sync(NetService, make$124);
269
269
  //#endregion
270
270
  //#region package.json
271
- var version$1 = "0.6.16";
271
+ var version$1 = "0.6.17";
272
272
  //#endregion
273
273
  //#region src/config.ts
274
274
  /**
@@ -3284,6 +3284,8 @@ const FUSION_GATE_PROMPT_PREFIX = "[fusion-gate]";
3284
3284
  const FUSION_ACTIVATION_PROMPT_PREFIX = "[fusion-activation]";
3285
3285
  /** Server message that returns a Builder to its unfinished phase on Jev's advice. */
3286
3286
  const FUSION_JEV_PROMPT_PREFIX = "[fusion-jev]";
3287
+ /** Server message that resumes a Fusion child once after a transient provider failure. */
3288
+ const FUSION_RETRY_PROMPT_PREFIX = "[fusion-retry]";
3287
3289
  /**
3288
3290
  * Message-id namespaces for server-generated fusion wakes. Wake detection is
3289
3291
  * keyed off these rather than message text, which a user or expanded skill
@@ -5296,7 +5298,7 @@ var OrchestrationSearchThreadsError = class extends Schema$1.TaggedErrorClass()(
5296
5298
  const ThreadWatchEventsInput = Schema$1.Struct({
5297
5299
  threadId: ThreadId.annotate({ description: "The thread to read events from. Any thread on this server, not only this session's own." }),
5298
5300
  afterSequence: Schema$1.optional(NonNegativeInt.annotate({ description: "Exclusive sequence cursor. Events with a higher sequence are returned. Omit to read from the beginning." })),
5299
- limit: Schema$1.optional(NonNegativeInt.check(Schema$1.isLessThanOrEqualTo(200)).annotate({ description: `Maximum events to return (default 50, max 200).` }))
5301
+ limit: Schema$1.optional(NonNegativeInt.annotate({ description: `Maximum events to return (default 50, max 200; larger values are clamped).` }))
5300
5302
  });
5301
5303
  const ThreadWatchEventsResult = Schema$1.Struct({
5302
5304
  threadId: ThreadId,
@@ -13355,7 +13357,9 @@ const ThreadContextUpdateInput = Schema$1.Struct({
13355
13357
  });
13356
13358
  const ThreadContextUpdateResult = Schema$1.Struct({
13357
13359
  pairId: ThreadPairId,
13358
- updatedAt: IsoDateTime
13360
+ updatedAt: IsoDateTime,
13361
+ /** Explains input the server dropped instead of rejecting the whole update. */
13362
+ notice: Schema$1.optional(Schema$1.String)
13359
13363
  });
13360
13364
  const RoleContextItems = Schema$1.Struct({
13361
13365
  supervisor: SharedContextItems,
@@ -21561,6 +21565,7 @@ const makeRuntimeSqliteLayer = Effect.fn("makeRuntimeSqliteLayer")(function* (co
21561
21565
  const applyPragmas = Effect.gen(function* () {
21562
21566
  const sql = yield* SqlClient.SqlClient;
21563
21567
  yield* sql`PRAGMA journal_mode = WAL;`;
21568
+ yield* sql`PRAGMA synchronous = NORMAL;`;
21564
21569
  yield* sql`PRAGMA foreign_keys = ON;`;
21565
21570
  });
21566
21571
  const setupFile = (dbPath) => Layer.effectDiscard(applyPragmas.pipe(Effect.andThen(runGuardedMigrations({ dbPath })), Effect.andThen(runHistoricalActivityCompaction())));
@@ -45417,8 +45422,9 @@ const makeProjectionSnapshotQuery = Effect.gen(function* () {
45417
45422
  threadId,
45418
45423
  ...bounds
45419
45424
  })).pipe(Effect.mapError(toPersistenceSqlOrDecodeError$1("ProjectionSnapshotQuery.getThreadDetailById:listActivities:query", "ProjectionSnapshotQuery.getThreadDetailById:listActivities:decodeRows")), Effect.map((rows) => rows.map(mapThreadActivityRow)));
45420
- const [threadRow, messageRows, proposedPlanRows, activities, checkpointRows, turnRows, latestTurnRow, sessionRow, scheduledTaskRows] = yield* Effect.all([
45421
- getActiveThreadRowById({ threadId }).pipe(Effect.mapError(toPersistenceSqlOrDecodeError$1("ProjectionSnapshotQuery.getThreadDetailById:getThread:query", "ProjectionSnapshotQuery.getThreadDetailById:getThread:decodeRow"))),
45425
+ const threadRow = yield* getActiveThreadRowById({ threadId }).pipe(Effect.mapError(toPersistenceSqlOrDecodeError$1("ProjectionSnapshotQuery.getThreadDetailById:getThread:query", "ProjectionSnapshotQuery.getThreadDetailById:getThread:decodeRow")));
45426
+ if (Option.isNone(threadRow)) return Option.none();
45427
+ const [messageRows, proposedPlanRows, activities, checkpointRows, turnRows, latestTurnRow, sessionRow, scheduledTaskRows] = yield* Effect.all([
45422
45428
  (bounds === void 0 ? listThreadMessageRowsByThread({ threadId }) : listThreadMessageRowsByThreadWindow({
45423
45429
  threadId,
45424
45430
  ...bounds
@@ -45431,7 +45437,6 @@ const makeProjectionSnapshotQuery = Effect.gen(function* () {
45431
45437
  getThreadSessionRowByThread({ threadId }).pipe(Effect.mapError(toPersistenceSqlOrDecodeError$1("ProjectionSnapshotQuery.getThreadDetailById:getSession:query", "ProjectionSnapshotQuery.getThreadDetailById:getSession:decodeRow"))),
45432
45438
  listThreadScheduledTaskRowsByThread({ threadId }).pipe(Effect.mapError(toPersistenceSqlOrDecodeError$1("ProjectionSnapshotQuery.getThreadDetailById:listScheduledTasks:query", "ProjectionSnapshotQuery.getThreadDetailById:listScheduledTasks:decodeRows")))
45433
45439
  ]);
45434
- if (Option.isNone(threadRow)) return Option.none();
45435
45440
  const thread = {
45436
45441
  id: threadRow.value.threadId,
45437
45442
  projectId: threadRow.value.projectId,
@@ -61866,6 +61871,38 @@ const prepareSharedCheckout = (input, git) => Effect.gen(function* () {
61866
61871
  return { branch };
61867
61872
  }).pipe(Effect.withSpan("prepareSharedCheckout"));
61868
61873
  //#endregion
61874
+ //#region src/orchestration/ShellStreamingThrottle.ts
61875
+ /** Minimum gap between shell refetches caused by one streaming message. */
61876
+ const SHELL_STREAMING_REFRESH_INTERVAL_MS = 1e3;
61877
+ /**
61878
+ * Per-subscription filter for the shell stream. A streaming assistant message
61879
+ * emits a delta roughly every 50 ms and each one only moves the thread's
61880
+ * `updatedAt`, yet every delta would otherwise refetch the thread shell and
61881
+ * push it to the client. The first delta of a message always passes (it can
61882
+ * change `latestTurn`), later deltas pass at most once per interval, and the
61883
+ * final non-streaming message always passes, so the shell still settles on the
61884
+ * current state.
61885
+ *
61886
+ * Returns `(event, nowMs) => keep`. Create one per subscription: the state is
61887
+ * which deltas this subscriber has already seen.
61888
+ */
61889
+ function makeShellStreamingThrottle(intervalMs = SHELL_STREAMING_REFRESH_INTERVAL_MS) {
61890
+ const lastPassedAtByMessageId = /* @__PURE__ */ new Map();
61891
+ return (event, nowMs) => {
61892
+ if (event.type !== "thread.message-sent") return true;
61893
+ const messageId = event.payload.messageId;
61894
+ if (!event.payload.streaming) {
61895
+ lastPassedAtByMessageId.delete(messageId);
61896
+ return true;
61897
+ }
61898
+ const lastPassedAt = lastPassedAtByMessageId.get(messageId);
61899
+ if (lastPassedAt !== void 0 && nowMs - lastPassedAt < intervalMs) return false;
61900
+ if (lastPassedAtByMessageId.size >= 1e3) lastPassedAtByMessageId.clear();
61901
+ lastPassedAtByMessageId.set(messageId, nowMs);
61902
+ return true;
61903
+ };
61904
+ }
61905
+ //#endregion
61869
61906
  //#region src/provider/ProviderInstanceEnvironment.ts
61870
61907
  function mergeProviderInstanceEnvironment(environment, baseEnv = process.env) {
61871
61908
  if (!environment || environment.length === 0) return baseEnv;
@@ -92270,6 +92307,11 @@ const makeCodexSubagentModelReporter = (options) => {
92270
92307
  */
92271
92308
  /** Codex's own name for the token variable of p4code's built-in server. */
92272
92309
  const CODEX_P4CODE_BEARER_TOKEN_ENV_VAR = "P4_MCP_BEARER_TOKEN";
92310
+ /**
92311
+ * Codex `mcp_servers.<name>.tool_timeout_sec` for p4code's built-in server, a
92312
+ * TOML float. An hour, because `ask_user_question` blocks until a person answers.
92313
+ */
92314
+ const CODEX_P4CODE_TOOL_TIMEOUT_SEC = "3600.0";
92273
92315
  /** Its own variable, so the Computer Use credential never rides on p4-code's. */
92274
92316
  const CODEX_P4_COMPUTER_USE_BEARER_TOKEN_ENV_VAR = "P4_COMPUTER_USE_BEARER_TOKEN";
92275
92317
  /** Prefix for the per-server variables the registered servers get. */
@@ -94405,7 +94447,9 @@ const makeCodexAdapter = Effect.fn("makeCodexAdapter")(function* (codexConfig, o
94405
94447
  "-c",
94406
94448
  `mcp_servers.${P4CODE_MCP_SERVER_NAME}.url=${mcpSession.endpoint}`,
94407
94449
  "-c",
94408
- `mcp_servers.${P4CODE_MCP_SERVER_NAME}.bearer_token_env_var="${CODEX_P4CODE_BEARER_TOKEN_ENV_VAR}"`
94450
+ `mcp_servers.${P4CODE_MCP_SERVER_NAME}.bearer_token_env_var="${CODEX_P4CODE_BEARER_TOKEN_ENV_VAR}"`,
94451
+ "-c",
94452
+ `mcp_servers.${P4CODE_MCP_SERVER_NAME}.tool_timeout_sec=${CODEX_P4CODE_TOOL_TIMEOUT_SEC}`
94409
94453
  ] : [],
94410
94454
  ...mcpSession?.computerUse ? [
94411
94455
  "-c",
@@ -132581,7 +132625,10 @@ const makeWsRpcLayer = (currentSession, previewAutomationBroker, computerUseBrok
132581
132625
  output.push(...yield* coalesceShellEvents(pendingEvents));
132582
132626
  return output;
132583
132627
  });
132584
- const coalesceShellLiveStream = (stream) => stream.pipe(Stream.groupedWithin(SHELL_COALESCE_MAX_CHUNK, SHELL_COALESCE_WINDOW), Stream.mapEffect(coalesceShellLiveInputs), Stream.flatMap((items) => Stream.fromIterable(items)));
132628
+ const coalesceShellLiveStream = (stream) => {
132629
+ const keepStreamingDelta = makeShellStreamingThrottle();
132630
+ return stream.pipe(Stream.filterEffect((input) => input.kind === "event" ? Clock.currentTimeMillis.pipe(Effect.map((nowMs) => keepStreamingDelta(input.event, nowMs))) : Effect.succeed(true)), Stream.groupedWithin(SHELL_COALESCE_MAX_CHUNK, SHELL_COALESCE_WINDOW), Stream.mapEffect(coalesceShellLiveInputs), Stream.flatMap((items) => Stream.fromIterable(items)));
132631
+ };
132585
132632
  const dispatchBootstrapTurnStart = (command) => Effect.gen(function* () {
132586
132633
  const bootstrap = command.bootstrap;
132587
132634
  const { bootstrap: _bootstrap, ...finalTurnStartCommand } = command;
@@ -136998,7 +137045,9 @@ const ThreadToolkitHandlersLive = ThreadToolkit.toLayer({
136998
137045
  const { invocation, pair, reject } = yield* requireSharedPair("thread_context_update");
136999
137046
  const isBuilder = invocation.threadId === pair.implementerThreadId;
137000
137047
  if (isBuilder && (input.requirements !== void 0 || input.decisions !== void 0)) return yield* reject("Supervisor owns requirements and decisions. Propose revisions through thread_ask.");
137001
- if (!isBuilder && input.summary !== void 0) return yield* reject("Only Builder may replace its phase summary.");
137048
+ const { summary, ...supervisorContext } = input;
137049
+ const ignoresSummary = !isBuilder && summary !== void 0;
137050
+ if (ignoresSummary && input.requirements === void 0 && input.decisions === void 0 && input.blockers.length === 0 && input.evidence.length === 0) return yield* reject("Only Builder may replace its phase summary.");
137002
137051
  const crypto = yield* Crypto.Crypto;
137003
137052
  const createdAt = DateTime.formatIso(yield* DateTime.now);
137004
137053
  yield* dispatchControl({
@@ -137014,14 +137063,15 @@ const ThreadToolkitHandlersLive = ThreadToolkit.toLayer({
137014
137063
  summary: "Shared working context updated",
137015
137064
  turnId: null,
137016
137065
  payload: {
137017
- ...input,
137066
+ ...isBuilder ? input : supervisorContext,
137018
137067
  pairId: pair.id
137019
137068
  }
137020
137069
  }
137021
137070
  }, invocation.threadId);
137022
137071
  return {
137023
137072
  pairId: pair.id,
137024
- updatedAt: createdAt
137073
+ updatedAt: createdAt,
137074
+ ...ignoresSummary ? { notice: "summary was ignored because only Builder owns its phase summary; the other fields were saved." } : {}
137025
137075
  };
137026
137076
  }),
137027
137077
  thread_plan_update: (input) => Effect.gen(function* () {
@@ -139053,7 +139103,7 @@ const WatchToolkitHandlersLive = WatchToolkit.toLayer({
139053
139103
  const page = yield* (yield* ThreadEventStreamService).read({
139054
139104
  threadId: input.threadId,
139055
139105
  ...input.afterSequence !== void 0 ? { afterSequence: input.afterSequence } : {},
139056
- limit: input.limit ?? 50
139106
+ limit: Math.min(input.limit ?? 50, 200)
139057
139107
  }).pipe(Effect.mapError((cause) => new ThreadWatchFailedError({
139058
139108
  threadId: input.threadId,
139059
139109
  detail: cause.message
@@ -145016,7 +145066,8 @@ function citedSequences(summary) {
145016
145066
  * sequence numbers, the summary is required to cite them, and a dropped line,
145017
145067
  * unrenderable transcript field, or dropped ledger row makes coverage
145018
145068
  * incomplete. A shortened transcript field or clipped ledger argument stays
145019
- * visible and its full text stays in the raw event, so neither does.
145069
+ * visible and its full text stays in the raw event, so neither does. A summary
145070
+ * line citing a sequence the transcript lacks is removed, never kept.
145020
145071
  *
145021
145072
  * @module orchestration/handoff/fusionEvidenceHandoff
145022
145073
  */
@@ -145170,6 +145221,34 @@ function summaryRejection(input) {
145170
145221
  if (cited.length === 0) return "summary-uncited";
145171
145222
  return cited.every((sequence) => input.includedSequences.has(sequence)) ? null : "summary-citation-invalid";
145172
145223
  }
145224
+ /**
145225
+ * The summary with every line citing a sequence absent from the transcript,
145226
+ * and the uncited lines continuing it, removed; or null when nothing was
145227
+ * removed or nothing cited survives.
145228
+ *
145229
+ * A line is the unit of claim: one invented citation condemns the line it is
145230
+ * on, never the lines beside it that cite real evidence. The removal is
145231
+ * disclosed in the text so the supervisor knows the summary was cut.
145232
+ */
145233
+ function salvageSummary(input) {
145234
+ const lines = input.summary.split("\n");
145235
+ const kept = [];
145236
+ let droppingClaim = false;
145237
+ for (const line of lines) {
145238
+ const cited = citedSequences(line);
145239
+ if (cited.length > 0) droppingClaim = !cited.every((sequence) => input.includedSequences.has(sequence));
145240
+ else if (/^\s*#{1,6}\s/u.test(line)) droppingClaim = false;
145241
+ if (!droppingClaim) kept.push(line);
145242
+ }
145243
+ const dropped = lines.length - kept.length;
145244
+ if (dropped === 0) return null;
145245
+ const text = kept.join("\n").trim();
145246
+ if (summaryRejection({
145247
+ summary: text,
145248
+ includedSequences: input.includedSequences
145249
+ }) !== null) return null;
145250
+ return `${text}\n\n[Server removed ${dropped} summary ${dropped === 1 ? "line" : "lines"} resting on sequences absent from this transcript.]`;
145251
+ }
145173
145252
  const buildFusionEvidenceHandoff = Effect.fn("fusionEvidenceHandoff.build")(function* (input) {
145174
145253
  const engine = yield* OrchestrationEngineService;
145175
145254
  const textGeneration = yield* TextGeneration;
@@ -145237,6 +145316,19 @@ const buildFusionEvidenceHandoff = Effect.fn("fusionEvidenceHandoff.build")(func
145237
145316
  produced: Option.none(),
145238
145317
  reason
145239
145318
  });
145319
+ const salvaged = (result) => {
145320
+ const summary = salvageSummary({
145321
+ summary: result.summary,
145322
+ includedSequences: evidence.includedSequences
145323
+ });
145324
+ return summary === null ? null : {
145325
+ produced: Option.some({
145326
+ ...result,
145327
+ summary
145328
+ }),
145329
+ reason: null
145330
+ };
145331
+ };
145240
145332
  const attempt = yield* Effect.gen(function* () {
145241
145333
  const first = yield* runSummary(handoffSummaryInstructions({ includedSequences: evidence.includedSequences }));
145242
145334
  if (Option.isNone(first)) return noSummary("summary-unavailable");
@@ -145250,6 +145342,8 @@ const buildFusionEvidenceHandoff = Effect.fn("fusionEvidenceHandoff.build")(func
145250
145342
  produced: first,
145251
145343
  reason: null
145252
145344
  };
145345
+ const firstSalvage = salvaged(first.value);
145346
+ if (firstSalvage !== null) return firstSalvage;
145253
145347
  const repaired = yield* runSummary(handoffSummaryRepairInstructions({
145254
145348
  rejection,
145255
145349
  summary: first.value.summary,
@@ -145265,7 +145359,7 @@ const buildFusionEvidenceHandoff = Effect.fn("fusionEvidenceHandoff.build")(func
145265
145359
  return repairedRejection === null ? {
145266
145360
  produced: repaired,
145267
145361
  reason: null
145268
- } : noSummary(repairedRejection);
145362
+ } : salvaged(repaired.value) ?? noSummary(repairedRejection);
145269
145363
  }).pipe(Effect.timeoutOption(HANDOFF_TIMEOUT), Effect.map(Option.getOrElse(() => noSummary("summary-unavailable"))));
145270
145364
  const produced = attempt.produced;
145271
145365
  if (Option.isNone(produced)) return yield* fallback(attempt.reason ?? "summary-unavailable");
@@ -145356,6 +145450,26 @@ const gateMessageId = (gateId, round) => MessageId.make(`${FUSION_GATE_MESSAGE_I
145356
145450
  const gateActivityId = (gateId, threadId, suffix) => EventId.make(`fusion-gate:${gateId}:${suffix}:${threadId}`);
145357
145451
  const pairFailureCommandId = (pairId, sequence, suffix) => CommandId.make(`server:fusion:${pairId}:provider-failure:${sequence}:${suffix}`);
145358
145452
  const pairFailureActivityId = (pairId, sequence, threadId) => EventId.make(`fusion-provider-failure:${pairId}:${sequence}:${threadId}`);
145453
+ const PROVIDER_RETRY_MESSAGE_ID_PREFIX = "fusion-provider-retry:";
145454
+ const providerRetryCommandId = (pairId, sequence) => CommandId.make(`server:fusion:${pairId}:provider-retry:${sequence}`);
145455
+ /** Carries the origin and the failed completion's sequence ahead of the free-form pair id. */
145456
+ const providerRetryMessageId = (origin, sequence, pairId) => MessageId.make(`${PROVIDER_RETRY_MESSAGE_ID_PREFIX}${origin}:${sequence}:${pairId}`);
145457
+ const PROVIDER_RETRY_MESSAGE_ID = /^fusion-provider-retry:(review|other):(\d+):/u;
145458
+ /** The failed completion a review retry resumes, or null for any other message. */
145459
+ const retriedReviewSequence = (messageId) => {
145460
+ const match = messageId === null ? null : PROVIDER_RETRY_MESSAGE_ID.exec(messageId);
145461
+ return match?.[1] === "review" ? Number(match[2]) : null;
145462
+ };
145463
+ const TRANSIENT_PROVIDER_FAILURE = /runtime stream failed|stream disconnected|at capacity|overloaded|server failed|server error|internal error|\b(?:500|502|503|504|529)\b/i;
145464
+ const PERSISTENT_PROVIDER_FAILURE = /usage limit|rate limit|quota|billing|auth|log ?in|credential|unauthori[sz]ed|forbidden|\b(?:401|403)\b|does not exist|not found/i;
145465
+ /**
145466
+ * Whether a failed turn's recorded error is worth one automatic retry: a
145467
+ * dropped stream, a provider at capacity, or a server-side outage. Usage
145468
+ * limits, auth and missing paths never are, because retrying cannot fix them.
145469
+ */
145470
+ function isTransientProviderFailure(lastError) {
145471
+ return typeof lastError === "string" && TRANSIENT_PROVIDER_FAILURE.test(lastError) && !PERSISTENT_PROVIDER_FAILURE.test(lastError);
145472
+ }
145359
145473
  const idlePauseCommandId = (pairId, sequence, threadId) => CommandId.make(`server:fusion:${pairId}:idle-pause:${sequence}:${threadId}`);
145360
145474
  const idlePauseActivityId = (pairId, sequence, threadId) => EventId.make(`fusion-idle-pause:${pairId}:${sequence}:${threadId}`);
145361
145475
  /**
@@ -145634,10 +145748,11 @@ const make$3 = Effect.gen(function* () {
145634
145748
  });
145635
145749
  });
145636
145750
  /**
145637
- * A provider-level failure is not a review boundary. Stop the other child if
145638
- * it is still active, record one visible pair-level reason, and leave both
145639
- * idle until a human starts the next turn. Deterministic ids make replay a
145640
- * no-op, while ignoring interrupted completions prevents a reciprocal loop.
145751
+ * A provider-level failure that was not retried is not a review boundary.
145752
+ * Stop the other child if it is still active, record one visible pair-level
145753
+ * reason, and leave both idle until a human starts the next turn.
145754
+ * Deterministic ids make replay a no-op, while ignoring interrupted
145755
+ * completions prevents a reciprocal loop.
145641
145756
  */
145642
145757
  const pausePairAfterProviderFailure = Effect.fn("FusionWatcherReactor.pausePairAfterProviderFailure")(function* (input) {
145643
145758
  const { readModel } = yield* readPairs;
@@ -145674,6 +145789,46 @@ const make$3 = Effect.gen(function* () {
145674
145789
  discard: true
145675
145790
  });
145676
145791
  });
145792
+ /**
145793
+ * Resumes a child whose turn failed on a transient provider error, once,
145794
+ * instead of pausing the pair and interrupting its healthy peer. True when
145795
+ * the retry went out (now or on an earlier pass); false sends the failure to
145796
+ * the pause. A retry turn that fails again, a turn whose start message is
145797
+ * unknown, a thread someone already started again, and a failure replayed
145798
+ * at boot are never retried.
145799
+ */
145800
+ const retryTransientProviderFailure = Effect.fn("FusionWatcherReactor.retryTransientProviderFailure")(function* (input) {
145801
+ const { pair, completion, readModel } = input;
145802
+ const threadId = completion.payload.threadId;
145803
+ const retryIds = new Set(["review", "other"].map((origin) => providerRetryMessageId(origin, completion.sequence, pair.id)));
145804
+ const laterStarts = yield* orchestrationEngine.readEvents(completion.sequence, readModel.snapshotSequence - completion.sequence, { eventTypes: ["thread.turn-start-requested"] }).pipe(Stream.takeWhile((event) => event.sequence <= readModel.snapshotSequence), Stream.filter((event) => event.type === "thread.turn-start-requested" && event.payload.threadId === threadId), Stream.runCollect);
145805
+ if (laterStarts.some((event) => event.type === "thread.turn-start-requested" && retryIds.has(event.payload.messageId))) return true;
145806
+ if (input.replayed) return false;
145807
+ const thread = readModel.threads.find((candidate) => candidate.id === threadId && candidate.deletedAt === null);
145808
+ if (laterStarts.length > 0 || thread === void 0 || thread.latestTurn?.state === "running" || !isTransientProviderFailure(thread.session?.lastError) || completion.payload.turnId === null) return false;
145809
+ const started = yield* projectionSnapshotQuery.getTurnStart(threadId, completion.payload.turnId).pipe(Effect.map(Option.getOrNull), Effect.orElseSucceed(() => null));
145810
+ if (started === null || started.messageId === null || started.messageId.startsWith(PROVIDER_RETRY_MESSAGE_ID_PREFIX)) return false;
145811
+ return yield* orchestrationEngine.dispatch({
145812
+ type: "thread.turn.start",
145813
+ commandId: providerRetryCommandId(pair.id, completion.sequence),
145814
+ threadId,
145815
+ message: {
145816
+ messageId: providerRetryMessageId(started.messageId.startsWith("fusion-review:") ? "review" : "other", completion.sequence, pair.id),
145817
+ role: "user",
145818
+ text: `${FUSION_RETRY_PROMPT_PREFIX} Your previous turn stopped on a transient provider error. Continue the same work from where it stopped; no new work or authority is added. If it was already finished, end with the report you would have given.`,
145819
+ attachments: []
145820
+ },
145821
+ runtimeMode: thread.runtimeMode,
145822
+ interactionMode: thread.interactionMode,
145823
+ compressMode: thread.compressMode,
145824
+ unpromptedSubagents: thread.unpromptedSubagents,
145825
+ createdAt: completion.occurredAt
145826
+ }).pipe(Effect.as(true), Effect.catchCause((cause) => Effect.logWarning("provider failure retry was not sent; pausing the pair instead", {
145827
+ pairId: pair.id,
145828
+ sequence: completion.sequence,
145829
+ cause
145830
+ }).pipe(Effect.as(false))));
145831
+ });
145677
145832
  const wakeWasRequested = Effect.fn("FusionWatcherReactor.wakeWasRequested")(function* (afterSequence, throughSequence, messageId) {
145678
145833
  return (yield* orchestrationEngine.readEvents(afterSequence, throughSequence - afterSequence, { eventTypes: ["thread.turn-start-requested"] }).pipe(Stream.takeWhile((event) => event.sequence <= throughSequence), Stream.filter((event) => event.type === "thread.turn-start-requested" && event.payload.messageId === messageId), Stream.take(1), Stream.runCollect)).length > 0;
145679
145834
  });
@@ -146008,7 +146163,7 @@ const make$3 = Effect.gen(function* () {
146008
146163
  resolvedAt: event.payload.createdAt
146009
146164
  });
146010
146165
  });
146011
- const isFusionProtocolText = (text) => text.startsWith("[fusion-review]") || text.startsWith("[fusion-gate]") || text.startsWith("[fusion-activation]") || text.startsWith("[fusion-jev]") || text.startsWith("[fusion-advice]");
146166
+ const isFusionProtocolText = (text) => text.startsWith("[fusion-review]") || text.startsWith("[fusion-gate]") || text.startsWith("[fusion-activation]") || text.startsWith("[fusion-jev]") || text.startsWith("[fusion-retry]") || text.startsWith("[fusion-advice]");
146012
146167
  const processMessageSent = Effect.fn("FusionWatcherReactor.processMessageSent")(function* (event) {
146013
146168
  if (event.sequence <= liveEventsAfterSequence) return;
146014
146169
  if (event.payload.role !== "user") return;
@@ -147081,17 +147236,19 @@ Set outcome to summarized, fallback, or failed from the review that just ended.
147081
147236
  if (Option.isNone(turn)) return;
147082
147237
  const started = turn.value;
147083
147238
  if (started.threadId !== pair.watcherThreadId) return;
147239
+ const retriedSequence = retriedReviewSequence(started.messageId);
147240
+ const retriedTurnId = retriedSequence === null ? null : yield* orchestrationEngine.readEvents(retriedSequence - 1, 1).pipe(Stream.runHead, Effect.map(Option.flatMap((event) => event.type === "thread.turn-completed" && event.payload.threadId === pair.watcherThreadId ? Option.fromNullishOr(event.payload.turnId) : Option.none())), Effect.map(Option.getOrNull), Effect.orElseSucceed(() => null));
147084
147241
  const readAt = Date.parse(latest.readAt);
147085
147242
  if (latest.turnId === null) {
147086
147243
  if (input.replayed) return;
147087
147244
  if (started.startedAt !== null && readAt < Date.parse(started.startedAt)) return;
147088
147245
  if (readAt > Date.parse(completion.occurredAt)) return;
147089
- } else if (latest.turnId !== completion.payload.turnId) return;
147246
+ } else if (latest.turnId !== completion.payload.turnId && (retriedTurnId === null || latest.turnId !== retriedTurnId)) return;
147090
147247
  const wakeAt = (prefix) => projectionSnapshotQuery.getLatestThreadMessageWithIdPrefix(pair.watcherThreadId, prefix).pipe(Effect.map(Option.flatMap((wake) => {
147091
147248
  const sentAt = Date.parse(wake.createdAt);
147092
147249
  return Number.isNaN(sentAt) || sentAt > readAt ? Option.none() : Option.some(sentAt);
147093
147250
  })), Effect.orElseSucceed(() => Option.none()));
147094
- if (!(started.messageId === null ? yield* Effect.map(Effect.all([wakeAt("fusion-review:"), wakeAt("fusion-gate:")]), ([review, gate]) => Option.isSome(review) && (Option.isNone(gate) || gate.value < review.value)) : started.messageId.startsWith("fusion-review:"))) return;
147251
+ if (!(started.messageId === null ? yield* Effect.map(Effect.all([wakeAt("fusion-review:"), wakeAt("fusion-gate:")]), ([review, gate]) => Option.isSome(review) && (Option.isNone(gate) || gate.value < review.value)) : started.messageId.startsWith("fusion-review:") || retriedSequence !== null)) return;
147095
147252
  if (latest.hasReport) {
147096
147253
  if (input.watcher !== void 0) yield* dispatchEvidenceReportRepair(pair, input.watcher, completion.occurredAt, {
147097
147254
  watchedThreadId: latest.watchedThreadId,
@@ -147107,6 +147264,12 @@ Set outcome to summarized, fallback, or failed from the review that just ended.
147107
147264
  const activePairs = (readModel.threadPairs ?? []).filter((pair) => pair.detachedAt === null);
147108
147265
  const failedPair = activePairs.find((pair) => pair.implementerThreadId === event.payload.threadId || pair.watcherThreadId === event.payload.threadId);
147109
147266
  if (event.payload.state === "failed" && failedPair !== void 0) {
147267
+ if (yield* retryTransientProviderFailure({
147268
+ pair: failedPair,
147269
+ completion: event,
147270
+ readModel,
147271
+ replayed: event.sequence <= liveEventsAfterSequence
147272
+ })) return;
147110
147273
  yield* pausePairAfterProviderFailure({
147111
147274
  pair: failedPair,
147112
147275
  failedThreadId: event.payload.threadId,
@@ -1,4 +1,4 @@
1
- import{r as e}from"./rolldown-runtime-QTnfLwEv.js";import{n as t,r as n,t as r}from"./compiler-runtime-CLAvuQ-D.js";import{$a as i,$u as a,Ba as o,Cl as s,Cn as c,Dt as l,Eu as ee,Ga as te,Kl as u,Kr as ne,La as d,Qa as f,Qr as re,Sl as p,Vr as m,Wa as h,Xa as ie,Za as g,_t as _,b as v,bu as ae,ct as y,eo as b,gt as x,h as S,hu as C,lu as w,vn as oe,wl as T,xl as E,yt as D}from"./previewAssetResource-CdRrwvdJ.js";import{a as O,c as se,i as k,n as A,o as j,r as M,s as ce,t as le}from"./toggle-group-BPwLpAJP.js";import{A as ue,Fr as de,J as fe,Lr as pe,M as N,Mr as me,Nr as he,Rr as ge,Y as _e,_ as ve,ar as ye,cr as be,ct as xe,dr as Se,dt as Ce,ft as P,h as we,ir as Te,j as Ee,jr as De,k as Oe,lr as ke,lt as Ae,nr as je,oi as Me,or as Ne,ot as Pe,pr as Fe,rr as Ie,si as Le,sr as Re,st as ze,ut as Be,yr as Ve}from"./index-sXPBcrsQ.js";import{a as He,n as Ue}from"./fileCommentAnnotations-CgO_0-Tr.js";var We=T(`pilcrow`,[[`path`,{d:`M13 4v16`,key:`8vvj80`}],[`path`,{d:`M17 4v16`,key:`7dpous`}],[`path`,{d:`M19 4H9.5a4.5 4.5 0 0 0 0 9H13`,key:`sh4n9v`}]]),F=e(n(),1);function Ge({threadRef:e,filePath:t,activeCwd:n,openInEditor:r,openFileSurface:i}){if(e){if(i){i(t);return}y.getState().openFile(e,t);return}r(n?S(t,n):t)}var I=r();function Ke(e,t){let n=(0,I.c)(4),r=Se(e,t),i;return n[0]!==r.data||n[1]!==r.error||n[2]!==r.isPending?(i={data:r.data,error:r.error,isPending:r.isPending},n[0]=r.data,n[1]=r.error,n[2]=r.isPending,n[3]=i):i=n[3],i}var L=t(),qe=[];function Je(e){return(e.endSide??e.side)===`deletions`?`deletions`:`additions`}function R(e,t,n){let r=Je(t),i=e.findIndex(e=>e.side===r&&e.lineNumber===t.end);return i<0?[...e,{side:r,lineNumber:t.end,metadata:{entries:[n]}}]:e.map((e,t)=>t===i?{...e,metadata:{entries:[...e.metadata.entries,n]}}:e)}function Ye(e){let t=(0,I.c)(50),{files:n,sectionId:r,sectionTitle:i,composerDraftTarget:a,options:o,viewerRef:s,className:c,renderHeaderPrefix:l}=e,ee=m(Qe),te=m(B),u;t[0]===a?u=t[1]:(u=e=>e.getComposerDraft(a)?.reviewComments??qe,t[0]=a,t[1]=u);let d=m(u),[f,p]=(0,F.useState)(null),[h,ie]=(0,F.useState)(null),g;t[2]===n?g=t[3]:(g=new Map(n.map(Ze)),t[2]=n,t[3]=g);let _=g,v;if(t[4]!==h||t[5]!==n||t[6]!==d||t[7]!==r){let e;t[9]!==h||t[10]!==d||t[11]!==r?(e=e=>{let{fileDiff:t,filePath:n,fileKey:i,collapsed:a}=e,o=d.filter(e=>e.sectionId===r&&e.filePath===n&&(e.fenceLanguage??`diff`)===`diff`).reduce((e,n)=>{let r=re(t,n);return r?R(e,r,{id:n.id,kind:`comment`,range:r,rangeLabel:n.rangeLabel,text:n.text}):e},[]),s=h?.fileKey===i?[...o,h.annotation]:o;return{id:i,type:`diff`,fileDiff:t,annotations:s,collapsed:a,version:ze(`${a?`1`:`0`}:${s.flatMap(z).join(`:`)}`)}},t[9]=h,t[10]=d,t[11]=r,t[12]=e):e=t[12],v=n.map(e),t[4]=h,t[5]=n,t[6]=d,t[7]=r,t[8]=v}else v=t[8];let ae=v,y;t[13]!==a||t[14]!==h?.annotation||t[15]!==te?(y=e=>{p(null),h?.annotation.metadata.entries.some(t=>t.id===e)?ie(null):te(a,e)},t[13]=a,t[14]=h?.annotation,t[15]=te,t[16]=y):y=t[16];let b=y,x;t[17]!==ee||t[18]!==a||t[19]!==h||t[20]!==_||t[21]!==r||t[22]!==i?(x=(e,t)=>{let n=h?.annotation.metadata.entries.find(t=>t.id===e),o=h?_.get(h.fileKey):void 0;if(!n||!o)return;let s=ne({id:n.id,sectionId:r,sectionTitle:i,filePath:o.filePath,fileDiff:o.fileDiff,range:n.range,text:t});s&&ee(a,s),p(null),ie(null)},t[17]=ee,t[18]=a,t[19]=h,t[20]=_,t[21]=r,t[22]=i,t[23]=x):x=t[23];let S=x,C;t[24]!==_||t[25]!==r||t[26]!==i?(C=(e,t)=>{if(!e)return;let n=t.item;if(n.type!==`diff`)return;let a=_.get(n.id);if(!a)return;let o=Ue(),s=ne({id:o,sectionId:r,sectionTitle:i,filePath:a.filePath,fileDiff:a.fileDiff,range:e,text:``});s&&ie({fileKey:n.id,annotation:{side:Je(e),lineNumber:e.end,metadata:{entries:[{id:o,kind:`draft`,range:e,rangeLabel:s.rangeLabel,text:``}]}}})},t[24]=_,t[25]=r,t[26]=i,t[27]=C):C=t[27];let w=C,oe=h!==null,T;t[28]===s?T=t[29]:(T=s?{ref:s}:{},t[28]=s,t[29]=T);let E;t[30]===c?E=t[31]:(E=c?{className:c}:{},t[30]=c,t[31]=E);let D=!oe,se=!oe,k;t[32]!==w||t[33]!==o||t[34]!==se||t[35]!==D?(k={...o,enableGutterUtility:D,enableLineSelection:se,onLineSelectionEnd:w},t[32]=w,t[33]=o,t[34]=se,t[35]=D,t[36]=k):k=t[36];let A;t[37]===l?A=t[38]:(A=e=>e.type===`diff`?l(e.fileDiff,e.id,e.collapsed===!0):null,t[37]=l,t[38]=A);let j;t[39]!==b||t[40]!==S?(j=e=>(0,L.jsx)(`div`,{className:`py-1`,children:e.metadata.entries.map(e=>(0,L.jsx)(He,{kind:e.kind,rangeLabel:e.rangeLabel,text:e.text,onCancel:()=>b(e.id),onComment:t=>S(e.id,t),onDelete:()=>b(e.id)},e.id))}),t[39]=b,t[40]=S,t[41]=j):j=t[41];let M;return t[42]!==ae||t[43]!==f||t[44]!==k||t[45]!==A||t[46]!==j||t[47]!==T||t[48]!==E?(M=(0,L.jsx)(O,{...T,...E,items:ae,selectedLines:f,onSelectedLinesChange:p,options:k,renderHeaderPrefix:A,renderAnnotation:j}),t[42]=ae,t[43]=f,t[44]=k,t[45]=A,t[46]=j,t[47]=T,t[48]=E,t[49]=M):M=t[49],M}function z(e){return e.metadata.entries.map(Xe)}function Xe(e){return`${e.id}:${e.rangeLabel}:${e.text}`}function Ze(e){return[e.fileKey,e]}function B(e){return e.removeReviewComment}function Qe(e){return e.addReviewComment}function $e(e){return{diffPreview:w(e,{label:`environment-data:review:diff-preview`,tag:a.reviewGetDiffPreview,staleTimeMs:5e3})}}var et=$e(c);function V(e){return e.remoteName&&e.name.startsWith(`${e.remoteName}/`)?e.name.slice(e.remoteName.length+1):e.name}function tt(e,t){let n=new Set(t),r=e.map(e=>{let r=t.filter(t=>n.has(t)&&V(t)===e.name),i=r.find(e=>e.remoteName===`origin`)??r[0]??null;return i&&n.delete(i),{id:`local:${e.name}`,label:e.name,local:e,remote:i}}),i=t.filter(e=>n.has(e)).map(e=>({id:`remote:${e.name}`,label:e.name,local:null,remote:e}));return[...r,...i]}function nt(e,t){let n=t.trim().toLocaleLowerCase();return n.length===0?e:e.filter(e=>e.label.toLocaleLowerCase().includes(n)||e.local?.name.toLocaleLowerCase().includes(n)===!0||e.remote?.name.toLocaleLowerCase().includes(n)===!0)}var H=`__automatic_base_ref__`,rt=new Set,it=`
1
+ import{r as e}from"./rolldown-runtime-QTnfLwEv.js";import{n as t,r as n,t as r}from"./compiler-runtime-CLAvuQ-D.js";import{$a as i,$u as a,Ba as o,Cl as s,Cn as c,Dt as l,Eu as ee,Ga as te,Kl as u,Kr as ne,La as d,Qa as f,Qr as re,Sl as p,Vr as m,Wa as h,Xa as ie,Za as g,_t as _,b as v,bu as ae,ct as y,eo as b,gt as x,h as S,hu as C,lu as w,vn as oe,wl as T,xl as E,yt as D}from"./previewAssetResource-CscIFHNv.js";import{a as O,c as se,i as k,n as A,o as j,r as M,s as ce,t as le}from"./toggle-group-DrOJrfFB.js";import{A as ue,Fr as de,J as fe,Lr as pe,M as N,Mr as me,Nr as he,Rr as ge,Y as _e,_ as ve,ar as ye,cr as be,ct as xe,dr as Se,dt as Ce,ft as P,h as we,ir as Te,j as Ee,jr as De,k as Oe,lr as ke,lt as Ae,nr as je,oi as Me,or as Ne,ot as Pe,pr as Fe,rr as Ie,si as Le,sr as Re,st as ze,ut as Be,yr as Ve}from"./index-DMIHPPHL.js";import{a as He,n as Ue}from"./fileCommentAnnotations-D-4zF4gD.js";var We=T(`pilcrow`,[[`path`,{d:`M13 4v16`,key:`8vvj80`}],[`path`,{d:`M17 4v16`,key:`7dpous`}],[`path`,{d:`M19 4H9.5a4.5 4.5 0 0 0 0 9H13`,key:`sh4n9v`}]]),F=e(n(),1);function Ge({threadRef:e,filePath:t,activeCwd:n,openInEditor:r,openFileSurface:i}){if(e){if(i){i(t);return}y.getState().openFile(e,t);return}r(n?S(t,n):t)}var I=r();function Ke(e,t){let n=(0,I.c)(4),r=Se(e,t),i;return n[0]!==r.data||n[1]!==r.error||n[2]!==r.isPending?(i={data:r.data,error:r.error,isPending:r.isPending},n[0]=r.data,n[1]=r.error,n[2]=r.isPending,n[3]=i):i=n[3],i}var L=t(),qe=[];function Je(e){return(e.endSide??e.side)===`deletions`?`deletions`:`additions`}function R(e,t,n){let r=Je(t),i=e.findIndex(e=>e.side===r&&e.lineNumber===t.end);return i<0?[...e,{side:r,lineNumber:t.end,metadata:{entries:[n]}}]:e.map((e,t)=>t===i?{...e,metadata:{entries:[...e.metadata.entries,n]}}:e)}function Ye(e){let t=(0,I.c)(50),{files:n,sectionId:r,sectionTitle:i,composerDraftTarget:a,options:o,viewerRef:s,className:c,renderHeaderPrefix:l}=e,ee=m(Qe),te=m(B),u;t[0]===a?u=t[1]:(u=e=>e.getComposerDraft(a)?.reviewComments??qe,t[0]=a,t[1]=u);let d=m(u),[f,p]=(0,F.useState)(null),[h,ie]=(0,F.useState)(null),g;t[2]===n?g=t[3]:(g=new Map(n.map(Ze)),t[2]=n,t[3]=g);let _=g,v;if(t[4]!==h||t[5]!==n||t[6]!==d||t[7]!==r){let e;t[9]!==h||t[10]!==d||t[11]!==r?(e=e=>{let{fileDiff:t,filePath:n,fileKey:i,collapsed:a}=e,o=d.filter(e=>e.sectionId===r&&e.filePath===n&&(e.fenceLanguage??`diff`)===`diff`).reduce((e,n)=>{let r=re(t,n);return r?R(e,r,{id:n.id,kind:`comment`,range:r,rangeLabel:n.rangeLabel,text:n.text}):e},[]),s=h?.fileKey===i?[...o,h.annotation]:o;return{id:i,type:`diff`,fileDiff:t,annotations:s,collapsed:a,version:ze(`${a?`1`:`0`}:${s.flatMap(z).join(`:`)}`)}},t[9]=h,t[10]=d,t[11]=r,t[12]=e):e=t[12],v=n.map(e),t[4]=h,t[5]=n,t[6]=d,t[7]=r,t[8]=v}else v=t[8];let ae=v,y;t[13]!==a||t[14]!==h?.annotation||t[15]!==te?(y=e=>{p(null),h?.annotation.metadata.entries.some(t=>t.id===e)?ie(null):te(a,e)},t[13]=a,t[14]=h?.annotation,t[15]=te,t[16]=y):y=t[16];let b=y,x;t[17]!==ee||t[18]!==a||t[19]!==h||t[20]!==_||t[21]!==r||t[22]!==i?(x=(e,t)=>{let n=h?.annotation.metadata.entries.find(t=>t.id===e),o=h?_.get(h.fileKey):void 0;if(!n||!o)return;let s=ne({id:n.id,sectionId:r,sectionTitle:i,filePath:o.filePath,fileDiff:o.fileDiff,range:n.range,text:t});s&&ee(a,s),p(null),ie(null)},t[17]=ee,t[18]=a,t[19]=h,t[20]=_,t[21]=r,t[22]=i,t[23]=x):x=t[23];let S=x,C;t[24]!==_||t[25]!==r||t[26]!==i?(C=(e,t)=>{if(!e)return;let n=t.item;if(n.type!==`diff`)return;let a=_.get(n.id);if(!a)return;let o=Ue(),s=ne({id:o,sectionId:r,sectionTitle:i,filePath:a.filePath,fileDiff:a.fileDiff,range:e,text:``});s&&ie({fileKey:n.id,annotation:{side:Je(e),lineNumber:e.end,metadata:{entries:[{id:o,kind:`draft`,range:e,rangeLabel:s.rangeLabel,text:``}]}}})},t[24]=_,t[25]=r,t[26]=i,t[27]=C):C=t[27];let w=C,oe=h!==null,T;t[28]===s?T=t[29]:(T=s?{ref:s}:{},t[28]=s,t[29]=T);let E;t[30]===c?E=t[31]:(E=c?{className:c}:{},t[30]=c,t[31]=E);let D=!oe,se=!oe,k;t[32]!==w||t[33]!==o||t[34]!==se||t[35]!==D?(k={...o,enableGutterUtility:D,enableLineSelection:se,onLineSelectionEnd:w},t[32]=w,t[33]=o,t[34]=se,t[35]=D,t[36]=k):k=t[36];let A;t[37]===l?A=t[38]:(A=e=>e.type===`diff`?l(e.fileDiff,e.id,e.collapsed===!0):null,t[37]=l,t[38]=A);let j;t[39]!==b||t[40]!==S?(j=e=>(0,L.jsx)(`div`,{className:`py-1`,children:e.metadata.entries.map(e=>(0,L.jsx)(He,{kind:e.kind,rangeLabel:e.rangeLabel,text:e.text,onCancel:()=>b(e.id),onComment:t=>S(e.id,t),onDelete:()=>b(e.id)},e.id))}),t[39]=b,t[40]=S,t[41]=j):j=t[41];let M;return t[42]!==ae||t[43]!==f||t[44]!==k||t[45]!==A||t[46]!==j||t[47]!==T||t[48]!==E?(M=(0,L.jsx)(O,{...T,...E,items:ae,selectedLines:f,onSelectedLinesChange:p,options:k,renderHeaderPrefix:A,renderAnnotation:j}),t[42]=ae,t[43]=f,t[44]=k,t[45]=A,t[46]=j,t[47]=T,t[48]=E,t[49]=M):M=t[49],M}function z(e){return e.metadata.entries.map(Xe)}function Xe(e){return`${e.id}:${e.rangeLabel}:${e.text}`}function Ze(e){return[e.fileKey,e]}function B(e){return e.removeReviewComment}function Qe(e){return e.addReviewComment}function $e(e){return{diffPreview:w(e,{label:`environment-data:review:diff-preview`,tag:a.reviewGetDiffPreview,staleTimeMs:5e3})}}var et=$e(c);function V(e){return e.remoteName&&e.name.startsWith(`${e.remoteName}/`)?e.name.slice(e.remoteName.length+1):e.name}function tt(e,t){let n=new Set(t),r=e.map(e=>{let r=t.filter(t=>n.has(t)&&V(t)===e.name),i=r.find(e=>e.remoteName===`origin`)??r[0]??null;return i&&n.delete(i),{id:`local:${e.name}`,label:e.name,local:e,remote:i}}),i=t.filter(e=>n.has(e)).map(e=>({id:`remote:${e.name}`,label:e.name,local:null,remote:e}));return[...r,...i]}function nt(e,t){let n=t.trim().toLocaleLowerCase();return n.length===0?e:e.filter(e=>e.label.toLocaleLowerCase().includes(n)||e.local?.name.toLocaleLowerCase().includes(n)===!0||e.remote?.name.toLocaleLowerCase().includes(n)===!0)}var H=`__automatic_base_ref__`,rt=new Set,it=`
2
2
  [data-diffs-header],
3
3
  [data-diff],
4
4
  [data-file],
@@ -95,4 +95,4 @@ import{r as e}from"./rolldown-runtime-QTnfLwEv.js";import{n as t,r as n,t as r}f
95
95
  text-decoration-color: currentColor;
96
96
  }
97
97
  `;function U({mode:e=`inline`,composerDraftTarget:t,initialGitScope:n,threadRef:r}){let{resolvedTheme:a}=v(),c=de(),[ne]=(0,F.useState)(n),[re,m]=(0,F.useState)(`stacked`),[y,S]=(0,F.useState)(c.wordWrap),[w,T]=(0,F.useState)(c.diffIgnoreWhitespace),[O,ve]=(0,F.useState)(``),[Se,ze]=(0,F.useState)(()=>({scopeKey:null,fileKeys:rt})),He=(0,F.useRef)(null),Ue=r.threadId,I=he(r),qe=I?.projectId??null,Je=me(I&&qe?{environmentId:I.environmentId,projectId:qe}:null),R=I?.worktreePath??Je?.workspaceRoot,z=u(oe.configValueAtom(I?.environmentId??null)),Xe=Fe(I?.environmentId??null,z?.availableEditors??[]),Ze=l(I!=null&&R!=null?De.status({environmentId:I.environmentId,input:{cwd:R}}):null),B=N(e=>Ee(e.byThreadKey,r,ne===`unstaged`)),Qe=Ze.data?.isRepo??!0,{turnDiffSummaries:$e,inferredCheckpointTurnCountByTurnId:V}=ue(I),U=(0,F.useMemo)(()=>[...$e].toSorted((e,t)=>{let n=e.checkpointTurnCount??V[e.turnId]??0,r=t.checkpointTurnCount??V[t.turnId]??0;return n===r?t.completedAt.localeCompare(e.completedAt):r-n}),[V,$e]);(0,F.useEffect)(()=>{B.kind===`turn`&&N.getState().reconcileTurnSelection(r,U.map(e=>e.turnId))},[B,U,r]);let W=B.kind===`turn`?B.turnId:null,G=B.kind===`unstaged`?`unstaged`:`branch`,K=B.kind===`branch`?B.baseRef:null,at=B.kind===`turn`?B.filePath:null,ot=B.kind===`turn`?B.revealRequestId:0,q=W===null?void 0:U.find(e=>e.turnId===W)??U[0],J=q&&(q.checkpointTurnCount??V[q.turnId]),st=U[0],ct=W===null?G===`unstaged`?`Working tree`:`Branch changes`:q?.turnId===st?.turnId?`Latest turn`:`Turn ${J??`?`}`,lt=q?`turn:${q.turnId}`:G,Y=`${r.environmentId}:${r.threadId}:${lt}`,ut=Se.scopeKey===Y?Se.fileKeys:rt,dt=q?`Turn ${J??`?`}`:G===`unstaged`?`Working tree`:`Branch changes`,ft=(0,F.useMemo)(()=>typeof J==`number`?{fromTurnCount:Math.max(0,J-1),toTurnCount:J}:null,[J]),pt=Ke({environmentId:I?.environmentId??null,threadId:Ue,fromTurnCount:ft?.fromTurnCount??null,toTurnCount:ft?.toTurnCount??null,ignoreWhitespace:w,cacheScope:q?`turn:${q.turnId}`:null},{enabled:Qe&&q!==void 0}),mt=l(W===null&&I&&R?et.diffPreview({environmentId:I.environmentId,input:{cwd:R,...K?{baseRef:K}:{},ignoreWhitespace:w}}):null),ht=W===null&&mt.error?.includes(`configured workspace root`)===!0&&z?.cwd!==void 0&&z.cwd!==R,gt=l(ht&&I&&z?et.diffPreview({environmentId:I.environmentId,input:{cwd:z.cwd,...K?{baseRef:K}:{},ignoreWhitespace:w}}):null),X=ht?gt:mt,Z=X.data?.sources.find(e=>e.kind===(G===`unstaged`?`working-tree`:`branch-range`)),_t=l(W===null&&G===`branch`&&I&&X.data?.cwd?De.listRefs({environmentId:I.environmentId,input:{cwd:X.data.cwd,includeMatchingRemoteRefs:!0,refKind:`local`,...O.trim().length>0?{query:O.trim()}:{},limit:100}}):null),vt=l(W===null&&G===`branch`&&I&&X.data?.cwd?De.listRefs({environmentId:I.environmentId,input:{cwd:X.data.cwd,includeMatchingRemoteRefs:!0,refKind:`remote`,...O.trim().length>0?{query:O.trim()}:{},limit:100}}):null),yt=tt(_t.data?.refs.filter(e=>e.name!==Z?.headRef)??[],vt.data?.refs??[]),bt=nt(yt,O),xt=e=>K&&K===e.remote?.name?K:e.local?.name??e.remote?.name??e.id,St=[H,...yt.map(xt)],Ct=[...O.trim().length===0?[H]:[],...bt.map(xt)],wt=Z?.diff,Tt=q?pt.data?.diff:wt,Et=!q&&Z?.truncated===!0,Dt=q?pt.isPending:X.isPending,Ot=q?pt.error:X.error,kt=typeof Tt==`string`&&Tt.trim().length===0,Q=(0,F.useMemo)(()=>Be(Tt,`diff-panel:${a}`,{compactPartialHunkOffsets:W===null}),[a,Tt,W]),At=(0,F.useMemo)(()=>!Q||Q.kind!==`files`?[]:Q.files.toSorted((e,t)=>P(e).localeCompare(P(t),void 0,{numeric:!0,sensitivity:`base`})),[Q]),$=(0,F.useMemo)(()=>At.map(e=>{let t=Pe(e);return{fileDiff:e,filePath:P(e),fileKey:t,collapsed:ut.has(t)}}),[ut,At]),jt=(0,F.useMemo)(()=>$.map(e=>e.fileKey),[$]),Mt=M(jt,ut),Nt=(0,F.useMemo)(()=>Ae(At),[At]);(0,F.useEffect)(()=>{if(!at)return;let e=$.find(e=>e.filePath===at);e&&He.current?.scrollTo({type:`item`,id:e.fileKey,align:`start`})},[$,at,ot]);let Pt=Oe({threadRef:r,workspaceRoot:R??null}),Ft=(0,F.useCallback)(e=>{Ge({threadRef:r,filePath:e,activeCwd:R,openFileSurface:Pt,openInEditor:e=>{(async()=>{let t=await Xe(e);t._tag===`Failure`&&!C(t)&&console.warn(`Failed to open diff file in editor.`,{operation:`open-diff-file`,environmentId:r.environmentId,threadId:r.threadId,...ee(ae(t))})})()}})},[R,Pt,Xe,r]),It=(0,F.useCallback)(e=>{ze(t=>{let n=new Set(t.scopeKey===Y?t.fileKeys:[]);return n.has(e)?n.delete(e):n.add(e),{scopeKey:Y,fileKeys:n}})},[Y]),Lt=(0,F.useCallback)(()=>{ze(e=>{let t=e.scopeKey===Y?e.fileKeys:rt;return{scopeKey:Y,fileKeys:k(jt,t)}})},[Y,jt]),Rt=e=>{N.getState().selectTurn(r,e)},zt=e=>{N.getState().selectGitScope(r,e)},Bt=e=>{N.getState().selectBranchBaseRef(r,e)};return(0,L.jsx)(_e,{mode:e,header:(0,L.jsxs)(L.Fragment,{children:[(0,L.jsxs)(`div`,{className:`flex min-w-0 flex-1 items-center gap-3 [-webkit-app-region:no-drag]`,children:[(0,L.jsxs)(o,{children:[(0,L.jsxs)(i,{className:`inline-flex h-6 max-w-full items-center gap-1 rounded-md bg-muted/70 px-2 text-xs font-medium text-foreground outline-none transition-colors hover:bg-muted focus-visible:ring-2 focus-visible:ring-ring`,"aria-label":`Diff scope: ${ct}`,children:[(0,L.jsx)(`span`,{className:`truncate`,children:ct}),(0,L.jsx)(p,{className:`size-3.5 shrink-0 text-muted-foreground`})]}),(0,L.jsxs)(te,{align:`start`,className:`w-60`,children:[(0,L.jsx)(h,{className:W===null&&G===`unstaged`?`bg-foreground/[0.08]`:void 0,onClick:()=>zt(`unstaged`),children:(0,L.jsx)(`span`,{children:`Working tree`})}),(0,L.jsx)(h,{className:W===null&&G===`branch`?`bg-foreground/[0.08]`:void 0,onClick:()=>zt(`branch`),children:(0,L.jsx)(`span`,{children:`Branch changes`})}),(0,L.jsx)(h,{className:W!==null&&q?.turnId===st?.turnId?`bg-foreground/[0.08]`:void 0,onClick:()=>{st&&Rt(st.turnId)},children:(0,L.jsx)(`span`,{children:`Latest turn`})}),(0,L.jsxs)(ie,{children:[(0,L.jsx)(f,{children:`Turn`}),(0,L.jsx)(g,{className:`w-64`,children:U.map(e=>{let t=e.checkpointTurnCount??V[e.turnId]??`?`;return(0,L.jsxs)(h,{className:e.turnId===q?.turnId?`bg-foreground/[0.08]`:void 0,onClick:()=>Rt(e.turnId),children:[(0,L.jsxs)(`span`,{children:[`Turn `,t]}),(0,L.jsx)(`span`,{className:`ml-auto text-xs tabular-nums text-muted-foreground`,children:Ve(e.completedAt,c.timestampFormat)})]},e.turnId)})})]})]})]}),W===null&&G===`branch`&&Z?.baseRef&&(0,L.jsxs)(`div`,{className:`flex min-w-0 max-w-full items-center gap-2 overflow-hidden text-xs text-muted-foreground`,title:`${Z.headRef??`HEAD`} → ${Z.baseRef}`,"aria-label":`Comparing ${Z.headRef??`HEAD`} against ${Z.baseRef}`,children:[(0,L.jsx)(`span`,{className:`min-w-0 max-w-48 truncate`,children:Z.headRef??`HEAD`}),(0,L.jsx)(Le,{className:`size-3.5 shrink-0 opacity-70`}),(0,L.jsxs)(je,{items:St,filteredItems:Ct,value:K??H,onOpenChange:e=>{e||ve(``)},onValueChange:e=>{e&&Bt(e===H?null:e)},children:[(0,L.jsxs)(be,{className:`inline-flex min-w-0 max-w-48 items-center gap-1 overflow-hidden rounded-md px-1.5 py-1 outline-none transition-colors hover:bg-muted hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring`,"aria-label":`Change comparison target. Currently ${Z.baseRef}`,children:[(0,L.jsx)(`span`,{className:`min-w-0 truncate`,children:Z.baseRef}),(0,L.jsx)(p,{className:`size-3.5 shrink-0 opacity-70`})]}),(0,L.jsxs)(Re,{align:`start`,className:`w-72 min-w-0 max-w-[calc(100vw-1rem)] overflow-hidden [&>[data-slot=combobox-popup]]:min-w-0 [&>[data-slot=combobox-popup]]:overflow-hidden`,children:[(0,L.jsx)(`div`,{className:`min-w-0 shrink-0 px-3 pt-2.5`,children:(0,L.jsxs)(`div`,{className:`relative -translate-y-px border-b border-border/70 pb-1.5 transition-colors focus-within:border-ring`,children:[(0,L.jsx)(ge,{"aria-hidden":`true`,className:`pointer-events-none absolute top-1.5 left-0 size-4 shrink-0 text-muted-foreground/55`}),(0,L.jsx)(Te,{className:`[&_input]:h-6.5 [&_input]:ps-5 [&_input]:font-sans [&_input]:leading-6.5`,inputClassName:`rounded-none bg-transparent text-sm`,placeholder:`Search refs...`,showTrigger:!1,size:`sm`,unstyled:!0,value:O,onChange:e=>ve(e.target.value)})]})}),(0,L.jsxs)(`div`,{className:`grid shrink-0 grid-cols-[1rem_minmax(0,1fr)] items-center gap-2 border-b border-border/70 ps-3 pe-6.5 pt-2 pb-1.5 font-medium text-[10px] text-muted-foreground uppercase tracking-wide`,children:[(0,L.jsx)(`span`,{"aria-hidden":`true`}),(0,L.jsxs)(`div`,{className:`grid min-w-0 grid-cols-[minmax(0,1fr)_2rem] items-center`,children:[(0,L.jsx)(`span`,{children:`Branch`}),(0,L.jsx)(`span`,{className:`text-right`,children:`Remote`})]})]}),(0,L.jsx)(Ie,{children:`No matching refs.`}),(0,L.jsxs)(Ne,{className:`max-h-64 min-w-0 overflow-x-hidden`,children:[(0,L.jsx)(ye,{className:`h-8 w-full min-w-0 grid-cols-[1rem_minmax(0,1fr)] py-0`,contentClassName:`w-full min-w-0 overflow-hidden`,value:H,children:(0,L.jsx)(`span`,{className:`block min-w-0 truncate`,children:`Automatic`})}),yt.map(e=>{let t=xt(e),n=e.local!==null&&e.remote!==null,r=e.remote?.name===t;return(0,L.jsx)(ye,{className:`h-8 w-full min-w-0 grid-cols-[1rem_minmax(0,1fr)] py-0`,contentClassName:`w-full min-w-0 overflow-hidden`,value:t,children:(0,L.jsxs)(`div`,{className:`grid w-full min-w-0 grid-cols-[minmax(0,1fr)_2rem] items-center overflow-hidden`,children:[(0,L.jsx)(`span`,{className:`block min-w-0 truncate pe-2`,children:e.label}),n?(0,L.jsx)(`div`,{className:`flex justify-end`,onClick:e=>e.stopPropagation(),onPointerDown:e=>e.stopPropagation(),children:(0,L.jsx)(ke,{"aria-label":`Use remote version of ${e.label}`,checked:r,className:`[--thumb-size:--spacing(3)]`,onCheckedChange:t=>{let n=t?e.remote?.name:e.local?.name;n&&Bt(n)}})}):e.remote?(0,L.jsx)(`span`,{className:`flex justify-end text-muted-foreground`,title:`Remote only`,children:(0,L.jsx)(s,{"aria-hidden":`true`,className:`size-3`})}):null]})},e.id)})]})]})]})]})]}),(0,L.jsxs)(`div`,{className:`flex shrink-0 items-center gap-1 [-webkit-app-region:no-drag]`,children:[$.length>0&&(0,L.jsx)(we,{additions:Nt.additions,deletions:Nt.deletions,className:`mr-1 text-[11px]`,layout:`inline`}),$.length>0&&(0,L.jsxs)(x,{children:[(0,L.jsx)(D,{render:(0,L.jsx)(d,{type:`button`,size:`icon-xs`,variant:`outline`,"aria-label":Mt?`Expand all files`:`Collapse all files`,onClick:Lt}),children:Mt?(0,L.jsx)(Me,{className:`size-3`}):(0,L.jsx)(se,{className:`size-3`})}),(0,L.jsx)(_,{side:`top`,children:Mt?`Expand all files`:`Collapse all files`})]}),(0,L.jsxs)(A,{className:`shrink-0`,variant:`outline`,size:`xs`,value:[re],onValueChange:e=>{let t=e[0];(t===`stacked`||t===`split`)&&m(t)},children:[(0,L.jsx)(le,{"aria-label":`Stacked diff view`,value:`stacked`,children:(0,L.jsx)(j,{className:`size-3`})}),(0,L.jsx)(le,{"aria-label":`Split diff view`,value:`split`,children:(0,L.jsx)(ce,{className:`size-3`})})]}),(0,L.jsxs)(x,{children:[(0,L.jsx)(D,{render:(0,L.jsx)(le,{"aria-label":y?`Disable diff line wrapping`:`Enable diff line wrapping`,variant:`outline`,size:`xs`,pressed:y,onPressedChange:e=>{S(!!e)}}),children:(0,L.jsx)(pe,{className:`size-3`})}),(0,L.jsx)(_,{side:`top`,children:y?`Disable line wrapping`:`Enable line wrapping`})]}),(0,L.jsxs)(x,{children:[(0,L.jsx)(D,{render:(0,L.jsx)(le,{"aria-label":w?`Show whitespace changes`:`Hide whitespace changes`,variant:`outline`,size:`xs`,pressed:w,onPressedChange:e=>{T(!!e)}}),children:(0,L.jsx)(We,{className:`size-3`})}),(0,L.jsx)(_,{side:`top`,children:w?`Show whitespace changes`:`Hide whitespace changes`})]})]})]}),children:I?Qe?W!==null&&U.length===0?(0,L.jsx)(`div`,{className:`flex flex-1 items-center justify-center px-5 text-center text-xs text-muted-foreground/70`,children:`No completed turns yet.`}):(0,L.jsx)(L.Fragment,{children:(0,L.jsxs)(`div`,{className:`diff-panel-viewport flex min-h-0 min-w-0 flex-1 flex-col overflow-hidden`,children:[Et&&(0,L.jsx)(`p`,{className:`shrink-0 border-b border-border/70 bg-muted/40 px-3 py-1.5 text-[11px] text-muted-foreground`,children:`This diff was truncated because it exceeded the preview limit. The changes shown are incomplete.`}),Ot&&!Q&&(0,L.jsx)(`div`,{className:`px-3`,children:(0,L.jsx)(`p`,{className:`mb-2 text-[11px] text-red-500/80`,children:Ot})}),Q?Q.kind===`files`?(0,L.jsx)(`div`,{className:`min-h-0 flex-1`,onClickCapture:e=>{let t=(e.nativeEvent.composedPath?.()??[]).find(e=>e instanceof HTMLElement&&e.hasAttribute(`data-title`))?.textContent?.trim();t&&Ft(t)},children:(0,L.jsx)(Ye,{viewerRef:He,className:`diff-render-surface h-full min-h-0 overflow-auto`,files:$,sectionId:lt,sectionTitle:dt,composerDraftTarget:t,renderHeaderPrefix:(e,t,n)=>{let r=P(e);return(0,L.jsxs)(x,{children:[(0,L.jsx)(D,{render:(0,L.jsx)(`button`,{type:`button`,className:b(`inline-flex size-5 shrink-0 cursor-pointer items-center justify-center rounded-sm border-0 bg-transparent p-0 transition-colors hover:bg-foreground/10 focus-visible:outline-hidden`,xe(e)),"aria-label":n?`Expand ${r}`:`Collapse ${r}`,"aria-expanded":!n,onClick:e=>{e.stopPropagation(),It(t)}}),children:n?(0,L.jsx)(E,{className:`size-4`}):(0,L.jsx)(p,{className:`size-4`})}),(0,L.jsx)(_,{side:`top`,children:n?`Expand diff`:`Collapse diff`})]})},options:{diffStyle:re===`split`?`split`:`unified`,lineDiffType:`none`,overflow:y?`wrap`:`scroll`,theme:Ce(a),themeType:a,unsafeCSS:it,stickyHeaders:!0,itemMetrics:{diffHeaderHeight:33},layout:{paddingTop:0,paddingBottom:8,gap:8}}},Y??lt)}):(0,L.jsx)(`div`,{className:`min-h-0 flex-1 overflow-auto p-2`,children:(0,L.jsxs)(`div`,{className:`space-y-2`,children:[(0,L.jsx)(`p`,{className:`text-[11px] text-muted-foreground/75`,children:Q.reason}),(0,L.jsx)(`pre`,{className:b(`max-h-[72vh] rounded-md border border-border/70 bg-background/70 p-3 font-mono text-[11px] leading-relaxed text-muted-foreground/90`,y?`overflow-auto whitespace-pre-wrap wrap-break-word`:`overflow-auto`),children:Q.text})]})}):Dt?(0,L.jsx)(fe,{label:q?`Loading checkpoint diff...`:G===`unstaged`?`Loading working tree diff...`:`Loading branch diff...`}):(0,L.jsx)(`div`,{className:`flex h-full items-center justify-center px-3 py-2 text-xs text-muted-foreground/70`,children:(0,L.jsx)(`p`,{children:kt?`No net changes in this selection.`:`No patch available for this selection.`})})]})}):(0,L.jsx)(`div`,{className:`flex flex-1 items-center justify-center px-5 text-center text-xs text-muted-foreground/70`,children:`Turn diffs are unavailable because this project is not a git repository.`}):(0,L.jsx)(`div`,{className:`flex flex-1 items-center justify-center px-5 text-center text-xs text-muted-foreground/70`,children:`Select a thread to inspect turn diffs.`})})}export{ve as DiffWorkerPoolProvider,U as default};
98
- //# sourceMappingURL=DiffPanel-D-49iCgp.js.map
98
+ //# sourceMappingURL=DiffPanel-vLwzeivL.js.map