FN-5759: inject active-goal context across engine lanes
Enable always-on active-goal prompt injection and diagnostic wiring across triage/executor flows. - Refactor goal-context injection so agent heartbeat, executor, and triage all apply the active-goal seam consistently. - Extend goal injection diagnostics implementation and exports, plus anchoring audit touchpoints. - Add and update tests for goal context injection behavior and diagnostics wiring. - Update architecture and diagnostics docs to describe the new always-on seam. Files changed: docs/architecture.md | 2 +- docs/diagnostics.md | 2 +- .../src/__tests__/goal-context-injection.test.ts | 19 ++++- .../goal-injection-diagnostics-wiring.test.ts | 98 +++++++++++++++++++++- packages/engine/src/agent-heartbeat.ts | 31 ++----- packages/engine/src/executor.ts | 32 ++----- packages/engine/src/goal-anchoring-audit.ts | 2 +- packages/engine/src/goal-injection-diagnostics.ts | 42 +++++++++- packages/engine/src/index.ts | 2 + packages/engine/src/triage.ts | 28 +++++-- 10 files changed, 191 insertions(+), 67 deletions(-) Fusion-Task-Id: FN-5759 Fusion-Task-Lineage: 1419de34-2e86-4459-bcd4-426f15c067fe
This commit is contained in:
@@ -436,7 +436,7 @@ Hybrid evaluator pipeline (FN-3389/FN-3391):
|
||||
- `reviewer`
|
||||
- `heartbeat`
|
||||
- Integration points append the built plugin section to the role-specific system/task prompt only when contributions exist, preserving existing prompts when no plugins contribute.
|
||||
- Executor and heartbeat system prompts also inject a shared `goalContext` dynamic layer via `buildGoalContextSection(...)`; when no active goals exist, no goal section is emitted.
|
||||
- Executor, heartbeat, and planning (triage) system prompts inject a shared `goalContext` dynamic layer via the canonical `resolveAndEmitGoalContext(...)` seam (which uses `buildGoalContextSection(...)`); when no active goals exist, no goal section is emitted.
|
||||
|
||||
### Agent Permissions
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
## Goal injection diagnostics (`[goal-injection]`)
|
||||
|
||||
Executor and heartbeat runs emit one goal-injection diagnostic with outcome `applied`, `no-goals`, or `disabled-or-failed`.
|
||||
Executor, heartbeat, and planning runs emit one goal-injection diagnostic with outcome `applied`, `no-goals`, or `disabled-or-failed`.
|
||||
|
||||
- Run-audit event: `prompt:goal-injection` (`database` domain, target lane) with metadata `{ lane, outcome, goalCount, goalIds, truncated, reason?, errorClass?, runId?, agentId?, taskId? }`.
|
||||
- Task log (executor lane with `taskId`): `[goal-injection] <outcome> count=<n> ids=<json-array> truncated=<bool> ...`.
|
||||
|
||||
@@ -32,8 +32,17 @@ function buildHeartbeatPrompt(activeGoals: Goal[]): { goalContext: string; promp
|
||||
return { goalContext, prompt: collapsePromptLayers(layers) };
|
||||
}
|
||||
|
||||
function buildPlanningPrompt(activeGoals: Goal[]): { goalContext: string; prompt: string } {
|
||||
const goalContext = buildGoalContextSection({ activeGoals }).text;
|
||||
const layers = buildPromptLayers({
|
||||
basePrompt: "PLANNING_BASE",
|
||||
goalContext,
|
||||
});
|
||||
return { goalContext, prompt: collapsePromptLayers(layers) };
|
||||
}
|
||||
|
||||
describe("goal context lane injection parity", () => {
|
||||
it("injects byte-identical goal block across heartbeat and executor lanes", () => {
|
||||
it("injects byte-identical goal block across heartbeat executor and planning lanes", () => {
|
||||
const activeGoals = [
|
||||
goal("G-001", "Ship CLI", "2026-01-01T00:00:00.000Z"),
|
||||
goal("G-002", "Harden engine", "2026-01-02T00:00:00.000Z"),
|
||||
@@ -42,21 +51,27 @@ describe("goal context lane injection parity", () => {
|
||||
const expectedGoalBlock = buildGoalContextSection({ activeGoals }).text;
|
||||
const executor = buildExecutorPrompt(activeGoals);
|
||||
const heartbeat = buildHeartbeatPrompt(activeGoals);
|
||||
const planning = buildPlanningPrompt(activeGoals);
|
||||
|
||||
expect(executor.goalContext).toBe(expectedGoalBlock);
|
||||
expect(heartbeat.goalContext).toBe(expectedGoalBlock);
|
||||
expect(planning.goalContext).toBe(expectedGoalBlock);
|
||||
});
|
||||
|
||||
it("emits no goal header or blank-line artifact when active goals are empty", () => {
|
||||
const executor = buildExecutorPrompt([]);
|
||||
const heartbeat = buildHeartbeatPrompt([]);
|
||||
const planning = buildPlanningPrompt([]);
|
||||
|
||||
expect(executor.goalContext).toBe("");
|
||||
expect(heartbeat.goalContext).toBe("");
|
||||
expect(planning.goalContext).toBe("");
|
||||
expect(executor.prompt).toBe("EXECUTOR_BASE");
|
||||
expect(heartbeat.prompt).toBe("HEARTBEAT_BASE");
|
||||
expect(planning.prompt).toBe("PLANNING_BASE");
|
||||
expect(executor.prompt).not.toContain("## Active Goals");
|
||||
expect(heartbeat.prompt).not.toContain("## Active Goals");
|
||||
expect(planning.prompt).not.toContain("## Active Goals");
|
||||
});
|
||||
|
||||
it("uses shared formatter output without lane-local reformatting", () => {
|
||||
@@ -65,8 +80,10 @@ describe("goal context lane injection parity", () => {
|
||||
const helperOutput = buildGoalContextSection({ activeGoals }).text;
|
||||
const executor = buildExecutorPrompt(activeGoals);
|
||||
const heartbeat = buildHeartbeatPrompt(activeGoals);
|
||||
const planning = buildPlanningPrompt(activeGoals);
|
||||
|
||||
expect(executor.goalContext).toEqual(helperOutput);
|
||||
expect(heartbeat.goalContext).toEqual(helperOutput);
|
||||
expect(planning.goalContext).toEqual(helperOutput);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import type { Goal } from "@fusion/core";
|
||||
import { emitGoalInjectionDiagnostic, resolveGoalContextForDiagnostics } from "../goal-injection-diagnostics.js";
|
||||
import {
|
||||
emitGoalInjectionDiagnostic,
|
||||
resolveAndEmitGoalContext,
|
||||
resolveGoalContextForDiagnostics,
|
||||
} from "../goal-injection-diagnostics.js";
|
||||
|
||||
function goal(id: string, title: string, createdAt: string): Goal {
|
||||
return {
|
||||
@@ -14,6 +18,98 @@ function goal(id: string, title: string, createdAt: string): Goal {
|
||||
}
|
||||
|
||||
describe("goal injection diagnostics wiring seam", () => {
|
||||
it("resolveAndEmitGoalContext emits applied diagnostics and audit for planning lane", async () => {
|
||||
const goals = [goal("G-1", "one", "2026-01-01T00:00:00.000Z")];
|
||||
const store = {
|
||||
getGoalStore: () => ({ listGoals: () => goals }),
|
||||
logEntry: vi.fn().mockResolvedValue(undefined),
|
||||
recordRunAuditEvent: vi.fn().mockResolvedValue(undefined),
|
||||
} as any;
|
||||
const audit = { database: vi.fn().mockResolvedValue(undefined) } as any;
|
||||
|
||||
const resolution = await resolveAndEmitGoalContext({
|
||||
lane: "planning",
|
||||
store,
|
||||
audit,
|
||||
taskId: "FN-1",
|
||||
runContext: { runId: "plan-run", agentId: "agent-1", taskId: "FN-1", phase: "plan" },
|
||||
});
|
||||
|
||||
expect(resolution.classification).toMatchObject({ outcome: "applied", goalCount: 1, goalIds: ["G-1"] });
|
||||
expect(audit.database).toHaveBeenCalledTimes(1);
|
||||
expect(store.recordRunAuditEvent).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("resolveAndEmitGoalContext emits no-goals semantics when goal store is empty", async () => {
|
||||
const store = {
|
||||
getGoalStore: () => ({ listGoals: () => [] }),
|
||||
logEntry: vi.fn().mockResolvedValue(undefined),
|
||||
recordRunAuditEvent: vi.fn().mockResolvedValue(undefined),
|
||||
} as any;
|
||||
const audit = { database: vi.fn().mockResolvedValue(undefined) } as any;
|
||||
|
||||
const resolution = await resolveAndEmitGoalContext({
|
||||
lane: "executor",
|
||||
store,
|
||||
audit,
|
||||
taskId: "FN-1",
|
||||
runContext: { runId: "exec-run", agentId: "agent-1", taskId: "FN-1", phase: "execute" },
|
||||
});
|
||||
|
||||
expect(resolution.goalContext).toBe("");
|
||||
expect(resolution.classification).toMatchObject({ outcome: "no-goals", goalCount: 0, goalIds: [] });
|
||||
expect(audit.database.mock.calls[0][0]).toMatchObject({
|
||||
type: "goal:injection-skipped",
|
||||
metadata: { lane: "executor", count: 0, reason: "no-active-goals" },
|
||||
});
|
||||
});
|
||||
|
||||
it("routes heartbeat executor and planning lanes through the same canonical helper", async () => {
|
||||
const lanes = ["heartbeat", "executor", "planning"] as const;
|
||||
for (const lane of lanes) {
|
||||
const store = {
|
||||
getGoalStore: () => ({ listGoals: () => [goal("G-1", "one", "2026-01-01T00:00:00.000Z")] }),
|
||||
logEntry: vi.fn().mockResolvedValue(undefined),
|
||||
recordRunAuditEvent: vi.fn().mockResolvedValue(undefined),
|
||||
} as any;
|
||||
const audit = { database: vi.fn().mockResolvedValue(undefined) } as any;
|
||||
|
||||
const resolution = await resolveAndEmitGoalContext({
|
||||
lane,
|
||||
store,
|
||||
audit,
|
||||
taskId: "FN-1",
|
||||
runContext: { runId: `${lane}-run`, agentId: "agent-1", taskId: "FN-1", phase: lane },
|
||||
});
|
||||
|
||||
expect(resolution.classification).toMatchObject({ outcome: "applied", goalCount: 1, goalIds: ["G-1"] });
|
||||
expect(audit.database).toHaveBeenCalledTimes(1);
|
||||
expect(audit.database.mock.calls[0][0].metadata.lane).toBe(lane);
|
||||
expect(store.recordRunAuditEvent).toHaveBeenCalledTimes(1);
|
||||
expect(store.recordRunAuditEvent.mock.calls[0][0].metadata.lane).toBe(lane);
|
||||
}
|
||||
});
|
||||
|
||||
it("resolveAndEmitGoalContext handles missing getGoalStore with disabled classification", async () => {
|
||||
const store = {
|
||||
logEntry: vi.fn().mockResolvedValue(undefined),
|
||||
recordRunAuditEvent: vi.fn().mockResolvedValue(undefined),
|
||||
} as any;
|
||||
const audit = { database: vi.fn().mockResolvedValue(undefined) } as any;
|
||||
|
||||
const resolution = await resolveAndEmitGoalContext({
|
||||
lane: "heartbeat",
|
||||
store,
|
||||
audit,
|
||||
taskId: "FN-1",
|
||||
runContext: { runId: "hb-run", agentId: "agent-1", taskId: "FN-1", phase: "heartbeat" },
|
||||
});
|
||||
|
||||
expect(resolution.goalContext).toBe("");
|
||||
expect(resolution.classification).toMatchObject({ outcome: "disabled-or-failed", reason: "store-unavailable" });
|
||||
expect(audit.database).toHaveBeenCalledTimes(1);
|
||||
expect(store.recordRunAuditEvent).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
it("emits applied audit metadata for positive injection", async () => {
|
||||
const goals = [goal("G-1", "one", "2026-01-01T00:00:00.000Z"), goal("G-2", "two", "2026-01-02T00:00:00.000Z")];
|
||||
const recordRunAuditEvent = vi.fn().mockResolvedValue(undefined);
|
||||
|
||||
@@ -32,11 +32,7 @@ import {
|
||||
} from "./agent-instructions.js";
|
||||
import { resolveHeartbeatPromptTemplate, resolveHeartbeatScopeDisciplineMode, selectHeartbeatProcedure } from "./heartbeat-procedure-resolver.js";
|
||||
import { buildPromptLayers, collapsePromptLayers } from "./prompt-layers.js";
|
||||
import {
|
||||
emitGoalInjectionDiagnostic,
|
||||
resolveGoalContextForDiagnostics,
|
||||
} from "./goal-injection-diagnostics.js";
|
||||
import { emitGoalAnchoringAudit } from "./goal-anchoring-audit.js";
|
||||
import { resolveAndEmitGoalContext } from "./goal-injection-diagnostics.js";
|
||||
import { createLogger, heartbeatLog, formatError } from "./logger.js";
|
||||
import { acquireTaskWorktree } from "./worktree-acquisition.js";
|
||||
import { createRunAuditor, type EngineRunContext } from "./run-audit.js";
|
||||
@@ -2387,31 +2383,14 @@ export class HeartbeatMonitor {
|
||||
heartbeatLog.log(`applied plugin prompt contributions for heartbeat surface`);
|
||||
}
|
||||
|
||||
const heartbeatGoalResolution = resolveGoalContextForDiagnostics({
|
||||
listActiveGoals:
|
||||
typeof taskStore.getGoalStore === "function"
|
||||
? () => taskStore.getGoalStore().listGoals({ status: "active" })
|
||||
: undefined,
|
||||
});
|
||||
const heartbeatGoalContext = heartbeatGoalResolution.goalContext;
|
||||
const heartbeatGoalClassification = heartbeatGoalResolution.classification;
|
||||
|
||||
await emitGoalAnchoringAudit(audit, {
|
||||
const heartbeatGoalResolution = await resolveAndEmitGoalContext({
|
||||
lane: "heartbeat",
|
||||
taskId,
|
||||
goalsInjected: heartbeatGoalClassification.goalCount,
|
||||
truncated: heartbeatGoalClassification.truncated,
|
||||
reason: heartbeatGoalClassification.outcome === "no-goals" ? "no-active-goals" : undefined,
|
||||
});
|
||||
|
||||
await emitGoalInjectionDiagnostic({
|
||||
lane: "heartbeat",
|
||||
...heartbeatGoalClassification,
|
||||
runId: run.id,
|
||||
agentId,
|
||||
store: taskStore,
|
||||
audit,
|
||||
taskId,
|
||||
runContext: engineRunContext,
|
||||
});
|
||||
const heartbeatGoalContext = heartbeatGoalResolution.goalContext;
|
||||
|
||||
const heartbeatLayers = buildPromptLayers({
|
||||
basePrompt: baseHeartbeatSystemPrompt,
|
||||
|
||||
@@ -100,11 +100,7 @@ import {
|
||||
buildPluginPromptSection,
|
||||
} from "./agent-instructions.js";
|
||||
import { buildPromptLayers, collapsePromptLayers } from "./prompt-layers.js";
|
||||
import {
|
||||
emitGoalInjectionDiagnostic,
|
||||
resolveGoalContextForDiagnostics,
|
||||
} from "./goal-injection-diagnostics.js";
|
||||
import { emitGoalAnchoringAudit } from "./goal-anchoring-audit.js";
|
||||
import { resolveAndEmitGoalContext } from "./goal-injection-diagnostics.js";
|
||||
import type { AgentReflectionService } from "./agent-reflection.js";
|
||||
import { createRunAuditor, generateSyntheticRunId, type EngineRunContext, type RunAuditor } from "./run-audit.js";
|
||||
import { AutoRecoveryDispatcher } from "./auto-recovery.js";
|
||||
@@ -4170,32 +4166,14 @@ export class TaskExecutor {
|
||||
executorLog.log(`${task.id}: applied plugin prompt contributions for executor-system surface`);
|
||||
}
|
||||
|
||||
const executorGoalResolution = resolveGoalContextForDiagnostics({
|
||||
listActiveGoals:
|
||||
typeof this.store.getGoalStore === "function"
|
||||
? () => this.store.getGoalStore().listGoals({ status: "active" })
|
||||
: undefined,
|
||||
});
|
||||
const executorGoalContext = executorGoalResolution.goalContext;
|
||||
const executorGoalClassification = executorGoalResolution.classification;
|
||||
|
||||
await emitGoalAnchoringAudit(audit, {
|
||||
const executorGoalResolution = await resolveAndEmitGoalContext({
|
||||
lane: "executor",
|
||||
taskId: task.id,
|
||||
goalsInjected: executorGoalClassification.goalCount,
|
||||
truncated: executorGoalClassification.truncated,
|
||||
reason: executorGoalClassification.outcome === "no-goals" ? "no-active-goals" : undefined,
|
||||
});
|
||||
|
||||
await emitGoalInjectionDiagnostic({
|
||||
lane: "executor",
|
||||
...executorGoalClassification,
|
||||
runId: engineRunContext.runId,
|
||||
agentId: engineRunContext.agentId,
|
||||
taskId: task.id,
|
||||
store: this.store,
|
||||
audit,
|
||||
taskId: task.id,
|
||||
runContext: engineRunContext,
|
||||
});
|
||||
const executorGoalContext = executorGoalResolution.goalContext;
|
||||
|
||||
const executorLayers = buildPromptLayers({
|
||||
basePrompt: getExecutorSystemPrompt(settings),
|
||||
|
||||
@@ -15,7 +15,7 @@ if (new Set(GOAL_AUDIT_TYPES).size !== GOAL_AUDIT_TYPES.length || GOAL_AUDIT_TYP
|
||||
}
|
||||
|
||||
/** Engine lane that attempted goal anchoring; powers dashboard filtering for cite-rate observability. */
|
||||
export type GoalAnchoringLane = "heartbeat" | "executor";
|
||||
export type GoalAnchoringLane = "heartbeat" | "executor" | "planning";
|
||||
|
||||
/**
|
||||
* Structured goal-injection audit payload.
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import type { Goal } from "@fusion/core";
|
||||
import { buildGoalContextSection, type GoalInjectionResult } from "./goal-context-injector.js";
|
||||
import type { TaskStore } from "@fusion/core";
|
||||
import type { GoalAnchoringLane } from "./goal-anchoring-audit.js";
|
||||
import { emitGoalAnchoringAudit } from "./goal-anchoring-audit.js";
|
||||
import type { EngineRunContext } from "./run-audit.js";
|
||||
import type { RunAuditor } from "./run-audit.js";
|
||||
import { createLogger } from "./logger.js";
|
||||
|
||||
const diagnosticsLog = createLogger("goal-injection-diagnostics");
|
||||
@@ -15,7 +18,7 @@ export type GoalInjectionDisabledReason =
|
||||
| "injector-threw";
|
||||
|
||||
export interface GoalInjectionDiagnostic {
|
||||
lane: "heartbeat" | "executor";
|
||||
lane: GoalAnchoringLane;
|
||||
outcome: GoalInjectionOutcome;
|
||||
goalCount: number;
|
||||
goalIds: string[];
|
||||
@@ -84,6 +87,14 @@ export interface GoalContextResolution {
|
||||
classification: GoalInjectionClassification;
|
||||
}
|
||||
|
||||
export interface ResolveAndEmitGoalContextInput {
|
||||
lane: GoalAnchoringLane;
|
||||
store: TaskStore;
|
||||
audit: RunAuditor;
|
||||
taskId?: string;
|
||||
runContext?: EngineRunContext | null;
|
||||
}
|
||||
|
||||
export interface ResolveGoalContextInput {
|
||||
listActiveGoals?: () => Goal[];
|
||||
injector?: (activeGoals: Goal[]) => GoalInjectionResult;
|
||||
@@ -121,6 +132,35 @@ export function resolveGoalContextForDiagnostics(input: ResolveGoalContextInput)
|
||||
}
|
||||
}
|
||||
|
||||
export async function resolveAndEmitGoalContext(input: ResolveAndEmitGoalContextInput): Promise<GoalContextResolution> {
|
||||
const resolution = resolveGoalContextForDiagnostics({
|
||||
listActiveGoals:
|
||||
typeof input.store.getGoalStore === "function"
|
||||
? () => input.store.getGoalStore().listGoals({ status: "active" })
|
||||
: undefined,
|
||||
});
|
||||
|
||||
await emitGoalAnchoringAudit(input.audit, {
|
||||
lane: input.lane,
|
||||
taskId: input.taskId,
|
||||
goalsInjected: resolution.classification.goalCount,
|
||||
truncated: resolution.classification.truncated,
|
||||
reason: resolution.classification.outcome === "no-goals" ? "no-active-goals" : undefined,
|
||||
});
|
||||
|
||||
await emitGoalInjectionDiagnostic({
|
||||
lane: input.lane,
|
||||
...resolution.classification,
|
||||
runId: input.runContext?.runId,
|
||||
agentId: input.runContext?.agentId,
|
||||
taskId: input.taskId,
|
||||
store: input.store,
|
||||
runContext: input.runContext,
|
||||
});
|
||||
|
||||
return resolution;
|
||||
}
|
||||
|
||||
function formatAgentLogLine(input: GoalInjectionDiagnostic): string {
|
||||
const ids = JSON.stringify(input.goalIds);
|
||||
const reason = input.reason ? ` reason=${input.reason}` : "";
|
||||
|
||||
@@ -186,11 +186,13 @@ export {
|
||||
classifyGoalInjectionFailure,
|
||||
classifyGoalInjectionResult,
|
||||
emitGoalInjectionDiagnostic,
|
||||
resolveAndEmitGoalContext,
|
||||
type GoalInjectionClassification,
|
||||
type GoalInjectionDiagnostic,
|
||||
type GoalInjectionDiagnosticInput,
|
||||
type GoalInjectionDisabledReason,
|
||||
type GoalInjectionOutcome,
|
||||
type ResolveAndEmitGoalContextInput,
|
||||
} from "./goal-injection-diagnostics.js";
|
||||
export {
|
||||
emitGoalAnchoringAudit,
|
||||
|
||||
@@ -81,6 +81,7 @@ import {
|
||||
import { runGhostBugPreflight } from "./triage-preflight.js";
|
||||
import { archiveAsGhostBug } from "./self-healing.js";
|
||||
import { createRunAuditor, generateSyntheticRunId } from "./run-audit.js";
|
||||
import { resolveAndEmitGoalContext } from "./goal-injection-diagnostics.js";
|
||||
|
||||
export const TRIAGE_SYSTEM_PROMPT = `You are a task specification agent for "fn", an AI-orchestrated task board.
|
||||
|
||||
@@ -1171,9 +1172,28 @@ export class TriageProcessor {
|
||||
planLog.log(`${task.id}: applied plugin prompt contributions for triage surface`);
|
||||
}
|
||||
|
||||
const triageRunContext = {
|
||||
runId: generateSyntheticRunId("triage", task.id),
|
||||
agentId: assignedAgent?.id ?? "triage",
|
||||
taskId: task.id,
|
||||
taskLineageId: task.lineageId,
|
||||
phase: "plan",
|
||||
source: "triage",
|
||||
} as const;
|
||||
|
||||
const runAuditor = createRunAuditor(this.store, triageRunContext);
|
||||
const triageGoalResolution = await resolveAndEmitGoalContext({
|
||||
lane: "planning",
|
||||
store: this.store,
|
||||
audit: runAuditor,
|
||||
taskId: task.id,
|
||||
runContext: triageRunContext,
|
||||
});
|
||||
|
||||
const triageLayers = buildPromptLayers({
|
||||
basePrompt: resolveAgentPrompt("triage", settings.agentPrompts)
|
||||
|| (isFast ? FAST_TRIAGE_SYSTEM_PROMPT : TRIAGE_SYSTEM_PROMPT),
|
||||
goalContext: triageGoalResolution.goalContext,
|
||||
agentInstructions: [
|
||||
triageIdentitySection,
|
||||
triageInstructions,
|
||||
@@ -1210,14 +1230,6 @@ export class TriageProcessor {
|
||||
defaultProvider: planningModel.provider,
|
||||
defaultModelId: planningModel.modelId,
|
||||
};
|
||||
const runAuditor = createRunAuditor(this.store, {
|
||||
runId: generateSyntheticRunId("triage", task.id),
|
||||
agentId: assignedAgent?.id ?? "triage",
|
||||
taskId: task.id,
|
||||
taskLineageId: task.lineageId,
|
||||
phase: "plan",
|
||||
source: "triage",
|
||||
});
|
||||
|
||||
let { session } = await createResolvedAgentSession({
|
||||
sessionPurpose: "triage",
|
||||
|
||||
Reference in New Issue
Block a user