@ggui-ai/negotiator 0.24.0 → 0.25.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 +6 -1
- package/dist/ensure-conforming-contract.d.ts +12 -1
- package/dist/ensure-conforming-contract.d.ts.map +1 -1
- package/dist/ensure-conforming-contract.js +40 -6
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/llm-caller.d.ts +26 -0
- package/dist/llm-caller.d.ts.map +1 -1
- package/dist/llm-caller.js +9 -0
- package/dist/llm-rerank.d.ts +8 -9
- package/dist/llm-rerank.d.ts.map +1 -1
- package/dist/llm-rerank.js +14 -7
- package/dist/normalize-draft.d.ts +20 -0
- package/dist/normalize-draft.d.ts.map +1 -1
- package/dist/normalize-draft.js +63 -39
- package/dist/preserve-action-members.d.ts +79 -0
- package/dist/preserve-action-members.d.ts.map +1 -0
- package/dist/preserve-action-members.js +199 -0
- package/dist/rerank-eval/run-probe.js +1 -1
- package/dist/synthesize-contract.d.ts +11 -0
- package/dist/synthesize-contract.d.ts.map +1 -1
- package/dist/synthesize-contract.js +30 -2
- package/package.json +3 -3
- package/src/ensure-conforming-contract.ts +53 -7
- package/src/index.ts +2 -2
- package/src/llm-caller.ts +34 -0
- package/src/llm-rerank.ts +21 -18
- package/src/normalize-draft.ts +70 -39
- package/src/preserve-action-members.ts +224 -0
- package/src/rerank-eval/run-probe.ts +1 -1
- package/src/synthesize-contract.ts +47 -3
package/README.md
CHANGED
|
@@ -35,7 +35,12 @@ pnpm add @ggui-ai/negotiator
|
|
|
35
35
|
- **`ensureConformingContract(...)`** — the create-path guarantee: validates
|
|
36
36
|
an untrusted draft and, on errors, deterministically normalizes or
|
|
37
37
|
LLM-repairs it so the handshake always returns a contract that passes the
|
|
38
|
-
backstop. Never throws.
|
|
38
|
+
backstop. Never throws. A repair keeps every member the draft declared on
|
|
39
|
+
its actions (`oneShot`, `confirm`, `nextStep`, `description`, `example`,
|
|
40
|
+
`icon`) and names anything it cannot keep at its own path — the LLM
|
|
41
|
+
repair as `REPAIR_MEMBER_DROPPED` / `REPAIR_ENTRY_DROPPED` with the
|
|
42
|
+
gate's reason in the message, the salvaged subset with the gate's code
|
|
43
|
+
at the cut path — so a declaration is never lost silently.
|
|
39
44
|
- **`rerankCandidates(...)`** — LLM judge that re-ranks blueprint-match
|
|
40
45
|
retrieval candidates (the semantic-match decision used by
|
|
41
46
|
`decideHandshake`).
|
|
@@ -62,7 +62,18 @@ export interface EnsureConformingAccepted {
|
|
|
62
62
|
* warnings on the (valid) draft. On `origin: 'synth'`, the ERROR
|
|
63
63
|
* findings that rejected the agent's draft — so the agent-side model
|
|
64
64
|
* learns what it got wrong, even though we repaired it. On
|
|
65
|
-
* `
|
|
65
|
+
* `llm-repair` they additionally carry one `REPAIR_MEMBER_DROPPED` per
|
|
66
|
+
* declared action-entry member the repair could not keep and one
|
|
67
|
+
* `REPAIR_ENTRY_DROPPED` per draft action it no longer carries
|
|
68
|
+
* (ggui#1421 — a repair preserves the draft's declarations, `oneShot`
|
|
69
|
+
* above all, and names any it must drop, each at its own path with the
|
|
70
|
+
* gate's reason in the message; a member under repair is named by the
|
|
71
|
+
* gate's own finding at that same path).
|
|
72
|
+
* The error findings are the union of what the gate refused on the
|
|
73
|
+
* raw draft and on its normalized form (the gate stops after the
|
|
74
|
+
* shape phase, so the raw lint alone can hide a semantic finding). On
|
|
75
|
+
* `salvaged-subset` they include one finding per dropped entry, with
|
|
76
|
+
* the gate's own code at the cut path.
|
|
66
77
|
*/
|
|
67
78
|
readonly findings: readonly SuggestionFinding[];
|
|
68
79
|
/** Operator- + LLM-readable explanation. */
|
|
@@ -1 +1 @@
|
|
|
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
|
|
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;;;;;;;;;;;;;;;;;OAiBG;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,CAqJjC"}
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
*/
|
|
36
36
|
import { lintContract, dataContractSchema, } from '@ggui-ai/protocol';
|
|
37
37
|
import { synthesizeContract } from './synthesize-contract.js';
|
|
38
|
-
import { normalizeDraft } from './normalize-draft.js';
|
|
38
|
+
import { liftedRequiredNames, normalizeDraft } from './normalize-draft.js';
|
|
39
39
|
import { salvageConformingSubset } from './salvage-draft.js';
|
|
40
40
|
export async function ensureConformingContract(deps, args) {
|
|
41
41
|
const lint = lintContract(args.draft);
|
|
@@ -58,11 +58,22 @@ export async function ensureConformingContract(deps, args) {
|
|
|
58
58
|
reasoning: 'agent draft passed validateContract; accepted verbatim (origin: agent)',
|
|
59
59
|
};
|
|
60
60
|
}
|
|
61
|
+
// ggui#1454 — a wrapper-level `propsSpec.required: [...]` is refused by the
|
|
62
|
+
// gate as an unrecognized key and LIFTED by `normalizeDraft` onto the named
|
|
63
|
+
// entries (ggui#1432). The finding stays (the draft used a non-wire shape,
|
|
64
|
+
// and the agent should see that); its message says the key was honoured,
|
|
65
|
+
// so it reads as a repair, not a refusal to fix and re-handshake for.
|
|
66
|
+
const lifted = liftedRequiredNames(args.draft);
|
|
67
|
+
const liftNote = lifted.length > 0
|
|
68
|
+
? ` — the wrapper-level \`required\` is not a wire key; it was lifted into ${lifted
|
|
69
|
+
.map((name) => `propsSpec.properties.${name}.required = true`)
|
|
70
|
+
.join(', ')} (repaired, not refused)`
|
|
71
|
+
: '';
|
|
61
72
|
const errorFindings = lint.errors.map((e) => ({
|
|
62
73
|
code: e.code,
|
|
63
74
|
severity: 'error',
|
|
64
75
|
path: e.path,
|
|
65
|
-
message: e.message,
|
|
76
|
+
message: e.code === 'CTR_SHAPE_UNRECOGNIZED_KEYS' && e.path === 'propsSpec' ? `${e.message}${liftNote}` : e.message,
|
|
66
77
|
}));
|
|
67
78
|
// L3 — deterministic normalization tier. Most agent malformations are
|
|
68
79
|
// mechanical (stray illegal wrapper keys, non-canonical schema types).
|
|
@@ -72,6 +83,25 @@ export async function ensureConformingContract(deps, args) {
|
|
|
72
83
|
// LLM call). Semantic deficiencies fall through to the repair loop.
|
|
73
84
|
const normalized = normalizeDraft(args.draft);
|
|
74
85
|
const normLint = lintContract(normalized);
|
|
86
|
+
// The gate stops after the shape phase, so the RAW draft's lint may
|
|
87
|
+
// name only its mechanical errors while the NORMALIZED draft's lint
|
|
88
|
+
// reaches the semantic ones (a dangling `nextStep`). Every path the
|
|
89
|
+
// gate refused on either tree is the agent's to see (ggui#1421): the
|
|
90
|
+
// union, raw first, one finding per (code, path).
|
|
91
|
+
const gateFindings = [...errorFindings];
|
|
92
|
+
for (const e of normLint.errors) {
|
|
93
|
+
if (!gateFindings.some((f) => f.code === e.code && f.path === e.path)) {
|
|
94
|
+
gateFindings.push({ code: e.code, severity: 'error', path: e.path, message: e.message });
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/** `gateFindings` plus `more`, one finding per (code, path) — a drop the gate already named is not named twice. */
|
|
98
|
+
const withGateFindings = (more) => {
|
|
99
|
+
const out = [...gateFindings];
|
|
100
|
+
for (const f of more)
|
|
101
|
+
if (!out.some((g) => g.code === f.code && g.path === f.path))
|
|
102
|
+
out.push(f);
|
|
103
|
+
return out;
|
|
104
|
+
};
|
|
75
105
|
if (normLint.errors.length === 0) {
|
|
76
106
|
return {
|
|
77
107
|
contract: dataContractSchema.parse(normalized),
|
|
@@ -95,12 +125,16 @@ export async function ensureConformingContract(deps, args) {
|
|
|
95
125
|
});
|
|
96
126
|
if (synth.contract !== null &&
|
|
97
127
|
lintContract(synth.contract).errors.length === 0) {
|
|
128
|
+
const droppedPaths = synth.dropped.map((d) => d.path);
|
|
98
129
|
return {
|
|
99
130
|
contract: synth.contract,
|
|
100
131
|
origin: 'synth',
|
|
101
132
|
method: 'llm-repair',
|
|
102
|
-
findings:
|
|
103
|
-
reasoning: `repaired the agent draft to pass validateContract — ${synth.reason}
|
|
133
|
+
findings: withGateFindings(synth.dropped),
|
|
134
|
+
reasoning: `repaired the agent draft to pass validateContract — ${synth.reason}` +
|
|
135
|
+
(droppedPaths.length > 0
|
|
136
|
+
? `; dropped ${droppedPaths.length} declared action ${droppedPaths.length === 1 ? 'member/entry' : 'members/entries'} the repair could not keep (${droppedPaths.join(', ')}) — each is a finding`
|
|
137
|
+
: ''),
|
|
104
138
|
};
|
|
105
139
|
}
|
|
106
140
|
// Repair impossible (LLM down, provider can't synthesize, or the
|
|
@@ -115,7 +149,7 @@ export async function ensureConformingContract(deps, args) {
|
|
|
115
149
|
contract: salvaged.contract,
|
|
116
150
|
origin: 'synth',
|
|
117
151
|
method: 'salvaged-subset',
|
|
118
|
-
findings:
|
|
152
|
+
findings: withGateFindings(salvaged.dropped),
|
|
119
153
|
reasoning: `could not repair the agent draft within budget (${synth.reason}); ` +
|
|
120
154
|
`proposing the conforming SUBSET of your draft — dropped ${droppedPaths.length} ` +
|
|
121
155
|
`entr${droppedPaths.length === 1 ? 'y' : 'ies'} the protocol refused (${droppedPaths.join(', ')}). ` +
|
|
@@ -129,7 +163,7 @@ export async function ensureConformingContract(deps, args) {
|
|
|
129
163
|
contract: null,
|
|
130
164
|
origin: 'agent',
|
|
131
165
|
method: 'declined',
|
|
132
|
-
findings:
|
|
166
|
+
findings: gateFindings,
|
|
133
167
|
reasoning: `declined: could not repair the agent draft within budget (${synth.reason}) and no entry of it ` +
|
|
134
168
|
`passes the contract gate — nothing to propose. Fix the findings (every one names its path) ` +
|
|
135
169
|
`and re-handshake; do not render against this handshake.`,
|
package/dist/index.d.ts
CHANGED
|
@@ -25,8 +25,8 @@
|
|
|
25
25
|
* consumers need. Each additive export carries semver weight.
|
|
26
26
|
*/
|
|
27
27
|
export { hashContract, buildVariant } from './contract-hash.js';
|
|
28
|
-
export type { LLMCaller, LLMCallerConfig, ToolSchema } from './llm-caller.js';
|
|
29
|
-
export { llmRerankJudge, rerankCandidates } from './llm-rerank.js';
|
|
28
|
+
export type { LLMCaller, LLMCallerConfig, Metered, TokenUsage, ToolSchema } from './llm-caller.js';
|
|
29
|
+
export { llmRerankJudge, rerankCandidates, RERANK_SYSTEM_PROMPT } from './llm-rerank.js';
|
|
30
30
|
export type { RerankCandidate, RerankDecision, RerankJudge, RerankQuery, } from './llm-rerank.js';
|
|
31
31
|
export { synthesizeContract } from './synthesize-contract.js';
|
|
32
32
|
export type { SynthesizeContractResult } from './synthesize-contract.js';
|
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;
|
|
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,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AACnG,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AACzF,YAAY,EACV,eAAe,EACf,cAAc,EACd,WAAW,EACX,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
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
* consumers need. Each additive export carries semver weight.
|
|
26
26
|
*/
|
|
27
27
|
export { hashContract, buildVariant } from './contract-hash.js';
|
|
28
|
-
export { llmRerankJudge, rerankCandidates } from './llm-rerank.js';
|
|
28
|
+
export { llmRerankJudge, rerankCandidates, RERANK_SYSTEM_PROMPT } 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';
|
package/dist/llm-caller.d.ts
CHANGED
|
@@ -35,6 +35,15 @@
|
|
|
35
35
|
* that can't produce tool input at all simply omit this method;
|
|
36
36
|
* consumers fall back to `call` + regex JSON extraction. Absence is
|
|
37
37
|
* not an error.
|
|
38
|
+
* - `callStructuredMetered?(...)` is OPTIONAL: `callStructured`'s contract
|
|
39
|
+
* (the tool input, or a throw), with the call's token usage beside it
|
|
40
|
+
* as the provider reported it. `usage` is ABSENT when the provider
|
|
41
|
+
* reported none, never zeros: a consumer reads absence as "unmetered".
|
|
42
|
+
* A consumer that has it prefers it to `callStructured`; an
|
|
43
|
+
* implementation that cannot read usage omits it, and every caller
|
|
44
|
+
* written without it keeps compiling. It is a method rather than a
|
|
45
|
+
* usage callback because judges run concurrently, and a result carries
|
|
46
|
+
* its own usage where a callback would need correlating to its call.
|
|
38
47
|
* - `ToolSchema.input_schema` follows the OpenAI tool-use JSON
|
|
39
48
|
* Schema convention. Implementations that use a different
|
|
40
49
|
* tool-use protocol (e.g., Anthropic's variant) MUST translate at
|
|
@@ -46,6 +55,16 @@ export interface ToolSchema {
|
|
|
46
55
|
description: string;
|
|
47
56
|
input_schema: Record<string, unknown>;
|
|
48
57
|
}
|
|
58
|
+
/** Tokens one provider call consumed, as the provider reported them. */
|
|
59
|
+
export interface TokenUsage {
|
|
60
|
+
readonly input: number;
|
|
61
|
+
readonly output: number;
|
|
62
|
+
}
|
|
63
|
+
/** A call's result with the usage the provider reported for it; `usage` absent = unmetered. */
|
|
64
|
+
export interface Metered<T> {
|
|
65
|
+
readonly value: T;
|
|
66
|
+
readonly usage?: TokenUsage;
|
|
67
|
+
}
|
|
49
68
|
/** Chat-model dispatcher consumed by the negotiator's synthesis + judge primitives. */
|
|
50
69
|
export interface LLMCaller {
|
|
51
70
|
/**
|
|
@@ -62,6 +81,13 @@ export interface LLMCaller {
|
|
|
62
81
|
* extraction on the text path.
|
|
63
82
|
*/
|
|
64
83
|
callStructured?(systemPrompt: string, userMessage: string, tool: ToolSchema, maxTokens?: number): Promise<unknown>;
|
|
84
|
+
/**
|
|
85
|
+
* {@link callStructured}, with the call's token usage beside the tool
|
|
86
|
+
* input (see the normative semantics above). Omit it when the provider's
|
|
87
|
+
* usage cannot be read; consumers then fall back to `callStructured` and
|
|
88
|
+
* treat the call as unmetered.
|
|
89
|
+
*/
|
|
90
|
+
callStructuredMetered?(systemPrompt: string, userMessage: string, tool: ToolSchema, maxTokens?: number): Promise<Metered<unknown>>;
|
|
65
91
|
}
|
|
66
92
|
/**
|
|
67
93
|
* Provider + model selector for factory-style LLM caller
|
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
|
|
1
|
+
{"version":3,"file":"llm-caller.d.ts","sourceRoot":"","sources":["../src/llm-caller.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkDG;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,wEAAwE;AACxE,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,+FAA+F;AAC/F,MAAM,WAAW,OAAO,CAAC,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC;IAClB,QAAQ,CAAC,KAAK,CAAC,EAAE,UAAU,CAAC;CAC7B;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;;;;;;;OAOG;IACH,cAAc,CAAC,CACb,YAAY,EAAE,MAAM,EACpB,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,UAAU,EAChB,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,OAAO,CAAC,CAAC;IAEpB;;;;;OAKG;IACH,qBAAqB,CAAC,CACpB,YAAY,EAAE,MAAM,EACpB,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,UAAU,EAChB,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC;CAC9B;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
|
@@ -35,6 +35,15 @@
|
|
|
35
35
|
* that can't produce tool input at all simply omit this method;
|
|
36
36
|
* consumers fall back to `call` + regex JSON extraction. Absence is
|
|
37
37
|
* not an error.
|
|
38
|
+
* - `callStructuredMetered?(...)` is OPTIONAL: `callStructured`'s contract
|
|
39
|
+
* (the tool input, or a throw), with the call's token usage beside it
|
|
40
|
+
* as the provider reported it. `usage` is ABSENT when the provider
|
|
41
|
+
* reported none, never zeros: a consumer reads absence as "unmetered".
|
|
42
|
+
* A consumer that has it prefers it to `callStructured`; an
|
|
43
|
+
* implementation that cannot read usage omits it, and every caller
|
|
44
|
+
* written without it keeps compiling. It is a method rather than a
|
|
45
|
+
* usage callback because judges run concurrently, and a result carries
|
|
46
|
+
* its own usage where a callback would need correlating to its call.
|
|
38
47
|
* - `ToolSchema.input_schema` follows the OpenAI tool-use JSON
|
|
39
48
|
* Schema convention. Implementations that use a different
|
|
40
49
|
* tool-use protocol (e.g., Anthropic's variant) MUST translate at
|
package/dist/llm-rerank.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { LLMCaller, ToolSchema } from './llm-caller.js';
|
|
1
|
+
import type { LLMCaller, TokenUsage, ToolSchema } from './llm-caller.js';
|
|
2
2
|
/**
|
|
3
3
|
* One candidate blueprint for the LLM judge to consider.
|
|
4
4
|
*
|
|
@@ -53,15 +53,14 @@ export interface RerankDecision {
|
|
|
53
53
|
/** Wall-clock latency of the LLM call. */
|
|
54
54
|
readonly latencyMs: number;
|
|
55
55
|
/**
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
56
|
+
* Tokens the decision cost, as the provider reported them — for the
|
|
57
|
+
* cache-trace sink and cost accounting. ABSENT means unmetered, never
|
|
58
|
+
* zero: the judge called a provider and has no count for the call (a
|
|
59
|
+
* caller without `callStructuredMetered`, a provider that reported no
|
|
60
|
+
* usage, or a call that threw). `{ input: 0, output: 0 }` is a true
|
|
61
|
+
* zero: the decision was made without a provider call (ggui#1418).
|
|
60
62
|
*/
|
|
61
|
-
readonly tokenCost
|
|
62
|
-
readonly input: number;
|
|
63
|
-
readonly output: number;
|
|
64
|
-
};
|
|
63
|
+
readonly tokenCost?: TokenUsage;
|
|
65
64
|
}
|
|
66
65
|
/** Query the user's request the judge is matching against. */
|
|
67
66
|
export interface RerankQuery {
|
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":"AAeA,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"llm-rerank.d.ts","sourceRoot":"","sources":["../src/llm-rerank.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAEzE;;;;;;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;;;;;OAKG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,0CAA0C;IAC1C,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B;;;;;;;OAOG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,UAAU,CAAC;CACjC;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;AAkFF;;;;;;;;;;;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,CAyDzB;AAGD;;;;;;;GAOG;AACH,MAAM,MAAM,WAAW,GAAG,CACxB,KAAK,EAAE,WAAW,EAClB,UAAU,EAAE,SAAS,eAAe,EAAE,KACnC,OAAO,CAAC,cAAc,CAAC,CAAC;AAE7B;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,SAAS,GAAG,WAAW,CAE1D;AAED,OAAO,EAAE,oBAAoB,EAAE,WAAW,EAAE,CAAC"}
|
package/dist/llm-rerank.js
CHANGED
|
@@ -137,7 +137,8 @@ export async function rerankCandidates(deps, query, candidates) {
|
|
|
137
137
|
}
|
|
138
138
|
const userMessage = buildUserMessage(query, candidates);
|
|
139
139
|
const candidateIds = new Set(candidates.map((c) => c.id));
|
|
140
|
-
|
|
140
|
+
const { callStructured, callStructuredMetered } = deps.llm;
|
|
141
|
+
if (typeof callStructuredMetered !== 'function' && typeof callStructured !== 'function') {
|
|
141
142
|
return {
|
|
142
143
|
matchId: null,
|
|
143
144
|
confidence: 0,
|
|
@@ -146,9 +147,19 @@ export async function rerankCandidates(deps, query, candidates) {
|
|
|
146
147
|
tokenCost: { input: 0, output: 0 },
|
|
147
148
|
};
|
|
148
149
|
}
|
|
150
|
+
// The metered method when the caller has it (its usage is the decision's
|
|
151
|
+
// cost), else the plain one (unmetered: no tokenCost).
|
|
149
152
|
let toolInput;
|
|
153
|
+
let usage;
|
|
150
154
|
try {
|
|
151
|
-
|
|
155
|
+
if (typeof callStructuredMetered === 'function') {
|
|
156
|
+
const metered = await callStructuredMetered.call(deps.llm, RERANK_SYSTEM_PROMPT, userMessage, RERANK_TOOL, 512);
|
|
157
|
+
toolInput = metered.value;
|
|
158
|
+
usage = metered.usage;
|
|
159
|
+
}
|
|
160
|
+
else if (typeof callStructured === 'function') {
|
|
161
|
+
toolInput = await callStructured.call(deps.llm, RERANK_SYSTEM_PROMPT, userMessage, RERANK_TOOL, 512);
|
|
162
|
+
}
|
|
152
163
|
}
|
|
153
164
|
catch (err) {
|
|
154
165
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -157,7 +168,6 @@ export async function rerankCandidates(deps, query, candidates) {
|
|
|
157
168
|
confidence: 0,
|
|
158
169
|
reason: `llm-rerank: callStructured threw — ${message}`,
|
|
159
170
|
latencyMs: Date.now() - startedAt,
|
|
160
|
-
tokenCost: { input: 0, output: 0 },
|
|
161
171
|
};
|
|
162
172
|
}
|
|
163
173
|
const parsed = parseToolInput(toolInput, candidateIds);
|
|
@@ -166,10 +176,7 @@ export async function rerankCandidates(deps, query, candidates) {
|
|
|
166
176
|
confidence: parsed.confidence,
|
|
167
177
|
reason: parsed.reason,
|
|
168
178
|
latencyMs: Date.now() - startedAt,
|
|
169
|
-
|
|
170
|
-
// we don't have today. Default to zero; the cost gate is measured
|
|
171
|
-
// out-of-band from billing data during the probe.
|
|
172
|
-
tokenCost: { input: 0, output: 0 },
|
|
179
|
+
...(usage !== undefined ? { tokenCost: usage } : {}),
|
|
173
180
|
};
|
|
174
181
|
}
|
|
175
182
|
/**
|
|
@@ -11,6 +11,12 @@
|
|
|
11
11
|
*
|
|
12
12
|
* This pass fixes the mechanical classes deterministically, preserving
|
|
13
13
|
* the agent's intent exactly:
|
|
14
|
+
* - lifts a wrapper-level `propsSpec.required: [...]` (JSON Schema's
|
|
15
|
+
* spelling of the same declaration) into each listed entry's
|
|
16
|
+
* `required: true` before the key goes — the agent said which props
|
|
17
|
+
* are required, and the served contract keeps saying it (ggui#1432);
|
|
18
|
+
* an entry's own explicit `required` wins, and a name with no entry
|
|
19
|
+
* lifts nothing;
|
|
14
20
|
* - strips keys the protocol's `.strict()` spec schemas would reject,
|
|
15
21
|
* keeping only the allowed keys at each wrapper / entry level;
|
|
16
22
|
* - canonicalizes every inner JSON Schema via {@link normalizeSchema}
|
|
@@ -29,5 +35,19 @@
|
|
|
29
35
|
* (the top-level DataContract schema is `.passthrough()`); only the
|
|
30
36
|
* `.strict()` spec wrappers and entries are cleaned.
|
|
31
37
|
*/
|
|
38
|
+
/**
|
|
39
|
+
* The prop names a wrapper-level `propsSpec.required: [...]` will be
|
|
40
|
+
* lifted onto by {@link normalizeDraft} (ggui#1432): each listed name that
|
|
41
|
+
* has an entry and whose entry declares no `required` of its own, in the
|
|
42
|
+
* wrapper's order and de-duplicated. A ghost name (no entry) and an entry
|
|
43
|
+
* with its own word are skipped, exactly as the lift skips them. Empty when
|
|
44
|
+
* nothing lifts.
|
|
45
|
+
*
|
|
46
|
+
* One source of truth for the lift and for the finding that reports it
|
|
47
|
+
* (ggui#1454): the gate's `CTR_SHAPE_UNRECOGNIZED_KEYS` at `propsSpec` is
|
|
48
|
+
* a repair here, not a refusal, and its message says which entries gained
|
|
49
|
+
* `required: true`.
|
|
50
|
+
*/
|
|
51
|
+
export declare function liftedRequiredNames(draft: unknown): readonly string[];
|
|
32
52
|
export declare function normalizeDraft(draft: unknown): unknown;
|
|
33
53
|
//# sourceMappingURL=normalize-draft.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"normalize-draft.d.ts","sourceRoot":"","sources":["../src/normalize-draft.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"normalize-draft.d.ts","sourceRoot":"","sources":["../src/normalize-draft.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAgGH;;;;;;GAMG;AACH;;;;;;;;;;;;GAYG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,SAAS,MAAM,EAAE,CAUrE;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAuDtD"}
|
package/dist/normalize-draft.js
CHANGED
|
@@ -11,6 +11,12 @@
|
|
|
11
11
|
*
|
|
12
12
|
* This pass fixes the mechanical classes deterministically, preserving
|
|
13
13
|
* the agent's intent exactly:
|
|
14
|
+
* - lifts a wrapper-level `propsSpec.required: [...]` (JSON Schema's
|
|
15
|
+
* spelling of the same declaration) into each listed entry's
|
|
16
|
+
* `required: true` before the key goes — the agent said which props
|
|
17
|
+
* are required, and the served contract keeps saying it (ggui#1432);
|
|
18
|
+
* an entry's own explicit `required` wins, and a name with no entry
|
|
19
|
+
* lifts nothing;
|
|
14
20
|
* - strips keys the protocol's `.strict()` spec schemas would reject,
|
|
15
21
|
* keeping only the allowed keys at each wrapper / entry level;
|
|
16
22
|
* - canonicalizes every inner JSON Schema via {@link normalizeSchema}
|
|
@@ -22,43 +28,23 @@
|
|
|
22
28
|
* cross-refs) are deliberately out of scope — those still go to the
|
|
23
29
|
* repair loop, where reasoning earns its keep.
|
|
24
30
|
*/
|
|
31
|
+
import { actionEntrySchema, agentToolEntrySchema, contextEntrySchema, propEntrySchema, propsSpecSchema, streamChannelEntrySchema, } from '@ggui-ai/protocol';
|
|
25
32
|
import { normalizeSchema } from './normalize-schema.js';
|
|
26
|
-
// Allowed-key sets
|
|
27
|
-
// (schemas/data-contract.ts)
|
|
28
|
-
// the
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
const
|
|
39
|
-
'description',
|
|
40
|
-
'schema',
|
|
41
|
-
'default',
|
|
42
|
-
'debounceMs',
|
|
43
|
-
'example',
|
|
44
|
-
]);
|
|
45
|
-
const ACTION_ENTRY_KEYS = new Set([
|
|
46
|
-
'description',
|
|
47
|
-
'label',
|
|
48
|
-
'schema',
|
|
49
|
-
'example',
|
|
50
|
-
'icon',
|
|
51
|
-
'confirm',
|
|
52
|
-
'nextStep',
|
|
53
|
-
]);
|
|
54
|
-
const STREAM_ENTRY_KEYS = new Set(['description', 'schema', 'source']);
|
|
55
|
-
const AGENT_TOOL_KEYS = new Set(['serverInfo', 'toolInfo', 'usage', 'example']);
|
|
33
|
+
// Allowed-key sets are DERIVED from the protocol's `.strict()` spec
|
|
34
|
+
// schemas (schemas/data-contract.ts) — never hand-copied. A hand-copied
|
|
35
|
+
// mirror drifts the day the schema grows: `oneShot` joined
|
|
36
|
+
// `actionEntrySchema` on 2026-09-15 (ggui#1108) and a stale mirror here
|
|
37
|
+
// stripped it from every repaired draft for eleven days (ggui#1421).
|
|
38
|
+
// Stripping anything outside these sets is safe: the strict schema
|
|
39
|
+
// would reject it as CTR_SHAPE_UNRECOGNIZED_KEYS.
|
|
40
|
+
const PROPS_WRAPPER_KEYS = new Set(Object.keys(propsSpecSchema.shape));
|
|
41
|
+
const PROP_ENTRY_KEYS = new Set(Object.keys(propEntrySchema.shape));
|
|
42
|
+
const CONTEXT_ENTRY_KEYS = new Set(Object.keys(contextEntrySchema.shape));
|
|
43
|
+
const ACTION_ENTRY_KEYS = new Set(Object.keys(actionEntrySchema.shape));
|
|
44
|
+
const STREAM_ENTRY_KEYS = new Set(Object.keys(streamChannelEntrySchema.shape));
|
|
45
|
+
const AGENT_TOOL_KEYS = new Set(Object.keys(agentToolEntrySchema.shape));
|
|
56
46
|
/** Inner keys of an {@link AgentToolEntry.toolInfo} (the MCP descriptor). */
|
|
57
|
-
const AGENT_TOOL_INFO_KEYS = new Set(
|
|
58
|
-
'inputSchema',
|
|
59
|
-
'description',
|
|
60
|
-
'outputSchema',
|
|
61
|
-
]);
|
|
47
|
+
const AGENT_TOOL_INFO_KEYS = new Set(Object.keys(agentToolEntrySchema.shape.toolInfo.shape));
|
|
62
48
|
function isRecord(value) {
|
|
63
49
|
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
64
50
|
}
|
|
@@ -121,21 +107,59 @@ function cleanAgentToolMap(map) {
|
|
|
121
107
|
* (the top-level DataContract schema is `.passthrough()`); only the
|
|
122
108
|
* `.strict()` spec wrappers and entries are cleaned.
|
|
123
109
|
*/
|
|
110
|
+
/**
|
|
111
|
+
* The prop names a wrapper-level `propsSpec.required: [...]` will be
|
|
112
|
+
* lifted onto by {@link normalizeDraft} (ggui#1432): each listed name that
|
|
113
|
+
* has an entry and whose entry declares no `required` of its own, in the
|
|
114
|
+
* wrapper's order and de-duplicated. A ghost name (no entry) and an entry
|
|
115
|
+
* with its own word are skipped, exactly as the lift skips them. Empty when
|
|
116
|
+
* nothing lifts.
|
|
117
|
+
*
|
|
118
|
+
* One source of truth for the lift and for the finding that reports it
|
|
119
|
+
* (ggui#1454): the gate's `CTR_SHAPE_UNRECOGNIZED_KEYS` at `propsSpec` is
|
|
120
|
+
* a repair here, not a refusal, and its message says which entries gained
|
|
121
|
+
* `required: true`.
|
|
122
|
+
*/
|
|
123
|
+
export function liftedRequiredNames(draft) {
|
|
124
|
+
if (!isRecord(draft) || !isRecord(draft['propsSpec']))
|
|
125
|
+
return [];
|
|
126
|
+
const wrapper = draft['propsSpec'];
|
|
127
|
+
if (!Array.isArray(wrapper['required']) || !isRecord(wrapper['properties']))
|
|
128
|
+
return [];
|
|
129
|
+
const entries = wrapper['properties'];
|
|
130
|
+
// De-duplicated: a name the wrapper lists twice lifts once and is reported once.
|
|
131
|
+
return [...new Set(wrapper['required'].filter((name) => typeof name === 'string'))].filter((name) => {
|
|
132
|
+
const entry = entries[name];
|
|
133
|
+
return isRecord(entry) && entry['required'] === undefined;
|
|
134
|
+
});
|
|
135
|
+
}
|
|
124
136
|
export function normalizeDraft(draft) {
|
|
125
137
|
if (!isRecord(draft))
|
|
126
138
|
return draft;
|
|
127
139
|
const out = { ...draft };
|
|
128
140
|
// propsSpec wrapper: keep {description, properties}; clean each PropEntry.
|
|
141
|
+
// A wrapper-level `required: [...]` is the agent's declaration in JSON
|
|
142
|
+
// Schema's spelling — lift it into the listed entries before the key
|
|
143
|
+
// goes, so the served contract still says which props are required.
|
|
129
144
|
if (isRecord(out['propsSpec'])) {
|
|
145
|
+
const wrapper = out['propsSpec'];
|
|
130
146
|
const ps = {};
|
|
131
|
-
for (const [key, value] of Object.entries(
|
|
147
|
+
for (const [key, value] of Object.entries(wrapper)) {
|
|
132
148
|
if (PROPS_WRAPPER_KEYS.has(key))
|
|
133
149
|
ps[key] = value;
|
|
134
150
|
}
|
|
135
151
|
if (isRecord(ps['properties'])) {
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
152
|
+
const cleaned = cleanEntryMap(ps['properties'], PROP_ENTRY_KEYS, ['schema']);
|
|
153
|
+
// The same selection `liftedRequiredNames` reports (one source of truth);
|
|
154
|
+
// `cleanEntryMap` keeps an entry's own `required`, so the test is the same on
|
|
155
|
+
// the cleaned map as on the raw one.
|
|
156
|
+
for (const name of liftedRequiredNames(draft)) {
|
|
157
|
+
const entry = cleaned[name];
|
|
158
|
+
if (!isRecord(entry))
|
|
159
|
+
continue;
|
|
160
|
+
cleaned[name] = { ...entry, required: true };
|
|
161
|
+
}
|
|
162
|
+
ps['properties'] = cleaned;
|
|
139
163
|
}
|
|
140
164
|
out['propsSpec'] = ps;
|
|
141
165
|
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Action-entry member preservation — the deterministic backstop that
|
|
3
|
+
* keeps a repair FAITHFUL to what the agent DECLARED on its actions
|
|
4
|
+
* (ggui#1421).
|
|
5
|
+
*
|
|
6
|
+
* The repair tool authors exactly two members per action, `label` and
|
|
7
|
+
* `schema` (`SYNTHESIZE_TOOL`); every other member of an
|
|
8
|
+
* {@link ActionEntry} — `description`, `example`, `icon`, `confirm`,
|
|
9
|
+
* `oneShot`, `nextStep` — is the agent's declaration, and the whole
|
|
10
|
+
* one-shot line (ggui#1108 → ggui#1223: the runtime's second-dispatch
|
|
11
|
+
* guard, the server's spend record, ui-gen's `useActionSpent` binding)
|
|
12
|
+
* keys on `oneShot === true` being ON THE SERVED CONTRACT. A repair that
|
|
13
|
+
* re-emits an entry from the tool's answer alone serves a card with no
|
|
14
|
+
* guard, silently. So the overlay below puts the draft's declarations
|
|
15
|
+
* back on the repaired candidate, deterministically, and NAMES whatever
|
|
16
|
+
* it cannot keep:
|
|
17
|
+
*
|
|
18
|
+
* - `REPAIR_MEMBER_DROPPED` at `actionSpec.<name>.<member>` — a
|
|
19
|
+
* declared member the merged contract cannot carry (the gate refuses
|
|
20
|
+
* it there, e.g. a `nextStep` whose tool the repair did not
|
|
21
|
+
* re-declare, or a member that fails its own schema);
|
|
22
|
+
* - `REPAIR_ENTRY_DROPPED` at `actionSpec.<name>` — a draft action the
|
|
23
|
+
* candidate no longer carries under that name (renamed or removed),
|
|
24
|
+
* which takes every member with it.
|
|
25
|
+
*
|
|
26
|
+
* A member whose path a DRAFT finding already names is under repair: it
|
|
27
|
+
* is not restored, and its absence is named by that finding, at the same
|
|
28
|
+
* path. Every other loss is named HERE, at the member's or the entry's
|
|
29
|
+
* own path, with the gate's reason in the message — so the invariant an
|
|
30
|
+
* agent can rely on is one sentence: a declared member or action missing
|
|
31
|
+
* from the served proposal always has a finding at its own path. The
|
|
32
|
+
* `CTR_*` codes say what the gate refused; the `REPAIR_*` codes say what
|
|
33
|
+
* the served proposal does not carry of the declaration, and why. The
|
|
34
|
+
* codes are handshake-decision codes like `COVERAGE_GAP` — declared where
|
|
35
|
+
* they are emitted, read by the agent as strings in `validationFindings`.
|
|
36
|
+
* Model-independent: the overlay works whatever the repair LLM returns.
|
|
37
|
+
*/
|
|
38
|
+
import { type DataContract, type SuggestionFinding } from '@ggui-ai/protocol';
|
|
39
|
+
/** A declared action-entry member the repair could not keep; path `actionSpec.<name>.<member>`. */
|
|
40
|
+
export declare const REPAIR_MEMBER_DROPPED: "REPAIR_MEMBER_DROPPED";
|
|
41
|
+
/** A draft action entry the repair no longer carries under its name; path `actionSpec.<name>`. */
|
|
42
|
+
export declare const REPAIR_ENTRY_DROPPED: "REPAIR_ENTRY_DROPPED";
|
|
43
|
+
/**
|
|
44
|
+
* Whether `finding` is a member drop on one of `entryNames` — matched by
|
|
45
|
+
* the exact `actionSpec.<name>.<member>` path per declared member, never
|
|
46
|
+
* by a dotted prefix (an action named `a` is not a prefix of `a.b`).
|
|
47
|
+
*/
|
|
48
|
+
export declare function isMemberDropOf(finding: SuggestionFinding, entryNames: readonly string[]): boolean;
|
|
49
|
+
/**
|
|
50
|
+
* Overlay the draft's declared members onto the candidate's action
|
|
51
|
+
* entries (same name), keeping the candidate's `label` and `schema`. A
|
|
52
|
+
* member whose exact path a draft finding names is under repair and
|
|
53
|
+
* stays out. Then lint the merged contract and un-restore, until the
|
|
54
|
+
* tree is stable, every restored member the gate refuses — at its own
|
|
55
|
+
* path, or (for `nextStep`) as `CTR_SCHEMA_INCOMPAT` at the sibling
|
|
56
|
+
* `.schema`, the path the compatibility check reports on — naming each
|
|
57
|
+
* one, so the returned contract never fails the gate BECAUSE of the
|
|
58
|
+
* overlay. A malformed candidate entry (no `label`) is left un-overlaid
|
|
59
|
+
* for the loop's own gate to retry; nothing here throws on the model's
|
|
60
|
+
* answer. Entries the candidate does not carry get nothing back here —
|
|
61
|
+
* see {@link findDroppedActionEntries}.
|
|
62
|
+
*/
|
|
63
|
+
export declare function restoreDraftActionMembers(draft: unknown, candidate: DataContract, underRepair: ReadonlySet<string>): {
|
|
64
|
+
readonly contract: DataContract;
|
|
65
|
+
readonly dropped: readonly SuggestionFinding[];
|
|
66
|
+
};
|
|
67
|
+
/**
|
|
68
|
+
* One `REPAIR_ENTRY_DROPPED` per draft action entry the candidate no
|
|
69
|
+
* longer carries under that name — renamed or removed by the repair (or
|
|
70
|
+
* pruned by the loop's own placement pass) — always, because the members
|
|
71
|
+
* it carried (`oneShot` above all) went with it and nothing else names
|
|
72
|
+
* that. When the gate refused the NAME itself (`CTR_DUP_NAME` /
|
|
73
|
+
* `CTR_RESERVED_NAME` at `actionSpec.<name>`, so the path is under
|
|
74
|
+
* repair) the message says so and does not send the agent back to a
|
|
75
|
+
* name the gate will refuse again. Empty when every draft entry survived,
|
|
76
|
+
* and for drafts that declare no actions.
|
|
77
|
+
*/
|
|
78
|
+
export declare function findDroppedActionEntries(draft: unknown, candidate: DataContract, underRepair: ReadonlySet<string>): readonly SuggestionFinding[];
|
|
79
|
+
//# sourceMappingURL=preserve-action-members.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preserve-action-members.d.ts","sourceRoot":"","sources":["../src/preserve-action-members.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAEH,OAAO,EAIL,KAAK,YAAY,EACjB,KAAK,iBAAiB,EACvB,MAAM,mBAAmB,CAAC;AAE3B,mGAAmG;AACnG,eAAO,MAAM,qBAAqB,EAAG,uBAAgC,CAAC;AACtE,kGAAkG;AAClG,eAAO,MAAM,oBAAoB,EAAG,sBAA+B,CAAC;AA6CpE;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,iBAAiB,EAAE,UAAU,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAEjG;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,yBAAyB,CACvC,KAAK,EAAE,OAAO,EACd,SAAS,EAAE,YAAY,EACvB,WAAW,EAAE,WAAW,CAAC,MAAM,CAAC,GAC/B;IAAE,QAAQ,CAAC,QAAQ,EAAE,YAAY,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,SAAS,iBAAiB,EAAE,CAAA;CAAE,CAuErF;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,OAAO,EACd,SAAS,EAAE,YAAY,EACvB,WAAW,EAAE,WAAW,CAAC,MAAM,CAAC,GAC/B,SAAS,iBAAiB,EAAE,CAc9B"}
|