Heartbeat no longer writes its own report; it triggers the shared incremental review and, per plan intervention, derives evidence-backed suggestions from the review's graph changes. Manual review is incremental by default with explicit reanalysis. The Supervisor tab is renamed Smart heartbeat with heartbeat plans, and the redundant inner plan frame is removed.
263 lines
11 KiB
TypeScript
263 lines
11 KiB
TypeScript
import { z } from 'zod'
|
|
import { heartbeatScopeSchema } from './assistant-contracts'
|
|
|
|
export const supervisionTriggerSchema = z.enum(['manual', 'heartbeat'])
|
|
export type SupervisionTrigger = z.infer<typeof supervisionTriggerSchema>
|
|
|
|
export const supervisionTimeRangeSchema = z
|
|
.object({
|
|
from: z.string().datetime({ offset: true }),
|
|
to: z.string().datetime({ offset: true })
|
|
})
|
|
.strict()
|
|
.refine((value) => Date.parse(value.from) <= Date.parse(value.to), {
|
|
message: '监督者时间范围无效'
|
|
})
|
|
|
|
export const supervisionRunRequestSchema = z
|
|
.object({
|
|
trigger: supervisionTriggerSchema,
|
|
scope: heartbeatScopeSchema,
|
|
timeRange: supervisionTimeRangeSchema,
|
|
// Reviews skip processed, unchanged sources by default. Only an explicit
|
|
// user re-analysis reads the whole interval again; it never moves progress.
|
|
reanalyze: z.boolean().optional()
|
|
})
|
|
.strict()
|
|
|
|
export type SupervisionRunRequest = z.infer<typeof supervisionRunRequestSchema>
|
|
|
|
/** Whether a review reads and advances the shared incremental progress. */
|
|
export function isIncrementalReview(request: Pick<SupervisionRunRequest, 'trigger' | 'reanalyze'>): boolean {
|
|
return request.trigger === 'heartbeat' || request.reanalyze !== true
|
|
}
|
|
|
|
export const supervisionSuggestionKindSchema = z.enum(['open_item', 'conflict', 'convention', 'revision'])
|
|
export type SupervisionSuggestionKind = z.infer<typeof supervisionSuggestionKindSchema>
|
|
export type SupervisionSuggestion = {
|
|
id: string
|
|
resultId: string | null
|
|
heartbeatRunId: string | null
|
|
scope: SupervisionRunRequest['scope']
|
|
kind: SupervisionSuggestionKind
|
|
title: string
|
|
detail: string
|
|
sourceIds: string[]
|
|
entityId: string | null
|
|
relationId: string | null
|
|
taskId: string | null
|
|
status: 'pending' | 'accepted' | 'dismissed'
|
|
createdAt: string
|
|
}
|
|
export const supervisionSuggestionListRequestSchema = z.object({
|
|
status: z.enum(['pending', 'all']).default('pending'),
|
|
limit: z.number().int().min(1).max(100).default(50),
|
|
offset: z.number().int().min(0).max(100_000).default(0)
|
|
}).strict()
|
|
export const supervisionSuggestionActionSchema = z.object({
|
|
id: z.string().uuid(),
|
|
action: z.enum(['accept', 'dismiss'])
|
|
}).strict()
|
|
export type SupervisionSuggestionAction = z.infer<typeof supervisionSuggestionActionSchema>
|
|
export const supervisionSuggestionRetrySchema = z.object({ heartbeatRunId: z.string().min(1).max(256) }).strict()
|
|
|
|
export const supervisionActivityRequestSchema = z.object({
|
|
configId: z.string().min(1).max(256).optional(),
|
|
limit: z.number().int().min(1).max(100).default(50),
|
|
offset: z.number().int().min(0).max(100_000).default(0)
|
|
}).strict()
|
|
export type SupervisionActivityRequest = z.input<typeof supervisionActivityRequestSchema>
|
|
export type SupervisionActivity = {
|
|
id: string
|
|
kind: 'supervision' | 'heartbeat'
|
|
trigger: 'manual' | 'scheduled' | 'heartbeat'
|
|
status: 'running' | 'completed' | 'failed' | 'skipped' | 'no_change' | 'paused' | 'cancelled'
|
|
reviewProgress?: import('./supervision-review-contracts').SupervisionReviewProgress
|
|
scope: SupervisionRunRequest['scope'] | null
|
|
startedAt: string
|
|
completedAt: string | null
|
|
timeRange: SupervisionRunRequest['timeRange'] | null
|
|
error: string | null
|
|
summary: string | null
|
|
resultId: string | null
|
|
heartbeatStatus: 'claimed' | 'completed' | 'failed' | 'skipped' | 'no_change' | null
|
|
supervisionStatus: 'running' | 'completed' | 'failed' | 'no_change' | 'paused' | 'cancelled' | null
|
|
/** Intervention after an automatic review; null when not applicable. */
|
|
suggestionStatus?: 'running' | 'completed' | 'failed' | 'skipped' | null
|
|
suggestionError?: string | null
|
|
suggestionCount?: number
|
|
}
|
|
|
|
export const supervisionEvidenceSchema = z
|
|
.object({
|
|
id: z.string().min(1).max(256),
|
|
sourceType: z.enum(['conversation', 'task', 'note', 'memory', 'knowledge']),
|
|
sourceId: z.string().min(1).max(256),
|
|
title: z.string().max(500),
|
|
content: z.string().max(8_000),
|
|
occurredAt: z.string().datetime({ offset: true }),
|
|
locator: z.record(z.string(), z.unknown()).optional()
|
|
})
|
|
.strict()
|
|
|
|
export type SupervisionEvidence = z.infer<typeof supervisionEvidenceSchema>
|
|
|
|
const sourceReferenceIdsSchema = z.array(z.string().min(1).max(256))
|
|
|
|
export const supervisionEventSchema = z
|
|
.object({
|
|
title: z.string().min(1),
|
|
description: z.string(),
|
|
occurredAt: z.string().datetime({ offset: true }),
|
|
eventType: z.enum(['decision', 'change', 'discussion', 'milestone']),
|
|
entityIds: z.array(z.string().regex(/^[a-zA-Z0-9_-]{1,120}$/)),
|
|
sourceReferenceIds: sourceReferenceIdsSchema
|
|
})
|
|
.strict()
|
|
|
|
export const supervisionEntitySchema = z
|
|
.object({
|
|
id: z.string().regex(/^[a-zA-Z0-9_-]{1,120}$/),
|
|
persistedId: z.string().uuid().optional(),
|
|
label: z.string().min(1),
|
|
description: z.string(),
|
|
sourceReferenceIds: sourceReferenceIdsSchema
|
|
})
|
|
.strict()
|
|
|
|
export const supervisionRelationSchema = z
|
|
.object({
|
|
fromEntityId: z.string().regex(/^[a-zA-Z0-9_-]{1,120}$/),
|
|
toEntityId: z.string().regex(/^[a-zA-Z0-9_-]{1,120}$/),
|
|
relationType: z.enum(['supports', 'depends-on', 'contrasts', 'related']),
|
|
reason: z.string(),
|
|
sourceReferenceIds: sourceReferenceIdsSchema
|
|
})
|
|
.strict()
|
|
|
|
export const supervisionEntityChangeSchema = z.object({
|
|
entityId: z.string().regex(/^[a-zA-Z0-9_-]{1,120}$/),
|
|
changeType: z.enum(['proposed', 'added', 'verified', 'revised']),
|
|
description: z.string(),
|
|
sourceReferenceIds: sourceReferenceIdsSchema
|
|
}).strict()
|
|
|
|
export const supervisionSummaryOutputSchema = z
|
|
.object({
|
|
summary: z.string().min(1),
|
|
changeDigest: z.string(),
|
|
openItems: z.array(z.string().min(1)),
|
|
events: z.array(supervisionEventSchema),
|
|
entities: z.array(supervisionEntitySchema),
|
|
entityChanges: z.array(supervisionEntityChangeSchema),
|
|
relations: z.array(supervisionRelationSchema)
|
|
})
|
|
.strict()
|
|
|
|
export type SupervisionSummaryOutput = z.infer<
|
|
typeof supervisionSummaryOutputSchema
|
|
>
|
|
|
|
// Navigation combines saved summaries; it cannot create or replace leaf facts.
|
|
export const supervisionNavigationOutputSchema = supervisionSummaryOutputSchema.pick({
|
|
summary: true, changeDigest: true, openItems: true
|
|
}).extend({
|
|
events: z.array(z.never()).max(0).optional(),
|
|
entities: z.array(z.never()).max(0).optional(),
|
|
entityChanges: z.array(z.never()).max(0).optional(),
|
|
relations: z.array(z.never()).max(0).optional()
|
|
}).strict()
|
|
|
|
export const supervisionEntityActionSchema = z
|
|
.object({
|
|
resultId: z.string().min(1).optional(),
|
|
entityId: z.string().regex(/^[a-zA-Z0-9_-]{1,120}$/),
|
|
action: z.enum(['confirm', 'revise', 'revoke']),
|
|
label: z.string().trim().min(1).optional(),
|
|
description: z.string().optional()
|
|
})
|
|
.strict()
|
|
|
|
export const supervisionRelationActionSchema = z
|
|
.object({
|
|
resultId: z.string().min(1).optional(),
|
|
relationId: z.string().uuid(),
|
|
action: z.enum(['confirm', 'revoke'])
|
|
})
|
|
.strict()
|
|
|
|
export const supervisionSourceRequestSchema = z
|
|
.object({ sourceId: z.string().min(1).max(256) })
|
|
.strict()
|
|
|
|
export const supervisionContinueContextRequestSchema = z.object({
|
|
sourceId: z.string().min(1).max(256),
|
|
resultId: z.string().min(1).max(256)
|
|
}).strict()
|
|
export type SupervisionContinueContextRequest = z.infer<typeof supervisionContinueContextRequestSchema>
|
|
|
|
export const supervisionContinueRequestSchema = z.object({
|
|
conversationId: z.string().min(1).max(128),
|
|
prompt: z.string().trim().min(1).max(12_000),
|
|
runtimeSelection: z.unknown().optional(),
|
|
projectId: z.string().min(1).max(128).optional()
|
|
}).strict()
|
|
export type SupervisionContinueRequest = z.infer<typeof supervisionContinueRequestSchema>
|
|
|
|
export const supervisionKnowledgePreviewRequestSchema = z.object({
|
|
operation: z.enum(['create-entity', 'update-entity']),
|
|
libraryId: z.string().uuid(),
|
|
entityId: z.string().uuid().optional(),
|
|
label: z.string().trim().min(1).max(120),
|
|
type: z.string().trim().min(1).max(120),
|
|
description: z.string().trim().max(2_000),
|
|
aliases: z.array(z.string().trim().min(1).max(120)).max(50),
|
|
sourceId: z.string().min(1).max(256)
|
|
}).strict().superRefine((value, context) => {
|
|
if (value.operation === 'update-entity' && !value.entityId) {
|
|
context.addIssue({ code: 'custom', path: ['entityId'], message: 'update-entity requires entityId' })
|
|
}
|
|
})
|
|
export type SupervisionKnowledgePreviewRequest = z.infer<typeof supervisionKnowledgePreviewRequestSchema>
|
|
|
|
export const supervisionKnowledgeCommitRequestSchema = z.object({
|
|
previewId: z.string().uuid()
|
|
}).strict()
|
|
export type SupervisionKnowledgeCommitRequest = z.infer<typeof supervisionKnowledgeCommitRequestSchema>
|
|
|
|
export type SupervisionEntityAction = z.infer<typeof supervisionEntityActionSchema>
|
|
export type SupervisionRelationAction = z.infer<typeof supervisionRelationActionSchema>
|
|
|
|
export const supervisionResultViewSchema = z.object({
|
|
runId: z.string().optional(),
|
|
coverage: z.record(z.string(), z.unknown()).optional(),
|
|
storyLineId: z.string(),
|
|
sourceId: z.string().nullable(),
|
|
id: z.string(), summary: z.string(), changeDigest: z.string(), createdAt: z.string(),
|
|
scope: heartbeatScopeSchema, timeRange: supervisionTimeRangeSchema,
|
|
openItems: z.array(z.string())
|
|
})
|
|
export type SupervisionResultView = z.infer<typeof supervisionResultViewSchema>
|
|
|
|
export const supervisionTargetSchema = z.discriminatedUnion('type', [
|
|
z.object({ type: z.literal('conversation'), conversationId: z.string().min(1).max(128) }).strict(),
|
|
z.object({ type: z.literal('task'), taskId: z.string().min(1).max(128) }).strict()
|
|
])
|
|
export type SupervisionTarget = z.infer<typeof supervisionTargetSchema>
|
|
export const supervisionOverviewRequestSchema = z.object({ target: supervisionTargetSchema.optional(), resultId: z.string().min(1).max(256).optional() }).strict()
|
|
export const supervisionGraphRequestSchema = z.object({
|
|
resultId: z.string().min(1).optional(), storyLineId: z.string().min(1).optional()
|
|
}).strict()
|
|
export type SupervisionGraphRequest = z.infer<typeof supervisionGraphRequestSchema>
|
|
|
|
export const supervisionGraphViewSchema = z.object({
|
|
storyLine: z.object({ id: z.string(), scope_json: z.string() }).nullable(),
|
|
events: z.array(z.object({ id: z.string(), title: z.string(), description: z.string(), occurred_at: z.string() })),
|
|
entities: z.array(z.object({ id: z.string(), canonical_label: z.string(), description: z.string(), confirmation_state: z.string() })),
|
|
relations: z.array(z.object({ id: z.string(), from_entity_id: z.string(), to_entity_id: z.string(), relation_type: z.string(), reason: z.string(), confirmation_state: z.string() })),
|
|
sources: z.array(z.object({ id: z.string(), title: z.string(), occurred_at: z.string() })),
|
|
eventEntities: z.array(z.object({ event_id: z.string(), entity_id: z.string() })),
|
|
eventSources: z.array(z.object({ event_id: z.string(), source_id: z.string() }))
|
|
})
|
|
export type SupervisionGraphView = z.infer<typeof supervisionGraphViewSchema>
|