@tpsdev-ai/flair 0.29.0 → 0.31.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.
Files changed (117) hide show
  1. package/README.md +194 -377
  2. package/dist/cli-shim.cjs +1 -1
  3. package/dist/cli.js +1567 -366
  4. package/dist/deploy.js +241 -43
  5. package/dist/fabric-upgrade.js +77 -24
  6. package/dist/federation/scheduler.js +500 -0
  7. package/dist/fleet-verify.js +3 -3
  8. package/dist/install/clients.js +111 -53
  9. package/dist/lib/mcp-enable.js +2 -2
  10. package/dist/lib/mcp-spec.js +128 -0
  11. package/dist/lib/safe-snapshot-extract.js +231 -0
  12. package/dist/lib/scheduler-platform.js +128 -0
  13. package/dist/lib/xml-escape.js +54 -0
  14. package/dist/rem/scheduler.js +212 -55
  15. package/dist/rem/snapshot.js +13 -0
  16. package/dist/replication-convergence.js +505 -0
  17. package/dist/resources/A2AAdapter.js +1 -1
  18. package/dist/resources/Admin.js +1 -1
  19. package/dist/resources/AdminConnectors.js +1 -1
  20. package/dist/resources/AdminDashboard.js +1 -1
  21. package/dist/resources/AdminIdp.js +1 -1
  22. package/dist/resources/AdminInstance.js +1 -1
  23. package/dist/resources/AdminMemory.js +1 -1
  24. package/dist/resources/AdminPrincipals.js +1 -1
  25. package/dist/resources/Agent.js +1 -1
  26. package/dist/resources/AgentCard.js +1 -1
  27. package/dist/resources/AgentSeed.js +1 -1
  28. package/dist/resources/AttentionQuery.js +1 -1
  29. package/dist/resources/Credential.js +1 -1
  30. package/dist/resources/Federation.js +1 -1
  31. package/dist/resources/Instance.js +1 -1
  32. package/dist/resources/Integration.js +1 -1
  33. package/dist/resources/MCPClientMetadata.js +1 -1
  34. package/dist/resources/Memory.js +1 -1
  35. package/dist/resources/MemoryBootstrap.js +8 -9
  36. package/dist/resources/MemoryCandidate.js +168 -0
  37. package/dist/resources/MemoryConsolidate.js +1 -1
  38. package/dist/resources/MemoryDedupStats.js +1 -1
  39. package/dist/resources/MemoryFeed.js +1 -1
  40. package/dist/resources/MemoryGrant.js +1 -1
  41. package/dist/resources/MemoryMaintenance.js +1 -1
  42. package/dist/resources/MemoryReflect.js +2 -2
  43. package/dist/resources/MemoryReindex.js +1 -1
  44. package/dist/resources/MemoryUsage.js +1 -1
  45. package/dist/resources/OAuth.js +1 -1
  46. package/dist/resources/OAuthClient.js +1 -1
  47. package/dist/resources/OrgEvent.js +1 -1
  48. package/dist/resources/OrgEventCatchup.js +1 -1
  49. package/dist/resources/OrgEventMaintenance.js +1 -1
  50. package/dist/resources/PairingToken.js +1 -1
  51. package/dist/resources/Peer.js +1 -1
  52. package/dist/resources/Presence.js +3 -3
  53. package/dist/resources/RecordUsage.js +1 -1
  54. package/dist/resources/Relationship.js +1 -1
  55. package/dist/resources/SemanticSearch.js +18 -46
  56. package/dist/resources/SkillScan.js +1 -1
  57. package/dist/resources/Soul.js +1 -1
  58. package/dist/resources/SoulFeed.js +1 -1
  59. package/dist/resources/WorkspaceLatest.js +1 -1
  60. package/dist/resources/WorkspaceState.js +1 -1
  61. package/dist/resources/XAA.js +1 -1
  62. package/dist/resources/a2a-url.js +1 -1
  63. package/dist/resources/abstention.js +1 -1
  64. package/dist/resources/agent-auth.js +1 -1
  65. package/dist/resources/agentcard-fields.js +1 -1
  66. package/dist/resources/auth-middleware.js +1 -1
  67. package/dist/resources/embeddings-boot.js +14 -16
  68. package/dist/resources/embeddings-provider.js +15 -12
  69. package/dist/resources/federation-cleanup.js +5 -5
  70. package/dist/resources/federation-nonce-store.js +5 -5
  71. package/dist/resources/health.js +53 -25
  72. package/dist/resources/in-process.js +225 -0
  73. package/dist/resources/instance-identity.js +1 -1
  74. package/dist/resources/mcp-client-metadata-fields.js +1 -1
  75. package/dist/resources/mcp-handler.js +1 -1
  76. package/dist/resources/mcp-oauth.js +2 -2
  77. package/dist/resources/mcp-tools.js +23 -17
  78. package/dist/resources/memory-read-scope.js +1 -1
  79. package/dist/resources/memory-reflect-lib.js +3 -3
  80. package/dist/resources/memory-visibility.js +3 -3
  81. package/dist/resources/migration-boot.js +81 -11
  82. package/dist/resources/migrations/data-dir.js +205 -0
  83. package/dist/resources/migrations/embedding-stamp.js +3 -3
  84. package/dist/resources/migrations/graph-heal.js +2 -2
  85. package/dist/resources/migrations/ledger.js +1 -1
  86. package/dist/resources/migrations/progress.js +33 -0
  87. package/dist/resources/migrations/registry.js +14 -5
  88. package/dist/resources/migrations/runner.js +93 -13
  89. package/dist/resources/migrations/state.js +13 -2
  90. package/dist/resources/migrations/synthetic-test-migration.js +1 -1
  91. package/dist/resources/migrations/visibility-backfill.js +232 -0
  92. package/dist/resources/models-dir.js +18 -9
  93. package/dist/resources/record-types.js +28 -0
  94. package/dist/resources/semantic-retrieval-core.js +6 -5
  95. package/dist/resources/usage-recording.js +1 -1
  96. package/dist/src/lib/scheduler-platform.js +128 -0
  97. package/dist/src/lib/xml-escape.js +54 -0
  98. package/dist/src/rem/scheduler.js +397 -0
  99. package/docs/deploying-on-fabric.md +267 -0
  100. package/docs/deployment.md +6 -0
  101. package/docs/embedding-in-a-harper-app.md +299 -0
  102. package/docs/federation.md +61 -4
  103. package/docs/integrations.md +3 -0
  104. package/docs/mcp-clients.md +16 -7
  105. package/docs/quickstart.md +80 -54
  106. package/docs/releasing.md +72 -38
  107. package/docs/supply-chain-policy.md +40 -4
  108. package/docs/troubleshooting.md +24 -0
  109. package/docs/upgrade.md +100 -5
  110. package/package.json +4 -14
  111. package/schemas/memory.graphql +9 -1
  112. package/templates/bin/flair-federation-sync.sh.tmpl +28 -0
  113. package/templates/launchd/dev.flair.federation.sync.plist.tmpl +47 -0
  114. package/templates/systemd/flair-federation-sync.service.tmpl +21 -0
  115. package/templates/systemd/flair-federation-sync.timer.tmpl +16 -0
  116. package/dist/resources/rerank-provider.js +0 -569
  117. package/docs/rerank-provisioning.md +0 -101
