caik-cli 0.1.1 → 0.6.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/README.md +8 -7
- package/dist/api-6OX4ICXN.js +9 -0
- package/dist/auto-improve-skills-2COKTU5C.js +8 -0
- package/dist/autoresearch-Y7WW6L4O.js +24 -0
- package/dist/chunk-2YHUDOJL.js +54 -0
- package/dist/chunk-3TXNZINH.js +775 -0
- package/dist/chunk-5MHNQAV4.js +317 -0
- package/dist/chunk-7AIZTHHZ.js +152 -0
- package/dist/chunk-D4IM3YRX.js +166 -0
- package/dist/chunk-DJJHS7KK.js +62 -0
- package/dist/chunk-DKZBQRR3.js +91 -0
- package/dist/chunk-FLSHJZLC.js +613 -0
- package/dist/chunk-H2ZKCXMJ.js +202 -0
- package/dist/chunk-ILMOSMD3.js +83 -0
- package/dist/chunk-KYTHKH6V.js +79 -0
- package/dist/chunk-LTKHLRM4.js +272 -0
- package/dist/chunk-T32AEP3O.js +146 -0
- package/dist/chunk-T73Z5UMA.js +14437 -0
- package/dist/chunk-TFKT7V7H.js +1545 -0
- package/dist/chunk-US4CYDNS.js +524 -0
- package/dist/chunk-ZLRN7Q7C.js +27 -0
- package/dist/claude-code-6DF4YARB.js +8 -0
- package/dist/config-CS7734SA.js +24 -0
- package/dist/correction-classifier-TLPKRNLI.js +93 -0
- package/dist/cursor-Z4XXDCAM.js +8 -0
- package/dist/daemon/autoresearch-2MAEM2YI.js +272 -0
- package/dist/daemon/chunk-545XA5CB.js +77 -0
- package/dist/daemon/chunk-HEYFAUHL.js +90 -0
- package/dist/daemon/chunk-MLKGABMK.js +9 -0
- package/dist/daemon/chunk-NJICGNCK.js +150 -0
- package/dist/daemon/chunk-OD5NUFH2.js +181 -0
- package/dist/daemon/chunk-SM2FSXIP.js +60 -0
- package/dist/daemon/chunk-UMDJFPN6.js +163 -0
- package/dist/daemon/config-F7HE3JRY.js +23 -0
- package/dist/daemon/db-QEXVVTAL.js +15 -0
- package/dist/daemon/eval-generator-OR2FAYLB.js +316 -0
- package/dist/daemon/improver-TGEK6MPE.js +186 -0
- package/dist/daemon/llm-FUJ2TBYT.js +11 -0
- package/dist/daemon/nudge-detector-NFRHWZY6.js +140 -0
- package/dist/daemon/platform-7N3LQDIB.js +16381 -0
- package/dist/daemon/registry-FI4GTO3H.js +20 -0
- package/dist/daemon/server.js +356 -0
- package/dist/daemon/trace-store-T7XFGQSX.js +19 -0
- package/dist/daemon-UXYMG46V.js +85 -0
- package/dist/db-TLNRIXLK.js +18 -0
- package/dist/eval-generator-GGMRPO3K.js +21 -0
- package/dist/eval-runner-EF4K6T5Y.js +15 -0
- package/dist/index.js +8033 -568
- package/dist/llm-3UUZX6PX.js +12 -0
- package/dist/platform-52NREMBS.js +33 -0
- package/dist/repo-installer-K6ADOW3E.js +25 -0
- package/dist/setup-P744STZE.js +16 -0
- package/dist/test-loop-Y7QQE55P.js +127 -0
- package/dist/trace-store-FVLMNNDK.js +20 -0
- package/package.json +9 -3
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
import {
|
|
2
|
+
runEvalSuite
|
|
3
|
+
} from "./chunk-UMDJFPN6.js";
|
|
4
|
+
import {
|
|
5
|
+
callAnthropic
|
|
6
|
+
} from "./chunk-SM2FSXIP.js";
|
|
7
|
+
import {
|
|
8
|
+
findRegistryEntry,
|
|
9
|
+
upsertRegistryEntry
|
|
10
|
+
} from "./chunk-HEYFAUHL.js";
|
|
11
|
+
import "./chunk-545XA5CB.js";
|
|
12
|
+
import "./chunk-MLKGABMK.js";
|
|
13
|
+
|
|
14
|
+
// src/daemon/autoresearch.ts
|
|
15
|
+
import { readFileSync, writeFileSync, mkdirSync, existsSync } from "fs";
|
|
16
|
+
import { join } from "path";
|
|
17
|
+
import { homedir } from "os";
|
|
18
|
+
import { randomUUID } from "crypto";
|
|
19
|
+
var STRATEGY_DESCRIPTIONS = {
|
|
20
|
+
targeted_fix: "Fix the specific failing eval cases while preserving passing behavior.",
|
|
21
|
+
simplify: "Simplify the skill instructions while maintaining the same behavior.",
|
|
22
|
+
restructure: "Restructure the skill for clarity and completeness.",
|
|
23
|
+
scope_narrow: "Narrow the scope \u2014 be more specific about what the skill does and doesn't do.",
|
|
24
|
+
format_refine: "Add explicit format specifications for output structure."
|
|
25
|
+
};
|
|
26
|
+
function selectStrategies(suite, failedCaseIds) {
|
|
27
|
+
const strategies = ["targeted_fix"];
|
|
28
|
+
if (failedCaseIds.length === 0) return strategies;
|
|
29
|
+
const failedCases = suite.cases.filter((c) => failedCaseIds.includes(c.id));
|
|
30
|
+
const failedCategories = failedCases.map((c) => c.category.toLowerCase());
|
|
31
|
+
const failRate = failedCaseIds.length / Math.max(suite.cases.length, 1);
|
|
32
|
+
if (failedCategories.some((c) => c.includes("scope"))) {
|
|
33
|
+
strategies.push("scope_narrow");
|
|
34
|
+
}
|
|
35
|
+
if (failedCategories.some((c) => c.includes("format"))) {
|
|
36
|
+
strategies.push("format_refine");
|
|
37
|
+
}
|
|
38
|
+
if (failRate <= 0.3) {
|
|
39
|
+
strategies.push("simplify");
|
|
40
|
+
}
|
|
41
|
+
const uniqueCategories = new Set(failedCategories);
|
|
42
|
+
if (failRate > 0.5 && uniqueCategories.size >= 2) {
|
|
43
|
+
strategies.push("restructure");
|
|
44
|
+
}
|
|
45
|
+
return [...new Set(strategies)];
|
|
46
|
+
}
|
|
47
|
+
function shouldAutoApply(mode, result) {
|
|
48
|
+
if (mode === "manual") return false;
|
|
49
|
+
if (!result.bestContent) return false;
|
|
50
|
+
if (mode === "assisted") {
|
|
51
|
+
return result.bestScore.passRate === 1 && result.bestScore.lengthRatio >= 0.7 && result.bestScore.lengthRatio <= 1.5;
|
|
52
|
+
}
|
|
53
|
+
if (mode === "autonomous") {
|
|
54
|
+
return result.bestScore.passRate > result.baselineScore.passRate;
|
|
55
|
+
}
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
var MUTATION_SYSTEM = `You are improving a Claude Code artifact. You will be given the current content, the specific eval cases it fails, and context from the correction traces that generated those cases. Make targeted changes to pass the failing cases while preserving behavior for passing cases. Return ONLY the improved content, no explanation or commentary.`;
|
|
59
|
+
async function generateMutations(currentContent, suite, failedCaseIds, strategies, apiKey, model) {
|
|
60
|
+
const failedCases = suite.cases.filter((c) => failedCaseIds.includes(c.id));
|
|
61
|
+
const mutations = [];
|
|
62
|
+
let llmCalls = 0;
|
|
63
|
+
const failedCaseSummary = failedCases.map((c) => `- [${c.category}] ${c.description} (${c.assertion.type})`).join("\n");
|
|
64
|
+
const passingCases = suite.cases.filter((c) => !failedCaseIds.includes(c.id));
|
|
65
|
+
const passingCaseSummary = passingCases.length > 0 ? passingCases.map((c) => `- [${c.category}] ${c.description}`).join("\n") : "(none)";
|
|
66
|
+
for (const strategy of strategies) {
|
|
67
|
+
const userMessage = `Current artifact content:
|
|
68
|
+
<skill>
|
|
69
|
+
${currentContent}
|
|
70
|
+
</skill>
|
|
71
|
+
|
|
72
|
+
Failing eval cases (${failedCaseIds.length}/${suite.cases.length}) \u2014 FIX THESE:
|
|
73
|
+
${failedCaseSummary}
|
|
74
|
+
|
|
75
|
+
Passing eval cases (${passingCases.length}/${suite.cases.length}) \u2014 PRESERVE THESE:
|
|
76
|
+
${passingCaseSummary}
|
|
77
|
+
|
|
78
|
+
Strategy: ${strategy} \u2014 ${STRATEGY_DESCRIPTIONS[strategy] ?? "Improve the skill."}
|
|
79
|
+
|
|
80
|
+
Rewrite the skill to pass the failing cases while keeping the passing cases passing.`;
|
|
81
|
+
try {
|
|
82
|
+
const result = await callAnthropic(apiKey, {
|
|
83
|
+
model,
|
|
84
|
+
system: MUTATION_SYSTEM,
|
|
85
|
+
userMessage,
|
|
86
|
+
maxTokens: 4096
|
|
87
|
+
});
|
|
88
|
+
llmCalls++;
|
|
89
|
+
let content = result.text.trim();
|
|
90
|
+
content = content.replace(/^<skill>\s*/i, "").replace(/\s*<\/skill>\s*$/i, "");
|
|
91
|
+
const ratio = content.length / currentContent.length;
|
|
92
|
+
if (ratio >= 0.3 && ratio <= 3 && content.length > 10) {
|
|
93
|
+
mutations.push({ content, strategy });
|
|
94
|
+
}
|
|
95
|
+
} catch {
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return { mutations, llmCalls };
|
|
99
|
+
}
|
|
100
|
+
async function autoResearchLoop(slug, skillPath, suite, config, apiKey) {
|
|
101
|
+
const startTime = Date.now();
|
|
102
|
+
let totalLLMCalls = 0;
|
|
103
|
+
const usedStrategies = [];
|
|
104
|
+
const currentContent = readFileSync(skillPath, "utf-8");
|
|
105
|
+
const baselineScore = await runEvalSuite(currentContent, suite, currentContent, apiKey, config.mutationModel);
|
|
106
|
+
let best = currentContent;
|
|
107
|
+
let bestScore = baselineScore;
|
|
108
|
+
for (let iteration = 0; iteration < config.maxIterations; iteration++) {
|
|
109
|
+
if (totalLLMCalls >= config.maxLLMCalls) break;
|
|
110
|
+
const strategies = selectStrategies(suite, bestScore.failedCaseIds).slice(
|
|
111
|
+
0,
|
|
112
|
+
config.mutationsPerIteration
|
|
113
|
+
);
|
|
114
|
+
const { mutations, llmCalls } = await generateMutations(
|
|
115
|
+
best,
|
|
116
|
+
suite,
|
|
117
|
+
bestScore.failedCaseIds,
|
|
118
|
+
strategies,
|
|
119
|
+
apiKey,
|
|
120
|
+
config.mutationModel
|
|
121
|
+
);
|
|
122
|
+
totalLLMCalls += llmCalls;
|
|
123
|
+
let improved = false;
|
|
124
|
+
for (const mutation of mutations) {
|
|
125
|
+
if (totalLLMCalls >= config.maxLLMCalls) break;
|
|
126
|
+
const score = await runEvalSuite(mutation.content, suite, currentContent, apiKey, config.mutationModel);
|
|
127
|
+
if (score.passRate > bestScore.passRate) {
|
|
128
|
+
best = mutation.content;
|
|
129
|
+
bestScore = score;
|
|
130
|
+
improved = true;
|
|
131
|
+
if (!usedStrategies.includes(mutation.strategy)) {
|
|
132
|
+
usedStrategies.push(mutation.strategy);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
if (bestScore.passRate === 1) break;
|
|
137
|
+
if (!improved) break;
|
|
138
|
+
}
|
|
139
|
+
const duration = Date.now() - startTime;
|
|
140
|
+
return {
|
|
141
|
+
bestContent: best !== currentContent ? best : null,
|
|
142
|
+
baselineScore,
|
|
143
|
+
bestScore,
|
|
144
|
+
iterations: Math.min(config.maxIterations, totalLLMCalls),
|
|
145
|
+
totalLLMCalls,
|
|
146
|
+
strategies: usedStrategies,
|
|
147
|
+
duration
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
function applyImprovement(slug, skillPath, improvedContent, result, db) {
|
|
151
|
+
const entry = findRegistryEntry(slug);
|
|
152
|
+
if (!entry) return;
|
|
153
|
+
const versionsDir = join(homedir(), ".caik", "versions", slug);
|
|
154
|
+
mkdirSync(versionsDir, { recursive: true });
|
|
155
|
+
const versionFile = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-") + ".md";
|
|
156
|
+
writeFileSync(join(versionsDir, versionFile), readFileSync(skillPath, "utf-8"), "utf-8");
|
|
157
|
+
writeFileSync(skillPath, improvedContent, "utf-8");
|
|
158
|
+
const obsPath = join(homedir(), ".caik", "observations", `${slug}.jsonl`);
|
|
159
|
+
if (existsSync(obsPath)) {
|
|
160
|
+
writeFileSync(obsPath, "", "utf-8");
|
|
161
|
+
}
|
|
162
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
163
|
+
const updatedEntry = {
|
|
164
|
+
...entry,
|
|
165
|
+
updatedAt: now,
|
|
166
|
+
lastImprovedAt: now,
|
|
167
|
+
localVersion: (entry.localVersion ?? 0) + 1,
|
|
168
|
+
pendingImprovement: false,
|
|
169
|
+
lastAutoAppliedAt: now,
|
|
170
|
+
preApplyCorrectionRate: result.baselineScore.passRate,
|
|
171
|
+
improvementLog: [
|
|
172
|
+
...entry.improvementLog ?? [],
|
|
173
|
+
{ ts: now, type: `autoresearch:${result.strategies.join("+")}` }
|
|
174
|
+
],
|
|
175
|
+
lastLoopResult: {
|
|
176
|
+
score: result.bestScore.passRate,
|
|
177
|
+
iterations: result.iterations,
|
|
178
|
+
timestamp: now
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
upsertRegistryEntry(updatedEntry);
|
|
182
|
+
db.prepare(
|
|
183
|
+
`INSERT INTO loop_results (id, slug, baseline_pass_rate, best_pass_rate, baseline_pass_count, best_pass_count,
|
|
184
|
+
total_cases, iterations, total_llm_calls, duration_ms, strategies, best_content, applied, created_at)
|
|
185
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, ?)`
|
|
186
|
+
).run(
|
|
187
|
+
randomUUID(),
|
|
188
|
+
slug,
|
|
189
|
+
result.baselineScore.passRate,
|
|
190
|
+
result.bestScore.passRate,
|
|
191
|
+
result.baselineScore.passCount,
|
|
192
|
+
result.bestScore.passCount,
|
|
193
|
+
result.bestScore.totalCases,
|
|
194
|
+
result.iterations,
|
|
195
|
+
result.totalLLMCalls,
|
|
196
|
+
result.duration,
|
|
197
|
+
JSON.stringify(result.strategies),
|
|
198
|
+
improvedContent,
|
|
199
|
+
now
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
function proposeImprovement(slug, skillPath, improvedContent, result, db) {
|
|
203
|
+
const entry = findRegistryEntry(slug);
|
|
204
|
+
if (!entry) return;
|
|
205
|
+
const currentContent = readFileSync(skillPath, "utf-8");
|
|
206
|
+
const versionsDir = join(homedir(), ".caik", "versions", slug);
|
|
207
|
+
mkdirSync(versionsDir, { recursive: true });
|
|
208
|
+
const versionFile = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-") + ".md";
|
|
209
|
+
writeFileSync(join(versionsDir, versionFile), currentContent, "utf-8");
|
|
210
|
+
const proposedPath = skillPath.endsWith("SKILL.md") ? skillPath.replace(/SKILL\.md$/, "SKILL.proposed.md") : `${skillPath}.proposed`;
|
|
211
|
+
writeFileSync(proposedPath, improvedContent, "utf-8");
|
|
212
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
213
|
+
const updatedEntry = {
|
|
214
|
+
...entry,
|
|
215
|
+
updatedAt: now,
|
|
216
|
+
pendingImprovement: true,
|
|
217
|
+
improvementLog: [
|
|
218
|
+
...entry.improvementLog ?? [],
|
|
219
|
+
{ ts: now, type: `autoresearch:${result.strategies.join("+")}` }
|
|
220
|
+
],
|
|
221
|
+
lastLoopResult: {
|
|
222
|
+
score: result.bestScore.passRate,
|
|
223
|
+
iterations: result.iterations,
|
|
224
|
+
timestamp: now
|
|
225
|
+
}
|
|
226
|
+
};
|
|
227
|
+
upsertRegistryEntry(updatedEntry);
|
|
228
|
+
db.prepare(
|
|
229
|
+
`INSERT INTO loop_results (id, slug, baseline_pass_rate, best_pass_rate, baseline_pass_count, best_pass_count,
|
|
230
|
+
total_cases, iterations, total_llm_calls, duration_ms, strategies, best_content, applied, created_at)
|
|
231
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0, ?)`
|
|
232
|
+
).run(
|
|
233
|
+
randomUUID(),
|
|
234
|
+
slug,
|
|
235
|
+
result.baselineScore.passRate,
|
|
236
|
+
result.bestScore.passRate,
|
|
237
|
+
result.baselineScore.passCount,
|
|
238
|
+
result.bestScore.passCount,
|
|
239
|
+
result.bestScore.totalCases,
|
|
240
|
+
result.iterations,
|
|
241
|
+
result.totalLLMCalls,
|
|
242
|
+
result.duration,
|
|
243
|
+
JSON.stringify(result.strategies),
|
|
244
|
+
improvedContent,
|
|
245
|
+
now
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
function checkWatchdog(slug, entry, observations) {
|
|
249
|
+
if (!entry.lastAutoAppliedAt) return { shouldRollback: false };
|
|
250
|
+
const postApply = observations.filter(
|
|
251
|
+
(o) => new Date(o.timestamp) > new Date(entry.lastAutoAppliedAt)
|
|
252
|
+
);
|
|
253
|
+
if (postApply.length < 10) return { shouldRollback: false };
|
|
254
|
+
const postCorrectionRate = postApply.filter((o) => o.correctionType).length / postApply.length;
|
|
255
|
+
const preCorrectionRate = entry.preApplyCorrectionRate ?? 0;
|
|
256
|
+
if (postCorrectionRate - preCorrectionRate > 0.2) {
|
|
257
|
+
return {
|
|
258
|
+
shouldRollback: true,
|
|
259
|
+
reason: `Correction rate increased from ${(preCorrectionRate * 100).toFixed(0)}% to ${(postCorrectionRate * 100).toFixed(0)}%`
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
return { shouldRollback: false };
|
|
263
|
+
}
|
|
264
|
+
export {
|
|
265
|
+
applyImprovement,
|
|
266
|
+
autoResearchLoop,
|
|
267
|
+
checkWatchdog,
|
|
268
|
+
generateMutations,
|
|
269
|
+
proposeImprovement,
|
|
270
|
+
selectStrategies,
|
|
271
|
+
shouldAutoApply
|
|
272
|
+
};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
// src/config.ts
|
|
2
|
+
import { readFileSync, writeFileSync, mkdirSync, chmodSync, existsSync } from "fs";
|
|
3
|
+
import { join } from "path";
|
|
4
|
+
import { homedir } from "os";
|
|
5
|
+
var CONTRIBUTION_LEVELS = [
|
|
6
|
+
{ value: "none", name: "None", description: "Nothing sent. Directory access only." },
|
|
7
|
+
{ value: "minimal", name: "Minimal", description: "Install/uninstall + engagement + retention. Basic recommendations." },
|
|
8
|
+
{ value: "contributor", name: "Contributor", description: "Error/success signals + co-installs. Full recommendations. (default)" },
|
|
9
|
+
{ value: "collective", name: "Collective", description: "Stack context + session shape + workflow patterns. Proactive recommendations + leaderboard." }
|
|
10
|
+
];
|
|
11
|
+
var DEFAULT_CONFIG = {
|
|
12
|
+
apiUrl: "https://www.caik.dev",
|
|
13
|
+
defaultLimit: 10,
|
|
14
|
+
version: 1
|
|
15
|
+
};
|
|
16
|
+
function getConfigDir() {
|
|
17
|
+
return join(homedir(), ".caik");
|
|
18
|
+
}
|
|
19
|
+
function getConfigPath() {
|
|
20
|
+
return join(getConfigDir(), "config.json");
|
|
21
|
+
}
|
|
22
|
+
function readConfig() {
|
|
23
|
+
const path = getConfigPath();
|
|
24
|
+
if (!existsSync(path)) {
|
|
25
|
+
return { ...DEFAULT_CONFIG };
|
|
26
|
+
}
|
|
27
|
+
try {
|
|
28
|
+
const raw = readFileSync(path, "utf-8");
|
|
29
|
+
const parsed = JSON.parse(raw);
|
|
30
|
+
return { ...DEFAULT_CONFIG, ...parsed };
|
|
31
|
+
} catch {
|
|
32
|
+
return { ...DEFAULT_CONFIG };
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
function writeConfig(config) {
|
|
36
|
+
const dir = getConfigDir();
|
|
37
|
+
if (!existsSync(dir)) {
|
|
38
|
+
mkdirSync(dir, { recursive: true, mode: 448 });
|
|
39
|
+
}
|
|
40
|
+
const path = getConfigPath();
|
|
41
|
+
writeFileSync(path, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
42
|
+
chmodSync(path, 384);
|
|
43
|
+
}
|
|
44
|
+
function getOrCreateInstallationId() {
|
|
45
|
+
const config = readConfig();
|
|
46
|
+
if (config.installationId) return config.installationId;
|
|
47
|
+
const id = `inst_${globalThis.crypto.randomUUID().replace(/-/g, "").slice(0, 16)}`;
|
|
48
|
+
writeConfig({ ...config, installationId: id });
|
|
49
|
+
return id;
|
|
50
|
+
}
|
|
51
|
+
function getApiKey() {
|
|
52
|
+
return process.env.CAIK_API_KEY ?? readConfig().apiKey;
|
|
53
|
+
}
|
|
54
|
+
function setApiKey(key) {
|
|
55
|
+
const config = readConfig();
|
|
56
|
+
config.apiKey = key;
|
|
57
|
+
writeConfig(config);
|
|
58
|
+
}
|
|
59
|
+
function resolveConfig(opts) {
|
|
60
|
+
const config = readConfig();
|
|
61
|
+
return {
|
|
62
|
+
apiUrl: opts.apiUrl ?? process.env.CAIK_API_URL ?? config.apiUrl ?? DEFAULT_CONFIG.apiUrl,
|
|
63
|
+
apiKey: opts.apiKey ?? process.env.CAIK_API_KEY ?? config.apiKey
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export {
|
|
68
|
+
CONTRIBUTION_LEVELS,
|
|
69
|
+
getConfigDir,
|
|
70
|
+
getConfigPath,
|
|
71
|
+
readConfig,
|
|
72
|
+
writeConfig,
|
|
73
|
+
getOrCreateInstallationId,
|
|
74
|
+
getApiKey,
|
|
75
|
+
setApiKey,
|
|
76
|
+
resolveConfig
|
|
77
|
+
};
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getConfigDir
|
|
3
|
+
} from "./chunk-545XA5CB.js";
|
|
4
|
+
|
|
5
|
+
// src/platform/registry.ts
|
|
6
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync, unlinkSync } from "fs";
|
|
7
|
+
import { join } from "path";
|
|
8
|
+
var REGISTRY_VERSION = 1;
|
|
9
|
+
function getRegistryPath() {
|
|
10
|
+
return join(getConfigDir(), "registry.json");
|
|
11
|
+
}
|
|
12
|
+
function emptyRegistry() {
|
|
13
|
+
return { version: REGISTRY_VERSION, entries: [] };
|
|
14
|
+
}
|
|
15
|
+
function readRegistry() {
|
|
16
|
+
const path = getRegistryPath();
|
|
17
|
+
if (!existsSync(path)) return emptyRegistry();
|
|
18
|
+
try {
|
|
19
|
+
const raw = readFileSync(path, "utf-8");
|
|
20
|
+
const parsed = JSON.parse(raw);
|
|
21
|
+
if (!parsed.entries || !Array.isArray(parsed.entries)) return emptyRegistry();
|
|
22
|
+
return parsed;
|
|
23
|
+
} catch {
|
|
24
|
+
return emptyRegistry();
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function writeRegistry(registry) {
|
|
28
|
+
const dir = getConfigDir();
|
|
29
|
+
if (!existsSync(dir)) {
|
|
30
|
+
mkdirSync(dir, { recursive: true, mode: 448 });
|
|
31
|
+
}
|
|
32
|
+
const path = getRegistryPath();
|
|
33
|
+
writeFileSync(path, JSON.stringify(registry, null, 2) + "\n", "utf-8");
|
|
34
|
+
}
|
|
35
|
+
function upsertRegistryEntry(entry) {
|
|
36
|
+
const registry = readRegistry();
|
|
37
|
+
const idx = registry.entries.findIndex(
|
|
38
|
+
(e) => e.slug === entry.slug && e.platform === entry.platform
|
|
39
|
+
);
|
|
40
|
+
if (idx >= 0) {
|
|
41
|
+
registry.entries[idx] = entry;
|
|
42
|
+
} else {
|
|
43
|
+
registry.entries.push(entry);
|
|
44
|
+
}
|
|
45
|
+
writeRegistry(registry);
|
|
46
|
+
}
|
|
47
|
+
function removeRegistryEntry(slug, platform) {
|
|
48
|
+
const registry = readRegistry();
|
|
49
|
+
const idx = registry.entries.findIndex(
|
|
50
|
+
(e) => e.slug === slug && e.platform === platform
|
|
51
|
+
);
|
|
52
|
+
if (idx < 0) return null;
|
|
53
|
+
const [removed] = registry.entries.splice(idx, 1);
|
|
54
|
+
writeRegistry(registry);
|
|
55
|
+
return removed;
|
|
56
|
+
}
|
|
57
|
+
function findRegistryEntry(slug, platform) {
|
|
58
|
+
const registry = readRegistry();
|
|
59
|
+
return registry.entries.find(
|
|
60
|
+
(e) => e.slug === slug && (!platform || e.platform === platform)
|
|
61
|
+
) ?? null;
|
|
62
|
+
}
|
|
63
|
+
function listRegistryEntries(platform) {
|
|
64
|
+
const registry = readRegistry();
|
|
65
|
+
if (!platform) return registry.entries;
|
|
66
|
+
return registry.entries.filter((e) => e.platform === platform);
|
|
67
|
+
}
|
|
68
|
+
function cleanupFiles(entry) {
|
|
69
|
+
const failed = [];
|
|
70
|
+
for (const file of entry.files) {
|
|
71
|
+
try {
|
|
72
|
+
if (existsSync(file)) {
|
|
73
|
+
unlinkSync(file);
|
|
74
|
+
}
|
|
75
|
+
} catch {
|
|
76
|
+
failed.push(file);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return failed;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export {
|
|
83
|
+
readRegistry,
|
|
84
|
+
writeRegistry,
|
|
85
|
+
upsertRegistryEntry,
|
|
86
|
+
removeRegistryEntry,
|
|
87
|
+
findRegistryEntry,
|
|
88
|
+
listRegistryEntries,
|
|
89
|
+
cleanupFiles
|
|
90
|
+
};
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
// src/daemon/trace-store.ts
|
|
2
|
+
import { randomUUID } from "crypto";
|
|
3
|
+
function bufferToolCall(db, sessionId, toolName, toolInput, toolResponse, success, slug) {
|
|
4
|
+
db.prepare(
|
|
5
|
+
`INSERT INTO session_buffer (session_id, type, timestamp, tool_name, tool_input, tool_response, success, slug)
|
|
6
|
+
VALUES (?, 'tool_call', ?, ?, ?, ?, ?, ?)`
|
|
7
|
+
).run(
|
|
8
|
+
sessionId,
|
|
9
|
+
(/* @__PURE__ */ new Date()).toISOString(),
|
|
10
|
+
toolName,
|
|
11
|
+
JSON.stringify(toolInput),
|
|
12
|
+
JSON.stringify(toolResponse),
|
|
13
|
+
success ? 1 : 0,
|
|
14
|
+
slug
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
function bufferPrompt(db, sessionId, prompt, correctionType, slug) {
|
|
18
|
+
const type = correctionType ? "correction" : "prompt";
|
|
19
|
+
db.prepare(
|
|
20
|
+
`INSERT INTO session_buffer (session_id, type, timestamp, prompt, correction_type, slug)
|
|
21
|
+
VALUES (?, ?, ?, ?, ?, ?)`
|
|
22
|
+
).run(sessionId, type, (/* @__PURE__ */ new Date()).toISOString(), prompt, correctionType ?? null, slug ?? null);
|
|
23
|
+
}
|
|
24
|
+
function buildTraces(db, sessionId) {
|
|
25
|
+
const rows = db.prepare("SELECT * FROM session_buffer WHERE session_id = ? ORDER BY id ASC").all(sessionId);
|
|
26
|
+
const traces = [];
|
|
27
|
+
const lastToolCall = /* @__PURE__ */ new Map();
|
|
28
|
+
for (const row of rows) {
|
|
29
|
+
if (row.type === "tool_call" && row.slug) {
|
|
30
|
+
lastToolCall.set(row.slug, row);
|
|
31
|
+
}
|
|
32
|
+
if (row.type === "correction" && row.correction_type) {
|
|
33
|
+
const slug = row.slug ?? "unknown";
|
|
34
|
+
const toolCall = lastToolCall.get(slug);
|
|
35
|
+
traces.push({
|
|
36
|
+
id: randomUUID(),
|
|
37
|
+
sessionId,
|
|
38
|
+
slug,
|
|
39
|
+
timestamp: row.timestamp,
|
|
40
|
+
kind: "correction",
|
|
41
|
+
toolName: toolCall?.tool_name ?? "unknown",
|
|
42
|
+
toolInput: toolCall?.tool_input ? JSON.parse(toolCall.tool_input) : {},
|
|
43
|
+
toolResponse: toolCall?.tool_response ? JSON.parse(toolCall.tool_response) : {},
|
|
44
|
+
correctionType: row.correction_type,
|
|
45
|
+
correctionPrompt: row.prompt ?? void 0
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
const successfulCalls = rows.filter(
|
|
50
|
+
(r) => r.type === "tool_call" && r.success === 1 && r.slug
|
|
51
|
+
);
|
|
52
|
+
for (const row of successfulCalls) {
|
|
53
|
+
if (Math.random() < 0.2) {
|
|
54
|
+
traces.push({
|
|
55
|
+
id: randomUUID(),
|
|
56
|
+
sessionId,
|
|
57
|
+
slug: row.slug,
|
|
58
|
+
timestamp: row.timestamp,
|
|
59
|
+
kind: "success",
|
|
60
|
+
toolName: row.tool_name ?? "unknown",
|
|
61
|
+
toolInput: row.tool_input ? JSON.parse(row.tool_input) : {},
|
|
62
|
+
toolResponse: row.tool_response ? JSON.parse(row.tool_response) : {}
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return traces;
|
|
67
|
+
}
|
|
68
|
+
function persistTraces(db, traces) {
|
|
69
|
+
const stmt = db.prepare(
|
|
70
|
+
`INSERT INTO traces (id, session_id, slug, timestamp, kind, tool_name, tool_input, tool_response,
|
|
71
|
+
correction_type, correction_prompt, skill_content_hash, created_at)
|
|
72
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
|
|
73
|
+
);
|
|
74
|
+
const insertMany = db.transaction((items) => {
|
|
75
|
+
for (const t of items) {
|
|
76
|
+
stmt.run(
|
|
77
|
+
t.id,
|
|
78
|
+
t.sessionId,
|
|
79
|
+
t.slug,
|
|
80
|
+
t.timestamp,
|
|
81
|
+
t.kind,
|
|
82
|
+
t.toolName,
|
|
83
|
+
JSON.stringify(t.toolInput),
|
|
84
|
+
JSON.stringify(t.toolResponse),
|
|
85
|
+
t.correctionType ?? null,
|
|
86
|
+
t.correctionPrompt ?? null,
|
|
87
|
+
t.skillContentHash ?? null,
|
|
88
|
+
(/* @__PURE__ */ new Date()).toISOString()
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
insertMany(traces);
|
|
93
|
+
}
|
|
94
|
+
function rowToTrace(row) {
|
|
95
|
+
return {
|
|
96
|
+
id: row.id,
|
|
97
|
+
sessionId: row.session_id,
|
|
98
|
+
slug: row.slug,
|
|
99
|
+
timestamp: row.timestamp,
|
|
100
|
+
kind: row.kind,
|
|
101
|
+
toolName: row.tool_name,
|
|
102
|
+
toolInput: row.tool_input ? JSON.parse(row.tool_input) : {},
|
|
103
|
+
toolResponse: row.tool_response ? JSON.parse(row.tool_response) : {},
|
|
104
|
+
correctionType: row.correction_type ?? void 0,
|
|
105
|
+
correctionPrompt: row.correction_prompt ?? void 0,
|
|
106
|
+
skillContentHash: row.skill_content_hash ?? void 0
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
function getTraces(db, slug, opts) {
|
|
110
|
+
let sql = "SELECT * FROM traces WHERE slug = ?";
|
|
111
|
+
const params = [slug];
|
|
112
|
+
if (opts?.kind) {
|
|
113
|
+
sql += " AND kind = ?";
|
|
114
|
+
params.push(opts.kind);
|
|
115
|
+
}
|
|
116
|
+
sql += " ORDER BY timestamp DESC";
|
|
117
|
+
if (opts?.limit) {
|
|
118
|
+
sql += " LIMIT ?";
|
|
119
|
+
params.push(opts.limit);
|
|
120
|
+
}
|
|
121
|
+
const rows = db.prepare(sql).all(...params);
|
|
122
|
+
return rows.map(rowToTrace);
|
|
123
|
+
}
|
|
124
|
+
function getTraceCount(db, slug) {
|
|
125
|
+
const row = db.prepare(
|
|
126
|
+
`SELECT
|
|
127
|
+
COUNT(*) as total,
|
|
128
|
+
SUM(CASE WHEN kind = 'correction' THEN 1 ELSE 0 END) as corrections,
|
|
129
|
+
SUM(CASE WHEN kind = 'success' THEN 1 ELSE 0 END) as successes
|
|
130
|
+
FROM traces WHERE slug = ?`
|
|
131
|
+
).get(slug);
|
|
132
|
+
return {
|
|
133
|
+
total: row.total,
|
|
134
|
+
corrections: row.corrections ?? 0,
|
|
135
|
+
successes: row.successes ?? 0
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
function clearSessionBuffer(db, sessionId) {
|
|
139
|
+
db.prepare("DELETE FROM session_buffer WHERE session_id = ?").run(sessionId);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export {
|
|
143
|
+
bufferToolCall,
|
|
144
|
+
bufferPrompt,
|
|
145
|
+
buildTraces,
|
|
146
|
+
persistTraces,
|
|
147
|
+
getTraces,
|
|
148
|
+
getTraceCount,
|
|
149
|
+
clearSessionBuffer
|
|
150
|
+
};
|