@tpsdev-ai/flair 0.53.0 → 0.54.1

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 (97) hide show
  1. package/README.md +4 -1
  2. package/dist/build-info.json +3 -3
  3. package/dist/cli.js +1791 -15648
  4. package/dist/commands/agent.js +453 -0
  5. package/dist/commands/attention.js +121 -0
  6. package/dist/commands/backup.js +115 -0
  7. package/dist/commands/bootstrap.js +91 -0
  8. package/dist/commands/bridge.js +608 -0
  9. package/dist/commands/deploy.js +180 -0
  10. package/dist/commands/doctor.js +1654 -0
  11. package/dist/commands/export.js +110 -0
  12. package/dist/commands/federation.js +1575 -0
  13. package/dist/commands/fleet.js +73 -0
  14. package/dist/commands/grant.js +109 -0
  15. package/dist/commands/hook.js +193 -0
  16. package/dist/commands/idp.js +193 -0
  17. package/dist/commands/import.js +134 -0
  18. package/dist/commands/init.js +1203 -0
  19. package/dist/commands/inspect.js +45 -0
  20. package/dist/commands/keys.js +187 -0
  21. package/dist/commands/mcp.js +707 -0
  22. package/dist/commands/memory.js +501 -0
  23. package/dist/commands/migrate-harness-memory.js +270 -0
  24. package/dist/commands/orgevent.js +138 -0
  25. package/dist/commands/presence.js +76 -0
  26. package/dist/commands/principal.js +338 -0
  27. package/dist/commands/quality.js +1164 -0
  28. package/dist/commands/reembed.js +296 -0
  29. package/dist/commands/relationship.js +76 -0
  30. package/dist/commands/rem.js +1048 -0
  31. package/dist/commands/restore.js +130 -0
  32. package/dist/commands/search.js +244 -0
  33. package/dist/commands/service.js +315 -0
  34. package/dist/commands/session.js +184 -0
  35. package/dist/commands/soul.js +155 -0
  36. package/dist/commands/status.js +914 -0
  37. package/dist/commands/test.js +93 -0
  38. package/dist/commands/uninstall.js +143 -0
  39. package/dist/commands/upgrade.js +1592 -0
  40. package/dist/commands/workspace.js +114 -0
  41. package/dist/deploy.js +24 -0
  42. package/dist/fabric-npm-install.js +87 -0
  43. package/dist/federation-verify.js +498 -0
  44. package/dist/fleet-verify.js +144 -21
  45. package/dist/install/clients.js +167 -0
  46. package/dist/lib/auth-resolve.js +76 -1
  47. package/dist/lib/daemon-liveness.js +131 -2
  48. package/dist/lib/doctor-config-path.js +61 -0
  49. package/dist/lib/doctor-federation-driver.js +189 -0
  50. package/dist/lib/doctor-run.js +40 -0
  51. package/dist/lib/entity-vocab-cli.js +3 -3
  52. package/dist/lib/federation-pair-identity.js +47 -0
  53. package/dist/lib/launchd-repair.js +5 -4
  54. package/dist/lib/ops-api-bind.js +115 -0
  55. package/dist/lib/owned-pins.js +219 -0
  56. package/dist/lib/uninstall-purge.js +218 -0
  57. package/dist/rem/restore.js +8 -10
  58. package/dist/resources/AgentReadPosition.js +74 -0
  59. package/dist/resources/Federation.js +8 -2
  60. package/dist/resources/Memory.js +4 -3
  61. package/dist/resources/MemoryBootstrap.js +41 -25
  62. package/dist/resources/MemoryCandidate.js +5 -6
  63. package/dist/resources/OrgEventCatchup.js +126 -47
  64. package/dist/resources/agent-read-position-lib.js +83 -0
  65. package/dist/resources/agent-read-position.js +120 -0
  66. package/dist/resources/embeddings-boot.js +32 -0
  67. package/dist/resources/federation-peer-liveness.js +73 -0
  68. package/dist/resources/health.js +68 -19
  69. package/dist/resources/mcp-tools.js +43 -279
  70. package/dist/resources/memory-visibility.js +3 -3
  71. package/dist/resources/migration-boot.js +59 -18
  72. package/dist/resources/migrations/embedding-stamp.js +20 -1
  73. package/dist/resources/migrations/recheck.js +43 -0
  74. package/dist/resources/migrations/runner.js +6 -1
  75. package/dist/resources/migrations/stamp-outstanding.js +171 -0
  76. package/dist/resources/migrations/visibility-backfill.js +2 -2
  77. package/dist/resources/org-event-catchup-lib.js +47 -0
  78. package/dist/resources/record-owner-guard.js +1 -0
  79. package/dist/stamp-migration-verify.js +163 -0
  80. package/dist/stamp-outstanding.js +144 -0
  81. package/docs/api-reference.md +4 -2
  82. package/docs/deploying-on-fabric.md +11 -10
  83. package/docs/deployment.md +3 -1
  84. package/docs/federation.md +19 -0
  85. package/docs/hosted-on-fabric.md +3 -3
  86. package/docs/quickstart.md +2 -1
  87. package/docs/releasing.md +15 -7
  88. package/docs/spoke-bringup.md +10 -5
  89. package/docs/standalone-local.md +3 -1
  90. package/docs/upgrade.md +25 -6
  91. package/node_modules/@tpsdev-ai/flair-tool-descriptors/LICENSE +19 -0
  92. package/node_modules/@tpsdev-ai/flair-tool-descriptors/README.md +22 -0
  93. package/node_modules/@tpsdev-ai/flair-tool-descriptors/dist/index.d.ts +70 -0
  94. package/node_modules/@tpsdev-ai/flair-tool-descriptors/dist/index.js +665 -0
  95. package/node_modules/@tpsdev-ai/flair-tool-descriptors/package.json +46 -0
  96. package/package.json +9 -4
  97. package/schemas/agent.graphql +15 -0
