import { Applog } from '../applog/datom-types.ts' import { query } from './basic.ts' import { QueryNode } from './types.ts' import { Logger } from 'besonders-logger' const { WARN, LOG, DEBUG, VERBOSE, ERROR } = Logger.setup(Logger.INFO) // eslint-disable-line no-unused-vars /** * Situations are meant to flag and possibly autocorrect or autosuggest solutions * * Types of Situations: * Divergences = leaf nodes that are based on different previous versions/contexts * Conflicts/Disagreement = different agents setting the same attribute (based on same previous) * Overwrite = one agent's action implicitly overrides another's without explicit confirmation * Conscious Disagreement = agents explicitly acknowledge and accept conflicting changes * Suggestions = recommendations for improvements or alternatives */ interface Situation { name: string desc: string intensity: number type: typeof SituationTypes[keyof typeof SituationTypes] query: typeof query // wovin can export common examples resolutionOptions: Resolution[] metadata?: { agentId?: string timestamp?: number context?: Record } } interface QueryResultWithSituations { result: any situations: Situation[] threadView?: ThreadView } interface ThreadView { id: string situations: SituatedMessage[] resolutionState: ResolutionState } interface SituatedMessage { id: string situation: Situation message: string resolutions: ResolutionAction[] timestamp: number agentId?: string } interface ResolutionState { pending: string[] // situation IDs resolved: string[] // situation IDs ignored: string[] // situation IDs } interface ResolutionAction { name: string applied: boolean appliedAt?: number appliedBy?: string } const SituationTypes = { divergence: 'divergence', disagreement: 'disagreement', overwrite: 'overwrite', consciousDisagreement: 'consciousDisagreement', suggestion: 'suggestion', } as const interface Resolution { name: string desc: string assertion: () => Applog[] } const ContentDivergence = { name: 'ContentDivergence', desc: 'block/content changes from different agents based on different pv logs', intensity: 10, type: SituationTypes.divergence, query: QueryNode, resolutionOptions: [], } const AttributeOverwrite = { name: 'AttributeOverwrite', desc: 'one agent implicitly overwrites another agent\'s attribute change without confirmation', intensity: 8, type: SituationTypes.overwrite, query: QueryNode, resolutionOptions: [ { name: 'restore', desc: 'restore the previous value', assertion: () => [] // TODO: implement restoration logic }, { name: 'confirm', desc: 'confirm the overwrite as intentional', assertion: () => [] // TODO: implement confirmation logic } ], } const ConsciousDisagreement = { name: 'ConsciousDisagreement', desc: 'agents explicitly acknowledge and accept conflicting changes to the same attribute', intensity: 5, type: SituationTypes.consciousDisagreement, query: QueryNode, resolutionOptions: [ { name: 'merge', desc: 'create a merged version that incorporates both perspectives', assertion: () => [] // TODO: implement merge logic }, { name: 'branch', desc: 'create separate branches for each perspective', assertion: () => [] // TODO: implement branching logic } ], } const ContentSuggestion = { name: 'ContentSuggestion', desc: 'AI or agent suggests improvements to existing content', intensity: 3, type: SituationTypes.suggestion, query: QueryNode, resolutionOptions: [ { name: 'accept', desc: 'accept the suggestion and apply changes', assertion: () => [] // TODO: implement acceptance logic }, { name: 'reject', desc: 'reject the suggestion', assertion: () => [] // TODO: implement rejection logic }, { name: 'modify', desc: 'accept with modifications', assertion: () => [] // TODO: implement modification logic } ], } /** * Creates a thread view for situations detected in query results */ export function createThreadForSituations( result: any, situations: Situation[], options: { threadId?: string groupByType?: boolean sortByIntensity?: boolean } = {} ): QueryResultWithSituations { const threadId = options.threadId || `thread_${Date.now()}_${Math.random().toString(36).substr(2, 9)}` // Sort situations if requested let sortedSituations = [...situations] if (options.sortByIntensity) { sortedSituations.sort((a, b) => b.intensity - a.intensity) } // Group by type if requested const threadMessages: SituatedMessage[] = sortedSituations.map((situation, index) => ({ id: `${threadId}_msg_${index}`, situation, message: formatSituationMessage(situation), resolutions: situation.resolutionOptions.map(option => ({ name: option.name, applied: false, })), timestamp: Date.now(), agentId: situation.metadata?.agentId, })) const threadView: ThreadView = { id: threadId, situations: threadMessages, resolutionState: { pending: threadMessages.map(msg => msg.id), resolved: [], ignored: [], }, } return { result, situations, threadView, } } /** * Formats a situation into a human-readable message for the thread */ function formatSituationMessage(situation: Situation): string { const typeEmoji = { [SituationTypes.divergence]: '🔀', [SituationTypes.disagreement]: '⚠️', [SituationTypes.overwrite]: '🔄', [SituationTypes.consciousDisagreement]: '💭', [SituationTypes.suggestion]: '💡', } const emoji = typeEmoji[situation.type] || '❓' return `${emoji} **${situation.name}**: ${situation.desc} (intensity: ${situation.intensity})` } /** * Applies a resolution action to a situation in a thread */ export function applyResolution( threadView: ThreadView, messageId: string, resolutionName: string, appliedBy: string ): ThreadView { const updatedMessages = threadView.situations.map(msg => { if (msg.id === messageId) { const updatedResolutions = msg.resolutions.map(res => res.name === resolutionName ? { ...res, applied: true, appliedAt: Date.now(), appliedBy } : res ) return { ...msg, resolutions: updatedResolutions, } } return msg }) const message = updatedMessages.find(msg => msg.id === messageId) const hasAppliedResolution = message?.resolutions.some(res => res.applied) const resolutionState = { ...threadView.resolutionState, pending: hasAppliedResolution ? threadView.resolutionState.pending.filter(id => id !== messageId) : threadView.resolutionState.pending, resolved: hasAppliedResolution ? [...threadView.resolutionState.resolved, messageId] : threadView.resolutionState.resolved, } return { ...threadView, situations: updatedMessages, resolutionState, } }