@theronap/cortex-mcp 0.9.88 → 0.9.89
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 +41 -4
- package/package.json +1 -1
- package/skills/log/SKILL.md +16 -3
package/lib/server.mjs
CHANGED
|
@@ -192,15 +192,17 @@ export async function runServer(version) {
|
|
|
192
192
|
'log_session',
|
|
193
193
|
{
|
|
194
194
|
title: 'Log this session to Agnoclast',
|
|
195
|
-
description: 'Persist a CURATED summary of this work session as its durable Agnoclast record (authoritative — supersedes the auto-capture hook). Call at session close after composing the summary. Pass sessionId (the Claude Code session id) if you have it so this dedupes with the auto-capture of the same session.',
|
|
195
|
+
description: 'Persist a CURATED summary of this work session as its durable Agnoclast record (authoritative — supersedes the auto-capture hook). Call at session close after composing the summary. Pass sessionId (the Claude Code session id) if you have it so this dedupes with the auto-capture of the same session. If you belong to more than one brain, pass `brain` — without it a session log has no route and is STAGED rather than recorded.',
|
|
196
196
|
inputSchema: {
|
|
197
197
|
summary: z.string().describe('the curated session summary (what was done, decided, left open) — becomes the durable record'),
|
|
198
198
|
project: z.string().optional().describe('project key/name this session worked in'),
|
|
199
199
|
title: z.string().optional().describe('short title for the session'),
|
|
200
200
|
sessionId: z.string().optional().describe('the Claude Code session id (dedupes with the auto-capture hook of the same session)'),
|
|
201
|
+
brain: z.string().optional().describe('which brain to record this session in (name or org id, one of your own). REQUIRED IN EFFECT for a multi-brain member: session-class sources route only by an explicit brain or a sole membership, so omitting it stages the log instead of recording it.'),
|
|
202
|
+
privacy: z.enum(['accessible', 'scoped', 'confidential']).optional().describe('tier this record AT WRITE TIME. Use when the summary names confidential work (a candidate evaluation, a security finding) — safer than letting it land org-visible and re-tiering after, which leaves it readable in between.'),
|
|
201
203
|
},
|
|
202
204
|
},
|
|
203
|
-
async ({ summary, project, title, sessionId }) => {
|
|
205
|
+
async ({ summary, project, title, sessionId, brain, privacy }) => {
|
|
204
206
|
const res = await fetchCortex(`${BASE}/api/ingest`, {
|
|
205
207
|
method: 'POST',
|
|
206
208
|
headers: { Authorization: `Bearer ${TOKEN}`, 'Content-Type': 'application/json' },
|
|
@@ -211,6 +213,13 @@ export async function runServer(version) {
|
|
|
211
213
|
...(project ? { project } : {}),
|
|
212
214
|
...(title ? { title } : {}),
|
|
213
215
|
...(sessionId ? { sessionId } : {}),
|
|
216
|
+
// ADR-0022 deleted the write pointer, so a session-class source routes ONLY by an explicit
|
|
217
|
+
// brain or a sole membership — anything else STAGES. This tool never sent one, so every
|
|
218
|
+
// close-out from a multi-brain member landed in staged_records instead of the org. Measured
|
|
219
|
+
// 2026-08-09: 86 staged rows, and the pile is not drainable by /api/staged/promote because
|
|
220
|
+
// promote resolves through the same branch session-class sources skip.
|
|
221
|
+
...(brain ? { brain } : {}),
|
|
222
|
+
...(privacy ? { privacy } : {}),
|
|
214
223
|
payload: { via: 'log_session' },
|
|
215
224
|
}),
|
|
216
225
|
})
|
|
@@ -219,7 +228,26 @@ export async function runServer(version) {
|
|
|
219
228
|
throw new Error(classify(res.status, res.headers.get('content-type'), body, res.headers.get('x-vercel-id')).message)
|
|
220
229
|
}
|
|
221
230
|
const j = await res.json().catch(() => ({}))
|
|
222
|
-
|
|
231
|
+
|
|
232
|
+
// NEVER report a non-record as "logged". `staged` and `skipped` are 200 OK responses that wrote
|
|
233
|
+
// no record, and this line used to print "Logged to Agnoclast (authoritative) … updated existing"
|
|
234
|
+
// for both — `j.inserted` is merely falsy on a staged write, which reads as an UPDATE. That is
|
|
235
|
+
// how a session close-out was reported as saved when it had not been (2026-08-09), and it is the
|
|
236
|
+
// same failure shape the cortex-log skill warns about for `author`: a success string over a write
|
|
237
|
+
// that did not land. The caller cannot tell the difference, so the message has to.
|
|
238
|
+
if (j.staged) {
|
|
239
|
+
const why = j.reason === 'no_route_for_source' && !brain
|
|
240
|
+
? 'no brain was named and you belong to more than one, so it had nowhere to route'
|
|
241
|
+
: `reason: ${j.reason ?? 'unknown'}`
|
|
242
|
+
return { content: [{ type: 'text', text: `NOT LOGGED — STAGED, not recorded (${why}). Re-run log_session with brain:"<name>" to record it. Staged session logs cannot currently be drained by /api/staged/promote.` }] }
|
|
243
|
+
}
|
|
244
|
+
if (j.skipped) {
|
|
245
|
+
return { content: [{ type: 'text', text: `NOT LOGGED — the server skipped this write: ${j.skipped}` }] }
|
|
246
|
+
}
|
|
247
|
+
if (!j.id) {
|
|
248
|
+
return { content: [{ type: 'text', text: `NOT LOGGED — the server returned no record id. Raw response: ${JSON.stringify(j).slice(0, 300)}` }] }
|
|
249
|
+
}
|
|
250
|
+
return { content: [{ type: 'text', text: `Logged to Agnoclast (authoritative): "${j.title ?? title ?? 'session'}" — ${j.inserted ? 'new record' : 'updated existing'} (id ${j.id}).` }] }
|
|
223
251
|
},
|
|
224
252
|
)
|
|
225
253
|
|
|
@@ -1792,7 +1820,16 @@ export async function runServer(version) {
|
|
|
1792
1820
|
const out = await res.json().catch(() => null)
|
|
1793
1821
|
if (!res.ok) {
|
|
1794
1822
|
if (out?.error === 'already_claimed') {
|
|
1795
|
-
|
|
1823
|
+
// Name the holder when the server knows it. The fallback matters: `heldBy` is absent when
|
|
1824
|
+
// nothing holds a live lease, which means the record was not claimable rather than taken —
|
|
1825
|
+
// printing "another session has it" there sends the reader chasing a session that does not
|
|
1826
|
+
// exist. (This branch printed a bare `undefined` until 2026-08-14; the field was renamed
|
|
1827
|
+
// server-side and the tool was never updated, which is the whole reason it says both now.)
|
|
1828
|
+
if (out.heldBy) {
|
|
1829
|
+
const until = out.heldUntil ? `, lease to ${out.heldUntil}` : ''
|
|
1830
|
+
return toolError(`Session ${String(out.heldBy).slice(0, 16)}… is holding that record${until}. Leave it to them.`)
|
|
1831
|
+
}
|
|
1832
|
+
return toolError(out.detail || 'Could not claim that record and no session holds it — re-run pending_records; it may have been resolved already.')
|
|
1796
1833
|
}
|
|
1797
1834
|
return toolError(`Could not claim record: ${out?.error ?? res.status}`)
|
|
1798
1835
|
}
|
package/package.json
CHANGED
package/skills/log/SKILL.md
CHANGED
|
@@ -36,9 +36,22 @@ No arguments. Read the conversation context.
|
|
|
36
36
|
session's authoritative Agnoclast record (`capture_source='skill'`). The background auto-capture is a
|
|
37
37
|
fallback and will not overwrite it; passing the same `sessionId` the auto-capture uses dedupes them
|
|
38
38
|
onto one record. This — not the raw-transcript re-derivation — is the canonical record going forward.
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
39
|
+
|
|
40
|
+
⚠ **If you belong to more than one brain, pass `brain`.** ADR-0022 deleted the write pointer, so a
|
|
41
|
+
session-class source routes only by an explicit brain or a sole membership — omit it and the log is
|
|
42
|
+
**STAGED, not recorded**, and staged session logs are not drainable by `/api/staged/promote`. Pick the
|
|
43
|
+
brain the work was actually in (`my_brains` shows what each holds). This silently swallowed 86 close-outs
|
|
44
|
+
before it was caught on 2026-08-09.
|
|
45
|
+
4. **Confirm + flag privacy** — **read the result text, do not assume it succeeded.** `log_session` now
|
|
46
|
+
answers `NOT LOGGED — STAGED…` or `NOT LOGGED — the server skipped…` when no record was written; only a
|
|
47
|
+
message carrying a record id means it landed. (It previously printed "Logged … updated existing" for a
|
|
48
|
+
staged write, because `inserted` is merely falsy when nothing is recorded — an agent reported a session
|
|
49
|
+
as saved when it was not.) If it errors, tell the user to run `npx -y @theronap/cortex-mcp doctor`.
|
|
50
|
+
|
|
51
|
+
If any record from this session should be confidential, prefer passing `privacy: "confidential"` on the
|
|
52
|
+
`log_session` call itself so it is tiered **at write time** rather than landing org-visible and being
|
|
53
|
+
corrected after. Otherwise note it so the user can mark it (`set_record_privacy`). Default is org-visible
|
|
54
|
+
under access rules.
|
|
42
55
|
5. **Sweep the wiki (author what you now understand)** — the HARD backstop for live authoring
|
|
43
56
|
([[cortex-wiki-authoring-spec]] D2). For each node whose understanding meaningfully advanced this
|
|
44
57
|
session (the project(s) worked on, people you coordinated with, and yourself when your own focus
|