@@ -81,15 +81,69 @@ flair federation pair https://<fabric-node>:19926/<instance-name> --token-from .
81
81
 
82
82
  Replace `<fabric-node>`, `<instance-name>`, and `<hub-admin-password>` with your actual values.
83
83
 
84
+ Running the hub on Fabric has its own considerations — port derivation against a managed
85
+ `443` endpoint, why the sync driver can only be installed on a machine you control, and
86
+ the observability limits of a node you have no shell on. See
87
+ [`docs/deploying-on-fabric.md`](deploying-on-fabric.md).
88
+
84
89
  ## Sync
85
90
 
86
- Push local changes to the hub:
91
+ Push local changes to the hub, once:
87
92
 
88
93
  ```bash
89
94
  flair federation sync --admin-pass <password>
90
95
  # Output: ✅ Synced 12 records (0 skipped) in 145ms
91
96
  ```
92
97
 
98
+ ### Keeping it synced
99
+
100
+ `flair federation sync` is one-shot and `flair federation watch` only runs
101
+ while its terminal is open. Neither survives a logout, so a spoke that is only
102
+ ever synced by hand looks paired but stops replicating — enable the scheduled
103
+ driver instead:
104
+
105
+ ```bash
106
+ flair federation sync enable # every 300s by default
107
+ flair federation sync enable --interval 900 # or pick your own cadence
108
+ flair federation sync status # is anything actually driving sync?
109
+ flair federation sync disable
110
+ ```
111
+
112
+ This installs a **periodic one-shot**: a launchd job (`StartInterval`) on
113
+ macOS, a systemd user timer (`OnUnitActiveSec`) on Linux, each invoking
114
+ `flair federation sync` on the interval. It is deliberately not a supervised
115
+ long-lived watcher — the sync holds no state between runs, so a resident
116
+ process would buy nothing, and a supervisor cannot restart a process that
117
+ hangs rather than exits. The trade-off is latency, and `--interval` is the
118
+ knob. The first sync runs immediately on enable.
119
+
120
+ `flair federation watch` is unchanged and still the right tool for an
121
+ interactive "watch it sync while I debug" session.
122
+
123
+ **Credentials.** The scheduler never writes a password into a unit file. It
124
+ stores the *path* given to `--admin-pass-file` (defaulting to
125
+ `~/.flair/admin-pass` when that exists) and the CLI reads the file at run time,
126
+ refusing it unless it is owner-only (`chmod 600`). Pass `--no-credentials` to
127
+ wire none at all.
128
+
129
+ ### Is anything driving sync?
130
+
131
+ `flair federation status` reports the driver alongside the peer table, because
132
+ "no peer has merged in 24h" has two completely different causes:
133
+
134
+ | What you see | What it means | What to do |
135
+ |---|---|---|
136
+ | `Sync driver: active` | A managed driver is loaded and syncs are landing | Nothing |
137
+ | `Sync driver: active … but no peer contact in <window>` | Sync **is** running; the runs are not reaching the peer | `flair federation reachability`, then the driver log |
138
+ | `Sync driver: NONE` | Nothing has run sync since you paired | `flair federation sync enable` |
139
+ | `Sync driver: INSTALLED BUT NOT LOADED` | Unit files exist, the service manager never loaded them | `flair federation sync enable` |
140
+ | `Sync driver: none managed by Flair — but syncs are landing` | A cron entry / hand-written unit is driving it | Nothing |
141
+
142
+ The check is local to the machine running the CLI, so it is omitted when
143
+ `--target` points at a remote instance.
144
+
145
+ Driver logs: `~/.flair/logs/federation-sync.{stdout,stderr}.log`.
146
+
93
147
  ## Security
