@theronap/agnoclast-mcp 0.9.208 → 0.9.209

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.
Files changed (2) hide show
  1. package/lib/server.mjs +44 -6
  2. package/package.json +1 -1
package/lib/server.mjs CHANGED
@@ -5515,9 +5515,10 @@ function renderNudge(payload) {
5515
5515
  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.'),
5516
5516
  to: z.string().describe('who becomes the owner — an org member\'s email, or their user id. Must be an ACTIVE member of the same brain as the page. Pass your own address to take ownership yourself.'),
5517
5517
  confirm_audience: z.string().optional().describe('ONLY after a refusal that lists who would start reading the page. A reports / peers / down page is addressed to its OWNER\'s team, so a new owner means a new audience; the first call is refused with the names and a confirm token. Pass that token here once the person asking has seen the list and means it. The token covers exactly that list — if the page or the org chart changed since, it is refused again with the new one. Never guess it and never send it on a first call.'),
5518
+ tier_decision: z.enum(['keep', 'delegate', 'accessible', 'down', 'reports', 'peers', 'scoped', 'confidential']).optional().describe('REQUIRED for a reports / peers / down page (Theron, 2026-10-03: whoever hands one over re-evaluates its tier then and there). "keep" = same tier, re-aimed at the new owner\'s team; a tier = owner and tier change together; "delegate" = the new owner decides, and until they do NOTHING about the page changes — same owner, same readers — while they may read it to decide. Ask the person which they want; never pick one for them. Omit it for any other tier (it transfers as before).'),
5518
5519
  },
5519
5520
  },
