wu-framework 1.1.14 → 1.1.16

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