wu-framework 1.1.6 → 1.1.8

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 (90) hide show
  1. package/README.md +511 -977
  2. package/dist/wu-framework.cjs.js +3 -1
  3. package/dist/wu-framework.cjs.js.map +1 -0
  4. package/dist/wu-framework.dev.js +7533 -2761
  5. package/dist/wu-framework.dev.js.map +1 -1
  6. package/dist/wu-framework.esm.js +3 -0
  7. package/dist/wu-framework.esm.js.map +1 -0
  8. package/dist/wu-framework.umd.js +3 -1
  9. package/dist/wu-framework.umd.js.map +1 -0
  10. package/integrations/astro/README.md +127 -0
  11. package/integrations/astro/WuApp.astro +63 -0
  12. package/integrations/astro/WuShell.astro +39 -0
  13. package/integrations/astro/index.js +68 -0
  14. package/integrations/astro/package.json +38 -0
  15. package/integrations/astro/types.d.ts +53 -0
  16. package/package.json +94 -74
  17. package/src/adapters/angular/ai.js +30 -0
  18. package/src/adapters/angular/index.d.ts +154 -0
  19. package/src/adapters/angular/index.js +932 -0
  20. package/src/adapters/angular.d.ts +3 -154
  21. package/src/adapters/angular.js +3 -813
  22. package/src/adapters/index.js +35 -24
  23. package/src/adapters/lit/ai.js +20 -0
  24. package/src/adapters/lit/index.d.ts +120 -0
  25. package/src/adapters/lit/index.js +721 -0
  26. package/src/adapters/lit.d.ts +3 -120
  27. package/src/adapters/lit.js +3 -726
  28. package/src/adapters/preact/ai.js +33 -0
  29. package/src/adapters/preact/index.d.ts +108 -0
  30. package/src/adapters/preact/index.js +661 -0
  31. package/src/adapters/preact.d.ts +3 -108
  32. package/src/adapters/preact.js +3 -665
  33. package/src/adapters/react/ai.js +135 -0
  34. package/src/adapters/react/index.d.ts +246 -0
  35. package/src/adapters/react/index.js +689 -0
  36. package/src/adapters/react.d.ts +3 -212
  37. package/src/adapters/react.js +3 -513
  38. package/src/adapters/shared.js +64 -0
  39. package/src/adapters/solid/ai.js +32 -0
  40. package/src/adapters/solid/index.d.ts +101 -0
  41. package/src/adapters/solid/index.js +586 -0
  42. package/src/adapters/solid.d.ts +3 -101
  43. package/src/adapters/solid.js +3 -591
  44. package/src/adapters/svelte/ai.js +31 -0
  45. package/src/adapters/svelte/index.d.ts +166 -0
  46. package/src/adapters/svelte/index.js +798 -0
  47. package/src/adapters/svelte.d.ts +3 -166
  48. package/src/adapters/svelte.js +3 -803
  49. package/src/adapters/vanilla/ai.js +30 -0
  50. package/src/adapters/vanilla/index.d.ts +179 -0
  51. package/src/adapters/vanilla/index.js +785 -0
  52. package/src/adapters/vanilla.d.ts +3 -179
  53. package/src/adapters/vanilla.js +3 -791
  54. package/src/adapters/vue/ai.js +52 -0
  55. package/src/adapters/vue/index.d.ts +299 -0
  56. package/src/adapters/vue/index.js +608 -0
  57. package/src/adapters/vue.d.ts +3 -299
  58. package/src/adapters/vue.js +3 -611
  59. package/src/ai/wu-ai-actions.js +261 -0
  60. package/src/ai/wu-ai-browser.js +663 -0
  61. package/src/ai/wu-ai-context.js +332 -0
  62. package/src/ai/wu-ai-conversation.js +554 -0
  63. package/src/ai/wu-ai-permissions.js +381 -0
  64. package/src/ai/wu-ai-provider.js +605 -0
  65. package/src/ai/wu-ai-schema.js +225 -0
  66. package/src/ai/wu-ai-triggers.js +396 -0
  67. package/src/ai/wu-ai.js +474 -0
  68. package/src/core/wu-app.js +50 -8
  69. package/src/core/wu-cache.js +1 -1
  70. package/src/core/wu-core.js +645 -677
  71. package/src/core/wu-html-parser.js +121 -211
  72. package/src/core/wu-iframe-sandbox.js +328 -0
  73. package/src/core/wu-mcp-bridge.js +647 -0
  74. package/src/core/wu-overrides.js +510 -0
  75. package/src/core/wu-prefetch.js +414 -0
  76. package/src/core/wu-proxy-sandbox.js +398 -75
  77. package/src/core/wu-sandbox.js +86 -268
  78. package/src/core/wu-script-executor.js +79 -182
  79. package/src/core/wu-snapshot-sandbox.js +149 -106
  80. package/src/core/wu-strategies.js +13 -0
  81. package/src/core/wu-style-bridge.js +0 -2
  82. package/src/index.js +139 -665
  83. package/dist/wu-framework.hex.js +0 -23
  84. package/dist/wu-framework.min.js +0 -1
  85. package/dist/wu-framework.obf.js +0 -1
  86. package/scripts/build-protected.js +0 -366
  87. package/scripts/build.js +0 -212
  88. package/scripts/rollup-plugin-hex.js +0 -143
  89. package/src/core/wu-registry.js +0 -60
  90. package/src/core/wu-sandbox-pool.js +0 -390
