Skip to content

feat(agent-designer): resolve skill bindings at invocation (replace + force skill-mode)#602

Merged
philmerrell merged 1 commit into
developfrom
feature/agent-skill-binding-resolution
Jul 9, 2026
Merged

feat(agent-designer): resolve skill bindings at invocation (replace + force skill-mode)#602
philmerrell merged 1 commit into
developfrom
feature/agent-skill-binding-resolution

Conversation

@philmerrell

Copy link
Copy Markdown
Contributor

Summary

Completes the tool/skill runtime-resolution gap (tools landed in #601). An Agent's skill bindings were stored by the Designer but inert at run time — chatting an Agent ignored its bound skills. This resolves them, mirroring the shipped model/tool overrides. With this, every binding kind resolves at invocation and no inert kinds remain.

What changed

Run-time (inference-api)resolve_agent_invocation now returns plan.skills (ResolvedSkills). When an Agent binds skills they replace the request's skills for the turn, and the route forces agent_type="skill" so the SkillAgent discloses exactly the bound set. Each bound skill is re-checked against the invoking user via AppRoleService.can_access_skill; a missing skill — or the Skills feature being disabled in the environment — blocks the turn with a message (D5). No skill binding ⇒ plan.skills is None ⇒ the request's agent_type/enabled_skills drive the turn exactly as today.

Resume-safe by construction — the effective agent_type/enabled_skills are reassigned before the main-turn get_agent, so they flow into the construction snapshot (service.py:256-258) and a paused bound-skill agent resumes on the same skills_hash. (Same mechanism the tool slice relies on; scope-verified there's no closure-local trap — the streaming generator starts after get_agent.)

Design-time (app-api)skill dropped from inert (no inert kinds remain). A bound skill is flag-gated (skills_enabled()) and must be in the author's palette (resolve_accessible_skill_ids, the same source the Designer picker fetches — cf. the tool check). The palette is resolved once per write, only when skills are enabled.

Governance note

Binding skills to an Agent forces skill-mode for that turn, bypassing the admin chat-mode policy. This is intentional — an Agent is a governed authored capability (like model-override winning over the request) — but worth knowing when debugging why an agent runs in skill mode.

Tests

  • +6 resolver cases (override, dedupe, flag-off block, block-on-missing, per-invoker, none→passthrough)
  • +6 validation cases (accessible/inaccessible/empty-ref/flag-off/fetch-once/lazy)
  • Full backend suite green: 4631 passed, 3 skipped

🤖 Generated with Claude Code

… force skill-mode)

Completes the tool/skill runtime-resolution gap (tools landed in #601). An Agent's
`skill` bindings were stored by the Designer but inert at run time. This resolves them,
mirroring the tool/model overrides:

- Run-time (inference-api): `resolve_agent_invocation` now returns `plan.skills`
  (`ResolvedSkills`). When an Agent binds skills they *replace* the request's skills for
  the turn AND the route forces `agent_type="skill"` so the SkillAgent discloses exactly
  the bound set. Each bound skill is re-checked against the INVOKING user via
  `AppRoleService.can_access_skill`; a missing skill — or the Skills feature being
  disabled in this environment — blocks the turn with a message (D5). No skill binding ⇒
  `plan.skills is None` ⇒ the request's agent_type/enabled_skills drive the turn as today.
  Wired by reassigning `effective_agent_type`/`effective_skill_ids` before the main-turn
  get_agent, so the values flow into the construction snapshot and a bound-skill agent
  resumes on the same skills_hash (resume-safe, same mechanism the tool slice relies on).
- Design-time (app-api): `skill` dropped from inert (no inert kinds remain). A bound skill
  is flag-gated (`skills_enabled()`) and must be in the author's palette
  (`resolve_accessible_skill_ids`, the same source the picker fetches — cf. the tool
  check); the palette is resolved once per write and only when skills are enabled.

Tests: +6 resolver (override, dedupe, flag-off block, block-on-missing, per-invoker, none
→passthrough) + 6 validation (accessible/inaccessible/empty-ref/flag-off/fetch-once/lazy).
Full backend suite green (4631 passed).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@philmerrell philmerrell merged commit fee9b1c into develop Jul 9, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant