@flui-cloud/semantic-surface 0.1.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.
@@ -0,0 +1,257 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://flui.cloud/schemas/semantic-surface/0.2/semantic-surface.schema.json",
4
+ "title": "Semantic Surface Snapshot",
5
+ "description": "A serializable, medium-independent representation of the user's current attention and of what the interface has actually presented to them. See semantic-surface-core-v0.2.md.",
6
+ "type": "object",
7
+ "required": ["schemaVersion", "app", "surface", "attention", "scopes"],
8
+ "additionalProperties": false,
9
+ "properties": {
10
+ "schemaVersion": {
11
+ "const": "0.2",
12
+ "description": "A major.minor compatibility version, not a full SemVer string: it names a compatibility family, so non-breaking corrections can ship without invalidating existing snapshots."
13
+ },
14
+ "app": { "$ref": "#/$defs/app" },
15
+ "surface": { "$ref": "#/$defs/surface" },
16
+ "attention": {
17
+ "type": "array",
18
+ "maxItems": 16,
19
+ "items": { "$ref": "#/$defs/attentionTarget" },
20
+ "description": "Ordered by salience: the first element is the primary target. May be empty; an invented selection is not allowed."
21
+ },
22
+ "scopes": {
23
+ "type": "array",
24
+ "minItems": 1,
25
+ "maxItems": 200,
26
+ "items": { "$ref": "#/$defs/scope" },
27
+ "description": "Flat list. Hierarchy is expressed with parentId. An inactive scope never appears."
28
+ },
29
+ "extensions": { "$ref": "#/$defs/extensions" }
30
+ },
31
+ "$defs": {
32
+ "identifier": {
33
+ "type": "string",
34
+ "minLength": 1,
35
+ "maxLength": 128,
36
+ "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$"
37
+ },
38
+ "entityRef": {
39
+ "type": "string",
40
+ "format": "uri",
41
+ "maxLength": 512,
42
+ "pattern": "^[a-z][a-z0-9+.-]*://[a-z][a-z0-9._-]*/(?:[A-Za-z0-9._~!$&'()*+,;=:@-]|%[0-9A-Fa-f]{2})+$",
43
+ "description": "<namespace>://<entity-type>/<entity-id>. The entity-id is a single URI path segment: unreserved and sub-delimiter characters, or correctly percent-encoded octets. No nested path, query or fragment — composite identities use a separator inside the segment. Stable, passed explicitly to tools, never proof of authorisation."
44
+ },
45
+ "resourceRef": {
46
+ "type": "string",
47
+ "format": "uri",
48
+ "maxLength": 1024,
49
+ "pattern": "^[a-z][a-z0-9+.-]*://[^\\s]+$",
50
+ "description": "Where to fetch the authoritative value. Unlike entityRef, multi-segment paths and query strings are allowed."
51
+ },
52
+ "namespacedKey": {
53
+ "type": "string",
54
+ "maxLength": 128,
55
+ "pattern": "^[a-z][a-z0-9_]*(\\.[a-z0-9_]+)+$",
56
+ "description": "Namespaced key: at least one dot. For infrastructure metrics prefer the OpenTelemetry Semantic Conventions."
57
+ },
58
+ "scopeKind": {
59
+ "type": "string",
60
+ "maxLength": 64,
61
+ "pattern": "^(page|region|selection|list|form|overlay)$|^[a-z][a-z0-9_]*(\\.[a-z0-9_]+)+$",
62
+ "description": "A reserved core value, or a namespaced extension (e.g. vops.terminal)."
63
+ },
64
+ "attentionReason": {
65
+ "type": "string",
66
+ "maxLength": 64,
67
+ "pattern": "^(route|selection|active-view|overlay|manual)$|^[a-z][a-z0-9_]*(\\.[a-z0-9_]+)+$"
68
+ },
69
+ "shortText": { "type": "string", "maxLength": 200 },
70
+ "freeText": {
71
+ "type": "string",
72
+ "maxLength": 500,
73
+ "description": "Only text actually presented to the user, or produced by the backend as part of the product. Never text written to steer the model. No markup, no secrets, no operational instructions."
74
+ },
75
+ "timestamp": {
76
+ "type": "string",
77
+ "format": "date-time",
78
+ "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[+-]\\d{2}:\\d{2})$",
79
+ "description": "ISO 8601 with an explicit offset. The pattern is a deliberate belt-and-braces duplicate of the format, because validators silently ignore formats they do not know. It constrains lexical shape only: a well-formed but non-existent instant such as 2026-99-45T29:81:75+84:92 passes here and is caught by the semantic validator as invalid-timestamp-value."
80
+ },
81
+ "extensions": {
82
+ "type": "object",
83
+ "description": "Namespaced keys (e.g. vops.*). A consumer must ignore extensions it does not know.",
84
+ "propertyNames": { "pattern": "^[a-z][a-z0-9_]*(\\.[a-z0-9_]+)+$" },
85
+ "additionalProperties": true
86
+ },
87
+
88
+ "app": {
89
+ "type": "object",
90
+ "required": ["id"],
91
+ "additionalProperties": false,
92
+ "properties": {
93
+ "id": { "$ref": "#/$defs/identifier" },
94
+ "version": { "type": "string", "maxLength": 64 }
95
+ }
96
+ },
97
+
98
+ "surface": {
99
+ "type": "object",
100
+ "required": ["id", "revision", "generatedAt"],
101
+ "additionalProperties": false,
102
+ "properties": {
103
+ "id": { "$ref": "#/$defs/identifier" },
104
+ "route": { "type": "string", "maxLength": 512 },
105
+ "revision": {
106
+ "type": "integer",
107
+ "minimum": 0,
108
+ "description": "Monotonically increasing. Diagnoses UI context; does not replace backend versions or ETags."
109
+ },
110
+ "generatedAt": {
111
+ "$ref": "#/$defs/timestamp",
112
+ "description": "When the snapshot was produced. Not the age of the data: see observation.observedAt."
113
+ },
114
+ "locale": { "type": "string", "maxLength": 35 },
115
+ "truncated": {
116
+ "type": "boolean",
117
+ "description": "True when the budget dropped something."
118
+ }
119
+ }
120
+ },
121
+
122
+ "attentionTarget": {
123
+ "type": "object",
124
+ "required": ["scopeId"],
125
+ "additionalProperties": false,
126
+ "properties": {
127
+ "scopeId": { "$ref": "#/$defs/identifier" },
128
+ "entityRef": { "$ref": "#/$defs/entityRef" },
129
+ "reason": { "$ref": "#/$defs/attentionReason" }
130
+ }
131
+ },
132
+
133
+ "scope": {
134
+ "type": "object",
135
+ "required": ["id", "kind"],
136
+ "additionalProperties": false,
137
+ "properties": {
138
+ "id": { "$ref": "#/$defs/identifier" },
139
+ "parentId": { "$ref": "#/$defs/identifier" },
140
+ "kind": { "$ref": "#/$defs/scopeKind" },
141
+ "label": { "$ref": "#/$defs/shortText" },
142
+ "entities": {
143
+ "type": "array",
144
+ "maxItems": 64,
145
+ "items": { "$ref": "#/$defs/entityReference" }
146
+ },
147
+ "observations": {
148
+ "type": "array",
149
+ "maxItems": 64,
150
+ "items": { "$ref": "#/$defs/observation" }
151
+ },
152
+ "state": { "$ref": "#/$defs/scopeState" },
153
+ "completeness": { "$ref": "#/$defs/completeness" },
154
+ "extensions": { "$ref": "#/$defs/extensions" }
155
+ }
156
+ },
157
+
158
+ "scopeState": {
159
+ "type": "object",
160
+ "additionalProperties": false,
161
+ "description": "Distinguishes three conditions that would otherwise collapse into 'empty scope'.",
162
+ "properties": {
163
+ "loading": { "type": "boolean" },
164
+ "error": { "type": "boolean" },
165
+ "errorCode": {
166
+ "$ref": "#/$defs/namespacedKey",
167
+ "description": "A namespaced code such as vops.host.unreachable, never the free-text error message: text coming from a backend is an injection vector."
168
+ },
169
+ "empty": { "type": "boolean" }
170
+ },
171
+ "allOf": [
172
+ {
173
+ "if": { "required": ["errorCode"] },
174
+ "then": {
175
+ "required": ["error"],
176
+ "properties": { "error": { "const": true } }
177
+ }
178
+ }
179
+ ]
180
+ },
181
+
182
+ "completeness": {
183
+ "type": "object",
184
+ "required": ["shown"],
185
+ "additionalProperties": false,
186
+ "description": "Should be present on every scope representing a paginated, filtered or virtualised list.",
187
+ "properties": {
188
+ "shown": { "type": "integer", "minimum": 0 },
189
+ "total": { "type": "integer", "minimum": 0 },
190
+ "filtered": { "type": "boolean" },
191
+ "truncated": { "type": "boolean" }
192
+ }
193
+ },
194
+
195
+ "entityReference": {
196
+ "type": "object",
197
+ "required": ["ref"],
198
+ "additionalProperties": false,
199
+ "description": "A single identifying field: the v0.1 ref/type/id triple has been removed.",
200
+ "properties": {
201
+ "ref": { "$ref": "#/$defs/entityRef" },
202
+ "label": { "$ref": "#/$defs/shortText" },
203
+ "role": { "enum": ["primary", "selected", "related"] }
204
+ }
205
+ },
206
+
207
+ "observation": {
208
+ "type": "object",
209
+ "required": ["key", "presentedAs"],
210
+ "additionalProperties": false,
211
+ "description": "Always means: the interface presented this, referring to that moment. Never: this is true now.",
212
+ "properties": {
213
+ "key": { "$ref": "#/$defs/namespacedKey" },
214
+ "presentedAs": {
215
+ "type": "object",
216
+ "additionalProperties": false,
217
+ "description": "The only container for the presented value: there is no bare 'value' field beside it. A unit alone presents nothing, so either value or text is required.",
218
+ "properties": {
219
+ "value": {
220
+ "description": "A compact scalar actually presented to the user. Charts, series and structures belong behind resourceRef.",
221
+ "oneOf": [
222
+ { "type": "number" },
223
+ { "type": "boolean" },
224
+ { "type": "null" },
225
+ { "type": "string", "maxLength": 200 }
226
+ ]
227
+ },
228
+ "unit": { "type": "string", "maxLength": 32 },
229
+ "text": { "$ref": "#/$defs/freeText" }
230
+ },
231
+ "anyOf": [
232
+ { "required": ["value"] },
233
+ { "required": ["text"] }
234
+ ]
235
+ },
236
+ "source": { "enum": ["ui", "api", "derived"] },
237
+ "observedAt": {
238
+ "$ref": "#/$defs/timestamp",
239
+ "description": "When the value was measured or received. If absent the age is UNKNOWN: a consumer must not assume it equals surface.generatedAt."
240
+ },
241
+ "resourceRef": { "$ref": "#/$defs/resourceRef" }
242
+ },
243
+ "allOf": [
244
+ {
245
+ "if": {
246
+ "required": ["presentedAs"],
247
+ "properties": { "presentedAs": { "required": ["text"] } }
248
+ },
249
+ "then": {
250
+ "required": ["source"],
251
+ "$comment": "Free text is admitted only with provenance (core spec section 3.5)."
252
+ }
253
+ }
254
+ ]
255
+ }
256
+ }
257
+ }
@@ -0,0 +1,8 @@
1
+ export { ATTENTION_REASONS, SCOPE_KINDS, SURFACE_SCHEMA_VERSION, } from './surface.types';
2
+ export type { AttentionTarget, Completeness, EntityReference, EntityRole, Observation, ObservationSource, ScopeState, SemanticScopeSnapshot, SurfaceSnapshot, } from './surface.types';
3
+ export { MAX_SNAPSHOT_BYTES, acceptSurface, surfaceSchemaFile, validateSurfaceSchema, } from './surface-validate';
4
+ export type { SurfaceValidation } from './surface-validate';
5
+ export { isRealInstant, validateSurfaceSemantics } from './surface-semantics';
6
+ export type { IssueSeverity, SemanticValidationIssue, SemanticValidationOptions, } from './surface-semantics';
7
+ export { renderSurfaceDigest, safe } from './surface-digest';
8
+ export type { DigestOptions, SurfaceDigest } from './surface-digest';
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.safe = exports.renderSurfaceDigest = exports.validateSurfaceSemantics = exports.isRealInstant = exports.validateSurfaceSchema = exports.surfaceSchemaFile = exports.acceptSurface = exports.MAX_SNAPSHOT_BYTES = exports.SURFACE_SCHEMA_VERSION = exports.SCOPE_KINDS = exports.ATTENTION_REASONS = void 0;
4
+ var surface_types_1 = require("./surface.types");
5
+ Object.defineProperty(exports, "ATTENTION_REASONS", { enumerable: true, get: function () { return surface_types_1.ATTENTION_REASONS; } });
6
+ Object.defineProperty(exports, "SCOPE_KINDS", { enumerable: true, get: function () { return surface_types_1.SCOPE_KINDS; } });
7
+ Object.defineProperty(exports, "SURFACE_SCHEMA_VERSION", { enumerable: true, get: function () { return surface_types_1.SURFACE_SCHEMA_VERSION; } });
8
+ var surface_validate_1 = require("./surface-validate");
9
+ Object.defineProperty(exports, "MAX_SNAPSHOT_BYTES", { enumerable: true, get: function () { return surface_validate_1.MAX_SNAPSHOT_BYTES; } });
10
+ Object.defineProperty(exports, "acceptSurface", { enumerable: true, get: function () { return surface_validate_1.acceptSurface; } });
11
+ Object.defineProperty(exports, "surfaceSchemaFile", { enumerable: true, get: function () { return surface_validate_1.surfaceSchemaFile; } });
12
+ Object.defineProperty(exports, "validateSurfaceSchema", { enumerable: true, get: function () { return surface_validate_1.validateSurfaceSchema; } });
13
+ var surface_semantics_1 = require("./surface-semantics");
14
+ Object.defineProperty(exports, "isRealInstant", { enumerable: true, get: function () { return surface_semantics_1.isRealInstant; } });
15
+ Object.defineProperty(exports, "validateSurfaceSemantics", { enumerable: true, get: function () { return surface_semantics_1.validateSurfaceSemantics; } });
16
+ var surface_digest_1 = require("./surface-digest");
17
+ Object.defineProperty(exports, "renderSurfaceDigest", { enumerable: true, get: function () { return surface_digest_1.renderSurfaceDigest; } });
18
+ Object.defineProperty(exports, "safe", { enumerable: true, get: function () { return surface_digest_1.safe; } });
@@ -0,0 +1,257 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://flui.cloud/schemas/semantic-surface/0.2/semantic-surface.schema.json",
4
+ "title": "Semantic Surface Snapshot",
5
+ "description": "A serializable, medium-independent representation of the user's current attention and of what the interface has actually presented to them. See semantic-surface-core-v0.2.md.",
6
+ "type": "object",
7
+ "required": ["schemaVersion", "app", "surface", "attention", "scopes"],
8
+ "additionalProperties": false,
9
+ "properties": {
10
+ "schemaVersion": {
11
+ "const": "0.2",
12
+ "description": "A major.minor compatibility version, not a full SemVer string: it names a compatibility family, so non-breaking corrections can ship without invalidating existing snapshots."
13
+ },
14
+ "app": { "$ref": "#/$defs/app" },
15
+ "surface": { "$ref": "#/$defs/surface" },
16
+ "attention": {
17
+ "type": "array",
18
+ "maxItems": 16,
19
+ "items": { "$ref": "#/$defs/attentionTarget" },
20
+ "description": "Ordered by salience: the first element is the primary target. May be empty; an invented selection is not allowed."
21
+ },
22
+ "scopes": {
23
+ "type": "array",
24
+ "minItems": 1,
25
+ "maxItems": 200,
26
+ "items": { "$ref": "#/$defs/scope" },
27
+ "description": "Flat list. Hierarchy is expressed with parentId. An inactive scope never appears."
28
+ },
29
+ "extensions": { "$ref": "#/$defs/extensions" }
30
+ },
31
+ "$defs": {
32
+ "identifier": {
33
+ "type": "string",
34
+ "minLength": 1,
35
+ "maxLength": 128,
36
+ "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$"
37
+ },
38
+ "entityRef": {
39
+ "type": "string",
40
+ "format": "uri",
41
+ "maxLength": 512,
42
+ "pattern": "^[a-z][a-z0-9+.-]*://[a-z][a-z0-9._-]*/(?:[A-Za-z0-9._~!$&'()*+,;=:@-]|%[0-9A-Fa-f]{2})+$",
43
+ "description": "<namespace>://<entity-type>/<entity-id>. The entity-id is a single URI path segment: unreserved and sub-delimiter characters, or correctly percent-encoded octets. No nested path, query or fragment — composite identities use a separator inside the segment. Stable, passed explicitly to tools, never proof of authorisation."
44
+ },
45
+ "resourceRef": {
46
+ "type": "string",
47
+ "format": "uri",
48
+ "maxLength": 1024,
49
+ "pattern": "^[a-z][a-z0-9+.-]*://[^\\s]+$",
50
+ "description": "Where to fetch the authoritative value. Unlike entityRef, multi-segment paths and query strings are allowed."
51
+ },
52
+ "namespacedKey": {
53
+ "type": "string",
54
+ "maxLength": 128,
55
+ "pattern": "^[a-z][a-z0-9_]*(\\.[a-z0-9_]+)+$",
56
+ "description": "Namespaced key: at least one dot. For infrastructure metrics prefer the OpenTelemetry Semantic Conventions."
57
+ },
58
+ "scopeKind": {
59
+ "type": "string",
60
+ "maxLength": 64,
61
+ "pattern": "^(page|region|selection|list|form|overlay)$|^[a-z][a-z0-9_]*(\\.[a-z0-9_]+)+$",
62
+ "description": "A reserved core value, or a namespaced extension (e.g. vops.terminal)."
63
+ },
64
+ "attentionReason": {
65
+ "type": "string",
66
+ "maxLength": 64,
67
+ "pattern": "^(route|selection|active-view|overlay|manual)$|^[a-z][a-z0-9_]*(\\.[a-z0-9_]+)+$"
68
+ },
69
+ "shortText": { "type": "string", "maxLength": 200 },
70
+ "freeText": {
71
+ "type": "string",
72
+ "maxLength": 500,
73
+ "description": "Only text actually presented to the user, or produced by the backend as part of the product. Never text written to steer the model. No markup, no secrets, no operational instructions."
74
+ },
75
+ "timestamp": {
76
+ "type": "string",
77
+ "format": "date-time",
78
+ "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[+-]\\d{2}:\\d{2})$",
79
+ "description": "ISO 8601 with an explicit offset. The pattern is a deliberate belt-and-braces duplicate of the format, because validators silently ignore formats they do not know. It constrains lexical shape only: a well-formed but non-existent instant such as 2026-99-45T29:81:75+84:92 passes here and is caught by the semantic validator as invalid-timestamp-value."
80
+ },
81
+ "extensions": {
82
+ "type": "object",
83
+ "description": "Namespaced keys (e.g. vops.*). A consumer must ignore extensions it does not know.",
84
+ "propertyNames": { "pattern": "^[a-z][a-z0-9_]*(\\.[a-z0-9_]+)+$" },
85
+ "additionalProperties": true
86
+ },
87
+
88
+ "app": {
89
+ "type": "object",
90
+ "required": ["id"],
91
+ "additionalProperties": false,
92
+ "properties": {
93
+ "id": { "$ref": "#/$defs/identifier" },
94
+ "version": { "type": "string", "maxLength": 64 }
95
+ }
96
+ },
97
+
98
+ "surface": {
99
+ "type": "object",
100
+ "required": ["id", "revision", "generatedAt"],
101
+ "additionalProperties": false,
102
+ "properties": {
103
+ "id": { "$ref": "#/$defs/identifier" },
104
+ "route": { "type": "string", "maxLength": 512 },
105
+ "revision": {
106
+ "type": "integer",
107
+ "minimum": 0,
108
+ "description": "Monotonically increasing. Diagnoses UI context; does not replace backend versions or ETags."
109
+ },
110
+ "generatedAt": {
111
+ "$ref": "#/$defs/timestamp",
112
+ "description": "When the snapshot was produced. Not the age of the data: see observation.observedAt."
113
+ },
114
+ "locale": { "type": "string", "maxLength": 35 },
115
+ "truncated": {
116
+ "type": "boolean",
117
+ "description": "True when the budget dropped something."
118
+ }
119
+ }
120
+ },
121
+
122
+ "attentionTarget": {
123
+ "type": "object",
124
+ "required": ["scopeId"],
125
+ "additionalProperties": false,
126
+ "properties": {
127
+ "scopeId": { "$ref": "#/$defs/identifier" },
128
+ "entityRef": { "$ref": "#/$defs/entityRef" },
129
+ "reason": { "$ref": "#/$defs/attentionReason" }
130
+ }
131
+ },
132
+
133
+ "scope": {
134
+ "type": "object",
135
+ "required": ["id", "kind"],
136
+ "additionalProperties": false,
137
+ "properties": {
138
+ "id": { "$ref": "#/$defs/identifier" },
139
+ "parentId": { "$ref": "#/$defs/identifier" },
140
+ "kind": { "$ref": "#/$defs/scopeKind" },
141
+ "label": { "$ref": "#/$defs/shortText" },
142
+ "entities": {
143
+ "type": "array",
144
+ "maxItems": 64,
145
+ "items": { "$ref": "#/$defs/entityReference" }
146
+ },
147
+ "observations": {
148
+ "type": "array",
149
+ "maxItems": 64,
150
+ "items": { "$ref": "#/$defs/observation" }
151
+ },
152
+ "state": { "$ref": "#/$defs/scopeState" },
153
+ "completeness": { "$ref": "#/$defs/completeness" },
154
+ "extensions": { "$ref": "#/$defs/extensions" }
155
+ }
156
+ },
157
+
158
+ "scopeState": {
159
+ "type": "object",
160
+ "additionalProperties": false,
161
+ "description": "Distinguishes three conditions that would otherwise collapse into 'empty scope'.",
162
+ "properties": {
163
+ "loading": { "type": "boolean" },
164
+ "error": { "type": "boolean" },
165
+ "errorCode": {
166
+ "$ref": "#/$defs/namespacedKey",
167
+ "description": "A namespaced code such as vops.host.unreachable, never the free-text error message: text coming from a backend is an injection vector."
168
+ },
169
+ "empty": { "type": "boolean" }
170
+ },
171
+ "allOf": [
172
+ {
173
+ "if": { "required": ["errorCode"] },
174
+ "then": {
175
+ "required": ["error"],
176
+ "properties": { "error": { "const": true } }
177
+ }
178
+ }
179
+ ]
180
+ },
181
+
182
+ "completeness": {
183
+ "type": "object",
184
+ "required": ["shown"],
185
+ "additionalProperties": false,
186
+ "description": "Should be present on every scope representing a paginated, filtered or virtualised list.",
187
+ "properties": {
188
+ "shown": { "type": "integer", "minimum": 0 },
189
+ "total": { "type": "integer", "minimum": 0 },
190
+ "filtered": { "type": "boolean" },
191
+ "truncated": { "type": "boolean" }
192
+ }
193
+ },
194
+
195
+ "entityReference": {
196
+ "type": "object",
197
+ "required": ["ref"],
198
+ "additionalProperties": false,
199
+ "description": "A single identifying field: the v0.1 ref/type/id triple has been removed.",
200
+ "properties": {
201
+ "ref": { "$ref": "#/$defs/entityRef" },
202
+ "label": { "$ref": "#/$defs/shortText" },
203
+ "role": { "enum": ["primary", "selected", "related"] }
204
+ }
205
+ },
206
+
207
+ "observation": {
208
+ "type": "object",
209
+ "required": ["key", "presentedAs"],
210
+ "additionalProperties": false,
211
+ "description": "Always means: the interface presented this, referring to that moment. Never: this is true now.",
212
+ "properties": {
213
+ "key": { "$ref": "#/$defs/namespacedKey" },
214
+ "presentedAs": {
215
+ "type": "object",
216
+ "additionalProperties": false,
217
+ "description": "The only container for the presented value: there is no bare 'value' field beside it. A unit alone presents nothing, so either value or text is required.",
218
+ "properties": {
219
+ "value": {
220
+ "description": "A compact scalar actually presented to the user. Charts, series and structures belong behind resourceRef.",
221
+ "oneOf": [
222
+ { "type": "number" },
223
+ { "type": "boolean" },
224
+ { "type": "null" },
225
+ { "type": "string", "maxLength": 200 }
226
+ ]
227
+ },
228
+ "unit": { "type": "string", "maxLength": 32 },
229
+ "text": { "$ref": "#/$defs/freeText" }
230
+ },
231
+ "anyOf": [
232
+ { "required": ["value"] },
233
+ { "required": ["text"] }
234
+ ]
235
+ },
236
+ "source": { "enum": ["ui", "api", "derived"] },
237
+ "observedAt": {
238
+ "$ref": "#/$defs/timestamp",
239
+ "description": "When the value was measured or received. If absent the age is UNKNOWN: a consumer must not assume it equals surface.generatedAt."
240
+ },
241
+ "resourceRef": { "$ref": "#/$defs/resourceRef" }
242
+ },
243
+ "allOf": [
244
+ {
245
+ "if": {
246
+ "required": ["presentedAs"],
247
+ "properties": { "presentedAs": { "required": ["text"] } }
248
+ },
249
+ "then": {
250
+ "required": ["source"],
251
+ "$comment": "Free text is admitted only with provenance (core spec section 3.5)."
252
+ }
253
+ }
254
+ ]
255
+ }
256
+ }
257
+ }
@@ -0,0 +1,47 @@
1
+ import { SurfaceSnapshot } from './surface.types';
2
+ /**
3
+ * A snapshot rendered for a prompt.
4
+ *
5
+ * Four properties matter more than the layout.
6
+ *
7
+ * **Deterministic** — the same snapshot renders to the same bytes, which is why no
8
+ * relative age is ever computed from a clock: the age below is the distance between
9
+ * two timestamps that are both in the snapshot.
10
+ *
11
+ * **Complete** — every fact the Surface declares is rendered. Nothing is dropped for
12
+ * being judged unimportant: the only omission is the budget's, and it says so. A
13
+ * hand-written salience heuristic would be a second, silent selection on top of the
14
+ * producer's own, and the fact it dropped would be invisible to a model that has no
15
+ * reason to suspect a gap.
16
+ *
17
+ * **Bounded** — the budget is enforced here, in the order §9 prescribes.
18
+ *
19
+ * **Fenced** — every string that could have come from a machine passes through `safe`,
20
+ * because a resource name is data and must never be able to close the fence and start
21
+ * giving instructions (§8.4).
22
+ */
23
+ export interface DigestOptions {
24
+ maxBytes?: number;
25
+ maxTextLength?: number;
26
+ /** Off by default: refs are the longest thing on a line and cost every turn, while
27
+ * one call to the full-snapshot tool returns all of them at once. */
28
+ includeResourceRefs?: boolean;
29
+ /** The tail line names the tool a model can call for the whole snapshot. Every host
30
+ * names that tool itself, so the default here is generic and hosts override it. */
31
+ fullSnapshotTool?: string;
32
+ }
33
+ export interface SurfaceDigest {
34
+ text: string;
35
+ bytes: number;
36
+ truncated: boolean;
37
+ omittedObservations: number;
38
+ }
39
+ export declare function renderSurfaceDigest(snapshot: SurfaceSnapshot, options?: DigestOptions): SurfaceDigest;
40
+ /**
41
+ * Everything that can carry text from a machine passes through here.
42
+ *
43
+ * Newlines are collapsed because a line break is how a value would forge a new section;
44
+ * angle brackets are removed because they are how it would forge the closing fence. The
45
+ * loss is nil for host names, codes, units and package names, and total for an attack.
46
+ */
47
+ export declare function safe(value: string, maxLength: number): string;