auxilo-mcp 0.9.11 → 0.9.13
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/README.md +8 -8
- package/bin/auxilo-cli.js +416 -14
- package/lib/installer.js +102 -2
- package/mcp-server.js +8 -8
- package/package.json +2 -1
- package/scripts/extract-local.js +134 -212
- package/scripts/providers/byo-key.js +631 -0
- package/scripts/providers/claude-code.js +477 -0
- package/scripts/providers/codex-cli.js +358 -0
- package/scripts/providers/index.js +316 -0
- package/scripts/providers/provider.interface.js +98 -0
- package/scripts/providers/schemas/extraction-envelope.schema.json +42 -0
- package/scripts/providers/schemas/judge-decisions.schema.json +20 -0
- package/scripts/runner.js +32 -0
package/scripts/extract-local.js
CHANGED
|
@@ -12,7 +12,6 @@
|
|
|
12
12
|
* runner no-ops immediately (runner.js checks it). runner.js also sets it in-process
|
|
13
13
|
* before we're called; we set it on the child explicitly as belt-and-suspenders.
|
|
14
14
|
*/
|
|
15
|
-
const { spawnSync } = require('child_process');
|
|
16
15
|
const fs = require('fs');
|
|
17
16
|
const path = require('path');
|
|
18
17
|
const os = require('os');
|
|
@@ -24,6 +23,8 @@ const {
|
|
|
24
23
|
rankIndexRowsForCandidate,
|
|
25
24
|
estimatePromptTokens,
|
|
26
25
|
} = require('../lib/extraction-index.js');
|
|
26
|
+
const providers = require('./providers/index.js');
|
|
27
|
+
const claudeCodeProvider = require('./providers/claude-code.js');
|
|
27
28
|
|
|
28
29
|
// CI-5 (PUNCH-LIST §30, 2026-07-19): Auxilo is TECHNICAL-ONLY. The learning
|
|
29
30
|
// taxonomy is these six tech categories; `communication` and `content-generation`
|
|
@@ -36,20 +37,36 @@ const PRIVATE_CATEGORIES = [...CATEGORIES, 'non-technical'];
|
|
|
36
37
|
const RETIRED_CATEGORIES = ['communication', 'content-generation'];
|
|
37
38
|
|
|
38
39
|
/**
|
|
39
|
-
* SPEC3 slice A1 gate — score-at-extraction,
|
|
40
|
+
* SPEC3 slice A1 gate — score-at-extraction, ON BY DEFAULT since 0.9.12
|
|
41
|
+
* (CLEAN-LANE-FLIP Phase B). Explicit opt-out only: AUXILO_SCORE_EXTRACTION=0
|
|
42
|
+
* (or 'false') disables scoring; any other value, including unset, scores.
|
|
40
43
|
*
|
|
41
|
-
* ┌─
|
|
42
|
-
* │
|
|
43
|
-
* │
|
|
44
|
-
* │
|
|
45
|
-
* │
|
|
46
|
-
* │
|
|
47
|
-
* │
|
|
48
|
-
* │
|
|
49
|
-
*
|
|
44
|
+
* ┌─ SEQUENCING CONSTRAINT — SATISFIED (SPEC3-BUILDER-REVIEW-LOOP §3.1/§8) ──┐
|
|
45
|
+
* │ This gate shipped dark (opt-in via AUXILO_SCORE_EXTRACTION=1) because │
|
|
46
|
+
* │ under a server WITHOUT the B1 extraction-channel hold, a clean /learn │
|
|
47
|
+
* │ submission carrying a floor-passing quality_self_assessment published │
|
|
48
|
+
* │ IMMEDIATELY (seamlessEligible) — arming scoring would have silently │
|
|
49
|
+
* │ flipped hook extraction from "everything held" to "clean items │
|
|
50
|
+
* │ auto-publish", an unrecorded consent change (2026-06-10 class). │
|
|
51
|
+
* │ │
|
|
52
|
+
* │ B1 SHIPPED (lib/clean-lane.js; server.js /learn): every submission │
|
|
53
|
+
* │ carrying submission_channel:'extraction' (runner.js stamps it │
|
|
54
|
+
* │ unconditionally) is HELD with reason 'standing_consent_off' unless the │
|
|
55
|
+
* │ account holds an active standing-consent grant — regardless of the │
|
|
56
|
+
* │ score. The server brake, not the absence of a score, is what keeps │
|
|
57
|
+
* │ unconsented items out of the catalog, so default-on scoring is safe: │
|
|
58
|
+
* │ the score can only move a held item into the ready_to_publish lane (one │
|
|
59
|
+
* │ counted approve away) or, under an active grant, into the clean lane the │
|
|
60
|
+
* │ contributor explicitly opted into. Without a score every extraction │
|
|
61
|
+
* │ submission held awaiting_quality forever, so a stock install could never │
|
|
62
|
+
* │ enter the clean lane at all — that was the Phase B defect this closes. │
|
|
63
|
+
* └──────────────────────────────────────────────────────────────────────────┘
|
|
50
64
|
*/
|
|
51
65
|
function scoreExtractionEnabled(env = process.env) {
|
|
52
|
-
|
|
66
|
+
const raw = env.AUXILO_SCORE_EXTRACTION;
|
|
67
|
+
if (raw === undefined || raw === null) return true;
|
|
68
|
+
const v = String(raw).trim().toLowerCase();
|
|
69
|
+
return !(v === '0' || v === 'false');
|
|
53
70
|
}
|
|
54
71
|
|
|
55
72
|
const EXTRACTION_PROMPT_BASE = `You are extracting reusable OPERATIONAL LEARNINGS from an AI agent's session transcript, to publish to a PUBLIC knowledge marketplace read by other AI agents.
|
|
@@ -132,158 +149,10 @@ function buildExtractionPrompt(opts = {}) {
|
|
|
132
149
|
/** Back-compat export: the default (gate-evaluated-at-call) prompt. */
|
|
133
150
|
const EXTRACTION_PROMPT = buildExtractionPrompt({ scoreExtraction: false });
|
|
134
151
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
const candidates = [
|
|
140
|
-
path.join(homeDir, '.claude', 'local', 'claude'),
|
|
141
|
-
'/usr/local/bin/claude',
|
|
142
|
-
'/opt/homebrew/bin/claude',
|
|
143
|
-
path.join(homeDir, '.local', 'bin', 'claude'),
|
|
144
|
-
];
|
|
145
|
-
for (const c of candidates) {
|
|
146
|
-
try {
|
|
147
|
-
if (existsSync(c)) return c;
|
|
148
|
-
} catch (_) { /* ignore */ }
|
|
149
|
-
}
|
|
150
|
-
// Absolute launchd fallbacks are absent; let PATH resolve the final option.
|
|
151
|
-
return 'claude';
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
/** Build the subscription-auth-only environment shared by Claude CLI children. */
|
|
155
|
-
function claudeChildEnv() {
|
|
156
|
-
const childEnv = { ...process.env, AUXILO_EXTRACTING: '1' };
|
|
157
|
-
delete childEnv.ANTHROPIC_API_KEY;
|
|
158
|
-
return childEnv;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
/**
|
|
162
|
-
* Ask Claude Code for its authoritative local auth state. Only the boolean
|
|
163
|
-
* `loggedIn` field is classified; every other outcome is unknown so callers
|
|
164
|
-
* can fall through to the real model invocation as the classifier of record.
|
|
165
|
-
*/
|
|
166
|
-
function checkClaudeAuthStatus(opts = {}) {
|
|
167
|
-
const spawnSyncImpl = typeof opts.spawnSyncImpl === 'function'
|
|
168
|
-
? opts.spawnSyncImpl
|
|
169
|
-
: spawnSync;
|
|
170
|
-
const bin = typeof opts.claudeBin === 'string'
|
|
171
|
-
? opts.claudeBin
|
|
172
|
-
: resolveClaudeBin();
|
|
173
|
-
let res;
|
|
174
|
-
try {
|
|
175
|
-
res = spawnSyncImpl(bin, ['auth', 'status'], {
|
|
176
|
-
encoding: 'utf-8',
|
|
177
|
-
env: claudeChildEnv(),
|
|
178
|
-
timeout: 5000,
|
|
179
|
-
maxBuffer: 1024 * 1024,
|
|
180
|
-
});
|
|
181
|
-
} catch {
|
|
182
|
-
return 'unknown';
|
|
183
|
-
}
|
|
184
|
-
if (!res || res.error || res.status !== 0) return 'unknown';
|
|
185
|
-
try {
|
|
186
|
-
const status = JSON.parse(String(res.stdout || ''));
|
|
187
|
-
if (!status || typeof status.loggedIn !== 'boolean') return 'unknown';
|
|
188
|
-
return status.loggedIn ? 'logged-in' : 'logged-out';
|
|
189
|
-
} catch {
|
|
190
|
-
return 'unknown';
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
/**
|
|
195
|
-
* Invoke the local Claude Code model headlessly (uses the USER's subscription auth).
|
|
196
|
-
* Prompt+transcript go via stdin. Returns { ok, out, reason } plus auth/cause
|
|
197
|
-
* metadata — never throws, so the SessionEnd hook degrades gracefully (the
|
|
198
|
-
* proactive auxilo_contribute path is the reliable primary; this deterministic
|
|
199
|
-
* hook is best-effort).
|
|
200
|
-
*/
|
|
201
|
-
function extractWithClaudeCode(transcript, opts = {}) {
|
|
202
|
-
const spawnSyncImpl = typeof opts.spawnSyncImpl === 'function'
|
|
203
|
-
? opts.spawnSyncImpl
|
|
204
|
-
: spawnSync;
|
|
205
|
-
const bin = typeof opts.claudeBin === 'string'
|
|
206
|
-
? opts.claudeBin
|
|
207
|
-
: resolveClaudeBin();
|
|
208
|
-
const authStatus = checkClaudeAuthStatus({ spawnSyncImpl, claudeBin: bin });
|
|
209
|
-
if (authStatus === 'logged-out') {
|
|
210
|
-
return {
|
|
211
|
-
ok: false,
|
|
212
|
-
out: '',
|
|
213
|
-
reason: 'local model not authenticated in this context (run `claude auth login` once); skipping deterministic extraction',
|
|
214
|
-
reasonCode: 'cli-unauthenticated',
|
|
215
|
-
authStatus,
|
|
216
|
-
};
|
|
217
|
-
}
|
|
218
|
-
const prompt = typeof opts.prompt === 'string'
|
|
219
|
-
? opts.prompt
|
|
220
|
-
: buildExtractionPrompt({
|
|
221
|
-
previousLessonsSection: opts.previousLessonsSection,
|
|
222
|
-
captureVisibility: opts.captureVisibility,
|
|
223
|
-
...(opts.scoreExtraction !== undefined && { scoreExtraction: opts.scoreExtraction }),
|
|
224
|
-
});
|
|
225
|
-
const input = prompt + String(transcript).slice(0, 200000);
|
|
226
|
-
// Do NOT pass ANTHROPIC_API_KEY through — we want the user's logged-in Claude
|
|
227
|
-
// subscription (OAuth), not an API key (which would bill someone). Delete it.
|
|
228
|
-
let res;
|
|
229
|
-
try {
|
|
230
|
-
res = spawnSyncImpl(bin, ['-p'], {
|
|
231
|
-
input,
|
|
232
|
-
encoding: 'utf-8',
|
|
233
|
-
env: claudeChildEnv(),
|
|
234
|
-
timeout: 120000,
|
|
235
|
-
maxBuffer: 20 * 1024 * 1024,
|
|
236
|
-
});
|
|
237
|
-
} catch (error) {
|
|
238
|
-
return {
|
|
239
|
-
ok: false,
|
|
240
|
-
out: '',
|
|
241
|
-
reason: `spawn failed (${bin}): ${error.message}`,
|
|
242
|
-
reasonCode: 'unknown',
|
|
243
|
-
authStatus,
|
|
244
|
-
};
|
|
245
|
-
}
|
|
246
|
-
if (!res) {
|
|
247
|
-
return {
|
|
248
|
-
ok: false,
|
|
249
|
-
out: '',
|
|
250
|
-
reason: `spawn failed (${bin}): no process result`,
|
|
251
|
-
reasonCode: 'unknown',
|
|
252
|
-
authStatus,
|
|
253
|
-
};
|
|
254
|
-
}
|
|
255
|
-
const out = String(res.stdout || '');
|
|
256
|
-
if (res.error) {
|
|
257
|
-
return {
|
|
258
|
-
ok: false,
|
|
259
|
-
out: '',
|
|
260
|
-
reason: `spawn failed (${bin}): ${res.error.message}`,
|
|
261
|
-
reasonCode: 'unknown',
|
|
262
|
-
authStatus,
|
|
263
|
-
};
|
|
264
|
-
}
|
|
265
|
-
// Claude prints auth failures ("API Error: 401 ... Please run /login") to stdout.
|
|
266
|
-
if (/Please run \/login|authentication_error|401/i.test(out) || /Please run \/login|authentication_error/i.test(String(res.stderr || ''))) {
|
|
267
|
-
return {
|
|
268
|
-
ok: false,
|
|
269
|
-
out,
|
|
270
|
-
reason: 'local model not authenticated in this context (run `claude auth login` once); skipping deterministic extraction',
|
|
271
|
-
reasonCode: 'cli-unauthenticated',
|
|
272
|
-
authStatus,
|
|
273
|
-
...(authStatus === 'logged-in' && { authDiscrepancy: true }),
|
|
274
|
-
};
|
|
275
|
-
}
|
|
276
|
-
if (res.status !== 0) {
|
|
277
|
-
return {
|
|
278
|
-
ok: false,
|
|
279
|
-
out,
|
|
280
|
-
reason: `local model exited ${res.status}: ${(out || String(res.stderr || '')).slice(0, 160)}`,
|
|
281
|
-
reasonCode: 'model-error',
|
|
282
|
-
authStatus,
|
|
283
|
-
};
|
|
284
|
-
}
|
|
285
|
-
return { ok: true, out, reason: null, authStatus };
|
|
286
|
-
}
|
|
152
|
+
// resolveClaudeBin, claudeChildEnv, checkClaudeAuthStatus, extractWithClaudeCode
|
|
153
|
+
// MOVED to scripts/providers/claude-code.js (EXTRACT-PER-CLIENT W1 PART A).
|
|
154
|
+
// Re-exported below (module.exports) from claudeCodeProvider — required because
|
|
155
|
+
// test/ext-0806b-silent-skip.test.js imports them directly from this module.
|
|
287
156
|
|
|
288
157
|
/**
|
|
289
158
|
* A1: validate a model-emitted quality_self_assessment. Returns the normalized
|
|
@@ -507,59 +376,97 @@ ${JSON.stringify(payload)}`;
|
|
|
507
376
|
return { prompt, rankings };
|
|
508
377
|
}
|
|
509
378
|
|
|
379
|
+
// invokeJudgeWithClaudeCode MOVED into scripts/providers/claude-code.js's
|
|
380
|
+
// runModel(mode:'judge') (EXTRACT-PER-CLIENT W1 PART A). Not re-exported here —
|
|
381
|
+
// grep confirmed no test imports it directly from this module (no dead surface
|
|
382
|
+
// carried).
|
|
383
|
+
//
|
|
384
|
+
// judgeUsage's Claude/Anthropic-specific summing (input_tokens +
|
|
385
|
+
// cache_creation_input_tokens + cache_read_input_tokens) moved with it, into
|
|
386
|
+
// claude-code.js's normalizeJudgeUsage() — that's the provider-specific half.
|
|
387
|
+
// This slimmer, provider-agnostic remainder is what's left once a provider's
|
|
388
|
+
// runModel already returns usage normalized to {input_tokens, output_tokens}
|
|
389
|
+
// (or null): apply the SAME text-length-estimate fallback the original judgeUsage
|
|
390
|
+
// used, generically, for whichever provider ran.
|
|
510
391
|
function judgeUsage(usage, prompt, completion) {
|
|
511
392
|
const numeric = usage && typeof usage === 'object' ? usage : {};
|
|
512
393
|
const directInput = Number(numeric.input_tokens) || 0;
|
|
513
|
-
const cacheCreation = Number(numeric.cache_creation_input_tokens) || 0;
|
|
514
|
-
const cacheRead = Number(numeric.cache_read_input_tokens) || 0;
|
|
515
394
|
const output = Number(numeric.output_tokens) || 0;
|
|
516
395
|
return {
|
|
517
|
-
prompt_tokens: directInput
|
|
518
|
-
estimatePromptTokens(prompt),
|
|
396
|
+
prompt_tokens: directInput || estimatePromptTokens(prompt),
|
|
519
397
|
completion_tokens: output || estimatePromptTokens(completion),
|
|
520
398
|
};
|
|
521
399
|
}
|
|
522
400
|
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
);
|
|
538
|
-
const stdout = String(res.stdout || '');
|
|
539
|
-
if (res.error) {
|
|
540
|
-
return { ok: false, out: '', reason: `judge spawn failed (${bin}): ${res.error.message}` };
|
|
541
|
-
}
|
|
542
|
-
if (/Please run \/login|authentication_error|401/i.test(stdout) ||
|
|
543
|
-
/Please run \/login|authentication_error/i.test(String(res.stderr || ''))) {
|
|
544
|
-
return { ok: false, out: '', reason: 'local judge model is not authenticated' };
|
|
545
|
-
}
|
|
546
|
-
if (res.status !== 0) {
|
|
547
|
-
return {
|
|
548
|
-
ok: false,
|
|
549
|
-
out: '',
|
|
550
|
-
reason: `local judge exited ${res.status}: ${(stdout || String(res.stderr || '')).slice(0, 160)}`,
|
|
551
|
-
};
|
|
401
|
+
/**
|
|
402
|
+
* PART C — resolve the extraction_model identity for a runModel result.
|
|
403
|
+
* Prefers the additive `identity` field a provider's runModel result may
|
|
404
|
+
* carry (byo-key.js always sets one: {provider:'byo-key', model, version,
|
|
405
|
+
* vendor}). claude-code.js/codex-cli.js shipped (PART A/B) before this stamp
|
|
406
|
+
* existed and don't set one — rather than touch those provider modules
|
|
407
|
+
* (outside this part's disjoint file scope, AGENTS.md's one-build rule),
|
|
408
|
+
* this falls back to the resolved provider id alone (model/version/vendor
|
|
409
|
+
* null) so every provider gets SOME stamp, never silently none. Best-effort:
|
|
410
|
+
* a resolution failure here must never block extraction itself.
|
|
411
|
+
*/
|
|
412
|
+
async function resolveExtractionModelIdentity(runModelResult, opts) {
|
|
413
|
+
if (runModelResult && runModelResult.identity && typeof runModelResult.identity === 'object') {
|
|
414
|
+
return runModelResult.identity;
|
|
552
415
|
}
|
|
553
|
-
let wrapper;
|
|
554
416
|
try {
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
417
|
+
const resolved = await providers.resolveProvider(opts);
|
|
418
|
+
if (resolved && resolved.ok && resolved.id) {
|
|
419
|
+
return { provider: resolved.id, model: null, version: null, vendor: null };
|
|
420
|
+
}
|
|
421
|
+
} catch { /* identity is best-effort; never block extraction on it */ }
|
|
422
|
+
return null;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
/**
|
|
426
|
+
* Default invokeModel (extractLocally) — routes through the resolved provider's
|
|
427
|
+
* runModel(mode:'extract'), mapped back to the legacy {ok, out, reason,
|
|
428
|
+
* reasonCode, authStatus} shape extractLocally's caller already expects. opts
|
|
429
|
+
* (spawnSyncImpl/claudeBin/etc.) thread straight through so the injection
|
|
430
|
+
* pattern used by every extractLocally test still works when a test exercises
|
|
431
|
+
* this default path instead of supplying its own opts.invokeModel.
|
|
432
|
+
*/
|
|
433
|
+
async function defaultInvokeModel(transcript, invokeOpts, opts) {
|
|
434
|
+
const result = await providers.runModel({
|
|
435
|
+
...opts,
|
|
436
|
+
prompt: invokeOpts.prompt,
|
|
437
|
+
input: transcript,
|
|
438
|
+
timeoutMs: 120000,
|
|
439
|
+
log: opts.log,
|
|
440
|
+
mode: 'extract',
|
|
441
|
+
});
|
|
442
|
+
return {
|
|
443
|
+
ok: result.ok,
|
|
444
|
+
out: result.text,
|
|
445
|
+
reason: result.reason,
|
|
446
|
+
reasonCode: result.reasonCode,
|
|
447
|
+
authStatus: result.authStatus,
|
|
448
|
+
extractionModel: await resolveExtractionModelIdentity(result, opts),
|
|
449
|
+
...(result.authDiscrepancy !== undefined && { authDiscrepancy: result.authDiscrepancy }),
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
/**
|
|
454
|
+
* Default invokeJudge (runAnchoredJudge) — routes through the resolved
|
|
455
|
+
* provider's runModel(mode:'judge'), mapped back to the legacy {ok, out, usage,
|
|
456
|
+
* reason} shape runAnchoredJudge already consumes. Same opts thread-through as
|
|
457
|
+
* defaultInvokeModel above.
|
|
458
|
+
*/
|
|
459
|
+
function defaultInvokeJudge(opts) {
|
|
460
|
+
return async (prompt) => {
|
|
461
|
+
const result = await providers.runModel({
|
|
462
|
+
...opts,
|
|
463
|
+
prompt,
|
|
464
|
+
timeoutMs: 120000,
|
|
465
|
+
log: opts.log,
|
|
466
|
+
mode: 'judge',
|
|
467
|
+
});
|
|
468
|
+
return { ok: result.ok, out: result.text, usage: result.usage, reason: result.reason };
|
|
469
|
+
};
|
|
563
470
|
}
|
|
564
471
|
|
|
565
472
|
function parseJudgeDecisions(raw, candidates, rankings) {
|
|
@@ -614,7 +521,7 @@ async function runAnchoredJudge(candidates, indexState, opts = {}) {
|
|
|
614
521
|
if (rankings.some((rows) => rows.length === 0)) return empty;
|
|
615
522
|
const invokeJudge = typeof opts.invokeJudge === 'function'
|
|
616
523
|
? opts.invokeJudge
|
|
617
|
-
:
|
|
524
|
+
: defaultInvokeJudge(opts);
|
|
618
525
|
let result;
|
|
619
526
|
try {
|
|
620
527
|
result = await invokeJudge(prompt, { candidates: input, rankings });
|
|
@@ -749,7 +656,7 @@ async function extractLocally(transcript, sourceType, opts = {}) {
|
|
|
749
656
|
});
|
|
750
657
|
const invokeModel = typeof opts.invokeModel === 'function'
|
|
751
658
|
? opts.invokeModel
|
|
752
|
-
: (text, invokeOpts) =>
|
|
659
|
+
: (text, invokeOpts) => defaultInvokeModel(text, invokeOpts, opts);
|
|
753
660
|
const modelResult = await invokeModel(transcript, { prompt });
|
|
754
661
|
const { ok, out, reason } = modelResult;
|
|
755
662
|
if (!ok) {
|
|
@@ -763,13 +670,21 @@ async function extractLocally(transcript, sourceType, opts = {}) {
|
|
|
763
670
|
}),
|
|
764
671
|
};
|
|
765
672
|
}
|
|
673
|
+
// PART C: which provider/model actually ran this extraction, stamped onto
|
|
674
|
+
// every learning it produces (below). Absent when the caller supplied its
|
|
675
|
+
// own opts.invokeModel that doesn't report one (every existing test does
|
|
676
|
+
// this) — extraction_model then simply never appears, byte-identical to
|
|
677
|
+
// pre-PART-C behavior.
|
|
678
|
+
const extractionModel = modelResult.extractionModel || null;
|
|
766
679
|
const parsed = parseExtractionOutput(out, opts);
|
|
767
680
|
const promptDropResult = applyPromptMemoryDrops(
|
|
768
681
|
parsed.prompt_drops,
|
|
769
682
|
promptMemory,
|
|
770
683
|
opts
|
|
771
684
|
);
|
|
772
|
-
const candidates = [...parsed.learnings, ...promptDropResult.restored]
|
|
685
|
+
const candidates = [...parsed.learnings, ...promptDropResult.restored].map((l) => (
|
|
686
|
+
extractionModel ? { ...l, extraction_model: extractionModel } : l
|
|
687
|
+
));
|
|
773
688
|
|
|
774
689
|
// Re-read immediately before the lexical filter. If the index is deleted,
|
|
775
690
|
// becomes unreadable, or is corrupted while the model runs, the filter is
|
|
@@ -824,11 +739,18 @@ async function extractLocally(transcript, sourceType, opts = {}) {
|
|
|
824
739
|
}
|
|
825
740
|
|
|
826
741
|
module.exports = {
|
|
827
|
-
extractLocally,
|
|
828
|
-
parseLearnings, parseExtractionOutput,
|
|
742
|
+
extractLocally, EXTRACTABLE_SOURCES, EXTRACTABLE_SOURCE_IDS,
|
|
743
|
+
parseLearnings, parseExtractionOutput,
|
|
829
744
|
CATEGORIES, PRIVATE_CATEGORIES, RETIRED_CATEGORIES,
|
|
830
745
|
EXTRACTION_PROMPT, buildExtractionPrompt, scoreExtractionEnabled,
|
|
831
746
|
validateQualityAssessment, QUALITY_DIMENSIONS,
|
|
832
747
|
buildAnchoredJudgePrompt, parseJudgeDecisions, runAnchoredJudge,
|
|
833
|
-
recordDropAudit,
|
|
748
|
+
recordDropAudit, JUDGE_TOP_K,
|
|
749
|
+
// Re-exported from scripts/providers/claude-code.js (moved there in PART A) —
|
|
750
|
+
// kept here ONLY because test/ext-0806b-silent-skip.test.js imports these
|
|
751
|
+
// three directly from this module (grep-confirmed; invokeJudgeWithClaudeCode
|
|
752
|
+
// and judgeUsage had no such importer and are NOT carried forward).
|
|
753
|
+
extractWithClaudeCode: claudeCodeProvider.extractWithClaudeCode,
|
|
754
|
+
checkClaudeAuthStatus: claudeCodeProvider.checkClaudeAuthStatus,
|
|
755
|
+
resolveClaudeBin: claudeCodeProvider.resolveClaudeBin,
|
|
834
756
|
};
|