@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,161 @@
1
+ /**
2
+ * Negotiator — top-level orchestrator over the public storage +
3
+ * decision-engine seams.
4
+ *
5
+ * Pipeline:
6
+ * 1. RAG search (per-scope + optional shared pool) via
7
+ * {@link ragSearch} — composes `EmbeddingProvider.embed` +
8
+ * `VectorStore.query` from `@ggui-ai/mcp-server-core`.
9
+ * 2. Read session state (optional injectable).
10
+ * 3. Fast-path for exact blueprint hits — skip the decision LLM.
11
+ * 4. Otherwise call {@link makeDecision} with the RAG candidates and
12
+ * session stack; fold the picked blueprint's pool provenance into
13
+ * the return value.
14
+ *
15
+ * Timing logs (stable format — consumed by benchmarks):
16
+ * [negotiate] embedding: Xms | search: Xms | candidates: N
17
+ * [negotiate] candidate: <id8> | <description> | hash=<h16>
18
+ * [negotiate] FAST PATH: blueprint=<id8> hash=<h12> | LLM decision: 0ms | total: Xms
19
+ * [negotiate] LLM PATH: action=X blueprint=<id8> hash=<h12> | LLM decision: Xms | total: Xms
20
+ *
21
+ * ### Public surface + semver weight
22
+ *
23
+ * Exported:
24
+ * - `negotiate(deps, input)` — runtime orchestrator.
25
+ * - `NegotiateDeps` — injection shape (embedding / vectors / llm +
26
+ * optional session-state reader + optional progress callback).
27
+ * - `NegotiateInput` — agent signal + config.
28
+ * - `NegotiateConfig` — minimum fields the orchestrator actually
29
+ * reads. Pool selection is expressed as `includeSharedPool:
30
+ * boolean` rather than a `poolMode` enum, so callers decide
31
+ * shared-pool inclusion explicitly at each call site.
32
+ * - `NegotiateResult` — the decision result returned to callers.
33
+ *
34
+ * ### Why this package, not `mcp-server-core`
35
+ *
36
+ * `mcp-server-core` locks storage/runtime seams MCP server
37
+ * implementers bind against (`EmbeddingProvider`, `VectorStore`,
38
+ * `Negotiator`). `negotiate()` is a *composition* over those seams —
39
+ * decision-engine semantics, not a new seam. Adding it to
40
+ * `mcp-server-core` would drag the LLM prompts + tool-schemas into a
41
+ * package whose job is to stay minimal and runtime-agnostic.
42
+ */
43
+ import { ragSearch } from './rag-search.js';
44
+ import { makeDecision } from './decision.js';
45
+ /** Empty session state for cold starts and benchmarks. */
46
+ const EMPTY_SESSION = {
47
+ stack: [],
48
+ conversationHistory: [],
49
+ };
50
+ /**
51
+ * Orchestrate one negotiation call — RAG search, session read, fast
52
+ * path, decision LLM. See module docstring for the pipeline outline.
53
+ */
54
+ export async function negotiate(deps, input) {
55
+ const { agent, config } = input;
56
+ const negotiateStart = Date.now();
57
+ deps.onProgress?.('negotiating', 'Analyzing request...');
58
+ // Step 1: RAG search (per-app + optional shared pool in parallel).
59
+ // `embedding` + `vectors` are optional. When either is missing,
60
+ // ragSearch is a no-op (returns empty options + 0 latency); the
61
+ // decision LLM runs against zero candidates and falls back to
62
+ // "create" via its standard branching. OSS without RAG infrastructure
63
+ // gets useful negotiation from the decision LLM alone.
64
+ const queryText = agent.prompt ?? JSON.stringify(agent.data ?? {});
65
+ const emptyResult = { options: [], embeddingLatencyMs: 0, searchLatencyMs: 0 };
66
+ const ragDeps = deps.embedding !== undefined && deps.vectors !== undefined
67
+ ? { embedding: deps.embedding, vectors: deps.vectors }
68
+ : undefined;
69
+ const [appResult, sharedResult] = await Promise.all([
70
+ ragDeps
71
+ ? ragSearch(ragDeps, { prompt: queryText, scope: config.appId })
72
+ : Promise.resolve(emptyResult),
73
+ ragDeps && config.includeSharedPool
74
+ ? ragSearch(ragDeps, { prompt: queryText, scope: 'shared' })
75
+ : Promise.resolve(emptyResult),
76
+ ]);
77
+ const ragResult = {
78
+ options: [...appResult.options, ...sharedResult.options],
79
+ embeddingLatencyMs: Math.max(appResult.embeddingLatencyMs, sharedResult.embeddingLatencyMs),
80
+ searchLatencyMs: Math.max(appResult.searchLatencyMs, sharedResult.searchLatencyMs),
81
+ };
82
+ // eslint-disable-next-line no-console
83
+ console.log(`[negotiate] embedding: ${ragResult.embeddingLatencyMs}ms | search: ${ragResult.searchLatencyMs}ms | candidates: ${ragResult.options.length}`);
84
+ for (const opt of ragResult.options) {
85
+ // eslint-disable-next-line no-console
86
+ console.log(`[negotiate] candidate: ${opt.blueprintId?.slice(-8) ?? 'none'} | ${opt.description.slice(0, 80)} | hash=${opt.contractHash?.slice(0, 16) ?? 'none'}`);
87
+ }
88
+ deps.onProgress?.('blueprint_search', ragResult.options.length > 0
89
+ ? `Found ${ragResult.options.length} blueprint candidate${ragResult.options.length > 1 ? 's' : ''}`
90
+ : 'No blueprints found');
91
+ // Step 2: Read session state (falls back to EMPTY_SESSION).
92
+ const sessionState = deps.readSessionState
93
+ ? ((await deps.readSessionState(config.sessionId)) ?? EMPTY_SESSION)
94
+ : EMPTY_SESSION;
95
+ // Step 3: Fast-path for high-confidence exact matches — skip decision LLM.
96
+ const exactOpt = ragResult.options.find((opt) => opt.description.includes('exact'));
97
+ if (exactOpt) {
98
+ const stackHasSameType = sessionState.stack.some((item) => item.prompt && agent.prompt && item.prompt === agent.prompt);
99
+ const action = stackHasSameType ? 'update' : 'create';
100
+ const totalMs = Date.now() - negotiateStart;
101
+ // eslint-disable-next-line no-console
102
+ console.log(`[negotiate] FAST PATH: blueprint=${exactOpt.blueprintId?.slice(-8)} hash=${exactOpt.contractHash?.slice(0, 12) ?? 'none'} | LLM decision: 0ms | total: ${totalMs}ms`);
103
+ deps.onProgress?.('deciding', 'Exact match found — fast path');
104
+ // The agent's prompt is the outer-pipeline intent (`intent` is not
105
+ // a contract field). Fallback contract is the empty contract — the
106
+ // four-spec surface is omitted entirely (no props, no actions, no
107
+ // streams, no context).
108
+ const fallbackContract = {};
109
+ return {
110
+ decision: {
111
+ action,
112
+ reasoning: `Exact blueprint match (high confidence). ${action === 'update' ? 'Updating existing view.' : 'Creating new view.'}`,
113
+ blueprintId: exactOpt.blueprintId,
114
+ contract: exactOpt.contract ?? fallbackContract,
115
+ },
116
+ alternatives: [],
117
+ storedContractHash: exactOpt.contractHash,
118
+ storedPoolSource: exactOpt.poolSource,
119
+ embeddingLatencyMs: ragResult.embeddingLatencyMs,
120
+ searchLatencyMs: ragResult.searchLatencyMs,
121
+ decisionLatencyMs: 0,
122
+ };
123
+ }
124
+ // Step 4: Build decision input + call the LLM.
125
+ const decisionInput = {
126
+ agentData: agent.data,
127
+ agentPrompt: agent.prompt,
128
+ agentContext: agent.context,
129
+ agentTools: agent.agentTools,
130
+ ...(agent.gadgets
131
+ ? { gadgets: agent.gadgets }
132
+ : {}),
133
+ sessionState,
134
+ blueprintCandidates: ragResult.options.map((opt) => ({
135
+ blueprintId: opt.blueprintId ?? opt.id,
136
+ description: opt.description,
137
+ contract: opt.contract,
138
+ similarity: parseFloat(opt.description.match(/similarity: (\d+)%/)?.[1] ?? '0') / 100,
139
+ verdict: (opt.description.includes('exact') ? 'exact' : 'partial'),
140
+ })),
141
+ };
142
+ deps.onProgress?.('deciding', 'Choosing the best UI approach...');
143
+ const decisionStart = Date.now();
144
+ const { decision, alternatives } = await makeDecision(decisionInput, deps.llm);
145
+ const decisionLatencyMs = Date.now() - decisionStart;
146
+ const totalMs = Date.now() - negotiateStart;
147
+ const pickedBlueprint = decision.blueprintId
148
+ ? ragResult.options.find((opt) => (opt.blueprintId ?? opt.id) === decision.blueprintId)
149
+ : undefined;
150
+ // eslint-disable-next-line no-console
151
+ console.log(`[negotiate] LLM PATH: action=${decision.action} blueprint=${decision.blueprintId?.slice(-8) ?? 'none'} hash=${pickedBlueprint?.contractHash?.slice(0, 12) ?? 'none'} | LLM decision: ${decisionLatencyMs}ms | total: ${totalMs}ms`);
152
+ return {
153
+ decision,
154
+ alternatives,
155
+ storedContractHash: pickedBlueprint?.contractHash,
156
+ storedPoolSource: pickedBlueprint?.poolSource,
157
+ embeddingLatencyMs: ragResult.embeddingLatencyMs,
158
+ searchLatencyMs: ragResult.searchLatencyMs,
159
+ decisionLatencyMs,
160
+ };
161
+ }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Deterministic repair of the invalid JSON Schema `type` spellings an
3
+ * LLM occasionally emits in a synthesized contract. Runs inside
4
+ * `buildContract` (before the `dataContractSchema` validation gate) so
5
+ * the dominant synth-decline class — a `type` value outside the seven
6
+ * JSON Schema primitives — is structurally eliminated rather than left
7
+ * to a repair retry.
8
+ *
9
+ * Pure, recursive, total: a schema that is already valid passes
10
+ * through unchanged — every mapping fires only on a value NOT in the
11
+ * valid set. Recursion descends only into sub-schema positions, so
12
+ * data-value positions (`default`, `const`, `enum`, `examples`) are
13
+ * left verbatim — a `default` of `{type: "list"}` is user data, not a
14
+ * schema, and must not be rewritten.
15
+ */
16
+ /**
17
+ * Recursively normalize a JSON Schema, repairing invalid `type`
18
+ * values. Non-object input (booleans, primitives) passes through —
19
+ * `additionalProperties: false` and the like are valid as-is.
20
+ */
21
+ export declare function normalizeSchema(schema: unknown): unknown;
22
+ //# sourceMappingURL=normalize-schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"normalize-schema.d.ts","sourceRoot":"","sources":["../src/normalize-schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAkJH;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CA2BxD"}
@@ -0,0 +1,191 @@
1
+ /**
2
+ * Deterministic repair of the invalid JSON Schema `type` spellings an
3
+ * LLM occasionally emits in a synthesized contract. Runs inside
4
+ * `buildContract` (before the `dataContractSchema` validation gate) so
5
+ * the dominant synth-decline class — a `type` value outside the seven
6
+ * JSON Schema primitives — is structurally eliminated rather than left
7
+ * to a repair retry.
8
+ *
9
+ * Pure, recursive, total: a schema that is already valid passes
10
+ * through unchanged — every mapping fires only on a value NOT in the
11
+ * valid set. Recursion descends only into sub-schema positions, so
12
+ * data-value positions (`default`, `const`, `enum`, `examples`) are
13
+ * left verbatim — a `default` of `{type: "list"}` is user data, not a
14
+ * schema, and must not be rewritten.
15
+ */
16
+ /** The seven JSON Schema primitive type names. */
17
+ const VALID_TYPES = new Set([
18
+ 'string',
19
+ 'number',
20
+ 'integer',
21
+ 'boolean',
22
+ 'array',
23
+ 'object',
24
+ 'null',
25
+ ]);
26
+ /** Unambiguous non-canonical type spellings → canonical primitive. */
27
+ const TYPE_ALIASES = {
28
+ list: 'array',
29
+ tuple: 'array',
30
+ sequence: 'array',
31
+ dict: 'object',
32
+ map: 'object',
33
+ record: 'object',
34
+ hash: 'object',
35
+ str: 'string',
36
+ text: 'string',
37
+ char: 'string',
38
+ int: 'integer',
39
+ long: 'integer',
40
+ short: 'integer',
41
+ float: 'number',
42
+ double: 'number',
43
+ decimal: 'number',
44
+ num: 'number',
45
+ numeric: 'number',
46
+ bool: 'boolean',
47
+ };
48
+ /** Type words that mean "no constraint" — the `type` key is dropped. */
49
+ const DROP_TYPES = new Set(['any', 'unknown', 'mixed', 'void']);
50
+ /** Object keys whose value is itself a single schema. */
51
+ const SCHEMA_VALUED_KEYS = new Set([
52
+ 'items',
53
+ 'additionalProperties',
54
+ 'not',
55
+ 'contains',
56
+ 'propertyNames',
57
+ ]);
58
+ /** Object keys whose value is a map of name → schema. */
59
+ const SCHEMA_MAP_KEYS = new Set([
60
+ 'properties',
61
+ 'patternProperties',
62
+ '$defs',
63
+ 'definitions',
64
+ ]);
65
+ /** Object keys whose value is an array of schemas. */
66
+ const SCHEMA_ARRAY_KEYS = new Set([
67
+ 'allOf',
68
+ 'anyOf',
69
+ 'oneOf',
70
+ 'prefixItems',
71
+ ]);
72
+ /**
73
+ * Infer the base type of an `enum`-constrained field from its value
74
+ * list. Mixed / empty / non-array → `string` (the safe default, and
75
+ * the overwhelmingly common enum shape).
76
+ */
77
+ function inferEnumBaseType(enumSibling) {
78
+ if (!Array.isArray(enumSibling) || enumSibling.length === 0) {
79
+ return 'string';
80
+ }
81
+ if (enumSibling.every((v) => typeof v === 'string'))
82
+ return 'string';
83
+ if (enumSibling.every((v) => typeof v === 'number'))
84
+ return 'number';
85
+ if (enumSibling.every((v) => typeof v === 'boolean'))
86
+ return 'boolean';
87
+ return 'string';
88
+ }
89
+ /**
90
+ * Map one `type` value to a canonical primitive, or `undefined` when
91
+ * the `type` key should be dropped entirely (an absent `type` is valid
92
+ * JSON Schema — it simply imposes no constraint).
93
+ *
94
+ * `enumSibling` is the schema's `enum` array, used to pick the base
95
+ * type when the LLM wrote the (invalid) `type: "enum"`.
96
+ */
97
+ function normalizeTypeValue(value, enumSibling) {
98
+ if (typeof value === 'string') {
99
+ const lower = value.toLowerCase();
100
+ if (VALID_TYPES.has(lower))
101
+ return lower;
102
+ if (lower === 'enum')
103
+ return inferEnumBaseType(enumSibling);
104
+ if (DROP_TYPES.has(lower))
105
+ return undefined;
106
+ const alias = TYPE_ALIASES[lower];
107
+ if (alias !== undefined)
108
+ return alias;
109
+ // Unrecognized garbage — drop the constraint rather than guess.
110
+ return undefined;
111
+ }
112
+ if (Array.isArray(value)) {
113
+ // Union type (`["string", "null"]`). The protocol contract schema
114
+ // accepts only a single string; recover the first valid non-null
115
+ // member, dropping the nullable arm.
116
+ for (const member of value) {
117
+ if (typeof member === 'string') {
118
+ const norm = normalizeTypeValue(member, enumSibling);
119
+ if (norm !== undefined && norm !== 'null')
120
+ return norm;
121
+ }
122
+ }
123
+ return 'string';
124
+ }
125
+ // `type` was a number / object / boolean — meaningless; drop it.
126
+ return undefined;
127
+ }
128
+ /** Normalize a map of name → schema (e.g. `properties`). */
129
+ function normalizeSchemaMap(value) {
130
+ if (typeof value !== 'object' || value === null || Array.isArray(value)) {
131
+ return value;
132
+ }
133
+ const out = {};
134
+ for (const [name, sub] of Object.entries(value)) {
135
+ out[name] = coerceToSchema(sub);
136
+ }
137
+ return out;
138
+ }
139
+ /**
140
+ * Coerce a value found in a schema-expecting position into a schema
141
+ * object. An LLM sometimes writes the shorthand `items: "number"`
142
+ * where JSON Schema requires `items: {type: "number"}` — expand it
143
+ * (`"number"` → `{type: "number"}`, an unrecognized string → `{}`,
144
+ * the unconstrained schema). A boolean (`additionalProperties: false`)
145
+ * is a valid schema-position value and passes through; anything else
146
+ * recurses through {@link normalizeSchema}.
147
+ */
148
+ function coerceToSchema(value) {
149
+ if (typeof value === 'string') {
150
+ const t = normalizeTypeValue(value, undefined);
151
+ return t === undefined ? {} : { type: t };
152
+ }
153
+ return normalizeSchema(value);
154
+ }
155
+ /**
156
+ * Recursively normalize a JSON Schema, repairing invalid `type`
157
+ * values. Non-object input (booleans, primitives) passes through —
158
+ * `additionalProperties: false` and the like are valid as-is.
159
+ */
160
+ export function normalizeSchema(schema) {
161
+ if (typeof schema !== 'object' || schema === null || Array.isArray(schema)) {
162
+ return schema;
163
+ }
164
+ const obj = schema;
165
+ const out = {};
166
+ for (const [key, value] of Object.entries(obj)) {
167
+ if (key === 'type') {
168
+ const fixed = normalizeTypeValue(value, obj['enum']);
169
+ if (fixed !== undefined)
170
+ out[key] = fixed;
171
+ continue;
172
+ }
173
+ if (SCHEMA_VALUED_KEYS.has(key)) {
174
+ out[key] = coerceToSchema(value);
175
+ }
176
+ else if (SCHEMA_MAP_KEYS.has(key)) {
177
+ out[key] = normalizeSchemaMap(value);
178
+ }
179
+ else if (SCHEMA_ARRAY_KEYS.has(key)) {
180
+ out[key] = Array.isArray(value)
181
+ ? value.map((s) => coerceToSchema(s))
182
+ : value;
183
+ }
184
+ else {
185
+ // Data-value position (`default`, `const`, `enum`, `examples`,
186
+ // `required`, `description`, …) — leave verbatim.
187
+ out[key] = value;
188
+ }
189
+ }
190
+ return out;
191
+ }
package/dist/pure.d.ts ADDED
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Pure helpers — zero I/O, zero AWS dependencies.
3
+ *
4
+ * - `inferInteractionMode(text)` — keyword-priority mapper from free
5
+ * text (category / pattern / prompt) to a categorical interaction
6
+ * label. Used for telemetry / RAG hinting only; NOT part of the
7
+ * `DataContract` wire surface. Deterministic; default `'display'`.
8
+ * - `inferJsonSchemaType(tsType)` — TypeScript-style type-string
9
+ * mapper to the JSON Schema type union. Deterministic.
10
+ */
11
+ /**
12
+ * Categorical interaction-mode label used by `inferInteractionMode` for
13
+ * telemetry / RAG hinting. NOT a `DataContract` field — the four typed
14
+ * specs (propsSpec / actionSpec / contextSpec / streamSpec) describe
15
+ * the wire surface exhaustively, so interaction mode is a derived
16
+ * heuristic, never part of the contract.
17
+ */
18
+ export type InteractionMode = 'display' | 'collect' | 'converse' | 'broadcast' | 'flow';
19
+ /**
20
+ * Infer interaction mode from any text (category, pattern, or prompt).
21
+ * Consolidated keyword matcher — all call sites use the same function.
22
+ *
23
+ * Used by rag-search and external benchmark runners for routing /
24
+ * telemetry. Deterministic; no I/O. Matches the first rule in priority
25
+ * order, defaulting to 'display'.
26
+ */
27
+ export declare function inferInteractionMode(text: string): InteractionMode;
28
+ /** Map TypeScript-style type strings to JSON Schema types. */
29
+ export declare function inferJsonSchemaType(tsType: string): 'string' | 'number' | 'boolean' | 'array' | 'object';
30
+ //# sourceMappingURL=pure.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pure.d.ts","sourceRoot":"","sources":["../src/pure.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH;;;;;;GAMG;AACH,MAAM,MAAM,eAAe,GAAG,SAAS,GAAG,SAAS,GAAG,UAAU,GAAG,WAAW,GAAG,MAAM,CAAC;AAExF;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,eAAe,CAOlE;AAED,8DAA8D;AAC9D,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,OAAO,GAAG,QAAQ,CAOxG"}
package/dist/pure.js ADDED
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Pure helpers — zero I/O, zero AWS dependencies.
3
+ *
4
+ * - `inferInteractionMode(text)` — keyword-priority mapper from free
5
+ * text (category / pattern / prompt) to a categorical interaction
6
+ * label. Used for telemetry / RAG hinting only; NOT part of the
7
+ * `DataContract` wire surface. Deterministic; default `'display'`.
8
+ * - `inferJsonSchemaType(tsType)` — TypeScript-style type-string
9
+ * mapper to the JSON Schema type union. Deterministic.
10
+ */
11
+ /**
12
+ * Infer interaction mode from any text (category, pattern, or prompt).
13
+ * Consolidated keyword matcher — all call sites use the same function.
14
+ *
15
+ * Used by rag-search and external benchmark runners for routing /
16
+ * telemetry. Deterministic; no I/O. Matches the first rule in priority
17
+ * order, defaulting to 'display'.
18
+ */
19
+ export function inferInteractionMode(text) {
20
+ const t = text.toLowerCase();
21
+ if (t.includes('form') || t.includes('survey') || t.includes('input') || t.includes('collect') || t.includes('sign up') || t.includes('contact'))
22
+ return 'collect';
23
+ if (t.includes('chat') || t.includes('conversation') || t.includes('messaging') || t.includes('talk'))
24
+ return 'converse';
25
+ if (t.includes('dashboard') || t.includes('monitor') || t.includes('live') || t.includes('feed') || t.includes('real-time'))
26
+ return 'broadcast';
27
+ if (t.includes('wizard') || t.includes('onboarding') || t.includes('checkout') || t.includes('stepper') || t.includes('multi-step') || t.includes('flow') || t.includes('step'))
28
+ return 'flow';
29
+ return 'display';
30
+ }
31
+ /** Map TypeScript-style type strings to JSON Schema types. */
32
+ export function inferJsonSchemaType(tsType) {
33
+ const t = tsType.toLowerCase();
34
+ if (t.includes('string'))
35
+ return 'string';
36
+ if (t.includes('number') || t.includes('int') || t.includes('float'))
37
+ return 'number';
38
+ if (t.includes('boolean') || t.includes('bool'))
39
+ return 'boolean';
40
+ if (t.includes('array') || t.includes('[]'))
41
+ return 'array';
42
+ return 'object';
43
+ }
@@ -0,0 +1,73 @@
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 type { EmbeddingProvider, VectorStore } from '@ggui-ai/mcp-server-core';
42
+ import type { NegotiatorOption } from './types.js';
43
+ /** RAG search result with per-stage timing. */
44
+ export interface RagSearchResult {
45
+ options: NegotiatorOption[];
46
+ embeddingLatencyMs: number;
47
+ searchLatencyMs: number;
48
+ }
49
+ /** Dependencies — both public seams from `@ggui-ai/mcp-server-core`. */
50
+ export interface RagSearchDeps {
51
+ embedding: EmbeddingProvider;
52
+ vectors: VectorStore;
53
+ }
54
+ export interface RagSearchInput {
55
+ /** Natural-language query text. Embedded as-is. */
56
+ prompt: string;
57
+ /**
58
+ * Scope / tenant partition. Typically `appId` for per-app indexes
59
+ * or the literal `"shared"` for the global catalog. Passed straight
60
+ * through to `VectorStore.query`; the scope is the tenant boundary,
61
+ * so a query never crosses into another tenant's index.
62
+ */
63
+ scope: string;
64
+ /** Override the k-NN cut-off (default 10). */
65
+ maxCandidates?: number;
66
+ }
67
+ /**
68
+ * Search the vector index for blueprints matching `prompt` within
69
+ * `scope`. See the module docstring for the confidence-band pipeline
70
+ * and encoding contract.
71
+ */
72
+ export declare function ragSearch(deps: RagSearchDeps, input: RagSearchInput): Promise<RagSearchResult>;
73
+ //# sourceMappingURL=rag-search.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rag-search.d.ts","sourceRoot":"","sources":["../src/rag-search.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AAEH,OAAO,KAAK,EACV,iBAAiB,EAEjB,WAAW,EACZ,MAAM,0BAA0B,CAAC;AAGlC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEnD,+CAA+C;AAC/C,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,gBAAgB,EAAE,CAAC;IAC5B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,wEAAwE;AACxE,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,iBAAiB,CAAC;IAC7B,OAAO,EAAE,WAAW,CAAC;CACtB;AAED,MAAM,WAAW,cAAc;IAC7B,mDAAmD;IACnD,MAAM,EAAE,MAAM,CAAC;IACf;;;;;OAKG;IACH,KAAK,EAAE,MAAM,CAAC;IACd,8CAA8C;IAC9C,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAWD;;;;GAIG;AACH,wBAAsB,SAAS,CAC7B,IAAI,EAAE,aAAa,EACnB,KAAK,EAAE,cAAc,GACpB,OAAO,CAAC,eAAe,CAAC,CAwC1B"}