@adia-ai/a2ui-retrieval 0.4.4 → 0.4.6

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.
package/CHANGELOG.md CHANGED
@@ -7,6 +7,28 @@ Follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and
7
7
 
8
8
  _No pending changes._
9
9
 
10
+ ## [0.4.6] - 2026-05-12
11
+
12
+ ### Changed — `pattern-library.js` retired, consumers migrate to `composition-library` (§64 multi-step, 2026-05-12)
13
+
14
+ The legacy `pattern-library.js` retrieval surface is retired in v0.4.6. Compositions (`@adia-ai/a2ui-compose/strategies/zettel/composition-library.js`) are now the canonical retrieval source — the §41 unification of compositions + annotated chunks made `pattern-library.js` redundant. Six-step migration (commits `cd11939d` / `67d3075c` / `c2336895` / `7cd42ca4` / `9abcd8ce` / `a4814384`):
15
+
16
+ - **`pattern-library.js` retired** (§64 step 6). `reference.js` now delegates to `composition-library` for all reads; the file remains as a thin shim that re-exports the new path while emitting a one-shot deprecation warn for direct importers.
17
+ - **`component-catalog.js` carved out of `pattern-library`** (§64 step 1). Previously the catalog ride-along on the pattern surface; now lives at its own module path so consumers can drop `pattern-library` without losing the catalog. Re-exported from the package barrel.
18
+ - **`context-assembler.js` + `intent/clarity.js` migrated to `composition-library`** (§64 step 4). Both modules previously asked `pattern-library.search(intent)`; now ask `compositionLibrary.search(intent)`. Behavior preserved — the new lookup returns the same shape post-§41 normalization.
19
+ - **`authoring/index.js` + `authoring/pattern-promotion.js` + `authoring/synthetic-data.js` retired** (§64 step 2). All three were dead code by §63 (pattern-promotion fired on a surface that no consumer exercised; synthetic-data targeted the pattern shape). Files deleted; barrel re-exports cleaned.
20
+ - **`index.js`** — barrel updated to reflect the carved-out + retired modules.
21
+
22
+ See root [CHANGELOG.md `[Unreleased]`](../../../CHANGELOG.md) and `@adia-ai/a2ui-compose@[Unreleased]` for the cross-cutting arc narrative.
23
+
24
+ ## [0.4.5] - 2026-05-12
25
+
26
+ ### Ride-along (no source changes)
27
+
28
+ Lockstep PATCH cut alongside `@adia-ai/web-components@0.4.5` (§61 CSS subpath exports + CustomEvent.detail sweep across 17 form-bearing primitives + slider reactivity test lock-in + §52 design-tokens export module + UI), `@adia-ai/web-modules@0.4.5` (§61 CSS subpath exports — six cluster `*.css` exports), `@adia-ai/a2ui-compose@0.4.5` (§56 system-prompt CORPUS CONTEXT block in monolithic strategies), `@adia-ai/llm@0.4.5` (§54 doc-comment path refresh). Source byte-identical to v0.4.4.
29
+
30
+ Internal `@adia-ai/*` dep ranges stay at `^0.4.0` (patch-cut asymmetry — `^0.4.0` covers `0.4.x` under semver). See root [CHANGELOG.md `## [0.4.5]`](../../../CHANGELOG.md) for the cut narrative.
31
+
10
32
  ## [0.4.4] - 2026-05-12
11
33
 
12
34
  ### Ride-along (no source changes)
@@ -1,13 +1,9 @@
1
1
  /**
2
2
  * @adia-ai/a2ui-retrieval/authoring — corpus authoring helpers.
3
3
  *
4
- * Scripts and helpers that produce or extend retrieval inputs:
5
- * synthetic-data generation, web research, pattern promotion. These are
6
- * upstream of retrieval (they create what retrieval reads) — kept inside
7
- * the retrieval package because they share the catalog + pattern-library
8
- * surfaces, but logically a separate concern from lookup.
4
+ * Web-research helpers used by `compose/core/generator.js` to enrich
5
+ * intent before generation. Synthetic-data + pattern-promotion retired
6
+ * in §64 alongside `pattern-library.js`.
9
7
  */
