@theronap/agnoclast-mcp 0.9.182 → 0.9.183

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 +42 -17
  2. package/package.json +1 -1
package/lib/server.mjs CHANGED
@@ -89,6 +89,27 @@ export function intakeMaterializePath(mode) {
89
89
  }
90
90
 
91
91
  /** ADR-0062 §5.2. Which page governs a materialized record, and who chose it — or '' for an older server. */
92
+ // ADR-0051's refusal, rendered as INSTRUCTIONS — shared by route_record and intake_materialize so the two
93
+ // verbs cannot drift apart (the server shares OTHER_HOMES_DETAIL for the same reason). The agent is one
94
+ // call from succeeding, and the whole point of the gate is the question it asks; a bare
95
+ // "other_homes_unchecked" reads as a malfunction and gets retried verbatim.
96
+ function otherHomesRefusalText(opener) {
97
+ return [
98
+ opener,
99
+ '',
100
+ 'What OTHER pages does this record belong on? A record is usually about more than the page an',
101
+ 'identifier matched: the project it concerns, the person it is about, the decision it bears on.',
102
+ '',
103
+ ' know some already → add them to documentIds and call again',
104
+ ' none come to mind → go look first. grep or read for pages this could belong to;',
105
+ ' you know the namespace better than any matcher does',
106
+ ' looked, found none → call again with searchedForOtherHomes: true',
107
+ '',
108
+ 'Parking is still a complete answer, and a wrong attachment is still worse than none. The gate',
109
+ 'asks that you looked, not that you found something.',
110
+ ].join('\n')
111
+ }
112
+
92
113
  export function renderGoverningLine(governing) {
93
114
  if (!governing || typeof governing !== 'object') return ''
94
115
  const name = governing.title || 'a page'
@@ -1117,7 +1138,8 @@ function renderNudge(payload) {
1117
1138
  description:
1118
1139
  'Atomically publish a claimed intake item into one brain — the disclosure moment (ADR-0062). Deterministic identifier homes in that brain are always attached; documentIds may add further pages. The record is born at the strictest of its source baseline, what the producer asked for, and every attached page; a source floor (iMessage, claude-code) pins it. ' +
1119
1140
  'Name the governing page with governingPage (one of documentIds, never a person page) — otherwise the ranking picks it. ' +
1120
- 'Pass mode "preview" first when your choice differs from the claim\'s proposal: it returns the pages, the governing page and the tier this exact call would produce, and writes nothing. Never writes private intake into search/history before this call.',
1141
+ 'Pass mode "preview" first when your choice differs from the claim\'s proposal: it returns the pages, the governing page and the tier this exact call would produce, and writes nothing. Never writes private intake into search/history before this call. ' +
1142
+ '⚠ ADR-0051: a unit does not finish until you looked for its OTHER homes. A call whose documentIds add nothing beyond the deterministic homes it gets anyway is REFUSED once — name the pages this unit is about (the design it implements, the project, the person), or go look, or say you looked with searchedForOtherHomes.',
1121
1143
  inputSchema: {
1122
1144
  intakeItemId: z.string().describe('intake item uuid'),
1123
1145
  orgId: z.string().describe('destination brain org uuid'),
@@ -1136,6 +1158,12 @@ function renderNudge(payload) {
1136
1158
  mode: z.enum(['preview', 'apply']).optional().describe(
1137
1159
  '"preview": report the pages, governing page and tier this call would produce, and write nothing. Default "apply".',
1138
1160
  ),
1161
+ searchedForOtherHomes: z.boolean().optional().describe(
1162
+ 'ADR-0051. "I looked for other homes and there are genuinely none." Only consulted when documentIds add no page ' +
1163
+ 'beyond the deterministic homes. ⚠ DO NOT SEND THIS ON THE FIRST CALL AS A MATTER OF COURSE — it is the answer AFTER ' +
1164
+ 'looking, not a way past the question. Measured 2026-09-25: 30 records filed through this verb in one afternoon with no ' +
1165
+ 'judged page on any of them.',
1166
+ ),
1139
1167
  },
1140
1168
  },
1141
1169
  async (args) => {
@@ -1158,10 +1186,20 @@ function renderNudge(payload) {
1158
1186
  },
1159
1187
  attachmentMeta: (args.documentIds ?? []).map(() => ({ origin: args.origin ?? 'llm' })),
1160
1188
  ...(args.governingPage ? { governingDocumentId: args.governingPage } : {}),
1189
+ ...(args.searchedForOtherHomes ? { searchedForOtherHomes: true } : {}),
1161
1190
  }),
1162
1191
  })
1163
1192
  if (!res.ok) {
1164
1193
  const body = await res.text()
1194
+ if (res.status === 409) {
1195
+ let err = null
1196
+ try { err = JSON.parse(body)?.error } catch { /* not JSON: fall through to the raw text */ }
1197
+ if (err === 'other_homes_unchecked') {
1198
+ return toolError(otherHomesRefusalText(
1199
+ 'Not filed yet — every page named is one this unit gets anyway, so this would publish it without adding anything of your judgment.',
1200
+ ))
1201
+ }
1202
+ }
1165
1203
  if (preview && res.status === 404) {
1166
1204
  return toolError('This server cannot preview a materialization yet — nothing was written. Materialize without mode, or wait for the server to update.')
1167
1205
  }
@@ -4641,22 +4679,9 @@ function renderNudge(payload) {
4641
4679
  // agent is one call from succeeding and the whole point of the gate is the question it asks. A
4642
4680
  // bare "other_homes_unchecked" would read as a malfunction and get retried verbatim.
4643
4681
  if (!res.ok && out?.error === 'other_homes_unchecked') {
4644
- return toolError([
4645
- park
4646
- ? 'Not parked yet — before a record is filed as having no home, look for its other homes.'
4647
- : 'Not routed yet — every page you named is already attached, so this call would finish the record without adding anything.',
4648
- '',
4649
- 'What OTHER pages does this record belong on? A record is usually about more than the page an',
4650
- 'identifier matched: the project it concerns, the person it is about, the decision it bears on.',
4651
- '',
4652
- ' know some already → add them to documentIds and call again',
4653
- ' none come to mind → go look first. grep or read for pages this could belong to;',
4654
- ' you know the namespace better than any matcher does',
4655
- ' looked, found none → call again with searchedForOtherHomes: true',
4656
- '',
4657
- 'Parking is still a complete answer, and a wrong attachment is still worse than none. The gate',
4658
- 'asks that you looked, not that you found something.',
4659
- ].join('\n'))
4682
+ return toolError(otherHomesRefusalText(park
4683
+ ? 'Not parked yet — before a record is filed as having no home, look for its other homes.'
4684
+ : 'Not routed yet — every page you named is already attached, so this call would finish the record without adding anything.'))
4660
4685
  }
4661
4686
  if (!res.ok) return toolError(`Could not route record: ${out?.error ?? res.status}${out?.detail ? ` — ${out.detail}` : ''}`)
4662
4687
  if (park) return { content: [{ type: 'text', text: 'Parked. It stays visible and unrouted rather than badly attached.' }] }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@theronap/agnoclast-mcp",
3
- "version": "0.9.182",
3
+ "version": "0.9.183",
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": {