@theronap/cortex-mcp 0.9.61 → 0.9.62
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 +39 -14
- package/package.json +1 -1
package/lib/server.mjs
CHANGED
|
@@ -235,8 +235,8 @@ export async function runServer(version) {
|
|
|
235
235
|
server.registerTool(
|
|
236
236
|
'timeline_pull',
|
|
237
237
|
{
|
|
238
|
-
title: 'Pull
|
|
239
|
-
description: "Surface
|
|
238
|
+
title: 'Pull the unclaimed backlog to triage',
|
|
239
|
+
description: "Surface everything captured in the org that NO ONE has judged yet — the backlog awaiting your judgment. Two sections: unattributed EMAIL threads (grouped, since a thread is what you actually triage), and unclaimed RECORDS from every other source (commits, sessions, docs, calendar) one by one. Call it as you work; when you recognize which KNOWN project a surfaced thread is about, attribute it with attribute_thread. Things you don't recognize: leave them — they stay in the backlog and self-heal as the graph fills. Pass a `project` you're working on to RANK the thread half by relevance (threads sharing participants with that project come first, marked ★); records are always newest-first. Returns enough to RECOGNIZE an item — participants, subject, title, recency — never the bodies.",
|
|
240
240
|
inputSchema: {
|
|
241
241
|
limit: z.number().optional().describe('max threads to return (default 20)'),
|
|
242
242
|
project: z.string().optional().describe('optional KNOWN project slug to rank the backlog by relevance to what you are working on; omit for the whole backlog newest-first'),
|
|
@@ -252,18 +252,37 @@ export async function runServer(version) {
|
|
|
252
252
|
const body = await res.text()
|
|
253
253
|
throw new Error(classify(res.status, res.headers.get('content-type'), body, res.headers.get('x-vercel-id')).message)
|
|
254
254
|
}
|
|
255
|
-
const { threads } = await res.json()
|
|
256
|
-
if (!threads?.length) return { content: [{ type: 'text', text: '
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
255
|
+
const { threads, records } = await res.json()
|
|
256
|
+
if (!threads?.length && !records?.length) return { content: [{ type: 'text', text: 'Nothing in the backlog — everything captured so far has been claimed.' }] }
|
|
257
|
+
|
|
258
|
+
const sections = []
|
|
259
|
+
|
|
260
|
+
if (threads?.length) {
|
|
261
|
+
const lines = threads.map((t) => {
|
|
262
|
+
const who = (t.participants ?? []).map((p) => String(p).replace(/^email:/, '')).join(', ')
|
|
263
|
+
const when = t.lastAt ? String(t.lastAt).slice(0, 10) : '—'
|
|
264
|
+
const rel = t.relevance && t.relevance.score > 0 ? ` · ★${t.relevance.score} ${t.relevance.reason}` : ''
|
|
265
|
+
return `- ${t.threadKey} — ${t.subject ?? '(no subject)'} · ${who || 'unknown participants'} · ${t.eventCount} msg · ${when}${rel}`
|
|
266
|
+
})
|
|
267
|
+
const header = project
|
|
268
|
+
? `Unattributed threads (${threads.length}), ranked for project:${project} (★ = shares participants)`
|
|
269
|
+
: `Unattributed threads (${threads.length})`
|
|
270
|
+
sections.push(`${header} — attribute recognized ones with attribute_thread(threadKey, project):\n${lines.join('\n')}`)
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// The other 12 sources. No thread concept exists for a commit or a session log, so each is one
|
|
274
|
+
// row. `node` is shown when the record already carries a project — often the whole answer, and it
|
|
275
|
+
// is why these are NOT ranked by relevance: a record that already has a node needs judgment least.
|
|
276
|
+
if (records?.length) {
|
|
277
|
+
const lines = records.map((r) => {
|
|
278
|
+
const when = r.occurredAt ? String(r.occurredAt).slice(0, 10) : '—'
|
|
279
|
+
const node = r.node ? ` · → ${r.node}` : ''
|
|
280
|
+
return `- ${r.source}/${r.recordType ?? 'record'} — ${r.title ?? '(untitled)'} · ${when}${node} [${r.recordId}]`
|
|
281
|
+
})
|
|
282
|
+
sections.push(`Unclaimed records (${records.length}) from other sources — newest first:\n${lines.join('\n')}`)
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
return { content: [{ type: 'text', text: sections.join('\n\n') }] }
|
|
267
286
|
},
|
|
268
287
|
)
|
|
269
288
|
|
|
@@ -542,6 +561,12 @@ export async function runServer(version) {
|
|
|
542
561
|
const allBody = m.tiers.flatMap((t) => (t.sections ?? []).map((s) => s.body)).join('\n')
|
|
543
562
|
const stamps = [...new Set((allBody.match(/\[\[repo:[a-z0-9][a-z0-9-]*\/[a-z0-9_.-]+\]\]/gi) ?? []).map((s) => s.toLowerCase()))]
|
|
544
563
|
if (stamps.length) footer += `\n— This page carries ${stamps.join(', ')} — \`read_page "${name}"\` with history: true for its event timeline (page = present, timeline = history).`
|
|
564
|
+
// Gate 4's per-node backlog nudge (also gate 3's KWA-36). The SERVER decides whether this
|
|
565
|
+
// fires — it sends `nudge` only when 25+ records are unclaimed AND the page has gone 7+ days
|
|
566
|
+
// unedited — so there is no threshold logic here to drift out of sync. It rides in the footer
|
|
567
|
+
// because that is the surface with evidence behind it: a session read a page through a [[link]],
|
|
568
|
+
// saw the footer, and repaired the page. A dashboard nobody opens would not have.
|
|
569
|
+
if (m.backlog?.nudge) footer += `\n— ${m.backlog.nudge}`
|
|
545
570
|
const brainTag = tagBrain ? ` · brain: ${m.brain}` : ''
|
|
546
571
|
return `# ${m.title ?? name} (full authored page${brainTag})\n\n${blocks.join('\n\n---\n\n')}\n\n${footer}`
|
|
547
572
|
}
|