groove-dev 0.27.103 → 0.27.104
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/moe-training/client/index.js +1 -0
- package/moe-training/client/step-classifier.js +1 -1
- package/moe-training/client/trajectory-capture.js +16 -7
- package/moe-training/test/client/step-classifier.test.js +42 -0
- package/moe-training/test/client/trajectory-capture.test.js +95 -0
- package/node_modules/@groove-dev/cli/package.json +1 -1
- package/node_modules/@groove-dev/daemon/package.json +1 -1
- package/node_modules/@groove-dev/daemon/src/api.js +19 -3
- package/node_modules/@groove-dev/gui/dist/assets/{index-8gdXdRnq.js → index-oUBAPJv6.js} +15 -15
- package/node_modules/@groove-dev/gui/dist/index.html +1 -1
- package/node_modules/@groove-dev/gui/package.json +1 -1
- package/node_modules/@groove-dev/gui/src/components/settings/ProviderSetupWizard.jsx +28 -2
- package/node_modules/@groove-dev/gui/src/views/settings.jsx +23 -2
- package/node_modules/moe-training/client/index.js +1 -0
- package/node_modules/moe-training/client/step-classifier.js +1 -1
- package/node_modules/moe-training/client/trajectory-capture.js +16 -7
- package/node_modules/moe-training/test/client/step-classifier.test.js +42 -0
- package/node_modules/moe-training/test/client/trajectory-capture.test.js +95 -0
- package/package.json +1 -1
- package/packages/cli/package.json +1 -1
- package/packages/daemon/package.json +1 -1
- package/packages/daemon/src/api.js +19 -3
- package/packages/gui/dist/assets/{index-8gdXdRnq.js → index-oUBAPJv6.js} +15 -15
- package/packages/gui/dist/index.html +1 -1
- package/packages/gui/package.json +1 -1
- package/packages/gui/src/components/settings/ProviderSetupWizard.jsx +28 -2
- package/packages/gui/src/views/settings.jsx +23 -2
|
@@ -8,3 +8,4 @@ export { TransmissionQueue } from './transmission-queue.js';
|
|
|
8
8
|
export { EnvelopeBuilder } from './envelope-builder.js';
|
|
9
9
|
export { StepClassifier } from './step-classifier.js';
|
|
10
10
|
export { getParser } from './parsers/index.js';
|
|
11
|
+
export { DomainTagger } from './domain-tagger.js';
|
|
@@ -38,7 +38,7 @@ export class StepClassifier {
|
|
|
38
38
|
|
|
39
39
|
const content = step.content || '';
|
|
40
40
|
|
|
41
|
-
if ((step.type === 'action' || step.type === 'observation') && ERROR_SIGNAL_RE.test(content)) {
|
|
41
|
+
if ((step.type === 'action' || step.type === 'observation') && step.is_error !== false && ERROR_SIGNAL_RE.test(content)) {
|
|
42
42
|
step.type = 'error';
|
|
43
43
|
}
|
|
44
44
|
|
|
@@ -8,6 +8,7 @@ import { StepClassifier } from './step-classifier.js';
|
|
|
8
8
|
import { EnvelopeBuilder } from './envelope-builder.js';
|
|
9
9
|
import { SessionAttestation } from './session-attestation.js';
|
|
10
10
|
import { TransmissionQueue } from './transmission-queue.js';
|
|
11
|
+
import { DomainTagger } from './domain-tagger.js';
|
|
11
12
|
import {
|
|
12
13
|
CHUNK_TIMEOUT_MS,
|
|
13
14
|
CENTRAL_COMMAND_URL,
|
|
@@ -33,7 +34,7 @@ export class TrajectoryCapture {
|
|
|
33
34
|
this._contexts = new Map();
|
|
34
35
|
}
|
|
35
36
|
|
|
36
|
-
init() {
|
|
37
|
+
async init() {
|
|
37
38
|
if (!ConsentManager.isCaptureEnabled()) {
|
|
38
39
|
this._enabled = false;
|
|
39
40
|
return;
|
|
@@ -43,6 +44,8 @@ export class TrajectoryCapture {
|
|
|
43
44
|
this._attestation = new SessionAttestation(this._centralCommandUrl);
|
|
44
45
|
this._transmissionQueue = new TransmissionQueue(this._centralCommandUrl);
|
|
45
46
|
this._transmissionQueue.start();
|
|
47
|
+
this._domainTagger = new DomainTagger();
|
|
48
|
+
await this._domainTagger.init();
|
|
46
49
|
this._offlineRetryTimer = setInterval(() => {
|
|
47
50
|
this._retryOfflineQueue();
|
|
48
51
|
}, OFFLINE_RETRY_INTERVAL_MS);
|
|
@@ -126,10 +129,6 @@ export class TrajectoryCapture {
|
|
|
126
129
|
if (resolved) ctx.metadata.model_engine = resolved;
|
|
127
130
|
}
|
|
128
131
|
|
|
129
|
-
const tokens = ctx.parser.extractTokens(jsonEvent);
|
|
130
|
-
if (tokens) {
|
|
131
|
-
ctx.totalTokens += (tokens.input || 0) + (tokens.output || 0);
|
|
132
|
-
}
|
|
133
132
|
}
|
|
134
133
|
|
|
135
134
|
onUserMessage(agentId, text) {
|
|
@@ -198,6 +197,7 @@ export class TrajectoryCapture {
|
|
|
198
197
|
...ev,
|
|
199
198
|
};
|
|
200
199
|
|
|
200
|
+
ctx.totalTokens += ev.token_count;
|
|
201
201
|
if (ev.type === 'error') ctx.errorsEncountered++;
|
|
202
202
|
ctx.allSteps.push(step);
|
|
203
203
|
|
|
@@ -254,6 +254,14 @@ export class TrajectoryCapture {
|
|
|
254
254
|
const userInterventions = StepClassifier.countUserInterventions(ctx.allSteps);
|
|
255
255
|
const durationSeconds = Math.round((Date.now() - ctx.startTime) / 1000);
|
|
256
256
|
|
|
257
|
+
if (this._domainTagger) {
|
|
258
|
+
const role = ctx.metadata.agent_role || '';
|
|
259
|
+
const firstPrompt = ctx.allSteps.find((s) => s.type === 'thought')?.content || '';
|
|
260
|
+
const thoughtSteps = ctx.allSteps.filter((s) => s.type === 'thought');
|
|
261
|
+
const routingText = DomainTagger.buildRoutingText(role, firstPrompt, thoughtSteps);
|
|
262
|
+
ctx.metadata.domain_tags = await this._domainTagger.tag(routingText);
|
|
263
|
+
}
|
|
264
|
+
|
|
257
265
|
const { tier, reason: tierReason } = this._computeQualityTier(ctx, status, userInterventions);
|
|
258
266
|
const { eligible, exclusionReason } = this._computeTrainingEligibility(ctx, durationSeconds);
|
|
259
267
|
|
|
@@ -296,8 +304,9 @@ export class TrajectoryCapture {
|
|
|
296
304
|
|
|
297
305
|
_computeQualityTier(ctx, status, userInterventions) {
|
|
298
306
|
const quality = ctx.metadata.session_quality;
|
|
299
|
-
if (quality >= TIER_A_MIN_QUALITY && ctx.errorsEncountered === 0 && userInterventions === 0 && status === 'SUCCESS') {
|
|
300
|
-
|
|
307
|
+
if (quality >= TIER_A_MIN_QUALITY && (ctx.errorsEncountered === 0 || ctx.errorsEncountered <= ctx.errorsRecovered) && userInterventions === 0 && status === 'SUCCESS') {
|
|
308
|
+
const reason = ctx.errorsEncountered > 0 ? 'high_quality_errors_recovered' : 'high_quality_no_errors';
|
|
309
|
+
return { tier: 'TIER_A', reason };
|
|
301
310
|
}
|
|
302
311
|
if (status !== 'SUCCESS') {
|
|
303
312
|
return { tier: 'TIER_C', reason: 'non_success_status' };
|
|
@@ -132,4 +132,46 @@ describe('StepClassifier', () => {
|
|
|
132
132
|
assert.ok(result);
|
|
133
133
|
assert.equal(result.type, 'action');
|
|
134
134
|
});
|
|
135
|
+
|
|
136
|
+
it('preserves observation type when is_error is false despite error keywords', () => {
|
|
137
|
+
const classifier = new StepClassifier();
|
|
138
|
+
const step = { type: 'observation', content: 'Cannot find module foo', is_error: false };
|
|
139
|
+
const result = classifier.onStep(step);
|
|
140
|
+
assert.equal(result.type, 'observation');
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it('preserves observation type when is_error:false and content has TypeError', () => {
|
|
144
|
+
const classifier = new StepClassifier();
|
|
145
|
+
const step = { type: 'observation', content: 'TypeError: something failed', is_error: false };
|
|
146
|
+
const result = classifier.onStep(step);
|
|
147
|
+
assert.equal(result.type, 'observation');
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it('still reclassifies observation to error when is_error is true', () => {
|
|
151
|
+
const classifier = new StepClassifier();
|
|
152
|
+
const step = { type: 'observation', content: 'Command failed with exit code 1', is_error: true };
|
|
153
|
+
const result = classifier.onStep(step);
|
|
154
|
+
assert.equal(result.type, 'error');
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
it('still reclassifies observation to error when is_error is undefined', () => {
|
|
158
|
+
const classifier = new StepClassifier();
|
|
159
|
+
const step = { type: 'observation', content: 'ENOENT: no such file or directory' };
|
|
160
|
+
const result = classifier.onStep(step);
|
|
161
|
+
assert.equal(result.type, 'error');
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it('preserves action type when is_error is false', () => {
|
|
165
|
+
const classifier = new StepClassifier();
|
|
166
|
+
const step = { type: 'action', content: 'Command failed with exit code 1', is_error: false };
|
|
167
|
+
const result = classifier.onStep(step);
|
|
168
|
+
assert.equal(result.type, 'action');
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
it('still reclassifies action to error when is_error is not set', () => {
|
|
172
|
+
const classifier = new StepClassifier();
|
|
173
|
+
const step = { type: 'action', content: 'Command failed with exit code 1' };
|
|
174
|
+
const result = classifier.onStep(step);
|
|
175
|
+
assert.equal(result.type, 'error');
|
|
176
|
+
});
|
|
135
177
|
});
|
|
@@ -292,6 +292,101 @@ describe('TrajectoryCapture — user feedback emission', () => {
|
|
|
292
292
|
});
|
|
293
293
|
});
|
|
294
294
|
|
|
295
|
+
describe('TrajectoryCapture — token counting via _processStep', () => {
|
|
296
|
+
it('accumulates token_count from every step type', () => {
|
|
297
|
+
const tc = makeTc();
|
|
298
|
+
tc._scrubber = { scrub: (s) => s };
|
|
299
|
+
const ctx = makeCtx();
|
|
300
|
+
ctx.totalTokens = 0;
|
|
301
|
+
ctx.stepCount = 0;
|
|
302
|
+
ctx.allSteps = [];
|
|
303
|
+
ctx.builder = { addStep: () => null };
|
|
304
|
+
ctx.classifier = {
|
|
305
|
+
onStep: (s) => s,
|
|
306
|
+
};
|
|
307
|
+
|
|
308
|
+
tc._processStep('agent-1', ctx, { type: 'thought', content: 'thinking about it', token_count: 50 });
|
|
309
|
+
tc._processStep('agent-1', ctx, { type: 'action', content: 'run test', token_count: 30 });
|
|
310
|
+
tc._processStep('agent-1', ctx, { type: 'observation', content: 'test passed', token_count: 100 });
|
|
311
|
+
tc._processStep('agent-1', ctx, { type: 'thought', content: 'next step', token_count: 20 });
|
|
312
|
+
|
|
313
|
+
assert.equal(ctx.totalTokens, 200);
|
|
314
|
+
assert.equal(ctx.stepCount, 4);
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
it('estimates token_count when not provided', () => {
|
|
318
|
+
const tc = makeTc();
|
|
319
|
+
tc._scrubber = { scrub: (s) => s };
|
|
320
|
+
const ctx = makeCtx();
|
|
321
|
+
ctx.totalTokens = 0;
|
|
322
|
+
ctx.stepCount = 0;
|
|
323
|
+
ctx.allSteps = [];
|
|
324
|
+
ctx.builder = { addStep: () => null };
|
|
325
|
+
ctx.classifier = {
|
|
326
|
+
onStep: (s) => s,
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
tc._processStep('agent-1', ctx, { type: 'thought', content: 'a'.repeat(100) });
|
|
330
|
+
assert.equal(ctx.totalTokens, 25);
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
it('does not double-count tokens from onStdoutLine', () => {
|
|
334
|
+
const tc = makeTc();
|
|
335
|
+
tc._scrubber = { scrub: (s) => s };
|
|
336
|
+
tc._enabled = true;
|
|
337
|
+
|
|
338
|
+
const ctx = makeCtx();
|
|
339
|
+
ctx.totalTokens = 0;
|
|
340
|
+
ctx.stepCount = 0;
|
|
341
|
+
ctx.allSteps = [];
|
|
342
|
+
ctx.builder = { addStep: () => null };
|
|
343
|
+
ctx.classifier = {
|
|
344
|
+
onStep: (s) => s,
|
|
345
|
+
};
|
|
346
|
+
ctx.parser = {
|
|
347
|
+
parseEvent: () => ({ type: 'thought', content: 'hello', token_count: 10 }),
|
|
348
|
+
extractModel: () => null,
|
|
349
|
+
};
|
|
350
|
+
tc._contexts.set('agent-x', ctx);
|
|
351
|
+
|
|
352
|
+
tc.onStdoutLine('agent-x', '{"type":"assistant"}');
|
|
353
|
+
assert.equal(ctx.totalTokens, 10);
|
|
354
|
+
});
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
describe('TrajectoryCapture — TIER_A with recovered errors', () => {
|
|
358
|
+
it('TIER_A when all errors are recovered', () => {
|
|
359
|
+
const tc = makeTc();
|
|
360
|
+
const ctx = makeCtx({ quality: 80, errorsEncountered: 2, errorsRecovered: 2 });
|
|
361
|
+
const result = tc._computeQualityTier(ctx, 'SUCCESS', 0);
|
|
362
|
+
assert.equal(result.tier, 'TIER_A');
|
|
363
|
+
assert.equal(result.reason, 'high_quality_errors_recovered');
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
it('TIER_A when errors recovered exceed errors encountered', () => {
|
|
367
|
+
const tc = makeTc();
|
|
368
|
+
const ctx = makeCtx({ quality: 75, errorsEncountered: 1, errorsRecovered: 2 });
|
|
369
|
+
const result = tc._computeQualityTier(ctx, 'SUCCESS', 0);
|
|
370
|
+
assert.equal(result.tier, 'TIER_A');
|
|
371
|
+
assert.equal(result.reason, 'high_quality_errors_recovered');
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
it('not TIER_A when errors exceed recoveries', () => {
|
|
375
|
+
const tc = makeTc();
|
|
376
|
+
const ctx = makeCtx({ quality: 80, errorsEncountered: 3, errorsRecovered: 1 });
|
|
377
|
+
const result = tc._computeQualityTier(ctx, 'SUCCESS', 0);
|
|
378
|
+
assert.notEqual(result.tier, 'TIER_A');
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
it('TIER_A with zero errors still uses original reason', () => {
|
|
382
|
+
const tc = makeTc();
|
|
383
|
+
const ctx = makeCtx({ quality: 80, errorsEncountered: 0 });
|
|
384
|
+
const result = tc._computeQualityTier(ctx, 'SUCCESS', 0);
|
|
385
|
+
assert.equal(result.tier, 'TIER_A');
|
|
386
|
+
assert.equal(result.reason, 'high_quality_no_errors');
|
|
387
|
+
});
|
|
388
|
+
});
|
|
389
|
+
|
|
295
390
|
describe('TrajectoryCapture — _computeQuality', () => {
|
|
296
391
|
it('base score is 50', () => {
|
|
297
392
|
const tc = makeTc();
|
|
@@ -758,9 +758,25 @@ export function createApi(app, daemon) {
|
|
|
758
758
|
|
|
759
759
|
proc.on('close', (code) => {
|
|
760
760
|
clearTimeout(timeout);
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
761
|
+
if (code === 0) {
|
|
762
|
+
let hasKey = false;
|
|
763
|
+
try {
|
|
764
|
+
const authPath = resolve(homedir(), '.codex', 'auth.json');
|
|
765
|
+
if (existsSync(authPath)) {
|
|
766
|
+
const auth = JSON.parse(readFileSync(authPath, 'utf8'));
|
|
767
|
+
const token = auth.OPENAI_API_KEY
|
|
768
|
+
|| (auth.auth_mode === 'chatgpt' && auth.tokens?.id_token)
|
|
769
|
+
|| null;
|
|
770
|
+
if (token) {
|
|
771
|
+
daemon.credentials.setKey('codex', token);
|
|
772
|
+
hasKey = true;
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
} catch { /* auth.json missing or malformed — login still succeeded */ }
|
|
776
|
+
respond({ status: 'authenticated', hasKey });
|
|
777
|
+
} else {
|
|
778
|
+
respond({ status: 'error', error: stderr.slice(-200) || `Login failed (exit ${code})` });
|
|
779
|
+
}
|
|
764
780
|
});
|
|
765
781
|
|
|
766
782
|
proc.on('error', (err) => {
|