@tpsdev-ai/flair-mcp 0.56.0 → 0.58.0
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/README.md +5 -5
- package/dist/adapter-tools.js +2 -1
- package/dist/continuity-capture-hook.d.ts +7 -0
- package/dist/continuity-capture-hook.js +3 -2
- package/dist/continuity.d.ts +69 -0
- package/dist/continuity.js +132 -14
- package/dist/env-guard.d.ts +2 -1
- package/dist/env-guard.js +2 -1
- package/dist/precompact-hook.d.ts +174 -0
- package/dist/precompact-hook.js +418 -0
- package/dist/precompact.d.ts +404 -0
- package/dist/precompact.js +895 -0
- package/dist/prompt-recall-hook.d.ts +289 -0
- package/dist/prompt-recall-hook.js +651 -0
- package/dist/record-id-path.d.ts +20 -0
- package/dist/record-id-path.js +28 -0
- package/dist/session-start-hook.d.ts +29 -6
- package/dist/session-start-hook.js +104 -11
- package/dist/tool-descriptors/index.js +15 -15
- package/package.json +6 -4
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* record-id-path.ts — the one rule for a record id used as the single dynamic
|
|
3
|
+
* path segment of a `/Memory/<id>` request built inside flair-mcp (flair#1970).
|
|
4
|
+
*
|
|
5
|
+
* Self-contained on purpose: this module must load and typecheck WITHOUT
|
|
6
|
+
* @tpsdev-ai/flair-client's built dist present (the root `bun test` and strict
|
|
7
|
+
* test-suite typecheck lanes run before the client is built), so it does not
|
|
8
|
+
* import the client's own `encodeRecordId` — it states the same rule here.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Percent-encode an id so it reaches the server as ONE path segment that
|
|
12
|
+
* decodes back to the id, addressing exactly that record. REFUSES an id that is
|
|
13
|
+
* exactly `.` or `..`: percent-encoding leaves those unchanged and URL
|
|
14
|
+
* normalization collapses `/Memory/.` to `/Memory/` and `/Memory/..` to `/`, so
|
|
15
|
+
* the sent path would not be the id (nor the signed path). Such an id cannot
|
|
16
|
+
* address its record, so it is an error, not a request.
|
|
17
|
+
*/
|
|
18
|
+
export function encodeRecordId(id) {
|
|
19
|
+
if (id === "." || id === "..") {
|
|
20
|
+
throw new Error(`record id ${JSON.stringify(id)} is a URL path dot-segment ("." or ".."); ` +
|
|
21
|
+
`it cannot be addressed as one path segment of /Memory/<id>. Use a different id.`);
|
|
22
|
+
}
|
|
23
|
+
return encodeURIComponent(id);
|
|
24
|
+
}
|
|
25
|
+
/** The PUT path for a row: its id as ONE percent-encoded path segment. */
|
|
26
|
+
export function memoryPutPath(id) {
|
|
27
|
+
return `/Memory/${encodeRecordId(id)}`;
|
|
28
|
+
}
|
|
@@ -17,11 +17,14 @@
|
|
|
17
17
|
*
|
|
18
18
|
* NO-OP-ON-ANY-FAILURE GUARANTEE
|
|
19
19
|
* ------------------------------
|
|
20
|
-
* This hook can never block or break Claude Code startup. Every failure mode
|
|
21
|
-
* missing FLAIR_AGENT_ID, malformed stdin, Flair unreachable, auth error, a
|
|
22
|
-
* hung daemon, an unexpected throw
|
|
23
|
-
*
|
|
24
|
-
* a
|
|
20
|
+
* This hook can never block or break Claude Code startup. Every failure mode
|
|
21
|
+
* (missing FLAIR_AGENT_ID, malformed stdin, Flair unreachable, auth error, a
|
|
22
|
+
* hung daemon, an unexpected throw) exits 0. Malformed stdin is treated as
|
|
23
|
+
* empty input and can still yield bootstrap context. A failed bootstrap yields
|
|
24
|
+
* a pre-compaction record and/or a continuity resume hint only when their
|
|
25
|
+
* separate lookups find one; otherwise stdout is `{}`. The hook attempts one
|
|
26
|
+
* stderr diagnostic when bootstrap fails (flair#1943). The Codex command retains stderr and the
|
|
27
|
+
* Claude Code command discards it; delivery depends on stderr being writable.
|
|
25
28
|
*
|
|
26
29
|
* A hard timeout (FLAIR_HOOK_TIMEOUT_MS, default 8s) wraps the bootstrap call
|
|
27
30
|
* so a stalled Flair daemon can't hang session startup; on timeout we no-op.
|
|
@@ -96,10 +99,30 @@ interface BootstrapClient extends Partial<PresencePoster> {
|
|
|
96
99
|
* sharing it; re-exported here unchanged so existing importers keep working.
|
|
97
100
|
*/
|
|
98
101
|
export { isProbeMode };
|
|
102
|
+
/** The hook's OWN bootstrap-timer rejection (flair#1943). A dedicated class so
|
|
103
|
+
* the classifier recognises its own timeout by IDENTITY, never by reading a
|
|
104
|
+
* message. */
|
|
105
|
+
export declare class BootstrapTimeoutError extends Error {
|
|
106
|
+
constructor();
|
|
107
|
+
}
|
|
108
|
+
export type BootstrapFailureKind = "auth" | "timeout" | "unreachable" | `http-${number}`;
|
|
109
|
+
/**
|
|
110
|
+
* flair#1943 — classify a bootstrap failure for the one stderr line. Reads a
|
|
111
|
+
* numeric HTTP status FIRST (`status`, what FlairError carries, then
|
|
112
|
+
* `status_code`, then `statusCode`); when a status exists the message is never
|
|
113
|
+
* consulted. With no status, the ONLY timeout is the hook's own bootstrap
|
|
114
|
+
* timer (a BootstrapTimeoutError) or an error whose name is exactly
|
|
115
|
+
* `TimeoutError`; everything else is `unreachable`. No kind is ever decided
|
|
116
|
+
* from message text. Never reads or includes credentials.
|
|
117
|
+
*/
|
|
118
|
+
export declare function classifyBootstrapFailure(err: unknown): BootstrapFailureKind;
|
|
99
119
|
/**
|
|
100
120
|
* Core hook logic, with injectable dependencies so it can be unit-tested
|
|
101
121
|
* without a live Flair daemon. Returns the exact string to print to stdout.
|
|
102
|
-
*
|
|
122
|
+
* A failed bootstrap can still return a pre-compaction record and a
|
|
123
|
+
* continuity resume hint. Without bootstrap context, a pre-compaction record
|
|
124
|
+
* or a resume hint, this returns NOOP_OUTPUT. The entry
|
|
125
|
+
* point catches unexpected exceptions.
|
|
103
126
|
*
|
|
104
127
|
* @param rawInput the raw stdin string (may be empty / malformed)
|
|
105
128
|
* @param makeClient factory for the bootstrap client (defaults to FlairClient)
|
|
@@ -17,11 +17,14 @@
|
|
|
17
17
|
*
|
|
18
18
|
* NO-OP-ON-ANY-FAILURE GUARANTEE
|
|
19
19
|
* ------------------------------
|
|
20
|
-
* This hook can never block or break Claude Code startup. Every failure mode
|
|
21
|
-
* missing FLAIR_AGENT_ID, malformed stdin, Flair unreachable, auth error, a
|
|
22
|
-
* hung daemon, an unexpected throw
|
|
23
|
-
*
|
|
24
|
-
* a
|
|
20
|
+
* This hook can never block or break Claude Code startup. Every failure mode
|
|
21
|
+
* (missing FLAIR_AGENT_ID, malformed stdin, Flair unreachable, auth error, a
|
|
22
|
+
* hung daemon, an unexpected throw) exits 0. Malformed stdin is treated as
|
|
23
|
+
* empty input and can still yield bootstrap context. A failed bootstrap yields
|
|
24
|
+
* a pre-compaction record and/or a continuity resume hint only when their
|
|
25
|
+
* separate lookups find one; otherwise stdout is `{}`. The hook attempts one
|
|
26
|
+
* stderr diagnostic when bootstrap fails (flair#1943). The Codex command retains stderr and the
|
|
27
|
+
* Claude Code command discards it; delivery depends on stderr being writable.
|
|
25
28
|
*
|
|
26
29
|
* A hard timeout (FLAIR_HOOK_TIMEOUT_MS, default 8s) wraps the bootstrap call
|
|
27
30
|
* so a stalled Flair daemon can't hang session startup; on timeout we no-op.
|
|
@@ -78,6 +81,7 @@ import { basename } from "node:path";
|
|
|
78
81
|
import { deriveActivity, postPresenceSafe, resolvePresenceTimeoutMs } from "./presence.js";
|
|
79
82
|
import { isProbeMode, readEnvOrUnset, stripInterpolationLiteralsFromEnv } from "./env-guard.js";
|
|
80
83
|
import { buildResumeHint, discoverResume, prepareContinuityBoot, resolveContinuityTimeoutMs, } from "./continuity.js";
|
|
84
|
+
import { fetchPreCompactRecord, formatPreCompactContext, resolvePreCompactLookup } from "./precompact.js";
|
|
81
85
|
/** Claude Code SessionStart additionalContext hard limit (chars). */
|
|
82
86
|
const MAX_CHARS = 10_000;
|
|
83
87
|
/** Token budget for the bootstrap call — matches the proven prototype. */
|
|
@@ -120,10 +124,19 @@ function readStdin() {
|
|
|
120
124
|
setTimeout(() => resolve(data), 200).unref?.();
|
|
121
125
|
});
|
|
122
126
|
}
|
|
123
|
-
/**
|
|
127
|
+
/** The hook's OWN bootstrap-timer rejection (flair#1943). A dedicated class so
|
|
128
|
+
* the classifier recognises its own timeout by IDENTITY, never by reading a
|
|
129
|
+
* message. */
|
|
130
|
+
export class BootstrapTimeoutError extends Error {
|
|
131
|
+
constructor() {
|
|
132
|
+
super("bootstrap timeout");
|
|
133
|
+
this.name = "BootstrapTimeoutError";
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
/** Race a promise against a timeout. Rejects with a BootstrapTimeoutError if exceeded. */
|
|
124
137
|
function withTimeout(promise, ms) {
|
|
125
138
|
return new Promise((resolve, reject) => {
|
|
126
|
-
const timer = setTimeout(() => reject(new
|
|
139
|
+
const timer = setTimeout(() => reject(new BootstrapTimeoutError()), ms);
|
|
127
140
|
timer.unref?.();
|
|
128
141
|
promise.then((value) => {
|
|
129
142
|
clearTimeout(timer);
|
|
@@ -134,6 +147,60 @@ function withTimeout(promise, ms) {
|
|
|
134
147
|
});
|
|
135
148
|
});
|
|
136
149
|
}
|
|
150
|
+
/**
|
|
151
|
+
* flair#1943 — classify a bootstrap failure for the one stderr line. Reads a
|
|
152
|
+
* numeric HTTP status FIRST (`status`, what FlairError carries, then
|
|
153
|
+
* `status_code`, then `statusCode`); when a status exists the message is never
|
|
154
|
+
* consulted. With no status, the ONLY timeout is the hook's own bootstrap
|
|
155
|
+
* timer (a BootstrapTimeoutError) or an error whose name is exactly
|
|
156
|
+
* `TimeoutError`; everything else is `unreachable`. No kind is ever decided
|
|
157
|
+
* from message text. Never reads or includes credentials.
|
|
158
|
+
*/
|
|
159
|
+
export function classifyBootstrapFailure(err) {
|
|
160
|
+
const e = err;
|
|
161
|
+
const status = numericStatus(e);
|
|
162
|
+
if (status !== undefined)
|
|
163
|
+
return status === 401 || status === 403 ? "auth" : `http-${status}`;
|
|
164
|
+
if (err instanceof BootstrapTimeoutError)
|
|
165
|
+
return "timeout";
|
|
166
|
+
if (typeof e?.name === "string" && e.name === "TimeoutError")
|
|
167
|
+
return "timeout";
|
|
168
|
+
return "unreachable";
|
|
169
|
+
}
|
|
170
|
+
/** The first NUMERIC HTTP status the error carries, checked `status` →
|
|
171
|
+
* `status_code` → `statusCode` (flair#1943). */
|
|
172
|
+
function numericStatus(e) {
|
|
173
|
+
for (const key of ["status", "status_code", "statusCode"]) {
|
|
174
|
+
const v = e?.[key];
|
|
175
|
+
if (typeof v === "number" && Number.isFinite(v))
|
|
176
|
+
return v;
|
|
177
|
+
}
|
|
178
|
+
return undefined;
|
|
179
|
+
}
|
|
180
|
+
// flair#1943: one no-op 'error' listener per process, so repeated failed
|
|
181
|
+
// runs in one process never add listeners (and never trigger Node's
|
|
182
|
+
// max-listeners warning on stderr).
|
|
183
|
+
let stderrErrorAbsorbed = false;
|
|
184
|
+
/** The stderr diagnostic for a failed bootstrap. NAMES the actor, the state and
|
|
185
|
+
* the remedy; never contains a key, token, password or Authorization value. */
|
|
186
|
+
function reportBootstrapFailure(err) {
|
|
187
|
+
const kind = classifyBootstrapFailure(err);
|
|
188
|
+
const line = `flair session-start: bootstrap failed (${kind}); this session starts without bootstrap context. Next: run \`flair doctor\`, and check FLAIR_URL and this agent's key.\n`;
|
|
189
|
+
try {
|
|
190
|
+
// Best-effort (flair#1943): a failed stderr write (a closed pipe → EPIPE)
|
|
191
|
+
// must not change stdout or the exit code. The write may throw
|
|
192
|
+
// SYNCHRONOUSLY or surface later as an 'error' event on the stream; absorb
|
|
193
|
+
// both, so the hook still prints its payload and exits 0.
|
|
194
|
+
if (!stderrErrorAbsorbed) {
|
|
195
|
+
process.stderr.on("error", () => { });
|
|
196
|
+
stderrErrorAbsorbed = true;
|
|
197
|
+
}
|
|
198
|
+
process.stderr.write(line);
|
|
199
|
+
}
|
|
200
|
+
catch {
|
|
201
|
+
// ignore — the diagnostic is best-effort
|
|
202
|
+
}
|
|
203
|
+
}
|
|
137
204
|
/** Build the SessionStart hook output JSON from a context string. */
|
|
138
205
|
function hookOutput(context) {
|
|
139
206
|
return JSON.stringify({
|
|
@@ -146,7 +213,10 @@ function hookOutput(context) {
|
|
|
146
213
|
/**
|
|
147
214
|
* Core hook logic, with injectable dependencies so it can be unit-tested
|
|
148
215
|
* without a live Flair daemon. Returns the exact string to print to stdout.
|
|
149
|
-
*
|
|
216
|
+
* A failed bootstrap can still return a pre-compaction record and a
|
|
217
|
+
* continuity resume hint. Without bootstrap context, a pre-compaction record
|
|
218
|
+
* or a resume hint, this returns NOOP_OUTPUT. The entry
|
|
219
|
+
* point catches unexpected exceptions.
|
|
150
220
|
*
|
|
151
221
|
* @param rawInput the raw stdin string (may be empty / malformed)
|
|
152
222
|
* @param makeClient factory for the bootstrap client (defaults to FlairClient)
|
|
@@ -198,6 +268,18 @@ export async function runHook(rawInput, makeClient = defaultClientFactory) {
|
|
|
198
268
|
const resumeHintDone = continuity.active && typeof client.request === "function"
|
|
199
269
|
? withTimeout(discoverResume(client, agentId, continuity.priorPointer).then((result) => buildResumeHint(result)), resolveContinuityTimeoutMs()).catch(() => null)
|
|
200
270
|
: Promise.resolve(null);
|
|
271
|
+
// Pre-compaction record (flair#2069): decided locally, fetched concurrently,
|
|
272
|
+
// the marker read and the fetch both bounded by the same continuity timeout;
|
|
273
|
+
// null (nothing shown) on any failure.
|
|
274
|
+
const precompactDone = typeof client.request === "function"
|
|
275
|
+
? withTimeout((async () => {
|
|
276
|
+
const lookup = await resolvePreCompactLookup(input, agentId, continuity);
|
|
277
|
+
if (!lookup)
|
|
278
|
+
return null;
|
|
279
|
+
const record = await fetchPreCompactRecord(client, agentId, lookup);
|
|
280
|
+
return record ? formatPreCompactContext(record) : null;
|
|
281
|
+
})(), resolveContinuityTimeoutMs()).catch(() => null)
|
|
282
|
+
: Promise.resolve(null);
|
|
201
283
|
let context = "";
|
|
202
284
|
try {
|
|
203
285
|
const res = await withTimeout(Promise.resolve(client.bootstrap({
|
|
@@ -207,14 +289,25 @@ export async function runHook(rawInput, makeClient = defaultClientFactory) {
|
|
|
207
289
|
})), resolveTimeoutMs());
|
|
208
290
|
context = res && res.context ? String(res.context) : "";
|
|
209
291
|
}
|
|
210
|
-
catch {
|
|
292
|
+
catch (err) {
|
|
211
293
|
context = ""; // flair unreachable / auth error / timeout → no bootstrap context
|
|
294
|
+
// flair#1943: keeping stderr open cannot reveal an error never written to
|
|
295
|
+
// it. Write ONE line to STDERR (never stdout — that is the hook payload),
|
|
296
|
+
// so a real failure stays visible instead of being swallowed. A failed
|
|
297
|
+
// bootstrap contributes no bootstrap context; a continuity resume hint may
|
|
298
|
+
// still be returned. The entry point preserves a successful exit.
|
|
299
|
+
reportBootstrapFailure(err);
|
|
212
300
|
}
|
|
213
301
|
const resumeHint = await resumeHintDone;
|
|
302
|
+
const precompactBlock = await precompactDone;
|
|
214
303
|
await presenceDone;
|
|
215
|
-
// Combine:
|
|
216
|
-
//
|
|
304
|
+
// Combine: the pre-compaction record FIRST (bounded, so the MAX_CHARS cut
|
|
305
|
+
// below can only shorten what follows it), then the bootstrap context, then
|
|
306
|
+
// AT MOST one continuity hint line. Any piece may be absent; all absent ⇒
|
|
307
|
+
// the inert no-op output.
|
|
217
308
|
const pieces = [];
|
|
309
|
+
if (precompactBlock)
|
|
310
|
+
pieces.push(precompactBlock);
|
|
218
311
|
if (context.trim())
|
|
219
312
|
pieces.push(context);
|
|
220
313
|
if (resumeHint)
|
|
@@ -51,7 +51,7 @@ export function descriptorNames(descriptors) {
|
|
|
51
51
|
export const TOOL_DESCRIPTORS = [
|
|
52
52
|
{
|
|
53
53
|
"name": "memory_search",
|
|
54
|
-
"description": "Search memories by meaning. Understands temporal queries like 'what happened today'.
|
|
54
|
+
"description": "Search memories by meaning. Understands temporal queries like 'what happened today'. Non-admin callers are scoped to their own and other agents' non-private memories; administrator requests may have broader access.",
|
|
55
55
|
"inputSchema": {
|
|
56
56
|
"type": "object",
|
|
57
57
|
"properties": {
|
|
@@ -80,7 +80,7 @@ export const TOOL_DESCRIPTORS = [
|
|
|
80
80
|
"query"
|
|
81
81
|
]
|
|
82
82
|
},
|
|
83
|
-
"outputShape": "{ results: MemoryRecord[] } — semantic hits
|
|
83
|
+
"outputShape": "{ results: MemoryRecord[] } — semantic hits subject to the caller's read scope; each hit carries content, never the raw embedding.",
|
|
84
84
|
"annotations": {
|
|
85
85
|
"readOnlyHint": true
|
|
86
86
|
},
|
|
@@ -135,7 +135,7 @@ export const TOOL_DESCRIPTORS = [
|
|
|
135
135
|
"private",
|
|
136
136
|
"shared"
|
|
137
137
|
],
|
|
138
|
-
"description": "Writer-controlled sharing intent. Omit to use the server's durability-keyed default: permanent/persistent -> shared, standard/ephemeral -> private. private — owner-
|
|
138
|
+
"description": "Writer-controlled sharing intent. Omit to use the server's durability-keyed default: permanent/persistent -> shared, standard/ephemeral -> private. private — readable by its owner and administrators; other non-admin agents cannot read it, including through a memory grant. shared — visible to the owner and every other agent on this instance. The visibility the write actually landed on is returned in the result."
|
|
139
139
|
},
|
|
140
140
|
"usedMemoryIds": {
|
|
141
141
|
"type": "array",
|
|
@@ -189,7 +189,7 @@ export const TOOL_DESCRIPTORS = [
|
|
|
189
189
|
},
|
|
190
190
|
{
|
|
191
191
|
"name": "skill_search",
|
|
192
|
-
"description": "Find skills (reusable capabilities/procedures) that apply to a task. Ranks skill-tagged memories by their `trigger` ('when to use') against your task text. Returns a lightweight CATALOG — id, name, trigger, description, tags, agentId — NOT the full procedure (fetch that with skill_get).
|
|
192
|
+
"description": "Find skills (reusable capabilities/procedures) that apply to a task. Ranks skill-tagged memories by their `trigger` ('when to use') against your task text. Returns a lightweight CATALOG — id, name, trigger, description, tags, agentId — NOT the full procedure (fetch that with skill_get). Non-admin callers can retrieve their own and other agents' non-private skills; administrators can also retrieve private skills.",
|
|
193
193
|
"inputSchema": {
|
|
194
194
|
"type": "object",
|
|
195
195
|
"properties": {
|
|
@@ -206,14 +206,14 @@ export const TOOL_DESCRIPTORS = [
|
|
|
206
206
|
"task"
|
|
207
207
|
]
|
|
208
208
|
},
|
|
209
|
-
"outputShape": "{ results: SkillCard[] } — the skill catalog (lightweight id/name/trigger/description/tags/agentId, ranked by trigger match); the full procedure and the raw embedding are never on a card.
|
|
209
|
+
"outputShape": "{ results: SkillCard[] } — the skill catalog (lightweight id/name/trigger/description/tags/agentId, ranked by trigger match); the full procedure and the raw embedding are never on a card. Non-admin callers can retrieve their own and other agents' non-private skills; administrators can also retrieve private skills.",
|
|
210
210
|
"annotations": {
|
|
211
211
|
"readOnlyHint": true
|
|
212
212
|
}
|
|
213
213
|
},
|
|
214
214
|
{
|
|
215
215
|
"name": "skill_get",
|
|
216
|
-
"description": "Retrieve a full skill by ID — the complete procedure (`content`) plus trigger and metadata. The disclosure step after skill_search's catalog.
|
|
216
|
+
"description": "Retrieve a full skill by ID — the complete procedure (`content`) plus trigger and metadata. The disclosure step after skill_search's catalog. Reads follow the caller's authorization: non-admin callers can retrieve their own and other agents' non-private skills; administrators can also retrieve private skills. A non-skill id returns not-found. The raw embedding vector is never returned.",
|
|
217
217
|
"inputSchema": {
|
|
218
218
|
"type": "object",
|
|
219
219
|
"properties": {
|
|
@@ -226,7 +226,7 @@ export const TOOL_DESCRIPTORS = [
|
|
|
226
226
|
"id"
|
|
227
227
|
]
|
|
228
228
|
},
|
|
229
|
-
"outputShape": "The full skill record { id, agentId, content, trigger, tags, durability, metadata, createdAt, ... } for a skill readable under the caller's read-scope — embedding + embeddingModel always stripped. A non-
|
|
229
|
+
"outputShape": "The full skill record { id, agentId, content, trigger, tags, durability, metadata, createdAt, ... } for a skill readable under the caller's read-scope — embedding + embeddingModel always stripped. A non-admin caller cannot read another agent's private skill; administrators retain access. A readable non-skill id is reported as not found.",
|
|
230
230
|
"annotations": {
|
|
231
231
|
"readOnlyHint": true
|
|
232
232
|
}
|
|
@@ -268,7 +268,7 @@ export const TOOL_DESCRIPTORS = [
|
|
|
268
268
|
},
|
|
269
269
|
{
|
|
270
270
|
"name": "memory_basement",
|
|
271
|
-
"description": "Send a memory to the basement (archive it). Sets archived=true and stamps archivedAt. The memory is removed from bootstrap and default search but remains retrievable via memory_get and memory_search(includeArchived:true). Deliberate and GLOBAL — this is a visibility flag, not a deletion: provenance and history are untouched.
|
|
271
|
+
"description": "Send a memory to the basement (archive it). Sets archived=true and stamps archivedAt. The memory is removed from bootstrap and default search but remains retrievable via memory_get and memory_search(includeArchived:true). Deliberate and GLOBAL — this is a visibility flag, not a deletion: provenance and history are untouched. Non-admin callers can modify only their own memories; administrators can also modify other agents' memories.",
|
|
272
272
|
"inputSchema": {
|
|
273
273
|
"type": "object",
|
|
274
274
|
"properties": {
|
|
@@ -286,7 +286,7 @@ export const TOOL_DESCRIPTORS = [
|
|
|
286
286
|
},
|
|
287
287
|
{
|
|
288
288
|
"name": "memory_restore",
|
|
289
|
-
"description": "Restore a basemented (archived) memory. Clears archived and archivedAt. Deliberate and GLOBAL — this un-retires the memory for EVERY session, not a session-local view (per-session reuse is drawers, which do not exist yet).
|
|
289
|
+
"description": "Restore a basemented (archived) memory. Clears archived and archivedAt. Deliberate and GLOBAL — this un-retires the memory for EVERY session, not a session-local view (per-session reuse is drawers, which do not exist yet). Non-admin callers can modify only their own memories; administrators can also modify other agents' memories.",
|
|
290
290
|
"inputSchema": {
|
|
291
291
|
"type": "object",
|
|
292
292
|
"properties": {
|
|
@@ -325,7 +325,7 @@ export const TOOL_DESCRIPTORS = [
|
|
|
325
325
|
"id"
|
|
326
326
|
]
|
|
327
327
|
},
|
|
328
|
-
"outputShape": "The full memory record { id, agentId, content, durability, createdAt, ... } for the caller's
|
|
328
|
+
"outputShape": "The full memory record { id, agentId, content, durability, createdAt, ... } for the requested ID, subject to the caller's read scope; embedding and embeddingModel are stripped by default.",
|
|
329
329
|
"annotations": {
|
|
330
330
|
"readOnlyHint": true
|
|
331
331
|
},
|
|
@@ -336,7 +336,7 @@ export const TOOL_DESCRIPTORS = [
|
|
|
336
336
|
},
|
|
337
337
|
{
|
|
338
338
|
"name": "memory_delete",
|
|
339
|
-
"description": "Delete a memory by ID.
|
|
339
|
+
"description": "Delete a memory by ID. Non-admin callers can delete only their own memories; administrators can also delete other agents' memories.",
|
|
340
340
|
"inputSchema": {
|
|
341
341
|
"type": "object",
|
|
342
342
|
"properties": {
|
|
@@ -349,7 +349,7 @@ export const TOOL_DESCRIPTORS = [
|
|
|
349
349
|
"id"
|
|
350
350
|
]
|
|
351
351
|
},
|
|
352
|
-
"outputShape": "Deletes
|
|
352
|
+
"outputShape": "Deletes a memory by ID when authorized, at any durability tier (success echo is thin). Cross-owner deletion returns { error, status:403 } for a non-admin; a deleted row round-trips as gone via memory_get.",
|
|
353
353
|
"annotations": {
|
|
354
354
|
"destructiveHint": true
|
|
355
355
|
}
|
|
@@ -490,7 +490,7 @@ export const TOOL_DESCRIPTORS = [
|
|
|
490
490
|
]
|
|
491
491
|
},
|
|
492
492
|
"outputShape": "Refuses runtime Soul writes, including admin-agent delegation, with { error, status:403 }. Operators use the authenticated REST or CLI path.",
|
|
493
|
-
"stdioDescription": "Set a personality or project context entry
|
|
493
|
+
"stdioDescription": "Set a personality or project context entry, included in every bootstrap. Soul writes require verified administrator Basic credentials; Ed25519 agent requests are refused. Operators should use the REST API or CLI."
|
|
494
494
|
},
|
|
495
495
|
{
|
|
496
496
|
"name": "soul_get",
|
|
@@ -588,7 +588,7 @@ export const TOOL_DESCRIPTORS = [
|
|
|
588
588
|
},
|
|
589
589
|
{
|
|
590
590
|
"name": "flair_catchup",
|
|
591
|
-
"description": "Drain
|
|
591
|
+
"description": "Drain the catch-up feed for the configured `FLAIR_AGENT_ID`: directed or broadcast org events after the effective cursor. Omit `after` to start at that agent's durable watermark, or pass `after` to choose an exclusive cursor. Pass `ack` to advance the watermark before this call reads a page; the response includes `nextAfter` for paging. The tool has no argument to change the agent id. Agent requests are signed for the configured id; verified administrator Basic credentials may read that configured feed. Unacknowledged events remain eligible after restart; acknowledged events are skipped by default, but an explicit older `after` can replay them.",
|
|
592
592
|
"inputSchema": {
|
|
593
593
|
"type": "object",
|
|
594
594
|
"properties": {
|
|
@@ -606,7 +606,7 @@ export const TOOL_DESCRIPTORS = [
|
|
|
606
606
|
}
|
|
607
607
|
}
|
|
608
608
|
},
|
|
609
|
-
"outputShape": "{ events: OrgEvent[], after, nextAfter, watermark, hasMore, pageSize, acked? } — the
|
|
609
|
+
"outputShape": "{ events: OrgEvent[], after, nextAfter, watermark, hasMore, pageSize, acked? } — the configured agent's directed and broadcast events after the effective cursor; `ack` advances that agent's durable watermark monotonically.",
|
|
610
610
|
"native": false
|
|
611
611
|
},
|
|
612
612
|
{
|
package/package.json
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tpsdev-ai/flair-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.58.0",
|
|
4
4
|
"description": "MCP server for Flair — persistent memory for Claude Code, Cursor, and any MCP client.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"bin": {
|
|
8
8
|
"flair-mcp": "dist/mcp-shim.cjs",
|
|
9
|
+
"flair-precompact": "dist/precompact-hook.js",
|
|
9
10
|
"flair-session-start": "dist/session-start-hook.js",
|
|
10
|
-
"flair-continuity-capture": "dist/continuity-capture-hook.js"
|
|
11
|
+
"flair-continuity-capture": "dist/continuity-capture-hook.js",
|
|
12
|
+
"flair-prompt-recall": "dist/prompt-recall-hook.js"
|
|
11
13
|
},
|
|
12
14
|
"files": [
|
|
13
15
|
"dist/",
|
|
@@ -19,7 +21,7 @@
|
|
|
19
21
|
"prebuild": "node ../../scripts/vendor-tool-descriptors.mjs packages/flair-mcp/src/tool-descriptors",
|
|
20
22
|
"test": "bun test",
|
|
21
23
|
"prepack": "npm run build",
|
|
22
|
-
"postinstall": "node -e \"try{const{chmodSync,statSync}=require('fs');for(const p of ['dist/mcp-shim.cjs','dist/index.js','dist/session-start-hook.js','dist/continuity-capture-hook.js']){try{if(statSync(p).isFile()){chmodSync(p,0o755);console.error('@tpsdev-ai/flair-mcp: chmod +x ' + p + ' OK')}}catch(e){if(e.code!=='ENOENT')console.error('postinstall warn:',e.message)}}}catch(e){console.error('postinstall warn:',e.message)}\""
|
|
24
|
+
"postinstall": "node -e \"try{const{chmodSync,statSync}=require('fs');for(const p of ['dist/mcp-shim.cjs','dist/index.js','dist/session-start-hook.js','dist/continuity-capture-hook.js','dist/prompt-recall-hook.js','dist/precompact-hook.js']){try{if(statSync(p).isFile()){chmodSync(p,0o755);console.error('@tpsdev-ai/flair-mcp: chmod +x ' + p + ' OK')}}catch(e){if(e.code!=='ENOENT')console.error('postinstall warn:',e.message)}}}catch(e){console.error('postinstall warn:',e.message)}\""
|
|
23
25
|
},
|
|
24
26
|
"publishConfig": {
|
|
25
27
|
"access": "public"
|
|
@@ -29,7 +31,7 @@
|
|
|
29
31
|
},
|
|
30
32
|
"dependencies": {
|
|
31
33
|
"@modelcontextprotocol/sdk": "1.27.1",
|
|
32
|
-
"@tpsdev-ai/flair-client": "0.
|
|
34
|
+
"@tpsdev-ai/flair-client": "0.58.0",
|
|
33
35
|
"zod": "4.3.6"
|
|
34
36
|
},
|
|
35
37
|
"license": "Apache-2.0",
|