@tangle-network/traces 0.16.3 → 0.17.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 +24 -3
- package/dist/{chunk-G5KKIQZ6.js → chunk-4QY4U4XW.js} +610 -186
- package/dist/cli.js +6 -5
- package/dist/index.d.ts +126 -26
- package/dist/index.js +7 -1
- package/docs/trace-analysts.md +6 -2
- package/package.json +7 -7
- package/skills/inspect-agent-traces/SKILL.md +33 -31
- package/docs/replay-batch-20260802.md +0 -76
|
@@ -9,13 +9,13 @@ function sourceFileId(path) {
|
|
|
9
9
|
function pointerPart(value) {
|
|
10
10
|
return value.replaceAll("~", "~0").replaceAll("/", "~1");
|
|
11
11
|
}
|
|
12
|
-
function locateSourceObjects(value, path,
|
|
12
|
+
function locateSourceObjects(value, path, bytes2, offset = 0) {
|
|
13
13
|
if (!value || typeof value !== "object") return;
|
|
14
14
|
const record2 = {
|
|
15
15
|
sourceId: sourceFileId(path),
|
|
16
16
|
recordOffset: offset,
|
|
17
|
-
recordBytes:
|
|
18
|
-
recordSha256: createHash("sha256").update(
|
|
17
|
+
recordBytes: bytes2.length,
|
|
18
|
+
recordSha256: createHash("sha256").update(bytes2).digest("hex")
|
|
19
19
|
};
|
|
20
20
|
const pending = [{ value, pointer: "#" }];
|
|
21
21
|
while (pending.length > 0) {
|
|
@@ -317,35 +317,35 @@ function serialize(value) {
|
|
|
317
317
|
return { value: canonicalJson(value), mimeType: "application/json" };
|
|
318
318
|
}
|
|
319
319
|
function utf8Prefix(value, maxBytes) {
|
|
320
|
-
let
|
|
320
|
+
let bytes2 = 0;
|
|
321
321
|
let end = 0;
|
|
322
322
|
for (const char of value) {
|
|
323
323
|
const charBytes = Buffer.byteLength(char);
|
|
324
|
-
if (
|
|
325
|
-
|
|
324
|
+
if (bytes2 + charBytes > maxBytes) break;
|
|
325
|
+
bytes2 += charBytes;
|
|
326
326
|
end += char.length;
|
|
327
327
|
}
|
|
328
328
|
return Buffer.from(value.slice(0, end), "utf8").toString("utf8");
|
|
329
329
|
}
|
|
330
|
-
function truncatedValue(value,
|
|
330
|
+
function truncatedValue(value, bytes2) {
|
|
331
331
|
const longestMarker = `
|
|
332
|
-
[truncated ${
|
|
332
|
+
[truncated ${bytes2} bytes]`;
|
|
333
333
|
const prefix = utf8Prefix(value, TOOL_IO_VALUE_MAX_BYTES - Buffer.byteLength(longestMarker));
|
|
334
|
-
const omittedBytes =
|
|
334
|
+
const omittedBytes = bytes2 - Buffer.byteLength(prefix);
|
|
335
335
|
return `${prefix}
|
|
336
336
|
[truncated ${omittedBytes} bytes]`;
|
|
337
337
|
}
|
|
338
338
|
function valueAttributes(side, raw) {
|
|
339
339
|
const serialized = serialize(raw);
|
|
340
340
|
if (!serialized) return {};
|
|
341
|
-
const
|
|
341
|
+
const bytes2 = Buffer.byteLength(serialized.value);
|
|
342
342
|
const sha2562 = createHash2("sha256").update(serialized.value).digest("hex");
|
|
343
|
-
const truncated =
|
|
344
|
-
const value = truncated ? truncatedValue(serialized.value,
|
|
343
|
+
const truncated = bytes2 > TOOL_IO_VALUE_MAX_BYTES;
|
|
344
|
+
const value = truncated ? truncatedValue(serialized.value, bytes2) : serialized.value;
|
|
345
345
|
return {
|
|
346
346
|
[`${side}.value`]: value,
|
|
347
347
|
[`${side}.mime_type`]: serialized.mimeType,
|
|
348
|
-
[`traces.${side}.bytes`]:
|
|
348
|
+
[`traces.${side}.bytes`]: bytes2,
|
|
349
349
|
[`traces.${side}.sha256`]: sha2562,
|
|
350
350
|
[`traces.${side}.truncated`]: truncated
|
|
351
351
|
};
|
|
@@ -1011,16 +1011,36 @@ function toOpenInferenceSpan(s) {
|
|
|
1011
1011
|
}
|
|
1012
1012
|
function serializeSpans(spans) {
|
|
1013
1013
|
if (spans.length === 0) return "";
|
|
1014
|
-
|
|
1015
|
-
|
|
1014
|
+
let out = "";
|
|
1015
|
+
for (const line of iterSerializedSpans(spans)) out += line;
|
|
1016
|
+
return out;
|
|
1017
|
+
}
|
|
1018
|
+
function* iterSerializedSpans(spans) {
|
|
1019
|
+
for (const s of validateOtlpSpans(spans, "serialized spans")) {
|
|
1020
|
+
yield `${JSON.stringify(toOpenInferenceSpan(s))}
|
|
1016
1021
|
`;
|
|
1022
|
+
}
|
|
1017
1023
|
}
|
|
1024
|
+
var OTLP_WRITE_CHUNK_CHARS = 4 * 1024 * 1024;
|
|
1018
1025
|
async function writeOtlpFile(spans, outPath) {
|
|
1019
|
-
const { mkdtemp: mkdtemp8,
|
|
1026
|
+
const { mkdtemp: mkdtemp8, open: open2 } = await import("fs/promises");
|
|
1020
1027
|
const { tmpdir: tmpdir8 } = await import("os");
|
|
1021
1028
|
const { join: join35 } = await import("path");
|
|
1022
1029
|
const path = outPath ?? join35(await mkdtemp8(join35(tmpdir8(), "traces-")), "spans.otlp.jsonl");
|
|
1023
|
-
await
|
|
1030
|
+
const handle = await open2(path, "w");
|
|
1031
|
+
try {
|
|
1032
|
+
let chunk = "";
|
|
1033
|
+
for (const line of iterSerializedSpans(spans)) {
|
|
1034
|
+
chunk += line;
|
|
1035
|
+
if (chunk.length >= OTLP_WRITE_CHUNK_CHARS) {
|
|
1036
|
+
await handle.write(chunk);
|
|
1037
|
+
chunk = "";
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
if (chunk.length > 0) await handle.write(chunk);
|
|
1041
|
+
} finally {
|
|
1042
|
+
await handle.close();
|
|
1043
|
+
}
|
|
1024
1044
|
return path;
|
|
1025
1045
|
}
|
|
1026
1046
|
|
|
@@ -1367,18 +1387,18 @@ function isMissingJsonSource(error) {
|
|
|
1367
1387
|
}
|
|
1368
1388
|
async function readJsonFile(path, options = {}) {
|
|
1369
1389
|
let raw;
|
|
1370
|
-
let
|
|
1390
|
+
let bytes2;
|
|
1371
1391
|
try {
|
|
1372
1392
|
if (options.captureSources) {
|
|
1373
|
-
|
|
1374
|
-
raw =
|
|
1393
|
+
bytes2 = await readFile3(path);
|
|
1394
|
+
raw = bytes2.toString("utf8");
|
|
1375
1395
|
} else raw = await readFile3(path, "utf8");
|
|
1376
1396
|
} catch (error) {
|
|
1377
1397
|
throw new JsonSourceError(path, "read", error);
|
|
1378
1398
|
}
|
|
1379
1399
|
try {
|
|
1380
1400
|
const value = JSON.parse(raw);
|
|
1381
|
-
if (
|
|
1401
|
+
if (bytes2) locateSourceObjects(value, path, bytes2);
|
|
1382
1402
|
return value;
|
|
1383
1403
|
} catch (error) {
|
|
1384
1404
|
throw new JsonSourceError(path, "parse", pathSafeParseCause(error));
|
|
@@ -1585,9 +1605,9 @@ function parseLine(rawLine, path, lineNumber, byteOffset, options) {
|
|
|
1585
1605
|
const jsonBytes = rawLine.at(-1) === 13 ? rawLine.subarray(0, -1) : rawLine;
|
|
1586
1606
|
if (jsonBytes.length === 0) return void 0;
|
|
1587
1607
|
if (!isUtf8(jsonBytes)) return handleCorruption(rawLine, path, lineNumber, byteOffset, options);
|
|
1588
|
-
const json2 = jsonBytes.toString("utf8");
|
|
1589
|
-
if (json2.trim().length === 0) return void 0;
|
|
1590
1608
|
try {
|
|
1609
|
+
const json2 = jsonBytes.toString("utf8");
|
|
1610
|
+
if (json2.trim().length === 0) return void 0;
|
|
1591
1611
|
const value = JSON.parse(json2);
|
|
1592
1612
|
if (options.captureSources) locateSourceObjects(value, path, rawLine, byteOffset);
|
|
1593
1613
|
return value;
|
|
@@ -1615,12 +1635,12 @@ async function* readJsonl(path, options = { mode: "strict" }) {
|
|
|
1615
1635
|
try {
|
|
1616
1636
|
for await (const chunk of input) {
|
|
1617
1637
|
options.signal?.throwIfAborted();
|
|
1618
|
-
const
|
|
1638
|
+
const bytes2 = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
1619
1639
|
let start = 0;
|
|
1620
|
-
let newline =
|
|
1640
|
+
let newline = bytes2.indexOf(10);
|
|
1621
1641
|
while (newline !== -1) {
|
|
1622
1642
|
options.signal?.throwIfAborted();
|
|
1623
|
-
fragments.push(
|
|
1643
|
+
fragments.push(bytes2.subarray(start, newline));
|
|
1624
1644
|
const rawLine = fragments.length === 1 ? fragments[0] : Buffer.concat(fragments);
|
|
1625
1645
|
fragments = [];
|
|
1626
1646
|
lineNumber += 1;
|
|
@@ -1628,9 +1648,9 @@ async function* readJsonl(path, options = { mode: "strict" }) {
|
|
|
1628
1648
|
if (parsed !== void 0) yield parsed;
|
|
1629
1649
|
byteOffset += rawLine.length + 1;
|
|
1630
1650
|
start = newline + 1;
|
|
1631
|
-
newline =
|
|
1651
|
+
newline = bytes2.indexOf(10, start);
|
|
1632
1652
|
}
|
|
1633
|
-
if (start <
|
|
1653
|
+
if (start < bytes2.length) fragments.push(bytes2.subarray(start));
|
|
1634
1654
|
}
|
|
1635
1655
|
if (fragments.length > 0) {
|
|
1636
1656
|
options.signal?.throwIfAborted();
|
|
@@ -1814,7 +1834,7 @@ var CORRECTIVE_REACTIONS = ["correction", "frustration", "jargon", "structure"];
|
|
|
1814
1834
|
import { createHash as createHash6 } from "crypto";
|
|
1815
1835
|
import { readdir as readdir5, stat as stat3 } from "fs/promises";
|
|
1816
1836
|
import { homedir as homedir3 } from "os";
|
|
1817
|
-
import { basename as
|
|
1837
|
+
import { basename as basename4, join as join5, relative as relative3 } from "path";
|
|
1818
1838
|
import {
|
|
1819
1839
|
applyLlmSpanOtlpAttributes as applyLlmSpanOtlpAttributes2,
|
|
1820
1840
|
LLM_CACHED_TOKENS,
|
|
@@ -1827,7 +1847,7 @@ import { ATTR as ATTR3, deriveHexId as deriveHexId2 } from "@tangle-network/agen
|
|
|
1827
1847
|
// src/adapters/claude-subagent-sources.ts
|
|
1828
1848
|
import { readdir as readdir4, realpath } from "fs/promises";
|
|
1829
1849
|
import { homedir as homedir2 } from "os";
|
|
1830
|
-
import {
|
|
1850
|
+
import { dirname as dirname3, isAbsolute as isAbsolute2, join as join4, relative as relative2, resolve as resolve3 } from "path";
|
|
1831
1851
|
|
|
1832
1852
|
// src/adapters/claude-workflow.ts
|
|
1833
1853
|
import { basename as basename3, dirname as dirname2, isAbsolute, relative, resolve as resolve2 } from "path";
|
|
@@ -1928,7 +1948,7 @@ function workflowRunIdForSubagent(subDir, file) {
|
|
|
1928
1948
|
}
|
|
1929
1949
|
return parts[1];
|
|
1930
1950
|
}
|
|
1931
|
-
function bindWorkflowSubagent(file, runId, transcriptDir, bindings) {
|
|
1951
|
+
function bindWorkflowSubagent(file, runId, transcriptDir, bindings, sourceTranscriptDir = transcriptDir) {
|
|
1932
1952
|
const runBindings = bindings.get(runId);
|
|
1933
1953
|
if (!runBindings || runBindings.length === 0) {
|
|
1934
1954
|
throw new ClaudeTaskScopeError(
|
|
@@ -1942,7 +1962,7 @@ function bindWorkflowSubagent(file, runId, transcriptDir, bindings) {
|
|
|
1942
1962
|
);
|
|
1943
1963
|
}
|
|
1944
1964
|
const localBindings = runBindings.filter(
|
|
1945
|
-
(binding) => resolve2(binding.transcriptDir) ===
|
|
1965
|
+
(binding) => resolve2(binding.transcriptDir) === resolve2(sourceTranscriptDir)
|
|
1946
1966
|
);
|
|
1947
1967
|
if (localBindings.length === 0) {
|
|
1948
1968
|
throw new ClaudeTaskScopeError(
|
|
@@ -1995,7 +2015,7 @@ function selectWorkflowBinding(file, bindings, childStartedAt, explicitToolUseId
|
|
|
1995
2015
|
}
|
|
1996
2016
|
|
|
1997
2017
|
// src/adapters/claude-subagent-sources.ts
|
|
1998
|
-
async function listSubagentFiles(root, signal) {
|
|
2018
|
+
async function listSubagentFiles(root, signal, copiedWorkflow = false) {
|
|
1999
2019
|
const pending = [root];
|
|
2000
2020
|
const files = [];
|
|
2001
2021
|
while (pending.length > 0) {
|
|
@@ -2012,6 +2032,11 @@ async function listSubagentFiles(root, signal) {
|
|
|
2012
2032
|
signal?.throwIfAborted();
|
|
2013
2033
|
for (const entry of entries) {
|
|
2014
2034
|
const path = join4(dir, entry.name);
|
|
2035
|
+
if (copiedWorkflow && entry.isSymbolicLink()) {
|
|
2036
|
+
throw new ClaudeTaskScopeError(
|
|
2037
|
+
`Claude Workflow subagent does not match copied source: ${path}`
|
|
2038
|
+
);
|
|
2039
|
+
}
|
|
2015
2040
|
if (entry.isDirectory()) {
|
|
2016
2041
|
pending.push(path);
|
|
2017
2042
|
} else if (entry.isFile() && /^agent-.*\.jsonl$/.test(entry.name)) {
|
|
@@ -2029,23 +2054,47 @@ function workflowStorageRoot(ref) {
|
|
|
2029
2054
|
const nativeRoot = resolve3(homedir2(), ".claude", "projects");
|
|
2030
2055
|
return pathIsWithin(nativeRoot, ref.path) ? nativeRoot : dirname3(resolve3(ref.path));
|
|
2031
2056
|
}
|
|
2057
|
+
function relocatedWorkflowDirectory(ref, binding) {
|
|
2058
|
+
const source = binding.transcriptDir;
|
|
2059
|
+
if (resolve3(source) !== source || source.includes("\\")) return void 0;
|
|
2060
|
+
const parts = source.split("/").slice(-7);
|
|
2061
|
+
const sessionId = parts[3];
|
|
2062
|
+
if (parts[0] !== ".claude" || parts[1] !== "projects" || !parts[2] || !sessionId || !/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(sessionId) || parts[4] !== "subagents" || parts[5] !== "workflows" || parts[6] !== binding.runId) return void 0;
|
|
2063
|
+
const sessionRoot2 = join4(dirname3(resolve3(ref.path)), sessionId);
|
|
2064
|
+
return {
|
|
2065
|
+
sessionRoot: sessionRoot2,
|
|
2066
|
+
transcriptDir: join4(sessionRoot2, "subagents", "workflows", binding.runId)
|
|
2067
|
+
};
|
|
2068
|
+
}
|
|
2032
2069
|
async function workflowTranscriptDirectories(ref, bindings) {
|
|
2033
2070
|
const allowedRoot = workflowStorageRoot(ref);
|
|
2034
2071
|
const allowedRealRoot = await realpath(allowedRoot);
|
|
2035
2072
|
const directories = /* @__PURE__ */ new Map();
|
|
2036
2073
|
for (const runBindings of bindings.values()) {
|
|
2037
2074
|
for (const binding of runBindings) {
|
|
2038
|
-
|
|
2075
|
+
let directory = resolve3(binding.transcriptDir);
|
|
2076
|
+
let relocation;
|
|
2039
2077
|
if (!pathIsWithin(allowedRoot, directory)) {
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2078
|
+
relocation = relocatedWorkflowDirectory(ref, binding);
|
|
2079
|
+
if (!relocation || !pathIsWithin(allowedRoot, relocation.transcriptDir)) {
|
|
2080
|
+
throw new ClaudeTaskScopeError(
|
|
2081
|
+
`Claude Workflow transcript directory escapes ${allowedRoot}: ${directory}`
|
|
2082
|
+
);
|
|
2083
|
+
}
|
|
2084
|
+
directory = relocation.transcriptDir;
|
|
2043
2085
|
}
|
|
2044
2086
|
let realDirectory;
|
|
2045
2087
|
try {
|
|
2046
2088
|
realDirectory = await realpath(directory);
|
|
2047
2089
|
} catch (error) {
|
|
2048
|
-
if (isMissingPathError(error))
|
|
2090
|
+
if (isMissingPathError(error)) {
|
|
2091
|
+
if (relocation) {
|
|
2092
|
+
throw new ClaudeTaskScopeError(
|
|
2093
|
+
`Cannot relocate Claude Workflow run ${binding.runId}: copied transcript directory is missing: ${directory}`
|
|
2094
|
+
);
|
|
2095
|
+
}
|
|
2096
|
+
continue;
|
|
2097
|
+
}
|
|
2049
2098
|
throw error;
|
|
2050
2099
|
}
|
|
2051
2100
|
if (!pathIsWithin(allowedRealRoot, realDirectory)) {
|
|
@@ -2053,10 +2102,38 @@ async function workflowTranscriptDirectories(ref, bindings) {
|
|
|
2053
2102
|
`Claude Workflow transcript directory escapes ${allowedRoot}: ${directory}`
|
|
2054
2103
|
);
|
|
2055
2104
|
}
|
|
2056
|
-
|
|
2105
|
+
if (relocation) {
|
|
2106
|
+
const copiedRoot = dirname3(resolve3(ref.path));
|
|
2107
|
+
const copiedRealRoot = await realpath(copiedRoot);
|
|
2108
|
+
const sessionRealRoot = await realpath(relocation.sessionRoot);
|
|
2109
|
+
const expectedSessionRealRoot = resolve3(
|
|
2110
|
+
copiedRealRoot,
|
|
2111
|
+
relative2(copiedRoot, relocation.sessionRoot)
|
|
2112
|
+
);
|
|
2113
|
+
const expectedRealDirectory = resolve3(copiedRealRoot, relative2(copiedRoot, directory));
|
|
2114
|
+
if (sessionRealRoot !== expectedSessionRealRoot || realDirectory !== expectedRealDirectory) {
|
|
2115
|
+
throw new ClaudeTaskScopeError(
|
|
2116
|
+
`Claude Workflow transcript directory does not match copied session subtree ${relocation.sessionRoot}: ${directory}`
|
|
2117
|
+
);
|
|
2118
|
+
}
|
|
2119
|
+
}
|
|
2120
|
+
const previous = directories.get(realDirectory);
|
|
2121
|
+
if (previous && (previous.runId !== binding.runId || resolve3(previous.sourceTranscriptDir ?? previous.transcriptDir) !== resolve3(binding.transcriptDir))) {
|
|
2122
|
+
throw new ClaudeTaskScopeError(
|
|
2123
|
+
`Claude Workflow transcript directory ${directory} has conflicting source bindings`
|
|
2124
|
+
);
|
|
2125
|
+
}
|
|
2126
|
+
directories.set(realDirectory, {
|
|
2127
|
+
runId: binding.runId,
|
|
2128
|
+
transcriptDir: directory,
|
|
2129
|
+
sourceTranscriptDir: binding.transcriptDir,
|
|
2130
|
+
...relocation ? { copiedWorkflow: true } : {}
|
|
2131
|
+
});
|
|
2057
2132
|
}
|
|
2058
2133
|
}
|
|
2059
|
-
return [...directories.values()].sort(
|
|
2134
|
+
return [...directories.values()].sort(
|
|
2135
|
+
(left, right) => left.transcriptDir.localeCompare(right.transcriptDir)
|
|
2136
|
+
);
|
|
2060
2137
|
}
|
|
2061
2138
|
async function collectClaudeSubagentSources(ref, bindings, signal) {
|
|
2062
2139
|
const subDir = join4(ref.path.replace(/\.jsonl$/, ""), "subagents");
|
|
@@ -2073,10 +2150,11 @@ async function collectClaudeSubagentSources(ref, bindings, signal) {
|
|
|
2073
2150
|
});
|
|
2074
2151
|
}
|
|
2075
2152
|
}
|
|
2076
|
-
for (const
|
|
2153
|
+
for (const location of await workflowTranscriptDirectories(ref, bindings)) {
|
|
2077
2154
|
signal?.throwIfAborted();
|
|
2078
|
-
const runId =
|
|
2079
|
-
|
|
2155
|
+
const { runId, transcriptDir } = location;
|
|
2156
|
+
const files2 = await listSubagentFiles(transcriptDir, signal, location.copiedWorkflow);
|
|
2157
|
+
for (const file of files2) {
|
|
2080
2158
|
const path = resolve3(file);
|
|
2081
2159
|
filesByPath.set(path, file);
|
|
2082
2160
|
const previous = workflowByPath.get(path);
|
|
@@ -2085,7 +2163,7 @@ async function collectClaudeSubagentSources(ref, bindings, signal) {
|
|
|
2085
2163
|
`Claude Workflow subagent ${file} belongs to multiple transcript directories`
|
|
2086
2164
|
);
|
|
2087
2165
|
}
|
|
2088
|
-
workflowByPath.set(path,
|
|
2166
|
+
workflowByPath.set(path, location);
|
|
2089
2167
|
}
|
|
2090
2168
|
}
|
|
2091
2169
|
const files = [...filesByPath.values()].sort();
|
|
@@ -2171,6 +2249,7 @@ function createClaudeStream(startStep) {
|
|
|
2171
2249
|
step: startStep,
|
|
2172
2250
|
sawUserTurn: false,
|
|
2173
2251
|
promptSpans: [],
|
|
2252
|
+
promptSpanByUid: /* @__PURE__ */ new Map(),
|
|
2174
2253
|
sawRecordedOrigin: false,
|
|
2175
2254
|
firstRecordAt: null,
|
|
2176
2255
|
lastRecordAt: null
|
|
@@ -2203,6 +2282,9 @@ function projectToolResult(toolUseId, isError, output, structuredWorkflowRun, ou
|
|
|
2203
2282
|
}
|
|
2204
2283
|
function projectClaudeEvent(event) {
|
|
2205
2284
|
const timestamp = event.timestamp ?? EPOCH;
|
|
2285
|
+
if (event.type === "system" && event.subtype === "compact_boundary") {
|
|
2286
|
+
return { kind: "compaction", timestamp: event.timestamp ?? "", metadata: event.compactMetadata ?? {}, parentToolUseId: event.parent_tool_use_id };
|
|
2287
|
+
}
|
|
2206
2288
|
if (event.type === "assistant" && event.message) {
|
|
2207
2289
|
const tools = [];
|
|
2208
2290
|
for (const block of asBlocks(event.message.content)) {
|
|
@@ -2344,7 +2426,7 @@ function indexWorkflowProjection(projection, index) {
|
|
|
2344
2426
|
function fingerprintClaudeEvent(event) {
|
|
2345
2427
|
return createHash6("sha256").update(JSON.stringify(
|
|
2346
2428
|
event,
|
|
2347
|
-
(key, value) => key === "contentSource" || key.startsWith(SOURCE_ATTRIBUTE_PREFIX) ? void 0 : value
|
|
2429
|
+
(key, value) => key === "contentSource" || key === "originKind" || key.startsWith(SOURCE_ATTRIBUTE_PREFIX) ? void 0 : value
|
|
2348
2430
|
)).digest("hex");
|
|
2349
2431
|
}
|
|
2350
2432
|
function startsClaudeTask(projection) {
|
|
@@ -2358,21 +2440,43 @@ function startsClaudeTask(projection) {
|
|
|
2358
2440
|
function distinctClaudeEvent(event, seen, sourcePath, fallbackUid) {
|
|
2359
2441
|
const projection = projectClaudeEvent(event);
|
|
2360
2442
|
if (event.uuid) {
|
|
2443
|
+
const uid = event.uuid;
|
|
2361
2444
|
const fingerprint = fingerprintClaudeEvent(projection);
|
|
2362
|
-
const
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2445
|
+
const originKind = (projection.kind === "user" || projection.kind === "queued-prompt" ? projection.originKind : null) ?? null;
|
|
2446
|
+
const previous = seen.get(uid);
|
|
2447
|
+
if (previous) {
|
|
2448
|
+
if (previous.fingerprint !== fingerprint) {
|
|
2449
|
+
throw new ClaudeEventConflictError(sourcePath, uid);
|
|
2450
|
+
}
|
|
2451
|
+
if (previous.originKind === null && originKind !== null) return { originEnrichment: originKind, uid };
|
|
2452
|
+
if (previous.originKind !== null && originKind !== null && previous.originKind !== originKind) {
|
|
2453
|
+
throw new ClaudeEventConflictError(sourcePath, uid);
|
|
2366
2454
|
}
|
|
2367
2455
|
return void 0;
|
|
2368
2456
|
}
|
|
2369
|
-
seen.set(
|
|
2457
|
+
seen.set(uid, { fingerprint, originKind });
|
|
2370
2458
|
}
|
|
2371
2459
|
return { projection, uid: event.uuid ?? fallbackUid };
|
|
2372
2460
|
}
|
|
2461
|
+
function applyOriginEnrichment(uid, originKind, state) {
|
|
2462
|
+
const prompt = state.promptSpanByUid.get(uid);
|
|
2463
|
+
if (!prompt) return;
|
|
2464
|
+
if (prompt.attributes[ORIGIN_RECORDED_ATTR] === true) return;
|
|
2465
|
+
state.sawRecordedOrigin = true;
|
|
2466
|
+
prompt.attributes[ORIGIN_RECORDED_ATTR] = true;
|
|
2467
|
+
prompt.attributes[ORIGIN_KIND_ATTR] = originKind;
|
|
2468
|
+
prompt.attributes[ACTOR_ATTR] = claudeActorFromOrigin(
|
|
2469
|
+
originKind,
|
|
2470
|
+
() => prompt.attributes[ACTOR_ATTR]
|
|
2471
|
+
);
|
|
2472
|
+
}
|
|
2373
2473
|
function consumeDistinctClaudeEvent(event, ctx, state, seen, sourcePath) {
|
|
2374
2474
|
const accepted = distinctClaudeEvent(event, seen, sourcePath, `step${state.step}`);
|
|
2375
2475
|
if (!accepted) return false;
|
|
2476
|
+
if ("originEnrichment" in accepted) {
|
|
2477
|
+
applyOriginEnrichment(accepted.uid, accepted.originEnrichment, state);
|
|
2478
|
+
return false;
|
|
2479
|
+
}
|
|
2376
2480
|
consumeClaudeEvent(accepted.projection, accepted.uid, ctx, state);
|
|
2377
2481
|
return true;
|
|
2378
2482
|
}
|
|
@@ -2382,6 +2486,34 @@ function inStreamParent(parentToolUseId, state) {
|
|
|
2382
2486
|
return parentTool ? { span: parentTool, confidence: "explicit" } : void 0;
|
|
2383
2487
|
}
|
|
2384
2488
|
function consumeClaudeEvent(event, uid, ctx, state) {
|
|
2489
|
+
if (event.kind === "compaction") {
|
|
2490
|
+
const extra = {};
|
|
2491
|
+
const numericFields = {
|
|
2492
|
+
preTokens: "traces.compaction.tokens_before",
|
|
2493
|
+
postTokens: "traces.compaction.tokens_after",
|
|
2494
|
+
durationMs: "traces.compaction.latency_ms",
|
|
2495
|
+
cumulativeDroppedTokens: "traces.compaction.cumulative_dropped_tokens"
|
|
2496
|
+
};
|
|
2497
|
+
for (const [field, key] of Object.entries(numericFields)) {
|
|
2498
|
+
const value = event.metadata[field];
|
|
2499
|
+
if (typeof value === "number" && Number.isFinite(value) && value >= 0) extra[key] = value;
|
|
2500
|
+
}
|
|
2501
|
+
if (typeof event.metadata.trigger === "string") extra["traces.compaction.trigger"] = event.metadata.trigger;
|
|
2502
|
+
const parent = inStreamParent(event.parentToolUseId, state);
|
|
2503
|
+
state.spans.push(span({
|
|
2504
|
+
traceId: ctx.traceId,
|
|
2505
|
+
spanId: ctx.idPrefix + uid + ":compacted",
|
|
2506
|
+
parentSpanId: parent ? parent.span.span_id : ctx.rootParent,
|
|
2507
|
+
name: "session.compacted",
|
|
2508
|
+
kind: "CHAIN",
|
|
2509
|
+
startTime: event.timestamp,
|
|
2510
|
+
service: SERVICE2,
|
|
2511
|
+
agent: ctx.agent,
|
|
2512
|
+
step: state.step++,
|
|
2513
|
+
extra
|
|
2514
|
+
}));
|
|
2515
|
+
return;
|
|
2516
|
+
}
|
|
2385
2517
|
if (event.kind === "assistant") {
|
|
2386
2518
|
const messageId = event.messageId ?? uid;
|
|
2387
2519
|
let llmSpan = state.llmSpanByMessageId.get(messageId);
|
|
@@ -2495,6 +2627,7 @@ function consumeClaudeEvent(event, uid, ctx, state) {
|
|
|
2495
2627
|
if (userParent) prompt.attributes[ATTR3.parentConfidence] = userParent.confidence;
|
|
2496
2628
|
state.spans.push(prompt);
|
|
2497
2629
|
state.promptSpans.push(prompt);
|
|
2630
|
+
state.promptSpanByUid.set(uid, prompt);
|
|
2498
2631
|
state.step += 1;
|
|
2499
2632
|
}
|
|
2500
2633
|
for (const result of event.results) {
|
|
@@ -2524,6 +2657,7 @@ function consumeClaudeEvent(event, uid, ctx, state) {
|
|
|
2524
2657
|
}
|
|
2525
2658
|
state.spans.push(prompt);
|
|
2526
2659
|
state.promptSpans.push(prompt);
|
|
2660
|
+
state.promptSpanByUid.set(uid, prompt);
|
|
2527
2661
|
state.step += 1;
|
|
2528
2662
|
} else if (event.kind === "file-change") {
|
|
2529
2663
|
state.spans.push(span({
|
|
@@ -2685,7 +2819,7 @@ async function readClaudeWorkflowBindings(ref, options) {
|
|
|
2685
2819
|
options.signal?.throwIfAborted();
|
|
2686
2820
|
const accepted = distinctClaudeEvent(event, seen, ref.path, `source${row}`);
|
|
2687
2821
|
row += 1;
|
|
2688
|
-
if (accepted) indexWorkflowProjection(accepted.projection, index);
|
|
2822
|
+
if (accepted && "projection" in accepted) indexWorkflowProjection(accepted.projection, index);
|
|
2689
2823
|
}
|
|
2690
2824
|
return bindWorkflowRuns(index);
|
|
2691
2825
|
}
|
|
@@ -2794,7 +2928,7 @@ var ClaudeAdapter = class {
|
|
|
2794
2928
|
if (opts.sinceMs && st.mtimeMs < opts.sinceMs) continue;
|
|
2795
2929
|
const cwd = `/${dir.replace(/^-/, "").replace(/-/g, "/")}`;
|
|
2796
2930
|
if (opts.cwd && !cwd.startsWith(opts.cwd)) continue;
|
|
2797
|
-
refs.push({ harness: this.harness, sessionId:
|
|
2931
|
+
refs.push({ harness: this.harness, sessionId: basename4(f, ".jsonl"), path, cwd, mtimeMs: st.mtimeMs });
|
|
2798
2932
|
}
|
|
2799
2933
|
}
|
|
2800
2934
|
return refs.sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
@@ -2837,7 +2971,10 @@ var ClaudeAdapter = class {
|
|
|
2837
2971
|
runResult = { failed: event.is_error, subtype: event.subtype ?? "unknown" };
|
|
2838
2972
|
}
|
|
2839
2973
|
const accepted = distinctClaudeEvent(event, seen, ref.path, `step${state.step}`);
|
|
2840
|
-
if (!accepted)
|
|
2974
|
+
if (!accepted || "originEnrichment" in accepted) {
|
|
2975
|
+
if (accepted) applyOriginEnrichment(accepted.uid, accepted.originEnrichment, state);
|
|
2976
|
+
continue;
|
|
2977
|
+
}
|
|
2841
2978
|
indexWorkflowProjection(accepted.projection, workflowIndex);
|
|
2842
2979
|
const startsTask = startsClaudeTask(accepted.projection);
|
|
2843
2980
|
if (startsTask && taskScope === "latest") {
|
|
@@ -2945,7 +3082,7 @@ var ClaudeAdapter = class {
|
|
|
2945
3082
|
const agent = {
|
|
2946
3083
|
file,
|
|
2947
3084
|
sourceKey,
|
|
2948
|
-
agentId:
|
|
3085
|
+
agentId: basename4(file, ".jsonl").replace(/^agent-/, ""),
|
|
2949
3086
|
meta,
|
|
2950
3087
|
...meta.toolUseId ? { parentToolUseId: meta.toolUseId } : {}
|
|
2951
3088
|
};
|
|
@@ -2954,11 +3091,12 @@ var ClaudeAdapter = class {
|
|
|
2954
3091
|
file,
|
|
2955
3092
|
workflowRunId,
|
|
2956
3093
|
workflowLocation.transcriptDir,
|
|
2957
|
-
workflowBindings
|
|
3094
|
+
workflowBindings,
|
|
3095
|
+
workflowLocation.sourceTranscriptDir
|
|
2958
3096
|
);
|
|
2959
3097
|
const parsed = await parseClaudeSubagent(ref, traceId, agent, options);
|
|
2960
3098
|
const { binding, confidence } = selectWorkflowBinding(
|
|
2961
|
-
|
|
3099
|
+
basename4(file),
|
|
2962
3100
|
runBindings,
|
|
2963
3101
|
firstSubagentTimestamp(agent, parsed),
|
|
2964
3102
|
meta.toolUseId
|
|
@@ -3527,7 +3665,7 @@ async function resolveCodexParentTask(ref, childSessionId, options = {}) {
|
|
|
3527
3665
|
// src/adapters/codex.ts
|
|
3528
3666
|
import { readdir as readdir6, stat as stat4 } from "fs/promises";
|
|
3529
3667
|
import { homedir as homedir4 } from "os";
|
|
3530
|
-
import { basename as
|
|
3668
|
+
import { basename as basename5, join as join6 } from "path";
|
|
3531
3669
|
import { deriveHexId as deriveHexId3 } from "@tangle-network/agent-trace-contract";
|
|
3532
3670
|
var SERVICE3 = "codex";
|
|
3533
3671
|
var SESSION_HEAD_LINES = 40;
|
|
@@ -3999,7 +4137,7 @@ var CodexAdapter = class {
|
|
|
3999
4137
|
}
|
|
4000
4138
|
if (opts.sinceMs && st.mtimeMs < opts.sinceMs) return void 0;
|
|
4001
4139
|
let cwd = null;
|
|
4002
|
-
let id =
|
|
4140
|
+
let id = basename5(path).replace(/^rollout-[\dT-]+-/, "").replace(/\.jsonl$/, "");
|
|
4003
4141
|
const ref = { harness: this.harness, sessionId: id, path, cwd, mtimeMs: st.mtimeMs };
|
|
4004
4142
|
const head = await takeJsonl(path, SESSION_HEAD_LINES, sessionJsonlOptions(ref));
|
|
4005
4143
|
for (const parsed of head) {
|
|
@@ -4027,7 +4165,7 @@ var CodexAdapter = class {
|
|
|
4027
4165
|
async locateBySessionId(sessionId, opts = {}) {
|
|
4028
4166
|
const refs = [];
|
|
4029
4167
|
for await (const path of walkRollouts(this.root())) {
|
|
4030
|
-
if (!
|
|
4168
|
+
if (!basename5(path).includes(sessionId)) continue;
|
|
4031
4169
|
const ref = await this.refFromPath(path, opts);
|
|
4032
4170
|
if (ref?.sessionId === sessionId) refs.push(ref);
|
|
4033
4171
|
}
|
|
@@ -4242,12 +4380,12 @@ var CodexAdapter = class {
|
|
|
4242
4380
|
spans.push(turnSpan);
|
|
4243
4381
|
step += 1;
|
|
4244
4382
|
};
|
|
4245
|
-
const recordInheritedContext = (l, ts) => {
|
|
4383
|
+
const recordInheritedContext = (l, ts, inScope = false) => {
|
|
4246
4384
|
if (l.type === "compacted") {
|
|
4247
4385
|
const payload = l.payload;
|
|
4248
4386
|
if (!payload) return;
|
|
4249
4387
|
const summary = capText(typeof payload.message === "string" ? payload.message : "");
|
|
4250
|
-
if (
|
|
4388
|
+
if (claimInheritedSpan()) {
|
|
4251
4389
|
spans.push(span({
|
|
4252
4390
|
traceId,
|
|
4253
4391
|
spanId: `inherited:${step}:compacted`,
|
|
@@ -4262,7 +4400,7 @@ var CodexAdapter = class {
|
|
|
4262
4400
|
contentSource: textSources(payload, "message"),
|
|
4263
4401
|
extra: {
|
|
4264
4402
|
[INHERITED_SPAN_ATTR]: true,
|
|
4265
|
-
[INHERITED_SOURCE_ATTR]: "compacted",
|
|
4403
|
+
[INHERITED_SOURCE_ATTR]: inScope ? "compacted" : "pre-task-prefix",
|
|
4266
4404
|
...typeof payload.window_number === "number" ? { "traces.codex.compaction_window_number": payload.window_number } : {},
|
|
4267
4405
|
...payload.window_id ? { "traces.codex.compaction_window_id": payload.window_id } : {},
|
|
4268
4406
|
...payload.previous_window_id ? { "traces.codex.compaction_previous_window_id": payload.previous_window_id } : {}
|
|
@@ -4436,7 +4574,7 @@ var CodexAdapter = class {
|
|
|
4436
4574
|
step += 1;
|
|
4437
4575
|
}
|
|
4438
4576
|
} else if (l.type === "compacted") {
|
|
4439
|
-
recordInheritedContext(l, ts);
|
|
4577
|
+
recordInheritedContext(l, ts, true);
|
|
4440
4578
|
} else if (l.type === "response_item" && (l.payload?.type === "function_call" || l.payload?.type === "custom_tool_call")) {
|
|
4441
4579
|
const outerName = l.payload.name ?? "tool";
|
|
4442
4580
|
const callId = l.payload.call_id ?? `${step}`;
|
|
@@ -5276,7 +5414,7 @@ var CopilotAdapter = class {
|
|
|
5276
5414
|
// src/adapters/factory.ts
|
|
5277
5415
|
import { readdir as readdir8, stat as stat6 } from "fs/promises";
|
|
5278
5416
|
import { homedir as homedir6 } from "os";
|
|
5279
|
-
import { basename as
|
|
5417
|
+
import { basename as basename6, join as join8 } from "path";
|
|
5280
5418
|
var SERVICE6 = "factory";
|
|
5281
5419
|
function textOf4(content) {
|
|
5282
5420
|
return capText(
|
|
@@ -5321,7 +5459,7 @@ var FactoryAdapter = class {
|
|
|
5321
5459
|
throw error;
|
|
5322
5460
|
}
|
|
5323
5461
|
if (opts.sinceMs && st.mtimeMs < opts.sinceMs) continue;
|
|
5324
|
-
refs.push({ harness: this.harness, sessionId:
|
|
5462
|
+
refs.push({ harness: this.harness, sessionId: basename6(f, ".jsonl"), path, cwd, mtimeMs: st.mtimeMs });
|
|
5325
5463
|
}
|
|
5326
5464
|
}
|
|
5327
5465
|
return refs.sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
@@ -5460,7 +5598,7 @@ var FactoryAdapter = class {
|
|
|
5460
5598
|
// src/adapters/forge.ts
|
|
5461
5599
|
import { readdir as readdir9, stat as stat7 } from "fs/promises";
|
|
5462
5600
|
import { homedir as homedir7 } from "os";
|
|
5463
|
-
import { basename as
|
|
5601
|
+
import { basename as basename7, join as join9 } from "path";
|
|
5464
5602
|
var SERVICE7 = "forge";
|
|
5465
5603
|
function tokens(t) {
|
|
5466
5604
|
if (!t) return null;
|
|
@@ -5498,7 +5636,7 @@ var ForgeAdapter = class {
|
|
|
5498
5636
|
throw error;
|
|
5499
5637
|
}
|
|
5500
5638
|
if (opts.sinceMs && st.mtimeMs < opts.sinceMs) continue;
|
|
5501
|
-
refs.push({ harness: this.harness, sessionId:
|
|
5639
|
+
refs.push({ harness: this.harness, sessionId: basename7(f, ".json"), path, cwd: dir, mtimeMs: st.mtimeMs });
|
|
5502
5640
|
}
|
|
5503
5641
|
}
|
|
5504
5642
|
return refs.sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
@@ -5960,7 +6098,7 @@ var OpencodeAdapter = class {
|
|
|
5960
6098
|
// src/adapters/pi.ts
|
|
5961
6099
|
import { readdir as readdir12, stat as stat10 } from "fs/promises";
|
|
5962
6100
|
import { homedir as homedir10 } from "os";
|
|
5963
|
-
import { basename as
|
|
6101
|
+
import { basename as basename8, join as join12, resolve as resolve4 } from "path";
|
|
5964
6102
|
var SERVICE9 = "pi";
|
|
5965
6103
|
function terminalStatus(message) {
|
|
5966
6104
|
const stopReason = message?.stopReason ?? "missing";
|
|
@@ -6042,7 +6180,7 @@ var PiAdapter = class {
|
|
|
6042
6180
|
}
|
|
6043
6181
|
const refs = [];
|
|
6044
6182
|
for (const path of paths) {
|
|
6045
|
-
const f =
|
|
6183
|
+
const f = basename8(path);
|
|
6046
6184
|
let st;
|
|
6047
6185
|
try {
|
|
6048
6186
|
st = await stat10(path);
|
|
@@ -6051,7 +6189,7 @@ var PiAdapter = class {
|
|
|
6051
6189
|
throw error;
|
|
6052
6190
|
}
|
|
6053
6191
|
if (opts.sinceMs && st.mtimeMs < opts.sinceMs) continue;
|
|
6054
|
-
let id =
|
|
6192
|
+
let id = basename8(f, ".jsonl").replace(/^[\dTZ.-]+_/, "");
|
|
6055
6193
|
let cwd = null;
|
|
6056
6194
|
const ref = {
|
|
6057
6195
|
harness: this.harness,
|
|
@@ -6099,6 +6237,27 @@ var PiAdapter = class {
|
|
|
6099
6237
|
if (!sessionLine && l.type === "session") {
|
|
6100
6238
|
sessionLine = { id: l.id, timestamp: l.timestamp };
|
|
6101
6239
|
}
|
|
6240
|
+
if (l.type === "compaction") {
|
|
6241
|
+
spans.push(span({
|
|
6242
|
+
traceId: sourceTraceId,
|
|
6243
|
+
spanId: "compacted:" + (l.id ?? step),
|
|
6244
|
+
parentSpanId: sourceRootId,
|
|
6245
|
+
name: "session.compacted",
|
|
6246
|
+
kind: "CHAIN",
|
|
6247
|
+
service: SERVICE9,
|
|
6248
|
+
agent: SERVICE9,
|
|
6249
|
+
startTime: l.timestamp ?? "",
|
|
6250
|
+
step: step++,
|
|
6251
|
+
content: typeof l.summary === "string" ? capText(l.summary) : void 0,
|
|
6252
|
+
contentSource: sourceOf(l, "summary"),
|
|
6253
|
+
extra: {
|
|
6254
|
+
...typeof l.tokensBefore === "number" && Number.isFinite(l.tokensBefore) && l.tokensBefore >= 0 ? { "traces.compaction.tokens_before": l.tokensBefore } : {},
|
|
6255
|
+
...typeof l.firstKeptEntryId === "string" ? { "traces.pi.first_kept_entry_id": l.firstKeptEntryId } : {},
|
|
6256
|
+
...l.systemMessage !== void 0 ? { "gen_ai.system_instructions": JSON.stringify(l.systemMessage) } : {}
|
|
6257
|
+
}
|
|
6258
|
+
}));
|
|
6259
|
+
continue;
|
|
6260
|
+
}
|
|
6102
6261
|
if (l.type !== "message" || !l.message) continue;
|
|
6103
6262
|
const ts = l.timestamp ?? (/* @__PURE__ */ new Date(0)).toISOString();
|
|
6104
6263
|
const mid = l.id ?? `m${step}`;
|
|
@@ -6263,7 +6422,7 @@ var PiAdapter = class {
|
|
|
6263
6422
|
// src/adapters/qwen.ts
|
|
6264
6423
|
import { readdir as readdir13, stat as stat11 } from "fs/promises";
|
|
6265
6424
|
import { homedir as homedir11 } from "os";
|
|
6266
|
-
import { basename as
|
|
6425
|
+
import { basename as basename9, join as join13 } from "path";
|
|
6267
6426
|
var SERVICE10 = "qwen";
|
|
6268
6427
|
function textOf9(parts) {
|
|
6269
6428
|
if (!parts) return "";
|
|
@@ -6306,7 +6465,7 @@ var QwenAdapter = class {
|
|
|
6306
6465
|
throw error;
|
|
6307
6466
|
}
|
|
6308
6467
|
if (opts.sinceMs && st.mtimeMs < opts.sinceMs) continue;
|
|
6309
|
-
refs.push({ harness: this.harness, sessionId:
|
|
6468
|
+
refs.push({ harness: this.harness, sessionId: basename9(f, ".jsonl"), path, cwd: null, mtimeMs: st.mtimeMs });
|
|
6310
6469
|
}
|
|
6311
6470
|
}
|
|
6312
6471
|
return refs.sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
@@ -6719,7 +6878,7 @@ function extractAbsolutePaths(spans) {
|
|
|
6719
6878
|
async function resolveRepoAttrs(cwd) {
|
|
6720
6879
|
if (!cwd) return {};
|
|
6721
6880
|
const attrs = { [ATTR.CWD]: cwd };
|
|
6722
|
-
const
|
|
6881
|
+
const basename18 = (() => {
|
|
6723
6882
|
const trimmed = cwd.replace(/\/+$/g, "");
|
|
6724
6883
|
const idx = trimmed.lastIndexOf("/");
|
|
6725
6884
|
const name = idx === -1 ? trimmed : trimmed.slice(idx + 1);
|
|
@@ -6741,7 +6900,7 @@ async function resolveRepoAttrs(cwd) {
|
|
|
6741
6900
|
if (commit) attrs[ATTR.GIT_COMMIT] = commit;
|
|
6742
6901
|
} catch {
|
|
6743
6902
|
}
|
|
6744
|
-
attrs[ATTR.SUBJECT_KEY] =
|
|
6903
|
+
attrs[ATTR.SUBJECT_KEY] = basename18;
|
|
6745
6904
|
return attrs;
|
|
6746
6905
|
}
|
|
6747
6906
|
async function repoCandidateForPath(path) {
|
|
@@ -7336,7 +7495,7 @@ async function readLoopRuns(cwds, sessionIds) {
|
|
|
7336
7495
|
}
|
|
7337
7496
|
return { linkedCounts, unlinkedCounts, filesRead };
|
|
7338
7497
|
}
|
|
7339
|
-
|
|
7498
|
+
function computeAdoption(spans) {
|
|
7340
7499
|
const { sessionByTrace, conflicts } = indexSessionIdsByTrace(spans);
|
|
7341
7500
|
const identifiedSessions = new Set(sessionByTrace.values());
|
|
7342
7501
|
const unassignedTraceIds = new Set(
|
|
@@ -7393,11 +7552,9 @@ async function analyzeAdoption(spans, opts = {}) {
|
|
|
7393
7552
|
subagentSpawns[type] = (subagentSpawns[type] ?? 0) + 1;
|
|
7394
7553
|
}
|
|
7395
7554
|
}
|
|
7396
|
-
const {
|
|
7397
|
-
|
|
7398
|
-
|
|
7399
|
-
filesRead
|
|
7400
|
-
} = await readLoopRuns(opts.cwds ?? [], identifiedSessions);
|
|
7555
|
+
const loopDispatchedRuns = {};
|
|
7556
|
+
const unlinkedLoopDispatchedRuns = {};
|
|
7557
|
+
const filesRead = 0;
|
|
7401
7558
|
const totalSkillInvocations = Object.values(skillInvocations).reduce((a, b) => a + b, 0);
|
|
7402
7559
|
const totalSkillDocumentReads = Object.values(skillDocumentReads).reduce((a, b) => a + b, 0);
|
|
7403
7560
|
const totalSubagentSpawns = Object.values(subagentSpawns).reduce((a, b) => a + b, 0);
|
|
@@ -7441,6 +7598,19 @@ async function analyzeAdoption(spans, opts = {}) {
|
|
|
7441
7598
|
totalUnlinkedLoopDispatchedRuns
|
|
7442
7599
|
};
|
|
7443
7600
|
}
|
|
7601
|
+
async function analyzeAdoption(spans, opts = {}) {
|
|
7602
|
+
const report = computeAdoption(spans);
|
|
7603
|
+
const { sessionByTrace } = indexSessionIdsByTrace(spans);
|
|
7604
|
+
const history = await readLoopRuns(opts.cwds ?? [], new Set(sessionByTrace.values()));
|
|
7605
|
+
return {
|
|
7606
|
+
...report,
|
|
7607
|
+
loopDispatchedRuns: history.linkedCounts,
|
|
7608
|
+
totalLoopDispatchedRuns: Object.values(history.linkedCounts).reduce((sum, count2) => sum + count2, 0),
|
|
7609
|
+
unlinkedLoopDispatchedRuns: history.unlinkedCounts,
|
|
7610
|
+
totalUnlinkedLoopDispatchedRuns: Object.values(history.unlinkedCounts).reduce((sum, count2) => sum + count2, 0),
|
|
7611
|
+
skillRunFilesRead: history.filesRead
|
|
7612
|
+
};
|
|
7613
|
+
}
|
|
7444
7614
|
|
|
7445
7615
|
// src/agentic-routing.ts
|
|
7446
7616
|
import {
|
|
@@ -7933,7 +8103,7 @@ var COMMAND_PREFIXES = /* @__PURE__ */ new Set([
|
|
|
7933
8103
|
var ASSIGNMENT = /^[A-Za-z_][A-Za-z0-9_]*=/;
|
|
7934
8104
|
var MAX_SHELL_DEPTH = 4;
|
|
7935
8105
|
var MAX_WORDS = 2e4;
|
|
7936
|
-
function
|
|
8106
|
+
function basename10(word) {
|
|
7937
8107
|
const cut = word.lastIndexOf("/");
|
|
7938
8108
|
return cut === -1 ? word : word.slice(cut + 1);
|
|
7939
8109
|
}
|
|
@@ -8032,7 +8202,7 @@ var ShellScanner = class {
|
|
|
8032
8202
|
let start = 0;
|
|
8033
8203
|
while (start < words.length) {
|
|
8034
8204
|
const word = words[start];
|
|
8035
|
-
if (ASSIGNMENT.test(word) || COMMAND_PREFIXES.has(
|
|
8205
|
+
if (ASSIGNMENT.test(word) || COMMAND_PREFIXES.has(basename10(word))) start += 1;
|
|
8036
8206
|
else break;
|
|
8037
8207
|
}
|
|
8038
8208
|
return start === 0 || start >= words.length ? words : words.slice(start);
|
|
@@ -8173,7 +8343,7 @@ function shellCommands(script, depth = 0) {
|
|
|
8173
8343
|
const out = [];
|
|
8174
8344
|
for (const words of commands) {
|
|
8175
8345
|
out.push(words);
|
|
8176
|
-
const name =
|
|
8346
|
+
const name = basename10(words[0] ?? "");
|
|
8177
8347
|
if (!SHELLS.has(name) || depth >= MAX_SHELL_DEPTH) continue;
|
|
8178
8348
|
for (let index = 1; index < words.length; index += 1) {
|
|
8179
8349
|
const word = words[index];
|
|
@@ -8201,7 +8371,7 @@ function commandTextFromInput(input) {
|
|
|
8201
8371
|
if (!Array.isArray(value) || value.length === 0) return void 0;
|
|
8202
8372
|
const words = value.filter((entry) => typeof entry === "string");
|
|
8203
8373
|
if (words.length !== value.length) return void 0;
|
|
8204
|
-
const head =
|
|
8374
|
+
const head = basename10(words[0] ?? "");
|
|
8205
8375
|
if (SHELLS.has(head)) {
|
|
8206
8376
|
const flag = words.findIndex((word, position) => position > 0 && /^-[a-z]*c$/.test(word));
|
|
8207
8377
|
if (flag !== -1 && words[flag + 1] !== void 0) return words[flag + 1];
|
|
@@ -8263,7 +8433,7 @@ function stringAttribute2(span2, key) {
|
|
|
8263
8433
|
const value = span2.attributes[key];
|
|
8264
8434
|
return typeof value === "string" ? value : null;
|
|
8265
8435
|
}
|
|
8266
|
-
function
|
|
8436
|
+
function basename11(word) {
|
|
8267
8437
|
const cut = word.lastIndexOf("/");
|
|
8268
8438
|
return cut === -1 ? word : word.slice(cut + 1);
|
|
8269
8439
|
}
|
|
@@ -8346,7 +8516,7 @@ function numbersInLine(line) {
|
|
|
8346
8516
|
function inferHeadBranch(commands, upTo, output) {
|
|
8347
8517
|
for (let index = upTo - 1; index >= 0; index -= 1) {
|
|
8348
8518
|
const words = commands[index];
|
|
8349
|
-
if (
|
|
8519
|
+
if (basename11(words[0] ?? "") !== "git" || words.length < 2) continue;
|
|
8350
8520
|
const sub = words[1];
|
|
8351
8521
|
const rest = words.slice(2);
|
|
8352
8522
|
if (sub === "push") {
|
|
@@ -8399,13 +8569,13 @@ function mergeEvent(record2, recordIndex, words) {
|
|
|
8399
8569
|
const target = positional[0] ?? null;
|
|
8400
8570
|
const numberInCommand = target === null ? null : /^#?(\d+)$/.exec(target)?.[1] ?? numbersInLine(target)[0] ?? null;
|
|
8401
8571
|
const numberInOutput = numberInCommand === null ? MERGED_NUMBER.exec(record2.output)?.[1] ?? null : null;
|
|
8402
|
-
const
|
|
8403
|
-
if (!reachedGh(record2, "merge",
|
|
8572
|
+
const number2 = numberInCommand ?? numberInOutput;
|
|
8573
|
+
if (!reachedGh(record2, "merge", number2 !== null)) return null;
|
|
8404
8574
|
const evidence3 = numberInCommand !== null ? "the pull request the command names" : numberInOutput !== null ? `the merge line this command printed, in span ${record2.spanId}` : target !== null ? "the branch the command names; no output that joins to it stated a number" : "none: the command named neither a pull request nor a branch";
|
|
8405
8575
|
return {
|
|
8406
8576
|
action: "merge",
|
|
8407
|
-
number,
|
|
8408
|
-
headBranch:
|
|
8577
|
+
number: number2,
|
|
8578
|
+
headBranch: number2 === null ? target : null,
|
|
8409
8579
|
command: words.join(" "),
|
|
8410
8580
|
evidence: evidence3,
|
|
8411
8581
|
spanIds: [record2.spanId],
|
|
@@ -8421,7 +8591,7 @@ function eventsFromRecord(record2, recordIndex) {
|
|
|
8421
8591
|
let createIndex = 0;
|
|
8422
8592
|
for (let index = 0; index < commands.length; index += 1) {
|
|
8423
8593
|
const words = commands[index];
|
|
8424
|
-
if (words.length < 3 || !GH_NAMES.has(
|
|
8594
|
+
if (words.length < 3 || !GH_NAMES.has(basename11(words[0])) || words[1] !== "pr") continue;
|
|
8425
8595
|
const action = words[2];
|
|
8426
8596
|
if (action === "create") {
|
|
8427
8597
|
const printedNumber = printed[createIndex] ?? null;
|
|
@@ -8444,7 +8614,7 @@ function numberFromLaterOutput(records, after, headBranch) {
|
|
|
8444
8614
|
${record2.output}`.split("\n")) {
|
|
8445
8615
|
const line = raw.replace(ANSI, "");
|
|
8446
8616
|
if (!line.includes(headBranch)) continue;
|
|
8447
|
-
for (const
|
|
8617
|
+
for (const number2 of numbersInLine(line)) found.add(number2);
|
|
8448
8618
|
}
|
|
8449
8619
|
if (found.size === 1) return { number: [...found][0], spanId: record2.spanId };
|
|
8450
8620
|
}
|
|
@@ -8533,9 +8703,144 @@ function readPullRequests(spans) {
|
|
|
8533
8703
|
};
|
|
8534
8704
|
}
|
|
8535
8705
|
|
|
8706
|
+
// src/session-facts.ts
|
|
8707
|
+
import { createHash as createHash7 } from "crypto";
|
|
8708
|
+
|
|
8709
|
+
// src/session-measurements.ts
|
|
8710
|
+
var number = (value) => typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : null;
|
|
8711
|
+
function measure(spans, read, reason) {
|
|
8712
|
+
let total = 0;
|
|
8713
|
+
let measured2 = 0;
|
|
8714
|
+
for (const span2 of spans) {
|
|
8715
|
+
const value = read(span2);
|
|
8716
|
+
if (value !== null) {
|
|
8717
|
+
total += value;
|
|
8718
|
+
measured2 += 1;
|
|
8719
|
+
}
|
|
8720
|
+
}
|
|
8721
|
+
return {
|
|
8722
|
+
total: measured2 > 0 || spans.length === 0 ? total : null,
|
|
8723
|
+
measured: measured2,
|
|
8724
|
+
eligible: spans.length,
|
|
8725
|
+
unavailable: measured2 === spans.length ? null : reason
|
|
8726
|
+
};
|
|
8727
|
+
}
|
|
8728
|
+
function bytes(span2, side) {
|
|
8729
|
+
const recorded = number(span2.attributes[`traces.${side}.bytes`]);
|
|
8730
|
+
if (recorded !== null) return recorded;
|
|
8731
|
+
const text2 = span2.attributes[`${side}.value`];
|
|
8732
|
+
if (typeof text2 !== "string" || span2.attributes[`traces.${side}.truncated`] === true) return null;
|
|
8733
|
+
return Buffer.byteLength(text2);
|
|
8734
|
+
}
|
|
8735
|
+
function toolMeasurements(tools) {
|
|
8736
|
+
const groups = /* @__PURE__ */ new Map();
|
|
8737
|
+
for (const tool of tools) {
|
|
8738
|
+
const name = typeof tool.attributes["tool.name"] === "string" ? tool.attributes["tool.name"] : tool.name;
|
|
8739
|
+
const group = groups.get(name);
|
|
8740
|
+
if (group) group.push(tool);
|
|
8741
|
+
else groups.set(name, [tool]);
|
|
8742
|
+
}
|
|
8743
|
+
return [...groups].sort(([a], [b]) => a.localeCompare(b)).map(([name, spans]) => ({
|
|
8744
|
+
name,
|
|
8745
|
+
calls: spans.length,
|
|
8746
|
+
outcomes: {
|
|
8747
|
+
ok: spans.filter((span2) => span2.status.code === "OK").length,
|
|
8748
|
+
error: spans.filter((span2) => span2.status.code === "ERROR").length,
|
|
8749
|
+
unset: spans.filter((span2) => span2.status.code === "UNSET").length
|
|
8750
|
+
},
|
|
8751
|
+
inputBytes: measure(spans, (span2) => bytes(span2, "input"), "Input byte count missing or truncated without a size receipt."),
|
|
8752
|
+
outputBytes: measure(spans, (span2) => bytes(span2, "output"), "Output byte count missing or truncated without a size receipt."),
|
|
8753
|
+
callToResultMs: measure(spans, (span2) => {
|
|
8754
|
+
if (span2.status.code === "UNSET") return null;
|
|
8755
|
+
const elapsed2 = Date.parse(span2.end_time) - Date.parse(span2.start_time);
|
|
8756
|
+
return Number.isFinite(elapsed2) && elapsed2 > 0 ? elapsed2 : null;
|
|
8757
|
+
}, "A completed span with distinct recorded endpoints is required; equal endpoints may be an adapter default."),
|
|
8758
|
+
spanIds: spans.map((span2) => span2.span_id)
|
|
8759
|
+
}));
|
|
8760
|
+
}
|
|
8761
|
+
function compactions(spans) {
|
|
8762
|
+
const events = spans.filter((span2) => span2.name === "session.compacted" && span2.attributes[INHERITED_SOURCE_ATTR] !== "pre-task-prefix").sort((a, b) => Date.parse(a.start_time) - Date.parse(b.start_time));
|
|
8763
|
+
if (events.length === 0) return {
|
|
8764
|
+
value: null,
|
|
8765
|
+
spanIds: [],
|
|
8766
|
+
unavailable: "No compaction boundary was captured. This does not establish that no compaction occurred."
|
|
8767
|
+
};
|
|
8768
|
+
return {
|
|
8769
|
+
value: events.map((span2, i) => {
|
|
8770
|
+
const previous = i > 0 ? events[i - 1] : void 0;
|
|
8771
|
+
const interval = previous ? Date.parse(span2.start_time) - Date.parse(previous.start_time) : NaN;
|
|
8772
|
+
return {
|
|
8773
|
+
spanId: span2.span_id,
|
|
8774
|
+
at: span2.start_time,
|
|
8775
|
+
sincePreviousMs: Number.isFinite(interval) && interval >= 0 ? interval : null,
|
|
8776
|
+
latencyMs: number(span2.attributes["traces.compaction.latency_ms"]),
|
|
8777
|
+
tokensBefore: number(span2.attributes["traces.compaction.tokens_before"]),
|
|
8778
|
+
tokensAfter: number(span2.attributes["traces.compaction.tokens_after"])
|
|
8779
|
+
};
|
|
8780
|
+
}),
|
|
8781
|
+
spanIds: events.map((span2) => span2.span_id),
|
|
8782
|
+
unavailable: null,
|
|
8783
|
+
partial: "Observed boundaries only. Latency and context sizes are null unless the source explicitly records them."
|
|
8784
|
+
};
|
|
8785
|
+
}
|
|
8786
|
+
function contextFootprints(spans) {
|
|
8787
|
+
const out = [];
|
|
8788
|
+
for (const span2 of spans) {
|
|
8789
|
+
const fields = ["gen_ai.tool.definitions", "gen_ai.system_instructions"];
|
|
8790
|
+
if (span2.name === "message.system" || span2.name === "message.developer" || span2.name === "user.prompt" && span2.attributes["tangle.actor"] === "injected") fields.push("content");
|
|
8791
|
+
for (const field of fields) {
|
|
8792
|
+
const value = span2.attributes[field];
|
|
8793
|
+
if (value === void 0 || value === null) continue;
|
|
8794
|
+
const text2 = typeof value === "string" ? value : canonicalJson(value);
|
|
8795
|
+
let entries = Array.isArray(value) ? value.length : null;
|
|
8796
|
+
if (typeof value === "string") {
|
|
8797
|
+
try {
|
|
8798
|
+
const parsed = JSON.parse(value);
|
|
8799
|
+
if (Array.isArray(parsed)) entries = parsed.length;
|
|
8800
|
+
} catch {
|
|
8801
|
+
}
|
|
8802
|
+
}
|
|
8803
|
+
out.push({ spanId: span2.span_id, field, bytes: Buffer.byteLength(text2), lines: text2.split("\n").length, entries, scope: "captured-attribute" });
|
|
8804
|
+
}
|
|
8805
|
+
}
|
|
8806
|
+
return out;
|
|
8807
|
+
}
|
|
8808
|
+
function computeSessionMeasurements(spans, tools, sessionId, harness) {
|
|
8809
|
+
const spawns = tools.filter((span2) => span2.attributes["traces.agent.spawn"] === true || span2.attributes["traces.codex.agent_operation"] === "spawn_agent").sort((a, b) => Date.parse(a.start_time) - Date.parse(b.start_time));
|
|
8810
|
+
return {
|
|
8811
|
+
observedSpawns: spawns.length,
|
|
8812
|
+
spawnIntervalsMs: spawns.map((span2, i) => {
|
|
8813
|
+
const previous = spawns[i - 1];
|
|
8814
|
+
const interval = previous ? Date.parse(span2.start_time) - Date.parse(previous.start_time) : NaN;
|
|
8815
|
+
return { spanId: span2.span_id, intervalMs: Number.isFinite(interval) && interval >= 0 ? interval : null };
|
|
8816
|
+
}),
|
|
8817
|
+
execution: spans.length > 0 ? summarizeSpanExecution(spans) : null,
|
|
8818
|
+
adoption: computeAdoption(spans),
|
|
8819
|
+
relationship: describeSessionRelationship({
|
|
8820
|
+
sessionId: sessionId ?? spans[0]?.trace_id ?? "",
|
|
8821
|
+
harness: harness ?? "",
|
|
8822
|
+
path: "",
|
|
8823
|
+
cwd: null,
|
|
8824
|
+
mtimeMs: 0
|
|
8825
|
+
}, spans),
|
|
8826
|
+
tools: toolMeasurements(tools),
|
|
8827
|
+
compactions: compactions(spans),
|
|
8828
|
+
contextFootprints: contextFootprints(spans),
|
|
8829
|
+
coverage: {
|
|
8830
|
+
profile: "Native spans do not establish the authored or effective AgentProfile revision; join the Runtime profile receipt by recorded identity.",
|
|
8831
|
+
materializedResources: "Captured attributes measure only the visible representation. Missing tools, skills, MCP definitions, and resources are unknown.",
|
|
8832
|
+
contextTokens: "No tokenizer estimates are substituted for the actual request. Per-component context token counts require producer telemetry.",
|
|
8833
|
+
toolTokens: "Tool byte counts are not model input tokens; token attribution to tool content is not captured.",
|
|
8834
|
+
compaction: "Only recorded boundaries are counted. Zero-duration adapter defaults are not measured compaction latency.",
|
|
8835
|
+
semanticAssessments: "Success at the task, useful skill application, waste, reward hacking, safety, and dishonesty require separately attributed evaluation."
|
|
8836
|
+
}
|
|
8837
|
+
};
|
|
8838
|
+
}
|
|
8839
|
+
|
|
8536
8840
|
// src/session-facts.ts
|
|
8537
8841
|
import { OPENINFERENCE_SPAN_KIND as OPENINFERENCE_SPAN_KIND4, TOOL_NAME as TOOL_NAME5 } from "@tangle-network/agent-eval/trace-attributes";
|
|
8538
8842
|
import { ingestSpans, rankFirstFailure } from "@tangle-network/agent-eval/diagnosis";
|
|
8843
|
+
var SESSION_FACTS_MEASUREMENT_VERSION = "3";
|
|
8539
8844
|
var SESSION_TOKEN_TOTAL_ATTR = "traces.session.total_tokens";
|
|
8540
8845
|
var SPAN_SYNTHESIZED_ATTR = "traces.codex.span_synthesized";
|
|
8541
8846
|
var CORRUPTION_COUNT_ATTR = "traces.session.corruption_count";
|
|
@@ -8810,15 +9115,17 @@ function humanTurnsOf(spans) {
|
|
|
8810
9115
|
excluded: [...excluded].sort(([left], [right]) => left.localeCompare(right)).map(([reason, spanIds]) => ({ reason, turns: spanIds.length, spanIds }))
|
|
8811
9116
|
};
|
|
8812
9117
|
}
|
|
8813
|
-
function computeSessionFacts(spans) {
|
|
9118
|
+
function computeSessionFacts(spans, options = {}) {
|
|
8814
9119
|
const { sessionByTrace } = indexSessionIdsByTrace(spans);
|
|
8815
9120
|
const byTrace = /* @__PURE__ */ new Map();
|
|
8816
9121
|
for (const span2 of spans) {
|
|
8817
|
-
|
|
9122
|
+
const group = byTrace.get(span2.trace_id);
|
|
9123
|
+
if (group) group.push(span2);
|
|
9124
|
+
else byTrace.set(span2.trace_id, [span2]);
|
|
8818
9125
|
}
|
|
8819
|
-
return [...byTrace].sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0).map(([traceId, traceSpans]) => sessionFactsForTrace(traceId, sessionByTrace.get(traceId) ?? null, traceSpans));
|
|
9126
|
+
return [...byTrace].sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0).map(([traceId, traceSpans]) => sessionFactsForTrace(traceId, sessionByTrace.get(traceId) ?? null, traceSpans, options));
|
|
8820
9127
|
}
|
|
8821
|
-
function sessionFactsForTrace(traceId, sessionId, unordered) {
|
|
9128
|
+
function sessionFactsForTrace(traceId, sessionId, unordered, options) {
|
|
8822
9129
|
const spans = [...unordered].sort(byTraceOrder);
|
|
8823
9130
|
const harness = spans.map((span2) => stringAttr2(span2, "service.name")).find((name) => name !== null) ?? null;
|
|
8824
9131
|
const ownSpans = spans.filter((span2) => !isSubagentSpan(span2.attributes));
|
|
@@ -8857,7 +9164,7 @@ function sessionFactsForTrace(traceId, sessionId, unordered) {
|
|
|
8857
9164
|
(span2) => span2.parent_span_id !== null && span2.attributes[CORRUPTION_RECEIPT_VERSION_ATTR] === void 0
|
|
8858
9165
|
).length;
|
|
8859
9166
|
const corruptionSpan = spans.find((span2) => typeof span2.attributes[CORRUPTION_COUNT_ATTR] === "number");
|
|
8860
|
-
|
|
9167
|
+
const facts = {
|
|
8861
9168
|
schemaVersion: 1,
|
|
8862
9169
|
kind: "traces.session_facts",
|
|
8863
9170
|
traceId,
|
|
@@ -8874,6 +9181,7 @@ function sessionFactsForTrace(traceId, sessionId, unordered) {
|
|
|
8874
9181
|
spanIds: [],
|
|
8875
9182
|
unavailable: `no span carries ${CORRUPTION_COUNT_ATTR}; this trace was not read through the session integrity path, so whether any record was skipped is unknown`
|
|
8876
9183
|
},
|
|
9184
|
+
measurements: computeSessionMeasurements(ownSpans, invoked, sessionId, harness),
|
|
8877
9185
|
toolCalls: {
|
|
8878
9186
|
value: invoked.length,
|
|
8879
9187
|
spanIds: invoked.map((span2) => span2.span_id),
|
|
@@ -8926,15 +9234,50 @@ function sessionFactsForTrace(traceId, sessionId, unordered) {
|
|
|
8926
9234
|
unavailable: `no span carries ${SESSION_TOKEN_TOTAL_ATTR}; the per-turn llm.turn deltas in this trace are a different quantity and summing them would not be the harness total`
|
|
8927
9235
|
}
|
|
8928
9236
|
};
|
|
9237
|
+
if (options.includeContent !== false) return facts;
|
|
9238
|
+
const withheld = () => ({
|
|
9239
|
+
value: null,
|
|
9240
|
+
spanIds: [],
|
|
9241
|
+
unavailable: "Content was not requested."
|
|
9242
|
+
});
|
|
9243
|
+
return {
|
|
9244
|
+
...facts,
|
|
9245
|
+
humanTurns: withheld(),
|
|
9246
|
+
finalMessages: withheld(),
|
|
9247
|
+
changedFiles: withheld(),
|
|
9248
|
+
pullRequests: withheld(),
|
|
9249
|
+
subagents: {
|
|
9250
|
+
...facts.subagents,
|
|
9251
|
+
value: facts.subagents.value?.map((entry) => ({
|
|
9252
|
+
...entry,
|
|
9253
|
+
taskName: null,
|
|
9254
|
+
taskNameUnavailable: "Assignment text was not requested."
|
|
9255
|
+
})) ?? null
|
|
9256
|
+
},
|
|
9257
|
+
firstFailure: facts.firstFailure.status === "found" ? { ...facts.firstFailure, message: null, classification: facts.firstFailure.classification ? { ...facts.firstFailure.classification, reason: "Content was not requested." } : null } : facts.firstFailure
|
|
9258
|
+
};
|
|
8929
9259
|
}
|
|
8930
9260
|
function buildSessionFactsReport(spans, options = {}) {
|
|
9261
|
+
const digest = createHash7("sha256");
|
|
9262
|
+
const ordered2 = [...spans].sort((a, b) => a.trace_id.localeCompare(b.trace_id) || a.span_id.localeCompare(b.span_id));
|
|
9263
|
+
let evidenceThrough = null;
|
|
9264
|
+
for (const span2 of ordered2) {
|
|
9265
|
+
digest.update(canonicalJson(span2)).update("\n");
|
|
9266
|
+
for (const time of [span2.start_time, span2.end_time]) {
|
|
9267
|
+
if (Number.isFinite(Date.parse(time)) && (evidenceThrough === null || Date.parse(time) > Date.parse(evidenceThrough))) evidenceThrough = time;
|
|
9268
|
+
}
|
|
9269
|
+
}
|
|
8931
9270
|
return {
|
|
8932
9271
|
schemaVersion: 1,
|
|
8933
9272
|
kind: "traces.session_facts_report",
|
|
9273
|
+
measurementVersion: SESSION_FACTS_MEASUREMENT_VERSION,
|
|
9274
|
+
sourceDigest: "sha256:" + digest.digest("hex"),
|
|
9275
|
+
evidenceThrough,
|
|
9276
|
+
contentIncluded: options.includeContent !== false,
|
|
8934
9277
|
generatedAt: options.generatedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
8935
9278
|
harness: options.harness ?? null,
|
|
8936
9279
|
spanCount: spans.length,
|
|
8937
|
-
sessions: computeSessionFacts(spans)
|
|
9280
|
+
sessions: computeSessionFacts(spans, options)
|
|
8938
9281
|
};
|
|
8939
9282
|
}
|
|
8940
9283
|
function factLine(label2, fact, rendered) {
|
|
@@ -8983,6 +9326,8 @@ function renderSessionFacts(report) {
|
|
|
8983
9326
|
lines.push(factLine("changed files", facts.changedFiles, String(facts.changedFiles.value?.length ?? 0)));
|
|
8984
9327
|
lines.push(factLine("first record", facts.firstRecordAt, String(facts.firstRecordAt.value)));
|
|
8985
9328
|
lines.push(factLine("last record", facts.lastRecordAt, String(facts.lastRecordAt.value)));
|
|
9329
|
+
lines.push(factLine("compactions", facts.measurements.compactions, String(facts.measurements.compactions.value?.length ?? 0)));
|
|
9330
|
+
lines.push(" skills: " + facts.measurements.adoption.totalSkillInvocations + " recorded invocation(s), " + facts.measurements.adoption.totalSkillDocumentReads + " document read(s); telemetry " + facts.measurements.adoption.skillTelemetryStatus);
|
|
8986
9331
|
lines.push(factLine("token total", facts.tokenTotal, String(facts.tokenTotal.value)));
|
|
8987
9332
|
lines.push(` first failure: ${renderFirstFailure(facts.firstFailure)}`);
|
|
8988
9333
|
if ((facts.unreadRecords.value ?? 0) > 0) {
|
|
@@ -9028,6 +9373,9 @@ ${JSON.stringify({ sessions, ...dropped2.length ? { omitted_fields: dropped2 } :
|
|
|
9028
9373
|
["tool_call_span_ids", (facts) => {
|
|
9029
9374
|
facts.tool_calls.span_ids = [];
|
|
9030
9375
|
}],
|
|
9376
|
+
["measurements", (facts) => {
|
|
9377
|
+
delete facts.measurements;
|
|
9378
|
+
}],
|
|
9031
9379
|
["tool_calls_by_name", (facts) => {
|
|
9032
9380
|
delete facts.tool_calls_by_name;
|
|
9033
9381
|
}],
|
|
@@ -9152,7 +9500,8 @@ function compactFacts(facts) {
|
|
|
9152
9500
|
first_record_at: factJson(facts.firstRecordAt),
|
|
9153
9501
|
last_record_at: factJson(facts.lastRecordAt),
|
|
9154
9502
|
token_total: factJson(facts.tokenTotal),
|
|
9155
|
-
first_failure: compactFirstFailure(facts.firstFailure)
|
|
9503
|
+
first_failure: compactFirstFailure(facts.firstFailure),
|
|
9504
|
+
measurements: facts.measurements
|
|
9156
9505
|
};
|
|
9157
9506
|
}
|
|
9158
9507
|
function compactFirstFailure(failure2) {
|
|
@@ -9181,7 +9530,7 @@ function sessionFactsContext(spans, options = {}) {
|
|
|
9181
9530
|
if (report.sessions.length === 0) return void 0;
|
|
9182
9531
|
return renderSessionFactsContext(report, options);
|
|
9183
9532
|
}
|
|
9184
|
-
var SESSION_FACTS_VERSION_SUFFIX = "session-facts.
|
|
9533
|
+
var SESSION_FACTS_VERSION_SUFFIX = "session-facts." + SESSION_FACTS_MEASUREMENT_VERSION;
|
|
9185
9534
|
function withSessionFactsContext(definitions, spans, options = {}) {
|
|
9186
9535
|
const sheet = sessionFactsContext(spans, options);
|
|
9187
9536
|
if (sheet === void 0) return [...definitions];
|
|
@@ -9208,13 +9557,13 @@ import { OtlpFileTraceStore } from "@tangle-network/agent-eval/traces";
|
|
|
9208
9557
|
|
|
9209
9558
|
// src/bundle-source.ts
|
|
9210
9559
|
import { isUtf8 as isUtf82 } from "buffer";
|
|
9211
|
-
import { createHash as
|
|
9560
|
+
import { createHash as createHash10 } from "crypto";
|
|
9212
9561
|
import { constants } from "fs";
|
|
9213
9562
|
import { lstat as lstat2, open, realpath as realpath3 } from "fs/promises";
|
|
9214
9563
|
import { dirname as dirname8, isAbsolute as isAbsolute4, join as join20, relative as relative5, resolve as resolve7, sep as sep2 } from "path";
|
|
9215
9564
|
|
|
9216
9565
|
// src/bundle.ts
|
|
9217
|
-
import { createHash as
|
|
9566
|
+
import { createHash as createHash9 } from "crypto";
|
|
9218
9567
|
import { copyFile, cp, lstat, mkdir as mkdir2, readdir as readdir16, readFile as readFile7, stat as stat14, writeFile as writeFile5 } from "fs/promises";
|
|
9219
9568
|
import { dirname as dirname7, join as join19, relative as relative4, resolve as resolve6, sep } from "path";
|
|
9220
9569
|
|
|
@@ -10443,7 +10792,7 @@ function measured(value, render) {
|
|
|
10443
10792
|
}
|
|
10444
10793
|
function distributionRow(label2, distribution, unit = "") {
|
|
10445
10794
|
if (distribution.n === 0) return `| ${label2} | 0 | not captured | not captured | not captured | not captured | not captured |`;
|
|
10446
|
-
const value = (
|
|
10795
|
+
const value = (number2) => measured(number2, (captured) => `${decimal(captured)}${unit}`);
|
|
10447
10796
|
return `| ${label2} | ${count(distribution.n)} | ${value(distribution.min)} | ${value(distribution.mean)} | ${value(distribution.p50)} | ${value(distribution.p95)} | ${value(distribution.max)} |`;
|
|
10448
10797
|
}
|
|
10449
10798
|
function tokenRows(usage) {
|
|
@@ -11794,7 +12143,7 @@ async function writeSessionIndexFile(index, outPath) {
|
|
|
11794
12143
|
}
|
|
11795
12144
|
|
|
11796
12145
|
// src/session-selection.ts
|
|
11797
|
-
import { createHash as
|
|
12146
|
+
import { createHash as createHash8 } from "crypto";
|
|
11798
12147
|
import { createReadStream as createReadStream2 } from "fs";
|
|
11799
12148
|
|
|
11800
12149
|
// src/session-workflow.ts
|
|
@@ -12120,7 +12469,7 @@ async function collectSessionWorkflow(adapter, seeds, options = {}) {
|
|
|
12120
12469
|
// src/session-selection.ts
|
|
12121
12470
|
async function fileSha256(path, signal) {
|
|
12122
12471
|
signal?.throwIfAborted();
|
|
12123
|
-
const hash2 =
|
|
12472
|
+
const hash2 = createHash8("sha256");
|
|
12124
12473
|
try {
|
|
12125
12474
|
for await (const chunk of createReadStream2(path, { signal })) {
|
|
12126
12475
|
signal?.throwIfAborted();
|
|
@@ -12148,7 +12497,7 @@ async function sourceSnapshot(adapter, ref, signal) {
|
|
|
12148
12497
|
signal?.throwIfAborted();
|
|
12149
12498
|
files.push({ path, sha256: await fileSha256(path, signal) });
|
|
12150
12499
|
}
|
|
12151
|
-
const sha2562 = files.length === 1 ? files[0].sha256 :
|
|
12500
|
+
const sha2562 = files.length === 1 ? files[0].sha256 : createHash8("sha256").update(JSON.stringify(files)).digest("hex");
|
|
12152
12501
|
return { sha256: sha2562, files };
|
|
12153
12502
|
}
|
|
12154
12503
|
function summarizeWorkflows(workflows) {
|
|
@@ -12274,8 +12623,8 @@ var KNOWN_LIMITS = [
|
|
|
12274
12623
|
"progress.md is copied whole; no session-dated section extraction is performed",
|
|
12275
12624
|
"open pull-request state is not captured: the bundler makes no network calls"
|
|
12276
12625
|
];
|
|
12277
|
-
function sha256Hex(
|
|
12278
|
-
return
|
|
12626
|
+
function sha256Hex(bytes2) {
|
|
12627
|
+
return createHash9("sha256").update(bytes2).digest("hex");
|
|
12279
12628
|
}
|
|
12280
12629
|
function bundlePath(...parts) {
|
|
12281
12630
|
return parts.join("/");
|
|
@@ -12682,13 +13031,13 @@ async function verifySessionBundle(bundleDir) {
|
|
|
12682
13031
|
let source;
|
|
12683
13032
|
if (manifest.projection) {
|
|
12684
13033
|
const sourceManifest = join19(manifest.projection.sourceDirectory, "manifest.json");
|
|
12685
|
-
const
|
|
13034
|
+
const bytes2 = await readFile7(sourceManifest).catch((error) => {
|
|
12686
13035
|
if (isMissingPathError(error)) return null;
|
|
12687
13036
|
throw error;
|
|
12688
13037
|
});
|
|
12689
13038
|
source = {
|
|
12690
13039
|
directory: manifest.projection.sourceDirectory,
|
|
12691
|
-
status:
|
|
13040
|
+
status: bytes2 === null ? "unavailable" : sha256Hex(bytes2) === manifest.projection.sourceManifestSha256 ? "match" : "mismatch"
|
|
12692
13041
|
};
|
|
12693
13042
|
}
|
|
12694
13043
|
return {
|
|
@@ -12732,8 +13081,8 @@ async function openBundleFile(root, file) {
|
|
|
12732
13081
|
return handle;
|
|
12733
13082
|
}
|
|
12734
13083
|
async function fileDigest(handle, signal) {
|
|
12735
|
-
const hash2 =
|
|
12736
|
-
for await (const
|
|
13084
|
+
const hash2 = createHash10("sha256");
|
|
13085
|
+
for await (const bytes2 of handle.createReadStream({ start: 0, autoClose: false, signal })) hash2.update(bytes2);
|
|
12737
13086
|
return hash2.digest("hex");
|
|
12738
13087
|
}
|
|
12739
13088
|
async function validateFile(root, file, signal) {
|
|
@@ -12833,7 +13182,7 @@ async function createBundleSourceReader(directory, spans, options = {}) {
|
|
|
12833
13182
|
handle = await openBundleFile(root, file);
|
|
12834
13183
|
const before = await handle.stat();
|
|
12835
13184
|
if (await fileDigest(handle, signal) !== file.sha256) return unavailable("retained source digest mismatch");
|
|
12836
|
-
const hash2 =
|
|
13185
|
+
const hash2 = createHash10("sha256");
|
|
12837
13186
|
const record2 = Buffer.alloc(ref.recordBytes);
|
|
12838
13187
|
let position = 0;
|
|
12839
13188
|
for await (const raw of handle.createReadStream({
|
|
@@ -12842,10 +13191,10 @@ async function createBundleSourceReader(directory, spans, options = {}) {
|
|
|
12842
13191
|
autoClose: false,
|
|
12843
13192
|
signal
|
|
12844
13193
|
})) {
|
|
12845
|
-
const
|
|
12846
|
-
hash2.update(
|
|
12847
|
-
|
|
12848
|
-
position +=
|
|
13194
|
+
const bytes3 = Buffer.isBuffer(raw) ? raw : Buffer.from(raw);
|
|
13195
|
+
hash2.update(bytes3);
|
|
13196
|
+
bytes3.copy(record2, position);
|
|
13197
|
+
position += bytes3.length;
|
|
12849
13198
|
}
|
|
12850
13199
|
const after = await handle.stat();
|
|
12851
13200
|
if (position !== ref.recordBytes || hash2.digest("hex") !== ref.recordSha256 || before.size !== after.size || before.mtimeMs !== after.mtimeMs || before.ctimeMs !== after.ctimeMs) {
|
|
@@ -12868,22 +13217,22 @@ async function createBundleSourceReader(directory, spans, options = {}) {
|
|
|
12868
13217
|
if (input.offset > field.length) return unavailable("source offset is outside the selected field");
|
|
12869
13218
|
const window = field.subarray(input.offset, Math.min(field.length, input.offset + input.limit));
|
|
12870
13219
|
if (window.length > 0 && (window[0] & 192) === 128) return unavailable("offset splits a UTF-8 character");
|
|
12871
|
-
let
|
|
13220
|
+
let bytes2 = window;
|
|
12872
13221
|
for (let omitted = 0; omitted <= 3; omitted += 1) {
|
|
12873
|
-
|
|
12874
|
-
if (isUtf82(
|
|
13222
|
+
bytes2 = window.subarray(0, Math.max(0, window.length - omitted));
|
|
13223
|
+
if (isUtf82(bytes2)) break;
|
|
12875
13224
|
}
|
|
12876
|
-
if (!isUtf82(
|
|
13225
|
+
if (!isUtf82(bytes2) || bytes2.length === 0 && input.offset < field.length) {
|
|
12877
13226
|
return unavailable("window cannot contain a complete UTF-8 character");
|
|
12878
13227
|
}
|
|
12879
|
-
const next = input.offset +
|
|
13228
|
+
const next = input.offset + bytes2.length;
|
|
12880
13229
|
return {
|
|
12881
13230
|
status: "available",
|
|
12882
13231
|
trace_id: input.trace_id,
|
|
12883
13232
|
span_id: input.span_id,
|
|
12884
13233
|
attribute: input.attribute,
|
|
12885
13234
|
source_index: sourceIndex,
|
|
12886
|
-
text:
|
|
13235
|
+
text: bytes2.toString("utf8"),
|
|
12887
13236
|
offset: input.offset,
|
|
12888
13237
|
total_bytes: field.length,
|
|
12889
13238
|
next_offset: next < field.length ? next : null,
|
|
@@ -13016,7 +13365,7 @@ async function analyzeSpans(spans, opts = {}) {
|
|
|
13016
13365
|
}
|
|
13017
13366
|
|
|
13018
13367
|
// src/chat-trajectory.ts
|
|
13019
|
-
import { createHash as
|
|
13368
|
+
import { createHash as createHash11 } from "crypto";
|
|
13020
13369
|
function isChatTrajectoryMessage(value) {
|
|
13021
13370
|
return isObject2(value) && typeof value.role === "string" && typeof value.content === "string";
|
|
13022
13371
|
}
|
|
@@ -13101,7 +13450,7 @@ function trajectoryId(trajectory) {
|
|
|
13101
13450
|
const value = trajectory.trajectory_id ?? trajectory.traj_id ?? trajectory.instance_id;
|
|
13102
13451
|
if (typeof value === "string" && value.trim()) return value;
|
|
13103
13452
|
if (typeof value === "number" && Number.isSafeInteger(value)) return String(value);
|
|
13104
|
-
return `trajectory-${
|
|
13453
|
+
return `trajectory-${createHash11("sha256").update(JSON.stringify(trajectory.messages)).digest("hex").slice(0, 24)}`;
|
|
13105
13454
|
}
|
|
13106
13455
|
function messageKind(role) {
|
|
13107
13456
|
const normalized = role.trim().toLowerCase();
|
|
@@ -13158,8 +13507,8 @@ function parseTime2(value) {
|
|
|
13158
13507
|
}
|
|
13159
13508
|
function finiteNumber(value, key) {
|
|
13160
13509
|
if (!isObject2(value)) return void 0;
|
|
13161
|
-
const
|
|
13162
|
-
return typeof
|
|
13510
|
+
const number2 = value[key];
|
|
13511
|
+
return typeof number2 === "number" && Number.isFinite(number2) ? number2 : void 0;
|
|
13163
13512
|
}
|
|
13164
13513
|
function stringValue2(value) {
|
|
13165
13514
|
return typeof value === "string" && value.trim() ? value : void 0;
|
|
@@ -13178,10 +13527,10 @@ import {
|
|
|
13178
13527
|
readFile as readFile10,
|
|
13179
13528
|
rm as rm2
|
|
13180
13529
|
} from "fs/promises";
|
|
13181
|
-
import { basename as
|
|
13530
|
+
import { basename as basename13, dirname as dirname10, join as join23, resolve as resolve9 } from "path";
|
|
13182
13531
|
|
|
13183
13532
|
// src/codetracebench-files.ts
|
|
13184
|
-
import { createHash as
|
|
13533
|
+
import { createHash as createHash12, randomUUID } from "crypto";
|
|
13185
13534
|
import {
|
|
13186
13535
|
link,
|
|
13187
13536
|
lstat as lstat3,
|
|
@@ -13196,13 +13545,13 @@ import {
|
|
|
13196
13545
|
writeFile as writeFile6
|
|
13197
13546
|
} from "fs/promises";
|
|
13198
13547
|
import { hostname } from "os";
|
|
13199
|
-
import { basename as
|
|
13548
|
+
import { basename as basename12, dirname as dirname9, isAbsolute as isAbsolute5, join as join21, relative as relative6, resolve as resolve8, sep as sep3 } from "path";
|
|
13200
13549
|
async function claimImportLock(input) {
|
|
13201
13550
|
const claimId = randomUUID();
|
|
13202
13551
|
const path = `${input.outputDirectory}.lock`;
|
|
13203
13552
|
const temporary = join21(
|
|
13204
13553
|
dirname9(path),
|
|
13205
|
-
`.${
|
|
13554
|
+
`.${basename12(path)}.${claimId}.tmp`
|
|
13206
13555
|
);
|
|
13207
13556
|
const metadata = {
|
|
13208
13557
|
kind: "traces.codetracebench-import-lock",
|
|
@@ -13364,7 +13713,7 @@ async function atomicWriteFile(path, content, signal) {
|
|
|
13364
13713
|
signal.throwIfAborted();
|
|
13365
13714
|
const temporary = join21(
|
|
13366
13715
|
dirname9(path),
|
|
13367
|
-
`.${
|
|
13716
|
+
`.${basename12(path)}.${randomUUID()}.tmp`
|
|
13368
13717
|
);
|
|
13369
13718
|
try {
|
|
13370
13719
|
await writeFile6(temporary, content, {
|
|
@@ -13379,7 +13728,7 @@ async function atomicWriteFile(path, content, signal) {
|
|
|
13379
13728
|
}
|
|
13380
13729
|
}
|
|
13381
13730
|
function sha256(value) {
|
|
13382
|
-
return
|
|
13731
|
+
return createHash12("sha256").update(value).digest("hex");
|
|
13383
13732
|
}
|
|
13384
13733
|
function isObject3(value) {
|
|
13385
13734
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
@@ -13616,10 +13965,10 @@ async function readOptionalTask(trajectoryDirectory, caseDirectory, row, signal)
|
|
|
13616
13965
|
content: `Task: ${row.taskName}`
|
|
13617
13966
|
};
|
|
13618
13967
|
}
|
|
13619
|
-
const
|
|
13620
|
-
const content =
|
|
13968
|
+
const bytes2 = await readFile9(path, { signal });
|
|
13969
|
+
const content = bytes2.toString("utf8").trim();
|
|
13621
13970
|
if (!content) throw new Error(`${row.trajId}/task.md is empty`);
|
|
13622
|
-
return { source: "task.md", content, path, bytes };
|
|
13971
|
+
return { source: "task.md", content, path, bytes: bytes2 };
|
|
13623
13972
|
}
|
|
13624
13973
|
function assertNoLabelLeak(spans, row) {
|
|
13625
13974
|
for (const item of spans) {
|
|
@@ -13697,7 +14046,7 @@ async function importCodeTraceBench(options) {
|
|
|
13697
14046
|
const rowsPath = resolve9(options.rowsPath);
|
|
13698
14047
|
const trajectoryPath = resolve9(options.trajectoryDir);
|
|
13699
14048
|
const outputDirectory = resolve9(options.outDir);
|
|
13700
|
-
const outputBase =
|
|
14049
|
+
const outputBase = basename13(outputDirectory);
|
|
13701
14050
|
if (!outputBase || outputDirectory === dirname10(outputDirectory)) {
|
|
13702
14051
|
throw new Error("CodeTraceBench output directory must not be a filesystem root");
|
|
13703
14052
|
}
|
|
@@ -13801,10 +14150,10 @@ async function runCodeTraceBenchImport(input) {
|
|
|
13801
14150
|
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
13802
14151
|
input: {
|
|
13803
14152
|
revision,
|
|
13804
|
-
rowsFile:
|
|
14153
|
+
rowsFile: basename13(rowsPath),
|
|
13805
14154
|
rowsSha256,
|
|
13806
14155
|
rowsBytes: rowsBytes.byteLength,
|
|
13807
|
-
trajectoryDirectory:
|
|
14156
|
+
trajectoryDirectory: basename13(trajectoryDirectory),
|
|
13808
14157
|
sha256: inputSha256
|
|
13809
14158
|
},
|
|
13810
14159
|
settings: {
|
|
@@ -14269,7 +14618,7 @@ async function cmdVerifyFindings(argv) {
|
|
|
14269
14618
|
}
|
|
14270
14619
|
|
|
14271
14620
|
// src/hodoscope.ts
|
|
14272
|
-
import { createHash as
|
|
14621
|
+
import { createHash as createHash13 } from "crypto";
|
|
14273
14622
|
import { mkdir as mkdir5, mkdtemp as mkdtemp5, readFile as readFile11, readdir as readdir17, writeFile as writeFile7 } from "fs/promises";
|
|
14274
14623
|
import { endianness, tmpdir as tmpdir5 } from "os";
|
|
14275
14624
|
import { join as join24, resolve as resolve10 } from "path";
|
|
@@ -14308,7 +14657,7 @@ async function writeHodoscopeInput(otlpPath, options = {}) {
|
|
|
14308
14657
|
throw new Error(`Hodoscope trace '${traceId}' contains no assistant actions`);
|
|
14309
14658
|
}
|
|
14310
14659
|
actionCount += actions;
|
|
14311
|
-
const file = `${
|
|
14660
|
+
const file = `${createHash13("sha256").update(traceId).digest("hex").slice(0, 24)}.json`;
|
|
14312
14661
|
await writeFile7(join24(samples, file), `${JSON.stringify(trajectory, null, 2)}
|
|
14313
14662
|
`, "utf8");
|
|
14314
14663
|
}
|
|
@@ -14820,7 +15169,7 @@ function validateEmbedding(encoded, dimensions, path) {
|
|
|
14820
15169
|
`Hodoscope ${path} encodes ${encoded.length} characters; expected ${expectedLength} for ${dimensions} float32 values`
|
|
14821
15170
|
);
|
|
14822
15171
|
}
|
|
14823
|
-
const
|
|
15172
|
+
const bytes2 = new Uint8Array(dimensions * 4);
|
|
14824
15173
|
for (let group = 0; group < dimensions; group += 1) {
|
|
14825
15174
|
let value = 0;
|
|
14826
15175
|
const encodedOffset = group * 5;
|
|
@@ -14836,12 +15185,12 @@ function validateEmbedding(encoded, dimensions, path) {
|
|
|
14836
15185
|
throw new Error(`Hodoscope ${path} contains an overflowing base85 block at index ${group}`);
|
|
14837
15186
|
}
|
|
14838
15187
|
const byteOffset = group * 4;
|
|
14839
|
-
|
|
14840
|
-
|
|
14841
|
-
|
|
14842
|
-
|
|
15188
|
+
bytes2[byteOffset] = Math.floor(value / 16777216) & 255;
|
|
15189
|
+
bytes2[byteOffset + 1] = Math.floor(value / 65536) & 255;
|
|
15190
|
+
bytes2[byteOffset + 2] = Math.floor(value / 256) & 255;
|
|
15191
|
+
bytes2[byteOffset + 3] = value & 255;
|
|
14843
15192
|
}
|
|
14844
|
-
const view = new DataView(
|
|
15193
|
+
const view = new DataView(bytes2.buffer, bytes2.byteOffset, bytes2.byteLength);
|
|
14845
15194
|
const littleEndian = endianness() === "LE";
|
|
14846
15195
|
for (let index = 0; index < dimensions; index += 1) {
|
|
14847
15196
|
if (!Number.isFinite(view.getFloat32(index * 4, littleEndian))) {
|
|
@@ -14956,7 +15305,7 @@ function assertPositiveInteger(name, value) {
|
|
|
14956
15305
|
}
|
|
14957
15306
|
|
|
14958
15307
|
// src/analyst-engine-prime.ts
|
|
14959
|
-
import { createHash as
|
|
15308
|
+
import { createHash as createHash14 } from "crypto";
|
|
14960
15309
|
import { request as httpRequest } from "http";
|
|
14961
15310
|
import { request as httpsRequest } from "https";
|
|
14962
15311
|
var DEFAULT_BRIDGE_URL = "http://localhost:4181";
|
|
@@ -14969,6 +15318,17 @@ var MAX_FINDINGS = 10;
|
|
|
14969
15318
|
var MAX_SPAN_IDS_PER_FINDING = 8;
|
|
14970
15319
|
var SEVERITIES = /* @__PURE__ */ new Set(["critical", "high", "medium", "low", "info"]);
|
|
14971
15320
|
var ANALYST_ID = "prime";
|
|
15321
|
+
var TRAJECTORY_FILE_NAME = "trajectory.otlp.jsonl";
|
|
15322
|
+
async function fileDelivery(otlpPath, perAttributeCharCap, signal) {
|
|
15323
|
+
signal?.throwIfAborted();
|
|
15324
|
+
const { copyFile: copyFile2, mkdtemp: mkdtemp8, stat: stat19 } = await import("fs/promises");
|
|
15325
|
+
const { tmpdir: tmpdir8 } = await import("os");
|
|
15326
|
+
const { join: join35 } = await import("path");
|
|
15327
|
+
const cwd = await mkdtemp8(join35(tmpdir8(), "traces-prime-"));
|
|
15328
|
+
await copyFile2(otlpPath, join35(cwd, TRAJECTORY_FILE_NAME));
|
|
15329
|
+
const artifactBytes = (await stat19(otlpPath)).size;
|
|
15330
|
+
return { mode: "file-cwd", perAttributeCharCap, renderedChars: artifactBytes, cwd, fileName: TRAJECTORY_FILE_NAME };
|
|
15331
|
+
}
|
|
14972
15332
|
function positiveIntegerEnv(name) {
|
|
14973
15333
|
const raw = process.env[name];
|
|
14974
15334
|
if (raw === void 0 || raw === "") return void 0;
|
|
@@ -15019,7 +15379,7 @@ function capRowAttributes(row, cap) {
|
|
|
15019
15379
|
}
|
|
15020
15380
|
return { ...row, attributes: capped };
|
|
15021
15381
|
}
|
|
15022
|
-
async function projectArtifact(otlpPath, maxInlineChars, perAttributeCharCap, signal) {
|
|
15382
|
+
async function projectArtifact(otlpPath, maxInlineChars, perAttributeCharCap, deliveryMode, signal) {
|
|
15023
15383
|
const rows = [];
|
|
15024
15384
|
const spanTraces = /* @__PURE__ */ new Map();
|
|
15025
15385
|
let index = 0;
|
|
@@ -15043,9 +15403,18 @@ async function projectArtifact(otlpPath, maxInlineChars, perAttributeCharCap, si
|
|
|
15043
15403
|
}
|
|
15044
15404
|
if (rows.length === 0) throw new Error(`${otlpPath} contains no spans`);
|
|
15045
15405
|
const traceCount = new Set(rows.map((row) => row.trace_id)).size;
|
|
15406
|
+
if (deliveryMode === "file") {
|
|
15407
|
+
return {
|
|
15408
|
+
rows,
|
|
15409
|
+
rendered: null,
|
|
15410
|
+
delivery: await fileDelivery(otlpPath, perAttributeCharCap, signal),
|
|
15411
|
+
traceCount,
|
|
15412
|
+
spanTraces
|
|
15413
|
+
};
|
|
15414
|
+
}
|
|
15046
15415
|
let projected = rows;
|
|
15047
15416
|
let rendered = JSON.stringify(projected);
|
|
15048
|
-
|
|
15417
|
+
let delivery = {
|
|
15049
15418
|
mode: "inline-json",
|
|
15050
15419
|
perAttributeCharCap: null,
|
|
15051
15420
|
renderedChars: rendered.length
|
|
@@ -15053,18 +15422,30 @@ async function projectArtifact(otlpPath, maxInlineChars, perAttributeCharCap, si
|
|
|
15053
15422
|
if (rendered.length > maxInlineChars) {
|
|
15054
15423
|
projected = rows.map((row) => capRowAttributes(row, perAttributeCharCap));
|
|
15055
15424
|
rendered = JSON.stringify(projected);
|
|
15056
|
-
delivery
|
|
15057
|
-
|
|
15425
|
+
delivery = {
|
|
15426
|
+
mode: "inline-json",
|
|
15427
|
+
perAttributeCharCap,
|
|
15428
|
+
renderedChars: rendered.length
|
|
15429
|
+
};
|
|
15058
15430
|
}
|
|
15059
15431
|
if (rendered.length > maxInlineChars) {
|
|
15060
|
-
|
|
15061
|
-
|
|
15062
|
-
|
|
15432
|
+
if (deliveryMode === "inline") {
|
|
15433
|
+
throw new Error(
|
|
15434
|
+
`trajectory renders to ${rendered.length} chars even at per-attribute cap ${perAttributeCharCap} (budget ${maxInlineChars}); inline delivery impossible`
|
|
15435
|
+
);
|
|
15436
|
+
}
|
|
15437
|
+
return {
|
|
15438
|
+
rows,
|
|
15439
|
+
rendered: null,
|
|
15440
|
+
delivery: await fileDelivery(otlpPath, perAttributeCharCap, signal),
|
|
15441
|
+
traceCount,
|
|
15442
|
+
spanTraces
|
|
15443
|
+
};
|
|
15063
15444
|
}
|
|
15064
15445
|
return { rows: projected, rendered, delivery, traceCount, spanTraces };
|
|
15065
15446
|
}
|
|
15066
15447
|
var OUTPUT_CONTRACT_LINES = [
|
|
15067
|
-
"OUTPUT CONTRACT (
|
|
15448
|
+
"OUTPUT CONTRACT (inline delivery \u2014 the whole trajectory is in this prompt):",
|
|
15068
15449
|
"You are a one-shot analyst. Every fact you need is in the TRAJECTORY JSON below.",
|
|
15069
15450
|
"Do not run shell commands, do not read or write files, do not use any tools.",
|
|
15070
15451
|
"Reply with EXACTLY one fenced ```json code block and no other fenced block. The JSON object has exactly two fields:",
|
|
@@ -15080,7 +15461,35 @@ var OUTPUT_CONTRACT_LINES = [
|
|
|
15080
15461
|
`Report at most ${MAX_FINDINGS} findings; a finding cites 1..${MAX_SPAN_IDS_PER_FINDING} span_ids, each copied VERBATIM from a span in the trajectory below.`,
|
|
15081
15462
|
'"findings" is [] only for a clean trajectory.'
|
|
15082
15463
|
];
|
|
15464
|
+
var FILE_DELIVERY_CONTRACT_LINES = [
|
|
15465
|
+
"OUTPUT CONTRACT (file delivery \u2014 the trajectory is a FILE in your working directory):",
|
|
15466
|
+
`You are a recursive language model with a REPL. The trajectory is ${TRAJECTORY_FILE_NAME} in your`,
|
|
15467
|
+
"working directory: OpenInference JSONL, one span object per line. Load it yourself and inspect it with code",
|
|
15468
|
+
'(e.g. rows = [json.loads(line) for line in open("trajectory.otlp.jsonl")]), paging rather than printing it whole;',
|
|
15469
|
+
"fan out with rlm() for sub-questions when that pays for itself. Stay inside your working directory.",
|
|
15470
|
+
"Reply with EXACTLY one fenced ```json code block and no other fenced block. The JSON object has exactly two fields:",
|
|
15471
|
+
' "answer": string \u2014 ONE short sentence (max 300 chars) summarizing your verdict.',
|
|
15472
|
+
' "findings": array (possibly empty) of finding rows, each exactly:',
|
|
15473
|
+
' {"span_ids": [string, ...],',
|
|
15474
|
+
' "severity": "critical"|"high"|"medium"|"low"|"info",',
|
|
15475
|
+
' "area": string (short kebab-case topic, max 40 chars),',
|
|
15476
|
+
' "claim": string (ONE short sentence, max 200 chars),',
|
|
15477
|
+
' "action": string (optional, ONE short imperative sentence, max 200 chars),',
|
|
15478
|
+
' "confidence": number 0..1}',
|
|
15479
|
+
"Do NOT include a rationale field. Keep every string SHORT \u2014 long strings get corrupted in transport and void your work.",
|
|
15480
|
+
`Report at most ${MAX_FINDINGS} findings; a finding cites 1..${MAX_SPAN_IDS_PER_FINDING} span_ids, each copied VERBATIM from a span in ${TRAJECTORY_FILE_NAME}.`,
|
|
15481
|
+
'"findings" is [] only for a clean trajectory.'
|
|
15482
|
+
];
|
|
15083
15483
|
function buildPrompt(question, projection) {
|
|
15484
|
+
if (projection.delivery.mode === "file-cwd") {
|
|
15485
|
+
return [
|
|
15486
|
+
`QUESTION: ${question}`,
|
|
15487
|
+
"",
|
|
15488
|
+
...FILE_DELIVERY_CONTRACT_LINES,
|
|
15489
|
+
"",
|
|
15490
|
+
`TRAJECTORY (${projection.traceCount} trace(s); ${projection.rows.length} spans): the file ${projection.delivery.fileName} in your working directory (${projection.delivery.renderedChars} bytes). It is NOT inlined in this prompt.`
|
|
15491
|
+
].join("\n");
|
|
15492
|
+
}
|
|
15084
15493
|
return [
|
|
15085
15494
|
`QUESTION: ${question}`,
|
|
15086
15495
|
"",
|
|
@@ -15180,7 +15589,7 @@ function toAnalystFinding(row, index, spanTraces, model, producedAt) {
|
|
|
15180
15589
|
const traces = spanTraces.get(spanId);
|
|
15181
15590
|
return spanEvidenceUri([...traces][0], spanId);
|
|
15182
15591
|
});
|
|
15183
|
-
const findingId = `prime-${
|
|
15592
|
+
const findingId = `prime-${createHash14("sha256").update(`${index}
|
|
15184
15593
|
${row.claim}
|
|
15185
15594
|
${uris.join("\n")}`).digest("hex").slice(0, 12)}`;
|
|
15186
15595
|
return {
|
|
@@ -15227,7 +15636,7 @@ var httpJsonTransport = ({ url, body, signal }) => {
|
|
|
15227
15636
|
};
|
|
15228
15637
|
var PrimeBridgeError = class extends Error {
|
|
15229
15638
|
};
|
|
15230
|
-
async function callBridge(transport, url, model, content, timeoutMs, callerSignal, turn) {
|
|
15639
|
+
async function callBridge(transport, url, model, content, timeoutMs, callerSignal, turn, cwd) {
|
|
15231
15640
|
const controller = new AbortController();
|
|
15232
15641
|
const timer = setTimeout(
|
|
15233
15642
|
() => controller.abort(new Error(`${turn}bridge call exceeded ${timeoutMs}ms`)),
|
|
@@ -15240,7 +15649,7 @@ async function callBridge(transport, url, model, content, timeoutMs, callerSigna
|
|
|
15240
15649
|
try {
|
|
15241
15650
|
response = await transport({
|
|
15242
15651
|
url,
|
|
15243
|
-
body: { model, messages: [{ role: "user", content }] },
|
|
15652
|
+
body: { model, messages: [{ role: "user", content }], ...cwd ? { cwd } : {} },
|
|
15244
15653
|
signal: controller.signal
|
|
15245
15654
|
});
|
|
15246
15655
|
} catch (error) {
|
|
@@ -15287,6 +15696,10 @@ function primeAnalyzer(opts = {}) {
|
|
|
15287
15696
|
throw new RangeError("perAttributeCharCap must be a positive safe integer");
|
|
15288
15697
|
}
|
|
15289
15698
|
const repairEnabled = opts.repair ?? true;
|
|
15699
|
+
const deliveryMode = opts.delivery ?? "auto";
|
|
15700
|
+
if (deliveryMode !== "auto" && deliveryMode !== "file" && deliveryMode !== "inline") {
|
|
15701
|
+
throw new RangeError(`delivery must be 'auto', 'file', or 'inline', got '${String(deliveryMode)}'`);
|
|
15702
|
+
}
|
|
15290
15703
|
const transport = opts.transport ?? httpJsonTransport;
|
|
15291
15704
|
const url = `${bridgeUrl}/v1/chat/completions`;
|
|
15292
15705
|
return {
|
|
@@ -15294,12 +15707,19 @@ function primeAnalyzer(opts = {}) {
|
|
|
15294
15707
|
async analyze(otlpPath, analyzeOpts = {}) {
|
|
15295
15708
|
let projection;
|
|
15296
15709
|
try {
|
|
15297
|
-
projection = await projectArtifact(
|
|
15710
|
+
projection = await projectArtifact(
|
|
15711
|
+
otlpPath,
|
|
15712
|
+
maxInlineChars,
|
|
15713
|
+
perAttributeCharCap,
|
|
15714
|
+
deliveryMode,
|
|
15715
|
+
analyzeOpts.signal
|
|
15716
|
+
);
|
|
15298
15717
|
} catch (error) {
|
|
15299
15718
|
return failure("", error instanceof Error ? error.message : String(error));
|
|
15300
15719
|
}
|
|
15301
15720
|
const question = analyzeOpts.prompt ?? opts.defaultPrompt ?? DEFAULT_QUESTION;
|
|
15302
|
-
const deliveryLine = `delivery: ${projection.delivery.mode} (${projection.delivery.renderedChars} chars, per-attribute cap ${projection.delivery.perAttributeCharCap ?? "none"})`;
|
|
15721
|
+
const deliveryLine = projection.delivery.mode === "file-cwd" ? `delivery: file-cwd (${projection.rows.length} spans; ${projection.delivery.fileName} in cwd ${projection.delivery.cwd}, ${projection.delivery.renderedChars} bytes; per-attribute cap not applied)` : `delivery: ${projection.delivery.mode} (${projection.delivery.renderedChars} chars, per-attribute cap ${projection.delivery.perAttributeCharCap ?? "none"})`;
|
|
15722
|
+
const deliveryCwd = projection.delivery.mode === "file-cwd" ? projection.delivery.cwd : void 0;
|
|
15303
15723
|
let reply;
|
|
15304
15724
|
try {
|
|
15305
15725
|
reply = await callBridge(
|
|
@@ -15309,7 +15729,8 @@ function primeAnalyzer(opts = {}) {
|
|
|
15309
15729
|
buildPrompt(question, projection),
|
|
15310
15730
|
timeoutMs,
|
|
15311
15731
|
analyzeOpts.signal,
|
|
15312
|
-
""
|
|
15732
|
+
"",
|
|
15733
|
+
deliveryCwd
|
|
15313
15734
|
);
|
|
15314
15735
|
} catch (error) {
|
|
15315
15736
|
if (!(error instanceof PrimeBridgeError)) throw error;
|
|
@@ -15466,7 +15887,7 @@ async function applyRedactor(spans, redactor) {
|
|
|
15466
15887
|
}
|
|
15467
15888
|
|
|
15468
15889
|
// src/file-export.ts
|
|
15469
|
-
import { createHash as
|
|
15890
|
+
import { createHash as createHash15 } from "crypto";
|
|
15470
15891
|
import { mkdtemp as mkdtemp6, readFile as readFile12, writeFile as writeFile8 } from "fs/promises";
|
|
15471
15892
|
import { tmpdir as tmpdir6 } from "os";
|
|
15472
15893
|
import { join as join25 } from "path";
|
|
@@ -15504,7 +15925,7 @@ function stableJson(value) {
|
|
|
15504
15925
|
});
|
|
15505
15926
|
}
|
|
15506
15927
|
function hashId(value, chars = 16) {
|
|
15507
|
-
return
|
|
15928
|
+
return createHash15("sha256").update(stableJson(value)).digest("hex").slice(0, chars);
|
|
15508
15929
|
}
|
|
15509
15930
|
function isoTime2(value) {
|
|
15510
15931
|
if (typeof value === "string") {
|
|
@@ -16148,7 +16569,7 @@ async function writeTraceEvidenceExportFile(inputPath, outPath, opts = {}) {
|
|
|
16148
16569
|
// src/inspect.ts
|
|
16149
16570
|
import { mkdtemp as mkdtemp7, readFile as readFile13, writeFile as writeFile9 } from "fs/promises";
|
|
16150
16571
|
import { tmpdir as tmpdir7 } from "os";
|
|
16151
|
-
import { basename as
|
|
16572
|
+
import { basename as basename14, join as join26 } from "path";
|
|
16152
16573
|
var severityRank2 = {
|
|
16153
16574
|
high: 0,
|
|
16154
16575
|
medium: 1,
|
|
@@ -16331,7 +16752,7 @@ function addContextFindings(index, findings) {
|
|
|
16331
16752
|
refs: invalidJsonlFiles.slice(0, 5).map((file) => file.path)
|
|
16332
16753
|
});
|
|
16333
16754
|
}
|
|
16334
|
-
const skillRunFiles = files.filter((file) =>
|
|
16755
|
+
const skillRunFiles = files.filter((file) => basename14(file.path) === "skill-runs.jsonl" && (file.jsonl?.rows ?? 0) > 0).map((file) => {
|
|
16335
16756
|
const rows = file.jsonl?.rows ?? 0;
|
|
16336
16757
|
const keys = file.jsonl?.keys ?? {};
|
|
16337
16758
|
const linkKeyCounts = {
|
|
@@ -16443,7 +16864,7 @@ async function writeInspectionReportFile(report, outPath) {
|
|
|
16443
16864
|
}
|
|
16444
16865
|
|
|
16445
16866
|
// src/live.ts
|
|
16446
|
-
import { createHash as
|
|
16867
|
+
import { createHash as createHash16 } from "crypto";
|
|
16447
16868
|
var VERIFY_RE = /\b(test|tests|vitest|jest|pytest|go test|cargo test|tsc|typecheck|lint|biome|eslint|build|check:invariants|preflight)\b/i;
|
|
16448
16869
|
var CHANGE_RE = /\b(apply_patch|edit|write|create|delete|rename|replace|update|git apply|sed -i|perl -pi|tee\s|cat\s+>)/i;
|
|
16449
16870
|
var CLAIM_DONE_RE = /\b(done|fixed|complete|completed|implemented|merged|verified|all green|tests pass|ready|works now)\b/i;
|
|
@@ -16463,7 +16884,7 @@ var sleep = (ms2, signal) => new Promise((resolve13) => {
|
|
|
16463
16884
|
signal?.addEventListener("abort", onAbort, { once: true });
|
|
16464
16885
|
});
|
|
16465
16886
|
function hash(parts) {
|
|
16466
|
-
return
|
|
16887
|
+
return createHash16("sha256").update(parts.join("\0")).digest("hex").slice(0, 16);
|
|
16467
16888
|
}
|
|
16468
16889
|
function asString(value) {
|
|
16469
16890
|
if (typeof value === "string" && value.trim()) return value;
|
|
@@ -16871,7 +17292,7 @@ async function streamSessions(opts) {
|
|
|
16871
17292
|
|
|
16872
17293
|
// src/supervisor-run-context.ts
|
|
16873
17294
|
import { readFile as readFile14, stat as stat16 } from "fs/promises";
|
|
16874
|
-
import { basename as
|
|
17295
|
+
import { basename as basename15, join as join27 } from "path";
|
|
16875
17296
|
import { materializeTreeView } from "@tangle-network/agent-runtime/kernel";
|
|
16876
17297
|
import {
|
|
16877
17298
|
readRuntimeSupervisorRun
|
|
@@ -17037,7 +17458,7 @@ async function readFileRunContext(runDir, opts = {}) {
|
|
|
17037
17458
|
return {
|
|
17038
17459
|
...sources,
|
|
17039
17460
|
instanceId: opts.instanceId !== void 0 ? opts.instanceId : sources.instanceId,
|
|
17040
|
-
arm: opts.arm !== void 0 ? opts.arm :
|
|
17461
|
+
arm: opts.arm !== void 0 ? opts.arm : basename15(runDir),
|
|
17041
17462
|
workers: sources.workers !== null && coordination.present ? workerSources(sources.workers, coordination) : sources.workers,
|
|
17042
17463
|
...sources.journal !== null ? {
|
|
17043
17464
|
rootTranscriptRef: join27(runDir, SPAWN_JOURNAL_FILE),
|
|
@@ -18689,7 +19110,7 @@ async function watchSessions(opts) {
|
|
|
18689
19110
|
}
|
|
18690
19111
|
|
|
18691
19112
|
// src/bundle-view.ts
|
|
18692
|
-
import { createHash as
|
|
19113
|
+
import { createHash as createHash17 } from "crypto";
|
|
18693
19114
|
import { mkdir as mkdir7, readdir as readdir20, readFile as readFile16, writeFile as writeFile11 } from "fs/promises";
|
|
18694
19115
|
import { dirname as dirname11, join as join31, resolve as resolve12 } from "path";
|
|
18695
19116
|
var EVIDENCE_ONLY_ALLOW_LIST = [
|
|
@@ -18882,8 +19303,8 @@ async function projectSessionBundle(opts) {
|
|
|
18882
19303
|
const candidates = [];
|
|
18883
19304
|
for (const path of await listSessionBundleFiles(sourceDir)) {
|
|
18884
19305
|
if (path === "manifest.json") continue;
|
|
18885
|
-
const
|
|
18886
|
-
const sha2562 =
|
|
19306
|
+
const bytes2 = await readFile16(join31(sourceDir, path));
|
|
19307
|
+
const sha2562 = createHash17("sha256").update(bytes2).digest("hex");
|
|
18887
19308
|
const recorded = sourceHashes.get(path);
|
|
18888
19309
|
if (!recorded) {
|
|
18889
19310
|
throw new Error(
|
|
@@ -18897,10 +19318,10 @@ async function projectSessionBundle(opts) {
|
|
|
18897
19318
|
}
|
|
18898
19319
|
const verdict = classifyEvidenceOnlyPath(path);
|
|
18899
19320
|
if (!verdict.carried) {
|
|
18900
|
-
excluded.push({ path, bytes:
|
|
19321
|
+
excluded.push({ path, bytes: bytes2.length, sha256: sha2562, rule: verdict.rule, reason: verdict.reason });
|
|
18901
19322
|
continue;
|
|
18902
19323
|
}
|
|
18903
|
-
candidates.push({ path, bytes, sha256: sha2562, carries: verdict.carries, owned: verdict.owned });
|
|
19324
|
+
candidates.push({ path, bytes: bytes2, sha256: sha2562, carries: verdict.carries, owned: verdict.owned });
|
|
18904
19325
|
}
|
|
18905
19326
|
const report = await findSessionTextLeaks({
|
|
18906
19327
|
viewDir: sourceDir,
|
|
@@ -18959,7 +19380,7 @@ async function projectSessionBundle(opts) {
|
|
|
18959
19380
|
knownLimits: [...source.knownLimits, ...EVIDENCE_ONLY_LIMITS],
|
|
18960
19381
|
projection: {
|
|
18961
19382
|
sourceDirectory: sourceDir,
|
|
18962
|
-
sourceManifestSha256:
|
|
19383
|
+
sourceManifestSha256: createHash17("sha256").update(sourceManifestBytes).digest("hex"),
|
|
18963
19384
|
leakCheck
|
|
18964
19385
|
}
|
|
18965
19386
|
};
|
|
@@ -19169,7 +19590,7 @@ function alreadyUploaded(state, harness, sessionId, hash2) {
|
|
|
19169
19590
|
import { readFile as readFile18 } from "fs/promises";
|
|
19170
19591
|
import { createRequire as createRequire2 } from "module";
|
|
19171
19592
|
import { hostname as hostname2 } from "os";
|
|
19172
|
-
import { basename as
|
|
19593
|
+
import { basename as basename16, join as join33 } from "path";
|
|
19173
19594
|
import { hostedClientFromEnv } from "@tangle-network/agent-eval/hosted";
|
|
19174
19595
|
import { assessShareSafety as assessShareSafety2, REDACTION_VERSION, redact as redact2, shareAllowed } from "@tangle-network/agent-eval/traces";
|
|
19175
19596
|
var require2 = createRequire2(import.meta.url);
|
|
@@ -19203,7 +19624,7 @@ async function gitBranch(cwd) {
|
|
|
19203
19624
|
async function sessionMeta(item) {
|
|
19204
19625
|
const meta = {
|
|
19205
19626
|
[ATTR.HARNESS]: item.ref.harness,
|
|
19206
|
-
[ATTR.SESSION_FILE]:
|
|
19627
|
+
[ATTR.SESSION_FILE]: basename16(item.ref.path),
|
|
19207
19628
|
[ATTR.HOST]: hostname2(),
|
|
19208
19629
|
[ATTR.UPLOADER]: `tangle-traces@${TRACES_VERSION}`,
|
|
19209
19630
|
[ATTR.REDACTION_VERSION]: REDACTION_VERSION,
|
|
@@ -19369,7 +19790,7 @@ async function executeUpload(plan, opts = {}) {
|
|
|
19369
19790
|
|
|
19370
19791
|
// src/verify-safe.ts
|
|
19371
19792
|
import { lstat as lstat4, readdir as readdir21, readFile as readFile19 } from "fs/promises";
|
|
19372
|
-
import { basename as
|
|
19793
|
+
import { basename as basename17, extname, join as join34, relative as relative7 } from "path";
|
|
19373
19794
|
import {
|
|
19374
19795
|
assessShareSafety as assessShareSafety3,
|
|
19375
19796
|
combineVerdicts as combineVerdicts2
|
|
@@ -19382,15 +19803,15 @@ async function verifySafe(target, options = {}) {
|
|
|
19382
19803
|
const unreadable = [];
|
|
19383
19804
|
const files = await listFiles(target, unreadable);
|
|
19384
19805
|
for (const file of files) {
|
|
19385
|
-
const label2 = relative7(target, file) ||
|
|
19386
|
-
let
|
|
19806
|
+
const label2 = relative7(target, file) || basename17(file);
|
|
19807
|
+
let bytes2;
|
|
19387
19808
|
try {
|
|
19388
|
-
|
|
19809
|
+
bytes2 = await readFile19(file);
|
|
19389
19810
|
} catch (error) {
|
|
19390
19811
|
unreadable.push(`${label2}: ${error instanceof Error ? error.message : String(error)}`);
|
|
19391
19812
|
continue;
|
|
19392
19813
|
}
|
|
19393
|
-
const text2 = decodeUtf8(
|
|
19814
|
+
const text2 = decodeUtf8(bytes2);
|
|
19394
19815
|
if (text2 === void 0) {
|
|
19395
19816
|
unreadable.push(`${label2}: not UTF-8 text`);
|
|
19396
19817
|
continue;
|
|
@@ -19439,10 +19860,10 @@ async function listFiles(target, unreadable) {
|
|
|
19439
19860
|
await walk(target);
|
|
19440
19861
|
return out;
|
|
19441
19862
|
}
|
|
19442
|
-
function decodeUtf8(
|
|
19443
|
-
if (
|
|
19863
|
+
function decodeUtf8(bytes2) {
|
|
19864
|
+
if (bytes2.includes(0)) return void 0;
|
|
19444
19865
|
try {
|
|
19445
|
-
return new TextDecoder("utf-8", { fatal: true }).decode(
|
|
19866
|
+
return new TextDecoder("utf-8", { fatal: true }).decode(bytes2);
|
|
19446
19867
|
} catch {
|
|
19447
19868
|
return void 0;
|
|
19448
19869
|
}
|
|
@@ -19513,6 +19934,7 @@ export {
|
|
|
19513
19934
|
EXPORT_RULE,
|
|
19514
19935
|
toOpenInferenceSpan,
|
|
19515
19936
|
serializeSpans,
|
|
19937
|
+
iterSerializedSpans,
|
|
19516
19938
|
writeOtlpFile,
|
|
19517
19939
|
summarizeOtlpExecution,
|
|
19518
19940
|
summarizeSpanExecution,
|
|
@@ -19574,6 +19996,7 @@ export {
|
|
|
19574
19996
|
serializePolicyEvidence,
|
|
19575
19997
|
writePolicyEvidenceFile,
|
|
19576
19998
|
countSkillRunsJsonl,
|
|
19999
|
+
computeAdoption,
|
|
19577
20000
|
analyzeAdoption,
|
|
19578
20001
|
planTraceAgenticRoute,
|
|
19579
20002
|
traceAgenticKinds,
|
|
@@ -19585,6 +20008,7 @@ export {
|
|
|
19585
20008
|
shellCommands,
|
|
19586
20009
|
commandTextFromInput,
|
|
19587
20010
|
readPullRequests,
|
|
20011
|
+
SESSION_FACTS_MEASUREMENT_VERSION,
|
|
19588
20012
|
SESSION_TOKEN_TOTAL_ATTR,
|
|
19589
20013
|
SPAN_SYNTHESIZED_ATTR,
|
|
19590
20014
|
FACT_TEXT_CAP,
|