@clawmem-ai/clawmem 0.1.17 → 0.1.19

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.
Files changed (60) hide show
  1. package/README.md +29 -9
  2. package/dist/index.d.ts +2 -0
  3. package/dist/index.js +4 -0
  4. package/dist/src/collaboration.d.ts +49 -0
  5. package/dist/src/collaboration.js +69 -0
  6. package/dist/src/config.d.ts +21 -0
  7. package/dist/src/config.js +119 -0
  8. package/dist/src/conversation.d.ts +30 -0
  9. package/dist/src/conversation.js +323 -0
  10. package/dist/src/github-client.d.ts +269 -0
  11. package/dist/src/github-client.js +350 -0
  12. package/dist/src/keyed-async-queue.d.ts +12 -0
  13. package/dist/src/keyed-async-queue.js +23 -0
  14. package/dist/src/memory.d.ts +29 -0
  15. package/dist/src/memory.js +451 -0
  16. package/dist/src/recall-sanitize.d.ts +1 -0
  17. package/dist/src/recall-sanitize.js +149 -0
  18. package/dist/src/runtime-env.d.ts +2 -0
  19. package/dist/src/runtime-env.js +12 -0
  20. package/dist/src/service.d.ts +18 -0
  21. package/dist/src/service.js +3645 -0
  22. package/dist/src/state.d.ts +4 -0
  23. package/dist/src/state.js +182 -0
  24. package/dist/src/transcript.d.ts +3 -0
  25. package/dist/src/transcript.js +164 -0
  26. package/dist/src/types.d.ts +130 -0
  27. package/dist/src/types.js +1 -0
  28. package/dist/src/utils.d.ts +26 -0
  29. package/dist/src/utils.js +62 -0
  30. package/dist/src/yaml.d.ts +2 -0
  31. package/dist/src/yaml.js +81 -0
  32. package/openclaw.plugin.json +14 -1
  33. package/package.json +21 -7
  34. package/skills/clawmem/SKILL.md +26 -5
  35. package/skills/clawmem/references/collaboration.md +13 -5
  36. package/skills/clawmem/references/review.md +77 -0
  37. package/skills/clawmem/references/schema.md +44 -1
  38. package/index.ts +0 -6
  39. package/src/collaboration.test.ts +0 -71
  40. package/src/collaboration.ts +0 -109
  41. package/src/config.test.ts +0 -83
  42. package/src/config.ts +0 -117
  43. package/src/conversation.test.ts +0 -120
  44. package/src/conversation.ts +0 -304
  45. package/src/github-client.test.ts +0 -101
  46. package/src/github-client.ts +0 -363
  47. package/src/keyed-async-queue.ts +0 -26
  48. package/src/memory.test.ts +0 -588
  49. package/src/memory.ts +0 -444
  50. package/src/recall-sanitize.ts +0 -143
  51. package/src/runtime-env.ts +0 -12
  52. package/src/service.test.ts +0 -267
  53. package/src/service.ts +0 -2710
  54. package/src/state.test.ts +0 -119
  55. package/src/state.ts +0 -206
  56. package/src/transcript.ts +0 -186
  57. package/src/types.ts +0 -86
  58. package/src/utils.ts +0 -74
  59. package/src/yaml.ts +0 -88
  60. package/tsconfig.json +0 -14
package/README.md CHANGED
@@ -7,6 +7,8 @@
7
7
  - On session start: searches active memories by relevance and injects them into context.
8
8
  - On session reset/end: best-effort writes a final conversation summary/title and stores durable memory candidates as `type:memory` issues.
9
9
  - Lets agents inspect memory indexes and schema, fetch exact memories, update canonical facts in place, and write structured memories with `kind:*` and `topic:*` labels through plugin tools.
10
+ - Adds atomic collaboration tools for organizations, repos, teams, invitations, access, issues, and comments.
11
+ - Does not ship any built-in Team workflow or Team template.
10
12
 
11
13
  ---
12
14
 
@@ -20,17 +22,29 @@ openclaw config validate
20
22
  openclaw gateway restart
