@theronap/agnoclast-mcp 0.9.164 → 0.9.166

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 +61 -4
  2. package/package.json +1 -1
package/lib/server.mjs CHANGED
@@ -285,6 +285,48 @@ function renderArrival(payload) {
285
285
  ` — \`staged_records\` lists them. Not offered to you, just counted; \`ignore_arrivals\` if this is noise right now.`
286
286
  }
287
287
 
288
+ function renderSettling(payload) {
289
+ const feed = payload?.settling
290
+ const offers = Array.isArray(feed?.offers) ? feed.offers : []
291
+ if (!offers.length) return ''
292
+ const total = Number(feed?.total ?? offers.length)
293
+
294
+ const lines = offers.map((x) => {
295
+ const why = x.via?.length ? ` — matches ${x.via.map((v) => `\`${v}\``).join(', ')}` : ''
296
+ // Looks without claims is evidence about the UNIT, not about the sessions that walked away.
297
+ const looked = x.looks > 0 ? ` [looked at ${x.looks}x, still unclaimed]` : ''
298
+ return ` - ${x.source} · ${x.hoursAgo}h ago · ${x.intakeItemId}${why}${looked}`
299
+ })
300
+
301
+ // ⚠ SAY THE TRUE TOTAL, NOT THE PAGE SIZE. Measured 2026-09-22: one identifier matched 113 settling
302
+ // units against a cap of 12. A block that says "here are 12" when there are 113 is claiming
303
+ // certainty it does not have — and renders identically every turn, which teaches the reader that
304
+ // it never changes and can be skipped.
305
+ const more = total > offers.length
306
+ ? `\n— Showing ${offers.length} of ${total}. \`intake_changes\` lists the rest; this is a sample, not the queue.`
307
+ : ''
308
+
309
+ // ⚠ CONTENTION IS STATED, exactly as the record nudge states `k`. Measured the same day: THREE live
310
+ // sessions each matched the SAME 113 units on one identifier. Silent about that, each would claim
311
+ // the same units and learn about the race from a failed lease.
312
+ const maxHolders = offers.reduce((m, x) => Math.max(m, Number(x.holders ?? 1)), 1)
313
+ const who = maxHolders > 1
314
+ ? `\n— ⚠ ${maxHolders} live sessions of yours hold a matching identifier, so these are offered to all of them. \`intake_claim\` takes a lease — claim before you work, not after, or two sessions judge the same arrival.`
315
+ : `\n— No other live session of yours holds a matching identifier, so nobody else is looking at these.`
316
+
317
+ return `\n\n⚡ ARRIVALS SETTLING — yours alone, and no one else can see these yet:\n${lines.join('\n')}\n` +
318
+ `— These are unjudged ARRIVALS, not records. Since automatic materialization was deleted, nothing files them for you.\n` +
319
+ `— \`intake_look <id>\` reads the full body without claiming it — looking is free, non-exclusive and logged, so look rather than guess from the headline.\n` +
320
+ // ⚠ NAME THE EXACT CALL, INCLUDING THE ARRAY. This line used to read "Then `intake_claim` and
321
+ // `intake_materialize`" straight after a list of ids, which reads as `intake_claim <id>`. It is not:
322
+ // the parameter is `intakeItemIds`, an ARRAY, and on 2026-09-22 following this very sentence took a
323
+ // lease on ten unrelated units. The singular is accepted now, but an offer must still state the call
324
+ // that acts on the thing being offered — a reader should not have to consult a schema to use a nudge.
325
+ `— Then \`intake_claim {intakeItemIds: ["<id>"]}\` to take a lease on THAT unit (the array is required; without an id you claim the head of the queue instead), then \`intake_materialize\` into the brain and pages it belongs to — or \`intake_discard\`, or \`intake_defer\` with a question when only the owner can decide.` +
326
+ more + who +
327
+ `\n— If none are yours, leave them: they stay settling for your other sessions, and become everyone's business once the window closes.`
328
+ }
329
+
288
330
  function renderNudge(payload) {
289
331
  const n = Array.isArray(payload?.nudge) ? payload.nudge : []
290
332
  if (!n.length) return ''
@@ -945,6 +987,17 @@ function renderNudge(payload) {
945
987
  claimKind: z.enum(['relevance', 'cleanup']).optional().describe('default relevance'),
946
988
  limit: z.number().optional().describe('max items (default 10). Ignored when intakeItemIds is given.'),
947
989
  leaseMinutes: z.number().optional().describe('lease length in minutes, 5-60 (default 30). Prefer intake_look for read-only inspection — a short lease is only for briefly exclusive work.'),
990
+ // 🔴 SINGULAR ACCEPTED, AND NOT AS A CONVENIENCE. Every sibling verb takes `intakeItemId`:
991
+ // intake_look, intake_materialize, intake_release, intake_defer, intake_discard. This one
992
+ // alone was plural, so the natural singular was NOT IN THE SCHEMA and was silently dropped
993
+ // — and the fallback is not an error, it is claiming the head of the queue. On 2026-09-22 a
994
+ // session offered one unit by the settling block called `intake_claim {intakeItemId}` and
995
+ // took a 30-minute lease on TEN unrelated units belonging to other sessions' work. A typo
996
+ // that becomes a side effect is worse than a typo that fails.
997
+ intakeItemId: z.string().optional().describe(
998
+ 'a single unit to claim — the same thing as intakeItemIds with one element. Accepted because ' +
999
+ 'every other intake verb spells it this way.',
1000
+ ),
948
1001
  intakeItemIds: z.array(z.string()).optional().describe(
949
1002
  'claim these specific units instead of the head of the queue (max 50). Without it you get FIFO, ' +
950
1003
  'so reaching one known unit means claiming everything ahead of it. Ids come from intake_changes ' +
@@ -956,7 +1009,11 @@ function renderNudge(payload) {
956
1009
  ),
957
1010
  },
958
1011
  },
959
- async ({ claimKind, limit, intakeItemIds, leaseMinutes }) => {
1012
+ async ({ claimKind, limit, intakeItemId, intakeItemIds, leaseMinutes }) => {
1013
+ // Merge the two spellings before anything else looks at them. Passing both is not an error —
1014
+ // the union is what the caller plainly meant.
1015
+ const ids = [...new Set([...(intakeItemIds ?? []), ...(intakeItemId ? [intakeItemId] : [])])]
1016
+ intakeItemIds = ids.length ? ids : undefined
960
1017
  const res = await fetchCortex(`${BASE}/api/intake/claim`, {
961
1018
  method: 'POST',
962
1019
  headers: { Authorization: `Bearer ${TOKEN}`, 'Content-Type': 'application/json' },
@@ -1389,7 +1446,7 @@ function renderNudge(payload) {
1389
1446
  throw new Error(classify(res.status, res.headers.get('content-type'), body, res.headers.get('x-vercel-id')).message)
1390
1447
  }
1391
1448
  const payload = await res.json()
1392
- return { content: [{ type: 'text', text: formatGrepHits(payload, query) + renderArrival(payload) + renderNudge(payload) }] }
1449
+ return { content: [{ type: 'text', text: formatGrepHits(payload, query) + renderArrival(payload) + renderNudge(payload) + renderSettling(payload) }] }
1393
1450
  },
1394
1451
  )
1395
1452
 
@@ -1698,7 +1755,7 @@ function renderNudge(payload) {
1698
1755
  // `k` is stated rather than hidden. "Only you" and "you and two others" call for different
1699
1756
  // behaviour, and a nudge that claimed certainty it did not have is how an agent learns to stop
1700
1757
  // reading them.
1701
- const nudgeBlock = renderArrival(page) + renderNudge(page)
1758
+ const nudgeBlock = renderArrival(page) + renderNudge(page) + renderSettling(page)
1702
1759
 
1703
1760
  if (matches.length === 1) {
1704
1761
  return { content: [{ type: 'text', text: renderMatch(matches[0], false) + nudgeBlock }] }
@@ -3449,7 +3506,7 @@ function renderNudge(payload) {
3449
3506
  return toolError(`Could not list records: ${d.message}`)
3450
3507
  }
3451
3508
  const payload = await res.json()
3452
- return { content: [{ type: 'text', text: (payload?.text ?? '') + renderArrival(payload) + renderNudge(payload) }] }
3509
+ return { content: [{ type: 'text', text: (payload?.text ?? '') + renderArrival(payload) + renderNudge(payload) + renderSettling(payload) }] }
3453
3510
  },
3454
3511
  )
3455
3512
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@theronap/agnoclast-mcp",
3
- "version": "0.9.164",
3
+ "version": "0.9.166",
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": {