selftune 0.2.22 → 0.2.23
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 +4 -2
- package/apps/local-dashboard/dist/assets/index-CwOtTrUS.css +1 -0
- package/apps/local-dashboard/dist/assets/index-f1HQpbeH.js +59 -0
- package/apps/local-dashboard/dist/assets/vendor-ui-jVSaIZey.js +12 -0
- package/apps/local-dashboard/dist/index.html +3 -3
- package/cli/selftune/adapters/pi/hook.ts +273 -0
- package/cli/selftune/adapters/pi/install.ts +207 -0
- package/cli/selftune/constants.ts +10 -1
- package/cli/selftune/dashboard-contract.ts +14 -0
- package/cli/selftune/evolution/engines/judge-engine.ts +96 -0
- package/cli/selftune/evolution/engines/replay-engine.ts +158 -0
- package/cli/selftune/evolution/evidence.ts +2 -6
- package/cli/selftune/evolution/evolve-body.ts +73 -20
- package/cli/selftune/evolution/validate-body.ts +78 -42
- package/cli/selftune/evolution/validate-routing.ts +45 -104
- package/cli/selftune/hooks/skill-eval.ts +2 -1
- package/cli/selftune/hooks-shared/types.ts +1 -0
- package/cli/selftune/index.ts +23 -5
- package/cli/selftune/ingestors/pi-ingest.ts +726 -0
- package/cli/selftune/init.ts +11 -1
- package/cli/selftune/localdb/direct-write.ts +85 -0
- package/cli/selftune/localdb/materialize.ts +6 -7
- package/cli/selftune/localdb/queries.ts +126 -0
- package/cli/selftune/localdb/schema.ts +38 -0
- package/cli/selftune/observability.ts +8 -1
- package/cli/selftune/orchestrate.ts +43 -0
- package/cli/selftune/registry/client.ts +74 -0
- package/cli/selftune/registry/history.ts +54 -0
- package/cli/selftune/registry/index.ts +90 -0
- package/cli/selftune/registry/install.ts +141 -0
- package/cli/selftune/registry/list.ts +44 -0
- package/cli/selftune/registry/push.ts +171 -0
- package/cli/selftune/registry/rollback.ts +49 -0
- package/cli/selftune/registry/status.ts +62 -0
- package/cli/selftune/registry/sync.ts +125 -0
- package/cli/selftune/repair/skill-usage.ts +4 -1
- package/cli/selftune/status.ts +31 -0
- package/cli/selftune/sync.ts +127 -23
- package/cli/selftune/types.ts +2 -1
- package/cli/selftune/utils/jsonl.ts +1 -30
- package/cli/selftune/utils/skill-discovery.ts +22 -0
- package/node_modules/@selftune/telemetry-contract/fixtures/evidence-only-push.ts +1 -1
- package/node_modules/@selftune/telemetry-contract/fixtures/golden.test.ts +0 -1
- package/node_modules/@selftune/telemetry-contract/fixtures/partial-push-unresolved-parents.ts +1 -1
- package/node_modules/@selftune/telemetry-contract/package.json +1 -1
- package/node_modules/@selftune/telemetry-contract/src/index.ts +1 -0
- package/node_modules/@selftune/telemetry-contract/src/schemas.ts +22 -4
- package/node_modules/@selftune/telemetry-contract/src/types.ts +1 -12
- package/node_modules/@selftune/telemetry-contract/tests/compatibility.test.ts +0 -1
- package/package.json +1 -1
- package/packages/telemetry-contract/fixtures/evidence-only-push.ts +1 -1
- package/packages/telemetry-contract/fixtures/golden.test.ts +0 -1
- package/packages/telemetry-contract/fixtures/partial-push-unresolved-parents.ts +1 -1
- package/packages/telemetry-contract/package.json +1 -1
- package/packages/telemetry-contract/src/index.ts +1 -0
- package/packages/telemetry-contract/src/schemas.ts +22 -4
- package/packages/telemetry-contract/src/types.ts +1 -12
- package/packages/telemetry-contract/tests/compatibility.test.ts +0 -1
- package/packages/ui/AGENTS.md +16 -0
- package/packages/ui/README.md +1 -1
- package/packages/ui/package.json +1 -1
- package/packages/ui/src/components/ActivityTimeline.tsx +152 -168
- package/packages/ui/src/components/AnalyticsCharts.tsx +344 -0
- package/packages/ui/src/components/EvidenceViewer.tsx +153 -443
- package/packages/ui/src/components/EvolutionTimeline.tsx +34 -87
- package/packages/ui/src/components/InfoTip.tsx +1 -2
- package/packages/ui/src/components/InvocationsPanel.tsx +413 -0
- package/packages/ui/src/components/JobHistoryTimeline.tsx +156 -0
- package/packages/ui/src/components/OrchestrateRunsPanel.tsx +18 -36
- package/packages/ui/src/components/OverviewPanels.tsx +652 -0
- package/packages/ui/src/components/PipelineStatusBar.tsx +65 -0
- package/packages/ui/src/components/SkillReportGuide.tsx +215 -0
- package/packages/ui/src/components/SkillReportPanels.tsx +919 -0
- package/packages/ui/src/components/SkillsLibrary.tsx +437 -0
- package/packages/ui/src/components/index.ts +56 -1
- package/packages/ui/src/components/section-cards.tsx +18 -35
- package/packages/ui/src/components/skill-health-grid.tsx +47 -37
- package/packages/ui/src/lib/constants.tsx +0 -1
- package/packages/ui/src/primitives/card.tsx +1 -1
- package/packages/ui/src/primitives/checkbox.tsx +1 -1
- package/packages/ui/src/primitives/dropdown-menu.tsx +2 -2
- package/packages/ui/src/primitives/select.tsx +2 -2
- package/packages/ui/src/types.ts +172 -4
- package/skill/SKILL.md +18 -4
- package/skill/Workflows/Ingest.md +60 -2
- package/skill/Workflows/Initialize.md +8 -5
- package/skill/Workflows/PlatformHooks.md +19 -3
- package/skill/Workflows/Registry.md +99 -0
- package/skill/Workflows/Sync.md +3 -1
- package/apps/local-dashboard/dist/assets/index-D8O-RG1I.js +0 -60
- package/apps/local-dashboard/dist/assets/index-_EcLywDg.css +0 -1
- package/apps/local-dashboard/dist/assets/vendor-ui-CGEmUayx.js +0 -12
- package/cli/selftune/utils/html.ts +0 -27
- package/packages/ui/src/components/RecentActivityFeed.tsx +0 -117
package/cli/selftune/sync.ts
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* - Codex rollout logs
|
|
8
8
|
* - OpenCode session history
|
|
9
9
|
* - OpenClaw session history
|
|
10
|
+
* - Pi session history
|
|
10
11
|
*
|
|
11
12
|
* After syncing raw session/query/telemetry records, it rebuilds the repaired
|
|
12
13
|
* skill-usage overlay from Claude transcripts and Codex rollouts so monitoring,
|
|
@@ -25,6 +26,8 @@ import {
|
|
|
25
26
|
OPENCLAW_AGENTS_DIR,
|
|
26
27
|
OPENCLAW_INGEST_MARKER,
|
|
27
28
|
OPENCODE_INGEST_MARKER,
|
|
29
|
+
PI_INGEST_MARKER,
|
|
30
|
+
PI_SESSIONS_DIR,
|
|
28
31
|
QUERY_LOG,
|
|
29
32
|
REPAIRED_SKILL_LOG,
|
|
30
33
|
REPAIRED_SKILL_SESSIONS_MARKER,
|
|
@@ -56,7 +59,14 @@ import {
|
|
|
56
59
|
readSessionsFromSqlite,
|
|
57
60
|
writeSession as writeOpenCodeSession,
|
|
58
61
|
} from "./ingestors/opencode-ingest.js";
|
|
62
|
+
import {
|
|
63
|
+
findPiSessions,
|
|
64
|
+
findPiSkillNames,
|
|
65
|
+
parsePiSession,
|
|
66
|
+
writeSession as writePiSession,
|
|
67
|
+
} from "./ingestors/pi-ingest.js";
|
|
59
68
|
import { getDb } from "./localdb/db.js";
|
|
69
|
+
import { writeCronRunToDb } from "./localdb/direct-write.js";
|
|
60
70
|
import { querySkillUsageRecords } from "./localdb/queries.js";
|
|
61
71
|
import {
|
|
62
72
|
persistRepairedSkillUsageToDb,
|
|
@@ -91,6 +101,7 @@ export interface SyncResult {
|
|
|
91
101
|
codex: SyncStepResult;
|
|
92
102
|
opencode: SyncStepResult;
|
|
93
103
|
openclaw: SyncStepResult;
|
|
104
|
+
pi: SyncStepResult;
|
|
94
105
|
};
|
|
95
106
|
repair: {
|
|
96
107
|
ran: boolean;
|
|
@@ -113,6 +124,7 @@ export interface SyncOptions {
|
|
|
113
124
|
codexHome: string;
|
|
114
125
|
opencodeDataDir: string;
|
|
115
126
|
openclawAgentsDir: string;
|
|
127
|
+
piSessionsDir: string;
|
|
116
128
|
skillLogPath: string;
|
|
117
129
|
repairedSkillLogPath: string;
|
|
118
130
|
repairedSessionsPath: string;
|
|
@@ -123,6 +135,7 @@ export interface SyncOptions {
|
|
|
123
135
|
syncCodex: boolean;
|
|
124
136
|
syncOpenCode: boolean;
|
|
125
137
|
syncOpenClaw: boolean;
|
|
138
|
+
syncPi: boolean;
|
|
126
139
|
rebuildSkillUsage: boolean;
|
|
127
140
|
}
|
|
128
141
|
|
|
@@ -133,6 +146,7 @@ export interface SyncDeps {
|
|
|
133
146
|
syncCodex?: (options: SyncOptions) => SyncStepResult;
|
|
134
147
|
syncOpenCode?: (options: SyncOptions) => SyncStepResult;
|
|
135
148
|
syncOpenClaw?: (options: SyncOptions) => SyncStepResult;
|
|
149
|
+
syncPi?: (options: SyncOptions) => SyncStepResult;
|
|
136
150
|
rebuildSkillUsage?: (options: SyncOptions) => {
|
|
137
151
|
repairedSessions: number;
|
|
138
152
|
repairedRecords: number;
|
|
@@ -154,6 +168,7 @@ export function createDefaultSyncOptions(overrides: Partial<SyncOptions> = {}):
|
|
|
154
168
|
codexHome: DEFAULT_CODEX_HOME,
|
|
155
169
|
opencodeDataDir: DEFAULT_OPENCODE_DATA_DIR,
|
|
156
170
|
openclawAgentsDir: OPENCLAW_AGENTS_DIR,
|
|
171
|
+
piSessionsDir: PI_SESSIONS_DIR,
|
|
157
172
|
skillLogPath: SKILL_LOG,
|
|
158
173
|
repairedSkillLogPath: REPAIRED_SKILL_LOG,
|
|
159
174
|
repairedSessionsPath: REPAIRED_SKILL_SESSIONS_MARKER,
|
|
@@ -163,6 +178,7 @@ export function createDefaultSyncOptions(overrides: Partial<SyncOptions> = {}):
|
|
|
163
178
|
syncCodex: true,
|
|
164
179
|
syncOpenCode: true,
|
|
165
180
|
syncOpenClaw: true,
|
|
181
|
+
syncPi: true,
|
|
166
182
|
rebuildSkillUsage: true,
|
|
167
183
|
...overrides,
|
|
168
184
|
};
|
|
@@ -356,6 +372,45 @@ function syncOpenClawSource(
|
|
|
356
372
|
};
|
|
357
373
|
}
|
|
358
374
|
|
|
375
|
+
function syncPiSource(options: SyncOptions, onProgress?: SyncProgressCallback): SyncStepResult {
|
|
376
|
+
if (!existsSync(options.piSessionsDir)) {
|
|
377
|
+
return { available: false, scanned: 0, synced: 0, skipped: 0 };
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
onProgress?.("scanning Pi sessions...");
|
|
381
|
+
const sinceTs = options.since ? options.since.getTime() : null;
|
|
382
|
+
const allSessions = findPiSessions(options.piSessionsDir, sinceTs);
|
|
383
|
+
const skillNames = findPiSkillNames();
|
|
384
|
+
const alreadyIngested = options.force ? new Set<string>() : loadMarker(PI_INGEST_MARKER);
|
|
385
|
+
const pending = allSessions.filter((session) => !alreadyIngested.has(session.sessionId));
|
|
386
|
+
onProgress?.(`found ${allSessions.length} sessions, ${pending.length} pending`);
|
|
387
|
+
const newIngested = new Set<string>();
|
|
388
|
+
let synced = 0;
|
|
389
|
+
let skipped = 0;
|
|
390
|
+
|
|
391
|
+
for (const sessionFile of pending) {
|
|
392
|
+
const session = parsePiSession(sessionFile.filePath, skillNames);
|
|
393
|
+
if (!session.session_id || !session.timestamp) {
|
|
394
|
+
skipped += 1;
|
|
395
|
+
continue;
|
|
396
|
+
}
|
|
397
|
+
writePiSession(session, options.dryRun);
|
|
398
|
+
newIngested.add(sessionFile.sessionId);
|
|
399
|
+
synced += 1;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
if (!options.dryRun && newIngested.size > 0) {
|
|
403
|
+
saveMarker(PI_INGEST_MARKER, new Set([...alreadyIngested, ...newIngested]));
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
return {
|
|
407
|
+
available: true,
|
|
408
|
+
scanned: allSessions.length,
|
|
409
|
+
synced,
|
|
410
|
+
skipped,
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
|
|
359
414
|
function rebuildSkillUsageOverlay(
|
|
360
415
|
options: SyncOptions,
|
|
361
416
|
onProgress?: SyncProgressCallback,
|
|
@@ -445,6 +500,7 @@ export function syncSources(
|
|
|
445
500
|
const runCodex = deps.syncCodex;
|
|
446
501
|
const runOpenCode = deps.syncOpenCode;
|
|
447
502
|
const runOpenClaw = deps.syncOpenClaw;
|
|
503
|
+
const runPi = deps.syncPi;
|
|
448
504
|
const runRepair = deps.rebuildSkillUsage;
|
|
449
505
|
const runCreatorContributions = deps.stageCreatorContributions;
|
|
450
506
|
const db = getDb();
|
|
@@ -485,6 +541,10 @@ export function syncSources(
|
|
|
485
541
|
)
|
|
486
542
|
: disabledStep;
|
|
487
543
|
|
|
544
|
+
const pi = options.syncPi
|
|
545
|
+
? timePhase("pi", () => (runPi ? runPi(options) : syncPiSource(options, onProgress)), timings)
|
|
546
|
+
: disabledStep;
|
|
547
|
+
|
|
488
548
|
const repair = options.rebuildSkillUsage
|
|
489
549
|
? timePhase(
|
|
490
550
|
"repair",
|
|
@@ -512,10 +572,10 @@ export function syncSources(
|
|
|
512
572
|
|
|
513
573
|
const totalElapsed = Math.round(performance.now() - totalStart);
|
|
514
574
|
|
|
515
|
-
|
|
575
|
+
const syncResult: SyncResult = {
|
|
516
576
|
since: options.since ? options.since.toISOString() : null,
|
|
517
577
|
dry_run: options.dryRun,
|
|
518
|
-
sources: { claude, codex, opencode, openclaw },
|
|
578
|
+
sources: { claude, codex, opencode, openclaw, pi },
|
|
519
579
|
repair: {
|
|
520
580
|
ran: options.rebuildSkillUsage,
|
|
521
581
|
repaired_sessions: repair.repairedSessions,
|
|
@@ -526,6 +586,8 @@ export function syncSources(
|
|
|
526
586
|
timings,
|
|
527
587
|
total_elapsed_ms: totalElapsed,
|
|
528
588
|
};
|
|
589
|
+
|
|
590
|
+
return syncResult;
|
|
529
591
|
}
|
|
530
592
|
|
|
531
593
|
function formatMs(ms: number): string {
|
|
@@ -549,6 +611,7 @@ export async function cliMain(): Promise<void> {
|
|
|
549
611
|
"codex-home": { type: "string", default: DEFAULT_CODEX_HOME },
|
|
550
612
|
"opencode-data-dir": { type: "string", default: DEFAULT_OPENCODE_DATA_DIR },
|
|
551
613
|
"openclaw-agents-dir": { type: "string", default: OPENCLAW_AGENTS_DIR },
|
|
614
|
+
"pi-sessions-dir": { type: "string", default: PI_SESSIONS_DIR },
|
|
552
615
|
"skill-log": { type: "string", default: SKILL_LOG },
|
|
553
616
|
"repaired-skill-log": { type: "string", default: REPAIRED_SKILL_LOG },
|
|
554
617
|
"repaired-sessions-marker": { type: "string", default: REPAIRED_SKILL_SESSIONS_MARKER },
|
|
@@ -559,6 +622,7 @@ export async function cliMain(): Promise<void> {
|
|
|
559
622
|
"no-codex": { type: "boolean", default: false },
|
|
560
623
|
"no-opencode": { type: "boolean", default: false },
|
|
561
624
|
"no-openclaw": { type: "boolean", default: false },
|
|
625
|
+
"no-pi": { type: "boolean", default: false },
|
|
562
626
|
"no-repair": { type: "boolean", default: false },
|
|
563
627
|
json: { type: "boolean", default: false },
|
|
564
628
|
help: { type: "boolean", short: "h", default: false },
|
|
@@ -577,6 +641,7 @@ Options:
|
|
|
577
641
|
--codex-home <dir> Codex home directory (default: ~/.codex)
|
|
578
642
|
--opencode-data-dir <dir> OpenCode data directory
|
|
579
643
|
--openclaw-agents-dir <dir> OpenClaw agents directory
|
|
644
|
+
--pi-sessions-dir <dir> Pi sessions directory
|
|
580
645
|
--skill-log <path> Raw skill usage log path
|
|
581
646
|
--repaired-skill-log <path> Repaired overlay log path
|
|
582
647
|
--repaired-sessions-marker <p> Repaired session marker path
|
|
@@ -587,6 +652,7 @@ Options:
|
|
|
587
652
|
--no-codex Skip Codex rollout ingest
|
|
588
653
|
--no-opencode Skip OpenCode ingest
|
|
589
654
|
--no-openclaw Skip OpenClaw ingest
|
|
655
|
+
--no-pi Skip Pi ingest
|
|
590
656
|
--no-repair Skip rebuilt skill-usage overlay
|
|
591
657
|
--json Output raw JSON instead of human-readable summary
|
|
592
658
|
-h, --help Show this help`);
|
|
@@ -622,27 +688,64 @@ Options:
|
|
|
622
688
|
process.stderr.write(`selftune sync${flags.length ? ` ${flags.join(" ")}` : ""}\n`);
|
|
623
689
|
}
|
|
624
690
|
|
|
625
|
-
const
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
691
|
+
const syncStartedAt = new Date();
|
|
692
|
+
const syncStart = performance.now();
|
|
693
|
+
let result: SyncResult;
|
|
694
|
+
try {
|
|
695
|
+
result = syncSources(
|
|
696
|
+
createDefaultSyncOptions({
|
|
697
|
+
projectsDir: values["projects-dir"] ?? CLAUDE_CODE_PROJECTS_DIR,
|
|
698
|
+
codexHome: values["codex-home"] ?? DEFAULT_CODEX_HOME,
|
|
699
|
+
opencodeDataDir: values["opencode-data-dir"] ?? DEFAULT_OPENCODE_DATA_DIR,
|
|
700
|
+
openclawAgentsDir: values["openclaw-agents-dir"] ?? OPENCLAW_AGENTS_DIR,
|
|
701
|
+
piSessionsDir: values["pi-sessions-dir"] ?? PI_SESSIONS_DIR,
|
|
702
|
+
skillLogPath: values["skill-log"] ?? SKILL_LOG,
|
|
703
|
+
repairedSkillLogPath: values["repaired-skill-log"] ?? REPAIRED_SKILL_LOG,
|
|
704
|
+
repairedSessionsPath: values["repaired-sessions-marker"] ?? REPAIRED_SKILL_SESSIONS_MARKER,
|
|
705
|
+
since,
|
|
706
|
+
dryRun: values["dry-run"] ?? false,
|
|
707
|
+
force: values.force ?? false,
|
|
708
|
+
syncClaude: !(values["no-claude"] ?? false),
|
|
709
|
+
syncCodex: !(values["no-codex"] ?? false),
|
|
710
|
+
syncOpenCode: !(values["no-opencode"] ?? false),
|
|
711
|
+
syncOpenClaw: !(values["no-openclaw"] ?? false),
|
|
712
|
+
syncPi: !(values["no-pi"] ?? false),
|
|
713
|
+
rebuildSkillUsage: !(values["no-repair"] ?? false),
|
|
714
|
+
}),
|
|
715
|
+
{},
|
|
716
|
+
onProgress,
|
|
717
|
+
);
|
|
718
|
+
} catch (err) {
|
|
719
|
+
const syncElapsed = Math.round(performance.now() - syncStart);
|
|
720
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
721
|
+
writeCronRunToDb(getDb(), {
|
|
722
|
+
jobName: "sync",
|
|
723
|
+
startedAt: syncStartedAt.toISOString(),
|
|
724
|
+
elapsedMs: syncElapsed,
|
|
725
|
+
status: "error",
|
|
726
|
+
error: message,
|
|
727
|
+
});
|
|
728
|
+
throw err;
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
// Log successful sync run to unified cron_runs timeline
|
|
732
|
+
const syncElapsed = Math.round(performance.now() - syncStart);
|
|
733
|
+
const s = result.sources;
|
|
734
|
+
writeCronRunToDb(getDb(), {
|
|
735
|
+
jobName: "sync",
|
|
736
|
+
startedAt: syncStartedAt.toISOString(),
|
|
737
|
+
elapsedMs: syncElapsed,
|
|
738
|
+
status: "success",
|
|
739
|
+
metrics: {
|
|
740
|
+
total_synced:
|
|
741
|
+
s.claude.synced + s.codex.synced + s.opencode.synced + s.openclaw.synced + s.pi.synced,
|
|
742
|
+
claude_synced: s.claude.synced,
|
|
743
|
+
codex_synced: s.codex.synced,
|
|
744
|
+
opencode_synced: s.opencode.synced,
|
|
745
|
+
openclaw_synced: s.openclaw.synced,
|
|
746
|
+
pi_synced: s.pi.synced,
|
|
747
|
+
},
|
|
748
|
+
});
|
|
646
749
|
|
|
647
750
|
if (jsonOutput) {
|
|
648
751
|
console.log(JSON.stringify(result, null, 2));
|
|
@@ -662,6 +765,7 @@ Options:
|
|
|
662
765
|
process.stderr.write(
|
|
663
766
|
`${formatStepLine("OpenClaw", result.sources.openclaw, timingMap.get("openclaw"))}\n`,
|
|
664
767
|
);
|
|
768
|
+
process.stderr.write(`${formatStepLine("Pi", result.sources.pi, timingMap.get("pi"))}\n`);
|
|
665
769
|
|
|
666
770
|
if (result.repair.ran) {
|
|
667
771
|
const repairTiming = timingMap.get("repair");
|
package/cli/selftune/types.ts
CHANGED
|
@@ -34,7 +34,7 @@ export type AlphaLinkState =
|
|
|
34
34
|
| "ready";
|
|
35
35
|
|
|
36
36
|
export interface SelftuneConfig {
|
|
37
|
-
agent_type: "claude_code" | "codex" | "opencode" | "openclaw" | "unknown";
|
|
37
|
+
agent_type: "claude_code" | "codex" | "opencode" | "openclaw" | "pi" | "unknown";
|
|
38
38
|
cli_path: string;
|
|
39
39
|
llm_mode: "agent";
|
|
40
40
|
agent_cli: string | null;
|
|
@@ -738,6 +738,7 @@ export interface BodyValidationResult {
|
|
|
738
738
|
before_pass_rate?: number;
|
|
739
739
|
after_pass_rate?: number;
|
|
740
740
|
per_entry_results?: RoutingReplayEntryResult[];
|
|
741
|
+
before_entry_results?: RoutingReplayEntryResult[];
|
|
741
742
|
}
|
|
742
743
|
|
|
743
744
|
/** Configuration for which LLM model a role should use. */
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* JSONL read
|
|
2
|
+
* JSONL read utilities and marker file helpers.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import {
|
|
6
|
-
appendFileSync,
|
|
7
6
|
closeSync,
|
|
8
7
|
existsSync,
|
|
9
8
|
fstatSync,
|
|
@@ -15,10 +14,6 @@ import {
|
|
|
15
14
|
} from "node:fs";
|
|
16
15
|
import { dirname } from "node:path";
|
|
17
16
|
|
|
18
|
-
import { createLogger } from "./logging.js";
|
|
19
|
-
import type { LogType } from "./schema-validator.js";
|
|
20
|
-
import { validateRecord } from "./schema-validator.js";
|
|
21
|
-
|
|
22
17
|
/**
|
|
23
18
|
* Read a JSONL file and return parsed records.
|
|
24
19
|
* Skips blank lines and lines that fail to parse.
|
|
@@ -86,30 +81,6 @@ export function readJsonlFrom<T = Record<string, unknown>>(
|
|
|
86
81
|
}
|
|
87
82
|
}
|
|
88
83
|
|
|
89
|
-
/**
|
|
90
|
-
* Append a single record to a JSONL file. Creates parent directories if needed.
|
|
91
|
-
* When logType is provided, validates the record and logs warnings on failure
|
|
92
|
-
* but still writes the record (fail-open: hooks must never block).
|
|
93
|
-
*
|
|
94
|
-
* @deprecated Phase 3: JSONL writes removed. Retained for materializer/test utilities only.
|
|
95
|
-
*/
|
|
96
|
-
export function appendJsonl(path: string, record: unknown, logType?: LogType): void {
|
|
97
|
-
if (logType) {
|
|
98
|
-
const result = validateRecord(record, logType);
|
|
99
|
-
if (!result.valid) {
|
|
100
|
-
const logger = createLogger("jsonl");
|
|
101
|
-
for (const error of result.errors) {
|
|
102
|
-
logger.warn(`Validation warning for ${logType}: ${error}`);
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
const dir = dirname(path);
|
|
107
|
-
if (!existsSync(dir)) {
|
|
108
|
-
mkdirSync(dir, { recursive: true });
|
|
109
|
-
}
|
|
110
|
-
appendFileSync(path, `${JSON.stringify(record)}\n`, "utf-8");
|
|
111
|
-
}
|
|
112
|
-
|
|
113
84
|
/**
|
|
114
85
|
* Load a marker file (JSON array of strings) for idempotent ingestion.
|
|
115
86
|
*/
|
|
@@ -263,6 +263,28 @@ export function classifySkillPath(
|
|
|
263
263
|
return { skill_scope: "unknown" };
|
|
264
264
|
}
|
|
265
265
|
|
|
266
|
+
const TEST_PATH_SEGMENTS = [
|
|
267
|
+
"/tests/",
|
|
268
|
+
"/__tests__/",
|
|
269
|
+
"/test/",
|
|
270
|
+
"/fixtures/",
|
|
271
|
+
"/sandbox/",
|
|
272
|
+
"/test-data/",
|
|
273
|
+
"/testdata/",
|
|
274
|
+
"/mock/",
|
|
275
|
+
"/mocks/",
|
|
276
|
+
];
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Check if a skill path is inside a test/fixture directory.
|
|
280
|
+
* Used to prevent test fixture skills from leaking into production data.
|
|
281
|
+
*/
|
|
282
|
+
export function isTestFixturePath(skillPath: string): boolean {
|
|
283
|
+
if (!skillPath) return false;
|
|
284
|
+
const normalized = skillPath.toLowerCase();
|
|
285
|
+
return TEST_PATH_SEGMENTS.some((seg) => normalized.includes(seg));
|
|
286
|
+
}
|
|
287
|
+
|
|
266
288
|
export function extractSkillNamesFromInstructions(
|
|
267
289
|
text: string,
|
|
268
290
|
knownSkillNames?: Iterable<string>,
|
|
@@ -7,7 +7,7 @@ import type { PushPayloadV2 } from "../src/schemas.js";
|
|
|
7
7
|
export const evidenceOnlyPush: PushPayloadV2 = {
|
|
8
8
|
schema_version: "2.0",
|
|
9
9
|
client_version: "0.9.0",
|
|
10
|
-
push_id: "d4e5f6a7-b8c9-
|
|
10
|
+
push_id: "d4e5f6a7-b8c9-8123-9efa-234567890123",
|
|
11
11
|
normalizer_version: "0.2.1",
|
|
12
12
|
canonical: {
|
|
13
13
|
sessions: [],
|
package/node_modules/@selftune/telemetry-contract/fixtures/partial-push-unresolved-parents.ts
CHANGED
|
@@ -10,7 +10,7 @@ import type { PushPayloadV2 } from "../src/schemas.js";
|
|
|
10
10
|
export const partialPushUnresolvedParents: PushPayloadV2 = {
|
|
11
11
|
schema_version: "2.0",
|
|
12
12
|
client_version: "0.9.0",
|
|
13
|
-
push_id: "c3d4e5f6-a7b8-
|
|
13
|
+
push_id: "c3d4e5f6-a7b8-8012-8def-123456789012",
|
|
14
14
|
normalizer_version: "0.2.1",
|
|
15
15
|
canonical: {
|
|
16
16
|
sessions: [],
|
|
@@ -13,9 +13,9 @@
|
|
|
13
13
|
"type": "module",
|
|
14
14
|
"exports": {
|
|
15
15
|
".": "./index.ts",
|
|
16
|
-
"./schemas": "./src/schemas.ts",
|
|
17
16
|
"./types": "./src/types.ts",
|
|
18
17
|
"./validators": "./src/validators.ts",
|
|
18
|
+
"./schemas": "./src/schemas.ts",
|
|
19
19
|
"./fixtures": "./fixtures/index.ts"
|
|
20
20
|
},
|
|
21
21
|
"dependencies": {
|
|
@@ -1,5 +1,13 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Zod validation schemas for all canonical telemetry record types
|
|
3
|
+
* and the PushPayloadV2 envelope.
|
|
4
|
+
*
|
|
5
|
+
* This is the single source of truth -- cloud consumers should import
|
|
6
|
+
* from @selftune/telemetry-contract/schemas instead of maintaining
|
|
7
|
+
* their own copies.
|
|
8
|
+
*/
|
|
2
9
|
|
|
10
|
+
import { z } from "zod";
|
|
3
11
|
import {
|
|
4
12
|
CANONICAL_CAPTURE_MODES,
|
|
5
13
|
CANONICAL_COMPLETION_STATUSES,
|
|
@@ -11,6 +19,8 @@ import {
|
|
|
11
19
|
CANONICAL_SOURCE_SESSION_KINDS,
|
|
12
20
|
} from "./types.js";
|
|
13
21
|
|
|
22
|
+
// ---------- Shared enum schemas ----------
|
|
23
|
+
|
|
14
24
|
export const canonicalPlatformSchema = z.enum(CANONICAL_PLATFORMS);
|
|
15
25
|
export const captureModeSchema = z.enum(CANONICAL_CAPTURE_MODES);
|
|
16
26
|
export const sourceSessionKindSchema = z.enum(CANONICAL_SOURCE_SESSION_KINDS);
|
|
@@ -19,6 +29,8 @@ export const invocationModeSchema = z.enum(CANONICAL_INVOCATION_MODES);
|
|
|
19
29
|
export const completionStatusSchema = z.enum(CANONICAL_COMPLETION_STATUSES);
|
|
20
30
|
export const recordKindSchema = z.enum(CANONICAL_RECORD_KINDS);
|
|
21
31
|
|
|
32
|
+
// ---------- Shared structural schemas ----------
|
|
33
|
+
|
|
22
34
|
export const rawSourceRefSchema = z.object({
|
|
23
35
|
path: z.string().optional(),
|
|
24
36
|
line: z.number().int().nonnegative().optional(),
|
|
@@ -42,6 +54,8 @@ export const canonicalSessionRecordBaseSchema = canonicalRecordBaseSchema.extend
|
|
|
42
54
|
session_id: z.string().min(1),
|
|
43
55
|
});
|
|
44
56
|
|
|
57
|
+
// ---------- Canonical record schemas ----------
|
|
58
|
+
|
|
45
59
|
export const CanonicalSessionRecordSchema = canonicalSessionRecordBaseSchema.extend({
|
|
46
60
|
record_kind: z.literal("session"),
|
|
47
61
|
external_session_id: z.string().optional(),
|
|
@@ -137,6 +151,8 @@ export const CanonicalEvolutionEvidenceRecordSchema = z.object({
|
|
|
137
151
|
raw_source_ref: rawSourceRefSchema.optional(),
|
|
138
152
|
});
|
|
139
153
|
|
|
154
|
+
// ---------- Orchestrate run schemas ----------
|
|
155
|
+
|
|
140
156
|
export const OrchestrateRunSkillActionSchema = z.object({
|
|
141
157
|
skill: z.string().min(1),
|
|
142
158
|
action: z.enum(["evolve", "watch", "skip"]),
|
|
@@ -163,12 +179,12 @@ export const PushOrchestrateRunRecordSchema = z.object({
|
|
|
163
179
|
skill_actions: z.array(OrchestrateRunSkillActionSchema),
|
|
164
180
|
});
|
|
165
181
|
|
|
182
|
+
// ---------- Push V2 envelope ----------
|
|
183
|
+
|
|
166
184
|
export const PushPayloadV2Schema = z.object({
|
|
167
185
|
schema_version: z.literal("2.0"),
|
|
168
186
|
client_version: z.string().min(1),
|
|
169
|
-
|
|
170
|
-
// requires a stable non-empty idempotency key.
|
|
171
|
-
push_id: z.string().min(1),
|
|
187
|
+
push_id: z.string().uuid(),
|
|
172
188
|
normalizer_version: z.string().min(1),
|
|
173
189
|
canonical: z.object({
|
|
174
190
|
sessions: z.array(CanonicalSessionRecordSchema).min(0),
|
|
@@ -181,6 +197,8 @@ export const PushPayloadV2Schema = z.object({
|
|
|
181
197
|
}),
|
|
182
198
|
});
|
|
183
199
|
|
|
200
|
+
// ---------- Inferred types from Zod schemas ----------
|
|
201
|
+
|
|
184
202
|
export type PushPayloadV2 = z.infer<typeof PushPayloadV2Schema>;
|
|
185
203
|
export type ZodCanonicalSessionRecord = z.infer<typeof CanonicalSessionRecordSchema>;
|
|
186
204
|
export type ZodCanonicalPromptRecord = z.infer<typeof CanonicalPromptRecordSchema>;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export const CANONICAL_SCHEMA_VERSION = "2.0" as const;
|
|
2
2
|
export type CanonicalSchemaVersion = typeof CANONICAL_SCHEMA_VERSION;
|
|
3
3
|
|
|
4
|
-
export const CANONICAL_PLATFORMS = ["claude_code", "codex", "opencode", "openclaw"] as const;
|
|
4
|
+
export const CANONICAL_PLATFORMS = ["claude_code", "codex", "opencode", "openclaw", "pi"] as const;
|
|
5
5
|
export type CanonicalPlatform = (typeof CANONICAL_PLATFORMS)[number];
|
|
6
6
|
|
|
7
7
|
export const CANONICAL_CAPTURE_MODES = [
|
|
@@ -143,18 +143,7 @@ export interface CanonicalExecutionFactRecord extends CanonicalSessionRecordBase
|
|
|
143
143
|
errors_encountered: number;
|
|
144
144
|
input_tokens?: number;
|
|
145
145
|
output_tokens?: number;
|
|
146
|
-
cached_input_tokens?: number;
|
|
147
|
-
reasoning_output_tokens?: number;
|
|
148
|
-
cost_usd?: number;
|
|
149
146
|
duration_ms?: number;
|
|
150
|
-
files_changed?: number;
|
|
151
|
-
lines_added?: number;
|
|
152
|
-
lines_removed?: number;
|
|
153
|
-
lines_modified?: number;
|
|
154
|
-
/** Count of output-producing tool calls (Write, Edit, WebFetch, WebSearch, Skill, Agent). */
|
|
155
|
-
artifact_count?: number;
|
|
156
|
-
/** Inferred session type based on tool distribution. */
|
|
157
|
-
session_type?: "dev" | "research" | "content" | "mixed";
|
|
158
147
|
completion_status?: CanonicalCompletionStatus;
|
|
159
148
|
end_reason?: string;
|
|
160
149
|
}
|
package/package.json
CHANGED
|
@@ -7,7 +7,7 @@ import type { PushPayloadV2 } from "../src/schemas.js";
|
|
|
7
7
|
export const evidenceOnlyPush: PushPayloadV2 = {
|
|
8
8
|
schema_version: "2.0",
|
|
9
9
|
client_version: "0.9.0",
|
|
10
|
-
push_id: "d4e5f6a7-b8c9-
|
|
10
|
+
push_id: "d4e5f6a7-b8c9-8123-9efa-234567890123",
|
|
11
11
|
normalizer_version: "0.2.1",
|
|
12
12
|
canonical: {
|
|
13
13
|
sessions: [],
|
|
@@ -10,7 +10,7 @@ import type { PushPayloadV2 } from "../src/schemas.js";
|
|
|
10
10
|
export const partialPushUnresolvedParents: PushPayloadV2 = {
|
|
11
11
|
schema_version: "2.0",
|
|
12
12
|
client_version: "0.9.0",
|
|
13
|
-
push_id: "c3d4e5f6-a7b8-
|
|
13
|
+
push_id: "c3d4e5f6-a7b8-8012-8def-123456789012",
|
|
14
14
|
normalizer_version: "0.2.1",
|
|
15
15
|
canonical: {
|
|
16
16
|
sessions: [],
|
|
@@ -13,9 +13,9 @@
|
|
|
13
13
|
"type": "module",
|
|
14
14
|
"exports": {
|
|
15
15
|
".": "./index.ts",
|
|
16
|
-
"./schemas": "./src/schemas.ts",
|
|
17
16
|
"./types": "./src/types.ts",
|
|
18
17
|
"./validators": "./src/validators.ts",
|
|
18
|
+
"./schemas": "./src/schemas.ts",
|
|
19
19
|
"./fixtures": "./fixtures/index.ts"
|
|
20
20
|
},
|
|
21
21
|
"dependencies": {
|
|
@@ -1,5 +1,13 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Zod validation schemas for all canonical telemetry record types
|
|
3
|
+
* and the PushPayloadV2 envelope.
|
|
4
|
+
*
|
|
5
|
+
* This is the single source of truth -- cloud consumers should import
|
|
6
|
+
* from @selftune/telemetry-contract/schemas instead of maintaining
|
|
7
|
+
* their own copies.
|
|
8
|
+
*/
|
|
2
9
|
|
|
10
|
+
import { z } from "zod";
|
|
3
11
|
import {
|
|
4
12
|
CANONICAL_CAPTURE_MODES,
|
|
5
13
|
CANONICAL_COMPLETION_STATUSES,
|
|
@@ -11,6 +19,8 @@ import {
|
|
|
11
19
|
CANONICAL_SOURCE_SESSION_KINDS,
|
|
12
20
|
} from "./types.js";
|
|
13
21
|
|
|
22
|
+
// ---------- Shared enum schemas ----------
|
|
23
|
+
|
|
14
24
|
export const canonicalPlatformSchema = z.enum(CANONICAL_PLATFORMS);
|
|
15
25
|
export const captureModeSchema = z.enum(CANONICAL_CAPTURE_MODES);
|
|
16
26
|
export const sourceSessionKindSchema = z.enum(CANONICAL_SOURCE_SESSION_KINDS);
|
|
@@ -19,6 +29,8 @@ export const invocationModeSchema = z.enum(CANONICAL_INVOCATION_MODES);
|
|
|
19
29
|
export const completionStatusSchema = z.enum(CANONICAL_COMPLETION_STATUSES);
|
|
20
30
|
export const recordKindSchema = z.enum(CANONICAL_RECORD_KINDS);
|
|
21
31
|
|
|
32
|
+
// ---------- Shared structural schemas ----------
|
|
33
|
+
|
|
22
34
|
export const rawSourceRefSchema = z.object({
|
|
23
35
|
path: z.string().optional(),
|
|
24
36
|
line: z.number().int().nonnegative().optional(),
|
|
@@ -42,6 +54,8 @@ export const canonicalSessionRecordBaseSchema = canonicalRecordBaseSchema.extend
|
|
|
42
54
|
session_id: z.string().min(1),
|
|
43
55
|
});
|
|
44
56
|
|
|
57
|
+
// ---------- Canonical record schemas ----------
|
|
58
|
+
|
|
45
59
|
export const CanonicalSessionRecordSchema = canonicalSessionRecordBaseSchema.extend({
|
|
46
60
|
record_kind: z.literal("session"),
|
|
47
61
|
external_session_id: z.string().optional(),
|
|
@@ -137,6 +151,8 @@ export const CanonicalEvolutionEvidenceRecordSchema = z.object({
|
|
|
137
151
|
raw_source_ref: rawSourceRefSchema.optional(),
|
|
138
152
|
});
|
|
139
153
|
|
|
154
|
+
// ---------- Orchestrate run schemas ----------
|
|
155
|
+
|
|
140
156
|
export const OrchestrateRunSkillActionSchema = z.object({
|
|
141
157
|
skill: z.string().min(1),
|
|
142
158
|
action: z.enum(["evolve", "watch", "skip"]),
|
|
@@ -163,12 +179,12 @@ export const PushOrchestrateRunRecordSchema = z.object({
|
|
|
163
179
|
skill_actions: z.array(OrchestrateRunSkillActionSchema),
|
|
164
180
|
});
|
|
165
181
|
|
|
182
|
+
// ---------- Push V2 envelope ----------
|
|
183
|
+
|
|
166
184
|
export const PushPayloadV2Schema = z.object({
|
|
167
185
|
schema_version: z.literal("2.0"),
|
|
168
186
|
client_version: z.string().min(1),
|
|
169
|
-
|
|
170
|
-
// requires a stable non-empty idempotency key.
|
|
171
|
-
push_id: z.string().min(1),
|
|
187
|
+
push_id: z.string().uuid(),
|
|
172
188
|
normalizer_version: z.string().min(1),
|
|
173
189
|
canonical: z.object({
|
|
174
190
|
sessions: z.array(CanonicalSessionRecordSchema).min(0),
|
|
@@ -181,6 +197,8 @@ export const PushPayloadV2Schema = z.object({
|
|
|
181
197
|
}),
|
|
182
198
|
});
|
|
183
199
|
|
|
200
|
+
// ---------- Inferred types from Zod schemas ----------
|
|
201
|
+
|
|
184
202
|
export type PushPayloadV2 = z.infer<typeof PushPayloadV2Schema>;
|
|
185
203
|
export type ZodCanonicalSessionRecord = z.infer<typeof CanonicalSessionRecordSchema>;
|
|
186
204
|
export type ZodCanonicalPromptRecord = z.infer<typeof CanonicalPromptRecordSchema>;
|