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.
Files changed (2) hide show
  1. package/dist/index.js +168 -12
  2. 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) return { interrupt, toolReject, toolError, tokens, prompts };
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
- return { interrupt, toolReject, toolError, tokens, prompts };
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
- m.tokens = { ...event.tokens };
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;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "teamai-cli",
3
- "version": "0.23.0-beta.6",
3
+ "version": "0.23.0-beta.7",
4
4
  "description": "TeamAI — Make Every Team AI Native (skill sync + shared knowledge base, powered by Git)",
5
5
  "type": "module",
6
6
  "bin": {