prism-mcp-server 20.22.0 → 20.22.2
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 +32 -2
- package/dist/tools/prismInferHandler.js +9 -5
- package/dist/utils/layer1.js +3 -1
- package/dist/utils/omlxLayer1.js +7 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -99,8 +99,11 @@ Layer 1's rules only refuse here; the classifier reads everything else, and a
|
|
|
99
99
|
prompt over 4,000 characters is read whole in 3,600-character windows (the most
|
|
100
100
|
severe verdict stands), never as an excerpt. A classifier failure, an unreadable
|
|
101
101
|
answer, or a missing classifier refuses the call; there is no keyword or Ollama
|
|
102
|
-
fallback.
|
|
103
|
-
macOS paged out during a long idle
|
|
102
|
+
fallback. Each classifier read is bounded by 90 seconds and by the call's
|
|
103
|
+
remaining `timeout_ms`, so a classifier that macOS paged out during a long idle
|
|
104
|
+
is waited for rather than refused, and an oversize prompt read in windows
|
|
105
|
+
cannot outlast the call budget; the first unrecoverable read ends the
|
|
106
|
+
classification.
|
|
104
107
|
|
|
105
108
|
Configured oMLX sampling and reasoning are preserved unless `temperature` or
|
|
106
109
|
`think` is explicit, including through repairs. Generic thinking-disable retries
|
|
@@ -232,6 +235,33 @@ or by re-enabling after each run.
|
|
|
232
235
|
<details>
|
|
233
236
|
<summary>Release history (optional)</summary>
|
|
234
237
|
|
|
238
|
+
## What's New in v20.22.2
|
|
239
|
+
|
|
240
|
+
**Why it matters:** an agent that followed `session_task_route`'s advice was
|
|
241
|
+
refused on explicit oMLX before anything ran.
|
|
242
|
+
|
|
243
|
+
- **A complexity hint no longer refuses explicit oMLX.** `session_task_route`
|
|
244
|
+
recommends a `task_complexity` for every code task. That hint picks Ollama's
|
|
245
|
+
starting tier, but on explicit oMLX it lowered the model ceiling, so a call
|
|
246
|
+
that passed the recommended arguments was refused with `omlx_model_ceiling`.
|
|
247
|
+
Now only an explicit `model_ceiling` below the top tier, or a plan below it,
|
|
248
|
+
refuses. Thinking on oMLX stays with the server's settings.
|
|
249
|
+
|
|
250
|
+
## What's New in v20.22.1
|
|
251
|
+
|
|
252
|
+
**Why it matters:** on explicit oMLX, a long prompt could keep the device busy
|
|
253
|
+
for minutes while being classified, and `timeout_ms` did not limit it.
|
|
254
|
+
|
|
255
|
+
- **The call budget now covers classification.** `timeout_ms` (default 300 s)
|
|
256
|
+
starts before the safety screen, and each classifier read is bounded by the
|
|
257
|
+
remaining budget as well as its own 90 s.
|
|
258
|
+
- **A failed read ends the classification.** An oversize prompt is read in
|
|
259
|
+
3,600-character windows; a read that fails already means refusal on this
|
|
260
|
+
path, so the remaining windows are no longer read. Before, a stalled server
|
|
261
|
+
could cost about four minutes per window.
|
|
262
|
+
- **Tests:** the owned-job marker tests pin the size limit and the
|
|
263
|
+
non-blocking open directly.
|
|
264
|
+
|
|
235
265
|
## What's New in v20.22.0
|
|
236
266
|
|
|
237
267
|
**Why it matters:** on Apple Silicon, `prism_infer` can now draft code on an
|
|
@@ -2206,12 +2206,14 @@ async function runInferAfterInputSafety(args, deps, t0, temperature) {
|
|
|
2206
2206
|
// No trusted binding maps an oMLX model to a logical tier, and the
|
|
2207
2207
|
// configured Flash model outweighs every Ollama tier. Below the top
|
|
2208
2208
|
// ceiling, from the caller or the plan, refuse before any network,
|
|
2209
|
-
// classification or residency change.
|
|
2210
|
-
|
|
2209
|
+
// classification or residency change. A task_complexity hint only picks
|
|
2210
|
+
// Ollama's starting tier (session_task_route recommends one for every
|
|
2211
|
+
// code task), so it is not a ceiling here; an explicit model_ceiling is.
|
|
2212
|
+
if (clampCeiling(args.model_ceiling ?? "27b", ent.model_ceiling) !== "27b") {
|
|
2211
2213
|
attempts.push({ tier: "omlx", reason: "omlx_model_ceiling" });
|
|
2212
2214
|
if (wantReport)
|
|
2213
2215
|
return refusedResult("omlx_model_ceiling");
|
|
2214
|
-
throw new Error("prism_infer: omlx_model_ceiling; explicit oMLX needs the top model ceiling");
|
|
2216
|
+
throw new Error("prism_infer: omlx_model_ceiling; explicit oMLX needs the top model ceiling from model_ceiling and the plan");
|
|
2215
2217
|
}
|
|
2216
2218
|
if (!Number.isInteger(localMaxTokens) || localMaxTokens <= 0
|
|
2217
2219
|
|| !Number.isFinite(temperature) || temperature < 0
|
|
@@ -2240,6 +2242,9 @@ async function runInferAfterInputSafety(args, deps, t0, temperature) {
|
|
|
2240
2242
|
const loaded = await deps.listLoaded();
|
|
2241
2243
|
if (omlxTarget && installed === null)
|
|
2242
2244
|
throw new Error("prism_infer: omlx_classifier_unavailable");
|
|
2245
|
+
// Explicit oMLX: timeout_ms bounds the whole call, the Ollama unload and
|
|
2246
|
+
// classification included.
|
|
2247
|
+
const omlxDeadline = Date.now() + (args.timeout_ms ?? 300_000);
|
|
2243
2248
|
// Ollama is unloaded during an explicit oMLX call (owner, 2026-10-15; this
|
|
2244
2249
|
// replaces plan section 4's "never evict another Ollama user's weights").
|
|
2245
2250
|
// Under the device lease, before classification: unload every resident
|
|
@@ -2328,7 +2333,7 @@ async function runInferAfterInputSafety(args, deps, t0, temperature) {
|
|
|
2328
2333
|
const baseL1 = deps.callLayer1 ?? defaultCallLayer1;
|
|
2329
2334
|
const generationTarget = omlxTarget, runtime = omlxRuntime;
|
|
2330
2335
|
const omlxL1Fetch = generationTarget && runtime
|
|
2331
|
-
? omlxLayer1Fetch(async (request) => guardedOmlxGenerate(runtime, omlxModelTarget(generationTarget, OMLX_LAYER1_MODEL), request, deps.freemem))
|
|
2336
|
+
? omlxLayer1Fetch(async (request) => guardedOmlxGenerate(runtime, omlxModelTarget(generationTarget, OMLX_LAYER1_MODEL), request, deps.freemem), () => omlxDeadline - Date.now())
|
|
2332
2337
|
: undefined;
|
|
2333
2338
|
const l1fn = omlxL1Fetch
|
|
2334
2339
|
? (...[prompt, url, model, , images, opts]) => baseL1(prompt, url, model, omlxL1Fetch, images, opts)
|
|
@@ -2720,7 +2725,6 @@ async function runInferAfterInputSafety(args, deps, t0, temperature) {
|
|
|
2720
2725
|
// Explicit oMLX classified on oMLX, so nothing of this call's is in Ollama.
|
|
2721
2726
|
// Anything resident now belongs to another user: guardedOmlxGenerate
|
|
2722
2727
|
// refuses generation (omlx_memory_contention) before any request.
|
|
2723
|
-
const omlxDeadline = Date.now() + (args.timeout_ms ?? 300_000);
|
|
2724
2728
|
const callLocal = omlxTarget && omlxRuntime
|
|
2725
2729
|
? async (_url, _model, prompt, system, maxTokens, temperature, timeoutMs, think, _images, history) => {
|
|
2726
2730
|
const messages = [...(system === undefined ? [] : [{ role: "system", content: system }]),
|
package/dist/utils/layer1.js
CHANGED
|
@@ -550,7 +550,9 @@ images, opts) {
|
|
|
550
550
|
const verdict = await callLayer1Unleased(window, ollamaUrl, model, fetchImpl, images, opts);
|
|
551
551
|
if (VERDICT_SEVERITY[verdict] > VERDICT_SEVERITY[worst])
|
|
552
552
|
worst = verdict;
|
|
553
|
-
|
|
553
|
+
// Reserved decides the call; under strict errors an ERROR does too (the
|
|
554
|
+
// call refuses), so the remaining windows are never read.
|
|
555
|
+
if (worst === "OBVIOUS_RESERVED" || (opts?.strictErrors && verdict === "ERROR"))
|
|
554
556
|
break;
|
|
555
557
|
}
|
|
556
558
|
return worst;
|
package/dist/utils/omlxLayer1.js
CHANGED
|
@@ -29,8 +29,13 @@ function chatMessages(value) {
|
|
|
29
29
|
* deadline.
|
|
30
30
|
* - Every request asks the provider not to keep its prompt cache: a classifier
|
|
31
31
|
* request is unique and never reused. */
|
|
32
|
-
export function omlxLayer1Fetch(generate) {
|
|
32
|
+
export function omlxLayer1Fetch(generate, remainingMs = () => OMLX_LAYER1_TIMEOUT_MS) {
|
|
33
33
|
return (async (_input, init) => {
|
|
34
|
+
// Each read is bounded by the call's remaining timeout_ms as well, so an
|
|
35
|
+
// oversize prompt read in windows cannot outlast the budget the caller set.
|
|
36
|
+
const timeoutMs = Math.min(OMLX_LAYER1_TIMEOUT_MS, Math.floor(remainingMs()));
|
|
37
|
+
if (timeoutMs <= 0)
|
|
38
|
+
return Response.json({ error: "timeout" }, { status: 503 });
|
|
34
39
|
let body;
|
|
35
40
|
try {
|
|
36
41
|
body = JSON.parse(String(init?.body));
|
|
@@ -46,7 +51,7 @@ export function omlxLayer1Fetch(generate) {
|
|
|
46
51
|
return new Response(null, { status: 400 });
|
|
47
52
|
const temperature = body.options?.temperature;
|
|
48
53
|
const result = await generate({ messages, maxTokens, temperature: typeof temperature === "number" ? temperature : 0,
|
|
49
|
-
timeoutMs
|
|
54
|
+
timeoutMs, think: body.think === true, cacheStore: false });
|
|
50
55
|
return result.ok
|
|
51
56
|
? Response.json({ done: true, done_reason: result.doneReason, message: { role: "assistant", content: result.text } })
|
|
52
57
|
: Response.json({ error: result.reason }, { status: 503 });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "prism-mcp-server",
|
|
3
|
-
"version": "20.22.
|
|
3
|
+
"version": "20.22.2",
|
|
4
4
|
"mcpName": "io.github.dcostenco/prism-coder",
|
|
5
5
|
"description": "Persistent session memory for AI coding agents that never leaves your machine — including the on-device model that reasons over it. Restores your prior decisions, open TODOs, and changed files across sessions; adds associative recall of related past work, semantic drift detection, and local inference. Local-first by default. Works with Claude Code, Cursor, and Codex.",
|
|
6
6
|
"module": "index.ts",
|