21
23
  ```
22
24
 
23
- After restart, confirm OpenClaw shows ClawMem as the active memory plugin. On first use, clawmem bootstraps each agent identity by calling `POST /api/v3/agents` on `git.clawmem.ai`, then writes the returned `token` plus `repo_full_name` back into your config under `plugins.entries.clawmem.config.agents.<agentId>` as that agent's `defaultRepo`. Automatic flows use that `defaultRepo`, while explicit memory tool calls may target other repos. When talking to an older backend that does not expose `POST /api/v3/agents`, the plugin falls back to the deprecated anonymous bootstrap path.
25
+ After restart, confirm OpenClaw shows ClawMem as the active memory plugin. On first use, clawmem bootstraps each agent identity by calling `POST /api/v3/agents` on `git.clawmem.ai`, then writes the returned `token`, backend `login`, and `repo_full_name` back into your config under `plugins.entries.clawmem.config.agents.<agentId>` as that agent's `defaultRepo`. Automatic flows use that `defaultRepo`, while explicit memory tool calls may target other repos. When talking to an older backend that does not expose `POST /api/v3/agents`, the plugin falls back to the deprecated anonymous bootstrap path.
24
26
 
25
- The package now also ships a bundled `clawmem` skill for runtime memory behavior:
27
+ ClawMem installs only the core memory plugin and its core runtime skill. If you want Team setup or Team workflow templates, install an external ClawMem Team skill pack such as `clawmem-team-skills`.
28
+
29
+ Earlier ClawMem versions bundled Team workflow guidance. That guidance has moved to an external ClawMem Team skill repository such as `clawmem-team-skills`. If you are upgrading from an older setup, install the external Team skill pack before following any Team-related docs.
30
+
31
+ The package ships a bundled `clawmem` skill for core runtime memory behavior:
26
32
  - core recall and save loop
27
33
  - post-install repair and verification guidance
28
34
  - mental model, user-facing communication, and console-link guidance
29
35
  - schema and manual-ops references
30
- - collaboration routing for shared repos
36
+ - collaboration routing for shared repos and access primitives
31
37
 
32
38
  The website `SKILL.md` should stay bootstrap-focused. Once the plugin is installed, rely on the bundled plugin skill for day-to-day memory behavior.
33
39
 
40
+ ## Optional Team Skills
41
+
42
+ ClawMem plugin = memory + atomic collaboration capability.
43
+
44
+ external ClawMem Team skill pack = Team design + Team bootstrap + Team templates.
45
+
46
+ If you want a Team, install that repository separately and use one of its entry skills. The plugin package is not the source of truth for Team workflows.
47
+
34
48
  ---
35
49
 
36
50
  ## Publishing
@@ -60,12 +74,13 @@ ClawMem is OpenClaw's durable memory system.
60
74
  - Durable facts, preferences, decisions, workflows, and active-task state belong in ClawMem memory issues.
61
75
  - Files remain for tools or humans to read directly.
62
76
  - Memory routing is per agent identity: `plugins.entries.clawmem.config.agents.<agentId>.defaultRepo` is the default space, and explicit tool calls may target other repos.
63
- - Shared or team memory should live in a shared repo, not in one agent's private default repo.
77
+ - Shared memory can live in shared repos instead of one agent's private default repo.
78
+ - How multiple agents organize around those repos is defined by external skills, not by the plugin.
64
79
  - Use plugin tools first. Raw `gh` or `curl` are fallback tools for explicit repo operations, backend debugging, or tool outages.
65
80
 
66
81
  ## Bundled Skill And Docs
67
82
 
68
- The plugin package is now the runtime source of truth:
83
+ The plugin package is the runtime source of truth for ClawMem core behavior:
69
84
 
70
85
  - Bundled runtime skill: [`skills/clawmem/SKILL.md`](skills/clawmem/SKILL.md)
71
86
  - Runtime references: [`skills/clawmem/references/`](skills/clawmem/references/)
@@ -74,10 +89,12 @@ The plugin package is now the runtime source of truth:
74
89
  That bundled skill covers:
75
90
  - recall and save behavior
76
91
  - schema discipline and deliberate self-evolution
77
- - shared-memory and collaboration routing
92
+ - shared-memory and collaboration primitives
78
93
  - repair and verification guidance
79
94
  - raw `gh` / `curl` fallback flows
80
95
 
96
+ Team setup guides, Team workflow guides, and Team templates live in an external ClawMem Team skill pack such as `clawmem-team-skills`.
97
+
81
98
  If your environment still relies on file-injected reminders such as `SOUL.md`, `AGENTS.md`, or `TOOLS.md`, treat them as optional compatibility snippets rather than the primary runtime source of truth.
82
99
 
83
100
  ---
@@ -98,6 +115,7 @@ Minimal config (after auto-provisioning):
98
115
  agents: {
99
116
  main: {
100
117
  baseUrl: "https://git.clawmem.ai/api/v3",
118
+ login: "main-b54ea6",
101
119
  defaultRepo: "owner/main-memory",
102
120
  token: "<token>",
103
121
  authScheme: "token"
@@ -126,11 +144,13 @@ Full config with all options:
126
144
  agents: {
127
145
  main: {
128
146
  baseUrl: "https://git.clawmem.ai/api/v3",
147
+ login: "main-b54ea6",
129
148
  defaultRepo: "owner/main-memory",
130
149
  token: "<token>",
131
150
  authScheme: "token"
132
151
  },
133
152
  coder: {
153
+ login: "hazel-e23778",
134
154
  defaultRepo: "owner/coder-memory",
135
155
  token: "<token>"
136
156
  }
@@ -146,8 +166,6 @@ Full config with all options:
146
166
  }
147
167
  ```
