codiedev 0.9.53 → 0.9.55
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/captureSignals.d.ts +19 -0
- package/dist/captureSignals.js +119 -0
- package/dist/hook.js +4 -0
- package/dist/transcriptModels.d.ts +18 -0
- package/dist/transcriptModels.js +207 -0
- package/dist/utils.d.ts +4 -0
- package/dist/utils.js +5 -2
- package/package.json +1 -1
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export declare const CAPTURE_SIGNALS_VERSION = 1;
|
|
2
|
+
export type CaptureSignals = {
|
|
3
|
+
version: 1;
|
|
4
|
+
parsedRecords: number;
|
|
5
|
+
timestampCount: number;
|
|
6
|
+
firstAt?: number;
|
|
7
|
+
lastAt?: number;
|
|
8
|
+
activeMs: number;
|
|
9
|
+
idleThresholdMs: number;
|
|
10
|
+
skillInvocations: string[];
|
|
11
|
+
skillPulls: string[];
|
|
12
|
+
toolCalls: Array<{
|
|
13
|
+
name: string;
|
|
14
|
+
calls: number;
|
|
15
|
+
}>;
|
|
16
|
+
truncated: boolean;
|
|
17
|
+
};
|
|
18
|
+
/** Compact diagnostic inputs only: never persist prompts, commands or tool output. */
|
|
19
|
+
export declare function parseCaptureSignals(content: string, source?: string): CaptureSignals;
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CAPTURE_SIGNALS_VERSION = void 0;
|
|
4
|
+
exports.parseCaptureSignals = parseCaptureSignals;
|
|
5
|
+
const transcriptModels_1 = require("./transcriptModels");
|
|
6
|
+
exports.CAPTURE_SIGNALS_VERSION = 1;
|
|
7
|
+
const IDLE_MS = 5 * 60_000;
|
|
8
|
+
const LIMIT = 100;
|
|
9
|
+
const skillKey = (value) => typeof value === "string"
|
|
10
|
+
? value
|
|
11
|
+
.replace(/^\//, "")
|
|
12
|
+
.replace(/\.md$/i, "")
|
|
13
|
+
.replace(/["'`]/g, "")
|
|
14
|
+
.trim()
|
|
15
|
+
.toLowerCase()
|
|
16
|
+
: "";
|
|
17
|
+
/** Compact diagnostic inputs only: never persist prompts, commands or tool output. */
|
|
18
|
+
function parseCaptureSignals(content, source) {
|
|
19
|
+
const timestamps = new Set();
|
|
20
|
+
const invocations = new Set(), pulls = new Set();
|
|
21
|
+
const tools = new Map(), calls = new Set();
|
|
22
|
+
let parsedRecords = 0, truncated = false;
|
|
23
|
+
const addSkill = (set, value) => {
|
|
24
|
+
const key = skillKey(value);
|
|
25
|
+
if (!key || key.length > 200 || !/^[a-z0-9][a-z0-9_:/.-]*$/i.test(key))
|
|
26
|
+
return;
|
|
27
|
+
if (set.size >= LIMIT && !set.has(key)) {
|
|
28
|
+
truncated = true;
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
set.add(key);
|
|
32
|
+
};
|
|
33
|
+
const command = (value) => {
|
|
34
|
+
if (typeof value !== "string")
|
|
35
|
+
return;
|
|
36
|
+
for (const match of value.matchAll(/\bcodiedev\s+pull\s+([^\s"'`]+)/gi))
|
|
37
|
+
addSkill(pulls, match[1]);
|
|
38
|
+
};
|
|
39
|
+
const tool = (name, id, input) => {
|
|
40
|
+
if (typeof name !== "string" || !name || name.length > 100)
|
|
41
|
+
return;
|
|
42
|
+
const key = typeof id === "string" ? id : `${parsedRecords}:${calls.size}`;
|
|
43
|
+
if (calls.has(key))
|
|
44
|
+
return;
|
|
45
|
+
calls.add(key);
|
|
46
|
+
if (tools.size < LIMIT || tools.has(name))
|
|
47
|
+
tools.set(name, (tools.get(name) ?? 0) + 1);
|
|
48
|
+
else
|
|
49
|
+
truncated = true;
|
|
50
|
+
let args = input;
|
|
51
|
+
if (typeof args === "string") {
|
|
52
|
+
try {
|
|
53
|
+
args = JSON.parse(args);
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
command(args);
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (name.toLowerCase() === "skill")
|
|
61
|
+
addSkill(invocations, args?.skill ?? args?.name);
|
|
62
|
+
command(args?.command ?? args?.cmd);
|
|
63
|
+
};
|
|
64
|
+
for (const record of (0, transcriptModels_1.transcriptRecords)(content, source)) {
|
|
65
|
+
if (!record || typeof record !== "object")
|
|
66
|
+
continue;
|
|
67
|
+
const e = record;
|
|
68
|
+
parsedRecords++;
|
|
69
|
+
const time = typeof e.timestamp === "string"
|
|
70
|
+
? Date.parse(e.timestamp)
|
|
71
|
+
: e.info?.time?.created;
|
|
72
|
+
if (typeof time === "number" && Number.isFinite(time) && time > 0)
|
|
73
|
+
timestamps.add(time);
|
|
74
|
+
const blocks = e.message?.content;
|
|
75
|
+
if (Array.isArray(blocks))
|
|
76
|
+
for (const part of blocks) {
|
|
77
|
+
if (part?.type === "tool_use")
|
|
78
|
+
tool(part.name, part.id, part.input);
|
|
79
|
+
if (part?.type === "text" && e.type === "user") {
|
|
80
|
+
for (const match of String(part.text ?? "").matchAll(/<command-name>\/?([a-z0-9:_-]+)<\/command-name>/gi))
|
|
81
|
+
addSkill(invocations, match[1]);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
if (typeof blocks === "string" && e.type === "user")
|
|
85
|
+
for (const match of blocks.matchAll(/<command-name>\/?([a-z0-9:_-]+)<\/command-name>/gi))
|
|
86
|
+
addSkill(invocations, match[1]);
|
|
87
|
+
if (e.type === "response_item" &&
|
|
88
|
+
["function_call", "custom_tool_call"].includes(e.payload?.type))
|
|
89
|
+
tool(e.payload.name, e.payload.call_id, e.payload.arguments ?? e.payload.input);
|
|
90
|
+
if (e.info?.role === "assistant" && Array.isArray(e.parts))
|
|
91
|
+
for (const part of e.parts) {
|
|
92
|
+
if (part?.type === "tool")
|
|
93
|
+
tool(part.tool, part.callID ?? part.id, part.state?.input);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
const sorted = [...timestamps].sort((a, b) => a - b);
|
|
97
|
+
let activeMs = 0;
|
|
98
|
+
for (let i = 1; i < sorted.length; i++) {
|
|
99
|
+
const gap = sorted[i] - sorted[i - 1];
|
|
100
|
+
if (gap <= IDLE_MS)
|
|
101
|
+
activeMs += gap;
|
|
102
|
+
}
|
|
103
|
+
return {
|
|
104
|
+
version: exports.CAPTURE_SIGNALS_VERSION,
|
|
105
|
+
parsedRecords,
|
|
106
|
+
timestampCount: sorted.length,
|
|
107
|
+
...(sorted.length
|
|
108
|
+
? { firstAt: sorted[0], lastAt: sorted[sorted.length - 1] }
|
|
109
|
+
: {}),
|
|
110
|
+
activeMs,
|
|
111
|
+
idleThresholdMs: IDLE_MS,
|
|
112
|
+
skillInvocations: [...invocations].sort(),
|
|
113
|
+
skillPulls: [...pulls].sort(),
|
|
114
|
+
toolCalls: [...tools]
|
|
115
|
+
.map(([name, calls]) => ({ name, calls }))
|
|
116
|
+
.sort((a, b) => a.name.localeCompare(b.name)),
|
|
117
|
+
truncated,
|
|
118
|
+
};
|
|
119
|
+
}
|
package/dist/hook.js
CHANGED
|
@@ -35,6 +35,8 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
35
35
|
})();
|
|
36
36
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
37
37
|
exports.buildDiscoveryContext = buildDiscoveryContext;
|
|
38
|
+
const captureSignals_1 = require("./captureSignals");
|
|
39
|
+
const transcriptModels_1 = require("./transcriptModels");
|
|
38
40
|
const fs = __importStar(require("fs"));
|
|
39
41
|
const os = __importStar(require("os"));
|
|
40
42
|
const https = __importStar(require("https"));
|
|
@@ -764,6 +766,8 @@ async function runCaptureFinishInner(input, config, session_id, transcript_path,
|
|
|
764
766
|
cacheReadTokens: usage.cacheReadTokens,
|
|
765
767
|
cacheCreationTokens: usage.cacheCreationTokens,
|
|
766
768
|
model: usage.model,
|
|
769
|
+
captureSignals: (0, captureSignals_1.parseCaptureSignals)(mode === "desktop" || mode === "opencode" ? rawTranscriptContent : transcriptContent, mode),
|
|
770
|
+
modelUsage: (0, transcriptModels_1.parseTranscriptModels)(mode === "desktop" || mode === "opencode" ? rawTranscriptContent : transcriptContent, mode),
|
|
767
771
|
sessionStartedAt,
|
|
768
772
|
interruptCount: steering.interrupts,
|
|
769
773
|
rejectionCount: steering.rejections,
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** Compact per-model usage, derived from captured transcript records. */
|
|
2
|
+
export type TranscriptModelUsage = {
|
|
3
|
+
model: string;
|
|
4
|
+
/** Assistant message IDs; nonzero cumulative usage events for Codex. */
|
|
5
|
+
requests: number;
|
|
6
|
+
inputTokens: number;
|
|
7
|
+
outputTokens: number;
|
|
8
|
+
cacheReadTokens: number;
|
|
9
|
+
cacheCreationTokens: number;
|
|
10
|
+
costUsd?: number;
|
|
11
|
+
/** Reported provider figure, retained separately from trusted billing inputs. */
|
|
12
|
+
reportedCostUsd?: number;
|
|
13
|
+
/** Reported reasoning is included in outputTokens, never added again. */
|
|
14
|
+
reasoningTokens?: number;
|
|
15
|
+
};
|
|
16
|
+
export declare function transcriptRecords(content: string, source?: string): Generator<unknown>;
|
|
17
|
+
export declare function parseTranscriptModels(content: string, source?: string): TranscriptModelUsage[];
|
|
18
|
+
export declare function totalModelTokens(row: Omit<TranscriptModelUsage, "model" | "requests">): number;
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.transcriptRecords = transcriptRecords;
|
|
4
|
+
exports.parseTranscriptModels = parseTranscriptModels;
|
|
5
|
+
exports.totalModelTokens = totalModelTokens;
|
|
6
|
+
const positive = (n) => typeof n === "number" && Number.isFinite(n) ? Math.max(0, n) : 0;
|
|
7
|
+
const empty = (model) => ({
|
|
8
|
+
model,
|
|
9
|
+
requests: 0,
|
|
10
|
+
inputTokens: 0,
|
|
11
|
+
outputTokens: 0,
|
|
12
|
+
cacheReadTokens: 0,
|
|
13
|
+
cacheCreationTokens: 0,
|
|
14
|
+
});
|
|
15
|
+
function* transcriptRecords(content, source) {
|
|
16
|
+
if (content.trimStart().startsWith("[") ||
|
|
17
|
+
((source === "opencode" || source === "desktop") &&
|
|
18
|
+
content.trimStart().startsWith("{"))) {
|
|
19
|
+
try {
|
|
20
|
+
const parsed = JSON.parse(content);
|
|
21
|
+
const list = Array.isArray(parsed) ? parsed : parsed?.data;
|
|
22
|
+
if (Array.isArray(list)) {
|
|
23
|
+
yield* list;
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
/* Fall through to JSONL for partial captures. */
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
let start = 0;
|
|
32
|
+
while (start < content.length) {
|
|
33
|
+
const newline = content.indexOf("\n", start);
|
|
34
|
+
const end = newline < 0 ? content.length : newline;
|
|
35
|
+
try {
|
|
36
|
+
yield JSON.parse(content.slice(start, end));
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
/* partial or non-JSON line */
|
|
40
|
+
}
|
|
41
|
+
start = end + 1;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function parseTranscriptModels(content, source) {
|
|
45
|
+
const buckets = new Map();
|
|
46
|
+
const messages = new Map();
|
|
47
|
+
let model = "Unattributed";
|
|
48
|
+
let previous = {
|
|
49
|
+
inputTokens: 0,
|
|
50
|
+
outputTokens: 0,
|
|
51
|
+
cacheReadTokens: 0,
|
|
52
|
+
cacheCreationTokens: 0,
|
|
53
|
+
};
|
|
54
|
+
let previousReasoning = 0;
|
|
55
|
+
let index = 0;
|
|
56
|
+
for (const record of transcriptRecords(content, source)) {
|
|
57
|
+
const event = record;
|
|
58
|
+
index++;
|
|
59
|
+
if (!event || typeof event !== "object")
|
|
60
|
+
continue;
|
|
61
|
+
let usage = null;
|
|
62
|
+
let messageId = String(index);
|
|
63
|
+
if (source === "codex") {
|
|
64
|
+
if (event.type === "turn_context" &&
|
|
65
|
+
typeof event.payload?.model === "string")
|
|
66
|
+
model = event.payload.model;
|
|
67
|
+
if (event.type !== "event_msg" || event.payload?.type !== "token_count")
|
|
68
|
+
continue;
|
|
69
|
+
const raw = event.payload?.info?.total_token_usage;
|
|
70
|
+
if (!raw)
|
|
71
|
+
continue;
|
|
72
|
+
const total = {
|
|
73
|
+
inputTokens: Math.max(0, positive(raw.input_tokens) - positive(raw.cached_input_tokens)),
|
|
74
|
+
outputTokens: positive(raw.output_tokens),
|
|
75
|
+
cacheReadTokens: positive(raw.cached_input_tokens),
|
|
76
|
+
cacheCreationTokens: 0,
|
|
77
|
+
};
|
|
78
|
+
// Codex reasoning_output_tokens is a subset of output_tokens. Cumulative
|
|
79
|
+
// snapshots must be differenced, including across a model switch.
|
|
80
|
+
usage = { ...empty(model), requests: 1 };
|
|
81
|
+
const reset = positive(raw.input_tokens) <
|
|
82
|
+
previous.inputTokens + previous.cacheReadTokens ||
|
|
83
|
+
total.outputTokens < previous.outputTokens;
|
|
84
|
+
for (const key of [
|
|
85
|
+
"inputTokens",
|
|
86
|
+
"outputTokens",
|
|
87
|
+
"cacheReadTokens",
|
|
88
|
+
"cacheCreationTokens",
|
|
89
|
+
])
|
|
90
|
+
usage[key] = reset
|
|
91
|
+
? total[key]
|
|
92
|
+
: Math.max(0, total[key] - previous[key]);
|
|
93
|
+
const reasoning = typeof raw.reasoning_output_tokens === "number" &&
|
|
94
|
+
raw.reasoning_output_tokens >= 0 &&
|
|
95
|
+
raw.reasoning_output_tokens <= total.outputTokens
|
|
96
|
+
? raw.reasoning_output_tokens
|
|
97
|
+
: undefined;
|
|
98
|
+
if (reasoning !== undefined &&
|
|
99
|
+
(reset || previousReasoning !== undefined)) {
|
|
100
|
+
const delta = reset
|
|
101
|
+
? reasoning
|
|
102
|
+
: Math.max(0, reasoning - previousReasoning);
|
|
103
|
+
if (delta <= usage.outputTokens)
|
|
104
|
+
usage.reasoningTokens = delta;
|
|
105
|
+
}
|
|
106
|
+
previousReasoning = reasoning;
|
|
107
|
+
previous = total;
|
|
108
|
+
if (!totalModelTokens(usage))
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
else if (event.info?.role === "assistant") {
|
|
112
|
+
const info = event.info, tokens = info.tokens;
|
|
113
|
+
if (!tokens)
|
|
114
|
+
continue;
|
|
115
|
+
const id = typeof info.modelID === "string" ? info.modelID : "Unattributed";
|
|
116
|
+
model =
|
|
117
|
+
info.providerID && id !== "Unattributed"
|
|
118
|
+
? `${info.providerID}/${id}`
|
|
119
|
+
: id;
|
|
120
|
+
usage = {
|
|
121
|
+
...empty(model),
|
|
122
|
+
requests: 1,
|
|
123
|
+
inputTokens: positive(tokens.input),
|
|
124
|
+
outputTokens: positive(tokens.output) + positive(tokens.reasoning),
|
|
125
|
+
...(typeof tokens.reasoning === "number"
|
|
126
|
+
? { reasoningTokens: positive(tokens.reasoning) }
|
|
127
|
+
: {}),
|
|
128
|
+
cacheReadTokens: positive(tokens.cache?.read),
|
|
129
|
+
cacheCreationTokens: positive(tokens.cache?.write),
|
|
130
|
+
...(typeof info.cost === "number"
|
|
131
|
+
? {
|
|
132
|
+
reportedCostUsd: positive(info.cost),
|
|
133
|
+
...(source === "desktop" ? { costUsd: positive(info.cost) } : {}),
|
|
134
|
+
}
|
|
135
|
+
: {}),
|
|
136
|
+
};
|
|
137
|
+
messageId = info.id ?? messageId;
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
const message = event.message;
|
|
141
|
+
if (!message?.usage)
|
|
142
|
+
continue;
|
|
143
|
+
model =
|
|
144
|
+
typeof message.model === "string" ? message.model : "Unattributed";
|
|
145
|
+
const raw = message.usage;
|
|
146
|
+
usage = {
|
|
147
|
+
...empty(model),
|
|
148
|
+
requests: 1,
|
|
149
|
+
inputTokens: positive(raw.input_tokens),
|
|
150
|
+
outputTokens: positive(raw.output_tokens),
|
|
151
|
+
cacheReadTokens: positive(raw.cache_read_input_tokens),
|
|
152
|
+
cacheCreationTokens: positive(raw.cache_creation_input_tokens),
|
|
153
|
+
};
|
|
154
|
+
messageId = message.id ?? event.uuid ?? messageId;
|
|
155
|
+
}
|
|
156
|
+
// Client placeholders are not model executions.
|
|
157
|
+
if (!usage.model.trim() || usage.model.startsWith("<"))
|
|
158
|
+
continue;
|
|
159
|
+
// Streaming snapshots repeat message ids; retain the latest snapshot once.
|
|
160
|
+
messages.set(messageId, usage);
|
|
161
|
+
}
|
|
162
|
+
const incompleteCosts = new Set();
|
|
163
|
+
const incompleteReasoning = new Set();
|
|
164
|
+
const incompleteReportedCosts = new Set();
|
|
165
|
+
for (const usage of messages.values()) {
|
|
166
|
+
const key = buckets.has(usage.model) || buckets.size < 63
|
|
167
|
+
? usage.model
|
|
168
|
+
: "Unattributed";
|
|
169
|
+
const bucket = buckets.get(key) ?? empty(key);
|
|
170
|
+
for (const field of [
|
|
171
|
+
"requests",
|
|
172
|
+
"inputTokens",
|
|
173
|
+
"outputTokens",
|
|
174
|
+
"cacheReadTokens",
|
|
175
|
+
"cacheCreationTokens",
|
|
176
|
+
])
|
|
177
|
+
bucket[field] += usage[field];
|
|
178
|
+
if (usage.reasoningTokens !== undefined)
|
|
179
|
+
bucket.reasoningTokens =
|
|
180
|
+
(bucket.reasoningTokens ?? 0) + usage.reasoningTokens;
|
|
181
|
+
else
|
|
182
|
+
incompleteReasoning.add(key);
|
|
183
|
+
if (usage.reportedCostUsd !== undefined)
|
|
184
|
+
bucket.reportedCostUsd =
|
|
185
|
+
(bucket.reportedCostUsd ?? 0) + usage.reportedCostUsd;
|
|
186
|
+
else
|
|
187
|
+
incompleteReportedCosts.add(key);
|
|
188
|
+
if (usage.costUsd !== undefined)
|
|
189
|
+
bucket.costUsd = (bucket.costUsd ?? 0) + usage.costUsd;
|
|
190
|
+
else
|
|
191
|
+
incompleteCosts.add(key);
|
|
192
|
+
buckets.set(key, bucket);
|
|
193
|
+
}
|
|
194
|
+
for (const key of incompleteReportedCosts)
|
|
195
|
+
delete buckets.get(key).reportedCostUsd;
|
|
196
|
+
for (const key of incompleteCosts)
|
|
197
|
+
delete buckets.get(key).costUsd;
|
|
198
|
+
for (const key of incompleteReasoning)
|
|
199
|
+
delete buckets.get(key).reasoningTokens;
|
|
200
|
+
return [...buckets.values()];
|
|
201
|
+
}
|
|
202
|
+
function totalModelTokens(row) {
|
|
203
|
+
return (row.inputTokens +
|
|
204
|
+
row.outputTokens +
|
|
205
|
+
row.cacheReadTokens +
|
|
206
|
+
row.cacheCreationTokens);
|
|
207
|
+
}
|
package/dist/utils.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import * as fs from "fs";
|
|
2
|
+
import type { CaptureSignals } from "./captureSignals";
|
|
3
|
+
import type { TranscriptModelUsage } from "./transcriptModels";
|
|
2
4
|
import { spawn as nodeSpawn } from "child_process";
|
|
3
5
|
import type { CaptureLogEntry } from "./captureLog";
|
|
4
6
|
import { codiedevStateDir } from "./configDir";
|
|
@@ -58,6 +60,8 @@ export declare function parseAuthoredShas(transcriptContent: string, repoRoot: s
|
|
|
58
60
|
export declare function getGitBranch(cwd: string): string | null;
|
|
59
61
|
export declare function getGitHeadSha(cwd: string): string | null;
|
|
60
62
|
export interface CapturePayloadInput {
|
|
63
|
+
modelUsage?: TranscriptModelUsage[];
|
|
64
|
+
captureSignals?: CaptureSignals;
|
|
61
65
|
sessionId?: string;
|
|
62
66
|
transcriptBase64: string;
|
|
63
67
|
remoteUrl: string | null;
|
package/dist/utils.js
CHANGED
|
@@ -853,6 +853,8 @@ function buildCapturePayload(input) {
|
|
|
853
853
|
cacheReadTokens: input.cacheReadTokens,
|
|
854
854
|
cacheCreationTokens: input.cacheCreationTokens,
|
|
855
855
|
model: input.model ?? undefined,
|
|
856
|
+
modelUsage: input.modelUsage?.length ? input.modelUsage : undefined,
|
|
857
|
+
captureSignals: input.captureSignals,
|
|
856
858
|
sessionStartedAt: input.sessionStartedAt ?? undefined,
|
|
857
859
|
interruptCount: input.interruptCount,
|
|
858
860
|
rejectionCount: input.rejectionCount,
|
|
@@ -2047,7 +2049,8 @@ function parseCodexUsage(content) {
|
|
|
2047
2049
|
const input = last.input_tokens ?? 0;
|
|
2048
2050
|
return {
|
|
2049
2051
|
inputTokens: Math.max(0, input - cached),
|
|
2050
|
-
|
|
2052
|
+
// Reasoning is included in output_tokens, not additional output.
|
|
2053
|
+
outputTokens: last.output_tokens ?? 0,
|
|
2051
2054
|
// Codex only reports cached reads; creation is structurally zero.
|
|
2052
2055
|
cacheTokens: cached,
|
|
2053
2056
|
cacheReadTokens: cached,
|
|
@@ -2268,7 +2271,7 @@ function parseCodexStats(content) {
|
|
|
2268
2271
|
if (payloadType === "message") {
|
|
2269
2272
|
messageCount++;
|
|
2270
2273
|
}
|
|
2271
|
-
else if (payloadType === "function_call") {
|
|
2274
|
+
else if (payloadType === "function_call" || payloadType === "custom_tool_call") {
|
|
2272
2275
|
toolCallCount++;
|
|
2273
2276
|
}
|
|
2274
2277
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codiedev",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.55",
|
|
4
4
|
"description": "Connect Claude Code, Codex, Cursor, OpenCode, or VS Code Copilot to Codiedev for team session capture and artifact collaboration — and run the governed Codie harness",
|
|
5
5
|
"bin": {
|
|
6
6
|
"codiedev": "dist/cli.js",
|