wu-framework 1.1.7 → 1.1.9

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 (95) hide show
  1. package/LICENSE +19 -1
  2. package/README.md +257 -1122
  3. package/dist/wu-framework.cjs.js +3 -1
  4. package/dist/wu-framework.cjs.js.map +1 -0
  5. package/dist/wu-framework.dev.js +9867 -3183
  6. package/dist/wu-framework.dev.js.map +1 -1
  7. package/dist/wu-framework.esm.js +3 -0
  8. package/dist/wu-framework.esm.js.map +1 -0
  9. package/dist/wu-framework.umd.js +3 -1
  10. package/dist/wu-framework.umd.js.map +1 -0
  11. package/integrations/astro/README.md +127 -0
  12. package/integrations/astro/WuApp.astro +63 -0
  13. package/integrations/astro/WuShell.astro +39 -0
  14. package/integrations/astro/index.js +68 -0
  15. package/integrations/astro/package.json +38 -0
  16. package/integrations/astro/types.d.ts +53 -0
  17. package/package.json +96 -72
  18. package/src/adapters/angular/ai.js +30 -0
  19. package/src/adapters/angular/index.d.ts +154 -0
  20. package/src/adapters/angular/index.js +932 -0
  21. package/src/adapters/angular.d.ts +3 -154
  22. package/src/adapters/angular.js +3 -813
  23. package/src/adapters/index.js +35 -24
  24. package/src/adapters/lit/ai.js +20 -0
  25. package/src/adapters/lit/index.d.ts +120 -0
  26. package/src/adapters/lit/index.js +721 -0
  27. package/src/adapters/lit.d.ts +3 -120
  28. package/src/adapters/lit.js +3 -726
  29. package/src/adapters/preact/ai.js +33 -0
  30. package/src/adapters/preact/index.d.ts +108 -0
  31. package/src/adapters/preact/index.js +661 -0
  32. package/src/adapters/preact.d.ts +3 -108
  33. package/src/adapters/preact.js +3 -665
  34. package/src/adapters/react/ai.js +135 -0
  35. package/src/adapters/react/index.d.ts +246 -0
  36. package/src/adapters/react/index.js +694 -0
  37. package/src/adapters/react.d.ts +3 -212
  38. package/src/adapters/react.js +3 -513
  39. package/src/adapters/shared.js +64 -0
  40. package/src/adapters/solid/ai.js +32 -0
  41. package/src/adapters/solid/index.d.ts +101 -0
  42. package/src/adapters/solid/index.js +586 -0
  43. package/src/adapters/solid.d.ts +3 -101
  44. package/src/adapters/solid.js +3 -591
  45. package/src/adapters/svelte/ai.js +31 -0
  46. package/src/adapters/svelte/index.d.ts +166 -0
  47. package/src/adapters/svelte/index.js +798 -0
  48. package/src/adapters/svelte.d.ts +3 -166
  49. package/src/adapters/svelte.js +3 -803
  50. package/src/adapters/vanilla/ai.js +30 -0
  51. package/src/adapters/vanilla/index.d.ts +179 -0
  52. package/src/adapters/vanilla/index.js +785 -0
  53. package/src/adapters/vanilla.d.ts +3 -179
  54. package/src/adapters/vanilla.js +3 -791
  55. package/src/adapters/vue/ai.js +52 -0
  56. package/src/adapters/vue/index.d.ts +299 -0
  57. package/src/adapters/vue/index.js +608 -0
  58. package/src/adapters/vue.d.ts +3 -299
  59. package/src/adapters/vue.js +3 -611
  60. package/src/ai/wu-ai-actions.js +261 -0
  61. package/src/ai/wu-ai-agent.js +546 -0
  62. package/src/ai/wu-ai-browser-primitives.js +354 -0
  63. package/src/ai/wu-ai-browser.js +380 -0
  64. package/src/ai/wu-ai-context.js +332 -0
  65. package/src/ai/wu-ai-conversation.js +613 -0
  66. package/src/ai/wu-ai-orchestrate.js +1021 -0
  67. package/src/ai/wu-ai-permissions.js +381 -0
  68. package/src/ai/wu-ai-provider.js +700 -0
  69. package/src/ai/wu-ai-schema.js +225 -0
  70. package/src/ai/wu-ai-triggers.js +396 -0
  71. package/src/ai/wu-ai.js +804 -0
  72. package/src/core/wu-app.js +50 -8
  73. package/src/core/wu-cache.js +2 -3
  74. package/src/core/wu-core.js +648 -681
  75. package/src/core/wu-html-parser.js +121 -211
  76. package/src/core/wu-iframe-sandbox.js +328 -0
  77. package/src/core/wu-mcp-bridge.js +431 -0
  78. package/src/core/wu-overrides.js +510 -0
  79. package/src/core/wu-plugin.js +4 -1
  80. package/src/core/wu-prefetch.js +414 -0
  81. package/src/core/wu-proxy-sandbox.js +398 -75
  82. package/src/core/wu-sandbox.js +86 -268
  83. package/src/core/wu-script-executor.js +79 -182
  84. package/src/core/wu-snapshot-sandbox.js +149 -106
  85. package/src/core/wu-strategies.js +13 -0
  86. package/src/core/wu-style-bridge.js +23 -23
  87. package/src/index.js +162 -665
  88. package/dist/wu-framework.hex.js +0 -23
  89. package/dist/wu-framework.min.js +0 -1
  90. package/dist/wu-framework.obf.js +0 -1
  91. package/scripts/build-protected.js +0 -366
  92. package/scripts/build.js +0 -212
  93. package/scripts/rollup-plugin-hex.js +0 -143
  94. package/src/core/wu-registry.js +0 -60
  95. 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
+ }