@theronap/cortex-mcp 0.9.65 → 0.9.66
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/lib/server.mjs +24 -11
- package/package.json +1 -1
package/lib/server.mjs
CHANGED
|
@@ -557,6 +557,12 @@ export async function runServer(version) {
|
|
|
557
557
|
// word "today" from sections it was never meant to touch. This footer renders on EVERY
|
|
558
558
|
// page read in the system, so it was the single widest surface pointing the wrong way.
|
|
559
559
|
let footer = `— Follow any [[links]] above with read_page to go deeper.\n— If you hold fresher FIRST-HAND truth than this page — something you established THIS session (ran the command, made the change, hold the artifact) that a section here gets concretely wrong — fix just that passage with \`edit_page\`: quote the wrong text as old_string and pass this page's \`version\` as base_version (any node you are permitted to edit; concise; a material contradiction, not merely extra detail). If the save comes back stale the page changed under you — the conflict hands back what changed, so re-anchor from that instead of re-reading. Reading a stale page you can fix IS the trigger to fix it. Editing is pre-authorized — do NOT ask the user before updating (edits are versioned + reversible via page_history/rollback_page); update, then briefly report it. Use \`author\` only to CREATE a page or rewrite one wholesale: it re-emits every section, so untouched sections get retyped on the way through and drift.\n— Citing code? Use a SYMBOL and file (\`formConnections\` in \`web/app/api/ingest/route.ts\`), never a line number — line numbers drift with every commit above them. And cite only what you opened THIS session; re-emitting a reference you read on another page is how a stale claim gains a second source and starts looking corroborated.`
|
|
560
|
+
// ADDRESSING (2026-08-04). The server accepts `ref` on every page/node route and its ambiguity
|
|
561
|
+
// 409s hand refs back — but read_page never PRINTED one, so the only way to obtain a ref was to
|
|
562
|
+
// trigger the error first. That made ID addressing reachable in principle and unusable in
|
|
563
|
+
// practice. The ref is per-NODE (engram_ref) while `version` is per-TIER, which is why it rides
|
|
564
|
+
// on the page header rather than inside a tier block.
|
|
565
|
+
if (m.ref) footer += `\n— \`ref:\` above is this page's stable id. Pass it as \`ref\` to any page tool (set_page_privacy, page grants, node policy, author, rollback, node timeline) to address THIS page: refs are unique across brains, so two pages sharing a name in different brains cannot collide and no active-brain guess is involved. Prefer it over \`name\` whenever you already hold one.`
|
|
560
566
|
// slice 4: when the page carries identifier stamps, the history projection is one flag away.
|
|
561
567
|
const allBody = m.tiers.flatMap((t) => (t.sections ?? []).map((s) => s.body)).join('\n')
|
|
562
568
|
const stamps = [...new Set((allBody.match(/\[\[repo:[a-z0-9][a-z0-9-]*\/[a-z0-9_.-]+\]\]/gi) ?? []).map((s) => s.toLowerCase()))]
|
|
@@ -568,7 +574,10 @@ export async function runServer(version) {
|
|
|
568
574
|
// saw the footer, and repaired the page. A dashboard nobody opens would not have.
|
|
569
575
|
if (m.backlog?.nudge) footer += `\n— ${m.backlog.nudge}`
|
|
570
576
|
const brainTag = tagBrain ? ` · brain: ${m.brain}` : ''
|
|
571
|
-
|
|
577
|
+
// Always emitted, not only in the multi-brain case: the ref is what makes the brain question
|
|
578
|
+
// moot, so withholding it until brains collide is exactly backwards.
|
|
579
|
+
const refLine = m.ref ? `\nref: ${m.ref}` : ''
|
|
580
|
+
return `# ${m.title ?? name} (full authored page${brainTag})${refLine}\n\n${blocks.join('\n\n---\n\n')}\n\n${footer}`
|
|
572
581
|
}
|
|
573
582
|
if (matches.length === 1) {
|
|
574
583
|
return { content: [{ type: 'text', text: renderMatch(matches[0], false) }] }
|
|
@@ -1400,20 +1409,21 @@ export async function runServer(version) {
|
|
|
1400
1409
|
description: 'Re-tier a wiki page you own or may edit: "accessible" (anyone in the org), "scoped" (owner + their management chain), or "confidential" (owner only, plus explicit grants). Demoting a PROJECT page also demotes its evidence records (demote-only; each record\'s owner is notified and can revert). Promotions never touch records. If the target tier already has a page, merge your content into it via `author` FIRST, then read_page the target again to get its fresh version, then re-run this with absorb=true and target_version=<that version> — absorb will REJECT (not silently drop content) if target_version doesn\'t match what\'s actually there, so a merge that didn\'t really land can\'t destroy your source page. Org admins may demote any page, never promote.',
|
|
1401
1410
|
inputSchema: {
|
|
1402
1411
|
kind: z.enum(['project', 'person', 'org', 'user']).describe('the page kind'),
|
|
1403
|
-
name: z.string().describe('the exact page name'),
|
|
1412
|
+
name: z.string().optional().describe('the exact page name (or pass `ref` instead — one of the two is required)'),
|
|
1413
|
+
ref: z.string().optional().describe('the page\'s stable id, printed as `ref:` by read_page. PREFER THIS over name when you have it: a ref is unique across brains, so it addresses exactly one page and never needs a brain to disambiguate it.'),
|
|
1404
1414
|
tier: z.enum(['accessible', 'scoped', 'confidential']).describe('the new visibility tier'),
|
|
1405
1415
|
source_tier: z.enum(['accessible', 'scoped', 'confidential']).optional().describe('when the node has multiple tier variants: which one to move'),
|
|
1406
1416
|
absorb: z.boolean().optional().describe('after merging your content into an existing target-tier page via author: true removes your now-absorbed source variant. Requires target_version.'),
|
|
1407
1417
|
target_version: z.string().optional().describe('REQUIRED with absorb=true — the target page\'s version, read via read_page AFTER your author() merge landed. Proves the merge actually happened before your source page is deleted; a stale or guessed value is rejected, not silently accepted.'),
|
|
1408
1418
|
},
|
|
1409
1419
|
},
|
|
1410
|
-
async ({ kind, name, tier, source_tier, absorb, target_version }) => {
|
|
1420
|
+
async ({ kind, name, ref, tier, source_tier, absorb, target_version }) => {
|
|
1411
1421
|
let res
|
|
1412
1422
|
try {
|
|
1413
1423
|
res = await fetchCortex(`${BASE}/api/brain/page-privacy`, {
|
|
1414
1424
|
method: 'POST',
|
|
1415
1425
|
headers: { Authorization: `Bearer ${TOKEN}`, 'Content-Type': 'application/json' },
|
|
1416
|
-
body: JSON.stringify({ kind, name, tier, ...(source_tier ? { source_tier } : {}), ...(absorb ? { absorb: true } : {}), ...(target_version ? { target_version } : {}) }),
|
|
1426
|
+
body: JSON.stringify({ kind, ...(name ? { name } : {}), ...(ref ? { ref } : {}), tier, ...(source_tier ? { source_tier } : {}), ...(absorb ? { absorb: true } : {}), ...(target_version ? { target_version } : {}) }),
|
|
1417
1427
|
})
|
|
1418
1428
|
} catch (e) {
|
|
1419
1429
|
return toolError(`Could not set page privacy: ${e.message}`)
|
|
@@ -1445,19 +1455,20 @@ export async function runServer(version) {
|
|
|
1445
1455
|
description: 'Share one of YOUR non-accessible wiki pages with a specific org member (or take that access back). A grant lets exactly that person read the page even though its tier would hide it — the escape hatch for "confidential, but Dana needs it". Owner-only. Grants survive re-tiering: revoke them when they should end.',
|
|
1446
1456
|
inputSchema: {
|
|
1447
1457
|
kind: z.enum(['project', 'person', 'org', 'user']).describe('the page kind'),
|
|
1448
|
-
name: z.string().describe('the exact page name'),
|
|
1449
|
-
|
|
1458
|
+
name: z.string().optional().describe('the exact page name (or pass `ref` instead — one of the two is required)'),
|
|
1459
|
+
ref: z.string().optional().describe('the page\'s stable id, printed as `ref:` by read_page. Prefer this over name: unique across brains, so it addresses exactly one page.'),
|
|
1460
|
+
grantee: z.string().describe('display name or email of a member OF THE PAGE\'S BRAIN (must resolve uniquely — use email if ambiguous)'),
|
|
1450
1461
|
action: z.enum(['grant', 'revoke']).describe('grant or revoke'),
|
|
1451
1462
|
tier: z.enum(['scoped', 'confidential']).optional().describe('which variant (default: the most restrictive one)'),
|
|
1452
1463
|
},
|
|
1453
1464
|
},
|
|
1454
|
-
async ({ kind, name, grantee, action, tier }) => {
|
|
1465
|
+
async ({ kind, name, ref, grantee, action, tier }) => {
|
|
1455
1466
|
let res
|
|
1456
1467
|
try {
|
|
1457
1468
|
res = await fetchCortex(`${BASE}/api/brain/page-grants`, {
|
|
1458
1469
|
method: 'POST',
|
|
1459
1470
|
headers: { Authorization: `Bearer ${TOKEN}`, 'Content-Type': 'application/json' },
|
|
1460
|
-
body: JSON.stringify({ kind, name, grantee, action, ...(tier ? { tier } : {}) }),
|
|
1471
|
+
body: JSON.stringify({ kind, ...(name ? { name } : {}), ...(ref ? { ref } : {}), grantee, action, ...(tier ? { tier } : {}) }),
|
|
1461
1472
|
})
|
|
1462
1473
|
} catch (e) {
|
|
1463
1474
|
return toolError(`Could not ${action}: ${e.message}`)
|
|
@@ -1476,13 +1487,15 @@ export async function runServer(version) {
|
|
|
1476
1487
|
description: 'Show every explicit access grant on YOUR page\'s tier variants (owner-only). Use after re-tiering a page — grants survive tier changes and keep granting until revoked.',
|
|
1477
1488
|
inputSchema: {
|
|
1478
1489
|
kind: z.enum(['project', 'person', 'org', 'user']).describe('the page kind'),
|
|
1479
|
-
name: z.string().describe('the exact page name'),
|
|
1490
|
+
name: z.string().optional().describe('the exact page name (or pass `ref` instead — one of the two is required)'),
|
|
1491
|
+
ref: z.string().optional().describe('the page\'s stable id, printed as `ref:` by read_page. Prefer this over name: unique across brains, so it addresses exactly one page.'),
|
|
1480
1492
|
},
|
|
1481
1493
|
},
|
|
1482
|
-
async ({ kind, name }) => {
|
|
1494
|
+
async ({ kind, name, ref }) => {
|
|
1483
1495
|
let res
|
|
1496
|
+
const addr = ref ? `ref=${encodeURIComponent(ref)}` : `name=${encodeURIComponent(name ?? '')}`
|
|
1484
1497
|
try {
|
|
1485
|
-
res = await fetchCortex(`${BASE}/api/brain/page-grants?kind=${encodeURIComponent(kind)}
|
|
1498
|
+
res = await fetchCortex(`${BASE}/api/brain/page-grants?kind=${encodeURIComponent(kind)}&${addr}`, {
|
|
1486
1499
|
headers: { Authorization: `Bearer ${TOKEN}` },
|
|
1487
1500
|
})
|
|
1488
1501
|
} catch (e) {
|