selftune 0.2.0 → 0.2.1
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/.claude/agents/diagnosis-analyst.md +20 -10
- package/.claude/agents/evolution-reviewer.md +14 -1
- package/.claude/agents/integration-guide.md +18 -6
- package/.claude/agents/pattern-analyst.md +18 -5
- package/CHANGELOG.md +12 -4
- package/README.md +43 -35
- package/apps/local-dashboard/dist/assets/geist-cyrillic-wght-normal-CHSlOQsW.woff2 +0 -0
- package/apps/local-dashboard/dist/assets/geist-latin-ext-wght-normal-DMtmJ5ZE.woff2 +0 -0
- package/apps/local-dashboard/dist/assets/geist-latin-wght-normal-Dm3htQBi.woff2 +0 -0
- package/apps/local-dashboard/dist/assets/index-C4EOTFZ2.js +15 -0
- package/apps/local-dashboard/dist/assets/index-bl-Webyd.css +1 -0
- package/apps/local-dashboard/dist/assets/vendor-react-U7zYD9Rg.js +60 -0
- package/apps/local-dashboard/dist/assets/vendor-table-B7VF2Ipl.js +26 -0
- package/apps/local-dashboard/dist/assets/vendor-ui-D7_zX_qy.js +346 -0
- package/apps/local-dashboard/dist/favicon.png +0 -0
- package/apps/local-dashboard/dist/index.html +17 -0
- package/apps/local-dashboard/dist/logo.png +0 -0
- package/apps/local-dashboard/dist/logo.svg +9 -0
- package/cli/selftune/badge/badge-data.ts +1 -1
- package/cli/selftune/badge/badge.ts +4 -8
- package/cli/selftune/canonical-export.ts +183 -0
- package/cli/selftune/constants.ts +28 -0
- package/cli/selftune/contribute/contribute.ts +1 -1
- package/cli/selftune/cron/setup.ts +17 -17
- package/cli/selftune/dashboard-contract.ts +202 -0
- package/cli/selftune/dashboard-server.ts +653 -186
- package/cli/selftune/dashboard.ts +41 -176
- package/cli/selftune/eval/baseline.ts +5 -4
- package/cli/selftune/eval/composability-v2.ts +273 -0
- package/cli/selftune/eval/hooks-to-evals.ts +34 -15
- package/cli/selftune/eval/unit-test-cli.ts +1 -1
- package/cli/selftune/evolution/evidence.ts +26 -0
- package/cli/selftune/evolution/evolve-body.ts +105 -11
- package/cli/selftune/evolution/evolve.ts +371 -25
- package/cli/selftune/evolution/extract-patterns.ts +87 -29
- package/cli/selftune/evolution/rollback.ts +2 -2
- package/cli/selftune/grading/auto-grade.ts +200 -0
- package/cli/selftune/grading/grade-session.ts +448 -97
- package/cli/selftune/grading/results.ts +42 -0
- package/cli/selftune/hooks/prompt-log.ts +172 -2
- package/cli/selftune/hooks/session-stop.ts +123 -3
- package/cli/selftune/hooks/skill-eval.ts +119 -3
- package/cli/selftune/index.ts +395 -116
- package/cli/selftune/ingestors/claude-replay.ts +140 -114
- package/cli/selftune/ingestors/codex-rollout.ts +345 -46
- package/cli/selftune/ingestors/codex-wrapper.ts +207 -39
- package/cli/selftune/ingestors/openclaw-ingest.ts +141 -8
- package/cli/selftune/ingestors/opencode-ingest.ts +193 -17
- package/cli/selftune/init.ts +227 -14
- package/cli/selftune/last.ts +14 -5
- package/cli/selftune/localdb/db.ts +63 -0
- package/cli/selftune/localdb/materialize.ts +428 -0
- package/cli/selftune/localdb/queries.ts +376 -0
- package/cli/selftune/localdb/schema.ts +204 -0
- package/cli/selftune/monitoring/watch.ts +66 -15
- package/cli/selftune/normalization.ts +682 -0
- package/cli/selftune/observability.ts +19 -44
- package/cli/selftune/orchestrate.ts +1073 -0
- package/cli/selftune/quickstart.ts +203 -0
- package/cli/selftune/repair/skill-usage.ts +576 -0
- package/cli/selftune/schedule.ts +561 -0
- package/cli/selftune/status.ts +48 -26
- package/cli/selftune/sync.ts +627 -0
- package/cli/selftune/types.ts +148 -0
- package/cli/selftune/utils/canonical-log.ts +45 -0
- package/cli/selftune/utils/hooks.ts +41 -0
- package/cli/selftune/utils/html.ts +27 -0
- package/cli/selftune/utils/llm-call.ts +78 -20
- package/cli/selftune/utils/math.ts +10 -0
- package/cli/selftune/utils/query-filter.ts +139 -0
- package/cli/selftune/utils/skill-discovery.ts +340 -0
- package/cli/selftune/utils/skill-log.ts +68 -0
- package/cli/selftune/utils/skill-usage-confidence.ts +18 -0
- package/cli/selftune/utils/transcript.ts +272 -26
- package/cli/selftune/workflows/discover.ts +254 -0
- package/cli/selftune/workflows/skill-md-writer.ts +288 -0
- package/cli/selftune/workflows/workflows.ts +188 -0
- package/package.json +21 -8
- package/packages/telemetry-contract/README.md +11 -0
- package/packages/telemetry-contract/fixtures/golden.json +87 -0
- package/packages/telemetry-contract/fixtures/golden.test.ts +42 -0
- package/packages/telemetry-contract/index.ts +1 -0
- package/packages/telemetry-contract/package.json +19 -0
- package/packages/telemetry-contract/src/index.ts +2 -0
- package/packages/telemetry-contract/src/types.ts +163 -0
- package/packages/telemetry-contract/src/validators.ts +109 -0
- package/skill/SKILL.md +84 -53
- package/skill/Workflows/AutoActivation.md +17 -16
- package/skill/Workflows/Badge.md +6 -0
- package/skill/Workflows/Baseline.md +46 -23
- package/skill/Workflows/Composability.md +12 -5
- package/skill/Workflows/Contribute.md +17 -14
- package/skill/Workflows/Cron.md +56 -79
- package/skill/Workflows/Dashboard.md +45 -34
- package/skill/Workflows/Doctor.md +30 -17
- package/skill/Workflows/Evals.md +64 -40
- package/skill/Workflows/EvolutionMemory.md +2 -0
- package/skill/Workflows/Evolve.md +102 -47
- package/skill/Workflows/EvolveBody.md +6 -6
- package/skill/Workflows/Grade.md +36 -31
- package/skill/Workflows/ImportSkillsBench.md +11 -5
- package/skill/Workflows/Ingest.md +43 -36
- package/skill/Workflows/Initialize.md +44 -30
- package/skill/Workflows/Orchestrate.md +139 -0
- package/skill/Workflows/Replay.md +39 -18
- package/skill/Workflows/Rollback.md +3 -3
- package/skill/Workflows/Schedule.md +61 -0
- package/skill/Workflows/Sync.md +88 -0
- package/skill/Workflows/UnitTest.md +34 -22
- package/skill/Workflows/Watch.md +14 -4
- package/skill/Workflows/Workflows.md +129 -0
- package/skill/assets/activation-rules-default.json +26 -0
- package/skill/assets/multi-skill-settings.json +63 -0
- package/skill/assets/single-skill-settings.json +57 -0
- package/skill/references/invocation-taxonomy.md +2 -2
- package/skill/references/logs.md +164 -2
- package/skill/references/setup-patterns.md +65 -0
- package/skill/references/version-history.md +40 -0
- package/skill/settings_snippet.json +1 -1
- package/templates/multi-skill-settings.json +7 -7
- package/templates/single-skill-settings.json +6 -6
- package/dashboard/index.html +0 -1680
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SQLite database lifecycle for selftune local materialized view store.
|
|
3
|
+
*
|
|
4
|
+
* Uses Bun's built-in SQLite driver. The database file lives at
|
|
5
|
+
* ~/.selftune/selftune.db and is treated as a disposable cache —
|
|
6
|
+
* it can always be rebuilt from the authoritative JSONL logs.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { Database } from "bun:sqlite";
|
|
10
|
+
import { existsSync, mkdirSync } from "node:fs";
|
|
11
|
+
import { dirname, join } from "node:path";
|
|
12
|
+
import { SELFTUNE_CONFIG_DIR } from "../constants.js";
|
|
13
|
+
import { ALL_DDL } from "./schema.js";
|
|
14
|
+
|
|
15
|
+
/** Default database file path. */
|
|
16
|
+
export const DB_PATH = join(SELFTUNE_CONFIG_DIR, "selftune.db");
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Open (or create) the selftune SQLite database at the given path.
|
|
20
|
+
* Runs all DDL to ensure the schema exists. Uses WAL mode for
|
|
21
|
+
* concurrent read/write safety.
|
|
22
|
+
*
|
|
23
|
+
* Pass ":memory:" for an in-memory database (useful for tests).
|
|
24
|
+
*/
|
|
25
|
+
export function openDb(dbPath: string = DB_PATH): Database {
|
|
26
|
+
// Ensure parent directory exists for file-based databases
|
|
27
|
+
if (dbPath !== ":memory:") {
|
|
28
|
+
const dir = dirname(dbPath);
|
|
29
|
+
if (!existsSync(dir)) {
|
|
30
|
+
mkdirSync(dir, { recursive: true });
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const db = new Database(dbPath);
|
|
35
|
+
|
|
36
|
+
// Enable WAL mode for better concurrent access
|
|
37
|
+
db.run("PRAGMA journal_mode = WAL");
|
|
38
|
+
db.run("PRAGMA foreign_keys = ON");
|
|
39
|
+
|
|
40
|
+
// Run all DDL statements
|
|
41
|
+
for (const ddl of ALL_DDL) {
|
|
42
|
+
db.run(ddl);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return db;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Get a metadata value from the _meta table.
|
|
50
|
+
*/
|
|
51
|
+
export function getMeta(db: Database, key: string): string | null {
|
|
52
|
+
const row = db.query("SELECT value FROM _meta WHERE key = ?").get(key) as {
|
|
53
|
+
value: string;
|
|
54
|
+
} | null;
|
|
55
|
+
return row?.value ?? null;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Set a metadata value in the _meta table.
|
|
60
|
+
*/
|
|
61
|
+
export function setMeta(db: Database, key: string, value: string): void {
|
|
62
|
+
db.run("INSERT OR REPLACE INTO _meta (key, value) VALUES (?, ?)", [key, value]);
|
|
63
|
+
}
|
|
@@ -0,0 +1,428 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Materializer: reads JSONL source-of-truth logs and inserts structured
|
|
3
|
+
* records into the local SQLite database.
|
|
4
|
+
*
|
|
5
|
+
* Supports two modes:
|
|
6
|
+
* - Full rebuild: drops all data and re-inserts from scratch
|
|
7
|
+
* - Incremental: only inserts records newer than last materialization
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { Database } from "bun:sqlite";
|
|
11
|
+
import type {
|
|
12
|
+
CanonicalExecutionFactRecord,
|
|
13
|
+
CanonicalPromptRecord,
|
|
14
|
+
CanonicalRecord,
|
|
15
|
+
CanonicalSessionRecord,
|
|
16
|
+
CanonicalSkillInvocationRecord,
|
|
17
|
+
} from "@selftune/telemetry-contract";
|
|
18
|
+
import {
|
|
19
|
+
CANONICAL_LOG,
|
|
20
|
+
EVOLUTION_AUDIT_LOG,
|
|
21
|
+
EVOLUTION_EVIDENCE_LOG,
|
|
22
|
+
ORCHESTRATE_RUN_LOG,
|
|
23
|
+
TELEMETRY_LOG,
|
|
24
|
+
} from "../constants.js";
|
|
25
|
+
import type { OrchestrateRunReport } from "../dashboard-contract.js";
|
|
26
|
+
import { readEvidenceTrail } from "../evolution/evidence.js";
|
|
27
|
+
import type {
|
|
28
|
+
EvolutionAuditEntry,
|
|
29
|
+
EvolutionEvidenceEntry,
|
|
30
|
+
SessionTelemetryRecord,
|
|
31
|
+
SkillUsageRecord,
|
|
32
|
+
} from "../types.js";
|
|
33
|
+
import { readCanonicalRecords } from "../utils/canonical-log.js";
|
|
34
|
+
import { readJsonl } from "../utils/jsonl.js";
|
|
35
|
+
import { readEffectiveSkillUsageRecords } from "../utils/skill-log.js";
|
|
36
|
+
import { getMeta, setMeta } from "./db.js";
|
|
37
|
+
|
|
38
|
+
/** Meta key tracking last materialization timestamp. */
|
|
39
|
+
const META_LAST_MATERIALIZED = "last_materialized_at";
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Full rebuild: drop all data tables, then re-insert everything.
|
|
43
|
+
*/
|
|
44
|
+
export function materializeFull(db: Database, options?: MaterializeOptions): MaterializeResult {
|
|
45
|
+
const tables = [
|
|
46
|
+
"skill_usage",
|
|
47
|
+
"session_telemetry",
|
|
48
|
+
"evolution_audit",
|
|
49
|
+
"evolution_evidence",
|
|
50
|
+
"execution_facts",
|
|
51
|
+
"skill_invocations",
|
|
52
|
+
"prompts",
|
|
53
|
+
"sessions",
|
|
54
|
+
"orchestrate_runs",
|
|
55
|
+
];
|
|
56
|
+
for (const table of tables) {
|
|
57
|
+
db.run(`DELETE FROM ${table}`);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return materializeIncremental(db, { ...options, since: null });
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export interface MaterializeOptions {
|
|
64
|
+
canonicalLogPath?: string;
|
|
65
|
+
telemetryLogPath?: string;
|
|
66
|
+
evolutionAuditPath?: string;
|
|
67
|
+
evolutionEvidencePath?: string;
|
|
68
|
+
orchestrateRunLogPath?: string;
|
|
69
|
+
since?: string | null;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export interface MaterializeResult {
|
|
73
|
+
sessions: number;
|
|
74
|
+
prompts: number;
|
|
75
|
+
skillInvocations: number;
|
|
76
|
+
executionFacts: number;
|
|
77
|
+
sessionTelemetry: number;
|
|
78
|
+
skillUsage: number;
|
|
79
|
+
evolutionAudit: number;
|
|
80
|
+
evolutionEvidence: number;
|
|
81
|
+
orchestrateRuns: number;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Incremental materialization: only insert records newer than last run.
|
|
86
|
+
* Uses INSERT OR IGNORE for idempotency on primary-keyed tables,
|
|
87
|
+
* and UNIQUE indexes for deduplication on append-only tables.
|
|
88
|
+
*/
|
|
89
|
+
export function materializeIncremental(
|
|
90
|
+
db: Database,
|
|
91
|
+
options?: MaterializeOptions,
|
|
92
|
+
): MaterializeResult {
|
|
93
|
+
const since = options?.since !== undefined ? options.since : getMeta(db, META_LAST_MATERIALIZED);
|
|
94
|
+
const now = new Date().toISOString();
|
|
95
|
+
|
|
96
|
+
const result: MaterializeResult = {
|
|
97
|
+
sessions: 0,
|
|
98
|
+
prompts: 0,
|
|
99
|
+
skillInvocations: 0,
|
|
100
|
+
executionFacts: 0,
|
|
101
|
+
sessionTelemetry: 0,
|
|
102
|
+
skillUsage: 0,
|
|
103
|
+
evolutionAudit: 0,
|
|
104
|
+
evolutionEvidence: 0,
|
|
105
|
+
orchestrateRuns: 0,
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
// -- Read all data BEFORE opening the transaction ---------------------------
|
|
109
|
+
// This keeps file I/O out of the write lock for better concurrency.
|
|
110
|
+
|
|
111
|
+
const canonical = readCanonicalRecords(options?.canonicalLogPath ?? CANONICAL_LOG);
|
|
112
|
+
const filteredCanonical = since ? canonical.filter((r) => r.normalized_at > since) : canonical;
|
|
113
|
+
|
|
114
|
+
// Pre-partition canonical records by kind (single pass instead of 4x full scan)
|
|
115
|
+
const byKind = new Map<string, CanonicalRecord[]>();
|
|
116
|
+
for (const r of filteredCanonical) {
|
|
117
|
+
const arr = byKind.get(r.record_kind);
|
|
118
|
+
if (arr) arr.push(r);
|
|
119
|
+
else byKind.set(r.record_kind, [r]);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const telemetry = readJsonl<SessionTelemetryRecord>(options?.telemetryLogPath ?? TELEMETRY_LOG);
|
|
123
|
+
const filteredTelemetry = since ? telemetry.filter((r) => r.timestamp > since) : telemetry;
|
|
124
|
+
|
|
125
|
+
const skills = readEffectiveSkillUsageRecords();
|
|
126
|
+
const filteredSkills = since ? skills.filter((r) => r.timestamp > since) : skills;
|
|
127
|
+
|
|
128
|
+
const audit = readJsonl<EvolutionAuditEntry>(options?.evolutionAuditPath ?? EVOLUTION_AUDIT_LOG);
|
|
129
|
+
const filteredAudit = since ? audit.filter((r) => r.timestamp > since) : audit;
|
|
130
|
+
|
|
131
|
+
const evidence = readEvidenceTrail(
|
|
132
|
+
undefined,
|
|
133
|
+
options?.evolutionEvidencePath ?? EVOLUTION_EVIDENCE_LOG,
|
|
134
|
+
);
|
|
135
|
+
const filteredEvidence = since ? evidence.filter((r) => r.timestamp > since) : evidence;
|
|
136
|
+
|
|
137
|
+
const orchestrateRuns = readJsonl<OrchestrateRunReport>(
|
|
138
|
+
options?.orchestrateRunLogPath ?? ORCHESTRATE_RUN_LOG,
|
|
139
|
+
);
|
|
140
|
+
const filteredOrchestrateRuns = since
|
|
141
|
+
? orchestrateRuns.filter((r) => r.timestamp > since)
|
|
142
|
+
: orchestrateRuns;
|
|
143
|
+
|
|
144
|
+
// -- Insert everything inside a single transaction --------------------------
|
|
145
|
+
db.run("BEGIN TRANSACTION");
|
|
146
|
+
try {
|
|
147
|
+
result.sessions = insertSessions(db, byKind.get("session") ?? []);
|
|
148
|
+
result.prompts = insertPrompts(db, byKind.get("prompt") ?? []);
|
|
149
|
+
result.skillInvocations = insertSkillInvocations(db, byKind.get("skill_invocation") ?? []);
|
|
150
|
+
result.executionFacts = insertExecutionFacts(db, byKind.get("execution_fact") ?? []);
|
|
151
|
+
result.sessionTelemetry = insertSessionTelemetry(db, filteredTelemetry);
|
|
152
|
+
result.skillUsage = insertSkillUsage(db, filteredSkills);
|
|
153
|
+
result.evolutionAudit = insertEvolutionAudit(db, filteredAudit);
|
|
154
|
+
result.evolutionEvidence = insertEvolutionEvidence(db, filteredEvidence);
|
|
155
|
+
result.orchestrateRuns = insertOrchestrateRuns(db, filteredOrchestrateRuns);
|
|
156
|
+
|
|
157
|
+
setMeta(db, META_LAST_MATERIALIZED, now);
|
|
158
|
+
db.run("COMMIT");
|
|
159
|
+
} catch (err) {
|
|
160
|
+
db.run("ROLLBACK");
|
|
161
|
+
throw err;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return result;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// -- Insert helpers -----------------------------------------------------------
|
|
168
|
+
|
|
169
|
+
function insertSessions(db: Database, records: CanonicalRecord[]): number {
|
|
170
|
+
const stmt = db.prepare(`
|
|
171
|
+
INSERT OR IGNORE INTO sessions
|
|
172
|
+
(session_id, started_at, ended_at, platform, model, completion_status,
|
|
173
|
+
source_session_kind, agent_cli, workspace_path, repo_remote, branch,
|
|
174
|
+
schema_version, normalized_at)
|
|
175
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
176
|
+
`);
|
|
177
|
+
|
|
178
|
+
let count = 0;
|
|
179
|
+
for (const r of records) {
|
|
180
|
+
const s = r as CanonicalSessionRecord;
|
|
181
|
+
stmt.run(
|
|
182
|
+
s.session_id,
|
|
183
|
+
s.started_at ?? null,
|
|
184
|
+
s.ended_at ?? null,
|
|
185
|
+
s.platform,
|
|
186
|
+
s.model ?? null,
|
|
187
|
+
s.completion_status ?? null,
|
|
188
|
+
s.source_session_kind ?? null,
|
|
189
|
+
s.agent_cli ?? null,
|
|
190
|
+
s.workspace_path ?? null,
|
|
191
|
+
s.repo_remote ?? null,
|
|
192
|
+
s.branch ?? null,
|
|
193
|
+
s.schema_version,
|
|
194
|
+
s.normalized_at,
|
|
195
|
+
);
|
|
196
|
+
count++;
|
|
197
|
+
}
|
|
198
|
+
return count;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function insertPrompts(db: Database, records: CanonicalRecord[]): number {
|
|
202
|
+
const stmt = db.prepare(`
|
|
203
|
+
INSERT OR IGNORE INTO prompts
|
|
204
|
+
(prompt_id, session_id, occurred_at, prompt_kind, is_actionable, prompt_index, prompt_text)
|
|
205
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
206
|
+
`);
|
|
207
|
+
|
|
208
|
+
let count = 0;
|
|
209
|
+
for (const r of records) {
|
|
210
|
+
const p = r as CanonicalPromptRecord;
|
|
211
|
+
stmt.run(
|
|
212
|
+
p.prompt_id,
|
|
213
|
+
p.session_id,
|
|
214
|
+
p.occurred_at,
|
|
215
|
+
p.prompt_kind,
|
|
216
|
+
p.is_actionable ? 1 : 0,
|
|
217
|
+
p.prompt_index ?? null,
|
|
218
|
+
p.prompt_text,
|
|
219
|
+
);
|
|
220
|
+
count++;
|
|
221
|
+
}
|
|
222
|
+
return count;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function insertSkillInvocations(db: Database, records: CanonicalRecord[]): number {
|
|
226
|
+
const stmt = db.prepare(`
|
|
227
|
+
INSERT OR IGNORE INTO skill_invocations
|
|
228
|
+
(skill_invocation_id, session_id, occurred_at, skill_name, invocation_mode,
|
|
229
|
+
triggered, confidence, tool_name, matched_prompt_id)
|
|
230
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
231
|
+
`);
|
|
232
|
+
|
|
233
|
+
let count = 0;
|
|
234
|
+
for (const r of records) {
|
|
235
|
+
const si = r as CanonicalSkillInvocationRecord;
|
|
236
|
+
stmt.run(
|
|
237
|
+
si.skill_invocation_id,
|
|
238
|
+
si.session_id,
|
|
239
|
+
si.occurred_at,
|
|
240
|
+
si.skill_name,
|
|
241
|
+
si.invocation_mode,
|
|
242
|
+
si.triggered ? 1 : 0,
|
|
243
|
+
si.confidence,
|
|
244
|
+
si.tool_name ?? null,
|
|
245
|
+
si.matched_prompt_id ?? null,
|
|
246
|
+
);
|
|
247
|
+
count++;
|
|
248
|
+
}
|
|
249
|
+
return count;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
function insertExecutionFacts(db: Database, records: CanonicalRecord[]): number {
|
|
253
|
+
const stmt = db.prepare(`
|
|
254
|
+
INSERT INTO execution_facts
|
|
255
|
+
(session_id, occurred_at, prompt_id, tool_calls_json, total_tool_calls,
|
|
256
|
+
assistant_turns, errors_encountered, input_tokens, output_tokens,
|
|
257
|
+
duration_ms, completion_status)
|
|
258
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
259
|
+
`);
|
|
260
|
+
|
|
261
|
+
let count = 0;
|
|
262
|
+
for (const r of records) {
|
|
263
|
+
const ef = r as CanonicalExecutionFactRecord;
|
|
264
|
+
stmt.run(
|
|
265
|
+
ef.session_id,
|
|
266
|
+
ef.occurred_at,
|
|
267
|
+
ef.prompt_id ?? null,
|
|
268
|
+
JSON.stringify(ef.tool_calls_json),
|
|
269
|
+
ef.total_tool_calls,
|
|
270
|
+
ef.assistant_turns,
|
|
271
|
+
ef.errors_encountered,
|
|
272
|
+
ef.input_tokens ?? null,
|
|
273
|
+
ef.output_tokens ?? null,
|
|
274
|
+
ef.duration_ms ?? null,
|
|
275
|
+
ef.completion_status ?? null,
|
|
276
|
+
);
|
|
277
|
+
count++;
|
|
278
|
+
}
|
|
279
|
+
return count;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
function insertSessionTelemetry(db: Database, records: SessionTelemetryRecord[]): number {
|
|
283
|
+
const stmt = db.prepare(`
|
|
284
|
+
INSERT OR IGNORE INTO session_telemetry
|
|
285
|
+
(session_id, timestamp, cwd, transcript_path, tool_calls_json,
|
|
286
|
+
total_tool_calls, bash_commands_json, skills_triggered_json,
|
|
287
|
+
skills_invoked_json, assistant_turns, errors_encountered,
|
|
288
|
+
transcript_chars, last_user_query, source, input_tokens, output_tokens)
|
|
289
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
290
|
+
`);
|
|
291
|
+
|
|
292
|
+
let count = 0;
|
|
293
|
+
for (const r of records) {
|
|
294
|
+
stmt.run(
|
|
295
|
+
r.session_id,
|
|
296
|
+
r.timestamp,
|
|
297
|
+
r.cwd,
|
|
298
|
+
r.transcript_path,
|
|
299
|
+
JSON.stringify(r.tool_calls),
|
|
300
|
+
r.total_tool_calls,
|
|
301
|
+
JSON.stringify(r.bash_commands),
|
|
302
|
+
JSON.stringify(r.skills_triggered),
|
|
303
|
+
r.skills_invoked ? JSON.stringify(r.skills_invoked) : null,
|
|
304
|
+
r.assistant_turns,
|
|
305
|
+
r.errors_encountered,
|
|
306
|
+
r.transcript_chars,
|
|
307
|
+
r.last_user_query,
|
|
308
|
+
r.source ?? null,
|
|
309
|
+
r.input_tokens ?? null,
|
|
310
|
+
r.output_tokens ?? null,
|
|
311
|
+
);
|
|
312
|
+
count++;
|
|
313
|
+
}
|
|
314
|
+
return count;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
function insertSkillUsage(db: Database, records: SkillUsageRecord[]): number {
|
|
318
|
+
// Uses INSERT OR IGNORE with a UNIQUE index on the dedup composite key
|
|
319
|
+
// (idx_skill_usage_dedup defined in schema.ts).
|
|
320
|
+
const stmt = db.prepare(`
|
|
321
|
+
INSERT OR IGNORE INTO skill_usage
|
|
322
|
+
(timestamp, session_id, skill_name, skill_path, skill_scope, query, triggered, source)
|
|
323
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
324
|
+
`);
|
|
325
|
+
|
|
326
|
+
let count = 0;
|
|
327
|
+
for (const r of records) {
|
|
328
|
+
stmt.run(
|
|
329
|
+
r.timestamp,
|
|
330
|
+
r.session_id,
|
|
331
|
+
r.skill_name,
|
|
332
|
+
r.skill_path,
|
|
333
|
+
r.skill_scope ?? null,
|
|
334
|
+
r.query,
|
|
335
|
+
r.triggered ? 1 : 0,
|
|
336
|
+
r.source ?? null,
|
|
337
|
+
);
|
|
338
|
+
count++;
|
|
339
|
+
}
|
|
340
|
+
return count;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
function insertEvolutionAudit(db: Database, records: EvolutionAuditEntry[]): number {
|
|
344
|
+
// Uses INSERT OR IGNORE with a UNIQUE index on the dedup composite key
|
|
345
|
+
// (idx_evo_audit_dedup defined in schema.ts).
|
|
346
|
+
const stmt = db.prepare(`
|
|
347
|
+
INSERT OR IGNORE INTO evolution_audit
|
|
348
|
+
(timestamp, proposal_id, skill_name, action, details, eval_snapshot_json)
|
|
349
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
350
|
+
`);
|
|
351
|
+
|
|
352
|
+
let count = 0;
|
|
353
|
+
for (const r of records) {
|
|
354
|
+
stmt.run(
|
|
355
|
+
r.timestamp,
|
|
356
|
+
r.proposal_id,
|
|
357
|
+
r.skill_name ?? null,
|
|
358
|
+
r.action,
|
|
359
|
+
r.details,
|
|
360
|
+
r.eval_snapshot ? JSON.stringify(r.eval_snapshot) : null,
|
|
361
|
+
);
|
|
362
|
+
count++;
|
|
363
|
+
}
|
|
364
|
+
return count;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
function insertEvolutionEvidence(db: Database, records: EvolutionEvidenceEntry[]): number {
|
|
368
|
+
// Uses INSERT OR IGNORE with a UNIQUE index on the dedup composite key
|
|
369
|
+
// (idx_evo_evidence_dedup defined in schema.ts).
|
|
370
|
+
const stmt = db.prepare(`
|
|
371
|
+
INSERT OR IGNORE INTO evolution_evidence
|
|
372
|
+
(timestamp, proposal_id, skill_name, skill_path, target, stage,
|
|
373
|
+
rationale, confidence, details, original_text, proposed_text,
|
|
374
|
+
eval_set_json, validation_json)
|
|
375
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
376
|
+
`);
|
|
377
|
+
|
|
378
|
+
let count = 0;
|
|
379
|
+
for (const r of records) {
|
|
380
|
+
stmt.run(
|
|
381
|
+
r.timestamp,
|
|
382
|
+
r.proposal_id,
|
|
383
|
+
r.skill_name,
|
|
384
|
+
r.skill_path,
|
|
385
|
+
r.target,
|
|
386
|
+
r.stage,
|
|
387
|
+
r.rationale ?? null,
|
|
388
|
+
r.confidence ?? null,
|
|
389
|
+
r.details ?? null,
|
|
390
|
+
r.original_text ?? null,
|
|
391
|
+
r.proposed_text ?? null,
|
|
392
|
+
r.eval_set ? JSON.stringify(r.eval_set) : null,
|
|
393
|
+
r.validation ? JSON.stringify(r.validation) : null,
|
|
394
|
+
);
|
|
395
|
+
count++;
|
|
396
|
+
}
|
|
397
|
+
return count;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
function insertOrchestrateRuns(db: Database, records: OrchestrateRunReport[]): number {
|
|
401
|
+
const stmt = db.prepare(`
|
|
402
|
+
INSERT OR IGNORE INTO orchestrate_runs
|
|
403
|
+
(run_id, timestamp, elapsed_ms, dry_run, approval_mode,
|
|
404
|
+
total_skills, evaluated, evolved, deployed, watched, skipped,
|
|
405
|
+
skill_actions_json)
|
|
406
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
407
|
+
`);
|
|
408
|
+
|
|
409
|
+
let count = 0;
|
|
410
|
+
for (const r of records) {
|
|
411
|
+
stmt.run(
|
|
412
|
+
r.run_id,
|
|
413
|
+
r.timestamp,
|
|
414
|
+
r.elapsed_ms,
|
|
415
|
+
r.dry_run ? 1 : 0,
|
|
416
|
+
r.approval_mode,
|
|
417
|
+
r.total_skills,
|
|
418
|
+
r.evaluated,
|
|
419
|
+
r.evolved,
|
|
420
|
+
r.deployed,
|
|
421
|
+
r.watched,
|
|
422
|
+
r.skipped,
|
|
423
|
+
JSON.stringify(r.skill_actions),
|
|
424
|
+
);
|
|
425
|
+
count++;
|
|
426
|
+
}
|
|
427
|
+
return count;
|
|
428
|
+
}
|