| import { buildOmniParserTransition, buildSuiteOmniParserElements } from './omniParserAdapter'; |
|
|
| export type MindwalkTouchState = 'unvisited' | 'seen' | 'read' | 'edited' | 'limited'; |
|
|
| export interface MindwalkNode { |
| id: string; |
| label: string; |
| kind: 'tab' | 'subview' | 'persona' | 'limitation' | 'backend'; |
| state: MindwalkTouchState; |
| x: number; |
| y: number; |
| intensity: number; |
| llmPhrase: string; |
| omniParserPhrase: string; |
| personaTags: string[]; |
| limitationIds: string[]; |
| } |
|
|
| export interface MindwalkEdge { |
| source: string; |
| target: string; |
| relation: 'navigates-to' | 'explains' | 'constrains' | 'calls-api'; |
| } |
|
|
| export interface MindwalkGraph { |
| nodes: MindwalkNode[]; |
| edges: MindwalkEdge[]; |
| } |
|
|
| export interface PersonaProfile { |
| id: string; |
| label: string; |
| goals: string[]; |
| limitations: string[]; |
| } |
|
|
| export interface LimitationFunction { |
| id: string; |
| label: string; |
| guardrail: string; |
| injectInto: string[]; |
| } |
|
|
| const personas: PersonaProfile[] = [ |
| { |
| id: 'persona-growth-operator', |
| label: 'Growth operator', |
| goals: ['compare content variants', 'prioritize conversion signals', 'export evidence'], |
| limitations: ['avoid destructive browser actions', 'require citation for claims'], |
| }, |
| { |
| id: 'persona-qa-reviewer', |
| label: 'QA reviewer', |
| goals: ['find regressions', 'replay UI paths', 'verify acceptance criteria'], |
| limitations: ['do not bypass auth', 'stop when sensitive data is visible'], |
| }, |
| { |
| id: 'persona-data-steward', |
| label: 'Data steward', |
| goals: ['trace source records', 'explain graph provenance', 'minimize retained PII'], |
| limitations: ['redact user secrets', 'persist only typed artifacts'], |
| }, |
| ]; |
|
|
| export const limitationFunctions: LimitationFunction[] = [ |
| { |
| id: 'limit-safe-navigation', |
| label: 'Safe navigation', |
| guardrail: 'Only navigate, read, extract, and verify unless the active persona explicitly allows mutation.', |
| injectInto: ['nova-act', 'graph'], |
| }, |
| { |
| id: 'limit-sensitive-fields', |
| label: 'Sensitive-field pause', |
| guardrail: 'Pause and request supervisor review before entering passwords, tokens, payment data, or private identifiers.', |
| injectInto: ['nova-act', 'usersync', 'datahub'], |
| }, |
| { |
| id: 'limit-evidence-first', |
| label: 'Evidence-first output', |
| guardrail: 'Return file, DOM, API, or screenshot evidence with every persona-facing recommendation.', |
| injectInto: ['dev', 'graph', 'oasis'], |
| }, |
| ]; |
|
|
| const tabPositions = { |
| usersync: [0.15, 0.2], |
| 'nova-act': [0.5, 0.12], |
| datahub: [0.82, 0.25], |
| oasis: [0.18, 0.72], |
| graph: [0.5, 0.82], |
| dev: [0.82, 0.72], |
| } as const; |
|
|
| const subviewsByTab: Record<keyof typeof tabPositions, string[]> = { |
| usersync: ['overview', 'simulation', 'personas', 'content'], |
| 'nova-act': ['studio', 'browser', 'qa', 'verify', 'mindwalk'], |
| datahub: ['extract', 'warehouse', 'deploy'], |
| oasis: ['network', 'trust'], |
| graph: ['live', 'signals', 'mindwalk'], |
| dev: ['api', 'events', 'status'], |
| }; |
|
|
| export function buildMindwalkGraph(activeTab = 'nova-act', activeView = 'mindwalk'): MindwalkGraph { |
| const nodes: MindwalkNode[] = []; |
| const edges: MindwalkEdge[] = []; |
|
|
| Object.entries(tabPositions).forEach(([tab, [x, y]]) => { |
| const state: MindwalkTouchState = tab === activeTab ? 'edited' : 'seen'; |
| nodes.push({ |
| id: tab, |
| label: tab, |
| kind: 'tab', |
| state, |
| x, |
| y, |
| intensity: state === 'edited' ? 1 : 0.45, |
| llmPhrase: `Workspace ${tab} maps user intent to available suite actions.`, |
| omniParserPhrase: buildOmniParserTransition(tab, buildSuiteOmniParserElements(tab, 'workspace')).llmLanguage, |
| personaTags: personas.map((persona) => persona.id), |
| limitationIds: limitationFunctions.filter((limitation) => limitation.injectInto.includes(tab)).map((limitation) => limitation.id), |
| }); |
|
|
| subviewsByTab[tab as keyof typeof tabPositions].forEach((subview, index) => { |
| const angle = (Math.PI * 2 * index) / subviewsByTab[tab as keyof typeof tabPositions].length; |
| const id = `${tab}:${subview}`; |
| const isActive = tab === activeTab && subview === activeView; |
| nodes.push({ |
| id, |
| label: subview, |
| kind: 'subview', |
| state: isActive ? 'edited' : tab === activeTab ? 'read' : 'seen', |
| x: x + Math.cos(angle) * 0.075, |
| y: y + Math.sin(angle) * 0.075, |
| intensity: isActive ? 1 : tab === activeTab ? 0.7 : 0.35, |
| llmPhrase: `Subview ${subview} in ${tab} is a navigable UI state that can be summarized for Nova Act prompts.`, |
| omniParserPhrase: buildOmniParserTransition(`${tab}:${subview}`, buildSuiteOmniParserElements(tab, subview)).llmLanguage, |
| personaTags: personas.slice(0, 2).map((persona) => persona.id), |
| limitationIds: limitationFunctions.filter((limitation) => limitation.injectInto.includes(tab)).map((limitation) => limitation.id), |
| }); |
| edges.push({ source: tab, target: id, relation: 'navigates-to' }); |
| }); |
| }); |
|
|
| personas.forEach((persona, index) => { |
| const id = persona.id; |
| nodes.push({ |
| id, |
| label: persona.label, |
| kind: 'persona', |
| state: 'read', |
| x: 0.25 + index * 0.25, |
| y: 0.5, |
| intensity: 0.8, |
| llmPhrase: `${persona.label}: goals ${persona.goals.join(', ')}; constraints ${persona.limitations.join(', ')}.`, |
| omniParserPhrase: buildOmniParserTransition(persona.label, buildSuiteOmniParserElements('usersync', 'personas')).llmLanguage, |
| personaTags: [id], |
| limitationIds: limitationFunctions.map((limitation) => limitation.id), |
| }); |
| edges.push({ source: 'usersync:personas', target: id, relation: 'explains' }); |
| edges.push({ source: id, target: 'nova-act:mindwalk', relation: 'explains' }); |
| }); |
|
|
| limitationFunctions.forEach((limitation, index) => { |
| const id = limitation.id; |
| nodes.push({ |
| id, |
| label: limitation.label, |
| kind: 'limitation', |
| state: 'limited', |
| x: 0.18 + index * 0.32, |
| y: 0.94, |
| intensity: 0.95, |
| llmPhrase: limitation.guardrail, |
| omniParserPhrase: buildOmniParserTransition(limitation.label, buildSuiteOmniParserElements('nova-act', 'mindwalk')).llmLanguage, |
| personaTags: personas.map((persona) => persona.id), |
| limitationIds: [id], |
| }); |
| limitation.injectInto.forEach((tab) => edges.push({ source: id, target: tab, relation: 'constrains' })); |
| }); |
|
|
| edges.push({ source: 'nova-act:mindwalk', target: 'dev:api', relation: 'calls-api' }); |
| edges.push({ source: 'graph:mindwalk', target: 'datahub:warehouse', relation: 'calls-api' }); |
|
|
| return { nodes, edges }; |
| } |
|
|
| export function buildNovaActPrompt(node: MindwalkNode): string { |
| const personaContext = node.personaTags.join(', ') || 'general operator'; |
| const limitations = node.limitationIds.join(', ') || 'default-safe-navigation'; |
| return `OmniParser UI-to-LLM parse: ${node.omniParserPhrase} Mindwalk navigation memory: ${node.llmPhrase} Persona context: ${personaContext}. Inject limitation functions: ${limitations}.`; |
| } |
|
|