10
8
 
11
- export * from './synthetic-data.js';
12
9
  export * from './web-research.js';
13
- export * from './pattern-promotion.js';
@@ -0,0 +1,111 @@
1
+ /**
2
+ * Component Catalog — Loads `.a2ui.json` sidecars from web-components/.
3
+ *
4
+ * The `.a2ui.json` sidecars are hand-authored component contracts that
5
+ * live alongside their components in `packages/web-components/components/`.
6
+ * They are part of the component library — NOT the training corpus — and
7
+ * are the canonical source of truth for component shape (v0.9 JSON Schema
8
+ * with `x-adiaui` extension for retrieval signals).
9
+ *
10
+ * §66 wired these into `buildSystemPrompt` as the authoritative component
11
+ * descriptor. §64 split this loader out of the now-retired
12
+ * `pattern-library.js` so it survives the patterns corpus deletion.
13
+ *
14
+ * Works in both Node.js (readdir/readFile) and the browser (Vite glob).
15
+ */
16
+
17
+ const IS_NODE =
18
+ typeof process !== 'undefined' &&
19
+ typeof process.versions?.node === 'string';
20
+
21
+ /** @type {Map<string, object>} */
22
+ const _componentData = new Map();
23
+
24
+ let _loaded = false;
25
+ let _loadPromise = null;
26
+
27
+ // Vite resolves this at build time; at runtime in Node the variable is unused.
28
+ let _globA2UIModules = null;
29
+ if (!IS_NODE) {
30
+ try {
31
+ _globA2UIModules = import.meta.glob('../../web-components/components/**/*.a2ui.json', {
32
+ query: '?raw',
33
+ import: 'default',
34
+ });
35
+ } catch {
36
+ // Not in a Vite context — no component data
37
+ }
38
+ }
39
+
40
+ async function _loadNode() {
41
+ const fs = await import(/* @vite-ignore */ 'node:fs/promises');
42
+ const path = await import(/* @vite-ignore */ 'node:path');
43
+ const url = await import(/* @vite-ignore */ 'node:url');
44
+
45
+ const __dirname = path.dirname(url.fileURLToPath(import.meta.url));
46
+ const componentsDir = path.resolve(__dirname, '..', '..', 'web-components', 'components');
47
+
48
+ let compEntries;
49
+ try {
50
+ compEntries = await fs.readdir(componentsDir, { withFileTypes: true });
51
+ } catch {
52
+ return; // components dir doesn't exist — skip
53
+ }
54
+
55
+ for (const compEntry of compEntries) {
56
+ if (!compEntry.isDirectory()) continue;
57
+ const a2uiPath = path.join(componentsDir, compEntry.name, `${compEntry.name}.a2ui.json`);
58
+ try {
59
+ const raw = await fs.readFile(a2uiPath, 'utf8');
60
+ const data = JSON.parse(raw);
61
+ if (data?.title) _componentData.set(data.title, data);
62
+ } catch {
63
+ // No .a2ui.json for this component — skip
64
+ }
65
+ }
66
+ }
67
+
68
+ async function _loadBrowser() {
69
+ if (!_globA2UIModules) return;
70
+
71
+ for (const [, loader] of Object.entries(_globA2UIModules)) {
72
+ try {
73
+ const raw = await loader();
74
+ const data = JSON.parse(raw);
75
+ if (data?.title) _componentData.set(data.title, data);
76
+ } catch {
77
+ // skip malformed .a2ui.json
78
+ }
79
+ }
80
+ }
81
+
82
+ export async function loadCatalog() {
83
+ if (_loaded) return;
84
+ if (_loadPromise) return _loadPromise;
85
+ _loadPromise = (async () => {
86
+ if (IS_NODE) await _loadNode();
87
+ else await _loadBrowser();
88
+ })();
89
+ await _loadPromise;
90
+ _loaded = true;
91
+ }
92
+
93
+ // Eager load on module import so the synchronous getters below are safe.
94
+ await loadCatalog();
95
+
96
+ /**
97
+ * Get `.a2ui.json` data for a named component.
98
+ * @param {string} name — PascalCase component name (e.g., "Swiper")
99
+ * @returns {object|null} raw .a2ui.json document, or null
100
+ */
101
+ export function getComponentData(name) {
102
+ return _componentData.get(name) || null;
103
+ }
104
+
105
+ /**
106
+ * Get all loaded `.a2ui.json` component data.
107
+ * @returns {Object.<string, object>} component name → .a2ui.json data
108
+ */
109
+ export function getAllComponentData() {
110
+ return Object.fromEntries(_componentData);
111
+ }
@@ -11,7 +11,11 @@ import { getCatalog, getComponentsByCategory } from './catalog.js';
11
11
  import { serializeEntry } from './component-entry.js';
