selftune 0.2.9 → 0.2.10
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 +35 -35
- package/apps/local-dashboard/dist/assets/index-BZVLv70T.js +16 -0
- package/apps/local-dashboard/dist/assets/{vendor-react-BQH_6WrG.js → vendor-react-BXP54cYo.js} +4 -4
- package/apps/local-dashboard/dist/assets/{vendor-table-dK1QMLq9.js → vendor-table-DTF_SXoy.js} +1 -1
- package/apps/local-dashboard/dist/assets/{vendor-ui-CO2mrx6e.js → vendor-ui-CWU0d1wd.js} +66 -66
- package/apps/local-dashboard/dist/index.html +15 -15
- package/bin/selftune.cjs +1 -1
- package/cli/selftune/activation-rules.ts +1 -0
- package/cli/selftune/alpha-upload/build-payloads.ts +18 -2
- package/cli/selftune/alpha-upload/stage-canonical.ts +94 -0
- package/cli/selftune/auth/device-code.ts +32 -0
- package/cli/selftune/auto-update.ts +12 -0
- package/cli/selftune/badge/badge.ts +1 -0
- package/cli/selftune/canonical-export.ts +5 -0
- package/cli/selftune/claude-agents.ts +154 -0
- package/cli/selftune/contribute/bundle.ts +1 -0
- package/cli/selftune/contribute/contribute.ts +1 -0
- package/cli/selftune/cron/setup.ts +2 -2
- package/cli/selftune/dashboard-server.ts +1 -0
- package/cli/selftune/eval/hooks-to-evals.ts +1 -0
- package/cli/selftune/eval/import-skillsbench.ts +1 -0
- package/cli/selftune/eval/synthetic-evals.ts +2 -3
- package/cli/selftune/eval/unit-test.ts +1 -0
- package/cli/selftune/evolution/deploy-proposal.ts +1 -0
- package/cli/selftune/evolution/evolve-body.ts +93 -6
- package/cli/selftune/evolution/evolve.ts +0 -1
- package/cli/selftune/evolution/propose-body.ts +3 -2
- package/cli/selftune/evolution/propose-routing.ts +3 -2
- package/cli/selftune/evolution/refine-body.ts +3 -2
- package/cli/selftune/export.ts +1 -0
- package/cli/selftune/grading/grade-session.ts +8 -0
- package/cli/selftune/hooks/auto-activate.ts +1 -0
- package/cli/selftune/hooks/evolution-guard.ts +1 -1
- package/cli/selftune/hooks/prompt-log.ts +1 -0
- package/cli/selftune/hooks/session-stop.ts +34 -40
- package/cli/selftune/hooks/skill-change-guard.ts +1 -0
- package/cli/selftune/hooks/skill-eval.ts +1 -1
- package/cli/selftune/index.ts +23 -14
- package/cli/selftune/ingestors/claude-replay.ts +1 -0
- package/cli/selftune/ingestors/codex-rollout.ts +1 -0
- package/cli/selftune/ingestors/codex-wrapper.ts +1 -0
- package/cli/selftune/ingestors/openclaw-ingest.ts +1 -0
- package/cli/selftune/ingestors/opencode-ingest.ts +1 -0
- package/cli/selftune/init.ts +121 -29
- package/cli/selftune/localdb/db.ts +1 -0
- package/cli/selftune/localdb/direct-write.ts +39 -0
- package/cli/selftune/localdb/materialize.ts +2 -0
- package/cli/selftune/localdb/queries.ts +53 -0
- package/cli/selftune/localdb/schema.ts +28 -0
- package/cli/selftune/normalization.ts +1 -0
- package/cli/selftune/observability.ts +1 -0
- package/cli/selftune/repair/skill-usage.ts +1 -0
- package/cli/selftune/routes/orchestrate-runs.ts +1 -0
- package/cli/selftune/routes/overview.ts +1 -0
- package/cli/selftune/routes/skill-report.ts +1 -0
- package/cli/selftune/sync.ts +30 -1
- package/cli/selftune/uninstall.ts +412 -0
- package/cli/selftune/utils/canonical-log.ts +2 -0
- package/cli/selftune/utils/jsonl.ts +1 -0
- package/cli/selftune/utils/llm-call.ts +131 -3
- package/cli/selftune/utils/skill-log.ts +1 -0
- package/cli/selftune/utils/transcript.ts +1 -0
- package/cli/selftune/utils/trigger-check.ts +1 -1
- package/cli/selftune/workflows/skill-md-writer.ts +5 -5
- package/cli/selftune/workflows/workflows.ts +1 -0
- package/package.json +37 -33
- package/packages/telemetry-contract/fixtures/golden.test.ts +1 -0
- package/packages/telemetry-contract/package.json +1 -1
- package/packages/telemetry-contract/src/schemas.ts +1 -0
- package/packages/telemetry-contract/tests/compatibility.test.ts +1 -0
- package/packages/ui/README.md +35 -34
- package/packages/ui/package.json +3 -3
- package/packages/ui/src/components/ActivityTimeline.tsx +49 -42
- package/packages/ui/src/components/EvidenceViewer.tsx +306 -182
- package/packages/ui/src/components/EvolutionTimeline.tsx +83 -72
- package/packages/ui/src/components/InfoTip.tsx +4 -3
- package/packages/ui/src/components/OrchestrateRunsPanel.tsx +60 -53
- package/packages/ui/src/components/section-cards.tsx +19 -24
- package/packages/ui/src/components/skill-health-grid.tsx +213 -193
- package/packages/ui/src/lib/constants.tsx +1 -0
- package/packages/ui/src/primitives/badge.tsx +12 -15
- package/packages/ui/src/primitives/button.tsx +7 -7
- package/packages/ui/src/primitives/card.tsx +15 -26
- package/packages/ui/src/primitives/checkbox.tsx +7 -8
- package/packages/ui/src/primitives/collapsible.tsx +5 -5
- package/packages/ui/src/primitives/dropdown-menu.tsx +45 -55
- package/packages/ui/src/primitives/label.tsx +6 -6
- package/packages/ui/src/primitives/select.tsx +28 -37
- package/packages/ui/src/primitives/table.tsx +17 -44
- package/packages/ui/src/primitives/tabs.tsx +14 -21
- package/packages/ui/src/primitives/tooltip.tsx +10 -22
- package/skill/SKILL.md +70 -57
- package/skill/Workflows/AlphaUpload.md +4 -4
- package/skill/Workflows/AutoActivation.md +11 -6
- package/skill/Workflows/Badge.md +22 -16
- package/skill/Workflows/Baseline.md +34 -36
- package/skill/Workflows/Composability.md +16 -11
- package/skill/Workflows/Contribute.md +26 -21
- package/skill/Workflows/Cron.md +23 -22
- package/skill/Workflows/Dashboard.md +32 -27
- package/skill/Workflows/Doctor.md +33 -27
- package/skill/Workflows/Evals.md +48 -47
- package/skill/Workflows/EvolutionMemory.md +31 -21
- package/skill/Workflows/Evolve.md +84 -82
- package/skill/Workflows/EvolveBody.md +58 -47
- package/skill/Workflows/Grade.md +16 -13
- package/skill/Workflows/ImportSkillsBench.md +9 -6
- package/skill/Workflows/Ingest.md +36 -21
- package/skill/Workflows/Initialize.md +108 -40
- package/skill/Workflows/Orchestrate.md +22 -16
- package/skill/Workflows/Replay.md +12 -7
- package/skill/Workflows/Rollback.md +13 -6
- package/skill/Workflows/Schedule.md +6 -6
- package/skill/Workflows/Sync.md +18 -11
- package/skill/Workflows/UnitTest.md +28 -17
- package/skill/Workflows/Watch.md +28 -21
- package/skill/agents/diagnosis-analyst.md +11 -0
- package/skill/agents/evolution-reviewer.md +15 -1
- package/skill/agents/integration-guide.md +10 -0
- package/skill/agents/pattern-analyst.md +12 -1
- package/skill/references/grading-methodology.md +23 -24
- package/skill/references/interactive-config.md +7 -7
- package/skill/references/invocation-taxonomy.md +22 -20
- package/skill/references/logs.md +14 -6
- package/skill/references/setup-patterns.md +4 -2
- package/.claude/agents/diagnosis-analyst.md +0 -156
- package/.claude/agents/evolution-reviewer.md +0 -180
- package/.claude/agents/integration-guide.md +0 -212
- package/.claude/agents/pattern-analyst.md +0 -160
- package/apps/local-dashboard/dist/assets/index-C4UYGWKr.js +0 -15
|
@@ -25,7 +25,8 @@ import type {
|
|
|
25
25
|
QueryLogRecord,
|
|
26
26
|
SkillUsageRecord,
|
|
27
27
|
} from "../types.js";
|
|
28
|
-
|
|
28
|
+
import type { EffortLevel, SubagentCallOptions } from "../utils/llm-call.js";
|
|
29
|
+
import { callViaSubagent } from "../utils/llm-call.js";
|
|
29
30
|
import { appendAuditEntry } from "./audit.js";
|
|
30
31
|
import { checkConstitutionSizeOnly } from "./constitutional.js";
|
|
31
32
|
import { parseSkillSections, replaceBody, replaceSection } from "./deploy-proposal.js";
|
|
@@ -57,6 +58,9 @@ export interface EvolveBodyOptions {
|
|
|
57
58
|
fewShotExamples?: string[];
|
|
58
59
|
gradingResults?: GradingResult[];
|
|
59
60
|
validationModel?: string;
|
|
61
|
+
teacherEffort?: EffortLevel;
|
|
62
|
+
/** Run evolution-reviewer subagent as Gate 4 before deployment. */
|
|
63
|
+
useReviewer?: boolean;
|
|
60
64
|
}
|
|
61
65
|
|
|
62
66
|
export interface EvolveBodyResult {
|
|
@@ -89,6 +93,7 @@ export interface EvolveBodyDeps {
|
|
|
89
93
|
readEffectiveSkillUsageRecords?: () => SkillUsageRecord[];
|
|
90
94
|
readFileSync?: typeof readFileSync;
|
|
91
95
|
writeFileSync?: (path: string, data: string, encoding: string) => void;
|
|
96
|
+
callViaSubagent?: (options: SubagentCallOptions) => Promise<string>;
|
|
92
97
|
}
|
|
93
98
|
|
|
94
99
|
// ---------------------------------------------------------------------------
|
|
@@ -110,6 +115,19 @@ function createAuditEntry(
|
|
|
110
115
|
};
|
|
111
116
|
}
|
|
112
117
|
|
|
118
|
+
// ---------------------------------------------------------------------------
|
|
119
|
+
// Pipeline defaults — enforced even when the calling agent omits flags
|
|
120
|
+
// ---------------------------------------------------------------------------
|
|
121
|
+
|
|
122
|
+
/** Default teacher model: Opus 4.6 for highest-quality proposals. */
|
|
123
|
+
const DEFAULT_TEACHER_MODEL = "opus";
|
|
124
|
+
|
|
125
|
+
/** Default student model: Haiku for cheap, fast validation gates. */
|
|
126
|
+
const DEFAULT_STUDENT_MODEL = "haiku";
|
|
127
|
+
|
|
128
|
+
/** Default teacher effort: extended thinking for multi-constraint reasoning. */
|
|
129
|
+
const DEFAULT_TEACHER_EFFORT: EffortLevel = "high";
|
|
130
|
+
|
|
113
131
|
// ---------------------------------------------------------------------------
|
|
114
132
|
// Main orchestrator
|
|
115
133
|
// ---------------------------------------------------------------------------
|
|
@@ -124,8 +142,6 @@ export async function evolveBody(
|
|
|
124
142
|
target,
|
|
125
143
|
teacherAgent,
|
|
126
144
|
studentAgent,
|
|
127
|
-
teacherModel,
|
|
128
|
-
studentModel,
|
|
129
145
|
evalSetPath,
|
|
130
146
|
dryRun,
|
|
131
147
|
maxIterations,
|
|
@@ -133,6 +149,11 @@ export async function evolveBody(
|
|
|
133
149
|
fewShotExamples,
|
|
134
150
|
} = options;
|
|
135
151
|
|
|
152
|
+
// Apply pipeline defaults for models/effort when not explicitly provided
|
|
153
|
+
const teacherModel = options.teacherModel ?? DEFAULT_TEACHER_MODEL;
|
|
154
|
+
const studentModel = options.studentModel ?? DEFAULT_STUDENT_MODEL;
|
|
155
|
+
const teacherEffort = options.teacherEffort ?? DEFAULT_TEACHER_EFFORT;
|
|
156
|
+
|
|
136
157
|
// Resolve injectable dependencies
|
|
137
158
|
const _extractFailurePatterns = _deps.extractFailurePatterns ?? extractFailurePatterns;
|
|
138
159
|
const _generateBodyProposal = _deps.generateBodyProposal ?? generateBodyProposal;
|
|
@@ -151,6 +172,7 @@ export async function evolveBody(
|
|
|
151
172
|
});
|
|
152
173
|
const _readFileSync = _deps.readFileSync ?? readFileSync;
|
|
153
174
|
const _writeFileSync = _deps.writeFileSync ?? (await import("node:fs")).writeFileSync;
|
|
175
|
+
const _callViaSubagent = _deps.callViaSubagent ?? callViaSubagent;
|
|
154
176
|
|
|
155
177
|
const auditEntries: EvolutionAuditEntry[] = [];
|
|
156
178
|
|
|
@@ -306,6 +328,7 @@ export async function evolveBody(
|
|
|
306
328
|
skillPath,
|
|
307
329
|
teacherAgent,
|
|
308
330
|
teacherModel,
|
|
331
|
+
teacherEffort,
|
|
309
332
|
);
|
|
310
333
|
} else {
|
|
311
334
|
proposal = await _generateBodyProposal(
|
|
@@ -318,6 +341,7 @@ export async function evolveBody(
|
|
|
318
341
|
teacherModel,
|
|
319
342
|
fewShotExamples,
|
|
320
343
|
executionContext,
|
|
344
|
+
teacherEffort,
|
|
321
345
|
);
|
|
322
346
|
}
|
|
323
347
|
} else if (lastProposal && lastValidation) {
|
|
@@ -327,6 +351,7 @@ export async function evolveBody(
|
|
|
327
351
|
lastValidation,
|
|
328
352
|
teacherAgent,
|
|
329
353
|
teacherModel,
|
|
354
|
+
options.teacherEffort,
|
|
330
355
|
);
|
|
331
356
|
} else {
|
|
332
357
|
break;
|
|
@@ -496,7 +521,63 @@ export async function evolveBody(
|
|
|
496
521
|
}
|
|
497
522
|
}
|
|
498
523
|
|
|
499
|
-
// Step 5:
|
|
524
|
+
// Step 5: Optional evolution-reviewer gate (Gate 4)
|
|
525
|
+
if (options.useReviewer && lastProposal && lastValidation?.improved) {
|
|
526
|
+
try {
|
|
527
|
+
const reviewPrompt = [
|
|
528
|
+
`Review this ${target} evolution proposal for the "${skillName}" skill.`,
|
|
529
|
+
``,
|
|
530
|
+
`Proposal ID: ${lastProposal.proposal_id}`,
|
|
531
|
+
`Skill path: ${skillPath}`,
|
|
532
|
+
`Target: ${target}`,
|
|
533
|
+
`Confidence: ${lastProposal.confidence}`,
|
|
534
|
+
`Validation: ${lastValidation.gates_passed}/${lastValidation.gates_total} gates passed`,
|
|
535
|
+
`Regressions: ${lastValidation.regressions.length > 0 ? lastValidation.regressions.join(", ") : "none"}`,
|
|
536
|
+
``,
|
|
537
|
+
`Original content:`,
|
|
538
|
+
lastProposal.original_body,
|
|
539
|
+
``,
|
|
540
|
+
`Proposed content:`,
|
|
541
|
+
lastProposal.proposed_body,
|
|
542
|
+
``,
|
|
543
|
+
`Rationale: ${lastProposal.rationale}`,
|
|
544
|
+
].join("\n");
|
|
545
|
+
|
|
546
|
+
const reviewOutput = await _callViaSubagent({
|
|
547
|
+
agentName: "evolution-reviewer",
|
|
548
|
+
prompt: reviewPrompt,
|
|
549
|
+
maxTurns: 8,
|
|
550
|
+
allowedTools: ["Read", "Grep", "Glob", "Bash"],
|
|
551
|
+
});
|
|
552
|
+
|
|
553
|
+
const isRejected = /\bREJECT\b/.test(reviewOutput) && !/\bAPPROVE\b/.test(reviewOutput);
|
|
554
|
+
recordAudit(
|
|
555
|
+
lastProposal.proposal_id,
|
|
556
|
+
isRejected ? "rejected" : "validated",
|
|
557
|
+
`Evolution reviewer: ${isRejected ? "REJECTED" : "APPROVED"}`,
|
|
558
|
+
);
|
|
559
|
+
|
|
560
|
+
if (isRejected) {
|
|
561
|
+
return {
|
|
562
|
+
proposal: lastProposal,
|
|
563
|
+
validation: lastValidation,
|
|
564
|
+
deployed: false,
|
|
565
|
+
auditEntries,
|
|
566
|
+
reason: `Evolution reviewer rejected proposal: ${reviewOutput.slice(0, 500)}`,
|
|
567
|
+
};
|
|
568
|
+
}
|
|
569
|
+
} catch (reviewError) {
|
|
570
|
+
// Fail-open: if reviewer crashes, log it and continue to deploy
|
|
571
|
+
const msg = reviewError instanceof Error ? reviewError.message : String(reviewError);
|
|
572
|
+
recordAudit(
|
|
573
|
+
lastProposal.proposal_id,
|
|
574
|
+
"validated",
|
|
575
|
+
`Evolution reviewer failed (fail-open): ${msg}`,
|
|
576
|
+
);
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
// Step 6: Deploy or dry-run
|
|
500
581
|
if (dryRun) {
|
|
501
582
|
return {
|
|
502
583
|
proposal: lastProposal,
|
|
@@ -594,6 +675,8 @@ export async function cliMain(): Promise<void> {
|
|
|
594
675
|
"task-description": { type: "string" },
|
|
595
676
|
"few-shot": { type: "string" },
|
|
596
677
|
"validation-model": { type: "string" },
|
|
678
|
+
"teacher-effort": { type: "string", default: "high" },
|
|
679
|
+
review: { type: "boolean", default: false },
|
|
597
680
|
help: { type: "boolean", default: false },
|
|
598
681
|
},
|
|
599
682
|
strict: true,
|
|
@@ -611,8 +694,8 @@ Options:
|
|
|
611
694
|
--target Evolution target: body, routing (default: body)
|
|
612
695
|
--teacher-agent Teacher agent CLI (claude, codex, etc.)
|
|
613
696
|
--student-agent Student agent CLI for validation
|
|
614
|
-
--teacher-model Model flag for teacher agent
|
|
615
|
-
--student-model Model flag for student agent
|
|
697
|
+
--teacher-model Model flag for teacher agent (default: opus)
|
|
698
|
+
--student-model Model flag for student agent (default: haiku)
|
|
616
699
|
--eval-set Path to eval set JSON
|
|
617
700
|
--dry-run Validate without deploying
|
|
618
701
|
--max-iterations Max refinement iterations (default: 3)
|
|
@@ -620,6 +703,8 @@ Options:
|
|
|
620
703
|
--task-description Optional task description context
|
|
621
704
|
--few-shot Comma-separated paths to example skill files
|
|
622
705
|
--validation-model Model for trigger-check validation calls (overrides --student-model for validation)
|
|
706
|
+
--teacher-effort Effort level for teacher LLM: low, medium, high, max (default: high)
|
|
707
|
+
--review Run evolution-reviewer subagent before deployment (Gate 4)
|
|
623
708
|
--help Show this help message`);
|
|
624
709
|
process.exit(0);
|
|
625
710
|
}
|
|
@@ -669,6 +754,8 @@ Options:
|
|
|
669
754
|
fewShotExamples,
|
|
670
755
|
gradingResults,
|
|
671
756
|
validationModel: values["validation-model"],
|
|
757
|
+
teacherEffort: (values["teacher-effort"] as EffortLevel) ?? "high",
|
|
758
|
+
useReviewer: values.review ?? false,
|
|
672
759
|
});
|
|
673
760
|
|
|
674
761
|
console.log(JSON.stringify(result, null, 2));
|
|
@@ -37,7 +37,6 @@ import type {
|
|
|
37
37
|
SkillUsageRecord,
|
|
38
38
|
} from "../types.js";
|
|
39
39
|
import { parseFrontmatter, replaceFrontmatterDescription } from "../utils/frontmatter.js";
|
|
40
|
-
|
|
41
40
|
import { createEvolveTUI } from "../utils/tui.js";
|
|
42
41
|
import { appendAuditEntry } from "./audit.js";
|
|
43
42
|
import { checkConstitution } from "./constitutional.js";
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import type { BodyEvolutionProposal, EvolutionTarget, FailurePattern } from "../types.js";
|
|
10
|
-
import { callLlm, stripMarkdownFences } from "../utils/llm-call.js";
|
|
10
|
+
import { type EffortLevel, callLlm, stripMarkdownFences } from "../utils/llm-call.js";
|
|
11
11
|
|
|
12
12
|
// ---------------------------------------------------------------------------
|
|
13
13
|
// System prompt
|
|
@@ -160,6 +160,7 @@ export async function generateBodyProposal(
|
|
|
160
160
|
modelFlag?: string,
|
|
161
161
|
fewShotExamples?: string[],
|
|
162
162
|
executionContext?: ExecutionContext,
|
|
163
|
+
effort?: EffortLevel,
|
|
163
164
|
): Promise<BodyEvolutionProposal> {
|
|
164
165
|
const prompt = buildBodyGenerationPrompt(
|
|
165
166
|
currentContent,
|
|
@@ -169,7 +170,7 @@ export async function generateBodyProposal(
|
|
|
169
170
|
fewShotExamples,
|
|
170
171
|
executionContext,
|
|
171
172
|
);
|
|
172
|
-
const rawResponse = await callLlm(BODY_GENERATOR_SYSTEM, prompt, agent, modelFlag);
|
|
173
|
+
const rawResponse = await callLlm(BODY_GENERATOR_SYSTEM, prompt, agent, modelFlag, effort);
|
|
173
174
|
const { proposed_body, rationale, confidence } = parseBodyProposalResponse(rawResponse);
|
|
174
175
|
|
|
175
176
|
return {
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import type { BodyEvolutionProposal, EvolutionTarget, FailurePattern } from "../types.js";
|
|
9
|
-
import { callLlm, stripMarkdownFences } from "../utils/llm-call.js";
|
|
9
|
+
import { type EffortLevel, callLlm, stripMarkdownFences } from "../utils/llm-call.js";
|
|
10
10
|
|
|
11
11
|
// ---------------------------------------------------------------------------
|
|
12
12
|
// System prompt
|
|
@@ -139,6 +139,7 @@ export async function generateRoutingProposal(
|
|
|
139
139
|
skillPath: string,
|
|
140
140
|
agent: string,
|
|
141
141
|
modelFlag?: string,
|
|
142
|
+
effort?: EffortLevel,
|
|
142
143
|
): Promise<BodyEvolutionProposal> {
|
|
143
144
|
const prompt = buildRoutingProposalPrompt(
|
|
144
145
|
currentRouting,
|
|
@@ -147,7 +148,7 @@ export async function generateRoutingProposal(
|
|
|
147
148
|
missedQueries,
|
|
148
149
|
skillName,
|
|
149
150
|
);
|
|
150
|
-
const rawResponse = await callLlm(ROUTING_PROPOSER_SYSTEM, prompt, agent, modelFlag);
|
|
151
|
+
const rawResponse = await callLlm(ROUTING_PROPOSER_SYSTEM, prompt, agent, modelFlag, effort);
|
|
151
152
|
const { proposed_routing, rationale, confidence } = parseRoutingProposalResponse(rawResponse);
|
|
152
153
|
|
|
153
154
|
return {
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import type { BodyEvolutionProposal, BodyValidationResult } from "../types.js";
|
|
9
|
-
import { callLlm, stripMarkdownFences } from "../utils/llm-call.js";
|
|
9
|
+
import { type EffortLevel, callLlm, stripMarkdownFences } from "../utils/llm-call.js";
|
|
10
10
|
|
|
11
11
|
// ---------------------------------------------------------------------------
|
|
12
12
|
// System prompt
|
|
@@ -118,6 +118,7 @@ export async function refineBodyProposal(
|
|
|
118
118
|
validationResult: BodyValidationResult,
|
|
119
119
|
agent: string,
|
|
120
120
|
modelFlag?: string,
|
|
121
|
+
effort?: EffortLevel,
|
|
121
122
|
): Promise<BodyEvolutionProposal> {
|
|
122
123
|
const prompt = buildRefinementPrompt(
|
|
123
124
|
proposal.proposed_body,
|
|
@@ -126,7 +127,7 @@ export async function refineBodyProposal(
|
|
|
126
127
|
validationResult.regressions,
|
|
127
128
|
);
|
|
128
129
|
|
|
129
|
-
const rawResponse = await callLlm(BODY_REFINER_SYSTEM, prompt, agent, modelFlag);
|
|
130
|
+
const rawResponse = await callLlm(BODY_REFINER_SYSTEM, prompt, agent, modelFlag, effort);
|
|
130
131
|
const { refined_body, changes_made, confidence } = parseRefinementResponse(rawResponse);
|
|
131
132
|
|
|
132
133
|
return {
|
package/cli/selftune/export.ts
CHANGED
|
@@ -884,6 +884,14 @@ Options:
|
|
|
884
884
|
}
|
|
885
885
|
writeFileSync(outputPath, JSON.stringify(result, null, 2), "utf-8");
|
|
886
886
|
|
|
887
|
+
// Persist to SQLite for upload staging (fail-open)
|
|
888
|
+
try {
|
|
889
|
+
const { writeGradingResultToDb } = await import("../localdb/direct-write.js");
|
|
890
|
+
writeGradingResultToDb(result);
|
|
891
|
+
} catch {
|
|
892
|
+
// fail-open: grading file is already written above
|
|
893
|
+
}
|
|
894
|
+
|
|
887
895
|
printSummary(result);
|
|
888
896
|
console.log(`\nWrote ${outputPath}`);
|
|
889
897
|
}
|
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
|
|
17
17
|
import { existsSync, readFileSync } from "node:fs";
|
|
18
18
|
import { basename, dirname, join } from "node:path";
|
|
19
|
-
import { EVOLUTION_AUDIT_LOG, SELFTUNE_CONFIG_DIR } from "../constants.js";
|
|
20
19
|
|
|
20
|
+
import { EVOLUTION_AUDIT_LOG, SELFTUNE_CONFIG_DIR } from "../constants.js";
|
|
21
21
|
import type { PreToolUsePayload } from "../types.js";
|
|
22
22
|
import { readJsonl } from "../utils/jsonl.js";
|
|
23
23
|
|
|
@@ -9,9 +9,9 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import { execSync } from "node:child_process";
|
|
12
|
-
import {
|
|
13
|
-
import { CANONICAL_LOG, ORCHESTRATE_LOCK, TELEMETRY_LOG } from "../constants.js";
|
|
12
|
+
import { readFileSync } from "node:fs";
|
|
14
13
|
|
|
14
|
+
import { CANONICAL_LOG, ORCHESTRATE_LOCK, TELEMETRY_LOG } from "../constants.js";
|
|
15
15
|
import {
|
|
16
16
|
appendCanonicalRecords,
|
|
17
17
|
buildCanonicalExecutionFact,
|
|
@@ -25,6 +25,22 @@ import { parseTranscript } from "../utils/transcript.js";
|
|
|
25
25
|
|
|
26
26
|
const LOCK_STALE_MS = 30 * 60 * 1000;
|
|
27
27
|
|
|
28
|
+
interface ReactiveSpawnDeps {
|
|
29
|
+
spawnOrchestrate?: () => boolean;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function hasFreshOrchestrateLock(lockPath: string): boolean {
|
|
33
|
+
try {
|
|
34
|
+
const lockContent = readFileSync(lockPath, "utf8");
|
|
35
|
+
const lock = JSON.parse(lockContent) as { timestamp?: string };
|
|
36
|
+
if (typeof lock.timestamp !== "string") return false;
|
|
37
|
+
const lockAge = Date.now() - new Date(lock.timestamp).getTime();
|
|
38
|
+
return Number.isFinite(lockAge) && lockAge < LOCK_STALE_MS;
|
|
39
|
+
} catch {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
28
44
|
/**
|
|
29
45
|
* Check for pending improvement signals and spawn a focused orchestrate run
|
|
30
46
|
* in the background if warranted. Fire-and-forget — the hook exits immediately.
|
|
@@ -33,6 +49,7 @@ const LOCK_STALE_MS = 30 * 60 * 1000;
|
|
|
33
49
|
*/
|
|
34
50
|
export async function maybeSpawnReactiveOrchestrate(
|
|
35
51
|
lockPath: string = ORCHESTRATE_LOCK,
|
|
52
|
+
deps: ReactiveSpawnDeps = {},
|
|
36
53
|
): Promise<boolean> {
|
|
37
54
|
try {
|
|
38
55
|
// Read pending signals from SQLite (dynamic import to reduce hook startup cost)
|
|
@@ -42,48 +59,25 @@ export async function maybeSpawnReactiveOrchestrate(
|
|
|
42
59
|
const pending = queryImprovementSignals(db, false);
|
|
43
60
|
if (pending.length === 0) return false;
|
|
44
61
|
|
|
45
|
-
//
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
fd = openSync(lockPath, "wx");
|
|
49
|
-
writeFileSync(fd, JSON.stringify({ timestamp: new Date().toISOString(), pid: process.pid }));
|
|
50
|
-
closeSync(fd);
|
|
51
|
-
} catch (lockErr: unknown) {
|
|
52
|
-
// Lock exists — check if stale
|
|
53
|
-
if ((lockErr as NodeJS.ErrnoException).code === "EEXIST") {
|
|
54
|
-
try {
|
|
55
|
-
const lockContent = readFileSync(lockPath, "utf8");
|
|
56
|
-
const lock = JSON.parse(lockContent);
|
|
57
|
-
const lockAge = Date.now() - new Date(lock.timestamp).getTime();
|
|
58
|
-
if (lockAge < LOCK_STALE_MS) return false; // Active lock, skip
|
|
59
|
-
// Stale lock — override
|
|
60
|
-
writeFileSync(
|
|
61
|
-
lockPath,
|
|
62
|
-
JSON.stringify({ timestamp: new Date().toISOString(), pid: process.pid }),
|
|
63
|
-
);
|
|
64
|
-
} catch {
|
|
65
|
-
return false; // Can't read lock, skip
|
|
66
|
-
}
|
|
67
|
-
} else {
|
|
68
|
-
return false;
|
|
69
|
-
}
|
|
70
|
-
}
|
|
62
|
+
// Do not pre-claim the orchestrate lock here. The spawned process must
|
|
63
|
+
// acquire its own lock or it will immediately self-block on startup.
|
|
64
|
+
if (hasFreshOrchestrateLock(lockPath)) return false;
|
|
71
65
|
|
|
72
66
|
// Spawn orchestrate in background (fire-and-forget)
|
|
73
67
|
try {
|
|
74
|
-
const
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
68
|
+
const spawnOrchestrate =
|
|
69
|
+
deps.spawnOrchestrate ??
|
|
70
|
+
(() => {
|
|
71
|
+
const proc = Bun.spawn(["selftune", "orchestrate", "--max-skills", "2"], {
|
|
72
|
+
stdout: "ignore",
|
|
73
|
+
stderr: "ignore",
|
|
74
|
+
stdin: "ignore",
|
|
75
|
+
});
|
|
76
|
+
proc.unref();
|
|
77
|
+
return true;
|
|
78
|
+
});
|
|
79
|
+
if (!spawnOrchestrate()) return false;
|
|
80
80
|
} catch {
|
|
81
|
-
// Spawn failed — release our lock
|
|
82
|
-
try {
|
|
83
|
-
unlinkSync(lockPath);
|
|
84
|
-
} catch {
|
|
85
|
-
/* ignore */
|
|
86
|
-
}
|
|
87
81
|
return false;
|
|
88
82
|
}
|
|
89
83
|
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
import { existsSync, readFileSync } from "node:fs";
|
|
15
15
|
import { basename, dirname } from "node:path";
|
|
16
|
+
|
|
16
17
|
import { CANONICAL_LOG, SKILL_LOG } from "../constants.js";
|
|
17
18
|
import {
|
|
18
19
|
appendCanonicalRecord,
|
|
@@ -24,7 +25,6 @@ import {
|
|
|
24
25
|
getLatestPromptIdentity,
|
|
25
26
|
} from "../normalization.js";
|
|
26
27
|
import type { PostToolUsePayload, SkillUsageRecord } from "../types.js";
|
|
27
|
-
|
|
28
28
|
import { classifySkillPath } from "../utils/skill-discovery.js";
|
|
29
29
|
import { getLastUserMessage } from "../utils/transcript.js";
|
|
30
30
|
|
package/cli/selftune/index.ts
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
* selftune sync — Sync source-truth telemetry across supported agents
|
|
11
11
|
* selftune orchestrate — Run autonomous core loop (sync → status → evolve → watch)
|
|
12
12
|
* selftune init — Initialize agent identity and config
|
|
13
|
+
* selftune uninstall — Clean removal of all selftune data and config
|
|
13
14
|
* selftune status — Show skill health summary
|
|
14
15
|
* selftune watch — Monitor post-deploy skill health
|
|
15
16
|
* selftune doctor — Run health checks
|
|
@@ -44,6 +45,7 @@ Commands:
|
|
|
44
45
|
sync Sync source-truth telemetry across supported agents
|
|
45
46
|
orchestrate Run autonomous core loop (sync → status → evolve → watch)
|
|
46
47
|
init Initialize agent identity and config
|
|
48
|
+
uninstall Clean removal of all selftune data and config
|
|
47
49
|
status Show skill health summary
|
|
48
50
|
watch Monitor post-deploy skill health
|
|
49
51
|
doctor Run health checks
|
|
@@ -338,6 +340,11 @@ Run 'selftune eval <action> --help' for action-specific options.`);
|
|
|
338
340
|
await cliMain();
|
|
339
341
|
break;
|
|
340
342
|
}
|
|
343
|
+
case "uninstall": {
|
|
344
|
+
const { cliMain } = await import("./uninstall.js");
|
|
345
|
+
await cliMain();
|
|
346
|
+
break;
|
|
347
|
+
}
|
|
341
348
|
case "contribute": {
|
|
342
349
|
const { cliMain } = await import("./contribute/contribute.js");
|
|
343
350
|
await cliMain();
|
|
@@ -464,7 +471,7 @@ Run 'selftune cron <subcommand> --help' for subcommand-specific options.`);
|
|
|
464
471
|
}
|
|
465
472
|
case "sync": {
|
|
466
473
|
const { cliMain } = await import("./sync.js");
|
|
467
|
-
cliMain();
|
|
474
|
+
await cliMain();
|
|
468
475
|
break;
|
|
469
476
|
}
|
|
470
477
|
case "workflows": {
|
|
@@ -606,9 +613,8 @@ Output:
|
|
|
606
613
|
const { readAlphaIdentity } = await import("./alpha-identity.js");
|
|
607
614
|
const { getDb } = await import("./localdb/db.js");
|
|
608
615
|
const { runUploadCycle } = await import("./alpha-upload/index.js");
|
|
609
|
-
const { getSelftuneVersion, readConfiguredAgentType } =
|
|
610
|
-
"./utils/selftune-meta.js"
|
|
611
|
-
);
|
|
616
|
+
const { getSelftuneVersion, readConfiguredAgentType } =
|
|
617
|
+
await import("./utils/selftune-meta.js");
|
|
612
618
|
|
|
613
619
|
const identity = readAlphaIdentity(SELFTUNE_CONFIG_PATH);
|
|
614
620
|
if (!identity?.enrolled) {
|
|
@@ -670,36 +676,39 @@ Output:
|
|
|
670
676
|
}
|
|
671
677
|
case "relink": {
|
|
672
678
|
const { SELFTUNE_CONFIG_PATH } = await import("./constants.js");
|
|
673
|
-
const { readAlphaIdentity, writeAlphaIdentity, generateUserId } =
|
|
674
|
-
"./alpha-identity.js"
|
|
675
|
-
|
|
676
|
-
|
|
679
|
+
const { readAlphaIdentity, writeAlphaIdentity, generateUserId } =
|
|
680
|
+
await import("./alpha-identity.js");
|
|
681
|
+
const { buildVerificationUrl, pollDeviceCode, requestDeviceCode, tryOpenUrl } =
|
|
682
|
+
await import("./auth/device-code.js");
|
|
677
683
|
const { chmodSync } = await import("node:fs");
|
|
678
684
|
|
|
679
685
|
const existingIdentity = readAlphaIdentity(SELFTUNE_CONFIG_PATH);
|
|
680
686
|
process.stderr.write("[alpha relink] Starting device-code authentication flow...\n");
|
|
681
687
|
|
|
682
688
|
const grant = await requestDeviceCode();
|
|
689
|
+
const verificationUrlWithCode = buildVerificationUrl(
|
|
690
|
+
grant.verification_url,
|
|
691
|
+
grant.user_code,
|
|
692
|
+
);
|
|
683
693
|
|
|
684
694
|
console.log(
|
|
685
695
|
JSON.stringify({
|
|
686
696
|
level: "info",
|
|
687
697
|
code: "device_code_issued",
|
|
688
698
|
verification_url: grant.verification_url,
|
|
699
|
+
verification_url_with_code: verificationUrlWithCode,
|
|
689
700
|
user_code: grant.user_code,
|
|
690
701
|
expires_in: grant.expires_in,
|
|
691
|
-
message: `Open ${
|
|
702
|
+
message: `Open ${verificationUrlWithCode} to approve.`,
|
|
692
703
|
}),
|
|
693
704
|
);
|
|
694
705
|
|
|
695
706
|
// Try to open browser
|
|
696
|
-
|
|
697
|
-
const url = `${grant.verification_url}?code=${grant.user_code}`;
|
|
698
|
-
Bun.spawn(["open", url], { stdout: "ignore", stderr: "ignore" });
|
|
707
|
+
if (tryOpenUrl(verificationUrlWithCode)) {
|
|
699
708
|
process.stderr.write("[alpha relink] Browser opened. Waiting for approval...\n");
|
|
700
|
-
}
|
|
709
|
+
} else {
|
|
701
710
|
process.stderr.write(
|
|
702
|
-
|
|
711
|
+
`[alpha relink] Could not open browser. Visit ${verificationUrlWithCode} manually.\n`,
|
|
703
712
|
);
|
|
704
713
|
}
|
|
705
714
|
|
|
@@ -25,6 +25,7 @@ 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
|
+
|
|
28
29
|
import { CANONICAL_LOG, QUERY_LOG, SKILL_LOG, TELEMETRY_LOG } from "../constants.js";
|
|
29
30
|
import {
|
|
30
31
|
appendCanonicalRecords,
|
|
@@ -25,6 +25,7 @@ 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
|
+
|
|
28
29
|
import { CANONICAL_LOG, QUERY_LOG, SKILL_LOG, TELEMETRY_LOG } from "../constants.js";
|
|
29
30
|
import {
|
|
30
31
|
appendCanonicalRecords,
|