@clawmem-ai/clawmem 0.1.18 → 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.
- package/README.md +28 -9
- package/dist/index.d.ts +2 -0
- package/dist/index.js +4 -0
- package/dist/src/collaboration.d.ts +49 -0
- package/dist/src/collaboration.js +69 -0
- package/dist/src/config.d.ts +21 -0
- package/dist/src/config.js +119 -0
- package/dist/src/conversation.d.ts +30 -0
- package/dist/src/conversation.js +323 -0
- package/dist/src/github-client.d.ts +269 -0
- package/dist/src/github-client.js +350 -0
- package/dist/src/keyed-async-queue.d.ts +12 -0
- package/dist/src/keyed-async-queue.js +23 -0
- package/dist/src/memory.d.ts +29 -0
- package/dist/src/memory.js +451 -0
- package/dist/src/recall-sanitize.d.ts +1 -0
- package/dist/src/recall-sanitize.js +149 -0
- package/dist/src/runtime-env.d.ts +2 -0
- package/dist/src/runtime-env.js +12 -0
- package/dist/src/service.d.ts +18 -0
- package/dist/src/service.js +3645 -0
- package/dist/src/state.d.ts +4 -0
- package/dist/src/state.js +182 -0
- package/dist/src/transcript.d.ts +3 -0
- package/dist/src/transcript.js +164 -0
- package/dist/src/types.d.ts +130 -0
- package/dist/src/types.js +1 -0
- package/dist/src/utils.d.ts +26 -0
- package/dist/src/utils.js +62 -0
- package/dist/src/yaml.d.ts +2 -0
- package/dist/src/yaml.js +81 -0
- package/openclaw.plugin.json +14 -1
- package/package.json +21 -7
- package/skills/clawmem/SKILL.md +26 -5
- package/skills/clawmem/references/collaboration.md +13 -5
- package/skills/clawmem/references/review.md +77 -0
- package/skills/clawmem/references/schema.md +44 -1
- package/index.ts +0 -6
- package/src/collaboration.test.ts +0 -71
- package/src/collaboration.ts +0 -109
- package/src/config.test.ts +0 -83
- package/src/config.ts +0 -117
- package/src/conversation.test.ts +0 -120
- package/src/conversation.ts +0 -304
- package/src/github-client.test.ts +0 -101
- package/src/github-client.ts +0 -363
- package/src/keyed-async-queue.ts +0 -26
- package/src/memory.test.ts +0 -588
- package/src/memory.ts +0 -444
- package/src/recall-sanitize.ts +0 -143
- package/src/runtime-env.ts +0 -12
- package/src/service.test.ts +0 -337
- package/src/service.ts +0 -2786
- package/src/state.test.ts +0 -119
- package/src/state.ts +0 -206
- package/src/transcript.ts +0 -186
- package/src/types.ts +0 -86
- package/src/utils.ts +0 -74
- package/src/yaml.ts +0 -88
- package/tsconfig.json +0 -15
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`
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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.
|
|
@@ -159,7 +177,7 @@ Full config with all options:
|
|
|
159
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.
|
|
160
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.
|
|
161
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.
|
|
162
|
-
- The plugin exposes
|
|
180
|
+
- The plugin exposes memory tools, collaboration tools, a default-repo retarget tool, and generic issue/comment tools for mid-session use.
|
|
163
181
|
- Route resolution is now: agent identity supplies credentials, `defaultRepo` is the fallback memory space, and explicit tool calls may override repo per operation.
|
|
164
182
|
- `memory_store` accepts optional schema hints such as kind and topics; the plugin normalizes them into managed `kind:*` and `topic:*` labels.
|
|
165
183
|
- Memory issues no longer use `session:*` labels. Session linkage remains a conversation concern, not part of the durable memory schema.
|
|
@@ -167,3 +185,4 @@ Full config with all options:
|
|
|
167
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).
|
|
168
186
|
- Memory extraction now prefers one atomic fact per memory item instead of bundling whole sessions into a single node.
|
|
169
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.
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -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;
|