feat(FN-3703): implement assigned-agent triage inheritance in engine and tr
Implements assigned-agent triage inheritance (FN-3703), allowing triage logic to be delegated to the assigned agent rather than always routing through the original owner, with test coverage and documentation. Also adds immediate wake controls for agent inbox and message API (FN-3087), wires shared s Fusion-Task-Id: FN-3703
This commit is contained in:
@@ -19,13 +19,18 @@ import type {
|
||||
AgentSession,
|
||||
} from "@mariozechner/pi-coding-agent";
|
||||
import { describeModel, promptWithFallback } from "./pi.js";
|
||||
import { createResolvedAgentSession, extractRuntimeHint } from "./agent-session-helpers.js";
|
||||
import {
|
||||
createResolvedAgentSession,
|
||||
extractRuntimeHint,
|
||||
resolvePlanningSessionModel,
|
||||
} from "./agent-session-helpers.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,
|
||||
resolveAgentInstructionsWithRatings,
|
||||
buildSystemPromptWithInstructions,
|
||||
buildPluginPromptSection,
|
||||
} from "./agent-instructions.js";
|
||||
@@ -934,6 +939,10 @@ export class TriageProcessor {
|
||||
// Track subtasks created during triage when breakIntoSubtasks was requested.
|
||||
const createdSubtasksRef: { current: string[] } = { current: [] };
|
||||
|
||||
const assignedAgent = task.assignedAgentId && this.options.agentStore
|
||||
? await this.options.agentStore.getAgent(task.assignedAgentId).catch(() => null)
|
||||
: null;
|
||||
|
||||
const customTools = [
|
||||
...this.createTriageTools({
|
||||
parentTaskId: task.id,
|
||||
@@ -949,7 +958,15 @@ export class TriageProcessor {
|
||||
getSettings: async () => this.store.getSettings(),
|
||||
})
|
||||
: []),
|
||||
...createMemoryTools(this.rootDir, settings),
|
||||
...createMemoryTools(this.rootDir, settings, assignedAgent
|
||||
? {
|
||||
agentMemory: {
|
||||
agentId: assignedAgent.id,
|
||||
agentName: assignedAgent.name,
|
||||
memory: assignedAgent.memory,
|
||||
},
|
||||
}
|
||||
: undefined),
|
||||
// Agent delegation tools — discover and delegate work to other agents.
|
||||
...(this.options.agentStore ? [
|
||||
createListAgentsTool(this.options.agentStore),
|
||||
@@ -967,19 +984,22 @@ export class TriageProcessor {
|
||||
),
|
||||
];
|
||||
|
||||
const assignedAgent = task.assignedAgentId && this.options.agentStore
|
||||
? await this.options.agentStore.getAgent(task.assignedAgentId).catch(() => null)
|
||||
: null;
|
||||
let triageRuntimeHint = extractRuntimeHint(assignedAgent?.runtimeConfig);
|
||||
|
||||
// Resolve per-agent custom instructions for the triage role
|
||||
// Resolve per-agent custom instructions for the triage role or assigned agent.
|
||||
let triageInstructions = "";
|
||||
if (this.options.agentStore) {
|
||||
if (assignedAgent) {
|
||||
triageInstructions = await resolveAgentInstructionsWithRatings(
|
||||
assignedAgent,
|
||||
this.rootDir,
|
||||
this.options.agentStore,
|
||||
);
|
||||
} else if (this.options.agentStore) {
|
||||
try {
|
||||
const agents = await this.options.agentStore.listAgents({ role: "triage" });
|
||||
for (const agent of agents) {
|
||||
triageRuntimeHint ??= extractRuntimeHint(agent.runtimeConfig);
|
||||
if (agent.instructionsText || agent.instructionsPath) {
|
||||
if (agent.instructionsText || agent.instructionsPath || agent.soul || agent.memory) {
|
||||
triageInstructions = await resolveAgentInstructions(agent, this.rootDir);
|
||||
break;
|
||||
}
|
||||
@@ -990,10 +1010,13 @@ export class TriageProcessor {
|
||||
}
|
||||
}
|
||||
planLog.log(`${task.id}: planning in ${isFast ? "fast" : "standard"} mode`);
|
||||
const triageIdentitySection = assignedAgent
|
||||
? `## Identity\n\nYou are ${assignedAgent.name}${assignedAgent.title?.trim() ? `, ${assignedAgent.title.trim()}` : ""} (agent ID: ${assignedAgent.id}, role: ${assignedAgent.role}).`
|
||||
: "";
|
||||
const triageSystemPrompt = buildSystemPromptWithInstructions(
|
||||
resolveAgentPrompt("triage", settings.agentPrompts)
|
||||
|| (isFast ? FAST_TRIAGE_SYSTEM_PROMPT : TRIAGE_SYSTEM_PROMPT),
|
||||
triageInstructions,
|
||||
[triageIdentitySection, triageInstructions].filter((section) => section.trim()).join("\n\n"),
|
||||
);
|
||||
const triageContributions = this.options.pluginRunner
|
||||
?.getPromptContributionsForSurface("triage")
|
||||
@@ -1030,30 +1053,16 @@ export class TriageProcessor {
|
||||
onThinking: agentLogger.onThinking,
|
||||
onToolStart: agentLogger.onToolStart,
|
||||
onToolEnd: agentLogger.onToolEnd,
|
||||
// Resolve planning model using canonical lane hierarchy:
|
||||
// 1. Task planning override pair (planningModelProvider + planningModelId)
|
||||
// 2. Project planning lane pair (planningProvider + planningModelId)
|
||||
// 3. Global planning lane pair (planningGlobalProvider + planningGlobalModelId)
|
||||
// 4. Project default override pair (defaultProviderOverride + defaultModelIdOverride)
|
||||
// 5. Global default pair (defaultProvider + defaultModelId)
|
||||
defaultProvider: task.planningModelProvider && task.planningModelId
|
||||
? task.planningModelProvider
|
||||
: (settings.planningProvider && settings.planningModelId
|
||||
? settings.planningProvider
|
||||
: (settings.planningGlobalProvider && settings.planningGlobalModelId
|
||||
? settings.planningGlobalProvider
|
||||
: (settings.defaultProviderOverride && settings.defaultModelIdOverride
|
||||
? settings.defaultProviderOverride
|
||||
: settings.defaultProvider))),
|
||||
defaultModelId: task.planningModelProvider && task.planningModelId
|
||||
? task.planningModelId
|
||||
: (settings.planningProvider && settings.planningModelId
|
||||
? settings.planningModelId
|
||||
: (settings.planningGlobalProvider && settings.planningGlobalModelId
|
||||
? settings.planningGlobalModelId
|
||||
: (settings.defaultProviderOverride && settings.defaultModelIdOverride
|
||||
? settings.defaultModelIdOverride
|
||||
: settings.defaultModelId))),
|
||||
// Resolve planning model using executor-style precedence:
|
||||
// 1. Assigned durable agent runtime model pair when complete
|
||||
// 2. Task planning override pair
|
||||
// 3. Planning/project/global fallbacks
|
||||
...resolvePlanningSessionModel(
|
||||
task.planningModelProvider,
|
||||
task.planningModelId,
|
||||
settings,
|
||||
assignedAgent?.runtimeConfig,
|
||||
),
|
||||
fallbackProvider: settings.planningFallbackProvider && settings.planningFallbackModelId
|
||||
? settings.planningFallbackProvider
|
||||
: settings.fallbackProvider,
|
||||
|
||||
Reference in New Issue
Block a user