@manta-eu/client 0.1.9 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (206) hide show
  1. package/README.md +21 -0
  2. package/dist/generated/graphql.d.ts +1132 -2
  3. package/dist/generated/graphql.d.ts.map +1 -1
  4. package/dist/generated/graphql.js +637 -0
  5. package/dist/generated/graphql.js.map +1 -0
  6. package/dist/graphql-clients.d.ts +10 -1
  7. package/dist/graphql-clients.d.ts.map +1 -1
  8. package/dist/graphql-clients.js +25 -3
  9. package/dist/graphql-clients.js.map +1 -0
  10. package/dist/index.js +1 -0
  11. package/dist/index.js.map +1 -0
  12. package/dist/shared/action-document.js +1 -0
  13. package/dist/shared/action-document.js.map +1 -0
  14. package/dist/shared/action-plan.js +1 -0
  15. package/dist/shared/action-plan.js.map +1 -0
  16. package/dist/shared/agent-mode.js +1 -0
  17. package/dist/shared/agent-mode.js.map +1 -0
  18. package/dist/shared/agent-types.js +1 -0
  19. package/dist/shared/agent-types.js.map +1 -0
  20. package/dist/shared/chat-context.js +1 -0
  21. package/dist/shared/chat-context.js.map +1 -0
  22. package/dist/shared/chat-manager.d.ts +6 -0
  23. package/dist/shared/chat-manager.d.ts.map +1 -1
  24. package/dist/shared/chat-manager.js +5 -0
  25. package/dist/shared/chat-manager.js.map +1 -0
  26. package/dist/shared/chat-turn-errors.d.ts +10 -0
  27. package/dist/shared/chat-turn-errors.d.ts.map +1 -1
  28. package/dist/shared/chat-turn-errors.js +11 -0
  29. package/dist/shared/chat-turn-errors.js.map +1 -0
  30. package/dist/shared/constants.d.ts +1 -0
  31. package/dist/shared/constants.d.ts.map +1 -1
  32. package/dist/shared/constants.js +2 -0
  33. package/dist/shared/constants.js.map +1 -0
  34. package/dist/shared/conversation-starter-defaults.js +1 -0
  35. package/dist/shared/conversation-starter-defaults.js.map +1 -0
  36. package/dist/shared/datastore-id.js +1 -0
  37. package/dist/shared/datastore-id.js.map +1 -0
  38. package/dist/shared/datastore-path.js +1 -0
  39. package/dist/shared/datastore-path.js.map +1 -0
  40. package/dist/shared/duration.js +1 -0
  41. package/dist/shared/duration.js.map +1 -0
  42. package/dist/shared/email.js +1 -0
  43. package/dist/shared/email.js.map +1 -0
  44. package/dist/shared/entity-set.js +1 -0
  45. package/dist/shared/entity-set.js.map +1 -0
  46. package/dist/shared/error-utils.js +1 -0
  47. package/dist/shared/error-utils.js.map +1 -0
  48. package/dist/shared/explorer-paths.js +1 -0
  49. package/dist/shared/explorer-paths.js.map +1 -0
  50. package/dist/shared/fileIri.js +1 -0
  51. package/dist/shared/fileIri.js.map +1 -0
  52. package/dist/shared/index.js +1 -0
  53. package/dist/shared/index.js.map +1 -0
  54. package/dist/shared/inferal-stream.js +1 -0
  55. package/dist/shared/inferal-stream.js.map +1 -0
  56. package/dist/shared/live-context.js +1 -0
  57. package/dist/shared/live-context.js.map +1 -0
  58. package/dist/shared/maintenance-schedule.js +1 -0
  59. package/dist/shared/maintenance-schedule.js.map +1 -0
  60. package/dist/shared/memory-citation-sampling.js +1 -0
  61. package/dist/shared/memory-citation-sampling.js.map +1 -0
  62. package/dist/shared/memory-confidence.js +1 -0
  63. package/dist/shared/memory-confidence.js.map +1 -0
  64. package/dist/shared/memory-consolidation-limits.js +1 -0
  65. package/dist/shared/memory-consolidation-limits.js.map +1 -0
  66. package/dist/shared/memory-dates.js +1 -0
  67. package/dist/shared/memory-dates.js.map +1 -0
  68. package/dist/shared/memory-decay.js +1 -0
  69. package/dist/shared/memory-decay.js.map +1 -0
  70. package/dist/shared/memory-diversity.js +1 -0
  71. package/dist/shared/memory-diversity.js.map +1 -0
  72. package/dist/shared/memory-entity-recall.js +1 -0
  73. package/dist/shared/memory-entity-recall.js.map +1 -0
  74. package/dist/shared/memory-importance.js +1 -0
  75. package/dist/shared/memory-importance.js.map +1 -0
  76. package/dist/shared/memory-keyword-recall.js +1 -0
  77. package/dist/shared/memory-keyword-recall.js.map +1 -0
  78. package/dist/shared/memory-kind-iris.js +1 -0
  79. package/dist/shared/memory-kind-iris.js.map +1 -0
  80. package/dist/shared/memory-preload-gate.js +1 -0
  81. package/dist/shared/memory-preload-gate.js.map +1 -0
  82. package/dist/shared/memory-sources.js +1 -0
  83. package/dist/shared/memory-sources.js.map +1 -0
  84. package/dist/shared/memory-teams.js +1 -0
  85. package/dist/shared/memory-teams.js.map +1 -0
  86. package/dist/shared/memory-token-budget.js +1 -0
  87. package/dist/shared/memory-token-budget.js.map +1 -0
  88. package/dist/shared/memory-volatility.js +1 -0
  89. package/dist/shared/memory-volatility.js.map +1 -0
  90. package/dist/shared/message-utils.js +1 -0
  91. package/dist/shared/message-utils.js.map +1 -0
  92. package/dist/shared/model.js +1 -0
  93. package/dist/shared/model.js.map +1 -0
  94. package/dist/shared/org-roles.js +1 -0
  95. package/dist/shared/org-roles.js.map +1 -0
  96. package/dist/shared/page-context.js +1 -0
  97. package/dist/shared/page-context.js.map +1 -0
  98. package/dist/shared/permissions.js +1 -0
  99. package/dist/shared/permissions.js.map +1 -0
  100. package/dist/shared/questionnaire.js +1 -0
  101. package/dist/shared/questionnaire.js.map +1 -0
  102. package/dist/shared/rdf-namespaces.js +1 -0
  103. package/dist/shared/rdf-namespaces.js.map +1 -0
  104. package/dist/shared/rule-iri.js +1 -0
  105. package/dist/shared/rule-iri.js.map +1 -0
  106. package/dist/shared/rule-search.js +1 -0
  107. package/dist/shared/rule-search.js.map +1 -0
  108. package/dist/shared/session-types.js +1 -0
  109. package/dist/shared/session-types.js.map +1 -0
  110. package/dist/shared/stakeholder-iri.js +1 -0
  111. package/dist/shared/stakeholder-iri.js.map +1 -0
  112. package/dist/shared/team-activation.js +1 -0
  113. package/dist/shared/team-activation.js.map +1 -0
  114. package/dist/shared/team-filters.js +1 -0
  115. package/dist/shared/team-filters.js.map +1 -0
  116. package/dist/shared/team.d.ts +1 -0
  117. package/dist/shared/team.d.ts.map +1 -1
  118. package/dist/shared/team.js +1 -0
  119. package/dist/shared/team.js.map +1 -0
  120. package/dist/shared/tools/coercers.js +1 -0
  121. package/dist/shared/tools/coercers.js.map +1 -0
  122. package/dist/shared/tools/index.js +1 -0
  123. package/dist/shared/tools/index.js.map +1 -0
  124. package/dist/shared/tools/metadata.js +1 -0
  125. package/dist/shared/tools/metadata.js.map +1 -0
  126. package/dist/shared/tools/test-triple.js +1 -0
  127. package/dist/shared/tools/test-triple.js.map +1 -0
  128. package/dist/shared/tools/types.js +1 -0
  129. package/dist/shared/tools/types.js.map +1 -0
  130. package/dist/shared/upload.js +1 -0
  131. package/dist/shared/upload.js.map +1 -0
  132. package/dist/shared/website-url.js +1 -0
  133. package/dist/shared/website-url.js.map +1 -0
  134. package/dist/shared/workspace-artifacts.js +1 -0
  135. package/dist/shared/workspace-artifacts.js.map +1 -0
  136. package/dist/shared/writable-shape-query.js +1 -0
  137. package/dist/shared/writable-shape-query.js.map +1 -0
  138. package/dist/types/jsonld.js +1 -0
  139. package/dist/types/jsonld.js.map +1 -0
  140. package/dist/types/sparql-types.js +1 -0
  141. package/dist/types/sparql-types.js.map +1 -0
  142. package/package.json +11 -6
  143. package/src/generated/graphql.ts +12158 -0
  144. package/src/graphql-clients.ts +67 -0
  145. package/src/index.ts +2 -0
  146. package/src/shared/action-document.ts +56 -0
  147. package/src/shared/action-plan.ts +1 -0
  148. package/src/shared/agent-mode.ts +46 -0
  149. package/src/shared/agent-types.ts +46 -0
  150. package/src/shared/chat-context.ts +47 -0
  151. package/src/shared/chat-manager.ts +199 -0
  152. package/src/shared/chat-turn-errors.ts +35 -0
  153. package/src/shared/constants.ts +19 -0
  154. package/src/shared/conversation-starter-defaults.ts +27 -0
  155. package/src/shared/datastore-id.ts +25 -0
  156. package/src/shared/datastore-path.ts +80 -0
  157. package/src/shared/duration.ts +61 -0
  158. package/src/shared/email.ts +54 -0
  159. package/src/shared/entity-set.ts +33 -0
  160. package/src/shared/error-utils.ts +79 -0
  161. package/src/shared/explorer-paths.ts +9 -0
  162. package/src/shared/fileIri.ts +25 -0
  163. package/src/shared/index.ts +31 -0
  164. package/src/shared/inferal-stream.ts +2 -0
  165. package/src/shared/live-context.ts +15 -0
  166. package/src/shared/maintenance-schedule.ts +47 -0
  167. package/src/shared/memory-citation-sampling.ts +26 -0
  168. package/src/shared/memory-confidence.ts +8 -0
  169. package/src/shared/memory-consolidation-limits.ts +9 -0
  170. package/src/shared/memory-dates.ts +7 -0
  171. package/src/shared/memory-decay.ts +70 -0
  172. package/src/shared/memory-diversity.ts +55 -0
  173. package/src/shared/memory-entity-recall.ts +26 -0
  174. package/src/shared/memory-importance.ts +12 -0
  175. package/src/shared/memory-keyword-recall.ts +149 -0
  176. package/src/shared/memory-kind-iris.ts +36 -0
  177. package/src/shared/memory-preload-gate.ts +150 -0
  178. package/src/shared/memory-sources.ts +5 -0
  179. package/src/shared/memory-teams.ts +3 -0
  180. package/src/shared/memory-token-budget.ts +31 -0
  181. package/src/shared/memory-volatility.ts +90 -0
  182. package/src/shared/message-utils.ts +37 -0
  183. package/src/shared/model.ts +117 -0
  184. package/src/shared/org-roles.ts +21 -0
  185. package/src/shared/page-context.ts +7 -0
  186. package/src/shared/permissions.ts +19 -0
  187. package/src/shared/questionnaire.ts +76 -0
  188. package/src/shared/rdf-namespaces.ts +62 -0
  189. package/src/shared/rule-iri.ts +15 -0
  190. package/src/shared/rule-search.ts +1 -0
  191. package/src/shared/session-types.ts +14 -0
  192. package/src/shared/stakeholder-iri.ts +23 -0
  193. package/src/shared/team-activation.ts +9 -0
  194. package/src/shared/team-filters.ts +33 -0
  195. package/src/shared/team.ts +244 -0
  196. package/src/shared/tools/coercers.ts +82 -0
  197. package/src/shared/tools/index.ts +49 -0
  198. package/src/shared/tools/metadata.ts +205 -0
  199. package/src/shared/tools/test-triple.ts +33 -0
  200. package/src/shared/tools/types.ts +302 -0
  201. package/src/shared/upload.ts +57 -0
  202. package/src/shared/website-url.ts +32 -0
  203. package/src/shared/workspace-artifacts.ts +68 -0
  204. package/src/shared/writable-shape-query.ts +24 -0
  205. package/src/types/jsonld.ts +19 -0
  206. package/src/types/sparql-types.ts +10 -0
