docs(specs): add delivery package for direct profile invocation
-co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,122 @@
|
||||
---
|
||||
created: 2026-09-14
|
||||
status: complete
|
||||
requirements:
|
||||
- REQ-PLUGINS-DIRECT-PROFILE-INVOCATION-001
|
||||
system_design:
|
||||
- ../../specs/plugins/system-design/plugin-direct-profile-invocation.md
|
||||
legacy_specs: []
|
||||
---
|
||||
|
||||
# Implementation Plan: Plugin Direct Agent Profile Invocation
|
||||
|
||||
## Overview
|
||||
|
||||
Plugins that delegate a one-shot LLM step currently select execution identity
|
||||
through the legacy `utility_agent` config field, which models the choice as a
|
||||
custom utility prompt and hides the agent profile that actually executes.
|
||||
This plan adds a direct `agent_profile` selection to the Host invocation
|
||||
contract, wires the host to execute the selected profile via
|
||||
`ExecuteProfilePrompt`, and keeps declaration-gated legacy behavior
|
||||
untouched. The companion [kandev-plugin-notes PR 7](https://github.com/yattdev/kandev-plugin-notes/pull/7)
|
||||
consumes the new `format: agent-profile` field at exact Notes head
|
||||
`f8548f9d7a1eaabf19d27bb0b0af8f75d8eaeec4`.
|
||||
|
||||
The smallest reproduction of the addressed defect is:
|
||||
|
||||
1. Open Settings > Plugins > Notes. The Utility Agent field lists rows from
|
||||
the utility/custom-prompt catalog, not the platform's agent profiles.
|
||||
2. Selecting a row persists a utility-prompt ID that fails at invocation with
|
||||
a generic error rather than executing the configured agent profile.
|
||||
|
||||
## Scope
|
||||
|
||||
### In scope
|
||||
|
||||
- The `agent-profile` config renderer format, shared-profile-picker reuse,
|
||||
and eligibility filtering in the web app.
|
||||
- Direct profile routing, typed `FailedPrecondition` translation, and legacy
|
||||
fallback bounding in the backend plugins host.
|
||||
- Hydration merging in the plugin config form and store profile-list
|
||||
hydration revision checks.
|
||||
- Accessibility and display-label reload for the picker.
|
||||
- The companion Notes manifest, webhook error translation, tests, docs, and
|
||||
packaging evidence for the Notes half of the delivery.
|
||||
|
||||
### Out of scope
|
||||
|
||||
- Changing the public `Host.InvokeUtilityAgent` signature or `agent_invoke`
|
||||
gate semantics.
|
||||
- Changing utility-agent resolution for plugins that declare only
|
||||
`format: utility-agent`.
|
||||
- New database or config migration for the dual selector formats.
|
||||
|
||||
## Technical approach
|
||||
|
||||
### Settings rendering
|
||||
|
||||
Extend `config-schema.ts` with the `agent-profile` format renderer and route
|
||||
it through `utility-agent-profile-picker.tsx` in `agentRegistration` mode.
|
||||
Eligibility requires enabled, non-deleted, global, non-CLI-passthrough,
|
||||
inference-capable profiles; unavailable saved IDs render replaceably.
|
||||
`plugin-config-form.tsx` merges hydration results so concurrent profile
|
||||
snapshot and config updates do not overwrite each other, and
|
||||
`use-settings-data.ts`/`hydrator.ts` guard store revisions against stale
|
||||
profile snapshots with WebSocket-triggered retry.
|
||||
|
||||
### Host execution
|
||||
|
||||
`host_utility.go` gates on `agent_invoke` first, validates the declared
|
||||
format, resolves the profile once at call start, and executes with
|
||||
`ExecuteProfilePrompt(profileID, prompt)`. Missing/ineligible selections
|
||||
return typed `FailedPrecondition` before runner start; non-not-found store
|
||||
errors pass through unchanged in `services.go`. Legacy `utility_agent`
|
||||
routing runs only when no direct selection exists and the active manifest
|
||||
declares that selector, per
|
||||
[ADR 0048](../../decisions/0048-plugin-host-utility-agent-invoke.md).
|
||||
|
||||
### Notes companion
|
||||
|
||||
The plugin uses the daemonized target server, refreshes
|
||||
`Host.InvokeUtilityAgent` enhancement to send the selected profile ID,
|
||||
translates `FailedPrecondition` to an actionable HTTP 412 setup message,
|
||||
updates its manifest contract test, README, packaging, and test-tooling
|
||||
pins, and carries final visual QA evidence for the picker, mobile settings,
|
||||
and enhancement preview.
|
||||
|
||||
## Tests
|
||||
|
||||
- Backend focused Go tests for direct routing, eligibility/error classes,
|
||||
capability gating, and legacy fallback preservation.
|
||||
- Frontend unit tests for config-schema formats, picker eligibility and
|
||||
required fields, hydration merge, store snapshot revision, and WS retry.
|
||||
- Notes Go tests for the manifest contract, webhook error mapping, and
|
||||
enhancement payload, plus `node --test ui/bundle.test.mjs` wiring smoke.
|
||||
|
||||
## Work orders
|
||||
|
||||
- [x] [Task 01: Direct profile invocation and settings rendering](task-01-direct-profile-invocation.md)
|
||||
- [x] [Task 02: Notes companion manifest, errors, and evidence](task-02-notes-companion-delivery.md)
|
||||
|
||||
## Verification results
|
||||
|
||||
- Backend: focused `internal/plugins` and `internal/backendapp` Go tests
|
||||
passed, including the direct-profile routing, eligibility, cancellation,
|
||||
and store-failure cases; static checks passed.
|
||||
- Frontend: config-schema and picker tests, plugin-config-form hydration
|
||||
tests, settings hydration and WS agent-handler tests, web typecheck, and
|
||||
lint all passed at CI for the delivery head.
|
||||
- Notes: Go test suite, `node --test ui/bundle.test.mjs`, package smoke, doc
|
||||
validation, and packaging metadata checks passed.
|
||||
- Notes PR evidence and committed screenshots confirmed the picker, mobile
|
||||
layout, and enhancement preview behavior.
|
||||
|
||||
## Risks
|
||||
|
||||
- Concurrent profile-list hydration and plugin-config hydration can clobber
|
||||
state; merge and revision guards prevent lossy overwrites.
|
||||
- Legacy stored `utility_agent` values must remain executable for trusted
|
||||
pre-transition manifests, so fallback gating is explicitly bounded in ADR
|
||||
0048 and regression-tested.
|
||||
- Config failures must not be misclassified as operational failures, or web
|
||||
hooks will mislead operators with wrong HTTP status messages.
|
||||
@@ -0,0 +1,152 @@
|
||||
---
|
||||
id: "01-direct-profile-invocation"
|
||||
title: "Direct profile invocation and settings rendering"
|
||||
status: done
|
||||
wave: 1
|
||||
depends_on: []
|
||||
plan: "plan.md"
|
||||
requirements:
|
||||
- REQ-PLUGINS-DIRECT-PROFILE-INVOCATION-001
|
||||
acceptance_criteria:
|
||||
- AC-PLUGINS-DIRECT-PROFILE-INVOCATION-001.1
|
||||
- AC-PLUGINS-DIRECT-PROFILE-INVOCATION-001.2
|
||||
- AC-PLUGINS-DIRECT-PROFILE-INVOCATION-001.3
|
||||
- AC-PLUGINS-DIRECT-PROFILE-INVOCATION-001.4
|
||||
- AC-PLUGINS-DIRECT-PROFILE-INVOCATION-001.5
|
||||
- AC-PLUGINS-DIRECT-PROFILE-INVOCATION-001.6
|
||||
- AC-PLUGINS-DIRECT-PROFILE-INVOCATION-001.7
|
||||
system_design:
|
||||
- ../../specs/plugins/system-design/plugin-direct-profile-invocation.md
|
||||
---
|
||||
|
||||
# Task 01: Direct Profile Invocation and Settings Rendering
|
||||
|
||||
## Summary
|
||||
|
||||
Add the direct `agent-profile` config format to Settings > Plugins, render
|
||||
only eligible platform profiles through the shared picker, and route
|
||||
`Host.InvokeUtilityAgent` to `ExecuteProfilePrompt` on the stored profile ID
|
||||
with typed configuration errors and bounded legacy fallback.
|
||||
|
||||
## In scope
|
||||
|
||||
- Add the `agent-profile` renderer, picker-mode eligibility filtering, and
|
||||
unavailable-selection replaceable rendering.
|
||||
- Register the companion Notes settings picker via the daemonized target
|
||||
server and the platform agent registry.
|
||||
- Keep the plugin config form's hydration merge and the store profile-list
|
||||
revision guard so profile/config updates do not overwrite each other.
|
||||
- Route direct selections to `ExecuteProfilePrompt`; return typed
|
||||
`FailedPrecondition` for missing/ineligible selections before any runner
|
||||
starts.
|
||||
- Adapt host-utility manager errors to gRPC codes with not-found/config
|
||||
classes; pass non-not-found store failures through unchanged.
|
||||
- Bound legacy `utility_agent` fallback to trusted declared-selector
|
||||
configurations per ADR 0048.
|
||||
- Cover the paths with backend and frontend focused tests, docs updates, and
|
||||
PR validation evidence.
|
||||
|
||||
## Out of scope
|
||||
|
||||
- Notes-side manifest, webhook translation, packaging, and QA evidence (Task
|
||||
02).
|
||||
- `Host.InvokeUtilityAgent` signature or gate semantic changes.
|
||||
- Utility-agent behavior for manifests that declare only the legacy format.
|
||||
|
||||
## Acceptance
|
||||
|
||||
- Notes's Utility Agent settings field lists eligible profiles, not utility
|
||||
or custom-prompt rows, on desktop and mobile widths.
|
||||
- Selecting a profile persists its stable ID, marks the shared settings
|
||||
contributor dirty, saves, and reloads showing the same display label.
|
||||
- Disabled, deleted, workspace-scoped, and CLI-passthrough profiles cannot be
|
||||
newly selected; previously saved unavailable selections render replaceably.
|
||||
- `Host.InvokeUtilityAgent` with a declared, selected profile executes that
|
||||
profile directly with no utility/custom-prompt lookup; missing or
|
||||
ineligible selections return typed `FailedPrecondition`, and
|
||||
non-`agent_invoke` plugins are denied before profile lookup.
|
||||
- Direct selection takes precedence over a coexisting legacy value;
|
||||
legacy-only behavior is unchanged.
|
||||
|
||||
## Verification
|
||||
|
||||
```bash
|
||||
cd apps/backend
|
||||
go test ./internal/plugins/... ./internal/backendapp/...
|
||||
cd ../apps && pnpm --filter @kandev/web test -- --run \
|
||||
lib/plugins/config-schema.test.ts \
|
||||
components/settings/utility-agent-profile-picker.test.ts \
|
||||
components/settings/plugins/plugin-config-form.agent-profile.test.tsx \
|
||||
hooks/domains/settings/use-settings-data.test.tsx \
|
||||
lib/state/hydration/hydrator.test.ts \
|
||||
lib/state/slices/settings/types.test.ts \
|
||||
lib/ws/handlers/agents.test.ts
|
||||
pnpm --filter @kandev/web typecheck
|
||||
pnpm --filter @kandev/web lint
|
||||
```
|
||||
|
||||
## Files likely touched
|
||||
|
||||
- `apps/web/lib/plugins/config-schema.ts`
|
||||
- `apps/web/components/settings/utility-agent-profile-picker.tsx`
|
||||
- `apps/web/components/settings/plugins/plugin-config-form.tsx`
|
||||
- `apps/web/hooks/domains/settings/use-settings-data.ts`
|
||||
- `apps/web/lib/state/hydration/hydrator.ts`
|
||||
- `apps/web/lib/state/slices/settings/types.ts`
|
||||
- `apps/web/lib/types/backend.ts`
|
||||
- `apps/web/lib/types/http-agents.ts`
|
||||
- `apps/web/lib/ws/handlers/agents.ts`
|
||||
- `apps/backend/internal/plugins/host_utility.go`
|
||||
- `apps/backend/internal/plugins/host.go`
|
||||
- `apps/backend/internal/plugins/service.go`
|
||||
- `apps/backend/internal/backendapp/services.go`
|
||||
- `apps/backend/internal/agent/settings/controller/profile_crud.go`
|
||||
- `apps/backend/pkg/pluginsdk/host.go`
|
||||
- `docs/decisions/0048-plugin-host-utility-agent-invoke.md`
|
||||
- `docs/public/plugins-authoring.md`
|
||||
- `docs/public/plugins-manifest.md`
|
||||
- `docs/screenshots/pr-validation/2870/*`
|
||||
|
||||
## Dependencies
|
||||
|
||||
None.
|
||||
|
||||
## Risks
|
||||
|
||||
- Legacy fallback must remain available for stored configs that still expect
|
||||
it; explicit bounds and tests prevent silent removal.
|
||||
- Concurrent hydration of config and profile snapshots can cause lossy
|
||||
overwrites without merge and revision guards.
|
||||
- Eligibility criteria must match the agents system's rules, so the shared
|
||||
registration provider is anchored to the same store used by the settings
|
||||
API.
|
||||
|
||||
## Parallelism
|
||||
|
||||
`sequential`
|
||||
|
||||
## Inputs
|
||||
|
||||
- `docs/specs/plugins/requirements/plugin-direct-profile-invocation.md`
|
||||
- `docs/specs/plugins/system-design/plugin-direct-profile-invocation.md`
|
||||
- `docs/specs/agents/requirements/utility-agent-profiles.md`
|
||||
- `docs/decisions/0048-plugin-host-utility-agent-invoke.md`
|
||||
- `apps/web/lib/plugins/config-schema.ts`
|
||||
- `apps/web/components/settings/utility-agent-profile-picker.tsx`
|
||||
- `apps/backend/internal/plugins/host_utility.go`
|
||||
|
||||
## Results
|
||||
|
||||
- Added the `agent-profile` renderer and shared-picker `agentRegistration`
|
||||
mode with utility-execution eligibility (enabled, global, non-CLI,
|
||||
inference-capable) and replaceable unavailable-selection rendering.
|
||||
- Registered the daemonized target server so Notes renders platform profiles
|
||||
with correct labels, persisted the saved stable ID, and surfaced the
|
||||
profile name in the enhancement preview.
|
||||
- Preserved legacy `utility_agent` fallback for trusted declared-selector
|
||||
configs, bounded per ADR 0048 and regression-tested.
|
||||
- Wired hydration merges and store revision guards so profile snapshot and
|
||||
plugin-config updates do not clobber each other; profile WS updates retry
|
||||
when a render would go stale.
|
||||
- Verification: focused backend `go test` and web vitest/lint/typecheck all
|
||||
passing at the delivery head, with PR validation screenshots added.
|
||||
@@ -0,0 +1,100 @@
|
||||
---
|
||||
id: "02-notes-companion-delivery"
|
||||
title: "Notes companion manifest, errors, and evidence"
|
||||
status: done
|
||||
wave: 1
|
||||
depends_on:
|
||||
- "01-direct-profile-invocation"
|
||||
plan: "plan.md"
|
||||
requirements:
|
||||
- REQ-PLUGINS-DIRECT-PROFILE-INVOCATION-001
|
||||
acceptance_criteria:
|
||||
- AC-PLUGINS-DIRECT-PROFILE-INVOCATION-001.1
|
||||
- AC-PLUGINS-DIRECT-PROFILE-INVOCATION-001.4
|
||||
- AC-PLUGINS-DIRECT-PROFILE-INVOCATION-001.5
|
||||
- AC-PLUGINS-DIRECT-PROFILE-INVOCATION-001.8
|
||||
system_design:
|
||||
- ../../specs/plugins/system-design/plugin-direct-profile-invocation.md
|
||||
---
|
||||
|
||||
# Task 02: Notes Companion Manifest, Errors, and Evidence
|
||||
|
||||
## Summary
|
||||
|
||||
Update the Notes plugin (yattdev/kandev-plugin-notes at
|
||||
`f8548f9d7a1eaabf19d27bb0b0af8f75d8eaeec4`, PR 7) to declare the
|
||||
agent-profile-backed setting, translate `FailedPrecondition` into an
|
||||
actionable setup response, align packaging, and record final delivery
|
||||
evidence.
|
||||
|
||||
## In scope
|
||||
|
||||
- Notes manifest declaration and contract-pinning tests for the
|
||||
agent-profile field name and format.
|
||||
- Refresh the enhancement/proofread path to send the selected stable profile
|
||||
ID in the `Host.InvokeUtilityAgent` payload.
|
||||
- Translate `FailedPrecondition` webhook responses to HTTP 412 with an
|
||||
actionable profile-selection/setup message; keep operational failures as
|
||||
502-class execution errors.
|
||||
- SDK target output update, README/usage-copy alignment to a single direct
|
||||
profile selection, packaging metadata consistency, and test tooling pin
|
||||
alignment (Kandev SDK v0.2.4).
|
||||
- Final PR body, visual evidence, and Notes-side read-me screenshots.
|
||||
|
||||
## Out of scope
|
||||
|
||||
- Host-side routing, eligibility, translation, and UI rendering, covered by
|
||||
Task 01.
|
||||
- Legacy `utility_agent` semantics for other plugins.
|
||||
|
||||
## Acceptance
|
||||
|
||||
- Notes declares and pins the agent-profile settings field, its payload
|
||||
carries the stable profile ID, and its UI shows the selected profile label.
|
||||
- A missing or ineligible profile yields HTTP 412 with actionable copy, not
|
||||
a generic 500/502.
|
||||
- Notes packaging, version metadata, doc links, and screenshots are
|
||||
consistent with the shipped manifest and SDK.
|
||||
|
||||
## Verification
|
||||
|
||||
```bash
|
||||
cd kandev-plugin-notes
|
||||
go test ./...
|
||||
node --test ui/bundle.test.mjs
|
||||
go vet ./... && test -z "$(gofmt -l .)"
|
||||
```
|
||||
|
||||
## Files likely touched
|
||||
|
||||
- kandev-plugin-notes `internal/server`, `manifest`, `webhook`, `go.mod`,
|
||||
`go.sum`, README, docs links, packaging, UI bundle smoke tests.
|
||||
|
||||
## Dependencies
|
||||
|
||||
- `01-direct-profile-invocation`
|
||||
|
||||
## Risks
|
||||
|
||||
- Full-replacement plugin config updates can drop fields unless the plugin's
|
||||
hydration writes the complete record.
|
||||
- Committing PR screenshots requires binary assets on the Notes fork head.
|
||||
|
||||
## Parallelism
|
||||
|
||||
`sequential`
|
||||
|
||||
## Inputs
|
||||
|
||||
- `docs/specs/plugins/requirements/plugin-direct-profile-invocation.md`
|
||||
- `docs/specs/plugins/system-design/plugin-direct-profile-invocation.md`
|
||||
- kandev-plugin-notes Task 01 implementation and Kandev SDK contract.
|
||||
|
||||
## Results
|
||||
|
||||
- Notes PR 7 CI green (`Build plugin packages`,
|
||||
`Tidy, format, vet, and test`, `GitGuardian`).
|
||||
- Notes PR body corrected to reference the commands actually run in QA.
|
||||
- Draft statuses cleared by marking Notes PR 7 (`f8548f9d7...`), Notes PR 7,
|
||||
and Kandev PR 2870 (`0f36c0a6b...`) ready for review; both are CLEAN and
|
||||
MERGEABLE at their exact delivery heads.
|
||||
@@ -0,0 +1,99 @@
|
||||
---
|
||||
status: active
|
||||
system: plugins
|
||||
created: 2026-09-14
|
||||
owners:
|
||||
- kandev
|
||||
---
|
||||
# Plugin Direct Agent Profile Invocation Requirements
|
||||
|
||||
## Overview
|
||||
|
||||
Plugins performing a one-shot LLM step previously chose execution identity by
|
||||
selecting a utility agent, which forced them to route through a secondary
|
||||
configurable object and hid the actual agent profile that would run. Users
|
||||
need to select the platform agent profile directly from a plugin's settings so
|
||||
the profile that executes is the profile they configured, while existing
|
||||
plugins that still declare the legacy `utility_agent` selector keep working.
|
||||
The plugin system owns the Host invocation contract and its configuration
|
||||
state; the agents system continues to own profile eligibility and utility
|
||||
execution resolution.
|
||||
|
||||
## Terminology
|
||||
|
||||
- **Direct profile selection:** a plugin `config_schema` field with
|
||||
`type: string` and `format: agent-profile` whose stored value is a stable
|
||||
agent-profile ID executed directly by the host.
|
||||
- **Legacy utility-agent selection:** an existing `config_schema` field with
|
||||
`format: utility-agent` whose stored value resolves through the utility
|
||||
agent catalog, unchanged for plugins that still declare it.
|
||||
- **Eligibility:** enabled, non-deleted, global, non-CLI-passthrough profiles
|
||||
whose agent supports sessionless inference; the backend remains
|
||||
authoritative.
|
||||
|
||||
## Requirements
|
||||
|
||||
### REQ-PLUGINS-DIRECT-PROFILE-INVOCATION-001: Direct agent profile invocation for plugins
|
||||
|
||||
**Intent:** Let a plugin invoke one executable agent profile of the
|
||||
operator's choosing without inventing a custom utility prompt, while keeping
|
||||
the public Host RPC, SDK, and capability gate source-compatible and
|
||||
preserving installed legacy configurations.
|
||||
|
||||
**User story:** As a Kandev operator configuring a plugin such as Notes, I
|
||||
want to pick the agent profile that performs the plugin's LLM step directly
|
||||
in the plugin's settings, so that the profile I configured is the profile
|
||||
that executes and I do not have to maintain a separate custom utility
|
||||
prompt.
|
||||
|
||||
#### Acceptance criteria
|
||||
|
||||
- **AC-PLUGINS-DIRECT-PROFILE-INVOCATION-001.1:** When a plugin manifest
|
||||
declares a `config_schema` field with `format: agent-profile`, Settings >
|
||||
Plugins shall list only eligible platform agent profiles and shall not list
|
||||
utility or custom-prompt rows, so built-in and custom utility prompts with
|
||||
the same or different IDs cannot be confused with executable profiles.
|
||||
- **AC-PLUGINS-DIRECT-PROFILE-INVOCATION-001.2:** When the user selects a
|
||||
profile, the plugin configuration shall persist that profile's stable ID
|
||||
through the shared settings save path, the contributor shall become dirty
|
||||
and save successfully, and a reload shall again show the selected profile's
|
||||
display label.
|
||||
- **AC-PLUGINS-DIRECT-PROFILE-INVOCATION-001.3:** When a profile is disabled,
|
||||
deleted, workspace-scoped, or CLI-passthrough, the picker shall exclude it
|
||||
from new selections and the settings surface shall represent a previously
|
||||
saved unavailable selection safely and replaceably rather than failing
|
||||
render.
|
||||
- **AC-PLUGINS-DIRECT-PROFILE-INVOCATION-001.4:** When a plugin with
|
||||
`capabilities.agent_invoke: true` invokes `Host.InvokeUtilityAgent` with a
|
||||
non-empty direct `agent_profile` configuration, the host shall route the
|
||||
prompt to `ExecuteProfilePrompt` using that exact profile ID and shall not
|
||||
resolve or execute a utility or custom-prompt definition.
|
||||
- **AC-PLUGINS-DIRECT-PROFILE-INVOCATION-001.5:** When the direct profile
|
||||
selection is missing, or the selected profile is missing or ineligible at
|
||||
execution time, the host shall return a typed gRPC `FailedPrecondition`
|
||||
that plugins can translate into an actionable configuration message rather
|
||||
than a generic execution failure.
|
||||
- **AC-PLUGINS-DIRECT-PROFILE-INVOCATION-001.6:** When a plugin's
|
||||
configuration holds both a direct profile and a legacy `utility_agent`
|
||||
value, the direct profile shall take precedence; when only the legacy value
|
||||
exists and the active manifest declares that selector, the existing
|
||||
utility-agent resolution and execution behavior shall remain unchanged.
|
||||
- **AC-PLUGINS-DIRECT-PROFILE-INVOCATION-001.7:** A plugin that does not
|
||||
declare `capabilities.agent_invoke` shall be denied before any profile or
|
||||
configuration lookup, and operational runner failures that occur after
|
||||
successful configuration validation shall remain execution errors instead
|
||||
of being misreported as configuration failures.
|
||||
- **AC-PLUGINS-DIRECT-PROFILE-INVOCATION-001.8:** The plugin manifest and its
|
||||
contract tests shall declare the agent-profile-backed setting with a
|
||||
pinned field name and format, and the plugin's package metadata shall stay
|
||||
internally consistent.
|
||||
|
||||
## Out of scope
|
||||
|
||||
- Changing the public `Host.InvokeUtilityAgent(ctx, prompt)` RPC or SDK
|
||||
signature, or the `agent_invoke` capability-gate semantics.
|
||||
- Adding new config migration; a database or schema migration is not
|
||||
required for the dual-format selection.
|
||||
- Changing utility-agent resolution for plugins that continue to declare
|
||||
only the legacy `utility_agent` selector.
|
||||
- Changing agent-profile eligibility rules owned by the agents system.
|
||||
@@ -0,0 +1,107 @@
|
||||
---
|
||||
status: current
|
||||
system: plugins
|
||||
requirements:
|
||||
- REQ-PLUGINS-DIRECT-PROFILE-INVOCATION-001
|
||||
created: 2026-09-14
|
||||
owners:
|
||||
- kandev
|
||||
---
|
||||
# Plugin Direct Agent Profile Invocation System Design
|
||||
|
||||
## Purpose and boundaries
|
||||
|
||||
The plugin system owns the `Host.InvokeUtilityAgent` contract and the
|
||||
plugin-configuration state that selects its execution identity. This design
|
||||
adds a direct agent-profile selection path beside the legacy utility-agent
|
||||
selection, without changing the public RPC, SDK, or capability gate. The
|
||||
agents system continues to own profile eligibility and the utility execution
|
||||
resolution described by
|
||||
[Profile-backed Utility Agents](../../agents/system-design/utility-agent-profiles.md).
|
||||
The UI system's shared settings-save coordinator and hydration contracts
|
||||
remain independent dependencies, as does
|
||||
[ADR 0048](../../../decisions/0048-plugin-host-utility-agent-invoke.md) for
|
||||
the invocation precedence rule.
|
||||
|
||||
## Requirement mapping
|
||||
|
||||
| Requirement | Design sections |
|
||||
| --- | --- |
|
||||
| `REQ-PLUGINS-DIRECT-PROFILE-INVOCATION-001` | [Configuration selection](#configuration-selection), [Host execution routing](#host-execution-routing), [Eligibility and error translation](#eligibility-and-error-translation), [Legacy compatibility](#legacy-compatibility) |
|
||||
|
||||
## Configuration selection
|
||||
|
||||
`apps/web/lib/plugins/config-schema.ts` adds the `agent-profile` renderer
|
||||
format beside the existing `utility-agent` format. Settings > Plugins renders
|
||||
the field through the shared utility-agent profile picker component with
|
||||
`agentRegistration` data, which exposes each platform profile's name, label,
|
||||
workspace scope, CLI passthrough, and `inference_capable` flag.
|
||||
|
||||
Eligibility filtering is `utility-execution` scoped: a profile is selectable
|
||||
only when it is enabled, non-deleted, global (not workspace- or
|
||||
Office-scoped), not CLI passthrough, and its agent declares
|
||||
`inference_capable === true`. The picker does not fall back to matching
|
||||
platform profiles against utility or custom-prompt catalog rows: rows from
|
||||
`/api/v1/utility/agents` are never shown in the `agent-profile` field.
|
||||
Unknown or ineligible saved IDs render through the picker's unavailable
|
||||
option so the stored value stays visible-and-replaceable.
|
||||
|
||||
File-field hydration (`plugin-config-form.tsx`) merges its backend profile
|
||||
response with the current store snapshot instead of replacing the record, so
|
||||
plugin config hydration and independent profile-list updates do not clobber
|
||||
each other. Store-side profile-list hydration in
|
||||
`use-settings-data.ts`/`hydrator.ts` compares an incoming profile snapshot
|
||||
against what is already loaded and does not overwrite a higher store
|
||||
revision; a WebSocket profile update triggers a retry when the list render
|
||||
would otherwise go stale.
|
||||
|
||||
## Host execution routing
|
||||
|
||||
`internal/plugins/host_utility.go` resolves the invocation identity at the
|
||||
start of each call, before any execution:
|
||||
|
||||
1. The `agent_invoke` capability gate runs first; a plugin without it is
|
||||
denied before configuration or profile lookup.
|
||||
2. If the manifest's `config_schema` declares a field with
|
||||
`format: agent-profile` and the stored value is non-empty, that value is
|
||||
the direct profile ID. The host validates the declared format first, then
|
||||
resolves the profile through the agent registry: missing or ineligible
|
||||
profiles return typed `FailedPrecondition` before any runner starts, so
|
||||
configuration failure and operational failure remain distinct classes.
|
||||
3. With a resolved eligible profile, the host calls
|
||||
`ExecuteProfilePrompt(profileID, prompt)` directly on the host-utility
|
||||
tier. No utility or custom-prompt definition is looked up or executed in
|
||||
this path.
|
||||
4. Legacy `utility_agent` routing is consulted only when no direct profile is
|
||||
selected, the active manifest declares the `utility-agent` format, and the
|
||||
stored legacy value passes the existing trusted-resolution checks. Its
|
||||
behavior is otherwise unchanged.
|
||||
|
||||
`internal/backendapp/services.go` adapts the host-utility manager's typed
|
||||
revalidation errors to gRPC codes: not-found/missing selection and
|
||||
ineligibility surface as `FailedPrecondition`; store failures other than
|
||||
not-found are returned unchanged so they remain server-side errors rather
|
||||
than misleading the plugin's configuration messaging.
|
||||
|
||||
## Eligibility and error translation
|
||||
|
||||
The Notes webhook and other plugin callers translate `FailedPrecondition`
|
||||
into an actionable "select an agent profile" setup message (HTTP 412) and
|
||||
leave other codes (for example `Internal`) as execution failures (HTTP 502).
|
||||
Service wiring in `services.go` builds the agent-profile provider from the
|
||||
same registration store used by the settings API, so the picker and the
|
||||
execution path judge eligibility with one source of truth. Renaming or
|
||||
reflagging a profile revalidates at the next invocation; an in-flight call
|
||||
keeps the configuration resolved when it started.
|
||||
|
||||
## Legacy compatibility
|
||||
|
||||
Manifests that keep only `format: utility-agent` continue to render the
|
||||
utility-agent picker and persist utility prompt IDs; their stored values
|
||||
resolve and execute exactly as before this change. A plugin updating its
|
||||
manifest from `utility-agent` to `agent-profile` keeps its legacy value
|
||||
until the operator saves a direct selection, and
|
||||
`docs/decisions/0048-plugin-host-utility-agent-invoke.md` documents the
|
||||
verified-transition rule that bounds the legacy fallback to trusted
|
||||
pre-transition configurations. No config migration runs; both selector
|
||||
formats coexist in stored plugin configuration.
|
||||
Reference in New Issue
Block a user