@theronap/agnoclast-mcp 0.9.233 → 0.9.234

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 CHANGED
@@ -31,6 +31,7 @@ import { formatCreated, formatDryRun, formatSources, homeFromArgs, pickSource, r
31
31
  import { formatSeriesRefusal, formatSeriesUpdated } from './series_render.mjs'
32
32
  import { formatSends } from './sends_render.mjs'
33
33
  import { applyLock, fetchLockState, lockedAnswerFor, lockStateWithin } from './locked_tools.mjs'
34
+ import { filterUnshownSettling } from './settling_shown.mjs'
34
35
 
35
36
  // PAGE tiers (ADR-0071). Six for a PAGE; a RECORD keeps three (`privacy` on log_session and
36
37
  // set_record_privacy below) — an owner-relative tier is applied to a page's records when they are read
@@ -450,9 +451,18 @@ function renderArrival(payload) {
450
451
  ` — \`staged_records\` lists them. Not offered to you, just counted; \`ignore_arrivals\` if this is noise right now.`
451
452
  }
452
453
 
454
+ // ASK ONCE, THEN ONLY WHAT IS NEW (Theron, 2026-10-14). This block rode on EVERY tool reply, unchanged,
455
+ // and an offer that can be ignored on every call is ignored on every call — measured that day: 869
456
+ // pending, 0 claimed, while the block was printed to every session. Now a unit is shown to this process
457
+ // (one MCP server = one session) the first time only, framed as a decision owed before other work.
458
+ // Units every session passes on are escalated to the PERSON at the next session start
459
+ // (web/lib/engine/orphan_escalation.ts), so a unit dropped here is never lost — it changes audience.
460
+ const settlingShown = new Set()
461
+
453
462
  function renderSettling(payload) {
454
463
  const feed = payload?.settling
455
- const offers = Array.isArray(feed?.offers) ? feed.offers : []
464
+ const firstSight = settlingShown.size === 0
465
+ const offers = filterUnshownSettling(Array.isArray(feed?.offers) ? feed.offers : [], settlingShown)
456
466
  if (!offers.length) return ''
457
467
  const total = Number(feed?.total ?? offers.length)
458
468
 
@@ -483,7 +493,10 @@ function renderSettling(payload) {
483
493
  ? `\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.`
484
494
  : ''
485
495
 
486
- return `\n\n⚡ ARRIVALS SETTLING — yours alone, and no one else can see these yet:\n${lines.join('\n')}\n` +
496
+ const head = firstSight
497
+ ? `\n\n⚡ ARRIVALS SETTLING — DECIDE EACH LINE BEFORE CONTINUING: \`intake_claim\` it if it belongs to this session's work, otherwise \`not_mine\`. You are asked once; when every session has passed, the person is told at the next session start.\n`
498
+ : `\n\n⚡ NEW ARRIVALS SETTLING — decide each: \`intake_claim\` or \`not_mine\`:\n`
499
+ return `${head}${lines.join('\n')}\n` +
487
500
  `— These are unjudged ARRIVALS, not records. Since automatic materialization was deleted, nothing files them for you.\n` +
488
501
  `— \`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` +
489
502
  `— 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.\n` +
@@ -0,0 +1,9 @@
1
+ // Ask-once filter for the ⚡ ARRIVALS SETTLING block (2026-10-14). Pure, so it is testable without
2
+ // importing server.mjs. An offer is fresh if ANY id it stands for has not been shown to this session;
3
+ // a text conversation counts as one offer carrying many ids, so one new text re-raises its chat.
4
+ export function filterUnshownSettling(offers, shown) {
5
+ const idsOf = (x) => (x.conversation?.ids?.length ? x.conversation.ids : [x.intakeItemId])
6
+ const fresh = offers.filter((x) => idsOf(x).some((id) => !shown.has(id)))
7
+ for (const x of fresh) for (const id of idsOf(x)) shown.add(id)
8
+ return fresh
9
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@theronap/agnoclast-mcp",
3
- "version": "0.9.233",
3
+ "version": "0.9.234",
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": {