94
148
 
95
149
  ### Signed requests
@@ -125,10 +179,13 @@ Records with `updatedAt` more than 5 minutes in the future are rejected. This pr
125
179
 
126
180
  | Command | Description |
127
181
  |---------|-------------|
128
- | `flair federation status` | Show instance identity and peer connections |
182
+ | `flair federation status` | Show instance identity, peer connections, and whether anything is driving sync |
129
183
  | `flair federation pair <hub-url> --token-from <file>` | Pair this spoke with a hub using a token triple file (or `-` for stdin) |
130
184
  | `flair federation sync` | Push local changes to the hub (one-shot) |
131
- | `flair federation watch [--interval <s>]` | Run sync in a foreground daemon loop (default 30s) |
185
+ | `flair federation sync enable [--interval <s>] [--admin-pass-file <path>]` | Install the scheduled sync driver (launchd on macOS, systemd timer on Linux) |
186
+ | `flair federation sync disable [--remove-shim]` | Remove the scheduled sync driver |
187
+ | `flair federation sync status` | Show whether the driver is installed and genuinely active |
188
+ | `flair federation watch [--interval <s>]` | Run sync in a foreground loop for an interactive session (default 30s) |
132
189
  | `flair federation reachability` | Probe local instance + each paired peer (read-only) |
133
190
  | `flair federation token [--ttl <min>]` | Generate a one-time pairing token triple (hub only) |
134
191
 
@@ -174,6 +231,6 @@ flair federation unpin <instanceId>
174
231
  ## Limitations (1.0)
175
232
 
176
233
  - **HTTP push only** — no persistent WebSocket connections or real-time sync
177
- - **Manual or watch-loop sync** — `flair federation sync` is one-shot; `flair federation watch --interval 30` runs it on a loop in a foreground daemon (wrap in launchd / systemd for production)
234
+ - **Polled sync** — `flair federation sync enable` schedules a periodic one-shot (launchd / systemd, default 300s); there is no write-path trigger, so a new memory replicates on the next tick rather than immediately
178
235
  - **Single hub** — spoke-to-spoke sync goes through the hub
179
236
  - **Record-level LWW** — not field-level; concurrent edits to different fields of the same record may lose data
@@ -24,6 +24,8 @@ Where Flair already runs. Each integration shown here is a working surface — t
24
24
 
25
25
  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).
26
26
 
27
+ **Already running on Harper?** Every surface above reaches Flair over HTTP. If your application is itself a Harper app, you can skip the network entirely — load Flair as a component of the same instance and call its resources in-process. See [embedding-in-a-harper-app.md](embedding-in-a-harper-app.md), which also covers the table-vs-resource distinction that decides whether your memories are scoped.
28
+
27
29
  **Adjacent: memory bridges** — for moving memories between Flair and another memory product. Five bridges ship today (Mem0, ChatGPT exports, claude-project files, agentic-stack, markdown); see [bridges.md](bridges.md). Bridges are import/export plumbing, not live orchestrator integrations.
28
30
 
29
31
  ---
@@ -191,6 +193,7 @@ If it has a custom memory protocol, the adapter pattern is small (~200 lines). L
191
193
  ## See also
192
194
 
193
195
  - [Quickstart](quickstart.md) — `flair init` to working memory in 30 seconds
