@theronap/agnoclast-mcp 0.9.212 → 0.9.213
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/intake_batch_render.mjs +26 -0
- package/lib/server.mjs +46 -2
- package/package.json +1 -1
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// Rendering for ADR-0076 §4.5 — a conversation's texts, decided together and filed one record each.
|
|
2
|
+
// Kept out of server.mjs so it can be tested on its output, not on its source text.
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* A batch materialization, per id. ⚠ PARTIAL FAILURE IS REPORTED PER ID, never summarised as success — a
|
|
6
|
+
* batch that half landed is the one outcome an agent must not mistake for done (same rule as
|
|
7
|
+
* place_staged_record's batch).
|
|
8
|
+
*/
|
|
9
|
+
export function renderMaterializeBatch(results) {
|
|
10
|
+
const good = results.filter((r) => r.ok)
|
|
11
|
+
const bad = results.filter((r) => !r.ok)
|
|
12
|
+
return `Batch of ${results.length}: ${good.length} filed, ${bad.length} failed — one record each.\n` +
|
|
13
|
+
results.map((r) => ` ${r.ok ? '✓' : '✗'} ${r.id} — ${r.detail}`).join('\n') +
|
|
14
|
+
(bad.length ? `\n\n⚠ ${bad.length} did NOT land. They are still in the pile; fix the cause and file those ids again (a unit must be claimed first).` : '')
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* One settling offer that stands for a whole text conversation (the server collapsed its waiting texts into
|
|
19
|
+
* it), or null when the offer is a single arrival and renders the ordinary way.
|
|
20
|
+
*/
|
|
21
|
+
export function renderConversationOffer(x, why = '', looked = '') {
|
|
22
|
+
if (!x?.conversation || !(x.conversation.count > 1)) return null
|
|
23
|
+
const ids = x.conversation.ids.slice(0, 12).join(', ') + (x.conversation.ids.length > 12 ? ', …' : '')
|
|
24
|
+
return ` - 📱 ${x.conversation.count} texts in one conversation · newest ${x.hoursAgo}h ago · ${ids}${why}${looked}\n` +
|
|
25
|
+
' decide them together: `intake_look` the newest, `intake_claim` them all, then file them in one `intake_materialize` (intakeItemIds) — one record each'
|
|
26
|
+
}
|
package/lib/server.mjs
CHANGED
|
@@ -24,6 +24,7 @@ import { spawnObligationWorker } from './obligations_worker.mjs'
|
|
|
24
24
|
import { renderObligations, renderPageObligations, tierReads, who } from './obligations_render.mjs'
|
|
25
25
|
import { renderRemovalQuestion, renderRemoved } from './member_remove_render.mjs'
|
|
26
26
|
import { renderSettings } from './settings_render.mjs'
|
|
27
|
+
import { renderConversationOffer, renderMaterializeBatch } from './intake_batch_render.mjs'
|
|
27
28
|
import { formatCreated, formatDryRun, formatSources, homeFromArgs, pickSource, renderHomeProblem } from './custom_sources.mjs'
|
|
28
29
|
|
|
29
30
|
// PAGE tiers (ADR-0071). Six for a PAGE; a RECORD keeps three (`privacy` on log_session and
|
|
@@ -428,6 +429,9 @@ function renderSettling(payload) {
|
|
|
428
429
|
? ' — ⚠ NO session of yours matches this; it is here because it reached nobody'
|
|
429
430
|
: ' — every session that matched it declined, so it is back to everyone'
|
|
430
431
|
const looked = x.looks > 0 ? ` [looked at ${x.looks}x, still unclaimed]` : ''
|
|
432
|
+
// ADR-0076 §4.5: a conversation's waiting texts arrive one per message and are offered as ONE line.
|
|
433
|
+
const conversationLine = renderConversationOffer(x, why, looked)
|
|
434
|
+
if (conversationLine) return conversationLine
|
|
431
435
|
return ` - ${x.source} · ${x.hoursAgo}h ago · ${x.intakeItemId}${why}${looked}`
|
|
432
436
|
})
|
|
433
437
|
|
|
@@ -1230,6 +1234,12 @@ function renderNudge(payload) {
|
|
|
1230
1234
|
'⚠ ADR-0077: a GitHub commit or PR does not finish until every changed path has an answer. If a changed file\'s deepest folder (or, in a grab-bag folder like web/lib/engine/ or db/supabase/, the file itself) has no claim and no decline, the call is REFUSED with the open questions; answer each in folderDecisions and call again.',
|
|
1231
1235
|
inputSchema: {
|
|
1232
1236
|
intakeItemId: z.string().describe('intake item uuid'),
|
|
1237
|
+
intakeItemIds: z.array(z.string()).optional().describe(
|
|
1238
|
+
'MORE units to file exactly the same way, in one call — one record EACH, never merged (ADR-0076 §4.5). ' +
|
|
1239
|
+
'For a conversation\'s waiting texts that you decided together: claim them all with `intake_claim`, then pass ' +
|
|
1240
|
+
'the first as intakeItemId and the rest here. title/summary/dedupeKey are ignored in a batch (each unit keeps its ' +
|
|
1241
|
+
'own), and a preview covers intakeItemId only. Each id is reported separately; a failure never hides behind a success.',
|
|
1242
|
+
),
|
|
1233
1243
|
orgId: z.string().describe('destination brain org uuid'),
|
|
1234
1244
|
documentIds: z.array(z.string()).optional().describe('additional pages in orgId — each a page `ref:` exactly as read_page shows it, or a brain_documents id (deterministic homes are merged automatically)'),
|
|
1235
1245
|
title: z.string().optional(),
|
|
@@ -1266,6 +1276,38 @@ function renderNudge(payload) {
|
|
|
1266
1276
|
},
|
|
1267
1277
|
},
|
|
1268
1278
|
async (args) => {
|
|
1279
|
+
// ADR-0076 §4.5 — A BATCH: one decision, N records. Theron, 2026-10-05: a conversation's waiting texts
|
|
1280
|
+
// are looked up once and handled together, but "it shouldn't file all of them as one record". The
|
|
1281
|
+
// loop lives here, as it does for place_staged_record's batch: the route stays one unit per call.
|
|
1282
|
+
const batch = [...new Set([args.intakeItemId, ...(args.intakeItemIds ?? [])])].slice(0, 50)
|
|
1283
|
+
if (batch.length > 1 && args.mode !== 'preview') {
|
|
1284
|
+
const results = []
|
|
1285
|
+
for (const id of batch) {
|
|
1286
|
+
try {
|
|
1287
|
+
const r = await fetchCortex(`${BASE}${intakeMaterializePath(undefined)}`, {
|
|
1288
|
+
method: 'POST',
|
|
1289
|
+
headers: { Authorization: `Bearer ${TOKEN}`, 'Content-Type': 'application/json' },
|
|
1290
|
+
body: JSON.stringify({
|
|
1291
|
+
intakeItemId: id,
|
|
1292
|
+
orgId: args.orgId,
|
|
1293
|
+
documentIds: args.documentIds ?? [],
|
|
1294
|
+
record: { source: args.source, record_type: args.recordType },
|
|
1295
|
+
attachmentMeta: (args.documentIds ?? []).map(() => ({ origin: args.origin ?? 'llm' })),
|
|
1296
|
+
...(args.governingPage ? { governingDocumentId: args.governingPage } : {}),
|
|
1297
|
+
...(args.searchedForOtherHomes ? { searchedForOtherHomes: true } : {}),
|
|
1298
|
+
// One placement is a decision about ONE conversation: the server refuses any id whose
|
|
1299
|
+
// batch differs from the first id's, so a stray id is never filed onto these pages.
|
|
1300
|
+
sameBatchAs: batch[0],
|
|
1301
|
+
}),
|
|
1302
|
+
})
|
|
1303
|
+
const o = await r.json().catch(() => null)
|
|
1304
|
+
results.push({ id, ok: r.ok, detail: r.ok ? (o?.recordId ?? 'filed') : (o?.error ?? `HTTP ${r.status}`) })
|
|
1305
|
+
} catch (e) {
|
|
1306
|
+
results.push({ id, ok: false, detail: e.message })
|
|
1307
|
+
}
|
|
1308
|
+
}
|
|
1309
|
+
return { content: [{ type: 'text', text: renderMaterializeBatch(results) }] }
|
|
1310
|
+
}
|
|
1269
1311
|
// A preview goes to its OWN route: a server too old to know it answers 404, where the materialize
|
|
1270
1312
|
// route would ignore a `mode` it does not know and publish.
|
|
1271
1313
|
const preview = args.mode === 'preview'
|
|
@@ -1312,8 +1354,10 @@ function renderNudge(payload) {
|
|
|
1312
1354
|
}
|
|
1313
1355
|
// Same redirect-not-dispatch rule as place_staged_record. A staged arrival needs no claim and
|
|
1314
1356
|
// no unseal, so the fix is a different verb rather than a retry of this one.
|
|
1315
|
-
|
|
1316
|
-
|
|
1357
|
+
// ⚠ `args.intakeItemId`, not a bare `intakeItemId`: this handler never declared one, so a 404 here
|
|
1358
|
+
// threw a ReferenceError instead of saying which verb to use (found 2026-10-05 adding batches).
|
|
1359
|
+
if (res.status === 404 && (await pileHolding(args.intakeItemId)) === 'staged') {
|
|
1360
|
+
return toolError(`${args.intakeItemId} is a STAGED arrival, not an intake unit — \`intake_materialize\` cannot act on it. Use \`place_staged_record\` with the pages it belongs to; staged arrivals take no lease, so there is nothing to claim or release. (\`staged_look\` reads it first, and carries the brain-ladder proposal.)`)
|
|
1317
1361
|
}
|
|
1318
1362
|
return toolError(body)
|
|
1319
1363
|
}
|
package/package.json
CHANGED