teamai-cli 0.23.0-beta.6 → 0.23.0-beta.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +168 -12
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -747,7 +747,18 @@ var init_types = __esm({
|
|
|
747
747
|
"instead",
|
|
748
748
|
"don't",
|
|
749
749
|
"that's not",
|
|
750
|
-
"not what"
|
|
750
|
+
"not what",
|
|
751
|
+
// Japanese: "that's wrong" / "not that" / "redo" / "you got it wrong" / "on your own" / "put it back".
|
|
752
|
+
"\u9055\u3046",
|
|
753
|
+
"\u3061\u304C\u3046",
|
|
754
|
+
"\u305D\u3046\u3058\u3083\u306A",
|
|
755
|
+
"\u305D\u3046\u3067\u306F\u306A",
|
|
756
|
+
"\u3084\u308A\u76F4",
|
|
757
|
+
"\u3084\u308A\u306A\u304A\u3057",
|
|
758
|
+
"\u9593\u9055\u3063\u3066",
|
|
759
|
+
"\u9593\u9055\u3048",
|
|
760
|
+
"\u52DD\u624B\u306B",
|
|
761
|
+
"\u623B\u3057\u3066"
|
|
751
762
|
];
|
|
752
763
|
INTERVENTION_SCAN_MAX_BYTES = 50 * 1024 * 1024;
|
|
753
764
|
TRANSCRIPT_INTERRUPT_PREFIX = "[Request interrupted by user";
|
|
@@ -6562,22 +6573,32 @@ async function readLastAssistantOutput(transcriptPath) {
|
|
|
6562
6573
|
}
|
|
6563
6574
|
}
|
|
6564
6575
|
async function scanTranscriptStop(transcriptPath, opts) {
|
|
6576
|
+
if (path20.basename(transcriptPath) === "index.json") {
|
|
6577
|
+
const cb = await scanCodebuddyIndex(transcriptPath, opts?.frictionOnly ?? false);
|
|
6578
|
+
if (cb) return cb;
|
|
6579
|
+
}
|
|
6580
|
+
const initial = await scanJsonlTranscriptOnce(transcriptPath);
|
|
6581
|
+
if (opts?.frictionOnly || !isCodexTool(opts?.tool)) return initial.result;
|
|
6582
|
+
const flushedSnapshot = await waitForCodexUsageFlush(transcriptPath, initial.codexSnapshot);
|
|
6583
|
+
return flushedSnapshot ? { ...initial.result, tokens: flushedSnapshot.tokens, tokenScope: flushedSnapshot.scope } : initial.result;
|
|
6584
|
+
}
|
|
6585
|
+
async function scanJsonlTranscriptOnce(transcriptPath) {
|
|
6565
6586
|
let interrupt = 0;
|
|
6566
6587
|
let toolReject = 0;
|
|
6567
6588
|
let toolError = 0;
|
|
6568
6589
|
let prompts = 0;
|
|
6569
6590
|
const tokens = emptyTokenUsage();
|
|
6591
|
+
let codexSessionSnapshot = null;
|
|
6592
|
+
let codexTranscriptSnapshot = null;
|
|
6570
6593
|
const countedUsageKeys = /* @__PURE__ */ new Set();
|
|
6571
|
-
if (path20.basename(transcriptPath) === "index.json") {
|
|
6572
|
-
const cb = await scanCodebuddyIndex(transcriptPath, opts?.frictionOnly ?? false);
|
|
6573
|
-
if (cb) return cb;
|
|
6574
|
-
}
|
|
6575
6594
|
try {
|
|
6576
6595
|
const stat8 = await fs9.promises.stat(transcriptPath);
|
|
6577
|
-
if (stat8.size === 0)
|
|
6596
|
+
if (stat8.size === 0) {
|
|
6597
|
+
return { result: { interrupt, toolReject, toolError, tokens, prompts }, codexSnapshot: null };
|
|
6598
|
+
}
|
|
6578
6599
|
if (stat8.size > INTERVENTION_SCAN_MAX_BYTES) {
|
|
6579
6600
|
log.warn(`dashboard: transcript too large to scan (${stat8.size} bytes)`);
|
|
6580
|
-
return { interrupt, toolReject, toolError, tokens, prompts };
|
|
6601
|
+
return { result: { interrupt, toolReject, toolError, tokens, prompts }, codexSnapshot: null };
|
|
6581
6602
|
}
|
|
6582
6603
|
const rl = readline2.createInterface({
|
|
6583
6604
|
input: fs9.createReadStream(transcriptPath, { encoding: "utf-8" }),
|
|
@@ -6585,13 +6606,19 @@ async function scanTranscriptStop(transcriptPath, opts) {
|
|
|
6585
6606
|
});
|
|
6586
6607
|
for await (const line of rl) {
|
|
6587
6608
|
const trimmed = line.trim();
|
|
6588
|
-
if (!trimmed || !trimmed.includes('"user"') && !trimmed.includes('"assistant"')) continue;
|
|
6609
|
+
if (!trimmed || !trimmed.includes('"user"') && !trimmed.includes('"assistant"') && !trimmed.includes('"token_usage_record"') && !trimmed.includes('"token_count"')) continue;
|
|
6589
6610
|
let entry;
|
|
6590
6611
|
try {
|
|
6591
6612
|
entry = JSON.parse(trimmed);
|
|
6592
6613
|
} catch {
|
|
6593
6614
|
continue;
|
|
6594
6615
|
}
|
|
6616
|
+
const codexUsage = parseCodexCumulativeUsage(entry);
|
|
6617
|
+
if (codexUsage) {
|
|
6618
|
+
if (codexUsage.scope === "session") codexSessionSnapshot = codexUsage;
|
|
6619
|
+
else codexTranscriptSnapshot = codexUsage;
|
|
6620
|
+
continue;
|
|
6621
|
+
}
|
|
6595
6622
|
if (entry.type === "assistant") {
|
|
6596
6623
|
const usage = entry.message?.usage;
|
|
6597
6624
|
const dedupKey2 = typeof entry.message?.id === "string" ? entry.message.id : typeof entry.requestId === "string" ? entry.requestId : void 0;
|
|
@@ -6638,7 +6665,97 @@ async function scanTranscriptStop(transcriptPath, opts) {
|
|
|
6638
6665
|
} catch (e) {
|
|
6639
6666
|
log.warn(`dashboard: failed to scan transcript: ${e.message}`);
|
|
6640
6667
|
}
|
|
6641
|
-
|
|
6668
|
+
const codexSnapshot = codexSessionSnapshot ?? codexTranscriptSnapshot;
|
|
6669
|
+
const result = {
|
|
6670
|
+
interrupt,
|
|
6671
|
+
toolReject,
|
|
6672
|
+
toolError,
|
|
6673
|
+
tokens: codexSnapshot?.tokens ?? tokens,
|
|
6674
|
+
prompts,
|
|
6675
|
+
...codexSnapshot ? { tokenScope: codexSnapshot.scope } : {}
|
|
6676
|
+
};
|
|
6677
|
+
return { result, codexSnapshot };
|
|
6678
|
+
}
|
|
6679
|
+
function asRecord(value) {
|
|
6680
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
6681
|
+
}
|
|
6682
|
+
function codexUsageToTokenUsage(usage) {
|
|
6683
|
+
const inclusiveInput = toNum(usage.input_tokens);
|
|
6684
|
+
const cacheRead = toNum(usage.cached_input_tokens);
|
|
6685
|
+
const cacheCreation = toNum(usage.cache_write_input_tokens);
|
|
6686
|
+
return {
|
|
6687
|
+
input: Math.max(0, inclusiveInput - cacheRead - cacheCreation),
|
|
6688
|
+
output: toNum(usage.output_tokens),
|
|
6689
|
+
cacheRead,
|
|
6690
|
+
cacheCreation
|
|
6691
|
+
};
|
|
6692
|
+
}
|
|
6693
|
+
function parseCodexCumulativeUsage(entry) {
|
|
6694
|
+
const payload = asRecord(entry.payload);
|
|
6695
|
+
if (entry.type === "token_usage_record") {
|
|
6696
|
+
const usage = asRecord(payload?.thread_token_usage);
|
|
6697
|
+
return usage ? { tokens: codexUsageToTokenUsage(usage), scope: "session" } : null;
|
|
6698
|
+
}
|
|
6699
|
+
if (entry.type === "event_msg" && payload?.type === "token_count") {
|
|
6700
|
+
const usage = asRecord(asRecord(payload.info)?.total_token_usage);
|
|
6701
|
+
return usage ? { tokens: codexUsageToTokenUsage(usage), scope: "transcript" } : null;
|
|
6702
|
+
}
|
|
6703
|
+
return null;
|
|
6704
|
+
}
|
|
6705
|
+
function isCodexTool(tool) {
|
|
6706
|
+
return typeof tool === "string" && tool.toLowerCase().includes("codex");
|
|
6707
|
+
}
|
|
6708
|
+
function codexSnapshotEquals(a, b) {
|
|
6709
|
+
return a !== null && a.scope === b.scope && a.tokens.input === b.tokens.input && a.tokens.output === b.tokens.output && a.tokens.cacheRead === b.tokens.cacheRead && a.tokens.cacheCreation === b.tokens.cacheCreation;
|
|
6710
|
+
}
|
|
6711
|
+
function preferCodexSnapshot(current, observed) {
|
|
6712
|
+
if (current?.scope === "session" && observed.scope === "transcript") return current;
|
|
6713
|
+
return observed;
|
|
6714
|
+
}
|
|
6715
|
+
async function readLatestCodexUsageFromTail(transcriptPath) {
|
|
6716
|
+
try {
|
|
6717
|
+
const stat8 = await fs9.promises.stat(transcriptPath);
|
|
6718
|
+
if (stat8.size === 0) return null;
|
|
6719
|
+
const readSize = Math.min(stat8.size, CODEX_USAGE_TAIL_BYTES);
|
|
6720
|
+
const offset = stat8.size - readSize;
|
|
6721
|
+
const fh = await fs9.promises.open(transcriptPath, "r");
|
|
6722
|
+
try {
|
|
6723
|
+
const buffer = Buffer.alloc(readSize);
|
|
6724
|
+
await fh.read(buffer, 0, readSize, offset);
|
|
6725
|
+
const lines = buffer.toString("utf-8").split("\n");
|
|
6726
|
+
if (offset > 0) lines.shift();
|
|
6727
|
+
let latestSession = null;
|
|
6728
|
+
let latestTranscript = null;
|
|
6729
|
+
for (const line of lines) {
|
|
6730
|
+
if (!line.includes('"token_usage_record"') && !line.includes('"token_count"')) continue;
|
|
6731
|
+
try {
|
|
6732
|
+
const parsed = JSON.parse(line);
|
|
6733
|
+
const snapshot = parseCodexCumulativeUsage(parsed);
|
|
6734
|
+
if (snapshot?.scope === "session") latestSession = snapshot;
|
|
6735
|
+
else if (snapshot) latestTranscript = snapshot;
|
|
6736
|
+
} catch {
|
|
6737
|
+
}
|
|
6738
|
+
}
|
|
6739
|
+
return latestSession ?? latestTranscript;
|
|
6740
|
+
} finally {
|
|
6741
|
+
await fh.close();
|
|
6742
|
+
}
|
|
6743
|
+
} catch {
|
|
6744
|
+
return null;
|
|
6745
|
+
}
|
|
6746
|
+
}
|
|
6747
|
+
async function waitForCodexUsageFlush(transcriptPath, initial) {
|
|
6748
|
+
let latest = initial;
|
|
6749
|
+
for (let attempt = 1; attempt < CODEX_USAGE_MAX_ATTEMPTS; attempt++) {
|
|
6750
|
+
await new Promise((resolve) => setTimeout(resolve, CODEX_USAGE_RETRY_MS));
|
|
6751
|
+
const observed = await readLatestCodexUsageFromTail(transcriptPath);
|
|
6752
|
+
if (!observed) continue;
|
|
6753
|
+
latest = preferCodexSnapshot(latest, observed);
|
|
6754
|
+
if (!codexSnapshotEquals(initial, latest) && (initial !== null || totalTokenCount(latest.tokens) > 0)) {
|
|
6755
|
+
return latest;
|
|
6756
|
+
}
|
|
6757
|
+
}
|
|
6758
|
+
return latest;
|
|
6642
6759
|
}
|
|
6643
6760
|
async function readCodebuddyIndexOnce(transcriptPath) {
|
|
6644
6761
|
try {
|
|
@@ -6834,7 +6951,7 @@ async function parseHookEvent(raw, tool) {
|
|
|
6834
6951
|
if (output) {
|
|
6835
6952
|
event.stoppedOutput = output;
|
|
6836
6953
|
}
|
|
6837
|
-
const scan = await scanTranscriptStop(hookData.transcript_path);
|
|
6954
|
+
const scan = await scanTranscriptStop(hookData.transcript_path, { tool });
|
|
6838
6955
|
if (scan.interrupt > 0 || scan.toolReject > 0 || scan.toolError > 0) {
|
|
6839
6956
|
event.interventions = {
|
|
6840
6957
|
interrupt: scan.interrupt,
|
|
@@ -6844,6 +6961,7 @@ async function parseHookEvent(raw, tool) {
|
|
|
6844
6961
|
}
|
|
6845
6962
|
if (scan.tokens.input > 0 || scan.tokens.output > 0 || scan.tokens.cacheRead > 0 || scan.tokens.cacheCreation > 0) {
|
|
6846
6963
|
event.tokens = scan.tokens;
|
|
6964
|
+
if (scan.tokenScope) event.tokenScope = scan.tokenScope;
|
|
6847
6965
|
}
|
|
6848
6966
|
if (scan.prompts > 0) {
|
|
6849
6967
|
event.prompts = scan.prompts;
|
|
@@ -6984,11 +7102,23 @@ function rebuildSessions(events) {
|
|
|
6984
7102
|
});
|
|
6985
7103
|
return result;
|
|
6986
7104
|
}
|
|
7105
|
+
function setLatestTokenSnapshot(snapshots, key, event) {
|
|
7106
|
+
if (!event.tokens) return;
|
|
7107
|
+
const current = snapshots.get(key);
|
|
7108
|
+
const candidateTime = Date.parse(event.timestamp);
|
|
7109
|
+
const currentTime = current ? Date.parse(current.timestamp) : Number.NaN;
|
|
7110
|
+
if (!current || !Number.isFinite(candidateTime) || !Number.isFinite(currentTime) || candidateTime >= currentTime) {
|
|
7111
|
+
snapshots.set(key, { timestamp: event.timestamp, tokens: event.tokens });
|
|
7112
|
+
}
|
|
7113
|
+
}
|
|
6987
7114
|
function aggregateSessionMetrics(events) {
|
|
6988
7115
|
const map = /* @__PURE__ */ new Map();
|
|
6989
7116
|
const lastStopAt = /* @__PURE__ */ new Map();
|
|
6990
7117
|
const submitCount = /* @__PURE__ */ new Map();
|
|
6991
7118
|
const stopPrompts = /* @__PURE__ */ new Map();
|
|
7119
|
+
const unscopedTokens = /* @__PURE__ */ new Map();
|
|
7120
|
+
const sessionTokens = /* @__PURE__ */ new Map();
|
|
7121
|
+
const transcriptTokens = /* @__PURE__ */ new Map();
|
|
6992
7122
|
for (const event of events) {
|
|
6993
7123
|
let m = map.get(event.sessionId);
|
|
6994
7124
|
if (!m) {
|
|
@@ -7001,7 +7131,18 @@ function aggregateSessionMetrics(events) {
|
|
|
7001
7131
|
m.toolReject = event.interventions.toolReject;
|
|
7002
7132
|
}
|
|
7003
7133
|
if (event.tokens) {
|
|
7004
|
-
|
|
7134
|
+
if (event.tokenScope === "session") {
|
|
7135
|
+
setLatestTokenSnapshot(sessionTokens, event.sessionId, event);
|
|
7136
|
+
} else if (event.tokenScope === "transcript" && event.transcriptPath) {
|
|
7137
|
+
let segments = transcriptTokens.get(event.sessionId);
|
|
7138
|
+
if (!segments) {
|
|
7139
|
+
segments = /* @__PURE__ */ new Map();
|
|
7140
|
+
transcriptTokens.set(event.sessionId, segments);
|
|
7141
|
+
}
|
|
7142
|
+
setLatestTokenSnapshot(segments, event.transcriptPath, event);
|
|
7143
|
+
} else {
|
|
7144
|
+
setLatestTokenSnapshot(unscopedTokens, event.sessionId, event);
|
|
7145
|
+
}
|
|
7005
7146
|
}
|
|
7006
7147
|
if (typeof event.prompts === "number") {
|
|
7007
7148
|
stopPrompts.set(event.sessionId, event.prompts);
|
|
@@ -7020,6 +7161,18 @@ function aggregateSessionMetrics(events) {
|
|
|
7020
7161
|
}
|
|
7021
7162
|
}
|
|
7022
7163
|
for (const [sid, m] of map) {
|
|
7164
|
+
const sessionSnapshot = sessionTokens.get(sid);
|
|
7165
|
+
const segments = transcriptTokens.get(sid);
|
|
7166
|
+
if (sessionSnapshot) {
|
|
7167
|
+
m.tokens = { ...sessionSnapshot.tokens };
|
|
7168
|
+
} else if (segments && segments.size > 0) {
|
|
7169
|
+
let total = emptyTokenUsage();
|
|
7170
|
+
for (const segment of segments.values()) total = addTokenUsage(total, segment.tokens);
|
|
7171
|
+
m.tokens = total;
|
|
7172
|
+
} else {
|
|
7173
|
+
const unscoped = unscopedTokens.get(sid);
|
|
7174
|
+
if (unscoped) m.tokens = { ...unscoped.tokens };
|
|
7175
|
+
}
|
|
7023
7176
|
m.prompts = Math.max(submitCount.get(sid) ?? 0, stopPrompts.get(sid) ?? 0);
|
|
7024
7177
|
}
|
|
7025
7178
|
return map;
|
|
@@ -7062,7 +7215,7 @@ async function dashboardReport(toolArg) {
|
|
|
7062
7215
|
compactEvents().catch(() => {
|
|
7063
7216
|
});
|
|
7064
7217
|
}
|
|
7065
|
-
var TRANSCRIPT_TAIL_BYTES, STOPPED_OUTPUT_MAX_CHARS, CODEBUDDY_USAGE_MAX_ATTEMPTS, CODEBUDDY_USAGE_RETRY_MS, CODEBUDDY_BLOB_MAX_COUNT, CODEBUDDY_REJECT_MARKER;
|
|
7218
|
+
var TRANSCRIPT_TAIL_BYTES, STOPPED_OUTPUT_MAX_CHARS, CODEX_USAGE_TAIL_BYTES, CODEX_USAGE_MAX_ATTEMPTS, CODEX_USAGE_RETRY_MS, CODEBUDDY_USAGE_MAX_ATTEMPTS, CODEBUDDY_USAGE_RETRY_MS, CODEBUDDY_BLOB_MAX_COUNT, CODEBUDDY_REJECT_MARKER;
|
|
7066
7219
|
var init_dashboard_collector = __esm({
|
|
7067
7220
|
"src/dashboard-collector.ts"() {
|
|
7068
7221
|
"use strict";
|
|
@@ -7077,6 +7230,9 @@ var init_dashboard_collector = __esm({
|
|
|
7077
7230
|
init_home();
|
|
7078
7231
|
TRANSCRIPT_TAIL_BYTES = 10240;
|
|
7079
7232
|
STOPPED_OUTPUT_MAX_CHARS = 500;
|
|
7233
|
+
CODEX_USAGE_TAIL_BYTES = 256 * 1024;
|
|
7234
|
+
CODEX_USAGE_MAX_ATTEMPTS = 8;
|
|
7235
|
+
CODEX_USAGE_RETRY_MS = 250;
|
|
7080
7236
|
CODEBUDDY_USAGE_MAX_ATTEMPTS = 8;
|
|
7081
7237
|
CODEBUDDY_USAGE_RETRY_MS = 250;
|
|
7082
7238
|
CODEBUDDY_BLOB_MAX_COUNT = 2e3;
|