@@ -0,0 +1,149 @@
1
+ import { isPresent } from "@manta-eu/lib/present";
2
+ import { memoryTokens, type DiversityCandidate } from "./memory-diversity";
3
+
4
+ export const rankFusionConstant = 60;
5
+
6
+ export const maxKeywordTerms = 8;
7
+
8
+ export type KeywordCandidate = DiversityCandidate & { iri: string };
9
+
10
+ // A term feeds a SPARQL CONTAINS that admits rows, so a stray function word like "the" would widen the pool to the
11
+ // entire store.
12
+ const stopWords = new Set([
13
+ "the",
14
+ "and",
15
+ "but",
16
+ "for",
17
+ "not",
18
+ "you",
19
+ "your",
20
+ "our",
21
+ "are",
22
+ "was",
23
+ "were",
24
+ "has",
25
+ "have",
26
+ "had",
27
+ "with",
28
+ "from",
29
+ "that",
30
+ "this",
31
+ "these",
32
+ "those",
33
+ "what",
34
+ "when",
35
+ "where",
36
+ "which",
37
+ "who",
38
+ "why",
39
+ "how",
40
+ "can",
41
+ "could",
42
+ "would",
43
+ "should",
44
+ "will",
45
+ "shall",
46
+ "may",
47
+ "might",
48
+ "must",
49
+ "there",
50
+ "their",
51
+ "them",
52
+ "they",
53
+ "its",
54
+ "his",
55
+ "her",
56
+ "him",
57
+ "she",
58
+ "here",
59
+ "into",
60
+ "onto",
61
+ "over",
62
+ "under",
63
+ "about",
64
+ "than",
65
+ "then",
66
+ "some",
67
+ "any",
68
+ "all",
69
+ "both",
70
+ "each",
71
+ "more",
72
+ "most",
73
+ "other",
74
+ "such",
75
+ "only",
76
+ "own",
77
+ "same",
78
+ "too",
79
+ "very",
80
+ "just",
81
+ "now",
82
+ "get",
83
+ "got",
84
+ "let",
85
+ "make",
86
+ "made",
87
+ "does",
88
+ "did",
89
+ "done",
90
+ "been",
91
+ "being",
92
+ "also",
93
+ "well",
94
+ "much",
95
+ "many",
96
+ "like",
97
+ "want",
98
+ "need",
99
+ "give",
100
+ "take",
101
+ "see",
102
+ ]);
103
+
104
+ export function keywordTerms(text: string, limit: number = maxKeywordTerms): string[] {
105
+ return [...memoryTokens({ label: text })]
106
+ .filter((token) => !stopWords.has(token))
107
+ .sort((a, b) => b.length - a.length || a.localeCompare(b))
108
+ .slice(0, limit);
109
+ }
110
+
111
+ export function keywordOverlap(memory: DiversityCandidate, terms: readonly string[]): number {
112
+ if (!isPresent(terms)) return 0;
113
+ const tokens = memoryTokens(memory);
114
+ let hits = 0;
115
+ for (const term of terms) if (tokens.has(term)) hits += 1;
116
+ return hits;
117
+ }
118
+
119
+ export function fuseRankings<T extends { iri: string }>(lists: readonly (readonly T[])[]): T[] {
120
+ const scores = new Map<string, number>();
121
+ const byIri = new Map<string, T>();
122
+
123
+ for (const list of lists) {
124
+ list.forEach((item, rank) => {
125
+ scores.set(item.iri, (scores.get(item.iri) ?? 0) + 1 / (rankFusionConstant + rank));
126
+ if (!byIri.has(item.iri)) byIri.set(item.iri, item);
127
+ });
128
+ }
129
+
130
+ return [...scores.entries()]
131
+ .sort(([iriA, scoreA], [iriB, scoreB]) => scoreB - scoreA || iriA.localeCompare(iriB))
132
+ .map(([iri]) => byIri.get(iri))
133
+ .filter((item) => item !== undefined);
134
+ }
135
+
136
+ export function fuseKeywordRanking<T extends KeywordCandidate>(semanticOrder: readonly T[], queryText: string): T[] {
137
+ const terms = keywordTerms(queryText);
138
+ if (!isPresent(terms) || !isPresent(semanticOrder)) return [...semanticOrder];
139
+
140
+ const keywordOrder = semanticOrder
141
+ .map((memory) => ({ memory, hits: keywordOverlap(memory, terms) }))
142
+ .filter((scored) => scored.hits > 0)
143
+ .sort((a, b) => b.hits - a.hits)
144
+ .map((scored) => scored.memory);
145
+
146
+ if (!isPresent(keywordOrder)) return [...semanticOrder];
147
+
148
+ return fuseRankings([semanticOrder, keywordOrder]);
149
+ }
@@ -0,0 +1,36 @@
1
+ import { vocabBase } from "@manta-eu/ldo/realm";
2
+
3
+ // GraphQL MemoryKind, backend valid-kind?, and KG sh:in must stay in sync with this.
4
+ export const memoryKinds = ["preference", "fact", "objective", "context"] as const;
5
+ export type MemoryKind = (typeof memoryKinds)[number];
6
+
7
+ const memoryKindIriBase = `${vocabBase}MemoryKind_`;
8
+
9
+ export const memoryKindIris = Object.fromEntries(
10
+ memoryKinds.map((kind) => [kind, `${memoryKindIriBase}${kind}`]),
11
+ ) as Record<MemoryKind, string>;
12
+
13
+ export type MemoryKindIri = (typeof memoryKindIris)[MemoryKind];
14
+
15
+ const iriToKind = new Map<string, MemoryKind>(memoryKinds.map((kind) => [memoryKindIris[kind], kind]));
16
+
17
+ function capitalize(value: string): string {
18
+ return value.charAt(0).toUpperCase() + value.slice(1);
19
+ }
20
+
21
+ export function isKindIri(iri: string): boolean {
22
+ return iriToKind.has(iri);
23
+ }
24
+
25
+ export function kindIriToKind(iri: string): MemoryKind | null {
26
+ return iriToKind.get(iri) ?? null;
27
+ }
28
+
29
+ export function kindIriToLabel(iri: string): string | null {
30
+ const kind = iriToKind.get(iri);
31
+ return kind ? capitalize(kind) : null;
32
+ }
33
+
34
+ export function kindToKindIri(kind: MemoryKind): string {
35
+ return memoryKindIris[kind];
36
+ }
@@ -0,0 +1,150 @@
1
+ import { isPresent } from "@manta-eu/lib/present";
2
+ import { msPerDay, parseMemoryDate } from "./memory-dates";
3
+ import { shouldDemoteOnUsage, type UseWeights } from "./memory-decay";
4
+
5
+ export { parseMemoryDate };
6
+ export type { UseWeights };
7
+
8
+ export const memoryPreloadConfidenceThreshold = 0.75;
9
+ export const memoryPreloadImportanceThreshold = 4;
10
+ export const memorySearchImportanceThreshold = 2;
11
+
12
+ export const memoryPreloadRowLimit = 20;
13
+
14
+ export const companyMemoryScope = "company";
15
+
16
+ export const validFromWorthShowingDays = 2;
17
+
18
+ export const trustedUntilExpiringSoonDays = 7;
19
+
20
+ export type MemoryPreloadCandidate = {
21
+ importance: number;
22
+ confidence?: number | null;
23
+ memoryValidUntil?: Date | string | null;
24
+ memoryValidFrom?: Date | string | null;
25
+ memoryTrustedUntil?: Date | string | null;
26
+ memoryUseCount?: number | null;
27
+ memoryCiteCount?: number | null;
28
+ memoryLastUsedAt?: Date | string | null;
29
+ scope?: string | null;
30
+ supersededBy?: string | null;
31
+ };
32
+
33
+ export type MemoryPreloadExclusion =
34
+ | "archived"
35
+ | "importance"
36
+ | "confidence"
37
+ | "stale"
38
+ | "expired"
39
+ | "scheduled"
40
+ | "cold";
41
+
42
+ export function isPastTrustedUntilAt(memoryTrustedUntil: Date | string | null | undefined, now: Date): boolean {
43
+ const horizon = parseMemoryDate(memoryTrustedUntil);
44
+ return horizon != null && horizon <= now;
45
+ }
46
+
47
+ // Elapsed time, not calendar days: this module runs in the browser too, where the local zone would
48
+ // shift the boundary by a day either side of UTC.
49
+ export function isTrustedUntilExpiringSoon(memoryTrustedUntil: Date | string | null | undefined, now: Date): boolean {
50
+ const horizon = parseMemoryDate(memoryTrustedUntil);
51
+ if (horizon == null || horizon <= now) return false;
52
+ return horizon.getTime() - now.getTime() <= trustedUntilExpiringSoonDays * msPerDay;
53
+ }
54
+
55
+ export function isMemoryArchived(memory: MemoryPreloadCandidate): boolean {
56
+ return isPresent(memory.supersededBy);
57
+ }
58
+
59
+ export function isNeverUsed(memory: MemoryPreloadCandidate, now: Date = new Date()): boolean {
60
+ return isMemoryPreloaded(memory, now) && (memory.memoryUseCount ?? 0) + (memory.memoryCiteCount ?? 0) === 0;
61
+ }
62
+
63
+ export function needsTrustReview(memory: MemoryPreloadCandidate, now: Date = new Date()): boolean {
64
+ if (isMemoryArchived(memory)) return false;
65
+ if (memory.memoryValidUntil) return false;
66
+ return (
67
+ isPastTrustedUntilAt(memory.memoryTrustedUntil, now) || isTrustedUntilExpiringSoon(memory.memoryTrustedUntil, now)
68
+ );
69
+ }
70
+
71
+ export function isMemorySearchable(memory: MemoryPreloadCandidate): boolean {
72
+ return !isMemoryArchived(memory) && memory.importance >= memorySearchImportanceThreshold;
73
+ }
74
+
75
+ // Mirrors the SPARQL pre-load gate; "cold" stays client-side since decay is weighted per caller.
76
+ export function memoryPreloadQueryExclusions(
77
+ memory: MemoryPreloadCandidate,
78
+ now: Date = new Date(),
79
+ ): MemoryPreloadExclusion[] {
80
+ const exclusions: MemoryPreloadExclusion[] = [];
81
+ if (isMemoryArchived(memory)) exclusions.push("archived");
82
+ if (memory.importance < memoryPreloadImportanceThreshold) exclusions.push("importance");
83
+ if (
84
+ memory.scope !== companyMemoryScope &&
85
+ memory.confidence != null &&
86
+ memory.confidence < memoryPreloadConfidenceThreshold
87
+ ) {
88
+ exclusions.push("confidence");
89
+ }
90
+ if (isPastTrustedUntilAt(memory.memoryTrustedUntil, now)) exclusions.push("stale");
91
+
92
+ // Mirrors liveContextMemoryDateFilter: the query drops on all three date predicates, not just trust.
93
+ const validUntil = parseMemoryDate(memory.memoryValidUntil);
94
+ if (validUntil != null && validUntil <= now) exclusions.push("expired");
95
+ const validFrom = parseMemoryDate(memory.memoryValidFrom);
96
+ if (validFrom != null && validFrom > now) exclusions.push("scheduled");
97
+
98
+ return exclusions;
99
+ }
100
+
101
+ export function isMemoryQueryGateEligible(memory: MemoryPreloadCandidate, now: Date = new Date()): boolean {
102
+ return !isPresent(memoryPreloadQueryExclusions(memory, now));
103
+ }
104
+
105
+ export function memoryPreloadExclusions(
106
+ memory: MemoryPreloadCandidate,
107
+ now: Date = new Date(),
108
+ weights?: UseWeights,
109
+ ): MemoryPreloadExclusion[] {
110
+ const exclusions = memoryPreloadQueryExclusions(memory, now);
111
+ if (shouldDemoteOnUsage(memory, now, weights)) exclusions.push("cold");
112
+
113
+ return exclusions;
114
+ }
115
+
116
+ export function isMemoryPreloaded(
117
+ memory: MemoryPreloadCandidate,
118
+ now: Date = new Date(),
119
+ weights?: UseWeights,
120
+ ): boolean {
121
+ return !isPresent(memoryPreloadExclusions(memory, now, weights));
122
+ }
123
+
124
+ const exclusionReasons: Record<MemoryPreloadExclusion, string> = {
125
+ archived: "it was replaced by a newer memory",
126
+ importance: `importance is below ${memoryPreloadImportanceThreshold} (High)`,
127
+ confidence: `confidence is below ${memoryPreloadConfidenceThreshold} — it has not been reviewed`,
128
+ stale: "it is no longer trusted and needs re-confirming",
129
+ expired: "its expiry date has passed",
130
+ scheduled: "it does not take effect until a later date",
131
+ cold: "nothing has used it in a long time, so it no longer earns a place in every prompt",
132
+ };
133
+
134
+ export function describeMemoryPreloadExclusions(
135
+ memory: MemoryPreloadCandidate,
136
+ exclusions: MemoryPreloadExclusion[],
137
+ ): string {
138
+ if (!isPresent(exclusions)) {
139
+ return `In your assistant's context, up to ${memoryPreloadRowLimit} per scope on any one turn (most relevant first).`;
140
+ }
141
+
142
+ const reasons = exclusions.map((exclusion) => exclusionReasons[exclusion]).join("; ");
143
+ return isMemorySearchable(memory)
144
+ ? `Not in context: ${reasons}. Still retrievable with searchMemories.`
145
+ : `Not in context: ${reasons}. Not retrievable with searchMemories either.`;
146
+ }
147
+
148
+ export function describeMemoryPreload(memory: MemoryPreloadCandidate, now: Date = new Date()): string {
149
+ return describeMemoryPreloadExclusions(memory, memoryPreloadExclusions(memory, now));
150
+ }
@@ -0,0 +1,5 @@
1
+ import { expandPrefix } from "./rdf-namespaces";
2
+
3
+ export const userManualMemorySource = expandPrefix(":UserManual");
4
+
5
+ export const derivedAbstractionMemorySource = expandPrefix(":DerivedAbstraction");
@@ -0,0 +1,3 @@
1
+ export function unionOfTeams(teamSets: readonly (readonly string[] | null | undefined)[]): string[] {
2
+ return [...new Set(teamSets.flatMap((teams) => teams ?? []))];
3
+ }
@@ -0,0 +1,31 @@
1
+ import { isPresent } from "@manta-eu/lib/present";
2
+ import { memoryPreloadRowLimit } from "./memory-preload-gate";
3
+
4
+ // 4 chars/token approximates English prose; the serving model's real tokenizer is not available in the Worker.
5
+ export const charsPerTokenEstimate = 4;
6
+
7
+ export const memoryPreloadRowTokenAllowance = 120;
8
+
9
+ export const memoryPreloadTokenBudgetPerBlock = memoryPreloadRowLimit * memoryPreloadRowTokenAllowance;
10
+
11
+ export const memoryPreloadBlockCount = 3;
12
+
13
+ export const memoryPreloadTokenBudgetTotal = memoryPreloadTokenBudgetPerBlock * memoryPreloadBlockCount;
14
+
15
+ export function estimateTokens(text: string): number {
16
+ return Math.ceil(text.length / charsPerTokenEstimate);
17
+ }
18
+
19
+ export function packToBudget<T>(items: readonly T[], cost: (item: T) => number, budget: number): T[] {
20
+ const kept: T[] = [];
21
+ let spent = 0;
22
+
23
+ for (const item of items) {
24
+ const itemCost = cost(item);
25
+ if (isPresent(kept) && spent + itemCost > budget) continue;
26
+ kept.push(item);
27
+ spent += itemCost;
28
+ }
29
+
30
+ return kept;
31
+ }
@@ -0,0 +1,90 @@
1
+ import { msPerDay, parseMemoryDate } from "./memory-dates";
2
+
3
+ export const memoryVolatilities = ["stable", "slow", "volatile"] as const;
4
+
5
+ export type MemoryVolatility = (typeof memoryVolatilities)[number];
6
+
7
+ export type MemoryVolatilityRule = { trustDays: number | null; maxImportance: number };
8
+
9
+ export const volatilityRules: Record<MemoryVolatility, MemoryVolatilityRule> = {
10
+ stable: { trustDays: null, maxImportance: 5 },
11
+ slow: { trustDays: 90, maxImportance: 3 },
12
+ volatile: { trustDays: 7, maxImportance: 2 },
13
+ };
14
+
15
+ export const defaultMemoryVolatility: MemoryVolatility = "slow";
16
+
17
+ export function isMemoryVolatility(value: unknown): value is MemoryVolatility {
18
+ return typeof value === "string" && memoryVolatilities.includes(value as MemoryVolatility);
19
+ }
20
+
21
+ export function normalizeVolatility(value: unknown): MemoryVolatility {
22
+ return isMemoryVolatility(value) ? value : defaultMemoryVolatility;
23
+ }
24
+
25
+ export function volatilityTrustDays(volatility: unknown): number | null {
26
+ return volatilityRules[normalizeVolatility(volatility)].trustDays;
27
+ }
28
+
29
+ export function maxImportanceForVolatility(volatility: unknown): number {
30
+ return volatilityRules[normalizeVolatility(volatility)].maxImportance;
31
+ }
32
+
33
+ export const volatilityInferenceSlack = 1.5;
34
+
35
+ export function inferVolatility(
36
+ memoryTrustedUntil: Date | string | null | undefined,
37
+ since: Date | string | null | undefined,
38
+ ): MemoryVolatility {
39
+ if (memoryTrustedUntil == null) return "stable";
40
+
41
+ const horizon = parseMemoryDate(memoryTrustedUntil);
42
+ const from = parseMemoryDate(since);
43
+ if (horizon == null || from == null) return defaultMemoryVolatility;
44
+
45
+ const spanDays = (horizon.getTime() - from.getTime()) / msPerDay;
46
+ const volatileDays = volatilityRules.volatile.trustDays ?? 0;
47
+ return spanDays <= volatileDays * volatilityInferenceSlack ? "volatile" : defaultMemoryVolatility;
48
+ }
49
+
50
+ export type VolatilityTrace = {
51
+ memoryTrustedUntil?: Date | string | null;
52
+ memoryValidUntil?: Date | string | null;
53
+ extractedAt?: Date | string | null;
54
+ };
55
+
56
+ export function isDeadlineDerivedHorizon(
57
+ memoryTrustedUntil: Date | string | null | undefined,
58
+ memoryValidUntil: Date | string | null | undefined,
59
+ ): boolean {
60
+ const horizon = parseMemoryDate(memoryTrustedUntil);
61
+ const deadline = parseMemoryDate(memoryValidUntil);
62
+ return horizon != null && deadline != null && horizon.getTime() === deadline.getTime();
63
+ }
64
+
65
+ export function importanceCeilingFor(memory: VolatilityTrace): number {
66
+ return isDeadlineDerivedHorizon(memory.memoryTrustedUntil, memory.memoryValidUntil)
67
+ ? maxImportanceForVolatility("stable")
68
+ : maxImportanceForVolatility(inferVolatility(memory.memoryTrustedUntil, memory.extractedAt));
69
+ }
70
+
71
+ export function capHorizonByDeadline(
72
+ horizon: string | null,
73
+ memoryValidUntil: string | null,
74
+ now: Date = new Date(),
75
+ ): string | null {
76
+ const deadline = memoryValidUntil != null && new Date(memoryValidUntil) > now ? memoryValidUntil : null;
77
+ if (deadline == null) return horizon;
78
+ if (horizon == null) return deadline;
79
+ return new Date(deadline) < new Date(horizon) ? deadline : horizon;
80
+ }
81
+
82
+ export function resolveTrustedUntil(
83
+ volatility: unknown,
84
+ memoryValidUntil: string | null,
85
+ now: Date = new Date(),
86
+ ): string | null {
87
+ const days = volatilityTrustDays(volatility);
88
+ const fromVolatility = days != null ? new Date(now.getTime() + days * msPerDay).toISOString() : null;
89
+ return capHorizonByDeadline(fromVolatility, memoryValidUntil, now);
90
+ }
@@ -0,0 +1,37 @@
1
+ import { isPresent } from "@manta-eu/lib/present";
2
+ import type { FileUIPart } from "ai";
3
+ import { v7 as uuidv7 } from "uuid";
4
+ import type { OperatorAllowedModel } from "./model";
5
+ import type { PageContext } from "./page-context";
6
+ import type { ChatUIMessage } from "./index";
7
+
8
+ export type CreateUserMessageArgs = {
9
+ text: string;
10
+ fileParts?: FileUIPart[];
11
+ pageContext?: PageContext;
12
+ model?: OperatorAllowedModel;
13
+ };
14
+
15
+ export function createUserMessage(args: CreateUserMessageArgs): ChatUIMessage {
16
+ const { text, fileParts, pageContext, model } = args;
17
+ const parts: ChatUIMessage["parts"] = [];
18
+
19
+ if (isPresent(fileParts)) {
20
+ parts.push(...fileParts);
21
+ }
22
+
23
+ if (text) {
24
+ parts.push({ type: "text", text });
25
+ }
26
+
27
+ return {
28
+ id: uuidv7(),
29
+ role: "user",
30
+ parts,
31
+ metadata: {
32
+ createdAt: new Date().toISOString(),
33
+ ...(pageContext && { pageContext }),
34
+ ...(model && { model }),
35
+ },
36
+ };
37
+ }
@@ -0,0 +1,117 @@
1
+ import type { LanguageModel } from "ai";
2
+ import { z } from "zod";
3
+
4
+ export const opus_5_5 = "claude-opus-5-5" satisfies LanguageModel;
5
+ export const sonnet_4_5 = "claude-sonnet-4-5-20250929" satisfies LanguageModel;
6
+ export const haiku_4_5 = "claude-haiku-4-5-20251001" satisfies LanguageModel;
7
+ export const vllmPrimaryModel = "vllm-primary" satisfies LanguageModel;
8
+ export const vertexClaudeModel = "vertex-claude" satisfies LanguageModel;
9
+
10
+ export const anthropicPrimaryModel = opus_5_5;
11
+
12
+ export const anthropicSupportedMimeTypes: readonly string[] = [
13
+ "image/png",
14
+ "image/jpeg",
15
+ "image/gif",
16
+ "image/webp",
17
+ "application/pdf",
18
+ ];
19
+
20
+ export const vllmSupportedMimeTypes: readonly string[] = ["image/png", "image/jpeg", "image/gif", "image/webp"];
21
+
22
+ export const modelOptions = [
23
+ {
24
+ id: opus_5_5,
25
+ label: "Claude Opus",
26
+ provider: "anthropic",
27
+ contextWindow: 200_000,
28
+ isOperatorAllowed: true,
29
+ supportedMimeTypes: anthropicSupportedMimeTypes,
30
+ supportsPromptCaching: true,
31
+ },
32
+ {
33
+ id: sonnet_4_5,
34
+ label: "Claude Sonnet",
35
+ provider: "anthropic",
36
+ contextWindow: 200_000,
37
+ isOperatorAllowed: true,
38
+ supportedMimeTypes: anthropicSupportedMimeTypes,
39
+ supportsPromptCaching: true,
40
+ },
41
+ {
42
+ id: haiku_4_5,
43
+ label: "Claude Haiku",
44
+ provider: "anthropic",
45
+ contextWindow: 200_000,
46
+ isOperatorAllowed: false,
47
+ supportedMimeTypes: anthropicSupportedMimeTypes,
48
+ supportsPromptCaching: true,
49
+ },
50
+ {
51
+ id: vllmPrimaryModel,
52
+ label: "Manta LLM",
53
+ provider: "vllm",
54
+ contextWindow: 262_144,
55
+ isOperatorAllowed: true,
56
+ supportedMimeTypes: vllmSupportedMimeTypes,
57
+ supportsPromptCaching: false,
58
+ },
59
+ {
60
+ id: vertexClaudeModel,
61
+ label: "Claude (Vertex AI)",
62
+ provider: "google-vertex-anthropic",
63
+ // A floor, not the exact window: the model behind this id is whatever GOOGLE_VERTEX_MODEL
64
+ // names, and which Claude generations a project can reach depends on its pinned region.
65
+ contextWindow: 200_000,
66
+ isOperatorAllowed: true,
67
+ supportedMimeTypes: anthropicSupportedMimeTypes,
68
+ supportsPromptCaching: true,
69
+ },
70
+ ] as const;
71
+
72
+ export type MantaModelId = (typeof modelOptions)[number]["id"];
73
+
74
+ const modelById = new Map<MantaModelId, (typeof modelOptions)[number]>(modelOptions.map((m) => [m.id, m]));
75
+
76
+ export const getModelMetaById = (modelId: MantaModelId) => {
77
+ const meta = modelById.get(modelId);
78
+
79
+ if (!meta) {
80
+ throw new Error(`Unknown model ID: ${modelId}`);
81
+ }
82
+
83
+ return meta;
84
+ };
85
+
86
+ export const getSupportedMimeTypes = (modelId: MantaModelId) => {
87
+ return getModelMetaById(modelId).supportedMimeTypes;
88
+ };
89
+
90
+ export function requiresSingleSystemMessage(modelId: MantaModelId): boolean {
91
+ return !getModelMetaById(modelId).supportsPromptCaching;
92
+ }
93
+
94
+ const validModelSchema = z.enum(modelOptions.map((m) => m.id));
95
+ export const isValidModelId = (value: unknown): value is MantaModelId => {
96
+ return validModelSchema.safeParse(value).success;
97
+ };
98
+
99
+ export const operatorModelOptions = modelOptions.filter((m) => m.isOperatorAllowed);
100
+
101
+ export type OperatorAllowedModel = (typeof operatorModelOptions)[number]["id"];
102
+
103
+ export function isOperatorAllowedModel(value: unknown): value is OperatorAllowedModel {
104
+ return isValidModelId(value) && getModelMetaById(value).isOperatorAllowed;
105
+ }
106
+
107
+ export type AllowedModels = readonly [MantaModelId, ...MantaModelId[]];
108
+
109
+ export function getMinimumContextWindow(allowedModels: AllowedModels): number {
110
+ return Math.min(...allowedModels.map((modelId) => getModelMetaById(modelId).contextWindow));
111
+ }
112
+
113
+ export function operatorModelOptionsFor(allowedModels: readonly MantaModelId[]) {
114
+ const allowed = new Set<MantaModelId>(allowedModels);
115
+
116
+ return operatorModelOptions.filter((model) => allowed.has(model.id));
117
+ }
@@ -0,0 +1,21 @@
1
+ import type { Capability } from "./permissions";
2
+
3
+ export type OrgRoleDefinition = {
4
+ readonly id: string;
5
+ readonly groupName: string;
6
+ readonly label: string;
7
+ readonly capabilities: readonly Capability[];
8
+ };
9
+
10
+ export const orgAdminRole: OrgRoleDefinition = {
11
+ id: "admin",
12
+ groupName: "Admins",
13
+ label: "Admin",
14
+ capabilities: ["team:manage", "org:settings", "org:integrations", "org:streams", "org:memories"],
15
+ };
16
+
17
+ export const plainMemberRole = "user";
18
+
19
+ export function findOrgRole(roles: readonly OrgRoleDefinition[], id: string): OrgRoleDefinition | undefined {
20
+ return roles.find((role) => role.id === id);
21
+ }
@@ -0,0 +1,7 @@
1
+ import type { IRI } from "@manta-eu/rdf-lib/namespaces";
2
+
3
+ /** The entities a screen is showing, so the agent can answer about what the user is looking at. */
4
+ export type PageContext = {
5
+ iris: IRI[];
6
+ typenames: string[];
7
+ };
@@ -0,0 +1,19 @@
1
+ import { orgAdminRole } from "./org-roles";
2
+
3
+ export const capabilities = ["team:manage", "org:settings", "org:integrations", "org:streams", "org:memories"] as const;
4
+
5
+ export type Capability = (typeof capabilities)[number];
6
+
7
+ export function isCapability(value: string): value is Capability {
8
+ return capabilities.some((candidate) => candidate === value);
9
+ }
10
+
11
+ export type CapabilityPrincipal = {
12
+ isOperator: boolean;
13
+ isOrgAdmin: boolean;
14
+ };
15
+
16
+ export function hasCapability(principal: CapabilityPrincipal, capability: Capability): boolean {
17
+ if (principal.isOperator) return true;
18
+ return principal.isOrgAdmin && orgAdminRole.capabilities.includes(capability);
19
+ }