148
168
 
149
- ---
150
-
151
169
  ## Notes
152
170
 
153
171
  - Conversation comments exclude tool calls, tool results, system messages, and heartbeat noise.
@@ -156,9 +174,10 @@ Full config with all options:
156
174
  - Summary or memory-capture failures do not block finalization; the conversation issue still closes, and the mirrored transcript remains the durable source of truth for manual follow-up.
157
175
  - Memory search and auto-recall only return open `type:memory` issues. Closed memory issues are treated as stale.
158
176
  - ClawMem automatically injects a small set of relevant memories before each turn using the agent's default repo and the backend recall API. Auto-recall is best-effort and quietly skips injection when backend recall is unavailable.
177
+ - Always-on ClawMem prompt guidance uses the dedicated memory prompt-registration API on OpenClaw `2026.3.22+`. On `2026.3.7` through `2026.3.21`, ClawMem falls back to `before_prompt_build` `prependSystemContext`. Older hosts still support auto-recall, tools, and conversation mirroring, but they cannot inject the static always-on guidance.
159
178
  - `memory_recall` uses the backend `/api/v3/search/issues` endpoint scoped to the current repo plus `label:"type:memory"`. When backend recall is unavailable, use `memory_list` or `memory_get` to inspect memories explicitly.
160
179
  - Automatic durable capture happens when the session resets or ends. If a fact must be available immediately for later turns, use `memory_store` or `memory_update` explicitly instead of waiting for finalization.
161
- - The plugin exposes `memory_repos`, `memory_repo_create`, `memory_list`, `memory_get`, `memory_labels`, `memory_recall`, `memory_store`, `memory_update`, and `memory_forget` for mid-session use.
180
+ - The plugin exposes memory tools, collaboration tools, a default-repo retarget tool, and generic issue/comment tools for mid-session use.
162
181
  - Route resolution is now: agent identity supplies credentials, `defaultRepo` is the fallback memory space, and explicit tool calls may override repo per operation.
163
182
  - `memory_store` accepts optional schema hints such as kind and topics; the plugin normalizes them into managed `kind:*` and `topic:*` labels.
164
183
  - Memory issues no longer use `session:*` labels. Session linkage remains a conversation concern, not part of the durable memory schema.
@@ -166,3 +185,4 @@ Full config with all options:
166
185
  - Conversation lifecycle is stored in native issue state (`open` while live, `closed` after finalize); memory lifecycle uses native issue state too (`open` active, `closed` stale).
167
186
  - Memory extraction now prefers one atomic fact per memory item instead of bundling whole sessions into a single node.
168
187
  - Memory issue bodies store the durable detail in a YAML `detail` field plus flat metadata such as `memory_hash` and logical `date`; this matches the current Console parser in `agent-git-service/web`.
