wu-framework 1.1.15 → 1.1.17

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 (88) hide show
  1. package/README.md +52 -20
  2. package/dist/wu-framework.cjs.js +1 -1
  3. package/dist/wu-framework.cjs.js.map +1 -1
  4. package/dist/wu-framework.dev.js +15511 -15146
  5. package/dist/wu-framework.dev.js.map +1 -1
  6. package/dist/wu-framework.esm.js +1 -1
  7. package/dist/wu-framework.esm.js.map +1 -1
  8. package/dist/wu-framework.umd.js +1 -1
  9. package/dist/wu-framework.umd.js.map +1 -1
  10. package/package.json +166 -161
  11. package/src/adapters/angular/ai.js +30 -30
  12. package/src/adapters/angular/index.d.ts +154 -154
  13. package/src/adapters/angular/index.js +932 -932
  14. package/src/adapters/angular.d.ts +3 -3
  15. package/src/adapters/angular.js +3 -3
  16. package/src/adapters/index.js +168 -168
  17. package/src/adapters/lit/ai.js +20 -20
  18. package/src/adapters/lit/index.d.ts +120 -120
  19. package/src/adapters/lit/index.js +721 -721
  20. package/src/adapters/lit.d.ts +3 -3
  21. package/src/adapters/lit.js +3 -3
  22. package/src/adapters/preact/ai.js +33 -33
  23. package/src/adapters/preact/index.d.ts +108 -108
  24. package/src/adapters/preact/index.js +661 -661
  25. package/src/adapters/preact.d.ts +3 -3
  26. package/src/adapters/preact.js +3 -3
  27. package/src/adapters/react/index.js +48 -54
  28. package/src/adapters/react.d.ts +3 -3
  29. package/src/adapters/react.js +3 -3
  30. package/src/adapters/shared.js +64 -64
  31. package/src/adapters/solid/ai.js +32 -32
  32. package/src/adapters/solid/index.d.ts +101 -101
  33. package/src/adapters/solid/index.js +586 -586
  34. package/src/adapters/solid.d.ts +3 -3
  35. package/src/adapters/solid.js +3 -3
  36. package/src/adapters/svelte/ai.js +31 -31
  37. package/src/adapters/svelte/index.d.ts +166 -166
  38. package/src/adapters/svelte/index.js +798 -798
  39. package/src/adapters/svelte.d.ts +3 -3
  40. package/src/adapters/svelte.js +3 -3
  41. package/src/adapters/vanilla/ai.js +30 -30
  42. package/src/adapters/vanilla/index.d.ts +179 -179
  43. package/src/adapters/vanilla/index.js +785 -785
  44. package/src/adapters/vanilla.d.ts +3 -3
  45. package/src/adapters/vanilla.js +3 -3
  46. package/src/adapters/vue/ai.js +52 -52
  47. package/src/adapters/vue/index.d.ts +299 -299
  48. package/src/adapters/vue/index.js +610 -610
  49. package/src/adapters/vue.d.ts +3 -3
  50. package/src/adapters/vue.js +3 -3
  51. package/src/ai/wu-ai-actions.js +261 -261
  52. package/src/ai/wu-ai-agent.js +546 -546
  53. package/src/ai/wu-ai-browser-primitives.js +354 -354
  54. package/src/ai/wu-ai-browser.js +380 -380
  55. package/src/ai/wu-ai-context.js +332 -332
  56. package/src/ai/wu-ai-conversation.js +613 -613
  57. package/src/ai/wu-ai-orchestrate.js +1021 -1021
  58. package/src/ai/wu-ai-permissions.js +381 -381
  59. package/src/ai/wu-ai-provider.js +700 -700
  60. package/src/ai/wu-ai-schema.js +225 -225
  61. package/src/ai/wu-ai-triggers.js +396 -396
  62. package/src/ai/wu-ai.js +804 -804
  63. package/src/core/wu-app.js +236 -236
  64. package/src/core/wu-cache.js +498 -477
  65. package/src/core/wu-core.js +1412 -1398
  66. package/src/core/wu-error-boundary.js +396 -382
  67. package/src/core/wu-event-bus.js +390 -348
  68. package/src/core/wu-hooks.js +350 -350
  69. package/src/core/wu-html-parser.js +199 -190
  70. package/src/core/wu-iframe-sandbox.js +328 -328
  71. package/src/core/wu-loader.js +385 -273
  72. package/src/core/wu-logger.js +142 -134
  73. package/src/core/wu-manifest.js +532 -509
  74. package/src/core/wu-mcp-bridge.js +432 -432
  75. package/src/core/wu-overrides.js +510 -510
  76. package/src/core/wu-performance.js +228 -228
  77. package/src/core/wu-plugin.js +401 -348
  78. package/src/core/wu-prefetch.js +414 -414
  79. package/src/core/wu-proxy-sandbox.js +477 -476
  80. package/src/core/wu-sandbox.js +779 -779
  81. package/src/core/wu-script-executor.js +161 -113
  82. package/src/core/wu-snapshot-sandbox.js +227 -227
  83. package/src/core/wu-store.js +13 -3
  84. package/src/core/wu-strategies.js +256 -256
  85. package/src/core/wu-style-bridge.js +477 -477
  86. package/src/index.d.ts +317 -0
  87. package/src/index.js +234 -224
  88. package/src/utils/dependency-resolver.js +327 -327
@@ -1,332 +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
- }
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
+ }