@mclawnet/agent 0.6.11 → 0.6.12

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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=skill-sync.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skill-sync.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/skill-sync.test.ts"],"names":[],"mappings":""}
@@ -2,6 +2,10 @@ import {
2
2
  loadConfig
3
3
  } from "./chunk-CBZIH6FY.js";
4
4
 
5
+ // src/start.ts
6
+ import { homedir as homedir3 } from "os";
7
+ import { join as join4 } from "path";
8
+
5
9
  // src/hub-connection.ts
6
10
  import { hostname as osHostname } from "os";
7
11
  import WebSocket from "ws";
@@ -248,7 +252,7 @@ async function handleLoadSessionHistory(workDir, claudeSessionId, opts) {
248
252
  }
249
253
 
250
254
  // src/hub-connection.ts
251
- import { createLogger } from "@mclawnet/logger";
255
+ import { createLogger, previewFields } from "@mclawnet/logger";
252
256
  var log = createLogger({ module: "agent" });
253
257
  var HubConnection = class {
254
258
  ws = null;
@@ -610,6 +614,7 @@ var HubConnection = class {
610
614
  const sm = this.sessionManager;
611
615
  const spawnAndSend = (resumeId, label) => {
612
616
  log.info({ sessionId, claudeSessionId: resumeId, workDir, label }, "claude.execute: spawning");
617
+ log.debug({ sessionId, ...previewFields(content) }, "claude.execute: input");
613
618
  sm.createSession({ sessionId, workDir, resumeId, useBrainCore, roleId: "role-__assistant__" }).then(() => {
614
619
  sm.sendInput(sessionId, content);
615
620
  }).catch((err) => {
@@ -622,6 +627,7 @@ var HubConnection = class {
622
627
  };
623
628
  if (sm.isHealthy(sessionId)) {
624
629
  log.info({ sessionId }, "claude.execute: reusing healthy session");
630
+ log.debug({ sessionId, ...previewFields(content) }, "claude.execute: input");
625
631
  sm.sendInput(sessionId, content);
626
632
  } else if (sm.hasSession(sessionId) && claudeSessionId) {
627
633
  log.warn({ sessionId, claudeSessionId }, "claude.execute: session unhealthy, recreating with --resume");
@@ -689,7 +695,10 @@ var HubConnection = class {
689
695
  return true;
690
696
  }
691
697
  if (msg.type === "claude.input") {
692
- log.info({ sessionId: msg.sessionId }, "claude.input");
698
+ log.debug(
699
+ { sessionId: msg.sessionId, ...previewFields(msg.content) },
700
+ "claude.input"
701
+ );
693
702
  this.sessionManager.sendInput(msg.sessionId, msg.content);
694
703
  return true;
695
704
  }
@@ -778,9 +787,299 @@ var HubConnection = class {
778
787
  };
779
788
 
780
789
  // src/session-manager.ts
781
- import { createLogger as createLogger2 } from "@mclawnet/logger";
790
+ import { createLogger as createLogger3, previewFields as previewFields2 } from "@mclawnet/logger";
782
791
  import { buildMemorySection } from "@mclawnet/memory";
783
- var log2 = createLogger2({ module: "agent/session-manager" });
792
+
793
+ // src/skill-loader.ts
794
+ import { existsSync as existsSync2, mkdirSync, readdirSync as readdirSync2, readFileSync as readFileSync2, writeFileSync } from "fs";
795
+ import { createHash } from "crypto";
796
+ import { join as join2, dirname } from "path";
797
+ import { homedir as homedir2 } from "os";
798
+ import { createRequire } from "module";
799
+ import { fileURLToPath } from "url";
800
+ import { createLogger as createLogger2 } from "@mclawnet/logger";
801
+ import { ManifestManager, mergeSkillSections } from "@mclawnet/skill-manager";
802
+ var log2 = createLogger2({ module: "agent/skill-loader" });
803
+ var CLAWNET_DIR = join2(homedir2(), ".clawnet");
804
+ var SKILLS_DIR = join2(CLAWNET_DIR, ".claude", "skills");
805
+ var MCP_CONFIG_PATH = join2(CLAWNET_DIR, "mcp.json");
806
+ async function initSkills() {
807
+ ensureSkillsDir();
808
+ syncBuiltinSkills(CLAWNET_DIR, defaultBuiltinSourceDir());
809
+ ensureMcpConfig();
810
+ scanSkills();
811
+ }
812
+ function defaultBuiltinSourceDir() {
813
+ const thisFile = fileURLToPath(import.meta.url);
814
+ return join2(dirname(thisFile), "..", "skills");
815
+ }
816
+ function sha256(s) {
817
+ return createHash("sha256").update(s).digest("hex");
818
+ }
819
+ function readBuiltinVersion(content) {
820
+ const match = content.match(/^version:\s*(.+)$/m);
821
+ return match ? match[1].trim().replace(/^["']|["']$/g, "") : "1.0.0";
822
+ }
823
+ function syncBuiltinSkills(rootDir, srcDir) {
824
+ const skillsDir = join2(rootDir, ".claude", "skills");
825
+ if (!existsSync2(skillsDir)) mkdirSync(skillsDir, { recursive: true });
826
+ if (!existsSync2(srcDir)) {
827
+ log2.debug({ srcDir }, "no built-in skills directory found, skipping");
828
+ return;
829
+ }
830
+ const manifest = new ManifestManager(rootDir);
831
+ let entries;
832
+ try {
833
+ entries = readdirSync2(srcDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
834
+ } catch {
835
+ log2.warn({ srcDir }, "failed to read built-in skills directory");
836
+ return;
837
+ }
838
+ for (const skillName of entries) {
839
+ const srcSkillMd = join2(srcDir, skillName, "SKILL.md");
840
+ if (!existsSync2(srcSkillMd)) continue;
841
+ const destDir = join2(skillsDir, skillName);
842
+ const destSkillMd = join2(destDir, "SKILL.md");
843
+ const baseSnapshotPath = join2(destDir, ".base.md");
844
+ let officialContent;
845
+ try {
846
+ officialContent = readFileSync2(srcSkillMd, "utf-8");
847
+ } catch (err) {
848
+ log2.warn({ skill: skillName, err }, "failed to read built-in skill");
849
+ continue;
850
+ }
851
+ const officialHash = sha256(officialContent);
852
+ const officialVersion = readBuiltinVersion(officialContent);
853
+ if (!existsSync2(destSkillMd)) {
854
+ try {
855
+ mkdirSync(destDir, { recursive: true });
856
+ writeFileSync(destSkillMd, officialContent);
857
+ writeFileSync(baseSnapshotPath, officialContent);
858
+ manifest.register(skillName, officialHash, officialVersion);
859
+ log2.info({ skill: skillName, version: officialVersion }, "installed built-in skill");
860
+ } catch (err) {
861
+ log2.warn({ skill: skillName, err }, "failed to install built-in skill");
862
+ }
863
+ continue;
864
+ }
865
+ const userContent = readFileSync2(destSkillMd, "utf-8");
866
+ const userHash = sha256(userContent);
867
+ manifest.refresh(skillName, userHash);
868
+ if (!existsSync2(baseSnapshotPath)) {
869
+ const entry = manifest.load().skills[skillName];
870
+ if (entry && !entry.userModified) {
871
+ try {
872
+ writeFileSync(baseSnapshotPath, userContent);
873
+ } catch (err) {
874
+ log2.debug({ skill: skillName, err }, "failed to write lazy .base.md");
875
+ }
876
+ }
877
+ }
878
+ const action = manifest.determineSyncAction(skillName, officialHash, officialVersion);
879
+ switch (action) {
880
+ case "skip":
881
+ log2.debug({ skill: skillName }, "skill already up to date");
882
+ break;
883
+ case "direct-overwrite":
884
+ try {
885
+ writeFileSync(destSkillMd, officialContent);
886
+ writeFileSync(baseSnapshotPath, officialContent);
887
+ manifest.markSynced(skillName, officialHash, officialHash, officialVersion);
888
+ log2.info({ skill: skillName, version: officialVersion }, "upgraded built-in skill");
889
+ } catch (err) {
890
+ log2.warn({ skill: skillName, err }, "failed to overwrite skill");
891
+ }
892
+ break;
893
+ case "keep-user":
894
+ log2.debug({ skill: skillName }, "keeping user-modified skill");
895
+ break;
896
+ case "needs-merge": {
897
+ if (!existsSync2(baseSnapshotPath)) {
898
+ try {
899
+ writeFileSync(
900
+ destSkillMd + ".conflict",
901
+ renderConflictFile(skillName, officialContent, [
902
+ {
903
+ section: "(entire file)",
904
+ official: officialContent,
905
+ user: userContent
906
+ }
907
+ ])
908
+ );
909
+ log2.warn(
910
+ { skill: skillName },
911
+ "missing .base.md snapshot \u2014 skipping auto-merge, wrote .conflict advisory"
912
+ );
913
+ } catch (err) {
914
+ log2.warn({ skill: skillName, err }, "failed to write .conflict advisory");
915
+ }
916
+ break;
917
+ }
918
+ const baseContent = readFileSync2(baseSnapshotPath, "utf-8");
919
+ const merged = mergeSkillSections(baseContent, officialContent, userContent);
920
+ if (merged.success && merged.content) {
921
+ try {
922
+ writeFileSync(destSkillMd, merged.content);
923
+ writeFileSync(baseSnapshotPath, officialContent);
924
+ const newCurrentHash = sha256(merged.content);
925
+ manifest.markSynced(skillName, officialHash, newCurrentHash, officialVersion);
926
+ log2.info({ skill: skillName }, "auto-merged skill upgrade");
927
+ } catch (err) {
928
+ log2.warn({ skill: skillName, err }, "failed to write merged skill");
929
+ }
930
+ } else {
931
+ try {
932
+ writeFileSync(
933
+ destSkillMd + ".conflict",
934
+ renderConflictFile(skillName, officialContent, merged.conflicts ?? [])
935
+ );
936
+ log2.warn(
937
+ { skill: skillName, conflicts: merged.conflicts?.length },
938
+ "skill merge conflict \u2014 user version kept, .conflict file written"
939
+ );
940
+ } catch (err) {
941
+ log2.warn({ skill: skillName, err }, "failed to write .conflict file");
942
+ }
943
+ }
944
+ break;
945
+ }
946
+ }
947
+ }
948
+ }
949
+ function renderConflictFile(skillName, officialContent, conflicts) {
950
+ const lines = [
951
+ `# Skill upgrade conflict: ${skillName}`,
952
+ ``,
953
+ `The official skill was updated, and your local copy was also modified.`,
954
+ `Please review each section below and reconcile manually.`,
955
+ ``,
956
+ `## Conflicted sections`,
957
+ ``
958
+ ];
959
+ for (const c of conflicts) {
960
+ lines.push(`### ${c.section}`);
961
+ lines.push("");
962
+ lines.push("**Official:**");
963
+ lines.push("```");
964
+ lines.push(c.official);
965
+ lines.push("```");
966
+ lines.push("");
967
+ lines.push("**Your version:**");
968
+ lines.push("```");
969
+ lines.push(c.user);
970
+ lines.push("```");
971
+ lines.push("");
972
+ }
973
+ lines.push("## Full official content");
974
+ lines.push("```");
975
+ lines.push(officialContent);
976
+ lines.push("```");
977
+ return lines.join("\n");
978
+ }
979
+ function ensureSkillsDir() {
980
+ if (!existsSync2(SKILLS_DIR)) {
981
+ mkdirSync(SKILLS_DIR, { recursive: true });
982
+ log2.info({ dir: SKILLS_DIR }, "created skills directory");
983
+ }
984
+ }
985
+ var cachedSkills = [];
986
+ function scanSkills() {
987
+ if (!existsSync2(SKILLS_DIR)) return [];
988
+ let dirs;
989
+ try {
990
+ dirs = readdirSync2(SKILLS_DIR, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
991
+ } catch {
992
+ log2.warn("failed to read skills directory for scanning");
993
+ return [];
994
+ }
995
+ const skills = [];
996
+ for (const dirName of dirs) {
997
+ const skillMd = join2(SKILLS_DIR, dirName, "SKILL.md");
998
+ if (!existsSync2(skillMd)) continue;
999
+ try {
1000
+ const content = readFileSync2(skillMd, "utf-8");
1001
+ const parsed = parseFrontmatter(content);
1002
+ if (parsed.name) {
1003
+ skills.push({
1004
+ name: parsed.name,
1005
+ description: parsed.description || dirName
1006
+ });
1007
+ }
1008
+ } catch (err) {
1009
+ log2.debug({ skill: dirName, err }, "failed to parse SKILL.md frontmatter");
1010
+ }
1011
+ }
1012
+ cachedSkills = skills;
1013
+ log2.info({ count: skills.length }, "scanned skills");
1014
+ return skills;
1015
+ }
1016
+ function getSkillList() {
1017
+ return cachedSkills;
1018
+ }
1019
+ function getPendingNotification() {
1020
+ const pendingPath = join2(CLAWNET_DIR, ".claude", "pending-evolutions.json");
1021
+ if (!existsSync2(pendingPath)) return null;
1022
+ try {
1023
+ const raw = JSON.parse(readFileSync2(pendingPath, "utf-8"));
1024
+ const pending = Array.isArray(raw?.pending) ? raw.pending : [];
1025
+ if (pending.length === 0) return null;
1026
+ const lines = pending.map(
1027
+ (p) => `- [${p.signal ?? "?"}] ${p.skillName ?? "?"}: ${p.problem ?? ""} (confidence: ${p.confidence ?? "?"})`
1028
+ );
1029
+ const text = `\u6709 ${pending.length} \u6761 Skill \u8FDB\u5316\u63D0\u6848\u5F85\u5BA1\u6838\uFF1A
1030
+ ${lines.join("\n")}
1031
+ \u4F7F\u7528 skill_pending_list \u67E5\u770B\u8BE6\u60C5\uFF0Cskill_pending_approve / skill_pending_reject \u5904\u7406\u3002`;
1032
+ return { count: pending.length, text };
1033
+ } catch (err) {
1034
+ log2.debug({ err }, "failed to read pending-evolutions.json");
1035
+ return null;
1036
+ }
1037
+ }
1038
+ function parseFrontmatter(content) {
1039
+ const match = content.match(/^---\s*\n([\s\S]*?)\n---/);
1040
+ if (!match) return { name: "", description: "" };
1041
+ const yaml = match[1];
1042
+ const nameMatch = yaml.match(/^name:\s*(.+)$/m);
1043
+ const descMatch = yaml.match(/^description:\s*(.+)$/m);
1044
+ const strip = (s) => s.trim().replace(/^["']|["']$/g, "");
1045
+ return {
1046
+ name: strip(nameMatch?.[1] ?? ""),
1047
+ description: strip(descMatch?.[1] ?? "")
1048
+ };
1049
+ }
1050
+ function ensureMcpConfig() {
1051
+ if (existsSync2(MCP_CONFIG_PATH)) {
1052
+ log2.debug("mcp.json already exists, skipping");
1053
+ return;
1054
+ }
1055
+ let mcpServerPath;
1056
+ try {
1057
+ const req = createRequire(import.meta.url);
1058
+ const mcpPkgDir = dirname(req.resolve("@mclawnet/mcp-server/package.json"));
1059
+ mcpServerPath = join2(mcpPkgDir, "dist", "server.js");
1060
+ } catch {
1061
+ log2.warn("could not resolve @mclawnet/mcp-server package path, skipping mcp.json generation");
1062
+ return;
1063
+ }
1064
+ const config = {
1065
+ mcpServers: {
1066
+ "clawnet-mcp": {
1067
+ command: "node",
1068
+ args: [mcpServerPath]
1069
+ }
1070
+ }
1071
+ };
1072
+ try {
1073
+ mkdirSync(CLAWNET_DIR, { recursive: true });
1074
+ writeFileSync(MCP_CONFIG_PATH, JSON.stringify(config, null, 2) + "\n");
1075
+ log2.info({ path: MCP_CONFIG_PATH }, "generated default mcp.json");
1076
+ } catch (err) {
1077
+ log2.warn({ err }, "failed to generate mcp.json");
1078
+ }
1079
+ }
1080
+
1081
+ // src/session-manager.ts
1082
+ var log3 = createLogger3({ module: "agent/session-manager" });
784
1083
  var DEFAULT_MAX_PROCESSES = 10;
785
1084
  var MAX_PROCESSES = Number(process.env.CLAWNET_MAX_PROCESSES) || DEFAULT_MAX_PROCESSES;
786
1085
  var SessionManager = class {
@@ -821,10 +1120,26 @@ var SessionManager = class {
821
1120
  options.systemPrompt = options.systemPrompt ? `${memorySection}${roleHint}
822
1121
 
823
1122
  ${options.systemPrompt}` : `${memorySection}${roleHint}`;
824
- log2.debug({ roleId: options.roleId, sessionId: options.sessionId }, "memory prompt + roleId hint injected");
1123
+ log3.debug({ roleId: options.roleId, sessionId: options.sessionId }, "memory prompt + roleId hint injected");
825
1124
  } catch (err) {
826
- log2.warn({ err, roleId: options.roleId }, "failed to build memory section, proceeding without");
1125
+ log3.warn({ err, roleId: options.roleId }, "failed to build memory section, proceeding without");
1126
+ }
1127
+ }
1128
+ try {
1129
+ const notice = getPendingNotification();
1130
+ if (notice) {
1131
+ const noticeBlock = `
1132
+
1133
+ [Skill \u8FDB\u5316\u63D0\u6848]
1134
+ ${notice.text}`;
1135
+ options.systemPrompt = options.systemPrompt ? `${options.systemPrompt}${noticeBlock}` : noticeBlock.trimStart();
1136
+ log3.debug(
1137
+ { sessionId: options.sessionId, pending: notice.count },
1138
+ "pending skill evolution notice injected"
1139
+ );
827
1140
  }
1141
+ } catch (err) {
1142
+ log3.debug({ err }, "failed to inject pending notification");
828
1143
  }
829
1144
  try {
830
1145
  const process2 = await this.adapter.spawn(options);
@@ -850,7 +1165,7 @@ ${options.systemPrompt}` : `${memorySection}${roleHint}`;
850
1165
  if (this.onSessionStarted) {
851
1166
  this.adapter.onSessionStarted?.(process2, (info) => {
852
1167
  if (this.aborting.has(options.sessionId)) {
853
- log2.debug(
1168
+ log3.debug(
854
1169
  { sessionId: options.sessionId },
855
1170
  "suppressing late session_started \u2014 session is aborting"
856
1171
  );
@@ -866,7 +1181,7 @@ ${options.systemPrompt}` : `${memorySection}${roleHint}`;
866
1181
  if (this.sessions.get(options.sessionId) === process2) {
867
1182
  this.sessions.delete(options.sessionId);
868
1183
  this.conversationBuffer.delete(options.sessionId);
869
- log2.warn({ sessionId: options.sessionId, exitCode: code }, "backend process exited unexpectedly, evicted from session map");
1184
+ log3.warn({ sessionId: options.sessionId, exitCode: code }, "backend process exited unexpectedly, evicted from session map");
870
1185
  this.onSessionError(options.sessionId, `backend process exited (code=${code ?? "null"})`);
871
1186
  }
872
1187
  });
@@ -883,6 +1198,10 @@ ${options.systemPrompt}` : `${memorySection}${roleHint}`;
883
1198
  this.onSessionError(sessionId, `No active session: ${sessionId}`);
884
1199
  return;
885
1200
  }
1201
+ log3.debug(
1202
+ { sessionId, ...previewFields2(input) },
1203
+ "sendInput \u2192 backend stdin"
1204
+ );
886
1205
  this.adapter.send(process2, input);
887
1206
  }
888
1207
  async abortSession(sessionId) {
@@ -950,138 +1269,6 @@ ${options.systemPrompt}` : `${memorySection}${roleHint}`;
950
1269
  // src/start.ts
951
1270
  import { SwarmCoordinator, initRoles } from "@mclawnet/swarm";
952
1271
 
953
- // src/skill-loader.ts
954
- import { existsSync as existsSync2, mkdirSync, copyFileSync, readdirSync as readdirSync2, readFileSync as readFileSync2, writeFileSync } from "fs";
955
- import { join as join2, dirname } from "path";
956
- import { homedir as homedir2 } from "os";
957
- import { createRequire } from "module";
958
- import { fileURLToPath } from "url";
959
- import { createLogger as createLogger3 } from "@mclawnet/logger";
960
- var log3 = createLogger3({ module: "agent/skill-loader" });
961
- var CLAWNET_DIR = join2(homedir2(), ".clawnet");
962
- var SKILLS_DIR = join2(CLAWNET_DIR, ".claude", "skills");
963
- var MCP_CONFIG_PATH = join2(CLAWNET_DIR, "mcp.json");
964
- async function initSkills() {
965
- ensureSkillsDir();
966
- copyBuiltinSkills();
967
- ensureMcpConfig();
968
- scanSkills();
969
- }
970
- function ensureSkillsDir() {
971
- if (!existsSync2(SKILLS_DIR)) {
972
- mkdirSync(SKILLS_DIR, { recursive: true });
973
- log3.info({ dir: SKILLS_DIR }, "created skills directory");
974
- }
975
- }
976
- function copyBuiltinSkills() {
977
- const thisFile = fileURLToPath(import.meta.url);
978
- const srcDir = join2(dirname(thisFile), "..", "skills");
979
- if (!existsSync2(srcDir)) {
980
- log3.debug({ srcDir }, "no built-in skills directory found, skipping");
981
- return;
982
- }
983
- let entries;
984
- try {
985
- entries = readdirSync2(srcDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
986
- } catch {
987
- log3.warn({ srcDir }, "failed to read built-in skills directory");
988
- return;
989
- }
990
- for (const skillName of entries) {
991
- const srcSkillMd = join2(srcDir, skillName, "SKILL.md");
992
- const destDir = join2(SKILLS_DIR, skillName);
993
- const destSkillMd = join2(destDir, "SKILL.md");
994
- if (!existsSync2(srcSkillMd)) continue;
995
- if (existsSync2(destSkillMd)) {
996
- log3.debug({ skill: skillName }, "skill already exists, skipping");
997
- continue;
998
- }
999
- try {
1000
- mkdirSync(destDir, { recursive: true });
1001
- copyFileSync(srcSkillMd, destSkillMd);
1002
- log3.info({ skill: skillName }, "copied built-in skill");
1003
- } catch (err) {
1004
- log3.warn({ skill: skillName, err }, "failed to copy built-in skill");
1005
- }
1006
- }
1007
- }
1008
- var cachedSkills = [];
1009
- function scanSkills() {
1010
- if (!existsSync2(SKILLS_DIR)) return [];
1011
- let dirs;
1012
- try {
1013
- dirs = readdirSync2(SKILLS_DIR, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
1014
- } catch {
1015
- log3.warn("failed to read skills directory for scanning");
1016
- return [];
1017
- }
1018
- const skills = [];
1019
- for (const dirName of dirs) {
1020
- const skillMd = join2(SKILLS_DIR, dirName, "SKILL.md");
1021
- if (!existsSync2(skillMd)) continue;
1022
- try {
1023
- const content = readFileSync2(skillMd, "utf-8");
1024
- const parsed = parseFrontmatter(content);
1025
- if (parsed.name) {
1026
- skills.push({
1027
- name: parsed.name,
1028
- description: parsed.description || dirName
1029
- });
1030
- }
1031
- } catch (err) {
1032
- log3.debug({ skill: dirName, err }, "failed to parse SKILL.md frontmatter");
1033
- }
1034
- }
1035
- cachedSkills = skills;
1036
- log3.info({ count: skills.length }, "scanned skills");
1037
- return skills;
1038
- }
1039
- function getSkillList() {
1040
- return cachedSkills;
1041
- }
1042
- function parseFrontmatter(content) {
1043
- const match = content.match(/^---\s*\n([\s\S]*?)\n---/);
1044
- if (!match) return { name: "", description: "" };
1045
- const yaml = match[1];
1046
- const nameMatch = yaml.match(/^name:\s*(.+)$/m);
1047
- const descMatch = yaml.match(/^description:\s*(.+)$/m);
1048
- const strip = (s) => s.trim().replace(/^["']|["']$/g, "");
1049
- return {
1050
- name: strip(nameMatch?.[1] ?? ""),
1051
- description: strip(descMatch?.[1] ?? "")
1052
- };
1053
- }
1054
- function ensureMcpConfig() {
1055
- if (existsSync2(MCP_CONFIG_PATH)) {
1056
- log3.debug("mcp.json already exists, skipping");
1057
- return;
1058
- }
1059
- let mcpServerPath;
1060
- try {
1061
- const req = createRequire(import.meta.url);
1062
- const memoryPkgDir = dirname(req.resolve("@mclawnet/memory/package.json"));
1063
- mcpServerPath = join2(memoryPkgDir, "dist", "mcp", "server.js");
1064
- } catch {
1065
- log3.warn("could not resolve @mclawnet/memory package path, skipping mcp.json generation");
1066
- return;
1067
- }
1068
- const config = {
1069
- mcpServers: {
1070
- "clawnet-memory": {
1071
- command: "node",
1072
- args: [mcpServerPath]
1073
- }
1074
- }
1075
- };
1076
- try {
1077
- mkdirSync(CLAWNET_DIR, { recursive: true });
1078
- writeFileSync(MCP_CONFIG_PATH, JSON.stringify(config, null, 2) + "\n");
1079
- log3.info({ path: MCP_CONFIG_PATH }, "generated default mcp.json");
1080
- } catch (err) {
1081
- log3.warn({ err }, "failed to generate mcp.json");
1082
- }
1083
- }
1084
-
1085
1272
  // src/brain-bridge.ts
1086
1273
  import { existsSync as existsSync3, readFileSync as readFileSync3, readdirSync as readdirSync3 } from "fs";
1087
1274
  import { join as join3 } from "path";
@@ -1465,6 +1652,19 @@ var FsBridge = class {
1465
1652
 
1466
1653
  // src/start.ts
1467
1654
  import { createLogger as createLogger6 } from "@mclawnet/logger";
1655
+ import {
1656
+ initDatabase,
1657
+ MemoryStore,
1658
+ EmbeddingService,
1659
+ createEmbeddingProviders,
1660
+ distillConversation
1661
+ } from "@mclawnet/memory";
1662
+ import {
1663
+ SkillStore,
1664
+ EvolutionPipeline,
1665
+ AccumulationScanner,
1666
+ triggerFromAccumulation
1667
+ } from "@mclawnet/skill-manager";
1468
1668
  var log6 = createLogger6({ module: "agent" });
1469
1669
  async function startAgent(options) {
1470
1670
  const config = loadConfig(options.config);
@@ -1496,6 +1696,58 @@ async function startAgent(options) {
1496
1696
  }
1497
1697
  });
1498
1698
  let swarmCoordinator;
1699
+ const clawnetDir = process.env.CLAWNET_DIR ?? join4(homedir3(), ".clawnet");
1700
+ const dbPath = process.env.CLAWNET_MEMORY_DB ?? join4(clawnetDir, "memory.db");
1701
+ let memoryStore = null;
1702
+ let embeddingService = null;
1703
+ let evolutionPipeline = null;
1704
+ let skillStore = null;
1705
+ let memoryDb = null;
1706
+ try {
1707
+ memoryDb = initDatabase(dbPath);
1708
+ memoryStore = new MemoryStore(memoryDb);
1709
+ embeddingService = new EmbeddingService(memoryDb, createEmbeddingProviders());
1710
+ skillStore = new SkillStore(clawnetDir);
1711
+ evolutionPipeline = new EvolutionPipeline(clawnetDir);
1712
+ } catch (err) {
1713
+ log6.warn({ err }, "failed to init memory/skill infra; distillation disabled");
1714
+ }
1715
+ const onBeforeClose = async (sessionId, messages) => {
1716
+ if (messages.length === 0) return;
1717
+ if (!memoryStore || !embeddingService) return;
1718
+ try {
1719
+ await distillConversation(
1720
+ messages,
1721
+ "role-__assistant__",
1722
+ memoryStore,
1723
+ embeddingService
1724
+ );
1725
+ } catch (err) {
1726
+ log6.warn({ err, sessionId }, "distillation failed (non-fatal)");
1727
+ }
1728
+ try {
1729
+ if (!skillStore || !evolutionPipeline) return;
1730
+ const skills = skillStore.scan();
1731
+ if (skills.length === 0) return;
1732
+ const recent = memoryStore.getMemoriesByRole("role-__assistant__", "working");
1733
+ const refs = recent.map((m) => ({
1734
+ id: m.id,
1735
+ type: m.type,
1736
+ content: m.content,
1737
+ domain: m.domain
1738
+ }));
1739
+ const scanner = new AccumulationScanner();
1740
+ const signals = scanner.scan(
1741
+ refs,
1742
+ skills.map((s) => s.name)
1743
+ );
1744
+ if (signals.length > 0) {
1745
+ await triggerFromAccumulation(signals, evolutionPipeline);
1746
+ }
1747
+ } catch (err) {
1748
+ log6.warn({ err, sessionId }, "accumulation scan failed (non-fatal)");
1749
+ }
1750
+ };
1499
1751
  const sessionManager = new SessionManager({
1500
1752
  adapter: options.adapter,
1501
1753
  onOutput: (sessionId, data) => {
@@ -1530,7 +1782,8 @@ async function startAgent(options) {
1530
1782
  sessionId,
1531
1783
  error
1532
1784
  });
1533
- }
1785
+ },
1786
+ onBeforeClose
1534
1787
  });
1535
1788
  swarmCoordinator = new SwarmCoordinator(sessionManager, hub);
1536
1789
  hub.setSessionManager(sessionManager);
@@ -1541,6 +1794,13 @@ async function startAgent(options) {
1541
1794
  log6.info("shutting down");
1542
1795
  await sessionManager.closeAll();
1543
1796
  hub.destroy();
1797
+ if (memoryDb) {
1798
+ try {
1799
+ memoryDb.close();
1800
+ } catch (err) {
1801
+ log6.warn({ err }, "failed to close memory db");
1802
+ }
1803
+ }
1544
1804
  process.exit(0);
1545
1805
  };
1546
1806
  process.on("SIGINT", shutdown);
@@ -1556,4 +1816,4 @@ export {
1556
1816
  FsBridge,
1557
1817
  startAgent
1558
1818
  };
1559
- //# sourceMappingURL=chunk-HQMVKDYY.js.map
1819
+ //# sourceMappingURL=chunk-MSDIRBXF.js.map