@yhong91/vibetime 0.1.43 → 0.1.45
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/bin/vibetime.mjs +361 -236
- package/package.json +1 -1
package/bin/vibetime.mjs
CHANGED
|
@@ -1719,6 +1719,69 @@ import { promisify } from "node:util";
|
|
|
1719
1719
|
// src/lib/activity.ts
|
|
1720
1720
|
import path2 from "node:path";
|
|
1721
1721
|
|
|
1722
|
+
// src/lib/fields.ts
|
|
1723
|
+
function stringField(object, key) {
|
|
1724
|
+
if (!isPlainObject(object)) {
|
|
1725
|
+
return void 0;
|
|
1726
|
+
}
|
|
1727
|
+
const value = object[key];
|
|
1728
|
+
return typeof value === "string" ? value : void 0;
|
|
1729
|
+
}
|
|
1730
|
+
function numberField(object, key) {
|
|
1731
|
+
const value = object[key];
|
|
1732
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
1733
|
+
}
|
|
1734
|
+
function objectField(object, key) {
|
|
1735
|
+
if (!isPlainObject(object) || !isPlainObject(object[key])) {
|
|
1736
|
+
return {};
|
|
1737
|
+
}
|
|
1738
|
+
return object[key];
|
|
1739
|
+
}
|
|
1740
|
+
function arrayField(object, key) {
|
|
1741
|
+
if (!isPlainObject(object) || !Array.isArray(object[key])) {
|
|
1742
|
+
return [];
|
|
1743
|
+
}
|
|
1744
|
+
return object[key];
|
|
1745
|
+
}
|
|
1746
|
+
function isPlainObject(value) {
|
|
1747
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
1748
|
+
}
|
|
1749
|
+
function stringRefs(value) {
|
|
1750
|
+
const refs2 = {};
|
|
1751
|
+
for (const [key, item] of Object.entries(value || {})) {
|
|
1752
|
+
if (typeof item === "string" && item.length > 0) {
|
|
1753
|
+
refs2[key] = item;
|
|
1754
|
+
}
|
|
1755
|
+
}
|
|
1756
|
+
return refs2;
|
|
1757
|
+
}
|
|
1758
|
+
function stringOption(value) {
|
|
1759
|
+
if (Array.isArray(value)) {
|
|
1760
|
+
return value.at(-1);
|
|
1761
|
+
}
|
|
1762
|
+
return typeof value === "string" ? value : void 0;
|
|
1763
|
+
}
|
|
1764
|
+
function valuesOption(value) {
|
|
1765
|
+
if (Array.isArray(value)) {
|
|
1766
|
+
return value;
|
|
1767
|
+
}
|
|
1768
|
+
return typeof value === "string" ? [value] : [];
|
|
1769
|
+
}
|
|
1770
|
+
function numberOption(value) {
|
|
1771
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
1772
|
+
return value;
|
|
1773
|
+
}
|
|
1774
|
+
if (Array.isArray(value)) {
|
|
1775
|
+
return numberOption(value.at(-1));
|
|
1776
|
+
}
|
|
1777
|
+
const text = stringOption(value);
|
|
1778
|
+
if (!text) {
|
|
1779
|
+
return void 0;
|
|
1780
|
+
}
|
|
1781
|
+
const parsed = Number(text);
|
|
1782
|
+
return Number.isFinite(parsed) ? parsed : void 0;
|
|
1783
|
+
}
|
|
1784
|
+
|
|
1722
1785
|
// src/lib/shell.ts
|
|
1723
1786
|
import path from "node:path";
|
|
1724
1787
|
function fileActivitiesFromShellCommand(command, ts, rootCwd, initialCwd) {
|
|
@@ -1845,7 +1908,7 @@ function operationForTool(tool) {
|
|
|
1845
1908
|
if (["write"].includes(normalized)) {
|
|
1846
1909
|
return "write";
|
|
1847
1910
|
}
|
|
1848
|
-
if (["edit", "multiedit", "notebookedit", "apply_patch", "applypatch"].includes(normalized)) {
|
|
1911
|
+
if (["edit", "multiedit", "notebookedit", "apply_patch", "applypatch", "searchreplace"].includes(normalized)) {
|
|
1849
1912
|
return "edit";
|
|
1850
1913
|
}
|
|
1851
1914
|
return "read";
|
|
@@ -1926,78 +1989,71 @@ function countTextLines(text) {
|
|
|
1926
1989
|
}
|
|
1927
1990
|
return text.split(/\r\n|\r|\n/).length;
|
|
1928
1991
|
}
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
// src/lib/fields.ts
|
|
1939
|
-
function stringField(object, key) {
|
|
1940
|
-
if (!isPlainObject(object)) {
|
|
1941
|
-
return void 0;
|
|
1942
|
-
}
|
|
1943
|
-
const value = object[key];
|
|
1944
|
-
return typeof value === "string" ? value : void 0;
|
|
1945
|
-
}
|
|
1946
|
-
function numberField(object, key) {
|
|
1947
|
-
const value = object[key];
|
|
1948
|
-
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
1949
|
-
}
|
|
1950
|
-
function objectField(object, key) {
|
|
1951
|
-
if (!isPlainObject(object) || !isPlainObject(object[key])) {
|
|
1952
|
-
return {};
|
|
1953
|
-
}
|
|
1954
|
-
return object[key];
|
|
1955
|
-
}
|
|
1956
|
-
function arrayField(object, key) {
|
|
1957
|
-
if (!isPlainObject(object) || !Array.isArray(object[key])) {
|
|
1958
|
-
return [];
|
|
1959
|
-
}
|
|
1960
|
-
return object[key];
|
|
1961
|
-
}
|
|
1962
|
-
function isPlainObject(value) {
|
|
1963
|
-
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
1964
|
-
}
|
|
1965
|
-
function stringRefs(value) {
|
|
1966
|
-
const refs2 = {};
|
|
1967
|
-
for (const [key, item] of Object.entries(value || {})) {
|
|
1968
|
-
if (typeof item === "string" && item.length > 0) {
|
|
1969
|
-
refs2[key] = item;
|
|
1992
|
+
function claudeStyleToolFileActivities(tool, input, ts, cwd) {
|
|
1993
|
+
const changes = /* @__PURE__ */ new Map();
|
|
1994
|
+
const operation = operationForTool(tool);
|
|
1995
|
+
const workdir = stringField(input, "cwd") || cwd;
|
|
1996
|
+
for (const key of ["file_path", "path", "notebook_path"]) {
|
|
1997
|
+
const filePath = stringField(input, key);
|
|
1998
|
+
if (!filePath) {
|
|
1999
|
+
continue;
|
|
1970
2000
|
}
|
|
2001
|
+
const metrics = claudeStyleFileMetrics(tool, input);
|
|
2002
|
+
addResolvedPathActivity(changes, filePath, operation, ts, cwd, workdir, metrics);
|
|
1971
2003
|
}
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
2004
|
+
for (const edit of [...arrayField(input, "edits"), ...arrayField(input, "replacements")]) {
|
|
2005
|
+
if (!isPlainObject(edit)) {
|
|
2006
|
+
continue;
|
|
2007
|
+
}
|
|
2008
|
+
const filePath = stringField(input, "file_path") || stringField(input, "path");
|
|
2009
|
+
if (!filePath) {
|
|
2010
|
+
continue;
|
|
2011
|
+
}
|
|
2012
|
+
addResolvedPathActivity(changes, filePath, "edit", ts, cwd, workdir, {
|
|
2013
|
+
linesAdded: countTextLines(stringField(edit, "new_string") || stringField(edit, "new_text")),
|
|
2014
|
+
linesRemoved: countTextLines(stringField(edit, "old_string") || stringField(edit, "original_text"))
|
|
2015
|
+
});
|
|
1977
2016
|
}
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
2017
|
+
const command = stringField(input, "command");
|
|
2018
|
+
if (command) {
|
|
2019
|
+
for (const item of fileActivitiesFromShellCommand(command, ts, cwd, workdir)) {
|
|
2020
|
+
changes.set(item.path, mergeFileActivity(changes.get(item.path), item));
|
|
2021
|
+
}
|
|
1983
2022
|
}
|
|
1984
|
-
return
|
|
2023
|
+
return [...changes.values()];
|
|
1985
2024
|
}
|
|
1986
|
-
function
|
|
1987
|
-
|
|
1988
|
-
|
|
2025
|
+
function claudeStyleFileMetrics(tool, input) {
|
|
2026
|
+
const normalized = tool.toLowerCase();
|
|
2027
|
+
if (normalized === "read") {
|
|
2028
|
+
return { linesRead: numberField(input, "limit") };
|
|
1989
2029
|
}
|
|
1990
|
-
if (
|
|
1991
|
-
|
|
2030
|
+
if (normalized === "write") {
|
|
2031
|
+
const content = stringField(input, "content") || stringField(input, "file_content");
|
|
2032
|
+
return {
|
|
2033
|
+
linesAdded: countTextLines(content),
|
|
2034
|
+
charsWritten: content?.length
|
|
2035
|
+
};
|
|
1992
2036
|
}
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
2037
|
+
if (normalized === "edit" || normalized === "multiedit" || normalized === "searchreplace") {
|
|
2038
|
+
const oldString = stringField(input, "old_string");
|
|
2039
|
+
const newString = stringField(input, "new_string");
|
|
2040
|
+
return {
|
|
2041
|
+
linesAdded: countTextLines(newString),
|
|
2042
|
+
linesRemoved: countTextLines(oldString),
|
|
2043
|
+
charsWritten: newString?.length
|
|
2044
|
+
};
|
|
1996
2045
|
}
|
|
1997
|
-
|
|
1998
|
-
return Number.isFinite(parsed) ? parsed : void 0;
|
|
2046
|
+
return {};
|
|
1999
2047
|
}
|
|
2000
2048
|
|
|
2049
|
+
// src/lib/constants.ts
|
|
2050
|
+
var PACKAGE_VERSION = true ? "0.1.45" : "0.1.1";
|
|
2051
|
+
var DEFAULT_API_URL = "http://121.196.224.82:3001";
|
|
2052
|
+
var DEFAULT_BACKFILL_BATCH_SIZE = 50;
|
|
2053
|
+
var DEFAULT_BACKFILL_BATCH_BYTES = 800 * 1024;
|
|
2054
|
+
var ROLLUP_BUCKET_MS = 15 * 60 * 1e3;
|
|
2055
|
+
var DEFAULT_HOOK_SYNC_MIN_INTERVAL_SECONDS = 60;
|
|
2056
|
+
|
|
2001
2057
|
// src/lib/jsonl.ts
|
|
2002
2058
|
function parseJsonLine(line) {
|
|
2003
2059
|
try {
|
|
@@ -3868,14 +3924,14 @@ function claudeProjectRootFromCwds(projectDir, cwds) {
|
|
|
3868
3924
|
return void 0;
|
|
3869
3925
|
}
|
|
3870
3926
|
function encodeClaudeProjectPath(value) {
|
|
3871
|
-
return path7.resolve(value).split(path7.sep).join("-").
|
|
3927
|
+
return path7.resolve(value).split(path7.sep).join("-").replaceAll("_", "-");
|
|
3872
3928
|
}
|
|
3873
3929
|
function rawClaudeProjectPath(value) {
|
|
3874
3930
|
return path7.resolve(value).split(path7.sep).join("-");
|
|
3875
3931
|
}
|
|
3876
3932
|
function claudeEncodedVariants(value) {
|
|
3877
3933
|
const raw = rawClaudeProjectPath(value);
|
|
3878
|
-
const normalized = raw.
|
|
3934
|
+
const normalized = raw.replaceAll("_", "-");
|
|
3879
3935
|
return raw === normalized ? [raw] : [raw, normalized];
|
|
3880
3936
|
}
|
|
3881
3937
|
function claudeEncodedProjectSuffix(projectDir, home) {
|
|
@@ -4129,7 +4185,7 @@ async function discoverCoworkTranscripts(root) {
|
|
|
4129
4185
|
function coworkMetaPathForTranscript(transcriptPath) {
|
|
4130
4186
|
const marker = `${path8.sep}.claude${path8.sep}projects${path8.sep}`;
|
|
4131
4187
|
const index = transcriptPath.indexOf(marker);
|
|
4132
|
-
if (index
|
|
4188
|
+
if (index === -1) {
|
|
4133
4189
|
return void 0;
|
|
4134
4190
|
}
|
|
4135
4191
|
return `${transcriptPath.slice(0, index)}.json`;
|
|
@@ -4144,7 +4200,7 @@ function projectFromMeta(meta) {
|
|
|
4144
4200
|
}
|
|
4145
4201
|
const resolved = path8.resolve(folder);
|
|
4146
4202
|
return {
|
|
4147
|
-
project: path8.basename(resolved).
|
|
4203
|
+
project: path8.basename(resolved).replaceAll("-", "_") || "cowork",
|
|
4148
4204
|
cwd: resolved
|
|
4149
4205
|
};
|
|
4150
4206
|
}
|
|
@@ -4269,7 +4325,7 @@ function createClaudeCoworkAdapter() {
|
|
|
4269
4325
|
}
|
|
4270
4326
|
|
|
4271
4327
|
// src/adapters/codebuddy.ts
|
|
4272
|
-
import {
|
|
4328
|
+
import { readdir as readdir4, readFile as readFile5 } from "node:fs/promises";
|
|
4273
4329
|
import path9 from "node:path";
|
|
4274
4330
|
var ENDPOINT_MODEL_ID_RE = /^(?:ep|endpoint)-/i;
|
|
4275
4331
|
async function parseCodebuddyTraceFile(filePath, options) {
|
|
@@ -4504,6 +4560,28 @@ async function parseCodebuddyTraceFile(filePath, options) {
|
|
|
4504
4560
|
sourceId: span.spanId
|
|
4505
4561
|
})
|
|
4506
4562
|
}), index, "function");
|
|
4563
|
+
const toolInput = parseEmbeddedJson(span.toolInput);
|
|
4564
|
+
if (isPlainObject(toolInput)) {
|
|
4565
|
+
const fileActivities = claudeStyleToolFileActivities(tool, toolInput, ts, cwd);
|
|
4566
|
+
if (fileActivities.length > 0) {
|
|
4567
|
+
push(baseEvent({
|
|
4568
|
+
ts,
|
|
4569
|
+
type: eventTypeFromFileActivities(fileActivities),
|
|
4570
|
+
operation: `${tool} file activity`,
|
|
4571
|
+
sessionId,
|
|
4572
|
+
cwd,
|
|
4573
|
+
project,
|
|
4574
|
+
model,
|
|
4575
|
+
tool,
|
|
4576
|
+
confidence: "derived",
|
|
4577
|
+
fileActivities,
|
|
4578
|
+
metrics: summarizeFileActivities(fileActivities),
|
|
4579
|
+
refs: stringRefs({
|
|
4580
|
+
sourceId: span.spanId
|
|
4581
|
+
})
|
|
4582
|
+
}), index, "function");
|
|
4583
|
+
}
|
|
4584
|
+
}
|
|
4507
4585
|
if (span.endedAt) {
|
|
4508
4586
|
const durationMs = span.duration ?? void 0;
|
|
4509
4587
|
push(baseEvent({
|
|
@@ -4527,30 +4605,28 @@ async function parseCodebuddyTraceFile(filePath, options) {
|
|
|
4527
4605
|
})
|
|
4528
4606
|
}), index, "function");
|
|
4529
4607
|
}
|
|
4530
|
-
if (tool === "Bash") {
|
|
4531
|
-
|
|
4532
|
-
|
|
4533
|
-
|
|
4534
|
-
|
|
4535
|
-
|
|
4536
|
-
|
|
4537
|
-
|
|
4538
|
-
|
|
4539
|
-
|
|
4540
|
-
|
|
4541
|
-
|
|
4542
|
-
|
|
4543
|
-
|
|
4544
|
-
|
|
4545
|
-
|
|
4546
|
-
|
|
4547
|
-
|
|
4548
|
-
|
|
4549
|
-
|
|
4550
|
-
|
|
4551
|
-
|
|
4552
|
-
}), index, "function");
|
|
4553
|
-
}
|
|
4608
|
+
if (tool === "Bash" && span.endedAt) {
|
|
4609
|
+
const durationMs = span.duration ?? void 0;
|
|
4610
|
+
push(baseEvent({
|
|
4611
|
+
ts: span.endedAt,
|
|
4612
|
+
type: toolSuccess ? "command.completed" : "command.failed",
|
|
4613
|
+
operation: "Bash completed",
|
|
4614
|
+
sessionId,
|
|
4615
|
+
cwd,
|
|
4616
|
+
project,
|
|
4617
|
+
model,
|
|
4618
|
+
tool,
|
|
4619
|
+
success: toolSuccess,
|
|
4620
|
+
confidence: "derived",
|
|
4621
|
+
metrics: {
|
|
4622
|
+
commandCalls: 1,
|
|
4623
|
+
commandDurationMs: durationMs,
|
|
4624
|
+
durationMs
|
|
4625
|
+
},
|
|
4626
|
+
refs: stringRefs({
|
|
4627
|
+
sourceId: span.spanId
|
|
4628
|
+
})
|
|
4629
|
+
}), index, "function");
|
|
4554
4630
|
}
|
|
4555
4631
|
continue;
|
|
4556
4632
|
}
|
|
@@ -4755,7 +4831,6 @@ async function resolveSessionIdFromSiblingTraces(filePath) {
|
|
|
4755
4831
|
}
|
|
4756
4832
|
} catch {
|
|
4757
4833
|
}
|
|
4758
|
-
return void 0;
|
|
4759
4834
|
})();
|
|
4760
4835
|
siblingSessionIdByDir.set(dir, cached);
|
|
4761
4836
|
}
|
|
@@ -4949,6 +5024,33 @@ import { readFile as readFile6 } from "node:fs/promises";
|
|
|
4949
5024
|
import path11 from "node:path";
|
|
4950
5025
|
|
|
4951
5026
|
// src/lib/diff.ts
|
|
5027
|
+
function parseApplyPatch(patch, ts) {
|
|
5028
|
+
const changes = [];
|
|
5029
|
+
let current;
|
|
5030
|
+
for (const line of patch.split("\n")) {
|
|
5031
|
+
const fileMatch = line.match(/^\*\*\* (Add|Update|Delete) File: (.+)$/);
|
|
5032
|
+
if (fileMatch) {
|
|
5033
|
+
current = {
|
|
5034
|
+
ts,
|
|
5035
|
+
path: fileMatch[2].trim(),
|
|
5036
|
+
operation: fileMatch[1] === "Add" ? "create" : fileMatch[1] === "Delete" ? "delete" : "edit",
|
|
5037
|
+
linesAdded: 0,
|
|
5038
|
+
linesRemoved: 0
|
|
5039
|
+
};
|
|
5040
|
+
changes.push(current);
|
|
5041
|
+
continue;
|
|
5042
|
+
}
|
|
5043
|
+
if (!current) {
|
|
5044
|
+
continue;
|
|
5045
|
+
}
|
|
5046
|
+
if (line.startsWith("+") && !line.startsWith("+++")) {
|
|
5047
|
+
current.linesAdded = (current.linesAdded || 0) + 1;
|
|
5048
|
+
} else if (line.startsWith("-") && !line.startsWith("---")) {
|
|
5049
|
+
current.linesRemoved = (current.linesRemoved || 0) + 1;
|
|
5050
|
+
}
|
|
5051
|
+
}
|
|
5052
|
+
return changes;
|
|
5053
|
+
}
|
|
4952
5054
|
function diffStats(diff) {
|
|
4953
5055
|
let linesAdded = 0;
|
|
4954
5056
|
let linesRemoved = 0;
|
|
@@ -4992,10 +5094,10 @@ function fileActivitiesFromPatchChanges(changes, ts, cwd, displayFilePath3) {
|
|
|
4992
5094
|
}
|
|
4993
5095
|
|
|
4994
5096
|
// src/lib/session-context.ts
|
|
4995
|
-
init_fs();
|
|
4996
5097
|
import { mkdir as mkdir3, writeFile as writeFile3 } from "node:fs/promises";
|
|
4997
5098
|
import os4 from "node:os";
|
|
4998
5099
|
import path10 from "node:path";
|
|
5100
|
+
init_fs();
|
|
4999
5101
|
var SESSION_CONTEXT_VERSION = 1;
|
|
5000
5102
|
function sessionContextDir(home) {
|
|
5001
5103
|
return path10.join(home, ".vibetime", "session-context");
|
|
@@ -5814,7 +5916,7 @@ async function parseCopilotSessionFile(filePath, options) {
|
|
|
5814
5916
|
model = msgModel;
|
|
5815
5917
|
}
|
|
5816
5918
|
const msgTurnId = stringField(data, "turnId");
|
|
5817
|
-
const turnId = msgTurnId
|
|
5919
|
+
const turnId = msgTurnId === void 0 ? currentTurnId : `turn_${createStableHash([sessionId, msgTurnId]).slice(0, 24)}`;
|
|
5818
5920
|
events.push(baseCopilotEvent({
|
|
5819
5921
|
ts,
|
|
5820
5922
|
type: "agent.operation",
|
|
@@ -5841,7 +5943,7 @@ async function parseCopilotSessionFile(filePath, options) {
|
|
|
5841
5943
|
const toolCallId = stringField(data, "toolCallId");
|
|
5842
5944
|
const toolName = stringField(data, "toolName") || "tool";
|
|
5843
5945
|
const toolTurnId = stringField(data, "turnId");
|
|
5844
|
-
const turnId = toolTurnId
|
|
5946
|
+
const turnId = toolTurnId === void 0 ? currentTurnId : `turn_${createStableHash([sessionId, toolTurnId]).slice(0, 24)}`;
|
|
5845
5947
|
if (toolCallId) {
|
|
5846
5948
|
pendingTools.set(toolCallId, { tool: toolName, startedAt: ts, turnId });
|
|
5847
5949
|
}
|
|
@@ -5859,6 +5961,24 @@ async function parseCopilotSessionFile(filePath, options) {
|
|
|
5859
5961
|
metrics: { toolCalls: 1 },
|
|
5860
5962
|
refs: stringRefs({ sourceId: toolCallId })
|
|
5861
5963
|
}));
|
|
5964
|
+
const fileActivities = copilotFileActivities(toolName, data.arguments, ts, cwd);
|
|
5965
|
+
if (fileActivities.length > 0) {
|
|
5966
|
+
events.push(baseCopilotEvent({
|
|
5967
|
+
ts,
|
|
5968
|
+
type: eventTypeFromFileActivities(fileActivities),
|
|
5969
|
+
operation: `${toolName} file activity`,
|
|
5970
|
+
sessionId,
|
|
5971
|
+
turnId,
|
|
5972
|
+
cwd,
|
|
5973
|
+
project,
|
|
5974
|
+
model,
|
|
5975
|
+
tool: toolName,
|
|
5976
|
+
confidence: "derived",
|
|
5977
|
+
fileActivities,
|
|
5978
|
+
metrics: summarizeFileActivities(fileActivities),
|
|
5979
|
+
refs: stringRefs({ sourceId: toolCallId })
|
|
5980
|
+
}));
|
|
5981
|
+
}
|
|
5862
5982
|
break;
|
|
5863
5983
|
}
|
|
5864
5984
|
case "tool.execution_complete": {
|
|
@@ -5881,7 +6001,7 @@ async function parseCopilotSessionFile(filePath, options) {
|
|
|
5881
6001
|
tool: pending?.tool,
|
|
5882
6002
|
success,
|
|
5883
6003
|
confidence: "exact",
|
|
5884
|
-
metrics: durationMs
|
|
6004
|
+
metrics: durationMs === void 0 ? void 0 : { toolDurationMs: durationMs, durationMs },
|
|
5885
6005
|
refs: stringRefs({ sourceId: toolCallId })
|
|
5886
6006
|
}));
|
|
5887
6007
|
const toolLower = (pending?.tool || "").toLowerCase();
|
|
@@ -5910,7 +6030,7 @@ async function parseCopilotSessionFile(filePath, options) {
|
|
|
5910
6030
|
}
|
|
5911
6031
|
case "assistant.turn_end": {
|
|
5912
6032
|
const endTurnId = stringField(data, "turnId");
|
|
5913
|
-
const turnId = endTurnId
|
|
6033
|
+
const turnId = endTurnId === void 0 ? currentTurnId : `turn_${createStableHash([sessionId, endTurnId]).slice(0, 24)}`;
|
|
5914
6034
|
const accum = turnId ? turnTokenAccum.get(turnId) : void 0;
|
|
5915
6035
|
if (accum && accum.tokensOutput && accum.tokensOutput > 0) {
|
|
5916
6036
|
accum.reasoningEffort = reasoningEffort;
|
|
@@ -5997,6 +6117,29 @@ async function parseCopilotSessionFile(filePath, options) {
|
|
|
5997
6117
|
}
|
|
5998
6118
|
return events.filter((event) => validateCanonicalEvent(event).valid);
|
|
5999
6119
|
}
|
|
6120
|
+
function copilotFileActivities(tool, args, ts, cwd) {
|
|
6121
|
+
const normalized = tool.toLowerCase();
|
|
6122
|
+
if (normalized === "apply_patch" && typeof args === "string") {
|
|
6123
|
+
return parseApplyPatch(args, ts);
|
|
6124
|
+
}
|
|
6125
|
+
if (!isPlainObject(args)) {
|
|
6126
|
+
return [];
|
|
6127
|
+
}
|
|
6128
|
+
const changes = /* @__PURE__ */ new Map();
|
|
6129
|
+
const filePath = stringField(args, "path") || stringField(args, "file_path");
|
|
6130
|
+
if (filePath) {
|
|
6131
|
+
const metrics = {};
|
|
6132
|
+
if (normalized === "edit") {
|
|
6133
|
+
const oldStr = stringField(args, "old_str");
|
|
6134
|
+
const newStr = stringField(args, "new_str");
|
|
6135
|
+
metrics.linesAdded = countTextLines(newStr);
|
|
6136
|
+
metrics.linesRemoved = countTextLines(oldStr);
|
|
6137
|
+
metrics.charsWritten = newStr?.length;
|
|
6138
|
+
}
|
|
6139
|
+
addResolvedPathActivity(changes, filePath, operationForTool(normalized === "view" ? "read" : tool), ts, cwd, cwd, metrics);
|
|
6140
|
+
}
|
|
6141
|
+
return [...changes.values()];
|
|
6142
|
+
}
|
|
6000
6143
|
function baseCopilotEvent(event) {
|
|
6001
6144
|
return {
|
|
6002
6145
|
schemaVersion: AGENT_TIME_SCHEMA_VERSION,
|
|
@@ -6205,7 +6348,7 @@ async function parseGrokSessionFile(filePath, options) {
|
|
|
6205
6348
|
}
|
|
6206
6349
|
if (type === "turn_started") {
|
|
6207
6350
|
const turnNumber = numberField(raw, "turn_number");
|
|
6208
|
-
currentTurnId = turnNumber
|
|
6351
|
+
currentTurnId = turnNumber === void 0 ? `turn_${createStableHash([sessionId, lineNumber]).slice(0, 24)}` : `turn_${turnNumber}`;
|
|
6209
6352
|
currentTurnStartedAt = ts;
|
|
6210
6353
|
const turnModel = stringField(raw, "model_id") || model;
|
|
6211
6354
|
push(base({
|
|
@@ -6229,7 +6372,7 @@ async function parseGrokSessionFile(filePath, options) {
|
|
|
6229
6372
|
turnId,
|
|
6230
6373
|
success,
|
|
6231
6374
|
confidence: "exact",
|
|
6232
|
-
metrics: durationMs
|
|
6375
|
+
metrics: durationMs === void 0 ? void 0 : { durationMs },
|
|
6233
6376
|
refs: stringRefs({
|
|
6234
6377
|
sourceId: `${sessionId}:${turnId || "turn"}:ended`,
|
|
6235
6378
|
outcome
|
|
@@ -6374,7 +6517,7 @@ function resolveSessionDir(filePath) {
|
|
|
6374
6517
|
if (base === "summary.json" || base === "events.jsonl" || base === "signals.json") {
|
|
6375
6518
|
return path13.dirname(filePath);
|
|
6376
6519
|
}
|
|
6377
|
-
if (
|
|
6520
|
+
if (/^[0-9a-f]{8}-[0-9a-f-]{27,}$/i.test(base)) {
|
|
6378
6521
|
return filePath;
|
|
6379
6522
|
}
|
|
6380
6523
|
return void 0;
|
|
@@ -7381,6 +7524,18 @@ function piFileActivitiesFromToolCall(tool, input, ts, cwd) {
|
|
|
7381
7524
|
addResolvedPathActivity(changes, filePath, operation, ts, cwd, cwd, piToolFileMetrics(normalized, input));
|
|
7382
7525
|
}
|
|
7383
7526
|
}
|
|
7527
|
+
if (normalized === "edit") {
|
|
7528
|
+
const filePath = stringField(input, "path") || stringField(input, "file_path") || stringField(input, "filePath");
|
|
7529
|
+
for (const edit of Array.isArray(input.edits) ? input.edits : []) {
|
|
7530
|
+
if (!isPlainObject(edit) || !filePath) {
|
|
7531
|
+
continue;
|
|
7532
|
+
}
|
|
7533
|
+
addResolvedPathActivity(changes, filePath, "edit", ts, cwd, cwd, {
|
|
7534
|
+
linesAdded: countTextLines(stringField(edit, "newText") || stringField(edit, "new_text")),
|
|
7535
|
+
linesRemoved: countTextLines(stringField(edit, "oldText") || stringField(edit, "old_text"))
|
|
7536
|
+
});
|
|
7537
|
+
}
|
|
7538
|
+
}
|
|
7384
7539
|
if (normalized === "bash" || normalized === "run") {
|
|
7385
7540
|
const command = stringField(input, "command");
|
|
7386
7541
|
if (command) {
|
|
@@ -8069,7 +8224,7 @@ async function parseQoderCnSessionFile(filePath, options) {
|
|
|
8069
8224
|
const toolUseId = stringField(toolUse, "id") || `tool_${createStableHash([filePath, lineNumber, tool]).slice(0, 24)}`;
|
|
8070
8225
|
const input = objectField(toolUse, "input");
|
|
8071
8226
|
const command = stringField(input, "command");
|
|
8072
|
-
const fileActivities =
|
|
8227
|
+
const fileActivities = claudeStyleToolFileActivities(tool, input, ts, cwd);
|
|
8073
8228
|
const agentInstanceId = tool === "Agent" ? `agent_${createStableHash([sessionId, toolUseId]).slice(0, 24)}` : void 0;
|
|
8074
8229
|
pendingTools.set(toolUseId, {
|
|
8075
8230
|
id: toolUseId,
|
|
@@ -8150,7 +8305,11 @@ async function parseQoderCnSessionFile(filePath, options) {
|
|
|
8150
8305
|
const mapped = {
|
|
8151
8306
|
...event,
|
|
8152
8307
|
sessionId: parentSessionId,
|
|
8153
|
-
refs: {
|
|
8308
|
+
refs: {
|
|
8309
|
+
...event.refs,
|
|
8310
|
+
sourcePathHash: parentSourcePathHash,
|
|
8311
|
+
...event.sessionId && event.sessionId !== parentSessionId ? { supersededSessionId: event.sessionId } : {}
|
|
8312
|
+
}
|
|
8154
8313
|
};
|
|
8155
8314
|
return rebuildEventIdentity2(mapped);
|
|
8156
8315
|
});
|
|
@@ -8163,7 +8322,11 @@ async function parseQoderCnSessionFile(filePath, options) {
|
|
|
8163
8322
|
return validEvents.map((event) => rebuildEventIdentity2({
|
|
8164
8323
|
...event,
|
|
8165
8324
|
sessionId: dbModelCalls.rootSessionId,
|
|
8166
|
-
refs: {
|
|
8325
|
+
refs: {
|
|
8326
|
+
...event.refs,
|
|
8327
|
+
sourcePathHash: parentSourcePathHash,
|
|
8328
|
+
...event.sessionId && event.sessionId !== dbModelCalls.rootSessionId ? { supersededSessionId: event.sessionId } : {}
|
|
8329
|
+
}
|
|
8167
8330
|
}));
|
|
8168
8331
|
}
|
|
8169
8332
|
return validEvents;
|
|
@@ -8198,62 +8361,6 @@ function qoderCnSubagentMetrics(toolResult, fallbackDurationMs) {
|
|
|
8198
8361
|
tokensTotal: numberField(toolResult, "totalTokens") || totalInputTokens + outputTokens || void 0
|
|
8199
8362
|
};
|
|
8200
8363
|
}
|
|
8201
|
-
function fileActivitiesFromQoderCnToolUse(tool, input, ts, cwd) {
|
|
8202
|
-
const changes = /* @__PURE__ */ new Map();
|
|
8203
|
-
const operation = operationForTool(tool);
|
|
8204
|
-
const workdir = stringField(input, "cwd") || cwd;
|
|
8205
|
-
for (const key of ["file_path", "path", "notebook_path"]) {
|
|
8206
|
-
const filePath = stringField(input, key);
|
|
8207
|
-
if (!filePath) {
|
|
8208
|
-
continue;
|
|
8209
|
-
}
|
|
8210
|
-
const metrics = fileMetricsFromQoderCnToolInput(tool, input);
|
|
8211
|
-
addResolvedPathActivity(changes, filePath, operation, ts, cwd, workdir, metrics);
|
|
8212
|
-
}
|
|
8213
|
-
for (const edit of arrayField4(input, "edits")) {
|
|
8214
|
-
if (!isPlainObject(edit)) {
|
|
8215
|
-
continue;
|
|
8216
|
-
}
|
|
8217
|
-
const filePath = stringField(input, "file_path") || stringField(input, "path");
|
|
8218
|
-
if (!filePath) {
|
|
8219
|
-
continue;
|
|
8220
|
-
}
|
|
8221
|
-
addResolvedPathActivity(changes, filePath, "edit", ts, cwd, workdir, {
|
|
8222
|
-
linesAdded: countTextLines(stringField(edit, "new_string")),
|
|
8223
|
-
linesRemoved: countTextLines(stringField(edit, "old_string"))
|
|
8224
|
-
});
|
|
8225
|
-
}
|
|
8226
|
-
const command = stringField(input, "command");
|
|
8227
|
-
if (command) {
|
|
8228
|
-
for (const item of fileActivitiesFromShellCommand(command, ts, cwd, workdir)) {
|
|
8229
|
-
changes.set(item.path, mergeFileActivity(changes.get(item.path), item));
|
|
8230
|
-
}
|
|
8231
|
-
}
|
|
8232
|
-
return [...changes.values()];
|
|
8233
|
-
}
|
|
8234
|
-
function fileMetricsFromQoderCnToolInput(tool, input) {
|
|
8235
|
-
const normalized = tool.toLowerCase();
|
|
8236
|
-
if (normalized === "read") {
|
|
8237
|
-
return { linesRead: numberField(input, "limit") };
|
|
8238
|
-
}
|
|
8239
|
-
if (normalized === "write") {
|
|
8240
|
-
const content = stringField(input, "content");
|
|
8241
|
-
return {
|
|
8242
|
-
linesAdded: countTextLines(content),
|
|
8243
|
-
charsWritten: content?.length
|
|
8244
|
-
};
|
|
8245
|
-
}
|
|
8246
|
-
if (normalized === "edit" || normalized === "multiedit") {
|
|
8247
|
-
const oldString = stringField(input, "old_string");
|
|
8248
|
-
const newString = stringField(input, "new_string");
|
|
8249
|
-
return {
|
|
8250
|
-
linesAdded: countTextLines(newString),
|
|
8251
|
-
linesRemoved: countTextLines(oldString),
|
|
8252
|
-
charsWritten: newString?.length
|
|
8253
|
-
};
|
|
8254
|
-
}
|
|
8255
|
-
return {};
|
|
8256
|
-
}
|
|
8257
8364
|
function arrayField4(object, key) {
|
|
8258
8365
|
if (!isPlainObject(object) || !Array.isArray(object[key])) {
|
|
8259
8366
|
return [];
|
|
@@ -8414,7 +8521,7 @@ function qoderCnProjectRootFromCwds(projectDir, cwds) {
|
|
|
8414
8521
|
return void 0;
|
|
8415
8522
|
}
|
|
8416
8523
|
function encodeQoderCnProjectPath(value) {
|
|
8417
|
-
return path17.resolve(value).split(path17.sep).join("-").
|
|
8524
|
+
return path17.resolve(value).split(path17.sep).join("-").replaceAll("_", "-");
|
|
8418
8525
|
}
|
|
8419
8526
|
async function qoderCnProjectFromFilePath(filePath, options) {
|
|
8420
8527
|
const projectDir = path17.basename(path17.dirname(filePath));
|
|
@@ -8895,7 +9002,7 @@ async function parseQoderSessionFile(filePath, options) {
|
|
|
8895
9002
|
const toolUseId = stringField(toolUse, "id") || `tool_${createStableHash([filePath, lineNumber, tool]).slice(0, 24)}`;
|
|
8896
9003
|
const input = objectField(toolUse, "input");
|
|
8897
9004
|
const command = stringField(input, "command");
|
|
8898
|
-
const fileActivities =
|
|
9005
|
+
const fileActivities = claudeStyleToolFileActivities(tool, input, ts, cwd);
|
|
8899
9006
|
const agentInstanceId = tool === "Agent" ? `agent_${createStableHash([sessionId, toolUseId]).slice(0, 24)}` : void 0;
|
|
8900
9007
|
pendingTools.set(toolUseId, {
|
|
8901
9008
|
id: toolUseId,
|
|
@@ -8976,7 +9083,11 @@ async function parseQoderSessionFile(filePath, options) {
|
|
|
8976
9083
|
const mapped = {
|
|
8977
9084
|
...event,
|
|
8978
9085
|
sessionId: parentSessionId,
|
|
8979
|
-
refs: {
|
|
9086
|
+
refs: {
|
|
9087
|
+
...event.refs,
|
|
9088
|
+
sourcePathHash: parentSourcePathHash,
|
|
9089
|
+
...event.sessionId && event.sessionId !== parentSessionId ? { supersededSessionId: event.sessionId } : {}
|
|
9090
|
+
}
|
|
8980
9091
|
};
|
|
8981
9092
|
return rebuildEventIdentity3(mapped);
|
|
8982
9093
|
});
|
|
@@ -8989,7 +9100,11 @@ async function parseQoderSessionFile(filePath, options) {
|
|
|
8989
9100
|
return validEvents.map((event) => rebuildEventIdentity3({
|
|
8990
9101
|
...event,
|
|
8991
9102
|
sessionId: dbModelCalls.rootSessionId,
|
|
8992
|
-
refs: {
|
|
9103
|
+
refs: {
|
|
9104
|
+
...event.refs,
|
|
9105
|
+
sourcePathHash: parentSourcePathHash,
|
|
9106
|
+
...event.sessionId && event.sessionId !== dbModelCalls.rootSessionId ? { supersededSessionId: event.sessionId } : {}
|
|
9107
|
+
}
|
|
8993
9108
|
}));
|
|
8994
9109
|
}
|
|
8995
9110
|
return validEvents;
|
|
@@ -9024,62 +9139,6 @@ function qoderSubagentMetrics(toolResult, fallbackDurationMs) {
|
|
|
9024
9139
|
tokensTotal: numberField(toolResult, "totalTokens") || totalInputTokens + outputTokens || void 0
|
|
9025
9140
|
};
|
|
9026
9141
|
}
|
|
9027
|
-
function fileActivitiesFromQoderToolUse(tool, input, ts, cwd) {
|
|
9028
|
-
const changes = /* @__PURE__ */ new Map();
|
|
9029
|
-
const operation = operationForTool(tool);
|
|
9030
|
-
const workdir = stringField(input, "cwd") || cwd;
|
|
9031
|
-
for (const key of ["file_path", "path", "notebook_path"]) {
|
|
9032
|
-
const filePath = stringField(input, key);
|
|
9033
|
-
if (!filePath) {
|
|
9034
|
-
continue;
|
|
9035
|
-
}
|
|
9036
|
-
const metrics = fileMetricsFromQoderToolInput(tool, input);
|
|
9037
|
-
addResolvedPathActivity(changes, filePath, operation, ts, cwd, workdir, metrics);
|
|
9038
|
-
}
|
|
9039
|
-
for (const edit of arrayField5(input, "edits")) {
|
|
9040
|
-
if (!isPlainObject(edit)) {
|
|
9041
|
-
continue;
|
|
9042
|
-
}
|
|
9043
|
-
const filePath = stringField(input, "file_path") || stringField(input, "path");
|
|
9044
|
-
if (!filePath) {
|
|
9045
|
-
continue;
|
|
9046
|
-
}
|
|
9047
|
-
addResolvedPathActivity(changes, filePath, "edit", ts, cwd, workdir, {
|
|
9048
|
-
linesAdded: countTextLines(stringField(edit, "new_string")),
|
|
9049
|
-
linesRemoved: countTextLines(stringField(edit, "old_string"))
|
|
9050
|
-
});
|
|
9051
|
-
}
|
|
9052
|
-
const command = stringField(input, "command");
|
|
9053
|
-
if (command) {
|
|
9054
|
-
for (const item of fileActivitiesFromShellCommand(command, ts, cwd, workdir)) {
|
|
9055
|
-
changes.set(item.path, mergeFileActivity(changes.get(item.path), item));
|
|
9056
|
-
}
|
|
9057
|
-
}
|
|
9058
|
-
return [...changes.values()];
|
|
9059
|
-
}
|
|
9060
|
-
function fileMetricsFromQoderToolInput(tool, input) {
|
|
9061
|
-
const normalized = tool.toLowerCase();
|
|
9062
|
-
if (normalized === "read") {
|
|
9063
|
-
return { linesRead: numberField(input, "limit") };
|
|
9064
|
-
}
|
|
9065
|
-
if (normalized === "write") {
|
|
9066
|
-
const content = stringField(input, "content");
|
|
9067
|
-
return {
|
|
9068
|
-
linesAdded: countTextLines(content),
|
|
9069
|
-
charsWritten: content?.length
|
|
9070
|
-
};
|
|
9071
|
-
}
|
|
9072
|
-
if (normalized === "edit" || normalized === "multiedit") {
|
|
9073
|
-
const oldString = stringField(input, "old_string");
|
|
9074
|
-
const newString = stringField(input, "new_string");
|
|
9075
|
-
return {
|
|
9076
|
-
linesAdded: countTextLines(newString),
|
|
9077
|
-
linesRemoved: countTextLines(oldString),
|
|
9078
|
-
charsWritten: newString?.length
|
|
9079
|
-
};
|
|
9080
|
-
}
|
|
9081
|
-
return {};
|
|
9082
|
-
}
|
|
9083
9142
|
function arrayField5(object, key) {
|
|
9084
9143
|
if (!isPlainObject(object) || !Array.isArray(object[key])) {
|
|
9085
9144
|
return [];
|
|
@@ -9206,14 +9265,14 @@ function qoderProjectRootFromCwds(projectDir, cwds) {
|
|
|
9206
9265
|
return void 0;
|
|
9207
9266
|
}
|
|
9208
9267
|
function encodeQoderProjectPath(value) {
|
|
9209
|
-
return path18.resolve(value).split(path18.sep).join("-").
|
|
9268
|
+
return path18.resolve(value).split(path18.sep).join("-").replaceAll("_", "-");
|
|
9210
9269
|
}
|
|
9211
9270
|
function rawQoderProjectPath(value) {
|
|
9212
9271
|
return path18.resolve(value).split(path18.sep).join("-");
|
|
9213
9272
|
}
|
|
9214
9273
|
function qoderEncodedVariants(value) {
|
|
9215
9274
|
const raw = rawQoderProjectPath(value);
|
|
9216
|
-
const normalized = raw.
|
|
9275
|
+
const normalized = raw.replaceAll("_", "-");
|
|
9217
9276
|
return raw === normalized ? [raw] : [raw, normalized];
|
|
9218
9277
|
}
|
|
9219
9278
|
function qoderEncodedProjectSuffix(projectDir, home) {
|
|
@@ -9369,7 +9428,7 @@ function normalizeId(id) {
|
|
|
9369
9428
|
}
|
|
9370
9429
|
|
|
9371
9430
|
// src/adapters/workbuddy.ts
|
|
9372
|
-
import {
|
|
9431
|
+
import { readdir as readdir9, readFile as readFile12, stat as stat10 } from "node:fs/promises";
|
|
9373
9432
|
import path19 from "node:path";
|
|
9374
9433
|
function workbuddyProjectsDir(home, env) {
|
|
9375
9434
|
const override = env?.WORKBUDDY_PROJECTS_DIR || env?.WORKBUDDY_HOME;
|
|
@@ -9575,7 +9634,9 @@ async function parseWorkbuddySessionFile(filePath, options) {
|
|
|
9575
9634
|
confidence: "derived",
|
|
9576
9635
|
refs: { sourceId: `${sessionPrefix}:session`, ...subagentRefs }
|
|
9577
9636
|
}, lines[0], filePath, options);
|
|
9578
|
-
if (event)
|
|
9637
|
+
if (event) {
|
|
9638
|
+
events.push(event);
|
|
9639
|
+
}
|
|
9579
9640
|
}
|
|
9580
9641
|
for (const line of lines) {
|
|
9581
9642
|
const record = line.record;
|
|
@@ -9590,7 +9651,9 @@ async function parseWorkbuddySessionFile(filePath, options) {
|
|
|
9590
9651
|
continue;
|
|
9591
9652
|
}
|
|
9592
9653
|
const ended = closeTurn(line);
|
|
9593
|
-
if (ended)
|
|
9654
|
+
if (ended) {
|
|
9655
|
+
events.push(ended);
|
|
9656
|
+
}
|
|
9594
9657
|
turnIndex += 1;
|
|
9595
9658
|
currentTurnId = `${sessionPrefix}:turn:${turnIndex}`;
|
|
9596
9659
|
turnLastTs = ts;
|
|
@@ -9628,7 +9691,9 @@ async function parseWorkbuddySessionFile(filePath, options) {
|
|
|
9628
9691
|
refs: { sourceId: `${sourceId}:prompt` }
|
|
9629
9692
|
}, line, filePath, options)
|
|
9630
9693
|
]) {
|
|
9631
|
-
if (event)
|
|
9694
|
+
if (event) {
|
|
9695
|
+
events.push(event);
|
|
9696
|
+
}
|
|
9632
9697
|
}
|
|
9633
9698
|
continue;
|
|
9634
9699
|
}
|
|
@@ -9655,7 +9720,9 @@ async function parseWorkbuddySessionFile(filePath, options) {
|
|
|
9655
9720
|
confidence: "partial",
|
|
9656
9721
|
refs: { sourceId: `${sourceId}:usage` }
|
|
9657
9722
|
}, line, filePath, options);
|
|
9658
|
-
if (usage)
|
|
9723
|
+
if (usage) {
|
|
9724
|
+
events.push(usage);
|
|
9725
|
+
}
|
|
9659
9726
|
}
|
|
9660
9727
|
continue;
|
|
9661
9728
|
}
|
|
@@ -9682,7 +9749,40 @@ async function parseWorkbuddySessionFile(filePath, options) {
|
|
|
9682
9749
|
confidence: "exact",
|
|
9683
9750
|
refs: { sourceId: `${callId}:start` }
|
|
9684
9751
|
}, line, filePath, options);
|
|
9685
|
-
if (started)
|
|
9752
|
+
if (started) {
|
|
9753
|
+
events.push(started);
|
|
9754
|
+
}
|
|
9755
|
+
let toolInput;
|
|
9756
|
+
try {
|
|
9757
|
+
toolInput = JSON.parse(stringField(record, "arguments") || "");
|
|
9758
|
+
} catch {
|
|
9759
|
+
toolInput = void 0;
|
|
9760
|
+
}
|
|
9761
|
+
if (isPlainObject(toolInput)) {
|
|
9762
|
+
const fileActivities = claudeStyleToolFileActivities(tool, toolInput, ts, cwd);
|
|
9763
|
+
if (fileActivities.length > 0) {
|
|
9764
|
+
const fileEvent = makeEvent({
|
|
9765
|
+
schemaVersion: AGENT_TIME_SCHEMA_VERSION,
|
|
9766
|
+
ts,
|
|
9767
|
+
type: eventTypeFromFileActivities(fileActivities),
|
|
9768
|
+
source: "workbuddy",
|
|
9769
|
+
workspaceId,
|
|
9770
|
+
project,
|
|
9771
|
+
cwd,
|
|
9772
|
+
sessionId: sessionPrefix,
|
|
9773
|
+
turnId: currentTurnId,
|
|
9774
|
+
agent: "workbuddy",
|
|
9775
|
+
tool,
|
|
9776
|
+
confidence: "derived",
|
|
9777
|
+
fileActivities,
|
|
9778
|
+
metrics: summarizeFileActivities(fileActivities),
|
|
9779
|
+
refs: { sourceId: `${callId}:files` }
|
|
9780
|
+
}, line, filePath, options);
|
|
9781
|
+
if (fileEvent) {
|
|
9782
|
+
events.push(fileEvent);
|
|
9783
|
+
}
|
|
9784
|
+
}
|
|
9785
|
+
}
|
|
9686
9786
|
}
|
|
9687
9787
|
const metrics = workbuddyUsageMetrics(record);
|
|
9688
9788
|
const model = stringField(objectField(record, "providerData"), "model");
|
|
@@ -9703,7 +9803,9 @@ async function parseWorkbuddySessionFile(filePath, options) {
|
|
|
9703
9803
|
confidence: metrics ? "partial" : "derived",
|
|
9704
9804
|
refs: { sourceId: `${sourceId}:usage:${callId || line.lineNumber}` }
|
|
9705
9805
|
}, line, filePath, options);
|
|
9706
|
-
if (usage)
|
|
9806
|
+
if (usage) {
|
|
9807
|
+
events.push(usage);
|
|
9808
|
+
}
|
|
9707
9809
|
}
|
|
9708
9810
|
continue;
|
|
9709
9811
|
}
|
|
@@ -9732,13 +9834,17 @@ async function parseWorkbuddySessionFile(filePath, options) {
|
|
|
9732
9834
|
confidence: start ? "derived" : "partial",
|
|
9733
9835
|
refs: { sourceId: `${callId}:result` }
|
|
9734
9836
|
}, line, filePath, options);
|
|
9735
|
-
if (completed)
|
|
9837
|
+
if (completed) {
|
|
9838
|
+
events.push(completed);
|
|
9839
|
+
}
|
|
9736
9840
|
}
|
|
9737
9841
|
}
|
|
9738
9842
|
const lastLine = lines.at(-1);
|
|
9739
9843
|
if (lastLine) {
|
|
9740
9844
|
const ended = closeTurn(lastLine);
|
|
9741
|
-
if (ended)
|
|
9845
|
+
if (ended) {
|
|
9846
|
+
events.push(ended);
|
|
9847
|
+
}
|
|
9742
9848
|
}
|
|
9743
9849
|
const lastTs = lastLine ? timestampFrom(numberField(lastLine.record, "timestamp")) : void 0;
|
|
9744
9850
|
if (lastLine && lastTs) {
|
|
@@ -9755,7 +9861,9 @@ async function parseWorkbuddySessionFile(filePath, options) {
|
|
|
9755
9861
|
confidence: "derived",
|
|
9756
9862
|
refs: { sourceId: `${sessionPrefix}:ended` }
|
|
9757
9863
|
}, lastLine, filePath, options);
|
|
9758
|
-
if (event)
|
|
9864
|
+
if (event) {
|
|
9865
|
+
events.push(event);
|
|
9866
|
+
}
|
|
9759
9867
|
}
|
|
9760
9868
|
return events;
|
|
9761
9869
|
}
|
|
@@ -10758,6 +10866,7 @@ function buildSessionRollup(rollupKey, events) {
|
|
|
10758
10866
|
const agent = first.agent || ordered.find((event) => event.agent)?.agent;
|
|
10759
10867
|
const startedAt = ordered[0]?.ts || (/* @__PURE__ */ new Date()).toISOString();
|
|
10760
10868
|
const lastEventAt = ordered.at(-1)?.ts || startedAt;
|
|
10869
|
+
const supersededSessionIds = [...new Set(ordered.map((event) => event.refs?.supersededSessionId).filter((id) => Boolean(id) && id !== sessionId))];
|
|
10761
10870
|
const timeBuckets = /* @__PURE__ */ new Map();
|
|
10762
10871
|
const modelRollups = /* @__PURE__ */ new Map();
|
|
10763
10872
|
const toolRollups = /* @__PURE__ */ new Map();
|
|
@@ -11040,7 +11149,8 @@ function buildSessionRollup(rollupKey, events) {
|
|
|
11040
11149
|
startedAt: startedAt2,
|
|
11041
11150
|
durationMs: timing?.lastCallAt ? Math.max(0, Date.parse(timing.lastCallAt) - Date.parse(startedAt2)) : 0
|
|
11042
11151
|
};
|
|
11043
|
-
}).sort((a, b) => a.startedAt.localeCompare(b.startedAt))
|
|
11152
|
+
}).sort((a, b) => a.startedAt.localeCompare(b.startedAt)),
|
|
11153
|
+
...supersededSessionIds.length > 0 ? { supersededSessionIds } : {}
|
|
11044
11154
|
};
|
|
11045
11155
|
return {
|
|
11046
11156
|
...baseRollup,
|
|
@@ -11454,7 +11564,7 @@ async function postRollupBatch(remote, rollups, options = {}) {
|
|
|
11454
11564
|
};
|
|
11455
11565
|
}
|
|
11456
11566
|
async function deleteRollupsBySource(remote, source, machine, options = {}) {
|
|
11457
|
-
const query = `source=${encodeURIComponent(source)}${options.preserveTokens ? "&preserveTokens=1" : ""}${options.allowHistoricalRewrite ? "&allowHistoricalRewrite=1" : ""}`;
|
|
11567
|
+
const query = `source=${encodeURIComponent(source)}${options.rollupKey ? `&rollupKey=${encodeURIComponent(options.rollupKey)}` : ""}${options.preserveTokens ? "&preserveTokens=1" : ""}${options.allowHistoricalRewrite ? "&allowHistoricalRewrite=1" : ""}`;
|
|
11458
11568
|
const response = await remote.fetchImpl(
|
|
11459
11569
|
joinUrl(remote.baseUrl, `/v3/agent/sessions?${query}`),
|
|
11460
11570
|
{ method: "DELETE", headers: buildHeaders(remote.token, machine) }
|
|
@@ -12311,6 +12421,21 @@ async function sendSessionRollupBatch(rollups, options, ctx) {
|
|
|
12311
12421
|
machine,
|
|
12312
12422
|
allowHistoricalRewrite: options["purge-all"] === true
|
|
12313
12423
|
});
|
|
12424
|
+
if (options.force && result.conflicts === 0) {
|
|
12425
|
+
for (const rollup of rollups) {
|
|
12426
|
+
for (const sessionId of rollup.supersededSessionIds ?? []) {
|
|
12427
|
+
try {
|
|
12428
|
+
await deleteRollupsBySource(remote, rollup.source, machine, {
|
|
12429
|
+
rollupKey: createImportKey(["rollup", rollup.source, sessionId]),
|
|
12430
|
+
allowHistoricalRewrite: options["purge-all"] === true
|
|
12431
|
+
});
|
|
12432
|
+
} catch (error) {
|
|
12433
|
+
debug(ctx, `Failed to delete superseded ${rollup.source} session ${sessionId}: ${error.message}
|
|
12434
|
+
`);
|
|
12435
|
+
}
|
|
12436
|
+
}
|
|
12437
|
+
}
|
|
12438
|
+
}
|
|
12314
12439
|
return result;
|
|
12315
12440
|
}
|
|
12316
12441
|
async function deleteSessionRollupsBySourceAPI(source, options, ctx, preserveTokens) {
|
|
@@ -12392,8 +12517,8 @@ function selectBackfillFilesForImport(files, watermarkTs) {
|
|
|
12392
12517
|
}
|
|
12393
12518
|
}
|
|
12394
12519
|
const order = /* @__PURE__ */ new Map();
|
|
12395
|
-
for (
|
|
12396
|
-
order.set(
|
|
12520
|
+
for (const [i, file] of files.entries()) {
|
|
12521
|
+
order.set(file.path, i);
|
|
12397
12522
|
}
|
|
12398
12523
|
picked.sort((a, b) => (order.get(a.path) ?? 0) - (order.get(b.path) ?? 0));
|
|
12399
12524
|
return picked;
|
package/package.json
CHANGED