@arclabs561/ai-visual-test 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. package/.secretsignore.example +20 -0
  2. package/CHANGELOG.md +360 -0
  3. package/CONTRIBUTING.md +63 -0
  4. package/DEPLOYMENT.md +80 -0
  5. package/LICENSE +22 -0
  6. package/README.md +142 -0
  7. package/SECURITY.md +108 -0
  8. package/api/health.js +34 -0
  9. package/api/validate.js +252 -0
  10. package/index.d.ts +1221 -0
  11. package/package.json +112 -0
  12. package/public/index.html +149 -0
  13. package/src/batch-optimizer.mjs +451 -0
  14. package/src/bias-detector.mjs +370 -0
  15. package/src/bias-mitigation.mjs +233 -0
  16. package/src/cache.mjs +433 -0
  17. package/src/config.mjs +268 -0
  18. package/src/constants.mjs +80 -0
  19. package/src/context-compressor.mjs +350 -0
  20. package/src/convenience.mjs +617 -0
  21. package/src/cost-tracker.mjs +257 -0
  22. package/src/cross-modal-consistency.mjs +170 -0
  23. package/src/data-extractor.mjs +232 -0
  24. package/src/dynamic-few-shot.mjs +140 -0
  25. package/src/dynamic-prompts.mjs +361 -0
  26. package/src/ensemble/index.mjs +53 -0
  27. package/src/ensemble-judge.mjs +366 -0
  28. package/src/error-handler.mjs +67 -0
  29. package/src/errors.mjs +167 -0
  30. package/src/experience-propagation.mjs +128 -0
  31. package/src/experience-tracer.mjs +487 -0
  32. package/src/explanation-manager.mjs +299 -0
  33. package/src/feedback-aggregator.mjs +248 -0
  34. package/src/game-goal-prompts.mjs +478 -0
  35. package/src/game-player.mjs +548 -0
  36. package/src/hallucination-detector.mjs +155 -0
  37. package/src/helpers/playwright.mjs +80 -0
  38. package/src/human-validation-manager.mjs +516 -0
  39. package/src/index.mjs +364 -0
  40. package/src/judge.mjs +929 -0
  41. package/src/latency-aware-batch-optimizer.mjs +192 -0
  42. package/src/load-env.mjs +159 -0
  43. package/src/logger.mjs +55 -0
  44. package/src/metrics.mjs +187 -0
  45. package/src/model-tier-selector.mjs +221 -0
  46. package/src/multi-modal/index.mjs +36 -0
  47. package/src/multi-modal-fusion.mjs +190 -0
  48. package/src/multi-modal.mjs +524 -0
  49. package/src/natural-language-specs.mjs +1071 -0
  50. package/src/pair-comparison.mjs +277 -0
  51. package/src/persona/index.mjs +42 -0
  52. package/src/persona-enhanced.mjs +200 -0
  53. package/src/persona-experience.mjs +572 -0
  54. package/src/position-counterbalance.mjs +140 -0
  55. package/src/prompt-composer.mjs +375 -0
  56. package/src/render-change-detector.mjs +583 -0
  57. package/src/research-enhanced-validation.mjs +436 -0
  58. package/src/retry.mjs +152 -0
  59. package/src/rubrics.mjs +231 -0
  60. package/src/score-tracker.mjs +277 -0
  61. package/src/smart-validator.mjs +447 -0
  62. package/src/spec-config.mjs +106 -0
  63. package/src/spec-templates.mjs +347 -0
  64. package/src/specs/index.mjs +38 -0
  65. package/src/temporal/index.mjs +102 -0
  66. package/src/temporal-adaptive.mjs +163 -0
  67. package/src/temporal-batch-optimizer.mjs +222 -0
  68. package/src/temporal-constants.mjs +69 -0
  69. package/src/temporal-context.mjs +49 -0
  70. package/src/temporal-decision-manager.mjs +271 -0
  71. package/src/temporal-decision.mjs +669 -0
  72. package/src/temporal-errors.mjs +58 -0
  73. package/src/temporal-note-pruner.mjs +173 -0
  74. package/src/temporal-preprocessor.mjs +543 -0
  75. package/src/temporal-prompt-formatter.mjs +219 -0
  76. package/src/temporal-validation.mjs +159 -0
  77. package/src/temporal.mjs +415 -0
  78. package/src/type-guards.mjs +311 -0
  79. package/src/uncertainty-reducer.mjs +470 -0
  80. package/src/utils/index.mjs +175 -0
  81. package/src/validation-framework.mjs +321 -0
  82. package/src/validation-result-normalizer.mjs +64 -0
  83. package/src/validation.mjs +243 -0
  84. package/src/validators/accessibility-programmatic.mjs +345 -0
  85. package/src/validators/accessibility-validator.mjs +223 -0
  86. package/src/validators/batch-validator.mjs +143 -0
  87. package/src/validators/hybrid-validator.mjs +268 -0
  88. package/src/validators/index.mjs +34 -0
  89. package/src/validators/prompt-builder.mjs +218 -0
  90. package/src/validators/rubric.mjs +85 -0
  91. package/src/validators/state-programmatic.mjs +260 -0
  92. package/src/validators/state-validator.mjs +291 -0
  93. package/vercel.json +27 -0