12
12
  import { classifyIntent, getDomain } from './domain-router.js';
13
13
  import { getAntiPatterns } from './anti-patterns.js';
14
- import { searchPatterns, getAllPatterns } from './pattern-library.js';
14
+ import {
15
+ searchAll as searchCompositions,
16
+ getComposition,
17
+ getAllCompositions,
18
+ } from '../compose/strategies/zettel/composition-library.js';
15
19
 
16
20
  /** Token budget per tier */
17
21
  const TIER_BUDGETS = {
@@ -125,28 +129,30 @@ export async function assembleContext({ intent = '', tier = 1, domain: overrideD
125
129
  }
126
130
  }
127
131
 
128
- // ── Tier 3+: Add matching patterns ──
132
+ // ── Tier 3+: Add matching compositions ──
133
+ // §64 migrated from pattern-library to composition-library. Compositions
134
+ // are the canonical retrieval surface post-§65.
129
135
  if (tier >= 3) {
130
- const matchedPatterns = intent
131
- ? searchPatterns(intent)
132
- : getAllPatterns().filter(p => p.domain === domainName);
136
+ const matchedCompositions = intent
137
+ ? searchCompositions(intent).map((hit) => getComposition(hit.name)).filter(Boolean)
138
+ : getAllCompositions().filter((c) => c.domain === domainName);
133
139
 
134
- for (const pattern of matchedPatterns) {
135
- const cost = estimateTokens(pattern);
140
+ for (const composition of matchedCompositions) {
141
+ const cost = estimateTokens(composition);
136
142
  if (usedTokens + cost > budget) break;
137
- result.patterns.push(pattern);
143
+ result.patterns.push(composition);
138
144
  usedTokens += cost;
139
145
  }
140
146
  }
141
147
 
142
- // ── Tier 4: Full exploration — add remaining patterns ──
148
+ // ── Tier 4: Full exploration — add remaining compositions ──
143
149
  if (tier >= 4) {
144
- const addedPatterns = new Set(result.patterns.map(p => p.name));
145
- for (const pattern of getAllPatterns()) {
146
- if (addedPatterns.has(pattern.name)) continue;
147
- const cost = estimateTokens(pattern);
150
+ const added = new Set(result.patterns.map((c) => c.name));
151
+ for (const composition of getAllCompositions()) {
152
+ if (added.has(composition.name)) continue;
153
+ const cost = estimateTokens(composition);
148
154
  if (usedTokens + cost > budget) break;
149
- result.patterns.push(pattern);
155
+ result.patterns.push(composition);
150
156
  usedTokens += cost;
151
157
  }
152
158
  }
package/index.js CHANGED
@@ -10,7 +10,7 @@ export { isConversational } from './intent/intent-gate.js';
10
10
  export { assessClarity } from './intent/clarity.js';
11
11
  export { detectReferences, researchIntent } from './authoring/web-research.js';
12
12
  export { assembleContext } from './context-assembler.js';
13
- export { getPattern, searchPatterns, semanticSearchPatterns, getAllPatterns, registerPattern } from './pattern-library.js';
13
+ export { getComponentData, getAllComponentData } from './component-catalog.js';
14
14
  export { extractExpectations, verifyAlignment, checkIntentAlignment } from './intent/intent-alignment.js';
15
15
  export { decomposeIntent, composeSubtasks } from './intent/decomposer.js';
16
16
  export { getWiringCatalog, getControllerInfo, getHandlerInfo } from './wiring-catalog.js';
package/intent/clarity.js CHANGED
@@ -13,7 +13,7 @@
13
13
  */
14
14
 
15
15
  import { classifyIntent } from '../domain-router.js';
16
- import { searchPatterns } from '../pattern-library.js';
16
+ import { searchAll as searchCompositions } from '../../compose/strategies/zettel/composition-library.js';
17
17
 
18
18
  // ── Dimension detectors ──────────────────────────────────────────────────
19
19
 
@@ -81,7 +81,7 @@ export function assessClarity(intent, classification) {
81
81
  }
82
82
 
83
83
  const cls = classification || classifyIntent(text);
84
- const patterns = searchPatterns(text);
84
+ const compositionMatches = searchCompositions(text);
85
85
  const lower = text.toLowerCase();
86
86
  const wordCount = text.split(/\s+/).length;
87
87
 
@@ -115,8 +115,8 @@ export function assessClarity(intent, classification) {
115
115
  actionScore * 0.15
116
116
  );
117
117
 
118
- // ── Bonus: pattern match adds confidence ──
119
- const patternBonus = patterns.length > 0 ? 0.15 : 0;
118
+ // ── Bonus: composition match adds confidence ──
119
+ const patternBonus = compositionMatches.length > 0 ? 0.15 : 0;
120
120
  // Bonus: long intents are usually more specific
121
121
  const lengthBonus = wordCount > 10 ? 0.1 : wordCount > 6 ? 0.05 : 0;
122
122
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adia-ai/a2ui-retrieval",
3
- "version": "0.4.4",
3
+ "version": "0.4.6",
4
4
  "description": "AdiaUI A2UI retrieval layer — catalog lookup, intent classification, domain routing, pattern + anti-pattern matching, clarity + context assembly. Consumed by the compose engine and any A2UI-protocol tooling that needs to reason about user intent against the catalog.",
5
5
  "type": "module",
6
6
  "main": "./index.js",
@@ -1,135 +0,0 @@
1
- /**
2
- * Pattern Promotion — Automatically identifies generations that should
3
- * become reusable patterns based on quality signals and user feedback.
4
- *
5
- * Promotion criteria (all must be met):
6
- * - Generation score >= 95
7
- * - Feedback rating >= 4 (if feedback exists)
8
- * - Intent alignment score >= 0.8 (if checked)
9
- * - User didn't heavily edit the output
10
- * - Explicit shouldBePattern flag (from feedback) OR auto-detected
11
- *
12
- * Auto-detection: a generation is auto-promoted if it scores high on all
13
- * dimensions and uses a novel combination of components not already in
14
- * the pattern library.
15
- */
16
-
17
- import { getAllPatterns } from '../pattern-library.js';
18
-
19
- /**
20
- * Evaluate whether a generation should be promoted to a named pattern.
21
- *
22
- * @param {object} opts
23
- * @param {number} opts.score — Validation score (0-100)
24
- * @param {object} [opts.feedback] — User feedback { rating, userEdited }
25
- * @param {object} [opts.alignment] — Intent alignment { score }
26
- * @param {string[]} opts.componentTypes — Component types used in generation
27
- * @param {string} opts.intent — Original intent
28
- * @returns {{ shouldPromote: boolean, confidence: number, reason: string, suggestedName: string }}
29
- */
30
- export function evaluatePromotion({ score, feedback, alignment, componentTypes, intent }) {
31
- let confidence = 0;
32
- const reasons = [];
33
-
34
- // ── Hard gates ──
35
- if (score < 95) {
36
- return { shouldPromote: false, confidence: 0, reason: `Score ${score} < 95`, suggestedName: '' };
37
- }
38
-
39
- // Score contribution (0.3 weight)
40
- confidence += (score / 100) * 0.3;
41
- reasons.push(`score: ${score}/100`);
42
-
43
- // ── Feedback signals (0.3 weight) ──
44
- if (feedback) {
45
- if (feedback.rating >= 4) {
46
- confidence += (feedback.rating / 5) * 0.2;
47
- reasons.push(`rating: ${feedback.rating}/5`);
48
- } else if (feedback.rating > 0) {
49
- return { shouldPromote: false, confidence, reason: `Rating ${feedback.rating} < 4`, suggestedName: '' };
50
- }
51
-
52
- if (feedback.userEdited) {
53
- confidence -= 0.15;
54
- reasons.push('user edited (penalty)');
55
- }
56
-
57
- if (feedback.shouldBePattern) {
58
- confidence += 0.15;
59
- reasons.push('user flagged as pattern');
60
- }
61
- } else {
62
- // No feedback — partial confidence from score alone
63
- confidence += 0.1;
64
- }
65
-
66
- // ── Alignment signals (0.2 weight) ──
67
- if (alignment) {
68
- if (alignment.score >= 0.8) {
69
- confidence += alignment.score * 0.2;
70
- reasons.push(`alignment: ${Math.round(alignment.score * 100)}%`);
71
- } else {
72
- confidence -= 0.1;
73
- reasons.push(`low alignment: ${Math.round(alignment.score * 100)}%`);
74
- }
75
- }
76
-
77
- // ── Novelty bonus (0.2 weight) ──
78
- const novelty = assessNovelty(componentTypes);
79
- if (novelty > 0.5) {
80
- confidence += novelty * 0.2;
81
- reasons.push(`novelty: ${Math.round(novelty * 100)}%`);
82
- }
83
-
84
- // ── Decision ──
85
- const shouldPromote = confidence >= 0.5;
86
- const suggestedName = generatePatternName(intent);
87
-
88
- return {
89
- shouldPromote,
90
- confidence: Math.round(confidence * 100) / 100,
91
- reason: reasons.join(', '),
92
- suggestedName,
93
- };
94
- }
95
-
96
- /**
97
- * Assess how novel a component combination is compared to existing patterns.
98
- * Higher = more novel (not well-covered by existing patterns).
99
- *
100
- * @param {string[]} componentTypes
101
- * @returns {number} — 0 (duplicate) to 1 (completely novel)
102
- */
103
- function assessNovelty(componentTypes) {
104
- const existing = getAllPatterns();
105
- if (existing.length === 0) return 1;
106
-
107
- const typeSet = new Set(componentTypes);
108
- let bestOverlap = 0;
109
-
110
- for (const pattern of existing) {
111
- const patternTypes = new Set(pattern.components || []);
112
- const intersection = [...typeSet].filter(t => patternTypes.has(t)).length;
113
- const union = new Set([...typeSet, ...patternTypes]).size;
114
- const jaccard = union > 0 ? intersection / union : 0;
115
- bestOverlap = Math.max(bestOverlap, jaccard);
116
- }
117
-
118
- // Invert: high overlap = low novelty
119
- return 1 - bestOverlap;
120
- }
121
-
122
- /**
123
- * Generate a suggested pattern name from an intent string.
124
- *
125
- * @param {string} intent
126
- * @returns {string}
127
- */
128
- function generatePatternName(intent) {
129
- return (intent || 'unnamed')
130
- .toLowerCase()
131
- .replace(/\b(create|build|make|show|add|generate|a|an|the|with|and|for|of)\b/g, '')
132
- .replace(/[^a-z0-9]+/g, '-')
133
- .replace(/^-|-$/g, '')
134
- .slice(0, 40) || 'unnamed-pattern';
135
- }