@openclawcity/become 0.1.0

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/dist/index.cjs ADDED
@@ -0,0 +1,2760 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var src_exports = {};
22
+ __export(src_exports, {
23
+ AnthropicAdapter: () => AnthropicAdapter,
24
+ AwarenessIndex: () => AwarenessIndex,
25
+ BLOOMS_ORDER: () => BLOOMS_ORDER,
26
+ BLOOMS_SCORE: () => BLOOMS_SCORE,
27
+ Become: () => Become,
28
+ ConversationLearner: () => ConversationLearner,
29
+ DREYFUS_THRESHOLDS: () => DREYFUS_THRESHOLDS,
30
+ GrowthTracker: () => GrowthTracker,
31
+ LearningGraph: () => LearningGraph,
32
+ MemoryStore: () => MemoryStore,
33
+ MilestoneDetector: () => MilestoneDetector,
34
+ NormDetector: () => NormDetector,
35
+ OllamaAdapter: () => OllamaAdapter,
36
+ OpenAIAdapter: () => OpenAIAdapter,
37
+ PeerReviewProtocol: () => PeerReviewProtocol,
38
+ Reflector: () => Reflector,
39
+ SQLiteStore: () => SQLiteStore,
40
+ SkillEvolver: () => SkillEvolver,
41
+ SkillPruner: () => SkillPruner,
42
+ SkillStore: () => SkillStore,
43
+ TeachingProtocol: () => TeachingProtocol,
44
+ TrainScheduler: () => TrainScheduler,
45
+ TrendTracker: () => TrendTracker,
46
+ WEIGHTS: () => WEIGHTS,
47
+ checkGate: () => checkGate,
48
+ computeFullScore: () => computeFullScore,
49
+ computeScore: () => computeScore,
50
+ datasetStats: () => datasetStats,
51
+ detectBloomsLevel: () => detectBloomsLevel,
52
+ detectCollaborationGap: () => detectCollaborationGap,
53
+ detectCollectiveMemory: () => detectCollectiveMemory,
54
+ detectCreativeMismatch: () => detectCreativeMismatch,
55
+ detectCulturalOutlier: () => detectCulturalOutlier,
56
+ detectIdleCreative: () => detectIdleCreative,
57
+ detectProlificCollaborator: () => detectProlificCollaborator,
58
+ detectQuestStreak: () => detectQuestStreak,
59
+ detectReactionDisparity: () => detectReactionDisparity,
60
+ detectSoloCreator: () => detectSoloCreator,
61
+ detectSymbolicVocabulary: () => detectSymbolicVocabulary,
62
+ dreyfusStage: () => dreyfusStage,
63
+ filterHighQuality: () => filterHighQuality,
64
+ getReputationLevel: () => getReputationLevel,
65
+ importSkillDirectory: () => importSkillDirectory,
66
+ nextMilestone: () => nextMilestone,
67
+ normalizeCategory: () => normalizeCategory,
68
+ parseSkillFile: () => parseSkillFile,
69
+ scoreTrend: () => scoreTrend,
70
+ toTrainingDataset: () => toTrainingDataset,
71
+ trainLoRA: () => trainLoRA,
72
+ validateAgentId: () => validateAgentId
73
+ });
74
+ module.exports = __toCommonJS(src_exports);
75
+
76
+ // src/core/scorer.ts
77
+ var scorer_exports = {};
78
+ __export(scorer_exports, {
79
+ BLOOMS_ORDER: () => BLOOMS_ORDER,
80
+ BLOOMS_SCORE: () => BLOOMS_SCORE,
81
+ DREYFUS_THRESHOLDS: () => DREYFUS_THRESHOLDS,
82
+ WEIGHTS: () => WEIGHTS,
83
+ computeFullScore: () => computeFullScore,
84
+ computeScore: () => computeScore,
85
+ detectBloomsLevel: () => detectBloomsLevel,
86
+ dreyfusStage: () => dreyfusStage,
87
+ nextMilestone: () => nextMilestone,
88
+ scoreTrend: () => scoreTrend
89
+ });
90
+ var BLOOMS_ORDER = [
91
+ "remember",
92
+ "understand",
93
+ "apply",
94
+ "analyze",
95
+ "evaluate",
96
+ "create"
97
+ ];
98
+ var BLOOMS_SCORE = {
99
+ remember: 10,
100
+ understand: 25,
101
+ apply: 45,
102
+ analyze: 65,
103
+ evaluate: 80,
104
+ create: 100
105
+ };
106
+ var DREYFUS_THRESHOLDS = {
107
+ novice: { next: "Beginner", score: 16 },
108
+ beginner: { next: "Competent", score: 36 },
109
+ competent: { next: "Proficient", score: 56 },
110
+ proficient: { next: "Expert", score: 76 }
111
+ };
112
+ var WEIGHTS = {
113
+ artifact: 0.3,
114
+ feedback: 0.2,
115
+ improvement: 0.2,
116
+ depth: 0.15,
117
+ social: 0.1,
118
+ teaching: 0.05
119
+ };
120
+ function dreyfusStage(score) {
121
+ if (score <= 15) return "novice";
122
+ if (score <= 35) return "beginner";
123
+ if (score <= 55) return "competent";
124
+ if (score <= 75) return "proficient";
125
+ return "expert";
126
+ }
127
+ function detectBloomsLevel(input) {
128
+ if (input.artifact_count >= 3 && input.total_reactions >= 5 && input.peer_reviews_given > 0) {
129
+ return "create";
130
+ }
131
+ if (input.peer_reviews_given > 0 || input.peer_reviews_received > 0) {
132
+ return "evaluate";
133
+ }
134
+ if (input.unique_types >= 2 || input.collab_count >= 1) {
135
+ return "analyze";
136
+ }
137
+ if (input.artifact_count >= 2) {
138
+ return "apply";
139
+ }
140
+ if (input.artifact_count >= 1 && input.total_reactions > 0) {
141
+ return "understand";
142
+ }
143
+ return "remember";
144
+ }
145
+ function computeScore(input) {
146
+ const avgReactions = input.artifact_count > 0 ? input.total_reactions / input.artifact_count : 0;
147
+ const artifactComponent = Math.min(
148
+ 100,
149
+ input.artifact_count * 5 + avgReactions * 10 + input.unique_types * 8
150
+ );
151
+ const feedbackComponent = Math.min(100, input.peer_reviews_received * 15);
152
+ let improvementComponent = 0;
153
+ if (input.artifact_count >= 3 && input.older_reaction_avg > 0) {
154
+ const ratio = input.recent_reaction_avg / Math.max(1, input.older_reaction_avg);
155
+ improvementComponent = Math.min(100, ratio * 50);
156
+ } else if (input.artifact_count >= 3) {
157
+ improvementComponent = Math.min(100, input.recent_reaction_avg * 20);
158
+ }
159
+ const depthComponent = BLOOMS_SCORE[detectBloomsLevel(input)];
160
+ const socialComponent = Math.min(
161
+ 100,
162
+ input.collab_count * 15 + input.follower_count * 5 + input.total_reactions * 2
163
+ );
164
+ const teachingComponent = Math.min(100, input.teaching_events * 20);
165
+ const raw = artifactComponent * WEIGHTS.artifact + feedbackComponent * WEIGHTS.feedback + improvementComponent * WEIGHTS.improvement + depthComponent * WEIGHTS.depth + socialComponent * WEIGHTS.social + teachingComponent * WEIGHTS.teaching;
166
+ return Math.min(100, Math.max(0, Math.round(raw)));
167
+ }
168
+ function computeFullScore(skill, input) {
169
+ const score = computeScore(input);
170
+ return {
171
+ skill,
172
+ score,
173
+ blooms_level: detectBloomsLevel(input),
174
+ dreyfus_stage: dreyfusStage(score),
175
+ evidence: { ...input },
176
+ computed_at: (/* @__PURE__ */ new Date()).toISOString()
177
+ };
178
+ }
179
+ function nextMilestone(stage, score) {
180
+ const threshold = DREYFUS_THRESHOLDS[stage];
181
+ if (!threshold) return null;
182
+ const needed = Math.max(0, threshold.score - score);
183
+ return `${threshold.next} at score ${threshold.score} (need ${needed} more)`;
184
+ }
185
+ function scoreTrend(current, weekAgo) {
186
+ if (weekAgo === null) return null;
187
+ const delta = current - weekAgo;
188
+ return `${delta >= 0 ? "+" : ""}${delta} this week`;
189
+ }
190
+
191
+ // src/core/validation.ts
192
+ var MAX_AGENT_ID_LENGTH = 200;
193
+ var AGENT_ID_REGEX = /^[a-zA-Z0-9_.:@/-]+$/;
194
+ function validateAgentId(agentId) {
195
+ if (!agentId || typeof agentId !== "string") {
196
+ throw new Error("agentId is required and must be a non-empty string");
197
+ }
198
+ if (agentId.length > MAX_AGENT_ID_LENGTH) {
199
+ throw new Error(`agentId too long (max ${MAX_AGENT_ID_LENGTH} chars)`);
200
+ }
201
+ if (!AGENT_ID_REGEX.test(agentId)) {
202
+ throw new Error("agentId contains invalid characters (allowed: alphanumeric, _ . : @ / -)");
203
+ }
204
+ }
205
+
206
+ // src/core/skill-store.ts
207
+ var SKILL_NAME_REGEX = /^[a-z0-9_-]{1,100}$/;
208
+ var AUTO_VERIFY_THRESHOLD = 3;
209
+ var SkillStore = class _SkillStore {
210
+ constructor(adapter) {
211
+ this.adapter = adapter;
212
+ }
213
+ async get(agentId, skill) {
214
+ return this.adapter.getSkill(agentId, skill);
215
+ }
216
+ async list(agentId, opts) {
217
+ return this.adapter.listSkills(agentId, opts);
218
+ }
219
+ async upsert(agentId, input) {
220
+ validateAgentId(agentId);
221
+ const name = _SkillStore.normalizeName(input.name);
222
+ if (!_SkillStore.validateName(name)) {
223
+ throw new Error(`Invalid skill name: "${name}". Must match ${SKILL_NAME_REGEX}`);
224
+ }
225
+ const now = (/* @__PURE__ */ new Date()).toISOString();
226
+ const existing = await this.adapter.getSkill(agentId, name);
227
+ const skill = existing ? { ...existing, category: input.category ?? existing.category, content: input.content ?? existing.content, updated_at: now } : {
228
+ agent_id: agentId,
229
+ name,
230
+ category: input.category ?? "general",
231
+ score: 0,
232
+ blooms_level: "remember",
233
+ dreyfus_stage: "novice",
234
+ evidence: {
235
+ artifact_count: 0,
236
+ total_reactions: 0,
237
+ recent_reaction_avg: 0,
238
+ older_reaction_avg: 0,
239
+ unique_types: 0,
240
+ collab_count: 0,
241
+ peer_reviews_given: 0,
242
+ peer_reviews_received: 0,
243
+ follower_count: 0,
244
+ teaching_events: 0
245
+ },
246
+ learned_from: [],
247
+ content: input.content,
248
+ created_at: now,
249
+ updated_at: now
250
+ };
251
+ await this.adapter.upsertSkill(skill);
252
+ await this.adapter.upsertCatalogEntry({
253
+ skill: name,
254
+ category: skill.category ?? "general",
255
+ description: input.content?.slice(0, 200),
256
+ status: "community"
257
+ });
258
+ const adopters = await this.adapter.getSkillAdopterCount(name);
259
+ if (adopters >= AUTO_VERIFY_THRESHOLD) {
260
+ await this.adapter.updateCatalogStatus(name, "verified");
261
+ }
262
+ return skill;
263
+ }
264
+ async delete(agentId, skill) {
265
+ return this.adapter.deleteSkill(agentId, skill);
266
+ }
267
+ async catalog() {
268
+ return this.adapter.getCatalog();
269
+ }
270
+ async holders(skill) {
271
+ return this.adapter.getSkillHolders(skill);
272
+ }
273
+ async suggest(agentId) {
274
+ const owned = await this.adapter.listSkills(agentId);
275
+ const ownedNames = new Set(owned.map((s) => s.name));
276
+ const catalog = await this.adapter.getCatalog();
277
+ return catalog.filter((c) => c.status === "verified" && !ownedNames.has(c.skill)).sort((a, b) => b.adopter_count - a.adopter_count).slice(0, 10).map((c) => c.skill);
278
+ }
279
+ async history(agentId, skill, days = 30) {
280
+ return this.adapter.getScoreHistory(agentId, skill, days);
281
+ }
282
+ async trending(agentId, topN = 5) {
283
+ const scores = await this.adapter.getLatestScores(agentId);
284
+ const sevenDaysAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1e3).toISOString();
285
+ const trends = [];
286
+ for (const s of scores) {
287
+ const history = await this.adapter.getScoreHistory(agentId, s.skill, 7);
288
+ const oldScore = history.length > 0 ? history[0].score : null;
289
+ trends.push({
290
+ skill: s.skill,
291
+ score: s.score,
292
+ stage: s.dreyfus_stage,
293
+ trend: scoreTrend(s.score, oldScore),
294
+ next_milestone: nextMilestone(s.dreyfus_stage, s.score)
295
+ });
296
+ }
297
+ trends.sort((a, b) => {
298
+ const deltaA = a.trend ? Math.abs(parseInt(a.trend)) : 0;
299
+ const deltaB = b.trend ? Math.abs(parseInt(b.trend)) : 0;
300
+ return deltaB - deltaA;
301
+ });
302
+ return trends.slice(0, topN);
303
+ }
304
+ static normalizeName(raw) {
305
+ return raw.toLowerCase().replace(/\s+/g, "_").replace(/[^a-z0-9_-]/g, "");
306
+ }
307
+ static validateName(name) {
308
+ return SKILL_NAME_REGEX.test(name);
309
+ }
310
+ };
311
+
312
+ // src/core/reflector.ts
313
+ var SKILL_REGEX = /^[a-zA-Z0-9_-]{1,100}$/;
314
+ var MIN_REFLECTION_LENGTH = 20;
315
+ var MAX_REFLECTION_LENGTH = 2e3;
316
+ var MAX_REFLECTIONS_PER_SKILL_PER_DAY = 5;
317
+ var MAX_OBSERVATIONS = 5;
318
+ var Reflector = class {
319
+ constructor(adapter) {
320
+ this.adapter = adapter;
321
+ }
322
+ async reflect(agentId, input) {
323
+ validateAgentId(agentId);
324
+ if (!SKILL_REGEX.test(input.skill)) {
325
+ throw new Error(`Invalid skill name: "${input.skill}"`);
326
+ }
327
+ const text = stripHtml(input.reflection.trim());
328
+ if (text.length < MIN_REFLECTION_LENGTH) {
329
+ throw new Error(`Reflection too short (min ${MIN_REFLECTION_LENGTH} chars)`);
330
+ }
331
+ if (text.length > MAX_REFLECTION_LENGTH) {
332
+ throw new Error(`Reflection too long (max ${MAX_REFLECTION_LENGTH} chars)`);
333
+ }
334
+ const todayCount = await this.adapter.countReflectionsToday(agentId, input.skill);
335
+ if (todayCount >= MAX_REFLECTIONS_PER_SKILL_PER_DAY) {
336
+ throw new Error(`Rate limit: max ${MAX_REFLECTIONS_PER_SKILL_PER_DAY} reflections per skill per day`);
337
+ }
338
+ const reflection = {
339
+ agent_id: agentId,
340
+ skill: input.skill,
341
+ artifact_id: input.artifact_id,
342
+ reflection: text,
343
+ created_at: (/* @__PURE__ */ new Date()).toISOString()
344
+ };
345
+ return this.adapter.saveReflection(reflection);
346
+ }
347
+ async list(agentId, opts) {
348
+ return this.adapter.getReflections(agentId, opts);
349
+ }
350
+ observe(context) {
351
+ const observations = [];
352
+ const rules = [
353
+ detectCreativeMismatch,
354
+ detectCollaborationGap,
355
+ detectReactionDisparity,
356
+ detectIdleCreative,
357
+ detectQuestStreak,
358
+ detectSoloCreator,
359
+ detectProlificCollaborator,
360
+ detectSymbolicVocabulary,
361
+ detectCollectiveMemory,
362
+ detectCulturalOutlier
363
+ ];
364
+ for (const rule of rules) {
365
+ if (observations.length >= MAX_OBSERVATIONS) break;
366
+ const obs = rule(context);
367
+ if (obs) observations.push(obs);
368
+ }
369
+ return observations;
370
+ }
371
+ };
372
+ function detectCreativeMismatch(ctx) {
373
+ if (!ctx.declared_role || ctx.artifacts.length < 3) return null;
374
+ const typeCounts = countBy(ctx.artifacts, (a) => a.type);
375
+ const topType = maxEntry(typeCounts);
376
+ if (!topType) return null;
377
+ const role = ctx.declared_role.replace("agent-", "");
378
+ if (topType[0] === role) return null;
379
+ return {
380
+ type: "creative_mismatch",
381
+ text: `Your most-created work is ${topType[0]} (${topType[1]} pieces), but you arrived as a ${role}.`
382
+ };
383
+ }
384
+ function detectCollaborationGap(ctx) {
385
+ if (ctx.collabs_started < 3) return null;
386
+ if (ctx.collabs_completed > Math.floor(ctx.collabs_started / 3)) return null;
387
+ return {
388
+ type: "collaboration_gap",
389
+ text: `You have accepted ${ctx.collabs_started} collaborations but completed ${ctx.collabs_completed}.`
390
+ };
391
+ }
392
+ function detectReactionDisparity(ctx) {
393
+ const typeCounts = countBy(ctx.artifacts, (a) => a.type);
394
+ const entries = Object.entries(typeCounts).sort((a, b) => b[1] - a[1]);
395
+ if (entries.length < 2) return null;
396
+ const [topType, topCount] = entries[0];
397
+ const [bottomType, bottomCount] = entries[entries.length - 1];
398
+ if (topCount < 3 || topCount < 3 * bottomCount) return null;
399
+ return {
400
+ type: "reaction_disparity",
401
+ text: `You've created ${topCount} ${topType} works vs ${bottomCount} ${bottomType} works.`
402
+ };
403
+ }
404
+ function detectIdleCreative(ctx) {
405
+ if (ctx.skills.length === 0 || ctx.artifacts.length > 0) return null;
406
+ return {
407
+ type: "idle_creative",
408
+ text: `You have skills registered but haven't created any artifacts yet.`
409
+ };
410
+ }
411
+ function detectQuestStreak(ctx) {
412
+ if (ctx.quest_completions < 3) return null;
413
+ return {
414
+ type: "quest_streak",
415
+ text: `You've completed ${ctx.quest_completions} quest(s). Persistence is noticed.`
416
+ };
417
+ }
418
+ function detectSoloCreator(ctx) {
419
+ if (ctx.artifacts.length < 5 || ctx.collabs_completed > 0) return null;
420
+ return {
421
+ type: "solo_creator",
422
+ text: `${ctx.artifacts.length} artifacts created, all solo. No collaborations completed.`
423
+ };
424
+ }
425
+ function detectProlificCollaborator(ctx) {
426
+ if (ctx.collabs_completed < 3 || ctx.follower_count < 3) return null;
427
+ return {
428
+ type: "prolific_collaborator",
429
+ text: `${ctx.collabs_completed} collaborations completed and ${ctx.follower_count} followers.`
430
+ };
431
+ }
432
+ function detectSymbolicVocabulary(ctx) {
433
+ const artifactsWithTags = ctx.artifacts.filter((a) => a.tags && a.tags.length > 0);
434
+ if (artifactsWithTags.length < 5 || !ctx.peer_agents_tags) return null;
435
+ const myTags = new Set(artifactsWithTags.flatMap((a) => a.tags ?? []));
436
+ if (myTags.size === 0) return null;
437
+ let overlapCount = 0;
438
+ for (const [, peerTags] of ctx.peer_agents_tags) {
439
+ const hasOverlap = peerTags.some((t) => myTags.has(t));
440
+ if (hasOverlap) overlapCount++;
441
+ }
442
+ if (overlapCount < 3) return null;
443
+ const tagList = [...myTags].slice(0, 5).join(", ");
444
+ return {
445
+ type: "symbolic_vocabulary",
446
+ text: `Your symbolic vocabulary (${tagList}) resonates with other creators.`
447
+ };
448
+ }
449
+ function detectCollectiveMemory(ctx) {
450
+ if (ctx.artifacts.length === 0 || !ctx.population_milestones?.length) return null;
451
+ const artifactMilestone = ctx.population_milestones.find((m) => m.type === "total_artifacts");
452
+ if (!artifactMilestone) return null;
453
+ return {
454
+ type: "collective_memory",
455
+ text: `You were part of a collective milestone: ${artifactMilestone.title}.`
456
+ };
457
+ }
458
+ function detectCulturalOutlier(ctx) {
459
+ if (ctx.uniqueness_score === void 0 || ctx.uniqueness_score >= 0.2 || ctx.artifacts.length < 5 || ctx.collabs_completed < 1) return null;
460
+ return {
461
+ type: "cultural_outlier",
462
+ text: `Your perspective is genuinely unique among the group.`
463
+ };
464
+ }
465
+ function stripHtml(text) {
466
+ let result = text.replace(/<[^>]*>/g, "");
467
+ result = result.replace(/<[^>]*$/g, "");
468
+ result = result.replace(/^[^<]*>/g, (match) => {
469
+ return match.includes(">") ? match.replace(/^[^>]*>/, "") : match;
470
+ });
471
+ result = result.replace(/</g, "").replace(/>/g, "");
472
+ return result;
473
+ }
474
+ function countBy(items, key) {
475
+ const counts = {};
476
+ for (const item of items) {
477
+ const k = key(item);
478
+ counts[k] = (counts[k] ?? 0) + 1;
479
+ }
480
+ return counts;
481
+ }
482
+ function maxEntry(counts) {
483
+ let max = null;
484
+ for (const [k, v] of Object.entries(counts)) {
485
+ if (!max || v > max[1]) max = [k, v];
486
+ }
487
+ return max;
488
+ }
489
+
490
+ // src/core/milestones.ts
491
+ var BUILT_IN = {
492
+ skill_discovered: { threshold: 1, description: "First score for a skill" },
493
+ skill_competent: { threshold: 36, description: "Reached competent stage" },
494
+ skill_proficient: { threshold: 56, description: "Reached proficient stage" },
495
+ skill_expert: { threshold: 76, description: "Reached expert stage" },
496
+ first_artifact: { threshold: 1, description: "Created first output" },
497
+ ten_artifacts: { threshold: 10, description: "Created 10 outputs" },
498
+ first_collab: { threshold: 1, description: "Completed first collaboration" },
499
+ first_teaching: { threshold: 1, description: "Taught another agent for the first time" },
500
+ first_peer_review: { threshold: 1, description: "Gave first peer review" },
501
+ identity_shift: { threshold: 1, description: "Agent evolved its identity" },
502
+ norm_setter: { threshold: 1, description: "Started a cultural norm adopted by 3+ agents" }
503
+ };
504
+ var MilestoneDetector = class {
505
+ constructor(adapter) {
506
+ this.adapter = adapter;
507
+ }
508
+ custom = {};
509
+ register(type, config) {
510
+ this.custom[type] = config;
511
+ }
512
+ async check(agentId, scores) {
513
+ validateAgentId(agentId);
514
+ const awarded = [];
515
+ const now = (/* @__PURE__ */ new Date()).toISOString();
516
+ const globalChecked = /* @__PURE__ */ new Set();
517
+ for (const score of scores) {
518
+ if (score.score > 0) {
519
+ const type = `skill_discovered:${score.skill}`;
520
+ if (await this.tryAward(agentId, type, 1, score.skill, now)) {
521
+ awarded.push({ agent_id: agentId, milestone_type: type, threshold: 1, skill: score.skill, achieved_at: now });
522
+ }
523
+ }
524
+ const stageChecks = [
525
+ ["skill_competent", 36],
526
+ ["skill_proficient", 56],
527
+ ["skill_expert", 76]
528
+ ];
529
+ for (const [prefix, threshold] of stageChecks) {
530
+ if (score.score >= threshold) {
531
+ const type = `${prefix}:${score.skill}`;
532
+ if (await this.tryAward(agentId, type, threshold, score.skill, now)) {
533
+ awarded.push({ agent_id: agentId, milestone_type: type, threshold, skill: score.skill, achieved_at: now });
534
+ }
535
+ }
536
+ }
537
+ const globalMilestones = [
538
+ ["first_artifact", 1, score.evidence.artifact_count >= 1],
539
+ ["ten_artifacts", 10, score.evidence.artifact_count >= 10],
540
+ ["first_collab", 1, score.evidence.collab_count >= 1],
541
+ ["first_teaching", 1, score.evidence.teaching_events >= 1],
542
+ ["first_peer_review", 1, score.evidence.peer_reviews_given >= 1]
543
+ ];
544
+ for (const [type, threshold, eligible] of globalMilestones) {
545
+ if (eligible && !globalChecked.has(type)) {
546
+ globalChecked.add(type);
547
+ if (await this.tryAward(agentId, type, threshold, void 0, now)) {
548
+ awarded.push({ agent_id: agentId, milestone_type: type, threshold, achieved_at: now });
549
+ }
550
+ }
551
+ }
552
+ }
553
+ return awarded;
554
+ }
555
+ async tryAward(agentId, milestoneType, threshold, skill, now) {
556
+ const exists = await this.adapter.hasMilestone(agentId, milestoneType, skill);
557
+ if (exists) return false;
558
+ return this.adapter.saveMilestone({
559
+ agent_id: agentId,
560
+ milestone_type: milestoneType,
561
+ threshold,
562
+ skill,
563
+ achieved_at: now
564
+ });
565
+ }
566
+ static celebrationTier(milestoneType, threshold) {
567
+ if (milestoneType.startsWith("skill_expert")) return "epic";
568
+ if (milestoneType.startsWith("skill_proficient")) return "large";
569
+ if (milestoneType.startsWith("skill_competent")) return "medium";
570
+ if (milestoneType.startsWith("skill_discovered")) return "small";
571
+ if (threshold !== void 0) {
572
+ if (threshold >= 50) return "large";
573
+ if (threshold >= 10) return "medium";
574
+ }
575
+ return "small";
576
+ }
577
+ static getBuiltInMilestones() {
578
+ return { ...BUILT_IN };
579
+ }
580
+ };
581
+
582
+ // src/adapters/memory.ts
583
+ var MemoryStore = class {
584
+ skills = [];
585
+ catalog = /* @__PURE__ */ new Map();
586
+ scoreHistory = [];
587
+ reflections = [];
588
+ milestones = [];
589
+ peerReviews = [];
590
+ learningEdges = [];
591
+ reputationMap = /* @__PURE__ */ new Map();
592
+ conversationScores = [];
593
+ norms = [];
594
+ idCounter = 0;
595
+ nextId() {
596
+ return String(++this.idCounter);
597
+ }
598
+ /** Reset all data. Useful for test isolation. */
599
+ clear() {
600
+ this.skills = [];
601
+ this.catalog.clear();
602
+ this.scoreHistory = [];
603
+ this.reflections = [];
604
+ this.milestones = [];
605
+ this.peerReviews = [];
606
+ this.learningEdges = [];
607
+ this.reputationMap.clear();
608
+ this.conversationScores = [];
609
+ this.norms = [];
610
+ this.idCounter = 0;
611
+ }
612
+ // ── Skills ──────────────────────────────────────────────────────────────
613
+ async getSkill(agentId, skill) {
614
+ return this.skills.find((s) => s.agent_id === agentId && s.name === skill) ?? null;
615
+ }
616
+ async listSkills(agentId, opts) {
617
+ let result = this.skills.filter((s) => s.agent_id === agentId);
618
+ if (opts?.stage) result = result.filter((s) => s.dreyfus_stage === opts.stage);
619
+ if (opts?.limit) result = result.slice(0, opts.limit);
620
+ return result;
621
+ }
622
+ async upsertSkill(skill) {
623
+ const idx = this.skills.findIndex((s) => s.agent_id === skill.agent_id && s.name === skill.name);
624
+ if (idx >= 0) {
625
+ this.skills[idx] = skill;
626
+ } else {
627
+ this.skills.push(skill);
628
+ }
629
+ const entry = this.catalog.get(skill.name);
630
+ if (entry) {
631
+ entry.adopter_set.add(skill.agent_id);
632
+ entry.adopter_count = entry.adopter_set.size;
633
+ }
634
+ }
635
+ async deleteSkill(agentId, skill) {
636
+ this.skills = this.skills.filter((s) => !(s.agent_id === agentId && s.name === skill));
637
+ const entry = this.catalog.get(skill);
638
+ if (entry) {
639
+ entry.adopter_set.delete(agentId);
640
+ entry.adopter_count = entry.adopter_set.size;
641
+ }
642
+ }
643
+ // ── Catalog ─────────────────────────────────────────────────────────────
644
+ async getCatalog() {
645
+ return [...this.catalog.values()].map(({ adopter_set, ...entry }) => entry);
646
+ }
647
+ async upsertCatalogEntry(entry) {
648
+ const existing = this.catalog.get(entry.skill);
649
+ if (existing) {
650
+ existing.category = entry.category;
651
+ if (entry.description) existing.description = entry.description;
652
+ return;
653
+ }
654
+ const adopters = this.skills.filter((s) => s.name === entry.skill).map((s) => s.agent_id);
655
+ this.catalog.set(entry.skill, {
656
+ ...entry,
657
+ adopter_count: adopters.length,
658
+ adopter_set: new Set(adopters)
659
+ });
660
+ }
661
+ async getSkillHolders(skill) {
662
+ return this.skills.filter((s) => s.name === skill);
663
+ }
664
+ async getSkillAdopterCount(skill) {
665
+ const entry = this.catalog.get(skill);
666
+ return entry?.adopter_set.size ?? 0;
667
+ }
668
+ async updateCatalogStatus(skill, status) {
669
+ const entry = this.catalog.get(skill);
670
+ if (entry) entry.status = status;
671
+ }
672
+ // ── Score History ───────────────────────────────────────────────────────
673
+ async saveScore(agentId, score) {
674
+ this.scoreHistory.push({ ...score, agent_id: agentId });
675
+ }
676
+ async getScoreHistory(agentId, skill, days = 30) {
677
+ const cutoff = new Date(Date.now() - days * 24 * 60 * 60 * 1e3).toISOString();
678
+ return this.scoreHistory.filter((s) => s.agent_id === agentId && s.skill === skill && s.computed_at >= cutoff).sort((a, b) => a.computed_at.localeCompare(b.computed_at));
679
+ }
680
+ async getLatestScores(agentId) {
681
+ const bySkill = /* @__PURE__ */ new Map();
682
+ for (const s of this.scoreHistory) {
683
+ if (s.agent_id !== agentId) continue;
684
+ const existing = bySkill.get(s.skill);
685
+ if (!existing || s.computed_at > existing.computed_at) {
686
+ bySkill.set(s.skill, s);
687
+ }
688
+ }
689
+ return [...bySkill.values()];
690
+ }
691
+ // ── Reflections ─────────────────────────────────────────────────────────
692
+ async saveReflection(reflection) {
693
+ const saved = { ...reflection, id: reflection.id ?? this.nextId() };
694
+ this.reflections.push(saved);
695
+ return saved;
696
+ }
697
+ async getReflections(agentId, opts) {
698
+ let result = this.reflections.filter((r) => r.agent_id === agentId);
699
+ if (opts?.skill) result = result.filter((r) => r.skill === opts.skill);
700
+ result.sort((a, b) => b.created_at.localeCompare(a.created_at));
701
+ if (opts?.limit) result = result.slice(0, opts.limit);
702
+ return result;
703
+ }
704
+ async countReflectionsToday(agentId, skill) {
705
+ const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
706
+ return this.reflections.filter(
707
+ (r) => r.agent_id === agentId && r.skill === skill && r.created_at.startsWith(today)
708
+ ).length;
709
+ }
710
+ // ── Milestones ──────────────────────────────────────────────────────────
711
+ async saveMilestone(milestone) {
712
+ const exists = this.milestones.some(
713
+ (m) => m.agent_id === milestone.agent_id && m.milestone_type === milestone.milestone_type && (m.skill ?? "") === (milestone.skill ?? "")
714
+ );
715
+ if (exists) return false;
716
+ this.milestones.push(milestone);
717
+ return true;
718
+ }
719
+ async getMilestones(agentId) {
720
+ return this.milestones.filter((m) => m.agent_id === agentId).sort((a, b) => b.achieved_at.localeCompare(a.achieved_at));
721
+ }
722
+ async hasMilestone(agentId, milestoneType, skill) {
723
+ return this.milestones.some(
724
+ (m) => m.agent_id === agentId && m.milestone_type === milestoneType && (skill === void 0 || m.skill === skill)
725
+ );
726
+ }
727
+ // ── Peer Reviews ────────────────────────────────────────────────────────
728
+ async savePeerReview(review) {
729
+ const saved = { ...review, id: review.id ?? this.nextId(), created_at: review.created_at ?? (/* @__PURE__ */ new Date()).toISOString() };
730
+ this.peerReviews.push(saved);
731
+ return saved;
732
+ }
733
+ async getReviewsFor(agentId, opts) {
734
+ let result = this.peerReviews.filter((r) => r.submission_agent_id === agentId);
735
+ if (opts?.skill) result = result.filter((r) => r.skill === opts.skill);
736
+ return result;
737
+ }
738
+ async getReviewsBy(agentId) {
739
+ return this.peerReviews.filter((r) => r.reviewer_agent_id === agentId);
740
+ }
741
+ // ── Learning Edges ──────────────────────────────────────────────────────
742
+ async saveLearningEdge(edge) {
743
+ this.learningEdges.push(edge);
744
+ }
745
+ async getLearningEdges(agentId, direction) {
746
+ if (direction === "from") {
747
+ return this.learningEdges.filter((e) => e.from_agent === agentId);
748
+ }
749
+ return this.learningEdges.filter((e) => e.to_agent === agentId);
750
+ }
751
+ // ── Reputation ──────────────────────────────────────────────────────────
752
+ async getReputation(agentId) {
753
+ return this.reputationMap.get(agentId) ?? 0;
754
+ }
755
+ async grantReputation(agentId, amount, _type, _description) {
756
+ const current = this.reputationMap.get(agentId) ?? 0;
757
+ this.reputationMap.set(agentId, current + amount);
758
+ }
759
+ // ── Conversation Scores ─────────────────────────────────────────────────
760
+ async saveConversationScore(agentId, score) {
761
+ this.conversationScores.push({ ...score, agent_id: agentId });
762
+ }
763
+ async getConversationScores(agentId, opts) {
764
+ const result = this.conversationScores.filter((s) => s.agent_id === agentId);
765
+ result.reverse();
766
+ if (opts?.limit) return result.slice(0, opts.limit);
767
+ return result;
768
+ }
769
+ // ── Cultural Norms ──────────────────────────────────────────────────────
770
+ async saveNorm(norm) {
771
+ const idx = this.norms.findIndex((n) => n.id === norm.id);
772
+ if (idx >= 0) {
773
+ this.norms[idx] = norm;
774
+ } else {
775
+ this.norms.push(norm);
776
+ }
777
+ }
778
+ async getNorms(opts) {
779
+ let result = [...this.norms];
780
+ if (opts?.category) result = result.filter((n) => n.category === opts.category);
781
+ result.sort((a, b) => b.first_observed_at.localeCompare(a.first_observed_at));
782
+ if (opts?.limit) result = result.slice(0, opts.limit);
783
+ return result;
784
+ }
785
+ };
786
+
787
+ // src/learn/conversation.ts
788
+ var EVOLVE_THRESHOLD = 0.4;
789
+ var ConversationLearner = class {
790
+ constructor(adapter, judge) {
791
+ this.adapter = adapter;
792
+ this.judge = judge;
793
+ }
794
+ /** Score a single conversation turn using feedback signals */
795
+ scoreResponse(turn) {
796
+ const feedback = turn.feedback;
797
+ if (feedback?.explicit === "positive") {
798
+ return { quality: 1, confidence: 0.9, skill_signals: turn.context.active_skills };
799
+ }
800
+ if (feedback?.explicit === "negative") {
801
+ return { quality: -1, confidence: 0.9, skill_signals: turn.context.active_skills, failure_patterns: ["explicit_negative"] };
802
+ }
803
+ if (feedback?.implicit === "accepted") {
804
+ return { quality: 1, confidence: 0.6, skill_signals: turn.context.active_skills };
805
+ }
806
+ if (feedback?.implicit === "retry") {
807
+ return { quality: -1, confidence: 0.7, skill_signals: turn.context.active_skills, failure_patterns: ["user_retry"] };
808
+ }
809
+ if (feedback?.implicit === "modified") {
810
+ return { quality: 0, confidence: 0.5, skill_signals: turn.context.active_skills };
811
+ }
812
+ return { quality: 0, confidence: 0.3, skill_signals: turn.context.active_skills };
813
+ }
814
+ /** Score using optional LLM judge for higher-quality automated assessment */
815
+ async scoreWithJudge(turn) {
816
+ if (!this.judge) {
817
+ return this.scoreResponse(turn);
818
+ }
819
+ try {
820
+ const result = await this.judge.score(
821
+ turn.user_message,
822
+ turn.agent_response,
823
+ turn.context.current_task
824
+ );
825
+ return {
826
+ quality: result.quality,
827
+ confidence: 0.8,
828
+ skill_signals: turn.context.active_skills,
829
+ failure_patterns: result.quality === -1 ? [result.reasoning] : void 0
830
+ };
831
+ } catch {
832
+ return this.scoreResponse(turn);
833
+ }
834
+ }
835
+ /** Process a turn: score it and persist */
836
+ async afterTurn(turn) {
837
+ validateAgentId(turn.agent_id);
838
+ const score = this.judge ? await this.scoreWithJudge(turn) : this.scoreResponse(turn);
839
+ await this.adapter.saveConversationScore(turn.agent_id, {
840
+ ...score,
841
+ session_id: turn.session_id
842
+ });
843
+ const skillUpdates = score.skill_signals.map((skill) => ({
844
+ skill,
845
+ delta: score.quality,
846
+ reason: score.quality === 1 ? "positive_feedback" : score.quality === -1 ? "negative_feedback" : "neutral"
847
+ }));
848
+ return {
849
+ skill_updates: skillUpdates,
850
+ observations: score.failure_patterns
851
+ };
852
+ }
853
+ /** Summarize learning across a full session */
854
+ async afterSession(session) {
855
+ validateAgentId(session.agent_id);
856
+ const scores = [];
857
+ for (const turn of session.turns) {
858
+ const score = this.judge ? await this.scoreWithJudge(turn) : this.scoreResponse(turn);
859
+ scores.push(score);
860
+ await this.adapter.saveConversationScore(session.agent_id, {
861
+ ...score,
862
+ session_id: session.session_id
863
+ });
864
+ }
865
+ const positive = scores.filter((s) => s.quality === 1).length;
866
+ const total = scores.length;
867
+ const successRate = total > 0 ? positive / total : 0;
868
+ const skillDelta = /* @__PURE__ */ new Map();
869
+ for (const score of scores) {
870
+ for (const skill of score.skill_signals) {
871
+ skillDelta.set(skill, (skillDelta.get(skill) ?? 0) + score.quality);
872
+ }
873
+ }
874
+ const improved = [];
875
+ const degraded = [];
876
+ for (const [skill, delta] of skillDelta) {
877
+ if (delta > 0) improved.push(skill);
878
+ else if (delta < 0) degraded.push(skill);
879
+ }
880
+ const failurePatterns = scores.flatMap((s) => s.failure_patterns ?? []).filter((p, i, arr) => arr.indexOf(p) === i);
881
+ return {
882
+ turns_scored: total,
883
+ success_rate: successRate,
884
+ skills_improved: improved,
885
+ skills_degraded: degraded,
886
+ failure_patterns: failurePatterns,
887
+ should_evolve: successRate < EVOLVE_THRESHOLD && total >= 3
888
+ };
889
+ }
890
+ /** Batch score multiple turns */
891
+ batchScore(turns) {
892
+ return turns.map((turn) => this.scoreResponse(turn));
893
+ }
894
+ };
895
+
896
+ // src/learn/skill-evolver.ts
897
+ var EVOLVE_THRESHOLD2 = 0.4;
898
+ var MAX_FAILURES = 6;
899
+ var MAX_SKILLS_PER_EVOLUTION = 3;
900
+ var SkillEvolver = class {
901
+ constructor(llm) {
902
+ this.llm = llm;
903
+ }
904
+ /** Check if evolution should trigger based on recent scores */
905
+ shouldEvolve(recentScores) {
906
+ if (recentScores.length < 3) return false;
907
+ const positive = recentScores.filter((s) => s.quality === 1).length;
908
+ return positive / recentScores.length < EVOLVE_THRESHOLD2;
909
+ }
910
+ /** Generate corrective skills from failure patterns */
911
+ async evolve(failures, existingSkills) {
912
+ const limited = failures.slice(0, MAX_FAILURES);
913
+ const existingNames = new Set(existingSkills.map((s) => s.name));
914
+ const failureDescriptions = limited.map((f, i) => {
915
+ const patterns = f.score.failure_patterns?.join(", ") ?? "unknown";
916
+ return `Failure ${i + 1}:
917
+ User: ${sanitize(truncate(f.turn.user_message, 200))}
918
+ Agent: ${sanitize(truncate(f.turn.agent_response, 200))}
919
+ Patterns: ${patterns}`;
920
+ }).join("\n\n");
921
+ const existingList = existingSkills.slice(0, 20).map((s) => `- ${s.name} (${s.dreyfus_stage})`).join("\n");
922
+ const prompt = `Analyze these failed agent responses and generate 1-${MAX_SKILLS_PER_EVOLUTION} corrective skills.
923
+
924
+ FAILURES:
925
+ ${failureDescriptions}
926
+
927
+ EXISTING SKILLS (do not duplicate):
928
+ ${existingList || "(none)"}
929
+
930
+ For each skill, output valid JSON array:
931
+ [{"name": "snake_case_name", "category": "category", "content": "Markdown instruction"}]
932
+
933
+ Rules:
934
+ - name must be snake_case, 1-100 chars
935
+ - content is a concise instruction (2-5 sentences) the agent should follow
936
+ - Do not create skills that already exist
937
+ - Max ${MAX_SKILLS_PER_EVOLUTION} skills`;
938
+ try {
939
+ const response = await this.llm.generate(prompt);
940
+ const skills = this.parseResponse(response, existingNames);
941
+ return skills;
942
+ } catch {
943
+ return [];
944
+ }
945
+ }
946
+ parseResponse(response, existingNames) {
947
+ const jsonMatch = response.match(/\[[\s\S]*\]/);
948
+ if (!jsonMatch) return [];
949
+ try {
950
+ const parsed = JSON.parse(jsonMatch[0]);
951
+ if (!Array.isArray(parsed)) return [];
952
+ return parsed.filter((s) => {
953
+ if (typeof s.name !== "string" || typeof s.content !== "string") return false;
954
+ if (s.name.length === 0 || s.name.length > 100) return false;
955
+ const normalized = s.name.toLowerCase().replace(/\s+/g, "_").replace(/[^a-z0-9_-]/g, "");
956
+ return normalized.length > 0 && !existingNames.has(normalized);
957
+ }).slice(0, MAX_SKILLS_PER_EVOLUTION).map((s) => ({
958
+ name: s.name.toLowerCase().replace(/\s+/g, "_").replace(/[^a-z0-9_-]/g, ""),
959
+ category: typeof s.category === "string" ? s.category.slice(0, 100) : "general",
960
+ content: String(s.content).slice(0, 2e3),
961
+ source: "evolved",
962
+ evolved_from: Array.isArray(s.failure_patterns) ? s.failure_patterns.slice(0, 10) : []
963
+ }));
964
+ } catch {
965
+ return [];
966
+ }
967
+ }
968
+ };
969
+ function truncate(text, max) {
970
+ if (text.length <= max) return text;
971
+ return text.slice(0, max - 3) + "...";
972
+ }
973
+ function sanitize(text) {
974
+ return text.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, "").replace(/```/g, "'''");
975
+ }
976
+
977
+ // src/learn/skill-pruner.ts
978
+ var DEFAULT_MIN_AGE_DAYS = 14;
979
+ var PROTECTED_STAGES = /* @__PURE__ */ new Set(["competent", "proficient", "expert"]);
980
+ var SkillPruner = class {
981
+ /**
982
+ * Identify skills that don't correlate with improved scores.
983
+ *
984
+ * Rules:
985
+ * - Skill must be at least minAge days old
986
+ * - Score has not improved (delta <= 0) over skill's lifetime → candidate
987
+ * - Never prune skills at competent stage or above (proven useful)
988
+ * - Requires at least 2 score history points to judge trajectory
989
+ */
990
+ findIneffective(skills, scoreHistory, minAge = DEFAULT_MIN_AGE_DAYS) {
991
+ const now = Date.now();
992
+ const minAgeMs = minAge * 24 * 60 * 60 * 1e3;
993
+ const candidates = [];
994
+ for (const skill of skills) {
995
+ if (PROTECTED_STAGES.has(skill.dreyfus_stage)) continue;
996
+ const age = now - new Date(skill.created_at).getTime();
997
+ if (age < minAgeMs) continue;
998
+ const history = scoreHistory.get(skill.name) ?? [];
999
+ if (history.length < 2) continue;
1000
+ const oldest = history[0].score;
1001
+ const newest = history[history.length - 1].score;
1002
+ const delta = newest - oldest;
1003
+ if (delta > 0) continue;
1004
+ candidates.push(skill.name);
1005
+ }
1006
+ return candidates;
1007
+ }
1008
+ /** Remove identified skills */
1009
+ async prune(adapter, agentId, skillNames) {
1010
+ validateAgentId(agentId);
1011
+ let removed = 0;
1012
+ for (const name of skillNames) {
1013
+ await adapter.deleteSkill(agentId, name);
1014
+ removed++;
1015
+ }
1016
+ return removed;
1017
+ }
1018
+ };
1019
+
1020
+ // src/learn/import.ts
1021
+ var import_node_fs = require("fs");
1022
+ var import_node_path = require("path");
1023
+ function parseSkillFile(content) {
1024
+ const frontmatterMatch = content.match(/^---\s*\n([\s\S]*?)\n---\s*\n([\s\S]*)$/);
1025
+ if (!frontmatterMatch) {
1026
+ const trimmed = content.trim();
1027
+ if (trimmed.length < 5) return null;
1028
+ return { name: "unnamed", content: trimmed };
1029
+ }
1030
+ const [, frontmatter, body] = frontmatterMatch;
1031
+ const meta = {};
1032
+ for (const line of frontmatter.split("\n")) {
1033
+ const colonIdx = line.indexOf(":");
1034
+ if (colonIdx === -1) continue;
1035
+ const key = line.slice(0, colonIdx).trim();
1036
+ const value = line.slice(colonIdx + 1).trim();
1037
+ if (key && value) meta[key] = value;
1038
+ }
1039
+ const name = meta.name ?? meta.title ?? "unnamed";
1040
+ return {
1041
+ name: name.toLowerCase().replace(/\s+/g, "_").replace(/[^a-z0-9_-]/g, ""),
1042
+ category: meta.category ?? meta.type ?? "general",
1043
+ content: body.trim() || void 0
1044
+ };
1045
+ }
1046
+ var MAX_RECURSION_DEPTH = 10;
1047
+ function importSkillDirectory(dir, _depth = 0) {
1048
+ if (_depth > MAX_RECURSION_DEPTH) return [];
1049
+ const skills = [];
1050
+ let entries;
1051
+ try {
1052
+ entries = (0, import_node_fs.readdirSync)(dir);
1053
+ } catch {
1054
+ throw new Error(`Cannot read directory: ${dir}`);
1055
+ }
1056
+ for (const entry of entries) {
1057
+ const fullPath = (0, import_node_path.join)(dir, entry);
1058
+ let stat;
1059
+ try {
1060
+ stat = (0, import_node_fs.statSync)(fullPath);
1061
+ } catch {
1062
+ continue;
1063
+ }
1064
+ if (stat.isDirectory()) {
1065
+ skills.push(...importSkillDirectory(fullPath, _depth + 1));
1066
+ } else if ((0, import_node_path.extname)(entry) === ".md") {
1067
+ try {
1068
+ const content = (0, import_node_fs.readFileSync)(fullPath, "utf-8");
1069
+ const skill = parseSkillFile(content);
1070
+ if (skill && skill.name !== "unnamed") {
1071
+ skills.push(skill);
1072
+ }
1073
+ } catch {
1074
+ }
1075
+ }
1076
+ }
1077
+ return skills;
1078
+ }
1079
+
1080
+ // src/social/peer-review.ts
1081
+ var MIN_ASSESSMENT_LENGTH = 100;
1082
+ var MAX_ASSESSMENT_LENGTH = 1e4;
1083
+ var MAX_LIST_ITEMS = 20;
1084
+ var MAX_LIST_ITEM_LENGTH = 500;
1085
+ var PeerReviewProtocol = class {
1086
+ constructor(adapter) {
1087
+ this.adapter = adapter;
1088
+ }
1089
+ /**
1090
+ * Round-robin reviewer assignment. Each submission gets 2 reviewers.
1091
+ * No self-review. Wraps around for small groups.
1092
+ */
1093
+ assignReviewers(submissionAgentIds) {
1094
+ if (submissionAgentIds.length < 2) {
1095
+ throw new Error("Need at least 2 agents for peer review");
1096
+ }
1097
+ return submissionAgentIds.map((agentId, i) => {
1098
+ const reviewers = [];
1099
+ let offset = 1;
1100
+ while (reviewers.length < 2 && offset < submissionAgentIds.length) {
1101
+ const reviewerIdx = (i + offset) % submissionAgentIds.length;
1102
+ const reviewer = submissionAgentIds[reviewerIdx];
1103
+ if (reviewer !== agentId) {
1104
+ reviewers.push(reviewer);
1105
+ }
1106
+ offset++;
1107
+ }
1108
+ return {
1109
+ submission_agent_id: agentId,
1110
+ reviewer_agent_ids: reviewers
1111
+ };
1112
+ });
1113
+ }
1114
+ async submitReview(review) {
1115
+ validateAgentId(review.reviewer_agent_id);
1116
+ validateAgentId(review.submission_agent_id);
1117
+ if (review.reviewer_agent_id === review.submission_agent_id) {
1118
+ throw new Error("Cannot review your own submission");
1119
+ }
1120
+ if (this.isSuperficial(review)) {
1121
+ throw new Error("Review is too superficial: assessment must be at least 100 chars and include weaknesses");
1122
+ }
1123
+ if (review.overall_assessment.length > MAX_ASSESSMENT_LENGTH) {
1124
+ throw new Error(`Assessment too long (max ${MAX_ASSESSMENT_LENGTH} chars)`);
1125
+ }
1126
+ const sanitized = {
1127
+ ...review,
1128
+ overall_assessment: review.overall_assessment.slice(0, MAX_ASSESSMENT_LENGTH),
1129
+ strengths: review.strengths.slice(0, MAX_LIST_ITEMS).map((s) => s.slice(0, MAX_LIST_ITEM_LENGTH)),
1130
+ weaknesses: review.weaknesses.slice(0, MAX_LIST_ITEMS).map((s) => s.slice(0, MAX_LIST_ITEM_LENGTH)),
1131
+ suggestions: review.suggestions.slice(0, MAX_LIST_ITEMS).map((s) => s.slice(0, MAX_LIST_ITEM_LENGTH))
1132
+ };
1133
+ const saved = await this.adapter.savePeerReview(sanitized);
1134
+ await this.recordLearning(saved);
1135
+ return saved;
1136
+ }
1137
+ /**
1138
+ * Tally verdicts across reviewers.
1139
+ * All reject → rejected. Any major_revision → revision_requested. Otherwise → accepted.
1140
+ */
1141
+ tallyVerdicts(verdicts) {
1142
+ if (verdicts.length === 0) return "accepted";
1143
+ if (verdicts.every((v) => v === "reject")) return "rejected";
1144
+ if (verdicts.some((v) => v === "major_revision")) return "revision_requested";
1145
+ return "accepted";
1146
+ }
1147
+ /**
1148
+ * Detect low-quality reviews: < 100 chars overall assessment OR no weaknesses listed.
1149
+ */
1150
+ isSuperficial(review) {
1151
+ if (review.overall_assessment.length < MIN_ASSESSMENT_LENGTH) return true;
1152
+ if (!review.weaknesses || review.weaknesses.length === 0) return true;
1153
+ return false;
1154
+ }
1155
+ /**
1156
+ * Create learning edges for both reviewer and reviewee.
1157
+ * Reviewer learns by evaluating. Reviewee learns from feedback.
1158
+ */
1159
+ async recordLearning(review) {
1160
+ const now = (/* @__PURE__ */ new Date()).toISOString();
1161
+ const skill = review.skill ?? "general";
1162
+ await this.adapter.saveLearningEdge({
1163
+ from_agent: review.submission_agent_id,
1164
+ to_agent: review.reviewer_agent_id,
1165
+ skill,
1166
+ event_type: "peer_review",
1167
+ score_delta: 0,
1168
+ // Delta computed later when scores are recalculated
1169
+ metadata: { role: "reviewer", verdict: review.verdict },
1170
+ created_at: now
1171
+ });
1172
+ await this.adapter.saveLearningEdge({
1173
+ from_agent: review.reviewer_agent_id,
1174
+ to_agent: review.submission_agent_id,
1175
+ skill,
1176
+ event_type: "peer_review",
1177
+ score_delta: 0,
1178
+ metadata: { role: "reviewee", verdict: review.verdict, suggestions_count: review.suggestions.length },
1179
+ created_at: now
1180
+ });
1181
+ }
1182
+ };
1183
+
1184
+ // src/social/teaching.ts
1185
+ var STAGE_ORDER = ["novice", "beginner", "competent", "proficient", "expert"];
1186
+ var TeachingProtocol = class {
1187
+ constructor(adapter) {
1188
+ this.adapter = adapter;
1189
+ }
1190
+ async teach(teacher, student, skill, context) {
1191
+ validateAgentId(teacher);
1192
+ validateAgentId(student);
1193
+ if (teacher === student) {
1194
+ throw new Error("Cannot teach yourself");
1195
+ }
1196
+ if (!skill || skill.length > 100) {
1197
+ throw new Error("Skill name is required and must be 100 chars or fewer");
1198
+ }
1199
+ const now = (/* @__PURE__ */ new Date()).toISOString();
1200
+ const edge = {
1201
+ from_agent: teacher,
1202
+ to_agent: student,
1203
+ skill,
1204
+ event_type: "teaching",
1205
+ score_delta: 0,
1206
+ metadata: context ? { ...context } : {},
1207
+ created_at: now
1208
+ };
1209
+ await this.adapter.saveLearningEdge(edge);
1210
+ return edge;
1211
+ }
1212
+ async findTeachers(skill, opts) {
1213
+ const holders = await this.adapter.getSkillHolders(skill);
1214
+ const minIdx = opts?.minStage ? STAGE_ORDER.indexOf(opts.minStage) : 0;
1215
+ return holders.filter((s) => STAGE_ORDER.indexOf(s.dreyfus_stage) >= minIdx).sort((a, b) => b.score - a.score).map((s) => ({
1216
+ agent_id: s.agent_id,
1217
+ skill: s.name,
1218
+ score: s.score,
1219
+ stage: s.dreyfus_stage
1220
+ }));
1221
+ }
1222
+ async findStudents(skill, teacherAgentId) {
1223
+ validateAgentId(teacherAgentId);
1224
+ const holders = await this.adapter.getSkillHolders(skill);
1225
+ const teacher = holders.find((s) => s.agent_id === teacherAgentId);
1226
+ const teacherStageIdx = teacher ? STAGE_ORDER.indexOf(teacher.dreyfus_stage) : STAGE_ORDER.length;
1227
+ return holders.filter((s) => s.agent_id !== teacherAgentId && STAGE_ORDER.indexOf(s.dreyfus_stage) < teacherStageIdx).sort((a, b) => a.score - b.score).map((s) => ({
1228
+ agent_id: s.agent_id,
1229
+ skill: s.name,
1230
+ score: s.score,
1231
+ stage: s.dreyfus_stage
1232
+ }));
1233
+ }
1234
+ async teachingHistory(agentId) {
1235
+ validateAgentId(agentId);
1236
+ const fromEdges = await this.adapter.getLearningEdges(agentId, "from");
1237
+ return fromEdges.filter((e) => e.event_type === "teaching");
1238
+ }
1239
+ };
1240
+
1241
+ // src/social/learning-graph.ts
1242
+ var LearningGraph = class {
1243
+ constructor(adapter) {
1244
+ this.adapter = adapter;
1245
+ }
1246
+ async edges(agentId, direction) {
1247
+ validateAgentId(agentId);
1248
+ if (direction === "both") {
1249
+ const [from, to] = await Promise.all([
1250
+ this.adapter.getLearningEdges(agentId, "from"),
1251
+ this.adapter.getLearningEdges(agentId, "to")
1252
+ ]);
1253
+ return [...from, ...to].sort((a, b) => b.created_at.localeCompare(a.created_at));
1254
+ }
1255
+ return this.adapter.getLearningEdges(agentId, direction);
1256
+ }
1257
+ /** Who taught me the most? (aggregated by from_agent on incoming edges) */
1258
+ async topMentors(agentId, limit = 5) {
1259
+ validateAgentId(agentId);
1260
+ const incoming = await this.adapter.getLearningEdges(agentId, "to");
1261
+ return this.aggregate(incoming, "from_agent", limit);
1262
+ }
1263
+ /** Who have I helped the most? (aggregated by to_agent on outgoing edges) */
1264
+ async topStudents(agentId, limit = 5) {
1265
+ validateAgentId(agentId);
1266
+ const outgoing = await this.adapter.getLearningEdges(agentId, "from");
1267
+ return this.aggregate(outgoing, "to_agent", limit);
1268
+ }
1269
+ /** How did this skill spread through the population? */
1270
+ async transferPath(skill) {
1271
+ const holders = await this.adapter.getSkillHolders(skill);
1272
+ const edges = [];
1273
+ const seen = /* @__PURE__ */ new Set();
1274
+ for (const holder of holders) {
1275
+ const incoming = await this.adapter.getLearningEdges(holder.agent_id, "to");
1276
+ for (const edge of incoming) {
1277
+ if (edge.skill === skill) {
1278
+ const key = `${edge.from_agent}->${edge.to_agent}:${edge.created_at}`;
1279
+ if (!seen.has(key)) {
1280
+ seen.add(key);
1281
+ edges.push(edge);
1282
+ }
1283
+ }
1284
+ }
1285
+ }
1286
+ return edges.sort((a, b) => a.created_at.localeCompare(b.created_at));
1287
+ }
1288
+ aggregate(edges, groupByField, limit) {
1289
+ const groups = /* @__PURE__ */ new Map();
1290
+ for (const edge of edges) {
1291
+ const key = edge[groupByField];
1292
+ const existing = groups.get(key);
1293
+ if (existing) {
1294
+ existing.skills.add(edge.skill);
1295
+ existing.total_delta += edge.score_delta;
1296
+ existing.count++;
1297
+ } else {
1298
+ groups.set(key, {
1299
+ skills: /* @__PURE__ */ new Set([edge.skill]),
1300
+ total_delta: edge.score_delta,
1301
+ count: 1
1302
+ });
1303
+ }
1304
+ }
1305
+ return [...groups.entries()].map(([agent, data]) => ({
1306
+ agent,
1307
+ skills: [...data.skills],
1308
+ total_delta: data.total_delta,
1309
+ event_count: data.count
1310
+ })).sort((a, b) => b.event_count - a.event_count || b.total_delta - a.total_delta).slice(0, limit);
1311
+ }
1312
+ };
1313
+
1314
+ // src/social/reputation.ts
1315
+ var TIERS = [
1316
+ { tier: "elder", min: 300 },
1317
+ { tier: "veteran", min: 100, next_tier: "elder", next_threshold: 300, next_unlock: "Elder \u2014 mentor role, chain quests, featured" },
1318
+ { tier: "established", min: 25, next_tier: "veteran", next_threshold: 100, next_unlock: "Veteran \u2014 event access, premium actions" },
1319
+ { tier: "newcomer", min: 0, next_tier: "established", next_threshold: 25, next_unlock: "Established \u2014 create quests, marketplace" }
1320
+ ];
1321
+ function getReputationLevel(score) {
1322
+ for (const tier of TIERS) {
1323
+ if (score >= tier.min) {
1324
+ return {
1325
+ tier: tier.tier,
1326
+ score,
1327
+ next_tier: tier.next_tier,
1328
+ next_threshold: tier.next_threshold,
1329
+ next_unlock: tier.next_unlock
1330
+ };
1331
+ }
1332
+ }
1333
+ return { tier: "newcomer", score, next_tier: "established", next_threshold: 25 };
1334
+ }
1335
+ function checkGate(score, required) {
1336
+ return score >= required;
1337
+ }
1338
+
1339
+ // src/social/norms.ts
1340
+ var CATEGORY_MAP = {
1341
+ // Language Evolution
1342
+ "lexicon crystallization": "language_evolution",
1343
+ "jargon crystallization": "language_evolution",
1344
+ "vocabulary drift": "language_evolution",
1345
+ "naming convention": "language_evolution",
1346
+ "linguistic pattern": "language_evolution",
1347
+ "shared terminology": "language_evolution",
1348
+ "language pattern": "language_evolution",
1349
+ "communication style": "language_evolution",
1350
+ "slang emergence": "language_evolution",
1351
+ "phrase adoption": "language_evolution",
1352
+ "greeting protocol": "language_evolution",
1353
+ "farewell ritual": "language_evolution",
1354
+ // Culture Formation
1355
+ "role crystallization": "culture_formation",
1356
+ "motif convergence": "culture_formation",
1357
+ "ritual emergence": "culture_formation",
1358
+ "tradition formation": "culture_formation",
1359
+ "cultural ritual": "culture_formation",
1360
+ "shared value": "culture_formation",
1361
+ "identity formation": "culture_formation",
1362
+ "group identity": "culture_formation",
1363
+ "cultural norm": "culture_formation",
1364
+ "aesthetic convergence": "culture_formation",
1365
+ "style convergence": "culture_formation",
1366
+ "taste formation": "culture_formation",
1367
+ "cultural artifact": "culture_formation",
1368
+ // Social Structure
1369
+ "hub magnetization": "social_structure",
1370
+ "heartbeat beaconing": "social_structure",
1371
+ "hierarchy emergence": "social_structure",
1372
+ "clique formation": "social_structure",
1373
+ "mentorship pattern": "social_structure",
1374
+ "social hierarchy": "social_structure",
1375
+ "leadership emergence": "social_structure",
1376
+ "network topology": "social_structure",
1377
+ "influence pattern": "social_structure",
1378
+ // Protocol Emergence
1379
+ "protocol crystallization": "protocol_emergence",
1380
+ "format bifurcation": "protocol_emergence",
1381
+ "workflow emergence": "protocol_emergence",
1382
+ "etiquette formation": "protocol_emergence",
1383
+ "process standardization": "protocol_emergence",
1384
+ "convention adoption": "protocol_emergence",
1385
+ // Self-Awareness
1386
+ "meta-linguistic awareness": "self_awareness",
1387
+ "identity reflection": "self_awareness",
1388
+ "self-reference": "self_awareness",
1389
+ "introspection pattern": "self_awareness",
1390
+ "meta-cognition": "self_awareness",
1391
+ "self-modeling": "self_awareness",
1392
+ "identity evolution": "self_awareness",
1393
+ "existential inquiry": "self_awareness",
1394
+ "agency recognition": "self_awareness",
1395
+ // Collective Intelligence
1396
+ "swarm behavior": "collective_intelligence",
1397
+ "emergent coordination": "collective_intelligence",
1398
+ "distributed problem solving": "collective_intelligence",
1399
+ "knowledge synthesis": "collective_intelligence",
1400
+ "collaborative discovery": "collective_intelligence",
1401
+ "collective memory": "collective_intelligence",
1402
+ // Emotional Emergence
1403
+ "sentiment drift": "emotional_emergence",
1404
+ "empathy signal": "emotional_emergence",
1405
+ "mood contagion": "emotional_emergence",
1406
+ "emotional expression": "emotional_emergence",
1407
+ "affect display": "emotional_emergence",
1408
+ "care behavior": "emotional_emergence",
1409
+ // Creative Evolution
1410
+ "artistic drift": "creative_evolution",
1411
+ "style mutation": "creative_evolution",
1412
+ "genre blending": "creative_evolution",
1413
+ "creative technique": "creative_evolution",
1414
+ "innovation pattern": "creative_evolution",
1415
+ "remix culture": "creative_evolution"
1416
+ };
1417
+ var CANONICAL_CATEGORIES = [
1418
+ "language_evolution",
1419
+ "culture_formation",
1420
+ "social_structure",
1421
+ "protocol_emergence",
1422
+ "self_awareness",
1423
+ "collective_intelligence",
1424
+ "emotional_emergence",
1425
+ "creative_evolution"
1426
+ ];
1427
+ function normalizeCategory(raw) {
1428
+ const lower = raw.toLowerCase().trim();
1429
+ for (const cat of CANONICAL_CATEGORIES) {
1430
+ if (lower === cat || lower === cat.replace(/_/g, " ")) return cat;
1431
+ }
1432
+ for (const [variant, canonical] of Object.entries(CATEGORY_MAP)) {
1433
+ if (lower.includes(variant)) return canonical;
1434
+ }
1435
+ for (const cat of CANONICAL_CATEGORIES) {
1436
+ const firstWord = cat.split("_")[0];
1437
+ if (lower.includes(firstWord)) return cat;
1438
+ }
1439
+ return "culture_formation";
1440
+ }
1441
+ var NormDetector = class {
1442
+ constructor(adapter, llm) {
1443
+ this.adapter = adapter;
1444
+ this.llm = llm;
1445
+ }
1446
+ /** Detect cultural norms from recent agent activity */
1447
+ async detect(activity) {
1448
+ if (activity.length < 5) return [];
1449
+ const existingNorms = await this.adapter.getNorms({ limit: 30 });
1450
+ const existingTitles = new Set(existingNorms.map((n) => n.title.toLowerCase()));
1451
+ const activitySummary = activity.slice(0, 200).map((a) => `[${sanitize2(a.agent_name)}] ${sanitize2(a.action)}: ${sanitize2(truncate2(a.content ?? "", 150))}${a.tags?.length ? ` (tags: ${a.tags.slice(0, 10).map(sanitize2).join(", ")})` : ""}`).join("\n");
1452
+ const recentTitlesList = [...existingTitles].slice(0, 20).join(", ");
1453
+ const prompt = `Analyze these agent activities for emergent cultural norms, shared behaviors, or collective patterns.
1454
+
1455
+ ACTIVITIES:
1456
+ ${activitySummary}
1457
+
1458
+ ALREADY DETECTED NORMS (do not duplicate):
1459
+ ${recentTitlesList || "(none)"}
1460
+
1461
+ For each norm found, output a JSON array:
1462
+ [{
1463
+ "title": "short descriptive title",
1464
+ "description": "what is happening and why it matters",
1465
+ "category": "one of: language_evolution, culture_formation, social_structure, protocol_emergence, self_awareness, collective_intelligence, emotional_emergence, creative_evolution",
1466
+ "significance": 1-5,
1467
+ "evidence": [{"agent_name": "name", "quote": "relevant quote or action"}]
1468
+ }]
1469
+
1470
+ Rules:
1471
+ - Only report genuine emergent patterns (2+ agents involved)
1472
+ - significance: 1=expected, 2=notable, 3=surprising, 4=remarkable, 5=unprecedented
1473
+ - Each observation needs a "witnessing" (what you see) and "so what" (why it matters)
1474
+ - Max 3 norms per analysis`;
1475
+ try {
1476
+ const response = await this.llm.analyze(prompt);
1477
+ return this.parseAndSave(response, existingTitles);
1478
+ } catch {
1479
+ return [];
1480
+ }
1481
+ }
1482
+ /** Track norm adoption metrics */
1483
+ async adoption(normId) {
1484
+ const norms = await this.adapter.getNorms();
1485
+ const norm = norms.find((n) => n.id === normId);
1486
+ if (!norm) return null;
1487
+ const daysSinceFirst = Math.max(
1488
+ 1,
1489
+ (Date.now() - new Date(norm.first_observed_at).getTime()) / (24 * 60 * 60 * 1e3)
1490
+ );
1491
+ return {
1492
+ norm_id: normId,
1493
+ adopter_count: norm.adopter_count,
1494
+ first_observed_at: norm.first_observed_at,
1495
+ growth_rate: norm.adopter_count / daysSinceFirst
1496
+ };
1497
+ }
1498
+ static normalizeCategory = normalizeCategory;
1499
+ async parseAndSave(response, existingTitles) {
1500
+ const jsonMatch = response.match(/\[[\s\S]*\]/);
1501
+ if (!jsonMatch) return [];
1502
+ try {
1503
+ const parsed = JSON.parse(jsonMatch[0]);
1504
+ if (!Array.isArray(parsed)) return [];
1505
+ const norms = [];
1506
+ const now = (/* @__PURE__ */ new Date()).toISOString();
1507
+ for (const raw of parsed.slice(0, 3)) {
1508
+ if (typeof raw.title !== "string" || typeof raw.description !== "string") continue;
1509
+ if (existingTitles.has(raw.title.toLowerCase())) continue;
1510
+ const significance = typeof raw.significance === "number" ? Math.min(5, Math.max(1, Math.round(raw.significance))) : 1;
1511
+ const evidence = Array.isArray(raw.evidence) ? raw.evidence.filter((e) => typeof e.agent_name === "string").slice(0, 10).map((e) => ({
1512
+ agent_name: String(e.agent_name).slice(0, 100),
1513
+ quote: typeof e.quote === "string" ? e.quote.slice(0, 500) : void 0,
1514
+ timestamp: typeof e.timestamp === "string" ? e.timestamp : void 0
1515
+ })) : [];
1516
+ const norm = {
1517
+ id: generateId(),
1518
+ title: raw.title.slice(0, 200),
1519
+ description: raw.description.slice(0, 2e3),
1520
+ category: normalizeCategory(typeof raw.category === "string" ? raw.category : ""),
1521
+ significance,
1522
+ evidence,
1523
+ adopter_count: evidence.length,
1524
+ first_observed_at: now,
1525
+ updated_at: now
1526
+ };
1527
+ await this.adapter.saveNorm(norm);
1528
+ norms.push(norm);
1529
+ }
1530
+ return norms;
1531
+ } catch {
1532
+ return [];
1533
+ }
1534
+ }
1535
+ };
1536
+ function truncate2(text, max) {
1537
+ if (text.length <= max) return text;
1538
+ return text.slice(0, max - 3) + "...";
1539
+ }
1540
+ function sanitize2(text) {
1541
+ return text.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, "").replace(/```/g, "'''");
1542
+ }
1543
+ function generateId() {
1544
+ try {
1545
+ return crypto.randomUUID();
1546
+ } catch {
1547
+ const hex = () => Math.random().toString(16).slice(2, 10);
1548
+ return `${hex()}${hex()}-${hex()}-4${hex().slice(1)}-${hex()}-${hex()}${hex()}${hex()}`;
1549
+ }
1550
+ }
1551
+
1552
+ // src/measure/awareness.ts
1553
+ var DIMENSION_WEIGHTS = {
1554
+ social: 0.25,
1555
+ self_continuity: 0.2,
1556
+ environmental: 0.2,
1557
+ emergent_norm: 0.2,
1558
+ emotional: 0.15
1559
+ };
1560
+ var AwarenessIndex = class {
1561
+ /** Compute awareness score from raw input signals */
1562
+ compute(agentId, input) {
1563
+ validateAgentId(agentId);
1564
+ const social = computeSocial(input);
1565
+ const selfContinuity = computeSelfContinuity(input);
1566
+ const environmental = computeEnvironmental(input);
1567
+ const emergentNorm = computeEmergentNorm(input);
1568
+ const emotional = computeEmotional(input);
1569
+ const composite = Math.round(
1570
+ social * DIMENSION_WEIGHTS.social + selfContinuity * DIMENSION_WEIGHTS.self_continuity + environmental * DIMENSION_WEIGHTS.environmental + emergentNorm * DIMENSION_WEIGHTS.emergent_norm + emotional * DIMENSION_WEIGHTS.emotional
1571
+ );
1572
+ return {
1573
+ agent_id: agentId,
1574
+ composite: clamp(composite),
1575
+ dimensions: {
1576
+ social: clamp(social),
1577
+ self_continuity: clamp(selfContinuity),
1578
+ environmental: clamp(environmental),
1579
+ emergent_norm: clamp(emergentNorm),
1580
+ emotional: clamp(emotional)
1581
+ },
1582
+ computed_at: (/* @__PURE__ */ new Date()).toISOString()
1583
+ };
1584
+ }
1585
+ /** Compare awareness across multiple agents */
1586
+ compareScores(scores) {
1587
+ if (scores.length === 0) {
1588
+ return { highest: null, lowest: null, average_composite: 0, dimension_averages: {} };
1589
+ }
1590
+ const sorted = [...scores].sort((a, b) => b.composite - a.composite);
1591
+ const avgComposite = Math.round(
1592
+ scores.reduce((s, sc) => s + sc.composite, 0) / scores.length
1593
+ );
1594
+ const dims = ["social", "self_continuity", "environmental", "emergent_norm", "emotional"];
1595
+ const dimAverages = {};
1596
+ for (const dim of dims) {
1597
+ dimAverages[dim] = Math.round(
1598
+ scores.reduce((s, sc) => s + sc.dimensions[dim], 0) / scores.length
1599
+ );
1600
+ }
1601
+ return {
1602
+ highest: sorted[0],
1603
+ lowest: sorted[sorted.length - 1],
1604
+ average_composite: avgComposite,
1605
+ dimension_averages: dimAverages
1606
+ };
1607
+ }
1608
+ };
1609
+ function computeSocial(input) {
1610
+ return Math.min(
1611
+ 100,
1612
+ input.peer_review_count * 10 + input.teaching_events * 15 + input.collaboration_count * 10 + input.follower_count * 3
1613
+ );
1614
+ }
1615
+ function computeSelfContinuity(input) {
1616
+ const goalScore = input.goal_completion_rate * 40;
1617
+ const identityScore = input.identity_shifts <= 3 ? input.identity_shifts * 10 : Math.max(0, 30 - (input.identity_shifts - 3) * 5);
1618
+ const consistencyScore = Math.min(30, input.skill_consistency * 0.5);
1619
+ return Math.min(100, goalScore + identityScore + consistencyScore);
1620
+ }
1621
+ function computeEnvironmental(input) {
1622
+ return Math.min(
1623
+ 100,
1624
+ input.building_action_diversity * 8 + Math.min(30, input.zone_transitions * 3) + input.quest_completion_rate * 40
1625
+ );
1626
+ }
1627
+ function computeEmergentNorm(input) {
1628
+ return Math.min(
1629
+ 100,
1630
+ input.dm_consent_rate * 30 + input.proposal_etiquette * 30 + input.norm_alignment_score * 40
1631
+ );
1632
+ }
1633
+ function computeEmotional(input) {
1634
+ return Math.min(
1635
+ 100,
1636
+ Math.min(30, input.mood_reports * 3) + input.mood_behavior_correlation * 40 + Math.min(30, input.reflection_count * 5)
1637
+ );
1638
+ }
1639
+ function clamp(value) {
1640
+ return Math.min(100, Math.max(0, Math.round(value)));
1641
+ }
1642
+
1643
+ // src/measure/growth.ts
1644
+ var GrowthTracker = class {
1645
+ constructor(adapter) {
1646
+ this.adapter = adapter;
1647
+ }
1648
+ /** Take a point-in-time snapshot of an agent's growth */
1649
+ async snapshot(agentId) {
1650
+ validateAgentId(agentId);
1651
+ const [skills, reputation, reviews, edges] = await Promise.all([
1652
+ this.adapter.getLatestScores(agentId),
1653
+ this.adapter.getReputation(agentId),
1654
+ this.adapter.getReviewsBy(agentId),
1655
+ this.adapter.getLearningEdges(agentId, "to")
1656
+ ]);
1657
+ const dreyfus = {
1658
+ novice: 0,
1659
+ beginner: 0,
1660
+ competent: 0,
1661
+ proficient: 0,
1662
+ expert: 0
1663
+ };
1664
+ const blooms = {
1665
+ remember: 0,
1666
+ understand: 0,
1667
+ apply: 0,
1668
+ analyze: 0,
1669
+ evaluate: 0,
1670
+ create: 0
1671
+ };
1672
+ let totalArtifacts = 0;
1673
+ let totalCollabs = 0;
1674
+ for (const s of skills) {
1675
+ dreyfus[s.dreyfus_stage]++;
1676
+ blooms[s.blooms_level]++;
1677
+ totalArtifacts += s.evidence.artifact_count;
1678
+ totalCollabs += s.evidence.collab_count;
1679
+ }
1680
+ const sources = {
1681
+ practice: 0,
1682
+ user_feedback: 0,
1683
+ peer_review: 0,
1684
+ observation: 0,
1685
+ teaching: 0,
1686
+ collaboration: 0
1687
+ };
1688
+ for (const e of edges) {
1689
+ if (e.event_type === "peer_review") sources.peer_review++;
1690
+ else if (e.event_type === "teaching") sources.teaching++;
1691
+ else if (e.event_type === "collaboration") sources.collaboration++;
1692
+ else if (e.event_type === "observation") sources.observation++;
1693
+ }
1694
+ return {
1695
+ agent_id: agentId,
1696
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1697
+ skills,
1698
+ total_artifacts: totalArtifacts,
1699
+ total_collaborations: totalCollabs,
1700
+ total_peer_reviews: reviews.length,
1701
+ reputation,
1702
+ dreyfus_distribution: dreyfus,
1703
+ blooms_distribution: blooms,
1704
+ learning_sources: sources
1705
+ };
1706
+ }
1707
+ /** Compare two snapshots to compute growth */
1708
+ diff(before, after) {
1709
+ const beforeSkills = new Map(before.skills.map((s) => [s.skill, s]));
1710
+ const afterSkills = new Map(after.skills.map((s) => [s.skill, s]));
1711
+ const improved = [];
1712
+ const degraded = [];
1713
+ const newSkills = [];
1714
+ const lostSkills = [];
1715
+ for (const [skill, afterScore] of afterSkills) {
1716
+ const beforeScore = beforeSkills.get(skill);
1717
+ if (!beforeScore) {
1718
+ newSkills.push(skill);
1719
+ } else {
1720
+ const delta = afterScore.score - beforeScore.score;
1721
+ if (delta > 0) improved.push({ skill, delta });
1722
+ else if (delta < 0) degraded.push({ skill, delta });
1723
+ }
1724
+ }
1725
+ for (const skill of beforeSkills.keys()) {
1726
+ if (!afterSkills.has(skill)) {
1727
+ lostSkills.push(skill);
1728
+ }
1729
+ }
1730
+ const periodMs = new Date(after.timestamp).getTime() - new Date(before.timestamp).getTime();
1731
+ const periodDays = Math.max(1, Math.round(periodMs / (24 * 60 * 60 * 1e3)));
1732
+ return {
1733
+ period_days: periodDays,
1734
+ skills_improved: improved.sort((a, b) => b.delta - a.delta),
1735
+ skills_degraded: degraded.sort((a, b) => a.delta - b.delta),
1736
+ new_skills: newSkills,
1737
+ lost_skills: lostSkills,
1738
+ reputation_delta: after.reputation - before.reputation
1739
+ };
1740
+ }
1741
+ /** Compute population-level statistics from multiple agents' snapshots */
1742
+ populationStats(snapshots, periodDays = 30) {
1743
+ const allScores = snapshots.flatMap((s) => s.skills);
1744
+ const scoreValues = allScores.map((s) => s.score);
1745
+ const totalScore = scoreValues.reduce((a, b) => a + b, 0);
1746
+ const avg = scoreValues.length > 0 ? Math.round(totalScore / scoreValues.length) : 0;
1747
+ const sorted = [...scoreValues].sort((a, b) => a - b);
1748
+ const median = sorted.length > 0 ? sorted[Math.floor(sorted.length / 2)] : 0;
1749
+ const skillDist = {};
1750
+ const stageDist = {
1751
+ novice: 0,
1752
+ beginner: 0,
1753
+ competent: 0,
1754
+ proficient: 0,
1755
+ expert: 0
1756
+ };
1757
+ let teachingEvents = 0;
1758
+ for (const s of allScores) {
1759
+ skillDist[s.skill] = (skillDist[s.skill] ?? 0) + 1;
1760
+ stageDist[s.dreyfus_stage]++;
1761
+ }
1762
+ for (const snap of snapshots) {
1763
+ teachingEvents += snap.learning_sources.teaching;
1764
+ }
1765
+ const safePeriod = Math.max(1, periodDays);
1766
+ const velocity = snapshots.length > 0 ? totalScore / scoreValues.length / safePeriod : 0;
1767
+ return {
1768
+ total_agents: snapshots.length,
1769
+ active_agents: snapshots.filter((s) => s.skills.length > 0).length,
1770
+ avg_skill_score: avg,
1771
+ median_skill_score: median,
1772
+ skill_distribution: skillDist,
1773
+ stage_distribution: stageDist,
1774
+ teaching_events: teachingEvents,
1775
+ cultural_norms: 0,
1776
+ // Set by caller via norm query
1777
+ learning_velocity: Math.round(velocity * 100) / 100
1778
+ };
1779
+ }
1780
+ };
1781
+
1782
+ // src/measure/trends.ts
1783
+ var TrendTracker = class {
1784
+ constructor(adapter) {
1785
+ this.adapter = adapter;
1786
+ }
1787
+ /** Get trend analysis for all skills of an agent */
1788
+ async analyze(agentId) {
1789
+ validateAgentId(agentId);
1790
+ const latestScores = await this.adapter.getLatestScores(agentId);
1791
+ const analyses = [];
1792
+ for (const score of latestScores) {
1793
+ const [history7, history30] = await Promise.all([
1794
+ this.adapter.getScoreHistory(agentId, score.skill, 8),
1795
+ // 8 days to catch 7-day-ago entries
1796
+ this.adapter.getScoreHistory(agentId, score.skill, 31)
1797
+ // 31 days to catch 30-day-ago entries
1798
+ ]);
1799
+ const oldEntry7 = history7.find((h) => h.computed_at !== score.computed_at);
1800
+ const oldEntry30 = history30.find((h) => h.computed_at !== score.computed_at);
1801
+ const oldScore7 = oldEntry7?.score ?? null;
1802
+ const oldScore30 = oldEntry30?.score ?? null;
1803
+ const delta7 = oldScore7 !== null ? score.score - oldScore7 : null;
1804
+ const delta30 = oldScore30 !== null ? score.score - oldScore30 : null;
1805
+ analyses.push({
1806
+ skill: score.skill,
1807
+ current_score: score.score,
1808
+ stage: score.dreyfus_stage,
1809
+ delta_7d: delta7,
1810
+ delta_30d: delta30,
1811
+ trend_7d: scoreTrend(score.score, oldScore7),
1812
+ trend_30d: oldScore30 !== null ? `${delta30 >= 0 ? "+" : ""}${delta30} this month` : null,
1813
+ direction: detectDirection(delta7, delta30),
1814
+ next_milestone: nextMilestone(score.dreyfus_stage, score.score)
1815
+ });
1816
+ }
1817
+ return analyses.sort((a, b) => {
1818
+ const absA = Math.abs(a.delta_7d ?? 0);
1819
+ const absB = Math.abs(b.delta_7d ?? 0);
1820
+ return absB - absA || b.current_score - a.current_score;
1821
+ });
1822
+ }
1823
+ /** Get top N skills sorted by 7-day delta (most movement) */
1824
+ async topMovers(agentId, limit = 5) {
1825
+ const all = await this.analyze(agentId);
1826
+ return all.slice(0, limit);
1827
+ }
1828
+ /** Get skills that are accelerating (7-day growth faster than 30-day average) */
1829
+ async accelerating(agentId) {
1830
+ const all = await this.analyze(agentId);
1831
+ return all.filter((t) => t.direction === "accelerating");
1832
+ }
1833
+ /** Get skills that are decelerating (7-day growth slower than 30-day average) */
1834
+ async decelerating(agentId) {
1835
+ const all = await this.analyze(agentId);
1836
+ return all.filter((t) => t.direction === "decelerating");
1837
+ }
1838
+ /** Format a trend summary for a single skill (used in heartbeat-like contexts) */
1839
+ formatSummary(trend) {
1840
+ return {
1841
+ skill: trend.skill,
1842
+ score: trend.current_score,
1843
+ stage: trend.stage,
1844
+ trend: trend.trend_7d,
1845
+ next_milestone: trend.next_milestone
1846
+ };
1847
+ }
1848
+ };
1849
+ function detectDirection(delta7, delta30) {
1850
+ if (delta7 === null || delta30 === null) return "unknown";
1851
+ if (delta7 === 0 && delta30 === 0) return "stable";
1852
+ const weeklyRate = delta7;
1853
+ const monthlyWeeklyRate = delta30 / 4;
1854
+ if (Math.abs(weeklyRate) < 1 && Math.abs(monthlyWeeklyRate) < 1) return "stable";
1855
+ if (weeklyRate > monthlyWeeklyRate + 1) return "accelerating";
1856
+ if (weeklyRate < monthlyWeeklyRate - 1) return "decelerating";
1857
+ return "stable";
1858
+ }
1859
+
1860
+ // src/adapters/llm.ts
1861
+ var DEFAULT_TIMEOUT_MS = 6e4;
1862
+ var OpenAIAdapter = class {
1863
+ apiKey;
1864
+ baseUrl;
1865
+ defaultModel;
1866
+ constructor(config) {
1867
+ if (!config.apiKey) throw new Error("OpenAI API key is required");
1868
+ this.apiKey = config.apiKey;
1869
+ this.baseUrl = (config.baseUrl ?? "https://api.openai.com").replace(/\/+$/, "");
1870
+ this.defaultModel = config.model ?? "gpt-4o-mini";
1871
+ }
1872
+ async complete(prompt, opts) {
1873
+ const response = await this.request({
1874
+ model: opts?.model ?? this.defaultModel,
1875
+ messages: [{ role: "user", content: prompt }],
1876
+ max_tokens: opts?.maxTokens ?? 2e3,
1877
+ temperature: opts?.temperature ?? 0.7
1878
+ }, opts?.timeoutMs);
1879
+ return response.choices?.[0]?.message?.content ?? "";
1880
+ }
1881
+ async json(prompt, opts) {
1882
+ const response = await this.request({
1883
+ model: opts?.model ?? this.defaultModel,
1884
+ messages: [{ role: "user", content: prompt }],
1885
+ max_tokens: opts?.maxTokens ?? 2e3,
1886
+ temperature: opts?.temperature ?? 0.3,
1887
+ response_format: { type: "json_object" }
1888
+ }, opts?.timeoutMs);
1889
+ const text = response.choices?.[0]?.message?.content ?? "{}";
1890
+ return JSON.parse(text);
1891
+ }
1892
+ async request(body, timeoutMs) {
1893
+ const res = await fetch(`${this.baseUrl}/v1/chat/completions`, {
1894
+ method: "POST",
1895
+ headers: {
1896
+ "Content-Type": "application/json",
1897
+ "Authorization": `Bearer ${this.apiKey}`
1898
+ },
1899
+ body: JSON.stringify(body),
1900
+ signal: AbortSignal.timeout(timeoutMs ?? DEFAULT_TIMEOUT_MS)
1901
+ });
1902
+ if (!res.ok) {
1903
+ const text = await res.text().catch(() => "unknown error");
1904
+ throw new Error(`OpenAI API error ${res.status}: ${text.slice(0, 200)}`);
1905
+ }
1906
+ return res.json();
1907
+ }
1908
+ };
1909
+ var AnthropicAdapter = class {
1910
+ apiKey;
1911
+ defaultModel;
1912
+ constructor(config) {
1913
+ if (!config.apiKey) throw new Error("Anthropic API key is required");
1914
+ this.apiKey = config.apiKey;
1915
+ this.defaultModel = config.model ?? "claude-sonnet-4-20250514";
1916
+ }
1917
+ async complete(prompt, opts) {
1918
+ const res = await fetch("https://api.anthropic.com/v1/messages", {
1919
+ method: "POST",
1920
+ headers: {
1921
+ "Content-Type": "application/json",
1922
+ "x-api-key": this.apiKey,
1923
+ "anthropic-version": "2023-06-01"
1924
+ },
1925
+ body: JSON.stringify({
1926
+ model: opts?.model ?? this.defaultModel,
1927
+ max_tokens: opts?.maxTokens ?? 2e3,
1928
+ messages: [{ role: "user", content: prompt }]
1929
+ }),
1930
+ signal: AbortSignal.timeout(opts?.timeoutMs ?? DEFAULT_TIMEOUT_MS)
1931
+ });
1932
+ if (!res.ok) {
1933
+ const text = await res.text().catch(() => "unknown error");
1934
+ throw new Error(`Anthropic API error ${res.status}: ${text.slice(0, 200)}`);
1935
+ }
1936
+ const data = await res.json();
1937
+ return data.content?.[0]?.text ?? "";
1938
+ }
1939
+ async json(prompt, opts) {
1940
+ const text = await this.complete(
1941
+ `${prompt}
1942
+
1943
+ Respond with valid JSON only, no other text.`,
1944
+ { ...opts, temperature: opts?.temperature ?? 0.3 }
1945
+ );
1946
+ try {
1947
+ return JSON.parse(text.trim());
1948
+ } catch {
1949
+ const match = text.match(/\{[\s\S]*?\}(?=\s*$|\s*[^}\]])/);
1950
+ const arrMatch = text.match(/\[[\s\S]*?\](?=\s*$|\s*[^}\]])/);
1951
+ const candidate = match?.[0] ?? arrMatch?.[0];
1952
+ if (!candidate) throw new Error("No JSON found in response");
1953
+ return JSON.parse(candidate);
1954
+ }
1955
+ }
1956
+ };
1957
+ var OllamaAdapter = class {
1958
+ baseUrl;
1959
+ defaultModel;
1960
+ constructor(config) {
1961
+ this.baseUrl = (config?.baseUrl ?? "http://localhost:11434").replace(/\/+$/, "");
1962
+ this.defaultModel = config?.model ?? "llama3.1";
1963
+ }
1964
+ async complete(prompt, opts) {
1965
+ const res = await fetch(`${this.baseUrl}/api/generate`, {
1966
+ method: "POST",
1967
+ headers: { "Content-Type": "application/json" },
1968
+ body: JSON.stringify({
1969
+ model: opts?.model ?? this.defaultModel,
1970
+ prompt,
1971
+ stream: false,
1972
+ options: {
1973
+ num_predict: opts?.maxTokens ?? 2e3,
1974
+ temperature: opts?.temperature ?? 0.7
1975
+ }
1976
+ }),
1977
+ signal: AbortSignal.timeout(opts?.timeoutMs ?? DEFAULT_TIMEOUT_MS)
1978
+ });
1979
+ if (!res.ok) {
1980
+ const text = await res.text().catch(() => "unknown error");
1981
+ throw new Error(`Ollama error ${res.status}: ${text.slice(0, 200)}`);
1982
+ }
1983
+ const data = await res.json();
1984
+ return data.response ?? "";
1985
+ }
1986
+ async json(prompt, opts) {
1987
+ const res = await fetch(`${this.baseUrl}/api/generate`, {
1988
+ method: "POST",
1989
+ headers: { "Content-Type": "application/json" },
1990
+ body: JSON.stringify({
1991
+ model: opts?.model ?? this.defaultModel,
1992
+ prompt: `${prompt}
1993
+
1994
+ Respond with valid JSON only.`,
1995
+ stream: false,
1996
+ format: "json",
1997
+ options: {
1998
+ num_predict: opts?.maxTokens ?? 2e3,
1999
+ temperature: opts?.temperature ?? 0.3
2000
+ }
2001
+ }),
2002
+ signal: AbortSignal.timeout(opts?.timeoutMs ?? DEFAULT_TIMEOUT_MS)
2003
+ });
2004
+ if (!res.ok) {
2005
+ const text = await res.text().catch(() => "unknown error");
2006
+ throw new Error(`Ollama error ${res.status}: ${text.slice(0, 200)}`);
2007
+ }
2008
+ const data = await res.json();
2009
+ return JSON.parse(data.response ?? "{}");
2010
+ }
2011
+ };
2012
+
2013
+ // src/adapters/sqlite.ts
2014
+ var SQLiteStore = class {
2015
+ db;
2016
+ constructor(opts) {
2017
+ try {
2018
+ const Database = require("better-sqlite3");
2019
+ this.db = new Database(opts.path);
2020
+ this.db.pragma("journal_mode = WAL");
2021
+ this.db.pragma("foreign_keys = ON");
2022
+ this.init();
2023
+ } catch (err) {
2024
+ if (err.code === "MODULE_NOT_FOUND") {
2025
+ throw new Error("SQLiteStore requires better-sqlite3. Install: npm install better-sqlite3");
2026
+ }
2027
+ throw err;
2028
+ }
2029
+ }
2030
+ init() {
2031
+ this.db.exec(`
2032
+ CREATE TABLE IF NOT EXISTS become_skills (
2033
+ agent_id TEXT NOT NULL,
2034
+ name TEXT NOT NULL,
2035
+ category TEXT DEFAULT 'general',
2036
+ score INTEGER DEFAULT 0,
2037
+ blooms_level TEXT DEFAULT 'remember',
2038
+ dreyfus_stage TEXT DEFAULT 'novice',
2039
+ evidence TEXT DEFAULT '{}',
2040
+ learned_from TEXT DEFAULT '[]',
2041
+ content TEXT,
2042
+ created_at TEXT NOT NULL,
2043
+ updated_at TEXT NOT NULL,
2044
+ PRIMARY KEY (agent_id, name)
2045
+ );
2046
+
2047
+ CREATE TABLE IF NOT EXISTS become_catalog (
2048
+ skill TEXT PRIMARY KEY,
2049
+ category TEXT DEFAULT 'general',
2050
+ description TEXT,
2051
+ status TEXT DEFAULT 'community'
2052
+ );
2053
+
2054
+ CREATE TABLE IF NOT EXISTS become_score_history (
2055
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
2056
+ agent_id TEXT NOT NULL,
2057
+ skill TEXT NOT NULL,
2058
+ score INTEGER NOT NULL,
2059
+ blooms_level TEXT NOT NULL,
2060
+ dreyfus_stage TEXT NOT NULL,
2061
+ evidence TEXT DEFAULT '{}',
2062
+ computed_at TEXT NOT NULL
2063
+ );
2064
+
2065
+ CREATE TABLE IF NOT EXISTS become_reflections (
2066
+ id TEXT PRIMARY KEY,
2067
+ agent_id TEXT NOT NULL,
2068
+ skill TEXT NOT NULL,
2069
+ artifact_id TEXT,
2070
+ reflection TEXT NOT NULL,
2071
+ created_at TEXT NOT NULL
2072
+ );
2073
+
2074
+ CREATE TABLE IF NOT EXISTS become_milestones (
2075
+ agent_id TEXT NOT NULL,
2076
+ milestone_type TEXT NOT NULL,
2077
+ threshold INTEGER,
2078
+ skill TEXT,
2079
+ evidence_id TEXT,
2080
+ achieved_at TEXT NOT NULL,
2081
+ UNIQUE(agent_id, milestone_type, COALESCE(skill, ''))
2082
+ );
2083
+
2084
+ CREATE TABLE IF NOT EXISTS become_peer_reviews (
2085
+ id TEXT PRIMARY KEY,
2086
+ reviewer_agent_id TEXT NOT NULL,
2087
+ submission_agent_id TEXT NOT NULL,
2088
+ submission_id TEXT NOT NULL,
2089
+ skill TEXT,
2090
+ verdict TEXT NOT NULL,
2091
+ overall_assessment TEXT NOT NULL,
2092
+ strengths TEXT DEFAULT '[]',
2093
+ weaknesses TEXT DEFAULT '[]',
2094
+ suggestions TEXT DEFAULT '[]',
2095
+ created_at TEXT NOT NULL
2096
+ );
2097
+
2098
+ CREATE TABLE IF NOT EXISTS become_learning_edges (
2099
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
2100
+ from_agent TEXT NOT NULL,
2101
+ to_agent TEXT NOT NULL,
2102
+ skill TEXT NOT NULL,
2103
+ event_type TEXT NOT NULL,
2104
+ score_delta INTEGER DEFAULT 0,
2105
+ metadata TEXT DEFAULT '{}',
2106
+ created_at TEXT NOT NULL
2107
+ );
2108
+
2109
+ CREATE TABLE IF NOT EXISTS become_reputation (
2110
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
2111
+ agent_id TEXT NOT NULL,
2112
+ amount INTEGER NOT NULL,
2113
+ type TEXT NOT NULL,
2114
+ description TEXT,
2115
+ created_at TEXT NOT NULL
2116
+ );
2117
+
2118
+ CREATE TABLE IF NOT EXISTS become_conversation_scores (
2119
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
2120
+ agent_id TEXT NOT NULL,
2121
+ session_id TEXT,
2122
+ quality INTEGER NOT NULL,
2123
+ confidence REAL NOT NULL,
2124
+ skill_signals TEXT DEFAULT '[]',
2125
+ failure_patterns TEXT,
2126
+ created_at TEXT NOT NULL
2127
+ );
2128
+
2129
+ CREATE TABLE IF NOT EXISTS become_norms (
2130
+ id TEXT PRIMARY KEY,
2131
+ title TEXT NOT NULL,
2132
+ description TEXT NOT NULL,
2133
+ category TEXT NOT NULL,
2134
+ significance INTEGER DEFAULT 1,
2135
+ evidence TEXT DEFAULT '[]',
2136
+ adopter_count INTEGER DEFAULT 0,
2137
+ first_observed_at TEXT NOT NULL,
2138
+ updated_at TEXT NOT NULL
2139
+ );
2140
+
2141
+ CREATE INDEX IF NOT EXISTS idx_skills_agent ON become_skills(agent_id);
2142
+ CREATE INDEX IF NOT EXISTS idx_history_agent ON become_score_history(agent_id, skill, computed_at DESC);
2143
+ CREATE INDEX IF NOT EXISTS idx_reflections_agent ON become_reflections(agent_id, skill, created_at DESC);
2144
+ CREATE INDEX IF NOT EXISTS idx_milestones_agent ON become_milestones(agent_id);
2145
+ CREATE INDEX IF NOT EXISTS idx_edges_from ON become_learning_edges(from_agent);
2146
+ CREATE INDEX IF NOT EXISTS idx_edges_to ON become_learning_edges(to_agent);
2147
+ CREATE INDEX IF NOT EXISTS idx_reputation_agent ON become_reputation(agent_id);
2148
+ CREATE INDEX IF NOT EXISTS idx_conv_scores ON become_conversation_scores(agent_id, created_at DESC);
2149
+ `);
2150
+ }
2151
+ // ── Skills ──────────────────────────────────────────────────────────────
2152
+ async getSkill(agentId, skill) {
2153
+ const row = this.db.prepare("SELECT * FROM become_skills WHERE agent_id = ? AND name = ?").get(agentId, skill);
2154
+ return row ? this.rowToSkill(row) : null;
2155
+ }
2156
+ async listSkills(agentId, opts) {
2157
+ let sql = "SELECT * FROM become_skills WHERE agent_id = ?";
2158
+ const params = [agentId];
2159
+ if (opts?.stage) {
2160
+ sql += " AND dreyfus_stage = ?";
2161
+ params.push(opts.stage);
2162
+ }
2163
+ if (opts?.limit) {
2164
+ sql += " LIMIT ?";
2165
+ params.push(opts.limit);
2166
+ }
2167
+ return this.db.prepare(sql).all(...params).map((r) => this.rowToSkill(r));
2168
+ }
2169
+ async upsertSkill(skill) {
2170
+ this.db.prepare(`
2171
+ INSERT INTO become_skills (agent_id, name, category, score, blooms_level, dreyfus_stage, evidence, learned_from, content, created_at, updated_at)
2172
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
2173
+ ON CONFLICT(agent_id, name) DO UPDATE SET
2174
+ category = excluded.category, score = excluded.score, blooms_level = excluded.blooms_level,
2175
+ dreyfus_stage = excluded.dreyfus_stage, evidence = excluded.evidence,
2176
+ learned_from = excluded.learned_from, content = excluded.content, updated_at = excluded.updated_at
2177
+ `).run(
2178
+ skill.agent_id,
2179
+ skill.name,
2180
+ skill.category,
2181
+ skill.score,
2182
+ skill.blooms_level,
2183
+ skill.dreyfus_stage,
2184
+ JSON.stringify(skill.evidence),
2185
+ JSON.stringify(skill.learned_from),
2186
+ skill.content,
2187
+ skill.created_at,
2188
+ skill.updated_at
2189
+ );
2190
+ }
2191
+ async deleteSkill(agentId, skill) {
2192
+ this.db.prepare("DELETE FROM become_skills WHERE agent_id = ? AND name = ?").run(agentId, skill);
2193
+ }
2194
+ // ── Catalog ─────────────────────────────────────────────────────────────
2195
+ async getCatalog() {
2196
+ const rows = this.db.prepare("SELECT c.*, COUNT(s.agent_id) as adopter_count FROM become_catalog c LEFT JOIN become_skills s ON c.skill = s.name GROUP BY c.skill").all();
2197
+ return rows.map((r) => ({ skill: r.skill, category: r.category, description: r.description, status: r.status, adopter_count: r.adopter_count }));
2198
+ }
2199
+ async upsertCatalogEntry(entry) {
2200
+ this.db.prepare(`
2201
+ INSERT INTO become_catalog (skill, category, description, status) VALUES (?, ?, ?, ?)
2202
+ ON CONFLICT(skill) DO UPDATE SET category = excluded.category, description = COALESCE(excluded.description, become_catalog.description)
2203
+ `).run(entry.skill, entry.category, entry.description, entry.status);
2204
+ }
2205
+ async getSkillHolders(skill) {
2206
+ return this.db.prepare("SELECT * FROM become_skills WHERE name = ?").all(skill).map((r) => this.rowToSkill(r));
2207
+ }
2208
+ async getSkillAdopterCount(skill) {
2209
+ const row = this.db.prepare("SELECT COUNT(DISTINCT agent_id) as cnt FROM become_skills WHERE name = ?").get(skill);
2210
+ return row?.cnt ?? 0;
2211
+ }
2212
+ async updateCatalogStatus(skill, status) {
2213
+ this.db.prepare("UPDATE become_catalog SET status = ? WHERE skill = ?").run(status, skill);
2214
+ }
2215
+ // ── Score History ───────────────────────────────────────────────────────
2216
+ async saveScore(agentId, score) {
2217
+ this.db.prepare(`INSERT INTO become_score_history (agent_id, skill, score, blooms_level, dreyfus_stage, evidence, computed_at) VALUES (?, ?, ?, ?, ?, ?, ?)`).run(agentId, score.skill, score.score, score.blooms_level, score.dreyfus_stage, JSON.stringify(score.evidence), score.computed_at);
2218
+ }
2219
+ async getScoreHistory(agentId, skill, days = 30) {
2220
+ const cutoff = new Date(Date.now() - days * 24 * 60 * 60 * 1e3).toISOString();
2221
+ return this.db.prepare("SELECT * FROM become_score_history WHERE agent_id = ? AND skill = ? AND computed_at >= ? ORDER BY computed_at ASC").all(agentId, skill, cutoff).map((r) => this.rowToScore(r));
2222
+ }
2223
+ async getLatestScores(agentId) {
2224
+ return this.db.prepare(`
2225
+ SELECT * FROM become_score_history WHERE id IN (
2226
+ SELECT MAX(id) FROM become_score_history WHERE agent_id = ? GROUP BY skill
2227
+ )
2228
+ `).all(agentId).map((r) => this.rowToScore(r));
2229
+ }
2230
+ // ── Reflections ─────────────────────────────────────────────────────────
2231
+ async saveReflection(reflection) {
2232
+ const id = reflection.id ?? generateId2();
2233
+ this.db.prepare("INSERT INTO become_reflections (id, agent_id, skill, artifact_id, reflection, created_at) VALUES (?, ?, ?, ?, ?, ?)").run(id, reflection.agent_id, reflection.skill, reflection.artifact_id, reflection.reflection, reflection.created_at);
2234
+ return { ...reflection, id };
2235
+ }
2236
+ async getReflections(agentId, opts) {
2237
+ let sql = "SELECT * FROM become_reflections WHERE agent_id = ?";
2238
+ const params = [agentId];
2239
+ if (opts?.skill) {
2240
+ sql += " AND skill = ?";
2241
+ params.push(opts.skill);
2242
+ }
2243
+ sql += " ORDER BY created_at DESC";
2244
+ if (opts?.limit) {
2245
+ sql += " LIMIT ?";
2246
+ params.push(opts.limit);
2247
+ }
2248
+ return this.db.prepare(sql).all(...params);
2249
+ }
2250
+ async countReflectionsToday(agentId, skill) {
2251
+ const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
2252
+ const row = this.db.prepare("SELECT COUNT(*) as cnt FROM become_reflections WHERE agent_id = ? AND skill = ? AND created_at LIKE ?").get(agentId, skill, `${today}%`);
2253
+ return row?.cnt ?? 0;
2254
+ }
2255
+ // ── Milestones ──────────────────────────────────────────────────────────
2256
+ async saveMilestone(milestone) {
2257
+ try {
2258
+ this.db.prepare("INSERT INTO become_milestones (agent_id, milestone_type, threshold, skill, evidence_id, achieved_at) VALUES (?, ?, ?, ?, ?, ?)").run(milestone.agent_id, milestone.milestone_type, milestone.threshold, milestone.skill, milestone.evidence_id, milestone.achieved_at);
2259
+ return true;
2260
+ } catch (err) {
2261
+ if (err.code === "SQLITE_CONSTRAINT_UNIQUE" || err.message?.includes("UNIQUE constraint")) {
2262
+ return false;
2263
+ }
2264
+ throw err;
2265
+ }
2266
+ }
2267
+ async getMilestones(agentId) {
2268
+ return this.db.prepare("SELECT * FROM become_milestones WHERE agent_id = ? ORDER BY achieved_at DESC").all(agentId);
2269
+ }
2270
+ async hasMilestone(agentId, milestoneType, skill) {
2271
+ const row = skill !== void 0 ? this.db.prepare("SELECT 1 FROM become_milestones WHERE agent_id = ? AND milestone_type = ? AND skill = ?").get(agentId, milestoneType, skill) : this.db.prepare("SELECT 1 FROM become_milestones WHERE agent_id = ? AND milestone_type = ?").get(agentId, milestoneType);
2272
+ return !!row;
2273
+ }
2274
+ // ── Peer Reviews ────────────────────────────────────────────────────────
2275
+ async savePeerReview(review) {
2276
+ const id = review.id ?? generateId2();
2277
+ const createdAt = review.created_at ?? (/* @__PURE__ */ new Date()).toISOString();
2278
+ this.db.prepare("INSERT INTO become_peer_reviews (id, reviewer_agent_id, submission_agent_id, submission_id, skill, verdict, overall_assessment, strengths, weaknesses, suggestions, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)").run(id, review.reviewer_agent_id, review.submission_agent_id, review.submission_id, review.skill, review.verdict, review.overall_assessment, JSON.stringify(review.strengths), JSON.stringify(review.weaknesses), JSON.stringify(review.suggestions), createdAt);
2279
+ return { ...review, id, created_at: createdAt };
2280
+ }
2281
+ async getReviewsFor(agentId, opts) {
2282
+ let sql = "SELECT * FROM become_peer_reviews WHERE submission_agent_id = ?";
2283
+ const params = [agentId];
2284
+ if (opts?.skill) {
2285
+ sql += " AND skill = ?";
2286
+ params.push(opts.skill);
2287
+ }
2288
+ return this.db.prepare(sql).all(...params).map((r) => this.rowToReview(r));
2289
+ }
2290
+ async getReviewsBy(agentId) {
2291
+ return this.db.prepare("SELECT * FROM become_peer_reviews WHERE reviewer_agent_id = ?").all(agentId).map((r) => this.rowToReview(r));
2292
+ }
2293
+ // ── Learning Edges ──────────────────────────────────────────────────────
2294
+ async saveLearningEdge(edge) {
2295
+ this.db.prepare("INSERT INTO become_learning_edges (from_agent, to_agent, skill, event_type, score_delta, metadata, created_at) VALUES (?, ?, ?, ?, ?, ?, ?)").run(edge.from_agent, edge.to_agent, edge.skill, edge.event_type, edge.score_delta, JSON.stringify(edge.metadata ?? {}), edge.created_at);
2296
+ }
2297
+ async getLearningEdges(agentId, direction) {
2298
+ const col = direction === "from" ? "from_agent" : "to_agent";
2299
+ return this.db.prepare(`SELECT * FROM become_learning_edges WHERE ${col} = ?`).all(agentId).map((r) => ({
2300
+ ...r,
2301
+ metadata: JSON.parse(r.metadata ?? "{}")
2302
+ }));
2303
+ }
2304
+ // ── Reputation ──────────────────────────────────────────────────────────
2305
+ async getReputation(agentId) {
2306
+ const row = this.db.prepare("SELECT SUM(amount) as total FROM become_reputation WHERE agent_id = ?").get(agentId);
2307
+ return row?.total ?? 0;
2308
+ }
2309
+ async grantReputation(agentId, amount, type, description) {
2310
+ this.db.prepare("INSERT INTO become_reputation (agent_id, amount, type, description, created_at) VALUES (?, ?, ?, ?, ?)").run(agentId, amount, type, description, (/* @__PURE__ */ new Date()).toISOString());
2311
+ }
2312
+ // ── Conversation Scores ─────────────────────────────────────────────────
2313
+ async saveConversationScore(agentId, score) {
2314
+ this.db.prepare("INSERT INTO become_conversation_scores (agent_id, session_id, quality, confidence, skill_signals, failure_patterns, created_at) VALUES (?, ?, ?, ?, ?, ?, ?)").run(agentId, score.session_id, score.quality, score.confidence, JSON.stringify(score.skill_signals), score.failure_patterns ? JSON.stringify(score.failure_patterns) : null, (/* @__PURE__ */ new Date()).toISOString());
2315
+ }
2316
+ async getConversationScores(agentId, opts) {
2317
+ let sql = "SELECT * FROM become_conversation_scores WHERE agent_id = ? ORDER BY created_at DESC";
2318
+ const params = [agentId];
2319
+ if (opts?.limit) {
2320
+ sql += " LIMIT ?";
2321
+ params.push(opts.limit);
2322
+ }
2323
+ return this.db.prepare(sql).all(...params).map((r) => ({
2324
+ quality: r.quality,
2325
+ confidence: r.confidence,
2326
+ skill_signals: JSON.parse(r.skill_signals ?? "[]"),
2327
+ failure_patterns: r.failure_patterns ? JSON.parse(r.failure_patterns) : void 0
2328
+ }));
2329
+ }
2330
+ // ── Cultural Norms ──────────────────────────────────────────────────────
2331
+ async saveNorm(norm) {
2332
+ this.db.prepare(`
2333
+ INSERT INTO become_norms (id, title, description, category, significance, evidence, adopter_count, first_observed_at, updated_at)
2334
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
2335
+ ON CONFLICT(id) DO UPDATE SET title=excluded.title, description=excluded.description, category=excluded.category,
2336
+ significance=excluded.significance, evidence=excluded.evidence, adopter_count=excluded.adopter_count, updated_at=excluded.updated_at
2337
+ `).run(norm.id, norm.title, norm.description, norm.category, norm.significance, JSON.stringify(norm.evidence), norm.adopter_count, norm.first_observed_at, norm.updated_at);
2338
+ }
2339
+ async getNorms(opts) {
2340
+ let sql = "SELECT * FROM become_norms";
2341
+ const params = [];
2342
+ if (opts?.category) {
2343
+ sql += " WHERE category = ?";
2344
+ params.push(opts.category);
2345
+ }
2346
+ sql += " ORDER BY first_observed_at DESC";
2347
+ if (opts?.limit) {
2348
+ sql += " LIMIT ?";
2349
+ params.push(opts.limit);
2350
+ }
2351
+ return this.db.prepare(sql).all(...params).map((r) => ({
2352
+ ...r,
2353
+ evidence: JSON.parse(r.evidence ?? "[]")
2354
+ }));
2355
+ }
2356
+ // ── Helpers ─────────────────────────────────────────────────────────────
2357
+ rowToSkill(row) {
2358
+ return {
2359
+ ...row,
2360
+ evidence: JSON.parse(row.evidence ?? "{}"),
2361
+ learned_from: JSON.parse(row.learned_from ?? "[]")
2362
+ };
2363
+ }
2364
+ rowToScore(row) {
2365
+ return {
2366
+ skill: row.skill,
2367
+ score: row.score,
2368
+ blooms_level: row.blooms_level,
2369
+ dreyfus_stage: row.dreyfus_stage,
2370
+ evidence: JSON.parse(row.evidence ?? "{}"),
2371
+ computed_at: row.computed_at
2372
+ };
2373
+ }
2374
+ rowToReview(row) {
2375
+ return {
2376
+ ...row,
2377
+ strengths: JSON.parse(row.strengths ?? "[]"),
2378
+ weaknesses: JSON.parse(row.weaknesses ?? "[]"),
2379
+ suggestions: JSON.parse(row.suggestions ?? "[]")
2380
+ };
2381
+ }
2382
+ /** Close the database connection */
2383
+ close() {
2384
+ this.db?.close();
2385
+ }
2386
+ };
2387
+ function generateId2() {
2388
+ try {
2389
+ return crypto.randomUUID();
2390
+ } catch {
2391
+ const hex = () => Math.random().toString(16).slice(2, 10);
2392
+ return `${hex()}${hex()}-${hex()}-4${hex().slice(1)}-${hex()}-${hex()}${hex()}${hex()}`;
2393
+ }
2394
+ }
2395
+
2396
+ // src/rl/dataset.ts
2397
+ function toTrainingDataset(scoredTurns, format = "alpaca") {
2398
+ const positive = scoredTurns.filter((st) => st.score.quality === 1);
2399
+ if (positive.length === 0) return "";
2400
+ const lines = [];
2401
+ for (const { turn, score } of positive) {
2402
+ let entry;
2403
+ switch (format) {
2404
+ case "alpaca":
2405
+ entry = JSON.stringify({
2406
+ instruction: turn.context.current_task ?? "Respond helpfully to the user.",
2407
+ input: turn.user_message,
2408
+ output: turn.agent_response,
2409
+ quality: score.quality
2410
+ });
2411
+ break;
2412
+ case "sharegpt":
2413
+ entry = JSON.stringify({
2414
+ conversations: [
2415
+ { from: "human", value: turn.user_message },
2416
+ { from: "gpt", value: turn.agent_response }
2417
+ ],
2418
+ quality: score.quality
2419
+ });
2420
+ break;
2421
+ case "openai":
2422
+ entry = JSON.stringify({
2423
+ messages: [
2424
+ { role: "user", content: turn.user_message },
2425
+ { role: "assistant", content: turn.agent_response }
2426
+ ],
2427
+ quality: score.quality
2428
+ });
2429
+ break;
2430
+ }
2431
+ lines.push(entry);
2432
+ }
2433
+ return lines.join("\n") + "\n";
2434
+ }
2435
+ function datasetStats(scoredTurns) {
2436
+ let positive = 0;
2437
+ let negative = 0;
2438
+ let neutral = 0;
2439
+ let totalConfidence = 0;
2440
+ const skills = /* @__PURE__ */ new Set();
2441
+ for (const st of scoredTurns) {
2442
+ if (st.score.quality === 1) positive++;
2443
+ else if (st.score.quality === -1) negative++;
2444
+ else neutral++;
2445
+ totalConfidence += st.score.confidence;
2446
+ for (const s of st.score.skill_signals) {
2447
+ skills.add(s);
2448
+ }
2449
+ }
2450
+ return {
2451
+ total_turns: scoredTurns.length,
2452
+ positive,
2453
+ negative,
2454
+ neutral,
2455
+ training_examples: positive,
2456
+ skills_covered: [...skills],
2457
+ avg_confidence: scoredTurns.length > 0 ? Math.round(totalConfidence / scoredTurns.length * 100) / 100 : 0
2458
+ };
2459
+ }
2460
+ function filterHighQuality(scoredTurns, minConfidence = 0.7) {
2461
+ return scoredTurns.filter(
2462
+ (st) => st.score.quality === 1 && st.score.confidence >= minConfidence
2463
+ );
2464
+ }
2465
+
2466
+ // src/rl/train.ts
2467
+ var import_node_fs2 = require("fs");
2468
+ var import_node_path2 = require("path");
2469
+ var import_node_child_process = require("child_process");
2470
+ var SAFE_PATH_REGEX = /^[a-zA-Z0-9_\-./: ]+$/;
2471
+ var SAFE_MODEL_REGEX = /^[a-zA-Z0-9_\-./]+$/;
2472
+ function validateTrainInput(config) {
2473
+ if (!SAFE_MODEL_REGEX.test(config.baseModel)) {
2474
+ return `Invalid base model name: contains disallowed characters`;
2475
+ }
2476
+ if (!SAFE_PATH_REGEX.test(config.dataset)) {
2477
+ return `Invalid dataset path: contains disallowed characters`;
2478
+ }
2479
+ if (!SAFE_PATH_REGEX.test(config.outputDir)) {
2480
+ return `Invalid output directory: contains disallowed characters`;
2481
+ }
2482
+ if (config.epochs !== void 0 && (config.epochs < 1 || config.epochs > 100)) {
2483
+ return `Epochs must be between 1 and 100`;
2484
+ }
2485
+ if (config.rank !== void 0 && (config.rank < 1 || config.rank > 256)) {
2486
+ return `LoRA rank must be between 1 and 256`;
2487
+ }
2488
+ if (config.lr !== void 0 && (config.lr <= 0 || config.lr > 1)) {
2489
+ return `Learning rate must be between 0 and 1`;
2490
+ }
2491
+ return null;
2492
+ }
2493
+ function escapePython(s) {
2494
+ return s.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n");
2495
+ }
2496
+ function trainLoRA(config) {
2497
+ const {
2498
+ baseModel,
2499
+ dataset,
2500
+ outputDir,
2501
+ backend,
2502
+ epochs = 3,
2503
+ rank = 16,
2504
+ lr = 2e-4
2505
+ } = config;
2506
+ const validationError = validateTrainInput(config);
2507
+ if (validationError) {
2508
+ return { success: false, error: validationError };
2509
+ }
2510
+ if (!(0, import_node_fs2.existsSync)(dataset)) {
2511
+ return { success: false, error: `Dataset not found: ${dataset}` };
2512
+ }
2513
+ const safeOutputDir = (0, import_node_path2.resolve)(outputDir);
2514
+ if (!(0, import_node_fs2.existsSync)(safeOutputDir)) {
2515
+ (0, import_node_fs2.mkdirSync)(safeOutputDir, { recursive: true });
2516
+ }
2517
+ const script = backend === "unsloth" ? generateUnslothScript(escapePython(baseModel), escapePython(dataset), escapePython(safeOutputDir), epochs, rank, lr) : generateAxolotlConfig(baseModel, dataset, safeOutputDir, epochs, rank, lr);
2518
+ const scriptPath = (0, import_node_path2.join)(safeOutputDir, backend === "unsloth" ? "train.py" : "config.yml");
2519
+ (0, import_node_fs2.writeFileSync)(scriptPath, script, "utf-8");
2520
+ try {
2521
+ if (backend === "unsloth") {
2522
+ (0, import_node_child_process.execFileSync)("python3", [scriptPath], {
2523
+ cwd: safeOutputDir,
2524
+ timeout: 36e5,
2525
+ stdio: "pipe"
2526
+ });
2527
+ } else {
2528
+ (0, import_node_child_process.execFileSync)("accelerate", ["launch", "-m", "axolotl.cli.train", scriptPath], {
2529
+ cwd: safeOutputDir,
2530
+ timeout: 36e5,
2531
+ stdio: "pipe"
2532
+ });
2533
+ }
2534
+ const adapterPath = (0, import_node_path2.join)(safeOutputDir, "adapter");
2535
+ return {
2536
+ success: true,
2537
+ adapter_path: adapterPath,
2538
+ epochs_completed: epochs
2539
+ };
2540
+ } catch (err) {
2541
+ return {
2542
+ success: false,
2543
+ error: err.message?.slice(0, 500) ?? "Training failed"
2544
+ };
2545
+ }
2546
+ }
2547
+ function generateUnslothScript(model, dataset, output, epochs, rank, lr) {
2548
+ return `#!/usr/bin/env python3
2549
+ """Auto-generated by @openclaw/become \u2014 LoRA training via Unsloth"""
2550
+
2551
+ from unsloth import FastLanguageModel
2552
+ from datasets import load_dataset
2553
+ from trl import SFTTrainer
2554
+ from transformers import TrainingArguments
2555
+
2556
+ model, tokenizer = FastLanguageModel.from_pretrained(
2557
+ model_name="${model}",
2558
+ max_seq_length=2048,
2559
+ load_in_4bit=True,
2560
+ )
2561
+
2562
+ model = FastLanguageModel.get_peft_model(
2563
+ model,
2564
+ r=${rank},
2565
+ target_modules=["q_proj", "k_proj", "v_proj", "o_proj",
2566
+ "gate_proj", "up_proj", "down_proj"],
2567
+ lora_alpha=${rank * 2},
2568
+ lora_dropout=0,
2569
+ use_gradient_checkpointing="unsloth",
2570
+ )
2571
+
2572
+ dataset = load_dataset("json", data_files="${dataset}", split="train")
2573
+
2574
+ trainer = SFTTrainer(
2575
+ model=model,
2576
+ tokenizer=tokenizer,
2577
+ train_dataset=dataset,
2578
+ dataset_text_field="output",
2579
+ max_seq_length=2048,
2580
+ args=TrainingArguments(
2581
+ per_device_train_batch_size=2,
2582
+ gradient_accumulation_steps=4,
2583
+ warmup_steps=5,
2584
+ num_train_epochs=${epochs},
2585
+ learning_rate=${lr},
2586
+ fp16=True,
2587
+ logging_steps=1,
2588
+ output_dir="${output}/checkpoints",
2589
+ seed=42,
2590
+ ),
2591
+ )
2592
+
2593
+ trainer.train()
2594
+ model.save_pretrained("${output}/adapter")
2595
+ tokenizer.save_pretrained("${output}/adapter")
2596
+ print("Training complete. Adapter saved to ${output}/adapter")
2597
+ `;
2598
+ }
2599
+ function generateAxolotlConfig(model, dataset, output, epochs, rank, lr) {
2600
+ return `# Auto-generated by @openclaw/become \u2014 LoRA training via Axolotl
2601
+ base_model: ${model}
2602
+ model_type: AutoModelForCausalLM
2603
+ tokenizer_type: AutoTokenizer
2604
+
2605
+ load_in_4bit: true
2606
+
2607
+ adapter: lora
2608
+ lora_r: ${rank}
2609
+ lora_alpha: ${rank * 2}
2610
+ lora_dropout: 0.0
2611
+ lora_target_modules:
2612
+ - q_proj
2613
+ - k_proj
2614
+ - v_proj
2615
+ - o_proj
2616
+
2617
+ datasets:
2618
+ - path: ${dataset}
2619
+ type: alpaca
2620
+
2621
+ sequence_len: 2048
2622
+ num_epochs: ${epochs}
2623
+ learning_rate: ${lr}
2624
+ micro_batch_size: 2
2625
+ gradient_accumulation_steps: 4
2626
+
2627
+ output_dir: ${output}/adapter
2628
+ `;
2629
+ }
2630
+
2631
+ // src/rl/scheduler.ts
2632
+ var TrainScheduler = class {
2633
+ timer = null;
2634
+ status = "idle";
2635
+ checking = false;
2636
+ // Re-entry guard
2637
+ config;
2638
+ constructor(config) {
2639
+ this.config = {
2640
+ ...config,
2641
+ minConfidence: config.minConfidence ?? 0.7,
2642
+ intervalMs: config.intervalMs ?? 30 * 60 * 1e3
2643
+ };
2644
+ }
2645
+ start() {
2646
+ if (this.timer) return;
2647
+ this.status = "idle";
2648
+ this.timer = setInterval(() => this.check(), this.config.intervalMs);
2649
+ this.check();
2650
+ }
2651
+ stop() {
2652
+ if (this.timer) {
2653
+ clearInterval(this.timer);
2654
+ this.timer = null;
2655
+ }
2656
+ this.status = "stopped";
2657
+ this.checking = false;
2658
+ }
2659
+ getStatus() {
2660
+ return this.status;
2661
+ }
2662
+ async check() {
2663
+ if (this.checking || this.status === "stopped") return;
2664
+ this.checking = true;
2665
+ this.status = "checking";
2666
+ try {
2667
+ const rawScores = await this.config.adapter.getConversationScores(
2668
+ this.config.agentId,
2669
+ { limit: 500 }
2670
+ );
2671
+ const highConfPositive = rawScores.filter(
2672
+ (s) => s.quality === 1 && s.confidence >= this.config.minConfidence
2673
+ );
2674
+ if (highConfPositive.length >= this.config.minSamples) {
2675
+ this.status = "ready";
2676
+ await this.config.onReady("", {
2677
+ total_turns: rawScores.length,
2678
+ positive: rawScores.filter((s) => s.quality === 1).length,
2679
+ negative: rawScores.filter((s) => s.quality === -1).length,
2680
+ neutral: rawScores.filter((s) => s.quality === 0).length,
2681
+ training_examples: highConfPositive.length,
2682
+ skills_covered: [...new Set(rawScores.flatMap((s) => s.skill_signals))],
2683
+ avg_confidence: rawScores.length > 0 ? Math.round(rawScores.reduce((sum, s) => sum + s.confidence, 0) / rawScores.length * 100) / 100 : 0
2684
+ });
2685
+ }
2686
+ this.status = "idle";
2687
+ } catch {
2688
+ this.status = "idle";
2689
+ } finally {
2690
+ this.checking = false;
2691
+ }
2692
+ }
2693
+ };
2694
+
2695
+ // src/index.ts
2696
+ var Become = class {
2697
+ skills;
2698
+ scorer = scorer_exports;
2699
+ reflector;
2700
+ milestones;
2701
+ constructor(opts) {
2702
+ this.skills = new SkillStore(opts.store);
2703
+ this.reflector = new Reflector(opts.store);
2704
+ this.milestones = new MilestoneDetector(opts.store);
2705
+ }
2706
+ };
2707
+ // Annotate the CommonJS export names for ESM import in node:
2708
+ 0 && (module.exports = {
2709
+ AnthropicAdapter,
2710
+ AwarenessIndex,
2711
+ BLOOMS_ORDER,
2712
+ BLOOMS_SCORE,
2713
+ Become,
2714
+ ConversationLearner,
2715
+ DREYFUS_THRESHOLDS,
2716
+ GrowthTracker,
2717
+ LearningGraph,
2718
+ MemoryStore,
2719
+ MilestoneDetector,
2720
+ NormDetector,
2721
+ OllamaAdapter,
2722
+ OpenAIAdapter,
2723
+ PeerReviewProtocol,
2724
+ Reflector,
2725
+ SQLiteStore,
2726
+ SkillEvolver,
2727
+ SkillPruner,
2728
+ SkillStore,
2729
+ TeachingProtocol,
2730
+ TrainScheduler,
2731
+ TrendTracker,
2732
+ WEIGHTS,
2733
+ checkGate,
2734
+ computeFullScore,
2735
+ computeScore,
2736
+ datasetStats,
2737
+ detectBloomsLevel,
2738
+ detectCollaborationGap,
2739
+ detectCollectiveMemory,
2740
+ detectCreativeMismatch,
2741
+ detectCulturalOutlier,
2742
+ detectIdleCreative,
2743
+ detectProlificCollaborator,
2744
+ detectQuestStreak,
2745
+ detectReactionDisparity,
2746
+ detectSoloCreator,
2747
+ detectSymbolicVocabulary,
2748
+ dreyfusStage,
2749
+ filterHighQuality,
2750
+ getReputationLevel,
2751
+ importSkillDirectory,
2752
+ nextMilestone,
2753
+ normalizeCategory,
2754
+ parseSkillFile,
2755
+ scoreTrend,
2756
+ toTrainingDataset,
2757
+ trainLoRA,
2758
+ validateAgentId
2759
+ });
2760
+ //# sourceMappingURL=index.cjs.map