@@ -154,7 +154,8 @@ Skill-tagged Memory rows embed from `trigger` (the recall signal), not
154
154
  | GET / POST / PUT | `/WorkspaceState` | Ed25519 | Owner-only. POST stamps `agentId`; PUT rejects a mismatch. |
155
155
  | GET | `/WorkspaceLatest` | Ed25519 | Latest workspace row for the caller. |
156
156
  | GET / POST / PUT | `/OrgEvent` | Ed25519 | Any verified agent reads every event. Writes stamp `authorId`. |
157
- | GET | `/OrgEventCatchup` | Ed25519 | Catch-up feed for the caller. |
157
+ | GET / POST | `/OrgEventCatchup` | Ed25519 | Catch-up feed for the caller. `since` is optional (defaults to the per-agent watermark). GET pages; POST acks `{ position }` (advance-on-ack). |
158
+ | GET / POST | `/AgentReadPosition` | Ed25519 | Owner-only watermark (`stream`, default `org-event`). Foundation for light-comms catch-up. |
158
159
  | POST | `/OrgEventMaintenance` | Ed25519 / admin | Expire / sweep org events. |
159
160
  | POST | `/AttentionQuery` | Ed25519 | Cross-table “what touches entity E”. Entity strings: [docs/entity-vocabulary.md](entity-vocabulary.md). |
160
161
 
@@ -163,7 +164,7 @@ Skill-tagged Memory rows embed from `trigger` (the recall signal), not
163
164
  | Method | Path | Auth | Notes |
164
165
  |--------|------|------|-------|
165
166
  | GET | `/FederationInstance` | Admin Basic | Local instance identity (CLI / admin). Peers do not call this during pair. |
166
- | POST | `/FederationPair` | Pairing token + body-sig | Public at the Harper role gate. Handler validates token, signature, anti-replay. Fabric uses the bootstrap-user triple from `flair federation token`. |
167
+ | POST | `/FederationPair` | Pairing token + body-sig | Public at the Harper role gate. Handler validates token, signature, anti-replay. Response includes `instance {id, publicKey}` when this hub has an Instance row; `instance` is null if it does not (flair#839). The spoke CLI must not store an empty hub key (flair#822). Fabric uses the bootstrap-user triple from `flair federation token`. |
167
168
  | POST | `/FederationSync` | Peer body-sig | Public at the role gate. Merge Memory / Soul / Agent / Relationship (and classifier-ready Message). Originator + per-record signature checks. |
168
169
  | GET | `/FederationPeers` | Admin Basic | Known peers. |
169
170
  | GET / write | `/Instance` | Read: Ed25519. Write: admin | Instance row (`flair_…` id, role hub/spoke). |
@@ -355,6 +356,7 @@ ed25519 / idp) and **Integration** (legacy platform connection).
355
356
  | **Asset** | memory.graphql | yes | Blob (`contentType`, `data`) owned by `agentId`, linked by `memoryId` |
356
357
  | **WorkspaceState** | workspace.graphql | yes | Current work (`ref`, `provider`, `phase`, `entities`) |
357
358
  | **OrgEvent** | event.graphql | yes | Org-visible event (`authorId`, `kind`, `summary`, `entities`) |
