@theronap/cortex-mcp 0.9.135 → 0.9.136

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 +32 -3
  2. package/package.json +1 -1
package/lib/server.mjs CHANGED
@@ -3476,13 +3476,21 @@ function renderNudge(payload) {
3476
3476
  'route_record',
3477
3477
  {
3478
3478
  title: 'Route a record to the pages it belongs on',
3479
- description: 'Attach a pending record to the pages you judge correct — the point of the whole triage path. Use this when you have real context on what the work was; that judgment is better than any rule the webhook could run. Attachments are additive: existing deterministic homes (routing identifiers, your profile) stay. Pass park=true instead when you have looked and there is genuinely no good home — parking beats attaching to a page that merely shares a word.',
3479
+ description: 'Attach a pending record to the pages you judge correct — the point of the whole triage path. Use this when you have real context on what the work was; that judgment is better than any rule the webhook could run. Attachments are additive: existing deterministic homes (routing identifiers, your profile) stay. Pass park=true instead when you have looked and there is genuinely no good home — parking beats attaching to a page that merely shares a word. \u26a0 ADR-0051: a record does not finish until somebody looked for its OTHER homes. A call that would end the record without adding a page it did not already have \u2014 a park, or a route naming only pages already attached \u2014 is REFUSED once, with instructions. Name the other pages, or go look, or say you looked.',
3480
3480
  inputSchema: {
3481
3481
  recordId: z.string().describe('record id from pending_records'),
3482
3482
  documentIds: z.array(z.string()).optional().describe('pages to attach. Accepts EITHER a brain_documents id (from a pending_records sweep) OR the `ref:` that read_page prints for a page — they are different columns and read_page only ever gives you the second, which is why this used to fail as no_documents on ids that looked perfectly valid. A ref resolves to that node\'s current page, accessible copy first. Anything that resolves to no page in this brain is named back to you rather than silently dropped.'),
3483
3483
  reason: z.string().describe('why these pages — recorded with the attachment'),
3484
3484
  park: z.boolean().optional().describe('no good home exists; leave it alone rather than guessing'),
3485
3485
  tier: z.number().int().optional().describe('2 when this came from the cheap sweep rather than your own context'),
3486
+ searchedForOtherHomes: z.boolean().optional().describe(
3487
+ 'ADR-0051. "I looked for other homes and there are genuinely none." Only consulted when the call '
3488
+ + 'would otherwise finish the record without adding a page it did not already have — so a route that '
3489
+ + 'attaches something new never needs it, and a park always does. '
3490
+ + '⚠ DO NOT SEND THIS ON THE FIRST CALL AS A MATTER OF COURSE. It is the answer AFTER looking, not a '
3491
+ + 'way past the question. The gate exists because a session-start instruction to route converted at '
3492
+ + '11% over 21 days; sending this reflexively rebuilds exactly the failure it was measured against.',
3493
+ ),
3486
3494
  identifierDispositions: z.array(z.any()).optional().describe(
3487
3495
  'ADR-0044. REQUIRED when this record carries identifiers no page claims — the refusal names exactly which. '
3488
3496
  + 'One entry per unresolved identifier, and this is a DIFFERENT question from documentIds. '
@@ -3501,13 +3509,13 @@ function renderNudge(payload) {
3501
3509
  ),
3502
3510
  },
3503
3511
  },
3504
- async ({ recordId, documentIds, reason, park, tier, identifierDispositions }) => {
3512
+ async ({ recordId, documentIds, reason, park, tier, identifierDispositions, searchedForOtherHomes }) => {
3505
3513
  let res
3506
3514
  try {
3507
3515
  res = await fetchCortex(`${BASE}/api/brain/triage`, {
3508
3516
  method: 'POST',
3509
3517
  headers: { Authorization: `Bearer ${TOKEN}`, 'Content-Type': 'application/json' },
3510
- body: JSON.stringify({ action: park ? 'park' : 'route', recordId, documentIds, reason, tier, identifierDispositions }),
3518
+ body: JSON.stringify({ action: park ? 'park' : 'route', recordId, documentIds, reason, tier, identifierDispositions, searchedForOtherHomes }),
3511
3519
  })
3512
3520
  } catch (e) {
3513
3521
  return toolError(`Could not route record: ${e.message}`)
@@ -3539,6 +3547,27 @@ function renderNudge(payload) {
3539
3547
  }
3540
3548
  return toolError(lines.join('\n'))
3541
3549
  }
3550
+ // ADR-0051 refusal. Rendered as INSTRUCTIONS for the same reason the ADR-0044 one above is: the
3551
+ // agent is one call from succeeding and the whole point of the gate is the question it asks. A
3552
+ // bare "other_homes_unchecked" would read as a malfunction and get retried verbatim.
3553
+ if (!res.ok && out?.error === 'other_homes_unchecked') {
3554
+ return toolError([
3555
+ park
3556
+ ? 'Not parked yet — before a record is filed as having no home, look for its other homes.'
3557
+ : 'Not routed yet — every page you named is already attached, so this call would finish the record without adding anything.',
3558
+ '',
3559
+ 'What OTHER pages does this record belong on? A record is usually about more than the page an',
3560
+ 'identifier matched: the project it concerns, the person it is about, the decision it bears on.',
3561
+ '',
3562
+ ' know some already → add them to documentIds and call again',
3563
+ ' none come to mind → go look first. grep or read for pages this could belong to;',
3564
+ ' you know the namespace better than any matcher does',
3565
+ ' looked, found none → call again with searchedForOtherHomes: true',
3566
+ '',
3567
+ 'Parking is still a complete answer, and a wrong attachment is still worse than none. The gate',
3568
+ 'asks that you looked, not that you found something.',
3569
+ ].join('\n'))
3570
+ }
3542
3571
  if (!res.ok) return toolError(`Could not route record: ${out?.error ?? res.status}${out?.detail ? ` — ${out.detail}` : ''}`)
3543
3572
  if (park) return { content: [{ type: 'text', text: 'Parked. It stays visible and unrouted rather than badly attached.' }] }
3544
3573
  return { content: [{ type: 'text', text: `Routed — attached to ${out?.attached?.length ?? 0} page(s). Recorded as a session judgment, not a rule match.` }] }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@theronap/cortex-mcp",
3
- "version": "0.9.135",
3
+ "version": "0.9.136",
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": {