5520
- async ({ kind, name, ref, to, confirm_audience }) => {
5521
+ async ({ kind, name, ref, to, confirm_audience, tier_decision }) => {
5521
5522
  let res
5522
5523
  try {
5523
5524
  res = await fetchCortex(`${BASE}/api/brain/node-policy`, {
@@ -5525,7 +5526,7 @@ function renderNudge(payload) {
5525
5526
  headers: { Authorization: `Bearer ${TOKEN}`, 'Content-Type': 'application/json' },
5526
5527
  // No `tier`: a node has exactly one page (ADR-0064 §4), and the route resolves it. Sending a
5527
5528
  // guessed tier is how this used to 404 on precisely the scoped/confidential pages it matters for.
5528
- body: JSON.stringify({ kind, ...(name ? { name } : {}), ...(ref ? { ref } : {}), transfer_to: to, ...(confirm_audience ? { confirm_audience } : {}) }),
5529
+ body: JSON.stringify({ kind, ...(name ? { name } : {}), ...(ref ? { ref } : {}), transfer_to: to, ...(confirm_audience ? { confirm_audience } : {}), ...(tier_decision ? { tier_decision } : {}) }),
5529
5530
  })
5530
5531
  } catch (e) {
5531
5532
  return toolError(`Could not transfer ownership: ${e.message}`)
@@ -5538,10 +5539,21 @@ function renderNudge(payload) {
5538
5539
  const d = classify(res.status, res.headers.get('content-type'), '', res.headers.get('x-vercel-id'))
5539
5540
  return toolError(`Could not transfer ownership: ${d.message}`)
5540
5541
  }
5542
+ if (out?.pending_for) {
5543
+ return {
5544
+ content: [{
5545
+ type: 'text',
5546
+ text: `Handed over for re-evaluation — ${to} decides this page's tier. Nothing about the page has changed: it is still ${out.tier}, owned and read exactly as before, until they choose.${out.reader_granted ? ' They were given read access so they can decide.' : ''} They see it at their next session start and complete it with confirm_page_ownership.`,
5547
+ }],
5548
+ }
5549
+ }
5550
+ const retier = out?.retiered
5551
+ ? ` It moved from ${out.retiered.from} to ${out.retiered.to} in the same step${out.retiered.cascaded_records ? `; ${out.retiered.cascaded_records} record(s) filed under it followed the narrowing` : ''}.`
5552
+ : ` The page is still ${out.tier}${tier_decision === 'keep' ? ', now addressed to its new owner\'s team' : '; transferring at this tier does not re-tier, so check that its audience is still right'}.`
5541
5553
  return {
5542
5554
  content: [{
5543
5555
  type: 'text',
5544
- text: `Done — ${name ?? out.ref} now belongs to ${to}. The page is still ${out.tier}; transferring never re-tiers, so check that its audience is still right.${renderAudienceChange(out.audience_change)}`,
5556
+ text: `Done — ${name ?? out.ref} now belongs to ${to}.${retier}${renderAudienceChange(out.audience_change)}`,
5545
5557
  }],
5546
5558
  }
5547
5559
  },
@@ -5555,13 +5567,39 @@ function renderNudge(payload) {
5555
5567
  'confirm_page_ownership',
5556
5568
  {
5557
5569
  title: 'Confirm pages handed to you',
5558
- description: 'When someone hands you a page — a colleague using transfer_page_ownership, or a steward rehoming a departed member\'s page — your session start lists it under "Pages handed to you" until you confirm it. Pass the page `refs` (as listed there) to confirm those, or `all: true` for every open one. Confirming only clears the notice: the page is already yours. To pass one on instead, use transfer_page_ownership.',
5570
+ description: 'When someone hands you a page — a colleague using transfer_page_ownership, or a steward rehoming a departed member\'s page — your session start lists it under "Pages handed to you" until you confirm it. Pass the page `refs` (as listed there) to confirm those, or `all: true` for every open one. Confirming only clears the notice: the page is already yours. To pass one on instead, use transfer_page_ownership. A page listed under "Pages waiting on your tier decision" is DIFFERENT: it is not yours yet, and stays exactly as it was until you choose. Take it with `ref` + `tier_decision` ("keep" its tier, re-aimed at your team, or a tier); `all` never takes those.',
5559
5571
  inputSchema: {
5560
5572
  refs: z.array(z.string()).optional().describe('page refs, as listed under "Pages handed to you"'),
5561
- all: z.boolean().optional().describe('true = confirm every open notice'),
5573
+ all: z.boolean().optional().describe('true = confirm every open notice (never takes a page waiting on your tier decision)'),
5574
+ ref: z.string().optional().describe('ONE page waiting on your tier decision, as listed under "Pages waiting on your tier decision"'),
5575
+ tier_decision: z.enum(['keep', 'accessible', 'down', 'reports', 'peers', 'scoped', 'confidential']).optional().describe('with `ref`: "keep" its tier (re-aimed at your team) or the tier it should have. Ask the person; never choose for them.'),
5576
+ confirm_audience: z.string().optional().describe('ONLY after a refusal listing who would start reading the page — the token from it, once the person has seen the list and means it.'),
5562
5577
  },
5563
5578
  },
5564
- async ({ refs, all }) => {
5579
+ async ({ refs, all, ref, tier_decision, confirm_audience }) => {
5580
+ if (ref || tier_decision) {
5581
+ if (!ref || !tier_decision) return toolError('To take a page waiting on your tier decision, pass both `ref` and `tier_decision`.')
5582
+ let res
5583
+ try {
5584
+ res = await fetchCortex(`${BASE}/api/brain/ownership-notices`, {
5585
+ method: 'POST',
5586
+ headers: { Authorization: `Bearer ${TOKEN}`, 'Content-Type': 'application/json' },
5587
+ body: JSON.stringify({ ref, tier_decision, ...(confirm_audience ? { confirm_audience } : {}) }),
5588
+ })
5589
+ } catch (e) {
5590
+ return toolError(`Could not take the page: ${e.message}`)
5591
+ }
5592
+ const out = await res.json().catch(() => null)
5593
+ if (!res.ok) {
5594
+ if (out?.error) return toolError(`Could not take the page: ${out.error}`)
5595
+ const d = classify(res.status, res.headers.get('content-type'), '', res.headers.get('x-vercel-id'))
5596
+ return toolError(`Could not take the page: ${d.message}`)
5597
+ }
5598
+ const moved = out?.from_tier && out.from_tier !== out.tier
5599
+ ? ` It moved from ${out.from_tier} to ${out.tier}${out.cascaded_records ? `; ${out.cascaded_records} record(s) filed under it followed the narrowing` : ''}.`
5600
+ : ` It stays ${out?.tier}, now addressed to your team.`
5601
+ return { content: [{ type: 'text', text: `Done — the page is yours.${moved}${renderAudienceChange(out?.audience_change)}` }] }
5602
+ }
5565
5603
  if (!all && !(refs && refs.length)) return toolError('Pass `refs` (the pages to confirm) or `all: true`.')
5566
5604
  let res
5567
5605
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@theronap/agnoclast-mcp",
3
- "version": "0.9.208",
3
+ "version": "0.9.209",
4
4
  "description": "Connect your AI assistant to Cortex — your org's projects, activity, gaps, and directives, scoped to you.",
5
5
  "type": "module",
6
6
  "bin": {