selftune 0.2.18 → 0.2.20
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 +9 -4
- package/apps/local-dashboard/dist/assets/index-D8O-RG1I.js +60 -0
- package/apps/local-dashboard/dist/assets/index-_EcLywDg.css +1 -0
- package/apps/local-dashboard/dist/assets/vendor-table-BIiI3YhS.js +1 -0
- package/apps/local-dashboard/dist/assets/vendor-ui-CGEmUayx.js +12 -0
- package/apps/local-dashboard/dist/index.html +5 -5
- package/cli/selftune/alpha-upload/stage-canonical.ts +7 -6
- package/cli/selftune/constants.ts +10 -0
- package/cli/selftune/contribute/contribute.ts +30 -2
- package/cli/selftune/contribution-config.ts +249 -0
- package/cli/selftune/contribution-relay.ts +177 -0
- package/cli/selftune/contribution-signals.ts +219 -0
- package/cli/selftune/contribution-staging.ts +147 -0
- package/cli/selftune/contributions.ts +532 -0
- package/cli/selftune/creator-contributions.ts +333 -0
- package/cli/selftune/dashboard-contract.ts +209 -1
- package/cli/selftune/dashboard-server.ts +45 -11
- package/cli/selftune/eval/family-overlap.ts +714 -0
- package/cli/selftune/eval/hooks-to-evals.ts +182 -28
- package/cli/selftune/eval/synthetic-evals.ts +298 -11
- package/cli/selftune/evolution/evidence.ts +5 -0
- package/cli/selftune/evolution/evolve-body.ts +62 -2
- package/cli/selftune/evolution/evolve.ts +58 -1
- package/cli/selftune/evolution/validate-body.ts +10 -0
- package/cli/selftune/evolution/validate-host-replay.ts +236 -0
- package/cli/selftune/evolution/validate-proposal.ts +10 -0
- package/cli/selftune/evolution/validate-routing.ts +112 -5
- package/cli/selftune/export.ts +2 -2
- package/cli/selftune/index.ts +41 -5
- package/cli/selftune/ingestors/codex-rollout.ts +31 -35
- package/cli/selftune/ingestors/codex-wrapper.ts +32 -24
- package/cli/selftune/localdb/db.ts +2 -2
- package/cli/selftune/localdb/direct-write.ts +8 -3
- package/cli/selftune/localdb/materialize.ts +7 -2
- package/cli/selftune/localdb/queries.ts +712 -31
- package/cli/selftune/localdb/schema.ts +30 -1
- package/cli/selftune/recover.ts +153 -0
- package/cli/selftune/repair/skill-usage.ts +363 -4
- package/cli/selftune/routes/actions.ts +35 -1
- package/cli/selftune/routes/analytics.ts +14 -0
- package/cli/selftune/routes/index.ts +1 -0
- package/cli/selftune/routes/overview.ts +112 -4
- package/cli/selftune/routes/skill-report.ts +575 -11
- package/cli/selftune/status.ts +81 -2
- package/cli/selftune/sync.ts +56 -2
- package/cli/selftune/trust-model.ts +66 -0
- package/cli/selftune/types.ts +103 -0
- package/cli/selftune/utils/skill-detection.ts +43 -0
- package/cli/selftune/utils/text-similarity.ts +73 -0
- package/cli/selftune/watchlist.ts +65 -0
- package/package.json +1 -1
- package/packages/ui/src/components/ActivityTimeline.tsx +165 -150
- package/packages/ui/src/components/EvidenceViewer.tsx +419 -145
- package/packages/ui/src/components/EvolutionTimeline.tsx +81 -29
- package/packages/ui/src/components/OrchestrateRunsPanel.tsx +33 -16
- package/packages/ui/src/components/RecentActivityFeed.tsx +72 -41
- package/packages/ui/src/components/section-cards.tsx +12 -9
- package/packages/ui/src/primitives/card.tsx +1 -1
- package/packages/ui/src/types.ts +4 -0
- package/skill/SKILL.md +11 -1
- package/skill/Workflows/AlphaUpload.md +4 -0
- package/skill/Workflows/Composability.md +78 -0
- package/skill/Workflows/Contribute.md +6 -3
- package/skill/Workflows/Contributions.md +97 -0
- package/skill/Workflows/CreatorContributions.md +74 -0
- package/skill/Workflows/Dashboard.md +31 -0
- package/skill/Workflows/Evals.md +57 -8
- package/skill/Workflows/Evolve.md +23 -0
- package/skill/Workflows/Ingest.md +7 -0
- package/skill/Workflows/Initialize.md +20 -1
- package/skill/Workflows/Recover.md +84 -0
- package/skill/Workflows/RepairSkillUsage.md +12 -4
- package/skill/Workflows/Sync.md +18 -12
- package/apps/local-dashboard/dist/assets/index-BMIS6uUh.css +0 -2
- package/apps/local-dashboard/dist/assets/index-DOu3iLD9.js +0 -16
- package/apps/local-dashboard/dist/assets/vendor-table-pHbDxq36.js +0 -8
- package/apps/local-dashboard/dist/assets/vendor-ui-DIwlrGlb.js +0 -12
|
@@ -10,13 +10,14 @@ import {
|
|
|
10
10
|
ChevronRightIcon,
|
|
11
11
|
} from "lucide-react";
|
|
12
12
|
import { useState } from "react";
|
|
13
|
+
import type { ReactNode } from "react";
|
|
13
14
|
|
|
14
15
|
import { timeAgo } from "../lib/format";
|
|
15
16
|
import { cn } from "../lib/utils";
|
|
16
17
|
import { Badge } from "../primitives/badge";
|
|
17
18
|
import type { EvalSnapshot, EvolutionEntry } from "../types";
|
|
18
19
|
|
|
19
|
-
const ACTION_ICON: Record<string,
|
|
20
|
+
const ACTION_ICON: Record<string, ReactNode> = {
|
|
20
21
|
created: <CircleDotIcon className="size-3.5" />,
|
|
21
22
|
validated: <ShieldCheckIcon className="size-3.5" />,
|
|
22
23
|
deployed: <RocketIcon className="size-3.5" />,
|
|
@@ -25,27 +26,43 @@ const ACTION_ICON: Record<string, React.ReactNode> = {
|
|
|
25
26
|
};
|
|
26
27
|
|
|
27
28
|
const ACTION_COLOR: Record<string, string> = {
|
|
28
|
-
created: "bg-
|
|
29
|
-
validated: "bg-
|
|
30
|
-
deployed: "bg-
|
|
31
|
-
rejected: "bg-
|
|
32
|
-
rolled_back: "bg-
|
|
29
|
+
created: "bg-primary/15",
|
|
30
|
+
validated: "bg-primary/25",
|
|
31
|
+
deployed: "bg-primary/30",
|
|
32
|
+
rejected: "bg-destructive/20",
|
|
33
|
+
rolled_back: "bg-destructive/15",
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const ACTION_ICON_COLOR: Record<string, string> = {
|
|
37
|
+
created: "text-primary/70",
|
|
38
|
+
validated: "text-primary/85",
|
|
39
|
+
deployed: "text-primary",
|
|
40
|
+
rejected: "text-destructive",
|
|
41
|
+
rolled_back: "text-destructive/70",
|
|
33
42
|
};
|
|
34
43
|
|
|
35
44
|
const ACTION_RING: Record<string, string> = {
|
|
36
|
-
created: "ring-
|
|
37
|
-
validated: "ring-
|
|
38
|
-
deployed: "ring-
|
|
39
|
-
rejected: "ring-
|
|
40
|
-
rolled_back: "ring-
|
|
45
|
+
created: "ring-primary/15",
|
|
46
|
+
validated: "ring-primary/25",
|
|
47
|
+
deployed: "ring-primary/30",
|
|
48
|
+
rejected: "ring-destructive/25",
|
|
49
|
+
rolled_back: "ring-destructive/15",
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const ACTION_DOT: Record<string, string> = {
|
|
53
|
+
created: "bg-primary/40 ring-primary/30",
|
|
54
|
+
validated: "bg-primary/60 ring-primary/40",
|
|
55
|
+
deployed: "bg-primary ring-primary/50",
|
|
56
|
+
rejected: "bg-destructive/60 ring-destructive/40",
|
|
57
|
+
rolled_back: "bg-destructive/40 ring-destructive/30",
|
|
41
58
|
};
|
|
42
59
|
|
|
43
60
|
const ACTION_LINE: Record<string, string> = {
|
|
44
|
-
created: "bg-
|
|
45
|
-
validated: "bg-
|
|
46
|
-
deployed: "bg-
|
|
47
|
-
rejected: "bg-
|
|
48
|
-
rolled_back: "bg-
|
|
61
|
+
created: "bg-primary/15",
|
|
62
|
+
validated: "bg-primary/20",
|
|
63
|
+
deployed: "bg-primary/25",
|
|
64
|
+
rejected: "bg-destructive/20",
|
|
65
|
+
rolled_back: "bg-destructive/15",
|
|
49
66
|
};
|
|
50
67
|
|
|
51
68
|
interface Props {
|
|
@@ -54,6 +71,21 @@ interface Props {
|
|
|
54
71
|
onSelect: (proposalId: string) => void;
|
|
55
72
|
}
|
|
56
73
|
|
|
74
|
+
function validationModeBadge(
|
|
75
|
+
mode?: string | null,
|
|
76
|
+
): { label: string; variant: "default" | "secondary" | "outline" } | null {
|
|
77
|
+
switch (mode) {
|
|
78
|
+
case "host_replay":
|
|
79
|
+
return { label: "replay", variant: "default" };
|
|
80
|
+
case "llm_judge":
|
|
81
|
+
return { label: "judge", variant: "secondary" };
|
|
82
|
+
case "structural_guard":
|
|
83
|
+
return { label: "structural", variant: "outline" };
|
|
84
|
+
default:
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
57
89
|
/** Group evolution entries by proposal_id, ordered newest-first. */
|
|
58
90
|
function groupByProposal(entries: EvolutionEntry[]) {
|
|
59
91
|
const map = new Map<string, EvolutionEntry[]>();
|
|
@@ -93,7 +125,7 @@ function PassRateDelta({ snapshot }: { snapshot: EvalSnapshot }) {
|
|
|
93
125
|
<span
|
|
94
126
|
className={cn(
|
|
95
127
|
"inline-flex items-center gap-0.5 text-[10px] font-mono font-medium",
|
|
96
|
-
isPositive ? "text-
|
|
128
|
+
isPositive ? "text-primary" : "text-destructive",
|
|
97
129
|
)}
|
|
98
130
|
>
|
|
99
131
|
{isPositive ? (
|
|
@@ -119,23 +151,35 @@ function LifecycleLegend() {
|
|
|
119
151
|
const [open, setOpen] = useState(false);
|
|
120
152
|
|
|
121
153
|
return (
|
|
122
|
-
<div className="px-2 pb-
|
|
154
|
+
<div className="px-2 pb-3">
|
|
123
155
|
<button
|
|
124
156
|
type="button"
|
|
125
157
|
onClick={() => setOpen(!open)}
|
|
126
|
-
|
|
158
|
+
aria-expanded={open}
|
|
159
|
+
aria-controls="evolution-lifecycle-stages"
|
|
160
|
+
className="flex w-full items-center gap-1 text-[10px] text-muted-foreground/70 transition-colors hover:text-muted-foreground"
|
|
127
161
|
>
|
|
128
162
|
{open ? <ChevronDownIcon className="size-3" /> : <ChevronRightIcon className="size-3" />}
|
|
129
163
|
Lifecycle stages
|
|
130
164
|
</button>
|
|
131
165
|
{open && (
|
|
132
|
-
<div
|
|
166
|
+
<div
|
|
167
|
+
id="evolution-lifecycle-stages"
|
|
168
|
+
className="mt-1.5 space-y-2.5 rounded-md border bg-muted/30 p-2"
|
|
169
|
+
>
|
|
133
170
|
{LIFECYCLE_STEPS.map((step) => (
|
|
134
171
|
<div key={step.action} className="flex items-start gap-2">
|
|
135
|
-
<div
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
172
|
+
<div
|
|
173
|
+
className={cn(
|
|
174
|
+
"size-2 rounded-full shrink-0 ring-1 mt-[3px]",
|
|
175
|
+
ACTION_DOT[step.action],
|
|
176
|
+
)}
|
|
177
|
+
/>
|
|
178
|
+
<div className="min-w-0 flex flex-col gap-0.5">
|
|
179
|
+
<span className="text-[10px] font-medium leading-none">{step.label}</span>
|
|
180
|
+
<span className="text-[10px] text-muted-foreground/70 leading-tight">
|
|
181
|
+
{step.desc}
|
|
182
|
+
</span>
|
|
139
183
|
</div>
|
|
140
184
|
</div>
|
|
141
185
|
))}
|
|
@@ -158,7 +202,7 @@ export function EvolutionTimeline({ entries, selectedProposalId, onSelect }: Pro
|
|
|
158
202
|
|
|
159
203
|
return (
|
|
160
204
|
<div className="flex flex-col gap-0">
|
|
161
|
-
<h2 className="
|
|
205
|
+
<h2 className="px-2 pb-2 text-[10px] font-semibold uppercase tracking-[0.18em] text-muted-foreground/80">
|
|
162
206
|
Evolution
|
|
163
207
|
</h2>
|
|
164
208
|
<LifecycleLegend />
|
|
@@ -167,11 +211,13 @@ export function EvolutionTimeline({ entries, selectedProposalId, onSelect }: Pro
|
|
|
167
211
|
const terminal = terminalAction(steps);
|
|
168
212
|
const isSelected = selectedProposalId === proposalId;
|
|
169
213
|
const lastStep = steps[steps.length - 1];
|
|
170
|
-
const dotColor = ACTION_COLOR[terminal] ?? "bg-muted-foreground";
|
|
214
|
+
const dotColor = ACTION_COLOR[terminal] ?? "bg-muted-foreground/20";
|
|
215
|
+
const iconColor = ACTION_ICON_COLOR[terminal] ?? "text-muted-foreground";
|
|
171
216
|
const ringColor = ACTION_RING[terminal] ?? "ring-muted-foreground/30";
|
|
172
217
|
const lineColor = ACTION_LINE[terminal] ?? "bg-border";
|
|
173
218
|
const isLast = groupIdx === groups.length - 1;
|
|
174
219
|
const snapshot = findEvalSnapshot(steps);
|
|
220
|
+
const validationBadge = validationModeBadge(lastStep.validation_mode);
|
|
175
221
|
|
|
176
222
|
return (
|
|
177
223
|
<div key={proposalId} className="relative flex gap-3">
|
|
@@ -179,14 +225,15 @@ export function EvolutionTimeline({ entries, selectedProposalId, onSelect }: Pro
|
|
|
179
225
|
<div className="flex flex-col items-center">
|
|
180
226
|
<div
|
|
181
227
|
className={cn(
|
|
182
|
-
"flex items-center justify-center size-7 rounded-full ring-2
|
|
228
|
+
"flex items-center justify-center size-7 rounded-full ring-2 shrink-0 z-10",
|
|
183
229
|
dotColor,
|
|
184
230
|
ringColor,
|
|
231
|
+
iconColor,
|
|
185
232
|
)}
|
|
186
233
|
>
|
|
187
234
|
{ACTION_ICON[terminal] ?? <CircleDotIcon className="size-3.5" />}
|
|
188
235
|
</div>
|
|
189
|
-
{!isLast && <div className={cn("w-0.5 flex-1 min-h-[
|
|
236
|
+
{!isLast && <div className={cn("w-0.5 flex-1 min-h-[8px] my-1", lineColor)} />}
|
|
190
237
|
</div>
|
|
191
238
|
|
|
192
239
|
{/* Content */}
|
|
@@ -215,6 +262,11 @@ export function EvolutionTimeline({ entries, selectedProposalId, onSelect }: Pro
|
|
|
215
262
|
<span className="text-[10px] text-muted-foreground">
|
|
216
263
|
{timeAgo(lastStep.timestamp)}
|
|
217
264
|
</span>
|
|
265
|
+
{validationBadge && (
|
|
266
|
+
<Badge variant={validationBadge.variant} className="text-[9px] uppercase">
|
|
267
|
+
{validationBadge.label}
|
|
268
|
+
</Badge>
|
|
269
|
+
)}
|
|
218
270
|
</div>
|
|
219
271
|
{/* Pass rate delta from eval snapshot */}
|
|
220
272
|
{snapshot && (
|
|
@@ -241,7 +293,7 @@ export function EvolutionTimeline({ entries, selectedProposalId, onSelect }: Pro
|
|
|
241
293
|
key={`${s.action}-${i}`}
|
|
242
294
|
className={cn(
|
|
243
295
|
"size-1.5 rounded-full",
|
|
244
|
-
|
|
296
|
+
ACTION_DOT[s.action] ?? "bg-muted-foreground/40",
|
|
245
297
|
)}
|
|
246
298
|
/>
|
|
247
299
|
))}
|
|
@@ -8,7 +8,7 @@ import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "../primitiv
|
|
|
8
8
|
import type { OrchestrateRunReport, OrchestrateRunSkillAction } from "../types";
|
|
9
9
|
|
|
10
10
|
const ACTION_ICON: Record<string, React.ReactNode> = {
|
|
11
|
-
evolve: <ZapIcon className="size-3 text-
|
|
11
|
+
evolve: <ZapIcon className="size-3 text-primary-accent" />,
|
|
12
12
|
watch: <EyeIcon className="size-3 text-blue-500" />,
|
|
13
13
|
skip: <SkipForwardIcon className="size-3 text-muted-foreground" />,
|
|
14
14
|
};
|
|
@@ -66,17 +66,15 @@ function RunCard({ run }: { run: OrchestrateRunReport }) {
|
|
|
66
66
|
<div
|
|
67
67
|
className={`mt-1.5 size-2 shrink-0 rounded-full ${
|
|
68
68
|
run.deployed > 0
|
|
69
|
-
? "bg-
|
|
69
|
+
? "bg-primary"
|
|
70
70
|
: run.evolved > 0
|
|
71
|
-
? "bg-
|
|
71
|
+
? "bg-primary-accent"
|
|
72
72
|
: "bg-muted-foreground/40"
|
|
73
73
|
}`}
|
|
74
74
|
/>
|
|
75
75
|
<div className="flex-1 min-w-0">
|
|
76
76
|
<div className="flex items-center gap-2">
|
|
77
|
-
<span className="text-
|
|
78
|
-
{timeAgo(run.timestamp)}
|
|
79
|
-
</span>
|
|
77
|
+
<span className="text-[10px] font-mono text-slate-500">{timeAgo(run.timestamp)}</span>
|
|
80
78
|
{run.dry_run && (
|
|
81
79
|
<Badge variant="outline" className="text-[10px] h-4 px-1.5">
|
|
82
80
|
dry-run
|
|
@@ -90,7 +88,7 @@ function RunCard({ run }: { run: OrchestrateRunReport }) {
|
|
|
90
88
|
</div>
|
|
91
89
|
<div className="flex items-center gap-3 mt-1 text-xs text-muted-foreground">
|
|
92
90
|
{run.deployed > 0 && (
|
|
93
|
-
<span className="text-
|
|
91
|
+
<span className="text-primary font-medium">{run.deployed} deployed</span>
|
|
94
92
|
)}
|
|
95
93
|
{run.evolved > 0 && <span>{run.evolved} evolved</span>}
|
|
96
94
|
{run.watched > 0 && <span>{run.watched} watched</span>}
|
|
@@ -104,7 +102,7 @@ function RunCard({ run }: { run: OrchestrateRunReport }) {
|
|
|
104
102
|
</div>
|
|
105
103
|
</CollapsibleTrigger>
|
|
106
104
|
<CollapsibleContent>
|
|
107
|
-
<div className="ml-5 pl-3 border-l border-border space-y-0.5 pb-2">
|
|
105
|
+
<div className="ml-5 pl-3 border-l border-border/15 space-y-0.5 pb-2">
|
|
108
106
|
{nonSkipActions.map((action, i) => (
|
|
109
107
|
<SkillActionRow key={`${action.skill}-${i}`} action={action} />
|
|
110
108
|
))}
|
|
@@ -126,7 +124,32 @@ function RunCard({ run }: { run: OrchestrateRunReport }) {
|
|
|
126
124
|
);
|
|
127
125
|
}
|
|
128
126
|
|
|
129
|
-
export function OrchestrateRunsPanel({
|
|
127
|
+
export function OrchestrateRunsPanel({
|
|
128
|
+
runs,
|
|
129
|
+
embedded = false,
|
|
130
|
+
}: {
|
|
131
|
+
runs: OrchestrateRunReport[];
|
|
132
|
+
embedded?: boolean;
|
|
133
|
+
}) {
|
|
134
|
+
const totalDeployed = runs.reduce((sum, r) => sum + r.deployed, 0);
|
|
135
|
+
const content =
|
|
136
|
+
runs.length === 0 ? (
|
|
137
|
+
<p className="py-4 text-center text-sm text-muted-foreground">
|
|
138
|
+
No orchestrate runs yet. Run{" "}
|
|
139
|
+
<code className="rounded bg-muted px-1 py-0.5 text-xs">selftune orchestrate</code> to start.
|
|
140
|
+
</p>
|
|
141
|
+
) : (
|
|
142
|
+
<div className="space-y-0">
|
|
143
|
+
{runs.slice(0, 10).map((run) => (
|
|
144
|
+
<RunCard key={run.run_id} run={run} />
|
|
145
|
+
))}
|
|
146
|
+
</div>
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
if (embedded) {
|
|
150
|
+
return <div>{content}</div>;
|
|
151
|
+
}
|
|
152
|
+
|
|
130
153
|
if (runs.length === 0) {
|
|
131
154
|
return (
|
|
132
155
|
<Card>
|
|
@@ -147,8 +170,6 @@ export function OrchestrateRunsPanel({ runs }: { runs: OrchestrateRunReport[] })
|
|
|
147
170
|
);
|
|
148
171
|
}
|
|
149
172
|
|
|
150
|
-
const totalDeployed = runs.reduce((sum, r) => sum + r.deployed, 0);
|
|
151
|
-
|
|
152
173
|
return (
|
|
153
174
|
<Card>
|
|
154
175
|
<CardHeader>
|
|
@@ -161,11 +182,7 @@ export function OrchestrateRunsPanel({ runs }: { runs: OrchestrateRunReport[] })
|
|
|
161
182
|
{totalDeployed > 0 && <> · {totalDeployed} total deployments</>}
|
|
162
183
|
</CardDescription>
|
|
163
184
|
</CardHeader>
|
|
164
|
-
<CardContent
|
|
165
|
-
{runs.slice(0, 10).map((run) => (
|
|
166
|
-
<RunCard key={run.run_id} run={run} />
|
|
167
|
-
))}
|
|
168
|
-
</CardContent>
|
|
185
|
+
<CardContent>{content}</CardContent>
|
|
169
186
|
</Card>
|
|
170
187
|
);
|
|
171
188
|
}
|
|
@@ -13,7 +13,77 @@ export interface RecentActivityItem {
|
|
|
13
13
|
is_live: boolean;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
export function RecentActivityFeed({
|
|
16
|
+
export function RecentActivityFeed({
|
|
17
|
+
items,
|
|
18
|
+
embedded = false,
|
|
19
|
+
}: {
|
|
20
|
+
items: RecentActivityItem[];
|
|
21
|
+
embedded?: boolean;
|
|
22
|
+
}) {
|
|
23
|
+
const content =
|
|
24
|
+
items.length === 0 ? (
|
|
25
|
+
<p className="py-6 text-center text-sm text-muted-foreground">No recent skill invocations</p>
|
|
26
|
+
) : (
|
|
27
|
+
<div className="space-y-2.5">
|
|
28
|
+
{items.slice(0, 20).map((item, i) => (
|
|
29
|
+
<div
|
|
30
|
+
key={`${item.session_id}-${item.skill_name}-${i}`}
|
|
31
|
+
className="flex gap-3 rounded-md p-1.5"
|
|
32
|
+
>
|
|
33
|
+
<div
|
|
34
|
+
className={`mt-0.5 w-10 h-10 shrink-0 rounded-xl bg-input flex items-center justify-center`}
|
|
35
|
+
>
|
|
36
|
+
<div
|
|
37
|
+
className={`size-2 rounded-full ${
|
|
38
|
+
item.triggered
|
|
39
|
+
? "bg-primary shadow-[0_0_8px_rgba(79,242,255,0.6)]"
|
|
40
|
+
: "bg-muted-foreground/40"
|
|
41
|
+
}`}
|
|
42
|
+
/>
|
|
43
|
+
</div>
|
|
44
|
+
<div className="flex-1 min-w-0 space-y-0.5">
|
|
45
|
+
<div className="flex flex-wrap items-center gap-2">
|
|
46
|
+
<span className="truncate font-bold text-sm">{item.skill_name}</span>
|
|
47
|
+
{item.is_live && (
|
|
48
|
+
<Badge variant="outline" className="h-4 gap-1 px-1 text-[10px]">
|
|
49
|
+
<CircleDotIcon className="size-2.5 text-primary" />
|
|
50
|
+
live
|
|
51
|
+
</Badge>
|
|
52
|
+
)}
|
|
53
|
+
{item.triggered ? (
|
|
54
|
+
<Badge
|
|
55
|
+
variant="default"
|
|
56
|
+
className="h-4 px-1 text-[10px] font-bold uppercase tracking-tighter bg-primary/10 text-primary"
|
|
57
|
+
>
|
|
58
|
+
triggered
|
|
59
|
+
</Badge>
|
|
60
|
+
) : (
|
|
61
|
+
<Badge
|
|
62
|
+
variant="secondary"
|
|
63
|
+
className="h-4 px-1 text-[10px] font-bold uppercase tracking-tighter"
|
|
64
|
+
>
|
|
65
|
+
checked
|
|
66
|
+
</Badge>
|
|
67
|
+
)}
|
|
68
|
+
<span className="ml-auto shrink-0 font-mono text-[10px] text-slate-500">
|
|
69
|
+
{timeAgo(item.timestamp)}
|
|
70
|
+
</span>
|
|
71
|
+
</div>
|
|
72
|
+
{item.query && (
|
|
73
|
+
<p className="line-clamp-1 text-sm text-card-foreground leading-relaxed">
|
|
74
|
+
{item.query}
|
|
75
|
+
</p>
|
|
76
|
+
)}
|
|
77
|
+
</div>
|
|
78
|
+
</div>
|
|
79
|
+
))}
|
|
80
|
+
</div>
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
if (embedded) {
|
|
84
|
+
return <div>{content}</div>;
|
|
85
|
+
}
|
|
86
|
+
|
|
17
87
|
if (items.length === 0) {
|
|
18
88
|
return (
|
|
19
89
|
<Card>
|
|
@@ -41,46 +111,7 @@ export function RecentActivityFeed({ items }: { items: RecentActivityItem[] }) {
|
|
|
41
111
|
</CardTitle>
|
|
42
112
|
<CardDescription>Latest skill invocations across sessions</CardDescription>
|
|
43
113
|
</CardHeader>
|
|
44
|
-
<CardContent
|
|
45
|
-
{items.slice(0, 20).map((item, i) => (
|
|
46
|
-
<div
|
|
47
|
-
key={`${item.session_id}-${item.skill_name}-${i}`}
|
|
48
|
-
className="flex gap-3 rounded-md p-1.5"
|
|
49
|
-
>
|
|
50
|
-
<div
|
|
51
|
-
className={`mt-1 size-2 shrink-0 rounded-full ${
|
|
52
|
-
item.triggered ? "bg-emerald-500" : "bg-muted-foreground/40"
|
|
53
|
-
}`}
|
|
54
|
-
/>
|
|
55
|
-
<div className="flex-1 min-w-0 space-y-0.5">
|
|
56
|
-
<div className="flex items-center gap-2 flex-wrap">
|
|
57
|
-
<span className="text-xs font-medium truncate">{item.skill_name}</span>
|
|
58
|
-
{item.is_live && (
|
|
59
|
-
<Badge variant="outline" className="h-4 px-1 text-[10px] gap-1">
|
|
60
|
-
<CircleDotIcon className="size-2.5 text-emerald-500" />
|
|
61
|
-
live
|
|
62
|
-
</Badge>
|
|
63
|
-
)}
|
|
64
|
-
{item.triggered ? (
|
|
65
|
-
<Badge variant="default" className="h-4 px-1 text-[10px]">
|
|
66
|
-
triggered
|
|
67
|
-
</Badge>
|
|
68
|
-
) : (
|
|
69
|
-
<Badge variant="secondary" className="h-4 px-1 text-[10px]">
|
|
70
|
-
checked
|
|
71
|
-
</Badge>
|
|
72
|
-
)}
|
|
73
|
-
<span className="text-[10px] text-muted-foreground font-mono ml-auto shrink-0">
|
|
74
|
-
{timeAgo(item.timestamp)}
|
|
75
|
-
</span>
|
|
76
|
-
</div>
|
|
77
|
-
{item.query && (
|
|
78
|
-
<p className="text-xs text-muted-foreground line-clamp-1 font-mono">{item.query}</p>
|
|
79
|
-
)}
|
|
80
|
-
</div>
|
|
81
|
-
</div>
|
|
82
|
-
))}
|
|
83
|
-
</CardContent>
|
|
114
|
+
<CardContent>{content}</CardContent>
|
|
84
115
|
</Card>
|
|
85
116
|
);
|
|
86
117
|
}
|
|
@@ -24,6 +24,9 @@ interface SectionCardsProps {
|
|
|
24
24
|
activeSessionsCount?: number;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
const CARD_DESCRIPTION_CLASS =
|
|
28
|
+
"flex items-center gap-1.5 text-[10px] uppercase tracking-widest text-slate-500";
|
|
29
|
+
|
|
27
30
|
export function SectionCards({
|
|
28
31
|
skillsCount,
|
|
29
32
|
avgPassRate,
|
|
@@ -38,10 +41,10 @@ export function SectionCards({
|
|
|
38
41
|
const passRateGood = avgPassRate !== null && avgPassRate >= 0.7;
|
|
39
42
|
|
|
40
43
|
return (
|
|
41
|
-
<div className="grid grid-cols-1 gap-4 px-4 *:data-[slot=card]:shadow-
|
|
44
|
+
<div className="grid grid-cols-1 gap-4 px-4 *:data-[slot=card]:shadow-none lg:px-6 @xl/main:grid-cols-2 @5xl/main:grid-cols-3">
|
|
42
45
|
<Card className="@container/card">
|
|
43
46
|
<CardHeader>
|
|
44
|
-
<CardDescription className=
|
|
47
|
+
<CardDescription className={CARD_DESCRIPTION_CLASS}>
|
|
45
48
|
<LayersIcon className="size-3.5" />
|
|
46
49
|
Skills Monitored
|
|
47
50
|
<InfoTip text="Total number of skills detected and being tracked by selftune" />
|
|
@@ -60,7 +63,7 @@ export function SectionCards({
|
|
|
60
63
|
|
|
61
64
|
<Card className="@container/card">
|
|
62
65
|
<CardHeader>
|
|
63
|
-
<CardDescription className=
|
|
66
|
+
<CardDescription className={CARD_DESCRIPTION_CLASS}>
|
|
64
67
|
<FlaskConicalIcon className="size-3.5" />
|
|
65
68
|
Avg Trigger Rate
|
|
66
69
|
<InfoTip text="Average percentage of skill checks that resulted in a trigger across all graded skills (5+ checks). Run selftune evolve to improve this." />
|
|
@@ -91,7 +94,7 @@ export function SectionCards({
|
|
|
91
94
|
|
|
92
95
|
<Card className="@container/card">
|
|
93
96
|
<CardHeader>
|
|
94
|
-
<CardDescription className=
|
|
97
|
+
<CardDescription className={CARD_DESCRIPTION_CLASS}>
|
|
95
98
|
<SearchXIcon className="size-3.5" />
|
|
96
99
|
Unmatched Queries
|
|
97
100
|
<InfoTip text="User prompts that didn't match any skill's trigger criteria — potential gaps in coverage" />
|
|
@@ -112,7 +115,7 @@ export function SectionCards({
|
|
|
112
115
|
|
|
113
116
|
<Card className="@container/card">
|
|
114
117
|
<CardHeader>
|
|
115
|
-
<CardDescription className=
|
|
118
|
+
<CardDescription className={CARD_DESCRIPTION_CLASS}>
|
|
116
119
|
<ActivityIcon className="size-3.5" />
|
|
117
120
|
Sessions
|
|
118
121
|
<InfoTip text="Total agent sessions that have been recorded and analyzed" />
|
|
@@ -124,8 +127,8 @@ export function SectionCards({
|
|
|
124
127
|
<CardAction>
|
|
125
128
|
<Badge variant="outline" className="gap-1.5">
|
|
126
129
|
<span className="relative flex size-2">
|
|
127
|
-
<span className="absolute inline-flex size-full animate-ping rounded-full bg-
|
|
128
|
-
<span className="relative inline-flex size-2 rounded-full bg-
|
|
130
|
+
<span className="absolute inline-flex size-full animate-ping rounded-full bg-primary opacity-75" />
|
|
131
|
+
<span className="relative inline-flex size-2 rounded-full bg-primary shadow-[0_0_8px_color-mix(in_srgb,var(--primary)_60%,transparent)]" />
|
|
129
132
|
</span>
|
|
130
133
|
{activeSessionsCount} in progress
|
|
131
134
|
</Badge>
|
|
@@ -136,7 +139,7 @@ export function SectionCards({
|
|
|
136
139
|
|
|
137
140
|
<Card className="@container/card">
|
|
138
141
|
<CardHeader>
|
|
139
|
-
<CardDescription className=
|
|
142
|
+
<CardDescription className={CARD_DESCRIPTION_CLASS}>
|
|
140
143
|
<AlertTriangleIcon className="size-3.5" />
|
|
141
144
|
Undeployed Proposals
|
|
142
145
|
<InfoTip text="Evolution proposals that have been generated but not yet validated or deployed. Requires running selftune evolve." />
|
|
@@ -158,7 +161,7 @@ export function SectionCards({
|
|
|
158
161
|
|
|
159
162
|
<Card className="@container/card">
|
|
160
163
|
<CardHeader>
|
|
161
|
-
<CardDescription className=
|
|
164
|
+
<CardDescription className={CARD_DESCRIPTION_CLASS}>
|
|
162
165
|
<EyeIcon className="size-3.5" />
|
|
163
166
|
Total Evidence
|
|
164
167
|
<InfoTip text="Number of evidence entries documenting skill changes with before/after validation results. Requires running selftune evolve." />
|
|
@@ -12,7 +12,7 @@ function Card({
|
|
|
12
12
|
data-slot="card"
|
|
13
13
|
data-size={size}
|
|
14
14
|
className={cn(
|
|
15
|
-
"group/card flex flex-col gap-4 overflow-hidden rounded-
|
|
15
|
+
"group/card flex flex-col gap-4 overflow-hidden rounded-2xl bg-card py-4 text-sm text-card-foreground ring-1 ring-foreground/5 has-data-[slot=card-footer]:pb-0 has-[>img:first-child]:pt-0 data-[size=sm]:gap-3 data-[size=sm]:py-3 data-[size=sm]:has-data-[slot=card-footer]:pb-0 *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl",
|
|
16
16
|
className,
|
|
17
17
|
)}
|
|
18
18
|
{...props}
|
package/packages/ui/src/types.ts
CHANGED
|
@@ -31,6 +31,10 @@ export interface EvolutionEntry {
|
|
|
31
31
|
action: string;
|
|
32
32
|
details: string;
|
|
33
33
|
eval_snapshot?: EvalSnapshot | null;
|
|
34
|
+
validation_mode?: "structural_guard" | "host_replay" | "llm_judge" | null;
|
|
35
|
+
validation_agent?: string | null;
|
|
36
|
+
validation_fixture_id?: string | null;
|
|
37
|
+
validation_evidence_ref?: string | null;
|
|
34
38
|
}
|
|
35
39
|
|
|
36
40
|
export interface UnmatchedQuery {
|
package/skill/SKILL.md
CHANGED
|
@@ -89,6 +89,7 @@ selftune eval generate --skill <name> [--list-skills] [--stats] [--max N] [
|
|
|
89
89
|
selftune eval unit-test --skill <name> --tests <path> [--run-agent] [--generate]
|
|
90
90
|
selftune eval import --dir <path> --skill <name> --output <path> [--match-strategy exact|fuzzy]
|
|
91
91
|
selftune eval composability --skill <name> [--window N] [--telemetry-log <path>]
|
|
92
|
+
selftune eval family-overlap --prefix <family-> | --skills <a,b,c> [--parent-skill <name>] [--min-overlap 0.3] [--min-shared 2]
|
|
92
93
|
|
|
93
94
|
# Other commands
|
|
94
95
|
selftune watch --skill <name> --skill-path <path> [--auto-rollback]
|
|
@@ -96,6 +97,8 @@ selftune status
|
|
|
96
97
|
selftune last
|
|
97
98
|
selftune doctor
|
|
98
99
|
selftune dashboard [--port <port>] [--no-open]
|
|
100
|
+
selftune contributions [status|preview <skill>|upload [--dry-run]|approve <skill>|revoke <skill>|default <ask|always|never>|reset]
|
|
101
|
+
selftune creator-contributions [status|enable --skill <name>|enable --all [--prefix <value>]|disable --skill <name>]
|
|
99
102
|
selftune contribute [--skill NAME] [--preview] [--sanitize LEVEL] [--submit]
|
|
100
103
|
selftune cron setup [--dry-run] # auto-detect platform (cron/launchd/systemd)
|
|
101
104
|
selftune cron setup --platform openclaw [--dry-run] [--tz <timezone>] # OpenClaw-specific
|
|
@@ -115,6 +118,7 @@ selftune badge --skill <name> [--format svg|markdown|url] [--output PATH]
|
|
|
115
118
|
# Maintenance
|
|
116
119
|
selftune quickstart
|
|
117
120
|
selftune repair-skill-usage [--since DATE] [--dry-run]
|
|
121
|
+
selftune recover [--full] [--force] [--since DATE]
|
|
118
122
|
selftune export-canonical [--out FILE] [--platform NAME] [--record-kind KIND] [--pretty] [--push-payload]
|
|
119
123
|
selftune uninstall [--dry-run] [--keep-logs] [--npm-uninstall]
|
|
120
124
|
|
|
@@ -141,6 +145,8 @@ selftune status # shows c
|
|
|
141
145
|
| doctor, health, hooks, broken, diagnose, not working, something wrong | Doctor | Workflows/Doctor.md |
|
|
142
146
|
| ingest, import, codex logs, opencode, openclaw, wrap codex | Ingest | Workflows/Ingest.md |
|
|
143
147
|
| replay, backfill, claude transcripts, historical sessions | Replay | Workflows/Replay.md |
|
|
148
|
+
| contributions, sharing preferences, opt in creator sharing, opt out creator sharing, approve contributions, revoke contributions, preview contributions, upload contributions, relay queue, contribution upload, contribution preview | Contributions | Workflows/Contributions.md |
|
|
149
|
+
| creator contributions, bundle contribution config, selftune.contribute.json, enable creator contribution, disable creator contribution, bulk enable creator contribution, enable all creator contributions, creator prefix config, --all, --prefix | CreatorContributions | Workflows/CreatorContributions.md |
|
|
144
150
|
| contribute, share, community, export data, anonymized, give back | Contribute | Workflows/Contribute.md |
|
|
145
151
|
| init, setup, set up, bootstrap, first time, install, configure selftune, alpha, enroll, alpha enrollment, cloud link, upload credential | Initialize | Workflows/Initialize.md |
|
|
146
152
|
| cron, schedule, automate evolution, run automatically | Cron | Workflows/Cron.md |
|
|
@@ -149,7 +155,7 @@ selftune status # shows c
|
|
|
149
155
|
| evolution memory, session continuity, what happened last | EvolutionMemory | Workflows/EvolutionMemory.md |
|
|
150
156
|
| grade baseline, baseline lift, adds value, skill value, no-skill comparison | Baseline | Workflows/Baseline.md |
|
|
151
157
|
| eval unit-test, skill test, test skill, generate tests, run tests | UnitTest | Workflows/UnitTest.md |
|
|
152
|
-
| eval composability, co-occurrence, skill conflicts, skills together
|
|
158
|
+
| eval composability, co-occurrence, skill conflicts, skills together, family overlap, sibling confusion, consolidate skill family | Composability | Workflows/Composability.md |
|
|
153
159
|
| eval import, skillsbench, external evals, benchmark tasks | ImportSkillsBench | Workflows/ImportSkillsBench.md |
|
|
154
160
|
| telemetry, analytics, disable analytics, opt out, tracking, privacy | Telemetry | Workflows/Telemetry.md |
|
|
155
161
|
| orchestrate, autonomous, full loop, improve all skills, run selftune loop | Orchestrate | Workflows/Orchestrate.md |
|
|
@@ -157,6 +163,7 @@ selftune status # shows c
|
|
|
157
163
|
| badge, readme badge, skill badge, health badge | Badge | Workflows/Badge.md |
|
|
158
164
|
| workflows, discover workflows, list workflows, multi-skill workflows | Workflows | Workflows/Workflows.md |
|
|
159
165
|
| alpha upload, upload data, send alpha data, manual upload, dry run upload | AlphaUpload | Workflows/AlphaUpload.md |
|
|
166
|
+
| recover, rebuild sqlite, recover db, legacy backfill, restore from export snapshot | Recover | Workflows/Recover.md |
|
|
160
167
|
| quickstart, getting started, onboard, first time setup, new user | Quickstart | Workflows/Quickstart.md |
|
|
161
168
|
| uninstall, remove selftune, clean up, teardown | Uninstall | Workflows/Uninstall.md |
|
|
162
169
|
| repair, rebuild usage, fix skill usage, trustworthy usage, repair-skill-usage | RepairSkillUsage | Workflows/RepairSkillUsage.md |
|
|
@@ -345,6 +352,9 @@ accomplish a task _using_ a skill, route to that skill instead.
|
|
|
345
352
|
| `Workflows/Quickstart.md` | Guided onboarding: init, ingest, status | First-time setup for new users |
|
|
346
353
|
| `Workflows/Uninstall.md` | Clean removal of selftune data and config | When removing selftune completely |
|
|
347
354
|
| `Workflows/RepairSkillUsage.md` | Rebuild skill usage from source transcripts | When skill usage data seems inaccurate |
|
|
355
|
+
| `Workflows/Recover.md` | Recover SQLite from legacy/exported JSONL | When rebuilding or backfilling SQLite |
|
|
356
|
+
| `Workflows/Contributions.md` | Manage creator-directed sharing preferences | When approving or revoking creator contribution |
|
|
357
|
+
| `Workflows/CreatorContributions.md` | Manage bundled `selftune.contribute.json` configs | When preparing a skill package for creator contributions |
|
|
348
358
|
| `Workflows/ExportCanonical.md` | Export canonical telemetry for downstream use | When exporting data for external consumption |
|
|
349
359
|
| `Workflows/Hook.md` | Manual hook invocation for debugging | When debugging or testing hooks manually |
|
|
350
360
|
| `references/logs.md` | Log file formats (telemetry, usage, queries, audit) | When parsing or debugging log files |
|
|
@@ -24,6 +24,10 @@ selftune alpha upload [--dry-run]
|
|
|
24
24
|
4. Build V2 push envelopes and flush them to the cloud API
|
|
25
25
|
5. Print a JSON summary with `enrolled`, `prepared`, `sent`, `failed`, `skipped`, and optional `guidance`
|
|
26
26
|
|
|
27
|
+
`selftune sync` already triggers an upload cycle automatically when alpha is
|
|
28
|
+
enrolled. Use this workflow when you want a manual upload now or want to see a
|
|
29
|
+
dry-run summary of what SQLite-backed staging would send.
|
|
30
|
+
|
|
27
31
|
## Examples
|
|
28
32
|
|
|
29
33
|
Preview the upload:
|