فهرست منبع

fix(marketplace): keep package metadata out of agent prompts

Alvin Unreal 4 روز پیش
والد
کامیت
05fe440cad

+ 5 - 3
docs/marketplace.md

@@ -50,11 +50,13 @@ compared.
 
 ## Manifest routing versions
 
-Schema-v2 manifests retain the legacy routing object and prompt-mode behavior.
-Schema-v3 manifests use a deterministic routing object with `lane`, `stats`,
+Schema-v2 manifests retain the legacy routing object. Schema-v3 manifests use
+a deterministic routing object with `lane`, `stats`,
 `delegateWhen`, `avoid`, and optional `additionalInstructions`; extensions are
 append-only. V3 routing lines are single-line bounded values, and list order is
-preserved in the generated routing block.
+preserved in the generated routing block. For both versions, an activated
+marketplace agent receives its authored `prompt` unchanged; manifest routing
+metadata is rendered only in the orchestrator's routing prompt.
 
 ## In-session tool
 

+ 1 - 1
package.json

@@ -1,6 +1,6 @@
 {
   "name": "oh-my-opencode-slim",
-  "version": "3.0.0-beta.6",
+  "version": "3.0.0-beta.7",
   "packageManager": "bun@1.3.14",
   "description": "Lightweight agent orchestration plugin for OpenCode - a slimmed-down fork of oh-my-opencode",
   "main": "dist/index.js",

+ 1 - 20
src/agents/index.ts

@@ -17,7 +17,6 @@ import { type HostConfigSnapshot, RuntimeConfig } from '../config/runtime';
 import { applyOrchestratorModelConfig } from '../config/strip-orchestrator-model';
 import {
   type ActivatedMarketplaceAgent,
-  composePackagePrompt,
   type MarketplaceActivationPlan,
   type MarketplaceDiagnostic,
   reservedRuntimeNames,
@@ -1412,11 +1411,6 @@ export function createAgents(
       const role = activated.manifest.extends
         ? ROLE_DEFINITIONS[activated.manifest.extends.builtin]
         : undefined;
-      const override = getOverrideFromAgents(mergedAgents, name);
-      const customPrompts = loadAgentPrompt(name, {
-        preset: runtime.preset,
-        projectDirectory: options?.projectDirectory,
-      });
       const policy = activated.manifest.model;
       const model =
         policy.source === 'explicit'
@@ -1459,20 +1453,7 @@ export function createAgents(
       if (activated.manifest.color !== undefined) {
         agent.config.color = activated.manifest.color;
       }
-      agent.config.prompt = resolvePrompt(
-        name,
-        override?.prompt,
-        customPrompts.prompt,
-        role
-          ? composePackagePrompt(
-              role.basePrompt,
-              activated.manifest.prompt,
-              activated.manifest.extends?.promptMode ?? 'append',
-            )
-          : activated.manifest.prompt,
-        customPrompts.appendPrompt,
-        [TASK_REJECTION_INSTRUCTION],
-      );
+      agent.config.prompt = activated.manifest.prompt;
       return [agent];
     },
   );

+ 1 - 7
src/marketplace/activation.test.ts

@@ -15,7 +15,6 @@ import {
   buildResolvedAgentRegistry,
   resolveDesiredMarketplaceLiveFromDisk,
 } from '../agents';
-import { ROLE_DEFINITIONS } from '../agents/role-definitions';
 import type { PluginConfig } from '../config';
 import { RuntimeConfig } from '../config/runtime';
 import { resolveRuntimeAgentName } from '../utils/agent-variant';
@@ -115,12 +114,7 @@ describe('marketplace runtime activation', () => {
         (agent) => agent.name === 'docsresearcher',
       );
       expect(derived?.baseRole).toBe('explorer');
-      expect(derived?.config.prompt).toContain(
-        ROLE_DEFINITIONS.explorer.basePrompt,
-      );
-      expect(derived?.config.prompt).toContain(
-        'Prefer documentation paths first.',
-      );
+      expect(derived?.config.prompt).toBe('Prefer documentation paths first.');
       expect(registry.mcpLists.docsresearcher).toEqual([]);
       expect(
         registry.routing.some((entry) => entry.agentName === 'docsresearcher'),

+ 0 - 11
src/marketplace/activation.ts

@@ -76,17 +76,6 @@ export function marketplaceActivationFromRuntime(
   return runtime.plugin?.presets?.[presetName]?.marketplace;
 }
 
-/** Compose only the package-owned extension layers. */
-export function composePackagePrompt(
-  builtinPrompt: string,
-  packagePrompt: string,
-  mode: 'append' | 'replace',
-): string {
-  return mode === 'replace'
-    ? packagePrompt
-    : `${builtinPrompt}\n\n${packagePrompt}`;
-}
-
 export function reservedRuntimeNames(
   runtime: RuntimeConfig,
   ownOverrideKey?: string,

+ 3 - 13
src/marketplace/agents-only.test.ts

@@ -6,10 +6,7 @@ import { buildResolvedAgentRegistry } from '../agents';
 import { RuntimeConfig } from '../config/runtime';
 import { PresetSchema } from '../config/schema';
 import { adaptPermissions } from '../v2/adapters';
-import {
-  composePackagePrompt,
-  resolveMarketplaceActivation,
-} from './activation';
+import { resolveMarketplaceActivation } from './activation';
 import { RETIRED_MARKETPLACE_PACKAGE_IDS } from './retirements';
 import {
   MarketplaceAgentManifestSchema,
@@ -81,15 +78,6 @@ describe('agents-only marketplace contract', () => {
     ).toBe(false);
   });
 
-  test('composes extension prompts in append and replace modes', () => {
-    expect(
-      composePackagePrompt('Builtin prompt.', 'Package prompt.', 'append'),
-    ).toBe('Builtin prompt.\n\nPackage prompt.');
-    expect(
-      composePackagePrompt('Builtin prompt.', 'Package prompt.', 'replace'),
-    ).toBe('Package prompt.');
-  });
-
   test('uses exact declared capabilities and keeps readonly extensions readonly', () => {
     const root = mkdtempSync(join(tmpdir(), 'marketplace-v2-'));
     try {
@@ -141,6 +129,8 @@ describe('agents-only marketplace contract', () => {
         { id: 'provider/model-b' },
       ]);
       expect(registry.sdkConfigs.derived.permission?.edit).toBe('deny');
+      expect(registry.sdkConfigs.standalone.prompt).toBe(manifest.prompt);
+      expect(registry.sdkConfigs.derived.prompt).toBe(manifest.prompt);
       expect(
         registry.agents.find((agent) => agent.name === 'explorer'),
       ).toBeDefined();

+ 65 - 0
src/marketplace/routing.test.ts

@@ -151,6 +151,59 @@ describe('marketplace routing renderer', () => {
     );
   });
 
+  test('keeps v3 routing metadata in the orchestrator prompt only', () => {
+    const root = mkdtempSync(join(tmpdir(), 'marketplace-routing-v3-'));
+    try {
+      const manifest = {
+        ...v3Manifest,
+        skills: [],
+        mcps: [],
+        tools: [],
+      } satisfies MarketplacePackageManifestV3;
+      const store = new MarketplaceStore({ rootDir: root });
+      store.install({ manifest });
+      RuntimeConfig.reset(root);
+      const runtime = RuntimeConfig.init(root, {
+        preset: 'work',
+        presets: {
+          work: {
+            agents: {},
+            marketplace: { agents: [manifest.id] },
+          },
+        },
+      });
+      const registry = buildResolvedAgentRegistry(runtime, {
+        marketplaceStore: store,
+        availableMcpNames: [],
+      });
+      const activatedAgent = registry.agents.find(
+        (agent) => agent.name === manifest.agentName,
+      );
+      const route = registry.routing.find(
+        (entry) => entry.agentName === manifest.agentName,
+      );
+      const orchestrator = registry.agents.find(
+        (agent) => agent.name === 'orchestrator',
+      );
+
+      expect(activatedAgent?.config.prompt).toBe(manifest.prompt);
+      for (const metadata of [
+        '- Package:',
+        '- Package lane:',
+        '**Delegate when:**',
+        '**Avoid:**',
+      ]) {
+        expect(activatedAgent?.config.prompt).not.toContain(metadata);
+      }
+      for (const metadata of ['- Stats:', '**Delegate when:**', '**Avoid:**']) {
+        expect(route?.routingBlock).toContain(metadata);
+        expect(orchestrator?.config.prompt).toContain(metadata);
+      }
+    } finally {
+      rmSync(root, { recursive: true, force: true });
+    }
+  });
+
   test('renders a runtime display alias without changing the manifest default', () => {
     const derived = {
       ...baseManifest,
@@ -194,10 +247,22 @@ describe('marketplace routing renderer', () => {
       const route = registry.routing.find(
         (entry) => entry.agentName === 'build-agent',
       );
+      const activatedAgent = registry.agents.find(
+        (agent) => agent.name === 'routing-agent',
+      );
+      const orchestrator = registry.agents.find(
+        (agent) => agent.name === 'orchestrator',
+      );
 
       expect(route?.routingBlock).toBe(
         renderMarketplaceAutoDelegationBlock(manifest, 'build-agent'),
       );
+      expect(activatedAgent?.config.prompt).toBe(manifest.prompt);
+      expect(activatedAgent?.config.prompt).not.toContain('- Package:');
+      expect(activatedAgent?.config.prompt).not.toContain('- Package lane:');
+      expect(activatedAgent?.config.prompt).not.toContain('- Stats:');
+      expect(activatedAgent?.config.prompt).not.toContain('**Delegate when:**');
+      expect(orchestrator?.config.prompt).toContain(route?.routingBlock ?? '');
     } finally {
       rmSync(root, { recursive: true, force: true });
     }