feat: RMM Overshell, IT Glue audit/write-back, LogLift, link-aware bundles, dashboard overhaul
- RMM Overshell (migration 077): admin page, dispatch UI, executor/worker, target resolver, script registry (AD/DHCP/DNS/event-log/services/software/network/loglift) - LogLift evidence pipeline (migration 078): upload webhook, B2 storage client, receiver/matcher, EventLogCollector PowerShell script - IT Glue audit + write-back (migrations 075, 076): asset-audit runner, ticket xrefs, applications/configurations browse pages + apply/revert/audit endpoints - Link-aware analyzer bundles (migration 073) + provider toggle (migration 074): link-discovery service, OpenRouter LLM provider, related-tickets/itglue-suggestion panels, analyze-bundle endpoint - Endpoint data model + device-link reconciliation (migrations 079, 080): conflicts admin page, reconciler service, resolve endpoints - Dashboard overhaul: integration-health service + alerts, overview/health endpoints - Permissions: add itglue + rmm scopes; middleware: public /api/rmm/loglift route Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
378e68ad8a
commit
1112a06afe
132 changed files with 21352 additions and 743 deletions
|
|
@ -213,6 +213,9 @@ export const PersistedAnalysis = z.object({
|
|||
|
||||
filteredNoiseCount: z.number().int().nonnegative(),
|
||||
errorMessage: z.string().nullable(),
|
||||
|
||||
/** Phase 3: which LLM provider produced this analysis. */
|
||||
provider: z.enum(['anthropic', 'openrouter']).default('anthropic'),
|
||||
});
|
||||
export type PersistedAnalysis = z.infer<typeof PersistedAnalysis>;
|
||||
|
||||
|
|
@ -440,6 +443,7 @@ export type AggregateReduceResponse = z.infer<typeof AggregateReduceResponse>;
|
|||
|
||||
export const AggregateReportStatus = z.enum([
|
||||
'pending',
|
||||
'pending_analyses',
|
||||
'running',
|
||||
'complete',
|
||||
'failed',
|
||||
|
|
@ -450,8 +454,12 @@ export type AggregateReportStatus = z.infer<typeof AggregateReportStatus>;
|
|||
// API request bodies.
|
||||
// =============================================================================
|
||||
|
||||
export const ProviderEnum = z.enum(['anthropic', 'openrouter']);
|
||||
export type ProviderEnum = z.infer<typeof ProviderEnum>;
|
||||
|
||||
export const AnalyzeTicketRequest = z.object({
|
||||
force: z.boolean().optional().default(false),
|
||||
provider: ProviderEnum.optional().default('anthropic'),
|
||||
});
|
||||
export type AnalyzeTicketRequest = z.infer<typeof AnalyzeTicketRequest>;
|
||||
|
||||
|
|
@ -461,6 +469,112 @@ export const ShareAnalysisRequest = z.object({
|
|||
});
|
||||
export type ShareAnalysisRequest = z.infer<typeof ShareAnalysisRequest>;
|
||||
|
||||
// =============================================================================
|
||||
// Phase 3 — link discovery & bundle analysis.
|
||||
// =============================================================================
|
||||
|
||||
export const LinkConfidence = z.enum(['high', 'medium']);
|
||||
export type LinkConfidence = z.infer<typeof LinkConfidence>;
|
||||
|
||||
export const LinkSource = z.enum([
|
||||
'related_tickets_section',
|
||||
'description_mention',
|
||||
'note_mention',
|
||||
'problem_ticket_id',
|
||||
'llm_suggested',
|
||||
]);
|
||||
export type LinkSource = z.infer<typeof LinkSource>;
|
||||
|
||||
export const TicketRef = z.object({
|
||||
ticket_number: z.string(),
|
||||
title: z.string().nullable(),
|
||||
status_label: z.string().nullable(),
|
||||
last_activity_date: z.string().datetime({ offset: true }).nullable(),
|
||||
source: LinkSource,
|
||||
confidence: LinkConfidence,
|
||||
reason: z.string().nullable(),
|
||||
});
|
||||
export type TicketRef = z.infer<typeof TicketRef>;
|
||||
|
||||
export const DiscoveredLinks = z.object({
|
||||
explicit: z.array(TicketRef),
|
||||
suggested: z.array(TicketRef),
|
||||
isProblemTicket: z.boolean(),
|
||||
problemTicketSignals: z.array(z.string()),
|
||||
});
|
||||
export type DiscoveredLinks = z.infer<typeof DiscoveredLinks>;
|
||||
|
||||
export const BundleAnalyzeRequest = z.object({
|
||||
linkedTicketNumbers: z.array(z.string()).max(50),
|
||||
includeItglueContext: z.boolean().optional().default(true),
|
||||
reportTitle: z.string().max(200).nullable().optional(),
|
||||
confirmedCost: z.boolean().optional().default(false),
|
||||
provider: ProviderEnum.optional().default('anthropic'),
|
||||
});
|
||||
export type BundleAnalyzeRequest = z.infer<typeof BundleAnalyzeRequest>;
|
||||
|
||||
export const SuggestLinksRequest = z.object({
|
||||
includeSuggested: z.boolean().optional().default(true),
|
||||
});
|
||||
export type SuggestLinksRequest = z.infer<typeof SuggestLinksRequest>;
|
||||
|
||||
// =============================================================================
|
||||
// Phase 4 — IT Glue asset audit (LLM-driven gap analysis vs ticket history).
|
||||
// =============================================================================
|
||||
|
||||
export const AuditConfidence = z.enum(['high', 'medium', 'low']);
|
||||
export type AuditConfidence = z.infer<typeof AuditConfidence>;
|
||||
|
||||
export const AssetAuditFieldGap = z.object({
|
||||
field_name: z.string(),
|
||||
why_missing_matters: z.string(),
|
||||
suggested_value: z.string().nullable(),
|
||||
evidence_ticket_numbers: z.array(z.string()),
|
||||
confidence: AuditConfidence,
|
||||
});
|
||||
export type AssetAuditFieldGap = z.infer<typeof AssetAuditFieldGap>;
|
||||
|
||||
export const AssetAuditNotePromotion = z.object({
|
||||
quoted_note_text: z.string(),
|
||||
target_field: z.string(),
|
||||
suggested_value: z.string(),
|
||||
confidence: AuditConfidence,
|
||||
});
|
||||
export type AssetAuditNotePromotion = z.infer<typeof AssetAuditNotePromotion>;
|
||||
|
||||
export const AssetAuditContradiction = z.object({
|
||||
description: z.string(),
|
||||
evidence: z.string(),
|
||||
});
|
||||
export type AssetAuditContradiction = z.infer<typeof AssetAuditContradiction>;
|
||||
|
||||
export const AssetAuditResponse = z.object({
|
||||
field_gaps: z.array(AssetAuditFieldGap),
|
||||
notes_promotions: z.array(AssetAuditNotePromotion),
|
||||
contradictions: z.array(AssetAuditContradiction),
|
||||
overall_score: z.number().min(0).max(1),
|
||||
});
|
||||
export type AssetAuditResponse = z.infer<typeof AssetAuditResponse>;
|
||||
|
||||
export const RunAssetAuditRequest = z.object({
|
||||
provider: ProviderEnum.optional().default('anthropic'),
|
||||
});
|
||||
export type RunAssetAuditRequest = z.infer<typeof RunAssetAuditRequest>;
|
||||
|
||||
export const ApplyAssetSuggestionRequest = z.object({
|
||||
auditId: z.string().uuid(),
|
||||
fieldName: z.string(),
|
||||
suggestedValue: z.unknown(),
|
||||
/** ticket_numbers + gap description for the source_evidence row. */
|
||||
sourceEvidence: z
|
||||
.object({
|
||||
ticket_numbers: z.array(z.string()).optional(),
|
||||
gap_description: z.string().optional(),
|
||||
})
|
||||
.optional(),
|
||||
});
|
||||
export type ApplyAssetSuggestionRequest = z.infer<typeof ApplyAssetSuggestionRequest>;
|
||||
|
||||
// =============================================================================
|
||||
// Internal pipeline payload — passed between stages, NOT a wire format.
|
||||
// =============================================================================
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue