feat(skills): rebuild the skill system for the multi-user model
Nightly Build / build (push) Successful in 8m6s
Nightly Build / build (push) Successful in 8m6s
Per blueprint/skill-project.md: the old single-namespace, hand-maintained
index is gone, replaced by a read-only, two-scope tree whose index is a
runtime function of its content.
- skills/ index generated at runtime (crates/skald-core/src/skills/:
inventory, install, validate, watch), injected through the new
<!-- SKILLS_LIST --> placeholder in AGENT.md (agents/common/skills.md);
meta.json inject_skills flag removed. 11 chat/task agents carry the
include, the 4 system agents do not.
- Two trees, both read-only in both directions: skills/shared/{id} (the
group's) and skills/{username}/{id} (one member's own, on the stable
userid). The root is closed too: UserFs::SkillMounts + RouteError (alias
probe, plain-denied paths, no home fallback) and a per-user
.skills-root/{userid} container mount with the two scope mounts nested
inside, plus the fifth self-heal axis (skills_mounted).
- Agent verbs: skill_register/skill_delete (Config group, global scope
behind the new skill.manage capability), fetch_repo for public repos,
list_items(type="skills"); reads are plain read_file on the printed
path. Seeded @fs_read skills/* allow.
- Freshness: a digest-gated watcher on the two trees emits
SystemEvent::SkillsChanged, whose subscriber rebuilds the frozen prompt
prefix via Skald::invalidate_prompt_prefix; in-process writers invalidate
directly.
- The build ships no skills: the three bundled skills (ics2json,
mcp-builder, skill-creator) and skills/index.md are removed, skills/ is
instance data (gitignored, not packaged, no longer pruned by update.sh).
- Docs: skills.md, agents.md, shared-folders.md added; docs/index.md and
agents/README.md updated.
This commit is contained in:
@@ -196,6 +196,33 @@ impl Skald {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Rebuilds the frozen system prefix of the conversations a skills change made
|
||||
/// wrong (blueprint §6).
|
||||
///
|
||||
/// The prefix is normally left alone until its conversation has been idle for
|
||||
/// twenty minutes, which is right for an *injected file* and wrong for the
|
||||
/// **index**: an admin who installs a skill and then asks the assistant to use
|
||||
/// it would be told, at length and in good faith, that no such skill exists.
|
||||
///
|
||||
/// Called **directly** by the two skill tools, not through the system bus.
|
||||
/// Whoever writes a skill through a tool is inside this process and can say so;
|
||||
/// the bus (`SkillsChanged`) is for the other case — someone editing files on
|
||||
/// the box — where nothing in-process knows. A miss here is not a lost event,
|
||||
/// it is a user who is simply not logged in and whose next login builds a fresh
|
||||
/// prefix anyway.
|
||||
pub async fn invalidate_prompt_prefix(&self, scope: crate::skills::PromptScope) {
|
||||
for ctx in self.rt_user_contexts().all_live().await {
|
||||
let concerns = match &scope {
|
||||
// The group's tree is in everybody's index.
|
||||
crate::skills::PromptScope::Everyone => true,
|
||||
crate::skills::PromptScope::User(id) => *id == ctx.user_id,
|
||||
};
|
||||
if concerns {
|
||||
ctx.sessions.loop_runtime().invalidate_prefixes();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Refresh every live user's global-connector access set in place — call after an
|
||||
/// admin enables/deletes a global connector or changes who may use it, so running
|
||||
/// sessions see it without a restart (the §7 MCP twin of the §6 fs remount). The
|
||||
|
||||
Reference in New Issue
Block a user