196
+ - [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
194
197
  - [Memory bridges](bridges.md) — import/export Flair ↔ Mem0, ChatGPT, claude-project, markdown, agentic-stack (five bridges shipped)
195
198
  - [Federation](federation.md) — pair instances peer-to-peer for cross-machine sync
196
199
  - [Supply-chain policy](supply-chain-policy.md) — what we do to keep this list of integrations safe
@@ -38,7 +38,9 @@ Flair runs as a local server at `http://127.0.0.1:19926` by default. The MCP ser
38
38
 
39
39
  Pick whichever you use. The MCP server is the same package; only the config syntax differs.
40
40
 
41
- > **Pin the version.** The snippets below use the bare package name for readability. `flair init` wires clients to a **pinned** spec (`@tpsdev-ai/flair-mcp@<version>`) on purpose: an unpinned reference re-resolves to whatever is currently published on every agent session, so any future publish reaches your machine silently. If you wire by hand, append the version you intend to run `@tpsdev-ai/flair-mcp@0.28.0` and bump it deliberately. `flair init` is the easier path and does this for you.
41
+ > **Pin the version.** An unpinned `@tpsdev-ai/flair-mcp` re-resolves to whatever is currently published on *every* agent session, so any future publish reaches your machine silently, with no lockfile and no review step in the path. `flair init` wires clients to a **pinned** spec on purpose, and every MCP-server config snippet below is written the same way: `@tpsdev-ai/flair-mcp@<version>`.
42
+ >
43
+ > Replace `<version>` with the version you intend to run — the one you already have is `flair --version` — and bump it deliberately. Leaving the literal `<version>` in place fails loudly at `npx`, which is the intended failure: better than a config that looks pinned and isn't. `flair init` is the easier path and fills this in for you.
42
44
 
43
45
  ### Claude Code
44
46
 
@@ -47,14 +49,14 @@ The canonical approach is the `claude mcp add` CLI (writes to `~/.claude.json`):
47
49
  ```bash
48
50
  claude mcp add flair --scope user \
49
51
  -e FLAIR_AGENT_ID=my-project \
50
- -- npx -y @tpsdev-ai/flair-mcp
52
+ -- npx -y @tpsdev-ai/flair-mcp@<version>
51
53
  ```
52
54
 
53
55
  Verify:
54
56
 
55
57
  ```bash
56
58
  claude mcp list
57
- # → flair (stdio, npx -y @tpsdev-ai/flair-mcp)
59
+ # → flair (stdio, npx -y @tpsdev-ai/flair-mcp@<version>)
58
60
  ```
59
61
 
60
62
  Or, if you prefer the project-scoped `.mcp.json` checked into your repo:
@@ -64,7 +66,7 @@ Or, if you prefer the project-scoped `.mcp.json` checked into your repo:
64
66
  "mcpServers": {
65
67
  "flair": {
66
68
  "command": "npx",
67
- "args": ["-y", "@tpsdev-ai/flair-mcp"],
69
+ "args": ["-y", "@tpsdev-ai/flair-mcp@<version>"],
68
70
  "env": {
69
71
  "FLAIR_AGENT_ID": "my-project"
70
72
  }
@@ -116,7 +118,14 @@ Or wire it by hand — add a `SessionStart` hook to `~/.claude/settings.json`:
116
118
  }
117
119
  ```
118
120
 
119
- Swap `me` for your `FLAIR_AGENT_ID`. The hook reads Claude Code's SessionStart
121
+ Swap `me` for your `FLAIR_AGENT_ID`. Unlike the MCP-server snippets above, this
122
+ one is shown **unpinned**, because that is what `flair hook install` writes and
123
+ what the tooling recognises: `flair hook status` matches the exact unpinned
124
+ command, so a hand-pinned hook reports `wired: false` there (while `flair
125
+ doctor` still sees it). Pinning this line is therefore not yet supported —
126
+ prefer `flair hook install`.
127
+
128
+ The hook reads Claude Code's SessionStart
120
129
  payload on stdin, calls Flair's `bootstrap` (soul + relevant memories +
121
130
  predicted context, scoped to your project by the session's working directory),
122
131
  and emits it as `hookSpecificOutput.additionalContext` — which Claude Code
@@ -143,7 +152,7 @@ Edit `~/.gemini/settings.json` (create it if absent):
143
152
  "mcpServers": {
144
153
  "flair": {
145
154
  "command": "npx",
146
- "args": ["-y", "@tpsdev-ai/flair-mcp"],
155
+ "args": ["-y", "@tpsdev-ai/flair-mcp@<version>"],
147
156
  "env": {
148
157
  "FLAIR_AGENT_ID": "my-project"
149
158
  }
@@ -165,7 +174,7 @@ Edit `~/.codex/config.toml` (create it if absent):
165
174
  ```toml
166
175
  [mcp_servers.flair]
167
176
  command = "npx"
168
- args = ["-y", "@tpsdev-ai/flair-mcp"]
177
+ args = ["-y", "@tpsdev-ai/flair-mcp@<version>"]
169
178
 
170
179
  [mcp_servers.flair.env]
171
180
  FLAIR_AGENT_ID = "my-project"
@@ -2,37 +2,50 @@
2
2
 
3
3
  From zero to a persistent agent memory in five minutes.
4
4
 
5
- ## 0. Prerequisite
5
+ ## 0. Prerequisites
6
6
 
7
- **Node.js 22 or newer.** That's it — no Docker, no database to install, no API keys. Flair runs in a single process with embeddings computed locally.
7
+ **Node.js 22 or newer.** No Docker, no database to install, no API keys Flair runs in a single process and computes embeddings locally.
8
8
 
9
9
  ```bash
10
- node --version # v22.x.x or higher
10
+ node --version # v22.x.x or newer
11
11
  ```
12
12
 
13
- If you need Node 22: [nodejs.org](https://nodejs.org/) or `brew install node@24`.
13
+ Need it? [nodejs.org](https://nodejs.org/), `nvm install 22`, or `brew install node`.
14
14
 
15
- ## 1. Install (30 seconds)
15
+ **A user-writable npm global prefix.** Do not install Flair with `sudo`. A root-owned install cannot write the embedding model into its own package directory, and semantic search silently falls back to keyword-only matching. `nvm` gives you a user-owned prefix already; otherwise:
16
+
17
+ ```bash
18
+ mkdir -p ~/.npm-global
19
+ npm config set prefix ~/.npm-global
20
+ export PATH="$HOME/.npm-global/bin:$PATH" # add this to ~/.zshrc or ~/.bashrc
21
+ ```
22
+
23
+ ## 1. Install
16
24
 
17
25
  ```bash
18
26
  npm install -g @tpsdev-ai/flair
19
27
  ```
20
28
 
21
- `flair`, `flair-mcp`, and the client library all come in under one install.
29
+ One install gives you `flair`, `flair-mcp` and the client library.
22
30
 
23
- ## 2. Bootstrap Flair (1–2 minutes)
31
+ ## 2. Bootstrap Flair and register an agent
24
32
 
25
33
  ```bash
26
- flair init
34
+ flair init --agent local
27
35
  ```
28
36
 
29
- First run does a few things:
37
+ > **Pass `--agent`.** A bare `flair init` bootstraps the instance and stops there — it prints `✅ Flair initialized (no agent registered)` and creates no keypair, no soul, and no MCP wiring.
38
+
39
+ First run does six things:
30
40
 
31
- 1. Installs the embedded Harper (memory store) into `~/.flair/data/`.
32
- 2. Downloads the local embedding model (~80 MB first run only).
41
+ 1. Installs the embedded Harper memory store into `~/.flair/data/`.
42
+ 2. Downloads the local embedding model — about 80 MB, first run only.
33
43
  3. Starts Flair as a launchd / systemd service on port 19926.
34
- 4. Creates a default agent (`--agent-id local` unless you pass one).
35
- 5. Opens a short **soul wizard** so your agent knows who it is.
44
+ 4. Generates the agent's Ed25519 keypair in `~/.flair/keys/` and registers it.
45
+ 5. Wires every MCP client it detects Claude Code, Cursor, Codex CLI, Gemini CLI — to `npx -y @tpsdev-ai/flair-mcp`, then smoke-tests it. `--no-mcp` skips this.
46
+ 6. Opens a short **soul wizard** so your agent knows who it is.
47
+
48
+ It also generates a Harper admin password and writes it to `~/.flair/admin-pass` (mode 0600). The CLI prints that **path**, never the value — read the file when a command needs the password, and prefer `--admin-pass-file ~/.flair/admin-pass` over pasting it into a command line.
36
49
 
37
50
  The soul wizard offers a few shapes:
38
51
 
@@ -49,7 +62,7 @@ The soul wizard offers a few shapes:
49
62
  (s) Skip — set up later; `flair doctor` will nudge
50
63
  ```
51
64
 
52
- Pick the template that matches how you'll use this agent. You can edit or replace any entry later with `flair soul set`.
65
+ Pick the template that matches how you'll use the agent. Edit or replace any entry later with `flair soul set`. The wizard is skipped automatically in a non-interactive shell, and by `--skip-soul`.
53
66
 
54
67
  ## 3. Confirm it's running
55
68
 
@@ -57,61 +70,68 @@ Pick the template that matches how you'll use this agent. You can edit or replac
57
70
  flair status
58
71
  ```
59
72
 
60
- A bare `flair status` (no agent, no admin credentials) only shows the public health
61
- summary:
62
-
63
73
  ```
64
- Flair vX.Y.Z — 🟢 running (PID 12345, uptime 1m)
65
- URL: http://127.0.0.1:19926
74
+ Flair vX.Y.Z — running PID 12345 · uptime 0m
75
+ URL http://127.0.0.1:19926
66
76
 
67
- ✓ all checks passing
68
- ```
77
+ Memory
78
+ Total 0
79
+ Durability permanent:0 · persistent:0 · standard:0 · ephemeral:0
80
+ Archived 0
69
81
 
70
- The **🟢** icon means everything is healthy. A **🟡** would mean there's something worth looking at — usually surfaced with a recommended command inline. See [docs/troubleshooting.md](troubleshooting.md) if you see **🔴 unreachable**.
82
+ Agents
83
+ Total 1 — local
71
84
 
72
- For the full picture — Memory, Agents, and Soul detail — pass the agent you just created:
85
+ REM
86
+ Nightly disabled
73
87
 
74
- ```bash
75
- flair status --agent local
76
- ```
88
+ Federation not configured
77
89
 
78
- ```
79
- Flair vX.Y.Z — 🟢 running (PID 12345, uptime 1m)
80
- URL: http://127.0.0.1:19926
90
+ Bridges none installed
81
91
 
82
- Memory:
83
- Total: 0
84
- Durability: 0 permanent / 0 persistent / 0 standard / 0 ephemeral
85
-
86
- Agents:
87
- 1 total — local
88
-
89
- Soul:
90
- 3 entries — 0 critical / 0 high / 3 standard / 0 low
92
+ Disk
93
+ Data ~/.flair/data — 0 B
94
+ Snapshots ~/.flair/snapshots 0 B
91
95
 
92
96
  ✓ all checks passing
93
97
  ```
94
98
 
99
+ You will also see a **Soul** section if you completed the wizard in step 2.
100
+
101
+ `✓` means healthy. `⚠` means something is worth looking at, and the warning names the command that fixes it. For `✗ unreachable`, see [troubleshooting.md](troubleshooting.md).
102
+
95
103
  ## 4. Write your first memory
96
104
 
97
105
  ```bash
98
- flair memory add --agent local --content "Harper v5 sandbox blocks node:module but process.dlopen works"
106
+ flair memory add --agent local "Harper v5 sandbox blocks node:module but process.dlopen works"
107
+ ```
108
+
109
+ ```json
110
+ {
111
+ "id": "local-1785277247486",
112
+ "written": true,
113
+ "deduplicated": false
114
+ }
99
115
  ```
100
116
 
101
- Flair generates a semantic embedding locally and stores it. No network calls.
117
+ Flair embedded the text locally on write. No network calls.
102
118
 
103
119
  ## 5. Find it back by meaning
104
120
 
105
121
  ```bash
106
- flair memory search --agent local --q "native addon loading in sandboxed runtimes"
122
+ flair search --agent local "native addon loading in sandboxed runtimes"
107
123
  ```
108
124
 
109
125
  ```
110
126
  Harper v5 sandbox blocks node:module but process.dlopen works
111
- (2026-04-22 · memory · 67%)
127
+ ( 2026-07-28 · standard · 100% )
112
128
  ```
113
129
 
114
- **You searched for a concept, not the keywords.** The 67% is the semantic-similarity score.
130
+ You searched for a concept, not the keywords. The line under each hit is its creation date, durability tier, and rank score.
131
+
132
+ > The percentage is a **rank-fusion score, not a similarity**. It is normalized so the top result is always near 100%. Read it as ordering within these results, never as confidence that the match is good.
133
+
134
+ Add `--explain` to see the ranking inputs, or `--limit`, `--tag`, `--since 7d` to narrow the search. `flair memory search` runs the same query but always prints raw JSON — use it when piping to a script.
115
135
 
116
136
  ## 6. Give your agent context on boot
117
137
 
@@ -119,35 +139,41 @@ flair memory search --agent local --q "native addon loading in sandboxed runtime
119
139
  flair bootstrap --agent local --max-tokens 2000
120
140
  ```
121
141
 
122
- This returns a formatted text block that includes the soul entries you just set plus recent/relevant memories. Paste that into any LLM session — Claude Code, Codex, Cursor, an Anthropic API call — to give the agent its identity + memory in one shot.
142
+ ```
143
+ ## Recent Context
144
+ 📝 Harper v5 sandbox blocks node:module but process.dlopen works (2026-07-28)
145
+ ✓ budget 20/2000 tokens (1%) · ✓ 1 included · ✓ 0 truncated
146
+ ```
147
+
148
+ Soul entries and relevant memories, in one block sized to a token budget. Paste it into any LLM session — Claude Code, Codex, Cursor, an API call — to hand the agent its identity and memory in one shot.
123
149
 
124
- If you use Claude Code, add this to your `CLAUDE.md`:
150
+ Using Claude Code? Add this to your `CLAUDE.md`:
125
151
 
126
152
  ```
127
153
  At the start of every session, run mcp__flair__bootstrap before responding.
128
154
  ```
129
155
 
130
- With the MCP server wired up (see the [integration section in README.md](../README.md#integration)), Claude Code runs bootstrap automatically on every new session.
156
+ With the MCP server wired up `flair init` does this automatically for every client it detects — Claude Code runs bootstrap on every new session. See the [integration section in README.md](../README.md#integration).
131
157
 
132
158
  ## What's next
133
159
 
134
160
  | You want to... | Go to |
135
161
  |----------------|-------|
136
162
  | Add more agents to the same instance | `flair agent add <id>` |
137
- | Let one agent read another's memories | `flair grant <from> <to>` ([docs/auth.md](auth.md)) |
138
- | Import memories from agentic-stack / Mem0 / etc. | [docs/bridges.md](bridges.md) |
139
- | Sync memories across machines | [docs/federation.md](federation.md) |
163
+ | Keep a memory owner-only | `flair memory add --visibility private` — reads are otherwise open to every agent on the instance ([auth.md](auth.md)) |
164
+ | Import memories from agentic-stack / Mem0 / etc. | [bridges.md](bridges.md) |
165
+ | Sync memories across machines | [federation.md](federation.md) |
140
166
  | Integrate with OpenClaw, Claude Code, Cursor | [README.md#integration](../README.md#integration) |
141
- | Fix something that isn't working | [docs/troubleshooting.md](troubleshooting.md) |
142
- | Upgrade to a new version | `flair upgrade --restart` or [docs/upgrade.md](upgrade.md) |
167
+ | Fix something that isn't working | `flair doctor`, then [troubleshooting.md](troubleshooting.md) |
168
+ | Upgrade to a new version | `flair upgrade` (restarts automatically) or [upgrade.md](upgrade.md) |
143
169
 
144
170
  ## If you change your mind
145
171
 
146
172
  ```bash
147
- flair stop # stop the service (keeps data)
173
+ flair stop # stop the service, keep data
148
174
  flair restart # restart
149
- flair uninstall # remove the service (keeps data + keys)
150
- flair uninstall --purge # remove everything including data and keys
175
+ flair uninstall # remove the service, keep data and keys
176
+ flair uninstall --purge # remove everything, including data and keys
151
177
  ```
152
178
 
153
179
  All reversible. Your memories aren't locked in.
package/docs/releasing.md CHANGED
@@ -5,9 +5,9 @@ Flair publishes eight workspace packages to npm under `@tpsdev-ai/*`. Releases a
5
5
  (no `NPM_TOKEN` lives anywhere) and submits each package to npm's **staging** area.
6
6
  A maintainer then approves the staged tarballs on npmjs.com with 2FA to make them live.
7
7
 
8
- > `flair-bench` is version-bumped and tagged in lockstep with the other 7, but stages
9
- > in its own step in CI (allowed to fail) until its one-time bootstrap is done — see
10
- > [flair-bench bootstrap](#flair-bench-bootstrap-one-time) below.
8
+ > `flair-bench` is version-bumped and tagged in lockstep with the other 7, and stages in
9
+ > its own step in CI for [historical reasons](#flair-bench-bootstrap-one-time-done). That
10
+ > step is no longer allowed to fail: all eight packages must stage for a release to pass.
11
11
 
12
12
  ```
13
13
  merge release PR ──▶ push tag v0.11.0 ──▶ CI stages all packages ──▶ npm staging
@@ -29,13 +29,39 @@ the maintainer's 2FA approval.
29
29
  ### Phase 1 — open the release PR
30
30
 
31
31
  ```bash
32
- # promote ## Unreleased to the new version in CHANGELOG.md first, then:
33
32
  ./scripts/release.sh 0.11.0
34
33
  ```
35
34
 
36
- This bumps every workspace package to the version, aligns internal deps, refreshes
37
- `bun.lock`, builds, tests, and opens a `release: v0.11.0` PR. Review and merge it
38
- (CI green + K&S approval) the same as any other PR.
35
+ This assembles the changelog, bumps every workspace package to the version, aligns
36
+ internal deps, refreshes `bun.lock`, builds, tests, and opens a `release: v0.11.0` PR.
37
+ Review and merge it (CI green + K&S approval) the same as any other PR.
38
+
39
+ **Nothing about the version is bumped by hand.** The version is declared outside
40
+ `package.json` too — `packages/flair-bench/src/version.ts` holds it as a `TOOL_VERSION`
41
+ constant — and `release.sh` bumps every such site. It checks them *before* creating the
42
+ branch, so an out-of-sync tree aborts while nothing has been touched, and again after the
43
+ bump so a missed site can't be committed. The same check runs in CI
44
+ (`node scripts/check-version-sync.mjs`) and fails on any file outside the known set that
45
+ declares the release version, so a new version-bearing file is caught on the PR that adds
46
+ it. If you ever find yourself editing a version constant by hand during a release, that is
47
+ a bug in the script — add the file to `SOURCE_VERSION_FILES` in
48
+ `scripts/check-version-sync.mjs` and to the `git add` list in `scripts/release.sh`.
49
+
50
+ **The changelog is assembled, not hand-promoted.** Entries land during development as one
51
+ file per change under `.changelog/unreleased/` (flair#835 — a shared `[Unreleased]` block
52
+ conflicted on every concurrent PR, and resolving a conflict dismisses approvals). The
53
+ release script runs:
54
+
55
+ ```bash
56
+ node scripts/changelog-fragments.mjs promote 0.11.0
57
+ ```
58
+
59
+ which writes a `## [0.11.0] - <date>` section from the fragments — Keep a Changelog category
60
+ order, filename order within a category, entry bodies copied verbatim — and deletes the
61
+ fragment files. Released history above it is not touched. It **refuses** to run when the
62
+ fragment directory is empty (nothing to release) or when someone hand-wrote an entry into
63
+ `## [Unreleased]` that the step would otherwise silently overwrite. Preview any time with
64
+ `node scripts/changelog-fragments.mjs render`.
39
65
 
40
66
  ### Phase 2 — tag the release
41
67
 
@@ -51,9 +77,10 @@ workflow, which:
51
77
 
52
78
  1. Resolves the version from the tag and validates it as semver.
53
79
  2. Verifies the tagged commit is an ancestor of `main` (a tag can't ship un-merged code).
54
- 3. Verifies all 7 `package.json` files are at that version.
80
+ 3. Verifies all 8 `package.json` files are at that version.
55
81
  4. Builds every package.
56
- 5. Runs `npm stage publish` for each package in dependency order (flair-client first).
82
+ 5. Runs `npm stage publish` for each package in dependency order (flair-client first),
83
+ then `flair-bench` in its own step. Any one of the eight failing fails the release.
57
84
 
58
85
  It authenticates via OIDC — no secrets, and it does **not** create or move any tag (the
59
86
  tag you pushed is the trigger). Watch the run; when it's green, the packages are staged
@@ -66,7 +93,8 @@ by `scripts/changelog-extract.mjs`). It is idempotent: re-running the workflow o
66
93
  re-pushing the tag updates the existing release rather than failing. The GitHub release
67
94
  documents the tagged commit immediately; it does not wait on the npm 2FA gate. If the
68
95
  CHANGELOG has no section for the version, this job fails loudly rather than cutting an
69
- empty release — so always promote `## [Unreleased]` to `## [X.Y.Z]` in the release PR.
96
+ empty release — which is why phase 1's fragment assembly refuses to produce an empty
97
+ section rather than letting the failure surface here, after the tag is already pushed.
70
98
 
71
99
  > `workflow_dispatch` with a `version` input remains as a manual fallback (needs
72
100
  > `Actions: write`), but the tag push is the normal path.
@@ -82,10 +110,9 @@ npm stage view <stage-id> # inspect one
82
110
  npm stage approve <stage-id> # 2FA prompt; package goes live
83
111
  ```
84
112
 
85
- There are seven lockstep-staged packages, so seven approvals (the web UI lists them on
113
+ There are eight lockstep-staged packages, so eight approvals (the web UI lists them on
86
114
  one page). Approve in dependency order if installing immediately — flair-client before
87
- its dependents — though staging does not itself resolve dependencies. `flair-bench`
88
- stages separately and only appears here once its bootstrap (below) is done.
115
+ its dependents — though staging does not itself resolve dependencies.
89
116
 
90
117
  Verify when done:
91
118
 
@@ -99,7 +126,7 @@ These are configured once and reused for every release.
99
126
 
100
127
  ### npm trusted publisher (per package)
101
128
 
102
- For **each** of the seven packages, on npmjs.com → the package → **Settings → Trusted
129
+ For **each** of the eight packages, on npmjs.com → the package → **Settings → Trusted
103
130
  Publisher → Add**:
104
131
 
105
132
  | Field | Value |
@@ -116,32 +143,39 @@ CI/OIDC identity from publishing anything live directly — the only path to liv
116
143
  human 2FA approval of a staged package.
117
144
 
118
145
  Packages: `flair-client`, `flair-mcp`, `flair`, `openclaw-flair`, `pi-flair`,
119
- `n8n-nodes-flair`, `langgraph-flair`.
146
+ `n8n-nodes-flair`, `langgraph-flair`, `flair-bench`.
120
147
 
121
148
  > A package must already exist on npm before a trusted publisher can be added — all
122
- > seven already do. This account-level config can only be done by an npm org owner.
123
- > `flair-bench` doesn't exist on npm yet (verified via `npm view @tpsdev-ai/flair-bench`
124
- > 404 as of 2026-07-13) so it can't have a Trusted Publisher yet either — see below.
125
-
126
- ### `flair-bench` bootstrap (one-time)
127
-
128
- `flair-bench` (added 2026-07-12, flair#702) is wired into the version-bump/tag flow
129
- (`scripts/release.sh`, `release-publish.yml`'s version-check) alongside the other 7, but
130
- `npm stage publish` categorically requires the package already exist on the npm registry
131
- (`npm help stage`: "Package must exist"), and a Trusted Publisher can only be registered
132
- for a package that already exists chicken-and-egg for a brand-new package. Until an npm
133
- org owner does this once, the workflow's dedicated "Stage-publish flair-bench" step is
134
- expected to fail (it's `continue-on-error: true` so it doesn't block the other 7):
135
-
136
- 1. From a machine logged into npm with 2FA: `cd packages/flair-bench && npm run build &&
137
- npm publish --access public` one normal (non-staged) publish to create the package
138
- on the registry. Any valid semver works; the very next lockstep release will bump it
139
- to match the other 7 automatically (it's in `scripts/release.sh`'s `PACKAGES` array).
140
- 2. Add the Trusted Publisher for `@tpsdev-ai/flair-bench` using the same table as the
141
- other 7 above (org=`tpsdev-ai`, repo=`flair`, workflow=`release-publish.yml`,
142
- environment=`release`, allowed=`npm stage publish` only).
143
- 3. Remove `continue-on-error: true` from the "Stage-publish flair-bench" step in
144
- `release-publish.yml` once a tag push has staged it successfully.
149
+ > eight already do. This account-level config can only be done by an npm org owner.
150
+
151
+ ### `flair-bench` bootstrap (one-time, done)
152
+
153
+ **Nothing to do here.** This section is kept because the next brand-new package added to
154
+ the release set will hit the same wall, and because it explains why `flair-bench` still
155
+ stages in its own workflow step.
156
+
157
+ `flair-bench` (added 2026-07-12, flair#702) was wired into the version-bump/tag flow
158
+ (`scripts/release.sh`, `release-publish.yml`'s version-check) alongside the other 7 before
159
+ it existed on npm at all. That is a chicken-and-egg: `npm stage publish` categorically
160
+ requires the package to already exist on the registry (`npm help stage`: "Package must
161
+ exist"), and a Trusted Publisher can only be registered for a package that already exists.
162
+ So the workflow gave it a dedicated "Stage-publish flair-bench" step marked
163
+ `continue-on-error: true`, letting the expected failure pass without blocking the other 7,
164
+ and an npm org owner broke the cycle once:
165
+
166
+ 1. One normal (non-staged) `npm publish --access public` from a machine logged into npm
167
+ with 2FA, to create the package on the registry. Any valid semver works the next
168
+ lockstep release bumps it to match the other 7 automatically.
169
+ 2. Add its Trusted Publisher using the same table as the other packages above.
170
+
171
+ Both are done, and the step staged `@tpsdev-ai/flair-bench` successfully at v0.30.0, so
172
+ `continue-on-error` has been **removed**: a flair-bench staging failure now fails the
173
+ release like any other package's.
174
+
175
+ > Why it mattered to go back and remove it: `continue-on-error: true` makes a step report
176
+ > `conclusion: success` even when it failed. While it was set, a green run was not evidence
177
+ > that flair-bench had staged — the only way to know was to read the raw log. A justified
178
+ > exception outlives the condition that justified it unless someone returns for it.
145
179
 
146
180
  ### GitHub `release` environment
147
181