opencode-engram 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,111 @@
1
+ import { computeTurns, type TurnComputeItem } from "../domain/domain.ts";
2
+ import type { PluginInput } from "../common/common.ts";
3
+ import {
4
+ buildFingerprint,
5
+ clearTurnCache,
6
+ getTurnMapWithCache,
7
+ type SessionFingerprint,
8
+ } from "../core/turn-index.ts";
9
+ import type { SessionTarget } from "../core/index.ts";
10
+ import type { Logger } from "./logger.ts";
11
+ import {
12
+ type MessagePage,
13
+ getAllMessages,
14
+ internalScanPageSize,
15
+ } from "./message-io.ts";
16
+
17
+ /**
18
+ * Build a session fingerprint for turn cache invalidation.
19
+ *
20
+ * Uses version and updated time from session metadata.
21
+ * Returns undefined if metadata is unavailable.
22
+ */
23
+ export function getSessionFingerprint(
24
+ root: SessionTarget["session"],
25
+ ): SessionFingerprint | undefined {
26
+ const version = root.version;
27
+ const updated = root.updatedAt;
28
+ if (version === undefined || updated === undefined) {
29
+ return undefined;
30
+ }
31
+
32
+ return buildFingerprint(version, updated);
33
+ }
34
+
35
+ /**
36
+ * Fetch turn items from all messages in the upstream history.
37
+ *
38
+ * This is the fetch callback used by turn cache operations.
39
+ * Optionally accepts a seed page to avoid re-fetching the first page.
40
+ */
41
+ export async function fetchTurnItems(
42
+ input: PluginInput,
43
+ sessionId: string,
44
+ scanPageSize: number,
45
+ seedPage?: MessagePage,
46
+ ): Promise<TurnComputeItem[]> {
47
+ const allMessages = await getAllMessages(input, sessionId, scanPageSize, seedPage);
48
+ return allMessages.map((msg): TurnComputeItem => ({
49
+ id: msg.info.id,
50
+ role: msg.info.role as "user" | "assistant",
51
+ time: msg.info.time.created,
52
+ }));
53
+ }
54
+
55
+ /**
56
+ * Retrieve a turn map with cache, falling back to rebuild if needed.
57
+ *
58
+ * Handles the common pattern of:
59
+ * 1. Attempt cache hit
60
+ * 2. Check for missing IDs
61
+ * 3. Clear cache and rebuild if stale
62
+ *
63
+ * @param input Plugin input for message fetching
64
+ * @param target Resolved session target metadata
65
+ * @param seedPage Optional seed page to avoid duplicate fetches
66
+ * @param requiredIds Message IDs that must be present in the turn map
67
+ * @param journal Logger for debug output
68
+ * @returns Turn map guaranteed to contain all required IDs (or throws)
69
+ */
70
+ export async function getTurnMapWithFallback(
71
+ input: PluginInput,
72
+ target: SessionTarget,
73
+ seedPage: MessagePage | undefined,
74
+ requiredIds: string[],
75
+ journal: Logger,
76
+ ): Promise<Map<string, number>> {
77
+ const targetSession = target.session;
78
+ const fingerprint = getSessionFingerprint(targetSession);
79
+
80
+ const { turnMap: initialTurnMap } = await getTurnMapWithCache(
81
+ targetSession.id,
82
+ fingerprint,
83
+ () => fetchTurnItems(input, targetSession.id, internalScanPageSize, seedPage),
84
+ computeTurns,
85
+ journal,
86
+ );
87
+
88
+ // Check if all required IDs are present
89
+ const missingIds = requiredIds.filter((id) => initialTurnMap.get(id) === undefined);
90
+ if (missingIds.length === 0) {
91
+ return initialTurnMap;
92
+ }
93
+
94
+ // Cache is stale - clear and rebuild
95
+ journal.debug("turn map missing required ids, rebuilding from full scan", {
96
+ targetSessionID: targetSession.id,
97
+ missingCount: missingIds.length,
98
+ missingIDs: missingIds,
99
+ });
100
+
101
+ clearTurnCache(targetSession.id);
102
+ const { turnMap: rebuiltTurnMap } = await getTurnMapWithCache(
103
+ targetSession.id,
104
+ fingerprint,
105
+ () => fetchTurnItems(input, targetSession.id, internalScanPageSize, seedPage),
106
+ computeTurns,
107
+ journal,
108
+ );
109
+
110
+ return rebuiltTurnMap;
111
+ }