359
+ | **AgentReadPosition** | agent.graphql | no | Per-agent watermark (`agentId`, `stream`, `position`). HTTP via `/AgentReadPosition`, not raw-table REST. |
358
360
  | **Message** | message.graphql | yes | Signed envelope (`from`, `to`, `threadId`, `seq`, `state`, `signature`) |
359
361
  | **OAuthClient** | oauth.graphql | yes | Registered OAuth clients |
360
362
  | **OAuthAuthCode** | oauth.graphql | no | Single-use codes + PKCE |
@@ -241,7 +241,9 @@ Provision once, from one place, before serving multi-region traffic: step 2's
241
241
  | `flair fleet verify --target <url>` | health, auth, version across origin + Flair peers |
242
242
  | `flair federation status\|verify\|reachability --target <url>` | peer table, sync recency, probes |
243
243
 
244
- `fleet verify` exit codes: 1 origin failed, 2 peer version skew, 3 peer unverifiable.
244
+ `fleet verify` exit codes: 1 origin failed, 2 reachable peer diverged (wrong version), 3 reachable peer unreachable/auth-failed. Unverifiable peers (no endpoint on file) warn and exit 0.
245
+
246
+ `federation verify` is a different question (did this canary land). Exit 0 includes unverifiable peers (401/403, unreachable); exit 1 is a reachable peer missing the canary. It pushes before probing so a spoke with no sync daemon can still check.
245
247
 
246
248
  > **A credential mismatch renders as an empty section, not an error.** `flair status`
247
249
  > reads `/HealthDetail` with `FLAIR_ADMIN_PASS` / `HDB_ADMIN_PASSWORD` / a pinned agent
@@ -299,17 +301,16 @@ without this re-flip will appear healthy (`/Health` green) while its MCP tools a
299
301
  dark to every connected client. If you rely on MCP, add the re-flip to your upgrade
300
302
  runbook.
301
303
 
302
- ### Known hazard: unbounded npm cache
304
+ ### npm cache on the node
303
305
 
