@kindgi/memory 0.1.4-rc.5 → 0.1.5-rc.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/errors.d.ts +10 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/fusion.d.ts +20 -0
- package/dist/fusion.d.ts.map +1 -0
- package/dist/fusion.js +34 -0
- package/dist/fusion.js.map +1 -0
- package/dist/index.d.ts +8 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/log.d.ts +14 -0
- package/dist/log.d.ts.map +1 -1
- package/dist/memory-binding.d.ts +32 -2
- package/dist/memory-binding.d.ts.map +1 -1
- package/dist/readers.d.ts +18 -0
- package/dist/readers.d.ts.map +1 -0
- package/dist/readers.js +40 -0
- package/dist/readers.js.map +1 -0
- package/dist/recall.d.ts +100 -0
- package/dist/recall.d.ts.map +1 -0
- package/dist/recall.js +37 -0
- package/dist/recall.js.map +1 -0
- package/dist/remember.d.ts +76 -0
- package/dist/remember.d.ts.map +1 -0
- package/dist/remember.js +4 -0
- package/dist/remember.js.map +1 -0
- package/dist/types.d.ts +92 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/errors.ts +11 -0
- package/src/fusion.ts +50 -0
- package/src/index.ts +33 -0
- package/src/log.ts +14 -0
- package/src/memory-binding.ts +35 -1
- package/src/readers.ts +41 -0
- package/src/recall.ts +124 -0
- package/src/remember.ts +79 -0
- package/src/types.ts +98 -1
package/dist/types.d.ts
CHANGED
|
@@ -18,7 +18,66 @@ export interface MemoryScope {
|
|
|
18
18
|
readonly projectId?: ProjectId;
|
|
19
19
|
readonly threadId?: ThreadId;
|
|
20
20
|
readonly sessionId?: SessionId;
|
|
21
|
+
/**
|
|
22
|
+
* An app's end user, by the app's own opaque id (as conversations and
|
|
23
|
+
* judgments name them): a fact about or for one person who isn't a
|
|
24
|
+
* Kindgi user. A participant's facts are private to that participant's
|
|
25
|
+
* runs. Needs `projectId`: an app's end users belong to a project.
|
|
26
|
+
*/
|
|
27
|
+
readonly participantId?: string;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Who can see which facts: the containers a reader may read, worked out
|
|
31
|
+
* by the server from the run or the caller (never from a request body),
|
|
32
|
+
* and applied inside every query. A fact is readable when each container
|
|
33
|
+
* its scope names is one the reader has: its project, org, user,
|
|
34
|
+
* participant and thread (a fact naming none of them is tenant-wide, and
|
|
35
|
+
* every reader in the tenant sees it). A missing field grants none.
|
|
36
|
+
*/
|
|
37
|
+
export interface MemoryReaders {
|
|
38
|
+
/** Every fact in the tenant (a tenant admin). The other fields are then ignored. */
|
|
39
|
+
readonly all?: true;
|
|
40
|
+
/** Projects whose facts it reads. */
|
|
41
|
+
readonly projectIds?: readonly ProjectId[];
|
|
42
|
+
/** Orgs whose org-wide facts (no project) it reads: a run's project's org. */
|
|
43
|
+
readonly orgIds?: readonly OrgId[];
|
|
44
|
+
/** Kindgi users whose personal facts it reads. */
|
|
45
|
+
readonly userIds?: readonly UserId[];
|
|
46
|
+
/** End users (participants) whose facts it reads. */
|
|
47
|
+
readonly participantIds?: readonly string[];
|
|
48
|
+
/** Conversations whose thread facts it reads. */
|
|
49
|
+
readonly threadIds?: readonly ThreadId[];
|
|
50
|
+
/**
|
|
51
|
+
* Projects where it reads every participant's and every thread's facts:
|
|
52
|
+
* an app's own credential, acting for all of its end users.
|
|
53
|
+
*/
|
|
54
|
+
readonly onBehalfOfProjectIds?: readonly ProjectId[];
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* How far a fact is trusted. `verified`: a person with the right checked
|
|
58
|
+
* it. `asserted`: an app or a person wrote it. `unverified`: an agent
|
|
59
|
+
* remembered it during a conversation.
|
|
60
|
+
*/
|
|
61
|
+
export type FactTrust = 'verified' | 'asserted' | 'unverified';
|
|
62
|
+
/** Whom a fact is about: what access and erasure requests by person find. */
|
|
63
|
+
export interface FactSubject {
|
|
64
|
+
readonly kind: 'participant' | 'user' | 'external';
|
|
65
|
+
readonly id: string;
|
|
66
|
+
}
|
|
67
|
+
/** Who asserted a fact (PROV `wasAttributedTo`), set by the server from the writer. */
|
|
68
|
+
export interface FactAttribution {
|
|
69
|
+
readonly kind: 'user' | 'service' | 'agent';
|
|
70
|
+
readonly id: string;
|
|
71
|
+
readonly agentVersion?: string;
|
|
72
|
+
}
|
|
73
|
+
/** The run step that wrote a fact (PROV `wasGeneratedBy`), for one an agent wrote. */
|
|
74
|
+
export interface FactGeneratedBy {
|
|
75
|
+
readonly runId: string;
|
|
76
|
+
readonly stepId?: string;
|
|
77
|
+
readonly toolCallId?: string;
|
|
21
78
|
}
|
|
79
|
+
/** Why a revision stopped being current. */
|
|
80
|
+
export type FactInvalidationReason = 'superseded' | 'deleted' | 'erased' | 'expired';
|
|
22
81
|
/**
|
|
23
82
|
* Retention override at the fact level. Tenant policy sets defaults; a
|
|
24
83
|
* fact-level override wins if present.
|
|
@@ -68,7 +127,13 @@ export interface SourceRefresh {
|
|
|
68
127
|
* per-fact retrieval hint.
|
|
69
128
|
*/
|
|
70
129
|
export interface Fact<TContent = unknown> {
|
|
130
|
+
/**
|
|
131
|
+
* The fact, across its revisions: superseding keeps it. (For a fact that
|
|
132
|
+
* was never superseded it is also its one revision's id.)
|
|
133
|
+
*/
|
|
71
134
|
readonly id: FactId;
|
|
135
|
+
/** This revision's own id; absent where it equals `id`. */
|
|
136
|
+
readonly revisionId?: string;
|
|
72
137
|
/**
|
|
73
138
|
* Fact type identifier. Packs define their own; a few general-purpose
|
|
74
139
|
* names are conventional ('working-memory', 'user-profile', 'summary',
|
|
@@ -76,7 +141,7 @@ export interface Fact<TContent = unknown> {
|
|
|
76
141
|
*/
|
|
77
142
|
readonly type: string;
|
|
78
143
|
readonly scope: MemoryScope;
|
|
79
|
-
/**
|
|
144
|
+
/** The revision number within the fact: 1, then one more per supersede or verify. */
|
|
80
145
|
readonly version: number;
|
|
81
146
|
readonly createdAt: Timestamp;
|
|
82
147
|
readonly updatedAt?: Timestamp;
|
|
@@ -92,6 +157,32 @@ export interface Fact<TContent = unknown> {
|
|
|
92
157
|
readonly retention?: Retention;
|
|
93
158
|
readonly source?: Source;
|
|
94
159
|
readonly causedByLogId?: readonly string[];
|
|
160
|
+
/** The revision this one replaced (PROV `wasRevisionOf`). */
|
|
95
161
|
readonly supersedes?: FactId;
|
|
162
|
+
/** How far it's trusted. Absent on facts from before trust was recorded: `asserted`. */
|
|
163
|
+
readonly trust?: FactTrust;
|
|
164
|
+
readonly verifiedBy?: string;
|
|
165
|
+
readonly verifiedAt?: Timestamp;
|
|
166
|
+
readonly attributedTo?: FactAttribution;
|
|
167
|
+
readonly generatedBy?: FactGeneratedBy;
|
|
168
|
+
readonly subjects?: readonly FactSubject[];
|
|
169
|
+
/** When the fact is true in the world (application time); absent: always. */
|
|
170
|
+
readonly validFrom?: Timestamp;
|
|
171
|
+
readonly validUntil?: Timestamp;
|
|
172
|
+
/** When it was said or seen. */
|
|
173
|
+
readonly observedAt?: Timestamp;
|
|
174
|
+
/** When this revision stopped being current (record time), by whom, and why; absent: current. */
|
|
175
|
+
readonly invalidatedAt?: Timestamp;
|
|
176
|
+
readonly invalidatedBy?: string;
|
|
177
|
+
readonly invalidationReason?: FactInvalidationReason;
|
|
178
|
+
/** `pending` while a person must approve it: a pending fact is never retrieved. */
|
|
179
|
+
readonly review?: 'pending';
|
|
180
|
+
/**
|
|
181
|
+
* When this revision stops being readable: from its retention
|
|
182
|
+
* (`keepUntil`, or `keepDays` from the fact's first write), or an
|
|
183
|
+
* agent-remembered fact's unverified window. No read returns it after.
|
|
184
|
+
* Absent: it doesn't expire.
|
|
185
|
+
*/
|
|
186
|
+
readonly expiresAt?: Timestamp;
|
|
96
187
|
}
|
|
97
188
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,MAAM,EACN,KAAK,EACL,SAAS,EACT,SAAS,EACT,QAAQ,EACR,QAAQ,EACR,SAAS,EACT,MAAM,EACP,MAAM,eAAe,CAAC;AAEvB;;;;;;;GAOG;AACH,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAC5B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC;IACvB;;;OAGG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,CAAC;IAC/B,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC;IAC7B,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,CAAC;CAChC;AAED;;;GAGG;AACH,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,CAAC;IAC/B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC;CAC9B;AAED;;;GAGG;AACH,MAAM,WAAW,MAAM;IACrB,QAAQ,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,GAAG,UAAU,GAAG,aAAa,GAAG,YAAY,CAAC;IAC/E,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,SAAS,EAAE,eAAe,CAAC;IACpC,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC;CACjC;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,cAAc,CAAC,EAAE,SAAS,CAAC;IACpC,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;CACjC;AAED,MAAM,WAAW,aAAa;IAC5B;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,EAAE,SAAS,GAAG,YAAY,GAAG,QAAQ,CAAC;IACvD;;;OAGG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,IAAI,CAAC,QAAQ,GAAG,OAAO;IACtC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB;;;;OAIG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC;IAC5B,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kindgi/memory",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5-rc.0",
|
|
4
4
|
"description": "Kindgi™ memory type surface. Public type contract for the memory subsystem: Fact (typed versioned record, Kind-A immutable or Kind-B cached-view), Retention (fact-level retention override), MemoryScope (where in the tenant/user/thread hierarchy a Fact lives), Source + SourceFreshness + SourceRefresh (external-source metadata for Kind-B facts). Also LogEntry + LOG_KINDS (hash-chained run log), RetrievalHit, the MemoryError variants, and MemoryQueryBinding (the data-access interface a binding implementation provides). Types and interfaces only — no runtime state. Storage, indexes, retrieval, and retention sweeps are provided by a binding implementation, such as the Postgres-backed one in the Kindgi runtime.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -28,8 +28,8 @@
|
|
|
28
28
|
"README.md"
|
|
29
29
|
],
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@kindgi/embedding": "0.1.
|
|
32
|
-
"@kindgi/types": "0.1.
|
|
31
|
+
"@kindgi/embedding": "0.1.5-rc.0",
|
|
32
|
+
"@kindgi/types": "0.1.5-rc.0"
|
|
33
33
|
},
|
|
34
34
|
"engines": {
|
|
35
35
|
"node": ">=22.12.0"
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"scripts": {
|
|
47
47
|
"build": "tsc -p tsconfig.build.json",
|
|
48
48
|
"typecheck": "tsc --noEmit",
|
|
49
|
-
"test": "vitest run
|
|
49
|
+
"test": "vitest run",
|
|
50
50
|
"clean": "rm -rf dist *.tsbuildinfo"
|
|
51
51
|
}
|
|
52
52
|
}
|
package/src/errors.ts
CHANGED
|
@@ -22,6 +22,7 @@ export type MemoryError =
|
|
|
22
22
|
| EmbeddingError
|
|
23
23
|
| RefreshHandlerMissingError
|
|
24
24
|
| RetentionViolationError
|
|
25
|
+
| ErasureInProgressError
|
|
25
26
|
| PersistenceError;
|
|
26
27
|
|
|
27
28
|
export interface InvalidLogEntryError {
|
|
@@ -69,6 +70,16 @@ export interface RetentionViolationError {
|
|
|
69
70
|
readonly reason: 'legal-hold' | 'keep-until' | 'keep-days';
|
|
70
71
|
}
|
|
71
72
|
|
|
73
|
+
/**
|
|
74
|
+
* A write for a person (by scope or subject), or a conversation, that an
|
|
75
|
+
* erasure in progress holds: nothing new lands for them until it
|
|
76
|
+
* completes.
|
|
77
|
+
*/
|
|
78
|
+
export interface ErasureInProgressError {
|
|
79
|
+
readonly code: 'erasure-in-progress';
|
|
80
|
+
readonly message: string;
|
|
81
|
+
}
|
|
82
|
+
|
|
72
83
|
export interface PersistenceError {
|
|
73
84
|
readonly code: 'persistence-error';
|
|
74
85
|
readonly message: string;
|
package/src/fusion.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// Copyright (C) 2026 Kindgi Inc.
|
|
3
|
+
|
|
4
|
+
/** The `k` of reciprocal rank fusion: how much the top ranks dominate (60 is the usual choice). */
|
|
5
|
+
export const RRF_K = 60;
|
|
6
|
+
|
|
7
|
+
/** One fused item: its score and its 1-based rank in each leg that found it. */
|
|
8
|
+
export interface Fused<T> {
|
|
9
|
+
readonly item: T;
|
|
10
|
+
/** `Σ 1 / (k + rank)` over the legs that found it. */
|
|
11
|
+
readonly score: number;
|
|
12
|
+
/** Its rank in each leg, by the leg's name; absent where that leg didn't find it. */
|
|
13
|
+
readonly ranks: Readonly<Record<string, number>>;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Reciprocal rank fusion of ranked result lists ("legs", each best
|
|
18
|
+
* first): every item scores `Σ 1 / (k + rank)` over the legs that found
|
|
19
|
+
* it, so agreement between legs wins and raw scores (a full-text rank, a
|
|
20
|
+
* cosine similarity) never need to be compared. Ties keep the order of
|
|
21
|
+
* the first leg that found them. Items are matched across legs by `key`;
|
|
22
|
+
* the first leg's copy of an item is kept.
|
|
23
|
+
*/
|
|
24
|
+
export function fuseByRank<T>(
|
|
25
|
+
legs: Readonly<Record<string, readonly T[]>>,
|
|
26
|
+
key: (item: T) => string,
|
|
27
|
+
k: number = RRF_K,
|
|
28
|
+
): readonly Fused<T>[] {
|
|
29
|
+
const fused = new Map<
|
|
30
|
+
string,
|
|
31
|
+
{ item: T; score: number; ranks: Record<string, number>; first: number }
|
|
32
|
+
>();
|
|
33
|
+
let seen = 0;
|
|
34
|
+
for (const [leg, items] of Object.entries(legs)) {
|
|
35
|
+
items.forEach((item, i) => {
|
|
36
|
+
const id = key(item);
|
|
37
|
+
const rank = i + 1;
|
|
38
|
+
const entry = fused.get(id);
|
|
39
|
+
if (entry === undefined) {
|
|
40
|
+
fused.set(id, { item, score: 1 / (k + rank), ranks: { [leg]: rank }, first: seen++ });
|
|
41
|
+
} else if (entry.ranks[leg] === undefined) {
|
|
42
|
+
entry.score += 1 / (k + rank);
|
|
43
|
+
entry.ranks[leg] = rank;
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
return [...fused.values()]
|
|
48
|
+
.sort((a, b) => b.score - a.score || a.first - b.first)
|
|
49
|
+
.map(({ item, score, ranks }) => ({ item, score, ranks }));
|
|
50
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -4,6 +4,12 @@
|
|
|
4
4
|
// ============ Wire types ============
|
|
5
5
|
export type {
|
|
6
6
|
Fact,
|
|
7
|
+
FactAttribution,
|
|
8
|
+
FactGeneratedBy,
|
|
9
|
+
FactInvalidationReason,
|
|
10
|
+
FactSubject,
|
|
11
|
+
FactTrust,
|
|
12
|
+
MemoryReaders,
|
|
7
13
|
MemoryScope,
|
|
8
14
|
Retention,
|
|
9
15
|
Source,
|
|
@@ -11,6 +17,23 @@ export type {
|
|
|
11
17
|
SourceRefresh,
|
|
12
18
|
} from './types.js';
|
|
13
19
|
|
|
20
|
+
// ============ The scope guard ============
|
|
21
|
+
export { isReadableBy } from './readers.js';
|
|
22
|
+
|
|
23
|
+
// ============ Recalling earlier conversations ============
|
|
24
|
+
export { isRecallReadableBy } from './recall.js';
|
|
25
|
+
export type {
|
|
26
|
+
RecallHit,
|
|
27
|
+
RecallRow,
|
|
28
|
+
RecallSelection,
|
|
29
|
+
RecalledMessage,
|
|
30
|
+
SearchConversationsInput,
|
|
31
|
+
} from './recall.js';
|
|
32
|
+
|
|
33
|
+
// ============ Hybrid retrieval ============
|
|
34
|
+
export { RRF_K, fuseByRank } from './fusion.js';
|
|
35
|
+
export type { Fused } from './fusion.js';
|
|
36
|
+
|
|
14
37
|
// ============ Log types ============
|
|
15
38
|
export { LOG_KINDS } from './log.js';
|
|
16
39
|
export type { LogEntry, LogKind } from './log.js';
|
|
@@ -20,6 +43,7 @@ export type { RetrievalHit } from './retrieval.js';
|
|
|
20
43
|
|
|
21
44
|
// ============ Errors ============
|
|
22
45
|
export type {
|
|
46
|
+
ErasureInProgressError,
|
|
23
47
|
FactNotFoundError,
|
|
24
48
|
InvalidFactError,
|
|
25
49
|
InvalidLogEntryError,
|
|
@@ -30,6 +54,15 @@ export type {
|
|
|
30
54
|
RetentionViolationError,
|
|
31
55
|
} from './errors.js';
|
|
32
56
|
|
|
57
|
+
// ============ Agent memory writes (the `remember` tool) ============
|
|
58
|
+
export type {
|
|
59
|
+
MemoryRememberBinding,
|
|
60
|
+
RememberFactInput,
|
|
61
|
+
RememberFactResult,
|
|
62
|
+
RememberReviewReason,
|
|
63
|
+
RememberedContent,
|
|
64
|
+
} from './remember.js';
|
|
65
|
+
|
|
33
66
|
// ============ MemoryQueryBinding — caller-plugged data-access surface ============
|
|
34
67
|
export type {
|
|
35
68
|
AppendLogInput,
|
package/src/log.ts
CHANGED
|
@@ -48,4 +48,18 @@ export interface LogEntry {
|
|
|
48
48
|
readonly prevHash: string;
|
|
49
49
|
readonly entryHash: string;
|
|
50
50
|
readonly causedByLogId?: string;
|
|
51
|
+
/**
|
|
52
|
+
* How `entryHash` was computed. `2`: over the payload's hash
|
|
53
|
+
* (`contentHash`), so a payload cleared by an erasure still verifies.
|
|
54
|
+
* `1` (entries from before): over the payload itself. Absent: 1.
|
|
55
|
+
*/
|
|
56
|
+
readonly hashVersion?: 1 | 2;
|
|
57
|
+
/** When an erasure cleared `payload` (its `contentHash` stays). */
|
|
58
|
+
readonly payloadErasedAt?: Timestamp;
|
|
59
|
+
/**
|
|
60
|
+
* The salt of a v2 entry's `contentHash`: the hash is over the payload
|
|
61
|
+
* and this salt, so it can't confirm a guessed payload. An erasure
|
|
62
|
+
* clears it with the payload.
|
|
63
|
+
*/
|
|
64
|
+
readonly payloadSalt?: string;
|
|
51
65
|
}
|
package/src/memory-binding.ts
CHANGED
|
@@ -7,10 +7,12 @@ import type {
|
|
|
7
7
|
LogEntry,
|
|
8
8
|
LogKind,
|
|
9
9
|
MemoryError,
|
|
10
|
+
MemoryReaders,
|
|
10
11
|
MemoryScope,
|
|
11
12
|
RetrievalHit,
|
|
12
13
|
} from '@kindgi/memory';
|
|
13
14
|
import type { Result, RunId, TenantId } from '@kindgi/types';
|
|
15
|
+
import type { RecallHit, SearchConversationsInput } from './recall.js';
|
|
14
16
|
|
|
15
17
|
/**
|
|
16
18
|
* Caller-plugged data-access surface for the memory subsystem. Agent
|
|
@@ -32,7 +34,9 @@ import type { Result, RunId, TenantId } from '@kindgi/types';
|
|
|
32
34
|
export interface MemoryQueryBinding {
|
|
33
35
|
/**
|
|
34
36
|
* List facts filtered by tenant, type, and scope, capped by `limit`
|
|
35
|
-
* (no pagination cursor)
|
|
37
|
+
* (no pagination cursor): each fact's current revision, never one
|
|
38
|
+
* pending review, and only what `readers` may see (the guard, applied
|
|
39
|
+
* in the query before the limit).
|
|
36
40
|
*/
|
|
37
41
|
listFacts<TContent = unknown>(
|
|
38
42
|
input: ListFactsInput,
|
|
@@ -56,6 +60,19 @@ export interface MemoryQueryBinding {
|
|
|
56
60
|
input: SearchBySemanticInput,
|
|
57
61
|
): Promise<Result<readonly RetrievalHit<TContent>[], MemoryError>>;
|
|
58
62
|
|
|
63
|
+
/**
|
|
64
|
+
* Recall messages of earlier conversations (a retrieval intent with
|
|
65
|
+
* `source: 'conversations'`): what `readers` may recall
|
|
66
|
+
* (`isRecallReadableBy`), narrowed by `selections`, newest first
|
|
67
|
+
* (`list`), by full-text rank (`keyword`) or by meaning (`semantic`).
|
|
68
|
+
* Optional: without it, such an intent recalls nothing and its turn
|
|
69
|
+
* journals why. `semantic` without embeddings answers
|
|
70
|
+
* `embedding-unavailable`, as fact search does.
|
|
71
|
+
*/
|
|
72
|
+
searchConversations?(
|
|
73
|
+
input: SearchConversationsInput,
|
|
74
|
+
): Promise<Result<readonly RecallHit[], MemoryError>>;
|
|
75
|
+
|
|
59
76
|
/**
|
|
60
77
|
* Append one entry to the run's hash-chained log. The implementation
|
|
61
78
|
* assigns `sequence` and `prevHash` / `entryHash` atomically; the
|
|
@@ -75,8 +92,15 @@ export interface MemoryQueryBinding {
|
|
|
75
92
|
export interface ListFactsInput {
|
|
76
93
|
readonly tenantId: TenantId;
|
|
77
94
|
readonly type?: string;
|
|
95
|
+
/** Narrows within what `readers` may see: every given key must match. */
|
|
78
96
|
readonly scope?: Partial<MemoryScope>;
|
|
97
|
+
/**
|
|
98
|
+
* What the reader may see (the scope guard), applied inside the query
|
|
99
|
+
* before any limit. Absent: only tenant-wide facts.
|
|
100
|
+
*/
|
|
101
|
+
readonly readers?: MemoryReaders;
|
|
79
102
|
readonly limit?: number;
|
|
103
|
+
/** Ignored: a list holds each fact's current revision. */
|
|
80
104
|
readonly latestOnly?: boolean;
|
|
81
105
|
}
|
|
82
106
|
|
|
@@ -85,6 +109,11 @@ export interface SearchByKeywordInput {
|
|
|
85
109
|
readonly query: string;
|
|
86
110
|
readonly type?: string;
|
|
87
111
|
readonly scope?: Partial<MemoryScope>;
|
|
112
|
+
/**
|
|
113
|
+
* What the reader may see (the scope guard), applied inside the query
|
|
114
|
+
* before any limit. Absent: only tenant-wide facts.
|
|
115
|
+
*/
|
|
116
|
+
readonly readers?: MemoryReaders;
|
|
88
117
|
readonly topK?: number;
|
|
89
118
|
}
|
|
90
119
|
|
|
@@ -99,6 +128,11 @@ export interface SearchBySemanticInput {
|
|
|
99
128
|
readonly embeddingModel?: string;
|
|
100
129
|
readonly type?: string;
|
|
101
130
|
readonly scope?: Partial<MemoryScope>;
|
|
131
|
+
/**
|
|
132
|
+
* What the reader may see (the scope guard), applied inside the query
|
|
133
|
+
* before any limit. Absent: only tenant-wide facts.
|
|
134
|
+
*/
|
|
135
|
+
readonly readers?: MemoryReaders;
|
|
102
136
|
readonly topK?: number;
|
|
103
137
|
}
|
|
104
138
|
|
package/src/readers.ts
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// Copyright (C) 2026 Kindgi Inc.
|
|
3
|
+
|
|
4
|
+
import type { MemoryReaders, MemoryScope } from './types.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Whether `readers` may see a fact in `scope`: the scope guard, as every
|
|
8
|
+
* memory binding applies it inside its queries (a SQL binding's guard
|
|
9
|
+
* must agree with this one, case for case).
|
|
10
|
+
*
|
|
11
|
+
* Each container the scope names must be one the readers have:
|
|
12
|
+
* - its project (or one they act in for all end users,
|
|
13
|
+
* `onBehalfOfProjectIds`);
|
|
14
|
+
* - its org, for an org-wide fact (no project);
|
|
15
|
+
* - its user;
|
|
16
|
+
* - its participant, or the project on behalf of all of them;
|
|
17
|
+
* - its thread, or the project on behalf of all of them.
|
|
18
|
+
* A fact naming none of them is tenant-wide: every reader sees it. The
|
|
19
|
+
* session is not a container.
|
|
20
|
+
*/
|
|
21
|
+
export function isReadableBy(scope: MemoryScope, readers: MemoryReaders): boolean {
|
|
22
|
+
if (readers.all === true) return true;
|
|
23
|
+
const project = scope.projectId;
|
|
24
|
+
const has = <T>(ids: readonly T[] | undefined, id: T) => ids?.includes(id) === true;
|
|
25
|
+
const onBehalf = project !== undefined && has(readers.onBehalfOfProjectIds, project);
|
|
26
|
+
if (project !== undefined && !(has(readers.projectIds, project) || onBehalf)) return false;
|
|
27
|
+
if (project === undefined && scope.orgId !== undefined && !has(readers.orgIds, scope.orgId)) {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
if (scope.userId !== undefined && !has(readers.userIds, scope.userId)) return false;
|
|
31
|
+
if (
|
|
32
|
+
scope.participantId !== undefined &&
|
|
33
|
+
!(has(readers.participantIds, scope.participantId) || onBehalf)
|
|
34
|
+
) {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
if (scope.threadId !== undefined && !(has(readers.threadIds, scope.threadId) || onBehalf)) {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
return true;
|
|
41
|
+
}
|
package/src/recall.ts
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// Copyright (C) 2026 Kindgi Inc.
|
|
3
|
+
|
|
4
|
+
import type { EmbeddingProviderRegistry } from '@kindgi/embedding';
|
|
5
|
+
import type { ScopeSegment, TenantId, Timestamp } from '@kindgi/types';
|
|
6
|
+
|
|
7
|
+
import type { MemoryReaders } from './types.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* One message of an earlier conversation, as recall finds it: a user's
|
|
11
|
+
* message or an agent's answer (tool calls and the turns that made them
|
|
12
|
+
* are not indexed), with the indexed messages either side for context.
|
|
13
|
+
*/
|
|
14
|
+
export interface RecalledMessage {
|
|
15
|
+
readonly conversationId: string;
|
|
16
|
+
readonly sequence: number;
|
|
17
|
+
readonly role: 'user' | 'agent';
|
|
18
|
+
readonly text: string;
|
|
19
|
+
readonly createdAt: Timestamp;
|
|
20
|
+
/** The agent the conversation was with. */
|
|
21
|
+
readonly agentId: string;
|
|
22
|
+
readonly projectId?: string;
|
|
23
|
+
/** The conversation's end user (the app's own id for them). */
|
|
24
|
+
readonly participantId?: string;
|
|
25
|
+
/** The Kindgi user its turns acted for, when one did. */
|
|
26
|
+
readonly userId?: string;
|
|
27
|
+
/** The indexed message (of the roles recalled) just before it in its conversation, if any. */
|
|
28
|
+
readonly before?: { readonly role: 'user' | 'agent'; readonly text: string };
|
|
29
|
+
/** The indexed message (of the roles recalled) just after it, if any. */
|
|
30
|
+
readonly after?: { readonly role: 'user' | 'agent'; readonly text: string };
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** A recalled message and how well it matched (a full-text rank or a cosine similarity). */
|
|
34
|
+
export interface RecallHit {
|
|
35
|
+
readonly message: RecalledMessage;
|
|
36
|
+
readonly score?: number;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* What one recall narrows to, within what the readers may see. Always
|
|
41
|
+
* one agent's conversations. Every other field given must match.
|
|
42
|
+
*/
|
|
43
|
+
export interface RecallSelection {
|
|
44
|
+
readonly agentId: string;
|
|
45
|
+
/** Only this conversation (with `beforeSequence`: its messages older than that). */
|
|
46
|
+
readonly conversationId?: string;
|
|
47
|
+
readonly beforeSequence?: number;
|
|
48
|
+
/** Every conversation but this one. */
|
|
49
|
+
readonly excludeConversationId?: string;
|
|
50
|
+
readonly participantId?: string;
|
|
51
|
+
readonly userId?: string;
|
|
52
|
+
readonly projectId?: string;
|
|
53
|
+
/** Conversations whose segment path starts with this one (the same customer). */
|
|
54
|
+
readonly segmentsPrefix?: readonly ScopeSegment[];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface SearchConversationsInput {
|
|
58
|
+
readonly tenantId: TenantId;
|
|
59
|
+
/** What the reader may see (`isRecallReadableBy`), applied in the query before the limit. */
|
|
60
|
+
readonly readers: MemoryReaders;
|
|
61
|
+
/** The narrowings, merged: a message matching any of them. */
|
|
62
|
+
readonly selections: readonly RecallSelection[];
|
|
63
|
+
/** `list`: the newest first, no query. `keyword`: full-text. `semantic`: by meaning. */
|
|
64
|
+
readonly mode: 'list' | 'keyword' | 'semantic';
|
|
65
|
+
/**
|
|
66
|
+
* Whose messages: the hits and their neighbours alike. Default
|
|
67
|
+
* `['user']`, the people's own words; an agent's earlier answers only
|
|
68
|
+
* when asked for.
|
|
69
|
+
*/
|
|
70
|
+
readonly roles?: readonly ('user' | 'agent')[];
|
|
71
|
+
readonly query?: string;
|
|
72
|
+
/** For `semantic`: the registry the query is embedded with. */
|
|
73
|
+
readonly embeddingRegistry?: EmbeddingProviderRegistry;
|
|
74
|
+
readonly embeddingModel?: string;
|
|
75
|
+
readonly topK: number;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** The containers a recalled message's conversation is in, as the recall guard sees them. */
|
|
79
|
+
export interface RecallRow {
|
|
80
|
+
readonly conversationId: string;
|
|
81
|
+
readonly projectId?: string;
|
|
82
|
+
readonly participantId?: string;
|
|
83
|
+
readonly userId?: string;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Whether `readers` may recall a message of this conversation: the recall
|
|
88
|
+
* guard, as every binding applies it inside its query (a SQL binding's
|
|
89
|
+
* guard must agree with this one, case for case). Stricter than facts'
|
|
90
|
+
* (`isReadableBy`): a conversation is always someone's.
|
|
91
|
+
* - **Its project:** one the readers have, or act in for every end user
|
|
92
|
+
* (`onBehalfOfProjectIds`). A conversation without a project passes
|
|
93
|
+
* only by its person or the conversation itself.
|
|
94
|
+
* - **Its person:** its end user when it has one, else the Kindgi user
|
|
95
|
+
* its turns acted for. The readers must be that person
|
|
96
|
+
* (`participantIds`, or `userIds` for a conversation without an end
|
|
97
|
+
* user), be in the conversation (`threadIds`), or act in its project
|
|
98
|
+
* for every end user. An app's credential is one Kindgi user for all
|
|
99
|
+
* of its end users, so the user never opens an end user's
|
|
100
|
+
* conversation. A conversation naming no person is readable only
|
|
101
|
+
* from inside it, or for its whole project.
|
|
102
|
+
*/
|
|
103
|
+
export function isRecallReadableBy(row: RecallRow, readers: MemoryReaders): boolean {
|
|
104
|
+
if (readers.all === true) return true;
|
|
105
|
+
const has = <T>(ids: readonly T[] | undefined, id: T | undefined) =>
|
|
106
|
+
id !== undefined && ids?.includes(id) === true;
|
|
107
|
+
const project = row.projectId;
|
|
108
|
+
const onBehalf = has(readers.onBehalfOfProjectIds as readonly string[] | undefined, project);
|
|
109
|
+
if (
|
|
110
|
+
project !== undefined &&
|
|
111
|
+
!(has(readers.projectIds as readonly string[] | undefined, project) || onBehalf)
|
|
112
|
+
) {
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
const isPerson =
|
|
116
|
+
row.participantId !== undefined
|
|
117
|
+
? has(readers.participantIds, row.participantId)
|
|
118
|
+
: has(readers.userIds as readonly string[] | undefined, row.userId);
|
|
119
|
+
return (
|
|
120
|
+
isPerson ||
|
|
121
|
+
has(readers.threadIds as readonly string[] | undefined, row.conversationId) ||
|
|
122
|
+
onBehalf
|
|
123
|
+
);
|
|
124
|
+
}
|
package/src/remember.ts
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// Copyright (C) 2026 Kindgi Inc.
|
|
3
|
+
|
|
4
|
+
import type { Result, TenantId, Timestamp } from '@kindgi/types';
|
|
5
|
+
|
|
6
|
+
import type { MemoryError } from './errors.js';
|
|
7
|
+
import type { Fact, FactSubject, MemoryScope } from './types.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* What an agent remembers through its `remember` tool: a short text, and
|
|
11
|
+
* optionally the slot (`key`) it fills. A new value for a slot the same
|
|
12
|
+
* agent filled before, for the same type and scope, is that fact's next
|
|
13
|
+
* revision.
|
|
14
|
+
*/
|
|
15
|
+
export interface RememberedContent {
|
|
16
|
+
readonly text: string;
|
|
17
|
+
readonly key?: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** Why a remembered fact waits for a person before any read sees it. */
|
|
21
|
+
export type RememberReviewReason =
|
|
22
|
+
/** The declared scope reaches beyond one person (`same-project`, `tenant`). */
|
|
23
|
+
| 'wide-scope'
|
|
24
|
+
/** The text reads like an instruction (always/never/ignore, a URL, a tool name). */
|
|
25
|
+
| 'instruction-like';
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* One `remember` call, as the agent layer builds it from the agent's
|
|
29
|
+
* declaration and the run. The model chooses only the type (among those
|
|
30
|
+
* declared), the text, the slot and how long it's true; never the scope,
|
|
31
|
+
* the trust or whom it's attributed to.
|
|
32
|
+
*/
|
|
33
|
+
export interface RememberFactInput {
|
|
34
|
+
readonly tenantId: TenantId;
|
|
35
|
+
/** Where it's stored: the declaration's scope, filled from the run. */
|
|
36
|
+
readonly scope: MemoryScope;
|
|
37
|
+
readonly type: string;
|
|
38
|
+
readonly content: RememberedContent;
|
|
39
|
+
/** Whom it's about: the conversation's end user, else the run's user. */
|
|
40
|
+
readonly subjects: readonly FactSubject[];
|
|
41
|
+
/** The agent version that remembered it. */
|
|
42
|
+
readonly agent: { readonly id: string; readonly version: string };
|
|
43
|
+
/**
|
|
44
|
+
* The call that wrote it. A second call with the same run and tool call
|
|
45
|
+
* (a step re-run after a crash) returns the fact the first one wrote.
|
|
46
|
+
*/
|
|
47
|
+
readonly generatedBy: {
|
|
48
|
+
readonly runId: string;
|
|
49
|
+
readonly stepId?: string;
|
|
50
|
+
readonly toolCallId: string;
|
|
51
|
+
};
|
|
52
|
+
/** How long it's kept unless a person verifies it (retention `keepDays`). */
|
|
53
|
+
readonly keepDays: number;
|
|
54
|
+
/** When it stops being true in the world, if the model said so. */
|
|
55
|
+
readonly validUntil?: Timestamp;
|
|
56
|
+
/** Present when a person must approve it first; no read sees it until then. */
|
|
57
|
+
readonly review?: { readonly reasons: readonly RememberReviewReason[] };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export interface RememberFactResult {
|
|
61
|
+
/** The revision written: `trust: unverified`, `review: pending` while it waits. */
|
|
62
|
+
readonly fact: Fact<RememberedContent>;
|
|
63
|
+
/**
|
|
64
|
+
* `created`: a new fact. `superseded`: the next revision of the fact
|
|
65
|
+
* that held the slot. `replayed`: this call had already written it.
|
|
66
|
+
*/
|
|
67
|
+
readonly outcome: 'created' | 'superseded' | 'replayed';
|
|
68
|
+
/** The approval a pending fact waits on. */
|
|
69
|
+
readonly approvalId?: string;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Agent memory writes (the `remember` tool). Optional on the agent
|
|
74
|
+
* bindings: a host without it offers the tool, and a call says it
|
|
75
|
+
* can't remember.
|
|
76
|
+
*/
|
|
77
|
+
export interface MemoryRememberBinding {
|
|
78
|
+
remember(input: RememberFactInput): Promise<Result<RememberFactResult, MemoryError>>;
|
|
79
|
+
}
|