@tpsdev-ai/flair 0.49.0 → 0.50.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/dist/bridges/runtime/roundtrip.js +91 -2
- package/dist/build-info.json +3 -3
- package/dist/cli.js +383 -110
- package/dist/deploy.js +20 -3
- package/dist/doctor-client.js +59 -31
- package/dist/federation/scheduler.js +24 -3
- package/dist/hook-install.js +45 -13
- package/dist/lib/scheduler-platform.js +132 -10
- package/dist/lib/scratch-owner.js +49 -0
- package/dist/rem/scheduler.js +23 -5
- package/dist/resources/MemoryBootstrap.js +8 -4
- package/dist/resources/health.js +52 -7
- package/dist/resources/mcp-tools.js +1 -0
- package/dist/resources/search-readiness.js +100 -0
- package/dist/resources/semantic-retrieval-core.js +9 -1
- package/dist/resources/sort-comparators.js +45 -0
- package/dist/src/lib/scheduler-platform.js +132 -10
- package/dist/src/rem/scheduler.js +23 -5
- package/docs/auth.md +5 -0
- package/docs/deepseek-harness.md +1 -1
- package/docs/hosted-on-fabric.md +2 -0
- package/docs/integrations.md +53 -1
- package/docs/mcp-clients.md +67 -15
- package/docs/quickstart-fabric.md +1 -1
- package/docs/troubleshooting.md +25 -0
- package/package.json +2 -2
|
@@ -19,7 +19,7 @@ import { homedir } from "node:os";
|
|
|
19
19
|
import { spawn } from "node:child_process";
|
|
20
20
|
import { fileURLToPath } from "node:url";
|
|
21
21
|
import { escapeXml } from "../lib/xml-escape.js";
|
|
22
|
-
import { detectPlatform as detectPlatformFor, spawnReport, readTemplate as readTemplateFrom, renderTemplateWith, writeFileWithDir, interpretActiveResult, describeLoadFailure as describeLoadFailureFor, describeExitCode, resolveNodeBin, verifyFirstRun, STATUS_CHECK_TIMEOUT_MS, } from "../lib/scheduler-platform.js";
|
|
22
|
+
import { detectPlatform as detectPlatformFor, spawnReport, readTemplate as readTemplateFrom, renderTemplateWith, writeFileWithDir, interpretActiveResult, describeLoadFailure as describeLoadFailureFor, describeExitCode, resolveNodeBin, resolveFlairBin, formatFlairBinWarning, verifyFirstRun, probeUserLingerEnabled, STATUS_CHECK_TIMEOUT_MS, } from "../lib/scheduler-platform.js";
|
|
23
23
|
// Re-exported so this module's public surface is unchanged by the extraction
|
|
24
24
|
// into src/lib/scheduler-platform.ts (a second scheduler — `flair federation
|
|
25
25
|
// sync enable` — needs the identical launchctl/systemctl interpretation, and
|
|
@@ -180,8 +180,8 @@ export async function queryActiveStateAsync(plat, timeoutMs = STATUS_CHECK_TIMEO
|
|
|
180
180
|
* known pattern — the caller already prints the raw stderr, so the operator
|
|
181
181
|
* still has something to go on.
|
|
182
182
|
*/
|
|
183
|
-
export function describeLoadFailure(plat, loadResult) {
|
|
184
|
-
return describeLoadFailureFor(plat, loadResult, "flair rem nightly enable");
|
|
183
|
+
export function describeLoadFailure(plat, loadResult, session) {
|
|
184
|
+
return describeLoadFailureFor(plat, loadResult, "flair rem nightly enable", session);
|
|
185
185
|
}
|
|
186
186
|
/**
|
|
187
187
|
* Formats the `flair rem nightly enable` report from an `EnableResult`.
|
|
@@ -199,6 +199,15 @@ export function describeLoadFailure(plat, loadResult) {
|
|
|
199
199
|
* happen once. A missing `loadResult`/`firstRun` (test-only skipLoad shape)
|
|
200
200
|
* therefore withholds the headline too, instead of being treated as success.
|
|
201
201
|
*/
|
|
202
|
+
function appendFlairBinWarning(lines, r) {
|
|
203
|
+
if (r.flairBinCanonical !== false || !r.flairBin)
|
|
204
|
+
return;
|
|
205
|
+
const warning = formatFlairBinWarning(r.flairBin, r.flairBinPublic ?? null, "flair rem nightly enable");
|
|
206
|
+
if (warning.length === 0)
|
|
207
|
+
return;
|
|
208
|
+
lines.push("");
|
|
209
|
+
lines.push(...warning);
|
|
210
|
+
}
|
|
202
211
|
export function formatEnableReport(r, input) {
|
|
203
212
|
const { hour, minute, agentId, flairUrl } = input;
|
|
204
213
|
const scheduleTime = `${String(hour).padStart(2, "0")}:${String(minute).padStart(2, "0")}`;
|
|
@@ -216,11 +225,15 @@ export function formatEnableReport(r, input) {
|
|
|
216
225
|
];
|
|
217
226
|
if (lr.stderr)
|
|
218
227
|
lines.push(` stderr: ${lr.stderr.trim()}`);
|
|
219
|
-
const
|
|
228
|
+
const lingerEnabled = input.lingerEnabled !== undefined
|
|
229
|
+
? input.lingerEnabled
|
|
230
|
+
: (r.platform === "linux" ? (input.probeLinger ?? probeUserLingerEnabled)() : undefined);
|
|
231
|
+
const remedy = describeLoadFailure(r.platform, lr, { lingerEnabled, env: input.env });
|
|
220
232
|
lines.push("");
|
|
221
233
|
lines.push(remedy ? ` ${remedy}` : ` Re-run the activation command above manually to see the full diagnostic.`);
|
|
222
234
|
lines.push("");
|
|
223
235
|
lines.push(` Nothing is scheduled until activation succeeds. Check anytime with: flair rem nightly status`);
|
|
236
|
+
appendFlairBinWarning(lines, r);
|
|
224
237
|
return { lines, ok: false };
|
|
225
238
|
}
|
|
226
239
|
if (!r.firstRunVerified) {
|
|
@@ -272,6 +285,7 @@ export function formatEnableReport(r, input) {
|
|
|
272
285
|
}
|
|
273
286
|
lines.push("");
|
|
274
287
|
lines.push(` Check anytime with: flair rem nightly status`);
|
|
288
|
+
appendFlairBinWarning(lines, r);
|
|
275
289
|
return { lines, ok: false };
|
|
276
290
|
}
|
|
277
291
|
const lines = [
|
|
@@ -288,6 +302,7 @@ export function formatEnableReport(r, input) {
|
|
|
288
302
|
lines.push(` First run: completed through the service manager, exit 0`);
|
|
289
303
|
lines.push("");
|
|
290
304
|
lines.push(`Disable with \`flair rem nightly disable\`.`);
|
|
305
|
+
appendFlairBinWarning(lines, r);
|
|
291
306
|
return { lines, ok: true };
|
|
292
307
|
}
|
|
293
308
|
/**
|
|
@@ -329,7 +344,8 @@ export function formatStatusReport(s) {
|
|
|
329
344
|
*/
|
|
330
345
|
export function enableScheduler(opts) {
|
|
331
346
|
const plat = detectPlatform(opts.platformOverride);
|
|
332
|
-
const
|
|
347
|
+
const resolvedFlair = resolveFlairBin(opts.flairBin);
|
|
348
|
+
const flairBin = resolvedFlair.path;
|
|
333
349
|
const nodeBin = resolveNodeBin(opts.nodeBin);
|
|
334
350
|
const shimPath = opts.shimPathOverride ?? SHIM_PATH_DEFAULT;
|
|
335
351
|
const templateRoot = opts.templateRootOverride ?? defaultTemplateRoot();
|
|
@@ -383,6 +399,7 @@ export function enableScheduler(opts) {
|
|
|
383
399
|
return {
|
|
384
400
|
platform: plat, shimPath, schedulerPath: plistPath, loadCommand, loadResult,
|
|
385
401
|
firstRunVerified: firstRun?.verified === true, firstRun,
|
|
402
|
+
flairBin, flairBinCanonical: resolvedFlair.canonical, flairBinPublic: resolvedFlair.publicBin,
|
|
386
403
|
};
|
|
387
404
|
}
|
|
388
405
|
// Linux: systemd user units.
|
|
@@ -408,6 +425,7 @@ export function enableScheduler(opts) {
|
|
|
408
425
|
return {
|
|
409
426
|
platform: plat, shimPath, schedulerPath: timerPath, loadCommand, loadResult,
|
|
410
427
|
firstRunVerified: firstRun?.verified === true, firstRun,
|
|
428
|
+
flairBin, flairBinCanonical: resolvedFlair.canonical, flairBinPublic: resolvedFlair.publicBin,
|
|
411
429
|
};
|
|
412
430
|
}
|
|
413
431
|
/**
|
package/docs/auth.md
CHANGED
|
@@ -39,6 +39,11 @@ flair agent add myagent
|
|
|
39
39
|
|
|
40
40
|
This is the default and recommended auth for single-instance deployments.
|
|
41
41
|
|
|
42
|
+
Adapter or hosted-Flair 404? Identity is three things (keyfile + agent id +
|
|
43
|
+
the `Agent` row on **that** instance). A by-id 404 is fail-closed ownership,
|
|
44
|
+
never an existence signal. The adapter write-up is
|
|
45
|
+
[integrations.md — Hosted Flair auth](integrations.md#hosted-flair-auth--your-agent-got-a-404).
|
|
46
|
+
|
|
42
47
|
## Deployment shapes: personal vs org
|
|
43
48
|
|
|
44
49
|
Flair has no `mode`/`shape` config setting — the shape you get is emergent from *how you provision principals*, not something you declare:
|
package/docs/deepseek-harness.md
CHANGED
|
@@ -4,7 +4,7 @@ Give DeepSeek Harness (DSH) sessions persistent, portable memory — no plugin c
|
|
|
4
4
|
|
|
5
5
|
> **Verified against DSH as of 2026-08-20** (`deepseek-ai/deepseek-harness`, branch `master`). DSH is a developer preview and its own README promises compatibility-breaking changes. If wiring fails after a DSH upgrade, re-check the config field names against [their MCP client README](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/mcp/mcp-client/README.md) before suspecting Flair.
|
|
6
6
|
|
|
7
|
-
The same
|
|
7
|
+
The same twelve tools every other MCP client gets ([full table in mcp-clients.md](mcp-clients.md#what-the-mcp-server-exposes)) appear to the model under DSH's server-qualified names: `mcp__flair__memory_search`, `mcp__flair__memory_store`, `mcp__flair__bootstrap`, and so on — the same `mcp__<server>__<tool>` convention Claude Code uses.
|
|
8
8
|
|
|
9
9
|
Two caveats up front, both structural to DSH's bridge (details below):
|
|
10
10
|
|
package/docs/hosted-on-fabric.md
CHANGED
|
@@ -138,6 +138,8 @@ flair agent add mybot --target "$FLAIR_URL" --ops-target <ops-url>
|
|
|
138
138
|
|
|
139
139
|
Auth is the same protocol as standalone: Ed25519 signature of `agentId:timestamp:nonce:METHOD:/path`, 30-second replay window, nonce deduplication. The difference is purely the transport — HTTPS instead of localhost HTTP.
|
|
140
140
|
|
|
141
|
+
An adapter that just got a 404 is almost never "Harper wants a different verb." Check the three identity pieces (keyfile, agent id, `Agent` row on **this** instance) and treat by-id 404 as fail-closed ownership, not an existence signal: [integrations.md — Hosted Flair auth](integrations.md#hosted-flair-auth--your-agent-got-a-404).
|
|
142
|
+
|
|
141
143
|
See [secrets-and-keys.md](secrets-and-keys.md) for the full threat model.
|
|
142
144
|
|
|
143
145
|
---
|
package/docs/integrations.md
CHANGED
|
@@ -23,6 +23,8 @@ Where Flair already runs. Each integration shown here is a working surface — t
|
|
|
23
23
|
| **n8n** | [`n8n-nodes-flair`](#n8n) | FlairApi credential | Three nodes (chat memory, search, store) |
|
|
24
24
|
| **Hermes Agent** | [`hermes-flair`](#hermes-agent) | Ed25519 | Python `MemoryProvider` |
|
|
25
25
|
| **Pi agent** | [`pi-flair`](#pi-agent) | Ed25519 | Native pi extension (pi has no MCP support); `flair init --client pi` wires it, `flair doctor` checks it |
|
|
26
|
+
| **Google ADK** (Python) | [`adk-flair`](../packages/adk-flair/README.md) | Ed25519 | `BaseMemoryService`; see [hosted auth](#hosted-flair-auth--your-agent-got-a-404) if you just got a 404 |
|
|
27
|
+
| **Google ADK** (JS/TS) | [`@tpsdev-ai/adk-flair`](../packages/adk-flair-js/README.md) | Ed25519 | Same identity model as the Python package |
|
|
26
28
|
|
|
27
29
|
Don't see your harness? If it speaks **MCP** — Flair already works with `flair-mcp`. If it has a **custom memory protocol** like LangGraph's `BaseStore` or CrewAI's `RAGStorage`, an adapter is a ~200-line package; [open an issue](https://github.com/tpsdev-ai/flair/issues) or [send a PR](https://github.com/tpsdev-ai/flair).
|
|
28
30
|
|
|
@@ -32,6 +34,56 @@ Don't see your harness? If it speaks **MCP** — Flair already works with `flair
|
|
|
32
34
|
|
|
33
35
|
---
|
|
34
36
|
|
|
37
|
+
## Hosted Flair auth — your agent got a 404
|
|
38
|
+
|
|
39
|
+
Written for the person whose adapter just got a 404 against a hosted Flair (Harper Fabric or any non-localhost URL). Laptop `flair init` on `127.0.0.1:19926` is a different machine from the one signing the request.
|
|
40
|
+
|
|
41
|
+
The protocol lives in [auth.md](auth.md). Key lifecycle lives in [secrets-and-keys.md](secrets-and-keys.md). Fabric registration (including the ops port trap) lives in [quickstart-fabric.md](quickstart-fabric.md). This section is only the identity check.
|
|
42
|
+
|
|
43
|
+
### Identity is three things that must match
|
|
44
|
+
|
|
45
|
+
Ed25519 agent auth is not a password. The server accepts a request only when all three line up:
|
|
46
|
+
|
|
47
|
+
1. **Agent id** — `FLAIR_AGENT_ID`. This is the string inside the signature.
|
|
48
|
+
2. **Keyfile on the machine that signs** — `FLAIR_KEYFILE` (adk-flair / adk-flair-js) or `FLAIR_KEY_PATH` (flair-mcp, Hermes, pi). The private key never leaves that host. `flair agent add` writes `~/.flair/keys/<id>.key`.
|
|
49
|
+
3. **Server-side `Agent` row on the instance at `FLAIR_URL`** whose `publicKey` matches that keyfile. Registration is per instance. A key minted against localhost is not registered on Fabric until you run `flair agent add <id> --target` at that URL.
|
|
50
|
+
|
|
51
|
+
The signed payload is `agentId:timestamp:nonce:METHOD:/path` (30-second replay window).
|
|
52
|
+
|
|
53
|
+
Do not "fix" a 401 or 404 by pasting the Harper admin password into the agent's standing environment. Admin Basic auth is for registration, once. The first signed call against an unregistered id is **401 `unknown_agent`** — that is fail-closed working as designed.
|
|
54
|
+
|
|
55
|
+
### The three failure shapes
|
|
56
|
+
|
|
57
|
+
| Shape | What is true | What you see | What to do |
|
|
58
|
+
|---|---|---|---|
|
|
59
|
+
| **Record missing** | No `Agent` row for this id on **this** instance | `401 {"error":"unknown_agent"}` on every signed route | Register against the hosted URL: `flair agent add <id> --target "$FLAIR_URL" --ops-target <ops-url> --admin-pass-file <path>`. Fabric ops is not `data-port − 1` — see [quickstart-fabric.md](quickstart-fabric.md). |
|
|
60
|
+
| **Key mismatch** | The id exists; the public key on the server is not the one in your keyfile | `401 {"error":"invalid_signature"}` | Same id, wrong key — copied from another host, rotated on one side only, or the env pointing at a different agent's file. Point the env at the key that matches **this** instance, or re-seed the hosted `Agent` row from the key on this machine: `flair agent add <id> --target "$FLAIR_URL" --ops-target <ops-url> --admin-pass-file <path>` (reuses the local keyfile). `flair agent rotate-key` is localhost-only. Restart the adapter so it reloads the key. |
|
|
61
|
+
| **Config wrong** | Identity may be fine; you are not talking to the Flair you think | Timeouts, connection errors, or **404 from Harper's catch-all** | `FLAIR_URL` must be the origin the **signing process** can open (cloud-agent localhost is the VM, not your laptop). adk-flair also needs `FLAIR_ALLOW_REMOTE_URL=1` and a raised `FLAIR_HTTP_TIMEOUT` (defaults are localhost fail-fast). A `FLAIR_URL` with a path prefix sends every request to a route that does not exist. `/Health` can be 200 while `/Memory` is still 404 if the Flair app is not loaded yet. |
|
|
62
|
+
|
|
63
|
+
Clock skew is a fourth, rarer 401: `timestamp_out_of_window`.
|
|
64
|
+
|
|
65
|
+
`flair agent list` talks to **localhost**. It cannot tell you whether the hosted instance has your Agent row. The discriminator is the 401 body on a signed request.
|
|
66
|
+
|
|
67
|
+
### 404 on by-id routes is not an existence signal
|
|
68
|
+
|
|
69
|
+
`GET /Memory/{id}`, `PUT /Memory/{id}`, and the adapter/MCP wrappers (`memory_get`, a by-id update) return **404** both when the id is absent **and** when the record exists but your principal may not see it (another agent's `private` memory, or outside your read scope). Same status, same body shape. That is fail-closed ownership ([flair#1264](https://github.com/tpsdev-ai/flair/issues/1264)) — a 403 would confirm the id exists and can name the owner.
|
|
70
|
+
|
|
71
|
+
**Do not treat that 404 as "the record is missing, so create it" or "Harper rejected the verb."** Creates go to `POST /Memory/` (id in the body). A by-id 404 after a write usually means you are not the owner the server thinks you are — go back to the three shapes above — not that you should switch PUT for POST.
|
|
72
|
+
|
|
73
|
+
A verified agent that is not allowed the row gets 404, never 403. Anonymous by-id reads are denied at the gate.
|
|
74
|
+
|
|
75
|
+
### Per-adapter env
|
|
76
|
+
|
|
77
|
+
| Adapter | URL | Agent id | Keyfile | Hosted extras |
|
|
78
|
+
|---|---|---|---|---|
|
|
79
|
+
| **adk-flair** / **adk-flair-js** | `FLAIR_URL` | `FLAIR_AGENT_ID` | `FLAIR_KEYFILE` | `FLAIR_ALLOW_REMOTE_URL=1`, `FLAIR_HTTP_TIMEOUT` — [adk-flair README](../packages/adk-flair/README.md#hosted-flair) |
|
|
80
|
+
| **flair-mcp** / Cursor plugin | `FLAIR_URL` | `FLAIR_AGENT_ID` | `FLAIR_KEY_PATH` (optional; auto-resolved) | Key must be on the **npx host** |
|
|
81
|
+
| **Hermes / pi / LangGraph** | `FLAIR_URL` | `FLAIR_AGENT_ID` | `FLAIR_KEY_PATH` or client `keyPath` | Same Ed25519 model |
|
|
82
|
+
|
|
83
|
+
n8n still uses Harper admin Basic auth — it is not this path. See [n8n.md](n8n.md#security).
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
35
87
|
## Claude Code, Cursor, Codex, Gemini CLI, Continue.dev, Goose — via `flair-mcp`
|
|
36
88
|
|
|
37
89
|
[`@tpsdev-ai/flair-mcp`](https://www.npmjs.com/package/@tpsdev-ai/flair-mcp) is a [Model Context Protocol](https://modelcontextprotocol.io/) server that exposes Flair as a memory tool to any MCP-speaking client. One server, every MCP client.
|
|
@@ -209,7 +261,6 @@ If it has a custom memory protocol, the adapter pattern is small (~200 lines). L
|
|
|
209
261
|
- CrewAI (Python `BaseRAGStorage` protocol)
|
|
210
262
|
- AG2 / AutoGen (Python)
|
|
211
263
|
- Mastra (TS, denser thread model)
|
|
212
|
-
- ADK (Google, Python + TS)
|
|
213
264
|
|
|
214
265
|
[Open an issue](https://github.com/tpsdev-ai/flair/issues) describing the harness and we'll triage. PRs welcome — see [`packages/langgraph-flair`](../packages/langgraph-flair) as the smallest-shape reference.
|
|
215
266
|
|
|
@@ -219,6 +270,7 @@ If it has a custom memory protocol, the adapter pattern is small (~200 lines). L
|
|
|
219
270
|
|
|
220
271
|
- [Quickstart](quickstart.md) — `flair init` to working memory on a laptop
|
|
221
272
|
- [Fabric Quickstart](quickstart-fabric.md) — `flair deploy` to a reachable Harper Fabric URL
|
|
273
|
+
- [Hosted Flair auth](#hosted-flair-auth--your-agent-got-a-404) — Ed25519 identity, the three 401/404 shapes, why a by-id 404 is not an existence signal
|
|
222
274
|
- [Embedding in a Harper app](embedding-in-a-harper-app.md) — run Flair as a component of your own Harper instance and call it in-process
|
|
223
275
|
- [Memory bridges](bridges.md) — import/export Flair ↔ Mem0, ChatGPT, claude-project, markdown, agentic-stack (five bridges shipped)
|
|
224
276
|
- [Federation](federation.md) — pair instances peer-to-peer for cross-machine sync
|
package/docs/mcp-clients.md
CHANGED
|
@@ -96,10 +96,10 @@ flair hook status # wired? correct shape? which agent/instance?
|
|
|
96
96
|
flair hook uninstall # removes only Flair's hook entry
|
|
97
97
|
```
|
|
98
98
|
|
|
99
|
-
`--harness claude-code` is
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
99
|
+
`--harness` defaults to `claude-code`. `codex` is also supported (writes
|
|
100
|
+
`~/.codex/hooks.json` — see the Codex section below). `flair doctor` checks
|
|
101
|
+
the same hook for each detected harness and recognizes anything
|
|
102
|
+
`flair hook install` writes.
|
|
103
103
|
|
|
104
104
|
Or wire it by hand — add a `SessionStart` hook to `~/.claude/settings.json`:
|
|
105
105
|
|
|
@@ -111,7 +111,7 @@ Or wire it by hand — add a `SessionStart` hook to `~/.claude/settings.json`:
|
|
|
111
111
|
"hooks": [
|
|
112
112
|
{
|
|
113
113
|
"type": "command",
|
|
114
|
-
"command": "sh -c 'out=$(FLAIR_AGENT_ID=me npx -y -p @tpsdev-ai/flair-mcp flair-session-start 2>/dev/null) && printf %s \"$out\" || true'"
|
|
114
|
+
"command": "sh -c 'out=$(FLAIR_AGENT_ID=me npx -y -p @tpsdev-ai/flair-mcp@<version> flair-session-start 2>/dev/null) && printf %s \"$out\" || true'"
|
|
115
115
|
}
|
|
116
116
|
]
|
|
117
117
|
}
|
|
@@ -120,12 +120,23 @@ Or wire it by hand — add a `SessionStart` hook to `~/.claude/settings.json`:
|
|
|
120
120
|
}
|
|
121
121
|
```
|
|
122
122
|
|
|
123
|
-
Swap `me` for your `FLAIR_AGENT_ID
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
123
|
+
Swap `me` for your `FLAIR_AGENT_ID` and `<version>` for `flair --version`. This
|
|
124
|
+
is the same pin `flair init` writes into client MCP configs (`mcpServerSpec()`,
|
|
125
|
+
flair#907): a wired hook should not self-update to a freshly published
|
|
126
|
+
`flair-mcp` any more than a wired MCP client should. `flair hook install`,
|
|
127
|
+
`flair init`, and `flair doctor --fix` (when adding a missing hook) write that
|
|
128
|
+
pin; re-run `flair hook install` to advance a stale or pre-#1143 unpinned hook
|
|
129
|
+
to the running CLI's version.
|
|
130
|
+
|
|
131
|
+
That is a different surface from public plugin `mcp.json` files, which stay
|
|
132
|
+
**unpinned** on purpose (flair#1308) so directory listings that scrape them do
|
|
133
|
+
not freeze on a shipped version. User-local wiring is pinned; catalog
|
|
134
|
+
manifests are not.
|
|
135
|
+
|
|
136
|
+
`flair hook status` recognises both the current pinned `-p` form and an older
|
|
137
|
+
unpinned `-p` invocation as `correctShape`. The pre-#1166 form (no `-p`, which
|
|
138
|
+
runs the MCP shim) is still flagged. Prefer `flair hook install` over
|
|
139
|
+
hand-editing.
|
|
129
140
|
|
|
130
141
|
The `sh -c ... || true` wrapper is not decoration. The invocation resolves a
|
|
131
142
|
package binary through whatever Node runtime your shell exposes, and under a
|
|
@@ -208,6 +219,46 @@ For project-scoped trust (per Codex's MCP guide), the same block in `.codex/conf
|
|
|
208
219
|
|
|
209
220
|
Restart your Codex CLI session and the `flair_*` tools become available to the agent.
|
|
210
221
|
|
|
222
|
+
#### Auto-recall on session start (optional hook)
|
|
223
|
+
|
|
224
|
+
Wiring the MCP server alone does not load memory at session start — Codex
|
|
225
|
+
then has pull tools it never thinks to call. The same `flair-session-start`
|
|
226
|
+
command Claude Code uses writes Codex's SessionStart hook (same JSON schema,
|
|
227
|
+
into `~/.codex/hooks.json`):
|
|
228
|
+
|
|
229
|
+
```bash
|
|
230
|
+
flair hook install --harness codex
|
|
231
|
+
flair hook status --harness codex
|
|
232
|
+
flair hook uninstall --harness codex
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
`flair doctor` reports this hook when Codex is detected. After install, trust
|
|
236
|
+
the new command in Codex with `/hooks` — untrusted hooks are listed and
|
|
237
|
+
skipped.
|
|
238
|
+
|
|
239
|
+
---
|
|
240
|
+
|
|
241
|
+
## Hookless harnesses (Gemini, Cursor, and anything without SessionStart)
|
|
242
|
+
|
|
243
|
+
Some clients have no session-start hook Flair can write. Wiring the MCP
|
|
244
|
+
server is necessary but not sufficient — the model still has to choose to
|
|
245
|
+
call `bootstrap`. Add a short instruction block to the file that client
|
|
246
|
+
already loads (`AGENTS.md`, `GEMINI.md`, or the equivalent):
|
|
247
|
+
|
|
248
|
+
```markdown
|
|
249
|
+
## Flair memory
|
|
250
|
+
|
|
251
|
+
At the start of every session, call the Flair `bootstrap` tool before
|
|
252
|
+
responding. Before a deep-dive, `memory_search` for related prior work.
|
|
253
|
+
At wrap, `memory_store` durable lessons.
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
Without that, a config that looks wired is the known failure shape
|
|
257
|
+
([#989](https://github.com/tpsdev-ai/flair/issues/989),
|
|
258
|
+
[#908](https://github.com/tpsdev-ai/flair/issues/908)): tools exist, memory
|
|
259
|
+
never enters the working set. Use `flair hook install` when the client has
|
|
260
|
+
a SessionStart hook; use this static block when it does not.
|
|
261
|
+
|
|
211
262
|
---
|
|
212
263
|
|
|
213
264
|
## Step 3 — Verify
|
|
@@ -222,13 +273,13 @@ If you see (a) the agent calling the `bootstrap` tool returning soul + recent me
|
|
|
222
273
|
|
|
223
274
|
## What the MCP server exposes
|
|
224
275
|
|
|
225
|
-
|
|
276
|
+
Twelve tools, kept deliberately small:
|
|
226
277
|
|
|
227
278
|
| Tool | What it does |
|
|
228
279
|
|---|---|
|
|
229
280
|
| `memory_search` | Semantic search across your agent's memories |
|
|
230
|
-
| `memory_store` | Save a memory with type, durability, tags, visibility. Auto-dedups near-duplicates |
|
|
231
|
-
| `memory_update` | Update an existing memory by ID — overwrite in place, or version it with `preserveHistory` |
|
|
281
|
+
| `memory_store` | Save a memory with type, durability, tags, visibility. Auto-dedups near-duplicates. Optional `usedMemoryIds` cites memories that informed the write |
|
|
282
|
+
| `memory_update` | Update an existing memory by ID — overwrite in place, or version it with `preserveHistory`. Optional `usedMemoryIds` for citation-on-write |
|
|
232
283
|
| `memory_get` | Fetch a specific memory by ID |
|
|
233
284
|
| `memory_delete` | Remove a memory |
|
|
234
285
|
| `relationship_store` | Record a subject-predicate-object relationship triple (e.g. "nathan manages flair") |
|
|
@@ -237,6 +288,7 @@ Eleven tools, kept deliberately small:
|
|
|
237
288
|
| `soul_get` | Get a soul entry |
|
|
238
289
|
| `flair_workspace_set` | Set your agent's current workspace state (ref/branch, phase, task) in the Office Space |
|
|
239
290
|
| `flair_orgevent` | Publish an org-wide coordination event (claim/release/status) to the Office Space |
|
|
291
|
+
| `record_usage` | Report that recalled memories were actually used (id + optional one-line how-it-was-used). Drives `usageCount` / `usageBoost` |
|
|
240
292
|
|
|
241
293
|
Writes are scoped per-agent (your `FLAIR_AGENT_ID`) and enforced by Flair's server, not by client convention — you can't write as another agent. Reads are more open by design: any agent on the same Flair instance can read any other agent's **non-private** memories, with no grant to set up (open-within-org read; see [SECURITY.md](../SECURITY.md)).
|
|
242
294
|
|
|
@@ -298,7 +350,7 @@ Future MCP-capable agent CLIs (and there are more landing every month) will work
|
|
|
298
350
|
|
|
299
351
|
**"connection_error: could not reach Flair at http://127.0.0.1:19926".** The Flair server isn't running. Run `flair status` to check; `flair start` to bring it up.
|
|
300
352
|
|
|
301
|
-
**"auth_error: …" on every call.** The agent identity doesn't match a registered key. Re-run `flair agent add <id>` (idempotent on re-add — won't lose existing memories).
|
|
353
|
+
**"auth_error: …" on every call.** The agent identity doesn't match a registered key. Re-run `flair agent add <id>` (idempotent on re-add — won't lose existing memories). Against a hosted instance the same three shapes apply (record missing / key mismatch / config wrong), and a by-id 404 is fail-closed ownership, not an existence signal — [Hosted Flair auth](integrations.md#hosted-flair-auth--your-agent-got-a-404).
|
|
302
354
|
|
|
303
355
|
**Tool calls succeed but the agent doesn't see results in subsequent turns.** Check that the CLI is actually invoking `bootstrap` at session start — most CLIs need an explicit prompt nudge ("call the bootstrap tool now") on first use. Subsequent turns should pick up automatically once the CLI sees the schema.
|
|
304
356
|
|
|
@@ -119,7 +119,7 @@ Same connector, different panel. This is the field-verified sequence — includi
|
|
|
119
119
|
|
|
120
120
|
Keep the pass file mode `0600`. Newer releases add `--admin-pass-file <path>`, which reads the file in-process (invisible to `ps`) — check `flair agent add --help` and prefer it when present.
|
|
121
121
|
|
|
122
|
-
5.
|
|
122
|
+
5. The next tool call picks up the key — a miss is not cached. A 401 names the agent and the paths that were looked in. If those are not where `flair agent add` wrote the file, the MCP process's home differs from the shell; set `FLAIR_KEY_PATH` to the absolute path of the `.key` file.
|
|
123
123
|
|
|
124
124
|
6. Verify: ask the agent to "load my Flair bootstrap". You should get soul + memories **including shared org context** — findings written by teammate agents. A shared-visibility write from this agent is now readable by every org agent.
|
|
125
125
|
|
package/docs/troubleshooting.md
CHANGED
|
@@ -105,6 +105,31 @@ date
|
|
|
105
105
|
|
|
106
106
|
If using the MCP server, restart Claude Code after rotating keys.
|
|
107
107
|
|
|
108
|
+
`flair agent list` is localhost-only. Against a hosted instance, the
|
|
109
|
+
discriminator is the 401 body on a signed request (`unknown_agent` vs
|
|
110
|
+
`invalid_signature`), not a local agent list. Adapter-shaped walkthrough:
|
|
111
|
+
[integrations.md — Hosted Flair auth](integrations.md#hosted-flair-auth--your-agent-got-a-404).
|
|
112
|
+
|
|
113
|
+
### 404 on `GET`/`PUT /Memory/{id}`
|
|
114
|
+
|
|
115
|
+
**Symptoms:** `memory_get`, a by-id update, or an adapter write/read against
|
|
116
|
+
`/Memory/{id}` returns 404. The agent reports "not found" or "Harper rejected
|
|
117
|
+
the verb."
|
|
118
|
+
|
|
119
|
+
**This is not an existence signal.** By-id routes return the same 404 when
|
|
120
|
+
the id is absent and when the record exists but the caller may not see it
|
|
121
|
+
(fail-closed ownership, [flair#1264](https://github.com/tpsdev-ai/flair/issues/1264)).
|
|
122
|
+
A 403 would confirm the id and can name the owner; Flair refuses that.
|
|
123
|
+
|
|
124
|
+
**Also not this:** `401 unknown_agent` / `401 invalid_signature` (identity —
|
|
125
|
+
see above). Harper's catch-all 404 when the Flair app is not loaded yet
|
|
126
|
+
(`/Health` can already be 200). A `FLAIR_URL` with a path prefix.
|
|
127
|
+
|
|
128
|
+
Creates go to `POST /Memory/` (id in the body). Do not treat a by-id 404 as
|
|
129
|
+
a reason to paste admin credentials into the agent's environment.
|
|
130
|
+
|
|
131
|
+
Full adapter guide: [integrations.md — Hosted Flair auth](integrations.md#hosted-flair-auth--your-agent-got-a-404).
|
|
132
|
+
|
|
108
133
|
### "signing key ... could not be parsed as an Ed25519 private key"
|
|
109
134
|
|
|
110
135
|
**Symptoms:** `flair doctor` reports, naming the file:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tpsdev-ai/flair",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.50.0",
|
|
4
4
|
"packageManager": "bun@1.3.10",
|
|
5
5
|
"description": "Identity, memory, and soul for AI agents. Cryptographic identity (Ed25519), semantic memory with local embeddings, and persistent personality — all in a single process.",
|
|
6
6
|
"type": "module",
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
"access": "public"
|
|
60
60
|
},
|
|
61
61
|
"engines": {
|
|
62
|
-
"node": ">=
|
|
62
|
+
"node": "^22.18.0 || >=24.0.0"
|
|
63
63
|
},
|
|
64
64
|
"dependencies": {
|
|
65
65
|
"@harperfast/oauth": "2.5.0",
|