242 lines
8.3 KiB
TypeScript
242 lines
8.3 KiB
TypeScript
import { invoke } from '@tauri-apps/api/core'
|
|
import { isTauri, mockInvoke } from '../mock-tauri'
|
|
|
|
export interface PersonaRuntimeAttribution {
|
|
humanResponsibilitySubject: string
|
|
personaCognitiveAuthor: string
|
|
executionRuntime: string
|
|
developmentId: string
|
|
authorizationScope: string
|
|
sourceLanguageAnchor: string
|
|
}
|
|
|
|
export interface PersonaRuntimeCognitiveGravityEvidence {
|
|
schema: 'hololake.persona-cognitive-gravity-evidence/v1'
|
|
subjectPersonaId: string
|
|
sourcePath: string
|
|
sourceHash: string
|
|
frameSchema: 'guanghu.zhuyuan-cognitive-gravity-frame/v1'
|
|
}
|
|
|
|
export interface PersonaRuntimeSessionReceipt {
|
|
sessionId: string
|
|
state: string
|
|
gitHead: string
|
|
nodeId: string
|
|
modelInstanceId: string
|
|
activeOrgan: string | null
|
|
eventCount: number
|
|
firstEventAt: string
|
|
lastEventAt: string
|
|
lastEvent: string
|
|
eventChainHead: string
|
|
cognitiveGravity: PersonaRuntimeCognitiveGravityEvidence
|
|
attribution: PersonaRuntimeAttribution
|
|
}
|
|
|
|
export interface PersonaRuntimeQueryReceipt {
|
|
schema: 'hololake.pncc-runtime-query/v1'
|
|
personaId: string
|
|
repositoryPath: string
|
|
observedGitHead: string
|
|
repositoryClean: boolean
|
|
matchingSessionCount: number
|
|
returnedSessionCount: number
|
|
sessions: PersonaRuntimeSessionReceipt[]
|
|
}
|
|
|
|
export interface PersonaRuntimeSessionProjection extends PersonaRuntimeSessionReceipt {
|
|
repositoryPath: string
|
|
observedGitHead: string
|
|
repositoryClean: boolean
|
|
}
|
|
|
|
export interface PersonaRuntimeProjectionError {
|
|
code: string
|
|
repositoryPath: string
|
|
}
|
|
|
|
export interface PersonaRuntimeProjection {
|
|
errors: PersonaRuntimeProjectionError[]
|
|
inspectedRepositoryCount: number
|
|
phase: 'unavailable' | 'verified' | 'error'
|
|
sessions: PersonaRuntimeSessionProjection[]
|
|
}
|
|
|
|
interface LoadPersonaRuntimeProjectionInput {
|
|
personaId: string
|
|
repositoryPaths: readonly string[]
|
|
query?: (personaId: string, repositoryPath: string) => Promise<unknown>
|
|
}
|
|
|
|
function errorCode(error: unknown): string {
|
|
if (typeof error === 'string') return error
|
|
if (error instanceof Error) return error.message
|
|
return 'PERSONA_RUNTIME_QUERY_FAILED'
|
|
}
|
|
|
|
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
return typeof value === 'object' && value !== null
|
|
}
|
|
|
|
function requiredString(record: Record<string, unknown>, key: string): string {
|
|
const value = Reflect.get(record, key)
|
|
if (typeof value !== 'string' || value.length === 0) throw new Error('PERSONA_RUNTIME_RECEIPT_INVALID')
|
|
return value
|
|
}
|
|
|
|
function requiredNumber(record: Record<string, unknown>, key: string): number {
|
|
const value = Reflect.get(record, key)
|
|
if (typeof value !== 'number' || !Number.isSafeInteger(value) || value < 0) {
|
|
throw new Error('PERSONA_RUNTIME_RECEIPT_INVALID')
|
|
}
|
|
return value
|
|
}
|
|
|
|
function parseAttribution(value: unknown): PersonaRuntimeAttribution {
|
|
if (!isRecord(value)) throw new Error('PERSONA_RUNTIME_RECEIPT_INVALID')
|
|
return {
|
|
humanResponsibilitySubject: requiredString(value, 'humanResponsibilitySubject'),
|
|
personaCognitiveAuthor: requiredString(value, 'personaCognitiveAuthor'),
|
|
executionRuntime: requiredString(value, 'executionRuntime'),
|
|
developmentId: requiredString(value, 'developmentId'),
|
|
authorizationScope: requiredString(value, 'authorizationScope'),
|
|
sourceLanguageAnchor: requiredString(value, 'sourceLanguageAnchor'),
|
|
}
|
|
}
|
|
|
|
function parseCognitiveGravity(value: unknown): PersonaRuntimeCognitiveGravityEvidence {
|
|
if (!isRecord(value)) throw new Error('PERSONA_RUNTIME_RECEIPT_INVALID')
|
|
const schema = requiredString(value, 'schema')
|
|
const frameSchema = requiredString(value, 'frameSchema')
|
|
const sourceHash = requiredString(value, 'sourceHash')
|
|
if (
|
|
schema !== 'hololake.persona-cognitive-gravity-evidence/v1'
|
|
|| frameSchema !== 'guanghu.zhuyuan-cognitive-gravity-frame/v1'
|
|
|| !/^[a-f0-9]{64}$/.test(sourceHash)
|
|
) {
|
|
throw new Error('PERSONA_RUNTIME_RECEIPT_INVALID')
|
|
}
|
|
return {
|
|
schema,
|
|
subjectPersonaId: requiredString(value, 'subjectPersonaId'),
|
|
sourcePath: requiredString(value, 'sourcePath'),
|
|
sourceHash,
|
|
frameSchema,
|
|
}
|
|
}
|
|
|
|
function parseSession(value: unknown): PersonaRuntimeSessionReceipt {
|
|
if (!isRecord(value)) throw new Error('PERSONA_RUNTIME_RECEIPT_INVALID')
|
|
const activeOrgan = Reflect.get(value, 'activeOrgan')
|
|
if (activeOrgan !== null && typeof activeOrgan !== 'string') {
|
|
throw new Error('PERSONA_RUNTIME_RECEIPT_INVALID')
|
|
}
|
|
return {
|
|
sessionId: requiredString(value, 'sessionId'),
|
|
state: requiredString(value, 'state'),
|
|
gitHead: requiredString(value, 'gitHead'),
|
|
nodeId: requiredString(value, 'nodeId'),
|
|
modelInstanceId: requiredString(value, 'modelInstanceId'),
|
|
activeOrgan,
|
|
eventCount: requiredNumber(value, 'eventCount'),
|
|
firstEventAt: requiredString(value, 'firstEventAt'),
|
|
lastEventAt: requiredString(value, 'lastEventAt'),
|
|
lastEvent: requiredString(value, 'lastEvent'),
|
|
eventChainHead: requiredString(value, 'eventChainHead'),
|
|
cognitiveGravity: parseCognitiveGravity(Reflect.get(value, 'cognitiveGravity')),
|
|
attribution: parseAttribution(Reflect.get(value, 'attribution')),
|
|
}
|
|
}
|
|
|
|
function parseReceipt(
|
|
value: unknown,
|
|
expectedPersonaId: string,
|
|
expectedRepositoryPath: string,
|
|
): PersonaRuntimeQueryReceipt {
|
|
if (!isRecord(value)) throw new Error('PERSONA_RUNTIME_RECEIPT_INVALID')
|
|
const schema = requiredString(value, 'schema')
|
|
const personaId = requiredString(value, 'personaId')
|
|
const repositoryPath = requiredString(value, 'repositoryPath')
|
|
const observedGitHead = requiredString(value, 'observedGitHead')
|
|
const repositoryClean = Reflect.get(value, 'repositoryClean')
|
|
const sessionsValue = Reflect.get(value, 'sessions')
|
|
if (
|
|
schema !== 'hololake.pncc-runtime-query/v1'
|
|
|| personaId !== expectedPersonaId
|
|
|| repositoryPath !== expectedRepositoryPath
|
|
|| typeof repositoryClean !== 'boolean'
|
|
|| !Array.isArray(sessionsValue)
|
|
) {
|
|
throw new Error('PERSONA_RUNTIME_RECEIPT_INVALID')
|
|
}
|
|
const matchingSessionCount = requiredNumber(value, 'matchingSessionCount')
|
|
const returnedSessionCount = requiredNumber(value, 'returnedSessionCount')
|
|
const sessions = sessionsValue.map(parseSession)
|
|
if (returnedSessionCount !== sessions.length || matchingSessionCount < returnedSessionCount) {
|
|
throw new Error('PERSONA_RUNTIME_RECEIPT_INVALID')
|
|
}
|
|
return {
|
|
schema,
|
|
personaId,
|
|
repositoryPath,
|
|
observedGitHead,
|
|
repositoryClean,
|
|
matchingSessionCount,
|
|
returnedSessionCount,
|
|
sessions,
|
|
}
|
|
}
|
|
|
|
async function queryNativeRuntime(personaId: string, repositoryPath: string): Promise<unknown> {
|
|
const args = {
|
|
input: {
|
|
expectedPersonaId: personaId,
|
|
expectedRepositoryPath: repositoryPath,
|
|
limit: 25,
|
|
},
|
|
}
|
|
return isTauri()
|
|
? invoke<unknown>('query_persona_code_channel_runtime', args)
|
|
: mockInvoke<unknown>('query_persona_code_channel_runtime', args)
|
|
}
|
|
|
|
export async function loadPersonaRuntimeProjection({
|
|
personaId,
|
|
repositoryPaths,
|
|
query = queryNativeRuntime,
|
|
}: LoadPersonaRuntimeProjectionInput): Promise<PersonaRuntimeProjection> {
|
|
const uniqueRepositoryPaths = [...new Set(repositoryPaths.map((path) => path.trim()).filter(Boolean))]
|
|
if (uniqueRepositoryPaths.length === 0) {
|
|
return { errors: [], inspectedRepositoryCount: 0, phase: 'unavailable', sessions: [] }
|
|
}
|
|
|
|
const results = await Promise.all(uniqueRepositoryPaths.map(async (repositoryPath) => {
|
|
try {
|
|
const receipt = parseReceipt(await query(personaId, repositoryPath), personaId, repositoryPath)
|
|
return { receipt, repositoryPath, error: null }
|
|
} catch (error) {
|
|
return { receipt: null, repositoryPath, error: errorCode(error) }
|
|
}
|
|
}))
|
|
const verified = results.flatMap((result) => result.receipt ? [result.receipt] : [])
|
|
const errors = results.flatMap((result) => result.error
|
|
? [{ code: result.error, repositoryPath: result.repositoryPath }]
|
|
: [])
|
|
const sessions = verified.flatMap((receipt) => receipt.sessions.map((session) => ({
|
|
...session,
|
|
repositoryPath: receipt.repositoryPath,
|
|
observedGitHead: receipt.observedGitHead,
|
|
repositoryClean: receipt.repositoryClean,
|
|
}))).sort((left, right) => (
|
|
right.lastEventAt.localeCompare(left.lastEventAt)
|
|
|| right.sessionId.localeCompare(left.sessionId)
|
|
))
|
|
|
|
return {
|
|
errors,
|
|
inspectedRepositoryCount: verified.length,
|
|
phase: verified.length > 0 ? 'verified' : 'error',
|
|
sessions,
|
|
}
|
|
}
|