feat(FN-1511): apply skill selection to triage and executor sessions

This commit is contained in:
gsxdsm
2026-04-14 08:20:22 -07:00
parent 0944fff7ba
commit 4e217e493d
3 changed files with 53 additions and 0 deletions

View File

@@ -12,6 +12,7 @@ import { findWorktreeUser } from "./merger.js";
import { generateWorktreeName, slugify } from "./worktree-names.js";
import { Type, type Static } from "@mariozechner/pi-ai";
import { createKbAgent, describeModel, promptWithFallback, compactSessionContext } from "./pi.js";
import { buildSessionSkillContext } from "./session-skill-context.js";
import { reviewStep, type ReviewVerdict } from "./reviewer.js";
import { AuthStorage, ModelRegistry, SessionManager, getAgentDir, type ToolDefinition, type AgentSession } from "@mariozechner/pi-coding-agent";
import { PRIORITY_EXECUTE, type AgentSemaphore } from "./concurrency.js";
@@ -1123,6 +1124,15 @@ export class TaskExecutor {
// When runStepsInNewSessions is enabled, each step runs in its own
// fresh agent session via StepSessionExecutor. Otherwise, the existing
// single-session flow runs all steps in one monolithic session.
// Build skill selection context early so it's available in both paths
const skillContext = await buildSessionSkillContext({
agentStore: this.options.agentStore!,
task: detail,
sessionPurpose: "executor",
projectRootDir: this.rootDir,
});
if (settings.runStepsInNewSessions) {
// ── Step-Session Path ──────────────────────────────────────────
executorLog.log(`${task.id}: using step-session mode (maxParallel=${settings.maxParallelSteps ?? 2})`);
@@ -1136,6 +1146,8 @@ export class TaskExecutor {
semaphore: this.options.semaphore,
stuckTaskDetector: this.options.stuckTaskDetector,
pluginRunner: this.options.pluginRunner,
// Pass skill selection context from the main executor session
skillSelection: skillContext.skillSelectionContext,
onStepStart: (stepIndex) => {
this.options.stuckTaskDetector?.recordProgress(task.id);
try {
@@ -1436,6 +1448,8 @@ export class TaskExecutor {
fallbackModelId: executorFallbackModelId,
defaultThinkingLevel: executorThinkingLevel,
sessionManager,
// Skill selection: use assigned agent skills if available, otherwise role fallback
...(skillContext.skillSelectionContext ? { skillSelection: skillContext.skillSelectionContext } : {}),
});
if (isResuming) {
@@ -1666,6 +1680,8 @@ export class TaskExecutor {
fallbackModelId: executorFallbackModelId,
defaultThinkingLevel: executorThinkingLevel,
sessionManager: SessionManager.create(worktreePath),
// Skill selection: use assigned agent skills if available, otherwise role fallback
...(skillContext.skillSelectionContext ? { skillSelection: skillContext.skillSelectionContext } : {}),
});
// Update session file for the retry session (so pause/resume works)
if (retrySessionFile) {
@@ -3091,6 +3107,14 @@ and show an appropriate message to the user.\`
const stepInstructions = await this.resolveInstructionsForRole("executor");
const stepSystemPrompt = buildSystemPromptWithInstructions(systemPrompt, stepInstructions);
// Build skill selection context for workflow step session
const skillContext = await buildSessionSkillContext({
agentStore: this.options.agentStore!,
task,
sessionPurpose: "executor",
projectRootDir: this.rootDir,
});
const { session } = await createKbAgent({
cwd: worktreePath,
systemPrompt: stepSystemPrompt,
@@ -3100,6 +3124,8 @@ and show an appropriate message to the user.\`
fallbackProvider: settings.fallbackProvider,
fallbackModelId: settings.fallbackModelId,
defaultThinkingLevel: settings.defaultThinkingLevel,
// Skill selection: use assigned agent skills if available, otherwise role fallback
...(skillContext.skillSelectionContext ? { skillSelection: skillContext.skillSelectionContext } : {}),
});
executorLog.log(`${task.id}: workflow step '${workflowStep.name}' using model ${describeModel(session)}${useOverride ? " (workflow step override)" : ""}`);
@@ -3911,6 +3937,15 @@ and show an appropriate message to the user.\`
const childBasePrompt = `You are a child agent spawned by a parent task executor. Your job is to complete the following delegated task. Work autonomously and thoroughly. Report your findings and results.\n\nParent task: ${taskId}\nChild agent: ${agent.id} (${name})`;
const childSystemPrompt = buildSystemPromptWithInstructions(childBasePrompt, childInstructions);
// Build skill selection context for child agent session
const childTask = await this.store.getTask(taskId);
const skillContext = await buildSessionSkillContext({
agentStore: this.options.agentStore!,
task: childTask,
sessionPurpose: "executor",
projectRootDir: this.rootDir,
});
// Create child agent session
const { session: childSession } = await createKbAgent({
cwd: childWorktreePath,
@@ -3920,6 +3955,8 @@ and show an appropriate message to the user.\`
defaultModelId: settings.defaultModelId,
fallbackProvider: settings.fallbackProvider,
fallbackModelId: settings.fallbackModelId,
// Skill selection: use assigned agent skills if available, otherwise role fallback
...(skillContext.skillSelectionContext ? { skillSelection: skillContext.skillSelectionContext } : {}),
});
// Store tracking state

View File

@@ -20,6 +20,7 @@ import type { AgentSession } from "@mariozechner/pi-coding-agent";
import type { TaskDetail, Settings, TaskStore } from "@fusion/core";
import { createKbAgent, promptWithFallback, describeModel, compactSessionContext } from "./pi.js";
import type { SkillSelectionContext } from "./skill-resolver.js";
import { generateWorktreeName } from "./worktree-names.js";
import { AgentSemaphore } from "./concurrency.js";
import { StuckTaskDetector } from "./stuck-task-detector.js";
@@ -74,6 +75,8 @@ export interface StepSessionExecutorOptions {
onStepStart?: (stepIndex: number) => void;
/** Callback invoked when a step completes (success or failure). */
onStepComplete?: (stepIndex: number, result: StepResult) => void;
/** Optional skill selection context for session creation. */
skillSelection?: SkillSelectionContext;
}
// ── File Scope Extraction ─────────────────────────────────────────────
@@ -789,6 +792,8 @@ export class StepSessionExecutor {
agentLogger.onToolEnd(name, isError, result);
stuckTaskDetector?.recordActivity(trackingKey);
},
// Skill selection from step-session executor options
...(this.options.skillSelection ? { skillSelection: this.options.skillSelection } : {}),
});
session = createResult.session;

View File

@@ -14,6 +14,7 @@ import type {
} from "@mariozechner/pi-coding-agent";
import { createKbAgent, describeModel, promptWithFallback } from "./pi.js";
import { reviewStep, type ReviewVerdict } from "./reviewer.js";
import { buildSessionSkillContext } from "./session-skill-context.js";
import { PRIORITY_SPECIFY, type AgentSemaphore } from "./concurrency.js";
import { AgentLogger } from "./agent-logger.js";
import { resolveAgentInstructions, buildSystemPromptWithInstructions } from "./agent-instructions.js";
@@ -679,6 +680,14 @@ export class TriageProcessor {
triageInstructions,
);
// Build skill selection context (assigned agent skills take precedence over role fallback)
const skillContext = await buildSessionSkillContext({
agentStore: this.options.agentStore!,
task,
sessionPurpose: "triage",
projectRootDir: this.rootDir,
});
const { session } = await createKbAgent({
cwd: this.rootDir,
systemPrompt: triageSystemPrompt,
@@ -706,6 +715,8 @@ export class TriageProcessor {
? settings.planningFallbackModelId
: settings.fallbackModelId,
defaultThinkingLevel: settings.defaultThinkingLevel,
// Skill selection: use assigned agent skills if available, otherwise role fallback
...(skillContext.skillSelectionContext ? { skillSelection: skillContext.skillSelectionContext } : {}),
});
const modelDesc = describeModel(session);