@tpsdev-ai/flair 0.29.0 → 0.30.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/cli-shim.cjs +1 -1
- package/dist/cli.js +231 -104
- package/dist/deploy.js +29 -19
- package/dist/fabric-upgrade.js +61 -23
- package/dist/fleet-verify.js +3 -3
- package/dist/lib/mcp-enable.js +2 -2
- package/dist/rem/scheduler.js +226 -17
- package/dist/resources/A2AAdapter.js +1 -1
- package/dist/resources/Admin.js +1 -1
- package/dist/resources/AdminConnectors.js +1 -1
- package/dist/resources/AdminDashboard.js +1 -1
- package/dist/resources/AdminIdp.js +1 -1
- package/dist/resources/AdminInstance.js +1 -1
- package/dist/resources/AdminMemory.js +1 -1
- package/dist/resources/AdminPrincipals.js +1 -1
- package/dist/resources/Agent.js +1 -1
- package/dist/resources/AgentCard.js +1 -1
- package/dist/resources/AgentSeed.js +1 -1
- package/dist/resources/AttentionQuery.js +1 -1
- package/dist/resources/Credential.js +1 -1
- package/dist/resources/Federation.js +1 -1
- package/dist/resources/Instance.js +1 -1
- package/dist/resources/Integration.js +1 -1
- package/dist/resources/MCPClientMetadata.js +1 -1
- package/dist/resources/Memory.js +1 -1
- package/dist/resources/MemoryBootstrap.js +1 -1
- package/dist/resources/MemoryCandidate.js +168 -0
- package/dist/resources/MemoryConsolidate.js +1 -1
- package/dist/resources/MemoryDedupStats.js +1 -1
- package/dist/resources/MemoryFeed.js +1 -1
- package/dist/resources/MemoryGrant.js +1 -1
- package/dist/resources/MemoryMaintenance.js +1 -1
- package/dist/resources/MemoryReflect.js +2 -2
- package/dist/resources/MemoryReindex.js +1 -1
- package/dist/resources/MemoryUsage.js +1 -1
- package/dist/resources/OAuth.js +1 -1
- package/dist/resources/OAuthClient.js +1 -1
- package/dist/resources/OrgEvent.js +1 -1
- package/dist/resources/OrgEventCatchup.js +1 -1
- package/dist/resources/OrgEventMaintenance.js +1 -1
- package/dist/resources/PairingToken.js +1 -1
- package/dist/resources/Peer.js +1 -1
- package/dist/resources/Presence.js +3 -3
- package/dist/resources/RecordUsage.js +1 -1
- package/dist/resources/Relationship.js +1 -1
- package/dist/resources/SemanticSearch.js +1 -1
- package/dist/resources/SkillScan.js +1 -1
- package/dist/resources/Soul.js +1 -1
- package/dist/resources/SoulFeed.js +1 -1
- package/dist/resources/WorkspaceLatest.js +1 -1
- package/dist/resources/WorkspaceState.js +1 -1
- package/dist/resources/XAA.js +1 -1
- package/dist/resources/a2a-url.js +1 -1
- package/dist/resources/agent-auth.js +1 -1
- package/dist/resources/agentcard-fields.js +1 -1
- package/dist/resources/auth-middleware.js +1 -1
- package/dist/resources/embeddings-boot.js +4 -4
- package/dist/resources/embeddings-provider.js +5 -5
- package/dist/resources/federation-cleanup.js +5 -5
- package/dist/resources/federation-nonce-store.js +5 -5
- package/dist/resources/health.js +29 -6
- package/dist/resources/instance-identity.js +1 -1
- package/dist/resources/mcp-client-metadata-fields.js +1 -1
- package/dist/resources/mcp-handler.js +1 -1
- package/dist/resources/mcp-oauth.js +2 -2
- package/dist/resources/memory-read-scope.js +1 -1
- package/dist/resources/memory-reflect-lib.js +3 -3
- package/dist/resources/memory-visibility.js +3 -3
- package/dist/resources/migration-boot.js +1 -1
- package/dist/resources/migrations/embedding-stamp.js +3 -3
- package/dist/resources/migrations/graph-heal.js +2 -2
- package/dist/resources/migrations/ledger.js +1 -1
- package/dist/resources/migrations/registry.js +14 -5
- package/dist/resources/migrations/runner.js +64 -11
- package/dist/resources/migrations/synthetic-test-migration.js +1 -1
- package/dist/resources/migrations/visibility-backfill.js +232 -0
- package/dist/resources/record-types.js +28 -0
- package/dist/resources/semantic-retrieval-core.js +1 -1
- package/dist/resources/usage-recording.js +1 -1
- package/dist/src/rem/scheduler.js +449 -0
- package/docs/deployment.md +1 -0
- package/docs/supply-chain-policy.md +4 -4
- package/docs/upgrade.md +2 -2
- package/package.json +4 -4
- package/schemas/memory.graphql +9 -1
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { databases } from "harper";
|
|
2
|
+
import { resolveAgentAuth } from "./agent-auth.js";
|
|
3
|
+
import { makeAuthGate, makeReadScope, makeByIdReadGate, resolveAuthGate, stampAttribution, FORBIDDEN, UNAUTH, } from "./record-type-kit.js";
|
|
4
|
+
import { RECORD_TYPES } from "./record-types.js";
|
|
5
|
+
// Parameterized from RECORD_TYPES.MemoryCandidate (record-types slice 2,
|
|
6
|
+
// flair#520) rather than a hand-typed "owner-only" literal — the registry is
|
|
7
|
+
// the single source of truth this class draws its read-scope mode from.
|
|
8
|
+
// Exported solely so test/unit/record-types-registry.test.ts's drift
|
|
9
|
+
// tripwire can introspect the composed resolver's tagged `.mode`/
|
|
10
|
+
// `.ownerField` against RECORD_TYPES.MemoryCandidate — not for any other
|
|
11
|
+
// runtime consumer.
|
|
12
|
+
export const memoryCandidateReadScope = makeReadScope(RECORD_TYPES.MemoryCandidate.readScope, RECORD_TYPES.MemoryCandidate.ownerField);
|
|
13
|
+
const memoryCandidateByIdReadGate = makeByIdReadGate(memoryCandidateReadScope);
|
|
14
|
+
// See makeAuthGate's doc (record-type-kit.ts): must be wired as a genuine
|
|
15
|
+
// prototype method below, never a class-field assignment.
|
|
16
|
+
const memoryCandidateAuthGate = makeAuthGate();
|
|
17
|
+
/**
|
|
18
|
+
* MemoryCandidate resource — closes flair#849.
|
|
19
|
+
*
|
|
20
|
+
* The schema (schemas/memory.graphql) declared `@table` for this type with
|
|
21
|
+
* no `@export` and no resource file at all, so it had NO REST surface —
|
|
22
|
+
* `flair rem candidates`/`promote`/`reject` (src/cli.ts) all 404'd on every
|
|
23
|
+
* call, a clean-room dogfood blocker. Adding bare `@export` would have
|
|
24
|
+
* reopened the exact P0 leak the memory-soul-read-gate family fix closed for
|
|
25
|
+
* every other agent-facing table: Harper's `Resource` default is
|
|
26
|
+
* `allowRead/allowCreate/allowUpdate/allowDelete(user) { return
|
|
27
|
+
* user?.role.permission.super_user }` (test/unit/resource-allow-decision.test.ts
|
|
28
|
+
* enforces every exported resource makes an explicit allow-decision instead
|
|
29
|
+
* of falling through to that default), so an unguarded `@export` is reachable
|
|
30
|
+
* by `authorizeLocal`'s forged loopback super_user and, once the global gate
|
|
31
|
+
* stops rejecting a genuinely anonymous remote caller, by that caller too.
|
|
32
|
+
*
|
|
33
|
+
* Read: identity-gated (allowRead — verified agents, admins, and trusted
|
|
34
|
+
* internal calls pass; anonymous HTTP denied) AND per-agent scoped
|
|
35
|
+
* ("owner-only" — see RECORD_TYPES.MemoryCandidate's doc comment for why: a
|
|
36
|
+
* candidate is an unreviewed draft distillation, not yet promoted, and must
|
|
37
|
+
* not be org-readable). Same "owner-only" composition Relationship.ts uses,
|
|
38
|
+
* parameterized from the registry — this is NOT MemoryGrant.ts's bespoke
|
|
39
|
+
* owner-OR-grantee model (MemoryCandidate has no second-party field).
|
|
40
|
+
*
|
|
41
|
+
* Write: post()/put()/delete() all self-enforce inline (no allowCreate/
|
|
42
|
+
* allowUpdate/allowDelete wrapper — same deliberate omission Memory.ts's
|
|
43
|
+
* allowRead() doc explains: adding an unverified allow* gate on top of
|
|
44
|
+
* self-enforcing methods risks regressing owner writes). Non-admin agents
|
|
45
|
+
* may create/modify only their OWN candidates (`agentId` no-forge
|
|
46
|
+
* attribution, "validate-truthy" — present+mismatched agentId is rejected,
|
|
47
|
+
* absent passes through, matching Memory.post()/put()'s exact idiom for the
|
|
48
|
+
* same single-owner-field shape) and may delete only their own candidates
|
|
49
|
+
* (mirrors MemoryGrant.ts's delete() ownership check). Internal calls (the
|
|
50
|
+
* FLAIR-NIGHTLY-REM staging writer — resources/MemoryReflect.ts's
|
|
51
|
+
* `databases.flair.MemoryCandidate.put(row)` — and resources/health.ts's
|
|
52
|
+
* pending-count read) and admin agents pass every method unfiltered, exactly
|
|
53
|
+
* like every other table this pattern covers.
|
|
54
|
+
*/
|
|
55
|
+
export class MemoryCandidate extends databases.flair.MemoryCandidate {
|
|
56
|
+
/**
|
|
57
|
+
* Self-authorize now that the global gate is non-rejecting (memory-soul-
|
|
58
|
+
* read-gate family fix — same pattern as Memory.ts/Relationship.ts/
|
|
59
|
+
* WorkspaceState.ts/OrgEvent.ts/Soul.ts/MemoryGrant.ts). Harper routes
|
|
60
|
+
* `GET /MemoryCandidate/<id>` to get() and the collection describe
|
|
61
|
+
* (`GET /MemoryCandidate`) outside search(), so both must be gated here,
|
|
62
|
+
* not just search(). Per-record owner scoping happens in get() below; the
|
|
63
|
+
* collection scope is still in search().
|
|
64
|
+
*/
|
|
65
|
+
allowRead() { return memoryCandidateAuthGate.call(this); }
|
|
66
|
+
/**
|
|
67
|
+
* Override get() to scope by-id reads the same way search() scopes
|
|
68
|
+
* collection reads. Never distinguishes "doesn't exist" from "exists but
|
|
69
|
+
* not yours" — both return 404, never 403, so a denied caller can't use
|
|
70
|
+
* get() to enumerate other agents' candidate ids (same discipline
|
|
71
|
+
* makeByIdReadGate documents, and MemoryGrant.ts's get() hand-rolls
|
|
72
|
+
* identically). `flair rem promote`/`flair rem reject` (src/cli.ts) both
|
|
73
|
+
* fetch a candidate by id before writing back — this is their read path.
|
|
74
|
+
*/
|
|
75
|
+
async get(target) {
|
|
76
|
+
// Collection / query reads arrive as a RequestTarget with
|
|
77
|
+
// `isCollection === true`, and are governed by search() (same owner
|
|
78
|
+
// scoping). Only a genuine by-id get is ownership-checked below.
|
|
79
|
+
if (!target || (typeof target === "object" && target.isCollection)) {
|
|
80
|
+
return this.search(target);
|
|
81
|
+
}
|
|
82
|
+
return memoryCandidateByIdReadGate.call(this, target, (t) => super.get(t));
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Scope collection reads to the caller's own candidates. This is the
|
|
86
|
+
* handler `flair rem candidates` reaches via `POST
|
|
87
|
+
* /MemoryCandidate/search_by_conditions` (src/cli.ts) — the agentId
|
|
88
|
+
* condition is wrapped as the OUTERMOST `and` block (Relationship.ts's
|
|
89
|
+
* same "Security Critical" discipline) so a caller-supplied `operator:
|
|
90
|
+
* "or"` in the request body cannot boolean-inject past the owner scope.
|
|
91
|
+
*/
|
|
92
|
+
async search(query) {
|
|
93
|
+
const ctx = this.getContext?.();
|
|
94
|
+
const gate = await resolveAuthGate(ctx, UNAUTH());
|
|
95
|
+
if (gate.kind === "denied")
|
|
96
|
+
return gate.response;
|
|
97
|
+
if (gate.kind === "unfiltered")
|
|
98
|
+
return super.search(query);
|
|
99
|
+
// Non-admin agent: scope to own candidates only.
|
|
100
|
+
const scope = await memoryCandidateReadScope(gate.agentId);
|
|
101
|
+
const agentCondition = scope.condition;
|
|
102
|
+
if (!query?.conditions) {
|
|
103
|
+
return super.search({ conditions: [agentCondition], ...(query || {}) });
|
|
104
|
+
}
|
|
105
|
+
return super.search({
|
|
106
|
+
...query,
|
|
107
|
+
conditions: [agentCondition, { conditions: query.conditions, operator: query.operator || "and" }],
|
|
108
|
+
operator: "and",
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* No shipped CLI path calls POST today (the FLAIR-NIGHTLY-REM staging
|
|
113
|
+
* writer uses an internal `.put()`, not `.post()` — see MemoryReflect.ts).
|
|
114
|
+
* Guarded anyway, on the same "don't leave any exported verb unguarded"
|
|
115
|
+
* discipline the rest of this file follows: anonymous denied; a non-admin
|
|
116
|
+
* agent may only stage a candidate attributed to itself.
|
|
117
|
+
*/
|
|
118
|
+
async post(content) {
|
|
119
|
+
const ctx = this.getContext?.();
|
|
120
|
+
const auth = await resolveAgentAuth(ctx);
|
|
121
|
+
if (auth.kind === "anonymous")
|
|
122
|
+
return UNAUTH();
|
|
123
|
+
const attr = stampAttribution(auth, content, RECORD_TYPES.MemoryCandidate.ownerField, RECORD_TYPES.MemoryCandidate.attribution.post, "forbidden: cannot stage a memory candidate for another agent");
|
|
124
|
+
if (attr.denied)
|
|
125
|
+
return attr.denied;
|
|
126
|
+
return super.post(content);
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* `flair rem promote`/`flair rem reject` (src/cli.ts) both PUT the
|
|
130
|
+
* candidate row back with an updated status/reviewerId/reviewRationale/
|
|
131
|
+
* decidedAt, spreading the existing record (agentId included, unchanged).
|
|
132
|
+
* Non-admin: the no-forge attribution check rejects a PRESENT, mismatched
|
|
133
|
+
* agentId (an agent can't repurpose this endpoint to rewrite another
|
|
134
|
+
* agent's candidate) but never stamps one in — the promote/reject flow
|
|
135
|
+
* always carries the original agentId forward untouched.
|
|
136
|
+
*/
|
|
137
|
+
async put(content) {
|
|
138
|
+
const ctx = this.getContext?.();
|
|
139
|
+
const auth = await resolveAgentAuth(ctx);
|
|
140
|
+
if (auth.kind === "anonymous")
|
|
141
|
+
return UNAUTH();
|
|
142
|
+
const attr = stampAttribution(auth, content, RECORD_TYPES.MemoryCandidate.ownerField, RECORD_TYPES.MemoryCandidate.attribution.put, "forbidden: cannot modify a memory candidate owned by another agent");
|
|
143
|
+
if (attr.denied)
|
|
144
|
+
return attr.denied;
|
|
145
|
+
return super.put(content);
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* No shipped CLI path calls DELETE today. Guarded on the same discipline
|
|
149
|
+
* as post() above — mirrors MemoryGrant.ts's delete(): owner-only,
|
|
150
|
+
* admin/internal unfiltered, a genuinely-missing id is a clean no-op
|
|
151
|
+
* super.delete() rather than a FORBIDDEN (no existence oracle).
|
|
152
|
+
*/
|
|
153
|
+
async delete(id, context) {
|
|
154
|
+
const ctx = this.getContext?.();
|
|
155
|
+
const gate = await resolveAuthGate(ctx, UNAUTH());
|
|
156
|
+
if (gate.kind === "denied")
|
|
157
|
+
return gate.response;
|
|
158
|
+
if (gate.kind === "unfiltered")
|
|
159
|
+
return super.delete(id, context);
|
|
160
|
+
const record = await super.get(id);
|
|
161
|
+
if (!record)
|
|
162
|
+
return super.delete(id, context);
|
|
163
|
+
if (record[RECORD_TYPES.MemoryCandidate.ownerField] !== gate.agentId) {
|
|
164
|
+
return FORBIDDEN("forbidden: cannot delete a memory candidate owned by another agent");
|
|
165
|
+
}
|
|
166
|
+
return super.delete(id, context);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* candidates Array<{ memory, suggestion, reason }>
|
|
16
16
|
* prompt string
|
|
17
17
|
*/
|
|
18
|
-
import { Resource, databases } from "
|
|
18
|
+
import { Resource, databases } from "harper";
|
|
19
19
|
import { isAdmin, allowVerified } from "./agent-auth.js";
|
|
20
20
|
import { evaluate, parseDuration } from "./memory-consolidate-lib.js";
|
|
21
21
|
export class ConsolidateMemories extends Resource {
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
* agent's own memories; this one is never scoped that way — it reads every
|
|
53
53
|
* agent's embeddings — so it follows the admin-gated precedent instead.
|
|
54
54
|
*/
|
|
55
|
-
import { Resource, databases } from "
|
|
55
|
+
import { Resource, databases } from "harper";
|
|
56
56
|
import { mkdirSync, writeFileSync } from "node:fs";
|
|
57
57
|
import { dirname } from "node:path";
|
|
58
58
|
import { allowAdmin } from "./agent-auth.js";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Resource, databases } from "
|
|
1
|
+
import { Resource, databases } from "harper";
|
|
2
2
|
import { allowVerified } from "./agent-auth.js";
|
|
3
3
|
import { computeContentHash, findExistingMemoryByContentHash } from "./memory-feed-lib.js";
|
|
4
4
|
export class FeedMemories extends Resource {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { databases } from "
|
|
1
|
+
import { databases } from "harper";
|
|
2
2
|
import { resolveAgentAuth, allowVerified } from "./agent-auth.js";
|
|
3
3
|
const FORBIDDEN = (msg) => new Response(JSON.stringify({ error: msg }), { status: 403, headers: { "Content-Type": "application/json" } });
|
|
4
4
|
const UNAUTH = () => new Response(JSON.stringify({ error: "authentication required" }), { status: 401, headers: { "Content-Type": "application/json" } });
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* Migrated to the standard `extends Resource` shape with `allowCreate()`
|
|
17
17
|
* to gate auth correctly.
|
|
18
18
|
*/
|
|
19
|
-
import { Resource, databases } from "
|
|
19
|
+
import { Resource, databases } from "harper";
|
|
20
20
|
import { isAdmin } from "./agent-auth.js";
|
|
21
21
|
export class MemoryMaintenance extends Resource {
|
|
22
22
|
/** POST requires auth — either an agent acting on its own memories, or admin. */
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
* Resource }` outright — see test/unit/resource-allow.test.ts). This
|
|
37
37
|
* resource is a thin orchestrator over the lib's tested functions.
|
|
38
38
|
*/
|
|
39
|
-
import { Resource, databases, models, logger } from "
|
|
39
|
+
import { Resource, databases, models, logger } from "harper";
|
|
40
40
|
import { randomBytes } from "node:crypto";
|
|
41
41
|
import { isAdmin, allowVerified } from "./agent-auth.js";
|
|
42
42
|
import { patchRecordSilent } from "./table-helpers.js";
|
|
@@ -143,7 +143,7 @@ export class ReflectMemories extends Resource {
|
|
|
143
143
|
existingPendingClaims.push(c.claim);
|
|
144
144
|
}
|
|
145
145
|
const toStage = dedupeCandidates(outcome.candidates, existingPendingClaims);
|
|
146
|
-
// generatedBy: GenerateResult in the pinned
|
|
146
|
+
// generatedBy: GenerateResult in the pinned harper 5.1.17 has
|
|
147
147
|
// no model/backend-id field (content/finishReason/usage/toolCalls/trace
|
|
148
148
|
// only) — the "from the generate result if available" branch is
|
|
149
149
|
// unreachable in this version, so this always falls back to the
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
*
|
|
24
24
|
* This endpoint is idempotent: running it again finds zero drift.
|
|
25
25
|
*/
|
|
26
|
-
import { Resource, databases } from "
|
|
26
|
+
import { Resource, databases } from "harper";
|
|
27
27
|
import { isAdmin, allowAdmin } from "./agent-auth.js";
|
|
28
28
|
export class MemoryReindex extends Resource {
|
|
29
29
|
// Admin-only: permit verified ADMIN agents (Basic-admin is super_user and
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
* to expose "which agent used which memory" cross-agent, and narrowing this
|
|
42
42
|
* costs nothing.
|
|
43
43
|
*/
|
|
44
|
-
import { databases } from "
|
|
44
|
+
import { databases } from "harper";
|
|
45
45
|
import { resolveAgentAuth, allowVerified } from "./agent-auth.js";
|
|
46
46
|
const FORBIDDEN = (msg) => new Response(JSON.stringify({ error: msg }), { status: 403, headers: { "Content-Type": "application/json" } });
|
|
47
47
|
const UNAUTH = () => new Response(JSON.stringify({ error: "authentication required" }), { status: 401, headers: { "Content-Type": "application/json" } });
|
package/dist/resources/OAuth.js
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* requests slipped through once the gate stopped rejecting. resolveAgentAuth
|
|
11
11
|
* distinguishes internal/agent/anonymous explicitly.
|
|
12
12
|
*/
|
|
13
|
-
import { databases } from "
|
|
13
|
+
import { databases } from "harper";
|
|
14
14
|
import { resolveAgentAuth } from "./agent-auth.js";
|
|
15
15
|
import { invalidEntitiesResponse } from "./entity-vocab.js";
|
|
16
16
|
import { makeAuthGate, resolveAuthGate, stampAttribution, FORBIDDEN, UNAUTH, } from "./record-type-kit.js";
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* Sorted by createdAt ascending (oldest first for in-order processing).
|
|
10
10
|
* Limit 50 events max.
|
|
11
11
|
*/
|
|
12
|
-
import { Resource, databases } from "
|
|
12
|
+
import { Resource, databases } from "harper";
|
|
13
13
|
import { allowVerified, resolveAgentAuth } from "./agent-auth.js";
|
|
14
14
|
export class OrgEventCatchup extends Resource {
|
|
15
15
|
// Self-authorize via the Ed25519 agent verify (auth reshape removes the gate's
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* POST /OrgEventMaintenance/ — deletes all records where expiresAt < now.
|
|
5
5
|
* Auth: admin only.
|
|
6
6
|
*/
|
|
7
|
-
import { Resource, databases } from "
|
|
7
|
+
import { Resource, databases } from "harper";
|
|
8
8
|
import { allowAdmin } from "./agent-auth.js";
|
|
9
9
|
export class OrgEventMaintenance extends Resource {
|
|
10
10
|
// Admin-only: permit verified ADMIN agents (Basic-admin is super_user and
|
package/dist/resources/Peer.js
CHANGED
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
* content. get() additionally gates currentTask itself to verified
|
|
27
27
|
* in-org agents only; see get()'s inline comment.
|
|
28
28
|
*/
|
|
29
|
-
import { databases } from "
|
|
29
|
+
import { databases } from "harper";
|
|
30
30
|
import { existsSync, readFileSync } from "node:fs";
|
|
31
31
|
import { dirname, join } from "node:path";
|
|
32
32
|
import { fileURLToPath } from "node:url";
|
|
@@ -80,7 +80,7 @@ function resolveVersion() {
|
|
|
80
80
|
return process.env.npm_package_version ?? "dev";
|
|
81
81
|
}
|
|
82
82
|
/**
|
|
83
|
-
* Resolve the running
|
|
83
|
+
* Resolve the running harper version. Unlike flair's own
|
|
84
84
|
* package.json (readable via a plain relative path above), Harper's package
|
|
85
85
|
* only exports "." → dist/index.js — there's no "./package.json" subpath, so
|
|
86
86
|
* requiring it directly throws (Node's exports-map enforcement). Resolve the
|
|
@@ -93,7 +93,7 @@ function resolveVersion() {
|
|
|
93
93
|
function resolveHarperVersion() {
|
|
94
94
|
try {
|
|
95
95
|
const req = createRequire(import.meta.url);
|
|
96
|
-
const mainPath = req.resolve("
|
|
96
|
+
const mainPath = req.resolve("harper");
|
|
97
97
|
const pkgPath = join(dirname(mainPath), "..", "package.json");
|
|
98
98
|
if (existsSync(pkgPath)) {
|
|
99
99
|
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
@@ -93,7 +93,7 @@
|
|
|
93
93
|
* sanitized (control-character-stripped, length-capped) and stored as-is,
|
|
94
94
|
* for audit/analytics only. Treat it as untrusted data, always.
|
|
95
95
|
*/
|
|
96
|
-
import { Resource } from "
|
|
96
|
+
import { Resource } from "harper";
|
|
97
97
|
import { resolveAgentAuth } from "./agent-auth.js";
|
|
98
98
|
import { checkRateLimit, rateLimitResponse } from "./rate-limiter.js";
|
|
99
99
|
import { recordUsageContribution, MAX_USAGE_IDS_PER_CALL } from "./usage-recording.js";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { databases } from "
|
|
1
|
+
import { databases } from "harper";
|
|
2
2
|
import { resolveAgentAuth } from "./agent-auth.js";
|
|
3
3
|
import { checkRateLimit, rateLimitResponse } from "./rate-limiter.js";
|
|
4
4
|
import { localInstanceId } from "./instance-identity.js";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Resource, databases } from "
|
|
1
|
+
import { Resource, databases } from "harper";
|
|
2
2
|
import { resolveAgentAuth, allowVerified } from "./agent-auth.js";
|
|
3
3
|
import { getEmbedding, getMode } from "./embeddings-provider.js";
|
|
4
4
|
import { patchRecord } from "./table-helpers.js";
|
package/dist/resources/Soul.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { databases } from "
|
|
1
|
+
import { databases } from "harper";
|
|
2
2
|
import { resolveAgentAuth } from "./agent-auth.js";
|
|
3
3
|
import { localInstanceId } from "./instance-identity.js";
|
|
4
4
|
import { makeAuthGate, stampAttribution, UNAUTH } from "./record-type-kit.js";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Resource, databases } from '
|
|
1
|
+
import { Resource, databases } from 'harper';
|
|
2
2
|
import { allowVerified } from './agent-auth.js';
|
|
3
3
|
export class FeedSouls extends Resource {
|
|
4
4
|
// Self-authorize the subscription via the Ed25519 agent verify (auth reshape
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* GET /WorkspaceLatest/{agentId} — returns most recent WorkspaceState record.
|
|
5
5
|
* Auth: requesting agent must match agentId in path (or be admin).
|
|
6
6
|
*/
|
|
7
|
-
import { Resource, databases } from "
|
|
7
|
+
import { Resource, databases } from "harper";
|
|
8
8
|
import { allowVerified, resolveAgentAuth } from "./agent-auth.js";
|
|
9
9
|
export class WorkspaceLatest extends Resource {
|
|
10
10
|
// Self-authorize via the Ed25519 agent verify (auth reshape removes the gate's
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* Note: Harper's static methods call instance methods with positional args only.
|
|
8
8
|
* Use this.getContext() to access request context (tpsAgent, tpsAgentIsAdmin).
|
|
9
9
|
*/
|
|
10
|
-
import { databases } from "
|
|
10
|
+
import { databases } from "harper";
|
|
11
11
|
import { resolveAgentAuth } from "./agent-auth.js";
|
|
12
12
|
import { invalidEntitiesResponse } from "./entity-vocab.js";
|
|
13
13
|
import { makeAuthGate, makeReadScope, makeByIdReadGate, resolveAuthGate, stampAttribution, FORBIDDEN, UNAUTH, } from "./record-type-kit.js";
|
package/dist/resources/XAA.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
// reflect the REAL listening port, or a remote A2A peer that follows discovery
|
|
7
7
|
// hits a dead port.
|
|
8
8
|
//
|
|
9
|
-
// Kept free of any
|
|
9
|
+
// Kept free of any harper import so the resolution logic is
|
|
10
10
|
// unit-testable without spinning up Harper (mirrors agentcard-fields.ts —
|
|
11
11
|
// avoids the simulator-pattern drift that let the AdminInstance predicate be
|
|
12
12
|
// reproduced-not-imported).
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* verified against the agent's stored Ed25519 public key. Replay is bounded by a
|
|
16
16
|
* 30s timestamp window + a per-(agent,nonce) seen-set pruned to that window.
|
|
17
17
|
*/
|
|
18
|
-
import { databases } from "
|
|
18
|
+
import { databases } from "harper";
|
|
19
19
|
import { WINDOW_MS, isNonceReplay, recordNonce, importEd25519Key, b64ToArrayBuffer, parseTpsEd25519Header } from "./ed25519-auth.js";
|
|
20
20
|
/**
|
|
21
21
|
* Shared Harper user that verified Ed25519 agents resolve to (least-privilege
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
// fallback that could surface a private soul (an operator's internal note,
|
|
8
8
|
// prompt fragment, or credential reminder) on the public endpoint.
|
|
9
9
|
//
|
|
10
|
-
// Kept free of any
|
|
10
|
+
// Kept free of any harper import so the real logic is unit-testable
|
|
11
11
|
// without spinning up Harper (avoids the simulator-pattern that let the
|
|
12
12
|
// description-fallback leak ship untested).
|
|
13
13
|
export function readSoulKind(entry) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { patchRecord } from "./table-helpers.js";
|
|
2
|
-
import { server, databases } from "
|
|
2
|
+
import { server, databases } from "harper";
|
|
3
3
|
import { getEmbedding } from "./embeddings-provider.js";
|
|
4
4
|
import { isAdmin, FLAIR_AGENT_USERNAME } from "./agent-auth.js";
|
|
5
5
|
import { WINDOW_MS, isNonceReplay, recordNonce, importEd25519Key, b64ToArrayBuffer, parseTpsEd25519Header } from "./ed25519-auth.js";
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* The previous mechanism (removed by this change) delivered the registration
|
|
8
8
|
* as a `models.embedding.default` block via the `HARPER_CONFIG` env var
|
|
9
9
|
* (src/cli.ts's old `buildEmbeddingsHarperConfigEnv`) — a "merge layer" that
|
|
10
|
-
* Harper's environment-manager (
|
|
10
|
+
* Harper's environment-manager (`harper`'s
|
|
11
11
|
* `config/harperConfigEnvVars.js`) reasserts on every boot AND PERSISTS into
|
|
12
12
|
* the instance-root `harper-config.yaml`. That file proved to be config-as-
|
|
13
13
|
* STATE, not config-as-intent: `flair#694`'s downgrade-and-revert CI lane
|
|
@@ -28,14 +28,14 @@
|
|
|
28
28
|
* to), leaving `models.embedding.default: {}` — an empty shell —
|
|
29
29
|
* persisted to disk.
|
|
30
30
|
* 3. The next boot's config schema validator
|
|
31
|
-
* (
|
|
31
|
+
* (`harper`'s `validation/configValidator.js`) resolves the
|
|
32
32
|
* entry's backend via `Joi.alternatives().conditional('.backend', {...,
|
|
33
33
|
* otherwise: unknownBackendEntrySchema})`; with `backend` absent, that
|
|
34
34
|
* falls through to `unknownBackendEntrySchema = Joi.object({backend:
|
|
35
35
|
* string.required()})`, which throws exactly: "Harper config file
|
|
36
36
|
* validation error: 'models.embedding.default.backend' is required" —
|
|
37
|
-
* Harper refuses to boot. Confirmed byte-identical in both
|
|
38
|
-
*
|
|
37
|
+
* Harper refuses to boot. Confirmed byte-identical in both harper
|
|
38
|
+
* 5.1.15 and 5.1.17 (this is upstream env-var-config behavior,
|
|
39
39
|
* not a schema difference between versions), and reproduced locally by
|
|
40
40
|
* replaying the exact downgrade-and-revert sequence.
|
|
41
41
|
*
|
|
@@ -36,11 +36,11 @@
|
|
|
36
36
|
* what this migration removes: `models.embed` is a process-wide singleton
|
|
37
37
|
* Harper initializes at boot, off the first-call dynamic-import race.
|
|
38
38
|
*
|
|
39
|
-
*
|
|
39
|
+
* `harper` is dynamic-imported here (deferred to first actual
|
|
40
40
|
* getEmbedding()/getMode()/getStatus() call), NOT statically imported like
|
|
41
41
|
* every other resource's `Resource`/`databases`/`server` — those work
|
|
42
42
|
* unmocked-import-free only because every test that (transitively) imports
|
|
43
|
-
* them already mocks
|
|
43
|
+
* them already mocks `harper` first. Statically importing
|
|
44
44
|
* `models` here would make *any* test that imports this file for
|
|
45
45
|
* `resolveModelsDir()` alone (test/unit/embeddings-models-dir.test.ts, which
|
|
46
46
|
* has no reason to know about Harper at all) also eagerly load Harper's real
|
|
@@ -186,7 +186,7 @@ function prefixesEnabled() {
|
|
|
186
186
|
* Build the options object passed to `models.embed()`. Pulled out as its own
|
|
187
187
|
* pure, harper-free function so the value-forwarding (and the reject-a-
|
|
188
188
|
* wrong-value guard) is unit-testable without touching the deferred
|
|
189
|
-
*
|
|
189
|
+
* `harper` import this file's header explains — see
|
|
190
190
|
* test/unit/embeddings-provider-input-type.test.ts.
|
|
191
191
|
*
|
|
192
192
|
* Gate ON (default): rejects anything other than the literal
|
|
@@ -217,7 +217,7 @@ let _modelsApi;
|
|
|
217
217
|
/**
|
|
218
218
|
* Resolve (and cache) Harper's `models` facade via a deferred import — see file header.
|
|
219
219
|
*
|
|
220
|
-
* NOTE for anyone using this as a reference: `models` (the
|
|
220
|
+
* NOTE for anyone using this as a reference: `models` (the `harper`
|
|
221
221
|
* package export) and a component's `scope.models` are the SAME boot-time
|
|
222
222
|
* singleton — Harper's jsLoader hands components `scope.models = <the global
|
|
223
223
|
* models>` (two accessors, one object; the model registry lives on that singleton).
|
|
@@ -229,7 +229,7 @@ let _modelsApi;
|
|
|
229
229
|
async function getModelsApi() {
|
|
230
230
|
if (_modelsApi)
|
|
231
231
|
return _modelsApi;
|
|
232
|
-
const harper = await import("
|
|
232
|
+
const harper = await import("harper");
|
|
233
233
|
_modelsApi = harper.models;
|
|
234
234
|
return _modelsApi;
|
|
235
235
|
}
|
|
@@ -11,13 +11,13 @@ let cleanupTimer = null;
|
|
|
11
11
|
* and performs housekeeping on expired/unconsumed token records.
|
|
12
12
|
*
|
|
13
13
|
* In test environments, callers pass mock serverOp/db via `opts` so this
|
|
14
|
-
* module never imports
|
|
14
|
+
* module never imports harper at the top level (which would
|
|
15
15
|
* crash when STORAGE_PATH isn't set).
|
|
16
16
|
*/
|
|
17
17
|
export async function initFederationCleanup(opts) {
|
|
18
18
|
const immediate = opts?.immediateTick ?? true;
|
|
19
19
|
// Resolve server / databases: use caller-supplied mocks when available,
|
|
20
|
-
// otherwise lazy-import
|
|
20
|
+
// otherwise lazy-import harper at call time.
|
|
21
21
|
let svr;
|
|
22
22
|
let db;
|
|
23
23
|
if (opts?.serverOp && opts?.db) {
|
|
@@ -26,12 +26,12 @@ export async function initFederationCleanup(opts) {
|
|
|
26
26
|
}
|
|
27
27
|
else {
|
|
28
28
|
try {
|
|
29
|
-
const harper = await import("
|
|
29
|
+
const harper = await import("harper");
|
|
30
30
|
svr = opts?.serverOp ?? harper.server.operation;
|
|
31
31
|
db = opts?.db ?? harper.databases;
|
|
32
32
|
}
|
|
33
33
|
catch (err) {
|
|
34
|
-
console.error("[federation-cleanup] failed to load
|
|
34
|
+
console.error("[federation-cleanup] failed to load harper:", err?.message ?? err);
|
|
35
35
|
return;
|
|
36
36
|
}
|
|
37
37
|
}
|
|
@@ -94,7 +94,7 @@ export async function runCleanupTick(opts = {}) {
|
|
|
94
94
|
db = opts.db;
|
|
95
95
|
}
|
|
96
96
|
else {
|
|
97
|
-
const harper = await import("
|
|
97
|
+
const harper = await import("harper");
|
|
98
98
|
svr = opts.serverOp ?? harper.server.operation;
|
|
99
99
|
db = opts.db ?? harper.databases;
|
|
100
100
|
}
|
|
@@ -44,7 +44,7 @@ async function resolveDb(opts, cache) {
|
|
|
44
44
|
if (opts.db)
|
|
45
45
|
return opts.db;
|
|
46
46
|
if (!cache.db) {
|
|
47
|
-
cache.db = import("
|
|
47
|
+
cache.db = import("harper").then((h) => h.databases);
|
|
48
48
|
}
|
|
49
49
|
return cache.db;
|
|
50
50
|
}
|
|
@@ -123,7 +123,7 @@ export async function runNonceCleanupTick(opts = {}) {
|
|
|
123
123
|
db = opts.db;
|
|
124
124
|
}
|
|
125
125
|
else {
|
|
126
|
-
const harper = await import("
|
|
126
|
+
const harper = await import("harper");
|
|
127
127
|
db = harper.databases;
|
|
128
128
|
}
|
|
129
129
|
const retentionMs = opts.retentionMs ?? DEFAULT_RETENTION_MS;
|
|
@@ -159,7 +159,7 @@ export async function runNonceCleanupTick(opts = {}) {
|
|
|
159
159
|
* Initialise the periodic Nonce-table eviction sweep (5-min cadence).
|
|
160
160
|
*
|
|
161
161
|
* In test environments, callers pass a mock `db` via `opts` so this module
|
|
162
|
-
* never imports
|
|
162
|
+
* never imports harper at the top level (mirrors
|
|
163
163
|
* federation-cleanup.ts's `initFederationCleanup`).
|
|
164
164
|
*/
|
|
165
165
|
export async function initNonceStoreCleanup(opts) {
|
|
@@ -170,11 +170,11 @@ export async function initNonceStoreCleanup(opts) {
|
|
|
170
170
|
}
|
|
171
171
|
else {
|
|
172
172
|
try {
|
|
173
|
-
const harper = await import("
|
|
173
|
+
const harper = await import("harper");
|
|
174
174
|
db = harper.databases;
|
|
175
175
|
}
|
|
176
176
|
catch (err) {
|
|
177
|
-
console.error("[federation-nonce-store] failed to load
|
|
177
|
+
console.error("[federation-nonce-store] failed to load harper:", err?.message ?? err);
|
|
178
178
|
return;
|
|
179
179
|
}
|
|
180
180
|
}
|