@@ -0,0 +1,219 @@
1
+ /**
2
+ * Temporal Prompt Formatter
3
+ *
4
+ * Formats temporal notes (single-scale and multi-scale) for optimal VLM understanding.
5
+ * Provides different formatting strategies for different temporal aggregation types.
6
+ */
7
+
8
+ import { formatNotesForPrompt } from './temporal.mjs';
9
+
10
+ /**
11
+ * Format temporal notes for prompt inclusion
12
+ *
13
+ * Handles both single-scale (AggregatedTemporalNotes) and multi-scale (MultiScaleAggregation)
14
+ * temporal notes, formatting them optimally for VLM understanding.
15
+ *
16
+ * @param {import('./index.mjs').AggregatedTemporalNotes | import('./index.mjs').MultiScaleAggregation | Array} temporalNotes - Temporal notes (aggregated, multi-scale, or raw array)
17
+ * @param {Object} options - Formatting options
18
+ * @param {boolean} [options.includeMultiScale=true] - Include multi-scale insights if available
19
+ * @param {boolean} [options.naturalLanguage=true] - Use natural language formatting
20
+ * @returns {string} Formatted temporal context for prompt
21
+ */
22
+ export function formatTemporalForPrompt(temporalNotes, options = {}) {
23
+ const {
24
+ includeMultiScale = true,
25
+ naturalLanguage = true
26
+ } = options;
27
+
28
+ if (!temporalNotes) {
29
+ return '';
30
+ }
31
+
32
+ // Handle raw array - should be aggregated first (caller's responsibility)
33
+ if (Array.isArray(temporalNotes)) {
34
+ return ''; // Don't format raw arrays here
35
+ }
36
+
37
+ // Handle multi-scale aggregation
38
+ if (temporalNotes.scales && !temporalNotes.windows) {
39
+ return formatMultiScaleForPrompt(temporalNotes, { naturalLanguage });
40
+ }
41
+
42
+ // Handle single-scale aggregation
43
+ if (temporalNotes.windows) {
44
+ return formatSingleScaleForPrompt(temporalNotes, { naturalLanguage });
45
+ }
46
+
47
+ return '';
48
+ }
49
+
50
+ /**
51
+ * Format single-scale aggregated notes for prompt
52
+ */
53
+ function formatSingleScaleForPrompt(aggregated, options = {}) {
54
+ const { naturalLanguage = true } = options;
55
+
56
+ if (naturalLanguage) {
57
+ // Natural language format
58
+ const parts = [];
59
+
60
+ parts.push('TEMPORAL CONTEXT:');
61
+ parts.push(aggregated.summary || 'Previous observations over time.');
62
+
63
+ if (aggregated.windows && aggregated.windows.length > 0) {
64
+ parts.push('\nTime Windows:');
65
+ aggregated.windows.slice(-5).forEach((window, i) => {
66
+ const timeRange = window.timeRange || `window ${window.window || i + 1}`;
67
+ const score = window.avgScore !== null && window.avgScore !== undefined
68
+ ? `${window.avgScore.toFixed(1)}/10`
69
+ : 'N/A';
70
+ parts.push(` ${timeRange}: Score ${score}, ${window.noteCount || 0} observations`);
71
+ if (window.observations) {
72
+ parts.push(` Notes: ${window.observations.substring(0, 150)}${window.observations.length > 150 ? '...' : ''}`);
73
+ }
74
+ });
75
+ }
76
+
77
+ if (aggregated.coherence !== null && aggregated.coherence !== undefined) {
78
+ const coherenceDesc = aggregated.coherence > 0.7 ? 'high' : aggregated.coherence > 0.4 ? 'moderate' : 'low';
79
+ parts.push(`\nTemporal Coherence: ${(aggregated.coherence * 100).toFixed(0)}% (${coherenceDesc})`);
80
+ }
81
+
82
+ if (aggregated.conflicts && aggregated.conflicts.length > 0) {
83
+ parts.push(`\nCoherence Issues: ${aggregated.conflicts.length} potential conflicts detected`);
84
+ }
85
+
86
+ parts.push('\nWhen evaluating, consider how the current state relates to these temporal patterns and trends.');
87
+
88
+ return parts.join('\n');
89
+ } else {
90
+ // Structured format (original)
91
+ return formatNotesForPrompt(aggregated);
92
+ }
93
+ }
94
+
95
+ /**
96
+ * Format multi-scale aggregation for prompt
97
+ */
98
+ function formatMultiScaleForPrompt(multiScale, options = {}) {
99
+ const { naturalLanguage = true } = options;
100
+
101
+ if (!multiScale.scales || Object.keys(multiScale.scales).length === 0) {
102
+ return '';
103
+ }
104
+
105
+ const parts = [];
106
+
107
+ if (naturalLanguage) {
108
+ parts.push('TEMPORAL CONTEXT (Multi-Scale Analysis):');
109
+ parts.push('The experience has been analyzed across multiple time scales:');
110
+ parts.push('');
111
+
112
+ // Order scales by time (shortest to longest)
113
+ const scaleOrder = ['immediate', 'short', 'medium', 'long'];
114
+ const orderedScales = scaleOrder
115
+ .filter(scale => multiScale.scales[scale])
116
+ .map(scale => [scale, multiScale.scales[scale]]);
117
+
118
+ orderedScales.forEach(([scaleName, scaleData]) => {
119
+ const scaleDescriptions = {
120
+ immediate: 'Instant perception (0.1s) - visual feedback, animations',
121
+ short: 'Quick interaction (1s) - button responses, immediate feedback',
122
+ medium: 'Short task (5s) - form filling, quick actions',
123
+ long: 'Longer session (30s) - overall experience, engagement'
124
+ };
125
+
126
+ parts.push(`${scaleName.toUpperCase()} Scale (${scaleDescriptions[scaleName] || scaleName}):`);
127
+
128
+ if (scaleData.windows && scaleData.windows.length > 0) {
129
+ const recentWindows = scaleData.windows.slice(-3); // Last 3 windows
130
+ recentWindows.forEach((window, i) => {
131
+ const timeRange = window.timeRange || `window ${window.window || i + 1}`;
132
+ const score = window.avgScore !== null && window.avgScore !== undefined
133
+ ? `${window.avgScore.toFixed(1)}/10`
134
+ : 'N/A';
135
+ parts.push(` ${timeRange}: Score ${score}`);
136
+ });
137
+
138
+ if (scaleData.coherence !== null && scaleData.coherence !== undefined) {
139
+ const coherenceDesc = scaleData.coherence > 0.7 ? 'high' : scaleData.coherence > 0.4 ? 'moderate' : 'low';
140
+ parts.push(` Coherence: ${(scaleData.coherence * 100).toFixed(0)}% (${coherenceDesc})`);
141
+ }
142
+ } else {
143
+ parts.push(` No windows in this scale`);
144
+ }
145
+ parts.push('');
146
+ });
147
+
148
+ // Overall summary
149
+ if (multiScale.summary) {
150
+ parts.push(`Overall: ${multiScale.summary}`);
151
+ }
152
+
153
+ parts.push('\nWhen evaluating, consider patterns at different time scales:');
154
+ parts.push('- Immediate: Visual feedback and animation quality');
155
+ parts.push('- Short: Interaction responsiveness and quick feedback');
156
+ parts.push('- Medium: Task completion flow and user journey');
157
+ parts.push('- Long: Overall experience quality and engagement');
158
+
159
+ return parts.join('\n');
160
+ } else {
161
+ // Structured format
162
+ parts.push('TEMPORAL CONTEXT (Multi-Scale):');
163
+ parts.push(multiScale.summary || 'Multi-scale temporal analysis');
164
+ parts.push('');
165
+
166
+ Object.entries(multiScale.scales).forEach(([scaleName, scaleData]) => {
167
+ parts.push(`${scaleName.toUpperCase()} Scale:`);
168
+ parts.push(` Windows: ${scaleData.windows?.length || 0}`);
169
+ parts.push(` Coherence: ${scaleData.coherence ? (scaleData.coherence * 100).toFixed(0) : 'N/A'}%`);
170
+ if (scaleData.windows && scaleData.windows.length > 0) {
171
+ scaleData.windows.slice(-3).forEach(window => {
172
+ const timeRange = window.timeRange || `window ${window.window}`;
173
+ const score = window.avgScore !== null && window.avgScore !== undefined
174
+ ? `${window.avgScore.toFixed(1)}/10`
175
+ : 'N/A';
176
+ parts.push(` ${timeRange}: ${score}`);
177
+ });
178
+ }
179
+ parts.push('');
180
+ });
181
+
182
+ return parts.join('\n');
183
+ }
184
+ }
185
+
186
+ /**
187
+ * Format temporal notes with optimal strategy
188
+ *
189
+ * Chooses the best formatting based on the type of temporal notes provided.
190
+ *
191
+ * @param {any} temporalNotes - Temporal notes (any format)
192
+ * @param {Object} options - Formatting options
193
+ * @returns {string} Formatted temporal context
194
+ */
195
+ export function formatTemporalContext(temporalNotes, options = {}) {
196
+ if (!temporalNotes) {
197
+ return '';
198
+ }
199
+
200
+ // Raw array - return empty (should be aggregated first)
201
+ if (Array.isArray(temporalNotes)) {
202
+ return '';
203
+ }
204
+
205
+ // Multi-scale - use multi-scale formatter
206
+ if (temporalNotes.scales && !temporalNotes.windows) {
207
+ return formatMultiScaleForPrompt(temporalNotes, options);
208
+ }
209
+
210
+ // Single-scale - use single-scale formatter
211
+ if (temporalNotes.windows) {
212
+ return formatSingleScaleForPrompt(temporalNotes, options);
213
+ }
214
+
215
+ return '';
216
+ }
217
+
218
+ export { formatSingleScaleForPrompt, formatMultiScaleForPrompt };
219
+
@@ -0,0 +1,159 @@
1
+ /**
2
+ * Temporal Validation
3
+ * Input validation for temporal decision-making components
4
+ */
5
+
6
+ import { MultiScaleError, PerceptionTimeError, SequentialContextError } from './temporal-errors.mjs';
7
+ import { warn } from './logger.mjs';
8
+
9
+ /**
10
+ * Validate temporal notes
11
+ */
12
+ export function validateNotes(notes) {
13
+ if (!Array.isArray(notes)) {
14
+ throw new MultiScaleError('Notes must be an array', { received: typeof notes });
15
+ }
16
+
17
+ const validNotes = [];
18
+ const invalidNotes = [];
19
+
20
+ for (let i = 0; i < notes.length; i++) {
21
+ const note = notes[i];
22
+
23
+ if (!note || typeof note !== 'object') {
24
+ invalidNotes.push({ index: i, reason: 'Not an object' });
25
+ continue;
26
+ }
27
+
28
+ if (!note.timestamp && note.elapsed === undefined) {
29
+ invalidNotes.push({ index: i, reason: 'Missing timestamp or elapsed' });
30
+ continue;
31
+ }
32
+
33
+ if (note.timestamp && typeof note.timestamp !== 'number') {
34
+ invalidNotes.push({ index: i, reason: 'Invalid timestamp type' });
35
+ continue;
36
+ }
37
+
38
+ if (note.elapsed !== undefined && typeof note.elapsed !== 'number') {
39
+ invalidNotes.push({ index: i, reason: 'Invalid elapsed type' });
40
+ continue;
41
+ }
42
+
43
+ validNotes.push(note);
44
+ }
45
+
46
+ if (invalidNotes.length > 0) {
47
+ warn(`[Temporal] ${invalidNotes.length} invalid notes filtered out:`, invalidNotes);
48
+ }
49
+
50
+ return validNotes;
51
+ }
52
+
53
+ /**
54
+ * Validate and sort notes
55
+ * Convenience function that validates and sorts in one step
56
+ */
57
+ export function validateAndSortNotes(notes) {
58
+ const validNotes = validateNotes(notes);
59
+ return validNotes.sort((a, b) => (a.timestamp || 0) - (b.timestamp || 0));
60
+ }
61
+
62
+ /**
63
+ * Validate time scales
64
+ */
65
+ export function validateTimeScales(timeScales) {
66
+ if (typeof timeScales !== 'object' || timeScales === null) {
67
+ throw new MultiScaleError('Time scales must be an object', { received: typeof timeScales });
68
+ }
69
+
70
+ for (const [name, value] of Object.entries(timeScales)) {
71
+ if (typeof value !== 'number' || value <= 0) {
72
+ throw new MultiScaleError(`Invalid time scale ${name}: ${value}`, {
73
+ scaleName: name,
74
+ value,
75
+ type: typeof value
76
+ });
77
+ }
78
+ }
79
+
80
+ return true;
81
+ }
82
+
83
+ /**
84
+ * Validate action for human perception time
85
+ */
86
+ export function validateAction(action) {
87
+ const validActions = ['page-load', 'reading', 'interaction', 'evaluation', 'scanning', 'visual-appeal'];
88
+
89
+ if (typeof action !== 'string') {
90
+ throw new PerceptionTimeError('Action must be a string', { received: typeof action });
91
+ }
92
+
93
+ if (!validActions.includes(action)) {
94
+ throw new PerceptionTimeError(`Invalid action: ${action}`, {
95
+ action,
96
+ validActions
97
+ });
98
+ }
99
+
100
+ return true;
101
+ }
102
+
103
+ /**
104
+ * Validate context for human perception time
105
+ */
106
+ export function validatePerceptionContext(context) {
107
+ if (context === null || typeof context !== 'object') {
108
+ throw new PerceptionTimeError('Context must be an object', { received: typeof context });
109
+ }
110
+
111
+ if (context.attentionLevel && !['focused', 'normal', 'distracted'].includes(context.attentionLevel)) {
112
+ throw new PerceptionTimeError(`Invalid attentionLevel: ${context.attentionLevel}`, {
113
+ attentionLevel: context.attentionLevel,
114
+ validLevels: ['focused', 'normal', 'distracted']
115
+ });
116
+ }
117
+
118
+ if (context.actionComplexity && !['simple', 'normal', 'complex'].includes(context.actionComplexity)) {
119
+ throw new PerceptionTimeError(`Invalid actionComplexity: ${context.actionComplexity}`, {
120
+ actionComplexity: context.actionComplexity,
121
+ validComplexities: ['simple', 'normal', 'complex']
122
+ });
123
+ }
124
+
125
+ if (context.contentLength !== undefined && (typeof context.contentLength !== 'number' || context.contentLength < 0)) {
126
+ throw new PerceptionTimeError('contentLength must be a non-negative number', {
127
+ contentLength: context.contentLength
128
+ });
129
+ }
130
+
131
+ return true;
132
+ }
133
+
134
+ /**
135
+ * Validate sequential decision context options
136
+ */
137
+ export function validateSequentialContextOptions(options) {
138
+ // Allow empty object or undefined (defaults will be used)
139
+ if (options === null || options === undefined) {
140
+ return true;
141
+ }
142
+
143
+ // Allow empty object {} (defaults will be used)
144
+ if (typeof options !== 'object') {
145
+ throw new SequentialContextError('Options must be an object', { received: typeof options });
146
+ }
147
+
148
+ // Only validate if maxHistory is explicitly provided
149
+ if (options.maxHistory !== undefined && options.maxHistory !== null) {
150
+ if (typeof options.maxHistory !== 'number' || options.maxHistory < 1) {
151
+ throw new SequentialContextError('maxHistory must be a positive number', {
152
+ maxHistory: options.maxHistory
153
+ });
154
+ }
155
+ }
156
+
157
+ return true;
158
+ }
159
+