304
- **Open [flair#886](https://github.com/tpsdev-ai/flair/issues/886).** Every deploy runs a
305
- server-side `npm install` using the node's default cache. npm never evicts it, so it
306
- grows until it fills the quota.
306
+ `flair deploy` and `flair upgrade --target` pass Harper an `install_command` that runs
307
+ `npm install --cache <tmp>` and deletes that directory when the install finishes. The
308
+ node's `~/.npm/_cacache` is no longer the install cache, so repeated deploys do not
309
+ accumulate tarballs ([flair#886](https://github.com/tpsdev-ai/flair/issues/886)).
307
310
 
308
- No in-product mitigation: no cache flag, no alternate location, no cleanup. One install
309
- per deploy bounds the *rate*, not the total. Harper consults `install_command` only when
310
- `node_modules` is absent and `deploy_component` has no force-reinstall option, so the
311
- obvious fix isn't available — and clearing the cache needs node access this shape
312
- doesn't give you.
311
+ Harper still consults `install_command` only when `node_modules` is absent that is
312
+ exactly when a fresh extract runs the install, which is every deploy (Harper replaces
313
+ the component directory). There is nothing for an operator to clean.
313
314
 
314
315
  ### Backup and rollback
315
316
 
@@ -194,6 +194,7 @@ These are read by the Harper process at boot (same places as the table above: la
194
194
  | Variable | Default | What it does |
195
195
  |----------|---------|--------------|
196
196
  | `FLAIR_EMBED_THREADS` | `max(1, availableParallelism() − 1)` | CPU threads for in-process embedding (harper-fabric-embeddings / llama.cpp). Host-aware so a 4-core box does not inherit HFE's fixed 6, and an 8-vCPU ingest host is not stuck at 6 idle cores. One core is left for Harper's event loop and the OS. `availableParallelism()` respects a container CPU quota. Set a positive integer to pin. Invalid values fall back to the default. |
197
+ | `FLAIR_EMBED_GPU_LAYERS` | unset (HFE default `0`, CPU only) | Layers to offload to the GPU. **Unset omits the field** so harper-fabric-embeddings keeps its own default of 0. This is a pin for measurement (the ingest-throughput bench, flair#1436), not a new product default — #1437 is the detect-and-default decision. `0` = CPU; `99` = full offload (Metal on mac-arm64). Invalid values fall through to omit. |
197
198
  | `FLAIR_HYBRID_RETRIEVAL` | `true` | Hybrid BM25 + vector retrieval. Set `false` / `0` / `off` to revert to the legacy HNSW + keyword-bump path. |
198
199
  | `FLAIR_MODELS_DIR` | `<data-dir>/models` | Directory the embedding GGUF is loaded from (and downloaded into on first boot). Point this at a pre-seeded directory to skip the HuggingFace download; see [troubleshooting.md](troubleshooting.md). |
199
200
 
@@ -216,6 +217,7 @@ Always backup before upgrades.
216
217
  ## Uninstall
217
218
 
218
219
  ```bash
219
- flair uninstall # stops server, removes ~/.flair/, removes launchd/systemd service
220
+ flair uninstall # stop the server and remove the launchd/systemd service; keep data and keys
221
+ flair uninstall --purge # also remove ~/.flair (data, keys, secrets), schedulers, and client wiring
220
222
  npm uninstall -g @tpsdev-ai/flair
221
223
  ```
@@ -120,6 +120,24 @@ knob. The first sync runs immediately on enable.
120
120
  `flair federation watch` is unchanged and still the right tool for an
121
121
  interactive "watch it sync while I debug" session.
122
122
 
123
+ ### Did the canary actually land?
124
+
125
+ `flair federation verify` writes a tagged memory, **pushes it** (so a
126
+ freshly paired spoke with no sync daemon can still pass), then probes each
127
+ peer. It uses the same couldn't-check-≠-failed split as `flair fleet verify`
128
+ (flair#988 / #823):
129
+
130
+ | What the probe saw | Verdict | Exit |
131
+ |---|---|---|
132
+ | Canary found | OK | 0 |
133
+ | HTTP 401/403, unreachable, or no endpoint | UNVERIFIABLE (warning) | 0 |
134
+ | Reachable peer answered 200 without the canary after a successful push | FAIL | 1 |
135
+ | Revoked peer | UNVERIFIABLE (warning; not probed) | 0 |
136
+
137
+ A 401 is "could not authenticate to that peer," not "sync failed." Do not
138
+ treat unverifiable as a pass that hides a reachable peer on the wrong side
139
+ of the canary.
140
+
123
141
  **Credentials.** The scheduler never writes a password into a unit file. It
124
142
  stores the *path* given to `--admin-pass-file` (defaulting to
125
143
  `~/.flair/admin-pass` when that exists) and the CLI reads the file at run time,
@@ -197,6 +215,7 @@ Records with `updatedAt` more than 5 minutes in the future are rejected. This pr
197
215
  | `flair federation sync disable [--remove-shim]` | Remove the scheduled sync driver |
198
216
  | `flair federation sync status` | Show whether the driver is installed and genuinely active |
199
217
  | `flair federation watch [--interval <s>]` | Run sync in a foreground loop for an interactive session (default 30s) |
218
+ | `flair federation verify [--wait <s>] [--admin-pass <pass>]` | Write a canary, push it, and check each peer. Flag/file admin credentials authenticate the admin-gated peer listing. 401/403, unreachable, and revoked are UNVERIFIABLE (warning, exit 0); a reachable peer missing the canary still FAILs (exit 1). |
200
219
  | `flair federation reachability` | Probe local instance + each paired peer (read-only) |
201
220
  | `flair federation token [--ttl <min>]` | Generate a one-time pairing token triple (hub only) |
202
221
 
@@ -155,7 +155,7 @@ flair status --target https://<cluster>.<org>.harperfabric.com
155
155
  flair fleet verify --target https://<cluster>.<org>.harperfabric.com
156
156
  ```
157
157
 
158
- `fleet verify` checks health, auth, and version across the origin node plus every Flair federation peer on file. Exit codes: 0 = all verified, 1 = origin failed, 2 = peer version skew, 3 = peer unverifiable.
158
+ `fleet verify` checks health, auth, and version across the origin node plus every Flair federation peer on file. Exit codes: 0 = all probed nodes verified (unverifiable peers warn, do not fail), 1 = origin failed, 2 = reachable peer diverged, 3 = reachable peer unreachable/auth-failed.
159
159
 
160
160
  > **A credential mismatch renders as an empty section.** `flair status` reads `/HealthDetail` with `FLAIR_ADMIN_PASS` / `HDB_ADMIN_PASSWORD` / a pinned agent key — **not** the `FABRIC_*` credentials. On failure it renders blank.
161
161
 
@@ -241,9 +241,9 @@ Fabric gives you N regional nodes running one component — **not** N Flair inst
241
241
 
242
242
  `flair status` reports usage for two directories: no free space, no total, no quota. An instance can hit its quota with nothing saying so. The one indirect signal is a migration halting for space.
243
243
 
244
- ### Unbounded npm cache
244
+ ### npm cache is ephemeral per deploy
245
245
 
246
- Every deploy runs a server-side `npm install` using the node's default cache. npm never evicts it, so it grows until it fills the quota. There is no cache flag, alternate location, or cleanup option. [flair#886](https://github.com/tpsdev-ai/flair/issues/886).
246
+ `flair deploy` / `flair upgrade --target` run the node's `npm install` against a temporary cache and delete it afterwards, so hub quota no longer grows with every install ([flair#886](https://github.com/tpsdev-ai/flair/issues/886)).
247
247
 
248
248
  ---
249
249
 
@@ -202,7 +202,8 @@ Soul entries and relevant memories, in one block sized to a token budget. Paste
202
202
  flair stop # stop the service, keep data
203
203
  flair restart # restart
204
204
  flair uninstall # remove the service, keep data and keys
205
- flair uninstall --purge # remove everything, including data and keys
205
+ flair uninstall --purge # remove data, keys, secrets, schedulers, and client wiring
206
+ # (the npm package is left; `npm uninstall -g @tpsdev-ai/flair`)
206
207
  ```
207
208
 
208
209
  All reversible. Your memories aren't locked in.
package/docs/releasing.md CHANGED
@@ -1,13 +1,15 @@
1
1
  # Releasing Flair
2
2
 
3
- Flair publishes eight workspace packages to npm under `@tpsdev-ai/*`. Releases are
3
+ Flair publishes nine workspace packages to npm under `@tpsdev-ai/*`. Releases are
4
4
  **tokenless** and **staged**: CI authenticates to npm with a short-lived OIDC token
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
8
  > `flair-bench` is version-bumped and tagged in lockstep with the other 7, and stages in
9
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.
10
+ > step is no longer allowed to fail: every already-published package must stage
11
+ > for a release to pass. `@tpsdev-ai/flair-tool-descriptors` is the one
12
+ > first-publish exception until an npm org owner bootstraps it.
11
13
 
12
14
  ```
13
15
  merge release PR ──▶ push tag v0.11.0 ──▶ CI stages all packages ──▶ npm staging
@@ -145,11 +147,17 @@ Leave `npm publish` **unchecked** under allowed actions. This structurally preve
145
147
  CI/OIDC identity from publishing anything live directly — the only path to live is the
146
148
  human 2FA approval of a staged package.
147
149
 
148
- Packages: `flair-client`, `flair-mcp`, `flair`, `openclaw-flair`, `pi-flair`,
149
- `n8n-nodes-flair`, `langgraph-flair`, `flair-bench`.
150
-
151
- > A package must already exist on npm before a trusted publisher can be added — all
152
- > eight already do. This account-level config can only be done by an npm org owner.
150
+ Packages: `flair-client`, `flair-tool-descriptors`, `flair-mcp`, `flair`,
151
+ `openclaw-flair`, `pi-flair`, `n8n-nodes-flair`, `langgraph-flair`, `flair-bench`.
152
+
153
+ > A package must already exist on npm before a trusted publisher can be added.
154
+ > `@tpsdev-ai/flair-tool-descriptors` (flair#1580) is the next first-publish:
155
+ > stage it with the dedicated continue-on-error step, then an npm org owner
156
+ > publishes once and registers its Trusted Publisher (same playbook as
157
+ > `flair-bench` below). Until that exists on the registry, `@tpsdev-ai/flair`
158
+ > and `@tpsdev-ai/flair-mcp` tarballs bundle it (`bundleDependencies` +
159
+ > `scripts/materialize-bundled-descriptors.mjs`) so `npm install` of those
160
+ > tarballs does not 404.
153
161
 
154
162
  ### `flair-bench` bootstrap (one-time, done)
155
163
 
@@ -138,8 +138,8 @@ The `--admin-pass` is required so the CLI can write the hub as a local `Peer` re
138
138
 
139
139
  1. The spoke POSTs a signed pairing request to the hub's `/FederationPair` endpoint.
140
140
  2. The bootstrap user authenticates at the Harper platform layer (works on standalone and Fabric deployments alike).
141
- 3. The hub validates the one-time token, verifies the Ed25519 signature, and creates a `Peer` record.
142
- 4. The spoke writes a `Peer` record pointing to the hub so sync knows where to push.
141
+ 3. The hub validates the one-time token, verifies the Ed25519 signature, and creates a `Peer` record. The response includes the hub's `instance {id, publicKey}` when the hub has a FederationInstance row.
142
+ 4. The spoke writes a `Peer` record pointing to the hub so sync knows where to push. A missing hub `publicKey` is an error, never an empty string. That does not create the hub's Instance row.
143
143
 
144
144
  ---
145
145
 
@@ -156,13 +156,18 @@ Syncing to hub: <hub-instance-id>...
156
156
  ✅ Synced 0 records (0 skipped) in 45ms
157
157
  ```
158
158
 
159
- Then verify reachability across the federation:
159
+ Then verify the pairing. `federation verify` pushes the canary itself — you
160
+ do not need the systemd timer from §7 first:
160
161
 
161
162
  ```bash
163
+ flair federation verify --admin-pass "$FLAIR_ADMIN_PASS"
162
164
  flair federation reachability
163
165
  ```
164
166
 
165
- Both local and hub peer should report `OK`.
167
+ `verify` writes a tagged memory, syncs it, and checks each peer. HTTP 401/403,
168
+ an unreachable peer, or a revoked leftover row is UNVERIFIABLE (a warning),
169
+ not FAIL. A reachable peer that is missing the canary still fails.
170
+ `reachability` should report `OK` for local and the hub.
166
171
 
167
172
  ---
168
173
 
@@ -290,7 +295,7 @@ flair federation pair https://fabric-node.example.com:19926/<instance> \
290
295
  | Hub: mint token | `flair federation token --admin-pass <pass> > triple.json` |
291
296
  | Spoke: pair | `flair federation pair <hub-url> --token-from ./triple.json --admin-pass <pass>` |
292
297
  | Spoke: sync | `flair federation sync --admin-pass <pass>` |
293
- | Spoke: verify | `flair federation reachability` |
298
+ | Spoke: verify | `flair federation verify --admin-pass <pass>` then `flair federation reachability` |
294
299
  | Watch loop | `flair federation watch --interval 30` |
295
300
 
296
301
  ---
@@ -57,7 +57,8 @@ flair status # check everything is working
57
57
  flair stop # stop the service (keeps data)
58
58
  flair restart # restart the service
59
59
  flair uninstall # remove the service (keeps data + keys)
60
- flair uninstall --purge # remove everything including data and keys
60
+ flair uninstall --purge # remove data, keys, secrets, schedulers, and client wiring
61
+ # (the npm package is left; `npm uninstall -g @tpsdev-ai/flair`)
61
62
  ```
62
63
 
63
64
  On macOS the service is a launchd plist at `~/Library/LaunchAgents/ai.tpsdev.flair.plist`. On Linux it is a systemd unit at `~/.config/systemd/user/flair.service`. Both auto-start on login/boot and restart on crash.
@@ -108,6 +109,7 @@ logging:
108
109
  | `FLAIR_KEY_PASSPHRASE` | Passphrase for AES-256-GCM encryption of federation private-key seeds. | Set explicitly for production federation deployments. |
109
110
  | `FLAIR_URL` | Override the Flair base URL for CLI commands (points to a remote instance). | When connecting from a different machine. |
110
111
  | `FLAIR_EMBED_THREADS` | CPU threads for in-process embedding. Default is `max(1, availableParallelism() − 1)` — host-aware, one core left for Harper. | Pin a positive integer on a dedicated ingest host, or when the default leaves cores idle / oversubscribed. See [deployment.md](deployment.md#performance-related-environment-variables). |
112
+ | `FLAIR_EMBED_GPU_LAYERS` | GPU layers to offload. Unset leaves HFE's default of 0 (CPU only) — this is a pin, not a new default. | Only to measure Metal offload (`99`) or pin CPU (`0`). See [deployment.md](deployment.md#performance-related-environment-variables) and #1437. |
111
113
  | `FLAIR_HYBRID_RETRIEVAL` | Hybrid BM25 + vector retrieval (default on). | Set `false` to revert to HNSW-only. |
112
114
  | `FLAIR_MODELS_DIR` | Directory the embedding GGUF is loaded from. | When the model lives outside `<data-dir>/models`. |
113
115
 
package/docs/upgrade.md CHANGED
@@ -39,7 +39,11 @@ flair doctor
39
39
  `flair upgrade` checks and upgrades the npm-global packages (`@tpsdev-ai/flair`,
40
40
  `@tpsdev-ai/flair-mcp`) and, if present, the `openclaw-flair` plugin (via
41
41
  `openclaw plugins install --force --pin`, not `npm install -g` — it needs OpenClaw's
42
- own plugin loader). Pass `--all` to also see `flair-client` (normally hidden as a
42
+ own plugin loader). It also refreshes every `@tpsdev-ai/flair-mcp@<version>` pin
43
+ it previously wrote — MCP server entries **and** SessionStart hook commands —
44
+ for each already-wired client (`~/.claude.json`, `~/.claude/settings.json`,
45
+ `~/.codex/config.toml`, `~/.codex/hooks.json`, and the other auto-wired clients).
46
+ Pass `--all` to also see `flair-client` (normally hidden as a
43
47
  transitive dependency). **Other integrations upgrade in their own ecosystem, not via
44
48
  `flair upgrade`:** `pi-flair` (pi's plugin manager), `langgraph-flair` / `hermes-flair`
45
49
  (pip / your Python package manager), `n8n-nodes-flair` (n8n's Community Nodes UI).
@@ -264,6 +268,20 @@ flair upgrade --target https://<fabric-node>/<instance-name> \
264
268
  --fabric-user <admin> --fabric-password <pass>
265
269
  ```
266
270
 
271
+ ### Post-deploy embedding-stamp verify
272
+
273
+ As of flair#1073, both `flair deploy` and `flair upgrade --target` poll
274
+ authenticated `/HealthDetail` after the served-API check until the
275
+ `embedding-stamp` migration has converged — every real embedding is in the
276
+ current `+searchprefix` space, and the migration is not halted or still
277
+ running. Route verify only proves the component is serving; it does not
278
+ prove the boot-keyed re-embed actually finished. A Fabric instance that
279
+ stayed split for days after 0.30.0 was the incident this closes.
280
+
281
+ `--no-verify` skips this check together with the served-API check.
282
+ `--verify-timeout` covers both. Token-only auth skips the stamp check
283
+ (it needs Basic admin to read `/HealthDetail`) and says so.
284
+
267
285
  ### Post-deploy fleet verify
268
286
 
269
287
  As of flair#636, both `flair deploy` and `flair upgrade --target` automatically run a
@@ -287,10 +305,10 @@ Exit codes:
287
305
 
288
306
  | Code | Meaning |
289
307
  |------|---------|
290
- | 0 | All nodes verified: healthy, authenticated, and version-matched |
308
+ | 0 | All probed nodes verified. Unverifiable peers (no endpoint on file) are listed as a warning and do not fail |
291
309
  | 1 | Origin failed (unreachable, unauthenticated, or wrong version) |
292
- | 2 | Origin OK, but a reachable peer is running a different version (skew) |
293
- | 3 | Origin OK, no skew among reachable peers, but a peer couldn't be verified at all (unreachable, auth rejected, or no endpoint on file) |
310
+ | 2 | A reachable node diverged (wrong version) NOT converged |
311
+ | 3 | A reachable peer was unreachable or rejected auth (not unverifiable) |
294
312
 
295
313
  **What "peer" means here — read before trusting a green sweep:** this checks
296
314
  *Flair's own* federation peer table, not Harper Fabric's own cluster-replication
@@ -300,8 +318,9 @@ in this cluster and are they in sync") is harper-pro-only and unavailable in the
300
318
  Fabric's own replication topology, on the origin or anywhere else. A Fabric replica
301
319
  that was never separately paired as a Flair federation peer (`flair federation pair`)
302
320
  is invisible to this sweep: `0 peers known` means "0 peers on file," never "0 peers
303
- exist." A peer with no usable endpoint is reported `unverifiable` — never silently
304
- dropped, never shown green. The sweep also needs Basic-auth credentials
321
+ exist." A peer with no usable endpoint is reported `unverifiable` — listed and
322
+ warned, never shown green, and does not fail the run (a reachable peer on the
323
+ wrong version still does). The sweep also needs Basic-auth credentials
305
324
  (`FABRIC_USER`/`FABRIC_PASSWORD` env, or the discouraged inline
306
325
  `--fabric-user`/`--fabric-password`) to authenticate each peer probe; a token-only
307
326
  (`--fabric-token`) deploy skips it with a note instead of a silent no-op.
@@ -0,0 +1,19 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ Copyright 2026 TPS Dev AI
8
+
9
+ Licensed under the Apache License, Version 2.0 (the "License");
10
+ you may not use this file except in compliance with the License.
11
+ You may obtain a copy of the License at
12
+
13
+ http://www.apache.org/licenses/LICENSE-2.0
14
+
15
+ Unless required by applicable law or agreed to in writing, software
16
+ distributed under the License is distributed on an "AS IS" BASIS,
17
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18
+ See the License for the specific language governing permissions and
19
+ limitations under the License.
@@ -0,0 +1,22 @@
1
+ # @tpsdev-ai/flair-tool-descriptors
2
+
3
+ Transport-agnostic MCP tool descriptors for [Flair](https://tps.dev/#flair).
4
+
5
+ This package is **pure data and types**: tool name, description, JSON Schema
6
+ `inputSchema`, output shape, and reviewed surface flags. It imports neither
7
+ Harper nor FlairClient. The Flair server binds each native descriptor to its
8
+ Harper implementation; `@tpsdev-ai/flair-mcp` binds each stdio descriptor to a
9
+ FlairClient HTTP call. Both tool sets are derived from this list, so a new
10
+ descriptor appears on every listed surface with zero hand-wiring (flair#1580).
11
+
12
+ ## Install
13
+
14
+ ```bash
15
+ npm install @tpsdev-ai/flair-tool-descriptors
16
+ ```
17
+
18
+ ## Surfaces
19
+
20
+ `native` and `stdio` default to true. Set either to `false` for a reviewed
21
+ one-sided tool (`attention` is native-only; `relationship_store` is
22
+ stdio-only). The #1578 exemption list is derived from those flags.
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Transport-agnostic MCP tool descriptors (flair#1580).
3
+ *
4
+ * Pure data + types: name, description, inputSchema, output shape, and
5
+ * reviewed surface flags. No Harper, no FlairClient, no Zod, no HTTP.
6
+ *
7
+ * The server TOOLS registry binds each native descriptor to its Harper impl.
8
+ * The flair-mcp stdio adapter binds each stdio descriptor to a FlairClient
9
+ * call. Both tool sets are DERIVED from this list — a new descriptor appears
10
+ * on every surface that lists it, with zero hand-wiring.
11
+ */
12
+ /** JSON Schema object used as MCP tools/list inputSchema. */
13
+ export interface JsonSchemaObject {
14
+ type: "object";
15
+ properties: Record<string, JsonSchemaProperty>;
16
+ required?: string[];
17
+ }
18
+ export interface JsonSchemaProperty {
19
+ type?: string;
20
+ description?: string;
21
+ enum?: string[];
22
+ items?: {
23
+ type?: string;
24
+ };
25
+ default?: unknown;
26
+ }
27
+ /** MCP tool descriptor as returned by tools/list. */
28
+ export interface McpToolDef {
29
+ name: string;
30
+ description: string;
31
+ inputSchema: JsonSchemaObject;
32
+ annotations?: Record<string, unknown>;
33
+ }
34
+ /**
35
+ * One MCP-facing tool. `native` / `stdio` default true — omit both and the
36
+ * tool appears on every surface. Set false for a reviewed one-sided tool
37
+ * (the #1578 exemption list is derived from these flags).
38
+ */
39
+ export interface ToolDescriptor {
40
+ name: string;
41
+ description: string;
42
+ inputSchema: JsonSchemaObject;
43
+ /** One-line output shape (MCP metadata). Native conformance contracts pin this as `summary`. */
44
+ outputShape: string;
45
+ annotations?: Record<string, unknown>;
46
+ /** When false, native /mcp does not bind this tool. Default true. */
47
+ native?: boolean;
48
+ /** When false, the stdio adapter does not bind this tool. Default true. */
49
+ stdio?: boolean;
50
+ /** Stdio-only description when the HTTP path differs from native /mcp policy. */
51
+ stdioDescription?: string;
52
+ /** Properties advertised on native /mcp only (reviewed, e.g. flair#1579). */
53
+ stdioOmitProperties?: readonly string[];
54
+ /** Properties advertised on the stdio adapter only (reviewed). */
55
+ stdioExtraProperties?: Record<string, JsonSchemaProperty>;
56
+ }
57
+ export declare function isNativeTool(d: ToolDescriptor): boolean;
58
+ export declare function isStdioTool(d: ToolDescriptor): boolean;
59
+ export declare function toMcpToolDef(d: ToolDescriptor): McpToolDef;
60
+ /** Native tools/list def, minus reviewed stdio-only omissions. */
61
+ export declare function toStdioMcpToolDef(d: ToolDescriptor): McpToolDef;
62
+ export declare function descriptorNames(descriptors: readonly ToolDescriptor[]): string[];
63
+ export declare const TOOL_DESCRIPTORS: readonly ToolDescriptor[];
64
+ export declare const NATIVE_TOOL_DESCRIPTORS: readonly ToolDescriptor[];
65
+ export declare const STDIO_TOOL_DESCRIPTORS: readonly ToolDescriptor[];
66
+ /** Derived #1578 exemption list — one-sided by construction, not hand-synced. */
67
+ export declare const SURFACE_EXEMPTIONS: {
68
+ readonly registryOnly: string[];
69
+ readonly adapterOnly: string[];
70
+ };