fix(plugins): expose enabled skills for agent assignment

This commit is contained in:
2026-09-01 20:27:51 +08:00
parent 08099ea1c2
commit adc28db785
3 changed files with 146 additions and 6 deletions

View File

@@ -0,0 +1,84 @@
# Task: Diagnose and fix Web Search tool routing from current main
## Identity
- Task ID: 20260901-web-search-tool-routing-fix-6f9d3b82
- Mode: Feature
- Branch: codex/20260901-web-search-tool-routing-fix-6f9d3b82-web-search-tool-routing-fix
- Worktree: D:\Datas\OthersProjects\makelore-web-search-tool-routing-fix-6f9d3b82
- Base commit: 08099ea1c2ea24efc10c20f878fc25a557ea9d90
- Owner: codex-root
- Status: Ready for Integration
## Scope
- Reproduce the reported installed-client behavior where an explicit request to use
Web Search invokes `agent_browser` instead of `makelore_web_search`.
- Trace the exact current package version, project Plugin selection, Agent Skill
assignment, backend admission, effective frozen worker snapshot, and Pi CLI tool
catalog without changing server policy or production data.
- If a client defect is confirmed, add a regression at the narrow owning seam,
implement the smallest fix, and verify the affected packaged/runtime path.
## Intent And Constraints
- Concurrent Task Gate: Passed in the isolated worktree from exact local
`main` base `08099ea1c2ea24efc10c20f878fc25a557ea9d90`. The two unrelated
untracked packaging records in the user root remain untouched.
- Planning Gate: Passed after loading the project memory entry points, current
architecture/data-flow/domain rules, ADR-006, evidence/commitments, the prior
Web Search coordinator scope, and the two local 1.2.2/1.2.3 package records.
- Confirmed product contract: official Web Search is bundled but optional; Account
acquisition, project enablement, Agent assignment, current Admission, and the
frozen parent worker snapshot are separate gates. Child workers receive no hosted
tool.
- Diagnosis must begin with a deterministic loop that observes the actual worker
tool catalog. A prompt-only change is not acceptable unless the tool is proven
present and the routing failure remains.
- Preserve the current project, conversations, user data, credentials, unrelated
task records, and production server state. No push, deploy, publication, paid
Provider call, or destructive cleanup is in scope.
## Outcome
- Confirmed the installed client is MakeLore 1.2.3 and the affected project has
`makelore.web-search` enabled, but Agent `cc` is assigned only `agent-browser`.
The latest persisted Pi session consequently exposes only `agent_browser`; this
is not a model-choice conflict between two available tools.
- Reproduced the client bootstrap defect at the Product Host seam: the unscoped
Agent assignment catalog called the effective resolver with an empty assignment,
then treated `effectiveSkillIds=[]` as the plugin availability set. An enabled
Web Search Skill therefore disappeared until it had already been assigned.
- Changed the unscoped catalog to use project-enabled plugin IDs directly and to
avoid runtime/admission resolution. Agent-scoped projections still use the
resolver-approved effective subset, so the fix does not materialize Web Search
into a worker before explicit Agent assignment.
- Added a regression proving an enabled/unassigned Web Search Skill is available
in the assignment catalog, while the same Skill remains unavailable in Agent
`builder`'s scoped projection until assigned.
- Applied the requested operational correction to the affected project only:
Agent `cc` now has `agent-browser` and `makelore-web-search`; the existing
`makelore.web-search` project enablement remains unchanged.
## Verification
- TDD RED: `pnpm exec vitest run tests/unit/coding-product-services.test.ts` =
1 failed / 10 passed; the enabled Web Search Skill was absent.
- Focused GREEN: Product Host, effective resolver, capability registry, Pi resource
loader, and composition = 5 files / 45 tests passed.
- Full client suite on the final implementation = 215 files / 1785 passed / 2
skipped, plus pressure 1/1 passed.
- `pnpm run typecheck` passed. Scoped ESLint passed with zero findings.
- The affected project JSON parses successfully and reports Agent `cc` Skills
`agent-browser,makelore-web-search` with Web Search project enablement `true`.
## Follow-ups
- The currently running 1.2.3 process does not contain the assignment-catalog UI
fix. The affected Agent assignment is already corrected on disk; start a new
conversation so a new frozen parent worker reads it and includes
`makelore_web_search`. Existing workers intentionally remain frozen.
## Promotion Candidates
- None recorded.

