@ggui-ai/negotiator 0.24.0 → 0.26.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.
@@ -1,54 +0,0 @@
1
- /**
2
- * Contract Hashing — deterministic cache key from data contract.
3
- *
4
- * Uses RFC 8785 (JSON Canonicalization Scheme) for deterministic
5
- * serialization, then SHA-256 for the hash.
6
- *
7
- * What's in the hash: intent only (the semantic identity).
8
- * What's NOT in the hash: props, actions, stream (including per-channel source), adaptations.
9
- *
10
- * Why intent-only: The LLM generates slightly different prop descriptions
11
- * each call, but the INTENT is deterministic (provided by the agent or
12
- * generated once). Same intent = same component = same cached code.
13
- * Props are passed at render time, not baked into the component.
14
- *
15
- * The `interaction` field was removed from `DataContract` in pre-launch
16
- * cleanup (the four-spec surface — props/action/context/stream —
17
- * describes the wire exhaustively); this function used to factor it in
18
- * but no longer does. Old goldens were rotated when the field went away.
19
- */
20
- import type { DataContract } from '@ggui-ai/protocol';
21
- /**
22
- * Hash a data contract to a deterministic cache key.
23
- *
24
- * Hashes intent only — the semantic identity of the UI. Props,
25
- * actions, stream specs are for code generation, not caching.
26
- *
27
- * `intent` is passed separately because `DataContract` itself does
28
- * not carry an `intent` field — the outer pipeline owns intent
29
- * (`story.intent` on `ggui_render`, the operator prompt for harness
30
- * benchmarks). Threading intent into the hash keeps cache identity
31
- * stable across negotiations: different intents produce different
32
- * generated code (labels, copy, layout) even when the wire surface is
33
- * identical.
34
- *
35
- * The `contract` argument is retained on the signature for forward
36
- * compatibility — future cache-key fields (e.g., a structural
37
- * fingerprint of the four specs) can be folded in without breaking
38
- * call sites — but is not consulted by the current implementation.
39
- *
40
- * @param contract - The data contract from negotiation (unused today)
41
- * @param intent - The outer pipeline's intent (story.intent on
42
- * ggui_render). Empty/falsy values are dropped from the hash input.
43
- * @returns Contract hash prefixed with `ch_` (e.g., `ch_a3f8b2c1e9d04567`)
44
- */
45
- export declare function hashContract(_contract: DataContract, intent: string): string;
46
- /**
47
- * Build a variant key from shell type and device category.
48
- *
49
- * @param shellType - Shell type (default: 'universal')
50
- * @param deviceCategory - Device category (default: 'universal')
51
- * @returns Variant string (e.g., 'fullscreen:mobile', 'universal:universal')
52
- */
53
- export declare function buildVariant(shellType?: string, deviceCategory?: string): string;
54
- //# sourceMappingURL=contract-hash.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"contract-hash.d.ts","sourceRoot":"","sources":["../src/contract-hash.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAiCtD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,YAAY,CAC1B,SAAS,EAAE,YAAY,EACvB,MAAM,EAAE,MAAM,GACb,MAAM,CAMR;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAC1B,SAAS,SAAc,EACvB,cAAc,SAAc,GAC3B,MAAM,CAER"}
@@ -1,96 +0,0 @@
1
- /**
2
- * Contract Hashing — deterministic cache key from data contract.
3
- *
4
- * Uses RFC 8785 (JSON Canonicalization Scheme) for deterministic
5
- * serialization, then SHA-256 for the hash.
6
- *
7
- * What's in the hash: intent only (the semantic identity).
8
- * What's NOT in the hash: props, actions, stream (including per-channel source), adaptations.
9
- *
10
- * Why intent-only: The LLM generates slightly different prop descriptions
11
- * each call, but the INTENT is deterministic (provided by the agent or
12
- * generated once). Same intent = same component = same cached code.
13
- * Props are passed at render time, not baked into the component.
14
- *
15
- * The `interaction` field was removed from `DataContract` in pre-launch
16
- * cleanup (the four-spec surface — props/action/context/stream —
17
- * describes the wire exhaustively); this function used to factor it in
18
- * but no longer does. Old goldens were rotated when the field went away.
19
- */
20
- import { createHash } from 'node:crypto';
21
- /**
22
- * RFC 8785 JSON Canonicalization Scheme.
23
- *
24
- * Produces deterministic JSON by:
25
- * - Sorting object keys lexicographically (recursive)
26
- * - Normalizing numbers (no trailing zeros, no +0)
27
- * - Removing undefined values
28
- * - No whitespace
29
- */
30
- function canonicalize(value) {
31
- if (value === null || value === undefined)
32
- return 'null';
33
- if (typeof value === 'boolean')
34
- return value ? 'true' : 'false';
35
- if (typeof value === 'number') {
36
- if (!isFinite(value))
37
- return 'null';
38
- return Object.is(value, -0) ? '0' : String(value);
39
- }
40
- if (typeof value === 'string')
41
- return JSON.stringify(value);
42
- if (Array.isArray(value)) {
43
- return '[' + value.map((v) => canonicalize(v)).join(',') + ']';
44
- }
45
- if (typeof value === 'object') {
46
- const obj = value;
47
- const keys = Object.keys(obj)
48
- .filter((k) => obj[k] !== undefined)
49
- .sort();
50
- const entries = keys.map((k) => JSON.stringify(k) + ':' + canonicalize(obj[k]));
51
- return '{' + entries.join(',') + '}';
52
- }
53
- return 'null';
54
- }
55
- /**
56
- * Hash a data contract to a deterministic cache key.
57
- *
58
- * Hashes intent only — the semantic identity of the UI. Props,
59
- * actions, stream specs are for code generation, not caching.
60
- *
61
- * `intent` is passed separately because `DataContract` itself does
62
- * not carry an `intent` field — the outer pipeline owns intent
63
- * (`story.intent` on `ggui_render`, the operator prompt for harness
64
- * benchmarks). Threading intent into the hash keeps cache identity
65
- * stable across negotiations: different intents produce different
66
- * generated code (labels, copy, layout) even when the wire surface is
67
- * identical.
68
- *
69
- * The `contract` argument is retained on the signature for forward
70
- * compatibility — future cache-key fields (e.g., a structural
71
- * fingerprint of the four specs) can be folded in without breaking
72
- * call sites — but is not consulted by the current implementation.
73
- *
74
- * @param contract - The data contract from negotiation (unused today)
75
- * @param intent - The outer pipeline's intent (story.intent on
76
- * ggui_render). Empty/falsy values are dropped from the hash input.
77
- * @returns Contract hash prefixed with `ch_` (e.g., `ch_a3f8b2c1e9d04567`)
78
- */
79
- export function hashContract(_contract, intent) {
80
- const input = {};
81
- if (intent)
82
- input.intent = intent;
83
- const canonical = canonicalize(input);
84
- const hash = createHash('sha256').update(canonical).digest('hex');
85
- return `ch_${hash.slice(0, 16)}`;
86
- }
87
- /**
88
- * Build a variant key from shell type and device category.
89
- *
90
- * @param shellType - Shell type (default: 'universal')
91
- * @param deviceCategory - Device category (default: 'universal')
92
- * @returns Variant string (e.g., 'fullscreen:mobile', 'universal:universal')
93
- */
94
- export function buildVariant(shellType = 'universal', deviceCategory = 'universal') {
95
- return `${shellType}:${deviceCategory}`;
96
- }
@@ -1,102 +0,0 @@
1
- /**
2
- * Contract Hashing — deterministic cache key from data contract.
3
- *
4
- * Uses RFC 8785 (JSON Canonicalization Scheme) for deterministic
5
- * serialization, then SHA-256 for the hash.
6
- *
7
- * What's in the hash: intent only (the semantic identity).
8
- * What's NOT in the hash: props, actions, stream (including per-channel source), adaptations.
9
- *
10
- * Why intent-only: The LLM generates slightly different prop descriptions
11
- * each call, but the INTENT is deterministic (provided by the agent or
12
- * generated once). Same intent = same component = same cached code.
13
- * Props are passed at render time, not baked into the component.
14
- *
15
- * The `interaction` field was removed from `DataContract` in pre-launch
16
- * cleanup (the four-spec surface — props/action/context/stream —
17
- * describes the wire exhaustively); this function used to factor it in
18
- * but no longer does. Old goldens were rotated when the field went away.
19
- */
20
-
21
- import { createHash } from 'node:crypto';
22
- import type { DataContract } from '@ggui-ai/protocol';
23
-
24
- /**
25
- * RFC 8785 JSON Canonicalization Scheme.
26
- *
27
- * Produces deterministic JSON by:
28
- * - Sorting object keys lexicographically (recursive)
29
- * - Normalizing numbers (no trailing zeros, no +0)
30
- * - Removing undefined values
31
- * - No whitespace
32
- */
33
- function canonicalize(value: unknown): string {
34
- if (value === null || value === undefined) return 'null';
35
- if (typeof value === 'boolean') return value ? 'true' : 'false';
36
- if (typeof value === 'number') {
37
- if (!isFinite(value)) return 'null';
38
- return Object.is(value, -0) ? '0' : String(value);
39
- }
40
- if (typeof value === 'string') return JSON.stringify(value);
41
- if (Array.isArray(value)) {
42
- return '[' + value.map((v) => canonicalize(v)).join(',') + ']';
43
- }
44
- if (typeof value === 'object') {
45
- const obj = value as Record<string, unknown>;
46
- const keys = Object.keys(obj)
47
- .filter((k) => obj[k] !== undefined)
48
- .sort();
49
- const entries = keys.map((k) => JSON.stringify(k) + ':' + canonicalize(obj[k]));
50
- return '{' + entries.join(',') + '}';
51
- }
52
- return 'null';
53
- }
54
-
55
- /**
56
- * Hash a data contract to a deterministic cache key.
57
- *
58
- * Hashes intent only — the semantic identity of the UI. Props,
59
- * actions, stream specs are for code generation, not caching.
60
- *
61
- * `intent` is passed separately because `DataContract` itself does
62
- * not carry an `intent` field — the outer pipeline owns intent
63
- * (`story.intent` on `ggui_render`, the operator prompt for harness
64
- * benchmarks). Threading intent into the hash keeps cache identity
65
- * stable across negotiations: different intents produce different
66
- * generated code (labels, copy, layout) even when the wire surface is
67
- * identical.
68
- *
69
- * The `contract` argument is retained on the signature for forward
70
- * compatibility — future cache-key fields (e.g., a structural
71
- * fingerprint of the four specs) can be folded in without breaking
72
- * call sites — but is not consulted by the current implementation.
73
- *
74
- * @param contract - The data contract from negotiation (unused today)
75
- * @param intent - The outer pipeline's intent (story.intent on
76
- * ggui_render). Empty/falsy values are dropped from the hash input.
77
- * @returns Contract hash prefixed with `ch_` (e.g., `ch_a3f8b2c1e9d04567`)
78
- */
79
- export function hashContract(
80
- _contract: DataContract,
81
- intent: string,
82
- ): string {
83
- const input: Record<string, unknown> = {};
84
- if (intent) input.intent = intent;
85
- const canonical = canonicalize(input);
86
- const hash = createHash('sha256').update(canonical).digest('hex');
87
- return `ch_${hash.slice(0, 16)}`;
88
- }
89
-
90
- /**
91
- * Build a variant key from shell type and device category.
92
- *
93
- * @param shellType - Shell type (default: 'universal')
94
- * @param deviceCategory - Device category (default: 'universal')
95
- * @returns Variant string (e.g., 'fullscreen:mobile', 'universal:universal')
96
- */
97
- export function buildVariant(
98
- shellType = 'universal',
99
- deviceCategory = 'universal',
100
- ): string {
101
- return `${shellType}:${deviceCategory}`;
102
- }