FN-5758: add cited-goal audit aggregation and runtime route

Add end-to-end goal-citation audit trail plumbing across core, engine, CLI, and dashboard.

- extend goal citation extraction with aggregation logic and new core coverage
- expose cited-goal runtime data through dashboard routes with dedicated API tests
- wire goal-citation audit details into engine diagnostics and CLI extension audit expectations
- document the new audit behavior and publish a patch changeset for @runfusion/fusion

Files changed:
 .changeset/fn-5758-goal-citation-audit.md          |  9 +++
 docs/dashboard-guide.md                            |  2 +
 docs/diagnostics.md                                |  4 ++
 .../__tests__/extension-goal-tools-audit.test.ts   |  6 +-
 packages/cli/src/extension.ts                      |  3 +
 .../goal-citation-audit-aggregation.test.ts        | 63 ++++++++++++++++++
 packages/core/src/goal-citation-extractor.ts       | 54 ++++++++++++++-
 packages/core/src/index.ts                         |  1 +
 .../src/__tests__/routes-run-cited-goals.test.ts   | 77 ++++++++++++++++++++++
 packages/dashboard/src/routes.ts                   | 11 ++++
 .../src/routes/register-agent-runtime-routes.ts    | 49 ++++++++++++++
 .../src/__tests__/goal-anchoring-audit.test.ts     | 40 +++++++----
 packages/engine/src/goal-anchoring-audit.ts        |  4 ++
 packages/engine/src/goal-injection-diagnostics.ts  |  1 +
 14 files changed, 308 insertions(+), 16 deletions(-)

Fusion-Task-Id: FN-5758

Fusion-Task-Lineage: b077c0b7-6ca4-489d-8c36-73d80a2afdb2
This commit is contained in:
gsxdsm
2026-05-30 22:53:24 -07:00
parent d98ff8780c
commit 194dfa9387
14 changed files with 308 additions and 16 deletions

View File

@@ -4,6 +4,7 @@ import type { ApiRoutesContext } from "./types.js";
type NormalizedAuditEvent = import("../routes.js").NormalizedRunAuditEvent;
type TimelineEntry = import("../routes.js").TimelineEntry;
type RunAuditResponse = import("../routes.js").RunAuditResponse;
type RunCitedGoalsResponse = import("../routes.js").RunCitedGoalsResponse;
type RunTimelineResponse = import("../routes.js").RunTimelineResponse;
interface AgentRuntimeRouteDeps {
@@ -1519,6 +1520,54 @@ export function registerAgentRuntimeRoutes(ctx: ApiRoutesContext, deps: AgentRun
}
});
/**
* GET /api/agents/:id/runs/:runId/cited-goals
* Get cited goal IDs aggregated from goal-related run-audit events for a specific run.
*/
router.get("/agents/:id/runs/:runId/cited-goals", async (req, res) => {
try {
const { store: scopedStore } = await getProjectContext(req);
const { AgentStore, collectCitedGoalIdsFromAudit } = await import("@fusion/core");
const agentStore = new AgentStore({ rootDir: scopedStore.getFusionDir() });
await agentStore.init();
const runId = req.params.runId;
if (!runId || runId.trim().length === 0) {
throw badRequest("runId is required");
}
const run = await agentStore.getRunDetail(req.params.id, req.params.runId);
if (!run) {
throw notFound("Run not found");
}
const goalEvents = scopedStore.getRunAuditEvents({
runId: req.params.runId,
domain: "database",
});
const { injectedGoalIds, retrievedGoalIds, citedGoalIds } = collectCitedGoalIdsFromAudit(goalEvents);
const taskId = run.contextSnapshot?.taskId as string | undefined;
const response: RunCitedGoalsResponse = {
runId: req.params.runId,
taskId,
injectedGoalIds,
retrievedGoalIds,
citedGoalIds,
};
res.json(response);
} catch (err: unknown) {
if (err instanceof ApiError) {
throw err;
}
if ((err instanceof Error ? err.message : String(err)).includes("not found")) {
throw notFound(err instanceof Error ? err.message : String(err));
} else {
rethrowAsApiError(err);
}
}
});
/**
* GET /api/agents/:id/runs/:runId/timeline
* Get a correlated timeline combining run-audit events and agent logs for a specific run.