feat: shared supervisor timeline, story assignment and time-spiral view
Each source version is extracted once for every review scope; events carry spans and projects from their sources, and attention density is counted from messages. After publication, events are assigned to project features and sub-threads (cross-project stories only when enabled), with user rename, merge, remove, move and undo. The story graph gains a lazily loaded Three.js time-spiral view. Includes the 3D concept demo, real-data DeepSeek check scripts and docs.
This commit is contained in:
@@ -2225,6 +2225,9 @@ export type DesktopApi = {
|
||||
suggestions: (input?: { status?: 'pending' | 'all'; limit?: number; offset?: number }) => Promise<import('./supervision-contracts').SupervisionSuggestion[]>
|
||||
suggestionAction: (input: import('./supervision-contracts').SupervisionSuggestionAction) => Promise<import('./supervision-contracts').SupervisionSuggestion>
|
||||
retrySuggestions: (input: { heartbeatRunId: string }) => Promise<number>
|
||||
stories: (input: import('./supervision-story-contracts').SupervisionStoryListRequest) => Promise<import('./supervision-story-contracts').SupervisionStoryView>
|
||||
storyAction: (input: import('./supervision-story-contracts').SupervisionStoryAction) => Promise<void>
|
||||
retryStories: (input: { runId: string }) => Promise<import('./supervision-review-contracts').SupervisionReviewProgress>
|
||||
}
|
||||
experts: {
|
||||
list: () => Promise<AssistantExpert[]>
|
||||
|
||||
@@ -295,6 +295,9 @@ export const ipcChannels = {
|
||||
supervisionContinue: 'supervision:continue',
|
||||
supervisionKnowledgePreview: 'supervision:knowledge-preview',
|
||||
supervisionKnowledgeCommit: 'supervision:knowledge-commit',
|
||||
supervisionStories: 'supervision:stories',
|
||||
supervisionStoryAction: 'supervision:story-action',
|
||||
supervisionRetryStories: 'supervision:retry-stories',
|
||||
expertsList: 'experts:list',
|
||||
expertsCreate: 'experts:create',
|
||||
expertsUpdate: 'experts:update',
|
||||
|
||||
@@ -252,11 +252,14 @@ export type SupervisionGraphRequest = z.infer<typeof supervisionGraphRequestSche
|
||||
|
||||
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() })),
|
||||
events: z.array(z.object({ id: z.string(), title: z.string(), description: z.string(), occurred_at: z.string(),
|
||||
project_id: z.string().nullish(), started_at: z.string().nullish(), ended_at: z.string().nullish() })),
|
||||
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() }))
|
||||
eventSources: z.array(z.object({ event_id: z.string(), source_id: z.string() })),
|
||||
attention: z.array(z.object({ start: z.string(), turns: z.number(), characters: z.number() })).optional()
|
||||
})
|
||||
export type SupervisionAttentionSlot = { start: string; turns: number; characters: number }
|
||||
export type SupervisionGraphView = z.infer<typeof supervisionGraphViewSchema>
|
||||
|
||||
@@ -6,8 +6,13 @@ export const supervisionReviewSettingsSchema = z.object({
|
||||
batchCharacters: z.number().int().min(1000).max(16000).default(8000),
|
||||
batchMessages: z.number().int().min(1).max(50).default(20),
|
||||
executionSeconds: z.number().int().min(30).max(3600).default(300),
|
||||
responseKiB: z.number().int().min(100).max(16384).optional()
|
||||
responseKiB: z.number().int().min(100).max(16384).optional(),
|
||||
// Off: an event can only join knowledge already seen in the same project.
|
||||
crossProject: z.boolean().optional(),
|
||||
// A feature splits into sub-threads only once it holds at least this many events.
|
||||
storyThreadEvents: z.number().int().min(4).max(500).optional()
|
||||
}).strict()
|
||||
export const defaultStoryThreadEvents = 20
|
||||
export type SupervisionReviewSettings = z.infer<typeof supervisionReviewSettingsSchema>
|
||||
export const defaultSupervisionReviewSettings = supervisionReviewSettingsSchema.parse({})
|
||||
export const supervisionReviewIdSchema = z.object({ runId: z.string().uuid() }).strict()
|
||||
@@ -31,6 +36,8 @@ export type SupervisionReviewProgress = {
|
||||
inFlight?: number
|
||||
phase?: 'collecting' | 'extracting' | 'summarizing' | 'saving'
|
||||
navigationNodes?: number
|
||||
/** Story assignment after publication. A failure leaves the published review intact. */
|
||||
stories?: { status: 'running' | 'completed' | 'failed'; error?: string; calls?: number; assigned?: number; unassigned?: number; created?: number }
|
||||
settings?: SupervisionReviewSettings & { timeoutSeconds: number; concurrency: number }
|
||||
}
|
||||
export type SupervisionReviewBatch = {
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
import { z } from 'zod'
|
||||
import { heartbeatScopeSchema } from './assistant-contracts'
|
||||
|
||||
export type SupervisionStory = {
|
||||
id: string
|
||||
projectId: string | null
|
||||
projectName: string | null
|
||||
parentId: string | null
|
||||
level: 'feature' | 'thread' | 'cross'
|
||||
name: string
|
||||
description: string
|
||||
/** `concluded` only when an event states it; quiet stories stay active. */
|
||||
state: 'active' | 'concluded'
|
||||
stateEventId: string | null
|
||||
userEdited: boolean
|
||||
/** primary events make up the story's length and density; others are associated links (cross-project). */
|
||||
events: SupervisionStoryEvent[]
|
||||
startedAt: string | null
|
||||
endedAt: string | null
|
||||
}
|
||||
|
||||
export type SupervisionStoryEvent = { id: string; title: string; projectId: string | null; startedAt: string; endedAt: string; primary: boolean; userSet: boolean }
|
||||
|
||||
export type SupervisionStoryView = { stories: SupervisionStory[]; unassigned: number; canUndo: boolean }
|
||||
|
||||
export const supervisionStoryListSchema = z.object({ scope: heartbeatScopeSchema }).strict()
|
||||
export type SupervisionStoryListRequest = z.infer<typeof supervisionStoryListSchema>
|
||||
|
||||
export const supervisionStoryActionSchema = z.discriminatedUnion('action', [
|
||||
z.object({ action: z.literal('rename'), storyId: z.string().uuid(), name: z.string().trim().min(1).max(60), description: z.string().trim().max(400).optional() }).strict(),
|
||||
z.object({ action: z.literal('merge'), storyId: z.string().uuid(), intoId: z.string().uuid() }).strict(),
|
||||
z.object({ action: z.literal('remove'), storyId: z.string().uuid() }).strict(),
|
||||
z.object({ action: z.literal('move'), eventId: z.string().min(1).max(256), storyId: z.string().uuid().nullable() }).strict(),
|
||||
z.object({ action: z.literal('undo') }).strict()
|
||||
])
|
||||
export type SupervisionStoryAction = z.infer<typeof supervisionStoryActionSchema>
|
||||
Reference in New Issue
Block a user