@clawmem-ai/clawmem 0.1.16 → 0.1.18
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/README.md +6 -7
- package/openclaw.plugin.json +4 -31
- package/package.json +12 -5
- package/skills/clawmem/SKILL.md +5 -5
- package/skills/clawmem/references/collaboration.md +43 -1
- package/skills/clawmem/references/schema.md +2 -1
- package/src/config.test.ts +0 -3
- package/src/config.ts +0 -3
- package/src/conversation.test.ts +63 -13
- package/src/conversation.ts +89 -392
- package/src/github-client.test.ts +101 -0
- package/src/github-client.ts +59 -0
- package/src/memory.test.ts +131 -46
- package/src/memory.ts +81 -392
- package/src/service.test.ts +188 -0
- package/src/service.ts +804 -419
- package/src/state.test.ts +47 -16
- package/src/state.ts +87 -119
- package/src/types.ts +9 -26
- package/tsconfig.json +1 -0
package/src/state.test.ts
CHANGED
|
@@ -36,13 +36,10 @@ async function testMigratesLegacyV2State(): Promise<void> {
|
|
|
36
36
|
}, async (filePath) => {
|
|
37
37
|
const state = await loadState(filePath);
|
|
38
38
|
const session = state.sessions["main:s-1"];
|
|
39
|
-
assert(state.version ===
|
|
39
|
+
assert(state.version === 4, "expected state version 4 after migration");
|
|
40
40
|
assert(Boolean(session), "expected migrated session to exist");
|
|
41
|
-
assert(session?.derived?.
|
|
42
|
-
assert(session?.derived?.
|
|
43
|
-
assert(session?.derived?.summary.status === "pending", "expected finalized legacy sessions to keep summary pending");
|
|
44
|
-
assert(session?.derived?.memory.appliedCursor === 4, "expected legacy memory sync cursor to migrate into appliedCursor");
|
|
45
|
-
assert(session?.lastMemorySyncCount === 4, "expected compatibility field to mirror appliedCursor");
|
|
41
|
+
assert(session?.derived?.summary.status === "error", "expected finalized legacy sessions without a final summary to surface as needing manual attention");
|
|
42
|
+
assert(session?.derived?.memory.capturedCursor === 4, "expected legacy memory sync cursor to migrate into capturedCursor");
|
|
46
43
|
});
|
|
47
44
|
}
|
|
48
45
|
|
|
@@ -56,17 +53,12 @@ async function testNormalizesRunningTaskStates(): Promise<void> {
|
|
|
56
53
|
lastMirroredCount: 3,
|
|
57
54
|
turnCount: 3,
|
|
58
55
|
derived: {
|
|
59
|
-
digest: { cursor: 1, status: "running", attempt: 2, text: "digest" },
|
|
60
56
|
summary: { basedOnCursor: 0, status: "running" },
|
|
61
57
|
memory: {
|
|
62
58
|
extractCursor: 1,
|
|
63
59
|
appliedCursor: 0,
|
|
64
60
|
extractStatus: "running",
|
|
65
61
|
reconcileStatus: "running",
|
|
66
|
-
attempt: 1,
|
|
67
|
-
pendingCandidates: [
|
|
68
|
-
{ candidateId: "abc", detail: "Remember Redis is atomic with Lua." },
|
|
69
|
-
],
|
|
70
62
|
},
|
|
71
63
|
},
|
|
72
64
|
},
|
|
@@ -74,15 +66,54 @@ async function testNormalizesRunningTaskStates(): Promise<void> {
|
|
|
74
66
|
}, async (filePath) => {
|
|
75
67
|
const state = await loadState(filePath);
|
|
76
68
|
const session = state.sessions["main:s-2"];
|
|
77
|
-
assert(session?.derived?.
|
|
78
|
-
assert(session?.derived?.
|
|
79
|
-
assert(session?.derived?.memory.
|
|
80
|
-
|
|
81
|
-
|
|
69
|
+
assert(session?.derived?.summary.status === "idle", "expected running summary tasks to normalize to idle on load");
|
|
70
|
+
assert(session?.derived?.memory.status === "idle", "expected running memory tasks to normalize to idle on load");
|
|
71
|
+
assert(session?.derived?.memory.capturedCursor === 0, "expected captured cursor to preserve the applied progress");
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async function testPreservesCachedFinalArtifacts(): Promise<void> {
|
|
76
|
+
await withTempStateFile({
|
|
77
|
+
version: 4,
|
|
78
|
+
sessions: {
|
|
79
|
+
"main:s-3": {
|
|
80
|
+
sessionId: "s-3",
|
|
81
|
+
agentId: "main",
|
|
82
|
+
lastMirroredCount: 5,
|
|
83
|
+
turnCount: 5,
|
|
84
|
+
derived: {
|
|
85
|
+
summary: {
|
|
86
|
+
basedOnCursor: 5,
|
|
87
|
+
status: "idle",
|
|
88
|
+
text: "Recovered summary",
|
|
89
|
+
title: "Recovered title",
|
|
90
|
+
},
|
|
91
|
+
memory: {
|
|
92
|
+
capturedCursor: 0,
|
|
93
|
+
status: "error",
|
|
94
|
+
candidates: [
|
|
95
|
+
{
|
|
96
|
+
candidateId: "cand-1",
|
|
97
|
+
detail: "Store this durable fact.",
|
|
98
|
+
kind: "lesson",
|
|
99
|
+
topics: ["redis"],
|
|
100
|
+
},
|
|
101
|
+
],
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
}, async (filePath) => {
|
|
107
|
+
const state = await loadState(filePath);
|
|
108
|
+
const session = state.sessions["main:s-3"];
|
|
109
|
+
assert(session?.derived?.summary.title === "Recovered title", "expected cached finalize title to survive state load");
|
|
110
|
+
assert(session?.derived?.memory.candidates?.length === 1, "expected cached memory candidates to survive state load");
|
|
111
|
+
assert(session?.derived?.memory.candidates?.[0]?.detail === "Store this durable fact.", "expected cached candidate detail to survive state load");
|
|
82
112
|
});
|
|
83
113
|
}
|
|
84
114
|
|
|
85
115
|
await testMigratesLegacyV2State();
|
|
86
116
|
await testNormalizesRunningTaskStates();
|
|
117
|
+
await testPreservesCachedFinalArtifacts();
|
|
87
118
|
|
|
88
119
|
console.log("state tests passed");
|
package/src/state.ts
CHANGED
|
@@ -4,7 +4,7 @@ import type { MemoryCandidate, PluginState, SessionDerivedState, SessionMirrorSt
|
|
|
4
4
|
import { normalizeAgentId, sessionScopeKey } from "./utils.js";
|
|
5
5
|
|
|
6
6
|
const EMPTY_STATE: PluginState = {
|
|
7
|
-
version:
|
|
7
|
+
version: 4,
|
|
8
8
|
sessions: {},
|
|
9
9
|
};
|
|
10
10
|
|
|
@@ -38,10 +38,9 @@ function sanitizeState(value: unknown): PluginState {
|
|
|
38
38
|
return structuredClone(EMPTY_STATE);
|
|
39
39
|
}
|
|
40
40
|
const raw = value as Record<string, unknown>;
|
|
41
|
-
const sessions =
|
|
42
|
-
raw.sessions
|
|
43
|
-
|
|
44
|
-
: {};
|
|
41
|
+
const sessions = raw.sessions && typeof raw.sessions === "object"
|
|
42
|
+
? (raw.sessions as Record<string, unknown>)
|
|
43
|
+
: {};
|
|
45
44
|
const migrations: Record<string, string> = {};
|
|
46
45
|
if (raw.migrations && typeof raw.migrations === "object") {
|
|
47
46
|
for (const [k, v] of Object.entries(raw.migrations as Record<string, unknown>)) {
|
|
@@ -50,30 +49,19 @@ function sanitizeState(value: unknown): PluginState {
|
|
|
50
49
|
}
|
|
51
50
|
}
|
|
52
51
|
const out: PluginState = {
|
|
53
|
-
version:
|
|
52
|
+
version: 4,
|
|
54
53
|
sessions: {},
|
|
55
54
|
...(Object.keys(migrations).length > 0 ? { migrations } : {}),
|
|
56
55
|
};
|
|
57
56
|
for (const [storedKey, sessionValue] of Object.entries(sessions)) {
|
|
58
|
-
if (!sessionValue || typeof sessionValue !== "object" || !storedKey.trim())
|
|
59
|
-
continue;
|
|
60
|
-
}
|
|
57
|
+
if (!sessionValue || typeof sessionValue !== "object" || !storedKey.trim()) continue;
|
|
61
58
|
const rawSession = sessionValue as Record<string, unknown>;
|
|
62
59
|
const sessionId = readString(rawSession.sessionId) ?? storedKey.trim();
|
|
63
|
-
if (!sessionId)
|
|
64
|
-
continue;
|
|
65
|
-
}
|
|
60
|
+
if (!sessionId) continue;
|
|
66
61
|
const agentId = normalizeAgentId(readString(rawSession.agentId));
|
|
67
62
|
const lastMirroredCount = readNumber(rawSession.lastMirroredCount) ?? 0;
|
|
68
63
|
const finalizedAt = readString(rawSession.finalizedAt);
|
|
69
|
-
const
|
|
70
|
-
const lastMemorySyncCount = readNumber(rawSession.lastMemorySyncCount);
|
|
71
|
-
const derived = sanitizeDerivedState(rawSession.derived, {
|
|
72
|
-
lastMirroredCount,
|
|
73
|
-
finalizedAt,
|
|
74
|
-
summaryStatus,
|
|
75
|
-
lastMemorySyncCount,
|
|
76
|
-
});
|
|
64
|
+
const derived = sanitizeDerivedState(rawSession, lastMirroredCount, finalizedAt);
|
|
77
65
|
out.sessions[sessionScopeKey(sessionId, agentId)] = {
|
|
78
66
|
sessionId,
|
|
79
67
|
sessionKey: readString(rawSession.sessionKey),
|
|
@@ -81,14 +69,11 @@ function sanitizeState(value: unknown): PluginState {
|
|
|
81
69
|
agentId,
|
|
82
70
|
issueNumber: readNumber(rawSession.issueNumber),
|
|
83
71
|
issueTitle: readString(rawSession.issueTitle),
|
|
84
|
-
titleSource:
|
|
72
|
+
titleSource: readTitleSource(rawSession.titleSource),
|
|
85
73
|
lastMirroredCount,
|
|
86
74
|
turnCount: readNumber(rawSession.turnCount) ?? 0,
|
|
87
|
-
lastMemorySyncCount: derived.memory.appliedCursor,
|
|
88
|
-
summaryStatus: derived.summary.status === "complete" ? "complete" : finalizedAt ? "pending" : undefined,
|
|
89
75
|
finalizedAt,
|
|
90
76
|
lastSummaryHash: readString(rawSession.lastSummaryHash),
|
|
91
|
-
lastTurnHash: readString(rawSession.lastTurnHash),
|
|
92
77
|
derived,
|
|
93
78
|
createdAt: readString(rawSession.createdAt),
|
|
94
79
|
updatedAt: readString(rawSession.updatedAt),
|
|
@@ -98,92 +83,62 @@ function sanitizeState(value: unknown): PluginState {
|
|
|
98
83
|
}
|
|
99
84
|
|
|
100
85
|
function sanitizeDerivedState(
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
finalizedAt?: string;
|
|
105
|
-
summaryStatus?: "pending" | "complete";
|
|
106
|
-
lastMemorySyncCount?: number;
|
|
107
|
-
},
|
|
86
|
+
rawSession: Record<string, unknown>,
|
|
87
|
+
lastMirroredCount: number,
|
|
88
|
+
finalizedAt?: string,
|
|
108
89
|
): SessionDerivedState {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
const
|
|
113
|
-
const
|
|
114
|
-
|
|
115
|
-
const
|
|
116
|
-
const
|
|
117
|
-
const
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
extractStatus: readTaskStatus(memory?.extractStatus, extractCursor < lastMirroredCount ? "pending" : "idle"),
|
|
140
|
-
reconcileStatus: readTaskStatus(memory?.reconcileStatus, appliedCursor < extractCursor ? "pending" : "idle"),
|
|
141
|
-
attempt: readNumber(memory?.attempt) ?? 0,
|
|
142
|
-
pendingCandidates: Array.isArray(memory?.pendingCandidates)
|
|
143
|
-
? memory.pendingCandidates.map(sanitizeCandidate).filter((candidate): candidate is MemoryCandidate => Boolean(candidate))
|
|
144
|
-
: [],
|
|
145
|
-
lastError: readString(memory?.lastError),
|
|
146
|
-
updatedAt: readString(memory?.updatedAt),
|
|
147
|
-
},
|
|
148
|
-
};
|
|
149
|
-
}
|
|
90
|
+
const rawDerived = asRecord(rawSession.derived);
|
|
91
|
+
const rawSummary = asRecord(rawDerived?.summary);
|
|
92
|
+
const rawMemory = asRecord(rawDerived?.memory);
|
|
93
|
+
const legacySummaryStatus = readEnum(rawSession.summaryStatus, ["pending", "complete"]);
|
|
94
|
+
const legacyMemoryCursor = readNumber(rawSession.lastMemorySyncCount);
|
|
95
|
+
|
|
96
|
+
const summaryText = readString(rawSummary?.text);
|
|
97
|
+
const summaryTitle = readString(rawSummary?.title);
|
|
98
|
+
const summaryStatus = readTaskStatus(
|
|
99
|
+
rawSummary?.status,
|
|
100
|
+
summaryText || legacySummaryStatus === "complete" ? "complete" : "idle",
|
|
101
|
+
);
|
|
102
|
+
const summaryCursor = clampCursor(
|
|
103
|
+
readNumber(rawSummary?.basedOnCursor),
|
|
104
|
+
summaryStatus === "complete" ? lastMirroredCount : 0,
|
|
105
|
+
lastMirroredCount,
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
const capturedCursor = clampCursor(
|
|
109
|
+
readNumber(rawMemory?.capturedCursor)
|
|
110
|
+
?? readNumber(rawMemory?.appliedCursor)
|
|
111
|
+
?? legacyMemoryCursor,
|
|
112
|
+
summaryStatus === "complete" ? lastMirroredCount : 0,
|
|
113
|
+
lastMirroredCount,
|
|
114
|
+
);
|
|
115
|
+
const memoryStatus = readTaskStatus(
|
|
116
|
+
rawMemory?.status ?? rawMemory?.extractStatus ?? rawMemory?.reconcileStatus,
|
|
117
|
+
capturedCursor >= lastMirroredCount && lastMirroredCount > 0 ? "complete" : "idle",
|
|
118
|
+
);
|
|
119
|
+
const candidates = readMemoryCandidates(rawMemory?.candidates);
|
|
150
120
|
|
|
151
|
-
function migrateDerivedState(fallback: {
|
|
152
|
-
lastMirroredCount: number;
|
|
153
|
-
finalizedAt?: string;
|
|
154
|
-
summaryStatus?: "pending" | "complete";
|
|
155
|
-
lastMemorySyncCount?: number;
|
|
156
|
-
}): SessionDerivedState {
|
|
157
|
-
const mirrorCursor = fallback.lastMirroredCount;
|
|
158
|
-
const finalized = Boolean(fallback.finalizedAt);
|
|
159
|
-
const summaryComplete = fallback.summaryStatus === "complete";
|
|
160
|
-
const appliedCursor = clampCursor(fallback.lastMemorySyncCount, mirrorCursor);
|
|
161
|
-
const digestCursor = finalized && summaryComplete ? mirrorCursor : 0;
|
|
162
121
|
return {
|
|
163
|
-
digest: {
|
|
164
|
-
cursor: digestCursor,
|
|
165
|
-
status: digestCursor < mirrorCursor ? "pending" : "idle",
|
|
166
|
-
attempt: 0,
|
|
167
|
-
},
|
|
168
122
|
summary: {
|
|
169
|
-
basedOnCursor:
|
|
170
|
-
status:
|
|
123
|
+
basedOnCursor: summaryCursor,
|
|
124
|
+
status: finalizedAt && summaryStatus === "idle" && lastMirroredCount > 0 ? "error" : summaryStatus,
|
|
125
|
+
...(summaryText ? { text: summaryText } : {}),
|
|
126
|
+
...(summaryTitle ? { title: summaryTitle } : {}),
|
|
127
|
+
...(readString(rawSummary?.lastError) ? { lastError: readString(rawSummary?.lastError) } : {}),
|
|
128
|
+
...(readString(rawSummary?.updatedAt) ? { updatedAt: readString(rawSummary?.updatedAt) } : {}),
|
|
171
129
|
},
|
|
172
130
|
memory: {
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
pendingCandidates: [],
|
|
131
|
+
capturedCursor,
|
|
132
|
+
status: memoryStatus,
|
|
133
|
+
...(candidates ? { candidates } : {}),
|
|
134
|
+
...(readString(rawMemory?.lastError) ? { lastError: readString(rawMemory?.lastError) } : {}),
|
|
135
|
+
...(readString(rawMemory?.updatedAt) ? { updatedAt: readString(rawMemory?.updatedAt) } : {}),
|
|
179
136
|
},
|
|
180
137
|
};
|
|
181
138
|
}
|
|
182
139
|
|
|
183
140
|
function readString(value: unknown): string | undefined {
|
|
184
|
-
if (typeof value !== "string")
|
|
185
|
-
return undefined;
|
|
186
|
-
}
|
|
141
|
+
if (typeof value !== "string") return undefined;
|
|
187
142
|
const trimmed = value.trim();
|
|
188
143
|
return trimmed ? trimmed : undefined;
|
|
189
144
|
}
|
|
@@ -194,42 +149,55 @@ function readEnum<T extends string>(value: unknown, allowed: T[]): T | undefined
|
|
|
194
149
|
}
|
|
195
150
|
|
|
196
151
|
function readNumber(value: unknown): number | undefined {
|
|
197
|
-
if (typeof value !== "number" || !Number.isFinite(value))
|
|
198
|
-
return undefined;
|
|
199
|
-
}
|
|
152
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return undefined;
|
|
200
153
|
return Math.max(0, Math.floor(value));
|
|
201
154
|
}
|
|
202
155
|
|
|
203
156
|
function readTaskStatus(value: unknown, fallback: SessionTaskStatus): SessionTaskStatus {
|
|
204
157
|
const status = readEnum(value, ["idle", "pending", "running", "complete", "error"]);
|
|
205
158
|
if (!status) return fallback;
|
|
206
|
-
|
|
159
|
+
if (status === "pending" || status === "running") return "idle";
|
|
160
|
+
return status;
|
|
207
161
|
}
|
|
208
162
|
|
|
209
|
-
function
|
|
163
|
+
function readTitleSource(value: unknown): "placeholder" | "llm" | undefined {
|
|
164
|
+
const source = readEnum(value, ["placeholder", "digest", "llm"]);
|
|
165
|
+
if (!source) return undefined;
|
|
166
|
+
return source === "digest" ? "llm" : source;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function readMemoryCandidates(value: unknown): MemoryCandidate[] | undefined {
|
|
170
|
+
if (!Array.isArray(value)) return undefined;
|
|
171
|
+
const out = value
|
|
172
|
+
.map((entry) => sanitizeMemoryCandidate(entry))
|
|
173
|
+
.filter((candidate): candidate is MemoryCandidate => candidate !== null);
|
|
174
|
+
return out;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function sanitizeMemoryCandidate(value: unknown): MemoryCandidate | null {
|
|
210
178
|
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
211
179
|
const record = value as Record<string, unknown>;
|
|
212
|
-
const detail = readString(record.detail);
|
|
213
180
|
const candidateId = readString(record.candidateId);
|
|
214
|
-
|
|
181
|
+
const detail = readString(record.detail);
|
|
182
|
+
if (!candidateId || !detail) return null;
|
|
183
|
+
const title = readString(record.title);
|
|
184
|
+
const kind = readString(record.kind);
|
|
185
|
+
const topics = Array.isArray(record.topics)
|
|
186
|
+
? record.topics.map((topic) => readString(topic)).filter((topic): topic is string => Boolean(topic))
|
|
187
|
+
: undefined;
|
|
188
|
+
const evidence = readString(record.evidence);
|
|
215
189
|
return {
|
|
216
|
-
candidateId
|
|
190
|
+
candidateId,
|
|
217
191
|
detail,
|
|
218
|
-
...(
|
|
219
|
-
...(
|
|
220
|
-
...(
|
|
221
|
-
|
|
222
|
-
topics: record.topics
|
|
223
|
-
.map((topic) => readString(topic))
|
|
224
|
-
.filter((topic): topic is string => Boolean(topic)),
|
|
225
|
-
}
|
|
226
|
-
: {}),
|
|
227
|
-
...(readString(record.evidence) ? { evidence: readString(record.evidence) } : {}),
|
|
192
|
+
...(title ? { title } : {}),
|
|
193
|
+
...(kind ? { kind } : {}),
|
|
194
|
+
...(topics && topics.length > 0 ? { topics } : {}),
|
|
195
|
+
...(evidence ? { evidence } : {}),
|
|
228
196
|
};
|
|
229
197
|
}
|
|
230
198
|
|
|
231
|
-
function clampCursor(value: number | undefined, max: number): number {
|
|
232
|
-
if (typeof value !== "number" || !Number.isFinite(value)) return 0;
|
|
199
|
+
function clampCursor(value: number | undefined, fallback: number, max: number): number {
|
|
200
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return Math.min(max, Math.max(0, fallback));
|
|
233
201
|
return Math.min(max, Math.max(0, Math.floor(value)));
|
|
234
202
|
}
|
|
235
203
|
|
package/src/types.ts
CHANGED
|
@@ -16,11 +16,8 @@ export type ClawMemPluginConfig = {
|
|
|
16
16
|
agents: Record<string, ClawMemAgentConfig>;
|
|
17
17
|
memoryRecallLimit: number;
|
|
18
18
|
memoryAutoRecallLimit: number;
|
|
19
|
-
turnCommentDelayMs: number;
|
|
20
|
-
digestWaitTimeoutMs: number;
|
|
21
19
|
summaryWaitTimeoutMs: number;
|
|
22
20
|
memoryExtractWaitTimeoutMs: number;
|
|
23
|
-
memoryReconcileWaitTimeoutMs: number;
|
|
24
21
|
};
|
|
25
22
|
|
|
26
23
|
export type ClawMemResolvedRoute = {
|
|
@@ -35,7 +32,7 @@ export type ClawMemResolvedRoute = {
|
|
|
35
32
|
export type BootstrapIdentityResponse = { token: string; repo_full_name: string };
|
|
36
33
|
export type AgentRegistrationResponse = BootstrapIdentityResponse & { login: string };
|
|
37
34
|
export type AnonymousSessionResponse = BootstrapIdentityResponse & { owner_login: string; repo_name: string };
|
|
38
|
-
export type SessionTaskStatus = "idle" | "
|
|
35
|
+
export type SessionTaskStatus = "idle" | "complete" | "error";
|
|
39
36
|
export type MemoryCandidate = {
|
|
40
37
|
candidateId: string;
|
|
41
38
|
detail: string;
|
|
@@ -44,48 +41,34 @@ export type MemoryCandidate = {
|
|
|
44
41
|
topics?: string[];
|
|
45
42
|
evidence?: string;
|
|
46
43
|
};
|
|
47
|
-
export type SessionDigestState = {
|
|
48
|
-
cursor: number;
|
|
49
|
-
status: SessionTaskStatus;
|
|
50
|
-
attempt: number;
|
|
51
|
-
text?: string;
|
|
52
|
-
title?: string;
|
|
53
|
-
lastError?: string;
|
|
54
|
-
updatedAt?: string;
|
|
55
|
-
};
|
|
56
44
|
export type SessionSummaryState = {
|
|
57
45
|
basedOnCursor: number;
|
|
58
46
|
status: SessionTaskStatus;
|
|
59
47
|
text?: string;
|
|
48
|
+
title?: string;
|
|
60
49
|
lastError?: string;
|
|
61
50
|
updatedAt?: string;
|
|
62
51
|
};
|
|
63
52
|
export type SessionMemoryState = {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
reconcileStatus: SessionTaskStatus;
|
|
68
|
-
attempt: number;
|
|
69
|
-
pendingCandidates: MemoryCandidate[];
|
|
53
|
+
capturedCursor: number;
|
|
54
|
+
status: SessionTaskStatus;
|
|
55
|
+
candidates?: MemoryCandidate[];
|
|
70
56
|
lastError?: string;
|
|
71
57
|
updatedAt?: string;
|
|
72
58
|
};
|
|
73
59
|
export type SessionDerivedState = {
|
|
74
|
-
digest: SessionDigestState;
|
|
75
60
|
summary: SessionSummaryState;
|
|
76
61
|
memory: SessionMemoryState;
|
|
77
62
|
};
|
|
78
63
|
export type SessionMirrorState = {
|
|
79
64
|
sessionId: string; sessionKey?: string; sessionFile?: string; agentId?: string;
|
|
80
|
-
issueNumber?: number; issueTitle?: string; titleSource?: "placeholder" | "
|
|
81
|
-
lastMirroredCount: number; turnCount: number;
|
|
82
|
-
|
|
83
|
-
summaryStatus?: "pending" | "complete";
|
|
84
|
-
finalizedAt?: string; lastSummaryHash?: string; lastTurnHash?: string;
|
|
65
|
+
issueNumber?: number; issueTitle?: string; titleSource?: "placeholder" | "llm";
|
|
66
|
+
lastMirroredCount: number; turnCount: number;
|
|
67
|
+
finalizedAt?: string; lastSummaryHash?: string;
|
|
85
68
|
derived?: SessionDerivedState;
|
|
86
69
|
createdAt?: string; updatedAt?: string;
|
|
87
70
|
};
|
|
88
|
-
export type PluginState = { version:
|
|
71
|
+
export type PluginState = { version: 4; sessions: Record<string, SessionMirrorState>; migrations?: Record<string, string> };
|
|
89
72
|
export type NormalizedMessage = { role: string; text: string; toolName?: string; timestamp?: string; stopReason?: string };
|
|
90
73
|
export type TranscriptSnapshot = { sessionId?: string; messages: NormalizedMessage[] };
|
|
91
74
|
export type MemoryDraft = { title?: string; detail: string; kind?: string; topics?: string[] };
|