pi-harness-runtime 1.1.95 → 1.1.97
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/CHANGELOG.md +33 -0
- package/harness/loop-runtime.ts +3 -0
- package/index.ts +130 -29
- package/package.json +2 -1
- package/packages/a2a-adapter/package.json +1 -1
- package/packages/architecture-generator/package.json +1 -1
- package/packages/auth/package.json +1 -1
- package/packages/autonomous-refactor/package.json +1 -1
- package/packages/autonomous-runtime/package.json +1 -1
- package/packages/cache-strategy/package.json +1 -1
- package/packages/capability-registry/package.json +1 -1
- package/packages/checkpoint/package.json +1 -1
- package/packages/cli-plugin-sdk/package.json +1 -1
- package/packages/clipboard/package.json +1 -1
- package/packages/clipboard-plugin/package.json +1 -1
- package/packages/code-generation/package.json +1 -1
- package/packages/code-review/package.json +1 -1
- package/packages/codex-adapter/package.json +1 -1
- package/packages/config-capture/package.json +1 -1
- package/packages/context-compiler/package.json +1 -1
- package/packages/context-discovery/package.json +1 -1
- package/packages/context-manager/package.json +1 -1
- package/packages/cookie-sanitizer/package.json +1 -1
- package/packages/cost-optimizer/package.json +1 -1
- package/packages/dependency-analyzer/package.json +1 -1
- package/packages/django-plugin/package.json +1 -1
- package/packages/doc-generator/package.json +1 -1
- package/packages/evaluation-engine/package.json +1 -1
- package/packages/evaluation-runner/package.json +1 -1
- package/packages/event-bus/package.json +1 -1
- package/packages/event-store/package.json +1 -1
- package/packages/experience-replay/package.json +1 -1
- package/packages/feedback-collector/package.json +1 -1
- package/packages/file-copy-helper/package.json +1 -1
- package/packages/framework-detector/package.json +1 -1
- package/packages/framework-plugin-sdk/package.json +1 -1
- package/packages/frappe-plugin/package.json +1 -1
- package/packages/generic-web-plugin/package.json +1 -1
- package/packages/health-monitor/package.json +1 -1
- package/packages/intent-analyzer/package.json +1 -1
- package/packages/jev-judge/package.json +1 -1
- package/packages/knowledge-graph/package.json +1 -1
- package/packages/knowledge-retrieval/package.json +1 -1
- package/packages/laravel-plugin/package.json +1 -1
- package/packages/learning-engine/package.json +1 -1
- package/packages/mcp-adapter/package.json +1 -1
- package/packages/memory-engine/package.json +1 -1
- package/packages/milestone-manager/package.json +1 -1
- package/packages/model-registry/package.json +1 -1
- package/packages/nextjs-plugin/package.json +1 -1
- package/packages/notification/package.json +1 -1
- package/packages/observability/package.json +1 -1
- package/packages/okf-indexer/package.json +1 -1
- package/packages/performance-optimizer/package.json +1 -1
- package/packages/privilege-broker/package.json +1 -1
- package/packages/project-analyzer/package.json +1 -1
- package/packages/project-bootstrap/package.json +1 -1
- package/packages/projection-engine/package.json +1 -1
- package/packages/prompt-compiler/package.json +1 -1
- package/packages/prompt-versioning/package.json +1 -1
- package/packages/provider-adapter-sdk/package.json +1 -1
- package/packages/provider-router/package.json +1 -1
- package/packages/provider-selector/package.json +1 -1
- package/packages/providers/package.json +1 -1
- package/packages/qdrant-skills/dist/client.d.ts +51 -0
- package/packages/qdrant-skills/dist/client.d.ts.map +1 -0
- package/packages/qdrant-skills/dist/client.js +105 -0
- package/packages/qdrant-skills/dist/client.js.map +1 -0
- package/packages/qdrant-skills/dist/embedder.d.ts +39 -0
- package/packages/qdrant-skills/dist/embedder.d.ts.map +1 -0
- package/packages/qdrant-skills/dist/embedder.js +101 -0
- package/packages/qdrant-skills/dist/embedder.js.map +1 -0
- package/packages/qdrant-skills/dist/index.d.ts +25 -0
- package/packages/qdrant-skills/dist/index.d.ts.map +1 -0
- package/packages/qdrant-skills/dist/index.js +25 -0
- package/packages/qdrant-skills/dist/index.js.map +1 -0
- package/packages/qdrant-skills/package.json +27 -0
- package/packages/qdrant-skills/src/client.ts +178 -0
- package/packages/qdrant-skills/src/embedder.ts +137 -0
- package/packages/qdrant-skills/src/index.ts +42 -0
- package/packages/quota-manager/package.json +1 -1
- package/packages/rate-limiter/package.json +1 -1
- package/packages/react-vite-plugin/package.json +1 -1
- package/packages/release-manager/package.json +1 -1
- package/packages/requirement-compiler/package.json +1 -1
- package/packages/runtime/package.json +1 -1
- package/packages/scheduler/package.json +1 -1
- package/packages/scheduler-adapter/package.json +1 -1
- package/packages/session/package.json +1 -1
- package/packages/session-api/package.json +1 -1
- package/packages/session-export/package.json +1 -1
- package/packages/shared-context/package.json +1 -1
- package/packages/skill-mcp-client/package.json +1 -1
- package/packages/skill-registry/package.json +1 -1
- package/packages/skills/dist/index.d.ts +40 -0
- package/packages/skills/dist/index.d.ts.map +1 -0
- package/packages/skills/dist/index.js +84 -0
- package/packages/skills/dist/index.js.map +1 -0
- package/packages/skills/dist/linter.d.ts +38 -0
- package/packages/skills/dist/linter.d.ts.map +1 -0
- package/packages/skills/dist/linter.js +323 -0
- package/packages/skills/dist/linter.js.map +1 -0
- package/packages/skills/dist/loader.d.ts +74 -0
- package/packages/skills/dist/loader.d.ts.map +1 -0
- package/packages/skills/dist/loader.js +280 -0
- package/packages/skills/dist/loader.js.map +1 -0
- package/packages/skills/dist/parser.d.ts +32 -0
- package/packages/skills/dist/parser.d.ts.map +1 -0
- package/packages/skills/dist/parser.js +459 -0
- package/packages/skills/dist/parser.js.map +1 -0
- package/packages/skills/dist/scanner.d.ts +33 -0
- package/packages/skills/dist/scanner.d.ts.map +1 -0
- package/packages/skills/dist/scanner.js +257 -0
- package/packages/skills/dist/scanner.js.map +1 -0
- package/packages/skills/dist/skill-commands.d.ts +33 -0
- package/packages/skills/dist/skill-commands.d.ts.map +1 -0
- package/packages/skills/dist/skill-commands.js +239 -0
- package/packages/skills/dist/skill-commands.js.map +1 -0
- package/packages/skills/dist/skill-tool.d.ts +120 -0
- package/packages/skills/dist/skill-tool.d.ts.map +1 -0
- package/packages/skills/dist/skill-tool.js +239 -0
- package/packages/skills/dist/skill-tool.js.map +1 -0
- package/packages/skills/dist/skill-versioning.d.ts +123 -0
- package/packages/skills/dist/skill-versioning.d.ts.map +1 -0
- package/packages/skills/dist/skill-versioning.js +240 -0
- package/packages/skills/dist/skill-versioning.js.map +1 -0
- package/packages/skills/dist/types.d.ts +226 -0
- package/packages/skills/dist/types.d.ts.map +1 -0
- package/packages/skills/dist/types.js +21 -0
- package/packages/skills/dist/types.js.map +1 -0
- package/packages/skills/package.json +46 -0
- package/packages/skills/src/index.ts +95 -0
- package/packages/skills/src/linter.ts +371 -0
- package/packages/skills/src/loader.ts +335 -0
- package/packages/skills/src/parser.ts +548 -0
- package/packages/skills/src/scanner.ts +322 -0
- package/packages/skills/src/skill-commands.ts +318 -0
- package/packages/skills/src/skill-tool.ts +324 -0
- package/packages/skills/src/skill-versioning.ts +345 -0
- package/packages/skills/src/types.ts +259 -0
- package/packages/skills/test/init-skills.test.ts +118 -0
- package/packages/sprint-planner/package.json +1 -1
- package/packages/subscription-engine/package.json +1 -1
- package/packages/task-compiler/package.json +1 -1
- package/packages/tencentdb-memory/package.json +1 -1
- package/packages/tencentdb-sync/package.json +1 -1
- package/packages/test-data-generator/package.json +1 -1
- package/packages/test-generator/package.json +1 -1
- package/packages/todo-bd-sync/package.json +1 -1
- package/packages/token-estimation/package.json +1 -1
- package/packages/token-optimizer/package.json +1 -1
- package/packages/trajectory/dist/src/curator.d.ts +189 -0
- package/packages/trajectory/dist/src/curator.d.ts.map +1 -0
- package/packages/trajectory/dist/src/curator.js +340 -0
- package/packages/trajectory/dist/src/curator.js.map +1 -0
- package/packages/trajectory/dist/src/journey-renderer.d.ts +53 -0
- package/packages/trajectory/dist/src/journey-renderer.d.ts.map +1 -0
- package/packages/trajectory/dist/src/journey-renderer.js +324 -0
- package/packages/trajectory/dist/src/journey-renderer.js.map +1 -0
- package/packages/trajectory/dist/src/learning-graph.d.ts +159 -0
- package/packages/trajectory/dist/src/learning-graph.d.ts.map +1 -0
- package/packages/trajectory/dist/src/learning-graph.js +258 -0
- package/packages/trajectory/dist/src/learning-graph.js.map +1 -0
- package/packages/trajectory/dist/src/memory-store.d.ts +146 -0
- package/packages/trajectory/dist/src/memory-store.d.ts.map +1 -0
- package/packages/trajectory/dist/src/memory-store.js +472 -0
- package/packages/trajectory/dist/src/memory-store.js.map +1 -0
- package/packages/trajectory/dist/src/review-fork.d.ts +164 -0
- package/packages/trajectory/dist/src/review-fork.d.ts.map +1 -0
- package/packages/trajectory/dist/src/review-fork.js +332 -0
- package/packages/trajectory/dist/src/review-fork.js.map +1 -0
- package/packages/trajectory/dist/src/write-approval.d.ts +136 -0
- package/packages/trajectory/dist/src/write-approval.d.ts.map +1 -0
- package/packages/trajectory/dist/src/write-approval.js +281 -0
- package/packages/trajectory/dist/src/write-approval.js.map +1 -0
- package/packages/trajectory/package.json +1 -1
- package/packages/trajectory/src/curator.ts +485 -0
- package/packages/trajectory/src/journey-renderer.ts +399 -0
- package/packages/trajectory/src/learning-graph.ts +363 -0
- package/packages/trajectory/src/memory-store.ts +578 -0
- package/packages/trajectory/src/review-fork.ts +450 -0
- package/packages/trajectory/src/write-approval.ts +372 -0
- package/packages/tui/package.json +1 -1
- package/packages/types/package.json +1 -1
- package/packages/workflow-events/package.json +1 -1
- package/packages/workspace-scanner/package.json +1 -1
- package/packages/worktree/package.json +1 -1
- package/packages/write-review/dist/blackboard.d.ts +98 -0
- package/packages/write-review/dist/blackboard.d.ts.map +1 -0
- package/packages/write-review/dist/blackboard.js +289 -0
- package/packages/write-review/dist/blackboard.js.map +1 -0
- package/packages/write-review/dist/event-bus.d.ts +106 -0
- package/packages/write-review/dist/event-bus.d.ts.map +1 -0
- package/packages/write-review/dist/event-bus.js +294 -0
- package/packages/write-review/dist/event-bus.js.map +1 -0
- package/packages/write-review/dist/gate.d.ts +37 -0
- package/packages/write-review/dist/gate.d.ts.map +1 -0
- package/packages/write-review/dist/gate.js +138 -0
- package/packages/write-review/dist/gate.js.map +1 -0
- package/packages/write-review/dist/index.d.ts +22 -0
- package/packages/write-review/dist/index.d.ts.map +1 -0
- package/packages/write-review/dist/index.js +60 -0
- package/packages/write-review/dist/index.js.map +1 -0
- package/packages/write-review/dist/injection.d.ts +40 -0
- package/packages/write-review/dist/injection.d.ts.map +1 -0
- package/packages/write-review/dist/injection.js +179 -0
- package/packages/write-review/dist/injection.js.map +1 -0
- package/packages/write-review/dist/review.d.ts +54 -0
- package/packages/write-review/dist/review.d.ts.map +1 -0
- package/packages/write-review/dist/review.js +154 -0
- package/packages/write-review/dist/review.js.map +1 -0
- package/packages/write-review/dist/trigger.d.ts +39 -0
- package/packages/write-review/dist/trigger.d.ts.map +1 -0
- package/packages/write-review/dist/trigger.js +107 -0
- package/packages/write-review/dist/trigger.js.map +1 -0
- package/packages/write-review/dist/types.d.ts +78 -0
- package/packages/write-review/dist/types.d.ts.map +1 -0
- package/packages/write-review/dist/types.js +26 -0
- package/packages/write-review/dist/types.js.map +1 -0
- package/packages/write-review/package.json +1 -1
- package/packages/write-review/src/event-bus.ts +351 -0
- package/packages/write-review/src/index.ts +1 -0
- package/packages/write-review/src/injection.ts +25 -3
- package/proactive-compact.ts +51 -7
- package/scripts/sync-skills.ts +275 -0
- package/scripts/sync-to-qdrant.ts +398 -0
|
@@ -0,0 +1,485 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Curator - Consolidation and Quality Enforcement
|
|
3
|
+
*
|
|
4
|
+
* Periodic pass that:
|
|
5
|
+
* - Merges fragmented memory into skills
|
|
6
|
+
* - Extracts common patterns into skills
|
|
7
|
+
* - Prunes outdated items
|
|
8
|
+
* - Enforces quality standards
|
|
9
|
+
*
|
|
10
|
+
* Based on Hermes Agent's curator system.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import type { Trajectory } from "./types.js";
|
|
14
|
+
import type { MemoryEntry } from "./memory-store.js";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Local Skill type to avoid circular dependency
|
|
18
|
+
*/
|
|
19
|
+
interface LocalSkill {
|
|
20
|
+
id: string;
|
|
21
|
+
frontmatter: { name: string; description: string };
|
|
22
|
+
hermes?: {
|
|
23
|
+
author?: string;
|
|
24
|
+
created_at?: string;
|
|
25
|
+
usage_count?: number;
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Local lint result type
|
|
31
|
+
*/
|
|
32
|
+
interface LocalLintResult {
|
|
33
|
+
rule: string;
|
|
34
|
+
severity: "error" | "warning" | "info";
|
|
35
|
+
message: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Curator configuration
|
|
40
|
+
*/
|
|
41
|
+
export interface CuratorConfig {
|
|
42
|
+
/** Enable curator */
|
|
43
|
+
enabled: boolean;
|
|
44
|
+
/** Days between consolidation passes */
|
|
45
|
+
consolidationIntervalDays: number;
|
|
46
|
+
/** Max memory entries before consolidation */
|
|
47
|
+
memoryThreshold: number;
|
|
48
|
+
/** Min similar entries to trigger merge */
|
|
49
|
+
similarityThreshold: number;
|
|
50
|
+
/** Archive skills older than this */
|
|
51
|
+
archiveAgeDays: number;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Default curator configuration
|
|
56
|
+
*/
|
|
57
|
+
export const DEFAULT_CURATOR_CONFIG: CuratorConfig = {
|
|
58
|
+
enabled: true,
|
|
59
|
+
consolidationIntervalDays: 7,
|
|
60
|
+
memoryThreshold: 50,
|
|
61
|
+
similarityThreshold: 3,
|
|
62
|
+
archiveAgeDays: 90,
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Curator action types
|
|
67
|
+
*/
|
|
68
|
+
export type CuratorAction =
|
|
69
|
+
| { type: "merge_memory"; entries: MemoryEntry[]; into: string }
|
|
70
|
+
| { type: "extract_skill"; pattern: string; from: string[] }
|
|
71
|
+
| { type: "archive_memory"; id: string }
|
|
72
|
+
| { type: "archive_skill"; id: string }
|
|
73
|
+
| { type: "patch_skill"; id: string; patch: Partial<LocalSkill> }
|
|
74
|
+
| { type: "prune_low_confidence"; items: { id: string; type: "memory" | "skill"; confidence: number }[] };
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Curator event types
|
|
78
|
+
*/
|
|
79
|
+
export type CuratorEvent =
|
|
80
|
+
| { type: "curator.started" }
|
|
81
|
+
| { type: "curator.completed"; actions: CuratorAction[] }
|
|
82
|
+
| { type: "curator.action"; action: CuratorAction }
|
|
83
|
+
| { type: "curator.error"; error: string };
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Curator event handler
|
|
87
|
+
*/
|
|
88
|
+
export type CuratorEventHandler = (event: CuratorEvent) => void;
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Similarity result
|
|
92
|
+
*/
|
|
93
|
+
export interface SimilarityResult {
|
|
94
|
+
entries: MemoryEntry[];
|
|
95
|
+
similarity: number; // 0-1
|
|
96
|
+
commonTheme: string;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Curator class
|
|
101
|
+
*/
|
|
102
|
+
export class Curator {
|
|
103
|
+
private config: CuratorConfig;
|
|
104
|
+
private handlers: Set<CuratorEventHandler> = new Set();
|
|
105
|
+
private lastConsolidation: number = Date.now();
|
|
106
|
+
|
|
107
|
+
constructor(config?: Partial<CuratorConfig>) {
|
|
108
|
+
this.config = { ...DEFAULT_CURATOR_CONFIG, ...config };
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Register event handler
|
|
113
|
+
*/
|
|
114
|
+
on(handler: CuratorEventHandler): () => void {
|
|
115
|
+
this.handlers.add(handler);
|
|
116
|
+
return () => this.handlers.delete(handler);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Emit event
|
|
121
|
+
*/
|
|
122
|
+
private emit(event: CuratorEvent): void {
|
|
123
|
+
for (const handler of this.handlers) {
|
|
124
|
+
try {
|
|
125
|
+
handler(event);
|
|
126
|
+
} catch (err) {
|
|
127
|
+
console.error("[Curator] Handler error:", err);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Check if consolidation should run
|
|
134
|
+
*/
|
|
135
|
+
shouldConsolidate(): boolean {
|
|
136
|
+
if (!this.config.enabled) return false;
|
|
137
|
+
|
|
138
|
+
const daysSinceConsolidation =
|
|
139
|
+
(Date.now() - this.lastConsolidation) / (1000 * 60 * 60 * 24);
|
|
140
|
+
|
|
141
|
+
return daysSinceConsolidation >= this.config.consolidationIntervalDays;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Run consolidation pass
|
|
146
|
+
*/
|
|
147
|
+
async consolidate(
|
|
148
|
+
memory: MemoryEntry[],
|
|
149
|
+
skills: LocalSkill[],
|
|
150
|
+
trajectories: Trajectory[]
|
|
151
|
+
): Promise<CuratorAction[]> {
|
|
152
|
+
this.emit({ type: "curator.started" });
|
|
153
|
+
const actions: CuratorAction[] = [];
|
|
154
|
+
|
|
155
|
+
try {
|
|
156
|
+
// 1. Find similar memory entries
|
|
157
|
+
const similarGroups = this.findSimilarMemory(memory);
|
|
158
|
+
for (const group of similarGroups) {
|
|
159
|
+
if (group.entries.length >= this.config.similarityThreshold) {
|
|
160
|
+
const action = this.suggestMerge(group);
|
|
161
|
+
if (action) {
|
|
162
|
+
actions.push(action);
|
|
163
|
+
this.emit({ type: "curator.action", action });
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// 2. Extract patterns from trajectories
|
|
169
|
+
const patterns = this.extractPatterns(trajectories);
|
|
170
|
+
for (const pattern of patterns) {
|
|
171
|
+
const action = this.suggestSkillExtraction(pattern);
|
|
172
|
+
if (action) {
|
|
173
|
+
actions.push(action);
|
|
174
|
+
this.emit({ type: "curator.action", action });
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// 3. Check for outdated items
|
|
179
|
+
const outdated = this.findOutdatedItems(memory, skills);
|
|
180
|
+
for (const item of outdated) {
|
|
181
|
+
const action = this.suggestArchive(item);
|
|
182
|
+
if (action) {
|
|
183
|
+
actions.push(action);
|
|
184
|
+
this.emit({ type: "curator.action", action });
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// 4. Lint skills for quality
|
|
189
|
+
for (const skill of skills) {
|
|
190
|
+
const results: LocalLintResult[] = []; // Linting would require skills package
|
|
191
|
+
const errors: LocalLintResult[] = results.filter((r) => r.severity === "error");
|
|
192
|
+
if (errors.length > 0) {
|
|
193
|
+
// Suggest patch to fix errors
|
|
194
|
+
const action = this.suggestSkillPatch(skill, errors);
|
|
195
|
+
if (action) {
|
|
196
|
+
actions.push(action);
|
|
197
|
+
this.emit({ type: "curator.action", action });
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Update last consolidation time
|
|
203
|
+
this.lastConsolidation = Date.now();
|
|
204
|
+
|
|
205
|
+
this.emit({ type: "curator.completed", actions });
|
|
206
|
+
} catch (err) {
|
|
207
|
+
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
208
|
+
this.emit({ type: "curator.error", error: errorMsg });
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return actions;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Find similar memory entries
|
|
216
|
+
*/
|
|
217
|
+
findSimilarMemory(memory: MemoryEntry[]): SimilarityResult[] {
|
|
218
|
+
const groups: Map<string, MemoryEntry[]> = new Map();
|
|
219
|
+
|
|
220
|
+
for (const entry of memory) {
|
|
221
|
+
// Extract key terms from content
|
|
222
|
+
const terms = this.extractTerms(entry.content);
|
|
223
|
+
|
|
224
|
+
for (const term of terms) {
|
|
225
|
+
const key = term.toLowerCase();
|
|
226
|
+
if (!groups.has(key)) {
|
|
227
|
+
groups.set(key, []);
|
|
228
|
+
}
|
|
229
|
+
groups.get(key)!.push(entry);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Filter to groups with multiple entries
|
|
234
|
+
const results: SimilarityResult[] = [];
|
|
235
|
+
for (const [theme, entries] of groups) {
|
|
236
|
+
// Deduplicate entries
|
|
237
|
+
const unique = Array.from(new Set(entries.map((e) => e.id)))
|
|
238
|
+
.map((id) => entries.find((e) => e.id === id)!);
|
|
239
|
+
|
|
240
|
+
if (unique.length >= 2) {
|
|
241
|
+
results.push({
|
|
242
|
+
entries: unique,
|
|
243
|
+
similarity: Math.min(1, unique.length / 10),
|
|
244
|
+
commonTheme: theme,
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Sort by similarity descending
|
|
250
|
+
results.sort((a, b) => b.similarity - a.similarity);
|
|
251
|
+
|
|
252
|
+
return results;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Extract key terms from content
|
|
257
|
+
*/
|
|
258
|
+
private extractTerms(content: string): string[] {
|
|
259
|
+
// Simple term extraction
|
|
260
|
+
const words = content
|
|
261
|
+
.toLowerCase()
|
|
262
|
+
.replace(/[^\w\s]/g, " ")
|
|
263
|
+
.split(/\s+/)
|
|
264
|
+
.filter((w) => w.length > 4);
|
|
265
|
+
|
|
266
|
+
// Get most common words
|
|
267
|
+
const freq: Record<string, number> = {};
|
|
268
|
+
for (const word of words) {
|
|
269
|
+
freq[word] = (freq[word] || 0) + 1;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// Return top terms
|
|
273
|
+
return Object.entries(freq)
|
|
274
|
+
.sort((a, b) => b[1] - a[1])
|
|
275
|
+
.slice(0, 5)
|
|
276
|
+
.map(([word]) => word);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Suggest memory merge
|
|
281
|
+
*/
|
|
282
|
+
private suggestMerge(group: SimilarityResult): CuratorAction | null {
|
|
283
|
+
// Find the most recent/relevant entry to keep
|
|
284
|
+
const keeper = group.entries.reduce((a, b) =>
|
|
285
|
+
a.updated_at > b.updated_at ? a : b
|
|
286
|
+
);
|
|
287
|
+
|
|
288
|
+
return {
|
|
289
|
+
type: "merge_memory",
|
|
290
|
+
entries: group.entries.filter((e) => e.id !== keeper.id),
|
|
291
|
+
into: keeper.id,
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Extract patterns from trajectories
|
|
297
|
+
*/
|
|
298
|
+
extractPatterns(trajectories: Trajectory[]): string[] {
|
|
299
|
+
const patterns: string[] = [];
|
|
300
|
+
|
|
301
|
+
// Group by verdict
|
|
302
|
+
const byVerdict: Record<string, Trajectory[]> = {};
|
|
303
|
+
for (const t of trajectories) {
|
|
304
|
+
if (!byVerdict[t.verdict]) {
|
|
305
|
+
byVerdict[t.verdict] = [];
|
|
306
|
+
}
|
|
307
|
+
byVerdict[t.verdict].push(t);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// Find converged patterns (approved with low iterations)
|
|
311
|
+
const fastConverged = trajectories.filter(
|
|
312
|
+
(t) => t.verdict === "approved" && t.iterations <= 2
|
|
313
|
+
);
|
|
314
|
+
|
|
315
|
+
for (const t of fastConverged) {
|
|
316
|
+
const taskType = this.categorizeTask(t.taskRequest);
|
|
317
|
+
patterns.push(`fast-${taskType}`);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// Find stuck patterns (high iterations)
|
|
321
|
+
const stuck = trajectories.filter(
|
|
322
|
+
(t) => t.iterations >= 5 && t.verdict !== "approved"
|
|
323
|
+
);
|
|
324
|
+
|
|
325
|
+
for (const t of stuck) {
|
|
326
|
+
const taskType = this.categorizeTask(t.taskRequest);
|
|
327
|
+
patterns.push(`stuck-${taskType}`);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
return [...new Set(patterns)];
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Categorize task from request
|
|
335
|
+
*/
|
|
336
|
+
private categorizeTask(request: string): string {
|
|
337
|
+
const lower = request.toLowerCase();
|
|
338
|
+
|
|
339
|
+
if (lower.includes("test")) return "testing";
|
|
340
|
+
if (lower.includes("fix") || lower.includes("bug")) return "bugfix";
|
|
341
|
+
if (lower.includes("refactor")) return "refactor";
|
|
342
|
+
if (lower.includes("api")) return "api";
|
|
343
|
+
if (lower.includes("config") || lower.includes("setup")) return "config";
|
|
344
|
+
|
|
345
|
+
return "general";
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* Suggest skill extraction
|
|
350
|
+
*/
|
|
351
|
+
private suggestSkillExtraction(pattern: string): CuratorAction | null {
|
|
352
|
+
return {
|
|
353
|
+
type: "extract_skill",
|
|
354
|
+
pattern,
|
|
355
|
+
from: [], // Would be populated from trajectories
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* Find outdated items
|
|
361
|
+
*/
|
|
362
|
+
findOutdatedItems(
|
|
363
|
+
memory: MemoryEntry[],
|
|
364
|
+
skills: LocalSkill[]
|
|
365
|
+
): Array<{ id: string; type: "memory" | "skill"; ageDays: number }> {
|
|
366
|
+
const outdated: Array<{ id: string; type: "memory" | "skill"; ageDays: number }> = [];
|
|
367
|
+
const now = Date.now();
|
|
368
|
+
|
|
369
|
+
for (const entry of memory) {
|
|
370
|
+
const ageMs = now - new Date(entry.created_at).getTime();
|
|
371
|
+
const ageDays = ageMs / (1000 * 60 * 60 * 24);
|
|
372
|
+
|
|
373
|
+
if (ageDays > this.config.archiveAgeDays) {
|
|
374
|
+
outdated.push({ id: entry.id, type: "memory", ageDays });
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
for (const skill of skills) {
|
|
379
|
+
const created = skill.hermes?.created_at
|
|
380
|
+
? new Date(skill.hermes.created_at).getTime()
|
|
381
|
+
: now;
|
|
382
|
+
const ageMs = now - created;
|
|
383
|
+
const ageDays = ageMs / (1000 * 60 * 60 * 24);
|
|
384
|
+
|
|
385
|
+
// Only archive agent-created skills, not user skills
|
|
386
|
+
if (ageDays > this.config.archiveAgeDays && skill.hermes?.author === "agent") {
|
|
387
|
+
const usageCount = skill.hermes?.usage_count ?? 0;
|
|
388
|
+
if (usageCount === 0) {
|
|
389
|
+
outdated.push({ id: skill.id, type: "skill", ageDays });
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
return outdated;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
/**
|
|
398
|
+
* Suggest archive action
|
|
399
|
+
*/
|
|
400
|
+
private suggestArchive(
|
|
401
|
+
item: { id: string; type: "memory" | "skill"; ageDays: number }
|
|
402
|
+
): CuratorAction {
|
|
403
|
+
return {
|
|
404
|
+
type: item.type === "memory" ? "archive_memory" : "archive_skill",
|
|
405
|
+
id: item.id,
|
|
406
|
+
} as CuratorAction;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* Suggest skill patch
|
|
411
|
+
*/
|
|
412
|
+
private suggestSkillPatch(
|
|
413
|
+
skill: LocalSkill,
|
|
414
|
+
errors: Array<{ rule: string; message: string }>
|
|
415
|
+
): CuratorAction {
|
|
416
|
+
// Generate patch suggestion based on errors
|
|
417
|
+
const patches: Partial<LocalSkill> = {};
|
|
418
|
+
|
|
419
|
+
for (const error of errors) {
|
|
420
|
+
switch (error.rule) {
|
|
421
|
+
case "missing-description":
|
|
422
|
+
// Would need to generate a description
|
|
423
|
+
break;
|
|
424
|
+
case "oversized-body":
|
|
425
|
+
// Would need to suggest splitting
|
|
426
|
+
break;
|
|
427
|
+
case "incident-log-shape":
|
|
428
|
+
// Would need to clean up content
|
|
429
|
+
break;
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
return {
|
|
434
|
+
type: "patch_skill",
|
|
435
|
+
id: skill.id,
|
|
436
|
+
patch: patches,
|
|
437
|
+
};
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
/**
|
|
441
|
+
* Get quality summary
|
|
442
|
+
*/
|
|
443
|
+
getQualitySummary(skills: LocalSkill[]): {
|
|
444
|
+
total: number;
|
|
445
|
+
errors: number;
|
|
446
|
+
warnings: number;
|
|
447
|
+
byRule: Record<string, number>;
|
|
448
|
+
} {
|
|
449
|
+
// Note: Full linting requires skills package - returning placeholder
|
|
450
|
+
return {
|
|
451
|
+
total: skills.length,
|
|
452
|
+
errors: 0,
|
|
453
|
+
warnings: 0,
|
|
454
|
+
byRule: {},
|
|
455
|
+
};
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
/**
|
|
459
|
+
* Update configuration
|
|
460
|
+
*/
|
|
461
|
+
updateConfig(config: Partial<CuratorConfig>): void {
|
|
462
|
+
this.config = { ...this.config, ...config };
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
/**
|
|
466
|
+
* Get configuration
|
|
467
|
+
*/
|
|
468
|
+
getConfig(): CuratorConfig {
|
|
469
|
+
return { ...this.config };
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
// Global instance
|
|
474
|
+
let globalCurator: Curator | null = null;
|
|
475
|
+
|
|
476
|
+
export function getGlobalCurator(): Curator {
|
|
477
|
+
if (!globalCurator) {
|
|
478
|
+
globalCurator = new Curator();
|
|
479
|
+
}
|
|
480
|
+
return globalCurator;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
export function createCurator(config?: Partial<CuratorConfig>): Curator {
|
|
484
|
+
return new Curator(config);
|
|
485
|
+
}
|