fix(plugins): expose enabled skills for agent assignment
This commit is contained in:
1 parent
08099ea1c2
commit
adc28db785
3 files changed
+146
-6
No files matched your search
@@ -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.
|
||||||
@@ -674,7 +674,7 @@ export function createCodingProductHost(options: CodingProductHostOptions): Codi
|
|||||||
async listSkills(agentId) {
|
async listSkills(agentId) {
|
||||||
const project = await activeProject();
|
const project = await activeProject();
|
||||||
const assignedSkillIds = await selectedSkillIds(project.path, agentId);
|
const assignedSkillIds = await selectedSkillIds(project.path, agentId);
|
||||||
const effective = options.effectiveResolver
|
const effective = agentId && options.effectiveResolver
|
||||||
? await options.effectiveResolver.resolve({
|
? await options.effectiveResolver.resolve({
|
||||||
projectId: project.id,
|
projectId: project.id,
|
||||||
projectPath: project.path,
|
projectPath: project.path,
|
||||||
@@ -687,11 +687,17 @@ export function createCodingProductHost(options: CodingProductHostOptions): Codi
|
|||||||
// still supplies the availability set; only the worker opener receives
|
// still supplies the availability set; only the worker opener receives
|
||||||
// the effective subset.
|
// the effective subset.
|
||||||
const skillIds = assignedSkillIds;
|
const skillIds = assignedSkillIds;
|
||||||
const enabledPluginIds = effective
|
// The unscoped list is the Agent assignment catalog. It must expose
|
||||||
? effective.effectiveSkillIds
|
// every project-enabled plugin Skill before that Skill has been assigned;
|
||||||
: options.getEnabledPluginIds
|
// the resolver's effective subset is only authoritative once an Agent is
|
||||||
? await options.getEnabledPluginIds(project.path)
|
// 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(
|
return await options.productTools.listSkills(
|
||||||
skillIds,
|
skillIds,
|
||||||
enabledPluginIds,
|
enabledPluginIds,
|
||||||
|
|||||||
@@ -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 () => {
|
it('reads exact-run changes from the same PiProductTools tracker instance', async () => {
|
||||||
const root = await configuredProject();
|
const root = await configuredProject();
|
||||||
await writeFile(path.join(root, 'notes.txt'), 'baseline\n', 'utf8');
|
await writeFile(path.join(root, 'notes.txt'), 'baseline\n', 'utf8');
|
||||||
|
|||||||
Reference in new issue
Block a user