@ggui-ai/negotiator 0.2.0-alpha.4 → 0.3.0-rc.0
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/README.md +25 -19
- package/dist/contract-validators.d.ts +20 -16
- package/dist/contract-validators.d.ts.map +1 -1
- package/dist/contract-validators.js +11 -8
- package/dist/index.d.ts +20 -20
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +20 -14
- package/dist/llm-caller.d.ts +9 -9
- package/dist/llm-caller.d.ts.map +1 -1
- package/dist/llm-caller.js +8 -8
- package/dist/llm-rerank.d.ts +9 -8
- package/dist/llm-rerank.d.ts.map +1 -1
- package/dist/llm-rerank.js +4 -25
- package/dist/normalize-draft.d.ts.map +1 -1
- package/dist/normalize-draft.js +33 -9
- package/dist/normalize-schema.d.ts.map +1 -1
- package/dist/normalize-schema.js +12 -0
- package/dist/synth-bench/cache-key-probe.d.ts +3 -0
- package/dist/synth-bench/cache-key-probe.d.ts.map +1 -0
- package/dist/synth-bench/cache-key-probe.js +177 -0
- package/dist/synth-bench/corpus.d.ts.map +1 -1
- package/dist/synth-bench/corpus.js +28 -20
- package/dist/synth-bench/run-bench.js +2 -2
- package/dist/synthesize-contract.d.ts +2 -3
- package/dist/synthesize-contract.d.ts.map +1 -1
- package/dist/synthesize-contract.js +20 -15
- package/package.json +8 -7
- package/src/contract-validators.ts +21 -17
- package/src/index.ts +20 -33
- package/src/llm-caller.ts +9 -9
- package/src/llm-rerank.ts +12 -17
- package/src/normalize-draft.ts +35 -9
- package/src/normalize-schema.ts +11 -0
- package/src/synth-bench/cache-key-probe.ts +213 -0
- package/src/synth-bench/corpus.ts +28 -20
- package/src/synth-bench/run-bench.ts +2 -2
- package/src/synthesize-contract.ts +24 -20
- package/dist/decision-input.d.ts +0 -51
- package/dist/decision-input.d.ts.map +0 -1
- package/dist/decision-input.js +0 -17
- package/dist/decision.d.ts +0 -58
- package/dist/decision.d.ts.map +0 -1
- package/dist/decision.js +0 -501
- package/dist/intent.d.ts +0 -22
- package/dist/intent.d.ts.map +0 -1
- package/dist/intent.js +0 -28
- package/dist/negotiate.d.ts +0 -142
- package/dist/negotiate.d.ts.map +0 -1
- package/dist/negotiate.js +0 -163
- package/dist/pure.d.ts +0 -30
- package/dist/pure.d.ts.map +0 -1
- package/dist/pure.js +0 -43
- package/dist/rag-search.d.ts +0 -73
- package/dist/rag-search.d.ts.map +0 -1
- package/dist/rag-search.js +0 -192
- package/dist/render.d.ts +0 -57
- package/dist/render.d.ts.map +0 -1
- package/dist/render.js +0 -25
- package/dist/suggestion.d.ts +0 -38
- package/dist/suggestion.d.ts.map +0 -1
- package/dist/suggestion.js +0 -47
- package/dist/types.d.ts +0 -30
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js +0 -13
- package/src/decision-input.ts +0 -52
- package/src/decision.ts +0 -581
- package/src/intent.ts +0 -37
- package/src/negotiate.ts +0 -314
- package/src/pure.ts +0 -46
- package/src/rag-search.ts +0 -274
- package/src/render.ts +0 -56
- package/src/suggestion.ts +0 -73
- package/src/types.ts +0 -31
package/README.md
CHANGED
|
@@ -1,11 +1,19 @@
|
|
|
1
1
|
# @ggui-ai/negotiator
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Contract-synthesis + match-judge engine for
|
|
4
|
+
[ggui](https://github.com/ggui-ai/ggui)'s handshake.
|
|
4
5
|
|
|
5
|
-
Given an agent's
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
Given an agent's draft contract + intent, this package synthesizes (or
|
|
7
|
+
repairs) a conforming `DataContract`, judges blueprint-match candidates for
|
|
8
|
+
reuse, and validates contract structure + novelty — the primitives the
|
|
9
|
+
handshake composes to always return a valid contract.
|
|
10
|
+
|
|
11
|
+
> The handshake **decision** itself (find-similar → reuse vs synth-create)
|
|
12
|
+
> lives in the shared `decideHandshake` core in
|
|
13
|
+
> [`@ggui-ai/mcp-server-handlers`](../mcp-server-handlers), which composes
|
|
14
|
+
> the primitives below. The former in-package `negotiate()` RAG+decision
|
|
15
|
+
> pipeline was retired in favor of that unified, adapter-injected core —
|
|
16
|
+
> one decision spine; LLM and storage bindings plug in per deployment.
|
|
9
17
|
|
|
10
18
|
The package is deployment-agnostic. It composes the storage interfaces
|
|
11
19
|
defined in `@ggui-ai/mcp-server-core` (`EmbeddingProvider`, `VectorStore`),
|
|
@@ -20,28 +28,26 @@ pnpm add @ggui-ai/negotiator
|
|
|
20
28
|
|
|
21
29
|
## What's in the box
|
|
22
30
|
|
|
23
|
-
- **`negotiate(deps, input)`** — top-level orchestrator. Runs RAG search over
|
|
24
|
-
registered blueprints, reads render state, fast-paths exact blueprint
|
|
25
|
-
hits, and otherwise calls the decision LLM.
|
|
26
|
-
- **`makeDecision(...)`** — the decision step in isolation: pick an action
|
|
27
|
-
(`create` / `update` / `replace`) and a blueprint from the
|
|
28
|
-
candidate set.
|
|
29
31
|
- **`synthesizeContract(...)`** — cold-path contract synthesizer. Turns an
|
|
30
32
|
agent intent into a `DataContract` (props / context / action / stream
|
|
31
33
|
specs, plus gadget references), with a repair loop and a schema-validation
|
|
32
34
|
gate.
|
|
33
|
-
- **`
|
|
34
|
-
|
|
35
|
-
-
|
|
36
|
-
|
|
35
|
+
- **`ensureConformingContract(...)`** — the create-path guarantee: validates
|
|
36
|
+
an untrusted draft and, on errors, deterministically normalizes or
|
|
37
|
+
LLM-repairs it so the handshake always returns a contract that passes the
|
|
38
|
+
backstop. Never throws.
|
|
39
|
+
- **`rerankCandidates(...)`** — LLM judge that re-ranks blueprint-match
|
|
40
|
+
retrieval candidates (the semantic-match decision used by
|
|
41
|
+
`decideHandshake`).
|
|
42
|
+
- **`validateContractRedundancy` / `validateContractNovelty`** — advisory
|
|
37
43
|
validators for the actions-vs-context placement rule.
|
|
38
44
|
|
|
39
45
|
```ts
|
|
40
|
-
import {
|
|
46
|
+
import { ensureConformingContract } from "@ggui-ai/negotiator";
|
|
41
47
|
|
|
42
|
-
const result = await
|
|
43
|
-
// result.
|
|
44
|
-
// result.
|
|
48
|
+
const result = await ensureConformingContract({ llm }, { intent, draft });
|
|
49
|
+
// result.origin — "agent" (clean) | "synth" (repaired)
|
|
50
|
+
// result.contract — a DataContract guaranteed to pass the handshake backstop
|
|
45
51
|
```
|
|
46
52
|
|
|
47
53
|
## License
|
|
@@ -3,9 +3,12 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Two detectors live here:
|
|
5
5
|
*
|
|
6
|
-
* - {@link
|
|
7
|
-
* flag over-specified contracts without any runtime dependency.
|
|
8
|
-
*
|
|
6
|
+
* - {@link validateContractRedundancy} — pure redundancy heuristics that
|
|
7
|
+
* flag over-specified contracts without any runtime dependency.
|
|
8
|
+
* (Deliberately NOT named `validateContractStructure` — that name is
|
|
9
|
+
* owned by `@ggui-ai/protocol`'s normative structural validator,
|
|
10
|
+
* which returns `ContractViolation`s; this one returns advisory
|
|
11
|
+
* findings.) The load-bearing finding is `redundant-action`: an empty-payload
|
|
9
12
|
* `actionSpec` entry whose name parses as a mutator of an existing
|
|
10
13
|
* `contextSpec` slot. Real example that motivated this module: a
|
|
11
14
|
* synthesizer emitted both `actionSpec.increment` (empty payload)
|
|
@@ -59,39 +62,40 @@ export interface ContractValidationResult {
|
|
|
59
62
|
}
|
|
60
63
|
/**
|
|
61
64
|
* Dependencies the novelty detector needs. Embedding provider + vector
|
|
62
|
-
* store are the same seams the
|
|
63
|
-
* novelty check operates over the production index without any
|
|
64
|
-
* infrastructure.
|
|
65
|
+
* store are the same seams the handshake's find-similar retrieval uses,
|
|
66
|
+
* so the novelty check operates over the production index without any
|
|
67
|
+
* extra infrastructure.
|
|
65
68
|
*/
|
|
66
69
|
export interface ContractValidationNoveltyDeps {
|
|
67
70
|
readonly embedding: EmbeddingProvider;
|
|
68
71
|
readonly vectorStore: VectorStore;
|
|
69
72
|
/** Tenant / partition the nearest-neighbor query runs against. Same
|
|
70
|
-
* semantics as `
|
|
73
|
+
* semantics as the `scope` argument on `VectorStore.query` — the
|
|
74
|
+
* tenant / index partition, typically `appId`. */
|
|
71
75
|
readonly scope: string;
|
|
72
76
|
}
|
|
73
77
|
export interface ContractValidationNoveltyOptions {
|
|
74
78
|
/**
|
|
75
79
|
* Cosine distance threshold above which the contract is flagged as
|
|
76
|
-
* novel. Distance is `1 - cosine_similarity`. Default `0.8
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
* still flag for review.
|
|
80
|
+
* novel. Distance is `1 - cosine_similarity`. Default `0.8`: a
|
|
81
|
+
* contract whose nearest stored neighbor sits beyond distance 0.8
|
|
82
|
+
* is effectively outside retrieval range, with a small buffer below
|
|
83
|
+
* the hard one-tail boundary (distance 0.85) so contracts that
|
|
84
|
+
* hover near retrieval but slightly above still flag for review.
|
|
81
85
|
*/
|
|
82
86
|
readonly thresholdCosine?: number;
|
|
83
87
|
}
|
|
84
88
|
/**
|
|
85
|
-
* Run the synchronous
|
|
89
|
+
* Run the synchronous redundancy detector against `contract`.
|
|
86
90
|
*
|
|
87
|
-
*
|
|
91
|
+
* One finding kind:
|
|
88
92
|
* - `redundant-action`: empty-payload action whose name parses as a
|
|
89
93
|
* mutator of an existing context slot.
|
|
90
94
|
*
|
|
91
|
-
* Returns an empty findings array for contracts that don't trip
|
|
95
|
+
* Returns an empty findings array for contracts that don't trip the
|
|
92
96
|
* heuristic.
|
|
93
97
|
*/
|
|
94
|
-
export declare function
|
|
98
|
+
export declare function validateContractRedundancy(contract: DataContract): ContractValidationResult;
|
|
95
99
|
/**
|
|
96
100
|
* Validate the actions-vs-context placement rule: actions drive agent
|
|
97
101
|
* turns, context observes state. Findings 1-3 are advisory `warn`;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contract-validators.d.ts","sourceRoot":"","sources":["../src/contract-validators.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"contract-validators.d.ts","sourceRoot":"","sources":["../src/contract-validators.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAc,MAAM,mBAAmB,CAAC;AAElE,OAAO,KAAK,EACV,iBAAiB,EACjB,WAAW,EACZ,MAAM,0BAA0B,CAAC;AAMlC;;;;;;;;;GASG;AACH,MAAM,MAAM,6BAA6B,GACrC,kBAAkB,GAClB,aAAa,GACb,mCAAmC,GACnC,2BAA2B,GAC3B,6BAA6B,GAC7B,4BAA4B,GAC5B,8BAA8B,GAC9B,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC;AAElB;;;;;;GAMG;AACH,MAAM,WAAW,yBAAyB;IACxC,QAAQ,CAAC,IAAI,EAAE,6BAA6B,CAAC;IAC7C,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;IACpC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,QAAQ,EAAE,SAAS,yBAAyB,EAAE,CAAC;CACzD;AAED;;;;;GAKG;AACH,MAAM,WAAW,6BAA6B;IAC5C,QAAQ,CAAC,SAAS,EAAE,iBAAiB,CAAC;IACtC,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC;IAClC;;sDAEkD;IAClD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,gCAAgC;IAC/C;;;;;;;OAOG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;CACnC;AAwJD;;;;;;;;;GASG;AACH,wBAAgB,0BAA0B,CACxC,QAAQ,EAAE,YAAY,GACrB,wBAAwB,CAkC1B;AAsCD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,YAAY,GACrB,wBAAwB,CA8D1B;AAiCD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,yBAAyB,CACvC,QAAQ,EAAE,YAAY,EACtB,MAAM,EAAE,MAAM,GACb,wBAAwB,CAoC1B;AAQD;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,uBAAuB,CAC3C,QAAQ,EAAE,YAAY,EACtB,IAAI,EAAE,6BAA6B,EACnC,OAAO,GAAE,gCAAqC,GAC7C,OAAO,CAAC,wBAAwB,CAAC,CAkCnC;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,wBAAwB,GAC/B,MAAM,CAKR"}
|
|
@@ -3,9 +3,12 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Two detectors live here:
|
|
5
5
|
*
|
|
6
|
-
* - {@link
|
|
7
|
-
* flag over-specified contracts without any runtime dependency.
|
|
8
|
-
*
|
|
6
|
+
* - {@link validateContractRedundancy} — pure redundancy heuristics that
|
|
7
|
+
* flag over-specified contracts without any runtime dependency.
|
|
8
|
+
* (Deliberately NOT named `validateContractStructure` — that name is
|
|
9
|
+
* owned by `@ggui-ai/protocol`'s normative structural validator,
|
|
10
|
+
* which returns `ContractViolation`s; this one returns advisory
|
|
11
|
+
* findings.) The load-bearing finding is `redundant-action`: an empty-payload
|
|
9
12
|
* `actionSpec` entry whose name parses as a mutator of an existing
|
|
10
13
|
* `contextSpec` slot. Real example that motivated this module: a
|
|
11
14
|
* synthesizer emitted both `actionSpec.increment` (empty payload)
|
|
@@ -167,19 +170,19 @@ function isEmptyPayloadSchema(schema) {
|
|
|
167
170
|
return Object.keys(schema.properties).length === 0;
|
|
168
171
|
}
|
|
169
172
|
// =============================================================================
|
|
170
|
-
//
|
|
173
|
+
// Redundancy validator (synchronous, dependency-free)
|
|
171
174
|
// =============================================================================
|
|
172
175
|
/**
|
|
173
|
-
* Run the synchronous
|
|
176
|
+
* Run the synchronous redundancy detector against `contract`.
|
|
174
177
|
*
|
|
175
|
-
*
|
|
178
|
+
* One finding kind:
|
|
176
179
|
* - `redundant-action`: empty-payload action whose name parses as a
|
|
177
180
|
* mutator of an existing context slot.
|
|
178
181
|
*
|
|
179
|
-
* Returns an empty findings array for contracts that don't trip
|
|
182
|
+
* Returns an empty findings array for contracts that don't trip the
|
|
180
183
|
* heuristic.
|
|
181
184
|
*/
|
|
182
|
-
export function
|
|
185
|
+
export function validateContractRedundancy(contract) {
|
|
183
186
|
const findings = [];
|
|
184
187
|
const actionSpec = contract.actionSpec;
|
|
185
188
|
const contextSpec = contract.contextSpec;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,38 +1,38 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @ggui-ai/negotiator — open-source
|
|
2
|
+
* @ggui-ai/negotiator — open-source contract-synthesis + match-judge
|
|
3
|
+
* engine for ggui's handshake.
|
|
3
4
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
5
|
+
* Given an agent's draft contract + intent, this package:
|
|
6
|
+
* - synthesizes / repairs a conforming `DataContract`
|
|
7
|
+
* (`synthesizeContract`, `ensureConformingContract`) so the
|
|
8
|
+
* handshake always returns a valid contract;
|
|
9
|
+
* - judges blueprint-match candidates for reuse (`rerankCandidates`);
|
|
10
|
+
* - validates contract structure + novelty (`contract-validators`);
|
|
11
|
+
* - hashes contracts into identity + variant keys (`hashContract`,
|
|
12
|
+
* `buildVariant`) and normalizes untrusted drafts (`normalizeDraft`).
|
|
13
|
+
*
|
|
14
|
+
* The HANDSHAKE DECISION itself (find-similar → reuse vs synth-create)
|
|
15
|
+
* lives in the shared `decideHandshake` core in
|
|
16
|
+
* `@ggui-ai/mcp-server-handlers`, which composes these primitives; the
|
|
17
|
+
* former in-package `negotiate()` RAG+decision pipeline was retired in
|
|
18
|
+
* favor of that unified, adapter-injected core.
|
|
6
19
|
*
|
|
7
20
|
* Composes the storage seams defined in `@ggui-ai/mcp-server-core`
|
|
8
|
-
* (`EmbeddingProvider`, `VectorStore
|
|
9
|
-
*
|
|
10
|
-
* managed embedding service or vector store) live behind those seams
|
|
11
|
-
* so this package stays deployment-agnostic.
|
|
21
|
+
* (`EmbeddingProvider`, `VectorStore`) so this package stays
|
|
22
|
+
* deployment-agnostic.
|
|
12
23
|
*
|
|
13
24
|
* This barrel stays narrow — it exports only the minimum surface
|
|
14
25
|
* consumers need. Each additive export carries semver weight.
|
|
15
26
|
*/
|
|
16
27
|
export { hashContract, buildVariant } from './contract-hash.js';
|
|
17
|
-
export { computeIntentId, shouldSuppressSuggestion } from './intent.js';
|
|
18
|
-
export { detectDataPatterns, buildSuggestion } from './suggestion.js';
|
|
19
|
-
export type { NegotiatorSuggestion } from './suggestion.js';
|
|
20
|
-
export { inferInteractionMode, inferJsonSchemaType } from './pure.js';
|
|
21
|
-
export { ragSearch } from './rag-search.js';
|
|
22
|
-
export type { RagSearchDeps, RagSearchInput, RagSearchResult, } from './rag-search.js';
|
|
23
|
-
export type { NegotiatorOption } from './types.js';
|
|
24
28
|
export type { LLMCaller, LLMCallerConfig, ToolSchema } from './llm-caller.js';
|
|
25
|
-
export type { RenderState, RenderEntry } from './render.js';
|
|
26
|
-
export type { NegotiatorDecisionInput } from './decision-input.js';
|
|
27
|
-
export { DECISION_SYSTEM_PROMPT, buildDecisionUserMessage, makeDecision, } from './decision.js';
|
|
28
|
-
export { negotiate } from './negotiate.js';
|
|
29
|
-
export type { NegotiateDeps, NegotiateInput, NegotiateConfig, NegotiateResult, } from './negotiate.js';
|
|
30
29
|
export { rerankCandidates } from './llm-rerank.js';
|
|
31
30
|
export type { RerankCandidate, RerankDecision, RerankQuery, } from './llm-rerank.js';
|
|
32
31
|
export { synthesizeContract } from './synthesize-contract.js';
|
|
33
32
|
export type { SynthesizeContractResult } from './synthesize-contract.js';
|
|
34
33
|
export { ensureConformingContract } from './ensure-conforming-contract.js';
|
|
35
34
|
export type { EnsureConformingResult } from './ensure-conforming-contract.js';
|
|
36
|
-
export {
|
|
35
|
+
export { normalizeDraft } from './normalize-draft.js';
|
|
36
|
+
export { validateContractRedundancy, validateContractNovelty, formatValidationFindings, } from './contract-validators.js';
|
|
37
37
|
export type { ContractValidationFinding, ContractValidationFindingKind, ContractValidationResult, ContractValidationNoveltyDeps, ContractValidationNoveltyOptions, } from './contract-validators.js';
|
|
38
38
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAChE,YAAY,EAAE,SAAS,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC9E,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACnD,YAAY,EACV,eAAe,EACf,cAAc,EACd,WAAW,GACZ,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAC9D,YAAY,EAAE,wBAAwB,EAAE,MAAM,0BAA0B,CAAC;AACzE,OAAO,EAAE,wBAAwB,EAAE,MAAM,iCAAiC,CAAC;AAC3E,YAAY,EAAE,sBAAsB,EAAE,MAAM,iCAAiC,CAAC;AAC9E,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EACL,0BAA0B,EAC1B,uBAAuB,EACvB,wBAAwB,GACzB,MAAM,0BAA0B,CAAC;AAClC,YAAY,EACV,yBAAyB,EACzB,6BAA6B,EAC7B,wBAAwB,EACxB,6BAA6B,EAC7B,gCAAgC,GACjC,MAAM,0BAA0B,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,26 +1,32 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @ggui-ai/negotiator — open-source
|
|
2
|
+
* @ggui-ai/negotiator — open-source contract-synthesis + match-judge
|
|
3
|
+
* engine for ggui's handshake.
|
|
3
4
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
5
|
+
* Given an agent's draft contract + intent, this package:
|
|
6
|
+
* - synthesizes / repairs a conforming `DataContract`
|
|
7
|
+
* (`synthesizeContract`, `ensureConformingContract`) so the
|
|
8
|
+
* handshake always returns a valid contract;
|
|
9
|
+
* - judges blueprint-match candidates for reuse (`rerankCandidates`);
|
|
10
|
+
* - validates contract structure + novelty (`contract-validators`);
|
|
11
|
+
* - hashes contracts into identity + variant keys (`hashContract`,
|
|
12
|
+
* `buildVariant`) and normalizes untrusted drafts (`normalizeDraft`).
|
|
13
|
+
*
|
|
14
|
+
* The HANDSHAKE DECISION itself (find-similar → reuse vs synth-create)
|
|
15
|
+
* lives in the shared `decideHandshake` core in
|
|
16
|
+
* `@ggui-ai/mcp-server-handlers`, which composes these primitives; the
|
|
17
|
+
* former in-package `negotiate()` RAG+decision pipeline was retired in
|
|
18
|
+
* favor of that unified, adapter-injected core.
|
|
6
19
|
*
|
|
7
20
|
* Composes the storage seams defined in `@ggui-ai/mcp-server-core`
|
|
8
|
-
* (`EmbeddingProvider`, `VectorStore
|
|
9
|
-
*
|
|
10
|
-
* managed embedding service or vector store) live behind those seams
|
|
11
|
-
* so this package stays deployment-agnostic.
|
|
21
|
+
* (`EmbeddingProvider`, `VectorStore`) so this package stays
|
|
22
|
+
* deployment-agnostic.
|
|
12
23
|
*
|
|
13
24
|
* This barrel stays narrow — it exports only the minimum surface
|
|
14
25
|
* consumers need. Each additive export carries semver weight.
|
|
15
26
|
*/
|
|
16
27
|
export { hashContract, buildVariant } from './contract-hash.js';
|
|
17
|
-
export { computeIntentId, shouldSuppressSuggestion } from './intent.js';
|
|
18
|
-
export { detectDataPatterns, buildSuggestion } from './suggestion.js';
|
|
19
|
-
export { inferInteractionMode, inferJsonSchemaType } from './pure.js';
|
|
20
|
-
export { ragSearch } from './rag-search.js';
|
|
21
|
-
export { DECISION_SYSTEM_PROMPT, buildDecisionUserMessage, makeDecision, } from './decision.js';
|
|
22
|
-
export { negotiate } from './negotiate.js';
|
|
23
28
|
export { rerankCandidates } from './llm-rerank.js';
|
|
24
29
|
export { synthesizeContract } from './synthesize-contract.js';
|
|
25
30
|
export { ensureConformingContract } from './ensure-conforming-contract.js';
|
|
26
|
-
export {
|
|
31
|
+
export { normalizeDraft } from './normalize-draft.js';
|
|
32
|
+
export { validateContractRedundancy, validateContractNovelty, formatValidationFindings, } from './contract-validators.js';
|
package/dist/llm-caller.d.ts
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `LLMCaller` — the
|
|
2
|
+
* `LLMCaller` — the negotiator's LLM dispatcher.
|
|
3
3
|
*
|
|
4
4
|
* Narrow abstraction over "call a chat model, optionally with a forced
|
|
5
5
|
* tool-use schema for guaranteed-JSON structured output." Kept public
|
|
6
6
|
* so OSS consumers of `@ggui-ai/negotiator` can bring their own LLM
|
|
7
7
|
* provider (Anthropic direct, OpenAI, Google, a local model, a
|
|
8
|
-
* community LiteLLM wrapper) without touching the
|
|
8
|
+
* community LiteLLM wrapper) without touching the synthesis / judge
|
|
9
9
|
* source.
|
|
10
10
|
*
|
|
11
11
|
* **Why this lives in `@ggui-ai/negotiator`, not
|
|
12
12
|
* `@ggui-ai/mcp-server-core`.** `mcp-server-core` contains the
|
|
13
13
|
* storage + runtime seams an MCP server implementer binds against
|
|
14
14
|
* (`VectorStore`, `EmbeddingProvider`, `KeyValueStore`,
|
|
15
|
-
* `BlueprintProvider
|
|
16
|
-
*
|
|
17
|
-
* lifting it to `mcp-server-core` would grow the
|
|
18
|
-
* speculatively. If a second consumer outside the
|
|
19
|
-
* surfaces later, the "where does `LLMCaller` live?"
|
|
20
|
-
* re-opened at that point.
|
|
15
|
+
* `BlueprintProvider`). `LLMCaller` is an engine-internal
|
|
16
|
+
* dispatcher — one level below the synthesis + judge primitives this
|
|
17
|
+
* package exports — so lifting it to `mcp-server-core` would grow the
|
|
18
|
+
* public seam count speculatively. If a second consumer outside the
|
|
19
|
+
* negotiator surfaces later, the "where does `LLMCaller` live?"
|
|
20
|
+
* question can be re-opened at that point.
|
|
21
21
|
*
|
|
22
22
|
* Normative semantics:
|
|
23
23
|
* - `call(systemPrompt, userMessage, maxTokens?)` returns the raw
|
|
@@ -41,7 +41,7 @@ export interface ToolSchema {
|
|
|
41
41
|
description: string;
|
|
42
42
|
input_schema: Record<string, unknown>;
|
|
43
43
|
}
|
|
44
|
-
/** Chat-model dispatcher consumed by the negotiator
|
|
44
|
+
/** Chat-model dispatcher consumed by the negotiator's synthesis + judge primitives. */
|
|
45
45
|
export interface LLMCaller {
|
|
46
46
|
/**
|
|
47
47
|
* Call the model in plain-text mode. `maxTokens` defaults to
|
package/dist/llm-caller.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"llm-caller.d.ts","sourceRoot":"","sources":["../src/llm-caller.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAEH,6DAA6D;AAC7D,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACvC;AAED,
|
|
1
|
+
{"version":3,"file":"llm-caller.d.ts","sourceRoot":"","sources":["../src/llm-caller.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAEH,6DAA6D;AAC7D,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACvC;AAED,uFAAuF;AACvF,MAAM,WAAW,SAAS;IACxB;;;OAGG;IACH,IAAI,CACF,YAAY,EAAE,MAAM,EACpB,WAAW,EAAE,MAAM,EACnB,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,CAAC,CAAC;IAEnB;;;;;OAKG;IACH,cAAc,CAAC,CAAC,CAAC,EACf,YAAY,EAAE,MAAM,EACpB,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,UAAU,EAChB,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,CAAC,CAAC,CAAC;CACf;AAED;;;;;GAKG;AACH,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,WAAW,GAAG,QAAQ,GAAG,QAAQ,GAAG,YAAY,GAAG,SAAS,CAAC;IACvE,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB"}
|
package/dist/llm-caller.js
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `LLMCaller` — the
|
|
2
|
+
* `LLMCaller` — the negotiator's LLM dispatcher.
|
|
3
3
|
*
|
|
4
4
|
* Narrow abstraction over "call a chat model, optionally with a forced
|
|
5
5
|
* tool-use schema for guaranteed-JSON structured output." Kept public
|
|
6
6
|
* so OSS consumers of `@ggui-ai/negotiator` can bring their own LLM
|
|
7
7
|
* provider (Anthropic direct, OpenAI, Google, a local model, a
|
|
8
|
-
* community LiteLLM wrapper) without touching the
|
|
8
|
+
* community LiteLLM wrapper) without touching the synthesis / judge
|
|
9
9
|
* source.
|
|
10
10
|
*
|
|
11
11
|
* **Why this lives in `@ggui-ai/negotiator`, not
|
|
12
12
|
* `@ggui-ai/mcp-server-core`.** `mcp-server-core` contains the
|
|
13
13
|
* storage + runtime seams an MCP server implementer binds against
|
|
14
14
|
* (`VectorStore`, `EmbeddingProvider`, `KeyValueStore`,
|
|
15
|
-
* `BlueprintProvider
|
|
16
|
-
*
|
|
17
|
-
* lifting it to `mcp-server-core` would grow the
|
|
18
|
-
* speculatively. If a second consumer outside the
|
|
19
|
-
* surfaces later, the "where does `LLMCaller` live?"
|
|
20
|
-
* re-opened at that point.
|
|
15
|
+
* `BlueprintProvider`). `LLMCaller` is an engine-internal
|
|
16
|
+
* dispatcher — one level below the synthesis + judge primitives this
|
|
17
|
+
* package exports — so lifting it to `mcp-server-core` would grow the
|
|
18
|
+
* public seam count speculatively. If a second consumer outside the
|
|
19
|
+
* negotiator surfaces later, the "where does `LLMCaller` live?"
|
|
20
|
+
* question can be re-opened at that point.
|
|
21
21
|
*
|
|
22
22
|
* Normative semantics:
|
|
23
23
|
* - `call(systemPrompt, userMessage, maxTokens?)` returns the raw
|
package/dist/llm-rerank.d.ts
CHANGED
|
@@ -12,7 +12,6 @@
|
|
|
12
12
|
* judge restores precision. Combined break-even hit rate is ~10%;
|
|
13
13
|
* realistic workloads observe 30-70%.
|
|
14
14
|
*/
|
|
15
|
-
import { summarizeContract } from '@ggui-ai/protocol';
|
|
16
15
|
import type { LLMCaller, ToolSchema } from './llm-caller.js';
|
|
17
16
|
/**
|
|
18
17
|
* One candidate blueprint for the LLM judge to consider.
|
|
@@ -28,7 +27,7 @@ export interface RerankCandidate {
|
|
|
28
27
|
readonly cachedIntent: string;
|
|
29
28
|
/**
|
|
30
29
|
* One-line summary of the blueprint's contract surface. Format
|
|
31
|
-
* matches `summarizeContract()`
|
|
30
|
+
* matches `@ggui-ai/protocol`'s `summarizeContract()` — `slots=...; actions=...;
|
|
32
31
|
* streams=...; props=...` so the judge sees the structural shape
|
|
33
32
|
* without the JSON noise.
|
|
34
33
|
*/
|
|
@@ -49,10 +48,13 @@ export interface RerankDecision {
|
|
|
49
48
|
*/
|
|
50
49
|
readonly matchId: string | null;
|
|
51
50
|
/**
|
|
52
|
-
* Confidence on `[0, 1]
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
51
|
+
* Confidence on `[0, 1]` — how strongly the matched candidate is in
|
|
52
|
+
* the same task-and-shape family as the request, NOT how completely
|
|
53
|
+
* its fields cover the request. Field/slot/action deltas are
|
|
54
|
+
* reconciled and reported to the agent separately; they never lower
|
|
55
|
+
* this score. Caller compares against a threshold (e.g. 0.6) before
|
|
56
|
+
* treating the decision as a hit. Returned even when matchId is null
|
|
57
|
+
* so callers can log "judge declined with confidence X."
|
|
56
58
|
*/
|
|
57
59
|
readonly confidence: number;
|
|
58
60
|
/**
|
|
@@ -79,9 +81,8 @@ export interface RerankQuery {
|
|
|
79
81
|
readonly intent: string;
|
|
80
82
|
readonly contractSummary: string;
|
|
81
83
|
}
|
|
82
|
-
declare const RERANK_SYSTEM_PROMPT = "You match user UI requests against previously-generated UI blueprints. Each blueprint was produced for a past request and stored. Decide whether any candidate
|
|
84
|
+
declare const RERANK_SYSTEM_PROMPT = "You match user UI requests against previously-generated UI blueprints. Each blueprint was produced for a past request and stored. Decide whether any candidate belongs to the SAME FAMILY as the current request \u2014 i.e. it would serve as a reasonable starting point that the requester can refine, not a pixel-exact replica.\n\nMATCH means the candidate is the same intended user task AND the same broad UI shape (component types, layout pattern). A candidate still MATCHES when the current request adds or omits fields, slots, or actions relative to the cached blueprint \u2014 a superset, a subset, or an overlapping wire surface all still match. Added or omitted fields/slots/actions DO NOT block a match and are NOT yours to judge: those wire-surface deltas are reconciled and reported to the agent separately, after you decide. Judge similarity of task and shape, never coverage of fields.\n\nNO-MATCH means the candidate is a fundamentally different thing \u2014 a different intended task (haiku composer vs tweet draft, login vs signup), a different UI shape (form vs list vs dashboard), OR a conflicting load-bearing fixed VALUE baked into the blueprint (calendar pinned to Jan vs a request for Mar \u2014 same contract shape, but the fixed value conflicts). Reserve NO-MATCH for these; do not decline a candidate merely because its fields, slots, or actions differ from the request.\n\nVisual style differences alone (minimal vs ornate, dense vs spacious) DO NOT block a match \u2014 the user can refine those after they get a working UI.\n\nOutput exactly ONE tool call with your decision. confidence is a number in [0, 1] measuring how strongly the candidate is in the same task-and-shape family as the request (not how completely its fields cover the request). matchId is a string from the candidate ids, or null when no candidate matches. reason is a short sentence the operator can use to debug.";
|
|
83
85
|
declare const RERANK_TOOL: ToolSchema;
|
|
84
|
-
export { summarizeContract };
|
|
85
86
|
/**
|
|
86
87
|
* Run the rerank judge against a query + candidate list.
|
|
87
88
|
*
|
package/dist/llm-rerank.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"llm-rerank.d.ts","sourceRoot":"","sources":["../src/llm-rerank.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,
|
|
1
|
+
{"version":3,"file":"llm-rerank.d.ts","sourceRoot":"","sources":["../src/llm-rerank.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE7D;;;;;;GAMG;AACH,MAAM,WAAW,eAAe;IAC9B,+DAA+D;IAC/D,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,gEAAgE;IAChE,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B;;;;;OAKG;IACH,QAAQ,CAAC,qBAAqB,EAAE,MAAM,CAAC;IACvC;;;;;OAKG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,0CAA0C;AAC1C,MAAM,WAAW,cAAc;IAC7B;;;OAGG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC;;;;;;;;OAQG;IACH,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B;;;;OAIG;IACH,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,0CAA0C;IAC1C,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B;;;;;OAKG;IACH,QAAQ,CAAC,SAAS,EAAE;QAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;CACzE;AAED,8DAA8D;AAC9D,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;CAClC;AAED,QAAA,MAAM,oBAAoB,03DAQ6U,CAAC;AAExW,QAAA,MAAM,WAAW,EAAE,UA4BlB,CAAC;AA6EF;;;;;;;;;;;GAWG;AACH,wBAAsB,gBAAgB,CACpC,IAAI,EAAE;IAAE,QAAQ,CAAC,GAAG,EAAE,SAAS,CAAA;CAAE,EACjC,KAAK,EAAE,WAAW,EAClB,UAAU,EAAE,SAAS,eAAe,EAAE,GACrC,OAAO,CAAC,cAAc,CAAC,CAwDzB;AAGD,OAAO,EAAE,oBAAoB,EAAE,WAAW,EAAE,CAAC"}
|
package/dist/llm-rerank.js
CHANGED
|
@@ -1,27 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
* LLM rerank — Tier-2 precision oracle for the blueprint registry.
|
|
3
|
-
*
|
|
4
|
-
* Given a user's UI request (intent + contract structure) and a set
|
|
5
|
-
* of candidate cached blueprints retrieved by RAG, ask a fast LLM
|
|
6
|
-
* (Haiku 4.5) which candidate (if any) matches. Returns a structured
|
|
7
|
-
* decision so the caller can branch deterministically.
|
|
8
|
-
*
|
|
9
|
-
* This module is the precision half of the blueprint-first
|
|
10
|
-
* architecture: RAG retrieval is high-recall but low-precision (bge-
|
|
11
|
-
* small confuses topic-similar but UI-divergent prompts); the LLM
|
|
12
|
-
* judge restores precision. Combined break-even hit rate is ~10%;
|
|
13
|
-
* realistic workloads observe 30-70%.
|
|
14
|
-
*/
|
|
15
|
-
import { summarizeContract } from '@ggui-ai/protocol';
|
|
16
|
-
const RERANK_SYSTEM_PROMPT = `You match user UI requests against previously-generated UI blueprints. Each blueprint was produced for a past request and stored. Decide whether any candidate produces the SAME USEFUL UI for the current request.
|
|
1
|
+
const RERANK_SYSTEM_PROMPT = `You match user UI requests against previously-generated UI blueprints. Each blueprint was produced for a past request and stored. Decide whether any candidate belongs to the SAME FAMILY as the current request — i.e. it would serve as a reasonable starting point that the requester can refine, not a pixel-exact replica.
|
|
17
2
|
|
|
18
|
-
MATCH means the candidate
|
|
3
|
+
MATCH means the candidate is the same intended user task AND the same broad UI shape (component types, layout pattern). A candidate still MATCHES when the current request adds or omits fields, slots, or actions relative to the cached blueprint — a superset, a subset, or an overlapping wire surface all still match. Added or omitted fields/slots/actions DO NOT block a match and are NOT yours to judge: those wire-surface deltas are reconciled and reported to the agent separately, after you decide. Judge similarity of task and shape, never coverage of fields.
|
|
19
4
|
|
|
20
|
-
NO-MATCH means the candidate
|
|
5
|
+
NO-MATCH means the candidate is a fundamentally different thing — a different intended task (haiku composer vs tweet draft, login vs signup), a different UI shape (form vs list vs dashboard), OR a conflicting load-bearing fixed VALUE baked into the blueprint (calendar pinned to Jan vs a request for Mar — same contract shape, but the fixed value conflicts). Reserve NO-MATCH for these; do not decline a candidate merely because its fields, slots, or actions differ from the request.
|
|
21
6
|
|
|
22
7
|
Visual style differences alone (minimal vs ornate, dense vs spacious) DO NOT block a match — the user can refine those after they get a working UI.
|
|
23
8
|
|
|
24
|
-
Output exactly ONE tool call with your decision. confidence is a number in [0, 1]. matchId is a string from the candidate ids, or null when no candidate matches. reason is a short sentence the operator can use to debug.`;
|
|
9
|
+
Output exactly ONE tool call with your decision. confidence is a number in [0, 1] measuring how strongly the candidate is in the same task-and-shape family as the request (not how completely its fields cover the request). matchId is a string from the candidate ids, or null when no candidate matches. reason is a short sentence the operator can use to debug.`;
|
|
25
10
|
const RERANK_TOOL = {
|
|
26
11
|
name: 'submit_rerank_decision',
|
|
27
12
|
description: 'Submit your match-vs-no-match decision over the candidates.',
|
|
@@ -72,12 +57,6 @@ function truncate(text, max) {
|
|
|
72
57
|
return text;
|
|
73
58
|
return `${text.slice(0, max - 1)}…`;
|
|
74
59
|
}
|
|
75
|
-
// Re-export `summarizeContract` from protocol for backwards-compatible
|
|
76
|
-
// access through `@ggui-ai/negotiator/llm-rerank` consumers (the
|
|
77
|
-
// canonical home is `@ggui-ai/protocol` — both the registry storage
|
|
78
|
-
// and the rerank prompt depend on it). Kept as a re-export so the
|
|
79
|
-
// existing test imports here keep working.
|
|
80
|
-
export { summarizeContract };
|
|
81
60
|
function clampConfidence(value) {
|
|
82
61
|
if (typeof value !== 'number' || !Number.isFinite(value))
|
|
83
62
|
return 0;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"normalize-draft.d.ts","sourceRoot":"","sources":["../src/normalize-draft.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;
|
|
1
|
+
{"version":3,"file":"normalize-draft.d.ts","sourceRoot":"","sources":["../src/normalize-draft.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AA2GH;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CA4CtD"}
|
package/dist/normalize-draft.js
CHANGED
|
@@ -52,13 +52,12 @@ const ACTION_ENTRY_KEYS = new Set([
|
|
|
52
52
|
'nextStep',
|
|
53
53
|
]);
|
|
54
54
|
const STREAM_ENTRY_KEYS = new Set(['description', 'schema', 'source']);
|
|
55
|
-
const AGENT_TOOL_KEYS = new Set([
|
|
56
|
-
|
|
57
|
-
|
|
55
|
+
const AGENT_TOOL_KEYS = new Set(['serverInfo', 'toolInfo', 'usage', 'example']);
|
|
56
|
+
/** Inner keys of an {@link AgentToolEntry.toolInfo} (the MCP descriptor). */
|
|
57
|
+
const AGENT_TOOL_INFO_KEYS = new Set([
|
|
58
58
|
'inputSchema',
|
|
59
|
+
'description',
|
|
59
60
|
'outputSchema',
|
|
60
|
-
'required',
|
|
61
|
-
'example',
|
|
62
61
|
]);
|
|
63
62
|
function isRecord(value) {
|
|
64
63
|
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
@@ -87,6 +86,34 @@ function cleanEntryMap(map, allowed, schemaFields) {
|
|
|
87
86
|
}
|
|
88
87
|
return out;
|
|
89
88
|
}
|
|
89
|
+
/** Clean a single `AgentToolEntry`: keep only the allowed outer keys
|
|
90
|
+
* ({@link AGENT_TOOL_KEYS}), then clean the nested `toolInfo` to its
|
|
91
|
+
* inner keys ({@link AGENT_TOOL_INFO_KEYS}) and normalize
|
|
92
|
+
* `toolInfo.inputSchema` / `toolInfo.outputSchema` so the `.strict()`
|
|
93
|
+
* schema doesn't reject a stray nested key. Non-record entries pass
|
|
94
|
+
* through untouched. */
|
|
95
|
+
function cleanAgentToolEntry(entry) {
|
|
96
|
+
if (!isRecord(entry))
|
|
97
|
+
return entry;
|
|
98
|
+
const out = {};
|
|
99
|
+
for (const [key, value] of Object.entries(entry)) {
|
|
100
|
+
if (!AGENT_TOOL_KEYS.has(key))
|
|
101
|
+
continue; // strip the illegal outer key
|
|
102
|
+
out[key] =
|
|
103
|
+
key === 'toolInfo'
|
|
104
|
+
? cleanEntry(value, AGENT_TOOL_INFO_KEYS, ['inputSchema', 'outputSchema'])
|
|
105
|
+
: value;
|
|
106
|
+
}
|
|
107
|
+
return out;
|
|
108
|
+
}
|
|
109
|
+
/** Apply {@link cleanAgentToolEntry} across the agent-tool catalog. */
|
|
110
|
+
function cleanAgentToolMap(map) {
|
|
111
|
+
const out = {};
|
|
112
|
+
for (const [name, entry] of Object.entries(map)) {
|
|
113
|
+
out[name] = cleanAgentToolEntry(entry);
|
|
114
|
+
}
|
|
115
|
+
return out;
|
|
116
|
+
}
|
|
90
117
|
/**
|
|
91
118
|
* Return a structurally-normalized copy of an untrusted draft: illegal
|
|
92
119
|
* wrapper/entry keys stripped, inner schemas canonicalized. Pure — never
|
|
@@ -132,10 +159,7 @@ export function normalizeDraft(draft) {
|
|
|
132
159
|
if (isRecord(ac['tools'])) {
|
|
133
160
|
out['agentCapabilities'] = {
|
|
134
161
|
...ac,
|
|
135
|
-
tools:
|
|
136
|
-
'inputSchema',
|
|
137
|
-
'outputSchema',
|
|
138
|
-
]),
|
|
162
|
+
tools: cleanAgentToolMap(ac['tools']),
|
|
139
163
|
};
|
|
140
164
|
}
|
|
141
165
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"normalize-schema.d.ts","sourceRoot":"","sources":["../src/normalize-schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;
|
|
1
|
+
{"version":3,"file":"normalize-schema.d.ts","sourceRoot":"","sources":["../src/normalize-schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AA6JH;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CA2BxD"}
|
package/dist/normalize-schema.js
CHANGED
|
@@ -106,6 +106,18 @@ function normalizeTypeValue(value, enumSibling) {
|
|
|
106
106
|
const alias = TYPE_ALIASES[lower];
|
|
107
107
|
if (alias !== undefined)
|
|
108
108
|
return alias;
|
|
109
|
+
if (lower.includes('|')) {
|
|
110
|
+
// Pipe-union string (`"STRING|null"`). Some models emit the union
|
|
111
|
+
// as one pipe-delimited string rather than the JSON Schema array
|
|
112
|
+
// form; recover the first valid non-null member, mirroring the
|
|
113
|
+
// array branch below (drops the nullable arm).
|
|
114
|
+
for (const member of lower.split('|')) {
|
|
115
|
+
const norm = normalizeTypeValue(member, enumSibling);
|
|
116
|
+
if (norm !== undefined && norm !== 'null')
|
|
117
|
+
return norm;
|
|
118
|
+
}
|
|
119
|
+
return undefined;
|
|
120
|
+
}
|
|
109
121
|
// Unrecognized garbage — drop the constraint rather than guess.
|
|
110
122
|
return undefined;
|
|
111
123
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache-key-probe.d.ts","sourceRoot":"","sources":["../../src/synth-bench/cache-key-probe.ts"],"names":[],"mappings":""}
|