@remixhq/claude-plugin 0.1.22 → 0.1.24

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.
@@ -36,8 +36,8 @@ var require_windows = __commonJS({
36
36
  "use strict";
37
37
  module2.exports = isexe;
38
38
  isexe.sync = sync;
39
- var fs17 = require("fs");
40
- function checkPathExt(path17, options) {
39
+ var fs16 = require("fs");
40
+ function checkPathExt(path16, options) {
41
41
  var pathext = options.pathExt !== void 0 ? options.pathExt : process.env.PATHEXT;
42
42
  if (!pathext) {
43
43
  return true;
@@ -48,25 +48,25 @@ var require_windows = __commonJS({
48
48
  }
49
49
  for (var i2 = 0; i2 < pathext.length; i2++) {
50
50
  var p = pathext[i2].toLowerCase();
51
- if (p && path17.substr(-p.length).toLowerCase() === p) {
51
+ if (p && path16.substr(-p.length).toLowerCase() === p) {
52
52
  return true;
53
53
  }
54
54
  }
55
55
  return false;
56
56
  }
57
- function checkStat(stat, path17, options) {
57
+ function checkStat(stat, path16, options) {
58
58
  if (!stat.isSymbolicLink() && !stat.isFile()) {
59
59
  return false;
60
60
  }
61
- return checkPathExt(path17, options);
61
+ return checkPathExt(path16, options);
62
62
  }
63
- function isexe(path17, options, cb) {
64
- fs17.stat(path17, function(er, stat) {
65
- cb(er, er ? false : checkStat(stat, path17, options));
63
+ function isexe(path16, options, cb) {
64
+ fs16.stat(path16, function(er, stat) {
65
+ cb(er, er ? false : checkStat(stat, path16, options));
66
66
  });
67
67
  }
68
- function sync(path17, options) {
69
- return checkStat(fs17.statSync(path17), path17, options);
68
+ function sync(path16, options) {
69
+ return checkStat(fs16.statSync(path16), path16, options);
70
70
  }
71
71
  }
72
72
  });
@@ -77,14 +77,14 @@ var require_mode = __commonJS({
77
77
  "use strict";
78
78
  module2.exports = isexe;
79
79
  isexe.sync = sync;
80
- var fs17 = require("fs");
81
- function isexe(path17, options, cb) {
82
- fs17.stat(path17, function(er, stat) {
80
+ var fs16 = require("fs");
81
+ function isexe(path16, options, cb) {
82
+ fs16.stat(path16, function(er, stat) {
83
83
  cb(er, er ? false : checkStat(stat, options));
84
84
  });
85
85
  }
86
- function sync(path17, options) {
87
- return checkStat(fs17.statSync(path17), options);
86
+ function sync(path16, options) {
87
+ return checkStat(fs16.statSync(path16), options);
88
88
  }
89
89
  function checkStat(stat, options) {
90
90
  return stat.isFile() && checkMode(stat, options);
@@ -109,7 +109,7 @@ var require_mode = __commonJS({
109
109
  var require_isexe = __commonJS({
110
110
  "node_modules/isexe/index.js"(exports2, module2) {
111
111
  "use strict";
112
- var fs17 = require("fs");
112
+ var fs16 = require("fs");
113
113
  var core;
114
114
  if (process.platform === "win32" || global.TESTING_WINDOWS) {
115
115
  core = require_windows();
@@ -118,7 +118,7 @@ var require_isexe = __commonJS({
118
118
  }
119
119
  module2.exports = isexe;
120
120
  isexe.sync = sync;
121
- function isexe(path17, options, cb) {
121
+ function isexe(path16, options, cb) {
122
122
  if (typeof options === "function") {
123
123
  cb = options;
124
124
  options = {};
@@ -128,7 +128,7 @@ var require_isexe = __commonJS({
128
128
  throw new TypeError("callback not provided");
129
129
  }
130
130
  return new Promise(function(resolve, reject) {
131
- isexe(path17, options || {}, function(er, is) {
131
+ isexe(path16, options || {}, function(er, is) {
132
132
  if (er) {
133
133
  reject(er);
134
134
  } else {
@@ -137,7 +137,7 @@ var require_isexe = __commonJS({
137
137
  });
138
138
  });
139
139
  }
140
- core(path17, options || {}, function(er, is) {
140
+ core(path16, options || {}, function(er, is) {
141
141
  if (er) {
142
142
  if (er.code === "EACCES" || options && options.ignoreErrors) {
143
143
  er = null;
@@ -147,9 +147,9 @@ var require_isexe = __commonJS({
147
147
  cb(er, is);
148
148
  });
149
149
  }
150
- function sync(path17, options) {
150
+ function sync(path16, options) {
151
151
  try {
152
- return core.sync(path17, options || {});
152
+ return core.sync(path16, options || {});
153
153
  } catch (er) {
154
154
  if (options && options.ignoreErrors || er.code === "EACCES") {
155
155
  return false;
@@ -166,7 +166,7 @@ var require_which = __commonJS({
166
166
  "node_modules/which/which.js"(exports2, module2) {
167
167
  "use strict";
168
168
  var isWindows = process.platform === "win32" || process.env.OSTYPE === "cygwin" || process.env.OSTYPE === "msys";
169
- var path17 = require("path");
169
+ var path16 = require("path");
170
170
  var COLON = isWindows ? ";" : ":";
171
171
  var isexe = require_isexe();
172
172
  var getNotFoundError = (cmd) => Object.assign(new Error(`not found: ${cmd}`), { code: "ENOENT" });
@@ -204,7 +204,7 @@ var require_which = __commonJS({
204
204
  return opt.all && found.length ? resolve(found) : reject(getNotFoundError(cmd));
205
205
  const ppRaw = pathEnv[i2];
206
206
  const pathPart = /^".*"$/.test(ppRaw) ? ppRaw.slice(1, -1) : ppRaw;
207
- const pCmd = path17.join(pathPart, cmd);
207
+ const pCmd = path16.join(pathPart, cmd);
208
208
  const p = !pathPart && /^\.[\\\/]/.test(cmd) ? cmd.slice(0, 2) + pCmd : pCmd;
209
209
  resolve(subStep(p, i2, 0));
210
210
  });
@@ -231,7 +231,7 @@ var require_which = __commonJS({
231
231
  for (let i2 = 0; i2 < pathEnv.length; i2++) {
232
232
  const ppRaw = pathEnv[i2];
233
233
  const pathPart = /^".*"$/.test(ppRaw) ? ppRaw.slice(1, -1) : ppRaw;
234
- const pCmd = path17.join(pathPart, cmd);
234
+ const pCmd = path16.join(pathPart, cmd);
235
235
  const p = !pathPart && /^\.[\\\/]/.test(cmd) ? cmd.slice(0, 2) + pCmd : pCmd;
236
236
  for (let j = 0; j < pathExt.length; j++) {
237
237
  const cur = p + pathExt[j];
@@ -279,7 +279,7 @@ var require_path_key = __commonJS({
279
279
  var require_resolveCommand = __commonJS({
280
280
  "node_modules/cross-spawn/lib/util/resolveCommand.js"(exports2, module2) {
281
281
  "use strict";
282
- var path17 = require("path");
282
+ var path16 = require("path");
283
283
  var which = require_which();
284
284
  var getPathKey = require_path_key();
285
285
  function resolveCommandAttempt(parsed, withoutPathExt) {
@@ -297,7 +297,7 @@ var require_resolveCommand = __commonJS({
297
297
  try {
298
298
  resolved = which.sync(parsed.command, {
299
299
  path: env[getPathKey({ env })],
300
- pathExt: withoutPathExt ? path17.delimiter : void 0
300
+ pathExt: withoutPathExt ? path16.delimiter : void 0
301
301
  });
302
302
  } catch (e) {
303
303
  } finally {
@@ -306,7 +306,7 @@ var require_resolveCommand = __commonJS({
306
306
  }
307
307
  }
308
308
  if (resolved) {
309
- resolved = path17.resolve(hasCustomCwd ? parsed.options.cwd : "", resolved);
309
+ resolved = path16.resolve(hasCustomCwd ? parsed.options.cwd : "", resolved);
310
310
  }
311
311
  return resolved;
312
312
  }
@@ -360,8 +360,8 @@ var require_shebang_command = __commonJS({
360
360
  if (!match) {
361
361
  return null;
362
362
  }
363
- const [path17, argument] = match[0].replace(/#! ?/, "").split(" ");
364
- const binary = path17.split("/").pop();
363
+ const [path16, argument] = match[0].replace(/#! ?/, "").split(" ");
364
+ const binary = path16.split("/").pop();
365
365
  if (binary === "env") {
366
366
  return argument;
367
367
  }
@@ -374,16 +374,16 @@ var require_shebang_command = __commonJS({
374
374
  var require_readShebang = __commonJS({
375
375
  "node_modules/cross-spawn/lib/util/readShebang.js"(exports2, module2) {
376
376
  "use strict";
377
- var fs17 = require("fs");
377
+ var fs16 = require("fs");
378
378
  var shebangCommand = require_shebang_command();
379
379
  function readShebang(command) {
380
380
  const size = 150;
381
381
  const buffer = Buffer.alloc(size);
382
382
  let fd;
383
383
  try {
384
- fd = fs17.openSync(command, "r");
385
- fs17.readSync(fd, buffer, 0, size, 0);
386
- fs17.closeSync(fd);
384
+ fd = fs16.openSync(command, "r");
385
+ fs16.readSync(fd, buffer, 0, size, 0);
386
+ fs16.closeSync(fd);
387
387
  } catch (e) {
388
388
  }
389
389
  return shebangCommand(buffer.toString());
@@ -396,7 +396,7 @@ var require_readShebang = __commonJS({
396
396
  var require_parse = __commonJS({
397
397
  "node_modules/cross-spawn/lib/parse.js"(exports2, module2) {
398
398
  "use strict";
399
- var path17 = require("path");
399
+ var path16 = require("path");
400
400
  var resolveCommand = require_resolveCommand();
401
401
  var escape2 = require_escape();
402
402
  var readShebang = require_readShebang();
@@ -421,7 +421,7 @@ var require_parse = __commonJS({
421
421
  const needsShell = !isExecutableRegExp.test(commandFile);
422
422
  if (parsed.options.forceShell || needsShell) {
423
423
  const needsDoubleEscapeMetaChars = isCmdShimRegExp.test(commandFile);
424
- parsed.command = path17.normalize(parsed.command);
424
+ parsed.command = path16.normalize(parsed.command);
425
425
  parsed.command = escape2.command(parsed.command);
426
426
  parsed.args = parsed.args.map((arg) => escape2.argument(arg, needsDoubleEscapeMetaChars));
427
427
  const shellCommand = [parsed.command].concat(parsed.args).join(" ");
@@ -3724,8 +3724,8 @@ var require_utils = __commonJS({
3724
3724
  }
3725
3725
  return ind;
3726
3726
  }
3727
- function removeDotSegments(path17) {
3728
- let input = path17;
3727
+ function removeDotSegments(path16) {
3728
+ let input = path16;
3729
3729
  const output = [];
3730
3730
  let nextSlash = -1;
3731
3731
  let len = 0;
@@ -3924,8 +3924,8 @@ var require_schemes = __commonJS({
3924
3924
  wsComponent.secure = void 0;
3925
3925
  }
3926
3926
  if (wsComponent.resourceName) {
3927
- const [path17, query] = wsComponent.resourceName.split("?");
3928
- wsComponent.path = path17 && path17 !== "/" ? path17 : void 0;
3927
+ const [path16, query] = wsComponent.resourceName.split("?");
3928
+ wsComponent.path = path16 && path16 !== "/" ? path16 : void 0;
3929
3929
  wsComponent.query = query;
3930
3930
  wsComponent.resourceName = void 0;
3931
3931
  }
@@ -7287,12 +7287,12 @@ var require_dist = __commonJS({
7287
7287
  throw new Error(`Unknown format "${name}"`);
7288
7288
  return f;
7289
7289
  };
7290
- function addFormats(ajv, list, fs17, exportName) {
7290
+ function addFormats(ajv, list, fs16, exportName) {
7291
7291
  var _a;
7292
7292
  var _b;
7293
7293
  (_a = (_b = ajv.opts.code).formats) !== null && _a !== void 0 ? _a : _b.formats = (0, codegen_1._)`require("ajv-formats/dist/formats").${exportName}`;
7294
7294
  for (const f of list)
7295
- ajv.addFormat(f, fs17[f]);
7295
+ ajv.addFormat(f, fs16[f]);
7296
7296
  }
7297
7297
  module2.exports = exports2 = formatsPlugin;
7298
7298
  Object.defineProperty(exports2, "__esModule", { value: true });
@@ -7498,10 +7498,10 @@ function assignProp(target, prop, value) {
7498
7498
  configurable: true
7499
7499
  });
7500
7500
  }
7501
- function getElementAtPath(obj, path17) {
7502
- if (!path17)
7501
+ function getElementAtPath(obj, path16) {
7502
+ if (!path16)
7503
7503
  return obj;
7504
- return path17.reduce((acc, key) => acc?.[key], obj);
7504
+ return path16.reduce((acc, key) => acc?.[key], obj);
7505
7505
  }
7506
7506
  function promiseAllObject(promisesObj) {
7507
7507
  const keys = Object.keys(promisesObj);
@@ -7821,11 +7821,11 @@ function aborted(x, startIndex = 0) {
7821
7821
  }
7822
7822
  return false;
7823
7823
  }
7824
- function prefixIssues(path17, issues) {
7824
+ function prefixIssues(path16, issues) {
7825
7825
  return issues.map((iss) => {
7826
7826
  var _a;
7827
7827
  (_a = iss).path ?? (_a.path = []);
7828
- iss.path.unshift(path17);
7828
+ iss.path.unshift(path16);
7829
7829
  return iss;
7830
7830
  });
7831
7831
  }
@@ -13430,21 +13430,27 @@ var REMIX_ERROR_CODES = {
13430
13430
  PREFERRED_BRANCH_MISMATCH: "PREFERRED_BRANCH_MISMATCH"
13431
13431
  };
13432
13432
 
13433
- // node_modules/@remixhq/core/dist/chunk-YZ34ICNN.js
13433
+ // node_modules/@remixhq/core/dist/chunk-7XJGOKEO.js
13434
13434
  var RemixError = class extends Error {
13435
13435
  code;
13436
13436
  exitCode;
13437
13437
  hint;
13438
+ // HTTP status code when this error originates from an API response.
13439
+ // null for non-HTTP errors (validation, local IO, programming bugs).
13440
+ // Callers use this to distinguish transient (5xx) from permanent (4xx)
13441
+ // API failures without resorting to error-message string matching.
13442
+ statusCode;
13438
13443
  constructor(message, opts) {
13439
13444
  super(message);
13440
13445
  this.name = "RemixError";
13441
13446
  this.code = opts?.code ?? null;
13442
13447
  this.exitCode = opts?.exitCode ?? 1;
13443
13448
  this.hint = opts?.hint ?? null;
13449
+ this.statusCode = opts?.statusCode ?? null;
13444
13450
  }
13445
13451
  };
13446
13452
 
13447
- // node_modules/@remixhq/core/dist/chunk-WT6VRLXU.js
13453
+ // node_modules/@remixhq/core/dist/chunk-S4ECO35X.js
13448
13454
  var import_promises12 = __toESM(require("fs/promises"), 1);
13449
13455
  var import_crypto = require("crypto");
13450
13456
  var import_os = __toESM(require("os"), 1);
@@ -17831,13 +17837,13 @@ var logOutputSync = ({ serializedResult, fdNumber, state, verboseInfo, encoding,
17831
17837
  }
17832
17838
  };
17833
17839
  var writeToFiles = (serializedResult, stdioItems, outputFiles) => {
17834
- for (const { path: path17, append } of stdioItems.filter(({ type }) => FILE_TYPES.has(type))) {
17835
- const pathString = typeof path17 === "string" ? path17 : path17.toString();
17840
+ for (const { path: path16, append } of stdioItems.filter(({ type }) => FILE_TYPES.has(type))) {
17841
+ const pathString = typeof path16 === "string" ? path16 : path16.toString();
17836
17842
  if (append || outputFiles.has(pathString)) {
17837
- (0, import_node_fs4.appendFileSync)(path17, serializedResult);
17843
+ (0, import_node_fs4.appendFileSync)(path16, serializedResult);
17838
17844
  } else {
17839
17845
  outputFiles.add(pathString);
17840
- (0, import_node_fs4.writeFileSync)(path17, serializedResult);
17846
+ (0, import_node_fs4.writeFileSync)(path16, serializedResult);
17841
17847
  }
17842
17848
  }
17843
17849
  };
@@ -20225,7 +20231,7 @@ var {
20225
20231
  getCancelSignal: getCancelSignal2
20226
20232
  } = getIpcExport();
20227
20233
 
20228
- // node_modules/@remixhq/core/dist/chunk-WT6VRLXU.js
20234
+ // node_modules/@remixhq/core/dist/chunk-S4ECO35X.js
20229
20235
  var GIT_REMOTE_PROTOCOL_RE = /^(https?|ssh):\/\//i;
20230
20236
  var SCP_LIKE_GIT_REMOTE_RE = /^(?<user>[^@\s]+)@(?<host>[^:\s]+):(?<path>[^\\\s]+)$/;
20231
20237
  var CANONICAL_GIT_REMOTE_RE = /^(?<host>(?:localhost|[a-z0-9.-]+))\/(?<path>[^\\\s]+)$/i;
@@ -20691,7 +20697,7 @@ function summarizeUnifiedDiff(diff) {
20691
20697
  return { changedFilesCount, insertions, deletions };
20692
20698
  }
20693
20699
 
20694
- // node_modules/@remixhq/core/dist/chunk-YCFLOHJV.js
20700
+ // node_modules/@remixhq/core/dist/chunk-DBVN42RF.js
20695
20701
  var import_promises13 = __toESM(require("fs/promises"), 1);
20696
20702
  var import_path2 = __toESM(require("path"), 1);
20697
20703
  var import_promises14 = __toESM(require("fs/promises"), 1);
@@ -21044,14 +21050,11 @@ var import_crypto8 = __toESM(require("crypto"), 1);
21044
21050
  var import_fs = __toESM(require("fs"), 1);
21045
21051
  var import_fs2 = __toESM(require("fs"), 1);
21046
21052
  var import_stream3 = require("stream");
21047
- var import_crypto9 = require("crypto");
21048
21053
  var import_promises23 = __toESM(require("fs/promises"), 1);
21054
+ var import_os6 = __toESM(require("os"), 1);
21049
21055
  var import_path12 = __toESM(require("path"), 1);
21050
21056
  var import_promises24 = __toESM(require("fs/promises"), 1);
21051
- var import_os6 = __toESM(require("os"), 1);
21052
21057
  var import_path13 = __toESM(require("path"), 1);
21053
- var import_promises25 = __toESM(require("fs/promises"), 1);
21054
- var import_path14 = __toESM(require("path"), 1);
21055
21058
  var APP_DELTA_CACHE_TTL_MS = 5e3;
21056
21059
  var appDeltaCache = /* @__PURE__ */ new Map();
21057
21060
  var cacheClock = () => Date.now();
@@ -21060,6 +21063,8 @@ function buildAppDeltaCacheKey(appId, payload) {
21060
21063
  appId,
21061
21064
  payload.baseHeadHash,
21062
21065
  payload.targetHeadHash ?? "",
21066
+ payload.baseRevisionId ?? "",
21067
+ payload.targetRevisionId ?? "",
21063
21068
  payload.localSnapshotHash ?? "",
21064
21069
  payload.repoFingerprint ?? "",
21065
21070
  payload.remoteUrl ?? "",
@@ -21188,9 +21193,6 @@ function getAsyncJobDir(jobId) {
21188
21193
  function getAsyncJobFilePath(jobId) {
21189
21194
  return import_path5.default.join(getAsyncJobDir(jobId), "job.json");
21190
21195
  }
21191
- function getAsyncJobBundlePath(jobId) {
21192
- return import_path5.default.join(getAsyncJobDir(jobId), "bundle.bundle");
21193
- }
21194
21196
  function getLogsRoot() {
21195
21197
  return import_path5.default.join(getCollabStateRoot(), "logs");
21196
21198
  }
@@ -21459,11 +21461,11 @@ async function readLocalBaseline(params) {
21459
21461
  const raw = await import_promises16.default.readFile(getBaselinePath(params), "utf8");
21460
21462
  const parsed = JSON.parse(raw);
21461
21463
  if (!parsed || typeof parsed !== "object") return null;
21462
- if (parsed.schemaVersion !== 1 || typeof parsed.key !== "string" || typeof parsed.repoRoot !== "string") {
21464
+ if (![1, 2].includes(Number(parsed.schemaVersion)) || typeof parsed.key !== "string" || typeof parsed.repoRoot !== "string") {
21463
21465
  return null;
21464
21466
  }
21465
21467
  return {
21466
- schemaVersion: 1,
21468
+ schemaVersion: Number(parsed.schemaVersion) === 2 ? 2 : 1,
21467
21469
  key: parsed.key,
21468
21470
  repoRoot: parsed.repoRoot,
21469
21471
  repoFingerprint: parsed.repoFingerprint ?? null,
@@ -21472,6 +21474,8 @@ async function readLocalBaseline(params) {
21472
21474
  branchName: parsed.branchName ?? null,
21473
21475
  lastSnapshotId: parsed.lastSnapshotId ?? null,
21474
21476
  lastSnapshotHash: parsed.lastSnapshotHash ?? null,
21477
+ lastServerRevisionId: parsed.lastServerRevisionId ?? null,
21478
+ lastServerTreeHash: parsed.lastServerTreeHash ?? null,
21475
21479
  lastServerHeadHash: parsed.lastServerHeadHash ?? null,
21476
21480
  lastSeenLocalCommitHash: parsed.lastSeenLocalCommitHash ?? null,
21477
21481
  updatedAt: String(parsed.updatedAt ?? "")
@@ -21483,7 +21487,7 @@ async function readLocalBaseline(params) {
21483
21487
  async function writeLocalBaseline(baseline) {
21484
21488
  const key = buildLaneStateKey(baseline);
21485
21489
  const normalized = {
21486
- schemaVersion: 1,
21490
+ schemaVersion: 2,
21487
21491
  key,
21488
21492
  repoRoot: baseline.repoRoot,
21489
21493
  repoFingerprint: baseline.repoFingerprint ?? null,
@@ -21492,6 +21496,8 @@ async function writeLocalBaseline(baseline) {
21492
21496
  branchName: baseline.branchName ?? null,
21493
21497
  lastSnapshotId: baseline.lastSnapshotId ?? null,
21494
21498
  lastSnapshotHash: baseline.lastSnapshotHash ?? null,
21499
+ lastServerRevisionId: baseline.lastServerRevisionId ?? null,
21500
+ lastServerTreeHash: baseline.lastServerTreeHash ?? null,
21495
21501
  lastServerHeadHash: baseline.lastServerHeadHash ?? null,
21496
21502
  lastSeenLocalCommitHash: baseline.lastSeenLocalCommitHash ?? null,
21497
21503
  updatedAt: baseline.updatedAt ?? (/* @__PURE__ */ new Date()).toISOString()
@@ -21833,6 +21839,7 @@ function normalizeJob2(input) {
21833
21839
  prompt: input.prompt,
21834
21840
  assistantResponse: input.assistantResponse,
21835
21841
  baselineSnapshotId: input.baselineSnapshotId ?? null,
21842
+ baselineServerRevisionId: input.baselineServerRevisionId ?? null,
21836
21843
  baselineServerHeadHash: input.baselineServerHeadHash ?? null,
21837
21844
  currentSnapshotId: input.currentSnapshotId,
21838
21845
  capturedAt: input.capturedAt ?? now,
@@ -21862,6 +21869,7 @@ async function readPendingFinalizeJob(jobId) {
21862
21869
  prompt: String(parsed.prompt ?? ""),
21863
21870
  assistantResponse: String(parsed.assistantResponse ?? ""),
21864
21871
  baselineSnapshotId: parsed.baselineSnapshotId ?? null,
21872
+ baselineServerRevisionId: parsed.baselineServerRevisionId ?? null,
21865
21873
  baselineServerHeadHash: parsed.baselineServerHeadHash ?? null,
21866
21874
  currentSnapshotId: String(parsed.currentSnapshotId ?? ""),
21867
21875
  capturedAt: parsed.capturedAt,
@@ -22351,6 +22359,15 @@ function shouldRequireRemoteLaneForCurrentBranch(params) {
22351
22359
  if (params.currentBranch === defaultBranch) return false;
22352
22360
  return !params.binding.laneId || params.binding.currentAppId === params.binding.upstreamAppId;
22353
22361
  }
22362
+ function resolveLaneLookupProjectId(params) {
22363
+ const currentBranch = normalizeBranchName2(params.currentBranch);
22364
+ const defaultBranch = normalizeBranchName2(params.defaultBranch);
22365
+ const localProjectId = params.localBinding.projectId ?? null;
22366
+ if (currentBranch && currentBranch !== defaultBranch && localProjectId) {
22367
+ return localProjectId;
22368
+ }
22369
+ return params.explicitRootProjectId ?? (params.requireRemoteLane ? void 0 : localProjectId ?? params.fallbackProjectId ?? void 0);
22370
+ }
22354
22371
  async function persistResolvedLane(repoRoot, binding) {
22355
22372
  await writeCollabBinding(repoRoot, {
22356
22373
  projectId: binding.projectId,
@@ -22429,7 +22446,14 @@ async function resolveActiveLaneBindingUncached(params, state) {
22429
22446
  };
22430
22447
  }
22431
22448
  const laneResp2 = await params.api.resolveProjectLaneBinding({
22432
- projectId: state.explicitRootBinding?.projectId ?? (requireRemoteLane ? void 0 : localBinding.projectId ?? state.projectId ?? void 0),
22449
+ projectId: resolveLaneLookupProjectId({
22450
+ explicitRootProjectId: state.explicitRootBinding?.projectId,
22451
+ localBinding,
22452
+ currentBranch,
22453
+ defaultBranch: state.defaultBranch,
22454
+ requireRemoteLane,
22455
+ fallbackProjectId: state.projectId
22456
+ }),
22433
22457
  repoFingerprint: state.repoFingerprint ?? void 0,
22434
22458
  remoteUrl: state.remoteUrl ?? void 0,
22435
22459
  defaultBranch: state.defaultBranch ?? void 0,
@@ -22588,6 +22612,8 @@ function buildBaseState() {
22588
22612
  branchName: null,
22589
22613
  localCommitHash: null,
22590
22614
  currentSnapshotHash: null,
22615
+ currentServerRevisionId: null,
22616
+ currentServerTreeHash: null,
22591
22617
  currentServerHeadHash: null,
22592
22618
  currentServerHeadCommitId: null,
22593
22619
  worktreeClean: false,
@@ -22621,6 +22647,8 @@ function buildBaseState() {
22621
22647
  baseline: {
22622
22648
  lastSnapshotId: null,
22623
22649
  lastSnapshotHash: null,
22650
+ lastServerRevisionId: null,
22651
+ lastServerTreeHash: null,
22624
22652
  lastServerHeadHash: null,
22625
22653
  lastSeenLocalCommitHash: null
22626
22654
  }
@@ -22747,6 +22775,8 @@ async function collabDetectRepoState(params) {
22747
22775
  summarizeAsyncJobs({ repoRoot, branchName: binding.branchName ?? null })
22748
22776
  ]);
22749
22777
  const appHead = unwrapResponseObject(headResp, "app head");
22778
+ detected.currentServerRevisionId = appHead.headRevisionId ?? null;
22779
+ detected.currentServerTreeHash = appHead.treeHash ?? null;
22750
22780
  detected.currentServerHeadHash = appHead.headCommitHash;
22751
22781
  detected.currentServerHeadCommitId = appHead.headCommitId;
22752
22782
  detected.currentSnapshotHash = inspection.snapshotHash;
@@ -22755,6 +22785,8 @@ async function collabDetectRepoState(params) {
22755
22785
  detected.baseline = {
22756
22786
  lastSnapshotId: baseline?.lastSnapshotId ?? null,
22757
22787
  lastSnapshotHash: baseline?.lastSnapshotHash ?? null,
22788
+ lastServerRevisionId: baseline?.lastServerRevisionId ?? null,
22789
+ lastServerTreeHash: baseline?.lastServerTreeHash ?? null,
22758
22790
  lastServerHeadHash: baseline?.lastServerHeadHash ?? null,
22759
22791
  lastSeenLocalCommitHash: baseline?.lastSeenLocalCommitHash ?? null
22760
22792
  };
@@ -22764,6 +22796,7 @@ async function collabDetectRepoState(params) {
22764
22796
  const bootstrapResp = await params.api.getAppDelta(binding.currentAppId, {
22765
22797
  baseHeadHash: localCommitHash,
22766
22798
  targetHeadHash: appHead.headCommitHash,
22799
+ targetRevisionId: appHead.headRevisionId,
22767
22800
  repoFingerprint: binding.repoFingerprint ?? void 0,
22768
22801
  remoteUrl: binding.remoteUrl ?? void 0,
22769
22802
  defaultBranch: binding.defaultBranch ?? void 0
@@ -22786,7 +22819,7 @@ async function collabDetectRepoState(params) {
22786
22819
  }
22787
22820
  }
22788
22821
  detected.repoState = "external_local_base_changed";
22789
- detected.hint = "No local Remix baseline exists for this lane yet. Run `remix collab re-anchor` to anchor this checkout.";
22822
+ detected.hint = "No local Remix revision baseline exists for this lane yet. Run `remix collab init` or sync this lane to seed the baseline.";
22790
22823
  return detected;
22791
22824
  }
22792
22825
  const localHeadMovedSinceBaseline = Boolean(baseline.lastSeenLocalCommitHash) && localCommitHash !== baseline.lastSeenLocalCommitHash;
@@ -22805,7 +22838,30 @@ async function collabDetectRepoState(params) {
22805
22838
  return detected;
22806
22839
  }
22807
22840
  const localChanged = inspection.snapshotHash !== baseline.lastSnapshotHash;
22808
- const serverChanged = appHead.headCommitHash !== baseline.lastServerHeadHash;
22841
+ const serverHeadChanged = appHead.headCommitHash !== baseline.lastServerHeadHash;
22842
+ const revisionChanged = Boolean(
22843
+ baseline.lastServerRevisionId && (appHead.headRevisionId ?? null) !== baseline.lastServerRevisionId
22844
+ );
22845
+ const equivalentRevisionDrift = revisionChanged && !serverHeadChanged;
22846
+ if (equivalentRevisionDrift) {
22847
+ await writeLocalBaseline({
22848
+ repoRoot,
22849
+ repoFingerprint: binding.repoFingerprint,
22850
+ laneId: binding.laneId,
22851
+ currentAppId: binding.currentAppId,
22852
+ branchName: binding.branchName,
22853
+ lastSnapshotId: baseline.lastSnapshotId,
22854
+ lastSnapshotHash: baseline.lastSnapshotHash,
22855
+ lastServerRevisionId: appHead.headRevisionId ?? null,
22856
+ lastServerTreeHash: appHead.treeHash ?? baseline.lastServerTreeHash ?? null,
22857
+ lastServerHeadHash: appHead.headCommitHash,
22858
+ lastSeenLocalCommitHash: baseline.lastSeenLocalCommitHash
22859
+ });
22860
+ detected.baseline.lastServerRevisionId = appHead.headRevisionId ?? null;
22861
+ detected.baseline.lastServerTreeHash = appHead.treeHash ?? baseline.lastServerTreeHash ?? null;
22862
+ detected.baseline.lastServerHeadHash = appHead.headCommitHash;
22863
+ }
22864
+ const serverChanged = serverHeadChanged;
22809
22865
  if (!localChanged && !serverChanged) {
22810
22866
  detected.repoState = "idle";
22811
22867
  return detected;
@@ -23229,6 +23285,7 @@ function buildWorkspaceMetadata(params) {
23229
23285
  recordingMode: "boundary_delta",
23230
23286
  baselineSnapshotId: params.baselineSnapshotId,
23231
23287
  currentSnapshotId: params.currentSnapshotId,
23288
+ baselineServerRevisionId: params.baselineServerRevisionId ?? null,
23232
23289
  baselineServerHeadHash: params.baselineServerHeadHash,
23233
23290
  currentSnapshotHash: params.currentSnapshotHash,
23234
23291
  localCommitHash: params.localCommitHash,
@@ -23247,6 +23304,59 @@ function buildWorkspaceMetadata(params) {
23247
23304
  }
23248
23305
  return metadata;
23249
23306
  }
23307
+ async function findExistingChangeStepByIdempotency(params) {
23308
+ const idempotencyKey = params.idempotencyKey?.trim();
23309
+ if (!idempotencyKey) return null;
23310
+ const resp = await params.api.listChangeSteps(params.appId, { limit: 1, idempotencyKey });
23311
+ const responseObject = unwrapResponseObject(
23312
+ resp,
23313
+ "change step list"
23314
+ );
23315
+ const steps = Array.isArray(responseObject) ? responseObject : Array.isArray(responseObject.items) ? responseObject.items : [];
23316
+ return steps.find((step) => step.idempotencyKey === idempotencyKey) ?? null;
23317
+ }
23318
+ async function writeBaselineFromSucceededChangeStep(params) {
23319
+ const nextServerHeadHash = typeof params.changeStep.headCommitHash === "string" ? params.changeStep.headCommitHash.trim() : "";
23320
+ if (!nextServerHeadHash) {
23321
+ throw buildFinalizeCliError({
23322
+ message: "Backend returned a succeeded change step without a head commit hash.",
23323
+ exitCode: 1,
23324
+ hint: "This is a backend invariant violation; retry will not help. Run `remix collab status` before trying again.",
23325
+ disposition: "terminal",
23326
+ reason: "missing_head_commit_hash"
23327
+ });
23328
+ }
23329
+ let nextServerRevisionId = typeof params.changeStep.resultRevisionId === "string" ? params.changeStep.resultRevisionId.trim() : "";
23330
+ let nextServerTreeHash = null;
23331
+ if (!nextServerRevisionId) {
23332
+ const freshHeadResp = await params.api.getAppHead(params.job.currentAppId);
23333
+ const freshHead = unwrapResponseObject(freshHeadResp, "app head");
23334
+ if (freshHead.headCommitHash !== nextServerHeadHash || !freshHead.headRevisionId) {
23335
+ throw buildFinalizeCliError({
23336
+ message: "Backend returned a succeeded change step without a matching result revision.",
23337
+ exitCode: 1,
23338
+ hint: "The local baseline was not advanced because the post-step revision could not be verified. Restart the backend/CLI and retry after checking `remix collab status`.",
23339
+ disposition: "terminal",
23340
+ reason: "missing_result_revision_id"
23341
+ });
23342
+ }
23343
+ nextServerRevisionId = freshHead.headRevisionId;
23344
+ nextServerTreeHash = freshHead.treeHash ?? null;
23345
+ }
23346
+ await writeLocalBaseline({
23347
+ repoRoot: params.job.repoRoot,
23348
+ repoFingerprint: params.job.repoFingerprint,
23349
+ laneId: params.job.laneId,
23350
+ currentAppId: params.job.currentAppId,
23351
+ branchName: params.job.branchName,
23352
+ lastSnapshotId: params.snapshot.id,
23353
+ lastSnapshotHash: params.snapshot.snapshotHash,
23354
+ lastServerRevisionId: nextServerRevisionId,
23355
+ lastServerTreeHash: nextServerTreeHash,
23356
+ lastServerHeadHash: nextServerHeadHash,
23357
+ lastSeenLocalCommitHash: params.snapshot.localCommitHash
23358
+ });
23359
+ }
23250
23360
  async function harvestPreTurnEvents(repoRoot, fromCommit, toCommit) {
23251
23361
  if (!toCommit) return null;
23252
23362
  try {
@@ -23307,12 +23417,12 @@ async function processClaimedPendingFinalizeJobInner(params) {
23307
23417
  throw buildFinalizeCliError({
23308
23418
  message: "Local baseline is missing for this queued finalize job.",
23309
23419
  exitCode: 2,
23310
- hint: "Run `remix collab re-anchor` to anchor the repository again.",
23420
+ hint: "Run `remix collab init` to seed this checkout's revision baseline.",
23311
23421
  disposition: "terminal",
23312
23422
  reason: "baseline_missing"
23313
23423
  });
23314
23424
  }
23315
- const baselineDrifted = baseline.lastSnapshotId !== job.baselineSnapshotId || baseline.lastServerHeadHash !== job.baselineServerHeadHash;
23425
+ const baselineDrifted = baseline.lastSnapshotId !== job.baselineSnapshotId || (job.baselineServerRevisionId ? baseline.lastServerRevisionId !== job.baselineServerRevisionId : false) || baseline.lastServerHeadHash !== job.baselineServerHeadHash;
23316
23426
  const appHead = unwrapResponseObject(appHeadResp, "app head");
23317
23427
  const remoteUrl = readMetadataString(job, "remoteUrl");
23318
23428
  const defaultBranch = readMetadataString(job, "defaultBranch");
@@ -23335,12 +23445,13 @@ async function processClaimedPendingFinalizeJobInner(params) {
23335
23445
  throw buildFinalizeCliError({
23336
23446
  message: "Finalize queue baseline drifted before this job was processed.",
23337
23447
  exitCode: 1,
23338
- hint: "Process queued finalize jobs in capture order, or re-anchor the repository before retrying.",
23448
+ hint: "Process queued finalize jobs in capture order, or run `remix collab init` to refresh the revision baseline before retrying.",
23339
23449
  disposition: "terminal",
23340
23450
  reason: "baseline_drifted"
23341
23451
  });
23342
23452
  }
23343
- if (appHead.headCommitHash !== job.baselineServerHeadHash) {
23453
+ const serverStillAtBaseline = job.baselineServerRevisionId ? appHead.headRevisionId === job.baselineServerRevisionId : appHead.headCommitHash === job.baselineServerHeadHash;
23454
+ if (!serverStillAtBaseline) {
23344
23455
  throw buildFinalizeCliError({
23345
23456
  message: "Server lane changed before a no-diff turn could be recorded.",
23346
23457
  exitCode: 2,
@@ -23362,6 +23473,7 @@ async function processClaimedPendingFinalizeJobInner(params) {
23362
23473
  defaultBranch,
23363
23474
  baselineSnapshotId: job.baselineSnapshotId,
23364
23475
  currentSnapshotId: job.currentSnapshotId,
23476
+ baselineServerRevisionId: job.baselineServerRevisionId,
23365
23477
  baselineServerHeadHash: job.baselineServerHeadHash,
23366
23478
  currentSnapshotHash: snapshot.snapshotHash,
23367
23479
  localCommitHash: snapshot.localCommitHash,
@@ -23382,6 +23494,8 @@ async function processClaimedPendingFinalizeJobInner(params) {
23382
23494
  branchName: job.branchName,
23383
23495
  lastSnapshotId: snapshot.id,
23384
23496
  lastSnapshotHash: snapshot.snapshotHash,
23497
+ lastServerRevisionId: appHead.headRevisionId ?? null,
23498
+ lastServerTreeHash: appHead.treeHash ?? null,
23385
23499
  lastServerHeadHash: appHead.headCommitHash,
23386
23500
  lastSeenLocalCommitHash: snapshot.localCommitHash
23387
23501
  });
@@ -23402,14 +23516,14 @@ async function processClaimedPendingFinalizeJobInner(params) {
23402
23516
  };
23403
23517
  }
23404
23518
  const localBaselineAdvanced = baseline.lastSnapshotId !== job.baselineSnapshotId;
23405
- const serverHeadAdvanced = appHead.headCommitHash !== job.baselineServerHeadHash;
23519
+ const serverHeadAdvanced = job.baselineServerRevisionId ? appHead.headRevisionId !== job.baselineServerRevisionId : appHead.headCommitHash !== job.baselineServerHeadHash;
23406
23520
  if (baselineDrifted) {
23407
23521
  const consistentAdvance = localBaselineAdvanced && serverHeadAdvanced;
23408
23522
  if (!consistentAdvance) {
23409
23523
  throw buildFinalizeCliError({
23410
23524
  message: `Finalize queue baseline advanced inconsistently before this job was processed (localBaselineAdvanced=${localBaselineAdvanced}, serverHeadAdvanced=${serverHeadAdvanced}, jobBaselineSnapshotId=${job.baselineSnapshotId ?? "null"}, liveBaselineSnapshotId=${baseline.lastSnapshotId ?? "null"}, jobBaselineServerHeadHash=${job.baselineServerHeadHash ?? "null"}, liveBaselineServerHeadHash=${baseline.lastServerHeadHash ?? "null"}, currentAppHeadHash=${appHead.headCommitHash}). This indicates local Remix state diverged from the backend in a way that should not be reachable in normal operation; please report this as a bug.`,
23411
23525
  exitCode: 1,
23412
- hint: "Run `remix collab status` to inspect, then `remix collab re-anchor` only if the lane has no valid baseline.",
23526
+ hint: "Run `remix collab status` to inspect, then sync or reconcile before retrying.",
23413
23527
  disposition: "terminal",
23414
23528
  reason: "baseline_drifted"
23415
23529
  });
@@ -23417,6 +23531,7 @@ async function processClaimedPendingFinalizeJobInner(params) {
23417
23531
  }
23418
23532
  let submissionDiff = diffResult.diff;
23419
23533
  let submissionBaseHeadHash = job.baselineServerHeadHash;
23534
+ let submissionBaseRevisionId = job.baselineServerRevisionId;
23420
23535
  let replayedFromBaseHash = null;
23421
23536
  if (!submissionBaseHeadHash) {
23422
23537
  throw buildFinalizeCliError({
@@ -23427,6 +23542,34 @@ async function processClaimedPendingFinalizeJobInner(params) {
23427
23542
  });
23428
23543
  }
23429
23544
  const replayNeeded = appHead.headCommitHash !== submissionBaseHeadHash || baselineDrifted;
23545
+ if (replayNeeded) {
23546
+ const existingChangeStep = await findExistingChangeStepByIdempotency({
23547
+ api: params.api,
23548
+ appId: job.currentAppId,
23549
+ idempotencyKey: job.idempotencyKey
23550
+ });
23551
+ if (existingChangeStep) {
23552
+ const changeStep2 = existingChangeStep.status === "succeeded" ? existingChangeStep : await pollChangeStep(params.api, job.currentAppId, existingChangeStep.id);
23553
+ invalidateAppHeadCache(job.currentAppId);
23554
+ invalidateAppDeltaCacheForApp(job.currentAppId);
23555
+ await writeBaselineFromSucceededChangeStep({ api: params.api, job, snapshot, changeStep: changeStep2 });
23556
+ await updatePendingFinalizeJob(job.id, {
23557
+ status: "completed",
23558
+ metadata: { changeStepId: String(changeStep2.id ?? "") }
23559
+ });
23560
+ return {
23561
+ mode: "changed_turn",
23562
+ idempotencyKey: job.idempotencyKey ?? "",
23563
+ queued: false,
23564
+ jobId: job.id,
23565
+ repoState,
23566
+ changeStep: changeStep2,
23567
+ collabTurn: null,
23568
+ autoSync: null,
23569
+ warnings: []
23570
+ };
23571
+ }
23572
+ }
23430
23573
  if (replayNeeded) {
23431
23574
  try {
23432
23575
  const replayResp = await params.api.startChangeStepReplay(job.currentAppId, {
@@ -23434,7 +23577,9 @@ async function processClaimedPendingFinalizeJobInner(params) {
23434
23577
  assistantResponse: job.assistantResponse,
23435
23578
  diff: diffResult.diff,
23436
23579
  baseCommitHash: submissionBaseHeadHash,
23580
+ baseRevisionId: job.baselineServerRevisionId,
23437
23581
  targetHeadCommitHash: appHead.headCommitHash,
23582
+ targetRevisionId: appHead.headRevisionId,
23438
23583
  expectedPaths: diffResult.changedPaths,
23439
23584
  actor,
23440
23585
  workspaceMetadata: buildWorkspaceMetadata({
@@ -23444,6 +23589,7 @@ async function processClaimedPendingFinalizeJobInner(params) {
23444
23589
  defaultBranch,
23445
23590
  baselineSnapshotId: job.baselineSnapshotId,
23446
23591
  currentSnapshotId: job.currentSnapshotId,
23592
+ baselineServerRevisionId: job.baselineServerRevisionId,
23447
23593
  baselineServerHeadHash: job.baselineServerHeadHash,
23448
23594
  currentSnapshotHash: snapshot.snapshotHash,
23449
23595
  localCommitHash: snapshot.localCommitHash,
@@ -23469,6 +23615,7 @@ async function processClaimedPendingFinalizeJobInner(params) {
23469
23615
  submissionDiff = replayDiff.diff;
23470
23616
  replayedFromBaseHash = submissionBaseHeadHash;
23471
23617
  submissionBaseHeadHash = appHead.headCommitHash;
23618
+ submissionBaseRevisionId = appHead.headRevisionId;
23472
23619
  } catch (error2) {
23473
23620
  if (error2 instanceof RemixError && error2.finalizeDisposition === void 0) {
23474
23621
  const detail = error2.hint ? `${error2.message} (${error2.hint})` : error2.message;
@@ -23490,6 +23637,7 @@ async function processClaimedPendingFinalizeJobInner(params) {
23490
23637
  assistantResponse: job.assistantResponse,
23491
23638
  diff: submissionDiff,
23492
23639
  baseCommitHash: submissionBaseHeadHash,
23640
+ baseRevisionId: submissionBaseRevisionId,
23493
23641
  headCommitHash: submissionBaseHeadHash,
23494
23642
  changedFilesCount: diffResult.stats.changedFilesCount,
23495
23643
  insertions: diffResult.stats.insertions,
@@ -23502,6 +23650,7 @@ async function processClaimedPendingFinalizeJobInner(params) {
23502
23650
  defaultBranch,
23503
23651
  baselineSnapshotId: job.baselineSnapshotId,
23504
23652
  currentSnapshotId: job.currentSnapshotId,
23653
+ baselineServerRevisionId: job.baselineServerRevisionId,
23505
23654
  baselineServerHeadHash: job.baselineServerHeadHash,
23506
23655
  currentSnapshotHash: snapshot.snapshotHash,
23507
23656
  localCommitHash: snapshot.localCommitHash,
@@ -23518,27 +23667,7 @@ async function processClaimedPendingFinalizeJobInner(params) {
23518
23667
  const changeStep = await pollChangeStep(params.api, job.currentAppId, String(createdStep.id));
23519
23668
  invalidateAppHeadCache(job.currentAppId);
23520
23669
  invalidateAppDeltaCacheForApp(job.currentAppId);
23521
- const nextServerHeadHash = typeof changeStep.headCommitHash === "string" ? changeStep.headCommitHash.trim() : "";
23522
- if (!nextServerHeadHash) {
23523
- throw buildFinalizeCliError({
23524
- message: "Backend returned a succeeded change step without a head commit hash.",
23525
- exitCode: 1,
23526
- hint: "This is a backend invariant violation; retry will not help. Re-anchor and try again.",
23527
- disposition: "terminal",
23528
- reason: "missing_head_commit_hash"
23529
- });
23530
- }
23531
- await writeLocalBaseline({
23532
- repoRoot: job.repoRoot,
23533
- repoFingerprint: job.repoFingerprint,
23534
- laneId: job.laneId,
23535
- currentAppId: job.currentAppId,
23536
- branchName: job.branchName,
23537
- lastSnapshotId: snapshot.id,
23538
- lastSnapshotHash: snapshot.snapshotHash,
23539
- lastServerHeadHash: nextServerHeadHash,
23540
- lastSeenLocalCommitHash: snapshot.localCommitHash
23541
- });
23670
+ await writeBaselineFromSucceededChangeStep({ api: params.api, job, snapshot, changeStep });
23542
23671
  await updatePendingFinalizeJob(job.id, {
23543
23672
  status: "completed",
23544
23673
  metadata: { changeStepId: String(changeStep.id ?? "") }
@@ -23596,9 +23725,10 @@ var FINALIZE_PREFLIGHT_FAILURE_CODES = [
23596
23725
  // Server has commits we don't. Fix: `remix collab sync` (safe to
23597
23726
  // auto-run for fast-forward; non-FF refused by the command itself).
23598
23727
  "pull_required",
23599
- // Local base hash doesn't match the recorded baseline (force-push,
23600
- // hard reset, rebase). Fix: `remix collab re-anchor`.
23601
- "re_anchor_required"
23728
+ // Both local and server changed. Fix: inspect and apply reconcile.
23729
+ "reconcile_required",
23730
+ // Local revision baseline is missing. Fix: `remix collab init` or sync.
23731
+ "baseline_missing"
23602
23732
  ];
23603
23733
  var CODE_SET = new Set(FINALIZE_PREFLIGHT_FAILURE_CODES);
23604
23734
  var DEFAULT_ACQUIRE_TIMEOUT_MS = 15e3;
@@ -23818,7 +23948,7 @@ async function ensureWorkspaceMatchesBaseline(params) {
23818
23948
  if (!baseline?.lastSnapshotHash || !baseline.lastServerHeadHash) {
23819
23949
  throw new RemixError("Local Remix baseline is missing for this lane.", {
23820
23950
  exitCode: 2,
23821
- hint: "Run `remix collab re-anchor` to create a fresh baseline before applying server changes."
23951
+ hint: "Run `remix collab init` or sync from a checkout with a valid revision baseline before applying server changes."
23822
23952
  });
23823
23953
  }
23824
23954
  const inspection = await inspectLocalSnapshot({
@@ -23890,11 +24020,12 @@ async function collabSync(params) {
23890
24020
  const repoSnapshot = await captureRepoSnapshot(repoRoot, { includeWorkspaceDiffHash: true });
23891
24021
  const bootstrapFromLocalHead = !detected.baseline.lastSnapshotHash || !detected.baseline.lastServerHeadHash;
23892
24022
  let baselineServerHeadHash;
24023
+ let baselineServerRevisionId = null;
23893
24024
  if (bootstrapFromLocalHead) {
23894
24025
  if (!headCommitHash) {
23895
24026
  throw new RemixError("Failed to resolve local HEAD commit for the initial sync bootstrap.", {
23896
24027
  exitCode: 1,
23897
- hint: "Retry after Git HEAD is available, or run `remix collab re-anchor` if this checkout has no local Remix baseline yet."
24028
+ hint: "Retry after Git HEAD is available, or run `remix collab init` to seed this checkout's revision baseline."
23898
24029
  });
23899
24030
  }
23900
24031
  baselineServerHeadHash = headCommitHash;
@@ -23909,13 +24040,15 @@ async function collabSync(params) {
23909
24040
  if (!baseline.lastServerHeadHash) {
23910
24041
  throw new RemixError("Local Remix baseline is missing the last acknowledged server head.", {
23911
24042
  exitCode: 2,
23912
- hint: "Run `remix collab re-anchor` to create a fresh baseline before pulling server changes."
24043
+ hint: "Run `remix collab init` or sync from a checkout with a valid revision baseline before pulling server changes."
23913
24044
  });
23914
24045
  }
23915
24046
  baselineServerHeadHash = baseline.lastServerHeadHash;
24047
+ baselineServerRevisionId = baseline.lastServerRevisionId;
23916
24048
  }
23917
24049
  const deltaResp = await params.api.getAppDelta(binding.currentAppId, {
23918
24050
  baseHeadHash: baselineServerHeadHash,
24051
+ baseRevisionId: baselineServerRevisionId,
23919
24052
  repoFingerprint: binding.repoFingerprint ?? void 0,
23920
24053
  remoteUrl: binding.remoteUrl ?? void 0,
23921
24054
  defaultBranch: binding.defaultBranch ?? void 0
@@ -23939,13 +24072,54 @@ async function collabSync(params) {
23939
24072
  applied: false,
23940
24073
  dryRun: params.dryRun
23941
24074
  };
23942
- if (params.dryRun || delta.status === "up_to_date") {
24075
+ if (params.dryRun) {
23943
24076
  return previewResult;
23944
24077
  }
24078
+ if (delta.status === "up_to_date") {
24079
+ if (!bootstrapFromLocalHead) {
24080
+ return previewResult;
24081
+ }
24082
+ return withRepoMutationLock(
24083
+ {
24084
+ cwd: repoRoot,
24085
+ operation: "collabSync"
24086
+ },
24087
+ async ({ repoRoot: lockedRepoRoot, warnings }) => {
24088
+ await assertRepoSnapshotUnchanged(lockedRepoRoot, repoSnapshot, {
24089
+ operation: "`remix collab sync`",
24090
+ recoveryHint: "The repository changed before the first local Remix baseline could be created. Review the local changes and rerun `remix collab sync`."
24091
+ });
24092
+ const snapshot = await captureLocalSnapshot({
24093
+ repoRoot: lockedRepoRoot,
24094
+ repoFingerprint: binding.repoFingerprint,
24095
+ laneId: binding.laneId,
24096
+ branchName: binding.branchName
24097
+ });
24098
+ await writeLocalBaseline({
24099
+ repoRoot: lockedRepoRoot,
24100
+ repoFingerprint: binding.repoFingerprint,
24101
+ laneId: binding.laneId,
24102
+ currentAppId: binding.currentAppId,
24103
+ branchName: binding.branchName,
24104
+ lastSnapshotId: snapshot.id,
24105
+ lastSnapshotHash: snapshot.snapshotHash,
24106
+ lastServerRevisionId: delta.targetRevisionId ?? null,
24107
+ lastServerTreeHash: delta.targetTreeHash ?? null,
24108
+ lastServerHeadHash: delta.targetHeadHash,
24109
+ lastSeenLocalCommitHash: snapshot.localCommitHash
24110
+ });
24111
+ return {
24112
+ ...previewResult,
24113
+ localCommitHash: snapshot.localCommitHash,
24114
+ ...warnings.length > 0 ? { warnings } : {}
24115
+ };
24116
+ }
24117
+ );
24118
+ }
23945
24119
  if (delta.status === "base_unknown") {
23946
24120
  throw new RemixError("Direct pull is unavailable because Remix can no longer diff from the last acknowledged server head.", {
23947
24121
  exitCode: 2,
23948
- hint: "Run `remix collab reconcile --dry-run` to inspect recovery options before retrying. If this checkout has no local Remix baseline yet for this lane, `remix collab re-anchor` may be required."
24122
+ hint: "Run `remix collab reconcile --dry-run` to inspect recovery options before retrying. If this checkout has no local Remix baseline yet for this lane, run `remix collab init` to seed one."
23949
24123
  });
23950
24124
  }
23951
24125
  if (delta.status !== "delta_ready") {
@@ -23989,6 +24163,8 @@ async function collabSync(params) {
23989
24163
  branchName: binding.branchName,
23990
24164
  lastSnapshotId: snapshot.id,
23991
24165
  lastSnapshotHash: snapshot.snapshotHash,
24166
+ lastServerRevisionId: delta.targetRevisionId ?? null,
24167
+ lastServerTreeHash: delta.targetTreeHash ?? null,
23992
24168
  lastServerHeadHash: delta.targetHeadHash,
23993
24169
  lastSeenLocalCommitHash: snapshot.localCommitHash
23994
24170
  });
@@ -24274,6 +24450,8 @@ async function collabCheckout(params) {
24274
24450
  branchName: branchNameForBaseline,
24275
24451
  lastSnapshotId: snapshot.id,
24276
24452
  lastSnapshotHash: snapshot.snapshotHash,
24453
+ lastServerRevisionId: appHead.headRevisionId ?? null,
24454
+ lastServerTreeHash: appHead.treeHash ?? null,
24277
24455
  lastServerHeadHash: appHead.headCommitHash,
24278
24456
  lastSeenLocalCommitHash: snapshot.localCommitHash
24279
24457
  });
@@ -24631,25 +24809,21 @@ async function trySeedEquivalentBranchBaseline(params) {
24631
24809
  branchName: params.branchName,
24632
24810
  persistBlobs: false
24633
24811
  });
24634
- if (inspection.snapshotHash !== defaultBaseline.lastSnapshotHash) {
24812
+ if (inspection.snapshotHash !== defaultBaseline.lastSnapshotHash && inspection.localCommitHash !== defaultBaseline.lastSeenLocalCommitHash) {
24635
24813
  return null;
24636
24814
  }
24637
- const snapshot = await captureLocalSnapshot({
24638
- repoRoot: params.repoRoot,
24639
- repoFingerprint: params.repoFingerprint,
24640
- laneId: params.laneId,
24641
- branchName: params.branchName
24642
- });
24643
24815
  await writeLocalBaseline({
24644
24816
  repoRoot: params.repoRoot,
24645
24817
  repoFingerprint: params.repoFingerprint,
24646
24818
  laneId: params.laneId,
24647
24819
  currentAppId: params.currentAppId,
24648
24820
  branchName: params.branchName,
24649
- lastSnapshotId: snapshot.id,
24650
- lastSnapshotHash: snapshot.snapshotHash,
24821
+ lastSnapshotId: defaultBaseline.lastSnapshotId,
24822
+ lastSnapshotHash: defaultBaseline.lastSnapshotHash,
24823
+ lastServerRevisionId: params.appHeadRevisionId,
24824
+ lastServerTreeHash: params.appTreeHash,
24651
24825
  lastServerHeadHash: params.appHeadHash,
24652
- lastSeenLocalCommitHash: snapshot.localCommitHash
24826
+ lastSeenLocalCommitHash: inspection.localCommitHash
24653
24827
  });
24654
24828
  return "seeded";
24655
24829
  }
@@ -24659,11 +24833,11 @@ async function resolveInitBaselineStatus(params) {
24659
24833
  laneId: params.laneId,
24660
24834
  repoRoot: params.repoRoot
24661
24835
  });
24662
- if (baseline?.lastSnapshotHash && baseline.lastServerHeadHash) {
24836
+ if (baseline?.lastSnapshotHash && (baseline.lastServerRevisionId || baseline.lastServerHeadHash)) {
24663
24837
  return "existing";
24664
24838
  }
24665
24839
  const localHeadCommitHash = await getHeadCommitHash(params.repoRoot);
24666
- if (!localHeadCommitHash) return "requires_re_anchor";
24840
+ if (!localHeadCommitHash) return "baseline_missing";
24667
24841
  const appHead = unwrapResponseObject(
24668
24842
  await params.api.getAppHead(params.currentAppId),
24669
24843
  "app head"
@@ -24690,6 +24864,7 @@ async function resolveInitBaselineStatus(params) {
24690
24864
  const deltaResp = await params.api.getAppDelta(params.currentAppId, {
24691
24865
  baseHeadHash: localHeadCommitHash,
24692
24866
  targetHeadHash: appHead.headCommitHash,
24867
+ targetRevisionId: appHead.headRevisionId,
24693
24868
  localSnapshotHash,
24694
24869
  repoFingerprint: params.repoFingerprint,
24695
24870
  remoteUrl: params.remoteUrl ?? void 0,
@@ -24697,6 +24872,21 @@ async function resolveInitBaselineStatus(params) {
24697
24872
  });
24698
24873
  const delta = unwrapResponseObject(deltaResp, "app delta");
24699
24874
  if (delta.status === "up_to_date" || delta.status === "delta_ready") {
24875
+ const equivalentBaseline = await trySeedEquivalentBranchBaseline({
24876
+ repoRoot: params.repoRoot,
24877
+ repoFingerprint: params.repoFingerprint,
24878
+ laneId: params.laneId,
24879
+ currentAppId: params.currentAppId,
24880
+ upstreamAppId: params.upstreamAppId ?? null,
24881
+ branchName: params.branchName,
24882
+ defaultBranch: params.defaultBranch,
24883
+ appHeadHash: appHead.headCommitHash,
24884
+ appHeadRevisionId: appHead.headRevisionId ?? null,
24885
+ appTreeHash: appHead.treeHash ?? null
24886
+ });
24887
+ if (equivalentBaseline) {
24888
+ return equivalentBaseline;
24889
+ }
24700
24890
  return "requires_sync";
24701
24891
  }
24702
24892
  if (delta.status === "content_equivalent") {
@@ -24719,7 +24909,9 @@ async function resolveInitBaselineStatus(params) {
24719
24909
  upstreamAppId: params.upstreamAppId ?? null,
24720
24910
  branchName: params.branchName,
24721
24911
  defaultBranch: params.defaultBranch,
24722
- appHeadHash: appHead.headCommitHash
24912
+ appHeadHash: appHead.headCommitHash,
24913
+ appHeadRevisionId: appHead.headRevisionId ?? null,
24914
+ appTreeHash: appHead.treeHash ?? null
24723
24915
  });
24724
24916
  if (equivalentBaseline) {
24725
24917
  return equivalentBaseline;
@@ -24727,7 +24919,7 @@ async function resolveInitBaselineStatus(params) {
24727
24919
  }
24728
24920
  } catch {
24729
24921
  }
24730
- return "requires_re_anchor";
24922
+ return "baseline_missing";
24731
24923
  }
24732
24924
  async function seedImportedInitBaseline(params) {
24733
24925
  const appHead = unwrapResponseObject(
@@ -24748,6 +24940,8 @@ async function seedImportedInitBaseline(params) {
24748
24940
  branchName: params.branchName,
24749
24941
  lastSnapshotId: snapshot.id,
24750
24942
  lastSnapshotHash: snapshot.snapshotHash,
24943
+ lastServerRevisionId: appHead.headRevisionId ?? null,
24944
+ lastServerTreeHash: appHead.treeHash ?? null,
24751
24945
  lastServerHeadHash: appHead.headCommitHash,
24752
24946
  lastSeenLocalCommitHash: snapshot.localCommitHash
24753
24947
  });
@@ -24761,7 +24955,6 @@ async function collabInit(params) {
24761
24955
  },
24762
24956
  async ({ repoRoot, warnings }) => {
24763
24957
  await ensureGitInfoExcludeEntries(repoRoot, [".remix/"]);
24764
- await ensureCleanWorktree(repoRoot, "`remix collab init`");
24765
24958
  if (params.path?.trim()) {
24766
24959
  throw new RemixError("`remix collab init --path` is not supported.", {
24767
24960
  exitCode: 2,
@@ -24769,6 +24962,10 @@ async function collabInit(params) {
24769
24962
  });
24770
24963
  }
24771
24964
  const localBindingState = await readCollabBindingState(repoRoot, { persist: true });
24965
+ const hasExistingBinding = localBindingState != null && Object.keys(localBindingState.branchBindings ?? {}).length > 0;
24966
+ if (params.forceNew || !hasExistingBinding) {
24967
+ await ensureCleanWorktree(repoRoot, "`remix collab init`");
24968
+ }
24772
24969
  const persistedRemoteUrl = normalizeGitRemote(localBindingState?.remoteUrl ?? null);
24773
24970
  const currentBranch = await getCurrentBranch(repoRoot);
24774
24971
  const defaultBranch = localBindingState?.defaultBranch ?? await getDefaultBranch(repoRoot) ?? currentBranch;
@@ -25620,248 +25817,6 @@ function hasPendingFinalize(summary) {
25620
25817
  function buildPendingFinalizeHint() {
25621
25818
  return "Drain or await the local finalize queue first, then retry after the queued Remix turn finishes recording remotely.";
25622
25819
  }
25623
- async function collabReAnchor(params) {
25624
- const repoRoot = await findGitRoot(params.cwd);
25625
- const binding = await ensureActiveLaneBinding({
25626
- repoRoot,
25627
- api: params.api,
25628
- operation: "`remix collab re-anchor`"
25629
- });
25630
- if (!binding) {
25631
- throw new RemixError("Repository is not bound to Remix.", {
25632
- exitCode: 2,
25633
- hint: "Run `remix collab init` first."
25634
- });
25635
- }
25636
- const detected = await collabDetectRepoState({
25637
- api: params.api,
25638
- cwd: repoRoot,
25639
- allowBranchMismatch: params.allowBranchMismatch
25640
- });
25641
- if (detected.status === "metadata_conflict" || detected.status === "branch_mismatch") {
25642
- throw new RemixError("Repository must be realigned before seeding a fresh local Remix baseline.", {
25643
- exitCode: 2,
25644
- hint: detected.hint
25645
- });
25646
- }
25647
- if (detected.status !== "ready" || !detected.binding) {
25648
- throw new RemixError(detected.hint || "Repository is not ready for re-anchor.", {
25649
- exitCode: 2,
25650
- hint: detected.hint
25651
- });
25652
- }
25653
- if (detected.repoState === "server_only_changed") {
25654
- throw new RemixError("This checkout is already on a server-known base and only needs a local pull.", {
25655
- exitCode: 2,
25656
- hint: "Run `remix collab sync` instead of `remix collab re-anchor`."
25657
- });
25658
- }
25659
- if (detected.repoState === "both_changed") {
25660
- throw new RemixError("Both the local workspace and the server lane changed since the last agreed baseline.", {
25661
- exitCode: 2,
25662
- hint: "Run `remix collab reconcile` to replay the local boundary onto the newer server head."
25663
- });
25664
- }
25665
- if (detected.repoState === "local_only_changed") {
25666
- if (hasPendingFinalize(detected.pendingFinalize)) {
25667
- throw new RemixError("Re-anchor is not needed while queued Remix turn recording is still processing.", {
25668
- exitCode: 2,
25669
- hint: buildPendingFinalizeHint()
25670
- });
25671
- }
25672
- throw new RemixError("Re-anchor is not the right command for local content changes.", {
25673
- exitCode: 2,
25674
- hint: "Remix is source-blind: any local content change since the last recorded turn \u2014 including manual commits, pulls, merges, and rebases \u2014 is recorded with `remix collab finalize-turn`. Use `remix collab re-anchor` only when no local Remix baseline exists yet for this lane (status reports `re_anchor`)."
25675
- });
25676
- }
25677
- if (detected.repoState === "idle") {
25678
- throw new RemixError("This checkout is already aligned with Remix.", {
25679
- exitCode: 2,
25680
- hint: "No re-anchor step is needed. Re-anchor only applies when no local Remix baseline exists yet for this lane."
25681
- });
25682
- }
25683
- await ensureCleanWorktree(repoRoot, "`remix collab re-anchor`");
25684
- const branch = await requireCurrentBranch(repoRoot);
25685
- const headCommitHash = await getHeadCommitHash(repoRoot);
25686
- if (!headCommitHash) {
25687
- throw new RemixError("Failed to resolve local HEAD commit.", { exitCode: 1 });
25688
- }
25689
- if (params.asyncSubmit && !params.dryRun) {
25690
- const pending = await findPendingAsyncJob({
25691
- repoRoot,
25692
- branchName: binding.branchName ?? branch,
25693
- kind: "re_anchor"
25694
- });
25695
- if (pending) {
25696
- return {
25697
- status: "queued",
25698
- queued: true,
25699
- jobId: pending.id,
25700
- repoRoot,
25701
- branch,
25702
- currentAppId: binding.currentAppId,
25703
- dryRun: false,
25704
- applied: false
25705
- };
25706
- }
25707
- }
25708
- const preflightResp = await params.api.preflightAppReconcile(binding.currentAppId, {
25709
- localHeadCommitHash: headCommitHash,
25710
- repoFingerprint: binding.repoFingerprint ?? void 0,
25711
- remoteUrl: binding.remoteUrl ?? void 0,
25712
- defaultBranch: binding.defaultBranch ?? void 0
25713
- });
25714
- const preflight = unwrapResponseObject(preflightResp, "reconcile preflight");
25715
- if (preflight.status === "metadata_conflict") {
25716
- throw new RemixError("Local repository metadata conflicts with the bound Remix app.", {
25717
- exitCode: 2,
25718
- hint: preflight.warnings.join("\n") || "Run the command from the correct bound repository."
25719
- });
25720
- }
25721
- const preview = {
25722
- status: preflight.status === "up_to_date" ? "reanchored" : "re_anchor_required",
25723
- repoRoot,
25724
- branch,
25725
- currentAppId: binding.currentAppId,
25726
- localHeadCommitHash: headCommitHash,
25727
- targetHeadCommitHash: preflight.targetHeadCommitHash,
25728
- targetHeadCommitId: preflight.targetHeadCommitId,
25729
- warnings: preflight.warnings,
25730
- applied: false,
25731
- dryRun: params.dryRun === true
25732
- };
25733
- if (params.dryRun) {
25734
- return preview;
25735
- }
25736
- let anchoredServerHeadHash = preflight.targetHeadCommitHash;
25737
- if (params.asyncSubmit && preflight.status === "ready_to_reconcile") {
25738
- const failed = await findFailedAsyncJob({
25739
- repoRoot,
25740
- branchName: binding.branchName ?? branch,
25741
- kind: "re_anchor"
25742
- });
25743
- if (failed) {
25744
- await deleteAsyncJob(failed.id);
25745
- }
25746
- const { bundlePath: tmpBundlePath, headCommitHash: bundledHeadCommitHash } = await createGitBundle(
25747
- repoRoot,
25748
- "re-anchor.bundle"
25749
- );
25750
- const tmpBundleDir = import_path12.default.dirname(tmpBundlePath);
25751
- try {
25752
- const jobId = (0, import_crypto9.randomUUID)();
25753
- const durableBundlePath = getAsyncJobBundlePath(jobId);
25754
- await import_promises23.default.mkdir(getAsyncJobDir(jobId), { recursive: true });
25755
- try {
25756
- await import_promises23.default.rename(tmpBundlePath, durableBundlePath);
25757
- } catch (error2) {
25758
- if (error2?.code !== "EXDEV") throw error2;
25759
- await import_promises23.default.copyFile(tmpBundlePath, durableBundlePath);
25760
- await import_promises23.default.unlink(tmpBundlePath).catch(() => void 0);
25761
- }
25762
- const bundleSha = await sha256FileHex(durableBundlePath);
25763
- const job = await enqueueAsyncJob({
25764
- id: jobId,
25765
- kind: "re_anchor",
25766
- status: "queued",
25767
- repoRoot,
25768
- repoFingerprint: binding.repoFingerprint,
25769
- branchName: binding.branchName ?? branch,
25770
- laneId: binding.laneId,
25771
- retryCount: 0,
25772
- error: null,
25773
- idempotencyKey: null,
25774
- payload: {
25775
- bundlePath: durableBundlePath,
25776
- bundleSha256: bundleSha,
25777
- localHeadCommitHash: bundledHeadCommitHash,
25778
- targetHeadCommitHash: preflight.targetHeadCommitHash,
25779
- appId: binding.currentAppId
25780
- }
25781
- });
25782
- await logDrainerEvent(job.id, "submitted", { kind: "re_anchor" });
25783
- return {
25784
- status: "queued",
25785
- queued: true,
25786
- jobId: job.id,
25787
- repoRoot,
25788
- branch,
25789
- currentAppId: binding.currentAppId,
25790
- localHeadCommitHash: bundledHeadCommitHash,
25791
- targetHeadCommitHash: preflight.targetHeadCommitHash,
25792
- warnings: preflight.warnings,
25793
- dryRun: false,
25794
- applied: false
25795
- };
25796
- } finally {
25797
- await import_promises23.default.rm(tmpBundleDir, { recursive: true, force: true }).catch(() => void 0);
25798
- }
25799
- }
25800
- if (preflight.status === "ready_to_reconcile") {
25801
- const { bundlePath, headCommitHash: bundledHeadCommitHash } = await createGitBundle(repoRoot, "re-anchor.bundle");
25802
- const bundleTempDir = import_path12.default.dirname(bundlePath);
25803
- try {
25804
- const bundleStat = await import_promises23.default.stat(bundlePath);
25805
- const checksumSha256 = await sha256FileHex(bundlePath);
25806
- const presignResp = await params.api.presignImportUploadFirstParty({
25807
- file: {
25808
- name: import_path12.default.basename(bundlePath),
25809
- mimeType: "application/x-git-bundle",
25810
- size: bundleStat.size,
25811
- checksumSha256
25812
- }
25813
- });
25814
- const uploadTarget = unwrapResponseObject(presignResp, "import upload target");
25815
- await uploadPresigned({
25816
- uploadUrl: String(uploadTarget.uploadUrl),
25817
- filePath: bundlePath,
25818
- headers: uploadTarget.headers ?? {}
25819
- });
25820
- const startResp = await params.api.startAppReconcile(binding.currentAppId, {
25821
- uploadId: String(uploadTarget.uploadId),
25822
- localHeadCommitHash: bundledHeadCommitHash,
25823
- repoFingerprint: binding.repoFingerprint ?? void 0,
25824
- remoteUrl: binding.remoteUrl ?? void 0,
25825
- defaultBranch: binding.defaultBranch ?? void 0,
25826
- idempotencyKey: buildDeterministicIdempotencyKey({
25827
- kind: "collab_re_anchor_v1",
25828
- appId: binding.currentAppId,
25829
- localHeadCommitHash: bundledHeadCommitHash,
25830
- targetHeadCommitHash: preflight.targetHeadCommitHash
25831
- })
25832
- });
25833
- const started = unwrapResponseObject(startResp, "reconcile");
25834
- const reconcile2 = await pollReconcile(params.api, binding.currentAppId, started.id);
25835
- anchoredServerHeadHash = reconcile2.reconciledHeadCommitHash ?? reconcile2.targetHeadCommitHash ?? preflight.targetHeadCommitHash;
25836
- } finally {
25837
- await import_promises23.default.rm(bundleTempDir, { recursive: true, force: true });
25838
- }
25839
- }
25840
- const snapshot = await captureLocalSnapshot({
25841
- repoRoot,
25842
- repoFingerprint: binding.repoFingerprint,
25843
- laneId: binding.laneId,
25844
- branchName: binding.branchName
25845
- });
25846
- await writeLocalBaseline({
25847
- repoRoot,
25848
- repoFingerprint: binding.repoFingerprint,
25849
- laneId: binding.laneId,
25850
- currentAppId: binding.currentAppId,
25851
- branchName: binding.branchName,
25852
- lastSnapshotId: snapshot.id,
25853
- lastSnapshotHash: snapshot.snapshotHash,
25854
- lastServerHeadHash: anchoredServerHeadHash,
25855
- lastSeenLocalCommitHash: snapshot.localCommitHash
25856
- });
25857
- return {
25858
- ...preview,
25859
- status: "reanchored",
25860
- targetHeadCommitHash: anchoredServerHeadHash,
25861
- applied: true,
25862
- dryRun: false
25863
- };
25864
- }
25865
25820
  async function reconcileBothChanged(params) {
25866
25821
  const repoRoot = await findGitRoot(params.cwd);
25867
25822
  const binding = await ensureActiveLaneBinding({
@@ -25884,7 +25839,7 @@ async function reconcileBothChanged(params) {
25884
25839
  if (!baseline?.lastSnapshotId || !baseline.lastServerHeadHash) {
25885
25840
  throw new RemixError("Local Remix baseline is missing for this lane.", {
25886
25841
  exitCode: 2,
25887
- hint: "Run `remix collab re-anchor` to create a fresh baseline first."
25842
+ hint: "Run `remix collab init` or sync from a checkout with a valid revision baseline first."
25888
25843
  });
25889
25844
  }
25890
25845
  const currentSnapshot = await captureLocalSnapshot({
@@ -25907,6 +25862,7 @@ async function reconcileBothChanged(params) {
25907
25862
  params.api.getAppHead(binding.currentAppId),
25908
25863
  params.api.getAppDelta(binding.currentAppId, {
25909
25864
  baseHeadHash: baseline.lastServerHeadHash,
25865
+ baseRevisionId: baseline.lastServerRevisionId,
25910
25866
  repoFingerprint: binding.repoFingerprint ?? void 0,
25911
25867
  remoteUrl: binding.remoteUrl ?? void 0,
25912
25868
  defaultBranch: binding.defaultBranch ?? void 0
@@ -25924,7 +25880,7 @@ async function reconcileBothChanged(params) {
25924
25880
  if (delta.status === "base_unknown") {
25925
25881
  throw new RemixError("Reconcile cannot pull the newer server state from the last acknowledged baseline.", {
25926
25882
  exitCode: 2,
25927
- hint: "Run `remix collab re-anchor` to re-anchor this checkout before retrying."
25883
+ hint: "Run `remix collab init` to seed a fresh revision baseline for this checkout before retrying."
25928
25884
  });
25929
25885
  }
25930
25886
  if (delta.status !== "delta_ready" && delta.status !== "up_to_date") {
@@ -25955,7 +25911,9 @@ async function reconcileBothChanged(params) {
25955
25911
  assistantResponse: "Replay the local boundary delta onto the latest server head without recording a new change step.",
25956
25912
  diff: diffResult.diff,
25957
25913
  baseCommitHash: baseline.lastServerHeadHash,
25914
+ baseRevisionId: baseline.lastServerRevisionId,
25958
25915
  targetHeadCommitHash: appHead.headCommitHash,
25916
+ targetRevisionId: appHead.headRevisionId,
25959
25917
  expectedPaths: diffResult.changedPaths,
25960
25918
  workspaceMetadata: {
25961
25919
  recordingMode: "boundary_delta",
@@ -25963,6 +25921,7 @@ async function reconcileBothChanged(params) {
25963
25921
  branch,
25964
25922
  baselineSnapshotId: baseline.lastSnapshotId,
25965
25923
  currentSnapshotId: currentSnapshot.id,
25924
+ baselineServerRevisionId: baseline.lastServerRevisionId,
25966
25925
  baselineServerHeadHash: baseline.lastServerHeadHash,
25967
25926
  currentSnapshotHash: currentSnapshot.snapshotHash,
25968
25927
  localCommitHash: currentSnapshot.localCommitHash,
@@ -25981,12 +25940,12 @@ async function reconcileBothChanged(params) {
25981
25940
  const replay = await pollChangeStepReplay(params.api, binding.currentAppId, String(replayStart.id));
25982
25941
  const replayDiffResp = await params.api.getChangeStepReplayDiff(binding.currentAppId, replay.id);
25983
25942
  const replayDiff = unwrapResponseObject(replayDiffResp, "change step replay diff");
25984
- const tempRoot = await import_promises24.default.mkdtemp(import_path13.default.join(import_os6.default.tmpdir(), "remix-reconcile-"));
25943
+ const tempRoot = await import_promises23.default.mkdtemp(import_path12.default.join(import_os6.default.tmpdir(), "remix-reconcile-"));
25985
25944
  let serverHeadSnapshot = null;
25986
25945
  let mergedSnapshot = null;
25987
25946
  try {
25988
- const tempRepoRoot = import_path13.default.join(tempRoot, "repo");
25989
- await import_promises24.default.mkdir(tempRepoRoot, { recursive: true });
25947
+ const tempRepoRoot = import_path12.default.join(tempRoot, "repo");
25948
+ await import_promises23.default.mkdir(tempRepoRoot, { recursive: true });
25990
25949
  await execa("git", ["init"], { cwd: tempRepoRoot, stderr: "ignore" });
25991
25950
  await materializeLocalSnapshot(baseline.lastSnapshotId, tempRepoRoot);
25992
25951
  if (delta.status === "delta_ready" && delta.diff.trim()) {
@@ -26008,7 +25967,7 @@ async function reconcileBothChanged(params) {
26008
25967
  branchName: binding.branchName
26009
25968
  });
26010
25969
  } finally {
26011
- await import_promises24.default.rm(tempRoot, { recursive: true, force: true }).catch(() => void 0);
25970
+ await import_promises23.default.rm(tempRoot, { recursive: true, force: true }).catch(() => void 0);
26012
25971
  }
26013
25972
  if (!serverHeadSnapshot || !mergedSnapshot) {
26014
25973
  throw new RemixError("Failed to materialize the reconciled local workspace.", { exitCode: 1 });
@@ -26045,6 +26004,8 @@ async function reconcileBothChanged(params) {
26045
26004
  branchName: binding.branchName,
26046
26005
  lastSnapshotId: serverHeadSnapshot.id,
26047
26006
  lastSnapshotHash: serverHeadSnapshot.snapshotHash,
26007
+ lastServerRevisionId: appHead.headRevisionId ?? null,
26008
+ lastServerTreeHash: appHead.treeHash ?? null,
26048
26009
  lastServerHeadHash: appHead.headCommitHash,
26049
26010
  lastSeenLocalCommitHash: restoredSnapshot.localCommitHash
26050
26011
  });
@@ -26080,7 +26041,10 @@ async function collabReconcile(params) {
26080
26041
  return reconcileBothChanged(params);
26081
26042
  }
26082
26043
  if (detected.repoState === "external_local_base_changed") {
26083
- return collabReAnchor(params);
26044
+ throw new RemixError("This checkout needs a local Remix revision baseline before reconciliation.", {
26045
+ exitCode: 2,
26046
+ hint: detected.hint || "Run `remix collab init` or `remix collab sync` to seed the baseline."
26047
+ });
26084
26048
  }
26085
26049
  if (detected.repoState === "local_only_changed") {
26086
26050
  if (hasPendingFinalize(detected.pendingFinalize)) {
@@ -26187,6 +26151,8 @@ async function collabRemix(params) {
26187
26151
  branchName: branchNameForBaseline,
26188
26152
  lastSnapshotId: snapshot.id,
26189
26153
  lastSnapshotHash: snapshot.snapshotHash,
26154
+ lastServerRevisionId: appHead.headRevisionId ?? null,
26155
+ lastServerTreeHash: appHead.treeHash ?? null,
26190
26156
  lastServerHeadHash: appHead.headCommitHash,
26191
26157
  lastSeenLocalCommitHash: snapshot.localCommitHash
26192
26158
  });
@@ -26307,11 +26273,15 @@ function createBaseStatus() {
26307
26273
  baseline: {
26308
26274
  lastSnapshotId: null,
26309
26275
  lastSnapshotHash: null,
26276
+ lastServerRevisionId: null,
26277
+ lastServerTreeHash: null,
26310
26278
  lastServerHeadHash: null,
26311
26279
  lastSeenLocalCommitHash: null
26312
26280
  },
26313
26281
  current: {
26314
26282
  snapshotHash: null,
26283
+ serverRevisionId: null,
26284
+ serverTreeHash: null,
26315
26285
  serverHeadHash: null,
26316
26286
  serverHeadCommitId: null,
26317
26287
  localCommitHash: null
@@ -26398,6 +26368,8 @@ async function collabStatus(params) {
26398
26368
  status.alignment.baseline = detected.baseline;
26399
26369
  status.alignment.current = {
26400
26370
  snapshotHash: detected.currentSnapshotHash,
26371
+ serverRevisionId: detected.currentServerRevisionId,
26372
+ serverTreeHash: detected.currentServerTreeHash,
26401
26373
  serverHeadHash: detected.currentServerHeadHash,
26402
26374
  serverHeadCommitId: detected.currentServerHeadCommitId,
26403
26375
  localCommitHash: detected.localCommitHash
@@ -26461,7 +26433,7 @@ async function collabStatus(params) {
26461
26433
  status.reconcile.canApply = !status.repo.branchMismatch;
26462
26434
  status.recommendedAction = "reconcile";
26463
26435
  } else if (detected.repoState === "external_local_base_changed") {
26464
- status.recommendedAction = "re_anchor";
26436
+ status.recommendedAction = "init";
26465
26437
  addBlockedReason(status.sync, "baseline_missing");
26466
26438
  addBlockedReason(status.reconcile, "baseline_missing");
26467
26439
  } else if (detected.repoState === "local_only_changed") {
@@ -26714,10 +26686,10 @@ async function processInitJob(job, api) {
26714
26686
  try {
26715
26687
  await updateAsyncJob(job.id, { status: "submitting", error: null });
26716
26688
  await logDrainerEvent(job.id, "claimed", { kind: "init" });
26717
- const bundleStat = await import_promises25.default.stat(job.payload.bundlePath);
26689
+ const bundleStat = await import_promises24.default.stat(job.payload.bundlePath);
26718
26690
  const presignResp = await api.presignImportUploadFirstParty({
26719
26691
  file: {
26720
- name: import_path14.default.basename(job.payload.bundlePath),
26692
+ name: import_path13.default.basename(job.payload.bundlePath),
26721
26693
  mimeType: "application/x-git-bundle",
26722
26694
  size: bundleStat.size,
26723
26695
  checksumSha256: job.payload.bundleSha256
@@ -26734,7 +26706,7 @@ async function processInitJob(job, api) {
26734
26706
  await updateAsyncJob(job.id, { status: "server_processing" });
26735
26707
  const importResp = await api.importFromUploadFirstParty({
26736
26708
  uploadId: String(presign.uploadId),
26737
- appName: job.payload.appName?.trim() || import_path14.default.basename(job.repoRoot),
26709
+ appName: job.payload.appName?.trim() || import_path13.default.basename(job.repoRoot),
26738
26710
  platform: "generic",
26739
26711
  isPublic: false,
26740
26712
  branch: job.payload.defaultBranch && job.branchName && job.branchName !== job.payload.defaultBranch ? job.payload.defaultBranch : job.branchName ?? void 0,
@@ -26928,7 +26900,7 @@ async function processInitPostJob(job, api) {
26928
26900
  if (outcome.status === "failed") {
26929
26901
  const bindingPath = getCollabBindingPath(job.repoRoot);
26930
26902
  try {
26931
- await import_promises25.default.unlink(bindingPath);
26903
+ await import_promises24.default.unlink(bindingPath);
26932
26904
  await logDrainerEvent(job.id, "binding_cleared", {
26933
26905
  kind: "init_post",
26934
26906
  appId: job.payload.appId,
@@ -26969,10 +26941,10 @@ async function processReAnchorJob(job, api) {
26969
26941
  }
26970
26942
  let anchoredServerHeadHash = preflight.targetHeadCommitHash;
26971
26943
  if (preflight.status === "ready_to_reconcile") {
26972
- const bundleStat = await import_promises25.default.stat(job.payload.bundlePath);
26944
+ const bundleStat = await import_promises24.default.stat(job.payload.bundlePath);
26973
26945
  const presignResp = await api.presignImportUploadFirstParty({
26974
26946
  file: {
26975
- name: import_path14.default.basename(job.payload.bundlePath),
26947
+ name: import_path13.default.basename(job.payload.bundlePath),
26976
26948
  mimeType: "application/x-git-bundle",
26977
26949
  size: bundleStat.size,
26978
26950
  checksumSha256: job.payload.bundleSha256
@@ -27076,9 +27048,9 @@ async function collabReAnchorProcess(jobId, opts) {
27076
27048
  }
27077
27049
  async function acquireDrainerPidLock() {
27078
27050
  const pidPath = getDrainerPidPath();
27079
- await import_promises25.default.mkdir(import_path14.default.dirname(pidPath), { recursive: true });
27051
+ await import_promises24.default.mkdir(import_path13.default.dirname(pidPath), { recursive: true });
27080
27052
  try {
27081
- const existing = await import_promises25.default.readFile(pidPath, "utf8").catch(() => "");
27053
+ const existing = await import_promises24.default.readFile(pidPath, "utf8").catch(() => "");
27082
27054
  const existingPid = parseInt(existing.trim(), 10);
27083
27055
  if (Number.isFinite(existingPid) && existingPid > 0 && existingPid !== process.pid) {
27084
27056
  try {
@@ -27088,13 +27060,13 @@ async function acquireDrainerPidLock() {
27088
27060
  if (error2?.code !== "ESRCH") return null;
27089
27061
  }
27090
27062
  }
27091
- await import_promises25.default.writeFile(pidPath, String(process.pid), "utf8");
27063
+ await import_promises24.default.writeFile(pidPath, String(process.pid), "utf8");
27092
27064
  return {
27093
27065
  release: async () => {
27094
27066
  try {
27095
- const current = (await import_promises25.default.readFile(pidPath, "utf8")).trim();
27067
+ const current = (await import_promises24.default.readFile(pidPath, "utf8")).trim();
27096
27068
  if (current === String(process.pid)) {
27097
- await import_promises25.default.unlink(pidPath).catch(() => void 0);
27069
+ await import_promises24.default.unlink(pidPath).catch(() => void 0);
27098
27070
  }
27099
27071
  } catch {
27100
27072
  }
@@ -27609,8 +27581,8 @@ function getErrorMap() {
27609
27581
 
27610
27582
  // node_modules/zod/v3/helpers/parseUtil.js
27611
27583
  var makeIssue = (params) => {
27612
- const { data, path: path17, errorMaps, issueData } = params;
27613
- const fullPath = [...path17, ...issueData.path || []];
27584
+ const { data, path: path16, errorMaps, issueData } = params;
27585
+ const fullPath = [...path16, ...issueData.path || []];
27614
27586
  const fullIssue = {
27615
27587
  ...issueData,
27616
27588
  path: fullPath
@@ -27726,11 +27698,11 @@ var errorUtil;
27726
27698
 
27727
27699
  // node_modules/zod/v3/types.js
27728
27700
  var ParseInputLazyPath = class {
27729
- constructor(parent, value, path17, key) {
27701
+ constructor(parent, value, path16, key) {
27730
27702
  this._cachedPath = [];
27731
27703
  this.parent = parent;
27732
27704
  this.data = value;
27733
- this._path = path17;
27705
+ this._path = path16;
27734
27706
  this._key = key;
27735
27707
  }
27736
27708
  get path() {
@@ -35222,7 +35194,7 @@ var EMPTY_COMPLETION_RESULT = {
35222
35194
  }
35223
35195
  };
35224
35196
 
35225
- // node_modules/@remixhq/core/dist/chunk-US5SM7ZC.js
35197
+ // node_modules/@remixhq/core/dist/chunk-C2FOZ3O7.js
35226
35198
  async function readJsonSafe(res) {
35227
35199
  const ct = res.headers.get("content-type") ?? "";
35228
35200
  if (!ct.toLowerCase().includes("application/json")) return null;
@@ -35235,8 +35207,13 @@ async function readJsonSafe(res) {
35235
35207
  function createApiClient(config2, opts) {
35236
35208
  const apiKey = (opts?.apiKey ?? "").trim();
35237
35209
  const tokenProvider = opts?.tokenProvider;
35210
+ const defaultTimeoutMs = typeof opts?.defaultRequestTimeoutMs === "number" && opts.defaultRequestTimeoutMs > 0 ? opts.defaultRequestTimeoutMs : null;
35238
35211
  const CLIENT_KEY_HEADER = "x-comerge-api-key";
35239
- async function request(path17, init) {
35212
+ function makeTimeoutSignal(timeoutMs) {
35213
+ const ms = typeof timeoutMs === "number" && timeoutMs > 0 ? timeoutMs : defaultTimeoutMs;
35214
+ return ms != null ? AbortSignal.timeout(ms) : void 0;
35215
+ }
35216
+ async function request(path16, init, opts2) {
35240
35217
  if (!tokenProvider) {
35241
35218
  throw new RemixError("API client is missing a token provider.", {
35242
35219
  exitCode: 1,
@@ -35244,9 +35221,10 @@ function createApiClient(config2, opts) {
35244
35221
  });
35245
35222
  }
35246
35223
  const auth = await tokenProvider();
35247
- const url = new URL(path17, config2.apiUrl).toString();
35224
+ const url = new URL(path16, config2.apiUrl).toString();
35248
35225
  const doFetch = async (bearer) => fetch(url, {
35249
35226
  ...init,
35227
+ signal: makeTimeoutSignal(opts2?.timeoutMs),
35250
35228
  headers: {
35251
35229
  Accept: "application/json",
35252
35230
  "Content-Type": "application/json",
@@ -35263,12 +35241,16 @@ function createApiClient(config2, opts) {
35263
35241
  if (!res.ok) {
35264
35242
  const body = await readJsonSafe(res);
35265
35243
  const msg = (body && typeof body === "object" && body && "message" in body && typeof body.message === "string" ? body.message : null) ?? `Request failed (status ${res.status})`;
35266
- throw new RemixError(msg, { exitCode: 1, hint: body ? JSON.stringify(body, null, 2) : null });
35244
+ throw new RemixError(msg, {
35245
+ exitCode: 1,
35246
+ hint: body ? JSON.stringify(body, null, 2) : null,
35247
+ statusCode: res.status
35248
+ });
35267
35249
  }
35268
35250
  const json = await readJsonSafe(res);
35269
35251
  return json ?? null;
35270
35252
  }
35271
- async function requestBinary(path17, init) {
35253
+ async function requestBinary(path16, init, opts2) {
35272
35254
  if (!tokenProvider) {
35273
35255
  throw new RemixError("API client is missing a token provider.", {
35274
35256
  exitCode: 1,
@@ -35276,9 +35258,10 @@ function createApiClient(config2, opts) {
35276
35258
  });
35277
35259
  }
35278
35260
  const auth = await tokenProvider();
35279
- const url = new URL(path17, config2.apiUrl).toString();
35261
+ const url = new URL(path16, config2.apiUrl).toString();
35280
35262
  const doFetch = async (bearer) => fetch(url, {
35281
35263
  ...init,
35264
+ signal: makeTimeoutSignal(opts2?.timeoutMs),
35282
35265
  headers: {
35283
35266
  Accept: "*/*",
35284
35267
  ...init?.headers ?? {},
@@ -35294,7 +35277,11 @@ function createApiClient(config2, opts) {
35294
35277
  if (!res.ok) {
35295
35278
  const body = await readJsonSafe(res);
35296
35279
  const msg = (body && typeof body === "object" && body && "message" in body && typeof body.message === "string" ? body.message : null) ?? `Request failed (status ${res.status})`;
35297
- throw new RemixError(msg, { exitCode: 1, hint: body ? JSON.stringify(body, null, 2) : null });
35280
+ throw new RemixError(msg, {
35281
+ exitCode: 1,
35282
+ hint: body ? JSON.stringify(body, null, 2) : null,
35283
+ statusCode: res.status
35284
+ });
35298
35285
  }
35299
35286
  const contentDisposition = res.headers.get("content-disposition") ?? "";
35300
35287
  const fileNameMatch = contentDisposition.match(/filename=\"([^\"]+)\"/i);
@@ -35401,6 +35388,14 @@ function createApiClient(config2, opts) {
35401
35388
  method: "POST",
35402
35389
  body: JSON.stringify(payload)
35403
35390
  }),
35391
+ listChangeSteps: (appId, params) => {
35392
+ const qs = new URLSearchParams();
35393
+ if (params?.limit !== void 0) qs.set("limit", String(params.limit));
35394
+ if (params?.offset !== void 0) qs.set("offset", String(params.offset));
35395
+ if (params?.idempotencyKey) qs.set("idempotencyKey", params.idempotencyKey);
35396
+ const suffix = qs.toString() ? `?${qs.toString()}` : "";
35397
+ return request(`/v1/apps/${encodeURIComponent(appId)}/change-steps${suffix}`, { method: "GET" });
35398
+ },
35404
35399
  createCollabTurn: (appId, payload) => request(`/v1/apps/${encodeURIComponent(appId)}/collab-turns`, {
35405
35400
  method: "POST",
35406
35401
  body: JSON.stringify(payload)
@@ -35648,10 +35643,10 @@ function createApiClient(config2, opts) {
35648
35643
  };
35649
35644
  }
35650
35645
 
35651
- // node_modules/@remixhq/core/dist/chunk-P6JHXOV4.js
35652
- var import_promises26 = __toESM(require("fs/promises"), 1);
35646
+ // node_modules/@remixhq/core/dist/chunk-XETDXVGM.js
35647
+ var import_promises25 = __toESM(require("fs/promises"), 1);
35653
35648
  var import_os7 = __toESM(require("os"), 1);
35654
- var import_path15 = __toESM(require("path"), 1);
35649
+ var import_path14 = __toESM(require("path"), 1);
35655
35650
 
35656
35651
  // node_modules/tslib/tslib.es6.mjs
35657
35652
  function __rest(s, e) {
@@ -44754,8 +44749,8 @@ var IcebergError = class extends Error {
44754
44749
  return this.status === 419;
44755
44750
  }
44756
44751
  };
44757
- function buildUrl(baseUrl, path17, query) {
44758
- const url = new URL(path17, baseUrl);
44752
+ function buildUrl(baseUrl, path16, query) {
44753
+ const url = new URL(path16, baseUrl);
44759
44754
  if (query) {
44760
44755
  for (const [key, value] of Object.entries(query)) {
44761
44756
  if (value !== void 0) {
@@ -44785,12 +44780,12 @@ function createFetchClient(options) {
44785
44780
  return {
44786
44781
  async request({
44787
44782
  method,
44788
- path: path17,
44783
+ path: path16,
44789
44784
  query,
44790
44785
  body,
44791
44786
  headers
44792
44787
  }) {
44793
- const url = buildUrl(options.baseUrl, path17, query);
44788
+ const url = buildUrl(options.baseUrl, path16, query);
44794
44789
  const authHeaders = await buildAuthHeaders(options.auth);
44795
44790
  const res = await fetchFn(url, {
44796
44791
  method,
@@ -45628,7 +45623,7 @@ var StorageFileApi = class extends BaseApiClient {
45628
45623
  * @param path The relative file path. Should be of the format `folder/subfolder/filename.png`. The bucket must already exist before attempting to upload.
45629
45624
  * @param fileBody The body of the file to be stored in the bucket.
45630
45625
  */
45631
- async uploadOrUpdate(method, path17, fileBody, fileOptions) {
45626
+ async uploadOrUpdate(method, path16, fileBody, fileOptions) {
45632
45627
  var _this = this;
45633
45628
  return _this.handleOperation(async () => {
45634
45629
  let body;
@@ -45652,7 +45647,7 @@ var StorageFileApi = class extends BaseApiClient {
45652
45647
  if ((typeof ReadableStream !== "undefined" && body instanceof ReadableStream || body && typeof body === "object" && "pipe" in body && typeof body.pipe === "function") && !options.duplex) options.duplex = "half";
45653
45648
  }
45654
45649
  if (fileOptions === null || fileOptions === void 0 ? void 0 : fileOptions.headers) for (const [key, value] of Object.entries(fileOptions.headers)) headers = setHeader(headers, key, value);
45655
- const cleanPath = _this._removeEmptyFolders(path17);
45650
+ const cleanPath = _this._removeEmptyFolders(path16);
45656
45651
  const _path = _this._getFinalPath(cleanPath);
45657
45652
  const data = await (method == "PUT" ? put : post)(_this.fetch, `${_this.url}/object/${_path}`, body, _objectSpread22({ headers }, (options === null || options === void 0 ? void 0 : options.duplex) ? { duplex: options.duplex } : {}));
45658
45653
  return {
@@ -45713,8 +45708,8 @@ var StorageFileApi = class extends BaseApiClient {
45713
45708
  * - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works
45714
45709
  * - For React Native, using either `Blob`, `File` or `FormData` does not work as intended. Upload file using `ArrayBuffer` from base64 file data instead, see example below.
45715
45710
  */
45716
- async upload(path17, fileBody, fileOptions) {
45717
- return this.uploadOrUpdate("POST", path17, fileBody, fileOptions);
45711
+ async upload(path16, fileBody, fileOptions) {
45712
+ return this.uploadOrUpdate("POST", path16, fileBody, fileOptions);
45718
45713
  }
45719
45714
  /**
45720
45715
  * Upload a file with a token generated from `createSignedUploadUrl`.
@@ -45753,9 +45748,9 @@ var StorageFileApi = class extends BaseApiClient {
45753
45748
  * - `objects` table permissions: none
45754
45749
  * - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works
45755
45750
  */
45756
- async uploadToSignedUrl(path17, token, fileBody, fileOptions) {
45751
+ async uploadToSignedUrl(path16, token, fileBody, fileOptions) {
45757
45752
  var _this3 = this;
45758
- const cleanPath = _this3._removeEmptyFolders(path17);
45753
+ const cleanPath = _this3._removeEmptyFolders(path16);
45759
45754
  const _path = _this3._getFinalPath(cleanPath);
45760
45755
  const url = new URL(_this3.url + `/object/upload/sign/${_path}`);
45761
45756
  url.searchParams.set("token", token);
@@ -45817,10 +45812,10 @@ var StorageFileApi = class extends BaseApiClient {
45817
45812
  * - `objects` table permissions: `insert`
45818
45813
  * - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works
45819
45814
  */
45820
- async createSignedUploadUrl(path17, options) {
45815
+ async createSignedUploadUrl(path16, options) {
45821
45816
  var _this4 = this;
45822
45817
  return _this4.handleOperation(async () => {
45823
- let _path = _this4._getFinalPath(path17);
45818
+ let _path = _this4._getFinalPath(path16);
45824
45819
  const headers = _objectSpread22({}, _this4.headers);
45825
45820
  if (options === null || options === void 0 ? void 0 : options.upsert) headers["x-upsert"] = "true";
45826
45821
  const data = await post(_this4.fetch, `${_this4.url}/object/upload/sign/${_path}`, {}, { headers });
@@ -45829,7 +45824,7 @@ var StorageFileApi = class extends BaseApiClient {
45829
45824
  if (!token) throw new StorageError("No token returned by API");
45830
45825
  return {
45831
45826
  signedUrl: url.toString(),
45832
- path: path17,
45827
+ path: path16,
45833
45828
  token
45834
45829
  };
45835
45830
  });
@@ -45885,8 +45880,8 @@ var StorageFileApi = class extends BaseApiClient {
45885
45880
  * - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works
45886
45881
  * - For React Native, using either `Blob`, `File` or `FormData` does not work as intended. Update file using `ArrayBuffer` from base64 file data instead, see example below.
45887
45882
  */
45888
- async update(path17, fileBody, fileOptions) {
45889
- return this.uploadOrUpdate("PUT", path17, fileBody, fileOptions);
45883
+ async update(path16, fileBody, fileOptions) {
45884
+ return this.uploadOrUpdate("PUT", path16, fileBody, fileOptions);
45890
45885
  }
45891
45886
  /**
45892
45887
  * Moves an existing file to a new path in the same bucket.
@@ -46034,10 +46029,10 @@ var StorageFileApi = class extends BaseApiClient {
46034
46029
  * - `objects` table permissions: `select`
46035
46030
  * - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works
46036
46031
  */
46037
- async createSignedUrl(path17, expiresIn, options) {
46032
+ async createSignedUrl(path16, expiresIn, options) {
46038
46033
  var _this8 = this;
46039
46034
  return _this8.handleOperation(async () => {
46040
- let _path = _this8._getFinalPath(path17);
46035
+ let _path = _this8._getFinalPath(path16);
46041
46036
  const hasTransform = typeof (options === null || options === void 0 ? void 0 : options.transform) === "object" && options.transform !== null && Object.keys(options.transform).length > 0;
46042
46037
  let data = await post(_this8.fetch, `${_this8.url}/object/sign/${_path}`, _objectSpread22({ expiresIn }, hasTransform ? { transform: options.transform } : {}), { headers: _this8.headers });
46043
46038
  const query = new URLSearchParams();
@@ -46171,13 +46166,13 @@ var StorageFileApi = class extends BaseApiClient {
46171
46166
  * - `objects` table permissions: `select`
46172
46167
  * - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works
46173
46168
  */
46174
- download(path17, options, parameters) {
46169
+ download(path16, options, parameters) {
46175
46170
  const renderPath = typeof (options === null || options === void 0 ? void 0 : options.transform) === "object" && options.transform !== null && Object.keys(options.transform).length > 0 ? "render/image/authenticated" : "object";
46176
46171
  const query = new URLSearchParams();
46177
46172
  if (options === null || options === void 0 ? void 0 : options.transform) this.applyTransformOptsToQuery(query, options.transform);
46178
46173
  if ((options === null || options === void 0 ? void 0 : options.cacheNonce) != null) query.set("cacheNonce", String(options.cacheNonce));
46179
46174
  const queryString = query.toString();
46180
- const _path = this._getFinalPath(path17);
46175
+ const _path = this._getFinalPath(path16);
46181
46176
  const downloadFn = () => get(this.fetch, `${this.url}/${renderPath}/${_path}${queryString ? `?${queryString}` : ""}`, {
46182
46177
  headers: this.headers,
46183
46178
  noResolveJson: true
@@ -46207,9 +46202,9 @@ var StorageFileApi = class extends BaseApiClient {
46207
46202
  * }
46208
46203
  * ```
46209
46204
  */
46210
- async info(path17) {
46205
+ async info(path16) {
46211
46206
  var _this10 = this;
46212
- const _path = _this10._getFinalPath(path17);
46207
+ const _path = _this10._getFinalPath(path16);
46213
46208
  return _this10.handleOperation(async () => {
46214
46209
  return recursiveToCamel(await get(_this10.fetch, `${_this10.url}/object/info/${_path}`, { headers: _this10.headers }));
46215
46210
  });
@@ -46229,9 +46224,9 @@ var StorageFileApi = class extends BaseApiClient {
46229
46224
  * .exists('folder/avatar1.png')
46230
46225
  * ```
46231
46226
  */
46232
- async exists(path17) {
46227
+ async exists(path16) {
46233
46228
  var _this11 = this;
46234
- const _path = _this11._getFinalPath(path17);
46229
+ const _path = _this11._getFinalPath(path16);
46235
46230
  try {
46236
46231
  await head(_this11.fetch, `${_this11.url}/object/${_path}`, { headers: _this11.headers });
46237
46232
  return {
@@ -46309,8 +46304,8 @@ var StorageFileApi = class extends BaseApiClient {
46309
46304
  * - `objects` table permissions: none
46310
46305
  * - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works
46311
46306
  */
46312
- getPublicUrl(path17, options) {
46313
- const _path = this._getFinalPath(path17);
46307
+ getPublicUrl(path16, options) {
46308
+ const _path = this._getFinalPath(path16);
46314
46309
  const query = new URLSearchParams();
46315
46310
  if (options === null || options === void 0 ? void 0 : options.download) query.set("download", options.download === true ? "" : options.download);
46316
46311
  if (options === null || options === void 0 ? void 0 : options.transform) this.applyTransformOptsToQuery(query, options.transform);
@@ -46447,10 +46442,10 @@ var StorageFileApi = class extends BaseApiClient {
46447
46442
  * - `objects` table permissions: `select`
46448
46443
  * - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works
46449
46444
  */
46450
- async list(path17, options, parameters) {
46445
+ async list(path16, options, parameters) {
46451
46446
  var _this13 = this;
46452
46447
  return _this13.handleOperation(async () => {
46453
- const body = _objectSpread22(_objectSpread22(_objectSpread22({}, DEFAULT_SEARCH_OPTIONS), options), {}, { prefix: path17 || "" });
46448
+ const body = _objectSpread22(_objectSpread22(_objectSpread22({}, DEFAULT_SEARCH_OPTIONS), options), {}, { prefix: path16 || "" });
46454
46449
  return await post(_this13.fetch, `${_this13.url}/object/list/${_this13.bucketId}`, body, { headers: _this13.headers }, parameters);
46455
46450
  });
46456
46451
  }
@@ -46514,11 +46509,11 @@ var StorageFileApi = class extends BaseApiClient {
46514
46509
  if (typeof Buffer !== "undefined") return Buffer.from(data).toString("base64");
46515
46510
  return btoa(data);
46516
46511
  }
46517
- _getFinalPath(path17) {
46518
- return `${this.bucketId}/${path17.replace(/^\/+/, "")}`;
46512
+ _getFinalPath(path16) {
46513
+ return `${this.bucketId}/${path16.replace(/^\/+/, "")}`;
46519
46514
  }
46520
- _removeEmptyFolders(path17) {
46521
- return path17.replace(/^\/|\/$/g, "").replace(/\/+/g, "/");
46515
+ _removeEmptyFolders(path16) {
46516
+ return path16.replace(/^\/|\/$/g, "").replace(/\/+/g, "/");
46522
46517
  }
46523
46518
  /** Modifies the `query`, appending values the from `transform` */
46524
46519
  applyTransformOptsToQuery(query, transform2) {
@@ -55593,7 +55588,7 @@ function shouldShowDeprecationWarning() {
55593
55588
  }
55594
55589
  if (shouldShowDeprecationWarning()) console.warn("\u26A0\uFE0F Node.js 18 and below are deprecated and will no longer be supported in future versions of @supabase/supabase-js. Please upgrade to Node.js 20 or later. For more information, visit: https://github.com/orgs/supabase/discussions/37217");
55595
55590
 
55596
- // node_modules/@remixhq/core/dist/chunk-P6JHXOV4.js
55591
+ // node_modules/@remixhq/core/dist/chunk-XETDXVGM.js
55597
55592
  var storedSessionSchema = external_exports.object({
55598
55593
  access_token: external_exports.string().min(1),
55599
55594
  refresh_token: external_exports.string().min(1),
@@ -55607,7 +55602,7 @@ var storedSessionSchema = external_exports.object({
55607
55602
  function xdgConfigHome() {
55608
55603
  const value = process.env.XDG_CONFIG_HOME;
55609
55604
  if (typeof value === "string" && value.trim()) return value;
55610
- return import_path15.default.join(import_os7.default.homedir(), ".config");
55605
+ return import_path14.default.join(import_os7.default.homedir(), ".config");
55611
55606
  }
55612
55607
  async function maybeLoadKeytar() {
55613
55608
  try {
@@ -55625,22 +55620,22 @@ async function maybeLoadKeytar() {
55625
55620
  return null;
55626
55621
  }
55627
55622
  async function ensurePathPermissions(filePath) {
55628
- const dir = import_path15.default.dirname(filePath);
55629
- await import_promises26.default.mkdir(dir, { recursive: true });
55623
+ const dir = import_path14.default.dirname(filePath);
55624
+ await import_promises25.default.mkdir(dir, { recursive: true });
55630
55625
  try {
55631
- await import_promises26.default.chmod(dir, 448);
55626
+ await import_promises25.default.chmod(dir, 448);
55632
55627
  } catch {
55633
55628
  }
55634
55629
  try {
55635
- await import_promises26.default.chmod(filePath, 384);
55630
+ await import_promises25.default.chmod(filePath, 384);
55636
55631
  } catch {
55637
55632
  }
55638
55633
  }
55639
55634
  async function writeJsonAtomic2(filePath, value) {
55640
- await import_promises26.default.mkdir(import_path15.default.dirname(filePath), { recursive: true });
55635
+ await import_promises25.default.mkdir(import_path14.default.dirname(filePath), { recursive: true });
55641
55636
  const tmpPath = `${filePath}.tmp-${Date.now()}-${Math.random().toString(16).slice(2)}`;
55642
- await import_promises26.default.writeFile(tmpPath, JSON.stringify(value, null, 2) + "\n", "utf8");
55643
- await import_promises26.default.rename(tmpPath, filePath);
55637
+ await import_promises25.default.writeFile(tmpPath, JSON.stringify(value, null, 2) + "\n", "utf8");
55638
+ await import_promises25.default.rename(tmpPath, filePath);
55644
55639
  }
55645
55640
  async function writeSessionFileFallback(filePath, session) {
55646
55641
  await writeJsonAtomic2(filePath, session);
@@ -55649,7 +55644,7 @@ async function writeSessionFileFallback(filePath, session) {
55649
55644
  function createLocalSessionStore(params) {
55650
55645
  const service = params?.service?.trim() || "remix-cli";
55651
55646
  const account = params?.account?.trim() || "default";
55652
- const filePath = params?.filePath?.trim() || import_path15.default.join(xdgConfigHome(), "remix", "session.json");
55647
+ const filePath = params?.filePath?.trim() || import_path14.default.join(xdgConfigHome(), "remix", "session.json");
55653
55648
  async function readKeytar() {
55654
55649
  const keytar = await maybeLoadKeytar();
55655
55650
  if (!keytar) return null;
@@ -55663,7 +55658,7 @@ function createLocalSessionStore(params) {
55663
55658
  }
55664
55659
  }
55665
55660
  async function readFile() {
55666
- const raw = await import_promises26.default.readFile(filePath, "utf8").catch(() => null);
55661
+ const raw = await import_promises25.default.readFile(filePath, "utf8").catch(() => null);
55667
55662
  if (!raw) return null;
55668
55663
  try {
55669
55664
  const parsed = storedSessionSchema.safeParse(JSON.parse(raw));
@@ -55807,7 +55802,7 @@ function createSupabaseAuthHelpers(config2) {
55807
55802
  };
55808
55803
  }
55809
55804
 
55810
- // node_modules/@remixhq/core/dist/chunk-VM3CGCNX.js
55805
+ // node_modules/@remixhq/core/dist/chunk-XCZRNB35.js
55811
55806
  var DEFAULT_API_URL = "https://api.remix.one";
55812
55807
  var DEFAULT_SUPABASE_URL = "https://xtfxwbckjpfmqubnsusu.supabase.co";
55813
55808
  var DEFAULT_SUPABASE_ANON_KEY = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inh0Znh3YmNranBmbXF1Ym5zdXN1Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3NjA2MDEyMzAsImV4cCI6MjA3NjE3NzIzMH0.dzWGAWrK4CvrmHVHzf8w7JlUZohdap0ZPnLZnABMV8s";
@@ -55845,10 +55840,10 @@ async function resolveConfig(_opts) {
55845
55840
  }
55846
55841
 
55847
55842
  // node_modules/@remixhq/mcp/dist/index.js
55848
- var import_path16 = __toESM(require("path"), 1);
55843
+ var import_path15 = __toESM(require("path"), 1);
55849
55844
  var import_child_process = require("child_process");
55850
55845
  var import_fs3 = require("fs");
55851
- var import_path17 = __toESM(require("path"), 1);
55846
+ var import_path16 = __toESM(require("path"), 1);
55852
55847
  async function createRemixTokenProvider(config2) {
55853
55848
  const resolvedConfig = config2 ?? await resolveConfig();
55854
55849
  const sessionStore = createLocalSessionStore();
@@ -56077,6 +56072,15 @@ function normalizeByMessage(err) {
56077
56072
  category: "remote_state"
56078
56073
  });
56079
56074
  }
56075
+ if (statusCode === 409) {
56076
+ return makeNormalized({
56077
+ code: ERROR_CODES.REMOTE_ERROR,
56078
+ message,
56079
+ hint,
56080
+ retryable: true,
56081
+ category: "remote_state"
56082
+ });
56083
+ }
56080
56084
  if (message.includes("Timed out") || message.includes("failed") || message.includes("error state")) {
56081
56085
  return makeNormalized({
56082
56086
  code: ERROR_CODES.REMOTE_ERROR,
@@ -56141,12 +56145,12 @@ function parsePositiveIntEnv(name, fallback) {
56141
56145
  }
56142
56146
  function parseAllowedRoots(raw) {
56143
56147
  if (!raw) return null;
56144
- const roots = raw.split(import_path16.default.delimiter).map((entry) => entry.trim()).filter(Boolean).map((entry) => import_path16.default.resolve(entry));
56148
+ const roots = raw.split(import_path15.default.delimiter).map((entry) => entry.trim()).filter(Boolean).map((entry) => import_path15.default.resolve(entry));
56145
56149
  return roots.length > 0 ? roots : null;
56146
56150
  }
56147
56151
  function isWithinRoot(root, candidate) {
56148
- const relative = import_path16.default.relative(root, candidate);
56149
- return relative === "" || !relative.startsWith("..") && !import_path16.default.isAbsolute(relative);
56152
+ const relative = import_path15.default.relative(root, candidate);
56153
+ return relative === "" || !relative.startsWith("..") && !import_path15.default.isAbsolute(relative);
56150
56154
  }
56151
56155
  function loadPolicy() {
56152
56156
  return {
@@ -56158,7 +56162,7 @@ function loadPolicy() {
56158
56162
  };
56159
56163
  }
56160
56164
  function resolvePolicyCwd(policy, cwd) {
56161
- const resolved = import_path16.default.resolve(cwd?.trim() || process.cwd());
56165
+ const resolved = import_path15.default.resolve(cwd?.trim() || process.cwd());
56162
56166
  if (!policy.allowedRepoRoots) return resolved;
56163
56167
  if (policy.allowedRepoRoots.some((root) => isWithinRoot(root, resolved))) return resolved;
56164
56168
  throw createPolicyError("Requested working directory is outside the allowed repository roots.", resolved);
@@ -56240,7 +56244,6 @@ function makeSuccessResult2(envelope) {
56240
56244
  function makeErrorResult(envelope) {
56241
56245
  return {
56242
56246
  content: [{ type: "text", text: toJsonText(envelope) }],
56243
- structuredContent: envelope,
56244
56247
  isError: true
56245
56248
  };
56246
56249
  }
@@ -56317,9 +56320,6 @@ var applyInputSchema = {
56317
56320
  confirm: external_exports.boolean(),
56318
56321
  allowBranchMismatch: external_exports.boolean().optional()
56319
56322
  };
56320
- var reAnchorInputSchema = {
56321
- ...applyInputSchema
56322
- };
56323
56323
  var requestMergeInputSchema = {
56324
56324
  ...commonRequestFieldsSchema
56325
56325
  };
@@ -56432,7 +56432,7 @@ var initSyncDataSchema = external_exports.object({
56432
56432
  repoRoot: external_exports.string(),
56433
56433
  bindingMode: external_exports.enum(["legacy", "lane", "explicit_root"]).optional(),
56434
56434
  createdCanonicalFamily: external_exports.boolean().optional(),
56435
- baselineStatus: external_exports.enum(["seeded", "existing", "requires_re_anchor", "requires_sync"]).optional()
56435
+ baselineStatus: external_exports.enum(["seeded", "existing", "baseline_missing", "requires_sync"]).optional()
56436
56436
  });
56437
56437
  var initQueuedDataSchema = external_exports.object({
56438
56438
  queued: external_exports.literal(true),
@@ -56480,7 +56480,6 @@ var drainFinalizeQueueDataSchema = external_exports.object({
56480
56480
  results: external_exports.array(genericRecordSchema)
56481
56481
  });
56482
56482
  var syncDataSchema = genericRecordSchema;
56483
- var reAnchorDataSchema = genericRecordSchema;
56484
56483
  var requestMergeDataSchema = genericRecordSchema;
56485
56484
  var mergeRequestQueueDataSchema = external_exports.object({
56486
56485
  queue: mergeRequestQueueSchema,
@@ -56556,7 +56555,6 @@ var addSuccessSchema = makeSuccessSchema(addDataSchema);
56556
56555
  var recordTurnSuccessSchema = makeSuccessSchema(recordTurnDataSchema);
56557
56556
  var drainFinalizeQueueSuccessSchema = makeSuccessSchema(drainFinalizeQueueDataSchema);
56558
56557
  var syncSuccessSchema = makeSuccessSchema(syncDataSchema);
56559
- var reAnchorSuccessSchema = makeSuccessSchema(reAnchorDataSchema);
56560
56558
  var requestMergeSuccessSchema = makeSuccessSchema(requestMergeDataSchema);
56561
56559
  var mergeRequestQueueSuccessSchema = makeSuccessSchema(mergeRequestQueueDataSchema);
56562
56560
  var viewMergeRequestSuccessSchema = makeSuccessSchema(viewMergeRequestDataSchema);
@@ -56575,7 +56573,7 @@ var updateMemberRoleSuccessSchema = makeSuccessSchema(updateMemberRoleDataSchema
56575
56573
  function getRiskLevel(status) {
56576
56574
  if (status.recommendedAction === "reconcile") return "high";
56577
56575
  if (status.recommendedAction === "choose_family" || status.recommendedAction === "await_finalize") return "medium";
56578
- if (status.recommendedAction === "pull" || status.recommendedAction === "re_anchor" || status.remote.incomingOpenMergeRequestCount) {
56576
+ if (status.recommendedAction === "pull" || status.remote.incomingOpenMergeRequestCount) {
56579
56577
  return "medium";
56580
56578
  }
56581
56579
  if (status.repo.branchMismatch || !status.repo.isGitRepo || !status.binding.isBound || !status.repo.worktree.isClean) return "medium";
@@ -56592,10 +56590,6 @@ function getRecommendedNextActions(status) {
56592
56590
  return ["Run remix_collab_init to bind the repository to Remix before using any Remix collaboration mutation flow."];
56593
56591
  case "pull":
56594
56592
  return ["Run remix_collab_sync_preview, then remix_collab_sync_apply if the preview is acceptable. This pulls the server delta into the local working tree without rewriting local git history."];
56595
- case "re_anchor":
56596
- return [
56597
- "Run remix_collab_re_anchor_preview, then remix_collab_re_anchor_apply. This seeds a local Remix baseline. It is required because no local baseline exists for this lane yet (fresh clone, deleted .remix/ state, or first init didn't seed) \u2014 not because of any specific git operation. After it succeeds, automatic hook recording can capture completed turns."
56598
- ];
56599
56593
  case "record":
56600
56594
  return [
56601
56595
  "No MCP recording tool is required. Automatic hook finalization will capture the local boundary at the end of the completed turn; this covers agent edits, manual user edits, git commit, git pull, git merge, git rebase, and git reset."
@@ -56680,8 +56674,8 @@ async function initCollab(params) {
56680
56674
  return {
56681
56675
  data: syncResult,
56682
56676
  warnings: collectResultWarnings(result),
56683
- recommendedNextActions: syncResult.baselineStatus === "requires_re_anchor" ? [
56684
- "This checkout has no local Remix baseline yet. Run remix_collab_re_anchor_preview, then remix_collab_re_anchor_apply to seed one. After it succeeds, automatic hook recording can capture completed turns."
56677
+ recommendedNextActions: syncResult.baselineStatus === "baseline_missing" ? [
56678
+ "This checkout has no local Remix revision baseline yet. Run remix_collab_init or remix_collab_sync_preview/apply to seed one. After it succeeds, automatic hook recording can capture completed turns."
56685
56679
  ] : syncResult.baselineStatus === "requires_sync" ? [
56686
56680
  "Run remix_collab_sync_preview, then remix_collab_sync_apply to pull the server delta and create the first local baseline for this checkout."
56687
56681
  ] : ["Run remix_collab_status to inspect sync, reconcile, and merge-request readiness before mutating bound-repo state."],
@@ -56784,26 +56778,6 @@ async function syncCollab(params) {
56784
56778
  }
56785
56779
  };
56786
56780
  }
56787
- async function reAnchor(params) {
56788
- const api = await createCollabApiClient();
56789
- const result = await collabReAnchor({
56790
- api,
56791
- cwd: params.cwd,
56792
- dryRun: params.dryRun,
56793
- allowBranchMismatch: params.allowBranchMismatch ?? false
56794
- });
56795
- return {
56796
- data: result,
56797
- warnings: collectWarnings(result.warnings),
56798
- recommendedNextActions: params.dryRun ? [
56799
- "Run remix_collab_re_anchor_apply with confirm=true to seed a local Remix baseline for this checkout. Re-anchor is for missing-baseline cases only and does not replace automatic hook recording for ordinary local content changes."
56800
- ] : [],
56801
- logContext: {
56802
- repoRoot: result.repoRoot,
56803
- appId: result.currentAppId
56804
- }
56805
- };
56806
- }
56807
56781
  async function requestMerge(params) {
56808
56782
  const api = await createCollabApiClient();
56809
56783
  const drainWarnings = await drainBeforeMutation(api);
@@ -57520,22 +57494,25 @@ async function accessDebug(params) {
57520
57494
  }
57521
57495
  };
57522
57496
  }
57523
- var MARKER_REL_PATH = import_path17.default.join(".remix", ".history-imported");
57524
- var LOG_REL_PATH = import_path17.default.join(".remix", "history-import.log");
57497
+ var MARKER_REL_PATH = import_path16.default.join(".remix", ".history-imported");
57498
+ var LOG_REL_PATH = import_path16.default.join(".remix", "history-import.log");
57525
57499
  function shouldAutoSpawnHistoryImport(repoRoot) {
57526
57500
  try {
57527
- return !(0, import_fs3.existsSync)(import_path17.default.join(repoRoot, MARKER_REL_PATH));
57501
+ return !(0, import_fs3.existsSync)(import_path16.default.join(repoRoot, MARKER_REL_PATH));
57528
57502
  } catch {
57529
57503
  return false;
57530
57504
  }
57531
57505
  }
57532
- function spawnHistoryImportDetached(repoRoot) {
57533
- const remixDir = import_path17.default.join(repoRoot, ".remix");
57506
+ function isAutoSpawnEligibleBindingMode(bindingMode) {
57507
+ return bindingMode === "explicit_root";
57508
+ }
57509
+ function spawnHistoryImportDetached(repoRoot, options) {
57510
+ const remixDir = import_path16.default.join(repoRoot, ".remix");
57534
57511
  try {
57535
57512
  (0, import_fs3.mkdirSync)(remixDir, { recursive: true });
57536
57513
  } catch {
57537
57514
  }
57538
- const logPath = import_path17.default.join(repoRoot, LOG_REL_PATH);
57515
+ const logPath = import_path16.default.join(repoRoot, LOG_REL_PATH);
57539
57516
  const out = (0, import_fs3.openSync)(logPath, "a");
57540
57517
  const err = (0, import_fs3.openSync)(logPath, "a");
57541
57518
  const child = (0, import_child_process.spawn)(
@@ -57545,6 +57522,8 @@ function spawnHistoryImportDetached(repoRoot) {
57545
57522
  "import",
57546
57523
  "--repo",
57547
57524
  repoRoot,
57525
+ "--before",
57526
+ options.cutoffAt,
57548
57527
  // Include prompt text for parity with the CLI auto-spawn path:
57549
57528
  // first-time UX is a lot worse if the dashboard renders every
57550
57529
  // historical row as "(prompt not uploaded)".
@@ -57612,6 +57591,24 @@ function buildErrorEnvelope(tool, requestId, error2) {
57612
57591
  recommendedNextActions
57613
57592
  };
57614
57593
  }
57594
+ function buildOutputValidationErrorEnvelope(tool, requestId, error2) {
57595
+ return {
57596
+ schemaVersion: SCHEMA_VERSION,
57597
+ ok: false,
57598
+ tool,
57599
+ requestId: requestId ?? null,
57600
+ error: {
57601
+ code: ERROR_CODES.INTERNAL_ERROR,
57602
+ message: "Tool output failed validation against its declared MCP schema.",
57603
+ hint: error2.issues.map((issue2) => `${issue2.path.join(".") || "output"}: ${issue2.message}`).join("; "),
57604
+ retryable: false,
57605
+ category: "internal"
57606
+ },
57607
+ warnings: [],
57608
+ risks: ["The MCP server returned an output shape that did not match the tool descriptor."],
57609
+ recommendedNextActions: ["Report this MCP schema mismatch with the tool name and error hint."]
57610
+ };
57611
+ }
57615
57612
  function registerTool(server, context, params) {
57616
57613
  const errorSchema = makeErrorSchema();
57617
57614
  server.registerTool(
@@ -57630,7 +57627,20 @@ function registerTool(server, context, params) {
57630
57627
  assertToolAccess(context.policy, params.access);
57631
57628
  const result = await params.run(rawArgs);
57632
57629
  const envelope = buildSuccessEnvelope(params.name, requestId, result);
57633
- params.outputSchema.parse(envelope);
57630
+ const parsedEnvelope = params.outputSchema.safeParse(envelope);
57631
+ if (!parsedEnvelope.success) {
57632
+ const errorEnvelope = buildOutputValidationErrorEnvelope(params.name, requestId, parsedEnvelope.error);
57633
+ context.logger.log({
57634
+ level: "error",
57635
+ message: "tool_output_validation_failed",
57636
+ tool: params.name,
57637
+ requestId: errorEnvelope.requestId,
57638
+ durationMs: Date.now() - startedAt,
57639
+ result: "error",
57640
+ errorCode: errorEnvelope.error.code
57641
+ });
57642
+ return makeErrorResult(errorEnvelope);
57643
+ }
57634
57644
  context.logger.log({
57635
57645
  level: "info",
57636
57646
  message: "tool_completed",
@@ -57693,11 +57703,13 @@ function registerCollabTools(server, context) {
57693
57703
  });
57694
57704
  try {
57695
57705
  const repoRoot = result && typeof result === "object" && "data" in result && result.data && typeof result.data.repoRoot === "string" ? result.data.repoRoot : null;
57696
- if (repoRoot && shouldAutoSpawnHistoryImport(repoRoot)) {
57697
- const spawned = spawnHistoryImportDetached(repoRoot);
57706
+ const bindingMode = result && typeof result === "object" && "data" in result && result.data && typeof result.data.bindingMode === "string" ? result.data.bindingMode : null;
57707
+ if (repoRoot && isAutoSpawnEligibleBindingMode(bindingMode) && shouldAutoSpawnHistoryImport(repoRoot)) {
57708
+ const cutoffAt = (/* @__PURE__ */ new Date()).toISOString();
57709
+ const spawned = spawnHistoryImportDetached(repoRoot, { cutoffAt });
57698
57710
  context.logger.log({
57699
57711
  level: "info",
57700
- message: `history_import_auto_spawned pid=${spawned.pid ?? "?"} log=${spawned.logPath}`,
57712
+ message: `history_import_auto_spawned pid=${spawned.pid ?? "?"} log=${spawned.logPath} cutoffAt=${cutoffAt}`,
57701
57713
  tool: "remix_collab_init",
57702
57714
  repoRoot
57703
57715
  });
@@ -57801,31 +57813,6 @@ function registerCollabTools(server, context) {
57801
57813
  return syncCollab({ cwd, dryRun: false, allowBranchMismatch: input.allowBranchMismatch ?? false });
57802
57814
  }
57803
57815
  });
57804
- registerTool(server, context, {
57805
- name: "remix_collab_re_anchor_preview",
57806
- description: "Preview whether this checkout needs a fresh local Remix baseline. Use only when status reports `re_anchor` (no local baseline exists for this lane yet \u2014 fresh clone, deleted `.remix/` state, or first init didn't seed). Re-anchor does not replace automatic hook recording; ordinary local content changes (including merges, pulls, and rebases) are captured at the completed-turn boundary, not by re-anchor.",
57807
- access: "read",
57808
- inputSchema: previewInputSchema,
57809
- outputSchema: reAnchorSuccessSchema,
57810
- run: async (args) => {
57811
- const input = external_exports.object(previewInputSchema).parse(args);
57812
- const cwd = resolvePolicyCwd(context.policy, input.cwd);
57813
- return reAnchor({ cwd, dryRun: true });
57814
- }
57815
- });
57816
- registerTool(server, context, {
57817
- name: "remix_collab_re_anchor_apply",
57818
- description: "Establish a local Remix baseline for the current checkout against the existing app head, without rewriting the local checkout afterward. Required only when status reports `re_anchor` (missing local baseline). It does not replace automatic hook recording \u2014 local commits, pulls, merges, and rebases are still captured at the completed-turn boundary.",
57819
- access: "local_write",
57820
- inputSchema: reAnchorInputSchema,
57821
- outputSchema: reAnchorSuccessSchema,
57822
- run: async (args) => {
57823
- const input = external_exports.object(reAnchorInputSchema).parse(args);
57824
- assertConfirm(input.confirm, "remix_collab_re_anchor_apply");
57825
- const cwd = resolvePolicyCwd(context.policy, input.cwd);
57826
- return reAnchor({ cwd, dryRun: false, allowBranchMismatch: input.allowBranchMismatch ?? false });
57827
- }
57828
- });
57829
57816
  registerTool(server, context, {
57830
57817
  name: "remix_collab_request_merge",
57831
57818
  description: "Open a prompt-backed Remix merge request from the current bound repository to its upstream app instead of merging locally with raw git.",
@@ -59590,6 +59577,7 @@ function createRemixMcpServer(params) {
59590
59577
  }
59591
59578
 
59592
59579
  // src/hook-auth.ts
59580
+ var HOOK_API_REQUEST_TIMEOUT_MS = 6e4;
59593
59581
  async function createHookCollabApiClient() {
59594
59582
  const config2 = await resolveConfig();
59595
59583
  const sessionStore = createLocalSessionStore();
@@ -59602,18 +59590,19 @@ async function createHookCollabApiClient() {
59602
59590
  }
59603
59591
  });
59604
59592
  return createApiClient(config2, {
59605
- tokenProvider
59593
+ tokenProvider,
59594
+ defaultRequestTimeoutMs: HOOK_API_REQUEST_TIMEOUT_MS
59606
59595
  });
59607
59596
  }
59608
59597
 
59609
59598
  // src/hook-diagnostics.ts
59610
59599
  var import_node_crypto2 = require("crypto");
59611
- var import_promises28 = __toESM(require("fs/promises"), 1);
59600
+ var import_promises27 = __toESM(require("fs/promises"), 1);
59612
59601
  var import_node_os5 = __toESM(require("os"), 1);
59613
59602
  var import_node_path7 = __toESM(require("path"), 1);
59614
59603
 
59615
59604
  // src/hook-state.ts
59616
- var import_promises27 = __toESM(require("fs/promises"), 1);
59605
+ var import_promises26 = __toESM(require("fs/promises"), 1);
59617
59606
  var import_node_os4 = __toESM(require("os"), 1);
59618
59607
  var import_node_path6 = __toESM(require("path"), 1);
59619
59608
  var import_node_crypto = require("crypto");
@@ -59718,7 +59707,7 @@ function getPendingTurnStateRootPath() {
59718
59707
  return stateRoot();
59719
59708
  }
59720
59709
  async function loadPendingTurnState(sessionId) {
59721
- const raw = await import_promises27.default.readFile(statePath(sessionId), "utf8").catch(() => null);
59710
+ const raw = await import_promises26.default.readFile(statePath(sessionId), "utf8").catch(() => null);
59722
59711
  if (!raw) return null;
59723
59712
  try {
59724
59713
  const parsed = JSON.parse(raw);
@@ -59745,7 +59734,7 @@ async function loadPendingTurnState(sessionId) {
59745
59734
  }
59746
59735
  async function listPendingTurnStateSummaries() {
59747
59736
  const root = stateRoot();
59748
- const entries = await import_promises27.default.readdir(root, { withFileTypes: true }).catch(() => []);
59737
+ const entries = await import_promises26.default.readdir(root, { withFileTypes: true }).catch(() => []);
59749
59738
  const sessionIds = entries.filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map((entry) => entry.name.replace(/\.json$/, "")).sort((a2, b) => a2.localeCompare(b));
59750
59739
  const states = await Promise.all(sessionIds.map((sessionId) => loadPendingTurnState(sessionId)));
59751
59740
  return states.filter((state) => state !== null).sort((a2, b) => b.submittedAt.localeCompare(a2.submittedAt)).map((state) => summarizePendingTurnState(state));
@@ -59754,7 +59743,7 @@ async function listPendingTurnStateSummaries() {
59754
59743
  // package.json
59755
59744
  var package_default = {
59756
59745
  name: "@remixhq/claude-plugin",
59757
- version: "0.1.22",
59746
+ version: "0.1.24",
59758
59747
  description: "Claude Code plugin for Remix collaboration workflows",
59759
59748
  homepage: "https://github.com/RemixDotOne/remix-claude-plugin",
59760
59749
  license: "MIT",
@@ -59792,8 +59781,8 @@ var package_default = {
59792
59781
  prepack: "npm run build"
59793
59782
  },
59794
59783
  dependencies: {
59795
- "@remixhq/core": "^0.1.17",
59796
- "@remixhq/mcp": "^0.1.17"
59784
+ "@remixhq/core": "^0.1.19",
59785
+ "@remixhq/mcp": "^0.1.19"
59797
59786
  },
59798
59787
  devDependencies: {
59799
59788
  "@types/node": "^25.4.0",
@@ -59835,7 +59824,7 @@ function clampEventLimit(limit) {
59835
59824
  return Math.max(1, Math.min(MAX_EVENT_LIMIT, Math.trunc(limit)));
59836
59825
  }
59837
59826
  async function readEventsFromFile(filePath) {
59838
- const raw = await import_promises28.default.readFile(filePath, "utf8").catch(() => null);
59827
+ const raw = await import_promises27.default.readFile(filePath, "utf8").catch(() => null);
59839
59828
  if (!raw) return [];
59840
59829
  return raw.split("\n").map((line) => line.trim()).filter(Boolean).flatMap((line) => {
59841
59830
  try {