hillclimb 0.9.0 → 0.9.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/main.js +67 -22
- package/package.json +1 -1
package/dist/main.js
CHANGED
|
@@ -568,7 +568,7 @@ var PlatformClient = class {
|
|
|
568
568
|
const sizeBytes = body.kind === "buffer" ? body.buffer.length : body.sizeBytes;
|
|
569
569
|
const prefix = `/api/v1/contributions/${encodeURIComponent(contributionId)}`;
|
|
570
570
|
let digest;
|
|
571
|
-
const verify = async (uploadId) => {
|
|
571
|
+
const verify = async (uploadId, options = {}) => {
|
|
572
572
|
if (digest === void 0) {
|
|
573
573
|
const hash = createHash("sha256");
|
|
574
574
|
let localSize = 0;
|
|
@@ -590,7 +590,8 @@ var PlatformClient = class {
|
|
|
590
590
|
return this.verifyReplayArtifact(
|
|
591
591
|
`${prefix}/uploads/${encodeURIComponent(uploadId)}/download`,
|
|
592
592
|
sizeBytes,
|
|
593
|
-
digest
|
|
593
|
+
digest,
|
|
594
|
+
options
|
|
594
595
|
);
|
|
595
596
|
};
|
|
596
597
|
const put = async (target2) => {
|
|
@@ -642,7 +643,7 @@ var PlatformClient = class {
|
|
|
642
643
|
"UPLOAD_REPLAY_MISMATCH"
|
|
643
644
|
);
|
|
644
645
|
}
|
|
645
|
-
const verified = await verify(existing.id);
|
|
646
|
+
const verified = await verify(existing.id, { forbiddenIsAbsent: true });
|
|
646
647
|
if (verified.exists) return;
|
|
647
648
|
target = await this.request(
|
|
648
649
|
"POST",
|
|
@@ -665,7 +666,7 @@ var PlatformClient = class {
|
|
|
665
666
|
}
|
|
666
667
|
await put(target);
|
|
667
668
|
}
|
|
668
|
-
async verifyReplayArtifact(relative, sizeBytes, expectedDigest) {
|
|
669
|
+
async verifyReplayArtifact(relative, sizeBytes, expectedDigest, options = {}) {
|
|
669
670
|
const redirect = await fetch(this.url(relative), {
|
|
670
671
|
headers: this.headers(),
|
|
671
672
|
redirect: "manual",
|
|
@@ -708,8 +709,13 @@ var PlatformClient = class {
|
|
|
708
709
|
} catch {
|
|
709
710
|
throw new PlatformError("Stored artifact verification request failed");
|
|
710
711
|
}
|
|
711
|
-
if (response.status === 404) {
|
|
712
|
+
if (response.status === 404 || response.status === 403 && options.forbiddenIsAbsent) {
|
|
712
713
|
await response.body?.cancel();
|
|
714
|
+
if (response.status === 403)
|
|
715
|
+
appendLog(
|
|
716
|
+
"info",
|
|
717
|
+
"upload-replay: storage refused the pending artifact (403); treating it as never written and letting the guarded PUT decide"
|
|
718
|
+
);
|
|
713
719
|
return { exists: false, url: location };
|
|
714
720
|
}
|
|
715
721
|
if (!response.ok) {
|
|
@@ -16831,7 +16837,7 @@ async function uploadArtifact(client, contributionId, filename, mimeType, buffer
|
|
|
16831
16837
|
}
|
|
16832
16838
|
|
|
16833
16839
|
// package.json
|
|
16834
|
-
var version = "0.9.
|
|
16840
|
+
var version = "0.9.2";
|
|
16835
16841
|
|
|
16836
16842
|
// src/version.ts
|
|
16837
16843
|
var CLI_VERSION = version;
|
|
@@ -16973,13 +16979,14 @@ function isPhantomClaudeHook(payload, toolOverride) {
|
|
|
16973
16979
|
var MAX_FORK_SWEEP_THREADS = 8;
|
|
16974
16980
|
async function runUploadInner(payload, options = {}) {
|
|
16975
16981
|
const outcome = await runUploadForSession(payload, options);
|
|
16976
|
-
|
|
16982
|
+
const settled = outcome === "uploaded" || outcome === "unchanged" || outcome === "skipped";
|
|
16983
|
+
if (!options.sweptFrom && payload.tool === "codex" && settled) {
|
|
16977
16984
|
await sweepCodexForkTranscripts(payload);
|
|
16978
16985
|
}
|
|
16979
|
-
if (!options.sweptFrom && payload.tool === "claude" &&
|
|
16986
|
+
if (!options.sweptFrom && payload.tool === "claude" && settled) {
|
|
16980
16987
|
await sweepClaudeSubagentTranscripts(payload);
|
|
16981
16988
|
}
|
|
16982
|
-
if (!options.sweptFrom &&
|
|
16989
|
+
if (!options.sweptFrom && settled) {
|
|
16983
16990
|
await sweepStrandedAgentUploads(resolveHookSessionId(payload));
|
|
16984
16991
|
}
|
|
16985
16992
|
return outcome;
|
|
@@ -17020,7 +17027,13 @@ async function sweepClaudeSubagentTranscripts(payload) {
|
|
|
17020
17027
|
const project = await findProjectForCwd(cwd);
|
|
17021
17028
|
if (!project) return;
|
|
17022
17029
|
files.sort((a, b) => b.modifiedMs - a.modifiedMs);
|
|
17023
|
-
const tally = {
|
|
17030
|
+
const tally = {
|
|
17031
|
+
uploaded: 0,
|
|
17032
|
+
unchanged: 0,
|
|
17033
|
+
pending: 0,
|
|
17034
|
+
skipped: 0,
|
|
17035
|
+
failed: 0
|
|
17036
|
+
};
|
|
17024
17037
|
for (const entry of files.slice(0, MAX_SUBAGENT_SWEEP)) {
|
|
17025
17038
|
const subagentSessionId = `agent-${entry.agentId}`;
|
|
17026
17039
|
try {
|
|
@@ -17039,6 +17052,7 @@ async function sweepClaudeSubagentTranscripts(payload) {
|
|
|
17039
17052
|
}
|
|
17040
17053
|
);
|
|
17041
17054
|
if (outcome === "uploaded") tally.uploaded++;
|
|
17055
|
+
else if (outcome === "unchanged") tally.unchanged++;
|
|
17042
17056
|
else if (outcome === "pending") tally.pending++;
|
|
17043
17057
|
else tally.skipped++;
|
|
17044
17058
|
appendLog(
|
|
@@ -17055,7 +17069,7 @@ async function sweepClaudeSubagentTranscripts(payload) {
|
|
|
17055
17069
|
}
|
|
17056
17070
|
appendLog(
|
|
17057
17071
|
"info",
|
|
17058
|
-
`[${parentSessionId}] subagent sweep (found=${files.length}, uploaded=${tally.uploaded}, pending=${tally.pending}, skipped=${tally.skipped}, failed=${tally.failed})`
|
|
17072
|
+
`[${parentSessionId}] subagent sweep (found=${files.length}, uploaded=${tally.uploaded}, unchanged=${tally.unchanged}, pending=${tally.pending}, skipped=${tally.skipped}, failed=${tally.failed})`
|
|
17059
17073
|
);
|
|
17060
17074
|
} catch (err) {
|
|
17061
17075
|
appendLog(
|
|
@@ -17178,7 +17192,13 @@ async function sweepCodexForkTranscripts(payload) {
|
|
|
17178
17192
|
registeredRoots
|
|
17179
17193
|
});
|
|
17180
17194
|
if (discovery.forks.length === 0) return;
|
|
17181
|
-
const tally = {
|
|
17195
|
+
const tally = {
|
|
17196
|
+
uploaded: 0,
|
|
17197
|
+
unchanged: 0,
|
|
17198
|
+
pending: 0,
|
|
17199
|
+
skipped: 0,
|
|
17200
|
+
failed: 0
|
|
17201
|
+
};
|
|
17182
17202
|
for (const fork of discovery.forks.slice(0, MAX_FORK_SWEEP_THREADS)) {
|
|
17183
17203
|
const project = await findProjectForCwd(fork.cwd) ?? parent;
|
|
17184
17204
|
const forkPayload = {
|
|
@@ -17194,6 +17214,7 @@ async function sweepCodexForkTranscripts(payload) {
|
|
|
17194
17214
|
sweptFrom: parentSessionId
|
|
17195
17215
|
});
|
|
17196
17216
|
if (outcome === "uploaded") tally.uploaded++;
|
|
17217
|
+
else if (outcome === "unchanged") tally.unchanged++;
|
|
17197
17218
|
else if (outcome === "pending") tally.pending++;
|
|
17198
17219
|
else tally.skipped++;
|
|
17199
17220
|
appendLog(
|
|
@@ -17210,7 +17231,7 @@ async function sweepCodexForkTranscripts(payload) {
|
|
|
17210
17231
|
}
|
|
17211
17232
|
appendLog(
|
|
17212
17233
|
"info",
|
|
17213
|
-
`[${parentSessionId}] fork sweep (scannedRollouts=${discovery.scannedRollouts}, forks=${discovery.forks.length}, uploaded=${tally.uploaded}, pending=${tally.pending}, skipped=${tally.skipped}, failed=${tally.failed})`
|
|
17234
|
+
`[${parentSessionId}] fork sweep (scannedRollouts=${discovery.scannedRollouts}, forks=${discovery.forks.length}, uploaded=${tally.uploaded}, unchanged=${tally.unchanged}, pending=${tally.pending}, skipped=${tally.skipped}, failed=${tally.failed})`
|
|
17214
17235
|
);
|
|
17215
17236
|
} catch (err) {
|
|
17216
17237
|
appendLog(
|
|
@@ -17219,6 +17240,13 @@ async function sweepCodexForkTranscripts(payload) {
|
|
|
17219
17240
|
);
|
|
17220
17241
|
}
|
|
17221
17242
|
}
|
|
17243
|
+
async function settledOutcome(before, drained, repoRoot, sourceTool, sessionId) {
|
|
17244
|
+
if (!drained) return "pending";
|
|
17245
|
+
const after = await readUploadState(repoRoot, sourceTool, sessionId);
|
|
17246
|
+
if (!after) return "unchanged";
|
|
17247
|
+
if (!before) return "uploaded";
|
|
17248
|
+
return after.uploadCount !== before.uploadCount || after.lastUploadedAt !== before.lastUploadedAt ? "uploaded" : "unchanged";
|
|
17249
|
+
}
|
|
17222
17250
|
async function runUploadForSession(payload, options) {
|
|
17223
17251
|
if (isPhantomClaudeHook(payload)) {
|
|
17224
17252
|
appendLog("info", "upload: skipping phantom Claude hook fired by Cursor");
|
|
@@ -17265,7 +17293,8 @@ async function runUploadForSession(payload, options) {
|
|
|
17265
17293
|
"info",
|
|
17266
17294
|
`[${sessionId}] replaying ${pending.length} pending capture(s) without reading the live transcript`
|
|
17267
17295
|
);
|
|
17268
|
-
|
|
17296
|
+
const before2 = await readUploadState(repoRoot, sourceTool, sessionId);
|
|
17297
|
+
const drained2 = await drainCapturedSessions({
|
|
17269
17298
|
sessionId,
|
|
17270
17299
|
repoRoot,
|
|
17271
17300
|
config,
|
|
@@ -17273,7 +17302,8 @@ async function runUploadForSession(payload, options) {
|
|
|
17273
17302
|
eventKind: eventKind2,
|
|
17274
17303
|
recordedAt,
|
|
17275
17304
|
transcriptPath: firstPending.transcriptPath
|
|
17276
|
-
})
|
|
17305
|
+
});
|
|
17306
|
+
return settledOutcome(before2, drained2, repoRoot, sourceTool, sessionId);
|
|
17277
17307
|
};
|
|
17278
17308
|
const transcriptPath = await resolveTranscriptPath(
|
|
17279
17309
|
payload,
|
|
@@ -17330,7 +17360,8 @@ async function runUploadForSession(payload, options) {
|
|
|
17330
17360
|
);
|
|
17331
17361
|
return "skipped";
|
|
17332
17362
|
}
|
|
17333
|
-
const
|
|
17363
|
+
const before = await readUploadState(repoRoot, sourceTool, sessionId);
|
|
17364
|
+
const drained = await captureAndUploadSession({
|
|
17334
17365
|
sessionId,
|
|
17335
17366
|
transcriptPath: transcriptResolved,
|
|
17336
17367
|
repoRoot,
|
|
@@ -17340,7 +17371,7 @@ async function runUploadForSession(payload, options) {
|
|
|
17340
17371
|
recordedAt,
|
|
17341
17372
|
subagent: options.subagent
|
|
17342
17373
|
});
|
|
17343
|
-
return
|
|
17374
|
+
return settledOutcome(before, drained, repoRoot, sourceTool, sessionId);
|
|
17344
17375
|
}
|
|
17345
17376
|
var AGENT_MAX_UPLOAD_BYTES = 1024 * 1024 * 1024;
|
|
17346
17377
|
function makeTranscriptGroup(repoRoot, sourceTool, transcriptPath, content) {
|
|
@@ -20402,6 +20433,7 @@ async function sweepCodexForkCaptures(params) {
|
|
|
20402
20433
|
worktree: 0,
|
|
20403
20434
|
sameTree: 0,
|
|
20404
20435
|
unrelated: 0,
|
|
20436
|
+
deferred: 0,
|
|
20405
20437
|
initialized: 0,
|
|
20406
20438
|
uploaded: 0,
|
|
20407
20439
|
unchanged: 0,
|
|
@@ -20409,8 +20441,14 @@ async function sweepCodexForkCaptures(params) {
|
|
|
20409
20441
|
failed: 0,
|
|
20410
20442
|
skipped: 0
|
|
20411
20443
|
};
|
|
20412
|
-
|
|
20413
|
-
|
|
20444
|
+
const targets = /* @__PURE__ */ new Map();
|
|
20445
|
+
const hooked = /* @__PURE__ */ new Set();
|
|
20446
|
+
for (const fork of discovery.forks) {
|
|
20447
|
+
let target = targets.get(fork.cwd);
|
|
20448
|
+
if (target === void 0) {
|
|
20449
|
+
target = await resolveForkTarget(fork.cwd, repos);
|
|
20450
|
+
targets.set(fork.cwd, target);
|
|
20451
|
+
}
|
|
20414
20452
|
if (!target) {
|
|
20415
20453
|
tally.unrelated++;
|
|
20416
20454
|
continue;
|
|
@@ -20419,12 +20457,19 @@ async function sweepCodexForkCaptures(params) {
|
|
|
20419
20457
|
tally.sameTree++;
|
|
20420
20458
|
continue;
|
|
20421
20459
|
}
|
|
20422
|
-
tally.worktree++;
|
|
20423
20460
|
const repo = {
|
|
20424
20461
|
repoRoot: target.repoRoot,
|
|
20425
20462
|
config: target.config
|
|
20426
20463
|
};
|
|
20427
|
-
|
|
20464
|
+
if (!hooked.has(repo.repoRoot)) {
|
|
20465
|
+
hooked.add(repo.repoRoot);
|
|
20466
|
+
await propagateCodexHooks(project.repoRoot, repo.repoRoot);
|
|
20467
|
+
}
|
|
20468
|
+
if (tally.worktree >= MAX_FORK_SWEEP_THREADS2) {
|
|
20469
|
+
tally.deferred++;
|
|
20470
|
+
continue;
|
|
20471
|
+
}
|
|
20472
|
+
tally.worktree++;
|
|
20428
20473
|
const forkPayload = {
|
|
20429
20474
|
session_id: fork.threadId,
|
|
20430
20475
|
cwd: fork.cwd,
|
|
@@ -20508,7 +20553,7 @@ async function sweepCodexForkCaptures(params) {
|
|
|
20508
20553
|
}
|
|
20509
20554
|
appendLog(
|
|
20510
20555
|
"info",
|
|
20511
|
-
`git-traces: fork sweep (parent=${parentSessionId}, event=${event}, triggerRepo=${project.repoRoot}, scannedRollouts=${discovery.scannedRollouts}, forks=${discovery.forks.length}, worktree=${tally.worktree}, sameTree=${tally.sameTree}, unrelated=${tally.unrelated}, initialized=${tally.initialized}, uploaded=${tally.uploaded}, unchanged=${tally.unchanged}, pending=${tally.pending}, failed=${tally.failed}, skipped=${tally.skipped})`
|
|
20556
|
+
`git-traces: fork sweep (parent=${parentSessionId}, event=${event}, triggerRepo=${project.repoRoot}, scannedRollouts=${discovery.scannedRollouts}, forks=${discovery.forks.length}, worktree=${tally.worktree}, sameTree=${tally.sameTree}, unrelated=${tally.unrelated}, initialized=${tally.initialized}, uploaded=${tally.uploaded}, unchanged=${tally.unchanged}, pending=${tally.pending}, failed=${tally.failed}, skipped=${tally.skipped}, deferred=${tally.deferred})`
|
|
20512
20557
|
);
|
|
20513
20558
|
}
|
|
20514
20559
|
async function sweepCodexForksSafely(params) {
|