agent-skillboard 0.3.1 → 0.3.3

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/src/route.mjs CHANGED
@@ -4,15 +4,13 @@ import {
4
4
  guardCommand,
5
5
  overlapResolutionForRoute,
6
6
  policyMemoryForRoute,
7
- policyMemoryForV2Route,
8
7
  postUsePolicySuggestionForCapabilityRoute,
9
- postUsePolicySuggestionForV2Route,
10
8
  recommendationReason,
11
9
  routeCandidate,
12
10
  selectedRouteSkill,
13
11
  usageDisclosure
14
12
  } from "./route-advisory.mjs";
15
- import { confidenceFor, possibleWorkflowSkills, selectRoute, selectV2Route } from "./route-selection.mjs";
13
+ import { confidenceFor, possibleWorkflowSkills, selectRoute } from "./route-selection.mjs";
16
14
 
17
15
  export function routeSkill(workspace, options) {
18
16
  const intent = options.intent.trim();
@@ -103,57 +101,42 @@ export function routeSkill(workspace, options) {
103
101
  }
104
102
 
105
103
  function routeV2Skill(workspace, options, intent) {
106
- const { candidates, selected } = selectV2Route(workspace, intent, options.agent);
107
- if (selected === undefined) {
108
- return {
109
- ok: true, intent, workflow: null, agent: options.agent ?? null,
110
- matched_capability: null, matched_skill: null, match_source: "none", confidence: "none",
111
- matched_terms: [], recommendation_reason: "No enabled skill installed for this agent matched this request.",
112
- recommended_skill: null, fallback_skills: [], route_candidates: [], overlap_resolution: null,
113
- policy_memory: null, guard_command: null, guard: null, usage_disclosure: null,
114
- post_use_policy_suggestion: null, possible_skills: [], candidates: []
115
- };
116
- }
117
- const routed = candidates.map((candidate) => ({
118
- skill: candidate.skill.id,
119
- role: candidate.explicit ? "matched" : "alternative",
120
- guard: candidate.guard
121
- }));
122
- const matchedTerms = [...new Set([...selected.matchedIntents, ...selected.metadataMatches])];
123
- const contextName = options.agent === undefined ? "the current agent" : `agent ${options.agent}`;
124
- const recommended = routed[0];
125
- const overlapResolution = overlapResolutionForRoute({
126
- matchedCapability: intent,
127
- recommended,
128
- routedSkills: routed,
129
- workflowName: contextName
130
- });
131
- const policyMemory = policyMemoryForV2Route({ intent, selected, candidates, workflowName: undefined });
132
- const postUsePolicySuggestion = postUsePolicySuggestionForV2Route({
133
- intent, selected, candidates, workflowName: undefined, options, policyMemory
134
- });
104
+ const possibleSkills = workspace.skills
105
+ .filter((skill) => canUseSkill(workspace, skill.id, undefined, options.agent).allowed)
106
+ .map((skill) => v2ModelSkill(workspace, skill))
107
+ .sort((left, right) => left.id.localeCompare(right.id));
108
+ const modelSelectionRequired = possibleSkills.length > 0;
135
109
  return {
136
110
  ok: true, intent, workflow: null, agent: options.agent ?? null,
137
- matched_capability: null,
138
- matched_skill: selected.skill.id,
139
- match_source: selected.explicit
140
- ? "explicit-skill"
141
- : selected.matchedIntents.length > 0
142
- ? "skill-preference"
143
- : "skill-metadata",
144
- confidence: confidenceFor(selected.score),
145
- matched_terms: matchedTerms,
146
- recommendation_reason: `Recommended ${selected.skill.id} because it is enabled, installed for this agent, and best matches the request.`,
147
- recommended_skill: selected.skill.id,
148
- fallback_skills: candidates.slice(1).map((candidate) => candidate.skill.id),
149
- route_candidates: routed.map((entry, index) => routeCandidate(entry, index === 0)),
150
- overlap_resolution: overlapResolution, policy_memory: policyMemory,
151
- guard_command: guardCommand(selected.skill.id, undefined, options),
152
- guard: selected.guard,
153
- usage_disclosure: usageDisclosure(selected.skill.id, policyMemory),
154
- post_use_policy_suggestion: postUsePolicySuggestion,
155
- possible_skills: routed.map((entry) => ({ id: entry.skill, allowed: true })),
156
- candidates: candidates.map((candidate) => ({ skill_id: candidate.skill.id, score: candidate.score }))
111
+ selection_mode: "model",
112
+ model_selection_required: modelSelectionRequired,
113
+ matched_capability: null, matched_skill: null, match_source: "none",
114
+ confidence: "none", matched_terms: [],
115
+ recommendation_reason: modelSelectionRequired
116
+ ? "SkillBoard does not interpret v2 request text. The active model should choose from the eligible skill descriptions and saved preferences, or use no skill when none fits."
117
+ : "No enabled skill is installed for the selected agent.",
118
+ recommended_skill: null, fallback_skills: [], route_candidates: [],
119
+ overlap_resolution: null, policy_memory: null,
120
+ guard_command: null, guard: null, usage_disclosure: null,
121
+ post_use_policy_suggestion: null,
122
+ possible_skills: possibleSkills,
123
+ candidates: []
124
+ };
125
+ }
126
+
127
+ function v2ModelSkill(workspace, skill) {
128
+ const inventory = workspace.inventory?.skills?.find((candidate) => candidate.id === skill.id);
129
+ const installed = workspace.installedSkills.find((candidate) => (
130
+ candidate.id === skill.id
131
+ || (typeof skill.path === "string" && candidate.path === skill.path)
132
+ ));
133
+ return {
134
+ id: skill.id,
135
+ name: installed?.name ?? inventory?.name ?? skill.id,
136
+ description: installed?.description ?? inventory?.description ?? null,
137
+ path: installed?.path ?? inventory?.path ?? null,
138
+ preference: skill.preference ?? null,
139
+ allowed: true
157
140
  };
158
141
  }
159
142
 
@@ -0,0 +1,43 @@
1
+ import { dirname, join, resolve } from "node:path";
2
+ import { canAutomaticallyMigrateV2 } from "./migration/automatic-v2.mjs";
3
+ import { migrateV2 } from "./migration/v2-transaction.mjs";
4
+
5
+ export async function upgradeLegacyUserPolicy(options) {
6
+ if (options.inventoryPath !== null) {
7
+ return { status: "current", inventoryPath: options.inventoryPath, artifacts: [] };
8
+ }
9
+
10
+ const inventoryPath = resolve(options.home, ".skillboard", "inventory.json");
11
+ const migrationOptions = {
12
+ configPath: options.configPath,
13
+ inventoryPath,
14
+ failpoint: options.failpoint
15
+ };
16
+ const preview = await migrateV2({ ...migrationOptions, apply: false });
17
+ const observed = new Set(options.observedSkillIds);
18
+ const unobservedSkillIds = preview.skills.map(({ id }) => id).filter((id) => !observed.has(id));
19
+ if (!canAutomaticallyMigrateV2(preview) || unobservedSkillIds.length > 0) {
20
+ return {
21
+ status: "decision-required",
22
+ inventoryPath: null,
23
+ artifacts: [],
24
+ preview,
25
+ unobservedSkillIds
26
+ };
27
+ }
28
+
29
+ const applied = await migrateV2({
30
+ ...migrationOptions,
31
+ apply: true,
32
+ expectedInputSha256: preview.input_sha256
33
+ });
34
+ const directory = dirname(options.configPath);
35
+ const artifactNames = [applied.backup, applied.manifest, applied.inventory_backup].filter(Boolean);
36
+ return {
37
+ status: "upgraded",
38
+ inventoryPath,
39
+ backupPath: join(directory, applied.backup),
40
+ artifacts: artifactNames.map((name) => join(directory, name)),
41
+ report: applied
42
+ };
43
+ }