billion-context 0.1.73 → 0.1.74-pr.396.296

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -50217,12 +50217,6 @@ ${body.slice(0, 4e3)}...`;
50217
50217
  ${body}`;
50218
50218
  }
50219
50219
 
50220
- // src/sse-util.ts
50221
- function normalizeSseLineEndings(buf) {
50222
- if (buf.indexOf("\r") === -1) return buf;
50223
- return buf.replace(/\r\n|\r/g, "\n");
50224
- }
50225
-
50226
50220
  // src/loop/tag-echo-filter.ts
50227
50221
  var PAIRED = /\x3cacp\s[^<>]{0,256}>([^<>]{0,64})<\/acp>/;
50228
50222
  var LONE_OPEN = /\x3cacp(?:\s[^<>]{0,256})?>/;
@@ -51466,8 +51460,26 @@ function recordUsage(ctx, usage, round) {
51466
51460
  async function* runCompressLoop(upstream, ctx, requestBody, requestOptions, adapter, systemPrompt, signal) {
51467
51461
  let activeClearTimer = null;
51468
51462
  let currentUpstream = upstream;
51463
+ let roundBody = requestBody;
51469
51464
  const coreMessages = [...ctx.messages];
51470
51465
  let degradedRetried = false;
51466
+ let truncationRetried = false;
51467
+ const fetchUpstream = (body) => fetchWithRetry(
51468
+ requestOptions.url,
51469
+ {
51470
+ method: "POST",
51471
+ headers: requestOptions.headers,
51472
+ body: JSON.stringify(body),
51473
+ ...ctx.proxyUrl ? { dispatcher: proxyDispatcher(ctx.proxyUrl) } : {}
51474
+ },
51475
+ void 0,
51476
+ signal,
51477
+ (info) => {
51478
+ const lc = lastCompressSuffix(ctx.session.lastCompress);
51479
+ ctx.log(`[acp-proxy: upstream rejected replay (HTTP ${info.status}: ${info.detail.slice(0, 120)}); likely provider risk-control \u2014 retrying in ${info.delayMs}ms (attempt ${info.attempt}/${info.maxAttempts})${lc}]`);
51480
+ log("warn", `[acp-loop] upstream rejected replay (HTTP ${info.status}); retrying in ${info.delayMs}ms (attempt ${info.attempt}/${info.maxAttempts})${lc}`);
51481
+ }
51482
+ );
51471
51483
  try {
51472
51484
  for (let round = 1; round <= MAX_LOOP_ROUNDS; round++) {
51473
51485
  if (signal?.aborted) break;
@@ -51480,50 +51492,93 @@ async function* runCompressLoop(upstream, ctx, requestBody, requestOptions, adap
51480
51492
  let finishReason;
51481
51493
  let sawDone = false;
51482
51494
  let suppressCompletion = false;
51483
- for await (const ev of adapter.parseStream(currentUpstream, round)) {
51484
- if (signal?.aborted) break;
51485
- if (ev.kind === "text") {
51486
- assistantText += ev.delta;
51487
- if (!ctx.textProtocol && ev.raw) {
51488
- yield ev.raw;
51489
- } else if (!ctx.textProtocol && round > 1 && ev.delta.length > 0) {
51490
- yield adapter.emitText(ev.delta);
51491
- }
51492
- } else if (ev.kind === "reasoning") {
51493
- assistantReasoning += ev.delta;
51494
- let seg = reasoningSegments[reasoningSegments.length - 1];
51495
- if (reasoningSealed || !seg) {
51496
- seg = { text: "", signature: "" };
51497
- reasoningSegments.push(seg);
51498
- reasoningSealed = false;
51499
- }
51500
- seg.text += ev.delta;
51501
- if (ev.signature) seg.signature += ev.signature;
51502
- if (ev.blockEnd) reasoningSealed = true;
51503
- if (!ctx.textProtocol) {
51504
- if (ev.raw) {
51505
- yield ev.raw;
51506
- } else if (round > 1 && ev.delta.length > 0 && adapter.emitReasoning) {
51507
- yield adapter.emitReasoning(ev.delta);
51508
- }
51509
- }
51510
- } else if (ev.kind === "tool_call") {
51511
- calls.push({ name: ev.name, callId: ev.callId, arguments: ev.arguments, passthrough: ev.passthrough });
51512
- } else if (ev.kind === "usage") {
51513
- usage = {
51514
- inputTokens: ev.inputTokens,
51515
- outputTokens: ev.outputTokens,
51516
- cachedTokens: ev.cachedTokens
51517
- };
51518
- } else if (ev.kind === "done") {
51519
- sawDone = true;
51520
- finishReason = ev.finishReason;
51521
- suppressCompletion = ev.suppressCompletion === true;
51522
- } else if (ev.kind === "meta") {
51523
- if (round === 1 || !ev.firstRoundOnly) {
51524
- yield ev.chunk;
51495
+ let truncatedDone = false;
51496
+ let forwardedAny = false;
51497
+ const fwd = (chunk) => {
51498
+ forwardedAny = true;
51499
+ return chunk;
51500
+ };
51501
+ for (; ; ) {
51502
+ assistantText = "";
51503
+ assistantReasoning = "";
51504
+ reasoningSegments.length = 0;
51505
+ reasoningSealed = true;
51506
+ calls.length = 0;
51507
+ usage = {};
51508
+ finishReason = void 0;
51509
+ sawDone = false;
51510
+ suppressCompletion = false;
51511
+ truncatedDone = false;
51512
+ forwardedAny = false;
51513
+ for await (const ev of adapter.parseStream(currentUpstream, round)) {
51514
+ if (signal?.aborted) break;
51515
+ if (ev.kind === "text") {
51516
+ assistantText += ev.delta;
51517
+ if (!ctx.textProtocol && ev.raw) {
51518
+ yield fwd(ev.raw);
51519
+ } else if (!ctx.textProtocol && round > 1 && ev.delta.length > 0) {
51520
+ yield fwd(adapter.emitText(ev.delta));
51521
+ }
51522
+ } else if (ev.kind === "reasoning") {
51523
+ assistantReasoning += ev.delta;
51524
+ let seg = reasoningSegments[reasoningSegments.length - 1];
51525
+ if (reasoningSealed || !seg) {
51526
+ seg = { text: "", signature: "" };
51527
+ reasoningSegments.push(seg);
51528
+ reasoningSealed = false;
51529
+ }
51530
+ seg.text += ev.delta;
51531
+ if (ev.signature) seg.signature += ev.signature;
51532
+ if (ev.blockEnd) reasoningSealed = true;
51533
+ if (!ctx.textProtocol) {
51534
+ if (ev.raw) {
51535
+ yield fwd(ev.raw);
51536
+ } else if (round > 1 && ev.delta.length > 0 && adapter.emitReasoning) {
51537
+ yield fwd(adapter.emitReasoning(ev.delta));
51538
+ }
51539
+ }
51540
+ } else if (ev.kind === "tool_call") {
51541
+ calls.push({ name: ev.name, callId: ev.callId, arguments: ev.arguments, passthrough: ev.passthrough });
51542
+ } else if (ev.kind === "usage") {
51543
+ usage = {
51544
+ inputTokens: ev.inputTokens,
51545
+ outputTokens: ev.outputTokens,
51546
+ cachedTokens: ev.cachedTokens
51547
+ };
51548
+ } else if (ev.kind === "done") {
51549
+ sawDone = true;
51550
+ finishReason = ev.finishReason;
51551
+ suppressCompletion = ev.suppressCompletion === true;
51552
+ truncatedDone = ev.truncated === true;
51553
+ } else if (ev.kind === "meta") {
51554
+ if (round === 1 || !ev.firstRoundOnly) {
51555
+ yield fwd(ev.chunk);
51556
+ }
51557
+ }
51558
+ }
51559
+ if ((!sawDone || truncatedDone) && !forwardedAny && calls.length === 0 && !(ctx.textProtocol && assistantText.length > 0) && !signal?.aborted && !truncationRetried) {
51560
+ truncationRetried = true;
51561
+ ctx.log(`[acp-loop] round ${round}: upstream truncated before any content reached the client; retrying fetch once`);
51562
+ try {
51563
+ const respResult2 = await fetchUpstream(roundBody);
51564
+ if (!respResult2.response.body) {
51565
+ respResult2.clearTimer();
51566
+ throw new UpstreamHttpError(respResult2.response.status, "(empty response body)", 1);
51567
+ }
51568
+ currentUpstream = respResult2.response.body;
51569
+ if (activeClearTimer) activeClearTimer();
51570
+ activeClearTimer = respResult2.clearTimer;
51571
+ continue;
51572
+ } catch (e) {
51573
+ if (e instanceof UpstreamHttpError) {
51574
+ const suffix = e.attempts > 1 ? ` after ${e.attempts} attempt(s)` : "";
51575
+ ctx.log(`[acp-loop] round ${round}: truncation retry failed (upstream error ${e.status}${suffix}: ${e.body.slice(0, 200)})`);
51576
+ } else {
51577
+ ctx.log(`[acp-loop] round ${round}: truncation retry failed (${e instanceof Error ? e.message : String(e)})`);
51578
+ }
51525
51579
  }
51526
51580
  }
51581
+ break;
51527
51582
  }
51528
51583
  if (usage.inputTokens !== void 0 || usage.outputTokens !== void 0 || usage.cachedTokens !== void 0) {
51529
51584
  recordUsage(ctx, usage, round);
@@ -51657,7 +51712,6 @@ async function* runCompressLoop(upstream, ctx, requestBody, requestOptions, adap
51657
51712
  const partialReasoning = assistantReasoning.length;
51658
51713
  const msg = `upstream stream truncated (no completion event; round ${round}, ${partialText} text chars + ${partialReasoning} reasoning chars received)`;
51659
51714
  ctx.log(`[acp-loop] round ${round}: ${msg}`);
51660
- log("error", `[acp-loop] ${msg}`);
51661
51715
  yield adapter.emitError(msg);
51662
51716
  return;
51663
51717
  }
@@ -51674,22 +51728,6 @@ async function* runCompressLoop(upstream, ctx, requestBody, requestOptions, adap
51674
51728
  }
51675
51729
  ctx.log(`[acp-loop] round ${round}: proxy tool executed; re-requesting so the model sees the result`);
51676
51730
  if (signal?.aborted) break;
51677
- const fetchUpstream = (body) => fetchWithRetry(
51678
- requestOptions.url,
51679
- {
51680
- method: "POST",
51681
- headers: requestOptions.headers,
51682
- body: JSON.stringify(body),
51683
- ...ctx.proxyUrl ? { dispatcher: proxyDispatcher(ctx.proxyUrl) } : {}
51684
- },
51685
- void 0,
51686
- signal,
51687
- (info) => {
51688
- const lc = lastCompressSuffix(ctx.session.lastCompress);
51689
- ctx.log(`[acp-proxy: upstream rejected replay (HTTP ${info.status}: ${info.detail.slice(0, 120)}); likely provider risk-control \u2014 retrying in ${info.delayMs}ms (attempt ${info.attempt}/${info.maxAttempts})${lc}]`);
51690
- log("warn", `[acp-loop] upstream rejected replay (HTTP ${info.status}); retrying in ${info.delayMs}ms (attempt ${info.attempt}/${info.maxAttempts})${lc}`);
51691
- }
51692
- );
51693
51731
  let newBody = adapter.buildRequest(coreMessages, systemPrompt, requestBody);
51694
51732
  if (process.env.ACP_DUMP_BODY === "1") {
51695
51733
  try {
@@ -51732,6 +51770,7 @@ async function* runCompressLoop(upstream, ctx, requestBody, requestOptions, adap
51732
51770
  return;
51733
51771
  }
51734
51772
  currentUpstream = respResult.response.body;
51773
+ roundBody = newBody;
51735
51774
  if (activeClearTimer) activeClearTimer();
51736
51775
  activeClearTimer = respResult.clearTimer;
51737
51776
  }
@@ -52056,6 +52095,20 @@ function createResponsesAdapter(textProtocol, projection) {
52056
52095
  let responseObj = null;
52057
52096
  let terminalRaw = null;
52058
52097
  let terminalKind = null;
52098
+ let createdForwarded = false;
52099
+ let createdRespId = null;
52100
+ const ensureCreated = () => {
52101
+ if (createdForwarded) return null;
52102
+ createdForwarded = true;
52103
+ if (!createdRespId) createdRespId = `resp-proxy-${Date.now()}`;
52104
+ return Buffer.from(
52105
+ `event: response.created
52106
+ data: ${JSON.stringify({ type: "response.created", response: { id: createdRespId, status: "in_progress", output: [] } })}
52107
+
52108
+ `,
52109
+ "utf8"
52110
+ );
52111
+ };
52059
52112
  return {
52060
52113
  buildRequest(coreMessages, systemPrompt, requestBody) {
52061
52114
  const customToolCallIds = /* @__PURE__ */ new Set();
@@ -52110,6 +52163,11 @@ function createResponsesAdapter(textProtocol, projection) {
52110
52163
  outputIndex = Math.max(outputIndex, obj.output_index + 1);
52111
52164
  }
52112
52165
  if (type === "response.created" || type === "response.in_progress") {
52166
+ if (type === "response.created") {
52167
+ const resp = obj.response;
52168
+ if (resp && typeof resp.id === "string") createdRespId = resp.id;
52169
+ if (round === 1) createdForwarded = true;
52170
+ }
52113
52171
  yield { kind: "meta", chunk: rawBuf, firstRoundOnly: true };
52114
52172
  } else if (type === "response.output_item.added") {
52115
52173
  const item = obj.item;
@@ -52243,7 +52301,7 @@ function createResponsesAdapter(textProtocol, projection) {
52243
52301
  }
52244
52302
  }
52245
52303
  if (!terminalKind) {
52246
- yield { kind: "done", finishReason: "failed" };
52304
+ yield { kind: "done", finishReason: "failed", truncated: true };
52247
52305
  }
52248
52306
  },
52249
52307
  emitText(delta) {
@@ -52266,18 +52324,22 @@ function createResponsesAdapter(textProtocol, projection) {
52266
52324
  return terminalRaw;
52267
52325
  }
52268
52326
  if (!responseObj && opts?.finishReason === "failed") {
52327
+ const parts = [];
52328
+ const created = ensureCreated();
52329
+ if (created) parts.push(created);
52269
52330
  const failed = {
52270
- id: `resp-error-${Date.now()}`,
52331
+ id: createdRespId ?? `resp-error-${Date.now()}`,
52271
52332
  status: "failed",
52272
52333
  error: { code: "server_error", message: "upstream returned no response" }
52273
52334
  };
52274
- return Buffer.from(
52335
+ parts.push(Buffer.from(
52275
52336
  `event: response.failed
52276
52337
  data: ${JSON.stringify({ type: "response.failed", response: failed })}
52277
52338
 
52278
52339
  `,
52279
52340
  "utf8"
52280
- );
52341
+ ));
52342
+ return Buffer.concat(parts);
52281
52343
  }
52282
52344
  let resp = responseObj ? { ...responseObj } : { id: `resp-proxy-${Date.now()}`, status: "completed", output: [] };
52283
52345
  if (opts?.usage) {
@@ -52297,18 +52359,22 @@ data: ${JSON.stringify({ type: "response.failed", response: failed })}
52297
52359
  return buildCompleted(resp);
52298
52360
  },
52299
52361
  emitError(message) {
52362
+ const parts = [];
52363
+ const created = ensureCreated();
52364
+ if (created) parts.push(created);
52300
52365
  const resp = {
52301
- id: `resp-error-${Date.now()}`,
52366
+ id: createdRespId ?? `resp-error-${Date.now()}`,
52302
52367
  status: "failed",
52303
52368
  error: { code: "server_error", message }
52304
52369
  };
52305
- return Buffer.from(
52370
+ parts.push(Buffer.from(
52306
52371
  `event: response.failed
52307
52372
  data: ${JSON.stringify({ type: "response.failed", response: resp })}
52308
52373
 
52309
52374
  `,
52310
52375
  "utf8"
52311
- );
52376
+ ));
52377
+ return Buffer.concat(parts);
52312
52378
  },
52313
52379
  extractTextTriggers(text) {
52314
52380
  const calls = [];
@@ -52818,6 +52884,33 @@ function createAnthropicAdapter(requestBody, originalSystem) {
52818
52884
  const model = requestBody.model ?? void 0;
52819
52885
  let messageId;
52820
52886
  let clientIndex = 0;
52887
+ let messageStartForwarded = false;
52888
+ const openBlocks = [];
52889
+ const removeOpenBlock = (index) => {
52890
+ const i = openBlocks.indexOf(index);
52891
+ if (i >= 0) openBlocks.splice(i, 1);
52892
+ };
52893
+ const buildSyntheticMessageStart = () => {
52894
+ const extra = {};
52895
+ if (model) extra.model = model;
52896
+ const msg = {
52897
+ id: messageId ?? `msg_acp_error_${Date.now()}`,
52898
+ type: "message",
52899
+ role: "assistant",
52900
+ content: [],
52901
+ stop_reason: null,
52902
+ stop_sequence: null,
52903
+ usage: { input_tokens: 0, output_tokens: 0 },
52904
+ ...extra
52905
+ };
52906
+ return Buffer.from(
52907
+ `event: message_start
52908
+ data: ${JSON.stringify({ type: "message_start", message: msg })}
52909
+
52910
+ `,
52911
+ "utf8"
52912
+ );
52913
+ };
52821
52914
  const buildTextBlock = (index, text) => Buffer.from(
52822
52915
  `event: content_block_start
52823
52916
  data: ${JSON.stringify({ type: "content_block_start", index, content_block: { type: "text", text: "" } })}
@@ -52901,6 +52994,7 @@ ${systemPrompt}` : systemPrompt;
52901
52994
  if (typeof u2.input_tokens === "number") roundInput = u2.input_tokens;
52902
52995
  if (typeof u2.cache_read_input_tokens === "number") roundCached = u2.cache_read_input_tokens;
52903
52996
  if (round === 1) {
52997
+ messageStartForwarded = true;
52904
52998
  yield { kind: "meta", chunk: rawBuf, firstRoundOnly: true };
52905
52999
  }
52906
53000
  } else if (type === "ping") {
@@ -52916,6 +53010,7 @@ ${systemPrompt}` : systemPrompt;
52916
53010
  if (block.type === "thinking" || block.type === "redacted_thinking") thinkingIndexes.add(upstreamIndex);
52917
53011
  const ci2 = clientIndex++;
52918
53012
  indexMap.set(upstreamIndex, ci2);
53013
+ openBlocks.push(ci2);
52919
53014
  yield { kind: "meta", chunk: remapIndexInEvent(eventStr, ci2), firstRoundOnly: round === 1 };
52920
53015
  }
52921
53016
  } else if (type === "content_block_delta") {
@@ -52965,10 +53060,12 @@ ${systemPrompt}` : systemPrompt;
52965
53060
  };
52966
53061
  } else if (thinkingIndexes.delete(upstreamIndex)) {
52967
53062
  const ci2 = indexMap.get(upstreamIndex) ?? upstreamIndex;
53063
+ removeOpenBlock(ci2);
52968
53064
  yield { kind: "meta", chunk: remapIndexInEvent(eventStr, ci2), firstRoundOnly: false };
52969
53065
  yield { kind: "reasoning", delta: "", blockEnd: true };
52970
53066
  } else {
52971
53067
  const ci2 = indexMap.get(upstreamIndex) ?? upstreamIndex;
53068
+ removeOpenBlock(ci2);
52972
53069
  if (lastTextIndex !== null) {
52973
53070
  const tail = tagFilter.flush();
52974
53071
  if (tail.length > 0) {
@@ -53060,10 +53157,22 @@ data: ${JSON.stringify({ type: "content_block_stop", index })}
53060
53157
  );
53061
53158
  },
53062
53159
  emitError(message) {
53063
- const errBlock = buildTextBlock(clientIndex++, `
53160
+ const parts = [];
53161
+ if (!messageStartForwarded) {
53162
+ parts.push(buildSyntheticMessageStart());
53163
+ messageStartForwarded = true;
53164
+ }
53165
+ for (const index of openBlocks.splice(0)) {
53166
+ parts.push(Buffer.from(`event: content_block_stop
53167
+ data: ${JSON.stringify({ type: "content_block_stop", index })}
53168
+
53169
+ `, "utf8"));
53170
+ }
53171
+ parts.push(buildTextBlock(clientIndex++, `
53064
53172
  [acp-proxy: ${message}]
53065
- `);
53066
- return Buffer.concat([errBlock, buildTerminal("end_turn", 0, 0, 0)]);
53173
+ `));
53174
+ parts.push(buildTerminal("end_turn", 0, 0, 0));
53175
+ return Buffer.concat(parts);
53067
53176
  }
53068
53177
  };
53069
53178
  }
@@ -53383,6 +53492,11 @@ function rewriteResponsesJsonResponse(body, ctx) {
53383
53492
  if (!body || typeof body !== "object") return body;
53384
53493
  const b2 = body;
53385
53494
  if (!Array.isArray(b2.output)) return body;
53495
+ const probe = JSON.stringify(b2.output);
53496
+ if (containsRenderTagText(probe)) {
53497
+ ctx.log(`[warn: tag echo] non-stream responses output contains <acp tag: ${probe.slice(0, 120).replace(/\n/g, " ")}`);
53498
+ stripResponsesText(b2);
53499
+ }
53386
53500
  let converted = false;
53387
53501
  let sawReal = false;
53388
53502
  const noteParts = [];
@@ -53944,6 +54058,14 @@ function buildStatusPanel(input) {
53944
54058
  import { fileURLToPath as fileURLToPath2 } from "url";
53945
54059
  import fs4 from "fs";
53946
54060
  import path8 from "path";
54061
+
54062
+ // src/sse-util.ts
54063
+ function normalizeSseLineEndings(buf) {
54064
+ if (buf.indexOf("\r") === -1) return buf;
54065
+ return buf.replace(/\r\n|\r/g, "\n");
54066
+ }
54067
+
54068
+ // src/plugin.ts
53947
54069
  var PROXY_VERSION = (() => {
53948
54070
  try {
53949
54071
  const here = fileURLToPath2(import.meta.url);
@@ -54603,7 +54725,7 @@ async function pipePluginJson(stream2, res, session, protocol) {
54603
54725
  try {
54604
54726
  const json = JSON.parse(text);
54605
54727
  const usage = json["usage"];
54606
- if (usage) {
54728
+ if (session && usage) {
54607
54729
  const input = num2(usage["prompt_tokens"]) ?? num2(usage["input_tokens"]);
54608
54730
  if (input !== void 0) {
54609
54731
  applyUsageSample(session, {
@@ -57618,7 +57740,12 @@ ${hdrText}
57618
57740
  if (prepared && prepared.resetAfterSuccess) {
57619
57741
  const [toClient, toObserve] = responseBody.tee();
57620
57742
  const observed = observeResponsesTerminalState(toObserve, prepared.stream);
57621
- await pipeThrough(toClient, res);
57743
+ const tagLog = (msg) => log2("info", `[${prepared.session.id}] ${msg}`);
57744
+ if ((upstream.headers.get("content-type") ?? "").includes("text/event-stream")) {
57745
+ await pipePluginResponsesWithStrip(toClient, res, void 0, tagLog);
57746
+ } else {
57747
+ await pipeThrough(toClient, res);
57748
+ }
57622
57749
  clearUpstreamTimer();
57623
57750
  const terminal = await observed;
57624
57751
  if (terminal === "completed") {
@@ -57636,6 +57763,9 @@ ${hdrText}
57636
57763
  await pipePluginChatWithStrip(responseBody, res, p2.protocol, void 0, tagLog);
57637
57764
  }
57638
57765
  clearUpstreamTimer();
57766
+ } else if (prepared && (upstream.headers.get("content-type") ?? "").includes("application/json")) {
57767
+ await pipePluginJson(responseBody, res, void 0, prepared.protocol);
57768
+ clearUpstreamTimer();
57639
57769
  } else {
57640
57770
  await pipeThrough(responseBody, res);
57641
57771
  clearUpstreamTimer();
@@ -62203,19 +62333,20 @@ function livePidHoldsOverlay(overlay) {
62203
62333
  function linkOverlayEntry(realHome, overlay, entry) {
62204
62334
  const target = path14.join(realHome, entry);
62205
62335
  const link = path14.join(overlay, entry);
62206
- try {
62207
- fs11.symlinkSync(target, link);
62208
- return true;
62209
- } catch (err2) {
62210
- const code = err2.code;
62211
- if (code !== "EPERM" && code !== "EACCES" && code !== "EINVAL") return false;
62212
- }
62213
62336
  let st2;
62214
62337
  try {
62215
62338
  st2 = fs11.lstatSync(target);
62216
62339
  } catch {
62217
62340
  return false;
62218
62341
  }
62342
+ const kind = st2.isDirectory() ? process.platform === "win32" ? "junction" : "dir" : "file";
62343
+ try {
62344
+ fs11.symlinkSync(target, link, kind);
62345
+ return true;
62346
+ } catch (err2) {
62347
+ const code = err2.code;
62348
+ if (code !== "EPERM" && code !== "EACCES" && code !== "EINVAL") return false;
62349
+ }
62219
62350
  if (st2.isDirectory()) {
62220
62351
  try {
62221
62352
  fs11.symlinkSync(target, link, "junction");
@@ -62246,17 +62377,39 @@ function isWriteThroughHardlink(overlayPath, realPath, st2) {
62246
62377
  }
62247
62378
  }
62248
62379
  function sqliteSetMembers(base) {
62249
- return [base, `${base}-wal`, `${base}-shm`];
62380
+ return [base, `${base}-wal`, `${base}-shm`, `${base}-journal`];
62381
+ }
62382
+ function freeConflictName(dst) {
62383
+ let candidate = `${dst}.bili-conflict`;
62384
+ let n = 1;
62385
+ while (n < 1e5) {
62386
+ try {
62387
+ fs11.lstatSync(candidate);
62388
+ candidate = `${dst}.bili-conflict.${n}`;
62389
+ n += 1;
62390
+ } catch {
62391
+ return candidate;
62392
+ }
62393
+ }
62394
+ return candidate;
62250
62395
  }
62251
62396
  function mergeSqliteSet(overlay, realHome, base) {
62252
- const members = sqliteSetMembers(base).filter((m2) => {
62397
+ const members = sqliteSetMembers(base);
62398
+ const statFile = (dir, m2) => {
62253
62399
  try {
62254
- return fs11.lstatSync(path14.join(overlay, m2)).isFile();
62400
+ const st2 = fs11.lstatSync(path14.join(dir, m2));
62401
+ return st2.isFile() ? st2 : void 0;
62255
62402
  } catch {
62256
- return false;
62403
+ return void 0;
62257
62404
  }
62258
- });
62259
- if (members.length === 0) return true;
62405
+ };
62406
+ const oMain = statFile(overlay, base);
62407
+ const rMain = statFile(realHome, base);
62408
+ let winner;
62409
+ if (oMain && rMain) winner = rMain.mtimeMs >= oMain.mtimeMs ? "real" : "overlay";
62410
+ else if (oMain) winner = "overlay";
62411
+ else if (rMain) winner = "real";
62412
+ else winner = "orphan";
62260
62413
  const undo = [];
62261
62414
  const rollback = () => {
62262
62415
  for (const step of undo.reverse()) {
@@ -62267,46 +62420,39 @@ function mergeSqliteSet(overlay, realHome, base) {
62267
62420
  }
62268
62421
  undo.length = 0;
62269
62422
  };
62423
+ const movePreserving = (src, dst) => {
62424
+ let dstStat;
62425
+ try {
62426
+ dstStat = fs11.lstatSync(dst);
62427
+ } catch {
62428
+ }
62429
+ if (dstStat) {
62430
+ if (dstStat.isDirectory()) throw new Error("target is a directory");
62431
+ const conflict = freeConflictName(dst);
62432
+ fs11.renameSync(dst, conflict);
62433
+ undo.push(() => fs11.renameSync(conflict, dst));
62434
+ }
62435
+ fs11.renameSync(src, dst);
62436
+ undo.push(() => fs11.renameSync(dst, src));
62437
+ };
62438
+ const preserveAsConflict = (src, name) => {
62439
+ const conflict = freeConflictName(path14.join(realHome, name));
62440
+ fs11.renameSync(src, conflict);
62441
+ undo.push(() => fs11.renameSync(conflict, src));
62442
+ };
62270
62443
  try {
62271
62444
  for (const m2 of members) {
62272
- const src = path14.join(overlay, m2);
62273
- const dst = path14.join(realHome, m2);
62274
- let dstStat;
62275
- try {
62276
- dstStat = fs11.lstatSync(dst);
62277
- } catch {
62278
- }
62279
- if (dstStat && dstStat.isDirectory()) {
62280
- rollback();
62281
- return false;
62282
- }
62283
- const srcStat = fs11.lstatSync(src);
62284
- if (dstStat && dstStat.mtimeMs >= srcStat.mtimeMs) {
62285
- try {
62286
- fs11.renameSync(src, `${dst}.bili-conflict`);
62287
- } catch {
62288
- rollback();
62289
- return false;
62290
- }
62291
- undo.push(() => fs11.renameSync(`${dst}.bili-conflict`, src));
62292
- continue;
62293
- }
62294
- if (dstStat) {
62295
- try {
62296
- fs11.renameSync(dst, `${dst}.bili-conflict`);
62297
- } catch {
62298
- rollback();
62299
- return false;
62300
- }
62301
- undo.push(() => fs11.renameSync(`${dst}.bili-conflict`, dst));
62302
- }
62303
- try {
62304
- fs11.renameSync(src, dst);
62305
- } catch {
62306
- rollback();
62307
- return false;
62445
+ const o = statFile(overlay, m2);
62446
+ const r = statFile(realHome, m2);
62447
+ if (winner === "overlay") {
62448
+ if (o) movePreserving(path14.join(overlay, m2), path14.join(realHome, m2));
62449
+ else if (r) preserveAsConflict(path14.join(realHome, m2), m2);
62450
+ } else if (winner === "real") {
62451
+ if (o) preserveAsConflict(path14.join(overlay, m2), m2);
62452
+ } else {
62453
+ if (o) preserveAsConflict(path14.join(overlay, m2), m2);
62454
+ else if (r) preserveAsConflict(path14.join(realHome, m2), m2);
62308
62455
  }
62309
- undo.push(() => fs11.renameSync(dst, src));
62310
62456
  }
62311
62457
  return true;
62312
62458
  } catch {
@@ -62470,7 +62616,7 @@ function mergeOverlayEntry(src, dst, excludedName) {
62470
62616
  if (st2.isDirectory()) {
62471
62617
  if (dstStat && !dstStat.isDirectory()) {
62472
62618
  try {
62473
- fs11.renameSync(dst, `${dst}.bili-conflict`);
62619
+ fs11.renameSync(dst, freeConflictName(dst));
62474
62620
  dstStat = void 0;
62475
62621
  } catch {
62476
62622
  return false;
@@ -62496,7 +62642,7 @@ function mergeOverlayEntry(src, dst, excludedName) {
62496
62642
  }
62497
62643
  if (dstStat && dstStat.isDirectory()) {
62498
62644
  try {
62499
- fs11.renameSync(src, `${dst}.bili-conflict`);
62645
+ fs11.renameSync(src, freeConflictName(dst));
62500
62646
  return true;
62501
62647
  } catch {
62502
62648
  return false;
@@ -62519,7 +62665,7 @@ function mergeOverlayEntry(src, dst, excludedName) {
62519
62665
  }
62520
62666
  if (dstStat && dstStat.mtimeMs >= st2.mtimeMs) {
62521
62667
  try {
62522
- fs11.renameSync(src, `${dst}.bili-conflict`);
62668
+ fs11.renameSync(src, freeConflictName(dst));
62523
62669
  return true;
62524
62670
  } catch {
62525
62671
  return false;
@@ -62527,7 +62673,7 @@ function mergeOverlayEntry(src, dst, excludedName) {
62527
62673
  }
62528
62674
  if (dstStat) {
62529
62675
  try {
62530
- fs11.renameSync(dst, `${dst}.bili-conflict`);
62676
+ fs11.renameSync(dst, freeConflictName(dst));
62531
62677
  } catch {
62532
62678
  return false;
62533
62679
  }