File size: 7,065 Bytes
4fdaf19
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
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}.`;
}