hillclimb 0.9.2 → 0.9.4
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
CHANGED
|
@@ -5,11 +5,12 @@ import {
|
|
|
5
5
|
configDir,
|
|
6
6
|
configPath,
|
|
7
7
|
findProjectForCwd,
|
|
8
|
+
hasNestedGitFile,
|
|
8
9
|
loadProjects,
|
|
9
10
|
setLogPrefix,
|
|
10
11
|
todayLogPath,
|
|
11
12
|
upsertProject
|
|
12
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-UPXMA7RC.js";
|
|
13
14
|
|
|
14
15
|
// src/main.ts
|
|
15
16
|
import fs27 from "fs";
|
|
@@ -992,7 +993,7 @@ var TOOLS = [
|
|
|
992
993
|
{ eventName: "Stop", command: GIT_TRACES_CMD("claude") },
|
|
993
994
|
{ eventName: "SessionEnd", command: GIT_TRACES_CMD("claude") }
|
|
994
995
|
],
|
|
995
|
-
detect: () => isDir(path4.join(
|
|
996
|
+
detect: () => isDir(path4.join(homeDir(), ".claude"))
|
|
996
997
|
},
|
|
997
998
|
{
|
|
998
999
|
tool: "cursor",
|
|
@@ -1009,7 +1010,7 @@ var TOOLS = [
|
|
|
1009
1010
|
{ eventName: "stop", command: GIT_TRACES_CMD("cursor") },
|
|
1010
1011
|
{ eventName: "sessionEnd", command: GIT_TRACES_CMD("cursor") }
|
|
1011
1012
|
],
|
|
1012
|
-
detect: () => isDir(path4.join(
|
|
1013
|
+
detect: () => isDir(path4.join(homeDir(), ".cursor"))
|
|
1013
1014
|
},
|
|
1014
1015
|
{
|
|
1015
1016
|
// opencode has no settings-file hook system (the `experimental.hook` block
|
|
@@ -1024,7 +1025,7 @@ var TOOLS = [
|
|
|
1024
1025
|
format: "opencode",
|
|
1025
1026
|
events: [],
|
|
1026
1027
|
// Events are wired inside the plugin itself; see OPENCODE_PLUGIN_CONTENT.
|
|
1027
|
-
detect: () => isDir(path4.join(
|
|
1028
|
+
detect: () => isDir(path4.join(homeDir(), ".local", "share", "opencode")) || isDir(path4.join(homeDir(), ".config", "opencode"))
|
|
1028
1029
|
},
|
|
1029
1030
|
{
|
|
1030
1031
|
// VS Code Copilot Chat. Same "no SessionEnd" constraint as Codex (the
|
|
@@ -1064,7 +1065,7 @@ var TOOLS = [
|
|
|
1064
1065
|
{ eventName: "SessionStart", command: GIT_TRACES_CMD("codex") },
|
|
1065
1066
|
{ eventName: "Stop", command: GIT_TRACES_CMD("codex") }
|
|
1066
1067
|
],
|
|
1067
|
-
detect: () => isDir(path4.join(
|
|
1068
|
+
detect: () => isDir(path4.join(homeDir(), ".codex"))
|
|
1068
1069
|
}
|
|
1069
1070
|
];
|
|
1070
1071
|
function isDir(p7) {
|
|
@@ -1074,8 +1075,11 @@ function isDir(p7) {
|
|
|
1074
1075
|
return false;
|
|
1075
1076
|
}
|
|
1076
1077
|
}
|
|
1078
|
+
function homeDir() {
|
|
1079
|
+
return process.env.HOME || os.homedir();
|
|
1080
|
+
}
|
|
1077
1081
|
function copilotChatDetect() {
|
|
1078
|
-
const home =
|
|
1082
|
+
const home = homeDir();
|
|
1079
1083
|
const suffix = path4.join("User", "globalStorage", "github.copilot-chat");
|
|
1080
1084
|
const candidates = [
|
|
1081
1085
|
path4.join(home, "Library", "Application Support", "Code", suffix),
|
|
@@ -2720,22 +2724,76 @@ import { createReadStream } from "fs";
|
|
|
2720
2724
|
import fs11 from "fs/promises";
|
|
2721
2725
|
import path10 from "path";
|
|
2722
2726
|
|
|
2727
|
+
// src/timing-profile.ts
|
|
2728
|
+
var DEFAULT_STAGE_MIN_MS = 250;
|
|
2729
|
+
var DEFAULT_LOCK_WAIT_MIN_MS = 1e3;
|
|
2730
|
+
var STAGE_MIN_MS_ENV = "HILLCLIMB_TIMING_STAGE_MIN_MS";
|
|
2731
|
+
var LOCK_WAIT_MIN_MS_ENV = "HILLCLIMB_TIMING_LOCK_WAIT_MIN_MS";
|
|
2732
|
+
function thresholdMs(name, fallback) {
|
|
2733
|
+
const raw = process.env[name];
|
|
2734
|
+
if (raw === void 0 || raw.trim() === "") return fallback;
|
|
2735
|
+
const value = Number(raw);
|
|
2736
|
+
return Number.isFinite(value) && value >= 0 ? value : fallback;
|
|
2737
|
+
}
|
|
2738
|
+
function shouldLogStage(stage, elapsedMs, status) {
|
|
2739
|
+
if (stage === "lock-wait") {
|
|
2740
|
+
return elapsedMs >= thresholdMs(LOCK_WAIT_MIN_MS_ENV, DEFAULT_LOCK_WAIT_MIN_MS);
|
|
2741
|
+
}
|
|
2742
|
+
return status === "failed" || elapsedMs >= thresholdMs(STAGE_MIN_MS_ENV, DEFAULT_STAGE_MIN_MS);
|
|
2743
|
+
}
|
|
2744
|
+
var StageProfile = class {
|
|
2745
|
+
constructor(prefix) {
|
|
2746
|
+
this.prefix = prefix;
|
|
2747
|
+
}
|
|
2748
|
+
totals = /* @__PURE__ */ new Map();
|
|
2749
|
+
record(stage, elapsedMs, status) {
|
|
2750
|
+
const totals = this.totals.get(stage) ?? {
|
|
2751
|
+
count: 0,
|
|
2752
|
+
totalMs: 0,
|
|
2753
|
+
maxMs: 0,
|
|
2754
|
+
failed: 0
|
|
2755
|
+
};
|
|
2756
|
+
totals.count++;
|
|
2757
|
+
totals.totalMs += elapsedMs;
|
|
2758
|
+
totals.maxMs = Math.max(totals.maxMs, elapsedMs);
|
|
2759
|
+
if (status === "failed") totals.failed++;
|
|
2760
|
+
this.totals.set(stage, totals);
|
|
2761
|
+
}
|
|
2762
|
+
flush() {
|
|
2763
|
+
if (this.totals.size === 0) return;
|
|
2764
|
+
const entries = [...this.totals].map(([stage, totals]) => {
|
|
2765
|
+
let entry = `${stage}=${totals.totalMs}ms/${totals.count}`;
|
|
2766
|
+
if (totals.count > 1) entry += ` max=${totals.maxMs}ms`;
|
|
2767
|
+
if (totals.failed > 0) entry += ` failed=${totals.failed}`;
|
|
2768
|
+
return entry;
|
|
2769
|
+
});
|
|
2770
|
+
appendLog(
|
|
2771
|
+
"info",
|
|
2772
|
+
`${this.prefix} (stages=${this.totals.size}, ${entries.join(", ")})`
|
|
2773
|
+
);
|
|
2774
|
+
this.totals.clear();
|
|
2775
|
+
}
|
|
2776
|
+
};
|
|
2777
|
+
|
|
2723
2778
|
// src/agent-timing.ts
|
|
2779
|
+
var profile = new StageProfile("agent-upload: timing profile");
|
|
2724
2780
|
function startAgentStage(stage, context = "") {
|
|
2725
2781
|
const startedAt = Date.now();
|
|
2726
2782
|
const started = performance.now();
|
|
2727
2783
|
const suffix = context ? `, ${context}` : "";
|
|
2728
|
-
appendLog(
|
|
2729
|
-
"info",
|
|
2730
|
-
`agent-upload: timing (stage=${stage}, status=started, startedAt=${startedAt}${suffix})`
|
|
2731
|
-
);
|
|
2732
2784
|
return (status, details) => {
|
|
2785
|
+
const elapsedMs = Math.round(performance.now() - started);
|
|
2786
|
+
profile.record(stage, elapsedMs, status);
|
|
2787
|
+
if (!shouldLogStage(stage, elapsedMs, status)) return;
|
|
2733
2788
|
appendLog(
|
|
2734
2789
|
"info",
|
|
2735
|
-
`agent-upload: timing (stage=${stage}, status=${status}, elapsedMs=${
|
|
2790
|
+
`agent-upload: timing (stage=${stage}, status=${status}, elapsedMs=${elapsedMs}, startedAt=${startedAt}, finishedAt=${Date.now()}${suffix}${details ? `, ${details}` : ""})`
|
|
2736
2791
|
);
|
|
2737
2792
|
};
|
|
2738
2793
|
}
|
|
2794
|
+
function flushAgentProfile() {
|
|
2795
|
+
profile.flush();
|
|
2796
|
+
}
|
|
2739
2797
|
async function measureAgentStage(stage, action, context) {
|
|
2740
2798
|
const finish = startAgentStage(stage, context);
|
|
2741
2799
|
let status = "failed";
|
|
@@ -3199,6 +3257,47 @@ async function detectCodexLineage(childPath) {
|
|
|
3199
3257
|
}
|
|
3200
3258
|
}
|
|
3201
3259
|
|
|
3260
|
+
// src/git-traces/timing.ts
|
|
3261
|
+
import { performance as performance2 } from "perf_hooks";
|
|
3262
|
+
var profile2 = new StageProfile("git-traces: capture profile");
|
|
3263
|
+
function startCaptureStage(stage, context, startedAt = Date.now()) {
|
|
3264
|
+
const started = performance2.now();
|
|
3265
|
+
return (status, details) => {
|
|
3266
|
+
const elapsedMs = Math.round(performance2.now() - started);
|
|
3267
|
+
profile2.record(stage, elapsedMs, status);
|
|
3268
|
+
if (!shouldLogStage(stage, elapsedMs, status)) return;
|
|
3269
|
+
appendLog(
|
|
3270
|
+
"info",
|
|
3271
|
+
`git-traces: capture timing (${context}, stage=${stage}, status=${status}, elapsedMs=${elapsedMs}, startedAt=${startedAt}, finishedAt=${Date.now()}${details ? `, ${details}` : ""})`
|
|
3272
|
+
);
|
|
3273
|
+
};
|
|
3274
|
+
}
|
|
3275
|
+
function flushCaptureProfile() {
|
|
3276
|
+
profile2.flush();
|
|
3277
|
+
}
|
|
3278
|
+
async function timeAsyncCaptureStage(stage, context, run) {
|
|
3279
|
+
const finish = startCaptureStage(stage, context);
|
|
3280
|
+
let status = "failed";
|
|
3281
|
+
try {
|
|
3282
|
+
const result = await run();
|
|
3283
|
+
status = "ok";
|
|
3284
|
+
return result;
|
|
3285
|
+
} finally {
|
|
3286
|
+
finish(status);
|
|
3287
|
+
}
|
|
3288
|
+
}
|
|
3289
|
+
function timeCaptureStage(repoRoot, stage, run) {
|
|
3290
|
+
const finish = startCaptureStage(stage, `repo=${repoRoot}`);
|
|
3291
|
+
let status = "failed";
|
|
3292
|
+
try {
|
|
3293
|
+
const result = run();
|
|
3294
|
+
status = "ok";
|
|
3295
|
+
return result;
|
|
3296
|
+
} finally {
|
|
3297
|
+
finish(status);
|
|
3298
|
+
}
|
|
3299
|
+
}
|
|
3300
|
+
|
|
3202
3301
|
// src/hook-timing.ts
|
|
3203
3302
|
var HOOK_RECEIVED_AT_ENV = "HILLCLIMB_HOOK_RECEIVED_AT";
|
|
3204
3303
|
function pinHookReceivedAt(env, now = Date.now()) {
|
|
@@ -3222,6 +3321,8 @@ async function measureHookWork(kind, action) {
|
|
|
3222
3321
|
status = "completed";
|
|
3223
3322
|
return result;
|
|
3224
3323
|
} finally {
|
|
3324
|
+
flushCaptureProfile();
|
|
3325
|
+
flushAgentProfile();
|
|
3225
3326
|
appendLog(
|
|
3226
3327
|
"info",
|
|
3227
3328
|
`hook-work: kind=${kind} status=${status} finishedAt=${Date.now()} workMs=${Math.round(performance.now() - started)} hookAgeMs=${Math.max(0, Date.now() - receivedAt)}`
|
|
@@ -4469,17 +4570,22 @@ async function gitCommonDir(cwd) {
|
|
|
4469
4570
|
async function classifyForkCwd(cwd, parentRepoRoot) {
|
|
4470
4571
|
const resolved = await canonicalPath(cwd);
|
|
4471
4572
|
const parentRoot = await canonicalPath(parentRepoRoot);
|
|
4472
|
-
|
|
4573
|
+
const below = isAtOrBelow(resolved, parentRoot);
|
|
4574
|
+
if (below && !hasNestedGitFile(parentRoot, resolved))
|
|
4473
4575
|
return { kind: "same-tree", repoRoot: parentRoot };
|
|
4474
4576
|
const toplevel = await gitOut(resolved, ["rev-parse", "--show-toplevel"]);
|
|
4475
|
-
if (!toplevel)
|
|
4577
|
+
if (!toplevel)
|
|
4578
|
+
return below ? { kind: "same-tree", repoRoot: parentRoot } : { kind: "unrelated", repoRoot: null };
|
|
4579
|
+
const worktreeRoot = await canonicalPath(toplevel);
|
|
4580
|
+
if (worktreeRoot === parentRoot)
|
|
4581
|
+
return { kind: "same-tree", repoRoot: parentRoot };
|
|
4476
4582
|
const [forkCommon, parentCommon] = await Promise.all([
|
|
4477
4583
|
gitCommonDir(resolved),
|
|
4478
4584
|
gitCommonDir(parentRoot)
|
|
4479
4585
|
]);
|
|
4480
4586
|
if (forkCommon && parentCommon && forkCommon === parentCommon)
|
|
4481
|
-
return { kind: "worktree", repoRoot:
|
|
4482
|
-
return { kind: "unrelated", repoRoot: null };
|
|
4587
|
+
return { kind: "worktree", repoRoot: worktreeRoot };
|
|
4588
|
+
return below ? { kind: "same-tree", repoRoot: parentRoot } : { kind: "unrelated", repoRoot: null };
|
|
4483
4589
|
}
|
|
4484
4590
|
async function discoverCodexForkThreads(params) {
|
|
4485
4591
|
const nowMs = params.nowMs ?? Date.now();
|
|
@@ -4627,6 +4733,25 @@ async function readFileContent(file) {
|
|
|
4627
4733
|
return { kind: "error" };
|
|
4628
4734
|
}
|
|
4629
4735
|
}
|
|
4736
|
+
async function readFileBuffer(file) {
|
|
4737
|
+
if (file.isBinary) {
|
|
4738
|
+
return { kind: "binary" };
|
|
4739
|
+
}
|
|
4740
|
+
if (file.content) {
|
|
4741
|
+
return { kind: "bytes", content: file.content };
|
|
4742
|
+
}
|
|
4743
|
+
if (await checkBinary(file.absolutePath)) {
|
|
4744
|
+
return { kind: "binary" };
|
|
4745
|
+
}
|
|
4746
|
+
try {
|
|
4747
|
+
return {
|
|
4748
|
+
kind: "bytes",
|
|
4749
|
+
content: await fs13.promises.readFile(file.absolutePath)
|
|
4750
|
+
};
|
|
4751
|
+
} catch {
|
|
4752
|
+
return { kind: "error" };
|
|
4753
|
+
}
|
|
4754
|
+
}
|
|
4630
4755
|
|
|
4631
4756
|
// src/middleware/large-file.ts
|
|
4632
4757
|
import { constants as bufferConstants } from "buffer";
|
|
@@ -4637,36 +4762,307 @@ function getLargeFileThreshold() {
|
|
|
4637
4762
|
}
|
|
4638
4763
|
var NEWLINE = 10;
|
|
4639
4764
|
var OVERSIZED_LINE_PLACEHOLDER = '{"_hillclimb_omitted":"line exceeded max string length; dropped to allow redaction"}';
|
|
4640
|
-
function
|
|
4765
|
+
function scanBufferLines(buffer, redactLine, options = {}) {
|
|
4641
4766
|
const maxLineBytes = options.maxLineBytes ?? MAX_STRINGIFIABLE_BYTES;
|
|
4642
4767
|
const oversizedPlaceholder = options.oversizedPlaceholder ?? OVERSIZED_LINE_PLACEHOLDER;
|
|
4643
|
-
const
|
|
4644
|
-
const
|
|
4768
|
+
const end = options.end ?? buffer.length;
|
|
4769
|
+
const ownsFinalLine = end === buffer.length;
|
|
4770
|
+
const edits = [];
|
|
4645
4771
|
let totalCount = 0;
|
|
4646
4772
|
let oversizedLines = 0;
|
|
4647
|
-
let start = 0;
|
|
4648
|
-
|
|
4649
|
-
for (; ; ) {
|
|
4773
|
+
let start = options.start ?? 0;
|
|
4774
|
+
while (ownsFinalLine || start < end) {
|
|
4650
4775
|
const nl = buffer.indexOf(NEWLINE, start);
|
|
4651
|
-
const
|
|
4652
|
-
|
|
4653
|
-
if (!first) pieces.push(newlineBuf);
|
|
4654
|
-
first = false;
|
|
4655
|
-
if (slice.length > maxLineBytes) {
|
|
4776
|
+
const lineEnd = nl === -1 ? buffer.length : nl;
|
|
4777
|
+
if (lineEnd - start > maxLineBytes) {
|
|
4656
4778
|
oversizedLines++;
|
|
4657
|
-
|
|
4779
|
+
edits.push({ start, end: lineEnd, value: oversizedPlaceholder });
|
|
4658
4780
|
} else {
|
|
4659
|
-
const { value, count } = redactLine(
|
|
4660
|
-
|
|
4661
|
-
|
|
4781
|
+
const { value, count } = redactLine(
|
|
4782
|
+
buffer.toString("utf-8", start, lineEnd)
|
|
4783
|
+
);
|
|
4784
|
+
if (count > 0) {
|
|
4785
|
+
totalCount += count;
|
|
4786
|
+
edits.push({ start, end: lineEnd, value });
|
|
4787
|
+
}
|
|
4662
4788
|
}
|
|
4663
4789
|
if (nl === -1) break;
|
|
4664
4790
|
start = nl + 1;
|
|
4665
4791
|
}
|
|
4792
|
+
return { edits, count: totalCount, oversizedLines };
|
|
4793
|
+
}
|
|
4794
|
+
function applyLineEdits(buffer, edits) {
|
|
4795
|
+
if (edits.length === 0) return buffer;
|
|
4796
|
+
const pieces = [];
|
|
4797
|
+
let copied = 0;
|
|
4798
|
+
for (const edit of edits) {
|
|
4799
|
+
pieces.push(buffer.subarray(copied, edit.start));
|
|
4800
|
+
pieces.push(Buffer.from(edit.value, "utf-8"));
|
|
4801
|
+
copied = edit.end;
|
|
4802
|
+
}
|
|
4803
|
+
pieces.push(buffer.subarray(copied));
|
|
4804
|
+
return Buffer.concat(pieces);
|
|
4805
|
+
}
|
|
4806
|
+
function redactBufferByLine(buffer, redactLine, options = {}) {
|
|
4807
|
+
const { edits, count, oversizedLines } = scanBufferLines(
|
|
4808
|
+
buffer,
|
|
4809
|
+
redactLine,
|
|
4810
|
+
options
|
|
4811
|
+
);
|
|
4812
|
+
return { content: applyLineEdits(buffer, edits), count, oversizedLines };
|
|
4813
|
+
}
|
|
4814
|
+
|
|
4815
|
+
// src/middleware/pattern-prefilter.ts
|
|
4816
|
+
var MIN_LITERAL = 3;
|
|
4817
|
+
function score(req) {
|
|
4818
|
+
if (!req) return -1;
|
|
4819
|
+
let min = Number.POSITIVE_INFINITY;
|
|
4820
|
+
for (const s of req) min = Math.min(min, s.length);
|
|
4821
|
+
return min * 1e3 - req.length;
|
|
4822
|
+
}
|
|
4823
|
+
function better(a, b) {
|
|
4824
|
+
return score(b) > score(a) ? b : a;
|
|
4825
|
+
}
|
|
4826
|
+
function isAsciiLiteral(ch) {
|
|
4827
|
+
const c = ch.charCodeAt(0);
|
|
4828
|
+
return c >= 32 && c < 127;
|
|
4829
|
+
}
|
|
4830
|
+
var UNKNOWN = { literals: null, minLength: 0 };
|
|
4831
|
+
function analyzeRegex(source, flags = "") {
|
|
4832
|
+
if (flags.includes("v")) return UNKNOWN;
|
|
4833
|
+
let i = 0;
|
|
4834
|
+
function skipPast(close) {
|
|
4835
|
+
const at = source.indexOf(close, i);
|
|
4836
|
+
if (at === -1) throw new Error("unterminated escape");
|
|
4837
|
+
i = at + 1;
|
|
4838
|
+
}
|
|
4839
|
+
function flush(seq) {
|
|
4840
|
+
if (seq.run.length > 0) {
|
|
4841
|
+
seq.req = better(seq.req, [seq.run.toLowerCase()]);
|
|
4842
|
+
seq.run = "";
|
|
4843
|
+
}
|
|
4844
|
+
}
|
|
4845
|
+
function quantifier() {
|
|
4846
|
+
const ch = source[i];
|
|
4847
|
+
let min = null;
|
|
4848
|
+
if (ch === "*" || ch === "?") {
|
|
4849
|
+
min = 0;
|
|
4850
|
+
i++;
|
|
4851
|
+
} else if (ch === "+") {
|
|
4852
|
+
min = 1;
|
|
4853
|
+
i++;
|
|
4854
|
+
} else if (ch === "{") {
|
|
4855
|
+
const m = /^\{(\d+)(?:,\d*)?\}/.exec(source.slice(i));
|
|
4856
|
+
if (m) {
|
|
4857
|
+
min = Number(m[1]);
|
|
4858
|
+
i += m[0].length;
|
|
4859
|
+
}
|
|
4860
|
+
}
|
|
4861
|
+
if (min !== null && source[i] === "?") i++;
|
|
4862
|
+
return min;
|
|
4863
|
+
}
|
|
4864
|
+
function skipClass() {
|
|
4865
|
+
i++;
|
|
4866
|
+
while (i < source.length && source[i] !== "]") {
|
|
4867
|
+
if (source[i] === "\\") i++;
|
|
4868
|
+
i++;
|
|
4869
|
+
}
|
|
4870
|
+
i++;
|
|
4871
|
+
}
|
|
4872
|
+
function alternation() {
|
|
4873
|
+
const branches = [];
|
|
4874
|
+
let seq = { req: null, run: "", min: 0 };
|
|
4875
|
+
while (i < source.length && source[i] !== ")") {
|
|
4876
|
+
const ch = source[i];
|
|
4877
|
+
if (ch === "|") {
|
|
4878
|
+
flush(seq);
|
|
4879
|
+
branches.push(seq);
|
|
4880
|
+
seq = { req: null, run: "", min: 0 };
|
|
4881
|
+
i++;
|
|
4882
|
+
continue;
|
|
4883
|
+
}
|
|
4884
|
+
if (ch === "(") {
|
|
4885
|
+
i++;
|
|
4886
|
+
let lookaround = false;
|
|
4887
|
+
if (source[i] === "?") {
|
|
4888
|
+
const m = /^\?(?:[:=!]|<[=!]|<[A-Za-z_$][\w$]*>)/.exec(
|
|
4889
|
+
source.slice(i)
|
|
4890
|
+
);
|
|
4891
|
+
if (!m) throw new Error(`unsupported group at ${i}`);
|
|
4892
|
+
lookaround = m[0] !== "?:" && !m[0].endsWith(">");
|
|
4893
|
+
i += m[0].length;
|
|
4894
|
+
}
|
|
4895
|
+
const inner = alternation();
|
|
4896
|
+
if (source[i] !== ")") throw new Error("unbalanced group");
|
|
4897
|
+
i++;
|
|
4898
|
+
const min3 = quantifier();
|
|
4899
|
+
flush(seq);
|
|
4900
|
+
if (!lookaround) {
|
|
4901
|
+
seq.min += inner.min * (min3 ?? 1);
|
|
4902
|
+
if (min3 === null || min3 >= 1) seq.req = better(seq.req, inner.req);
|
|
4903
|
+
}
|
|
4904
|
+
continue;
|
|
4905
|
+
}
|
|
4906
|
+
if (ch === "[" || ch === ".") {
|
|
4907
|
+
if (ch === "[") skipClass();
|
|
4908
|
+
else i++;
|
|
4909
|
+
seq.min += quantifier() ?? 1;
|
|
4910
|
+
flush(seq);
|
|
4911
|
+
continue;
|
|
4912
|
+
}
|
|
4913
|
+
if (ch === "^" || ch === "$") {
|
|
4914
|
+
i++;
|
|
4915
|
+
quantifier();
|
|
4916
|
+
flush(seq);
|
|
4917
|
+
continue;
|
|
4918
|
+
}
|
|
4919
|
+
let lit;
|
|
4920
|
+
let width = 1;
|
|
4921
|
+
if (ch === "\\") {
|
|
4922
|
+
const e = source[i + 1];
|
|
4923
|
+
i += 2;
|
|
4924
|
+
if (/[A-Za-z0-9]/.test(e)) {
|
|
4925
|
+
if (!/[dDwWsS]/.test(e)) width = 0;
|
|
4926
|
+
if (e === "x") i += 2;
|
|
4927
|
+
else if (e === "u") {
|
|
4928
|
+
if (source[i] === "{") skipPast("}");
|
|
4929
|
+
else i += 4;
|
|
4930
|
+
} else if (e === "c") i += 1;
|
|
4931
|
+
else if (e === "k" && source[i] === "<") skipPast(">");
|
|
4932
|
+
else if ((e === "p" || e === "P") && source[i] === "{") skipPast("}");
|
|
4933
|
+
lit = null;
|
|
4934
|
+
} else {
|
|
4935
|
+
lit = e;
|
|
4936
|
+
}
|
|
4937
|
+
} else {
|
|
4938
|
+
lit = ch;
|
|
4939
|
+
i++;
|
|
4940
|
+
}
|
|
4941
|
+
const min2 = quantifier();
|
|
4942
|
+
seq.min += width * (min2 ?? 1);
|
|
4943
|
+
if (lit === null || !isAsciiLiteral(lit)) {
|
|
4944
|
+
flush(seq);
|
|
4945
|
+
} else if (min2 === null) {
|
|
4946
|
+
seq.run += lit;
|
|
4947
|
+
} else {
|
|
4948
|
+
if (min2 >= 1) seq.run += lit;
|
|
4949
|
+
flush(seq);
|
|
4950
|
+
}
|
|
4951
|
+
}
|
|
4952
|
+
flush(seq);
|
|
4953
|
+
branches.push(seq);
|
|
4954
|
+
let min = Number.POSITIVE_INFINITY;
|
|
4955
|
+
let all = [];
|
|
4956
|
+
for (const b of branches) {
|
|
4957
|
+
min = Math.min(min, b.min);
|
|
4958
|
+
if (!b.req) all = null;
|
|
4959
|
+
else all?.push(...b.req);
|
|
4960
|
+
}
|
|
4961
|
+
return { req: all ? [...new Set(all)] : null, min };
|
|
4962
|
+
}
|
|
4963
|
+
try {
|
|
4964
|
+
const { req, min } = alternation();
|
|
4965
|
+
if (i !== source.length) return UNKNOWN;
|
|
4966
|
+
const foldsBeyondAscii = flags.includes("i") && flags.includes("u");
|
|
4967
|
+
const usable = req && !foldsBeyondAscii && !req.some((s) => s.length < MIN_LITERAL);
|
|
4968
|
+
return { literals: usable ? req : null, minLength: min };
|
|
4969
|
+
} catch {
|
|
4970
|
+
return UNKNOWN;
|
|
4971
|
+
}
|
|
4972
|
+
}
|
|
4973
|
+
function isAscii(text2) {
|
|
4974
|
+
for (let k = 0; k < text2.length; k++) {
|
|
4975
|
+
if (text2.charCodeAt(k) >= 128) return false;
|
|
4976
|
+
}
|
|
4977
|
+
return true;
|
|
4978
|
+
}
|
|
4979
|
+
function buildPrefilter(literalsByRule) {
|
|
4980
|
+
const alwaysRun = [];
|
|
4981
|
+
const goto = [/* @__PURE__ */ new Map()];
|
|
4982
|
+
const outs = [[]];
|
|
4983
|
+
literalsByRule.forEach((given, rule) => {
|
|
4984
|
+
const lits = given?.map((lit) => lit.toLowerCase());
|
|
4985
|
+
if (!lits || lits.some((lit) => lit === "" || !isAscii(lit))) {
|
|
4986
|
+
alwaysRun.push(rule);
|
|
4987
|
+
return;
|
|
4988
|
+
}
|
|
4989
|
+
for (const lit of lits) {
|
|
4990
|
+
let s = 0;
|
|
4991
|
+
for (let k = 0; k < lit.length; k++) {
|
|
4992
|
+
const c = lit.charCodeAt(k);
|
|
4993
|
+
let n = goto[s].get(c);
|
|
4994
|
+
if (n === void 0) {
|
|
4995
|
+
n = goto.length;
|
|
4996
|
+
goto.push(/* @__PURE__ */ new Map());
|
|
4997
|
+
outs.push([]);
|
|
4998
|
+
goto[s].set(c, n);
|
|
4999
|
+
}
|
|
5000
|
+
s = n;
|
|
5001
|
+
}
|
|
5002
|
+
outs[s].push(rule);
|
|
5003
|
+
}
|
|
5004
|
+
});
|
|
5005
|
+
const nStates = goto.length;
|
|
5006
|
+
const next = new Int32Array(nStates << 7);
|
|
5007
|
+
const fail = new Int32Array(nStates);
|
|
5008
|
+
const queue = [];
|
|
5009
|
+
for (const [c, n] of goto[0]) {
|
|
5010
|
+
next[c] = n;
|
|
5011
|
+
queue.push(n);
|
|
5012
|
+
}
|
|
5013
|
+
for (let q = 0; q < queue.length; q++) {
|
|
5014
|
+
const s = queue[q];
|
|
5015
|
+
const f = fail[s];
|
|
5016
|
+
if (outs[f].length) outs[s] = [.../* @__PURE__ */ new Set([...outs[s], ...outs[f]])];
|
|
5017
|
+
const base = s << 7;
|
|
5018
|
+
const fbase = f << 7;
|
|
5019
|
+
for (let c = 0; c < 128; c++) next[base | c] = next[fbase | c];
|
|
5020
|
+
for (const [c, n] of goto[s]) {
|
|
5021
|
+
fail[n] = next[fbase | c];
|
|
5022
|
+
next[base | c] = n;
|
|
5023
|
+
queue.push(n);
|
|
5024
|
+
}
|
|
5025
|
+
}
|
|
5026
|
+
for (let s = 0; s < nStates; s++) {
|
|
5027
|
+
const base = s << 7;
|
|
5028
|
+
for (let c = 65; c <= 90; c++) next[base | c] = next[base | c + 32];
|
|
5029
|
+
}
|
|
5030
|
+
const hasOut = new Uint8Array(nStates);
|
|
5031
|
+
const outRules = outs.map((o, s) => {
|
|
5032
|
+
if (o.length === 0) return null;
|
|
5033
|
+
hasOut[s] = 1;
|
|
5034
|
+
return o;
|
|
5035
|
+
});
|
|
5036
|
+
const ruleStamp = new Uint32Array(literalsByRule.length);
|
|
5037
|
+
const stateStamp = new Uint32Array(nStates);
|
|
5038
|
+
let stamp = 0;
|
|
4666
5039
|
return {
|
|
4667
|
-
|
|
4668
|
-
|
|
4669
|
-
|
|
5040
|
+
alwaysRun,
|
|
5041
|
+
candidates(value, out) {
|
|
5042
|
+
stamp++;
|
|
5043
|
+
if (stamp === 4294967295) {
|
|
5044
|
+
ruleStamp.fill(0);
|
|
5045
|
+
stateStamp.fill(0);
|
|
5046
|
+
stamp = 1;
|
|
5047
|
+
}
|
|
5048
|
+
let s = 0;
|
|
5049
|
+
let found = false;
|
|
5050
|
+
for (let k = 0, n = value.length; k < n; k++) {
|
|
5051
|
+
const c = value.charCodeAt(k);
|
|
5052
|
+
s = c < 128 ? next[s << 7 | c] : 0;
|
|
5053
|
+
if (hasOut[s] === 1 && stateStamp[s] !== stamp) {
|
|
5054
|
+
stateStamp[s] = stamp;
|
|
5055
|
+
for (const r of outRules[s]) {
|
|
5056
|
+
if (ruleStamp[r] !== stamp) {
|
|
5057
|
+
ruleStamp[r] = stamp;
|
|
5058
|
+
out.push(r);
|
|
5059
|
+
found = true;
|
|
5060
|
+
}
|
|
5061
|
+
}
|
|
5062
|
+
}
|
|
5063
|
+
}
|
|
5064
|
+
if (found) out.sort((a, b) => a - b);
|
|
5065
|
+
}
|
|
4670
5066
|
};
|
|
4671
5067
|
}
|
|
4672
5068
|
|
|
@@ -13152,11 +13548,12 @@ function applyPattern(content, regex, groupCount, validate, prefixRun) {
|
|
|
13152
13548
|
result += content.slice(lastIndex);
|
|
13153
13549
|
return { value: result, count };
|
|
13154
13550
|
}
|
|
13155
|
-
function
|
|
13551
|
+
function redactStringSlow(value, patterns, owningKey = null, from = 0) {
|
|
13156
13552
|
let result = value;
|
|
13157
13553
|
let totalCount = 0;
|
|
13158
13554
|
let lower = null;
|
|
13159
|
-
for (
|
|
13555
|
+
for (let idx = from; idx < patterns.length; idx++) {
|
|
13556
|
+
const p7 = patterns[idx];
|
|
13160
13557
|
if (p7.precheck) {
|
|
13161
13558
|
if (lower === null) lower = result.toLowerCase();
|
|
13162
13559
|
let hit = false;
|
|
@@ -13184,6 +13581,69 @@ function redactString(value, patterns, owningKey = null) {
|
|
|
13184
13581
|
}
|
|
13185
13582
|
return { value: result, count: totalCount };
|
|
13186
13583
|
}
|
|
13584
|
+
var engines = /* @__PURE__ */ new WeakMap();
|
|
13585
|
+
var candidateScratch = [];
|
|
13586
|
+
function engineFor(patterns) {
|
|
13587
|
+
let engine = engines.get(patterns);
|
|
13588
|
+
if (!engine) {
|
|
13589
|
+
const minLength = new Int32Array(patterns.length);
|
|
13590
|
+
const literals = patterns.map((p7, idx) => {
|
|
13591
|
+
const facts = analyzeRegex(p7.regex.source, p7.regex.flags);
|
|
13592
|
+
minLength[idx] = facts.minLength;
|
|
13593
|
+
return p7.precheck ?? facts.literals;
|
|
13594
|
+
});
|
|
13595
|
+
engine = { prefilter: buildPrefilter(literals), minLength };
|
|
13596
|
+
engines.set(patterns, engine);
|
|
13597
|
+
}
|
|
13598
|
+
return engine;
|
|
13599
|
+
}
|
|
13600
|
+
function redactString(value, patterns, owningKey = null) {
|
|
13601
|
+
const { prefilter, minLength } = engineFor(patterns);
|
|
13602
|
+
const hits = candidateScratch;
|
|
13603
|
+
hits.length = 0;
|
|
13604
|
+
prefilter.candidates(value, hits);
|
|
13605
|
+
const always = prefilter.alwaysRun;
|
|
13606
|
+
let lower = null;
|
|
13607
|
+
let h = 0;
|
|
13608
|
+
let a = 0;
|
|
13609
|
+
while (h < hits.length || a < always.length) {
|
|
13610
|
+
const idx = a >= always.length || h < hits.length && hits[h] < always[a] ? hits[h++] : always[a++];
|
|
13611
|
+
if (value.length < minLength[idx]) continue;
|
|
13612
|
+
const p7 = patterns[idx];
|
|
13613
|
+
if (p7.precheck) {
|
|
13614
|
+
if (lower === null) lower = value.toLowerCase();
|
|
13615
|
+
let hit = false;
|
|
13616
|
+
for (const needle of p7.precheck) {
|
|
13617
|
+
if (lower.includes(needle)) {
|
|
13618
|
+
hit = true;
|
|
13619
|
+
break;
|
|
13620
|
+
}
|
|
13621
|
+
}
|
|
13622
|
+
if (!hit) continue;
|
|
13623
|
+
}
|
|
13624
|
+
if (p7.context) {
|
|
13625
|
+
const keyHit = owningKey !== null && p7.context.test(owningKey);
|
|
13626
|
+
if (!keyHit && (p7.keyOnlyContext || !p7.context.test(value))) continue;
|
|
13627
|
+
}
|
|
13628
|
+
const applied = applyPattern(
|
|
13629
|
+
value,
|
|
13630
|
+
p7.regex,
|
|
13631
|
+
p7.groupCount,
|
|
13632
|
+
p7.validate,
|
|
13633
|
+
p7.prefixRun
|
|
13634
|
+
);
|
|
13635
|
+
if (applied.count > 0) {
|
|
13636
|
+
const rest = redactStringSlow(
|
|
13637
|
+
applied.value,
|
|
13638
|
+
patterns,
|
|
13639
|
+
owningKey,
|
|
13640
|
+
idx + 1
|
|
13641
|
+
);
|
|
13642
|
+
return { value: rest.value, count: applied.count + rest.count };
|
|
13643
|
+
}
|
|
13644
|
+
}
|
|
13645
|
+
return { value, count: 0 };
|
|
13646
|
+
}
|
|
13187
13647
|
var keyMaskCaches = /* @__PURE__ */ new WeakMap();
|
|
13188
13648
|
var memoStringUnits = /* @__PURE__ */ new WeakMap();
|
|
13189
13649
|
var MAX_MEMO_STRING_UNITS = 8 * 1024 * 1024;
|
|
@@ -13248,6 +13708,16 @@ function walkAndRedactAll(value, patterns, memo, owningKey = null) {
|
|
|
13248
13708
|
}
|
|
13249
13709
|
return { value, count: 0 };
|
|
13250
13710
|
}
|
|
13711
|
+
function redactJsonlLine(line, patterns, memo) {
|
|
13712
|
+
if (!line.trim()) return { value: line, count: 0 };
|
|
13713
|
+
try {
|
|
13714
|
+
const parsed = JSON.parse(line);
|
|
13715
|
+
const walked = walkAndRedactAll(parsed, patterns, memo);
|
|
13716
|
+
return { value: JSON.stringify(walked.value), count: walked.count };
|
|
13717
|
+
} catch {
|
|
13718
|
+
return redactString(line, patterns);
|
|
13719
|
+
}
|
|
13720
|
+
}
|
|
13251
13721
|
function processFiles(files) {
|
|
13252
13722
|
const patterns = compilePatterns();
|
|
13253
13723
|
const results = [];
|
|
@@ -13257,19 +13727,10 @@ function processFiles(files) {
|
|
|
13257
13727
|
if (isJsonl) {
|
|
13258
13728
|
const memo = /* @__PURE__ */ new Map();
|
|
13259
13729
|
let count = 0;
|
|
13260
|
-
const
|
|
13261
|
-
|
|
13262
|
-
|
|
13263
|
-
|
|
13264
|
-
const parsed = JSON.parse(line);
|
|
13265
|
-
const walked = walkAndRedactAll(parsed, patterns, memo);
|
|
13266
|
-
count += walked.count;
|
|
13267
|
-
return JSON.stringify(walked.value);
|
|
13268
|
-
} catch {
|
|
13269
|
-
const r = redactString(line, patterns);
|
|
13270
|
-
count += r.count;
|
|
13271
|
-
return r.value;
|
|
13272
|
-
}
|
|
13730
|
+
const processed = content.split("\n").map((line) => {
|
|
13731
|
+
const r = redactJsonlLine(line, patterns, memo);
|
|
13732
|
+
count += r.count;
|
|
13733
|
+
return r.count > 0 ? r.value : line;
|
|
13273
13734
|
});
|
|
13274
13735
|
totalCount = count;
|
|
13275
13736
|
output = processed.join("\n");
|
|
@@ -13282,21 +13743,25 @@ function processFiles(files) {
|
|
|
13282
13743
|
}
|
|
13283
13744
|
return results;
|
|
13284
13745
|
}
|
|
13746
|
+
function processRanges(ranges) {
|
|
13747
|
+
const patterns = compilePatterns();
|
|
13748
|
+
return ranges.map(({ index, bytes, start, end }) => {
|
|
13749
|
+
const memo = /* @__PURE__ */ new Map();
|
|
13750
|
+
const buffer = Buffer.from(
|
|
13751
|
+
bytes.buffer,
|
|
13752
|
+
bytes.byteOffset,
|
|
13753
|
+
bytes.byteLength
|
|
13754
|
+
);
|
|
13755
|
+
const scanned = scanBufferLines(
|
|
13756
|
+
buffer,
|
|
13757
|
+
(line) => redactJsonlLine(line, patterns, memo),
|
|
13758
|
+
{ start, end }
|
|
13759
|
+
);
|
|
13760
|
+
return { index, ...scanned };
|
|
13761
|
+
});
|
|
13762
|
+
}
|
|
13285
13763
|
|
|
13286
13764
|
// src/middleware/pattern-redact.ts
|
|
13287
|
-
function redactPatternLine(line, patterns, memo, isJsonl) {
|
|
13288
|
-
if (isJsonl) {
|
|
13289
|
-
if (!line.trim()) return { value: line, count: 0 };
|
|
13290
|
-
try {
|
|
13291
|
-
const parsed = JSON.parse(line);
|
|
13292
|
-
const walked = walkAndRedactAll(parsed, patterns, memo);
|
|
13293
|
-
return { value: JSON.stringify(walked.value), count: walked.count };
|
|
13294
|
-
} catch {
|
|
13295
|
-
return redactString(line, patterns);
|
|
13296
|
-
}
|
|
13297
|
-
}
|
|
13298
|
-
return redactString(line, patterns);
|
|
13299
|
-
}
|
|
13300
13765
|
var WORKER_COUNT = Math.min(
|
|
13301
13766
|
os4.availableParallelism?.() ?? os4.cpus().length,
|
|
13302
13767
|
4
|
|
@@ -13304,6 +13769,31 @@ var WORKER_COUNT = Math.min(
|
|
|
13304
13769
|
var WORKER_THRESHOLD = 4;
|
|
13305
13770
|
var WORKER_STRING_THRESHOLD = 128 * 1024;
|
|
13306
13771
|
var WORKER_TIMEOUT_MS = 12e4;
|
|
13772
|
+
var RANGE_MIN_BYTES = 8 * 1024 * 1024;
|
|
13773
|
+
var NEWLINE2 = 10;
|
|
13774
|
+
function shareBytes(source) {
|
|
13775
|
+
if (source.buffer instanceof SharedArrayBuffer) return source;
|
|
13776
|
+
const shared = new Uint8Array(new SharedArrayBuffer(source.length));
|
|
13777
|
+
shared.set(source);
|
|
13778
|
+
return shared;
|
|
13779
|
+
}
|
|
13780
|
+
function splitRanges(index, bytes) {
|
|
13781
|
+
const parts = Math.min(
|
|
13782
|
+
WORKER_COUNT,
|
|
13783
|
+
Math.floor(bytes.length / RANGE_MIN_BYTES)
|
|
13784
|
+
);
|
|
13785
|
+
const ranges = [];
|
|
13786
|
+
let start = 0;
|
|
13787
|
+
for (let part = 1; part < parts; part++) {
|
|
13788
|
+
const target = Math.max(start, Math.floor(bytes.length / parts * part));
|
|
13789
|
+
const nl = bytes.indexOf(NEWLINE2, target);
|
|
13790
|
+
if (nl === -1 || nl + 1 >= bytes.length) break;
|
|
13791
|
+
ranges.push({ index, bytes, start, end: nl + 1 });
|
|
13792
|
+
start = nl + 1;
|
|
13793
|
+
}
|
|
13794
|
+
ranges.push({ index, bytes, start, end: bytes.length });
|
|
13795
|
+
return ranges;
|
|
13796
|
+
}
|
|
13307
13797
|
function resolveWorkerPath() {
|
|
13308
13798
|
return fileURLToPath(
|
|
13309
13799
|
new URL(
|
|
@@ -13312,7 +13802,7 @@ function resolveWorkerPath() {
|
|
|
13312
13802
|
)
|
|
13313
13803
|
);
|
|
13314
13804
|
}
|
|
13315
|
-
function runWorker(workerPath,
|
|
13805
|
+
function runWorker(workerPath, job, signal, timeoutMs = WORKER_TIMEOUT_MS) {
|
|
13316
13806
|
return new Promise((resolve, reject) => {
|
|
13317
13807
|
if (signal?.aborted) {
|
|
13318
13808
|
reject(
|
|
@@ -13320,7 +13810,7 @@ function runWorker(workerPath, files, signal, timeoutMs = WORKER_TIMEOUT_MS) {
|
|
|
13320
13810
|
);
|
|
13321
13811
|
return;
|
|
13322
13812
|
}
|
|
13323
|
-
const worker = new Worker(workerPath, { workerData:
|
|
13813
|
+
const worker = new Worker(workerPath, { workerData: job });
|
|
13324
13814
|
let settled = false;
|
|
13325
13815
|
const finish = (error, results) => {
|
|
13326
13816
|
if (settled) return;
|
|
@@ -13374,11 +13864,25 @@ var PatternRedactMiddleware = class {
|
|
|
13374
13864
|
}
|
|
13375
13865
|
const tasks = [];
|
|
13376
13866
|
const fileMap = /* @__PURE__ */ new Map();
|
|
13377
|
-
const passThrough =
|
|
13867
|
+
const passThrough = /* @__PURE__ */ new Map();
|
|
13378
13868
|
const largeResults = /* @__PURE__ */ new Map();
|
|
13869
|
+
const jsonl = /* @__PURE__ */ new Map();
|
|
13379
13870
|
for (let i = 0; i < group.files.length; i++) {
|
|
13380
13871
|
const file = group.files[i];
|
|
13381
13872
|
this.stats.filesScanned++;
|
|
13873
|
+
if (file.absolutePath.endsWith(".jsonl")) {
|
|
13874
|
+
const read2 = await readFileBuffer(file);
|
|
13875
|
+
if (read2.kind === "binary") {
|
|
13876
|
+
this.stats.binaryFiles++;
|
|
13877
|
+
passThrough.set(i, file);
|
|
13878
|
+
} else if (read2.kind === "error") {
|
|
13879
|
+
this.stats.filesSkipped++;
|
|
13880
|
+
} else {
|
|
13881
|
+
jsonl.set(i, read2.content);
|
|
13882
|
+
fileMap.set(i, file);
|
|
13883
|
+
}
|
|
13884
|
+
continue;
|
|
13885
|
+
}
|
|
13382
13886
|
if (file.content && !file.isBinary && file.content.length > getLargeFileThreshold()) {
|
|
13383
13887
|
largeResults.set(i, this.redactLargeFile(file));
|
|
13384
13888
|
continue;
|
|
@@ -13386,45 +13890,67 @@ var PatternRedactMiddleware = class {
|
|
|
13386
13890
|
const read = await readFileContent(file);
|
|
13387
13891
|
if (read.kind === "binary") {
|
|
13388
13892
|
this.stats.binaryFiles++;
|
|
13389
|
-
passThrough.
|
|
13893
|
+
passThrough.set(i, file);
|
|
13390
13894
|
continue;
|
|
13391
13895
|
}
|
|
13392
13896
|
if (read.kind === "error") {
|
|
13393
13897
|
this.stats.filesSkipped++;
|
|
13394
13898
|
continue;
|
|
13395
13899
|
}
|
|
13396
|
-
tasks.push({
|
|
13397
|
-
index: i,
|
|
13398
|
-
content: read.content,
|
|
13399
|
-
isJsonl: file.absolutePath.endsWith(".jsonl")
|
|
13400
|
-
});
|
|
13900
|
+
tasks.push({ index: i, content: read.content, isJsonl: false });
|
|
13401
13901
|
fileMap.set(i, file);
|
|
13402
13902
|
}
|
|
13403
|
-
|
|
13404
|
-
|
|
13405
|
-
|
|
13903
|
+
const sources2 = [...jsonl.values()];
|
|
13904
|
+
let output;
|
|
13905
|
+
if (tasks.length + sources2.length >= WORKER_THRESHOLD || tasks.some((task) => task.content.length >= WORKER_STRING_THRESHOLD) || sources2.some((source) => source.length >= WORKER_STRING_THRESHOLD)) {
|
|
13906
|
+
const ranges = [];
|
|
13907
|
+
for (const [index, source] of jsonl) {
|
|
13908
|
+
ranges.push(...splitRanges(index, shareBytes(source)));
|
|
13909
|
+
}
|
|
13910
|
+
output = await this.processWithWorkers(tasks, ranges);
|
|
13406
13911
|
} else {
|
|
13407
|
-
|
|
13912
|
+
output = {
|
|
13913
|
+
files: processFiles(tasks),
|
|
13914
|
+
ranges: processRanges(
|
|
13915
|
+
[...jsonl].map(([index, bytes]) => ({
|
|
13916
|
+
index,
|
|
13917
|
+
bytes,
|
|
13918
|
+
start: 0,
|
|
13919
|
+
end: bytes.length
|
|
13920
|
+
}))
|
|
13921
|
+
)
|
|
13922
|
+
};
|
|
13408
13923
|
}
|
|
13409
13924
|
const outputMap = /* @__PURE__ */ new Map();
|
|
13410
|
-
for (const r of
|
|
13411
|
-
|
|
13925
|
+
for (const r of output.files) outputMap.set(r.index, r);
|
|
13926
|
+
const scans = /* @__PURE__ */ new Map();
|
|
13927
|
+
for (const { index, edits, count, oversizedLines } of output.ranges) {
|
|
13928
|
+
const scan = scans.get(index);
|
|
13929
|
+
if (!scan) {
|
|
13930
|
+
scans.set(index, { edits, count, oversizedLines });
|
|
13931
|
+
continue;
|
|
13932
|
+
}
|
|
13933
|
+
for (const edit of edits) scan.edits.push(edit);
|
|
13934
|
+
scan.count += count;
|
|
13935
|
+
scan.oversizedLines += oversizedLines;
|
|
13412
13936
|
}
|
|
13413
13937
|
const newFiles = [];
|
|
13414
13938
|
for (let i = 0; i < group.files.length; i++) {
|
|
13415
|
-
const
|
|
13416
|
-
if (
|
|
13417
|
-
newFiles.push(
|
|
13939
|
+
const unchanged = largeResults.get(i) ?? passThrough.get(i);
|
|
13940
|
+
if (unchanged) {
|
|
13941
|
+
newFiles.push(unchanged);
|
|
13418
13942
|
continue;
|
|
13419
13943
|
}
|
|
13420
|
-
const
|
|
13421
|
-
if (
|
|
13422
|
-
|
|
13944
|
+
const file = fileMap.get(i);
|
|
13945
|
+
if (!file) continue;
|
|
13946
|
+
const source = jsonl.get(i);
|
|
13947
|
+
const scan = scans.get(i);
|
|
13948
|
+
if (source && scan) {
|
|
13949
|
+
newFiles.push(this.applyScan(file, source, scan));
|
|
13423
13950
|
continue;
|
|
13424
13951
|
}
|
|
13425
13952
|
const result = outputMap.get(i);
|
|
13426
|
-
|
|
13427
|
-
if (!result || !file) continue;
|
|
13953
|
+
if (!result) continue;
|
|
13428
13954
|
if (result.count > 0) {
|
|
13429
13955
|
this.stats.filesRedacted++;
|
|
13430
13956
|
this.stats.totalRedactions += result.count;
|
|
@@ -13438,17 +13964,31 @@ var PatternRedactMiddleware = class {
|
|
|
13438
13964
|
}
|
|
13439
13965
|
return { ...group, files: newFiles };
|
|
13440
13966
|
}
|
|
13441
|
-
|
|
13442
|
-
|
|
13443
|
-
|
|
13967
|
+
applyScan(file, source, scan) {
|
|
13968
|
+
if (scan.oversizedLines > 0) {
|
|
13969
|
+
appendLog(
|
|
13970
|
+
"warn",
|
|
13971
|
+
`pattern-redact: dropped ${scan.oversizedLines} oversized line(s) in ${file.absolutePath} (exceeded max string length)`
|
|
13972
|
+
);
|
|
13973
|
+
}
|
|
13974
|
+
if (scan.count > 0) {
|
|
13975
|
+
this.stats.filesRedacted++;
|
|
13976
|
+
this.stats.totalRedactions += scan.count;
|
|
13977
|
+
}
|
|
13978
|
+
if (scan.count === 0 && scan.oversizedLines === 0) return file;
|
|
13979
|
+
scan.edits.sort((a, b) => a.start - b.start);
|
|
13980
|
+
return { ...file, content: applyLineEdits(source, scan.edits) };
|
|
13981
|
+
}
|
|
13982
|
+
// Stream-redact an oversized non-JSONL file straight from its buffer, one line
|
|
13983
|
+
// at a time, so we never build a string larger than a single line.
|
|
13984
|
+
// Single-threaded (no worker pool) — it trades parallelism for bounded memory
|
|
13985
|
+
// on the one giant file.
|
|
13444
13986
|
redactLargeFile(file) {
|
|
13445
13987
|
const buffer = file.content;
|
|
13446
|
-
const isJsonl = file.absolutePath.endsWith(".jsonl");
|
|
13447
13988
|
const patterns = compilePatterns();
|
|
13448
|
-
const memo = /* @__PURE__ */ new Map();
|
|
13449
13989
|
const { content, count, oversizedLines } = redactBufferByLine(
|
|
13450
13990
|
buffer,
|
|
13451
|
-
(line) =>
|
|
13991
|
+
(line) => redactString(line, patterns)
|
|
13452
13992
|
);
|
|
13453
13993
|
if (oversizedLines > 0) {
|
|
13454
13994
|
appendLog(
|
|
@@ -13465,25 +14005,32 @@ var PatternRedactMiddleware = class {
|
|
|
13465
14005
|
}
|
|
13466
14006
|
return file;
|
|
13467
14007
|
}
|
|
13468
|
-
async processWithWorkers(tasks) {
|
|
14008
|
+
async processWithWorkers(tasks, ranges) {
|
|
13469
14009
|
const workerPath = resolveWorkerPath();
|
|
13470
14010
|
const controller = new AbortController();
|
|
13471
14011
|
const onAbort = () => controller.abort();
|
|
13472
14012
|
this.signal?.addEventListener("abort", onAbort, { once: true });
|
|
13473
14013
|
if (this.signal?.aborted) controller.abort();
|
|
13474
|
-
const
|
|
13475
|
-
(
|
|
13476
|
-
|
|
13477
|
-
|
|
13478
|
-
|
|
13479
|
-
|
|
13480
|
-
|
|
13481
|
-
|
|
13482
|
-
|
|
13483
|
-
|
|
13484
|
-
|
|
14014
|
+
const units = [
|
|
14015
|
+
...tasks.map((task) => ({ task, size: task.content.length })),
|
|
14016
|
+
...ranges.map((range) => ({ range, size: range.end - range.start }))
|
|
14017
|
+
].sort((a, b) => b.size - a.size);
|
|
14018
|
+
const jobs = Array.from({ length: WORKER_COUNT }, () => ({
|
|
14019
|
+
files: [],
|
|
14020
|
+
ranges: []
|
|
14021
|
+
}));
|
|
14022
|
+
units.forEach((unit, i) => {
|
|
14023
|
+
const job = jobs[i % WORKER_COUNT];
|
|
14024
|
+
if ("task" in unit) job.files.push(unit.task);
|
|
14025
|
+
else job.ranges.push(unit.range);
|
|
14026
|
+
});
|
|
14027
|
+
const pending = jobs.filter((job) => job.files.length > 0 || job.ranges.length > 0).map((job) => runWorker(workerPath, job, controller.signal));
|
|
13485
14028
|
try {
|
|
13486
|
-
|
|
14029
|
+
const outputs2 = await Promise.all(pending);
|
|
14030
|
+
return {
|
|
14031
|
+
files: outputs2.flatMap((o) => o.files),
|
|
14032
|
+
ranges: outputs2.flatMap((o) => o.ranges)
|
|
14033
|
+
};
|
|
13487
14034
|
} catch (error) {
|
|
13488
14035
|
controller.abort();
|
|
13489
14036
|
await Promise.allSettled(pending);
|
|
@@ -13555,8 +14102,20 @@ var RedactMiddleware = class {
|
|
|
13555
14102
|
for (const file of group.files) {
|
|
13556
14103
|
this.stats.filesScanned++;
|
|
13557
14104
|
regex.lastIndex = 0;
|
|
14105
|
+
if (file.absolutePath.endsWith(".jsonl")) {
|
|
14106
|
+
const read2 = await readFileBuffer(file);
|
|
14107
|
+
if (read2.kind === "binary") {
|
|
14108
|
+
this.stats.binaryFiles++;
|
|
14109
|
+
newFiles.push(file);
|
|
14110
|
+
} else if (read2.kind === "error") {
|
|
14111
|
+
this.stats.filesSkipped++;
|
|
14112
|
+
} else {
|
|
14113
|
+
newFiles.push(this.redactBuffer(file, read2.content, regex, true));
|
|
14114
|
+
}
|
|
14115
|
+
continue;
|
|
14116
|
+
}
|
|
13558
14117
|
if (file.content && !file.isBinary && file.content.length > getLargeFileThreshold()) {
|
|
13559
|
-
newFiles.push(this.
|
|
14118
|
+
newFiles.push(this.redactBuffer(file, file.content, regex, false));
|
|
13560
14119
|
continue;
|
|
13561
14120
|
}
|
|
13562
14121
|
const read = await readFileContent(file);
|
|
@@ -13569,18 +14128,7 @@ var RedactMiddleware = class {
|
|
|
13569
14128
|
this.stats.filesSkipped++;
|
|
13570
14129
|
continue;
|
|
13571
14130
|
}
|
|
13572
|
-
const
|
|
13573
|
-
let result;
|
|
13574
|
-
let count;
|
|
13575
|
-
if (file.absolutePath.endsWith(".jsonl")) {
|
|
13576
|
-
const processed = this.redactJsonl(content, regex);
|
|
13577
|
-
result = processed.result;
|
|
13578
|
-
count = processed.count;
|
|
13579
|
-
} else {
|
|
13580
|
-
const processed = this.redactString(content, regex);
|
|
13581
|
-
result = processed.result;
|
|
13582
|
-
count = processed.count;
|
|
13583
|
-
}
|
|
14131
|
+
const { result, count } = this.redactString(read.content, regex);
|
|
13584
14132
|
if (count > 0) {
|
|
13585
14133
|
this.stats.filesRedacted++;
|
|
13586
14134
|
this.stats.totalRedactions += count;
|
|
@@ -13594,13 +14142,9 @@ var RedactMiddleware = class {
|
|
|
13594
14142
|
}
|
|
13595
14143
|
return { ...group, files: newFiles };
|
|
13596
14144
|
}
|
|
13597
|
-
//
|
|
13598
|
-
//
|
|
13599
|
-
|
|
13600
|
-
// string — which holds for real transcripts (largest line is a few MiB).
|
|
13601
|
-
redactLargeFile(file, regex) {
|
|
13602
|
-
const buffer = file.content;
|
|
13603
|
-
const isJsonl = file.absolutePath.endsWith(".jsonl");
|
|
14145
|
+
// Redact a file line by line straight from its buffer. A line with no hit
|
|
14146
|
+
// keeps its exact bytes; a JSONL line with one is re-serialized.
|
|
14147
|
+
redactBuffer(file, buffer, regex, isJsonl) {
|
|
13604
14148
|
const { content, count, oversizedLines } = redactBufferByLine(
|
|
13605
14149
|
buffer,
|
|
13606
14150
|
(line) => this.redactLine(line, regex, isJsonl)
|
|
@@ -13635,24 +14179,6 @@ var RedactMiddleware = class {
|
|
|
13635
14179
|
const { result, count } = this.redactString(line, regex);
|
|
13636
14180
|
return { value: result, count };
|
|
13637
14181
|
}
|
|
13638
|
-
redactJsonl(content, regex) {
|
|
13639
|
-
let totalCount = 0;
|
|
13640
|
-
const lines = content.split("\n");
|
|
13641
|
-
const processed = lines.map((line) => {
|
|
13642
|
-
if (!line.trim()) return line;
|
|
13643
|
-
try {
|
|
13644
|
-
const parsed = JSON.parse(line);
|
|
13645
|
-
const walked = walkAndRedact(parsed, regex);
|
|
13646
|
-
totalCount += walked.count;
|
|
13647
|
-
return JSON.stringify(walked.value);
|
|
13648
|
-
} catch {
|
|
13649
|
-
const { result, count } = this.redactString(line, regex);
|
|
13650
|
-
totalCount += count;
|
|
13651
|
-
return result;
|
|
13652
|
-
}
|
|
13653
|
-
});
|
|
13654
|
-
return { result: processed.join("\n"), count: totalCount };
|
|
13655
|
-
}
|
|
13656
14182
|
redactString(content, regex) {
|
|
13657
14183
|
let count = 0;
|
|
13658
14184
|
const result = content.replace(regex, () => {
|
|
@@ -14662,46 +15188,6 @@ import fs17 from "fs";
|
|
|
14662
15188
|
import os7 from "os";
|
|
14663
15189
|
import path16 from "path";
|
|
14664
15190
|
import { createGzip } from "zlib";
|
|
14665
|
-
|
|
14666
|
-
// src/git-traces/timing.ts
|
|
14667
|
-
import { performance as performance2 } from "perf_hooks";
|
|
14668
|
-
function startCaptureStage(stage, context, startedAt = Date.now()) {
|
|
14669
|
-
const started = performance2.now();
|
|
14670
|
-
appendLog(
|
|
14671
|
-
"info",
|
|
14672
|
-
`git-traces: capture timing (${context}, stage=${stage}, status=started, startedAt=${startedAt})`
|
|
14673
|
-
);
|
|
14674
|
-
return (status, details) => {
|
|
14675
|
-
appendLog(
|
|
14676
|
-
"info",
|
|
14677
|
-
`git-traces: capture timing (${context}, stage=${stage}, status=${status}, elapsedMs=${Math.round(performance2.now() - started)}, startedAt=${startedAt}, finishedAt=${Date.now()}${details ? `, ${details}` : ""})`
|
|
14678
|
-
);
|
|
14679
|
-
};
|
|
14680
|
-
}
|
|
14681
|
-
async function timeAsyncCaptureStage(stage, context, run) {
|
|
14682
|
-
const finish = startCaptureStage(stage, context);
|
|
14683
|
-
let status = "failed";
|
|
14684
|
-
try {
|
|
14685
|
-
const result = await run();
|
|
14686
|
-
status = "ok";
|
|
14687
|
-
return result;
|
|
14688
|
-
} finally {
|
|
14689
|
-
finish(status);
|
|
14690
|
-
}
|
|
14691
|
-
}
|
|
14692
|
-
function timeCaptureStage(repoRoot, stage, run) {
|
|
14693
|
-
const finish = startCaptureStage(stage, `repo=${repoRoot}`);
|
|
14694
|
-
let status = "failed";
|
|
14695
|
-
try {
|
|
14696
|
-
const result = run();
|
|
14697
|
-
status = "ok";
|
|
14698
|
-
return result;
|
|
14699
|
-
} finally {
|
|
14700
|
-
finish(status);
|
|
14701
|
-
}
|
|
14702
|
-
}
|
|
14703
|
-
|
|
14704
|
-
// src/git-traces/git-ops.ts
|
|
14705
15191
|
var DEFAULT_GIT_COMMAND_TIMEOUT_MS = 12e4;
|
|
14706
15192
|
var DEFAULT_BUNDLE_TIMEOUT_MS = 3e5;
|
|
14707
15193
|
var FULL_TREE_SCAN_TIMEOUT_MS = 3e5;
|
|
@@ -15042,9 +15528,11 @@ function isGitRepo(dir) {
|
|
|
15042
15528
|
function stashRetryReason(failure) {
|
|
15043
15529
|
const status = failure.gitExitStatus;
|
|
15044
15530
|
if (status !== 1 && status !== 128) return null;
|
|
15045
|
-
|
|
15046
|
-
|
|
15047
|
-
|
|
15531
|
+
const stderr = failure.gitStderr ?? "";
|
|
15532
|
+
if (/Unable to create [^\n]*index\.lock[^\n]*: File exists/.test(stderr)) {
|
|
15533
|
+
return "index-lock-contention";
|
|
15534
|
+
}
|
|
15535
|
+
if (status === 1 && stderr.trim() === "error: could not write index") {
|
|
15048
15536
|
return "index-lock-contention";
|
|
15049
15537
|
}
|
|
15050
15538
|
if (status === 1 && !failure.gitStderr && !failure.gitStdout) {
|
|
@@ -15052,7 +15540,60 @@ function stashRetryReason(failure) {
|
|
|
15052
15540
|
}
|
|
15053
15541
|
return null;
|
|
15054
15542
|
}
|
|
15543
|
+
var PRIVATE_INDEX_ENV = "HILLCLIMB_GIT_PRIVATE_INDEX";
|
|
15544
|
+
function withPrivateIndex(repoRoot, run) {
|
|
15545
|
+
if (process.env[PRIVATE_INDEX_ENV] === "0") return run(null);
|
|
15546
|
+
let tmpDir = null;
|
|
15547
|
+
let copy = null;
|
|
15548
|
+
try {
|
|
15549
|
+
const indexPath = path16.resolve(
|
|
15550
|
+
repoRoot,
|
|
15551
|
+
git(repoRoot, ["rev-parse", "--git-path", "index"])
|
|
15552
|
+
);
|
|
15553
|
+
tmpDir = fs17.mkdtempSync(path16.join(os7.tmpdir(), "hillclimb-index-"));
|
|
15554
|
+
copy = path16.join(tmpDir, "index");
|
|
15555
|
+
const fd = fs17.openSync(indexPath, "r");
|
|
15556
|
+
let stat;
|
|
15557
|
+
let data;
|
|
15558
|
+
try {
|
|
15559
|
+
stat = fs17.fstatSync(fd);
|
|
15560
|
+
data = fs17.readFileSync(fd);
|
|
15561
|
+
} finally {
|
|
15562
|
+
fs17.closeSync(fd);
|
|
15563
|
+
}
|
|
15564
|
+
fs17.writeFileSync(copy, data);
|
|
15565
|
+
fs17.utimesSync(copy, stat.atime, stat.mtime);
|
|
15566
|
+
} catch (err) {
|
|
15567
|
+
const missing = err.code === "ENOENT";
|
|
15568
|
+
appendLog(
|
|
15569
|
+
missing ? "info" : "warn",
|
|
15570
|
+
`git-traces: private index unavailable; capturing on the repository index (repo=${repoRoot}, reason=${err instanceof Error ? err.message : String(err)})`
|
|
15571
|
+
);
|
|
15572
|
+
copy = null;
|
|
15573
|
+
}
|
|
15574
|
+
try {
|
|
15575
|
+
return run(copy ? privateIndexEnv(copy) : null);
|
|
15576
|
+
} finally {
|
|
15577
|
+
if (tmpDir) fs17.rmSync(tmpDir, { recursive: true, force: true });
|
|
15578
|
+
}
|
|
15579
|
+
}
|
|
15580
|
+
function privateIndexEnv(indexPath) {
|
|
15581
|
+
const existing = Number(process.env.GIT_CONFIG_COUNT);
|
|
15582
|
+
const slot = Number.isInteger(existing) && existing > 0 ? existing : 0;
|
|
15583
|
+
return {
|
|
15584
|
+
GIT_INDEX_FILE: indexPath,
|
|
15585
|
+
GIT_CONFIG_COUNT: String(slot + 1),
|
|
15586
|
+
[`GIT_CONFIG_KEY_${slot}`]: "core.splitIndex",
|
|
15587
|
+
[`GIT_CONFIG_VALUE_${slot}`]: "false"
|
|
15588
|
+
};
|
|
15589
|
+
}
|
|
15055
15590
|
function captureWorkingCommitSha(repoRoot) {
|
|
15591
|
+
return withPrivateIndex(
|
|
15592
|
+
repoRoot,
|
|
15593
|
+
(indexEnv) => captureWorkingCommitShaOnIndex(repoRoot, indexEnv)
|
|
15594
|
+
);
|
|
15595
|
+
}
|
|
15596
|
+
function captureWorkingCommitShaOnIndex(repoRoot, indexEnv) {
|
|
15056
15597
|
for (let attempt = 1; ; attempt++) {
|
|
15057
15598
|
let sha;
|
|
15058
15599
|
try {
|
|
@@ -15063,6 +15604,7 @@ function captureWorkingCommitSha(repoRoot) {
|
|
|
15063
15604
|
// Keep the two expected stderr classifications independent of locale.
|
|
15064
15605
|
gitWithEnv(repoRoot, ["stash", "create"], {
|
|
15065
15606
|
...collectorIdentityEnv(),
|
|
15607
|
+
...indexEnv,
|
|
15066
15608
|
LC_ALL: "C"
|
|
15067
15609
|
})
|
|
15068
15610
|
)
|
|
@@ -15075,7 +15617,10 @@ function captureWorkingCommitSha(repoRoot) {
|
|
|
15075
15617
|
"info",
|
|
15076
15618
|
`git-traces: stash create unavailable (repo=${repoRoot}, reason=unborn-head); capturing index tree`
|
|
15077
15619
|
);
|
|
15078
|
-
const tree =
|
|
15620
|
+
const tree = gitWithEnv(repoRoot, ["write-tree"], {
|
|
15621
|
+
...process.env,
|
|
15622
|
+
...indexEnv
|
|
15623
|
+
});
|
|
15079
15624
|
return commitTree(repoRoot, tree, "empty baseline");
|
|
15080
15625
|
}
|
|
15081
15626
|
const retryReason = stashRetryReason(failure);
|
|
@@ -15777,7 +16322,10 @@ function buildBaselineMetadata(repoRoot, sessionId, tool, baselineSha, cliVersio
|
|
|
15777
16322
|
const headSha = safeGit(repoRoot, ["rev-parse", "HEAD"]) ?? "unknown";
|
|
15778
16323
|
const branch = safeGit(repoRoot, ["rev-parse", "--abbrev-ref", "HEAD"]) ?? null;
|
|
15779
16324
|
const remoteUrl = safeGit(repoRoot, ["config", "--get", "remote.origin.url"]) ?? null;
|
|
15780
|
-
const statusRaw =
|
|
16325
|
+
const statusRaw = withPrivateIndex(
|
|
16326
|
+
repoRoot,
|
|
16327
|
+
(indexEnv) => safeGitBuffer(repoRoot, ["status", "--porcelain=v1", "-z"], indexEnv)
|
|
16328
|
+
) ?? Buffer.alloc(0);
|
|
15781
16329
|
const dirtyFiles = parseDirtyFilesFromStatus(statusRaw);
|
|
15782
16330
|
const authorName = safeGit(repoRoot, ["config", "user.name"]) ?? "";
|
|
15783
16331
|
const authorEmail = safeGit(repoRoot, ["config", "user.email"]) ?? "";
|
|
@@ -15926,9 +16474,13 @@ function safeGit(repoRoot, args) {
|
|
|
15926
16474
|
return null;
|
|
15927
16475
|
}
|
|
15928
16476
|
}
|
|
15929
|
-
function safeGitBuffer(repoRoot, args) {
|
|
16477
|
+
function safeGitBuffer(repoRoot, args, env = null) {
|
|
15930
16478
|
try {
|
|
15931
|
-
return gitBuffer(
|
|
16479
|
+
return gitBuffer(
|
|
16480
|
+
repoRoot,
|
|
16481
|
+
args,
|
|
16482
|
+
env ? { env: { ...process.env, ...env } } : {}
|
|
16483
|
+
);
|
|
15932
16484
|
} catch (err) {
|
|
15933
16485
|
if (isGitTimeoutError(err)) {
|
|
15934
16486
|
appendLog(
|
|
@@ -16837,7 +17389,7 @@ async function uploadArtifact(client, contributionId, filename, mimeType, buffer
|
|
|
16837
17389
|
}
|
|
16838
17390
|
|
|
16839
17391
|
// package.json
|
|
16840
|
-
var version = "0.9.
|
|
17392
|
+
var version = "0.9.4";
|
|
16841
17393
|
|
|
16842
17394
|
// src/version.ts
|
|
16843
17395
|
var CLI_VERSION = version;
|
|
@@ -16978,8 +17530,14 @@ function isPhantomClaudeHook(payload, toolOverride) {
|
|
|
16978
17530
|
}
|
|
16979
17531
|
var MAX_FORK_SWEEP_THREADS = 8;
|
|
16980
17532
|
async function runUploadInner(payload, options = {}) {
|
|
16981
|
-
|
|
16982
|
-
|
|
17533
|
+
let outcome;
|
|
17534
|
+
let failure;
|
|
17535
|
+
try {
|
|
17536
|
+
outcome = await runUploadForSession(payload, options);
|
|
17537
|
+
} catch (err) {
|
|
17538
|
+
failure = err;
|
|
17539
|
+
}
|
|
17540
|
+
const settled = failure !== void 0 || outcome === "uploaded" || outcome === "unchanged" || outcome === "skipped";
|
|
16983
17541
|
if (!options.sweptFrom && payload.tool === "codex" && settled) {
|
|
16984
17542
|
await sweepCodexForkTranscripts(payload);
|
|
16985
17543
|
}
|
|
@@ -16989,6 +17547,7 @@ async function runUploadInner(payload, options = {}) {
|
|
|
16989
17547
|
if (!options.sweptFrom && settled) {
|
|
16990
17548
|
await sweepStrandedAgentUploads(resolveHookSessionId(payload));
|
|
16991
17549
|
}
|
|
17550
|
+
if (failure !== void 0) throw failure;
|
|
16992
17551
|
return outcome;
|
|
16993
17552
|
}
|
|
16994
17553
|
var MAX_SUBAGENT_SWEEP = 32;
|