Author runnable skill bodies via OpenAI-compatible codegen model
create_skill now writes a real predict/act body: the small decision model still authors title + description (engine.generate), then a larger OpenAI-compatible model (default qwen38-iq3s) writes the runnable code against the SKILL.md contract. Bodies persist to data/skills/<cat>/<name>.py, are hot-loaded via importlib, merged into the running tree, and the request re-dispatches to the new leaf. The dashboard decision-flow view shows the title/description with a writing badge while the body is being written. Codegen failure degrades to a navigable stub.
This commit is contained in:
@@ -121,6 +121,11 @@ function eventRow(evt) {
|
||||
div.textContent = `queued (${evt.label})`;
|
||||
} else if (evt.kind === "preempted") {
|
||||
div.textContent = `preempted ${evt.preempted}`;
|
||||
} else if (evt.kind === "skill_writing") {
|
||||
div.textContent = `writing skill ${evt.skill} — ${evt.description} (${evt.model || "codegen"})`;
|
||||
} else if (evt.kind === "skill_created") {
|
||||
div.textContent = `created ${evt.skill}${evt.written ? " (body written)" : " (stub)"}`;
|
||||
div.title = evt.body || evt.description || "";
|
||||
}
|
||||
return div;
|
||||
}
|
||||
@@ -327,6 +332,36 @@ function eventNode(evt) {
|
||||
body.textContent = `interrupted ${evt.preempted}, requeued with state`;
|
||||
} else if (evt.kind === "dropped") {
|
||||
body.textContent = evt.reason || "";
|
||||
} else if (evt.kind === "skill_writing") {
|
||||
node.classList.add("writing");
|
||||
const title = document.createElement("div");
|
||||
title.className = "skill-title";
|
||||
title.textContent = evt.skill;
|
||||
body.appendChild(title);
|
||||
const desc = document.createElement("div");
|
||||
desc.className = "muted";
|
||||
desc.textContent = evt.description || "";
|
||||
body.appendChild(desc);
|
||||
const badge = document.createElement("span");
|
||||
badge.className = "writing-badge";
|
||||
badge.textContent = "writing skill body…";
|
||||
node.appendChild(badge);
|
||||
} else if (evt.kind === "skill_created") {
|
||||
const title = document.createElement("div");
|
||||
title.className = "skill-title";
|
||||
title.textContent = evt.skill;
|
||||
body.appendChild(title);
|
||||
const desc = document.createElement("div");
|
||||
desc.className = "muted";
|
||||
desc.textContent = evt.description || "";
|
||||
body.appendChild(desc);
|
||||
if (evt.body) {
|
||||
const p = document.createElement("div");
|
||||
p.className = "muted";
|
||||
p.textContent = `body: ${evt.body}`;
|
||||
node.appendChild(p);
|
||||
}
|
||||
node.classList.add(evt.written ? "ok" : "stub");
|
||||
} else {
|
||||
body.textContent = evt.summary || evt.text || "";
|
||||
}
|
||||
|
||||
@@ -283,6 +283,26 @@ select {
|
||||
|
||||
.node.event-node .evt-kind { text-transform: uppercase; color: var(--muted); font-size: 10px; }
|
||||
|
||||
.node.event-node .skill-title { font-weight: 700; }
|
||||
.node.event-node.writing { border-left: 3px solid var(--warn); }
|
||||
.node.event-node.stub { border-left: 3px solid var(--muted); }
|
||||
|
||||
.writing-badge {
|
||||
display: inline-block;
|
||||
margin-top: 8px;
|
||||
padding: 2px 8px;
|
||||
border: 1px solid var(--warn);
|
||||
border-radius: 10px;
|
||||
color: var(--warn);
|
||||
font-size: 10px;
|
||||
animation: writing-pulse 1.2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes writing-pulse {
|
||||
0%, 100% { opacity: 1; }
|
||||
50% { opacity: 0.35; }
|
||||
}
|
||||
|
||||
/* ---------- inspector / tree / status ---------- */
|
||||
|
||||
.right { border-right: none; }
|
||||
|
||||
Reference in New Issue
Block a user