@yhong91/vibetime 0.1.2 → 0.1.3
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 +801 -41
- package/package.json +1 -1
package/bin/vibetime.mjs
CHANGED
|
@@ -159,9 +159,9 @@ var init_fs = __esm({
|
|
|
159
159
|
|
|
160
160
|
// src/cli.ts
|
|
161
161
|
import { spawn } from "node:child_process";
|
|
162
|
-
import { mkdir as mkdir4, rename as rename2, rm, stat as
|
|
162
|
+
import { mkdir as mkdir4, rename as rename2, rm, stat as stat10, writeFile as writeFile3 } from "node:fs/promises";
|
|
163
163
|
import os7 from "node:os";
|
|
164
|
-
import
|
|
164
|
+
import path18 from "node:path";
|
|
165
165
|
import { fileURLToPath } from "node:url";
|
|
166
166
|
|
|
167
167
|
// ../shared/src/index.ts
|
|
@@ -197,7 +197,7 @@ var TELEMETRY_EVENT_TYPES = [
|
|
|
197
197
|
"agent.operation"
|
|
198
198
|
];
|
|
199
199
|
var FILE_ACTIVITY_OPERATIONS = ["read", "search", "create", "write", "edit", "delete"];
|
|
200
|
-
var BACKFILL_SOURCE_IDS = ["codex", "claude-code", "claude-cowork", "opencode", "pi", "agy", "codebuddy", "qoder", "qoder-cn"];
|
|
200
|
+
var BACKFILL_SOURCE_IDS = ["codex", "claude-code", "claude-cowork", "opencode", "pi", "agy", "codebuddy", "qoder", "qoder-cn", "workbuddy", "zcode"];
|
|
201
201
|
function createWorkspaceId(input) {
|
|
202
202
|
const basis = input.repoUrl || input.repoRoot || input.projectName || "unknown";
|
|
203
203
|
return `workspace_${fnv1a(basis)}`;
|
|
@@ -349,9 +349,9 @@ function removeVolatileHashFields(value) {
|
|
|
349
349
|
continue;
|
|
350
350
|
}
|
|
351
351
|
if (key === "refs" && isRecord(item)) {
|
|
352
|
-
const
|
|
353
|
-
delete
|
|
354
|
-
result[key] = removeVolatileHashFields(
|
|
352
|
+
const refs2 = { ...item };
|
|
353
|
+
delete refs2.payloadHash;
|
|
354
|
+
result[key] = removeVolatileHashFields(refs2);
|
|
355
355
|
continue;
|
|
356
356
|
}
|
|
357
357
|
result[key] = removeVolatileHashFields(item);
|
|
@@ -1195,7 +1195,7 @@ function countTextLines(text) {
|
|
|
1195
1195
|
}
|
|
1196
1196
|
|
|
1197
1197
|
// src/lib/constants.ts
|
|
1198
|
-
var PACKAGE_VERSION = true ? "0.1.
|
|
1198
|
+
var PACKAGE_VERSION = true ? "0.1.3" : "0.1.1";
|
|
1199
1199
|
var DEFAULT_API_URL = "http://121.196.224.82:3001";
|
|
1200
1200
|
var DEFAULT_BACKFILL_BATCH_SIZE = 50;
|
|
1201
1201
|
var DEFAULT_BACKFILL_BATCH_BYTES = 800 * 1024;
|
|
@@ -1224,13 +1224,13 @@ function isPlainObject(value) {
|
|
|
1224
1224
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
1225
1225
|
}
|
|
1226
1226
|
function stringRefs(value) {
|
|
1227
|
-
const
|
|
1227
|
+
const refs2 = {};
|
|
1228
1228
|
for (const [key, item] of Object.entries(value || {})) {
|
|
1229
1229
|
if (typeof item === "string" && item.length > 0) {
|
|
1230
|
-
|
|
1230
|
+
refs2[key] = item;
|
|
1231
1231
|
}
|
|
1232
1232
|
}
|
|
1233
|
-
return
|
|
1233
|
+
return refs2;
|
|
1234
1234
|
}
|
|
1235
1235
|
function stringOption(value) {
|
|
1236
1236
|
if (Array.isArray(value)) {
|
|
@@ -1308,7 +1308,7 @@ function withBackfillRefs(event, context) {
|
|
|
1308
1308
|
event.type,
|
|
1309
1309
|
event.refs?.sourceId
|
|
1310
1310
|
]);
|
|
1311
|
-
const
|
|
1311
|
+
const refs2 = {
|
|
1312
1312
|
...stringRefs(event.refs),
|
|
1313
1313
|
importKey,
|
|
1314
1314
|
sourcePathHash: context.sourcePathHash,
|
|
@@ -1318,12 +1318,12 @@ function withBackfillRefs(event, context) {
|
|
|
1318
1318
|
parserVersion: PACKAGE_VERSION
|
|
1319
1319
|
};
|
|
1320
1320
|
if (context.options.includeSourcePath) {
|
|
1321
|
-
|
|
1321
|
+
refs2.transcriptPath = context.filePath;
|
|
1322
1322
|
}
|
|
1323
1323
|
return {
|
|
1324
1324
|
...event,
|
|
1325
1325
|
id: createStableEventId(importKey),
|
|
1326
|
-
refs
|
|
1326
|
+
refs: refs2
|
|
1327
1327
|
};
|
|
1328
1328
|
}
|
|
1329
1329
|
function matchesBackfillFilters(event, options) {
|
|
@@ -2956,9 +2956,9 @@ async function parseClaudeCoworkSessionFile(filePath, options) {
|
|
|
2956
2956
|
const sessionId = prefixCoworkId(event.sessionId);
|
|
2957
2957
|
const turnId = prefixCoworkId(event.turnId);
|
|
2958
2958
|
const workspaceId = createWorkspaceId({ projectName: project, repoRoot: cwd });
|
|
2959
|
-
const
|
|
2959
|
+
const refs2 = { ...event.refs };
|
|
2960
2960
|
if (metadata.title) {
|
|
2961
|
-
|
|
2961
|
+
refs2.coworkTitleHash = `sha256:${createStableHash(metadata.title)}`;
|
|
2962
2962
|
}
|
|
2963
2963
|
const mapped = {
|
|
2964
2964
|
...event,
|
|
@@ -2970,7 +2970,7 @@ async function parseClaudeCoworkSessionFile(filePath, options) {
|
|
|
2970
2970
|
project,
|
|
2971
2971
|
sessionId,
|
|
2972
2972
|
turnId,
|
|
2973
|
-
refs
|
|
2973
|
+
refs: refs2
|
|
2974
2974
|
};
|
|
2975
2975
|
return rebuildEventIdentity(mapped);
|
|
2976
2976
|
});
|
|
@@ -3507,8 +3507,8 @@ async function codebuddyBackfillFiles(sourceRoot, home, env) {
|
|
|
3507
3507
|
}
|
|
3508
3508
|
const filePath = path8.join(traceDir, entry);
|
|
3509
3509
|
try {
|
|
3510
|
-
const
|
|
3511
|
-
files.push({ path: filePath, modifiedAt:
|
|
3510
|
+
const stat11 = await import("node:fs/promises").then((fs) => fs.stat(filePath));
|
|
3511
|
+
files.push({ path: filePath, modifiedAt: stat11.mtime.toISOString() });
|
|
3512
3512
|
} catch {
|
|
3513
3513
|
}
|
|
3514
3514
|
}
|
|
@@ -4513,19 +4513,19 @@ function opencodeDataCandidates(home, env) {
|
|
|
4513
4513
|
return [primary, path10.join(home, ".opencode", "opencode.db")];
|
|
4514
4514
|
}
|
|
4515
4515
|
async function opencodeBackfillFiles(sourceRoot, home = os4.homedir(), env) {
|
|
4516
|
-
const { stat:
|
|
4516
|
+
const { stat: stat11 } = await import("node:fs/promises");
|
|
4517
4517
|
if (sourceRoot) {
|
|
4518
4518
|
if (!sourceRoot.endsWith(".db")) {
|
|
4519
4519
|
return [];
|
|
4520
4520
|
}
|
|
4521
|
-
const info = await
|
|
4521
|
+
const info = await stat11(sourceRoot).catch(() => null);
|
|
4522
4522
|
if (!info) {
|
|
4523
4523
|
return [];
|
|
4524
4524
|
}
|
|
4525
4525
|
return [{ path: sourceRoot, modifiedAt: info.mtime.toISOString() }];
|
|
4526
4526
|
}
|
|
4527
4527
|
for (const candidatePath of opencodeDataCandidates(home, env)) {
|
|
4528
|
-
const info = await
|
|
4528
|
+
const info = await stat11(candidatePath).catch(() => null);
|
|
4529
4529
|
if (info) {
|
|
4530
4530
|
return [{ path: candidatePath, modifiedAt: info.mtime.toISOString() }];
|
|
4531
4531
|
}
|
|
@@ -6580,6 +6580,758 @@ function normalizeId(id) {
|
|
|
6580
6580
|
return id;
|
|
6581
6581
|
}
|
|
6582
6582
|
|
|
6583
|
+
// src/adapters/workbuddy.ts
|
|
6584
|
+
import { readFile as readFile10, readdir as readdir7, stat as stat7 } from "node:fs/promises";
|
|
6585
|
+
import path14 from "node:path";
|
|
6586
|
+
init_fs();
|
|
6587
|
+
function workbuddyProjectsDir(home, env) {
|
|
6588
|
+
const override = env?.WORKBUDDY_PROJECTS_DIR || env?.WORKBUDDY_HOME;
|
|
6589
|
+
if (override && override.trim()) {
|
|
6590
|
+
return path14.resolve(override, override.endsWith("projects") ? "" : "projects");
|
|
6591
|
+
}
|
|
6592
|
+
return path14.join(home, ".workbuddy", "projects");
|
|
6593
|
+
}
|
|
6594
|
+
function projectFromCwd(cwd, fallback) {
|
|
6595
|
+
if (!cwd) {
|
|
6596
|
+
return fallback;
|
|
6597
|
+
}
|
|
6598
|
+
return path14.basename(cwd) || fallback;
|
|
6599
|
+
}
|
|
6600
|
+
function sourceHash(filePath) {
|
|
6601
|
+
return `sha256:${createStableHash(filePath)}`;
|
|
6602
|
+
}
|
|
6603
|
+
function makeEvent(event, line, filePath, options) {
|
|
6604
|
+
const withRefs = withBackfillRefs(event, {
|
|
6605
|
+
filePath,
|
|
6606
|
+
sourcePathHash: sourceHash(filePath),
|
|
6607
|
+
lineNumber: line.lineNumber,
|
|
6608
|
+
topType: stringField(line.record, "type"),
|
|
6609
|
+
payloadType: stringField(line.record, "role") || stringField(line.record, "name"),
|
|
6610
|
+
options
|
|
6611
|
+
});
|
|
6612
|
+
return validateCanonicalEvent(withRefs).valid ? withRefs : void 0;
|
|
6613
|
+
}
|
|
6614
|
+
function workbuddyUsageMetrics(record) {
|
|
6615
|
+
const providerData = objectField(record, "providerData");
|
|
6616
|
+
const usage = objectField(providerData, "usage");
|
|
6617
|
+
const rawUsage = objectField(providerData, "rawUsage");
|
|
6618
|
+
if (Object.keys(usage).length === 0 && Object.keys(rawUsage).length === 0) {
|
|
6619
|
+
return void 0;
|
|
6620
|
+
}
|
|
6621
|
+
const input = numberField(usage, "inputTokens") ?? numberField(rawUsage, "prompt_tokens") ?? 0;
|
|
6622
|
+
const output = numberField(usage, "outputTokens") ?? numberField(rawUsage, "completion_tokens") ?? 0;
|
|
6623
|
+
const total = numberField(usage, "totalTokens") ?? numberField(rawUsage, "total_tokens") ?? 0;
|
|
6624
|
+
const cacheRead = workbuddyCachedTokens(usage, rawUsage);
|
|
6625
|
+
const cacheCreate = numberField(rawUsage, "cache_creation_input_tokens") ?? numberField(rawUsage, "prompt_cache_write_tokens") ?? 0;
|
|
6626
|
+
const reasoning = workbuddyReasoningTokens(usage, rawUsage);
|
|
6627
|
+
if (!input && !output && !total && !cacheRead && !cacheCreate && !reasoning) {
|
|
6628
|
+
return void 0;
|
|
6629
|
+
}
|
|
6630
|
+
return {
|
|
6631
|
+
tokensInput: input,
|
|
6632
|
+
tokensOutput: output,
|
|
6633
|
+
tokensCachedInput: cacheRead + cacheCreate,
|
|
6634
|
+
tokensCacheCreationInput: cacheCreate,
|
|
6635
|
+
tokensCacheReadInput: cacheRead,
|
|
6636
|
+
tokensReasoningOutput: reasoning,
|
|
6637
|
+
tokensTotal: total || input + output,
|
|
6638
|
+
modelCalls: 1
|
|
6639
|
+
};
|
|
6640
|
+
}
|
|
6641
|
+
function workbuddyCachedTokens(usage, rawUsage) {
|
|
6642
|
+
const rawCache = numberField(rawUsage, "cache_read_input_tokens") ?? numberField(rawUsage, "prompt_cache_hit_tokens") ?? numberField(rawUsage, "cached_tokens");
|
|
6643
|
+
if (rawCache !== void 0) {
|
|
6644
|
+
return rawCache;
|
|
6645
|
+
}
|
|
6646
|
+
const details = usage.inputTokensDetails;
|
|
6647
|
+
if (Array.isArray(details) && isPlainObject(details[0])) {
|
|
6648
|
+
return numberField(details[0], "cached_tokens") || 0;
|
|
6649
|
+
}
|
|
6650
|
+
const rawDetails = objectField(rawUsage, "prompt_tokens_details");
|
|
6651
|
+
return numberField(rawDetails, "cached_tokens") || 0;
|
|
6652
|
+
}
|
|
6653
|
+
function workbuddyReasoningTokens(usage, rawUsage) {
|
|
6654
|
+
const completionDetails = objectField(rawUsage, "completion_tokens_details");
|
|
6655
|
+
const raw = numberField(completionDetails, "reasoning_tokens") ?? numberField(rawUsage, "completion_thinking_tokens");
|
|
6656
|
+
if (raw !== void 0) {
|
|
6657
|
+
return raw;
|
|
6658
|
+
}
|
|
6659
|
+
const details = usage.outputTokensDetails;
|
|
6660
|
+
if (Array.isArray(details) && isPlainObject(details[0])) {
|
|
6661
|
+
return numberField(details[0], "reasoning_tokens") || 0;
|
|
6662
|
+
}
|
|
6663
|
+
return 0;
|
|
6664
|
+
}
|
|
6665
|
+
function contentLength(value) {
|
|
6666
|
+
if (typeof value === "string") {
|
|
6667
|
+
return value.length;
|
|
6668
|
+
}
|
|
6669
|
+
if (Array.isArray(value)) {
|
|
6670
|
+
return value.reduce((total, item) => {
|
|
6671
|
+
if (!isPlainObject(item)) {
|
|
6672
|
+
return total;
|
|
6673
|
+
}
|
|
6674
|
+
return total + (stringField(item, "text") || stringField(item, "input_text") || stringField(item, "output_text") || "").length;
|
|
6675
|
+
}, 0);
|
|
6676
|
+
}
|
|
6677
|
+
return 0;
|
|
6678
|
+
}
|
|
6679
|
+
async function readWorkbuddyLines(filePath) {
|
|
6680
|
+
const text = await readFile10(filePath, "utf8");
|
|
6681
|
+
return text.split(/\r?\n/).map((line, index) => {
|
|
6682
|
+
if (!line.trim()) {
|
|
6683
|
+
return void 0;
|
|
6684
|
+
}
|
|
6685
|
+
try {
|
|
6686
|
+
const record = JSON.parse(line);
|
|
6687
|
+
return isPlainObject(record) ? { lineNumber: index + 1, record } : void 0;
|
|
6688
|
+
} catch {
|
|
6689
|
+
return void 0;
|
|
6690
|
+
}
|
|
6691
|
+
}).filter((line) => Boolean(line));
|
|
6692
|
+
}
|
|
6693
|
+
async function parseWorkbuddySessionFile(filePath, options) {
|
|
6694
|
+
const lines = await readWorkbuddyLines(filePath);
|
|
6695
|
+
if (lines.length === 0) {
|
|
6696
|
+
return [];
|
|
6697
|
+
}
|
|
6698
|
+
const events = [];
|
|
6699
|
+
const first = lines[0].record;
|
|
6700
|
+
const sessionId = stringField(first, "sessionId") || path14.basename(filePath, ".jsonl");
|
|
6701
|
+
const fallbackProject = path14.basename(path14.dirname(filePath));
|
|
6702
|
+
const cwd = lines.map((line) => stringField(line.record, "cwd")).find(Boolean);
|
|
6703
|
+
const project = projectFromCwd(cwd, fallbackProject);
|
|
6704
|
+
const workspaceId = createWorkspaceId({ projectName: project, repoRoot: cwd });
|
|
6705
|
+
const sessionPrefix = `workbuddy:${sessionId}`;
|
|
6706
|
+
const toolStarts = /* @__PURE__ */ new Map();
|
|
6707
|
+
let currentTurnId = `${sessionPrefix}:turn:0`;
|
|
6708
|
+
let turnIndex = 0;
|
|
6709
|
+
const firstTs = lines.map((line) => timestampFrom(numberField(line.record, "timestamp"))).find(Boolean);
|
|
6710
|
+
if (firstTs) {
|
|
6711
|
+
const event = makeEvent({
|
|
6712
|
+
schemaVersion: AGENT_TIME_SCHEMA_VERSION,
|
|
6713
|
+
ts: firstTs,
|
|
6714
|
+
type: "session.started",
|
|
6715
|
+
source: "workbuddy",
|
|
6716
|
+
workspaceId,
|
|
6717
|
+
project,
|
|
6718
|
+
cwd,
|
|
6719
|
+
sessionId: sessionPrefix,
|
|
6720
|
+
agent: "workbuddy",
|
|
6721
|
+
confidence: "derived",
|
|
6722
|
+
refs: { sourceId: `${sessionPrefix}:session` }
|
|
6723
|
+
}, lines[0], filePath, options);
|
|
6724
|
+
if (event) events.push(event);
|
|
6725
|
+
}
|
|
6726
|
+
for (const line of lines) {
|
|
6727
|
+
const record = line.record;
|
|
6728
|
+
const ts = timestampFrom(numberField(record, "timestamp"));
|
|
6729
|
+
if (!ts) {
|
|
6730
|
+
continue;
|
|
6731
|
+
}
|
|
6732
|
+
const type = stringField(record, "type");
|
|
6733
|
+
const sourceId = stringField(record, "id") || `${line.lineNumber}`;
|
|
6734
|
+
if (type === "message" && stringField(record, "role") === "user") {
|
|
6735
|
+
turnIndex += 1;
|
|
6736
|
+
currentTurnId = `${sessionPrefix}:turn:${turnIndex}`;
|
|
6737
|
+
const promptChars = contentLength(record.content);
|
|
6738
|
+
for (const event of [
|
|
6739
|
+
makeEvent({
|
|
6740
|
+
schemaVersion: AGENT_TIME_SCHEMA_VERSION,
|
|
6741
|
+
ts,
|
|
6742
|
+
type: "turn.started",
|
|
6743
|
+
source: "workbuddy",
|
|
6744
|
+
workspaceId,
|
|
6745
|
+
project,
|
|
6746
|
+
cwd,
|
|
6747
|
+
sessionId: sessionPrefix,
|
|
6748
|
+
turnId: currentTurnId,
|
|
6749
|
+
agent: "workbuddy",
|
|
6750
|
+
confidence: "derived",
|
|
6751
|
+
refs: { sourceId: `${sourceId}:turn` }
|
|
6752
|
+
}, line, filePath, options),
|
|
6753
|
+
makeEvent({
|
|
6754
|
+
schemaVersion: AGENT_TIME_SCHEMA_VERSION,
|
|
6755
|
+
ts,
|
|
6756
|
+
type: "prompt.submitted",
|
|
6757
|
+
source: "workbuddy",
|
|
6758
|
+
workspaceId,
|
|
6759
|
+
project,
|
|
6760
|
+
cwd,
|
|
6761
|
+
sessionId: sessionPrefix,
|
|
6762
|
+
turnId: currentTurnId,
|
|
6763
|
+
agent: "workbuddy",
|
|
6764
|
+
metrics: { promptChars, prompts: 1 },
|
|
6765
|
+
confidence: "partial",
|
|
6766
|
+
refs: { sourceId: `${sourceId}:prompt` }
|
|
6767
|
+
}, line, filePath, options)
|
|
6768
|
+
]) {
|
|
6769
|
+
if (event) events.push(event);
|
|
6770
|
+
}
|
|
6771
|
+
continue;
|
|
6772
|
+
}
|
|
6773
|
+
if (type === "function_call") {
|
|
6774
|
+
const tool = stringField(record, "name");
|
|
6775
|
+
const callId = stringField(record, "callId");
|
|
6776
|
+
if (tool && callId) {
|
|
6777
|
+
toolStarts.set(`${sessionId}:${callId}`, { line, ts, tool, callId });
|
|
6778
|
+
const started = makeEvent({
|
|
6779
|
+
schemaVersion: AGENT_TIME_SCHEMA_VERSION,
|
|
6780
|
+
ts,
|
|
6781
|
+
type: "tool.started",
|
|
6782
|
+
source: "workbuddy",
|
|
6783
|
+
workspaceId,
|
|
6784
|
+
project,
|
|
6785
|
+
cwd,
|
|
6786
|
+
sessionId: sessionPrefix,
|
|
6787
|
+
turnId: currentTurnId,
|
|
6788
|
+
spanId: `${sessionPrefix}:tool:${callId}`,
|
|
6789
|
+
agent: "workbuddy",
|
|
6790
|
+
tool,
|
|
6791
|
+
confidence: "exact",
|
|
6792
|
+
refs: { sourceId: `${callId}:start` }
|
|
6793
|
+
}, line, filePath, options);
|
|
6794
|
+
if (started) events.push(started);
|
|
6795
|
+
}
|
|
6796
|
+
const metrics = workbuddyUsageMetrics(record);
|
|
6797
|
+
const model = stringField(objectField(record, "providerData"), "model");
|
|
6798
|
+
if (metrics || model) {
|
|
6799
|
+
const usage = makeEvent({
|
|
6800
|
+
schemaVersion: AGENT_TIME_SCHEMA_VERSION,
|
|
6801
|
+
ts,
|
|
6802
|
+
type: "model.usage",
|
|
6803
|
+
source: "workbuddy",
|
|
6804
|
+
workspaceId,
|
|
6805
|
+
project,
|
|
6806
|
+
cwd,
|
|
6807
|
+
sessionId: sessionPrefix,
|
|
6808
|
+
turnId: currentTurnId,
|
|
6809
|
+
agent: "workbuddy",
|
|
6810
|
+
model,
|
|
6811
|
+
metrics,
|
|
6812
|
+
confidence: metrics ? "partial" : "derived",
|
|
6813
|
+
refs: { sourceId: `${sourceId}:usage:${callId || line.lineNumber}` }
|
|
6814
|
+
}, line, filePath, options);
|
|
6815
|
+
if (usage) events.push(usage);
|
|
6816
|
+
}
|
|
6817
|
+
continue;
|
|
6818
|
+
}
|
|
6819
|
+
if (type === "function_call_result") {
|
|
6820
|
+
const tool = stringField(record, "name") || "tool";
|
|
6821
|
+
const callId = stringField(record, "callId") || `${line.lineNumber}`;
|
|
6822
|
+
const start = toolStarts.get(`${sessionId}:${callId}`);
|
|
6823
|
+
const durationMs = start?.ts ? Math.max(0, Date.parse(ts) - Date.parse(start.ts)) : void 0;
|
|
6824
|
+
const status = stringField(record, "status");
|
|
6825
|
+
const completed = makeEvent({
|
|
6826
|
+
schemaVersion: AGENT_TIME_SCHEMA_VERSION,
|
|
6827
|
+
ts,
|
|
6828
|
+
type: status === "failed" ? "tool.failed" : "tool.completed",
|
|
6829
|
+
source: "workbuddy",
|
|
6830
|
+
workspaceId,
|
|
6831
|
+
project,
|
|
6832
|
+
cwd,
|
|
6833
|
+
sessionId: sessionPrefix,
|
|
6834
|
+
turnId: currentTurnId,
|
|
6835
|
+
spanId: `${sessionPrefix}:tool:${callId}`,
|
|
6836
|
+
agent: "workbuddy",
|
|
6837
|
+
tool,
|
|
6838
|
+
success: status !== "failed",
|
|
6839
|
+
metrics: { toolDurationMs: durationMs, durationMs },
|
|
6840
|
+
confidence: start ? "derived" : "partial",
|
|
6841
|
+
refs: { sourceId: `${callId}:result` }
|
|
6842
|
+
}, line, filePath, options);
|
|
6843
|
+
if (completed) events.push(completed);
|
|
6844
|
+
}
|
|
6845
|
+
}
|
|
6846
|
+
const lastLine = lines.at(-1);
|
|
6847
|
+
const lastTs = lastLine ? timestampFrom(numberField(lastLine.record, "timestamp")) : void 0;
|
|
6848
|
+
if (lastLine && lastTs) {
|
|
6849
|
+
const event = makeEvent({
|
|
6850
|
+
schemaVersion: AGENT_TIME_SCHEMA_VERSION,
|
|
6851
|
+
ts: lastTs,
|
|
6852
|
+
type: "session.ended",
|
|
6853
|
+
source: "workbuddy",
|
|
6854
|
+
workspaceId,
|
|
6855
|
+
project,
|
|
6856
|
+
cwd,
|
|
6857
|
+
sessionId: sessionPrefix,
|
|
6858
|
+
agent: "workbuddy",
|
|
6859
|
+
confidence: "derived",
|
|
6860
|
+
refs: { sourceId: `${sessionPrefix}:ended` }
|
|
6861
|
+
}, lastLine, filePath, options);
|
|
6862
|
+
if (event) events.push(event);
|
|
6863
|
+
}
|
|
6864
|
+
return events;
|
|
6865
|
+
}
|
|
6866
|
+
async function workbuddyBackfillFiles(sourceRoot, home, env) {
|
|
6867
|
+
const base = sourceRoot || workbuddyProjectsDir(home, env);
|
|
6868
|
+
const files = [];
|
|
6869
|
+
try {
|
|
6870
|
+
const projects = await readdir7(base, { withFileTypes: true });
|
|
6871
|
+
for (const project of projects) {
|
|
6872
|
+
if (!project.isDirectory()) {
|
|
6873
|
+
continue;
|
|
6874
|
+
}
|
|
6875
|
+
const projectDir = path14.join(base, project.name);
|
|
6876
|
+
const entries = await readdir7(projectDir, { withFileTypes: true });
|
|
6877
|
+
for (const entry of entries) {
|
|
6878
|
+
if (entry.isFile() && entry.name.endsWith(".jsonl")) {
|
|
6879
|
+
const filePath = path14.join(projectDir, entry.name);
|
|
6880
|
+
const info = await stat7(filePath);
|
|
6881
|
+
files.push({ path: filePath, modifiedAt: info.mtime.toISOString() });
|
|
6882
|
+
}
|
|
6883
|
+
}
|
|
6884
|
+
}
|
|
6885
|
+
} catch {
|
|
6886
|
+
return [];
|
|
6887
|
+
}
|
|
6888
|
+
return files.sort((a, b) => a.path.localeCompare(b.path));
|
|
6889
|
+
}
|
|
6890
|
+
function createWorkbuddyAdapter() {
|
|
6891
|
+
return {
|
|
6892
|
+
id: "workbuddy",
|
|
6893
|
+
label: "WorkBuddy",
|
|
6894
|
+
agentName: "workbuddy",
|
|
6895
|
+
kind: "agent",
|
|
6896
|
+
detectPath(home, env) {
|
|
6897
|
+
return workbuddyProjectsDir(home, env);
|
|
6898
|
+
},
|
|
6899
|
+
installedPath(home, env) {
|
|
6900
|
+
return workbuddyProjectsDir(home, env);
|
|
6901
|
+
},
|
|
6902
|
+
async isInstalled(home, env) {
|
|
6903
|
+
return pathExists(workbuddyProjectsDir(home, env));
|
|
6904
|
+
},
|
|
6905
|
+
installEntries() {
|
|
6906
|
+
return [];
|
|
6907
|
+
},
|
|
6908
|
+
sourcePaths(home, env) {
|
|
6909
|
+
return [workbuddyProjectsDir(home, env)];
|
|
6910
|
+
},
|
|
6911
|
+
parseSessionFile: parseWorkbuddySessionFile
|
|
6912
|
+
};
|
|
6913
|
+
}
|
|
6914
|
+
|
|
6915
|
+
// src/adapters/zcode.ts
|
|
6916
|
+
import { execFile } from "node:child_process";
|
|
6917
|
+
import { stat as stat8 } from "node:fs/promises";
|
|
6918
|
+
import path15 from "node:path";
|
|
6919
|
+
import { promisify as promisify2 } from "node:util";
|
|
6920
|
+
init_fs();
|
|
6921
|
+
var execFileAsync = promisify2(execFile);
|
|
6922
|
+
function zcodeCliDir(home, env) {
|
|
6923
|
+
const override = env?.ZCODE_CLI_DIR || env?.ZCODE_HOME;
|
|
6924
|
+
if (override && override.trim()) {
|
|
6925
|
+
return path15.resolve(override, override.endsWith("cli") ? "" : "cli");
|
|
6926
|
+
}
|
|
6927
|
+
return path15.join(home, ".zcode", "cli");
|
|
6928
|
+
}
|
|
6929
|
+
function zcodeDbPath(home, env) {
|
|
6930
|
+
return path15.join(zcodeCliDir(home, env), "db", "db.sqlite");
|
|
6931
|
+
}
|
|
6932
|
+
function sourceHash2(filePath) {
|
|
6933
|
+
return `sha256:${createStableHash(filePath)}`;
|
|
6934
|
+
}
|
|
6935
|
+
function projectFromDirectory(directory) {
|
|
6936
|
+
return directory ? path15.basename(directory) || "zcode" : "zcode";
|
|
6937
|
+
}
|
|
6938
|
+
function isoFromMs(value) {
|
|
6939
|
+
return timestampFrom(typeof value === "number" ? value : Number(value));
|
|
6940
|
+
}
|
|
6941
|
+
function makeEvent2(event, row, rowNumber, filePath, options) {
|
|
6942
|
+
const withRefs = withBackfillRefs(event, {
|
|
6943
|
+
filePath,
|
|
6944
|
+
sourcePathHash: sourceHash2(filePath),
|
|
6945
|
+
lineNumber: rowNumber,
|
|
6946
|
+
topType: row.kind,
|
|
6947
|
+
payloadType: stringField(row, "status") || stringField(row, "query_source") || stringField(row, "tool_name"),
|
|
6948
|
+
options
|
|
6949
|
+
});
|
|
6950
|
+
return validateCanonicalEvent(withRefs).valid ? withRefs : void 0;
|
|
6951
|
+
}
|
|
6952
|
+
function refs(values) {
|
|
6953
|
+
const out = {};
|
|
6954
|
+
for (const [key, value] of Object.entries(values)) {
|
|
6955
|
+
if (value !== void 0 && value !== "") {
|
|
6956
|
+
out[key] = String(value);
|
|
6957
|
+
}
|
|
6958
|
+
}
|
|
6959
|
+
return out;
|
|
6960
|
+
}
|
|
6961
|
+
async function sqliteJsonRows(dbPath, sql) {
|
|
6962
|
+
try {
|
|
6963
|
+
const { stdout } = await execFileAsync("sqlite3", ["-readonly", dbPath, sql], {
|
|
6964
|
+
maxBuffer: 32 * 1024 * 1024
|
|
6965
|
+
});
|
|
6966
|
+
return stdout.split(/\r?\n/).map((line) => line.trim()).filter(Boolean).map((line) => {
|
|
6967
|
+
try {
|
|
6968
|
+
const parsed = JSON.parse(line);
|
|
6969
|
+
return isPlainObject(parsed) ? parsed : void 0;
|
|
6970
|
+
} catch {
|
|
6971
|
+
return void 0;
|
|
6972
|
+
}
|
|
6973
|
+
}).filter((row) => Boolean(row));
|
|
6974
|
+
} catch {
|
|
6975
|
+
return [];
|
|
6976
|
+
}
|
|
6977
|
+
}
|
|
6978
|
+
async function readZCodeRows(dbPath) {
|
|
6979
|
+
const queries = [
|
|
6980
|
+
`select json_object(
|
|
6981
|
+
'kind','session',
|
|
6982
|
+
'id',id,
|
|
6983
|
+
'parent_id',parent_id,
|
|
6984
|
+
'directory',directory,
|
|
6985
|
+
'title',title,
|
|
6986
|
+
'task_type',task_type,
|
|
6987
|
+
'time_created',time_created,
|
|
6988
|
+
'time_updated',time_updated
|
|
6989
|
+
) from session order by time_created, id;`,
|
|
6990
|
+
`select json_object(
|
|
6991
|
+
'kind','turn',
|
|
6992
|
+
'session_id',session_id,
|
|
6993
|
+
'turn_id',turn_id,
|
|
6994
|
+
'status',status,
|
|
6995
|
+
'started_at',started_at,
|
|
6996
|
+
'completed_at',completed_at,
|
|
6997
|
+
'duration_ms',duration_ms,
|
|
6998
|
+
'model_request_count',model_request_count,
|
|
6999
|
+
'tool_call_count',tool_call_count,
|
|
7000
|
+
'input_tokens',input_tokens,
|
|
7001
|
+
'output_tokens',output_tokens,
|
|
7002
|
+
'reasoning_tokens',reasoning_tokens,
|
|
7003
|
+
'cache_creation_input_tokens',cache_creation_input_tokens,
|
|
7004
|
+
'cache_read_input_tokens',cache_read_input_tokens,
|
|
7005
|
+
'computed_total_tokens',computed_total_tokens
|
|
7006
|
+
) from turn_usage order by started_at, session_id, turn_id;`,
|
|
7007
|
+
`select json_object(
|
|
7008
|
+
'kind','model',
|
|
7009
|
+
'id',id,
|
|
7010
|
+
'session_id',session_id,
|
|
7011
|
+
'turn_id',turn_id,
|
|
7012
|
+
'query_source',query_source,
|
|
7013
|
+
'provider_id',provider_id,
|
|
7014
|
+
'model_id',model_id,
|
|
7015
|
+
'agent',agent,
|
|
7016
|
+
'mode',mode,
|
|
7017
|
+
'status',status,
|
|
7018
|
+
'started_at',started_at,
|
|
7019
|
+
'completed_at',completed_at,
|
|
7020
|
+
'duration_ms',duration_ms,
|
|
7021
|
+
'input_tokens',input_tokens,
|
|
7022
|
+
'output_tokens',output_tokens,
|
|
7023
|
+
'reasoning_tokens',reasoning_tokens,
|
|
7024
|
+
'cache_creation_input_tokens',cache_creation_input_tokens,
|
|
7025
|
+
'cache_read_input_tokens',cache_read_input_tokens,
|
|
7026
|
+
'computed_total_tokens',computed_total_tokens,
|
|
7027
|
+
'tool_call_count',tool_call_count
|
|
7028
|
+
) from model_usage order by started_at, id;`,
|
|
7029
|
+
`select json_object(
|
|
7030
|
+
'kind','tool',
|
|
7031
|
+
'id',id,
|
|
7032
|
+
'session_id',session_id,
|
|
7033
|
+
'turn_id',turn_id,
|
|
7034
|
+
'tool_call_id',tool_call_id,
|
|
7035
|
+
'tool_name',tool_name,
|
|
7036
|
+
'side_effect_scope',side_effect_scope,
|
|
7037
|
+
'read_only',read_only,
|
|
7038
|
+
'destructive',destructive,
|
|
7039
|
+
'approval_status',approval_status,
|
|
7040
|
+
'status',status,
|
|
7041
|
+
'started_at',started_at,
|
|
7042
|
+
'completed_at',completed_at,
|
|
7043
|
+
'duration_ms',duration_ms,
|
|
7044
|
+
'output_bytes',output_bytes,
|
|
7045
|
+
'exit_code',exit_code
|
|
7046
|
+
) from tool_usage order by started_at, id;`
|
|
7047
|
+
];
|
|
7048
|
+
const results = await Promise.all(queries.map((query) => sqliteJsonRows(dbPath, query)));
|
|
7049
|
+
return results.flat();
|
|
7050
|
+
}
|
|
7051
|
+
function modelMetrics(row) {
|
|
7052
|
+
const input = numberField(row, "input_tokens") || 0;
|
|
7053
|
+
const output = numberField(row, "output_tokens") || 0;
|
|
7054
|
+
const reasoning = numberField(row, "reasoning_tokens") || 0;
|
|
7055
|
+
const cacheCreation = numberField(row, "cache_creation_input_tokens") || 0;
|
|
7056
|
+
const cacheRead = numberField(row, "cache_read_input_tokens") || 0;
|
|
7057
|
+
const total = numberField(row, "computed_total_tokens") || input + output + reasoning;
|
|
7058
|
+
const durationMs = numberField(row, "duration_ms");
|
|
7059
|
+
return {
|
|
7060
|
+
tokensInput: input,
|
|
7061
|
+
tokensOutput: output,
|
|
7062
|
+
tokensReasoningOutput: reasoning,
|
|
7063
|
+
tokensCachedInput: cacheCreation + cacheRead,
|
|
7064
|
+
tokensCacheCreationInput: cacheCreation,
|
|
7065
|
+
tokensCacheReadInput: cacheRead,
|
|
7066
|
+
tokensTotal: total,
|
|
7067
|
+
modelDurationMs: durationMs,
|
|
7068
|
+
durationMs,
|
|
7069
|
+
modelCalls: 1,
|
|
7070
|
+
toolCalls: numberField(row, "tool_call_count")
|
|
7071
|
+
};
|
|
7072
|
+
}
|
|
7073
|
+
function sessionContext(row, sessions) {
|
|
7074
|
+
const sessionId = stringField(row, "session_id") || stringField(row, "id") || "unknown";
|
|
7075
|
+
const session = sessions.get(sessionId);
|
|
7076
|
+
const cwd = stringField(session, "directory");
|
|
7077
|
+
const project = projectFromDirectory(cwd);
|
|
7078
|
+
return {
|
|
7079
|
+
rawSessionId: sessionId,
|
|
7080
|
+
sessionId: `zcode:${sessionId}`,
|
|
7081
|
+
cwd,
|
|
7082
|
+
project,
|
|
7083
|
+
workspaceId: createWorkspaceId({ projectName: project, repoRoot: cwd }),
|
|
7084
|
+
parentSessionId: stringField(session, "parent_id") ? `zcode:${stringField(session, "parent_id")}` : void 0
|
|
7085
|
+
};
|
|
7086
|
+
}
|
|
7087
|
+
async function parseZCodeDb(filePath, options) {
|
|
7088
|
+
const rows = await readZCodeRows(filePath);
|
|
7089
|
+
if (rows.length === 0) {
|
|
7090
|
+
return [];
|
|
7091
|
+
}
|
|
7092
|
+
const sessions = /* @__PURE__ */ new Map();
|
|
7093
|
+
for (const row of rows) {
|
|
7094
|
+
if (row.kind === "session") {
|
|
7095
|
+
const id = stringField(row, "id");
|
|
7096
|
+
if (id) {
|
|
7097
|
+
sessions.set(id, row);
|
|
7098
|
+
}
|
|
7099
|
+
}
|
|
7100
|
+
}
|
|
7101
|
+
const events = [];
|
|
7102
|
+
rows.forEach((row, index) => {
|
|
7103
|
+
const rowNumber = index + 1;
|
|
7104
|
+
if (row.kind === "session") {
|
|
7105
|
+
const id = stringField(row, "id");
|
|
7106
|
+
const ts = isoFromMs(numberField(row, "time_created"));
|
|
7107
|
+
if (!id || !ts) {
|
|
7108
|
+
return;
|
|
7109
|
+
}
|
|
7110
|
+
const ctx = sessionContext(row, sessions);
|
|
7111
|
+
const event = makeEvent2({
|
|
7112
|
+
schemaVersion: AGENT_TIME_SCHEMA_VERSION,
|
|
7113
|
+
ts,
|
|
7114
|
+
type: "session.started",
|
|
7115
|
+
source: "zcode",
|
|
7116
|
+
workspaceId: ctx.workspaceId,
|
|
7117
|
+
project: ctx.project,
|
|
7118
|
+
cwd: ctx.cwd,
|
|
7119
|
+
sessionId: ctx.sessionId,
|
|
7120
|
+
parentAgentInstanceId: ctx.parentSessionId,
|
|
7121
|
+
agent: "zcode",
|
|
7122
|
+
confidence: "exact",
|
|
7123
|
+
refs: refs({
|
|
7124
|
+
sourceId: `${id}:session`,
|
|
7125
|
+
zcodeTaskType: stringField(row, "task_type"),
|
|
7126
|
+
zcodeTitleHash: stringField(row, "title") ? `sha256:${createStableHash(stringField(row, "title"))}` : void 0
|
|
7127
|
+
})
|
|
7128
|
+
}, row, rowNumber, filePath, options);
|
|
7129
|
+
if (event) events.push(event);
|
|
7130
|
+
const completedTs = isoFromMs(numberField(row, "time_updated"));
|
|
7131
|
+
if (completedTs && completedTs !== ts) {
|
|
7132
|
+
const completed = makeEvent2({
|
|
7133
|
+
schemaVersion: AGENT_TIME_SCHEMA_VERSION,
|
|
7134
|
+
ts: completedTs,
|
|
7135
|
+
type: "session.ended",
|
|
7136
|
+
source: "zcode",
|
|
7137
|
+
workspaceId: ctx.workspaceId,
|
|
7138
|
+
project: ctx.project,
|
|
7139
|
+
cwd: ctx.cwd,
|
|
7140
|
+
sessionId: ctx.sessionId,
|
|
7141
|
+
parentAgentInstanceId: ctx.parentSessionId,
|
|
7142
|
+
agent: "zcode",
|
|
7143
|
+
confidence: "exact",
|
|
7144
|
+
refs: { sourceId: `${id}:session:ended` }
|
|
7145
|
+
}, row, rowNumber, filePath, options);
|
|
7146
|
+
if (completed) events.push(completed);
|
|
7147
|
+
}
|
|
7148
|
+
return;
|
|
7149
|
+
}
|
|
7150
|
+
if (row.kind === "turn") {
|
|
7151
|
+
const rawTurnId = stringField(row, "turn_id");
|
|
7152
|
+
const ts = isoFromMs(numberField(row, "started_at"));
|
|
7153
|
+
if (!rawTurnId || !ts) {
|
|
7154
|
+
return;
|
|
7155
|
+
}
|
|
7156
|
+
const ctx = sessionContext(row, sessions);
|
|
7157
|
+
const turnId = `zcode:${rawTurnId}`;
|
|
7158
|
+
const started = makeEvent2({
|
|
7159
|
+
schemaVersion: AGENT_TIME_SCHEMA_VERSION,
|
|
7160
|
+
ts,
|
|
7161
|
+
type: "turn.started",
|
|
7162
|
+
source: "zcode",
|
|
7163
|
+
workspaceId: ctx.workspaceId,
|
|
7164
|
+
project: ctx.project,
|
|
7165
|
+
cwd: ctx.cwd,
|
|
7166
|
+
sessionId: ctx.sessionId,
|
|
7167
|
+
turnId,
|
|
7168
|
+
agent: "zcode",
|
|
7169
|
+
confidence: "exact",
|
|
7170
|
+
refs: { sourceId: `${rawTurnId}:start` }
|
|
7171
|
+
}, row, rowNumber, filePath, options);
|
|
7172
|
+
if (started) events.push(started);
|
|
7173
|
+
const completedTs = isoFromMs(numberField(row, "completed_at"));
|
|
7174
|
+
if (completedTs) {
|
|
7175
|
+
const status = stringField(row, "status");
|
|
7176
|
+
const completed = makeEvent2({
|
|
7177
|
+
schemaVersion: AGENT_TIME_SCHEMA_VERSION,
|
|
7178
|
+
ts: completedTs,
|
|
7179
|
+
type: "turn.completed",
|
|
7180
|
+
source: "zcode",
|
|
7181
|
+
workspaceId: ctx.workspaceId,
|
|
7182
|
+
project: ctx.project,
|
|
7183
|
+
cwd: ctx.cwd,
|
|
7184
|
+
sessionId: ctx.sessionId,
|
|
7185
|
+
turnId,
|
|
7186
|
+
agent: "zcode",
|
|
7187
|
+
success: status === "completed",
|
|
7188
|
+
metrics: {
|
|
7189
|
+
durationMs: numberField(row, "duration_ms"),
|
|
7190
|
+
turns: 1,
|
|
7191
|
+
modelCalls: numberField(row, "model_request_count"),
|
|
7192
|
+
toolCalls: numberField(row, "tool_call_count")
|
|
7193
|
+
},
|
|
7194
|
+
confidence: "exact",
|
|
7195
|
+
refs: refs({ sourceId: `${rawTurnId}:completed`, zcodeStatus: status })
|
|
7196
|
+
}, row, rowNumber, filePath, options);
|
|
7197
|
+
if (completed) events.push(completed);
|
|
7198
|
+
}
|
|
7199
|
+
return;
|
|
7200
|
+
}
|
|
7201
|
+
if (row.kind === "model") {
|
|
7202
|
+
const ts = isoFromMs(numberField(row, "completed_at")) || isoFromMs(numberField(row, "started_at"));
|
|
7203
|
+
if (!ts) {
|
|
7204
|
+
return;
|
|
7205
|
+
}
|
|
7206
|
+
const ctx = sessionContext(row, sessions);
|
|
7207
|
+
const modelId = stringField(row, "model_id");
|
|
7208
|
+
const usage = makeEvent2({
|
|
7209
|
+
schemaVersion: AGENT_TIME_SCHEMA_VERSION,
|
|
7210
|
+
ts,
|
|
7211
|
+
type: "model.usage",
|
|
7212
|
+
source: "zcode",
|
|
7213
|
+
workspaceId: ctx.workspaceId,
|
|
7214
|
+
project: ctx.project,
|
|
7215
|
+
cwd: ctx.cwd,
|
|
7216
|
+
sessionId: ctx.sessionId,
|
|
7217
|
+
turnId: stringField(row, "turn_id") ? `zcode:${stringField(row, "turn_id")}` : void 0,
|
|
7218
|
+
agent: stringField(row, "agent") || "zcode",
|
|
7219
|
+
provider: stringField(row, "provider_id"),
|
|
7220
|
+
model: modelId,
|
|
7221
|
+
success: stringField(row, "status") === "completed",
|
|
7222
|
+
metrics: modelMetrics(row),
|
|
7223
|
+
confidence: "exact",
|
|
7224
|
+
refs: refs({
|
|
7225
|
+
sourceId: stringField(row, "id") || `${ctx.rawSessionId}:model:${rowNumber}`,
|
|
7226
|
+
zcodeQuerySource: stringField(row, "query_source"),
|
|
7227
|
+
zcodeMode: stringField(row, "mode"),
|
|
7228
|
+
zcodeStatus: stringField(row, "status")
|
|
7229
|
+
})
|
|
7230
|
+
}, row, rowNumber, filePath, options);
|
|
7231
|
+
if (usage) events.push(usage);
|
|
7232
|
+
return;
|
|
7233
|
+
}
|
|
7234
|
+
if (row.kind === "tool") {
|
|
7235
|
+
const startedTs = isoFromMs(numberField(row, "started_at"));
|
|
7236
|
+
const completedTs = isoFromMs(numberField(row, "completed_at")) || startedTs;
|
|
7237
|
+
const tool = stringField(row, "tool_name") || "tool";
|
|
7238
|
+
const toolCallId = stringField(row, "tool_call_id") || stringField(row, "id") || `${rowNumber}`;
|
|
7239
|
+
if (!startedTs) {
|
|
7240
|
+
return;
|
|
7241
|
+
}
|
|
7242
|
+
const ctx = sessionContext(row, sessions);
|
|
7243
|
+
const turnId = stringField(row, "turn_id") ? `zcode:${stringField(row, "turn_id")}` : void 0;
|
|
7244
|
+
const spanId = `${ctx.sessionId}:tool:${toolCallId}`;
|
|
7245
|
+
const started = makeEvent2({
|
|
7246
|
+
schemaVersion: AGENT_TIME_SCHEMA_VERSION,
|
|
7247
|
+
ts: startedTs,
|
|
7248
|
+
type: "tool.started",
|
|
7249
|
+
source: "zcode",
|
|
7250
|
+
workspaceId: ctx.workspaceId,
|
|
7251
|
+
project: ctx.project,
|
|
7252
|
+
cwd: ctx.cwd,
|
|
7253
|
+
sessionId: ctx.sessionId,
|
|
7254
|
+
turnId,
|
|
7255
|
+
spanId,
|
|
7256
|
+
agent: "zcode",
|
|
7257
|
+
tool,
|
|
7258
|
+
confidence: "exact",
|
|
7259
|
+
refs: refs({
|
|
7260
|
+
sourceId: `${toolCallId}:start`,
|
|
7261
|
+
zcodeSideEffectScope: stringField(row, "side_effect_scope"),
|
|
7262
|
+
zcodeApprovalStatus: stringField(row, "approval_status")
|
|
7263
|
+
})
|
|
7264
|
+
}, row, rowNumber, filePath, options);
|
|
7265
|
+
if (started) events.push(started);
|
|
7266
|
+
if (completedTs) {
|
|
7267
|
+
const status = stringField(row, "status");
|
|
7268
|
+
const completed = makeEvent2({
|
|
7269
|
+
schemaVersion: AGENT_TIME_SCHEMA_VERSION,
|
|
7270
|
+
ts: completedTs,
|
|
7271
|
+
type: status === "completed" ? "tool.completed" : "tool.failed",
|
|
7272
|
+
source: "zcode",
|
|
7273
|
+
workspaceId: ctx.workspaceId,
|
|
7274
|
+
project: ctx.project,
|
|
7275
|
+
cwd: ctx.cwd,
|
|
7276
|
+
sessionId: ctx.sessionId,
|
|
7277
|
+
turnId,
|
|
7278
|
+
spanId,
|
|
7279
|
+
agent: "zcode",
|
|
7280
|
+
tool,
|
|
7281
|
+
success: status === "completed",
|
|
7282
|
+
metrics: {
|
|
7283
|
+
toolDurationMs: numberField(row, "duration_ms"),
|
|
7284
|
+
durationMs: numberField(row, "duration_ms"),
|
|
7285
|
+
bytesRead: numberField(row, "output_bytes")
|
|
7286
|
+
},
|
|
7287
|
+
confidence: "exact",
|
|
7288
|
+
refs: refs({
|
|
7289
|
+
sourceId: `${toolCallId}:completed`,
|
|
7290
|
+
zcodeStatus: status,
|
|
7291
|
+
zcodeExitCode: numberField(row, "exit_code")
|
|
7292
|
+
})
|
|
7293
|
+
}, row, rowNumber, filePath, options);
|
|
7294
|
+
if (completed) events.push(completed);
|
|
7295
|
+
}
|
|
7296
|
+
}
|
|
7297
|
+
});
|
|
7298
|
+
return events.sort((a, b) => a.ts.localeCompare(b.ts) || (a.id || "").localeCompare(b.id || ""));
|
|
7299
|
+
}
|
|
7300
|
+
async function zcodeBackfillFiles(sourceRoot, home, env) {
|
|
7301
|
+
const candidate = sourceRoot || zcodeDbPath(home, env);
|
|
7302
|
+
const filePath = candidate.endsWith(".sqlite") ? candidate : path15.join(candidate, "db", "db.sqlite");
|
|
7303
|
+
try {
|
|
7304
|
+
const info = await stat8(filePath);
|
|
7305
|
+
return [{ path: filePath, modifiedAt: info.mtime.toISOString() }];
|
|
7306
|
+
} catch {
|
|
7307
|
+
return [];
|
|
7308
|
+
}
|
|
7309
|
+
}
|
|
7310
|
+
function createZCodeAdapter() {
|
|
7311
|
+
return {
|
|
7312
|
+
id: "zcode",
|
|
7313
|
+
label: "ZCode",
|
|
7314
|
+
agentName: "zcode",
|
|
7315
|
+
kind: "agent",
|
|
7316
|
+
detectPath(home, env) {
|
|
7317
|
+
return zcodeCliDir(home, env);
|
|
7318
|
+
},
|
|
7319
|
+
installedPath(home, env) {
|
|
7320
|
+
return zcodeDbPath(home, env);
|
|
7321
|
+
},
|
|
7322
|
+
async isInstalled(home, env) {
|
|
7323
|
+
return pathExists(zcodeDbPath(home, env));
|
|
7324
|
+
},
|
|
7325
|
+
installEntries() {
|
|
7326
|
+
return [];
|
|
7327
|
+
},
|
|
7328
|
+
sourcePaths(home, env) {
|
|
7329
|
+
return [zcodeDbPath(home, env)];
|
|
7330
|
+
},
|
|
7331
|
+
parseSessionFile: parseZCodeDb
|
|
7332
|
+
};
|
|
7333
|
+
}
|
|
7334
|
+
|
|
6583
7335
|
// src/lib/pricing.ts
|
|
6584
7336
|
function estimateEventCostUsd(event) {
|
|
6585
7337
|
if (event.type !== "model.usage") {
|
|
@@ -7016,15 +7768,15 @@ function hookCommandFromGroup(group) {
|
|
|
7016
7768
|
import { randomUUID } from "node:crypto";
|
|
7017
7769
|
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
7018
7770
|
import { homedir, hostname } from "node:os";
|
|
7019
|
-
import
|
|
7771
|
+
import path16 from "node:path";
|
|
7020
7772
|
function configDir(home = homedir()) {
|
|
7021
|
-
return
|
|
7773
|
+
return path16.join(home, ".vibetime");
|
|
7022
7774
|
}
|
|
7023
7775
|
function configPath(home = homedir()) {
|
|
7024
|
-
return
|
|
7776
|
+
return path16.join(configDir(home), "config.json");
|
|
7025
7777
|
}
|
|
7026
7778
|
function machineIdPath(home = homedir()) {
|
|
7027
|
-
return
|
|
7779
|
+
return path16.join(configDir(home), "machine-id");
|
|
7028
7780
|
}
|
|
7029
7781
|
function readConfig(home = homedir()) {
|
|
7030
7782
|
const file = configPath(home);
|
|
@@ -7071,15 +7823,15 @@ function defaultMachineName() {
|
|
|
7071
7823
|
init_fs();
|
|
7072
7824
|
|
|
7073
7825
|
// src/lib/logger.ts
|
|
7074
|
-
import { appendFile, mkdir as mkdir3, rename, stat as
|
|
7826
|
+
import { appendFile, mkdir as mkdir3, rename, stat as stat9 } from "node:fs/promises";
|
|
7075
7827
|
import { homedir as homedir2 } from "node:os";
|
|
7076
|
-
import
|
|
7828
|
+
import path17 from "node:path";
|
|
7077
7829
|
var MAX_BYTES = 1 * 1024 * 1024;
|
|
7078
7830
|
function logDir(home = homedir2()) {
|
|
7079
|
-
return
|
|
7831
|
+
return path17.join(home, ".vibetime", "logs");
|
|
7080
7832
|
}
|
|
7081
7833
|
function logPath(home = homedir2(), name = "cli.log") {
|
|
7082
|
-
return
|
|
7834
|
+
return path17.join(logDir(home), name);
|
|
7083
7835
|
}
|
|
7084
7836
|
function serializeError(error) {
|
|
7085
7837
|
if (error instanceof Error) {
|
|
@@ -7089,7 +7841,7 @@ function serializeError(error) {
|
|
|
7089
7841
|
}
|
|
7090
7842
|
async function rotateIfNeeded(file) {
|
|
7091
7843
|
try {
|
|
7092
|
-
const info = await
|
|
7844
|
+
const info = await stat9(file);
|
|
7093
7845
|
if (info.size > MAX_BYTES) {
|
|
7094
7846
|
await rename(file, `${file}.1`).catch(() => {
|
|
7095
7847
|
});
|
|
@@ -7254,8 +8006,8 @@ function buildHeaders(token, machine) {
|
|
|
7254
8006
|
...machine?.platform ? { "x-machine-platform": machine.platform } : {}
|
|
7255
8007
|
};
|
|
7256
8008
|
}
|
|
7257
|
-
function joinUrl(base,
|
|
7258
|
-
return new URL(
|
|
8009
|
+
function joinUrl(base, path19) {
|
|
8010
|
+
return new URL(path19, base.endsWith("/") ? base : `${base}/`).toString();
|
|
7259
8011
|
}
|
|
7260
8012
|
async function postRollupBatch(remote, rollups, options = {}) {
|
|
7261
8013
|
const response = await remote.fetchImpl(joinUrl(remote.baseUrl, "/v3/agent/ingest"), {
|
|
@@ -7334,6 +8086,8 @@ function createRegistry() {
|
|
|
7334
8086
|
registry.register(createCodebuddyAdapter());
|
|
7335
8087
|
registry.register(createQoderAdapter());
|
|
7336
8088
|
registry.register(createQoderCnAdapter());
|
|
8089
|
+
registry.register(createWorkbuddyAdapter());
|
|
8090
|
+
registry.register(createZCodeAdapter());
|
|
7337
8091
|
return registry;
|
|
7338
8092
|
}
|
|
7339
8093
|
var defaultContext = {
|
|
@@ -7745,11 +8499,17 @@ async function listBackfillSourceFiles(source, options, ctx) {
|
|
|
7745
8499
|
if (source.id === "codebuddy") {
|
|
7746
8500
|
return codebuddyBackfillFiles(stringOption(options["source-root"]), resolveHome2(options, ctx), ctx.env);
|
|
7747
8501
|
}
|
|
8502
|
+
if (source.id === "workbuddy") {
|
|
8503
|
+
return workbuddyBackfillFiles(stringOption(options["source-root"]), resolveHome2(options, ctx), ctx.env);
|
|
8504
|
+
}
|
|
8505
|
+
if (source.id === "zcode") {
|
|
8506
|
+
return zcodeBackfillFiles(stringOption(options["source-root"]), resolveHome2(options, ctx), ctx.env);
|
|
8507
|
+
}
|
|
7748
8508
|
const roots = stringOption(options["source-root"]) ? [requiredOption(options, "source-root")] : source.paths;
|
|
7749
8509
|
const fileLists = await Promise.all(roots.map((r) => listJsonlFiles(r)));
|
|
7750
8510
|
const files = fileLists.flat().sort().slice(0, numberOption(options.limit) || void 0);
|
|
7751
8511
|
return Promise.all(files.map(async (filePath) => {
|
|
7752
|
-
const info = await
|
|
8512
|
+
const info = await stat10(filePath);
|
|
7753
8513
|
return { path: filePath, modifiedAt: info.mtime.toISOString() };
|
|
7754
8514
|
}));
|
|
7755
8515
|
}
|
|
@@ -8058,13 +8818,13 @@ function selectBackfillFilesForImport(files, watermarkTs) {
|
|
|
8058
8818
|
});
|
|
8059
8819
|
}
|
|
8060
8820
|
function backfillIncrementalStatePath(home) {
|
|
8061
|
-
return
|
|
8821
|
+
return path18.join(home, ".vibetime", "backfill-state.json");
|
|
8062
8822
|
}
|
|
8063
8823
|
function syncLocalTriggerStatePath(home) {
|
|
8064
|
-
return
|
|
8824
|
+
return path18.join(home, ".vibetime", "sync-local-trigger.json");
|
|
8065
8825
|
}
|
|
8066
8826
|
function syncLocalTriggerLockPath(home) {
|
|
8067
|
-
return
|
|
8827
|
+
return path18.join(home, ".vibetime", "sync-local-trigger.lock");
|
|
8068
8828
|
}
|
|
8069
8829
|
async function readBackfillIncrementalState(home, ctx) {
|
|
8070
8830
|
const statePath = backfillIncrementalStatePath(home);
|
|
@@ -8104,7 +8864,7 @@ async function updateBackfillIncrementalState(home, state, selectedFilesBySource
|
|
|
8104
8864
|
state.sources[source] = { watermarkTs: latest };
|
|
8105
8865
|
}
|
|
8106
8866
|
const statePath = backfillIncrementalStatePath(home);
|
|
8107
|
-
await mkdir4(
|
|
8867
|
+
await mkdir4(path18.dirname(statePath), { recursive: true });
|
|
8108
8868
|
await writeFile3(statePath, `${JSON.stringify(state, null, 2)}
|
|
8109
8869
|
`, "utf8");
|
|
8110
8870
|
}
|
|
@@ -8137,7 +8897,7 @@ async function readSyncLocalTriggerState(statePath) {
|
|
|
8137
8897
|
return nextState;
|
|
8138
8898
|
}
|
|
8139
8899
|
async function writeSyncLocalTriggerState(statePath, state) {
|
|
8140
|
-
await mkdir4(
|
|
8900
|
+
await mkdir4(path18.dirname(statePath), { recursive: true });
|
|
8141
8901
|
const tmpPath = `${statePath}.tmp`;
|
|
8142
8902
|
await writeFile3(tmpPath, `${JSON.stringify(state, null, 2)}
|
|
8143
8903
|
`, "utf8");
|
|
@@ -8154,7 +8914,7 @@ async function readSyncLocalLock(lockPath) {
|
|
|
8154
8914
|
return { pid: lock.pid, startedAt: lock.startedAt };
|
|
8155
8915
|
}
|
|
8156
8916
|
async function writeSyncLocalLock(lockPath, lock) {
|
|
8157
|
-
await mkdir4(
|
|
8917
|
+
await mkdir4(path18.dirname(lockPath), { recursive: true });
|
|
8158
8918
|
await writeFile3(lockPath, `${JSON.stringify(lock, null, 2)}
|
|
8159
8919
|
`, "utf8");
|
|
8160
8920
|
}
|
|
@@ -8214,10 +8974,10 @@ function syncLocalRunnerEntryArgs(cliPath) {
|
|
|
8214
8974
|
if (cliPath.endsWith(".ts")) {
|
|
8215
8975
|
return ["--import", "tsx", cliPath];
|
|
8216
8976
|
}
|
|
8217
|
-
return [
|
|
8977
|
+
return [path18.resolve(path18.dirname(cliPath), "../bin/vibetime.mjs")];
|
|
8218
8978
|
}
|
|
8219
8979
|
function resolveHome2(options, ctx) {
|
|
8220
|
-
return
|
|
8980
|
+
return path18.resolve(stringOption(options.home) || ctx.env.HOME || os7.homedir());
|
|
8221
8981
|
}
|
|
8222
8982
|
function requestedTargets(options) {
|
|
8223
8983
|
const value = options.target || options.targets;
|
package/package.json
CHANGED