lumiverse-spindle-types 0.5.0 → 0.5.2
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/package.json +1 -1
- package/src/api.ts +157 -1
- package/src/index.ts +58 -0
- package/src/memories.ts +607 -0
- package/src/permissions.ts +11 -1
- package/src/spindle-api.ts +283 -0
package/package.json
CHANGED
package/src/api.ts
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
import type { SpindleManifest } from "./manifest";
|
|
2
2
|
import type { CouncilMemberContext } from "./council";
|
|
3
|
+
import type {
|
|
4
|
+
ChatLinkAttachDTO,
|
|
5
|
+
CortexQueryDTO,
|
|
6
|
+
MemoryCortexConfigDTO,
|
|
7
|
+
MemoryEntityStatusUpdateDTO,
|
|
8
|
+
MemoryEntityUpsertDTO,
|
|
9
|
+
MemoryRelationUpsertDTO,
|
|
10
|
+
VaultCreateDTO,
|
|
11
|
+
} from "./memories";
|
|
3
12
|
|
|
4
13
|
// ─── DTO types for messages ──────────────────────────────────────────────
|
|
5
14
|
|
|
@@ -1257,6 +1266,99 @@ export interface PermissionChangedDetail {
|
|
|
1257
1266
|
allGranted: string[];
|
|
1258
1267
|
}
|
|
1259
1268
|
|
|
1269
|
+
// ─── Web Search DTOs ────────────────────────────────────────────────────
|
|
1270
|
+
|
|
1271
|
+
/** Identifies which provider backs the user's configured web search engine. */
|
|
1272
|
+
export type WebSearchProviderDTO = "searxng";
|
|
1273
|
+
|
|
1274
|
+
/**
|
|
1275
|
+
* Safe view of a user's web search configuration. The raw API key is never
|
|
1276
|
+
* exposed — only `hasApiKey` indicates whether one is on file.
|
|
1277
|
+
*/
|
|
1278
|
+
export interface WebSearchSettingsDTO {
|
|
1279
|
+
enabled: boolean;
|
|
1280
|
+
provider: WebSearchProviderDTO;
|
|
1281
|
+
apiUrl: string;
|
|
1282
|
+
requestTimeoutMs: number;
|
|
1283
|
+
defaultResultCount: number;
|
|
1284
|
+
maxResultCount: number;
|
|
1285
|
+
maxPagesToScrape: number;
|
|
1286
|
+
maxCharsPerPage: number;
|
|
1287
|
+
language: string;
|
|
1288
|
+
safeSearch: 0 | 1 | 2;
|
|
1289
|
+
engines: string[];
|
|
1290
|
+
hasApiKey: boolean;
|
|
1291
|
+
}
|
|
1292
|
+
|
|
1293
|
+
/** A single search result row, normalized across providers. */
|
|
1294
|
+
export interface WebSearchResultDTO {
|
|
1295
|
+
title: string;
|
|
1296
|
+
url: string;
|
|
1297
|
+
snippet: string;
|
|
1298
|
+
/** Provider-reported engine identifier when available (e.g. `"google"`, `"bing"`). */
|
|
1299
|
+
engine?: string;
|
|
1300
|
+
/** Provider-reported relevance score when available. */
|
|
1301
|
+
score?: number;
|
|
1302
|
+
}
|
|
1303
|
+
|
|
1304
|
+
/**
|
|
1305
|
+
* A search result enriched with scraped page content. Only present when the
|
|
1306
|
+
* query was run with `scrape: true` (the default).
|
|
1307
|
+
*/
|
|
1308
|
+
export interface WebSearchDocumentDTO {
|
|
1309
|
+
title: string;
|
|
1310
|
+
url: string;
|
|
1311
|
+
snippet: string;
|
|
1312
|
+
/** How the page content was extracted (e.g. `"html"`, `"pdf"`). */
|
|
1313
|
+
sourceType?: string;
|
|
1314
|
+
/** Extracted page text, clipped to `WebSearchSettingsDTO.maxCharsPerPage`. */
|
|
1315
|
+
content?: string;
|
|
1316
|
+
/** Length of the source page content before clipping. */
|
|
1317
|
+
contentLength?: number;
|
|
1318
|
+
/** Populated when scraping failed for this result; `content` is then absent. */
|
|
1319
|
+
error?: string;
|
|
1320
|
+
}
|
|
1321
|
+
|
|
1322
|
+
/** Options forwarded to `spindle.webSearch.query()`. */
|
|
1323
|
+
export interface WebSearchRequestDTO {
|
|
1324
|
+
/** Free-text search query. Trimmed by the host; empty values are rejected. */
|
|
1325
|
+
query: string;
|
|
1326
|
+
/**
|
|
1327
|
+
* Desired number of results. Clamped to `WebSearchSettingsDTO.maxResultCount`
|
|
1328
|
+
* on the host; omit to use the user's `defaultResultCount`.
|
|
1329
|
+
*/
|
|
1330
|
+
count?: number;
|
|
1331
|
+
/**
|
|
1332
|
+
* When `true` (default), the host scrapes the first
|
|
1333
|
+
* `WebSearchSettingsDTO.maxPagesToScrape` results, fills in
|
|
1334
|
+
* `documents[].content`, and assembles the `context` block. Set to `false`
|
|
1335
|
+
* to skip scraping entirely — only `results` are returned, and
|
|
1336
|
+
* `documents` / `context` are omitted from the response. Useful when the
|
|
1337
|
+
* extension only needs titles, URLs, and snippets.
|
|
1338
|
+
*/
|
|
1339
|
+
scrape?: boolean;
|
|
1340
|
+
/** For operator-scoped extensions; ignored on user-scoped extensions. */
|
|
1341
|
+
userId?: string;
|
|
1342
|
+
}
|
|
1343
|
+
|
|
1344
|
+
/**
|
|
1345
|
+
* Result of a successful `spindle.webSearch.query()` call. `documents` and
|
|
1346
|
+
* `context` are omitted when the request was issued with `scrape: false`.
|
|
1347
|
+
*/
|
|
1348
|
+
export interface WebSearchResponseDTO {
|
|
1349
|
+
/** The (trimmed) query that was executed. */
|
|
1350
|
+
query: string;
|
|
1351
|
+
/** Raw normalized results from the search provider. */
|
|
1352
|
+
results: WebSearchResultDTO[];
|
|
1353
|
+
/** Per-result scraped page content. Absent when `scrape: false`. */
|
|
1354
|
+
documents?: WebSearchDocumentDTO[];
|
|
1355
|
+
/**
|
|
1356
|
+
* Pre-assembled, prompt-ready context block summarizing the query plus the
|
|
1357
|
+
* scraped documents. Absent when `scrape: false`.
|
|
1358
|
+
*/
|
|
1359
|
+
context?: string;
|
|
1360
|
+
}
|
|
1361
|
+
|
|
1260
1362
|
// ─── Theme DTOs ──────────────────────────────────────────────────────────
|
|
1261
1363
|
|
|
1262
1364
|
/**
|
|
@@ -2281,6 +2383,50 @@ export type WorkerToHost =
|
|
|
2281
2383
|
| { type: "generate_dry_run"; requestId: string; input: DryRunRequestDTO; userId?: string }
|
|
2282
2384
|
// ─── Chat Memories (gated: "chats") ───────────────────────────────
|
|
2283
2385
|
| { type: "chats_get_memories"; requestId: string; chatId: string; topK?: number; userId?: string }
|
|
2386
|
+
// ─── Memory Cortex & Long-Term Chat Memory (gated: "memories") ────
|
|
2387
|
+
| { type: "memories_config_get"; requestId: string; userId?: string }
|
|
2388
|
+
| { type: "memories_config_put"; requestId: string; patch: Partial<MemoryCortexConfigDTO>; userId?: string }
|
|
2389
|
+
| { type: "memories_query_cortex"; requestId: string; query: CortexQueryDTO }
|
|
2390
|
+
| { type: "memories_query_linked"; requestId: string; chatId: string; queryText?: string; userId?: string }
|
|
2391
|
+
| { type: "memories_get_cached"; requestId: string; chatId: string }
|
|
2392
|
+
| { type: "memories_get_cached_linked"; requestId: string; chatId: string }
|
|
2393
|
+
| { type: "memories_invalidate_cache"; requestId: string; chatId: string }
|
|
2394
|
+
| { type: "memories_invalidate_linked_cache"; requestId: string; chatId: string }
|
|
2395
|
+
| { type: "memories_entities_list"; requestId: string; chatId: string; activeOnly?: boolean; limit?: number; userId?: string }
|
|
2396
|
+
| { type: "memories_entities_get"; requestId: string; entityId: string; userId?: string }
|
|
2397
|
+
| { type: "memories_entities_find_by_name"; requestId: string; chatId: string; name: string; userId?: string }
|
|
2398
|
+
| { type: "memories_entities_upsert"; requestId: string; chatId: string; entity: MemoryEntityUpsertDTO; chunkId?: string | null; createdAt?: number; userId?: string }
|
|
2399
|
+
| { type: "memories_entities_update_status"; requestId: string; entityId: string; patch: MemoryEntityStatusUpdateDTO; userId?: string }
|
|
2400
|
+
| { type: "memories_entities_add_facts"; requestId: string; entityId: string; facts: string[]; userId?: string }
|
|
2401
|
+
| { type: "memories_entities_get_facts"; requestId: string; entityId: string; userId?: string }
|
|
2402
|
+
| { type: "memories_entities_update_emotional_valence"; requestId: string; entityId: string; valence: Record<string, number>; userId?: string }
|
|
2403
|
+
| { type: "memories_relations_list"; requestId: string; chatId: string; userId?: string }
|
|
2404
|
+
| { type: "memories_relations_list_all"; requestId: string; chatId: string; userId?: string }
|
|
2405
|
+
| { type: "memories_relations_for_entity"; requestId: string; chatId: string; entityId: string; userId?: string }
|
|
2406
|
+
| { type: "memories_relations_for_entities"; requestId: string; chatId: string; entityIds: string[]; limit?: number; userId?: string }
|
|
2407
|
+
| { type: "memories_relations_upsert"; requestId: string; chatId: string; relation: MemoryRelationUpsertDTO; chunkId?: string | null; userId?: string }
|
|
2408
|
+
| { type: "memories_consolidations_list"; requestId: string; chatId: string; tier?: number; userId?: string }
|
|
2409
|
+
| { type: "memories_consolidations_latest_arc"; requestId: string; chatId: string; userId?: string }
|
|
2410
|
+
| { type: "memories_consolidations_run"; requestId: string; chatId: string; userId?: string }
|
|
2411
|
+
| { type: "memories_salience_get"; requestId: string; chatId: string; limit?: number; offset?: number; userId?: string }
|
|
2412
|
+
| { type: "memories_vaults_list"; requestId: string; userId?: string }
|
|
2413
|
+
| { type: "memories_vaults_get"; requestId: string; vaultId: string; userId?: string }
|
|
2414
|
+
| { type: "memories_vaults_get_chunks"; requestId: string; vaultId: string; userId?: string }
|
|
2415
|
+
| { type: "memories_vaults_create"; requestId: string; input: VaultCreateDTO; userId?: string }
|
|
2416
|
+
| { type: "memories_vaults_rename"; requestId: string; vaultId: string; name: string; userId?: string }
|
|
2417
|
+
| { type: "memories_vaults_delete"; requestId: string; vaultId: string; userId?: string }
|
|
2418
|
+
| { type: "memories_vaults_reindex"; requestId: string; vaultId: string; userId?: string }
|
|
2419
|
+
| { type: "memories_links_list"; requestId: string; chatId: string; userId?: string }
|
|
2420
|
+
| { type: "memories_links_attach"; requestId: string; input: ChatLinkAttachDTO; userId?: string }
|
|
2421
|
+
| { type: "memories_links_remove"; requestId: string; chatId: string; linkId: string; userId?: string }
|
|
2422
|
+
| { type: "memories_links_toggle"; requestId: string; chatId: string; linkId: string; enabled: boolean; userId?: string }
|
|
2423
|
+
| { type: "memories_chat_chunks_list"; requestId: string; chatId: string; userId?: string }
|
|
2424
|
+
| { type: "memories_chat_memory_get"; requestId: string; chatId: string; topK?: number; userId?: string }
|
|
2425
|
+
| { type: "memories_chat_memory_warm"; requestId: string; chatId: string; force?: boolean; userId?: string }
|
|
2426
|
+
| { type: "memories_chat_memory_invalidate"; requestId: string; chatId: string; userId?: string }
|
|
2427
|
+
| { type: "memories_stats_usage"; requestId: string; chatId: string; userId?: string }
|
|
2428
|
+
| { type: "memories_stats_ingestion_status"; requestId: string; chatId: string; userId?: string }
|
|
2429
|
+
| { type: "memories_stats_ingestion_telemetry"; requestId: string; chatId: string; userId?: string }
|
|
2284
2430
|
// ─── Toast (free tier) ───────────────────────────────────────────────
|
|
2285
2431
|
| { type: "toast_show"; toastType: "success" | "warning" | "error" | "info"; message: string; title?: string; duration?: number; userId?: string }
|
|
2286
2432
|
// ─── Push Notifications (gated: "push_notification") ────────────────
|
|
@@ -2395,7 +2541,17 @@ export type WorkerToHost =
|
|
|
2395
2541
|
model?: string;
|
|
2396
2542
|
modelSource?: TokenModelSourceDTO;
|
|
2397
2543
|
userId?: string;
|
|
2398
|
-
}
|
|
2544
|
+
}
|
|
2545
|
+
// ─── Web Search (gated: "web_search") ─────────────────────────────────
|
|
2546
|
+
| {
|
|
2547
|
+
type: "web_search_query";
|
|
2548
|
+
requestId: string;
|
|
2549
|
+
query: string;
|
|
2550
|
+
count?: number;
|
|
2551
|
+
scrape?: boolean;
|
|
2552
|
+
userId?: string;
|
|
2553
|
+
}
|
|
2554
|
+
| { type: "web_search_get_settings"; requestId: string; userId?: string };
|
|
2399
2555
|
|
|
2400
2556
|
// ─── Host → Worker messages ──────────────────────────────────────────────
|
|
2401
2557
|
|
package/src/index.ts
CHANGED
|
@@ -155,6 +155,12 @@ export type {
|
|
|
155
155
|
MessageContentProcessorOrigin,
|
|
156
156
|
MessageContentProcessorCtxDTO,
|
|
157
157
|
MessageContentProcessorResultDTO,
|
|
158
|
+
WebSearchProviderDTO,
|
|
159
|
+
WebSearchSettingsDTO,
|
|
160
|
+
WebSearchResultDTO,
|
|
161
|
+
WebSearchDocumentDTO,
|
|
162
|
+
WebSearchRequestDTO,
|
|
163
|
+
WebSearchResponseDTO,
|
|
158
164
|
WorkerToHost,
|
|
159
165
|
HostToWorker,
|
|
160
166
|
} from "./api";
|
|
@@ -210,6 +216,58 @@ export type {
|
|
|
210
216
|
|
|
211
217
|
export type { ExtensionInfo } from "./extension-info";
|
|
212
218
|
|
|
219
|
+
export type {
|
|
220
|
+
EntityTypeDTO,
|
|
221
|
+
EntityStatusDTO,
|
|
222
|
+
MentionRoleDTO,
|
|
223
|
+
RelationTypeDTO,
|
|
224
|
+
RelationStatusDTO,
|
|
225
|
+
EmotionalTagDTO,
|
|
226
|
+
NarrativeFlagDTO,
|
|
227
|
+
ContradictionFlagDTO,
|
|
228
|
+
FactExtractionStatusDTO,
|
|
229
|
+
EntityConfidenceDTO,
|
|
230
|
+
SalienceSourceDTO,
|
|
231
|
+
ChatLinkTypeDTO,
|
|
232
|
+
SalienceBreakdownDTO,
|
|
233
|
+
MemoryEntityDTO,
|
|
234
|
+
MemoryEntityUpsertDTO,
|
|
235
|
+
MemoryEntityStatusUpdateDTO,
|
|
236
|
+
MemoryMentionDTO,
|
|
237
|
+
MemoryRelationDTO,
|
|
238
|
+
MemoryRelationUpsertDTO,
|
|
239
|
+
StatusChangeDTO,
|
|
240
|
+
MemorySalienceDTO,
|
|
241
|
+
MemoryConsolidationDTO,
|
|
242
|
+
CortexQueryDTO,
|
|
243
|
+
CortexMemoryComponentsDTO,
|
|
244
|
+
CortexMemoryDTO,
|
|
245
|
+
EntitySnapshotRelationshipDTO,
|
|
246
|
+
EntitySnapshotDTO,
|
|
247
|
+
RelationEdgeDTO,
|
|
248
|
+
CortexStatsDTO,
|
|
249
|
+
CortexResultDTO,
|
|
250
|
+
VaultDTO,
|
|
251
|
+
VaultChunkDTO,
|
|
252
|
+
VaultEntityDTO,
|
|
253
|
+
VaultRelationDTO,
|
|
254
|
+
VaultCreateDTO,
|
|
255
|
+
VaultWithContentsDTO,
|
|
256
|
+
VaultReindexResultDTO,
|
|
257
|
+
ChatLinkDTO,
|
|
258
|
+
ChatLinkAttachDTO,
|
|
259
|
+
VaultCortexDataDTO,
|
|
260
|
+
InterlinkCortexDataDTO,
|
|
261
|
+
LinkedCortexResultDTO,
|
|
262
|
+
MemoryCortexConfigDTO,
|
|
263
|
+
CortexUsageStatsDTO,
|
|
264
|
+
CortexIngestionTimingsDTO,
|
|
265
|
+
CortexIngestionStatusDTO,
|
|
266
|
+
CortexIngestionTelemetryDTO,
|
|
267
|
+
ChatChunkDTO,
|
|
268
|
+
ChatMemoryWarmupResultDTO,
|
|
269
|
+
} from "./memories";
|
|
270
|
+
|
|
213
271
|
export type {
|
|
214
272
|
SpindleAPI,
|
|
215
273
|
FrontendProcessHandle,
|
package/src/memories.ts
ADDED
|
@@ -0,0 +1,607 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Memory Cortex & Long-Term Chat Memory DTOs.
|
|
3
|
+
*
|
|
4
|
+
* Surfaces the Lumiverse hybrid memory architecture to extensions via the
|
|
5
|
+
* `memories` permission. Covers:
|
|
6
|
+
*
|
|
7
|
+
* - Memory Cortex
|
|
8
|
+
* • config (entity tracking, salience scoring, retrieval tuning)
|
|
9
|
+
* • retrieval (cortex query, linked-cortex query, vault query)
|
|
10
|
+
* • entity graph (entities, relations, mentions)
|
|
11
|
+
* • consolidations & salience records
|
|
12
|
+
* • vaults (snapshots of cortex state) and chat links (vault attach /
|
|
13
|
+
* bidirectional interlinks)
|
|
14
|
+
* • ingestion / maintenance telemetry
|
|
15
|
+
*
|
|
16
|
+
* - Long-Term Chat Memory
|
|
17
|
+
* • vectorized chat chunks (list, get, warm)
|
|
18
|
+
* • cached retrieval result for the {{memories}} macro
|
|
19
|
+
*
|
|
20
|
+
* Service-layer shapes use camelCase; SQLite row shapes (snake_case) are not
|
|
21
|
+
* exposed to extensions — DTOs are normalised here.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
// ─── Enums ─────────────────────────────────────────────────────────────
|
|
25
|
+
|
|
26
|
+
export type EntityTypeDTO =
|
|
27
|
+
| "character"
|
|
28
|
+
| "location"
|
|
29
|
+
| "item"
|
|
30
|
+
| "faction"
|
|
31
|
+
| "concept"
|
|
32
|
+
| "event";
|
|
33
|
+
|
|
34
|
+
export type EntityStatusDTO =
|
|
35
|
+
| "active"
|
|
36
|
+
| "inactive"
|
|
37
|
+
| "deceased"
|
|
38
|
+
| "destroyed"
|
|
39
|
+
| "unknown";
|
|
40
|
+
|
|
41
|
+
export type MentionRoleDTO =
|
|
42
|
+
| "subject"
|
|
43
|
+
| "object"
|
|
44
|
+
| "present"
|
|
45
|
+
| "referenced"
|
|
46
|
+
| "absent";
|
|
47
|
+
|
|
48
|
+
export type RelationTypeDTO =
|
|
49
|
+
| "ally"
|
|
50
|
+
| "enemy"
|
|
51
|
+
| "lover"
|
|
52
|
+
| "parent"
|
|
53
|
+
| "child"
|
|
54
|
+
| "sibling"
|
|
55
|
+
| "mentor"
|
|
56
|
+
| "rival"
|
|
57
|
+
| "owns"
|
|
58
|
+
| "member_of"
|
|
59
|
+
| "located_in"
|
|
60
|
+
| "fears"
|
|
61
|
+
| "serves"
|
|
62
|
+
| "custom";
|
|
63
|
+
|
|
64
|
+
export type RelationStatusDTO = "active" | "broken" | "dormant" | "former";
|
|
65
|
+
|
|
66
|
+
export type EmotionalTagDTO =
|
|
67
|
+
| "grief"
|
|
68
|
+
| "joy"
|
|
69
|
+
| "tension"
|
|
70
|
+
| "dread"
|
|
71
|
+
| "intimacy"
|
|
72
|
+
| "betrayal"
|
|
73
|
+
| "revelation"
|
|
74
|
+
| "resolve"
|
|
75
|
+
| "humor"
|
|
76
|
+
| "melancholy"
|
|
77
|
+
| "awe"
|
|
78
|
+
| "fury";
|
|
79
|
+
|
|
80
|
+
export type NarrativeFlagDTO =
|
|
81
|
+
| "first_meeting"
|
|
82
|
+
| "death"
|
|
83
|
+
| "promise"
|
|
84
|
+
| "confession"
|
|
85
|
+
| "departure"
|
|
86
|
+
| "transformation"
|
|
87
|
+
| "battle"
|
|
88
|
+
| "discovery"
|
|
89
|
+
| "reunion"
|
|
90
|
+
| "loss";
|
|
91
|
+
|
|
92
|
+
export type ContradictionFlagDTO = "none" | "temporal" | "complex" | "suspect";
|
|
93
|
+
|
|
94
|
+
export type FactExtractionStatusDTO = "never" | "attempted_empty" | "ok";
|
|
95
|
+
|
|
96
|
+
export type EntityConfidenceDTO = "confirmed" | "provisional";
|
|
97
|
+
|
|
98
|
+
export type SalienceSourceDTO = "heuristic" | "sidecar";
|
|
99
|
+
|
|
100
|
+
export type ChatLinkTypeDTO = "vault" | "interlink";
|
|
101
|
+
|
|
102
|
+
// ─── Entity Graph ──────────────────────────────────────────────────────
|
|
103
|
+
|
|
104
|
+
export interface SalienceBreakdownDTO {
|
|
105
|
+
mentionComponent: number;
|
|
106
|
+
arcComponent: number;
|
|
107
|
+
graphComponent: number;
|
|
108
|
+
total: number;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export interface MemoryEntityDTO {
|
|
112
|
+
id: string;
|
|
113
|
+
chatId: string;
|
|
114
|
+
name: string;
|
|
115
|
+
entityType: EntityTypeDTO;
|
|
116
|
+
aliases: string[];
|
|
117
|
+
description: string;
|
|
118
|
+
firstSeenChunkId: string | null;
|
|
119
|
+
lastSeenChunkId: string | null;
|
|
120
|
+
firstSeenAt: number | null;
|
|
121
|
+
lastSeenAt: number | null;
|
|
122
|
+
mentionCount: number;
|
|
123
|
+
salienceAvg: number;
|
|
124
|
+
status: EntityStatusDTO;
|
|
125
|
+
statusChangedAt: number | null;
|
|
126
|
+
facts: string[];
|
|
127
|
+
emotionalValence: Record<string, number>;
|
|
128
|
+
metadata: Record<string, unknown>;
|
|
129
|
+
createdAt: number;
|
|
130
|
+
updatedAt: number;
|
|
131
|
+
factExtractionStatus: FactExtractionStatusDTO;
|
|
132
|
+
factExtractionLastAttempt: number | null;
|
|
133
|
+
salienceBreakdown: SalienceBreakdownDTO;
|
|
134
|
+
lastMentionTimestamp: number | null;
|
|
135
|
+
recentMentionCount: number;
|
|
136
|
+
confidence: EntityConfidenceDTO;
|
|
137
|
+
userEditedAt: number | null;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Input shape for upserting an entity. Matches the heuristic / sidecar
|
|
142
|
+
* extraction shape so an extension can replay its own NER results into the
|
|
143
|
+
* graph. `confidence` is the raw [0, 1] extractor confidence; entities below
|
|
144
|
+
* the configured threshold are dropped by the host.
|
|
145
|
+
*/
|
|
146
|
+
export interface MemoryEntityUpsertDTO {
|
|
147
|
+
name: string;
|
|
148
|
+
type: EntityTypeDTO;
|
|
149
|
+
aliases?: string[];
|
|
150
|
+
confidence?: number;
|
|
151
|
+
role?: MentionRoleDTO;
|
|
152
|
+
/** Marks the entity as needing corroboration before promotion. */
|
|
153
|
+
provisional?: boolean;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export interface MemoryEntityStatusUpdateDTO {
|
|
157
|
+
status: EntityStatusDTO;
|
|
158
|
+
statusChangedAt?: number;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export interface MemoryMentionDTO {
|
|
162
|
+
id: string;
|
|
163
|
+
entityId: string;
|
|
164
|
+
chunkId: string;
|
|
165
|
+
chatId: string;
|
|
166
|
+
role: MentionRoleDTO;
|
|
167
|
+
excerpt: string | null;
|
|
168
|
+
sentiment: number;
|
|
169
|
+
createdAt: number;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// ─── Relations ─────────────────────────────────────────────────────────
|
|
173
|
+
|
|
174
|
+
export interface MemoryRelationDTO {
|
|
175
|
+
id: string;
|
|
176
|
+
chatId: string;
|
|
177
|
+
sourceEntityId: string;
|
|
178
|
+
targetEntityId: string;
|
|
179
|
+
relationType: RelationTypeDTO;
|
|
180
|
+
relationLabel: string | null;
|
|
181
|
+
strength: number;
|
|
182
|
+
sentiment: number;
|
|
183
|
+
evidenceChunkIds: string[];
|
|
184
|
+
firstEstablishedAt: number | null;
|
|
185
|
+
lastReinforcedAt: number | null;
|
|
186
|
+
status: RelationStatusDTO;
|
|
187
|
+
metadata: Record<string, unknown>;
|
|
188
|
+
createdAt: number;
|
|
189
|
+
updatedAt: number;
|
|
190
|
+
contradictionFlag: ContradictionFlagDTO;
|
|
191
|
+
contradictionPeerId: string | null;
|
|
192
|
+
sentimentRange: [number, number] | null;
|
|
193
|
+
supersededBy: string | null;
|
|
194
|
+
arcIds: string[];
|
|
195
|
+
firstSeenArcId: string | null;
|
|
196
|
+
lastSeenArcId: string | null;
|
|
197
|
+
lastEvidenceTimestamp: number | null;
|
|
198
|
+
decayRate: number;
|
|
199
|
+
edgeSalience: number;
|
|
200
|
+
labelAliases: string[];
|
|
201
|
+
canonicalEdgeId: string | null;
|
|
202
|
+
mergedInto: string | null;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Input shape for upserting a relation. Uses entity *names* (not ids) so
|
|
207
|
+
* extensions can produce relations symbolically — the host resolves canonical
|
|
208
|
+
* ids server-side. Both endpoints must already exist in the entity graph;
|
|
209
|
+
* the relation is silently dropped otherwise.
|
|
210
|
+
*/
|
|
211
|
+
export interface MemoryRelationUpsertDTO {
|
|
212
|
+
source: string;
|
|
213
|
+
target: string;
|
|
214
|
+
type: RelationTypeDTO;
|
|
215
|
+
label: string;
|
|
216
|
+
sentiment: number;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// ─── Salience ──────────────────────────────────────────────────────────
|
|
220
|
+
|
|
221
|
+
export interface StatusChangeDTO {
|
|
222
|
+
entity: string;
|
|
223
|
+
change: string;
|
|
224
|
+
detail: string;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
export interface MemorySalienceDTO {
|
|
228
|
+
chunkId: string;
|
|
229
|
+
chatId: string;
|
|
230
|
+
score: number;
|
|
231
|
+
scoreSource: SalienceSourceDTO;
|
|
232
|
+
emotionalTags: EmotionalTagDTO[];
|
|
233
|
+
statusChanges: StatusChangeDTO[];
|
|
234
|
+
narrativeFlags: NarrativeFlagDTO[];
|
|
235
|
+
hasDialogue: boolean;
|
|
236
|
+
hasAction: boolean;
|
|
237
|
+
hasInternalThought: boolean;
|
|
238
|
+
wordCount: number;
|
|
239
|
+
scoredAt: number;
|
|
240
|
+
scoredBy: string | null;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// ─── Consolidations ────────────────────────────────────────────────────
|
|
244
|
+
|
|
245
|
+
export interface MemoryConsolidationDTO {
|
|
246
|
+
id: string;
|
|
247
|
+
chatId: string;
|
|
248
|
+
tier: number;
|
|
249
|
+
title: string | null;
|
|
250
|
+
summary: string;
|
|
251
|
+
sourceChunkIds: string[];
|
|
252
|
+
sourceConsolidationIds: string[];
|
|
253
|
+
entityIds: string[];
|
|
254
|
+
messageRangeStart: number | null;
|
|
255
|
+
messageRangeEnd: number | null;
|
|
256
|
+
timeRangeStart: number | null;
|
|
257
|
+
timeRangeEnd: number | null;
|
|
258
|
+
salienceAvg: number;
|
|
259
|
+
emotionalTags: EmotionalTagDTO[];
|
|
260
|
+
tokenCount: number;
|
|
261
|
+
vectorizedAt: number | null;
|
|
262
|
+
vectorModel: string | null;
|
|
263
|
+
createdAt: number;
|
|
264
|
+
updatedAt: number;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// ─── Cortex Retrieval ──────────────────────────────────────────────────
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Input to a Memory Cortex retrieval query.
|
|
271
|
+
*
|
|
272
|
+
* The `userId` field is normally inferred from the calling extension's
|
|
273
|
+
* effective user; pass it explicitly only for operator-scoped extensions.
|
|
274
|
+
*/
|
|
275
|
+
export interface CortexQueryDTO {
|
|
276
|
+
chatId: string;
|
|
277
|
+
queryText: string;
|
|
278
|
+
entityFilter?: string[];
|
|
279
|
+
timeRange?: { start?: number; end?: number };
|
|
280
|
+
emotionalContext?: EmotionalTagDTO[];
|
|
281
|
+
generationType?: string;
|
|
282
|
+
topK?: number;
|
|
283
|
+
includeConsolidations?: boolean;
|
|
284
|
+
includeRelationships?: boolean;
|
|
285
|
+
excludeMessageIds?: string[];
|
|
286
|
+
userId?: string;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
export interface CortexMemoryComponentsDTO {
|
|
290
|
+
semantic: number;
|
|
291
|
+
salience: number;
|
|
292
|
+
recency: number;
|
|
293
|
+
reinforcement: number;
|
|
294
|
+
emotional: number;
|
|
295
|
+
entity: number;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
export interface CortexMemoryDTO {
|
|
299
|
+
source: "chunk" | "consolidation";
|
|
300
|
+
sourceId: string;
|
|
301
|
+
content: string;
|
|
302
|
+
finalScore: number;
|
|
303
|
+
components: CortexMemoryComponentsDTO;
|
|
304
|
+
emotionalTags: EmotionalTagDTO[];
|
|
305
|
+
entityNames: string[];
|
|
306
|
+
messageRange: [number, number];
|
|
307
|
+
timeRange: [number, number];
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
export interface EntitySnapshotRelationshipDTO {
|
|
311
|
+
targetName: string;
|
|
312
|
+
type: RelationTypeDTO;
|
|
313
|
+
label: string | null;
|
|
314
|
+
strength: number;
|
|
315
|
+
sentiment: number;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
export interface EntitySnapshotDTO {
|
|
319
|
+
id: string;
|
|
320
|
+
name: string;
|
|
321
|
+
type: EntityTypeDTO;
|
|
322
|
+
status: EntityStatusDTO;
|
|
323
|
+
description: string;
|
|
324
|
+
lastSeenAt: number | null;
|
|
325
|
+
mentionCount: number;
|
|
326
|
+
topFacts: string[];
|
|
327
|
+
emotionalProfile: Record<string, number>;
|
|
328
|
+
relationships: EntitySnapshotRelationshipDTO[];
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
export interface RelationEdgeDTO {
|
|
332
|
+
sourceName: string;
|
|
333
|
+
targetName: string;
|
|
334
|
+
type: RelationTypeDTO;
|
|
335
|
+
label: string | null;
|
|
336
|
+
strength: number;
|
|
337
|
+
sentiment: number;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
export interface CortexStatsDTO {
|
|
341
|
+
candidatePoolSize: number;
|
|
342
|
+
vectorSearchResults: number;
|
|
343
|
+
entitiesMatched: number;
|
|
344
|
+
scoreFusionApplied: boolean;
|
|
345
|
+
topScore: number;
|
|
346
|
+
retrievalTimeMs: number;
|
|
347
|
+
/** Set when the retrieval hit its internal time budget. */
|
|
348
|
+
timedOut?: boolean;
|
|
349
|
+
/** Set when retrieval bailed out because the caller's AbortSignal fired. */
|
|
350
|
+
aborted?: boolean;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
export interface CortexResultDTO {
|
|
354
|
+
memories: CortexMemoryDTO[];
|
|
355
|
+
entityContext: EntitySnapshotDTO[];
|
|
356
|
+
activeRelationships: RelationEdgeDTO[];
|
|
357
|
+
arcContext: string | null;
|
|
358
|
+
stats: CortexStatsDTO;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// ─── Vaults & Linked Cortex ────────────────────────────────────────────
|
|
362
|
+
|
|
363
|
+
export interface VaultDTO {
|
|
364
|
+
id: string;
|
|
365
|
+
userId: string;
|
|
366
|
+
sourceChatId: string | null;
|
|
367
|
+
sourceChatName: string | null;
|
|
368
|
+
name: string;
|
|
369
|
+
description: string;
|
|
370
|
+
entityCount: number;
|
|
371
|
+
relationCount: number;
|
|
372
|
+
chunkCount: number;
|
|
373
|
+
createdAt: number;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
export interface VaultChunkDTO {
|
|
377
|
+
id: string;
|
|
378
|
+
vaultId: string;
|
|
379
|
+
sourceChunkId: string;
|
|
380
|
+
content: string;
|
|
381
|
+
salienceScore: number | null;
|
|
382
|
+
emotionalTags: string[];
|
|
383
|
+
entityNames: string[];
|
|
384
|
+
sourceCreatedAt: number;
|
|
385
|
+
copiedAt: number;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
export interface VaultEntityDTO {
|
|
389
|
+
id: string;
|
|
390
|
+
vaultId: string;
|
|
391
|
+
name: string;
|
|
392
|
+
entityType: EntityTypeDTO;
|
|
393
|
+
aliases: string[];
|
|
394
|
+
description: string;
|
|
395
|
+
status: EntityStatusDTO;
|
|
396
|
+
facts: string[];
|
|
397
|
+
emotionalValence: Record<string, number>;
|
|
398
|
+
salienceAvg: number;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
export interface VaultRelationDTO {
|
|
402
|
+
id: string;
|
|
403
|
+
vaultId: string;
|
|
404
|
+
sourceEntityName: string;
|
|
405
|
+
targetEntityName: string;
|
|
406
|
+
relationType: RelationTypeDTO;
|
|
407
|
+
relationLabel: string | null;
|
|
408
|
+
strength: number;
|
|
409
|
+
sentiment: number;
|
|
410
|
+
status: RelationStatusDTO;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
export interface VaultCreateDTO {
|
|
414
|
+
chatId: string;
|
|
415
|
+
name: string;
|
|
416
|
+
description?: string;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
export interface VaultWithContentsDTO {
|
|
420
|
+
vault: VaultDTO;
|
|
421
|
+
entities: VaultEntityDTO[];
|
|
422
|
+
relations: VaultRelationDTO[];
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
export interface VaultReindexResultDTO {
|
|
426
|
+
mode: string;
|
|
427
|
+
chunkCount: number;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
export interface ChatLinkDTO {
|
|
431
|
+
id: string;
|
|
432
|
+
userId: string;
|
|
433
|
+
chatId: string;
|
|
434
|
+
linkType: ChatLinkTypeDTO;
|
|
435
|
+
vaultId: string | null;
|
|
436
|
+
vaultName: string | null;
|
|
437
|
+
vaultEntityCount: number | null;
|
|
438
|
+
vaultRelationCount: number | null;
|
|
439
|
+
targetChatId: string | null;
|
|
440
|
+
targetChatName: string | null;
|
|
441
|
+
targetChatExists: boolean;
|
|
442
|
+
label: string;
|
|
443
|
+
enabled: boolean;
|
|
444
|
+
priority: number;
|
|
445
|
+
createdAt: number;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
export interface ChatLinkAttachDTO {
|
|
449
|
+
chatId: string;
|
|
450
|
+
linkType: ChatLinkTypeDTO;
|
|
451
|
+
vaultId?: string;
|
|
452
|
+
targetChatId?: string;
|
|
453
|
+
label?: string;
|
|
454
|
+
/** Interlinks only. When true, also creates the reverse link on the target chat. */
|
|
455
|
+
bidirectional?: boolean;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
export interface VaultCortexDataDTO {
|
|
459
|
+
vaultId: string;
|
|
460
|
+
vaultName: string;
|
|
461
|
+
sourceChatId?: string;
|
|
462
|
+
entities: EntitySnapshotDTO[];
|
|
463
|
+
relations: RelationEdgeDTO[];
|
|
464
|
+
/** Retrieved memories from the vault's chunk snapshot (when queryText supplied). */
|
|
465
|
+
memories?: CortexMemoryDTO[];
|
|
466
|
+
arcContext?: string | null;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
export interface InterlinkCortexDataDTO {
|
|
470
|
+
targetChatId: string;
|
|
471
|
+
targetChatName: string;
|
|
472
|
+
result: CortexResultDTO;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
export interface LinkedCortexResultDTO {
|
|
476
|
+
vaults: VaultCortexDataDTO[];
|
|
477
|
+
interlinks: InterlinkCortexDataDTO[];
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
// ─── Cortex Config ─────────────────────────────────────────────────────
|
|
481
|
+
|
|
482
|
+
/**
|
|
483
|
+
* Memory Cortex configuration.
|
|
484
|
+
*
|
|
485
|
+
* The host owns the canonical schema (see `MemoryCortexConfig` in the
|
|
486
|
+
* backend). The DTO is intentionally permissive — only the top-level toggles
|
|
487
|
+
* extensions are likely to flip are typed, and the index signature passes
|
|
488
|
+
* advanced fields (retrieval tuning, sidecar configuration, decay curves,
|
|
489
|
+
* consolidation thresholds, entity pruning, etc.) straight through. New
|
|
490
|
+
* fields added by the host are visible immediately to extensions without
|
|
491
|
+
* a coordinated type bump.
|
|
492
|
+
*/
|
|
493
|
+
export interface MemoryCortexConfigDTO {
|
|
494
|
+
enabled: boolean;
|
|
495
|
+
entityTracking: boolean;
|
|
496
|
+
entityExtractionMode: string;
|
|
497
|
+
salienceScoring: boolean;
|
|
498
|
+
salienceScoringMode?: string;
|
|
499
|
+
thoughtMarkers?: unknown;
|
|
500
|
+
entityWhitelist?: string[];
|
|
501
|
+
entityExtractionFilters?: unknown;
|
|
502
|
+
retrieval?: Record<string, unknown>;
|
|
503
|
+
decay?: Record<string, unknown>;
|
|
504
|
+
consolidation?: Record<string, unknown>;
|
|
505
|
+
entityPruning?: Record<string, unknown>;
|
|
506
|
+
sidecar?: Record<string, unknown>;
|
|
507
|
+
sidecarTimeoutMs?: number;
|
|
508
|
+
retrievalTimeoutMs?: number;
|
|
509
|
+
[key: string]: unknown;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
// ─── Telemetry & Stats ─────────────────────────────────────────────────
|
|
513
|
+
|
|
514
|
+
export interface CortexUsageStatsDTO {
|
|
515
|
+
entityCount: number;
|
|
516
|
+
relationCount: number;
|
|
517
|
+
salienceRecordCount: number;
|
|
518
|
+
consolidationCount: number;
|
|
519
|
+
/** Advanced fields exposed by the host's gc module (mention counts, last
|
|
520
|
+
* GC timestamp, etc.) pass through unchanged. */
|
|
521
|
+
[key: string]: number | string | boolean | null | undefined;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
export interface CortexIngestionTimingsDTO {
|
|
525
|
+
mode: "heuristic" | "sidecar" | "mixed";
|
|
526
|
+
fontMs: number;
|
|
527
|
+
heuristicMs: number;
|
|
528
|
+
heuristicSalienceMs: number;
|
|
529
|
+
heuristicEntityMs: number;
|
|
530
|
+
heuristicRelationshipMs: number;
|
|
531
|
+
heuristicAliasMs: number;
|
|
532
|
+
sidecarMs: number;
|
|
533
|
+
graphMs: number;
|
|
534
|
+
dbMs: number;
|
|
535
|
+
totalMs: number;
|
|
536
|
+
completedAt: number;
|
|
537
|
+
chunkId: string;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
export interface CortexIngestionStatusDTO {
|
|
541
|
+
chatId: string;
|
|
542
|
+
status: "idle" | "processing" | "complete" | "error";
|
|
543
|
+
phase:
|
|
544
|
+
| "queued"
|
|
545
|
+
| "font"
|
|
546
|
+
| "heuristics"
|
|
547
|
+
| "sidecar"
|
|
548
|
+
| "persisting"
|
|
549
|
+
| "complete"
|
|
550
|
+
| "error";
|
|
551
|
+
chunkId: string | null;
|
|
552
|
+
startedAt: number | null;
|
|
553
|
+
updatedAt: number;
|
|
554
|
+
pendingJobs: number;
|
|
555
|
+
error?: string;
|
|
556
|
+
timings?: CortexIngestionTimingsDTO | null;
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
export interface CortexIngestionTelemetryDTO {
|
|
560
|
+
samples: number;
|
|
561
|
+
last: CortexIngestionTimingsDTO | null;
|
|
562
|
+
averages: {
|
|
563
|
+
fontMs: number;
|
|
564
|
+
heuristicMs: number;
|
|
565
|
+
sidecarMs: number;
|
|
566
|
+
graphMs: number;
|
|
567
|
+
dbMs: number;
|
|
568
|
+
totalMs: number;
|
|
569
|
+
};
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
// ─── Long-Term Chat Memory ─────────────────────────────────────────────
|
|
573
|
+
|
|
574
|
+
/**
|
|
575
|
+
* A vectorized chat chunk — the basic unit of long-term chat memory. Chunks
|
|
576
|
+
* are produced from contiguous same-role messages bounded by token target
|
|
577
|
+
* and scene-break heuristics; the {{memories}} macro retrieves the top-K
|
|
578
|
+
* chunks by hybrid vector + BM25 score during prompt assembly.
|
|
579
|
+
*/
|
|
580
|
+
export interface ChatChunkDTO {
|
|
581
|
+
id: string;
|
|
582
|
+
chatId: string;
|
|
583
|
+
startMessageId: string;
|
|
584
|
+
endMessageId: string;
|
|
585
|
+
messageIds: string[];
|
|
586
|
+
content: string;
|
|
587
|
+
tokenCount: number;
|
|
588
|
+
messageCount: number;
|
|
589
|
+
vectorizedAt: number | null;
|
|
590
|
+
vectorModel: string | null;
|
|
591
|
+
retrievalCount: number;
|
|
592
|
+
lastRetrievedAt: number | null;
|
|
593
|
+
createdAt: number;
|
|
594
|
+
updatedAt: number;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
/**
|
|
598
|
+
* Result of a long-term chat memory warmup. `status` describes the action
|
|
599
|
+
* taken; `reason` is a short human-readable string useful for diagnostics
|
|
600
|
+
* when nothing was queued.
|
|
601
|
+
*/
|
|
602
|
+
export interface ChatMemoryWarmupResultDTO {
|
|
603
|
+
status: "skipped" | "complete" | "rebuilding" | "queued" | "error";
|
|
604
|
+
reason?: string;
|
|
605
|
+
rebuilt?: boolean;
|
|
606
|
+
vectorizationsQueued?: number;
|
|
607
|
+
}
|
package/src/permissions.ts
CHANGED
|
@@ -15,7 +15,13 @@
|
|
|
15
15
|
* - "presets" — CRUD on user presets and prompt blocks
|
|
16
16
|
* - "personas" — CRUD on personas
|
|
17
17
|
* - "databanks" — CRUD on databanks and their documents
|
|
18
|
+
* - "memories" — CRUD on the Memory Cortex (entities, relations, vaults, chat
|
|
19
|
+
* links, consolidations) and long-term chat memory (vectorized
|
|
20
|
+
* chat-chunk retrieval, warmup, cache).
|
|
18
21
|
* - "macro_interceptor" — transform raw templates before macro parsing/dispatch
|
|
22
|
+
* - "web_search" — execute searches via the user's configured web search
|
|
23
|
+
* provider (e.g. SearXNG) and read the safe view of their
|
|
24
|
+
* web search settings (never the API key).
|
|
19
25
|
*/
|
|
20
26
|
export type SpindlePermission =
|
|
21
27
|
| "generation"
|
|
@@ -35,12 +41,14 @@ export type SpindlePermission =
|
|
|
35
41
|
| "world_books"
|
|
36
42
|
| "regex_scripts"
|
|
37
43
|
| "databanks"
|
|
44
|
+
| "memories"
|
|
38
45
|
| "personas"
|
|
39
46
|
| "push_notification"
|
|
40
47
|
| "image_gen"
|
|
41
48
|
| "images"
|
|
42
49
|
| "generation_parameters"
|
|
43
|
-
| "macro_interceptor"
|
|
50
|
+
| "macro_interceptor"
|
|
51
|
+
| "web_search";
|
|
44
52
|
|
|
45
53
|
export const ALL_PERMISSIONS: readonly SpindlePermission[] = [
|
|
46
54
|
"generation",
|
|
@@ -60,12 +68,14 @@ export const ALL_PERMISSIONS: readonly SpindlePermission[] = [
|
|
|
60
68
|
"world_books",
|
|
61
69
|
"regex_scripts",
|
|
62
70
|
"databanks",
|
|
71
|
+
"memories",
|
|
63
72
|
"personas",
|
|
64
73
|
"push_notification",
|
|
65
74
|
"image_gen",
|
|
66
75
|
"images",
|
|
67
76
|
"generation_parameters",
|
|
68
77
|
"macro_interceptor",
|
|
78
|
+
"web_search",
|
|
69
79
|
] as const;
|
|
70
80
|
|
|
71
81
|
export function isValidPermission(p: string): p is SpindlePermission {
|
package/src/spindle-api.ts
CHANGED
|
@@ -103,7 +103,35 @@ import type {
|
|
|
103
103
|
MessageContentProcessorResultDTO,
|
|
104
104
|
SharedRpcRequestContextDTO,
|
|
105
105
|
SharedRpcEndpointPolicyDTO,
|
|
106
|
+
WebSearchRequestDTO,
|
|
107
|
+
WebSearchResponseDTO,
|
|
108
|
+
WebSearchSettingsDTO,
|
|
106
109
|
} from "./api";
|
|
110
|
+
import type {
|
|
111
|
+
ChatChunkDTO,
|
|
112
|
+
ChatLinkAttachDTO,
|
|
113
|
+
ChatLinkDTO,
|
|
114
|
+
ChatMemoryWarmupResultDTO,
|
|
115
|
+
CortexIngestionStatusDTO,
|
|
116
|
+
CortexIngestionTelemetryDTO,
|
|
117
|
+
CortexQueryDTO,
|
|
118
|
+
CortexResultDTO,
|
|
119
|
+
CortexUsageStatsDTO,
|
|
120
|
+
LinkedCortexResultDTO,
|
|
121
|
+
MemoryConsolidationDTO,
|
|
122
|
+
MemoryCortexConfigDTO,
|
|
123
|
+
MemoryEntityDTO,
|
|
124
|
+
MemoryEntityStatusUpdateDTO,
|
|
125
|
+
MemoryEntityUpsertDTO,
|
|
126
|
+
MemoryRelationDTO,
|
|
127
|
+
MemoryRelationUpsertDTO,
|
|
128
|
+
MemorySalienceDTO,
|
|
129
|
+
VaultChunkDTO,
|
|
130
|
+
VaultCreateDTO,
|
|
131
|
+
VaultDTO,
|
|
132
|
+
VaultReindexResultDTO,
|
|
133
|
+
VaultWithContentsDTO,
|
|
134
|
+
} from "./memories";
|
|
107
135
|
|
|
108
136
|
export interface FrontendProcessHandle {
|
|
109
137
|
/** Host-assigned process ID unique within the extension runtime. */
|
|
@@ -827,6 +855,193 @@ export interface SpindleAPI {
|
|
|
827
855
|
};
|
|
828
856
|
};
|
|
829
857
|
|
|
858
|
+
/**
|
|
859
|
+
* Memory Cortex & Long-Term Chat Memory (permission: "memories").
|
|
860
|
+
*
|
|
861
|
+
* Lumiverse's hybrid memory architecture exposed as a single CRUD surface:
|
|
862
|
+
*
|
|
863
|
+
* - **`cortex`** — config get/put, retrieval (cortex query, linked-cortex
|
|
864
|
+
* query for vaults + interlinks), warm-cache reads, cache invalidation.
|
|
865
|
+
* - **`entities`** / **`relations`** — entity graph CRUD: list, find,
|
|
866
|
+
* upsert, status updates, fact appends, emotional valence, plus active
|
|
867
|
+
* + unfiltered relation reads and `upsert` for symbolic edges.
|
|
868
|
+
* - **`consolidations`** — list arcs by tier, fetch the latest arc, and
|
|
869
|
+
* `run()` to trigger a background consolidation pass.
|
|
870
|
+
* - **`salience`** — read salience records (score, emotional tags,
|
|
871
|
+
* narrative flags) for chunks already ingested.
|
|
872
|
+
* - **`vaults`** — frozen snapshots of cortex state. `create()` snapshots
|
|
873
|
+
* a chat's entities/relations/chunks, `reindex()` re-copies the chunk
|
|
874
|
+
* snapshot after a LanceDB reset, plus list / get / rename / delete.
|
|
875
|
+
* - **`links`** — attach vaults to chats or set up bidirectional
|
|
876
|
+
* chat-to-chat interlinks; list / remove / toggle.
|
|
877
|
+
* - **`chatMemory`** — the long-term chat memory layer used by the
|
|
878
|
+
* `{{memories}}` macro: list vectorized chunks, fetch top-K retrieval,
|
|
879
|
+
* warm a chat (rebuild + queue vectorization), invalidate cache.
|
|
880
|
+
* - **`stats`** — usage counters, live ingestion phase, and ingestion
|
|
881
|
+
* timing telemetry.
|
|
882
|
+
*
|
|
883
|
+
* For user-scoped extensions, `userId` is inferred from the extension
|
|
884
|
+
* owner. For operator-scoped extensions, pass `userId` to scope a call
|
|
885
|
+
* to a specific user.
|
|
886
|
+
*
|
|
887
|
+
* All chat-scoped calls verify chat ownership against the resolved user
|
|
888
|
+
* — extensions cannot read or mutate cortex state for chats they do not
|
|
889
|
+
* own.
|
|
890
|
+
*/
|
|
891
|
+
memories: {
|
|
892
|
+
cortex: {
|
|
893
|
+
/** Get the user's Memory Cortex configuration. */
|
|
894
|
+
getConfig(userId?: string): Promise<MemoryCortexConfigDTO>;
|
|
895
|
+
/** Patch the user's Memory Cortex configuration. Unspecified fields are left untouched. */
|
|
896
|
+
putConfig(patch: Partial<MemoryCortexConfigDTO>, userId?: string): Promise<MemoryCortexConfigDTO>;
|
|
897
|
+
/**
|
|
898
|
+
* Execute a cortex-enhanced memory retrieval. Combines vector search,
|
|
899
|
+
* entity-context fan-out, recency, reinforcement, and emotional
|
|
900
|
+
* components into a unified `CortexResultDTO`.
|
|
901
|
+
*
|
|
902
|
+
* Results are cached server-side; the next call within ~5 minutes
|
|
903
|
+
* for the same chat + query shape returns the cached value.
|
|
904
|
+
*/
|
|
905
|
+
query(query: CortexQueryDTO): Promise<CortexResultDTO>;
|
|
906
|
+
/**
|
|
907
|
+
* Read the most recent cortex result from the warm cache without
|
|
908
|
+
* triggering a re-query. Returns `null` if no cached result exists
|
|
909
|
+
* or it expired. Synchronous from the caller's perspective (no
|
|
910
|
+
* vector search runs); useful when an extension just needs the same
|
|
911
|
+
* memories the active generation saw.
|
|
912
|
+
*/
|
|
913
|
+
getCached(chatId: string): Promise<CortexResultDTO | null>;
|
|
914
|
+
/**
|
|
915
|
+
* Resolve linked-cortex data for a chat — every attached vault plus
|
|
916
|
+
* every bidirectional interlink target. When `queryText` is supplied,
|
|
917
|
+
* vault retrieval and interlink queries are enriched with relevance
|
|
918
|
+
* to the current conversation.
|
|
919
|
+
*/
|
|
920
|
+
queryLinked(chatId: string, options?: { queryText?: string; userId?: string }): Promise<LinkedCortexResultDTO>;
|
|
921
|
+
/** Read the cached linked-cortex result for a chat. Mirrors {@link getCached}. */
|
|
922
|
+
getCachedLinked(chatId: string): Promise<LinkedCortexResultDTO | null>;
|
|
923
|
+
/** Invalidate the cortex retrieval cache for a chat. */
|
|
924
|
+
invalidateCache(chatId: string): Promise<void>;
|
|
925
|
+
/** Invalidate the linked-cortex retrieval cache for a chat. */
|
|
926
|
+
invalidateLinkedCache(chatId: string): Promise<void>;
|
|
927
|
+
};
|
|
928
|
+
|
|
929
|
+
entities: {
|
|
930
|
+
/**
|
|
931
|
+
* List entities for a chat. By default returns only entities with
|
|
932
|
+
* `status: "active"`, ordered by salience; pass `activeOnly: false`
|
|
933
|
+
* to include retired / deceased / destroyed entities.
|
|
934
|
+
*/
|
|
935
|
+
list(chatId: string, options?: { activeOnly?: boolean; limit?: number; userId?: string }): Promise<MemoryEntityDTO[]>;
|
|
936
|
+
get(entityId: string, userId?: string): Promise<MemoryEntityDTO | null>;
|
|
937
|
+
findByName(chatId: string, name: string, userId?: string): Promise<MemoryEntityDTO | null>;
|
|
938
|
+
/**
|
|
939
|
+
* Upsert an entity. Matches against canonical name and known aliases;
|
|
940
|
+
* inserts a new row on miss. `chunkId` and `createdAt` attribute the
|
|
941
|
+
* mention used to fill `lastSeen*` fields — pass them when replaying
|
|
942
|
+
* an extractor over a specific chunk.
|
|
943
|
+
*/
|
|
944
|
+
upsert(
|
|
945
|
+
chatId: string,
|
|
946
|
+
entity: MemoryEntityUpsertDTO,
|
|
947
|
+
options?: { chunkId?: string | null; createdAt?: number; userId?: string },
|
|
948
|
+
): Promise<MemoryEntityDTO>;
|
|
949
|
+
updateStatus(entityId: string, patch: MemoryEntityStatusUpdateDTO, userId?: string): Promise<MemoryEntityDTO>;
|
|
950
|
+
addFacts(entityId: string, facts: string[], userId?: string): Promise<MemoryEntityDTO>;
|
|
951
|
+
getFacts(entityId: string, userId?: string): Promise<string[]>;
|
|
952
|
+
updateEmotionalValence(entityId: string, valence: Record<string, number>, userId?: string): Promise<MemoryEntityDTO>;
|
|
953
|
+
};
|
|
954
|
+
|
|
955
|
+
relations: {
|
|
956
|
+
/** Active relations for a chat (excludes superseded / merged edges). */
|
|
957
|
+
list(chatId: string, userId?: string): Promise<MemoryRelationDTO[]>;
|
|
958
|
+
/** Every relation row including superseded / merged edges — for diagnostics. */
|
|
959
|
+
listAll(chatId: string, userId?: string): Promise<MemoryRelationDTO[]>;
|
|
960
|
+
forEntity(chatId: string, entityId: string, userId?: string): Promise<MemoryRelationDTO[]>;
|
|
961
|
+
forEntities(chatId: string, entityIds: string[], options?: { limit?: number; userId?: string }): Promise<MemoryRelationDTO[]>;
|
|
962
|
+
/**
|
|
963
|
+
* Upsert a relation. Both endpoints must already exist in the entity
|
|
964
|
+
* graph (use `entities.upsert` first); the relation is silently
|
|
965
|
+
* dropped otherwise. `chunkId` attributes the evidence to a chunk.
|
|
966
|
+
*/
|
|
967
|
+
upsert(
|
|
968
|
+
chatId: string,
|
|
969
|
+
relation: MemoryRelationUpsertDTO,
|
|
970
|
+
options?: { chunkId?: string | null; userId?: string },
|
|
971
|
+
): Promise<MemoryRelationDTO | null>;
|
|
972
|
+
};
|
|
973
|
+
|
|
974
|
+
consolidations: {
|
|
975
|
+
/** List arcs for a chat. Pass `tier` to filter to one tier (1 = scene, 2 = chapter, …). */
|
|
976
|
+
list(chatId: string, options?: { tier?: number; userId?: string }): Promise<MemoryConsolidationDTO[]>;
|
|
977
|
+
/** The most recent arc across all tiers, or `null` if none have been produced. */
|
|
978
|
+
latestArc(chatId: string, userId?: string): Promise<MemoryConsolidationDTO | null>;
|
|
979
|
+
/**
|
|
980
|
+
* Trigger an async consolidation pass over the chat's chunks. Returns
|
|
981
|
+
* immediately; new arcs become visible via `list()` once the
|
|
982
|
+
* background job completes.
|
|
983
|
+
*/
|
|
984
|
+
run(chatId: string, userId?: string): Promise<void>;
|
|
985
|
+
};
|
|
986
|
+
|
|
987
|
+
salience: {
|
|
988
|
+
/** List salience records for a chat's chunks, ordered by `scoredAt`. */
|
|
989
|
+
list(chatId: string, options?: { limit?: number; offset?: number; userId?: string }): Promise<MemorySalienceDTO[]>;
|
|
990
|
+
};
|
|
991
|
+
|
|
992
|
+
vaults: {
|
|
993
|
+
list(userId?: string): Promise<VaultDTO[]>;
|
|
994
|
+
/** Fetch a vault with its entities + relations. Returns `null` if not found or not owned by the resolved user. */
|
|
995
|
+
get(vaultId: string, userId?: string): Promise<VaultWithContentsDTO | null>;
|
|
996
|
+
getChunks(vaultId: string, userId?: string): Promise<VaultChunkDTO[]>;
|
|
997
|
+
/**
|
|
998
|
+
* Snapshot a chat's cortex state into a new vault. Copies entities,
|
|
999
|
+
* relations and chunk content; LanceDB embeddings are copied in the
|
|
1000
|
+
* background.
|
|
1001
|
+
*/
|
|
1002
|
+
create(input: VaultCreateDTO, userId?: string): Promise<VaultDTO>;
|
|
1003
|
+
rename(vaultId: string, name: string, userId?: string): Promise<boolean>;
|
|
1004
|
+
delete(vaultId: string, userId?: string): Promise<boolean>;
|
|
1005
|
+
/** Re-run the LanceDB chunk copy for a vault (e.g. after an embedding model swap). */
|
|
1006
|
+
reindex(vaultId: string, userId?: string): Promise<VaultReindexResultDTO>;
|
|
1007
|
+
};
|
|
1008
|
+
|
|
1009
|
+
links: {
|
|
1010
|
+
list(chatId: string, userId?: string): Promise<ChatLinkDTO[]>;
|
|
1011
|
+
/**
|
|
1012
|
+
* Attach a vault or set up a chat-to-chat interlink. For interlinks,
|
|
1013
|
+
* pass `bidirectional: true` to also create the reverse link.
|
|
1014
|
+
*/
|
|
1015
|
+
attach(input: ChatLinkAttachDTO, userId?: string): Promise<ChatLinkDTO[]>;
|
|
1016
|
+
remove(chatId: string, linkId: string, userId?: string): Promise<boolean>;
|
|
1017
|
+
toggle(chatId: string, linkId: string, enabled: boolean, userId?: string): Promise<boolean>;
|
|
1018
|
+
};
|
|
1019
|
+
|
|
1020
|
+
chatMemory: {
|
|
1021
|
+
/**
|
|
1022
|
+
* List the vectorized chunks for a chat. Useful for inspecting the
|
|
1023
|
+
* raw retrieval index used by the `{{memories}}` macro.
|
|
1024
|
+
*/
|
|
1025
|
+
listChunks(chatId: string, userId?: string): Promise<ChatChunkDTO[]>;
|
|
1026
|
+
/** Top-K chat memory chunks for a chat via hybrid vector + BM25 search. */
|
|
1027
|
+
get(chatId: string, options?: { topK?: number; userId?: string }): Promise<ChatMemoryResultDTO>;
|
|
1028
|
+
/**
|
|
1029
|
+
* Warm long-term chat memory: rebuilds chunks if stale and queues any
|
|
1030
|
+
* pending chunk vectorizations. Pass `force: true` to rebuild even
|
|
1031
|
+
* when the chunk hash is fresh.
|
|
1032
|
+
*/
|
|
1033
|
+
warm(chatId: string, options?: { force?: boolean; userId?: string }): Promise<ChatMemoryWarmupResultDTO>;
|
|
1034
|
+
/** Drop the cached `{{memories}}` retrieval result for a chat. */
|
|
1035
|
+
invalidate(chatId: string, userId?: string): Promise<void>;
|
|
1036
|
+
};
|
|
1037
|
+
|
|
1038
|
+
stats: {
|
|
1039
|
+
usage(chatId: string, userId?: string): Promise<CortexUsageStatsDTO>;
|
|
1040
|
+
ingestionStatus(chatId: string, userId?: string): Promise<CortexIngestionStatusDTO | null>;
|
|
1041
|
+
ingestionTelemetry(chatId: string, userId?: string): Promise<CortexIngestionTelemetryDTO>;
|
|
1042
|
+
};
|
|
1043
|
+
};
|
|
1044
|
+
|
|
830
1045
|
/**
|
|
831
1046
|
* Personas CRUD (permission: "personas").
|
|
832
1047
|
* Manage user personas (identity profiles).
|
|
@@ -1158,6 +1373,74 @@ export interface SpindleAPI {
|
|
|
1158
1373
|
}>;
|
|
1159
1374
|
};
|
|
1160
1375
|
|
|
1376
|
+
/**
|
|
1377
|
+
* Web search (permission: `"web_search"`).
|
|
1378
|
+
*
|
|
1379
|
+
* Execute searches via the user's configured web search provider
|
|
1380
|
+
* (currently SearXNG; additional providers will be added over time) and
|
|
1381
|
+
* read the safe view of their web search settings. The host enforces all
|
|
1382
|
+
* upstream limits (engine list, max result count, max pages to scrape,
|
|
1383
|
+
* timeouts) — extensions cannot supply their own endpoint or API key.
|
|
1384
|
+
*
|
|
1385
|
+
* For user-scoped extensions, `userId` is inferred from the extension
|
|
1386
|
+
* owner. Operator-scoped extensions should pass the `userId` of the user
|
|
1387
|
+
* whose search engine should run the query.
|
|
1388
|
+
*
|
|
1389
|
+
* @example
|
|
1390
|
+
* ```ts
|
|
1391
|
+
* // Pre-flight: confirm web search is configured before issuing a query.
|
|
1392
|
+
* const settings = await spindle.webSearch.getSettings()
|
|
1393
|
+
* if (!settings.enabled) {
|
|
1394
|
+
* spindle.toast.warning('Configure a web search provider in Settings → Web Search first.')
|
|
1395
|
+
* return
|
|
1396
|
+
* }
|
|
1397
|
+
*
|
|
1398
|
+
* // Full enriched query — scrapes the top-N pages and returns a
|
|
1399
|
+
* // ready-to-inject context block.
|
|
1400
|
+
* const enriched = await spindle.webSearch.query({
|
|
1401
|
+
* query: 'latest LLM benchmark results',
|
|
1402
|
+
* count: 5,
|
|
1403
|
+
* })
|
|
1404
|
+
* await spindle.chat.appendMessage(chatId, {
|
|
1405
|
+
* role: 'system',
|
|
1406
|
+
* content: enriched.context ?? '',
|
|
1407
|
+
* })
|
|
1408
|
+
*
|
|
1409
|
+
* // Lightweight query — titles, URLs, snippets only.
|
|
1410
|
+
* const quick = await spindle.webSearch.query({
|
|
1411
|
+
* query: 'who won the 2026 world cup',
|
|
1412
|
+
* scrape: false,
|
|
1413
|
+
* })
|
|
1414
|
+
* for (const row of quick.results) {
|
|
1415
|
+
* spindle.log.info(`${row.title} — ${row.url}`)
|
|
1416
|
+
* }
|
|
1417
|
+
* ```
|
|
1418
|
+
*/
|
|
1419
|
+
webSearch: {
|
|
1420
|
+
/**
|
|
1421
|
+
* Run a search against the user's configured provider.
|
|
1422
|
+
*
|
|
1423
|
+
* Rejects with `"Web search is disabled"` when the user has not
|
|
1424
|
+
* configured a provider, and with `"Web search API URL is not configured"`
|
|
1425
|
+
* when the upstream endpoint is missing. Other upstream errors surface
|
|
1426
|
+
* as `Error("SearXNG returned HTTP NNN")` (or equivalent for future
|
|
1427
|
+
* providers).
|
|
1428
|
+
*
|
|
1429
|
+
* When `input.scrape` is `false`, `documents` and `context` are omitted
|
|
1430
|
+
* from the response. Otherwise the host scrapes up to
|
|
1431
|
+
* `WebSearchSettingsDTO.maxPagesToScrape` results, fills in
|
|
1432
|
+
* `documents[].content`, and assembles a prompt-ready `context` block.
|
|
1433
|
+
*/
|
|
1434
|
+
query(input: WebSearchRequestDTO): Promise<WebSearchResponseDTO>;
|
|
1435
|
+
/**
|
|
1436
|
+
* Read the safe view of the user's web search configuration. The raw
|
|
1437
|
+
* API key is never exposed — only `hasApiKey` indicates whether one is
|
|
1438
|
+
* on file. Useful for branching on `enabled` / `provider` /
|
|
1439
|
+
* `maxResultCount` before issuing a query.
|
|
1440
|
+
*/
|
|
1441
|
+
getSettings(userId?: string): Promise<WebSearchSettingsDTO>;
|
|
1442
|
+
};
|
|
1443
|
+
|
|
1161
1444
|
/**
|
|
1162
1445
|
* Text editor (free tier — no permission needed).
|
|
1163
1446
|
* Opens the native Lumiverse expanded text editor modal on the user's
|