@@ -0,0 +1,332 @@
1
+ /**
2
+ * WU-AI-CONTEXT: Automatic context collector for LLMs
3
+ *
4
+ * Collects state from wu.store, wu.eventBus, and registered apps.
5
+ * Builds a structured context object and system prompt for the LLM.
6
+ *
7
+ * Key design:
8
+ * - ON-DEMAND collection (no polling, zero CPU idle)
9
+ * - Token budget with priority (high > medium > low)
10
+ * - Sensitive data redaction
11
+ * - System prompt generation
12
+ */
13
+
14
+ import { logger } from '../core/wu-logger.js';
15
+ import { redactSensitive, estimateTokens, truncateToTokenBudget } from './wu-ai-schema.js';
16
+
17
+ // ─── Context Source Config ───────────────────────────────────────
18
+ //
19
+ // store: { include: ['user.*', 'cart.*'], priority: 'high' }
20
+ // events: { include: ['cart:*', 'user:*'], lastN: 10, priority: 'medium' }
21
+ // custom: [{ key: 'appVersion', value: () => '1.0', priority: 'low' }]
22
+
23
+ export class WuAIContext {
24
+ constructor({ store, eventBus, core }) {
25
+ this._store = store;
26
+ this._eventBus = eventBus;
27
+ this._core = core;
28
+
29
+ this._config = {
30
+ budget: 4000, // token budget
31
+ charRatio: 4, // chars per token estimate
32
+ sources: {
33
+ store: { include: [], priority: 'high' },
34
+ events: { include: [], lastN: 10, priority: 'medium' },
35
+ custom: [],
36
+ },
37
+ };
38
+
39
+ this._collectors = new Map(); // name → { collector, priority }
40
+ this._lastSnapshot = null;
41
+ }
42
+
43
+ /**
44
+ * Configure context collection.
45
+ */
46
+ configure(config) {
47
+ if (config.budget !== undefined) this._config.budget = config.budget;
48
+ if (config.charRatio !== undefined) this._config.charRatio = config.charRatio;
49
+ if (config.sources) {
50
+ if (config.sources.store) Object.assign(this._config.sources.store, config.sources.store);
51
+ if (config.sources.events) Object.assign(this._config.sources.events, config.sources.events);
52
+ if (config.sources.custom) this._config.sources.custom = config.sources.custom;
53
+ }
54
+ }
55
+
56
+ /**
57
+ * Register a named context collector.
58
+ *
59
+ * @param {string} name - Collector name (e.g., 'dashboard', 'analytics')
60
+ * @param {{ collector: Function, priority?: string }} config
61
+ */
62
+ register(name, config) {
63
+ this._collectors.set(name, {
64
+ collector: config.collector,
65
+ priority: config.priority || 'medium',
66
+ });
67
+ logger.wuDebug(`[wu-ai] Context collector registered: '${name}' (${config.priority || 'medium'})`);
68
+ }
69
+
70
+ /**
71
+ * Collect all context ON-DEMAND.
72
+ * Called before sending a message to the LLM.
73
+ *
74
+ * @returns {object} Context snapshot
75
+ */
76
+ async collect() {
77
+ const snapshot = {
78
+ _timestamp: Date.now(),
79
+ _mountedApps: this._getMountedApps(),
80
+ };
81
+
82
+ // Collect store sources
83
+ const storeData = this._collectStore();
84
+ if (storeData && Object.keys(storeData).length > 0) {
85
+ snapshot._store = storeData;
86
+ }
87
+
88
+ // Collect recent events
89
+ const events = this._collectEvents();
90
+ if (events.length > 0) {
91
+ snapshot._events = events;
92
+ }
93
+
94
+ // Collect custom sources
95
+ for (const custom of this._config.sources.custom) {
96
+ try {
97
+ const value = typeof custom.value === 'function' ? await custom.value() : custom.value;
98
+ snapshot[custom.key] = value;
99
+ } catch (err) {
100
+ logger.wuDebug(`[wu-ai] Custom collector '${custom.key}' failed: ${err.message}`);
101
+ }
102
+ }
103
+
104
+ // Collect registered collectors
105
+ for (const [name, config] of this._collectors) {
106
+ try {
107
+ const data = await config.collector();
108
+ snapshot[name] = data;
109
+ } catch (err) {
110
+ logger.wuDebug(`[wu-ai] Collector '${name}' failed: ${err.message}`);
111
+ }
112
+ }
113
+
114
+ this._lastSnapshot = snapshot;
115
+ return snapshot;
116
+ }
117
+
118
+ /**
119
+ * Build a system prompt from the context for the LLM.
120
+ *
121
+ * @param {{ tools?: Array }} [options] - Available tools to include
122
+ * @returns {string} System prompt
123
+ */
124
+ toSystemPrompt(options = {}) {
125
+ const snapshot = this._lastSnapshot;
126
+ if (!snapshot) return this._baseSystemPrompt(options);
127
+
128
+ const parts = [];
129
+
130
+ // Base instruction
131
+ parts.push(
132
+ 'You are an AI assistant connected to a live web application via Wu Framework.',
133
+ 'You can observe application state and execute actions when appropriate.',
134
+ ''
135
+ );
136
+
137
+ // Mounted apps (always included)
138
+ if (snapshot._mountedApps?.length) {
139
+ parts.push(`MOUNTED APPS: ${snapshot._mountedApps.join(', ')}`, '');
140
+ }
141
+
142
+ // Budget tracking
143
+ const budget = this._config.budget;
144
+ const charBudget = budget * this._config.charRatio;
145
+ let usedChars = parts.join('\n').length;
146
+
147
+ // Priority-based inclusion
148
+ const prioritized = this._prioritizeSections(snapshot);
149
+
150
+ for (const section of prioritized) {
151
+ const sectionText = section.text;
152
+ if (usedChars + sectionText.length > charBudget) {
153
+ // Try to truncate if high priority
154
+ if (section.priority === 'high') {
155
+ const remaining = charBudget - usedChars;
156
+ if (remaining > 100) {
157
+ parts.push(sectionText.slice(0, remaining) + '\n...[truncated]');
158
+ usedChars += remaining;
159
+ }
160
+ }
161
+ continue; // skip if over budget
162
+ }
163
+ parts.push(sectionText);
164
+ usedChars += sectionText.length;
165
+ }
166
+
167
+ // Tools (outside budget — LLM needs these)
168
+ if (options.tools?.length) {
169
+ parts.push('', 'AVAILABLE TOOLS:');
170
+ for (const tool of options.tools) {
171
+ parts.push(`- ${tool.name}: ${tool.description}`);
172
+ }
173
+ }
174
+
175
+ return parts.join('\n');
176
+ }
177
+
178
+ /**
179
+ * Get the last collected snapshot.
180
+ */
181
+ getSnapshot() {
182
+ return this._lastSnapshot;
183
+ }
184
+
185
+ /**
186
+ * Get a simplified context object for template interpolation.
187
+ */
188
+ getInterpolationContext() {
189
+ if (!this._lastSnapshot) return {};
190
+ const { _timestamp, _mountedApps, _store, _events, ...custom } = this._lastSnapshot;
191
+ return {
192
+ apps: _mountedApps,
193
+ store: _store,
194
+ events: _events,
195
+ ...custom,
196
+ };
197
+ }
198
+
199
+ // ── Private: Data Collection ──
200
+
201
+ _collectStore() {
202
+ if (!this._store) return {};
203
+ const { include } = this._config.sources.store;
204
+ if (!include || include.length === 0) return {};
205
+
206
+ const data = {};
207
+ for (const path of include) {
208
+ try {
209
+ const value = this._store.get(path);
210
+ if (value !== undefined) {
211
+ data[path] = redactSensitive(value);
212
+ }
213
+ } catch {
214
+ // Path doesn't exist, skip
215
+ }
216
+ }
217
+ return data;
218
+ }
219
+
220
+ _collectEvents() {
221
+ if (!this._eventBus) return [];
222
+ const { include, lastN } = this._config.sources.events;
223
+ if (!include || include.length === 0) return [];
224
+
225
+ const history = this._eventBus.history || [];
226
+ const matching = history.filter(event => {
227
+ return include.some(pattern => this._matchPattern(event.name || event.event, pattern));
228
+ });
229
+
230
+ return matching.slice(-(lastN || 10)).map(e => ({
231
+ event: e.name || e.event,
232
+ data: redactSensitive(e.data),
233
+ timestamp: e.timestamp,
234
+ }));
235
+ }
236
+
237
+ _getMountedApps() {
238
+ if (!this._core) return [];
239
+ try {
240
+ // WuCore exposes mounted as Map or getStats()
241
+ if (this._core.mounted instanceof Map) {
242
+ return [...this._core.mounted.keys()];
243
+ }
244
+ const stats = this._core.getStats?.();
245
+ return stats?.apps || stats?.mounted || [];
246
+ } catch {
247
+ return [];
248
+ }
249
+ }
250
+
251
+ // ── Private: Priority & Budget ──
252
+
253
+ _prioritizeSections(snapshot) {
254
+ const sections = [];
255
+ const storePriority = this._config.sources.store.priority || 'high';
256
+ const eventPriority = this._config.sources.events.priority || 'medium';
257
+
258
+ // Store snapshot
259
+ if (snapshot._store && Object.keys(snapshot._store).length > 0) {
260
+ sections.push({
261
+ priority: storePriority,
262
+ text: `APPLICATION STATE:\n${JSON.stringify(snapshot._store, null, 2)}`,
263
+ });
264
+ }
265
+
266
+ // Recent events
267
+ if (snapshot._events?.length) {
268
+ const eventLines = snapshot._events.map(e =>
269
+ ` [${e.event}] ${JSON.stringify(e.data)}`
270
+ ).join('\n');
271
+ sections.push({
272
+ priority: eventPriority,
273
+ text: `RECENT EVENTS:\n${eventLines}`,
274
+ });
275
+ }
276
+
277
+ // Custom collectors
278
+ for (const [name, config] of this._collectors) {
279
+ if (snapshot[name] !== undefined) {
280
+ sections.push({
281
+ priority: config.priority,
282
+ text: `${name.toUpperCase()}:\n${JSON.stringify(snapshot[name], null, 2)}`,
283
+ });
284
+ }
285
+ }
286
+
287
+ // Custom sources
288
+ for (const custom of this._config.sources.custom) {
289
+ if (snapshot[custom.key] !== undefined) {
290
+ sections.push({
291
+ priority: custom.priority || 'low',
292
+ text: `${custom.key}: ${JSON.stringify(snapshot[custom.key])}`,
293
+ });
294
+ }
295
+ }
296
+
297
+ // Sort: high → medium → low
298
+ const order = { high: 0, medium: 1, low: 2 };
299
+ sections.sort((a, b) => (order[a.priority] ?? 1) - (order[b.priority] ?? 1));
300
+
301
+ return sections;
302
+ }
303
+
304
+ _baseSystemPrompt(options = {}) {
305
+ let prompt = 'You are an AI assistant connected to a web application via Wu Framework.';
306
+ if (options.tools?.length) {
307
+ prompt += '\n\nAVAILABLE TOOLS:\n' + options.tools.map(t => `- ${t.name}: ${t.description}`).join('\n');
308
+ }
309
+ return prompt;
310
+ }
311
+
312
+ // ── Pattern Matching (reuse wu-framework convention) ──
313
+
314
+ _matchPattern(eventName, pattern) {
315
+ if (!eventName || !pattern) return false;
316
+ if (pattern === '*') return true;
317
+ if (!pattern.includes('*')) return eventName === pattern;
318
+
319
+ const regex = new RegExp('^' + pattern.replace(/\*/g, '[^:]*') + '$');
320
+ return regex.test(eventName);
321
+ }
322
+
323
+ getStats() {
324
+ return {
325
+ budget: this._config.budget,
326
+ collectors: [...this._collectors.keys()],
327
+ storePaths: this._config.sources.store.include,
328
+ eventPatterns: this._config.sources.events.include,
329
+ lastCollected: this._lastSnapshot?._timestamp || null,
330
+ };
331
+ }
332
+ }