@gitroll/tacitt-cli 0.0.0-stage → 0.1.0
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/README.md +84 -2
- package/bin/INDEX.md +9 -0
- package/bin/tacitt.cjs +16 -0
- package/dist/agent-setup-B85GaVIz.mjs +71 -0
- package/dist/agent-setup-worker.mjs +56 -0
- package/dist/cli.mjs +1343 -0
- package/dist/codex-title-worker.mjs +215 -0
- package/dist/configured-preview-worker.mjs +133 -0
- package/dist/configured-session-worker.mjs +117 -0
- package/dist/frame-D1j9R8xX.mjs +84 -0
- package/dist/heap-1FYhGds4.mjs +75 -0
- package/dist/heap-tfU61Ehy.mjs +7253 -0
- package/dist/job--TceKk0l.mjs +194 -0
- package/dist/locate-Bn32k2se.mjs +10647 -0
- package/dist/model-7Nu7szNP.mjs +41 -0
- package/dist/operation-executor.mjs +181 -0
- package/dist/preview-worker.mjs +22 -0
- package/dist/privacy-validator.mjs +46 -0
- package/dist/query-Da-KDbSV.mjs +14511 -0
- package/dist/run-CCATsHS2.mjs +916 -0
- package/dist/session-process.mjs +90 -0
- package/dist/src-BngGpFKd.mjs +16416 -0
- package/dist/src-CPPjOyHZ.mjs +12782 -0
- package/dist/src-DynBRDBc.mjs +6590 -0
- package/dist/store-database-worker.mjs +14 -0
- package/dist/web/THIRD_PARTY_LICENSES.md +388 -0
- package/dist/web/assets/ibm-plex-mono-cyrillic-400-normal-BSMlKf0J.woff2 +0 -0
- package/dist/web/assets/ibm-plex-mono-cyrillic-400-normal-CEL4l2ZJ.woff +0 -0
- package/dist/web/assets/ibm-plex-mono-cyrillic-500-normal-Ael50iVv.woff +0 -0
- package/dist/web/assets/ibm-plex-mono-cyrillic-500-normal-Bq9vWWag.woff2 +0 -0
- package/dist/web/assets/ibm-plex-mono-cyrillic-ext-400-normal-DMdlQ8Kv.woff +0 -0
- package/dist/web/assets/ibm-plex-mono-cyrillic-ext-400-normal-xuaO2J-f.woff2 +0 -0
- package/dist/web/assets/ibm-plex-mono-cyrillic-ext-500-normal-BIfNGwUT.woff +0 -0
- package/dist/web/assets/ibm-plex-mono-cyrillic-ext-500-normal-BqneJy0T.woff2 +0 -0
- package/dist/web/assets/ibm-plex-mono-latin-400-normal-CvHOgSBP.woff +0 -0
- package/dist/web/assets/ibm-plex-mono-latin-400-normal-DMJ8VG8y.woff2 +0 -0
- package/dist/web/assets/ibm-plex-mono-latin-500-normal-CB9ihrfo.woff +0 -0
- package/dist/web/assets/ibm-plex-mono-latin-500-normal-DSY6xOcd.woff2 +0 -0
- package/dist/web/assets/ibm-plex-mono-latin-ext-400-normal-BmRBH3aV.woff2 +0 -0
- package/dist/web/assets/ibm-plex-mono-latin-ext-400-normal-D3D2R8hC.woff +0 -0
- package/dist/web/assets/ibm-plex-mono-latin-ext-500-normal-CAhNIIs5.woff2 +0 -0
- package/dist/web/assets/ibm-plex-mono-latin-ext-500-normal-CZ70TYgx.woff +0 -0
- package/dist/web/assets/ibm-plex-mono-vietnamese-400-normal-BulugwFq.woff2 +0 -0
- package/dist/web/assets/ibm-plex-mono-vietnamese-400-normal-DDuiU_S-.woff +0 -0
- package/dist/web/assets/ibm-plex-mono-vietnamese-500-normal-C8zxqsMH.woff +0 -0
- package/dist/web/assets/ibm-plex-mono-vietnamese-500-normal-DZ4AoWbu.woff2 +0 -0
- package/dist/web/assets/ibm-plex-sans-cyrillic-400-normal-BTotfTJu.woff +0 -0
- package/dist/web/assets/ibm-plex-sans-cyrillic-400-normal-DZqxrq2p.woff2 +0 -0
- package/dist/web/assets/ibm-plex-sans-cyrillic-500-normal-ByOcLdNv.woff +0 -0
- package/dist/web/assets/ibm-plex-sans-cyrillic-500-normal-CocWQlwt.woff2 +0 -0
- package/dist/web/assets/ibm-plex-sans-cyrillic-600-normal-71GNu3SW.woff2 +0 -0
- package/dist/web/assets/ibm-plex-sans-cyrillic-600-normal-BGq0mW3O.woff +0 -0
- package/dist/web/assets/ibm-plex-sans-cyrillic-700-normal-D_uavGvn.woff2 +0 -0
- package/dist/web/assets/ibm-plex-sans-cyrillic-700-normal-RFb1v-CK.woff +0 -0
- package/dist/web/assets/ibm-plex-sans-cyrillic-ext-400-normal-Dsrv2Tcn.woff +0 -0
- package/dist/web/assets/ibm-plex-sans-cyrillic-ext-400-normal-g30qAdWV.woff2 +0 -0
- package/dist/web/assets/ibm-plex-sans-cyrillic-ext-500-normal-Cs5J6C77.woff2 +0 -0
- package/dist/web/assets/ibm-plex-sans-cyrillic-ext-500-normal-DB5PtV2g.woff +0 -0
- package/dist/web/assets/ibm-plex-sans-cyrillic-ext-600-normal-Bz0x94Yp.woff +0 -0
- package/dist/web/assets/ibm-plex-sans-cyrillic-ext-600-normal-DUMzJB7m.woff2 +0 -0
- package/dist/web/assets/ibm-plex-sans-cyrillic-ext-700-normal-BmRLXWpJ.woff2 +0 -0
- package/dist/web/assets/ibm-plex-sans-cyrillic-ext-700-normal-DCQwxkRk.woff +0 -0
- package/dist/web/assets/ibm-plex-sans-greek-400-normal-D9ESIMu3.woff +0 -0
- package/dist/web/assets/ibm-plex-sans-greek-400-normal-_efipK4i.woff2 +0 -0
- package/dist/web/assets/ibm-plex-sans-greek-500-normal-CuWXN6rf.woff +0 -0
- package/dist/web/assets/ibm-plex-sans-greek-500-normal-JMMifIXV.woff2 +0 -0
- package/dist/web/assets/ibm-plex-sans-greek-600-normal-D-CqTdkO.woff +0 -0
- package/dist/web/assets/ibm-plex-sans-greek-600-normal-DzTrcv_p.woff2 +0 -0
- package/dist/web/assets/ibm-plex-sans-greek-700-normal-CkZN0sRy.woff +0 -0
- package/dist/web/assets/ibm-plex-sans-greek-700-normal-wyJ6jzdA.woff2 +0 -0
- package/dist/web/assets/ibm-plex-sans-latin-400-normal-CDDApCn2.woff2 +0 -0
- package/dist/web/assets/ibm-plex-sans-latin-400-normal-CYLoc0-x.woff +0 -0
- package/dist/web/assets/ibm-plex-sans-latin-500-normal-6ng42L7E.woff2 +0 -0
- package/dist/web/assets/ibm-plex-sans-latin-500-normal-BgVn5rGT.woff +0 -0
- package/dist/web/assets/ibm-plex-sans-latin-600-normal-Cu4Hd6ag.woff +0 -0
- package/dist/web/assets/ibm-plex-sans-latin-600-normal-CuJfVYMP.woff2 +0 -0
- package/dist/web/assets/ibm-plex-sans-latin-700-normal-Bth3BMcD.woff +0 -0
- package/dist/web/assets/ibm-plex-sans-latin-700-normal-Bxkt5Cjx.woff2 +0 -0
- package/dist/web/assets/ibm-plex-sans-latin-ext-400-normal-C5H60-Va.woff2 +0 -0
- package/dist/web/assets/ibm-plex-sans-latin-ext-400-normal-RBey6euL.woff +0 -0
- package/dist/web/assets/ibm-plex-sans-latin-ext-500-normal-D0aIdm-b.woff +0 -0
- package/dist/web/assets/ibm-plex-sans-latin-ext-500-normal-DakdToA3.woff2 +0 -0
- package/dist/web/assets/ibm-plex-sans-latin-ext-600-normal-DIrixKbi.woff +0 -0
- package/dist/web/assets/ibm-plex-sans-latin-ext-600-normal-DOrvGEcy.woff2 +0 -0
- package/dist/web/assets/ibm-plex-sans-latin-ext-700-normal-DZH6Xh-4.woff +0 -0
- package/dist/web/assets/ibm-plex-sans-latin-ext-700-normal-aMgXseD4.woff2 +0 -0
- package/dist/web/assets/ibm-plex-sans-vietnamese-400-normal-DG4YqDda.woff2 +0 -0
- package/dist/web/assets/ibm-plex-sans-vietnamese-400-normal-fK1oJ5dG.woff +0 -0
- package/dist/web/assets/ibm-plex-sans-vietnamese-500-normal-BEb3_waV.woff +0 -0
- package/dist/web/assets/ibm-plex-sans-vietnamese-500-normal-e4dixQRQ.woff2 +0 -0
- package/dist/web/assets/ibm-plex-sans-vietnamese-600-normal-DgdngZtN.woff +0 -0
- package/dist/web/assets/ibm-plex-sans-vietnamese-600-normal-DpPYBSTl.woff2 +0 -0
- package/dist/web/assets/ibm-plex-sans-vietnamese-700-normal-BV5B8Are.woff +0 -0
- package/dist/web/assets/ibm-plex-sans-vietnamese-700-normal-CkEo6LDy.woff2 +0 -0
- package/dist/web/assets/index-CWQ05PFl.js +67 -0
- package/dist/web/assets/index-J-AWt_cW.css +1 -0
- package/dist/web/index.html +14 -0
- package/package.json +56 -3
- package/skills/INDEX.md +9 -0
- package/skills/tacitt/INDEX.md +11 -0
- package/skills/tacitt/SKILL.md +42 -0
- package/skills/tacitt/references/INDEX.md +15 -0
- package/skills/tacitt/references/b-method.md +22 -0
- package/skills/tacitt/references/bootstrap.md +109 -0
- package/skills/tacitt/references/classification.md +83 -0
- package/skills/tacitt/references/commands.md +147 -0
- package/skills/tacitt/references/journey.md +52 -0
- package/skills/tacitt/references/parallel-review.md +64 -0
- package/skills/tacitt/references/send.md +30 -0
- package/skills/tacitt/scripts/INDEX.md +43 -0
- package/skills/tacitt/scripts/answer.mjs +257 -0
- package/skills/tacitt/scripts/describe.mjs +55 -0
- package/skills/tacitt/scripts/journey-card-consolidation.d.mts +8 -0
- package/skills/tacitt/scripts/journey-card-consolidation.mjs +146 -0
- package/skills/tacitt/scripts/journey-classification-metadata.d.mts +16 -0
- package/skills/tacitt/scripts/journey-classification-metadata.mjs +111 -0
- package/skills/tacitt/scripts/journey-classification-progress.mjs +194 -0
- package/skills/tacitt/scripts/journey-classification-tasks.d.mts +18 -0
- package/skills/tacitt/scripts/journey-classification-tasks.mjs +345 -0
- package/skills/tacitt/scripts/journey-classification-validation.d.mts +20 -0
- package/skills/tacitt/scripts/journey-classification-validation.mjs +219 -0
- package/skills/tacitt/scripts/journey-classification.mjs +145 -0
- package/skills/tacitt/scripts/journey-consolidation-submit.mjs +190 -0
- package/skills/tacitt/scripts/journey-consolidation.d.mts +9 -0
- package/skills/tacitt/scripts/journey-consolidation.mjs +224 -0
- package/skills/tacitt/scripts/journey-scan-timing.mjs +143 -0
- package/skills/tacitt/scripts/journey-state.d.mts +42 -0
- package/skills/tacitt/scripts/journey-state.mjs +268 -0
- package/skills/tacitt/scripts/journey-tasks.d.mts +26 -0
- package/skills/tacitt/scripts/journey-tasks.mjs +183 -0
- package/skills/tacitt/scripts/journey.d.mts +38 -0
- package/skills/tacitt/scripts/journey.mjs +451 -0
- package/skills/tacitt/scripts/local-cli.d.mts +18 -0
- package/skills/tacitt/scripts/local-cli.mjs +116 -0
- package/skills/tacitt/scripts/privacy-validator.mjs +10350 -0
- package/skills/tacitt/scripts/review-stream-client.d.mts +22 -0
- package/skills/tacitt/scripts/review-stream-client.mjs +194 -0
- package/skills/tacitt/scripts/review.d.mts +87 -0
- package/skills/tacitt/scripts/review.mjs +1589 -0
- package/skills/tacitt/scripts/verify-cli.mjs +10 -0
|
@@ -0,0 +1,916 @@
|
|
|
1
|
+
import { tn as sessionSchema } from "./locate-Bn32k2se.mjs";
|
|
2
|
+
import { t as createZstdFrameCheck$1 } from "./frame-D1j9R8xX.mjs";
|
|
3
|
+
import "./src-CPPjOyHZ.mjs";
|
|
4
|
+
import { a as isRecord } from "./model-7Nu7szNP.mjs";
|
|
5
|
+
import { constants } from "node:fs";
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
import { lstat, open, stat } from "node:fs/promises";
|
|
8
|
+
import { isIP } from "node:net";
|
|
9
|
+
import { dirname, isAbsolute, relative, resolve, sep } from "node:path";
|
|
10
|
+
import { PassThrough, Readable } from "node:stream";
|
|
11
|
+
import { createHash } from "node:crypto";
|
|
12
|
+
import { pipeline } from "node:stream/promises";
|
|
13
|
+
import { constants as constants$1, createZstdDecompress } from "node:zlib";
|
|
14
|
+
import { getHeapStatistics } from "node:v8";
|
|
15
|
+
import { lintSource } from "@secretlint/core";
|
|
16
|
+
import { creator } from "@secretlint/secretlint-rule-aws";
|
|
17
|
+
import { creator as creator$1 } from "@secretlint/secretlint-rule-privatekey";
|
|
18
|
+
import { creator as creator$2 } from "@secretlint/secretlint-rule-basicauth";
|
|
19
|
+
import { creator as creator$3 } from "@secretlint/secretlint-rule-github";
|
|
20
|
+
//#region packages/harnesses/src/source/files.ts
|
|
21
|
+
async function noSymlinks(path) {
|
|
22
|
+
if (!isAbsolute(path) || path.length > 16384) throw new Error("UNSAFE_SOURCE_PATH");
|
|
23
|
+
let current = resolve(path);
|
|
24
|
+
let depth = 0;
|
|
25
|
+
while (true) {
|
|
26
|
+
if (++depth > 256) throw new Error("UNSAFE_SOURCE_PATH");
|
|
27
|
+
if ((await lstat(current)).isSymbolicLink()) throw new Error("UNSAFE_SOURCE_PATH");
|
|
28
|
+
const parent = dirname(current);
|
|
29
|
+
if (parent === current) return;
|
|
30
|
+
current = parent;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function matches(value, snapshot, mode = "snapshot") {
|
|
34
|
+
if (!value.isFile() || value.dev.toString() !== snapshot.dev || value.ino.toString() !== snapshot.ino) return false;
|
|
35
|
+
if (mode === "append" && value.size > BigInt(snapshot.size)) return true;
|
|
36
|
+
return value.size === BigInt(snapshot.size) && value.mtimeNs.toString() === snapshot.mtimeNs && value.ctimeNs.toString() === snapshot.ctimeNs;
|
|
37
|
+
}
|
|
38
|
+
async function fileSnapshot(path, target) {
|
|
39
|
+
const value = target ?? await stat(path, { bigint: true });
|
|
40
|
+
if (!value.isFile() || value.size > BigInt(Number.MAX_SAFE_INTEGER)) throw new Error("UNSAFE_SOURCE_PATH");
|
|
41
|
+
return {
|
|
42
|
+
dev: value.dev.toString(),
|
|
43
|
+
ino: value.ino.toString(),
|
|
44
|
+
size: Number(value.size),
|
|
45
|
+
mtimeNs: value.mtimeNs.toString(),
|
|
46
|
+
ctimeNs: value.ctimeNs.toString()
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
async function openSource(part, mode = "snapshot") {
|
|
50
|
+
const child = relative(resolve(part.root), resolve(part.path));
|
|
51
|
+
if (!isAbsolute(part.root) || !isAbsolute(part.path) || part.root.length > 16384 || part.path.length > 16384 || !child || isAbsolute(child) || child === ".." || child.startsWith(`..${sep}`)) throw new Error("UNSAFE_SOURCE_PATH");
|
|
52
|
+
if (!matches(await stat(part.path, { bigint: true }), part.snapshot, mode)) throw new Error("SOURCE_CHANGED");
|
|
53
|
+
const handle = await open(part.path, constants.O_RDONLY);
|
|
54
|
+
try {
|
|
55
|
+
if (!matches(await handle.stat({ bigint: true }), part.snapshot, mode)) throw new Error("SOURCE_CHANGED");
|
|
56
|
+
return handle;
|
|
57
|
+
} catch (error) {
|
|
58
|
+
try {
|
|
59
|
+
await handle.close();
|
|
60
|
+
} catch (cleanupError) {
|
|
61
|
+
throw new AggregateError([error, cleanupError], "SOURCE_METADATA_UNAVAILABLE", { cause: error });
|
|
62
|
+
}
|
|
63
|
+
throw error;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/** Returns true only when the source still matches the complete recorded snapshot. */
|
|
67
|
+
async function checkOpenSource(part, handle, mode = "snapshot") {
|
|
68
|
+
const opened = await handle.stat({ bigint: true });
|
|
69
|
+
const current = await stat(part.path, { bigint: true });
|
|
70
|
+
if (!matches(opened, part.snapshot, mode) || !matches(current, part.snapshot, mode)) throw new Error("SOURCE_CHANGED");
|
|
71
|
+
return matches(opened, part.snapshot) && matches(current, part.snapshot);
|
|
72
|
+
}
|
|
73
|
+
async function checkSource(part) {
|
|
74
|
+
await (await openSource(part)).close();
|
|
75
|
+
}
|
|
76
|
+
//#endregion
|
|
77
|
+
//#region packages/harnesses/src/preview/zstd/frame.ts
|
|
78
|
+
function createZstdFrameCheck() {
|
|
79
|
+
return createZstdFrameCheck$1(() => {
|
|
80
|
+
throw new Error("PREVIEW_DECODE_FAILED");
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
//#endregion
|
|
84
|
+
//#region packages/harnesses/src/preview/policy.ts
|
|
85
|
+
const previewPolicyVersion = "turn-preview-v2";
|
|
86
|
+
const previewLimits = {
|
|
87
|
+
recordBytes: 262144,
|
|
88
|
+
unitBytes: 65536,
|
|
89
|
+
messageBytes: 32768,
|
|
90
|
+
responseBytes: 131072,
|
|
91
|
+
pageUnits: 8,
|
|
92
|
+
completeUsers: 64,
|
|
93
|
+
gap: 8,
|
|
94
|
+
unitMilliseconds: 2e3,
|
|
95
|
+
requestMilliseconds: 3e4,
|
|
96
|
+
zstdWindowLogMax: 26
|
|
97
|
+
};
|
|
98
|
+
function previewSourceVersion(session) {
|
|
99
|
+
return createHash("sha256").update(JSON.stringify({
|
|
100
|
+
harness: session.harness,
|
|
101
|
+
sourceKey: session.sourceKey,
|
|
102
|
+
parts: session.parts.map((part) => ({
|
|
103
|
+
path: part.path,
|
|
104
|
+
root: part.root,
|
|
105
|
+
representation: part.representation,
|
|
106
|
+
snapshot: part.snapshot
|
|
107
|
+
}))
|
|
108
|
+
})).digest("hex");
|
|
109
|
+
}
|
|
110
|
+
function parsePreviewCursor(cursor, sourceVersion) {
|
|
111
|
+
if (cursor === null) return {
|
|
112
|
+
mode: "o",
|
|
113
|
+
ordinal: 0
|
|
114
|
+
};
|
|
115
|
+
if (cursor.length > 128) throw new Error("INVALID_PREVIEW_CURSOR");
|
|
116
|
+
const match = /^turn-preview-v2:([a-f0-9]{64}):([odr]):(0|[1-9][0-9]*)$/.exec(cursor);
|
|
117
|
+
const mode = match?.[2];
|
|
118
|
+
const ordinal = Number(match?.[3]);
|
|
119
|
+
if (!match || match[1] !== sourceVersion || mode !== "o" && mode !== "d" && mode !== "r" || !Number.isSafeInteger(ordinal) || mode === "d" && ordinal === 0) throw new Error("INVALID_PREVIEW_CURSOR");
|
|
120
|
+
return {
|
|
121
|
+
mode,
|
|
122
|
+
ordinal
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
function previewCursor(sourceVersion, mode, ordinal) {
|
|
126
|
+
return `${previewPolicyVersion}:${sourceVersion}:${mode}:${ordinal}`;
|
|
127
|
+
}
|
|
128
|
+
function selectedCount(total, mode) {
|
|
129
|
+
if (mode === "r") return 0;
|
|
130
|
+
if (total <= previewLimits.completeUsers) return total;
|
|
131
|
+
return Math.floor((total - 1) / previewLimits.gap) + 1 + ((total - 1) % previewLimits.gap === 0 ? 0 : 1);
|
|
132
|
+
}
|
|
133
|
+
function nextOrdinal(ordinal, total, mode) {
|
|
134
|
+
if (mode === "r" || ordinal >= total) return null;
|
|
135
|
+
if (ordinal === 0) return total > 0 ? 1 : null;
|
|
136
|
+
return mode === "d" || total <= previewLimits.completeUsers ? ordinal + 1 : Math.min(ordinal + previewLimits.gap, total);
|
|
137
|
+
}
|
|
138
|
+
function pageOrdinals(cursor, total, preamble) {
|
|
139
|
+
if (cursor.ordinal > total || cursor.mode === "d" && total === 0 || cursor.mode === "o" && total > previewLimits.completeUsers && cursor.ordinal > 0 && cursor.ordinal !== total && (cursor.ordinal - 1) % previewLimits.gap !== 0) throw new Error("INVALID_PREVIEW_CURSOR");
|
|
140
|
+
if (cursor.mode === "r") return [cursor.ordinal];
|
|
141
|
+
const result = [];
|
|
142
|
+
let ordinal = cursor.ordinal === 0 && !preamble && total > 0 ? 1 : cursor.ordinal;
|
|
143
|
+
while (ordinal !== null && result.length < previewLimits.pageUnits) {
|
|
144
|
+
result.push(ordinal);
|
|
145
|
+
ordinal = nextOrdinal(ordinal, total, cursor.mode);
|
|
146
|
+
}
|
|
147
|
+
return result;
|
|
148
|
+
}
|
|
149
|
+
//#endregion
|
|
150
|
+
//#region packages/harnesses/src/preview/protocol.ts
|
|
151
|
+
const previewInputSchema = z.object({
|
|
152
|
+
session: sessionSchema,
|
|
153
|
+
cursor: z.string().max(128).nullable()
|
|
154
|
+
});
|
|
155
|
+
const previewPageSchema = z.object({
|
|
156
|
+
processing: z.object({
|
|
157
|
+
kind: z.literal("configured"),
|
|
158
|
+
configurationId: z.string().max(128),
|
|
159
|
+
metadata: z.string().max(16384).nullable(),
|
|
160
|
+
metadataWithheld: z.boolean()
|
|
161
|
+
}).optional(),
|
|
162
|
+
policyVersion: z.literal(previewPolicyVersion),
|
|
163
|
+
sourceVersion: z.string().regex(/^[a-f0-9]{64}$/),
|
|
164
|
+
totalUserMessages: z.number().int().nonnegative(),
|
|
165
|
+
sampledUserMessages: z.number().int().nonnegative(),
|
|
166
|
+
skippedRanges: z.array(z.string().regex(/^[0-9]+-[0-9]+$/)).max(previewLimits.pageUnits),
|
|
167
|
+
incomplete: z.boolean(),
|
|
168
|
+
nextCursor: z.string().max(128).nullable(),
|
|
169
|
+
warnings: z.array(z.string().max(80)).max(32),
|
|
170
|
+
units: z.array(z.object({
|
|
171
|
+
ordinal: z.number().int().nonnegative(),
|
|
172
|
+
role: z.enum(["preamble", "user-turn"]),
|
|
173
|
+
text: z.string().nullable(),
|
|
174
|
+
detailsWithheld: z.boolean(),
|
|
175
|
+
conversation: z.object({
|
|
176
|
+
user: z.string().nullable(),
|
|
177
|
+
assistant: z.string().nullable(),
|
|
178
|
+
userWithheld: z.boolean(),
|
|
179
|
+
assistantWithheld: z.boolean(),
|
|
180
|
+
activities: z.array(z.string().max(80)).max(3)
|
|
181
|
+
}).nullable(),
|
|
182
|
+
withheld: z.boolean(),
|
|
183
|
+
warnings: z.array(z.string().max(80)).max(32)
|
|
184
|
+
})).max(previewLimits.pageUnits)
|
|
185
|
+
});
|
|
186
|
+
const previewMessageSchema = z.discriminatedUnion("kind", [
|
|
187
|
+
z.object({ kind: z.literal("processing-start") }),
|
|
188
|
+
z.object({ kind: z.literal("processing-end") }),
|
|
189
|
+
z.object({
|
|
190
|
+
kind: z.literal("unit-start"),
|
|
191
|
+
ordinal: z.number().int().nonnegative(),
|
|
192
|
+
timeoutPage: previewPageSchema
|
|
193
|
+
}),
|
|
194
|
+
z.object({
|
|
195
|
+
kind: z.literal("unit-end"),
|
|
196
|
+
ordinal: z.number().int().nonnegative()
|
|
197
|
+
}),
|
|
198
|
+
z.object({
|
|
199
|
+
kind: z.literal("result"),
|
|
200
|
+
page: previewPageSchema
|
|
201
|
+
}),
|
|
202
|
+
z.object({
|
|
203
|
+
kind: z.literal("error"),
|
|
204
|
+
code: z.enum([
|
|
205
|
+
"SOURCE_CHANGED",
|
|
206
|
+
"INVALID_PREVIEW_CURSOR",
|
|
207
|
+
"PREVIEW_WITHHELD",
|
|
208
|
+
"PREVIEW_ENVELOPE_CHANGED",
|
|
209
|
+
"PROCESSING_PROTECTED_CUSTOM_TERM"
|
|
210
|
+
])
|
|
211
|
+
})
|
|
212
|
+
]);
|
|
213
|
+
//#endregion
|
|
214
|
+
//#region packages/harnesses/src/preview/source-stream.ts
|
|
215
|
+
async function* previewChunks(part) {
|
|
216
|
+
const handle = await openSource(part);
|
|
217
|
+
const framing = part.representation === "jsonl.zst" ? createZstdFrameCheck() : void 0;
|
|
218
|
+
async function* source() {
|
|
219
|
+
let position = 0;
|
|
220
|
+
while (position < part.snapshot.size) {
|
|
221
|
+
const bytes = new Uint8Array(Math.min(65536, part.snapshot.size - position));
|
|
222
|
+
const { bytesRead } = await handle.read(bytes, 0, bytes.length, position);
|
|
223
|
+
if (!bytesRead) throw new Error("SOURCE_CHANGED");
|
|
224
|
+
position += bytesRead;
|
|
225
|
+
const chunk = bytes.subarray(0, bytesRead);
|
|
226
|
+
const boundaries = framing?.push(chunk) ?? [];
|
|
227
|
+
let start = 0;
|
|
228
|
+
for (const end of boundaries) {
|
|
229
|
+
if (end > start) yield chunk.subarray(start, end);
|
|
230
|
+
start = end;
|
|
231
|
+
}
|
|
232
|
+
if (start < chunk.length) yield chunk.subarray(start);
|
|
233
|
+
}
|
|
234
|
+
framing?.finish();
|
|
235
|
+
}
|
|
236
|
+
const input = Readable.from(source(), {
|
|
237
|
+
objectMode: false,
|
|
238
|
+
highWaterMark: 65536
|
|
239
|
+
});
|
|
240
|
+
const decoded = part.representation === "jsonl.zst" ? createZstdDecompress({
|
|
241
|
+
chunkSize: 65536,
|
|
242
|
+
params: { [constants$1.ZSTD_d_windowLogMax]: previewLimits.zstdWindowLogMax }
|
|
243
|
+
}) : new PassThrough({ highWaterMark: 65536 });
|
|
244
|
+
const cancellation = new AbortController();
|
|
245
|
+
const completion = pipeline(input, decoded, { signal: cancellation.signal }).then(() => true, () => false);
|
|
246
|
+
let failed = false;
|
|
247
|
+
try {
|
|
248
|
+
try {
|
|
249
|
+
for await (const chunk of decoded) {
|
|
250
|
+
if (!(chunk instanceof Uint8Array)) throw new Error("PREVIEW_DECODE_FAILED");
|
|
251
|
+
yield chunk;
|
|
252
|
+
}
|
|
253
|
+
failed = !await completion;
|
|
254
|
+
} catch {
|
|
255
|
+
failed = true;
|
|
256
|
+
}
|
|
257
|
+
if (failed) yield null;
|
|
258
|
+
} finally {
|
|
259
|
+
cancellation.abort();
|
|
260
|
+
await completion;
|
|
261
|
+
try {
|
|
262
|
+
await checkOpenSource(part, handle);
|
|
263
|
+
} finally {
|
|
264
|
+
await handle.close();
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
//#endregion
|
|
269
|
+
//#region packages/harnesses/src/preview/records.ts
|
|
270
|
+
async function* streamRecords(part) {
|
|
271
|
+
const decoder = new TextDecoder("utf-8", {
|
|
272
|
+
fatal: true,
|
|
273
|
+
ignoreBOM: true
|
|
274
|
+
});
|
|
275
|
+
let pieces = [];
|
|
276
|
+
let length = 0;
|
|
277
|
+
let oversized = false;
|
|
278
|
+
const finish = (newline) => {
|
|
279
|
+
const bytes = length + (newline ? 1 : 0);
|
|
280
|
+
let result;
|
|
281
|
+
if (oversized) result = {
|
|
282
|
+
kind: "unclassified",
|
|
283
|
+
warning: "PREVIEW_RECORD_LIMIT",
|
|
284
|
+
bytes
|
|
285
|
+
};
|
|
286
|
+
else try {
|
|
287
|
+
const text = decoder.decode(Buffer.concat(pieces, length));
|
|
288
|
+
if (/^[\t\r ]*$/.test(text)) result = null;
|
|
289
|
+
else result = {
|
|
290
|
+
kind: "record",
|
|
291
|
+
value: JSON.parse(text),
|
|
292
|
+
bytes
|
|
293
|
+
};
|
|
294
|
+
} catch {
|
|
295
|
+
result = {
|
|
296
|
+
kind: "unclassified",
|
|
297
|
+
warning: "MALFORMED_PREVIEW_RECORD",
|
|
298
|
+
bytes
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
pieces = [];
|
|
302
|
+
length = 0;
|
|
303
|
+
oversized = false;
|
|
304
|
+
return result;
|
|
305
|
+
};
|
|
306
|
+
for await (const chunk of previewChunks(part)) {
|
|
307
|
+
if (chunk === null) {
|
|
308
|
+
if (length > 0) {
|
|
309
|
+
pieces = [];
|
|
310
|
+
length = 0;
|
|
311
|
+
oversized = false;
|
|
312
|
+
}
|
|
313
|
+
yield {
|
|
314
|
+
kind: "unclassified",
|
|
315
|
+
warning: "PREVIEW_DECODE_FAILED",
|
|
316
|
+
bytes: 0
|
|
317
|
+
};
|
|
318
|
+
continue;
|
|
319
|
+
}
|
|
320
|
+
let start = 0;
|
|
321
|
+
while (start < chunk.length) {
|
|
322
|
+
const newline = chunk.indexOf(10, start);
|
|
323
|
+
const end = newline < 0 ? chunk.length : newline;
|
|
324
|
+
const piece = chunk.subarray(start, end);
|
|
325
|
+
length += piece.length;
|
|
326
|
+
if (!Number.isSafeInteger(length)) throw new Error("PREVIEW_WITHHELD");
|
|
327
|
+
if (length > previewLimits.recordBytes) {
|
|
328
|
+
oversized = true;
|
|
329
|
+
pieces = [];
|
|
330
|
+
} else if (!oversized && piece.length) pieces.push(piece);
|
|
331
|
+
if (newline >= 0) {
|
|
332
|
+
if (length > 0) {
|
|
333
|
+
const record = finish(true);
|
|
334
|
+
if (record) yield record;
|
|
335
|
+
}
|
|
336
|
+
start = newline + 1;
|
|
337
|
+
} else start = chunk.length;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
if (length > 0) {
|
|
341
|
+
const record = finish(false);
|
|
342
|
+
if (record) yield record;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
//#endregion
|
|
346
|
+
//#region packages/harnesses/src/preview/classify.ts
|
|
347
|
+
function classifyRecord(value, harness, sourceRole) {
|
|
348
|
+
if (!isRecord(value)) return {
|
|
349
|
+
responseUser: false,
|
|
350
|
+
eventUser: false,
|
|
351
|
+
known: false
|
|
352
|
+
};
|
|
353
|
+
if (harness === "claude") {
|
|
354
|
+
const message = isRecord(value["message"]) ? value["message"] : null;
|
|
355
|
+
const content = Array.isArray(message?.["content"]) ? message["content"] : null;
|
|
356
|
+
const toolOnly = content !== null && content.length > 0 && content.every((item) => isRecord(item) && item["type"] === "tool_result");
|
|
357
|
+
return {
|
|
358
|
+
responseUser: sourceRole !== "worker" && value["isSidechain"] !== true && value["type"] === "user" && !toolOnly && value["isMeta"] !== true,
|
|
359
|
+
eventUser: false,
|
|
360
|
+
known: [
|
|
361
|
+
"user",
|
|
362
|
+
"assistant",
|
|
363
|
+
"system",
|
|
364
|
+
"progress",
|
|
365
|
+
"summary",
|
|
366
|
+
"result",
|
|
367
|
+
"queue-operation",
|
|
368
|
+
"file-history-snapshot",
|
|
369
|
+
"last-prompt",
|
|
370
|
+
"custom-title",
|
|
371
|
+
"compact-boundary"
|
|
372
|
+
].includes(String(value["type"]))
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
const payload = isRecord(value["payload"]) ? value["payload"] : null;
|
|
376
|
+
return {
|
|
377
|
+
responseUser: sourceRole !== "worker" && value["type"] === "response_item" && payload?.["role"] === "user" && (payload["type"] === "message" || payload["type"] === void 0),
|
|
378
|
+
eventUser: sourceRole !== "worker" && value["type"] === "event_msg" && payload?.["type"] === "user_message",
|
|
379
|
+
known: [
|
|
380
|
+
"session_meta",
|
|
381
|
+
"turn_context",
|
|
382
|
+
"compacted"
|
|
383
|
+
].includes(String(value["type"])) || payload !== null && ["response_item", "event_msg"].includes(String(value["type"]))
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
//#endregion
|
|
387
|
+
//#region packages/harnesses/src/preview/conversation.ts
|
|
388
|
+
function emptyConversation() {
|
|
389
|
+
return {
|
|
390
|
+
user: null,
|
|
391
|
+
assistant: null,
|
|
392
|
+
userWithheld: false,
|
|
393
|
+
assistantWithheld: false,
|
|
394
|
+
assistantMessages: 0,
|
|
395
|
+
toolCalls: 0,
|
|
396
|
+
toolResults: 0,
|
|
397
|
+
attachments: 0
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
function contentText(value, conversation) {
|
|
401
|
+
if (typeof value === "string") return value;
|
|
402
|
+
if (!Array.isArray(value)) return null;
|
|
403
|
+
const parts = [];
|
|
404
|
+
for (const item of value) {
|
|
405
|
+
if (!isRecord(item)) continue;
|
|
406
|
+
if ([
|
|
407
|
+
"text",
|
|
408
|
+
"input_text",
|
|
409
|
+
"output_text"
|
|
410
|
+
].includes(String(item["type"]))) {
|
|
411
|
+
if (typeof item["text"] === "string") parts.push(item["text"]);
|
|
412
|
+
} else if (item["type"] === "tool_use") conversation.toolCalls++;
|
|
413
|
+
else if (item["type"] === "tool_result") conversation.toolResults++;
|
|
414
|
+
else if ([
|
|
415
|
+
"image",
|
|
416
|
+
"input_image",
|
|
417
|
+
"document",
|
|
418
|
+
"attachment"
|
|
419
|
+
].includes(String(item["type"]))) conversation.attachments++;
|
|
420
|
+
}
|
|
421
|
+
return parts.length > 0 ? parts.join("\n\n") : null;
|
|
422
|
+
}
|
|
423
|
+
function assignMessage(conversation, role, text) {
|
|
424
|
+
if (text === null || role !== "user" && role !== "assistant") return;
|
|
425
|
+
const withheld = Buffer.byteLength(text) > previewLimits.messageBytes;
|
|
426
|
+
if (role === "user") {
|
|
427
|
+
conversation.user = withheld ? null : text;
|
|
428
|
+
conversation.userWithheld = withheld;
|
|
429
|
+
} else {
|
|
430
|
+
conversation.assistantMessages++;
|
|
431
|
+
conversation.assistant = withheld ? null : text;
|
|
432
|
+
conversation.assistantWithheld = withheld;
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
function collectConversation(conversation, value, harness, mode, sourceRole) {
|
|
436
|
+
if (!isRecord(value)) return;
|
|
437
|
+
if (harness === "claude") {
|
|
438
|
+
const message = isRecord(value["message"]) ? value["message"] : null;
|
|
439
|
+
if (message && value["isMeta"] !== true && (value["type"] === "user" || value["type"] === "assistant")) {
|
|
440
|
+
const text = contentText(message["content"], conversation);
|
|
441
|
+
if (sourceRole !== "worker" && value["isSidechain"] !== true) assignMessage(conversation, value["type"], text);
|
|
442
|
+
}
|
|
443
|
+
return;
|
|
444
|
+
}
|
|
445
|
+
const payload = isRecord(value["payload"]) ? value["payload"] : null;
|
|
446
|
+
if (!payload) return;
|
|
447
|
+
const type = typeof payload["type"] === "string" ? payload["type"] : "";
|
|
448
|
+
if (value["type"] === "response_item") {
|
|
449
|
+
if ((payload["type"] === "message" || payload["type"] === void 0) && (payload["role"] === "user" || payload["role"] === "assistant")) {
|
|
450
|
+
const text = contentText(payload["content"], conversation);
|
|
451
|
+
if (sourceRole !== "worker") assignMessage(conversation, payload["role"], text);
|
|
452
|
+
} else if (["function_call", "custom_tool_call"].includes(type)) conversation.toolCalls++;
|
|
453
|
+
else if (["function_call_output", "custom_tool_call_output"].includes(type)) conversation.toolResults++;
|
|
454
|
+
} else if (value["type"] === "event_msg" && mode === "event") {
|
|
455
|
+
const role = payload["type"] === "user_message" ? "user" : payload["type"] === "agent_message" ? "assistant" : null;
|
|
456
|
+
const text = contentText(payload["message"], conversation);
|
|
457
|
+
if (sourceRole !== "worker") assignMessage(conversation, role, text);
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
function activitySummaries(conversation) {
|
|
461
|
+
const summaries = [];
|
|
462
|
+
const tools = Math.max(conversation.toolCalls, conversation.toolResults);
|
|
463
|
+
if (tools > 0) summaries.push(`${tools} tool ${tools === 1 ? "activity" : "activities"}`);
|
|
464
|
+
if (conversation.attachments > 0) summaries.push(`${conversation.attachments} ${conversation.attachments === 1 ? "attachment" : "attachments"}`);
|
|
465
|
+
const earlier = conversation.assistantMessages - 1;
|
|
466
|
+
if (earlier > 0) summaries.push(`${earlier} earlier assistant ${earlier === 1 ? "update" : "updates"}`);
|
|
467
|
+
return summaries;
|
|
468
|
+
}
|
|
469
|
+
//#endregion
|
|
470
|
+
//#region packages/harnesses/src/preview/sequence.ts
|
|
471
|
+
async function countSequence(session, stream = streamRecords) {
|
|
472
|
+
const parts = [];
|
|
473
|
+
const warnings = /* @__PURE__ */ new Set();
|
|
474
|
+
let totalUsers = 0;
|
|
475
|
+
let preamble = false;
|
|
476
|
+
let incomplete = false;
|
|
477
|
+
for (const part of session.parts) {
|
|
478
|
+
let responseUsers = 0;
|
|
479
|
+
let eventUsers = 0;
|
|
480
|
+
let responsePrefix = false;
|
|
481
|
+
let eventPrefix = false;
|
|
482
|
+
for await (const record of stream(part)) {
|
|
483
|
+
if (record.kind === "unclassified") {
|
|
484
|
+
incomplete = true;
|
|
485
|
+
warnings.add(record.warning);
|
|
486
|
+
responsePrefix ||= responseUsers === 0;
|
|
487
|
+
eventPrefix ||= eventUsers === 0;
|
|
488
|
+
continue;
|
|
489
|
+
}
|
|
490
|
+
const boundary = record.kind === "boundary" ? {
|
|
491
|
+
...record,
|
|
492
|
+
known: true
|
|
493
|
+
} : classifyRecord(record.value, session.harness, record.sourceRole);
|
|
494
|
+
if (!boundary.known) {
|
|
495
|
+
incomplete = true;
|
|
496
|
+
warnings.add("UNKNOWN_PREVIEW_RECORD");
|
|
497
|
+
}
|
|
498
|
+
if (boundary.responseUser) responseUsers++;
|
|
499
|
+
else responsePrefix ||= responseUsers === 0;
|
|
500
|
+
if (boundary.eventUser) eventUsers++;
|
|
501
|
+
else eventPrefix ||= eventUsers === 0;
|
|
502
|
+
}
|
|
503
|
+
const mode = session.harness === "codex" && responseUsers === 0 && eventUsers > 0 ? "event" : "response";
|
|
504
|
+
const users = mode === "event" ? eventUsers : responseUsers;
|
|
505
|
+
if (totalUsers === 0) preamble ||= mode === "event" ? eventPrefix : responsePrefix;
|
|
506
|
+
totalUsers += users;
|
|
507
|
+
if (!Number.isSafeInteger(totalUsers)) throw new Error("PREVIEW_WITHHELD");
|
|
508
|
+
parts.push({
|
|
509
|
+
mode,
|
|
510
|
+
users
|
|
511
|
+
});
|
|
512
|
+
if (mode === "event") warnings.add("CODEX_EVENT_MESSAGE_FALLBACK");
|
|
513
|
+
}
|
|
514
|
+
if (incomplete) warnings.add("USER_COUNT_INCOMPLETE");
|
|
515
|
+
if (totalUsers === 0) {
|
|
516
|
+
preamble = true;
|
|
517
|
+
warnings.add("NO_RECOGNIZED_USER_MESSAGES");
|
|
518
|
+
}
|
|
519
|
+
return {
|
|
520
|
+
parts,
|
|
521
|
+
totalUsers,
|
|
522
|
+
preamble,
|
|
523
|
+
incomplete,
|
|
524
|
+
warnings: [...warnings]
|
|
525
|
+
};
|
|
526
|
+
}
|
|
527
|
+
async function retainUnits(session, summary, ordinals, details = false, stream = streamRecords) {
|
|
528
|
+
const units = new Map(ordinals.map((ordinal) => [ordinal, {
|
|
529
|
+
ordinal,
|
|
530
|
+
records: [],
|
|
531
|
+
bytes: 0,
|
|
532
|
+
withheld: false,
|
|
533
|
+
detailsWithheld: false,
|
|
534
|
+
conversation: emptyConversation(),
|
|
535
|
+
warnings: [],
|
|
536
|
+
seen: false
|
|
537
|
+
}]));
|
|
538
|
+
let ordinal = 0;
|
|
539
|
+
for (const [index, part] of session.parts.entries()) {
|
|
540
|
+
const mode = summary.parts[index]?.mode;
|
|
541
|
+
if (!mode) throw new Error("SOURCE_CHANGED");
|
|
542
|
+
for await (const record of stream(part)) {
|
|
543
|
+
if (record.kind === "record" || record.kind === "boundary") {
|
|
544
|
+
const boundary = record.kind === "boundary" ? record : classifyRecord(record.value, session.harness, record.sourceRole);
|
|
545
|
+
if (mode === "event" ? boundary.eventUser : boundary.responseUser) ordinal++;
|
|
546
|
+
}
|
|
547
|
+
const unit = units.get(ordinal);
|
|
548
|
+
if (!unit) continue;
|
|
549
|
+
unit.seen = true;
|
|
550
|
+
unit.bytes += record.bytes;
|
|
551
|
+
if (record.kind === "boundary") {
|
|
552
|
+
unit.withheld = true;
|
|
553
|
+
unit.warnings.push("PREVIEW_WITHHELD");
|
|
554
|
+
}
|
|
555
|
+
if (record.kind === "unclassified") {
|
|
556
|
+
unit.withheld = true;
|
|
557
|
+
if (!unit.warnings.includes(record.warning)) unit.warnings.push(record.warning);
|
|
558
|
+
}
|
|
559
|
+
if (unit.bytes > previewLimits.unitBytes) {
|
|
560
|
+
unit.detailsWithheld = true;
|
|
561
|
+
if (!unit.warnings.includes("PREVIEW_DETAILS_LIMIT")) unit.warnings.push("PREVIEW_DETAILS_LIMIT");
|
|
562
|
+
}
|
|
563
|
+
if (!details && !unit.withheld && record.kind === "record") collectConversation(unit.conversation, record.value, session.harness, mode, record.sourceRole);
|
|
564
|
+
if (unit.withheld || unit.detailsWithheld) unit.records = [];
|
|
565
|
+
else if (details && record.kind === "record") unit.records.push(record.value);
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
if (ordinal !== summary.totalUsers) throw new Error("SOURCE_CHANGED");
|
|
569
|
+
for (const unit of units.values()) {
|
|
570
|
+
if (!unit.seen) {
|
|
571
|
+
unit.withheld = true;
|
|
572
|
+
unit.warnings.push("EMPTY_PREVIEW_UNIT");
|
|
573
|
+
}
|
|
574
|
+
unit.warnings = [...new Set(unit.warnings)];
|
|
575
|
+
}
|
|
576
|
+
return [...units.values()];
|
|
577
|
+
}
|
|
578
|
+
//#endregion
|
|
579
|
+
//#region packages/harnesses/src/preview/page.ts
|
|
580
|
+
function previewPage(context, units) {
|
|
581
|
+
const { sourceVersion, cursor, summary } = context;
|
|
582
|
+
const last = units.at(-1)?.ordinal;
|
|
583
|
+
const next = last === void 0 ? cursor.ordinal : nextOrdinal(last, summary.totalUsers, cursor.mode);
|
|
584
|
+
const skippedRanges = [];
|
|
585
|
+
if (cursor.mode === "o" && summary.totalUsers > previewLimits.completeUsers) for (const unit of units) {
|
|
586
|
+
if (unit.ordinal === 0) continue;
|
|
587
|
+
const following = nextOrdinal(unit.ordinal, summary.totalUsers, cursor.mode);
|
|
588
|
+
if (following !== null && following > unit.ordinal + 1) skippedRanges.push(`${unit.ordinal + 1}-${following - 1}`);
|
|
589
|
+
}
|
|
590
|
+
const incomplete = summary.incomplete || (context.processing?.metadataWithheld ?? false) || units.some((unit) => unit.withheld || unit.detailsWithheld || unit.conversation?.userWithheld || unit.conversation?.assistantWithheld);
|
|
591
|
+
return {
|
|
592
|
+
...context.processing ? { processing: context.processing } : {},
|
|
593
|
+
policyVersion: previewPolicyVersion,
|
|
594
|
+
sourceVersion,
|
|
595
|
+
totalUserMessages: summary.totalUsers,
|
|
596
|
+
sampledUserMessages: cursor.mode === "d" ? units.length : selectedCount(summary.totalUsers, cursor.mode),
|
|
597
|
+
skippedRanges,
|
|
598
|
+
incomplete,
|
|
599
|
+
units,
|
|
600
|
+
nextCursor: next === null ? null : previewCursor(sourceVersion, cursor.mode, next),
|
|
601
|
+
warnings: [
|
|
602
|
+
...summary.warnings,
|
|
603
|
+
...incomplete ? ["INCOMPLETE_PREVIEW"] : [],
|
|
604
|
+
...cursor.mode === "d" ? ["CONSECUTIVE_DRILLDOWN"] : []
|
|
605
|
+
]
|
|
606
|
+
};
|
|
607
|
+
}
|
|
608
|
+
function pageFits(page) {
|
|
609
|
+
return Buffer.byteLength(JSON.stringify(page)) <= previewLimits.responseBytes;
|
|
610
|
+
}
|
|
611
|
+
function withheldUnit(ordinal, warnings) {
|
|
612
|
+
return {
|
|
613
|
+
ordinal,
|
|
614
|
+
role: ordinal === 0 ? "preamble" : "user-turn",
|
|
615
|
+
text: null,
|
|
616
|
+
detailsWithheld: true,
|
|
617
|
+
conversation: null,
|
|
618
|
+
withheld: true,
|
|
619
|
+
warnings: [...new Set(warnings)]
|
|
620
|
+
};
|
|
621
|
+
}
|
|
622
|
+
//#endregion
|
|
623
|
+
//#region packages/harnesses/src/preview/worker/heap.ts
|
|
624
|
+
const previewHeapLimits = {
|
|
625
|
+
maxOldGenerationSizeMb: 128,
|
|
626
|
+
maxYoungGenerationSizeMb: 32
|
|
627
|
+
};
|
|
628
|
+
const maximumBytes = (previewHeapLimits.maxOldGenerationSizeMb + 96) * 1024 ** 2;
|
|
629
|
+
const minimumBytes = previewHeapLimits.maxOldGenerationSizeMb * 1024 ** 2;
|
|
630
|
+
/** Fails closed so no preview runs under a heap the Worker resource limits did not bound. */
|
|
631
|
+
function assertPreviewHeap(limitBytes = getHeapStatistics().heap_size_limit) {
|
|
632
|
+
if (limitBytes < minimumBytes || limitBytes > maximumBytes) throw new Error("PREVIEW_HEAP_LIMIT_INVALID");
|
|
633
|
+
}
|
|
634
|
+
//#endregion
|
|
635
|
+
//#region packages/harnesses/src/preview/guard/identifiers.ts
|
|
636
|
+
function luhn(value) {
|
|
637
|
+
let sum = 0;
|
|
638
|
+
let double = false;
|
|
639
|
+
for (let index = value.length - 1; index >= 0; index--) {
|
|
640
|
+
let digit = Number(value[index]);
|
|
641
|
+
if (double) {
|
|
642
|
+
digit *= 2;
|
|
643
|
+
if (digit > 9) digit -= 9;
|
|
644
|
+
}
|
|
645
|
+
sum += digit;
|
|
646
|
+
double = !double;
|
|
647
|
+
}
|
|
648
|
+
return sum % 10 === 0;
|
|
649
|
+
}
|
|
650
|
+
function redactIdentifiers(text) {
|
|
651
|
+
return text.replace(/\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/gi, "[EMAIL]").replace(/\b[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}\b/gi, "[UUID]").replace(/\b(?:\d{1,3}\.){3}\d{1,3}\b/g, (candidate) => isIP(candidate) === 4 ? "[IP]" : candidate).replace(/(?<![\w:])\[?[0-9a-f.:]+(?:%[\w.-]+)?\]?(?![\w:])/gi, (candidate) => {
|
|
652
|
+
if (!candidate.includes(":")) return candidate;
|
|
653
|
+
const address = candidate.replace(/^\[|\]$/g, "").split("%")[0] ?? "";
|
|
654
|
+
return isIP(address) === 6 ? "[IP]" : candidate;
|
|
655
|
+
}).replace(/(?<!\d)(?:\d[ -]?){12,18}\d(?!\d)/g, (candidate) => {
|
|
656
|
+
const digits = candidate.replace(/[ -]/g, "");
|
|
657
|
+
return !/^(\d)\1+$/.test(digits) && luhn(digits) ? "[PAYMENT_CARD]" : candidate;
|
|
658
|
+
});
|
|
659
|
+
}
|
|
660
|
+
//#endregion
|
|
661
|
+
//#region packages/harnesses/src/preview/guard/context.ts
|
|
662
|
+
const sensitiveKeys = /* @__PURE__ */ new Set([
|
|
663
|
+
"password",
|
|
664
|
+
"passwd",
|
|
665
|
+
"pwd",
|
|
666
|
+
"secret",
|
|
667
|
+
"clientsecret",
|
|
668
|
+
"token",
|
|
669
|
+
"accesstoken",
|
|
670
|
+
"refreshtoken",
|
|
671
|
+
"idtoken",
|
|
672
|
+
"apikey",
|
|
673
|
+
"credential",
|
|
674
|
+
"credentials",
|
|
675
|
+
"privatekey",
|
|
676
|
+
"authorization",
|
|
677
|
+
"proxyauthorization",
|
|
678
|
+
"cookie",
|
|
679
|
+
"setcookie",
|
|
680
|
+
"xapikey",
|
|
681
|
+
"awsaccesskeyid",
|
|
682
|
+
"awssecretaccesskey",
|
|
683
|
+
"awssessiontoken",
|
|
684
|
+
"databasepassword"
|
|
685
|
+
]);
|
|
686
|
+
const pathKeys = /* @__PURE__ */ new Set([
|
|
687
|
+
"cwd",
|
|
688
|
+
"workingdirectory",
|
|
689
|
+
"filepath",
|
|
690
|
+
"filename",
|
|
691
|
+
"directory",
|
|
692
|
+
"homedir",
|
|
693
|
+
"path"
|
|
694
|
+
]);
|
|
695
|
+
const normalizeKey = (key) => key.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
696
|
+
function valueCategory(key, value) {
|
|
697
|
+
if (key === void 0) return null;
|
|
698
|
+
const normalized = normalizeKey(key);
|
|
699
|
+
if (sensitiveKeys.has(normalized)) return "[SECRET]";
|
|
700
|
+
if (typeof value === "string" && pathKeys.has(normalized) && /^(?:\/|[a-z]:[\\/]|\\\\|~[^/]*\/)/i.test(value)) return "[PATH]";
|
|
701
|
+
if (typeof value === "string" && [
|
|
702
|
+
"hostname",
|
|
703
|
+
"server",
|
|
704
|
+
"endpoint",
|
|
705
|
+
"host"
|
|
706
|
+
].includes(normalized) && /^[a-z0-9.-]+(?::\d+)?$/i.test(value)) return "[HOST]";
|
|
707
|
+
return null;
|
|
708
|
+
}
|
|
709
|
+
function redactContext(text) {
|
|
710
|
+
return text.replace(/-----BEGIN [^-\r\n]*PRIVATE KEY-----[\s\S]*?(?:-----END [^-\r\n]*PRIVATE KEY-----|$)/g, "[PRIVATE_KEY]").replace(/\b(?:gh[pousr]_[a-z0-9_]{8,}|github_pat_[a-z0-9_]{8,}|sk-(?:ant-)?[a-z0-9_-]{8,}|AKIA[A-Z0-9]{16}|ASIA[A-Z0-9]{16})\b/gi, "[SECRET]").replace(/\beyJ[A-Za-z0-9_-]{2,}\.[A-Za-z0-9_-]+\.[A-Za-z0-9_.-]+\b/g, "[TOKEN]").replace(/\b(authorization|proxy-authorization|cookie|set-cookie)\s*["']?\s*[:=]\s*[^\r\n]+/gi, "$1: [SECRET]").replace(/\b((?:aws[_-]?)?(?:secret[_-]?access[_-]?key|access[_-]?key[_-]?id|session[_-]?token)|password|passwd|client[_-]?secret|secret|(?:access[_-]?|refresh[_-]?|id[_-]?)?token|api[_-]?key|credential|private[_-]?key)\s*["']?\s*[:=]\s*(?:"[^"\r\n]*"|'[^'\r\n]*'|[^\s,;\]}]+)/gi, "$1=[SECRET]").replace(/--(?:password|passwd|token|api-key|secret)(?:=|\s+)(?:"[^"\r\n]*"|'[^'\r\n]*'|[^\s]+)/gi, "--credential [SECRET]");
|
|
711
|
+
}
|
|
712
|
+
//#endregion
|
|
713
|
+
//#region packages/harnesses/src/preview/guard/paths.ts
|
|
714
|
+
const sensitiveQuery = /^(?:access_token|token|api[_-]?key|key|secret|password|signature|sig|credential|x-amz-.+|x-goog-.+)$/i;
|
|
715
|
+
function redactLocations(text, localRoots) {
|
|
716
|
+
let result = text;
|
|
717
|
+
for (const root of [...localRoots].filter((value) => value.length > 1).sort((a, b) => b.length - a.length)) {
|
|
718
|
+
const escaped = root.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
719
|
+
result = result.replace(new RegExp(`${escaped}(?![a-zA-Z0-9_.-])(?:[/\\\\][^\\s"'<>]*)?`, "g"), "[PATH]");
|
|
720
|
+
}
|
|
721
|
+
result = result.replace(/\b(?:https?|ssh|git|file|postgres(?:ql)?|mysql|mongodb(?:\+srv)?|redis|amqps?):\/\/[^\s<>"']+/gi, (candidate) => {
|
|
722
|
+
try {
|
|
723
|
+
const url = new URL(candidate);
|
|
724
|
+
if (url.protocol === "file:") return "[PATH]";
|
|
725
|
+
if (url.username || url.password || [...url.searchParams.keys()].some((key) => sensitiveQuery.test(key)) || url.hash) return "[PRIVATE_URL]";
|
|
726
|
+
if (["ssh:", "git:"].includes(url.protocol) || /\.git\/?$/.test(url.pathname) || /^(?:github\.com|gitlab\.com|bitbucket\.org|dev\.azure\.com)$/i.test(url.hostname)) return "[GIT_REMOTE]";
|
|
727
|
+
return candidate;
|
|
728
|
+
} catch {
|
|
729
|
+
return "[PRIVATE_URL]";
|
|
730
|
+
}
|
|
731
|
+
});
|
|
732
|
+
return result.replace(/\b[a-z]:[\\/][^\r\n"'<>]+/gi, "[PATH]").replace(/\\\\[^\s"'<>]+/g, "[PATH]").replace(/(?<![\w/])~[^\s/]*\/[^\s"'<>]+/g, "[PATH]").replace(/(?<![\w/:])\/(?:home|Users|tmp|var|opt|mnt|workspace|workspaces|private|root)\/[^\s"'<>]+/g, "[PATH]").replace(/\b[a-z0-9._-]+@[a-z0-9.-]+:[^\s"'<>]+/gi, "[GIT_REMOTE]").replace(/\bFile\s+["']\/[^"'\r\n]+["']/g, "File [PATH]");
|
|
733
|
+
}
|
|
734
|
+
//#endregion
|
|
735
|
+
//#region packages/harnesses/src/preview/guard/index.ts
|
|
736
|
+
const selected = [
|
|
737
|
+
creator,
|
|
738
|
+
creator$1,
|
|
739
|
+
creator$2,
|
|
740
|
+
creator$3
|
|
741
|
+
];
|
|
742
|
+
const config = { rules: selected.map((rule) => ({
|
|
743
|
+
id: rule.meta.id,
|
|
744
|
+
rule,
|
|
745
|
+
options: rule === creator ? { enableIDScanRule: true } : {}
|
|
746
|
+
})) };
|
|
747
|
+
selected.map((rule) => rule.meta.id);
|
|
748
|
+
async function redact(text, roots) {
|
|
749
|
+
if (text.length > 65536) throw new Error("TEXT_LIMIT");
|
|
750
|
+
const spans = (await lintSource({
|
|
751
|
+
source: {
|
|
752
|
+
content: text,
|
|
753
|
+
filePath: "/virtual/session.txt",
|
|
754
|
+
contentType: "text"
|
|
755
|
+
},
|
|
756
|
+
options: {
|
|
757
|
+
config,
|
|
758
|
+
maskSecrets: true,
|
|
759
|
+
noPhysicFilePath: true
|
|
760
|
+
}
|
|
761
|
+
})).messages.map((message) => message.range).sort((a, b) => a[0] - b[0]);
|
|
762
|
+
let end = 0;
|
|
763
|
+
let output = "";
|
|
764
|
+
for (const span of spans) {
|
|
765
|
+
if (span[1] <= end) continue;
|
|
766
|
+
if (span[0] > end) output += text.slice(end, span[0]);
|
|
767
|
+
output += "[SECRET]";
|
|
768
|
+
end = span[1];
|
|
769
|
+
}
|
|
770
|
+
output += text.slice(end);
|
|
771
|
+
return redactIdentifiers(redactContext(redactLocations(output, roots)));
|
|
772
|
+
}
|
|
773
|
+
async function previewText(text, roots) {
|
|
774
|
+
try {
|
|
775
|
+
if (Buffer.byteLength(text) > previewLimits.messageBytes) return null;
|
|
776
|
+
const safe = await redact(text, roots);
|
|
777
|
+
return Buffer.byteLength(safe) <= previewLimits.messageBytes ? safe : null;
|
|
778
|
+
} catch {
|
|
779
|
+
return null;
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
async function preview(value, injectFailure = false, context = {}) {
|
|
783
|
+
let fields = 0;
|
|
784
|
+
const parts = [];
|
|
785
|
+
let outputBytes = 0;
|
|
786
|
+
const roots = context.localRoots ?? [];
|
|
787
|
+
const append = async (value) => {
|
|
788
|
+
const safe = await redact(value, roots);
|
|
789
|
+
outputBytes += Buffer.byteLength(safe) + 1;
|
|
790
|
+
if (outputBytes > 65536) throw new Error("PREVIEW_LIMIT");
|
|
791
|
+
parts.push(safe);
|
|
792
|
+
};
|
|
793
|
+
async function visit(input, depth, key) {
|
|
794
|
+
if (injectFailure) throw new Error("INJECTED_SCANNER_FAILURE");
|
|
795
|
+
if (depth > 32 || ++fields > 4096) throw new Error("STRUCTURE_LIMIT");
|
|
796
|
+
const category = valueCategory(key, input);
|
|
797
|
+
if (category) {
|
|
798
|
+
await append(`${key ?? ""}: ${category}`);
|
|
799
|
+
return;
|
|
800
|
+
}
|
|
801
|
+
if (Array.isArray(input)) {
|
|
802
|
+
if (key !== void 0) await append(key);
|
|
803
|
+
for (const item of input) await visit(item, depth + 1);
|
|
804
|
+
} else if (input !== null && typeof input === "object") {
|
|
805
|
+
if (key !== void 0) await append(key);
|
|
806
|
+
for (const [childKey, child] of Object.entries(input)) await visit(child, depth + 1, childKey);
|
|
807
|
+
} else await append(`${key === void 0 ? "" : `${key}: `}${String(input)}`);
|
|
808
|
+
}
|
|
809
|
+
try {
|
|
810
|
+
await visit(value, 0);
|
|
811
|
+
const text = parts.join("\n");
|
|
812
|
+
if (Buffer.byteLength(text) > 65536) throw new Error("PREVIEW_LIMIT");
|
|
813
|
+
return {
|
|
814
|
+
status: "ready",
|
|
815
|
+
text,
|
|
816
|
+
fields
|
|
817
|
+
};
|
|
818
|
+
} catch {
|
|
819
|
+
return {
|
|
820
|
+
status: "withheld",
|
|
821
|
+
fields
|
|
822
|
+
};
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
//#endregion
|
|
826
|
+
//#region packages/harnesses/src/preview/presentation.ts
|
|
827
|
+
async function presentUnit(item, details, localRoots, presentation = {
|
|
828
|
+
records: async (records) => {
|
|
829
|
+
const guarded = await preview(records, false, { localRoots });
|
|
830
|
+
return guarded.status === "withheld" ? null : guarded.text ?? null;
|
|
831
|
+
},
|
|
832
|
+
text: (text) => previewText(text, localRoots)
|
|
833
|
+
}) {
|
|
834
|
+
const warnings = [...item.warnings];
|
|
835
|
+
let text = null;
|
|
836
|
+
let detailsWithheld = item.detailsWithheld;
|
|
837
|
+
let conversation = null;
|
|
838
|
+
if (details && !detailsWithheld) {
|
|
839
|
+
text = await presentation.records(item.records);
|
|
840
|
+
detailsWithheld = text === null;
|
|
841
|
+
if (detailsWithheld) warnings.push("PREVIEW_DETAILS_WITHHELD");
|
|
842
|
+
} else if (!details && item.ordinal > 0) {
|
|
843
|
+
const body = item.conversation;
|
|
844
|
+
const user = body.user === null ? null : await presentation.text(body.user);
|
|
845
|
+
const assistant = body.assistant === null ? null : await presentation.text(body.assistant);
|
|
846
|
+
const userWithheld = body.userWithheld || body.user !== null && user === null;
|
|
847
|
+
const assistantWithheld = body.assistantWithheld || body.assistant !== null && assistant === null;
|
|
848
|
+
conversation = {
|
|
849
|
+
user,
|
|
850
|
+
assistant,
|
|
851
|
+
userWithheld,
|
|
852
|
+
assistantWithheld,
|
|
853
|
+
activities: activitySummaries(body)
|
|
854
|
+
};
|
|
855
|
+
if (userWithheld || assistantWithheld) warnings.push("PREVIEW_MESSAGE_WITHHELD");
|
|
856
|
+
}
|
|
857
|
+
return {
|
|
858
|
+
ordinal: item.ordinal,
|
|
859
|
+
role: item.ordinal === 0 ? "preamble" : "user-turn",
|
|
860
|
+
text,
|
|
861
|
+
detailsWithheld,
|
|
862
|
+
conversation,
|
|
863
|
+
withheld: false,
|
|
864
|
+
warnings
|
|
865
|
+
};
|
|
866
|
+
}
|
|
867
|
+
//#endregion
|
|
868
|
+
//#region packages/harnesses/src/preview/run.ts
|
|
869
|
+
async function runPreview(session, rawCursor, notify, processed) {
|
|
870
|
+
const sourceVersion = processed?.sourceVersion ?? previewSourceVersion(session);
|
|
871
|
+
const cursor = parsePreviewCursor(rawCursor, sourceVersion);
|
|
872
|
+
const stream = processed?.stream;
|
|
873
|
+
const summary = await countSequence(session, processed?.countStream ?? processed?.stream);
|
|
874
|
+
const ordinals = pageOrdinals(cursor, summary.totalUsers, summary.preamble);
|
|
875
|
+
processed?.select?.(summary, ordinals);
|
|
876
|
+
const retained = await retainUnits(session, summary, ordinals, cursor.mode === "r", stream);
|
|
877
|
+
const processing = processed?.processing();
|
|
878
|
+
const context = {
|
|
879
|
+
sourceVersion,
|
|
880
|
+
cursor,
|
|
881
|
+
summary,
|
|
882
|
+
...processing ? { processing } : {}
|
|
883
|
+
};
|
|
884
|
+
const units = [];
|
|
885
|
+
const localRoots = [...new Set(session.parts.map((part) => part.root))];
|
|
886
|
+
for (const item of retained) {
|
|
887
|
+
let unit;
|
|
888
|
+
if (item.withheld) unit = withheldUnit(item.ordinal, item.warnings);
|
|
889
|
+
else {
|
|
890
|
+
const timeoutPage = previewPage(context, [...units, withheldUnit(item.ordinal, ["PREVIEW_UNIT_TIMEOUT"])]);
|
|
891
|
+
if (!pageFits(timeoutPage)) break;
|
|
892
|
+
notify({
|
|
893
|
+
kind: "unit-start",
|
|
894
|
+
ordinal: item.ordinal,
|
|
895
|
+
timeoutPage
|
|
896
|
+
});
|
|
897
|
+
unit = await presentUnit(item, cursor.mode === "r", localRoots, processed?.presentation);
|
|
898
|
+
notify({
|
|
899
|
+
kind: "unit-end",
|
|
900
|
+
ordinal: item.ordinal
|
|
901
|
+
});
|
|
902
|
+
item.records = [];
|
|
903
|
+
}
|
|
904
|
+
if (!pageFits(previewPage(context, [...units, unit]))) {
|
|
905
|
+
if (units.length > 0) break;
|
|
906
|
+
unit = withheldUnit(item.ordinal, ["PREVIEW_RESPONSE_LIMIT"]);
|
|
907
|
+
}
|
|
908
|
+
units.push(unit);
|
|
909
|
+
}
|
|
910
|
+
for (const part of session.parts) await checkSource(part);
|
|
911
|
+
const result = previewPage(context, units);
|
|
912
|
+
if (units.length === 0 || !pageFits(result)) throw new Error("PREVIEW_WITHHELD");
|
|
913
|
+
return result;
|
|
914
|
+
}
|
|
915
|
+
//#endregion
|
|
916
|
+
export { classifyRecord as a, previewLimits as c, checkOpenSource as d, checkSource as f, openSource as h, pageFits as i, previewSourceVersion as l, noSymlinks as m, assertPreviewHeap as n, previewInputSchema as o, fileSnapshot as p, previewHeapLimits as r, previewMessageSchema as s, runPreview as t, createZstdFrameCheck as u };
|