agnets done

This commit is contained in:
2026-08-28 11:02:02 +05:30
parent ab9adde6cd
commit eaf08e061d
58 changed files with 3506 additions and 4308 deletions

View File

@@ -44,7 +44,19 @@ const PER_SKILL = 3;
const TOTAL = 4;
/**
* The chips a page's skills offer.
* The chips a page's skills *declare*.
*
* NOT what the panel renders, and no longer on the production path. Which
* suggestions Owliver offers is decided by `GET /api/v1/owliver/suggestions` —
* the backend filters against the caller's role and ranks against the rows in
* PostgreSQL, and `lib/skills/serverSuggestions.js` turns the reply into chips.
* Nothing in `src/` ranks a suggestion any more.
*
* What is left here is the reading of a *definition*: given a skill file, what
* did its author write under `owliver.suggestions`, and which of those name a
* capability the skill actually offers. That is a question about the Markdown
* rather than about the workspace, and it is what the editor's preview and the
* definition checks in `scripts/skill-check.mjs` ask.
*
* Capped deliberately. A workspace with six skills attached would otherwise
* bury the page's own suggestions under twenty of them, and a suggestion nobody
@@ -365,52 +377,3 @@ export function presentable(data) {
export const CAPABILITY_SUMMARIES = OWLIVER_CAPABILITIES.map(
({ id, label, summary }) => ({ id, label, summary })
);
/* ── Suggestions for a record that has just appeared ────────────────────── */
/**
* What can now be asked about a record the conversation just produced.
*
* Creating a position is the moment "who could do this?" becomes worth asking,
* and the panel is the only thing that knows a position now exists. Rather than
* naming a skill to offer — which would put a candidate-matching feature inside
* the position-creation flow — this asks the registry the general question: of
* the skills attached to this page, which declare a capability whose reading is
* *about one position*? Those are exactly the ones that can say something about
* the record just made.
*
* The record's own title is appended to each prompt, so the answer resolves
* against it directly and the reader is never asked to pick from a list that
* includes the position they are looking at. Nothing is named here: a skill
* added tomorrow that reads a position is offered on the same terms.
*/
export function suggestionsForPosition(contextId, disabled = [], customSources = [], position) {
if (!position?.title) return [];
const chips = [];
for (const skill of owliverSkillsForContext(contextId, disabled, customSources)) {
/* Only capabilities that read one position — a workspace-wide reading has
nothing to do with the record that was just created. */
const scoped = skill.owliver.capabilities.filter(
(capability) => skill.owliver.responses[capability]?.context === 'positionId'
);
if (!scoped.length) continue;
const offered = skill.owliver.suggestions
.filter((s) => !s.capability || scoped.includes(s.capability))
.slice(0, 1)
.map((s) => ({
label: s.label,
/* Named, so the reading resolves against this position rather than
asking which one. */
prompt: `${s.prompt} for ${position.title}`,
skillId: skill.id,
skillCapability: s.capability || null,
}));
chips.push(...offered);
}
return chips.slice(0, 2);
}