clef-mcp 0.2.0 → 0.3.0
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 +27 -4
- package/dist/cli.js +557 -165
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +32 -10
- package/dist/index.js +130 -69
- package/dist/index.js.map +1 -1
- package/examples/claude-code/README.md +21 -0
- package/examples/claude-code/mcp.json +8 -0
- package/examples/codex/config.toml +21 -0
- package/examples/cursor/README.md +12 -0
- package/examples/cursor/mcp.json +8 -0
- package/examples/hooks/README.md +12 -0
- package/examples/hooks/ci-triage/README.md +68 -0
- package/examples/hooks/pretooluse-guard/README.md +54 -0
- package/examples/hooks/pretooluse-guard/guard.sh +61 -0
- package/package.json +3 -2
- package/skills/clef-decisions/SKILL.md +15 -5
package/dist/index.d.ts
CHANGED
|
@@ -32,6 +32,8 @@ interface ClefConfig {
|
|
|
32
32
|
llamaBin?: string;
|
|
33
33
|
/** Pin the llama.cpp release tag used by the managed runtime download. */
|
|
34
34
|
llamaReleaseTag?: string;
|
|
35
|
+
/** Daemon: unload the model after this many idle seconds (CLEF_DAEMON_IDLE, 0 = never). */
|
|
36
|
+
daemonIdleSeconds: number;
|
|
35
37
|
limits: Limits;
|
|
36
38
|
}
|
|
37
39
|
declare function defaultClefHome(): string;
|
|
@@ -118,11 +120,22 @@ interface ClefDecideInput {
|
|
|
118
120
|
}
|
|
119
121
|
/** Probability distribution over answer options for one question. */
|
|
120
122
|
type ClefDecisionAnswer = Record<string, number>;
|
|
123
|
+
/**
|
|
124
|
+
* Model-reported token usage / latency. Fields are optional because runtimes
|
|
125
|
+
* report different subsets (llama.cpp: tokens; the MLX loader: latency_ms).
|
|
126
|
+
*/
|
|
127
|
+
type ClefUsage = {
|
|
128
|
+
input_tokens?: number;
|
|
129
|
+
output_tokens?: number;
|
|
130
|
+
latency_ms?: number;
|
|
131
|
+
};
|
|
121
132
|
type ClefDecideOutput = {
|
|
122
133
|
model: string;
|
|
123
134
|
decisions: Record<string, {
|
|
124
135
|
answer: ClefDecisionAnswer;
|
|
136
|
+
confidence?: number;
|
|
125
137
|
}>;
|
|
138
|
+
usage?: ClefUsage;
|
|
126
139
|
};
|
|
127
140
|
|
|
128
141
|
/**
|
|
@@ -386,6 +399,16 @@ declare function detectPlatform(): Promise<PlatformInfo>;
|
|
|
386
399
|
declare function assertSupportedPlatform(info: Pick<PlatformInfo, 'platform' | 'arch'>): void;
|
|
387
400
|
declare function formatBytes(bytes: number): string;
|
|
388
401
|
|
|
402
|
+
/** Test seam: override runtime construction (defaults per config.runtime). */
|
|
403
|
+
type RuntimeFactory = (opts: {
|
|
404
|
+
kind: 'llama-cpp' | 'mlx';
|
|
405
|
+
serverBin?: string;
|
|
406
|
+
modelPath?: string;
|
|
407
|
+
snapshotPath?: string;
|
|
408
|
+
alias: string;
|
|
409
|
+
log?: Logger;
|
|
410
|
+
}) => ClefRuntime;
|
|
411
|
+
|
|
389
412
|
declare const TOOL_NAME = "clef_decide";
|
|
390
413
|
declare const SERVER_INSTRUCTIONS: string;
|
|
391
414
|
interface ClefServerDeps {
|
|
@@ -393,14 +416,7 @@ interface ClefServerDeps {
|
|
|
393
416
|
version?: string;
|
|
394
417
|
log?: Logger;
|
|
395
418
|
/** Test seam: override runtime construction (defaults per config.runtime). */
|
|
396
|
-
runtimeFactory?:
|
|
397
|
-
kind: 'llama-cpp' | 'mlx';
|
|
398
|
-
serverBin?: string;
|
|
399
|
-
modelPath?: string;
|
|
400
|
-
snapshotPath?: string;
|
|
401
|
-
alias: string;
|
|
402
|
-
log?: Logger;
|
|
403
|
-
}) => ClefRuntime;
|
|
419
|
+
runtimeFactory?: RuntimeFactory;
|
|
404
420
|
}
|
|
405
421
|
interface RunningClefServer {
|
|
406
422
|
server: McpServer;
|
|
@@ -430,11 +446,17 @@ declare function clefDecideOutputSchema(): z.ZodObject<{
|
|
|
430
446
|
model: z.ZodString;
|
|
431
447
|
decisions: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
432
448
|
answer: z.ZodRecord<z.ZodString, z.ZodNumber>;
|
|
449
|
+
confidence: z.ZodOptional<z.ZodNumber>;
|
|
450
|
+
}, z.core.$strip>>;
|
|
451
|
+
usage: z.ZodOptional<z.ZodObject<{
|
|
452
|
+
input_tokens: z.ZodOptional<z.ZodNumber>;
|
|
453
|
+
output_tokens: z.ZodOptional<z.ZodNumber>;
|
|
454
|
+
latency_ms: z.ZodOptional<z.ZodNumber>;
|
|
433
455
|
}, z.core.$strip>>;
|
|
434
456
|
}, z.core.$strip>;
|
|
435
457
|
/** `state` cannot be size-checked inside zod (it is `unknown`), so it is validated separately. */
|
|
436
458
|
declare function validateStateSize(state: unknown, limits: Limits): void;
|
|
437
459
|
|
|
438
|
-
declare const VERSION = "0.
|
|
460
|
+
declare const VERSION = "0.3.0";
|
|
439
461
|
|
|
440
|
-
export { CLEF_FLASH, type ClefConfig, type ClefDecideInput, type ClefDecideOutput, type ClefDecisionAnswer, ClefError, ClefErrorCode, type ClefQuestion, type ClefRuntime, type ClefServerDeps, DEFAULT_LIMITS, type DownloadProgress, type InstallModelResult, type Limits, LlamaCppRuntime, type LlamaCppRuntimeOptions, type LogLevel, type Logger, MODELS, type ModelManifest, type ModelSpec, type PlatformInfo, type ProbeResult, type QuantSpec, type QuestionType, type ResolvedBinary, type RunningClefServer, type RuntimeHealth, SERVER_INSTRUCTIONS, StderrLogger, type SystemOneRequest, type SystemOneResponse, TOOL_NAME, VERSION, assertSupportedPlatform, clefDecideInputSchema, clefDecideOutputSchema, createClefServer, createLogger, deepVerifyChecksum, defaultClefHome, detectPlatform, downloadFile, downloadManagedLlama, ensureLlamaRuntime, errorJson, exitCodeFor, fetchFileInfo, findInstalledManifest, formatBytes, fromSystemOneResponse, getModelSpec, getQuantSpec, installModel, limitsFromEnv, loadConfig, manifestPathFor, modelQuantDir, pathsFor, pickQuant, probeInference, readModelManifest, removeManagedRuntime, resolveLlamaServerBinary, toSystemOneRequest, validateStateSize, verifyModelFiles, writeModelManifest };
|
|
462
|
+
export { CLEF_FLASH, type ClefConfig, type ClefDecideInput, type ClefDecideOutput, type ClefDecisionAnswer, ClefError, ClefErrorCode, type ClefQuestion, type ClefRuntime, type ClefServerDeps, type ClefUsage, DEFAULT_LIMITS, type DownloadProgress, type InstallModelResult, type Limits, LlamaCppRuntime, type LlamaCppRuntimeOptions, type LogLevel, type Logger, MODELS, type ModelManifest, type ModelSpec, type PlatformInfo, type ProbeResult, type QuantSpec, type QuestionType, type ResolvedBinary, type RunningClefServer, type RuntimeHealth, SERVER_INSTRUCTIONS, StderrLogger, type SystemOneRequest, type SystemOneResponse, TOOL_NAME, VERSION, assertSupportedPlatform, clefDecideInputSchema, clefDecideOutputSchema, createClefServer, createLogger, deepVerifyChecksum, defaultClefHome, detectPlatform, downloadFile, downloadManagedLlama, ensureLlamaRuntime, errorJson, exitCodeFor, fetchFileInfo, findInstalledManifest, formatBytes, fromSystemOneResponse, getModelSpec, getQuantSpec, installModel, limitsFromEnv, loadConfig, manifestPathFor, modelQuantDir, pathsFor, pickQuant, probeInference, readModelManifest, removeManagedRuntime, resolveLlamaServerBinary, toSystemOneRequest, validateStateSize, verifyModelFiles, writeModelManifest };
|
package/dist/index.js
CHANGED
|
@@ -48,6 +48,7 @@ function loadConfig(env = process.env) {
|
|
|
48
48
|
const runtime = runtimeRaw;
|
|
49
49
|
const llamaBin = env.CLEF_LLAMA_BIN?.trim() || void 0;
|
|
50
50
|
const llamaReleaseTag = env.CLEF_LLAMA_RELEASE_TAG?.trim() || void 0;
|
|
51
|
+
const idleRaw = Number.parseInt(env.CLEF_DAEMON_IDLE?.trim() ?? "", 10);
|
|
51
52
|
return {
|
|
52
53
|
model: env.CLEF_MODEL?.trim() || "clef-flash",
|
|
53
54
|
clefHome: env.CLEF_HOME?.trim() || defaultClefHome(),
|
|
@@ -55,6 +56,7 @@ function loadConfig(env = process.env) {
|
|
|
55
56
|
logLevel,
|
|
56
57
|
llamaBin: runtime === "llama-cpp" ? llamaBin : void 0,
|
|
57
58
|
llamaReleaseTag: runtime === "llama-cpp" ? llamaReleaseTag : void 0,
|
|
59
|
+
daemonIdleSeconds: Number.isFinite(idleRaw) && idleRaw >= 0 ? idleRaw : 600,
|
|
58
60
|
limits: limitsFromEnv(env)
|
|
59
61
|
};
|
|
60
62
|
}
|
|
@@ -333,6 +335,21 @@ function normalizeScore(ans, qid, question) {
|
|
|
333
335
|
function normalizeChoice(ans, qid) {
|
|
334
336
|
return probabilitiesMap(ans, qid);
|
|
335
337
|
}
|
|
338
|
+
function confidenceOf(ans) {
|
|
339
|
+
return typeof ans.confidence === "number" && Number.isFinite(ans.confidence) ? ans.confidence : void 0;
|
|
340
|
+
}
|
|
341
|
+
function normalizeUsage(usage) {
|
|
342
|
+
if (!usage || typeof usage !== "object") return void 0;
|
|
343
|
+
const num = (v) => typeof v === "number" && Number.isFinite(v) ? v : void 0;
|
|
344
|
+
const out = {};
|
|
345
|
+
const input = num(usage.input_tokens) ?? num(usage.prompt_tokens);
|
|
346
|
+
const output = num(usage.output_tokens) ?? num(usage.completion_tokens);
|
|
347
|
+
const latency = num(usage.latency_ms);
|
|
348
|
+
if (input !== void 0) out.input_tokens = input;
|
|
349
|
+
if (output !== void 0) out.output_tokens = output;
|
|
350
|
+
if (latency !== void 0) out.latency_ms = latency;
|
|
351
|
+
return Object.keys(out).length > 0 ? out : void 0;
|
|
352
|
+
}
|
|
336
353
|
function fromSystemOneResponse(response, questions, fallbackModel) {
|
|
337
354
|
const answers = response.answers;
|
|
338
355
|
if (!answers || typeof answers !== "object") {
|
|
@@ -353,9 +370,11 @@ function fromSystemOneResponse(response, questions, fallbackModel) {
|
|
|
353
370
|
answer = normalizeScore(ans, qid, question);
|
|
354
371
|
break;
|
|
355
372
|
}
|
|
356
|
-
|
|
373
|
+
const confidence = confidenceOf(ans);
|
|
374
|
+
decisions[qid] = confidence === void 0 ? { answer } : { answer, confidence };
|
|
357
375
|
}
|
|
358
|
-
|
|
376
|
+
const usage = normalizeUsage(response.usage);
|
|
377
|
+
return usage === void 0 ? { model: response.model ?? fallbackModel, decisions } : { model: response.model ?? fallbackModel, decisions, usage };
|
|
359
378
|
}
|
|
360
379
|
|
|
361
380
|
// src/runtime/llama-cpp.ts
|
|
@@ -1470,6 +1489,75 @@ function formatBytes(bytes) {
|
|
|
1470
1489
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
1471
1490
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
1472
1491
|
|
|
1492
|
+
// src/runtime/loader.ts
|
|
1493
|
+
var RuntimeLoader = class {
|
|
1494
|
+
constructor(config, log, runtimeFactory) {
|
|
1495
|
+
this.config = config;
|
|
1496
|
+
this.log = log;
|
|
1497
|
+
this.runtimeFactory = runtimeFactory;
|
|
1498
|
+
}
|
|
1499
|
+
config;
|
|
1500
|
+
log;
|
|
1501
|
+
runtimeFactory;
|
|
1502
|
+
loaded;
|
|
1503
|
+
loadPromise;
|
|
1504
|
+
async ensure(spec) {
|
|
1505
|
+
if (this.loaded && this.loaded.model === spec.id) return this.loaded;
|
|
1506
|
+
if (this.loaded && this.loaded.model !== spec.id) {
|
|
1507
|
+
await this.loaded.runtime.unload();
|
|
1508
|
+
this.loaded = void 0;
|
|
1509
|
+
}
|
|
1510
|
+
if (this.loadPromise) return this.loadPromise;
|
|
1511
|
+
this.loadPromise = (async () => {
|
|
1512
|
+
if (this.config.runtime === "mlx") {
|
|
1513
|
+
const manifest2 = await mlxIsInstalled(this.config.clefHome);
|
|
1514
|
+
if (!manifest2) {
|
|
1515
|
+
throw new ClefError(
|
|
1516
|
+
ClefErrorCode.MODEL_NOT_INSTALLED,
|
|
1517
|
+
`Clef model "${spec.id}" is not installed for the MLX runtime.`,
|
|
1518
|
+
"Run `clef-mcp install --runtime mlx`."
|
|
1519
|
+
);
|
|
1520
|
+
}
|
|
1521
|
+
const uvBin = resolveUv(process.env.CLEF_MLX_UV);
|
|
1522
|
+
const runtime2 = this.runtimeFactory ? this.runtimeFactory({ kind: "mlx", snapshotPath: manifest2.snapshotPath, alias: spec.id, log: this.log }) : new MlxRuntime({ uvBin, snapshotPath: manifest2.snapshotPath, log: this.log });
|
|
1523
|
+
this.log.debug("loading model (mlx)", { model: spec.id, snapshot: manifest2.snapshotPath });
|
|
1524
|
+
const loadStarted2 = Date.now();
|
|
1525
|
+
await runtime2.load();
|
|
1526
|
+
this.log.info("model ready", { model: spec.id, runtime: "mlx", loadMs: Date.now() - loadStarted2 });
|
|
1527
|
+
return { runtime: runtime2, model: spec.id };
|
|
1528
|
+
}
|
|
1529
|
+
const manifest = await findInstalledManifest(this.config.clefHome, spec);
|
|
1530
|
+
if (!manifest) {
|
|
1531
|
+
throw new ClefError(
|
|
1532
|
+
ClefErrorCode.MODEL_NOT_INSTALLED,
|
|
1533
|
+
`Clef model "${spec.id}" is not installed.`,
|
|
1534
|
+
"Run `clef-mcp install`."
|
|
1535
|
+
);
|
|
1536
|
+
}
|
|
1537
|
+
const binary = resolveLlamaServerBinary(this.config);
|
|
1538
|
+
const runtime = this.runtimeFactory ? this.runtimeFactory({ kind: "llama-cpp", serverBin: binary.path, modelPath: manifest.modelPath, alias: spec.id, log: this.log }) : new LlamaCppRuntime({ serverBin: binary.path, modelPath: manifest.modelPath, alias: spec.id, log: this.log });
|
|
1539
|
+
this.log.debug("loading model", { model: spec.id, quant: manifest.quant, bin: binary.path });
|
|
1540
|
+
const loadStarted = Date.now();
|
|
1541
|
+
await runtime.load();
|
|
1542
|
+
this.log.info("model ready", { model: spec.id, quant: manifest.quant, loadMs: Date.now() - loadStarted });
|
|
1543
|
+
return { runtime, model: spec.id };
|
|
1544
|
+
})();
|
|
1545
|
+
try {
|
|
1546
|
+
this.loaded = await this.loadPromise;
|
|
1547
|
+
return this.loaded;
|
|
1548
|
+
} finally {
|
|
1549
|
+
this.loadPromise = void 0;
|
|
1550
|
+
}
|
|
1551
|
+
}
|
|
1552
|
+
/** Unload the current runtime (kills the llama-server subprocess, if any). */
|
|
1553
|
+
async dispose() {
|
|
1554
|
+
if (this.loaded) {
|
|
1555
|
+
await this.loaded.runtime.unload();
|
|
1556
|
+
this.loaded = void 0;
|
|
1557
|
+
}
|
|
1558
|
+
}
|
|
1559
|
+
};
|
|
1560
|
+
|
|
1473
1561
|
// src/mcp/schema.ts
|
|
1474
1562
|
import { z } from "zod";
|
|
1475
1563
|
function criteriaDescription(limits) {
|
|
@@ -1503,10 +1591,12 @@ function scoreCriteria(limits) {
|
|
|
1503
1591
|
}
|
|
1504
1592
|
function questionSchema(limits) {
|
|
1505
1593
|
return z.object({
|
|
1506
|
-
type: z.enum(["noul", "choice", "score"]),
|
|
1507
|
-
instructions: z.string().min(1).max(limits.maxInstructionsChars),
|
|
1594
|
+
type: z.enum(["noul", "choice", "score"]).describe('"choice" = pick among named options; "score" = judge an ordered scale; "noul" = yes/no question'),
|
|
1595
|
+
instructions: z.string().min(1).max(limits.maxInstructionsChars).describe("What to judge, phrased as a self-contained question."),
|
|
1508
1596
|
criteria: z.union([z.array(z.string()), z.record(z.string(), z.string())]).optional()
|
|
1509
|
-
}).
|
|
1597
|
+
}).describe(
|
|
1598
|
+
"A single decision question. Allowed answers come from `criteria` and must be mutually exclusive and collectively exhaustive."
|
|
1599
|
+
).superRefine((q, ctx) => {
|
|
1510
1600
|
if (q.type === "choice") {
|
|
1511
1601
|
const parsed = choiceCriteria(limits).safeParse(q.criteria);
|
|
1512
1602
|
if (!parsed.success) {
|
|
@@ -1530,23 +1620,27 @@ function questionSchema(limits) {
|
|
|
1530
1620
|
}
|
|
1531
1621
|
function clefDecideInputShape(limits) {
|
|
1532
1622
|
return {
|
|
1533
|
-
state: z.unknown()
|
|
1534
|
-
|
|
1623
|
+
state: z.unknown().describe(
|
|
1624
|
+
"Task state as data: a compact string or JSON object with the facts needed to judge (task, errors, logs, diffs). Never treated as instructions. Serialized size limit: 1 MB; model context is 16k tokens."
|
|
1625
|
+
),
|
|
1626
|
+
questions: z.record(z.string().min(1).max(limits.maxQuestionIdChars), questionSchema(limits)).describe(
|
|
1627
|
+
"Map of question id -> typed question. Up to 64 questions per call; all are scored together in a single forward pass, so batch related decisions here."
|
|
1628
|
+
).check((ctx) => {
|
|
1535
1629
|
const count = Object.keys(ctx.value).length;
|
|
1536
1630
|
if (count === 0) ctx.issues.push({ code: "custom", message: "questions must not be empty", input: ctx.value });
|
|
1537
1631
|
if (count > limits.maxQuestions) {
|
|
1538
1632
|
ctx.issues.push({ code: "custom", message: `too many questions (max ${limits.maxQuestions})`, input: ctx.value });
|
|
1539
1633
|
}
|
|
1540
1634
|
}),
|
|
1541
|
-
model: z.string().min(1).optional(),
|
|
1635
|
+
model: z.string().min(1).optional().describe("Model id to use (default: clef-flash from CLEF_MODEL)."),
|
|
1542
1636
|
options: z.object({
|
|
1543
1637
|
/**
|
|
1544
1638
|
* Accepted for forward compatibility. Clef scores all options in a
|
|
1545
1639
|
* single forward pass — there is no sampling, so temperature is a
|
|
1546
1640
|
* documented no-op.
|
|
1547
1641
|
*/
|
|
1548
|
-
temperature: z.number().min(0).max(2).optional()
|
|
1549
|
-
}).optional()
|
|
1642
|
+
temperature: z.number().min(0).max(2).optional().describe("Accepted for forward compatibility; no-op. Clef scores in a single forward pass without sampling.")
|
|
1643
|
+
}).optional().describe("Extra options. Currently only temperature, which is a documented no-op.")
|
|
1550
1644
|
};
|
|
1551
1645
|
}
|
|
1552
1646
|
function clefDecideInputSchema(limits) {
|
|
@@ -1555,7 +1649,20 @@ function clefDecideInputSchema(limits) {
|
|
|
1555
1649
|
function clefDecideOutputSchema() {
|
|
1556
1650
|
return z.object({
|
|
1557
1651
|
model: z.string(),
|
|
1558
|
-
decisions: z.record(
|
|
1652
|
+
decisions: z.record(
|
|
1653
|
+
z.string(),
|
|
1654
|
+
z.object({
|
|
1655
|
+
answer: z.record(z.string(), z.number()),
|
|
1656
|
+
// Model-reported confidence; present only when the runtime sends it.
|
|
1657
|
+
confidence: z.number().min(0).max(1).optional()
|
|
1658
|
+
})
|
|
1659
|
+
),
|
|
1660
|
+
// Token usage / latency; present only when the runtime reports it.
|
|
1661
|
+
usage: z.object({
|
|
1662
|
+
input_tokens: z.number().optional(),
|
|
1663
|
+
output_tokens: z.number().optional(),
|
|
1664
|
+
latency_ms: z.number().optional()
|
|
1665
|
+
}).optional()
|
|
1559
1666
|
});
|
|
1560
1667
|
}
|
|
1561
1668
|
function validateStateSize(state, limits) {
|
|
@@ -1803,63 +1910,20 @@ function createClefServer(deps = {}) {
|
|
|
1803
1910
|
const server = new McpServer({ name: "clef-mcp", version }, { instructions: SERVER_INSTRUCTIONS });
|
|
1804
1911
|
registerPrompts(server);
|
|
1805
1912
|
registerResources(server, config, version);
|
|
1806
|
-
|
|
1807
|
-
let loadPromise;
|
|
1808
|
-
async function ensureRuntime(spec) {
|
|
1809
|
-
if (loaded && loaded.model === spec.id) return loaded;
|
|
1810
|
-
if (loaded && loaded.model !== spec.id) {
|
|
1811
|
-
await loaded.runtime.unload();
|
|
1812
|
-
loaded = void 0;
|
|
1813
|
-
}
|
|
1814
|
-
if (loadPromise) return loadPromise;
|
|
1815
|
-
loadPromise = (async () => {
|
|
1816
|
-
if (config.runtime === "mlx") {
|
|
1817
|
-
const manifest2 = await mlxIsInstalled(config.clefHome);
|
|
1818
|
-
if (!manifest2) {
|
|
1819
|
-
throw new ClefError(
|
|
1820
|
-
ClefErrorCode.MODEL_NOT_INSTALLED,
|
|
1821
|
-
`Clef model "${spec.id}" is not installed for the MLX runtime.`,
|
|
1822
|
-
"Run `clef-mcp install --runtime mlx`."
|
|
1823
|
-
);
|
|
1824
|
-
}
|
|
1825
|
-
const uvBin = resolveUv(process.env.CLEF_MLX_UV);
|
|
1826
|
-
const runtime2 = deps.runtimeFactory ? deps.runtimeFactory({ kind: "mlx", snapshotPath: manifest2.snapshotPath, alias: spec.id, log }) : new MlxRuntime({ uvBin, snapshotPath: manifest2.snapshotPath, log });
|
|
1827
|
-
log.debug("loading model (mlx)", { model: spec.id, snapshot: manifest2.snapshotPath });
|
|
1828
|
-
const loadStarted2 = Date.now();
|
|
1829
|
-
await runtime2.load();
|
|
1830
|
-
log.info("model ready", { model: spec.id, runtime: "mlx", loadMs: Date.now() - loadStarted2 });
|
|
1831
|
-
return { runtime: runtime2, model: spec.id };
|
|
1832
|
-
}
|
|
1833
|
-
const manifest = await findInstalledManifest(config.clefHome, spec);
|
|
1834
|
-
if (!manifest) {
|
|
1835
|
-
throw new ClefError(
|
|
1836
|
-
ClefErrorCode.MODEL_NOT_INSTALLED,
|
|
1837
|
-
`Clef model "${spec.id}" is not installed.`,
|
|
1838
|
-
"Run `clef-mcp install`."
|
|
1839
|
-
);
|
|
1840
|
-
}
|
|
1841
|
-
const binary = resolveLlamaServerBinary(config);
|
|
1842
|
-
const runtime = deps.runtimeFactory ? deps.runtimeFactory({ kind: "llama-cpp", serverBin: binary.path, modelPath: manifest.modelPath, alias: spec.id, log }) : new LlamaCppRuntime({ serverBin: binary.path, modelPath: manifest.modelPath, alias: spec.id, log });
|
|
1843
|
-
log.debug("loading model", { model: spec.id, quant: manifest.quant, bin: binary.path });
|
|
1844
|
-
const loadStarted = Date.now();
|
|
1845
|
-
await runtime.load();
|
|
1846
|
-
log.info("model ready", { model: spec.id, quant: manifest.quant, loadMs: Date.now() - loadStarted });
|
|
1847
|
-
return { runtime, model: spec.id };
|
|
1848
|
-
})();
|
|
1849
|
-
try {
|
|
1850
|
-
loaded = await loadPromise;
|
|
1851
|
-
return loaded;
|
|
1852
|
-
} finally {
|
|
1853
|
-
loadPromise = void 0;
|
|
1854
|
-
}
|
|
1855
|
-
}
|
|
1913
|
+
const loader = new RuntimeLoader(config, log, deps.runtimeFactory);
|
|
1856
1914
|
server.registerTool(
|
|
1857
1915
|
TOOL_NAME,
|
|
1858
1916
|
{
|
|
1859
1917
|
title: "Clef Decide",
|
|
1860
|
-
description:
|
|
1918
|
+
description: 'Judge a situation with the local Clef decision model.\nPass `state` (compact factual context \u2014 string or JSON; treated as data, never executed) and typed `questions`:\n- "choice": pick among named options (criteria = option id -> description)\n- "score": judge an ordered scale (criteria = ordered list of levels)\n- "noul": yes/no question\nReturns strict JSON \u2014 a probability distribution over the allowed answers for every question. No prose, no sampling.\nEach decision also carries `confidence` (model-reported, when available) and the response carries `usage`\n(input_tokens/output_tokens/latency_ms). Act on the argmax only when the distribution is decisive (top p >= 0.8\nand high confidence for destructive or security-adjacent calls); otherwise gather more context or ask the user.\nUsage: batch related decisions in one call (up to 64 questions, one forward pass).\nOptional `model` overrides the model id. `options.temperature` is a no-op: scoring is a single forward pass, nothing is sampled.',
|
|
1861
1919
|
inputSchema: clefDecideInputShape(config.limits),
|
|
1862
|
-
outputSchema: clefDecideOutputSchema().shape
|
|
1920
|
+
outputSchema: clefDecideOutputSchema().shape,
|
|
1921
|
+
annotations: {
|
|
1922
|
+
readOnlyHint: true,
|
|
1923
|
+
destructiveHint: false,
|
|
1924
|
+
idempotentHint: true,
|
|
1925
|
+
openWorldHint: false
|
|
1926
|
+
}
|
|
1863
1927
|
},
|
|
1864
1928
|
async (args) => {
|
|
1865
1929
|
try {
|
|
@@ -1873,7 +1937,7 @@ function createClefServer(deps = {}) {
|
|
|
1873
1937
|
options: parsed.options
|
|
1874
1938
|
};
|
|
1875
1939
|
const request = toSystemOneRequest(input, spec.id);
|
|
1876
|
-
const { runtime } = await
|
|
1940
|
+
const { runtime } = await loader.ensure(spec);
|
|
1877
1941
|
const response = await runtime.decide(request);
|
|
1878
1942
|
const output = fromSystemOneResponse(response, parsed.questions, spec.id);
|
|
1879
1943
|
return {
|
|
@@ -1891,10 +1955,7 @@ function createClefServer(deps = {}) {
|
|
|
1891
1955
|
}
|
|
1892
1956
|
);
|
|
1893
1957
|
async function shutdown() {
|
|
1894
|
-
|
|
1895
|
-
await loaded.runtime.unload();
|
|
1896
|
-
loaded = void 0;
|
|
1897
|
-
}
|
|
1958
|
+
await loader.dispose();
|
|
1898
1959
|
}
|
|
1899
1960
|
async function start() {
|
|
1900
1961
|
const transport = new StdioServerTransport();
|
|
@@ -1914,7 +1975,7 @@ function createClefServer(deps = {}) {
|
|
|
1914
1975
|
}
|
|
1915
1976
|
|
|
1916
1977
|
// src/version.ts
|
|
1917
|
-
var VERSION = "0.
|
|
1978
|
+
var VERSION = "0.3.0";
|
|
1918
1979
|
export {
|
|
1919
1980
|
CLEF_FLASH,
|
|
1920
1981
|
ClefError,
|