View File

@@ -674,7 +674,7 @@ export function createCodingProductHost(options: CodingProductHostOptions): Codi
async listSkills(agentId) {
const project = await activeProject();
const assignedSkillIds = await selectedSkillIds(project.path, agentId);
const effective = options.effectiveResolver
const effective = agentId && options.effectiveResolver
? await options.effectiveResolver.resolve({
projectId: project.id,
projectPath: project.path,
@@ -687,11 +687,17 @@ export function createCodingProductHost(options: CodingProductHostOptions): Codi
// still supplies the availability set; only the worker opener receives
// the effective subset.
const skillIds = assignedSkillIds;
const enabledPluginIds = effective
? effective.effectiveSkillIds
: options.getEnabledPluginIds
? await options.getEnabledPluginIds(project.path)
: [];
// The unscoped list is the Agent assignment catalog. It must expose
// every project-enabled plugin Skill before that Skill has been assigned;
// the resolver's effective subset is only authoritative once an Agent is
// selected (and for worker-facing projections).
const enabledPluginIds = !agentId && options.getEnabledPluginIds
? await options.getEnabledPluginIds(project.path)
: effective
? effective.effectiveSkillIds
: options.getEnabledPluginIds
? await options.getEnabledPluginIds(project.path)
: [];
return await options.productTools.listSkills(
skillIds,
enabledPluginIds,

View File

@@ -218,6 +218,56 @@ describe('PI-105 product Host composition', () => {
}));
});
it('offers an enabled Web Search Skill before its first Agent assignment', async () => {
const root = await configuredProject(['agent-browser']);
const tools = new PiProductTools({
browser: {} as AgentBrowserModule,
attachments: new CodingAttachmentStore(path.join(root, 'attachments')),
bundledSkillsDir: path.resolve('resources/coding-skills'),
pluginSkillSources: [{
id: 'makelore-web-search',
pluginId: 'makelore.web-search',
directory: path.resolve(
'resources/coding-plugins/web-search/skills/makelore-web-search',
),
}],
});
const effectiveResolver = {
resolve: vi.fn(async () => ({
accountSessionId: 'account-a\u00001',
projectId: 'project-a',
pluginReleaseIds: [],
effectiveSkillIds: [],
skillEntries: [],
toolDefinitions: [],
runtimePolicies: [],
unavailableReasons: [{
pluginId: 'makelore.web-search',
code: 'skill_unassigned' as const,
message: 'Plugin Skill is not assigned',
}],
})),
getSkillSources: vi.fn(async () => []),
getPolicyState: vi.fn(() => ({
status: 'current' as const, catalog: null, revision: 1, lastVerifiedAt: 1,
})),
};
const host = createCodingProductHost({
projects: projectService(root),
productTools: tools,
effectiveResolver,
getEnabledPluginIds: async () => ['makelore.web-search'],
});
await expect(host.listSkills()).resolves.toContainEqual(expect.objectContaining({
id: 'makelore-web-search', selected: false, available: true, effective: false,
}));
expect(effectiveResolver.resolve).not.toHaveBeenCalled();
await expect(host.listSkills('builder')).resolves.not.toContainEqual(
expect.objectContaining({ id: 'makelore-web-search', available: true }),
);
});
it('reads exact-run changes from the same PiProductTools tracker instance', async () => {
const root = await configuredProject();
await writeFile(path.join(root, 'notes.txt'), 'baseline\n', 'utf8');