@psiclawops/hypermem 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/ARCHITECTURE.md +12 -3
- package/README.md +30 -6
- package/bin/hypermem-status.mjs +166 -0
- package/dist/background-indexer.d.ts +132 -0
- package/dist/background-indexer.d.ts.map +1 -0
- package/dist/background-indexer.js +1044 -0
- package/dist/cache.d.ts +110 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/cache.js +495 -0
- package/dist/compaction-fence.d.ts +89 -0
- package/dist/compaction-fence.d.ts.map +1 -0
- package/dist/compaction-fence.js +153 -0
- package/dist/compositor.d.ts +226 -0
- package/dist/compositor.d.ts.map +1 -0
- package/dist/compositor.js +2558 -0
- package/dist/content-type-classifier.d.ts +41 -0
- package/dist/content-type-classifier.d.ts.map +1 -0
- package/dist/content-type-classifier.js +181 -0
- package/dist/cross-agent.d.ts +62 -0
- package/dist/cross-agent.d.ts.map +1 -0
- package/dist/cross-agent.js +259 -0
- package/dist/db.d.ts +131 -0
- package/dist/db.d.ts.map +1 -0
- package/dist/db.js +402 -0
- package/dist/desired-state-store.d.ts +100 -0
- package/dist/desired-state-store.d.ts.map +1 -0
- package/dist/desired-state-store.js +222 -0
- package/dist/doc-chunk-store.d.ts +140 -0
- package/dist/doc-chunk-store.d.ts.map +1 -0
- package/dist/doc-chunk-store.js +391 -0
- package/dist/doc-chunker.d.ts +99 -0
- package/dist/doc-chunker.d.ts.map +1 -0
- package/dist/doc-chunker.js +324 -0
- package/dist/dreaming-promoter.d.ts +86 -0
- package/dist/dreaming-promoter.d.ts.map +1 -0
- package/dist/dreaming-promoter.js +381 -0
- package/dist/episode-store.d.ts +49 -0
- package/dist/episode-store.d.ts.map +1 -0
- package/dist/episode-store.js +135 -0
- package/dist/fact-store.d.ts +75 -0
- package/dist/fact-store.d.ts.map +1 -0
- package/dist/fact-store.js +236 -0
- package/dist/fleet-store.d.ts +144 -0
- package/dist/fleet-store.d.ts.map +1 -0
- package/dist/fleet-store.js +276 -0
- package/dist/fos-mod.d.ts +178 -0
- package/dist/fos-mod.d.ts.map +1 -0
- package/dist/fos-mod.js +416 -0
- package/dist/hybrid-retrieval.d.ts +64 -0
- package/dist/hybrid-retrieval.d.ts.map +1 -0
- package/dist/hybrid-retrieval.js +344 -0
- package/dist/image-eviction.d.ts +49 -0
- package/dist/image-eviction.d.ts.map +1 -0
- package/dist/image-eviction.js +251 -0
- package/dist/index.d.ts +650 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1072 -0
- package/dist/keystone-scorer.d.ts +51 -0
- package/dist/keystone-scorer.d.ts.map +1 -0
- package/dist/keystone-scorer.js +52 -0
- package/dist/knowledge-graph.d.ts +110 -0
- package/dist/knowledge-graph.d.ts.map +1 -0
- package/dist/knowledge-graph.js +305 -0
- package/dist/knowledge-lint.d.ts +29 -0
- package/dist/knowledge-lint.d.ts.map +1 -0
- package/dist/knowledge-lint.js +116 -0
- package/dist/knowledge-store.d.ts +72 -0
- package/dist/knowledge-store.d.ts.map +1 -0
- package/dist/knowledge-store.js +247 -0
- package/dist/library-schema.d.ts +22 -0
- package/dist/library-schema.d.ts.map +1 -0
- package/dist/library-schema.js +1038 -0
- package/dist/message-store.d.ts +89 -0
- package/dist/message-store.d.ts.map +1 -0
- package/dist/message-store.js +323 -0
- package/dist/metrics-dashboard.d.ts +114 -0
- package/dist/metrics-dashboard.d.ts.map +1 -0
- package/dist/metrics-dashboard.js +260 -0
- package/dist/obsidian-exporter.d.ts +57 -0
- package/dist/obsidian-exporter.d.ts.map +1 -0
- package/dist/obsidian-exporter.js +274 -0
- package/dist/obsidian-watcher.d.ts +147 -0
- package/dist/obsidian-watcher.d.ts.map +1 -0
- package/dist/obsidian-watcher.js +403 -0
- package/dist/open-domain.d.ts +46 -0
- package/dist/open-domain.d.ts.map +1 -0
- package/dist/open-domain.js +125 -0
- package/dist/preference-store.d.ts +54 -0
- package/dist/preference-store.d.ts.map +1 -0
- package/dist/preference-store.js +109 -0
- package/dist/preservation-gate.d.ts +82 -0
- package/dist/preservation-gate.d.ts.map +1 -0
- package/dist/preservation-gate.js +150 -0
- package/dist/proactive-pass.d.ts +63 -0
- package/dist/proactive-pass.d.ts.map +1 -0
- package/dist/proactive-pass.js +239 -0
- package/dist/profiles.d.ts +44 -0
- package/dist/profiles.d.ts.map +1 -0
- package/dist/profiles.js +227 -0
- package/dist/provider-translator.d.ts +50 -0
- package/dist/provider-translator.d.ts.map +1 -0
- package/dist/provider-translator.js +403 -0
- package/dist/rate-limiter.d.ts +76 -0
- package/dist/rate-limiter.d.ts.map +1 -0
- package/dist/rate-limiter.js +179 -0
- package/dist/repair-tool-pairs.d.ts +38 -0
- package/dist/repair-tool-pairs.d.ts.map +1 -0
- package/dist/repair-tool-pairs.js +138 -0
- package/dist/retrieval-policy.d.ts +51 -0
- package/dist/retrieval-policy.d.ts.map +1 -0
- package/dist/retrieval-policy.js +77 -0
- package/dist/schema.d.ts +15 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +229 -0
- package/dist/secret-scanner.d.ts +51 -0
- package/dist/secret-scanner.d.ts.map +1 -0
- package/dist/secret-scanner.js +248 -0
- package/dist/seed.d.ts +108 -0
- package/dist/seed.d.ts.map +1 -0
- package/dist/seed.js +177 -0
- package/dist/session-flusher.d.ts +53 -0
- package/dist/session-flusher.d.ts.map +1 -0
- package/dist/session-flusher.js +69 -0
- package/dist/session-topic-map.d.ts +41 -0
- package/dist/session-topic-map.d.ts.map +1 -0
- package/dist/session-topic-map.js +77 -0
- package/dist/spawn-context.d.ts +54 -0
- package/dist/spawn-context.d.ts.map +1 -0
- package/dist/spawn-context.js +159 -0
- package/dist/system-store.d.ts +73 -0
- package/dist/system-store.d.ts.map +1 -0
- package/dist/system-store.js +182 -0
- package/dist/temporal-store.d.ts +80 -0
- package/dist/temporal-store.d.ts.map +1 -0
- package/dist/temporal-store.js +149 -0
- package/dist/topic-detector.d.ts +35 -0
- package/dist/topic-detector.d.ts.map +1 -0
- package/dist/topic-detector.js +249 -0
- package/dist/topic-store.d.ts +45 -0
- package/dist/topic-store.d.ts.map +1 -0
- package/dist/topic-store.js +136 -0
- package/dist/topic-synthesizer.d.ts +51 -0
- package/dist/topic-synthesizer.d.ts.map +1 -0
- package/dist/topic-synthesizer.js +315 -0
- package/dist/trigger-registry.d.ts +63 -0
- package/dist/trigger-registry.d.ts.map +1 -0
- package/dist/trigger-registry.js +163 -0
- package/dist/types.d.ts +537 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +9 -0
- package/dist/vector-store.d.ts +170 -0
- package/dist/vector-store.d.ts.map +1 -0
- package/dist/vector-store.js +677 -0
- package/dist/version.d.ts +34 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +34 -0
- package/dist/wiki-page-emitter.d.ts +65 -0
- package/dist/wiki-page-emitter.d.ts.map +1 -0
- package/dist/wiki-page-emitter.js +258 -0
- package/dist/work-store.d.ts +112 -0
- package/dist/work-store.d.ts.map +1 -0
- package/dist/work-store.js +273 -0
- package/package.json +4 -1
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SessionTopicMap (P3.3)
|
|
3
|
+
*
|
|
4
|
+
* Manages per-session topic state in messages.db.
|
|
5
|
+
* Topics table: id(TEXT), session_key, name, created_at, last_active_at,
|
|
6
|
+
* message_count, metadata
|
|
7
|
+
*/
|
|
8
|
+
import type { DatabaseSync } from 'node:sqlite';
|
|
9
|
+
export declare class SessionTopicMap {
|
|
10
|
+
private db;
|
|
11
|
+
constructor(db: DatabaseSync);
|
|
12
|
+
/**
|
|
13
|
+
* Get the active topic for a session (most recently active).
|
|
14
|
+
*/
|
|
15
|
+
getActiveTopic(sessionKey: string): {
|
|
16
|
+
id: string;
|
|
17
|
+
name: string;
|
|
18
|
+
} | null;
|
|
19
|
+
/**
|
|
20
|
+
* Activate a topic: update last_active_at to now.
|
|
21
|
+
*/
|
|
22
|
+
activateTopic(sessionKey: string, topicId: string): void;
|
|
23
|
+
/**
|
|
24
|
+
* Create a new topic and activate it. Returns the new topicId.
|
|
25
|
+
*/
|
|
26
|
+
createTopic(sessionKey: string, name: string): string;
|
|
27
|
+
/**
|
|
28
|
+
* List all topics for a session, ordered by last_active_at DESC.
|
|
29
|
+
*/
|
|
30
|
+
listTopics(sessionKey: string): Array<{
|
|
31
|
+
id: string;
|
|
32
|
+
name: string;
|
|
33
|
+
messageCount: number;
|
|
34
|
+
lastActiveAt: number;
|
|
35
|
+
}>;
|
|
36
|
+
/**
|
|
37
|
+
* Increment message count for the given topic.
|
|
38
|
+
*/
|
|
39
|
+
incrementMessageCount(topicId: string): void;
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=session-topic-map.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-topic-map.d.ts","sourceRoot":"","sources":["../src/session-topic-map.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAGhD,qBAAa,eAAe;IACd,OAAO,CAAC,EAAE;gBAAF,EAAE,EAAE,YAAY;IAEpC;;OAEG;IACH,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;IAYvE;;OAEG;IACH,aAAa,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI;IAQxD;;OAEG;IACH,WAAW,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM;IAUrD;;OAEG;IACH,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,KAAK,CAAC;QACpC,EAAE,EAAE,MAAM,CAAC;QACX,IAAI,EAAE,MAAM,CAAC;QACb,YAAY,EAAE,MAAM,CAAC;QACrB,YAAY,EAAE,MAAM,CAAC;KACtB,CAAC;IAqBF;;OAEG;IACH,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;CAO7C"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SessionTopicMap (P3.3)
|
|
3
|
+
*
|
|
4
|
+
* Manages per-session topic state in messages.db.
|
|
5
|
+
* Topics table: id(TEXT), session_key, name, created_at, last_active_at,
|
|
6
|
+
* message_count, metadata
|
|
7
|
+
*/
|
|
8
|
+
import { randomUUID } from 'node:crypto';
|
|
9
|
+
export class SessionTopicMap {
|
|
10
|
+
db;
|
|
11
|
+
constructor(db) {
|
|
12
|
+
this.db = db;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Get the active topic for a session (most recently active).
|
|
16
|
+
*/
|
|
17
|
+
getActiveTopic(sessionKey) {
|
|
18
|
+
const row = this.db.prepare(`
|
|
19
|
+
SELECT id, name
|
|
20
|
+
FROM topics
|
|
21
|
+
WHERE session_key = ?
|
|
22
|
+
ORDER BY last_active_at DESC
|
|
23
|
+
LIMIT 1
|
|
24
|
+
`).get(sessionKey);
|
|
25
|
+
return row ?? null;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Activate a topic: update last_active_at to now.
|
|
29
|
+
*/
|
|
30
|
+
activateTopic(sessionKey, topicId) {
|
|
31
|
+
this.db.prepare(`
|
|
32
|
+
UPDATE topics
|
|
33
|
+
SET last_active_at = ?
|
|
34
|
+
WHERE id = ? AND session_key = ?
|
|
35
|
+
`).run(Date.now(), topicId, sessionKey);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Create a new topic and activate it. Returns the new topicId.
|
|
39
|
+
*/
|
|
40
|
+
createTopic(sessionKey, name) {
|
|
41
|
+
const id = randomUUID();
|
|
42
|
+
const now = Date.now();
|
|
43
|
+
this.db.prepare(`
|
|
44
|
+
INSERT INTO topics (id, session_key, name, created_at, last_active_at, message_count, metadata)
|
|
45
|
+
VALUES (?, ?, ?, ?, ?, 0, NULL)
|
|
46
|
+
`).run(id, sessionKey, name.slice(0, 40), now, now);
|
|
47
|
+
return id;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* List all topics for a session, ordered by last_active_at DESC.
|
|
51
|
+
*/
|
|
52
|
+
listTopics(sessionKey) {
|
|
53
|
+
const rows = this.db.prepare(`
|
|
54
|
+
SELECT id, name, message_count, last_active_at
|
|
55
|
+
FROM topics
|
|
56
|
+
WHERE session_key = ?
|
|
57
|
+
ORDER BY last_active_at DESC
|
|
58
|
+
`).all(sessionKey);
|
|
59
|
+
return rows.map(r => ({
|
|
60
|
+
id: r.id,
|
|
61
|
+
name: r.name,
|
|
62
|
+
messageCount: r.message_count,
|
|
63
|
+
lastActiveAt: r.last_active_at,
|
|
64
|
+
}));
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Increment message count for the given topic.
|
|
68
|
+
*/
|
|
69
|
+
incrementMessageCount(topicId) {
|
|
70
|
+
this.db.prepare(`
|
|
71
|
+
UPDATE topics
|
|
72
|
+
SET message_count = message_count + 1
|
|
73
|
+
WHERE id = ?
|
|
74
|
+
`).run(topicId);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=session-topic-map.js.map
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* hypermem Spawn Context — Subagent Context Inheritance
|
|
3
|
+
*
|
|
4
|
+
* Provides tools to snapshot a parent session's working context and make it
|
|
5
|
+
* available to a spawned subagent at compose time.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* const ctx = await buildSpawnContext(messageStore, docChunkStore, agentId, {
|
|
9
|
+
* parentSessionKey: 'agent:forge:webchat:main',
|
|
10
|
+
* workingSnapshot: 10,
|
|
11
|
+
* documents: ['/path/to/spec.md'],
|
|
12
|
+
* });
|
|
13
|
+
* // Inject ctx.parentContextBlock into the subagent task prompt.
|
|
14
|
+
* // Pass ctx.sessionKey as ComposeRequest.parentSessionKey for doc chunk retrieval.
|
|
15
|
+
* // When done: docChunkStore.clearSessionChunks(ctx.sessionKey);
|
|
16
|
+
*/
|
|
17
|
+
import type { MessageStore } from './message-store.js';
|
|
18
|
+
import type { DocChunkStore } from './doc-chunk-store.js';
|
|
19
|
+
export interface SpawnContextOptions {
|
|
20
|
+
/** Parent session key to snapshot working context from */
|
|
21
|
+
parentSessionKey: string;
|
|
22
|
+
/** Number of recent turns to include (default 25, max 50) */
|
|
23
|
+
workingSnapshot?: number;
|
|
24
|
+
/** File paths to chunk and inject as session-scoped doc chunks */
|
|
25
|
+
documents?: string[];
|
|
26
|
+
/** Token budget for the entire spawn context (default 4000) */
|
|
27
|
+
budgetTokens?: number;
|
|
28
|
+
}
|
|
29
|
+
export interface SpawnContext {
|
|
30
|
+
/** Formatted parent context block for injection into subagent task prompt */
|
|
31
|
+
parentContextBlock: string | null;
|
|
32
|
+
/** Session key to use for querying injected doc chunks at compose time */
|
|
33
|
+
sessionKey: string;
|
|
34
|
+
/** Summary of what was injected (for logging) */
|
|
35
|
+
summary: {
|
|
36
|
+
turnsIncluded: number;
|
|
37
|
+
documentsIndexed: number;
|
|
38
|
+
documentsSkipped: string[];
|
|
39
|
+
tokenEstimate: number;
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Build a spawn context for a subagent by snapshotting the parent session.
|
|
44
|
+
*
|
|
45
|
+
* 1. Generates a unique session key for this spawn.
|
|
46
|
+
* 2. Pulls recent turns from the parent session (L2 messages).
|
|
47
|
+
* 3. Formats them into a compact block for injection into the task prompt.
|
|
48
|
+
* 4. Optionally chunks and indexes documents as session-scoped doc chunks.
|
|
49
|
+
* 5. Returns the context block, spawn session key, and a summary.
|
|
50
|
+
*
|
|
51
|
+
* Errors are handled gracefully — this function never throws.
|
|
52
|
+
*/
|
|
53
|
+
export declare function buildSpawnContext(messageStore: MessageStore, docChunkStore: DocChunkStore, agentId: string, options: SpawnContextOptions): Promise<SpawnContext>;
|
|
54
|
+
//# sourceMappingURL=spawn-context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spawn-context.d.ts","sourceRoot":"","sources":["../src/spawn-context.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAI1D,MAAM,WAAW,mBAAmB;IAClC,0DAA0D;IAC1D,gBAAgB,EAAE,MAAM,CAAC;IACzB,6DAA6D;IAC7D,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,kEAAkE;IAClE,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,+DAA+D;IAC/D,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,YAAY;IAC3B,6EAA6E;IAC7E,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,0EAA0E;IAC1E,UAAU,EAAE,MAAM,CAAC;IACnB,iDAAiD;IACjD,OAAO,EAAE;QACP,aAAa,EAAE,MAAM,CAAC;QACtB,gBAAgB,EAAE,MAAM,CAAC;QACzB,gBAAgB,EAAE,MAAM,EAAE,CAAC;QAC3B,aAAa,EAAE,MAAM,CAAC;KACvB,CAAC;CACH;AA6DD;;;;;;;;;;GAUG;AACH,wBAAsB,iBAAiB,CACrC,YAAY,EAAE,YAAY,EAC1B,aAAa,EAAE,aAAa,EAC5B,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,YAAY,CAAC,CA4FvB"}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* hypermem Spawn Context — Subagent Context Inheritance
|
|
3
|
+
*
|
|
4
|
+
* Provides tools to snapshot a parent session's working context and make it
|
|
5
|
+
* available to a spawned subagent at compose time.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* const ctx = await buildSpawnContext(messageStore, docChunkStore, agentId, {
|
|
9
|
+
* parentSessionKey: 'agent:forge:webchat:main',
|
|
10
|
+
* workingSnapshot: 10,
|
|
11
|
+
* documents: ['/path/to/spec.md'],
|
|
12
|
+
* });
|
|
13
|
+
* // Inject ctx.parentContextBlock into the subagent task prompt.
|
|
14
|
+
* // Pass ctx.sessionKey as ComposeRequest.parentSessionKey for doc chunk retrieval.
|
|
15
|
+
* // When done: docChunkStore.clearSessionChunks(ctx.sessionKey);
|
|
16
|
+
*/
|
|
17
|
+
import { readFile } from 'node:fs/promises';
|
|
18
|
+
// ─── Constants ──────────────────────────────────────────────────
|
|
19
|
+
const DEFAULT_WORKING_SNAPSHOT = 25;
|
|
20
|
+
const MAX_WORKING_SNAPSHOT = 50;
|
|
21
|
+
const DEFAULT_BUDGET_TOKENS = 4000;
|
|
22
|
+
const MAX_CHUNK_CHARS = 500;
|
|
23
|
+
const CHUNK_OVERLAP_CHARS = 50;
|
|
24
|
+
// ─── Helpers ────────────────────────────────────────────────────
|
|
25
|
+
function estimateTokens(text) {
|
|
26
|
+
return Math.ceil(text.length / 4);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Split text on double-newlines, max 500 chars per chunk, with overlap.
|
|
30
|
+
*/
|
|
31
|
+
function chunkText(text) {
|
|
32
|
+
const paragraphs = text.split(/\n\n+/);
|
|
33
|
+
const chunks = [];
|
|
34
|
+
let current = '';
|
|
35
|
+
for (const para of paragraphs) {
|
|
36
|
+
const trimmed = para.trim();
|
|
37
|
+
if (!trimmed)
|
|
38
|
+
continue;
|
|
39
|
+
if (current.length === 0) {
|
|
40
|
+
current = trimmed;
|
|
41
|
+
}
|
|
42
|
+
else if (current.length + trimmed.length + 2 <= MAX_CHUNK_CHARS) {
|
|
43
|
+
current += '\n\n' + trimmed;
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
// Flush current chunk
|
|
47
|
+
chunks.push(current);
|
|
48
|
+
// Start new chunk with overlap from end of previous chunk
|
|
49
|
+
const overlap = current.slice(-CHUNK_OVERLAP_CHARS);
|
|
50
|
+
current = (overlap.trim() ? overlap.trim() + '\n\n' : '') + trimmed;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
if (current.trim()) {
|
|
54
|
+
chunks.push(current.trim());
|
|
55
|
+
}
|
|
56
|
+
// If text had no double-newlines or a single large paragraph, split by chars
|
|
57
|
+
if (chunks.length === 0 && text.trim()) {
|
|
58
|
+
let pos = 0;
|
|
59
|
+
const raw = text.trim();
|
|
60
|
+
while (pos < raw.length) {
|
|
61
|
+
const end = Math.min(pos + MAX_CHUNK_CHARS, raw.length);
|
|
62
|
+
chunks.push(raw.slice(pos, end));
|
|
63
|
+
pos += MAX_CHUNK_CHARS - CHUNK_OVERLAP_CHARS;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return chunks;
|
|
67
|
+
}
|
|
68
|
+
// ─── Main Function ───────────────────────────────────────────────
|
|
69
|
+
/**
|
|
70
|
+
* Build a spawn context for a subagent by snapshotting the parent session.
|
|
71
|
+
*
|
|
72
|
+
* 1. Generates a unique session key for this spawn.
|
|
73
|
+
* 2. Pulls recent turns from the parent session (L2 messages).
|
|
74
|
+
* 3. Formats them into a compact block for injection into the task prompt.
|
|
75
|
+
* 4. Optionally chunks and indexes documents as session-scoped doc chunks.
|
|
76
|
+
* 5. Returns the context block, spawn session key, and a summary.
|
|
77
|
+
*
|
|
78
|
+
* Errors are handled gracefully — this function never throws.
|
|
79
|
+
*/
|
|
80
|
+
export async function buildSpawnContext(messageStore, docChunkStore, agentId, options) {
|
|
81
|
+
const sessionKey = `spawn:${agentId}:${Date.now()}`;
|
|
82
|
+
const budgetTokens = options.budgetTokens ?? DEFAULT_BUDGET_TOKENS;
|
|
83
|
+
const snapshotN = Math.min(options.workingSnapshot ?? DEFAULT_WORKING_SNAPSHOT, MAX_WORKING_SNAPSHOT);
|
|
84
|
+
const summary = {
|
|
85
|
+
turnsIncluded: 0,
|
|
86
|
+
documentsIndexed: 0,
|
|
87
|
+
documentsSkipped: [],
|
|
88
|
+
tokenEstimate: 0,
|
|
89
|
+
};
|
|
90
|
+
// ── Step 1: Get recent turns from parent session ─────────────
|
|
91
|
+
let parentContextBlock = null;
|
|
92
|
+
try {
|
|
93
|
+
let turns = messageStore.getRecentTurns(options.parentSessionKey, snapshotN);
|
|
94
|
+
if (turns.length > 0) {
|
|
95
|
+
// Format turns into compact block
|
|
96
|
+
const formatBlock = (ts) => {
|
|
97
|
+
const lines = [
|
|
98
|
+
`## Parent Session Context`,
|
|
99
|
+
`[Last ${ts.length} turns from session ${options.parentSessionKey}]`,
|
|
100
|
+
'',
|
|
101
|
+
];
|
|
102
|
+
for (const turn of ts) {
|
|
103
|
+
const roleLabel = turn.role === 'user' ? 'User' : 'Assistant';
|
|
104
|
+
const content = turn.content?.trim() || '(empty)';
|
|
105
|
+
lines.push(`${roleLabel}: ${content}`);
|
|
106
|
+
}
|
|
107
|
+
return lines.join('\n');
|
|
108
|
+
};
|
|
109
|
+
let block = formatBlock(turns);
|
|
110
|
+
let tokenEst = estimateTokens(block);
|
|
111
|
+
// If over 60% of budget, truncate from oldest end
|
|
112
|
+
const maxContextTokens = Math.floor(budgetTokens * 0.6);
|
|
113
|
+
if (tokenEst > maxContextTokens) {
|
|
114
|
+
// Binary-ish approach: drop turns from the front until we fit
|
|
115
|
+
while (turns.length > 1 && estimateTokens(formatBlock(turns)) > maxContextTokens) {
|
|
116
|
+
turns = turns.slice(1);
|
|
117
|
+
}
|
|
118
|
+
block = formatBlock(turns);
|
|
119
|
+
tokenEst = estimateTokens(block);
|
|
120
|
+
}
|
|
121
|
+
if (turns.length > 0) {
|
|
122
|
+
parentContextBlock = block;
|
|
123
|
+
summary.turnsIncluded = turns.length;
|
|
124
|
+
summary.tokenEstimate += tokenEst;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
catch (err) {
|
|
129
|
+
console.warn('[hypermem:spawn-context] Failed to get recent turns:', err.message);
|
|
130
|
+
}
|
|
131
|
+
// ── Step 2: Index documents as session-scoped doc chunks ─────
|
|
132
|
+
const documents = options.documents ?? [];
|
|
133
|
+
for (const docPath of documents) {
|
|
134
|
+
try {
|
|
135
|
+
const content = await readFile(docPath, 'utf8');
|
|
136
|
+
const chunks = chunkText(content);
|
|
137
|
+
if (chunks.length === 0) {
|
|
138
|
+
summary.documentsSkipped.push(docPath);
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
docChunkStore.indexDocChunks(agentId, docPath, chunks, { sessionKey });
|
|
142
|
+
summary.documentsIndexed++;
|
|
143
|
+
// Add rough token estimate for documents
|
|
144
|
+
summary.tokenEstimate += estimateTokens(content);
|
|
145
|
+
}
|
|
146
|
+
catch (err) {
|
|
147
|
+
// File not found or unreadable — skip gracefully
|
|
148
|
+
const msg = err.message;
|
|
149
|
+
console.warn(`[hypermem:spawn-context] Skipping document "${docPath}": ${msg}`);
|
|
150
|
+
summary.documentsSkipped.push(docPath);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return {
|
|
154
|
+
parentContextBlock,
|
|
155
|
+
sessionKey,
|
|
156
|
+
summary,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
//# sourceMappingURL=spawn-context.js.map
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* hypermem System Registry Store
|
|
3
|
+
*
|
|
4
|
+
* Server config, service states, operational flags.
|
|
5
|
+
* Lives in the central library DB.
|
|
6
|
+
* The source of truth for "what's running and what state is it in."
|
|
7
|
+
*/
|
|
8
|
+
import type { DatabaseSync } from 'node:sqlite';
|
|
9
|
+
export interface SystemState {
|
|
10
|
+
id: number;
|
|
11
|
+
category: string;
|
|
12
|
+
key: string;
|
|
13
|
+
value: unknown;
|
|
14
|
+
updatedAt: string;
|
|
15
|
+
updatedBy: string | null;
|
|
16
|
+
ttl: string | null;
|
|
17
|
+
}
|
|
18
|
+
export interface SystemEvent {
|
|
19
|
+
id: number;
|
|
20
|
+
category: string;
|
|
21
|
+
key: string;
|
|
22
|
+
eventType: string;
|
|
23
|
+
oldValue: unknown;
|
|
24
|
+
newValue: unknown;
|
|
25
|
+
agentId: string | null;
|
|
26
|
+
createdAt: string;
|
|
27
|
+
metadata: Record<string, unknown> | null;
|
|
28
|
+
}
|
|
29
|
+
export declare class SystemStore {
|
|
30
|
+
private readonly db;
|
|
31
|
+
constructor(db: DatabaseSync);
|
|
32
|
+
/**
|
|
33
|
+
* Set a system state value. Records a change event if the value changed.
|
|
34
|
+
*/
|
|
35
|
+
set(category: string, key: string, value: unknown, opts?: {
|
|
36
|
+
updatedBy?: string;
|
|
37
|
+
ttl?: string;
|
|
38
|
+
}): SystemState;
|
|
39
|
+
/**
|
|
40
|
+
* Get a system state value.
|
|
41
|
+
*/
|
|
42
|
+
get(category: string, key: string): SystemState | null;
|
|
43
|
+
/**
|
|
44
|
+
* Get all state in a category.
|
|
45
|
+
*/
|
|
46
|
+
getCategory(category: string): SystemState[];
|
|
47
|
+
/**
|
|
48
|
+
* Delete a system state entry.
|
|
49
|
+
*/
|
|
50
|
+
delete(category: string, key: string, agentId?: string): boolean;
|
|
51
|
+
/**
|
|
52
|
+
* Record an event without changing state (e.g., restart, error, warning).
|
|
53
|
+
*/
|
|
54
|
+
recordEvent(category: string, key: string, eventType: string, opts?: {
|
|
55
|
+
agentId?: string;
|
|
56
|
+
metadata?: Record<string, unknown>;
|
|
57
|
+
}): void;
|
|
58
|
+
/**
|
|
59
|
+
* Get recent events for a category/key.
|
|
60
|
+
*/
|
|
61
|
+
getEvents(opts?: {
|
|
62
|
+
category?: string;
|
|
63
|
+
key?: string;
|
|
64
|
+
eventType?: string;
|
|
65
|
+
since?: string;
|
|
66
|
+
limit?: number;
|
|
67
|
+
}): SystemEvent[];
|
|
68
|
+
/**
|
|
69
|
+
* Prune expired TTL entries.
|
|
70
|
+
*/
|
|
71
|
+
pruneExpired(): number;
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=system-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"system-store.d.ts","sourceRoot":"","sources":["../src/system-store.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAMhD,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,OAAO,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;CACpB;AAED,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;IAClB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CAC1C;AAqCD,qBAAa,WAAW;IACV,OAAO,CAAC,QAAQ,CAAC,EAAE;gBAAF,EAAE,EAAE,YAAY;IAE7C;;OAEG;IACH,GAAG,CACD,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,OAAO,EACd,IAAI,CAAC,EAAE;QACL,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,GAAG,CAAC,EAAE,MAAM,CAAC;KACd,GACA,WAAW;IA8Bd;;OAEG;IACH,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI;IAsBtD;;OAEG;IACH,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,WAAW,EAAE;IAW5C;;OAEG;IACH,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO;IAmBhE;;OAEG;IACH,WAAW,CACT,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM,EACX,SAAS,EAAE,MAAM,EACjB,IAAI,CAAC,EAAE;QACL,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KACpC,GACA,IAAI;IAcP;;OAEG;IACH,SAAS,CAAC,IAAI,CAAC,EAAE;QACf,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,GAAG,CAAC,EAAE,MAAM,CAAC;QACb,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,GAAG,WAAW,EAAE;IA4BjB;;OAEG;IACH,YAAY,IAAI,MAAM;CAQvB"}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* hypermem System Registry Store
|
|
3
|
+
*
|
|
4
|
+
* Server config, service states, operational flags.
|
|
5
|
+
* Lives in the central library DB.
|
|
6
|
+
* The source of truth for "what's running and what state is it in."
|
|
7
|
+
*/
|
|
8
|
+
function nowIso() {
|
|
9
|
+
return new Date().toISOString();
|
|
10
|
+
}
|
|
11
|
+
function parseStateRow(row) {
|
|
12
|
+
let value;
|
|
13
|
+
try {
|
|
14
|
+
value = JSON.parse(row.value);
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
value = row.value;
|
|
18
|
+
}
|
|
19
|
+
return {
|
|
20
|
+
id: row.id,
|
|
21
|
+
category: row.category,
|
|
22
|
+
key: row.key,
|
|
23
|
+
value,
|
|
24
|
+
updatedAt: row.updated_at,
|
|
25
|
+
updatedBy: row.updated_by || null,
|
|
26
|
+
ttl: row.ttl || null,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
function parseEventRow(row) {
|
|
30
|
+
let oldValue;
|
|
31
|
+
let newValue;
|
|
32
|
+
let metadata = null;
|
|
33
|
+
try {
|
|
34
|
+
oldValue = row.old_value ? JSON.parse(row.old_value) : null;
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
oldValue = row.old_value;
|
|
38
|
+
}
|
|
39
|
+
try {
|
|
40
|
+
newValue = row.new_value ? JSON.parse(row.new_value) : null;
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
newValue = row.new_value;
|
|
44
|
+
}
|
|
45
|
+
try {
|
|
46
|
+
metadata = row.metadata ? JSON.parse(row.metadata) : null;
|
|
47
|
+
}
|
|
48
|
+
catch { /* ignore */ }
|
|
49
|
+
return {
|
|
50
|
+
id: row.id,
|
|
51
|
+
category: row.category,
|
|
52
|
+
key: row.key,
|
|
53
|
+
eventType: row.event_type,
|
|
54
|
+
oldValue,
|
|
55
|
+
newValue,
|
|
56
|
+
agentId: row.agent_id || null,
|
|
57
|
+
createdAt: row.created_at,
|
|
58
|
+
metadata,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
export class SystemStore {
|
|
62
|
+
db;
|
|
63
|
+
constructor(db) {
|
|
64
|
+
this.db = db;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Set a system state value. Records a change event if the value changed.
|
|
68
|
+
*/
|
|
69
|
+
set(category, key, value, opts) {
|
|
70
|
+
const now = nowIso();
|
|
71
|
+
const valueStr = JSON.stringify(value);
|
|
72
|
+
// Get old value for change tracking
|
|
73
|
+
const old = this.db.prepare('SELECT value FROM system_state WHERE category = ? AND key = ?').get(category, key);
|
|
74
|
+
this.db.prepare(`
|
|
75
|
+
INSERT INTO system_state (category, key, value, updated_at, updated_by, ttl)
|
|
76
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
77
|
+
ON CONFLICT(category, key) DO UPDATE SET
|
|
78
|
+
value = excluded.value,
|
|
79
|
+
updated_at = excluded.updated_at,
|
|
80
|
+
updated_by = excluded.updated_by,
|
|
81
|
+
ttl = COALESCE(excluded.ttl, ttl)
|
|
82
|
+
`).run(category, key, valueStr, now, opts?.updatedBy || null, opts?.ttl || null);
|
|
83
|
+
// Record change event if value actually changed
|
|
84
|
+
if (!old || old.value !== valueStr) {
|
|
85
|
+
this.db.prepare(`
|
|
86
|
+
INSERT INTO system_events (category, key, event_type, old_value, new_value, agent_id, created_at)
|
|
87
|
+
VALUES (?, ?, 'changed', ?, ?, ?, ?)
|
|
88
|
+
`).run(category, key, old?.value || null, valueStr, opts?.updatedBy || null, now);
|
|
89
|
+
}
|
|
90
|
+
return this.get(category, key);
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Get a system state value.
|
|
94
|
+
*/
|
|
95
|
+
get(category, key) {
|
|
96
|
+
const row = this.db.prepare('SELECT * FROM system_state WHERE category = ? AND key = ?').get(category, key);
|
|
97
|
+
if (!row)
|
|
98
|
+
return null;
|
|
99
|
+
// Check TTL
|
|
100
|
+
const state = parseStateRow(row);
|
|
101
|
+
if (state.ttl) {
|
|
102
|
+
const ttlDate = new Date(state.ttl);
|
|
103
|
+
if (ttlDate < new Date()) {
|
|
104
|
+
// Expired — delete and return null
|
|
105
|
+
this.db.prepare('DELETE FROM system_state WHERE category = ? AND key = ?')
|
|
106
|
+
.run(category, key);
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return state;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Get all state in a category.
|
|
114
|
+
*/
|
|
115
|
+
getCategory(category) {
|
|
116
|
+
const rows = this.db.prepare('SELECT * FROM system_state WHERE category = ? ORDER BY key').all(category);
|
|
117
|
+
return rows.map(parseStateRow).filter(s => {
|
|
118
|
+
if (s.ttl && new Date(s.ttl) < new Date())
|
|
119
|
+
return false;
|
|
120
|
+
return true;
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Delete a system state entry.
|
|
125
|
+
*/
|
|
126
|
+
delete(category, key, agentId) {
|
|
127
|
+
const old = this.db.prepare('SELECT value FROM system_state WHERE category = ? AND key = ?').get(category, key);
|
|
128
|
+
const result = this.db.prepare('DELETE FROM system_state WHERE category = ? AND key = ?').run(category, key);
|
|
129
|
+
if (old && result.changes > 0) {
|
|
130
|
+
this.db.prepare(`
|
|
131
|
+
INSERT INTO system_events (category, key, event_type, old_value, agent_id, created_at)
|
|
132
|
+
VALUES (?, ?, 'deleted', ?, ?, ?)
|
|
133
|
+
`).run(category, key, old.value, agentId || null, nowIso());
|
|
134
|
+
}
|
|
135
|
+
return result.changes > 0;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Record an event without changing state (e.g., restart, error, warning).
|
|
139
|
+
*/
|
|
140
|
+
recordEvent(category, key, eventType, opts) {
|
|
141
|
+
this.db.prepare(`
|
|
142
|
+
INSERT INTO system_events (category, key, event_type, agent_id, created_at, metadata)
|
|
143
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
144
|
+
`).run(category, key, eventType, opts?.agentId || null, nowIso(), opts?.metadata ? JSON.stringify(opts.metadata) : null);
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Get recent events for a category/key.
|
|
148
|
+
*/
|
|
149
|
+
getEvents(opts) {
|
|
150
|
+
let sql = 'SELECT * FROM system_events WHERE 1=1';
|
|
151
|
+
const params = [];
|
|
152
|
+
if (opts?.category) {
|
|
153
|
+
sql += ' AND category = ?';
|
|
154
|
+
params.push(opts.category);
|
|
155
|
+
}
|
|
156
|
+
if (opts?.key) {
|
|
157
|
+
sql += ' AND key = ?';
|
|
158
|
+
params.push(opts.key);
|
|
159
|
+
}
|
|
160
|
+
if (opts?.eventType) {
|
|
161
|
+
sql += ' AND event_type = ?';
|
|
162
|
+
params.push(opts.eventType);
|
|
163
|
+
}
|
|
164
|
+
if (opts?.since) {
|
|
165
|
+
sql += ' AND created_at > ?';
|
|
166
|
+
params.push(opts.since);
|
|
167
|
+
}
|
|
168
|
+
sql += ' ORDER BY created_at DESC LIMIT ?';
|
|
169
|
+
params.push(opts?.limit || 50);
|
|
170
|
+
const rows = this.db.prepare(sql).all(...params);
|
|
171
|
+
return rows.map(parseEventRow);
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Prune expired TTL entries.
|
|
175
|
+
*/
|
|
176
|
+
pruneExpired() {
|
|
177
|
+
const now = nowIso();
|
|
178
|
+
const result = this.db.prepare("DELETE FROM system_state WHERE ttl IS NOT NULL AND ttl < ?").run(now);
|
|
179
|
+
return result.changes;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
//# sourceMappingURL=system-store.js.map
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* hypermem Temporal Store
|
|
3
|
+
*
|
|
4
|
+
* Time-range retrieval over indexed facts. Uses the temporal_index table
|
|
5
|
+
* in library.db to answer LoCoMo-style temporal questions:
|
|
6
|
+
* "What happened before X?"
|
|
7
|
+
* "What changed between January and March?"
|
|
8
|
+
* "What was the most recent thing about Y?"
|
|
9
|
+
*
|
|
10
|
+
* occurred_at is initially populated from created_at (ingest time as proxy,
|
|
11
|
+
* confidence=0.5). Future: date extraction from fact text (confidence=0.9).
|
|
12
|
+
*
|
|
13
|
+
* Query path: SQL time-range filter on temporal_index JOIN facts.
|
|
14
|
+
* No vector search involved — purely temporal ordering.
|
|
15
|
+
*/
|
|
16
|
+
import type { DatabaseSync } from 'node:sqlite';
|
|
17
|
+
export interface TemporalFact {
|
|
18
|
+
factId: number;
|
|
19
|
+
agentId: string;
|
|
20
|
+
content: string;
|
|
21
|
+
domain: string | null;
|
|
22
|
+
occurredAt: number;
|
|
23
|
+
ingestAt: number;
|
|
24
|
+
timeRef: string | null;
|
|
25
|
+
confidence: number;
|
|
26
|
+
}
|
|
27
|
+
export interface TemporalQueryOptions {
|
|
28
|
+
/** Start of time range (unix ms). Omit for open-ended. */
|
|
29
|
+
fromMs?: number;
|
|
30
|
+
/** End of time range (unix ms). Omit for open-ended. */
|
|
31
|
+
toMs?: number;
|
|
32
|
+
/** Only return facts from this agent. */
|
|
33
|
+
agentId?: string;
|
|
34
|
+
/** Only return facts with this domain. */
|
|
35
|
+
domain?: string;
|
|
36
|
+
/** Sort order. Default: DESC (most recent first). */
|
|
37
|
+
order?: 'ASC' | 'DESC';
|
|
38
|
+
/** Max results. Default: 20. */
|
|
39
|
+
limit?: number;
|
|
40
|
+
/** Minimum confidence on temporal placement. Default: 0. */
|
|
41
|
+
minConfidence?: number;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Returns true if the query string contains temporal signals.
|
|
45
|
+
*/
|
|
46
|
+
export declare function hasTemporalSignals(query: string): boolean;
|
|
47
|
+
export declare class TemporalStore {
|
|
48
|
+
private readonly db;
|
|
49
|
+
constructor(db: DatabaseSync);
|
|
50
|
+
/**
|
|
51
|
+
* Index a newly created or updated fact into temporal_index.
|
|
52
|
+
* Uses created_at as occurred_at proxy (confidence=0.5).
|
|
53
|
+
* Safe to call multiple times — uses INSERT OR REPLACE.
|
|
54
|
+
*/
|
|
55
|
+
indexFact(factId: number, agentId: string, createdAt: string, opts?: {
|
|
56
|
+
timeRef?: string;
|
|
57
|
+
confidence?: number;
|
|
58
|
+
occurredAt?: number;
|
|
59
|
+
}): void;
|
|
60
|
+
/**
|
|
61
|
+
* Time-range query. Returns facts in temporal order.
|
|
62
|
+
* Joins temporal_index with facts to get content.
|
|
63
|
+
*/
|
|
64
|
+
timeRangeQuery(opts?: TemporalQueryOptions): TemporalFact[];
|
|
65
|
+
/**
|
|
66
|
+
* Get the most recent N facts for an agent (no time bounds).
|
|
67
|
+
* Useful for "what was the last thing about X" style queries.
|
|
68
|
+
*/
|
|
69
|
+
mostRecent(agentId: string, limit?: number): TemporalFact[];
|
|
70
|
+
/**
|
|
71
|
+
* Get fact count in the temporal index for an agent.
|
|
72
|
+
*/
|
|
73
|
+
getIndexedCount(agentId: string): number;
|
|
74
|
+
/**
|
|
75
|
+
* Backfill any facts not yet in the temporal index.
|
|
76
|
+
* Safe to run multiple times. Uses INSERT OR IGNORE.
|
|
77
|
+
*/
|
|
78
|
+
backfill(): number;
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=temporal-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"temporal-store.d.ts","sourceRoot":"","sources":["../src/temporal-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,oBAAoB;IACnC,0DAA0D;IAC1D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,wDAAwD;IACxD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,yCAAyC;IACzC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,0CAA0C;IAC1C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,qDAAqD;IACrD,KAAK,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IACvB,gCAAgC;IAChC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,4DAA4D;IAC5D,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAeD;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAGzD;AAED,qBAAa,aAAa;IACZ,OAAO,CAAC,QAAQ,CAAC,EAAE;gBAAF,EAAE,EAAE,YAAY;IAE7C;;;;OAIG;IACH,SAAS,CACP,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,IAAI,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,GACpE,IAAI;IAkBP;;;OAGG;IACH,cAAc,CAAC,IAAI,GAAE,oBAAyB,GAAG,YAAY,EAAE;IAgE/D;;;OAGG;IACH,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,SAAK,GAAG,YAAY,EAAE;IAIvD;;OAEG;IACH,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM;IAOxC;;;OAGG;IACH,QAAQ,IAAI,MAAM;CAenB"}
|