@ggui-ai/negotiator 0.1.0-rc.1

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 (91) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +49 -0
  3. package/dist/contract-hash.d.ts +54 -0
  4. package/dist/contract-hash.d.ts.map +1 -0
  5. package/dist/contract-hash.js +96 -0
  6. package/dist/contract-validators.d.ts +171 -0
  7. package/dist/contract-validators.d.ts.map +1 -0
  8. package/dist/contract-validators.js +478 -0
  9. package/dist/decision-input.d.ts +48 -0
  10. package/dist/decision-input.d.ts.map +1 -0
  11. package/dist/decision-input.js +14 -0
  12. package/dist/decision.d.ts +54 -0
  13. package/dist/decision.d.ts.map +1 -0
  14. package/dist/decision.js +500 -0
  15. package/dist/index.d.ts +36 -0
  16. package/dist/index.d.ts.map +1 -0
  17. package/dist/index.js +25 -0
  18. package/dist/intent.d.ts +22 -0
  19. package/dist/intent.d.ts.map +1 -0
  20. package/dist/intent.js +28 -0
  21. package/dist/llm-caller.d.ts +70 -0
  22. package/dist/llm-caller.d.ts.map +1 -0
  23. package/dist/llm-caller.js +38 -0
  24. package/dist/llm-rerank.d.ts +101 -0
  25. package/dist/llm-rerank.d.ts.map +1 -0
  26. package/dist/llm-rerank.js +178 -0
  27. package/dist/negotiate.d.ts +141 -0
  28. package/dist/negotiate.d.ts.map +1 -0
  29. package/dist/negotiate.js +161 -0
  30. package/dist/normalize-schema.d.ts +22 -0
  31. package/dist/normalize-schema.d.ts.map +1 -0
  32. package/dist/normalize-schema.js +191 -0
  33. package/dist/pure.d.ts +30 -0
  34. package/dist/pure.d.ts.map +1 -0
  35. package/dist/pure.js +43 -0
  36. package/dist/rag-search.d.ts +73 -0
  37. package/dist/rag-search.d.ts.map +1 -0
  38. package/dist/rag-search.js +192 -0
  39. package/dist/rerank-eval/pairs.d.ts +28 -0
  40. package/dist/rerank-eval/pairs.d.ts.map +1 -0
  41. package/dist/rerank-eval/pairs.js +531 -0
  42. package/dist/rerank-eval/run-probe-cli.d.ts +3 -0
  43. package/dist/rerank-eval/run-probe-cli.d.ts.map +1 -0
  44. package/dist/rerank-eval/run-probe-cli.js +146 -0
  45. package/dist/rerank-eval/run-probe.d.ts +68 -0
  46. package/dist/rerank-eval/run-probe.d.ts.map +1 -0
  47. package/dist/rerank-eval/run-probe.js +113 -0
  48. package/dist/session.d.ts +42 -0
  49. package/dist/session.d.ts.map +1 -0
  50. package/dist/session.js +21 -0
  51. package/dist/suggestion.d.ts +38 -0
  52. package/dist/suggestion.d.ts.map +1 -0
  53. package/dist/suggestion.js +47 -0
  54. package/dist/synth-bench/corpus.d.ts +106 -0
  55. package/dist/synth-bench/corpus.d.ts.map +1 -0
  56. package/dist/synth-bench/corpus.js +994 -0
  57. package/dist/synth-bench/run-bench-cli.d.ts +3 -0
  58. package/dist/synth-bench/run-bench-cli.d.ts.map +1 -0
  59. package/dist/synth-bench/run-bench-cli.js +181 -0
  60. package/dist/synth-bench/run-bench.d.ts +101 -0
  61. package/dist/synth-bench/run-bench.d.ts.map +1 -0
  62. package/dist/synth-bench/run-bench.js +374 -0
  63. package/dist/synthesize-contract.d.ts +131 -0
  64. package/dist/synthesize-contract.d.ts.map +1 -0
  65. package/dist/synthesize-contract.js +948 -0
  66. package/dist/types.d.ts +30 -0
  67. package/dist/types.d.ts.map +1 -0
  68. package/dist/types.js +13 -0
  69. package/package.json +74 -0
  70. package/src/contract-hash.ts +102 -0
  71. package/src/contract-validators.ts +604 -0
  72. package/src/decision-input.ts +49 -0
  73. package/src/decision.ts +581 -0
  74. package/src/index.ts +63 -0
  75. package/src/intent.ts +37 -0
  76. package/src/llm-caller.ts +82 -0
  77. package/src/llm-rerank.ts +280 -0
  78. package/src/negotiate.ts +312 -0
  79. package/src/normalize-schema.ts +193 -0
  80. package/src/pure.ts +46 -0
  81. package/src/rag-search.ts +274 -0
  82. package/src/rerank-eval/pairs.ts +624 -0
  83. package/src/rerank-eval/run-probe-cli.ts +197 -0
  84. package/src/rerank-eval/run-probe.ts +198 -0
  85. package/src/session.ts +41 -0
  86. package/src/suggestion.ts +73 -0
  87. package/src/synth-bench/corpus.ts +1126 -0
  88. package/src/synth-bench/run-bench-cli.ts +237 -0
  89. package/src/synth-bench/run-bench.ts +525 -0
  90. package/src/synthesize-contract.ts +1161 -0
  91. package/src/types.ts +31 -0
