@theronap/agnoclast-mcp 0.9.170 → 0.9.172
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 +93 -1
- package/package.json +1 -1
package/lib/server.mjs
CHANGED
|
@@ -2165,7 +2165,24 @@ function renderNudge(payload) {
|
|
|
2165
2165
|
// managed to list these rows for weeks without making one of them actionable.
|
|
2166
2166
|
lines.push(` ${a.stagedId}`)
|
|
2167
2167
|
lines.push(` ${a.title ?? '(no subject)'} · ${a.sourceType} · ${a.occurredAt?.slice(0, 10) ?? ''}`)
|
|
2168
|
-
|
|
2168
|
+
// 🔴 A QUESTION IS ALREADY OUT — say so FIRST, before any "held because" reason.
|
|
2169
|
+
//
|
|
2170
|
+
// Caught by using the feature once on real data, 2026-09-24, minutes after shipping it:
|
|
2171
|
+
// `staged_defer` wrote the question, `intake_cleanup_status` showed it to the owner, and
|
|
2172
|
+
// this listing rendered the arrival IDENTICALLY to three nobody had ever looked at. So the
|
|
2173
|
+
// next session would judge it from scratch and hit the same wall — which is the exact
|
|
2174
|
+
// failure the defer verb was built to end.
|
|
2175
|
+
//
|
|
2176
|
+
// ⚠ AND IT WAS IN THE API RESPONSE THE WHOLE TIME. `listStagedArrivals` returns
|
|
2177
|
+
// `deferredQuestion`, with an engine test asserting it. The test passed. What was missing
|
|
2178
|
+
// was a reader — the eighth instance of that shape this week, written into the very change
|
|
2179
|
+
// whose stated purpose was avoiding it. An engine-level test cannot catch it, because the
|
|
2180
|
+
// engine is not the surface anyone reads.
|
|
2181
|
+
if (a.deferredQuestion) {
|
|
2182
|
+
const asked = String(a.deferredQuestion.askedAt ?? '').slice(0, 10)
|
|
2183
|
+
lines.push(` ⏸ ASKED THE OWNER${asked ? ` on ${asked}` : ''} — do not re-judge this one: "${a.deferredQuestion.prompt}"`)
|
|
2184
|
+
lines.push(' Still pending on purpose: it needs placing once they answer. Their answer is in `intake_cleanup_status.openQuestions`.')
|
|
2185
|
+
} else if (a.blockingIdentifiers?.length) {
|
|
2169
2186
|
lines.push(` blocked by: ${a.blockingIdentifiers.join(', ')}`)
|
|
2170
2187
|
} else {
|
|
2171
2188
|
lines.push(' held because no brain could be determined (no unknown identifiers)')
|
|
@@ -4089,6 +4106,81 @@ function renderNudge(payload) {
|
|
|
4089
4106
|
},
|
|
4090
4107
|
)
|
|
4091
4108
|
|
|
4109
|
+
// ⭐ THE OTHER HALF OF set_routing_identifier (2026-09-24). A claim was ONE CALL to create and,
|
|
4110
|
+
// through this server, impossible to remove — so a wrong brain decision was permanent. That
|
|
4111
|
+
// contradicts the thing ADR-0066 is built on: filing is a judgment, and judgments get revised.
|
|
4112
|
+
//
|
|
4113
|
+
// ⚠ THE ENGINE AND THE ENDPOINT BOTH ALREADY EXISTED. `clearPageRoutingIdentifier` and the DELETE
|
|
4114
|
+
// on /api/brain/routing-identifiers have been there the whole time; only the agent-facing verb was
|
|
4115
|
+
// missing, so the capability was real and unreachable. Ninth instance of that shape this week.
|
|
4116
|
+
server.registerTool(
|
|
4117
|
+
'unclaim_routing_identifier',
|
|
4118
|
+
{
|
|
4119
|
+
title: 'Remove a routing claim from a page',
|
|
4120
|
+
description:
|
|
4121
|
+
'Remove an identifier claim from a page — the counterpart to `set_routing_identifier`. Use it when a claim sends mail to the wrong brain, when a page was claimed by mistake, or when TWO brains claim the same key and you want one of them to stop (that is the case where nothing routes at all, because a split stages rather than picking). ' +
|
|
4122
|
+
'⭐ THE RESPONSE TELLS YOU WHAT ROUTING DOES NOW, which is the reason to call it: removing a claim moves the identifier between "routes", "stages because nothing claims it" and "stages because several do", and the delete count alone cannot tell you which. Computed with the router\'s own logic. ' +
|
|
4123
|
+
'⚠ HISTORY IS NOT UNDONE BY DEFAULT. `set_routing_identifier` HEALS history — it attaches every record already carrying the identifier — but removing the claim leaves those attachments in place, and the response says how many. That asymmetry is deliberate: a claim is a ROUTING RULE, an attachment is a RECORD\'S PLACEMENT, and one such claim was measured accounting for 1,271 attachments on a single page. Pass `detach: true` only when you mean to unfile them too; it never touches a governing attachment, or one a person or an LLM made rather than this claim. ' +
|
|
4124
|
+
'⚠ REVERSIBLE BY CONSTRUCTION: `set_routing_identifier` puts it back in one call and re-heals history. No content is destroyed here, which is why this needs no dry run.',
|
|
4125
|
+
inputSchema: {
|
|
4126
|
+
kind: z.enum(['project', 'person', 'org', 'user']).describe('the page kind'),
|
|
4127
|
+
name: z.string().optional().describe('page title (or pass ref)'),
|
|
4128
|
+
ref: z.string().optional().describe('node ref from read_page — prefer over name when available'),
|
|
4129
|
+
brain: z.string().optional().describe('brain label when the title is ambiguous across brains — REQUIRED in practice when two brains hold a page of the same name, which is the usual reason you are here'),
|
|
4130
|
+
identifier: z.string().describe('the claim to remove, e.g. email:someone@example.com or repo:owner/name. See `my_routing_claims` for what is claimed where'),
|
|
4131
|
+
detach: z.boolean().optional().describe('also remove the attachments this claim made (default false). Leaves governing attachments and anything a person or LLM attached'),
|
|
4132
|
+
},
|
|
4133
|
+
},
|
|
4134
|
+
async ({ kind, name, ref, brain, identifier, detach }) => {
|
|
4135
|
+
if (!name && !ref) return toolError('Pass name or ref')
|
|
4136
|
+
let res
|
|
4137
|
+
try {
|
|
4138
|
+
res = await fetchCortex(`${BASE}/api/brain/routing-identifiers`, {
|
|
4139
|
+
method: 'DELETE',
|
|
4140
|
+
headers: { Authorization: `Bearer ${TOKEN}`, 'Content-Type': 'application/json' },
|
|
4141
|
+
body: JSON.stringify({ kind, name, ref, brain, identifier, detach: detach === true }),
|
|
4142
|
+
})
|
|
4143
|
+
} catch (e) {
|
|
4144
|
+
return toolError(`Could not remove the routing claim: ${e.message}`)
|
|
4145
|
+
}
|
|
4146
|
+
const out = await res.json().catch(() => null)
|
|
4147
|
+
if (!res.ok) {
|
|
4148
|
+
if (out?.candidates?.length) {
|
|
4149
|
+
return toolError(`That page title is ambiguous across brains — pass \`brain\`. Candidates: ${out.candidates.join(', ')}`)
|
|
4150
|
+
}
|
|
4151
|
+
return toolError(`Could not remove the routing claim: ${out?.error ?? res.status}`)
|
|
4152
|
+
}
|
|
4153
|
+
// A ZERO IS A VERDICT, NOT AN ERROR — the same convention my_routing_claims states. "No such
|
|
4154
|
+
// claim" is worth saying plainly, because the usual cause is that the claim is on a DIFFERENT
|
|
4155
|
+
// page (or in a different brain) than the caller assumed, and retrying the same call will not
|
|
4156
|
+
// find it either.
|
|
4157
|
+
if (!out?.removed) {
|
|
4158
|
+
return {
|
|
4159
|
+
content: [{
|
|
4160
|
+
type: 'text',
|
|
4161
|
+
text: `No claim on ${identifier} was found on that page, so nothing changed. That usually means the claim lives on a different page or in another brain — \`my_routing_claims ${identifier}\` shows every page that claims it.`,
|
|
4162
|
+
}],
|
|
4163
|
+
}
|
|
4164
|
+
}
|
|
4165
|
+
const left = typeof out?.attachmentsLeft === 'number' ? out.attachmentsLeft : null
|
|
4166
|
+
const historyLine = out?.detached
|
|
4167
|
+
? `\n ${out.detached} attachment(s) this claim had made were also removed; governing attachments and anything a person or an LLM attached were left alone.`
|
|
4168
|
+
: left
|
|
4169
|
+
? `\n ⚠ ${left} record(s) this claim previously attached to that page are STILL attached — removing the rule does not unfile history. Re-run with \`detach: true\` if they should come off too.`
|
|
4170
|
+
: ''
|
|
4171
|
+
return {
|
|
4172
|
+
content: [{
|
|
4173
|
+
type: 'text',
|
|
4174
|
+
text:
|
|
4175
|
+
`Removed the claim on ${out?.identifier ?? identifier}.\n` +
|
|
4176
|
+
` ➜ ${out?.routing?.verdict ?? '(routing outcome unavailable)'}` +
|
|
4177
|
+
historyLine +
|
|
4178
|
+
`\n Reversible: \`set_routing_identifier\` puts it back and re-attaches history.`,
|
|
4179
|
+
}],
|
|
4180
|
+
}
|
|
4181
|
+
},
|
|
4182
|
+
)
|
|
4183
|
+
|
|
4092
4184
|
// ── Gate 4 record triage, over timeline_claims ────────────────────────────────────────────
|
|
4093
4185
|
// A connector event materializes in seconds and has no idea what the work WAS. The session that
|
|
4094
4186
|
// did the work knows exactly, and arrives later. These three tools are that handoff: look at what
|
package/package.json
CHANGED