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.
Files changed (55) hide show
  1. package/README.md +8 -7
  2. package/dist/api-6OX4ICXN.js +9 -0
  3. package/dist/auto-improve-skills-2COKTU5C.js +8 -0
  4. package/dist/autoresearch-Y7WW6L4O.js +24 -0
  5. package/dist/chunk-2YHUDOJL.js +54 -0
  6. package/dist/chunk-3TXNZINH.js +775 -0
  7. package/dist/chunk-5MHNQAV4.js +317 -0
  8. package/dist/chunk-7AIZTHHZ.js +152 -0
  9. package/dist/chunk-D4IM3YRX.js +166 -0
  10. package/dist/chunk-DJJHS7KK.js +62 -0
  11. package/dist/chunk-DKZBQRR3.js +91 -0
  12. package/dist/chunk-FLSHJZLC.js +613 -0
  13. package/dist/chunk-H2ZKCXMJ.js +202 -0
  14. package/dist/chunk-ILMOSMD3.js +83 -0
  15. package/dist/chunk-KYTHKH6V.js +79 -0
  16. package/dist/chunk-LTKHLRM4.js +272 -0
  17. package/dist/chunk-T32AEP3O.js +146 -0
  18. package/dist/chunk-T73Z5UMA.js +14437 -0
  19. package/dist/chunk-TFKT7V7H.js +1545 -0
  20. package/dist/chunk-US4CYDNS.js +524 -0
  21. package/dist/chunk-ZLRN7Q7C.js +27 -0
  22. package/dist/claude-code-6DF4YARB.js +8 -0
  23. package/dist/config-CS7734SA.js +24 -0
  24. package/dist/correction-classifier-TLPKRNLI.js +93 -0
  25. package/dist/cursor-Z4XXDCAM.js +8 -0
  26. package/dist/daemon/autoresearch-2MAEM2YI.js +272 -0
  27. package/dist/daemon/chunk-545XA5CB.js +77 -0
  28. package/dist/daemon/chunk-HEYFAUHL.js +90 -0
  29. package/dist/daemon/chunk-MLKGABMK.js +9 -0
  30. package/dist/daemon/chunk-NJICGNCK.js +150 -0
  31. package/dist/daemon/chunk-OD5NUFH2.js +181 -0
  32. package/dist/daemon/chunk-SM2FSXIP.js +60 -0
  33. package/dist/daemon/chunk-UMDJFPN6.js +163 -0
  34. package/dist/daemon/config-F7HE3JRY.js +23 -0
  35. package/dist/daemon/db-QEXVVTAL.js +15 -0
  36. package/dist/daemon/eval-generator-OR2FAYLB.js +316 -0
  37. package/dist/daemon/improver-TGEK6MPE.js +186 -0
  38. package/dist/daemon/llm-FUJ2TBYT.js +11 -0
  39. package/dist/daemon/nudge-detector-NFRHWZY6.js +140 -0
  40. package/dist/daemon/platform-7N3LQDIB.js +16381 -0
  41. package/dist/daemon/registry-FI4GTO3H.js +20 -0
  42. package/dist/daemon/server.js +356 -0
  43. package/dist/daemon/trace-store-T7XFGQSX.js +19 -0
  44. package/dist/daemon-UXYMG46V.js +85 -0
  45. package/dist/db-TLNRIXLK.js +18 -0
  46. package/dist/eval-generator-GGMRPO3K.js +21 -0
  47. package/dist/eval-runner-EF4K6T5Y.js +15 -0
  48. package/dist/index.js +8033 -568
  49. package/dist/llm-3UUZX6PX.js +12 -0
  50. package/dist/platform-52NREMBS.js +33 -0
  51. package/dist/repo-installer-K6ADOW3E.js +25 -0
  52. package/dist/setup-P744STZE.js +16 -0
  53. package/dist/test-loop-Y7QQE55P.js +127 -0
  54. package/dist/trace-store-FVLMNNDK.js +20 -0
  55. 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,9 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __export = (target, all) => {
3
+ for (var name in all)
4
+ __defProp(target, name, { get: all[name], enumerable: true });
5
+ };
6
+
7
+ export {
8
+ __export
9
+ };
@@ -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
+ };