@clawmem-ai/clawmem 0.1.5 → 0.1.6
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 +1 -1
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/src/conversation.ts +40 -2
- package/src/service.ts +8 -1
package/README.md
CHANGED
|
@@ -53,7 +53,7 @@ Before the workflow can publish successfully, configure the package on npmjs.com
|
|
|
53
53
|
Release flow:
|
|
54
54
|
|
|
55
55
|
1. Bump `package.json` to the version you want to ship.
|
|
56
|
-
2. Create and push a matching tag such as `0.1.
|
|
56
|
+
2. Create and push a matching tag such as `0.1.6`.
|
|
57
57
|
3. GitHub Actions runs `.github/workflows/release.yml` and publishes with OIDC. No long-lived `NPM_TOKEN` secret is required.
|
|
58
58
|
|
|
59
59
|
The workflow intentionally publishes from a tag push instead of `workflow_dispatch`, because npm validates the workflow filename exactly when using trusted publishing.
|
package/openclaw.plugin.json
CHANGED
package/package.json
CHANGED
package/src/conversation.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import fs from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import type { OpenClawPluginApi } from "openclaw/plugin-sdk/core";
|
|
5
|
-
import { AGENT_LABEL_PREFIX, DEFAULT_LABELS, LABEL_ACTIVE, LABEL_CLOSED, SESSION_TITLE_PREFIX } from "./config.js";
|
|
5
|
+
import { AGENT_LABEL_PREFIX, DEFAULT_LABELS, LABEL_ACTIVE, LABEL_CLOSED, SESSION_TITLE_PREFIX, extractLabelNames } from "./config.js";
|
|
6
6
|
import type { GitHubIssueClient } from "./github-client.js";
|
|
7
7
|
import { normalizeMessages, readTranscriptSnapshot } from "./transcript.js";
|
|
8
8
|
import type { ClawMemPluginConfig, NormalizedMessage, SessionMirrorState, TranscriptSnapshot } from "./types.js";
|
|
@@ -36,7 +36,17 @@ export class ConversationMirror {
|
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
async ensureIssue(session: SessionMirrorState, snapshot: TranscriptSnapshot): Promise<void> {
|
|
39
|
-
if (session.issueNumber)
|
|
39
|
+
if (session.issueNumber) {
|
|
40
|
+
const existing = await this.lookupBoundIssue(session);
|
|
41
|
+
if (existing && this.isBoundIssue(session, existing)) {
|
|
42
|
+
session.issueTitle = existing.title?.trim() || session.issueTitle;
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
this.api.logger.warn(
|
|
46
|
+
`clawmem: issue binding for ${session.sessionId} is stale or mismatched (${session.issueNumber}); recreating`,
|
|
47
|
+
);
|
|
48
|
+
this.resetIssueBinding(session);
|
|
49
|
+
}
|
|
40
50
|
const title = `${SESSION_TITLE_PREFIX}${session.sessionId}`;
|
|
41
51
|
const labels = this.buildLabels(session, snapshot, false);
|
|
42
52
|
const body = this.renderBody(session, snapshot, "pending", false);
|
|
@@ -143,9 +153,37 @@ export class ConversationMirror {
|
|
|
143
153
|
} catch { /* directory unreadable */ }
|
|
144
154
|
return null;
|
|
145
155
|
}
|
|
156
|
+
|
|
157
|
+
private async lookupBoundIssue(session: SessionMirrorState): Promise<{ number: number; title?: string; labels?: Array<{ name?: string } | string> } | null> {
|
|
158
|
+
if (!session.issueNumber) return null;
|
|
159
|
+
try {
|
|
160
|
+
return await this.client.getIssue(session.issueNumber);
|
|
161
|
+
} catch (error) {
|
|
162
|
+
if (isNotFoundError(error)) return null;
|
|
163
|
+
throw error;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
private isBoundIssue(session: SessionMirrorState, issue: { title?: string; labels?: Array<{ name?: string } | string> }): boolean {
|
|
168
|
+
const labels = extractLabelNames(issue.labels);
|
|
169
|
+
return labels.includes("type:conversation") && labels.includes(`session:${session.sessionId}`);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
private resetIssueBinding(session: SessionMirrorState): void {
|
|
173
|
+
session.issueNumber = undefined;
|
|
174
|
+
session.issueTitle = undefined;
|
|
175
|
+
session.lastSummaryHash = undefined;
|
|
176
|
+
session.lastMirroredCount = 0;
|
|
177
|
+
session.turnCount = 0;
|
|
178
|
+
session.finalizedAt = undefined;
|
|
179
|
+
}
|
|
146
180
|
}
|
|
147
181
|
|
|
148
182
|
async function fexists(p: string): Promise<boolean> { try { return (await fs.promises.stat(p)).isFile(); } catch { return false; } }
|
|
183
|
+
function isNotFoundError(error: unknown): boolean {
|
|
184
|
+
const text = String(error);
|
|
185
|
+
return text.includes("HTTP 404");
|
|
186
|
+
}
|
|
149
187
|
function parseSummary(raw: string): string {
|
|
150
188
|
const tryParse = (s: string): string | null => {
|
|
151
189
|
try { const p = JSON.parse(s) as { summary?: unknown }; return typeof p?.summary === "string" && p.summary.trim() ? p.summary.trim() : null; }
|
package/src/service.ts
CHANGED
|
@@ -146,7 +146,14 @@ class ClawMemService {
|
|
|
146
146
|
return this.queue.enqueue(sessionId, async () => { await this.ensureLoaded(); return task(); });
|
|
147
147
|
}
|
|
148
148
|
private track<T>(promise: Promise<T>): Promise<T> {
|
|
149
|
-
this.pending.add(promise);
|
|
149
|
+
this.pending.add(promise);
|
|
150
|
+
// Avoid creating a second rejecting promise via finally(); OpenClaw treats
|
|
151
|
+
// unhandled rejections as fatal and exits the gateway process.
|
|
152
|
+
void promise.then(
|
|
153
|
+
() => this.pending.delete(promise),
|
|
154
|
+
() => this.pending.delete(promise),
|
|
155
|
+
);
|
|
156
|
+
return promise;
|
|
150
157
|
}
|
|
151
158
|
private getOrCreate(sessionId: string): SessionMirrorState {
|
|
152
159
|
if (this.state.sessions[sessionId]) return this.state.sessions[sessionId];
|