prism-mcp-server 20.5.0 → 20.5.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/dist/server.js +3 -1
- package/dist/skillManifestSync.js +25 -1
- package/dist/tools/ledgerHandlers.js +79 -3
- package/package.json +1 -1
package/dist/server.js
CHANGED
|
@@ -324,7 +324,9 @@ export const PRISM_SERVER_INSTRUCTIONS = `Prism MCP — The Mind Palace for AI A
|
|
|
324
324
|
`prompt_keywords ON-DEVICE to load symptom-triggered skills on turn one; it is used for routing ` +
|
|
325
325
|
`only and never leaves the machine. Pass {} only when there is no user message. ` +
|
|
326
326
|
`When the startup display lists "Symptom-triggered skills", those skills matched the first message's ` +
|
|
327
|
-
`trigger rules — read
|
|
327
|
+
`trigger rules — read each before proposing any change. Surfacing a name is not loading it: hosts that ` +
|
|
328
|
+
`do not auto-load skill files must fetch each body with knowledge_search, passing the skill name ` +
|
|
329
|
+
`exactly as listed. ` +
|
|
328
330
|
`Emit no preamble. Print the complete tool result verbatim as the entire first-turn startup display, before any optional ` +
|
|
329
331
|
`answer. Do not summarize, paraphrase, rename headings, reformat, or omit any returned section. Preserve its order and ` +
|
|
330
332
|
`line content. For a greeting-only prompt, stop after the verbatim startup display. ` +
|
|
@@ -386,9 +386,33 @@ async function enforceNativeEntitlements(incomingNames, agentsSkillsDir) {
|
|
|
386
386
|
await quarantineManagedSkill(target, name, agentsSkillsDir);
|
|
387
387
|
}
|
|
388
388
|
}
|
|
389
|
+
/**
|
|
390
|
+
* The cross-host canonical skills root — single source of truth.
|
|
391
|
+
*
|
|
392
|
+
* Do NOT re-derive this path anywhere else. It is overridable per caller
|
|
393
|
+
* (`agentsSkillsDir`) and per home (`homeDir`), so a literal copied into
|
|
394
|
+
* another module is wrong on any machine that overrides either.
|
|
395
|
+
*/
|
|
396
|
+
export function resolveCanonicalSkillsDir(options = {}) {
|
|
397
|
+
return options.agentsSkillsDir ?? join(options.homeDir ?? homedir(), ".agents", "skills");
|
|
398
|
+
}
|
|
399
|
+
/**
|
|
400
|
+
* Read a skill's body from the canonical root. Returns null when absent or
|
|
401
|
+
* unreadable — callers must degrade, never fail startup over it.
|
|
402
|
+
*/
|
|
403
|
+
export async function readNativeSkillBody(name, options = {}) {
|
|
404
|
+
if (!SAFE_NAME.test(name))
|
|
405
|
+
return null; // no traversal via name
|
|
406
|
+
try {
|
|
407
|
+
return await readFile(join(resolveCanonicalSkillsDir(options), name, "SKILL.md"), "utf8");
|
|
408
|
+
}
|
|
409
|
+
catch {
|
|
410
|
+
return null;
|
|
411
|
+
}
|
|
412
|
+
}
|
|
389
413
|
async function resolveNativeSkillsDirs(options) {
|
|
390
414
|
const userHome = options.homeDir ?? homedir();
|
|
391
|
-
const canonical = options
|
|
415
|
+
const canonical = resolveCanonicalSkillsDir(options);
|
|
392
416
|
let claudeCode = null;
|
|
393
417
|
let cursor = null;
|
|
394
418
|
if (typeof options.claudeCodeSkillsDir === "string") {
|
|
@@ -86,6 +86,60 @@ const MEMORY_BOUNDARY_SUFFIX = '\n</prism_memory>';
|
|
|
86
86
|
* budget — an unbounded list would starve the context it is meant to annotate.
|
|
87
87
|
*/
|
|
88
88
|
const MAX_SYMPTOM_SKILLS = 5;
|
|
89
|
+
/**
|
|
90
|
+
* Hard ceiling on the inlined skill body, and the share of the display budget
|
|
91
|
+
* it may take. Naming a skill is not delivering it: skill bodies reach agents
|
|
92
|
+
* ONLY as files under resolveNativeSkillsDirs, and hosts outside that list
|
|
93
|
+
* (Gemini) have no path to the content at all — no MCP tool serves it. Three
|
|
94
|
+
* instruction rewrites failed for that reason before it was found. Inlining
|
|
95
|
+
* removes the indirection entirely: the rule is simply in context.
|
|
96
|
+
*/
|
|
97
|
+
const SYMPTOM_SKILL_INLINE_MAX = 1_800;
|
|
98
|
+
const SYMPTOM_SKILL_BUDGET_SHARE = 0.4;
|
|
99
|
+
/** Below this the inlined rule is too clipped to be worth the space it costs. */
|
|
100
|
+
const SYMPTOM_SKILL_INLINE_MIN = 400;
|
|
101
|
+
/**
|
|
102
|
+
* The character budget a native startup display will actually be capped to.
|
|
103
|
+
*
|
|
104
|
+
* Single source of truth: capNativeStartupText caps against this, and the
|
|
105
|
+
* inlined rule is sized from it. Sizing the rule from the LEVEL constant
|
|
106
|
+
* instead let the suffix exceed the whole allowance — bootstrap divides the
|
|
107
|
+
* budget across rendered projects, so a per-project slice of 512 against an
|
|
108
|
+
* 1,800-char exempt suffix produced a 1,919-char display (275% over) with the
|
|
109
|
+
* session context entirely gone. The suffix is truncation-exempt by design;
|
|
110
|
+
* that only works if it is sized against the real budget.
|
|
111
|
+
*/
|
|
112
|
+
function effectiveNativeBudget(level, requestedMaxChars) {
|
|
113
|
+
const configuredLimit = NATIVE_STARTUP_MAX_CHARS[level];
|
|
114
|
+
return Math.max(512, Math.min(configuredLimit, requestedMaxChars ?? configuredLimit));
|
|
115
|
+
}
|
|
116
|
+
// Skill bodies are read via skillManifestSync.readNativeSkillBody, which owns
|
|
117
|
+
// the canonical root. That path is overridable per caller and per home, so a
|
|
118
|
+
// literal copied to this module would be wrong on any machine that overrides
|
|
119
|
+
// either — and would silently drift from the writer.
|
|
120
|
+
/**
|
|
121
|
+
* Drop the YAML frontmatter before inlining.
|
|
122
|
+
*
|
|
123
|
+
* `name`/`description`/`metadata` are routing and authoring metadata — the
|
|
124
|
+
* agent already has the name from the line above, and the rest is provenance.
|
|
125
|
+
* Measured at ~161 chars on data-before-code, which is what pushed the
|
|
126
|
+
* Anti-Patterns list past the cap and truncated it mid-word. Every character
|
|
127
|
+
* here is taken from the rule it is supposed to deliver.
|
|
128
|
+
*
|
|
129
|
+
* Returns "" for absent input so the caller's single truthiness check covers
|
|
130
|
+
* both "no body" and "frontmatter only".
|
|
131
|
+
*/
|
|
132
|
+
function stripSkillFrontmatter(raw) {
|
|
133
|
+
const text = (raw ?? "").trim();
|
|
134
|
+
if (!text.startsWith("---"))
|
|
135
|
+
return text;
|
|
136
|
+
// Closing fence must be its own line; a body line of "---" mid-document is
|
|
137
|
+
// not a terminator, so anchor on the newline pair.
|
|
138
|
+
const end = text.indexOf("\n---", 3);
|
|
139
|
+
if (end === -1)
|
|
140
|
+
return text; // unterminated frontmatter — inline as-is
|
|
141
|
+
return text.slice(text.indexOf("\n", end + 1) + 1).trim();
|
|
142
|
+
}
|
|
89
143
|
const NATIVE_STARTUP_MAX_CHARS = {
|
|
90
144
|
quick: 4_000,
|
|
91
145
|
standard: 8_000,
|
|
@@ -252,8 +306,7 @@ async function buildNativeSystemReadyBlock(snapshot, depth) {
|
|
|
252
306
|
`> - 🔄 **Skill sync:** ${SKILL_SYNC_STATUS_LABELS[snapshot.syncStatus]} · committed manifest${conflictSuffix}`;
|
|
253
307
|
}
|
|
254
308
|
function capNativeStartupText(text, level, requestedMaxChars, suffix = "") {
|
|
255
|
-
const
|
|
256
|
-
const maxChars = Math.max(512, Math.min(configuredLimit, requestedMaxChars ?? configuredLimit));
|
|
309
|
+
const maxChars = effectiveNativeBudget(level, requestedMaxChars);
|
|
257
310
|
if (text.length + suffix.length <= maxChars)
|
|
258
311
|
return text + suffix;
|
|
259
312
|
const marker = `\n\n… Additional ${level} context omitted to keep native startup within its display budget.`;
|
|
@@ -1262,9 +1315,32 @@ export async function sessionLoadContextHandler(args, options = {}) {
|
|
|
1262
1315
|
const overflow = matched.length - shown.length;
|
|
1263
1316
|
// Imperative, not a label: a bare list is decorative, and nothing
|
|
1264
1317
|
// else in the pipeline tells the agent to act on it.
|
|
1318
|
+
// Names are interpolated, never a placeholder. A `<skill name>`
|
|
1319
|
+
// placeholder here rendered as knowledge_search("") on a real host —
|
|
1320
|
+
// markdown/HTML display ate the angle brackets as an unknown tag.
|
|
1321
|
+
// Never emit angle brackets in text a host will render.
|
|
1265
1322
|
symptomSkillSuffix = `\n\n**Symptom-triggered skills:** ${shown.join(", ")}` +
|
|
1266
1323
|
(overflow > 0 ? `, … ${overflow} more` : "") +
|
|
1267
|
-
`\nThe first message matches these skills' trigger rules.
|
|
1324
|
+
`\nThe first message matches these skills' trigger rules. Follow them before ` +
|
|
1325
|
+
`proposing any change.\n`;
|
|
1326
|
+
// INLINE the top match's body rather than pointing at it. Naming a
|
|
1327
|
+
// skill is not delivering it: bodies reach agents only as files under
|
|
1328
|
+
// the canonical root, and hosts outside that mirror have no path to
|
|
1329
|
+
// the content — no MCP tool serves it. Three instruction rewrites
|
|
1330
|
+
// failed on that gap. Inlining removes the indirection entirely.
|
|
1331
|
+
const { readNativeSkillBody } = await import("../skillManifestSync.js");
|
|
1332
|
+
const body = stripSkillFrontmatter(await readNativeSkillBody(shown[0]));
|
|
1333
|
+
// Size against the budget this display will ACTUALLY be capped to,
|
|
1334
|
+
// not the level constant — bootstrap divides it across projects.
|
|
1335
|
+
const cap = Math.min(SYMPTOM_SKILL_INLINE_MAX, Math.floor(effectiveNativeBudget(level, options.nativeMaxChars) * SYMPTOM_SKILL_BUDGET_SHARE));
|
|
1336
|
+
// Too tight to carry a useful rule: keep the name line, which is
|
|
1337
|
+
// small, and leave the remaining budget to the session context.
|
|
1338
|
+
if (body && cap >= SYMPTOM_SKILL_INLINE_MIN) {
|
|
1339
|
+
const clipped = body.length > cap
|
|
1340
|
+
? `${body.slice(0, cap).trimEnd()}\n… (rule truncated to fit the startup budget)`
|
|
1341
|
+
: body;
|
|
1342
|
+
symptomSkillSuffix += `\n--- ${shown[0]} ---\n${clipped}\n`;
|
|
1343
|
+
}
|
|
1268
1344
|
}
|
|
1269
1345
|
}
|
|
1270
1346
|
catch (err) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "prism-mcp-server",
|
|
3
|
-
"version": "20.5.
|
|
3
|
+
"version": "20.5.2",
|
|
4
4
|
"mcpName": "io.github.dcostenco/prism-coder",
|
|
5
5
|
"description": "Prism Coder — Cognitive memory + tool-calling intelligence for AI agents. Mind Palace persistent memory (BFCL Gold Certified, 100% Tool-Call Accuracy, 114 Agent Skills, PHI Guard, Tier Enforcement, Prompt-Based Skill Routing, Zero-Search HDC/HRR retrieval, HRR Semantic Drift Detection across BCBA/Coding/AAC domains, HIPAA-hardened local or subscription-gated Synalux storage, SLERP-optimized GRPO alignment) plus the prism-coder 1.7B–32B open-weights LLM fleet.",
|
|
6
6
|
"module": "index.ts",
|