@@ -0,0 +1,192 @@
1
+ /**
2
+ * RAG Search — embedding-similarity blueprint retrieval over the
3
+ * public `@ggui-ai/mcp-server-core` storage seam.
4
+ *
5
+ * Given agent `prompt` + a tenant `scope` (typically appId, or the
6
+ * literal `"shared"` for the global catalog), this helper:
7
+ *
8
+ * 1. Embeds the prompt via {@link EmbeddingProvider.embed}.
9
+ * 2. Queries the {@link VectorStore} for the top-K nearest neighbors
10
+ * (server-side cosine, scope-partitioned — no cross-tenant leak).
11
+ * 3. Filters by minimum score, biases private (per-app registered
12
+ * UIs) over shared (generated pool) at the same score band, and
13
+ * projects each hit into a {@link NegotiatorOption}.
14
+ *
15
+ * Two pipeline paths emerge from the confidence band:
16
+ * - **High confidence** (`score >= HIGH_CONFIDENCE_THRESHOLD`): the
17
+ * hit is marked `exact`. Upstream callers (V3 `negotiate()`) can
18
+ * short-circuit the decision LLM entirely.
19
+ * - **Medium confidence** (`score >= RETRIEVAL_MIN_SCORE` and below
20
+ * the exact threshold): marked `partial`. Upstream passes these to
21
+ * the decision LLM as candidates.
22
+ *
23
+ * ### Public-seam contract
24
+ *
25
+ * This function reads only the public `VectorStore` surface (scalar
26
+ * metadata). Consumers that still hold the rich `EmbeddingStorage`
27
+ * shape bridge via
28
+ * `@ggui-cloud/aws-adapters.embeddingStorageToVectorStore`, which
29
+ * encodes array-valued fields (`props`, `callbacks`, `sourceTools`)
30
+ * as JSON strings inside `metadata`. Any `VectorStore` implementation
31
+ * that stores fresh writes through `writeRagVector` (see
32
+ * `mcp-servers/ggui-protocol/src/adapters/vector-store.ts`) uses the
33
+ * same encoding — so existing caches, fresh writes, and community
34
+ * `VectorStore` backends all collide on the same retrieval key.
35
+ *
36
+ * ### Scope
37
+ *
38
+ * No AWS bindings, no LLM dependency. Pure composition over the two
39
+ * public seams. The one-LLM-call `makeDecision()` step is separate.
40
+ */
41
+ import { inferJsonSchemaType } from './pure.js';
42
+ /** Minimum cosine similarity to be considered a retrieval candidate. */
43
+ const RETRIEVAL_MIN_SCORE = 0.15;
44
+ /** Cosine similarity above this → exact match (skip LLM). */
45
+ const HIGH_CONFIDENCE_THRESHOLD = 0.45;
46
+ /** Default top-K for the k-NN query. */
47
+ const DEFAULT_MAX_CANDIDATES = 10;
48
+ /**
49
+ * Search the vector index for blueprints matching `prompt` within
50
+ * `scope`. See the module docstring for the confidence-band pipeline
51
+ * and encoding contract.
52
+ */
53
+ export async function ragSearch(deps, input) {
54
+ const topK = input.maxCandidates ?? DEFAULT_MAX_CANDIDATES;
55
+ // Stage 1: embed the query text
56
+ const embedStart = Date.now();
57
+ const queryEmbedding = await deps.embedding.embed(input.prompt);
58
+ const embeddingLatencyMs = Date.now() - embedStart;
59
+ // Stage 2: nearest-neighbor query
60
+ const searchStart = Date.now();
61
+ const results = await deps.vectors.query(input.scope, queryEmbedding, topK);
62
+ const searchLatencyMs = Date.now() - searchStart;
63
+ // Filter + sort: private (registered UIs) always beats shared (generated)
64
+ // at the same score level; then descending score.
65
+ const candidates = results
66
+ .filter((r) => r.score >= RETRIEVAL_MIN_SCORE)
67
+ .sort((a, b) => {
68
+ const aPrivate = readPoolSource(a.metadata) === 'private' ? 1 : 0;
69
+ const bPrivate = readPoolSource(b.metadata) === 'private' ? 1 : 0;
70
+ if (aPrivate !== bPrivate)
71
+ return bPrivate - aPrivate;
72
+ return b.score - a.score;
73
+ });
74
+ if (candidates.length === 0) {
75
+ return { options: [], embeddingLatencyMs, searchLatencyMs };
76
+ }
77
+ const seenContracts = new Set();
78
+ const options = [];
79
+ for (const match of candidates) {
80
+ const option = buildOption(match);
81
+ if (!option)
82
+ continue;
83
+ if (seenContracts.has(option.contractKey))
84
+ continue;
85
+ seenContracts.add(option.contractKey);
86
+ options.push(option.value);
87
+ }
88
+ return { options, embeddingLatencyMs, searchLatencyMs };
89
+ }
90
+ /**
91
+ * Project a public {@link VectorSearchResult} hit into a
92
+ * {@link NegotiatorOption}, together with the dedup key used to
93
+ * collapse hits that describe the same semantic contract.
94
+ */
95
+ function buildOption(match) {
96
+ const isExact = match.score >= HIGH_CONFIDENCE_THRESHOLD;
97
+ const verdict = isExact ? 'exact' : 'partial';
98
+ const blueprintHash = match.key;
99
+ // Registered (private) blueprints already carry a `p_` prefix in
100
+ // their hash; generated pool entries do not — prefix them as `c_`
101
+ // to match the legacy option ID format that downstream consumers
102
+ // (decision prompt, cache layer) have been reading since V2.
103
+ const blueprintId = blueprintHash.startsWith('p_')
104
+ ? blueprintHash
105
+ : `c_${blueprintHash}`;
106
+ const prompt = readString(match.metadata.prompt, '');
107
+ const intent = readString(match.metadata.intent, '');
108
+ const category = readString(match.metadata.category, '');
109
+ const contractHash = readString(match.metadata.contractHash, '');
110
+ const poolSource = readPoolSource(match.metadata);
111
+ const featured = Boolean(match.metadata.featured);
112
+ const props = readProps(match.metadata.props);
113
+ const contract = buildContract({ prompt, intent, category, props });
114
+ // Dedup: prefer matching on semantic intent; fall back to prop
115
+ // signature so two blueprints with identical intents but different
116
+ // prop shapes don't collapse together.
117
+ const contractKey = intent !== ''
118
+ ? intent
119
+ : JSON.stringify(Object.keys(contract.propsSpec?.properties ?? {}).sort());
120
+ const option = {
121
+ id: `rag_${blueprintId.slice(-8)}`,
122
+ type: 'blueprint',
123
+ blueprintId,
124
+ pattern: category,
125
+ description: `${prompt} (similarity: ${Math.round(match.score * 100)}%, ${verdict})`,
126
+ pros: [
127
+ isExact
128
+ ? 'Exact blueprint match — instant render'
129
+ : 'Semantically matched to your request',
130
+ ...(featured ? ['Featured blueprint — curated quality'] : []),
131
+ ],
132
+ cons: [
133
+ ...(isExact ? [] : ['Partial match — may need adjustments']),
134
+ ...(!isExact ? ['Fixed layout — limited customization'] : []),
135
+ ],
136
+ renderTime: 'instant',
137
+ contract,
138
+ ...(contractHash !== '' ? { contractHash } : {}),
139
+ ...(poolSource !== undefined ? { poolSource } : {}),
140
+ };
141
+ return { value: option, contractKey };
142
+ }
143
+ function buildContract(args) {
144
+ // `intent` is not a contract field. The `prompt` and `intent` fields
145
+ // stay on the args because callers still use them for RAG keys +
146
+ // dedup; the returned contract carries structural shape only.
147
+ const { props } = args;
148
+ return {
149
+ propsSpec: {
150
+ properties: Object.fromEntries(props.map((p) => [
151
+ p.name,
152
+ {
153
+ description: p.description,
154
+ schema: { type: inferJsonSchemaType(p.type) },
155
+ required: p.required,
156
+ ...(p.example !== undefined
157
+ ? { example: p.example }
158
+ : {}),
159
+ },
160
+ ])),
161
+ },
162
+ };
163
+ }
164
+ function readString(value, fallback) {
165
+ return typeof value === 'string' ? value : fallback;
166
+ }
167
+ function readPoolSource(metadata) {
168
+ const v = metadata.poolSource;
169
+ return v === 'shared' || v === 'private' ? v : undefined;
170
+ }
171
+ function readProps(value) {
172
+ if (typeof value !== 'string' || value.length === 0)
173
+ return [];
174
+ try {
175
+ const parsed = JSON.parse(value);
176
+ if (!Array.isArray(parsed))
177
+ return [];
178
+ return parsed.filter(isPropSpec);
179
+ }
180
+ catch {
181
+ return [];
182
+ }
183
+ }
184
+ function isPropSpec(value) {
185
+ if (value === null || typeof value !== 'object')
186
+ return false;
187
+ const v = value;
188
+ return (typeof v.name === 'string' &&
189
+ typeof v.type === 'string' &&
190
+ typeof v.required === 'boolean' &&
191
+ typeof v.description === 'string');
192
+ }
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Eval pairs for the rerank quality probe.
3
+ *
4
+ * 25 hand-built pairs. Each pair has:
5
+ * - A user query (intent + contract summary)
6
+ * - 3-5 candidates (one or zero of which is the gold match)
7
+ * - The gold-standard label: `goldMatchId` or `null` for no-match
8
+ * - `kind`: 'should-match' | 'no-match' | 'adversarial'
9
+ *
10
+ * Adversarial pairs (n=10) are structurally identical to a candidate
11
+ * but differ in load-bearing intent — the judge MUST reject them. If
12
+ * the judge accepts adversarials, the adversarial false-positive rate
13
+ * exceeds its quality gate.
14
+ *
15
+ * Eval-only — not exported from the package index.
16
+ */
17
+ import type { RerankCandidate, RerankQuery } from '../llm-rerank.js';
18
+ export interface EvalPair {
19
+ readonly id: string;
20
+ readonly kind: 'should-match' | 'no-match' | 'adversarial';
21
+ readonly query: RerankQuery;
22
+ readonly candidates: readonly RerankCandidate[];
23
+ readonly goldMatchId: string | null;
24
+ readonly note?: string;
25
+ }
26
+ export declare const EVAL_PAIRS: readonly EvalPair[];
27
+ export declare function pairsByKind(kind: EvalPair['kind']): readonly EvalPair[];
28
+ //# sourceMappingURL=pairs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pairs.d.ts","sourceRoot":"","sources":["../../src/rerank-eval/pairs.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,KAAK,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAErE,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,cAAc,GAAG,UAAU,GAAG,aAAa,CAAC;IAC3D,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC;IAC5B,QAAQ,CAAC,UAAU,EAAE,SAAS,eAAe,EAAE,CAAC;IAChD,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,eAAO,MAAM,UAAU,EAAE,SAAS,QAAQ,EAglBzC,CAAC;AAEF,wBAAgB,WAAW,CAAC,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,GAAG,SAAS,QAAQ,EAAE,CAEvE"}