@posthog/agent 2.3.510 → 2.3.513

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.
@@ -5955,6 +5955,7 @@ async function getGitBusyState(git) {
5955
5955
  }
5956
5956
  return { busy: false };
5957
5957
  }
5958
+ var MAX_WORKTREE_FILE_BYTES = 1024 * 1024;
5958
5959
  async function createWorktreeTree(git, baseDir, head) {
5959
5960
  const { tempGit, tempIndexPath } = await createTempIndexGit(git, baseDir, "checkpoint-worktree");
5960
5961
  try {
@@ -5964,6 +5965,7 @@ async function createWorktreeTree(git, baseDir, head) {
5964
5965
  await tempGit.raw(["read-tree", "--empty"]);
5965
5966
  }
5966
5967
  await tempGit.raw(["add", "-A", "--", "."]);
5968
+ await reconcileLargeBlobs(tempGit, head, MAX_WORKTREE_FILE_BYTES);
5967
5969
  const treeHash = await tempGit.raw(["write-tree"]);
5968
5970
  return treeHash.trim();
5969
5971
  } finally {
@@ -5971,6 +5973,77 @@ async function createWorktreeTree(git, baseDir, head) {
5971
5973
  });
5972
5974
  }
5973
5975
  }
5976
+ async function reconcileLargeBlobs(tempGit, head, maxBytes) {
5977
+ const intermediateTree = (await tempGit.raw(["write-tree"])).trim();
5978
+ const largePaths = await listLargeBlobPaths(tempGit, intermediateTree, maxBytes);
5979
+ if (largePaths.length === 0)
5980
+ return;
5981
+ const headEntries = head ? await readHeadBlobEntries(tempGit, head, largePaths) : /* @__PURE__ */ new Map();
5982
+ for (const filePath of largePaths) {
5983
+ const headEntry = headEntries.get(filePath);
5984
+ if (headEntry) {
5985
+ await tempGit.raw([
5986
+ "update-index",
5987
+ "--cacheinfo",
5988
+ `${headEntry.mode},${headEntry.hash},${filePath}`
5989
+ ]);
5990
+ } else {
5991
+ await tempGit.raw(["update-index", "--force-remove", filePath]).catch(() => {
5992
+ });
5993
+ }
5994
+ }
5995
+ }
5996
+ async function listLargeBlobPaths(tempGit, tree, maxBytes) {
5997
+ const output = await tempGit.raw(["ls-tree", "-r", "-l", tree]);
5998
+ const result = [];
5999
+ for (const line of output.split("\n")) {
6000
+ if (!line)
6001
+ continue;
6002
+ const tabIndex = line.indexOf(" ");
6003
+ if (tabIndex < 0)
6004
+ continue;
6005
+ const meta = line.slice(0, tabIndex);
6006
+ const filePath = line.slice(tabIndex + 1);
6007
+ const parts = meta.split(/\s+/);
6008
+ if (parts.length < 4)
6009
+ continue;
6010
+ const [, type, , sizeStr] = parts;
6011
+ if (type !== "blob")
6012
+ continue;
6013
+ if (sizeStr === "-")
6014
+ continue;
6015
+ const size = Number.parseInt(sizeStr, 10);
6016
+ if (Number.isFinite(size) && size > maxBytes) {
6017
+ result.push(filePath);
6018
+ }
6019
+ }
6020
+ return result;
6021
+ }
6022
+ async function readHeadBlobEntries(tempGit, head, paths) {
6023
+ const result = /* @__PURE__ */ new Map();
6024
+ const CHUNK_SIZE = 100;
6025
+ for (let i = 0; i < paths.length; i += CHUNK_SIZE) {
6026
+ const chunk = paths.slice(i, i + CHUNK_SIZE);
6027
+ const output = await tempGit.raw(["ls-tree", "-r", head, "--", ...chunk]).catch(() => "");
6028
+ for (const line of output.split("\n")) {
6029
+ if (!line)
6030
+ continue;
6031
+ const tabIndex = line.indexOf(" ");
6032
+ if (tabIndex < 0)
6033
+ continue;
6034
+ const meta = line.slice(0, tabIndex);
6035
+ const filePath = line.slice(tabIndex + 1);
6036
+ const parts = meta.split(/\s+/);
6037
+ if (parts.length < 3)
6038
+ continue;
6039
+ const [mode, type, hash] = parts;
6040
+ if (type !== "blob")
6041
+ continue;
6042
+ result.set(filePath, { mode, hash });
6043
+ }
6044
+ }
6045
+ return result;
6046
+ }
5974
6047
  async function createMetaTree(git, baseDir, indexTree, worktreeTree) {
5975
6048
  const { tempGit, tempIndexPath } = await createTempIndexGit(git, baseDir, "checkpoint-meta");
5976
6049
  try {
@@ -6043,6 +6116,7 @@ async function deleteCheckpoint(git, checkpointId) {
6043
6116
  // ../git/dist/handoff.js
6044
6117
  var HANDOFF_HEAD_REF_PREFIX = "refs/posthog-code-handoff/head/";
6045
6118
  var CHECKPOINT_REF_PREFIX2 = "refs/posthog-code-checkpoint/";
6119
+ var MAX_HANDOFF_FILE_BYTES = 1024 * 1024;
6046
6120
  var GitHandoffTracker = class {
6047
6121
  repositoryPath;
6048
6122
  logger;
@@ -6050,7 +6124,7 @@ var GitHandoffTracker = class {
6050
6124
  this.repositoryPath = config.repositoryPath;
6051
6125
  this.logger = config.logger;
6052
6126
  }
6053
- async captureForHandoff(_localGitState) {
6127
+ async captureForHandoff(localGitState) {
6054
6128
  const captureSaga = new CaptureCheckpointSaga(this.logger);
6055
6129
  const result = await captureSaga.run({ baseDir: this.repositoryPath });
6056
6130
  if (!result.success) {
@@ -6060,17 +6134,20 @@ var GitHandoffTracker = class {
6060
6134
  const git = createGitClient(this.repositoryPath);
6061
6135
  const tempDir = await this.createTempDir(checkpoint.checkpointId);
6062
6136
  const checkpointRef = `${CHECKPOINT_REF_PREFIX2}${checkpoint.checkpointId}`;
6063
- const packRefs = [
6064
- checkpoint.head,
6065
- checkpoint.indexTree,
6066
- checkpoint.worktreeTree
6067
- ].filter((ref) => !!ref);
6068
- const headRef = checkpoint.head ? `${HANDOFF_HEAD_REF_PREFIX}${checkpoint.checkpointId}` : void 0;
6069
- const packPrefix = path3.join(tempDir, checkpoint.checkpointId);
6070
6137
  try {
6138
+ const reconciledIndex = await this.reconcileHandoffIndex(git, checkpoint.head, checkpoint.indexTree, tempDir, checkpoint.checkpointId);
6139
+ const packBaseline = localGitState?.upstreamHead ?? null;
6140
+ const packRefs = [
6141
+ checkpoint.head,
6142
+ reconciledIndex.indexTree,
6143
+ checkpoint.worktreeTree,
6144
+ packBaseline ? `^${packBaseline}` : null
6145
+ ].filter((ref) => !!ref);
6146
+ const headRef = checkpoint.head ? `${HANDOFF_HEAD_REF_PREFIX}${checkpoint.checkpointId}` : void 0;
6147
+ const packPrefix = path3.join(tempDir, checkpoint.checkpointId);
6071
6148
  const [headPack, indexFile, tracking] = await Promise.all([
6072
6149
  this.captureObjectPack(packPrefix, packRefs),
6073
- this.copyIndexFile(git, checkpoint.checkpointId, tempDir),
6150
+ this.statFileArtifact(reconciledIndex.indexFilePath),
6074
6151
  getTrackingMetadata(git, checkpoint.branch)
6075
6152
  ]);
6076
6153
  return {
@@ -6081,7 +6158,7 @@ var GitHandoffTracker = class {
6081
6158
  headRef,
6082
6159
  head: checkpoint.head,
6083
6160
  branch: checkpoint.branch,
6084
- indexTree: checkpoint.indexTree,
6161
+ indexTree: reconciledIndex.indexTree,
6085
6162
  worktreeTree: checkpoint.worktreeTree,
6086
6163
  timestamp: checkpoint.timestamp,
6087
6164
  upstreamRemote: tracking.upstreamRemote,
@@ -6101,6 +6178,7 @@ var GitHandoffTracker = class {
6101
6178
  const { checkpoint, headPackPath, indexPath, localGitState, onDivergedBranch } = input;
6102
6179
  const git = createGitClient(this.repositoryPath);
6103
6180
  if (headPackPath) {
6181
+ await this.ensureBaselineForApply(git, checkpoint, localGitState);
6104
6182
  await this.unpackPackFile(headPackPath);
6105
6183
  }
6106
6184
  if (checkpoint.branch && checkpoint.head) {
@@ -6139,14 +6217,89 @@ var GitHandoffTracker = class {
6139
6217
  });
6140
6218
  return { path: packPath, rawBytes };
6141
6219
  }
6142
- async copyIndexFile(git, checkpointId, tempDir) {
6143
- const indexPath = await this.getGitPath(git, "index");
6144
- const copiedIndexPath = path3.join(tempDir, `${checkpointId}.index`);
6145
- await copyFile(indexPath, copiedIndexPath);
6146
- return {
6147
- path: copiedIndexPath,
6148
- rawBytes: await this.getFileSize(copiedIndexPath)
6149
- };
6220
+ async reconcileHandoffIndex(git, head, indexTree, tempDir, checkpointId) {
6221
+ const realIndexPath = await this.getGitPath(git, "index");
6222
+ const tempIndexPath = path3.join(tempDir, `${checkpointId}.index`);
6223
+ await copyFile(realIndexPath, tempIndexPath);
6224
+ const largePaths = await this.listLargeBlobsInTree(indexTree, MAX_HANDOFF_FILE_BYTES);
6225
+ if (largePaths.length === 0) {
6226
+ return { indexTree, indexFilePath: tempIndexPath };
6227
+ }
6228
+ const headBlobs = head ? await this.readHeadBlobsForPaths(head, largePaths) : /* @__PURE__ */ new Map();
6229
+ const env = { ...process.env, GIT_INDEX_FILE: tempIndexPath };
6230
+ for (const filePath of largePaths) {
6231
+ const headBlob = headBlobs.get(filePath);
6232
+ if (headBlob) {
6233
+ await this.runGitWithEnv(env, [
6234
+ "update-index",
6235
+ "--cacheinfo",
6236
+ `${headBlob.mode},${headBlob.hash},${filePath}`
6237
+ ]);
6238
+ } else {
6239
+ await this.runGitWithEnv(env, [
6240
+ "update-index",
6241
+ "--force-remove",
6242
+ filePath
6243
+ ]).catch(() => {
6244
+ });
6245
+ }
6246
+ }
6247
+ const reconciledTree = (await this.runGitWithEnv(env, ["write-tree"])).trim();
6248
+ return { indexTree: reconciledTree, indexFilePath: tempIndexPath };
6249
+ }
6250
+ async listLargeBlobsInTree(tree, maxBytes) {
6251
+ const { stdout } = await this.runGitProcess(["ls-tree", "-r", "-l", tree], "");
6252
+ const result = [];
6253
+ for (const line of stdout.split("\n")) {
6254
+ if (!line)
6255
+ continue;
6256
+ const tabIndex = line.indexOf(" ");
6257
+ if (tabIndex < 0)
6258
+ continue;
6259
+ const meta = line.slice(0, tabIndex);
6260
+ const filePath = line.slice(tabIndex + 1);
6261
+ const parts = meta.split(/\s+/);
6262
+ if (parts.length < 4)
6263
+ continue;
6264
+ const [, type, , sizeStr] = parts;
6265
+ if (type !== "blob")
6266
+ continue;
6267
+ if (sizeStr === "-")
6268
+ continue;
6269
+ const size = Number.parseInt(sizeStr, 10);
6270
+ if (Number.isFinite(size) && size > maxBytes) {
6271
+ result.push(filePath);
6272
+ }
6273
+ }
6274
+ return result;
6275
+ }
6276
+ async readHeadBlobsForPaths(head, paths) {
6277
+ const result = /* @__PURE__ */ new Map();
6278
+ const CHUNK_SIZE = 100;
6279
+ for (let i = 0; i < paths.length; i += CHUNK_SIZE) {
6280
+ const chunk = paths.slice(i, i + CHUNK_SIZE);
6281
+ const { stdout } = await this.runGitProcess(["ls-tree", "-r", head, "--", ...chunk], "").catch(() => ({ stdout: "", stderr: "" }));
6282
+ for (const line of stdout.split("\n")) {
6283
+ if (!line)
6284
+ continue;
6285
+ const tabIndex = line.indexOf(" ");
6286
+ if (tabIndex < 0)
6287
+ continue;
6288
+ const meta = line.slice(0, tabIndex);
6289
+ const filePath = line.slice(tabIndex + 1);
6290
+ const parts = meta.split(/\s+/);
6291
+ if (parts.length < 3)
6292
+ continue;
6293
+ const [mode, type, hash] = parts;
6294
+ if (type !== "blob")
6295
+ continue;
6296
+ result.set(filePath, { mode, hash });
6297
+ }
6298
+ }
6299
+ return result;
6300
+ }
6301
+ async statFileArtifact(filePath) {
6302
+ return { path: filePath, rawBytes: await this.getFileSize(filePath) };
6150
6303
  }
6151
6304
  async restoreIndexFile(git, indexPath) {
6152
6305
  const gitIndexPath = await this.getGitPath(git, "index");
@@ -6174,6 +6327,20 @@ var GitHandoffTracker = class {
6174
6327
  shouldRestoreTracking(branchStatus2, localGitState, tracking) {
6175
6328
  return branchStatus2.kind === "missing" || !hasTrackingConfig(localGitState) && (tracking.upstreamRemote !== null || tracking.upstreamMergeRef !== null);
6176
6329
  }
6330
+ async ensureBaselineForApply(git, checkpoint, localGitState) {
6331
+ const tracking = this.getPreferredTracking(localGitState, checkpoint);
6332
+ if (!tracking.upstreamRemote || !tracking.upstreamMergeRef)
6333
+ return;
6334
+ await this.ensureRemoteForTracking(git, tracking).catch(() => {
6335
+ });
6336
+ await git.raw(["fetch", tracking.upstreamRemote, tracking.upstreamMergeRef]).catch((err) => {
6337
+ this.logger?.error("Handoff baseline fetch failed; if the pack excludes commits the receiver does not already have, the subsequent unpack/read-tree will fail with an object-missing error", {
6338
+ err: String(err),
6339
+ remote: tracking.upstreamRemote,
6340
+ ref: tracking.upstreamMergeRef
6341
+ });
6342
+ });
6343
+ }
6177
6344
  async ensureRemoteForTracking(git, tracking) {
6178
6345
  if (!tracking.upstreamRemote || !tracking.remoteUrl)
6179
6346
  return;
@@ -6310,6 +6477,31 @@ var GitHandoffTracker = class {
6310
6477
  });
6311
6478
  });
6312
6479
  }
6480
+ async runGitWithEnv(env, args) {
6481
+ return new Promise((resolve2, reject) => {
6482
+ const child = spawn2("git", args, {
6483
+ cwd: this.repositoryPath,
6484
+ stdio: ["ignore", "pipe", "pipe"],
6485
+ env
6486
+ });
6487
+ let stdout = "";
6488
+ let stderr = "";
6489
+ child.stdout.on("data", (chunk) => {
6490
+ stdout += chunk.toString();
6491
+ });
6492
+ child.stderr.on("data", (chunk) => {
6493
+ stderr += chunk.toString();
6494
+ });
6495
+ child.on("error", reject);
6496
+ child.on("close", (code) => {
6497
+ if (code === 0) {
6498
+ resolve2(stdout);
6499
+ return;
6500
+ }
6501
+ reject(new Error(stderr || `git ${args.join(" ")} failed with code ${code}`));
6502
+ });
6503
+ });
6504
+ }
6313
6505
  runGitProcess(args, input) {
6314
6506
  return new Promise((resolve2, reject) => {
6315
6507
  const child = spawn2("git", args, {
@@ -6332,6 +6524,8 @@ var GitHandoffTracker = class {
6332
6524
  }
6333
6525
  reject(new Error(stderr || `git ${args.join(" ")} failed with code ${code}`));
6334
6526
  });
6527
+ child.stdin.on("error", () => {
6528
+ });
6335
6529
  child.stdin.end(input);
6336
6530
  });
6337
6531
  }