selftune 0.1.4 → 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 +156 -0
- package/.claude/agents/evolution-reviewer.md +180 -0
- package/.claude/agents/integration-guide.md +212 -0
- package/.claude/agents/pattern-analyst.md +160 -0
- package/CHANGELOG.md +46 -1
- package/README.md +105 -257
- 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/assets/BeforeAfter.gif +0 -0
- package/assets/FeedbackLoop.gif +0 -0
- package/assets/logo.svg +9 -0
- package/assets/skill-health-badge.svg +20 -0
- package/cli/selftune/activation-rules.ts +171 -0
- package/cli/selftune/badge/badge-data.ts +108 -0
- package/cli/selftune/badge/badge-svg.ts +212 -0
- package/cli/selftune/badge/badge.ts +99 -0
- package/cli/selftune/canonical-export.ts +183 -0
- package/cli/selftune/constants.ts +103 -1
- package/cli/selftune/contribute/bundle.ts +314 -0
- package/cli/selftune/contribute/contribute.ts +214 -0
- package/cli/selftune/contribute/sanitize.ts +162 -0
- package/cli/selftune/cron/setup.ts +266 -0
- package/cli/selftune/dashboard-contract.ts +202 -0
- package/cli/selftune/dashboard-server.ts +1049 -0
- package/cli/selftune/dashboard.ts +43 -156
- package/cli/selftune/eval/baseline.ts +248 -0
- package/cli/selftune/eval/composability-v2.ts +273 -0
- package/cli/selftune/eval/composability.ts +117 -0
- package/cli/selftune/eval/generate-unit-tests.ts +143 -0
- package/cli/selftune/eval/hooks-to-evals.ts +101 -16
- package/cli/selftune/eval/import-skillsbench.ts +221 -0
- package/cli/selftune/eval/synthetic-evals.ts +172 -0
- package/cli/selftune/eval/unit-test-cli.ts +152 -0
- package/cli/selftune/eval/unit-test.ts +196 -0
- package/cli/selftune/evolution/deploy-proposal.ts +142 -1
- package/cli/selftune/evolution/evidence.ts +26 -0
- package/cli/selftune/evolution/evolve-body.ts +586 -0
- package/cli/selftune/evolution/evolve.ts +825 -116
- package/cli/selftune/evolution/extract-patterns.ts +105 -16
- package/cli/selftune/evolution/pareto.ts +314 -0
- package/cli/selftune/evolution/propose-body.ts +171 -0
- package/cli/selftune/evolution/propose-description.ts +100 -2
- package/cli/selftune/evolution/propose-routing.ts +166 -0
- package/cli/selftune/evolution/refine-body.ts +141 -0
- package/cli/selftune/evolution/rollback.ts +21 -4
- package/cli/selftune/evolution/validate-body.ts +254 -0
- package/cli/selftune/evolution/validate-proposal.ts +257 -35
- package/cli/selftune/evolution/validate-routing.ts +177 -0
- package/cli/selftune/grading/auto-grade.ts +200 -0
- package/cli/selftune/grading/grade-session.ts +513 -42
- package/cli/selftune/grading/pre-gates.ts +104 -0
- package/cli/selftune/grading/results.ts +42 -0
- package/cli/selftune/hooks/auto-activate.ts +185 -0
- package/cli/selftune/hooks/evolution-guard.ts +165 -0
- package/cli/selftune/hooks/prompt-log.ts +172 -2
- package/cli/selftune/hooks/session-stop.ts +123 -3
- package/cli/selftune/hooks/skill-change-guard.ts +112 -0
- package/cli/selftune/hooks/skill-eval.ts +119 -3
- package/cli/selftune/index.ts +415 -48
- package/cli/selftune/ingestors/claude-replay.ts +377 -0
- 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 +573 -0
- package/cli/selftune/ingestors/opencode-ingest.ts +193 -17
- package/cli/selftune/init.ts +376 -16
- 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/memory/writer.ts +447 -0
- package/cli/selftune/monitoring/watch.ts +90 -16
- 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 +59 -33
- package/cli/selftune/sync.ts +627 -0
- package/cli/selftune/types.ts +525 -5
- package/cli/selftune/utils/canonical-log.ts +45 -0
- package/cli/selftune/utils/frontmatter.ts +217 -0
- package/cli/selftune/utils/hooks.ts +41 -0
- package/cli/selftune/utils/html.ts +27 -0
- package/cli/selftune/utils/llm-call.ts +103 -19
- 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 +307 -26
- package/cli/selftune/utils/trigger-check.ts +89 -0
- package/cli/selftune/utils/tui.ts +156 -0
- 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 +28 -11
- 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 +180 -33
- package/skill/Workflows/AutoActivation.md +145 -0
- package/skill/Workflows/Badge.md +124 -0
- package/skill/Workflows/Baseline.md +144 -0
- package/skill/Workflows/Composability.md +107 -0
- package/skill/Workflows/Contribute.md +94 -0
- package/skill/Workflows/Cron.md +132 -0
- package/skill/Workflows/Dashboard.md +214 -0
- package/skill/Workflows/Doctor.md +63 -14
- package/skill/Workflows/Evals.md +110 -18
- package/skill/Workflows/EvolutionMemory.md +154 -0
- package/skill/Workflows/Evolve.md +181 -21
- package/skill/Workflows/EvolveBody.md +159 -0
- package/skill/Workflows/Grade.md +36 -31
- package/skill/Workflows/ImportSkillsBench.md +117 -0
- package/skill/Workflows/Ingest.md +142 -21
- package/skill/Workflows/Initialize.md +91 -23
- package/skill/Workflows/Orchestrate.md +139 -0
- package/skill/Workflows/Replay.md +91 -0
- package/skill/Workflows/Rollback.md +23 -4
- package/skill/Workflows/Schedule.md +61 -0
- package/skill/Workflows/Sync.md +88 -0
- package/skill/Workflows/UnitTest.md +150 -0
- package/skill/Workflows/Watch.md +33 -1
- 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 +23 -0
- package/templates/activation-rules-default.json +27 -0
- package/templates/multi-skill-settings.json +64 -0
- package/templates/single-skill-settings.json +58 -0
- package/dashboard/index.html +0 -1119
|
@@ -25,8 +25,24 @@ import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
|
25
25
|
import { homedir } from "node:os";
|
|
26
26
|
import { basename, join } from "node:path";
|
|
27
27
|
import { parseArgs } from "node:util";
|
|
28
|
-
import { QUERY_LOG, SKILL_LOG, TELEMETRY_LOG } from "../constants.js";
|
|
29
|
-
import
|
|
28
|
+
import { CANONICAL_LOG, QUERY_LOG, SKILL_LOG, TELEMETRY_LOG } from "../constants.js";
|
|
29
|
+
import {
|
|
30
|
+
appendCanonicalRecords,
|
|
31
|
+
buildCanonicalExecutionFact,
|
|
32
|
+
buildCanonicalPrompt,
|
|
33
|
+
buildCanonicalSession,
|
|
34
|
+
buildCanonicalSkillInvocation,
|
|
35
|
+
type CanonicalBaseInput,
|
|
36
|
+
deriveInvocationMode,
|
|
37
|
+
derivePromptId,
|
|
38
|
+
deriveSkillInvocationId,
|
|
39
|
+
} from "../normalization.js";
|
|
40
|
+
import type {
|
|
41
|
+
CanonicalRecord,
|
|
42
|
+
QueryLogRecord,
|
|
43
|
+
SessionTelemetryRecord,
|
|
44
|
+
SkillUsageRecord,
|
|
45
|
+
} from "../types.js";
|
|
30
46
|
import { appendJsonl, loadMarker, saveMarker } from "../utils/jsonl.js";
|
|
31
47
|
|
|
32
48
|
const XDG_DATA_HOME = process.env.XDG_DATA_HOME ?? join(homedir(), ".local", "share");
|
|
@@ -48,6 +64,26 @@ const OPENCODE_SKILLS_DIRS = [
|
|
|
48
64
|
join(homedir(), ".config", "opencode", "skills"),
|
|
49
65
|
];
|
|
50
66
|
|
|
67
|
+
interface TriggeredSkillDetection {
|
|
68
|
+
skill_name: string;
|
|
69
|
+
has_skill_md_read: boolean;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function escapeRegExp(value: string): string {
|
|
73
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function containsWholeSkillMention(text: string, skillName: string): boolean {
|
|
77
|
+
const trimmedSkillName = skillName.trim();
|
|
78
|
+
if (!text || !trimmedSkillName) return false;
|
|
79
|
+
|
|
80
|
+
const pattern = new RegExp(
|
|
81
|
+
`(^|[^A-Za-z0-9_])${escapeRegExp(trimmedSkillName)}([^A-Za-z0-9_]|$)`,
|
|
82
|
+
"i",
|
|
83
|
+
);
|
|
84
|
+
return pattern.test(text);
|
|
85
|
+
}
|
|
86
|
+
|
|
51
87
|
/** Return skill names from OpenCode skill directories. */
|
|
52
88
|
export function findSkillNames(dirs: string[] = OPENCODE_SKILLS_DIRS): Set<string> {
|
|
53
89
|
const names = new Set<string>();
|
|
@@ -79,9 +115,12 @@ export interface ParsedSession {
|
|
|
79
115
|
total_tool_calls: number;
|
|
80
116
|
bash_commands: string[];
|
|
81
117
|
skills_triggered: string[];
|
|
118
|
+
skill_detections?: TriggeredSkillDetection[];
|
|
82
119
|
assistant_turns: number;
|
|
83
120
|
errors_encountered: number;
|
|
84
121
|
transcript_chars: number;
|
|
122
|
+
/** True when local session JSON is metadata-only (no embedded messages). */
|
|
123
|
+
is_metadata_only?: boolean;
|
|
85
124
|
}
|
|
86
125
|
|
|
87
126
|
/** Return a human-readable schema summary for --show-schema. */
|
|
@@ -202,10 +241,24 @@ export function readSessionsFromSqlite(
|
|
|
202
241
|
let firstUserQuery = "";
|
|
203
242
|
const toolCalls: Record<string, number> = {};
|
|
204
243
|
const bashCommands: string[] = [];
|
|
205
|
-
const
|
|
244
|
+
const skillDetections = new Map<string, TriggeredSkillDetection>();
|
|
206
245
|
let errors = 0;
|
|
207
246
|
let assistantTurns = 0;
|
|
208
247
|
|
|
248
|
+
const noteSkillDetection = (skillName: string, hasSkillMdRead: boolean): void => {
|
|
249
|
+
const normalizedSkillName = skillName.trim();
|
|
250
|
+
if (!normalizedSkillName) return;
|
|
251
|
+
const existing = skillDetections.get(normalizedSkillName);
|
|
252
|
+
if (existing) {
|
|
253
|
+
existing.has_skill_md_read = existing.has_skill_md_read || hasSkillMdRead;
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
skillDetections.set(normalizedSkillName, {
|
|
257
|
+
skill_name: normalizedSkillName,
|
|
258
|
+
has_skill_md_read: hasSkillMdRead,
|
|
259
|
+
});
|
|
260
|
+
};
|
|
261
|
+
|
|
209
262
|
for (const msg of msgRows) {
|
|
210
263
|
const role = (msg.role as string) ?? "";
|
|
211
264
|
const blocks = normalizeContent(msg.content ?? "[]");
|
|
@@ -251,9 +304,7 @@ export function readSessionsFromSqlite(
|
|
|
251
304
|
const filePath = (inp.file_path as string) ?? (inp.path as string) ?? "";
|
|
252
305
|
if (basename(filePath).toUpperCase() === "SKILL.MD") {
|
|
253
306
|
const skillName = basename(join(filePath, ".."));
|
|
254
|
-
|
|
255
|
-
skillsTriggered.push(skillName);
|
|
256
|
-
}
|
|
307
|
+
noteSkillDetection(skillName, true);
|
|
257
308
|
}
|
|
258
309
|
}
|
|
259
310
|
}
|
|
@@ -271,8 +322,8 @@ export function readSessionsFromSqlite(
|
|
|
271
322
|
// Check text content for skill name mentions
|
|
272
323
|
const textContent = (block.text as string) ?? "";
|
|
273
324
|
for (const skillName of skillNames) {
|
|
274
|
-
if (textContent
|
|
275
|
-
|
|
325
|
+
if (containsWholeSkillMention(textContent, skillName)) {
|
|
326
|
+
noteSkillDetection(skillName, false);
|
|
276
327
|
}
|
|
277
328
|
}
|
|
278
329
|
}
|
|
@@ -299,7 +350,8 @@ export function readSessionsFromSqlite(
|
|
|
299
350
|
tool_calls: toolCalls,
|
|
300
351
|
total_tool_calls: Object.values(toolCalls).reduce((a, b) => a + b, 0),
|
|
301
352
|
bash_commands: bashCommands,
|
|
302
|
-
skills_triggered:
|
|
353
|
+
skills_triggered: [...skillDetections.values()].map((entry) => entry.skill_name),
|
|
354
|
+
skill_detections: [...skillDetections.values()],
|
|
303
355
|
assistant_turns: assistantTurns,
|
|
304
356
|
errors_encountered: errors,
|
|
305
357
|
transcript_chars: 0,
|
|
@@ -349,13 +401,30 @@ export function readSessionsFromJsonFiles(
|
|
|
349
401
|
const timestamp = new Date(created * 1000).toISOString();
|
|
350
402
|
const messages = (data.messages as Array<Record<string, unknown>>) ?? [];
|
|
351
403
|
|
|
404
|
+
// Detect metadata-only session files (no message bodies)
|
|
405
|
+
const isMetadataOnly = messages.length === 0;
|
|
406
|
+
|
|
352
407
|
let firstUserQuery = "";
|
|
353
408
|
const toolCalls: Record<string, number> = {};
|
|
354
409
|
const bashCommands: string[] = [];
|
|
355
|
-
const
|
|
410
|
+
const skillDetections = new Map<string, TriggeredSkillDetection>();
|
|
356
411
|
let errors = 0;
|
|
357
412
|
let turns = 0;
|
|
358
413
|
|
|
414
|
+
const noteSkillDetection = (skillName: string, hasSkillMdRead: boolean): void => {
|
|
415
|
+
const normalizedSkillName = skillName.trim();
|
|
416
|
+
if (!normalizedSkillName) return;
|
|
417
|
+
const existing = skillDetections.get(normalizedSkillName);
|
|
418
|
+
if (existing) {
|
|
419
|
+
existing.has_skill_md_read = existing.has_skill_md_read || hasSkillMdRead;
|
|
420
|
+
return;
|
|
421
|
+
}
|
|
422
|
+
skillDetections.set(normalizedSkillName, {
|
|
423
|
+
skill_name: normalizedSkillName,
|
|
424
|
+
has_skill_md_read: hasSkillMdRead,
|
|
425
|
+
});
|
|
426
|
+
};
|
|
427
|
+
|
|
359
428
|
for (const msg of messages) {
|
|
360
429
|
const role = (msg.role as string) ?? "";
|
|
361
430
|
const blocks = normalizeContent(msg.content ?? []);
|
|
@@ -385,17 +454,15 @@ export function readSessionsFromJsonFiles(
|
|
|
385
454
|
const fp = (inp.file_path as string) ?? "";
|
|
386
455
|
if (basename(fp).toUpperCase() === "SKILL.MD") {
|
|
387
456
|
const sn = basename(join(fp, ".."));
|
|
388
|
-
|
|
389
|
-
skillsTriggered.push(sn);
|
|
390
|
-
}
|
|
457
|
+
noteSkillDetection(sn, true);
|
|
391
458
|
}
|
|
392
459
|
}
|
|
393
460
|
}
|
|
394
461
|
|
|
395
462
|
const text = (block.text as string) ?? "";
|
|
396
463
|
for (const skillName of skillNames) {
|
|
397
|
-
if (text
|
|
398
|
-
|
|
464
|
+
if (containsWholeSkillMention(text, skillName)) {
|
|
465
|
+
noteSkillDetection(skillName, false);
|
|
399
466
|
}
|
|
400
467
|
}
|
|
401
468
|
}
|
|
@@ -422,10 +489,12 @@ export function readSessionsFromJsonFiles(
|
|
|
422
489
|
tool_calls: toolCalls,
|
|
423
490
|
total_tool_calls: Object.values(toolCalls).reduce((a, b) => a + b, 0),
|
|
424
491
|
bash_commands: bashCommands,
|
|
425
|
-
skills_triggered:
|
|
492
|
+
skills_triggered: [...skillDetections.values()].map((entry) => entry.skill_name),
|
|
493
|
+
skill_detections: [...skillDetections.values()],
|
|
426
494
|
assistant_turns: turns,
|
|
427
495
|
errors_encountered: errors,
|
|
428
496
|
transcript_chars: statSync(filePath).size,
|
|
497
|
+
is_metadata_only: isMetadataOnly,
|
|
429
498
|
});
|
|
430
499
|
}
|
|
431
500
|
|
|
@@ -439,6 +508,7 @@ export function writeSession(
|
|
|
439
508
|
queryLogPath: string = QUERY_LOG,
|
|
440
509
|
telemetryLogPath: string = TELEMETRY_LOG,
|
|
441
510
|
skillLogPath: string = SKILL_LOG,
|
|
511
|
+
canonicalLogPath: string = CANONICAL_LOG,
|
|
442
512
|
): void {
|
|
443
513
|
const { query: prompt, session_id: sessionId, skills_triggered: skills } = session;
|
|
444
514
|
|
|
@@ -460,7 +530,21 @@ export function writeSession(
|
|
|
460
530
|
appendJsonl(queryLogPath, queryRecord, "all_queries");
|
|
461
531
|
}
|
|
462
532
|
|
|
463
|
-
const
|
|
533
|
+
const telemetry: SessionTelemetryRecord = {
|
|
534
|
+
timestamp: session.timestamp,
|
|
535
|
+
session_id: session.session_id,
|
|
536
|
+
cwd: session.cwd,
|
|
537
|
+
transcript_path: session.transcript_path,
|
|
538
|
+
tool_calls: session.tool_calls,
|
|
539
|
+
total_tool_calls: session.total_tool_calls,
|
|
540
|
+
bash_commands: session.bash_commands,
|
|
541
|
+
skills_triggered: session.skills_triggered,
|
|
542
|
+
assistant_turns: session.assistant_turns,
|
|
543
|
+
errors_encountered: session.errors_encountered,
|
|
544
|
+
transcript_chars: session.transcript_chars,
|
|
545
|
+
last_user_query: session.last_user_query,
|
|
546
|
+
source: session.source,
|
|
547
|
+
};
|
|
464
548
|
appendJsonl(telemetryLogPath, telemetry, "session_telemetry");
|
|
465
549
|
|
|
466
550
|
for (const skillName of skills) {
|
|
@@ -475,6 +559,98 @@ export function writeSession(
|
|
|
475
559
|
};
|
|
476
560
|
appendJsonl(skillLogPath, skillRecord, "skill_usage");
|
|
477
561
|
}
|
|
562
|
+
|
|
563
|
+
// --- Canonical normalization records (additive) ---
|
|
564
|
+
const canonicalRecords = buildCanonicalRecordsFromOpenCode(session);
|
|
565
|
+
appendCanonicalRecords(canonicalRecords, canonicalLogPath);
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
/** Build canonical records from a parsed OpenCode session. */
|
|
569
|
+
export function buildCanonicalRecordsFromOpenCode(session: ParsedSession): CanonicalRecord[] {
|
|
570
|
+
const records: CanonicalRecord[] = [];
|
|
571
|
+
const sourceKind = session.is_metadata_only ? ("replayed" as const) : ("replayed" as const);
|
|
572
|
+
const baseInput: CanonicalBaseInput = {
|
|
573
|
+
platform: "opencode",
|
|
574
|
+
capture_mode: "batch_ingest",
|
|
575
|
+
source_session_kind: sourceKind,
|
|
576
|
+
session_id: session.session_id,
|
|
577
|
+
raw_source_ref: {
|
|
578
|
+
path: session.transcript_path,
|
|
579
|
+
event_type: session.source,
|
|
580
|
+
metadata: session.is_metadata_only ? { metadata_only: true } : undefined,
|
|
581
|
+
},
|
|
582
|
+
};
|
|
583
|
+
|
|
584
|
+
records.push(
|
|
585
|
+
buildCanonicalSession({
|
|
586
|
+
...baseInput,
|
|
587
|
+
started_at: session.timestamp,
|
|
588
|
+
workspace_path: session.cwd || undefined,
|
|
589
|
+
}),
|
|
590
|
+
);
|
|
591
|
+
|
|
592
|
+
const promptEmitted = Boolean(
|
|
593
|
+
session.query && session.query.length >= 4 && !session.is_metadata_only,
|
|
594
|
+
);
|
|
595
|
+
const promptId = promptEmitted ? derivePromptId(session.session_id, 0) : undefined;
|
|
596
|
+
|
|
597
|
+
if (promptId) {
|
|
598
|
+
records.push(
|
|
599
|
+
buildCanonicalPrompt({
|
|
600
|
+
...baseInput,
|
|
601
|
+
prompt_id: promptId,
|
|
602
|
+
occurred_at: session.timestamp,
|
|
603
|
+
prompt_text: session.query,
|
|
604
|
+
prompt_index: 0,
|
|
605
|
+
}),
|
|
606
|
+
);
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
const skillDetections =
|
|
610
|
+
session.skill_detections ??
|
|
611
|
+
session.skills_triggered.map((skillName) => ({
|
|
612
|
+
skill_name: skillName,
|
|
613
|
+
has_skill_md_read: false,
|
|
614
|
+
}));
|
|
615
|
+
|
|
616
|
+
for (let i = 0; i < skillDetections.length; i++) {
|
|
617
|
+
const detection = skillDetections[i];
|
|
618
|
+
const skillName = detection.skill_name;
|
|
619
|
+
const { invocation_mode, confidence } = deriveInvocationMode({
|
|
620
|
+
has_skill_md_read: detection.has_skill_md_read,
|
|
621
|
+
is_text_mention_only: !detection.has_skill_md_read,
|
|
622
|
+
});
|
|
623
|
+
records.push(
|
|
624
|
+
buildCanonicalSkillInvocation({
|
|
625
|
+
...baseInput,
|
|
626
|
+
skill_invocation_id: deriveSkillInvocationId(session.session_id, skillName, i),
|
|
627
|
+
occurred_at: session.timestamp,
|
|
628
|
+
matched_prompt_id: promptId,
|
|
629
|
+
skill_name: skillName,
|
|
630
|
+
skill_path: `(opencode:${skillName})`,
|
|
631
|
+
invocation_mode,
|
|
632
|
+
triggered: true,
|
|
633
|
+
confidence,
|
|
634
|
+
}),
|
|
635
|
+
);
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
if (!session.is_metadata_only) {
|
|
639
|
+
records.push(
|
|
640
|
+
buildCanonicalExecutionFact({
|
|
641
|
+
...baseInput,
|
|
642
|
+
occurred_at: session.timestamp,
|
|
643
|
+
prompt_id: promptId,
|
|
644
|
+
tool_calls_json: session.tool_calls,
|
|
645
|
+
total_tool_calls: session.total_tool_calls,
|
|
646
|
+
bash_commands_redacted: session.bash_commands,
|
|
647
|
+
assistant_turns: session.assistant_turns,
|
|
648
|
+
errors_encountered: session.errors_encountered,
|
|
649
|
+
}),
|
|
650
|
+
);
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
return records;
|
|
478
654
|
}
|
|
479
655
|
|
|
480
656
|
// --- CLI main ---
|