@ggui-ai/negotiator 0.8.0 → 0.10.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/dist/contract-validators.d.ts.map +1 -1
- package/dist/contract-validators.js +7 -1
- package/dist/ensure-conforming-contract.d.ts +42 -16
- package/dist/ensure-conforming-contract.d.ts.map +1 -1
- package/dist/ensure-conforming-contract.js +36 -14
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/normalize-schema.d.ts.map +1 -1
- package/dist/normalize-schema.js +34 -19
- package/dist/salvage-draft.d.ts +64 -0
- package/dist/salvage-draft.d.ts.map +1 -0
- package/dist/salvage-draft.js +191 -0
- package/dist/synth-bench/corpus.js +1 -1
- package/dist/synth-bench/round-trip-score.js +3 -3
- package/dist/synth-bench/run-repair-bench.d.ts +11 -4
- package/dist/synth-bench/run-repair-bench.d.ts.map +1 -1
- package/dist/synth-bench/run-repair-bench.js +10 -5
- package/dist/synthesize-contract.js +4 -4
- package/package.json +3 -3
- package/src/contract-validators.ts +7 -1
- package/src/ensure-conforming-contract.ts +79 -26
- package/src/index.ts +14 -1
- package/src/normalize-schema.ts +36 -18
- package/src/salvage-draft.ts +204 -0
- package/src/synth-bench/corpus.ts +1 -1
- package/src/synth-bench/round-trip-score.ts +3 -3
- package/src/synth-bench/run-repair-bench.ts +23 -9
- package/src/synthesize-contract.ts +4 -4
|
@@ -1 +1 @@
|
|
|
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;
|
|
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;AA8JD;;;;;;;;;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"}
|
|
@@ -163,7 +163,13 @@ function nameImpliesMutation(args) {
|
|
|
163
163
|
function isEmptyPayloadSchema(schema) {
|
|
164
164
|
if (schema === undefined)
|
|
165
165
|
return true;
|
|
166
|
-
|
|
166
|
+
// `type` may be a draft-07 type ARRAY (`['object','null']`) since
|
|
167
|
+
// draft-2026-08-19 — treat a type set containing 'object' like the
|
|
168
|
+
// single-string form.
|
|
169
|
+
const declaresObject = Array.isArray(schema.type)
|
|
170
|
+
? schema.type.includes('object')
|
|
171
|
+
: schema.type === 'object';
|
|
172
|
+
if (!declaresObject)
|
|
167
173
|
return false;
|
|
168
174
|
if (schema.properties === undefined)
|
|
169
175
|
return true;
|
|
@@ -13,10 +13,14 @@
|
|
|
13
13
|
* seeded with the draft + the deterministic
|
|
14
14
|
* findings, looping until the gate is green
|
|
15
15
|
* (origin: 'synth')
|
|
16
|
-
* - repair impossible →
|
|
17
|
-
* (LLM down / provider
|
|
18
|
-
* can't synth / budget
|
|
19
|
-
* exhausted)
|
|
16
|
+
* - repair impossible → the conforming SUBSET of the draft (every
|
|
17
|
+
* (LLM down / provider refused entry dropped and reported;
|
|
18
|
+
* can't synth / budget origin 'synth', method 'salvaged-subset')
|
|
19
|
+
* exhausted) — or, when nothing usable survives, a
|
|
20
|
+
* DECLINE (`contract: null`, method
|
|
21
|
+
* 'declined') with the findings. NEVER
|
|
22
|
+
* throws; NEVER an empty contract on the
|
|
23
|
+
* agent's behalf (ggui#523 item 3).
|
|
20
24
|
*
|
|
21
25
|
* Determinism lives in the GATE (`lintContract`), never in the repair.
|
|
22
26
|
* The repair LLM is non-deterministic, but the loop only exits when the
|
|
@@ -31,34 +35,56 @@
|
|
|
31
35
|
*/
|
|
32
36
|
import { type DataContract, type GadgetDescriptor, type SuggestionFinding } from '@ggui-ai/protocol';
|
|
33
37
|
import type { LLMCaller } from './llm-caller.js';
|
|
34
|
-
|
|
38
|
+
/**
|
|
39
|
+
* How a conforming contract was produced — finer-grained than `origin`,
|
|
40
|
+
* for telemetry (the efficiency tiers):
|
|
41
|
+
* - `verbatim` — draft was clean; returned as-is (origin agent).
|
|
42
|
+
* - `normalized` — deterministic fix only, NO LLM (origin synth).
|
|
43
|
+
* - `llm-repair` — the bounded LLM repair loop ran (origin synth).
|
|
44
|
+
* - `salvaged-subset` — unrepairable within budget; the conforming
|
|
45
|
+
* SUBSET of the draft, refused entries dropped
|
|
46
|
+
* and reported (origin synth).
|
|
47
|
+
*/
|
|
48
|
+
export type EnsureConformingMethod = 'verbatim' | 'normalized' | 'llm-repair' | 'salvaged-subset';
|
|
49
|
+
/** A conforming contract was produced (the common case). */
|
|
50
|
+
export interface EnsureConformingAccepted {
|
|
35
51
|
/** A contract guaranteed to pass `lintContract` with zero errors. */
|
|
36
52
|
readonly contract: DataContract;
|
|
37
53
|
/**
|
|
38
54
|
* - `'agent'` — the draft was already conforming; returned verbatim.
|
|
39
55
|
* - `'synth'` — the draft had errors; this is the repaired result
|
|
40
|
-
* (or the
|
|
56
|
+
* (or the salvaged subset when repair was impossible).
|
|
41
57
|
*/
|
|
42
58
|
readonly origin: 'agent' | 'synth';
|
|
43
|
-
|
|
44
|
-
* How the conforming contract was produced — finer-grained than
|
|
45
|
-
* `origin`, for telemetry (the efficiency tiers):
|
|
46
|
-
* - `verbatim` — draft was clean; returned as-is (origin agent).
|
|
47
|
-
* - `normalized` — deterministic fix only, NO LLM (origin synth).
|
|
48
|
-
* - `llm-repair` — the bounded LLM repair loop ran (origin synth).
|
|
49
|
-
* - `fallback-empty`— unrepairable; minimal `{}` contract (origin synth).
|
|
50
|
-
*/
|
|
51
|
-
readonly method: 'verbatim' | 'normalized' | 'llm-repair' | 'fallback-empty';
|
|
59
|
+
readonly method: EnsureConformingMethod;
|
|
52
60
|
/**
|
|
53
61
|
* Findings surfaced to the agent. On `origin: 'agent'`, any hygiene
|
|
54
62
|
* warnings on the (valid) draft. On `origin: 'synth'`, the ERROR
|
|
55
63
|
* findings that rejected the agent's draft — so the agent-side model
|
|
56
|
-
* learns what it got wrong, even though we repaired it.
|
|
64
|
+
* learns what it got wrong, even though we repaired it. On
|
|
65
|
+
* `salvaged-subset` they include one finding per dropped entry.
|
|
57
66
|
*/
|
|
58
67
|
readonly findings: readonly SuggestionFinding[];
|
|
59
68
|
/** Operator- + LLM-readable explanation. */
|
|
60
69
|
readonly reasoning: string;
|
|
61
70
|
}
|
|
71
|
+
/**
|
|
72
|
+
* Nothing in the draft could be kept: repair failed AND no entry
|
|
73
|
+
* survives the gate. There is no contract to propose — the caller
|
|
74
|
+
* answers `action: 'declined'` with the findings and the agent fixes
|
|
75
|
+
* and re-handshakes. This is what replaced the empty-contract fallback:
|
|
76
|
+
* a hollow "success" was indistinguishable from a rejection and the
|
|
77
|
+
* observed recovery was a field-by-field bisect (ggui#523 item 3).
|
|
78
|
+
*/
|
|
79
|
+
export interface EnsureConformingDeclined {
|
|
80
|
+
readonly contract: null;
|
|
81
|
+
readonly origin: 'agent';
|
|
82
|
+
readonly method: 'declined';
|
|
83
|
+
/** The ERROR findings that rejected the draft — every one of them. */
|
|
84
|
+
readonly findings: readonly SuggestionFinding[];
|
|
85
|
+
readonly reasoning: string;
|
|
86
|
+
}
|
|
87
|
+
export type EnsureConformingResult = EnsureConformingAccepted | EnsureConformingDeclined;
|
|
62
88
|
export declare function ensureConformingContract(deps: {
|
|
63
89
|
readonly llm: LLMCaller;
|
|
64
90
|
}, args: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ensure-conforming-contract.d.ts","sourceRoot":"","sources":["../src/ensure-conforming-contract.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"ensure-conforming-contract.d.ts","sourceRoot":"","sources":["../src/ensure-conforming-contract.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,OAAO,EAGL,KAAK,YAAY,EACjB,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EACvB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAKjD;;;;;;;;;GASG;AACH,MAAM,MAAM,sBAAsB,GAC9B,UAAU,GACV,YAAY,GACZ,YAAY,GACZ,iBAAiB,CAAC;AAEtB,4DAA4D;AAC5D,MAAM,WAAW,wBAAwB;IACvC,qEAAqE;IACrE,QAAQ,CAAC,QAAQ,EAAE,YAAY,CAAC;IAChC;;;;OAIG;IACH,QAAQ,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC;IACnC,QAAQ,CAAC,MAAM,EAAE,sBAAsB,CAAC;IACxC;;;;;;OAMG;IACH,QAAQ,CAAC,QAAQ,EAAE,SAAS,iBAAiB,EAAE,CAAC;IAChD,4CAA4C;IAC5C,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,QAAQ,EAAE,IAAI,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,sEAAsE;IACtE,QAAQ,CAAC,QAAQ,EAAE,SAAS,iBAAiB,EAAE,CAAC;IAChD,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,MAAM,sBAAsB,GAAG,wBAAwB,GAAG,wBAAwB,CAAC;AAEzF,wBAAsB,wBAAwB,CAC5C,IAAI,EAAE;IAAE,QAAQ,CAAC,GAAG,EAAE,SAAS,CAAA;CAAE,EACjC,IAAI,EAAE;IACJ,oEAAoE;IACpE,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,UAAU,CAAC,EAAE,SAAS,gBAAgB,EAAE,CAAC;CACnD,GACA,OAAO,CAAC,sBAAsB,CAAC,CAkHjC"}
|
|
@@ -13,10 +13,14 @@
|
|
|
13
13
|
* seeded with the draft + the deterministic
|
|
14
14
|
* findings, looping until the gate is green
|
|
15
15
|
* (origin: 'synth')
|
|
16
|
-
* - repair impossible →
|
|
17
|
-
* (LLM down / provider
|
|
18
|
-
* can't synth / budget
|
|
19
|
-
* exhausted)
|
|
16
|
+
* - repair impossible → the conforming SUBSET of the draft (every
|
|
17
|
+
* (LLM down / provider refused entry dropped and reported;
|
|
18
|
+
* can't synth / budget origin 'synth', method 'salvaged-subset')
|
|
19
|
+
* exhausted) — or, when nothing usable survives, a
|
|
20
|
+
* DECLINE (`contract: null`, method
|
|
21
|
+
* 'declined') with the findings. NEVER
|
|
22
|
+
* throws; NEVER an empty contract on the
|
|
23
|
+
* agent's behalf (ggui#523 item 3).
|
|
20
24
|
*
|
|
21
25
|
* Determinism lives in the GATE (`lintContract`), never in the repair.
|
|
22
26
|
* The repair LLM is non-deterministic, but the loop only exits when the
|
|
@@ -32,8 +36,7 @@
|
|
|
32
36
|
import { lintContract, dataContractSchema, } from '@ggui-ai/protocol';
|
|
33
37
|
import { synthesizeContract } from './synthesize-contract.js';
|
|
34
38
|
import { normalizeDraft } from './normalize-draft.js';
|
|
35
|
-
|
|
36
|
-
const EMPTY_CONTRACT = {};
|
|
39
|
+
import { salvageConformingSubset } from './salvage-draft.js';
|
|
37
40
|
export async function ensureConformingContract(deps, args) {
|
|
38
41
|
const lint = lintContract(args.draft);
|
|
39
42
|
const warnFindings = lint.warnings.map((w) => ({
|
|
@@ -101,15 +104,34 @@ export async function ensureConformingContract(deps, args) {
|
|
|
101
104
|
};
|
|
102
105
|
}
|
|
103
106
|
// Repair impossible (LLM down, provider can't synthesize, or the
|
|
104
|
-
// repair budget exhausted).
|
|
105
|
-
//
|
|
106
|
-
//
|
|
107
|
-
//
|
|
107
|
+
// repair budget exhausted). Keep what conforms: drop exactly the
|
|
108
|
+
// entries the gate refuses, report each drop, and propose the rest —
|
|
109
|
+
// the agent's own draft minus the parts the protocol rejected. Never
|
|
110
|
+
// an empty contract dressed as a proposal.
|
|
111
|
+
const salvaged = salvageConformingSubset(normalized);
|
|
112
|
+
if (salvaged !== null) {
|
|
113
|
+
const droppedPaths = salvaged.dropped.map((d) => d.path);
|
|
114
|
+
return {
|
|
115
|
+
contract: salvaged.contract,
|
|
116
|
+
origin: 'synth',
|
|
117
|
+
method: 'salvaged-subset',
|
|
118
|
+
findings: [...errorFindings, ...salvaged.dropped],
|
|
119
|
+
reasoning: `could not repair the agent draft within budget (${synth.reason}); ` +
|
|
120
|
+
`proposing the conforming SUBSET of your draft — dropped ${droppedPaths.length} ` +
|
|
121
|
+
`entr${droppedPaths.length === 1 ? 'y' : 'ies'} the protocol refused (${droppedPaths.join(', ')}). ` +
|
|
122
|
+
`Each drop is a finding: fix those entries and re-handshake, or render this subset ` +
|
|
123
|
+
`and re-declare them via ggui_render override.`,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
// Nothing usable survives. Decline: there is no contract to propose,
|
|
127
|
+
// and the findings say exactly why.
|
|
108
128
|
return {
|
|
109
|
-
contract:
|
|
110
|
-
origin: '
|
|
111
|
-
method: '
|
|
129
|
+
contract: null,
|
|
130
|
+
origin: 'agent',
|
|
131
|
+
method: 'declined',
|
|
112
132
|
findings: errorFindings,
|
|
113
|
-
reasoning: `could not repair the agent draft within budget (${synth.reason})
|
|
133
|
+
reasoning: `declined: could not repair the agent draft within budget (${synth.reason}) and no entry of it ` +
|
|
134
|
+
`passes the contract gate — nothing to propose. Fix the findings (every one names its path) ` +
|
|
135
|
+
`and re-handshake; do not render against this handshake.`,
|
|
114
136
|
};
|
|
115
137
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -31,8 +31,9 @@ export type { RerankCandidate, RerankDecision, RerankQuery, } from './llm-rerank
|
|
|
31
31
|
export { synthesizeContract } from './synthesize-contract.js';
|
|
32
32
|
export type { SynthesizeContractResult } from './synthesize-contract.js';
|
|
33
33
|
export { ensureConformingContract } from './ensure-conforming-contract.js';
|
|
34
|
-
export type { EnsureConformingResult } from './ensure-conforming-contract.js';
|
|
34
|
+
export type { EnsureConformingAccepted, EnsureConformingDeclined, EnsureConformingMethod, EnsureConformingResult, } from './ensure-conforming-contract.js';
|
|
35
35
|
export { normalizeDraft } from './normalize-draft.js';
|
|
36
|
+
export { salvageConformingSubset, declaresAnySurface, type SalvageResult, } from './salvage-draft.js';
|
|
36
37
|
export { validateContractRedundancy, validateContractNovelty, formatValidationFindings, } from './contract-validators.js';
|
|
37
38
|
export type { ContractValidationFinding, ContractValidationFindingKind, ContractValidationResult, ContractValidationNoveltyDeps, ContractValidationNoveltyOptions, } from './contract-validators.js';
|
|
38
39
|
//# 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;;;;;;;;;;;;;;;;;;;;;;;;;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,
|
|
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,EACV,wBAAwB,EACxB,wBAAwB,EACxB,sBAAsB,EACtB,sBAAsB,GACvB,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAItD,OAAO,EACL,uBAAuB,EACvB,kBAAkB,EAClB,KAAK,aAAa,GACnB,MAAM,oBAAoB,CAAC;AAC5B,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
|
@@ -29,4 +29,8 @@ export { rerankCandidates } from './llm-rerank.js';
|
|
|
29
29
|
export { synthesizeContract } from './synthesize-contract.js';
|
|
30
30
|
export { ensureConformingContract } from './ensure-conforming-contract.js';
|
|
31
31
|
export { normalizeDraft } from './normalize-draft.js';
|
|
32
|
+
// The last-resort tier that replaced the empty-contract fallback (ggui#523
|
|
33
|
+
// item 3): keep the conforming subset of a draft, or decline. Shared with
|
|
34
|
+
// the handlers' no-LLM paths so every producer answers the same way.
|
|
35
|
+
export { salvageConformingSubset, declaresAnySurface, } from './salvage-draft.js';
|
|
32
36
|
export { validateContractRedundancy, validateContractNovelty, formatValidationFindings, } from './contract-validators.js';
|
|
@@ -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;AA+KH;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CA2BxD"}
|
package/dist/normalize-schema.js
CHANGED
|
@@ -109,34 +109,49 @@ function normalizeTypeValue(value, enumSibling) {
|
|
|
109
109
|
if (lower.includes('|')) {
|
|
110
110
|
// Pipe-union string (`"STRING|null"`). Some models emit the union
|
|
111
111
|
// as one pipe-delimited string rather than the JSON Schema array
|
|
112
|
-
// form; recover
|
|
113
|
-
//
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
}
|
|
119
|
-
return undefined;
|
|
112
|
+
// form; recover it AS the draft-07 array form — since
|
|
113
|
+
// draft-2026-08-19 the protocol's JsonSchema.type admits type
|
|
114
|
+
// arrays, and the nullable arm is load-bearing (schema-precise
|
|
115
|
+
// render: dropping it narrows the contract and turns the agent's
|
|
116
|
+
// legal `null` into a contract_violation).
|
|
117
|
+
return normalizeTypeMembers(lower.split('|'), enumSibling);
|
|
120
118
|
}
|
|
121
119
|
// Unrecognized garbage — drop the constraint rather than guess.
|
|
122
120
|
return undefined;
|
|
123
121
|
}
|
|
124
122
|
if (Array.isArray(value)) {
|
|
125
|
-
//
|
|
126
|
-
//
|
|
127
|
-
//
|
|
128
|
-
for
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
if (norm !== undefined && norm !== 'null')
|
|
132
|
-
return norm;
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
return 'string';
|
|
123
|
+
// Draft-07 union type (`["string", "null"]`) — PRESERVED, with
|
|
124
|
+
// each member normalized individually (invalid members drop). The
|
|
125
|
+
// pre-draft-2026-08-19 behavior collapsed to the first non-null
|
|
126
|
+
// member; see the pipe-union note above for why that narrowing is
|
|
127
|
+
// now a contract-violation factory.
|
|
128
|
+
return normalizeTypeMembers(value, enumSibling) ?? 'string';
|
|
136
129
|
}
|
|
137
130
|
// `type` was a number / object / boolean — meaningless; drop it.
|
|
138
131
|
return undefined;
|
|
139
132
|
}
|
|
133
|
+
/**
|
|
134
|
+
* Normalize a list of candidate type members: each member runs through
|
|
135
|
+
* {@link normalizeTypeValue} (case folding, aliases, drop-words),
|
|
136
|
+
* survivors dedupe in order. Two-plus survivors → the draft-07 array
|
|
137
|
+
* form; exactly one → the plain string form; none → `undefined`.
|
|
138
|
+
*/
|
|
139
|
+
function normalizeTypeMembers(members, enumSibling) {
|
|
140
|
+
const survivors = [];
|
|
141
|
+
for (const member of members) {
|
|
142
|
+
if (typeof member !== 'string')
|
|
143
|
+
continue;
|
|
144
|
+
const norm = normalizeTypeValue(member, enumSibling);
|
|
145
|
+
if (typeof norm === 'string' && !survivors.includes(norm)) {
|
|
146
|
+
survivors.push(norm);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
if (survivors.length === 0)
|
|
150
|
+
return undefined;
|
|
151
|
+
if (survivors.length === 1)
|
|
152
|
+
return survivors[0];
|
|
153
|
+
return survivors;
|
|
154
|
+
}
|
|
140
155
|
/** Normalize a map of name → schema (e.g. `properties`). */
|
|
141
156
|
function normalizeSchemaMap(value) {
|
|
142
157
|
if (typeof value !== 'object' || value === null || Array.isArray(value)) {
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `salvageConformingSubset` — the last-resort tier that replaces the
|
|
3
|
+
* empty-contract fallback (ggui#523 item 3, "make `{}` impossible").
|
|
4
|
+
*
|
|
5
|
+
* When a draft cannot be made to conform any other way (no LLM bound,
|
|
6
|
+
* provider down, repair budget exhausted), the negotiator used to hand
|
|
7
|
+
* back the trivially-conforming `{}` with the findings attached — a
|
|
8
|
+
* response the agent cannot tell apart from success: `action: create`,
|
|
9
|
+
* a handshakeId, a `nextStep`, and a contract that declares NOTHING. The
|
|
10
|
+
* paired render then paints a hollow shell, and the observed recovery
|
|
11
|
+
* is a field-by-field bisect (No Silent Block, applied to contracts).
|
|
12
|
+
*
|
|
13
|
+
* This tier instead keeps what DOES conform. It deletes exactly the
|
|
14
|
+
* entries the deterministic gate names — one offending property,
|
|
15
|
+
* action, stream, context slot, or tool at a time (a bad sub-field
|
|
16
|
+
* first, the whole entry only if that was not enough) — re-lints, and
|
|
17
|
+
* repeats until the gate is green. The result is the agent's own draft
|
|
18
|
+
* minus the parts the protocol refused, with every drop reported as a
|
|
19
|
+
* finding, so the agent sees exactly what to fix in one read.
|
|
20
|
+
*
|
|
21
|
+
* It returns `null` — "nothing salvageable" — when what survives
|
|
22
|
+
* declares no surface at all (no props, actions, streams, context
|
|
23
|
+
* slots, or tools), or when an error is structural (the root is not an
|
|
24
|
+
* object). `null` is the DECLINE signal: the caller answers
|
|
25
|
+
* `action: 'declined'` with the findings, never a proposal. Between the
|
|
26
|
+
* two, no path produces an empty contract on the agent's behalf.
|
|
27
|
+
*
|
|
28
|
+
* Deterministic and pure: no LLM, no mutation of the input, no throw.
|
|
29
|
+
* Bounded: every iteration removes at least one key or returns.
|
|
30
|
+
*/
|
|
31
|
+
import { type DataContract, type SuggestionFinding } from '@ggui-ai/protocol';
|
|
32
|
+
export interface SalvageResult {
|
|
33
|
+
/** A contract guaranteed to pass `lintContract` with zero errors, and to declare at least one surface. */
|
|
34
|
+
readonly contract: DataContract;
|
|
35
|
+
/**
|
|
36
|
+
* What was removed to get there — one finding per deleted key, in
|
|
37
|
+
* deletion order, carrying the gate's own code + message for that
|
|
38
|
+
* path. Surfaced to the agent verbatim.
|
|
39
|
+
*/
|
|
40
|
+
readonly dropped: readonly SuggestionFinding[];
|
|
41
|
+
}
|
|
42
|
+
/** Does the contract declare at least one surface an agent could use? */
|
|
43
|
+
export declare function declaresAnySurface(contract: DataContract): boolean;
|
|
44
|
+
/**
|
|
45
|
+
* Where to cut for an offending path. Returns the key path to delete
|
|
46
|
+
* (as segments) or `null` when the error is structural.
|
|
47
|
+
*
|
|
48
|
+
* propsSpec.properties.<k>[.…] → try the sub-field, then the property
|
|
49
|
+
* actionSpec|streamSpec|contextSpec.<k>[.…] → sub-field, then the entry
|
|
50
|
+
* agentCapabilities.tools.<k>[.…] → sub-field, then the tool
|
|
51
|
+
* <spec>[.other] → the whole spec
|
|
52
|
+
* <unknown-top-level-key>[.…] → that key (retired fields, typos)
|
|
53
|
+
* <root> → structural, null
|
|
54
|
+
*
|
|
55
|
+
* `leafFirst` picks the deeper cut when one exists; the caller retries
|
|
56
|
+
* with `false` if that cut did not clear the entry.
|
|
57
|
+
*/
|
|
58
|
+
export declare function cutFor(path: string, leafFirst: boolean): readonly string[] | null;
|
|
59
|
+
/**
|
|
60
|
+
* Keep the conforming subset of `draft` (already normalized by the
|
|
61
|
+
* caller, ideally). See the module docstring for the contract.
|
|
62
|
+
*/
|
|
63
|
+
export declare function salvageConformingSubset(draft: unknown): SalvageResult | null;
|
|
64
|
+
//# sourceMappingURL=salvage-draft.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"salvage-draft.d.ts","sourceRoot":"","sources":["../src/salvage-draft.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,OAAO,EAGL,KAAK,YAAY,EACjB,KAAK,iBAAiB,EACvB,MAAM,mBAAmB,CAAC;AAkB3B,MAAM,WAAW,aAAa;IAC5B,0GAA0G;IAC1G,QAAQ,CAAC,QAAQ,EAAE,YAAY,CAAC;IAChC;;;;OAIG;IACH,QAAQ,CAAC,OAAO,EAAE,SAAS,iBAAiB,EAAE,CAAC;CAChD;AAMD,yEAAyE;AACzE,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,YAAY,GAAG,OAAO,CASlE;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,GAAG,SAAS,MAAM,EAAE,GAAG,IAAI,CA6BjF;AA8BD;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,OAAO,GAAG,aAAa,GAAG,IAAI,CA8C5E"}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `salvageConformingSubset` — the last-resort tier that replaces the
|
|
3
|
+
* empty-contract fallback (ggui#523 item 3, "make `{}` impossible").
|
|
4
|
+
*
|
|
5
|
+
* When a draft cannot be made to conform any other way (no LLM bound,
|
|
6
|
+
* provider down, repair budget exhausted), the negotiator used to hand
|
|
7
|
+
* back the trivially-conforming `{}` with the findings attached — a
|
|
8
|
+
* response the agent cannot tell apart from success: `action: create`,
|
|
9
|
+
* a handshakeId, a `nextStep`, and a contract that declares NOTHING. The
|
|
10
|
+
* paired render then paints a hollow shell, and the observed recovery
|
|
11
|
+
* is a field-by-field bisect (No Silent Block, applied to contracts).
|
|
12
|
+
*
|
|
13
|
+
* This tier instead keeps what DOES conform. It deletes exactly the
|
|
14
|
+
* entries the deterministic gate names — one offending property,
|
|
15
|
+
* action, stream, context slot, or tool at a time (a bad sub-field
|
|
16
|
+
* first, the whole entry only if that was not enough) — re-lints, and
|
|
17
|
+
* repeats until the gate is green. The result is the agent's own draft
|
|
18
|
+
* minus the parts the protocol refused, with every drop reported as a
|
|
19
|
+
* finding, so the agent sees exactly what to fix in one read.
|
|
20
|
+
*
|
|
21
|
+
* It returns `null` — "nothing salvageable" — when what survives
|
|
22
|
+
* declares no surface at all (no props, actions, streams, context
|
|
23
|
+
* slots, or tools), or when an error is structural (the root is not an
|
|
24
|
+
* object). `null` is the DECLINE signal: the caller answers
|
|
25
|
+
* `action: 'declined'` with the findings, never a proposal. Between the
|
|
26
|
+
* two, no path produces an empty contract on the agent's behalf.
|
|
27
|
+
*
|
|
28
|
+
* Deterministic and pure: no LLM, no mutation of the input, no throw.
|
|
29
|
+
* Bounded: every iteration removes at least one key or returns.
|
|
30
|
+
*/
|
|
31
|
+
import { dataContractSchema, lintContract, } from '@ggui-ai/protocol';
|
|
32
|
+
/** The six top-level spec keys the DataContract declares. */
|
|
33
|
+
const SPEC_KEYS = new Set([
|
|
34
|
+
'propsSpec',
|
|
35
|
+
'actionSpec',
|
|
36
|
+
'streamSpec',
|
|
37
|
+
'contextSpec',
|
|
38
|
+
'agentCapabilities',
|
|
39
|
+
'clientCapabilities',
|
|
40
|
+
]);
|
|
41
|
+
/** Spec maps whose direct children are the droppable entries. */
|
|
42
|
+
const ENTRY_MAP_SPECS = new Set(['actionSpec', 'streamSpec', 'contextSpec']);
|
|
43
|
+
/** Hard cap on gate iterations — every iteration deletes ≥1 key. */
|
|
44
|
+
const MAX_ROUNDS = 100;
|
|
45
|
+
function isRecord(value) {
|
|
46
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
47
|
+
}
|
|
48
|
+
/** Does the contract declare at least one surface an agent could use? */
|
|
49
|
+
export function declaresAnySurface(contract) {
|
|
50
|
+
const props = contract.propsSpec?.properties;
|
|
51
|
+
if (props !== undefined && Object.keys(props).length > 0)
|
|
52
|
+
return true;
|
|
53
|
+
if (contract.actionSpec !== undefined && Object.keys(contract.actionSpec).length > 0)
|
|
54
|
+
return true;
|
|
55
|
+
if (contract.streamSpec !== undefined && Object.keys(contract.streamSpec).length > 0)
|
|
56
|
+
return true;
|
|
57
|
+
if (contract.contextSpec !== undefined && Object.keys(contract.contextSpec).length > 0)
|
|
58
|
+
return true;
|
|
59
|
+
const tools = contract.agentCapabilities?.tools;
|
|
60
|
+
if (tools !== undefined && Object.keys(tools).length > 0)
|
|
61
|
+
return true;
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Where to cut for an offending path. Returns the key path to delete
|
|
66
|
+
* (as segments) or `null` when the error is structural.
|
|
67
|
+
*
|
|
68
|
+
* propsSpec.properties.<k>[.…] → try the sub-field, then the property
|
|
69
|
+
* actionSpec|streamSpec|contextSpec.<k>[.…] → sub-field, then the entry
|
|
70
|
+
* agentCapabilities.tools.<k>[.…] → sub-field, then the tool
|
|
71
|
+
* <spec>[.other] → the whole spec
|
|
72
|
+
* <unknown-top-level-key>[.…] → that key (retired fields, typos)
|
|
73
|
+
* <root> → structural, null
|
|
74
|
+
*
|
|
75
|
+
* `leafFirst` picks the deeper cut when one exists; the caller retries
|
|
76
|
+
* with `false` if that cut did not clear the entry.
|
|
77
|
+
*/
|
|
78
|
+
export function cutFor(path, leafFirst) {
|
|
79
|
+
if (path === '' || path === '<root>')
|
|
80
|
+
return null;
|
|
81
|
+
const seg = path.split('.');
|
|
82
|
+
const head = seg[0];
|
|
83
|
+
if (head === 'propsSpec') {
|
|
84
|
+
if (seg[1] === 'properties' && seg.length >= 3) {
|
|
85
|
+
const entry = seg.slice(0, 3);
|
|
86
|
+
return leafFirst && seg.length > 3 ? seg : entry;
|
|
87
|
+
}
|
|
88
|
+
return ['propsSpec'];
|
|
89
|
+
}
|
|
90
|
+
if (ENTRY_MAP_SPECS.has(head)) {
|
|
91
|
+
if (seg.length >= 2) {
|
|
92
|
+
const entry = seg.slice(0, 2);
|
|
93
|
+
return leafFirst && seg.length > 2 ? seg : entry;
|
|
94
|
+
}
|
|
95
|
+
return [head];
|
|
96
|
+
}
|
|
97
|
+
if (head === 'agentCapabilities') {
|
|
98
|
+
if (seg[1] === 'tools' && seg.length >= 3) {
|
|
99
|
+
const entry = seg.slice(0, 3);
|
|
100
|
+
return leafFirst && seg.length > 3 ? seg : entry;
|
|
101
|
+
}
|
|
102
|
+
return ['agentCapabilities'];
|
|
103
|
+
}
|
|
104
|
+
if (SPEC_KEYS.has(head))
|
|
105
|
+
return [head];
|
|
106
|
+
// Unknown / retired top-level field — the whole key goes.
|
|
107
|
+
return [head];
|
|
108
|
+
}
|
|
109
|
+
/** Delete `keyPath` from a structural copy of `root`; false if absent. */
|
|
110
|
+
function deleteAt(root, keyPath) {
|
|
111
|
+
let node = root;
|
|
112
|
+
for (let i = 0; i < keyPath.length - 1; i += 1) {
|
|
113
|
+
const next = node[keyPath[i]];
|
|
114
|
+
if (!isRecord(next))
|
|
115
|
+
return false;
|
|
116
|
+
// Copy-on-write down the path so the input is never mutated.
|
|
117
|
+
const copy = { ...next };
|
|
118
|
+
node[keyPath[i]] = copy;
|
|
119
|
+
node = copy;
|
|
120
|
+
}
|
|
121
|
+
const leaf = keyPath[keyPath.length - 1];
|
|
122
|
+
if (!(leaf in node))
|
|
123
|
+
return false;
|
|
124
|
+
delete node[leaf];
|
|
125
|
+
// A dropped prop must leave `propsSpec.required` too — a stale name
|
|
126
|
+
// there is its own gate error and would only cost another round.
|
|
127
|
+
if (keyPath.length === 3 && keyPath[0] === 'propsSpec' && keyPath[1] === 'properties') {
|
|
128
|
+
const ps = root['propsSpec'];
|
|
129
|
+
if (isRecord(ps) && Array.isArray(ps['required'])) {
|
|
130
|
+
root['propsSpec'] = {
|
|
131
|
+
...ps,
|
|
132
|
+
required: ps['required'].filter((name) => name !== leaf),
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return true;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Keep the conforming subset of `draft` (already normalized by the
|
|
140
|
+
* caller, ideally). See the module docstring for the contract.
|
|
141
|
+
*/
|
|
142
|
+
export function salvageConformingSubset(draft) {
|
|
143
|
+
if (!isRecord(draft))
|
|
144
|
+
return null;
|
|
145
|
+
let working = { ...draft };
|
|
146
|
+
const dropped = [];
|
|
147
|
+
/** Cuts already tried at leaf depth for a path — the retry goes to the entry. */
|
|
148
|
+
const leafTried = new Set();
|
|
149
|
+
for (let round = 0; round < MAX_ROUNDS; round += 1) {
|
|
150
|
+
const lint = lintContract(working);
|
|
151
|
+
if (lint.errors.length === 0) {
|
|
152
|
+
// Shape passed ⇒ the strict parse cannot throw.
|
|
153
|
+
const contract = dataContractSchema.parse(working);
|
|
154
|
+
return declaresAnySurface(contract) ? { contract, dropped } : null;
|
|
155
|
+
}
|
|
156
|
+
let cutSomething = false;
|
|
157
|
+
for (const issue of lint.errors) {
|
|
158
|
+
const leafFirst = !leafTried.has(issue.path);
|
|
159
|
+
const cut = cutFor(issue.path, leafFirst);
|
|
160
|
+
if (cut === null)
|
|
161
|
+
return null; // structural — nothing to keep
|
|
162
|
+
const cutKey = cut.join('.');
|
|
163
|
+
if (leafFirst && cutKey === issue.path)
|
|
164
|
+
leafTried.add(issue.path);
|
|
165
|
+
const next = { ...working };
|
|
166
|
+
if (!deleteAt(next, cut)) {
|
|
167
|
+
// The path names something that is not there (a reference
|
|
168
|
+
// target, a computed check) — fall back to the entry cut once,
|
|
169
|
+
// then give up on this issue for the round.
|
|
170
|
+
if (leafFirst) {
|
|
171
|
+
leafTried.add(issue.path);
|
|
172
|
+
const entryCut = cutFor(issue.path, false);
|
|
173
|
+
if (entryCut !== null && deleteAt(next, entryCut)) {
|
|
174
|
+
working = next;
|
|
175
|
+
dropped.push({ code: issue.code, severity: 'error', path: entryCut.join('.'), message: issue.message });
|
|
176
|
+
cutSomething = true;
|
|
177
|
+
break;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
working = next;
|
|
183
|
+
dropped.push({ code: issue.code, severity: 'error', path: cutKey, message: issue.message });
|
|
184
|
+
cutSomething = true;
|
|
185
|
+
break; // one cut per round — re-lint before the next decision
|
|
186
|
+
}
|
|
187
|
+
if (!cutSomething)
|
|
188
|
+
return null; // no cut could be applied — structural
|
|
189
|
+
}
|
|
190
|
+
return null;
|
|
191
|
+
}
|
|
@@ -337,7 +337,7 @@ export const BENCH_CORPUS = [
|
|
|
337
337
|
'create',
|
|
338
338
|
'remove',
|
|
339
339
|
],
|
|
340
|
-
notes: 'agent-backed/persisted = the agent OWNS the items → todos seed on propsSpec (refreshed via
|
|
340
|
+
notes: 'agent-backed/persisted = the agent OWNS the items → todos seed on propsSpec (refreshed via ggui_amend); add/delete/toggle are discrete events on actionSpec. contextSpec has no agent-push channel, so an agent-owned persisted list there cannot round-trip — this is the round-trip-correct shape, aligned with list-message-thread / list-file-browser (both props-bearing agent-supplied collections).',
|
|
341
341
|
},
|
|
342
342
|
},
|
|
343
343
|
{
|
|
@@ -31,9 +31,9 @@
|
|
|
31
31
|
import { validatePropsData } from '@ggui-ai/protocol';
|
|
32
32
|
/**
|
|
33
33
|
* True when a contract declares none of the six spec surfaces — the
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
* carries no wire at all.
|
|
34
|
+
* empty contract the bench substitutes for a DECLINE (and the `{}` the
|
|
35
|
+
* retired fallback used to return for an unrepairable draft). Such a
|
|
36
|
+
* contract is structurally valid but carries no wire at all.
|
|
37
37
|
*/
|
|
38
38
|
function isEmptyContract(contract) {
|
|
39
39
|
return (contract.propsSpec === undefined &&
|
|
@@ -25,19 +25,26 @@
|
|
|
25
25
|
* deterministic scorer pinning lives in round-trip-score.test.ts.
|
|
26
26
|
*/
|
|
27
27
|
import type { DataContract, SuggestionFinding } from '@ggui-ai/protocol';
|
|
28
|
+
import { type EnsureConformingMethod } from '../ensure-conforming-contract.js';
|
|
28
29
|
import type { LLMCaller } from '../llm-caller.js';
|
|
29
30
|
import { type ScoreResult } from './run-bench.js';
|
|
30
31
|
import { type RoundTripScore } from './round-trip-score.js';
|
|
31
32
|
import { type BenchEntry } from './corpus.js';
|
|
32
33
|
export interface RepairBenchOutcome {
|
|
33
34
|
readonly entry: BenchEntry;
|
|
34
|
-
/**
|
|
35
|
-
|
|
35
|
+
/**
|
|
36
|
+
* The conforming contract ensureConformingContract produced — or
|
|
37
|
+
* `null` when it DECLINED (nothing in the draft could be kept; ggui#523
|
|
38
|
+
* item 3 retired the empty-contract fallback). Scored as the empty
|
|
39
|
+
* contract so the tiers stay comparable across that change.
|
|
40
|
+
*/
|
|
41
|
+
readonly contract: DataContract | null;
|
|
36
42
|
/** `agent` = clean draft returned verbatim; `synth` = repaired in-place. */
|
|
37
43
|
readonly origin: 'agent' | 'synth';
|
|
38
44
|
/** How the contract was produced (the efficiency tier): verbatim /
|
|
39
|
-
* normalized (deterministic, no LLM) / llm-repair /
|
|
40
|
-
|
|
45
|
+
* normalized (deterministic, no LLM) / llm-repair / salvaged-subset /
|
|
46
|
+
* declined. */
|
|
47
|
+
readonly method: EnsureConformingMethod | 'declined';
|
|
41
48
|
/** Structural shape score (ride-along secondary signal). */
|
|
42
49
|
readonly shape: ScoreResult;
|
|
43
50
|
/** Round-trip usability — null when the entry declares no round-trip
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run-repair-bench.d.ts","sourceRoot":"","sources":["../../src/synth-bench/run-repair-bench.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"run-repair-bench.d.ts","sourceRoot":"","sources":["../../src/synth-bench/run-repair-bench.ts"],"names":[],"mappings":"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"}
|