@tpsdev-ai/flair 0.21.0 → 0.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -7
- package/SECURITY.md +24 -2
- package/config.yaml +32 -5
- package/dist/cli.js +1755 -213
- package/dist/deploy.js +3 -4
- package/dist/fleet-presence.js +98 -0
- package/dist/fleet-verify.js +291 -0
- package/dist/mcp-client-assertion.js +364 -0
- package/dist/probe.js +97 -0
- package/dist/rem/runner.js +82 -12
- package/dist/resources/AttentionQuery.js +356 -0
- package/dist/resources/Credential.js +11 -1
- package/dist/resources/Federation.js +23 -0
- package/dist/resources/MCPClientMetadata.js +88 -0
- package/dist/resources/Memory.js +52 -53
- package/dist/resources/MemoryBootstrap.js +422 -76
- package/dist/resources/MemoryReflect.js +105 -49
- package/dist/resources/MemoryUsage.js +104 -0
- package/dist/resources/OAuth.js +8 -1
- package/dist/resources/OrgEvent.js +11 -0
- package/dist/resources/Presence.js +218 -19
- package/dist/resources/RecordUsage.js +230 -0
- package/dist/resources/Relationship.js +98 -25
- package/dist/resources/SemanticSearch.js +85 -317
- package/dist/resources/SkillScan.js +15 -0
- package/dist/resources/WorkspaceState.js +11 -0
- package/dist/resources/agent-auth.js +60 -2
- package/dist/resources/auth-middleware.js +18 -9
- package/dist/resources/bm25.js +12 -6
- package/dist/resources/collision-lib.js +157 -0
- package/dist/resources/embeddings-boot.js +149 -0
- package/dist/resources/embeddings-provider.js +364 -106
- package/dist/resources/entity-vocab.js +139 -0
- package/dist/resources/health.js +97 -6
- package/dist/resources/mcp-client-metadata-fields.js +112 -0
- package/dist/resources/mcp-handler.js +1 -1
- package/dist/resources/mcp-tools.js +88 -10
- package/dist/resources/memory-reflect-lib.js +289 -0
- package/dist/resources/migration-boot.js +143 -0
- package/dist/resources/migrations/dir-safety.js +71 -0
- package/dist/resources/migrations/embedding-stamp.js +178 -0
- package/dist/resources/migrations/envelope.js +43 -0
- package/dist/resources/migrations/export.js +38 -0
- package/dist/resources/migrations/ledger.js +55 -0
- package/dist/resources/migrations/lock.js +154 -0
- package/dist/resources/migrations/progress.js +31 -0
- package/dist/resources/migrations/registry.js +36 -0
- package/dist/resources/migrations/risk-policy.js +23 -0
- package/dist/resources/migrations/runner.js +456 -0
- package/dist/resources/migrations/snapshot.js +127 -0
- package/dist/resources/migrations/source-fields.js +93 -0
- package/dist/resources/migrations/space.js +73 -0
- package/dist/resources/migrations/state.js +64 -0
- package/dist/resources/migrations/status.js +39 -0
- package/dist/resources/migrations/synthetic-test-migration.js +93 -0
- package/dist/resources/migrations/types.js +9 -0
- package/dist/resources/presence-internal.js +29 -0
- package/dist/resources/provenance.js +50 -0
- package/dist/resources/rate-limiter.js +8 -1
- package/dist/resources/scoring.js +124 -5
- package/dist/resources/semantic-retrieval-core.js +317 -0
- package/dist/version-handshake.js +122 -0
- package/package.json +4 -5
- package/schemas/event.graphql +5 -0
- package/schemas/memory.graphql +66 -0
- package/schemas/schema.graphql +5 -43
- package/schemas/workspace.graphql +3 -0
- package/dist/resources/IngestEvents.js +0 -162
- package/dist/resources/IssueTokens.js +0 -19
- package/dist/resources/ObsAgentSnapshot.js +0 -13
- package/dist/resources/ObsEventFeed.js +0 -13
- package/dist/resources/ObsOffice.js +0 -19
- package/dist/resources/ObservationCenter.js +0 -23
- package/ui/observation-center.html +0 -385
package/dist/resources/Memory.js
CHANGED
|
@@ -4,9 +4,11 @@ import { isAdmin, resolveAgentAuth, allowVerified } from "./agent-auth.js";
|
|
|
4
4
|
import { localInstanceId } from "./instance-identity.js";
|
|
5
5
|
import { getEmbedding, getModelId } from "./embeddings-provider.js";
|
|
6
6
|
import { scanFields, isStrictMode } from "./content-safety.js";
|
|
7
|
+
import { invalidEntitiesResponse } from "./entity-vocab.js";
|
|
7
8
|
import { checkRateLimit, rateLimitResponse } from "./rate-limiter.js";
|
|
8
9
|
import { resolveReadScope } from "./memory-read-scope.js";
|
|
9
10
|
import { DEDUP_COSINE_THRESHOLD_DEFAULT, DEDUP_LEXICAL_THRESHOLD_DEFAULT, DEDUP_MIN_CONTENT_LENGTH, computeMatchConfidence, cosineSimilarity, isConservativeMatch, } from "./dedup.js";
|
|
11
|
+
import { buildProvenance } from "./provenance.js";
|
|
10
12
|
const FORBIDDEN = (msg) => new Response(JSON.stringify({ error: msg }), { status: 403, headers: { "Content-Type": "application/json" } });
|
|
11
13
|
const UNAUTH = () => new Response(JSON.stringify({ error: "authentication required" }), { status: 401, headers: { "Content-Type": "application/json" } });
|
|
12
14
|
const NOT_FOUND = () => new Response(JSON.stringify({ error: "not found" }), { status: 404, headers: { "Content-Type": "application/json" } });
|
|
@@ -171,10 +173,29 @@ async function runDedupGate(ctx, content) {
|
|
|
171
173
|
if (typeof content.content !== "string" || content.content.length < DEDUP_MIN_CONTENT_LENGTH) {
|
|
172
174
|
return null;
|
|
173
175
|
}
|
|
176
|
+
// flair#504 Phase 2: 'document' — this embedding IS the stored vector (the
|
|
177
|
+
// "generate embedding if missing" step in post()/put() below reuses
|
|
178
|
+
// whatever this computes), so it MUST use the same inputType as every
|
|
179
|
+
// other document-write site or dedup's cosine compare would cross prefixed
|
|
180
|
+
// and unprefixed spaces. All three Memory doc sites (here, post(), put())
|
|
181
|
+
// move together in one commit for exactly that reason — see
|
|
182
|
+
// embeddings-provider.ts's file header for why the VALUE must be the
|
|
183
|
+
// literal 'document', never the prefix string.
|
|
184
|
+
//
|
|
185
|
+
// Dedup-during-transition transient (documented per Kern's review, not a
|
|
186
|
+
// bug to fix here): mid stage-2 re-embed, a NEW write embeds 'document'
|
|
187
|
+
// (prefixed) but may compare against an OLDER stored vector that hasn't
|
|
188
|
+
// been re-embedded yet (unprefixed) — cross-space cosine, so dedup can
|
|
189
|
+
// miss a near-duplicate during that window. Bounded (the re-embed pass is
|
|
190
|
+
// batched and finishes in minutes), self-healing once the pass completes,
|
|
191
|
+
// and a missed dedup is a duplicate row, not data loss — quality, not
|
|
192
|
+
// correctness. Stage 1 (this PR) doesn't trigger this at all: no re-embed
|
|
193
|
+
// runs, so there's no mixed-space window until stage 2's separate,
|
|
194
|
+
// deliberate ops step.
|
|
174
195
|
let embedding = Array.isArray(content.embedding) ? content.embedding : null;
|
|
175
196
|
if (!embedding) {
|
|
176
197
|
try {
|
|
177
|
-
embedding = await getEmbedding(content.content);
|
|
198
|
+
embedding = await getEmbedding(content.content, "document");
|
|
178
199
|
}
|
|
179
200
|
catch {
|
|
180
201
|
embedding = null;
|
|
@@ -314,55 +335,17 @@ function defaultVisibilityForDurability(durability) {
|
|
|
314
335
|
/**
|
|
315
336
|
* ─── Write-time provenance stamp (memory-provenance slice 1) ────────────────
|
|
316
337
|
*
|
|
317
|
-
*
|
|
318
|
-
*
|
|
319
|
-
*
|
|
320
|
-
*
|
|
321
|
-
*
|
|
322
|
-
*
|
|
323
|
-
*
|
|
324
|
-
*
|
|
325
|
-
*
|
|
326
|
-
*
|
|
327
|
-
* (resolveAgentAuth) — never from anything the caller can forge on the
|
|
328
|
-
* request body. `kind: "agent"` → the Ed25519-verified agentId. Any other
|
|
329
|
-
* verdict (in practice only `kind: "internal"` — a trusted in-process call
|
|
330
|
-
* with no per-agent identity to attribute) stamps `null` rather than
|
|
331
|
-
* throwing; `kind: "anonymous"` never reaches here — both post()/put()
|
|
332
|
-
* already 401 it before this point.
|
|
333
|
-
* - `verified.timestamp` reuses the server-clock `createdAt` the caller has
|
|
334
|
-
* already computed by this point (never client-suppliable) — the same
|
|
335
|
-
* "stamp a dynamic attribute the server controls" mechanism as the existing
|
|
336
|
-
* `embeddingModel = getModelId()` stamp elsewhere in this file. NOTE: the
|
|
337
|
-
* *signed* Ed25519 request timestamp is also available (it's verified in
|
|
338
|
-
* auth-middleware) but currently discarded there rather than threaded
|
|
339
|
-
* through to resource methods — a future slice can carry it alongside (or
|
|
340
|
-
* instead of) this server-clock timestamp without changing this shape's
|
|
341
|
-
* `v: 1` contract. Not done here to keep auth-middleware untouched.
|
|
342
|
-
* - `claimed.model` is an OPTIONAL, UNVERIFIED passthrough: included only
|
|
343
|
-
* when the incoming write payload itself already carries a non-empty
|
|
344
|
-
* string `model` field. No client/CLI sets one today — this just means the
|
|
345
|
-
* server won't discard it if/when a future write path does. Never
|
|
346
|
-
* invented, never defaulted, and the `claimed` key is omitted entirely
|
|
347
|
-
* (not stamped as `{}`) when absent.
|
|
348
|
-
*
|
|
349
|
-
* Deliberately NOT implemented in this slice: a context-fingerprint field —
|
|
350
|
-
* bootstrap doesn't return the IDs a fingerprint would need, so it requires
|
|
351
|
-
* client cooperation that's out of scope here.
|
|
338
|
+
* `buildProvenance` itself now lives in ./provenance.ts (imported above) —
|
|
339
|
+
* extracted so resources/Relationship.ts's write path can reuse the EXACT
|
|
340
|
+
* same `{v, verified, claimed?}` shape (the relationship-write-path spec's
|
|
341
|
+
* "reuse buildProvenance as-is" contract) instead of a hand-copied format
|
|
342
|
+
* that could drift. See that module for the full field-by-field rationale
|
|
343
|
+
* (verified.agentId from the auth verdict never the body, verified.timestamp
|
|
344
|
+
* = the server-computed createdAt, optional unverified claimed.model
|
|
345
|
+
* passthrough). Deliberately NOT implemented in this slice: a
|
|
346
|
+
* context-fingerprint field — bootstrap doesn't return the IDs a fingerprint
|
|
347
|
+
* would need, so it requires client cooperation that's out of scope here.
|
|
352
348
|
*/
|
|
353
|
-
function buildProvenance(auth, createdAt, content) {
|
|
354
|
-
const provenance = {
|
|
355
|
-
v: 1,
|
|
356
|
-
verified: {
|
|
357
|
-
agentId: auth.kind === "agent" ? auth.agentId : null,
|
|
358
|
-
timestamp: createdAt,
|
|
359
|
-
},
|
|
360
|
-
};
|
|
361
|
-
if (typeof content?.model === "string" && content.model.length > 0) {
|
|
362
|
-
provenance.claimed = { model: content.model };
|
|
363
|
-
}
|
|
364
|
-
return JSON.stringify(provenance);
|
|
365
|
-
}
|
|
366
349
|
/**
|
|
367
350
|
* ─── Write-time originatorInstanceId stamp (federation-edge-hardening slice 1) ──
|
|
368
351
|
*
|
|
@@ -567,6 +550,13 @@ export class Memory extends databases.flair.Memory {
|
|
|
567
550
|
if (!content.validFrom) {
|
|
568
551
|
content.validFrom = content.createdAt;
|
|
569
552
|
}
|
|
553
|
+
// attention-plane vocabulary gate (flair#675): `entities`, if present,
|
|
554
|
+
// must be well-formed vocabulary strings — see resources/entity-vocab.ts.
|
|
555
|
+
// Field is additive/optional (v1 schema-only; no auto-derivation here —
|
|
556
|
+
// that producer is a follow-up); absent entities is not an error.
|
|
557
|
+
const entitiesError = invalidEntitiesResponse(content.entities);
|
|
558
|
+
if (entitiesError)
|
|
559
|
+
return entitiesError;
|
|
570
560
|
if (content.durability === "ephemeral" && !content.expiresAt) {
|
|
571
561
|
const ttlHours = Number(process.env.FLAIR_EPHEMERAL_TTL_HOURS || 24);
|
|
572
562
|
content.expiresAt = new Date(Date.now() + ttlHours * 3600_000).toISOString();
|
|
@@ -601,9 +591,11 @@ export class Memory extends databases.flair.Memory {
|
|
|
601
591
|
delete content.lexicalThreshold;
|
|
602
592
|
}
|
|
603
593
|
// Generate embedding from content text (no-op if the dedup gate above
|
|
604
|
-
// already computed one for this content).
|
|
594
|
+
// already computed one for this content). flair#504 Phase 2: 'document'
|
|
595
|
+
// — see runDedupGate's comment above for why all three Memory doc sites
|
|
596
|
+
// must move together.
|
|
605
597
|
if (content.content && !content.embedding) {
|
|
606
|
-
const vec = await getEmbedding(content.content);
|
|
598
|
+
const vec = await getEmbedding(content.content, "document");
|
|
607
599
|
if (vec) {
|
|
608
600
|
content.embedding = vec;
|
|
609
601
|
content.embeddingModel = getModelId();
|
|
@@ -699,6 +691,10 @@ export class Memory extends databases.flair.Memory {
|
|
|
699
691
|
if (content.supersedes && !content.validFrom) {
|
|
700
692
|
content.validFrom = content.createdAt;
|
|
701
693
|
}
|
|
694
|
+
// attention-plane vocabulary gate (flair#675) — see post()'s comment above.
|
|
695
|
+
const entitiesError = invalidEntitiesResponse(content.entities);
|
|
696
|
+
if (entitiesError)
|
|
697
|
+
return entitiesError;
|
|
702
698
|
// Content safety scan on updated content + summary.
|
|
703
699
|
if (content.content || content.summary) {
|
|
704
700
|
const safety = scanFields(content, ["content", "summary"]);
|
|
@@ -745,9 +741,12 @@ export class Memory extends databases.flair.Memory {
|
|
|
745
741
|
dedupMatch = await runDedupGate(ctx, content);
|
|
746
742
|
}
|
|
747
743
|
// Re-generate embedding if content changed (no-op if the dedup gate above
|
|
748
|
-
// already computed one for this content).
|
|
744
|
+
// already computed one for this content). flair#504 Phase 2: 'document'
|
|
745
|
+
// — this is also the regen branch `flair reembed` triggers (clears
|
|
746
|
+
// embedding/embeddingModel then hits this put()), so it's what actually
|
|
747
|
+
// re-embeds a stale row WITH the prefix once stage 2 runs.
|
|
749
748
|
if (content.content && !content.embedding) {
|
|
750
|
-
const vec = await getEmbedding(content.content);
|
|
749
|
+
const vec = await getEmbedding(content.content, "document");
|
|
751
750
|
if (vec) {
|
|
752
751
|
content.embedding = vec;
|
|
753
752
|
content.embeddingModel = getModelId();
|