@magnusekdahl/parallix 1.3.2 → 1.3.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/lib/agents/mistral-telemetry.js +122 -23
- package/lib/agents/mistral-telemetry.ts +141 -26
- package/lib/agents/mistral.js +1 -1
- package/lib/agents/mistral.ts +1 -1
- package/package.json +1 -1
- package/px.js +8 -4
|
@@ -1,40 +1,139 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.DEFAULT_MISTRAL_LOG_DIR = void 0;
|
|
7
|
+
exports.parseMistralMeta = parseMistralMeta;
|
|
8
|
+
exports.extractMistralTelemetry = extractMistralTelemetry;
|
|
9
|
+
exports.getMistralProviderModel = getMistralProviderModel;
|
|
10
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
11
|
+
const node_os_1 = __importDefault(require("node:os"));
|
|
12
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
2
13
|
/**
|
|
3
|
-
* Mistral (Vibe) Telemetry
|
|
14
|
+
* Mistral (Vibe) Telemetry Parser
|
|
15
|
+
*
|
|
16
|
+
* Mistral Vibe writes structured token-usage data to per-session meta files:
|
|
17
|
+
* ~/.vibe/logs/session/<session_id>/meta.json
|
|
4
18
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
19
|
+
* Each meta.json contains a `stats` object with token counts:
|
|
20
|
+
* - session_prompt_tokens → inputTokens
|
|
21
|
+
* - session_completion_tokens → outputTokens
|
|
22
|
+
* - session_total_llm_tokens → totalTokens
|
|
9
23
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
24
|
+
* This module scans that directory for the most recent meta.json, parses the
|
|
25
|
+
* stats block, and returns a telemetry object mirroring the shape used by
|
|
26
|
+
* codex-telemetry.ts.
|
|
13
27
|
*
|
|
14
|
-
* See task-
|
|
15
|
-
* verification is tracked as follow-up task-1288 ("Verify vibe/mistral
|
|
16
|
-
* telemetry once usage-unblocked"), which names the missing evidence and the
|
|
17
|
-
* reason it could not be collected in the task-1285 environment.
|
|
28
|
+
* See task-1288 for the discovery that confirmed the structured source.
|
|
18
29
|
*/
|
|
19
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
-
exports.extractMistralTelemetry = extractMistralTelemetry;
|
|
21
|
-
exports.getMistralProviderModel = getMistralProviderModel;
|
|
22
30
|
/**
|
|
23
|
-
*
|
|
24
|
-
*
|
|
31
|
+
* The default directory where Vibe writes its session meta files.
|
|
32
|
+
* Overridden by tests via extractMistralTelemetry(basePath).
|
|
33
|
+
*/
|
|
34
|
+
exports.DEFAULT_MISTRAL_LOG_DIR = node_path_1.default.join(node_os_1.default.homedir(), '.vibe', 'logs', 'session');
|
|
35
|
+
/**
|
|
36
|
+
* Parse the `stats` block from a meta.json file into a telemetry object.
|
|
37
|
+
* Returns null when the content yields no usable signal (missing stats,
|
|
38
|
+
* empty object, or non-object stats).
|
|
39
|
+
*/
|
|
40
|
+
function parseMistralMeta(meta) {
|
|
41
|
+
if (!meta || typeof meta !== 'object') {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
const stats = meta.stats;
|
|
45
|
+
if (!stats || typeof stats !== 'object') {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
const s = stats;
|
|
49
|
+
const inputTokens = Number(s.session_prompt_tokens) || 0;
|
|
50
|
+
const outputTokens = Number(s.session_completion_tokens) || 0;
|
|
51
|
+
const totalTokens = Number(s.session_total_llm_tokens) || 0;
|
|
52
|
+
// Return null when there is no usable signal (all zeros).
|
|
53
|
+
// Mirrors the codex-telemetry pattern where honest zeros still indicate
|
|
54
|
+
// a parseable source was found but contained no actual usage.
|
|
55
|
+
if (!inputTokens && !outputTokens && !totalTokens) {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
return {
|
|
59
|
+
inputTokens,
|
|
60
|
+
outputTokens,
|
|
61
|
+
totalTokens,
|
|
62
|
+
contextTokens: Number(s.context_tokens) || 0,
|
|
63
|
+
toolCallsAgreed: Number(s.tool_calls_agreed) || 0,
|
|
64
|
+
toolCallsRejected: Number(s.tool_calls_rejected) || 0,
|
|
65
|
+
toolCallsFailed: Number(s.tool_calls_failed) || 0,
|
|
66
|
+
toolCallsSucceeded: Number(s.tool_calls_succeeded) || 0,
|
|
67
|
+
sessionCost: Number(s.session_cost) || 0,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Scan ~/.vibe/logs/session/ for the most recent session meta.json, parse its
|
|
72
|
+
* stats block, and return a telemetry object.
|
|
25
73
|
*
|
|
26
|
-
* @param
|
|
27
|
-
* @
|
|
74
|
+
* @param result - Legacy launcher result object (ignored; kept for API compat)
|
|
75
|
+
* @param basePath - Override the default session log directory. Used by tests.
|
|
28
76
|
*/
|
|
29
|
-
function extractMistralTelemetry() {
|
|
30
|
-
//
|
|
77
|
+
function extractMistralTelemetry(result, basePath) {
|
|
78
|
+
void result; // legacy param, ignored — telemetry comes from on-disk meta.json
|
|
79
|
+
const scanDir = basePath || exports.DEFAULT_MISTRAL_LOG_DIR;
|
|
80
|
+
// Scan session subdirectories for the newest meta.json.
|
|
81
|
+
// Basenames are session_<YYYYMMDD>_<HHMMSS>_<id>, so alphabetical sort = chronological.
|
|
82
|
+
let sessionDirs;
|
|
83
|
+
try {
|
|
84
|
+
sessionDirs = node_fs_1.default.readdirSync(scanDir);
|
|
85
|
+
}
|
|
86
|
+
catch (_) {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
const dirs = sessionDirs
|
|
90
|
+
.filter((d) => {
|
|
91
|
+
if (!d.startsWith('session_')) {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
const full = node_path_1.default.join(scanDir, d);
|
|
95
|
+
try {
|
|
96
|
+
return node_fs_1.default.statSync(full).isDirectory();
|
|
97
|
+
}
|
|
98
|
+
catch (_) {
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
})
|
|
102
|
+
.sort();
|
|
103
|
+
if (dirs.length === 0) {
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
// Walk newest-first; return the first session that has a parseable meta.json.
|
|
107
|
+
for (let i = dirs.length - 1; i >= 0; i--) {
|
|
108
|
+
const metaPath = node_path_1.default.join(scanDir, dirs[i], 'meta.json');
|
|
109
|
+
if (!node_fs_1.default.existsSync(metaPath)) {
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
let content;
|
|
113
|
+
try {
|
|
114
|
+
content = node_fs_1.default.readFileSync(metaPath, 'utf8');
|
|
115
|
+
}
|
|
116
|
+
catch (_) {
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
let meta;
|
|
120
|
+
try {
|
|
121
|
+
meta = JSON.parse(content);
|
|
122
|
+
}
|
|
123
|
+
catch (_) {
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
const telemetry = parseMistralMeta(meta);
|
|
127
|
+
if (!telemetry) {
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
return { ...telemetry, path: metaPath };
|
|
131
|
+
}
|
|
31
132
|
return null;
|
|
32
133
|
}
|
|
33
134
|
/**
|
|
34
135
|
* Return the provider/model pair for mistral tasks.
|
|
35
136
|
* Used as fallback when telemetry is null.
|
|
36
|
-
*
|
|
37
|
-
* @returns {{provider: string, model: string}}
|
|
38
137
|
*/
|
|
39
138
|
function getMistralProviderModel() {
|
|
40
139
|
return { provider: 'mistral', model: 'mistral' };
|
|
@@ -1,44 +1,159 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import os from 'node:os';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
|
|
1
5
|
/**
|
|
2
|
-
* Mistral (Vibe) Telemetry
|
|
6
|
+
* Mistral (Vibe) Telemetry Parser
|
|
7
|
+
*
|
|
8
|
+
* Mistral Vibe writes structured token-usage data to per-session meta files:
|
|
9
|
+
* ~/.vibe/logs/session/<session_id>/meta.json
|
|
3
10
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
11
|
+
* Each meta.json contains a `stats` object with token counts:
|
|
12
|
+
* - session_prompt_tokens → inputTokens
|
|
13
|
+
* - session_completion_tokens → outputTokens
|
|
14
|
+
* - session_total_llm_tokens → totalTokens
|
|
8
15
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
16
|
+
* This module scans that directory for the most recent meta.json, parses the
|
|
17
|
+
* stats block, and returns a telemetry object mirroring the shape used by
|
|
18
|
+
* codex-telemetry.ts.
|
|
12
19
|
*
|
|
13
|
-
* See task-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
20
|
+
* See task-1288 for the discovery that confirmed the structured source.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* The default directory where Vibe writes its session meta files.
|
|
25
|
+
* Overridden by tests via extractMistralTelemetry(basePath).
|
|
17
26
|
*/
|
|
27
|
+
export const DEFAULT_MISTRAL_LOG_DIR = path.join(os.homedir(), '.vibe', 'logs', 'session');
|
|
28
|
+
|
|
29
|
+
interface TelemetryResult {
|
|
30
|
+
inputTokens: number;
|
|
31
|
+
outputTokens: number;
|
|
32
|
+
totalTokens: number;
|
|
33
|
+
contextTokens: number;
|
|
34
|
+
toolCallsAgreed: number;
|
|
35
|
+
toolCallsRejected: number;
|
|
36
|
+
toolCallsFailed: number;
|
|
37
|
+
toolCallsSucceeded: number;
|
|
38
|
+
sessionCost: number;
|
|
39
|
+
path?: string;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
interface ParseableMeta {
|
|
43
|
+
stats?: Record<string, unknown>;
|
|
44
|
+
[key: string]: unknown;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
interface StatsBlock {
|
|
48
|
+
session_prompt_tokens?: unknown;
|
|
49
|
+
session_completion_tokens?: unknown;
|
|
50
|
+
session_total_llm_tokens?: unknown;
|
|
51
|
+
context_tokens?: unknown;
|
|
52
|
+
tool_calls_agreed?: unknown;
|
|
53
|
+
tool_calls_rejected?: unknown;
|
|
54
|
+
tool_calls_failed?: unknown;
|
|
55
|
+
tool_calls_succeeded?: unknown;
|
|
56
|
+
session_cost?: unknown;
|
|
57
|
+
[key: string]: unknown;
|
|
58
|
+
}
|
|
18
59
|
|
|
19
60
|
/**
|
|
20
|
-
*
|
|
21
|
-
*
|
|
61
|
+
* Parse the `stats` block from a meta.json file into a telemetry object.
|
|
62
|
+
* Returns null when the content yields no usable signal (missing stats,
|
|
63
|
+
* empty object, or non-object stats).
|
|
64
|
+
*/
|
|
65
|
+
export function parseMistralMeta(meta: ParseableMeta | null | undefined): TelemetryResult | null {
|
|
66
|
+
if (!meta || typeof meta !== 'object') {return null;}
|
|
67
|
+
|
|
68
|
+
const stats = meta.stats;
|
|
69
|
+
if (!stats || typeof stats !== 'object') {return null;}
|
|
70
|
+
|
|
71
|
+
const s = stats as StatsBlock;
|
|
72
|
+
const inputTokens = Number(s.session_prompt_tokens) || 0;
|
|
73
|
+
const outputTokens = Number(s.session_completion_tokens) || 0;
|
|
74
|
+
const totalTokens = Number(s.session_total_llm_tokens) || 0;
|
|
75
|
+
|
|
76
|
+
// Return null when there is no usable signal (all zeros).
|
|
77
|
+
// Mirrors the codex-telemetry pattern where honest zeros still indicate
|
|
78
|
+
// a parseable source was found but contained no actual usage.
|
|
79
|
+
if (!inputTokens && !outputTokens && !totalTokens) {return null;}
|
|
80
|
+
|
|
81
|
+
return {
|
|
82
|
+
inputTokens,
|
|
83
|
+
outputTokens,
|
|
84
|
+
totalTokens,
|
|
85
|
+
contextTokens: Number(s.context_tokens) || 0,
|
|
86
|
+
toolCallsAgreed: Number(s.tool_calls_agreed) || 0,
|
|
87
|
+
toolCallsRejected: Number(s.tool_calls_rejected) || 0,
|
|
88
|
+
toolCallsFailed: Number(s.tool_calls_failed) || 0,
|
|
89
|
+
toolCallsSucceeded: Number(s.tool_calls_succeeded) || 0,
|
|
90
|
+
sessionCost: Number(s.session_cost) || 0,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Scan ~/.vibe/logs/session/ for the most recent session meta.json, parse its
|
|
96
|
+
* stats block, and return a telemetry object.
|
|
22
97
|
*
|
|
23
|
-
* @param
|
|
24
|
-
* @
|
|
98
|
+
* @param result - Legacy launcher result object (ignored; kept for API compat)
|
|
99
|
+
* @param basePath - Override the default session log directory. Used by tests.
|
|
25
100
|
*/
|
|
26
|
-
function extractMistralTelemetry() {
|
|
27
|
-
//
|
|
101
|
+
export function extractMistralTelemetry(result: unknown, basePath?: string): TelemetryResult | null {
|
|
102
|
+
void result; // legacy param, ignored — telemetry comes from on-disk meta.json
|
|
103
|
+
|
|
104
|
+
const scanDir = basePath || DEFAULT_MISTRAL_LOG_DIR;
|
|
105
|
+
|
|
106
|
+
// Scan session subdirectories for the newest meta.json.
|
|
107
|
+
// Basenames are session_<YYYYMMDD>_<HHMMSS>_<id>, so alphabetical sort = chronological.
|
|
108
|
+
let sessionDirs: string[];
|
|
109
|
+
try {
|
|
110
|
+
sessionDirs = fs.readdirSync(scanDir);
|
|
111
|
+
} catch (_) {
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const dirs = sessionDirs
|
|
116
|
+
.filter((d: string) => {
|
|
117
|
+
if (!d.startsWith('session_')) {return false;}
|
|
118
|
+
const full = path.join(scanDir, d);
|
|
119
|
+
try { return fs.statSync(full).isDirectory(); } catch (_) { return false; }
|
|
120
|
+
})
|
|
121
|
+
.sort();
|
|
122
|
+
|
|
123
|
+
if (dirs.length === 0) {return null;}
|
|
124
|
+
|
|
125
|
+
// Walk newest-first; return the first session that has a parseable meta.json.
|
|
126
|
+
for (let i = dirs.length - 1; i >= 0; i--) {
|
|
127
|
+
const metaPath = path.join(scanDir, dirs[i], 'meta.json');
|
|
128
|
+
if (!fs.existsSync(metaPath)) {continue;}
|
|
129
|
+
|
|
130
|
+
let content: string;
|
|
131
|
+
try {
|
|
132
|
+
content = fs.readFileSync(metaPath, 'utf8');
|
|
133
|
+
} catch (_) {
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
let meta: ParseableMeta;
|
|
138
|
+
try {
|
|
139
|
+
meta = JSON.parse(content);
|
|
140
|
+
} catch (_) {
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const telemetry = parseMistralMeta(meta);
|
|
145
|
+
if (!telemetry) {continue;}
|
|
146
|
+
|
|
147
|
+
return { ...telemetry, path: metaPath };
|
|
148
|
+
}
|
|
149
|
+
|
|
28
150
|
return null;
|
|
29
151
|
}
|
|
30
152
|
|
|
31
153
|
/**
|
|
32
154
|
* Return the provider/model pair for mistral tasks.
|
|
33
155
|
* Used as fallback when telemetry is null.
|
|
34
|
-
*
|
|
35
|
-
* @returns {{provider: string, model: string}}
|
|
36
156
|
*/
|
|
37
|
-
function getMistralProviderModel() {
|
|
157
|
+
export function getMistralProviderModel(): { provider: string; model: string } {
|
|
38
158
|
return { provider: 'mistral', model: 'mistral' };
|
|
39
159
|
}
|
|
40
|
-
|
|
41
|
-
export {
|
|
42
|
-
extractMistralTelemetry,
|
|
43
|
-
getMistralProviderModel,
|
|
44
|
-
};
|
package/lib/agents/mistral.js
CHANGED
|
@@ -14,7 +14,7 @@ const spawn_tee_js_1 = require("../core/spawn-tee.js");
|
|
|
14
14
|
// Current session ID format in meta.json: UUID like "a3dd3d4d-f97d-d57d-4942-a1f694e3a922"
|
|
15
15
|
// Directory naming uses first 8 chars: session_20260521_162703_a3dd3d4d
|
|
16
16
|
// No stdout marker detected in testing, so we leave this as null.
|
|
17
|
-
// Telemetry: mistral/vibe does not expose token-usage data. See mistral-telemetry.
|
|
17
|
+
// Telemetry: mistral/vibe does not expose token-usage data. See mistral-telemetry.ts
|
|
18
18
|
// for the honest-zero stub. Stats hooks in active.js and review-loop.js call
|
|
19
19
|
// recordStageStats which defaults to '0' for tokens when telemetry is null.
|
|
20
20
|
function extractMistralSessionId(stdout) {
|
package/lib/agents/mistral.ts
CHANGED
|
@@ -22,7 +22,7 @@ interface StartMistralAgentOptions extends MistralInvocationOptions {
|
|
|
22
22
|
// Current session ID format in meta.json: UUID like "a3dd3d4d-f97d-d57d-4942-a1f694e3a922"
|
|
23
23
|
// Directory naming uses first 8 chars: session_20260521_162703_a3dd3d4d
|
|
24
24
|
// No stdout marker detected in testing, so we leave this as null.
|
|
25
|
-
// Telemetry: mistral/vibe does not expose token-usage data. See mistral-telemetry.
|
|
25
|
+
// Telemetry: mistral/vibe does not expose token-usage data. See mistral-telemetry.ts
|
|
26
26
|
// for the honest-zero stub. Stats hooks in active.js and review-loop.js call
|
|
27
27
|
// recordStageStats which defaults to '0' for tokens when telemetry is null.
|
|
28
28
|
|
package/package.json
CHANGED
package/px.js
CHANGED
|
@@ -51,10 +51,11 @@ function resolveRuntimePath() {
|
|
|
51
51
|
if (typeof __filename === 'string' && __filename) {
|
|
52
52
|
return __filename;
|
|
53
53
|
}
|
|
54
|
-
|
|
55
|
-
|
|
54
|
+
const arg1 = typeof process.argv[1] === 'string' ? process.argv[1] : '';
|
|
55
|
+
if (arg1.endsWith('/px.ts') || arg1.endsWith('/px.js')) {
|
|
56
|
+
return arg1;
|
|
56
57
|
}
|
|
57
|
-
return node_path_1.default.resolve(process.cwd(), 'px.
|
|
58
|
+
return node_path_1.default.resolve(process.cwd(), 'px.ts');
|
|
58
59
|
}
|
|
59
60
|
const runtimePath = resolveRuntimePath();
|
|
60
61
|
const _require = (0, node_module_1.createRequire)(runtimePath);
|
|
@@ -259,6 +260,9 @@ async function run(argv = process.argv.slice(2), options = {}) {
|
|
|
259
260
|
process.chdir(previousCwd);
|
|
260
261
|
}
|
|
261
262
|
}
|
|
262
|
-
|
|
263
|
+
const _cjsMain = typeof require !== 'undefined' && require.main === module;
|
|
264
|
+
const _arg1 = typeof process.argv[1] === 'string' && process.argv[1] ? process.argv[1] : undefined;
|
|
265
|
+
const _esmMain = _arg1 && _arg1.endsWith('/px.ts');
|
|
266
|
+
if (_esmMain || _cjsMain) {
|
|
263
267
|
run().then(code => { process.exitCode = code; });
|
|
264
268
|
}
|