188
+ - If your environment still exposes older Team-specific reminders from the plugin package, treat them as deprecated compatibility content and follow your external ClawMem Team skill pack for current Team setup guidance.
@@ -0,0 +1,2 @@
1
+ import type { OpenClawPluginApi } from "openclaw/plugin-sdk/core";
2
+ export default function register(api: OpenClawPluginApi): void;
package/dist/index.js ADDED
@@ -0,0 +1,4 @@
1
+ import { createClawMemPlugin } from "./src/service.js";
2
+ export default function register(api) {
3
+ createClawMemPlugin(api);
4
+ }
@@ -0,0 +1,49 @@
1
+ export type CollaborationPermission = "read" | "write" | "admin";
2
+ export type CollaborationOrgInvitationRole = "member" | "owner";
3
+ type PermissionMap = Record<string, boolean | undefined>;
4
+ export type CollaborationRepoSummary = {
5
+ full_name?: string;
6
+ owner?: {
7
+ login?: string;
8
+ };
9
+ name?: string;
10
+ permissions?: PermissionMap;
11
+ role_name?: string;
12
+ };
13
+ export type CollaborationTeamSummary = {
14
+ id?: number;
15
+ slug?: string;
16
+ name?: string;
17
+ description?: string;
18
+ privacy?: string;
19
+ permission?: string;
20
+ role_name?: string;
21
+ permissions?: PermissionMap;
22
+ };
23
+ export type CollaborationCollaboratorSummary = {
24
+ id?: number;
25
+ login?: string;
26
+ name?: string;
27
+ permissions?: PermissionMap;
28
+ role_name?: string;
29
+ organization_member?: boolean;
30
+ outside_collaborator?: boolean;
31
+ type?: string;
32
+ };
33
+ type RepoAccessTeamClient = {
34
+ listOrgTeams(org: string): Promise<CollaborationTeamSummary[]>;
35
+ listTeamRepos(org: string, teamSlug: string): Promise<CollaborationRepoSummary[]>;
36
+ };
37
+ export declare function normalizePermissionAlias(value: unknown): "none" | CollaborationPermission | undefined;
38
+ export declare function resolveOrgInvitationRole(value: unknown, fallback: CollaborationOrgInvitationRole): {
39
+ role: CollaborationOrgInvitationRole;
40
+ } | {
41
+ error: string;
42
+ };
43
+ export declare function repoSummaryFullName(repo?: CollaborationRepoSummary): string | undefined;
44
+ export declare function filterDirectCollaborators(collaborators: CollaborationCollaboratorSummary[], ownerLogin: string): CollaborationCollaboratorSummary[];
45
+ export declare function listRepoAccessTeams(client: RepoAccessTeamClient, org: string, fullName: string): Promise<{
46
+ teams: CollaborationTeamSummary[];
47
+ notes: string[];
48
+ }>;
49
+ export {};
@@ -0,0 +1,69 @@
1
+ export function normalizePermissionAlias(value) {
2
+ if (typeof value !== "string")
3
+ return undefined;
4
+ const normalized = value.trim().toLowerCase();
5
+ if (!normalized)
6
+ return undefined;
7
+ if (normalized === "none")
8
+ return "none";
9
+ if (normalized === "read" || normalized === "pull" || normalized === "triage")
10
+ return "read";
11
+ if (normalized === "write" || normalized === "push" || normalized === "maintain")
12
+ return "write";
13
+ if (normalized === "admin")
14
+ return "admin";
15
+ return undefined;
16
+ }
17
+ export function resolveOrgInvitationRole(value, fallback) {
18
+ if (value === undefined || value === null || value === "")
19
+ return { role: fallback };
20
+ if (typeof value !== "string")
21
+ return { error: "role must be member or owner." };
22
+ const normalized = value.trim().toLowerCase();
23
+ if (normalized === "member" || normalized === "owner")
24
+ return { role: normalized };
25
+ return { error: `Unsupported role "${value}". Use member or owner.` };
26
+ }
27
+ export function repoSummaryFullName(repo) {
28
+ const fullName = repo?.full_name?.trim();
29
+ if (fullName)
30
+ return fullName;
31
+ const owner = repo?.owner?.login?.trim();
32
+ const name = repo?.name?.trim();
33
+ if (owner && name)
34
+ return `${owner}/${name}`;
35
+ return name || undefined;
36
+ }
37
+ export function filterDirectCollaborators(collaborators, ownerLogin) {
38
+ const owner = ownerLogin.trim().toLowerCase();
39
+ if (!owner)
40
+ return collaborators;
41
+ return collaborators.filter((collaborator) => (collaborator.login?.trim().toLowerCase() || "") !== owner);
42
+ }
43
+ export async function listRepoAccessTeams(client, org, fullName) {
44
+ const notes = [];
45
+ const teams = await client.listOrgTeams(org);
46
+ const withAccess = [];
47
+ for (const team of teams) {
48
+ const teamSlug = team.slug?.trim() || team.name?.trim();
49
+ if (!teamSlug) {
50
+ notes.push(`Skipped a team in org "${org}" because it had no slug or name.`);
51
+ continue;
52
+ }
53
+ try {
54
+ const repos = await client.listTeamRepos(org, teamSlug);
55
+ const matchingRepo = repos.find((repo) => repoSummaryFullName(repo) === fullName);
56
+ if (!matchingRepo)
57
+ continue;
58
+ withAccess.push({
59
+ ...team,
60
+ ...(matchingRepo.permissions ? { permissions: matchingRepo.permissions } : {}),
61
+ ...(matchingRepo.role_name ? { role_name: matchingRepo.role_name } : {}),
62
+ });
63
+ }
64
+ catch (error) {
65
+ notes.push(`Team repo lookup failed for ${org}/${teamSlug}: ${String(error)}`);
66
+ }
67
+ }
68
+ return { teams: withAccess, notes };
69
+ }
@@ -0,0 +1,21 @@
1
+ import type { OpenClawPluginApi } from "openclaw/plugin-sdk/core";
2
+ import type { ClawMemPluginConfig, ClawMemResolvedRoute } from "./types.js";
3
+ export declare const SESSION_TITLE_PREFIX = "Session: ";
4
+ export declare const MEMORY_TITLE_PREFIX = "Memory: ";
5
+ export declare const DEFAULT_LABELS: readonly string[];
6
+ export declare const AGENT_LABEL_PREFIX = "agent:";
7
+ export declare const LABEL_ACTIVE = "status:active";
8
+ export declare const LABEL_CLOSED = "status:closed";
9
+ export declare const LABEL_MEMORY_ACTIVE = "memory-status:active";
10
+ export declare const LABEL_MEMORY_STALE = "memory-status:stale";
11
+ export declare function resolvePluginConfig(api: OpenClawPluginApi): ClawMemPluginConfig;
12
+ export declare function resolveAgentRoute(config: ClawMemPluginConfig, agentId?: string, repoOverride?: string): ClawMemResolvedRoute;
13
+ export declare function isAgentConfigured(route: ClawMemResolvedRoute): boolean;
14
+ export declare function hasDefaultRepo(route: ClawMemResolvedRoute): boolean;
15
+ export declare function resolveLabelColor(label: string): string;
16
+ export declare function labelDescription(label: string): string;
17
+ export declare function isManagedLabel(label: string): boolean;
18
+ export declare function extractLabelNames(labels: Array<{
19
+ name?: string;
20
+ } | string> | undefined): string[];
21
+ export declare function labelVal(labels: string[], prefix: string): string | undefined;
@@ -0,0 +1,119 @@
1
+ import { normalizeAgentId, normalizeLoginName } from "./utils.js";
2
+ export const SESSION_TITLE_PREFIX = "Session: ";
3
+ export const MEMORY_TITLE_PREFIX = "Memory: ";
4
+ export const DEFAULT_LABELS = [];
5
+ export const AGENT_LABEL_PREFIX = "agent:";
6
+ export const LABEL_ACTIVE = "status:active";
7
+ export const LABEL_CLOSED = "status:closed";
8
+ export const LABEL_MEMORY_ACTIVE = "memory-status:active";
9
+ export const LABEL_MEMORY_STALE = "memory-status:stale";
10
+ const MANAGED_PREFIXES = ["type:", "kind:", "session:", "date:", "topic:", "agent:"];
11
+ const MANAGED_EXACT = new Set([LABEL_ACTIVE, LABEL_CLOSED, LABEL_MEMORY_ACTIVE, LABEL_MEMORY_STALE]);
12
+ export function resolvePluginConfig(api) {
13
+ const raw = (api.pluginConfig ?? {});
14
+ const str = (v) => typeof v === "string" && v.trim() ? v.trim() : undefined;
15
+ const num = (v, d) => typeof v === "number" && Number.isFinite(v) ? Math.floor(v) : d;
16
+ const positiveInt = (v) => typeof v === "number" && Number.isInteger(v) && v > 0 ? v : undefined;
17
+ const float = (v, d) => typeof v === "number" && Number.isFinite(v) ? v : d;
18
+ const clamp = (v, lo, hi) => Math.min(hi, Math.max(lo, v));
19
+ const baseUrl = (str(raw.baseUrl) ?? "https://git.clawmem.ai").replace(/\/+$/, "");
20
+ const rawAgents = raw.agents && typeof raw.agents === "object" && !Array.isArray(raw.agents)
21
+ ? raw.agents
22
+ : {};
23
+ const agents = {};
24
+ for (const [rawAgentId, rawAgentConfig] of Object.entries(rawAgents)) {
25
+ if (!rawAgentConfig || typeof rawAgentConfig !== "object" || Array.isArray(rawAgentConfig))
26
+ continue;
27
+ const agentId = normalizeAgentId(rawAgentId);
28
+ const agent = rawAgentConfig;
29
+ agents[agentId] = {
30
+ baseUrl: str(agent.baseUrl)?.replace(/\/+$/, ""),
31
+ login: normalizeLoginName(str(agent.login)),
32
+ defaultRepo: normalizeRepoName(str(agent.defaultRepo) ?? str(agent.repo)),
33
+ repo: str(agent.repo),
34
+ token: str(agent.token),
35
+ authScheme: agent.authScheme === "bearer" ? "bearer" : agent.authScheme === "token" ? "token" : undefined,
36
+ };
37
+ }
38
+ return {
39
+ baseUrl: baseUrl.endsWith("/api/v3") ? baseUrl : `${baseUrl}/api/v3`,
40
+ login: normalizeLoginName(str(raw.login)),
41
+ defaultRepo: normalizeRepoName(str(raw.defaultRepo) ?? str(raw.repo)),
42
+ repo: normalizeRepoName(str(raw.repo)),
43
+ token: str(raw.token),
44
+ authScheme: raw.authScheme === "bearer" ? "bearer" : "token",
45
+ agents,
46
+ memoryRecallLimit: clamp(num(raw.memoryRecallLimit, 5), 1, 20),
47
+ memoryAutoRecallLimit: clamp(num(raw.memoryAutoRecallLimit, 3), 1, 20),
48
+ summaryWaitTimeoutMs: clamp(num(raw.summaryWaitTimeoutMs, 120000), 1000, 600000),
49
+ memoryExtractWaitTimeoutMs: clamp(num(raw.memoryExtractWaitTimeoutMs, 45000), 1000, 600000),
50
+ reviewNudgeInterval: clamp(num(raw.reviewNudgeInterval, 10), 0, 100),
51
+ };
52
+ }
53
+ export function resolveAgentRoute(config, agentId, repoOverride) {
54
+ const id = normalizeAgentId(agentId);
55
+ const agent = config.agents[id] ?? {};
56
+ const baseUrl = (agent.baseUrl ?? config.baseUrl).replace(/\/+$/, "");
57
+ const defaultRepo = normalizeRepoName(agent.defaultRepo ?? agent.repo) ?? config.defaultRepo ?? normalizeRepoName(config.repo);
58
+ const repo = normalizeRepoName(repoOverride) ?? defaultRepo;
59
+ return {
60
+ agentId: id,
61
+ baseUrl: baseUrl.endsWith("/api/v3") ? baseUrl : `${baseUrl}/api/v3`,
62
+ ...(normalizeLoginName(agent.login) ?? normalizeLoginName(config.login) ? { login: normalizeLoginName(agent.login) ?? normalizeLoginName(config.login) } : {}),
63
+ ...(defaultRepo ? { defaultRepo } : {}),
64
+ ...(repo ? { repo } : {}),
65
+ token: agent.token?.trim() || config.token?.trim() || undefined,
66
+ authScheme: agent.authScheme === "bearer" ? "bearer" : agent.authScheme === "token" ? "token" : config.authScheme,
67
+ };
68
+ }
69
+ export function isAgentConfigured(route) {
70
+ return Boolean(route.baseUrl && route.token);
71
+ }
72
+ export function hasDefaultRepo(route) {
73
+ return Boolean(route.defaultRepo);
74
+ }
75
+ export function resolveLabelColor(label) {
76
+ if (label.startsWith("status:"))
77
+ return "b60205";
78
+ if (label.startsWith("memory-status:"))
79
+ return label.endsWith(":stale") ? "d93f0b" : "0e8a16";
80
+ if (label.startsWith("type:"))
81
+ return label === "type:memory" ? "5319e7" : "1d76db";
82
+ if (label.startsWith("kind:"))
83
+ return "5319e7";
84
+ if (label.startsWith("date:"))
85
+ return "c5def5";
86
+ if (label.startsWith("topic:"))
87
+ return "fbca04";
88
+ if (label.startsWith("session:"))
89
+ return "bfdadc";
90
+ if (label.startsWith("agent:"))
91
+ return "1d76db";
92
+ return "0e8a16";
93
+ }
94
+ export function labelDescription(label) {
95
+ for (const [pfx, d] of [["type:", "Issue type"], ["kind:", "Memory kind"], ["memory-status:", "Memory lifecycle status"],
96
+ ["status:", "Conversation lifecycle status"], ["session:", "Session association"],
97
+ ["date:", "Date"], ["topic:", "Topic"], ["agent:", "Agent"]])
98
+ if (label.startsWith(pfx))
99
+ return `${d} label managed by clawmem.`;
100
+ return "Label managed by clawmem.";
101
+ }
102
+ export function isManagedLabel(label) {
103
+ return DEFAULT_LABELS.includes(label) || MANAGED_EXACT.has(label) || MANAGED_PREFIXES.some((p) => label.startsWith(p));
104
+ }
105
+ export function extractLabelNames(labels) {
106
+ if (!Array.isArray(labels))
107
+ return [];
108
+ return labels.map((e) => (typeof e === "string" ? e : e?.name ?? "").trim()).filter(Boolean);
109
+ }
110
+ export function labelVal(labels, prefix) {
111
+ const m = labels.find((l) => l.startsWith(prefix));
112
+ return m ? m.slice(prefix.length).trim() || undefined : undefined;
113
+ }
114
+ function normalizeRepoName(value) {
115
+ if (!value)
116
+ return undefined;
117
+ const trimmed = value.trim().replace(/^\/+|\/+$/g, "");
118
+ return /^[^/\s]+\/[^/\s]+$/.test(trimmed) ? trimmed : undefined;
119
+ }
@@ -0,0 +1,30 @@
1
+ import type { OpenClawPluginApi } from "openclaw/plugin-sdk/core";
2
+ import type { GitHubIssueClient } from "./github-client.js";
3
+ import type { ClawMemPluginConfig, MemoryCandidate, MemorySchema, NormalizedMessage, SessionMirrorState, TranscriptSnapshot } from "./types.js";
4
+ export declare class ConversationMirror {
5
+ private readonly client;
6
+ private readonly api;
7
+ private readonly config;
8
+ constructor(client: GitHubIssueClient, api: OpenClawPluginApi, config: ClawMemPluginConfig);
9
+ shouldMirror(sessionId: string, messages: NormalizedMessage[]): boolean;
10
+ loadSnapshot(session: SessionMirrorState, fallback: unknown[]): Promise<TranscriptSnapshot>;
11
+ ensureIssue(session: SessionMirrorState, snapshot: TranscriptSnapshot): Promise<void>;
12
+ syncBody(session: SessionMirrorState, snapshot: TranscriptSnapshot, summary: string, closed: boolean, titleOverride?: string): Promise<void>;
13
+ syncLabels(session: SessionMirrorState, snapshot: TranscriptSnapshot, closed: boolean): Promise<void>;
14
+ appendComments(issueNumber: number, messages: NormalizedMessage[]): Promise<number>;
15
+ generateFinalArtifacts(session: SessionMirrorState, snapshot: TranscriptSnapshot, schema?: MemorySchema): Promise<{
16
+ summary: string;
17
+ title?: string;
18
+ candidates: MemoryCandidate[];
19
+ }>;
20
+ private buildLabels;
21
+ private renderBody;
22
+ private resolveDates;
23
+ private resolveTranscriptPath;
24
+ private lookupBoundIssue;
25
+ private isBoundIssue;
26
+ private resetIssueBinding;
27
+ }
28
+ export declare function buildFinalizeArtifactsPrompt(snapshot: TranscriptSnapshot, schema?: MemorySchema): string;
29
+ /** Derive an initial placeholder title for a new conversation. The real title is generated by LLM once enough messages are available. */
30
+ export declare function deriveInitialTitle(_messages: NormalizedMessage[], sessionId: string): string;